@scriptc/compiler 0.3.3 → 0.3.4

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 (213) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +713 -26
  2. package/dist/backend/cycle-mutability.d.ts +5 -0
  3. package/dist/backend/cycle-mutability.js +62 -3
  4. package/dist/backend/cycle-mutability.js.map +1 -1
  5. package/dist/backend/executable-features.js +1 -0
  6. package/dist/backend/executable-features.js.map +1 -1
  7. package/dist/backend/library-pack.js +1 -0
  8. package/dist/backend/library-pack.js.map +1 -1
  9. package/dist/backend/llvm/call-lifetimes.d.ts +5 -1
  10. package/dist/backend/llvm/call-lifetimes.js +11 -3
  11. package/dist/backend/llvm/call-lifetimes.js.map +1 -1
  12. package/dist/backend/llvm/call-lifetimes.test.js +31 -1
  13. package/dist/backend/llvm/call-lifetimes.test.js.map +1 -1
  14. package/dist/backend/llvm/common.d.ts +5 -0
  15. package/dist/backend/llvm/common.js +7 -0
  16. package/dist/backend/llvm/common.js.map +1 -1
  17. package/dist/backend/llvm/dense-array-access.js +2 -2
  18. package/dist/backend/llvm/dense-array-access.js.map +1 -1
  19. package/dist/backend/llvm/dyn.d.ts +40 -1
  20. package/dist/backend/llvm/dyn.js +178 -27
  21. package/dist/backend/llvm/dyn.js.map +1 -1
  22. package/dist/backend/llvm/emitter.d.ts +12 -0
  23. package/dist/backend/llvm/emitter.js +164 -27
  24. package/dist/backend/llvm/emitter.js.map +1 -1
  25. package/dist/backend/llvm/expr-bytes.d.ts +9 -0
  26. package/dist/backend/llvm/expr-bytes.js +158 -63
  27. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  28. package/dist/backend/llvm/expr-calls.d.ts +3 -1
  29. package/dist/backend/llvm/expr-calls.js +49 -1
  30. package/dist/backend/llvm/expr-calls.js.map +1 -1
  31. package/dist/backend/llvm/expr-containers.js +16 -5
  32. package/dist/backend/llvm/expr-containers.js.map +1 -1
  33. package/dist/backend/llvm/intern-tables.test.d.ts +1 -0
  34. package/dist/backend/llvm/intern-tables.test.js +77 -0
  35. package/dist/backend/llvm/intern-tables.test.js.map +1 -0
  36. package/dist/backend/llvm/lib-datagram.js +10 -0
  37. package/dist/backend/llvm/lib-datagram.js.map +1 -1
  38. package/dist/backend/llvm/lib-dispatch.js +92 -1
  39. package/dist/backend/llvm/lib-dispatch.js.map +1 -1
  40. package/dist/backend/llvm/lib-filesystem.js +51 -0
  41. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  42. package/dist/backend/llvm/lib-network.js +16 -0
  43. package/dist/backend/llvm/lib-network.js.map +1 -1
  44. package/dist/backend/llvm/lib-shared.d.ts +5 -0
  45. package/dist/backend/llvm/lib-shared.js +80 -1
  46. package/dist/backend/llvm/lib-shared.js.map +1 -1
  47. package/dist/backend/llvm/lib-stream.js +87 -0
  48. package/dist/backend/llvm/lib-stream.js.map +1 -1
  49. package/dist/backend/llvm/lib-test.js +9 -0
  50. package/dist/backend/llvm/lib-test.js.map +1 -1
  51. package/dist/backend/llvm/map-number-lookup.d.ts +4 -0
  52. package/dist/backend/llvm/map-number-lookup.js +22 -0
  53. package/dist/backend/llvm/map-number-lookup.js.map +1 -1
  54. package/dist/backend/llvm/map-number-lookup.test.js +56 -0
  55. package/dist/backend/llvm/map-number-lookup.test.js.map +1 -1
  56. package/dist/backend/llvm/map-read-lifetimes.d.ts +9 -0
  57. package/dist/backend/llvm/map-read-lifetimes.js +89 -1
  58. package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
  59. package/dist/backend/llvm/map-read-lifetimes.test.js +129 -1
  60. package/dist/backend/llvm/map-read-lifetimes.test.js.map +1 -1
  61. package/dist/backend/llvm/publish.d.ts +5 -0
  62. package/dist/backend/llvm/publish.js +22 -3
  63. package/dist/backend/llvm/publish.js.map +1 -1
  64. package/dist/backend/llvm/reference-writes.js +5 -2
  65. package/dist/backend/llvm/reference-writes.js.map +1 -1
  66. package/dist/backend/llvm/regions.d.ts +4 -1
  67. package/dist/backend/llvm/regions.js +12 -3
  68. package/dist/backend/llvm/regions.js.map +1 -1
  69. package/dist/backend/llvm/string-reads.js +2 -2
  70. package/dist/backend/llvm/string-reads.js.map +1 -1
  71. package/dist/backend/llvm/string-slices.js +2 -2
  72. package/dist/backend/llvm/string-slices.js.map +1 -1
  73. package/dist/backend/may-throw.js +4 -0
  74. package/dist/backend/may-throw.js.map +1 -1
  75. package/dist/backend/native/contracts.d.ts +3 -0
  76. package/dist/backend/native/executable.js +13 -5
  77. package/dist/backend/native/executable.js.map +1 -1
  78. package/dist/backend/native/runtime-inputs.d.ts +1 -1
  79. package/dist/backend/native/runtime-inputs.js +1 -0
  80. package/dist/backend/native/runtime-inputs.js.map +1 -1
  81. package/dist/backend/native-codegen-core.d.ts +4 -4
  82. package/dist/backend/native-codegen-core.js +4 -6
  83. package/dist/backend/native-codegen-core.js.map +1 -1
  84. package/dist/backend/native-codegen.test.js +7 -8
  85. package/dist/backend/native-codegen.test.js.map +1 -1
  86. package/dist/backend/native-link-info-core.d.ts +3 -0
  87. package/dist/backend/native-link-info-core.js.map +1 -1
  88. package/dist/backend/publication.js +3 -1
  89. package/dist/backend/publication.js.map +1 -1
  90. package/dist/backend/runtime-pack-targets.test.js +1 -0
  91. package/dist/backend/runtime-pack-targets.test.js.map +1 -1
  92. package/dist/backend/runtime-pack.test.js +1 -0
  93. package/dist/backend/runtime-pack.test.js.map +1 -1
  94. package/dist/backend/target-diagnostics.js +7 -0
  95. package/dist/backend/target-diagnostics.js.map +1 -1
  96. package/dist/backend/warm-cache.js +1 -0
  97. package/dist/backend/warm-cache.js.map +1 -1
  98. package/dist/executable/executable-cache.d.ts +1 -0
  99. package/dist/executable/executable-cache.js +1 -0
  100. package/dist/executable/executable-cache.js.map +1 -1
  101. package/dist/executable/executable-cache.test.js +1 -0
  102. package/dist/executable/executable-cache.test.js.map +1 -1
  103. package/dist/frontend/experimental-import.d.ts +5 -1
  104. package/dist/frontend/experimental-import.js +28 -3
  105. package/dist/frontend/experimental-import.js.map +1 -1
  106. package/dist/frontend/lowering/assert-instances.d.ts +38 -0
  107. package/dist/frontend/lowering/assert-instances.js +269 -0
  108. package/dist/frontend/lowering/assert-instances.js.map +1 -0
  109. package/dist/frontend/lowering/builtins/compression.d.ts +18 -0
  110. package/dist/frontend/lowering/builtins/compression.js +175 -2
  111. package/dist/frontend/lowering/builtins/compression.js.map +1 -1
  112. package/dist/frontend/lowering/builtins/constants.js +7 -2
  113. package/dist/frontend/lowering/builtins/constants.js.map +1 -1
  114. package/dist/frontend/lowering/builtins/crypto.d.ts +6 -0
  115. package/dist/frontend/lowering/builtins/crypto.js +111 -1
  116. package/dist/frontend/lowering/builtins/crypto.js.map +1 -1
  117. package/dist/frontend/lowering/builtins/errors.js +23 -0
  118. package/dist/frontend/lowering/builtins/errors.js.map +1 -1
  119. package/dist/frontend/lowering/builtins/timers.d.ts +3 -0
  120. package/dist/frontend/lowering/builtins/timers.js +109 -23
  121. package/dist/frontend/lowering/builtins/timers.js.map +1 -1
  122. package/dist/frontend/lowering/call-signatures.js +6 -0
  123. package/dist/frontend/lowering/call-signatures.js.map +1 -1
  124. package/dist/frontend/lowering/class-dynamic-dispatch.js +66 -12
  125. package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
  126. package/dist/frontend/lowering/intern-tables.d.ts +13 -0
  127. package/dist/frontend/lowering/intern-tables.js +536 -0
  128. package/dist/frontend/lowering/intern-tables.js.map +1 -0
  129. package/dist/frontend/lowering/lower-assert.js +700 -61
  130. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  131. package/dist/frontend/lowering/lower-async-iteration.js +11 -2
  132. package/dist/frontend/lowering/lower-async-iteration.js.map +1 -1
  133. package/dist/frontend/lowering/lower-calls.js +17 -3
  134. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  135. package/dist/frontend/lowering/lower-classes.d.ts +3 -0
  136. package/dist/frontend/lowering/lower-classes.js +36 -0
  137. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  138. package/dist/frontend/lowering/lower-dgram.js +77 -52
  139. package/dist/frontend/lowering/lower-dgram.js.map +1 -1
  140. package/dist/frontend/lowering/lower-exprs.js +12 -0
  141. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  142. package/dist/frontend/lowering/lower-inspect.d.ts +6 -0
  143. package/dist/frontend/lowering/lower-inspect.js +112 -39
  144. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  145. package/dist/frontend/lowering/lower-mock.d.ts +11 -0
  146. package/dist/frontend/lowering/lower-mock.js +504 -0
  147. package/dist/frontend/lowering/lower-mock.js.map +1 -0
  148. package/dist/frontend/lowering/lower-modules.js +7 -0
  149. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  150. package/dist/frontend/lowering/lower-server.d.ts +3 -0
  151. package/dist/frontend/lowering/lower-server.js +380 -30
  152. package/dist/frontend/lowering/lower-server.js.map +1 -1
  153. package/dist/frontend/lowering/lower-stmts.js +23 -2
  154. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  155. package/dist/frontend/lowering/lower-stream.d.ts +10 -6
  156. package/dist/frontend/lowering/lower-stream.js +786 -27
  157. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  158. package/dist/frontend/lowering/lower-test.js +86 -8
  159. package/dist/frontend/lowering/lower-test.js.map +1 -1
  160. package/dist/frontend/lowering/lowerer.d.ts +22 -0
  161. package/dist/frontend/lowering/lowerer.js +72 -7
  162. package/dist/frontend/lowering/lowerer.js.map +1 -1
  163. package/dist/frontend/lowering/pairs-snapshot.d.ts +6 -0
  164. package/dist/frontend/lowering/pairs-snapshot.js +49 -0
  165. package/dist/frontend/lowering/pairs-snapshot.js.map +1 -1
  166. package/dist/frontend/lowering/surfaces.d.ts +4 -0
  167. package/dist/frontend/lowering/surfaces.js +61 -15
  168. package/dist/frontend/lowering/surfaces.js.map +1 -1
  169. package/dist/frontend/lowering/zlib-constants.d.ts +1 -0
  170. package/dist/frontend/lowering/zlib-constants.js +181 -0
  171. package/dist/frontend/lowering/zlib-constants.js.map +1 -0
  172. package/dist/frontend/mock-shapes.d.ts +10 -0
  173. package/dist/frontend/mock-shapes.js +76 -0
  174. package/dist/frontend/mock-shapes.js.map +1 -0
  175. package/dist/frontend/pipeline.js +5 -3
  176. package/dist/frontend/pipeline.js.map +1 -1
  177. package/dist/frontend/program.js +6 -1
  178. package/dist/frontend/program.js.map +1 -1
  179. package/dist/frontend/resolve.d.ts +4 -0
  180. package/dist/frontend/resolve.js +14 -0
  181. package/dist/frontend/resolve.js.map +1 -1
  182. package/dist/frontend/tsc-codes.js +1 -0
  183. package/dist/frontend/tsc-codes.js.map +1 -1
  184. package/dist/frontend/type-mapper.d.ts +5 -0
  185. package/dist/frontend/type-mapper.js +102 -9
  186. package/dist/frontend/type-mapper.js.map +1 -1
  187. package/dist/index.js +2 -1
  188. package/dist/index.js.map +1 -1
  189. package/dist/ir/builtin-effects.js +36 -0
  190. package/dist/ir/builtin-effects.js.map +1 -1
  191. package/dist/ir/builtin-signatures.d.ts +1 -1
  192. package/dist/ir/builtin-signatures.js +147 -8
  193. package/dist/ir/builtin-signatures.js.map +1 -1
  194. package/dist/ir/ir.d.ts +20 -1
  195. package/dist/ir/ir.js +17 -0
  196. package/dist/ir/ir.js.map +1 -1
  197. package/dist/ir/runtime-features.d.ts +1 -0
  198. package/dist/ir/runtime-features.js +29 -2
  199. package/dist/ir/runtime-features.js.map +1 -1
  200. package/dist/ir/validate.js +195 -5
  201. package/dist/ir/validate.js.map +1 -1
  202. package/dist/library/ir-policy.js +1 -0
  203. package/dist/library/ir-policy.js.map +1 -1
  204. package/dist/native/cli.js +1 -0
  205. package/dist/native/cli.js.map +1 -1
  206. package/dist/native/compiler.js +1 -3
  207. package/dist/native/compiler.js.map +1 -1
  208. package/dist/native/prepare.js +1 -0
  209. package/dist/native/prepare.js.map +1 -1
  210. package/dist/native/sanitizer.test.js +1 -0
  211. package/dist/native/sanitizer.test.js.map +1 -1
  212. package/package.json +20 -20
  213. package/surface-manifest.json +869 -48
