@scriptc/compiler 0.0.12 → 0.0.14

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 (46) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +162 -19
  2. package/dist/backend/cc.d.ts +8 -0
  3. package/dist/backend/cc.js +12 -0
  4. package/dist/backend/cc.js.map +1 -1
  5. package/dist/backend/emission/emit-exprs.js +146 -16
  6. package/dist/backend/emission/emit-exprs.js.map +1 -1
  7. package/dist/backend/llvm/emitter.js +54 -7
  8. package/dist/backend/llvm/emitter.js.map +1 -1
  9. package/dist/frontend/lowering/crypto-tables.d.ts +7 -0
  10. package/dist/frontend/lowering/crypto-tables.js +139 -0
  11. package/dist/frontend/lowering/crypto-tables.js.map +1 -0
  12. package/dist/frontend/lowering/lower-builtins.d.ts +38 -20
  13. package/dist/frontend/lowering/lower-builtins.js +111 -39
  14. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  15. package/dist/frontend/lowering/lower-calls.js +8 -0
  16. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  17. package/dist/frontend/lowering/lower-classes.js +10 -2
  18. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  19. package/dist/frontend/lowering/lower-exprs.js +69 -7
  20. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  21. package/dist/frontend/lowering/lower-modules.js +1 -1
  22. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  23. package/dist/frontend/lowering/lower-server.d.ts +21 -0
  24. package/dist/frontend/lowering/lower-server.js +294 -23
  25. package/dist/frontend/lowering/lower-server.js.map +1 -1
  26. package/dist/frontend/lowering/lower-stmts.js +4 -4
  27. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  28. package/dist/frontend/lowering/lower-stream.js +1 -1
  29. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  30. package/dist/frontend/lowering/lowerer.d.ts +21 -3
  31. package/dist/frontend/lowering/lowerer.js +48 -8
  32. package/dist/frontend/lowering/lowerer.js.map +1 -1
  33. package/dist/frontend/lowering/surfaces.d.ts +6 -0
  34. package/dist/frontend/lowering/surfaces.js +76 -0
  35. package/dist/frontend/lowering/surfaces.js.map +1 -1
  36. package/dist/frontend/types.js +12 -0
  37. package/dist/frontend/types.js.map +1 -1
  38. package/dist/index.js +6 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/ir/nodes.d.ts +103 -9
  41. package/dist/ir/nodes.js +56 -1
  42. package/dist/ir/nodes.js.map +1 -1
  43. package/dist/ir/validate.js +55 -4
  44. package/dist/ir/validate.js.map +1 -1
  45. package/package.json +2 -2
  46. package/surface-manifest.json +286 -1
@@ -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
@@ -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
@@ -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
@@ -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) &&
@@ -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);
@@ -2668,12 +2862,37 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
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) ??
@@ -2860,8 +3090,8 @@ function lowerHttp2ModuleCall(L, expr, bi, loc) {
2860
3090
  * object literal with string values, or any Record whose values are
2861
3091
  * strings / `string | undefined` — the env.pairs shape). Every other key
2862
3092
  * fences by name. The callback registers as once('response'); get() is
2863
- * request() plus the eager end(). The url-string first argument has no
2864
- * lowering (build the options object instead). `secure` is the https
3093
+ * request() plus the eager end(). A URL-STRING first argument takes the
3094
+ * requestUrl row instead (both schemes; see below). `secure` is the https
2865
3095
  * spelling: port defaults to 443 and the rejectUnauthorized (boolean)
2866
3096
  * and ca (PEM string/Buffer) options join the lowered set. A
2867
3097
  * HttpClientFnBinding as `secure` is the requestFn-binding mode: whether
@@ -2890,22 +3120,38 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
2890
3120
  const optsNode = args[0];
