@scriptc/compiler 0.0.0 → 0.0.2

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 (206) hide show
  1. package/LICENSE +202 -0
  2. package/ambient/package.json +3 -0
  3. package/ambient/scriptc-node-fallback.d.ts +3001 -0
  4. package/ambient/scriptc-overrides.d.ts +95 -0
  5. package/ambient/scriptc.d.ts +74 -0
  6. package/dist/backend/cc.d.ts +169 -0
  7. package/dist/backend/cc.js +897 -0
  8. package/dist/backend/cc.js.map +1 -0
  9. package/dist/backend/emission/emit-async.d.ts +145 -0
  10. package/dist/backend/emission/emit-async.js +996 -0
  11. package/dist/backend/emission/emit-async.js.map +1 -0
  12. package/dist/backend/emission/emit-exprs.d.ts +3 -0
  13. package/dist/backend/emission/emit-exprs.js +5949 -0
  14. package/dist/backend/emission/emit-exprs.js.map +1 -0
  15. package/dist/backend/emission/emit-island.d.ts +45 -0
  16. package/dist/backend/emission/emit-island.js +271 -0
  17. package/dist/backend/emission/emit-island.js.map +1 -0
  18. package/dist/backend/emission/emit-shapes.d.ts +142 -0
  19. package/dist/backend/emission/emit-shapes.js +575 -0
  20. package/dist/backend/emission/emit-shapes.js.map +1 -0
  21. package/dist/backend/emission/emit-stmts.d.ts +78 -0
  22. package/dist/backend/emission/emit-stmts.js +960 -0
  23. package/dist/backend/emission/emit-stmts.js.map +1 -0
  24. package/dist/backend/emission/emit-types.d.ts +65 -0
  25. package/dist/backend/emission/emit-types.js +652 -0
  26. package/dist/backend/emission/emit-types.js.map +1 -0
  27. package/dist/backend/emission/emit-walkers.d.ts +154 -0
  28. package/dist/backend/emission/emit-walkers.js +1587 -0
  29. package/dist/backend/emission/emit-walkers.js.map +1 -0
  30. package/dist/backend/emission/emitter.d.ts +452 -0
  31. package/dist/backend/emission/emitter.js +1249 -0
  32. package/dist/backend/emission/emitter.js.map +1 -0
  33. package/dist/backend/emission/may-throw.d.ts +15 -0
  34. package/dist/backend/emission/may-throw.js +250 -0
  35. package/dist/backend/emission/may-throw.js.map +1 -0
  36. package/dist/backend/llvm/blocks.d.ts +21 -0
  37. package/dist/backend/llvm/blocks.js +68 -0
  38. package/dist/backend/llvm/blocks.js.map +1 -0
  39. package/dist/backend/llvm/classes.d.ts +84 -0
  40. package/dist/backend/llvm/classes.js +438 -0
  41. package/dist/backend/llvm/classes.js.map +1 -0
  42. package/dist/backend/llvm/dyn.d.ts +123 -0
  43. package/dist/backend/llvm/dyn.js +2624 -0
  44. package/dist/backend/llvm/dyn.js.map +1 -0
  45. package/dist/backend/llvm/emitter.d.ts +3 -0
  46. package/dist/backend/llvm/emitter.js +10102 -0
  47. package/dist/backend/llvm/emitter.js.map +1 -0
  48. package/dist/backend/llvm/shapes.d.ts +78 -0
  49. package/dist/backend/llvm/shapes.js +754 -0
  50. package/dist/backend/llvm/shapes.js.map +1 -0
  51. package/dist/backend/llvm/unsupported.d.ts +6 -0
  52. package/dist/backend/llvm/unsupported.js +12 -0
  53. package/dist/backend/llvm/unsupported.js.map +1 -0
  54. package/dist/backend/llvm/walkers.d.ts +58 -0
  55. package/dist/backend/llvm/walkers.js +793 -0
  56. package/dist/backend/llvm/walkers.js.map +1 -0
  57. package/dist/backend/mangle.d.ts +124 -0
  58. package/dist/backend/mangle.js +232 -0
  59. package/dist/backend/mangle.js.map +1 -0
  60. package/dist/coverage/report.d.ts +65 -0
  61. package/dist/coverage/report.js +238 -0
  62. package/dist/coverage/report.js.map +1 -0
  63. package/dist/diagnostics/diagnostic.d.ts +140 -0
  64. package/dist/diagnostics/diagnostic.js +458 -0
  65. package/dist/diagnostics/diagnostic.js.map +1 -0
  66. package/dist/diagnostics/render.d.ts +11 -0
  67. package/dist/diagnostics/render.js +58 -0
  68. package/dist/diagnostics/render.js.map +1 -0
  69. package/dist/frontend/cjs-lexer.d.ts +20 -0
  70. package/dist/frontend/cjs-lexer.js +813 -0
  71. package/dist/frontend/cjs-lexer.js.map +1 -0
  72. package/dist/frontend/lowering/http2-constants.d.ts +1 -0
  73. package/dist/frontend/lowering/http2-constants.js +251 -0
  74. package/dist/frontend/lowering/http2-constants.js.map +1 -0
  75. package/dist/frontend/lowering/lib-boundary.d.ts +6 -0
  76. package/dist/frontend/lowering/lib-boundary.js +143 -0
  77. package/dist/frontend/lowering/lib-boundary.js.map +1 -0
  78. package/dist/frontend/lowering/lower-assert.d.ts +18 -0
  79. package/dist/frontend/lowering/lower-assert.js +1269 -0
  80. package/dist/frontend/lowering/lower-assert.js.map +1 -0
  81. package/dist/frontend/lowering/lower-builtins.d.ts +511 -0
  82. package/dist/frontend/lowering/lower-builtins.js +5249 -0
  83. package/dist/frontend/lowering/lower-builtins.js.map +1 -0
  84. package/dist/frontend/lowering/lower-calls.d.ts +581 -0
  85. package/dist/frontend/lowering/lower-calls.js +6286 -0
  86. package/dist/frontend/lowering/lower-calls.js.map +1 -0
  87. package/dist/frontend/lowering/lower-classes.d.ts +727 -0
  88. package/dist/frontend/lowering/lower-classes.js +4701 -0
  89. package/dist/frontend/lowering/lower-classes.js.map +1 -0
  90. package/dist/frontend/lowering/lower-comptime.d.ts +53 -0
  91. package/dist/frontend/lowering/lower-comptime.js +244 -0
  92. package/dist/frontend/lowering/lower-comptime.js.map +1 -0
  93. package/dist/frontend/lowering/lower-containers.d.ts +486 -0
  94. package/dist/frontend/lowering/lower-containers.js +6345 -0
  95. package/dist/frontend/lowering/lower-containers.js.map +1 -0
  96. package/dist/frontend/lowering/lower-dgram.d.ts +20 -0
  97. package/dist/frontend/lowering/lower-dgram.js +319 -0
  98. package/dist/frontend/lowering/lower-dgram.js.map +1 -0
  99. package/dist/frontend/lowering/lower-emitter.d.ts +10 -0
  100. package/dist/frontend/lowering/lower-emitter.js +688 -0
  101. package/dist/frontend/lowering/lower-emitter.js.map +1 -0
  102. package/dist/frontend/lowering/lower-enums.d.ts +22 -0
  103. package/dist/frontend/lowering/lower-enums.js +235 -0
  104. package/dist/frontend/lowering/lower-enums.js.map +1 -0
  105. package/dist/frontend/lowering/lower-expando.d.ts +35 -0
  106. package/dist/frontend/lowering/lower-expando.js +276 -0
  107. package/dist/frontend/lowering/lower-expando.js.map +1 -0
  108. package/dist/frontend/lowering/lower-exprs.d.ts +524 -0
  109. package/dist/frontend/lowering/lower-exprs.js +8111 -0
  110. package/dist/frontend/lowering/lower-exprs.js.map +1 -0
  111. package/dist/frontend/lowering/lower-generators.d.ts +50 -0
  112. package/dist/frontend/lowering/lower-generators.js +421 -0
  113. package/dist/frontend/lowering/lower-generators.js.map +1 -0
  114. package/dist/frontend/lowering/lower-inspect.d.ts +20 -0
  115. package/dist/frontend/lowering/lower-inspect.js +1074 -0
  116. package/dist/frontend/lowering/lower-inspect.js.map +1 -0
  117. package/dist/frontend/lowering/lower-island.d.ts +108 -0
  118. package/dist/frontend/lowering/lower-island.js +650 -0
  119. package/dist/frontend/lowering/lower-island.js.map +1 -0
  120. package/dist/frontend/lowering/lower-mixins.d.ts +96 -0
  121. package/dist/frontend/lowering/lower-mixins.js +534 -0
  122. package/dist/frontend/lowering/lower-mixins.js.map +1 -0
  123. package/dist/frontend/lowering/lower-modules.d.ts +135 -0
  124. package/dist/frontend/lowering/lower-modules.js +1613 -0
  125. package/dist/frontend/lowering/lower-modules.js.map +1 -0
  126. package/dist/frontend/lowering/lower-namespaces.d.ts +168 -0
  127. package/dist/frontend/lowering/lower-namespaces.js +706 -0
  128. package/dist/frontend/lowering/lower-namespaces.js.map +1 -0
  129. package/dist/frontend/lowering/lower-server.d.ts +90 -0
  130. package/dist/frontend/lowering/lower-server.js +3471 -0
  131. package/dist/frontend/lowering/lower-server.js.map +1 -0
  132. package/dist/frontend/lowering/lower-stmts.d.ts +361 -0
  133. package/dist/frontend/lowering/lower-stmts.js +5341 -0
  134. package/dist/frontend/lowering/lower-stmts.js.map +1 -0
  135. package/dist/frontend/lowering/lower-stream.d.ts +88 -0
  136. package/dist/frontend/lowering/lower-stream.js +1533 -0
  137. package/dist/frontend/lowering/lower-stream.js.map +1 -0
  138. package/dist/frontend/lowering/lower-test.d.ts +27 -0
  139. package/dist/frontend/lowering/lower-test.js +353 -0
  140. package/dist/frontend/lowering/lower-test.js.map +1 -0
  141. package/dist/frontend/lowering/lowerer.d.ts +1748 -0
  142. package/dist/frontend/lowering/lowerer.js +6257 -0
  143. package/dist/frontend/lowering/lowerer.js.map +1 -0
  144. package/dist/frontend/lowering/surfaces.d.ts +272 -0
  145. package/dist/frontend/lowering/surfaces.js +1096 -0
  146. package/dist/frontend/lowering/surfaces.js.map +1 -0
  147. package/dist/frontend/npm-static.d.ts +55 -0
  148. package/dist/frontend/npm-static.js +300 -0
  149. package/dist/frontend/npm-static.js.map +1 -0
  150. package/dist/frontend/npm.d.ts +330 -0
  151. package/dist/frontend/npm.js +1259 -0
  152. package/dist/frontend/npm.js.map +1 -0
  153. package/dist/frontend/program.d.ts +186 -0
  154. package/dist/frontend/program.js +2255 -0
  155. package/dist/frontend/program.js.map +1 -0
  156. package/dist/frontend/provenance-registry.d.ts +48 -0
  157. package/dist/frontend/provenance-registry.js +87 -0
  158. package/dist/frontend/provenance-registry.js.map +1 -0
  159. package/dist/frontend/provenance.d.ts +6 -0
  160. package/dist/frontend/provenance.js +459 -0
  161. package/dist/frontend/provenance.js.map +1 -0
  162. package/dist/frontend/resolve.d.ts +56 -0
  163. package/dist/frontend/resolve.js +682 -0
  164. package/dist/frontend/resolve.js.map +1 -0
  165. package/dist/frontend/shared.d.ts +67 -0
  166. package/dist/frontend/shared.js +241 -0
  167. package/dist/frontend/shared.js.map +1 -0
  168. package/dist/frontend/ts7/adapter.d.ts +7 -0
  169. package/dist/frontend/ts7/adapter.js +54 -0
  170. package/dist/frontend/ts7/adapter.js.map +1 -0
  171. package/dist/frontend/ts7/ast.d.ts +50 -0
  172. package/dist/frontend/ts7/ast.js +211 -0
  173. package/dist/frontend/ts7/ast.js.map +1 -0
  174. package/dist/frontend/ts7/census-check.d.ts +1 -0
  175. package/dist/frontend/ts7/census-check.js +12 -0
  176. package/dist/frontend/ts7/census-check.js.map +1 -0
  177. package/dist/frontend/ts7/checker.d.ts +140 -0
  178. package/dist/frontend/ts7/checker.js +544 -0
  179. package/dist/frontend/ts7/checker.js.map +1 -0
  180. package/dist/frontend/ts7/enums.d.ts +18 -0
  181. package/dist/frontend/ts7/enums.js +43 -0
  182. package/dist/frontend/ts7/enums.js.map +1 -0
  183. package/dist/frontend/ts7/program.d.ts +99 -0
  184. package/dist/frontend/ts7/program.js +278 -0
  185. package/dist/frontend/ts7/program.js.map +1 -0
  186. package/dist/frontend/ts7/world-check.d.ts +3 -0
  187. package/dist/frontend/ts7/world-check.js +34 -0
  188. package/dist/frontend/ts7/world-check.js.map +1 -0
  189. package/dist/frontend/types.d.ts +205 -0
  190. package/dist/frontend/types.js +2485 -0
  191. package/dist/frontend/types.js.map +1 -0
  192. package/dist/index.d.ts +85 -0
  193. package/dist/index.js +429 -0
  194. package/dist/index.js.map +1 -0
  195. package/dist/ir/nodes.d.ts +4334 -0
  196. package/dist/ir/nodes.js +1906 -0
  197. package/dist/ir/nodes.js.map +1 -0
  198. package/dist/ir/serialize.d.ts +4 -0
  199. package/dist/ir/serialize.js +34 -0
  200. package/dist/ir/serialize.js.map +1 -0
  201. package/dist/ir/validate.d.ts +36 -0
  202. package/dist/ir/validate.js +4885 -0
  203. package/dist/ir/validate.js.map +1 -0
  204. package/package.json +30 -6
  205. package/README.md +0 -3
  206. package/index.js +0 -2
