@cj-tech-master/excelts 4.2.1 → 4.2.2-canary.20260115044841.88820eb

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 (331) hide show
  1. package/THIRD_PARTY_NOTICES.md +0 -31
  2. package/dist/browser/index.browser.d.ts +1 -0
  3. package/dist/browser/index.browser.js +12 -0
  4. package/dist/{esm/modules/archive → browser/modules/archive/compression}/compress.base.js +1 -1
  5. package/dist/{types/modules/archive → browser/modules/archive/compression}/compress.browser.d.ts +2 -8
  6. package/dist/browser/modules/archive/{compress.browser.js → compression/compress.browser.js} +3 -11
  7. package/dist/browser/modules/archive/{compress.d.ts → compression/compress.d.ts} +2 -2
  8. package/dist/{esm/modules/archive → browser/modules/archive/compression}/compress.js +1 -1
  9. package/dist/browser/modules/archive/{crc32.browser.d.ts → compression/crc32.browser.d.ts} +1 -1
  10. package/dist/browser/modules/archive/{crc32.d.ts → compression/crc32.d.ts} +1 -1
  11. package/dist/browser/modules/archive/{crc32.js → compression/crc32.js} +1 -1
  12. package/dist/browser/modules/archive/{deflate-fallback.js → compression/deflate-fallback.js} +1 -1
  13. package/dist/browser/modules/archive/{streaming-compress.browser.d.ts → compression/streaming-compress.browser.d.ts} +2 -2
  14. package/dist/browser/modules/archive/{streaming-compress.browser.js → compression/streaming-compress.browser.js} +3 -3
  15. package/dist/browser/modules/archive/{streaming-compress.d.ts → compression/streaming-compress.d.ts} +2 -2
  16. package/dist/browser/modules/archive/{streaming-compress.js → compression/streaming-compress.js} +2 -2
  17. package/dist/browser/modules/archive/defaults.d.ts +1 -0
  18. package/dist/browser/modules/archive/defaults.js +6 -3
  19. package/dist/browser/modules/archive/index.base.d.ts +4 -4
  20. package/dist/browser/modules/archive/index.base.js +3 -6
  21. package/dist/browser/modules/archive/index.browser.d.ts +3 -4
  22. package/dist/browser/modules/archive/index.browser.js +3 -7
  23. package/dist/browser/modules/archive/index.d.ts +3 -4
  24. package/dist/browser/modules/archive/index.js +3 -5
  25. package/dist/browser/modules/archive/internal/byte-queue.d.ts +33 -0
  26. package/dist/browser/modules/archive/internal/byte-queue.js +407 -0
  27. package/dist/browser/modules/archive/io/archive-sink.d.ts +8 -0
  28. package/dist/browser/modules/archive/io/archive-sink.js +45 -0
  29. package/dist/browser/modules/archive/io/archive-source.d.ts +6 -0
  30. package/dist/browser/modules/archive/io/archive-source.js +100 -0
  31. package/dist/browser/modules/archive/{extract.d.ts → unzip/extract.d.ts} +2 -2
  32. package/dist/browser/modules/archive/unzip/index.d.ts +40 -0
  33. package/dist/browser/modules/archive/unzip/index.js +164 -0
  34. package/dist/browser/modules/archive/{parse.base.d.ts → unzip/stream.base.d.ts} +36 -2
  35. package/dist/browser/modules/archive/unzip/stream.base.js +1022 -0
  36. package/dist/browser/modules/archive/{parse.browser.d.ts → unzip/stream.browser.d.ts} +1 -1
  37. package/dist/browser/modules/archive/{parse.browser.js → unzip/stream.browser.js} +371 -110
  38. package/dist/browser/modules/archive/{parse.d.ts → unzip/stream.d.ts} +2 -2
  39. package/dist/{esm/modules/archive/parse.js → browser/modules/archive/unzip/stream.js} +6 -5
  40. package/dist/browser/modules/archive/{zip-parser.d.ts → unzip/zip-parser.d.ts} +1 -1
  41. package/dist/{esm/modules/archive → browser/modules/archive/unzip}/zip-parser.js +38 -24
  42. package/dist/browser/modules/archive/utils/async-queue.d.ts +7 -0
  43. package/dist/browser/modules/archive/utils/async-queue.js +103 -0
  44. package/dist/browser/modules/archive/utils/bytes.js +16 -16
  45. package/dist/browser/modules/archive/utils/compressibility.d.ts +10 -0
  46. package/dist/browser/modules/archive/utils/compressibility.js +57 -0
  47. package/dist/browser/modules/archive/utils/parse-buffer.js +21 -23
  48. package/dist/browser/modules/archive/utils/pattern-scanner.d.ts +21 -0
  49. package/dist/browser/modules/archive/utils/pattern-scanner.js +27 -0
  50. package/dist/browser/modules/archive/utils/timestamps.js +62 -1
  51. package/dist/browser/modules/archive/utils/zip-extra-fields.d.ts +1 -1
  52. package/dist/browser/modules/archive/utils/zip-extra-fields.js +26 -14
  53. package/dist/browser/modules/archive/zip/index.d.ts +42 -0
  54. package/dist/browser/modules/archive/zip/index.js +157 -0
  55. package/dist/browser/modules/archive/{streaming-zip.d.ts → zip/stream.d.ts} +28 -5
  56. package/dist/browser/modules/archive/{streaming-zip.js → zip/stream.js} +192 -48
  57. package/dist/browser/modules/archive/zip/zip-bytes.d.ts +73 -0
  58. package/dist/browser/modules/archive/zip/zip-bytes.js +239 -0
  59. package/dist/{esm/modules/archive → browser/modules/archive/zip}/zip-entry-metadata.js +3 -3
  60. package/dist/browser/modules/archive/{zip-records.d.ts → zip-spec/zip-records.d.ts} +20 -0
  61. package/dist/browser/modules/archive/zip-spec/zip-records.js +126 -0
  62. package/dist/browser/modules/excel/stream/workbook-reader.browser.js +1 -1
  63. package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +1 -1
  64. package/dist/browser/modules/excel/stream/workbook-writer.browser.js +1 -1
  65. package/dist/browser/modules/excel/utils/ooxml-validator.d.ts +48 -0
  66. package/dist/browser/modules/excel/utils/ooxml-validator.js +469 -0
  67. package/dist/browser/modules/excel/utils/parse-sax.d.ts +3 -0
  68. package/dist/browser/modules/excel/utils/parse-sax.js +32 -13
  69. package/dist/browser/modules/excel/worksheet.js +5 -2
  70. package/dist/browser/modules/excel/xlsx/xform/core/app-xform.js +3 -3
  71. package/dist/browser/modules/excel/xlsx/xform/core/core-xform.js +56 -68
  72. package/dist/browser/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.d.ts +1 -0
  73. package/dist/browser/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +13 -1
  74. package/dist/browser/modules/excel/xlsx/xform/drawing/sp-xform.d.ts +18 -0
  75. package/dist/browser/modules/excel/xlsx/xform/drawing/sp-xform.js +112 -0
  76. package/dist/browser/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.d.ts +6 -1
  77. package/dist/browser/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +30 -2
  78. package/dist/browser/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +11 -0
  79. package/dist/browser/modules/excel/xlsx/xform/list-xform.js +8 -10
  80. package/dist/browser/modules/excel/xlsx/xform/sheet/page-setup-xform.d.ts +1 -0
  81. package/dist/browser/modules/excel/xlsx/xform/sheet/page-setup-xform.js +16 -2
  82. package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +110 -12
  83. package/dist/browser/modules/excel/xlsx/xform/strings/shared-string-xform.js +2 -3
  84. package/dist/browser/modules/excel/xlsx/xform/strings/text-xform.js +5 -7
  85. package/dist/browser/modules/excel/xlsx/xlsx.browser.js +11 -10
  86. package/dist/browser/modules/excel/xlsx/xlsx.js +1 -1
  87. package/dist/browser/modules/stream/base-transform.d.ts +3 -0
  88. package/dist/browser/modules/stream/base-transform.js +34 -20
  89. package/dist/browser/modules/stream/buffered-stream.d.ts +2 -12
  90. package/dist/browser/modules/stream/chunked-builder.js +4 -4
  91. package/dist/browser/modules/stream/index.browser.d.ts +13 -19
  92. package/dist/browser/modules/stream/index.browser.js +10 -22
  93. package/dist/browser/modules/stream/index.d.ts +18 -41
  94. package/dist/browser/modules/stream/index.js +15 -44
  95. package/dist/browser/modules/stream/internal/event-utils.d.ts +17 -0
  96. package/dist/browser/modules/stream/internal/event-utils.js +40 -0
  97. package/dist/browser/modules/stream/internal/type-guards.d.ts +9 -0
  98. package/dist/browser/modules/stream/internal/type-guards.js +24 -0
  99. package/dist/browser/modules/stream/pull-stream.d.ts +5 -6
  100. package/dist/browser/modules/stream/pull-stream.js +107 -43
  101. package/dist/browser/modules/stream/shared.d.ts +1 -1
  102. package/dist/browser/modules/stream/shared.js +7 -4
  103. package/dist/browser/modules/stream/streams.browser.d.ts +32 -44
  104. package/dist/browser/modules/stream/streams.browser.js +921 -836
  105. package/dist/browser/modules/stream/streams.d.ts +4 -20
  106. package/dist/browser/modules/stream/streams.js +146 -95
  107. package/dist/browser/modules/stream/utils.js +5 -38
  108. package/dist/cjs/modules/archive/{compress.base.js → compression/compress.base.js} +1 -1
  109. package/dist/cjs/modules/archive/{compress.browser.js → compression/compress.browser.js} +3 -11
  110. package/dist/cjs/modules/archive/{compress.js → compression/compress.js} +1 -1
  111. package/dist/cjs/modules/archive/{crc32.js → compression/crc32.js} +1 -1
  112. package/dist/cjs/modules/archive/{deflate-fallback.js → compression/deflate-fallback.js} +1 -1
  113. package/dist/cjs/modules/archive/{streaming-compress.browser.js → compression/streaming-compress.browser.js} +3 -3
  114. package/dist/cjs/modules/archive/{streaming-compress.js → compression/streaming-compress.js} +2 -2
  115. package/dist/cjs/modules/archive/defaults.js +7 -4
  116. package/dist/cjs/modules/archive/index.base.js +9 -19
  117. package/dist/cjs/modules/archive/index.browser.js +4 -10
  118. package/dist/cjs/modules/archive/index.js +4 -8
  119. package/dist/cjs/modules/archive/internal/byte-queue.js +411 -0
  120. package/dist/cjs/modules/archive/io/archive-sink.js +49 -0
  121. package/dist/cjs/modules/archive/io/archive-source.js +105 -0
  122. package/dist/cjs/modules/archive/unzip/index.js +170 -0
  123. package/dist/cjs/modules/archive/unzip/stream.base.js +1044 -0
  124. package/dist/cjs/modules/archive/{parse.browser.js → unzip/stream.browser.js} +372 -111
  125. package/dist/cjs/modules/archive/{parse.js → unzip/stream.js} +9 -8
  126. package/dist/cjs/modules/archive/{zip-parser.js → unzip/zip-parser.js} +47 -33
  127. package/dist/cjs/modules/archive/utils/async-queue.js +106 -0
  128. package/dist/cjs/modules/archive/utils/bytes.js +16 -16
  129. package/dist/cjs/modules/archive/utils/compressibility.js +60 -0
  130. package/dist/cjs/modules/archive/utils/parse-buffer.js +21 -23
  131. package/dist/cjs/modules/archive/utils/pattern-scanner.js +31 -0
  132. package/dist/cjs/modules/archive/utils/timestamps.js +64 -3
  133. package/dist/cjs/modules/archive/utils/zip-extra-fields.js +26 -14
  134. package/dist/cjs/modules/archive/zip/index.js +162 -0
  135. package/dist/cjs/modules/archive/{streaming-zip.js → zip/stream.js} +194 -50
  136. package/dist/cjs/modules/archive/zip/zip-bytes.js +242 -0
  137. package/dist/cjs/modules/archive/{zip-entry-metadata.js → zip/zip-entry-metadata.js} +5 -5
  138. package/dist/cjs/modules/archive/zip-spec/zip-records.js +136 -0
  139. package/dist/cjs/modules/excel/stream/workbook-reader.browser.js +2 -2
  140. package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +4 -4
  141. package/dist/cjs/modules/excel/utils/ooxml-validator.js +475 -0
  142. package/dist/cjs/modules/excel/utils/parse-sax.js +32 -13
  143. package/dist/cjs/modules/excel/worksheet.js +5 -2
  144. package/dist/cjs/modules/excel/xlsx/xform/core/app-xform.js +3 -3
  145. package/dist/cjs/modules/excel/xlsx/xform/core/core-xform.js +56 -68
  146. package/dist/cjs/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +13 -1
  147. package/dist/cjs/modules/excel/xlsx/xform/drawing/sp-xform.js +115 -0
  148. package/dist/cjs/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +30 -2
  149. package/dist/cjs/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +11 -0
  150. package/dist/cjs/modules/excel/xlsx/xform/list-xform.js +8 -10
  151. package/dist/cjs/modules/excel/xlsx/xform/sheet/page-setup-xform.js +16 -2
  152. package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +110 -12
  153. package/dist/cjs/modules/excel/xlsx/xform/strings/shared-string-xform.js +2 -3
  154. package/dist/cjs/modules/excel/xlsx/xform/strings/text-xform.js +5 -7
  155. package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +14 -13
  156. package/dist/cjs/modules/excel/xlsx/xlsx.js +2 -2
  157. package/dist/cjs/modules/stream/base-transform.js +34 -20
  158. package/dist/cjs/modules/stream/chunked-builder.js +4 -4
  159. package/dist/cjs/modules/stream/index.browser.js +10 -17
  160. package/dist/cjs/modules/stream/index.js +15 -39
  161. package/dist/cjs/modules/stream/internal/event-utils.js +43 -0
  162. package/dist/cjs/modules/stream/internal/type-guards.js +30 -0
  163. package/dist/cjs/modules/stream/pull-stream.js +107 -43
  164. package/dist/cjs/modules/stream/shared.js +7 -4
  165. package/dist/cjs/modules/stream/streams.browser.js +927 -847
  166. package/dist/cjs/modules/stream/streams.js +156 -107
  167. package/dist/cjs/modules/stream/utils.js +3 -36
  168. package/dist/esm/index.browser.js +12 -0
  169. package/dist/{browser/modules/archive → esm/modules/archive/compression}/compress.base.js +1 -1
  170. package/dist/esm/modules/archive/{compress.browser.js → compression/compress.browser.js} +3 -11
  171. package/dist/{browser/modules/archive → esm/modules/archive/compression}/compress.js +1 -1
  172. package/dist/esm/modules/archive/{crc32.js → compression/crc32.js} +1 -1
  173. package/dist/esm/modules/archive/{deflate-fallback.js → compression/deflate-fallback.js} +1 -1
  174. package/dist/esm/modules/archive/{streaming-compress.browser.js → compression/streaming-compress.browser.js} +3 -3
  175. package/dist/esm/modules/archive/{streaming-compress.js → compression/streaming-compress.js} +2 -2
  176. package/dist/esm/modules/archive/defaults.js +6 -3
  177. package/dist/esm/modules/archive/index.base.js +3 -6
  178. package/dist/esm/modules/archive/index.browser.js +3 -7
  179. package/dist/esm/modules/archive/index.js +3 -5
  180. package/dist/esm/modules/archive/internal/byte-queue.js +407 -0
  181. package/dist/esm/modules/archive/io/archive-sink.js +45 -0
  182. package/dist/esm/modules/archive/io/archive-source.js +100 -0
  183. package/dist/esm/modules/archive/unzip/index.js +164 -0
  184. package/dist/esm/modules/archive/unzip/stream.base.js +1022 -0
  185. package/dist/esm/modules/archive/{parse.browser.js → unzip/stream.browser.js} +371 -110
  186. package/dist/{browser/modules/archive/parse.js → esm/modules/archive/unzip/stream.js} +6 -5
  187. package/dist/{browser/modules/archive → esm/modules/archive/unzip}/zip-parser.js +38 -24
  188. package/dist/esm/modules/archive/utils/async-queue.js +103 -0
  189. package/dist/esm/modules/archive/utils/bytes.js +16 -16
  190. package/dist/esm/modules/archive/utils/compressibility.js +57 -0
  191. package/dist/esm/modules/archive/utils/parse-buffer.js +21 -23
  192. package/dist/esm/modules/archive/utils/pattern-scanner.js +27 -0
  193. package/dist/esm/modules/archive/utils/timestamps.js +62 -1
  194. package/dist/esm/modules/archive/utils/zip-extra-fields.js +26 -14
  195. package/dist/esm/modules/archive/zip/index.js +157 -0
  196. package/dist/esm/modules/archive/{streaming-zip.js → zip/stream.js} +192 -48
  197. package/dist/esm/modules/archive/zip/zip-bytes.js +239 -0
  198. package/dist/{browser/modules/archive → esm/modules/archive/zip}/zip-entry-metadata.js +3 -3
  199. package/dist/esm/modules/archive/zip-spec/zip-records.js +126 -0
  200. package/dist/esm/modules/excel/stream/workbook-reader.browser.js +1 -1
  201. package/dist/esm/modules/excel/stream/workbook-writer.browser.js +1 -1
  202. package/dist/esm/modules/excel/utils/ooxml-validator.js +469 -0
  203. package/dist/esm/modules/excel/utils/parse-sax.js +32 -13
  204. package/dist/esm/modules/excel/worksheet.js +5 -2
  205. package/dist/esm/modules/excel/xlsx/xform/core/app-xform.js +3 -3
  206. package/dist/esm/modules/excel/xlsx/xform/core/core-xform.js +56 -68
  207. package/dist/esm/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +13 -1
  208. package/dist/esm/modules/excel/xlsx/xform/drawing/sp-xform.js +112 -0
  209. package/dist/esm/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +30 -2
  210. package/dist/esm/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +11 -0
  211. package/dist/esm/modules/excel/xlsx/xform/list-xform.js +8 -10
  212. package/dist/esm/modules/excel/xlsx/xform/sheet/page-setup-xform.js +16 -2
  213. package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +110 -12
  214. package/dist/esm/modules/excel/xlsx/xform/strings/shared-string-xform.js +2 -3
  215. package/dist/esm/modules/excel/xlsx/xform/strings/text-xform.js +5 -7
  216. package/dist/esm/modules/excel/xlsx/xlsx.browser.js +11 -10
  217. package/dist/esm/modules/excel/xlsx/xlsx.js +1 -1
  218. package/dist/esm/modules/stream/base-transform.js +34 -20
  219. package/dist/esm/modules/stream/chunked-builder.js +4 -4
  220. package/dist/esm/modules/stream/index.browser.js +10 -22
  221. package/dist/esm/modules/stream/index.js +15 -44
  222. package/dist/esm/modules/stream/internal/event-utils.js +40 -0
  223. package/dist/esm/modules/stream/internal/type-guards.js +24 -0
  224. package/dist/esm/modules/stream/pull-stream.js +107 -43
  225. package/dist/esm/modules/stream/shared.js +7 -4
  226. package/dist/esm/modules/stream/streams.browser.js +921 -836
  227. package/dist/esm/modules/stream/streams.js +146 -95
  228. package/dist/esm/modules/stream/utils.js +5 -38
  229. package/dist/iife/THIRD_PARTY_NOTICES.md +0 -31
  230. package/dist/iife/excelts.iife.js +6592 -4537
  231. package/dist/iife/excelts.iife.js.map +1 -1
  232. package/dist/iife/excelts.iife.min.js +103 -31
  233. package/dist/types/index.browser.d.ts +1 -0
  234. package/dist/{browser/modules/archive → types/modules/archive/compression}/compress.browser.d.ts +2 -8
  235. package/dist/types/modules/archive/{streaming-compress.browser.d.ts → compression/streaming-compress.browser.d.ts} +1 -1
  236. package/dist/types/modules/archive/defaults.d.ts +1 -0
  237. package/dist/types/modules/archive/index.base.d.ts +4 -4
  238. package/dist/types/modules/archive/index.browser.d.ts +3 -4
  239. package/dist/types/modules/archive/index.d.ts +3 -4
  240. package/dist/types/modules/archive/internal/byte-queue.d.ts +33 -0
  241. package/dist/types/modules/archive/io/archive-sink.d.ts +8 -0
  242. package/dist/types/modules/archive/io/archive-source.d.ts +6 -0
  243. package/dist/types/modules/archive/unzip/index.d.ts +40 -0
  244. package/dist/types/modules/archive/{parse.base.d.ts → unzip/stream.base.d.ts} +38 -4
  245. package/dist/types/modules/archive/{parse.browser.d.ts → unzip/stream.browser.d.ts} +2 -2
  246. package/dist/types/modules/archive/{parse.d.ts → unzip/stream.d.ts} +3 -3
  247. package/dist/types/modules/archive/{zip-parser.d.ts → unzip/zip-parser.d.ts} +1 -1
  248. package/dist/types/modules/archive/utils/async-queue.d.ts +7 -0
  249. package/dist/types/modules/archive/utils/compressibility.d.ts +10 -0
  250. package/dist/types/modules/archive/utils/pattern-scanner.d.ts +21 -0
  251. package/dist/types/modules/archive/utils/zip-extra-fields.d.ts +1 -1
  252. package/dist/types/modules/archive/zip/index.d.ts +42 -0
  253. package/dist/types/modules/archive/{streaming-zip.d.ts → zip/stream.d.ts} +29 -6
  254. package/dist/types/modules/archive/zip/zip-bytes.d.ts +73 -0
  255. package/dist/types/modules/archive/{zip-entry-metadata.d.ts → zip/zip-entry-metadata.d.ts} +1 -1
  256. package/dist/types/modules/archive/{zip-records.d.ts → zip-spec/zip-records.d.ts} +20 -0
  257. package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +1 -1
  258. package/dist/types/modules/excel/utils/ooxml-validator.d.ts +48 -0
  259. package/dist/types/modules/excel/utils/parse-sax.d.ts +3 -0
  260. package/dist/types/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.d.ts +1 -0
  261. package/dist/types/modules/excel/xlsx/xform/drawing/sp-xform.d.ts +18 -0
  262. package/dist/types/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.d.ts +6 -1
  263. package/dist/types/modules/excel/xlsx/xform/sheet/page-setup-xform.d.ts +1 -0
  264. package/dist/types/modules/stream/base-transform.d.ts +3 -0
  265. package/dist/types/modules/stream/buffered-stream.d.ts +2 -12
  266. package/dist/types/modules/stream/index.browser.d.ts +13 -19
  267. package/dist/types/modules/stream/index.d.ts +18 -41
  268. package/dist/types/modules/stream/internal/event-utils.d.ts +17 -0
  269. package/dist/types/modules/stream/internal/type-guards.d.ts +9 -0
  270. package/dist/types/modules/stream/pull-stream.d.ts +5 -6
  271. package/dist/types/modules/stream/shared.d.ts +1 -1
  272. package/dist/types/modules/stream/streams.browser.d.ts +32 -44
  273. package/dist/types/modules/stream/streams.d.ts +4 -20
  274. package/package.json +14 -10
  275. package/dist/browser/modules/archive/byte-queue.d.ts +0 -18
  276. package/dist/browser/modules/archive/byte-queue.js +0 -125
  277. package/dist/browser/modules/archive/parse.base.js +0 -644
  278. package/dist/browser/modules/archive/utils/zip-extra.d.ts +0 -18
  279. package/dist/browser/modules/archive/utils/zip-extra.js +0 -68
  280. package/dist/browser/modules/archive/zip-builder.d.ts +0 -117
  281. package/dist/browser/modules/archive/zip-builder.js +0 -292
  282. package/dist/browser/modules/archive/zip-constants.d.ts +0 -18
  283. package/dist/browser/modules/archive/zip-constants.js +0 -23
  284. package/dist/browser/modules/archive/zip-records.js +0 -84
  285. package/dist/cjs/modules/archive/byte-queue.js +0 -129
  286. package/dist/cjs/modules/archive/parse.base.js +0 -666
  287. package/dist/cjs/modules/archive/utils/zip-extra.js +0 -74
  288. package/dist/cjs/modules/archive/zip-builder.js +0 -297
  289. package/dist/cjs/modules/archive/zip-constants.js +0 -26
  290. package/dist/cjs/modules/archive/zip-records.js +0 -90
  291. package/dist/esm/modules/archive/byte-queue.js +0 -125
  292. package/dist/esm/modules/archive/parse.base.js +0 -644
  293. package/dist/esm/modules/archive/utils/zip-extra.js +0 -68
  294. package/dist/esm/modules/archive/zip-builder.js +0 -292
  295. package/dist/esm/modules/archive/zip-constants.js +0 -23
  296. package/dist/esm/modules/archive/zip-records.js +0 -84
  297. package/dist/types/modules/archive/byte-queue.d.ts +0 -18
  298. package/dist/types/modules/archive/utils/zip-extra.d.ts +0 -18
  299. package/dist/types/modules/archive/zip-builder.d.ts +0 -117
  300. package/dist/types/modules/archive/zip-constants.d.ts +0 -18
  301. /package/dist/browser/modules/archive/{compress.base.d.ts → compression/compress.base.d.ts} +0 -0
  302. /package/dist/browser/modules/archive/{crc32.base.d.ts → compression/crc32.base.d.ts} +0 -0
  303. /package/dist/browser/modules/archive/{crc32.base.js → compression/crc32.base.js} +0 -0
  304. /package/dist/browser/modules/archive/{crc32.browser.js → compression/crc32.browser.js} +0 -0
  305. /package/dist/browser/modules/archive/{deflate-fallback.d.ts → compression/deflate-fallback.d.ts} +0 -0
  306. /package/dist/browser/modules/archive/{streaming-compress.base.d.ts → compression/streaming-compress.base.d.ts} +0 -0
  307. /package/dist/browser/modules/archive/{streaming-compress.base.js → compression/streaming-compress.base.js} +0 -0
  308. /package/dist/browser/modules/archive/{extract.js → unzip/extract.js} +0 -0
  309. /package/dist/browser/modules/archive/{zip-entry-metadata.d.ts → zip/zip-entry-metadata.d.ts} +0 -0
  310. /package/dist/browser/modules/archive/{zip-entry-info.d.ts → zip-spec/zip-entry-info.d.ts} +0 -0
  311. /package/dist/browser/modules/archive/{zip-entry-info.js → zip-spec/zip-entry-info.js} +0 -0
  312. /package/dist/cjs/modules/archive/{crc32.base.js → compression/crc32.base.js} +0 -0
  313. /package/dist/cjs/modules/archive/{crc32.browser.js → compression/crc32.browser.js} +0 -0
  314. /package/dist/cjs/modules/archive/{streaming-compress.base.js → compression/streaming-compress.base.js} +0 -0
  315. /package/dist/cjs/modules/archive/{extract.js → unzip/extract.js} +0 -0
  316. /package/dist/cjs/modules/archive/{zip-entry-info.js → zip-spec/zip-entry-info.js} +0 -0
  317. /package/dist/esm/modules/archive/{crc32.base.js → compression/crc32.base.js} +0 -0
  318. /package/dist/esm/modules/archive/{crc32.browser.js → compression/crc32.browser.js} +0 -0
  319. /package/dist/esm/modules/archive/{streaming-compress.base.js → compression/streaming-compress.base.js} +0 -0
  320. /package/dist/esm/modules/archive/{extract.js → unzip/extract.js} +0 -0
  321. /package/dist/esm/modules/archive/{zip-entry-info.js → zip-spec/zip-entry-info.js} +0 -0
  322. /package/dist/types/modules/archive/{compress.base.d.ts → compression/compress.base.d.ts} +0 -0
  323. /package/dist/types/modules/archive/{compress.d.ts → compression/compress.d.ts} +0 -0
  324. /package/dist/types/modules/archive/{crc32.base.d.ts → compression/crc32.base.d.ts} +0 -0
  325. /package/dist/types/modules/archive/{crc32.browser.d.ts → compression/crc32.browser.d.ts} +0 -0
  326. /package/dist/types/modules/archive/{crc32.d.ts → compression/crc32.d.ts} +0 -0
  327. /package/dist/types/modules/archive/{deflate-fallback.d.ts → compression/deflate-fallback.d.ts} +0 -0
  328. /package/dist/types/modules/archive/{streaming-compress.base.d.ts → compression/streaming-compress.base.d.ts} +0 -0
  329. /package/dist/types/modules/archive/{streaming-compress.d.ts → compression/streaming-compress.d.ts} +0 -0
  330. /package/dist/types/modules/archive/{extract.d.ts → unzip/extract.d.ts} +0 -0
  331. /package/dist/types/modules/archive/{zip-entry-info.d.ts → zip-spec/zip-entry-info.d.ts} +0 -0