2891
3121
  if (!ts.isObjectLiteralExpression(optsNode)) {
2892
3122
  const t = L.mapTypeOf(L.typeOf(optsNode));
2893
- if (t?.kind === "string" && !secureish) {
2894
- // The URL-string form — http.get(`http://127.0.0.1:${port}/x`[, cb]):
2895
- // the runtime parses through the WHATWG unit and dials; unparsable
2896
- // inputs and non-http schemes throw catchably (may-throw seed).
2897
- const url = L.lowerExprExpecting(optsNode, STRING);
3123
+ if ((t?.kind === "string" || t?.kind === "url") && binding === null) {
3124
+ // The URL-string form — http.get(`http://127.0.0.1:${port}/x`[, cb])
3125
+ // and its https spelling: the runtime parses through the WHATWG unit
3126
+ // and dials; unparsable inputs and a scheme that is not the calling
3127
+ // module's throw catchably (may-throw seed). No options means Node's
3128
+ // defaults, which for https is verification against the default
3129
+ // trust anchors. The requestFn binding has no URL row — whether the
3130
+ // dial is TLS is only known at runtime there, and the two schemes
3131
+ // reject each other's URLs.
3132
+ // A URL OBJECT is its href through the same parse — Node's own
3133
+ // reading of the argument, and the serialization round-trips.
3134
+ const url = t.kind === "url"
3135
+ ? { kind: "libCall", fn: "url.href", args: [L.lowerExpr(optsNode)], type: STRING, loc }
3136
+ : L.lowerExprExpecting(optsNode, STRING);
2898
3137
  const methodLit = { kind: "strLit", value: "GET", type: STRING, loc };
2899
3138
  const autoEnd = boolLit(member === "get", loc);
3139
+ const isTls = secure === true;
2900
3140
  if (args.length === 1) {
2901
- return { kind: "libCall", fn: "http.requestUrl", args: [url, methodLit, autoEnd], type: HTTPCLIENTREQ_T, loc };
3141
+ const fn = isTls ? "https.requestUrl" : "http.requestUrl";
3142
+ return { kind: "libCall", fn, args: [url, methodLit, autoEnd], type: HTTPCLIENTREQ_T, loc };
2902
3143
  }
2903
3144
  const { cb } = lowerCallbackArg(L, args[1], "response callbacks", 1, (p) => p.kind === "httpReq", "use (res) or ()", [HTTPREQ_T]);
2904
- return { kind: "libCall", fn: "http.requestUrlCb", args: [url, methodLit, autoEnd, cb], type: HTTPCLIENTREQ_T, loc };
3145
+ const fn = isTls ? "https.requestUrlCb" : "http.requestUrlCb";
3146
+ return { kind: "libCall", fn, args: [url, methodLit, autoEnd, cb], type: HTTPCLIENTREQ_T, loc };
2905
3147
  }
2906
3148
  L.noLowering(t?.kind === "string"
2907
- ? `${member} with a URL-string first argument`
2908
- : `${member} with a non-literal options argument`, optsNode, "pass the options as an object literal: { hostname, port, path, method, timeout?, headers? }");
3149
+ ? `a URL-string first argument through a request-function binding`
3150
+ : `${member} with a non-literal options argument`, optsNode, t?.kind === "string"
3151
+ // Only the binding mode reaches here now: the scheme would have to
3152
+ // agree with a dial chosen at runtime.
3153
+ ? "call http.request / https.request directly for the URL-string form, or pass the options as an object literal"
3154
+ : "pass the options as an object literal: { hostname, port, path, method, timeout?, headers? }");
2909
3155
  }
2910
3156
  let host = null;
2911
3157
  let port = null;
@@ -2918,6 +3164,8 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
2918
3164
  let connCb = null;
2919
3165
  /** agent: false — inject Connection: close into the request head. */
2920
3166
  let agentClose = null;
