diff --git a/.gitattributes b/.gitattributes index 5c28162f..309467bb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,5 @@ +# Force LF line endings for all text files (for consistent prettier formatting) +* text=auto eol=lf + package-lock.json linguist-generated=true src/generated/** linguist-generated=true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd795a94..be0db8ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,26 +11,43 @@ permissions: jobs: build: - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + name: Linux x64 + - os: ubuntu-24.04-arm + name: Linux ARM64 + - os: windows-latest + name: Windows x64 + - os: windows-11-arm + name: Windows ARM64 + - os: macos-latest + name: macOS ARM64 + + name: Build (${{ matrix.name }}) + runs-on: ${{ matrix.os }} + steps: - uses: actions/checkout@v4 - name: Verify no private URLs in package-lock.json + shell: bash run: '! grep -E "\"resolved\": \"https?://" package-lock.json | grep -v registry.npmjs.org' - - uses: oven-sh/setup-bun@v2 - with: - bun-version: latest - - uses: actions/setup-node@v4 with: node-version: "20" - run: npm install - - run: npm run build:all + - run: npm run build + + - run: npm run examples:build - name: Verify generated schemas are up-to-date + shell: bash run: | npm run generate:schemas git diff --exit-code src/generated/ || (echo "Generated schemas are out of date. Run 'npm run generate:schemas' and commit." && exit 1) @@ -67,3 +84,67 @@ jobs: name: test-results path: test-results/ retention-days: 7 + + # Test build in Windows WSL (Ubuntu) + build-wsl: + name: Build (Windows WSL) + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + + - uses: Vampire/setup-wsl@v5 + with: + distribution: Ubuntu-24.04 + + - name: Install Node.js in WSL + shell: wsl-bash {0} + run: | + sudo apt-get update + curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - + sudo apt-get install -y nodejs + + - name: Build and test in WSL + shell: wsl-bash {0} + run: | + npm install + npm run build + npm run examples:build + npm test + npm run prettier + + # Test that the package can be installed from git (triggers prepare script) + test-git-install: + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + name: Linux x64 + - os: ubuntu-24.04-arm + name: Linux ARM64 + - os: windows-latest + name: Windows x64 + - os: windows-11-arm + name: Windows ARM64 + - os: macos-latest + name: macOS ARM64 + + name: Test git install (${{ matrix.name }}) + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Create test project and install from git + shell: bash + run: | + mkdir test-project + cd test-project + npm init -y + # Install from the PR branch + npm install "git+https://github.com/${{ github.repository }}#${{ github.head_ref || github.ref_name }}" + # Verify the package is usable (ESM import) + node --input-type=module -e "import { App } from '@modelcontextprotocol/ext-apps'; console.log('Import successful:', typeof App)" diff --git a/README.md b/README.md index d6d542ed..d7cb04fe 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ The [`examples/`](https://github.com/modelcontextprotocol/ext-apps/tree/main/exa To run all examples together: -``` +```bash npm install npm run examples:start ``` diff --git a/examples/basic-host/package.json b/examples/basic-host/package.json index 7615eb15..10d9e1fc 100644 --- a/examples/basic-host/package.json +++ b/examples/basic-host/package.json @@ -4,11 +4,11 @@ "version": "1.0.0", "type": "module", "scripts": { - "build": "concurrently 'INPUT=index.html vite build' 'INPUT=sandbox.html vite build'", - "watch": "concurrently 'INPUT=index.html vite build --watch' 'INPUT=sandbox.html vite build --watch'", + "build": "concurrently \"cross-env INPUT=index.html vite build\" \"cross-env INPUT=sandbox.html vite build\"", + "watch": "concurrently \"cross-env INPUT=index.html vite build --watch\" \"cross-env INPUT=sandbox.html vite build --watch\"", "serve": "bun serve.ts", - "start": "NODE_ENV=development npm run build && npm run serve", - "dev": "NODE_ENV=development concurrently 'npm run watch' 'npm run serve'" + "start": "cross-env NODE_ENV=development npm run build && npm run serve", + "dev": "cross-env NODE_ENV=development concurrently \"npm run watch\" \"npm run serve\"" }, "dependencies": { "@modelcontextprotocol/ext-apps": "../..", @@ -23,7 +23,6 @@ "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", "@vitejs/plugin-react": "^4.3.4", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/basic-server-react/package.json b/examples/basic-server-react/package.json index 35aa9f35..a4b642c0 100644 --- a/examples/basic-server-react/package.json +++ b/examples/basic-server-react/package.json @@ -4,11 +4,11 @@ "private": true, "type": "module", "scripts": { - "build": "INPUT=mcp-app.html vite build", - "watch": "INPUT=mcp-app.html vite build --watch", + "build": "cross-env INPUT=mcp-app.html vite build", + "watch": "cross-env INPUT=mcp-app.html vite build --watch", "serve": "bun server.ts", - "start": "NODE_ENV=development npm run build && npm run serve", - "dev": "NODE_ENV=development concurrently 'npm run watch' 'npm run serve'" + "start": "cross-env NODE_ENV=development npm run build && npm run serve", + "dev": "cross-env NODE_ENV=development concurrently 'npm run watch' 'npm run serve'" }, "dependencies": { "@modelcontextprotocol/ext-apps": "../..", @@ -24,7 +24,6 @@ "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", "@vitejs/plugin-react": "^4.3.4", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/basic-server-vanillajs/package.json b/examples/basic-server-vanillajs/package.json index a44e65d1..1203dde9 100644 --- a/examples/basic-server-vanillajs/package.json +++ b/examples/basic-server-vanillajs/package.json @@ -4,11 +4,11 @@ "private": true, "type": "module", "scripts": { - "build": "INPUT=mcp-app.html vite build", - "watch": "INPUT=mcp-app.html vite build --watch", + "build": "cross-env INPUT=mcp-app.html vite build", + "watch": "cross-env INPUT=mcp-app.html vite build --watch", "serve": "bun server.ts", - "start": "NODE_ENV=development npm run build && npm run serve", - "dev": "NODE_ENV=development concurrently 'npm run watch' 'npm run serve'" + "start": "cross-env NODE_ENV=development npm run build && npm run serve", + "dev": "cross-env NODE_ENV=development concurrently 'npm run watch' 'npm run serve'" }, "dependencies": { "@modelcontextprotocol/ext-apps": "../..", @@ -19,7 +19,6 @@ "@types/cors": "^2.8.19", "@types/express": "^5.0.0", "@types/node": "^22.0.0", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/budget-allocator-server/package.json b/examples/budget-allocator-server/package.json index 80cacc9e..bffa9e99 100644 --- a/examples/budget-allocator-server/package.json +++ b/examples/budget-allocator-server/package.json @@ -4,14 +4,14 @@ "private": true, "type": "module", "scripts": { - "build": "INPUT=mcp-app.html vite build", - "watch": "INPUT=mcp-app.html vite build --watch", + "build": "cross-env INPUT=mcp-app.html vite build", + "watch": "cross-env INPUT=mcp-app.html vite build --watch", "serve:http": "bun server.ts", "serve:stdio": "bun server.ts --stdio", "start": "npm run start:http", - "start:http": "NODE_ENV=development npm run build && npm run serve:http", - "start:stdio": "NODE_ENV=development npm run build && npm run serve:stdio", - "dev": "NODE_ENV=development concurrently 'npm run watch' 'npm run serve:http'" + "start:http": "cross-env NODE_ENV=development npm run build && npm run serve:http", + "start:stdio": "cross-env NODE_ENV=development npm run build && npm run serve:stdio", + "dev": "cross-env NODE_ENV=development concurrently 'npm run watch' 'npm run serve:http'" }, "dependencies": { "@modelcontextprotocol/ext-apps": "../..", @@ -23,7 +23,6 @@ "@types/cors": "^2.8.19", "@types/express": "^5.0.0", "@types/node": "^22.0.0", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/cohort-heatmap-server/package.json b/examples/cohort-heatmap-server/package.json index 32e6cbf1..cd4db959 100644 --- a/examples/cohort-heatmap-server/package.json +++ b/examples/cohort-heatmap-server/package.json @@ -4,14 +4,14 @@ "private": true, "type": "module", "scripts": { - "build": "INPUT=mcp-app.html vite build", - "watch": "INPUT=mcp-app.html vite build --watch", + "build": "cross-env INPUT=mcp-app.html vite build", + "watch": "cross-env INPUT=mcp-app.html vite build --watch", "serve:http": "bun server.ts", "serve:stdio": "bun server.ts --stdio", "start": "npm run start:http", - "start:http": "NODE_ENV=development npm run build && npm run serve:http", - "start:stdio": "NODE_ENV=development npm run build && npm run serve:stdio", - "dev": "NODE_ENV=development concurrently 'npm run watch' 'npm run serve:http'" + "start:http": "cross-env NODE_ENV=development npm run build && npm run serve:http", + "start:stdio": "cross-env NODE_ENV=development npm run build && npm run serve:stdio", + "dev": "cross-env NODE_ENV=development concurrently 'npm run watch' 'npm run serve:http'" }, "dependencies": { "@modelcontextprotocol/ext-apps": "../..", @@ -27,7 +27,6 @@ "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", "@vitejs/plugin-react": "^4.3.4", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/customer-segmentation-server/package.json b/examples/customer-segmentation-server/package.json index 23083964..dfc96f14 100644 --- a/examples/customer-segmentation-server/package.json +++ b/examples/customer-segmentation-server/package.json @@ -4,14 +4,14 @@ "private": true, "type": "module", "scripts": { - "build": "INPUT=mcp-app.html vite build", - "watch": "INPUT=mcp-app.html vite build --watch", + "build": "cross-env INPUT=mcp-app.html vite build", + "watch": "cross-env INPUT=mcp-app.html vite build --watch", "serve:http": "bun server.ts", "serve:stdio": "bun server.ts --stdio", "start": "npm run start:http", - "start:http": "NODE_ENV=development npm run build && npm run serve:http", - "start:stdio": "NODE_ENV=development npm run build && npm run serve:stdio", - "dev": "NODE_ENV=development concurrently 'npm run watch' 'npm run serve:http'" + "start:http": "cross-env NODE_ENV=development npm run build && npm run serve:http", + "start:stdio": "cross-env NODE_ENV=development npm run build && npm run serve:stdio", + "dev": "cross-env NODE_ENV=development concurrently 'npm run watch' 'npm run serve:http'" }, "dependencies": { "@modelcontextprotocol/ext-apps": "../..", @@ -23,7 +23,6 @@ "@types/cors": "^2.8.19", "@types/express": "^5.0.0", "@types/node": "^22.0.0", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/scenario-modeler-server/package.json b/examples/scenario-modeler-server/package.json index a1e073f3..2eb11a25 100644 --- a/examples/scenario-modeler-server/package.json +++ b/examples/scenario-modeler-server/package.json @@ -4,14 +4,14 @@ "private": true, "type": "module", "scripts": { - "build": "INPUT=mcp-app.html vite build", - "watch": "INPUT=mcp-app.html vite build --watch", + "build": "cross-env INPUT=mcp-app.html vite build", + "watch": "cross-env INPUT=mcp-app.html vite build --watch", "serve:http": "bun server.ts", "serve:stdio": "bun server.ts --stdio", "start": "npm run start:http", - "start:http": "NODE_ENV=development npm run build && npm run serve:http", - "start:stdio": "NODE_ENV=development npm run build && npm run serve:stdio", - "dev": "NODE_ENV=development concurrently 'npm run watch' 'npm run serve:http'" + "start:http": "cross-env NODE_ENV=development npm run build && npm run serve:http", + "start:stdio": "cross-env NODE_ENV=development npm run build && npm run serve:stdio", + "dev": "cross-env NODE_ENV=development concurrently 'npm run watch' 'npm run serve:http'" }, "dependencies": { "@modelcontextprotocol/ext-apps": "../..", @@ -28,7 +28,6 @@ "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", "@vitejs/plugin-react": "^4.3.4", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/system-monitor-server/package.json b/examples/system-monitor-server/package.json index 21455742..082042e8 100644 --- a/examples/system-monitor-server/package.json +++ b/examples/system-monitor-server/package.json @@ -4,14 +4,14 @@ "private": true, "type": "module", "scripts": { - "build": "INPUT=mcp-app.html vite build", - "watch": "INPUT=mcp-app.html vite build --watch", + "build": "cross-env INPUT=mcp-app.html vite build", + "watch": "cross-env INPUT=mcp-app.html vite build --watch", "serve:http": "bun server.ts", "serve:stdio": "bun server.ts --stdio", "start": "npm run start:http", - "start:http": "NODE_ENV=development npm run build && npm run serve:http", - "start:stdio": "NODE_ENV=development npm run build && npm run serve:stdio", - "dev": "NODE_ENV=development concurrently 'npm run watch' 'npm run serve:http'" + "start:http": "cross-env NODE_ENV=development npm run build && npm run serve:http", + "start:stdio": "cross-env NODE_ENV=development npm run build && npm run serve:stdio", + "dev": "cross-env NODE_ENV=development concurrently 'npm run watch' 'npm run serve:http'" }, "dependencies": { "@modelcontextprotocol/ext-apps": "../..", @@ -24,7 +24,6 @@ "@types/cors": "^2.8.19", "@types/express": "^5.0.0", "@types/node": "^22.0.0", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/threejs-server/package.json b/examples/threejs-server/package.json index d691c8aa..2b5ce8b9 100644 --- a/examples/threejs-server/package.json +++ b/examples/threejs-server/package.json @@ -4,14 +4,14 @@ "private": true, "type": "module", "scripts": { - "build": "INPUT=mcp-app.html vite build", - "watch": "INPUT=mcp-app.html vite build --watch", + "build": "cross-env INPUT=mcp-app.html vite build", + "watch": "cross-env INPUT=mcp-app.html vite build --watch", "serve:http": "bun server.ts", "serve:stdio": "bun server.ts --stdio", "start": "npm run start:http", - "start:http": "NODE_ENV=development npm run build && npm run serve:http", - "start:stdio": "NODE_ENV=development npm run build && npm run serve:stdio", - "dev": "NODE_ENV=development concurrently 'npm run watch' 'npm run serve:http'" + "start:http": "cross-env NODE_ENV=development npm run build && npm run serve:http", + "start:stdio": "cross-env NODE_ENV=development npm run build && npm run serve:stdio", + "dev": "cross-env NODE_ENV=development concurrently 'npm run watch' 'npm run serve:http'" }, "dependencies": { "@modelcontextprotocol/ext-apps": "../..", @@ -29,7 +29,6 @@ "@types/react-dom": "^19.2.2", "@types/three": "^0.181.0", "@vitejs/plugin-react": "^4.3.4", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/examples/wiki-explorer-server/package.json b/examples/wiki-explorer-server/package.json index 4468d586..89eece39 100644 --- a/examples/wiki-explorer-server/package.json +++ b/examples/wiki-explorer-server/package.json @@ -4,14 +4,14 @@ "private": true, "type": "module", "scripts": { - "build": "INPUT=mcp-app.html vite build", - "watch": "INPUT=mcp-app.html vite build --watch", + "build": "cross-env INPUT=mcp-app.html vite build", + "watch": "cross-env INPUT=mcp-app.html vite build --watch", "serve:http": "bun server.ts", "serve:stdio": "bun server.ts --stdio", "start": "npm run start:http", - "start:http": "NODE_ENV=development npm run build && npm run serve:http", - "start:stdio": "NODE_ENV=development npm run build && npm run serve:stdio", - "dev": "NODE_ENV=development concurrently 'npm run watch' 'npm run serve:http'" + "start:http": "cross-env NODE_ENV=development npm run build && npm run serve:http", + "start:stdio": "cross-env NODE_ENV=development npm run build && npm run serve:stdio", + "dev": "cross-env NODE_ENV=development concurrently 'npm run watch' 'npm run serve:http'" }, "dependencies": { "@modelcontextprotocol/ext-apps": "../..", @@ -23,7 +23,6 @@ "@types/cors": "^2.8.19", "@types/express": "^5.0.0", "@types/node": "^22.0.0", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/package-lock.json b/package-lock.json index 099cff78..d1b73cb9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,12 +7,14 @@ "": { "name": "@modelcontextprotocol/ext-apps", "version": "0.1.0", + "hasInstallScript": true, "license": "MIT", "workspaces": [ "examples/*" ], "dependencies": { "@modelcontextprotocol/sdk": "^1.24.3", + "prettier": "^3.6.2", "react": "^19.2.0", "react-dom": "^19.2.0" }, @@ -21,14 +23,13 @@ "@types/bun": "^1.3.2", "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "esbuild": "^0.25.12", "express": "^5.1.0", "husky": "^9.1.7", "nodemon": "^3.1.0", - "prettier": "^3.6.2", "ts-to-zod": "^5.1.0", "tsx": "^4.21.0", "typedoc": "^0.28.14", @@ -36,6 +37,17 @@ "zod": "^4.1.13" }, "optionalDependencies": { + "@oven/bun-darwin-aarch64": "^1.3.4", + "@oven/bun-darwin-x64": "^1.3.4", + "@oven/bun-darwin-x64-baseline": "^1.3.4", + "@oven/bun-linux-aarch64": "^1.3.4", + "@oven/bun-linux-aarch64-musl": "^1.3.4", + "@oven/bun-linux-x64": "^1.3.4", + "@oven/bun-linux-x64-baseline": "^1.3.4", + "@oven/bun-linux-x64-musl": "^1.3.4", + "@oven/bun-linux-x64-musl-baseline": "^1.3.4", + "@oven/bun-windows-x64": "^1.3.4", + "@oven/bun-windows-x64-baseline": "^1.3.4", "@rollup/rollup-darwin-arm64": "^4.53.3", "@rollup/rollup-darwin-x64": "^4.53.3", "@rollup/rollup-linux-arm64-gnu": "^4.53.3", @@ -62,7 +74,6 @@ "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", "@vitejs/plugin-react": "^4.3.4", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", @@ -75,6 +86,8 @@ }, "examples/basic-host/node_modules/@types/node": { "version": "22.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", + "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", "dev": true, "license": "MIT", "dependencies": { @@ -104,7 +117,6 @@ "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", "@vitejs/plugin-react": "^4.3.4", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", @@ -115,6 +127,8 @@ }, "examples/basic-server-react/node_modules/@types/node": { "version": "22.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", + "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", "dev": true, "license": "MIT", "dependencies": { @@ -139,7 +153,6 @@ "@types/cors": "^2.8.19", "@types/express": "^5.0.0", "@types/node": "^22.0.0", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", @@ -150,6 +163,8 @@ }, "examples/basic-server-vanillajs/node_modules/@types/node": { "version": "22.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", + "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", "dev": true, "license": "MIT", "dependencies": { @@ -175,7 +190,6 @@ "@types/cors": "^2.8.19", "@types/express": "^5.0.0", "@types/node": "^22.0.0", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", @@ -186,6 +200,8 @@ }, "examples/budget-allocator-server/node_modules/@types/node": { "version": "22.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", + "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", "dev": true, "license": "MIT", "dependencies": { @@ -215,7 +231,6 @@ "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", "@vitejs/plugin-react": "^4.3.4", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", @@ -226,6 +241,8 @@ }, "examples/cohort-heatmap-server/node_modules/@types/node": { "version": "22.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", + "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", "dev": true, "license": "MIT", "dependencies": { @@ -251,7 +268,6 @@ "@types/cors": "^2.8.19", "@types/express": "^5.0.0", "@types/node": "^22.0.0", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", @@ -262,6 +278,8 @@ }, "examples/customer-segmentation-server/node_modules/@types/node": { "version": "22.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", + "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", "dev": true, "license": "MIT", "dependencies": { @@ -292,7 +310,6 @@ "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", "@vitejs/plugin-react": "^4.3.4", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", @@ -303,6 +320,8 @@ }, "examples/scenario-modeler-server/node_modules/@types/node": { "version": "22.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", + "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", "dev": true, "license": "MIT", "dependencies": { @@ -329,7 +348,6 @@ "@types/cors": "^2.8.19", "@types/express": "^5.0.0", "@types/node": "^22.0.0", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", @@ -340,6 +358,8 @@ }, "examples/system-monitor-server/node_modules/@types/node": { "version": "22.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", + "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", "dev": true, "license": "MIT", "dependencies": { @@ -371,7 +391,6 @@ "@types/react-dom": "^19.2.2", "@types/three": "^0.181.0", "@vitejs/plugin-react": "^4.3.4", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", @@ -382,6 +401,8 @@ }, "examples/threejs-server/node_modules/@types/node": { "version": "22.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", + "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", "dev": true, "license": "MIT", "dependencies": { @@ -407,7 +428,6 @@ "@types/cors": "^2.8.19", "@types/express": "^5.0.0", "@types/node": "^22.0.0", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", "express": "^5.1.0", @@ -764,6 +784,13 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/@epic-web/invariant": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", + "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==", + "dev": true, + "license": "MIT" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -1384,7 +1411,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1398,7 +1424,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1412,7 +1437,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1426,7 +1450,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1440,7 +1463,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1454,7 +1476,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1468,7 +1489,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1482,7 +1502,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1496,7 +1515,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1510,7 +1528,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1524,7 +1541,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1970,13 +1986,13 @@ } }, "node_modules/@types/bun": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.3.4.tgz", - "integrity": "sha512-EEPTKXHP+zKGPkhRLv+HI0UEX8/o+65hqARxLy8Ov5rIxMBPNTjeZww00CIihrIQGEQBYg+0roO5qOnS/7boGA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.3.3.tgz", + "integrity": "sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g==", "dev": true, "license": "MIT", "dependencies": { - "bun-types": "1.3.4" + "bun-types": "1.3.3" } }, "node_modules/@types/chai": { @@ -2067,9 +2083,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.0.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.0.tgz", - "integrity": "sha512-rl78HwuZlaDIUSeUKkmogkhebA+8K1Hy7tddZuJ3D0xV8pZSfsYGTsliGUol1JPzu9EKnTxPC4L1fiWouStRew==", + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2481,9 +2497,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.9.6", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.6.tgz", - "integrity": "sha512-v9BVVpOTLB59C9E7aSnmIF8h7qRsFpx+A2nugVMTszEOMcfjlZMsXRm4LF23I3Z9AJxc8ANpIvzbzONoX9VJlg==", + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.4.tgz", + "integrity": "sha512-ZCQ9GEWl73BVm8bu5Fts8nt7MHdbt5vY9bP6WGnUh+r3l8M7CgfyTlwsgCbMC66BNxPr6Xoce3j66Ms5YUQTNA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -2614,44 +2630,10 @@ "resolved": "examples/budget-allocator-server", "link": true }, - "node_modules/bun": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/bun/-/bun-1.3.4.tgz", - "integrity": "sha512-xV6KgD5ImquuKsoghzbWmYzeCXmmSgN6yJGz444hri2W+NGKNRFUNrEhy9+/rRXbvNA2qF0K0jAwqFNy1/GhBg==", - "cpu": [ - "arm64", - "x64" - ], - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "os": [ - "darwin", - "linux", - "win32" - ], - "bin": { - "bun": "bin/bun.exe", - "bunx": "bin/bunx.exe" - }, - "optionalDependencies": { - "@oven/bun-darwin-aarch64": "1.3.4", - "@oven/bun-darwin-x64": "1.3.4", - "@oven/bun-darwin-x64-baseline": "1.3.4", - "@oven/bun-linux-aarch64": "1.3.4", - "@oven/bun-linux-aarch64-musl": "1.3.4", - "@oven/bun-linux-x64": "1.3.4", - "@oven/bun-linux-x64-baseline": "1.3.4", - "@oven/bun-linux-x64-musl": "1.3.4", - "@oven/bun-linux-x64-musl-baseline": "1.3.4", - "@oven/bun-windows-x64": "1.3.4", - "@oven/bun-windows-x64-baseline": "1.3.4" - } - }, "node_modules/bun-types": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.3.4.tgz", - "integrity": "sha512-5ua817+BZPZOlNaRgGBpZJOSAQ9RQ17pkwPD0yR7CfJg+r8DgIILByFifDTa+IPDDxzf5VNhtNlcKqFzDgJvlQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.3.3.tgz", + "integrity": "sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2707,9 +2689,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001760", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz", - "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==", + "version": "1.0.30001759", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001759.tgz", + "integrity": "sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==", "dev": true, "funding": [ { @@ -3116,6 +3098,24 @@ "node": ">= 0.10" } }, + "node_modules/cross-env": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", + "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@epic-web/invariant": "^1.0.0", + "cross-spawn": "^7.0.6" + }, + "bin": { + "cross-env": "dist/bin/cross-env.js", + "cross-env-shell": "dist/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -3532,9 +3532,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.267", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", - "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "version": "1.5.266", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.266.tgz", + "integrity": "sha512-kgWEglXvkEfMH7rxP5OSZZwnaDWT7J9EoZCujhnpLbfi0bbNtRkgdX2E3gt0Uer11c61qCYktB3hwkAS325sJg==", "dev": true, "license": "ISC" }, @@ -3760,9 +3760,9 @@ } }, "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5220,7 +5220,6 @@ "version": "3.7.4", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", - "dev": true, "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" @@ -5865,9 +5864,9 @@ "link": true }, "node_modules/systeminformation": { - "version": "5.27.13", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.27.13.tgz", - "integrity": "sha512-geeE/7eNDoOhdc9j+qCsLlwbcyh0HnqhOZzmfNK4WBioWGUZbhwYrg+YZsZ3UJh4tmybQsnDuqzr3UoumMifew==", + "version": "5.27.11", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.27.11.tgz", + "integrity": "sha512-K3Lto/2m3K2twmKHdgx5B+0in9qhXK4YnoT9rIlgwN/4v7OV5c8IjbeAUkuky/6VzCQC7iKCAqi8rZathCdjHg==", "license": "MIT", "os": [ "darwin", @@ -7441,9 +7440,9 @@ } }, "node_modules/zod": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", - "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz", + "integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index 01f9d700..f4f6940e 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,9 @@ "examples/*" ], "scripts": { - "generate:schemas": "tsx scripts/generate-schemas.ts && prettier --write 'src/generated/**/*'", - "build": "npm run generate:schemas && bun build.bun.ts", + "postinstall": "node scripts/setup-bun.mjs || echo 'setup-bun.mjs failed or not available'", + "generate:schemas": "tsx scripts/generate-schemas.ts && prettier --write \"src/generated/**/*\"", + "build": "npm run generate:schemas && node scripts/run-bun.mjs build.bun.ts", "prepack": "npm run build", "build:all": "npm run build && npm run examples:build", "test": "bun test src", @@ -46,12 +47,11 @@ "examples:start": "NODE_ENV=development npm run build && bun examples/run-all.ts start", "examples:dev": "NODE_ENV=development bun examples/run-all.ts dev", "watch": "nodemon --watch src --ext ts,tsx --exec 'bun build.bun.ts'", - "prepare": "npm run build && husky", + "prepare": "node scripts/setup-bun.mjs && npm run build && husky", "docs": "typedoc", "docs:watch": "typedoc --watch", - "prettier:base-cmd": "prettier -u --ignore-path ./.gitignore --ignore-path ./.prettierignore", - "prettier": "yarn prettier:base-cmd \"$(pwd)/**/*.{js,jsx,ts,tsx,mjs,json,md,yml,yaml}\" --check", - "prettier:fix": "yarn prettier:base-cmd \"$(pwd)/**/*.{js,jsx,ts,tsx,mjs,json,md,yml,yaml}\" --write --list-different" + "prettier": "prettier -u \"**/*.{js,jsx,ts,tsx,mjs,json,md,yml,yaml}\" --check", + "prettier:fix": "prettier -u \"**/*.{js,jsx,ts,tsx,mjs,json,md,yml,yaml}\" --write" }, "author": "Olivier Chafik", "devDependencies": { @@ -59,9 +59,9 @@ "@types/bun": "^1.3.2", "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", - "bun": "^1.3.2", "concurrently": "^9.2.1", "cors": "^2.8.5", + "cross-env": "^10.1.0", "esbuild": "^0.25.12", "express": "^5.1.0", "husky": "^9.1.7", @@ -76,12 +76,23 @@ "zod": "^3.25.0 || ^4.0.0" }, "dependencies": { - "prettier": "^3.6.2", "@modelcontextprotocol/sdk": "^1.24.3", + "prettier": "^3.6.2", "react": "^19.2.0", "react-dom": "^19.2.0" }, "optionalDependencies": { + "@oven/bun-darwin-aarch64": "^1.3.4", + "@oven/bun-darwin-x64": "^1.3.4", + "@oven/bun-darwin-x64-baseline": "^1.3.4", + "@oven/bun-linux-aarch64": "^1.3.4", + "@oven/bun-linux-aarch64-musl": "^1.3.4", + "@oven/bun-linux-x64": "^1.3.4", + "@oven/bun-linux-x64-baseline": "^1.3.4", + "@oven/bun-linux-x64-musl": "^1.3.4", + "@oven/bun-linux-x64-musl-baseline": "^1.3.4", + "@oven/bun-windows-x64": "^1.3.4", + "@oven/bun-windows-x64-baseline": "^1.3.4", "@rollup/rollup-darwin-arm64": "^4.53.3", "@rollup/rollup-darwin-x64": "^4.53.3", "@rollup/rollup-linux-arm64-gnu": "^4.53.3", diff --git a/scripts/run-bun.mjs b/scripts/run-bun.mjs new file mode 100644 index 00000000..181858e1 --- /dev/null +++ b/scripts/run-bun.mjs @@ -0,0 +1,51 @@ +#!/usr/bin/env node +/** + * Wrapper script to run bun with the correct path. + * This is needed because during npm install's prepare hook, + * node_modules/.bin is not in PATH yet. + */ +import { spawn } from "child_process"; +import { existsSync } from "fs"; +import { join, dirname } from "path"; +import { fileURLToPath } from "url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const projectRoot = join(__dirname, ".."); +const isWindows = process.platform === "win32"; +const bunExe = isWindows ? "bun.exe" : "bun"; + +// Find bun binary +const bunPaths = [ + join(projectRoot, "node_modules", ".bin", bunExe), + // Fallback to system bun + "bun", +]; + +let bunPath = null; +for (const p of bunPaths) { + if (p === "bun" || existsSync(p)) { + bunPath = p; + break; + } +} + +if (!bunPath) { + console.error("Could not find bun binary"); + process.exit(1); +} + +// Run bun with the provided arguments +const args = process.argv.slice(2); +const child = spawn(bunPath, args, { + stdio: "inherit", + shell: isWindows, +}); + +child.on("exit", (code) => { + process.exit(code ?? 0); +}); + +child.on("error", (err) => { + console.error("Failed to run bun:", err.message); + process.exit(1); +}); diff --git a/scripts/setup-bun.mjs b/scripts/setup-bun.mjs new file mode 100644 index 00000000..c7ec0dba --- /dev/null +++ b/scripts/setup-bun.mjs @@ -0,0 +1,262 @@ +#!/usr/bin/env node +// Immediate log to verify script execution +console.log("[setup-bun] Script loaded"); + +/** + * Postinstall script to set up bun from platform-specific optional dependencies. + * Handles Windows ARM64 by downloading x64-baseline via emulation. + */ +import { + existsSync, + mkdirSync, + symlinkSync, + unlinkSync, + copyFileSync, + chmodSync, + writeFileSync, +} from "fs"; +import { join, dirname } from "path"; +import { spawnSync } from "child_process"; +import { fileURLToPath } from "url"; +import { get } from "https"; +import { createGunzip } from "zlib"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const projectRoot = join(__dirname, ".."); +const nodeModules = join(projectRoot, "node_modules"); +const binDir = join(nodeModules, ".bin"); + +const os = process.platform; +const arch = process.arch; +const isWindows = os === "win32"; +const bunExe = isWindows ? "bun.exe" : "bun"; + +// Detect libc type on Linux (glibc vs musl) +function detectLibc() { + if (os !== "linux") return null; + + // Check for musl-specific loader + const muslLoaders = [ + `/lib/ld-musl-${arch === "arm64" ? "aarch64" : "x86_64"}.so.1`, + "/lib/ld-musl-x86_64.so.1", + "/lib/ld-musl-aarch64.so.1", + ]; + + for (const loader of muslLoaders) { + if (existsSync(loader)) { + console.log(` Detected musl libc (found ${loader})`); + return "musl"; + } + } + + // Default to glibc on Linux + console.log(" Detected glibc (no musl loader found)"); + return "glibc"; +} + +// Platform to package mapping (matches @oven/bun-* package names) +// For Linux, separate glibc and musl packages +const platformPackages = { + darwin: { + arm64: ["bun-darwin-aarch64"], + x64: ["bun-darwin-x64", "bun-darwin-x64-baseline"], + }, + linux: { + arm64: { + glibc: ["bun-linux-aarch64"], + musl: ["bun-linux-aarch64-musl"], + }, + x64: { + glibc: ["bun-linux-x64", "bun-linux-x64-baseline"], + musl: ["bun-linux-x64-musl", "bun-linux-x64-musl-baseline"], + }, + }, + win32: { + x64: ["bun-windows-x64", "bun-windows-x64-baseline"], + arm64: ["bun-windows-x64-baseline"], // x64 runs via emulation on ARM64 + }, +}; + +function findBunBinary() { + let packages = platformPackages[os]?.[arch]; + + // For Linux, select packages based on libc type + if (os === "linux" && packages && typeof packages === "object") { + const libc = detectLibc(); + packages = packages[libc] || []; + } + + packages = packages || []; + console.log( + `Looking for bun packages: ${packages.join(", ") || "(none for this platform)"}`, + ); + + for (const pkg of packages) { + const binPath = join(nodeModules, "@oven", pkg, "bin", bunExe); + console.log(` Checking: ${binPath}`); + if (existsSync(binPath)) { + console.log(` Found bun at: ${binPath}`); + return binPath; + } else { + console.log(` Not found`); + } + } + + return null; +} + +async function downloadBunForWindowsArm64() { + // Windows ARM64 can run x64 binaries via emulation + const pkg = "bun-windows-x64-baseline"; + const version = "1.3.4"; + const url = `https://registry.npmjs.org/@oven/${pkg}/-/${pkg}-${version}.tgz`; + const destDir = join(nodeModules, "@oven", pkg); + + console.log(`Downloading ${pkg} for Windows ARM64 emulation...`); + + return new Promise((resolve, reject) => { + get(url, (response) => { + if (response.statusCode === 302 || response.statusCode === 301) { + get(response.headers.location, handleResponse).on("error", reject); + } else { + handleResponse(response); + } + + function handleResponse(res) { + if (res.statusCode !== 200) { + reject(new Error(`Failed to download: ${res.statusCode}`)); + return; + } + + const chunks = []; + const gunzip = createGunzip(); + + res.pipe(gunzip); + + gunzip.on("data", (chunk) => chunks.push(chunk)); + gunzip.on("end", () => { + try { + extractTar(Buffer.concat(chunks), destDir); + const binPath = join(destDir, "bin", bunExe); + if (existsSync(binPath)) { + resolve(binPath); + } else { + reject(new Error("Binary not found after extraction")); + } + } catch (err) { + reject(err); + } + }); + gunzip.on("error", reject); + } + }).on("error", reject); + }); +} + +function extractTar(buffer, destDir) { + // Simple tar extraction (512-byte blocks) + let offset = 0; + while (offset < buffer.length) { + const name = buffer + .toString("utf-8", offset, offset + 100) + .replace(/\0.*$/, "") + .replace("package/", ""); + const size = parseInt( + buffer.toString("utf-8", offset + 124, offset + 136).trim(), + 8, + ); + + offset += 512; + + if (!isNaN(size) && size > 0 && name) { + const filePath = join(destDir, name); + const fileDir = dirname(filePath); + if (!existsSync(fileDir)) { + mkdirSync(fileDir, { recursive: true }); + } + const content = buffer.subarray(offset, offset + size); + writeFileSync(filePath, content); + + // Make executable + if (name.endsWith(bunExe) || name === "bin/bun") { + try { + chmodSync(filePath, 0o755); + } catch {} + } + + offset += Math.ceil(size / 512) * 512; + } + } +} + +function setupBinLink(bunPath) { + if (!existsSync(binDir)) { + mkdirSync(binDir, { recursive: true }); + } + + const bunLink = join(binDir, bunExe); + const bunxLink = join(binDir, isWindows ? "bunx.exe" : "bunx"); + + // Remove existing links + for (const link of [bunLink, bunxLink]) { + try { + unlinkSync(link); + } catch {} + } + + if (isWindows) { + // On Windows, copy the binary (symlinks may not work without admin) + copyFileSync(bunPath, bunLink); + copyFileSync(bunPath, bunxLink); + } else { + // On Unix, use symlinks + symlinkSync(bunPath, bunLink); + symlinkSync(bunPath, bunxLink); + } + + console.log(`Bun linked to: ${bunLink}`); +} + +// Force immediate output +process.stdout.write("[setup-bun] Script starting...\n"); + +async function main() { + process.stdout.write(`[setup-bun] Setting up bun for ${os} ${arch}...\n`); + process.stdout.write(`[setup-bun] Project root: ${projectRoot}\n`); + process.stdout.write(`[setup-bun] Node modules: ${nodeModules}\n`); + + let bunPath = findBunBinary(); + + if (!bunPath && os === "win32" && arch === "arm64") { + try { + bunPath = await downloadBunForWindowsArm64(); + } catch (err) { + console.error("Failed to download bun for Windows ARM64:", err.message); + } + } + + if (!bunPath) { + console.log("No bun binary found in optional dependencies."); + console.log("Bun will need to be installed separately."); + console.log("See: https://bun.sh/docs/installation"); + process.exit(0); // Don't fail the install + } + + try { + setupBinLink(bunPath); + + // Verify installation + const result = spawnSync(bunPath, ["--version"], { encoding: "utf-8" }); + if (result.status === 0) { + console.log(`Bun ${result.stdout.trim()} installed successfully!`); + } + } catch (err) { + console.error("Failed to set up bun:", err.message); + process.exit(0); // Don't fail the install + } +} + +main().catch((err) => { + console.error(err); + process.exit(0); // Don't fail the install +});