@scriptc/compiler 0.0.8 → 0.0.10

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 (90) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +13 -0
  2. package/dist/backend/cc.js +14 -0
  3. package/dist/backend/cc.js.map +1 -1
  4. package/dist/backend/emission/emit-exprs.js +244 -17
  5. package/dist/backend/emission/emit-exprs.js.map +1 -1
  6. package/dist/backend/emission/emit-types.d.ts +3 -0
  7. package/dist/backend/emission/emit-types.js +12 -0
  8. package/dist/backend/emission/emit-types.js.map +1 -1
  9. package/dist/backend/emission/emit-walkers.d.ts +7 -5
  10. package/dist/backend/emission/emit-walkers.js +52 -12
  11. package/dist/backend/emission/emit-walkers.js.map +1 -1
  12. package/dist/backend/emission/emitter.d.ts +1 -1
  13. package/dist/backend/emission/emitter.js +30 -9
  14. package/dist/backend/emission/emitter.js.map +1 -1
  15. package/dist/backend/llvm/dyn.d.ts +1 -0
  16. package/dist/backend/llvm/dyn.js +83 -1
  17. package/dist/backend/llvm/dyn.js.map +1 -1
  18. package/dist/backend/llvm/emitter.js +357 -20
  19. package/dist/backend/llvm/emitter.js.map +1 -1
  20. package/dist/diagnostics/diagnostic.d.ts +73 -14
  21. package/dist/diagnostics/diagnostic.js +112 -31
  22. package/dist/diagnostics/diagnostic.js.map +1 -1
  23. package/dist/diagnostics/render.js +8 -0
  24. package/dist/diagnostics/render.js.map +1 -1
  25. package/dist/frontend/lowering/lib-boundary.js +11 -1
  26. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  27. package/dist/frontend/lowering/lower-assert.js +16 -0
  28. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  29. package/dist/frontend/lowering/lower-builtins.d.ts +24 -0
  30. package/dist/frontend/lowering/lower-builtins.js +256 -3
  31. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  32. package/dist/frontend/lowering/lower-calls.d.ts +71 -11
  33. package/dist/frontend/lowering/lower-calls.js +942 -18
  34. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  35. package/dist/frontend/lowering/lower-classes.js +27 -1
  36. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  37. package/dist/frontend/lowering/lower-containers.d.ts +21 -2
  38. package/dist/frontend/lowering/lower-containers.js +386 -53
  39. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  40. package/dist/frontend/lowering/lower-dgram.js +92 -17
  41. package/dist/frontend/lowering/lower-dgram.js.map +1 -1
  42. package/dist/frontend/lowering/lower-emitter.js +54 -0
  43. package/dist/frontend/lowering/lower-emitter.js.map +1 -1
  44. package/dist/frontend/lowering/lower-exprs.js +231 -17
  45. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  46. package/dist/frontend/lowering/lower-modules.js +33 -2
  47. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  48. package/dist/frontend/lowering/lower-namespaces.d.ts +25 -11
  49. package/dist/frontend/lowering/lower-namespaces.js +50 -15
  50. package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
  51. package/dist/frontend/lowering/lower-server.js +211 -4
  52. package/dist/frontend/lowering/lower-server.js.map +1 -1
  53. package/dist/frontend/lowering/lower-stmts.d.ts +1 -5
  54. package/dist/frontend/lowering/lower-stmts.js +660 -19
  55. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  56. package/dist/frontend/lowering/lower-stream.js +92 -5
  57. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  58. package/dist/frontend/lowering/lowerer.d.ts +102 -15
  59. package/dist/frontend/lowering/lowerer.js +378 -30
  60. package/dist/frontend/lowering/lowerer.js.map +1 -1
  61. package/dist/frontend/lowering/surfaces.js +20 -3
  62. package/dist/frontend/lowering/surfaces.js.map +1 -1
  63. package/dist/frontend/npm-static-rewrite.d.ts +7 -2
  64. package/dist/frontend/npm-static-rewrite.js +566 -138
  65. package/dist/frontend/npm-static-rewrite.js.map +1 -1
  66. package/dist/frontend/npm-static.js +11 -2
  67. package/dist/frontend/npm-static.js.map +1 -1
  68. package/dist/frontend/program.js +5 -5
  69. package/dist/frontend/program.js.map +1 -1
  70. package/dist/frontend/shared.d.ts +6 -1
  71. package/dist/frontend/shared.js +46 -1
  72. package/dist/frontend/shared.js.map +1 -1
  73. package/dist/frontend/types.d.ts +13 -0
  74. package/dist/frontend/types.js +88 -9
  75. package/dist/frontend/types.js.map +1 -1
  76. package/dist/index.js +169 -32
  77. package/dist/index.js.map +1 -1
  78. package/dist/ir/nodes.d.ts +230 -12
  79. package/dist/ir/nodes.js +85 -6
  80. package/dist/ir/nodes.js.map +1 -1
  81. package/dist/ir/validate.js +109 -9
  82. package/dist/ir/validate.js.map +1 -1
  83. package/dist/library/fence-eval.d.ts +77 -0
  84. package/dist/library/fence-eval.js +385 -0
  85. package/dist/library/fence-eval.js.map +1 -0
  86. package/dist/library/profile.d.ts +11 -1
  87. package/dist/library/profile.js +102 -18
  88. package/dist/library/profile.js.map +1 -1
  89. package/package.json +2 -2
  90. package/surface-manifest.json +10 -3
