@scriptc/compiler 0.0.10 → 0.0.12

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 (111) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +63 -15
  2. package/ambient/scriptc.d.ts +2 -2
  3. package/dist/backend/cc.d.ts +2 -2
  4. package/dist/backend/emission/emit-async.js +1 -1
  5. package/dist/backend/emission/emit-exprs.js +131 -54
  6. package/dist/backend/emission/emit-exprs.js.map +1 -1
  7. package/dist/backend/emission/emit-island.js +1 -1
  8. package/dist/backend/emission/emit-shapes.js +1 -1
  9. package/dist/backend/emission/emit-walkers.d.ts +14 -14
  10. package/dist/backend/emission/emit-walkers.js +44 -44
  11. package/dist/backend/emission/emit-walkers.js.map +1 -1
  12. package/dist/backend/emission/emitter.d.ts +3 -3
  13. package/dist/backend/emission/emitter.js +33 -9
  14. package/dist/backend/emission/emitter.js.map +1 -1
  15. package/dist/backend/emission/may-throw.js +1 -1
  16. package/dist/backend/llvm/dyn.d.ts +10 -10
  17. package/dist/backend/llvm/dyn.js +26 -26
  18. package/dist/backend/llvm/dyn.js.map +1 -1
  19. package/dist/backend/llvm/emitter.js +156 -47
  20. package/dist/backend/llvm/emitter.js.map +1 -1
  21. package/dist/backend/llvm/shapes.js +1 -1
  22. package/dist/backend/llvm/walkers.js +2 -2
  23. package/dist/backend/llvm/walkers.js.map +1 -1
  24. package/dist/coverage/surface-manifest.js +16 -2
  25. package/dist/coverage/surface-manifest.js.map +1 -1
  26. package/dist/diagnostics/diagnostic.d.ts +21 -8
  27. package/dist/diagnostics/diagnostic.js +48 -14
  28. package/dist/diagnostics/diagnostic.js.map +1 -1
  29. package/dist/frontend/lowering/lib-boundary.js +5 -5
  30. package/dist/frontend/lowering/lib-boundary.js.map +1 -1
  31. package/dist/frontend/lowering/lower-assert.js +12 -12
  32. package/dist/frontend/lowering/lower-assert.js.map +1 -1
  33. package/dist/frontend/lowering/lower-builtins.d.ts +63 -7
  34. package/dist/frontend/lowering/lower-builtins.js +327 -35
  35. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  36. package/dist/frontend/lowering/lower-calls.d.ts +4 -4
  37. package/dist/frontend/lowering/lower-calls.js +104 -70
  38. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  39. package/dist/frontend/lowering/lower-classes.d.ts +7 -0
  40. package/dist/frontend/lowering/lower-classes.js +82 -6
  41. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  42. package/dist/frontend/lowering/lower-containers.d.ts +9 -5
  43. package/dist/frontend/lowering/lower-containers.js +88 -26
  44. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  45. package/dist/frontend/lowering/lower-emitter.d.ts +70 -3
  46. package/dist/frontend/lowering/lower-emitter.js +438 -31
  47. package/dist/frontend/lowering/lower-emitter.js.map +1 -1
  48. package/dist/frontend/lowering/lower-exprs.d.ts +87 -12
  49. package/dist/frontend/lowering/lower-exprs.js +630 -125
  50. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  51. package/dist/frontend/lowering/lower-inspect.js +5 -5
  52. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  53. package/dist/frontend/lowering/lower-island.js +12 -3
  54. package/dist/frontend/lowering/lower-island.js.map +1 -1
  55. package/dist/frontend/lowering/lower-modules.d.ts +8 -5
  56. package/dist/frontend/lowering/lower-modules.js +157 -63
  57. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  58. package/dist/frontend/lowering/lower-namespaces.d.ts +24 -0
  59. package/dist/frontend/lowering/lower-namespaces.js +158 -16
  60. package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
  61. package/dist/frontend/lowering/lower-server.js +11 -11
  62. package/dist/frontend/lowering/lower-server.js.map +1 -1
  63. package/dist/frontend/lowering/lower-stmts.d.ts +1 -1
  64. package/dist/frontend/lowering/lower-stmts.js +271 -36
  65. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  66. package/dist/frontend/lowering/lower-stream.js +171 -20
  67. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  68. package/dist/frontend/lowering/lowerer.d.ts +27 -10
  69. package/dist/frontend/lowering/lowerer.js +156 -54
  70. package/dist/frontend/lowering/lowerer.js.map +1 -1
  71. package/dist/frontend/lowering/surfaces.d.ts +44 -0
  72. package/dist/frontend/lowering/surfaces.js +236 -5
  73. package/dist/frontend/lowering/surfaces.js.map +1 -1
  74. package/dist/frontend/npm.d.ts +12 -0
  75. package/dist/frontend/npm.js +30 -0
  76. package/dist/frontend/npm.js.map +1 -1
  77. package/dist/frontend/program.d.ts +25 -0
  78. package/dist/frontend/program.js +145 -136
  79. package/dist/frontend/program.js.map +1 -1
  80. package/dist/frontend/shared.d.ts +1 -1
  81. package/dist/frontend/shared.js +8 -8
  82. package/dist/frontend/shared.js.map +1 -1
  83. package/dist/frontend/types.d.ts +2 -2
  84. package/dist/frontend/types.js +15 -15
  85. package/dist/frontend/types.js.map +1 -1
  86. package/dist/index.d.ts +2 -1
  87. package/dist/index.js +169 -5
  88. package/dist/index.js.map +1 -1
  89. package/dist/ir/nodes.d.ts +174 -106
  90. package/dist/ir/nodes.js +69 -24
  91. package/dist/ir/nodes.js.map +1 -1
  92. package/dist/ir/validate.js +55 -36
  93. package/dist/ir/validate.js.map +1 -1
  94. package/dist/library/fence-eval.js +12 -2
  95. package/dist/library/fence-eval.js.map +1 -1
  96. package/dist/library/int-infer.d.ts +119 -0
  97. package/dist/library/int-infer.js +1314 -0
  98. package/dist/library/int-infer.js.map +1 -0
  99. package/dist/library/int-infer.test.d.ts +1 -0
  100. package/dist/library/int-infer.test.js +279 -0
  101. package/dist/library/int-infer.test.js.map +1 -0
  102. package/dist/library/profile.d.ts +34 -5
  103. package/dist/library/profile.js +77 -13
  104. package/dist/library/profile.js.map +1 -1
  105. package/dist/library/sidecar-validate.js +1 -1
  106. package/dist/library/sidecar-validate.js.map +1 -1
  107. package/dist/library/sidecar.d.ts +24 -1
  108. package/dist/library/sidecar.js +163 -16
  109. package/dist/library/sidecar.js.map +1 -1
  110. package/package.json +2 -2
  111. package/surface-manifest.json +210 -12