@@ -3,7 +3,7 @@ import * as ts from "../ts7/adapter.js";
3
3
  import { ladderFenceExpr } from "./lowerer.js";
4
4
  import { isJsSourceFile, locOf } from "../program.js";
5
5
  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/ir.js";
6
- 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";
6
+ import { AGENT_DOCUMENTED_OPTIONS, HTTPS_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";
7
7
  import { conditionalSpreadOf } from "./expressions/object-literals.js";
8
8
  import { resultIsDiscarded } from "./call-position.js";
9
9
  import { lowerCallbackArg as lowerCallbackArgShared } from "./callback-arg.js";
@@ -166,7 +166,7 @@ export function voidizedCallback(lowerer, cb, loc) {
166
166
  body: [
167
167
  {
168
168
  kind: "return",
169
- value: { kind: "closure", fnName: impl, captures: ["f.0"], type: toT, loc },
169
+ value: { kind: "closure", fnName: impl, captures: ["f.0"], type: toT, adapts: true, loc },
170
170
  loc,
171
171
  },
172
172
  ],
@@ -175,6 +175,20 @@ export function voidizedCallback(lowerer, cb, loc) {
175
175
  }
176
176
  return { kind: "call", callee: name, args: [cb], type: toT, loc };
177
177
  }
178
+ /** A socket or server listener with parameters that the runtime calls
179
+ * through the checked-dynamic boundary (close(hadError), write's
180
+ * cb(err), 'drop', getConnections): a dyn function value as is, or a
181
+ * typed function boxed with per-argument checks. */
182
+ function lowerSocketDynListener(lowerer, node, what) {
183
+ const cb = lowerer.lowerExpr(node);
184
+ if (cb.type.kind === "dyn")
185
+ return cb;
186
+ if (cb.type.kind === "func" &&
187
+ canBoxFuncIntoDyn(cb.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
188
+ return { kind: "dynFrom", value: cb, type: DYN, loc: locOf(node) };
189
+ }
190
+ lowerer.noLowering(`${what} that cannot cross the checked-dynamic boundary`, node, "the callback's parameters and return value must have checked-dynamic conversions");
191
+ }
178
192
  /** A registration/side-effect libCall re-shaped to answer its RECEIVER —
179
193
  * Node's `return this` chaining (server.listen(...), res.writeHead(...)):
180
194
  * an interned helper runs the void call and returns its first argument.
@@ -1030,6 +1044,31 @@ export function lowerHttpServerTimeoutAssignment(lowerer, left, right, loc) {
1030
1044
  loc,
1031
1045
  };
1032
1046
  }
1047
+ /** `server.maxConnections = n`: the value crosses as a dyn (undefined
1048
+ * and null unset the cap). */
1049
+ export function lowerNetServerMaxConnectionsAssignment(lowerer, left, right, loc) {
1050
+ if (!ts.isPropertyAccessExpression(left) || left.questionDotToken)
1051
+ return null;
1052
+ if (left.name.text !== "maxConnections")
1053
+ return null;
1054
+ if (lowerer.mapTypeOf(lowerer.typeOf(left.expression))?.kind !== "netServer")
1055
+ return null;
1056
+ if (!lowerer.isStdlibMember(left))
1057
+ return null;
1058
+ const receiver = coerceToHandle(lowerer, left.expression, NETSERVER_T);
1059
+ const value = lowerer.coerceToExpected(lowerer.lowerExpr(right), DYN);
1060
+ return {
1061
+ kind: "exprStmt",
1062
+ expr: {
1063
+ kind: "libCall",
1064
+ fn: "net.serverMaxConnectionsSet",
1065
+ args: [receiver, value],
1066
+ type: VOID,
1067
+ loc,
1068
+ },
1069
+ loc,
1070
+ };
1071
+ }
1033
1072
  /** Method calls on net.Server receivers: listen(port[, cb]), close([cb]),
1034
1073
  * on/once("connection" | "error" | "close", cb). address() alone fences
1035
1074
  * toward the composed address().port read. Null for other receivers. */
@@ -1041,6 +1080,24 @@ function lowerNetServerMethodCall(lowerer, call, access) {
1041
1080
  const name = access.name.text;
1042
1081
  const loc = locOf(call);
1043
1082
  const args = call.arguments;
1083
+ if (name === "getConnections") {
1084
+ // cb(null, count) on the next tick; answers the server.
1085
+ if (args.length !== 1) {
1086
+ lowerer.noLowering(`getConnections with ${args.length} arguments`, call, "the form is getConnections(callback)");
1087
+ }
1088
+ const receiver = coerceToHandle(lowerer, access.expression, NETSERVER_T);
1089
+ const cb = lowerSocketDynListener(lowerer, args[0], "getConnections callbacks");
1090
+ if (resultIsDiscarded(call)) {
1091
+ return {
1092
+ kind: "libCall",
1093
+ fn: "net.serverGetConnections",
1094
+ args: [receiver, cb],
1095
+ type: VOID,
1096
+ loc,
1097
+ };
1098
+ }
1099
+ return receiverReturningCall(lowerer, "net.serverGetConnections", [receiver, cb], NETSERVER_T, loc);
1100
+ }
1044
1101
  if (name === "setTimeout") {
1045
1102
  if (args.length < 1 || args.length > 2)
1046
1103
  lowerer.noLowering("server.setTimeout arguments", call, "pass milliseconds and an optional socket callback");
@@ -1356,6 +1413,17 @@ function lowerNetServerMethodCall(lowerer, call, access) {
1356
1413
  loc,
1357
1414
  };
1358
1415
  }
1416
+ if (event === "drop") {
1417
+ // A connection past maxConnections, with its local and remote
1418
+ // addresses.
1419
+ return {
1420
+ kind: "libCall",
1421
+ fn: "net.serverOnDrop",
1422
+ args: [receiver, lowerSocketDynListener(lowerer, args[1], "drop listeners"), once],
1423
+ type: VOID,
1424
+ loc,
1425
+ };
1426
+ }
1359
1427
  if (event === "session") {
1360
1428
  // The h2c server's per-connection session event: (session). The
1361
1429
  // result is voidized; the handle passes by reference.
@@ -1450,7 +1518,7 @@ function lowerNetServerMethodCall(lowerer, call, access) {
1450
1518
  loc,
1451
1519
  };
1452
1520
  }
1453
- lowerer.noLowering(`server.${name}(${event === null ? "non-literal event" : `"${event}"`}, ...)`, args[0], '"connection", "request", "stream", "session", "upgrade", "connect", "error", "close", "listening", and "sessionError" are the supported server events (as literals)');
1521
+ lowerer.noLowering(`server.${name}(${event === null ? "non-literal event" : `"${event}"`}, ...)`, args[0], '"connection", "request", "drop", "stream", "session", "upgrade", "connect", "error", "close", "listening", and "sessionError" are the supported server events (as literals)');
1454
1522
  }
1455
1523
  if (name === "emit") {
1456
1524
  // The demux route: emit("connection", socket) hands an accepted
@@ -1497,11 +1565,51 @@ function lowerNetServerMethodCall(lowerer, call, access) {
1497
1565
  lowerer.noLowering(`address with ${args.length} arguments`, call, "address() takes no arguments");
1498
1566
  }
1499
1567
  const result = lowerer.mapTypeOf(lowerer.typeOf(call));
1500
- if (!result || !isAddressInfoRecord(lowerer, result)) {
1568
+ if (result && isAddressInfoRecord(lowerer, result)) {
1569
+ const receiver = coerceToHandle(lowerer, access.expression, NETSERVER_T);
1570
+ return { kind: "libCall", fn: "net.serverAddress", args: [receiver], type: result, loc };
1571
+ }
1572
+ // @types/node's `string | AddressInfo | null`: the record while the
1573
+ // server listens (a TCP server never answers the pipe-path string),
1574
+ // null otherwise, like Node.
1575
+ const arms = result?.kind === "union" ? (lowerer.unions.get(result.unionId)?.arms ?? []) : [];
1576
+ const record = arms.find((arm) => isAddressInfoRecord(lowerer, arm));
1577
+ const nullTag = result?.kind === "union" ? lowerer.armTag(result.unionId, NULL_T) : -1;
1578
+ if (!result || result.kind !== "union" || record === undefined || nullTag < 0) {
1501
1579
  lowerer.noLowering("address() where the result is not the {address, family, port} record", call, "the AddressInfo shape is the supported result");
1502
1580
  }
1581
+ const unionId = result.unionId;
1503
1582
  const receiver = coerceToHandle(lowerer, access.expression, NETSERVER_T);
1504
- return { kind: "libCall", fn: "net.serverAddress", args: [receiver], type: result, loc };
1583
+ const local = lowerer.declareHiddenLocal("%addressServer", NETSERVER_T);
1584
+ const server = varRef(local.id, NETSERVER_T, loc);
1585
+ return {
1586
+ kind: "seqExpr",
1587
+ stmts: [{ kind: "varDecl", localId: local.id, init: receiver, loc }],
1588
+ result: {
1589
+ kind: "ternary",
1590
+ cond: { kind: "libCall", fn: "net.serverListening", args: [server], type: BOOL, loc },
1591
+ then: {
1592
+ kind: "unionWrap",
1593
+ unionId,
1594
+ tag: lowerer.armTag(unionId, record),
1595
+ value: { kind: "libCall", fn: "net.serverAddress", args: [server], type: record, loc },
1596
+ type: result,
1597
+ loc,
1598
+ },
1599
+ else_: {
1600
+ kind: "unionWrap",
1601
+ unionId,
1602
+ tag: nullTag,
1603
+ value: { kind: "unitLit", unit: "null", type: NULL_T, loc },
1604
+ type: result,
1605
+ loc,
1606
+ },
1607
+ type: result,
1608
+ loc,
1609
+ },
1610
+ type: result,
1611
+ loc,
1612
+ };
1505
1613
  }
1506
1614
  lowerer.noLowering(`Server.${name}`, call, 'listen, close, address().port, emit("connection", socket), and on/once of connection/request/upgrade/error/close/listening/sessionError are the supported Server members', lowerer.checker.getSymbolAtLocation(access.name));
1507
1615
  }
@@ -1556,6 +1664,79 @@ function lowerNetSocketMethodCall(lowerer, call, access) {
1556
1664
  }
1557
1665
  }
1558
1666
  }
