@scriptc/compiler 0.1.2 → 0.1.3

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 (151) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +59 -14
  2. package/dist/backend/c/async.d.ts +5 -0
  3. package/dist/backend/c/async.js +57 -0
  4. package/dist/backend/c/async.js.map +1 -1
  5. package/dist/backend/c/c-emitter.d.ts +3 -0
  6. package/dist/backend/c/c-emitter.js +15 -8
  7. package/dist/backend/c/c-emitter.js.map +1 -1
  8. package/dist/backend/c/exprs.d.ts +8 -1
  9. package/dist/backend/c/exprs.js +152 -9
  10. package/dist/backend/c/exprs.js.map +1 -1
  11. package/dist/backend/c/shapes.js +15 -13
  12. package/dist/backend/c/shapes.js.map +1 -1
  13. package/dist/backend/c/types.d.ts +4 -0
  14. package/dist/backend/c/types.js +18 -1
  15. package/dist/backend/c/types.js.map +1 -1
  16. package/dist/backend/c/types.test.js +8 -1
  17. package/dist/backend/c/types.test.js.map +1 -1
  18. package/dist/backend/c/walkers.js +32 -11
  19. package/dist/backend/c/walkers.js.map +1 -1
  20. package/dist/backend/json-literal.d.ts +7 -0
  21. package/dist/backend/json-literal.js +17 -0
  22. package/dist/backend/json-literal.js.map +1 -0
  23. package/dist/backend/json-literal.test.d.ts +1 -0
  24. package/dist/backend/json-literal.test.js +17 -0
  25. package/dist/backend/json-literal.test.js.map +1 -0
  26. package/dist/backend/link-plan.d.ts +1 -1
  27. package/dist/backend/link-plan.js +5 -1
  28. package/dist/backend/link-plan.js.map +1 -1
  29. package/dist/backend/llvm/classes.js +8 -7
  30. package/dist/backend/llvm/classes.js.map +1 -1
  31. package/dist/backend/llvm/common.d.ts +4 -0
  32. package/dist/backend/llvm/common.js +6 -0
  33. package/dist/backend/llvm/common.js.map +1 -1
  34. package/dist/backend/llvm/common.test.d.ts +1 -0
  35. package/dist/backend/llvm/common.test.js +9 -0
  36. package/dist/backend/llvm/common.test.js.map +1 -0
  37. package/dist/backend/llvm/dyn.d.ts +4 -0
  38. package/dist/backend/llvm/dyn.js +24 -13
  39. package/dist/backend/llvm/dyn.js.map +1 -1
  40. package/dist/backend/llvm/emitter.js +35 -7
  41. package/dist/backend/llvm/emitter.js.map +1 -1
  42. package/dist/backend/llvm/expr-context.d.ts +4 -0
  43. package/dist/backend/llvm/expr-dynamic.js +9 -0
  44. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  45. package/dist/backend/llvm/expr-primitives.js +8 -0
  46. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  47. package/dist/backend/llvm/expr-records.js +3 -3
  48. package/dist/backend/llvm/expr-records.js.map +1 -1
  49. package/dist/backend/llvm/expr-stream-bridges.js +2 -1
  50. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  51. package/dist/backend/llvm/expr-stream-callbacks.d.ts +3 -0
  52. package/dist/backend/llvm/expr-stream-callbacks.js +53 -0
  53. package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
  54. package/dist/backend/llvm/lib-dispatch.js +22 -0
  55. package/dist/backend/llvm/lib-dispatch.js.map +1 -1
  56. package/dist/backend/llvm/lib-process.js +50 -5
  57. package/dist/backend/llvm/lib-process.js.map +1 -1
  58. package/dist/backend/llvm/lib-shared.js +46 -0
  59. package/dist/backend/llvm/lib-shared.js.map +1 -1
  60. package/dist/backend/llvm/shapes.js +10 -8
  61. package/dist/backend/llvm/shapes.js.map +1 -1
  62. package/dist/backend/llvm/walkers.js +7 -5
  63. package/dist/backend/llvm/walkers.js.map +1 -1
  64. package/dist/backend/native-link-info.d.ts +2 -1
  65. package/dist/backend/native-link-info.js +4 -0
  66. package/dist/backend/native-link-info.js.map +1 -1
  67. package/dist/backend/native-link-info.test.js +24 -1
  68. package/dist/backend/native-link-info.test.js.map +1 -1
  69. package/dist/backend/native-toolchain.d.ts +8 -6
  70. package/dist/backend/native-toolchain.js +28 -13
  71. package/dist/backend/native-toolchain.js.map +1 -1
  72. package/dist/backend/native-toolchain.test.js +36 -1
  73. package/dist/backend/native-toolchain.test.js.map +1 -1
  74. package/dist/backend/runtime-pack-targets.test.js +1 -1
  75. package/dist/backend/runtime-pack-targets.test.js.map +1 -1
  76. package/dist/backend/runtime-pack.test.js +1 -0
  77. package/dist/backend/runtime-pack.test.js.map +1 -1
  78. package/dist/backend/targets.d.ts +5 -0
  79. package/dist/backend/targets.js +9 -0
  80. package/dist/backend/targets.js.map +1 -1
  81. package/dist/backend/targets.test.js +6 -1
  82. package/dist/backend/targets.test.js.map +1 -1
  83. package/dist/coverage/surface-manifest.js +1 -0
  84. package/dist/coverage/surface-manifest.js.map +1 -1
  85. package/dist/diagnostics/diagnostic.js +3 -3
  86. package/dist/diagnostics/diagnostic.js.map +1 -1
  87. package/dist/executable/executable-cache.d.ts +1 -0
  88. package/dist/executable/executable-cache.js +1 -0
  89. package/dist/executable/executable-cache.js.map +1 -1
  90. package/dist/executable/executable-cache.test.js +1 -0
  91. package/dist/executable/executable-cache.test.js.map +1 -1
  92. package/dist/frontend/lowering/containers/bytes.d.ts +0 -4
  93. package/dist/frontend/lowering/containers/bytes.js +112 -28
  94. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  95. package/dist/frontend/lowering/lower-assert.js +16 -10
  96. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  97. package/dist/frontend/lowering/lower-builtins.d.ts +22 -21
  98. package/dist/frontend/lowering/lower-builtins.js +392 -67
  99. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  100. package/dist/frontend/lowering/lower-calls.d.ts +8 -6
  101. package/dist/frontend/lowering/lower-calls.js +110 -12
  102. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  103. package/dist/frontend/lowering/lower-containers.js +12 -8
  104. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  105. package/dist/frontend/lowering/lower-exprs.js +104 -5
  106. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  107. package/dist/frontend/lowering/lower-inspect.js +7 -0
  108. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  109. package/dist/frontend/lowering/lower-modules.js +8 -0
  110. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  111. package/dist/frontend/lowering/lower-stmts.d.ts +5 -4
  112. package/dist/frontend/lowering/lower-stmts.js +23 -6
  113. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  114. package/dist/frontend/lowering/lowerer.d.ts +4 -4
  115. package/dist/frontend/lowering/lowerer.js +5 -5
  116. package/dist/frontend/lowering/lowerer.js.map +1 -1
  117. package/dist/frontend/lowering/surfaces.d.ts +5 -3
  118. package/dist/frontend/lowering/surfaces.js +33 -13
  119. package/dist/frontend/lowering/surfaces.js.map +1 -1
  120. package/dist/frontend/npm-static-declarations.js +27 -14
  121. package/dist/frontend/npm-static-declarations.js.map +1 -1
  122. package/dist/frontend/npm-static-declarations.test.js +10 -0
  123. package/dist/frontend/npm-static-declarations.test.js.map +1 -1
  124. package/dist/frontend/program.js +18 -2
  125. package/dist/frontend/program.js.map +1 -1
  126. package/dist/frontend/resolve.d.ts +22 -7
  127. package/dist/frontend/resolve.js +83 -22
  128. package/dist/frontend/resolve.js.map +1 -1
  129. package/dist/frontend/resolve.test.js +21 -1
  130. package/dist/frontend/resolve.test.js.map +1 -1
  131. package/dist/frontend/type-mapper.js +52 -10
  132. package/dist/frontend/type-mapper.js.map +1 -1
  133. package/dist/index.js +6 -0
  134. package/dist/index.js.map +1 -1
  135. package/dist/ir/analysis.js +1 -0
  136. package/dist/ir/analysis.js.map +1 -1
  137. package/dist/ir/ir.d.ts +67 -24
  138. package/dist/ir/ir.js +81 -3
  139. package/dist/ir/ir.js.map +1 -1
  140. package/dist/ir/serialize.d.ts +1 -1
  141. package/dist/ir/serialize.js +1 -1
  142. package/dist/ir/validate.js +77 -5
  143. package/dist/ir/validate.js.map +1 -1
  144. package/dist/library/int-infer.test.js +4 -4
  145. package/dist/library/library-cache.d.ts +1 -0
  146. package/dist/library/library-cache.js +1 -0
  147. package/dist/library/library-cache.js.map +1 -1
  148. package/dist/library/library-cache.test.js +18 -5
  149. package/dist/library/library-cache.test.js.map +1 -1
  150. package/package.json +17 -17
  151. package/surface-manifest.json +32 -17
