@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,1906 @@
1
+ /* scriptc IR — the only interface between frontend and backends.
2
+ *
3
+ * Design rules (see docs/ir.md for node-by-node semantics):
4
+ * - Plain JSON-safe discriminated unions: no classes, no parent pointers,
5
+ * no cycles. Cross-references are by string id.
6
+ * - Every node carries `loc` (source span) and every expression carries its
7
+ * computed `type` — backends never re-derive types.
8
+ * - Structured control flow (statement tree), not basic blocks: the only
9
+ * current backend is C and there are no optimization passes yet. When a mid-end
10
+ * arrives, a structured→CFG lowering pass slots in after validation.
11
+ * - The type union is deliberately written for extension (`dyn` shipped
12
+ * with JSON, `promise` with async). All switches over IrType/IrExpr/
13
+ * IrStmt must have exhaustive `never` default arms so adding a member
14
+ * turns into compile errors, not silent misbehavior.
15
+ */
16
+ /** The ref kinds whose values are JS OBJECTS for truthiness: always true
17
+ * ([] and {} included) — toBool accepts them (the operand still evaluates;
18
+ * the test is constant), and per-union truthiness helpers answer their
19
+ * arms with `true`. */
20
+ export const REF_TRUTHY_KINDS = new Set([
21
+ // symbol is not a JS object, but every symbol is truthy — the same
22
+ // constant-true answer.
23
+ "symbol",
24
+ "array", "map", "set", "regex", "url", "searchParams", "stats", "spawnRes", "child",
25
+ "netServer", "netSocket", "http2Session", "http2Stream", "dgramSocket", "testCtx", "httpReq", "httpRes", "httpClientReq",
26
+ "secureCtx", "fsWatcher", "childStream", "procStream", "bytes", "func", "object", "record", "promise",
27
+ // A generator object is a JS object: always truthy.
28
+ "generator",
29
+ // A class object is a JS object (constructors are functions): always truthy.
30
+ "classval",
31
+ ]);
32
+ export const F64 = { kind: "f64" };
33
+ export const BYTES_U8 = { kind: "bytes", elem: "u8" };
34
+ export const STRING = { kind: "string" };
35
+ export const BOOL = { kind: "bool" };
36
+ export const REGEX = { kind: "regex" };
37
+ export const URL_T = { kind: "url" };
38
+ export const SEARCH_PARAMS_T = { kind: "searchParams" };
39
+ export const SYMBOL_T = { kind: "symbol" };
40
+ export const STATS_T = { kind: "stats" };
41
+ export const SPAWNRES_T = { kind: "spawnRes" };
42
+ export const CHILD_T = { kind: "child" };
43
+ export const NETSERVER_T = { kind: "netServer" };
44
+ export const NETSOCKET_T = { kind: "netSocket" };
45
+ export const HTTP2SESSION_T = { kind: "http2Session" };
46
+ export const HTTP2STREAM_T = { kind: "http2Stream" };
47
+ export const DGRAMSOCK_T = { kind: "dgramSocket" };
48
+ export const TESTCTX_T = { kind: "testCtx" };
49
+ export const HTTPREQ_T = { kind: "httpReq" };
50
+ export const HTTPRES_T = { kind: "httpRes" };
51
+ export const HTTPCLIENTREQ_T = { kind: "httpClientReq" };
52
+ export const SECURECTX_T = { kind: "secureCtx" };
53
+ export const FSWATCHER_T = { kind: "fsWatcher" };
54
+ export const CHILDSTREAM_T = { kind: "childStream" };
55
+ export const PROCSTREAM_T = { kind: "procStream" };
56
+ export const VOID = { kind: "void" };
57
+ export const DYN = { kind: "dyn" };
58
+ export const JSVAL = { kind: "jsval" };
59
+ export const CAUGHT = { kind: "caught" };
60
+ export const UNDEFINED_T = { kind: "undefinedT" };
61
+ export const NULL_T = { kind: "nullT" };
62
+ /** True for the payload-less unit kinds (`undefined`/`null`). Unit values
63
+ * exist only inside unions: a unit-armed union instance is tag-only, so
64
+ * wrapping allocates no payload, narrowing to a unit arm produces no value
65
+ * (the frontend never emits it), and releasing has nothing to release. */
66
+ export function isUnitType(t) {
67
+ return t.kind === "undefinedT" || t.kind === "nullT";
68
+ }
69
+ export function arrayOf(elem) {
70
+ return { kind: "array", elem };
71
+ }
72
+ export function bytesOf(elem) {
73
+ return { kind: "bytes", elem };
74
+ }
75
+ export function mapOf(key, value) {
76
+ return { kind: "map", key, value };
77
+ }
78
+ export function setOf(elem) {
79
+ return { kind: "set", elem };
80
+ }
81
+ /** The Map KEY fence: string (content) or number (SameValueZero) — the two
82
+ * kinds a hash of the VALUE is honest for. Booleans, objects, and the rest
83
+ * of JS's anything-goes keys stay out. Shared by the frontend's
84
+ * type mapping/diagnostics and the validator. Set ELEMENTS use the same
85
+ * fence: a set is hashed storage of its elements exactly as a map is of
86
+ * its keys (isSupportedSetElem is this predicate under its own name). */
87
+ export function isSupportedMapKey(t) {
88
+ return t.kind === "f64" || t.kind === "string";
89
+ }
90
+ /** The Set ELEMENT fence — Map's key fence plus the refcounted HANDLE
91
+ * kinds stored under identity hashing (SameValueZero for JS objects IS
92
+ * reference identity, so a Set of server handles — portless's auxiliary-
93
+ * server registry — is honest hashed storage; SCR_MAP_KEY_REF in the
94
+ * runtime). netServer is the one handle admitted so far: it drops its
95
+ * listener closures at close, so a set-in-listener cycle is temporary —
96
+ * the child precedent's story. Symbols are identity values by DESIGN —
97
+ * SameValueZero on a symbol IS pointer identity, so a Set of symbols (the
98
+ * sentinel-registry idiom) is the same honest hashed storage with no
99
+ * cycle risk at all (symbols hold only strings). */
100
+ export function isSupportedSetElem(t) {
101
+ return isSupportedMapKey(t) || t.kind === "netServer" || t.kind === "symbol";
102
+ }
103
+ /** The Map VALUE fence: scalars plus every refcounted kind EXCEPT
104
+ * func/promise/dyn/jsval (and map itself — no maps of maps).
105
+ * Record/object/union values can point back at the map holding them, which
106
+ * is exactly why ref-valued maps are cycle-capable (see the backend's
107
+ * cycle analysis and docs/memory.md). Shared frontend/validator. */
108
+ export function isSupportedMapValue(t) {
109
+ switch (t.kind) {
110
+ case "f64":
111
+ case "string":
112
+ case "bool":
113
+ case "record":
114
+ case "object":
115
+ case "union":
116
+ case "array":
117
+ return true;
118
+ // A spawned child handle (Map<string, ChildProcess> — the mdns
119
+ // publisher registry): an ordinary refcounted pointer value (the
120
+ // scr_child_retain/release adapters), stored and read like any ref.
121
+ // It holds closures only until its terminal event fires (then drops
122
+ // them), so a child in a map cannot cycle through the map; the RC
123
+ // adapters handle its lifetime.
124
+ case "child":
125
+ return true;
126
+ // A SecureContext handle (Map<string, tls.SecureContext> — the SNI
127
+ // callback's per-hostname cache): immutable, holds no references —
128
+ // the same ordinary refcounted pointer story as child, without even
129
+ // the pre-settle closures.
130
+ case "secureCtx":
131
+ return true;
132
+ // A promise (Map<string, Promise<SecureContext>> — the SNI callback's
133
+ // in-flight dedupe map): refcounted, with trace/cycle support already
134
+ // in place (scr_promise_trace_v — the race machinery's refcounting).
135
+ // It holds reaction closures only until it settles (settled promises
136
+ // drop them), so the child rule's temporary-cycle story applies: a
137
+ // pending promise whose callbacks capture the map is a cycle only
138
+ // until settlement, and the collector handles the never-settling case.
139
+ case "promise":
140
+ return true;
141
+ // A class object (Map<string, typeof Shape> — the registry/factory
142
+ // idiom): an immortal static behind no-op RC adapters — it holds no
143
+ // references at all, so no trace, no cycles, ever.
144
+ case "classval":
145
+ return true;
146
+ default:
147
+ return false;
148
+ }
149
+ }
150
+ /** The INDEX-SIGNATURE value fence (`{ [k: string]: V }` shapes): the map
151
+ * VALUE kinds — the overflow portion IS a string-keyed map — plus dyn
152
+ * (`unknown`, an unknown-valued pricing-table shape: overflow reads surface ordinary
153
+ * dyn values validated by the usual checked casts). Shared frontend
154
+ * (mapType) / validator. */
155
+ export function isSupportedIndexValue(t) {
156
+ return t.kind === "dyn" || isSupportedMapValue(t);
157
+ }
158
+ export function funcOf(params, ret) {
159
+ return { kind: "func", params, ret };
160
+ }
161
+ /** Canonical, injective text form of an IrType — the building block of
162
+ * shape/union identity keys, generic-function instantiation keys, and the
163
+ * backend's per-type helper interning (jsonStringify/dynCheck walkers).
164
+ * Nested records/unions are represented by their (already interned, already
165
+ * canonical) shapeId/unionId, so keys stay finite and comparable. Lives in
166
+ * the IR (not the frontend) because both ends need it. */
167
+ export function typeKey(t) {
168
+ switch (t.kind) {
169
+ case "f64":
170
+ case "string":
171
+ case "bool":
172
+ case "regex":
173
+ case "url":
174
+ case "searchParams":
175
+ case "symbol":
176
+ case "stats":
177
+ case "spawnRes":
178
+ case "child":
179
+ case "netServer":
180
+ case "netSocket":
181
+ case "http2Session":
182
+ case "http2Stream":
183
+ case "dgramSocket":
184
+ case "testCtx":
185
+ case "httpReq":
186
+ case "httpRes":
187
+ case "httpClientReq":
188
+ case "secureCtx":
189
+ case "fsWatcher":
190
+ case "childStream":
191
+ case "procStream":
192
+ case "dyn":
193
+ case "jsval":
194
+ case "caught":
195
+ case "void":
196
+ return t.kind;
197
+ case "undefinedT":
198
+ return "undefined";
199
+ case "nullT":
200
+ return "null";
201
+ case "array":
202
+ return `array<${typeKey(t.elem)}>`;
203
+ case "bytes":
204
+ return `bytes<${t.elem}>`;
205
+ case "map":
206
+ return `map<${typeKey(t.key)},${typeKey(t.value)}>`;
207
+ case "set":
208
+ return `set<${typeKey(t.elem)}>`;
209
+ case "func":
210
+ return `func(${[...t.params.map(typeKey), ...(t.rest ? ["...dyn[]"] : [])].join(",")})=>${typeKey(t.ret)}`;
211
+ case "object":
212
+ return `object:${t.className}`;
213
+ case "classval":
214
+ return `classval:${t.className}`;
215
+ case "record":
216
+ return `record:${t.shapeId}`;
217
+ case "union":
218
+ return `union:${t.unionId}`;
219
+ case "promise":
220
+ return `promise<${typeKey(t.inner)}>`;
221
+ case "generator":
222
+ return `generator<${typeKey(t.yieldT)},${typeKey(t.retT)},${typeKey(t.nextT)}>`;
223
+ default: {
224
+ const _exhaustive = t;
225
+ void _exhaustive;
226
+ throw new Error("unreachable");
227
+ }
228
+ }
229
+ }
230
+ export function typeEquals(a, b) {
231
+ if (a.kind === "array")
232
+ return b.kind === "array" && typeEquals(a.elem, b.elem);
233
+ if (a.kind === "bytes")
234
+ return b.kind === "bytes" && a.elem === b.elem;
235
+ if (a.kind === "map") {
236
+ return b.kind === "map" && typeEquals(a.key, b.key) && typeEquals(a.value, b.value);
237
+ }
238
+ if (a.kind === "set")
239
+ return b.kind === "set" && typeEquals(a.elem, b.elem);
240
+ if (a.kind === "func") {
241
+ return (b.kind === "func" &&
242
+ a.params.length === b.params.length &&
243
+ (a.rest === true) === (b.rest === true) &&
244
+ a.params.every((p, i) => typeEquals(p, b.params[i])) &&
245
+ typeEquals(a.ret, b.ret));
246
+ }
247
+ if (a.kind === "object")
248
+ return b.kind === "object" && a.className === b.className;
249
+ if (a.kind === "classval")
250
+ return b.kind === "classval" && a.className === b.className;
251
+ // The frontend deduplicates shapes structurally (one shapeId per canonical
252
+ // field list), so id equality IS structural equality.
253
+ if (a.kind === "record")
254
+ return b.kind === "record" && a.shapeId === b.shapeId;
255
+ // Unions are interned like shapes: one unionId per canonical arm list.
256
+ if (a.kind === "union")
257
+ return b.kind === "union" && a.unionId === b.unionId;
258
+ if (a.kind === "promise")
259
+ return b.kind === "promise" && typeEquals(a.inner, b.inner);
260
+ if (a.kind === "generator") {
261
+ return (b.kind === "generator" &&
262
+ typeEquals(a.yieldT, b.yieldT) &&
263
+ typeEquals(a.retT, b.retT) &&
264
+ typeEquals(a.nextT, b.nextT));
265
+ }
266
+ return a.kind === b.kind;
267
+ }
268
+ /** True for types whose values are heap-allocated and reference-counted.
269
+ * The single dispatch point for the backend's RC machinery: retains,
270
+ * releases, frame/scope tracking, and NULL-initialized locals all key off
271
+ * this — adding a refcounted kind must not grow new per-kind checks outside
272
+ * the type-directed helpers. */
273
+ export function isRefCounted(t) {
274
+ return (t.kind === "string" ||
275
+ t.kind === "array" ||
276
+ t.kind === "map" ||
277
+ t.kind === "set" ||
278
+ // Every regex value is an immortal interned literal today, so its
279
+ // retains/releases are no-ops — riding the uniform machinery anyway
280
+ // means dynamic construction can arrive without a redesign.
281
+ t.kind === "regex" ||
282
+ // URL, Stats, and spawnSync-result instances are ordinary refcounted
283
+ // heap values (immutable, no cycles — strings/scalars inside only).
284
+ t.kind === "url" ||
285
+ // URLSearchParams lists are ordinary refcounted heap values (strings
286
+ // plus at most the owning-URL edge — no cycles).
287
+ t.kind === "searchParams" ||
288
+ // Symbols are the same story: immutable identity values holding at
289
+ // most two strings.
290
+ t.kind === "symbol" ||
291
+ t.kind === "stats" ||
292
+ t.kind === "spawnRes" ||
293
+ t.kind === "child" ||
294
+ // net handles are refcounted like child (listeners drop at settle,
295
+ // so lean allocation — see the IrType comment).
296
+ t.kind === "netServer" ||
297
+ t.kind === "netSocket" ||
298
+ // h2 sessions/streams are refcounted like the net pair (listeners
299
+ // drop at settlement; the session→stream↔session cycle breaks there).
300
+ t.kind === "http2Session" ||
301
+ t.kind === "http2Stream" ||
302
+ t.kind === "dgramSocket" ||
303
+ // TestContext handles are refcounted like dgramSocket (the runner
304
+ // tree owns children; no cycles through the handle).
305
+ t.kind === "testCtx" ||
306
+ t.kind === "httpReq" ||
307
+ t.kind === "httpRes" ||
308
+ t.kind === "httpClientReq" ||
309
+ // SecureContext handles are refcounted like url/stats (immutable, no
310
+ // cycles — parsed cert/key material inside only).
311
+ t.kind === "secureCtx" ||
312
+ // FSWatcher handles are refcounted like child (listeners drop at
313
+ // close, so lean allocation — see the IrType comment).
314
+ t.kind === "fsWatcher" ||
315
+ // Child-output streams are refcounted like child (listeners drop at
316
+ // EOF — see the IrType comment).
317
+ t.kind === "childStream" ||
318
+ // Typed arrays / Buffers are ordinary refcounted heap values (mutable,
319
+ // no cycles — raw bytes inside only).
320
+ t.kind === "bytes" ||
321
+ t.kind === "func" ||
322
+ t.kind === "object" ||
323
+ // Every class object is an immortal emitted static, so its
324
+ // retains/releases are no-ops — riding the uniform machinery (the
325
+ // regex-literal stance) keeps every container and box path unchanged.
326
+ t.kind === "classval" ||
327
+ t.kind === "record" ||
328
+ // The union CONTAINER is heap/refcounted regardless of which arm it
329
+ // holds (uniform representation keeps the RC discipline simple).
330
+ t.kind === "union" ||
331
+ t.kind === "promise" ||
332
+ // A generator object is a refcounted handle over its paused fiber and
333
+ // typed channel slots (ScrGen — lean allocation, no cycle header).
334
+ t.kind === "generator" ||
335
+ // A dyn value is a refcounted JSON DOM tree (ScrDyn).
336
+ t.kind === "dyn" ||
337
+ // An island value is a refcounted cell owning one engine value.
338
+ t.kind === "jsval" ||
339
+ // A catch binding is a refcounted snapshot box (ScrCaught).
340
+ t.kind === "caught");
341
+ }
342
+ /** The runtime-provided error classes, keyed by IR class name. The names
343
+ * are '%'-prefixed ('%' cannot appear in a TS identifier, so a user's own
344
+ * `class Error` can never collide). `lib` is the standard-library name the
345
+ * frontend recognizes; `kind` is the runtime's SCR_ERR_* index (backends
346
+ * stamp scr_error_vts[kind] and pick constructor kinds by it). Every
347
+ * emitted module carries all four class defs (flagged `runtime`) so the
348
+ * program's preorder numbering always covers them — the runtime's own
349
+ * throws (JSON/dynCheck/regex) mint instances of these classes whether or
350
+ * not user code mentions Error. */
351
+ export const RUNTIME_ERROR_CLASSES = new Map([
352
+ ["%Error", { lib: "Error", kind: 0, base: null }],
353
+ ["%TypeError", { lib: "TypeError", kind: 1, base: "%Error" }],
354
+ ["%RangeError", { lib: "RangeError", kind: 2, base: "%Error" }],
355
+ ["%SyntaxError", { lib: "SyntaxError", kind: 3, base: "%Error" }],
356
+ // DOMException — the web-standard error shape (a Node global since
357
+ // v17). Its extra state (the legacy numeric code, the options form's
358
+ // cause) lives in runtime-side slots BEYOND the ScrError prefix the IR
359
+ // fields describe, reached only through the error.dom* libCalls — so
360
+ // user `extends DOMException` is fenced (the subclass layout would
361
+ // overlap the hidden slots), where the other four extend freely.
362
+ ["%DOMException", { lib: "DOMException", kind: 4, base: "%Error" }],
363
+ ]);
364
+ /** The runtime-provided node:events EventEmitter class (ScrEmitter /
365
+ * scr_emitter_*, scr_events_emitter.c — link-gated by moduleUsesEmitter,
366
+ * so unlike the error classes its def rides a module only when the
367
+ * program touches the surface). The '%' name keeps it clear of user
368
+ * identifiers, exactly like the error classes. Backends emit no struct/
369
+ * RC/vtable for it; user subclasses embed the ScrEmitter prefix (the
370
+ * registry pointer and the display-name slot, stamped by the emitted
371
+ * allocation) and main() stamps the runtime vtable's preorder interval.
372
+ * The emitter hierarchy is UNCONDITIONALLY cycle-capable: the registry
373
+ * owns listener closures, whatever the subclass fields say. */
374
+ export const RUNTIME_EMITTER_CLASS = "%EventEmitter";
375
+ /** The runtime-provided node:stream classes (ScrStream / scr_stream_*,
376
+ * scr_stream.c — link-gated by moduleUsesStream). They root at the
377
+ * emitter class (base chains below), so the emitter method surface and
378
+ * upcasts apply unchanged; every instance shares ONE runtime layout (the
379
+ * ScrEmitter prefix plus a lazily-allocated stream-state pointer), so a
380
+ * Duplex upcast to Readable is the usual pointer reinterpret. `sides`
381
+ * names which halves the class carries — the lowering admits readable
382
+ * members on "r"-siders, writable members on "w"-siders. User `extends`
383
+ * compiles (phase 2): subclass structs embed the full ScrStream prefix
384
+ * (registry, display name, state pointer — one slot past the emitter
385
+ * prefix), construction runs the emitted allocation then a stream .init
386
+ * libCall at super(options), and overridden underscore methods bind as
387
+ * synthesized wrapper closures dispatching through the vtable; main()
388
+ * stamps each runtime vtable's preorder interval like the emitter's. */
389
+ export const RUNTIME_STREAM_CLASSES = new Map([
390
+ ["%Readable", { lib: "Readable", base: RUNTIME_EMITTER_CLASS, sides: "r" }],
391
+ ["%Writable", { lib: "Writable", base: RUNTIME_EMITTER_CLASS, sides: "w" }],
392
+ ["%Duplex", { lib: "Duplex", base: "%Readable", sides: "rw" }],
393
+ ["%Transform", { lib: "Transform", base: "%Duplex", sides: "rw" }],
394
+ ["%PassThrough", { lib: "PassThrough", base: "%Transform", sides: "rw" }],
395
+ ]);
396
+ /** Object-literal ACCESSOR properties (`{ get x() {...}, set x(v) {...} }`)
397
+ * live on the shape as reserved '%'-fields holding closures: `%get:x` a
398
+ * `() => T` invoked once per property READ (side effects and all — JS's
399
+ * evaluation), `%set:x` a `(v: T) => void` invoked per WRITE. The property
400
+ * name itself has NO data slot. Like every '%'-field the slots stay out of
401
+ * declaredOrder — and because the slot types are funcs, accessor-carrying
402
+ * shapes are never JSON-safe or dyn-convertible; the enumeration surfaces
403
+ * Node would answer differently (Object.keys includes accessor names,
404
+ * values/entries and spread invoke the getters) fence by name at their
405
+ * lowerings. */
406
+ export function accessorSlotProp(fieldName) {
407
+ if (fieldName.startsWith("%get:"))
408
+ return { kind: "get", prop: fieldName.slice(5) };
409
+ if (fieldName.startsWith("%set:"))
410
+ return { kind: "set", prop: fieldName.slice(5) };
411
+ return null;
412
+ }
413
+ /** True when the shape carries at least one accessor slot (see
414
+ * accessorSlotProp) — the predicate behind every enumeration-surface
415
+ * fence. */
416
+ export function shapeHasAccessorSlots(shape) {
417
+ return shape.fields.some((f) => accessorSlotProp(f.name) !== null);
418
+ }
419
+ /** The bytesIntrinsic methods that can raise a catchable error — backends'
420
+ * may-throw analyses seed on these exactly like MAY_THROW_LIB_FNS. */
421
+ export const MAY_THROW_BYTES_METHODS = new Set([
422
+ "setFrom",
423
+ "readNum",
424
+ "writeNum",
425
+ "readNumVar",
426
+ "writeNumVar",
427
+ "compareBuf",
428
+ "fill",
429
+ "fillNum",
430
+ "fillStr",
431
+ "copy",
432
+ "swap16",
433
+ "swap32",
434
+ "swap64",
435
+ "writeStr",
436
+ "dataViewNew",
437
+ "dvGetUint8",
438
+ "dvGetInt8",
439
+ "dvGetUint16",
440
+ "dvGetInt16",
441
+ "dvGetUint32",
442
+ "dvGetInt32",
443
+ "dvGetFloat32",
444
+ "dvGetFloat64",
445
+ "dvGetBigUint64Number",
446
+ "dvGetBigInt64Number",
447
+ ]);
448
+ /** Result-type rule for each island op (the validator enforces it; the
449
+ * frontend constructs nodes with exactly these). */
450
+ export function jsOpResultKind(op) {
451
+ switch (op) {
452
+ case "add":
453
+ case "sub":
454
+ case "mul":
455
+ case "div":
456
+ case "mod":
457
+ case "pow":
458
+ case "neg":
459
+ case "plus":
460
+ case "getProp":
461
+ case "getIdx":
462
+ case "callMethod":
463
+ case "callFn":
464
+ case "construct":
465
+ case "globalGet":
466
+ case "objLit":
467
+ case "arrLit":
468
+ case "undefLit":
469
+ case "nullLit":
470
+ case "iterNew":
471
+ case "optCallMethod":
472
+ return "jsval";
473
+ case "lt":
474
+ case "le":
475
+ case "gt":
476
+ case "ge":
477
+ case "eq":
478
+ case "neq":
479
+ case "instanceOf":
480
+ case "truthy":
481
+ case "not":
482
+ return "bool";
483
+ case "typeof":
484
+ case "toStr":
485
+ return "string";
486
+ case "setProp":
487
+ case "setIdx":
488
+ return "void";
489
+ default: {
490
+ const _exhaustive = op;
491
+ void _exhaustive;
492
+ throw new Error("unreachable");
493
+ }
494
+ }
495
+ }
496
+ /** True when a type is a union with an undefined arm — the optional-flavored
497
+ * slot marker shared by the frontend (record literals may omit such fields)
498
+ * and backends (JSON serializers DROP such fields when they hold undefined,
499
+ * dynCheck builders produce the undefined arm for a MISSING key). Note the
500
+ * question is about the TYPE, not a declaration's `?:` token: without
501
+ * exactOptionalPropertyTypes, `{a?: string}` and `{a: string | undefined}`
502
+ * are the same shape and behave identically — which is exactly Node's rule
503
+ * (JSON.stringify drops ANY undefined-valued field, declared optional or
504
+ * not). */
505
+ export function isUndefinedArmedUnion(t, getUnion) {
506
+ if (t.kind !== "union")
507
+ return false;
508
+ const def = getUnion(t.unionId);
509
+ return !!def && def.arms.some((a) => a.kind === "undefinedT");
510
+ }
511
+ /** True when a type is JSON-representable — the shared fence for
512
+ * `jsonStringify` (what can be serialized) and `dynCheck` (what a dyn value
513
+ * can be validated against): f64, string, bool, records, arrays, and unions
514
+ * of those, recursively. Closures, class instances, dyn itself, and void are
515
+ * not JSON. Registry lookups are parameters because the frontend holds
516
+ * registries and the validator/backend hold maps; recursive shapes/unions
517
+ * cannot exist (the frontend rejects them), so the recursion terminates. */
518
+ export function isJsonSafeType(t, getRecord, getUnion) {
519
+ return isJsonSafeAt(t, getRecord, getUnion, false);
520
+ }
521
+ /** The recursion behind isJsonSafeType. `inRecordField` is the ONE position
522
+ * where an undefined arm is JSON-representable: an undefined-armed union as
523
+ * a record field (the `a?: T` spelling) serializes by DROPPING the field
524
+ * when it holds undefined and validates a MISSING key as the undefined arm
525
+ * — both exactly Node. Everywhere else (a bare `T | undefined` value,
526
+ * stringified whole or cast to) exactness is unreachable: Node's stringify
527
+ * of bare undefined is not a string at all, and JSON text can never contain
528
+ * a value that matches the arm. */
529
+ function isJsonSafeAt(t, getRecord, getUnion, inRecordField) {
530
+ switch (t.kind) {
531
+ case "f64":
532
+ case "string":
533
+ case "bool":
534
+ return true;
535
+ case "array":
536
+ return isJsonSafeAt(t.elem, getRecord, getUnion, false);
537
+ case "record": {
538
+ const shape = getRecord(t.shapeId);
539
+ if (!shape)
540
+ return false;
541
+ // TUPLE positions are array slots, not droppable object keys: an
542
+ // undefined-armed position would stringify as `null` in JS (not
543
+ // drop), so tuples keep the array rule for their fields.
544
+ if (!shape.fields.every((f) => isJsonSafeAt(f.type, getRecord, getUnion, !shape.tuple))) {
545
+ return false;
546
+ }
547
+ // Overflow values sit in record-key position too: dyn is JSON-safe
548
+ // HERE (the DOM serializes itself; undefined-valued entries drop
549
+ // like any undefined-valued key), everything else follows the
550
+ // record-field rule.
551
+ if (shape.indexValue && shape.indexValue.kind !== "dyn") {
552
+ return isJsonSafeAt(shape.indexValue, getRecord, getUnion, true);
553
+ }
554
+ return true;
555
+ }
556
+ case "union": {
557
+ const def = getUnion(t.unionId);
558
+ return !!def && def.arms.every((a) => isJsonSafeAt(a, getRecord, getUnion, inRecordField));
559
+ }
560
+ case "func":
561
+ case "object":
562
+ // Class values stringify as "{}" husks in Node (own enumerable statics
563
+ // aside — not representable type-directedly); rejected like Maps.
564
+ case "classval":
565
+ // Maps are not JSON (JSON.stringify(new Map()) is "{}" in Node — an
566
+ // empty-object husk nobody wants; stringify/dynCheck reject instead).
567
+ case "map":
568
+ // Sets stringify as the same "{}" husk — rejected like Maps.
569
+ case "set":
570
+ // Regexes are not JSON (JSON.stringify(/a/) is "{}" in Node — the same
571
+ // empty-object husk as Maps; stringify/dynCheck reject instead).
572
+ case "regex":
573
+ // URLs stringify as "{}" husks in Node too (data properties live on
574
+ // internal slots) — rejected the same way; use url.href instead.
575
+ case "url":
576
+ // URLSearchParams stringifies as the same "{}" husk — rejected; use
577
+ // sp.toString() instead.
578
+ case "searchParams":
579
+ // Symbols are DROPPED by Node's stringify (undefined at the top level,
580
+ // omitted as object values) — silent divergence banned; rejected.
581
+ case "symbol":
582
+ // Typed arrays stringify as index-keyed objects ({"0":1,...}) and
583
+ // Buffers as {type:"Buffer",data:[...]} in Node — neither shape is
584
+ // representable type-directedly; rejected like Maps.
585
+ case "bytes":
586
+ case "stats":
587
+ case "spawnRes":
588
+ case "child":
589
+ case "netServer":
590
+ case "netSocket":
591
+ case "http2Session":
592
+ case "http2Stream":
593
+ case "dgramSocket":
594
+ case "testCtx":
595
+ case "httpReq":
596
+ case "httpRes":
597
+ case "httpClientReq":
598
+ case "secureCtx":
599
+ case "fsWatcher":
600
+ case "childStream":
601
+ case "procStream":
602
+ case "dyn":
603
+ case "jsval":
604
+ case "caught":
605
+ case "promise":
606
+ case "generator":
607
+ case "void":
608
+ return false;
609
+ // Representable exactly as a union arm in record-field position (the
610
+ // optional-field story above); bare undefined-armed unions stay fenced
611
+ // out of both stringify and dynCheck (narrow with '!== undefined'
612
+ // first, or model absence with a null arm).
613
+ case "undefinedT":
614
+ return inRecordField;
615
+ // JSON null ↔ the nullT arm: null-armed unions stringify (`null`) and
616
+ // validate (a JSON null matches exactly the nullT arm).
617
+ case "nullT":
618
+ return true;
619
+ default: {
620
+ const _exhaustive = t;
621
+ void _exhaustive;
622
+ throw new Error("unreachable");
623
+ }
624
+ }
625
+ }
626
+ /** THE island boundary predicate: true when a static type can cross into
627
+ * the island (jsMarshal — primitives by value, composites as deep JSON
628
+ * copies) and back out (jsExit — strict primitive extraction, composites
629
+ * through the dynCheck walker). Primitives are JSON-safe, so the rule
630
+ * coincides with isJsonSafeType; it has its own name because the boundary
631
+ * is its own concept — the frontend's implicit coercions, the explicit-cast
632
+ * lowering, and the validator's jsMarshal/jsExit rules all ask this ONE
633
+ * question, and the boundary rejection messages describe exactly this set. */
634
+ export function canCrossIslandBoundary(t, getRecord, getUnion) {
635
+ return isJsonSafeType(t, getRecord, getUnion);
636
+ }
637
+ /** True for a closure type that can cross INTO the island as a host
638
+ * function: every parameter jsval (the engine's arguments pass through as
639
+ * handles — no per-type extraction exists inside a host call) and the
640
+ * result jsval, void, or a primitive (which marshals back by value —
641
+ * `(x) => x * 2` on an 'any' x infers a number return). Contextual typing
642
+ * produces exactly this shape for callbacks passed to package APIs
643
+ * (`.action((a, b) => ...)` against `(...args: any[]) => void`). Arity is
644
+ * capped by the runtime's host-call argument buffer (scr_island.c). */
645
+ export const MAX_ISLAND_CALLBACK_ARITY = 16;
646
+ export function canMarshalFuncIntoIsland(t) {
647
+ return (t.kind === "func" &&
648
+ t.params.length <= MAX_ISLAND_CALLBACK_ARITY &&
649
+ t.params.every((p) => p.kind === "jsval") &&
650
+ (t.ret.kind === "jsval" || t.ret.kind === "void" ||
651
+ t.ret.kind === "f64" || t.ret.kind === "bool" || t.ret.kind === "string"));
652
+ }
653
+ /** Parameter types a TYPED closure may declare when it crosses INTO the
654
+ * island as a host function: jsval params take the engine argument as a
655
+ * handle (the all-'any' shape above); every other admitted type converts
656
+ * AT CALL TIME through the validated-exit machinery — strict primitives,
657
+ * JSON round-trip composites (the dynCheck walker: width-tolerant records,
658
+ * path-annotated failures). On top of the jsExit set, a bare `T | undefined`
659
+ * union is admitted here — an absent or undefined engine argument takes the
660
+ * undefined arm, exactly the missing-optional-field rule (and exactly the
661
+ * commander case: `.action((text: string | undefined, opts) => ...)` sees
662
+ * undefined when the command argument is omitted). */
663
+ export function isIslandCallbackParamType(t, getRecord, getUnion) {
664
+ if (t.kind === "jsval")
665
+ return true;
666
+ if (isJsonSafeType(t, getRecord, getUnion))
667
+ return true;
668
+ if (t.kind === "union") {
669
+ // A bare undefined-armed union: every non-undefined arm must be
670
+ // JSON-safe (arms never nest unions, so plain isJsonSafeType applies).
671
+ const def = getUnion(t.unionId);
672
+ return !!def && def.arms.every((a) => a.kind === "undefinedT" || isJsonSafeType(a, getRecord, getUnion));
673
+ }
674
+ return false;
675
+ }
676
+ /** Classify a typed island callback's RETURN for adapter synthesis: sync
677
+ * kinds marshal back by value ('void'|'jsval'|'f64'|'bool'|'string' — the
678
+ * canMarshalFuncIntoIsland set), 'json' marshals a JSON-safe composite
679
+ * through the type-directed serializer + engine parse (the jsMarshal
680
+ * composite path — commander's option-argument collectors return arrays
681
+ * this way), and a Promise of the by-value kinds wraps as an engine
682
+ * promise settled when the scriptc promise settles (async callbacks —
683
+ * the `.action(async ...)` case). Null for anything else (Promise of
684
+ * composites: still fenced). */
685
+ export function islandCallbackRet(t, getRecord, getUnion) {
686
+ const tagOf = (r) => r.kind === "void" || r.kind === "jsval" || r.kind === "f64" || r.kind === "bool" || r.kind === "string"
687
+ ? r.kind
688
+ : null;
689
+ if (t.kind === "promise") {
690
+ const tag = tagOf(t.inner);
691
+ return tag ? { async: true, tag } : null;
692
+ }
693
+ const tag = tagOf(t);
694
+ if (tag)
695
+ return { async: false, tag };
696
+ return isJsonSafeType(t, getRecord, getUnion) ? { async: false, tag: "json" } : null;
697
+ }
698
+ /** The TYPED extension of canMarshalFuncIntoIsland (a strict superset):
699
+ * closures whose params are per-argument-convertible at call time
700
+ * (isIslandCallbackParamType) and whose return classifies
701
+ * (islandCallbackRet). Same arity cap — the runtime's host-call argument
702
+ * buffer. Closures taking closures and 'unknown'-typed params stay fenced
703
+ * (no per-type extraction exists for them inside a host call). */
704
+ export function canMarshalTypedFuncIntoIsland(t, getRecord, getUnion) {
705
+ return (t.kind === "func" &&
706
+ t.params.length <= MAX_ISLAND_CALLBACK_ARITY &&
707
+ t.params.every((p) => isIslandCallbackParamType(p, getRecord, getUnion)) &&
708
+ islandCallbackRet(t.ret, getRecord, getUnion) !== null);
709
+ }
710
+ /* ── the checked-dynamic FUNCTION boundary ──────────────────────────────
711
+ * The STATIC twin of the island's typed-function marshaling
712
+ * (canMarshalTypedFuncIntoIsland): closures cross the dyn boundary as a
713
+ * callable DOM kind carrying the closure + a compiled per-signature call
714
+ * thunk. Two directions, two predicates, mutually recursive with the
715
+ * conversion domains (function types cannot ride records/unions — jsonSafe
716
+ * excludes them — so the recursion terminates):
717
+ *
718
+ * IN (canBoxFuncIntoDyn, the dynFrom domain's function arm): calling the
719
+ * box happens with DYN arguments, so every declared param must be
720
+ * dynCheckABLE (the thunk validates each argument into it) and the result
721
+ * must convert BACK to dyn (dynFrom's domain, functions included — a
722
+ * wrapper returning a wrapper boxes recursively).
723
+ *
724
+ * OUT (canAdaptDynFuncTo, the dynCheck domain's function arm): a dyn
725
+ * function landing in a typed func slot either unwraps directly (the boxed
726
+ * signature key equals the target's — same type, same ABI) or wraps in a
727
+ * per-target ADAPTER closure that converts each typed argument INTO dyn
728
+ * (so params must be dyn-convertible) and validates the dyn result into
729
+ * the target's return type (so the return must be dynCheckable).
730
+ *
731
+ * Deliberately fenced OUT of both directions: generic signatures (no
732
+ * concrete IR type exists), this-parameters, construct signatures (`new`
733
+ * through a dyn value), properties ON function values, and params/results
734
+ * outside the conversion domains (Maps, class instances, ...). Promises
735
+ * CONVERT in (canConvertToDyn's promise arm — an async dyn-boxed closure's
736
+ * return) but do not check OUT (`u as Promise<T>` stays fenced).
737
+ */
738
+ /** The runtime HANDLE kinds that cross the checked-dynamic boundary as
739
+ * the DOM's HANDLE kind (SCR_DYN_HANDLE): boxed by REFERENCE (identity —
740
+ * stateful I/O objects never copy), unboxed by tag check, members
741
+ * dispatched at runtime onto the same entry points the static lowerings
742
+ * use. The set is deliberately the handles whose member surfaces have
743
+ * complete static lowerings (the http/net receiver surface —
744
+ * `server.on('request', mustCall((req, res) => ...))` is the canonical
745
+ * crossing); other handle kinds keep the honest cannot-box fence. Each
746
+ * entry carries the runtime tag spelling and the class display name
747
+ * (dynCheck's "expected IncomingMessage ..." texts). */
748
+ export const DYN_HANDLE_KINDS = new Map([
749
+ ["httpReq", { tag: "SCR_DYNH_HTTP_REQ", cls: "IncomingMessage" }],
750
+ ["httpRes", { tag: "SCR_DYNH_HTTP_RES", cls: "ServerResponse" }],
751
+ ["netSocket", { tag: "SCR_DYNH_NET_SOCKET", cls: "Socket" }],
752
+ ["netServer", { tag: "SCR_DYNH_NET_SERVER", cls: "Server" }],
753
+ ["http2Session", { tag: "SCR_DYNH_H2_SESSION", cls: "Http2Session" }],
754
+ ["http2Stream", { tag: "SCR_DYNH_H2_STREAM", cls: "Http2Stream" }],
755
+ ]);
756
+ /** A static type that CONVERTS into a dyn DOM value — the dynFrom domain:
757
+ * JSON-safe data, bytes<u8> (payload copied), undefined-armed unions of
758
+ * JSON-safe arms, boxable function types, and the runtime HANDLE kinds
759
+ * (boxed by reference — DYN_HANDLE_KINDS). */
760
+ export function canConvertToDyn(t, getRecord, getUnion) {
761
+ if (isJsonSafeType(t, getRecord, getUnion))
762
+ return true;
763
+ // bytes<u8> is a DOM kind the walker boxes ANYWHERE (payload copied),
764
+ // including nested in records/arrays/unions — the tls/https options
765
+ // record's cert/key/ca Buffers. isJsonSafeType rejects nested bytes
766
+ // (no JSON-exact round trip), but dynFrom needs only that the walker
767
+ // can build the DOM value, which it can — so canConvertToDyn folds the
768
+ // bytes-bearing composites in beyond the JSON-safe core.
769
+ if (canBoxBytesComposite(t, getRecord, getUnion))
770
+ return true;
771
+ if (t.kind === "bytes" && t.elem === "u8")
772
+ return true;
773
+ // %Error converts as the DOM's error encoding ({%error, name, message,
774
+ // code?} — the caughtToDyn shape, scr_dyn_from_error): the dyn 'error'
775
+ // listener boundary (a mustCall-wrapped handler receiving the payload).
776
+ if (t.kind === "object" && t.className === "%Error")
777
+ return true;
778
+ if (t.kind === "func")
779
+ return canBoxFuncIntoDyn(t, getRecord, getUnion);
780
+ if (DYN_HANDLE_KINDS.has(t.kind))
781
+ return true;
782
+ // Promises box by REFERENCE (SCR_DYN_PROMISE): promise<dyn> carries its
783
+ // ScrPromise directly (the payload is already a DOM value), any other
784
+ // convertible-or-void inner boxes an ADAPTER promise whose emitted
785
+ // settle callback converts the payload (rejections copy raw — reasons
786
+ // are dynamically tagged). The dc tracePromise boundary and dyn-boxed
787
+ // async closures are the crossings.
788
+ if (t.kind === "promise") {
789
+ return (t.inner.kind === "dyn" ||
790
+ t.inner.kind === "void" ||
791
+ canConvertToDyn(t.inner, getRecord, getUnion));
792
+ }
793
+ if (t.kind === "union") {
794
+ const def = getUnion(t.unionId);
795
+ return !!def && def.arms.every((a) => a.kind === "undefinedT" || isJsonSafeType(a, getRecord, getUnion));
796
+ }
797
+ return false;
798
+ }
799
+ /** The bytes-bearing extension of the dynFrom domain: JSON-safe scalars
800
+ * plus bytes<u8> anywhere, recursing through records (fields + index
801
+ * value), arrays, and unit-armed unions — exactly the sc_td_* walker's
802
+ * capability for the tls/https options-record shapes. Returns false for
803
+ * a composite carrying any kind the walker cannot box (funcs, Maps,
804
+ * handles nested in a record); those still fence. */
805
+ function canBoxBytesComposite(t, getRecord, getUnion) {
806
+ switch (t.kind) {
807
+ case "f64":
808
+ case "string":
809
+ case "bool":
810
+ case "dyn":
811
+ case "undefinedT":
812
+ case "nullT":
813
+ return true;
814
+ case "bytes":
815
+ return t.elem === "u8";
816
+ case "array":
817
+ return canBoxBytesComposite(t.elem, getRecord, getUnion);
818
+ case "record": {
819
+ const shape = getRecord(t.shapeId);
820
+ if (!shape)
821
+ return false;
822
+ if (!shape.fields.every((f) => canBoxBytesComposite(f.type, getRecord, getUnion)))
823
+ return false;
824
+ return !shape.indexValue || canBoxBytesComposite(shape.indexValue, getRecord, getUnion);
825
+ }
826
+ case "union": {
827
+ const def = getUnion(t.unionId);
828
+ return !!def && def.arms.every((a) => canBoxBytesComposite(a, getRecord, getUnion));
829
+ }
830
+ default:
831
+ return false;
832
+ }
833
+ }
834
+ /** A type a dyn value can be VALIDATED into — the dynCheck domain:
835
+ * JSON-safe data, bytes<u8> (a fresh copy out), the %Error extraction,
836
+ * undefined-armed unions of JSON-safe arms, adaptable function types,
837
+ * and the runtime HANDLE kinds (a tag-checked reference unwrap —
838
+ * DYN_HANDLE_KINDS). */
839
+ export function canDynCheckTo(t, getRecord, getUnion) {
840
+ if (isJsonSafeType(t, getRecord, getUnion))
841
+ return true;
842
+ if (t.kind === "bytes" && t.elem === "u8")
843
+ return true;
844
+ if (t.kind === "object" && t.className === "%Error")
845
+ return true;
846
+ if (t.kind === "func")
847
+ return canAdaptDynFuncTo(t, getRecord, getUnion);
848
+ if (DYN_HANDLE_KINDS.has(t.kind))
849
+ return true;
850
+ if (t.kind === "union") {
851
+ const def = getUnion(t.unionId);
852
+ return !!def && def.arms.every((a) => a.kind === "undefinedT" || isJsonSafeType(a, getRecord, getUnion));
853
+ }
854
+ return false;
855
+ }
856
+ /** A closure type that can BOX into the DOM's function kind (dynFrom):
857
+ * every param dyn or dynCheckable (the thunk validates dyn arguments into
858
+ * them), return void, dyn, or dyn-convertible (the thunk converts it
859
+ * back). */
860
+ export function canBoxFuncIntoDyn(t, getRecord, getUnion) {
861
+ return (t.kind === "func" &&
862
+ t.params.every((p) => p.kind === "dyn" || canDynCheckTo(p, getRecord, getUnion)) &&
863
+ (t.ret.kind === "void" || t.ret.kind === "dyn" || canConvertToDyn(t.ret, getRecord, getUnion)));
864
+ }
865
+ /** A closure type a dyn function value can ADAPT to (dynCheck): every
866
+ * param dyn or dyn-convertible (the adapter converts typed arguments into
867
+ * dyn), return void, dyn, or dynCheckable (the adapter validates the dyn
868
+ * result). */
869
+ export function canAdaptDynFuncTo(t, getRecord, getUnion) {
870
+ return (t.kind === "func" &&
871
+ // A variadic (rest-marked) target would need the trailing rest-array
872
+ // param synthesized by the adapter — no adapter models that; variadic
873
+ // values live boxed and are called through their own thunks.
874
+ t.rest !== true &&
875
+ t.params.every((p) => p.kind === "dyn" || canConvertToDyn(p, getRecord, getUnion)) &&
876
+ (t.ret.kind === "void" || t.ret.kind === "dyn" || canDynCheckTo(t.ret, getRecord, getUnion)));
877
+ }
878
+ /** The MARSHAL-direction boundary (jsMarshal): everything that can cross
879
+ * out and back (canCrossIslandBoundary) plus qualifying closures — those
880
+ * enter as host functions but never EXIT (jsExit keeps the narrower
881
+ * predicate). */
882
+ export function canMarshalIntoIsland(t, getRecord, getUnion) {
883
+ return isJsonSafeType(t, getRecord, getUnion) || canMarshalFuncIntoIsland(t);
884
+ }
885
+ /** The EXIT-direction boundary (jsExit): everything round-trippable
886
+ * (canCrossIslandBoundary) plus BARE undefined/null-armed unions whose
887
+ * data arms are all JSON-safe — the engine's undefined takes the
888
+ * undefined arm before the JSON detour (JSON cannot spell undefined, and
889
+ * bare undefined-armed unions are JSON-unsafe for exactly that reason),
890
+ * null and data ride the round trip into the union's dynCheck. The
891
+ * package-API shape `result.headers` : `Record<string, string> |
892
+ * undefined` is the motivating case. */
893
+ export function canExitIslandToType(t, getRecord, getUnion) {
894
+ if (canCrossIslandBoundary(t, getRecord, getUnion))
895
+ return true;
896
+ // Uint8Array exits with a validated kind check + copy (engine Buffers
897
+ // pass — they ARE Uint8Arrays); other element widths stay out.
898
+ if (t.kind === "bytes" && t.elem === "u8")
899
+ return true;
900
+ if (t.kind === "union") {
901
+ const def = getUnion(t.unionId);
902
+ return (!!def &&
903
+ def.arms.some((a) => a.kind === "undefinedT") &&
904
+ def.arms.every((a) => isUnitType(a) || isJsonSafeType(a, getRecord, getUnion)));
905
+ }
906
+ return false;
907
+ }
908
+ /** True when the module contains any regex construct — a regexLit /
909
+ * regexIntrinsic node or a regex-typed slot anywhere. This is the link
910
+ * switch that pulls scr_regex.c + the vendored libregexp into the binary
911
+ * (cc.ts); regex-free programs keep the historical command line. A generic
912
+ * JSON walk: `kind` discriminants live only on IR objects, so user string
913
+ * VALUES can never false-positive. */
914
+ export function moduleUsesRegex(mod) {
915
+ let found = false;
916
+ const visit = (v) => {
917
+ if (found || v === null || typeof v !== "object")
918
+ return;
919
+ if (Array.isArray(v)) {
920
+ for (const item of v)
921
+ visit(item);
922
+ return;
923
+ }
924
+ const kind = v.kind;
925
+ if (kind === "regex" || kind === "regexLit" || kind === "regexIntrinsic") {
926
+ found = true;
927
+ return;
928
+ }
929
+ // The lre-backed case conversions live in scr_regex.c (libunicode's
930
+ // case tables): they ride the same link switch as regex nodes.
931
+ if (kind === "strIntrinsic") {
932
+ const method = v.method;
933
+ if (method === "toLowerCase" || method === "toUpperCase") {
934
+ found = true;
935
+ return;
936
+ }
937
+ }
938
+ // RegExp.escape lives in scr_regex.c too (needing no engine — it
939
+ // keeps the always-linked string TU out of hello-world's size class).
940
+ if (kind === "libCall" && v.fn === "regexp.escape") {
941
+ found = true;
942
+ return;
943
+ }
944
+ for (const key of Object.keys(v))
945
+ visit(v[key]);
946
+ };
947
+ visit(mod);
948
+ return found;
949
+ }
950
+ /** True when the embedded npm graph references fetch — the link switch
951
+ * that pulls scr_fetch.c + its socket/tls/zlib dependencies into the binary (cc.ts) and
952
+ * has the emitted main call scr_fetch_install. A word-boundary scan over
953
+ * the embedded SOURCES, erring toward linking: a false positive costs one
954
+ * dylib reference; a false negative would leave embedded code without the
955
+ * global at runtime. Static builds and fetch-free graphs keep their exact
956
+ * historical link lines. */
957
+ export function moduleUsesFetch(mod) {
958
+ const embedded = mod.embedded;
959
+ if (embedded && embedded.modules.some((m) => /\bfetch\b/.test(m.source)))
960
+ return true;
961
+ // USER-code fetch (the island-backed ambient): its lowering reads the
962
+ // engine's fetch global — the same jsOp walk shape as moduleUsesZlib.
963
+ let found = false;
964
+ const visit = (v) => {
965
+ if (found || v === null || typeof v !== "object")
966
+ return;
967
+ if (Array.isArray(v)) {
968
+ for (const item of v)
969
+ visit(item);
970
+ return;
971
+ }
972
+ const node = v;
973
+ if (node.kind === "jsOp" && node.op === "globalGet" && node.name === "fetch") {
974
+ found = true;
975
+ return;
976
+ }
977
+ for (const key of Object.keys(v))
978
+ visit(v[key]);
979
+ };
980
+ visit(mod);
981
+ return found;
982
+ }
983
+ /** The libCall fns served by the OPTIONAL events unit (scr_events.c):
984
+ * process signal/exit listeners and the piped-stdin surface. */
985
+ const PROCESS_EVENT_LIB_FNS = new Set([
986
+ "process.onSignal",
987
+ "process.offSignal",
988
+ "process.onExit",
989
+ "process.offExit",
990
+ "stdin.onData",
991
+ "stdin.onEnd",
992
+ "stdin.onError",
993
+ "stdin.nextChunk",
994
+ // node:readline rides the stdin unit (scr_readline.c links beside
995
+ // scr_events.c under the same gate).
996
+ "rl.create",
997
+ "rl.question",
998
+ "rl.close",
999
+ "rl.onClose",
1000
+ ]);
1001
+ /** True when the module uses the process-events surface — the link switch
1002
+ * that pulls scr_events.c into the binary and has the emitted main call
1003
+ * scr_events_install (cc.ts + emitter; the scr_regex/scr_fetch/scr_zlib
1004
+ * gating precedent). Event-free programs pay zero bytes and keep their
1005
+ * exact link line. Same generic-walk shape as moduleUsesRegex. */
1006
+ export function moduleUsesProcessEvents(mod) {
1007
+ let found = false;
1008
+ const visit = (v) => {
1009
+ if (found || v === null || typeof v !== "object")
1010
+ return;
1011
+ if (Array.isArray(v)) {
1012
+ for (const item of v)
1013
+ visit(item);
1014
+ return;
1015
+ }
1016
+ const node = v;
1017
+ if (node.kind === "libCall" && typeof node.fn === "string" && PROCESS_EVENT_LIB_FNS.has(node.fn)) {
1018
+ found = true;
1019
+ return;
1020
+ }
1021
+ for (const key of Object.keys(v))
1022
+ visit(v[key]);
1023
+ };
1024
+ visit(mod);
1025
+ return found;
1026
+ }
1027
+ /** True when the module uses the node:events EventEmitter surface — the
1028
+ * link switch that pulls scr_events_emitter.c into the binary (cc.ts; the
1029
+ * scr_events.c gating precedent, but pure data structure: no install, no
1030
+ * loop hooks). Two signals: the `%EventEmitter` class def rides the
1031
+ * module (any emitter-typed value or `extends EventEmitter` subclass
1032
+ * references it, and the emitted RC/trace helpers call scr_emitter_*),
1033
+ * or an emitter.* libCall survived (the defaultMaxListeners statics carry
1034
+ * no emitter-typed value). Emitter-free programs pay zero bytes and keep
1035
+ * their exact link line. */
1036
+ export function moduleUsesEmitter(mod) {
1037
+ if ((mod.classes ?? []).some((c) => c.name === RUNTIME_EMITTER_CLASS))
1038
+ return true;
1039
+ let found = false;
1040
+ const visit = (v) => {
1041
+ if (found || v === null || typeof v !== "object")
1042
+ return;
1043
+ if (Array.isArray(v)) {
1044
+ for (const item of v)
1045
+ visit(item);
1046
+ return;
1047
+ }
1048
+ const node = v;
1049
+ if (node.kind === "libCall" && typeof node.fn === "string" && node.fn.startsWith("emitter.")) {
1050
+ found = true;
1051
+ return;
1052
+ }
1053
+ for (const key of Object.keys(v))
1054
+ visit(v[key]);
1055
+ };
1056
+ visit(mod);
1057
+ return found;
1058
+ }
1059
+ /** True when the program touches the node:stream surface (scr_stream.c —
1060
+ * the moduleUsesEmitter story: the class defs ride the module whenever a
1061
+ * stream-typed value exists, and every stream libCall names its unit).
1062
+ * Stream programs always use the emitter unit too — the stream class
1063
+ * defs pull `%EventEmitter` through their base chain, so
1064
+ * moduleUsesEmitter answers true whenever this does. Stream-free
1065
+ * programs pay zero bytes and keep their exact link line. */
1066
+ export function moduleUsesStream(mod) {
1067
+ if ((mod.classes ?? []).some((c) => RUNTIME_STREAM_CLASSES.has(c.name)))
1068
+ return true;
1069
+ let found = false;
1070
+ const visit = (v) => {
1071
+ if (found || v === null || typeof v !== "object")
1072
+ return;
1073
+ if (Array.isArray(v)) {
1074
+ for (const item of v)
1075
+ visit(item);
1076
+ return;
1077
+ }
1078
+ const node = v;
1079
+ if (node.kind === "libCall" &&
1080
+ typeof node.fn === "string" &&
1081
+ (node.fn.startsWith("readable.") || node.fn.startsWith("writable.") ||
1082
+ node.fn.startsWith("duplex.") || node.fn.startsWith("transform.") ||
1083
+ node.fn.startsWith("passthrough.") ||
1084
+ node.fn === "stream.destroy" || node.fn === "stream.destroyErr" ||
1085
+ node.fn === "stream.prop" || node.fn === "stream.errored" ||
1086
+ node.fn.startsWith("stream.set"))) {
1087
+ found = true;
1088
+ return;
1089
+ }
1090
+ for (const key of Object.keys(v))
1091
+ visit(v[key]);
1092
+ };
1093
+ visit(mod);
1094
+ return found;
1095
+ }
1096
+ /** True when the embedded npm graph has an edge into `builtin` — the
1097
+ * island shim needs the corresponding native bridge linked (zlib's is
1098
+ * the first; the emitted main installs it before any island entry). */
1099
+ export function moduleEmbedsBuiltin(mod, builtin) {
1100
+ return mod.embedded !== undefined && mod.embedded.edges.some((e) => e.to === builtin);
1101
+ }
1102
+ /** Embedded module texts at least this long are DEFLATE-compressed into
1103
+ * the emitted C (emit-island.ts; each stays plain when deflate does not
1104
+ * shrink it) and inflated lazily by the island's module loader at first
1105
+ * load. Below it the zlib round trip cannot pay for itself. */
1106
+ export const NPM_COMPRESS_MIN = 1024;
1107
+ /** True when the emitted npm tables will carry compressed module text —
1108
+ * the SAME candidate test emit-island.ts compresses by, so index.ts's
1109
+ * zlib link switch and emitter.ts's inflater installation stay in
1110
+ * lockstep with the emission (a candidate whose deflate happens not to
1111
+ * shrink stays plain; the installed inflater is then just unused). */
1112
+ export function moduleEmbedsCompressedNpm(mod) {
1113
+ return (mod.embedded !== undefined &&
1114
+ mod.embedded.modules.some((m) => m.source.length >= NPM_COMPRESS_MIN || (m.esm ?? "").length >= NPM_COMPRESS_MIN));
1115
+ }
1116
+ /** True when the module contains any zlib libCall (the static lowering)
1117
+ * OR the embedded npm graph imports node:zlib (the island shim) — the
1118
+ * link switch that pulls scr_zlib.c + the system libz into the binary
1119
+ * (cc.ts); zlib-free programs keep their exact link line. Same
1120
+ * generic-walk shape as moduleUsesRegex: `kind`/`fn` discriminants live
1121
+ * only on IR objects. */
1122
+ export function moduleUsesZlib(mod) {
1123
+ if (moduleEmbedsBuiltin(mod, "node:zlib"))
1124
+ return true;
1125
+ let found = false;
1126
+ const visit = (v) => {
1127
+ if (found || v === null || typeof v !== "object")
1128
+ return;
1129
+ if (Array.isArray(v)) {
1130
+ for (const item of v)
1131
+ visit(item);
1132
+ return;
1133
+ }
1134
+ const node = v;
1135
+ if (node.kind === "libCall" && typeof node.fn === "string" && node.fn.startsWith("zlib.")) {
1136
+ found = true;
1137
+ return;
1138
+ }
1139
+ for (const key of Object.keys(v))
1140
+ visit(v[key]);
1141
+ };
1142
+ visit(mod);
1143
+ return found;
1144
+ }
1145
+ /** True when the module contains any dc.* libCall — the link switch that
1146
+ * pulls scr_dc.c (the diagnostics_channel registry and pub/sub) into the
1147
+ * binary (cc.ts). Channel-free binaries keep their exact size class.
1148
+ * Same walk shape as moduleUsesZlib. */
1149
+ export function moduleUsesDc(mod) {
1150
+ let found = false;
1151
+ const visit = (v) => {
1152
+ if (found || v === null || typeof v !== "object")
1153
+ return;
1154
+ if (Array.isArray(v)) {
1155
+ for (const item of v)
1156
+ visit(item);
1157
+ return;
1158
+ }
1159
+ const node = v;
1160
+ if (node.kind === "libCall" && typeof node.fn === "string" && node.fn.startsWith("dc.")) {
1161
+ found = true;
1162
+ return;
1163
+ }
1164
+ for (const key of Object.keys(v))
1165
+ visit(v[key]);
1166
+ };
1167
+ visit(mod);
1168
+ return found;
1169
+ }
1170
+ /** True when the module contains any assert libCall — the link switch
1171
+ * that pulls scr_assert.c into the binary (cc.ts). scr_regex.c calls the
1172
+ * assert throw/inspect helpers (assert.match lives there), so the regex
1173
+ * switch also pulls scr_assert.c; assert-free, regex-free binaries keep
1174
+ * the historical command line and size. Same walk shape as
1175
+ * moduleUsesZlib. */
1176
+ export function moduleUsesAssert(mod) {
1177
+ let found = false;
1178
+ const visit = (v) => {
1179
+ if (found || v === null || typeof v !== "object")
1180
+ return;
1181
+ if (Array.isArray(v)) {
1182
+ for (const item of v)
1183
+ visit(item);
1184
+ return;
1185
+ }
1186
+ const node = v;
1187
+ if (node.kind === "libCall" && typeof node.fn === "string" && node.fn.startsWith("assert.")) {
1188
+ found = true;
1189
+ return;
1190
+ }
1191
+ for (const key of Object.keys(v))
1192
+ visit(v[key]);
1193
+ };
1194
+ visit(mod);
1195
+ return found;
1196
+ }
1197
+ /** True when the module contains any dynInvoke node or dyn.defineProps
1198
+ * libCall — the link switch that pulls scr_dyn_invoke.c (the prototype-
1199
+ * method dispatch on dyn receivers, plus scr_dyn_display and
1200
+ * scr_dyn_define_props) into the binary (cc.ts; the assert gating
1201
+ * precedent — dispatch-free binaries keep their exact size class). Same
1202
+ * walk shape as moduleUsesZlib. */
1203
+ export function moduleUsesDynInvoke(mod) {
1204
+ let found = false;
1205
+ const visit = (v) => {
1206
+ if (found || v === null || typeof v !== "object")
1207
+ return;
1208
+ if (Array.isArray(v)) {
1209
+ for (const item of v)
1210
+ visit(item);
1211
+ return;
1212
+ }
1213
+ const node = v;
1214
+ if (node.kind === "dynInvoke" || (node.kind === "libCall" && node.fn === "dyn.defineProps")) {
1215
+ found = true;
1216
+ return;
1217
+ }
1218
+ for (const key of Object.keys(v))
1219
+ visit(v[key]);
1220
+ };
1221
+ visit(mod);
1222
+ return found;
1223
+ }
1224
+ /** True when the module contains any insp libCall — the link switch that
1225
+ * pulls scr_inspect.c into the binary (cc.ts; the assert gating
1226
+ * precedent — inspect-free binaries keep the historical command line and
1227
+ * size class). Same walk shape as moduleUsesZlib. */
1228
+ /** True when the module needs scr_async_dyn.c — the checked-dynamic
1229
+ * async surfaces (DOM-promise then/catch/finally reactions, await of a
1230
+ * dyn value, `new Promise(setImmediate)`, AsyncLocalStorage, the
1231
+ * unhandledRejection/warning process events). Also pulled by the
1232
+ * dynInvoke and dc gates (their TUs call into this one) — cc.ts. Same
1233
+ * walk shape as moduleUsesZlib. */
1234
+ export function moduleUsesDynAsync(mod) {
1235
+ const fns = new Set([
1236
+ "async.awaitDyn", "timers.immediatePromise",
1237
+ "process.onUnhandledRejection", "process.onWarning", "process.offWarning", "process.emitWarning",
1238
+ "als.new", "als.get", "als.run", "als.exitRun", "als.enterWith", "als.disable",
1239
+ "dc.chanBindStore", "dc.chanUnbindStore", "dc.chanRunStores",
1240
+ ]);
1241
+ let found = false;
1242
+ const visit = (v) => {
1243
+ if (found || v === null || typeof v !== "object")
1244
+ return;
1245
+ if (Array.isArray(v)) {
1246
+ for (const item of v)
1247
+ visit(item);
1248
+ return;
1249
+ }
1250
+ const node = v;
1251
+ if (node.kind === "libCall" && typeof node.fn === "string" && fns.has(node.fn)) {
1252
+ found = true;
1253
+ return;
1254
+ }
1255
+ // A DYN-typed await reads through scr_await_dyn (the DOM-crossing
1256
+ // await lives in the gated TU) — promise<dyn> receivers' awaits and
1257
+ // the lifted then/catch helpers alike.
1258
+ if (node.kind === "awaitExpr" && node.type !== undefined && node.type.kind === "dyn") {
1259
+ found = true;
1260
+ return;
1261
+ }
1262
+ for (const key of Object.keys(v))
1263
+ visit(v[key]);
1264
+ };
1265
+ visit(mod);
1266
+ return found;
1267
+ }
1268
+ export function moduleUsesInspect(mod) {
1269
+ let found = false;
1270
+ const visit = (v) => {
1271
+ if (found || v === null || typeof v !== "object")
1272
+ return;
1273
+ if (Array.isArray(v)) {
1274
+ for (const item of v)
1275
+ visit(item);
1276
+ return;
1277
+ }
1278
+ const node = v;
1279
+ if (node.kind === "libCall" && typeof node.fn === "string" && node.fn.startsWith("insp.")) {
1280
+ found = true;
1281
+ return;
1282
+ }
1283
+ for (const key of Object.keys(v))
1284
+ visit(v[key]);
1285
+ };
1286
+ visit(mod);
1287
+ return found;
1288
+ }
1289
+ /** True when the module contains any net libCall — the link switch that
1290
+ * pulls scr_net.c into the binary and has the emitted main call
1291
+ * scr_net_install (cc.ts + emitter; the scr_events gating precedent).
1292
+ * Net-free programs pay zero bytes and keep their exact link line. Same
1293
+ * generic-walk shape as moduleUsesZlib. */
1294
+ export function moduleUsesNet(mod) {
1295
+ let found = false;
1296
+ const visit = (v) => {
1297
+ if (found || v === null || typeof v !== "object")
1298
+ return;
1299
+ if (Array.isArray(v)) {
1300
+ for (const item of v)
1301
+ visit(item);
1302
+ return;
1303
+ }
1304
+ const node = v;
1305
+ if (node.kind === "libCall" && typeof node.fn === "string" &&
1306
+ (node.fn.startsWith("net.") || node.fn.startsWith("http.") ||
1307
+ node.fn.startsWith("tls.") || node.fn.startsWith("https.") ||
1308
+ node.fn.startsWith("http2."))) {
1309
+ // http/tls/https/http2 all ride ON net: their libCalls need scr_net.c
1310
+ // linked and installed too, so the net switch answers for every
1311
+ // server-family prefix.
1312
+ found = true;
1313
+ return;
1314
+ }
1315
+ // A net-family HANDLE TYPE anywhere in the IR (a global or local whose
1316
+ // initializing statement compiled to a runtime fence still carries the
1317
+ // type): its emitted release call needs the unit linked even when no
1318
+ // libCall survived the fencing.
1319
+ if (node.kind === "netServer" || node.kind === "netSocket" ||
1320
+ node.kind === "http2Session" || node.kind === "http2Stream" ||
1321
+ node.kind === "httpReq" || node.kind === "httpRes" ||
1322
+ node.kind === "httpClientReq" || node.kind === "secureCtx") {
1323
+ found = true;
1324
+ return;
1325
+ }
1326
+ for (const key of Object.keys(v))
1327
+ visit(v[key]);
1328
+ };
1329
+ visit(mod);
1330
+ return found;
1331
+ }
1332
+ /** True when the module contains any sym.* libCall or a symbol-kind type
1333
+ * anywhere in the IR — the link switch that pulls scr_symbol.c into the
1334
+ * binary (cc.ts; the scr_net gating precedent — no install call, the
1335
+ * Symbol.for registry initializes lazily). The TYPE check matters like
1336
+ * net's: a symbol-typed local whose initializer compiled to a runtime
1337
+ * fence still emits release calls that need the unit linked. Symbol-free
1338
+ * programs pay zero bytes and keep their exact link line. */
1339
+ export function moduleUsesSymbol(mod) {
1340
+ let found = false;
1341
+ const visit = (v) => {
1342
+ if (found || v === null || typeof v !== "object")
1343
+ return;
1344
+ if (Array.isArray(v)) {
1345
+ for (const item of v)
1346
+ visit(item);
1347
+ return;
1348
+ }
1349
+ const node = v;
1350
+ if (node.kind === "libCall" && typeof node.fn === "string" && node.fn.startsWith("sym.")) {
1351
+ found = true;
1352
+ return;
1353
+ }
1354
+ if (node.kind === "symbol") {
1355
+ found = true;
1356
+ return;
1357
+ }
1358
+ for (const key of Object.keys(v))
1359
+ visit(v[key]);
1360
+ };
1361
+ visit(mod);
1362
+ return found;
1363
+ }
1364
+ /** True when the module uses the URLSearchParams surface — sp.* libCalls,
1365
+ * the url.searchParams getter, or a searchParams-kind type anywhere on
1366
+ * the IR (a fenced statement can leave a typed local whose release call
1367
+ * still needs the unit linked) — the link switch that pulls
1368
+ * scr_url_params.c into the binary (the moduleUsesSymbol precedent: pure
1369
+ * data structure, no loop hooks, cross-compiles everywhere). sp-free
1370
+ * programs keep their exact link line; scr_url.c itself stays
1371
+ * always-linked and never references the unit. */
1372
+ export function moduleUsesSearchParams(mod) {
1373
+ let found = false;
1374
+ const visit = (v) => {
1375
+ if (found || v === null || typeof v !== "object")
1376
+ return;
1377
+ if (Array.isArray(v)) {
1378
+ for (const item of v)
1379
+ visit(item);
1380
+ return;
1381
+ }
1382
+ const node = v;
1383
+ if (node.kind === "libCall" && typeof node.fn === "string" &&
1384
+ (node.fn.startsWith("sp.") || node.fn === "url.searchParams")) {
1385
+ found = true;
1386
+ return;
1387
+ }
1388
+ if (node.kind === "searchParams") {
1389
+ found = true;
1390
+ return;
1391
+ }
1392
+ for (const key of Object.keys(v))
1393
+ visit(v[key]);
1394
+ };
1395
+ visit(mod);
1396
+ return found;
1397
+ }
1398
+ /** True when the module contains any fs.watch/watcher.* libCall — the
1399
+ * link switch that pulls scr_watch.c into the binary and has the emitted
1400
+ * main call scr_watch_install (cc.ts + emitter; the scr_net gating
1401
+ * precedent). Watch-free programs pay zero bytes and keep their exact
1402
+ * link line. Same generic-walk shape as moduleUsesZlib. */
1403
+ export function moduleUsesFsWatch(mod) {
1404
+ let found = false;
1405
+ const visit = (v) => {
1406
+ if (found || v === null || typeof v !== "object")
1407
+ return;
1408
+ if (Array.isArray(v)) {
1409
+ for (const item of v)
1410
+ visit(item);
1411
+ return;
1412
+ }
1413
+ const node = v;
1414
+ if (node.kind === "libCall" && typeof node.fn === "string" &&
1415
+ (node.fn.startsWith("fs.watch") || node.fn.startsWith("watcher."))) {
1416
+ found = true;
1417
+ return;
1418
+ }
1419
+ // A watcher HANDLE TYPE left behind by a fenced statement still emits
1420
+ // scr_watcher_release — the unit must link.
1421
+ if (node.kind === "fsWatcher") {
1422
+ found = true;
1423
+ return;
1424
+ }
1425
+ for (const key of Object.keys(v))
1426
+ visit(v[key]);
1427
+ };
1428
+ visit(mod);
1429
+ return found;
1430
+ }
1431
+ /** True when the module contains any test.* libCall or a testCtx handle
1432
+ * type — the link switch that pulls scr_test.c into the binary and has
1433
+ * the emitted main return scr_test_exit_code() after the loop drains
1434
+ * (cc.ts + emitter; the moduleUsesDgram shape). Test-free programs pay
1435
+ * zero bytes and keep their exact link line. */
1436
+ export function moduleUsesNodeTest(mod) {
1437
+ let found = false;
1438
+ const visit = (v) => {
1439
+ if (found || v === null || typeof v !== "object")
1440
+ return;
1441
+ if (Array.isArray(v)) {
1442
+ for (const item of v)
1443
+ visit(item);
1444
+ return;
1445
+ }
1446
+ const node = v;
1447
+ if (node.kind === "libCall" && typeof node.fn === "string" && node.fn.startsWith("test.")) {
1448
+ found = true;
1449
+ return;
1450
+ }
1451
+ // A TestContext HANDLE TYPE left behind by a fenced statement still
1452
+ // emits a release call — the unit must link (the dgram type story).
1453
+ if (node.kind === "testCtx") {
1454
+ found = true;
1455
+ return;
1456
+ }
1457
+ for (const key of Object.keys(v))
1458
+ visit(v[key]);
1459
+ };
1460
+ visit(mod);
1461
+ return found;
1462
+ }
1463
+ /** True when the module contains any dgram.* or dns.* libCall — the
1464
+ * link switch that pulls scr_dgram.c into the binary and has the emitted
1465
+ * main call scr_dgram_install (cc.ts + emitter; the scr_net gating
1466
+ * precedent — dns.lookup lives in the same unit, so either prefix
1467
+ * answers). Dgram-free programs pay zero bytes and keep their exact link
1468
+ * line. Same generic-walk shape as moduleUsesZlib. */
1469
+ export function moduleUsesDgram(mod) {
1470
+ let found = false;
1471
+ const visit = (v) => {
1472
+ if (found || v === null || typeof v !== "object")
1473
+ return;
1474
+ if (Array.isArray(v)) {
1475
+ for (const item of v)
1476
+ visit(item);
1477
+ return;
1478
+ }
1479
+ const node = v;
1480
+ if (node.kind === "libCall" && typeof node.fn === "string" &&
1481
+ (node.fn.startsWith("dgram.") || node.fn.startsWith("dns."))) {
1482
+ found = true;
1483
+ return;
1484
+ }
1485
+ // A dgram HANDLE TYPE left behind by a fenced statement still emits a
1486
+ // release call — the unit must link (the moduleUsesNet type story).
1487
+ if (node.kind === "dgramSocket") {
1488
+ found = true;
1489
+ return;
1490
+ }
1491
+ for (const key of Object.keys(v))
1492
+ visit(v[key]);
1493
+ };
1494
+ visit(mod);
1495
+ return found;
1496
+ }
1497
+ /** True when the module contains any http.* libCall — the link switch
1498
+ * that pulls scr_http.c into the binary (cc.ts; moduleUsesNet already
1499
+ * answers true for these, so scr_net.c comes along). */
1500
+ export function moduleUsesHttpServer(mod) {
1501
+ let found = false;
1502
+ const visit = (v) => {
1503
+ if (found || v === null || typeof v !== "object")
1504
+ return;
1505
+ if (Array.isArray(v)) {
1506
+ for (const item of v)
1507
+ visit(item);
1508
+ return;
1509
+ }
1510
+ const node = v;
1511
+ if (node.kind === "libCall" && typeof node.fn === "string" &&
1512
+ (node.fn.startsWith("http.") || node.fn.startsWith("tls.") || node.fn.startsWith("https.") ||
1513
+ node.fn.startsWith("http2."))) {
1514
+ // scr_tls.c calls into scr_http.c unconditionally (the https server
1515
+ // and client are the http ones over a transport), so any tls/https
1516
+ // use pulls the http unit too.
1517
+ found = true;
1518
+ return;
1519
+ }
1520
+ // An http-family HANDLE TYPE left behind by a fenced statement still
1521
+ // emits its release call — the unit must link (the moduleUsesNet type
1522
+ // story; secureCtx rides here because scr_tls.c calls into scr_http.c).
1523
+ if (node.kind === "httpReq" || node.kind === "httpRes" ||
1524
+ node.kind === "httpClientReq" || node.kind === "secureCtx") {
1525
+ found = true;
1526
+ return;
1527
+ }
1528
+ for (const key of Object.keys(v))
1529
+ visit(v[key]);
1530
+ };
1531
+ visit(mod);
1532
+ return found;
1533
+ }
1534
+ /** The legacy http2.* libCalls implemented by scr_http.c/scr_tls.c (the
1535
+ * allowHTTP1 compatibility slice) — they must NOT pull scr_http2.c, so
1536
+ * divergence-57 binaries keep their exact link line. */
1537
+ const HTTP2_LEGACY_FNS = new Set([
1538
+ "http2.createSecureServer", "http2.createSecureServerSni",
1539
+ "http2.serverOnSessionError", "http2.streamNoop", "http2.streamUndefCall",
1540
+ ]);
1541
+ /** True when the module uses the REAL h2 surface (scr_http2.c): any core
1542
+ * http2.* libCall, or an h2 handle type left behind by a fenced statement
1543
+ * (its emitted release call needs the unit — the moduleUsesNet story). */
1544
+ export function moduleUsesHttp2(mod) {
1545
+ let found = false;
1546
+ const visit = (v) => {
1547
+ if (found || v === null || typeof v !== "object")
1548
+ return;
1549
+ if (Array.isArray(v)) {
1550
+ for (const item of v)
1551
+ visit(item);
1552
+ return;
1553
+ }
1554
+ const node = v;
1555
+ if (node.kind === "libCall" && typeof node.fn === "string" &&
1556
+ node.fn.startsWith("http2.") && !HTTP2_LEGACY_FNS.has(node.fn)) {
1557
+ found = true;
1558
+ return;
1559
+ }
1560
+ if (node.kind === "http2Session" || node.kind === "http2Stream") {
1561
+ found = true;
1562
+ return;
1563
+ }
1564
+ for (const key of Object.keys(v))
1565
+ visit(v[key]);
1566
+ };
1567
+ visit(mod);
1568
+ return found;
1569
+ }
1570
+ /** True when the module contains any tls.* or https.* libCall — the link
1571
+ * switch that pulls scr_tls.c and the vendored mbedTLS archive into the
1572
+ * binary (cc.ts; moduleUsesNet and moduleUsesHttpServer already answer
1573
+ * true for these, so scr_net.c and scr_http.c come along). TLS-free
1574
+ * programs keep their exact link line and never build mbedTLS. */
1575
+ export function moduleUsesTls(mod) {
1576
+ let found = false;
1577
+ const visit = (v) => {
1578
+ if (found || v === null || typeof v !== "object")
1579
+ return;
1580
+ if (Array.isArray(v)) {
1581
+ for (const item of v)
1582
+ visit(item);
1583
+ return;
1584
+ }
1585
+ const node = v;
1586
+ if (node.kind === "libCall" && typeof node.fn === "string" &&
1587
+ (node.fn.startsWith("tls.") || node.fn.startsWith("https.") ||
1588
+ node.fn.startsWith("http2."))) {
1589
+ found = true;
1590
+ return;
1591
+ }
1592
+ // A secureCtx HANDLE TYPE left behind by a fenced statement still
1593
+ // emits scr_secure_ctx_release — the unit must link.
1594
+ if (node.kind === "secureCtx") {
1595
+ found = true;
1596
+ return;
1597
+ }
1598
+ for (const key of Object.keys(v))
1599
+ visit(v[key]);
1600
+ };
1601
+ visit(mod);
1602
+ return found;
1603
+ }
1604
+ /** The may-throw seed: libCall members that can raise. Every fs.* member
1605
+ * EXCEPT existsSync (which, like Node's, swallows errors and returns false)
1606
+ * throws a catchable error on failure; json.parse throws a catchable
1607
+ * SyntaxError-shaped string on malformed input; process.* members never
1608
+ * throw. Backends' may-throw analyses must treat a function containing one
1609
+ * of these as throwing, exactly like a `throw` statement (and must ALSO
1610
+ * seed on `dynCheck` and `awaitExpr` nodes, which throw on validation
1611
+ * failure / promise rejection). */
1612
+ export const MAY_THROW_LIB_FNS = new Set([
1613
+ "insp.jsonDyn",
1614
+ // diagnostics_channel: publish runs subscribers synchronously (a throw
1615
+ // propagates — the documented divergence from triggerUncaughtException);
1616
+ // the subscribe/unsubscribe forms throw Node's ERR_INVALID_ARG_TYPE for
1617
+ // non-function subscribers.
1618
+ "dc.publish",
1619
+ "dc.subscribe",
1620
+ "dc.unsubscribe",
1621
+ "dc.chanSubscribe",
1622
+ "dc.chanUnsubscribe",
1623
+ "dc.tcSubscribe",
1624
+ "dc.tcUnsubscribe",
1625
+ "dc.tcTraceSync",
1626
+ "dc.tcTraceCallback",
1627
+ "dc.tcTracePromise",
1628
+ "process.onUnhandledRejection",
1629
+ "async.awaitDyn",
1630
+ "als.run",
1631
+ "als.exitRun",
1632
+ "dc.chanBindStore",
1633
+ "dc.chanRunStores",
1634
+ "http.resWriteHeadDyn",
1635
+ "net.sockSetEncoding",
1636
+ "http.reqSetEncoding",
1637
+ "fs.readFileSyncBuf",
1638
+ "fs.readFileSyncDyn",
1639
+ // tls/https runtime options records: the walks throw the catchable
1640
+ // runtime fence for out-of-bounds members and non-PEM values (the
1641
+ // divergence-66 stance).
1642
+ "tls.pemDyn",
1643
+ "tls.createServerDyn",
1644
+ "tls.createServerDynCb",
1645
+ "https.createServerDyn",
1646
+ "https.createServerDynCb",
1647
+ "tls.connect",
1648
+ "tls.connectCb",
1649
+ // EventEmitter dispatch runs user listeners synchronously (emit), and
1650
+ // the mutation forms fire the newListener/removeListener meta events —
1651
+ // any of them can throw. emitError additionally THROWS its payload when
1652
+ // 'error' has no listener, Node's unhandled-'error' contract.
1653
+ "emitter.emit",
1654
+ "emitter.emitError",
1655
+ "emitter.on",
1656
+ "emitter.off",
1657
+ "emitter.checkListener",
1658
+ "emitter.onDyn",
1659
+ "emitter.offDyn",
1660
+ "emitter.onData",
1661
+ "emitter.onDataDyn",
1662
+ "emitter.emitData",
1663
+ "readable.nextChunk",
1664
+ "readable.nextChunkDyn",
1665
+ // The checked-dynamic chunk forms throw Node's ERR_INVALID_ARG_TYPE
1666
+ // chunk TypeError on a non-string/Buffer DOM kind.
1667
+ "net.sockWriteDyn",
1668
+ "net.sockEndDyn",
1669
+ "http.resWriteDyn",
1670
+ "http.resEndDyn",
1671
+ "http.clientWriteDyn",
1672
+ "http.clientEndDyn",
1673
+ // The URL-string client form throws catchably on an unparsable input
1674
+ // ("Invalid URL") or a non-http scheme (ERR_INVALID_PROTOCOL).
1675
+ "http.requestUrl",
1676
+ "http.requestUrlCb",
1677
+ // The h2 client's throwing entries: connect on a bad/non-http
1678
+ // authority (ERR_INVALID_URL / ERR_INVALID_PROTOCOL), request on a
1679
+ // closed/destroyed session (ERR_HTTP2_INVALID_SESSION), setEncoding on
1680
+ // an unknown name (ERR_UNKNOWN_ENCODING). Event fires run user code
1681
+ // synchronously (the stream-operations stance below), so the data/
1682
+ // write paths that can fire inline join too.
1683
+ "http2.connect",
1684
+ "http2.connectCb",
1685
+ "http2.sessionRequest",
1686
+ "http2.streamSetEncoding",
1687
+ "http2.streamRespond",
1688
+ "http2.streamWrite",
1689
+ "http2.streamWriteBytes",
1690
+ "http2.streamEnd",
1691
+ "http2.streamEndStr",
1692
+ "http2.streamEndBytes",
1693
+ "emitter.removeAll",
1694
+ // Stream operations run user code synchronously (option callbacks and
1695
+ // 'data'/'drain'/... listeners on the caller's stack, like emit) — any
1696
+ // of them may leave a listener's throw pending. destroy with no 'error'
1697
+ // listener throws the payload itself (the emitter.emitError contract).
1698
+ // The dyn-options constructors walk the record at runtime: a consumed-
1699
+ // but-unlowered option (objectMode, construct, ...) throws the compile
1700
+ // fence's runtime twin; a bad encoding throws ERR_UNKNOWN_ENCODING.
1701
+ "stream.finished",
1702
+ "stream.finishedDyn",
1703
+ "stream.pipeline",
1704
+ "stream.pipelineDyn",
1705
+ "readable.newDyn",
1706
+ "writable.newDyn",
1707
+ "duplex.newDyn",
1708
+ "transform.newDyn",
1709
+ "passthrough.newDyn",
1710
+ "readable.initDyn",
1711
+ "writable.initDyn",
1712
+ "duplex.initDyn",
1713
+ "transform.initDyn",
1714
+ "passthrough.initDyn",
1715
+ "readable.push",
1716
+ "readable.pushStr",
1717
+ "readable.pushNull",
1718
+ "readable.pushU",
1719
+ "readable.pushDyn",
1720
+ "readable.unshift",
1721
+ "readable.unshiftStr",
1722
+ "readable.read",
1723
+ "readable.resume",
1724
+ "readable.pipe",
1725
+ "readable.unpipe",
1726
+ "writable.write",
1727
+ "writable.writeStr",
1728
+ "writable.writeU",
1729
+ "writable.writeDyn",
1730
+ "writable.end",
1731
+ "writable.uncork",
1732
+ "stream.destroy",
1733
+ "stream.destroyErr",
1734
+ // setMaxListeners(n) throws Node's ERR_OUT_OF_RANGE RangeError for
1735
+ // negative/NaN arguments; the static form's default-max write validates
1736
+ // identically (Node's validateNumber(n, "setMaxListeners", 0)).
1737
+ "emitter.setMax",
1738
+ "emitter.setDefaultMax",
1739
+ // The createConnection request forms run the caller's dialer closure
1740
+ // synchronously — a throw there propagates like Node's.
1741
+ "http.requestConn",
1742
+ "http.requestConnCb",
1743
+ "process.kill",
1744
+ "process.killNum",
1745
+ // cpuUsage(prev)'s field validation: negative/non-finite prev fields
1746
+ // throw Node's ERR_INVALID_ARG_VALUE RangeError, catchably.
1747
+ "process.cpuPrevValidate",
1748
+ // Non-TTY stdin: Node's stdin has no setRawMode member, so the call is
1749
+ // the exact catchable TypeError.
1750
+ "process.stdinSetRawMode",
1751
+ // The unguarded h2-only stream call: always throws Node's TypeError.
1752
+ "http2.streamUndefCall",
1753
+ // A read of a declare-d const nothing defines: always throws Node's
1754
+ // catchable ReferenceError.
1755
+ "global.undefRead",
1756
+ "child.kill",
1757
+ // The caller's lookup runs synchronously inside the connect call — a
1758
+ // throw there propagates like Node's.
1759
+ "net.connectLookup",
1760
+ "cp.execSync",
1761
+ "cp.execCapture",
1762
+ // The client request constructors validate the method token
1763
+ // synchronously (Node's ClientRequest ctor throw:
1764
+ // ERR_INVALID_HTTP_TOKEN).
1765
+ "http.request",
1766
+ "http.requestCb",
1767
+ "http.requestConn",
1768
+ "http.requestConnCb",
1769
+ "https.request",
1770
+ "https.requestCb",
1771
+ "https.requestFn",
1772
+ "https.requestFnCb",
1773
+ "rl.question",
1774
+ "island.eval",
1775
+ "island.import",
1776
+ "island.castFail",
1777
+ "json.parse",
1778
+ // decodeURIComponent throws the spec's URIError on bad hex/invalid
1779
+ // UTF-8 octets (encodeURIComponent never throws — see the IrLibFn doc).
1780
+ "str.decodeUriComponent",
1781
+ // The base64 globals: atob/btoa throw the catchable DOMException
1782
+ // InvalidCharacterError on malformed input; the zero-argument form
1783
+ // always throws Node's TypeError [ERR_MISSING_ARGS].
1784
+ "str.atob",
1785
+ "str.btoa",
1786
+ "str.b64Missing",
1787
+ // queueMicrotask's checked-dynamic form throws ERR_INVALID_ARG_TYPE
1788
+ // synchronously on a non-function argument (the closure form never
1789
+ // throws — the callback's own throw surfaces at drain, uncaught).
1790
+ "timers.queueMicrotaskDyn",
1791
+ // structuredClone: option validation throws Node's TypeErrors,
1792
+ // functions/handles the spec's DataCloneError, cycles the scriptc
1793
+ // fence; the zero-argument form always throws ERR_MISSING_ARGS. The
1794
+ // DOMException clone shares the option validation.
1795
+ "dyn.structuredClone",
1796
+ "dyn.cloneMissing",
1797
+ "dyn.cloneTransferFail",
1798
+ // the dyn Object walks throw on null/undefined receivers
1799
+ "dyn.objKeys",
1800
+ "dyn.objValues",
1801
+ "dyn.objEntries",
1802
+ "error.domClone",
1803
+ // new RegExp compiles the pattern eagerly: an invalid pattern or flag
1804
+ // throws Node's catchable SyntaxError at construction.
1805
+ "regex.new",
1806
+ "dyn.keySet",
1807
+ "dyn.toString",
1808
+ "dyn.defineProps",
1809
+ "process.chdir",
1810
+ "fs.realpathSync",
1811
+ "fs.readFileSync",
1812
+ "fs.writeFileSync",
1813
+ "fs.appendFileSync",
1814
+ "fs.mkdirSync",
1815
+ "fs.rmSync",
1816
+ "fs.rmdirSync",
1817
+ "fs.readdirSync",
1818
+ "fs.readdirTypesSync",
1819
+ "url.new",
1820
+ "url.fileURLToPathUrl",
1821
+ "url.fileURLToPathStr",
1822
+ // The win32-target flavor of pathToFileURL (same runtime entry point —
1823
+ // the bridge dispatches by the binary's platform): Node's win32 arm
1824
+ // raises ERR_INVALID_ARG_VALUE TypeErrors for malformed UNC inputs, so
1825
+ // only THIS flavor seeds may-throw — posix emission stays untouched.
1826
+ "url.pathToFileURLWin32",
1827
+ // URLSearchParams from a string[][]: Node's ERR_INVALID_TUPLE TypeError
1828
+ // on a row that is not a [name, value] pair. The rest of the sp family
1829
+ // never throws.
1830
+ "sp.fromPairs",
1831
+ "fs.statSync",
1832
+ "crypto.randomBytesToString",
1833
+ "crypto.randomBytes",
1834
+ "buffer.concatLen",
1835
+ "fs.readFileSyncBytes",
1836
+ "fs.writeFileSyncBytes",
1837
+ "zlib.inflateSync",
1838
+ "date.toISOString",
1839
+ "fs.mkdirRecursiveSync",
1840
+ "fs.rmOptsSync",
1841
+ "fs.rmRetrySync",
1842
+ "fs.mkdtempSync",
1843
+ "fs.accessSync",
1844
+ "fs.readFdSync",
1845
+ "fs.readFdSyncBytes",
1846
+ // node:dgram state errors (Node throws synchronously): bind on a bound
1847
+ // socket, connect on a connected one, send/close/address on a closed or
1848
+ // never-bound one.
1849
+ "dgram.bind",
1850
+ "dgram.bindCb",
1851
+ "dgram.connect",
1852
+ "dgram.connectCb",
1853
+ "dgram.sendStr",
1854
+ "dgram.sendBytes",
1855
+ "dgram.address",
1856
+ "dgram.close",
1857
+ "dgram.closeCb",
1858
+ // The assert surface: every entry point except sameValue, bytesDeepEq,
1859
+ // and the shape accumulator's begin/slot/test calls throws the
1860
+ // catchable AssertionError on failure.
1861
+ "assert.ok",
1862
+ "assert.eqF64",
1863
+ "assert.eqStr",
1864
+ "assert.eqBool",
1865
+ "assert.eqSym",
1866
+ "assert.eqDyn",
1867
+ "assert.deepResult",
1868
+ "assert.match",
1869
+ "assert.refEqBytes",
1870
+ "assert.refEqFn",
1871
+ "assert.throwsNone",
1872
+ "assert.throwsMismatch",
1873
+ "assert.throwsRegex",
1874
+ "assert.shapeEnd",
1875
+ "assert.unwantedRejection",
1876
+ "assert.expectsErrDyn",
1877
+ "assert.ifErrorErr",
1878
+ "assert.ifErrorF64",
1879
+ "assert.ifErrorStr",
1880
+ "assert.ifErrorBool",
1881
+ "assert.ifErrorDyn",
1882
+ // util.inspect of an island `any` composite: the runtime tag is all
1883
+ // there is, so the honest answer is a catchable TypeError.
1884
+ "insp.jsval",
1885
+ // The wider sync fs slice: same catchable errno throws as the rest.
1886
+ "fs.unlinkSync",
1887
+ "fs.chmodSync",
1888
+ "fs.chownSync",
1889
+ "fs.copyFileSync",
1890
+ "fs.lstatSync",
1891
+ "fs.writeFileModeSync",
1892
+ "fs.mkdirModeSync",
1893
+ "fs.mkdirRecursiveModeSync",
1894
+ "fs.openSync",
1895
+ "fs.readSync",
1896
+ "fs.closeSync",
1897
+ "fs.watch",
1898
+ "fs.watchCb",
1899
+ "crypto.x509Fingerprint",
1900
+ "crypto.x509FingerprintStr",
1901
+ "crypto.x509ValidFrom",
1902
+ "crypto.x509ValidFromStr",
1903
+ "crypto.x509ValidTo",
1904
+ "crypto.x509ValidToStr",
1905
+ ]);
1906
+ //# sourceMappingURL=nodes.js.map