@scriptc/compiler 0.4.2 → 0.4.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 (636) hide show
  1. package/ambient/scriptc-node-fallback.d.ts +397 -37
  2. package/dist/backend/cycle-analysis.d.ts +4 -1
  3. package/dist/backend/cycle-analysis.js +6 -4
  4. package/dist/backend/cycle-analysis.js.map +1 -1
  5. package/dist/backend/cycle-analysis.test.js +55 -0
  6. package/dist/backend/cycle-analysis.test.js.map +1 -1
  7. package/dist/backend/cycle-mutability.d.ts +7 -0
  8. package/dist/backend/cycle-mutability.js +19 -1
  9. package/dist/backend/cycle-mutability.js.map +1 -1
  10. package/dist/backend/executable-features.js +2 -0
  11. package/dist/backend/executable-features.js.map +1 -1
  12. package/dist/backend/immutability.d.ts +6 -3
  13. package/dist/backend/immutability.js +33 -29
  14. package/dist/backend/immutability.js.map +1 -1
  15. package/dist/backend/library-pack.js +1 -0
  16. package/dist/backend/library-pack.js.map +1 -1
  17. package/dist/backend/llvm/alloc.d.ts +16 -1
  18. package/dist/backend/llvm/alloc.js +54 -4
  19. package/dist/backend/llvm/alloc.js.map +1 -1
  20. package/dist/backend/llvm/appended-array-literals.d.ts +2 -1
  21. package/dist/backend/llvm/appended-array-literals.js +44 -36
  22. package/dist/backend/llvm/appended-array-literals.js.map +1 -1
  23. package/dist/backend/llvm/appended-array-literals.test.js +4 -3
  24. package/dist/backend/llvm/appended-array-literals.test.js.map +1 -1
  25. package/dist/backend/llvm/array-search.d.ts +4 -3
  26. package/dist/backend/llvm/array-search.js +21 -9
  27. package/dist/backend/llvm/array-search.js.map +1 -1
  28. package/dist/backend/llvm/blocks.d.ts +1 -1
  29. package/dist/backend/llvm/blocks.js +2 -2
  30. package/dist/backend/llvm/blocks.js.map +1 -1
  31. package/dist/backend/llvm/byte-windows.js +2 -0
  32. package/dist/backend/llvm/byte-windows.js.map +1 -1
  33. package/dist/backend/llvm/bytes-alias.d.ts +6 -1
  34. package/dist/backend/llvm/bytes-alias.js +8 -2
  35. package/dist/backend/llvm/bytes-alias.js.map +1 -1
  36. package/dist/backend/llvm/bytes-loop-versions.d.ts +47 -5
  37. package/dist/backend/llvm/bytes-loop-versions.js +473 -32
  38. package/dist/backend/llvm/bytes-loop-versions.js.map +1 -1
  39. package/dist/backend/llvm/bytes-loop-versions.test.js +258 -2
  40. package/dist/backend/llvm/bytes-loop-versions.test.js.map +1 -1
  41. package/dist/backend/llvm/checked-value-lifetimes.js +2 -0
  42. package/dist/backend/llvm/checked-value-lifetimes.js.map +1 -1
  43. package/dist/backend/llvm/classes.d.ts +7 -5
  44. package/dist/backend/llvm/classes.js +44 -20
  45. package/dist/backend/llvm/classes.js.map +1 -1
  46. package/dist/backend/llvm/classes.test.js +167 -7
  47. package/dist/backend/llvm/classes.test.js.map +1 -1
  48. package/dist/backend/llvm/common.d.ts +9 -3
  49. package/dist/backend/llvm/common.js +9 -3
  50. package/dist/backend/llvm/common.js.map +1 -1
  51. package/dist/backend/llvm/constant-callbacks.d.ts +2 -1
  52. package/dist/backend/llvm/constant-callbacks.js +83 -52
  53. package/dist/backend/llvm/constant-callbacks.js.map +1 -1
  54. package/dist/backend/llvm/counted-loops.d.ts +7 -0
  55. package/dist/backend/llvm/counted-loops.js +18 -0
  56. package/dist/backend/llvm/counted-loops.js.map +1 -1
  57. package/dist/backend/llvm/dense-array-access.d.ts +39 -8
  58. package/dist/backend/llvm/dense-array-access.js +222 -52
  59. package/dist/backend/llvm/dense-array-access.js.map +1 -1
  60. package/dist/backend/llvm/dense-array-access.test.js +36 -0
  61. package/dist/backend/llvm/dense-array-access.test.js.map +1 -1
  62. package/dist/backend/llvm/dense-array-loops.d.ts +17 -1
  63. package/dist/backend/llvm/dense-array-loops.js +194 -5
  64. package/dist/backend/llvm/dense-array-loops.js.map +1 -1
  65. package/dist/backend/llvm/dense-array-loops.test.js +90 -1
  66. package/dist/backend/llvm/dense-array-loops.test.js.map +1 -1
  67. package/dist/backend/llvm/dyn-handle-tags.test.js +6 -1
  68. package/dist/backend/llvm/dyn-handle-tags.test.js.map +1 -1
  69. package/dist/backend/llvm/dyn.d.ts +26 -0
  70. package/dist/backend/llvm/dyn.js +352 -61
  71. package/dist/backend/llvm/dyn.js.map +1 -1
  72. package/dist/backend/llvm/emit-function.js +11 -20
  73. package/dist/backend/llvm/emit-function.js.map +1 -1
  74. package/dist/backend/llvm/emit-journal.d.ts +93 -0
  75. package/dist/backend/llvm/emit-journal.js +170 -0
  76. package/dist/backend/llvm/emit-journal.js.map +1 -0
  77. package/dist/backend/llvm/emit-module.js +195 -27
  78. package/dist/backend/llvm/emit-module.js.map +1 -1
  79. package/dist/backend/llvm/emit-statements.d.ts +0 -4
  80. package/dist/backend/llvm/emit-statements.js +268 -84
  81. package/dist/backend/llvm/emit-statements.js.map +1 -1
  82. package/dist/backend/llvm/emitter-state.d.ts +7 -0
  83. package/dist/backend/llvm/emitter-state.js +14 -0
  84. package/dist/backend/llvm/emitter-state.js.map +1 -0
  85. package/dist/backend/llvm/emitter.d.ts +176 -37
  86. package/dist/backend/llvm/emitter.js +724 -85
  87. package/dist/backend/llvm/emitter.js.map +1 -1
  88. package/dist/backend/llvm/emitter.test.js +54 -5
  89. package/dist/backend/llvm/emitter.test.js.map +1 -1
  90. package/dist/backend/llvm/expr-bytes.js +34 -12
  91. package/dist/backend/llvm/expr-bytes.js.map +1 -1
  92. package/dist/backend/llvm/expr-callbacks.js +47 -0
  93. package/dist/backend/llvm/expr-callbacks.js.map +1 -1
  94. package/dist/backend/llvm/expr-calls.js +28 -14
  95. package/dist/backend/llvm/expr-calls.js.map +1 -1
  96. package/dist/backend/llvm/expr-containers.d.ts +14 -2
  97. package/dist/backend/llvm/expr-containers.js +129 -37
  98. package/dist/backend/llvm/expr-containers.js.map +1 -1
  99. package/dist/backend/llvm/expr-context.d.ts +14 -0
  100. package/dist/backend/llvm/expr-dynamic.js +18 -11
  101. package/dist/backend/llvm/expr-dynamic.js.map +1 -1
  102. package/dist/backend/llvm/expr-primitives.js +100 -28
  103. package/dist/backend/llvm/expr-primitives.js.map +1 -1
  104. package/dist/backend/llvm/expr-primitives.test.js +11 -6
  105. package/dist/backend/llvm/expr-primitives.test.js.map +1 -1
  106. package/dist/backend/llvm/expr-records.d.ts +12 -0
  107. package/dist/backend/llvm/expr-records.js +106 -0
  108. package/dist/backend/llvm/expr-records.js.map +1 -1
  109. package/dist/backend/llvm/expr-stream-bridges.d.ts +2 -2
  110. package/dist/backend/llvm/expr-stream-bridges.js +194 -65
  111. package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
  112. package/dist/backend/llvm/expr-stream-bridges.test.js +1 -1
  113. package/dist/backend/llvm/expr-stream-bridges.test.js.map +1 -1
  114. package/dist/backend/llvm/expr-stream-callbacks.d.ts +2 -2
  115. package/dist/backend/llvm/expr-stream-callbacks.js +74 -20
  116. package/dist/backend/llvm/expr-stream-callbacks.js.map +1 -1
  117. package/dist/backend/llvm/int32-kind-loops.d.ts +39 -0
  118. package/dist/backend/llvm/int32-kind-loops.js +106 -0
  119. package/dist/backend/llvm/int32-kind-loops.js.map +1 -0
  120. package/dist/backend/llvm/integer-or-nan-indexes.test.d.ts +1 -0
  121. package/dist/backend/llvm/integer-or-nan-indexes.test.js +118 -0
  122. package/dist/backend/llvm/integer-or-nan-indexes.test.js.map +1 -0
  123. package/dist/backend/llvm/integer-values.d.ts +20 -1
  124. package/dist/backend/llvm/integer-values.js +71 -0
  125. package/dist/backend/llvm/integer-values.js.map +1 -1
  126. package/dist/backend/llvm/js-ic.d.ts +1 -1
  127. package/dist/backend/llvm/js-ic.js +1 -1
  128. package/dist/backend/llvm/js-ic.js.map +1 -1
  129. package/dist/backend/llvm/key-orders.d.ts +7 -0
  130. package/dist/backend/llvm/key-orders.js +33 -0
  131. package/dist/backend/llvm/key-orders.js.map +1 -0
  132. package/dist/backend/llvm/lib-connect.js +4 -0
  133. package/dist/backend/llvm/lib-connect.js.map +1 -1
  134. package/dist/backend/llvm/lib-datagram.js +11 -0
  135. package/dist/backend/llvm/lib-datagram.js.map +1 -1
  136. package/dist/backend/llvm/lib-dispatch.js +80 -2
  137. package/dist/backend/llvm/lib-dispatch.js.map +1 -1
  138. package/dist/backend/llvm/lib-filesystem.js +4 -4
  139. package/dist/backend/llvm/lib-filesystem.js.map +1 -1
  140. package/dist/backend/llvm/lib-http2.js +4 -0
  141. package/dist/backend/llvm/lib-http2.js.map +1 -1
  142. package/dist/backend/llvm/lib-lookup.js +16 -6
  143. package/dist/backend/llvm/lib-lookup.js.map +1 -1
  144. package/dist/backend/llvm/lib-shared.js +73 -2
  145. package/dist/backend/llvm/lib-shared.js.map +1 -1
  146. package/dist/backend/llvm/lib-stream.js +35 -5
  147. package/dist/backend/llvm/lib-stream.js.map +1 -1
  148. package/dist/backend/llvm/local-array-reads.js +115 -19
  149. package/dist/backend/llvm/local-array-reads.js.map +1 -1
  150. package/dist/backend/llvm/local-array-reads.test.js +14 -0
  151. package/dist/backend/llvm/local-array-reads.test.js.map +1 -1
  152. package/dist/backend/llvm/loop-state.d.ts +33 -0
  153. package/dist/backend/llvm/loop-state.js +42 -0
  154. package/dist/backend/llvm/loop-state.js.map +1 -0
  155. package/dist/backend/llvm/map-number-lookup.d.ts +26 -8
  156. package/dist/backend/llvm/map-number-lookup.js +91 -31
  157. package/dist/backend/llvm/map-number-lookup.js.map +1 -1
  158. package/dist/backend/llvm/map-number-lookup.test.js +55 -0
  159. package/dist/backend/llvm/map-number-lookup.test.js.map +1 -1
  160. package/dist/backend/llvm/map-read-lifetimes.js +4 -2
  161. package/dist/backend/llvm/map-read-lifetimes.js.map +1 -1
  162. package/dist/backend/llvm/nullable-unions.d.ts +2 -1
  163. package/dist/backend/llvm/nullable-unions.js +15 -3
  164. package/dist/backend/llvm/nullable-unions.js.map +1 -1
  165. package/dist/backend/llvm/publish.d.ts +10 -0
  166. package/dist/backend/llvm/publish.js +12 -0
  167. package/dist/backend/llvm/publish.js.map +1 -1
  168. package/dist/backend/llvm/receivers.js +6 -1
  169. package/dist/backend/llvm/receivers.js.map +1 -1
  170. package/dist/backend/llvm/record-results.d.ts +1 -9
  171. package/dist/backend/llvm/record-results.js +2 -11
  172. package/dist/backend/llvm/record-results.js.map +1 -1
  173. package/dist/backend/llvm/reference-writes.d.ts +7 -2
  174. package/dist/backend/llvm/reference-writes.js +89 -72
  175. package/dist/backend/llvm/reference-writes.js.map +1 -1
  176. package/dist/backend/llvm/running-extrema.d.ts +11 -0
  177. package/dist/backend/llvm/running-extrema.js +115 -0
  178. package/dist/backend/llvm/running-extrema.js.map +1 -0
  179. package/dist/backend/llvm/running-extrema.test.d.ts +1 -0
  180. package/dist/backend/llvm/running-extrema.test.js +89 -0
  181. package/dist/backend/llvm/running-extrema.test.js.map +1 -0
  182. package/dist/backend/llvm/shapes.d.ts +71 -15
  183. package/dist/backend/llvm/shapes.js +222 -33
  184. package/dist/backend/llvm/shapes.js.map +1 -1
  185. package/dist/backend/llvm/shapes.test.js +53 -7
  186. package/dist/backend/llvm/shapes.test.js.map +1 -1
  187. package/dist/backend/llvm/speculative-integers.d.ts +6 -0
  188. package/dist/backend/llvm/speculative-integers.js +2 -2
  189. package/dist/backend/llvm/speculative-integers.js.map +1 -1
  190. package/dist/backend/llvm/stack-objects.d.ts +3 -2
  191. package/dist/backend/llvm/stack-objects.js +5 -2
  192. package/dist/backend/llvm/stack-objects.js.map +1 -1
  193. package/dist/backend/llvm/string-reads.d.ts +12 -0
  194. package/dist/backend/llvm/string-reads.js +44 -0
  195. package/dist/backend/llvm/string-reads.js.map +1 -1
  196. package/dist/backend/llvm/walk-borrows.d.ts +3 -0
  197. package/dist/backend/llvm/walk-borrows.js +46 -41
  198. package/dist/backend/llvm/walk-borrows.js.map +1 -1
  199. package/dist/backend/llvm/walkers.d.ts +3 -0
  200. package/dist/backend/llvm/walkers.js +88 -65
  201. package/dist/backend/llvm/walkers.js.map +1 -1
  202. package/dist/backend/llvm/wrapping-sums.d.ts +16 -1
  203. package/dist/backend/llvm/wrapping-sums.js +126 -0
  204. package/dist/backend/llvm/wrapping-sums.js.map +1 -1
  205. package/dist/backend/may-throw.d.ts +8 -10
  206. package/dist/backend/may-throw.js +14 -10
  207. package/dist/backend/may-throw.js.map +1 -1
  208. package/dist/backend/native/contracts.d.ts +5 -0
  209. package/dist/backend/native/executable.js +7 -0
  210. package/dist/backend/native/executable.js.map +1 -1
  211. package/dist/backend/native/runtime-inputs.d.ts +1 -1
  212. package/dist/backend/native/runtime-inputs.js +1 -0
  213. package/dist/backend/native/runtime-inputs.js.map +1 -1
  214. package/dist/backend/native-link-info-core.d.ts +4 -0
  215. package/dist/backend/native-link-info-core.js.map +1 -1
  216. package/dist/backend/publication.d.ts +12 -1
  217. package/dist/backend/publication.js +37 -14
  218. package/dist/backend/publication.js.map +1 -1
  219. package/dist/backend/publication.test.js +30 -0
  220. package/dist/backend/publication.test.js.map +1 -1
  221. package/dist/backend/result-slots.d.ts +16 -0
  222. package/dist/backend/result-slots.js +23 -0
  223. package/dist/backend/result-slots.js.map +1 -0
  224. package/dist/backend/runtime-abi.d.ts +2 -2
  225. package/dist/backend/runtime-abi.js +2 -2
  226. package/dist/backend/runtime-pack-core.d.ts +1 -0
  227. package/dist/backend/runtime-pack-core.js +1 -0
  228. package/dist/backend/runtime-pack-core.js.map +1 -1
  229. package/dist/backend/runtime-pack-targets.test.js +2 -1
  230. package/dist/backend/runtime-pack-targets.test.js.map +1 -1
  231. package/dist/backend/runtime-pack.test.js +2 -1
  232. package/dist/backend/runtime-pack.test.js.map +1 -1
  233. package/dist/backend/stack-observations.d.ts +2 -1
  234. package/dist/backend/stack-observations.js +67 -42
  235. package/dist/backend/stack-observations.js.map +1 -1
  236. package/dist/backend/target-diagnostics.js +5 -3
  237. package/dist/backend/target-diagnostics.js.map +1 -1
  238. package/dist/backend/warm-cache.js +1 -0
  239. package/dist/backend/warm-cache.js.map +1 -1
  240. package/dist/cli/usage.d.ts +1 -1
  241. package/dist/cli/usage.js +3 -3
  242. package/dist/compat/fetch-profile.js +1 -1
  243. package/dist/compat/fetch-profile.js.map +1 -1
  244. package/dist/contracts/check.js +4 -3
  245. package/dist/contracts/check.js.map +1 -1
  246. package/dist/contracts/int32-elements.js +10 -2
  247. package/dist/contracts/int32-elements.js.map +1 -1
  248. package/dist/coverage/report.js +12 -3
  249. package/dist/coverage/report.js.map +1 -1
  250. package/dist/diagnostics/diagnostic.d.ts +4 -0
  251. package/dist/diagnostics/diagnostic.js.map +1 -1
  252. package/dist/diagnostics/envelope.d.ts +3 -0
  253. package/dist/diagnostics/envelope.js +1 -0
  254. package/dist/diagnostics/envelope.js.map +1 -1
  255. package/dist/executable/executable-cache.d.ts +2 -0
  256. package/dist/executable/executable-cache.js +1 -0
  257. package/dist/executable/executable-cache.js.map +1 -1
  258. package/dist/executable/executable-cache.test.js +1 -0
  259. package/dist/executable/executable-cache.test.js.map +1 -1
  260. package/dist/frontend/decl-projection.d.ts +41 -0
  261. package/dist/frontend/decl-projection.js +854 -0
  262. package/dist/frontend/decl-projection.js.map +1 -0
  263. package/dist/frontend/decl-projection.test.d.ts +1 -0
  264. package/dist/frontend/decl-projection.test.js +30 -0
  265. package/dist/frontend/decl-projection.test.js.map +1 -0
  266. package/dist/frontend/dts-paths.d.ts +15 -0
  267. package/dist/frontend/dts-paths.js +25 -0
  268. package/dist/frontend/dts-paths.js.map +1 -1
  269. package/dist/frontend/input-tracker.d.ts +6 -0
  270. package/dist/frontend/input-tracker.js +51 -28
  271. package/dist/frontend/input-tracker.js.map +1 -1
  272. package/dist/frontend/input-tracker.test.js +19 -0
  273. package/dist/frontend/input-tracker.test.js.map +1 -1
  274. package/dist/frontend/lowering/array-values.js +100 -2
  275. package/dist/frontend/lowering/array-values.js.map +1 -1
  276. package/dist/frontend/lowering/builtins/child-process-methods.js +46 -15
  277. package/dist/frontend/lowering/builtins/child-process-methods.js.map +1 -1
  278. package/dist/frontend/lowering/builtins/child-process.js +13 -2
  279. package/dist/frontend/lowering/builtins/child-process.js.map +1 -1
  280. package/dist/frontend/lowering/builtins/crypto.js +84 -9
  281. package/dist/frontend/lowering/builtins/crypto.js.map +1 -1
  282. package/dist/frontend/lowering/builtins/global-namespace-keys.d.ts +3 -0
  283. package/dist/frontend/lowering/builtins/global-namespace-keys.js +122 -0
  284. package/dist/frontend/lowering/builtins/global-namespace-keys.js.map +1 -0
  285. package/dist/frontend/lowering/builtins/numbers.js +9 -1
  286. package/dist/frontend/lowering/builtins/numbers.js.map +1 -1
  287. package/dist/frontend/lowering/builtins/promises.d.ts +6 -0
  288. package/dist/frontend/lowering/builtins/promises.js +88 -2
  289. package/dist/frontend/lowering/builtins/promises.js.map +1 -1
  290. package/dist/frontend/lowering/builtins/readline.d.ts +5 -1
  291. package/dist/frontend/lowering/builtins/readline.js +35 -3
  292. package/dist/frontend/lowering/builtins/readline.js.map +1 -1
  293. package/dist/frontend/lowering/builtins/url.js +24 -1
  294. package/dist/frontend/lowering/builtins/url.js.map +1 -1
  295. package/dist/frontend/lowering/builtins/webassembly.js +6 -3
  296. package/dist/frontend/lowering/builtins/webassembly.js.map +1 -1
  297. package/dist/frontend/lowering/class-callbacks.d.ts +5 -2
  298. package/dist/frontend/lowering/class-callbacks.js +82 -12
  299. package/dist/frontend/lowering/class-callbacks.js.map +1 -1
  300. package/dist/frontend/lowering/class-construction.d.ts +4 -1
  301. package/dist/frontend/lowering/class-construction.js +17 -3
  302. package/dist/frontend/lowering/class-construction.js.map +1 -1
  303. package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +39 -2
  304. package/dist/frontend/lowering/class-dynamic-dispatch.js +766 -108
  305. package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
  306. package/dist/frontend/lowering/class-dynamic-dispatch.test.js +47 -0
  307. package/dist/frontend/lowering/class-dynamic-dispatch.test.js.map +1 -1
  308. package/dist/frontend/lowering/class-iterables.d.ts +2 -1
  309. package/dist/frontend/lowering/class-iterables.js +9 -1
  310. package/dist/frontend/lowering/class-iterables.js.map +1 -1
  311. package/dist/frontend/lowering/class-method-mutations.js +32 -9
  312. package/dist/frontend/lowering/class-method-mutations.js.map +1 -1
  313. package/dist/frontend/lowering/class-prototypes.js +31 -2
  314. package/dist/frontend/lowering/class-prototypes.js.map +1 -1
  315. package/dist/frontend/lowering/class-runtime-statics.d.ts +9 -3
  316. package/dist/frontend/lowering/class-runtime-statics.js +29 -4
  317. package/dist/frontend/lowering/class-runtime-statics.js.map +1 -1
  318. package/dist/frontend/lowering/coercions/structural-plans.js +48 -1
  319. package/dist/frontend/lowering/coercions/structural-plans.js.map +1 -1
  320. package/dist/frontend/lowering/coercions/structural.js +91 -1
  321. package/dist/frontend/lowering/coercions/structural.js.map +1 -1
  322. package/dist/frontend/lowering/containers/array-callback-call.js +2 -2
  323. package/dist/frontend/lowering/containers/array-callback-call.js.map +1 -1
  324. package/dist/frontend/lowering/containers/array-callbacks.js +338 -18
  325. package/dist/frontend/lowering/containers/array-callbacks.js.map +1 -1
  326. package/dist/frontend/lowering/containers/array-concat.js +10 -0
  327. package/dist/frontend/lowering/containers/array-concat.js.map +1 -1
  328. package/dist/frontend/lowering/containers/array-methods.js +15 -0
  329. package/dist/frontend/lowering/containers/array-methods.js.map +1 -1
  330. package/dist/frontend/lowering/containers/array-pipelines.js +19 -8
  331. package/dist/frontend/lowering/containers/array-pipelines.js.map +1 -1
  332. package/dist/frontend/lowering/containers/bytes.js +2 -1
  333. package/dist/frontend/lowering/containers/bytes.js.map +1 -1
  334. package/dist/frontend/lowering/containers/callback-arguments.js +3 -0
  335. package/dist/frontend/lowering/containers/callback-arguments.js.map +1 -1
  336. package/dist/frontend/lowering/containers/collection-methods.js +7 -2
  337. package/dist/frontend/lowering/containers/collection-methods.js.map +1 -1
  338. package/dist/frontend/lowering/containers/filter-predicates.d.ts +6 -0
  339. package/dist/frontend/lowering/containers/filter-predicates.js +205 -0
  340. package/dist/frontend/lowering/containers/filter-predicates.js.map +1 -0
  341. package/dist/frontend/lowering/containers/indexed-objects.d.ts +0 -12
  342. package/dist/frontend/lowering/containers/indexed-objects.js +46 -7
  343. package/dist/frontend/lowering/containers/indexed-objects.js.map +1 -1
  344. package/dist/frontend/lowering/containers/string-and-regexp.js +55 -9
  345. package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
  346. package/dist/frontend/lowering/containers/string-replacement.js +43 -19
  347. package/dist/frontend/lowering/containers/string-replacement.js.map +1 -1
  348. package/dist/frontend/lowering/dispatch-sites.js +15 -1
  349. package/dist/frontend/lowering/dispatch-sites.js.map +1 -1
  350. package/dist/frontend/lowering/divergences.js +9 -1
  351. package/dist/frontend/lowering/divergences.js.map +1 -1
  352. package/dist/frontend/lowering/error-methods.js +47 -2
  353. package/dist/frontend/lowering/error-methods.js.map +1 -1
  354. package/dist/frontend/lowering/expressions/array-literals.js +47 -0
  355. package/dist/frontend/lowering/expressions/array-literals.js.map +1 -1
  356. package/dist/frontend/lowering/expressions/binary.js +36 -2
  357. package/dist/frontend/lowering/expressions/binary.js.map +1 -1
  358. package/dist/frontend/lowering/expressions/element-access.js +90 -13
  359. package/dist/frontend/lowering/expressions/element-access.js.map +1 -1
  360. package/dist/frontend/lowering/expressions/identifiers.js +33 -4
  361. package/dist/frontend/lowering/expressions/identifiers.js.map +1 -1
  362. package/dist/frontend/lowering/expressions/narrowing.js +95 -1
  363. package/dist/frontend/lowering/expressions/narrowing.js.map +1 -1
  364. package/dist/frontend/lowering/expressions/nullish-assignment.js +3 -1
  365. package/dist/frontend/lowering/expressions/nullish-assignment.js.map +1 -1
  366. package/dist/frontend/lowering/expressions/object-literals.d.ts +10 -0
  367. package/dist/frontend/lowering/expressions/object-literals.js +138 -51
  368. package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
  369. package/dist/frontend/lowering/expressions/optional-chains.js +12 -0
  370. package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
  371. package/dist/frontend/lowering/expressions/property-access.js +113 -11
  372. package/dist/frontend/lowering/expressions/property-access.js.map +1 -1
  373. package/dist/frontend/lowering/expressions/relational.js +212 -33
  374. package/dist/frontend/lowering/expressions/relational.js.map +1 -1
  375. package/dist/frontend/lowering/expressions/union-fields.js +4 -0
  376. package/dist/frontend/lowering/expressions/union-fields.js.map +1 -1
  377. package/dist/frontend/lowering/expressions/updates.js +22 -3
  378. package/dist/frontend/lowering/expressions/updates.js.map +1 -1
  379. package/dist/frontend/lowering/frozen-records.d.ts +28 -0
  380. package/dist/frontend/lowering/frozen-records.js +322 -0
  381. package/dist/frontend/lowering/frozen-records.js.map +1 -0
  382. package/dist/frontend/lowering/function-returns.js +49 -0
  383. package/dist/frontend/lowering/function-returns.js.map +1 -1
  384. package/dist/frontend/lowering/generic-functions.js +7 -2
  385. package/dist/frontend/lowering/generic-functions.js.map +1 -1
  386. package/dist/frontend/lowering/iterable-values.d.ts +49 -0
  387. package/dist/frontend/lowering/iterable-values.js +297 -0
  388. package/dist/frontend/lowering/iterable-values.js.map +1 -0
  389. package/dist/frontend/lowering/iterator-adapters.js +186 -1
  390. package/dist/frontend/lowering/iterator-adapters.js.map +1 -1
  391. package/dist/frontend/lowering/js-class-fields.d.ts +8 -0
  392. package/dist/frontend/lowering/js-class-fields.js +45 -10
  393. package/dist/frontend/lowering/js-class-fields.js.map +1 -1
  394. package/dist/frontend/lowering/key-order-witness.d.ts +18 -3
  395. package/dist/frontend/lowering/key-order-witness.js +770 -22
  396. package/dist/frontend/lowering/key-order-witness.js.map +1 -1
  397. package/dist/frontend/lowering/key-order-witness.test.js +428 -8
  398. package/dist/frontend/lowering/key-order-witness.test.js.map +1 -1
  399. package/dist/frontend/lowering/lower-async-iteration.js +4 -1
  400. package/dist/frontend/lowering/lower-async-iteration.js.map +1 -1
  401. package/dist/frontend/lowering/lower-calls.d.ts +7 -0
  402. package/dist/frontend/lowering/lower-calls.js +443 -41
  403. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  404. package/dist/frontend/lowering/lower-classes.d.ts +11 -2
  405. package/dist/frontend/lowering/lower-classes.js +302 -22
  406. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  407. package/dist/frontend/lowering/lower-dgram.js +3 -1
  408. package/dist/frontend/lowering/lower-dgram.js.map +1 -1
  409. package/dist/frontend/lowering/lower-exprs.js +10 -2
  410. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  411. package/dist/frontend/lowering/lower-functions.d.ts +11 -0
  412. package/dist/frontend/lowering/lower-functions.js +83 -0
  413. package/dist/frontend/lowering/lower-functions.js.map +1 -1
  414. package/dist/frontend/lowering/lower-generators.js +41 -31
  415. package/dist/frontend/lowering/lower-generators.js.map +1 -1
  416. package/dist/frontend/lowering/lower-global-value.js +6 -3
  417. package/dist/frontend/lowering/lower-global-value.js.map +1 -1
  418. package/dist/frontend/lowering/lower-inspect.js +18 -6
  419. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  420. package/dist/frontend/lowering/lower-modules.js +32 -0
  421. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  422. package/dist/frontend/lowering/lower-namespaces.js +5 -1
  423. package/dist/frontend/lowering/lower-namespaces.js.map +1 -1
  424. package/dist/frontend/lowering/lower-server.js +2 -2
  425. package/dist/frontend/lowering/lower-server.js.map +1 -1
  426. package/dist/frontend/lowering/lower-stdlib.d.ts +5 -0
  427. package/dist/frontend/lowering/lower-stdlib.js +30 -0
  428. package/dist/frontend/lowering/lower-stdlib.js.map +1 -1
  429. package/dist/frontend/lowering/lower-stmts.d.ts +23 -0
  430. package/dist/frontend/lowering/lower-stmts.js +454 -48
  431. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  432. package/dist/frontend/lowering/lower-stream.d.ts +20 -2
  433. package/dist/frontend/lowering/lower-stream.js +1483 -156
  434. package/dist/frontend/lowering/lower-stream.js.map +1 -1
  435. package/dist/frontend/lowering/lowerer.d.ts +77 -6
  436. package/dist/frontend/lowering/lowerer.js +313 -29
  437. package/dist/frontend/lowering/lowerer.js.map +1 -1
  438. package/dist/frontend/lowering/module-namespace-elements.d.ts +11 -1
  439. package/dist/frontend/lowering/module-namespace-elements.js +82 -5
  440. package/dist/frontend/lowering/module-namespace-elements.js.map +1 -1
  441. package/dist/frontend/lowering/new-target.d.ts +12 -0
  442. package/dist/frontend/lowering/new-target.js +280 -0
  443. package/dist/frontend/lowering/new-target.js.map +1 -0
  444. package/dist/frontend/lowering/surfaces.d.ts +3 -0
  445. package/dist/frontend/lowering/surfaces.js +18 -6
  446. package/dist/frontend/lowering/surfaces.js.map +1 -1
  447. package/dist/frontend/lowering/undefined-index.d.ts +27 -0
  448. package/dist/frontend/lowering/undefined-index.js +82 -0
  449. package/dist/frontend/lowering/undefined-index.js.map +1 -0
  450. package/dist/frontend/lowering/var-hoisting.js +29 -13
  451. package/dist/frontend/lowering/var-hoisting.js.map +1 -1
  452. package/dist/frontend/lowering/width-lift.d.ts +18 -0
  453. package/dist/frontend/npm-static-bundled-chunks.js +9 -11
  454. package/dist/frontend/npm-static-bundled-chunks.js.map +1 -1
  455. package/dist/frontend/npm-static-bundled-chunks.test.js +6 -5
  456. package/dist/frontend/npm-static-bundled-chunks.test.js.map +1 -1
  457. package/dist/frontend/npm-static-rewrite-syntax.js +9 -1
  458. package/dist/frontend/npm-static-rewrite-syntax.js.map +1 -1
  459. package/dist/frontend/npm-static.js +4 -20
  460. package/dist/frontend/npm-static.js.map +1 -1
  461. package/dist/frontend/package-shims.d.ts +3 -0
  462. package/dist/frontend/package-shims.js +71 -8
  463. package/dist/frontend/package-shims.js.map +1 -1
  464. package/dist/frontend/pipeline.js +18 -10
  465. package/dist/frontend/pipeline.js.map +1 -1
  466. package/dist/frontend/process-fork.d.ts +25 -0
  467. package/dist/frontend/process-fork.js +2 -0
  468. package/dist/frontend/process-fork.js.map +1 -0
  469. package/dist/frontend/program.d.ts +8 -1
  470. package/dist/frontend/program.js +484 -69
  471. package/dist/frontend/program.js.map +1 -1
  472. package/dist/frontend/program.test.js +43 -3
  473. package/dist/frontend/program.test.js.map +1 -1
  474. package/dist/frontend/token-equivalence.d.ts +3 -0
  475. package/dist/frontend/token-equivalence.js +409 -0
  476. package/dist/frontend/token-equivalence.js.map +1 -0
  477. package/dist/frontend/token-equivalence.test.d.ts +1 -0
  478. package/dist/frontend/token-equivalence.test.js +67 -0
  479. package/dist/frontend/token-equivalence.test.js.map +1 -0
  480. package/dist/frontend/ts7/ast-node.d.ts +22 -0
  481. package/dist/frontend/ts7/ast-node.js +1048 -103
  482. package/dist/frontend/ts7/ast-node.js.map +1 -1
  483. package/dist/frontend/ts7/ast-node.test.js +24 -0
  484. package/dist/frontend/ts7/ast-node.test.js.map +1 -1
  485. package/dist/frontend/ts7/ast-wire.d.ts +4 -3
  486. package/dist/frontend/ts7/ast-wire.js +34 -24
  487. package/dist/frontend/ts7/ast-wire.js.map +1 -1
  488. package/dist/frontend/ts7/ast-wire.test.js +23 -0
  489. package/dist/frontend/ts7/ast-wire.test.js.map +1 -1
  490. package/dist/frontend/ts7/checker-backend.d.ts +3 -4
  491. package/dist/frontend/ts7/checker-backend.js +2 -2
  492. package/dist/frontend/ts7/checker-backend.js.map +1 -1
  493. package/dist/frontend/ts7/checker-backend.test.js +5 -9
  494. package/dist/frontend/ts7/checker-backend.test.js.map +1 -1
  495. package/dist/frontend/ts7/checker.d.ts +4 -0
  496. package/dist/frontend/ts7/checker.js +7 -0
  497. package/dist/frontend/ts7/checker.js.map +1 -1
  498. package/dist/frontend/ts7/lib-diagnostics.d.ts +24 -0
  499. package/dist/frontend/ts7/lib-diagnostics.js +307 -0
  500. package/dist/frontend/ts7/lib-diagnostics.js.map +1 -0
  501. package/dist/frontend/ts7/lib-diagnostics.test.d.ts +1 -0
  502. package/dist/frontend/ts7/lib-diagnostics.test.js +199 -0
  503. package/dist/frontend/ts7/lib-diagnostics.test.js.map +1 -0
  504. package/dist/frontend/ts7/program-host.d.ts +7 -0
  505. package/dist/frontend/ts7/program-host.js +25 -2
  506. package/dist/frontend/ts7/program-host.js.map +1 -1
  507. package/dist/frontend/ts7/session.d.ts +14 -1
  508. package/dist/frontend/ts7/session.js +22 -0
  509. package/dist/frontend/ts7/session.js.map +1 -1
  510. package/dist/frontend/type-mapper.d.ts +13 -1
  511. package/dist/frontend/type-mapper.js +191 -11
  512. package/dist/frontend/type-mapper.js.map +1 -1
  513. package/dist/index.d.ts +0 -5
  514. package/dist/index.js +34 -4
  515. package/dist/index.js.map +1 -1
  516. package/dist/ir/analysis.js +5 -3
  517. package/dist/ir/analysis.js.map +1 -1
  518. package/dist/ir/array-length-stability.d.ts +4 -0
  519. package/dist/ir/array-length-stability.js +19 -6
  520. package/dist/ir/array-length-stability.js.map +1 -1
  521. package/dist/ir/array-length-stability.test.js +24 -0
  522. package/dist/ir/array-length-stability.test.js.map +1 -1
  523. package/dist/ir/builtin-effects.d.ts +7 -0
  524. package/dist/ir/builtin-effects.js +52 -0
  525. package/dist/ir/builtin-effects.js.map +1 -1
  526. package/dist/ir/builtin-signatures.d.ts +6 -4
  527. package/dist/ir/builtin-signatures.js +166 -13
  528. package/dist/ir/builtin-signatures.js.map +1 -1
  529. package/dist/ir/callback-specialization.d.ts +2 -1
  530. package/dist/ir/callback-specialization.js +23 -18
  531. package/dist/ir/callback-specialization.js.map +1 -1
  532. package/dist/ir/char-unit-locals.d.ts +19 -0
  533. package/dist/ir/char-unit-locals.js +185 -0
  534. package/dist/ir/char-unit-locals.js.map +1 -0
  535. package/dist/ir/char-unit-locals.test.d.ts +1 -0
  536. package/dist/ir/char-unit-locals.test.js +94 -0
  537. package/dist/ir/char-unit-locals.test.js.map +1 -0
  538. package/dist/ir/final-fields.d.ts +2 -1
  539. package/dist/ir/final-fields.js +14 -16
  540. package/dist/ir/final-fields.js.map +1 -1
  541. package/dist/ir/function-references.d.ts +2 -0
  542. package/dist/ir/function-references.js +2 -0
  543. package/dist/ir/function-references.js.map +1 -1
  544. package/dist/ir/function-references.test.js +3 -0
  545. package/dist/ir/function-references.test.js.map +1 -1
  546. package/dist/ir/initializer-bindings.d.ts +2 -1
  547. package/dist/ir/initializer-bindings.js +27 -20
  548. package/dist/ir/initializer-bindings.js.map +1 -1
  549. package/dist/ir/int32-arrays.d.ts +108 -0
  550. package/dist/ir/int32-arrays.js +831 -0
  551. package/dist/ir/int32-arrays.js.map +1 -0
  552. package/dist/ir/int32-slots.d.ts +24 -2
  553. package/dist/ir/int32-slots.js +259 -57
  554. package/dist/ir/int32-slots.js.map +1 -1
  555. package/dist/ir/integer-loops.d.ts +3 -3
  556. package/dist/ir/integer-loops.js +14 -6
  557. package/dist/ir/integer-loops.js.map +1 -1
  558. package/dist/ir/integer-loops.test.js +10 -0
  559. package/dist/ir/integer-loops.test.js.map +1 -1
  560. package/dist/ir/integer-ranges.d.ts +5 -0
  561. package/dist/ir/integer-ranges.js +125 -12
  562. package/dist/ir/integer-ranges.js.map +1 -1
  563. package/dist/ir/integer-ranges.test.js +30 -0
  564. package/dist/ir/integer-ranges.test.js.map +1 -1
  565. package/dist/ir/ir.d.ts +70 -7
  566. package/dist/ir/ir.js +58 -1
  567. package/dist/ir/ir.js.map +1 -1
  568. package/dist/ir/ir.test.js +21 -1
  569. package/dist/ir/ir.test.js.map +1 -1
  570. package/dist/ir/js-ic.d.ts +19 -2
  571. package/dist/ir/js-ic.js +95 -11
  572. package/dist/ir/js-ic.js.map +1 -1
  573. package/dist/ir/js-ic.test.js +188 -0
  574. package/dist/ir/js-ic.test.js.map +1 -1
  575. package/dist/ir/node-index.d.ts +57 -0
  576. package/dist/ir/node-index.js +215 -0
  577. package/dist/ir/node-index.js.map +1 -0
  578. package/dist/ir/paired-while-loops.d.ts +25 -0
  579. package/dist/ir/paired-while-loops.js +335 -0
  580. package/dist/ir/paired-while-loops.js.map +1 -0
  581. package/dist/ir/paired-while-loops.test.d.ts +1 -0
  582. package/dist/ir/paired-while-loops.test.js +163 -0
  583. package/dist/ir/paired-while-loops.test.js.map +1 -0
  584. package/dist/ir/parameter-versions.d.ts +25 -0
  585. package/dist/ir/parameter-versions.js +300 -0
  586. package/dist/ir/parameter-versions.js.map +1 -0
  587. package/dist/ir/runtime-features.d.ts +34 -13
  588. package/dist/ir/runtime-features.js +152 -38
  589. package/dist/ir/runtime-features.js.map +1 -1
  590. package/dist/ir/serialize.d.ts +12 -2
  591. package/dist/ir/serialize.js +16 -3
  592. package/dist/ir/serialize.js.map +1 -1
  593. package/dist/ir/serialize.test.js +17 -0
  594. package/dist/ir/serialize.test.js.map +1 -1
  595. package/dist/ir/traverse.js +18 -6
  596. package/dist/ir/traverse.js.map +1 -1
  597. package/dist/ir/validate.js +122 -36
  598. package/dist/ir/validate.js.map +1 -1
  599. package/dist/library/ir-policy.js +2 -0
  600. package/dist/library/ir-policy.js.map +1 -1
  601. package/dist/native/cache.d.ts +6 -1
  602. package/dist/native/cache.js +28 -5
  603. package/dist/native/cache.js.map +1 -1
  604. package/dist/native/cache.test.js +16 -2
  605. package/dist/native/cache.test.js.map +1 -1
  606. package/dist/native/cli.js +21 -1
  607. package/dist/native/cli.js.map +1 -1
  608. package/dist/native/compiler.js +8 -6
  609. package/dist/native/compiler.js.map +1 -1
  610. package/dist/native/executable-cache.d.ts +1 -1
  611. package/dist/native/executable-cache.js +2 -2
  612. package/dist/native/executable-cache.js.map +1 -1
  613. package/dist/native/prepare.d.ts +3 -1
  614. package/dist/native/prepare.js +119 -31
  615. package/dist/native/prepare.js.map +1 -1
  616. package/dist/native/prepare.test.js +78 -18
  617. package/dist/native/prepare.test.js.map +1 -1
  618. package/dist/native/sanitizer.test.js +2 -0
  619. package/dist/native/sanitizer.test.js.map +1 -1
  620. package/dist/native/text-file.d.ts +13 -3
  621. package/dist/native/text-file.js +63 -9
  622. package/dist/native/text-file.js.map +1 -1
  623. package/dist/native/text-file.test.d.ts +1 -0
  624. package/dist/native/text-file.test.js +49 -0
  625. package/dist/native/text-file.test.js.map +1 -0
  626. package/dist/native/toolchain.js +4 -4
  627. package/dist/native/toolchain.js.map +1 -1
  628. package/dist/native/toolchain.test.js +3 -3
  629. package/dist/native/toolchain.test.js.map +1 -1
  630. package/package.json +21 -21
  631. package/shims/chalk/v5.ts +688 -0
  632. package/shims/hono-node-server/v2.ts +645 -0
  633. package/shims/hot-shots/client.ts +1126 -0
  634. package/shims/hot-shots/v8-unix-dgram.ts +8 -0
  635. package/shims/hot-shots/v8.ts +8 -0
  636. package/surface-manifest.json +26 -4