3167
+ /** agent: <Agent value> — the requestAgent rows thread the handle. */
3168
+ let agentVal = null;
2921
3169
  for (const prop of optsNode.properties) {
2922
3170
  // Shorthand entries ({ port } for { port: port }) are the natural
2923
3171
  // spelling at these call sites — the shorthand's VALUE lowering
@@ -3055,8 +3303,11 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
3055
3303
  // model, so it LOWERS (the header injects below). null/undefined
3056
3304
  // pick the default agent, whose keep-alive request header the
3057
3305
  // compiled client already sends (socket REUSE stays the
3058
- // documented divergence either way). Agent instances configure
3059
- // pooling the runtime does not have — fence.
3306
+ // documented divergence either way). An Agent VALUE (the lowered
3307
+ // `new http.Agent(...)` handle — a checked-dynamic value) threads
3308
+ // through the requestAgent rows: getName-keyed maxSockets
3309
+ // accounting over one-dial-per-request connections; a runtime
3310
+ // non-Agent value is Node's ERR_INVALID_ARG_TYPE.
3060
3311
  const e = initializer !== null ? stripParensAndCasts(initializer) : null;
3061
3312
  if (e !== null && e.kind === ts.SyntaxKind.FalseKeyword) {
3062
3313
  agentClose = prop;
@@ -3067,9 +3318,14 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
3067
3318
  (ts.isIdentifier(e) && e.text === "undefined"))) {
3068
3319
  break; // the default agent: what the agent-free call compiles
3069
3320
  }
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");
3321
+ const v = initializer !== null
3322
+ ? L.lowerExpr(initializer)
3323
+ : L.lowerShorthandValue(prop);
3324
+ if (v.type.kind !== "dyn") {
3325
+ 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 " +
3326
+ "(or false / null / undefined)");
3327
+ }
3328
+ agentVal = v;
3073
3329
  break;
3074
3330
  }
3075
3331
  default:
@@ -3107,12 +3363,21 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
3107
3363
  // exists — a silent half-use; set headers.host instead.
3108
3364
  L.noLowering(`${member} mixing createConnection with hostname/host/port`, optsNode, "the dialer supplies the socket — set the Host header via headers: { host: ... } instead");
3109
3365
  }
3366
+ if (agentVal !== null && connCb !== null) {
3367
+ L.noLowering(`${member} mixing an agent value with createConnection`, optsNode, "both own the dial — pass one or the other");
3368
+ }
3369
+ if (agentVal !== null && binding !== null) {
3370
+ L.noLowering(`${member} through a module-function binding with an agent value`, optsNode, "call http.request/https.request directly when passing an Agent");
3371
+ }
3110
3372
  host ??= strLit("localhost");
3111
3373
  // The binding mode's default port follows the runtime dial: 443 on the
3112
3374
  // TLS arm, 80 on the plain one — exactly each client's own default.
3375
+ // With an AGENT the sentinel -1 says "no port option": the runtime
3376
+ // consults the agent's (settable) defaultPort first, Node's merge.
3113
3377
  port ??= binding !== null
3114
3378
  ? { kind: "ternary", cond: secureExpr(), then: numLit(443), else_: numLit(80), type: F64, loc }
3115
- : numLit(secure === true ? 443 : 80);
3379
+ : agentVal !== null ? numLit(-1)
3380
+ : numLit(secure === true ? 443 : 80);
3116
3381
  path ??= strLit("/");
3117
3382
  method ??= strLit("GET");
3118
3383
  timeout ??= numLit(0);
@@ -3132,14 +3397,20 @@ function lowerHttpClientCall(L, expr, member, loc, secure = false) {
3132
3397
  ca ??= strLit(""); /* none: /etc/ssl/cert.pem stands in for Node's roots */
3133
3398
  base.push(reject, ca);
3134
3399
  }
3400
+ if (agentVal !== null)
3401
+ base.push(agentVal);
3135
3402
  if (binding !== null)
3136
3403
  base.unshift(secureExpr());
3137
3404
  if (args.length === 1) {
3138
- const fn = binding !== null ? "https.requestFn" : secure === true ? "https.request" : "http.request";
3405
+ const fn = binding !== null ? "https.requestFn"
3406
+ : secure === true ? (agentVal !== null ? "https.requestAgent" : "https.request")
3407
+ : agentVal !== null ? "http.requestAgent" : "http.request";
3139
3408
  return { kind: "libCall", fn, args: base, type: HTTPCLIENTREQ_T, loc };
3140
3409
  }
3141
3410
  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";
3411
+ const fn = binding !== null ? "https.requestFnCb"
3412
+ : secure === true ? (agentVal !== null ? "https.requestAgentCb" : "https.requestCb")
3413
+ : agentVal !== null ? "http.requestAgentCb" : "http.requestCb";
3143
3414
  return { kind: "libCall", fn, args: [...base, cb], type: HTTPCLIENTREQ_T, loc };
3144
3415
  }
3145
3416
  /** The headers option of http.request: an object literal with literal