@scriptc/compiler 0.0.10 → 0.0.12

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 (111) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +63 -15
  2. package/ambient/scriptc.d.ts +2 -2
  3. package/dist/backend/cc.d.ts +2 -2
  4. package/dist/backend/emission/emit-async.js +1 -1
  5. package/dist/backend/emission/emit-exprs.js +131 -54
  6. package/dist/backend/emission/emit-exprs.js.map +1 -1
  7. package/dist/backend/emission/emit-island.js +1 -1
  8. package/dist/backend/emission/emit-shapes.js +1 -1
  9. package/dist/backend/emission/emit-walkers.d.ts +14 -14
  10. package/dist/backend/emission/emit-walkers.js +44 -44
  11. package/dist/backend/emission/emit-walkers.js.map +1 -1
  12. package/dist/backend/emission/emitter.d.ts +3 -3
  13. package/dist/backend/emission/emitter.js +33 -9
  14. package/dist/backend/emission/emitter.js.map +1 -1
  15. package/dist/backend/emission/may-throw.js +1 -1
  16. package/dist/backend/llvm/dyn.d.ts +10 -10
  17. package/dist/backend/llvm/dyn.js +26 -26
  18. package/dist/backend/llvm/dyn.js.map +1 -1
  19. package/dist/backend/llvm/emitter.js +156 -47
  20. package/dist/backend/llvm/emitter.js.map +1 -1
  21. package/dist/backend/llvm/shapes.js +1 -1
  22. package/dist/backend/llvm/walkers.js +2 -2
  23. package/dist/backend/llvm/walkers.js.map +1 -1
  24. package/dist/coverage/surface-manifest.js +16 -2
  25. package/dist/coverage/surface-manifest.js.map +1 -1
  26. package/dist/diagnostics/diagnostic.d.ts +21 -8
  27. package/dist/diagnostics/diagnostic.js +48 -14
  28. package/dist/diagnostics/diagnostic.js.map +1 -1
  29. package/dist/frontend/lowering/lib-boundary.js +5 -5
  30. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  31. package/dist/frontend/lowering/lower-assert.js +12 -12
  32. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  33. package/dist/frontend/lowering/lower-builtins.d.ts +63 -7
  34. package/dist/frontend/lowering/lower-builtins.js +327 -35
  35. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  36. package/dist/frontend/lowering/lower-calls.d.ts +4 -4
  37. package/dist/frontend/lowering/lower-calls.js +104 -70
  38. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  39. package/dist/frontend/lowering/lower-classes.d.ts +7 -0
  40. package/dist/frontend/lowering/lower-classes.js +82 -6
  41. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  42. package/dist/frontend/lowering/lower-containers.d.ts +9 -5
  43. package/dist/frontend/lowering/lower-containers.js +88 -26
  44. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  45. package/dist/frontend/lowering/lower-emitter.d.ts +70 -3
  46. package/dist/frontend/lowering/lower-emitter.js +438 -31
  47. package/dist/frontend/lowering/lower-emitter.js.map +1 -1
  48. package/dist/frontend/lowering/lower-exprs.d.ts +87 -12
  49. package/dist/frontend/lowering/lower-exprs.js +630 -125
  50. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  51. package/dist/frontend/lowering/lower-inspect.js +5 -5
  52. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  53. package/dist/frontend/lowering/lower-island.js +12 -3
  54. package/dist/frontend/lowering/lower-island.js.map +1 -1
  55. package/dist/frontend/lowering/lower-modules.d.ts +8 -5
  56. package/dist/frontend/lowering/lower-modules.js +157 -63
  57. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  58. package/dist/frontend/lowering/lower-namespaces.d.ts +24 -0
  59. package/dist/frontend/lowering/lower-namespaces.js +158 -16
  60. package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
  61. package/dist/frontend/lowering/lower-server.js +11 -11
  62. package/dist/frontend/lowering/lower-server.js.map +1 -1
  63. package/dist/frontend/lowering/lower-stmts.d.ts +1 -1
  64. package/dist/frontend/lowering/lower-stmts.js +271 -36
  65. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  66. package/dist/frontend/lowering/lower-stream.js +171 -20
  67. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  68. package/dist/frontend/lowering/lowerer.d.ts +27 -10
  69. package/dist/frontend/lowering/lowerer.js +156 -54
  70. package/dist/frontend/lowering/lowerer.js.map +1 -1
  71. package/dist/frontend/lowering/surfaces.d.ts +44 -0
  72. package/dist/frontend/lowering/surfaces.js +236 -5
  73. package/dist/frontend/lowering/surfaces.js.map +1 -1
  74. package/dist/frontend/npm.d.ts +12 -0
  75. package/dist/frontend/npm.js +30 -0
  76. package/dist/frontend/npm.js.map +1 -1
  77. package/dist/frontend/program.d.ts +25 -0
  78. package/dist/frontend/program.js +145 -136
  79. package/dist/frontend/program.js.map +1 -1
  80. package/dist/frontend/shared.d.ts +1 -1
  81. package/dist/frontend/shared.js +8 -8
  82. package/dist/frontend/shared.js.map +1 -1
  83. package/dist/frontend/types.d.ts +2 -2
  84. package/dist/frontend/types.js +15 -15
  85. package/dist/frontend/types.js.map +1 -1
  86. package/dist/index.d.ts +2 -1
  87. package/dist/index.js +169 -5
  88. package/dist/index.js.map +1 -1
  89. package/dist/ir/nodes.d.ts +174 -106
  90. package/dist/ir/nodes.js +69 -24
  91. package/dist/ir/nodes.js.map +1 -1
  92. package/dist/ir/validate.js +55 -36
  93. package/dist/ir/validate.js.map +1 -1
  94. package/dist/library/fence-eval.js +12 -2
  95. package/dist/library/fence-eval.js.map +1 -1
  96. package/dist/library/int-infer.d.ts +119 -0
  97. package/dist/library/int-infer.js +1314 -0
  98. package/dist/library/int-infer.js.map +1 -0
  99. package/dist/library/int-infer.test.d.ts +1 -0
  100. package/dist/library/int-infer.test.js +279 -0
  101. package/dist/library/int-infer.test.js.map +1 -0
  102. package/dist/library/profile.d.ts +34 -5
  103. package/dist/library/profile.js +77 -13
  104. package/dist/library/profile.js.map +1 -1
  105. package/dist/library/sidecar-validate.js +1 -1
  106. package/dist/library/sidecar-validate.js.map +1 -1
  107. package/dist/library/sidecar.d.ts +24 -1
  108. package/dist/library/sidecar.js +163 -16
  109. package/dist/library/sidecar.js.map +1 -1
  110. package/package.json +2 -2
  111. package/surface-manifest.json +210 -12
@@ -8,7 +8,7 @@ import * as ts from "../ts7/adapter.js";
8
8
  import { dirname } from "node:path";
9
9
  import { BOOL, CAUGHT, DYN, DYN_HANDLE_KINDS, F64, JSVAL, NULL_T, REF_TRUTHY_KINDS, REGEX, RUNTIME_ERROR_CLASSES, SEARCH_PARAMS_T, STRING, UNDEFINED_T, VOID, arrayOf, canAdaptDynFuncTo, canBoxFuncIntoDyn, canDynCheckTo, funcOf, isJsonSafeType, isUnitType, jsOpResultKind, shapeHasAccessorSlots, typeEquals, typeKey, unionFuncSetArmsOk } from "../../ir/nodes.js";
10
10
  import { cjsClassExprWholeExportOf, cjsExportAssignmentOf, cjsExportDiscardReason, isCjsExportTableLiteral, isCjsJsFile, isJsSourceFile, isModuleExportsAccess, isNodeEsmFile, locOf } from "../program.js";
11
- import { ARRAY_METHODS, builtinConstLit, builtinFenceHintOf, builtinModuleConstOf, builtinModuleFnOf, ISLAND_SURFACE, isChildSurfaceMember, MAP_METHODS, NARROW_FIRST, SET_METHODS, STR_METHODS, UNSUPPORTED_EXPR, sideEffectFreeOptionValue, stdlibGlobalNameOf } from "./surfaces.js";
11
+ import { ARRAY_METHODS, builtinConstLit, builtinFenceHintOf, builtinModuleConstOf, builtinModulesArrayLit, builtinModuleFnOf, ISLAND_SURFACE, isChildSurfaceMember, MAP_METHODS, NARROW_FIRST, SET_METHODS, STR_METHODS, UNSUPPORTED_EXPR, sideEffectFreeOptionValue, stdlibGlobalNameOf } from "./surfaces.js";
12
12
  import { blockedBindingUseDiag, recordShapeMismatchDiag, requiresDynamicPackageDiag, unsupportedDiag } from "../../diagnostics/diagnostic.js";
13
13
  import { PoisonError, dynUndefinedExpr, jsFuncNameOf, neverTaintedJsType, nodeThrowExpr, own } from "./lowerer.js";
14
14
  import { lowerIndexMergeHelper, lowerNpmStaticSafeIndexRead, strCharsCall } from "./lower-containers.js";