@@ -48,6 +48,7 @@ import { bindingNeverReassigned } from "./binding-analysis.js";
48
48
  import { extractIteratorValue, genStep, genValue, stepDrivable } from "./lower-generators.js";
49
49
  import { LiftedFrame, driveGenerator } from "./containers/iterator-helpers.js";
50
50
  import { lowerArrayIteratorValue, lowerStringIteratorValue } from "./iterator-adapters.js";
51
+ import { staticReadableStreamElementType } from "./lower-stdlib.js";
51
52
  import { BOOL, DYN, F64, RUNTIME_STREAM_CLASSES, STRING, VOID, arrayOf, BYTES_U8, canBoxFuncIntoDyn, canConvertToDyn, funcOf, typeEquals, typeKey, UNDEFINED_T, } from "../../ir/ir.js";
52
53
  const BYTES = BYTES_U8;
53
54
  /** An expression that only reads bindings and properties (no calls,
@@ -70,6 +71,55 @@ function pureRead(node) {
70
71
  }
71
72
  return false;
72
73
  }
74
+ /** A literal, `undefined`, or a function expression: a value that reads
75
+ * no binding or property. */
76
+ function constantOptionValue(node) {
77
+ let e = node;
78
+ while (ts.isParenthesizedExpression(e) || ts.isAsExpression(e) || ts.isTypeAssertion(e))
79
+ e = e.expression;
80
+ if (ts.isIdentifier(e))
81
+ return e.text === "undefined";
82
+ if (ts.isPrefixUnaryExpression(e) &&
83
+ (e.operator === ts.SyntaxKind.MinusToken ||
84
+ e.operator === ts.SyntaxKind.PlusToken ||
85
+ e.operator === ts.SyntaxKind.ExclamationToken))
86
+ return constantOptionValue(e.operand);
87
+ return (ts.isStringLiteralLike(e) ||
88
+ ts.isNumericLiteral(e) ||
89
+ ts.isBigIntLiteral(e) ||
90
+ e.kind === ts.SyntaxKind.TrueKeyword ||
91
+ e.kind === ts.SyntaxKind.FalseKeyword ||
92
+ e.kind === ts.SyntaxKind.NullKeyword ||
93
+ ts.isArrowFunction(e) ||
94
+ ts.isFunctionExpression(e));
95
+ }
96
+ /** Whether a runtime decodeStrings or defaultEncoding value (`prop`'s
97
+ * `value`) may be read before the other option members. Those values are
98
+ * read into locals before the constructor evaluates the other members,
99
+ * while Node evaluates the members in order, so every earlier member must
100
+ * evaluate without effects, and when the value itself has effects, every
101
+ * earlier member must also read nothing those effects could change. */
102
+ function earlierOptionsPure(options, prop, value) {
103
+ const valueHasEffects = !sideEffectFreeOptionValue(value) && !pureRead(value);
104
+ for (const member of options.properties.nodes) {
105
+ if (member === prop)
106
+ return true;
107
+ if (ts.isPropertyAssignment(member)) {
108
+ const init = member.initializer;
109
+ if (valueHasEffects
110
+ ? !constantOptionValue(init)
111
+ : !sideEffectFreeOptionValue(init) && !pureRead(init))
112
+ return false;
113
+ }
114
+ else if (ts.isShorthandPropertyAssignment(member)) {
115
+ if (valueHasEffects)
116
+ return false;
117
+ }
118
+ else if (ts.isSpreadAssignment(member))
119
+ return false;
120
+ }
121
+ return true;
122
+ }
73
123
  /** The stream sides of a receiver class: the nearest stream-class
74
124
  * ancestor's, or null off the stream hierarchy. */