@@ -11,9 +11,44 @@
11
11
  * - Edge >= 89
12
12
  */
13
13
  import { EventEmitter } from "./event-emitter.js";
14
- import { PullStream as StandalonePullStream } from "./pull-stream.js";
15
- import { BufferedStream as StandaloneBufferedStream, StringChunk as StandaloneStringChunk, BufferChunk as StandaloneBufferChunk } from "./buffered-stream.js";
14
+ import { PullStream } from "./pull-stream.js";
15
+ import { BufferedStream, BufferChunk, StringChunk } from "./buffered-stream.js";
16
16
  import { concatUint8Arrays, getTextDecoder, textDecoder } from "./shared.js";
17
+ import { isAsyncIterable, isReadableStream, isTransformStream, isWritableStream } from "./internal/type-guards.js";
18
+ const removeEmitterListener = (emitter, event, listener) => {
19
+ if (typeof emitter.off === "function") {
20
+ emitter.off(event, listener);
21
+ }
22
+ else if (typeof emitter.removeListener === "function") {
23
+ emitter.removeListener(event, listener);
24
+ }
25
+ };
26
+ const addEmitterListener = (emitter, event, listener, options) => {
27
+ if (options?.once && typeof emitter.once === "function") {
28
+ emitter.once(event, listener);
29
+ }
30
+ else {
31
+ emitter.on(event, listener);
32
+ }
33
+ return () => removeEmitterListener(emitter, event, listener);
34
+ };
35
+ const createListenerRegistry = () => {
36
+ const listeners = [];
37
+ return {
38
+ add: (emitter, event, listener) => {
39
+ listeners.push(addEmitterListener(emitter, event, listener));
40
+ },
41
+ once: (emitter, event, listener) => {
42
+ listeners.push(addEmitterListener(emitter, event, listener, { once: true }));
43
+ },
44
+ cleanup: () => {
45
+ for (let i = listeners.length - 1; i >= 0; i--) {
46
+ listeners[i]();
47
+ }
48
+ listeners.length = 0;
49
+ }
50
+ };
51
+ };
17
52
  // =============================================================================
