@nubjs/nub-win32-x64 0.0.13 → 0.0.15

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 (58) hide show
  1. package/bin/nub.exe +0 -0
  2. package/package.json +1 -1
  3. package/runtime/addons/nub-native.node +0 -0
  4. package/runtime/polyfills.cjs +178 -0
  5. package/runtime/preload-async-hooks.mjs +5 -6
  6. package/runtime/preload-common.cjs +548 -0
  7. package/runtime/preload.cjs +277 -0
  8. package/runtime/preload.mjs +22 -11
  9. package/runtime/transform-core.mjs +98 -199
  10. package/runtime/version.mjs +1 -1
  11. package/runtime/worker-polyfill.mjs +15 -2
  12. package/runtime/node_modules/@oxc-parser/binding-win32-x64-msvc/README.md +0 -3
  13. package/runtime/node_modules/@oxc-parser/binding-win32-x64-msvc/package.json +0 -39
  14. package/runtime/node_modules/@oxc-parser/binding-win32-x64-msvc/parser.win32-x64-msvc.node +0 -0
  15. package/runtime/node_modules/@oxc-project/types/LICENSE +0 -22
  16. package/runtime/node_modules/@oxc-project/types/README.md +0 -3
  17. package/runtime/node_modules/@oxc-project/types/package.json +0 -26
  18. package/runtime/node_modules/@oxc-project/types/types.d.ts +0 -1912
  19. package/runtime/node_modules/@oxc-transform/binding-win32-x64-msvc/README.md +0 -3
  20. package/runtime/node_modules/@oxc-transform/binding-win32-x64-msvc/package.json +0 -41
  21. package/runtime/node_modules/@oxc-transform/binding-win32-x64-msvc/transform.win32-x64-msvc.node +0 -0
  22. package/runtime/node_modules/oxc-parser/LICENSE +0 -22
  23. package/runtime/node_modules/oxc-parser/README.md +0 -167
  24. package/runtime/node_modules/oxc-parser/package.json +0 -153
  25. package/runtime/node_modules/oxc-parser/src-js/bindings.js +0 -601
  26. package/runtime/node_modules/oxc-parser/src-js/generated/constants.js +0 -105
  27. package/runtime/node_modules/oxc-parser/src-js/generated/deserialize/js.js +0 -5862
  28. package/runtime/node_modules/oxc-parser/src-js/generated/deserialize/js_range.js +0 -6403
  29. package/runtime/node_modules/oxc-parser/src-js/generated/deserialize/ts.js +0 -6154
  30. package/runtime/node_modules/oxc-parser/src-js/generated/deserialize/ts_range.js +0 -6723
  31. package/runtime/node_modules/oxc-parser/src-js/generated/lazy/constructors.js +0 -13875
  32. package/runtime/node_modules/oxc-parser/src-js/generated/lazy/type_ids.js +0 -191
  33. package/runtime/node_modules/oxc-parser/src-js/generated/lazy/walk.js +0 -5810
  34. package/runtime/node_modules/oxc-parser/src-js/generated/visit/keys.js +0 -220
  35. package/runtime/node_modules/oxc-parser/src-js/generated/visit/type_ids.js +0 -177
  36. package/runtime/node_modules/oxc-parser/src-js/generated/visit/visitor.d.ts +0 -387
  37. package/runtime/node_modules/oxc-parser/src-js/generated/visit/walk.js +0 -2455
  38. package/runtime/node_modules/oxc-parser/src-js/index.d.ts +0 -312
  39. package/runtime/node_modules/oxc-parser/src-js/index.js +0 -108
  40. package/runtime/node_modules/oxc-parser/src-js/raw-transfer/common.js +0 -301
  41. package/runtime/node_modules/oxc-parser/src-js/raw-transfer/eager.js +0 -255
  42. package/runtime/node_modules/oxc-parser/src-js/raw-transfer/lazy-common.js +0 -11
  43. package/runtime/node_modules/oxc-parser/src-js/raw-transfer/lazy.js +0 -162
  44. package/runtime/node_modules/oxc-parser/src-js/raw-transfer/node-array.js +0 -365
  45. package/runtime/node_modules/oxc-parser/src-js/raw-transfer/supported.js +0 -52
  46. package/runtime/node_modules/oxc-parser/src-js/raw-transfer/visitor.js +0 -127
  47. package/runtime/node_modules/oxc-parser/src-js/visit/index.js +0 -41
  48. package/runtime/node_modules/oxc-parser/src-js/visit/visitor.js +0 -405
  49. package/runtime/node_modules/oxc-parser/src-js/wasm.js +0 -13
  50. package/runtime/node_modules/oxc-parser/src-js/webcontainer-fallback.cjs +0 -21
  51. package/runtime/node_modules/oxc-parser/src-js/wrap.js +0 -57
  52. package/runtime/node_modules/oxc-transform/LICENSE +0 -22
  53. package/runtime/node_modules/oxc-transform/README.md +0 -84
  54. package/runtime/node_modules/oxc-transform/browser.js +0 -1
  55. package/runtime/node_modules/oxc-transform/index.d.ts +0 -658
  56. package/runtime/node_modules/oxc-transform/index.js +0 -598
  57. package/runtime/node_modules/oxc-transform/package.json +0 -114
  58. package/runtime/node_modules/oxc-transform/webcontainer-fallback.cjs +0 -21