75
125
  export function streamSidesOf(lowerer, info) {
@@ -135,10 +185,23 @@ export const UNDERSCORE_METHODS = new Map([
135
185
  ["transform", "_transform"],
136
186
  ["flush", "_flush"],
137
187
  ]);
188
+ /** The option callbacks applied after construction rather than through
189
+ * the constructor's callback slots (option name → method name): construct
190
+ * on every stream class, and writev on every class with a writable side
191
+ * (a Transform's batches reach writev, not the transform, as in Node). */
192
+ export const STREAM_EXTRA_CALLBACKS = new Map([
193
+ ["construct", "_construct"],
194
+ ["writev", "_writev"],
195
+ ]);
196
+ export function streamExtraCallbacks(cls) {
197
+ return cls === "%Readable" ? ["construct"] : ["construct", "writev"];
198
+ }
138
199
  /** The completion callback's parameter position in each underscore
139
200
  * method that takes one (cbTuples' "fn" positions). */
140
201
  export const STREAM_COMPLETION_CALLBACK_INDEX = new Map([
141
202
  ["_write", 2],
203
+ ["_writev", 1],
204
+ ["_construct", 0],
142
205
  ["_final", 0],
143
206
  ["_destroy", 1],
144
207
  ["_transform", 2],
@@ -256,9 +319,11 @@ function lowerStreamCallback(lowerer, which, node, thisType, fullTuple, chunkVal
256
319
  // supertype of it (`Buffer | string`); an encoding of another type
257
320
  // (`string | null | undefined`) converts from the encoding argument's
258
321
  // value.
259
- if (((i === 0 && (chunkValues || widensInto(lowerer, fullTuple[0], funcType.params[0], loc))) ||
322
+ if (((((i === 0 &&
323
+ (chunkValues || widensInto(lowerer, fullTuple[0], funcType.params[0], loc))) ||
260
324
  i === 1) &&
261
- (which === "write" || which === "transform") &&
325
+ (which === "write" || which === "transform")) ||
326
+ (i === 0 && which === "writev")) &&
262
327
  funcType.params[i].kind !== "func" &&
263
328
  canConvertToDyn(funcType.params[i], (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id)))
264
329
  continue;
@@ -359,14 +424,20 @@ function streamObjectModes(lowerer, ctorName, optsLit) {
359
424
  }
360
425
  return out;
361
426
  }
362
- /** Whether an options literal sets the writable side's object mode with a
363
- * `true` literal (objectMode, or writableObjectMode on a duplex stream).
364
- * Reports nothing: streamObjectModes fences the other value forms. */
427
+ /** Whether an options literal makes the writable side hold values: its
428
+ * object mode set with a `true` literal (objectMode, or writableObjectMode
429
+ * on a duplex stream), or decodeStrings: false (strings stay strings).
430
+ * Reports nothing: the options parse fences the other value forms. */
365
431
  function literalWritesValues(opts, duplexish) {
366
432
  for (const prop of opts.properties?.nodes ?? []) {
367
433
  if (!ts.isPropertyAssignment(prop))
368
434
  continue;
369
435
  const name = ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name) ? prop.name.text : null;
436
+ if (name === "decodeStrings") {
437
+ if (prop.initializer.kind !== ts.SyntaxKind.TrueKeyword)
438
+ return true;
439
+ continue;
440
+ }
370
441
  if (name !== "objectMode" && !(duplexish && name === "writableObjectMode"))
371
442
  continue;
372
443
  if (prop.initializer.kind !== ts.SyntaxKind.FalseKeyword)
@@ -384,6 +455,8 @@ function cbTuples(lowerer) {
384
455
  read: [F64],
385
456
  write: [BYTES, STRING, "fn"],
386
457
  final: ["fn"],
458
+ construct: ["fn"],
459
+ writev: [DYN, "fn"],
387
460
  destroy: [errorOrNull(lowerer), "fn"],
388
461
  transform: [BYTES, STRING, "fn"],
389
462
  flush: ["fn"],
@@ -577,10 +650,57 @@ function pinAnyDataCompletion(lowerer, which, fnNode) {
577
650
  const declared = lowerer.paramShape(param).type;
578
651
  if (declared.kind !== "func" || declared.rest || declared.params[1]?.kind !== "dyn")
579
652
  return null;
653
+ // Only when every use calls the callback with a Buffer, a string or
654
+ // nothing as `data`: another value (or a callback that escapes) keeps
655
+ // `data` checked-dynamic, so the runtime pushes it as push(data) does
656
+ // and reports ERR_INVALID_ARG_TYPE through 'error', as Node does.
657
+ if (fnNode.body === undefined || !completionDataIsChunk(lowerer, fnNode.body, symbol, param.name))
658
+ return null;
580
659
  const data = unionOf(lowerer, [BYTES, STRING, { kind: "undefinedT" }]);
581
660
  lowerer.stageSourceTypes.set(symbol, funcOf([declared.params[0], data, ...declared.params.slice(2)], declared.ret));
582
661
  return symbol;
583
662
  }
663
+ /** Whether every reference to the completion callback `symbol` in `body`
664
+ * calls it with a `data` argument that is a Buffer, a string, null or
665
+ * undefined (or with no data). */
666
+ function completionDataIsChunk(lowerer, body, symbol, declaration) {
667
+ let ok = true;
668
+ const visit = (node) => {
669
+ if (!ok)
670
+ return;
671
+ if (ts.isIdentifier(node) &&
672
+ node !== declaration &&
673
+ lowerer.checker.getSymbolAtLocation(node) === symbol) {
674
+ const call = node.parent;
675
+ if (!call || !ts.isCallExpression(call) || call.expression !== node) {
676
+ ok = false;
677
+ return;
678
+ }
679
+ const data = call.arguments.nodes[1];
680
+ if (data !== undefined) {
681
+ let t;
682
+ try {
683
+ t = lowerer.mapTypeOf(lowerer.typeOf(data));
684
+ }
685
+ catch {
686
+ t = null;
687
+ }
688
+ const arms = t?.kind === "union" ? (lowerer.unions.get(t.unionId)?.arms ?? null) : t ? [t] : null;
689
+ if (arms === null ||
690
+ !arms.every((a) => (a.kind === "bytes" && a.elem === "u8") ||
691
+ a.kind === "string" ||
692
+ a.kind === "undefinedT" ||
693
+ a.kind === "nullT")) {
694
+ ok = false;
695
+ return;
696
+ }
697
+ }
698
+ }
699
+ ts.forEachChild(node, visit);
700
+ };
701
+ visit(body);
702
+ return ok;
703
+ }
584
704
  /** Parses the options object literal of a stream constructor. `which`
585
705
  * names the callbacks this class accepts, in canonical flag order. */
586
706
  function parseStreamOptions(lowerer, ctorName, arg, thisType, accepted, loc,
@@ -601,11 +721,19 @@ fallbackCb) {
601
721
  badEncoding: null,
602
722
  pushEncoding: null,
603
723
  signal: null,
724
+ valueDecls: [],
725
+ decodeStringsValue: null,
726
+ defaultEncodingValue: null,
604
727
  readableObject: false,
605
728
  writableObject: false,
729
+ decodeStringsOff: false,
730
+ writeEncoding: null,
731
+ extras: [],
606
732
  flags: 0,
607
733
  cbs: [],
608
734
  };
735
+ const extraNames = streamExtraCallbacks(`%${ctorName}`);
736
+ const writableClass = ctorName !== "Readable";
609
737
  const followed = followOptionsArg(lowerer, arg);
610
738
  const optsLit = followed === null ? undefined : followed;
611
739
  if (optsLit !== undefined && !ts.isObjectLiteralExpression(optsLit)) {
@@ -617,6 +745,22 @@ fallbackCb) {
617
745
  const objectModes = streamObjectModes(lowerer, ctorName, optsLit !== undefined && ts.isObjectLiteralExpression(optsLit) ? optsLit : undefined);
618
746
  out.readableObject = objectModes.readable;
619
747
  out.writableObject = objectModes.writable;
748
+ // the chunk an option callback receives can be another value than a
749
+ // Buffer (literalWritesValues), read before any callback lowers
750
+ const writesValues = optsLit !== undefined &&
751
+ ts.isObjectLiteralExpression(optsLit) &&
752
+ literalWritesValues(optsLit, ctorName !== "Readable" && ctorName !== "Writable");
753
+ // In JavaScript a decodeStrings value known only at run time may let
754
+ // string writes through, so the write and transform callbacks' chunk
755
+ // parameters read as any (the declarations type it Buffer).
756
+ const runtimeDecodeStrings = optsLit !== undefined &&
757
+ isJsSourceFile(ts.getSourceFileOfNode(optsLit)) &&
758
+ ts.isObjectLiteralExpression(optsLit) &&
759
+ (optsLit.properties?.nodes ?? []).some((p) => ts.isPropertyAssignment(p) &&
760
+ (ts.isIdentifier(p.name) || ts.isStringLiteral(p.name)) &&
761
+ p.name.text === "decodeStrings" &&
762
+ p.initializer.kind !== ts.SyntaxKind.TrueKeyword &&
763
+ p.initializer.kind !== ts.SyntaxKind.FalseKeyword);
620
764
  if (out.readableObject)
621
765
  out.hwmR = numLit(16, loc);
622
766
  if (out.writableObject)
@@ -634,7 +778,7 @@ fallbackCb) {
634
778
  lowerer.unsupported("SC1090", prop, `computed or spread members in ${ctorName} options`);
635
779
  }
636
780
  // Callback members: shorthand methods and function-valued properties.
637
- if (accepted.includes(name)) {
781
+ if (accepted.includes(name) || extraNames.includes(name)) {
638
782
  let fnNode = null;
639
783
  if (ts.isMethodDeclaration(prop))
640
784
  fnNode = prop;
@@ -659,6 +803,7 @@ fallbackCb) {
659
803
  }
660
804
  const full = tuples[name].map((t) => (t === "fn" ? null : t));
661
805
  const pinned = out.readableObject ? null : pinAnyDataCompletion(lowerer, name, fnNode);
806
+ let anyChunk;
662
807
  try {
663
808
  // The completion-callback tail: typed by the checker contextually;
664
809
  // accept the user's declared func type as the tuple's entry so the
@@ -673,11 +818,22 @@ fallbackCb) {
673
818
  }
674
819
  return declared ?? funcOf([], VOID);
675
820
  });
676
- present.set(name, lowerStreamCallback(lowerer, name, fnNode, thisType, tuple, out.writableObject));
821
+ const chunkParam = runtimeDecodeStrings && (name === "write" || name === "transform")
822
+ ? fnNode.parameters.nodes[0]
823
+ : undefined;
824
+ anyChunk =
825
+ chunkParam !== undefined && ts.isIdentifier(chunkParam.name) && !chunkParam.type
826
+ ? lowerer.checker.getSymbolAtLocation(chunkParam.name)
827
+ : undefined;
828
+ if (anyChunk)
829
+ lowerer.anySymbols.add(anyChunk);
830
+ present.set(name, lowerStreamCallback(lowerer, name, fnNode, thisType, tuple, writesValues));
677
831
  }
678
832
  finally {
679
833
  if (pinned)
680
834
  lowerer.stageSourceTypes.delete(pinned);
835
+ if (anyChunk)
836
+ lowerer.anySymbols.delete(anyChunk);
681
837
  }
682
838
  continue;
683
839
  }
@@ -720,9 +876,29 @@ fallbackCb) {
720
876
  // read up front (streamObjectModes)
721
877
  break;
722
878
  case "decodeStrings": {
879
+ // WritableState's `options.decodeStrings === false`; a Readable
880
+ // ignores the option
723
881
  if (value.kind === ts.SyntaxKind.TrueKeyword)
724
882
  break; // Node's default
725
- lowerer.noLowering(`${ctorName} with decodeStrings: false`, prop, "chunks always decode to Buffers here (Node's default)");
883
+ if (value.kind === ts.SyntaxKind.FalseKeyword) {
884
+ if (writableClass)
885
+ out.decodeStringsOff = true;
886
+ break;
887
+ }
888
+ // a runtime value: WritableState's `options.decodeStrings !== false`
889
+ if (optsLit !== undefined &&
890
+ ts.isObjectLiteralExpression(optsLit) &&
891
+ !earlierOptionsPure(optsLit, prop, value))
892
+ lowerer.noLowering(`the ${ctorName} option 'decodeStrings' with a runtime value that an earlier option value could observe or change`, prop, "compute the option values into consts before the options object (the runtime value is read before the other options run)");
893
+ const local = lowerer.declareHiddenLocal("%decodeStrings", DYN);
894
+ out.valueDecls.push({
895
+ kind: "varDecl",
896
+ localId: local.id,
897
+ init: lowerer.lowerExprExpecting(value, DYN),
898
+ loc,
899
+ });
900
+ if (writableClass)
901
+ out.decodeStringsValue = varRef(local.id, DYN, loc);
726
902
  break;
727
903
  }
728
904
  case "encoding": {
@@ -758,12 +934,45 @@ fallbackCb) {
758
934
  out.badEncoding = t.value;
759
935
  break;
760
936
  }
761
- if (t.isStringLiteralType() && ctorName === "Readable") {
762
- out.pushEncoding = knownBufEncoding(t.value);
937
+ if (t.isStringLiteralType()) {
938
+ // the readable side decodes push(string) with it, and the
939
+ // writable side encodes write(string) with it (the name as
940
+ // written reaches _write when decodeStrings is false)
941
+ if (ctorName !== "Writable" && !out.readableObject)
942
+ out.pushEncoding = knownBufEncoding(t.value);
943
+ if (writableClass)
944
+ out.writeEncoding = t.value;
763
945
  break;
764
946
  }
765
947
  }
766
- lowerer.noLowering(`the ${ctorName} option 'defaultEncoding'`, prop, "utf8 (the default) is the supported write-side string encoding");
948
+ {
949
+ // a runtime value: null, undefined and utf8 are the default,
950
+ // another name Buffer.isEncoding knows is kept as given, and any
951
+ // other value throws ERR_UNKNOWN_ENCODING before construction
952
+ if (optsLit !== undefined &&
953
+ ts.isObjectLiteralExpression(optsLit) &&
954
+ !earlierOptionsPure(optsLit, prop, value))
955
+ lowerer.noLowering(`the ${ctorName} option 'defaultEncoding' with a runtime value that an earlier option value could observe or change`, prop, "compute the option values into consts before the options object (the runtime value is read before the other options run)");
956
+ const local = lowerer.declareHiddenLocal("%defaultEncoding", DYN);
957
+ const ref = varRef(local.id, DYN, loc);
958
+ out.valueDecls.push({
959
+ kind: "varDecl",
960
+ localId: local.id,
961
+ init: lowerer.lowerExprExpecting(value, DYN),
962
+ loc,
963
+ }, {
964
+ kind: "exprStmt",
965
+ expr: {
966
+ kind: "libCall",
967
+ fn: "stream.checkEncodingOption",
968
+ args: [ref],
969
+ type: VOID,
970
+ loc,
971
+ },
972
+ loc,
973
+ });
974
+ out.defaultEncodingValue = ref;
975
+ }
767
976
  break;
768
977
  case "signal": {
769
978
  // `if (options.signal) addAbortSignal(options.signal, this)`:
@@ -813,11 +1022,8 @@ fallbackCb) {
813
1022
  : "the final callback is supported on Writable and Duplex");
814
1023
  break;
815
1024
  }
816
- case "construct":
817
- lowerer.noLowering(`the ${ctorName} option 'construct'`, prop, "deferred construction (_construct) is not lowered yet");
818
- break;
819
1025
  case "writev":
820
- lowerer.noLowering(`the ${ctorName} option 'writev'`, prop, "batched writes (_writev) are not lowered yet — writes deliver one chunk at a time");
1026
+ lowerer.noLowering(`the ${ctorName} option 'writev'`, prop, "a Readable has no writable side");
821
1027
  break;
822
1028
  default:
823
1029
  lowerer.noLowering(`the ${ctorName} option '${name}'`, prop);
@@ -833,6 +1039,11 @@ fallbackCb) {
833
1039
  out.cbs.push(cb);
834
1040
  }
835
1041
  }
1042
+ for (const name of extraNames) {
1043
+ const cb = present.get(name) ?? fallbackCb?.(name) ?? null;
1044
+ if (cb)
1045
+ out.extras.push({ name, cb });
1046
+ }
836
1047
  return out;
837
1048
  }