18
53
  // Readable Stream Wrapper
19
54
  // =============================================================================
@@ -30,6 +65,7 @@ export class Readable extends EventEmitter {
30
65
  this._bufferSize = 0;
31
66
  this._reading = false;
32
67
  this._ended = false;
68
+ this._endEmitted = false;
33
69
  this._destroyed = false;
34
70
  this._errored = null;
35
71
  this._closed = false;
@@ -86,20 +122,7 @@ export class Readable extends EventEmitter {
86
122
  */
87
123
  static from(iterable, options) {
88
124
  const readable = new Readable({ ...options, objectMode: options?.objectMode ?? true });
89
- (async () => {
90
- try {
91
- for await (const chunk of iterable) {
92
- if (!readable.push(chunk)) {
93
- // Backpressure
94
- await new Promise(resolve => setTimeout(resolve, 0));
95
- }
96
- }
97
- readable.push(null);
98
- }
99
- catch (err) {
100
- readable.destroy(err);
101
- }
102
- })();
125
+ pumpAsyncIterableToReadable(readable, toAsyncIterable(iterable));
103
126
  return readable;
104
127
  }
105
128
  /**
@@ -144,16 +167,30 @@ export class Readable extends EventEmitter {
144
167
  // Controller may already be closed
145
168
  }
146
169
  }
147
- this.emit("end");
170
+ // Emit 'end' only after buffered data is fully drained.
171
+ // This avoids premature 'end' when producers push null while paused.
172
+ if (this._bufferedLength() === 0) {
173
+ this._emitEndOnce();
174
+ }
148
175
  // Note: Don't call destroy() here, let the stream be consumed naturally
149
176
  // The reader will return done:true when it finishes reading
150
177
  return false;
151
178
  }
179
+ // Keep the internal Web ReadableStream in sync for controllable streams.
180
+ // For external Web streams (_webStreamMode=true), push() is not the data source.
181
+ if (controller && !this._webStreamMode) {
182
+ try {
183
+ controller.enqueue(chunk);
184
+ }
185
+ catch {
186
+ // Controller may be closed/errored; Node-side buffering/events still work.
187
+ }
188
+ }
152
189
  if (this._flowing) {
153
190
  // In flowing mode, emit data directly without buffering or enqueueing
154
191
  // const chunkStr = chunk instanceof Uint8Array ? new TextDecoder().decode(chunk.slice(0, 50)) : String(chunk).slice(0, 50);
155
192
  // console.log(`[Readable#${this._id}.push FLOWING] emit data size:${(chunk as any).length || (chunk as any).byteLength} start:"${chunkStr}"`);
156
- this.emit("data", chunk);
193
+ this.emit("data", this._applyEncoding(chunk));
157
194
  // Check if stream was paused during emit (backpressure from consumer)
158
195
  if (!this._flowing) {
159
196
  return false;
@@ -178,10 +215,8 @@ export class Readable extends EventEmitter {
178
215
  if (!this.objectMode) {
179
216
  this._bufferSize += this._getChunkSize(chunk);
180
217
  }
181
- // NOTE: Do NOT enqueue to Web Stream controller here!
182
- // In push mode, _buffer is the only source of data for data events.
183
- // Web Stream is only used for async iteration when not in push mode.
184
- // Enqueueing here would cause data duplication when _startReading is also running.
218
+ // NOTE: We still buffer for Node-style read()/data semantics.
219
+ // The internal Web ReadableStream is also fed via controller.enqueue() above.
185
220
  // Emit readable event when buffer goes from empty to having data
186
221
  if (wasEmpty) {
187
222
  queueMicrotask(() => this.emit("readable"));
@@ -195,6 +230,13 @@ export class Readable extends EventEmitter {
195
230
  return this._bufferSize < this.readableHighWaterMark;
196
231
  }
197
232
  }
233
+ _emitEndOnce() {
234
+ if (this._endEmitted) {
235
+ return;
236
+ }
237
+ this._endEmitted = true;
238
+ this.emit("end");
239
+ }
198
240
  /**
199
241
  * Put a chunk back at the front of the buffer
200
242
  * Note: unshift is allowed even after end, as it's used to put back already read data
@@ -219,14 +261,22 @@ export class Readable extends EventEmitter {
219
261
  if (!this.objectMode) {
220
262
  this._bufferSize -= this._getChunkSize(chunk);
221
263
  }
222
- return this._applyEncoding(chunk);
264
+ const decoded = this._applyEncoding(chunk);
265
+ if (this._ended && this._bufferedLength() === 0) {
266
+ queueMicrotask(() => this._emitEndOnce());
267
+ }
268
+ return decoded;
223
269
  }
224
270
  // For binary mode, handle size
225
271
  const chunk = this._bufferShift();
226
272
  if (!this.objectMode) {
227
273
  this._bufferSize -= this._getChunkSize(chunk);
228
274
  }
229
- return this._applyEncoding(chunk);
275
+ const decoded = this._applyEncoding(chunk);
276
+ if (this._ended && this._bufferedLength() === 0) {
277
+ queueMicrotask(() => this._emitEndOnce());
278
+ }
279
+ return decoded;
230
280
  }
231
281
  return null;
232
282
  }
@@ -332,11 +382,11 @@ export class Readable extends EventEmitter {
332
382
  if (!this.objectMode) {
333
383
  this._bufferSize -= this._getChunkSize(chunk);
334
384
  }
335
- this.emit("data", chunk);
385
+ this.emit("data", this._applyEncoding(chunk));
336
386
  }
337
387
  // If already ended, emit end event
338
388
  if (this._ended && this._bufferedLength() === 0) {
339
- this.emit("end");
389
+ this._emitEndOnce();
340
390
  }
341
391
  else if (this._read) {
342
392
  // Call user-provided read function asynchronously
@@ -399,26 +449,36 @@ export class Readable extends EventEmitter {
399
449
  }
400
450
  this._pipeTo.push(dest);
401
451
  // Create listeners that we can later remove
452
+ let drainListener;
453
+ const removeDrainListener = () => {
454
+ if (!drainListener) {
455
+ return;
456
+ }
457
+ if (typeof eventTarget.off === "function") {
458
+ eventTarget.off("drain", drainListener);
459
+ }
460
+ else if (typeof eventTarget.removeListener === "function") {
461
+ eventTarget.removeListener("drain", drainListener);
462
+ }
463
+ drainListener = undefined;
464
+ };
402
465
  const dataListener = (chunk) => {
403
466
  // Call destination's write() method (not internal _writable.write())
404
467
  // This ensures Transform.write() logic runs properly
405
468
  const canWrite = dest.write(chunk);
406
469
  if (!canWrite) {
407
470
  this.pause();
408
- if (typeof eventTarget.once === "function") {
409
- eventTarget.once("drain", () => this.resume());
410
- }
411
- else {
412
- const resumeOnce = () => {
413
- if (typeof eventTarget.off === "function") {
414
- eventTarget.off("drain", resumeOnce);
415
- }
416
- else if (typeof eventTarget.removeListener === "function") {
417
- eventTarget.removeListener("drain", resumeOnce);
418
- }
471
+ // Install a removable, once-style drain listener.
472
+ if (!drainListener) {
473
+ drainListener = () => {
474
+ removeDrainListener();
419
475
  this.resume();
420
476
  };
421
- eventTarget.on("drain", resumeOnce);
477
+ eventTarget.on("drain", drainListener);
478
+ const entry = this._pipeListeners.get(dest);
479
+ if (entry) {
480
+ entry.drain = drainListener;
481
+ }
422
482
  }
423
483
  }
424
484
  };
@@ -438,7 +498,8 @@ export class Readable extends EventEmitter {
438
498
  this._pipeListeners.set(dest, {
439
499
  data: dataListener,
440
500
  end: endListener,
441
- error: errorListener
501
+ error: errorListener,
502
+ eventTarget
442
503
  });
443
504
  this.on("data", dataListener);
444
505
  this.once("end", endListener);
@@ -461,6 +522,14 @@ export class Readable extends EventEmitter {
461
522
  this.off("data", listeners.data);
462
523
  this.off("end", listeners.end);
463
524
  this.off("error", listeners.error);
525
+ if (listeners.drain) {
526
+ if (typeof listeners.eventTarget?.off === "function") {
527
+ listeners.eventTarget.off("drain", listeners.drain);
528
+ }
529
+ else if (typeof listeners.eventTarget?.removeListener === "function") {
530
+ listeners.eventTarget.removeListener("drain", listeners.drain);
531
+ }
532
+ }
464
533
  this._pipeListeners.delete(destination);
465
534
  }
466
535
  }
@@ -472,6 +541,14 @@ export class Readable extends EventEmitter {
472
541
  this.off("data", listeners.data);
473
542
  this.off("end", listeners.end);
474
543
  this.off("error", listeners.error);
544
+ if (listeners.drain) {
545
+ if (typeof listeners.eventTarget?.off === "function") {
546
+ listeners.eventTarget.off("drain", listeners.drain);
547
+ }
548
+ else if (typeof listeners.eventTarget?.removeListener === "function") {
549
+ listeners.eventTarget.removeListener("drain", listeners.drain);
550
+ }
551
+ }
475
552
  this._pipeListeners.delete(target);
476
553
  }
477
554
  }
@@ -490,12 +567,26 @@ export class Readable extends EventEmitter {
490
567
  }
491
568
  this._destroyed = true;
492
569
  this._ended = true;
570
+ // Ensure we detach from destinations to avoid leaking listeners.
571
+ this.unpipe();
493
572
  if (error) {
494
573
  this._errored = error;
495
574
  this.emit("error", error);
496
575
  }
497
576
  if (this._reader) {
498
- this._reader.cancel().catch(() => { });
577
+ const reader = this._reader;
578
+ this._reader = null;
579
+ reader
580
+ .cancel()
581
+ .catch(() => { })
582
+ .finally(() => {
583
+ try {
584
+ reader.releaseLock();
585
+ }
586
+ catch {
587
+ // Ignore if a read is still pending
588
+ }
589
+ });
499
590
  }
500
591
  this._closed = true;
501
592
  this.emit("close");
@@ -574,18 +665,38 @@ export class Readable extends EventEmitter {
574
665
  const { done, value } = await this._reader.read();
575
666
  // Check _pushMode again after async read - if push() was called, stop reading
576
667
  if (this._pushMode) {
668
+ if (this._reader) {
669
+ const reader = this._reader;
670
+ this._reader = null;
671
+ try {
672
+ reader.releaseLock();
673
+ }
674
+ catch {
675
+ // Ignore if a read is still pending
676
+ }
677
+ }
577
678
  break;
578
679
  }
579
680
  if (done) {
580
681
  this._ended = true;
581
- this.emit("end");
682
+ this._emitEndOnce();
683
+ if (this._reader) {
684
+ const reader = this._reader;
685
+ this._reader = null;
686
+ try {
687
+ reader.releaseLock();
688
+ }
689
+ catch {
690
+ // Ignore if a read is still pending
691
+ }
692
+ }
582
693
  break;
583
694
  }
584
695
  if (value !== undefined) {
585
696
  // In flowing mode, emit data directly without buffering
586
697
  // Only buffer if not flowing (paused mode)
587
698
  if (this._flowing) {
588
- this.emit("data", value);
699
+ this.emit("data", this._applyEncoding(value));
589
700
  }
590
701
  else {
591
702
  this._buffer.push(value);
@@ -598,6 +709,16 @@ export class Readable extends EventEmitter {
598
709
  }
599
710
  catch (err) {
600
711
  this.emit("error", err);
712
+ if (this._reader) {
713
+ const reader = this._reader;
714
+ this._reader = null;
715
+ try {
716
+ reader.releaseLock();
717
+ }
718
+ catch {
719
+ // Ignore if a read is still pending
720
+ }
721
+ }
601
722
  }
602
723
  finally {
603
724
  this._reading = false;
@@ -605,7 +726,8 @@ export class Readable extends EventEmitter {
605
726
  }
606
727
  /**
607
728
  * Async iterator support
608
- * Uses Web Stream reader for non-push mode, event-based for push mode
729
+ * Uses a unified event-queue iterator with simple backpressure.
730
+ * This matches Node's behavior more closely (iterator drives flowing mode).
609
731
  */
610
732
  async *[Symbol.asyncIterator]() {
611
733
  // First yield any buffered data
@@ -614,117 +736,124 @@ export class Readable extends EventEmitter {
614
736
  if (!this.objectMode) {
615
737
  this._bufferSize -= this._getChunkSize(chunk);
616
738
  }
617
- yield chunk;
739
+ yield this._applyEncoding(chunk);
618
740
  }
619
- // If already ended, we're done
620
741
  if (this._ended) {
621
742
  return;
622
743
  }
623
- // For controllable streams (not created from external Web Stream),
624
- // use event-based iteration since data comes from push() calls
625
- if (!this._webStreamMode) {
626
- // Create a promise-based queue for incoming data
627
- const dataQueue = [];
628
- let resolveNext = null;
629
- let rejectNext = null;
630
- let done = false;
631
- let streamError = null;
632
- let dataQueueIndex = 0;
633
- const dataHandler = (chunk) => {
634
- if (resolveNext) {
635
- resolveNext(chunk);
636
- resolveNext = null;
637
- rejectNext = null;
638
- }
639
- else {
640
- dataQueue.push(chunk);
641
- }
642
- };
643
- const endHandler = () => {
644
- done = true;
645
- if (resolveNext) {
646
- resolveNext(null);
647
- resolveNext = null;
648
- rejectNext = null;
649
- }
650
- };
651
- const errorHandler = (err) => {
652
- done = true;
653
- streamError = err;
654
- if (rejectNext) {
655
- rejectNext(err);
656
- resolveNext = null;
657
- rejectNext = null;
658
- }
659
- };
660
- const closeHandler = () => {
661
- // If stream closed without end event (e.g., after destroy()),
662
- // treat it as done
663
- done = true;
664
- if (resolveNext) {
665
- resolveNext(null);
666
- resolveNext = null;
667
- rejectNext = null;
668
- }
669
- };
670
- this.on("data", dataHandler);
671
- this.on("end", endHandler);
672
- this.on("error", errorHandler);
673
- this.on("close", closeHandler);
674
- try {
675
- // Enter flowing mode
676
- this.resume();
677
- while (!done || dataQueueIndex < dataQueue.length) {
678
- // Check for error before processing
679
- if (streamError) {
680
- throw streamError;
681
- }
682
- if (dataQueueIndex < dataQueue.length) {
683
- const chunk = dataQueue[dataQueueIndex++];
684
- if (dataQueueIndex >= 1024 && dataQueueIndex * 2 >= dataQueue.length) {
685
- dataQueue.splice(0, dataQueueIndex);
686
- dataQueueIndex = 0;
687
- }
688
- yield chunk;
689
- }
690
- else if (!done) {
691
- const chunk = await new Promise((resolve, reject) => {
692
- resolveNext = resolve;
693
- rejectNext = reject;
694
- });
695
- if (chunk !== null) {
696
- yield chunk;
697
- }
698
- }
699
- }
700
- // Check for error after loop
701
- if (streamError) {
702
- throw streamError;
703
- }
744
+ const highWaterMark = this.readableHighWaterMark;
745
+ const lowWaterMark = Math.max(0, Math.floor(highWaterMark / 2));
746
+ const chunkSizeForBackpressure = (chunk) => {
747
+ if (this.objectMode) {
748
+ return 1;
704
749
  }
705
- finally {
706
- this.off("data", dataHandler);
707
- this.off("end", endHandler);
708
- this.off("error", errorHandler);
709
- this.off("close", closeHandler);
750
+ if (chunk instanceof Uint8Array) {
751
+ return chunk.byteLength;
710
752
  }
711
- return;
712
- }
713
- // For Web Stream mode, use the underlying reader
714
- if (!this._reader) {
715
- this._reader = this._stream.getReader();
716
- }
753
+ if (typeof chunk === "string") {
754
+ return chunk.length;
755
+ }
756
+ return 1;
757
+ };
758
+ const dataQueue = [];
759
+ let dataQueueIndex = 0;
760
+ let queuedSize = 0;
761
+ let resolveNext = null;
762
+ let rejectNext = null;
763
+ let done = false;
764
+ let pausedByIterator = false;
765
+ let streamError = null;
766
+ const dataHandler = (chunk) => {
767
+ // data events are already encoding-aware; do not decode again here.
768
+ if (resolveNext) {
769
+ resolveNext(chunk);
770
+ resolveNext = null;
771
+ rejectNext = null;
772
+ }
773
+ else {
774
+ dataQueue.push(chunk);
775
+ }
776
+ queuedSize += chunkSizeForBackpressure(chunk);
777
+ if (!pausedByIterator && queuedSize >= highWaterMark) {
778
+ pausedByIterator = true;
779
+ this.pause();
780
+ }
781
+ };
782
+ const endHandler = () => {
783
+ done = true;
784
+ if (resolveNext) {
785
+ resolveNext(null);
786
+ resolveNext = null;
787
+ rejectNext = null;
788
+ }
789
+ };
790
+ const closeHandler = () => {
791
+ done = true;
792
+ if (resolveNext) {
793
+ resolveNext(null);
794
+ resolveNext = null;
795
+ rejectNext = null;
796
+ }
797
+ };
798
+ const errorHandler = (err) => {
799
+ done = true;
800
+ streamError = err;
801
+ if (rejectNext) {
802
+ rejectNext(err);
803
+ resolveNext = null;
804
+ rejectNext = null;
805
+ }
806
+ };
807
+ this.on("data", dataHandler);
808
+ this.on("end", endHandler);
809
+ this.on("error", errorHandler);
810
+ this.on("close", closeHandler);
717
811
  try {
812
+ // Iterator consumption should drive the stream.
813
+ this.resume();
718
814
  while (true) {
719
- const { done, value } = await this._reader.read();
815
+ if (streamError) {
816
+ throw streamError;
817
+ }
818
+ if (dataQueueIndex < dataQueue.length) {
819
+ const chunk = dataQueue[dataQueueIndex++];
820
+ queuedSize -= chunkSizeForBackpressure(chunk);
821
+ if (dataQueueIndex >= 1024 && dataQueueIndex * 2 >= dataQueue.length) {
822
+ dataQueue.splice(0, dataQueueIndex);
823
+ dataQueueIndex = 0;
824
+ }
825
+ if (pausedByIterator && queuedSize <= lowWaterMark && !done && !this._destroyed) {
826
+ pausedByIterator = false;
827
+ this.resume();
828
+ }
829
+ yield chunk;
830
+ continue;
831
+ }
720
832
  if (done) {
721
833
  break;
722
834
  }
723
- yield value;
835
+ const chunk = await new Promise((resolve, reject) => {
836
+ resolveNext = resolve;
837
+ rejectNext = reject;
838
+ });
839
+ if (chunk !== null) {
840
+ queuedSize -= chunkSizeForBackpressure(chunk);
841
+ if (pausedByIterator && queuedSize <= lowWaterMark && !done && !this._destroyed) {
842
+ pausedByIterator = false;
843
+ this.resume();
844
+ }
845
+ yield chunk;
846
+ }
847
+ }
848
+ if (streamError) {
849
+ throw streamError;
724
850
  }
725
851
  }
726
852
  finally {
727
- this._reader.releaseLock();
853
+ this.off("data", dataHandler);
854
+ this.off("end", endHandler);
855
+ this.off("error", errorHandler);
856
+ this.off("close", closeHandler);
728
857
  }
729
858
  }
730
859
  /**
@@ -943,6 +1072,32 @@ export class Readable extends EventEmitter {
943
1072
  return stream;
944
1073
  }
945
1074
  }
1075
+ function toAsyncIterable(iterable) {
1076
+ if (iterable && typeof iterable[Symbol.asyncIterator] === "function") {
1077
+ return iterable;
1078
+ }
1079
+ return (async function* () {
1080
+ for (const item of iterable) {
1081
+ yield item;
1082
+ }
1083
+ })();
1084
+ }
1085
+ function pumpAsyncIterableToReadable(readable, iterable) {
1086
+ (async () => {
1087
+ try {
1088
+ for await (const chunk of iterable) {
1089
+ if (!readable.push(chunk)) {
1090
+ // Simple backpressure: yield to consumer.
1091
+ await new Promise(resolve => setTimeout(resolve, 0));
1092
+ }
1093
+ }
1094
+ readable.push(null);
1095
+ }
1096
+ catch (err) {
1097
+ readable.destroy(err);
1098
+ }
1099
+ })();
1100
+ }
946
1101
  // =============================================================================
947
1102
  // Writable Stream Wrapper
948
1103
  // =============================================================================
@@ -960,10 +1115,12 @@ export class Writable extends EventEmitter {
960
1115
  this._closed = false;
961
1116
  this._pendingWrites = 0;
962
1117
  this._writableLength = 0;
1118
+ this._needDrain = false;
963
1119
  this._corked = 0;
964
1120
  this._corkedChunks = [];
965
1121
  this._defaultEncoding = "utf8";
966
1122
  this._aborted = false;
1123
+ this._ownsStream = false;
967
1124
  this.objectMode = options?.objectMode ?? false;
968
1125
  this.writableHighWaterMark = options?.highWaterMark ?? 16384;
969
1126
  this.autoDestroy = options?.autoDestroy ?? true;
@@ -979,8 +1136,10 @@ export class Writable extends EventEmitter {
979
1136
  }
980
1137
  if (options?.stream) {
981
1138
  this._stream = options.stream;
1139
+ this._ownsStream = false;
982
1140
  }
983
1141
  else {
1142
+ this._ownsStream = true;
984
1143
  // Create bound references to instance properties/methods for use in WritableStream callbacks
985
1144
  const getWriteFunc = () => this._writeFunc;
986
1145
  const getFinalFunc = () => this._finalFunc;
@@ -1087,25 +1246,37 @@ export class Writable extends EventEmitter {
1087
1246
  this._writableLength += chunkSize;
1088
1247
  return this._writableLength < this.writableHighWaterMark;
1089
1248
  }
1090
- return this._doWrite(chunk, cb);
1249
+ const ok = this._doWrite(chunk, cb);
1250
+ if (!ok) {
1251
+ this._needDrain = true;
1252
+ }
1253
+ return ok;
1091
1254
  }
1092
1255
  _doWrite(chunk, callback) {
1093
1256
  // Track pending writes for writableLength
1094
1257
  const chunkSize = this._getChunkSize(chunk);
1095
1258
  this._pendingWrites++;
1096
1259
  this._writableLength += chunkSize;
1097
- this._getWriter()
1260
+ const writer = this._getWriter();
1261
+ writer
1098
1262
  .write(chunk)
1099
1263
  .then(() => {
1100
1264
  this._pendingWrites--;
1101
1265
  this._writableLength -= chunkSize;
1102
- this.emit("drain");
1266
+ if (this._needDrain && this._writableLength < this.writableHighWaterMark) {
1267
+ this._needDrain = false;
1268
+ this.emit("drain");
1269
+ }
1103
1270
  callback?.(null);
1104
1271
  })
1105
1272
  .catch(err => {
1106
1273
  this._pendingWrites--;
1107
1274
  this._writableLength -= chunkSize;
1108
- this.emit("error", err);
1275
+ // Avoid double-emitting if we're already in an errored/destroyed state.
1276
+ if (!this._destroyed) {
1277
+ this._errored = err;
1278
+ this.emit("error", err);
1279
+ }
1109
1280
  callback?.(err);
1110
1281
  });
1111
1282
  // Return false if we've exceeded high water mark (for backpressure)
@@ -1136,12 +1307,29 @@ export class Writable extends EventEmitter {
1136
1307
  : callback;
1137
1308
  const finish = async () => {
1138
1309
  try {
1310
+ const writer = this._getWriter();
1139
1311
  if (chunk !== undefined) {
1140
- await this._getWriter().write(chunk);
1312
+ await writer.write(chunk);
1313
+ }
1314
+ await writer.close();
1315
+ if (this._writer === writer) {
1316
+ this._writer = null;
1317
+ try {
1318
+ writer.releaseLock();
1319
+ }
1320
+ catch {
1321
+ // Ignore
1322
+ }
1323
+ }
1324
+ // If we own the underlying Web WritableStream, its `close()` handler already
1325
+ // emits finish/close. For external streams, we must emit finish ourselves.
1326
+ if (!this._ownsStream) {
1327
+ this._finished = true;
1328
+ this.emit("finish");
1329
+ if (this.emitClose) {
1330
+ this.emit("close");
1331
+ }
1141
1332
  }
1142
- await this._getWriter().close();
1143
- this._finished = true;
1144
- this.emit("finish");
1145
1333
  if (cb) {
1146
1334
  cb();
1147
1335
  }
@@ -1162,12 +1350,24 @@ export class Writable extends EventEmitter {
1162
1350
  }
1163
1351
  this._destroyed = true;
1164
1352
  this._ended = true;
1165
- if (error) {
1353
+ if (error && !this._errored) {
1166
1354
  this._errored = error;
1167
1355
  this.emit("error", error);
1168
1356
  }
1169
1357
  if (this._writer) {
1170
- this._writer.abort(error).catch(() => { });
1358
+ const writer = this._writer;
1359
+ this._writer = null;
1360
+ writer
1361
+ .abort(error)
1362
+ .catch(() => { })
1363
+ .finally(() => {
1364
+ try {
1365
+ writer.releaseLock();
1366
+ }
1367
+ catch {
1368
+ // Ignore
1369
+ }
1370
+ });
1171
1371
  }
1172
1372
  this._closed = true;
1173
1373
  this.emit("close");
@@ -1318,342 +1518,259 @@ export function normalizeWritable(stream) {
1318
1518
  */
1319
1519
  export class Transform extends EventEmitter {
1320
1520
  /**
1321
- * Push data to the readable side (Node.js compatibility)
1322
- * Can be called from within transform callback
1521
+ * Push data to the readable side (Node.js compatibility).
1522
+ * Intended to be called from within transform/flush.
1323
1523
  */
1324
1524
  push(chunk) {
1325
- if (chunk === null) {
1326
- return false;
1327
- }
1328
- if (this._transformController) {
1329
- // If we're in a transform callback, enqueue directly
1330
- this._transformController.enqueue(chunk);
1331
- }
1332
- else {
1333
- // Otherwise buffer for later
1334
- this._pushBuffer.push(chunk);
1335
- }
1336
- return true;
1525
+ return this._readable.push(chunk);
1337
1526
  }
1338
1527
  constructor(options) {
1339
1528
  super();
1340
- this._ended = false;
1341
1529
  this._destroyed = false;
1530
+ this._ended = false;
1342
1531
  this._errored = false;
1343
- // Buffer for Node.js style push() calls during transform
1344
- this._pushBuffer = [];
1345
- // Controller for enqueueing pushed data (set during transform execution)
1346
- this._transformController = null;
1347
- // Buffer for writes that occur after end() but before writable is closed
1348
- this._pendingEndWrites = [];
1349
- // Whether end() has been called but writable not yet closed
1350
- this._endPending = false;
1351
- // Track if we've already set up data forwarding
1352
1532
  this._dataForwardingSetup = false;
1353
- /** @internal - whether we have a data event consumer */
1354
- this._hasDataConsumer = false;
1355
- /** @internal - whether we're auto-consuming the readable */
1356
- this._readableConsuming = false;
1357
- /** @internal - buffer for auto-consumed data */
1358
- this._autoConsumedBuffer = [];
1359
- this._autoConsumedBufferIndex = 0;
1360
- /** @internal - whether auto-consume has ended */
1361
- this._autoConsumeEnded = false;
1362
- /** @internal - promise that resolves when auto-consume finishes */
1363
- this._autoConsumePromise = null;
1364
- /** @internal - list of piped destinations for forwarding auto-consumed data */
1365
- this._pipeDestinations = [];
1533
+ this._endTimer = null;
1534
+ this._webStream = null;
1535
+ this._sideForwardingCleanup = null;
1366
1536
  this.objectMode = options?.objectMode ?? false;
1367
- const userTransform = options?.transform;
1368
- const userFlush = options?.flush;
1369
- // Create bound references for use in TransformStream callbacks
1370
- const setController = (ctrl) => {
1371
- this._transformController = ctrl;
1372
- };
1373
- const emitEvent = (event, ...args) => {
1374
- if (event === "error") {
1375
- // Only emit error once to prevent duplicate events
1376
- if (this._errored) {
1377
- return false;
1378
- }
1379
- this._errored = true;
1380
- // Also destroy the writable to prevent further writes
1381
- this._writable.destroy(args[0]);
1537
+ this._transformImpl = options?.transform;
1538
+ this._flushImpl = options?.flush;
1539
+ this._readable = new Readable({
1540
+ objectMode: this.objectMode
1541
+ });
1542
+ this._writable = new Writable({
1543
+ objectMode: this.objectMode,
1544
+ write: (chunk, _encoding, callback) => {
1545
+ this._runTransform(chunk)
1546
+ .then(() => callback(null))
1547
+ .catch(err => callback(err));
1548
+ },
1549
+ final: callback => {
1550
+ this._runFlush()
1551
+ .then(() => {
1552
+ this._readable.push(null);
1553
+ callback(null);
1554
+ })
1555
+ .catch(err => callback(err));
1382
1556
  }
1383
- return this.emit(event, ...args);
1384
- };
1385
- const getInstance = () => this;
1386
- // Check if subclass overrides _transform (for Node.js compatibility)
1387
- // We need to check this at runtime since the subclass constructor runs after super()
1388
- const hasSubclassTransform = () => {
1389
- // If userTransform was provided in options, use that
1390
- if (userTransform) {
1391
- return false;
1557
+ });
1558
+ this._setupSideForwarding();
1559
+ }
1560
+ _setupSideForwarding() {
1561
+ if (this._sideForwardingCleanup) {
1562
+ this._sideForwardingCleanup();
1563
+ this._sideForwardingCleanup = null;
1564
+ }
1565
+ const registry = createListenerRegistry();
1566
+ registry.once(this._readable, "end", () => this.emit("end"));
1567
+ registry.add(this._readable, "error", err => this._emitErrorOnce(err));
1568
+ registry.once(this._writable, "finish", () => this.emit("finish"));
1569
+ registry.add(this._writable, "drain", () => this.emit("drain"));
1570
+ registry.add(this._writable, "error", err => this._emitErrorOnce(err));
1571
+ this._sideForwardingCleanup = () => registry.cleanup();
1572
+ }
1573
+ _scheduleEnd() {
1574
+ if (this._destroyed || this._errored) {
1575
+ return;
1576
+ }
1577
+ if (this._writable.writableEnded) {
1578
+ return;
1579
+ }
1580
+ if (this._endTimer) {
1581
+ clearTimeout(this._endTimer);
1582
+ }
1583
+ // Defer closing to allow writes triggered during 'data' callbacks.
1584
+ this._endTimer = setTimeout(() => {
1585
+ this._endTimer = null;
1586
+ if (this._destroyed || this._errored || this._writable.writableEnded) {
1587
+ return;
1392
1588
  }
1393
- // Check if _transform is overridden (not the base class no-op)
1394
- const proto = Object.getPrototypeOf(this);
1395
- return proto._transform !== Transform.prototype._transform;
1396
- };
1397
- const hasSubclassFlush = () => {
1398
- if (userFlush) {
1399
- return false;
1589
+ this._writable.end();
1590
+ }, 0);
1591
+ }
1592
+ _emitErrorOnce(err) {
1593
+ if (this._errored) {
1594
+ return;
1595
+ }
1596
+ this._errored = true;
1597
+ const error = err instanceof Error ? err : new Error(String(err));
1598
+ this.emit("error", error);
1599
+ if (!this._destroyed) {
1600
+ this._destroyed = true;
1601
+ this._readable.destroy(error);
1602
+ this._writable.destroy(error);
1603
+ queueMicrotask(() => this.emit("close"));
1604
+ }
1605
+ }
1606
+ _hasSubclassTransform() {
1607
+ if (this._transformImpl) {
1608
+ return false;
1609
+ }
1610
+ const proto = Object.getPrototypeOf(this);
1611
+ return proto._transform !== Transform.prototype._transform;
1612
+ }
1613
+ _hasSubclassFlush() {
1614
+ if (this._flushImpl) {
1615
+ return false;
1616
+ }
1617
+ const proto = Object.getPrototypeOf(this);
1618
+ return proto._flush !== Transform.prototype._flush;
1619
+ }
1620
+ async _runTransform(chunk) {
1621
+ if (this._destroyed || this._errored) {
1622
+ throw new Error(this._errored ? "Cannot write after stream errored" : "Cannot write after stream destroyed");
1623
+ }
1624
+ try {
1625
+ if (this._hasSubclassTransform()) {
1626
+ await new Promise((resolve, reject) => {
1627
+ this._transform(chunk, "utf8", (err, data) => {
1628
+ if (err) {
1629
+ reject(err);
1630
+ return;
1631
+ }
1632
+ if (data !== undefined) {
1633
+ this.push(data);
1634
+ }
1635
+ resolve();
1636
+ });
1637
+ });
1638
+ return;
1400
1639
  }
1401
- const proto = Object.getPrototypeOf(this);
1402
- return proto._flush !== Transform.prototype._flush;
1403
- };
1404
- this._stream = new TransformStream({
1405
- transform: async (chunk, controller) => {
1406
- // Skip processing if already errored
1407
- if (this._errored) {
1408
- return;
1409
- }
1410
- try {
1411
- // Set controller for push() to use
1412
- setController(controller);
1413
- // Check for subclass _transform override first
1414
- if (hasSubclassTransform()) {
1415
- // Call subclass _transform method (Node.js style)
1416
- // _transform signature is (chunk, encoding, callback)
1417
- await new Promise((resolve, reject) => {
1418
- this._transform(chunk, "utf8", (err, data) => {
1419
- if (err) {
1420
- reject(err);
1421
- }
1422
- else {
1423
- if (data !== undefined) {
1424
- controller.enqueue(data);
1425
- }
1426
- resolve();
1427
- }
1428
- });
1429
- });
1430
- }
1431
- else if (userTransform) {
1432
- const transformParamCount = userTransform.length;
1433
- if (transformParamCount >= 3) {
1434
- // Node.js style: transform(chunk, encoding, callback)
1435
- await new Promise((resolve, reject) => {
1436
- userTransform.call(getInstance(), chunk, "utf8", (err, data) => {
1437
- if (err) {
1438
- reject(err);
1439
- }
1440
- else {
1441
- if (data !== undefined) {
1442
- controller.enqueue(data);
1443
- }
1444
- resolve();
1445
- }
1446
- });
1447
- });
1640
+ const userTransform = this._transformImpl;
1641
+ if (!userTransform) {
1642
+ this.push(chunk);
1643
+ return;
1644
+ }
1645
+ const paramCount = userTransform.length;
1646
+ if (paramCount >= 3) {
1647
+ await new Promise((resolve, reject) => {
1648
+ userTransform.call(this, chunk, "utf8", (err, data) => {
1649
+ if (err) {
1650
+ reject(err);
1651
+ return;
1448
1652
  }
1449
- else if (transformParamCount === 2) {
1450
- await new Promise((resolve, reject) => {
1451
- userTransform.call(getInstance(), chunk, (err, data) => {
1452
- if (err) {
1453
- reject(err);
1454
- }
1455
- else {
1456
- if (data !== undefined) {
1457
- controller.enqueue(data);
1458
- }
1459
- resolve();
1460
- }
1461
- });
1462
- });
1653
+ if (data !== undefined) {
1654
+ this.push(data);
1463
1655
  }
1464
- else {
1465
- // Simple style: transform(chunk) => result
1466
- const result = userTransform.call(getInstance(), chunk);
1467
- if (result && typeof result.then === "function") {
1468
- const awaitedResult = await result;
1469
- if (awaitedResult !== undefined) {
1470
- controller.enqueue(awaitedResult);
1471
- }
1472
- }
1473
- else {
1474
- if (result !== undefined) {
1475
- controller.enqueue(result);
1476
- }
1477
- }
1656
+ resolve();
1657
+ });
1658
+ });
1659
+ return;
1660
+ }
1661
+ if (paramCount === 2) {
1662
+ await new Promise((resolve, reject) => {
1663
+ userTransform.call(this, chunk, (err, data) => {
1664
+ if (err) {
1665
+ reject(err);
1666
+ return;
1478
1667
  }
1479
- }
1480
- else {
1481
- // Default: pass through
1482
- controller.enqueue(chunk);
1483
- }
1484
- }
1485
- catch (err) {
1486
- controller.error(err);
1487
- emitEvent("error", err);
1488
- }
1489
- finally {
1490
- setController(null);
1668
+ if (data !== undefined) {
1669
+ this.push(data);
1670
+ }
1671
+ resolve();
1672
+ });
1673
+ });
1674
+ return;
1675
+ }
1676
+ const result = userTransform.call(this, chunk);
1677
+ if (result && typeof result.then === "function") {
1678
+ const awaited = await result;
1679
+ if (awaited !== undefined) {
1680
+ this.push(awaited);
1491
1681
  }
1492
- },
1493
- flush: async (controller) => {
1494
- try {
1495
- setController(controller);
1496
- // Check for subclass _flush override first
1497
- if (hasSubclassFlush()) {
1498
- await new Promise((resolve, reject) => {
1499
- this._flush((err, data) => {
1500
- if (err) {
1501
- reject(err);
1502
- }
1503
- else {
1504
- if (data !== undefined) {
1505
- controller.enqueue(data);
1506
- }
1507
- resolve();
1508
- }
1509
- });
1510
- });
1511
- }
1512
- else if (userFlush) {
1513
- const flushParamCount = userFlush.length;
1514
- if (flushParamCount >= 1) {
1515
- // Node.js style: flush(callback)
1516
- await new Promise((resolve, reject) => {
1517
- userFlush.call(getInstance(), (err, data) => {
1518
- if (err) {
1519
- reject(err);
1520
- }
1521
- else {
1522
- if (data !== undefined) {
1523
- controller.enqueue(data);
1524
- }
1525
- resolve();
1526
- }
1527
- });
1528
- });
1682
+ return;
1683
+ }
1684
+ if (result !== undefined) {
1685
+ this.push(result);
1686
+ }
1687
+ }
1688
+ catch (err) {
1689
+ this._emitErrorOnce(err);
1690
+ throw err;
1691
+ }
1692
+ }
1693
+ async _runFlush() {
1694
+ if (this._destroyed || this._errored) {
1695
+ return;
1696
+ }
1697
+ try {
1698
+ if (this._hasSubclassFlush()) {
1699
+ await new Promise((resolve, reject) => {
1700
+ this._flush((err, data) => {
1701
+ if (err) {
1702
+ reject(err);
1703
+ return;
1529
1704
  }
1530
- else {
1531
- // Simple style: flush() => result
1532
- const result = userFlush.call(getInstance());
1533
- if (result && typeof result.then === "function") {
1534
- const awaitedResult = await result;
1535
- if (awaitedResult !== undefined && awaitedResult !== null) {
1536
- controller.enqueue(awaitedResult);
1537
- }
1538
- }
1539
- else {
1540
- if (result !== undefined && result !== null) {
1541
- controller.enqueue(result);
1542
- }
1543
- }
1705
+ if (data !== undefined) {
1706
+ this.push(data);
1544
1707
  }
1545
- }
1546
- // No flush defined - nothing to do
1547
- }
1548
- catch (err) {
1549
- controller.error(err);
1550
- emitEvent("error", err);
1551
- }
1552
- finally {
1553
- setController(null);
1554
- }
1708
+ resolve();
1709
+ });
1710
+ });
1711
+ return;
1555
1712
  }
1556
- });
1557
- this._readable = new Readable({
1558
- stream: this._stream.readable,
1559
- objectMode: this.objectMode
1560
- });
1561
- this._writable = new Writable({
1562
- stream: this._stream.writable,
1563
- objectMode: this.objectMode
1564
- });
1565
- // Forward non-data events (data forwarding is lazy to avoid premature flowing)
1566
- this._readable.on("end", () => this.emit("end"));
1567
- // Only forward errors if not already errored (to prevent duplicate events)
1568
- this._readable.on("error", err => {
1569
- if (!this._errored) {
1570
- this._errored = true;
1571
- this.emit("error", err);
1713
+ const userFlush = this._flushImpl;
1714
+ if (!userFlush) {
1715
+ return;
1716
+ }
1717
+ const paramCount = userFlush.length;
1718
+ if (paramCount >= 1) {
1719
+ await new Promise((resolve, reject) => {
1720
+ userFlush.call(this, (err, data) => {
1721
+ if (err) {
1722
+ reject(err);
1723
+ return;
1724
+ }
1725
+ if (data !== undefined) {
1726
+ this.push(data);
1727
+ }
1728
+ resolve();
1729
+ });
1730
+ });
1731
+ return;
1732
+ }
1733
+ const result = userFlush.call(this);
1734
+ if (result && typeof result.then === "function") {
1735
+ const awaited = await result;
1736
+ if (awaited !== undefined && awaited !== null) {
1737
+ this.push(awaited);
1738
+ }
1739
+ return;
1572
1740
  }
1573
- });
1574
- this._writable.on("finish", () => this.emit("finish"));
1575
- this._writable.on("drain", () => this.emit("drain"));
1576
- // Only forward errors if not already errored (to prevent duplicate events)
1577
- this._writable.on("error", err => {
1578
- if (!this._errored) {
1579
- this._errored = true;
1580
- this.emit("error", err);
1741
+ if (result !== undefined && result !== null) {
1742
+ this.push(result);
1581
1743
  }
1582
- });
1744
+ }
1745
+ catch (err) {
1746
+ this._emitErrorOnce(err);
1747
+ throw err;
1748
+ }
1583
1749
  }
1584
1750
  /**
1585
- * Override on to start flowing when data listener is added
1751
+ * Override on() to lazily forward readable 'data' events.
1752
+ * Avoids starting flowing mode unless requested.
1586
1753
  */
1587
1754
  on(event, listener) {
1588
- // Set up data forwarding when first external data listener is added
1589
1755
  if (event === "data" && !this._dataForwardingSetup) {
1590
1756
  this._dataForwardingSetup = true;
1591
- this._readable.on("data", data => this.emit("data", data));
1592
- }
1593
- super.on(event, listener);
1594
- // When data listener is added, mark as having consumer
1595
- // and start the readable in flowing mode
1596
- if (event === "data") {
1597
- this._hasDataConsumer = true;
1598
- this._readable.resume();
1757
+ this._readable.on("data", chunk => this.emit("data", chunk));
1599
1758
  }
1600
- return this;
1759
+ return super.on(event, listener);
1601
1760
  }
1602
1761
  write(chunk, encodingOrCallback, callback) {
1603
1762
  const cb = typeof encodingOrCallback === "function" ? encodingOrCallback : callback;
1604
- if (this._destroyed || this._errored) {
1605
- const err = new Error(this._errored ? "Cannot write after stream errored" : "Cannot write after stream destroyed");
1606
- queueMicrotask(() => this.emit("error", err));
1607
- cb?.(err);
1608
- return false;
1609
- }
1610
- // Ensure readable is being consumed to allow transform to execute
1611
- // This matches Node.js behavior where transform executes immediately on write
1612
- // Only auto-consume if no explicit consumer (data listener or pipe)
1613
- if (!this._readableConsuming && !this._hasDataConsumer) {
1614
- this._readableConsuming = true;
1615
- this._startAutoConsume();
1616
- }
1617
- // If end() was called but writable not yet closed, buffer the write
1618
- // This allows writes during data event handlers to be processed
1619
- if (this._endPending) {
1620
- this._pendingEndWrites.push({ chunk, callback: cb });
1621
- return true;
1763
+ // If end() has been requested, keep the close deferred as long as writes continue.
1764
+ if (this._ended && !this._writable.writableEnded) {
1765
+ this._scheduleEnd();
1622
1766
  }
1623
1767
  return this._writable.write(chunk, cb);
1624
1768
  }
1625
- /** @internal - auto-consume readable to allow transform to execute */
1626
- _startAutoConsume() {
1627
- this._autoConsumePromise = (async () => {
1628
- try {
1629
- for await (const chunk of this._readable) {
1630
- // Buffer the data for later retrieval
1631
- this._autoConsumedBuffer.push(chunk);
1632
- // Forward to any piped destinations
1633
- for (const dest of this._pipeDestinations) {
1634
- dest.write(chunk);
1635
- }
1636
- // Also emit data event for listeners
1637
- this.emit("data", chunk);
1638
- }
1639
- this._autoConsumeEnded = true;
1640
- // End all piped destinations
1641
- for (const dest of this._pipeDestinations) {
1642
- dest.end();
1643
- }
1644
- this.emit("end");
1645
- }
1646
- catch (err) {
1647
- this.emit("error", err);
1648
- }
1649
- })();
1650
- }
1651
1769
  end(chunkOrCallback, encodingOrCallback, callback) {
1652
1770
  if (this._ended) {
1653
1771
  return this;
1654
1772
  }
1655
1773
  this._ended = true;
1656
- this._endPending = true;
1657
1774
  const chunk = typeof chunkOrCallback === "function" ? undefined : chunkOrCallback;
1658
1775
  const cb = typeof chunkOrCallback === "function"
1659
1776
  ? chunkOrCallback
@@ -1666,18 +1783,7 @@ export class Transform extends EventEmitter {
1666
1783
  if (chunk !== undefined) {
1667
1784
  this._writable.write(chunk);
1668
1785
  }
1669
- // Use setTimeout(0) instead of queueMicrotask to ensure all transform
1670
- // processing and data events complete before we close the writable.
1671
- // Microtasks run before the TransformStream processes data.
1672
- setTimeout(() => {
1673
- // Process any writes that occurred during data events
1674
- for (const { chunk: pendingChunk, callback } of this._pendingEndWrites) {
1675
- this._writable.write(pendingChunk, callback);
1676
- }
1677
- this._pendingEndWrites = [];
1678
- this._endPending = false;
1679
- this._writable.end();
1680
- }, 0);
1786
+ this._scheduleEnd();
1681
1787
  return this;
1682
1788
  }
1683
1789
  /**
@@ -1687,31 +1793,9 @@ export class Transform extends EventEmitter {
1687
1793
  return this._readable.read(size);
1688
1794
  }
1689
1795
  /**
1690
- * Pipe to another stream (writable, transform, or duplex)
1796
+ * Pipe readable side to destination
1691
1797
  */
1692
1798
  pipe(destination) {
1693
- // Mark as having consumer to prevent new auto-consume from starting
1694
- this._hasDataConsumer = true;
1695
- // Get the writable target - handle both Transform (with internal _writable) and plain Writable
1696
- const dest = destination;
1697
- const target = dest?._writable ?? dest;
1698
- // Register destination for forwarding
1699
- this._pipeDestinations.push(target);
1700
- // If auto-consume is running or has run, we need to handle buffered data ourselves
1701
- if (this._readableConsuming) {
1702
- // Forward any buffered data from auto-consume to the destination
1703
- for (let i = 0; i < this._autoConsumedBuffer.length; i++) {
1704
- target.write(this._autoConsumedBuffer[i]);
1705
- }
1706
- // If auto-consume has ended, end the destination too
1707
- if (this._autoConsumeEnded) {
1708
- target.end();
1709
- }
1710
- // Don't call _readable.pipe() - auto-consume already consumed _readable
1711
- // Future data will be forwarded via the 'data' event listener below
1712
- return destination;
1713
- }
1714
- // No auto-consume running - use normal pipe through _readable
1715
1799
  return this._readable.pipe(destination);
1716
1800
  }
1717
1801
  /**
@@ -1749,6 +1833,10 @@ export class Transform extends EventEmitter {
1749
1833
  return;
1750
1834
  }
1751
1835
  this._destroyed = true;
1836
+ if (this._sideForwardingCleanup) {
1837
+ this._sideForwardingCleanup();
1838
+ this._sideForwardingCleanup = null;
1839
+ }
1752
1840
  this._readable.destroy(error);
1753
1841
  this._writable.destroy(error);
1754
1842
  queueMicrotask(() => this.emit("close"));
@@ -1757,7 +1845,44 @@ export class Transform extends EventEmitter {
1757
1845
  * Get the underlying Web TransformStream
1758
1846
  */
1759
1847
  get webStream() {
1760
- return this._stream;
1848
+ if (this._webStream) {
1849
+ return this._webStream;
1850
+ }
1851
+ // Web Streams interop layer.
1852
+ const iterator = this[Symbol.asyncIterator]();
1853
+ const readable = new ReadableStream({
1854
+ pull: async (controller) => {
1855
+ const { done, value } = await iterator.next();
1856
+ if (done) {
1857
+ controller.close();
1858
+ return;
1859
+ }
1860
+ controller.enqueue(value);
1861
+ },
1862
+ cancel: reason => {
1863
+ this.destroy(reason instanceof Error ? reason : new Error(String(reason)));
1864
+ }
1865
+ });
1866
+ const writable = new WritableStream({
1867
+ write: chunk => new Promise((resolve, reject) => {
1868
+ this.write(chunk, err => {
1869
+ if (err) {
1870
+ reject(err);
1871
+ }
1872
+ else {
1873
+ resolve();
1874
+ }
1875
+ });
1876
+ }),
1877
+ close: () => new Promise(resolve => {
1878
+ this.end(() => resolve());
1879
+ }),
1880
+ abort: reason => {
1881
+ this.destroy(reason instanceof Error ? reason : new Error(String(reason)));
1882
+ }
1883
+ });
1884
+ this._webStream = { readable, writable };
1885
+ return this._webStream;
1761
1886
  }
1762
1887
  get readable() {
1763
1888
  return this._readable.readable;
@@ -1799,19 +1924,6 @@ export class Transform extends EventEmitter {
1799
1924
  * Async iterator support
1800
1925
  */
1801
1926
  async *[Symbol.asyncIterator]() {
1802
- // If auto-consume is running, wait for it to finish and use its buffer
1803
- if (this._autoConsumePromise) {
1804
- await this._autoConsumePromise;
1805
- // Yield all buffered data
1806
- while (this._autoConsumedBufferIndex < this._autoConsumedBuffer.length) {
1807
- yield this._autoConsumedBuffer[this._autoConsumedBufferIndex++];
1808
- }
1809
- // Reset when drained to avoid prefix growth
1810
- this._autoConsumedBuffer.length = 0;
1811
- this._autoConsumedBufferIndex = 0;
1812
- return;
1813
- }
1814
- // Otherwise delegate to readable's iterator
1815
1927
  yield* this._readable[Symbol.asyncIterator]();
1816
1928
  }
1817
1929
  // =========================================================================
@@ -1822,23 +1934,18 @@ export class Transform extends EventEmitter {
1822
1934
  */
1823
1935
  static fromWeb(webStream, options) {
1824
1936
  const transform = new Transform(options);
1825
- // Connect the web stream - set the internal _stream property
1826
- transform._stream = webStream;
1937
+ transform._webStream = webStream;
1827
1938
  // Replace internal streams with the ones from the web stream
1828
1939
  const newReadable = Readable.fromWeb(webStream.readable, { objectMode: options?.objectMode });
1829
1940
  const newWritable = Writable.fromWeb(webStream.writable, { objectMode: options?.objectMode });
1830
- // Remove old event listeners before replacing
1831
- transform._readable.removeAllListeners();
1832
- transform._writable.removeAllListeners();
1941
+ if (transform._sideForwardingCleanup) {
1942
+ transform._sideForwardingCleanup();
1943
+ transform._sideForwardingCleanup = null;
1944
+ }
1833
1945
  transform._readable = newReadable;
1834
1946
  transform._writable = newWritable;
1835
- // Re-connect event forwarding
1836
- newReadable.on("data", (data) => transform.emit("data", data));
1837
- newReadable.on("end", () => transform.emit("end"));
1838
- newReadable.on("error", (err) => transform.emit("error", err));
1839
- newWritable.on("finish", () => transform.emit("finish"));
1840
- newWritable.on("drain", () => transform.emit("drain"));
1841
- newWritable.on("error", (err) => transform.emit("error", err));
1947
+ // Re-connect event forwarding (data forwarding remains lazy via Transform.on)
1948
+ transform._setupSideForwarding();
1842
1949
  return transform;
1843
1950
  }
1844
1951
  /**
@@ -1894,7 +2001,13 @@ export class Duplex extends EventEmitter {
1894
2001
  callback();
1895
2002
  }
1896
2003
  });
1897
- readable.on("error", err => duplex.emit("error", err));
2004
+ const onError = (err) => {
2005
+ duplex.emit("error", err);
2006
+ };
2007
+ const cleanupError = addEmitterListener(readable, "error", onError);
2008
+ addEmitterListener(readable, "end", cleanupError, { once: true });
2009
+ addEmitterListener(readable, "close", cleanupError, { once: true });
2010
+ addEmitterListener(sink, "finish", cleanupError, { once: true });
1898
2011
  readable.pipe(sink);
1899
2012
  };
1900
2013
  // If it has readable and/or writable properties
@@ -1902,21 +2015,25 @@ export class Duplex extends EventEmitter {
1902
2015
  source !== null &&
1903
2016
  "readable" in source &&
1904
2017
  "writable" in source) {
1905
- const duplex = new Duplex();
1906
2018
  const pair = source;
1907
- if (pair.readable) {
1908
- forwardReadableToDuplex(pair.readable, duplex);
1909
- }
1910
- if (pair.writable) {
1911
- return new Duplex({
1912
- objectMode: duplex.writableObjectMode,
1913
- write(chunk, encoding, callback) {
2019
+ // Create one duplex that can bridge both sides.
2020
+ // (Previous behavior returned a new writable-only Duplex and dropped the readable side.)
2021
+ const duplex = new Duplex({
2022
+ readableObjectMode: pair.readable?.readableObjectMode,
2023
+ writableObjectMode: pair.writable?.writableObjectMode,
2024
+ write: pair.writable
2025
+ ? (chunk, encoding, callback) => {
1914
2026
  pair.writable.write(chunk, encoding, callback);
1915
- },
1916
- final(callback) {
2027
+ }
2028
+ : undefined,
2029
+ final: pair.writable
2030
+ ? callback => {
1917
2031
  pair.writable.end(callback);
1918
2032
  }
1919
- });
2033
+ : undefined
2034
+ });
2035
+ if (pair.readable) {
2036
+ forwardReadableToDuplex(pair.readable, duplex);
1920
2037
  }
1921
2038
  return duplex;
1922
2039
  }
@@ -1954,9 +2071,22 @@ export class Duplex extends EventEmitter {
1954
2071
  */
1955
2072
  static fromWeb(pair, options) {
1956
2073
  const duplex = new Duplex(options);
1957
- // Replace internal streams
1958
- duplex._readable = new Readable({ stream: pair.readable });
1959
- duplex._writable = new Writable({ stream: pair.writable });
2074
+ const newReadable = new Readable({
2075
+ stream: pair.readable,
2076
+ objectMode: duplex.readableObjectMode
2077
+ });
2078
+ const newWritable = new Writable({
2079
+ stream: pair.writable,
2080
+ objectMode: duplex.writableObjectMode
2081
+ });
2082
+ if (duplex._sideForwardingCleanup) {
2083
+ duplex._sideForwardingCleanup();
2084
+ duplex._sideForwardingCleanup = null;
2085
+ }
2086
+ duplex._readable = newReadable;
2087
+ duplex._writable = newWritable;
2088
+ // Re-wire event forwarding (data forwarding remains lazy via Duplex.on)
2089
+ duplex._setupSideForwarding();
1960
2090
  return duplex;
1961
2091
  }
1962
2092
  /**
@@ -1972,6 +2102,7 @@ export class Duplex extends EventEmitter {
1972
2102
  super();
1973
2103
  // Track if we've already set up data forwarding
1974
2104
  this._dataForwardingSetup = false;
2105
+ this._sideForwardingCleanup = null;
1975
2106
  this.allowHalfOpen = options?.allowHalfOpen ?? true;
1976
2107
  // Support shorthand objectMode option
1977
2108
  const objectMode = options?.objectMode ?? false;
@@ -1988,23 +2119,31 @@ export class Duplex extends EventEmitter {
1988
2119
  write: options?.write?.bind(this),
1989
2120
  final: options?.final?.bind(this)
1990
2121
  });
2122
+ this._setupSideForwarding();
2123
+ }
2124
+ _setupSideForwarding() {
2125
+ if (this._sideForwardingCleanup) {
2126
+ this._sideForwardingCleanup();
2127
+ this._sideForwardingCleanup = null;
2128
+ }
2129
+ const registry = createListenerRegistry();
1991
2130
  // Forward non-data events (data forwarding is lazy to avoid premature flowing)
1992
- this._readable.on("end", () => {
2131
+ registry.once(this._readable, "end", () => {
1993
2132
  this.emit("end");
1994
- // If not allowHalfOpen, end the writable side too
1995
2133
  if (!this.allowHalfOpen) {
1996
2134
  this._writable.end();
1997
2135
  }
1998
2136
  });
1999
- this._readable.on("error", err => this.emit("error", err));
2000
- this._writable.on("finish", () => this.emit("finish"));
2001
- this._writable.on("drain", () => this.emit("drain"));
2002
- this._writable.on("close", () => {
2003
- // If not allowHalfOpen, destroy the readable side too
2137
+ registry.add(this._readable, "error", err => this.emit("error", err));
2138
+ registry.add(this._writable, "error", err => this.emit("error", err));
2139
+ registry.once(this._writable, "finish", () => this.emit("finish"));
2140
+ registry.add(this._writable, "drain", () => this.emit("drain"));
2141
+ registry.once(this._writable, "close", () => {
2004
2142
  if (!this.allowHalfOpen && !this._readable.destroyed) {
2005
2143
  this._readable.destroy();
2006
2144
  }
2007
2145
  });
2146
+ this._sideForwardingCleanup = () => registry.cleanup();
2008
2147
  }
2009
2148
  /**
2010
2149
  * Override on() to set up data forwarding lazily
@@ -2123,6 +2262,10 @@ export class Duplex extends EventEmitter {
2123
2262
  * Destroy both sides
2124
2263
  */
2125
2264
  destroy(error) {
2265
+ if (this._sideForwardingCleanup) {
2266
+ this._sideForwardingCleanup();
2267
+ this._sideForwardingCleanup = null;
2268
+ }
2126
2269
  this._readable.destroy(error);
2127
2270
  this._writable.destroy(error);
2128
2271
  return this;
@@ -2263,21 +2406,8 @@ export class Collector extends Writable {
2263
2406
  // =============================================================================
2264
2407
  // PullStream / BufferedStream / DataChunk helpers
2265
2408
  // =============================================================================
2266
- export class PullStream extends StandalonePullStream {
2267
- // Keep constructor signature aligned with streams.browser.ts public API
2268
- constructor(options) {
2269
- super(options);
2270
- }
2271
- }
2272
- export class StringChunk extends StandaloneStringChunk {
2273
- }
2274
- export class BufferChunk extends StandaloneBufferChunk {
2275
- }
2276
- export class BufferedStream extends StandaloneBufferedStream {
2277
- constructor(options) {
2278
- super(options);
2279
- }
2280
- }
2409
+ // Standalone cross-platform helpers
2410
+ export { PullStream, BufferedStream, StringChunk, BufferChunk };
2281
2411
  // =============================================================================
2282
2412
  // Stream Creation Functions
2283
2413
  // =============================================================================
@@ -2285,36 +2415,16 @@ export class BufferedStream extends StandaloneBufferedStream {
2285
2415
  * Create a readable stream with custom read implementation
2286
2416
  */
2287
2417
  export function createReadable(options) {
2288
- const readable = new Readable(options);
2289
- // Override read behavior if provided
2290
- if (options?.read) {
2291
- const originalRead = readable.read.bind(readable);
2292
- readable.read = function (size) {
2293
- options.read(size ?? 16384);
2294
- return originalRead(size);
2295
- };
2296
- }
2297
- return readable;
2418
+ // Readable already supports Node-style `read()` via the constructor option.
2419
+ // Keep this helper minimal to avoid accidental double-read behavior.
2420
+ return new Readable(options);
2298
2421
  }
2299
2422
  /**
2300
2423
  * Create a readable stream from an async iterable
2301
2424
  */
2302
2425
  export function createReadableFromAsyncIterable(iterable, options) {
2303
2426
  const readable = new Readable({ ...options, objectMode: options?.objectMode ?? true });
2304
- (async () => {
2305
- try {
2306
- for await (const chunk of iterable) {
2307
- if (!readable.push(chunk)) {
2308
- // Backpressure - wait a bit
2309
- await new Promise(resolve => setTimeout(resolve, 0));
2310
- }
2311
- }
2312
- readable.push(null);
2313
- }
2314
- catch (err) {
2315
- readable.destroy(err);
2316
- }
2317
- })();
2427
+ pumpAsyncIterableToReadable(readable, iterable);
2318
2428
  return readable;
2319
2429
  }
2320
2430
  /**
@@ -2343,38 +2453,8 @@ export function createReadableFromArray(data, options) {
2343
2453
  * Create a writable stream with custom write implementation
2344
2454
  */
2345
2455
  export function createWritable(options) {
2346
- // Create a custom WritableStream with user's handlers
2347
- const stream = new WritableStream({
2348
- write: async (chunk) => {
2349
- if (options?.write) {
2350
- return new Promise((resolve, reject) => {
2351
- options.write(chunk, "utf8", err => {
2352
- if (err) {
2353
- reject(err);
2354
- }
2355
- else {
2356
- resolve();
2357
- }
2358
- });
2359
- });
2360
- }
2361
- },
2362
- close: async () => {
2363
- if (options?.final) {
2364
- return new Promise((resolve, reject) => {
2365
- options.final(err => {
2366
- if (err) {
2367
- reject(err);
2368
- }
2369
- else {
2370
- resolve();
2371
- }
2372
- });
2373
- });
2374
- }
2375
- }
2376
- });
2377
- return new Writable({ ...options, stream });
2456
+ // Writable already supports Node-style `write()` / `final()` via the constructor.
2457
+ return new Writable(options);
2378
2458
  }
2379
2459
  /**
2380
2460
  * Create a transform stream from a transform function
@@ -2410,20 +2490,6 @@ export function createPullStream(options) {
2410
2490
  export function createBufferedStream(options) {
2411
2491
  return new BufferedStream(options);
2412
2492
  }
2413
- const isReadableStream = (value) => !!value && typeof value === "object" && typeof value.getReader === "function";
2414
- const isAsyncIterable = (value) => {
2415
- if (!value || (typeof value !== "object" && typeof value !== "function")) {
2416
- return false;
2417
- }
2418
- return typeof value[Symbol.asyncIterator] === "function";
2419
- };
2420
- const isWritableStream = (value) => !!value && typeof value === "object" && typeof value.getWriter === "function";
2421
- const isTransformStream = (value) => !!value &&
2422
- typeof value === "object" &&
2423
- !!value.readable &&
2424
- !!value.writable &&
2425
- isReadableStream(value.readable) &&
2426
- isWritableStream(value.writable);
2427
2493
  const isPipelineOptions = (value) => {
2428
2494
  if (!value || typeof value !== "object") {
2429
2495
  return false;
@@ -2509,11 +2575,17 @@ export function pipeline(...args) {
2509
2575
  const transforms = normalized.slice(1, -1);
2510
2576
  let completed = false;
2511
2577
  const allStreams = [source, ...transforms, destination];
2512
- const cleanup = (error) => {
2578
+ const registry = createListenerRegistry();
2579
+ let onAbort;
2580
+ const cleanupWithSignal = (error) => {
2513
2581
  if (completed) {
2514
2582
  return;
2515
2583
  }
2516
2584
  completed = true;
2585
+ registry.cleanup();
2586
+ if (onAbort && options.signal) {
2587
+ options.signal.removeEventListener("abort", onAbort);
2588
+ }
2517
2589
  // Destroy all streams on error
2518
2590
  if (error) {
2519
2591
  for (const stream of allStreams) {
@@ -2530,12 +2602,11 @@ export function pipeline(...args) {
2530
2602
  // Handle abort signal
2531
2603
  if (options.signal) {
2532
2604
  if (options.signal.aborted) {
2533
- cleanup(new Error("Pipeline aborted"));
2605
+ cleanupWithSignal(new Error("Pipeline aborted"));
2534
2606
  return;
2535
2607
  }
2536
- options.signal.addEventListener("abort", () => {
2537
- cleanup(new Error("Pipeline aborted"));
2538
- });
2608
+ onAbort = () => cleanupWithSignal(new Error("Pipeline aborted"));
2609
+ options.signal.addEventListener("abort", onAbort);
2539
2610
  }
2540
2611
  // Chain the streams
2541
2612
  let current = source;
@@ -2549,13 +2620,35 @@ export function pipeline(...args) {
2549
2620
  }
2550
2621
  else {
2551
2622
  // Don't end destination
2552
- current.on("data", chunk => destination.write(chunk));
2623
+ let paused = false;
2624
+ let waitingForDrain = false;
2625
+ const onDrain = () => {
2626
+ waitingForDrain = false;
2627
+ if (paused && typeof current.resume === "function") {
2628
+ paused = false;
2629
+ current.resume();
2630
+ }
2631
+ };
2632
+ const onData = (chunk) => {
2633
+ const ok = destination.write(chunk);
2634
+ if (!ok && !waitingForDrain) {
2635
+ waitingForDrain = true;
2636
+ if (!paused && typeof current.pause === "function") {
2637
+ paused = true;
2638
+ current.pause();
2639
+ }
2640
+ registry.once(destination, "drain", onDrain);
2641
+ }
2642
+ };
2643
+ const onEnd = () => cleanupWithSignal();
2644
+ registry.add(current, "data", onData);
2645
+ registry.once(current, "end", onEnd);
2553
2646
  }
2554
2647
  // Handle completion
2555
- destination.on("finish", () => cleanup());
2648
+ registry.once(destination, "finish", () => cleanupWithSignal());
2556
2649
  // Handle errors on all streams
2557
2650
  for (const stream of allStreams) {
2558
- stream.on("error", (err) => cleanup(err));
2651
+ registry.once(stream, "error", (err) => cleanupWithSignal(err));
2559
2652
  }
2560
2653
  });
2561
2654
  // If callback provided, use it
@@ -2595,11 +2688,20 @@ export function finished(stream, optionsOrCallback, callback) {
2595
2688
  const promise = new Promise((resolve, reject) => {
2596
2689
  const normalizedStream = toBrowserPipelineStream(stream);
2597
2690
  let resolved = false;
2691
+ const registry = createListenerRegistry();
2692
+ let onAbort;
2693
+ const cleanup = () => {
2694
+ registry.cleanup();
2695
+ if (onAbort && options.signal) {
2696
+ options.signal.removeEventListener("abort", onAbort);
2697
+ }
2698
+ };
2598
2699
  const done = (err) => {
2599
2700
  if (resolved) {
2600
2701
  return;
2601
2702
  }
2602
2703
  resolved = true;
2704
+ cleanup();
2603
2705
  if (err && !options.error) {
2604
2706
  reject(err);
2605
2707
  }
@@ -2613,9 +2715,8 @@ export function finished(stream, optionsOrCallback, callback) {
2613
2715
  done(new Error("Aborted"));
2614
2716
  return;
2615
2717
  }
2616
- options.signal.addEventListener("abort", () => {
2617
- done(new Error("Aborted"));
2618
- });
2718
+ onAbort = () => done(new Error("Aborted"));
2719
+ options.signal.addEventListener("abort", onAbort);
2619
2720
  }
2620
2721
  const checkReadable = options.readable !== false;
2621
2722
  const checkWritable = options.writable !== false;
@@ -2630,13 +2731,13 @@ export function finished(stream, optionsOrCallback, callback) {
2630
2731
  }
2631
2732
  // Listen for events
2632
2733
  if (checkWritable) {
2633
- normalizedStream.on("finish", () => done());
2734
+ registry.once(normalizedStream, "finish", () => done());
2634
2735
  }
2635
2736
  if (checkReadable) {
2636
- normalizedStream.on("end", () => done());
2737
+ registry.once(normalizedStream, "end", () => done());
2637
2738
  }
2638
- normalizedStream.on("error", (err) => done(err));
2639
- normalizedStream.on("close", () => done());
2739
+ registry.once(normalizedStream, "error", (err) => done(err));
2740
+ registry.once(normalizedStream, "close", () => done());
2640
2741
  });
2641
2742
  // If callback provided, use it
2642
2743
  if (cb) {
@@ -2658,38 +2759,8 @@ export async function streamToPromise(stream) {
2658
2759
  * (Browser equivalent of Node.js streamToBuffer)
2659
2760
  */
2660
2761
  export async function streamToUint8Array(stream) {
2661
- let iterable;
2662
- if (isReadableStream(stream)) {
2663
- iterable = Readable.fromWeb(stream);
2664
- }
2665
- else if (isAsyncIterable(stream)) {
2666
- iterable = stream;
2667
- }
2668
- else {
2669
- throw new Error("streamToUint8Array: unsupported stream type");
2670
- }
2671
- const chunks = [];
2672
- let totalLength = 0;
2673
- for await (const chunk of iterable) {
2674
- chunks.push(chunk);
2675
- totalLength += chunk.length;
2676
- }
2677
- // Fast paths
2678
- const len = chunks.length;
2679
- if (len === 0) {
2680
- return new Uint8Array(0);
2681
- }
2682
- if (len === 1) {
2683
- return chunks[0];
2684
- }
2685
- // Use precalculated total length
2686
- const result = new Uint8Array(totalLength);
2687
- let offset = 0;
2688
- for (let i = 0; i < len; i++) {
2689
- result.set(chunks[i], offset);
2690
- offset += chunks[i].length;
2691
- }
2692
- return result;
2762
+ const [chunks, totalLength] = await collectStreamChunks(stream);
2763
+ return concatUint8Arrays(chunks, totalLength);
2693
2764
  }
2694
2765
  /**
2695
2766
  * Alias for streamToUint8Array (Node.js compatibility)
@@ -2699,23 +2770,16 @@ export const streamToBuffer = streamToUint8Array;
2699
2770
  * Collect all data from a readable stream into a string
2700
2771
  */
2701
2772
  export async function streamToString(stream, encoding) {
2702
- const buffer = await streamToUint8Array(stream);
2703
- return getTextDecoder(encoding).decode(buffer);
2773
+ const [chunks, totalLength] = await collectStreamChunks(stream);
2774
+ const combined = concatUint8Arrays(chunks, totalLength);
2775
+ const decoder = encoding ? getTextDecoder(encoding) : textDecoder;
2776
+ return decoder.decode(combined);
2704
2777
  }
2705
2778
  /**
2706
2779
  * Drain a stream (consume all data without processing)
2707
2780
  */
2708
2781
  export async function drainStream(stream) {
2709
- let iterable;
2710
- if (isReadableStream(stream)) {
2711
- iterable = Readable.fromWeb(stream);
2712
- }
2713
- else if (isAsyncIterable(stream)) {
2714
- iterable = stream;
2715
- }
2716
- else {
2717
- throw new Error("drainStream: unsupported stream type");
2718
- }
2782
+ const iterable = toReadableAsyncIterable(stream, "drainStream");
2719
2783
  for await (const _chunk of iterable) {
2720
2784
  // Consume data
2721
2785
  }
@@ -2788,14 +2852,19 @@ export function addAbortSignal(signal, stream) {
2788
2852
  stream.destroy(new Error("Aborted"));
2789
2853
  return stream;
2790
2854
  }
2855
+ const cleanup = () => {
2856
+ signal.removeEventListener("abort", onAbort);
2857
+ removeEmitterListener(stream, "close", onClose);
2858
+ };
2791
2859
  const onAbort = () => {
2860
+ cleanup();
2792
2861
  stream.destroy(new Error("Aborted"));
2793
2862
  };
2794
- signal.addEventListener("abort", onAbort, { once: true });
2795
- // Clean up when stream is destroyed
2796
- stream.on("close", () => {
2797
- signal.removeEventListener("abort", onAbort);
2798
- });
2863
+ const onClose = () => {
2864
+ cleanup();
2865
+ };
2866
+ signal.addEventListener("abort", onAbort);
2867
+ addEmitterListener(stream, "close", onClose, { once: true });
2799
2868
  return stream;
2800
2869
  }
2801
2870
  /**
@@ -2804,60 +2873,68 @@ export function addAbortSignal(signal, stream) {
2804
2873
  export function createDuplex(options) {
2805
2874
  const readableObjectMode = options?.readableObjectMode ?? options?.objectMode;
2806
2875
  const writableObjectMode = options?.writableObjectMode ?? options?.objectMode;
2876
+ const underlyingWritable = options?.writable;
2807
2877
  const duplex = new Duplex({
2808
2878
  allowHalfOpen: options?.allowHalfOpen,
2809
2879
  readableHighWaterMark: options?.readableHighWaterMark,
2810
2880
  writableHighWaterMark: options?.writableHighWaterMark,
2811
2881
  readableObjectMode,
2812
- writableObjectMode
2882
+ writableObjectMode,
2883
+ read: options?.read,
2884
+ write: options?.write ??
2885
+ (underlyingWritable
2886
+ ? (chunk, encoding, callback) => {
2887
+ if (typeof underlyingWritable.write === "function") {
2888
+ underlyingWritable.write(chunk, encoding, callback);
2889
+ return;
2890
+ }
2891
+ // Best-effort sync sink
2892
+ try {
2893
+ underlyingWritable.write?.(chunk);
2894
+ callback(null);
2895
+ }
2896
+ catch (err) {
2897
+ callback(err);
2898
+ }
2899
+ }
2900
+ : undefined),
2901
+ final: options?.final ??
2902
+ (underlyingWritable
2903
+ ? (callback) => {
2904
+ if (typeof underlyingWritable.end === "function") {
2905
+ underlyingWritable.end((err) => callback(err ?? null));
2906
+ }
2907
+ else {
2908
+ underlyingWritable.end?.();
2909
+ callback(null);
2910
+ }
2911
+ }
2912
+ : undefined)
2813
2913
  });
2814
- // If custom readable/writable provided, pipe them
2914
+ // If an underlying readable is provided, forward it into the duplex readable side.
2815
2915
  if (options?.readable) {
2816
2916
  const readable = options.readable;
2817
- readable.on?.("data", (chunk) => duplex.push(chunk));
2818
- readable.on?.("end", () => duplex.push(null));
2819
- readable.on?.("error", (err) => duplex.destroy(err));
2820
- }
2821
- if (options?.writable) {
2822
- const writable = options.writable;
2823
- duplex.on("data", (chunk) => writable.write?.(chunk));
2824
- duplex.on("finish", () => writable.end?.());
2825
- }
2826
- // If custom read/write/final provided, override methods
2827
- if (options?.write) {
2828
- const _originalWrite = duplex.write.bind(duplex); // Keep bound reference for potential future use
2829
- duplex.write = function (chunk, encodingOrCallback, callback) {
2830
- const encoding = typeof encodingOrCallback === "string" ? encodingOrCallback : "utf8";
2831
- const cb = typeof encodingOrCallback === "function" ? encodingOrCallback : (callback ?? (() => { }));
2832
- options.write.call(duplex, chunk, encoding, cb);
2833
- return true;
2834
- };
2835
- }
2836
- if (options?.final) {
2837
- const originalEnd = duplex.end.bind(duplex);
2838
- duplex.end = function (chunkOrCallback, encodingOrCallback, callback) {
2839
- const cb = typeof chunkOrCallback === "function"
2840
- ? chunkOrCallback
2841
- : typeof encodingOrCallback === "function"
2842
- ? encodingOrCallback
2843
- : (callback ?? (() => { }));
2844
- if (chunkOrCallback !== undefined && typeof chunkOrCallback !== "function") {
2845
- duplex.write(chunkOrCallback);
2846
- }
2847
- // Call custom final handler
2848
- options.final.call(duplex, (err) => {
2849
- if (err) {
2850
- duplex.emit("error", err);
2851
- }
2852
- else {
2853
- duplex.emit("finish");
2854
- }
2855
- // Call original end to properly close writable side
2856
- originalEnd();
2857
- cb();
2858
- });
2859
- return duplex;
2860
- };
2917
+ const sink = new Writable({
2918
+ objectMode: duplex.readableObjectMode,
2919
+ write(chunk, _encoding, callback) {
2920
+ duplex.push(chunk);
2921
+ callback(null);
2922
+ },
2923
+ final(callback) {
2924
+ duplex.push(null);
2925
+ callback(null);
2926
+ }
2927
+ });
2928
+ if (typeof readable?.on === "function") {
2929
+ const onError = (err) => {
2930
+ duplex.destroy(err);
2931
+ };
2932
+ const cleanupError = addEmitterListener(readable, "error", onError);
2933
+ addEmitterListener(readable, "end", cleanupError, { once: true });
2934
+ addEmitterListener(readable, "close", cleanupError, { once: true });
2935
+ addEmitterListener(sink, "finish", cleanupError, { once: true });
2936
+ }
2937
+ readable.pipe?.(sink);
2861
2938
  }
2862
2939
  if (options?.destroy) {
2863
2940
  const originalDestroy = duplex.destroy.bind(duplex);
@@ -2881,20 +2958,7 @@ export function createDuplex(options) {
2881
2958
  */
2882
2959
  export function createReadableFromGenerator(generator, options) {
2883
2960
  const readable = new Readable({ ...options, objectMode: options?.objectMode ?? true });
2884
- (async () => {
2885
- try {
2886
- for await (const chunk of generator()) {
2887
- if (!readable.push(chunk)) {
2888
- // Backpressure
2889
- await new Promise(resolve => setTimeout(resolve, 0));
2890
- }
2891
- }
2892
- readable.push(null);
2893
- }
2894
- catch (err) {
2895
- readable.destroy(err);
2896
- }
2897
- })();
2961
+ pumpAsyncIterableToReadable(readable, generator());
2898
2962
  return readable;
2899
2963
  }
2900
2964
  /**
@@ -2924,8 +2988,8 @@ export function compose(...transforms) {
2924
2988
  transform: chunk => chunk
2925
2989
  });
2926
2990
  }
2927
- const isNativeTransform = (stream) => stream instanceof Transform;
2928
- if (len === 1 && isNativeTransform(transforms[0])) {
2991
+ // Preserve identity: compose(single) returns the same transform.
2992
+ if (len === 1) {
2929
2993
  return transforms[0];
2930
2994
  }
2931
2995
  // Chain the transforms: first → second → ... → last
@@ -2935,74 +2999,118 @@ export function compose(...transforms) {
2935
2999
  for (let i = 0; i < len - 1; i++) {
2936
3000
  transforms[i].pipe(transforms[i + 1]);
2937
3001
  }
2938
- class ComposedTransform extends Transform {
2939
- constructor() {
2940
- super(...arguments);
2941
- this._dataForwarding = false;
2942
- this._endForwarding = false;
3002
+ // A lightweight Transform wrapper that delegates:
3003
+ // - writable side to `first`
3004
+ // - readable side to `last`
3005
+ // It forwards relevant events lazily to avoid per-chunk overhead when unused.
3006
+ const composed = new Transform({
3007
+ objectMode: first?.objectMode ?? true,
3008
+ transform: chunk => chunk
3009
+ });
3010
+ const registry = createListenerRegistry();
3011
+ // Always forward errors; they are critical for pipeline semantics.
3012
+ for (const t of transforms) {
3013
+ registry.add(t, "error", (err) => composed.emit("error", err));
3014
+ }
3015
+ // Forward writable-side backpressure/completion events from `first`.
3016
+ registry.add(first, "drain", () => composed.emit("drain"));
3017
+ registry.once(first, "finish", () => composed.emit("finish"));
3018
+ // Forward readable-side events from `last` lazily.
3019
+ let forwardData = false;
3020
+ let forwardEnd = false;
3021
+ let forwardReadable = false;
3022
+ const ensureDataForwarding = () => {
3023
+ if (forwardData) {
3024
+ return;
2943
3025
  }
2944
- on(event, listener) {
2945
- if (event === "data" && !this._dataForwarding) {
2946
- this._dataForwarding = true;
2947
- last.on("data", (chunk) => this.emit("data", chunk));
2948
- }
2949
- if (event === "end" && !this._endForwarding) {
2950
- this._endForwarding = true;
2951
- last.on("end", () => this.emit("end"));
2952
- }
2953
- return super.on(event, listener);
3026
+ forwardData = true;
3027
+ registry.add(last, "data", (chunk) => composed.emit("data", chunk));
3028
+ };
3029
+ const ensureEndForwarding = () => {
3030
+ if (forwardEnd) {
3031
+ return;
2954
3032
  }
2955
- write(chunk, encodingOrCallback, callback) {
2956
- if (typeof encodingOrCallback === "function") {
2957
- return first.write(chunk, encodingOrCallback);
2958
- }
2959
- return first.write(chunk, encodingOrCallback, callback);
3033
+ forwardEnd = true;
3034
+ registry.once(last, "end", () => composed.emit("end"));
3035
+ };
3036
+ const ensureReadableForwarding = () => {
3037
+ if (forwardReadable) {
3038
+ return;
2960
3039
  }
2961
- end(chunkOrCallback, encodingOrCallback, callback) {
2962
- if (typeof chunkOrCallback === "function") {
2963
- first.end(chunkOrCallback);
2964
- return this;
2965
- }
2966
- if (typeof encodingOrCallback === "function") {
2967
- first.end(chunkOrCallback, encodingOrCallback);
2968
- return this;
2969
- }
2970
- first.end(chunkOrCallback, encodingOrCallback, callback);
2971
- return this;
3040
+ forwardReadable = true;
3041
+ registry.add(last, "readable", () => composed.emit("readable"));
3042
+ };
3043
+ const originalOn = composed.on.bind(composed);
3044
+ const originalOnce = composed.once.bind(composed);
3045
+ composed.on = (event, listener) => {
3046
+ if (event === "data") {
3047
+ ensureDataForwarding();
2972
3048
  }
2973
- pipe(destination) {
2974
- return last.pipe(destination);
3049
+ else if (event === "end") {
3050
+ ensureEndForwarding();
2975
3051
  }
2976
- destroy(error) {
2977
- for (const t of transforms) {
2978
- t.destroy(error);
2979
- }
2980
- super.destroy(error);
3052
+ else if (event === "readable") {
3053
+ ensureReadableForwarding();
2981
3054
  }
2982
- read(size) {
2983
- return typeof last.read === "function" ? last.read(size) : null;
3055
+ return originalOn(event, listener);
3056
+ };
3057
+ composed.once = (event, listener) => {
3058
+ if (event === "data") {
3059
+ ensureDataForwarding();
2984
3060
  }
2985
- async *[Symbol.asyncIterator]() {
2986
- const it = last?.[Symbol.asyncIterator]?.();
2987
- if (it) {
2988
- for await (const chunk of it) {
2989
- yield chunk;
2990
- }
2991
- return;
3061
+ else if (event === "end") {
3062
+ ensureEndForwarding();
3063
+ }
3064
+ else if (event === "readable") {
3065
+ ensureReadableForwarding();
3066
+ }
3067
+ return originalOnce(event, listener);
3068
+ };
3069
+ // Delegate core stream methods
3070
+ const firstAny = first;
3071
+ const lastAny = last;
3072
+ composed.write = (chunk, encodingOrCallback, callback) => {
3073
+ if (typeof encodingOrCallback === "function") {
3074
+ return firstAny.write(chunk, encodingOrCallback);
3075
+ }
3076
+ return firstAny.write(chunk, encodingOrCallback, callback);
3077
+ };
3078
+ composed.end = (chunkOrCallback, encodingOrCallback, callback) => {
3079
+ if (typeof chunkOrCallback === "function") {
3080
+ firstAny.end(chunkOrCallback);
3081
+ return composed;
3082
+ }
3083
+ if (typeof encodingOrCallback === "function") {
3084
+ firstAny.end(chunkOrCallback, encodingOrCallback);
3085
+ return composed;
3086
+ }
3087
+ firstAny.end(chunkOrCallback, encodingOrCallback, callback);
3088
+ return composed;
3089
+ };
3090
+ composed.pipe = (destination) => {
3091
+ return lastAny.pipe(destination);
3092
+ };
3093
+ composed.read = (size) => {
3094
+ return typeof lastAny.read === "function" ? lastAny.read(size) : null;
3095
+ };
3096
+ composed[Symbol.asyncIterator] = async function* () {
3097
+ const it = lastAny?.[Symbol.asyncIterator]?.();
3098
+ if (it) {
3099
+ for await (const chunk of it) {
3100
+ yield chunk;
2992
3101
  }
2993
- yield* super[Symbol.asyncIterator]();
3102
+ return;
2994
3103
  }
2995
- }
2996
- const composed = new ComposedTransform({
2997
- objectMode: first?.objectMode ?? true,
2998
- transform: chunk => chunk
3104
+ yield* Transform.prototype[Symbol.asyncIterator].call(composed);
3105
+ };
3106
+ const originalDestroy = composed.destroy.bind(composed);
3107
+ composed.destroy = ((error) => {
3108
+ registry.cleanup();
3109
+ for (const t of transforms) {
3110
+ t.destroy(error);
3111
+ }
3112
+ originalDestroy(error);
2999
3113
  });
3000
- // Forward errors from any transform
3001
- for (const t of transforms) {
3002
- t.on("error", (err) => {
3003
- composed.emit("error", err);
3004
- });
3005
- }
3006
3114
  // Reflect underlying readability/writability like the previous duck-typed wrapper
3007
3115
  Object.defineProperty(composed, "readable", {
3008
3116
  get: () => last.readable
@@ -3248,77 +3356,54 @@ export function duplexPair(options) {
3248
3356
  async function collectStreamChunks(stream) {
3249
3357
  const chunks = [];
3250
3358
  let totalLength = 0;
3251
- let iterable;
3252
- if (isReadableStream(stream)) {
3253
- iterable = Readable.fromWeb(stream);
3254
- }
3255
- else if (isAsyncIterable(stream)) {
3256
- iterable = stream;
3257
- }
3258
- else {
3259
- throw new Error("collectStreamChunks: unsupported stream type");
3260
- }
3359
+ const iterable = toReadableAsyncIterable(stream, "collectStreamChunks");
3261
3360
  for await (const chunk of iterable) {
3262
3361
  chunks.push(chunk);
3263
3362
  totalLength += chunk.length;
3264
3363
  }
3265
- return { chunks, totalLength };
3364
+ return [chunks, totalLength];
3266
3365
  }
3267
- // Helper to concatenate with known length (faster)
3268
- function concatWithLength(chunks, totalLength) {
3269
- const len = chunks.length;
3270
- if (len === 0) {
3271
- return new Uint8Array(0);
3272
- }
3273
- if (len === 1) {
3274
- return chunks[0];
3366
+ function toReadableAsyncIterable(stream, name) {
3367
+ if (isReadableStream(stream)) {
3368
+ return Readable.fromWeb(stream);
3275
3369
  }
3276
- const result = new Uint8Array(totalLength);
3277
- let offset = 0;
3278
- for (let i = 0; i < len; i++) {
3279
- result.set(chunks[i], offset);
3280
- offset += chunks[i].length;
3370
+ if (isAsyncIterable(stream)) {
3371
+ return stream;
3281
3372
  }
3282
- return result;
3373
+ throw new Error(`${name}: unsupported stream type`);
3283
3374
  }
3284
3375
  export const consumers = {
3285
3376
  /**
3286
3377
  * Consume entire stream as ArrayBuffer
3287
3378
  */
3288
3379
  async arrayBuffer(stream) {
3289
- const { chunks, totalLength } = await collectStreamChunks(stream);
3290
- const combined = concatWithLength(chunks, totalLength);
3291
- return combined.buffer.slice(combined.byteOffset, combined.byteOffset + combined.byteLength);
3380
+ const bytes = await streamToUint8Array(stream);
3381
+ return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
3292
3382
  },
3293
3383
  /**
3294
3384
  * Consume entire stream as Blob
3295
3385
  */
3296
3386
  async blob(stream, options) {
3297
- const { chunks } = await collectStreamChunks(stream);
3387
+ const [chunks] = await collectStreamChunks(stream);
3298
3388
  return new Blob(chunks, options);
3299
3389
  },
3300
3390
  /**
3301
3391
  * Consume entire stream as Buffer (Uint8Array in browser)
3302
3392
  */
3303
3393
  async buffer(stream) {
3304
- const { chunks, totalLength } = await collectStreamChunks(stream);
3305
- return concatWithLength(chunks, totalLength);
3394
+ return streamToUint8Array(stream);
3306
3395
  },
3307
3396
  /**
3308
3397
  * Consume entire stream as JSON
3309
3398
  */
3310
3399
  async json(stream) {
3311
- const text = await consumers.text(stream);
3312
- return JSON.parse(text);
3400
+ return JSON.parse(await streamToString(stream));
3313
3401
  },
3314
3402
  /**
3315
3403
  * Consume entire stream as text
3316
3404
  */
3317
3405
  async text(stream, encoding) {
3318
- const { chunks, totalLength } = await collectStreamChunks(stream);
3319
- const combined = concatWithLength(chunks, totalLength);
3320
- const decoder = encoding ? getTextDecoder(encoding) : textDecoder;
3321
- return decoder.decode(combined);
3406
+ return streamToString(stream, encoding);
3322
3407
  }
3323
3408
  };
3324
3409
  // =============================================================================