@@ -83,7 +83,22 @@ function lowerExprInner(L, expr) {
83
83
  if (chainRecv)
84
84
  return { ...chainRecv, loc };
85
85
  if (ts.isNumericLiteral(expr)) {
86
- return { kind: "numLit", value: Number(expr.text.replace(/_/g, "")), type: F64, loc };
86
+ const value = Number(expr.text.replace(/_/g, ""));
87
+ // Ask 4's representability input: a DECIMAL INTEGER source spelling
88
+ // that does not survive the trip through f64 (parse, format back,
89
+ // compare) rides the literal so the library integer-boundary check
90
+ // can refuse on the author's source text. `expr.text` is the
91
+ // scanner's COOKED value (already the nearest double), so the
92
+ // source spelling comes from the file text; numeric separators are
93
+ // spelling sugar and strip first. Round-tripping literals (every
94
+ // integer within ±(2^53−1)) and non-integer spellings carry
95
+ // nothing, so the IR is unchanged for programs that held their
96
+ // numbers.
97
+ const spelled = expr.getText().replace(/_/g, "");
98
+ if (/^\d+$/.test(spelled) && String(Number(spelled)) !== spelled) {
99
+ return { kind: "numLit", value: Number(spelled), spelling: spelled, type: F64, loc };
100
+ }
101
+ return { kind: "numLit", value, type: F64, loc };
87
102
  }
88
103
  if (expr.kind === ts.SyntaxKind.TrueKeyword) {
89
104
  return { kind: "boolLit", value: true, type: BOOL, loc };
@@ -207,9 +222,9 @@ function lowerExprInner(L, expr) {
207
222
  // A JS variable INITIALIZER whose BINDING registered as a
208
223
  // checked-dynamic module global (the typedef-annotated
209
224
  // doc-builder consts whose JSDoc types degraded — DYN slots):
210
- // the island build could never land (no engine→DOM crossing
225
+ // the island build could never land (no engine→dyn crossing
211
226
  // exists), so the literal takes its own world's path instead —
212
- // the DOM literal, or a static record the slot converts.
227
+ // the dyn literal, or a static record the slot converts.
213
228
  if (isJsSourceFile(expr.getSourceFile()) &&
214
229
  ts.isVariableDeclaration(expr.parent) &&
215
230
  expr.parent.initializer === expr &&
@@ -224,7 +239,7 @@ function lowerExprInner(L, expr) {
224
239
  // reference degraded to checker-`any` — the tsgo multi-file
225
240
  // value-as-type residue) is not an npm slot: when the literal's
226
241
  // OWN type maps statically, it builds that way — the binding's
227
- // checked-dynamic slot takes the DOM conversion. Genuinely
242
+ // checked-dynamic slot takes the dyn conversion. Genuinely
228
243
  // island slots (npm .d.ts provenance, plain `any`) keep the
229
244
  // native build.
230
245
  if (ctxTs !== undefined && (ctxTs.flags & ts.TypeFlags.Any) === 0) {
@@ -259,13 +274,22 @@ function lowerExprInner(L, expr) {
259
274
  const argsWith = [];
260
275
  const getters = [];
261
276
  let spread = null;
262
- // A MEMBER a JS file cannot lower or marshal (a generic function
263
- // as a value, a signature with checked-dynamic parameters — the
264
- // doc-builder public aggregate's degraded pieces): defer like a
265
- // statement fence — the slot holds a host closure that THROWS the
266
- // captured diagnostic when invoked, so building the aggregate
267
- // compiles and only a CALL through the island stops the run.
268
- const islandMemberFence = (diagsBefore, err, valueNode) => {
277
+ // A MEMBER a JS file cannot lower or marshal: defer like a
278
+ // statement fence, shaped by what the member IS. A FUNCTION-shaped
279
+ // member (a generic function as a value, a signature with
280
+ // checked-dynamic parameters — the doc-builder public aggregate's
281
+ // degraded pieces) becomes a host closure that THROWS the captured
282
+ // diagnostic when invoked — building the aggregate compiles and
283
+ // only a CALL through the island stops the run. A DATA-shaped
284
+ // member must NOT become a callable (the retired fence box's
285
+ // silent wrong answers: typeof said "function", downstream errors
286
+ // blamed the wrong thing — the withPlugins `plugins:` slot), so it
287
+ // defines through the engine's getter machinery instead: READING
288
+ // the member throws the diagnostic — the honest granularity, since
289
+ // using the value is exactly what cannot be answered. `getterName`
290
+ // null keeps the closure shape (function-shaped members, and the
291
+ // conditional-spread arms where getters cannot combine).
292
+ const islandMemberFence = (diagsBefore, err, valueNode, getterName = null) => {
269
293
  if (!(err instanceof PoisonError) ||
270
294
  !isJsSourceFile(expr.getSourceFile()) ||
271
295
  L.diagSink !== null ||
@@ -295,8 +319,33 @@ function lowerExprInner(L, expr) {
295
319
  loc: locOf(valueNode),
296
320
  });
297
321
  const fence = { kind: "closure", fnName, captures: [], type: funcOf([], VOID), loc: locOf(valueNode) };
322
+ if (getterName !== null) {
323
+ getters.push({ name: getterName, fn: L.jsvalIn(fence, valueNode), loc: locOf(valueNode) });
324
+ return null;
325
+ }
298
326
  return L.jsvalIn(fence, valueNode);
299
327
  };
328
+ // The member's SHAPE decides the fence's granularity: syntactic
329
+ // functions and checker-callable values keep the call-time
330
+ // closure; everything else (call results, awaits, data reads —
331
+ // the withPlugins `plugins:` shape) fences at the READ.
332
+ const funcShapedMember = (p) => {
333
+ if (ts.isMethodDeclaration(p))
334
+ return true;
335
+ const src = ts.isPropertyAssignment(p)
336
+ ? p.initializer
337
+ : ts.isShorthandPropertyAssignment(p)
338
+ ? p.name
339
+ : null;
340
+ if (!src || !ts.isExpression(src))
341
+ return true; // unknown form: keep the closure shape
342
+ let inner = src;
343
+ while (ts.isParenthesizedExpression(inner))
344
+ inner = inner.expression;
345
+ if (ts.isArrowFunction(inner) || ts.isFunctionExpression(inner))
346
+ return true;
347
+ return L.checker.getCallSignatures(L.typeOf(src)).length > 0;
348
+ };
300
349
  const pushProp = (name, value, valueNode, into) => {
301
350
  const diagsBefore = L.diags.length;
302
351
  let marshaled;
@@ -304,7 +353,15 @@ function lowerExprInner(L, expr) {
304
353
  marshaled = L.jsvalIn(value, valueNode);
305
354
  }
306
355
  catch (err) {
307
- marshaled = islandMemberFence(diagsBefore, err, valueNode);
356
+ // A value that LOWERED but cannot cross (a promise, a class
357
+ // instance): func-typed values keep the call-time closure;
358
+ // data-shaped values fence at the read (the getter), unless a
359
+ // conditional spread owns the literal (getters cannot combine).
360
+ const asGetter = value.type.kind !== "func" && spread === null;
361
+ const fence = islandMemberFence(diagsBefore, err, valueNode, asGetter ? name.text : null);
362
+ if (fence === null)
363
+ return; // registered as a fence getter — no data property
364
+ marshaled = fence;
308
365
  }
309
366
  for (const args of into) {
310
367
  args.push({
@@ -388,7 +445,11 @@ function lowerExprInner(L, expr) {
388
445
  : null;
389
446
  }
390
447
  catch (err) {
391
- value = islandMemberFence(valueDiagsBefore, err, prop);
448
+ const nameText = name && (ts.isIdentifier(name) || ts.isStringLiteral(name)) ? name.text : null;
449
+ const asGetter = nameText !== null && spread === null && !funcShapedMember(prop);
450
+ value = islandMemberFence(valueDiagsBefore, err, prop, asGetter ? nameText : null);
451
+ if (value === null)
452
+ continue; // registered as a fence getter — no data property
392
453
  }
393
454
  if (value && name && (ts.isIdentifier(name) || ts.isStringLiteral(name))) {
394
455
  pushProp(name, value, prop, [argsWithout, argsWith]);
@@ -479,7 +540,7 @@ function lowerExprInner(L, expr) {
479
540
  }
480
541
  // A checker-narrowed dyn read arrives as a VALIDATED extraction
481
542
  // (maybeNarrow's dynCheck bridge). typeof needs no extraction — the
482
- // DOM kind table answers the question directly, and answers it even
543
+ // dyn kind table answers the question directly, and answers it even
483
544
  // where the flow type lied (the extraction would throw instead) —
484
545
  // so unwrap back to the dyn value and take the dyn.typeof path.
485
546
  if (operand.kind === "dynCheck" && operand.value.type.kind === "dyn") {
@@ -524,7 +585,7 @@ function lowerExprInner(L, expr) {
524
585
  if (operand.type.kind === "dyn") {
525
586
  // Bare typeof on a dyn value: the runtime's kind→string table
526
587
  // (null answers "object", boxed closures "function" — JS-exact
527
- // for every DOM kind; "bigint"/"symbol" have no producers).
588
+ // for every dyn kind; "bigint"/"symbol" have no producers).
528
589
  return { kind: "libCall", fn: "dyn.typeof", args: [operand], type: STRING, loc };
529
590
  }
530
591
  L.unsupported("SC1090", expr, "typeof expressions on statically-typed values");
@@ -534,7 +595,7 @@ function lowerExprInner(L, expr) {
534
595
  return { kind: "selfRef", type: L.ctx.selfType, loc };
535
596
  }
536
597
  // `arguments` in a variadic JS function (the rest-marked form): the
537
- // synthetic trailing DOM-array param — lambdaSignature marked the
598
+ // synthetic trailing dyn-array param — lambdaSignature marked the
538
599
  // type and lowerLambda declared the local.
539
600
  if (expr.text === "arguments" && L.ctx.argumentsLocal) {
540
601
  return { kind: "varRef", localId: L.ctx.argumentsLocal.id, type: DYN, loc };
@@ -603,7 +664,7 @@ function lowerExprInner(L, expr) {
603
664
  // optional/default/rest declarations pass the value fence first.
604
665
  L.noteEdge(sig.name);
605
666
  // dynRest slots stay out of the VALUE type's param list (fn.length
606
- // semantics); the rest marker carries the trailing DOM-array ABI.
667
+ // semantics); the rest marker carries the trailing dyn-array ABI.
607
668
  const funcType = {
608
669
  kind: "func",
609
670
  params: sig.params.filter((p) => p.mode !== "dynRest").map((p) => p.type),
@@ -649,6 +710,11 @@ function lowerExprInner(L, expr) {
649
710
  const c = builtinModuleConstOf(L, bi.module, bi.member);
650
711
  if (c !== undefined)
651
712
  return builtinConstLit(c, loc);
713
+ // module.builtinModules — the baked Node v24 list, a fresh
714
+ // string[] per read.
715
+ if (bi.module === "module" && bi.member === "builtinModules") {
716
+ return builtinModulesArrayLit(loc);
717
+ }
652
718
  // JavaScript sources: a builtin member taken as a bare VALUE is
653
719
  // the same identity-token story as stdlib globals above (the
654
720
  // harness adds worker_threads.Worker to its identity Set).
@@ -979,7 +1045,7 @@ function lowerExprInner(L, expr) {
979
1045
  if (value.type.kind !== "promise") {
980
1046
  // A CHECKED-DYNAMIC operand (`await v` where v rode an untyped
981
1047
  // binding — a destructured helper's return, a dyn call result):
982
- // the runtime decides — a DOM promise adopts (rejections
1048
+ // the runtime decides — a dyn promise adopts (rejections
983
1049
  // re-throw), anything else takes JS's one-hop non-thenable await
984
1050
  // and answers itself. Thenable adoption stays unmodeled
985
1051
  // (SEMANTICS.md).
@@ -1137,9 +1203,9 @@ function lowerExprInner(L, expr) {
1137
1203
  // bound handle).
1138
1204
  if (L.isIslandExpr(expr.expression)) {
1139
1205
  const receiver = L.lowerExpr(expr.expression);
1140
- // The checker said 'any' but the VALUE lives in the DOM (`this`
1206
+ // The checker said 'any' but the VALUE lives in the checked-dynamic tree (`this`
1141
1207
  // in a plain JS function — dyn.this — or a checked-dynamic local
1142
- // behind an any-typed spelling): the property read is the DOM's
1208
+ // behind an any-typed spelling): the property read is the checked-dynamic tree's
1143
1209
  // own keyed read, dyn results and dyn chains exactly like every
1144
1210
  // checked-dynamic member access (a nullish receiver throws V8's
1145
1211
  // catchable TypeError, exactly Node). Never a jsOp over a dyn —
@@ -1177,6 +1243,15 @@ function lowerExprInner(L, expr) {
1177
1243
  }
1178
1244
  return read;
1179
1245
  }
1246
+ // `m.groups` on a match result whose regex is statically known:
1247
+ // the compile-time record projection over the honest slice (or
1248
+ // Node's undefined when the pattern has no named groups) — see
1249
+ // lowerMatchGroupsRead.
1250
+ {
1251
+ const g = lowerMatchGroupsRead(L, expr);
1252
+ if (g !== null)
1253
+ return g;
1254
+ }
1180
1255
  // `m.index` on a for-of-over-matchAll binding reads the companion-
1181
1256
  // index array at this iteration's cursor — always a number: every
1182
1257
  // drained row matched, so the lib's `.index` is never undefined
@@ -1414,14 +1489,14 @@ function lowerExprInner(L, expr) {
1414
1489
  // .length` where the binding is an evolving any[] living as a
1415
1490
  // checked-dynamic global): the stdlib member fence below would blame
1416
1491
  // the unmappable checker type (`any[].length`), but the VALUE is a
1417
- // DOM node — lower the receiver first and read through the DOM keyed
1492
+ // dyn node — lower the receiver first and read through the dyn keyed
1418
1493
  // read (ARR answers length; everything else JS's own-property
1419
1494
  // answer). Mapped receivers keep the fence-first order: their
1420
1495
  // members' gaps are real surface gaps ([1,2].entries), not
1421
1496
  // representation artifacts. `.length` on a receiver whose checker
1422
1497
  // ARRAY type lowers to the checked-dynamic representation —
1423
1498
  // `unknown[]`, the collapsed `(string | object)[]`, and the `any[]`
1424
- // an Array.isArray guard narrows a collapsed union to — is the DOM
1499
+ // an Array.isArray guard narrows a collapsed union to — is the checked-dynamic tree
1425
1500
  // array's OWN length (a keyed read the ARR kind answers exactly),
1426
1501
  // never an `Array.prototype` surface gap; both source languages.
1427
1502
  // Prototype-method VALUE reads (`ps.map` unparenthesized) keep the
@@ -1431,7 +1506,7 @@ function lowerExprInner(L, expr) {
1431
1506
  if ((isJsSourceFile(expr.getSourceFile()) &&
1432
1507
  (L.mapTypeOf(L.typeOf(expr.expression)) === null ||
1433
1508
  // A never-tainted receiver type maps (never rides as f64) but
1434
- // its VALUE lowered checked-dynamic — same DOM read.
1509
+ // its VALUE lowered checked-dynamic — same dyn read.
1435
1510
  neverTaintedJsType(L, expr.expression, L.typeOf(expr.expression)))) ||
1436
1511
  (expr.name.text === "length" &&
1437
1512
  (L.checkerAnyArray(expr.expression) ||
@@ -1542,7 +1617,7 @@ function lowerExprInner(L, expr) {
1542
1617
  // the property and the receiver's type instead of "property access".
1543
1618
  const recvLowered = L.lowerExpr(expr.expression);
1544
1619
  // A dyn receiver — JSON.parse's `any`, or `unknown` the checker
1545
- // narrowed to `object` — reads through the DOM keyed read: member
1620
+ // narrowed to `object` — reads through the dyn keyed read: member
1546
1621
  // or undefined (JS's own-property answer), throwing JS's TypeError
1547
1622
  // on undefined/null receivers unless an earlier `?.` guards the
1548
1623
  // chain. Scalar-narrowed occurrences bridge via maybeNarrow's
@@ -1552,6 +1627,31 @@ function lowerExprInner(L, expr) {
1552
1627
  const opt = chainGuardedByQuestionDot(expr.expression);
1553
1628
  return L.maybeNarrow({ kind: "dynKeyGet", key, ...(opt ? { optional: true } : {}), value: recvLowered, type: DYN, loc }, expr);
1554
1629
  }
1630
+ // The lowered receiver is a RECORD the checker spelled wider —
1631
+ // `s.match(re).groups.key` in a JS file: the checker says
1632
+ // `{ [key: string]: string } | undefined`, but the groups
1633
+ // projection already answered the record arm (its null trap
1634
+ // included). Declared fields read directly; an index-signature
1635
+ // shape serves undeclared keys through the overflow read, exactly
1636
+ // the dot-access rule on checker-spelled hybrids.
1637
+ if (recvLowered.type.kind === "record") {
1638
+ const recvShape = L.shapes.get(recvLowered.type.shapeId);
1639
+ const field = recvShape?.fields.find((f) => f.name === expr.name.text);
1640
+ if (field) {
1641
+ return L.maybeNarrow({ kind: "recordGet", obj: recvLowered, shapeId: recvLowered.type.shapeId, field: field.name, type: field.type, loc }, expr);
1642
+ }
1643
+ if (recvShape?.indexValue !== undefined && !recvShape.tuple) {
1644
+ return L.maybeNarrow({
1645
+ kind: "recordKeyGet",
1646
+ obj: recvLowered,
1647
+ shapeId: recvLowered.type.shapeId,
1648
+ key: { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) },
1649
+ overflowOnly: true,
1650
+ type: recvShape.indexValue,
1651
+ loc,
1652
+ }, expr);
1653
+ }
1654
+ }
1555
1655
  // An ABSTRACT property through an abstract-typed receiver: the
1556
1656
  // declaration is erased at runtime — Node defines no field for it,
1557
1657
  // each concrete subclass declares its OWN (at its own layout
@@ -1763,11 +1863,11 @@ export function lowerTernary(L, expr, expected) {
1763
1863
  // === "string" ? pkg.name : null` — the lowering world types the
1764
1864
  // unknown-receiver read `any`, which a static build cannot hold):
1765
1865
  // dyn represents null and undefined directly, so the ternary stays
1766
- // dyn — a unit arm converts to the DOM unit value (the dynFrom form
1866
+ // dyn — a unit arm converts to the dyn unit value (the dynFrom form
1767
1867
  // the index-signature lowerings already use). A FRESH literal arm
1768
1868
  // (`... ? pkg.scripts : {}` — the default-object idiom) converts the
1769
1869
  // same way when it is JSON-safe: nothing else aliases a literal, so
1770
- // the DOM copy is unobservable.
1870
+ // the dyn copy is unobservable.
1771
1871
  const dynish = (e) => e.type.kind === "dyn" ||
1772
1872
  e.kind === "unitLit" ||
1773
1873
  ((e.kind === "recordLit" || e.kind === "arrayLit") && L.dynConvertible(e.type));
@@ -1853,13 +1953,13 @@ export function maybeNarrow(L, expr, node) {
1853
1953
  (narrowed.kind === "f64" || narrowed.kind === "bool" || narrowed.kind === "string")) {
1854
1954
  return { kind: "dynCheck", value: expr, type: narrowed, loc: expr.loc };
1855
1955
  }
1856
- // An `instanceof Uint8Array` narrow: the DOM's bytes kind, extracted
1956
+ // An `instanceof Uint8Array` narrow: the checked-dynamic tree's bytes kind, extracted
1857
1957
  // with the same validated copy the checked cast uses (a Buffer that
1858
1958
  // crossed in rides the kind too — it IS a Uint8Array in Node).
1859
1959
  if (narrowed?.kind === "bytes" && narrowed.elem === "u8") {
1860
1960
  return { kind: "dynCheck", value: expr, type: narrowed, loc: expr.loc };
1861
1961
  }
1862
- // An `instanceof Error` narrow: the DOM's error encoding rebuilds a
1962
+ // An `instanceof Error` narrow: the checked-dynamic tree's error encoding rebuilds a
1863
1963
  // fresh %Error (name/message/code from the marker object — a COPY,
1864
1964
  // the unknown boundary's stance; SEMANTICS.md 67), validated like
1865
1965
  // every dyn extraction.
@@ -2035,7 +2135,7 @@ export function lowerLooseNullCompare(L, expr, loc) {
2035
2135
  return { kind: "boolLit", value: !negated, type: BOOL, loc };
2036
2136
  }
2037
2137
  if (other.type.kind === "dyn") {
2038
- // `v != null` on unknown: one DOM kind test covers both units.
2138
+ // `v != null` on unknown: one dyn kind test covers both units.
2039
2139
  return {
2040
2140
  kind: "dynTest",
2041
2141
  test: "nullish",
@@ -2194,7 +2294,7 @@ export function lowerNullishCoalesce(L, expr, loc) {
2194
2294
  // runtime kind (scr_dyn_is_nullish — UNDEF/NULL take the default;
2195
2295
  // a wrapped island value routes to the engine's test, defensively:
2196
2296
  // the wrap constructor scalar-normalizes engine null/undefined
2197
- // away). Both sides live in the DOM — the right converts through
2297
+ // away). Both sides live in the checked-dynamic tree — the right converts through
2198
2298
  // the usual boundary and evaluates lazily in its branch; a default
2199
2299
  // with no dyn representation keeps the fence.
2200
2300
  const right = L.coerceToExpected(L.lowerExpr(expr.right), DYN);
@@ -2476,7 +2576,7 @@ export function lowerOptionalChain(L, expr) {
2476
2576
  }
2477
2577
  // NUMBER-typed indices (`entries?.[0]`): the property key is
2478
2578
  // ToString(i), exactly JS — the canonical number text answers
2479
- // array indices in the DOM helper, anything else (fractions,
2579
+ // array indices in the dyn helper, anything else (fractions,
2480
2580
  // negatives, NaN) reads as an absent key.
2481
2581
  if (key.type.kind === "f64") {
2482
2582
  const skey = { kind: "toString", operand: key, type: STRING, loc: key.loc };
@@ -2484,12 +2584,12 @@ export function lowerOptionalChain(L, expr) {
2484
2584
  }
2485
2585
  }
2486
2586
  // The METHOD-call step (`rawName?.match(re)` on a dyn value): a
2487
- // nullish receiver short-circuits to the undefined DOM singleton
2587
+ // nullish receiver short-circuits to the undefined dyn singleton
2488
2588
  // (dyn represents undefined directly); anything else runs the
2489
2589
  // VALIDATED dynamic dispatch — the same one the truthy-guarded
2490
2590
  // spelling (`v ? v.match(...) : null`) compiles to, Node-shaped
2491
2591
  // TypeError on a kind mismatch included. The dispatch's static
2492
- // result converts back into the DOM (the chain's checker type is
2592
+ // result converts back into the checked-dynamic tree (the chain's checker type is
2493
2593
  // the error-any world's `any`, so dyn IS its representation).
2494
2594
  if (ts.isCallExpression(expr) &&
2495
2595
  ts.isPropertyAccessExpression(dotNode) &&
@@ -2660,7 +2760,7 @@ export function finishOptionalChain(L, expr, id, receiver, body, loc) {
2660
2760
  }
2661
2761
  // A dyn body (`pricing?.[key]` — an unknown-valued index-signature
2662
2762
  // read) stays dyn: `unknown | undefined` IS unknown, and dyn represents
2663
- // undefined directly (the unit path yields the undefined DOM value).
2763
+ // undefined directly (the unit path yields the undefined dyn value).
2664
2764
  if (body.type.kind === "dyn") {
2665
2765
  return { kind: "optChain", id, receiver, body, type: DYN, loc };
2666
2766
  }
@@ -2716,8 +2816,8 @@ export function ensureBool(L, e, node) {
2716
2816
  return { kind: "toBool", operand: e, type: BOOL, loc: e.loc };
2717
2817
  }
2718
2818
  if (e.type.kind === "dyn") {
2719
- // ToBoolean over the DOM (`if (pkg)` on a JSON.parse result): every
2720
- // DOM kind has a JS-exact answer — the truthy dynTest reads the kind
2819
+ // ToBoolean over the checked-dynamic tree (`if (pkg)` on a JSON.parse result): every
2820
+ // dyn kind has a JS-exact answer — the truthy dynTest reads the kind
2721
2821
  // tag (plus the scalar payload for number/string).
2722
2822
  return { kind: "dynTest", test: "truthy", value: e, type: BOOL, loc: e.loc };
2723
2823
  }
@@ -2743,7 +2843,7 @@ export function ensureBool(L, e, node) {
2743
2843
  L.badType(node, L.typeOf(node));
2744
2844
  }
2745
2845
  /** Truthiness needs a ToBoolean per arm: dyn/caught arms have none (a
2746
- * dynamic ToBoolean over the DOM / the snapshot box) — fence those
2846
+ * dynamic ToBoolean over the checked-dynamic tree / the snapshot box) — fence those
2747
2847
  * unions; every other arm kind is answerable (units false, scalars and
2748
2848
  * strings by value, refs true, jsval by the engine). */
2749
2849
  export function requireTruthyUnion(L, unionId, node) {
@@ -2772,7 +2872,7 @@ export function lowerIntrinsicProperty(L, expr) {
2772
2872
  return null;
2773
2873
  // A never-tainted JS receiver type lowered checked-dynamic
2774
2874
  // (neverTaintedJsType — `cmd.length` on `const cmd = ['pwd', []]`):
2775
- // stand down so the DOM keyed read below the chain answers, instead
2875
+ // stand down so the dyn keyed read below the chain answers, instead
2776
2876
  // of an array libCall over a dyn receiver hitting the boundary fence.
2777
2877
  let kind = neverTaintedJsType(L, expr.expression, L.typeOf(expr.expression))
2778
2878
  ? undefined
@@ -2994,8 +3094,8 @@ export function lowerIntrinsicProperty(L, expr) {
2994
3094
  // No ArrayBuffer VALUE exists — `.buffer` compiles only in the
2995
3095
  // one composed position that consumes it, the DataView
2996
3096
  // constructor's first argument (lowerDataViewNew peels it).
2997
- L.unsupported("SC1090", expr, "'.buffer' outside new DataView(x.buffer, ...) — scriptc typed arrays own their storage: " +
2998
- "no free-standing ArrayBuffer value exists, and slice()/subarray() copy");
3097
+ L.unsupported("SC1090", expr, "'.buffer' outside new DataView(x.buffer, ...) / Buffer.from(x.buffer, ...) " +
3098
+ "(no free-standing ArrayBuffer value exists; subarray() answers an aliasing view directly)");
2999
3099
  }
3000
3100
  if (name === "slice" || name === "subarray" || name === "set" || name === "toString") {
3001
3101
  L.unsupported("SC1090", expr, `typed-array methods as values (call '${name}' directly)`);
@@ -3036,8 +3136,8 @@ export function lowerIntrinsicProperty(L, expr) {
3036
3136
  return { kind: "jsExit", value: read, type: F64, loc: locOf(expr) };
3037
3137
  }
3038
3138
  // A CHECKED-DYNAMIC value behind an array-typed checker spelling
3039
- // (`Object.keys(u).length` — the dyn walk answers a DOM array while
3040
- // tsc spells string[]): the runtime-world dispatch — a DOM keyed
3139
+ // (`Object.keys(u).length` — the dyn walk answers a dyn array while
3140
+ // tsc spells string[]): the runtime-world dispatch — a dyn keyed
3041
3141
  // read validated into the declared number (a lying length throws
3042
3142
  // the catchable TypeError, the dynCheck stance).
3043
3143
  if (receiver.type.kind === "dyn") {
@@ -3080,7 +3180,7 @@ export function lowerArrayLiteral(L, expr, expected) {
3080
3180
  // A JS literal whose OWN inferred type is never-tainted
3081
3181
  // (neverTaintedJsType — the evolving `const gb = []`, the mixed
3082
3182
  // command tuple `['pwd', []]`) carries no element information: route
3083
- // it to the DOM fallback below rather than letting never's f64
3183
+ // it to the checked-dynamic tree fallback below rather than letting never's f64
3084
3184
  // representation build a static number array (a later dyn push would
3085
3185
  // throw "expected number at $, got string"; the tuple's union arm
3086
3186
  // would re-tag as number[] and fence). A REAL slot still wins:
@@ -3210,14 +3310,14 @@ export function lowerArrayLiteral(L, expr, expected) {
3210
3310
  if (!mapped || mapped.kind !== "array") {
3211
3311
  // The JS declaration fallback, literal-side: an element type with no
3212
3312
  // static home (a string | string[] mixed command tuple, an evolving
3213
- // []) builds as a DOM ARRAY — one dyn value whose elements each
3313
+ // []) builds as a dyn ARRAY — one dyn value whose elements each
3214
3314
  // convert through the usual boundary (dynFrom's JSON-safe domain);
3215
3315
  // an element that cannot convert fences per element. length/index
3216
- // reads and dynamic consumers ride the keyed-DOM paths. TS literals
3316
+ // reads and dynamic consumers ride the keyed-dyn paths. TS literals
3217
3317
  // take the same build when the slot ITSELF is checked-dynamic — an
3218
3318
  // `unknown[]` annotation or a collapsed `(string | object)[]` maps
3219
3319
  // to DYN wholesale now (mapType's dyn-element array rule), so the
3220
- // literal IS the DOM array.
3320
+ // literal IS the dyn array.
3221
3321
  if (isJsSourceFile(expr.getSourceFile()) || mapped?.kind === "dyn") {
3222
3322
  const elems = expr.elements.map((el) => {
3223
3323
  if (ts.isSpreadElement(el)) {
@@ -3503,12 +3603,12 @@ function propNameText(L, name) {
3503
3603
  return name.text;
3504
3604
  }
3505
3605
  /** The runtime-keyed JS object literal (a computed key that doesn't fold):
3506
- * builds a DOM object member-by-member. Keys evaluate before their values,
3606
+ * builds a dyn object member-by-member. Keys evaluate before their values,
3507
3607
  * properties in source order — JS's object-literal evaluation exactly.
3508
3608
  * Identifier/string keys are compile-time strings; numeric keys take
3509
3609
  * their canonical number string (`{ 0x10: v }` stores "16" — JS's
3510
3610
  * ToPropertyKey); computed keys evaluate and pass through ToString (the
3511
- * DOM's String() for dyn operands — `{ [field]: v }` where field is a
3611
+ * dyn's String() for dyn operands — `{ [field]: v }` where field is a
3512
3612
  * checked-dynamic param). Values convert through the usual dyn boundary
3513
3613
  * (dynFrom's domain, functions box); a value with no dyn representation
3514
3614
  * fences per property. Spreads, accessors, and methods stay fenced —
@@ -3601,7 +3701,7 @@ export function lowerDynObjectLiteral(L, expr) {
3601
3701
  L.unsupported("SC1101", valueExpr, `holding '${L.fmt(v.type)}' values in a runtime-keyed (computed-key) object literal`);
3602
3702
  }
3603
3703
  catch (err) {
3604
- // A member value the DOM cannot hold — a func whose signature
3704
+ // A member value the checked-dynamic tree cannot hold — a func whose signature
3605
3705
  // cannot box, an island ('any') handle, a record carrying func
3606
3706
  // fields (the export aggregate's typed utilities and npm-handle
3607
3707
  // members): the same call-time fence deferral as an unlowerable
@@ -3671,7 +3771,7 @@ function fenceClosureProbe(L, node, slotType, attempt) {
3671
3771
  return null;
3672
3772
  }
3673
3773
  // The trap's ABI: the field's func slot when one exists; otherwise (the
3674
- // DOM-object literal path — the value boxes into dyn) the all-dyn
3774
+ // dyn-object literal path — the value boxes into dyn) the all-dyn
3675
3775
  // signature of the lambda's own arity, which canBoxFuncIntoDyn always
3676
3776
  // admits.
3677
3777
  const fieldType = slotType !== undefined && slotType.kind === "func"
@@ -3814,7 +3914,7 @@ export function lowerObjectLiteral(L, expr) {
3814
3914
  const loc = locOf(expr);
3815
3915
  // The RUNTIME-KEYED literal (JS): a computed key that doesn't fold to a
3816
3916
  // compile-time string means the literal's shape is not a compile-time
3817
- // fact — no record shape can hold it. The whole literal builds as a DOM
3917
+ // fact — no record shape can hold it. The whole literal builds as a dyn
3818
3918
  // object instead (`{ [field]: criteria, actual: 0, ... }` —
3819
3919
  // test/common's _mustCallInner context), where keys are runtime string
3820
3920
  // values. TypeScript keeps the record world and its fence: this shape
@@ -4063,13 +4163,13 @@ export function lowerObjectLiteral(L, expr) {
4063
4163
  const shapeId = L.shapes.intern(fields.map((f) => ({ name: f.name, type: f.value.type })));
4064
4164
  return { kind: "recordLit", fields, type: { kind: "record", shapeId }, loc };
4065
4165
  }
4066
- // The JS declaration fallback, literal-side (the DOM-array rule's
4166
+ // The JS declaration fallback, literal-side (the checked-dynamic tree-array rule's
4067
4167
  // object form): a literal whose shape has no static home — an
4068
4168
  // unmappable contextual type over unmappable own fields (the
4069
4169
  // PropertyDescriptorMap argument of Object.defineProperties, nested
4070
- // descriptor records with `any` values) — builds as a DOM OBJECT:
4170
+ // descriptor records with `any` values) — builds as a dyn OBJECT:
4071
4171
  // each field converts through the usual dyn boundary, and dynamic
4072
- // consumers ride the keyed-DOM paths. TypeScript keeps the fence.
4172
+ // consumers ride the keyed-dyn paths. TypeScript keeps the fence.
4073
4173
  if ((!mapped || mapped.kind === "dyn") && isJsSourceFile(expr.getSourceFile())) {
4074
4174
  return lowerDynObjectLiteral(L, expr);
4075
4175
  }
@@ -4335,8 +4435,8 @@ export function lowerObjectLiteral(L, expr) {
4335
4435
  const recArm = recArms[0];
4336
4436
  const recTag = def.arms.indexOf(recArm);
4337
4437
  // A checked-dynamic VALUE under the union-mapped checker type
4338
- // (a JS DOM-holding binding): the present/absent desugar tests
4339
- // union tags a DOM box does not carry — fence honestly instead
4438
+ // (a JS dyn-holding binding): the present/absent desugar tests
4439
+ // union tags a dyn box does not carry — fence honestly instead
4340
4440
  // of the validator's ICE.
4341
4441
  const probedSrc = srcLowered ?? probeLower(L, srcNode);
4342
4442
  if (probedSrc?.type.kind === "dyn") {
@@ -4518,11 +4618,11 @@ export function lowerObjectLiteral(L, expr) {
4518
4618
  throw new PoisonError();
4519
4619
  }
4520
4620
  const obj = srcLowered ?? L.lowerExpr(srcNode);
4521
- // A record-mapped CHECKER type whose VALUE lives in the DOM (a
4621
+ // A record-mapped CHECKER type whose VALUE lives in the checked-dynamic tree (a
4522
4622
  // JS file-scope object-literal global): read each field from
4523
- // the DOM (dynKeyGet) and VALIDATE it into the source shape's
4623
+ // the checked-dynamic tree (dynKeyGet) and VALIDATE it into the source shape's
4524
4624
  // field type (dynCheck) — the checked-dynamic member-read
4525
- // discipline. A missing key answers the DOM undefined, exactly
4625
+ // discipline. A missing key answers the dyn undefined, exactly
4526
4626
  // the undefined-armed optional's absent case; a mismatched
4527
4627
  // runtime value throws the catchable TypeError, never a silent
4528
4628
  // wrong copy. Runtime-ADDED keys drop — width subtyping in
@@ -4530,7 +4630,7 @@ export function lowerObjectLiteral(L, expr) {
4530
4630
  let value;
4531
4631
  if (obj.type.kind === "dyn") {
4532
4632
  if (!canDynCheckTo(f.type, (id) => L.shapes.get(id), (id) => L.unions.get(id))) {
4533
- L.unsupported("SC1100", prop, `object spread of a checked-dynamic source whose field '${f.name}' ('${L.fmt(f.type)}') cannot validate out of the DOM (copy the fields explicitly)`);
4633
+ L.unsupported("SC1100", prop, `object spread of a checked-dynamic source whose field '${f.name}' ('${L.fmt(f.type)}') cannot validate out of the checked-dynamic tree (copy the fields explicitly)`);
4534
4634
  }
4535
4635
  value = {
4536
4636
  kind: "dynCheck",
@@ -4669,7 +4769,7 @@ export function lowerObjectLiteral(L, expr) {
4669
4769
  // lowering: a union field with one array-family arm builds the
4670
4770
  // literal AS that arm (lowerExprExpecting's IR-directed rule —
4671
4771
  // the option-table `default: [{ value: [] }]` shape), where the
4672
- // bare lowering would take the JS DOM fallback and fence.
4772
+ // bare lowering would take the JS dyn fallback and fence.
4673
4773
  let init = prop.initializer;
4674
4774
  while (ts.isParenthesizedExpression(init))
4675
4775
  init = init.expression;
@@ -4714,7 +4814,7 @@ export function lowerObjectLiteral(L, expr) {
4714
4814
  }
4715
4815
  if (!fieldType) {
4716
4816
  // Overflow entry: the value flows into the index signature's value
4717
- // slot — dyn slots take a DOM conversion (dynFrom), typed slots the
4817
+ // slot — dyn slots take a dyn conversion (dynFrom), typed slots the
4718
4818
  // ordinary coercion path. Later duplicates override (map semantics
4719
4819
  // are last-write-wins already; a duplicate literal key is a tsc
4720
4820
  // error anyway).
@@ -4756,7 +4856,7 @@ export function lowerObjectLiteral(L, expr) {
4756
4856
  for (const f of shape.fields) {
4757
4857
  if (provided.has(f.name))
4758
4858
  continue;
4759
- // 'unknown' fields complete with the DOM undefined — the absent
4859
+ // 'unknown' fields complete with the dyn undefined — the absent
4760
4860
  // property reads as undefined in Node, and a dyn slot holds
4761
4861
  // exactly that (the options-record call shape against
4762
4862
  // `{ plugins: unknown, ... }` — a JS caller the checker admits).
@@ -5117,7 +5217,7 @@ export function lowerElementAccess(L, expr) {
5117
5217
  // A never-tainted JS receiver type (neverTaintedJsType — `cmd[1]` on
5118
5218
  // `const cmd = ['pwd', []]`, whose binding lowered checked-dynamic)
5119
5219
  // dispatches as unmapped so the dyn-receiver branch below reads
5120
- // through the DOM instead of dynChecking into never's f64 residue.
5220
+ // through the checked-dynamic tree instead of dynChecking into never's f64 residue.
5121
5221
  const receiverIr = neverTaintedJsType(L, expr.expression, L.typeOf(expr.expression))
5122
5222
  ? null
5123
5223
  : L.mapTypeOf(L.typeOf(expr.expression));
@@ -5125,7 +5225,7 @@ export function lowerElementAccess(L, expr) {
5125
5225
  // Dispatch follows the RUNTIME world (383(d)): a checker-'any'
5126
5226
  // receiver whose value LOWERED checked-dynamic (`bag.list[0]` where
5127
5227
  // bag.list is a routed keyed read off a wrapped island value) takes
5128
- // the DOM keyed read below — the routed JSVAL arm reads the real
5228
+ // the dyn keyed read below — the routed JSVAL arm reads the real
5129
5229
  // engine element; a jsval-lowered receiver keeps the island read.
5130
5230
  const recv = L.lowerExpr(expr.expression);
5131
5231
  if (recv.type.kind !== "dyn")
@@ -5204,7 +5304,7 @@ export function lowerElementAccess(L, expr) {
5204
5304
  }
5205
5305
  }
5206
5306
  // `pkg["k"]` / `scripts[name]` / `scopeMatch[1]` on a dyn receiver (a
5207
- // JSON.parse result): the DOM keyed read, exactly the dot form.
5307
+ // JSON.parse result): the dyn keyed read, exactly the dot form.
5208
5308
  // NUMBER-typed indices convert through ToString first — JS property
5209
5309
  // keys are strings, and the canonical number text answers array
5210
5310
  // indices in the helper (fractions, negatives, and NaN read as
@@ -5314,7 +5414,7 @@ export function lowerElementAccess(L, expr) {
5314
5414
  return islandElementRead(L, expr, arr);
5315
5415
  // The checker sees an array but the VALUE lowered checked-dynamic (a
5316
5416
  // jsdoc-typed default export from a .js module — signature 14): the
5317
- // read rides the dynCheck boundary when the array shape is one the DOM
5417
+ // read rides the dynCheck boundary when the array shape is one the checked-dynamic tree
5318
5418
  // can validate, and fences by name when it is not — an arrayGet over a
5319
5419
  // dyn receiver is never emitted (the validator ICE).
5320
5420
  if (arr.type.kind === "dyn") {
@@ -5390,7 +5490,7 @@ function islandElementRead(L, expr, obj) {
5390
5490
  * the index signature's value type (dyn for `unknown`), or its
5391
5491
  * undefined-armed union under noUncheckedIndexedAccess. Every declared
5392
5492
  * field must be able to SURFACE as that type (equal, an arm of it, or —
5393
- * for dyn results — JSON-safe for the DOM conversion); shapes mixing in
5493
+ * for dyn results — JSON-safe for the dyn conversion); shapes mixing in
5394
5494
  * fields outside that stay fenced. Declared-only shapes support reads
5395
5495
  * whose key type proves membership (tsc's keyof check): all fields must
5396
5496
  * share the result type, and a smuggled miss traps. */
@@ -5409,9 +5509,9 @@ export function lowerRecordKeyRead(L, expr, shapeId, shape) {
5409
5509
  // The receiver lowers FIRST (both branches below read it, and JS
5410
5510
  // evaluates the receiver before the key).
5411
5511
  const obj = L.lowerExpr(expr.expression);
5412
- // A record-mapped CHECKER type over a VALUE living in the DOM (a JS
5512
+ // A record-mapped CHECKER type over a VALUE living in the checked-dynamic tree (a JS
5413
5513
  // file-scope object-literal global): the checked-dynamic keyed read —
5414
- // dynKeyGet against the runtime keys (a missing key answers the DOM
5514
+ // dynKeyGet against the runtime keys (a missing key answers the checked-dynamic tree
5415
5515
  // undefined, exactly JS); consumers validate (dynCheck) where a
5416
5516
  // static type is required, the member-read discipline.
5417
5517
  if (obj.type.kind === "dyn") {
@@ -5520,8 +5620,8 @@ export function recordKeyTypeLiteralText(L, node) {
5520
5620
  }
5521
5621
  /** Can every value a dynamic key can reach (declared fields + the
5522
5622
  * overflow) surface as the read's result type? dyn results need
5523
- * DOM-convertible fields (JSON-safe, with the undefined arm allowed at
5524
- * the top — an optional field's undefined becomes the undefined DOM
5623
+ * dyn-convertible fields (JSON-safe, with the undefined arm allowed at
5624
+ * the top — an optional field's undefined becomes the undefined dyn
5525
5625
  * value, exactly what the missing-key path produces); typed results need
5526
5626
  * each field to be the type itself or one of its arms, and the overflow
5527
5627
  * value to be the type or wrappable into it. */
@@ -5589,7 +5689,7 @@ export function lowerElementWrite(L, expr) {
5589
5689
  if (receiverIr?.kind === "jsval") {
5590
5690
  const obj = L.lowerExpr(target.expression);
5591
5691
  // Dispatch follows the RUNTIME world (383(d)): a checker-'any'
5592
- // receiver whose value LOWERED checked-dynamic takes the DOM keyed
5692
+ // receiver whose value LOWERED checked-dynamic takes the dyn keyed
5593
5693
  // write — the routed JSVAL arm lands it on the real engine object.
5594
5694
  if (obj.type.kind === "dyn") {
5595
5695
  const loc = locOf(expr);
@@ -5616,12 +5716,12 @@ export function lowerElementWrite(L, expr) {
5616
5716
  return { kind: "exprStmt", expr: { kind: "jsOp", op: "setIdx", args: [obj, key, value], type: VOID, loc }, loc };
5617
5717
  }
5618
5718
  // A checked-dynamic receiver TYPE (`unknown[]` and the collapsed
5619
- // `(string | object)[]` map to DYN wholesale now): the DOM keyed
5719
+ // `(string | object)[]` map to DYN wholesale now): the dyn keyed
5620
5720
  // write — dyn.keySet. Number keys canonicalize through the JS-exact
5621
5721
  // formatter; ARR receivers take canonical index keys as element
5622
5722
  // set/extend (undefined-hole padding, JS's length growth exactly),
5623
5723
  // OBJ receivers set the member, and the runtime throws Node's
5624
- // TypeErrors on every other kind. Values convert into the DOM;
5724
+ // TypeErrors on every other kind. Values convert into the checked-dynamic tree;
5625
5725
  // unconvertible ones fence per site (the dynFrom stance).
5626
5726
  if (receiverIr?.kind === "dyn") {
5627
5727
  const obj = L.lowerExpr(target.expression);
@@ -5635,7 +5735,7 @@ export function lowerElementWrite(L, expr) {
5635
5735
  }
5636
5736
  const value = L.coerceToExpected(L.lowerExpr(expr.right), DYN);
5637
5737
  if (value.type.kind !== "dyn") {
5638
- L.unsupported("SC1101", expr.right, `storing '${L.fmt(value.type)}' values in a checked-dynamic array (the value cannot convert into the DOM)`);
5738
+ L.unsupported("SC1101", expr.right, `storing '${L.fmt(value.type)}' values in a checked-dynamic array (the value cannot convert into the checked-dynamic tree)`);
5639
5739
  }
5640
5740
  return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [obj, key, value], type: VOID, loc }, loc };
5641
5741
  }
@@ -5675,11 +5775,11 @@ export function lowerElementWrite(L, expr) {
5675
5775
  const loc = locOf(expr);
5676
5776
  return { kind: "exprStmt", expr: { kind: "jsOp", op: "setIdx", args: [obj, key, value], type: VOID, loc }, loc };
5677
5777
  }
5678
- // A record-mapped CHECKER type over a VALUE living in the DOM (a
5778
+ // A record-mapped CHECKER type over a VALUE living in the checked-dynamic tree (a
5679
5779
  // JS file-scope object-literal global): the checked-dynamic keyed
5680
5780
  // write — dyn.keySet (later writes win, insertion order; Node's
5681
5781
  // TypeErrors on non-object receivers), the value converting into
5682
- // the DOM. Literal and runtime keys alike: the DOM's key set is
5782
+ // the checked-dynamic tree. Literal and runtime keys alike: the checked-dynamic tree's key set is
5683
5783
  // open, so no declared-field collision analysis applies.
5684
5784
  if (obj.type.kind === "dyn") {
5685
5785
  const loc = locOf(expr);
@@ -5694,7 +5794,7 @@ export function lowerElementWrite(L, expr) {
5694
5794
  }
5695
5795
  const value = L.coerceToExpected(L.lowerExpr(expr.right), DYN);
5696
5796
  if (value.type.kind !== "dyn") {
5697
- L.unsupported("SC1101", expr.right, `storing '${L.fmt(value.type)}' values in a checked-dynamic object (the value cannot convert into the DOM)`);
5797
+ L.unsupported("SC1101", expr.right, `storing '${L.fmt(value.type)}' values in a checked-dynamic object (the value cannot convert into the checked-dynamic tree)`);
5698
5798
  }
5699
5799
  return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [obj, key, value], type: VOID, loc }, loc };
5700
5800
  }
@@ -5782,7 +5882,7 @@ export function lowerElementWrite(L, expr) {
5782
5882
  // A LITERAL key naming no declared field is a pure overflow insert
5783
5883
  // — no collision to validate. Runtime keys must be able to collide
5784
5884
  // with every declared field: dyn slots validate through the
5785
- // dynCheck walker (fields must be DOM-convertible — the same
5885
+ // dynCheck walker (fields must be dyn-convertible — the same
5786
5886
  // convertibility the read needs); typed slots write through
5787
5887
  // directly, so every field must BE the index-value type.
5788
5888
  const overflowOnly = litKey !== null;
@@ -5821,7 +5921,7 @@ export function lowerElementWrite(L, expr) {
5821
5921
  // `result[key] = value`): dyn.keySet, exactly the record-mapped dyn
5822
5922
  // arm above — later writes win in insertion order, number keys
5823
5923
  // canonicalize through the JS-exact formatter (ToPropertyKey's string
5824
- // side), index keys on DOM arrays set/extend elements, and non-object
5924
+ // side), index keys on dyn arrays set/extend elements, and non-object
5825
5925
  // receivers throw Node's TypeErrors at runtime. An UNMAPPED checker
5826
5926
  // type (static `any` — mapTypeOf answers null without --dynamic)
5827
5927
  // probes the receiver's own lowered world: a dyn value takes the same
@@ -5844,7 +5944,7 @@ export function lowerElementWrite(L, expr) {
5844
5944
  }
5845
5945
  const value = L.coerceToExpected(L.lowerExpr(expr.right), DYN);
5846
5946
  if (value.type.kind !== "dyn") {
5847
- L.unsupported("SC1101", expr.right, `storing '${L.fmt(value.type)}' values in a checked-dynamic object (the value cannot convert into the DOM)`);
5947
+ L.unsupported("SC1101", expr.right, `storing '${L.fmt(value.type)}' values in a checked-dynamic object (the value cannot convert into the checked-dynamic tree)`);
5848
5948
  }
5849
5949
  return { kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [obj, key, value], type: VOID, loc }, loc };
5850
5950
  }
@@ -5878,7 +5978,7 @@ export function ensureString(L, e, node) {
5878
5978
  if (e.type.kind === "string")
5879
5979
  return e;
5880
5980
  if (e.type.kind === "dyn") {
5881
- // String(u) / `${u}`: a runtime dispatch over the DOM kind — Node's
5981
+ // String(u) / `${u}`: a runtime dispatch over the dyn kind — Node's
5882
5982
  // String() exactly (undefined/null texts, JS number formatting,
5883
5983
  // strings verbatim, arrays via join, objects as "[object Object]").
5884
5984
  return { kind: "toString", operand: e, type: STRING, loc: e.loc };
@@ -6004,7 +6104,7 @@ export function lowerTemplate(L, expr) {
6004
6104
  * documented divergence: a lying cast throws instead of corrupting
6005
6105
  * memory);
6006
6106
  * - dyn → anything else (closures, class instances, void) is rejected:
6007
- * those types cannot be found inside a JSON DOM. */
6107
+ * those types cannot be found inside a JSON dyn. */
6008
6108
  /** `e as T` and the old-style assertion `<T>e` — one node shape (both
6009
6109
  * carry `.type` and `.expression`), one lowering. */
6010
6110
  export function lowerAsExpression(L, expr) {
@@ -6108,7 +6208,7 @@ export function lowerAsExpression(L, expr) {
6108
6208
  return inner; // `as unknown`: erasure
6109
6209
  if (target.kind === "void" || !L.jsonSafe(target)) {
6110
6210
  // Bare undefined-armed targets pass when every OTHER arm is
6111
- // JSON-safe: the DOM holds a first-class undefined value now
6211
+ // JSON-safe: the checked-dynamic tree holds a first-class undefined value now
6112
6212
  // (index-signature overflow reads produce it for missing keys), and
6113
6213
  // the undefined arm matches exactly it — `p[key] as string |
6114
6214
  // undefined` is the missing-key idiom. Parsed JSON still never
@@ -6117,7 +6217,7 @@ export function lowerAsExpression(L, expr) {
6117
6217
  if (target.kind === "union" && L.dynConvertible(target)) {
6118
6218
  return { kind: "dynCheck", value: inner, type: target, loc: locOf(expr) };
6119
6219
  }
6120
- // Uint8Array targets: the DOM carries a bytes kind now (converted
6220
+ // Uint8Array targets: the checked-dynamic tree carries a bytes kind now (converted
6121
6221
  // stdin chunks) — the extraction validates the kind and copies out.
6122
6222
  if (target.kind === "bytes" && target.elem === "u8") {
6123
6223
  return { kind: "dynCheck", value: inner, type: target, loc: locOf(expr) };
@@ -6140,7 +6240,7 @@ export function lowerAsExpression(L, expr) {
6140
6240
  // — the errno-probing idiom): there is nothing to validate (every
6141
6241
  // field is unknown, exactly what the dyn value already answers) and
6142
6242
  // nothing to build — the cast is pure typing, so it ERASES and the
6143
- // reads ride the DOM keyed read.
6243
+ // reads ride the dyn keyed read.
6144
6244
  if (target.kind === "record") {
6145
6245
  const shape = L.shapes.get(target.shapeId);
6146
6246
  if (shape &&
@@ -6301,7 +6401,7 @@ export function lowerBinary(L, expr) {
6301
6401
  }
6302
6402
  // `events.defaultMaxListeners = v` — the module-property write
6303
6403
  // Node validates (validateNumber(n, 'defaultMaxListeners', 0)):
6304
- // the value crosses into the DOM and the runtime ladder throws
6404
+ // the value crosses into the checked-dynamic tree and the runtime ladder throws
6305
6405
  // ERR_INVALID_ARG_TYPE / ERR_OUT_OF_RANGE with Node's exact slot
6306
6406
  // name; valid numbers apply. The expression's value is the RHS
6307
6407
  // (JS's assignment yield).
@@ -6474,8 +6574,8 @@ export function lowerBinary(L, expr) {
6474
6574
  }
6475
6575
  if (left.type.kind === "dyn" || right.type.kind === "dyn") {
6476
6576
  // A checked-dynamic operand (`fn.name || '<anonymous>'` —
6477
- // test/common's _mustCallInner): both sides live in the DOM and
6478
- // the deciding test is ToBoolean over the DOM kind
6577
+ // test/common's _mustCallInner): both sides live in the checked-dynamic tree and
6578
+ // the deciding test is ToBoolean over the dyn kind
6479
6579
  // (scr_dyn_truthy) — JS value semantics exactly, result dyn. The
6480
6580
  // non-dyn side converts through the usual boundary; a value with
6481
6581
  // no dyn representation keeps the fence.
@@ -6611,7 +6711,7 @@ export function lowerBinary(L, expr) {
6611
6711
  const right = L.lowerExpr(expr.right);
6612
6712
  if (left.type.kind === "dyn" || right.type.kind === "dyn") {
6613
6713
  // The narrowing unit comparisons ARE answerable on unknown: `v ===
6614
- // undefined` / `v !== null` test the DOM node's kind directly, and
6714
+ // undefined` / `v !== null` test the dyn node's kind directly, and
6615
6715
  // tsc's control flow narrows the branches (reads then bridge through
6616
6716
  // maybeNarrow's validated extraction).
6617
6717
  if (op === ts.SyntaxKind.EqualsEqualsEqualsToken ||
@@ -6631,14 +6731,14 @@ export function lowerBinary(L, expr) {
6631
6731
  }
6632
6732
  // dyn vs SCALAR strict equality (`value !== ""`, `u === 5` — the
6633
6733
  // normalizePricing filter): a guarded kind test + payload compare,
6634
- // JS-exact (strict equality never coerces; a non-matching DOM kind
6734
+ // JS-exact (strict equality never coerces; a non-matching dyn kind
6635
6735
  // answers false). dyn vs dyn keeps the fence below.
6636
6736
  const dynSide = left.type.kind === "dyn" ? left : right;
6637
6737
  const scalarSide = dynSide === left ? right : left;
6638
6738
  if (dynSide.type.kind === "dyn" &&
6639
6739
  (scalarSide.type.kind === "f64" || scalarSide.type.kind === "string" || scalarSide.type.kind === "bool" ||
6640
6740
  // dyn vs dyn (`context.actual !== context.exact` —
6641
- // test/common's exit accounting): the runtime's whole-DOM
6741
+ // test/common's exit accounting): the runtime's whole-dyn
6642
6742
  // strict equality — scalars by value, units by kind,
6643
6743
  // reference kinds by node identity (scr_dyn_strict_eq).
6644
6744
  scalarSide.type.kind === "dyn")) {
@@ -6693,7 +6793,7 @@ export function lowerBinary(L, expr) {
6693
6793
  }
6694
6794
  if (other.type.kind === "string") {
6695
6795
  // String-context `+`: JS's answer is String(unknown) — the
6696
- // JS-exact DOM walker (numbers format, arrays join, objects
6796
+ // JS-exact dyn walker (numbers format, arrays join, objects
6697
6797
  // print [object Object], handles the same) — never a checked
6698
6798
  // cast: `'status ' + res.statusCode` concatenates like Node.
6699
6799
  const strOf = (e) => e.type.kind === "dyn" ? { kind: "toString", operand: e, type: STRING, loc: e.loc } : e;
@@ -6711,7 +6811,7 @@ export function lowerBinary(L, expr) {
6711
6811
  L.anyOpFence(`the '${ts.tokenToString(op) ?? ts.SyntaxKind[op]}' operator`, expr);
6712
6812
  }
6713
6813
  // tsc allows ===/!== on unknown (arithmetic/comparisons it rejects
6714
- // itself); a dynamic equality would need a DOM walk — validate first.
6814
+ // itself); a dynamic equality would need a dyn walk — validate first.
6715
6815
  L.unsupported("SC1100", expr, "operators on 'unknown' values");
6716
6816
  }
6717
6817
  // Operators over 'any' execute in the island with JS-exact semantics
@@ -7249,7 +7349,7 @@ export function lowerUnionTypeofTest(L, expr, loc) {
7249
7349
  return null;
7250
7350
  }
7251
7351
  /** `typeof v === "lit"` / `typeof v !== "lit"` where v is `unknown`-typed
7252
- * (dyn): the runtime kind test over the DOM node's tag. Only the kinds a
7352
+ * (dyn): the runtime kind test over the dyn node's tag. Only the kinds a
7253
7353
  * later read can bridge narrow ("string"/"number"/"boolean") plus
7254
7354
  * "undefined" (no payload to read); "object"/"function" tests keep the
7255
7355
  * fence — an object-narrowed `unknown` read has no lowering anyway (a
@@ -7333,7 +7433,7 @@ export function lowerDynTypeofTest(L, expr, loc) {
7333
7433
  loc,
7334
7434
  };
7335
7435
  }
7336
- // `typeof v === "object"`: the DOM answers exactly (object, array,
7436
+ // `typeof v === "object"`: the dyn answers exactly (object, array,
7337
7437
  // bytes, and null kinds — JS's oldest wart preserved). tsc narrows
7338
7438
  // the branch to `object | null`; reads past the narrow take the
7339
7439
  // checked-cast path per site.
@@ -7347,7 +7447,7 @@ export function lowerDynTypeofTest(L, expr, loc) {
7347
7447
  loc,
7348
7448
  };
7349
7449
  }
7350
- // `typeof v === "function"`: a REAL runtime test since the DOM's
7450
+ // `typeof v === "function"`: a REAL runtime test since the checked-dynamic tree's
7351
7451
  // function kind exists (boxed closures — the mustCall guard
7352
7452
  // `if (typeof fn !== 'function') throw` answers honestly).
7353
7453
  if (b.text === "function") {
@@ -7510,7 +7610,7 @@ export function probeLower(L, node) {
7510
7610
  * over a hierarchy class, `typeof` over a primitive), the read bridges
7511
7611
  * with a kind-unchecked `caughtNarrow` extraction (trust-the-checker,
7512
7612
  * exactly like unionNarrow). Every OTHER read is the narrowness fence:
7513
- * the binding's payload is a typed exception snapshot, not a dyn DOM, so
7613
+ * the binding's payload is a typed exception snapshot, not a dyn, so
7514
7614
  * un-narrowed uses have nothing sound to lower to. */
7515
7615
  export function caughtRead(L, node, local, loc) {
7516
7616
  const ref = { kind: "varRef", localId: local.id, type: CAUGHT, loc };
@@ -7526,10 +7626,10 @@ export function caughtRead(L, node, local, loc) {
7526
7626
  }
7527
7627
  }
7528
7628
  // An UN-narrowed use — the occurrence still types `unknown` — converts
7529
- // to a dyn DOM value (`options.onError?.(error)` — the caught snapshot
7629
+ // to a dyn value (`options.onError?.(error)` — the caught snapshot
7530
7630
  // flowing into an unknown slot): the typed→unknown deep-copy stance
7531
7631
  // extended to exception payloads. Error payloads keep their
7532
- // observability (instanceof Error / .message / String() — the DOM's
7632
+ // observability (instanceof Error / .message / String() — the checked-dynamic tree's
7533
7633
  // error encoding); other object payloads are type-erased at runtime and
7534
7634
  // convert to the "[object Object]" approximation — SEMANTICS.md 67.
7535
7635
  if (narrowed?.kind === "dyn") {
@@ -7635,14 +7735,14 @@ export function lowerInstanceOf(L, expr, loc) {
7635
7735
  L.builtinStreamInfoOf(rhsMemberSymbol) ??
7636
7736
  undefined;
7637
7737
  if (!target) {
7638
- // `u instanceof Uint8Array` on an `unknown` value: the DOM carries a
7738
+ // `u instanceof Uint8Array` on an `unknown` value: the checked-dynamic tree carries a
7639
7739
  // bytes kind — one runtime tag test, and tsc's narrowing types the
7640
7740
  // true branch (reads bridge through maybeNarrow's validated
7641
7741
  // extraction, like the typeof tests). Node's Buffer IS a Uint8Array
7642
7742
  // subclass and rides the same bytes kind, so both worlds answer true
7643
7743
  // for Buffer payloads — Node-exact (the bytes kind's other
7644
7744
  // divergences are SEMANTICS.md 45). Catch bindings stay out (their
7645
- // payload is a typed snapshot, not a DOM).
7745
+ // payload is a typed snapshot, not a dyn).
7646
7746
  if (ts.isIdentifier(expr.right) &&
7647
7747
  L.isStdlibGlobal(expr.right, "Uint8Array") &&
7648
7748
  !L.caughtLocalOf(expr.left)) {
@@ -7733,7 +7833,7 @@ export function lowerInstanceOf(L, expr, loc) {
7733
7833
  };
7734
7834
  }
7735
7835
  const left = L.lowerExpr(expr.left);
7736
- // `u instanceof Error` on an `unknown` value: the DOM's error encoding
7836
+ // `u instanceof Error` on an `unknown` value: the checked-dynamic tree's error encoding
7737
7837
  // (the shape caughtToDyn builds for Error payloads — the reserved
7738
7838
  // "%error" marker) answers the test, so a caught Error passed through
7739
7839
  // an unknown slot narrows like Node (`error instanceof Error ?
@@ -7746,7 +7846,7 @@ export function lowerInstanceOf(L, expr, loc) {
7746
7846
  return { kind: "dynTest", test: "error", value: left, type: BOOL, loc };
7747
7847
  }
7748
7848
  // `u instanceof TypeError` (and the other BUILTIN error classes) on a
7749
- // dyn value: the from_error cache holds the DOM↔error identity edge,
7849
+ // dyn value: the from_error cache holds the checked-dynamic tree↔error identity edge,
7750
7850
  // so the runtime resolves the encoding back to its error and asks the
7751
7851
  // vtable's stamped interval — exact for every error that crossed the
7752
7852
  // boundary (a hand-built {%error} literal answers false: subclass
@@ -8039,7 +8139,7 @@ export function lowerInExpression(L, expr, loc) {
8039
8139
  }
8040
8140
  }
8041
8141
  // A dyn receiver (`"portless" in pkg` after the `typeof pkg ===
8042
- // "object"` guard): own-member presence on the DOM — tsc admits `in`
8142
+ // "object"` guard): own-member presence on the checked-dynamic tree — tsc admits `in`
8043
8143
  // only on object-typed operands, so unit receivers are unreachable.
8044
8144
  if (recv.type.kind === "dyn") {
8045
8145
  return { kind: "dynHasKey", key, value: recv, type: BOOL, loc };
@@ -8255,11 +8355,14 @@ function lowerRuntimeKeyIn(L, expr, loc) {
8255
8355
  }
8256
8356
  /** A regex literal `/ab+c/gi` → regexLit (interned per (pattern, flags)
8257
8357
  * by the backend). The TS parser has already syntax-checked the literal;
8258
- * what remains here are the slice fences: the flag alphabet (d and v are
8259
- * declared-valid TS flags outside this slice) and named capture groups
8260
- * (their results are unreachable without exec/match). The engine
8261
- * validates the pattern itself lazily at first use (SEMANTICS.md
8262
- * documents the divergence from Node's parse-time SyntaxError). */
8358
+ * what remains here is the flag-alphabet fence (d and v are
8359
+ * declared-valid TS flags outside this slice). Named capture groups
8360
+ * `(?<name>...)` and `\k<name>` backreferences compile — libregexp
8361
+ * executes them natively, replace templates resolve `$<name>` at
8362
+ * runtime, and `.groups` reads desugar at their access sites
8363
+ * (matchResultNamedGroupsOf). The engine validates the pattern itself
8364
+ * lazily at first use (SEMANTICS.md documents the divergence from
8365
+ * Node's parse-time SyntaxError). */
8263
8366
  export function lowerRegexLiteral(L, expr) {
8264
8367
  const text = expr.text;
8265
8368
  const lastSlash = text.lastIndexOf("/");
@@ -8274,12 +8377,414 @@ export function lowerRegexLiteral(L, expr) {
8274
8377
  : `the regex '${f}' flag`);
8275
8378
  }
8276
8379
  }
8277
- // (?<name>...) — named groups; (?<= / (?<! are lookbehinds and fine.
8278
- if (/\(\?<(?![=!])/.test(pattern)) {
8279
- L.unsupported("SC1120", expr, "named capture groups in regex patterns");
8280
- }
8281
8380
  return { kind: "regexLit", pattern, flags, type: REGEX, loc: locOf(expr) };
8282
8381
  }
8382
+ /** The pattern's named capture groups, in source order: each `(?<name>`
8383
+ * with its 1-based capture index (numbered and named groups share the
8384
+ * numbering — the index IS the match slice's element position). Null
8385
+ * when the scan can't answer confidently (an unterminated construct, a
8386
+ * `\u`-escaped group name) — callers fence rather than guess. Duplicate
8387
+ * names (ES2025 — valid across alternatives) appear once per
8388
+ * declaration; consumers pick the participating occurrence. The scan
8389
+ * only needs to track escapes, character classes, and `(` kinds; tsc
8390
+ * has already syntax-checked the literal, so a malformed pattern here
8391
+ * answers null and the engine's lazy compile reports it. */
8392
+ export function namedCaptureGroupsOfPattern(pattern) {
8393
+ const groups = [];
8394
+ let captureIndex = 0;
8395
+ let inClass = false;
8396
+ let i = 0;
8397
+ while (i < pattern.length) {
8398
+ const c = pattern[i];
8399
+ if (c === "\\") {
8400
+ i += 2;
8401
+ continue;
8402
+ }
8403
+ if (inClass) {
8404
+ if (c === "]")
8405
+ inClass = false;
8406
+ i++;
8407
+ continue;
8408
+ }
8409
+ if (c === "[") {
8410
+ inClass = true;
8411
+ i++;
8412
+ continue;
8413
+ }
8414
+ if (c !== "(") {
8415
+ i++;
8416
+ continue;
8417
+ }
8418
+ if (pattern[i + 1] !== "?") {
8419
+ captureIndex++;
8420
+ i++;
8421
+ continue;
8422
+ }
8423
+ // (?<name> captures; (?<= and (?<! are lookbehinds; every other
8424
+ // (?… form — (?:, (?=, (?!, modifier groups (?i: — is non-capturing.
8425
+ if (pattern[i + 2] === "<" && pattern[i + 3] !== "=" && pattern[i + 3] !== "!") {
8426
+ const gt = pattern.indexOf(">", i + 3);
8427
+ if (gt < 0)
8428
+ return null;
8429
+ const name = pattern.slice(i + 3, gt);
8430
+ if (name.includes("\\"))
8431
+ return null; // \u-escaped names: bytecode spells them decoded
8432
+ captureIndex++;
8433
+ groups.push({ name, index: captureIndex });
8434
+ i = gt + 1;
8435
+ continue;
8436
+ }
8437
+ i += 2;
8438
+ }
8439
+ return groups;
8440
+ }
8441
+ /** The statically-known regex PATTERN behind an expression: a regex
8442
+ * literal, a const local initialized with one (the crypto.js
8443
+ * `const regexp = /(?<m>\d+)/` shape), or `new RegExp("...")` over a
8444
+ * string literal (the cooked text IS the pattern). Null when the regex
8445
+ * only exists at runtime — .groups consumers fence there. */
8446
+ export function staticRegexPatternOf(L, e) {
8447
+ let expr = e;
8448
+ for (;;) {
8449
+ if (ts.isParenthesizedExpression(expr) || ts.isNonNullExpression(expr)) {
8450
+ expr = expr.expression;
8451
+ continue;
8452
+ }
8453
+ break;
8454
+ }
8455
+ if (ts.isRegularExpressionLiteral(expr)) {
8456
+ const text = expr.text;
8457
+ return text.slice(1, text.lastIndexOf("/"));
8458
+ }
8459
+ if (ts.isNewExpression(expr) &&
8460
+ ts.isIdentifier(expr.expression) &&
8461
+ expr.expression.text === "RegExp" &&
8462
+ L.isStdlibGlobal(expr.expression, "RegExp") &&
8463
+ expr.arguments !== undefined &&
8464
+ expr.arguments.length >= 1 &&
8465
+ (ts.isStringLiteral(expr.arguments[0]) || ts.isNoSubstitutionTemplateLiteral(expr.arguments[0]))) {
8466
+ return expr.arguments[0].text;
8467
+ }
8468
+ if (ts.isIdentifier(expr)) {
8469
+ const init = constInitializerOf(L, expr);
8470
+ if (init !== null)
8471
+ return staticRegexPatternOf(L, init);
8472
+ }
8473
+ return null;
8474
+ }
8475
+ /** The initializer behind a CONST identifier binding (a plain
8476
+ * VariableDeclaration under a const list) — the one-hop provenance step
8477
+ * the regex traces walk. Null for let/var (reassignable), params,
8478
+ * destructured bindings, and unresolvable names. */
8479
+ function constInitializerOf(L, ident) {
8480
+ const sym = L.resolveValueSymbol(ident);
8481
+ const decl = sym ? L.checker.valueDeclarationOf(sym) : undefined;
8482
+ if (decl === undefined || !ts.isVariableDeclaration(decl) || decl.initializer === undefined)
8483
+ return null;
8484
+ if (!ts.isVariableDeclarationList(decl.parent) || (decl.parent.flags & ts.NodeFlags.Const) === 0)
8485
+ return null;
8486
+ return decl.initializer;
8487
+ }
8488
+ /** The named-group table of the regex that PRODUCED a match-result
8489
+ * expression — the `.groups` desugar's provenance question. Traces
8490
+ * (through parens, `!`, and const bindings):
8491
+ * - `re.exec(s)` / `s.match(re)` → re's static pattern
8492
+ * - a matchAll ROW: `rows[i]`, the for-of binding over a direct
8493
+ * `s.matchAll(re)` or a stored const drain → re's static pattern
8494
+ * Answers null when the producing regex isn't statically known (the
8495
+ * caller keeps its fence), or the groups list (possibly empty — a
8496
+ * traced regex WITHOUT named groups is the Node-undefined case). */
8497
+ export function matchResultNamedGroupsOf(L, e) {
8498
+ const reExpr = matchProducerRegexOf(L, e);
8499
+ if (reExpr === null)
8500
+ return null;
8501
+ const pattern = staticRegexPatternOf(L, reExpr);
8502
+ if (pattern === null)
8503
+ return null;
8504
+ return namedCaptureGroupsOfPattern(pattern);
8505
+ }
8506
+ /** The regex EXPRESSION whose match produced `e` (see
8507
+ * matchResultNamedGroupsOf for the traced shapes). */
8508
+ function matchProducerRegexOf(L, e) {
8509
+ let expr = e;
8510
+ for (;;) {
8511
+ if (ts.isParenthesizedExpression(expr) || ts.isNonNullExpression(expr)) {
8512
+ expr = expr.expression;
8513
+ continue;
8514
+ }
8515
+ break;
8516
+ }
8517
+ // The direct producers: re.exec(s) / s.match(re) — the receiver's
8518
+ // mapped type pins the STDLIB operation (a regex for exec, a string
8519
+ // or its nullable spelling for match — the claim lower-containers
8520
+ // makes), so a user method that happens to be named `match` with a
8521
+ // regex argument never traces.
8522
+ if (ts.isCallExpression(expr) && ts.isPropertyAccessExpression(expr.expression)) {
8523
+ const name = expr.expression.name.text;
8524
+ const recvT = L.mapTypeOf(L.typeOf(expr.expression.expression));
8525
+ if (name === "exec" && expr.arguments.length === 1 && recvT?.kind === "regex") {
8526
+ return expr.expression.expression;
8527
+ }
8528
+ if (name === "match" && expr.arguments.length === 1 &&
8529
+ (recvT?.kind === "string" || (recvT !== null && recvT !== undefined && nullableStringType(L, recvT))) &&
8530
+ L.mapTypeOf(L.typeOf(expr.arguments[0]))?.kind === "regex") {
8531
+ return expr.arguments[0];
8532
+ }
8533
+ }
8534
+ // A matchAll ROW by element access: rows[i] with rows tracing to the
8535
+ // drain call — or the direct s.matchAll(re)[i] spelling.
8536
+ if (ts.isElementAccessExpression(expr)) {
8537
+ return matchAllRegexOf(L, expr.expression);
8538
+ }
8539
+ if (ts.isIdentifier(expr)) {
8540
+ // The for-of binding over a matchAll drain: `for (const m of
8541
+ // s.matchAll(re))` (or over a stored const rows).
8542
+ const sym = L.resolveValueSymbol(expr);
8543
+ const decl = sym ? L.checker.valueDeclarationOf(sym) : undefined;
8544
+ if (decl !== undefined && ts.isVariableDeclaration(decl) &&
8545
+ ts.isVariableDeclarationList(decl.parent) && ts.isForOfStatement(decl.parent.parent)) {
8546
+ return matchAllRegexOf(L, decl.parent.parent.expression);
8547
+ }
8548
+ // A stored const match result: `const m = re.exec(s)`.
8549
+ const init = constInitializerOf(L, expr);
8550
+ if (init !== null)
8551
+ return matchProducerRegexOf(L, init);
8552
+ }
8553
+ return null;
8554
+ }
8555
+ /** The regex argument of a (possibly const-stored) `s.matchAll(re)`. */
8556
+ function matchAllRegexOf(L, e) {
8557
+ let expr = e;
8558
+ for (;;) {
8559
+ if (ts.isParenthesizedExpression(expr) || ts.isNonNullExpression(expr)) {
8560
+ expr = expr.expression;
8561
+ continue;
8562
+ }
8563
+ break;
8564
+ }
8565
+ if (ts.isCallExpression(expr) && ts.isPropertyAccessExpression(expr.expression) &&
8566
+ expr.expression.name.text === "matchAll" && expr.arguments.length === 1 &&
8567
+ L.mapTypeOf(L.typeOf(expr.expression.expression))?.kind === "string" &&
8568
+ L.mapTypeOf(L.typeOf(expr.arguments[0]))?.kind === "regex") {
8569
+ return expr.arguments[0];
8570
+ }
8571
+ // The eager-spread spelling: `[...s.matchAll(re)]`.
8572
+ if (ts.isArrayLiteralExpression(expr) && expr.elements.length === 1) {
8573
+ const only = expr.elements[0];
8574
+ if (ts.isSpreadElement(only))
8575
+ return matchAllRegexOf(L, only.expression);
8576
+ }
8577
+ if (ts.isIdentifier(expr)) {
8578
+ const init = constInitializerOf(L, expr);
8579
+ if (init !== null)
8580
+ return matchAllRegexOf(L, init);
8581
+ }
8582
+ return null;
8583
+ }
8584
+ /** `m.groups` on a match result whose producing regex is statically
8585
+ * known: the honest slice already HOLDS every named group's value at
8586
+ * its capture index, so the groups object is a compile-time record
8587
+ * projection — `{ year: m[1], month: m[2] }` — built by one interned
8588
+ * helper per (shape, index list, receiver type). Node's exact shape
8589
+ * decisions:
8590
+ * - no named groups → `undefined` (the identifier-receiver shapes —
8591
+ * a call receiver would lose its own null trap, so those keep the
8592
+ * member fence);
8593
+ * - a nullable receiver (the JS `s.match(re).groups` idiom) throws
8594
+ * Node's exact TypeError on the unit arms;
8595
+ * - every declared name is a key (nonparticipating groups hold "" —
8596
+ * divergence 51's rule, exactly the slice elements they project);
8597
+ * - ES2025 duplicate names (distinct alternatives) project the first
8598
+ * participating occurrence: at most one participates, and a
8599
+ * participating-empty "" falls through to slots that are "" anyway;
8600
+ * - key order is group declaration order (declaredOrder), Node's own.
8601
+ * The record has no prototype — Node's groups object is null-prototype,
8602
+ * observable only through util.inspect's "[Object: null prototype]"
8603
+ * prefix (the Object.groupBy stance, ledgered). Null when the receiver
8604
+ * isn't a match slice or the regex isn't traceable — the caller's
8605
+ * member fence (with the groups hint) names the gap. */
8606
+ export function lowerMatchGroupsRead(L, expr) {
8607
+ if (expr.name.text !== "groups" || expr.questionDotToken !== undefined)
8608
+ return null;
8609
+ const loc = locOf(expr);
8610
+ const strArr = arrayOf(STRING);
8611
+ const recvT = L.mapTypeOf(L.typeOf(expr.expression));
8612
+ if (!recvT || !isMatchSliceType(L, recvT) || !L.isStdlibMember(expr))
8613
+ return null;
8614
+ const groups = matchResultNamedGroupsOf(L, expr.expression);
8615
+ if (groups === null)
8616
+ return null;
8617
+ if (groups.length === 0) {
8618
+ // Node: a match result of a group-less regex answers undefined —
8619
+ // the unit literal, exactly the `undefined` identifier's lowering.
8620
+ // Identifier receivers only (evaluation-free): a CALL receiver's
8621
+ // exec/match must still run and null-trap, which a folded unit
8622
+ // cannot carry — those keep the fence.
8623
+ let r = expr.expression;
8624
+ while (ts.isParenthesizedExpression(r) || ts.isNonNullExpression(r))
8625
+ r = r.expression;
8626
+ if (ts.isIdentifier(r) && typeEquals(recvT, strArr)) {
8627
+ return { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc };
8628
+ }
8629
+ return null;
8630
+ }
8631
+ const recv = L.lowerExpr(expr.expression);
8632
+ if (!isMatchSliceType(L, recv.type))
8633
+ return null;
8634
+ return lowerGroupsProjection(L, recv, groups, loc);
8635
+ }
8636
+ /** True when the IR type is a string-or-units union — the Dict<string>
8637
+ * member spelling (`process.versions.openssl`), match's nullable
8638
+ * receiver claim. */
8639
+ function nullableStringType(L, t) {
8640
+ if (t.kind !== "union")
8641
+ return false;
8642
+ const arms = L.unions.get(t.unionId)?.arms ?? [];
8643
+ return arms.some((a) => a.kind === "string") && arms.every((a) => a.kind === "string" || isUnitType(a));
8644
+ }
8645
+ /** True when the IR type is the honest match slice — `string[]`, or a
8646
+ * union of it with unit arms only (`string[] | null`). The groups
8647
+ * projection's receiver gate, shared with the destructure interception. */
8648
+ export function isMatchSliceType(L, t) {
8649
+ if (typeEquals(t, arrayOf(STRING)))
8650
+ return true;
8651
+ return t.kind === "union" && matchSliceUnionArms(L, t.unionId) !== null;
8652
+ }
8653
+ /** The union-arm layout of a nullable match slice (`string[] | null`,
8654
+ * matchAll's checker spellings with undefined arms included): the
8655
+ * string[] arm's tag plus each unit arm's tag and spelling. Null when
8656
+ * the union holds anything else. */
8657
+ function matchSliceUnionArms(L, unionId) {
8658
+ const def = L.unions.get(unionId);
8659
+ if (!def)
8660
+ return null;
8661
+ const strArr = arrayOf(STRING);
8662
+ const arrTag = def.arms.findIndex((a) => typeEquals(a, strArr));
8663
+ if (arrTag < 0)
8664
+ return null;
8665
+ const units = [];
8666
+ for (let i = 0; i < def.arms.length; i++) {
8667
+ if (i === arrTag)
8668
+ continue;
8669
+ const a = def.arms[i];
8670
+ if (!isUnitType(a))
8671
+ return null;
8672
+ units.push({ tag: i, unit: a.kind === "nullT" ? "null" : "undefined" });
8673
+ }
8674
+ return { arrTag, units };
8675
+ }
8676
+ /** The groups-record projection call: the result is the CHECKER'S OWN
8677
+ * shape — `{ [key: string]: string }`, the hybrid index-signature
8678
+ * record with no declared fields — holding one OVERFLOW entry per
8679
+ * group name in declaration order, so every downstream consumer
8680
+ * (recordKeyGet reads, Object.keys, JSON, the `| undefined` union
8681
+ * wrap) meets exactly the type tsc reported. One lifted helper per
8682
+ * (index list, receiver representation) builds it; see
8683
+ * lowerMatchGroupsRead for the semantics the body encodes. */
8684
+ export function lowerGroupsProjection(L, recv, groups, loc) {
8685
+ // Name → its capture indices, in declaration order (duplicates keep
8686
+ // every occurrence — the projection picks the participating one).
8687
+ const order = [];
8688
+ const indicesByName = new Map();
8689
+ for (const g of groups) {
8690
+ const got = indicesByName.get(g.name);
8691
+ if (got) {
8692
+ got.push(g.index);
8693
+ }
8694
+ else {
8695
+ indicesByName.set(g.name, [g.index]);
8696
+ order.push(g.name);
8697
+ }
8698
+ }
8699
+ const shapeId = L.shapes.intern([], false, STRING);
8700
+ const recordT = { kind: "record", shapeId };
8701
+ const recvKey = recv.type.kind === "union" ? recv.type.unionId : "arr";
8702
+ const key = `regexgroups:${shapeId}:${groups.map((g) => `${g.name}=${g.index}`).join(",")}:${recvKey}`;
8703
+ let helper = L.widthHelpers.get(key);
8704
+ if (!helper) {
8705
+ helper = `%regex.groups.${L.widthHelpers.size}`;
8706
+ L.widthHelpers.set(key, helper);
8707
+ const body = [];
8708
+ const locals = [{ id: "m.0", name: "m", type: recv.type, mutable: true }];
8709
+ let mRef = { kind: "varRef", localId: "m.0", type: recv.type, loc };
8710
+ if (recv.type.kind === "union") {
8711
+ // Node's exact TypeError per unit arm, then the checked narrow.
8712
+ const arms = matchSliceUnionArms(L, recv.type.unionId);
8713
+ for (const u of arms.units) {
8714
+ body.push({
8715
+ kind: "if",
8716
+ cond: { kind: "unionIsTag", unionId: recv.type.unionId, tag: u.tag, negated: false, value: mRef, type: BOOL, loc },
8717
+ then: [
8718
+ {
8719
+ kind: "throw",
8720
+ value: {
8721
+ kind: "libCall",
8722
+ fn: "error.new",
8723
+ args: [
8724
+ { kind: "strLit", value: `Cannot read properties of ${u.unit} (reading 'groups')`, type: STRING, loc },
8725
+ ],
8726
+ type: { kind: "object", className: "%TypeError" },
8727
+ loc,
8728
+ },
8729
+ loc,
8730
+ },
8731
+ ],
8732
+ else_: null,
8733
+ loc,
8734
+ });
8735
+ }
8736
+ const narrowed = { kind: "unionNarrow", unionId: recv.type.unionId, tag: arms.arrTag, value: mRef, type: arrayOf(STRING), loc };
8737
+ locals.push({ id: "arr.0", name: "arr", type: arrayOf(STRING), mutable: false });
8738
+ body.push({ kind: "varDecl", localId: "arr.0", init: narrowed, loc });
8739
+ mRef = { kind: "varRef", localId: "arr.0", type: arrayOf(STRING), loc };
8740
+ }
8741
+ const num = (value) => ({ kind: "numLit", value, type: F64, loc });
8742
+ const elem = (index) => ({ kind: "arrayGet", arr: mRef, index: num(index), type: STRING, loc });
8743
+ const values = new Map();
8744
+ order.forEach((name, i) => {
8745
+ const idxs = indicesByName.get(name);
8746
+ if (idxs.length === 1) {
8747
+ values.set(name, elem(idxs[0]));
8748
+ return;
8749
+ }
8750
+ // Duplicates: the first non-"" occurrence (at most one
8751
+ // participates; all-"" answers "" like any nonparticipating slot).
8752
+ const id = `g${i}.0`;
8753
+ locals.push({ id, name: `g${i}`, type: STRING, mutable: true });
8754
+ body.push({ kind: "varDecl", localId: id, init: elem(idxs[0]), loc });
8755
+ const gRef = { kind: "varRef", localId: id, type: STRING, loc };
8756
+ for (const idx of idxs.slice(1)) {
8757
+ body.push({
8758
+ kind: "if",
8759
+ cond: { kind: "strEq", negated: false, left: gRef, right: { kind: "strLit", value: "", type: STRING, loc }, type: BOOL, loc },
8760
+ then: [{ kind: "assign", localId: id, value: elem(idx), loc }],
8761
+ else_: null,
8762
+ loc,
8763
+ });
8764
+ }
8765
+ values.set(name, gRef);
8766
+ });
8767
+ body.push({
8768
+ kind: "return",
8769
+ value: {
8770
+ kind: "recordLit",
8771
+ fields: order.map((name) => ({ name, value: values.get(name), overflow: true })),
8772
+ type: recordT,
8773
+ loc,
8774
+ },
8775
+ loc,
8776
+ });
8777
+ L.liftedFns.push({
8778
+ name: helper,
8779
+ params: [{ localId: "m.0", name: "m", type: recv.type }],
8780
+ returnType: recordT,
8781
+ locals,
8782
+ body,
8783
+ loc,
8784
+ });
8785
+ }
8786
+ return { kind: "call", callee: helper, args: [recv], type: recordT, loc };
8787
+ }
8283
8788
  /** Field read `obj.f` on class-instance and record receivers, through the
8284
8789
  * shared FieldTarget union (fieldGet / recordGet). Bound method references
8285
8790
  * on classes are rejected specifically; func-typed record fields are
@@ -8372,8 +8877,8 @@ export function lowerUnionProperty(L, expr) {
8372
8877
  // fallback's discipline.
8373
8878
  // A checker-union receiver whose VALUE lowered checked-dynamic (a
8374
8879
  // never-tainted JS chain — `cmd[1].length` on `const cmd = ['pwd',
8375
- // []]`, where the element read stayed a DOM node): read through the
8376
- // DOM keyed read like the unmappable-receiver path below the chain.
8880
+ // []]`, where the element read stayed a dyn node): read through the
8881
+ // dyn keyed read like the unmappable-receiver path below the chain.
8377
8882
  if (value.type.kind === "dyn") {
8378
8883
  const key = { kind: "strLit", value: expr.name.text, type: STRING, loc: locOf(expr.name) };
8379
8884
  return { kind: "dynKeyGet", key, value, type: DYN, loc: locOf(expr) };
@@ -8760,9 +9265,9 @@ export function symbolFieldTarget(L, expr) {
8760
9265
  * tsc-clean (the property types as the setter's param), but Node yields
8761
9266
  * undefined, which these property types cannot represent. */
8762
9267
  export function fieldGetExpr(L, target, loc, blame) {
8763
- // A record-shaped CHECKER target whose receiver VALUE lives in the DOM
9268
+ // A record-shaped CHECKER target whose receiver VALUE lives in the checked-dynamic tree
8764
9269
  // (a JS file-scope object-literal global): the checked-dynamic keyed
8765
- // read — dynKeyGet (a missing key answers the DOM undefined, exactly
9270
+ // read — dynKeyGet (a missing key answers the dyn undefined, exactly
8766
9271
  // JS); consumers validate (dynCheck) where a static type is required.
8767
9272
  if ((target.container === "record" || target.container === "recordOvf") &&
8768
9273
  target.obj.type.kind === "dyn") {
@@ -8843,15 +9348,15 @@ export function fieldGetExpr(L, target, loc, blame) {
8843
9348
  * call). A write to a getter-only property never gets here in a clean
8844
9349
  * program (tsc's TS2540 is the fence); the rejection is the backstop. */
8845
9350
  export function fieldSetStmt(L, target, value, loc, blame) {
8846
- // A record-shaped CHECKER target whose receiver VALUE lives in the DOM:
9351
+ // A record-shaped CHECKER target whose receiver VALUE lives in the checked-dynamic tree:
8847
9352
  // the checked-dynamic keyed write — dyn.keySet (later writes win,
8848
9353
  // insertion order; Node's TypeErrors on non-object receivers), the
8849
- // value converting into the DOM.
9354
+ // value converting into the checked-dynamic tree.
8850
9355
  if ((target.container === "record" || target.container === "recordOvf") &&
8851
9356
  target.obj.type.kind === "dyn") {
8852
9357
  const v = L.coerceToExpected(value, DYN);
8853
9358
  if (v.type.kind !== "dyn") {
8854
- L.unsupported("SC1101", blame, `storing '${L.fmt(value.type)}' values in a checked-dynamic object (the value cannot convert into the DOM)`);
9359
+ L.unsupported("SC1101", blame, `storing '${L.fmt(value.type)}' values in a checked-dynamic object (the value cannot convert into the checked-dynamic tree)`);
8855
9360
  }
8856
9361
  return {
8857
9362
  kind: "exprStmt",
@@ -8939,7 +9444,7 @@ export function lowerPromiseRejectCall(L, call, access) {
8939
9444
  const type = ctxT ? L.mapTypeOf(ctxT) : null;
8940
9445
  const reasonNode = call.arguments[0];
8941
9446
  // A CHECKED-DYNAMIC reason (`Promise.reject(value)` where value rode
8942
- // an untyped binding — the tracingChannel suite's shape): the DOM
9447
+ // an untyped binding — the tracingChannel suite's shape): the checked-dynamic tree
8943
9448
  // value IS the rejection payload (the thrown-dyn representation, so
8944
9449
  // identity survives to catch/unhandledRejection observers), and the
8945
9450
  // result is promise<dyn> when no context names a concrete one.
@@ -9082,7 +9587,7 @@ export function lowerFieldCompound(L, access, op, rhsNode, loc) {
9082
9587
  const concat = {
9083
9588
  kind: "strConcat",
9084
9589
  // String-context read: JS's String(unknown) over the field —
9085
- // the JS-exact DOM walker, never a checked cast.
9590
+ // the JS-exact dyn walker, never a checked cast.
9086
9591
  left: { kind: "toString", operand: read, type: STRING, loc },
9087
9592
  right: rhs,
9088
9593
  type: STRING,