@scriptc/compiler 0.0.11 → 0.0.13

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 (97) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +153 -24
  2. package/ambient/scriptc.d.ts +2 -2
  3. package/dist/backend/cc.d.ts +10 -2
  4. package/dist/backend/cc.js +12 -0
  5. package/dist/backend/cc.js.map +1 -1
  6. package/dist/backend/emission/emit-async.js +1 -1
  7. package/dist/backend/emission/emit-exprs.js +235 -64
  8. package/dist/backend/emission/emit-exprs.js.map +1 -1
  9. package/dist/backend/emission/emit-island.js +1 -1
  10. package/dist/backend/emission/emit-shapes.js +1 -1
  11. package/dist/backend/emission/emit-walkers.d.ts +14 -14
  12. package/dist/backend/emission/emit-walkers.js +44 -44
  13. package/dist/backend/emission/emit-walkers.js.map +1 -1
  14. package/dist/backend/emission/emitter.d.ts +3 -3
  15. package/dist/backend/emission/emitter.js +8 -8
  16. package/dist/backend/emission/may-throw.js +1 -1
  17. package/dist/backend/llvm/dyn.d.ts +10 -10
  18. package/dist/backend/llvm/dyn.js +26 -26
  19. package/dist/backend/llvm/dyn.js.map +1 -1
  20. package/dist/backend/llvm/emitter.js +140 -49
  21. package/dist/backend/llvm/emitter.js.map +1 -1
  22. package/dist/backend/llvm/shapes.js +1 -1
  23. package/dist/backend/llvm/walkers.js +2 -2
  24. package/dist/backend/llvm/walkers.js.map +1 -1
  25. package/dist/diagnostics/diagnostic.d.ts +1 -1
  26. package/dist/diagnostics/diagnostic.js +4 -3
  27. package/dist/diagnostics/diagnostic.js.map +1 -1
  28. package/dist/frontend/lowering/crypto-tables.d.ts +7 -0
  29. package/dist/frontend/lowering/crypto-tables.js +139 -0
  30. package/dist/frontend/lowering/crypto-tables.js.map +1 -0
  31. package/dist/frontend/lowering/lib-boundary.js +5 -5
  32. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  33. package/dist/frontend/lowering/lower-assert.js +12 -12
  34. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  35. package/dist/frontend/lowering/lower-builtins.d.ts +45 -27
  36. package/dist/frontend/lowering/lower-builtins.js +203 -70
  37. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  38. package/dist/frontend/lowering/lower-calls.d.ts +4 -4
  39. package/dist/frontend/lowering/lower-calls.js +74 -66
  40. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  41. package/dist/frontend/lowering/lower-classes.js +41 -7
  42. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  43. package/dist/frontend/lowering/lower-containers.d.ts +5 -5
  44. package/dist/frontend/lowering/lower-containers.js +19 -19
  45. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  46. package/dist/frontend/lowering/lower-emitter.js +2 -2
  47. package/dist/frontend/lowering/lower-emitter.js.map +1 -1
  48. package/dist/frontend/lowering/lower-exprs.d.ts +7 -7
  49. package/dist/frontend/lowering/lower-exprs.js +227 -119
  50. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  51. package/dist/frontend/lowering/lower-inspect.js +5 -5
  52. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  53. package/dist/frontend/lowering/lower-island.js +12 -3
  54. package/dist/frontend/lowering/lower-island.js.map +1 -1
  55. package/dist/frontend/lowering/lower-modules.js +17 -17
  56. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  57. package/dist/frontend/lowering/lower-namespaces.d.ts +11 -1
  58. package/dist/frontend/lowering/lower-namespaces.js +41 -6
  59. package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
  60. package/dist/frontend/lowering/lower-server.d.ts +21 -0
  61. package/dist/frontend/lowering/lower-server.js +278 -23
  62. package/dist/frontend/lowering/lower-server.js.map +1 -1
  63. package/dist/frontend/lowering/lower-stmts.d.ts +1 -1
  64. package/dist/frontend/lowering/lower-stmts.js +195 -34
  65. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  66. package/dist/frontend/lowering/lower-stream.js +67 -23
  67. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  68. package/dist/frontend/lowering/lowerer.d.ts +31 -13
  69. package/dist/frontend/lowering/lowerer.js +99 -45
  70. package/dist/frontend/lowering/lowerer.js.map +1 -1
  71. package/dist/frontend/lowering/surfaces.d.ts +6 -0
  72. package/dist/frontend/lowering/surfaces.js +78 -2
  73. package/dist/frontend/lowering/surfaces.js.map +1 -1
  74. package/dist/frontend/program.js +1 -1
  75. package/dist/frontend/shared.js +7 -7
  76. package/dist/frontend/shared.js.map +1 -1
  77. package/dist/frontend/types.d.ts +2 -2
  78. package/dist/frontend/types.js +27 -15
  79. package/dist/frontend/types.js.map +1 -1
  80. package/dist/index.js +44 -8
  81. package/dist/index.js.map +1 -1
  82. package/dist/ir/nodes.d.ts +208 -106
  83. package/dist/ir/nodes.js +100 -22
  84. package/dist/ir/nodes.js.map +1 -1
  85. package/dist/ir/validate.js +96 -38
  86. package/dist/ir/validate.js.map +1 -1
  87. package/dist/library/int-infer.js +13 -0
  88. package/dist/library/int-infer.js.map +1 -1
  89. package/dist/library/profile.d.ts +8 -6
  90. package/dist/library/profile.js.map +1 -1
  91. package/dist/library/sidecar-validate.js +1 -1
  92. package/dist/library/sidecar-validate.js.map +1 -1
  93. package/dist/library/sidecar.d.ts +1 -1
  94. package/dist/library/sidecar.js +20 -7
  95. package/dist/library/sidecar.js.map +1 -1
  96. package/package.json +2 -2
  97. package/surface-manifest.json +289 -4
