@openclaw/diagnostics-otel 2026.7.1-beta.2 → 2026.7.1-beta.4

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 (91) hide show
  1. package/dist/index.js +1 -2404
  2. package/dist/runtime-api-BUlvn2ET.js +2589 -0
  3. package/dist/runtime-api.js +2 -0
  4. package/node_modules/@types/node/README.md +1 -1
  5. package/node_modules/@types/node/assert/strict.d.ts +10 -1
  6. package/node_modules/@types/node/assert.d.ts +144 -17
  7. package/node_modules/@types/node/async_hooks.d.ts +9 -9
  8. package/node_modules/@types/node/buffer.d.ts +64 -30
  9. package/node_modules/@types/node/child_process.d.ts +17 -21
  10. package/node_modules/@types/node/cluster.d.ts +13 -13
  11. package/node_modules/@types/node/console.d.ts +1 -1
  12. package/node_modules/@types/node/crypto.d.ts +471 -575
  13. package/node_modules/@types/node/diagnostics_channel.d.ts +274 -32
  14. package/node_modules/@types/node/dns.d.ts +15 -15
  15. package/node_modules/@types/node/events.d.ts +3 -3
  16. package/node_modules/@types/node/ffi.d.ts +486 -0
  17. package/node_modules/@types/node/fs/promises.d.ts +7 -4
  18. package/node_modules/@types/node/fs.d.ts +24 -18
  19. package/node_modules/@types/node/globals.d.ts +4 -4
  20. package/node_modules/@types/node/http.d.ts +30 -5
  21. package/node_modules/@types/node/http2.d.ts +5 -1
  22. package/node_modules/@types/node/index.d.ts +1 -3
  23. package/node_modules/@types/node/inspector.d.ts +1 -1
  24. package/node_modules/@types/node/module.d.ts +16 -45
  25. package/node_modules/@types/node/net.d.ts +1 -1
  26. package/node_modules/@types/node/os.d.ts +2 -2
  27. package/node_modules/@types/node/package.json +4 -4
  28. package/node_modules/@types/node/perf_hooks.d.ts +1 -9
  29. package/node_modules/@types/node/process.d.ts +25 -12
  30. package/node_modules/@types/node/quic.d.ts +92 -11
  31. package/node_modules/@types/node/readline.d.ts +12 -11
  32. package/node_modules/@types/node/repl.d.ts +10 -10
  33. package/node_modules/@types/node/sqlite.d.ts +56 -1
  34. package/node_modules/@types/node/stream/iter.d.ts +150 -0
  35. package/node_modules/@types/node/stream/promises.d.ts +2 -2
  36. package/node_modules/@types/node/stream/web.d.ts +1 -1
  37. package/node_modules/@types/node/stream.d.ts +37 -5
  38. package/node_modules/@types/node/test/reporters.d.ts +1 -1
  39. package/node_modules/@types/node/test.d.ts +120 -10
  40. package/node_modules/@types/node/tls.d.ts +1 -1
  41. package/node_modules/@types/node/ts5.6/index.d.ts +2 -4
  42. package/node_modules/@types/node/ts5.7/index.d.ts +1 -3
  43. package/node_modules/@types/node/url.d.ts +3 -3
  44. package/node_modules/@types/node/util/types.d.ts +1 -1
  45. package/node_modules/@types/node/util.d.ts +22 -22
  46. package/node_modules/@types/node/v8.d.ts +85 -3
  47. package/node_modules/@types/node/vm.d.ts +9 -9
  48. package/node_modules/@types/node/web-globals/domexception.d.ts +15 -0
  49. package/node_modules/@types/node/worker_threads.d.ts +10 -12
  50. package/node_modules/@types/node/zlib.d.ts +3 -3
  51. package/node_modules/acorn/CHANGELOG.md +14 -0
  52. package/node_modules/acorn/README.md +3 -0
  53. package/node_modules/acorn/dist/acorn.d.mts +6 -0
  54. package/node_modules/acorn/dist/acorn.d.ts +6 -0
  55. package/node_modules/acorn/dist/acorn.js +87 -49
  56. package/node_modules/acorn/dist/acorn.mjs +87 -49
  57. package/node_modules/acorn/package.json +2 -2
  58. package/node_modules/import-in-the-middle/CHANGELOG.md +21 -0
  59. package/node_modules/import-in-the-middle/README.md +60 -0
  60. package/node_modules/import-in-the-middle/create-hook.mjs +375 -146
  61. package/node_modules/import-in-the-middle/lib/get-exports.mjs +88 -52
  62. package/node_modules/import-in-the-middle/lib/io.mjs +78 -0
  63. package/node_modules/import-in-the-middle/package.json +1 -1
  64. package/node_modules/import-in-the-middle/register-hooks.d.ts +35 -0
  65. package/node_modules/import-in-the-middle/register-hooks.mjs +63 -0
  66. package/node_modules/undici-types/agent.d.ts +0 -2
  67. package/node_modules/undici-types/client.d.ts +25 -19
  68. package/node_modules/undici-types/dispatcher.d.ts +7 -33
  69. package/node_modules/undici-types/dispatcher1-wrapper.d.ts +7 -0
  70. package/node_modules/undici-types/fetch.d.ts +24 -4
  71. package/node_modules/undici-types/h2c-client.d.ts +6 -6
  72. package/node_modules/undici-types/handlers.d.ts +1 -2
  73. package/node_modules/undici-types/header.d.ts +5 -0
  74. package/node_modules/undici-types/index.d.ts +3 -1
  75. package/node_modules/undici-types/interceptors.d.ts +1 -1
  76. package/node_modules/undici-types/package.json +1 -1
  77. package/node_modules/undici-types/pool.d.ts +0 -2
  78. package/node_modules/undici-types/proxy-agent.d.ts +2 -2
  79. package/node_modules/undici-types/round-robin-pool.d.ts +0 -2
  80. package/node_modules/undici-types/snapshot-agent.d.ts +4 -0
  81. package/node_modules/undici-types/socks5-proxy-agent.d.ts +2 -2
  82. package/node_modules/undici-types/webidl.d.ts +0 -1
  83. package/node_modules/yargs/CHANGELOG.md +330 -0
  84. package/node_modules/yargs/build/index.cjs +1 -1
  85. package/node_modules/yargs/package.json +6 -4
  86. package/node_modules/yargs/yargs +5 -9
  87. package/node_modules/yargs/yargs.cjs +15 -0
  88. package/npm-shrinkwrap.json +18 -18
  89. package/package.json +4 -4
  90. package/node_modules/@types/node/compatibility/iterators.d.ts +0 -21
  91. package/node_modules/yargs/browser.d.ts +0 -5