@@ -0,0 +1,548 @@
1
+ // Shared preload machinery for BOTH tiers — CommonJS, zero top-level await.
2
+ //
3
+ // The fast tier (Node 22.15+) loads this from a `--require` CJS preload
4
+ // (preload.cjs) so Node keeps its synchronous `Module.runMain` CJS entry path
5
+ // (top-level `executionAsyncId()===1`, sync exception origin, `require.main.id`
6
+ // `'.'`, `module.parent` `null`) — all of which the old `--import` ESM preload
7
+ // broke by forcing eager ESM-loader init that routed even a CJS entry through the
8
+ // async ESM module-job (R1). The compat tier (18.19–22.14) loads this from its
9
+ // async `--import` preload.mjs and reuses the same hook/require/watch/Temporal
10
+ // logic; only hook REGISTRATION differs (sync `module.registerHooks` on the fast
11
+ // tier vs async `module.register` loader worker on compat), which each entry owns.
12
+ //
13
+ // EVERYTHING here is synchronous and import-of-transform-core is a plain
14
+ // `require()` — transform-core.mjs has no top-level await and is require(esm)-able
15
+ // on the fast tier; the compat entry passes its already-imported core bindings in
16
+ // (it imported them as ESM), so this module never require()s the core there.
17
+
18
+ const module_ = require("node:module");
19
+ const { readdirSync } = require("node:fs");
20
+ const { fileURLToPath, pathToFileURL } = require("node:url");
21
+ const { join, dirname, extname: pathExtname } = require("node:path");
22
+
23
+ // ── Watch-mode dependency reporting (main thread only) ──────────────
24
+ // Under `nub watch`, Node's FilesWatcher only watches files in the import graph;
25
+ // config files (tsconfig.json, package.json) and `.env*` are NOT in any graph, so
26
+ // an edit to them otherwise goes stale. Node accepts incremental
27
+ // `process.send({'watch:require': [...]})` over its WATCH_REPORT_DEPENDENCIES IPC
28
+ // at ANY point in the child's life (it adds each path to the watch set), so we
29
+ // report config paths AS the core loader discovers them. The reporters are
30
+ // injected into the core via setWatchHooks so getTsconfigForDir / getPackageType
31
+ // self-report. The flush is coalesced via setImmediate.
32
+ function installWatchReporting(core) {
33
+ const WATCH_REPORTING =
34
+ process.env.WATCH_REPORT_DEPENDENCIES === "1" && typeof process.send === "function";
35
+ const watchReported = new Set();
36
+ const watchPending = [];
37
+ let watchFlushScheduled = false;
38
+ function flushWatchDeps() {
39
+ watchFlushScheduled = false;
40
+ if (watchPending.length === 0) return;
41
+ const batch = watchPending.splice(0, watchPending.length);
42
+ try { process.send({ "watch:require": batch }); } catch {}
43
+ }
44
+ function reportWatchDep(path) {
45
+ if (!WATCH_REPORTING || !path || watchReported.has(path)) return;
46
+ watchReported.add(path);
47
+ watchPending.push(path);
48
+ if (!watchFlushScheduled) {
49
+ watchFlushScheduled = true;
50
+ // A scheduled immediate is drained before the loop would exit, so even a
51
+ // script that finishes synchronously flushes its deps. (Don't unref: an
52
+ // unref'd immediate is skipped on a synchronous exit, dropping the report.)
53
+ setImmediate(flushWatchDeps);
54
+ }
55
+ }
56
+ // Report a directory's `.env*` files (the natural watch targets). Scanned once
57
+ // per directory, lazily.
58
+ const watchEnvScannedDirs = new Set();
59
+ function reportEnvFilesIn(dir) {
60
+ if (!WATCH_REPORTING || watchEnvScannedDirs.has(dir)) return;
61
+ watchEnvScannedDirs.add(dir);
62
+ let entries;
63
+ try { entries = readdirSync(dir); } catch { return; }
64
+ for (const name of entries) {
65
+ if (name === ".env" || name.startsWith(".env.")) reportWatchDep(join(dir, name));
66
+ }
67
+ }
68
+ core.setWatchHooks({ reportDep: reportWatchDep, reportEnvDir: reportEnvFilesIn });
69
+ return WATCH_REPORTING;
70
+ }
71
+
72
+ // ── Resolve / load hooks (sync `module.registerHooks` shape) ────────
73
+ // Returns `{ resolve, load }` closing over `core` + the watch flag. The compat
74
+ // tier does NOT use these (its hooks run async in the loader worker via
75
+ // preload-async-hooks.mjs); only the fast tier's `module.registerHooks` does.
76
+
77
+ // True once USER code registers its own `module.registerHooks` (a ts-node/tsx-style
78
+ // transpiler). nub registers exactly one hook set from the preload (the FIRST call
79
+ // after the wrap below); every later call is the user's. This lets the load hook
80
+ // tell apart a bare `'typescript'` format that a USER resolve hook set (defer — the
81
+ // user's own load hook will transpile) from the bare `'typescript'` that Node's
82
+ // NATIVE CJS loader assigns to a `.ts` entry/require in a package with no explicit
83
+ // `type` (transpile — there is no user hook to do it, and Node's strip-only mode
84
+ // can't handle enums/namespaces). See makeHooks().load.
85
+ let __userHooksRegistered = false;
86
+ function installUserHookDetector() {
87
+ if (typeof module_.registerHooks !== "function") return;
88
+ const orig = module_.registerHooks;
89
+ if (orig.__nubWrapped) return;
90
+ let seen = 0;
91
+ const wrapped = function (...args) {
92
+ // Call #1 is nub's own preload registration; #2+ are user hooks.
93
+ if (seen >= 1) __userHooksRegistered = true;
94
+ seen += 1;
95
+ return orig.apply(this, args);
96
+ };
97
+ wrapped.__nubWrapped = true;
98
+ try { module_.registerHooks = wrapped; } catch {}
99
+ }
100
+
101
+ function makeHooks(core, watchReporting) {
102
+ installUserHookDetector();
103
+
104
+ function resolve(specifier, context, nextResolve) {
105
+ const r = core.resolveSpec(specifier, context.parentURL);
106
+ return r ?? nextResolve(specifier, context);
107
+ }
108
+
109
+ function load(url, context, nextLoad) {
110
+ const ext = core.extname(url);
111
+
112
+ // Watch mode: surface this file's nearest config files (tsconfig.json,
113
+ // package.json) + sibling `.env*` so edits to them restart the run. Done for
114
+ // every user file (not just transpiled ones) — getTsconfigForDir/
115
+ // getPackageType self-report via the injected watch hooks.
116
+ if (watchReporting && url.startsWith("file:") && !core.isNodeModules(url)) {
117
+ try {
118
+ const dir = dirname(fileURLToPath(url));
119
+ core.getTsconfigForDir(dir);
120
+ core.getPackageType(dir);
121
+ } catch {}
122
+ }
123
+
124
+ // A USER resolve hook (a ts-node/tsx-style transpiler registered AFTER nub's
125
+ // own preload hook) claimed this file with the bare 'typescript' format: defer
126
+ // to the user's own load chain. The discriminator is `__userHooksRegistered`,
127
+ // NOT the bare format alone — Node's NATIVE CJS loader ALSO emits the bare
128
+ // string 'typescript' for a `.ts` entry/require whose nearest package.json has
129
+ // no explicit `type` (cjs/loader.js getFormatOfExtensionlessFile, lines ~1986),
130
+ // and in that native case nub MUST transpile (Node's strip-only mode can't
131
+ // handle enums/namespaces). So we only step aside when a user hook is present:
132
+ // nub registers exactly one hook set from the preload, the user registers theirs
133
+ // later, and registering theirs OUTERMOST (LIFO) means their load hook wraps
134
+ // nub's — it sets format='typescript', calls nextLoad into nub, and (without this
135
+ // guard) nub would transpile with oxc — a type-stripper, not a module-format
136
+ // transformer — leaving `export {}` verbatim and, for a `type:commonjs` package,
137
+ // handing Node format='commonjs' + ESM source = invalid CJS. Stepping aside lets
138
+ // nub fall through to Node's native load, returning raw TS source back up to the
139
+ // user's outer hook, which does the real ESM->CJS conversion, matching Node.
140
+ // Native 'module-typescript'/'commonjs-typescript' formats still fall through to
141
+ // nub's transpile below, so normal augmentation is unchanged.
142
+ if (__userHooksRegistered && context && context.format === "typescript") {
143
+ return nextLoad(url, context);
144
+ }
145
+
146
+ // R12: never transpile `.ts`/`.tsx`/… inside node_modules. Node itself throws
147
+ // ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING for TS under node_modules; if
148
+ // nub transpiled it instead, that native error would never surface and nub
149
+ // would be MORE permissive than Node. Fall through to `nextLoad` so Node's own
150
+ // handling (and its error) applies. (The TS-parent extensionless resolution in
151
+ // the resolve hook is intended and stays — only this load-time transpile is
152
+ // gated.)
153
+ if (core.TRANSPILE_EXTS.has(ext) && !core.isNodeModules(url)) {
154
+ return core.loadTranspile(url, ext);
155
+ }
156
+ if (ext in core.DATA_EXTS) return core.loadData(url, ext);
157
+
158
+ const r = nextLoad(url, context);
159
+ // nub's sync `module.registerHooks` load hook forces the synchronous
160
+ // module-job (ModuleJobSync.syncLink -> loadAndTranslateForImportInRequiredESM),
161
+ // which cannot async-fetch source. When a user `--experimental-loader` resolve
162
+ // hook sets `format` without a `source` (a pattern vanilla Node tolerates on its
163
+ // async load path by fetching the source itself), the default load returns
164
+ // source:null and Node's assertBufferSource throws ERR_INVALID_RETURN_PROPERTY_VALUE.
165
+ // Backfill the source from disk so the sync path matches Node — without touching
166
+ // nub's own resolve/transpile hooks.
167
+ //
168
+ // EXCEPTION — format 'commonjs' (and 'builtin') MUST keep source:null. For those
169
+ // formats Node's ESM loader deliberately returns no source and hands the module
170
+ // off to the NATIVE CommonJS loader (Module._load), where `require()` uses CJS
171
+ // resolution. A CJS `.js` ENTRY, when a user `--experimental-loader` is active, is
172
+ // routed through the ESM loader for format detection but still loads as CJS this
173
+ // way. If we backfilled its source, the ESM loader would instead translate it via
174
+ // its CommonJS-to-ESM wrapper, routing every inner `require()` through the ESM
175
+ // resolve hook — so `require('assert')` would hand the bare 'assert' specifier to
176
+ // the user's resolve hook and crash with ERR_INVALID_RETURN_PROPERTY_VALUE (the
177
+ // shadow-realm/custom-loaders corpus failure). Only ESM-shaped formats ('module',
178
+ // 'json', 'wasm', …) genuinely need the source on the sync path.
179
+ if (
180
+ r && r.source == null && r.format &&
181
+ r.format !== "commonjs" && r.format !== "builtin" &&
182
+ typeof url === "string" && url.startsWith("file:")
183
+ ) {
184
+ try {
185
+ const { readFileSync } = require("node:fs");
186
+ return { ...r, source: readFileSync(fileURLToPath(url)) };
187
+ } catch { /* fall through with the original result */ }
188
+ }
189
+ return r;
190
+ }
191
+
192
+ return { resolve, load };
193
+ }
194
+
195
+ // ── CommonJS require() augmentation (BOTH tiers) ────────────────────
196
+ // `module.registerHooks`' CJS-`require()` coverage is INCOMPLETE before ~Node 24:
197
+ // on Node 22.15 a `require()` from a `.cts` parent (which Node loads via the ESM
198
+ // translator's special-require) hits native Module._resolveFilename with no
199
+ // tsconfig/extensionless handling — a `require('@alias')` or `require('./x')` of a
200
+ // `.ts` target throws MODULE_NOT_FOUND, while the same code works on Node 26 (where
201
+ // registerHooks does cover it) and on the fast `import` path. On the compat tier
202
+ // (18.19–22.14) the only hook surface is `module.register`, which intercepts the
203
+ // ESM loader ONLY — so `require()` is entirely unaugmented there. Both gaps have
204
+ // the same closure: install this main-thread CJS shim, reusing the core's canonical
205
+ // resolveCjsPath / loadTranspile (no drift). It tries nub's resolution first and
206
+ // FALLS THROUGH to native on a miss, so it is a safe no-op on the versions where
207
+ // registerHooks already covers require (Node 24+/26). Mechanism stays within the
208
+ // augmenter rules: exactly what `--require`-installing the ts-node / tsx CJS shim
209
+ // has always done.
210
+ //
211
+ // This error is surfaced ONLY on Node versions without native require(esm)
212
+ // (< 20.19 / 22.0–22.11), where require() of an ES module genuinely cannot work.
213
+ // On every require(esm)-capable Node, Node loads the ES module itself and this is
214
+ // never reached. The message is user-facing: no internal mechanism names.
215
+ function requireEsmError(filename) {
216
+ const err = new Error(
217
+ `Cannot require() this file — it is an ES module.\n` +
218
+ ` ${filename}\n` +
219
+ `It uses \`import\`/\`export\`, so it loads as an ES module, and this version of ` +
220
+ `Node can't require() an ES module. Load it with \`import(...)\` instead, rename ` +
221
+ `it to .cts for a CommonJS module, or upgrade Node.`,
222
+ );
223
+ err.code = "ERR_REQUIRE_ESM";
224
+ return err;
225
+ }
226
+
227
+ // `withClassicTranspile` — also install the `require.extensions` (classic CommonJS
228
+ // loader) transpile hook. Needed ONLY on Node WITHOUT native require(esm)
229
+ // (< 20.19 / 22.0–22.11): there, `module.register`'s ESM-loader hooks can't reach a
230
+ // `require()`, AND an ES module simply can't be require()d, so we transpile CJS
231
+ // content classically and surface a clean error for ESM content. On require(esm)-
232
+ // capable Node we DON'T install it — registering `require.extensions['.ts']` would
233
+ // shadow Node's own native require(esm) of ES-module `.ts` files (breaking
234
+ // `require("./esm.ts")`), and the resolve shim below plus the tier's load hook
235
+ // already cover resolution + transpile.
236
+ function installCjsRequireHooks(core, withClassicTranspile) {
237
+ const origResolveFilename = module_._resolveFilename;
238
+ module_._resolveFilename = function (request, parent, isMain, options) {
239
+ let resolved = null;
240
+ try {
241
+ const parentPath = parent && typeof parent.filename === "string" ? parent.filename : null;
242
+ resolved = core.resolveCjsPath(request, parentPath);
243
+ } catch { /* fall through to Node */ }
244
+ if (resolved) {
245
+ if (withClassicTranspile && core.requireTargetIsEsm(resolved, pathExtname(resolved))) {
246
+ throw requireEsmError(resolved);
247
+ }
248
+ return resolved;
249
+ }
250
+ return origResolveFilename.call(this, request, parent, isMain, options);
251
+ };
252
+
253
+ if (!withClassicTranspile) return;
254
+
255
+ // require.extensions: transpile via the SAME loadTranspile the load hook uses —
256
+ // target:'es2022' lowering (`using`), tsconfig, source maps, the Stage-3
257
+ // decorator guard, and module-format detection are all identical to the fast
258
+ // tier. The path is already a real TS file (Module._resolveFilename ran first).
259
+ // A module-format source can't be _compile'd as CJS — same clean error as above.
260
+ const transpileExtension = (mod, filename) => {
261
+ const { source, format } = core.loadTranspile(pathToFileURL(filename).href, pathExtname(filename));
262
+ if (format === "module") throw requireEsmError(filename);
263
+ mod._compile(source, filename);
264
+ };
265
+ for (const ext of [".ts", ".cts", ".mts", ".tsx", ".jsx"]) {
266
+ module_._extensions[ext] = transpileExtension;
267
+ }
268
+ }
269
+
270
+ // ── Clobbered-polyfill preloading + Temporal lazy global ────────────
271
+ // Packages in the core's CLOBBER_MAP can't be imported after hooks register
272
+ // because the resolve hook returns a synthetic module instead of the real package.
273
+ // Load them here via CJS require (not yet hooked) and return them so the polyfill
274
+ // installer can stash them. Temporal is the exception (A37): the polyfill is ~18ms
275
+ // to load and most scripts never touch it, so we only RESOLVE its path now (cheap)
276
+ // and defer the load to a lazy global getter. Requiring it later by absolute path
277
+ // bypasses the CLOBBER_MAP resolve-hook entry, which keys on the specifier.
278
+ function preloadPolyfillPackages(reqFromRuntime) {
279
+ const preloaded = {};
280
+ // Feature-detect before requiring (A39): URLPattern is native on Node 24+, so
281
+ // skip loading the polyfill there. On 22.x it's absent → load it.
282
+ if (typeof globalThis.URLPattern === "undefined") {
283
+ try { preloaded.urlpattern = reqFromRuntime("urlpattern-polyfill"); } catch {}
284
+ }
285
+ // Float16Array: native on Node 24+, absent on the 22.x floor.
286
+ if (typeof globalThis.Float16Array === "undefined") {
287
+ try { preloaded.float16 = reqFromRuntime("@petamoriken/float16"); } catch {}
288
+ }
289
+ return preloaded;
290
+ }
291
+
292
+ // Install the lazy `globalThis.Temporal` getter. The polyfill is loaded — and even
293
+ // RESOLVED — only on first access. CRITICAL ordering note (regexp one-off): the
294
+ // `require.resolve("@js-temporal/polyfill")` is deferred INTO the getter, NOT run at
295
+ // preload top level. An unconditional resolve at startup mutates the legacy
296
+ // `RegExp.$_` static (the resolved node_modules path matches an internal regex), so
297
+ // a program inspecting `RegExp.$_` on its first line would otherwise see a leaked
298
+ // path (test-startup-empty-regexp-statics). Deferring the resolve keeps `RegExp.$_`
299
+ // empty at user-code start; the cost is paid only by a program that touches Temporal.
300
+ function installTemporalLazyGlobal(reqFromRuntime) {
301
+ if (typeof globalThis.Temporal !== "undefined") return;
302
+
303
+ const defineTemporal = (value) =>
304
+ Object.defineProperty(globalThis, "Temporal", {
305
+ value,
306
+ configurable: true,
307
+ writable: true,
308
+ enumerable: false,
309
+ });
310
+ Object.defineProperty(globalThis, "Temporal", {
311
+ configurable: true,
312
+ enumerable: false,
313
+ get() {
314
+ let temporalPath;
315
+ try { temporalPath = reqFromRuntime.resolve("@js-temporal/polyfill"); } catch {}
316
+ if (!temporalPath) return undefined;
317
+ const polyfill = reqFromRuntime(temporalPath);
318
+ // @js-temporal/polyfill exports `toTemporalInstant` as a function but does
319
+ // NOT auto-install it on Date.prototype (you assign it yourself). Install it
320
+ // here so that on the floor (no native Temporal) `date.toTemporalInstant()`
321
+ // AND the package clobber's re-export of `Date.prototype.toTemporalInstant`
322
+ // both work — matching native Node. Guarded so we never replace a native
323
+ // implementation on a runtime that ships Temporal.
324
+ if (
325
+ typeof Date.prototype.toTemporalInstant !== "function" &&
326
+ typeof polyfill.toTemporalInstant === "function"
327
+ ) {
328
+ Object.defineProperty(Date.prototype, "toTemporalInstant", {
329
+ value: polyfill.toTemporalInstant,
330
+ configurable: true,
331
+ writable: true,
332
+ enumerable: false,
333
+ });
334
+ }
335
+ const T = polyfill.Temporal;
336
+ defineTemporal(T);
337
+ return T;
338
+ },
339
+ set: defineTemporal,
340
+ });
341
+ }
342
+
343
+ // ── Compile-cache handling (R8) ─────────────────────────────────────
344
+ // nub injects its preload chain via `--require`, which Node loads at bootstrap.
345
+ // If the user set NODE_COMPILE_CACHE, Node would enable the V8 code cache BEFORE
346
+ // this preload runs and cache every module the chain pulls in (preload.cjs,
347
+ // transform-core.mjs, this file, polyfills.cjs, …) into the USER's dir — so a
348
+ // program reading `fs.readdirSync(NODE_COMPILE_CACHE)` would see ~9 nub entries,
349
+ // not its own 1 (program-observable; R8). spawn.rs prevents that by STRIPPING
350
+ // NODE_COMPILE_CACHE from the child env (bootstrap caches nothing) and stashing
351
+ // the original value in a sentinel file keyed on nub's PID — which is THIS child's
352
+ // `process.ppid` (nub is our direct parent). The dir travels via a sentinel file,
353
+ // never a NUB_* env var (brand boundary).
354
+ //
355
+ // Two preload steps consume it:
356
+ // 1. restoreCompileCacheEnv() runs EARLY, before transform-core.mjs is required,
357
+ // to put the original value BACK into process.env.NODE_COMPILE_CACHE. That
358
+ // matters because (a) transform-core reads `NODE_COMPILE_CACHE === "0"` as
359
+ // nub's transpile-cache disable signal, and (b) user code may read the env.
360
+ // Restoring it in JS does NOT re-trigger Node's V8 compile cache (Node
361
+ // configures that once at bootstrap from the now-stripped env), so the
362
+ // preload chain stays uncached. It also DELETES the sentinel (consume-once,
363
+ // so a recycled PID can't read stale state and the file never leaks).
364
+ // 2. reenableUserCompileCache() runs LAST, after all nub modules are loaded
365
+ // uncached and right before user code, and calls
366
+ // `module.enableCompileCache(dir)` for a real dir so the user's OWN modules
367
+ // cache as they always did. A value of "0" is nub's disable sentinel (Node
368
+ // treats "0" as a literal dir named 0, but nub honors it as "no caching"),
369
+ // so we skip enabling there.
370
+ // Best-effort throughout: a missing/unreadable sentinel or an enableCompileCache
371
+ // failure just means no user compile cache — strictly safer than the old pollution.
372
+ // `os.tmpdir()` without requiring `node:os`. Requiring os at preload pulls
373
+ // `Internal Binding os` + `NativeModule os` into process.moduleLoadList on EVERY
374
+ // startup (test-bootstrap-modules observes this) even though almost no run touches
375
+ // the compile-cache sentinel. This replica mirrors Node's libuv/os.tmpdir() env
376
+ // resolution (POSIX: TMPDIR→TMP→TEMP→/tmp; Win32: TEMP→TMP→SystemRoot/windir+\temp),
377
+ // trailing-separator-stripped, which is also what Rust's env::temp_dir() (the side
378
+ // that WRITES the sentinel in spawn.rs) resolves to — so both ends agree.
379
+ function tmpdirNoOs() {
380
+ const env = process.env;
381
+ if (process.platform === "win32") {
382
+ let dir = env.TEMP || env.TMP || ((env.SystemRoot || env.windir || "") + "\\temp");
383
+ if (dir.length > 1 && dir.endsWith("\\") && !dir.endsWith(":\\")) dir = dir.slice(0, -1);
384
+ return dir;
385
+ }
386
+ let dir = env.TMPDIR || env.TMP || env.TEMP || "/tmp";
387
+ if (dir.length > 1 && dir.endsWith("/")) dir = dir.slice(0, -1);
388
+ return dir;
389
+ }
390
+
391
+ function compileCacheSentinelPath() {
392
+ return join(tmpdirNoOs(), `nub-ccache-${process.ppid}`);
393
+ }
394
+
395
+ function restoreCompileCacheEnv() {
396
+ try {
397
+ const { readFileSync, rmSync } = require("node:fs");
398
+ const value = readFileSync(compileCacheSentinelPath(), "utf8");
399
+ try { rmSync(compileCacheSentinelPath()); } catch {}
400
+ if (value) process.env.NODE_COMPILE_CACHE = value;
401
+ } catch { /* no sentinel: env was never set, or already consumed */ }
402
+ // Propagate the R8 strip to node grandchildren the user spawns directly (plain
403
+ // node inheriting nub's --require preload + a live NODE_COMPILE_CACHE → it would
404
+ // cache nub's preload chain into the user's dir). The wrap MUST preserve each
405
+ // function's own symbols (esp. [util.promisify.custom]) — dropping them broke
406
+ // util.promisify(child_process.*) + abort/sync-io behavior. See wrapSpawnLike.
407
+ try { armChildProcessCompileCacheWrap(); } catch {}
408
+ }
409
+
410
+ // Arm the child_process compile-cache wrap WITHOUT eagerly requiring child_process.
411
+ //
412
+ // Eagerly `require("node:child_process")` at preload time pulls ~40 builtins into
413
+ // process.moduleLoadList on EVERY startup — net, dgram, the entire streams tree,
414
+ // spawn_sync/tty_wrap/pipe_wrap/tcp_wrap, os, vm, etc. (test-bootstrap-modules
415
+ // observes the exact list; child_process is the dominant extra-builtin source).
416
+ // A program that never spawns a child shouldn't pay that cost — and Node's own
417
+ // startup never loads child_process.
418
+ //
419
+ // So we intercept `Module._load` and apply the wrap to the child_process module the
420
+ // FIRST time USER code requires it (`require('child_process')` /
421
+ // `require('node:child_process')`), patching the returned singleton before handing
422
+ // it back. After patching once we restore the original `_load`, so steady-state
423
+ // require() has zero added overhead. If the user never requires child_process, the
424
+ // module is never loaded and the builtins stay out of the load list — matching Node.
425
+ let __cpWrapArmed = false;
426
+ function armChildProcessCompileCacheWrap() {
427
+ if (__cpWrapArmed || __cpWrapped) return;
428
+ __cpWrapArmed = true;
429
+ if (typeof module_._load !== "function") return;
430
+ const origLoad = module_._load;
431
+ module_._load = function (request, parent, isMain) {
432
+ const exports = origLoad.call(this, request, parent, isMain);
433
+ if (request === "child_process" || request === "node:child_process") {
434
+ module_._load = origLoad; // restore: one-shot, no steady-state overhead
435
+ try { wrapChildProcessCompileCache(exports); } catch {}
436
+ }
437
+ return exports;
438
+ };
439
+ }
440
+
441
+ // Monkey-patch child_process so node-targeted children the USER spawns with an
442
+ // explicit live NODE_COMPILE_CACHE get the SAME R8 treatment spawn.rs gives nub's
443
+ // own children: strip NODE_COMPILE_CACHE from the child env (so Node's bootstrap
444
+ // caches nothing of nub's inherited preload chain) and stash the original dir in a
445
+ // PID-keyed sentinel file the grandchild's restoreCompileCacheEnv() reads back via
446
+ // `process.ppid` to re-enable caching for the USER's own modules post-bootstrap.
447
+ // Brand rule: the dir travels via a sentinel file, never a NUB_* env var.
448
+ // `cp` is the already-loaded child_process exports object, passed in by the lazy
449
+ // `_load` interceptor so we never require it ourselves (which would defeat the
450
+ // deferral).
451
+ let __cpWrapped = false;
452
+ function wrapChildProcessCompileCache(cp) {
453
+ if (__cpWrapped || !cp) return;
454
+ __cpWrapped = true;
455
+ const { writeFileSync } = require("node:fs");
456
+ const { basename } = require("node:path");
457
+
458
+ const isNodeTarget = (command) => {
459
+ if (typeof command !== "string" || command.length === 0) return false;
460
+ if (command === process.execPath) return true;
461
+ const base = basename(command).toLowerCase();
462
+ return base === "node" || base === "node.exe";
463
+ };
464
+
465
+ // Returns a possibly-rewritten options object with NODE_COMPILE_CACHE stripped
466
+ // from its env, after writing the sentinel keyed on THIS process's pid (= the
467
+ // grandchild's process.ppid). No-op unless the child env explicitly carries a
468
+ // live (non-empty, != "0") NODE_COMPILE_CACHE — an inherited (undefined env)
469
+ // value was already stripped from this process by nub, so nothing to do there.
470
+ const stripFromOptions = (options) => {
471
+ if (!options || typeof options !== "object") return options;
472
+ const env = options.env;
473
+ if (!env || typeof env !== "object") return options;
474
+ const dir = env.NODE_COMPILE_CACHE;
475
+ if (!dir || dir === "0") return options;
476
+ try {
477
+ writeFileSync(join(tmpdirNoOs(), `nub-ccache-${process.pid}`), String(dir));
478
+ } catch { return options; }
479
+ const newEnv = { ...env };
480
+ delete newEnv.NODE_COMPILE_CACHE;
481
+ return { ...options, env: newEnv };
482
+ };
483
+
484
+ // For (command, args?, options?) signatures the options object is the last arg
485
+ // that is a non-array object; args is an optional array in between. Rewrites the
486
+ // call in place and dispatches to the original.
487
+ // Copy `orig`'s OWN symbols onto `wrapped` — crucially [util.promisify.custom],
488
+ // which Node sets on execFile/exec so `util.promisify(execFile)` returns a
489
+ // {stdout,stderr} promise. A bare wrapper without it silently changes promisify's
490
+ // result shape (broke test-child-process-promisified / -abortController /
491
+ // util-promisify-custom-names / sync-io-option / test-output-abort).
492
+ const preserveSymbols = (wrapped, orig) => {
493
+ for (const s of Object.getOwnPropertySymbols(orig)) {
494
+ try { wrapped[s] = orig[s]; } catch { /* read-only symbol: skip */ }
495
+ }
496
+ return wrapped;
497
+ };
498
+ const wrapSpawnLike = (orig) => preserveSymbols(function (command, ...rest) {
499
+ if (isNodeTarget(command)) {
500
+ let optIdx = -1;
501
+ for (let i = rest.length - 1; i >= 0; i--) {
502
+ const a = rest[i];
503
+ if (a && typeof a === "object" && !Array.isArray(a)) { optIdx = i; break; }
504
+ if (typeof a === "function") continue; // execFile callback
505
+ if (Array.isArray(a)) break; // args array — no options object present
506
+ }
507
+ if (optIdx >= 0) rest[optIdx] = stripFromOptions(rest[optIdx]);
508
+ }
509
+ return orig.call(this, command, ...rest);
510
+ }, orig);
511
+
512
+ cp.spawn = wrapSpawnLike(cp.spawn);
513
+ cp.spawnSync = wrapSpawnLike(cp.spawnSync);
514
+ cp.execFile = wrapSpawnLike(cp.execFile);
515
+ cp.execFileSync = wrapSpawnLike(cp.execFileSync);
516
+
517
+ // fork() always runs `process.execPath`, so it is always a node target. Its
518
+ // signature is (modulePath, args?, options?); reuse the same options rewrite.
519
+ const origFork = cp.fork;
520
+ cp.fork = function (modulePath, ...rest) {
521
+ let optIdx = -1;
522
+ for (let i = rest.length - 1; i >= 0; i--) {
523
+ const a = rest[i];
524
+ if (a && typeof a === "object" && !Array.isArray(a)) { optIdx = i; break; }
525
+ if (Array.isArray(a)) break;
526
+ }
527
+ if (optIdx >= 0) rest[optIdx] = stripFromOptions(rest[optIdx]);
528
+ return origFork.call(this, modulePath, ...rest);
529
+ };
530
+ }
531
+
532
+ function reenableUserCompileCache() {
533
+ const dir = process.env.NODE_COMPILE_CACHE;
534
+ // "0" is nub's disable signal (see transform-core); anything else is the user's
535
+ // real cache dir, which we re-point Node's compile cache at for THEIR modules.
536
+ if (!dir || dir === "0") return;
537
+ try { module_.enableCompileCache(dir); } catch {}
538
+ }
539
+
540
+ module.exports = {
541
+ installWatchReporting,
542
+ makeHooks,
543
+ installCjsRequireHooks,
544
+ preloadPolyfillPackages,
545
+ installTemporalLazyGlobal,
546
+ restoreCompileCacheEnv,
547
+ reenableUserCompileCache,
548
+ };