diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3333b41..77537da 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.4.0] - 2026-06-01
+
+### Added
+- **JSX/TSX support**: `.jsx` and `.tsx` files now run directly through the runtime. `.tsx` is type-stripped first (the existing TS pass) and then JSX is compiled to factory calls; `.jsx` goes straight through the JSX pass. `node app.tsx`, `require('./Component.tsx')`, and `index.tsx`/`index.jsx` directory resolution all work.
+ - Implemented with `acorn-jsx` (already a dependency) — a small, synchronous, pure-JS codegen pass, no Babel/esbuild and no wasm, keeping the bundle lean.
+ - The transform is **config-driven**: the nearest `tsconfig.json` or `jsconfig.json` in the VFS is resolved by walking upward from the script being transformed. `jsx` (`react` → classic `React.createElement`; `react-jsx`/`react-jsxdev` → automatic `react/jsx-runtime`), `jsxImportSource`, `jsxFactory`, and `jsxFragmentFactory` are all honored. Defaults to the automatic runtime with `react` when no config is found.
+ - Handles intrinsic vs. component tags, member-expression components (``), fragments, spread attributes/children, boolean attribute shorthand, `key` extraction (automatic runtime), nested JSX inside expressions, JSX whitespace collapsing, and HTML entity decoding.
+- **TypeScript import extension rewriting**: imports/requires that reference a sibling by its *output* extension (`import './foo.js'`, `require('./Card.jsx')`) now resolve to the TypeScript source on disk (`.ts`/`.tsx`/`.mts`/`.cts`) when the literal file is absent — matching `tsc`/Node behavior. A real `.js` sibling still wins over the rewritten `.ts`. Works from both `.js` and `.ts`/`.tsx` files.
+
+### Fixed
+- **Named exports now preserve their local binding** in the ESM→CJS transform. `export const`/`function`/`class` declarations previously became `exports.X = ...` outright, dropping the local `X` binding so later module-scope references broke (e.g. `export const o = {}; o.key = 'v'` produced invalid `exports.o = {}; o.key = 'v'`). The declaration is now kept and the value is assigned onto `exports` separately. Destructuring exports (`export const { a, b } = obj`, `export const [x] = arr`) export each bound name.
+
+## [0.3.0] - 2026-06-01
+
+### Added
+- **Native TypeScript support**: `.ts`, `.mts`, and `.cts` files now run directly through the runtime, mirroring Node.js's native type-stripping (stable since Node 23.6). `node app.ts`, `require('./mod.ts')`, and `index.ts` directory resolution all work out of the box.
+ - Implemented with `acorn-typescript`, a pure-JS acorn plugin — no `typescript` compiler dependency and no wasm, so stripping is synchronous (required for `require()`) and adds minimal bundle weight.
+ - Type-only syntax (annotations, interfaces, type aliases, generics, `import type`/`export type`, `as`/`satisfies`, non-null `!`, definite assignment, `declare`, class member modifiers, `implements`) is replaced with whitespace, preserving source positions.
+ - `.cts` is treated as CommonJS and `.mts`/`.ts` as ESM-capable, consistent with Node.
+ - Like Node's strip-only mode, constructs requiring code generation (enums, namespaces, parameter-property assignment) are not emitted.
+
## [0.2.14] - 2026-02-14
### Added
diff --git a/README.md b/README.md
index 51746e9..dd0f1e6 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ Built by the creators of [Macaly.com](https://macaly.com) — a tool that lets a
- **Run Any CLI Tool** - npm packages with `bin` entries (vitest, eslint, tsc, etc.) work automatically
- **Dev Servers** - Built-in Vite and Next.js development servers
- **Hot Module Replacement** - React Refresh support for instant updates
-- **TypeScript Support** - First-class TypeScript/TSX transformation via esbuild-wasm
+- **TypeScript & JSX Support** - Run `.ts`/`.mts`/`.cts` files directly via Node-style type-stripping, plus `.jsx`/`.tsx` via a synchronous, pure-JS JSX transform that's config-driven from the nearest `tsconfig.json`/`jsconfig.json`; first-class TypeScript/TSX transformation in dev servers via esbuild-wasm
- **Service Worker Architecture** - Intercepts requests for seamless dev experience
- **Optional Web Worker Support** - Offload code execution to a Web Worker for improved UI responsiveness
- **Secure by Default** - Cross-origin sandbox support for running untrusted code safely
diff --git a/package-lock.json b/package-lock.json
index 9612856..1e802e1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "almostnode",
- "version": "0.2.13",
+ "version": "0.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "almostnode",
- "version": "0.2.13",
+ "version": "0.3.0",
"license": "MIT",
"dependencies": {
"@ai-sdk/openai": "^3.0.28",
@@ -15,6 +15,7 @@
"@xterm/xterm": "^6.0.0",
"acorn": "^8.15.0",
"acorn-jsx": "^5.3.2",
+ "acorn-typescript": "^1.4.13",
"ai": "^6.0.85",
"brotli": "^1.3.3",
"brotli-wasm": "^3.0.1",
@@ -33,7 +34,8 @@
"@types/node": "^25.0.10",
"@types/pako": "^2.0.4",
"dotenv": "^17.3.1",
- "esbuild": "^0.27.2",
+ "esbuild": "^0.27.7",
+ "esbuild-plugins-node-modules-polyfill": "^1.8.1",
"jsdom": "^27.4.0",
"react-dom": "^19.2.4",
"typescript": "^5.9.3",
@@ -309,9 +311,9 @@
}
},
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
- "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz",
+ "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==",
"cpu": [
"ppc64"
],
@@ -326,9 +328,9 @@
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
- "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz",
+ "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==",
"cpu": [
"arm"
],
@@ -343,9 +345,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
- "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz",
+ "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==",
"cpu": [
"arm64"
],
@@ -360,9 +362,9 @@
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
- "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz",
+ "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==",
"cpu": [
"x64"
],
@@ -377,9 +379,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
- "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz",
+ "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==",
"cpu": [
"arm64"
],
@@ -394,9 +396,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
- "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz",
+ "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==",
"cpu": [
"x64"
],
@@ -411,9 +413,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
- "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==",
"cpu": [
"arm64"
],
@@ -428,9 +430,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
- "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz",
+ "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==",
"cpu": [
"x64"
],
@@ -445,9 +447,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
- "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz",
+ "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==",
"cpu": [
"arm"
],
@@ -462,9 +464,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
- "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz",
+ "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==",
"cpu": [
"arm64"
],
@@ -479,9 +481,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
- "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz",
+ "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==",
"cpu": [
"ia32"
],
@@ -496,9 +498,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
- "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz",
+ "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==",
"cpu": [
"loong64"
],
@@ -513,9 +515,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
- "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz",
+ "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==",
"cpu": [
"mips64el"
],
@@ -530,9 +532,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
- "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz",
+ "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==",
"cpu": [
"ppc64"
],
@@ -547,9 +549,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
- "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz",
+ "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==",
"cpu": [
"riscv64"
],
@@ -564,9 +566,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
- "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz",
+ "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==",
"cpu": [
"s390x"
],
@@ -581,9 +583,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
- "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz",
+ "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==",
"cpu": [
"x64"
],
@@ -598,9 +600,9 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
- "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==",
"cpu": [
"arm64"
],
@@ -615,9 +617,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
- "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz",
+ "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==",
"cpu": [
"x64"
],
@@ -632,9 +634,9 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
- "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==",
"cpu": [
"arm64"
],
@@ -649,9 +651,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
- "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz",
+ "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==",
"cpu": [
"x64"
],
@@ -666,9 +668,9 @@
}
},
"node_modules/@esbuild/openharmony-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
- "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz",
+ "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==",
"cpu": [
"arm64"
],
@@ -683,9 +685,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
- "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz",
+ "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==",
"cpu": [
"x64"
],
@@ -700,9 +702,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
- "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz",
+ "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==",
"cpu": [
"arm64"
],
@@ -717,9 +719,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
- "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz",
+ "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==",
"cpu": [
"ia32"
],
@@ -734,9 +736,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
- "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz",
+ "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==",
"cpu": [
"x64"
],
@@ -796,6 +798,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@jspm/core": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@jspm/core/-/core-2.1.0.tgz",
+ "integrity": "sha512-3sRl+pkyFY/kLmHl0cgHiFp2xEqErA8N3ECjMs7serSUBmoJ70lBa0PG5t0IM6WJgdZNyyI0R8YFfi5wM8+mzg==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
"node_modules/@mixmark-io/domino": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz",
@@ -1588,9 +1597,9 @@
]
},
"node_modules/acorn": {
- "version": "8.15.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
- "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
+ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
@@ -1608,6 +1617,15 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
+ "node_modules/acorn-typescript": {
+ "version": "1.4.13",
+ "resolved": "https://registry.npmjs.org/acorn-typescript/-/acorn-typescript-1.4.13.tgz",
+ "integrity": "sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==",
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": ">=8.9.0"
+ }
+ },
"node_modules/agent-base": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
@@ -1785,6 +1803,13 @@
"compressjs": "bin/compressjs"
}
},
+ "node_modules/confbox": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz",
+ "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/css-tree": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz",
@@ -1956,9 +1981,9 @@
"license": "MIT"
},
"node_modules/esbuild": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
- "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz",
+ "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -1969,32 +1994,50 @@
"node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.27.2",
- "@esbuild/android-arm": "0.27.2",
- "@esbuild/android-arm64": "0.27.2",
- "@esbuild/android-x64": "0.27.2",
- "@esbuild/darwin-arm64": "0.27.2",
- "@esbuild/darwin-x64": "0.27.2",
- "@esbuild/freebsd-arm64": "0.27.2",
- "@esbuild/freebsd-x64": "0.27.2",
- "@esbuild/linux-arm": "0.27.2",
- "@esbuild/linux-arm64": "0.27.2",
- "@esbuild/linux-ia32": "0.27.2",
- "@esbuild/linux-loong64": "0.27.2",
- "@esbuild/linux-mips64el": "0.27.2",
- "@esbuild/linux-ppc64": "0.27.2",
- "@esbuild/linux-riscv64": "0.27.2",
- "@esbuild/linux-s390x": "0.27.2",
- "@esbuild/linux-x64": "0.27.2",
- "@esbuild/netbsd-arm64": "0.27.2",
- "@esbuild/netbsd-x64": "0.27.2",
- "@esbuild/openbsd-arm64": "0.27.2",
- "@esbuild/openbsd-x64": "0.27.2",
- "@esbuild/openharmony-arm64": "0.27.2",
- "@esbuild/sunos-x64": "0.27.2",
- "@esbuild/win32-arm64": "0.27.2",
- "@esbuild/win32-ia32": "0.27.2",
- "@esbuild/win32-x64": "0.27.2"
+ "@esbuild/aix-ppc64": "0.27.7",
+ "@esbuild/android-arm": "0.27.7",
+ "@esbuild/android-arm64": "0.27.7",
+ "@esbuild/android-x64": "0.27.7",
+ "@esbuild/darwin-arm64": "0.27.7",
+ "@esbuild/darwin-x64": "0.27.7",
+ "@esbuild/freebsd-arm64": "0.27.7",
+ "@esbuild/freebsd-x64": "0.27.7",
+ "@esbuild/linux-arm": "0.27.7",
+ "@esbuild/linux-arm64": "0.27.7",
+ "@esbuild/linux-ia32": "0.27.7",
+ "@esbuild/linux-loong64": "0.27.7",
+ "@esbuild/linux-mips64el": "0.27.7",
+ "@esbuild/linux-ppc64": "0.27.7",
+ "@esbuild/linux-riscv64": "0.27.7",
+ "@esbuild/linux-s390x": "0.27.7",
+ "@esbuild/linux-x64": "0.27.7",
+ "@esbuild/netbsd-arm64": "0.27.7",
+ "@esbuild/netbsd-x64": "0.27.7",
+ "@esbuild/openbsd-arm64": "0.27.7",
+ "@esbuild/openbsd-x64": "0.27.7",
+ "@esbuild/openharmony-arm64": "0.27.7",
+ "@esbuild/sunos-x64": "0.27.7",
+ "@esbuild/win32-arm64": "0.27.7",
+ "@esbuild/win32-ia32": "0.27.7",
+ "@esbuild/win32-x64": "0.27.7"
+ }
+ },
+ "node_modules/esbuild-plugins-node-modules-polyfill": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/esbuild-plugins-node-modules-polyfill/-/esbuild-plugins-node-modules-polyfill-1.8.1.tgz",
+ "integrity": "sha512-7vxzmyTFDhYUNhjlciMPmp32eUafNIHiXvo8ZD22PzccnxMoGpPnsYn17gSBoFZgpRYNdCJcAWsQ60YVKgKg3A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jspm/core": "^2.1.0",
+ "local-pkg": "^1.1.2",
+ "resolve.exports": "^2.0.3"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "esbuild": ">=0.14.0 <=0.27.x"
}
},
"node_modules/estree-walker": {
@@ -2035,6 +2078,13 @@
"node": ">=12.0.0"
}
},
+ "node_modules/exsolve": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz",
+ "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/fast-xml-parser": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.3.tgz",
@@ -2280,6 +2330,24 @@
"node-liblzma": "^2.0.3"
}
},
+ "node_modules/local-pkg": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.2.1.tgz",
+ "integrity": "sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mlly": "^1.7.4",
+ "pkg-types": "^2.3.0",
+ "quansync": "^0.2.11"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
"node_modules/lru-cache": {
"version": "11.2.5",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz",
@@ -2348,6 +2416,38 @@
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
"license": "MIT"
},
+ "node_modules/mlly": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz",
+ "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.16.0",
+ "pathe": "^2.0.3",
+ "pkg-types": "^1.3.1",
+ "ufo": "^1.6.3"
+ }
+ },
+ "node_modules/mlly/node_modules/confbox": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz",
+ "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mlly/node_modules/pkg-types": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz",
+ "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "confbox": "^0.1.8",
+ "mlly": "^1.7.4",
+ "pathe": "^2.0.1"
+ }
+ },
"node_modules/modern-tar": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.7.3.tgz",
@@ -2513,6 +2613,18 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/pkg-types": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.1.tgz",
+ "integrity": "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "confbox": "^0.2.4",
+ "exsolve": "^1.0.8",
+ "pathe": "^2.0.3"
+ }
+ },
"node_modules/playwright": {
"version": "1.58.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.0.tgz",
@@ -2646,6 +2758,23 @@
"node": ">=18.0.0"
}
},
+ "node_modules/quansync": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz",
+ "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/antfu"
+ },
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
@@ -3167,6 +3296,13 @@
"node": ">=14.17"
}
},
+ "node_modules/ufo": {
+ "version": "1.6.4",
+ "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz",
+ "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/uint8array-extras": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz",
diff --git a/package.json b/package.json
index 8fbd9d5..7dcaa81 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "almostnode",
- "version": "0.2.14",
+ "version": "0.4.0",
"description": "Node.js in your browser. Just like that.",
"type": "module",
"license": "MIT",
@@ -91,6 +91,7 @@
"@xterm/xterm": "^6.0.0",
"acorn": "^8.15.0",
"acorn-jsx": "^5.3.2",
+ "acorn-typescript": "^1.4.13",
"ai": "^6.0.85",
"brotli": "^1.3.3",
"brotli-wasm": "^3.0.1",
@@ -109,7 +110,8 @@
"@types/node": "^25.0.10",
"@types/pako": "^2.0.4",
"dotenv": "^17.3.1",
- "esbuild": "^0.27.2",
+ "esbuild": "^0.27.7",
+ "esbuild-plugins-node-modules-polyfill": "^1.8.1",
"jsdom": "^27.4.0",
"react-dom": "^19.2.4",
"typescript": "^5.9.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..1420f48
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,2698 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@ai-sdk/openai':
+ specifier: ^3.0.28
+ version: 3.0.67(zod@4.4.3)
+ '@ai-sdk/react':
+ specifier: ^3.0.87
+ version: 3.0.195(react@19.2.6)(zod@4.4.3)
+ '@xterm/addon-fit':
+ specifier: ^0.11.0
+ version: 0.11.0
+ '@xterm/xterm':
+ specifier: ^6.0.0
+ version: 6.0.0
+ acorn:
+ specifier: ^8.15.0
+ version: 8.16.0
+ acorn-jsx:
+ specifier: ^5.3.2
+ version: 5.3.2(acorn@8.16.0)
+ acorn-typescript:
+ specifier: ^1.4.13
+ version: 1.4.13(acorn@8.16.0)
+ ai:
+ specifier: ^6.0.85
+ version: 6.0.193(zod@4.4.3)
+ brotli:
+ specifier: ^1.3.3
+ version: 1.3.3
+ brotli-wasm:
+ specifier: ^3.0.1
+ version: 3.0.1
+ comlink:
+ specifier: ^4.4.2
+ version: 4.4.2
+ css-tree:
+ specifier: ^3.1.0
+ version: 3.2.1
+ just-bash:
+ specifier: ^2.7.0
+ version: 2.14.5
+ pako:
+ specifier: ^2.1.0
+ version: 2.1.0
+ resolve.exports:
+ specifier: ^2.0.3
+ version: 2.0.3
+ vite-plugin-top-level-await:
+ specifier: ^1.6.0
+ version: 1.6.0(rollup@4.60.4)(vite@5.4.21(@types/node@25.9.1))
+ vite-plugin-wasm:
+ specifier: ^3.5.0
+ version: 3.6.0(vite@5.4.21(@types/node@25.9.1))
+ zod:
+ specifier: ^4.3.6
+ version: 4.4.3
+ devDependencies:
+ '@playwright/test':
+ specifier: ^1.58.0
+ version: 1.60.0
+ '@types/css-tree':
+ specifier: ^2.3.11
+ version: 2.3.11
+ '@types/node':
+ specifier: ^25.0.10
+ version: 25.9.1
+ '@types/pako':
+ specifier: ^2.0.4
+ version: 2.0.4
+ dotenv:
+ specifier: ^17.3.1
+ version: 17.4.2
+ esbuild:
+ specifier: ^0.27.2
+ version: 0.27.7
+ jsdom:
+ specifier: ^27.4.0
+ version: 27.4.0
+ react-dom:
+ specifier: ^19.2.4
+ version: 19.2.6(react@19.2.6)
+ typescript:
+ specifier: ^5.9.3
+ version: 5.9.3
+ vite:
+ specifier: ^5.4.0
+ version: 5.4.21(@types/node@25.9.1)
+ vitest:
+ specifier: ^4.0.18
+ version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@27.4.0)(vite@5.4.21(@types/node@25.9.1))
+
+packages:
+
+ '@acemir/cssom@0.9.31':
+ resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==}
+
+ '@ai-sdk/gateway@3.0.121':
+ resolution: {integrity: sha512-uY248djJRxa5W68MHiyqO8WLdOeKQoRClGg7PVX/VPhVW8SJNM7/l5DcrA5WAM3YfQrLyNkgZa2VOu8T0t8LUw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.25.76 || ^4.1.8
+
+ '@ai-sdk/openai@3.0.67':
+ resolution: {integrity: sha512-oAiGC9eWG7IgtdsdS74bOCnAAHarAfTJhWN9x5INwnWPekL802AvF+0I5DvLzIF1MIRmNw4N8mPSL/GUVbX9Mw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.25.76 || ^4.1.8
+
+ '@ai-sdk/provider-utils@4.0.27':
+ resolution: {integrity: sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.25.76 || ^4.1.8
+
+ '@ai-sdk/provider@3.0.10':
+ resolution: {integrity: sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw==}
+ engines: {node: '>=18'}
+
+ '@ai-sdk/react@3.0.195':
+ resolution: {integrity: sha512-+yIH84d4bBNzLKfaDDf4EocEH0XQKKNwNShxbrz5xAiJMNIPnWVWT9cyrSerYaGH3iNVS/g2io42PE4HNbc4RA==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ react: ^18 || ~19.0.1 || ~19.1.2 || ^19.2.1
+
+ '@asamuzakjp/css-color@4.1.2':
+ resolution: {integrity: sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==}
+
+ '@asamuzakjp/dom-selector@6.8.1':
+ resolution: {integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==}
+
+ '@asamuzakjp/nwsapi@2.3.9':
+ resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
+
+ '@borewit/text-codec@0.2.2':
+ resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==}
+
+ '@csstools/color-helpers@6.0.2':
+ resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==}
+ engines: {node: '>=20.19.0'}
+
+ '@csstools/css-calc@3.2.1':
+ resolution: {integrity: sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^4.0.0
+ '@csstools/css-tokenizer': ^4.0.0
+
+ '@csstools/css-color-parser@4.1.1':
+ resolution: {integrity: sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^4.0.0
+ '@csstools/css-tokenizer': ^4.0.0
+
+ '@csstools/css-parser-algorithms@4.0.0':
+ resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@csstools/css-tokenizer': ^4.0.0
+
+ '@csstools/css-syntax-patches-for-csstree@1.1.4':
+ resolution: {integrity: sha512-wgsqt92b7C7tQhIdPNxj0n9zuUbQlvAuI1exyzeNrOKOi62SD7ren8zqszmpVREjAOqg8cD2FqYhQfAuKjk4sw==}
+ peerDependencies:
+ css-tree: ^3.2.1
+ peerDependenciesMeta:
+ css-tree:
+ optional: true
+
+ '@csstools/css-tokenizer@4.0.0':
+ resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==}
+ engines: {node: '>=20.19.0'}
+
+ '@esbuild/aix-ppc64@0.21.5':
+ resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/aix-ppc64@0.27.7':
+ resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.21.5':
+ resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm64@0.27.7':
+ resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.21.5':
+ resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-arm@0.27.7':
+ resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.21.5':
+ resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/android-x64@0.27.7':
+ resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.21.5':
+ resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-arm64@0.27.7':
+ resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.21.5':
+ resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.27.7':
+ resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.21.5':
+ resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-arm64@0.27.7':
+ resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.21.5':
+ resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.27.7':
+ resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.21.5':
+ resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm64@0.27.7':
+ resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.21.5':
+ resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.27.7':
+ resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.21.5':
+ resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.27.7':
+ resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.21.5':
+ resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
+ engines: {node: '>=12'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.27.7':
+ resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.21.5':
+ resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
+ engines: {node: '>=12'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.27.7':
+ resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.21.5':
+ resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.27.7':
+ resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.21.5':
+ resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
+ engines: {node: '>=12'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.27.7':
+ resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.21.5':
+ resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
+ engines: {node: '>=12'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.27.7':
+ resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.21.5':
+ resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.27.7':
+ resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.27.7':
+ resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.21.5':
+ resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.27.7':
+ resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.27.7':
+ resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.21.5':
+ resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.27.7':
+ resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.27.7':
+ resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.21.5':
+ resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/sunos-x64@0.27.7':
+ resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.21.5':
+ resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-arm64@0.27.7':
+ resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.21.5':
+ resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.27.7':
+ resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.21.5':
+ resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.27.7':
+ resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@exodus/bytes@1.15.1':
+ resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+ peerDependencies:
+ '@noble/hashes': ^1.8.0 || ^2.0.0
+ peerDependenciesMeta:
+ '@noble/hashes':
+ optional: true
+
+ '@jitl/quickjs-ffi-types@0.32.0':
+ resolution: {integrity: sha512-v9T+GQpmk43VDJ7d72sf0Nexhk+ArvtUihW27dy7lqAl0zBObFKtSBBIm5RBjwIhE8VwsPPm9PNuvPvNqLWUEg==}
+
+ '@jitl/quickjs-wasmfile-debug-asyncify@0.32.0':
+ resolution: {integrity: sha512-EX8zbXwGqCgAE764M+qvkHtyXDi/FUoMBea0JnES7vCM3P7a2+EOZOjGv85wtZ2sJhI1oJ+nekmqpOODFDY+hw==}
+
+ '@jitl/quickjs-wasmfile-debug-sync@0.32.0':
+ resolution: {integrity: sha512-LeYWrPGC1uNCTBWvibo3ZLJj0CSVNYUXvJpXMCmuQ5Sap2cCACc3uvGvYV4homHHBAzfw5akoTqMMS4YFRtw+Q==}
+
+ '@jitl/quickjs-wasmfile-release-asyncify@0.32.0':
+ resolution: {integrity: sha512-3oSwPfja12ICz4aIblB58cuY8JlEq5Txt8Cut4VLo+LH47QN+mzCnSgnbB03hWzg1LBcc+VyyI9UOag7a1NF+Q==}
+
+ '@jitl/quickjs-wasmfile-release-sync@0.32.0':
+ resolution: {integrity: sha512-BKNDI/TPBfGlLNGYpLrhcDGXmIk4xHm4MRAisOBnOzpXVn9HZWsfmMAc9WMBrAHjvvds6HOikKeaOBKdPdpVrg==}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@mixmark-io/domino@2.2.0':
+ resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==}
+
+ '@mongodb-js/zstd@7.0.0':
+ resolution: {integrity: sha512-mQ2s0pYYiav+tzCDR05Zptem8Ey2v8s11lri5RKGhTtL4COVCvVCk5vtyRYNT+9L8qSfyOqqefF9UtnW8mC5jA==}
+ engines: {node: '>= 20.19.0'}
+
+ '@nodable/entities@2.1.1':
+ resolution: {integrity: sha512-Pig3HxDIoMgjdEH8OCf/dkcTmLFjJRjWuq8jSnklu284/TKOPibSRERmOykiwmyXTtv61mP+44f3GMx0tLAyjg==}
+
+ '@opentelemetry/api@1.9.1':
+ resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==}
+ engines: {node: '>=8.0.0'}
+
+ '@playwright/test@1.60.0':
+ resolution: {integrity: sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ '@rollup/plugin-virtual@3.0.2':
+ resolution: {integrity: sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+
+ '@rollup/rollup-android-arm-eabi@4.60.4':
+ resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.60.4':
+ resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.60.4':
+ resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.60.4':
+ resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.60.4':
+ resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.60.4':
+ resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.60.4':
+ resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.60.4':
+ resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==}
+ cpu: [arm]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-arm64-gnu@4.60.4':
+ resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-arm64-musl@4.60.4':
+ resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-loong64-gnu@4.60.4':
+ resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==}
+ cpu: [loong64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-loong64-musl@4.60.4':
+ resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==}
+ cpu: [loong64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-ppc64-gnu@4.60.4':
+ resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-ppc64-musl@4.60.4':
+ resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.60.4':
+ resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-riscv64-musl@4.60.4':
+ resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-s390x-gnu@4.60.4':
+ resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-x64-gnu@4.60.4':
+ resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-x64-musl@4.60.4':
+ resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-openbsd-x64@4.60.4':
+ resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@rollup/rollup-openharmony-arm64@4.60.4':
+ resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rollup/rollup-win32-arm64-msvc@4.60.4':
+ resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.60.4':
+ resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-gnu@4.60.4':
+ resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==}
+ cpu: [x64]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.60.4':
+ resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==}
+ cpu: [x64]
+ os: [win32]
+
+ '@standard-schema/spec@1.1.0':
+ resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
+
+ '@swc/core-darwin-arm64@1.15.40':
+ resolution: {integrity: sha512-PaYyclfmQ++77D8ityYvmmVzHv9aG8ROwt2GfG6/ccloy4Hgf80qtOnzb9VYvPsUT7Ty1uhuDRhv3XYpf62qhQ==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@swc/core-darwin-x64@1.15.40':
+ resolution: {integrity: sha512-HbbPzvfLBUXjIB1Ezks+//lNUjmLjfyd63XSwprJgrZaXYdm70kohXPJUWdqKZozolFxbPaO+xtBaiUp6BoueA==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@swc/core-linux-arm-gnueabihf@1.15.40':
+ resolution: {integrity: sha512-SlRZsCjOCPR2LvFs0Ri/Xrx/5o5TCt8vl4gW6mX1hEZOG0a625RxzRHpHdAQNGykmAN/7IeaFAJG+QnNmxlHcA==}
+ engines: {node: '>=10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@swc/core-linux-arm64-gnu@1.15.40':
+ resolution: {integrity: sha512-Q8byxJt2fh8CR3EUX6snBpy47AoBVm+In/+Z3rjDHMjC38ZvR9/gtUUNCT0tfrn4EdVsO8/QPi59nxrxvqxvBQ==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@swc/core-linux-arm64-musl@1.15.40':
+ resolution: {integrity: sha512-4z0MgHU+7M0pZDqBN1El7mFXDI1SBwinfcUkAyA4v8QrhOIUOZltySt2aStQLZGrdXVXM4Y4ylfiTC04ED+MoQ==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@swc/core-linux-ppc64-gnu@1.15.40':
+ resolution: {integrity: sha512-fLI4iUgeSZu0eRWUXwe6YzPFx9gHbFiPkl8Rp3mJfP8OpNR3nTQCGPvHdDh9xniW7mVvgMY4ni7A4VzqI1KrpA==}
+ engines: {node: '>=10'}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@swc/core-linux-s390x-gnu@1.15.40':
+ resolution: {integrity: sha512-YqeKMAb7d4nQSGMJQ454IlaCENpzcDqhvBE9+CPfdnYpnUXxd+BSrB6Xk0YjW8UyoEhUj4p6quATCxbsp6J3jg==}
+ engines: {node: '>=10'}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@swc/core-linux-x64-gnu@1.15.40':
+ resolution: {integrity: sha512-7HOuS1iGcme/j/TuL1TfmmLGiMQrjv/GmjyZeydl00FKPtpGXEldwqfI56xgd1YzrzoB2svWjxbGGyQ0TEASxg==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@swc/core-linux-x64-musl@1.15.40':
+ resolution: {integrity: sha512-h4kZYHc7dpc9P9u4brRJaS8Pl7tPVHAeiLSzw7T5RfIJgAoSdaCMKzI/2Uay9gFhaw8uyCDl0L5q37r0EpAfIA==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@swc/core-win32-arm64-msvc@1.15.40':
+ resolution: {integrity: sha512-+mQgKZXSj6mV38Zh05QaxSjUDmGP/R2JWlXZTDLSPkDzHU6p3GxN9eeSf5dfyDVU86946fmCvSzyl/ucImx8+A==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@swc/core-win32-ia32-msvc@1.15.40':
+ resolution: {integrity: sha512-yvwdPLGd25mcj/mNatjNQ0lZujtQD6psH3v9PNmMb+fSzjbNG8KIDxjFWrcV+fsFVLOkyOmdJsFmX7NAFjVyPw==}
+ engines: {node: '>=10'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@swc/core-win32-x64-msvc@1.15.40':
+ resolution: {integrity: sha512-OXtKsLU1bVtInzzDEAY2sYiF/rl4tvAnLLLpuMp3HzAOQZ5A+i69AKDhA1YLQTaMAqO3vzyYNVAYVRMPtSYD4w==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@swc/core@1.15.40':
+ resolution: {integrity: sha512-2kwzJikRvgtNAG7MwVZY2vEzZjTxKIq5jXOihuSV/8U+Hej8Va22t65aKnJZs3P+NwojZvR8Mf8kyM7O+V8sQg==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@swc/helpers': '>=0.5.17'
+ peerDependenciesMeta:
+ '@swc/helpers':
+ optional: true
+
+ '@swc/counter@0.1.3':
+ resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
+
+ '@swc/types@0.1.26':
+ resolution: {integrity: sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw==}
+
+ '@swc/wasm@1.15.40':
+ resolution: {integrity: sha512-FVS3SEJXBxjpxVUGSzaTaCdJjnXUalRftA/6hILMAJIcYHBoiBfJlxuH6s47iajlAJZP25e5Kf4HNHvvwyOEgw==}
+
+ '@tokenizer/inflate@0.4.1':
+ resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==}
+ engines: {node: '>=18'}
+
+ '@tokenizer/token@0.3.0':
+ resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
+
+ '@types/chai@5.2.3':
+ resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
+
+ '@types/css-tree@2.3.11':
+ resolution: {integrity: sha512-aEokibJOI77uIlqoBOkVbaQGC9zII0A+JH1kcTNKW2CwyYWD8KM6qdo+4c77wD3wZOQfJuNWAr9M4hdk+YhDIg==}
+
+ '@types/deep-eql@4.0.2':
+ resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
+
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+ '@types/estree@1.0.9':
+ resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
+
+ '@types/node@25.9.1':
+ resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==}
+
+ '@types/pako@2.0.4':
+ resolution: {integrity: sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==}
+
+ '@vercel/oidc@3.2.0':
+ resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==}
+ engines: {node: '>= 20'}
+
+ '@vitest/expect@4.1.7':
+ resolution: {integrity: sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==}
+
+ '@vitest/mocker@4.1.7':
+ resolution: {integrity: sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==}
+ peerDependencies:
+ msw: ^2.4.9
+ vite: ^6.0.0 || ^7.0.0 || ^8.0.0
+ peerDependenciesMeta:
+ msw:
+ optional: true
+ vite:
+ optional: true
+
+ '@vitest/pretty-format@4.1.7':
+ resolution: {integrity: sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==}
+
+ '@vitest/runner@4.1.7':
+ resolution: {integrity: sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==}
+
+ '@vitest/snapshot@4.1.7':
+ resolution: {integrity: sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==}
+
+ '@vitest/spy@4.1.7':
+ resolution: {integrity: sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==}
+
+ '@vitest/utils@4.1.7':
+ resolution: {integrity: sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==}
+
+ '@xterm/addon-fit@0.11.0':
+ resolution: {integrity: sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g==}
+
+ '@xterm/xterm@6.0.0':
+ resolution: {integrity: sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==}
+
+ acorn-jsx@5.3.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn-typescript@1.4.13:
+ resolution: {integrity: sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==}
+ peerDependencies:
+ acorn: '>=8.9.0'
+
+ acorn@8.16.0:
+ resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ agent-base@7.1.4:
+ resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
+ engines: {node: '>= 14'}
+
+ ai@6.0.193:
+ resolution: {integrity: sha512-VQOTOse8+X8kMtg61DNSXlYJzwOW4NjMLDJNk/qxClWsFe4oiyFJDHGGG1oezfGcFzuYuQe/8Z7r4kwiZWh2YQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ zod: ^3.25.76 || ^4.1.8
+
+ assertion-error@2.0.1:
+ resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
+ engines: {node: '>=12'}
+
+ balanced-match@4.0.4:
+ resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
+ engines: {node: 18 || 20 || >=22}
+
+ base64-js@1.5.1:
+ resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+
+ bidi-js@1.0.3:
+ resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
+
+ bl@4.1.0:
+ resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
+
+ brace-expansion@5.0.6:
+ resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==}
+ engines: {node: 18 || 20 || >=22}
+
+ brotli-wasm@3.0.1:
+ resolution: {integrity: sha512-U3K72/JAi3jITpdhZBqzSUq+DUY697tLxOuFXB+FpAE/Ug+5C3VZrv4uA674EUZHxNAuQ9wETXNqQkxZD6oL4A==}
+ engines: {node: '>=v18.0.0'}
+
+ brotli@1.3.3:
+ resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==}
+
+ buffer@5.7.1:
+ resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
+
+ chai@6.2.2:
+ resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
+ engines: {node: '>=18'}
+
+ chownr@1.1.4:
+ resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
+
+ comlink@4.4.2:
+ resolution: {integrity: sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==}
+
+ commander@6.2.1:
+ resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==}
+ engines: {node: '>= 6'}
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ css-tree@3.2.1:
+ resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+
+ cssstyle@5.3.7:
+ resolution: {integrity: sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==}
+ engines: {node: '>=20'}
+
+ data-urls@6.0.1:
+ resolution: {integrity: sha512-euIQENZg6x8mj3fO6o9+fOW8MimUI4PpD/fZBhJfeioZVy9TUpM4UY7KjQNVZFlqwJ0UdzRDzkycB997HEq1BQ==}
+ engines: {node: '>=20'}
+
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ decimal.js@10.6.0:
+ resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
+
+ decompress-response@6.0.0:
+ resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
+ engines: {node: '>=10'}
+
+ deep-extend@0.6.0:
+ resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
+ engines: {node: '>=4.0.0'}
+
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
+ detect-libc@2.1.2:
+ resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
+ engines: {node: '>=8'}
+
+ diff@8.0.4:
+ resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==}
+ engines: {node: '>=0.3.1'}
+
+ dotenv@17.4.2:
+ resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==}
+ engines: {node: '>=12'}
+
+ end-of-stream@1.4.5:
+ resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
+
+ entities@8.0.0:
+ resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==}
+ engines: {node: '>=20.19.0'}
+
+ es-module-lexer@2.1.0:
+ resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==}
+
+ esbuild@0.21.5:
+ resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
+ engines: {node: '>=12'}
+ hasBin: true
+
+ esbuild@0.27.7:
+ resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ estree-walker@3.0.3:
+ resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+
+ eventsource-parser@3.1.0:
+ resolution: {integrity: sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==}
+ engines: {node: '>=18.0.0'}
+
+ expand-template@2.0.3:
+ resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
+ engines: {node: '>=6'}
+
+ expect-type@1.3.0:
+ resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
+ engines: {node: '>=12.0.0'}
+
+ fast-xml-builder@1.2.0:
+ resolution: {integrity: sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==}
+
+ fast-xml-parser@5.8.0:
+ resolution: {integrity: sha512-6bIM7fsJxeo3uXv7OncQYsBAMPJ7V16Slahl/6M98C/i2q+vB1+4a0MtrvYwDFEUrwDSbAmeLDRXsOBwrL7yAg==}
+ hasBin: true
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ file-type@21.3.4:
+ resolution: {integrity: sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==}
+ engines: {node: '>=20'}
+
+ fs-constants@1.0.0:
+ resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
+
+ fsevents@2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ github-from-package@0.0.0:
+ resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
+
+ html-encoding-sniffer@6.0.0:
+ resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ http-proxy-agent@7.0.2:
+ resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
+ engines: {node: '>= 14'}
+
+ https-proxy-agent@7.0.6:
+ resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+ engines: {node: '>= 14'}
+
+ ieee754@1.2.1:
+ resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+
+ inherits@2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+ ini@1.3.8:
+ resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+
+ ini@6.0.0:
+ resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==}
+ engines: {node: ^20.17.0 || >=22.9.0}
+
+ is-potential-custom-element-name@1.0.1:
+ resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
+ jsdom@27.4.0:
+ resolution: {integrity: sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+ peerDependencies:
+ canvas: ^3.0.0
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+
+ json-schema@0.4.0:
+ resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
+
+ just-bash@2.14.5:
+ resolution: {integrity: sha512-MCBGnRlDeZ/MM7mcw+ZuSGFMBsggajrmKz6e/hrOAN7syvVZkjiY+Vh2wyCwN/CdcnAX5SxbiQB51n5nrQuX+g==}
+ hasBin: true
+
+ lru-cache@11.5.1:
+ resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==}
+ engines: {node: 20 || >=22}
+
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+
+ mdn-data@2.27.1:
+ resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==}
+
+ mimic-response@3.1.0:
+ resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
+ engines: {node: '>=10'}
+
+ minimatch@10.2.5:
+ resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
+ engines: {node: 18 || 20 || >=22}
+
+ minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
+ mkdirp-classic@0.5.3:
+ resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
+
+ modern-tar@0.7.6:
+ resolution: {integrity: sha512-sweCIVXzx1aIGTCdzcMlSZt1h8k5Tmk08VNAuRk3IU28XamGiOH5ypi11g6De2CH7PhYqSSnGy2A/EFhbWnVKg==}
+ engines: {node: '>=18.0.0'}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ nanoid@3.3.12:
+ resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ napi-build-utils@2.0.0:
+ resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
+
+ node-abi@3.92.0:
+ resolution: {integrity: sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==}
+ engines: {node: '>=10'}
+
+ node-addon-api@8.8.0:
+ resolution: {integrity: sha512-c5Ko1fZJIJmzhFIkhRN76WTq+fC6tWnGy9CXA0fA+XygsWZmEwG8vmbkNqxMyoaa0Tin4djul49NzdVcJJcjeA==}
+ engines: {node: ^18 || ^20 || >= 21}
+
+ node-gyp-build@4.8.4:
+ resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
+ hasBin: true
+
+ node-liblzma@2.2.0:
+ resolution: {integrity: sha512-s0KzNOWwOJJgPG6wxg6cKohnAl9Wk/oW1KrQaVzJBjQwVcUGPQCzpR46Ximygjqj/3KhOrtJXnYMp/xYAXp75g==}
+ engines: {node: '>=16.0.0'}
+ hasBin: true
+
+ obug@2.1.1:
+ resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==}
+
+ once@1.4.0:
+ resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+
+ pako@2.1.0:
+ resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==}
+
+ papaparse@5.5.3:
+ resolution: {integrity: sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==}
+
+ parse5@8.0.1:
+ resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==}
+
+ path-expression-matcher@1.5.0:
+ resolution: {integrity: sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==}
+ engines: {node: '>=14.0.0'}
+
+ pathe@2.0.3:
+ resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@4.0.4:
+ resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
+ engines: {node: '>=12'}
+
+ playwright-core@1.60.0:
+ resolution: {integrity: sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ playwright@1.60.0:
+ resolution: {integrity: sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ postcss@8.5.15:
+ resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prebuild-install@7.1.3:
+ resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
+ engines: {node: '>=10'}
+ deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available.
+ hasBin: true
+
+ pump@3.0.4:
+ resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ quickjs-emscripten-core@0.32.0:
+ resolution: {integrity: sha512-QFnPfjFey8EqknSrSxe1hZrf1/8z7/6s1QzGOmKo6++02r7QRRX7ZoyNaZh7JuVjWsVW87KnQrbZqnHkOAzUyg==}
+
+ quickjs-emscripten@0.32.0:
+ resolution: {integrity: sha512-So0Sqw869y/S2oE3Nuc0uT3Dhqgvsj8FSrwBdsuTosVsG8ME5/OcudU1GxsrIFdFABgy17GHnTVO9TYV/bLQcA==}
+ engines: {node: '>=16.0.0'}
+
+ rc@1.2.8:
+ resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
+ hasBin: true
+
+ re2js@1.3.3:
+ resolution: {integrity: sha512-s/I5zEAo79SUK0Qw4dpZKpiMwbQ6Gz0KU2NRr7eaO4x/p2g7Vvmn3hdeXDg8VsaUjfj/ora+e9oi27LX/C9+mw==}
+
+ react-dom@19.2.6:
+ resolution: {integrity: sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==}
+ peerDependencies:
+ react: ^19.2.6
+
+ react@19.2.6:
+ resolution: {integrity: sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==}
+ engines: {node: '>=0.10.0'}
+
+ readable-stream@3.6.2:
+ resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
+ engines: {node: '>= 6'}
+
+ require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
+ resolve.exports@2.0.3:
+ resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==}
+ engines: {node: '>=10'}
+
+ rollup@4.60.4:
+ resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ safe-buffer@5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
+ saxes@6.0.0:
+ resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+ engines: {node: '>=v12.22.7'}
+
+ scheduler@0.27.0:
+ resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
+
+ seek-bzip@2.0.0:
+ resolution: {integrity: sha512-SMguiTnYrhpLdk3PwfzHeotrcwi8bNV4iemL9tx9poR/yeaMYwB9VzR1w7b57DuWpuqR8n6oZboi0hj3AxZxQg==}
+ hasBin: true
+
+ semver@7.8.1:
+ resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ siginfo@2.0.0:
+ resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+
+ simple-concat@1.0.1:
+ resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
+
+ simple-get@4.0.1:
+ resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
+
+ smol-toml@1.6.1:
+ resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==}
+ engines: {node: '>= 18'}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ sprintf-js@1.1.3:
+ resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==}
+
+ sql.js@1.14.1:
+ resolution: {integrity: sha512-gcj8zBWU5cFsi9WUP+4bFNXAyF1iRpA3LLyS/DP5xlrNzGmPIizUeBggKa8DbDwdqaKwUcTEnChtd2grWo/x/A==}
+
+ stackback@0.0.2:
+ resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+
+ std-env@4.1.0:
+ resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==}
+
+ string_decoder@1.3.0:
+ resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
+
+ strip-json-comments@2.0.1:
+ resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
+ engines: {node: '>=0.10.0'}
+
+ strnum@2.3.0:
+ resolution: {integrity: sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==}
+
+ strtok3@10.3.5:
+ resolution: {integrity: sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==}
+ engines: {node: '>=18'}
+
+ swr@2.4.1:
+ resolution: {integrity: sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA==}
+ peerDependencies:
+ react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ symbol-tree@3.2.4:
+ resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
+ tar-fs@2.1.4:
+ resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==}
+
+ tar-stream@2.2.0:
+ resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
+ engines: {node: '>=6'}
+
+ throttleit@2.1.0:
+ resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==}
+ engines: {node: '>=18'}
+
+ tinybench@2.9.0:
+ resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
+
+ tinyexec@1.2.4:
+ resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==}
+ engines: {node: '>=18'}
+
+ tinyglobby@0.2.17:
+ resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
+ engines: {node: '>=12.0.0'}
+
+ tinyrainbow@3.1.0:
+ resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==}
+ engines: {node: '>=14.0.0'}
+
+ tldts-core@7.4.2:
+ resolution: {integrity: sha512-nwEyF4vl4RSJjwSjBUmOSxc3BFPoIFdlRthJ6e+5v9P3bHNsoD06UjuqMUspqp7vsEZ1beaHi1km+optiE17yA==}
+
+ tldts@7.4.2:
+ resolution: {integrity: sha512-kCwffuaH8ntKtygnWe1b4BJKWiCUH30n5KfoTr6IchcXOwR7chAOFJxFrH3vjANafUYrIA4a7SDL+nn7SiR4Sw==}
+ hasBin: true
+
+ token-types@6.1.2:
+ resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==}
+ engines: {node: '>=14.16'}
+
+ tough-cookie@6.0.1:
+ resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==}
+ engines: {node: '>=16'}
+
+ tr46@6.0.0:
+ resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==}
+ engines: {node: '>=20'}
+
+ tunnel-agent@0.6.0:
+ resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
+
+ turndown@7.2.4:
+ resolution: {integrity: sha512-I8yFsfRzmzK0WV1pNNOA4A7y4RDfFxPRxb3t+e3ui14qSGOxGtiSP6GjeX+Y6CHb7HYaFj7ECUD7VE5kQMZWGQ==}
+ engines: {node: '>=18', npm: '>=9'}
+
+ typescript@5.9.3:
+ resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ uint8array-extras@1.5.0:
+ resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==}
+ engines: {node: '>=18'}
+
+ undici-types@7.24.6:
+ resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==}
+
+ use-sync-external-store@1.6.0:
+ resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ util-deprecate@1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+ uuid@10.0.0:
+ resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
+ deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).
+ hasBin: true
+
+ vite-plugin-top-level-await@1.6.0:
+ resolution: {integrity: sha512-bNhUreLamTIkoulCR9aDXbTbhLk6n1YE8NJUTTxl5RYskNRtzOR0ASzSjBVRtNdjIfngDXo11qOsybGLNsrdww==}
+ peerDependencies:
+ vite: '>=2.8'
+
+ vite-plugin-wasm@3.6.0:
+ resolution: {integrity: sha512-mL/QPziiIA4RAA6DkaZZzOstdwbW5jO4Vz7Zenj0wieKWBlNvIvX5L5ljum9lcUX0ShNfBgCNLKTjNkRVVqcsw==}
+ peerDependencies:
+ vite: ^2 || ^3 || ^4 || ^5 || ^6 || ^7 || ^8
+
+ vite@5.4.21:
+ resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^18.0.0 || >=20.0.0
+ less: '*'
+ lightningcss: ^1.21.0
+ sass: '*'
+ sass-embedded: '*'
+ stylus: '*'
+ sugarss: '*'
+ terser: ^5.4.0
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+
+ vitest@4.1.7:
+ resolution: {integrity: sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==}
+ engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@opentelemetry/api': ^1.9.0
+ '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0
+ '@vitest/browser-playwright': 4.1.7
+ '@vitest/browser-preview': 4.1.7
+ '@vitest/browser-webdriverio': 4.1.7
+ '@vitest/coverage-istanbul': 4.1.7
+ '@vitest/coverage-v8': 4.1.7
+ '@vitest/ui': 4.1.7
+ happy-dom: '*'
+ jsdom: '*'
+ vite: ^6.0.0 || ^7.0.0 || ^8.0.0
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@opentelemetry/api':
+ optional: true
+ '@types/node':
+ optional: true
+ '@vitest/browser-playwright':
+ optional: true
+ '@vitest/browser-preview':
+ optional: true
+ '@vitest/browser-webdriverio':
+ optional: true
+ '@vitest/coverage-istanbul':
+ optional: true
+ '@vitest/coverage-v8':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+
+ w3c-xmlserializer@5.0.0:
+ resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+ engines: {node: '>=18'}
+
+ webidl-conversions@8.0.1:
+ resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
+ engines: {node: '>=20'}
+
+ whatwg-mimetype@4.0.0:
+ resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
+ engines: {node: '>=18'}
+
+ whatwg-mimetype@5.0.0:
+ resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==}
+ engines: {node: '>=20'}
+
+ whatwg-url@15.1.0:
+ resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==}
+ engines: {node: '>=20'}
+
+ why-is-node-running@2.3.0:
+ resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
+ engines: {node: '>=8'}
+ hasBin: true
+
+ wrappy@1.0.2:
+ resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+ ws@8.21.0:
+ resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ xml-name-validator@5.0.0:
+ resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+ engines: {node: '>=18'}
+
+ xml-naming@0.1.0:
+ resolution: {integrity: sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==}
+ engines: {node: '>=16.0.0'}
+
+ xmlchars@2.2.0:
+ resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
+ yaml@2.9.0:
+ resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==}
+ engines: {node: '>= 14.6'}
+ hasBin: true
+
+ zod@4.4.3:
+ resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==}
+
+snapshots:
+
+ '@acemir/cssom@0.9.31': {}
+
+ '@ai-sdk/gateway@3.0.121(zod@4.4.3)':
+ dependencies:
+ '@ai-sdk/provider': 3.0.10
+ '@ai-sdk/provider-utils': 4.0.27(zod@4.4.3)
+ '@vercel/oidc': 3.2.0
+ zod: 4.4.3
+
+ '@ai-sdk/openai@3.0.67(zod@4.4.3)':
+ dependencies:
+ '@ai-sdk/provider': 3.0.10
+ '@ai-sdk/provider-utils': 4.0.27(zod@4.4.3)
+ zod: 4.4.3
+
+ '@ai-sdk/provider-utils@4.0.27(zod@4.4.3)':
+ dependencies:
+ '@ai-sdk/provider': 3.0.10
+ '@standard-schema/spec': 1.1.0
+ eventsource-parser: 3.1.0
+ zod: 4.4.3
+
+ '@ai-sdk/provider@3.0.10':
+ dependencies:
+ json-schema: 0.4.0
+
+ '@ai-sdk/react@3.0.195(react@19.2.6)(zod@4.4.3)':
+ dependencies:
+ '@ai-sdk/provider-utils': 4.0.27(zod@4.4.3)
+ ai: 6.0.193(zod@4.4.3)
+ react: 19.2.6
+ swr: 2.4.1(react@19.2.6)
+ throttleit: 2.1.0
+ transitivePeerDependencies:
+ - zod
+
+ '@asamuzakjp/css-color@4.1.2':
+ dependencies:
+ '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+ lru-cache: 11.5.1
+
+ '@asamuzakjp/dom-selector@6.8.1':
+ dependencies:
+ '@asamuzakjp/nwsapi': 2.3.9
+ bidi-js: 1.0.3
+ css-tree: 3.2.1
+ is-potential-custom-element-name: 1.0.1
+ lru-cache: 11.5.1
+
+ '@asamuzakjp/nwsapi@2.3.9': {}
+
+ '@borewit/text-codec@0.2.2': {}
+
+ '@csstools/color-helpers@6.0.2': {}
+
+ '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
+ dependencies:
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@csstools/css-color-parser@4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
+ dependencies:
+ '@csstools/color-helpers': 6.0.2
+ '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)':
+ dependencies:
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@csstools/css-syntax-patches-for-csstree@1.1.4(css-tree@3.2.1)':
+ optionalDependencies:
+ css-tree: 3.2.1
+
+ '@csstools/css-tokenizer@4.0.0': {}
+
+ '@esbuild/aix-ppc64@0.21.5':
+ optional: true
+
+ '@esbuild/aix-ppc64@0.27.7':
+ optional: true
+
+ '@esbuild/android-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/android-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/android-arm@0.21.5':
+ optional: true
+
+ '@esbuild/android-arm@0.27.7':
+ optional: true
+
+ '@esbuild/android-x64@0.21.5':
+ optional: true
+
+ '@esbuild/android-x64@0.27.7':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/darwin-x64@0.21.5':
+ optional: true
+
+ '@esbuild/darwin-x64@0.27.7':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.21.5':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-arm@0.21.5':
+ optional: true
+
+ '@esbuild/linux-arm@0.27.7':
+ optional: true
+
+ '@esbuild/linux-ia32@0.21.5':
+ optional: true
+
+ '@esbuild/linux-ia32@0.27.7':
+ optional: true
+
+ '@esbuild/linux-loong64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-loong64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.21.5':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.27.7':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.27.7':
+ optional: true
+
+ '@esbuild/linux-s390x@0.21.5':
+ optional: true
+
+ '@esbuild/linux-s390x@0.27.7':
+ optional: true
+
+ '@esbuild/linux-x64@0.21.5':
+ optional: true
+
+ '@esbuild/linux-x64@0.27.7':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.21.5':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.27.7':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.21.5':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.27.7':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/sunos-x64@0.21.5':
+ optional: true
+
+ '@esbuild/sunos-x64@0.27.7':
+ optional: true
+
+ '@esbuild/win32-arm64@0.21.5':
+ optional: true
+
+ '@esbuild/win32-arm64@0.27.7':
+ optional: true
+
+ '@esbuild/win32-ia32@0.21.5':
+ optional: true
+
+ '@esbuild/win32-ia32@0.27.7':
+ optional: true
+
+ '@esbuild/win32-x64@0.21.5':
+ optional: true
+
+ '@esbuild/win32-x64@0.27.7':
+ optional: true
+
+ '@exodus/bytes@1.15.1': {}
+
+ '@jitl/quickjs-ffi-types@0.32.0': {}
+
+ '@jitl/quickjs-wasmfile-debug-asyncify@0.32.0':
+ dependencies:
+ '@jitl/quickjs-ffi-types': 0.32.0
+
+ '@jitl/quickjs-wasmfile-debug-sync@0.32.0':
+ dependencies:
+ '@jitl/quickjs-ffi-types': 0.32.0
+
+ '@jitl/quickjs-wasmfile-release-asyncify@0.32.0':
+ dependencies:
+ '@jitl/quickjs-ffi-types': 0.32.0
+
+ '@jitl/quickjs-wasmfile-release-sync@0.32.0':
+ dependencies:
+ '@jitl/quickjs-ffi-types': 0.32.0
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@mixmark-io/domino@2.2.0': {}
+
+ '@mongodb-js/zstd@7.0.0':
+ dependencies:
+ node-addon-api: 8.8.0
+ prebuild-install: 7.1.3
+ optional: true
+
+ '@nodable/entities@2.1.1': {}
+
+ '@opentelemetry/api@1.9.1': {}
+
+ '@playwright/test@1.60.0':
+ dependencies:
+ playwright: 1.60.0
+
+ '@rollup/plugin-virtual@3.0.2(rollup@4.60.4)':
+ optionalDependencies:
+ rollup: 4.60.4
+
+ '@rollup/rollup-android-arm-eabi@4.60.4':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.60.4':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.60.4':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.60.4':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.60.4':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.60.4':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.60.4':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.60.4':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.60.4':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.60.4':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-gnu@4.60.4':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-musl@4.60.4':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-gnu@4.60.4':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-musl@4.60.4':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.60.4':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.60.4':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.60.4':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.60.4':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.60.4':
+ optional: true
+
+ '@rollup/rollup-openbsd-x64@4.60.4':
+ optional: true
+
+ '@rollup/rollup-openharmony-arm64@4.60.4':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.60.4':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.60.4':
+ optional: true
+
+ '@rollup/rollup-win32-x64-gnu@4.60.4':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.60.4':
+ optional: true
+
+ '@standard-schema/spec@1.1.0': {}
+
+ '@swc/core-darwin-arm64@1.15.40':
+ optional: true
+
+ '@swc/core-darwin-x64@1.15.40':
+ optional: true
+
+ '@swc/core-linux-arm-gnueabihf@1.15.40':
+ optional: true
+
+ '@swc/core-linux-arm64-gnu@1.15.40':
+ optional: true
+
+ '@swc/core-linux-arm64-musl@1.15.40':
+ optional: true
+
+ '@swc/core-linux-ppc64-gnu@1.15.40':
+ optional: true
+
+ '@swc/core-linux-s390x-gnu@1.15.40':
+ optional: true
+
+ '@swc/core-linux-x64-gnu@1.15.40':
+ optional: true
+
+ '@swc/core-linux-x64-musl@1.15.40':
+ optional: true
+
+ '@swc/core-win32-arm64-msvc@1.15.40':
+ optional: true
+
+ '@swc/core-win32-ia32-msvc@1.15.40':
+ optional: true
+
+ '@swc/core-win32-x64-msvc@1.15.40':
+ optional: true
+
+ '@swc/core@1.15.40':
+ dependencies:
+ '@swc/counter': 0.1.3
+ '@swc/types': 0.1.26
+ optionalDependencies:
+ '@swc/core-darwin-arm64': 1.15.40
+ '@swc/core-darwin-x64': 1.15.40
+ '@swc/core-linux-arm-gnueabihf': 1.15.40
+ '@swc/core-linux-arm64-gnu': 1.15.40
+ '@swc/core-linux-arm64-musl': 1.15.40
+ '@swc/core-linux-ppc64-gnu': 1.15.40
+ '@swc/core-linux-s390x-gnu': 1.15.40
+ '@swc/core-linux-x64-gnu': 1.15.40
+ '@swc/core-linux-x64-musl': 1.15.40
+ '@swc/core-win32-arm64-msvc': 1.15.40
+ '@swc/core-win32-ia32-msvc': 1.15.40
+ '@swc/core-win32-x64-msvc': 1.15.40
+
+ '@swc/counter@0.1.3': {}
+
+ '@swc/types@0.1.26':
+ dependencies:
+ '@swc/counter': 0.1.3
+
+ '@swc/wasm@1.15.40': {}
+
+ '@tokenizer/inflate@0.4.1':
+ dependencies:
+ debug: 4.4.3
+ token-types: 6.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@tokenizer/token@0.3.0': {}
+
+ '@types/chai@5.2.3':
+ dependencies:
+ '@types/deep-eql': 4.0.2
+ assertion-error: 2.0.1
+
+ '@types/css-tree@2.3.11': {}
+
+ '@types/deep-eql@4.0.2': {}
+
+ '@types/estree@1.0.8': {}
+
+ '@types/estree@1.0.9': {}
+
+ '@types/node@25.9.1':
+ dependencies:
+ undici-types: 7.24.6
+
+ '@types/pako@2.0.4': {}
+
+ '@vercel/oidc@3.2.0': {}
+
+ '@vitest/expect@4.1.7':
+ dependencies:
+ '@standard-schema/spec': 1.1.0
+ '@types/chai': 5.2.3
+ '@vitest/spy': 4.1.7
+ '@vitest/utils': 4.1.7
+ chai: 6.2.2
+ tinyrainbow: 3.1.0
+
+ '@vitest/mocker@4.1.7(vite@5.4.21(@types/node@25.9.1))':
+ dependencies:
+ '@vitest/spy': 4.1.7
+ estree-walker: 3.0.3
+ magic-string: 0.30.21
+ optionalDependencies:
+ vite: 5.4.21(@types/node@25.9.1)
+
+ '@vitest/pretty-format@4.1.7':
+ dependencies:
+ tinyrainbow: 3.1.0
+
+ '@vitest/runner@4.1.7':
+ dependencies:
+ '@vitest/utils': 4.1.7
+ pathe: 2.0.3
+
+ '@vitest/snapshot@4.1.7':
+ dependencies:
+ '@vitest/pretty-format': 4.1.7
+ '@vitest/utils': 4.1.7
+ magic-string: 0.30.21
+ pathe: 2.0.3
+
+ '@vitest/spy@4.1.7': {}
+
+ '@vitest/utils@4.1.7':
+ dependencies:
+ '@vitest/pretty-format': 4.1.7
+ convert-source-map: 2.0.0
+ tinyrainbow: 3.1.0
+
+ '@xterm/addon-fit@0.11.0': {}
+
+ '@xterm/xterm@6.0.0': {}
+
+ acorn-jsx@5.3.2(acorn@8.16.0):
+ dependencies:
+ acorn: 8.16.0
+
+ acorn-typescript@1.4.13(acorn@8.16.0):
+ dependencies:
+ acorn: 8.16.0
+
+ acorn@8.16.0: {}
+
+ agent-base@7.1.4: {}
+
+ ai@6.0.193(zod@4.4.3):
+ dependencies:
+ '@ai-sdk/gateway': 3.0.121(zod@4.4.3)
+ '@ai-sdk/provider': 3.0.10
+ '@ai-sdk/provider-utils': 4.0.27(zod@4.4.3)
+ '@opentelemetry/api': 1.9.1
+ zod: 4.4.3
+
+ assertion-error@2.0.1: {}
+
+ balanced-match@4.0.4: {}
+
+ base64-js@1.5.1: {}
+
+ bidi-js@1.0.3:
+ dependencies:
+ require-from-string: 2.0.2
+
+ bl@4.1.0:
+ dependencies:
+ buffer: 5.7.1
+ inherits: 2.0.4
+ readable-stream: 3.6.2
+ optional: true
+
+ brace-expansion@5.0.6:
+ dependencies:
+ balanced-match: 4.0.4
+
+ brotli-wasm@3.0.1: {}
+
+ brotli@1.3.3:
+ dependencies:
+ base64-js: 1.5.1
+
+ buffer@5.7.1:
+ dependencies:
+ base64-js: 1.5.1
+ ieee754: 1.2.1
+ optional: true
+
+ chai@6.2.2: {}
+
+ chownr@1.1.4:
+ optional: true
+
+ comlink@4.4.2: {}
+
+ commander@6.2.1: {}
+
+ convert-source-map@2.0.0: {}
+
+ css-tree@3.2.1:
+ dependencies:
+ mdn-data: 2.27.1
+ source-map-js: 1.2.1
+
+ cssstyle@5.3.7:
+ dependencies:
+ '@asamuzakjp/css-color': 4.1.2
+ '@csstools/css-syntax-patches-for-csstree': 1.1.4(css-tree@3.2.1)
+ css-tree: 3.2.1
+ lru-cache: 11.5.1
+
+ data-urls@6.0.1:
+ dependencies:
+ whatwg-mimetype: 5.0.0
+ whatwg-url: 15.1.0
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ decimal.js@10.6.0: {}
+
+ decompress-response@6.0.0:
+ dependencies:
+ mimic-response: 3.1.0
+ optional: true
+
+ deep-extend@0.6.0:
+ optional: true
+
+ dequal@2.0.3: {}
+
+ detect-libc@2.1.2:
+ optional: true
+
+ diff@8.0.4: {}
+
+ dotenv@17.4.2: {}
+
+ end-of-stream@1.4.5:
+ dependencies:
+ once: 1.4.0
+ optional: true
+
+ entities@8.0.0: {}
+
+ es-module-lexer@2.1.0: {}
+
+ esbuild@0.21.5:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.21.5
+ '@esbuild/android-arm': 0.21.5
+ '@esbuild/android-arm64': 0.21.5
+ '@esbuild/android-x64': 0.21.5
+ '@esbuild/darwin-arm64': 0.21.5
+ '@esbuild/darwin-x64': 0.21.5
+ '@esbuild/freebsd-arm64': 0.21.5
+ '@esbuild/freebsd-x64': 0.21.5
+ '@esbuild/linux-arm': 0.21.5
+ '@esbuild/linux-arm64': 0.21.5
+ '@esbuild/linux-ia32': 0.21.5
+ '@esbuild/linux-loong64': 0.21.5
+ '@esbuild/linux-mips64el': 0.21.5
+ '@esbuild/linux-ppc64': 0.21.5
+ '@esbuild/linux-riscv64': 0.21.5
+ '@esbuild/linux-s390x': 0.21.5
+ '@esbuild/linux-x64': 0.21.5
+ '@esbuild/netbsd-x64': 0.21.5
+ '@esbuild/openbsd-x64': 0.21.5
+ '@esbuild/sunos-x64': 0.21.5
+ '@esbuild/win32-arm64': 0.21.5
+ '@esbuild/win32-ia32': 0.21.5
+ '@esbuild/win32-x64': 0.21.5
+
+ esbuild@0.27.7:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.27.7
+ '@esbuild/android-arm': 0.27.7
+ '@esbuild/android-arm64': 0.27.7
+ '@esbuild/android-x64': 0.27.7
+ '@esbuild/darwin-arm64': 0.27.7
+ '@esbuild/darwin-x64': 0.27.7
+ '@esbuild/freebsd-arm64': 0.27.7
+ '@esbuild/freebsd-x64': 0.27.7
+ '@esbuild/linux-arm': 0.27.7
+ '@esbuild/linux-arm64': 0.27.7
+ '@esbuild/linux-ia32': 0.27.7
+ '@esbuild/linux-loong64': 0.27.7
+ '@esbuild/linux-mips64el': 0.27.7
+ '@esbuild/linux-ppc64': 0.27.7
+ '@esbuild/linux-riscv64': 0.27.7
+ '@esbuild/linux-s390x': 0.27.7
+ '@esbuild/linux-x64': 0.27.7
+ '@esbuild/netbsd-arm64': 0.27.7
+ '@esbuild/netbsd-x64': 0.27.7
+ '@esbuild/openbsd-arm64': 0.27.7
+ '@esbuild/openbsd-x64': 0.27.7
+ '@esbuild/openharmony-arm64': 0.27.7
+ '@esbuild/sunos-x64': 0.27.7
+ '@esbuild/win32-arm64': 0.27.7
+ '@esbuild/win32-ia32': 0.27.7
+ '@esbuild/win32-x64': 0.27.7
+
+ estree-walker@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.9
+
+ eventsource-parser@3.1.0: {}
+
+ expand-template@2.0.3:
+ optional: true
+
+ expect-type@1.3.0: {}
+
+ fast-xml-builder@1.2.0:
+ dependencies:
+ path-expression-matcher: 1.5.0
+ xml-naming: 0.1.0
+
+ fast-xml-parser@5.8.0:
+ dependencies:
+ '@nodable/entities': 2.1.1
+ fast-xml-builder: 1.2.0
+ path-expression-matcher: 1.5.0
+ strnum: 2.3.0
+ xml-naming: 0.1.0
+
+ fdir@6.5.0(picomatch@4.0.4):
+ optionalDependencies:
+ picomatch: 4.0.4
+
+ file-type@21.3.4:
+ dependencies:
+ '@tokenizer/inflate': 0.4.1
+ strtok3: 10.3.5
+ token-types: 6.1.2
+ uint8array-extras: 1.5.0
+ transitivePeerDependencies:
+ - supports-color
+
+ fs-constants@1.0.0:
+ optional: true
+
+ fsevents@2.3.2:
+ optional: true
+
+ fsevents@2.3.3:
+ optional: true
+
+ github-from-package@0.0.0:
+ optional: true
+
+ html-encoding-sniffer@6.0.0:
+ dependencies:
+ '@exodus/bytes': 1.15.1
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
+ http-proxy-agent@7.0.2:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ https-proxy-agent@7.0.6:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ ieee754@1.2.1: {}
+
+ inherits@2.0.4:
+ optional: true
+
+ ini@1.3.8:
+ optional: true
+
+ ini@6.0.0: {}
+
+ is-potential-custom-element-name@1.0.1: {}
+
+ jsdom@27.4.0:
+ dependencies:
+ '@acemir/cssom': 0.9.31
+ '@asamuzakjp/dom-selector': 6.8.1
+ '@exodus/bytes': 1.15.1
+ cssstyle: 5.3.7
+ data-urls: 6.0.1
+ decimal.js: 10.6.0
+ html-encoding-sniffer: 6.0.0
+ http-proxy-agent: 7.0.2
+ https-proxy-agent: 7.0.6
+ is-potential-custom-element-name: 1.0.1
+ parse5: 8.0.1
+ saxes: 6.0.0
+ symbol-tree: 3.2.4
+ tough-cookie: 6.0.1
+ w3c-xmlserializer: 5.0.0
+ webidl-conversions: 8.0.1
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 15.1.0
+ ws: 8.21.0
+ xml-name-validator: 5.0.0
+ transitivePeerDependencies:
+ - '@noble/hashes'
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
+ json-schema@0.4.0: {}
+
+ just-bash@2.14.5:
+ dependencies:
+ diff: 8.0.4
+ fast-xml-parser: 5.8.0
+ file-type: 21.3.4
+ ini: 6.0.0
+ minimatch: 10.2.5
+ modern-tar: 0.7.6
+ papaparse: 5.5.3
+ quickjs-emscripten: 0.32.0
+ re2js: 1.3.3
+ seek-bzip: 2.0.0
+ smol-toml: 1.6.1
+ sprintf-js: 1.1.3
+ sql.js: 1.14.1
+ turndown: 7.2.4
+ yaml: 2.9.0
+ optionalDependencies:
+ '@mongodb-js/zstd': 7.0.0
+ node-liblzma: 2.2.0
+ transitivePeerDependencies:
+ - supports-color
+
+ lru-cache@11.5.1: {}
+
+ magic-string@0.30.21:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ mdn-data@2.27.1: {}
+
+ mimic-response@3.1.0:
+ optional: true
+
+ minimatch@10.2.5:
+ dependencies:
+ brace-expansion: 5.0.6
+
+ minimist@1.2.8:
+ optional: true
+
+ mkdirp-classic@0.5.3:
+ optional: true
+
+ modern-tar@0.7.6: {}
+
+ ms@2.1.3: {}
+
+ nanoid@3.3.12: {}
+
+ napi-build-utils@2.0.0:
+ optional: true
+
+ node-abi@3.92.0:
+ dependencies:
+ semver: 7.8.1
+ optional: true
+
+ node-addon-api@8.8.0:
+ optional: true
+
+ node-gyp-build@4.8.4:
+ optional: true
+
+ node-liblzma@2.2.0:
+ dependencies:
+ node-addon-api: 8.8.0
+ node-gyp-build: 4.8.4
+ optional: true
+
+ obug@2.1.1: {}
+
+ once@1.4.0:
+ dependencies:
+ wrappy: 1.0.2
+ optional: true
+
+ pako@2.1.0: {}
+
+ papaparse@5.5.3: {}
+
+ parse5@8.0.1:
+ dependencies:
+ entities: 8.0.0
+
+ path-expression-matcher@1.5.0: {}
+
+ pathe@2.0.3: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@4.0.4: {}
+
+ playwright-core@1.60.0: {}
+
+ playwright@1.60.0:
+ dependencies:
+ playwright-core: 1.60.0
+ optionalDependencies:
+ fsevents: 2.3.2
+
+ postcss@8.5.15:
+ dependencies:
+ nanoid: 3.3.12
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ prebuild-install@7.1.3:
+ dependencies:
+ detect-libc: 2.1.2
+ expand-template: 2.0.3
+ github-from-package: 0.0.0
+ minimist: 1.2.8
+ mkdirp-classic: 0.5.3
+ napi-build-utils: 2.0.0
+ node-abi: 3.92.0
+ pump: 3.0.4
+ rc: 1.2.8
+ simple-get: 4.0.1
+ tar-fs: 2.1.4
+ tunnel-agent: 0.6.0
+ optional: true
+
+ pump@3.0.4:
+ dependencies:
+ end-of-stream: 1.4.5
+ once: 1.4.0
+ optional: true
+
+ punycode@2.3.1: {}
+
+ quickjs-emscripten-core@0.32.0:
+ dependencies:
+ '@jitl/quickjs-ffi-types': 0.32.0
+
+ quickjs-emscripten@0.32.0:
+ dependencies:
+ '@jitl/quickjs-wasmfile-debug-asyncify': 0.32.0
+ '@jitl/quickjs-wasmfile-debug-sync': 0.32.0
+ '@jitl/quickjs-wasmfile-release-asyncify': 0.32.0
+ '@jitl/quickjs-wasmfile-release-sync': 0.32.0
+ quickjs-emscripten-core: 0.32.0
+
+ rc@1.2.8:
+ dependencies:
+ deep-extend: 0.6.0
+ ini: 1.3.8
+ minimist: 1.2.8
+ strip-json-comments: 2.0.1
+ optional: true
+
+ re2js@1.3.3: {}
+
+ react-dom@19.2.6(react@19.2.6):
+ dependencies:
+ react: 19.2.6
+ scheduler: 0.27.0
+
+ react@19.2.6: {}
+
+ readable-stream@3.6.2:
+ dependencies:
+ inherits: 2.0.4
+ string_decoder: 1.3.0
+ util-deprecate: 1.0.2
+ optional: true
+
+ require-from-string@2.0.2: {}
+
+ resolve.exports@2.0.3: {}
+
+ rollup@4.60.4:
+ dependencies:
+ '@types/estree': 1.0.8
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.60.4
+ '@rollup/rollup-android-arm64': 4.60.4
+ '@rollup/rollup-darwin-arm64': 4.60.4
+ '@rollup/rollup-darwin-x64': 4.60.4
+ '@rollup/rollup-freebsd-arm64': 4.60.4
+ '@rollup/rollup-freebsd-x64': 4.60.4
+ '@rollup/rollup-linux-arm-gnueabihf': 4.60.4
+ '@rollup/rollup-linux-arm-musleabihf': 4.60.4
+ '@rollup/rollup-linux-arm64-gnu': 4.60.4
+ '@rollup/rollup-linux-arm64-musl': 4.60.4
+ '@rollup/rollup-linux-loong64-gnu': 4.60.4
+ '@rollup/rollup-linux-loong64-musl': 4.60.4
+ '@rollup/rollup-linux-ppc64-gnu': 4.60.4
+ '@rollup/rollup-linux-ppc64-musl': 4.60.4
+ '@rollup/rollup-linux-riscv64-gnu': 4.60.4
+ '@rollup/rollup-linux-riscv64-musl': 4.60.4
+ '@rollup/rollup-linux-s390x-gnu': 4.60.4
+ '@rollup/rollup-linux-x64-gnu': 4.60.4
+ '@rollup/rollup-linux-x64-musl': 4.60.4
+ '@rollup/rollup-openbsd-x64': 4.60.4
+ '@rollup/rollup-openharmony-arm64': 4.60.4
+ '@rollup/rollup-win32-arm64-msvc': 4.60.4
+ '@rollup/rollup-win32-ia32-msvc': 4.60.4
+ '@rollup/rollup-win32-x64-gnu': 4.60.4
+ '@rollup/rollup-win32-x64-msvc': 4.60.4
+ fsevents: 2.3.3
+
+ safe-buffer@5.2.1:
+ optional: true
+
+ saxes@6.0.0:
+ dependencies:
+ xmlchars: 2.2.0
+
+ scheduler@0.27.0: {}
+
+ seek-bzip@2.0.0:
+ dependencies:
+ commander: 6.2.1
+
+ semver@7.8.1:
+ optional: true
+
+ siginfo@2.0.0: {}
+
+ simple-concat@1.0.1:
+ optional: true
+
+ simple-get@4.0.1:
+ dependencies:
+ decompress-response: 6.0.0
+ once: 1.4.0
+ simple-concat: 1.0.1
+ optional: true
+
+ smol-toml@1.6.1: {}
+
+ source-map-js@1.2.1: {}
+
+ sprintf-js@1.1.3: {}
+
+ sql.js@1.14.1: {}
+
+ stackback@0.0.2: {}
+
+ std-env@4.1.0: {}
+
+ string_decoder@1.3.0:
+ dependencies:
+ safe-buffer: 5.2.1
+ optional: true
+
+ strip-json-comments@2.0.1:
+ optional: true
+
+ strnum@2.3.0: {}
+
+ strtok3@10.3.5:
+ dependencies:
+ '@tokenizer/token': 0.3.0
+
+ swr@2.4.1(react@19.2.6):
+ dependencies:
+ dequal: 2.0.3
+ react: 19.2.6
+ use-sync-external-store: 1.6.0(react@19.2.6)
+
+ symbol-tree@3.2.4: {}
+
+ tar-fs@2.1.4:
+ dependencies:
+ chownr: 1.1.4
+ mkdirp-classic: 0.5.3
+ pump: 3.0.4
+ tar-stream: 2.2.0
+ optional: true
+
+ tar-stream@2.2.0:
+ dependencies:
+ bl: 4.1.0
+ end-of-stream: 1.4.5
+ fs-constants: 1.0.0
+ inherits: 2.0.4
+ readable-stream: 3.6.2
+ optional: true
+
+ throttleit@2.1.0: {}
+
+ tinybench@2.9.0: {}
+
+ tinyexec@1.2.4: {}
+
+ tinyglobby@0.2.17:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.4)
+ picomatch: 4.0.4
+
+ tinyrainbow@3.1.0: {}
+
+ tldts-core@7.4.2: {}
+
+ tldts@7.4.2:
+ dependencies:
+ tldts-core: 7.4.2
+
+ token-types@6.1.2:
+ dependencies:
+ '@borewit/text-codec': 0.2.2
+ '@tokenizer/token': 0.3.0
+ ieee754: 1.2.1
+
+ tough-cookie@6.0.1:
+ dependencies:
+ tldts: 7.4.2
+
+ tr46@6.0.0:
+ dependencies:
+ punycode: 2.3.1
+
+ tunnel-agent@0.6.0:
+ dependencies:
+ safe-buffer: 5.2.1
+ optional: true
+
+ turndown@7.2.4:
+ dependencies:
+ '@mixmark-io/domino': 2.2.0
+
+ typescript@5.9.3: {}
+
+ uint8array-extras@1.5.0: {}
+
+ undici-types@7.24.6: {}
+
+ use-sync-external-store@1.6.0(react@19.2.6):
+ dependencies:
+ react: 19.2.6
+
+ util-deprecate@1.0.2:
+ optional: true
+
+ uuid@10.0.0: {}
+
+ vite-plugin-top-level-await@1.6.0(rollup@4.60.4)(vite@5.4.21(@types/node@25.9.1)):
+ dependencies:
+ '@rollup/plugin-virtual': 3.0.2(rollup@4.60.4)
+ '@swc/core': 1.15.40
+ '@swc/wasm': 1.15.40
+ uuid: 10.0.0
+ vite: 5.4.21(@types/node@25.9.1)
+ transitivePeerDependencies:
+ - '@swc/helpers'
+ - rollup
+
+ vite-plugin-wasm@3.6.0(vite@5.4.21(@types/node@25.9.1)):
+ dependencies:
+ vite: 5.4.21(@types/node@25.9.1)
+
+ vite@5.4.21(@types/node@25.9.1):
+ dependencies:
+ esbuild: 0.21.5
+ postcss: 8.5.15
+ rollup: 4.60.4
+ optionalDependencies:
+ '@types/node': 25.9.1
+ fsevents: 2.3.3
+
+ vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(jsdom@27.4.0)(vite@5.4.21(@types/node@25.9.1)):
+ dependencies:
+ '@vitest/expect': 4.1.7
+ '@vitest/mocker': 4.1.7(vite@5.4.21(@types/node@25.9.1))
+ '@vitest/pretty-format': 4.1.7
+ '@vitest/runner': 4.1.7
+ '@vitest/snapshot': 4.1.7
+ '@vitest/spy': 4.1.7
+ '@vitest/utils': 4.1.7
+ es-module-lexer: 2.1.0
+ expect-type: 1.3.0
+ magic-string: 0.30.21
+ obug: 2.1.1
+ pathe: 2.0.3
+ picomatch: 4.0.4
+ std-env: 4.1.0
+ tinybench: 2.9.0
+ tinyexec: 1.2.4
+ tinyglobby: 0.2.17
+ tinyrainbow: 3.1.0
+ vite: 5.4.21(@types/node@25.9.1)
+ why-is-node-running: 2.3.0
+ optionalDependencies:
+ '@opentelemetry/api': 1.9.1
+ '@types/node': 25.9.1
+ jsdom: 27.4.0
+ transitivePeerDependencies:
+ - msw
+
+ w3c-xmlserializer@5.0.0:
+ dependencies:
+ xml-name-validator: 5.0.0
+
+ webidl-conversions@8.0.1: {}
+
+ whatwg-mimetype@4.0.0: {}
+
+ whatwg-mimetype@5.0.0: {}
+
+ whatwg-url@15.1.0:
+ dependencies:
+ tr46: 6.0.0
+ webidl-conversions: 8.0.1
+
+ why-is-node-running@2.3.0:
+ dependencies:
+ siginfo: 2.0.0
+ stackback: 0.0.2
+
+ wrappy@1.0.2:
+ optional: true
+
+ ws@8.21.0: {}
+
+ xml-name-validator@5.0.0: {}
+
+ xml-naming@0.1.0: {}
+
+ xmlchars@2.2.0: {}
+
+ yaml@2.9.0: {}
+
+ zod@4.4.3: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
new file mode 100644
index 0000000..c013273
--- /dev/null
+++ b/pnpm-workspace.yaml
@@ -0,0 +1,5 @@
+allowBuilds:
+ '@mongodb-js/zstd': set this to true or false
+ '@swc/core': set this to true or false
+ esbuild: set this to true or false
+ node-liblzma: set this to true or false
diff --git a/scripts/bundle.ts b/scripts/bundle.ts
new file mode 100644
index 0000000..e2752e2
--- /dev/null
+++ b/scripts/bundle.ts
@@ -0,0 +1,34 @@
+import { cp, readFile, stat } from "node:fs/promises";
+
+import { build } from "esbuild";
+import { nodeModulesPolyfillPlugin } from "esbuild-plugins-node-modules-polyfill";
+
+await build({
+ entryPoints: ["./dist/index.mjs"],
+ bundle: true,
+ format: "esm",
+ outfile: "./dist/bundle.js",
+ plugins: [
+ {
+ name: "fix-almostnode",
+ setup(build) {
+ build.onLoad({ filter: /.*/ }, async (args) => {
+ if (
+ await stat(args.path)
+ .then((s) => s.isFile())
+ .catch(() => false)
+ ) {
+ let contents = await readFile(args.path, "utf-8");
+ if (args.path.endsWith("just-bash/dist/bundle/browser.js")) {
+ contents = contents.replace("import{constants as Yo,", "import{");
+ return {
+ contents,
+ };
+ }
+ }
+ });
+ },
+ },
+ nodeModulesPolyfillPlugin(),
+ ],
+});
diff --git a/src/frameworks/code-transforms.ts b/src/frameworks/code-transforms.ts
index 27679cc..e6fc1e6 100644
--- a/src/frameworks/code-transforms.ts
+++ b/src/frameworks/code-transforms.ts
@@ -353,6 +353,36 @@ export function transformEsmToCjsSimple(code: string): string {
}
}
+/**
+ * Collect the bound identifier names from a binding target, supporting
+ * destructuring (object/array patterns, defaults, rest). Used so that
+ * `export const { a, b } = obj` / `export const [x] = arr` export each name.
+ */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function collectPatternNames(node: any, out: string[]): void {
+ if (!node) return;
+ switch (node.type) {
+ case 'Identifier':
+ out.push(node.name);
+ break;
+ case 'ObjectPattern':
+ for (const prop of node.properties) {
+ if (prop.type === 'RestElement') collectPatternNames(prop.argument, out);
+ else collectPatternNames(prop.value, out);
+ }
+ break;
+ case 'ArrayPattern':
+ for (const el of node.elements) collectPatternNames(el, out);
+ break;
+ case 'AssignmentPattern':
+ collectPatternNames(node.left, out);
+ break;
+ case 'RestElement':
+ collectPatternNames(node.argument, out);
+ break;
+ }
+}
+
/** AST-based ESM→CJS transform using acorn. */
function transformEsmToCjsAst(code: string): string {
const ast = acorn.parse(code, { ecmaVersion: 'latest', sourceType: 'module' });
@@ -412,23 +442,22 @@ function transformEsmToCjsAst(code: string): string {
} else if (node.type === 'ExportNamedDeclaration') {
if (node.declaration) {
const decl = node.declaration;
- if (decl.type === 'FunctionDeclaration') {
- const name = decl.id.name;
- const funcCode = code.slice(decl.start, node.end);
- replacements.push([node.start, node.end, `exports.${name} = ${funcCode}`]);
- } else if (decl.type === 'ClassDeclaration') {
+ // Keep the original declaration (preserving the local binding so later
+ // code can still reference/mutate it), then assign it onto `exports`.
+ // Replacing the declaration outright with `exports.X = ...` would drop
+ // the local binding and break e.g. `export const o = {}; o.k = 1;`.
+ const declCode = code.slice(decl.start, decl.end);
+ if (decl.type === 'FunctionDeclaration' || decl.type === 'ClassDeclaration') {
const name = decl.id.name;
- const classCode = code.slice(decl.start, node.end);
- replacements.push([node.start, node.end, `exports.${name} = ${classCode}`]);
+ replacements.push([node.start, node.end, `${declCode}\nexports.${name} = ${name};`]);
} else if (decl.type === 'VariableDeclaration') {
- // export const X = ..., export let Y = ...
- const parts: string[] = [];
+ // export const X = ..., export let Y = ..., export const { a, b } = ...
+ const names: string[] = [];
for (const declarator of decl.declarations) {
- const name = declarator.id.name;
- const initCode = declarator.init ? code.slice(declarator.init.start, declarator.init.end) : 'undefined';
- parts.push(`exports.${name} = ${initCode}`);
+ collectPatternNames(declarator.id, names);
}
- replacements.push([node.start, node.end, parts.join(';\n')]);
+ const assigns = names.map(n => `exports.${n} = ${n};`).join('\n');
+ replacements.push([node.start, node.end, `${declCode}\n${assigns}`]);
}
} else if (node.source) {
// Re-export: export { X } from './module'
diff --git a/src/frameworks/jsx-config.ts b/src/frameworks/jsx-config.ts
new file mode 100644
index 0000000..ede1730
--- /dev/null
+++ b/src/frameworks/jsx-config.ts
@@ -0,0 +1,174 @@
+/**
+ * JSX transform configuration resolution.
+ *
+ * Determines how `.jsx`/`.tsx` files should be compiled by reading the nearest
+ * `tsconfig.json` (or `jsconfig.json`) in the VFS, walking upward from the
+ * directory of the script being transformed until one is found.
+ *
+ * The relevant `compilerOptions` are:
+ * - `jsx` "react" | "react-jsx" | "react-jsxdev" |
+ * "preserve" | "react-native"
+ * - `jsxImportSource` module to import the automatic runtime from
+ * (default "react")
+ * - `jsxFactory` classic element factory (default "React.createElement")
+ * - `jsxFragmentFactory` classic fragment factory (default "React.Fragment")
+ */
+
+/** Minimal filesystem surface needed to resolve config (matches VirtualFS). */
+export interface ConfigFs {
+ existsSync(path: string): boolean;
+ readFileSync(path: string, encoding: 'utf8'): string;
+}
+
+export type JsxMode = 'classic' | 'automatic' | 'automatic-dev';
+
+export interface JsxConfig {
+ mode: JsxMode;
+ /** Automatic runtime import source (e.g. "react"). */
+ importSource: string;
+ /** Classic element factory (e.g. "React.createElement"). */
+ factory: string;
+ /** Classic fragment factory (e.g. "React.Fragment"). */
+ fragmentFactory: string;
+}
+
+/** Defaults mirror a modern setup: automatic runtime, React. */
+export const DEFAULT_JSX_CONFIG: JsxConfig = {
+ mode: 'automatic',
+ importSource: 'react',
+ factory: 'React.createElement',
+ fragmentFactory: 'React.Fragment',
+};
+
+/**
+ * Strip `//` and block comments and trailing commas from JSONC text so that
+ * it can be parsed with `JSON.parse`. tsconfig/jsconfig files commonly contain
+ * comments.
+ */
+function parseJsonc(text: string): unknown {
+ let out = '';
+ let inString = false;
+ let stringQuote = '';
+ let inLineComment = false;
+ let inBlockComment = false;
+
+ for (let i = 0; i < text.length; i++) {
+ const ch = text[i];
+ const next = text[i + 1];
+
+ if (inLineComment) {
+ if (ch === '\n') {
+ inLineComment = false;
+ out += ch;
+ }
+ continue;
+ }
+ if (inBlockComment) {
+ if (ch === '*' && next === '/') {
+ inBlockComment = false;
+ i++;
+ }
+ continue;
+ }
+ if (inString) {
+ out += ch;
+ if (ch === '\\') {
+ // Preserve escaped char verbatim.
+ out += text[i + 1] ?? '';
+ i++;
+ } else if (ch === stringQuote) {
+ inString = false;
+ }
+ continue;
+ }
+
+ if (ch === '"' || ch === "'") {
+ inString = true;
+ stringQuote = ch;
+ out += ch;
+ continue;
+ }
+ if (ch === '/' && next === '/') {
+ inLineComment = true;
+ i++;
+ continue;
+ }
+ if (ch === '/' && next === '*') {
+ inBlockComment = true;
+ i++;
+ continue;
+ }
+ out += ch;
+ }
+
+ // Remove trailing commas: `,}` / `,]` (whitespace allowed between).
+ out = out.replace(/,(\s*[}\]])/g, '$1');
+ return JSON.parse(out);
+}
+
+function dirname(p: string): string {
+ const idx = p.lastIndexOf('/');
+ if (idx <= 0) return '/';
+ return p.slice(0, idx);
+}
+
+/** Map a tsconfig `jsx` option to our internal transform mode. */
+function modeFromJsxOption(jsx: unknown): JsxMode {
+ switch (jsx) {
+ case 'react':
+ return 'classic';
+ case 'react-jsxdev':
+ return 'automatic-dev';
+ case 'react-jsx':
+ case 'preserve':
+ case 'react-native':
+ default:
+ // "preserve"/"react-native" can't actually run as-is; fall back to the
+ // modern automatic runtime so the file still executes.
+ return 'automatic';
+ }
+}
+
+/**
+ * Resolve the JSX transform config for `filePath` by walking up the directory
+ * tree looking for a `tsconfig.json` or `jsconfig.json` in the VFS. Returns the
+ * default config if none is found.
+ */
+export function resolveJsxConfig(fs: ConfigFs, filePath: string): JsxConfig {
+ let dir = dirname(filePath);
+
+ // Walk upward toward the root.
+ // eslint-disable-next-line no-constant-condition
+ while (true) {
+ for (const name of ['tsconfig.json', 'jsconfig.json']) {
+ const candidate = `${dir === '/' ? '' : dir}/${name}`;
+ if (!fs.existsSync(candidate)) continue;
+ try {
+ const parsed = parseJsonc(fs.readFileSync(candidate, 'utf8')) as {
+ compilerOptions?: Record;
+ };
+ const co = parsed.compilerOptions ?? {};
+ return {
+ mode: 'jsx' in co ? modeFromJsxOption(co.jsx) : DEFAULT_JSX_CONFIG.mode,
+ importSource:
+ typeof co.jsxImportSource === 'string'
+ ? co.jsxImportSource
+ : DEFAULT_JSX_CONFIG.importSource,
+ factory:
+ typeof co.jsxFactory === 'string' ? co.jsxFactory : DEFAULT_JSX_CONFIG.factory,
+ fragmentFactory:
+ typeof co.jsxFragmentFactory === 'string'
+ ? co.jsxFragmentFactory
+ : DEFAULT_JSX_CONFIG.fragmentFactory,
+ };
+ } catch {
+ // Malformed config — keep walking; fall back to defaults if none parse.
+ }
+ }
+
+ if (dir === '/' || dir === '') break;
+ dir = dirname(dir);
+ }
+
+ return DEFAULT_JSX_CONFIG;
+}
diff --git a/src/frameworks/jsx-transform.ts b/src/frameworks/jsx-transform.ts
new file mode 100644
index 0000000..8c400c4
--- /dev/null
+++ b/src/frameworks/jsx-transform.ts
@@ -0,0 +1,310 @@
+/**
+ * JSX → JavaScript transform (pure JS, synchronous).
+ *
+ * Parses with `acorn` + `acorn-jsx` and replaces every JSX element/fragment
+ * with calls to the configured factory. Supports both the classic runtime
+ * (`React.createElement` / `React.Fragment`) and the automatic runtime
+ * (`react/jsx-runtime`'s `jsx`/`jsxs`/`Fragment`, or the `*-dev` variants).
+ *
+ * Synchronous and dependency-light (acorn-jsx is already used elsewhere), so it
+ * can run inside `require()` like the TypeScript type-stripping pass. For
+ * `.tsx`, types are stripped first (whitespace-preserving) and this pass runs
+ * over the resulting JS+JSX.
+ */
+
+import * as acorn from 'acorn';
+import jsx from 'acorn-jsx';
+import type { JsxConfig } from './jsx-config';
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const JsxParser = (acorn.Parser as any).extend(jsx());
+
+/* eslint-disable @typescript-eslint/no-explicit-any */
+type Node = any;
+
+/** Module resolution extensions to try for JSX files. */
+export const JSX_RESOLVE_EXTENSIONS = ['.tsx', '.jsx'] as const;
+
+/** Returns true for `.jsx`/`.tsx` files. */
+export function isJsxFile(filename: string): boolean {
+ return /\.(jsx|tsx)$/.test(filename);
+}
+
+/** `.tsx` files need TypeScript type-stripping before the JSX transform. */
+export function isTsxFile(filename: string): boolean {
+ return filename.endsWith('.tsx');
+}
+
+/**
+ * Decode the handful of HTML entities JSX text commonly contains. Babel/React
+ * decode entities in JSX text and string-literal attribute values.
+ */
+function decodeEntities(text: string): string {
+ return text.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g, (whole, body: string) => {
+ if (body[0] === '#') {
+ const code =
+ body[1] === 'x' || body[1] === 'X'
+ ? parseInt(body.slice(2), 16)
+ : parseInt(body.slice(1), 10);
+ return Number.isNaN(code) ? whole : String.fromCodePoint(code);
+ }
+ const named: Record = {
+ amp: '&',
+ lt: '<',
+ gt: '>',
+ quot: '"',
+ apos: "'",
+ nbsp: '\u00a0',
+ copy: '\u00a9',
+ reg: '\u00ae',
+ };
+ return named[body] ?? whole;
+ });
+}
+
+/**
+ * Collapse JSX text per the JSX whitespace rules (matches Babel):
+ * lines are trimmed at element boundaries and joined with single spaces; lines
+ * that are entirely whitespace are dropped. Returns `null` if nothing remains.
+ */
+function cleanJsxText(raw: string): string | null {
+ const lines = raw.split(/\r\n|\n|\r/);
+ let lastNonEmpty = -1;
+ for (let i = 0; i < lines.length; i++) {
+ if (/[^ \t]/.test(lines[i])) lastNonEmpty = i;
+ }
+
+ let str = '';
+ for (let i = 0; i < lines.length; i++) {
+ const isFirst = i === 0;
+ const isLast = i === lines.length - 1;
+ const isLastNonEmpty = i === lastNonEmpty;
+ let trimmed = lines[i].replace(/\t/g, ' ');
+ if (!isFirst) trimmed = trimmed.replace(/^ +/, '');
+ if (!isLast) trimmed = trimmed.replace(/ +$/, '');
+ if (trimmed) {
+ if (!isLastNonEmpty) trimmed += ' ';
+ str += trimmed;
+ }
+ }
+ return str === '' ? null : decodeEntities(str);
+}
+
+class JsxCodegen {
+ /** Names bound by the injected automatic-runtime import. */
+ needsAutomaticImport = false;
+
+ constructor(
+ private readonly code: string,
+ private readonly cfg: JsxConfig,
+ private readonly allJsx: Node[],
+ ) {}
+
+ /** Transform a source range, replacing the outermost JSX nodes within it. */
+ transformRange(start: number, end: number): string {
+ const inRange = this.allJsx.filter((n) => n.start >= start && n.end <= end);
+ const outer = inRange.filter(
+ (n) => !inRange.some((o) => o !== n && o.start <= n.start && o.end >= n.end),
+ );
+ outer.sort((a, b) => a.start - b.start);
+
+ let out = '';
+ let cursor = start;
+ for (const node of outer) {
+ out += this.code.slice(cursor, node.start);
+ out += this.genElement(node);
+ cursor = node.end;
+ }
+ out += this.code.slice(cursor, end);
+ return out;
+ }
+
+ /** JS expression string for a JSXElement / JSXFragment. */
+ private genElement(node: Node): string {
+ const isFragment = node.type === 'JSXFragment';
+ const tag = isFragment ? null : this.genTag(node.openingElement.name);
+ const attributes: Node[] = isFragment ? [] : node.openingElement.attributes;
+ const children = this.genChildren(node.children);
+
+ if (this.cfg.mode === 'classic') {
+ const tagExpr = isFragment ? this.cfg.fragmentFactory : tag;
+ const props = this.genClassicProps(attributes);
+ const args = [tagExpr, props, ...children];
+ // Drop the trailing `null` props arg entirely when there are no children.
+ if (children.length === 0 && props === 'null') args.length = 1;
+ return `${this.cfg.factory}(${args.join(', ')})`;
+ }
+
+ // Automatic runtime.
+ this.needsAutomaticImport = true;
+ const dev = this.cfg.mode === 'automatic-dev';
+ const { key, props } = this.genAutomaticProps(attributes, children);
+ const tagExpr = isFragment ? '_Fragment' : (tag as string);
+ const isStatic = children.length > 1;
+ const callee = dev ? '_jsxDEV' : isStatic ? '_jsxs' : '_jsx';
+
+ if (dev) {
+ const args = [tagExpr, props, key ?? 'void 0', String(isStatic), 'void 0', 'void 0'];
+ return `${callee}(${args.join(', ')})`;
+ }
+ const args = [tagExpr, props];
+ if (key) args.push(key);
+ return `${callee}(${args.join(', ')})`;
+ }
+
+ /** Element name → tag expression (string literal for intrinsics, else ident). */
+ private genTag(name: Node): string {
+ if (name.type === 'JSXIdentifier') {
+ // Lowercase or hyphenated names are intrinsic (HTML) → string literal.
+ if (/^[a-z]/.test(name.name) || name.name.includes('-')) {
+ return JSON.stringify(name.name);
+ }
+ return name.name;
+ }
+ if (name.type === 'JSXMemberExpression') {
+ return `${this.genTag(name.object)}.${name.property.name}`;
+ }
+ if (name.type === 'JSXNamespacedName') {
+ return JSON.stringify(`${name.namespace.name}:${name.name.name}`);
+ }
+ return 'undefined';
+ }
+
+ private attrName(name: Node): string {
+ const raw =
+ name.type === 'JSXNamespacedName'
+ ? `${name.namespace.name}:${name.name.name}`
+ : name.name;
+ // Quote keys that aren't valid bare identifiers.
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(raw) ? raw : JSON.stringify(raw);
+ }
+
+ private attrValue(value: Node): string {
+ if (value == null) return 'true'; // boolean shorthand:
+ if (value.type === 'Literal') return JSON.stringify(decodeEntities(String(value.value)));
+ if (value.type === 'JSXExpressionContainer') {
+ return this.transformRange(value.expression.start, value.expression.end);
+ }
+ if (value.type === 'JSXElement' || value.type === 'JSXFragment') {
+ return this.genElement(value);
+ }
+ return this.code.slice(value.start, value.end);
+ }
+
+ /** Classic props: object literal or `null`. */
+ private genClassicProps(attributes: Node[]): string {
+ if (attributes.length === 0) return 'null';
+ const parts: string[] = [];
+ for (const attr of attributes) {
+ if (attr.type === 'JSXSpreadAttribute') {
+ parts.push(`...${this.transformRange(attr.argument.start, attr.argument.end)}`);
+ } else {
+ parts.push(`${this.attrName(attr.name)}: ${this.attrValue(attr.value)}`);
+ }
+ }
+ return parts.length === 0 ? '{}' : `{ ${parts.join(', ')} }`;
+ }
+
+ /**
+ * Automatic props: object literal that also carries `children`, with `key`
+ * pulled out into a separate return value (passed as a positional argument).
+ */
+ private genAutomaticProps(
+ attributes: Node[],
+ children: string[],
+ ): { key: string | null; props: string } {
+ const parts: string[] = [];
+ let key: string | null = null;
+ for (const attr of attributes) {
+ if (attr.type === 'JSXSpreadAttribute') {
+ parts.push(`...${this.transformRange(attr.argument.start, attr.argument.end)}`);
+ continue;
+ }
+ const name = this.attrName(attr.name);
+ if (name === 'key') {
+ key = this.attrValue(attr.value);
+ continue;
+ }
+ parts.push(`${name}: ${this.attrValue(attr.value)}`);
+ }
+ if (children.length === 1) {
+ parts.push(`children: ${children[0]}`);
+ } else if (children.length > 1) {
+ parts.push(`children: [${children.join(', ')}]`);
+ }
+ return { key, props: parts.length === 0 ? '{}' : `{ ${parts.join(', ')} }` };
+ }
+
+ /** Generate the list of child expression strings (whitespace-only dropped). */
+ private genChildren(children: Node[]): string[] {
+ const out: string[] = [];
+ for (const child of children) {
+ if (child.type === 'JSXText') {
+ const text = cleanJsxText(child.value);
+ if (text !== null) out.push(JSON.stringify(text));
+ } else if (child.type === 'JSXExpressionContainer') {
+ if (child.expression.type === 'JSXEmptyExpression') continue; // `{/* comment */}`
+ out.push(this.transformRange(child.expression.start, child.expression.end));
+ } else if (child.type === 'JSXSpreadChild') {
+ out.push(`...${this.transformRange(child.expression.start, child.expression.end)}`);
+ } else if (child.type === 'JSXElement' || child.type === 'JSXFragment') {
+ out.push(this.genElement(child));
+ }
+ }
+ return out;
+ }
+}
+
+/** Collect every JSXElement / JSXFragment node in the tree. */
+function collectJsx(ast: Node): Node[] {
+ const found: Node[] = [];
+ const visit = (node: Node): void => {
+ if (!node || typeof node !== 'object') return;
+ if (node.type === 'JSXElement' || node.type === 'JSXFragment') found.push(node);
+ for (const k in node) {
+ if (k === 'loc' || k === 'range' || k === 'start' || k === 'end' || k === 'type') continue;
+ const child = node[k];
+ if (Array.isArray(child)) {
+ for (const c of child) if (c && typeof c === 'object') visit(c);
+ } else if (child && typeof child === 'object') {
+ visit(child);
+ }
+ }
+ };
+ visit(ast);
+ return found;
+}
+
+/**
+ * Transform JSX in `code` to plain JavaScript using the given config. Returns
+ * the input unchanged if there is no JSX (or it fails to parse).
+ */
+export function transformJsx(code: string, cfg: JsxConfig): string {
+ // Fast bail: no `<` at all means no JSX.
+ if (!code.includes('<')) return code;
+
+ let ast: Node;
+ try {
+ ast = JsxParser.parse(code, { ecmaVersion: 'latest', sourceType: 'module', locations: true });
+ } catch {
+ return code;
+ }
+
+ const allJsx = collectJsx(ast);
+ if (allJsx.length === 0) return code;
+
+ const gen = new JsxCodegen(code, cfg, allJsx);
+ let out = gen.transformRange(0, code.length);
+
+ if (gen.needsAutomaticImport) {
+ const dev = cfg.mode === 'automatic-dev';
+ const runtime = `${cfg.importSource}/${dev ? 'jsx-dev-runtime' : 'jsx-runtime'}`;
+ const names = dev
+ ? 'jsxDEV as _jsxDEV, Fragment as _Fragment'
+ : 'jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment';
+ out = `import { ${names} } from ${JSON.stringify(runtime)};\n${out}`;
+ }
+
+ return out;
+}
+/* eslint-enable @typescript-eslint/no-explicit-any */
diff --git a/src/frameworks/strip-types.ts b/src/frameworks/strip-types.ts
new file mode 100644
index 0000000..9a0914d
--- /dev/null
+++ b/src/frameworks/strip-types.ts
@@ -0,0 +1,276 @@
+/**
+ * TypeScript type stripping (pure JS)
+ *
+ * Mirrors Node.js's native `.ts` support (type-stripping mode, stable since
+ * Node 23.6): TypeScript-only syntax is replaced with whitespace, leaving the
+ * runtime JavaScript and all source positions intact.
+ *
+ * Implemented on top of `acorn-typescript` (a pure-JS acorn plugin) — no
+ * `typescript` compiler dependency and no wasm, so it stays small and runs
+ * synchronously, which is required for `require()`.
+ *
+ * Like Node's strip-only mode, constructs that need code generation (enums,
+ * namespaces with runtime members, parameter-property assignments) are not
+ * emitted; we strip the type syntax best-effort.
+ */
+
+import * as acorn from 'acorn';
+import { tsPlugin } from 'acorn-typescript';
+
+// Build the TS-aware parser once.
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const TsParser = (acorn.Parser as any).extend(tsPlugin({ allowSatisfies: true }));
+
+// Node's native type-stripping supports .ts/.mts/.cts only — not .tsx (JSX).
+// (.tsx is still handled separately by the Vite/Next dev servers via esbuild.)
+const TS_EXTENSIONS = ['.ts', '.mts', '.cts'] as const;
+
+/** Module resolution extensions to try for TypeScript files. */
+export const TS_RESOLVE_EXTENSIONS = [...TS_EXTENSIONS];
+
+/** Returns true if the filename is a TypeScript source file. */
+export function isTypeScriptFile(filename: string): boolean {
+ return /\.(ts|mts|cts)$/.test(filename);
+}
+
+/**
+ * `.cts` is CommonJS TypeScript — like `.cjs`, it should not run the
+ * ESM→CJS transform after stripping.
+ */
+export function isCommonJsTypeScriptFile(filename: string): boolean {
+ return filename.endsWith('.cts');
+}
+
+// TS-only modifier keywords that must be removed from class members / params
+// (NOT `static`/`accessor`, which are real JavaScript).
+const TS_MODIFIER_RE = /\b(?:public|private|protected|readonly|abstract|override|declare)\b/g;
+
+/* eslint-disable @typescript-eslint/no-explicit-any */
+type Node = any;
+
+class Blanker {
+ private ranges: Array<[number, number]> = [];
+ constructor(public readonly code: string) {}
+
+ /** Mark a source range to be replaced with whitespace. */
+ blank(start: number, end: number): void {
+ if (end > start) this.ranges.push([start, end]);
+ }
+
+ /** Blank only TS modifier keywords within [start, end), keeping JS keywords. */
+ blankModifiers(start: number, end: number): void {
+ const slice = this.code.slice(start, end);
+ let m: RegExpExecArray | null;
+ TS_MODIFIER_RE.lastIndex = 0;
+ while ((m = TS_MODIFIER_RE.exec(slice)) !== null) {
+ this.blank(start + m.index, start + m.index + m[0].length);
+ }
+ }
+
+ apply(): string {
+ if (this.ranges.length === 0) return this.code;
+ // Replace each range with whitespace, preserving newlines for line parity.
+ const chars = this.code.split('');
+ for (const [start, end] of this.ranges) {
+ for (let i = start; i < end; i++) {
+ if (chars[i] !== '\n' && chars[i] !== '\r') chars[i] = ' ';
+ }
+ }
+ return chars.join('');
+ }
+}
+
+/** Recursively walk the AST, recording TS-only ranges to blank. */
+function visit(node: Node, b: Blanker): void {
+ if (!node || typeof node !== 'object') return;
+ const type: string = node.type;
+
+ switch (type) {
+ // Whole type-only declarations.
+ case 'TSInterfaceDeclaration':
+ case 'TSTypeAliasDeclaration':
+ case 'TSDeclareFunction':
+ b.blank(node.start, node.end);
+ return;
+
+ // `import type ...` / `export type ...` — drop entirely.
+ case 'ImportDeclaration':
+ if (node.importKind === 'type') {
+ b.blank(node.start, node.end);
+ return;
+ }
+ // Drop individual `type` specifiers (e.g. `import { a, type B } from ...`).
+ for (const spec of node.specifiers ?? []) {
+ if (spec.importKind === 'type') blankSpecifierWithComma(node.specifiers, spec, b);
+ }
+ break;
+ case 'ExportNamedDeclaration':
+ case 'ExportAllDeclaration':
+ if (node.exportKind === 'type') {
+ b.blank(node.start, node.end);
+ return;
+ }
+ break;
+
+ // Expression-level type syntax.
+ case 'TSAsExpression':
+ case 'TSSatisfiesExpression':
+ // Keep the expression, drop ` as T` / ` satisfies T`.
+ visit(node.expression, b);
+ b.blank(node.expression.end, node.end);
+ return;
+ case 'TSNonNullExpression':
+ // `expr!` -> `expr`
+ visit(node.expression, b);
+ b.blank(node.expression.end, node.end);
+ return;
+ case 'TSTypeAssertion':
+ // `expr` -> `expr`
+ b.blank(node.start, node.expression.start);
+ visit(node.expression, b);
+ return;
+ case 'TSInstantiationExpression':
+ // `foo` -> `foo`
+ visit(node.expression, b);
+ if (node.typeArguments) b.blank(node.typeArguments.start, node.typeArguments.end);
+ else if (node.typeParameters) b.blank(node.typeParameters.start, node.typeParameters.end);
+ return;
+ }
+
+ // `declare` statements (declare const/function/class/...).
+ if (node.declare) {
+ b.blank(node.start, node.end);
+ return;
+ }
+
+ // Type annotations and signatures attached anywhere (`: T`, `=> : T`).
+ if (node.typeAnnotation && node.typeAnnotation.type === 'TSTypeAnnotation') {
+ // Handle optional `?` / definite `!` that sits just before the colon.
+ blankOptionalOrDefinite(node, node.typeAnnotation.start, b);
+ b.blank(node.typeAnnotation.start, node.typeAnnotation.end);
+ } else if (node.optional || node.definite) {
+ blankOptionalOrDefinite(node, undefined, b);
+ }
+ if (node.returnType && node.returnType.type === 'TSTypeAnnotation') {
+ b.blank(node.returnType.start, node.returnType.end);
+ }
+
+ // Generic type parameters (`` on declarations) and type arguments
+ // (`foo()`, `new C()`) — both live under various keys.
+ for (const key of ['typeParameters', 'typeArguments', 'superTypeArguments']) {
+ const ta = node[key];
+ if (ta && (ta.type === 'TSTypeParameterInstantiation' || ta.type === 'TSTypeParameterDeclaration')) {
+ b.blank(ta.start, ta.end);
+ }
+ }
+
+ // Class heritage: `implements X, Y`.
+ if (Array.isArray(node.implements) && node.implements.length > 0) {
+ const first = node.implements[0];
+ const last = node.implements[node.implements.length - 1];
+ // Blank back to the `implements` keyword.
+ const kw = b.code.lastIndexOf('implements', first.start);
+ if (kw !== -1) b.blank(kw, last.end);
+ }
+
+ // Class member / parameter modifiers (public/private/readonly/abstract/...).
+ if ((type === 'PropertyDefinition' || type === 'MethodDefinition') && node.key) {
+ if (node.accessibility || node.readonly || node.abstract || node.override) {
+ b.blankModifiers(node.start, node.key.start);
+ }
+ }
+ if (type === 'TSParameterProperty') {
+ b.blankModifiers(node.start, node.parameter ? node.parameter.start : node.end);
+ visit(node.parameter, b);
+ return;
+ }
+ // `abstract class` keyword.
+ if (type === 'ClassDeclaration' && node.abstract && typeof node.start === 'number') {
+ const kw = b.code.indexOf('abstract', node.start);
+ if (kw !== -1 && kw < (node.id ? node.id.start : node.end)) b.blank(kw, kw + 'abstract'.length);
+ }
+
+ // Recurse into children (skip the typeAnnotation/returnType we already blanked,
+ // and skip pure-type subtrees which we don't need to descend).
+ for (const k in node) {
+ if (k === 'typeAnnotation' || k === 'returnType' || k === 'typeParameters' ||
+ k === 'typeArguments' || k === 'superTypeArguments' || k === 'implements' ||
+ k === 'loc' || k === 'range' || k === 'start' || k === 'end' || k === 'type') {
+ continue;
+ }
+ const child = node[k];
+ if (Array.isArray(child)) {
+ for (const c of child) if (c && typeof c === 'object' && c.type) visit(c, b);
+ } else if (child && typeof child === 'object' && child.type) {
+ visit(child, b);
+ }
+ }
+}
+
+/**
+ * Blank an optional `?` or definite-assignment `!` marker. acorn-typescript
+ * gives unreliable end positions for some optional nodes, so we locate the
+ * marker by scanning the source just before the type colon (or, when there is
+ * no annotation, after the node's key).
+ */
+function blankOptionalOrDefinite(node: Node, annotationStart: number | undefined, b: Blanker): void {
+ if (!node.optional && !node.definite) return;
+ const code: string = b.code;
+
+ // When there's a type annotation, the `?`/`!` is the last non-space char
+ // before the colon. (acorn-typescript's optional-node end offsets are
+ // unreliable, so scan from the colon backward instead.)
+ if (annotationStart !== undefined) {
+ let i = annotationStart - 1;
+ while (i >= 0 && (code[i] === ' ' || code[i] === '\t')) i--;
+ if (i >= 0 && (code[i] === '?' || code[i] === '!')) b.blank(i, i + 1);
+ return;
+ }
+
+ // No annotation (e.g. `function f(a?) {}`): the marker follows the name.
+ if (typeof node.start !== 'number') return;
+ let i = node.start;
+ while (i < code.length && /[A-Za-z0-9_$]/.test(code[i])) i++;
+ while (i < code.length && (code[i] === ' ' || code[i] === '\t')) i++;
+ if (code[i] === '?' || code[i] === '!') b.blank(i, i + 1);
+}
+
+/** Blank a type-only import/export specifier together with a separating comma. */
+function blankSpecifierWithComma(specifiers: Node[], spec: Node, b: Blanker): void {
+ const code: string = b.code;
+ const idx = specifiers.indexOf(spec);
+ // Prefer consuming a following comma; otherwise a preceding one.
+ let end = spec.end;
+ let start = spec.start;
+ const after = code.slice(spec.end);
+ const commaAfter = after.match(/^\s*,/);
+ if (idx < specifiers.length - 1 && commaAfter) {
+ end = spec.end + commaAfter[0].length;
+ } else {
+ // Last specifier: pull in the preceding comma.
+ const before = code.slice(0, spec.start);
+ const commaBefore = before.match(/,\s*$/);
+ if (commaBefore) start = spec.start - commaBefore[0].length;
+ }
+ b.blank(start, end);
+}
+/* eslint-enable @typescript-eslint/no-explicit-any */
+
+/**
+ * Strip TypeScript types from source code, returning plain JavaScript.
+ * Whitespace-preserving, so line/column positions are unchanged.
+ */
+export function stripTypeScriptTypes(code: string, _filename?: string): string {
+ let ast: Node;
+ try {
+ // acorn-typescript requires `locations`. We still use character offsets
+ // (.start/.end) for blanking.
+ ast = TsParser.parse(code, { ecmaVersion: 'latest', sourceType: 'module', locations: true });
+ } catch {
+ // If it doesn't parse as TS, return as-is and let the JS path report errors.
+ return code;
+ }
+ const b = new Blanker(code);
+ for (const stmt of ast.body) visit(stmt, b);
+ return b.apply();
+}
diff --git a/src/runtime.ts b/src/runtime.ts
index e34703e..30c6419 100644
--- a/src/runtime.ts
+++ b/src/runtime.ts
@@ -54,6 +54,14 @@ import * as diagnosticsChannelShim from './shims/diagnostics_channel';
import assertShim from './shims/assert';
import { resolve as resolveExports, imports as resolveImports } from 'resolve.exports';
import { transformEsmToCjsSimple } from './frameworks/code-transforms';
+import {
+ isTypeScriptFile,
+ isCommonJsTypeScriptFile,
+ stripTypeScriptTypes,
+ TS_RESOLVE_EXTENSIONS,
+} from './frameworks/strip-types';
+import { isJsxFile, transformJsx, JSX_RESOLVE_EXTENSIONS } from './frameworks/jsx-transform';
+import { resolveJsxConfig } from './frameworks/jsx-config';
import * as acorn from 'acorn';
/**
@@ -416,6 +424,28 @@ const builtinModules: Record = {
},
};
+/**
+ * TypeScript-style import extension rewriting.
+ *
+ * TS/ESM code commonly imports a sibling module by its *output* extension
+ * (`import './foo.js'`) even though the source on disk is `./foo.ts` / `.tsx`.
+ * When the literal extension doesn't exist, map it to the TypeScript source
+ * extensions and try those, matching `tsc`/`tsx`/Node's `--experimental` rules.
+ */
+const TS_EXT_REWRITES: Record = {
+ '.js': ['.ts', '.tsx'],
+ '.jsx': ['.tsx'],
+ '.mjs': ['.mts'],
+ '.cjs': ['.cts'],
+};
+
+function tsExtensionCandidates(p: string): string[] {
+ const m = p.match(/(\.(?:js|jsx|mjs|cjs))$/);
+ if (!m) return [];
+ const base = p.slice(0, -m[1].length);
+ return (TS_EXT_REWRITES[m[1]] ?? []).map(ext => base + ext);
+}
+
/**
* Create a require function for a specific module context
*/
@@ -505,16 +535,27 @@ function createRequire(
resolutionCache.set(cacheKey, resolved);
return resolved;
}
- // Directory - look for index.js
- const indexPath = pathShim.join(resolved, 'index.js');
- if (vfs.existsSync(indexPath)) {
- resolutionCache.set(cacheKey, indexPath);
- return indexPath;
+ // Directory - look for index.{js,json,ts,tsx,...}
+ for (const indexExt of ['.js', '.json', ...TS_RESOLVE_EXTENSIONS, ...JSX_RESOLVE_EXTENSIONS]) {
+ const indexPath = pathShim.join(resolved, 'index' + indexExt);
+ if (vfs.existsSync(indexPath)) {
+ resolutionCache.set(cacheKey, indexPath);
+ return indexPath;
+ }
+ }
+ }
+
+ // Rewrite a JS-style extension to its TypeScript source (e.g.
+ // `./foo.js` -> `./foo.ts`/`.tsx`) when the literal file is absent.
+ for (const candidate of tsExtensionCandidates(resolved)) {
+ if (vfs.existsSync(candidate)) {
+ resolutionCache.set(cacheKey, candidate);
+ return candidate;
}
}
// Try with extensions
- const extensions = ['.js', '.json'];
+ const extensions = ['.js', '.json', ...TS_RESOLVE_EXTENSIONS, ...JSX_RESOLVE_EXTENSIONS];
for (const ext of extensions) {
const withExt = resolved + ext;
if (vfs.existsSync(withExt)) {
@@ -535,15 +576,25 @@ function createRequire(
if (stats.isFile()) {
return basePath;
}
- // Directory - look for index.js
- const indexPath = pathShim.join(basePath, 'index.js');
- if (vfs.existsSync(indexPath)) {
- return indexPath;
+ // Directory - look for index.{js,json,ts,tsx,...}
+ for (const indexExt of ['.js', '.json', ...TS_RESOLVE_EXTENSIONS, ...JSX_RESOLVE_EXTENSIONS]) {
+ const indexPath = pathShim.join(basePath, 'index' + indexExt);
+ if (vfs.existsSync(indexPath)) {
+ return indexPath;
+ }
+ }
+ }
+
+ // Rewrite a JS-style extension to its TypeScript source (e.g.
+ // `./foo.js` -> `./foo.ts`/`.tsx`) when the literal file is absent.
+ for (const candidate of tsExtensionCandidates(basePath)) {
+ if (vfs.existsSync(candidate)) {
+ return candidate;
}
}
// Try with extensions
- const extensions = ['.js', '.json', '.node'];
+ const extensions = ['.js', '.json', '.node', ...TS_RESOLVE_EXTENSIONS, ...JSX_RESOLVE_EXTENSIONS];
for (const ext of extensions) {
const withExt = basePath + ext;
if (vfs.existsSync(withExt)) {
@@ -719,10 +770,23 @@ function createRequire(
code = code.slice(code.indexOf('\n') + 1);
}
+ // Strip TypeScript types first (mirrors Node's native .ts support).
+ // Whitespace-preserving, so the result still parses as plain JS. .tsx
+ // also goes through here so its TS syntax is removed before the JSX pass.
+ if (isTypeScriptFile(resolvedPath) || resolvedPath.endsWith('.tsx')) {
+ code = stripTypeScriptTypes(code, resolvedPath);
+ }
+
+ // Transform JSX (.jsx/.tsx) using the nearest tsconfig/jsconfig config.
+ if (isJsxFile(resolvedPath)) {
+ code = transformJsx(code, resolveJsxConfig(vfs, resolvedPath));
+ }
+
// Transform ESM to CJS if needed (for .mjs files or ESM that wasn't pre-transformed)
// transformEsmToCjs uses AST to handle import/export, import.meta, and dynamic imports
// It also handles already-CJS files safely (AST finds no ESM nodes → no-op)
- if (!resolvedPath.endsWith('.cjs')) {
+ // .cjs and .cts are CommonJS — skip the ESM transform.
+ if (!resolvedPath.endsWith('.cjs') && !isCommonJsTypeScriptFile(resolvedPath)) {
code = transformEsmToCjs(code, resolvedPath);
}
@@ -762,6 +826,9 @@ var process = $process;
var console = $console;
var import_meta = $importMeta;
var __dynamicImport = $dynamicImport;
+// Node.js code runs without a DOM — shadow browser globals as undefined
+var document = undefined;
+var window = undefined;
// Set up global.process and globalThis.process for code that accesses them directly
var global = globalThis;
globalThis.process = $process;
@@ -1282,8 +1349,20 @@ export class Runtime {
code = code.slice(code.indexOf('\n') + 1);
}
+ // Strip TypeScript types first (mirrors Node's native .ts support).
+ // .tsx also passes through here to remove TS syntax before the JSX pass.
+ if (isTypeScriptFile(filename) || filename.endsWith('.tsx')) {
+ code = stripTypeScriptTypes(code, filename);
+ }
+
+ // Transform JSX (.jsx/.tsx) using the nearest tsconfig/jsconfig config.
+ if (isJsxFile(filename)) {
+ code = transformJsx(code, resolveJsxConfig(this.vfs, filename));
+ }
+
// Transform ESM to CJS if needed (AST-based, handles import.meta and dynamic imports too)
- if (!filename.endsWith('.cjs')) {
+ // .cjs and .cts are CommonJS — skip the ESM transform.
+ if (!filename.endsWith('.cjs') && !isCommonJsTypeScriptFile(filename)) {
code = transformEsmToCjs(code, filename);
}
@@ -1301,6 +1380,9 @@ var process = $process;
var console = $console;
var import_meta = $importMeta;
var __dynamicImport = $dynamicImport;
+// Node.js code runs without a DOM — shadow browser globals as undefined
+var document = undefined;
+var window = undefined;
// Set up global.process and globalThis.process for code that accesses them directly
var global = globalThis;
globalThis.process = $process;
diff --git a/src/shims/child_process.ts b/src/shims/child_process.ts
index 1cd0263..0d1b077 100644
--- a/src/shims/child_process.ts
+++ b/src/shims/child_process.ts
@@ -124,12 +124,24 @@ export function initChildProcess(vfs: VirtualFS): void {
}
// Resolve the script path
- const resolvedPath = scriptPath.startsWith('/')
+ const basePath = scriptPath.startsWith('/')
? scriptPath
: `${ctx.cwd}/${scriptPath}`.replace(/\/+/g, '/');
+ // Try the exact path, then common extensions (mirrors `node app` resolving app.ts)
+ let resolvedPath = basePath;
+ if (!currentVfs.existsSync(resolvedPath) || currentVfs.statSync(resolvedPath).isDirectory()) {
+ const candidates = [
+ ...['.js', '.cjs', '.mjs', '.ts', '.mts', '.cts', '.tsx', '.jsx'].map(ext => basePath + ext),
+ ...['index.js', 'index.ts', 'index.mts', 'index.cts', 'index.tsx', 'index.jsx'].map(
+ name => `${basePath}/${name}`.replace(/\/+/g, '/')
+ ),
+ ];
+ resolvedPath = candidates.find(p => currentVfs!.existsSync(p)) ?? basePath;
+ }
+
if (!currentVfs.existsSync(resolvedPath)) {
- return { stdout: '', stderr: `Error: Cannot find module '${resolvedPath}'\n`, exitCode: 1 };
+ return { stdout: '', stderr: `Error: Cannot find module '${basePath}'\n`, exitCode: 1 };
}
let stdout = '';
diff --git a/tests/child_process.test.ts b/tests/child_process.test.ts
index 6e809cf..405b56a 100644
--- a/tests/child_process.test.ts
+++ b/tests/child_process.test.ts
@@ -135,6 +135,68 @@ child.on('exit', (code) => {
});
});
+ describe('node command with TypeScript', () => {
+ it('should run a .ts file via `node`', async () => {
+ vfs.writeFileSync(
+ '/script.ts',
+ `const msg: string = 'typed hello';
+ function shout(s: string): string { return s.toUpperCase(); }
+ console.log(shout(msg));`
+ );
+
+ const code = `
+const { exec } = require('child_process');
+exec('node /script.ts', (error, stdout) => {
+ console.log('out:', stdout.trim());
+});
+ `;
+ runtime.execute(code, '/test.js');
+ await new Promise(resolve => setTimeout(resolve, 200));
+
+ expect(consoleOutput.some(o => o.includes('TYPED HELLO'))).toBe(true);
+ });
+
+ it('should resolve `node script` to script.ts', async () => {
+ vfs.writeFileSync('/main.ts', `console.log('answer:' + (42 as number));`);
+
+ const code = `
+const { exec } = require('child_process');
+exec('node /main', (error, stdout) => {
+ console.log('out:', stdout.trim());
+});
+ `;
+ runtime.execute(code, '/test.js');
+ await new Promise(resolve => setTimeout(resolve, 200));
+
+ expect(consoleOutput.some(o => o.includes('answer:42'))).toBe(true);
+ });
+
+ it('should run a .tsx file via `node`', async () => {
+ vfs.writeFileSync(
+ '/node_modules/react/jsx-runtime.js',
+ `exports.Fragment = 'Fragment';
+ exports.jsx = exports.jsxs = (type, props) => ({ type, text: props.children });`
+ );
+ vfs.writeFileSync(
+ '/view.tsx',
+ `const label: string = 'rendered';
+ const el = {label}
;
+ console.log('tag:' + el.type + ' text:' + el.text);`
+ );
+
+ const code = `
+const { exec } = require('child_process');
+exec('node /view.tsx', (error, stdout) => {
+ console.log('out:', stdout.trim());
+});
+ `;
+ runtime.execute(code, '/test.js');
+ await new Promise(resolve => setTimeout(resolve, 200));
+
+ expect(consoleOutput.some(o => o.includes('tag:h1 text:rendered'))).toBe(true);
+ });
+ });
+
describe('shell features', () => {
it('should support pipes', async () => {
const code = `
diff --git a/tests/export-binding.test.ts b/tests/export-binding.test.ts
new file mode 100644
index 0000000..20b1f55
--- /dev/null
+++ b/tests/export-binding.test.ts
@@ -0,0 +1,77 @@
+import { describe, it, expect, beforeEach } from 'vitest';
+import { VirtualFS } from '../src/virtual-fs';
+import { Runtime } from '../src/runtime';
+
+describe('named exports preserve their local binding', () => {
+ let vfs: VirtualFS;
+ let runtime: Runtime;
+
+ beforeEach(() => {
+ vfs = new VirtualFS();
+ runtime = new Runtime(vfs);
+ });
+
+ it('lets later code mutate an exported const object', () => {
+ vfs.writeFileSync(
+ '/mod.js',
+ `export const something = {};
+ something.key = "value";`
+ );
+ vfs.writeFileSync(
+ '/main.js',
+ `const m = require('./mod');
+ module.exports = m.something;`
+ );
+ const { exports } = runtime.runFile('/main.js') as { exports: any };
+ expect(exports).toEqual({ key: 'value' });
+ });
+
+ it('lets module-scope code call an exported function', () => {
+ vfs.writeFileSync(
+ '/mod.js',
+ `export function add(a, b) { return a + b; }
+ export const sum = add(2, 3);`
+ );
+ vfs.writeFileSync('/main.js', `module.exports = require('./mod').sum;`);
+ const { exports } = runtime.runFile('/main.js') as { exports: any };
+ expect(exports).toBe(5);
+ });
+
+ it('lets module-scope code reference an exported class', () => {
+ vfs.writeFileSync(
+ '/mod.js',
+ `export class Box { constructor(v) { this.v = v; } }
+ export const boxed = new Box(7);`
+ );
+ vfs.writeFileSync('/main.js', `module.exports = require('./mod').boxed.v;`);
+ const { exports } = runtime.runFile('/main.js') as { exports: any };
+ expect(exports).toBe(7);
+ });
+
+ it('handles destructuring named exports', () => {
+ vfs.writeFileSync(
+ '/mod.js',
+ `const src = { a: 1, b: 2, rest1: 3, rest2: 4 };
+ export const { a, b, ...others } = src;
+ export const [first] = [10, 20];`
+ );
+ vfs.writeFileSync(
+ '/main.js',
+ `const m = require('./mod');
+ module.exports = { a: m.a, b: m.b, others: m.others, first: m.first };`
+ );
+ const { exports } = runtime.runFile('/main.js') as { exports: any };
+ expect(exports).toEqual({ a: 1, b: 2, others: { rest1: 3, rest2: 4 }, first: 10 });
+ });
+
+ it('works for .ts files too', () => {
+ vfs.writeFileSync(
+ '/mod.ts',
+ `export const config: Record = {};
+ config.ready = true;`
+ );
+ vfs.writeFileSync('/main.ts', `module.exports = require('./mod').config;`);
+ const { exports } = runtime.runFile('/main.ts') as { exports: any };
+ expect(exports).toEqual({ ready: true });
+ });
+});
diff --git a/tests/jsx-files.test.ts b/tests/jsx-files.test.ts
new file mode 100644
index 0000000..4f0d823
--- /dev/null
+++ b/tests/jsx-files.test.ts
@@ -0,0 +1,218 @@
+import { describe, it, expect, beforeEach } from 'vitest';
+import { VirtualFS } from '../src/virtual-fs';
+import { Runtime } from '../src/runtime';
+import { transformJsx } from '../src/frameworks/jsx-transform';
+import { resolveJsxConfig, DEFAULT_JSX_CONFIG } from '../src/frameworks/jsx-config';
+
+const classic = {
+ mode: 'classic' as const,
+ importSource: 'react',
+ factory: 'React.createElement',
+ fragmentFactory: 'React.Fragment',
+};
+
+describe('JSX transform (codegen)', () => {
+ it('automatic runtime: element with props, key, spread and children', () => {
+ const out = transformJsx(
+ 'const el = hi {name}
;',
+ DEFAULT_JSX_CONFIG
+ );
+ expect(out).toContain('import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"');
+ // multiple children -> _jsxs, key pulled out to 3rd arg
+ expect(out).toContain('_jsxs("div", {');
+ expect(out).toContain('className: "a"');
+ expect(out).toContain('...rest');
+ expect(out).toContain('children: ["hi ", name, _jsx("span", {})]');
+ expect(out).toMatch(/}, id\)/);
+ });
+
+ it('classic runtime: React.createElement with null props', () => {
+ const out = transformJsx('const el = ;', classic);
+ expect(out).toContain('React.createElement("span")');
+ expect(out).not.toContain('createElement("span", null)');
+ expect(out).not.toContain('jsx-runtime');
+ });
+
+ it('classic runtime: fragment + member-expression component', () => {
+ const out = transformJsx('const el = <>{k}>;', classic);
+ expect(out).toContain('React.createElement(React.Fragment, null,');
+ expect(out).toContain('React.createElement(Foo.Bar, { a: 1 }, k)');
+ });
+
+ it('handles nested JSX inside expressions', () => {
+ const out = transformJsx('const el = ;', DEFAULT_JSX_CONFIG);
+ expect(out).toContain('_jsx("ul", {');
+ expect(out).toContain('items.map(i => _jsx("li", {');
+ });
+
+ it('dev runtime emits _jsxDEV', () => {
+ const out = transformJsx('const el = ;', {
+ ...DEFAULT_JSX_CONFIG,
+ mode: 'automatic-dev',
+ });
+ expect(out).toContain('jsx-dev-runtime');
+ expect(out).toContain('_jsxDEV("div", {}, void 0, false');
+ });
+
+ it('respects custom jsxImportSource', () => {
+ const out = transformJsx('const el = ;', {
+ ...DEFAULT_JSX_CONFIG,
+ importSource: 'preact',
+ });
+ expect(out).toContain('from "preact/jsx-runtime"');
+ });
+});
+
+describe('JSX config resolution (tsconfig/jsconfig walk)', () => {
+ let vfs: VirtualFS;
+ beforeEach(() => {
+ vfs = new VirtualFS();
+ });
+
+ it('returns defaults when no config exists', () => {
+ expect(resolveJsxConfig(vfs, '/src/app.tsx')).toEqual(DEFAULT_JSX_CONFIG);
+ });
+
+ it('reads jsx mode from nearest tsconfig.json (walking up)', () => {
+ vfs.writeFileSync('/tsconfig.json', '{ "compilerOptions": { "jsx": "react" } }');
+ const cfg = resolveJsxConfig(vfs, '/src/components/app.tsx');
+ expect(cfg.mode).toBe('classic');
+ });
+
+ it('prefers a deeper config over a shallower one', () => {
+ vfs.writeFileSync('/tsconfig.json', '{ "compilerOptions": { "jsx": "react" } }');
+ vfs.writeFileSync('/src/tsconfig.json', '{ "compilerOptions": { "jsx": "react-jsx" } }');
+ expect(resolveJsxConfig(vfs, '/src/app.tsx').mode).toBe('automatic');
+ });
+
+ it('falls back to jsconfig.json and tolerates comments/trailing commas', () => {
+ vfs.writeFileSync(
+ '/jsconfig.json',
+ `{
+ // jsx settings
+ "compilerOptions": {
+ "jsx": "react",
+ "jsxFactory": "h",
+ "jsxFragmentFactory": "Fragment", /* preact classic */
+ },
+ }`
+ );
+ const cfg = resolveJsxConfig(vfs, '/app.jsx');
+ expect(cfg).toMatchObject({ mode: 'classic', factory: 'h', fragmentFactory: 'Fragment' });
+ });
+
+ it('reads jsxImportSource', () => {
+ vfs.writeFileSync(
+ '/tsconfig.json',
+ '{ "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "preact" } }'
+ );
+ expect(resolveJsxConfig(vfs, '/app.tsx').importSource).toBe('preact');
+ });
+});
+
+describe('Running .jsx/.tsx files through the runtime', () => {
+ let vfs: VirtualFS;
+ let runtime: Runtime;
+
+ beforeEach(() => {
+ vfs = new VirtualFS();
+ runtime = new Runtime(vfs);
+ // Minimal automatic + classic React runtimes that just record the calls.
+ vfs.writeFileSync('/node_modules/react/package.json', JSON.stringify({ name: 'react', version: '0.0.0' }));
+ vfs.writeFileSync(
+ '/node_modules/react/jsx-runtime.js',
+ `const h = (type, props) => ({ type, props });
+ exports.Fragment = 'Fragment';
+ exports.jsx = h;
+ exports.jsxs = h;`
+ );
+ vfs.writeFileSync(
+ '/node_modules/react/index.js',
+ `exports.Fragment = 'Fragment';
+ exports.createElement = (type, props, ...children) => ({ type, props, children });`
+ );
+ });
+
+ it('runs a .tsx file with the automatic runtime (default)', () => {
+ vfs.writeFileSync(
+ '/app.tsx',
+ `const name: string = 'world';
+ const el = hello {name}
;
+ module.exports = el;`
+ );
+ const { exports } = runtime.runFile('/app.tsx') as { exports: any };
+ expect(exports.type).toBe('div');
+ expect(exports.props.className).toBe('greeting');
+ expect(exports.props.children).toEqual(['hello ', 'world']);
+ });
+
+ it('runs a .jsx file with the classic runtime when configured', () => {
+ vfs.writeFileSync('/tsconfig.json', '{ "compilerOptions": { "jsx": "react" } }');
+ vfs.writeFileSync(
+ '/app.jsx',
+ `const React = require('react');
+ const el = ;
+ module.exports = el;`
+ );
+ const { exports } = runtime.runFile('/app.jsx') as { exports: any };
+ expect(exports.type).toBe('ul');
+ expect(exports.children).toHaveLength(2);
+ expect(exports.children[0].type).toBe('li');
+ });
+
+ it('lets a .ts file import a .ts module by its .js extension', () => {
+ vfs.writeFileSync('/util.ts', `export const n: number = 41;`);
+ vfs.writeFileSync(
+ '/main.ts',
+ `import { n } from './util.js';
+ module.exports = n + 1;`
+ );
+ const { exports } = runtime.runFile('/main.ts') as { exports: any };
+ expect(exports).toBe(42);
+ });
+
+ it('lets a .ts file import a .tsx module by its .jsx extension', () => {
+ vfs.writeFileSync('/Card.tsx', `export const Card = () => hi
;`);
+ vfs.writeFileSync(
+ '/main.ts',
+ `import { Card } from './Card.jsx';
+ module.exports = Card();`
+ );
+ const { exports } = runtime.runFile('/main.ts') as { exports: any };
+ expect(exports.type).toBe('div');
+ });
+
+ it('lets a plain .js file require a .ts module via its .js extension', () => {
+ vfs.writeFileSync('/lib.ts', `export const greet = (s: string): string => 'hi ' + s;`);
+ vfs.writeFileSync(
+ '/main.js',
+ `const { greet } = require('./lib.js');
+ module.exports = greet('ts');`
+ );
+ const { exports } = runtime.runFile('/main.js') as { exports: any };
+ expect(exports).toBe('hi ts');
+ });
+
+ it('prefers a real .js sibling over the rewritten .ts', () => {
+ vfs.writeFileSync('/dep.js', `module.exports = 'from-js';`);
+ vfs.writeFileSync('/dep.ts', `module.exports = 'from-ts';`);
+ vfs.writeFileSync('/main.ts', `module.exports = require('./dep.js');`);
+ const { exports } = runtime.runFile('/main.ts') as { exports: any };
+ expect(exports).toBe('from-js');
+ });
+
+ it('resolves require() of a .tsx file without extension', () => {
+ vfs.writeFileSync(
+ '/components/Card.tsx',
+ `export const Card = ({ title }: { title: string }) => {title}
;`
+ );
+ vfs.writeFileSync(
+ '/main.tsx',
+ `const { Card } = require('./components/Card');
+ module.exports = Card({ title: 'hi' });`
+ );
+ const { exports } = runtime.runFile('/main.tsx') as { exports: any };
+ expect(exports.type).toBe('div');
+ expect(exports.props.children).toBe('hi');
+ });
+});
diff --git a/tests/typescript-files.test.ts b/tests/typescript-files.test.ts
new file mode 100644
index 0000000..3c73c65
--- /dev/null
+++ b/tests/typescript-files.test.ts
@@ -0,0 +1,123 @@
+import { describe, it, expect, beforeEach } from 'vitest';
+import { VirtualFS } from '../src/virtual-fs';
+import { Runtime } from '../src/runtime';
+
+describe('TypeScript (.ts/.mts/.cts) support', () => {
+ let vfs: VirtualFS;
+ let runtime: Runtime;
+
+ beforeEach(() => {
+ vfs = new VirtualFS();
+ runtime = new Runtime(vfs);
+ });
+
+ it('strips type annotations and runs a .ts file', () => {
+ vfs.writeFileSync(
+ '/app.ts',
+ `const x: number = 21;
+ function double(n: number): number { return n * 2; }
+ module.exports = double(x);`
+ );
+ const { exports } = runtime.runFile('/app.ts');
+ expect(exports).toBe(42);
+ });
+
+ it('strips interfaces, type aliases and generics', () => {
+ vfs.writeFileSync(
+ '/g.ts',
+ `interface Box { value: T }
+ type ID = string | number;
+ function unwrap(b: Box): T { return b.value; }
+ const id: ID = 7;
+ module.exports = unwrap({ value: id });`
+ );
+ const { exports } = runtime.runFile('/g.ts');
+ expect(exports).toBe(7);
+ });
+
+ it('handles ESM import/export in .ts files', () => {
+ vfs.writeFileSync(
+ '/math.ts',
+ `export const add = (a: number, b: number): number => a + b;
+ export type Pair = [number, number];`
+ );
+ vfs.writeFileSync(
+ '/main.ts',
+ `import { add } from './math';
+ import type { Pair } from './math';
+ const p = [2, 3] as Pair;
+ module.exports = add(p[0], p[1]);`
+ );
+ const { exports } = runtime.runFile('/main.ts');
+ expect(exports).toBe(5);
+ });
+
+ it('resolves require() of a .ts file without extension', () => {
+ vfs.writeFileSync('/lib/util.ts', `export const greet = (n: string): string => 'hi ' + n;`);
+ vfs.writeFileSync(
+ '/index.ts',
+ `const { greet } = require('./lib/util');
+ module.exports = greet('node');`
+ );
+ const { exports } = runtime.runFile('/index.ts');
+ expect(exports).toBe('hi node');
+ });
+
+ it('resolves a directory index.ts', () => {
+ vfs.writeFileSync('/pkg/index.ts', `exports.ok = true as boolean;`);
+ vfs.writeFileSync('/main.ts', `module.exports = require('./pkg').ok;`);
+ const { exports } = runtime.runFile('/main.ts');
+ expect(exports).toBe(true);
+ });
+
+ it('treats .cts as CommonJS', () => {
+ vfs.writeFileSync(
+ '/cjs.cts',
+ `const value: number = 99;
+ module.exports = { value };`
+ );
+ const { exports } = runtime.runFile('/cjs.cts');
+ expect(exports).toEqual({ value: 99 });
+ });
+
+ it('strips as/satisfies, non-null and definite assignment', () => {
+ vfs.writeFileSync(
+ '/assert.ts',
+ `const raw: unknown = { n: 41 };
+ const obj = raw as { n: number };
+ const cfg = { x: 1 } satisfies Record;
+ let later!: number;
+ later = obj.n + cfg.x;
+ module.exports = later;`
+ );
+ const { exports } = runtime.runFile('/assert.ts');
+ expect(exports).toBe(42);
+ });
+
+ it('strips class member modifiers and generics', () => {
+ vfs.writeFileSync(
+ '/cls.ts',
+ `class Counter {
+ private readonly base: T;
+ count!: number;
+ constructor(base: T) { this.base = base; this.count = base; }
+ inc(by: number): number { this.count += by; return this.count; }
+ }
+ const c = new Counter(40);
+ module.exports = c.inc(2);`
+ );
+ const { exports } = runtime.runFile('/cls.ts');
+ expect(exports).toBe(42);
+ });
+
+ it('treats .mts as ESM', () => {
+ vfs.writeFileSync('/dep.mts', `export const n: number = 3;`);
+ vfs.writeFileSync(
+ '/m.mts',
+ `import { n } from './dep';
+ export const total: number = n + 1;`
+ );
+ const { exports } = runtime.runFile('/m.mts');
+ expect((exports as { total: number }).total).toBe(4);
+ });
+});