@@ -6,9 +6,10 @@ import { URL, fileURLToPath } from 'url'
6
6
  import { inspect } from 'util'
7
7
  import { builtinModules } from 'module'
8
8
  import {
9
- getExports as getExportsImpl,
9
+ getExports,
10
10
  hasModuleExportsCJSDefault
11
11
  } from './lib/get-exports.mjs'
12
+ import { RESOLVE, driveSync, driveAsync } from './lib/io.mjs'
12
13
 
13
14
  const specifiers = new Map()
14
15
  const isWin = process.platform === 'win32'
@@ -16,17 +17,34 @@ const isWin = process.platform === 'win32'
16
17
  // FIXME: Typescript extensions are added temporarily until we find a better
17
18
  // way of supporting arbitrary extensions
18
19
  const EXTENSION_RE = /\.(js|mjs|cjs|ts|mts|cts)$/
19
- const NODE_VERSION = process.versions.node.split('.')
20
- const NODE_MAJOR = Number(NODE_VERSION[0])
21
- const NODE_MINOR = Number(NODE_VERSION[1])
22
20
  const HANDLED_FORMATS = new Set(['builtin', 'module', 'commonjs'])
23
21
  const TRACE_WARNINGS = process.execArgv.includes('--trace-warnings')
24
22
 
