@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
package/src/metrics.ts ADDED
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Performance metrics for jiki containers.
3
+ *
4
+ * Tracks timing and counts for key operations: module resolution,
5
+ * transpilation, file I/O, cache hits/misses, and package installs.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const container = boot();
10
+ * // ... do work ...
11
+ * const m = container.metrics.snapshot();
12
+ * console.log(`Resolved ${m.resolveCount} modules in ${m.resolveTimeMs}ms`);
13
+ * ```
14
+ */
15
+
16
+ export interface MetricsSnapshot {
17
+ /** Total module resolutions performed. */
18
+ resolveCount: number;
19
+ /** Total time spent in module resolution (ms). */
20
+ resolveTimeMs: number;
21
+ /** Total transpilation calls. */
22
+ transpileCount: number;
23
+ /** Total time spent in transpilation (ms). */
24
+ transpileTimeMs: number;
25
+ /** Total VFS read operations. */
26
+ vfsReadCount: number;
27
+ /** Total VFS write operations. */
28
+ vfsWriteCount: number;
29
+ /** Module cache hits. */
30
+ cacheHits: number;
31
+ /** Module cache misses. */
32
+ cacheMisses: number;
33
+ /** Cache hit rate (0–1). */
34
+ cacheHitRate: number;
35
+ /** Total shell commands executed. */
36
+ commandCount: number;
37
+ /** Total packages installed. */
38
+ installCount: number;
39
+ /** Total time spent installing packages (ms). */
40
+ installTimeMs: number;
41
+ /** Timestamp of when metrics collection started. */
42
+ startedAt: number;
43
+ /** Elapsed time since metrics collection started (ms). */
44
+ uptimeMs: number;
45
+ }
46
+
47
+ export class Metrics {
48
+ private _resolveCount = 0;
49
+ private _resolveTimeMs = 0;
50
+ private _transpileCount = 0;
51
+ private _transpileTimeMs = 0;
52
+ private _vfsReadCount = 0;
53
+ private _vfsWriteCount = 0;
54
+ private _cacheHits = 0;
55
+ private _cacheMisses = 0;
56
+ private _commandCount = 0;
57
+ private _installCount = 0;
58
+ private _installTimeMs = 0;
59
+ private _startedAt = Date.now();
60
+
61
+ /** Record a module resolution. */
62
+ trackResolve(durationMs: number): void {
63
+ this._resolveCount++;
64
+ this._resolveTimeMs += durationMs;
65
+ }
66
+
67
+ /** Record a transpilation. */
68
+ trackTranspile(durationMs: number): void {
69
+ this._transpileCount++;
70
+ this._transpileTimeMs += durationMs;
71
+ }
72
+
73
+ /** Record a VFS read. */
74
+ trackRead(): void {
75
+ this._vfsReadCount++;
76
+ }
77
+
78
+ /** Record a VFS write. */
79
+ trackWrite(): void {
80
+ this._vfsWriteCount++;
81
+ }
82
+
83
+ /** Record a cache hit. */
84
+ trackCacheHit(): void {
85
+ this._cacheHits++;
86
+ }
87
+
88
+ /** Record a cache miss. */
89
+ trackCacheMiss(): void {
90
+ this._cacheMisses++;
91
+ }
92
+
93
+ /** Record a shell command execution. */
94
+ trackCommand(): void {
95
+ this._commandCount++;
96
+ }
97
+
98
+ /** Record a package install. */
99
+ trackInstall(durationMs: number): void {
100
+ this._installCount++;
101
+ this._installTimeMs += durationMs;
102
+ }
103
+
104
+ /** Get a snapshot of all metrics. */
105
+ snapshot(): MetricsSnapshot {
106
+ const total = this._cacheHits + this._cacheMisses;
107
+ return {
108
+ resolveCount: this._resolveCount,
109
+ resolveTimeMs: Math.round(this._resolveTimeMs * 100) / 100,
110
+ transpileCount: this._transpileCount,
111
+ transpileTimeMs: Math.round(this._transpileTimeMs * 100) / 100,
112
+ vfsReadCount: this._vfsReadCount,
113
+ vfsWriteCount: this._vfsWriteCount,
114
+ cacheHits: this._cacheHits,
115
+ cacheMisses: this._cacheMisses,
116
+ cacheHitRate: total > 0 ? this._cacheHits / total : 0,
117
+ commandCount: this._commandCount,
118
+ installCount: this._installCount,
119
+ installTimeMs: Math.round(this._installTimeMs * 100) / 100,
120
+ startedAt: this._startedAt,
121
+ uptimeMs: Date.now() - this._startedAt,
122
+ };
123
+ }
124
+
125
+ /** Reset all metrics. */
126
+ reset(): void {
127
+ this._resolveCount = 0;
128
+ this._resolveTimeMs = 0;
129
+ this._transpileCount = 0;
130
+ this._transpileTimeMs = 0;
131
+ this._vfsReadCount = 0;
132
+ this._vfsWriteCount = 0;
133
+ this._cacheHits = 0;
134
+ this._cacheMisses = 0;
135
+ this._commandCount = 0;
136
+ this._installCount = 0;
137
+ this._installTimeMs = 0;
138
+ this._startedAt = Date.now();
139
+ }
140
+ }
@@ -0,0 +1,511 @@
1
+ import { MemFS } from "./memfs";
2
+ import type { PackageJson } from "./types/package-json";
3
+ import type { FsShim } from "./polyfills/fs";
4
+ import type { Process } from "./polyfills/process";
5
+ import * as pathShim from "./polyfills/path";
6
+ import { resolveBuiltin, isBuiltinModule } from "./builtins";
7
+ import { getProcessedSource } from "./code-transform";
8
+ import {
9
+ wrapDynamicImport,
10
+ buildConsoleProxy,
11
+ buildModuleWrapper,
12
+ } from "./runtime-helpers";
13
+ import {
14
+ resolve as resolveExports,
15
+ imports as resolveImports,
16
+ } from "resolve.exports";
17
+ import type { PluginRegistry } from "./plugin";
18
+
19
+ export interface Module {
20
+ id: string;
21
+ filename: string;
22
+ exports: unknown;
23
+ loaded: boolean;
24
+ children: Module[];
25
+ paths: string[];
26
+ }
27
+
28
+ export interface AutoInstallProvider {
29
+ installSync(name: string): void;
30
+ }
31
+
32
+ export interface RuntimeOptions {
33
+ cwd?: string;
34
+ env?: Record<string, string>;
35
+ onConsole?: (method: string, args: unknown[]) => void;
36
+ onStdout?: (data: string) => void;
37
+ onStderr?: (data: string) => void;
38
+ autoInstall?: boolean;
39
+ autoInstallProvider?: AutoInstallProvider;
40
+ /** Enable inline source maps for TypeScript/JSX transpilation. */
41
+ sourceMaps?: boolean;
42
+ }
43
+
44
+ export interface RequireFunction {
45
+ (id: string): unknown;
46
+ resolve: ((id: string) => string) & {
47
+ paths: (id: string) => string[] | null;
48
+ };
49
+ cache: Record<string, Module>;
50
+ }
51
+
52
+ const FILE_EXTENSIONS = [
53
+ ".js",
54
+ ".ts",
55
+ ".tsx",
56
+ ".jsx",
57
+ ".json",
58
+ ".node",
59
+ ".mjs",
60
+ ".cjs",
61
+ ];
62
+ const INDEX_NAMES = [
63
+ "index.js",
64
+ "index.ts",
65
+ "index.tsx",
66
+ "index.json",
67
+ "index.node",
68
+ ];
69
+ const ENTRY_FIELDS = ["main", "module"] as const;
70
+
71
+ export class ModuleResolver {
72
+ private vfs: MemFS;
73
+ private fsShim: FsShim;
74
+ private proc: Process;
75
+ private opts: RuntimeOptions;
76
+ private cache: Record<string, Module>;
77
+ private transformedCache: Map<string, string>;
78
+ private debug: boolean;
79
+ private pluginRegistry?: PluginRegistry;
80
+
81
+ private pkgJsonMemo = new Map<string, PackageJson | null>();
82
+ private resolveMemo = new Map<string, string | null>();
83
+
84
+ clearMemos(): void {
85
+ this.pkgJsonMemo.clear();
86
+ this.resolveMemo.clear();
87
+ }
88
+
89
+ /** Invalidate cached resolution entries that match the given file path.
90
+ * This enables per-entry cache invalidation when a file changes on disk. */
91
+ invalidate(path: string): void {
92
+ // Remove any resolveMemo entries whose resolved value matches this path
93
+ for (const [key, value] of this.resolveMemo) {
94
+ if (value === path) this.resolveMemo.delete(key);
95
+ }
96
+ // Remove the module from the require cache so it gets re-evaluated
97
+ if (this.cache[path]) delete this.cache[path];
98
+ // Remove any pkgJsonMemo that matches this path (e.g. if a package.json was edited)
99
+ if (this.pkgJsonMemo.has(path)) this.pkgJsonMemo.delete(path);
100
+ // Remove transformed source cache
101
+ if (this.transformedCache.has(path)) this.transformedCache.delete(path);
102
+ }
103
+
104
+ constructor(
105
+ vfs: MemFS,
106
+ fsShim: FsShim,
107
+ proc: Process,
108
+ cache: Record<string, Module>,
109
+ opts: RuntimeOptions,
110
+ transformedCache: Map<string, string>,
111
+ pluginRegistry?: PluginRegistry,
112
+ ) {
113
+ this.vfs = vfs;
114
+ this.fsShim = fsShim;
115
+ this.proc = proc;
116
+ this.cache = cache;
117
+ this.opts = opts;
118
+ this.transformedCache = transformedCache;
119
+ this.debug = !!(opts.env && opts.env.DEBUG_RESOLVER);
120
+ this.pluginRegistry = pluginRegistry;
121
+ }
122
+
123
+ makeRequire(fromDir: string): RequireFunction {
124
+ const self = this;
125
+
126
+ const fn = ((id: string): unknown => {
127
+ const resolved = self.resolve(id, fromDir);
128
+ return self.load(resolved).exports;
129
+ }) as RequireFunction;
130
+
131
+ const resolveFn = (id: string): string => self.resolve(id, fromDir);
132
+ (resolveFn as any).paths = (id: string): string[] | null => {
133
+ const bare = id.startsWith("node:") ? id.slice(5) : id;
134
+ if (isBuiltinModule(bare)) return null;
135
+ const dirs: string[] = [];
136
+ let d = fromDir;
137
+ while (d !== "/") {
138
+ dirs.push(pathShim.join(d, "node_modules"));
139
+ d = pathShim.dirname(d);
140
+ }
141
+ dirs.push("/node_modules");
142
+ return dirs;
143
+ };
144
+ fn.resolve = resolveFn as RequireFunction["resolve"];
145
+ fn.cache = this.cache;
146
+ return fn;
147
+ }
148
+
149
+ resolve(id: string, fromDir: string): string {
150
+ if (id.startsWith("node:")) id = id.slice(5);
151
+ if (isBuiltinModule(id)) return id;
152
+
153
+ // --- Plugin onResolve hooks (first match wins) ---
154
+ if (this.pluginRegistry) {
155
+ const pluginResult = this.pluginRegistry.runResolve(id, fromDir);
156
+ if (pluginResult && pluginResult.path) return pluginResult.path;
157
+ }
158
+
159
+ if (id.startsWith("#")) return this.resolvePackageImport(id, fromDir);
160
+
161
+ const memoKey = `${fromDir}|${id}`;
162
+ const memoized = this.resolveMemo.get(memoKey);
163
+ if (memoized !== undefined) {
164
+ if (memoized === null) throw new Error(`Cannot find module '${id}'`);
165
+ return memoized;
166
+ }
167
+
168
+ let found: string | null = null;
169
+
170
+ if (
171
+ id === "." ||
172
+ id === ".." ||
173
+ id.startsWith("./") ||
174
+ id.startsWith("../") ||
175
+ id.startsWith("/")
176
+ ) {
177
+ const abs = id.startsWith("/") ? id : pathShim.resolve(fromDir, id);
178
+ found = this.probeFile(abs);
179
+ if (!found) {
180
+ this.resolveMemo.set(memoKey, null);
181
+ throw new Error(`Cannot find module '${id}' from '${fromDir}'`);
182
+ }
183
+ } else {
184
+ if (id === "rollup" || id.startsWith("rollup/")) return "rollup";
185
+ found = this.resolveFromNodeModules(id, fromDir);
186
+ if (!found && this.opts.autoInstall && this.opts.autoInstallProvider) {
187
+ const pkgName = this.extractPackageName(id);
188
+ try {
189
+ this.opts.autoInstallProvider.installSync(pkgName);
190
+ this.resolveMemo.delete(memoKey);
191
+ found = this.resolveFromNodeModules(id, fromDir);
192
+ } catch (e) {
193
+ if (this.debug) {
194
+ console.warn(
195
+ `[jiki:resolver] auto-install failed for '${pkgName}': ${(e as Error).message}`,
196
+ );
197
+ }
198
+ }
199
+ }
200
+ if (!found) {
201
+ this.resolveMemo.set(memoKey, null);
202
+ throw new Error(`Cannot find module '${id}' from '${fromDir}'`);
203
+ }
204
+ }
205
+
206
+ this.resolveMemo.set(memoKey, found);
207
+ return found;
208
+ }
209
+
210
+ load(resolved: string): Module {
211
+ if (this.cache[resolved]) return this.cache[resolved];
212
+
213
+ const builtin = resolveBuiltin(resolved);
214
+ if (builtin !== undefined) return this.cacheAndReturn(resolved, builtin);
215
+
216
+ if (resolved === "fs" || resolved === "fs/promises") {
217
+ const exp =
218
+ resolved === "fs/promises" ? this.fsShim.promises : this.fsShim;
219
+ return this.cacheAndReturn(resolved, exp);
220
+ }
221
+ if (resolved === "process") {
222
+ return this.cacheAndReturn("process", this.proc);
223
+ }
224
+
225
+ // --- Plugin onLoad hooks (first match wins) ---
226
+ if (this.pluginRegistry) {
227
+ const pluginResult = this.pluginRegistry.runLoad(resolved);
228
+ if (pluginResult && pluginResult.contents !== undefined) {
229
+ const entry: Module = {
230
+ id: resolved,
231
+ filename: resolved,
232
+ exports: {},
233
+ loaded: false,
234
+ children: [],
235
+ paths: [pathShim.dirname(resolved)],
236
+ };
237
+ this.cache[resolved] = entry;
238
+
239
+ try {
240
+ let source = getProcessedSource(
241
+ pluginResult.contents,
242
+ resolved,
243
+ this.transformedCache,
244
+ this.opts.sourceMaps,
245
+ );
246
+ // Run plugin transform pipeline on the loaded source.
247
+ source = this.pluginRegistry.runTransform(resolved, source);
248
+ const dir = pathShim.dirname(resolved);
249
+ const childRequire = this.makeRequire(dir);
250
+ const meta = {
251
+ url: `file://${resolved}`,
252
+ dirname: dir,
253
+ filename: resolved,
254
+ };
255
+ const dynImport = wrapDynamicImport(childRequire);
256
+ const consoleFacade = buildConsoleProxy(this.opts.onConsole);
257
+ const wrapper = buildModuleWrapper(source, resolved);
258
+
259
+ wrapper(
260
+ entry.exports,
261
+ childRequire,
262
+ entry,
263
+ resolved,
264
+ dir,
265
+ this.proc,
266
+ consoleFacade,
267
+ meta,
268
+ dynImport,
269
+ );
270
+ entry.loaded = true;
271
+ return entry;
272
+ } catch (err) {
273
+ delete this.cache[resolved];
274
+ throw new Error(
275
+ `Error loading module '${resolved}': ${(err as Error).message}`,
276
+ );
277
+ }
278
+ }
279
+ }
280
+
281
+ const entry: Module = {
282
+ id: resolved,
283
+ filename: resolved,
284
+ exports: {},
285
+ loaded: false,
286
+ children: [],
287
+ paths: [pathShim.dirname(resolved)],
288
+ };
289
+ this.cache[resolved] = entry;
290
+
291
+ try {
292
+ if (resolved.endsWith(".json")) {
293
+ entry.exports = JSON.parse(this.vfs.readFileSync(resolved, "utf8"));
294
+ entry.loaded = true;
295
+ return entry;
296
+ }
297
+
298
+ const rawSource = this.vfs.readFileSync(resolved, "utf8");
299
+ let source = getProcessedSource(
300
+ rawSource,
301
+ resolved,
302
+ this.transformedCache,
303
+ this.opts.sourceMaps,
304
+ );
305
+ // Run plugin transform pipeline on VFS-loaded source.
306
+ if (this.pluginRegistry) {
307
+ source = this.pluginRegistry.runTransform(resolved, source);
308
+ }
309
+
310
+ const dir = pathShim.dirname(resolved);
311
+ const childRequire = this.makeRequire(dir);
312
+ const meta = {
313
+ url: `file://${resolved}`,
314
+ dirname: dir,
315
+ filename: resolved,
316
+ };
317
+ const dynImport = wrapDynamicImport(childRequire);
318
+ const consoleFacade = buildConsoleProxy(this.opts.onConsole);
319
+ const wrapper = buildModuleWrapper(source, resolved);
320
+
321
+ wrapper(
322
+ entry.exports,
323
+ childRequire,
324
+ entry,
325
+ resolved,
326
+ dir,
327
+ this.proc,
328
+ consoleFacade,
329
+ meta,
330
+ dynImport,
331
+ );
332
+
333
+ entry.loaded = true;
334
+ return entry;
335
+ } catch (err) {
336
+ delete this.cache[resolved];
337
+ throw new Error(
338
+ `Error loading module '${resolved}': ${(err as Error).message}`,
339
+ );
340
+ }
341
+ }
342
+
343
+ private cacheAndReturn(id: string, exports: unknown): Module {
344
+ const m: Module = {
345
+ id,
346
+ filename: id,
347
+ exports,
348
+ loaded: true,
349
+ children: [],
350
+ paths: [],
351
+ };
352
+ this.cache[id] = m;
353
+ return m;
354
+ }
355
+
356
+ private readPkg(pkgPath: string): PackageJson | null {
357
+ if (this.pkgJsonMemo.has(pkgPath)) return this.pkgJsonMemo.get(pkgPath)!;
358
+ try {
359
+ const data = JSON.parse(
360
+ this.vfs.readFileSync(pkgPath, "utf8"),
361
+ ) as PackageJson;
362
+ this.pkgJsonMemo.set(pkgPath, data);
363
+ return data;
364
+ } catch {
365
+ this.pkgJsonMemo.set(pkgPath, null);
366
+ return null;
367
+ }
368
+ }
369
+
370
+ probeFile(base: string): string | null {
371
+ if (this.vfs.existsSync(base)) {
372
+ const st = this.vfs.statSync(base);
373
+ if (st.isFile()) return base;
374
+ if (st.isDirectory()) {
375
+ const pkg = this.readPkg(pathShim.join(base, "package.json"));
376
+ if (pkg) {
377
+ for (const field of ENTRY_FIELDS) {
378
+ const val = (pkg as Record<string, unknown>)[field];
379
+ if (typeof val === "string") {
380
+ const hit = this.probeFile(pathShim.join(base, val));
381
+ if (hit) return hit;
382
+ }
383
+ }
384
+ }
385
+ for (const idx of INDEX_NAMES) {
386
+ const p = pathShim.join(base, idx);
387
+ if (this.vfs.existsSync(p)) return p;
388
+ }
389
+ }
390
+ }
391
+ for (const ext of FILE_EXTENSIONS) {
392
+ const p = base + ext;
393
+ if (this.vfs.existsSync(p)) return p;
394
+ }
395
+ return null;
396
+ }
397
+
398
+ private resolvePackageImport(id: string, fromDir: string): string {
399
+ let dir = fromDir;
400
+ while (dir !== "/") {
401
+ const pkg = this.readPkg(pathShim.join(dir, "package.json"));
402
+ if (pkg?.imports) {
403
+ try {
404
+ const resolved = resolveImports(pkg, id, { require: true });
405
+ if (resolved && resolved.length > 0) {
406
+ const full = pathShim.join(dir, resolved[0]);
407
+ if (this.vfs.existsSync(full)) return full;
408
+ }
409
+ } catch (e) {
410
+ if (this.debug) {
411
+ console.warn(
412
+ `[jiki:resolver] imports resolution failed for '${id}' in ${dir}: ${(e as Error).message}`,
413
+ );
414
+ }
415
+ }
416
+ }
417
+ dir = pathShim.dirname(dir);
418
+ }
419
+ throw new Error(`Cannot find module '${id}'`);
420
+ }
421
+
422
+ private extractPackageName(id: string): string {
423
+ const segments = id.split("/");
424
+ if (segments[0].startsWith("@") && segments.length > 1) {
425
+ return `${segments[0]}/${segments[1]}`;
426
+ }
427
+ return segments[0];
428
+ }
429
+
430
+ private resolveFromNodeModules(id: string, fromDir: string): string | null {
431
+ const segments = id.split("/");
432
+ const pkgName =
433
+ segments[0].startsWith("@") && segments.length > 1
434
+ ? `${segments[0]}/${segments[1]}`
435
+ : segments[0];
436
+ const subpath = segments.slice(pkgName.split("/").length).join("/");
437
+
438
+ let dir = fromDir;
439
+ while (true) {
440
+ const nm = pathShim.join(dir, "node_modules");
441
+ if (this.vfs.existsSync(nm)) {
442
+ const pkgRoot = pathShim.join(nm, pkgName);
443
+ const hit = this.tryPackageEntry(pkgRoot, pkgName, subpath);
444
+ if (hit) return hit;
445
+ }
446
+ if (dir === "/") break;
447
+ dir = pathShim.dirname(dir);
448
+ }
449
+ return null;
450
+ }
451
+
452
+ private tryPackageEntry(
453
+ pkgRoot: string,
454
+ pkgName: string,
455
+ subpath: string,
456
+ ): string | null {
457
+ const pkg = this.readPkg(pathShim.join(pkgRoot, "package.json"));
458
+
459
+ if (pkg) {
460
+ if (pkg.exports) {
461
+ const target = subpath ? `${pkgName}/${subpath}` : pkgName;
462
+ for (const cond of [
463
+ { require: true }, // node + require + default (auto)
464
+ { import: true }, // node + import + default (auto)
465
+ { browser: true, require: true }, // browser + require + default
466
+ { browser: true, import: true }, // browser + import + default
467
+ { require: true, conditions: ["production"] }, // production builds
468
+ ] as const) {
469
+ try {
470
+ const resolved = resolveExports(pkg, target, cond);
471
+ if (resolved?.length) {
472
+ const hit = this.probeFile(pathShim.join(pkgRoot, resolved[0]));
473
+ if (hit) return hit;
474
+ }
475
+ } catch (e) {
476
+ if (this.debug) {
477
+ console.warn(
478
+ `[jiki:resolver] exports resolution failed for '${target}' in ${pkgRoot} with conditions ${JSON.stringify(cond)}: ${(e as Error).message}`,
479
+ );
480
+ }
481
+ }
482
+ }
483
+ // If exports is defined, do NOT fall through to main/module/browser
484
+ // per Node.js module resolution spec
485
+ return null;
486
+ }
487
+
488
+ if (subpath) {
489
+ const hit = this.probeFile(pathShim.join(pkgRoot, subpath));
490
+ if (hit) return hit;
491
+ }
492
+
493
+ if (typeof pkg.browser === "string") {
494
+ const hit = this.probeFile(pathShim.join(pkgRoot, pkg.browser));
495
+ if (hit) return hit;
496
+ }
497
+
498
+ if (pkg.main) {
499
+ const hit = this.probeFile(pathShim.join(pkgRoot, pkg.main));
500
+ if (hit) return hit;
501
+ }
502
+
503
+ if (pkg.module) {
504
+ const hit = this.probeFile(pathShim.join(pkgRoot, pkg.module));
505
+ if (hit) return hit;
506
+ }
507
+ }
508
+
509
+ return this.probeFile(subpath ? pathShim.join(pkgRoot, subpath) : pkgRoot);
510
+ }
511
+ }