@@ -31,7 +31,7 @@ import { dynFallbackType, nodeThrowExpr } from "./lowerer.js";
31
31
  import { isJsSourceFile, locOf } from "../program.js";
32
32
  import { newFnCtx, own } from "./lowerer.js";
33
33
  import { appendImplicitUndefinedReturn } from "./lower-calls.js";
34
- import { bufEncoding } from "./lower-containers.js";
34
+ import { bufEncoding, knownBufEncoding } from "./lower-containers.js";
35
35
  import { probeLower } from "./lower-exprs.js";
36
36
  import { BOOL, DYN, F64, RUNTIME_STREAM_CLASSES, STRING, VOID, arrayOf, bytesOf, canBoxFuncIntoDyn, funcOf, typeEquals, typeKey } from "../../ir/nodes.js";
37
37
  const BYTES = bytesOf("u8");
@@ -337,7 +337,7 @@ function followOptionsArg(L, arg) {
337
337
  * values as they are), then dynCheck'd into the all-dyn full Node tuple.
338
338
  * The emitted option thunk recognizes the THISLESS shape (its first
339
339
  * param is not the stream) and boxes each position by kind — the chunk
340
- * as DOM bytes, the error via the boundary's encoding, the completion
340
+ * as dyn bytes, the error via the boundary's encoding, the completion
341
341
  * callback as a callable dyn whose glue reports to the runtime's *_done.
342
342
  * DIVERGENCE (SEMANTICS.md): Node binds `this` to the stream when it
343
343
  * calls option callbacks; a value adapted through the dyn boundary is
@@ -412,6 +412,8 @@ fallbackCb) {
412
412
  readableSide: true,
413
413
  writableSide: true,
414
414
  encoding: null,
415
+ badEncoding: null,
416
+ pushEncoding: null,
415
417
  flags: 0,
416
418
  cbs: [],
417
419
  };
@@ -517,19 +519,41 @@ fallbackCb) {
517
519
  }