25
- let getExports
26
- if (NODE_MAJOR > 16 || (NODE_MAJOR === 16 && NODE_MINOR >= 16)) {
27
- getExports = getExportsImpl
28
- } else {
29
- getExports = (url) => import(url).then(Object.keys)
23
+ // process.versions.node is always "major.minor.patch" (nightlies add a suffix
24
+ // the regex ignores).
25
+ const [, NODE_MAJOR, NODE_MINOR, NODE_PATCH] =
26
+ process.versions.node.match(/^(\d+)\.(\d+)\.(\d+)/).map(Number)
27
+
28
+ /**
29
+ * Whether the running Node.js can correctly run the synchronous loader via
30
+ * `module.registerHooks`.
31
+ *
32
+ * `module.registerHooks` exists since v22.15, but its synchronous load hook
33
+ * rejected the nullish CommonJS `source` the loader returns for `require()`s
34
+ * pulled into the ESM graph (throwing `ERR_INVALID_RETURN_PROPERTY_VALUE`) until
35
+ * https://github.com/nodejs/node/pull/59929, released in 22.22.3, 24.11.1,
36
+ * 25.1.0 and 26.0.0. Earlier 24.x (<= 24.11.0) and 25.0.0 ship `registerHooks`
37
+ * but predate the fix, so the synchronous loader must fall back to the
38
+ * asynchronous one there.
39
+ *
40
+ * @returns {boolean}
41
+ */
42
+ export function supportsSyncHooks () {
43
+ if (NODE_MAJOR >= 26) return true
44
+ if (NODE_MAJOR === 25) return NODE_MINOR >= 1
45
+ if (NODE_MAJOR === 24) return NODE_MINOR > 11 || (NODE_MINOR === 11 && NODE_PATCH >= 1)
46
+ if (NODE_MAJOR === 22) return NODE_MINOR > 22 || (NODE_MINOR === 22 && NODE_PATCH >= 3)
47
+ return false
30
48
  }
31
49
 
32
50
  function hasIitm (url) {
@@ -181,21 +199,81 @@ function emitWarning (err) {
181
199
  process.emitWarning(warnMessage)
182
200
  }
183
201
 
202
+ /**
203
+ * Builds the setter/getter/re-export block injected into the wrapper module for
204
+ * a single named export. This is pure string generation, identical regardless
205
+ * of how the loader is driven, so both the synchronous and asynchronous paths
206
+ * share it.
207
+ *
208
+ * @param {string} n The exported name.
209
+ * @param {string} srcUrl The URL of the module the export belongs to.
210
+ * @returns {string}
211
+ */
212
+ function buildSetter (n, srcUrl) {
213
+ const variableName = `$${n.replace(/[^a-zA-Z0-9_$]/g, '_')}`
214
+ const objectKey = JSON.stringify(n)
215
+ const reExportedName = n === 'default' ? n : objectKey
216
+
217
+ // For the module.exports synthetic export (Node 23+), fall back to $default
218
+ // when namespace['module.exports'] is not exposed by the native ESM namespace
219
+ // (builtins don't expose it). This ensures the IITM hook proxy returns the
220
+ // actual CJS value (e.g. EventEmitter) when an instrumentor reads
221
+ // capturedExports['module.exports'], rather than undefined.
222
+ const moduleExportsFallback = n === 'module.exports' ? ' ?? $default' : ''
223
+
224
+ const reExportLine = (n === 'module.exports' && (srcUrl.startsWith('node:') || builtinModules.includes(srcUrl)))
225
+ ? ''
226
+ : `export { ${variableName} as ${reExportedName} }`
227
+
228
+ return `
229
+ let ${variableName}
230
+ __overridden[${objectKey}] = false
231
+ let ${variableName}Defer = false
232
+ try {
233
+ ${variableName} = _[${objectKey}] = namespace[${objectKey}]${moduleExportsFallback}
234
+ } catch (err) {
235
+ if (!(err instanceof ReferenceError)) throw err
236
+ ${variableName}Defer = true
237
+ }
238
+
239
+ if (${variableName}Defer || ${variableName} === undefined) {
240
+ __pending.push(__makeUpdater(
241
+ ${objectKey},
242
+ () => namespace[${objectKey}]${moduleExportsFallback},
243
+ (v) => { ${variableName} = _[${objectKey}] = v }
244
+ ))
245
+ }
246
+ ${reExportLine}
247
+ set[${objectKey}] = (v) => {
248
+ __overridden[${objectKey}] = true
249
+ ${variableName} = v
250
+ return true
251
+ }
252
+ get[${objectKey}] = () => ${variableName}
253
+ `
254
+ }
255
+
184
256
  /**
185
257
  * Processes a module's exports and builds a set of setter blocks.
186
258
  *
259
+ * Written as a "sans-io" generator (see `lib/io.mjs`): instead of calling the
260
+ * loader's resolve/load hooks directly it `yield`s `[RESOLVE, ...]` to resolve
261
+ * star re-exports and `[LOAD, ...]` (via {@link getExports}) to read source,
262
+ * and is driven by either {@link driveSync} (for
263
+ * `module.registerHooks`) or {@link driveAsync} (for `module.register`). The
264
+ * body is identical for both, so there is a single implementation to maintain.
265
+ *
187
266
  * @param {object} params
188
267
  * @param {string} params.srcUrl The full URL to the module to process.
189
268
  * @param {object} params.context Provided by the loaders API.
190
- * @param {Function} params.parentGetSource Provides the source code for the parent module.
191
- * @param {Function} params.parentResolve Provides the resolve function for the parent module.
192
269
  * @param {boolean} [params.excludeDefault = false] Exclude the default export.
193
270
  *
194
- * @returns {Promise<Map<string, string>>} The shimmed setters for all the exports
271
+ * @returns {Generator<Array, Map<string, string>>} A generator that yields I/O
272
+ * operations and ultimately returns the shimmed setters for all the exports
195
273
  * from the module and any transitive export all modules.
196
274
  */
197
- async function processModule ({ srcUrl, context, parentGetSource, parentResolve, excludeDefault = false }) {
198
- const exportNames = await getExports(srcUrl, context, parentGetSource)
275
+ function * processModule ({ srcUrl, context, excludeDefault = false }) {
276
+ const exportNames = yield * getExports(srcUrl, context)
199
277
  const starExports = new Set()
200
278
  const setters = new Map()
201
279
 
@@ -243,17 +321,14 @@ async function processModule ({ srcUrl, context, parentGetSource, parentResolve,
243
321
 
244
322
  // Relative paths need to be resolved relative to the parent module
245
323
  const newSpecifier = isBareSpecifier(modFile) ? modFile : new URL(modFile, srcUrl).href
246
- // We need to call `parentResolve` to resolve bare specifiers to a full
247
- // URL. We also need to call `parentResolve` for all sub-modules to get
248
- // the `format`. We can't rely on the parents `format` to know if this
249
- // sub-module is ESM or CJS!
250
- const result = await parentResolve(newSpecifier, { parentURL: srcUrl })
324
+ // We need to resolve bare specifiers to a full URL. We also need to
325
+ // resolve all sub-modules to get the `format`. We can't rely on the
326
+ // parent's `format` to know if this sub-module is ESM or CJS!
327
+ const result = yield [RESOLVE, newSpecifier, { parentURL: srcUrl }]
251
328
 
252
- const subSetters = await processModule({
329
+ const subSetters = yield * processModule({
253
330
  srcUrl: result.url,
254
331
  context: { ...context, format: result.format },
255
- parentGetSource,
256
- parentResolve,
257
332
  excludeDefault: true
258
333
  })
259
334
 
@@ -261,36 +336,7 @@ async function processModule ({ srcUrl, context, parentGetSource, parentResolve,
261
336
  addSetter(name, setter, true)
262
337
  }
263
338
  } else {
264
- const variableName = `$${n.replace(/[^a-zA-Z0-9_$]/g, '_')}`
265
- const objectKey = JSON.stringify(n)
266
- const reExportedName = n === 'default' ? n : objectKey
267
-
268
- addSetter(n, `
269
- let ${variableName}
270
- __overridden[${objectKey}] = false
271
- let ${variableName}Defer = false
272
- try {
273
- ${variableName} = _[${objectKey}] = namespace[${objectKey}]
274
- } catch (err) {
275
- if (!(err instanceof ReferenceError)) throw err
276
- ${variableName}Defer = true
277
- }
278
-
279
- if (${variableName}Defer || ${variableName} === undefined) {
280
- __pending.push(__makeUpdater(
281
- ${objectKey},
282
- () => namespace[${objectKey}],
283
- (v) => { ${variableName} = _[${objectKey}] = v }
284
- ))
285
- }
286
- export { ${variableName} as ${reExportedName} }
287
- set[${objectKey}] = (v) => {
288
- __overridden[${objectKey}] = true
289
- ${variableName} = v
290
- return true
291
- }
292
- get[${objectKey}] = () => ${variableName}
293
- `)
339
+ addSetter(n, buildSetter(n, srcUrl))
294
340
  }
295
341
  }
296
342
 
@@ -307,56 +353,105 @@ export function createHook (meta) {
307
353
  let cachedResolve
308
354
  const iitmURL = new URL('lib/register.js', meta.url).toString()
309
355
  let includeModules, excludeModules
356
+ let shouldInclude = defaultShouldInclude
357
+
358
+ // Track CJS module URLs that IITM has wrapped. On Node 24+, CJS modules loaded
359
+ // via loadCJSModule (in an ESM import chain) have their require() calls for
360
+ // builtins routed through the ESM resolver. Without this guard, IITM would
361
+ // intercept those require() calls and return an ESM namespace object instead
362
+ // of the native CJS module value (e.g. EventEmitter constructor), breaking
363
+ // patterns like `class App extends require('events') {}`.
364
+ const cjsInIitmChain = new Set()
365
+
366
+ // Default matcher, used unless the consumer supplies its own `shouldInclude`
367
+ // (see applyOptions). It applies the include/exclude lists, so finishResolve
368
+ // always has a predicate to call and never has to special-case its absence.
369
+ //
370
+ // We check the specifier to match libraries loaded with bare specifiers from
371
+ // node_modules, and the full file URL for non-bare specifier imports (relative
372
+ // paths would be error prone). An absolute path entry added via Hook over the
373
+ // message port matches the resolved file path, so it is resolved here.
374
+ function defaultShouldInclude (url, specifier) {
375
+ let resultPath
376
+ if (url.startsWith('file:')) {
377
+ const stackTraceLimit = Error.stackTraceLimit
378
+ Error.stackTraceLimit = 0
379
+ try {
380
+ resultPath = fileURLToPath(url)
381
+ } catch {}
382
+ Error.stackTraceLimit = stackTraceLimit
383
+ }
384
+ function match (each) {
385
+ if (each instanceof RegExp) {
386
+ return each.test(url)
387
+ }
310
388
 
311
- async function initialize (data) {
312
- if (global.__import_in_the_middle_initialized__) {
313
- process.emitWarning("The 'import-in-the-middle' hook has already been initialized")
389
+ return each === specifier || each === url || (resultPath && each === resultPath)
314
390
  }
315
391
 
316
- global.__import_in_the_middle_initialized__ = true
392
+ if (includeModules && !includeModules.some(match)) {
393
+ return false
394
+ }
317
395
 
318
- if (data) {
319
- includeModules = ensureArrayWithBareSpecifiersFileUrlsAndRegex(data.include, 'include')
320
- excludeModules = ensureArrayWithBareSpecifiersFileUrlsAndRegex(data.exclude, 'exclude')
396
+ if (excludeModules && excludeModules.some(match)) {
397
+ return false
398
+ }
321
399
 
322
- if (data.addHookMessagePort) {
323
- data.addHookMessagePort.on('message', (modules) => {
324
- if (includeModules === undefined) {
325
- includeModules = []
326
- }
400
+ return true
401
+ }
327
402
 
328
- for (const each of modules) {
329
- if (!each.startsWith('node:') && builtinModules.includes(each)) {
330
- includeModules.push(`node:${each}`)
331
- }
403
+ // Applies the include/exclude/message-port configuration. Shared by the
404
+ // asynchronous `initialize` (off-thread `module.register`, which receives
405
+ // `data` over the registration boundary) and by synchronous registration
406
+ // (`module.registerHooks`), which has no `initialize` step and passes the
407
+ // same options directly.
408
+ function applyOptions (data) {
409
+ includeModules = ensureArrayWithBareSpecifiersFileUrlsAndRegex(data.include, 'include')
410
+ excludeModules = ensureArrayWithBareSpecifiersFileUrlsAndRegex(data.exclude, 'exclude')
411
+
412
+ // A consumer can supply its own matcher as `shouldInclude(url, specifier)`,
413
+ // taking ownership of the include/exclude decision instead of expressing it
414
+ // as bare-specifier / file-URL / regex lists. It replaces the default list
415
+ // matcher and is called with the resolved URL and specifier; otherwise the
416
+ // default applies the include/exclude options.
417
+ shouldInclude = typeof data.shouldInclude === 'function' ? data.shouldInclude : defaultShouldInclude
418
+
419
+ if (data.addHookMessagePort) {
420
+ data.addHookMessagePort.on('message', (modules) => {
421
+ if (includeModules === undefined) {
422
+ includeModules = []
423
+ }
332
424
 
333
- includeModules.push(each)
425
+ for (const each of modules) {
426
+ if (!each.startsWith('node:') && builtinModules.includes(each)) {
427
+ includeModules.push(`node:${each}`)
334
428
  }
335
429
 
336
- data.addHookMessagePort.postMessage('ack')
337
- }).unref()
338
- }
430
+ includeModules.push(each)
431
+ }
432
+
433
+ data.addHookMessagePort.postMessage('ack')
434
+ }).unref()
339
435
  }
340
436
  }
341
437
 
342
- async function resolve (specifier, context, parentResolve) {
343
- cachedResolve = parentResolve
344
-
345
- // See https://github.com/nodejs/import-in-the-middle/pull/76.
346
- if (specifier === iitmURL) {
347
- return {
348
- url: specifier,
349
- shortCircuit: true
350
- }
438
+ async function initialize (data) {
439
+ if (global.__import_in_the_middle_initialized__) {
440
+ process.emitWarning("The 'import-in-the-middle' hook has already been initialized")
351
441
  }
352
442
 
353
- const { parentURL = '' } = context
354
- const newSpecifier = deleteIitm(specifier)
355
- if (isWin && parentURL.indexOf('file:node') === 0) {
356
- context.parentURL = ''
443
+ global.__import_in_the_middle_initialized__ = true
444
+
445
+ if (data) {
446
+ applyOptions(data)
357
447
  }
358
- const result = await parentResolve(newSpecifier, context)
448
+ }
359
449
 
450
+ // Shared post-processing for the `resolve` hook: everything that happens
451
+ // once the parent loader has turned the specifier into a resolved URL. The
452
+ // only difference between the asynchronous and synchronous hooks is whether
453
+ // that resolution was awaited, so all the wrapping decisions live here.
454
+ function finishResolve (result, specifier, context, parentURL) {
360
455
  // Do not wrap the entrypoint module. Many CLIs check whether they are the
361
456
  // "main" module (e.g. require.main === module). Wrapping changes how they
362
457
  // are evaluated, and can make them exit without doing anything.
@@ -367,37 +462,28 @@ export function createHook (meta) {
367
462
  return result
368
463
  }
369
464
 
370
- // For included/excluded modules, we check the specifier to match libraries
371
- // that are loaded with bare specifiers from node_modules.
372
- //
373
- // For non-bare specifier imports, we match to the full file URL because
374
- // using relative paths would be very error prone!
375
- let resultPath
376
- if (result.url.startsWith('file:')) {
377
- const stackTraceLimit = Error.stackTraceLimit
378
- Error.stackTraceLimit = 0
379
- try {
380
- resultPath = fileURLToPath(result.url)
381
- } catch {}
382
- Error.stackTraceLimit = stackTraceLimit
383
- }
384
- function match (each) {
385
- if (each instanceof RegExp) {
386
- return each.test(result.url)
387
- }
388
-
389
- return each === specifier || each === result.url || (resultPath && each === resultPath)
390
- }
391
-
465
+ // Never wrap a module whose format we don't handle (e.g. json, wasm); this
466
+ // holds regardless of how inclusion is decided below.
392
467
  if (result.format && !HANDLED_FORMATS.has(result.format)) {
393
468
  return result
394
469
  }
395
470
 
396
- if (includeModules && !includeModules.some(match)) {
471
+ // The synchronous hooks (`module.registerHooks`) fire for `require()` as well
472
+ // as `import`, but iitm only owns the ESM graph: CommonJS modules are
473
+ // instrumented separately through require-in-the-middle, and `require()` must
474
+ // return the native, mutable module value (e.g. graceful-fs does
475
+ // `Object.defineProperty(require('fs'), ...)`, which throws on a frozen ESM
476
+ // namespace). Node reports the active module system in `context.conditions`
477
+ // ('require' vs 'import'), so leave any require() resolution untouched. The
478
+ // asynchronous hook never sees the 'require' condition, so this is a no-op
479
+ // there and only affects the synchronous path.
480
+ if (context.conditions?.includes('require')) {
397
481
  return result
398
482
  }
399
483
 
400
- if (excludeModules && excludeModules.some(match)) {
484
+ // `shouldInclude` is always set (the include/exclude list matcher by default,
485
+ // a consumer-provided predicate otherwise), so no nullish check is needed.
486
+ if (!shouldInclude(result.url, specifier)) {
401
487
  return result
402
488
  }
403
489
 
@@ -405,6 +491,17 @@ export function createHook (meta) {
405
491
  return result
406
492
  }
407
493
 
494
+ // When a CJS module is loaded by an IITM shim, its require() calls for
495
+ // builtins may be routed through the ESM resolver on Node 24+. Skip IITM
496
+ // wrapping in that case so require() returns the native module value.
497
+ // We also propagate the membership to the resolved child so that its own
498
+ // transitive require() calls are likewise skipped (the entire synchronous
499
+ // CJS require chain must remain unwrapped to avoid ERR_VM_MODULE_LINK_FAILURE).
500
+ if (cjsInIitmChain.has(parentURL)) {
501
+ cjsInIitmChain.add(result.url)
502
+ return result
503
+ }
504
+
408
505
  // We don't want to attempt to wrap native modules
409
506
  if (result.url.endsWith('.node')) {
410
507
  return result
@@ -430,26 +527,63 @@ export function createHook (meta) {
430
527
  return {
431
528
  url: addIitm(result.url),
432
529
  shortCircuit: true,
433
- format: result.format
530
+ // Node's synchronous resolver drops `format: 'builtin'` for bare builtin
531
+ // specifiers (`require('crypto')` -> `node:crypto`), so restore it;
532
+ // otherwise the load hook reads `node:crypto` from disk and throws ENOENT.
533
+ format: result.format ?? (result.url.startsWith('node:') ? 'builtin' : undefined)
434
534
  }
435
535
  }
436
536
 
437
- async function getSource (url, context, parentGetSource) {
438
- if (hasIitm(url)) {
439
- const realUrl = deleteIitm(url)
440
- const originalSpecifier = specifiers.get(realUrl)
537
+ async function resolve (specifier, context, parentResolve) {
538
+ cachedResolve = parentResolve
441
539
 
442
- try {
443
- const setters = await processModule({
444
- srcUrl: realUrl,
445
- context,
446
- parentGetSource,
447
- parentResolve: cachedResolve
448
- })
449
- // If the module loaded successfully, we can remove the specifier to reduce memory usage early.
450
- specifiers.delete(realUrl)
451
- return {
452
- source: `
540
+ // See https://github.com/nodejs/import-in-the-middle/pull/76.
541
+ if (specifier === iitmURL) {
542
+ return {
543
+ url: specifier,
544
+ shortCircuit: true
545
+ }
546
+ }
547
+
548
+ const { parentURL = '' } = context
549
+ const newSpecifier = deleteIitm(specifier)
550
+ if (isWin && parentURL.indexOf('file:node') === 0) {
551
+ context.parentURL = ''
552
+ }
553
+ const result = await parentResolve(newSpecifier, context)
554
+
555
+ return finishResolve(result, specifier, context, parentURL)
556
+ }
557
+
558
+ // Synchronous counterpart to `resolve`, for `module.registerHooks`. The
559
+ // synchronous `nextResolve` returns its result directly. We stash it so the
560
+ // synchronous `load` hook can resolve star re-exports later, mirroring how
561
+ // `resolve` caches `parentResolve`.
562
+ function resolveSync (specifier, context, nextResolve) {
563
+ cachedResolve = nextResolve
564
+
565
+ if (specifier === iitmURL) {
566
+ return {
567
+ url: specifier,
568
+ shortCircuit: true
569
+ }
570
+ }
571
+
572
+ const { parentURL = '' } = context
573
+ const newSpecifier = deleteIitm(specifier)
574
+ if (isWin && parentURL.indexOf('file:node') === 0) {
575
+ context.parentURL = ''
576
+ }
577
+ const result = nextResolve(newSpecifier, context)
578
+
579
+ return finishResolve(result, specifier, context, parentURL)
580
+ }
581
+
582
+ // Builds the wrapper module source that re-exports the real module through
583
+ // iitm's proxy. Pure string generation shared by the asynchronous and
584
+ // synchronous `load` paths.
585
+ function buildWrapperSource (realUrl, setters, originalSpecifier) {
586
+ return `
453
587
  import { register } from '${iitmURL}'
454
588
  import * as namespace from ${JSON.stringify(realUrl)}
455
589
 
@@ -518,26 +652,46 @@ if (__pending.length > 0) {
518
652
 
519
653
  register(${JSON.stringify(realUrl)}, _, set, get, ${JSON.stringify(originalSpecifier)})
520
654
  `
521
- }
522
- } catch (cause) {
523
- // If the module failed loading, the specifier will not be used again, so
524
- // we can remove it to prevent a memory leak.
525
- specifiers.delete(realUrl)
526
- // If there are other ESM loader hooks registered as well as iitm,
527
- // depending on the order they are registered, source might not be
528
- // JavaScript.
529
- //
530
- // If we fail to parse a module for exports, we should fall back to the
531
- // parent loader. These modules will not be wrapped with proxies and
532
- // cannot be Hook'ed but at least this does not take down the entire app
533
- // and block iitm from being used.
534
- //
535
- // We log the error because there might be bugs in iitm and without this
536
- // it would be very tricky to debug
537
- const err = new Error(`'import-in-the-middle' failed to wrap '${realUrl}'`)
538
- err.cause = cause
539
- emitWarning(err)
655
+ }
656
+
657
+ // Bookkeeping shared by the async and sync wrap paths once `processModule`
658
+ // succeeds: free the specifier entry early, and remember CJS modules so their
659
+ // transitive require() chain bypasses iitm (see `load`). Returns the wrapper
660
+ // module source.
661
+ function onWrapSuccess (realUrl, context, originalSpecifier, setters) {
662
+ specifiers.delete(realUrl)
663
+ // context.format is set to 'commonjs' by getCjsExports during processModule.
664
+ if (context.format === 'commonjs') {
665
+ cjsInIitmChain.add(realUrl)
666
+ }
667
+ return buildWrapperSource(realUrl, setters, originalSpecifier)
668
+ }
669
+
670
+ // Bookkeeping shared by the async and sync wrap paths when `processModule`
671
+ // throws. iitm falls back to the parent loader so the module loads unwrapped
672
+ // (it just can't be Hook'ed) rather than taking down the whole app. We free
673
+ // the specifier entry to avoid a leak, and log because a failure here is
674
+ // usually an iitm bug and would otherwise be very tricky to debug.
675
+ function onWrapFailure (realUrl, cause) {
676
+ specifiers.delete(realUrl)
677
+ const err = new Error(`'import-in-the-middle' failed to wrap '${realUrl}'`)
678
+ err.cause = cause
679
+ emitWarning(err)
680
+ }
681
+
682
+ async function getSource (url, context, parentGetSource) {
683
+ if (hasIitm(url)) {
684
+ const realUrl = deleteIitm(url)
685
+ const originalSpecifier = specifiers.get(realUrl)
540
686
 
687
+ try {
688
+ const setters = await driveAsync(
689
+ processModule({ srcUrl: realUrl, context }),
690
+ { resolve: cachedResolve, load: parentGetSource }
691
+ )
692
+ return { source: onWrapSuccess(realUrl, context, originalSpecifier, setters) }
693
+ } catch (cause) {
694
+ onWrapFailure(realUrl, cause)
541
695
  // Revert back to the non-iitm URL
542
696
  url = realUrl
543
697
  }
@@ -546,6 +700,29 @@ register(${JSON.stringify(realUrl)}, _, set, get, ${JSON.stringify(originalSpeci
546
700
  return parentGetSource(url, context)
547
701
  }
548
702
 
703
+ // Synchronous counterpart to `getSource`, for `module.registerHooks`. Drives
704
+ // `processModule` straight through; all bookkeeping and source generation is
705
+ // shared with `getSource`.
706
+ function getSourceSync (url, context, nextLoad) {
707
+ if (hasIitm(url)) {
708
+ const realUrl = deleteIitm(url)
709
+ const originalSpecifier = specifiers.get(realUrl)
710
+
711
+ try {
712
+ const setters = driveSync(
713
+ processModule({ srcUrl: realUrl, context }),
714
+ { resolve: cachedResolve, load: nextLoad }
715
+ )
716
+ return { source: onWrapSuccess(realUrl, context, originalSpecifier, setters) }
717
+ } catch (cause) {
718
+ onWrapFailure(realUrl, cause)
719
+ url = realUrl
720
+ }
721
+ }
722
+
723
+ return nextLoad(url, context)
724
+ }
725
+
549
726
  async function load (url, context, parentLoad) {
550
727
  if (hasIitm(url)) {
551
728
  const result = await getSource(url, context, parentLoad)
@@ -563,8 +740,60 @@ register(${JSON.stringify(realUrl)}, _, set, get, ${JSON.stringify(originalSpeci
563
740
  return parentLoad(deleteIitm(url), context)
564
741
  }
565
742
 
743
+ // On Node 22+, when a CJS module is loaded through the ESM translator and
744
+ // another loader hook provides its source (instead of leaving source null
745
+ // for Node to read natively), require() calls inside that CJS module for
746
+ // packages using the "module-sync" exports condition fail with
747
+ // ERR_VM_MODULE_LINK_FAILURE. Work around this Node bug by stripping
748
+ // hook-provided source for CJS modules in the synchronous require chain,
749
+ // forcing Node to use its native CJS loader which handles this correctly.
750
+ if (cjsInIitmChain.has(url)) {
751
+ const result = await parentLoad(url, context)
752
+ if (result.format === 'commonjs' && result.source != null) {
753
+ return {
754
+ format: result.format,
755
+ source: undefined
756
+ }
757
+ }
758
+ return result
759
+ }
760
+
566
761
  return parentLoad(url, context)
567
762
  }
568
763
 
569
- return { initialize, load, resolve }
764
+ // Synchronous counterpart to `load`, for `module.registerHooks`. Mirrors the
765
+ // async `load` exactly — wrapping via `getSourceSync` and applying the same
766
+ // CJS-in-iitm-chain source stripping — only without awaiting.
767
+ function loadSync (url, context, nextLoad) {
768
+ if (hasIitm(url)) {
769
+ const result = getSourceSync(url, context, nextLoad)
770
+ // If wrapping failed, `getSourceSync()` may have fallen back to `nextLoad`,
771
+ // which can legally return `source: null` (e.g. for non-JS formats).
772
+ if (result && typeof result === 'object' && result.source != null) {
773
+ return {
774
+ source: result.source,
775
+ shortCircuit: true,
776
+ format: 'module'
777
+ }
778
+ }
779
+
780
+ // Fall back to the parent loader with the original (non-iitm) URL.
781
+ return nextLoad(deleteIitm(url), context)
782
+ }
783
+
784
+ if (cjsInIitmChain.has(url)) {
785
+ const result = nextLoad(url, context)
786
+ if (result.format === 'commonjs' && result.source != null) {
787
+ return {
788
+ format: result.format,
789
+ source: undefined
790
+ }
791
+ }
792
+ return result
793
+ }
794
+
795
+ return nextLoad(url, context)
796
+ }
797
+
798
+ return { initialize, load, resolve, resolveSync, loadSync, applyOptions }
570
799
  }