@@ -12,7 +12,7 @@ import * as ts from "../ts7/adapter.js";
12
12
  import { ladderFenceExpr, nodeThrowExpr } from "./lowerer.js";
13
13
  import { isJsSourceFile, locOf } from "../program.js";
14
14
  import { arrayOf, BOOL, BYTES_U8, canBoxFuncIntoDyn, canConvertToDyn, DYN, DYN_HANDLE_KINDS, F64, funcOf, HTTP2SESSION_T, HTTP2STREAM_T, HTTPCLIENTREQ_T, HTTPREQ_T, HTTPRES_T, NETSERVER_T, NETSOCKET_T, NULL_T, SECURECTX_T, STRING, UNDEFINED_T, typeKey, VOID } from "../../ir/nodes.js";
15
- import { builtinFenceHintOf, builtinModuleFnOf, fenceOrDropOptionKey, HTTP_CLIENT_DOCUMENTED_OPTIONS, HTTP_SERVER_DOCUMENTED_OPTIONS, HTTP2_SECURE_SERVER_DOCUMENTED_OPTIONS, HTTPS_CLIENT_DOCUMENTED_OPTIONS, sideEffectFreeOptionValue, TLS_SERVER_DOCUMENTED_OPTIONS, } from "./surfaces.js";
15
+ import { AGENT_DOCUMENTED_OPTIONS, builtinFenceHintOf, builtinModuleFnOf, fenceOrDropOptionKey, HTTP_CLIENT_DOCUMENTED_OPTIONS, HTTP_SERVER_DOCUMENTED_OPTIONS, HTTP2_SECURE_SERVER_DOCUMENTED_OPTIONS, HTTPS_CLIENT_DOCUMENTED_OPTIONS, sideEffectFreeOptionValue, TLS_SERVER_DOCUMENTED_OPTIONS, } from "./surfaces.js";
16
16
  import { conditionalSpreadOf } from "./lower-exprs.js";