@@ -24,7 +24,7 @@ import { CRYPTO_CIPHERS, CRYPTO_CONSTANTS, CRYPTO_CURVES, CRYPTO_HASHES } from "
24
24
  import { generatorMeta, timerStyleCallback } from "./lower-calls.js";
25
25
  import { registerHttpClientFnBinding, voidizedCallback } from "./lower-server.js";
26
26
  import { pairsSnapshotHelper } from "./pairs-snapshot.js";
27
- import { BOOL, BYTES_U8, CHILD_T, CHILDSTREAM_T, DYN, F64, FILEHANDLE_T, FSWATCHER_T, PROCSTREAM_T, JSVAL, NULL_T, SEARCH_PARAMS_T, SPAWNRES_T, STRING, UNDEFINED_T, VOID, arrayOf, canBoxFuncIntoDyn, canConvertToDyn, funcOf, isUnitType, typeEquals, typeKey } from "../../ir/ir.js";
27
+ import { BOOL, BYTES_U8, CHILD_T, CHILDSTREAM_T, CHILDWRITER_T, CRYPTOHASH_T, CRYPTOHMAC_T, DYN, F64, FILEHANDLE_T, FSWATCHER_T, PROCSTREAM_T, JSVAL, NULL_T, SEARCH_PARAMS_T, SPAWNRES_T, STRING, UNDEFINED_T, VOID, arrayOf, canBoxFuncIntoDyn, canConvertToDyn, funcOf, isUnitType, typeEquals, typeKey } from "../../ir/ir.js";
28
28
  import { boolLit, countedFor, numLit, strLit, varRef } from "../../ir/build.js";
29
29
  function optionalStringTags(lowerer, type) {
30
30
  if (type.kind !== "union")
@@ -2028,13 +2028,12 @@ export function lowerSpawnSyncCall(lowerer, expr, loc) {
2028
2028
  };
2029
2029
  }
2030
2030
  /** `spawn(command, args?, options)` → one cp.spawn / cp.spawnOpts
2031
- * libCall. The options argument must be an object LITERAL with an
2032
- * EXPLICIT stdio — "ignore" or "inherit" as the scalar, or the 3-tuple
2033
- * whose stdout/stderr slots may also be "pipe" (the child.stdout/
2034
- * child.stderr streams) or number fds; piped STDIN fences, and
2035
- * OMITTING the options means Node's default stdio, "pipe" on all
2036
- * three — fenced too, so a program never silently loses its child's
2037
- * output. The other lowered
2031
+ * libCall. The options argument, when present, must be an object
2032
+ * LITERAL. Stdio accepts "ignore", "inherit", or "pipe" as the scalar,
2033
+ * or the 3-tuple whose stdin pipe becomes child.stdin, stdout/stderr
2034
+ * pipes become child.stdout/child.stderr, and output slots may also be
2035
+ * number fds. Omitting stdio uses Node's default: all three piped. The
2036
+ * other lowered
2038
2037
  * members: `detached` (a boolean literal, inline or carried by the
2039
2038
  * conditional spread `...(c ? { detached: true } : {})` in either
2040
2039
  * orientation — POSIX_SPAWN_SETSID, the child gets its own session and
@@ -2044,11 +2043,28 @@ export function lowerSpawnSyncCall(lowerer, expr, loc) {
2044
2043
  * keeps its historical cp.spawn lowering. */
