@run0/jiki 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/dist/browser-bundle.d.ts +40 -0
  2. package/dist/builtins.d.ts +22 -0
  3. package/dist/code-transform.d.ts +7 -0
  4. package/dist/config/cdn.d.ts +13 -0
  5. package/dist/container.d.ts +101 -0
  6. package/dist/dev-server.d.ts +69 -0
  7. package/dist/errors.d.ts +19 -0
  8. package/dist/frameworks/code-transforms.d.ts +32 -0
  9. package/dist/frameworks/next-api-handler.d.ts +72 -0
  10. package/dist/frameworks/next-dev-server.d.ts +141 -0
  11. package/dist/frameworks/next-html-generator.d.ts +36 -0
  12. package/dist/frameworks/next-route-resolver.d.ts +19 -0
  13. package/dist/frameworks/next-shims.d.ts +78 -0
  14. package/dist/frameworks/remix-dev-server.d.ts +47 -0
  15. package/dist/frameworks/sveltekit-dev-server.d.ts +43 -0
  16. package/dist/frameworks/vite-dev-server.d.ts +50 -0
  17. package/dist/fs-errors.d.ts +36 -0
  18. package/dist/index.cjs +14916 -0
  19. package/dist/index.cjs.map +1 -0
  20. package/dist/index.d.ts +61 -0
  21. package/dist/index.mjs +14898 -0
  22. package/dist/index.mjs.map +1 -0
  23. package/dist/kernel.d.ts +48 -0
  24. package/dist/memfs.d.ts +144 -0
  25. package/dist/metrics.d.ts +78 -0
  26. package/dist/module-resolver.d.ts +60 -0
  27. package/dist/network-interceptor.d.ts +71 -0
  28. package/dist/npm/cache.d.ts +76 -0
  29. package/dist/npm/index.d.ts +60 -0
  30. package/dist/npm/lockfile-reader.d.ts +32 -0
  31. package/dist/npm/pnpm.d.ts +18 -0
  32. package/dist/npm/registry.d.ts +45 -0
  33. package/dist/npm/resolver.d.ts +39 -0
  34. package/dist/npm/sync-installer.d.ts +18 -0
  35. package/dist/npm/tarball.d.ts +4 -0
  36. package/dist/npm/workspaces.d.ts +46 -0
  37. package/dist/persistence.d.ts +94 -0
  38. package/dist/plugin.d.ts +156 -0
  39. package/dist/polyfills/assert.d.ts +30 -0
  40. package/dist/polyfills/child_process.d.ts +116 -0
  41. package/dist/polyfills/chokidar.d.ts +18 -0
  42. package/dist/polyfills/crypto.d.ts +49 -0
  43. package/dist/polyfills/events.d.ts +28 -0
  44. package/dist/polyfills/fs.d.ts +82 -0
  45. package/dist/polyfills/http.d.ts +147 -0
  46. package/dist/polyfills/module.d.ts +29 -0
  47. package/dist/polyfills/net.d.ts +53 -0
  48. package/dist/polyfills/os.d.ts +91 -0
  49. package/dist/polyfills/path.d.ts +96 -0
  50. package/dist/polyfills/perf_hooks.d.ts +21 -0
  51. package/dist/polyfills/process.d.ts +99 -0
  52. package/dist/polyfills/querystring.d.ts +15 -0
  53. package/dist/polyfills/readdirp.d.ts +18 -0
  54. package/dist/polyfills/readline.d.ts +32 -0
  55. package/dist/polyfills/stream.d.ts +106 -0
  56. package/dist/polyfills/stubs.d.ts +737 -0
  57. package/dist/polyfills/tty.d.ts +25 -0
  58. package/dist/polyfills/url.d.ts +41 -0
  59. package/dist/polyfills/util.d.ts +61 -0
  60. package/dist/polyfills/v8.d.ts +43 -0
  61. package/dist/polyfills/vm.d.ts +76 -0
  62. package/dist/polyfills/worker-threads.d.ts +77 -0
  63. package/dist/polyfills/ws.d.ts +32 -0
  64. package/dist/polyfills/zlib.d.ts +87 -0
  65. package/dist/runtime-helpers.d.ts +4 -0
  66. package/dist/runtime-interface.d.ts +39 -0
  67. package/dist/sandbox.d.ts +69 -0
  68. package/dist/server-bridge.d.ts +55 -0
  69. package/dist/shell-commands.d.ts +2 -0
  70. package/dist/shell.d.ts +101 -0
  71. package/dist/transpiler.d.ts +47 -0
  72. package/dist/type-checker.d.ts +57 -0
  73. package/dist/types/package-json.d.ts +17 -0
  74. package/dist/utils/binary-encoding.d.ts +4 -0
  75. package/dist/utils/hash.d.ts +6 -0
  76. package/dist/utils/safe-path.d.ts +6 -0
  77. package/dist/worker-runtime.d.ts +34 -0
  78. package/package.json +59 -0
  79. package/src/browser-bundle.ts +498 -0
  80. package/src/builtins.ts +222 -0
  81. package/src/code-transform.ts +183 -0
  82. package/src/config/cdn.ts +17 -0
  83. package/src/container.ts +343 -0
  84. package/src/dev-server.ts +322 -0
  85. package/src/errors.ts +604 -0
  86. package/src/frameworks/code-transforms.ts +667 -0
  87. package/src/frameworks/next-api-handler.ts +366 -0
  88. package/src/frameworks/next-dev-server.ts +1252 -0
  89. package/src/frameworks/next-html-generator.ts +585 -0
  90. package/src/frameworks/next-route-resolver.ts +521 -0
  91. package/src/frameworks/next-shims.ts +1084 -0
  92. package/src/frameworks/remix-dev-server.ts +163 -0
  93. package/src/frameworks/sveltekit-dev-server.ts +197 -0
  94. package/src/frameworks/vite-dev-server.ts +370 -0
  95. package/src/fs-errors.ts +118 -0
  96. package/src/index.ts +188 -0
  97. package/src/kernel.ts +381 -0
  98. package/src/memfs.ts +1006 -0
  99. package/src/metrics.ts +140 -0
  100. package/src/module-resolver.ts +511 -0
  101. package/src/network-interceptor.ts +143 -0
  102. package/src/npm/cache.ts +172 -0
  103. package/src/npm/index.ts +377 -0
  104. package/src/npm/lockfile-reader.ts +105 -0
  105. package/src/npm/pnpm.ts +108 -0
  106. package/src/npm/registry.ts +120 -0
  107. package/src/npm/resolver.ts +339 -0
  108. package/src/npm/sync-installer.ts +217 -0
  109. package/src/npm/tarball.ts +136 -0
  110. package/src/npm/workspaces.ts +255 -0
  111. package/src/persistence.ts +235 -0
  112. package/src/plugin.ts +293 -0
  113. package/src/polyfills/assert.ts +164 -0
  114. package/src/polyfills/child_process.ts +535 -0
  115. package/src/polyfills/chokidar.ts +52 -0
  116. package/src/polyfills/crypto.ts +433 -0
  117. package/src/polyfills/events.ts +178 -0
  118. package/src/polyfills/fs.ts +297 -0
  119. package/src/polyfills/http.ts +478 -0
  120. package/src/polyfills/module.ts +97 -0
  121. package/src/polyfills/net.ts +123 -0
  122. package/src/polyfills/os.ts +108 -0
  123. package/src/polyfills/path.ts +169 -0
  124. package/src/polyfills/perf_hooks.ts +30 -0
  125. package/src/polyfills/process.ts +349 -0
  126. package/src/polyfills/querystring.ts +66 -0
  127. package/src/polyfills/readdirp.ts +72 -0
  128. package/src/polyfills/readline.ts +80 -0
  129. package/src/polyfills/stream.ts +610 -0
  130. package/src/polyfills/stubs.ts +600 -0
  131. package/src/polyfills/tty.ts +43 -0
  132. package/src/polyfills/url.ts +97 -0
  133. package/src/polyfills/util.ts +173 -0
  134. package/src/polyfills/v8.ts +62 -0
  135. package/src/polyfills/vm.ts +111 -0
  136. package/src/polyfills/worker-threads.ts +189 -0
  137. package/src/polyfills/ws.ts +73 -0
  138. package/src/polyfills/zlib.ts +244 -0
  139. package/src/runtime-helpers.ts +83 -0
  140. package/src/runtime-interface.ts +46 -0
  141. package/src/sandbox.ts +178 -0
  142. package/src/server-bridge.ts +473 -0
  143. package/src/service-worker.ts +153 -0
  144. package/src/shell-commands.ts +708 -0
  145. package/src/shell.ts +795 -0
  146. package/src/transpiler.ts +282 -0
  147. package/src/type-checker.ts +241 -0
  148. package/src/types/package-json.ts +17 -0
  149. package/src/utils/binary-encoding.ts +38 -0
  150. package/src/utils/hash.ts +24 -0
  151. package/src/utils/safe-path.ts +38 -0
  152. package/src/worker-runtime.ts +42 -0