17
17
  /** The lowered value members of the net module — the surfaces.ts table's
18
18
  * spoke-side twin (the call SHAPES are all special-cased here, so the
@@ -152,7 +152,7 @@ function lowerCallbackArg(L, node, what, maxParams, paramOk, paramHint, dynTuple
152
152
  // hoisted `function onConn(socket)` — dyn params), or an
153
153
  // arguments-reading rest function: box it and adapt through the
154
154
  // dynCheck function boundary like the dyn flavor above — the event
155
- // payloads box into the DOM (handles by reference) and the body's
155
+ // payloads box into the checked-dynamic tree (handles by reference) and the body's
156
156
  // member uses dispatch at runtime.
157
157
  const boxed = { kind: "dynFrom", value: cb, type: DYN, loc: locOf(node) };
158
158
  const toT = funcOf([...dynTuple], VOID);
@@ -291,7 +291,7 @@ function h2HeadersCallbackAdapter(L, node, what, handleKind, withStatus, loc) {
291
291
  // value or an arguments-reading rest function of dyn params): wrap it
292
292
  // in a dynCheck to the CANONICAL event signature, then build the header
293
293
  // record and invoke through that typed boundary (handles box by
294
- // reference, the record boxes to a DOM object — the net server-callbacks
294
+ // reference, the record boxes to a dyn object — the net server-callbacks
295
295
  // precedent). The synthesized record IS the shape the adapter builds.
296
296
  const handleT = handleKind === "http2Stream" ? HTTP2STREAM_T : null;
297
297
  const isDynCb = cb.type.kind === "dyn" ||
@@ -1244,6 +1244,40 @@ function lowerNetSocketMethodCall(L, call, access) {
1244
1244
  const ms = L.lowerExprExpecting(args[0], F64);
1245
1245
  return { kind: "libCall", fn: "net.sockSetTimeout", args: [receiver, ms], type: VOID, loc };
1246
1246
  }
1247
+ if (name === "pause" || name === "resume") {
1248
+ // Flow control (the struct's flag comments in scr_net.c): pause holds
1249
+ // reads off — kernel/TCP backpressure is the buffer; resume flows
1250
+ // (and discards sans listeners, so 'end' is reachable). Both answer
1251
+ // the socket, Node's chaining.
1252
+ if (args.length !== 0) {
1253
+ L.noLowering(`${name} with ${args.length} arguments`, call, `the form is ${name}()`);
1254
+ }
1255
+ const receiver = handleReceiver(L, access.expression, NETSOCKET_T);
1256
+ const fn = name === "pause" ? "net.sockPause" : "net.sockResume";
1257
+ return { kind: "libCall", fn, args: [receiver], type: NETSOCKET_T, loc };
1258
+ }
1259
+ if (name === "setNoDelay") {
1260
+ // TCP_NODELAY on the live fd; missing/undefined means true (Node).
1261
+ // Answers the socket, Node's chaining.
1262
+ if (args.length > 1) {
1263
+ L.noLowering(`setNoDelay with ${args.length} arguments`, call, "the form is setNoDelay(noDelay?)");
1264
+ }
1265
+ const receiver = handleReceiver(L, access.expression, NETSOCKET_T);
1266
+ const enable = args.length === 1
1267
+ ? L.lowerExprExpecting(args[0], BOOL)
1268
+ : boolLit(true, loc);
1269
+ return { kind: "libCall", fn: "net.sockSetNoDelay", args: [receiver, enable], type: NETSOCKET_T, loc };
1270
+ }
1271
+ if (name === "destroySoon") {
1272
+ // end() now, destroy once the FIN actually flushed — Node's
1273
+ // 'finish'-then-destroy.
1274
+ requireStatementPosition(L, call, "socket.destroySoon()");
1275
+ if (args.length !== 0) {
1276
+ L.noLowering(`destroySoon with ${args.length} arguments`, call, "the form is destroySoon()");
1277
+ }
1278
+ const receiver = handleReceiver(L, access.expression, NETSOCKET_T);
1279
+ return { kind: "libCall", fn: "net.sockDestroySoon", args: [receiver], type: VOID, loc };
1280
+ }
1247
1281
  if (name === "read") {
1248
1282
  // socket.read(n?) — the demux peek. Answers the interned
1249
1283
  // `Buffer | null` union: exactly n buffered bytes, or null (Node's
@@ -1311,6 +1345,12 @@ function lowerNetSocketMethodCall(L, call, access) {
1311
1345
  : "net.sockOnConnect";
1312
1346
  return { kind: "libCall", fn, args: [receiver, cb, once], type: VOID, loc };
1313
1347
  }
1348
+ if (event === "finish") {
1349
+ // Fires once when the FIN goes out — once either way (the event
1350
+ // happens at most once per socket).
1351
+ const { cb } = lowerCallbackArg(L, args[1], "finish listeners", 0, () => false, "use ()", []);
1352
+ return { kind: "libCall", fn: "net.sockOnFinish", args: [receiver, cb], type: VOID, loc };
1353
+ }
1314
1354
  if (event === "secureConnect") {
1315
1355
  // The TLS client's post-handshake event: on a TLS socket the
1316
1356
  // 'connect' list already fires at establishment (scr_net.c's
@@ -1501,7 +1541,7 @@ function lowerH2SessionMethodCall(L, call, access) {
1501
1541
  return { kind: "libCall", fn: "http2.sessionDestroy", args: [receiver()], type: VOID, loc };
1502
1542
  }
1503
1543
  if (name === "settings") {
1504
- // session.settings(obj[, cb]): the settings record crosses as a DOM
1544
+ // session.settings(obj[, cb]): the settings record crosses as a dyn
1505
1545
  // value (the checked-dynamic boundary — object literals box); the
1506
1546
  // callback fires on the peer's ACK ((err, settings, duration) in the
1507
1547
  // dyn flavor, zero-arg in the typed one).
@@ -1515,7 +1555,7 @@ function lowerH2SessionMethodCall(L, call, access) {
1515
1555
  const cbV = L.lowerExpr(args[1]);
1516
1556
  if (cbV.type.kind === "dyn") {
1517
1557
  // The dyn callback keeps Node's exact (err, settings, duration)
1518
- // shape — the runtime fires it through the DOM.
1558
+ // shape — the runtime fires it through the checked-dynamic tree.
1519
1559
  return { kind: "libCall", fn: "http2.sessionSettingsDynCb", args: [receiver(), settingsArg, cbV], type: VOID, loc };
1520
1560
  }
1521
1561
  const { cb } = lowerCallbackArg(L, args[1], "settings callbacks", 0, () => false, "use () — or a dynamic (mustCall-wrapped) callback for the (err, settings, duration) shape", []);
@@ -1529,7 +1569,7 @@ function lowerH2SessionMethodCall(L, call, access) {
1529
1569
  const evT = L.typeOf(args[0]);
1530
1570
  const event = evT.isStringLiteralType() ? evT.value : null;
1531
1571
  if (event === "localSettings" || event === "remoteSettings") {
1532
- // The settings payload crosses as a DOM value: dyn (mustCall)
1572
+ // The settings payload crosses as a dyn value: dyn (mustCall)
1533
1573
  // listeners fire with the settings record; zero-arg typed
1534
1574
  // listeners register plainly.
1535
1575
  const local = boolLit(event === "localSettings", loc);
@@ -1794,6 +1834,17 @@ export function lowerServerProperty(L, expr) {
1794
1834
  const receiver = handleReceiver(L, expr.expression, NETSOCKET_T);
1795
1835
  return { kind: "libCall", fn: "net.sockWritable", args: [receiver], type: BOOL, loc };
1796
1836
  }
1837
+ if (recvKind === "netSocket" && L.isStdlibMember(expr) && expr.name.text === "bytesWritten") {
1838
+ // Every byte the write paths accepted (buffered included — Node
1839
+ // counts those too; plaintext on TLS sockets).
1840
+ const receiver = handleReceiver(L, expr.expression, NETSOCKET_T);
1841
+ return { kind: "libCall", fn: "net.sockBytesWritten", args: [receiver], type: F64, loc };
1842
+ }
1843
+ if (recvKind === "netSocket" && L.isStdlibMember(expr) && expr.name.text === "readable") {
1844
+ // true until the read half is done (peer FIN / destroy).
1845
+ const receiver = handleReceiver(L, expr.expression, NETSOCKET_T);
1846
+ return { kind: "libCall", fn: "net.sockReadable", args: [receiver], type: BOOL, loc };
1847
+ }
1797
1848
  if (recvKind === "netSocket" && L.isStdlibMember(expr) && expr.name.text === "remoteAddress") {
1798
1849
  // `string | undefined` — Node's read-time caching: a value read while
1799
1850
  // connected survives destroy; never-read sockets answer undefined
@@ -1845,7 +1896,7 @@ export function lowerServerProperty(L, expr) {
1845
1896
  if (m === "pendingSettingsAck")
1846
1897
  return { kind: "libCall", fn: "http2.sessionPendingSettingsAck", args: [recv()], type: BOOL, loc };
1847
1898
  if (m === "localSettings" || m === "remoteSettings") {
1848
- // The settings record crosses as a DOM value (the d.ts types it
1899
+ // The settings record crosses as a dyn value (the d.ts types it
1849
1900
  // `any`): member reads ride the checked-dynamic keyed read.
1850
1901
  return {
1851
1902
  kind: "libCall", fn: "http2.sessionSettingsGet",
@@ -1900,7 +1951,7 @@ function lowerRequestHandlerArg(L, node) {
1900
1951
  // A CHECKED-DYNAMIC handler (test/common's mustCall wrapper around
1901
1952
  // the (req, res) listener — the canonical suite shape): adapt
1902
1953
  // through the dynCheck function boundary. The adapter boxes req/res
1903
- // as DOM HANDLE values (SCR_DYN_HANDLE — reference identity) and
1954
+ // as dyn HANDLE values (SCR_DYN_HANDLE — reference identity) and
1904
1955
  // calls the wrapper through the checked-dynamic machinery; member
1905
1956
  // uses inside the wrapped body dispatch back onto the same http
1906
1957
  // entry points at runtime.
@@ -2037,6 +2088,113 @@ function lowerHttpCreateServerForms(L, expr, what, loc) {
2037
2088
  * createServer (Node's Server class IS the factory's product); called
2038
2089
  * from lowerNew ahead of the stdlib-constructor fence. Null when the
2039
2090
  * callee is not the http module's Server. */