@@ -9,7 +9,8 @@
9
9
  * beyond these shapes fences member-qualified — never a generic
10
10
  * rejection, never silence. */
11
11
  import * as ts from "../ts7/adapter.js";
12
- import { locOf } from "../program.js";
12
+ import { ladderFenceExpr, nodeThrowExpr } from "./lowerer.js";
13
+ import { isJsSourceFile, locOf } from "../program.js";
13
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";
14
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
16
  import { conditionalSpreadOf } from "./lower-exprs.js";
@@ -420,6 +421,28 @@ export function lowerNetModuleCall(L, expr, bi, loc) {
420
421
  return { kind: "libCall", fn: "net.createServer", args: [], type: NETSERVER_T, loc };
421
422
  }
422
423
  if (args.length === 1) {
424
+ // A provably-non-object, non-function argument (the invalid-input
425
+ // probes: createServer('path'), createServer(0)): Node throws
426
+ // ERR_INVALID_ARG_TYPE on 'options' before any server exists. DYN
427
+ // result — the checked-dynamic lane's concise arrows box it, and
428
+ // the throw means it never materializes.
429
+ if (isJsSourceFile(expr.getSourceFile())) {
430
+ const t = L.mapTypeOf(L.typeOf(args[0]));
431
+ if (t !== null && (t.kind === "string" || t.kind === "f64" || t.kind === "bool")) {
432
+ const raw = L.lowerExpr(args[0]);
433
+ return {
434
+ kind: "libCall",
435
+ fn: "error.argTypeThrow",
436
+ args: [
437
+ { kind: "strLit", value: "options", type: STRING, loc },
438
+ { kind: "strLit", value: "of type object", type: STRING, loc },
439
+ { kind: "dynFrom", value: raw, type: DYN, loc },
440
+ ],
441
+ type: NETSERVER_T,
442
+ loc,
443
+ };
444
+ }
445
+ }
423
446
  const { cb } = lowerCallbackArg(L, args[0], "connection handlers", 1, (p) => p.kind === "netSocket", "use (socket) or ()", [NETSOCKET_T]);
424
447
  return { kind: "libCall", fn: "net.createServerCb", args: [cb], type: NETSERVER_T, loc };
425
448
  }