@@ -0,0 +1,282 @@
1
+ /**
2
+ * TypeScript/TSX/JSX transpiler and bundler powered by esbuild-wasm.
3
+ * Provides lazy initialization with singleton pattern for the WASM runtime.
4
+ */
5
+
6
+ import * as esbuild from "esbuild-wasm";
7
+ import type { MemFS } from "./memfs";
8
+ import * as pathShim from "./polyfills/path";
9
+
10
+ let initialized = false;
11
+ let initializing: Promise<void> | null = null;
12
+
13
+ export interface TranspileOptions {
14
+ jsx?: "transform" | "preserve" | "automatic";
15
+ jsxFactory?: string;
16
+ jsxFragment?: string;
17
+ jsxImportSource?: string;
18
+ target?: string;
19
+ sourcemap?: boolean;
20
+ }
21
+
22
+ export interface BundleOptions {
23
+ entryPoint: string;
24
+ format?: "esm" | "cjs" | "iife";
25
+ platform?: "browser" | "node" | "neutral";
26
+ minify?: boolean;
27
+ sourcemap?: boolean;
28
+ target?: string;
29
+ external?: string[];
30
+ outfile?: string;
31
+ loader?: Record<string, string>;
32
+ define?: Record<string, string>;
33
+ }
34
+
35
+ export interface BundleResult {
36
+ code: string;
37
+ map?: string;
38
+ errors: esbuild.Message[];
39
+ warnings: esbuild.Message[];
40
+ }
41
+
42
+ export interface InitOptions {
43
+ wasmURL?: string | URL;
44
+ /** When true, esbuild runs transpilation in a Web Worker (browser only). */
45
+ useWorker?: boolean;
46
+ }
47
+
48
+ let customWasmURL: string | URL | undefined;
49
+
50
+ export function setWasmURL(url: string | URL): void {
51
+ customWasmURL = url;
52
+ }
53
+
54
+ function resolveWasmURL(options?: InitOptions): string | URL | undefined {
55
+ if (options?.wasmURL) return options.wasmURL;
56
+ if (customWasmURL) return customWasmURL;
57
+
58
+ const isBrowser =
59
+ typeof window !== "undefined" || typeof importScripts === "function";
60
+ if (!isBrowser) return undefined;
61
+
62
+ return "https://unpkg.com/esbuild-wasm@" + esbuild.version + "/esbuild.wasm";
63
+ }
64
+
65
+ export async function initTranspiler(options?: InitOptions): Promise<void> {
66
+ if (initialized) return;
67
+ if (initializing) return initializing;
68
+
69
+ const initOpts: esbuild.InitializeOptions = {
70
+ worker: options?.useWorker ?? false,
71
+ };
72
+ const wasmURL = resolveWasmURL(options);
73
+ if (wasmURL) initOpts.wasmURL = wasmURL;
74
+
75
+ initializing = esbuild.initialize(initOpts);
76
+ await initializing;
77
+ initialized = true;
78
+ initializing = null;
79
+ }
80
+
81
+ export function isInitialized(): boolean {
82
+ return initialized;
83
+ }
84
+
85
+ function loaderFromFilename(filename: string): esbuild.Loader {
86
+ if (filename.endsWith(".tsx")) return "tsx";
87
+ if (filename.endsWith(".ts")) return "ts";
88
+ if (filename.endsWith(".jsx")) return "jsx";
89
+ return "js";
90
+ }
91
+
92
+ export function needsTranspilation(filename: string): boolean {
93
+ return (
94
+ filename.endsWith(".ts") ||
95
+ filename.endsWith(".tsx") ||
96
+ filename.endsWith(".jsx")
97
+ );
98
+ }
99
+
100
+ export async function transpile(
101
+ code: string,
102
+ filename: string,
103
+ options: TranspileOptions = {},
104
+ ): Promise<string> {
105
+ await initTranspiler();
106
+
107
+ const loader = loaderFromFilename(filename);
108
+ const result = await esbuild.transform(code, {
109
+ loader,
110
+ format: "esm",
111
+ target: options.target || "esnext",
112
+ sourcemap: options.sourcemap ? "inline" : false,
113
+ jsx: options.jsx || "automatic",
114
+ jsxFactory: options.jsxFactory,
115
+ jsxFragment: options.jsxFragment,
116
+ jsxImportSource: options.jsxImportSource,
117
+ });
118
+
119
+ return result.code;
120
+ }
121
+
122
+ const isBrowserEnv =
123
+ typeof window !== "undefined" || typeof importScripts === "function";
124
+
125
+ export function hasSyncSupport(): boolean {
126
+ return !isBrowserEnv;
127
+ }
128
+
129
+ export function transpileSync(
130
+ code: string,
131
+ filename: string,
132
+ options: TranspileOptions = {},
133
+ ): string {
134
+ if (!initialized) {
135
+ throw new Error(
136
+ "Transpiler not initialized. Call initTranspiler() first or use transpile() which auto-initializes.",
137
+ );
138
+ }
139
+
140
+ if (isBrowserEnv) {
141
+ throw new Error(
142
+ "transpileSync is not available in the browser. Use transpile() (async) or prepareFile() to pre-transpile.",
143
+ );
144
+ }
145
+
146
+ const loader = loaderFromFilename(filename);
147
+ const result = esbuild.transformSync(code, {
148
+ loader,
149
+ format: "esm",
150
+ target: options.target || "esnext",
151
+ sourcemap: options.sourcemap ? "inline" : false,
152
+ jsx: options.jsx || "automatic",
153
+ jsxFactory: options.jsxFactory,
154
+ jsxFragment: options.jsxFragment,
155
+ jsxImportSource: options.jsxImportSource,
156
+ });
157
+
158
+ return result.code;
159
+ }
160
+
161
+ function createMemFSPlugin(vfs: MemFS, cwd: string): esbuild.Plugin {
162
+ return {
163
+ name: "memfs",
164
+ setup(build) {
165
+ build.onResolve({ filter: /.*/ }, args => {
166
+ if (args.kind === "entry-point") {
167
+ const resolved = pathShim.isAbsolute(args.path)
168
+ ? args.path
169
+ : pathShim.resolve(cwd, args.path);
170
+ return { path: resolved, namespace: "memfs" };
171
+ }
172
+
173
+ const resolveDir = args.resolveDir || cwd;
174
+
175
+ if (args.path.startsWith(".") || args.path.startsWith("/")) {
176
+ const base = args.path.startsWith("/")
177
+ ? args.path
178
+ : pathShim.resolve(resolveDir, args.path);
179
+
180
+ for (const candidate of [
181
+ base,
182
+ `${base}.ts`,
183
+ `${base}.tsx`,
184
+ `${base}.js`,
185
+ `${base}.jsx`,
186
+ `${base}.json`,
187
+ ]) {
188
+ if (vfs.existsSync(candidate)) {
189
+ try {
190
+ if (vfs.statSync(candidate).isFile()) {
191
+ return { path: candidate, namespace: "memfs" };
192
+ }
193
+ } catch {}
194
+ }
195
+ }
196
+
197
+ for (const indexFile of [
198
+ "index.ts",
199
+ "index.tsx",
200
+ "index.js",
201
+ "index.jsx",
202
+ "index.json",
203
+ ]) {
204
+ const indexPath = pathShim.join(base, indexFile);
205
+ if (vfs.existsSync(indexPath)) {
206
+ return { path: indexPath, namespace: "memfs" };
207
+ }
208
+ }
209
+ }
210
+
211
+ return { path: args.path, external: true };
212
+ });
213
+
214
+ build.onLoad({ filter: /.*/, namespace: "memfs" }, args => {
215
+ try {
216
+ const contents = vfs.readFileSync(args.path, "utf8");
217
+ return { contents, loader: loaderFromFilename(args.path) };
218
+ } catch (e) {
219
+ return {
220
+ errors: [
221
+ {
222
+ text: `File not found: ${args.path}`,
223
+ } as esbuild.PartialMessage,
224
+ ],
225
+ };
226
+ }
227
+ });
228
+ },
229
+ };
230
+ }
231
+
232
+ export async function bundle(
233
+ vfs: MemFS,
234
+ options: BundleOptions,
235
+ ): Promise<BundleResult> {
236
+ await initTranspiler();
237
+
238
+ const cwd = pathShim.dirname(
239
+ pathShim.isAbsolute(options.entryPoint)
240
+ ? options.entryPoint
241
+ : pathShim.resolve("/", options.entryPoint),
242
+ );
243
+
244
+ const result = await esbuild.build({
245
+ entryPoints: [options.entryPoint],
246
+ bundle: true,
247
+ write: false,
248
+ format: options.format || "esm",
249
+ platform: options.platform || "browser",
250
+ minify: options.minify || false,
251
+ sourcemap: options.sourcemap ? "inline" : false,
252
+ target: options.target || "esnext",
253
+ external: options.external,
254
+ define: options.define,
255
+ plugins: [createMemFSPlugin(vfs, cwd)],
256
+ });
257
+
258
+ const code = result.outputFiles?.[0]?.text || "";
259
+
260
+ if (options.outfile) {
261
+ const outDir = pathShim.dirname(options.outfile);
262
+ if (!vfs.existsSync(outDir)) {
263
+ vfs.mkdirSync(outDir, { recursive: true });
264
+ }
265
+ vfs.writeFileSync(options.outfile, code);
266
+ }
267
+
268
+ return {
269
+ code,
270
+ errors: result.errors,
271
+ warnings: result.warnings,
272
+ };
273
+ }
274
+
275
+ export async function stopTranspiler(): Promise<void> {
276
+ if (initialized) {
277
+ await esbuild.stop();
278
+ initialized = false;
279
+ }
280
+ }
281
+
282
+ export { esbuild };
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Lightweight TypeScript type-checker for jiki.
3
+ *
4
+ * Provides on-demand batch type checking by analyzing TypeScript files
5
+ * in the VFS. Uses a simple heuristic-based approach that catches common
6
+ * type errors without requiring the full TypeScript compiler.
7
+ *
8
+ * For full type checking, the TypeScript compiler can be installed as an
9
+ * npm package and run via the shell: `npx tsc --noEmit`.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const checker = new TypeChecker(container.vfs);
14
+ * const diagnostics = checker.check(['/src/app.ts', '/src/utils.ts']);
15
+ * for (const d of diagnostics) {
16
+ * console.log(`${d.file}:${d.line} - ${d.message}`);
17
+ * }
18
+ * ```
19
+ */
20
+
21
+ import type { MemFS } from "./memfs";
22
+ import * as pathShim from "./polyfills/path";
23
+
24
+ export interface Diagnostic {
25
+ file: string;
26
+ line: number;
27
+ column: number;
28
+ message: string;
29
+ severity: "error" | "warning" | "info";
30
+ code?: string;
31
+ }
32
+
33
+ export interface TypeCheckerOptions {
34
+ /** Strict mode (default: true). */
35
+ strict?: boolean;
36
+ /** Check for unused variables (default: false). */
37
+ noUnusedLocals?: boolean;
38
+ /** Check for implicit any (default: true in strict mode). */
39
+ noImplicitAny?: boolean;
40
+ }
41
+
42
+ /**
43
+ * Lightweight TypeScript type-checker that catches common errors
44
+ * using pattern matching and heuristics. Not a full type system,
45
+ * but catches the most common mistakes quickly.
46
+ */
47
+ export class TypeChecker {
48
+ private vfs: MemFS;
49
+ private opts: Required<TypeCheckerOptions>;
50
+
51
+ constructor(vfs: MemFS, options: TypeCheckerOptions = {}) {
52
+ this.vfs = vfs;
53
+ this.opts = {
54
+ strict: options.strict ?? true,
55
+ noUnusedLocals: options.noUnusedLocals ?? false,
56
+ noImplicitAny: options.noImplicitAny ?? options.strict !== false,
57
+ };
58
+ }
59
+
60
+ /**
61
+ * Check a list of TypeScript files for common errors.
62
+ * Returns diagnostics sorted by file and line number.
63
+ */
64
+ check(files: string[]): Diagnostic[] {
65
+ const diagnostics: Diagnostic[] = [];
66
+
67
+ for (const file of files) {
68
+ if (!file.match(/\.(ts|tsx)$/)) continue;
69
+ try {
70
+ const source = this.vfs.readFileSync(file, "utf8");
71
+ diagnostics.push(...this.checkFile(file, source));
72
+ } catch {
73
+ diagnostics.push({
74
+ file,
75
+ line: 0,
76
+ column: 0,
77
+ message: `Cannot read file: ${file}`,
78
+ severity: "error",
79
+ });
80
+ }
81
+ }
82
+
83
+ return diagnostics.sort(
84
+ (a, b) => a.file.localeCompare(b.file) || a.line - b.line,
85
+ );
86
+ }
87
+
88
+ /**
89
+ * Discover all .ts/.tsx files in a directory and check them.
90
+ */
91
+ checkAll(dir: string = "/src"): Diagnostic[] {
92
+ const files = this.discoverFiles(dir);
93
+ return this.check(files);
94
+ }
95
+
96
+ private discoverFiles(dir: string): string[] {
97
+ const files: string[] = [];
98
+ try {
99
+ for (const entry of this.vfs.readdirSync(dir)) {
100
+ const full = pathShim.join(dir, entry);
101
+ try {
102
+ if (this.vfs.statSync(full).isDirectory()) {
103
+ if (entry !== "node_modules" && !entry.startsWith(".")) {
104
+ files.push(...this.discoverFiles(full));
105
+ }
106
+ } else if (entry.match(/\.(ts|tsx)$/) && !entry.endsWith(".d.ts")) {
107
+ files.push(full);
108
+ }
109
+ } catch {
110
+ /* skip */
111
+ }
112
+ }
113
+ } catch {
114
+ /* dir may not exist */
115
+ }
116
+ return files;
117
+ }
118
+
119
+ private checkFile(file: string, source: string): Diagnostic[] {
120
+ const diags: Diagnostic[] = [];
121
+ const lines = source.split("\n");
122
+
123
+ for (let i = 0; i < lines.length; i++) {
124
+ const line = lines[i];
125
+ const lineNum = i + 1;
126
+
127
+ // Check for common TypeScript errors
128
+
129
+ // 1. Missing return type on exported functions (noImplicitAny)
130
+ if (this.opts.noImplicitAny) {
131
+ const exportFnMatch = line.match(
132
+ /^export\s+(?:default\s+)?function\s+(\w+)\s*\([^)]*\)\s*\{/,
133
+ );
134
+ if (exportFnMatch && !line.includes("):")) {
135
+ diags.push({
136
+ file,
137
+ line: lineNum,
138
+ column: 0,
139
+ message: `Function '${exportFnMatch[1]}' has no return type annotation`,
140
+ severity: "warning",
141
+ code: "TS7030",
142
+ });
143
+ }
144
+ }
145
+
146
+ // 2. Duplicate variable declarations in same scope
147
+ const constMatch = line.match(/^\s*(const|let|var)\s+(\w+)/);
148
+ if (constMatch) {
149
+ const varName = constMatch[2];
150
+ // Check for redeclaration in subsequent lines (same indentation level)
151
+ for (let j = i + 1; j < lines.length && j < i + 50; j++) {
152
+ const reDecl = lines[j].match(
153
+ new RegExp(`^\\s*(const|let)\\s+${varName}\\b`),
154
+ );
155
+ if (
156
+ reDecl &&
157
+ lines[j].match(/^\s*/)?.[0] === line.match(/^\s*/)?.[0]
158
+ ) {
159
+ diags.push({
160
+ file,
161
+ line: j + 1,
162
+ column: 0,
163
+ message: `Cannot redeclare block-scoped variable '${varName}'`,
164
+ severity: "error",
165
+ code: "TS2451",
166
+ });
167
+ break;
168
+ }
169
+ }
170
+ }
171
+
172
+ // 3. Using `any` when noImplicitAny is enabled
173
+ if (this.opts.noImplicitAny && this.opts.strict) {
174
+ const anyTypeMatch = line.match(/:\s*any\b/);
175
+ if (
176
+ anyTypeMatch &&
177
+ !line.includes("// @ts-ignore") &&
178
+ !line.includes("eslint-disable")
179
+ ) {
180
+ diags.push({
181
+ file,
182
+ line: lineNum,
183
+ column: anyTypeMatch.index ?? 0,
184
+ message: "Unexpected use of 'any' type",
185
+ severity: "warning",
186
+ code: "TS7006",
187
+ });
188
+ }
189
+ }
190
+
191
+ // 4. Unreachable code after return/throw
192
+ if (line.match(/^\s*(return|throw)\b/) && i + 1 < lines.length) {
193
+ const nextLine = lines[i + 1]?.trim();
194
+ if (
195
+ nextLine &&
196
+ !nextLine.startsWith("}") &&
197
+ !nextLine.startsWith("//") &&
198
+ !nextLine.startsWith("/*") &&
199
+ nextLine !== ""
200
+ ) {
201
+ diags.push({
202
+ file,
203
+ line: lineNum + 1,
204
+ column: 0,
205
+ message: "Unreachable code detected",
206
+ severity: "warning",
207
+ code: "TS7027",
208
+ });
209
+ }
210
+ }
211
+
212
+ // 5. Syntax: missing semicolons at statement boundaries (if strict)
213
+ // Only flag obvious cases: variable declarations without semicolons
214
+ if (this.opts.strict) {
215
+ const stmtMatch = line.match(
216
+ /^\s*(const|let|var)\s+\w+\s*=\s*.+[^;{,\s]\s*$/,
217
+ );
218
+ if (
219
+ stmtMatch &&
220
+ !line.includes("//") &&
221
+ !line.endsWith("=>") &&
222
+ !line.endsWith("(")
223
+ ) {
224
+ // Skip multi-line expressions
225
+ const nextLine = lines[i + 1]?.trim() || "";
226
+ if (
227
+ nextLine &&
228
+ !nextLine.startsWith(".") &&
229
+ !nextLine.startsWith("+") &&
230
+ !nextLine.startsWith("?") &&
231
+ !nextLine.startsWith(":")
232
+ ) {
233
+ // This is a potential missing semicolon — but too noisy, skip for now
234
+ }
235
+ }
236
+ }
237
+ }
238
+
239
+ return diags;
240
+ }
241
+ }
@@ -0,0 +1,17 @@
1
+ export interface PackageJson {
2
+ name?: string;
3
+ version?: string;
4
+ main?: string;
5
+ module?: string;
6
+ browser?: string | Record<string, string | false>;
7
+ exports?: unknown;
8
+ imports?: Record<string, unknown>;
9
+ bin?: string | Record<string, string>;
10
+ scripts?: Record<string, string>;
11
+ dependencies?: Record<string, string>;
12
+ devDependencies?: Record<string, string>;
13
+ peerDependencies?: Record<string, string>;
14
+ optionalDependencies?: Record<string, string>;
15
+ type?: "module" | "commonjs";
16
+ [key: string]: unknown;
17
+ }
@@ -0,0 +1,38 @@
1
+ const CHUNK = 8192;
2
+
3
+ export function uint8ToBase64(bytes: Uint8Array): string {
4
+ const parts: string[] = [];
5
+ for (let i = 0; i < bytes.length; i += CHUNK) {
6
+ parts.push(
7
+ String.fromCharCode.apply(null, Array.from(bytes.subarray(i, i + CHUNK))),
8
+ );
9
+ }
10
+ return btoa(parts.join(""));
11
+ }
12
+
13
+ export function base64ToUint8(base64: string): Uint8Array {
14
+ const binary = atob(base64);
15
+ const bytes = new Uint8Array(binary.length);
16
+ for (let i = 0; i < binary.length; i++) {
17
+ bytes[i] = binary.charCodeAt(i);
18
+ }
19
+ return bytes;
20
+ }
21
+
22
+ export function uint8ToHex(bytes: Uint8Array): string {
23
+ const hex = new Array(bytes.length);
24
+ for (let i = 0; i < bytes.length; i++) {
25
+ hex[i] = bytes[i].toString(16).padStart(2, "0");
26
+ }
27
+ return hex.join("");
28
+ }
29
+
30
+ export function uint8ToBinaryString(bytes: Uint8Array): string {
31
+ const parts: string[] = [];
32
+ for (let i = 0; i < bytes.length; i += CHUNK) {
33
+ parts.push(
34
+ String.fromCharCode.apply(null, Array.from(bytes.subarray(i, i + CHUNK))),
35
+ );
36
+ }
37
+ return parts.join("");
38
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * FNV-1a hash producing a 64-bit result (two 32-bit halves).
3
+ * Returns base36 string (~12 chars). Much lower collision probability
4
+ * than the previous 32-bit djb2 (~6 chars).
5
+ */
6
+ export function simpleHash(str: string): string {
7
+ // FNV-1a 64-bit offset basis and prime (split into high/low 32-bit)
8
+ let h0 = 0x811c9dc5; // low 32 bits of offset basis
9
+ let h1 = 0xcbf29ce4; // high 32 bits of offset basis
10
+
11
+ for (let i = 0; i < str.length; i++) {
12
+ const c = str.charCodeAt(i);
13
+ // XOR with byte
14
+ h0 ^= c;
15
+ // Multiply by FNV prime 0x00000100000001B3
16
+ // Using schoolbook multiplication for two 32-bit halves
17
+ const lo = Math.imul(h0, 0x01b3) >>> 0;
18
+ const hi = (Math.imul(h1, 0x01b3) + Math.imul(h0, 0x0100)) >>> 0;
19
+ h0 = lo;
20
+ h1 = hi;
21
+ }
22
+
23
+ return (h1 >>> 0).toString(36) + (h0 >>> 0).toString(36);
24
+ }
@@ -0,0 +1,38 @@
1
+ import { posix } from "../polyfills/path";
2
+
3
+ /**
4
+ * Resolve a URL path safely within a root directory.
5
+ * Strips query/hash, decodes, normalizes, and ensures the result
6
+ * never escapes outside root via .. traversal.
7
+ */
8
+ export function safePath(root: string, urlPath: string): string {
9
+ // Strip query string and hash
10
+ let p = urlPath.split("?")[0].split("#")[0];
11
+
12
+ // Decode percent-encoded characters
13
+ try {
14
+ p = decodeURIComponent(p);
15
+ } catch {
16
+ /* keep as-is */
17
+ }
18
+
19
+ // Ensure leading slash
20
+ if (!p.startsWith("/")) p = "/" + p;
21
+
22
+ // Normalize (resolves .., ., double slashes)
23
+ p = posix.normalize(p);
24
+
25
+ // Join with root
26
+ const full = root === "/" ? p : posix.normalize(root + "/" + p);
27
+
28
+ // Verify the result starts with root
29
+ const normalRoot = root === "/" ? "/" : posix.normalize(root);
30
+ if (
31
+ !full.startsWith(normalRoot === "/" ? "/" : normalRoot + "/") &&
32
+ full !== normalRoot
33
+ ) {
34
+ return normalRoot;
35
+ }
36
+
37
+ return full;
38
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Worker runtime configuration.
3
+ *
4
+ * When `worker` mode is enabled, CPU-intensive operations (transpilation via
5
+ * esbuild-wasm) are offloaded to a Web Worker. This prevents UI freezes
6
+ * during TypeScript/JSX compilation.
7
+ *
8
+ * The `worker` option on Container controls this:
9
+ * - `false` (default) — everything runs on the main thread
10
+ * - `true` — enable worker-based transpilation
11
+ * - `'auto'` — use workers when `Worker` is available (browser), skip in Node.js
12
+ *
13
+ * Full kernel isolation (executing `require()` and `new Function()` in a
14
+ * worker) is planned for a future release and requires SharedArrayBuffer
15
+ * for synchronous VFS access from the worker.
16
+ *
17
+ * @module
18
+ */
19
+
20
+ export type WorkerMode = boolean | "auto";
21
+
22
+ /**
23
+ * Determine whether to use worker-based transpilation.
24
+ * Returns `true` if workers should be enabled based on the mode
25
+ * and the current runtime environment.
26
+ */
27
+ export function shouldUseWorker(mode: WorkerMode): boolean {
28
+ if (mode === false) return false;
29
+ if (mode === true) return true;
30
+ // 'auto' — use workers in browser environments where Worker is available
31
+ return typeof Worker !== "undefined" && typeof window !== "undefined";
32
+ }
33
+
34
+ /**
35
+ * Configuration for the worker runtime.
36
+ * Currently only affects transpilation; future versions will support
37
+ * full kernel isolation.
38
+ */
39
+ export interface WorkerRuntimeConfig {
40
+ /** Whether to use Web Workers for transpilation. */
41
+ useWorker: boolean;
42
+ }