518
520
  case "encoding": {
519
521
  // The readable-side string-chunk mode: a literal encoding, folded
520
- // to its canonical name (Writable has no such option).
522
+ // to its canonical name (Writable has no such option). A literal
523
+ // spelling Node does not know is Node's RUNTIME rejection —
524
+ // ERR_UNKNOWN_ENCODING at construction, not a compile fence.
521
525
  if (ctorName === "Writable")
522
526
  L.noLowering(`the Writable option 'encoding'`, prop);
527
+ {
528
+ const t = L.typeOf(value);
529
+ if (t.isStringLiteralType() && knownBufEncoding(t.value) === undefined) {
530
+ out.badEncoding = t.value;
531
+ break;
532
+ }
533
+ }
523
534
  out.encoding = bufEncoding(L, `the ${ctorName} option 'encoding'`, value);
524
535
  break;
525
536
  }
526
537
  case "defaultEncoding":
527
- // The WRITE-side default: utf8 is the byte default — admit the
528
- // literal; other encodings would change what write(string) means.
538
+ // utf8 is the byte default — admit the literal. A literal Node
539
+ // does not know throws ERR_UNKNOWN_ENCODING at construction (the
540
+ // state ctor validates it), Node's ladder. On a pure Readable a
541
+ // valid non-utf8 name governs how push(string) DECODES chunks
542
+ // (Buffer.from(chunk, enc)) — lowered as a follow-up
543
+ // readable.pushEncoding. The write-side meaning (what
544
+ // write(string) does) stays fenced on the writable classes.
529
545
  {
530
546
  const t = L.typeOf(value);
531
547
  if (t.isStringLiteralType() && (t.value === "utf8" || t.value === "utf-8"))
532
548
  break;
549
+ if (t.isStringLiteralType() && knownBufEncoding(t.value) === undefined) {
550
+ out.badEncoding = t.value;
551
+ break;
552
+ }
553
+ if (t.isStringLiteralType() && ctorName === "Readable") {
554
+ out.pushEncoding = knownBufEncoding(t.value);
555
+ break;
556
+ }
533
557
  }
534
558
  L.noLowering(`the ${ctorName} option 'defaultEncoding'`, prop, "utf8 (the default) is the supported write-side string encoding");
535
559
  break;
@@ -602,7 +626,7 @@ function streamCtorArgs(L, cls, arg, thisType, loc, fallbackCb) {
602
626
  ? [o.hwmR, o.hwmW, o.autoDestroy, o.emitClose, o.allowHalfOpen,
603
627
  boolLit(o.readableSide, loc), boolLit(o.writableSide, loc), f64Lit(o.flags, loc)]
604
628
  : [cls === "%Writable" ? o.hwmW : o.hwmR, o.autoDestroy, o.emitClose, f64Lit(o.flags, loc)];
605
- return { args: [...head, ...o.cbs], encoding: o.encoding };
629
+ return { args: [...head, ...o.cbs], encoding: o.encoding, badEncoding: o.badEncoding, pushEncoding: o.pushEncoding };
606
630
  }
607
631
  /** `new Readable(opts?)` and friends — called from lowerNew once the
608
632
  * callee resolved to a builtin stream class. */
@@ -624,7 +648,17 @@ export function lowerStreamNew(L, expr, info) {
624
648
  }
625
649
  const fn = `${streamCtorShape(cls).fn}.new`;
626
650
  const parsed = streamCtorArgs(L, cls, args[0], thisType, loc);
627
- const built = { kind: "libCall", fn, args: parsed.args, type: thisType, loc };
651
+ if (parsed.badEncoding !== null) {
652
+ // Node's state constructors validate encodings first: an unknown
653
+ // spelling never constructs — the whole expression throws.
654
+ return nodeThrowExpr(1, "ERR_UNKNOWN_ENCODING", `Unknown encoding: ${parsed.badEncoding}`, thisType, loc);
655
+ }
656
+ let built = { kind: "libCall", fn, args: parsed.args, type: thisType, loc };
657
+ if (parsed.pushEncoding !== null) {
658
+ // { defaultEncoding }: chain the push-side decode default (the
659
+ // setEncoding chaining shape — answers the receiver +1).
660
+ built = { kind: "libCall", fn: "readable.pushEncoding", args: [built, strLit(parsed.pushEncoding, loc)], type: thisType, loc };
661
+ }
628
662
  if (parsed.encoding === null)
629
663
  return built;
630
664
  // { encoding }: chain a setEncoding over the fresh stream (the operand