@@ -438,6 +461,26 @@ export function lowerNetModuleCall(L, expr, bi, loc) {
438
461
  if (args.length >= 1 && ts.isObjectLiteralExpression(args[0])) {
439
462
  return lowerNetConnectOptions(L, expr, bi.member, loc);
440
463
  }
464
+ // A RUNTIME option bag (a record binding or dyn value — the
465
+ // invalid-input probes build theirs with computed keys): the
466
+ // checked-dynamic walk validates Node-order and the compiler-rendered
467
+ // fence is the post-validation tail.
468
+ if (args.length === 1 && isJsSourceFile(expr.getSourceFile())) {
469
+ const t = L.mapTypeOf(L.typeOf(args[0]));
470
+ if (t !== null && (t.kind === "dyn" || t.kind === "record")) {
471
+ const raw = L.lowerExpr(args[0]);
472
+ if (raw.type.kind === "dyn" || L.dynConvertible(raw.type)) {
473
+ const bag = raw.type.kind === "dyn" ? raw : { kind: "dynFrom", value: raw, type: DYN, loc };
474
+ return {
475
+ kind: "libCall",
476
+ fn: "net.connectOptsChk",
477
+ args: [bag, ladderFenceExpr(L, `${bi.member} with a runtime options record`, expr, "pass the options as an object literal — port, host, autoSelectFamily, autoSelectFamilyAttemptTimeout, and lookup are the supported options")],
478
+ type: NETSOCKET_T,
479
+ loc,
480
+ };
481
+ }
482
+ }
483
+ }
441
484
  if (args.length < 1 || args.length > 3) {
442
485
  L.noLowering(`${bi.member} with ${args.length} arguments`, expr, `the supported form is ${bi.member}(port, host?, connectListener?)`);
443
486
  }
@@ -510,10 +553,49 @@ function lookupFnShapeOk(L, t) {
510
553
  function lowerNetConnectOptions(L, expr, member, loc) {
511
554
  const args = expr.arguments;
512
555
  const optsNode = args[0];
556
+ const isJs = isJsSourceFile(expr.getSourceFile());
557
+ // The checked-dynamic option-bag route (JS sources): a literal with
558
+ // computed keys (the invalid-input probes' spelling) or an objectMode-
559
+ // trio member rides WHOLE to the runtime walk — Node's Socket-ctor
560
+ // validation order (the trio's ERR_INVALID_ARG_VALUE first, then
561
+ // port/host/autoSelectFamily), with the compiler-rendered fence as the
562
+ // post-validation tail.
563
+ if (isJs) {
564
+ const needsBag = optsNode.properties.some((p) => (!ts.isPropertyAssignment(p) && !ts.isShorthandPropertyAssignment(p)) ||
565
+ (ts.isPropertyAssignment(p) && ts.isComputedPropertyName(p.name)) ||
566
+ ["objectMode", "readableObjectMode", "writableObjectMode"].includes((ts.isPropertyAssignment(p) || ts.isShorthandPropertyAssignment(p)) &&
567
+ (ts.isIdentifier(p.name) || ts.isStringLiteral(p.name)) ? p.name.text : ""));
568
+ if (needsBag) {
569
+ const raw = L.lowerExpr(optsNode);
570
+ if (raw.type.kind === "dyn" || L.dynConvertible(raw.type)) {
571
+ const bag = raw.type.kind === "dyn" ? raw : { kind: "dynFrom", value: raw, type: DYN, loc };
572
+ return {
573
+ kind: "libCall",
574
+ fn: "net.connectOptsChk",
575
+ args: [bag, ladderFenceExpr(L, `${member} with these options`, optsNode, "port, host, autoSelectFamily, autoSelectFamilyAttemptTimeout, and lookup are the supported options")],
576
+ type: NETSOCKET_T,
577
+ loc,
578
+ };
579
+ }
580
+ }
581
+ }
513
582
  let port = null;
514
583
  let host = null;
515
584
  let lookup = null;
516
585
  let autoSelect = false;
586
+ let attempt = null;
587
+ let optionThrow = null;
588
+ const propThrow = (name, expected, got) => ({
589
+ kind: "libCall",
590
+ fn: "error.propTypeThrow",
591
+ args: [
592
+ { kind: "strLit", value: name, type: STRING, loc },
593
+ { kind: "strLit", value: expected, type: STRING, loc },
594
+ got.type.kind === "dyn" ? got : { kind: "dynFrom", value: got, type: DYN, loc },
595
+ ],
596
+ type: NETSOCKET_T,
597
+ loc,
598
+ });
517
599
  for (const prop of optsNode.properties) {
518
600
  let initializer;
519
601
  if (ts.isPropertyAssignment(prop) &&
@@ -539,6 +621,13 @@ function lowerNetConnectOptions(L, expr, member, loc) {
539
621
  else if (key === "host") {
540
622
  host = lowerVal();
541
623
  if (host.type.kind !== "string") {
624
+ // A provably-non-string host (the invalid-input probes): Node's
625
+ // lookupAndConnect throws ERR_INVALID_ARG_TYPE at connect time.
626
+ if (isJs && (host.type.kind === "dyn" || L.dynConvertible(host.type))) {
627
+ optionThrow ??= propThrow("options.host", "of type string", host);
628
+ host = null;
629
+ continue;
630
+ }
542
631
  L.noLowering(`a ${member} 'host' option of '${L.fmt(host.type)}' values`, prop, "the host is a string here");
543
632
  }
544
633
  }
@@ -547,10 +636,28 @@ function lowerNetConnectOptions(L, expr, member, loc) {
547
636
  // dial below (false/dynamic would mean Node's single-address dial,
548
637
  // which the lookup form does not implement).
549
638
  if (initializer === null || initializer.kind !== ts.SyntaxKind.TrueKeyword) {
639
+ // A provably-non-boolean value throws Node's validateBoolean
640
+ // ladder instead of fencing.
641
+ const raw = initializer !== null && isJs ? L.lowerExpr(initializer) : null;
642
+ if (raw !== null && raw.type.kind !== "bool" &&
643
+ (raw.type.kind === "dyn" || L.dynConvertible(raw.type))) {
644
+ optionThrow ??= propThrow("options.autoSelectFamily", "of type boolean", raw);
645
+ continue;
646
+ }
550
647
  L.noLowering(`${member} with a non-literal autoSelectFamily option`, prop, "the lowered form is autoSelectFamily: true beside a lookup function");
551
648
  }
552
649
  autoSelect = true;
553
650
  }
651
+ else if (key === "autoSelectFamilyAttemptTimeout") {
652
+ // The attempt budget validates at runtime (Node's validateInt32-
653
+ // from-1 ladder) and is then inert — the single dial has nothing
654
+ // to time, the autoSelectFamily simplification's sibling.
655
+ const raw = lowerVal();
656
+ if (!(raw.type.kind === "dyn" || raw.kind === "unitLit" || L.dynConvertible(raw.type))) {
657
+ L.noLowering(`a ${member} 'autoSelectFamilyAttemptTimeout' option of '${L.fmt(raw.type)}' values`, prop, "the budget is a number here");
658
+ }
659
+ attempt = raw.type.kind === "dyn" ? raw : { kind: "dynFrom", value: raw, type: DYN, loc };
660
+ }
554
661
  else if (key === "lookup") {
555
662
  if (initializer === null) {
556
663
  L.noLowering(`${member} with a shorthand lookup option`, prop, "spell it out: lookup: theResolver");
@@ -564,10 +671,21 @@ function lowerNetConnectOptions(L, expr, member, loc) {
564
671
  L.noLowering(`${member} option '${key}'`, prop, "port, host, autoSelectFamily, and lookup are the supported options");
565
672
  }
566
673
  }
674
+ // A collected option-contract violation replaces the whole call: Node
675
+ // throws it from Socket.connect before dialing (port validation held —
676
+ // the port arm above fenced non-number ports already).
677
+ if (optionThrow !== null)
678
+ return optionThrow;
567
679
  if (port === null) {
568
680
  L.noLowering(`${member} options without a port`, optsNode, "the supported options object is { port, host?, autoSelectFamily?, lookup? }");
569
681
  }
570
682
  host ??= { kind: "strLit", value: "localhost", type: STRING, loc };
683
+ if (attempt !== null) {
684
+ if (lookup !== null || args.length !== 1) {
685
+ L.noLowering(`${member} with an autoSelectFamilyAttemptTimeout beside a lookup or connect listener`, expr, "the validated-budget form is the bare options call — register listeners separately");
686
+ }
687
+ return { kind: "libCall", fn: "net.connectAttempt", args: [port, host, attempt], type: NETSOCKET_T, loc };
688
+ }
571
689
  if (lookup !== null) {
572
690
  if (!autoSelect) {
573
691
  L.noLowering(`${member} with a lookup but no autoSelectFamily: true`, optsNode, "the lookup form dials every answered address in order — Node does that under autoSelectFamily: true, so spell it");
@@ -771,6 +889,30 @@ function lowerNetServerMethodCall(L, call, access) {
771
889
  v6only = v;
772
890
  }
773
891
  }
892
+ else if (key === "signal" && ts.isPropertyAssignment(prop) &&
893
+ isJsSourceFile(call.getSourceFile())) {
894
+ // A provably-non-AbortSignal signal (the invalid-input probes:
895
+ // strings, numbers, plain records) throws Node's
896
+ // validateAbortSignal ladder; plausible signal values keep the
897
+ // fence — abort-driven close has no lowering yet.
898
+ const raw = L.lowerExpr(prop.initializer);
899
+ const provablyNot = raw.type.kind === "string" || raw.type.kind === "f64" ||
900
+ raw.type.kind === "bool" || raw.type.kind === "record" || raw.type.kind === "array";
901
+ if (provablyNot && L.dynConvertible(raw.type)) {
902
+ return {
903
+ kind: "libCall",
904
+ fn: "error.propTypeThrow",
905
+ args: [
906
+ { kind: "strLit", value: "options.signal", type: STRING, loc },
907
+ { kind: "strLit", value: "an instance of AbortSignal", type: STRING, loc },
908
+ { kind: "dynFrom", value: raw, type: DYN, loc },
909
+ ],
910
+ type: ts.isExpressionStatement(call.parent) ? VOID : NETSERVER_T,
911
+ loc,
912
+ };
913
+ }
914
+ L.noLowering(`listen option 'signal'`, prop, "abort-driven close has no lowering yet — port, host, and ipv6Only are the supported listen options");
915
+ }
774
916
  else {
775
917
  L.noLowering(`listen option '${key}'`, prop, "port, host, and ipv6Only are the supported listen options");
776
918
  }
@@ -1022,6 +1164,31 @@ function lowerNetSocketMethodCall(L, call, access) {
1022
1164
  const args = call.arguments;
1023
1165
  if (name === "write" || name === "end") {
1024
1166
  requireStatementPosition(L, call, `socket.${name}(...)`);
1167
+ // write(chunk, encoding) — the two-argument encoding form: 'buffer'
1168
+ // beside a string chunk is Node's stream_base typecheck ("Second
1169
+ // argument must be a buffer", thrown synchronously on an established
1170
+ // socket — the invalid-input probes' shape); utf8 spellings are the
1171
+ // plain write (that IS the encoding written); a Buffer chunk ignores
1172
+ // the encoding like Node does. Other encodings keep the fence.
1173
+ if (name === "write" && args.length === 2) {
1174
+ const encT = L.typeOf(args[1]);
1175
+ const chunkT = L.mapTypeOf(L.typeOf(args[0]));
1176
+ if (encT.isStringLiteralType() && chunkT !== null) {
1177
+ if (encT.value === "buffer" && chunkT.kind === "string" &&
1178
+ isJsSourceFile(call.getSourceFile())) {
1179
+ L.lowerExpr(args[0]); // evaluation order (effect-free in practice)
1180
+ return nodeThrowExpr(1, "ERR_INVALID_ARG_TYPE", "Second argument must be a buffer", VOID, loc);
1181
+ }
1182
+ const passthrough = (chunkT.kind === "string" && (encT.value === "utf8" || encT.value === "utf-8")) ||
1183
+ (chunkT.kind === "bytes" && chunkT.elem === "u8");
1184
+ if (passthrough) {
1185
+ const receiver2 = handleReceiver(L, access.expression, NETSOCKET_T);
1186
+ const data2 = L.lowerExpr(args[0]);
1187
+ const fn = data2.type.kind === "string" ? "net.sockWrite" : "net.sockWriteBytes";
1188
+ return { kind: "libCall", fn, args: [receiver2, data2], type: VOID, loc };
1189
+ }
1190
+ }
1191
+ }
1025
1192
  const maxArgs = name === "write" ? 1 : 1;
1026
1193
  const minArgs = name === "write" ? 1 : 0;
1027
1194
  if (args.length < minArgs || args.length > maxArgs) {
@@ -1981,9 +2148,29 @@ function lowerTlsServerOptions(L, node, what) {
1981
2148
  * checked-dynamic JS lane's record — passes whole to the runtime walk
1982
2149
  * (scr_tls_srv_opts_walk: same member split, fences thrown at runtime,
1983
2150
  * the divergence-66 stance); anything else keeps the compile fence. */
2151
+ /** The option keys whose Node argument contracts the runtime walker
2152
+ * validates (scr_tls_opts_validate) — a literal carrying any of them
2153
+ * rides WHOLE to the walker so the typed ladders run in Node's order
2154
+ * (the static walk would fence them before validating). */
2155
+ const TLS_VALIDATED_OPTIONS = new Set([
2156
+ "ciphers", "passphrase", "ecdhCurve", "sessionIdContext",
2157
+ "clientCertEngine", "privateKeyEngine", "privateKeyIdentifier",
2158
+ "minVersion", "maxVersion", "handshakeTimeout", "keepAliveInitialDelay",
2159
+ "sessionTimeout", "ticketKeys",
2160
+ ]);
2161
+ /** True when a literal options bag carries a runtime-validated key (and
2162
+ * the source is JS — TypeScript keeps its compile fences). */
2163
+ function tlsLiteralNeedsRuntimeWalk(node) {
2164
+ if (!isJsSourceFile(node.getSourceFile()))
2165
+ return false;
2166
+ return node.properties.some((p) => (ts.isPropertyAssignment(p) || ts.isShorthandPropertyAssignment(p)) &&
2167
+ (ts.isIdentifier(p.name) || ts.isStringLiteral(p.name)) &&
2168
+ TLS_VALIDATED_OPTIONS.has(p.name.text));
2169
+ }
1984
2170
  function lowerTlsServerOptionsOrDyn(L, node, what) {
1985
- if (ts.isObjectLiteralExpression(node))
2171
+ if (ts.isObjectLiteralExpression(node) && !tlsLiteralNeedsRuntimeWalk(node)) {
1986
2172
  return lowerTlsServerOptions(L, node, what);
2173
+ }
1987
2174
  const v = L.lowerExpr(node);
1988
2175
  if (v.type.kind === "dyn")
1989
2176
  return { dyn: v };
@@ -2123,6 +2310,23 @@ function lowerTlsModuleCall(L, expr, bi, loc) {
2123
2310
  if (bi.member === "connect") {
2124
2311
  return lowerTlsConnectCall(L, expr, loc);
2125
2312
  }
2313
+ if (bi.member === "getCACertificates" && args.length === 1 && !args.some(ts.isSpreadElement) &&
2314
+ isJsSourceFile(expr.getSourceFile())) {
2315
+ // The type-argument ladder (validateString + the documented name
2316
+ // set); the real CA list has no lowering, so a valid name meets the
2317
+ // compiler-rendered fence after the validation.
2318
+ const raw = L.lowerExpr(args[0]);
2319
+ if (raw.type.kind === "dyn" || raw.kind === "unitLit" || L.dynConvertible(raw.type)) {
2320
+ const t = raw.type.kind === "dyn" ? raw : { kind: "dynFrom", value: raw, type: DYN, loc };
2321
+ return {
2322
+ kind: "libCall",
2323
+ fn: "tls.caCertsChk",
2324
+ args: [t, ladderFenceExpr(L, "tls.getCACertificates", expr)],
2325
+ type: L.mapTypeOf(L.typeOf(expr)) ?? DYN,
2326
+ loc,
2327
+ };
2328
+ }
2329
+ }
2126
2330
  if (bi.member === "createSecureContext") {
2127
2331
  // createSecureContext({ cert, key }) → the opaque SecureContext handle
2128
2332
  // an SNI callback answers with. The minimal honest form: exactly the
@@ -2131,8 +2335,11 @@ function lowerTlsModuleCall(L, expr, bi, loc) {
2131
2335
  if (args.length !== 1) {
2132
2336
  L.noLowering(`createSecureContext with ${args.length} arguments`, expr, "the supported form is createSecureContext({ cert, key })");
2133
2337
  }
2134
- const { cert, key } = lowerTlsServerOptions(L, args[0], "tls.createSecureContext");
2135
- return { kind: "libCall", fn: "tls.createSecureContext", args: [cert, key], type: SECURECTX_T, loc };
2338
+ const opts = lowerTlsServerOptionsOrDyn(L, args[0], "tls.createSecureContext");
2339
+ if (opts.dyn !== undefined) {
2340
+ return { kind: "libCall", fn: "tls.createSecureContextDyn", args: [opts.dyn], type: SECURECTX_T, loc };
2341
+ }
2342
+ return { kind: "libCall", fn: "tls.createSecureContext", args: [opts.cert, opts.key], type: SECURECTX_T, loc };
2136
2343
  }
2137
2344
  L.noLowering(`tls.${bi.member}`, expr, builtinFenceHintOf("tls", bi.member) ??
2138
2345
  "createServer({ cert, key }, handler) and createSecureContext({ cert, key }) are the lowered tls module functions", L.resolveValueSymbol(expr.expression));