2045
2044
  export function lowerSpawnCall(lowerer, expr, loc) {
2046
2045
  if (expr.arguments.length > 3 || expr.arguments.some(ts.isSpreadElement)) {
2047
- lowerer.noLowering("spawn with this argument shape", expr, 'the supported form is spawn(command, args?, { stdio: "ignore" | "inherit", detached?, env?, cwd? })');
2046
+ lowerer.noLowering("spawn with this argument shape", expr, 'the supported form is spawn(command, args?, { stdio: "pipe" | "ignore" | "inherit", detached?, env?, cwd? })');
2048
2047
  }
2049
2048
  const cmd = lowerer.lowerExprExpecting(expr.arguments[0], STRING);
2050
- const argsNode = expr.arguments.length === 3 ? expr.arguments[1] : undefined;
2051
- const optsNode = expr.arguments[expr.arguments.length - 1];
2049
+ let argsNode;
2050
+ let optsNode;
2051
+ if (expr.arguments.length === 3) {
2052
+ argsNode = expr.arguments[1];
2053
+ optsNode = expr.arguments[2];
2054
+ }
2055
+ else if (expr.arguments.length === 2) {
2056
+ const second = expr.arguments[1];
2057
+ const secondType = lowerer.typeOf(second);
2058
+ const mapped = lowerer.mapTypeOf(secondType);
2059
+ if (lowerer.checker.isArrayType(secondType) || lowerer.checker.isTupleType(secondType) ||
2060
+ (mapped?.kind === "array" && mapped.elem.kind === "string"))
2061
+ argsNode = second;
2062
+ else
2063
+ optsNode = second;
2064
+ }
2065
+ if (optsNode !== undefined && !ts.isObjectLiteralExpression(optsNode)) {
2066
+ lowerer.noLowering("spawn with a non-literal options argument", optsNode, "pass stdio, detached, env, cwd, and windowsHide in an inline object literal");
2067
+ }
2052
2068
  const emptyStr = { kind: "strLit", value: "", type: STRING, loc };
2053
2069
  // Per-slot stdio modes (scr_child.c: 0 ignore, 1 inherit, 2 fd) and
2054
2070
  // the out/err fd expressions for mode 2 (the daemon-log idiom:
@@ -2062,8 +2078,7 @@ export function lowerSpawnCall(lowerer, expr, loc) {
2062
2078
  let envPairs = { kind: "arrayLit", elems: [], type: arrayOf(STRING), loc };
2063
2079
  let cwd = emptyStr;
2064
2080
  let plain = true; // exactly { stdio: "ignore" }: the historical libCall
2065
- const pipeFence = (node) => lowerer.noLowering('spawn with stdio: "pipe"', node, 'piped STDIN has no lowering — pipe stdout/stderr with the tuple form (stdio: ["ignore", "pipe", "pipe"]), or capture with spawnSync');
2066
- if (optsNode && ts.isObjectLiteralExpression(optsNode) && expr.arguments.length >= 2) {
2081
+ if (optsNode && ts.isObjectLiteralExpression(optsNode)) {
2067
2082
  for (const p of optsNode.properties) {
2068
2083
  // The conditional-spread idiom `...(isWindows ? {} : { detached:
2069
2084
  // true })` (either orientation): the one carried member supported
@@ -2099,18 +2114,15 @@ export function lowerSpawnCall(lowerer, expr, loc) {
2099
2114
  }
2100
2115
  switch (m.name) {
2101
2116
  case "stdio": {
2102
- // The 3-tuple form: stdin a "ignore"/"inherit" literal,
2103
- // stdout/stderr each a literal (including "pipe" — the
2104
- // child.stdout/stderr stream slots) OR a number-typed fd (an
2105
- // openSync result — dup2'd into the child, Node's fd slots).
2117
+ // The 3-tuple form: every slot accepts a literal, including
2118
+ // "pipe" (child.stdin/stdout/stderr); stdout/stderr also accept
2119
+ // a number-typed fd (an openSync result — dup2'd into the child).
2106
2120
  if (ts.isArrayLiteralExpression(m.value) && m.value.elements.length === 3) {
2107
2121
  const slot = (el, which) => {
2108
2122
  const t = lowerer.typeOf(el);
2109
2123
  if (t.isStringLiteralType()) {
2110
- if (t.value === "pipe" && which === 0)
2111
- pipeFence(el);
2112
2124
  if (t.value !== "ignore" && t.value !== "inherit" && t.value !== "pipe") {
2113
- lowerer.noLowering(`spawn with stdio "${t.value}"`, el, '"ignore", "inherit", "pipe" (stdout/stderr), and number fds are the supported stdio slots');
2125
+ lowerer.noLowering(`spawn with stdio "${t.value}"`, el, '"ignore", "inherit", "pipe", and number output fds are the supported stdio slots');
2114
2126
  }
2115
2127
  const mode = t.value === "inherit" ? 1 : t.value === "pipe" ? 3 : 0;
2116
2128
  if (which === 0)
@@ -2124,7 +2136,7 @@ export function lowerSpawnCall(lowerer, expr, loc) {
2124
2136
  return;
2125
2137
  }
2126
2138
  if (which === 0) {
2127
- lowerer.noLowering("spawn with this stdin slot", el, 'stdin takes "ignore" or "inherit" (fd stdin has no lowering)');
2139
+ lowerer.noLowering("spawn with this stdin slot", el, 'stdin takes "pipe", "ignore", or "inherit" (fd stdin has no lowering)');
2128
2140
  }
2129
2141
  if (lowerer.mapTypeOf(t)?.kind !== "f64") {
2130
2142
  lowerer.noLowering("spawn with this stdio option", el, 'each slot is "ignore", "inherit", or a number fd (an openSync result)');
@@ -2146,13 +2158,11 @@ export function lowerSpawnCall(lowerer, expr, loc) {
2146
2158
  }
2147
2159
  const t = lowerer.typeOf(m.value);
2148
2160
  const v = t.isStringLiteralType() ? t.value : null;
2149
- if (v === "pipe")
2150
- pipeFence(m.value);
2151
- if (v !== "ignore" && v !== "inherit") {
2152
- lowerer.noLowering("spawn with this stdio option", m.value, '"ignore" and "inherit" are the supported stdio literals ' +
2153
- '(or a 3-tuple of those and number fds; "pipe" has no lowering)');
2161
+ if (v !== "pipe" && v !== "ignore" && v !== "inherit") {
2162
+ lowerer.noLowering("spawn with this stdio option", m.value, '"pipe", "ignore", and "inherit" are the supported stdio literals ' +
2163
+ "(or a 3-tuple of those and number output fds)");
2154
2164
  }
2155
- const mode = v === "inherit" ? 1 : 0;
2165
+ const mode = v === "inherit" ? 1 : v === "pipe" ? 3 : 0;
2156
2166
  inMode = outMode = errMode = mode;
2157
2167
  sawStdio = true;
2158
2168
  if (v !== "ignore")
@@ -2190,7 +2200,8 @@ export function lowerSpawnCall(lowerer, expr, loc) {
2190
2200
  }
2191
2201
  }
2192
2202
  if (!sawStdio) {
2193
- lowerer.noLowering("spawn without { stdio: \"ignore\" }", expr, 'Node\'s default stdio is "pipe" (streams, no lowering) — pass { stdio: "ignore" } or { stdio: "inherit" } explicitly, or capture with spawnSync');
2203
+ inMode = outMode = errMode = 3;
2204
+ plain = false;
2194
2205
  }
2195
2206
  const argv = lowerer.lowerChildArgsArg(argsNode, loc);
2196
2207
  if (plain) {
@@ -3730,9 +3741,9 @@ export function fencedBuiltinImportOf(lowerer, ident) {
3730
3741
  /** The composed crypto pattern: `randomBytes(n).toString(enc)` lowers
3731
3742
  * as ONE string-producing libCall — the Buffer between the two calls
3732
3743
  * never exists at runtime. Only literal "hex"/"base64" encodings lower
3733
- * (the runtime implements exactly those); everything else — including a
3734
- * bare randomBytes(n) — fences with the Buffer story. Null when this
3735
- * isn't a toString on a crypto.randomBytes call. */
3744
+ * (the runtime implements exactly those); wider forms fall through to
3745
+ * the ordinary Buffer lowering. Null when this is not a toString on a
3746
+ * crypto.randomBytes call. */
3736
3747
  export function lowerCryptoComposedCall(lowerer, call, access) {
3737
3748
  if (call.questionDotToken || access.questionDotToken)
3738
3749
  return null;
@@ -3766,16 +3777,9 @@ export function lowerCryptoComposedCall(lowerer, call, access) {
3766
3777
  const enc = lowerer.lowerExprExpecting(encNode, STRING);
3767
3778
  return { kind: "libCall", fn: "crypto.randomBytesToString", args: [size, enc], type: STRING, loc };
3768
3779
  }
3769
- /** The composed hash chain — `createHash("sha256").update(data).digest("hex")`
3770
- * — fused into ONE libCall: the Hash handle never materializes (no Hash
3771
- * type exists in the value model), exactly the randomBytesToString
3772
- * stance. Both import spellings reach here (the named `createHash(...)`
3773
- * and the namespace `crypto.createHash(...)`). Once the chain is
3774
- * recognized, the narrow forms FENCE with pointed hints instead of
3775
- * falling to the generic member fence: sha256 is the lowered algorithm,
3776
- * one string- or Buffer-typed update, hex digests. Null when the callee
3777
- * isn't this chain at all (other Hash-typed code lands on the ordinary
3778
- * Hash.<member> fences). */
3780
+ /** Fast path for the exact single-update hash chain. Wider algorithms,
3781
+ * stored handles, multiple updates, Buffer digests, and input encodings
3782
+ * fall through to the first-class Hash lowering below. */
3779
3783
  function lowerHashDigestChain(lowerer, call, access) {
3780
3784
  const updateCall = access.expression;
3781
3785
  if (!ts.isCallExpression(updateCall) || updateCall.questionDotToken)
@@ -3799,16 +3803,14 @@ function lowerHashDigestChain(lowerer, call, access) {
3799
3803
  return null;
3800
3804
  const loc = locOf(call);
3801
3805
  const algT = chCall.arguments.length === 1 ? lowerer.typeOf(chCall.arguments[0]) : undefined;
3802
- if (!algT?.isStringLiteralType() || (algT.value !== "sha256" && algT.value !== "sha1")) {
3803
- lowerer.noLowering("createHash with this algorithm", chCall, 'sha256 and sha1 are the lowered algorithms: createHash("sha256") ' +
3804
- "(sha1 exists for the RFC 6455 Sec-WebSocket-Accept hash)");
3805
- }
3806
+ if (!algT?.isStringLiteralType() || (algT.value !== "sha256" && algT.value !== "sha1"))
3807
+ return null;
3806
3808
  if (updateCall.arguments.length !== 1) {
3807
- lowerer.noLowering(`Hash.update with ${updateCall.arguments.length} arguments`, updateCall, "one string or Buffer argument is the lowered update (input encodings have no lowering)");
3809
+ return null;
3808
3810
  }
3809
3811
  const encT = call.arguments.length === 1 ? lowerer.typeOf(call.arguments[0]) : undefined;
3810
3812
  if (!encT?.isStringLiteralType() || (encT.value !== "hex" && encT.value !== "base64")) {
3811
- lowerer.noLowering("Hash.digest with this encoding", call, 'hex and base64 are the lowered digests: .digest("hex") (the bare Buffer digest has no lowering)');
3813
+ return null;
3812
3814
  }
3813
3815
  // alg and enc are proven literals (fenced above), so lowering them
3814
3816
  // out of source position observes nothing; the data lowers between
@@ -3829,22 +3831,194 @@ function lowerHashDigestChain(lowerer, call, access) {
3829
3831
  const enc = lowerer.lowerExprExpecting(call.arguments[0], STRING);
3830
3832
  return { kind: "libCall", fn: "crypto.hashDigestStr", args: [alg, data, enc], type: STRING, loc };
3831
3833
  }
3832
- lowerer.noLowering(`Hash.update of '${dataIr ? lowerer.fmt(dataIr) : lowerer.checker.typeToString(lowerer.typeOf(dataNode))}' values`, dataNode, "string and Buffer/Uint8Array inputs are the lowered update forms");
3833
- }
3834
- /** The node:crypto introspection statics — build-time constants of the
3835
- * compiled runtime, baked at the call site (the http2.constants stance
3836
- * extended to calls): getFips() answers 0 (no FIPS provider can ever
3837
- * load into a compiled binary — Node's own answer for a non-FIPS
3838
- * build), and getCiphers()/getHashes()/getCurves() answer Node v24's
3839
- * name lists as fresh string[] literals. The lists are INTROSPECTION
3840
- * data (Node's contract is "names the provider recognizes"); the
3841
- * operations behind the names keep their per-member fences — a program
3842
- * that probes the list and then constructs a cipher fences at the
3843
- * construction site, never here. Null for other members (the dispatch
3844
- * keeps trying). */
3834
+ return null;
3835
+ }
3836
+ function cryptoEncoding(lowerer, node, use) {
3837
+ const type = lowerer.typeOf(node);
3838
+ if (!type.isStringLiteralType() || (type.value !== "hex" && type.value !== "base64")) {
3839
+ lowerer.noLowering(`${use} with this encoding`, node, 'the lowered output encodings are "hex" and "base64"');
3840
+ }
3841
+ return lowerer.lowerExprExpecting(node, STRING);
3842
+ }
3843
+ function cryptoAlgorithm(lowerer, node, use) {
3844
+ const type = lowerer.typeOf(node);
3845
+ if (type.isStringLiteralType() && !["md5", "sha1", "sha256"].includes(type.value.toLowerCase())) {
3846
+ lowerer.noLowering(`${use} with algorithm '${type.value}'`, node, "md5, sha1, and sha256 are the lowered digest algorithms");
3847
+ }
3848
+ return lowerer.lowerExprExpecting(node, STRING);
3849
+ }
3850
+ function cryptoInputBytes(lowerer, node, loc) {
3851
+ const value = lowerer.lowerExpr(node);
3852
+ if (value.type.kind === "bytes" && value.type.elem === "u8")
3853
+ return value;
3854
+ if (value.type.kind === "string") {
3855
+ return {
3856
+ kind: "libCall",
3857
+ fn: "buffer.fromStr",
3858
+ args: [value, { kind: "strLit", value: "utf8", type: STRING, loc }],
3859
+ type: BYTES_U8,
3860
+ loc,
3861
+ };
3862
+ }
3863
+ lowerer.noLowering(`crypto byte input of '${lowerer.fmt(value.type)}' values`, node, "string and Buffer/Uint8Array values are supported");
3864
+ }
3865
+ function cryptoBytesCallback(lowerer, node) {
3866
+ let callback = lowerer.lowerExpr(node);
3867
+ if (callback.type.kind === "dyn") {
3868
+ callback = { kind: "dynCheck", value: callback, type: funcOf([DYN, DYN], VOID), loc: locOf(node) };
3869
+ }
3870
+ if (callback.type.kind !== "func" || callback.type.params.length > 2) {
3871
+ lowerer.unsupported("SC1090", node, "crypto callbacks must accept at most (error, buffer)");
3872
+ }
3873
+ const error = callback.type.params[0];
3874
+ if (error !== undefined && error.kind !== "dyn") {
3875
+ if (error.kind !== "union") {
3876
+ lowerer.unsupported("SC1090", node, "crypto callback error parameters must be Error | null");
3877
+ }
3878
+ const def = lowerer.unions.get(error.unionId);
3879
+ const valid = !!def &&
3880
+ def.arms.some((arm) => arm.kind === "nullT") &&
3881
+ def.arms.some((arm) => arm.kind === "object" && arm.className === "%Error") &&
3882
+ def.arms.every((arm) => arm.kind === "nullT" || arm.kind === "undefinedT" || (arm.kind === "object" && arm.className === "%Error"));
3883
+ if (!valid)
3884
+ lowerer.unsupported("SC1090", node, "crypto callback error parameters must be Error | null");
3885
+ }
3886
+ const value = callback.type.params[1];
3887
+ if (value !== undefined && value.kind !== "dyn" && !(value.kind === "bytes" && value.elem === "u8")) {
3888
+ lowerer.unsupported("SC1090", node, "crypto callback result parameters must be Buffer/Uint8Array values");
3889
+ }
3890
+ return voidizedCallback(lowerer, callback, locOf(node));
3891
+ }
3892
+ /** The node:crypto utility and introspection statics. Hash/Hmac are real
3893
+ * native handles; one-shot hashing, constant-time equality, random-fill/
3894
+ * integer, and PBKDF2 share the same runtime primitives as the island.
3895
+ * The name lists remain build-time constants. */
3845
3896
  export function lowerCryptoModuleCall(lowerer, expr, bi, loc) {
3846
3897
  if (bi.module !== "crypto")
3847
3898
  return null;
3899
+ const args = expr.arguments;
3900
+ if (args.some(ts.isSpreadElement)) {
3901
+ lowerer.noLowering(`crypto.${bi.member} with spread arguments`, expr);
3902
+ }
3903
+ if (bi.member === "randomBytes") {
3904
+ if (args.length === 1)
3905
+ return null;
3906
+ if (args.length !== 2) {
3907
+ lowerer.noLowering(`crypto.randomBytes with ${args.length} arguments`, expr, "randomBytes(size) and randomBytes(size, callback) are supported");
3908
+ }
3909
+ return {
3910
+ kind: "libCall",
3911
+ fn: "crypto.randomBytesCb",
3912
+ args: [lowerer.lowerExprExpecting(args[0], F64), cryptoBytesCallback(lowerer, args[1])],
3913
+ type: VOID,
3914
+ loc,
3915
+ };
3916
+ }
3917
+ if (bi.member === "pbkdf2") {
3918
+ if (args.length !== 6) {
3919
+ lowerer.noLowering(`crypto.pbkdf2 with ${args.length} arguments`, expr, "pbkdf2(password, salt, iterations, keylen, digest, callback) is supported");
3920
+ }
3921
+ return {
3922
+ kind: "libCall",
3923
+ fn: "crypto.pbkdf2Cb",
3924
+ args: [
3925
+ cryptoInputBytes(lowerer, args[0], locOf(args[0])),
3926
+ cryptoInputBytes(lowerer, args[1], locOf(args[1])),
3927
+ lowerer.lowerExprExpecting(args[2], F64),
3928
+ lowerer.lowerExprExpecting(args[3], F64),
3929
+ cryptoAlgorithm(lowerer, args[4], "crypto.pbkdf2"),
3930
+ cryptoBytesCallback(lowerer, args[5]),
3931
+ ],
3932
+ type: VOID,
3933
+ loc,
3934
+ };
3935
+ }
3936
+ if (bi.member === "createHash") {
3937
+ if (args.length !== 1) {
3938
+ lowerer.noLowering(`crypto.createHash with ${args.length} arguments`, expr, "createHash(algorithm) is supported; options are not yet lowered");
3939
+ }
3940
+ return { kind: "libCall", fn: "crypto.hashNew", args: [cryptoAlgorithm(lowerer, args[0], "crypto.createHash")], type: CRYPTOHASH_T, loc };
3941
+ }
3942
+ if (bi.member === "createHmac") {
3943
+ if (args.length !== 2) {
3944
+ lowerer.noLowering(`crypto.createHmac with ${args.length} arguments`, expr, "createHmac(algorithm, stringOrBufferKey) is supported; options and KeyObject keys are not yet lowered");
3945
+ }
3946
+ const algorithm = cryptoAlgorithm(lowerer, args[0], "crypto.createHmac");
3947
+ const key = lowerer.lowerExpr(args[1]);
3948
+ if (key.type.kind === "string") {
3949
+ return { kind: "libCall", fn: "crypto.hmacNewStr", args: [algorithm, key], type: CRYPTOHMAC_T, loc };
3950
+ }
3951
+ if (key.type.kind === "bytes" && key.type.elem === "u8") {
3952
+ return { kind: "libCall", fn: "crypto.hmacNewBytes", args: [algorithm, key], type: CRYPTOHMAC_T, loc };
3953
+ }
3954
+ lowerer.noLowering(`crypto.createHmac with a '${lowerer.fmt(key.type)}' key`, args[1], "string and Buffer/Uint8Array keys are supported");
3955
+ }
3956
+ if (bi.member === "hash") {
3957
+ if (args.length < 2 || args.length > 3) {
3958
+ lowerer.noLowering(`crypto.hash with ${args.length} arguments`, expr, "hash(algorithm, stringOrBuffer[, \"hex\" | \"base64\"]) is supported");
3959
+ }
3960
+ const algorithm = cryptoAlgorithm(lowerer, args[0], "crypto.hash");
3961
+ const data = lowerer.lowerExpr(args[1]);
3962
+ const encoding = args[2]
3963
+ ? cryptoEncoding(lowerer, args[2], "crypto.hash")
3964
+ : { kind: "strLit", value: "hex", type: STRING, loc };
3965
+ if (data.type.kind === "string") {
3966
+ return { kind: "libCall", fn: "crypto.hashDigestStr", args: [algorithm, data, encoding], type: STRING, loc };
3967
+ }
3968
+ if (data.type.kind === "bytes" && data.type.elem === "u8") {
3969
+ return { kind: "libCall", fn: "crypto.hashDigestBytes", args: [algorithm, data, encoding], type: STRING, loc };
3970
+ }
3971
+ lowerer.noLowering(`crypto.hash of '${lowerer.fmt(data.type)}' data`, args[1], "string and Buffer/Uint8Array data are supported");
3972
+ }
3973
+ if (bi.member === "timingSafeEqual") {
3974
+ if (args.length !== 2)
3975
+ lowerer.noLowering(`crypto.timingSafeEqual with ${args.length} arguments`, expr);
3976
+ return {
3977
+ kind: "libCall",
3978
+ fn: "crypto.timingSafeEqual",
3979
+ args: [lowerer.lowerExprExpecting(args[0], BYTES_U8), lowerer.lowerExprExpecting(args[1], BYTES_U8)],
3980
+ type: BOOL,
3981
+ loc,
3982
+ };
3983
+ }
3984
+ if (bi.member === "randomFillSync") {
3985
+ if (args.length < 1 || args.length > 3) {
3986
+ lowerer.noLowering(`crypto.randomFillSync with ${args.length} arguments`, expr, "randomFillSync(buffer[, offset[, size]]) is supported");
3987
+ }
3988
+ const buffer = lowerer.lowerExprExpecting(args[0], BYTES_U8);
3989
+ const offset = args[1]
3990
+ ? lowerer.lowerExprExpecting(args[1], F64)
3991
+ : { kind: "numLit", value: 0, type: F64, loc };
3992
+ if (args[2]) {
3993
+ const size = lowerer.lowerExprExpecting(args[2], F64);
3994
+ return { kind: "libCall", fn: "crypto.randomFill", args: [buffer, offset, size], type: BYTES_U8, loc };
3995
+ }
3996
+ return { kind: "libCall", fn: "crypto.randomFillRest", args: [buffer, offset], type: BYTES_U8, loc };
3997
+ }
3998
+ if (bi.member === "randomInt") {
3999
+ if (args.length !== 1 && args.length !== 2) {
4000
+ lowerer.noLowering(`crypto.randomInt with ${args.length} arguments`, expr, "the synchronous randomInt(max) and randomInt(min, max) forms are supported");
4001
+ }
4002
+ const min = args.length === 1
4003
+ ? { kind: "numLit", value: 0, type: F64, loc }
4004
+ : lowerer.lowerExprExpecting(args[0], F64);
4005
+ const max = lowerer.lowerExprExpecting(args[args.length - 1], F64);
4006
+ return { kind: "libCall", fn: "crypto.randomInt", args: [min, max], type: F64, loc };
4007
+ }
4008
+ if (bi.member === "pbkdf2Sync") {
4009
+ if (args.length !== 5) {
4010
+ lowerer.noLowering(`crypto.pbkdf2Sync with ${args.length} arguments`, expr, "pbkdf2Sync(password, salt, iterations, keylen, digest) is supported");
4011
+ }
4012
+ const password = cryptoInputBytes(lowerer, args[0], locOf(args[0]));
4013
+ const salt = cryptoInputBytes(lowerer, args[1], locOf(args[1]));
4014
+ return {
4015
+ kind: "libCall",
4016
+ fn: "crypto.pbkdf2",
4017
+ args: [password, salt, lowerer.lowerExprExpecting(args[2], F64), lowerer.lowerExprExpecting(args[3], F64), cryptoAlgorithm(lowerer, args[4], "crypto.pbkdf2Sync")],
4018
+ type: BYTES_U8,
4019
+ loc,
4020
+ };
4021
+ }
3848
4022
  const LISTS = {
3849
4023
  getCiphers: CRYPTO_CIPHERS,
3850
4024
  getHashes: CRYPTO_HASHES,
@@ -4465,6 +4639,85 @@ export function lowerStatsMethodCall(lowerer, call, access) {
4465
4639
  }
4466
4640
  lowerer.noLowering(`Stats.${name}`, call, "isFile(), isDirectory(), isSymbolicLink(), size, blocks, nlink, atimeMs, and mtimeMs are the supported Stats members", lowerer.checker.getSymbolAtLocation(access.name));
4467
4641
  }
4642
+ /** Calls on native crypto.Hash/Hmac handles. The handle survives locals,
4643
+ * aliases, loops, and returns; update() retains and returns the receiver,
4644
+ * digest() finalizes it, and Hash.copy() snapshots the incremental state. */
4645
+ export function lowerCryptoHashMethodCall(lowerer, call, access) {
4646
+ if (call.questionDotToken || access.questionDotToken)
4647
+ return null;
4648
+ const receiverType = lowerer.mapTypeOf(lowerer.typeOf(access.expression));
4649
+ if (receiverType?.kind !== "cryptoHash" && receiverType?.kind !== "cryptoHmac")
4650
+ return null;
4651
+ if (!lowerer.isStdlibMember(access))
4652
+ return null;
4653
+ const name = access.name.text;
4654
+ const loc = locOf(call);
4655
+ const receiver = () => lowerer.lowerExprExpecting(access.expression, receiverType);
4656
+ if (name === "update") {
4657
+ if (call.arguments.length < 1 || call.arguments.length > 2 || call.arguments.some(ts.isSpreadElement)) {
4658
+ lowerer.noLowering(`${receiverType.kind === "cryptoHash" ? "Hash" : "Hmac"}.update with ${call.arguments.length} arguments`, call, "update(stringOrBuffer[, inputEncoding]) is supported");
4659
+ }
4660
+ const dataNode = call.arguments[0];
4661
+ const data = lowerer.lowerExpr(dataNode);
4662
+ const prefix = receiverType.kind === "cryptoHash" ? "crypto.hashUpdate" : "crypto.hmacUpdate";
4663
+ if (data.type.kind === "bytes" && data.type.elem === "u8") {
4664
+ if (call.arguments.length !== 1) {
4665
+ lowerer.noLowering("Hash/Hmac.update with an encoding for Buffer data", call.arguments[1], "input encodings apply only to string data");
4666
+ }
4667
+ return { kind: "libCall", fn: `${prefix}Bytes`, args: [receiver(), data], type: receiverType, loc };
4668
+ }
4669
+ if (data.type.kind === "string") {
4670
+ const encodingNode = call.arguments[1];
4671
+ if (encodingNode === undefined) {
4672
+ return { kind: "libCall", fn: `${prefix}Str`, args: [receiver(), data], type: receiverType, loc };
4673
+ }
4674
+ const encodingType = lowerer.typeOf(encodingNode);
4675
+ if (!encodingType.isStringLiteralType() || !["utf8", "utf-8", "hex", "base64"].includes(encodingType.value)) {
4676
+ lowerer.noLowering("Hash/Hmac.update with this input encoding", encodingNode, 'utf8, hex, and base64 string inputs are supported');
4677
+ }
4678
+ if (encodingType.value === "utf8" || encodingType.value === "utf-8") {
4679
+ lowerer.lowerExprExpecting(encodingNode, STRING);
4680
+ return { kind: "libCall", fn: `${prefix}Str`, args: [receiver(), data], type: receiverType, loc };
4681
+ }
4682
+ const encoded = {
4683
+ kind: "libCall",
4684
+ fn: "buffer.fromStr",
4685
+ args: [data, lowerer.lowerExprExpecting(encodingNode, STRING)],
4686
+ type: BYTES_U8,
4687
+ loc,
4688
+ };
4689
+ return { kind: "libCall", fn: `${prefix}Bytes`, args: [receiver(), encoded], type: receiverType, loc };
4690
+ }
4691
+ lowerer.noLowering(`Hash/Hmac.update of '${lowerer.fmt(data.type)}' values`, dataNode, "string and Buffer/Uint8Array inputs are supported");
4692
+ }
4693
+ if (name === "digest") {
4694
+ if (call.arguments.length > 1 || call.arguments.some(ts.isSpreadElement)) {
4695
+ lowerer.noLowering(`${receiverType.kind === "cryptoHash" ? "Hash" : "Hmac"}.digest with ${call.arguments.length} arguments`, call, 'digest() and digest("hex" | "base64") are supported');
4696
+ }
4697
+ if (call.arguments.length === 0) {
4698
+ return {
4699
+ kind: "libCall",
4700
+ fn: receiverType.kind === "cryptoHash" ? "crypto.hashDigestBuffer" : "crypto.hmacDigestBuffer",
4701
+ args: [receiver()],
4702
+ type: BYTES_U8,
4703
+ loc,
4704
+ };
4705
+ }
4706
+ return {
4707
+ kind: "libCall",
4708
+ fn: receiverType.kind === "cryptoHash" ? "crypto.hashDigestString" : "crypto.hmacDigestString",
4709
+ args: [receiver(), cryptoEncoding(lowerer, call.arguments[0], `${receiverType.kind === "cryptoHash" ? "Hash" : "Hmac"}.digest`)],
4710
+ type: STRING,
4711
+ loc,
4712
+ };
4713
+ }
4714
+ if (name === "copy" && receiverType.kind === "cryptoHash") {
4715
+ if (call.arguments.length !== 0)
4716
+ lowerer.noLowering(`Hash.copy with ${call.arguments.length} arguments`, call, "copy() without options is supported");
4717
+ return { kind: "libCall", fn: "crypto.hashCopy", args: [receiver()], type: CRYPTOHASH_T, loc };
4718
+ }
4719
+ lowerer.noLowering(`${receiverType.kind === "cryptoHash" ? "Hash" : "Hmac"}.${name}`, call, receiverType.kind === "cryptoHash" ? "update(), digest(), and copy() are supported" : "update() and digest() are supported", lowerer.checker.getSymbolAtLocation(access.name));
4720
+ }
4468
4721
  /** `Atomics.wait(int32Array, idx, expected, timeoutMs)` — the
4469
4722
  * synchronous-sleep idiom (RouteStore's
4470
4723
  * `Atomics.wait(sleepBuffer, 0, 0, ms)`): scriptc has no threads, so
@@ -4520,7 +4773,7 @@ export function lowerBuiltinExtraProperty(lowerer, expr) {
4520
4773
  return { kind: "recordGet", obj: receiver, shapeId: receiver.type.shapeId, field: "%enc", type: STRING, loc: locOf(expr) };
4521
4774
  }
4522
4775
  const kind = lowerer.mapTypeOf(lowerer.typeOf(expr.expression))?.kind;
4523
- if (kind !== "stats" && kind !== "fileHandle" && kind !== "spawnRes" && kind !== "child")
4776
+ if (kind !== "stats" && kind !== "fileHandle" && kind !== "spawnRes" && kind !== "child" && kind !== "childWriter")
4524
4777
  return null;
4525
4778
  if (kind === "child" ? !isChildSurfaceMember(lowerer, expr) : !lowerer.isStdlibMember(expr))
4526
4779
  return null;
@@ -4537,19 +4790,20 @@ export function lowerBuiltinExtraProperty(lowerer, expr) {
4537
4790
  }
4538
4791
  lowerer.noLowering(`FileHandle.${name}`, expr, "fd, close(), read(), write(), readFile(), writeFile(), appendFile(), and stat() are the supported FileHandle members", lowerer.checker.getSymbolAtLocation(expr.name));
4539
4792
  }
4540
- // child.stdout / child.stderr — the piped-output streams: the
4541
- // checker's `Readable | null` (null exactly when the slot was not
4542
- // piped), constructed type-directedly in the backend over the
4793
+ // child.stdin/stdout/stderr — the piped streams: the checker's
4794
+ // `Writable | null` / `Readable | null` (null exactly when the slot
4795
+ // was not piped), constructed type-directedly in the backend over the
4543
4796
  // +1-or-NULL runtime pair.
4544
- if (kind === "child" && (name === "stdout" || name === "stderr")) {
4797
+ if (kind === "child" && (name === "stdin" || name === "stdout" || name === "stderr")) {
4545
4798
  const receiver = lowerer.lowerExpr(expr.expression);
4799
+ const streamType = name === "stdin" ? CHILDWRITER_T : CHILDSTREAM_T;
4546
4800
  const type = {
4547
4801
  kind: "union",
4548
- unionId: lowerer.unions.intern([CHILDSTREAM_T, { kind: "nullT" }]),
4802
+ unionId: lowerer.unions.intern([streamType, { kind: "nullT" }]),
4549
4803
  };
4550
4804
  const read = {
4551
4805
  kind: "libCall",
4552
- fn: name === "stdout" ? "child.stdout" : "child.stderr",
4806
+ fn: name === "stdin" ? "child.stdin" : name === "stdout" ? "child.stdout" : "child.stderr",
4553
4807
  args: [receiver],
4554
4808
  type,
4555
4809
  loc,
@@ -4558,6 +4812,16 @@ export function lowerBuiltinExtraProperty(lowerer, expr) {
4558
4812
  }
4559
4813
  if (kind === "child")
4560
4814
  return null; // pid/exitCode/killed live in lowerIntrinsicProperty
4815
+ if (kind === "childWriter") {
4816
+ if (name === "writable") {
4817
+ const receiver = lowerer.lowerExprExpecting(expr.expression, CHILDWRITER_T);
4818
+ return { kind: "libCall", fn: "writer.writable", args: [receiver], type: BOOL, loc };
4819
+ }
4820
+ if (name === "write" || name === "end" || name === "destroy" || name === "on" || name === "once") {
4821
+ lowerer.unsupported("SC1090", expr, `child stdin methods as values (call '${name}' directly)`);
4822
+ }
4823
+ lowerer.noLowering(`Writable.${name}`, expr, 'write(string | Uint8Array), end(), destroy(), writable, and on/once("drain" | "finish" | "error", cb) are supported', lowerer.checker.getSymbolAtLocation(expr.name));
4824
+ }
4561
4825
  if (kind === "stats" &&
4562
4826
  (name === "blocks" || name === "nlink" || name === "atimeMs" || name === "mtimeMs")) {
4563
4827
  const receiver = lowerer.lowerExpr(expr.expression);
@@ -4741,6 +5005,67 @@ export function lowerChildStreamMethodCall(lowerer, call, access) {
4741
5005
  }
4742
5006
  lowerer.noLowering(`ReadableStream.${name}`, call, 'on/once("data" | "end", cb) are the supported child-stream members', lowerer.checker.getSymbolAtLocation(access.name));
4743
5007
  }
5008
+ /** Method calls on a piped child stdin writer. Writes copy one string or
5009
+ * Uint8Array into the nonblocking runtime queue and return the backpressure
5010
+ * signal. end()/destroy() are statement-only, as are drain/finish/error
5011
+ * listener registrations; unsupported callbacks/encodings stay fenced. */
5012
+ export function lowerChildWriterMethodCall(lowerer, call, access) {
5013
+ if (lowerer.chainBlocked(call, access))
5014
+ return null;
5015
+ if (lowerer.mapTypeOf(lowerer.typeOf(access.expression))?.kind !== "childWriter")
5016
+ return null;
5017
+ if (!lowerer.isStdlibMember(access))
5018
+ return null;
5019
+ const name = access.name.text;
5020
+ const loc = locOf(call);
5021
+ if (name === "write") {
5022
+ if (call.arguments.length !== 1 || call.arguments.some(ts.isSpreadElement)) {
5023
+ lowerer.noLowering(`child stdin write with ${call.arguments.length} arguments`, call, "write(string | Uint8Array) with no encoding or callback is supported");
5024
+ }
5025
+ const receiver = lowerer.lowerExprExpecting(access.expression, CHILDWRITER_T);
5026
+ const data = lowerer.lowerExpr(call.arguments[0]);
5027
+ if (data.type.kind === "string") {
5028
+ return { kind: "libCall", fn: "writer.writeString", args: [receiver, data], type: BOOL, loc };
5029
+ }
5030
+ if (data.type.kind === "bytes" && data.type.elem === "u8") {
5031
+ return { kind: "libCall", fn: "writer.writeBytes", args: [receiver, data], type: BOOL, loc };
5032
+ }
5033
+ lowerer.noLowering(`child stdin write of '${lowerer.fmt(data.type)}'`, call.arguments[0], "write one string, Buffer, or Uint8Array value (narrow unions first)");
5034
+ }
5035
+ if ((name === "end" || name === "destroy") && call.arguments.length === 0) {
5036
+ if (!ts.isExpressionStatement(call.parent)) {
5037
+ lowerer.unsupported("SC1090", call, `chaining child.stdin.${name}() (call it as its own statement)`);
5038
+ }
5039
+ const receiver = lowerer.lowerExprExpecting(access.expression, CHILDWRITER_T);
5040
+ return { kind: "libCall", fn: name === "end" ? "writer.end" : "writer.destroy", args: [receiver], type: VOID, loc };
5041
+ }
5042
+ if ((name === "on" || name === "once") && call.arguments.length === 2) {
5043
+ const eventType = lowerer.typeOf(call.arguments[0]);
5044
+ const event = eventType.isStringLiteralType() ? eventType.value : null;
5045
+ if (event !== "drain" && event !== "finish" && event !== "error") {
5046
+ lowerer.noLowering(`child stdin ${name}(${event === null ? "non-literal event" : `"${event}"`}, ...)`, call.arguments[0], '"drain", "finish", and "error" are the supported child stdin events');
5047
+ }
5048
+ if (!ts.isExpressionStatement(call.parent)) {
5049
+ lowerer.unsupported("SC1090", call, "chaining child stdin listener registration");
5050
+ }
5051
+ const receiver = lowerer.lowerExprExpecting(access.expression, CHILDWRITER_T);
5052
+ const cb = lowerer.lowerExpr(call.arguments[1]);
5053
+ const maxParams = event === "error" ? 1 : 0;
5054
+ if (cb.type.kind !== "func" || cb.type.ret.kind !== "void" || cb.type.params.length > maxParams) {
5055
+ lowerer.unsupported("SC1090", call.arguments[1], event === "error"
5056
+ ? "error listeners with at most one Error parameter and no return value"
5057
+ : `${event} listeners with no parameters or return value`);
5058
+ }
5059
+ if (event === "error" && cb.type.params[0] !== undefined &&
5060
+ !(cb.type.params[0].kind === "object" && cb.type.params[0].className === "%Error")) {
5061
+ lowerer.unsupported("SC1090", call.arguments[1], "child stdin error listeners whose parameter is Error");
5062
+ }
5063
+ const once = boolLit(name === "once", loc);
5064
+ const fn = event === "drain" ? "writer.onDrain" : event === "finish" ? "writer.onFinish" : "writer.onError";
5065
+ return { kind: "libCall", fn, args: [receiver, cb, once], type: VOID, loc };
5066
+ }
5067
+ lowerer.noLowering(`Writable.${name}`, call, 'write(string | Uint8Array), end(), destroy(), and on/once("drain" | "finish" | "error", cb) are supported', lowerer.checker.getSymbolAtLocation(access.name));
5068
+ }
4744
5069
  /** Method calls on first-class process-stream receivers (procStream —
4745
5070
  * a WritableStream-typed value like prefixStream's `output` param):
4746
5071
  * write(data) with one string, dispatched at runtime onto the exact