2091
+ /** `new http.Agent(opts?)` / `new https.Agent(opts?)` (property access or
2092
+ * named import): the Agent lowers to a checked-dynamic HANDLE —
2093
+ * getName/destroy and the sockets/requests/freeSockets counters dispatch
2094
+ * through the dyn handle ops, and the request path threads it (the
2095
+ * requestAgent rows). Options parse at the LITERAL construction site:
2096
+ * keepAlive (a boolean value — TRUE throws the runtime's named pooling
2097
+ * fence: this client dials one connection per request), keepAliveMsecs /
2098
+ * maxSockets / maxFreeSockets / timeout / port as numbers, scheduling
2099
+ * DROPS (no free pool exists, so selection order cannot observe), other
2100
+ * documented keys fence by name, and undocumented keys drop like Node
2101
+ * drops them. Null when the callee isn't the http/https Agent. */
2102
+ export function lowerHttpAgentNew(L, expr) {
2103
+ const callee = expr.expression;
2104
+ const bi = ts.isPropertyAccessExpression(callee) && callee.name.text === "Agent"
2105
+ ? L.builtinMemberOf(callee)
2106
+ : ts.isIdentifier(callee)
2107
+ ? L.builtinImportOf(callee)
2108
+ : null;
2109
+ if (!bi || bi.member !== "Agent" || (bi.module !== "http" && bi.module !== "https"))
2110
+ return null;
2111
+ const loc = locOf(expr);
2112
+ const api = `new ${bi.module}.Agent`;
2113
+ const numLit = (value) => ({ kind: "numLit", value, type: F64, loc });
2114
+ const boolAt = (value) => ({ kind: "boolLit", value, type: BOOL, loc });
2115
+ let keepAlive = boolAt(false);
2116
+ let kaMsecs = numLit(-1);
2117
+ let maxSockets = numLit(-1);
2118
+ let maxFree = numLit(-1);
2119
+ let timeout = numLit(-1);
2120
+ let port = numLit(-1);
2121
+ const args = expr.arguments ?? [];
2122
+ if (args.length > 1) {
2123
+ L.noLowering(`${api} with ${args.length} arguments`, expr, "the supported form is new Agent(options?)");
2124
+ }
2125
+ if (args.length === 1) {
2126
+ const optsNode = stripParensAndCasts(args[0]);
2127
+ if (!ts.isObjectLiteralExpression(optsNode)) {
2128
+ L.noLowering(`${api} with a non-literal options value`, args[0], "spell the options as an object literal at the construction site");
2129
+ }
2130
+ for (const prop of optsNode.properties) {
2131
+ if (ts.isSpreadAssignment(prop)) {
2132
+ L.noLowering(`${api} with an options spread`, prop, "write each option inline");
2133
+ }
2134
+ let initializer;
2135
+ if (ts.isPropertyAssignment(prop) &&
2136
+ (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name))) {
2137
+ initializer = prop.initializer;
2138
+ }
2139
+ else if (ts.isShorthandPropertyAssignment(prop)) {
2140
+ initializer = null;
2141
+ }
2142
+ else {
2143
+ L.noLowering(`${api} options with computed keys`, prop, "each option must be a plain `name: value` (or shorthand) entry with a literal key");
2144
+ }
2145
+ const key = prop.name.text;
2146
+ const lowerVal = (want) => {
2147
+ const v = initializer !== null
2148
+ ? L.lowerExpr(initializer)
2149
+ : L.lowerShorthandValue(prop);
2150
+ if (v.type.kind === "dyn") {
2151
+ return { kind: "dynCheck", value: v, type: want === "bool" ? BOOL : F64, loc: locOf(prop) };
2152
+ }
2153
+ if (v.type.kind !== want) {
2154
+ L.noLowering(`a ${api} '${key}' option of '${L.fmt(v.type)}' values`, prop, want === "bool" ? "the option value must be a boolean" : "the option value must be a number");
2155
+ }
2156
+ return v;
2157
+ };
2158
+ switch (key) {
2159
+ case "keepAlive":
2160
+ keepAlive = lowerVal("bool");
2161
+ break;
2162
+ case "keepAliveMsecs":
2163
+ kaMsecs = lowerVal("f64");
2164
+ break;
2165
+ case "maxSockets":
2166
+ maxSockets = lowerVal("f64");
2167
+ break;
2168
+ case "maxFreeSockets":
2169
+ maxFree = lowerVal("f64");
2170
+ break;
2171
+ case "timeout":
2172
+ timeout = lowerVal("f64");
2173
+ break;
2174
+ case "port":
2175
+ // Node merges agent options under portless request options —
2176
+ // the settable defaultPort carries this one.
2177
+ port = lowerVal("f64");
2178
+ break;
2179
+ case "scheduling":
2180
+ // Free-socket selection order: no free pool exists here (no
2181
+ // keep-alive reuse), so the choice cannot observe — dropped.
2182
+ if (initializer !== null)
2183
+ L.lowerExpr(initializer); // evaluate, like Node
2184
+ break;
2185
+ default:
2186
+ fenceOrDropOptionKey(L, prop, key, api, AGENT_DOCUMENTED_OPTIONS, "keepAlive, keepAliveMsecs, maxSockets, maxFreeSockets, timeout, port, and scheduling are the supported options");
2187
+ }
2188
+ }
2189
+ }
2190
+ return {
2191
+ kind: "libCall",
2192
+ fn: "http.agentNew",
2193
+ args: [boolAt(bi.module === "https"), keepAlive, kaMsecs, maxSockets, maxFree, timeout, port],
2194
+ type: DYN,
2195
+ loc,
2196
+ };
2197
+ }
2040
2198
  export function lowerHttpServerNew(L, expr) {
2041
2199
  const callee = expr.expression;
2042
2200
  const isHttpServer = (ts.isPropertyAccessExpression(callee) &&
@@ -2176,7 +2334,7 @@ function lowerTlsServerOptionsOrDyn(L, node, what) {
2176
2334
  return { dyn: v };
2177
2335
  // A typed options RECORD binding (`const options = { key, cert, ... };
2178
2336
  // createServer(options)` — the Node-suite spelling): box it into the
2179
- // DOM and hand the whole record to the runtime walk (the divergence-66
2337
+ // dyn and hand the whole record to the runtime walk (the divergence-66
2180
2338
  // stance — members read at runtime, out-of-bounds ones throw the
2181
2339
  // catchable fence, undefined/undocumented ones drop). canConvertToDyn
2182
2340
  // now folds in the bytes-bearing option records the walker can box.
@@ -2246,7 +2404,7 @@ function lowerTlsConnectCall(L, expr, loc) {
2246
2404
  }
2247
2405
  }
2248
2406
  // The options value rides whole to the runtime walk: dyn passes
2249
- // directly; a typed record that converts boxes into the DOM.
2407
+ // directly; a typed record that converts boxes into the checked-dynamic tree.
2250
2408
  let opts;
2251
2409
  if (optsNode !== null) {
2252
2410
  const o = L.lowerExpr(optsNode);
@@ -2265,7 +2423,7 @@ function lowerTlsConnectCall(L, expr, loc) {
2265
2423
  L.noLowering("tls.connect without a port or options", expr, FORMS_HINT);
2266
2424
  }
2267
2425
  // No options: Node's defaults (rejectUnauthorized: true) — the
2268
- // runtime walk reads an absent record (the DOM undefined).
2426
+ // runtime walk reads an absent record (the dyn undefined).
2269
2427
  opts = {
2270
2428
  kind: "dynFrom",
2271
2429
  value: { kind: "unitLit", unit: "undefined", type: UNDEFINED_T, loc },
@@ -2341,9 +2499,45 @@ function lowerTlsModuleCall(L, expr, bi, loc) {
2341
2499
  }
2342
2500
  return { kind: "libCall", fn: "tls.createSecureContext", args: [opts.cert, opts.key], type: SECURECTX_T, loc };
2343
2501
  }
2502
+ // The CA-store introspection pair (scr_tls_ca.c — its own link gate, no
2503
+ // mbedTLS): getCACertificates(type?) answers the per-type cached PEM
2504
+ // string array (an omitted type completes to "default", Node's own
2505
+ // default; unknown strings throw Node's ERR_INVALID_ARG_VALUE at
2506
+ // runtime), and setDefaultCACertificates(certs) replaces the default
2507
+ // set and the client trust anchors. rootCertificates is a VALUE read —
2508
+ // lowerTlsRootCertificates below.
2509
+ if (bi.member === "getCACertificates") {
2510
+ if (args.length > 1) {
2511
+ L.noLowering(`getCACertificates with ${args.length} arguments`, expr);
2512
+ }
2513
+ const typeArg = args.length === 1
2514
+ ? L.lowerExprExpecting(args[0], STRING)
2515
+ : { kind: "strLit", value: "default", type: STRING, loc };
2516
+ return { kind: "libCall", fn: "tlsca.get", args: [typeArg], type: arrayOf(STRING), loc };
2517
+ }
2518
+ if (bi.member === "setDefaultCACertificates") {
2519
+ if (args.length !== 1) {
2520
+ L.noLowering(`setDefaultCACertificates with ${args.length} arguments`, expr);
2521
+ }
2522
+ const certs = L.lowerExpr(args[0]);
2523
+ if (certs.type.kind !== "array" || certs.type.elem.kind !== "string") {
2524
+ L.noLowering(`setDefaultCACertificates of a '${L.fmt(certs.type)}' value`, args[0], "a string[] of PEM certificates is the lowered shape — decode Buffer/typed-array entries to strings first");
2525
+ }
2526
+ return { kind: "libCall", fn: "tlsca.set", args: [certs], type: VOID, loc };
2527
+ }
2344
2528
  L.noLowering(`tls.${bi.member}`, expr, builtinFenceHintOf("tls", bi.member) ??
2345
2529
  "createServer({ cert, key }, handler) and createSecureContext({ cert, key }) are the lowered tls module functions", L.resolveValueSymbol(expr.expression));
2346
2530
  }
2531
+ /** The tls.rootCertificates VALUE read (any import spelling): the same
2532
+ * cached array getCACertificates("bundled") answers — Node's own
2533
+ * equality (test-tls-get-ca-certificates-bundled pins certs ===
2534
+ * rootCertificates). Null for other members (the property chains keep
2535
+ * trying). */
2536
+ export function lowerTlsRootCertificates(L, bi, loc) {
2537
+ if (bi.module !== "tls" || bi.member !== "rootCertificates")
2538
+ return null;
2539
+ return { kind: "libCall", fn: "tlsca.root", args: [], type: arrayOf(STRING), loc };
2540
+ }
2347
2541
  const httpClientFnBindings = new WeakMap();
2348
2542
  export function httpClientFnBindingOf(L, sym) {
2349
2543
  return httpClientFnBindings.get(L)?.get(sym);
@@ -2663,17 +2857,42 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
2663
2857
  return { kind: "libCall", fn: "http2.connect", args: [auth, reject, ca], type: HTTP2SESSION_T, loc };
2664
2858
  }
2665
2859
  if (bi.member === "getDefaultSettings") {
2666
- // The constant defaults record, as a DOM value (the d.ts types it
2860
+ // The constant defaults record, as a dyn value (the d.ts types it
2667
2861
  // `any` — reads ride the checked-dynamic keyed read).
2668
2862
  return { kind: "libCall", fn: "http2.getDefaultSettings", args: [], type: DYN, loc };
2669
2863
  }
2670
2864
  if (bi.member === "createSecureServer") {
2671
- if (args.length !== 1) {
2672
- L.noLowering(`createSecureServer with ${args.length} arguments`, expr, "the supported form is createSecureServer({ allowHTTP1: true, cert, key }) — register the handler with server.on(\"request\", (req, res) => ...)");
2865
+ if (args.length < 1 || args.length > 2) {
2866
+ L.noLowering(`createSecureServer with ${args.length} arguments`, expr, "the supported forms are createSecureServer(options[, onRequestHandler]) with options { allowHTTP1?, cert, key }");
2673
2867
  }
2674
2868
  const optsNode = args[0];
2869
+ // The eager (req, res) handler — Node routes it as the first
2870
+ // 'request' listener; both flavors' compat layers serve it (the
2871
+ // allowHTTP1 server's HTTP/1.1 handles, the h2 server's compat
2872
+ // handles over streams).
2873
+ const handlerNode = args.length === 2 ? args[1] : null;
2675
2874
  if (!ts.isObjectLiteralExpression(optsNode)) {
2676
- L.noLowering("createSecureServer with a non-literal options argument", optsNode, "pass the options as an object literal: { allowHTTP1: true, cert, key }");
2875
+ // The RUNTIME options record (the divergence-66 stance, the
2876
+ // tls/https.createServerDyn precedent): allowHTTP1/cert/key read
2877
+ // at runtime, out-of-bounds TLS members throw the catchable
2878
+ // runtime fence, h2 session-tuning keys drop exactly like the
2879
+ // literal walk ignores them.
2880
+ const v = L.lowerExpr(optsNode);
2881
+ let dynOpts;
2882
+ if (v.type.kind === "dyn") {
2883
+ dynOpts = v;
2884
+ }
2885
+ else if (canConvertToDyn(v.type, (id) => L.shapes.get(id), (id) => L.unions.get(id))) {
2886
+ dynOpts = { kind: "dynFrom", value: v, type: DYN, loc: locOf(optsNode) };
2887
+ }
2888
+ else {
2889
+ L.noLowering("createSecureServer with a non-literal options argument", optsNode, "pass the options as an object literal ({ allowHTTP1: true, cert, key }) or a runtime record of those members");
2890
+ }
2891
+ if (handlerNode === null) {
2892
+ return { kind: "libCall", fn: "http2.createSecureServerDyn", args: [dynOpts], type: NETSERVER_T, loc };
2893
+ }
2894
+ const cb = lowerRequestHandlerArg(L, handlerNode);
2895
+ return { kind: "libCall", fn: "http2.createSecureServerDynCb", args: [dynOpts, cb], type: NETSERVER_T, loc };
2677
2896
  }
2678
2897
  let cert = null;
2679
2898
  let key = null;
@@ -2835,6 +3054,9 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
2835
3054
  if (cert === null || key === null) {
2836
3055
  L.noLowering("createSecureServer without both cert and key", optsNode, "the supported options object is { allowHTTP1?, cert, key } (PEM strings or Buffers)");
2837
3056
  }
3057
+ if (sni !== null && handlerNode !== null) {
3058
+ L.noLowering("createSecureServer with both an SNICallback and an eager handler", expr, "register the handler separately: server.on(\"request\", (req, res) => ...)");
3059
+ }
2838
3060
  if (!allowHttp1) {
2839
3061
  // The REAL h2-over-TLS server (scr_http2.c + scr_tls.c): ALPN
2840
3062
  // advertises h2 alone and the h2 session attaches at establishment
@@ -2844,11 +3066,19 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
2844
3066
  if (sni !== null) {
2845
3067
  L.noLowering("createSecureServer with an SNICallback and no allowHTTP1", optsNode, "SNICallback lowers on the allowHTTP1 compatibility server — the ALPN=h2 server serves one cert/key pair");
2846
3068
  }
3069
+ if (handlerNode !== null) {
3070
+ const cb = lowerRequestHandlerArg(L, handlerNode);
3071
+ return { kind: "libCall", fn: "http2.createSecureServerH2Req", args: [cert, key, cb], type: NETSERVER_T, loc };
3072
+ }
2847
3073
  return { kind: "libCall", fn: "http2.createSecureServerH2", args: [cert, key], type: NETSERVER_T, loc };
2848
3074
  }
2849
3075
  if (sni !== null) {
2850
3076
  return { kind: "libCall", fn: "http2.createSecureServerSni", args: [cert, key, sni], type: NETSERVER_T, loc };
2851
3077
  }
3078
+ if (handlerNode !== null) {
3079
+ const cb = lowerRequestHandlerArg(L, handlerNode);
3080
+ return { kind: "libCall", fn: "http2.createSecureServerReq", args: [cert, key, cb], type: NETSERVER_T, loc };
3081
+ }
2852
3082
  return { kind: "libCall", fn: "http2.createSecureServer", args: [cert, key], type: NETSERVER_T, loc };
2853
3083
  }
2854
3084
  L.noLowering(`http2.${bi.member}`, expr, builtinFenceHintOf("http2", bi.member) ??
@@ -2918,6 +3148,8 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
2918
3148
  let connCb = null;
2919
3149
  /** agent: false — inject Connection: close into the request head. */
2920
3150
  let agentClose = null;
3151
+ /** agent: <Agent value> — the requestAgent rows thread the handle. */
3152
+ let agentVal = null;
2921
3153
  for (const prop of optsNode.properties) {
2922
3154
  // Shorthand entries ({ port } for { port: port }) are the natural
2923
3155
  // spelling at these call sites — the shorthand's VALUE lowering
@@ -3055,8 +3287,11 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
3055
3287
  // model, so it LOWERS (the header injects below). null/undefined
3056
3288
  // pick the default agent, whose keep-alive request header the
3057
3289
  // compiled client already sends (socket REUSE stays the
3058
- // documented divergence either way). Agent instances configure
3059
- // pooling the runtime does not have — fence.
3290
+ // documented divergence either way). An Agent VALUE (the lowered
3291
+ // `new http.Agent(...)` handle — a checked-dynamic value) threads
3292
+ // through the requestAgent rows: getName-keyed maxSockets
3293
+ // accounting over one-dial-per-request connections; a runtime
3294
+ // non-Agent value is Node's ERR_INVALID_ARG_TYPE.
3060
3295
  const e = initializer !== null ? stripParensAndCasts(initializer) : null;
3061
3296
  if (e !== null && e.kind === ts.SyntaxKind.FalseKeyword) {
3062
3297
  agentClose = prop;
@@ -3067,9 +3302,14 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
3067
3302
  (ts.isIdentifier(e) && e.text === "undefined"))) {
3068
3303
  break; // the default agent: what the agent-free call compiles
3069
3304
  }
3070
- L.noLowering(`a ${member} 'agent' option carrying an Agent value`, prop, "compiled clients dial one connection per request and close it with the response — " +
3071
- "agent: false (lowered: sends Connection: close) and agent: null/undefined (the default) " +
3072
- "compile; pooling Agent instances have no lowering");
3305
+ const v = initializer !== null
3306
+ ? L.lowerExpr(initializer)
3307
+ : L.lowerShorthandValue(prop);
3308
+ if (v.type.kind !== "dyn") {
3309
+ L.noLowering(`a ${member} 'agent' option of '${L.fmt(v.type)}' values`, prop, "the agent is the checked-dynamic Agent handle new http.Agent(...) answers " +
3310
+ "(or false / null / undefined)");
3311
+ }
3312
+ agentVal = v;
3073
3313
  break;
3074
3314
  }
3075
3315
  default:
@@ -3107,12 +3347,21 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
3107
3347
  // exists — a silent half-use; set headers.host instead.
3108
3348
  L.noLowering(`${member} mixing createConnection with hostname/host/port`, optsNode, "the dialer supplies the socket — set the Host header via headers: { host: ... } instead");
3109
3349
  }
3350
+ if (agentVal !== null && connCb !== null) {
3351
+ L.noLowering(`${member} mixing an agent value with createConnection`, optsNode, "both own the dial — pass one or the other");
3352
+ }
3353
+ if (agentVal !== null && binding !== null) {
3354
+ L.noLowering(`${member} through a module-function binding with an agent value`, optsNode, "call http.request/https.request directly when passing an Agent");
3355
+ }
3110
3356
  host ??= strLit("localhost");
3111
3357
  // The binding mode's default port follows the runtime dial: 443 on the
3112
3358
  // TLS arm, 80 on the plain one — exactly each client's own default.
3359
+ // With an AGENT the sentinel -1 says "no port option": the runtime
3360
+ // consults the agent's (settable) defaultPort first, Node's merge.
3113
3361
  port ??= binding !== null
3114
3362
  ? { kind: "ternary", cond: secureExpr(), then: numLit(443), else_: numLit(80), type: F64, loc }
3115
- : numLit(secure === true ? 443 : 80);
3363
+ : agentVal !== null ? numLit(-1)
3364
+ : numLit(secure === true ? 443 : 80);
3116
3365
  path ??= strLit("/");
3117
3366
  method ??= strLit("GET");
3118
3367
  timeout ??= numLit(0);
@@ -3132,14 +3381,20 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
3132
3381
  ca ??= strLit(""); /* none: /etc/ssl/cert.pem stands in for Node's roots */
3133
3382
  base.push(reject, ca);
3134
3383
  }
3384
+ if (agentVal !== null)
3385
+ base.push(agentVal);
3135
3386
  if (binding !== null)
3136
3387
  base.unshift(secureExpr());
3137
3388
  if (args.length === 1) {
3138
- const fn = binding !== null ? "https.requestFn" : secure === true ? "https.request" : "http.request";
3389
+ const fn = binding !== null ? "https.requestFn"
3390
+ : secure === true ? (agentVal !== null ? "https.requestAgent" : "https.request")
3391
+ : agentVal !== null ? "http.requestAgent" : "http.request";
3139
3392
  return { kind: "libCall", fn, args: base, type: HTTPCLIENTREQ_T, loc };
3140
3393
  }
3141
3394
  const { cb } = lowerCallbackArg(L, args[1], "response callbacks", 1, (p) => p.kind === "httpReq", "use (res) or ()", [HTTPREQ_T]);
3142
- const fn = binding !== null ? "https.requestFnCb" : secure === true ? "https.requestCb" : "http.requestCb";
3395
+ const fn = binding !== null ? "https.requestFnCb"
3396
+ : secure === true ? (agentVal !== null ? "https.requestAgentCb" : "https.requestCb")
3397
+ : agentVal !== null ? "http.requestAgentCb" : "http.requestCb";
3143
3398
  return { kind: "libCall", fn, args: [...base, cb], type: HTTPCLIENTREQ_T, loc };
3144
3399
  }
3145
3400
  /** The headers option of http.request: an object literal with literal