838
1049
  /** The head + flags argument list of a stream construction/initialization
@@ -859,10 +1070,66 @@ function streamCtorArgs(lowerer, cls, arg, thisType, loc, fallbackCb) {
859
1070
  badEncoding: o.badEncoding,
860
1071
  pushEncoding: o.pushEncoding,
861
1072
  signal: o.signal,
1073
+ valueDecls: o.valueDecls,
1074
+ decodeStringsValue: o.decodeStringsValue,
1075
+ defaultEncodingValue: o.defaultEncodingValue,
862
1076
  readableObject: o.readableObject,
863
1077
  writableObject: o.writableObject,
1078
+ decodeStringsOff: o.decodeStringsOff,
1079
+ writeEncoding: o.writeEncoding,
1080
+ extras: o.extras,
864
1081
  };
865
1082
  }
1083
+ /** The follow-ups a stream's options apply over the fresh stream `recv`
1084
+ * after the chained ones: decodeStrings, the writable defaultEncoding,
1085
+ * then writev and construct (Node's constructors schedule construct
1086
+ * last). */
1087
+ function streamOptionFollowUps(parsed, recv, thisType, loc, writevFn = "stream.setWritev") {
1088
+ const out = [];
1089
+ if (parsed.decodeStringsOff) {
1090
+ out.push({
1091
+ kind: "exprStmt",
1092
+ expr: {
1093
+ kind: "libCall",
1094
+ fn: "stream.decodeStrings",
1095
+ args: [recv(), boolLit(false, loc)],
1096
+ type: thisType,
1097
+ loc,
1098
+ },
1099
+ loc,
1100
+ });
1101
+ }
1102
+ if (parsed.writeEncoding !== null) {
1103
+ out.push({
1104
+ kind: "exprStmt",
1105
+ expr: {
1106
+ kind: "libCall",
1107
+ fn: "writable.writeEncoding",
1108
+ args: [recv(), strLit(parsed.writeEncoding, loc)],
1109
+ type: thisType,
1110
+ loc,
1111
+ },
1112
+ loc,
1113
+ });
1114
+ }
1115
+ for (const name of ["writev", "construct"]) {
1116
+ const extra = parsed.extras.find((x) => x.name === name);
1117
+ if (!extra)
1118
+ continue;
1119
+ out.push({
1120
+ kind: "exprStmt",
1121
+ expr: {
1122
+ kind: "libCall",
1123
+ fn: name === "writev" ? writevFn : "stream.setConstruct",
1124
+ args: [recv(), extra.cb],
1125
+ type: VOID,
1126
+ loc,
1127
+ },
1128
+ loc,
1129
+ });
1130
+ }
1131
+ return out;
1132
+ }
866
1133
  /** `new Readable(opts?)` and friends — called from lowerNew once the
867
1134
  * callee resolved to a builtin stream class. */
868
1135
  export function lowerStreamNew(lowerer, expr, info) {
@@ -906,6 +1173,18 @@ export function lowerStreamNew(lowerer, expr, info) {
906
1173
  loc,
907
1174
  };
908
1175
  }
1176
+ const values = optionValuesArgs(parsed, loc);
1177
+ if (values) {
1178
+ // runtime decodeStrings and defaultEncoding values (answers the
1179
+ // receiver +1)
1180
+ built = {
1181
+ kind: "libCall",
1182
+ fn: "stream.optionValues",
1183
+ args: [built, ...values],
1184
+ type: thisType,
1185
+ loc,
1186
+ };
1187
+ }
909
1188
  if (parsed.pushEncoding !== null) {
910
1189
  // { defaultEncoding }: chain the push-side decode default (the
911
1190
  // setEncoding chaining shape — answers the receiver +1).
@@ -928,17 +1207,45 @@ export function lowerStreamNew(lowerer, expr, info) {
928
1207
  loc,
929
1208
  };
930
1209
  }
931
- if (parsed.signal === null)
932
- return built;
933
- // { signal }: addAbortSignal over the fresh stream (answers it +1).
1210
+ if (parsed.signal !== null) {
1211
+ // { signal }: addAbortSignal over the fresh stream (answers it +1).
1212
+ built = {
1213
+ kind: "libCall",
1214
+ fn: "stream.addAbortSignal",
1215
+ args: [built, parsed.signal],
1216
+ type: thisType,
1217
+ loc,
1218
+ };
1219
+ }
1220
+ if (!parsed.decodeStringsOff && parsed.writeEncoding === null && parsed.extras.length === 0) {
1221
+ return parsed.valueDecls.length > 0
1222
+ ? { kind: "seqExpr", stmts: parsed.valueDecls, result: built, type: thisType, loc }
1223
+ : built;
1224
+ }
1225
+ const fresh = lowerer.declareHiddenLocal("%stream", thisType);
1226
+ const followUps = streamOptionFollowUps(parsed, () => varRef(fresh.id, thisType, loc), thisType, loc);
934
1227
  return {
935
- kind: "libCall",
936
- fn: "stream.addAbortSignal",
937
- args: [built, parsed.signal],
1228
+ kind: "seqExpr",
1229
+ stmts: [
1230
+ ...parsed.valueDecls,
1231
+ { kind: "varDecl", localId: fresh.id, init: built, loc },
1232
+ ...followUps,
1233
+ ],
1234
+ result: varRef(fresh.id, thisType, loc),
938
1235
  type: thisType,
939
1236
  loc,
940
1237
  };
941
1238
  }
1239
+ /** stream.optionValues' (decodeStrings, defaultEncoding) arguments, or
1240
+ * null when both options are literals or absent. */
1241
+ function optionValuesArgs(parsed, loc) {
1242
+ if (parsed.decodeStringsValue === null && parsed.defaultEncodingValue === null)
1243
+ return null;
1244
+ return [
1245
+ parsed.decodeStringsValue ?? dynUndefinedExpr(loc),
1246
+ parsed.defaultEncodingValue ?? dynUndefinedExpr(loc),
1247
+ ];
1248
+ }
942
1249
  /** True when a value of Node's argument type `passed` widens implicitly
943
1250
  * into the declared parameter type `declared` (one of its union arms, or a
944
1251
  * sub-union of it). */
