diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..74c9478 --- /dev/null +++ b/.env.development @@ -0,0 +1,3 @@ +VITE_ASSETS_URL = '/fs' +#VITE_ASSETS_URL = 'http://localhost:8787' +#VITE_WORKER_URL=http://localhost:8787 diff --git a/.env.production b/.env.production index 4e40b6c..bd1626f 100644 --- a/.env.production +++ b/.env.production @@ -1,2 +1,3 @@ VITE_GITHUB_CLIENT_ID=6211bec51d65250aee37 VITE_WORKER_URL=https://worker.runruby.dev +VITE_ASSETS_URL = 'https://static.runruby.dev' diff --git a/.gitignore b/.gitignore index 4edcf69..5f4cdf4 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ tmp *.njsproj *.sln *.sw? +public/fs + diff --git a/bin/build.js b/bin/build.js new file mode 100755 index 0000000..02c2d2c --- /dev/null +++ b/bin/build.js @@ -0,0 +1,27 @@ +#!/usr/bin/env node + +import fs from "fs"; +import { join, dirname } from "path"; +import { fileURLToPath } from "url"; +import { execSync } from "child_process"; +import { snapshot } from "@webcontainer/snapshot"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +const fsDir = join(__dirname, "../public/fs"); +if (!fs.existsSync(fsDir)) { + fs.mkdirSync(fsDir, { recursive: true }); +} + +const stubs = await snapshot(join(__dirname, "../src/stubs")); +fs.writeFileSync(join(fsDir, "stubs"), stubs); + +execSync("npm i && npm run build", { + cwd: join(__dirname, "../packages/ruby-cli"), +}); + +const ruby = await snapshot(join(__dirname, "../packages/ruby-cli/dist")); + +fs.writeFileSync(join(fsDir, "ruby"), ruby); + +console.log("Snapshots created successfully."); diff --git a/package-lock.json b/package-lock.json index 57a9f34..7fac9cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,26 +15,32 @@ "@nanostores/react": "^0.7.1", "@ruby/3.3-wasm-wasi": "^2.6.2", "@ruby/wasm-wasi": "^2.5.0", + "@webcontainer/api": "^1.1.9", + "@webcontainer/snapshot": "^0.1.0", + "@xterm/addon-fit": "^0.9.0", + "@xterm/xterm": "^5.4.0", "dexie": "^3.2.5", "dexie-react-hooks": "^1.1.7", "file-saver": "^2.0.5", "jszip": "^3.10.1", "nanoid": "^5.0.5", - "nanostores": "^0.9.5", + "nanostores": "^0.10.0", "react": "^18.2.0", "react-arborist": "^3.4.0", "react-dom": "^18.2.0", "react-icons": "^5.0.1", - "react-responsive": "^9.0.2", - "strip-ansi": "^7.1.0" + "react-responsive": "^10.0.0", + "strip-ansi": "^7.1.0", + "xterm": "^5.3.0", + "xterm-addon-fit": "^0.8.0" }, "devDependencies": { "@types/file-saver": "^2.0.7", "@types/node": "^20.11.24", "@types/react": "^18.2.55", "@types/react-dom": "^18.2.19", - "@typescript-eslint/eslint-plugin": "^6.19.0", - "@typescript-eslint/parser": "^6.19.0", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", "@vitejs/plugin-react": "^4.2.1", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", @@ -1045,6 +1051,14 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/@msgpack/msgpack": { + "version": "3.0.0-beta2", + "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-3.0.0-beta2.tgz", + "integrity": "sha512-y+l1PNV0XDyY8sM3YtuMLK5vE3/hkfId+Do8pLo/OPxfxuFAUwcGz3oiiUuV46/aBpwTzZ+mRWVMtlSKbradhw==", + "engines": { + "node": ">= 14" + } + }, "node_modules/@nanostores/react": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/@nanostores/react/-/react-0.7.2.tgz", @@ -2051,6 +2065,35 @@ "vite": "^4.2.0 || ^5.0.0" } }, + "node_modules/@webcontainer/api": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webcontainer/api/-/api-1.2.0.tgz", + "integrity": "sha512-tzoKBd4lLdhHy5GHFpUkl+ndoSba8JqmB7x0ZQFnWfjbcbQOvKQfxA8MEMUYhgqjWHnbrWdAfnBEHz5f5lYG5A==" + }, + "node_modules/@webcontainer/snapshot": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@webcontainer/snapshot/-/snapshot-0.1.0.tgz", + "integrity": "sha512-PTIGQ3osUpTbK/dqB8RYbcZGv8IK+DJACx709z5sFbeIlngB3hUFpTEYFYs1SbUvr/AEQqvd0/bhc4ectrPRRw==", + "dependencies": { + "@msgpack/msgpack": "^3.0.0-beta2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@xterm/addon-fit": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.9.0.tgz", + "integrity": "sha512-hDlPPbTVPYyvwXu/asW8HbJkI/2RMi0cMaJnBZYVeJB0SWP2NeESMCNr+I7CvBlyI0sAxpxOg8Wk4OMkxBz9WA==", + "peerDependencies": { + "@xterm/xterm": "^5.0.0" + } + }, + "node_modules/@xterm/xterm": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", + "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==" + }, "node_modules/acorn": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", @@ -3552,9 +3595,9 @@ } }, "node_modules/matchmediaquery": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.3.1.tgz", - "integrity": "sha512-Hlk20WQHRIm9EE9luN1kjRjYXAQToHOIAHPJn9buxBwuhfTHoKUcX+lXBbxc85DVQfXYbEQ4HcwQdd128E3qHQ==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.4.2.tgz", + "integrity": "sha512-wrZpoT50ehYOudhDjt/YvUJc6eUzcdFPdmbizfgvswCKNHD1/OBOHYJpHie+HXpu6bSkEGieFMYk6VuutaiRfA==", "dependencies": { "css-mediaquery": "^0.1.2" } @@ -3652,9 +3695,9 @@ } }, "node_modules/nanostores": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/nanostores/-/nanostores-0.9.5.tgz", - "integrity": "sha512-Z+p+g8E7yzaWwOe5gEUB2Ox0rCEeXWYIZWmYvw/ajNYX8DlXdMvMDj8DWfM/subqPAcsf8l8Td4iAwO1DeIIRQ==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/nanostores/-/nanostores-0.10.3.tgz", + "integrity": "sha512-Nii8O1XqmawqSCf9o2aWqVxhKRN01+iue9/VEd1TiJCr9VT5XxgPFbF1Edl1XN6pwJcZRsl8Ki+z01yb/T/C2g==", "funding": [ { "type": "github", @@ -3662,7 +3705,7 @@ } ], "engines": { - "node": "^16.0.0 || ^18.0.0 || >=20.0.0" + "node": "^18.0.0 || >=20.0.0" } }, "node_modules/natural-compare": { @@ -4080,17 +4123,17 @@ } }, "node_modules/react-responsive": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-9.0.2.tgz", - "integrity": "sha512-+4CCab7z8G8glgJoRjAwocsgsv6VA2w7JPxFWHRc7kvz8mec1/K5LutNC2MG28Mn8mu6+bu04XZxHv5gyfT7xQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-10.0.0.tgz", + "integrity": "sha512-N6/UiRLGQyGUqrarhBZmrSmHi2FXSD++N5VbSKsBBvWfG0ZV7asvUBluSv5lSzdMyEVjzZ6Y8DL4OHABiztDOg==", "dependencies": { "hyphenate-style-name": "^1.0.0", - "matchmediaquery": "^0.3.0", + "matchmediaquery": "^0.4.2", "prop-types": "^15.6.1", - "shallow-equal": "^1.2.1" + "shallow-equal": "^3.1.0" }, "engines": { - "node": ">=0.10" + "node": ">=14" }, "peerDependencies": { "react": ">=16.8.0" @@ -4302,9 +4345,9 @@ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" }, "node_modules/shallow-equal": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", - "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-3.1.0.tgz", + "integrity": "sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg==" }, "node_modules/shebang-command": { "version": "2.0.0", @@ -4690,6 +4733,21 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/xterm": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/xterm/-/xterm-5.3.0.tgz", + "integrity": "sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg==", + "deprecated": "This package is now deprecated. Move to @xterm/xterm instead." + }, + "node_modules/xterm-addon-fit": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.8.0.tgz", + "integrity": "sha512-yj3Np7XlvxxhYF/EJ7p3KHaMt6OdwQ+HDu573Vx1lRXsVxOcnVJs51RgjZOouIZOczTsskaS+CpXspK81/DLqw==", + "deprecated": "This package is now deprecated. Move to @xterm/addon-fit instead.", + "peerDependencies": { + "xterm": "^5.0.0" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -5345,6 +5403,11 @@ "@monaco-editor/loader": "^1.4.0" } }, + "@msgpack/msgpack": { + "version": "3.0.0-beta2", + "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-3.0.0-beta2.tgz", + "integrity": "sha512-y+l1PNV0XDyY8sM3YtuMLK5vE3/hkfId+Do8pLo/OPxfxuFAUwcGz3oiiUuV46/aBpwTzZ+mRWVMtlSKbradhw==" + }, "@nanostores/react": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/@nanostores/react/-/react-0.7.2.tgz", @@ -5978,6 +6041,30 @@ "react-refresh": "^0.14.2" } }, + "@webcontainer/api": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webcontainer/api/-/api-1.2.0.tgz", + "integrity": "sha512-tzoKBd4lLdhHy5GHFpUkl+ndoSba8JqmB7x0ZQFnWfjbcbQOvKQfxA8MEMUYhgqjWHnbrWdAfnBEHz5f5lYG5A==" + }, + "@webcontainer/snapshot": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@webcontainer/snapshot/-/snapshot-0.1.0.tgz", + "integrity": "sha512-PTIGQ3osUpTbK/dqB8RYbcZGv8IK+DJACx709z5sFbeIlngB3hUFpTEYFYs1SbUvr/AEQqvd0/bhc4ectrPRRw==", + "requires": { + "@msgpack/msgpack": "^3.0.0-beta2" + } + }, + "@xterm/addon-fit": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.9.0.tgz", + "integrity": "sha512-hDlPPbTVPYyvwXu/asW8HbJkI/2RMi0cMaJnBZYVeJB0SWP2NeESMCNr+I7CvBlyI0sAxpxOg8Wk4OMkxBz9WA==", + "requires": {} + }, + "@xterm/xterm": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", + "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==" + }, "acorn": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", @@ -7092,9 +7179,9 @@ } }, "matchmediaquery": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.3.1.tgz", - "integrity": "sha512-Hlk20WQHRIm9EE9luN1kjRjYXAQToHOIAHPJn9buxBwuhfTHoKUcX+lXBbxc85DVQfXYbEQ4HcwQdd128E3qHQ==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.4.2.tgz", + "integrity": "sha512-wrZpoT50ehYOudhDjt/YvUJc6eUzcdFPdmbizfgvswCKNHD1/OBOHYJpHie+HXpu6bSkEGieFMYk6VuutaiRfA==", "requires": { "css-mediaquery": "^0.1.2" } @@ -7162,9 +7249,9 @@ "integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==" }, "nanostores": { - "version": "0.9.5", - "resolved": "https://registry.npmjs.org/nanostores/-/nanostores-0.9.5.tgz", - "integrity": "sha512-Z+p+g8E7yzaWwOe5gEUB2Ox0rCEeXWYIZWmYvw/ajNYX8DlXdMvMDj8DWfM/subqPAcsf8l8Td4iAwO1DeIIRQ==" + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/nanostores/-/nanostores-0.10.3.tgz", + "integrity": "sha512-Nii8O1XqmawqSCf9o2aWqVxhKRN01+iue9/VEd1TiJCr9VT5XxgPFbF1Edl1XN6pwJcZRsl8Ki+z01yb/T/C2g==" }, "natural-compare": { "version": "1.4.0", @@ -7442,14 +7529,14 @@ "dev": true }, "react-responsive": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-9.0.2.tgz", - "integrity": "sha512-+4CCab7z8G8glgJoRjAwocsgsv6VA2w7JPxFWHRc7kvz8mec1/K5LutNC2MG28Mn8mu6+bu04XZxHv5gyfT7xQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-10.0.0.tgz", + "integrity": "sha512-N6/UiRLGQyGUqrarhBZmrSmHi2FXSD++N5VbSKsBBvWfG0ZV7asvUBluSv5lSzdMyEVjzZ6Y8DL4OHABiztDOg==", "requires": { "hyphenate-style-name": "^1.0.0", - "matchmediaquery": "^0.3.0", + "matchmediaquery": "^0.4.2", "prop-types": "^15.6.1", - "shallow-equal": "^1.2.1" + "shallow-equal": "^3.1.0" } }, "react-window": { @@ -7595,9 +7682,9 @@ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" }, "shallow-equal": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", - "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-3.1.0.tgz", + "integrity": "sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg==" }, "shebang-command": { "version": "2.0.0", @@ -7840,6 +7927,17 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "xterm": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/xterm/-/xterm-5.3.0.tgz", + "integrity": "sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg==" + }, + "xterm-addon-fit": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.8.0.tgz", + "integrity": "sha512-yj3Np7XlvxxhYF/EJ7p3KHaMt6OdwQ+HDu573Vx1lRXsVxOcnVJs51RgjZOouIZOczTsskaS+CpXspK81/DLqw==", + "requires": {} + }, "yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package.json b/package.json index b08dccb..b1aa833 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,10 @@ "author": "Svyatoslav Kryukov ", "license": "MIT", "scripts": { - "dev": "vite", + "dev": "npm run build:wasm && vite", "build": "tsc && vite build", "format": "prettier --write .", + "build:wasm": "node bin/build.js", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview" }, @@ -20,26 +21,32 @@ "@nanostores/react": "^0.7.1", "@ruby/3.3-wasm-wasi": "^2.6.2", "@ruby/wasm-wasi": "^2.5.0", + "@webcontainer/api": "^1.1.9", + "@webcontainer/snapshot": "^0.1.0", + "@xterm/addon-fit": "^0.9.0", + "@xterm/xterm": "^5.4.0", "dexie": "^3.2.5", "dexie-react-hooks": "^1.1.7", "file-saver": "^2.0.5", "jszip": "^3.10.1", "nanoid": "^5.0.5", - "nanostores": "^0.9.5", + "nanostores": "^0.10.0", "react": "^18.2.0", "react-arborist": "^3.4.0", "react-dom": "^18.2.0", "react-icons": "^5.0.1", - "react-responsive": "^9.0.2", - "strip-ansi": "^7.1.0" + "react-responsive": "^10.0.0", + "strip-ansi": "^7.1.0", + "xterm": "^5.3.0", + "xterm-addon-fit": "^0.8.0" }, "devDependencies": { "@types/file-saver": "^2.0.7", "@types/node": "^20.11.24", "@types/react": "^18.2.55", "@types/react-dom": "^18.2.19", - "@typescript-eslint/eslint-plugin": "^6.19.0", - "@typescript-eslint/parser": "^6.19.0", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", "@vitejs/plugin-react": "^4.2.1", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", diff --git a/packages/ruby-cli/package-lock.json b/packages/ruby-cli/package-lock.json new file mode 100644 index 0000000..e738336 --- /dev/null +++ b/packages/ruby-cli/package-lock.json @@ -0,0 +1,1208 @@ +{ + "name": "ruby-cli", + "version": "0.1.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "ruby-cli", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@ruby/3.3-wasm-wasi": "^2.6.2", + "@ruby/wasm-wasi": "^2.6.2", + "process": "^0.11.10" + }, + "devDependencies": { + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-terser": "^0.4.4", + "@rollup/plugin-url": "^8.0.2", + "rollup": "^4.13.0", + "rollup-plugin-executable": "^1.6.3" + } + }, + "node_modules/@babel/runtime": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", + "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bjorn3/browser_wasi_shim": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@bjorn3/browser_wasi_shim/-/browser_wasi_shim-0.3.0.tgz", + "integrity": "sha512-FlRBYttPRLcWORzBe6g8nmYTafBkOEFeOqMYM4tAHJzFsQy4+xJA94z85a9BCs8S+Uzfh9LrkpII7DXr2iUVFg==" + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.2.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", + "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.2.1", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-terser": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", + "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==", + "dev": true, + "dependencies": { + "serialize-javascript": "^6.0.1", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-url": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-url/-/plugin-url-8.0.2.tgz", + "integrity": "sha512-5yW2LP5NBEgkvIRSSEdJkmxe5cUNZKG3eenKtfJvSkxVm/xTTu7w+ayBtNwhozl1ZnTUCU0xFaRQR+cBl2H7TQ==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "make-dir": "^3.1.0", + "mime": "^3.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", + "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", + "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", + "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", + "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", + "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", + "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", + "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", + "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", + "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", + "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", + "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", + "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", + "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@ruby/3.3-wasm-wasi": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@ruby/3.3-wasm-wasi/-/3.3-wasm-wasi-2.6.2.tgz", + "integrity": "sha512-HyJ3nIlciQ1FnF2RBKEHwMKtCzgbD1EjCxiCFntpoN6VLAVZNJe/uTsTj4XZRcUPov272fg33YERhiu/abtffQ==", + "dependencies": { + "@ruby/wasm-wasi": "^2.0.0" + } + }, + "node_modules/@ruby/wasm-wasi": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@ruby/wasm-wasi/-/wasm-wasi-2.6.2.tgz", + "integrity": "sha512-+y8uCYbtvDZZEU8VxM9JX6KoZJTvfmcEhRTIXdkxXwp/NFR7EE21pBEVYU+zzhCyKxRbmXp83BeTAYZuxe++HQ==", + "dependencies": { + "@bjorn3/browser_wasi_shim": "^0.3.0", + "tslib": "^2.6.3" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rollup": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", + "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.13.0", + "@rollup/rollup-android-arm64": "4.13.0", + "@rollup/rollup-darwin-arm64": "4.13.0", + "@rollup/rollup-darwin-x64": "4.13.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", + "@rollup/rollup-linux-arm64-gnu": "4.13.0", + "@rollup/rollup-linux-arm64-musl": "4.13.0", + "@rollup/rollup-linux-riscv64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-musl": "4.13.0", + "@rollup/rollup-win32-arm64-msvc": "4.13.0", + "@rollup/rollup-win32-ia32-msvc": "4.13.0", + "@rollup/rollup-win32-x64-msvc": "4.13.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-executable": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/rollup-plugin-executable/-/rollup-plugin-executable-1.6.3.tgz", + "integrity": "sha512-634q95UyowXcX5yu3zIxVQ5aWgE3WVE1zdIiwKangMYt1Lw0T0MfCHEPYvZP/0V7ni2NJr/4iVseIgCjkuSA1A==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.14.0" + }, + "engines": { + "node": ">=10.0.0", + "npm": ">=4.0.0", + "yarn": ">=1.0.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/smob": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.4.1.tgz", + "integrity": "sha512-9LK+E7Hv5R9u4g4C3p+jjLstaLe11MDsL21UpYaCNmapvMkYhqCV4A/f/3gyH8QjMyh6l68q9xC85vihY9ahMQ==", + "dev": true + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/terser": { + "version": "5.29.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.2.tgz", + "integrity": "sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + } + }, + "dependencies": { + "@babel/runtime": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", + "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.14.0" + } + }, + "@bjorn3/browser_wasi_shim": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@bjorn3/browser_wasi_shim/-/browser_wasi_shim-0.3.0.tgz", + "integrity": "sha512-FlRBYttPRLcWORzBe6g8nmYTafBkOEFeOqMYM4tAHJzFsQy4+xJA94z85a9BCs8S+Uzfh9LrkpII7DXr2iUVFg==" + }, + "@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true + }, + "@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "@rollup/plugin-node-resolve": { + "version": "15.2.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", + "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.2.1", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + } + }, + "@rollup/plugin-terser": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", + "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==", + "dev": true, + "requires": { + "serialize-javascript": "^6.0.1", + "smob": "^1.0.0", + "terser": "^5.17.4" + } + }, + "@rollup/plugin-url": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-url/-/plugin-url-8.0.2.tgz", + "integrity": "sha512-5yW2LP5NBEgkvIRSSEdJkmxe5cUNZKG3eenKtfJvSkxVm/xTTu7w+ayBtNwhozl1ZnTUCU0xFaRQR+cBl2H7TQ==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^5.0.1", + "make-dir": "^3.1.0", + "mime": "^3.0.0" + } + }, + "@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + } + }, + "@rollup/rollup-android-arm-eabi": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", + "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-android-arm64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", + "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-arm64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", + "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-x64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", + "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", + "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", + "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-musl": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", + "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-riscv64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", + "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", + "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-musl": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", + "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-arm64-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", + "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-ia32-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", + "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-x64-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", + "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "dev": true, + "optional": true + }, + "@ruby/3.3-wasm-wasi": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@ruby/3.3-wasm-wasi/-/3.3-wasm-wasi-2.6.2.tgz", + "integrity": "sha512-HyJ3nIlciQ1FnF2RBKEHwMKtCzgbD1EjCxiCFntpoN6VLAVZNJe/uTsTj4XZRcUPov272fg33YERhiu/abtffQ==", + "requires": { + "@ruby/wasm-wasi": "^2.0.0" + } + }, + "@ruby/wasm-wasi": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@ruby/wasm-wasi/-/wasm-wasi-2.6.2.tgz", + "integrity": "sha512-+y8uCYbtvDZZEU8VxM9JX6KoZJTvfmcEhRTIXdkxXwp/NFR7EE21pBEVYU+zzhCyKxRbmXp83BeTAYZuxe++HQ==", + "requires": { + "@bjorn3/browser_wasi_shim": "^0.3.0", + "tslib": "^2.6.3" + } + }, + "@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true + }, + "acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true + }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true + }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "requires": { + "function-bind": "^1.1.2" + } + }, + "is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "requires": { + "builtin-modules": "^3.3.0" + } + }, + "is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "requires": { + "hasown": "^2.0.0" + } + }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, + "resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "requires": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "rollup": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", + "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "dev": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.13.0", + "@rollup/rollup-android-arm64": "4.13.0", + "@rollup/rollup-darwin-arm64": "4.13.0", + "@rollup/rollup-darwin-x64": "4.13.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", + "@rollup/rollup-linux-arm64-gnu": "4.13.0", + "@rollup/rollup-linux-arm64-musl": "4.13.0", + "@rollup/rollup-linux-riscv64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-musl": "4.13.0", + "@rollup/rollup-win32-arm64-msvc": "4.13.0", + "@rollup/rollup-win32-ia32-msvc": "4.13.0", + "@rollup/rollup-win32-x64-msvc": "4.13.0", + "@types/estree": "1.0.5", + "fsevents": "~2.3.2" + } + }, + "rollup-plugin-executable": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/rollup-plugin-executable/-/rollup-plugin-executable-1.6.3.tgz", + "integrity": "sha512-634q95UyowXcX5yu3zIxVQ5aWgE3WVE1zdIiwKangMYt1Lw0T0MfCHEPYvZP/0V7ni2NJr/4iVseIgCjkuSA1A==", + "dev": true, + "requires": { + "@babel/runtime": "^7.14.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + }, + "serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "smob": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.4.1.tgz", + "integrity": "sha512-9LK+E7Hv5R9u4g4C3p+jjLstaLe11MDsL21UpYaCNmapvMkYhqCV4A/f/3gyH8QjMyh6l68q9xC85vihY9ahMQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "terser": { + "version": "5.29.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.2.tgz", + "integrity": "sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==", + "dev": true, + "requires": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + } + }, + "tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + } + } +} diff --git a/packages/ruby-cli/package.json b/packages/ruby-cli/package.json new file mode 100644 index 0000000..db0305b --- /dev/null +++ b/packages/ruby-cli/package.json @@ -0,0 +1,23 @@ +{ + "name": "ruby-cli", + "version": "0.1.0", + "description": "Ruby.wasm CLI", + "main": "src/index.js", + "author": "", + "license": "MIT", + "scripts": { + "build": "rollup -c" + }, + "dependencies": { + "@ruby/3.3-wasm-wasi": "^2.6.2", + "@ruby/wasm-wasi": "^2.6.2", + "process": "^0.11.10" + }, + "devDependencies": { + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-terser": "^0.4.4", + "@rollup/plugin-url": "^8.0.2", + "rollup": "^4.13.0", + "rollup-plugin-executable": "^1.6.3" + } +} diff --git a/packages/ruby-cli/rollup.config.mjs b/packages/ruby-cli/rollup.config.mjs new file mode 100644 index 0000000..7b364a1 --- /dev/null +++ b/packages/ruby-cli/rollup.config.mjs @@ -0,0 +1,26 @@ +import { nodeResolve } from "@rollup/plugin-node-resolve"; +import executable from "rollup-plugin-executable"; +import terser from "@rollup/plugin-terser"; +import url from "@rollup/plugin-url"; + +export default [ + { + input: { + ruby: `src/ruby.js`, + bundle: `src/bundler.js` + }, + output: { + dir: "dist", + format: "cjs", + entryFileNames: "[name]" + }, + makeAbsoluteExternalsRelative: true, + external: ["node:fs", "node:wasi", "node:buffer"], + plugins: [ + nodeResolve({ extensions: [".js", ".wasm"] }), + url({ include: ["**/*.wasm"] }), + terser(), + executable() + ] + } +]; diff --git a/packages/ruby-cli/src/bundler.js b/packages/ruby-cli/src/bundler.js new file mode 100644 index 0000000..a7a7d51 --- /dev/null +++ b/packages/ruby-cli/src/bundler.js @@ -0,0 +1,75 @@ +#!/usr/bin/env node + +import process from "process"; +import { initializeRuby } from "./rubyVM"; +import path from "path"; +import { readFileSync } from "node:fs"; + +const bundler = async () => { + const ruby = await initializeRuby([]); + const args = process.argv.slice(2); + + const setRubyEnv = (rubyInstance, rubyArgs) => { + let preset = `ARGV = ${JSON.stringify(rubyArgs)};`; + Object.entries(process.env).forEach(([k, v]) => { + preset += `ENV["${k}"] = "${v}";`; + }); + rubyInstance.eval(preset); + }; + + const executeRubyFile = async (rubyInstance, filePath) => { + const fileContent = readFileSync(filePath, "utf-8"); + await rubyInstance.evalAsync(` + require "rubygems_stub" + require "bundler_stub" + require "bundler/setup" + + eval(<<~'CODE', binding, '${filePath}', 1) + ${fileContent} + CODE + `); + }; + + const executeBundlerCLI = async (rubyInstance, cliArgs) => { + rubyInstance.eval(`ARGV = ${JSON.stringify(cliArgs)}`); + + await rubyInstance.evalAsync(` + require "rubygems_stub" + require "thread_stub" + require "bundler_stub" + require "bundler" + require "bundler/friendly_errors" + Bundler.with_friendly_errors do + require "bundler/cli" + help_flags = %w[--help -h] + help_flag_used = ARGV.any? { |a| help_flags.include? a } + args = help_flag_used ? Bundler::CLI.reformatted_help_args(ARGV) : ARGV + Bundler::CLI.start(args, debug: true) + end + `); + }; + + if (args[0] === "exec" && args[1] === "ruby") { + const rubyArgs = args.slice(2); + const rubyFile = rubyArgs.pop(); + + if (!rubyFile) { + console.error("Ruby file doesn't exist"); + process.exit(1); + } + + setRubyEnv(ruby, rubyArgs); + + const filePath = path.resolve(rubyFile); + await executeRubyFile(ruby, filePath); + } else { + await executeBundlerCLI(ruby, args); + } +}; + +bundler() + .then(() => process.exit(0)) + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/packages/ruby-cli/src/ruby.js b/packages/ruby-cli/src/ruby.js new file mode 100644 index 0000000..b109042 --- /dev/null +++ b/packages/ruby-cli/src/ruby.js @@ -0,0 +1,44 @@ +#!/usr/bin/env node + +import process from "process"; +import { initializeRuby } from "./rubyVM"; + +const main = async () => { + const args = process.argv.slice(2); + + const ruby = await initializeRuby(args); + let preset = `ARGV = ${JSON.stringify(args)};`; + Object.entries(process.env).forEach(([k, v]) => { + preset += `ENV["${k}"] = "${v}";`; + }); + ruby.eval(preset); + + args.forEach((arg, i) => { + if (arg.startsWith("-e")) { + let code = arg.slice(2); + if (code.startsWith("=")) { + ruby.evalAsync(code.slice(1)); + } else if (code.length === 0) { + ruby.evalAsync(process.argv[i + 3]); + } + ruby.evalAsync(code); + } + }); + + let fileToRun; + args.forEach((arg, i) => { + if (arg.startsWith("-")) { + fileToRun = undefined; + } else if (args[i - 1]?.startsWith("-") && args[i - 1].length === 2) { + fileToRun = undefined; + } else { + fileToRun ||= arg; + } + }); + + if (fileToRun) { + await ruby.evalAsync(`load "${fileToRun}"`); + } +}; + +main().then(() => process.exit(0)); diff --git a/packages/ruby-cli/src/ruby.wasm b/packages/ruby-cli/src/ruby.wasm new file mode 100644 index 0000000..262ba35 Binary files /dev/null and b/packages/ruby-cli/src/ruby.wasm differ diff --git a/packages/ruby-cli/src/rubyVM.js b/packages/ruby-cli/src/rubyVM.js new file mode 100644 index 0000000..2ad35a1 --- /dev/null +++ b/packages/ruby-cli/src/rubyVM.js @@ -0,0 +1,55 @@ +import { readFileSync } from "node:fs"; +import path from "path"; +import { WASI } from "node:wasi"; +import { consolePrinter, RubyVM } from "@ruby/wasm-wasi"; +import wasmFilePath from "../node_modules/@ruby/3.3-wasm-wasi/dist/ruby.debug+stdlib.wasm"; + +const rubyStubsPath = "/gem_stubs"; +const defaultStubsPath = `${rubyStubsPath}/default_stubs.rb`; +const rootFolder = "/home/runruby"; + +export const initializeRuby = async (args = []) => { + const wasmModule = await WebAssembly.compile( + // eslint-disable-next-line no-undef + readFileSync(path.resolve(__dirname, wasmFilePath)), + ); + + const wasi = new WASI({ + version: "preview1", + preopens: { + "/": rootFolder, + }, + env: { + BUNDLE_DEFAULT_INSTALL_USES_PATH: "/bundle", + }, + }); + + const rubyVM = new RubyVM(); + const imports = { + wasi_snapshot_preview1: wasi.wasiImport, + }; + + rubyVM.addToImports(imports); + + const printer = consolePrinter(); + printer.addToImports(imports); + + const instance = await WebAssembly.instantiate(wasmModule, imports); + + await rubyVM.setInstance(instance); + + printer.setMemory(instance.exports.memory); + + wasi.initialize(instance); + + rubyVM.initialize([ + "ruby.wasm", + "-e_=0", + "-EUTF-8", + `-I${rubyStubsPath}`, + `-r${defaultStubsPath}`, + ...args, + ]); + + return rubyVM; +}; diff --git a/runruby-worker/package-lock.json b/runruby-worker/package-lock.json index 0e60cc0..ac5cbcf 100644 --- a/runruby-worker/package-lock.json +++ b/runruby-worker/package-lock.json @@ -11,7 +11,7 @@ "devDependencies": { "@cloudflare/workers-types": "^4.20240222.0", "@types/node": "^20.11.24", - "itty-router": "^4.0.27", + "itty-router": "^5.0.17", "typescript": "^5.0.4", "wrangler": "^3.0.0" } @@ -882,10 +882,11 @@ } }, "node_modules/itty-router": { - "version": "4.0.27", - "resolved": "https://registry.npmjs.org/itty-router/-/itty-router-4.0.27.tgz", - "integrity": "sha512-Q3/GOE2EJvyu3hhxGN3WDWh3QNg4v7h1KFx/jSLcIOOkpSI1jUFTgGefEESXon4j5YwqCIf0DEemjiVAFSBiUw==", - "dev": true + "version": "5.0.17", + "resolved": "https://registry.npmjs.org/itty-router/-/itty-router-5.0.17.tgz", + "integrity": "sha512-ZHnPI0OOyTTLuNp2FdciejYaK4Wl3ZV3O0yEm8njOGggh/k/ek3BL7X2I5YsCOfc5vLhIJgj3Z4pUtLs6k9Ucg==", + "dev": true, + "license": "MIT" }, "node_modules/magic-string": { "version": "0.25.9", @@ -1821,9 +1822,9 @@ "dev": true }, "itty-router": { - "version": "4.0.27", - "resolved": "https://registry.npmjs.org/itty-router/-/itty-router-4.0.27.tgz", - "integrity": "sha512-Q3/GOE2EJvyu3hhxGN3WDWh3QNg4v7h1KFx/jSLcIOOkpSI1jUFTgGefEESXon4j5YwqCIf0DEemjiVAFSBiUw==", + "version": "5.0.17", + "resolved": "https://registry.npmjs.org/itty-router/-/itty-router-5.0.17.tgz", + "integrity": "sha512-ZHnPI0OOyTTLuNp2FdciejYaK4Wl3ZV3O0yEm8njOGggh/k/ek3BL7X2I5YsCOfc5vLhIJgj3Z4pUtLs6k9Ucg==", "dev": true }, "magic-string": { diff --git a/runruby-worker/package.json b/runruby-worker/package.json index 5e2824d..9adfcf8 100644 --- a/runruby-worker/package.json +++ b/runruby-worker/package.json @@ -13,7 +13,7 @@ "devDependencies": { "@cloudflare/workers-types": "^4.20240222.0", "@types/node": "^20.11.24", - "itty-router": "^4.0.27", + "itty-router": "^5.0.17", "typescript": "^5.0.4", "wrangler": "^3.0.0" } diff --git a/runruby-worker/src/index.ts b/runruby-worker/src/index.ts index ebc6f99..1dbde85 100644 --- a/runruby-worker/src/index.ts +++ b/runruby-worker/src/index.ts @@ -1,26 +1,44 @@ -import { createCors, error, html, json, Router, withCookies } from "itty-router"; - +import { cors, error, html, json, Router, withCookies } from "itty-router"; import apiRouter from "./apiRouter"; import createSession from "./withEncryptedSession"; import compactIndexSpecs from "./compactIndexSpecs"; import proxy from "./proxy"; +const dynamicOrigins = [ + /^https:\/\/httpslocalrunrubydev5173-[a-z0-9-]+\.w-corp-staticblitz\.com$/, + /^https:\/\/httpslocalhost5173-[a-z0-9-]+\.w-corp-staticblitz\.com$/, +]; + const { withEncryptedSession, setSessionCookie } = createSession(); -const { preflight, corsify } = createCors({ - methods: ["GET", "PATCH", "POST"], - // TODO: use env - origins: ["http://localhost:5173", "https://localhost:5173", "https://runruby.dev", "https://local.runruby.dev:5173"], - headers: { - "Access-Control-Allow-Credentials": "true", + +const { preflight, corsify } = cors({ + allowMethods: ["GET", "PATCH", "POST"], + origin: (origin: string) => { + const staticOrigins = [ + "http://localhost:5173", + "https://localhost:5173", + "https://runruby.dev", + "https://local.runruby.dev:5173", + "https://local.runruby.dev:5173/", + ]; + + if (staticOrigins.includes(origin) || dynamicOrigins.some((regex) => regex.test(origin))) { + return origin; + } + + return; }, + credentials: true, + allowHeaders: ["Access-Control-Allow-Credentials", "Access-Control-Allow-Origin"], }); -const router = Router(); +const router = Router({ + before: [withCookies, preflight, withEncryptedSession], + catch: error, + finally: [json, setSessionCookie, corsify], +}); router - .all("*", withCookies) - .all("*", preflight) - .all("*", withEncryptedSession) .all("/api/*", apiRouter.handle) .get("/proxy/*", proxy) .get("/compact_index_specs", compactIndexSpecs) @@ -28,6 +46,5 @@ router .all("*", () => new Response("Not Found.", { status: 404 })); export default { - fetch: (req: Request, env: Env, ctx: ExecutionContext) => - router.handle(req, env, ctx).then(json).catch(error).then(setSessionCookie).then(corsify), + fetch: router.fetch, }; diff --git a/runruby-worker/worker-configuration.d.ts b/runruby-worker/worker-configuration.d.ts index 32f3e20..48c919f 100644 --- a/runruby-worker/worker-configuration.d.ts +++ b/runruby-worker/worker-configuration.d.ts @@ -5,4 +5,6 @@ interface Env { GITHUB_CLIENT_ID: string; // GitHub OAuth app client secret GITHUB_CLIENT_SECRET: string; + // Allowed hosts for CORS + ALLOWED_HOSTS: string; } diff --git a/runruby-worker/wrangler.toml b/runruby-worker/wrangler.toml index 3eb6777..5765821 100644 --- a/runruby-worker/wrangler.toml +++ b/runruby-worker/wrangler.toml @@ -2,3 +2,6 @@ name = "runruby-worker" main = "src/index.ts" compatibility_flags = ["nodejs_compat"] compatibility_date = "2024-02-23" + +[vars] +ALLOWED_HOSTS = "localhost,runruby.dev,*.runruby.dev,*.w-corp-staticblitz.com" diff --git a/src/components/Content/Content.tsx b/src/components/Content/Content.tsx index b5444f4..9cb1c97 100644 --- a/src/components/Content/Content.tsx +++ b/src/components/Content/Content.tsx @@ -1,7 +1,7 @@ import { useEffect, useRef } from "react"; import { useLiveQuery } from "dexie-react-hooks"; -import { useVM } from "../../useVM.ts"; +//import { useVM } from "../../useVM.ts"; import { Output } from "../Output/Output.tsx"; import { Editor } from "../Editor/Editor.tsx"; import { Menu } from "../Menu/Menu.tsx"; @@ -12,7 +12,7 @@ import { $menu, toggleMenu } from "../../stores/menu.ts"; import { useStore } from "@nanostores/react"; export const Content = () => { - const { loading, log, result, runVM } = useVM(); + //const { loading, runVM } = useVM(); const gemsDirContent = useLiveQuery( () => db.fsCache.get({ key: "gemsDir" }), @@ -47,10 +47,10 @@ export const Content = () => {
- +
- +
); diff --git a/src/components/Editor/Editor.module.css b/src/components/Editor/Editor.module.css index 5b6d7e7..7b1b304 100644 --- a/src/components/Editor/Editor.module.css +++ b/src/components/Editor/Editor.module.css @@ -45,74 +45,3 @@ font-size: 14px; padding: 32px 32px; } - -.editorFooter { - padding: 16px; - text-align: right; - height: auto; - max-height: 50px; - border-top: 0.5px #ffffff14 solid; -} - -@media (max-width: 1280px) { - .editorFooter { - padding: 8px; - } -} - -.editorLoading { - margin: 0 16px; - display: inline-flex; - vertical-align: middle; -} - -.editorLoading svg { - animation: spin 1s linear infinite; -} - -@keyframes spin { - 100% { - transform: rotate(360deg); - } -} - -.installButton { - background-color: var(--secondary-color); - border: none; - color: var(--font-color); - font-weight: bold; - padding: 8px 16px; - text-align: center; - text-decoration: none; - display: inline-block; - font-size: 12px; - cursor: default; - border-radius: 8px; -} - -.installButton:hover { - background-color: var(--secondary-hover-color); -} - -.runButton { - background-color: var(--primary-color); - border: none; - color: var(--font-color); - font-weight: bold; - padding: 8px 16px; - text-align: center; - text-decoration: none; - display: inline-block; - font-size: 12px; - cursor: default; - border-radius: 8px; - margin-left: 8px; -} - -.runButton:hover { - background-color: var(--primary-hover-color); -} - -.buttonDisabled { - background-color: var(--tag-color) !important; -} diff --git a/src/components/Editor/Editor.tsx b/src/components/Editor/Editor.tsx index fe10023..fa800b6 100644 --- a/src/components/Editor/Editor.tsx +++ b/src/components/Editor/Editor.tsx @@ -1,121 +1,30 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useEffect, useState } from "react"; import MonacoEditor from "@monaco-editor/react"; import { useStore } from "@nanostores/react"; -import { VscLoading } from "react-icons/vsc"; -import { decode, writeFile } from "../../engines/wasi/editorFS.ts"; -import { RunVMParams } from "../../useVM.ts"; -import { db } from "../../db.ts"; -import { bundleDir, gemsDir } from "../../engines/wasi/wasi.ts"; -import { refreshCacheInfo } from "../../stores/cache.ts"; -import { openTab } from "../../stores/output.ts"; +import { decode } from "../../engines/wasi/editorFS.ts"; import { - $editor, currentFilePathStore, currentFileStore, - refreshTreeData, setCode, } from "../../stores/editor.ts"; import { useEditorTheme } from "../../useEditorTheme.ts"; -import { getQueryParam } from "../../fsInitializer.ts"; import cs from "./Editor.module.css"; +import { webcontainerInstance } from "../../engines/webcontainers"; -type EditorProps = { - loading: boolean; - runVM: (RunVMParams: RunVMParams) => void; -}; -export const Editor = ({ loading: VMRunning, runVM }: EditorProps) => { +export const Editor = () => { const [editorInitializing, setInitializing] = useState(true); - const bundleInstalled = useRef(false); - - const loading = VMRunning || editorInitializing; - const { treeData, tree, code } = useStore($editor); const currentFilePath = useStore(currentFilePathStore); const currentFile = useStore(currentFileStore); - const canRunBundleInstall = useMemo( - () => !loading && treeData.find((entry) => entry.name === "Gemfile"), - [loading, treeData], - ); - - const canRunCode = useMemo( - () => !loading && currentFilePath?.endsWith(".rb"), - [currentFilePath, loading], - ); - - const runCode = useCallback(() => { - if (!currentFilePath?.endsWith(".rb")) return; - - runVM({ - code: ` - ${ - canRunBundleInstall - ? `require "rubygems_stub" - require "bundler_stub" - require "bundler/setup"` - : "" - } - eval(<<~'CODE', binding, '${currentFilePath}', 1) - ${code} - CODE - `, - onBefore: () => { - openTab("logs"); - }, - onSuccess: () => { - openTab("logs"); - }, - onError: () => { - openTab("logs"); - }, - }); - }, [canRunBundleInstall, code, currentFilePath, runVM]); - - const bundleInstall = useCallback(() => { - runVM({ - code: `require "rubygems_stub" - require "thread_stub" - require "bundler_stub" - require "bundler/cli" - require "bundler/cli/install" - begin - Bundler::CLI::Install.new({path: './gems'}).run - rescue StandardError => e - $stderr << e.message << "\\n" << e.backtrace.join("\\n") - end - `, - onBefore: () => { - openTab("logs"); - }, - onSuccess: () => { - openTab("logs"); - db.fsCache.clear().then(async () => { - await db.fsCache.add({ - key: "gemsDir", - data: gemsDir.dir.contents as never, - }); - await db.fsCache.add({ - key: "bundleDir", - data: bundleDir.dir.contents as never, - }); - refreshCacheInfo(); - }); - }, - onError: () => { - openTab("logs"); - }, - onFinally: refreshTreeData, - }); - }, [runVM]); - - const activateFirstFile = useCallback(() => { - if (tree) { - const node = tree.visibleNodes.find((n) => n.data.name?.endsWith(".rb")); - node ? node.activate() : tree?.firstNode?.activate(); - } - }, [tree]); + // const activateFirstFile = useCallback(() => { + // if (tree) { + // const node = tree.visibleNodes.find((n) => n.data.name?.endsWith(".rb")); + // node ? node.activate() : tree?.firstNode?.activate(); + // } + // }, [tree]); useEffect(() => { if (currentFile === null) return; @@ -123,29 +32,20 @@ export const Editor = ({ loading: VMRunning, runVM }: EditorProps) => { setCode(decode(currentFile.data)); }, [currentFile]); - useEffect(() => { - if (editorInitializing && treeData) { - activateFirstFile(); - } else if (!bundleInstalled.current) { - (getQueryParam("gem") || getQueryParam("gist")) && - !getQueryParam("embed") && - canRunBundleInstall && - bundleInstall(); - bundleInstalled.current = true; - } - }, [ - treeData, - activateFirstFile, - bundleInstall, - canRunBundleInstall, - editorInitializing, - ]); + // useEffect(() => { + // if (editorInitializing && treeData) { + // activateFirstFile(); + // } else if (!bundleInstalled.current) { + // ((getQueryParam("gem") || getQueryParam("gist")) && !getQueryParam("embed")) && canRunBundleInstall && bundleInstall(); + // bundleInstalled.current = true; + // } + // }, [treeData, activateFirstFile, bundleInstall, canRunBundleInstall, editorInitializing]); const handleEditorChange = (value: string | undefined) => { setCode(value || ""); if (currentFilePath) { - writeFile(currentFilePath, value || ""); + webcontainerInstance.fs.writeFile(currentFilePath, value || ""); } }; @@ -190,25 +90,6 @@ export const Editor = ({ loading: VMRunning, runVM }: EditorProps) => { ) )} -
-
- {loading && } -
- - -
); }; diff --git a/src/components/Menu/Menu.tsx b/src/components/Menu/Menu.tsx index 3360948..d522371 100644 --- a/src/components/Menu/Menu.tsx +++ b/src/components/Menu/Menu.tsx @@ -7,7 +7,6 @@ import { Tree, TreeApi, } from "react-arborist"; -import { Directory } from "@bjorn3/browser_wasi_shim"; import { nanoid } from "nanoid"; import { VscFileZip, @@ -17,9 +16,9 @@ import { } from "react-icons/vsc"; import { useStore } from "@nanostores/react"; -import { mkdir, rename, rm, writeFile } from "../../engines/wasi/editorFS.ts"; +import { rm } from "../../engines/wasi/editorFS.ts"; import { Node } from "../Node/Node.tsx"; -import { Entity, idsMap, sortChildren } from "../../fsMap.ts"; +import { Entity } from "../../fsMap.ts"; import cs from "../Menu/Menu.module.css"; import { $editor, @@ -31,40 +30,37 @@ import { } from "../../stores/editor.ts"; import { $gist } from "../../stores/gists.ts"; import { downloadZip } from "../../downloadZip.ts"; +import { webcontainerInstance } from "../../engines/webcontainers"; export const Menu = () => { const { treeData, tree } = useStore($editor); const currentFilePath = useStore(currentFilePathStore); - const onRename: RenameHandler = ({ name, node }) => { + const onRename: RenameHandler = async ({ name, node }) => { const oldPath = node.data.fullPath; const newPath = oldPath.replace(new RegExp(`${node.data.name}$`), name); - if (rename(oldPath, newPath)) { - refreshTreeData(); - - setTimeout(() => { - $editorVersion.set($editorVersion.get() + 1); - }, 20); - } + await webcontainerInstance?.fs?.rename(oldPath, newPath); + // TODO check if needed + $editorVersion.set($editorVersion.get() + 1); }; - const onCreate: CreateHandler = ({ parentNode, type }) => { + const onCreate: CreateHandler = async ({ parentNode, type }) => { const name = type === "leaf" ? `new_file_${Date.now()}.rb` : `new_dir_${Date.now()}`; const path = `${parentNode ? parentNode.data.fullPath : ""}/${name}`; - const object = type === "leaf" ? writeFile(path, "") : mkdir(path); - - if (!object) { - throw new Error( - `Failed creating ${type === "leaf" ? "file" : "directory"} in the path: ${path}`, - ); + if (type === "leaf") { + await webcontainerInstance?.fs?.writeFile(path, ""); + } else { + await webcontainerInstance?.fs?.mkdir(path); } - const id = nanoid(); - idsMap.set(object, id); - refreshTreeData(); - return { id, name, object }; + // const object = (type === "leaf") ? writeFile(path, "") : mkdir(path); + // + // const id = nanoid(); + // idsMap.set(object, id); + // refreshTreeData(); + return { id: nanoid(), name, object: null }; }; const onDelete: DeleteHandler = ({ ids }) => { @@ -145,18 +141,15 @@ export const Menu = () => { width="100%" openByDefault={false} data={treeData} - childrenAccessor={({ object, fullPath }) => - object instanceof Directory ? sortChildren(object, fullPath) : null - } ref={setTreeRef as never} disableDrag={true} disableDrop={true} onRename={onRename} onCreate={onCreate} onDelete={onDelete} - onActivate={(node: NodeApi) => { + onActivate={(node: NodeApi) => { if (node.isLeaf) { - setCurrentNodeId(node.id); + setCurrentNodeId(node.data.fullPath); } }} > diff --git a/src/components/Output/Output.module.css b/src/components/Output/Output.module.css index b473655..922c86b 100644 --- a/src/components/Output/Output.module.css +++ b/src/components/Output/Output.module.css @@ -1,15 +1,20 @@ -.editorText { +.tab { padding: 0 16px; flex: 1; overflow-y: scroll; + display: none; } @media (max-width: 1280px) { - .editorText { + .tab { padding: 0 8px; } } +.activeTab { + display: block; +} + .switchButtons { display: flex; flex-direction: row; diff --git a/src/components/Output/Output.tsx b/src/components/Output/Output.tsx index b8a0355..640924f 100644 --- a/src/components/Output/Output.tsx +++ b/src/components/Output/Output.tsx @@ -1,38 +1,24 @@ -import MonacoEditor from "@monaco-editor/react"; -import stripAnsi from "strip-ansi"; import { useStore } from "@nanostores/react"; -import { useMemo } from "react"; -import { useEditorTheme } from "../../useEditorTheme.ts"; import { $output, openTab } from "../../stores/output.ts"; import { SettingsTab } from "./SettingsTab.tsx"; import { InfoTab } from "./InfoTab.tsx"; +import { Terminal } from "./Terminal.tsx"; import cs from "./Output.module.css"; -export const Output = ({ result, log }: { result: string; log: string[] }) => { +export const Output = () => { const outputStore = useStore($output); const { activeTab } = outputStore; - const value = useMemo(() => { - let v = ""; - v += stripAnsi(log.join("\n")); - if (result) { - if (v) v += "\n\n"; - v += ">> " + result; - } - return v; - }, [log, result]); - const theme = useEditorTheme(); - return ( <>
-
- {activeTab === "logs" ? ( - - ) : activeTab === "info" ? ( - - ) : ( - - )} +
+ +
+
+ +
+
+
); diff --git a/src/components/Output/Terminal.tsx b/src/components/Output/Terminal.tsx new file mode 100644 index 0000000..d231403 --- /dev/null +++ b/src/components/Output/Terminal.tsx @@ -0,0 +1,25 @@ +import { useEffect, useState } from "react"; +import { Terminal as Xterm } from "@xterm/xterm"; +import "@xterm/xterm/css/xterm.css"; + +import { startShell } from "../../engines/webcontainers"; + +export const Terminal = () => { + const [terminalRef, setTerminalRef] = useState(); + + useEffect(() => { + if (!terminalRef) return; + const xterm = new Xterm({ + convertEol: true, + }); + startShell(xterm).then(() => { + xterm.open(terminalRef); + }); + + return () => { + xterm.dispose(); + }; + }, [terminalRef]); + + return
node && setTerminalRef(node)} />; +}; diff --git a/src/engines/wasi/wasi.ts b/src/engines/wasi/wasi.ts index 4036809..9b3f108 100644 --- a/src/engines/wasi/wasi.ts +++ b/src/engines/wasi/wasi.ts @@ -19,6 +19,8 @@ import { composeInitialFS } from "../../fsInitializer.ts"; const wasmModulePromise = fetch(wasmUrl).then((response) => WebAssembly.compileStreaming(response), ); +//const wasmUrl = "https://cdn.jsdelivr.net/npm/@ruby/3.3-wasm-wasi/dist/ruby+stdlib.wasm"; +//const wasmModulePromise = fetch(wasmUrl).then((response) => WebAssembly.compileStreaming(response)); const rubyStubsPath = "/usr/local/lib/ruby_gems"; const emptyMap = new Map(); diff --git a/src/engines/webcontainers/index.ts b/src/engines/webcontainers/index.ts new file mode 100644 index 0000000..4279b6e --- /dev/null +++ b/src/engines/webcontainers/index.ts @@ -0,0 +1,95 @@ +import { DirectoryNode, FileSystemTree, WebContainer } from "@webcontainer/api"; +import { Terminal } from "@xterm/xterm"; +import { FitAddon } from "@xterm/addon-fit"; + +import { composeInitialFS } from "../../fsInitializer.ts"; + +export let webcontainerInstance: WebContainer; + +const mapFilesToFS = ( + files: { filename: string; content: string }[], +): FileSystemTree => { + const fs: FileSystemTree = {}; + files.forEach((file) => { + const path = file.filename.split("/"); + let current = fs; + for (let i = 0; i < path.length; i++) { + if (i === path.length - 1) { + current[path[i]] = { + file: { + contents: file.content, + }, + }; + } else { + current[path[i]] ||= { directory: {} }; + current = (current[path[i]] as DirectoryNode).directory; + } + } + }); + return fs; +}; + +window.addEventListener("load", async () => { + webcontainerInstance = await WebContainer.boot({ workdirName: "runruby" }); + + // mount project files + const { files } = await composeInitialFS(); + // await webcontainerInstance.fs.mkdir("/home/app"); + await webcontainerInstance.mount(mapFilesToFS(files)); + + // mount ruby cli + await webcontainerInstance.fs.mkdir("bin"); + const snapshotResponse = await fetch( + `${import.meta.env.VITE_ASSETS_URL}/ruby`, + ); + const snapshot = await snapshotResponse.arrayBuffer(); + await webcontainerInstance.mount(snapshot, { mountPoint: "bin" }); + await webcontainerInstance.spawn("chmod", ["+x", "bin/ruby"]); + await webcontainerInstance.spawn("chmod", ["+x", "bin/bundle"]); + await webcontainerInstance.spawn("chmod", ["+x", "bin/bundler"]); + + await webcontainerInstance.fs.mkdir("gem_stubs"); + const snapshotResponse1 = await fetch( + `${import.meta.env.VITE_ASSETS_URL}/stubs`, + ); + const snapshot1 = await snapshotResponse1.arrayBuffer(); + await webcontainerInstance.mount(snapshot1, { mountPoint: "gem_stubs" }); + + await webcontainerInstance.fs.mkdir("bundle"); + await webcontainerInstance.fs.mkdir("gems"); +}); + +export const startShell = async (terminal: Terminal) => { + while (!webcontainerInstance) { + await new Promise((resolve) => setTimeout(resolve, 100)); + } + const fitAddon = new FitAddon(); + + terminal.loadAddon(fitAddon); + + const shellProcess = await webcontainerInstance.spawn("jsh", { + terminal: { + cols: terminal.cols, + rows: terminal.rows, + }, + env: { + PATH: `${webcontainerInstance.workdir}/bin:${webcontainerInstance.path}`, + }, + }); + + shellProcess.output.pipeTo( + new WritableStream({ + write(data) { + terminal.write(data); + }, + }), + ); + + const input = shellProcess.input.getWriter(); + + terminal.onData((data) => { + input.write(data); + }); + + return shellProcess; +}; diff --git a/src/fsMap.ts b/src/fsMap.ts index 9bcce83..9536940 100644 --- a/src/fsMap.ts +++ b/src/fsMap.ts @@ -1,47 +1,6 @@ -import { Directory, File, SyncOPFSFile } from "@bjorn3/browser_wasi_shim"; -import { nanoid } from "nanoid"; -import { setDirty } from "./stores/editor.ts"; - export type Entity = { id: string; name: string; fullPath: string; - object: Directory | File | SyncOPFSFile; -}; - -export const idsMap = new Map(); - -export const sortChildren = (node: Directory, nodePath: string): Entity[] => { - const entries = Array.from(node.contents.entries()).map(([key, value]) => { - if ( - !( - value instanceof Directory || - value instanceof File || - value instanceof SyncOPFSFile - ) - ) { - throw new Error(`Unexpected type in directory contents: ${key}`); - } - - const id = idsMap.get(value); - const result = { - id: id || nanoid(), - name: key, - fullPath: `${nodePath ? nodePath + "/" : ""}${key}`, - object: value, - }; - if (!id) { - setDirty(result.fullPath); - } - idsMap.set(value, result.id); - return result; - }); - - entries.sort((a, b) => { - if (a.object instanceof Directory && b.object instanceof File) return -1; - if (b.object instanceof Directory && a.object instanceof File) return 1; - return a.name < b.name ? -1 : 1; - }); - - return entries; + children?: Entity[]; }; diff --git a/src/stores/cache.ts b/src/stores/cache.ts index 9a72e37..c8a2848 100644 --- a/src/stores/cache.ts +++ b/src/stores/cache.ts @@ -1,4 +1,4 @@ -import { action, map, onMount } from "nanostores"; +import { map, onMount } from "nanostores"; type CacheStoreValue = { info: { message?: string; usage?: number; quota?: number }; @@ -7,20 +7,20 @@ export const $cache = map({ info: { message: "Loading..." }, }); -export const refreshCacheInfo = action($cache, "increase", (store) => { - store.setKey("info", { message: "Loading..." }); +export const refreshCacheInfo = () => { + $cache.setKey("info", { message: "Loading..." }); if (navigator.storage && navigator.storage.estimate) { navigator.storage.estimate().then((estimation) => { - store.setKey("info", { + $cache.setKey("info", { usage: estimation.usage, quota: estimation.quota, }); }); } else { - store.setKey("info", { message: "StorageManager not found" }); + $cache.setKey("info", { message: "StorageManager not found" }); } -}); +}; onMount($cache, () => { refreshCacheInfo(); diff --git a/src/stores/editor.ts b/src/stores/editor.ts index fe5ca67..d85d8ad 100644 --- a/src/stores/editor.ts +++ b/src/stores/editor.ts @@ -1,8 +1,9 @@ -import { action, atom, computed, map, onMount } from "nanostores"; -import { Entity, sortChildren } from "../fsMap.ts"; +import { atom, computed, map, onMount } from "nanostores"; +import { Entity } from "../fsMap.ts"; import { TreeApi } from "react-arborist"; import { File } from "@bjorn3/browser_wasi_shim"; -import { initializeFS, workDir } from "../engines/wasi/wasi.ts"; +import { initializeFS } from "../engines/wasi/wasi.ts"; +import { webcontainerInstance } from "../engines/webcontainers"; type EditorStoreValue = { currentNodeId: string | null; @@ -26,28 +27,42 @@ onMount($editor, () => { initializeFS().then(refreshTreeData).then(cleanDirty); }); -export const refreshTreeData = action($editor, "refreshTreeData", (store) => { - store.setKey("treeData", sortChildren(workDir.dir, "")); -}); +export const getDirFiles = async (path: string) => { + while (!webcontainerInstance) { + await new Promise((resolve) => setTimeout(resolve, 100)); + } -export const setCurrentNodeId = action( - $editor, - "setCurrentNodeId", - (store, id: string | null) => { - store.setKey("currentNodeId", id); - }, -); + const files = await webcontainerInstance?.fs?.readdir(path, { + withFileTypes: true, + }); + const result: Entity[] = []; + for (const file of files) { + result.push({ + id: file.name, + name: file.name, + fullPath: `${path}/${file.name}`, + children: file.isDirectory() + ? await getDirFiles(`${path}/${file.name}`) + : undefined, + }); + } + return result; +}; -export const setCode = action( - $editor, - "setCode", - (store, code: string | null) => { - const currentNodeId = store.get().currentNodeId; - if (currentNodeId === null) return; +export const refreshTreeData = async () => { + $editor.setKey("treeData", await getDirFiles("")); +}; - store.setKey("code", code); - }, -); +export const setCurrentNodeId = (id: string | null) => { + $editor.setKey("currentNodeId", id); +}; + +export const setCode = (code: string | null) => { + const currentNodeId = $editor.get().currentNodeId; + if (currentNodeId === null) return; + + $editor.setKey("code", code); +}; export const setDirty = (path: string) => { const newDirtyFiles = [...new Set([...$editor.get().dirtyFiles, path])]; @@ -58,13 +73,9 @@ export const cleanDirty = () => { $editor.setKey("dirtyFiles", []); }; -export const setTree = action( - $editor, - "setTree", - (store, tree: TreeApi) => { - store.setKey("tree", tree); - }, -); +export const setTree = (tree: TreeApi) => { + $editor.setKey("tree", tree); +}; export const currentFilePathStore = computed( [$editor, $editorVersion], @@ -80,7 +91,7 @@ export const currentFileStore = computed($editor, (editor) => { const currentNode = editor.tree?.get(editor.currentNodeId); if (!currentNode) return null; - const currentFile = currentNode.data.object; + const currentFile = currentNode.data; if (currentFile instanceof File) { return currentFile; } else { diff --git a/src/stores/menu.ts b/src/stores/menu.ts index 26cd9da..723f2a0 100644 --- a/src/stores/menu.ts +++ b/src/stores/menu.ts @@ -1,4 +1,4 @@ -import { action, map } from "nanostores"; +import { map } from "nanostores"; type OutputStoreValue = { isOpen: boolean; @@ -7,6 +7,6 @@ export const $menu = map({ isOpen: false, }); -export const toggleMenu = action($menu, "isOpen", (store) => { - store.setKey("isOpen", !store.get().isOpen); -}); +export const toggleMenu = () => { + $menu.setKey("isOpen", !$menu.get().isOpen); +}; diff --git a/src/stores/output.ts b/src/stores/output.ts index 55fcf57..97cb1be 100644 --- a/src/stores/output.ts +++ b/src/stores/output.ts @@ -1,6 +1,6 @@ -import { action, map } from "nanostores"; +import { map } from "nanostores"; -type Tab = "info" | "logs" | "settings"; +type Tab = "info" | "terminal" | "settings"; type OutputStoreValue = { activeTab: Tab; @@ -10,6 +10,6 @@ export const $output = map({ activeTab: "info", }); -export const openTab = action($output, "openTab", (store, newTab: Tab) => { - store.setKey("activeTab", newTab); -}); +export const openTab = (newTab: Tab) => { + $output.setKey("activeTab", newTab); +}; diff --git a/src/stores/theme.ts b/src/stores/theme.ts index 9cd81a3..2efd851 100644 --- a/src/stores/theme.ts +++ b/src/stores/theme.ts @@ -1,4 +1,4 @@ -import { action, atom, onMount } from "nanostores"; +import { atom, onMount } from "nanostores"; export type Theme = "light" | "dark" | "system"; @@ -6,11 +6,11 @@ const THEME_KEY = "theme"; export const $theme = atom("system"); -export const setTheme = action($theme, "set", (store, theme: Theme) => { +export const setTheme = (theme: Theme) => { document.documentElement.setAttribute(THEME_KEY, theme); - store.set(theme); + $theme.set(theme); localStorage.setItem(THEME_KEY, theme); -}); +}; onMount($theme, () => { const url = new URL(window.location.href); diff --git a/src/stubs/bundler_stub.rb b/src/stubs/bundler_stub.rb index 6b79901..396371d 100644 --- a/src/stubs/bundler_stub.rb +++ b/src/stubs/bundler_stub.rb @@ -37,6 +37,7 @@ def deq Bundler::Fetcher::CompactIndex.prepend(Module.new do def specs(gem_names) uri = "https://worker.runruby.dev/compact_index_specs?gems=#{gem_names.join(',')}" + response = JSON.parse(JS::Connection.new.request(URI(uri), Gem::Net::HTTP::Get.new(URI(uri).request_uri)).body) if response["remainingGems"].empty? response["specs"] diff --git a/src/stubs/default_stubs.rb b/src/stubs/default_stubs.rb index f3f265e..3fe83f0 100644 --- a/src/stubs/default_stubs.rb +++ b/src/stubs/default_stubs.rb @@ -1 +1,17 @@ def Dir.home = "/" + +# TODO: implement? +# def Kernel.exec(*attrs) +# attrs[0] = "/home/runruby#{attrs[0]}" if attrs[0].start_with?("/") +# JS.eval(<<~CODE) +# const { exec } = require('child_process'); +# exec(`#{attrs.map(&:to_s).join(' ')}`, (error, stdout, stderr) => { +# if (stdout) console.log(stdout) +# if (stderr) console.error(stderr) +# if (error) { +# return 1 +# } +# return 0 +# }) +# CODE +# end diff --git a/src/stubs/index.ts b/src/stubs/index.ts index 8deb572..feeefe7 100644 --- a/src/stubs/index.ts +++ b/src/stubs/index.ts @@ -5,7 +5,11 @@ import { PreopenDirectory, } from "@bjorn3/browser_wasi_shim"; -const rubyStubs = import.meta.glob("./**/*.rb", { as: "raw", eager: true }); +const rubyStubs = import.meta.glob("./**/*.rb", { + query: "?raw", + import: "default", + eager: true, +}) as Record; const getDirectory = (paths: string[], rootDir: PreopenDirectory) => { return paths.reduce((acc, pathPart) => { diff --git a/src/stubs/js/connection.rb b/src/stubs/js/connection.rb index 25f4b49..931579e 100644 --- a/src/stubs/js/connection.rb +++ b/src/stubs/js/connection.rb @@ -80,7 +80,8 @@ def request(uri, req) js_response = JS.eval(<<~JS).await return fetch('#{proxy_uri(uri)}', { method: '#{req.method}', - headers: #{req.to_hash.transform_values { _1.join(';') }.to_json}, + // remove forbidden headers, see: https://github.com/nodejs/undici/issues/1470 + headers: #{req.to_hash.except("keep-alive", "connection").transform_values { _1.join(';') }.to_json}, body: #{req.body ? req.body.to_json : 'undefined'} }) .then(response => { @@ -99,6 +100,10 @@ def request(uri, req) }) } }) + .catch(error => { + console.error(error) + throw error + }) JS body = js_response[:uint8array].to_s != "undefined" ? js_response[:uint8array][:length].to_i.times.map {js_response[:uint8array].call(:at, _1).to_i}.pack("C*") : js_response[:text].to_s