@@ -781,11 +815,27 @@ export function lowerStreamSuperCall(L, info, base, argNodes, thisLocal, loc, bl
781
815
  }
782
816
  }
783
817
  const parsed = streamCtorArgs(L, cls, argNodes[0], thisType, loc, fallback);
818
+ if (parsed.badEncoding !== null) {
819
+ // super(options) with an unknown literal encoding: the state ctor
820
+ // rejects before any initialization, Node's ladder.
821
+ return [{
822
+ kind: "exprStmt",
823
+ expr: nodeThrowExpr(1, "ERR_UNKNOWN_ENCODING", `Unknown encoding: ${parsed.badEncoding}`, VOID, loc),
824
+ loc,
825
+ }];
826
+ }
784
827
  const out = [{
785
828
  kind: "exprStmt",
786
829
  expr: { kind: "libCall", fn: `${shape.fn}.init`, args: [thisRef(), ...parsed.args], type: VOID, loc },
787
830
  loc,
788
831
  }];
832
+ if (parsed.pushEncoding !== null) {
833
+ out.push({
834
+ kind: "exprStmt",
835
+ expr: { kind: "libCall", fn: "readable.pushEncoding", args: [thisRef(), strLit(parsed.pushEncoding, loc)], type: thisType, loc },
836
+ loc,
837
+ });
838
+ }
789
839
  if (parsed.encoding !== null) {
790
840
  out.push({
791
841
  kind: "exprStmt",
@@ -969,32 +1019,57 @@ export function streamClassAliasDecl(L, nameNode, init) {
969
1019
  return L.builtinNamespaceModuleOf(init.expression) === "stream";
970
1020
  }
971
1021
  /* ── the module functions (finished / pipeline — the callback forms) ──── */
1022
+ /** Node's pipeline stage ladder text (lib/internal/streams/pipeline.js's
1023
+ * makeAsyncIterable — every stage position renders the same "body"
1024
+ * clause). finished() keeps its narrower isNodeStream wording. */
1025
+ const PIPELINE_STAGE_EXPECTED = "of type function or an instance of Blob, ReadableStream, WritableStream, Stream, Iterable, AsyncIterable, or Promise or { readable, writable } pair";
972
1026
  /** A stream-rooted argument, lowered — or the pointed fence (pipeline's
973
- * iterable/generator/function stages have no lowering yet). */
1027
+ * iterable/generator/function stages have no lowering yet). `what` picks
1028
+ * the validation ladder: finished() rejects EVERY non-stream through
1029
+ * isNodeStream, while pipeline() accepts iterables (strings, arrays) and
1030
+ * { readable, writable } pairs — those shapes fence instead of throwing
1031
+ * a ladder Node never throws. */
974
1032
  function lowerStreamArg(L, node, what) {
975
1033
  const v = L.lowerExpr(node);
976
1034
  const info = v.type.kind === "object" ? L.classes.get(v.type.className) : undefined;
977
1035
  if (!streamSidesOf(L, info)) {
978
1036
  // A provably-non-stream value in a JS source (the invalid-input
979
- // probes: finished({}, cb)): Node's isNodeStream gate throws
980
- // ERR_INVALID_ARG_TYPE before any watcher exists.
981
- if (isJsSourceFile(node.getSourceFile()) &&
982
- (v.type.kind === "record" || v.type.kind === "string" || v.type.kind === "f64" ||
983
- v.type.kind === "bool" || v.type.kind === "array") &&
984
- L.dynConvertible(v.type)) {
1037
+ // probes: finished({}, cb), pipeline(42, dst, cb)): Node's gate
1038
+ // throws ERR_INVALID_ARG_TYPE before any watcher exists.
1039
+ const argTypeThrow = (argName, expected) => {
985
1040
  throw new StreamArgTypeThrow({
986
1041
  kind: "libCall",
987
1042
  fn: "error.argTypeThrow",
988
1043
  args: [
989
- { kind: "strLit", value: "stream", type: STRING, loc: locOf(node) },
990
- { kind: "strLit", value: "an instance of ReadableStream, WritableStream, or Stream", type: STRING, loc: locOf(node) },
1044
+ { kind: "strLit", value: argName, type: STRING, loc: locOf(node) },
1045
+ { kind: "strLit", value: expected, type: STRING, loc: locOf(node) },
991
1046
  { kind: "dynFrom", value: v, type: DYN, loc: locOf(node) },
992
1047
  ],
993
1048
  type: VOID,
994
1049
  loc: locOf(node),
995
1050
  });
1051
+ };
1052
+ if (isJsSourceFile(node.getSourceFile()) && L.dynConvertible(v.type)) {
1053
+ if (what === "pipeline") {
1054
+ // Strings and arrays are iterables — VALID pipeline stages in
1055
+ // Node — and a record carrying a readable/writable member may be
1056
+ // the duplex-pair form; none of those shapes has a lowering, so
1057
+ // they take the pointed fence below, never a throw.
1058
+ const pairish = v.type.kind === "record" &&
1059
+ (L.shapes.get(v.type.shapeId)?.fields ?? []).some((f) => f.name === "readable" || f.name === "writable");
1060
+ if ((v.type.kind === "record" && !pairish) || v.type.kind === "f64" ||
1061
+ v.type.kind === "bool" || v.type.kind === "nullT") {
1062
+ argTypeThrow("body", PIPELINE_STAGE_EXPECTED);
1063
+ }
1064
+ }
1065
+ else if (v.type.kind === "record" || v.type.kind === "string" || v.type.kind === "f64" ||
1066
+ v.type.kind === "bool" || v.type.kind === "array" || v.type.kind === "nullT") {
1067
+ argTypeThrow("stream", "an instance of ReadableStream, WritableStream, or Stream");
1068
+ }
996
1069
  }
997
- L.noLowering(`${what} over a '${L.fmt(v.type)}'`, node, "stream arguments are the lowered surface (iterables, generators, and plain functions have no lowering yet)");
1070
+ L.noLowering(`${what} over a '${L.fmt(v.type)}'`, node, what === "pipeline"
1071
+ ? "stream stages are the lowered surface — Node also accepts iterables (strings, arrays), generators, plain functions, and { readable, writable } pairs, but those have no lowering yet"
1072
+ : "stream arguments are the lowered surface (iterables, generators, and plain functions have no lowering yet)");
998
1073
  }
999
1074
  return v;
1000
1075
  }
@@ -1095,7 +1170,15 @@ export function lowerStreamModuleCall(L, call, bi, loc) {
1095
1170
  if (args.length < 2) {
1096
1171
  L.noLowering(`stream/promises.pipeline with ${args.length} arguments`, call, "the supported form is pipeline(source, ...streams)");
1097
1172
  }
1098
- const streams = args.map((a) => lowerStreamArg(L, a, "pipeline"));
1173
+ let streams;
1174
+ try {
1175
+ streams = args.map((a) => lowerStreamArg(L, a, "pipeline"));
1176
+ }
1177
+ catch (e) {
1178
+ if (e instanceof StreamArgTypeThrow)
1179
+ return e.expr;
1180
+ throw e;
1181
+ }
1099
1182
  return {
1100
1183
  kind: "libCall",
1101
1184
  fn: "sp.pipeline",
@@ -1106,6 +1189,43 @@ export function lowerStreamModuleCall(L, call, bi, loc) {
1106
1189
  }
1107
1190
  return null;
1108
1191
  }
1192
+ if (bi.module === "stream/consumers") {
1193
+ const args = call.arguments;
1194
+ // text/json/buffer(stream) → a pending promise the accumulate-and-
1195
+ // settle machinery resolves at the terminal point (the utf8 decode / the parsed
1196
+ // JSON DOM / the concatenated bytes) or rejects — with the stream's
1197
+ // error, ERR_STREAM_PREMATURE_CLOSE on an early close, or json's
1198
+ // SyntaxError on malformed text, Node's own rejection set.
1199
+ // arrayBuffer/blob return null here: neither value has a
1200
+ // representation, so the module-qualified fence (with the pointed
1201
+ // hints in BUILTIN_MODULE_FENCE_HINTS) speaks.
1202
+ if (bi.member === "text" || bi.member === "json" || bi.member === "buffer") {
1203
+ if (args.length !== 1) {
1204
+ L.noLowering(`stream/consumers.${bi.member} with ${args.length} arguments`, call, `the supported form is ${bi.member}(stream)`);
1205
+ }
1206
+ // A provably-non-stream argument: Node's consumers reject the
1207
+ // returned promise asynchronously with the plain "stream is not
1208
+ // async iterable" TypeError — no sync ladder exists to borrow
1209
+ // (finished/pipeline throw synchronously; consumers do not). A
1210
+ // sync throw here would be observably wrong timing, so the
1211
+ // provable case takes the named fence instead.
1212
+ const argV = L.lowerExpr(args[0]);
1213
+ const argInfo = argV.type.kind === "object" ? L.classes.get(argV.type.className) : undefined;
1214
+ if (argV.type.kind !== "dyn" && argV.type.kind !== "jsval" && !streamSidesOf(L, argInfo)) {
1215
+ L.noLowering(`stream/consumers.${bi.member} over a value that is not a stream`, call, "Node rejects the promise with 'stream is not async iterable' — pass a readable stream");
1216
+ }
1217
+ const recv = argV;
1218
+ const inner = bi.member === "text" ? STRING : bi.member === "json" ? DYN : BYTES;
1219
+ return {
1220
+ kind: "libCall",
1221
+ fn: `sc.${bi.member}`,
1222
+ args: [recv],
1223
+ type: { kind: "promise", inner },
1224
+ loc,
1225
+ };
1226
+ }
1227
+ return null;
1228
+ }
1109
1229
  if (bi.module !== "stream")
1110
1230
  return null;
1111
1231
  const args = call.arguments;
@@ -1147,7 +1267,15 @@ export function lowerStreamModuleCall(L, call, bi, loc) {
1147
1267
  if (args.length < 3) {
1148
1268
  L.noLowering(`pipeline with ${args.length} arguments`, call, "the supported form is pipeline(source, ...streams, callback)");
1149
1269
  }
1150
- const streams = args.slice(0, -1).map((a) => lowerStreamArg(L, a, "pipeline"));
1270
+ let streams;
1271
+ try {
1272
+ streams = args.slice(0, -1).map((a) => lowerStreamArg(L, a, "pipeline"));
1273
+ }
1274
+ catch (e) {
1275
+ if (e instanceof StreamArgTypeThrow)
1276
+ return e.expr;
1277
+ throw e;
1278
+ }
1151
1279
  const last = streams[streams.length - 1];
1152
1280
  const { cb, dyn } = lowerEosCallback(L, "pipeline", args[args.length - 1], last.type);
1153
1281
  return {
@@ -1204,7 +1332,7 @@ export function lowerStreamMethodCall(L, call, access, info) {
1204
1332
  if (t?.kind === "nullT")
1205
1333
  return "null";
1206
1334
  if (t?.kind === "dyn") {
1207
- // The JS lane's any-typed chunk: the runtime dispatches by DOM tag
1335
+ // The JS lane's any-typed chunk: the runtime dispatches by dyn tag
1208
1336
  // (bytes / string / null; anything else is the write TypeError).
1209
1337
  return "dyn";
1210
1338
  }
@@ -1235,6 +1363,29 @@ export function lowerStreamMethodCall(L, call, access, info) {
1235
1363
  if (args.length === 0 || args.length > 2) {
1236
1364
  L.noLowering(`${member} with ${args.length} arguments`, call, "the supported forms are (chunk) and (chunk, \"utf8\")");
1237
1365
  }
1366
+ // push(chunk, enc) with a LITERAL encoding: the per-call decode
1367
+ // (Buffer.from(chunk, enc)) — an explicit encoding (utf8 included)
1368
+ // overrides the stream's defaultEncoding; unknown literals throw
1369
+ // Node's ERR_UNKNOWN_ENCODING when the push evaluates. unshift keeps
1370
+ // the utf8-only fence below.
1371
+ if (member === "push" && args[1] !== undefined && chunkKind(args[0]) === "string") {
1372
+ const t = L.typeOf(args[1]);
1373
+ if (t.isStringLiteralType()) {
1374
+ const canonical = knownBufEncoding(t.value);
1375
+ if (canonical === undefined) {
1376
+ return nodeThrowExpr(1, "ERR_UNKNOWN_ENCODING", `Unknown encoding: ${t.value}`, BOOL, loc);
1377
+ }
1378
+ const receiver = L.lowerExpr(access.expression);
1379
+ const chunk = L.lowerExprExpecting(args[0], STRING);
1380
+ return {
1381
+ kind: "libCall",
1382
+ fn: "readable.pushStrEnc",
1383
+ args: [receiver, chunk, strLit(canonical, loc)],
1384
+ type: BOOL,
1385
+ loc,
1386
+ };
1387
+ }
1388
+ }
1238
1389
  checkUtf8Encoding(L, member, args[1]);
1239
1390
  const kind = chunkKind(args[0]);
1240
1391
  if ((kind === "null" || kind === "union" || kind === "dyn") && member === "unshift") {