@@ -1000,9 +1307,11 @@ function streamMethodWrapper(lowerer, info, which, methodName, thisType, loc, bl
1000
1307
  else if (!typeEquals(got.type, want)) {
1001
1308
  // A write or transform encoding of another type (`string | null |
1002
1309
  // undefined`) converts from the encoding argument's value: an
1003
- // object-mode write passes undefined, null or the name.
1004
- if (i === 1 &&
1005
- (which === "write" || which === "transform") &&
1310
+ // object-mode write passes undefined, null or the name. A chunk of
1311
+ // another type (`string | Buffer` under decodeStrings: false)
1312
+ // converts from the chunk's value.
1313
+ if ((i === 1 || (i === 0 && (chunkValues || which === "writev"))) &&
1314
+ (which === "write" || which === "transform" || which === "writev") &&
1006
1315
  got.type.kind !== "func" &&
1007
1316
  canConvertToDyn(got.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
1008
1317
  paramTypes.push(got.type);
@@ -1019,8 +1328,7 @@ function streamMethodWrapper(lowerer, info, which, methodName, thisType, loc, bl
1019
1328
  // chunk's value when the method runs (an object-mode side's value),
1020
1329
  // or throws a TypeError there; a byte-mode side's chunk is a Buffer.
1021
1330
  if (i === 0 &&
1022
- chunkValues &&
1023
- (which === "write" || which === "transform") &&
1331
+ (which === "writev" || (chunkValues && (which === "write" || which === "transform"))) &&
1024
1332
  got.type.kind !== "func" &&
1025
1333
  canConvertToDyn(got.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
1026
1334
  paramTypes.push(got.type);
@@ -1113,7 +1421,7 @@ export function lowerStreamSuperCall(lowerer, info, base, argNodes, thisLocal, l
1113
1421
  (!ts.isObjectLiteralExpression(optsNode) ||
1114
1422
  literalWritesValues(optsNode, cls !== "%Readable" && cls !== "%Writable"));
1115
1423
  const fallback = (name) => {
1116
- const methodName = UNDERSCORE_METHODS.get(name);
1424
+ const methodName = UNDERSCORE_METHODS.get(name) ?? STREAM_EXTRA_CALLBACKS.get(name);
1117
1425
  return methodName
1118
1426
  ? streamMethodWrapper(lowerer, info, name, methodName, thisType, loc, blame, chunkValues)
1119
1427
  : null;
@@ -1134,6 +1442,14 @@ export function lowerStreamSuperCall(lowerer, info, base, argNodes, thisLocal, l
1134
1442
  cbs.push(cb);
1135
1443
  }
1136
1444
  }
1445
+ // The class's _writev and _construct ride the same way: an options
1446
+ // writev or construct (applied by initDyn) wins.
1447
+ const extras = [];
1448
+ for (const name of streamExtraCallbacks(cls)) {
1449
+ const cb = fallback(name);
1450
+ if (cb)
1451
+ extras.push({ name, cb });
1452
+ }
1137
1453
  return [
1138
1454
  {
1139
1455
  kind: "exprStmt",
@@ -1146,6 +1462,7 @@ export function lowerStreamSuperCall(lowerer, info, base, argNodes, thisLocal, l
1146
1462
  },
1147
1463
  loc,
1148
1464
  },
1465
+ ...streamOptionFollowUps({ decodeStringsOff: false, writeEncoding: null, extras }, thisRef, thisType, loc, "stream.setWritevDefault"),
1149
1466
  ];
1150
1467
  }
1151
1468
  }
@@ -1162,6 +1479,7 @@ export function lowerStreamSuperCall(lowerer, info, base, argNodes, thisLocal, l
1162
1479
  ];
1163
1480
  }
1164
1481
  const out = [
1482
+ ...parsed.valueDecls,
1165
1483
  {
1166
1484
  kind: "exprStmt",
1167
1485
  expr: {
@@ -1187,6 +1505,20 @@ export function lowerStreamSuperCall(lowerer, info, base, argNodes, thisLocal, l
1187
1505
  loc,
1188
1506
  });
1189
1507
  }
1508
+ const values = optionValuesArgs(parsed, loc);
1509
+ if (values) {
1510
+ out.push({
1511
+ kind: "exprStmt",
1512
+ expr: {
1513
+ kind: "libCall",
1514
+ fn: "stream.optionValues",
1515
+ args: [thisRef(), ...values],
1516
+ type: thisType,
1517
+ loc,
1518
+ },
1519
+ loc,
1520
+ });
1521
+ }
1190
1522
  if (parsed.pushEncoding !== null) {
1191
1523
  out.push({
1192
1524
  kind: "exprStmt",
@@ -1226,6 +1558,7 @@ export function lowerStreamSuperCall(lowerer, info, base, argNodes, thisLocal, l
1226
1558
  loc,
1227
1559
  });
1228
1560
  }
1561
+ out.push(...streamOptionFollowUps(parsed, thisRef, thisType, loc));
1229
1562
  return out;
1230
1563
  }
1231
1564
  /* ── the underscore-method assignment surface ─────────────────────────── */
@@ -1269,14 +1602,16 @@ export function lowerStreamUnderscoreAssign(lowerer, expr) {
1269
1602
  const loc = locOf(expr);
1270
1603
  const cls = base.def.name;
1271
1604
  const clsName = cls.slice(1);
1272
- if (methodName === "_writev" || methodName === "_construct") {
1273
- lowerer.noLowering(`assigning '${methodName}' on a ${clsName}`, expr.left, methodName === "_writev"
1274
- ? "batched writes (_writev) are not lowered yet — writes deliver one chunk at a time"
1275
- : "deferred construction (_construct) is not lowered yet");
1276
- }
1277
- const which = optionByMethod.get(methodName);
1605
+ // `_construct`: Node's constructNT reads it on the construction tick, so
1606
+ // an assignment before that tick replaces a scheduled hook
1607
+ // (stream.replaceConstruct) and has no effect after it or without one.
1608
+ const which = methodName === "_writev"
1609
+ ? "writev"
1610
+ : methodName === "_construct"
1611
+ ? "construct"
1612
+ : optionByMethod.get(methodName);
1278
1613
  const { accepted } = streamCtorShape(cls);
1279
- if (!accepted.includes(which)) {
1614
+ if (!accepted.includes(which) && !streamExtraCallbacks(cls).includes(which)) {
1280
1615
  lowerer.noLowering(`assigning '${methodName}' on a ${clsName}`, expr.left, `a ${clsName}'s machinery consumes ${accepted.map((a) => `'${UNDERSCORE_METHODS.get(a)}'`).join("/")} — '${methodName}' would ride machinery with no lowering here`);
1281
1616
  }
1282
1617
  const thisType = recv.type;
@@ -1307,6 +1642,8 @@ export function lowerStreamUnderscoreAssign(lowerer, expr) {
1307
1642
  destroy: "stream.setDestroy",
1308
1643
  transform: "stream.setTransform",
1309
1644
  flush: "stream.setFlush",
1645
+ writev: "stream.setWritev",
1646
+ construct: "stream.replaceConstruct",
1310
1647
  };
1311
1648
  return {
1312
1649
  kind: "exprStmt",
@@ -1364,25 +1701,165 @@ export function lowerStreamStaticCall(lowerer, call, access) {
1364
1701
  }
1365
1702
  }
1366
1703
  }
1704
+ if (member === "fromWeb" && cls === "%Readable")
1705
+ return lowerReadableFromWeb(lowerer, call, loc);
1367
1706
  if (member !== "from") {
1368
1707
  lowerer.noLowering(`${cls.slice(1)}.${member}`, call);
1369
1708
  }
1709
+ if (cls === "%Duplex") {
1710
+ const fromArgs = call.arguments.nodes;
1711
+ if (fromArgs.length !== 1) {
1712
+ lowerer.noLowering(`Duplex.from with ${fromArgs.length} arguments`, call, "Duplex.from takes one body");
1713
+ }
1714
+ return duplexFromBody(lowerer, fromArgs[0], locOf(call));
1715
+ }
1370
1716
  if (cls !== "%Readable") {
1371
- lowerer.noLowering(`${cls.slice(1)}.from`, call, "Readable.from is the lowered form");
1717
+ lowerer.noLowering(`${cls.slice(1)}.from`, call, "Readable.from and Duplex.from are the lowered forms");
1372
1718
  }
1373
1719
  const args = call.arguments.nodes;
1374
1720
  if (args.length > 2) {
1375
1721
  lowerer.noLowering(`Readable.from with ${args.length} arguments`, call, "Readable.from(iterable, options) is the supported form");
1376
1722
  }
1377
- const type = { kind: "object", className: "%Readable" };
1723
+ return lowerFromSource(lowerer, call, args[0], args[1], false);
1724
+ }
1725
+ /** Readable.fromWeb(readableStream, options): Node's adapter over the web
1726
+ * stream's reader (scr_stream_from_web.c). The options are an object
1727
+ * literal: objectMode a boolean literal, highWaterMark a number, encoding
1728
+ * a literal name, and signal a variable or property read, applied with
1729
+ * addAbortSignal as the constructors do. */
1730
+ function lowerReadableFromWeb(lowerer, call, loc) {
1731
+ const args = call.arguments.nodes;
1732
+ const readable = { kind: "object", className: "%Readable" };
1733
+ if (args.length === 0 || args.length > 2 || args.some((a) => ts.isSpreadElement(a))) {
1734
+ lowerer.noLowering(`Readable.fromWeb with ${args.length} arguments`, call, "the supported form is Readable.fromWeb(readableStream, options)");
1735
+ }
1736
+ const web = lowerer.lowerExprExpecting(args[0], DYN);
1737
+ let objectMode = false;
1738
+ let hwm = numLit(-1, loc);
1739
+ let encoding = "";
1740
+ let signal = null;
1741
+ const options = args[1];
1742
+ if (options !== undefined) {
1743
+ if (!ts.isObjectLiteralExpression(options)) {
1744
+ lowerer.noLowering("Readable.fromWeb options that are not an object literal", options, "pass { objectMode, highWaterMark, encoding, signal } as an object literal");
1745
+ }
1746
+ for (const p of options.properties.nodes) {
1747
+ const key = (ts.isPropertyAssignment(p) || ts.isShorthandPropertyAssignment(p)) &&
1748
+ ts.isIdentifier(p.name)
1749
+ ? p.name.text
1750
+ : null;
1751
+ const value = key === null
1752
+ ? null
1753
+ : ts.isShorthandPropertyAssignment(p)
1754
+ ? p.name
1755
+ : p.initializer;
1756
+ if (key === "objectMode" && value) {
1757
+ if (value.kind === ts.SyntaxKind.TrueKeyword)
1758
+ objectMode = true;
1759
+ else if (value.kind !== ts.SyntaxKind.FalseKeyword)
1760
+ lowerer.noLowering("the Readable.fromWeb option 'objectMode' with this value", p, "write true or false");
1761
+ continue;
1762
+ }
1763
+ if (key === "highWaterMark" && value) {
1764
+ hwm = lowerer.lowerExprExpecting(value, F64);
1765
+ continue;
1766
+ }
1767
+ if (key === "encoding" && value) {
1768
+ const t = lowerer.typeOf(value);
1769
+ if (t.isStringLiteralType() && knownBufEncoding(t.value) === undefined) {
1770
+ return nodeThrowExpr(1, "ERR_INVALID_ARG_VALUE", `The property 'options.encoding' is invalid. Received '${t.value}'`, readable, loc);
1771
+ }
1772
+ encoding = bufEncoding(lowerer, "the Readable.fromWeb option 'encoding'", value);
1773
+ continue;
1774
+ }
1775
+ if (key === "signal" && value) {
1776
+ if (!sideEffectFreeOptionValue(value) && !pureRead(value)) {
1777
+ lowerer.noLowering("the Readable.fromWeb option 'signal' with this value", p, "pass a variable or property read (const signal = controller.signal)");
1778
+ }
1779
+ signal = lowerer.lowerExprExpecting(value, DYN);
1780
+ continue;
1781
+ }
1782
+ lowerer.noLowering(key === null
1783
+ ? "Readable.fromWeb options with this member"
1784
+ : `the Readable.fromWeb option '${key}'`, p, "objectMode, highWaterMark, encoding and signal are the Readable.fromWeb options");
1785
+ }
1786
+ }
1787
+ const built = {
1788
+ kind: "libCall",
1789
+ fn: "fetch.readableFromWeb",
1790
+ args: [web, boolLit(objectMode, loc), hwm, strLit(encoding, loc)],
1791
+ type: readable,
1792
+ loc,
1793
+ };
1794
+ if (signal === null)
1795
+ return built;
1796
+ return {
1797
+ kind: "libCall",
1798
+ fn: "stream.addAbortSignal",
1799
+ args: [built, signal],
1800
+ type: readable,
1801
+ loc,
1802
+ };
1803
+ }
1804
+ /** Readable.from(source, options), and for Duplex.from over an iterable
1805
+ * (duplex) duplexify.js's from(Duplexify, body, { objectMode: true,
1806
+ * writable: false }): the same builders with SCR_FROM_DUPLEX, answering a
1807
+ * Duplexify without a writable side. */
1808
+ function lowerFromSource(lowerer, at, srcNode, optsNode, duplex) {
1809
+ const loc = locOf(at);
1810
+ const call = at;
1811
+ const type = { kind: "object", className: duplex ? "%Duplex" : "%Readable" };
1812
+ const what = duplex ? "Duplex.from" : "Readable.from";
1813
+ // SCR_FROM_DUPLEX
1814
+ const duplexFlag = duplex ? 32 : 0;
1378
1815
  const js = isJsSourceFile(ts.getSourceFileOfNode(call));
1379
- const srcNode = args[0];
1380
- const optsNode = args[1];
1816
+ // A web ReadableStream is async iterable: from.js reads its iterator.
1817
+ // Duplex.from takes _duplexify({ readable: Readable.fromWeb(body) }).
1818
+ const webElem = srcNode ? staticReadableStreamElementType(lowerer, srcNode) : null;
1819
+ if (srcNode && webElem !== null && duplex) {
1820
+ const fromWeb = {
1821
+ kind: "libCall",
1822
+ fn: "fetch.readableFromWeb",
1823
+ args: [
1824
+ lowerer.lowerExprExpecting(srcNode, DYN),
1825
+ boolLit(false, loc),
1826
+ numLit(-1, loc),
1827
+ strLit("", loc),
1828
+ ],
1829
+ type: { kind: "object", className: "%Readable" },
1830
+ loc,
1831
+ };
1832
+ return { kind: "libCall", fn: "stream.duplexifyReadable", args: [fromWeb], type, loc };
1833
+ }
1834
+ if (srcNode && webElem !== null) {
1835
+ const source = lowerWebStreamSource(lowerer, srcNode, webElem, loc);
1836
+ const helpers = source ? fromGenHelpers(lowerer, source.out, loc) : null;
1837
+ const opts = optsNode ? fromOptions(lowerer, optsNode) : { flags: 0, hwm: null };
1838
+ if (!source || !helpers || !opts || !fromGenChunk(lowerer, webElem)) {
1839
+ lowerer.noLowering(`Readable.from over a web ReadableStream of '${lowerer.fmt(webElem)}'`, srcNode);
1840
+ }
1841
+ const hwm = opts.hwm ? lowerer.lowerExprExpecting(opts.hwm, F64) : numLit(0, loc);
1842
+ const gen = {
1843
+ kind: "callValue",
1844
+ callee: source.closure,
1845
+ args: [],
1846
+ type: source.out,
1847
+ loc,
1848
+ };
1849
+ return {
1850
+ kind: "libCall",
1851
+ fn: "readable.fromAsyncGen",
1852
+ args: [gen, ...helpers, numLit(opts.flags, loc), hwm],
1853
+ type,
1854
+ loc,
1855
+ };
1856
+ }
1381
1857
  const srcT = srcNode ? lowerer.mapTypeOf(lowerer.typeOf(srcNode)) : null;
1382
1858
  const typedElem = (t) => t?.kind === "string" ? "string" : t?.kind === "bytes" && t.elem === "u8" ? "bytes" : null;
1383
1859
  const single = typedElem(srcT);
1384
1860
  const elem = srcT?.kind === "array" ? typedElem(srcT.elem) : null;
1385
- const opts = optsNode ? fromOptions(lowerer, optsNode) : { flags: 0, hwm: null };
1861
+ const opts0 = optsNode ? fromOptions(lowerer, optsNode) : { flags: 0, hwm: null };
1862
+ const opts = opts0 ? { ...opts0, flags: opts0.flags | duplexFlag } : opts0;
1386
1863
  // A class whose [Symbol.iterator]() or [Symbol.asyncIterator]() is a
1387
1864
  // generator method: from.js calls the method when the stream is built,
1388
1865
  // then reads the generator like any other.
@@ -1473,13 +1950,15 @@ export function lowerStreamStaticCall(lowerer, call, access) {
1473
1950
  // The checked-dynamic form: the runtime takes from.js's ladder
1474
1951
  // (ERR_INVALID_ARG_TYPE for a non-iterable, the options, null values).
1475
1952
  const src = srcNode ? lowerer.lowerExprExpecting(srcNode, DYN) : dynUndefinedExpr(loc);
1953
+ if (duplex)
1954
+ return { kind: "libCall", fn: "stream.duplexFromDyn", args: [src], type, loc };
1476
1955
  const o = lowerOperatorValue(lowerer, optsNode, "Readable.from options", loc, true);
1477
1956
  return { kind: "libCall", fn: "readable.fromDyn", args: [src, o], type, loc };
1478
1957
  }
1479
1958
  if (srcNode && (single || elem)) {
1480
1959
  lowerer.noLowering("Readable.from with these options", optsNode, "an object literal with 'objectMode' (true or false) and a number 'highWaterMark' is supported");
1481
1960
  }
1482
- lowerer.noLowering(`Readable.from over a '${srcT ? lowerer.fmt(srcT) : "?"}' source`, srcNode ?? call, "the supported sources are a string or Buffer, and arrays, Sets, Maps, generators and class iterables of values that convert to checked-dynamic values");
1961
+ lowerer.noLowering(`${what} over a '${srcT ? lowerer.fmt(srcT) : "?"}' source`, srcNode ?? call, "the supported sources are a string or Buffer, and arrays, Sets, Maps, generators and class iterables of values that convert to checked-dynamic values");
1483
1962
  }
1484
1963
  /** Whether a generator's yields can be stream chunks: values that box
1485
1964
  * into checked-dynamic values (null included: the runtime destroys the
@@ -1960,6 +2439,24 @@ class StreamArgTypeThrow extends Error {
1960
2439
  this.expr = expr;
1961
2440
  }
1962
2441
  }
2442
+ /** Whether a checked-dynamic pipeline callback can take undefined as its
2443
+ * error: a callback that is already checked-dynamic, one without
2444
+ * parameters, and one whose first parameter (or rest element) type holds
2445
+ * undefined. */
2446
+ function callbackErrorAdmitsUndefined(lowerer, cb) {
2447
+ if (cb.kind !== "dynFrom" || cb.value.type.kind !== "func")
2448
+ return true;
2449
+ const fnT = cb.value.type;
2450
+ const first = fnT.params[0];
2451
+ if (first === undefined)
2452
+ return true;
2453
+ const p0 = fnT.rest === true && fnT.params.length === 1 && first.kind === "array" ? first.elem : first;
2454
+ if (p0.kind === "dyn" || p0.kind === "jsval" || p0.kind === "undefinedT")
2455
+ return true;
2456
+ if (p0.kind !== "union")
2457
+ return false;
2458
+ return lowerer.unions.get(p0.unionId)?.arms.some((a) => a.kind === "undefinedT") ?? false;
2459
+ }
1963
2460
  /** The finished/pipeline completion callback: an inline function lowers
1964
2461
  * through the option-callback machinery (leading `this`, an `Error |
1965
2462
  * null` prefix — Node binds the stream and passes the error); any other
@@ -2159,18 +2656,28 @@ export function lowerStreamModuleCall(lowerer, call, bi, loc) {
2159
2656
  : null;
2160
2657
  const stageArgs = opts ? args.slice(0, -1) : args;
2161
2658
  if (hasPipelineFunctionStage(lowerer, stageArgs)) {
2162
- if (opts) {
2163
- lowerer.noLowering("pipeline with function stages or iterable sources and options", lastArg, "pipeline(source, async function* (source) { ... }, destination) without options is supported");
2164
- }
2165
2659
  const staged = lowerPipelineStages(lowerer, stageArgs, loc);
2660
+ const type = staged.destPromise ?? { kind: "promise", inner: VOID };
2166
2661
  // A promise destination's value settles the result (same type).
2167
- return staged.wrap({
2168
- kind: "libCall",
2169
- fn: "sp.pipelineStages",
2170
- args: staged.args,
2171
- type: staged.destPromise ?? { kind: "promise", inner: VOID },
2172
- loc,
2173
- });
2662
+ if (!opts) {
2663
+ return staged.wrap({
2664
+ kind: "libCall",
2665
+ fn: "sp.pipelineStages",
2666
+ args: staged.args,
2667
+ type,
2668
+ loc,
2669
+ });
2670
+ }
2671
+ // the options: end, and the signal (flags bit 6)
2672
+ const flagsArg = staged.args[3];
2673
+ if (flagsArg.kind !== "numLit")
2674
+ throw new InternalCompilerError("pipeline flags not a literal");
2675
+ const args = [...staged.args, boolLit(opts.end, loc)];
2676
+ if (opts.signal) {
2677
+ args[3] = numLit(flagsArg.value | 64, loc);
2678
+ args.push(opts.signal);
2679
+ }
2680
+ return staged.wrap({ kind: "libCall", fn: "sp.pipelineStagesOpts", args, type, loc });
2174
2681
  }
2175
2682
  let streams;
2176
2683
  try {
@@ -2328,18 +2835,28 @@ export function lowerStreamModuleCall(lowerer, call, bi, loc) {
2328
2835
  }
2329
2836
  if (hasPipelineFunctionStage(lowerer, args.slice(0, -1))) {
2330
2837
  const staged = lowerPipelineStages(lowerer, args.slice(0, -1), loc);
2331
- if (staged.dest !== 0) {
2332
- // callback(err, value): the destination's value reaches the
2333
- // callback through the checked-dynamic path. Node answers an
2334
- // object-mode PassThrough here; reading it has no lowering.
2335
- if (!ts.isExpressionStatement(call.parent)) {
2336
- lowerer.noLowering("using the PassThrough that pipeline() answers for a function destination", call, "read the destination's value in the callback");
2337
- }
2838
+ const cbNode = args[args.length - 1];
2839
+ const cbParams = ts.isArrowFunction(cbNode) || ts.isFunctionExpression(cbNode)
2840
+ ? cbNode.parameters.nodes.length
2841
+ : 0;
2842
+ if (staged.dest === 2 || (staged.dest === 1 && cbParams > 1)) {
2843
+ // callback(err, value): the destination's value (undefined for
2844
+ // an async generator) reaches the callback through the
2845
+ // checked-dynamic path.
2338
2846
  const cb = lowerer.lowerExprExpecting(args[args.length - 1], DYN);
2847
+ // SCR_PIPE_TYPED_CB: a typed callback whose error parameter cannot
2848
+ // hold undefined takes null for no error; others take undefined.
2849
+ const stagedArgs = [...staged.args];
2850
+ const flagsArg = stagedArgs[3];
2851
+ if (flagsArg.kind !== "numLit")
2852
+ throw new InternalCompilerError("pipeline flags not a literal");
2853
+ if (!isJsSourceFile(ts.getSourceFileOfNode(call)) &&
2854
+ !callbackErrorAdmitsUndefined(lowerer, cb))
2855
+ stagedArgs[3] = numLit(flagsArg.value | 128, loc);
2339
2856
  return staged.wrap({
2340
2857
  kind: "libCall",
2341
2858
  fn: "stream.pipelineStagesDyn",
2342
- args: [...staged.args, cb],
2859
+ args: [...stagedArgs, cb],
2343
2860
  type: staged.last,
2344
2861
  loc,
2345
2862
  });
@@ -2372,8 +2889,12 @@ export function lowerStreamModuleCall(lowerer, call, bi, loc) {
2372
2889
  loc,
2373
2890
  };
2374
2891
  }
2375
- if (bi.member === "compose")
2376
- return lowerStreamCompose(lowerer, call, loc);
2892
+ if (bi.member === "compose") {
2893
+ if (call.arguments.nodes.some((a) => ts.isSpreadElement(a))) {
2894
+ lowerer.noLowering("compose with spread arguments", call, "pass the streams directly");
2895
+ }
2896
+ return lowerStreamCompose(lowerer, call.arguments.nodes, loc);
2897
+ }
2377
2898
  return null;
2378
2899
  }
2379
2900
  /** The function a pipeline() argument writes or names, or null when the
@@ -2424,6 +2945,9 @@ function hasPipelineFunctionStage(lowerer, args) {
2424
2945
  function iterableSourceType(lowerer, node) {
2425
2946
  if (pipelineStageFunction(lowerer, node) !== null)
2426
2947
  return null;
2948
+ const webElem = staticReadableStreamElementType(lowerer, node);
2949
+ if (webElem !== null)
2950
+ return webElem;
2427
2951
  let t;
2428
2952
  try {
2429
2953
  t = lowerer.mapTypeOf(lowerer.typeOf(node));
@@ -2446,7 +2970,229 @@ function iterableSourceType(lowerer, node) {
2446
2970
  * through an async-from-sync iterator, and an async generator that
2447
2971
  * yields the same elements resumes its consumer on the same ticks. Null
2448
2972
  * when the source has no such lowering. */
2973
+ /** A web ReadableStream source, as an async generator closure over its
2974
+ * reader: each pass awaits reader.read() and yields the chunk, as the
2975
+ * stream's async iterator does; leaving early cancels the stream (its
2976
+ * iterator's return()), and the reader's lock is released either way. */
2977
+ function lowerWebStreamSource(lowerer, node, elemT, loc) {
2978
+ const resultT = genResultRecord(elemT, VOID, lowerer.shapes, lowerer.unions);
2979
+ if (!resultT)
2980
+ return null;
2981
+ const valueField = lowerer.shapes.get(resultT.shapeId)?.fields.find((f) => f.name === "value");
2982
+ if (!valueField)
2983
+ return null;
2984
+ const out = {
2985
+ kind: "generator",
2986
+ async: true,
2987
+ yieldT: elemT,
2988
+ retT: VOID,
2989
+ nextT: UNDEFINED_T,
2990
+ };
2991
+ const genResultT = genResultRecord(elemT, VOID, lowerer.shapes, lowerer.unions);
2992
+ if (!genResultT)
2993
+ return null;
2994
+ const value = lowerer.lowerExprExpecting(node, DYN);
2995
+ const key = `webStreamSource:${typeKey(elemT)}`;
2996
+ let maker = lowerer.valueHelpers.get(key);
2997
+ if (!maker) {
2998
+ maker = `%stream.webStreamSource.${lowerer.valueHelpers.size}`;
2999
+ const genName = `${maker}.gen`;
3000
+ const frame = new LiftedFrame(loc);
3001
+ const srcId = frame.local("src", DYN, false);
3002
+ const readerId = frame.local("reader", DYN, false);
3003
+ const activeId = frame.local("active", BOOL, true);
3004
+ const promiseT = { kind: "promise", inner: resultT };
3005
+ const pId = frame.local("p", promiseT, false);
3006
+ const rId = frame.local("r", resultT, false);
3007
+ const reader = () => varRef(readerId, DYN, loc);
3008
+ const r = () => varRef(rId, resultT, loc);
3009
+ const setActive = (on) => ({
3010
+ kind: "assign",
3011
+ localId: activeId,
3012
+ value: { kind: "boolLit", value: on, type: BOOL, loc },
3013
+ loc,
3014
+ });
3015
+ const extracted = extractIteratorValue(lowerer, elemT, valueField.type, {
3016
+ kind: "recordGet",
3017
+ obj: r(),
3018
+ shapeId: resultT.shapeId,
3019
+ field: "value",
3020
+ type: valueField.type,
3021
+ loc,
3022
+ }, loc);
3023
+ if (!extracted)
3024
+ return null;
3025
+ const invoke = (method, args, calleeName) => ({
3026
+ kind: "dynInvoke",
3027
+ recv: reader(),
3028
+ method,
3029
+ calleeName,
3030
+ args,
3031
+ type: DYN,
3032
+ loc,
3033
+ });
3034
+ const body = [
3035
+ {
3036
+ kind: "varDecl",
3037
+ localId: readerId,
3038
+ init: {
3039
+ kind: "dynInvoke",
3040
+ recv: varRef(srcId, DYN, loc),
3041
+ method: "getReader",
3042
+ calleeName: "ReadableStream async iterator",
3043
+ args: [],
3044
+ type: DYN,
3045
+ loc,
3046
+ },
3047
+ loc,
3048
+ },
3049
+ {
3050
+ kind: "varDecl",
3051
+ localId: activeId,
3052
+ init: { kind: "boolLit", value: false, type: BOOL, loc },
3053
+ loc,
3054
+ },
3055
+ {
3056
+ kind: "tryCatch",
3057
+ tryBody: [
3058
+ {
3059
+ kind: "while",
3060
+ cond: { kind: "boolLit", value: true, type: BOOL, loc },
3061
+ body: [
3062
+ setActive(false),
3063
+ {
3064
+ kind: "varDecl",
3065
+ localId: pId,
3066
+ init: {
3067
+ kind: "libCall",
3068
+ fn: "fetch.readerRead",
3069
+ args: [reader()],
3070
+ type: promiseT,
3071
+ loc,
3072
+ },
3073
+ loc,
3074
+ },
3075
+ {
3076
+ kind: "varDecl",
3077
+ localId: rId,
3078
+ init: { kind: "awaitExpr", value: varRef(pId, promiseT, loc), type: resultT, loc },
3079
+ loc,
3080
+ },
3081
+ {
3082
+ kind: "if",
3083
+ cond: {
3084
+ kind: "recordGet",
3085
+ obj: r(),
3086
+ shapeId: resultT.shapeId,
3087
+ field: "done",
3088
+ type: BOOL,
3089
+ loc,
3090
+ },
3091
+ then: [{ kind: "break", loc }],
3092
+ else_: null,
3093
+ loc,
3094
+ },
3095
+ setActive(true),
3096
+ {
3097
+ kind: "exprStmt",
3098
+ expr: { kind: "yieldExpr", value: extracted, type: VOID, loc },
3099
+ loc,
3100
+ },
3101
+ ],
3102
+ loc,
3103
+ },
3104
+ ],
3105
+ catchBody: null,
3106
+ catchLocalId: null,
3107
+ finallyBody: [
3108
+ {
3109
+ kind: "tryCatch",
3110
+ tryBody: [
3111
+ {
3112
+ kind: "if",
3113
+ cond: varRef(activeId, BOOL, loc),
3114
+ then: [
3115
+ {
3116
+ kind: "exprStmt",
3117
+ expr: {
3118
+ kind: "libCall",
3119
+ fn: "async.awaitDyn",
3120
+ args: [
3121
+ invoke("cancel", [dynUndefinedExpr(loc)], "ReadableStream async iterator return"),
3122
+ ],
3123
+ type: DYN,
3124
+ loc,
3125
+ },
3126
+ loc,
3127
+ },
3128
+ ],
3129
+ else_: null,
3130
+ loc,
3131
+ },
3132
+ ],
3133
+ catchBody: null,
3134
+ catchLocalId: null,
3135
+ finallyBody: [
3136
+ {
3137
+ kind: "exprStmt",
3138
+ expr: invoke("releaseLock", [], "ReadableStream async iterator releaseLock"),
3139
+ loc,
3140
+ },
3141
+ ],
3142
+ loc,
3143
+ },
3144
+ ],
3145
+ loc,
3146
+ },
3147
+ { kind: "return", value: null, loc },
3148
+ ];
3149
+ const srcLocal = frame.locals.find((l) => l.id === srcId);
3150
+ srcLocal.boxed = true;
3151
+ lowerer.valueHelpers.set(key, maker);
3152
+ lowerer.liftedFns.push({
3153
+ name: genName,
3154
+ params: [],
3155
+ returnType: VOID,
3156
+ async: true,
3157
+ captures: [{ localId: srcId, name: "src", type: DYN }],
3158
+ locals: frame.locals,
3159
+ body,
3160
+ generator: { yieldT: elemT, nextT: UNDEFINED_T, resultType: genResultT },
3161
+ loc,
3162
+ });
3163
+ const makerSrc = { id: "src.0", name: "src", type: DYN, mutable: false, boxed: true };
3164
+ lowerer.liftedFns.push({
3165
+ name: maker,
3166
+ params: [{ localId: makerSrc.id, name: makerSrc.name, type: DYN }],
3167
+ returnType: funcOf([], out),
3168
+ locals: [makerSrc],
3169
+ body: [
3170
+ {
3171
+ kind: "return",
3172
+ value: {
3173
+ kind: "closure",
3174
+ fnName: genName,
3175
+ captures: [makerSrc.id],
3176
+ type: funcOf([], out),
3177
+ loc,
3178
+ },
3179
+ loc,
3180
+ },
3181
+ ],
3182
+ loc,
3183
+ });
3184
+ }
3185
+ return {
3186
+ closure: { kind: "call", callee: maker, args: [value], type: funcOf([], out), loc },
3187
+ out,
3188
+ };
3189
+ }
2449
3190
  function lowerIterableSource(lowerer, node, fn, loc) {
3191
+ if (fn === null) {
3192
+ const webElem = staticReadableStreamElementType(lowerer, node);
3193
+ if (webElem !== null)
3194
+ return lowerWebStreamSource(lowerer, node, webElem, loc);
3195
+ }
2450
3196
  let value;
2451
3197
  if (fn !== null) {
2452
3198
  if (fn.parameters.nodes.length > 0) {
@@ -2563,13 +3309,14 @@ function lowerIterableSource(lowerer, node, fn, loc) {
2563
3309
  /** pipeline(...) with function stages (pipeline.js). A function may
2564
3310
  * start the pipeline (a source function, called with `{ signal }`), may
2565
3311
  * end it (an async function whose promise's value reaches the callback or
2566
- * the promise), and runs of async generator functions may sit between
2567
- * streams; the last generator of a run that a stream follows is pumped
2568
- * into that stream. Each stage's `source` parameter is typed as what the
3312
+ * the promise, or an async generator function), and runs of async
3313
+ * generator functions may sit between streams; the last generator of a
3314
+ * run that a stream follows is pumped into that stream. A function
3315
+ * destination feeds the object-mode PassThrough that pipeline() answers. Each stage's `source` parameter is typed as what the
2569
3316
  * pipeline passes it: makeAsyncIterable answers the previous stream
2570
3317
  * itself (a Readable is async iterable) or the previous stage's
2571
3318
  * generator. */
2572
- function lowerPipelineStages(lowerer, stageArgs, loc) {
3319
+ function lowerPipelineStages(lowerer, stageArgs, loc, preLowered) {
2573
3320
  if (stageArgs.length > 52) {
2574
3321
  lowerer.noLowering("pipeline with function stages and more than 52 stages", stageArgs[52], "split the pipeline");
2575
3322
  }
@@ -2589,13 +3336,16 @@ function lowerPipelineStages(lowerer, stageArgs, loc) {
2589
3336
  const argOrder = [];
2590
3337
  for (let j = 0; j < stageArgs.length; j++) {
2591
3338
  const arg = stageArgs[j];
2592
- const fn = pipelineStageFunction(lowerer, arg);
3339
+ // compose's head and tail functions arrive as Duplex.from streams
3340
+ const fn = preLowered?.has(j) ? null : pipelineStageFunction(lowerer, arg);
2593
3341
  const syncSourceFn = j === 0 &&
2594
3342
  fn !== null &&
2595
3343
  !ts.isArrowFunction(fn) &&
2596
3344
  fn.asteriskToken !== undefined &&
2597
3345
  !(fn.modifiers?.nodes ?? []).some((m) => m.kind === ts.SyntaxKind.AsyncKeyword);
2598
- if (j === 0 && (syncSourceFn || (fn === null && iterableSourceType(lowerer, arg) !== null))) {
3346
+ if (j === 0 &&
3347
+ !preLowered?.has(j) &&
3348
+ (syncSourceFn || (fn === null && iterableSourceType(lowerer, arg) !== null))) {
2599
3349
  const source = lowerIterableSource(lowerer, arg, syncSourceFn ? fn : null, loc);
2600
3350
  if (source === null)
2601
3351
  lowerer.noLowering("pipeline over this source", arg);
@@ -2618,7 +3368,7 @@ function lowerPipelineStages(lowerer, stageArgs, loc) {
2618
3368
  }
2619
3369
  let stream;
2620
3370
  try {
2621
- stream = lowerStreamArg(lowerer, arg, "pipeline");
3371
+ stream = preLowered?.get(j) ?? lowerStreamArg(lowerer, arg, "pipeline");
2622
3372
  }
2623
3373
  catch (e) {
2624
3374
  if (e instanceof StreamArgTypeThrow)
@@ -2633,11 +3383,6 @@ function lowerPipelineStages(lowerer, stageArgs, loc) {
2633
3383
  const isLast = j === stageArgs.length - 1;
2634
3384
  const isAsync = (fn.modifiers?.nodes ?? []).some((m) => m.kind === ts.SyntaxKind.AsyncKeyword);
2635
3385
  const isGen = !ts.isArrowFunction(fn) && fn.asteriskToken !== undefined;
2636
- if (isLast && isGen) {
2637
- // pipeline() pumps it into an object-mode PassThrough, whose
2638
- // buffering decides when the pipeline finishes
2639
- lowerer.noLowering("a pipeline destination that is an async generator function", arg, "end the pipeline with a stream or an async function (source) { for await (const chunk of source) ... }");
2640
- }
2641
3386
  if (!isAsync || (!isGen && !isLast)) {
2642
3387
  lowerer.noLowering(isLast
2643
3388
  ? "a pipeline destination that is not an async function"
@@ -2665,6 +3410,16 @@ function lowerPipelineStages(lowerer, stageArgs, loc) {
2665
3410
  destPromise = lowered.out;
2666
3411
  srcT = null;
2667
3412
  }
3413
+ else if (isLast) {
3414
+ // an async generator destination: pumped into the object-mode
3415
+ // PassThrough that pipeline() answers
3416
+ const helpers = fromGenHelpers(lowerer, lowered.out, loc);
3417
+ if (!helpers)
3418
+ lowerer.noLowering("a pipeline destination with this generator type", arg);
3419
+ helperArgs.push(helpers[0], helpers[1], helpers[2]);
3420
+ dest = 1;
3421
+ srcT = null;
3422
+ }
2668
3423
  else {
2669
3424
  srcT = lowered.out;
2670
3425
  }
@@ -2690,7 +3445,8 @@ function lowerPipelineStages(lowerer, stageArgs, loc) {
2690
3445
  const bindArgs = (call) => argDecls.length === 0
2691
3446
  ? call
2692
3447
  : { kind: "seqExpr", stmts: argDecls, result: call, type: call.type, loc };
2693
- // SCR_PIPE_* flags: bits 1-2 the destination, bit 5 the signal and abort.
3448
+ // SCR_PIPE_* flags: bits 1-2 the destination, bit 5 the signal and
3449
+ // abort (bit 6, the promise form's options signal, is set by its caller).
2694
3450
  const flags = (dest << 1) | (mask !== 0 ? 32 : 0);
2695
3451
  const head = [
2696
3452
  numLit(streams.length, loc),
@@ -2702,7 +3458,7 @@ function lowerPipelineStages(lowerer, stageArgs, loc) {
2702
3458
  ...stages,
2703
3459
  ...helperArgs,
2704
3460
  ];
2705
- const last = dest === 2 ? { kind: "object", className: "%PassThrough" } : streams[streams.length - 1].type;
3461
+ const last = dest !== 0 ? { kind: "object", className: "%PassThrough" } : streams[streams.length - 1].type;
2706
3462
  if (mask === 0)
2707
3463
  return { args: head, last, dest, destPromise, wrap: bindArgs };
2708
3464
  // const ac = new AbortController(): its signal goes to the stages, and
@@ -2765,21 +3521,22 @@ function pipelineBoxHelper(lowerer, promiseT, loc) {
2765
3521
  }
2766
3522
  /** `() => ac.abort()` over the pipeline's AbortController: an interned
2767
3523
  * lifted function takes the controller and answers the closure. */
2768
- function pipelineAbortHelper(lowerer, ac, loc) {
2769
- const key = "pipelineAbort";
3524
+ function pipelineAbortHelper(lowerer, ac, loc, withReason = false) {
3525
+ const key = withReason ? "pipelineAbortReason" : "pipelineAbort";
2770
3526
  let name = lowerer.valueHelpers.get(key);
2771
- const abortT = funcOf([], VOID);
3527
+ const abortT = funcOf(withReason ? [DYN] : [], VOID);
2772
3528
  if (!name) {
2773
3529
  name = keyedHelperName(lowerer, "%stream.pipelineAbort", key);
2774
3530
  lowerer.valueHelpers.set(key, name);
2775
3531
  const acLocal = { id: "ac.0", name: "ac", type: DYN, mutable: false, boxed: true };
2776
3532
  const acRef = varRef(acLocal.id, DYN, loc);
3533
+ const reasonLocal = { id: "reason.0", name: "reason", type: DYN, mutable: false };
2777
3534
  lowerer.liftedFns.push({
2778
3535
  name: `${name}.abort`,
2779
- params: [],
3536
+ params: withReason ? [{ localId: reasonLocal.id, name: reasonLocal.name, type: DYN }] : [],
2780
3537
  returnType: VOID,
2781
3538
  captures: [{ localId: acLocal.id, name: acLocal.name, type: DYN }],
2782
- locals: [acLocal],
3539
+ locals: withReason ? [acLocal, reasonLocal] : [acLocal],
2783
3540
  body: [
2784
3541
  {
2785
3542
  kind: "exprStmt",
@@ -2788,7 +3545,7 @@ function pipelineAbortHelper(lowerer, ac, loc) {
2788
3545
  recv: acRef,
2789
3546
  method: "abort",
2790
3547
  calleeName: "ac.abort",
2791
- args: [],
3548
+ args: withReason ? [varRef(reasonLocal.id, DYN, loc)] : [],
2792
3549
  type: DYN,
2793
3550
  loc,
2794
3551
  },
@@ -2890,6 +3647,328 @@ function lowerStageFunction(lowerer, fn, srcT) {
2890
3647
  lowerer.implicitParamTypes = previousImplicit;
2891
3648
  }
2892
3649
  }
3650
+ /** The async generator a Duplex.from body iterates (duplexify.js's
3651
+ * fromAsyncGen source), as a closure the runtime calls with next() and
3652
+ * ack(): each pass awaits next() (the deferred a write or the final
3653
+ * settles), calls ack() (process.nextTick(cb) and a fresh deferred),
3654
+ * returns at the done marker, and yields the chunk. */
3655
+ function duplexSourceHelper(lowerer, genT, loc) {
3656
+ const nextT = funcOf([], { kind: "promise", inner: DYN });
3657
+ const ackT = funcOf([], VOID);
3658
+ const type = funcOf([nextT, ackT], genT);
3659
+ const key = "duplexSource";
3660
+ let name = lowerer.valueHelpers.get(key);
3661
+ if (!name) {
3662
+ const resultType = genResultRecord(DYN, VOID, lowerer.shapes, lowerer.unions);
3663
+ if (!resultType)
3664
+ return null;
3665
+ name = `%stream.duplexSource.${lowerer.valueHelpers.size}`;
3666
+ const frame = new LiftedFrame(loc);
3667
+ const nextId = frame.param("next", nextT);
3668
+ const ackId = frame.param("ack", ackT);
3669
+ const valueId = frame.local("value", DYN, false);
3670
+ const value = varRef(valueId, DYN, loc);
3671
+ const body = [
3672
+ {
3673
+ kind: "while",
3674
+ cond: { kind: "boolLit", value: true, type: BOOL, loc },
3675
+ body: [
3676
+ {
3677
+ kind: "varDecl",
3678
+ localId: valueId,
3679
+ init: {
3680
+ kind: "awaitExpr",
3681
+ value: {
3682
+ kind: "callValue",
3683
+ callee: varRef(nextId, nextT, loc),
3684
+ args: [],
3685
+ type: { kind: "promise", inner: DYN },
3686
+ loc,
3687
+ },
3688
+ type: DYN,
3689
+ loc,
3690
+ },
3691
+ loc,
3692
+ },
3693
+ {
3694
+ kind: "exprStmt",
3695
+ expr: {
3696
+ kind: "callValue",
3697
+ callee: varRef(ackId, ackT, loc),
3698
+ args: [],
3699
+ type: VOID,
3700
+ loc,
3701
+ },
3702
+ loc,
3703
+ },
3704
+ {
3705
+ kind: "if",
3706
+ cond: { kind: "libCall", fn: "stream.chunkDone", args: [value], type: BOOL, loc },
3707
+ then: [{ kind: "return", value: null, loc }],
3708
+ else_: null,
3709
+ loc,
3710
+ },
3711
+ { kind: "exprStmt", expr: { kind: "yieldExpr", value, type: VOID, loc }, loc },
3712
+ ],
3713
+ loc,
3714
+ },
3715
+ { kind: "return", value: null, loc },
3716
+ ];
3717
+ lowerer.valueHelpers.set(key, name);
3718
+ lowerer.liftedFns.push({
3719
+ name,
3720
+ params: frame.params,
3721
+ returnType: VOID,
3722
+ async: true,
3723
+ locals: frame.locals,
3724
+ body,
3725
+ generator: { yieldT: DYN, nextT: UNDEFINED_T, resultType },
3726
+ loc,
3727
+ });
3728
+ }
3729
+ return { kind: "closure", fnName: name, captures: [], type, loc };
3730
+ }
3731
+ /** Duplex.from(body) (duplexify.js) for the bodies with a lowering: an
3732
+ * async generator function, whose source generator the Duplex's writes
3733
+ * feed (stream.duplexFromGen); a duplex stream, which answers itself; and
3734
+ * the bodies Readable.from reads (iterables, strings, generators), as a
3735
+ * Duplexify that is not writable. */
3736
+ function duplexFromBody(lowerer, node, loc) {
3737
+ const duplex = { kind: "object", className: "%Duplex" };
3738
+ const fn = pipelineStageFunction(lowerer, node);
3739
+ if (fn !== null) {
3740
+ const isAsync = (fn.modifiers?.nodes ?? []).some((m) => m.kind === ts.SyntaxKind.AsyncKeyword);
3741
+ const isGen = !ts.isArrowFunction(fn) && fn.asteriskToken !== undefined;
3742
+ if (!isAsync) {
3743
+ lowerer.noLowering("Duplex.from over a function that is not async", node, "write the body as async function* (source) { ... } or async function (source) { ... }");
3744
+ }
3745
+ if (fn.parameters.nodes.length > 2) {
3746
+ lowerer.noLowering("a Duplex.from body with parameters after the options argument", fn.parameters.nodes[2], "the body takes the source and { signal }");
3747
+ }
3748
+ const sourceT = {
3749
+ kind: "generator",
3750
+ async: true,
3751
+ yieldT: DYN,
3752
+ retT: VOID,
3753
+ nextT: UNDEFINED_T,
3754
+ };
3755
+ const lowered = lowerStageFunction(lowerer, fn, sourceT);
3756
+ const params = lowered.closure.type.kind === "func" ? lowered.closure.type.params : [];
3757
+ // fromAsyncGen calls the body with { signal } of an AbortController
3758
+ // that destroy aborts with its error.
3759
+ let signalArgs = [];
3760
+ let signalDecls = [];
3761
+ if (params.length > 1) {
3762
+ if (params[1].kind !== "dyn") {
3763
+ lowerer.noLowering("a Duplex.from body's options argument with this type", fn.parameters.nodes[1], "type it as { signal: AbortSignal }");
3764
+ }
3765
+ const acLocal = lowerer.declareHiddenLocal("%duplexAbort", DYN);
3766
+ const ac = varRef(acLocal.id, DYN, loc);
3767
+ signalDecls = [
3768
+ {
3769
+ kind: "varDecl",
3770
+ localId: acLocal.id,
3771
+ init: { kind: "libCall", fn: "fetch.abortControllerNew", args: [], type: DYN, loc },
3772
+ loc,
3773
+ },
3774
+ ];
3775
+ signalArgs = [
3776
+ pipelineAbortHelper(lowerer, ac, loc, true),
3777
+ { kind: "dynKeyGet", value: ac, key: strLit("signal", loc), type: DYN, loc },
3778
+ ];
3779
+ }
3780
+ const withSignal = (fnName, args) => {
3781
+ const call = {
3782
+ kind: "libCall",
3783
+ fn: signalArgs.length > 0 ? `${fnName}Signal` : fnName,
3784
+ args: [...args, ...signalArgs],
3785
+ type: duplex,
3786
+ loc,
3787
+ };
3788
+ return signalDecls.length > 0
3789
+ ? { kind: "seqExpr", stmts: signalDecls, result: call, type: duplex, loc }
3790
+ : call;
3791
+ };
3792
+ if (!isGen) {
3793
+ // an async function: a writable Duplexify whose final waits for
3794
+ // the function's promise
3795
+ const asyncSource = duplexSourceHelper(lowerer, sourceT, loc);
3796
+ const out = lowered.out;
3797
+ if (out.kind !== "promise" || !asyncSource)
3798
+ return lowerer.noLowering("Duplex.from over a function with this return type", node);
3799
+ if (out.inner.kind !== "void" &&
3800
+ !canConvertToDyn(out.inner, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
3801
+ lowerer.noLowering(`Duplex.from over a function resolving to '${lowerer.fmt(out.inner)}'`, node);
3802
+ }
3803
+ return withSignal("stream.duplexFromAsyncFn", [
3804
+ lowered.closure,
3805
+ asyncSource,
3806
+ pipelineBoxHelper(lowerer, out, loc),
3807
+ ]);
3808
+ }
3809
+ const helpers = lowered.out.kind === "generator" ? fromGenHelpers(lowerer, lowered.out, loc) : null;
3810
+ const source = duplexSourceHelper(lowerer, sourceT, loc);
3811
+ if (!helpers || !source)
3812
+ lowerer.noLowering("Duplex.from over a function with this generator type", node);
3813
+ return withSignal("stream.duplexFromGen", [lowered.closure, source, ...helpers]);
3814
+ }
3815
+ // duplexify.js: a stream answers itself (a Duplex) or is wrapped; any
3816
+ // other body reads as from(Duplexify, body, { objectMode: true,
3817
+ // writable: false })
3818
+ let bodyT;
3819
+ try {
3820
+ bodyT = lowerer.mapTypeOf(lowerer.typeOf(node));
3821
+ }
3822
+ catch {
3823
+ bodyT = null;
3824
+ }
3825
+ const bodySides = bodyT?.kind === "object" ? streamSidesOf(lowerer, lowerer.classes.get(bodyT.className)) : null;
3826
+ let literal = node;
3827
+ while (ts.isParenthesizedExpression(literal))
3828
+ literal = literal.expression;
3829
+ if (bodySides === null && ts.isObjectLiteralExpression(literal))
3830
+ return duplexFromPair(lowerer, literal, loc);
3831
+ if (bodyT?.kind === "promise") {
3832
+ // a Duplexify that reads the promise's value
3833
+ const valueT = bodyT.inner;
3834
+ if (valueT.kind !== "void" &&
3835
+ !canConvertToDyn(valueT, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
3836
+ lowerer.noLowering(`Duplex.from over a promise of '${lowerer.fmt(valueT)}'`, node);
3837
+ }
3838
+ const promise = lowerer.lowerExpr(node);
3839
+ if (promise.type.kind !== "promise") {
3840
+ // a checked-dynamic promise (an untyped function's result)
3841
+ return {
3842
+ kind: "libCall",
3843
+ fn: "stream.duplexFromDyn",
3844
+ args: [lowerer.coerceToExpected(promise, DYN)],
3845
+ type: { kind: "object", className: "%Duplex" },
3846
+ loc,
3847
+ };
3848
+ }
3849
+ return {
3850
+ kind: "libCall",
3851
+ fn: "stream.duplexFromPromise",
3852
+ args: [
3853
+ promise,
3854
+ pipelineBoxHelper(lowerer, promise.type, loc),
3855
+ ],
3856
+ type: { kind: "object", className: "%Duplex" },
3857
+ loc,
3858
+ };
3859
+ }
3860
+ if (bodySides === null)
3861
+ return lowerFromSource(lowerer, node, node, undefined, true);
3862
+ let stream;
3863
+ try {
3864
+ stream = lowerStreamArg(lowerer, node, "compose");
3865
+ }
3866
+ catch (e) {
3867
+ if (e instanceof StreamArgTypeThrow)
3868
+ return e.expr;
3869
+ throw e;
3870
+ }
3871
+ const sides = stream.type.kind === "object"
3872
+ ? streamSidesOf(lowerer, lowerer.classes.get(stream.type.className))
3873
+ : null;
3874
+ // _duplexify({ readable }) / _duplexify({ writable })
3875
+ if (sides === "r" || sides === "w") {
3876
+ return {
3877
+ kind: "libCall",
3878
+ fn: sides === "r" ? "stream.duplexifyReadable" : "stream.duplexifyWritable",
3879
+ args: [stream],
3880
+ type: duplex,
3881
+ loc,
3882
+ };
3883
+ }
3884
+ if (sides !== "rw") {
3885
+ lowerer.noLowering("Duplex.from over a value of this type", node, "an async generator function, an iterable, a stream, or a { readable, writable } pair is the lowered body");
3886
+ }
3887
+ return stream;
3888
+ }
3889
+ /** Duplex.from({ readable, writable }): each member that is a stream with
3890
+ * that side is used as it is, any other member is Duplex.from of it, and
3891
+ * the pair reads and writes through _duplexify (duplexify.js). The
3892
+ * members evaluate in source order. */
3893
+ function duplexFromPair(lowerer, literal, loc) {
3894
+ const duplex = { kind: "object", className: "%Duplex" };
3895
+ const sides = {};
3896
+ // The members in source order; when that is not (readable, writable),
3897
+ // each value is bound to a local first, keeping the literal's
3898
+ // evaluation order.
3899
+ const members = [];
3900
+ for (const p of literal.properties.nodes) {
3901
+ const key = (ts.isPropertyAssignment(p) || ts.isShorthandPropertyAssignment(p)) && ts.isIdentifier(p.name)
3902
+ ? p.name.text
3903
+ : null;
3904
+ if (key !== "readable" && key !== "writable") {
3905
+ lowerer.noLowering("Duplex.from over an object with members other than readable and writable", p, "pass { readable, writable } with streams or Duplex.from bodies");
3906
+ }
3907
+ const value = ts.isShorthandPropertyAssignment(p)
3908
+ ? p.name
3909
+ : p.initializer;
3910
+ let valueT;
3911
+ try {
3912
+ valueT = lowerer.mapTypeOf(lowerer.typeOf(value));
3913
+ }
3914
+ catch {
3915
+ valueT = null;
3916
+ }
3917
+ const valueSides = valueT?.kind === "object"
3918
+ ? streamSidesOf(lowerer, lowerer.classes.get(valueT.className))
3919
+ : null;
3920
+ const wanted = key === "readable" ? "r" : "w";
3921
+ const direct = valueSides === "rw" || valueSides === wanted;
3922
+ members.push({
3923
+ key: key,
3924
+ value: direct
3925
+ ? lowerStreamArg(lowerer, value, "compose")
3926
+ : duplexFromBody(lowerer, value, loc),
3927
+ });
3928
+ }
3929
+ const decls = [];
3930
+ const inOrder = members.length <= 1 ||
3931
+ (members.length === 2 && members[0].key === "readable" && members[1].key === "writable");
3932
+ for (const m of members) {
3933
+ if (inOrder) {
3934
+ sides[m.key] = m.value;
3935
+ continue;
3936
+ }
3937
+ const local = lowerer.declareHiddenLocal(`%duplex${m.key}`, m.value.type);
3938
+ decls.push({ kind: "varDecl", localId: local.id, init: m.value, loc });
3939
+ sides[m.key] = varRef(local.id, m.value.type, loc);
3940
+ }
3941
+ const { readable, writable } = sides;
3942
+ if (readable && writable) {
3943
+ const call = {
3944
+ kind: "libCall",
3945
+ fn: "stream.duplexifyPair",
3946
+ args: [readable, writable],
3947
+ type: duplex,
3948
+ loc,
3949
+ };
3950
+ return decls.length > 0
3951
+ ? { kind: "seqExpr", stmts: decls, result: call, type: duplex, loc }
3952
+ : call;
3953
+ }
3954
+ if (decls.length > 0) {
3955
+ // a repeated member: every value is evaluated, the last one is used
3956
+ const one = readable ?? writable;
3957
+ const call = {
3958
+ kind: "libCall",
3959
+ fn: readable ? "stream.duplexifyReadable" : "stream.duplexifyWritable",
3960
+ args: [one],
3961
+ type: duplex,
3962
+ loc,
3963
+ };
3964
+ return { kind: "seqExpr", stmts: decls, result: call, type: duplex, loc };
3965
+ }
3966
+ if (readable)
3967
+ return { kind: "libCall", fn: "stream.duplexifyReadable", args: [readable], type: duplex, loc };
3968
+ if (writable)
3969
+ return { kind: "libCall", fn: "stream.duplexifyWritable", args: [writable], type: duplex, loc };
3970
+ return lowerer.noLowering("Duplex.from over an object with neither readable nor writable", literal, "pass { readable, writable } with streams or Duplex.from bodies");
3971
+ }
2893
3972
  /** The checker type of a stage's string or Buffer chunks, or null for
2894
3973
  * other chunk types. */
2895
3974
  function stageChunkCheckerType(lowerer, chunk, at) {
@@ -2905,18 +3984,51 @@ function stageChunkCheckerType(lowerer, chunk, at) {
2905
3984
  * classes fix which positions can be readable and writable; a stream in
2906
3985
  * the wrong position, a single Readable or Writable (Duplex.from wraps
2907
3986
  * those), and functions, iterables or web streams have no lowering. */
2908
- function lowerStreamCompose(lowerer, call, loc) {
2909
- const args = call.arguments.nodes;
3987
+ function lowerStreamCompose(lowerer, args, loc) {
2910
3988
  const duplex = { kind: "object", className: "%Duplex" };
2911
- if (args.some((a) => ts.isSpreadElement(a))) {
2912
- lowerer.noLowering("compose with spread arguments", call, "pass the streams directly");
2913
- }
2914
3989
  if (args.length === 0) {
2915
3990
  return nodeThrowExpr(1, "ERR_MISSING_ARGS", 'The "streams" argument must be specified', duplex, loc);
2916
3991
  }
3992
+ // A web ReadableStream first stage reads as an async iterable (the
3993
+ // pipeline's source), and alone it is Duplex.from of it; a later stage
3994
+ // would be Duplex.fromWeb of it.
3995
+ for (let i = 1; i < args.length; i++) {
3996
+ const a = args[i];
3997
+ // (typeOf on a stage function's name would report what collecting its
3998
+ // declared signature deferred)
3999
+ if (pipelineStageFunction(lowerer, a) !== null)
4000
+ continue;
4001
+ if (staticReadableStreamElementType(lowerer, a) !== null) {
4002
+ lowerer.noLowering("compose with a web ReadableStream after the first stage", a, "a web ReadableStream is supported as the first stage of compose or pipeline, and in Readable.from");
4003
+ }
4004
+ }
4005
+ // Duplex.from(stage) for one stage (duplexify.js), and for a function at
4006
+ // either end; functions between, an iterable first stage or a source
4007
+ // function run as the pipeline's function stages (compose.js)
4008
+ if (args.length === 1)
4009
+ return duplexFromBody(lowerer, args[0], loc);
4010
+ const last = args.length - 1;
4011
+ const isFn = (i) => pipelineStageFunction(lowerer, args[i]) !== null;
4012
+ const pre = new Map();
4013
+ if (isFn(0))
4014
+ pre.set(0, duplexFromBody(lowerer, args[0], loc));
4015
+ if (isFn(last))
4016
+ pre.set(last, duplexFromBody(lowerer, args[last], loc));
4017
+ const staged = args.some((_, i) => !pre.has(i) && isFn(i)) ||
4018
+ (!pre.has(0) && iterableSourceType(lowerer, args[0]) !== null);
4019
+ if (staged) {
4020
+ const lowered = lowerPipelineStages(lowerer, args, loc, pre);
4021
+ return lowered.wrap({
4022
+ kind: "libCall",
4023
+ fn: "stream.composeStages",
4024
+ args: lowered.args,
4025
+ type: duplex,
4026
+ loc,
4027
+ });
4028
+ }
2917
4029
  let streams;
2918
4030
  try {
2919
- streams = args.map((a) => lowerStreamArg(lowerer, a, "compose"));
4031
+ streams = args.map((a, i) => pre.get(i) ?? lowerStreamArg(lowerer, a, "compose"));
2920
4032
  }
2921
4033
  catch (e) {
2922
4034
  if (e instanceof StreamArgTypeThrow)
@@ -2948,6 +4060,28 @@ function lowerStreamCompose(lowerer, call, loc) {
2948
4060
  /* ── the method surface ───────────────────────────────────────────────── */
2949
4061
  /* ── the Readable operators ──────────────────────────────────────────── */
2950
4062
  /** The Readable operators (scr_stream_ops.c — Node's operators.js). */
4063
+ /** A stream member's receiver, lowered: a value the checker types as a
4064
+ * stream class whose lowering is checked-dynamic (a JavaScript binding
4065
+ * that holds an untyped call's result) or a union (an element read)
4066
+ * converts to that class, checked where the member runs. */
4067
+ function streamReceiverExpr(lowerer, node) {
4068
+ const value = lowerer.lowerExpr(node);
4069
+ if (value.type.kind !== "dyn" && value.type.kind !== "union")
4070
+ return value;
4071
+ let classT;
4072
+ try {
4073
+ classT = lowerer.mapTypeOf(lowerer.typeOf(node));
4074
+ }
4075
+ catch {
4076
+ return value;
4077
+ }
4078
+ if (classT?.kind !== "object" ||
4079
+ streamSidesOf(lowerer, lowerer.classes.get(classT.className)) === null)
4080
+ return value;
4081
+ return value.type.kind === "dyn"
4082
+ ? { kind: "dynCheck", value, type: classT, loc: value.loc }
4083
+ : lowerer.coerceToExpected(value, classT);
4084
+ }
2951
4085
  export const STREAM_OPERATORS = new Set([
2952
4086
  "map",
2953
4087
  "filter",
@@ -2961,50 +4095,125 @@ export const STREAM_OPERATORS = new Set([
2961
4095
  "find",
2962
4096
  "reduce",
2963
4097
  ]);
2964
- /** An operator callback as a checked-dynamic function value. Callbacks
2965
- * that declare the options argument ({ signal }) are refused: the
2966
- * AbortSignal Node passes there has no representation. map and flatMap
2967
- * callbacks must produce strings or Buffers (flatMap: arrays of them, or
2968
- * strings) — or checked-dynamic values, which the runtime checks. */
4098
+ /** An operator callback as a checked-dynamic function value, and whether
4099
+ * it declares the options argument ({ signal }), which the runtime then
4100
+ * passes. Its results convert to checked-dynamic values (flatMap's are
4101
+ * iterated with yield*). */
2969
4102
  function lowerOperatorCallback(lowerer, node, member, maxParams) {
2970
4103
  const loc = locOf(node);
2971
- const optionsParam = (at) => lowerer.noLowering(`${member} callbacks that declare the options argument`, at, "the { signal } argument has no lowering — take only the chunk");
2972
- if ((ts.isArrowFunction(node) || ts.isFunctionExpression(node)) &&
2973
- node.parameters.nodes.length > maxParams) {
2974
- optionsParam(node.parameters.nodes[maxParams]);
4104
+ const optionsParam = (at) => lowerer.noLowering(`${member} callbacks with parameters after the options argument`, at, "the callback takes the chunk and { signal }");
4105
+ let takesOptions = false;
4106
+ if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
4107
+ if (node.parameters.nodes.length > maxParams + 1)
4108
+ optionsParam(node.parameters.nodes[maxParams + 1]);
4109
+ takesOptions = node.parameters.nodes.length > maxParams;
2975
4110
  }
2976
4111
  const v = lowerer.lowerExpr(node);
4112
+ // A checked-dynamic function's parameters are not known here: it gets
4113
+ // the { signal } argument, as Node passes it to every callback.
2977
4114
  if (v.type.kind === "dyn")
2978
- return v;
4115
+ return { fn: v, takesOptions: true };
2979
4116
  if (v.type.kind !== "func" &&
2980
4117
  isJsSourceFile(ts.getSourceFileOfNode(node)) &&
2981
4118
  lowerer.dynConvertible(v.type)) {
2982
4119
  // JavaScript passes any value; Node's validateFunction throws.
2983
- return { kind: "dynFrom", value: v, type: DYN, loc };
4120
+ return { fn: { kind: "dynFrom", value: v, type: DYN, loc }, takesOptions: false };
2984
4121
  }
2985
4122
  if (v.type.kind !== "func") {
2986
4123
  lowerer.noLowering(`${member} with a '${lowerer.fmt(v.type)}' callback`, node, "pass a function");
2987
4124
  }
2988
- if (v.type.params.length > maxParams)
4125
+ if (v.type.params.length > maxParams + 1)
2989
4126
  optionsParam(node);
2990
- if (member === "map" || member === "flatMap") {
2991
- const ret = v.type.ret.kind === "promise" ? v.type.ret.inner : v.type.ret;
2992
- const chunk = (t) => t.kind === "string" || t.kind === "dyn" || (t.kind === "bytes" && t.elem === "u8");
2993
- const ok = member === "map"
2994
- ? chunk(ret)
2995
- : ret.kind === "dyn" || ret.kind === "string" || (ret.kind === "array" && chunk(ret.elem));
2996
- if (!ok) {
2997
- lowerer.noLowering(`${member} callbacks that return '${lowerer.fmt(ret)}'`, node, member === "map"
2998
- ? "map results are stream chunks: strings or Buffers (object-mode streams of other values are not lowered)"
2999
- : "flatMap results are arrays of strings or Buffers, or strings");
3000
- }
3001
- }
4127
+ if (v.type.params.length > maxParams)
4128
+ takesOptions = true;
3002
4129
  const getRecord = (id) => lowerer.shapes.get(id);
3003
4130
  const getUnion = (id) => lowerer.unions.get(id);
3004
4131
  if (!canBoxFuncIntoDyn(v.type, getRecord, getUnion)) {
3005
4132
  lowerer.noLowering(`${member} with this callback`, node, "the callback's parameter and result types must cross the checked-dynamic boundary");
3006
4133
  }
3007
- return { kind: "dynFrom", value: v, type: DYN, loc };
4134
+ const fn = { kind: "dynFrom", value: v, type: DYN, loc };
4135
+ // flatMap over a compiled generator: the runtime steps it through the
4136
+ // helpers over its type
4137
+ if (member === "flatMap" && v.type.ret.kind === "generator")
4138
+ return { fn, takesOptions, gen: v.type.ret };
4139
+ return { fn, takesOptions };
4140
+ }
4141
+ /** The operator callbacks' `{ signal }` (operators.js): map and the
4142
+ * operators over it pass `{ signal: AbortSignal.any([options?.signal]
4143
+ * .filter(Boolean)) }`. options is the operator's options value. */
4144
+ export function operatorSignalOptions(lowerer, options, loc) {
4145
+ void lowerer;
4146
+ const list = {
4147
+ kind: "libCall",
4148
+ fn: "readable.opSignalList",
4149
+ args: [options],
4150
+ type: DYN,
4151
+ loc,
4152
+ };
4153
+ const signal = { kind: "libCall", fn: "fetch.abortAny", args: [list], type: DYN, loc };
4154
+ return { kind: "libCall", fn: "readable.opSignalOpts", args: [signal], type: DYN, loc };
4155
+ }
4156
+ /** reduce's signal for a reducer that reads it (operators.js): `const ac
4157
+ * = new AbortController()`, `options.signal.addEventListener('abort', ()
4158
+ * => ac.abort(), { once: true })`, then reduceSignal's ac.abort() closure
4159
+ * and `{ signal: ac.signal }`. options is the bound options value. */
4160
+ export function reduceSignalSetup(lowerer, options, loc, declare = (name, type) => lowerer.declareHiddenLocal(name, type)) {
4161
+ const acLocal = declare("%opAbort", DYN);
4162
+ const ac = varRef(acLocal.id, DYN, loc);
4163
+ const sigLocal = declare("%opUserSignal", DYN);
4164
+ const sig = varRef(sigLocal.id, DYN, loc);
4165
+ const abort = pipelineAbortHelper(lowerer, ac, loc);
4166
+ const stmts = [
4167
+ {
4168
+ kind: "varDecl",
4169
+ localId: acLocal.id,
4170
+ init: { kind: "libCall", fn: "fetch.abortControllerNew", args: [], type: DYN, loc },
4171
+ loc,
4172
+ },
4173
+ {
4174
+ kind: "varDecl",
4175
+ localId: sigLocal.id,
4176
+ init: { kind: "libCall", fn: "readable.opSignalOf", args: [options], type: DYN, loc },
4177
+ loc,
4178
+ },
4179
+ {
4180
+ kind: "if",
4181
+ cond: { kind: "dynTest", test: "truthy", value: sig, type: BOOL, loc },
4182
+ then: [
4183
+ {
4184
+ kind: "exprStmt",
4185
+ expr: {
4186
+ kind: "dynInvoke",
4187
+ recv: sig,
4188
+ method: "addEventListener",
4189
+ calleeName: "options.signal.addEventListener",
4190
+ args: [
4191
+ { kind: "dynFrom", value: strLit("abort", loc), type: DYN, loc },
4192
+ { kind: "dynFrom", value: pipelineAbortHelper(lowerer, ac, loc), type: DYN, loc },
4193
+ { kind: "libCall", fn: "readable.opOnceOptions", args: [], type: DYN, loc },
4194
+ ],
4195
+ type: DYN,
4196
+ loc,
4197
+ },
4198
+ loc,
4199
+ },
4200
+ ],
4201
+ else_: null,
4202
+ loc,
4203
+ },
4204
+ ];
4205
+ const signal = {
4206
+ kind: "dynKeyGet",
4207
+ value: ac,
4208
+ key: strLit("signal", loc),
4209
+ type: DYN,
4210
+ loc,
4211
+ };
4212
+ return {
4213
+ stmts,
4214
+ abort,
4215
+ cbOpts: { kind: "libCall", fn: "readable.opSignalOpts", args: [signal], type: DYN, loc },
4216
+ };
3008
4217
  }
3009
4218
  /** An operator's options (or reduce's initial value): an object literal
3010
4219
  * becomes a checked-dynamic object, checked-dynamic values pass through,
@@ -3028,21 +4237,37 @@ function lowerOperatorValue(lowerer, node, what, loc, literalObject) {
3028
4237
  function lowerStreamOperator(lowerer, call, access, member) {
3029
4238
  const loc = locOf(call);
3030
4239
  const args = call.arguments.nodes;
3031
- const recv = lowerer.lowerExpr(access.expression);
4240
+ // JS order: the receiver, the callback, then the options; the
4241
+ // callback's { signal } is built over the options value afterwards.
4242
+ const decls = [];
4243
+ const bind = (value, name) => {
4244
+ const local = lowerer.declareHiddenLocal(name, value.type);
4245
+ decls.push({ kind: "varDecl", localId: local.id, init: value, loc });
4246
+ return varRef(local.id, value.type, loc);
4247
+ };
4248
+ const recv = bind(streamReceiverExpr(lowerer, access.expression), "%opRecv");
3032
4249
  const readable = { kind: "object", className: "%Readable" };
3033
4250
  const opts = (i) => lowerOperatorValue(lowerer, args[i], `${member} options`, loc, true);
4251
+ let takesOptions = false;
4252
+ let callbackGen;
3034
4253
  const fnArg = (maxParams) => {
3035
4254
  if (args[0] === undefined)
3036
4255
  return dynUndefinedExpr(loc);
3037
- return lowerOperatorCallback(lowerer, args[0], member, maxParams);
4256
+ const lowered = lowerOperatorCallback(lowerer, args[0], member, maxParams);
4257
+ takesOptions = lowered.takesOptions;
4258
+ callbackGen = lowered.gen;
4259
+ return bind(lowered.fn, "%opFn");
4260
+ };
4261
+ const lib = (fn, a, type) => {
4262
+ const call = { kind: "libCall", fn: fn, args: a, type, loc };
4263
+ return { kind: "seqExpr", stmts: decls, result: call, type, loc };
4264
+ };
4265
+ // the options argument (bound), and the callback's { signal } when it
4266
+ // declares one
4267
+ const withSignal = (optsValue) => {
4268
+ const o = bind(optsValue, "%opOptions");
4269
+ return [o, takesOptions ? operatorSignalOptions(lowerer, o, loc) : dynUndefinedExpr(loc)];
3038
4270
  };
3039
- const lib = (fn, a, type) => ({
3040
- kind: "libCall",
3041
- fn: fn,
3042
- args: a,
3043
- type,
3044
- loc,
3045
- });
3046
4271
  switch (member) {
3047
4272
  case "map":
3048
4273
  case "filter":
@@ -3053,7 +4278,12 @@ function lowerStreamOperator(lowerer, call, access, member) {
3053
4278
  : member === "filter"
3054
4279
  ? "readable.opFilter"
3055
4280
  : "readable.opFlatMap";
3056
- return lib(fnName, [recv, fn, opts(1)], readable);
4281
+ const rest = withSignal(opts(1));
4282
+ const genHelpers = callbackGen ? fromGenHelpers(lowerer, callbackGen, loc) : null;
4283
+ if (callbackGen && genHelpers) {
4284
+ return lib(callbackGen.async ? "readable.opFlatMapAsyncGen" : "readable.opFlatMapSyncGen", [recv, fn, ...rest, ...genHelpers], readable);
4285
+ }
4286
+ return lib(fnName, [recv, fn, ...rest], readable);
3057
4287
  }
3058
4288
  case "drop":
3059
4289
  case "take": {
@@ -3062,12 +4292,17 @@ function lowerStreamOperator(lowerer, call, access, member) {
3062
4292
  }
3063
4293
  case "toArray":
3064
4294
  return lib("readable.opToArray", [recv, opts(0)], { kind: "promise", inner: DYN });
3065
- case "forEach":
3066
- return lib("readable.opForEach", [recv, fnArg(1), opts(1)], { kind: "promise", inner: VOID });
4295
+ case "forEach": {
4296
+ const fn = fnArg(1);
4297
+ return lib("readable.opForEach", [recv, fn, ...withSignal(opts(1))], {
4298
+ kind: "promise",
4299
+ inner: VOID,
4300
+ });
4301
+ }
3067
4302
  case "some":
3068
4303
  case "every": {
3069
4304
  const fn = fnArg(1);
3070
- return lib(member === "some" ? "readable.opSome" : "readable.opEvery", [recv, fn, opts(1)], {
4305
+ return lib(member === "some" ? "readable.opSome" : "readable.opEvery", [recv, fn, ...withSignal(opts(1))], {
3071
4306
  kind: "promise",
3072
4307
  inner: BOOL,
3073
4308
  });
@@ -3078,7 +4313,11 @@ function lowerStreamOperator(lowerer, call, access, member) {
3078
4313
  if (inner && !(inner.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown))) {
3079
4314
  lowerer.noLowering("find with a type-guard callback", call, "the result is a checked-dynamic value — use a boolean predicate and narrow the result");
3080
4315
  }
3081
- return lib("readable.opFind", [recv, fnArg(1), opts(1)], { kind: "promise", inner: DYN });
4316
+ const fn = fnArg(1);
4317
+ return lib("readable.opFind", [recv, fn, ...withSignal(opts(1))], {
4318
+ kind: "promise",
4319
+ inner: DYN,
4320
+ });
3082
4321
  }
3083
4322
  case "reduce": {
3084
4323
  const t = lowerer.typeOf(call);
@@ -3104,8 +4343,17 @@ function lowerStreamOperator(lowerer, call, access, member) {
3104
4343
  }
3105
4344
  }
3106
4345
  const fn = fnArg(2);
3107
- const init = lowerOperatorValue(lowerer, args[1], "reduce's initial value", loc, false);
3108
- return lib("readable.opReduce", [recv, fn, init, boolLit(args.length >= 2, loc), opts(2), numLit(kind, loc)], { kind: "promise", inner });
4346
+ const init = bind(lowerOperatorValue(lowerer, args[1], "reduce's initial value", loc, false), "%opInit");
4347
+ const o = bind(opts(2), "%opOptions");
4348
+ const head = [recv, fn, init, boolLit(args.length >= 2, loc), o, numLit(kind, loc)];
4349
+ if (!takesOptions)
4350
+ return lib("readable.opReduce", head, { kind: "promise", inner });
4351
+ const setup = reduceSignalSetup(lowerer, o, loc);
4352
+ decls.push(...setup.stmts);
4353
+ return lib("readable.opReduceSignal", [...head, setup.abort, setup.cbOpts], {
4354
+ kind: "promise",
4355
+ inner,
4356
+ });
3109
4357
  }
3110
4358
  }
3111
4359
  throw new InternalCompilerError(`unknown stream operator ${member}`);
@@ -3145,7 +4393,7 @@ function pipeEndOption(lowerer, options, loc) {
3145
4393
  function pipeIntoStdio(lowerer, call, access, target) {
3146
4394
  const loc = locOf(call);
3147
4395
  // JS order: the receiver, then the destination, then the options.
3148
- const receiverValue = lowerer.lowerExpr(access.expression);
4396
+ const receiverValue = streamReceiverExpr(lowerer, access.expression);
3149
4397
  const source = lowerer.declareHiddenLocal("%pipeSource", receiverValue.type);
3150
4398
  const stdio = lowerer.declareHiddenLocal("%pipeStdio", target.type);
3151
4399
  const options = call.arguments.nodes[1];
@@ -3256,7 +4504,7 @@ function generalPipe(lowerer, dst) {
3256
4504
  function pipeIntoDyn(lowerer, call, access, target) {
3257
4505
  const loc = locOf(call);
3258
4506
  noteUntypedStreamFlow(lowerer, null);
3259
- const receiverValue = lowerer.lowerExpr(access.expression);
4507
+ const receiverValue = streamReceiverExpr(lowerer, access.expression);
3260
4508
  const source = lowerer.declareHiddenLocal("%pipeSource", receiverValue.type);
3261
4509
  const destination = lowerer.declareHiddenLocal("%pipeDestination", target.type);
3262
4510
  const stmts = [
@@ -3342,6 +4590,59 @@ function pipeIntoDyn(lowerer, call, access, target) {
3342
4590
  loc,
3343
4591
  };
3344
4592
  }
4593
+ /** readable.compose(stage, options): compose(this, stage), with
4594
+ * addAbortSignal(options.signal, composed) when the signal is set
4595
+ * (operators.js). */
4596
+ function lowerReadableCompose(lowerer, call, access) {
4597
+ const loc = locOf(call);
4598
+ const args = call.arguments.nodes;
4599
+ if (args.length === 0 || args.length > 2 || args.some((a) => ts.isSpreadElement(a))) {
4600
+ lowerer.noLowering(`readable.compose with ${args.length === 0 ? "no stage" : "these arguments"}`, call, "the supported form is readable.compose(stage) or readable.compose(stage, { signal })");
4601
+ }
4602
+ let signalNode = null;
4603
+ const options = args[1];
4604
+ if (options !== undefined &&
4605
+ options.kind !== ts.SyntaxKind.NullKeyword &&
4606
+ !(ts.isIdentifier(options) && options.text === "undefined")) {
4607
+ if (!ts.isObjectLiteralExpression(options)) {
4608
+ lowerer.noLowering("readable.compose options that are not an object literal", options, "pass { signal } as an object literal");
4609
+ }
4610
+ for (const p of options.properties.nodes) {
4611
+ const key = (ts.isPropertyAssignment(p) || ts.isShorthandPropertyAssignment(p)) &&
4612
+ ts.isIdentifier(p.name)
4613
+ ? p.name.text
4614
+ : null;
4615
+ if (key !== "signal") {
4616
+ lowerer.noLowering(key === null
4617
+ ? "readable.compose options with this member"
4618
+ : `the readable.compose option '${key}'`, p, "signal is the readable.compose option");
4619
+ }
4620
+ const value = ts.isShorthandPropertyAssignment(p)
4621
+ ? p.name
4622
+ : p.initializer;
4623
+ if (value.kind === ts.SyntaxKind.NullKeyword)
4624
+ continue;
4625
+ if (ts.isIdentifier(value) && value.text === "undefined")
4626
+ continue;
4627
+ // The signal is read after compose runs, so only values whose
4628
+ // evaluation has no effects.
4629
+ if (!sideEffectFreeOptionValue(value) && !pureRead(value)) {
4630
+ lowerer.noLowering("the readable.compose option 'signal' with this value", p, "pass a variable or property read (const signal = controller.signal)");
4631
+ }
4632
+ signalNode = value;
4633
+ }
4634
+ }
4635
+ const composed = lowerStreamCompose(lowerer, [access.expression, args[0]], loc);
4636
+ if (signalNode === null)
4637
+ return composed;
4638
+ return {
4639
+ kind: "libCall",
4640
+ fn: "stream.addAbortSignal",
4641
+ args: [composed, lowerer.lowerExprExpecting(signalNode, DYN)],
4642
+ type: composed.type,
4643
+ loc,
4644
+ };
4645
+ }
3345
4646
  export function lowerStreamMethodCall(lowerer, call, access, info) {
3346
4647
  const member = access.name.text;
3347
4648
  if (STREAM_OPERATORS.has(member)) {
@@ -3349,6 +4650,12 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3349
4650
  if (opSides === "r" || opSides === "rw")
3350
4651
  return lowerStreamOperator(lowerer, call, access, member);
3351
4652
  }
4653
+ if (member === "compose") {
4654
+ const composeSides = streamSidesOf(lowerer, info);
4655
+ return composeSides === "r" || composeSides === "rw"
4656
+ ? lowerReadableCompose(lowerer, call, access)
4657
+ : null;
4658
+ }
3352
4659
  if (!STREAM_API_MEMBERS.has(member))
3353
4660
  return null;
3354
4661
  const sides = streamSidesOf(lowerer, info);
@@ -3431,7 +4738,7 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3431
4738
  if (canonical === undefined) {
3432
4739
  return nodeThrowExpr(1, "ERR_UNKNOWN_ENCODING", `Unknown encoding: ${t.value}`, BOOL, loc);
3433
4740
  }
3434
- const receiver = lowerer.lowerExpr(access.expression);
4741
+ const receiver = streamReceiverExpr(lowerer, access.expression);
3435
4742
  const chunk = lowerer.lowerExprExpecting(args[0], STRING);
3436
4743
  return {
3437
4744
  kind: "libCall",
@@ -3447,7 +4754,7 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3447
4754
  if ((kind === "null" || kind === "union" || kind === "dyn") && member === "unshift") {
3448
4755
  // unshift(value): an object-mode stream's value at the front, null
3449
4756
  // the end of the stream, as Node's readableAddChunk takes them
3450
- const receiver = lowerer.lowerExpr(access.expression);
4757
+ const receiver = streamReceiverExpr(lowerer, access.expression);
3451
4758
  const chunk = lowerer.lowerExprExpecting(args[0], DYN);
3452
4759
  return {
3453
4760
  kind: "libCall",
@@ -3457,7 +4764,7 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3457
4764
  loc,
3458
4765
  };
3459
4766
  }
3460
- const receiver = lowerer.lowerExpr(access.expression);
4767
+ const receiver = streamReceiverExpr(lowerer, access.expression);
3461
4768
  if (kind === "null") {
3462
4769
  return { kind: "libCall", fn: "readable.pushNull", args: [receiver], type: BOOL, loc };
3463
4770
  }
@@ -3508,7 +4815,21 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3508
4815
  requireSide(readableSide, "read");
3509
4816
  if (args.length > 1)
3510
4817
  lowerer.noLowering(`read with ${args.length} arguments`, call);
3511
- const receiver = lowerer.lowerExpr(access.expression);
4818
+ const receiver = streamReceiverExpr(lowerer, access.expression);
4819
+ // JavaScript (and a result the declarations type as any) reads any
4820
+ // chunk: a Buffer, a string once encoded, or an object-mode value.
4821
+ const resultTs = lowerer.typeOf(call);
4822
+ if (isJsSourceFile(ts.getSourceFileOfNode(call)) ||
4823
+ (resultTs.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown)) !== 0) {
4824
+ const sizeDyn = args[0] ? lowerer.lowerExprExpecting(args[0], DYN) : dynUndefinedExpr(loc);
4825
+ return {
4826
+ kind: "libCall",
4827
+ fn: "readable.readDyn",
4828
+ args: [receiver, sizeDyn],
4829
+ type: DYN,
4830
+ loc,
4831
+ };
4832
+ }
3512
4833
  const size = args[0] ? lowerer.lowerExprExpecting(args[0], F64) : numLit(-1, loc);
3513
4834
  const type = unionOf(lowerer, [BYTES, { kind: "nullT" }]);
3514
4835
  const read = {
@@ -3524,7 +4845,7 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3524
4845
  requireSide(readableSide, member);
3525
4846
  if (args.length !== 0)
3526
4847
  lowerer.noLowering(`${member} with arguments`, call);
3527
- const receiver = lowerer.lowerExpr(access.expression);
4848
+ const receiver = streamReceiverExpr(lowerer, access.expression);
3528
4849
  return {
3529
4850
  kind: "libCall",
3530
4851
  fn: member === "pause" ? "readable.pause" : "readable.resume",
@@ -3537,7 +4858,7 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3537
4858
  requireSide(readableSide, "isPaused");
3538
4859
  if (args.length !== 0)
3539
4860
  lowerer.noLowering(`isPaused with arguments`, call);
3540
- const receiver = lowerer.lowerExpr(access.expression);
4861
+ const receiver = streamReceiverExpr(lowerer, access.expression);
3541
4862
  return { kind: "libCall", fn: "readable.isPaused", args: [receiver], type: BOOL, loc };
3542
4863
  }
3543
4864
  if (member === "pipe") {
@@ -3549,7 +4870,7 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3549
4870
  // Transform, and pipe() answers the value itself (the receiver
3550
4871
  // evaluates first, as in JS).
3551
4872
  if (isValueStream(lowerer, args[0])) {
3552
- const receiverValue = lowerer.lowerExpr(access.expression);
4873
+ const receiverValue = streamReceiverExpr(lowerer, access.expression);
3553
4874
  const source = lowerer.declareHiddenLocal("%pipeSource", receiverValue.type);
3554
4875
  const hashValue = lowerer.lowerExpr(args[0]);
3555
4876
  const hash = lowerer.declareHiddenLocal("%pipeHash", hashValue.type);
@@ -3593,7 +4914,7 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3593
4914
  lowerer.noLowering(`pipe into a '${lowerer.fmt(dst.type)}'`, args[0], "the destination must be a stream Writable/Duplex, process.stdout or process.stderr (sockets are not pipe destinations yet)");
3594
4915
  }
3595
4916
  const end = pipeEndOption(lowerer, args[1], loc);
3596
- const receiver = lowerer.lowerExpr(access.expression);
4917
+ const receiver = streamReceiverExpr(lowerer, access.expression);
3597
4918
  return {
3598
4919
  kind: "libCall",
3599
4920
  fn: generalPipe(lowerer, dst.type),
@@ -3606,7 +4927,7 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3606
4927
  requireSide(readableSide, "unpipe");
3607
4928
  if (args.length > 1)
3608
4929
  lowerer.noLowering(`unpipe with ${args.length} arguments`, call);
3609
- const receiver = lowerer.lowerExpr(access.expression);
4930
+ const receiver = streamReceiverExpr(lowerer, access.expression);
3610
4931
  if (args[0] !== undefined) {
3611
4932
  const dst = lowerer.lowerExpr(args[0]);
3612
4933
  if (dst.type.kind === "dyn")
@@ -3667,7 +4988,7 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3667
4988
  if (member === "destroy") {
3668
4989
  if (args.length > 1)
3669
4990
  lowerer.noLowering(`destroy with ${args.length} arguments`, call);
3670
- const receiver = lowerer.lowerExpr(access.expression);
4991
+ const receiver = streamReceiverExpr(lowerer, access.expression);
3671
4992
  if (args[0] !== undefined) {
3672
4993
  const err = lowerer.lowerExpr(args[0]);
3673
4994
  const rootsAtError = (t) => {
@@ -3714,7 +5035,7 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3714
5035
  // the callback took its place (a byte-mode side encodes a string
3715
5036
  // chunk with it).
3716
5037
  const writeEnc = (encArg, cbArg) => {
3717
- const receiver = lowerer.lowerExpr(access.expression);
5038
+ const receiver = streamReceiverExpr(lowerer, access.expression);
3718
5039
  const chunk = lowerer.lowerExprExpecting(args[0], DYN);
3719
5040
  const tail = [];
3720
5041
  if (encArg !== undefined)
@@ -3744,7 +5065,7 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3744
5065
  }
3745
5066
  return writeEnc(args[1], undefined);
3746
5067
  }
3747
- const receiver = lowerer.lowerExpr(access.expression);
5068
+ const receiver = streamReceiverExpr(lowerer, access.expression);
3748
5069
  const chunk = lowerer.lowerExprExpecting(args[0], DYN);
3749
5070
  return { kind: "libCall", fn: "writable.writeDyn", args: [receiver, chunk], type: BOOL, loc };
3750
5071
  }
@@ -3755,7 +5076,7 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3755
5076
  // re-dispatch on its IR type.
3756
5077
  if (args.length > 1)
3757
5078
  lowerer.noLowering("write with a union chunk and more arguments", call);
3758
- const receiver = lowerer.lowerExpr(access.expression);
5079
+ const receiver = streamReceiverExpr(lowerer, access.expression);
3759
5080
  const chunk = lowerer.lowerExpr(args[0]);
3760
5081
  const fn = chunk.type.kind === "union"
3761
5082
  ? "writable.writeU"
@@ -3778,7 +5099,7 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3778
5099
  return writeEnc(args[1], undefined);
3779
5100
  cbArg = args[1];
3780
5101
  }
3781
- const receiver = lowerer.lowerExpr(access.expression);
5102
+ const receiver = streamReceiverExpr(lowerer, access.expression);
3782
5103
  const chunk = kind === "bytes"
3783
5104
  ? lowerer.lowerExprExpecting(args[0], BYTES)
3784
5105
  : lowerer.lowerExprExpecting(args[0], STRING);
@@ -3817,7 +5138,7 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3817
5138
  encNode = args[1];
3818
5139
  cbArg = args[2];
3819
5140
  }
3820
- const receiver = lowerer.lowerExpr(access.expression);
5141
+ const receiver = streamReceiverExpr(lowerer, access.expression);
3821
5142
  let flags = 0;
3822
5143
  const tail = [];
3823
5144
  if (chunkNode !== undefined) {
@@ -3859,7 +5180,7 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3859
5180
  requireSide(writableSide, member);
3860
5181
  if (args.length !== 0)
3861
5182
  lowerer.noLowering(`${member} with arguments`, call);
3862
- const receiver = lowerer.lowerExpr(access.expression);
5183
+ const receiver = streamReceiverExpr(lowerer, access.expression);
3863
5184
  return {
3864
5185
  kind: "libCall",
3865
5186
  fn: member === "cork" ? "writable.cork" : "writable.uncork",
@@ -3874,7 +5195,7 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3874
5195
  lowerer.noLowering(`setEncoding with ${args.length} arguments`, call, "one literal encoding is the supported form");
3875
5196
  }
3876
5197
  const enc = bufEncoding(lowerer, "setEncoding", args[0]);
3877
- const receiver = lowerer.lowerExpr(access.expression);
5198
+ const receiver = streamReceiverExpr(lowerer, access.expression);
3878
5199
  return {
3879
5200
  kind: "libCall",
3880
5201
  fn: "readable.setEncoding",
@@ -3884,14 +5205,20 @@ export function lowerStreamMethodCall(lowerer, call, access, info) {
3884
5205
  };
3885
5206
  }
3886
5207
  if (member === "setDefaultEncoding") {
3887
- // The write-side default: utf8 IS the default — admit it; other
3888
- // encodings would change what write(string) means.
5208
+ // The write-side default: lowercased and validated (ERR_UNKNOWN_ENCODING,
5209
+ // thrown) at run time; answers the stream.
3889
5210
  requireSide(writableSide, "setDefaultEncoding");
3890
- const t = args[0] ? lowerer.typeOf(args[0]) : null;
3891
- if (t?.isStringLiteralType() && (t.value === "utf8" || t.value === "utf-8")) {
3892
- return lowerer.lowerExpr(access.expression);
3893
- }
3894
- lowerer.noLowering("setDefaultEncoding", call, "utf8 (the default, as a compile-time literal) is the supported write-side string encoding");
5211
+ if (args.length > 1)
5212
+ lowerer.noLowering(`setDefaultEncoding with ${args.length} arguments`, call);
5213
+ const receiver = streamReceiverExpr(lowerer, access.expression);
5214
+ const enc = args[0] ? lowerer.lowerExprExpecting(args[0], DYN) : dynUndefinedExpr(loc);
5215
+ return {
5216
+ kind: "libCall",
5217
+ fn: "writable.setDefaultEncoding",
5218
+ args: [receiver, enc],
5219
+ type: receiver.type,
5220
+ loc,
5221
+ };
3895
5222
  }
3896
5223
  return null;
3897
5224
  }
@@ -4024,7 +5351,7 @@ export function lowerStreamProperty(lowerer, expr, info) {
4024
5351
  const propCall = (type) => ({
4025
5352
  kind: "libCall",
4026
5353
  fn: "stream.prop",
4027
- args: [lowerer.lowerExpr(expr.expression), strLit(name, loc)],
5354
+ args: [streamReceiverExpr(lowerer, expr.expression), strLit(name, loc)],
4028
5355
  type,
4029
5356
  loc,
4030
5357
  });
@@ -4042,7 +5369,7 @@ export function lowerStreamProperty(lowerer, expr, info) {
4042
5369
  const read = {
4043
5370
  kind: "libCall",
4044
5371
  fn: "readable.flowing",
4045
- args: [lowerer.lowerExpr(expr.expression)],
5372
+ args: [streamReceiverExpr(lowerer, expr.expression)],
4046
5373
  type,
4047
5374
  loc,
4048
5375
  };
@@ -4056,7 +5383,7 @@ export function lowerStreamProperty(lowerer, expr, info) {
4056
5383
  const read = {
4057
5384
  kind: "libCall",
4058
5385
  fn: "stream.errored",
4059
- args: [lowerer.lowerExpr(expr.expression)],
5386
+ args: [streamReceiverExpr(lowerer, expr.expression)],
4060
5387
  type,
4061
5388
  loc,
4062
5389
  };