1667
+ // A trailing callback: write's runs once the data is flushed (with
1668
+ // null for the error), end's once the socket finishes ('finish').
1669
+ const last = args.length > 0 ? args[args.length - 1] : null;
1670
+ if (last !== null &&
1671
+ args.length <= 2 &&
1672
+ (name === "end" || args.length === 2) &&
1673
+ lowerer.checker.getCallSignatures(lowerer.typeOf(last)).length > 0) {
1674
+ const sockLocal = lowerer.declareHiddenLocal("%sockCb", NETSOCKET_T);
1675
+ const stmts = [
1676
+ {
1677
+ kind: "varDecl",
1678
+ localId: sockLocal.id,
1679
+ init: coerceToHandle(lowerer, access.expression, NETSOCKET_T),
1680
+ loc,
1681
+ },
1682
+ ];
1683
+ const receiver = () => ({
1684
+ kind: "varRef",
1685
+ localId: sockLocal.id,
1686
+ type: NETSOCKET_T,
1687
+ loc,
1688
+ });
1689
+ const dataNode = args.length === 2 ? args[0] : null;
1690
+ let main = {
1691
+ kind: "libCall",
1692
+ fn: "net.sockEnd",
1693
+ args: [receiver()],
1694
+ type: VOID,
1695
+ loc,
1696
+ };
1697
+ if (dataNode !== null) {
1698
+ const data = lowerer.lowerExpr(dataNode);
1699
+ const fn = data.type.kind === "string"
1700
+ ? name === "write"
1701
+ ? "net.sockWrite"
1702
+ : "net.sockEndStr"
1703
+ : data.type.kind === "bytes" && data.type.elem === "u8"
1704
+ ? name === "write"
1705
+ ? "net.sockWriteBytes"
1706
+ : "net.sockEndBytes"
1707
+ : data.type.kind === "dyn"
1708
+ ? name === "write"
1709
+ ? "net.sockWriteDyn"
1710
+ : "net.sockEndDyn"
1711
+ : null;
1712
+ if (fn === null) {
1713
+ lowerer.noLowering(`${name} of '${lowerer.fmt(data.type)}' data`, dataNode, NARROW_DATA_HINT);
1714
+ }
1715
+ main = { kind: "libCall", fn, args: [receiver(), data], type: VOID, loc };
1716
+ }
1717
+ const cb = lowerer.lowerExpr(last);
1718
+ let register;
1719
+ if (cb.type.kind === "func" && cb.type.params.length === 0 && cb.type.ret.kind === "void") {
1720
+ register = {
1721
+ kind: "libCall",
1722
+ fn: name === "write" ? "net.sockOnWriteFlush" : "net.sockOnFinish",
1723
+ args: [receiver(), cb],
1724
+ type: VOID,
1725
+ loc,
1726
+ };
1727
+ }
1728
+ else {
1729
+ register = {
1730
+ kind: "libCall",
1731
+ fn: name === "write" ? "net.sockOnWriteFlushDyn" : "net.sockOnFinishDyn",
1732
+ args: [receiver(), lowerSocketDynListener(lowerer, last, `${name} callbacks`)],
1733
+ type: VOID,
1734
+ loc,
1735
+ };
1736
+ }
1737
+ stmts.push({ kind: "exprStmt", expr: main, loc });
1738
+ return { kind: "seqExpr", stmts, result: register, type: VOID, loc };
1739
+ }
1559
1740
  const maxArgs = name === "write" ? 1 : 1;
