From cae98b589f986f589fd09da1a7ecd468aa7c67a5 Mon Sep 17 00:00:00 2001 From: Jacob Ebey Date: Mon, 1 Jun 2026 18:28:02 -0700 Subject: [PATCH 1/5] feat: native TypeScript (.ts/.mts/.cts) support in the runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Run TypeScript files 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 now work out of the box. Implemented with `acorn-typescript`, a pure-JS acorn plugin — no `typescript` compiler dependency and no wasm. This keeps stripping synchronous (required for `require()`) and adds minimal bundle weight (index.mjs stays ~1.2MB rather than ballooning to ~10MB as it would by pulling in the full typescript package). Type-only syntax is replaced with whitespace, preserving source positions: annotations, interfaces, type aliases, generics, `import type`/`export type`, `as`/`satisfies`, non-null `!`, definite assignment, `declare`, class member modifiers, and `implements`. Details: - New `src/frameworks/strip-types.ts`: parses with acorn-typescript and blanks TS-only AST node ranges, then the existing ESM→CJS pipeline runs. - runtime.ts: strip types before the ESM→CJS transform in loadModule and execute; .cts treated as CommonJS, .mts/.ts as ESM-capable; added .ts/.mts/.cts (and index.* variants) to module resolution. - child_process.ts: `node` command resolves .ts files, including the extensionless `node app` -> app.ts case. Matches Node's strip-only mode: constructs needing code generation (enums, namespaces, parameter-property assignment) are not emitted. Only .ts/.mts/.cts are handled; .tsx/JSX continues to go through the Vite/Next dev servers via esbuild. Adds tests/typescript-files.test.ts and `node app.ts` coverage in tests/child_process.test.ts. Full suite (2261) green, tsc clean. --- CHANGELOG.md | 9 ++ README.md | 2 +- package-lock.json | 14 +- package.json | 3 +- src/frameworks/strip-types.ts | 276 +++++++++++++++++++++++++++++++++ src/runtime.ts | 49 ++++-- src/shims/child_process.ts | 16 +- tests/child_process.test.ts | 37 +++++ tests/typescript-files.test.ts | 123 +++++++++++++++ 9 files changed, 510 insertions(+), 19 deletions(-) create mode 100644 src/frameworks/strip-types.ts create mode 100644 tests/typescript-files.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 3333b41..b183a1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ 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.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..40a5434 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 Support** - Run `.ts`/`.mts`/`.cts` files directly via Node-style type-stripping (synchronous, pure-JS); 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..d98fa8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "almostnode", - "version": "0.2.13", + "version": "0.2.14", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "almostnode", - "version": "0.2.13", + "version": "0.2.14", "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", @@ -1608,6 +1609,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", diff --git a/package.json b/package.json index 8fbd9d5..1a60e91 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "almostnode", - "version": "0.2.14", + "version": "0.3.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", 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..955500d 100644 --- a/src/runtime.ts +++ b/src/runtime.ts @@ -54,6 +54,12 @@ 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 * as acorn from 'acorn'; /** @@ -505,16 +511,18 @@ 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,...} + for (const indexExt of ['.js', '.json', ...TS_RESOLVE_EXTENSIONS]) { + const indexPath = pathShim.join(resolved, 'index' + indexExt); + if (vfs.existsSync(indexPath)) { + resolutionCache.set(cacheKey, indexPath); + return indexPath; + } } } // Try with extensions - const extensions = ['.js', '.json']; + const extensions = ['.js', '.json', ...TS_RESOLVE_EXTENSIONS]; for (const ext of extensions) { const withExt = resolved + ext; if (vfs.existsSync(withExt)) { @@ -535,15 +543,17 @@ 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,...} + for (const indexExt of ['.js', '.json', ...TS_RESOLVE_EXTENSIONS]) { + const indexPath = pathShim.join(basePath, 'index' + indexExt); + if (vfs.existsSync(indexPath)) { + return indexPath; + } } } // Try with extensions - const extensions = ['.js', '.json', '.node']; + const extensions = ['.js', '.json', '.node', ...TS_RESOLVE_EXTENSIONS]; for (const ext of extensions) { const withExt = basePath + ext; if (vfs.existsSync(withExt)) { @@ -719,10 +729,17 @@ 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. + if (isTypeScriptFile(resolvedPath)) { + code = stripTypeScriptTypes(code, 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); } @@ -1282,8 +1299,14 @@ export class Runtime { code = code.slice(code.indexOf('\n') + 1); } + // Strip TypeScript types first (mirrors Node's native .ts support). + if (isTypeScriptFile(filename)) { + code = stripTypeScriptTypes(code, 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); } diff --git a/src/shims/child_process.ts b/src/shims/child_process.ts index 1cd0263..fd5ce46 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'].map(ext => basePath + ext), + ...['index.js', 'index.ts', 'index.mts', 'index.cts'].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..a3e7ee2 100644 --- a/tests/child_process.test.ts +++ b/tests/child_process.test.ts @@ -135,6 +135,43 @@ 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); + }); + }); + describe('shell features', () => { it('should support pipes', async () => { const code = ` 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); + }); +}); From 41a3e52c1609f0a6eba19511093185074407d839 Mon Sep 17 00:00:00 2001 From: Jacob Ebey Date: Mon, 1 Jun 2026 19:45:02 -0700 Subject: [PATCH 2/5] add bundle script --- package-lock.json | 352 ++++-- package.json | 3 +- pnpm-lock.yaml | 2698 +++++++++++++++++++++++++++++++++++++++++++ pnpm-workspace.yaml | 5 + scripts/bundle.ts | 34 + 5 files changed, 2978 insertions(+), 114 deletions(-) create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml create mode 100644 scripts/bundle.ts diff --git a/package-lock.json b/package-lock.json index d98fa8d..1e802e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "almostnode", - "version": "0.2.14", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "almostnode", - "version": "0.2.14", + "version": "0.3.0", "license": "MIT", "dependencies": { "@ai-sdk/openai": "^3.0.28", @@ -34,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", @@ -310,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" ], @@ -327,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" ], @@ -344,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" ], @@ -361,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" ], @@ -378,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" ], @@ -395,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" ], @@ -412,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" ], @@ -429,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" ], @@ -446,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" ], @@ -463,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" ], @@ -480,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" ], @@ -497,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" ], @@ -514,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" ], @@ -531,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" ], @@ -548,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" ], @@ -565,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" ], @@ -582,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" ], @@ -599,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" ], @@ -616,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" ], @@ -633,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" ], @@ -650,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" ], @@ -667,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" ], @@ -684,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" ], @@ -701,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" ], @@ -718,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" ], @@ -735,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" ], @@ -797,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", @@ -1589,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" @@ -1795,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", @@ -1966,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", @@ -1979,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": { @@ -2045,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", @@ -2290,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", @@ -2358,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", @@ -2523,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", @@ -2656,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", @@ -3177,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 1a60e91..270f3b4 100644 --- a/package.json +++ b/package.json @@ -110,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(), + ], +}); From 610f0b7b9ba4c9b322b011f864fe3f54583d5329 Mon Sep 17 00:00:00 2001 From: Jacob Ebey Date: Mon, 1 Jun 2026 20:02:03 -0700 Subject: [PATCH 3/5] feat: add JSX/TSX support with config-driven transformation and runtime integration --- CHANGELOG.md | 8 + README.md | 2 +- package.json | 2 +- src/frameworks/jsx-config.ts | 174 ++++++++++++++++++ src/frameworks/jsx-transform.ts | 310 ++++++++++++++++++++++++++++++++ src/runtime.ts | 32 +++- src/shims/child_process.ts | 4 +- tests/child_process.test.ts | 25 +++ tests/jsx-files.test.ts | 177 ++++++++++++++++++ 9 files changed, 721 insertions(+), 13 deletions(-) create mode 100644 src/frameworks/jsx-config.ts create mode 100644 src/frameworks/jsx-transform.ts create mode 100644 tests/jsx-files.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index b183a1e..538a2cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ 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. + ## [0.3.0] - 2026-06-01 ### Added diff --git a/README.md b/README.md index 40a5434..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** - Run `.ts`/`.mts`/`.cts` files directly via Node-style type-stripping (synchronous, pure-JS); first-class TypeScript/TSX transformation in dev servers 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.json b/package.json index 270f3b4..7dcaa81 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "almostnode", - "version": "0.3.0", + "version": "0.4.0", "description": "Node.js in your browser. Just like that.", "type": "module", "license": "MIT", 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/runtime.ts b/src/runtime.ts index 955500d..b18c208 100644 --- a/src/runtime.ts +++ b/src/runtime.ts @@ -60,6 +60,8 @@ import { 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'; /** @@ -511,8 +513,8 @@ function createRequire( resolutionCache.set(cacheKey, resolved); return resolved; } - // Directory - look for index.{js,json,ts,...} - for (const indexExt of ['.js', '.json', ...TS_RESOLVE_EXTENSIONS]) { + // 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); @@ -522,7 +524,7 @@ function createRequire( } // Try with extensions - const extensions = ['.js', '.json', ...TS_RESOLVE_EXTENSIONS]; + const extensions = ['.js', '.json', ...TS_RESOLVE_EXTENSIONS, ...JSX_RESOLVE_EXTENSIONS]; for (const ext of extensions) { const withExt = resolved + ext; if (vfs.existsSync(withExt)) { @@ -543,8 +545,8 @@ function createRequire( if (stats.isFile()) { return basePath; } - // Directory - look for index.{js,json,ts,...} - for (const indexExt of ['.js', '.json', ...TS_RESOLVE_EXTENSIONS]) { + // 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; @@ -553,7 +555,7 @@ function createRequire( } // Try with extensions - const extensions = ['.js', '.json', '.node', ...TS_RESOLVE_EXTENSIONS]; + const extensions = ['.js', '.json', '.node', ...TS_RESOLVE_EXTENSIONS, ...JSX_RESOLVE_EXTENSIONS]; for (const ext of extensions) { const withExt = basePath + ext; if (vfs.existsSync(withExt)) { @@ -730,11 +732,17 @@ function createRequire( } // Strip TypeScript types first (mirrors Node's native .ts support). - // Whitespace-preserving, so the result still parses as plain JS. - if (isTypeScriptFile(resolvedPath)) { + // 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) @@ -1300,10 +1308,16 @@ export class Runtime { } // Strip TypeScript types first (mirrors Node's native .ts support). - if (isTypeScriptFile(filename)) { + // .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) // .cjs and .cts are CommonJS — skip the ESM transform. if (!filename.endsWith('.cjs') && !isCommonJsTypeScriptFile(filename)) { diff --git a/src/shims/child_process.ts b/src/shims/child_process.ts index fd5ce46..0d1b077 100644 --- a/src/shims/child_process.ts +++ b/src/shims/child_process.ts @@ -132,8 +132,8 @@ export function initChildProcess(vfs: VirtualFS): void { let resolvedPath = basePath; if (!currentVfs.existsSync(resolvedPath) || currentVfs.statSync(resolvedPath).isDirectory()) { const candidates = [ - ...['.js', '.cjs', '.mjs', '.ts', '.mts', '.cts'].map(ext => basePath + ext), - ...['index.js', 'index.ts', 'index.mts', 'index.cts'].map( + ...['.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, '/') ), ]; diff --git a/tests/child_process.test.ts b/tests/child_process.test.ts index a3e7ee2..405b56a 100644 --- a/tests/child_process.test.ts +++ b/tests/child_process.test.ts @@ -170,6 +170,31 @@ exec('node /main', (error, stdout) => { 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', () => { diff --git a/tests/jsx-files.test.ts b/tests/jsx-files.test.ts new file mode 100644 index 0000000..23922c0 --- /dev/null +++ b/tests/jsx-files.test.ts @@ -0,0 +1,177 @@ +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 =
    {items.map(i =>
  • {i}
  • )}
;', 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 =
  • a
  • b
; + 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('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'); + }); +}); From 2627348c56dfffce5eba4c44ef0e0040ad960a20 Mon Sep 17 00:00:00 2001 From: Jacob Ebey Date: Mon, 1 Jun 2026 23:12:57 -0700 Subject: [PATCH 4/5] feat: enhance TypeScript support with import extension rewriting and local binding preservation for named exports --- CHANGELOG.md | 4 ++ src/frameworks/code-transforms.ts | 55 ++++++++++++++++------ src/runtime.ts | 39 ++++++++++++++++ tests/export-binding.test.ts | 77 +++++++++++++++++++++++++++++++ tests/jsx-files.test.ts | 41 ++++++++++++++++ 5 files changed, 203 insertions(+), 13 deletions(-) create mode 100644 tests/export-binding.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 538a2cf..77537da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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 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/runtime.ts b/src/runtime.ts index b18c208..d5b6d0b 100644 --- a/src/runtime.ts +++ b/src/runtime.ts @@ -424,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 */ @@ -523,6 +545,15 @@ function createRequire( } } + // 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', ...TS_RESOLVE_EXTENSIONS, ...JSX_RESOLVE_EXTENSIONS]; for (const ext of extensions) { @@ -554,6 +585,14 @@ function createRequire( } } + // 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', ...TS_RESOLVE_EXTENSIONS, ...JSX_RESOLVE_EXTENSIONS]; for (const ext of extensions) { 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 index 23922c0..4f0d823 100644 --- a/tests/jsx-files.test.ts +++ b/tests/jsx-files.test.ts @@ -160,6 +160,47 @@ describe('Running .jsx/.tsx files through the runtime', () => { 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', From b8be5d068f87fade2670948b8a91b23ba7aaf16a Mon Sep 17 00:00:00 2001 From: Jacob Ebey Date: Tue, 2 Jun 2026 15:21:13 -0700 Subject: [PATCH 5/5] feat: define browser global variables as undefined for Node.js environment compatibility --- src/runtime.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/runtime.ts b/src/runtime.ts index d5b6d0b..30c6419 100644 --- a/src/runtime.ts +++ b/src/runtime.ts @@ -826,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; @@ -1377,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;