@@ -0,0 +1,1096 @@
1
+ /* Surface tables and provenance predicates of the lowerer: the island
2
+ * surface (Math/number/string/global functions marshalable through the
3
+ * dynamic island), the node builtin-module call tables and their fence
4
+ * hints, the string/array/map/set method-name sets, the unsupported-syntax
5
+ * diagnostic tables, and the "which world declared this symbol" predicates
6
+ * (ambient scriptc.d.ts / standard lib / @types/node provenance). */
7
+ import * as ts from "../ts7/adapter.js";
8
+ import { BOOL, BYTES_U8, CHILD_T, DYN, F64, RUNTIME_ERROR_CLASSES, SPAWNRES_T, STATS_T, STRING, URL_T, VOID, arrayOf } from "../../ir/nodes.js";
9
+ import { isNodeTypesPath, requireSpecOf } from "../program.js";
10
+ /** Statement-level constructs rejected wholesale, keyed by syntax kind. */
11
+ export const UNSUPPORTED_STMT = {
12
+ // Top-level ClassDeclarations are supported (collected in run()); one
13
+ // reaching lowerStmt is nested inside a function.
14
+ [ts.SyntaxKind.ClassDeclaration]: { code: "SC1090", feature: "class declarations inside functions" },
15
+ // DoStatement / SwitchStatement are supported; handled in lowerStmt.
16
+ // ForOfStatement is supported (arrays); handled in lowerStmt.
17
+ // ThrowStatement / TryStatement are supported (exceptions); handled in
18
+ // lowerStmt (catch bindings and jumps crossing a finally stay rejected).
19
+ // ForInStatement is supported (records/index-signature shapes via the
20
+ // Object.keys walk, arrays via index strings, globalThis empty) —
21
+ // handled in lowerStmt via lowerForIn; tuple/class/other receivers keep
22
+ // named SC1052 fences there.
23
+ // LabeledStatement is supported (labels on loops/switches directly, a
24
+ // labeled block wrapper for everything else) — handled in lowerStmt via
25
+ // lowerLabeled; what remains fenced is labeled jumps naming a loop whose
26
+ // lowering is a label-free desugar (SC1050 at the jump site).
27
+ // EnumDeclaration is supported (constant members fold; computed members
28
+ // fence) — handled in lowerStmt via lower-enums.ts.
29
+ [ts.SyntaxKind.ModuleDeclaration]: { code: "SC1090", feature: "namespaces" },
30
+ [ts.SyntaxKind.DebuggerStatement]: { code: "SC1090", feature: "debugger statements" },
31
+ // `with` blocks rewrite identifier resolution at runtime — statically
32
+ // uncompilable by design, and already an error in strict-mode JS.
33
+ [ts.SyntaxKind.WithStatement]: {
34
+ code: "SC1090",
35
+ feature: "'with' statements (runtime scope injection has no static resolution — bind the object to a variable and read members through it)",
36
+ },
37
+ };
38
+ export const UNSUPPORTED_EXPR = {
39
+ // ArrowFunction / FunctionExpression are supported (closures); handled in
40
+ // lowerExpr before this table.
41
+ [ts.SyntaxKind.ClassExpression]: { code: "SC1020" },
42
+ // ObjectLiteralExpression is supported (records); handled in lowerExpr.
43
+ // ArrayLiteralExpression / ElementAccessExpression are supported (arrays);
44
+ // handled in lowerExpr.
45
+ // PropertyAccessExpression rejections are per-site in lowerExpr (the
46
+ // receiver lowers first — its blocker wins — and the residue names the
47
+ // property and receiver type; never a generic "property access").
48
+ // NewExpression / ThisKeyword are supported (classes); handled in lowerExpr.
49
+ // YieldExpression is supported (generators); handled in lowerExpr via
50
+ // lowerYield (value-position yield* and non-generator contexts keep
51
+ // their SC1071 fences there).
52
+ // TaggedTemplateExpression is supported (an interned per-site strings
53
+ // object + an ordinary call); handled in lowerExpr via lowerTaggedTemplate.
54
+ [ts.SyntaxKind.TypeOfExpression]: { code: "SC1090", feature: "typeof expressions" },
55
+ [ts.SyntaxKind.DeleteExpression]: { code: "SC1090", feature: "delete expressions" },
56
+ // RegularExpressionLiteral is supported (regex); handled in lowerExpr.
57
+ [ts.SyntaxKind.SpreadElement]: { code: "SC1090", feature: "spread arguments" },
58
+ // PostfixUnaryExpression is supported (expression-position ++/-- over
59
+ // f64 locals/globals — lowerIncDec); field/element receivers fence there.
60
+ };
61
+ /** The narrow-first hint every union fence shares — how to get from a
62
+ * union-typed value to the single arm the static compiler can use. One
63
+ * string so the fences speak with one voice (and match SC2003's hint). */
64
+ export const NARROW_FIRST = "narrow first: check a discriminant field, or compare with '!== undefined'/'!== null' for unit arms";
65
+ /* ── the options-record stance ───────────────────────────────────────────
66
+ * Node's object-options rule: an options record's unknown keys are simply
67
+ * IGNORED — the runtime reads the keys it documents and never validates
68
+ * the rest, so `http.request({ port, wibble: 1 })` runs identically to the
69
+ * wibble-free call. Rejecting a real Node program's option keys with a
70
+ * generic excess-property error is therefore itself a divergence, so the
71
+ * RECORD-shaped options parameters of the fallback surface carry an
72
+ * `[option: string]: unknown` index signature (every key typechecks) and
73
+ * each option WALK splits per key instead:
74
+ * - lowered keys take their lowering (bad VALUE shapes keep their
75
+ * per-key fences);
76
+ * - keys Node DOCUMENTS for the API but this compiler cannot honor
77
+ * fence BY NAME at the use site — an option that changes
78
+ * Node-observable behavior must work or fence, never silently drop;
79
+ * - undocumented keys DROP exactly as Node drops them, provided the
80
+ * value expression is side-effect-free (an effectful initializer
81
+ * fences: Node would have evaluated it, so skipping it would be
82
+ * observable).
83
+ * fenceOrDropOptionKey is that split's tail — option walks call it for
84
+ * every key outside their lowered set. */
85
+ /** True iff evaluating the option value can have no observable effect —
86
+ * the drop-safety test of the options-record stance. Literals, identifier
87
+ * reads (shorthand included), closures (created, never called — the key
88
+ * is unknown, so nothing can reach it), prefix +/-/! over these, and
89
+ * object/array literals of these all qualify; anything with a call,
90
+ * a property read (getters), or an assignment does not. */
91
+ export function sideEffectFreeOptionValue(node) {
92
+ let e = node;
93
+ while (ts.isParenthesizedExpression(e) || ts.isAsExpression(e) || ts.isTypeAssertion(e))
94
+ e = e.expression;
95
+ if (ts.isIdentifier(e))
96
+ return true; // includes `undefined`
97
+ if (ts.isStringLiteralLike(e) || ts.isNumericLiteral(e) || ts.isBigIntLiteral(e))
98
+ return true;
99
+ if (e.kind === ts.SyntaxKind.TrueKeyword || e.kind === ts.SyntaxKind.FalseKeyword ||
100
+ e.kind === ts.SyntaxKind.NullKeyword)
101
+ return true;
102
+ if (ts.isArrowFunction(e) || ts.isFunctionExpression(e))
103
+ return true;
104
+ if (ts.isPrefixUnaryExpression(e) &&
105
+ (e.operator === ts.SyntaxKind.MinusToken || e.operator === ts.SyntaxKind.PlusToken ||
106
+ e.operator === ts.SyntaxKind.ExclamationToken)) {
107
+ return sideEffectFreeOptionValue(e.operand);
108
+ }
109
+ if (ts.isObjectLiteralExpression(e)) {
110
+ return e.properties.every((p) => ts.isShorthandPropertyAssignment(p) ||
111
+ (ts.isPropertyAssignment(p) && !ts.isComputedPropertyName(p.name) &&
112
+ sideEffectFreeOptionValue(p.initializer)));
113
+ }
114
+ if (ts.isArrayLiteralExpression(e)) {
115
+ return e.elements.every((el) => !ts.isSpreadElement(el) && sideEffectFreeOptionValue(el));
116
+ }
117
+ return false;
118
+ }
119
+ /** The stance's tail: `key` is outside the walk's lowered set. Documented
120
+ * keys of the API fence by name (pointed per-key hints first, the walk's
121
+ * supported-options hint otherwise); undocumented keys drop like Node
122
+ * drops them when the value is side-effect-free, and fence when it is
123
+ * not. `prop` is the property entry (assignment or shorthand) for spans. */
124
+ export function fenceOrDropOptionKey(L, prop, key, api, documented, supportedHint, pointedHints) {
125
+ if (documented.has(key)) {
126
+ L.noLowering(`${api} option '${key}'`, prop, pointedHints?.[key] ?? supportedHint);
127
+ }
128
+ const value = ts.isPropertyAssignment(prop) ? prop.initializer : null;
129
+ if (value !== null && !sideEffectFreeOptionValue(value)) {
130
+ L.noLowering(`the undocumented ${api} option '${key}' with an effectful value`, prop, "Node ignores undocumented option keys and so does this compiler, but Node still evaluates the value — hoist it (const v = ...) or drop the entry");
131
+ }
132
+ // Dropped: Node's own ignore, byte-exact.
133
+ }
134
+ /** http.request/http.get's documented option keys (Node v24 — the
135
+ * `http.request(options)` and `net.socket.connect` tables); everything
136
+ * else on a client options literal is undocumented and drops. */
137
+ export const HTTP_CLIENT_DOCUMENTED_OPTIONS = new Set([
138
+ "agent", "auth", "createConnection", "defaultPort", "family", "headers",
139
+ "hints", "host", "hostname", "insecureHTTPParser", "joinDuplicateHeaders",
140
+ "localAddress", "localPort", "lookup", "maxHeaderSize", "method", "path",
141
+ "port", "protocol", "setDefaultHeaders", "setHost", "signal", "socketPath",
142
+ "timeout", "uniqueHeaders",
143
+ ]);
144
+ /** http.createServer / http.Server's documented option keys (Node v24 —
145
+ * http.createServer options + the net.Server knobs it forwards). The
146
+ * lowered pair is requireHostHeader/joinDuplicateHeaders; the rest fence
147
+ * by name here, and unknown keys drop like Node drops them. */
148
+ export const HTTP_SERVER_DOCUMENTED_OPTIONS = new Set([
149
+ "IncomingMessage", "ServerResponse", "allowHalfOpen", "connectionsCheckingInterval",
150
+ "headersTimeout", "highWaterMark", "insecureHTTPParser", "joinDuplicateHeaders",
151
+ "keepAlive", "keepAliveInitialDelay", "keepAliveTimeout", "maxHeaderSize",
152
+ "noDelay", "pauseOnConnect", "rejectNonStandardBodyWrites", "requestTimeout",
153
+ "requireHostHeader", "uniqueHeaders",
154
+ ]);
155
+ /** https.request adds tls.connect's client-side TLS knobs. */
156
+ export const HTTPS_CLIENT_DOCUMENTED_OPTIONS = new Set([
157
+ ...HTTP_CLIENT_DOCUMENTED_OPTIONS,
158
+ "ca", "cert", "checkServerIdentity", "ciphers", "clientCertEngine", "crl",
159
+ "dhparam", "ecdhCurve", "highWaterMark", "honorCipherOrder", "key",
160
+ "maxVersion", "minVersion", "passphrase", "pfx", "privateKeyEngine",
161
+ "privateKeyIdentifier", "rejectUnauthorized", "secureOptions",
162
+ "secureProtocol", "servername", "session", "sessionIdContext",
163
+ "sessionTimeout", "sigalgs", "ticketKeys",
164
+ ]);
165
+ /** tls.createServer / tls.createSecureContext / https.createServer /
166
+ * http2.createSecureServer's documented TLS-side option keys (Node v24 —
167
+ * tls.createSecureContext + tls.createServer + net.createServer). */
168
+ export const TLS_SERVER_DOCUMENTED_OPTIONS = new Set([
169
+ "ALPNProtocols", "ALPNCallback", "SNICallback", "allowHalfOpen", "ca",
170
+ "cert", "ciphers", "clientCertEngine", "crl", "dhparam", "ecdhCurve",
171
+ "enableTrace", "handshakeTimeout", "highWaterMark", "honorCipherOrder",
172
+ "keepAlive", "keepAliveInitialDelay", "key", "maxVersion", "minVersion",
173
+ "noDelay", "passphrase", "pauseOnConnect", "pfx", "privateKeyEngine",
174
+ "privateKeyIdentifier", "pskCallback", "pskIdentityHint",
175
+ "rejectUnauthorized", "requestCert", "requestOCSP", "secureContext",
176
+ "secureOptions", "secureProtocol", "sessionIdContext", "sessionTimeout",
177
+ "sigalgs", "ticketKeys",
178
+ ]);
179
+ /** http2.createSecureServer's documented keys: the h2 knobs on top of the
180
+ * TLS server set. */
181
+ export const HTTP2_SECURE_SERVER_DOCUMENTED_OPTIONS = new Set([
182
+ ...TLS_SERVER_DOCUMENTED_OPTIONS,
183
+ "allowHTTP1", "maxDeflateDynamicTableSize", "maxSettings",
184
+ "maxSessionMemory", "maxHeaderListPairs", "maxOutstandingPings",
185
+ "maxSendHeaderBlockLength", "origins", "paddingStrategy",
186
+ "peerMaxConcurrentStreams", "remoteCustomSettings", "selectPadding",
187
+ "settings", "streamResetBurst", "streamResetRate",
188
+ "unknownProtocolTimeout",
189
+ ]);
190
+ /** fs.watch's documented option keys. */
191
+ export const FS_WATCH_DOCUMENTED_OPTIONS = new Set([
192
+ "persistent", "recursive", "encoding", "signal",
193
+ ]);
194
+ /** fs.readdirSync's documented option keys. */
195
+ export const FS_READDIR_DOCUMENTED_OPTIONS = new Set([
196
+ "encoding", "withFileTypes", "recursive",
197
+ ]);
198
+ /** fs.writeFileSync's documented option keys. */
199
+ export const FS_WRITE_FILE_DOCUMENTED_OPTIONS = new Set([
200
+ "encoding", "mode", "flag", "flush", "signal",
201
+ ]);
202
+ /** dns.lookup's documented option keys. */
203
+ export const DNS_LOOKUP_DOCUMENTED_OPTIONS = new Set([
204
+ "family", "hints", "all", "order", "verbatim",
205
+ ]);
206
+ /** readline.createInterface's documented option keys. */
207
+ export const READLINE_DOCUMENTED_OPTIONS = new Set([
208
+ "input", "output", "completer", "terminal", "history", "historySize",
209
+ "removeHistoryDuplicates", "prompt", "crlfDelay", "escapeCodeTimeout",
210
+ "tabSize", "signal",
211
+ ]);
212
+ /** The lowered Array<T> method surface. Like STR_METHODS, membership is
213
+ * only half the check — the resolved symbol must be declared by the
214
+ * standard library (isStdlibMember); lib-declared array methods outside
215
+ * this set hit the SC2020 fence. */
216
+ export const ARRAY_METHODS = new Set([
217
+ "push",
218
+ "pop",
219
+ "concat",
220
+ "map",
221
+ "filter",
222
+ "forEach",
223
+ "find",
224
+ "findIndex",
225
+ "findLast",
226
+ "findLastIndex",
227
+ "some",
228
+ "every",
229
+ "at",
230
+ "flatMap",
231
+ "reduce",
232
+ "reduceRight",
233
+ "indexOf",
234
+ "includes",
235
+ "join",
236
+ "slice",
237
+ ]);
238
+ /** The lowered Map<K, V> method surface. Like ARRAY_METHODS, membership is
239
+ * only half the check — the resolved symbol must be declared by the
240
+ * standard library (isStdlibMember). `size` is a property, handled in
241
+ * property-access position. */
242
+ export const MAP_METHODS = new Set(["get", "set", "has", "delete", "clear", "forEach"]);
243
+ /** The lowered Set<T> method surface — Map's minus get/set plus add.
244
+ * `size` is a property, handled in property-access position. */
245
+ export const SET_METHODS = new Set(["add", "has", "delete", "clear", "forEach"]);
246
+ /** The ES2025 Set composition surface (union/intersection/…): desugared
247
+ * to interned helper loops over the set iteration primitives — no user
248
+ * code runs mid-loop, so the walks need no enter/exit bracketing. The
249
+ * lib accepts any ReadonlySetLike argument; only a real Set lowers (the
250
+ * argument's has/size must be the builtins for the desugar to BE the
251
+ * spec's algorithm). */
252
+ export const SET_COMBINE_METHODS = new Set([
253
+ "union",
254
+ "intersection",
255
+ "difference",
256
+ "symmetricDifference",
257
+ "isSubsetOf",
258
+ "isSupersetOf",
259
+ "isDisjointFrom",
260
+ ]);
261
+ export const COMPOUND_ASSIGN_OPS = {
262
+ [ts.SyntaxKind.PlusEqualsToken]: "+",
263
+ [ts.SyntaxKind.MinusEqualsToken]: "-",
264
+ [ts.SyntaxKind.AsteriskEqualsToken]: "*",
265
+ [ts.SyntaxKind.SlashEqualsToken]: "/",
266
+ [ts.SyntaxKind.AmpersandEqualsToken]: "&",
267
+ [ts.SyntaxKind.BarEqualsToken]: "|",
268
+ [ts.SyntaxKind.CaretEqualsToken]: "^",
269
+ [ts.SyntaxKind.LessThanLessThanEqualsToken]: "<<",
270
+ [ts.SyntaxKind.GreaterThanGreaterThanEqualsToken]: ">>",
271
+ [ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken]: ">>>",
272
+ [ts.SyntaxKind.PercentEqualsToken]: "%",
273
+ [ts.SyntaxKind.AsteriskAsteriskEqualsToken]: "**",
274
+ };
275
+ /** String methods lowered to `strIntrinsic` (`.length` is handled in
276
+ * property-access position). Membership here is only half the check: the
277
+ * resolved symbol must also be declared by the standard library (see
278
+ * isStdlibMember), so a user-declared `slice` on some other type can never
279
+ * smuggle itself in by name. `minArgs`/`maxArgs` pin the LOWERED call
280
+ * forms: the lib declares wider signatures (optional position/fromIndex
281
+ * parameters) that typecheck but have no lowering — those forms are fenced
282
+ * per site instead of silently dropping arguments. */
283
+ export const STR_METHODS = {
284
+ charCodeAt: { method: "charCodeAt", result: F64, minArgs: 1, maxArgs: 1 },
285
+ charAt: { method: "charAt", result: STRING, minArgs: 1, maxArgs: 1 },
286
+ indexOf: { method: "indexOf", result: F64, minArgs: 1, maxArgs: 2 },
287
+ // includes with a position argument is indexOf's clamp exactly (the
288
+ // spec routes both through StringIndexOf) — the emitter composes
289
+ // scr_str_index_of(...) != -1 for the two-argument form.
290
+ includes: { method: "includes", result: BOOL, minArgs: 1, maxArgs: 2 },
291
+ startsWith: { method: "startsWith", result: BOOL, minArgs: 1, maxArgs: 1 },
292
+ endsWith: { method: "endsWith", result: BOOL, minArgs: 1, maxArgs: 1 },
293
+ slice: { method: "slice", result: STRING, minArgs: 0, maxArgs: 2 },
294
+ // substring: slice's clamp-and-swap sibling (negatives clamp to 0
295
+ // instead of counting from the end; start > end swaps).
296
+ substring: { method: "substring", result: STRING, minArgs: 1, maxArgs: 2 },
297
+ repeat: { method: "repeat", result: STRING, minArgs: 1, maxArgs: 1 },
298
+ trim: { method: "trim", result: STRING, minArgs: 0, maxArgs: 0 },
299
+ trimStart: { method: "trimStart", result: STRING, minArgs: 0, maxArgs: 0 },
300
+ trimEnd: { method: "trimEnd", result: STRING, minArgs: 0, maxArgs: 0 },
301
+ // The Annex B aliases (lib.es2019 declares them as trimStart/trimEnd's
302
+ // deprecated twins — same behavior, per spec).
303
+ trimLeft: { method: "trimStart", result: STRING, minArgs: 0, maxArgs: 0 },
304
+ trimRight: { method: "trimEnd", result: STRING, minArgs: 0, maxArgs: 0 },
305
+ // The STRING-separator split, no limit (the lib's limit parameter
306
+ // fences by arity here; regex separators lowered earlier as
307
+ // regexIntrinsic). Empty separator splits per UTF-16 code unit —
308
+ // astral halves become U+FFFD (SEMANTICS.md divergence 2, the same
309
+ // substitution the island's boundary marshal applied).
310
+ split: { method: "split", result: arrayOf(STRING), minArgs: 1, maxArgs: 1 },
311
+ // padStart/padEnd with the fill omitted: Node pads with " " — the
312
+ // lowering completes the default (lowerStringMethodCall).
313
+ padStart: { method: "padStart", result: STRING, minArgs: 1, maxArgs: 2 },
314
+ padEnd: { method: "padEnd", result: STRING, minArgs: 1, maxArgs: 2 },
315
+ // The lre-backed pair (ECMA Default Case Conversion, final sigma
316
+ // included — scr_regex.c): static now, no island needed; their presence
317
+ // flips the regex LINK switch (moduleUsesRegex).
318
+ toLowerCase: { method: "toLowerCase", result: STRING, minArgs: 0, maxArgs: 0 },
319
+ toUpperCase: { method: "toUpperCase", result: STRING, minArgs: 0, maxArgs: 0 },
320
+ // The ES2024 well-formedness pair — no-ops over the runtime's
321
+ // well-formed storage (constant true / the identity, per spec on
322
+ // well-formed input; where Node would answer false the program already
323
+ // diverged at the string's creation, SEMANTICS.md 2).
324
+ isWellFormed: { method: "isWellFormed", result: BOOL, minArgs: 0, maxArgs: 0 },
325
+ toWellFormed: { method: "toWellFormed", result: STRING, minArgs: 0, maxArgs: 0 },
326
+ };
327
+ export const ISL_N1 = { args: [F64], ret: F64 };
328
+ export const ISL_N2 = { args: [F64, F64], ret: F64 };
329
+ export const boundaryIntoIslandMsg = (typeName) => `passing a value of type '${typeName}' into dynamically-executed ('any'-typed) code ` +
330
+ `(only numbers, strings, booleans, typed arrays, URLs, undefined/null-armed unions, ` +
331
+ `and JSON-safe records/arrays/unions can cross the boundary)`;
332
+ export const boundaryOutOfIslandMsg = (typeName) => `using an 'any' value where '${typeName}' is expected ` +
333
+ `(an 'any' value can only exit to numbers, strings, booleans, JSON-safe records/arrays/unions, ` +
334
+ `and 'T | undefined' over those)`;
335
+ /** The island-backed surface — standard-library APIs with no static
336
+ * runtime implementation (Math.*, number/string methods beyond the
337
+ * intrinsic set, parseFloat, ...). ONE table drives both sides of the
338
+ * gate: under --dynamic each entry lowers to marshal → engine execution →
339
+ * validated exit to the declared return type; without the flag each use
340
+ * site is a per-site SC2012. Membership here is only half the check — the
341
+ * resolved symbol must be declared by the standard library (provenance,
342
+ * never names), so a user's own `parseFloat` or `.toFixed` takes the
343
+ * ordinary paths. The lib declares MORE than this table (extra members,
344
+ * optional-parameter forms): everything untabled falls through to the
345
+ * SC2020 lib fence — never an ICE, never a link error. Exported so the
346
+ * table-vs-lib consistency test can check every entry against the lib's
347
+ * own declarations — a drifted entry fails a test instead of surprising a
348
+ * user. */
349
+ export const ISLAND_SURFACE = {
350
+ /** `Math.<fn>(...)` lowers to callMethod(globalGet("Math"), fn, args);
351
+ * the readonly number props (`Math.PI`) to getProp(globalGet("Math")).
352
+ * min/max/atan2/hypot/pow are declared with exactly two parameters
353
+ * (rest/optional parameters aren't representable). */
354
+ math: {
355
+ // floor, min/max (two-arg), and random are STATIC now
356
+ // (STATIC_MATH_FNS below).
357
+ fns: {
358
+ abs: ISL_N1, acos: ISL_N1, asin: ISL_N1, atan: ISL_N1, atan2: ISL_N2,
359
+ cbrt: ISL_N1, ceil: ISL_N1, cos: ISL_N1, exp: ISL_N1,
360
+ hypot: ISL_N2, log: ISL_N1, log2: ISL_N1, log10: ISL_N1, pow: ISL_N2,
361
+ round: ISL_N1,
362
+ sign: ISL_N1, sin: ISL_N1, sqrt: ISL_N1, tan: ISL_N1, trunc: ISL_N1,
363
+ },
364
+ props: { PI: F64, E: F64 },
365
+ },
366
+ /** Methods on `number` receivers. The receiver marshals by value; the
367
+ * engine auto-boxes primitives on method calls, so `this` binds the
368
+ * number exactly as in JS. toString takes an EXPLICIT radix (radix-free
369
+ * conversion is the static template-literal path). */
370
+ number: {
371
+ toFixed: { args: [F64], ret: STRING },
372
+ toPrecision: { args: [F64], ret: STRING },
373
+ toString: { args: [F64], ret: STRING },
374
+ },
375
+ /** Methods on `string` receivers beyond the static strIntrinsic set
376
+ * (split/pad/trimStart/trimEnd moved to STR_METHODS — static now).
377
+ * replace/replaceAll take STRING patterns (no regex is declared). `at`
378
+ * returns `string`: out of range, the engine yields undefined and the
379
+ * validated exit refuses it with a catchable TypeError (the documented
380
+ * divergence from `string | undefined`). */
381
+ string: {
382
+ replace: { args: [STRING, STRING], ret: STRING },
383
+ replaceAll: { args: [STRING, STRING], ret: STRING },
384
+ at: { args: [F64], ret: STRING },
385
+ },
386
+ /** Global functions: parseInt and isNaN are STATIC (lower-calls.ts →
387
+ * num.parseInt/num.isNaN), and parseFloat/isFinite lower statically
388
+ * over EXACTLY-typed arguments (a string for parseFloat, a number for
389
+ * isFinite — num.parseFloat/number.isFinite); these island entries
390
+ * carry every OTHER argument shape, where the ToNumber/ToString
391
+ * coercions stay engine territory. */
392
+ globals: {
393
+ parseFloat: { args: [STRING], ret: F64 },
394
+ isFinite: { args: [F64], ret: BOOL },
395
+ },
396
+ };
397
+ /** Math members with a STATIC lowering — each is one C call that IS the
398
+ * JS operation, at the tabled arity (floor: libm's floor; min/max: the
399
+ * NaN-poisoning ±0-ordered scalar folds; random: arc4random-backed
400
+ * uniform [0,1) — SEMANTICS.md 62). Checked BEFORE the island table
401
+ * (lowerIslandMethodCall), so the tabled arities compile statically and
402
+ * other arities keep the island/fence story; unioned with the island
403
+ * table for the methods-as-values fence (lowerMathProperty). */
404
+ export const STATIC_MATH_FNS = {
405
+ floor: { fn: "math.floor", arity: 1 },
406
+ abs: { fn: "math.abs", arity: 1 },
407
+ round: { fn: "math.round", arity: 1 },
408
+ min: { fn: "math.min", arity: 2 },
409
+ max: { fn: "math.max", arity: 2 },
410
+ random: { fn: "math.random", arity: 0 },
411
+ };
412
+ /** The lowerable surface of the supported node builtin modules, keyed by
413
+ * CANONICAL module name (both "fs" and "node:fs" land on "fs" — see
414
+ * canonicalBuiltinModule). Like STR_METHODS, membership is only half the
415
+ * check — the identifier must be an IMPORT BINDING from that module's
416
+ * specifier (see builtinImportOf), so a user function named `readFileSync`
417
+ * can never smuggle itself in. Members @types/node declares beyond these
418
+ * tables typecheck and hit the SC2020-family fence at their use site. */
419
+ /** node:path's POSIX members — the bare-module table on posix targets and
420
+ * "path/posix" everywhere (Node's own aliasing: bare `path` IS the
421
+ * target platform's implementation, and each named namespace answers ITS
422
+ * platform's rules on any host). toNamespacedPath is the posix identity
423
+ * (Node: a non-op on posix systems). */
424
+ const PATH_MODULE_FNS = {
425
+ join: { fn: "path.join", params: [STRING], result: STRING, variadicPack: true },
426
+ resolve: { fn: "path.resolve", params: [STRING], result: STRING, variadicPack: true },
427
+ normalize: { fn: "path.normalize", params: [STRING], result: STRING },
428
+ dirname: { fn: "path.dirname", params: [STRING], result: STRING },
429
+ basename: { fn: "path.basename", params: [STRING, STRING], result: STRING, defaults: [""] },
430
+ extname: { fn: "path.extname", params: [STRING], result: STRING },
431
+ isAbsolute: { fn: "path.isAbsolute", params: [STRING], result: BOOL },
432
+ relative: { fn: "path.relative", params: [STRING, STRING], result: STRING },
433
+ toNamespacedPath: { fn: "path.toNamespacedPath", params: [STRING], result: STRING },
434
+ };
435
+ /** The win32 twins (scr_path.c's Node-v24 path.win32 port, byte-for-byte):
436
+ * "path/win32" everywhere, and the bare-module table when the build
437
+ * TARGETS win32 — Node on Windows is path.win32. */
438
+ const PATH_WIN32_MODULE_FNS = {
439
+ join: { fn: "path.win32Join", params: [STRING], result: STRING, variadicPack: true },
440
+ resolve: { fn: "path.win32Resolve", params: [STRING], result: STRING, variadicPack: true },
441
+ normalize: { fn: "path.win32Normalize", params: [STRING], result: STRING },
442
+ dirname: { fn: "path.win32Dirname", params: [STRING], result: STRING },
443
+ basename: { fn: "path.win32Basename", params: [STRING, STRING], result: STRING, defaults: [""] },
444
+ extname: { fn: "path.win32Extname", params: [STRING], result: STRING },
445
+ isAbsolute: { fn: "path.win32IsAbsolute", params: [STRING], result: BOOL },
446
+ relative: { fn: "path.win32Relative", params: [STRING, STRING], result: STRING },
447
+ toNamespacedPath: { fn: "path.win32ToNamespacedPath", params: [STRING], result: STRING },
448
+ };
449
+ export const BUILTIN_MODULE_FNS = {
450
+ fs: {
451
+ readFileSync: { fn: "fs.readFileSync", params: [STRING, STRING], result: STRING },
452
+ writeFileSync: { fn: "fs.writeFileSync", params: [STRING, STRING], result: VOID },
453
+ appendFileSync: { fn: "fs.appendFileSync", params: [STRING, STRING], result: VOID },
454
+ existsSync: { fn: "fs.existsSync", params: [STRING], result: BOOL },
455
+ mkdirSync: { fn: "fs.mkdirSync", params: [STRING], result: VOID },
456
+ rmSync: { fn: "fs.rmSync", params: [STRING], result: VOID },
457
+ rmdirSync: { fn: "fs.rmdirSync", params: [STRING], result: VOID },
458
+ readdirSync: { fn: "fs.readdirSync", params: [STRING], result: arrayOf(STRING) },
459
+ statSync: { fn: "fs.statSync", params: [STRING], result: STATS_T },
460
+ mkdtempSync: { fn: "fs.mkdtempSync", params: [STRING], result: STRING },
461
+ // accessSync's omitted mode completes to 0 (F_OK) in the special case
462
+ // in lowerBuiltinModuleCall — string `defaults` can't spell a number.
463
+ accessSync: { fn: "fs.accessSync", params: [STRING, F64], result: VOID },
464
+ unlinkSync: { fn: "fs.unlinkSync", params: [STRING], result: VOID },
465
+ chmodSync: { fn: "fs.chmodSync", params: [STRING, F64], result: VOID },
466
+ chownSync: { fn: "fs.chownSync", params: [STRING, F64, F64], result: VOID },
467
+ // The 2-argument form only: Node's mode flags (COPYFILE_EXCL, ...)
468
+ // land on the arity fence.
469
+ copyFileSync: { fn: "fs.copyFileSync", params: [STRING, STRING], result: VOID },
470
+ // statSync's no-follow sibling; stats.isSymbolicLink answers what the
471
+ // follow-free snapshot saw.
472
+ lstatSync: { fn: "fs.lstatSync", params: [STRING], result: STATS_T },
473
+ // realpath(3) — Node's realpathSync (failures spell syscall "lstat",
474
+ // Node's own message shape).
475
+ realpathSync: { fn: "fs.realpathSync", params: [STRING], result: STRING },
476
+ // The fd pair behind spawn's fd-stdio form (the daemon-log idiom:
477
+ // openSync(logPath, "a") → spawn stdio ["ignore", fd, fd] →
478
+ // closeSync). openSync takes Node's string flags ("r", "w", "a", the
479
+ // +/x variants — unknown flags throw Node's TypeError text); the
480
+ // numeric-mode third argument fences by arity.
481
+ openSync: { fn: "fs.openSync", params: [STRING, STRING], result: F64 },
482
+ // The 4-argument buffer form (fd, buffer, offset, length) — what the
483
+ // Node test harness's parseTestMetadata uses; the position parameter
484
+ // and options-object forms fence by arity/shape.
485
+ readSync: { fn: "fs.readSync", params: [F64, BYTES_U8, F64, F64], result: F64 },
486
+ closeSync: { fn: "fs.closeSync", params: [F64], result: VOID },
487
+ // Entirely special-cased (lowerFsWatchCall — the callback needs an
488
+ // adapter per listener shape); this entry only routes the dispatch.
489
+ watch: { fn: "fs.watch", params: [], result: VOID },
490
+ },
491
+ "fs/promises": {
492
+ // The sync operations behind already-settled promises: failures
493
+ // REJECT (catchable at the await) instead of throwing. readFile is
494
+ // utf8-fenced exactly like readFileSync (same special case below).
495
+ readFile: { fn: "fsp.readFile", params: [STRING, STRING], result: { kind: "promise", inner: STRING } },
496
+ writeFile: { fn: "fsp.writeFile", params: [STRING, STRING], result: { kind: "promise", inner: VOID } },
497
+ mkdir: { fn: "fsp.mkdir", params: [STRING], result: { kind: "promise", inner: VOID } },
498
+ readdir: { fn: "fsp.readdir", params: [STRING], result: { kind: "promise", inner: arrayOf(STRING) } },
499
+ rm: { fn: "fsp.rm", params: [STRING], result: { kind: "promise", inner: VOID } },
500
+ stat: { fn: "fsp.stat", params: [STRING], result: { kind: "promise", inner: STATS_T } },
501
+ unlink: { fn: "fsp.unlink", params: [STRING], result: { kind: "promise", inner: VOID } },
502
+ chmod: { fn: "fsp.chmod", params: [STRING, F64], result: { kind: "promise", inner: VOID } },
503
+ },
504
+ // The bare module's POSIX-target binding; a win32 target rebinds it to
505
+ // the win32 table (builtinModuleFnsOf — Node on Windows IS path.win32).
506
+ path: PATH_MODULE_FNS,
507
+ // Real Node modules ("node:path/posix" resolves), and the receivers of
508
+ // `path.posix.<member>` / `path.win32.<member>` through the namespace
509
+ // chain (builtinNamespaceModuleOf composes "path" + ".posix" to these
510
+ // keys). Both namespaces carry the FULL surface on every platform,
511
+ // Node's own rule: each answers its own platform's semantics anywhere.
512
+ "path/posix": PATH_MODULE_FNS,
513
+ "path/win32": PATH_WIN32_MODULE_FNS,
514
+ os: {
515
+ // One platform implementation: os.platform() === process.platform.
516
+ platform: { fn: "process.platform", params: [], result: STRING },
517
+ homedir: { fn: "os.homedir", params: [], result: STRING },
518
+ tmpdir: { fn: "os.tmpdir", params: [], result: STRING },
519
+ // uname(2)'s release field — Node's own implementation.
520
+ release: { fn: "os.release", params: [], result: STRING },
521
+ // uname(2)'s sysname field ("Darwin", "Linux", "Windows_NT") — the
522
+ // libFn/runtime pair predates this entry (the portless surface used
523
+ // them); this row makes the MODULE call reach them (test/common's
524
+ // isAIX/isIBMi getters are the canonical callers).
525
+ type: { fn: "os.type", params: [], result: STRING },
526
+ // Total physical memory in bytes — same predating-pair story.
527
+ totalmem: { fn: "os.totalmem", params: [], result: F64 },
528
+ // Entirely special-cased (lowerOsNetworkInterfacesCall): the result is
529
+ // the call site's mapped Dict<NetworkInterfaceInfo[]> shape, verified
530
+ // structurally there — this entry only routes the dispatch.
531
+ networkInterfaces: { fn: "os.networkInterfaces", params: [], result: VOID },
532
+ // Special-cased too (lowerOsUserInfoCall): the call site's mapped
533
+ // UserInfo record assembles field-by-field from scalar libCalls.
534
+ userInfo: { fn: "os.userName", params: [], result: VOID },
535
+ },
536
+ crypto: {
537
+ randomUUID: { fn: "crypto.randomUUID", params: [], result: STRING },
538
+ // A real Buffer result. The composed randomBytes(n).toString(enc)
539
+ // form KEEPS its one-libCall lowering (lowerCryptoComposedCall runs
540
+ // first and the Buffer never materializes there); this entry covers
541
+ // the bare calls and non-composed uses.
542
+ randomBytes: { fn: "crypto.randomBytes", params: [F64], result: BYTES_U8 },
543
+ },
544
+ zlib: {
545
+ // Buffer in, Buffer out, Node's default options; string inputs fence
546
+ // per site (see the zlib special case in lowerBuiltinModuleCall).
547
+ // cc.ts links libz only when these appear on the IR.
548
+ deflateSync: { fn: "zlib.deflateSync", params: [BYTES_U8], result: BYTES_U8 },
549
+ inflateSync: { fn: "zlib.inflateSync", params: [BYTES_U8], result: BYTES_U8 },
550
+ },
551
+ url: {
552
+ // fileURLToPath accepts a URL value OR a string — the call lowering
553
+ // picks the libFn by the ARGUMENT's static type (see the special case
554
+ // in lowerBuiltinModuleCall); the table entry carries the string form.
555
+ fileURLToPath: { fn: "url.fileURLToPathStr", params: [STRING], result: STRING },
556
+ pathToFileURL: { fn: "url.pathToFileURL", params: [STRING], result: URL_T },
557
+ },
558
+ child_process: {
559
+ // spawnSync's and spawn's call completions are entirely special-cased
560
+ // (an omitted args list completes to an empty string[]; spawnSync
561
+ // accepts exactly { encoding: "utf8" } as options, spawn requires
562
+ // exactly { stdio: "ignore" } — see lowerBuiltinModuleCall); the
563
+ // entries carry the canonical shapes.
564
+ spawnSync: { fn: "cp.spawnSync", params: [STRING, arrayOf(STRING)], result: SPAWNRES_T },
565
+ spawn: { fn: "cp.spawn", params: [STRING, arrayOf(STRING)], result: CHILD_T },
566
+ // execFileSync(file, args?, options?) and execSync(command, options?)
567
+ // share the cp.execSync runtime entry (execSync sets the shell flag);
568
+ // both call completions are special-cased in lowerBuiltinModuleCall.
569
+ // The entries carry canonical shapes for the coverage/fence machinery.
570
+ execFileSync: { fn: "cp.execSync", params: [STRING, arrayOf(STRING)], result: STRING },
571
+ execSync: { fn: "cp.execSync", params: [STRING], result: STRING },
572
+ },
573
+ // node:util lowers through TWO paths, neither tabled here: promisify
574
+ // only in the const-binding-over-execFile shape — recognized in
575
+ // lowerVarDecl / collectGlobals BEFORE any call lowering runs — and
576
+ // inspect/format/formatWithOptions through the util spoke
577
+ // (lower-inspect.ts: per-type synthesized traversal helpers,
578
+ // compile-time format strings), which both dispatch paths try before
579
+ // the member fence below.
580
+ util: {},
581
+ // node:string_decoder's surface is the StringDecoder CLASS — new/write/
582
+ // end are special-cased (lowerNew + lowerStringDecoderMethodCall); no
583
+ // function members exist to table.
584
+ string_decoder: {},
585
+ // node:readline: createInterface's options are entirely special-cased
586
+ // (exactly { input: process.stdin, output?: process.stdout } — see
587
+ // lowerBuiltinModuleCall); the entry carries the canonical shape. The
588
+ // interface's question/close/on lower through
589
+ // lowerReadlineMethodCall.
590
+ readline: {
591
+ createInterface: { fn: "rl.create", params: [], result: F64 },
592
+ },
593
+ // node:net and node:http lower ENTIRELY through the server spoke
594
+ // (lower-server.ts — every call shape is special-cased: closures, the
595
+ // optional connect host, writeHead's literal headers); the member list
596
+ // is NET_MODULE_FNS there. The modules still need keys here so future
597
+ // table entries have a home, and so the "recognized module, unlowered
598
+ // member" fence wording applies.
599
+ net: {
600
+ // The process-wide happy-eyeballs attempt budget: one runtime double
601
+ // in the core unit (never links scr_net.c by itself), default 250ms
602
+ // like Node's.
603
+ getDefaultAutoSelectFamilyAttemptTimeout: { fn: "net.getAutoSelTimeout", params: [], result: F64 },
604
+ setDefaultAutoSelectFamilyAttemptTimeout: { fn: "net.setAutoSelTimeout", params: [F64], result: VOID },
605
+ },
606
+ http: {},
607
+ // node:tls and node:https ride the same spoke (tls.createServer's
608
+ // options + secureConnection handler, https.createServer, and the
609
+ // https client's rejectUnauthorized/ca options are all special-cased
610
+ // in lower-server.ts).
611
+ tls: {},
612
+ https: {},
613
+ // node:dgram and node:dns lower ENTIRELY through the dgram spoke
614
+ // (lower-dgram.ts — createSocket's option literal, dns.lookup's
615
+ // options + callback, and the Socket method surface are all
616
+ // special-cased); the keys exist for the same reason net/http's do.
617
+ dgram: {},
618
+ dns: {},
619
+ // node:assert lowers ENTIRELY through the assert spoke (lower-assert.ts
620
+ // — every call shape is special-cased: optional messages complete, the
621
+ // comparisons pick per-type libCalls, deep equality synthesizes
622
+ // helpers); the keys exist so unlowered members fence module-qualified
623
+ // with the hints below.
624
+ assert: {},
625
+ "assert/strict": {},
626
+ // node:test lowers ENTIRELY through the test spoke (lower-test.ts —
627
+ // registrations, suites, hooks, the TestContext surface); the key
628
+ // exists so unlowered members fence module-qualified.
629
+ test: {},
630
+ // node:timers/promises — the delay-only setTimeout and bare setImmediate
631
+ // (void promises the shared timer heap settles). The arity completion
632
+ // (omitted delay = Node's 1ms) and the value/options fences are
633
+ // special-cased in lowerBuiltinModuleCall; these entries carry the
634
+ // canonical shapes and route the dispatch.
635
+ "timers/promises": {
636
+ setTimeout: { fn: "tp.setTimeout", params: [F64], result: { kind: "promise", inner: VOID } },
637
+ setImmediate: { fn: "tp.setImmediate", params: [], result: { kind: "promise", inner: VOID } },
638
+ },
639
+ // node:diagnostics_channel — the pub/sub core. channel() answers an f64
640
+ // handle (types.ts maps Channel to F64); the subscriber arguments box
641
+ // into the DOM (special-cased in lowerBuiltinModuleCall — the entries
642
+ // carry canonical shapes and route the dispatch). The Channel method
643
+ // surface lowers through lowerDcChannelMethodCall/lowerDcChannelProperty.
644
+ diagnostics_channel: {
645
+ channel: { fn: "dc.channel", params: [STRING], result: F64 },
646
+ subscribe: { fn: "dc.subscribe", params: [STRING, DYN], result: VOID },
647
+ unsubscribe: { fn: "dc.unsubscribe", params: [STRING, DYN], result: BOOL },
648
+ hasSubscribers: { fn: "dc.hasSubscribers", params: [STRING], result: BOOL },
649
+ // The string form's canonical shape; the collection form (an object
650
+ // literal of five Channels) is special-cased in lowerBuiltinModuleCall.
651
+ tracingChannel: { fn: "dc.tracingChannel", params: [STRING], result: F64 },
652
+ },
653
+ };
654
+ /** Builtin-module CONSTANTS (value reads of named imports): each lowers to
655
+ * a literal of its value's kind. The bare-module values are the TARGET
656
+ * platform's (builtinModuleConstOf swaps the win32 values in under a win32
657
+ * triple — Node's own platform conditionals); the named namespaces answer
658
+ * THEIR platform's constants everywhere. worker_threads/cluster carry the
659
+ * main-thread truths: a compiled binary IS the main thread (no JS-engine
660
+ * thread machinery) and never runs as a cluster worker (cluster forks the
661
+ * node binary itself) — Node's own answers for a directly-run script. */
662
+ export const BUILTIN_MODULE_CONSTS = {
663
+ path: { sep: "/", delimiter: ":" },
664
+ "path/posix": { sep: "/", delimiter: ":" },
665
+ "path/win32": { sep: "\\", delimiter: ";" },
666
+ os: { EOL: "\n" },
667
+ worker_threads: { isMainThread: true, threadId: 0 },
668
+ cluster: { isPrimary: true, isMaster: true, isWorker: false },
669
+ };
670
+ /** The win32-target overrides of the bare modules' constants: path's
671
+ * constants ARE the path/win32 namespace's, and os.EOL is CRLF (Node's
672
+ * `isWindows ? '\r\n' : '\n'`). */
673
+ const WIN32_TARGET_CONSTS = {
674
+ path: BUILTIN_MODULE_CONSTS["path/win32"],
675
+ os: { EOL: "\r\n" },
676
+ };
677
+ /** url's win32-target table: fileURLToPath is receiver-form-special-cased
678
+ * either way, and pathToFileURL swaps to its win32 IR flavor (the same
679
+ * runtime call, but in the may-throw seed — Node's win32 arm raises UNC
680
+ * TypeErrors where the posix arm never throws). */
681
+ const URL_WIN32_MODULE_FNS = {
682
+ fileURLToPath: { fn: "url.fileURLToPathStr", params: [STRING], result: STRING },
683
+ pathToFileURL: { fn: "url.pathToFileURLWin32", params: [STRING], result: URL_T },
684
+ };
685
+ /** BUILTIN_MODULE_FNS with the platform-conditional modules bound per
686
+ * TARGET: a win32 triple compiles Node-on-Windows semantics — `path` is
687
+ * path.win32 and url's bridge takes the win32 flavors. Fence wording is
688
+ * unaffected — callers keep naming the module the source spelled. */
689
+ export function builtinModuleFnsOf(L, module) {
690
+ if (L.targetPlatform === "win32") {
691
+ if (module === "path")
692
+ return BUILTIN_MODULE_FNS["path/win32"];
693
+ if (module === "url")
694
+ return URL_WIN32_MODULE_FNS;
695
+ }
696
+ return ownEntry(BUILTIN_MODULE_FNS, module);
697
+ }
698
+ /** Own-property lookup — lowerer.ts's own(), duplicated locally because
699
+ * lowerer.ts already imports surfaces.ts (a value import back would
700
+ * cycle). The tables here are plain object literals keyed by USER-written
701
+ * module/member names, so a bare index would also find Object.prototype
702
+ * members ("toString", "constructor", "__proto__"). */
703
+ function ownEntry(table, key) {
704
+ return Object.hasOwn(table, key) ? table[key] : undefined;
705
+ }
706
+ /** One MEMBER's table entry, own-property-safe (`path.toString` must not
707
+ * answer Object.prototype.toString as a BuiltinModuleFn). */
708
+ export function builtinModuleFnOf(L, module, member) {
709
+ const mod = builtinModuleFnsOf(L, module);
710
+ return mod ? ownEntry(mod, member) : undefined;
711
+ }
712
+ /** One member's fence hint, own-property-safe (a collision would print an
713
+ * inherited function into the diagnostic text). */
714
+ export function builtinFenceHintOf(module, member) {
715
+ const mod = ownEntry(BUILTIN_MODULE_FENCE_HINTS, module);
716
+ return mod ? ownEntry(mod, member) : undefined;
717
+ }
718
+ /** BUILTIN_MODULE_CONSTS with the win32-target overrides applied — the
719
+ * constants twin of builtinModuleFnsOf. */
720
+ export function builtinModuleConstOf(L, module, member) {
721
+ if (L.targetPlatform === "win32") {
722
+ const mod = ownEntry(WIN32_TARGET_CONSTS, module);
723
+ const w = mod ? ownEntry(mod, member) : undefined;
724
+ if (w !== undefined)
725
+ return w;
726
+ }
727
+ const mod = ownEntry(BUILTIN_MODULE_CONSTS, module);
728
+ return mod ? ownEntry(mod, member) : undefined;
729
+ }
730
+ /** The IR literal of a builtin-module constant — one place so both value
731
+ * paths (named-import bindings and namespace member reads) emit the same
732
+ * kinds. */
733
+ export function builtinConstLit(value, loc) {
734
+ if (typeof value === "string")
735
+ return { kind: "strLit", value, type: STRING, loc };
736
+ if (typeof value === "number")
737
+ return { kind: "numLit", value, type: F64, loc };
738
+ return { kind: "boolLit", value, type: BOOL, loc };
739
+ }
740
+ /** Member-specific hints for RECOGNIZED builtin modules whose member has
741
+ * no lowering. deflateSync/inflateSync lower now (Buffers are real);
742
+ * the rest of the zlib surface points at the lowered pair. */
743
+ export const ZLIB_HINT = "deflateSync and inflateSync are the lowered zlib surface";
744
+ /** The loose-equality quartet's shared hint: == coercion has no lowering
745
+ * anywhere in this compiler, and Node itself points at the strict forms. */
746
+ const ASSERT_LOOSE_HINT = "loose == equality has no lowering — the strict forms compare with " +
747
+ "Object.is/structural equality like Node's assert/strict module, where " +
748
+ "equal IS strictEqual";
749
+ const ASSERT_MODULE_HINTS = {
750
+ equal: ASSERT_LOOSE_HINT,
751
+ notEqual: ASSERT_LOOSE_HINT,
752
+ deepEqual: ASSERT_LOOSE_HINT,
753
+ notDeepEqual: ASSERT_LOOSE_HINT,
754
+ ifError: "test explicitly instead: assert.strictEqual(err, null) / " +
755
+ "assert.strictEqual(err, undefined)",
756
+ rejects: "await the promise inside assert.throws's callback story instead: " +
757
+ "try { await p; assert.fail(\"expected rejection\") } catch { ... }",
758
+ doesNotReject: "await the promise directly — an unexpected rejection already fails the test",
759
+ doesNotThrow: "call the function directly — an unexpected throw already fails the test",
760
+ AssertionError: "the class itself has no lowering — catch and test err.name === " +
761
+ '"AssertionError" or err.code === "ERR_ASSERTION"',
762
+ };
763
+ export const BUILTIN_MODULE_FENCE_HINTS = {
764
+ assert: ASSERT_MODULE_HINTS,
765
+ // The strict module's equal/notEqual/deepEqual/notDeepEqual ARE the
766
+ // strict comparisons (aliased in the spoke) — only the members with no
767
+ // lowering in either module fence here.
768
+ "assert/strict": {
769
+ ifError: ASSERT_MODULE_HINTS["ifError"],
770
+ rejects: ASSERT_MODULE_HINTS["rejects"],
771
+ doesNotReject: ASSERT_MODULE_HINTS["doesNotReject"],
772
+ doesNotThrow: ASSERT_MODULE_HINTS["doesNotThrow"],
773
+ AssertionError: ASSERT_MODULE_HINTS["AssertionError"],
774
+ },
775
+ util: {
776
+ promisify: "the one lowered shape is a const binding over child_process.execFile: " +
777
+ "const execFileAsync = promisify(execFile), then call execFileAsync directly",
778
+ // child_process.execFile itself exists to be promisified — the same
779
+ // story from the other end.
780
+ },
781
+ child_process: {
782
+ execFile: "the callback form has no lowering — promisify it: " +
783
+ "const execFileAsync = promisify(execFile) (from node:util), or use execFileSync",
784
+ },
785
+ crypto: {
786
+ createHash: "the one lowered shape is the composed chain " +
787
+ 'createHash("sha256").update(data).digest("hex") — the Hash handle itself has no lowering',
788
+ },
789
+ zlib: {
790
+ gzipSync: ZLIB_HINT,
791
+ gunzipSync: ZLIB_HINT,
792
+ unzipSync: ZLIB_HINT,
793
+ brotliCompressSync: ZLIB_HINT,
794
+ brotliDecompressSync: ZLIB_HINT,
795
+ },
796
+ http2: {
797
+ connect: "HTTP/2 client sessions have no lowering — the lowered http2 surface is the SERVER side: " +
798
+ "createSecureServer({ allowHTTP1: true, cert, key }), which serves HTTP/1.1 only " +
799
+ "(ALPN never offers h2 — h2-capable clients negotiate down); an HTTP/1.1 client is https.request",
800
+ createServer: "cleartext (h2c) servers have no lowering — the lowered http2 surface is " +
801
+ "createSecureServer({ allowHTTP1: true, cert, key }), which serves HTTP/1.1 over TLS " +
802
+ "(plain HTTP/1.1 is http.createServer)",
803
+ },
804
+ tls: {
805
+ createSecureContext: "the lowered form is createSecureContext({ cert, key }) — an opaque SecureContext handle " +
806
+ "for SNI callbacks (http2.createSecureServer accepts SNICallback); other options fence by name",
807
+ connect: "the lowered TLS client is https.request({ hostname, port, path, method, ca?, rejectUnauthorized? }); " +
808
+ "raw tls.connect sockets have no lowering yet",
809
+ },
810
+ };
811
+ /** The MEMBER chokepoint of the lib fence: called from the property-read
812
+ * and method-call fallbacks after every lowering has passed. When the
813
+ * accessed member is stdlib-declared, the use is standard-library surface
814
+ * with no lowering — report SC2020 naming `<container>.<member>` (the
815
+ * receiver's global name when it IS a stdlib global like Math or Promise,
816
+ * its widened type text otherwise). Returns silently for non-stdlib
817
+ * members so the caller's generic rejection applies. */
818
+ export function stdlibMemberFence(L, access) {
819
+ const sym = L.checker.getSymbolAtLocation(access.name);
820
+ if (!sym || !L.isStdlibSymbol(sym))
821
+ return;
822
+ const member = access.name.text;
823
+ const recv = access.expression;
824
+ // Name the container the way the source reads: the global's name when
825
+ // the receiver IS a stdlib global (Math, process), the dotted path for
826
+ // a member of one (process.stdout — its TYPE text would be the useless
827
+ // 'WriteStream & { fd: 1; }'), the receiver's widened type text
828
+ // otherwise.
829
+ const globalPathOf = (e) => {
830
+ if (ts.isIdentifier(e) && L.isStdlibSymbol(L.checker.getSymbolAtLocation(e))) {
831
+ return e.text;
832
+ }
833
+ if (ts.isPropertyAccessExpression(e) && ts.isIdentifier(e.expression)) {
834
+ const base = globalPathOf(e.expression);
835
+ if (base !== null)
836
+ return `${base}.${e.name.text}`;
837
+ }
838
+ return null;
839
+ };
840
+ const container = globalPathOf(recv) ??
841
+ L.checker.typeToString(L.checker.getBaseTypeOfLiteralType(L.typeOf(recv)));
842
+ let hint;
843
+ const recvIr = L.mapTypeOf(L.typeOf(recv));
844
+ if (recvIr?.kind === "promise" && (member === "then" || member === "catch" || member === "finally")) {
845
+ hint =
846
+ "'await' is the supported way to chain (p.then(f) with one fulfillment handler, p.catch " +
847
+ "with an INLINE handler, and p.finally(cb) compile) — try { await p } catch (e) is the general form";
848
+ }
849
+ else if (recvIr?.kind === "f64" && member === "toString") {
850
+ hint = "radix-free conversion is a template literal away: `${x}`; toString(radix) runs under --dynamic";
851
+ }
852
+ else if (container === "Math" && (member === "min" || member === "max")) {
853
+ hint =
854
+ `Math.${member} lowers with exactly two arguments or one number[] spread — ` +
855
+ `nest calls (Math.${member}(a, Math.${member}(b, c))) or spread an array (Math.${member}(...xs))`;
856
+ }
857
+ else if (recvIr?.kind === "array" &&
858
+ member === "index" &&
859
+ (container === "RegExpExecArray" || container === "RegExpMatchArray")) {
860
+ hint =
861
+ "'.index' is supported on the const binding of a for-of over a DIRECT matchAll call " +
862
+ "(for (const m of s.matchAll(re)) { ... m.index ... }); stored rows are honest " +
863
+ "string[] slices without it";
864
+ }
865
+ else if (recvIr?.kind === "array" && member === "flat") {
866
+ hint =
867
+ "flat has no lowering (flatMap does) — flatten into an accumulator instead: " +
868
+ "for (const x of xs) for (const y of x) out.push(y)";
869
+ }
870
+ else if (recvIr?.kind === "object" &&
871
+ RUNTIME_ERROR_CLASSES.has(recvIr.className) &&
872
+ member === "stack") {
873
+ hint =
874
+ "stack traces are not captured (frames would need runtime bookkeeping); " +
875
+ "name, message, and toString() are available";
876
+ }
877
+ else if (container === "process.stdin" || (container === "process" && member === "stdin")) {
878
+ hint =
879
+ "isTTY, destroy(), on/once of the data/end/error events, and " +
880
+ "`for await (const chunk of process.stdin)` are the supported stdin surface " +
881
+ '(or read everything at once: readFileSync(0, "utf8") from node:fs)';
882
+ }
883
+ else if ((container === "process" && (member === "stdout" || member === "stderr")) ||
884
+ container === "process.stdout" ||
885
+ container === "process.stderr") {
886
+ // @types/node territory: the fallback declarations don't have these
887
+ // members at all (a type error), so this fence only fires with the
888
+ // project's real Node types adopted.
889
+ hint =
890
+ "console.log is the supported way to write a line to stdout; " +
891
+ "the stdout/stderr stream objects have no lowering";
892
+ }
893
+ else if (container === "console") {
894
+ hint = "console.log(number | string | boolean ...) is the supported console surface";
895
+ }
896
+ else if (member === "prototype") {
897
+ hint =
898
+ "prototype objects are not values here (method lookup is static) — call the method on an instance instead";
899
+ }
900
+ else if (recvIr?.kind === "func" &&
901
+ (member === "call" || member === "apply" || member === "bind")) {
902
+ hint =
903
+ "call the function directly — plain and arrow functions here never read `this`, so " +
904
+ "f.call(thisArg, ...args) is f(...args), and bind's partial application is an arrow: (...rest) => f(a, ...rest)";
905
+ }
906
+ else if (recvIr?.kind === "string" &&
907
+ (member === "match" || member === "matchAll" || member === "search")) {
908
+ hint =
909
+ "the regex-LITERAL argument forms lower (s.match(/re/)); a string pattern constructs " +
910
+ "a RegExp at runtime, which has no lowering";
911
+ }
912
+ else if (container === "Intl" || container.startsWith("Intl.") ||
913
+ ["NumberFormat", "DateTimeFormat", "PluralRules", "Collator", "Locale",
914
+ "RelativeTimeFormat", "ListFormat", "Segmenter", "DisplayNames"].includes(container) ||
915
+ member === "toLocaleString" || member === "toLocaleDateString" ||
916
+ member === "toLocaleTimeString" || member === "toLocaleLowerCase" ||
917
+ member === "toLocaleUpperCase") {
918
+ hint =
919
+ "locale- and ICU-backed behavior lives outside the static runtime (the localeCompare " +
920
+ "stance: code-unit order, no collation/locale data) — format with template literals, " +
921
+ "toFixed, and toString instead";
922
+ }
923
+ else if (container === "Object" && member === "assign") {
924
+ hint =
925
+ "spread instead: { ...a, ...b } builds the merged record; what lowers: the empty-target " +
926
+ "literal-source shape (Object.assign({}, { ... }) IS the source literal) and merges INTO " +
927
+ "an index-signature record whose value slot every source value enters — " +
928
+ "other aliased targets are real mutation, and a function target (Object.assign(fn, { prop })) " +
929
+ "is a function-with-properties value the model cannot represent: bind the property separately";
930
+ }
931
+ L.noLowering(`${container}.${member}`, access, hint, sym);
932
+ }
933
+ /** True iff the accessed member is declared by the standard library —
934
+ * the same technique as isConsoleLog: trust declarations, not names. */
935
+ export function isStdlibMember(L, access) {
936
+ const direct = L.checker.getSymbolAtLocation(access.name);
937
+ if (direct)
938
+ return L.isStdlibSymbol(direct);
939
+ // An IMPLICIT-ANY instance body (the checker sees an `any` receiver —
940
+ // no member symbol resolves) or an ALIASED-TYPEOF narrow (the checker
941
+ // sees the un-narrowed union — `val.length` on String|Number has no
942
+ // property symbol): typeOf answers the bound/narrowed type — resolve
943
+ // the member from it, the same provenance answer the checker would
944
+ // give on a typed receiver (`path.startsWith` with path bound string
945
+ // IS String.prototype.startsWith).
946
+ if (L.implicitParamTypes !== null || L.aliasNarrowTypes.size > 0) {
947
+ const viaType = L.checker.getPropertyOfType(L.typeOf(access.expression), access.name.text);
948
+ return L.isStdlibSymbol(viaType ?? undefined);
949
+ }
950
+ return false;
951
+ }
952
+ /** The provenance check the CHILD receiver lowerings use: a stdlib
953
+ * member, OR a member the user's own child-shaped interface declares
954
+ * (the NgrokChildProcess idiom — mapType's duck rule admits an
955
+ * interface only when EVERY member names the lowered ChildProcess
956
+ * surface, so accepting its members here cannot widen the surface;
957
+ * the receiver-kind gate already proved the type maps to child). */
958
+ export function isChildSurfaceMember(L, access) {
959
+ if (isStdlibMember(L, access))
960
+ return true;
961
+ const sym = L.checker.getSymbolAtLocation(access.name);
962
+ const decl = sym ? L.checker.declarationsOf(sym)[0] : undefined;
963
+ const iface = decl?.parent;
964
+ if (!decl || !iface || !ts.isInterfaceDeclaration(iface))
965
+ return false;
966
+ if (L.isStdlibFile(decl.getSourceFile()))
967
+ return false;
968
+ return L.mapTypeOf(L.checker.getTypeAtLocation(iface.name))?.kind === "child";
969
+ }
970
+ /** True iff some declaration of the symbol lives in the standard library
971
+ * (shipped ambient or default lib — the provenance half of every
972
+ * supported-surface check). `some`, not `[0]`: divergence overrides merge
973
+ * with lib interfaces, so a member can carry declarations from both. A
974
+ * user's own declaration is in neither, so shadowing never matches. */
975
+ export function isStdlibSymbol(L, symbol) {
976
+ return !!symbol && L.checker.declarationsOf(symbol).some((d) => L.isStdlibFile(d.getSourceFile()));
977
+ }
978
+ /** The canonical stdlib-global name `expr` denotes, or null. Three
979
+ * spellings reach the same global (Node's own aliasing):
980
+ * - the bare identifier (`process`), name + provenance checked;
981
+ * - the `global` identifier — Node's alias of globalThis — and
982
+ * `globalThis` itself both canonicalize to "globalThis";
983
+ * - a property read off globalThis (`globalThis.process`,
984
+ * `global.process`) — `declare var` globals ARE properties of
985
+ * `typeof globalThis`, same symbol either way;
986
+ * - a local alias binding (`const process = globalThis.process`, the
987
+ * tamper-guard prologue) registered in stdlibGlobalAliases. */
988
+ export function stdlibGlobalNameOf(L, expr) {
989
+ if (ts.isParenthesizedExpression(expr))
990
+ return stdlibGlobalNameOf(L, expr.expression);
991
+ if (ts.isIdentifier(expr)) {
992
+ // `globalThis` itself: a reserved intrinsic — tsc rejects user
993
+ // bindings of the name, and its special symbol carries no ordinary
994
+ // declarations for the provenance check to see.
995
+ if (expr.text === "globalThis")
996
+ return "globalThis";
997
+ const symbol = L.checker.getSymbolAtLocation(expr);
998
+ if (!symbol)
999
+ return null;
1000
+ const alias = L.stdlibGlobalAliases.get(symbol);
1001
+ if (alias !== undefined)
1002
+ return alias;
1003
+ // An IMPORTED binding of a builtin module's re-exported global
1004
+ // (`import { Buffer } from "node:buffer"` — Node's module spelling
1005
+ // of the same object) resolves through the alias to the fallback
1006
+ // file's own export, so provenance and name check out exactly like
1007
+ // the bare-global spelling. A user module re-exporting its own
1008
+ // `Buffer` resolves to a user-file declaration and still misses.
1009
+ const resolved = symbol.flags & ts.SymbolFlags.Alias ? L.checker.getAliasedSymbol(symbol) : symbol;
1010
+ if (!L.isStdlibSymbol(resolved))
1011
+ return null;
1012
+ return resolved.name === "global" ? "globalThis" : resolved.name;
1013
+ }
1014
+ if (ts.isPropertyAccessExpression(expr) && !expr.questionDotToken) {
1015
+ if (stdlibGlobalNameOf(L, expr.expression) !== "globalThis")
1016
+ return null;
1017
+ const symbol = L.checker.getSymbolAtLocation(expr.name);
1018
+ if (!symbol || !L.isStdlibSymbol(symbol))
1019
+ return null;
1020
+ return symbol.name === "global" ? "globalThis" : symbol.name;
1021
+ }
1022
+ return null;
1023
+ }
1024
+ /** True iff `expr` denotes THE standard-library global `name` — name AND
1025
+ * provenance, because neither alone suffices: several globals share
1026
+ * member names (Math.log must never lower as console.log), and a user
1027
+ * binding shadowing a global's name has a different, non-stdlib symbol.
1028
+ * All of stdlibGlobalNameOf's spellings answer (globalThis.process is
1029
+ * process; `const process = globalThis.process` aliases through). */
1030
+ export function isStdlibGlobal(L, expr, name) {
1031
+ return stdlibGlobalNameOf(L, expr) === name;
1032
+ }
1033
+ /** The member name of a `<global>.<member>` access whose receiver is THE
1034
+ * standard-library global `name` (console, JSON, process, Math). Null for
1035
+ * anything else, so property-lowering chains keep trying other
1036
+ * receivers. */
1037
+ export function stdlibGlobalMember(L, access, name) {
1038
+ if (access.questionDotToken)
1039
+ return null;
1040
+ return L.isStdlibGlobal(access.expression, name) ? access.name.text : null;
1041
+ }
1042
+ /** `const process = globalThis.process` (and any `const x = <stdlib
1043
+ * global>` snapshot — the suite harness's tamper-guard prologue): the
1044
+ * binding is pure alias plumbing. Nothing in a compiled program can
1045
+ * reassign a stdlib global, so the snapshot IS the global: the symbol
1046
+ * registers in stdlibGlobalAliases (every receiver check resolves
1047
+ * through it — see stdlibGlobalNameOf) and the declaration emits
1048
+ * nothing. Only the globals with lowered member surfaces alias this
1049
+ * way; aliasing, say, `Math` would change nothing (its members lower
1050
+ * by receiver too). Returns true when recognized. */
1051
+ export function stdlibGlobalAliasDecl(L, nameNode, init) {
1052
+ if (!init || !ts.isIdentifier(nameNode))
1053
+ return false;
1054
+ // `const process = require('node:process')`: Node's process MODULE is
1055
+ // the global process object (module.exports === globalThis.process),
1056
+ // so the binding aliases the global exactly like `const process =
1057
+ // globalThis.process`. Preflight admits exactly this shape
1058
+ // (processModuleAliasRequire7); commander's lib/command.js opens with
1059
+ // it.
1060
+ const requireSpec = requireSpecOf(init);
1061
+ const name = requireSpec === "process" || requireSpec === "node:process"
1062
+ ? "process"
1063
+ : stdlibGlobalNameOf(L, init);
1064
+ if (name === null)
1065
+ return false;
1066
+ // Only alias OBJECT-shaped globals whose members lower by receiver
1067
+ // identity (process, console, globalThis itself). Function-valued
1068
+ // globals (setTimeout) taken as values are a different story — the
1069
+ // ordinary value paths (and their fences) apply.
1070
+ if (name !== "process" && name !== "console" && name !== "globalThis")
1071
+ return false;
1072
+ const symbol = L.checker.getSymbolAtLocation(nameNode);
1073
+ if (!symbol)
1074
+ return false;
1075
+ L.stdlibGlobalAliases.set(symbol, name);
1076
+ return true;
1077
+ }
1078
+ /** True iff the symbol is declared ONLY by the adopted @types/node
1079
+ * surface (no es-lib or shipped-ambient declaration merges in) — chooses
1080
+ * the SC2020 fence's wording: "typed by @types/node", not "standard
1081
+ * library". */
1082
+ export function nodeTypesOnlySymbol(L, sym) {
1083
+ const decls = sym ? L.checker.declarationsOf(sym) : undefined;
1084
+ if (!decls || decls.length === 0)
1085
+ return false;
1086
+ let viaNode = false;
1087
+ for (const d of decls) {
1088
+ const sf = d.getSourceFile();
1089
+ if (sf.isDeclarationFile && isNodeTypesPath(sf.fileName))
1090
+ viaNode = true;
1091
+ else if (L.isStdlibFile(sf))
1092
+ return false;
1093
+ }
1094
+ return viaNode;
1095
+ }
1096
+ //# sourceMappingURL=surfaces.js.map