1560
1741
  const minArgs = name === "write" ? 1 : 0;
1561
1742
  if (args.length < minArgs || args.length > maxArgs) {
@@ -1604,13 +1785,44 @@ function lowerNetSocketMethodCall(lowerer, call, access) {
1604
1785
  }
1605
1786
  if (name === "setTimeout") {
1606
1787
  requireStatementPosition(lowerer, call, "socket.setTimeout(...)");
1607
- if (args.length !== 1) {
1608
- lowerer.noLowering(`setTimeout with ${args.length} arguments`, call, "the supported form is setTimeout(ms) — register the callback separately: socket.once('timeout', ...)");
1788
+ if (args.length !== 1 && args.length !== 2) {
1789
+ lowerer.noLowering(`setTimeout with ${args.length} arguments`, call, "the supported forms are setTimeout(ms) and setTimeout(ms, callback)");
1609
1790
  }
1610
1791
  const receiver = coerceToHandle(lowerer, access.expression, NETSOCKET_T);
1611
1792
  const ms = lowerer.lowerExprExpecting(args[0], F64);
1793
+ if (args.length === 2) {
1794
+ // Node adds the callback as a once('timeout') listener (removes it
1795
+ // for 0); msecs is checked first either way.
1796
+ const { cb } = lowerCallbackArg(lowerer, args[1], "setTimeout callbacks", 0, () => false, "use ()", []);
1797
+ return {
1798
+ kind: "libCall",
1799
+ fn: "net.sockSetTimeoutCb",
1800
+ args: [receiver, ms, cb],
1801
+ type: VOID,
1802
+ loc,
1803
+ };
1804
+ }
1612
1805
  return { kind: "libCall", fn: "net.sockSetTimeout", args: [receiver, ms], type: VOID, loc };
1613
1806
  }
1807
+ if (name === "setKeepAlive") {
1808
+ // SO_KEEPALIVE plus the initial delay (whole seconds, at least 1);
1809
+ // Node's defaults are false and 0. Answers the socket.
1810
+ if (args.length > 2) {
1811
+ lowerer.noLowering(`setKeepAlive with ${args.length} arguments`, call, "the form is setKeepAlive(enable?, initialDelay?)");
1812
+ }
1813
+ const receiver = coerceToHandle(lowerer, access.expression, NETSOCKET_T);
1814
+ const enable = args.length >= 1 ? lowerer.lowerExprExpecting(args[0], BOOL) : boolLit(false, loc);
1815
+ const delay = args.length === 2
1816
+ ? lowerer.lowerExprExpecting(args[1], F64)
1817
+ : { kind: "numLit", value: 0, type: F64, loc };
1818
+ return {
1819
+ kind: "libCall",
1820
+ fn: "net.sockSetKeepAlive",
1821
+ args: [receiver, enable, delay],
1822
+ type: NETSOCKET_T,
1823
+ loc,
1824
+ };
1825
+ }
1614
1826
  if (name === "pause" || name === "resume") {
1615
1827
  // Flow control (the struct's flag comments in scr_net.c): pause holds
1616
1828
  // reads off — kernel/TCP backpressure is the buffer; resume flows
@@ -1699,6 +1911,28 @@ function lowerNetSocketMethodCall(lowerer, call, access) {
1699
1911
  const event = evT.isStringLiteralType() ? evT.value : null;
1700
1912
  const receiver = coerceToHandle(lowerer, access.expression, NETSOCKET_T);
1701
1913
  if (event === "data") {
1914
+ const sigs = lowerer.checker.getCallSignatures(lowerer.typeOf(args[1]));
1915
+ const firstParam = sigs.length === 1 ? sigs[0].getParameters()[0] : undefined;
1916
+ const param = firstParam !== undefined
1917
+ ? lowerer.mapTypeOf(lowerer.checker.getTypeOfSymbol(firstParam))
1918
+ : null;
1919
+ if (param !== null &&
1920
+ param.kind !== "dyn" &&
1921
+ !(param.kind === "bytes" && param.elem === "u8")) {
1922
+ // A chunk typed string (or string | Buffer): the listener takes
1923
+ // the chunk through a checked boundary, so a string arrives after
1924
+ // setEncoding and a Buffer without it fails the check instead of
1925
+ // being decoded.
1926
+ const boxed = lowerSocketDynListener(lowerer, args[1], "data listeners");
1927
+ const cb = { kind: "dynCheck", value: boxed, type: funcOf([DYN], VOID), loc };
1928
+ return {
1929
+ kind: "libCall",
1930
+ fn: "net.sockOnData",
1931
+ args: [receiver, cb, once],
1932
+ type: VOID,
1933
+ loc,
1934
+ };
1935
+ }
1702
1936
  const { cb } = lowerCallbackArg(lowerer, args[1], "data listeners", 1, (p) => p.kind === "bytes" && p.elem === "u8", "use (chunk: Buffer) or ()", [DYN]);
1703
1937
  return { kind: "libCall", fn: "net.sockOnData", args: [receiver, cb, once], type: VOID, loc };
1704
1938
  }
@@ -1712,9 +1946,23 @@ function lowerNetSocketMethodCall(lowerer, call, access) {
1712
1946
  loc,
1713
1947
  };
1714
1948
  }
1949
+ if (event === "close" &&
1950
+ lowerer.checker
1951
+ .getCallSignatures(lowerer.typeOf(args[1]))
1952
+ .some((sig) => sig.getParameters().length > 0)) {
1953
+ // close(hadError): true when the socket closed because of an error.
1954
+ return {
1955
+ kind: "libCall",
1956
+ fn: "net.sockOnCloseDyn",
1957
+ args: [receiver, lowerSocketDynListener(lowerer, args[1], "close listeners"), once],
1958
+ type: VOID,
1959
+ loc,
1960
+ };
1961
+ }
1715
1962
  if (event === "end" ||
1716
1963
  event === "close" ||
1717
1964
  event === "connect" ||
1965
+ event === "ready" ||
1718
1966
  event === "timeout" ||
1719
1967
  event === "readable") {
1720
1968
  const { cb } = lowerCallbackArg(lowerer, args[1], `${event} listeners`, 0, () => false, "use ()", []);
@@ -1726,7 +1974,9 @@ function lowerNetSocketMethodCall(lowerer, call, access) {
1726
1974
  ? "net.sockOnTimeout"
1727
1975
  : event === "readable"
1728
1976
  ? "net.sockOnReadable"
1729
- : "net.sockOnConnect";
1977
+ : event === "ready"
1978
+ ? "net.sockOnReady"
1979
+ : "net.sockOnConnect";
1730
1980
  return { kind: "libCall", fn, args: [receiver, cb, once], type: VOID, loc };
1731
1981
  }
1732
1982
  if (event === "finish") {
@@ -1761,9 +2011,9 @@ function lowerNetSocketMethodCall(lowerer, call, access) {
1761
2011
  loc,
1762
2012
  };
1763
2013
  }
1764
- lowerer.noLowering(`socket.${name}(${event === null ? "non-literal event" : `"${event}"`}, ...)`, args[0], '"data", "end", "close", "error", "connect", "timeout", "readable", "secureConnect", and "session" are the supported socket events (as literals)');
2014
+ lowerer.noLowering(`socket.${name}(${event === null ? "non-literal event" : `"${event}"`}, ...)`, args[0], '"data", "end", "close", "error", "connect", "ready", "timeout", "readable", "secureConnect", and "session" are the supported socket events (as literals)');
1765
2015
  }
1766
- lowerer.noLowering(`Socket.${name}`, call, "write, end, destroy, pipe, setTimeout, remoteAddress, read, unshift, and on/once of data/end/close/error/connect/timeout/readable are the supported Socket members", lowerer.checker.getSymbolAtLocation(access.name));
2016
+ lowerer.noLowering(`Socket.${name}`, call, "write, end, destroy, pipe, setTimeout, setKeepAlive, setNoDelay, the address fields, read, unshift, and on/once of data/end/close/error/connect/ready/timeout/readable are the supported Socket members", lowerer.checker.getSymbolAtLocation(access.name));
1767
2017
  }
1768
2018
  /** The method-call dispatch for both server-surface receiver kinds — one
1769
2019
  * entry in lower-calls.ts's intrinsic chain (the lowerChildMethodCall
@@ -1792,6 +2042,7 @@ function headersSnapshotHelper(lowerer, shapeId, loc) {
1792
2042
  libCall: "http.reqHeaderPairs",
1793
2043
  params: [{ localId: "r.0", name: "r", type: HTTPREQ_T }],
1794
2044
  callArgs: [varRef("r.0", HTTPREQ_T, loc)],
2045
+ listKey: { name: "set-cookie", libCall: "http.reqHeaderList" },
1795
2046
  });
1796
2047
  }
1797
2048
  function trailersSnapshotHelper(lowerer, shapeId, loc) {
@@ -2227,6 +2478,23 @@ export function lowerServerProperty(lowerer, expr) {
2227
2478
  const receiver = coerceToHandle(lowerer, expr.expression, NETSERVER_T);
2228
2479
  return { kind: "libCall", fn: "net.serverListening", args: [receiver], type: BOOL, loc };
2229
2480
  }
2481
+ if (expr.name.text === "maxConnections") {
2482
+ const receiver = coerceToHandle(lowerer, expr.expression, NETSERVER_T);
2483
+ const value = {
2484
+ kind: "libCall",
2485
+ fn: "net.serverMaxConnectionsGet",
2486
+ args: [receiver],
2487
+ type: DYN,
2488
+ loc,
2489
+ };
2490
+ // The property's declared type: an earlier `= undefined` narrows
2491
+ // the read's own type to undefined.
2492
+ const sym = lowerer.checker.getSymbolAtLocation(expr.name);
2493
+ const declared = lowerer.mapTypeOf(sym ? lowerer.checker.getTypeOfSymbol(sym) : lowerer.typeOf(expr));
2494
+ return declared === null || declared.kind === "dyn"
2495
+ ? value
2496
+ : lowerer.coerceInto(expr, value, declared);
2497
+ }
2230
2498
  const field = httpServerTimeoutField(expr.name.text);
2231
2499
  if (field !== null) {
2232
2500
  const receiver = coerceToHandle(lowerer, expr.expression, NETSERVER_T);
@@ -2428,6 +2696,45 @@ export function lowerServerProperty(lowerer, expr) {
2428
2696
  loc,
2429
2697
  };
2430
2698
  }
2699
+ if (recvKind === "netSocket" && lowerer.isStdlibMember(expr)) {
2700
+ const name = expr.name.text;
2701
+ // The other address fields: the peer's cached like remoteAddress, the
2702
+ // local ones read live (undefined while connecting and after close).
2703
+ const field = [
2704
+ "remotePort",
2705
+ "remoteFamily",
2706
+ "localAddress",
2707
+ "localPort",
2708
+ "localFamily",
2709
+ ].indexOf(name);
2710
+ if (field >= 0) {
2711
+ const receiver = coerceToHandle(lowerer, expr.expression, NETSOCKET_T);
2712
+ const value = {
2713
+ kind: "libCall",
2714
+ fn: "net.sockAddrField",
2715
+ args: [receiver, { kind: "numLit", value: field, type: F64, loc }],
2716
+ type: DYN,
2717
+ loc,
2718
+ };
2719
+ const declared = lowerer.mapTypeOf(lowerer.typeOf(expr));
2720
+ return declared === null || declared.kind === "dyn"
2721
+ ? value
2722
+ : lowerer.coerceInto(expr, value, declared);
2723
+ }
2724
+ const scalar = name === "bytesRead"
2725
+ ? { fn: "net.sockBytesRead", type: F64 }
2726
+ : name === "connecting"
2727
+ ? { fn: "net.sockConnecting", type: BOOL }
2728
+ : name === "pending"
2729
+ ? { fn: "net.sockPending", type: BOOL }
2730
+ : name === "readyState"
2731
+ ? { fn: "net.sockReadyState", type: STRING }
2732
+ : null;
2733
+ if (scalar !== null) {
2734
+ const receiver = coerceToHandle(lowerer, expr.expression, NETSOCKET_T);
2735
+ return { kind: "libCall", fn: scalar.fn, args: [receiver], type: scalar.type, loc };
2736
+ }
2737
+ }
2431
2738
  if (recvKind === "httpClientReq" &&
2432
2739
  lowerer.isStdlibMember(expr) &&
2433
2740
  expr.name.text === "destroyed") {
@@ -2481,14 +2788,15 @@ export function lowerServerProperty(lowerer, expr) {
2481
2788
  const collection = httpReqFieldCollection(lowerer, expr.expression);
2482
2789
  const receiver = coerceToHandle(lowerer, expr.expression.expression, HTTPREQ_T);
2483
2790
  const key = { kind: "strLit", value: expr.name.text, type: STRING, loc };
2484
- const fn = collection === "headers"
2791
+ const list = isSetCookieRead(lowerer, expr, collection, key);
2792
+ const fn = collection === "headers" && !list
2485
2793
  ? "http.reqHeader"
2486
2794
  : collection === "trailers"
2487
2795
  ? "http.reqTrailer"
2488
- : collection === "headersDistinct"
2796
+ : collection === "headersDistinct" || list
2489
2797
  ? "http.reqHeaderValues"
2490
2798
  : "http.reqTrailerValues";
2491
- const type = collection.endsWith("Distinct")
2799
+ const type = collection.endsWith("Distinct") || list
2492
2800
  ? lowerer.withUndefinedArm(arrayOf(STRING))
2493
2801
  : lowerer.envValueType();
2494
2802
  return { kind: "libCall", fn, args: [receiver, key], type, loc };
@@ -2962,7 +3270,9 @@ export function lowerHttpAgentNew(lowerer, expr) {
2962
3270
  lowerer.lowerExpr(initializer); // evaluate, like Node
2963
3271
  break;
2964
3272
  default:
2965
- fenceOrDropOptionKey(lowerer, prop, key, api, AGENT_DOCUMENTED_OPTIONS, "keepAlive, keepAliveMsecs, maxSockets, maxFreeSockets, timeout, port, and scheduling are the supported options");
3273
+ fenceOrDropOptionKey(lowerer, prop, key, api, bi.module === "https" ? HTTPS_AGENT_DOCUMENTED_OPTIONS : AGENT_DOCUMENTED_OPTIONS, bi.module === "https"
3274
+ ? "keepAlive, keepAliveMsecs, maxSockets, maxFreeSockets, timeout, port, and scheduling are the supported options; pass TLS options such as ca and rejectUnauthorized on each https.request"
3275
+ : "keepAlive, keepAliveMsecs, maxSockets, maxFreeSockets, timeout, port, and scheduling are the supported options");
2966
3276
  }
2967
3277
  }
2968
3278
  }
@@ -4804,22 +5114,36 @@ export function lowerHttpHeadersElement(lowerer, expr) {
4804
5114
  lowerer.unsupported("SC1090", expr.argumentExpression, `indexing req.${collection} with non-string keys`);
4805
5115
  }
4806
5116
  const receiver = lowerer.lowerExpr(recv);
5117
+ const list = isSetCookieRead(lowerer, expr, collection, key);
4807
5118
  return {
4808
5119
  kind: "libCall",
4809
- fn: collection === "headers"
5120
+ fn: collection === "headers" && !list
4810
5121
  ? "http.reqHeader"
4811
5122
  : collection === "trailers"
4812
5123
  ? "http.reqTrailer"
4813
- : collection === "headersDistinct"
5124
+ : collection === "headersDistinct" || list
4814
5125
  ? "http.reqHeaderValues"
4815
5126
  : "http.reqTrailerValues",
4816
5127
  args: [receiver, key],
4817
- type: collection.endsWith("Distinct")
5128
+ type: collection.endsWith("Distinct") || list
4818
5129
  ? lowerer.withUndefinedArm(arrayOf(STRING))
4819
5130
  : lowerer.envValueType(),
4820
5131
  loc: locOf(expr),
4821
5132
  };
4822
5133
  }
5134
+ /** `headers["set-cookie"]` reads Node's array of every Set-Cookie line,
5135
+ * the `string[] | undefined` that @types/node's IncomingHttpHeaders
5136
+ * declares. A read typed as a string (the bundled declarations type every
5137
+ * header as one) is refused, because Node's value is an array. */
5138
+ function isSetCookieRead(lowerer, expr, collection, key) {
5139
+ if (collection !== "headers" || key.kind !== "strLit" || key.value !== "set-cookie")
5140
+ return false;
5141
+ const type = lowerer.mapTypeOf(lowerer.typeOf(expr));
5142
+ const array = lowerer.withUndefinedArm(arrayOf(STRING));
5143
+ if (type?.kind === "union" && array.kind === "union" && type.unionId === array.unionId)
5144
+ return true;
5145
+ lowerer.noLowering("reading set-cookie through string-typed headers", expr, "Node stores set-cookie as an array: read headersDistinct['set-cookie'], or install @types/node, which types headers['set-cookie'] as string[]");
5146
+ }
4823
5147
  /** Method calls on IncomingMessage receivers: on/once("data" | "end").
4824
5148
  * Null for other receivers. */
4825
5149
  function lowerHttpReqMethodCall(lowerer, call, access) {
@@ -5078,12 +5402,17 @@ function lowerHttpResMethodCall(lowerer, call, access) {
5078
5402
  // retains the setHeader refusal below.
5079
5403
  value = lowerer.coerceInto(args[1], value, STRING);
5080
5404
  }
5081
- if (value.type.kind === "f64") {
5082
- // Node formats number values via String(n) — the same ToString.
5083
- value = { kind: "toString", operand: value, type: STRING, loc };
5084
- }
5085
5405
  if (value.type.kind !== "string") {
5086
- lowerer.noLowering(`setHeader with a '${lowerer.fmt(value.type)}' value`, args[1], "header values are strings or numbers here");
5406
+ // Numbers and arrays keep their type for getHeader/getHeaders, like
5407
+ // Node's stored value; numbers serialize as String(n) and arrays as
5408
+ // one line per element.
5409
+ return {
5410
+ kind: "libCall",
5411
+ fn: "http.resSetHeaderDyn",
5412
+ args: [receiver, header, lowerer.coerceToExpected(value, DYN)],
5413
+ type: VOID,
5414
+ loc,
5415
+ };
5087
5416
  }
5088
5417
  return {
5089
5418
  kind: "libCall",
@@ -5093,6 +5422,22 @@ function lowerHttpResMethodCall(lowerer, call, access) {
5093
5422
  loc,
5094
5423
  };
5095
5424
  }
5425
+ if (name === "appendHeader") {
5426
+ requireStatementPosition(lowerer, call, "res.appendHeader(...)");
5427
+ if (args.length !== 2) {
5428
+ lowerer.noLowering(`appendHeader with ${args.length} arguments`, call, "the supported form is appendHeader(name, value)");
5429
+ }
5430
+ const receiver = coerceToHandle(lowerer, access.expression, HTTPRES_T);
5431
+ const header = lowerer.lowerExprExpecting(args[0], STRING);
5432
+ const value = lowerer.lowerExpr(args[1]);
5433
+ return {
5434
+ kind: "libCall",
5435
+ fn: "http.resAppendHeader",
5436
+ args: [receiver, header, lowerer.coerceToExpected(value, DYN)],
5437
+ type: VOID,
5438
+ loc,
5439
+ };
5440
+ }
5096
5441
  if (name === "writeHead") {
5097
5442
  if (args.length < 1 || args.length > 3) {
5098
5443
  lowerer.noLowering(`writeHead with ${args.length} arguments`, call, "the supported forms are writeHead(status[, statusMessage][, headers]) — headers as a literal object, a Record, or a flat [name, value, ...] array");
@@ -5299,10 +5644,9 @@ function lowerHttpResMethodCall(lowerer, call, access) {
5299
5644
  }
5300
5645
  if (name === "getHeader" || name === "hasHeader" || name === "removeHeader") {
5301
5646
  // The header CRUD trio (setHeader's readers): getHeader answers the
5302
- // value as SET — `string | undefined` here, where Node can also
5303
- // answer numbers/arrays it was handed (this surface stores strings);
5304
- // removeHeader drops every case-insensitive match before the head
5305
- // goes out; hasHeader is the boolean probe.
5647
+ // value as set (a number, string or string[]); removeHeader drops
5648
+ // every case-insensitive match before the head goes out; hasHeader is
5649
+ // the boolean probe.
5306
5650
  if (name === "removeHeader")
5307
5651
  requireStatementPosition(lowerer, call, "res.removeHeader(...)");
5308
5652
  if (args.length !== 1) {
@@ -5311,13 +5655,19 @@ function lowerHttpResMethodCall(lowerer, call, access) {
5311
5655
  const receiver = coerceToHandle(lowerer, access.expression, HTTPRES_T);
5312
5656
  const header = lowerer.lowerExprExpecting(args[0], STRING);
5313
5657
  if (name === "getHeader") {
5314
- return {
5658
+ // The value as set: number, string or string[] (undefined when
5659
+ // absent), checked against the declared result type.
5660
+ const value = {
5315
5661
  kind: "libCall",
5316
- fn: "http.resGetHeader",
5662
+ fn: "http.resGetHeaderDyn",
5317
5663
  args: [receiver, header],
5318
- type: lowerer.envValueType(),
5664
+ type: DYN,
5319
5665
  loc,
5320
5666
  };
5667
+ const declared = lowerer.mapTypeOf(lowerer.typeOf(call));
5668
+ return declared === null || declared.kind === "dyn"
5669
+ ? value
5670
+ : lowerer.coerceInto(call, value, declared);
5321
5671
  }
5322
5672
  if (name === "hasHeader") {
5323
5673
  return {
@@ -5366,6 +5716,6 @@ function lowerHttpResMethodCall(lowerer, call, access) {
5366
5716
  }
5367
5717
  lowerer.noLowering(`res.${name}(${event === null ? "non-literal event" : `"${event}"`}, ...)`, args[0], '"close" and "finish" are the supported response events (as literals)');
5368
5718
  }
5369
- lowerer.noLowering(`ServerResponse.${name}`, call, "setHeader, getHeader, hasHeader, removeHeader, writeHead, writeContinue, writeProcessing, writeEarlyHints, setTimeout, write, end, destroy, response state reads, and on/once of close are the supported ServerResponse members", lowerer.checker.getSymbolAtLocation(access.name));
5719
+ lowerer.noLowering(`ServerResponse.${name}`, call, "setHeader, appendHeader, getHeader, hasHeader, removeHeader, writeHead, writeContinue, writeProcessing, writeEarlyHints, setTimeout, write, end, destroy, response state reads, and on/once of close are the supported ServerResponse members", lowerer.checker.getSymbolAtLocation(access.name));
5370
5720
  }
5371
5721
  //# sourceMappingURL=lower-server.js.map