@yume-chan/stream-extra 0.0.18 → 0.0.19

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 (87) hide show
  1. package/CHANGELOG.json +33 -21
  2. package/CHANGELOG.md +8 -2
  3. package/README.md +1 -1
  4. package/esm/buffered-transform.d.ts +11 -10
  5. package/esm/buffered-transform.d.ts.map +1 -1
  6. package/esm/buffered-transform.js +55 -55
  7. package/esm/buffered-transform.js.map +1 -1
  8. package/esm/buffered.d.ts +29 -27
  9. package/esm/buffered.d.ts.map +1 -1
  10. package/esm/buffered.js +128 -123
  11. package/esm/buffered.js.map +1 -1
  12. package/esm/chunk.d.ts +7 -4
  13. package/esm/chunk.d.ts.map +1 -1
  14. package/esm/chunk.js +48 -7
  15. package/esm/chunk.js.map +1 -1
  16. package/esm/consumable.d.ts +53 -0
  17. package/esm/consumable.d.ts.map +1 -0
  18. package/esm/consumable.js +178 -0
  19. package/esm/consumable.js.map +1 -0
  20. package/esm/decode-utf8.d.ts +4 -4
  21. package/esm/decode-utf8.js +11 -11
  22. package/esm/distribution.d.ts +24 -0
  23. package/esm/distribution.d.ts.map +1 -0
  24. package/esm/distribution.js +99 -0
  25. package/esm/distribution.js.map +1 -0
  26. package/esm/duplex.d.ts +48 -47
  27. package/esm/duplex.d.ts.map +1 -1
  28. package/esm/duplex.js +90 -92
  29. package/esm/duplex.js.map +1 -1
  30. package/esm/gather-string.d.ts +6 -6
  31. package/esm/gather-string.d.ts.map +1 -1
  32. package/esm/gather-string.js +15 -13
  33. package/esm/gather-string.js.map +1 -1
  34. package/esm/index.d.ts +16 -15
  35. package/esm/index.d.ts.map +1 -1
  36. package/esm/index.js +16 -15
  37. package/esm/index.js.map +1 -1
  38. package/esm/inspect.d.ts +4 -4
  39. package/esm/inspect.js +11 -11
  40. package/esm/pipe-from.d.ts +12 -11
  41. package/esm/pipe-from.d.ts.map +1 -1
  42. package/esm/pipe-from.js +23 -25
  43. package/esm/pipe-from.js.map +1 -1
  44. package/esm/push-readable.d.ts +19 -18
  45. package/esm/push-readable.d.ts.map +1 -1
  46. package/esm/push-readable.js +61 -61
  47. package/esm/push-readable.js.map +1 -1
  48. package/esm/split-string.d.ts +4 -4
  49. package/esm/split-string.d.ts.map +1 -1
  50. package/esm/split-string.js +24 -24
  51. package/esm/split-string.js.map +1 -1
  52. package/esm/stream.d.ts +25 -24
  53. package/esm/stream.d.ts.map +1 -1
  54. package/esm/stream.js +26 -26
  55. package/esm/stream.js.map +1 -1
  56. package/esm/struct-deserialize.d.ts +6 -6
  57. package/esm/struct-deserialize.d.ts.map +1 -1
  58. package/esm/struct-deserialize.js +8 -8
  59. package/esm/struct-serialize.d.ts +5 -5
  60. package/esm/struct-serialize.js +10 -10
  61. package/esm/trace.d.ts +3 -3
  62. package/esm/wrap-readable.d.ts +21 -20
  63. package/esm/wrap-readable.d.ts.map +1 -1
  64. package/esm/wrap-readable.js +56 -56
  65. package/esm/wrap-readable.js.map +1 -1
  66. package/esm/wrap-writable.d.ts +14 -12
  67. package/esm/wrap-writable.d.ts.map +1 -1
  68. package/esm/wrap-writable.js +62 -52
  69. package/esm/wrap-writable.js.map +1 -1
  70. package/package.json +7 -6
  71. package/src/buffered-transform.ts +5 -10
  72. package/src/buffered.ts +7 -5
  73. package/src/consumable.ts +281 -0
  74. package/src/distribution.ts +113 -0
  75. package/src/duplex.ts +15 -16
  76. package/src/gather-string.ts +5 -3
  77. package/src/index.ts +16 -15
  78. package/src/inspect.ts +1 -1
  79. package/src/pipe-from.ts +8 -6
  80. package/src/push-readable.ts +3 -7
  81. package/src/split-string.ts +5 -2
  82. package/src/stream.ts +1 -1
  83. package/src/struct-deserialize.ts +1 -1
  84. package/src/wrap-readable.ts +5 -5
  85. package/src/wrap-writable.ts +16 -4
  86. package/tsconfig.build.tsbuildinfo +1 -1
  87. package/src/chunk.ts +0 -15
@@ -1,13 +1,15 @@
1
- import { type ValueOrPromise } from "@yume-chan/struct";
2
- import { WritableStream } from "./stream.js";
3
- export type WrapWritableStreamStart<T> = () => ValueOrPromise<WritableStream<T>>;
4
- export interface WritableStreamWrapper<T> {
5
- start: WrapWritableStreamStart<T>;
6
- close?(): void | Promise<void>;
7
- }
8
- export declare class WrapWritableStream<T> extends WritableStream<T> {
9
- writable: WritableStream<T>;
10
- private writer;
11
- constructor(wrapper: WritableStream<T> | WrapWritableStreamStart<T> | WritableStreamWrapper<T>);
12
- }
1
+ import type { ValueOrPromise } from "@yume-chan/struct";
2
+ import type { TransformStream } from "./stream.js";
3
+ import { WritableStream } from "./stream.js";
4
+ export type WrapWritableStreamStart<T> = () => ValueOrPromise<WritableStream<T>>;
5
+ export interface WritableStreamWrapper<T> {
6
+ start: WrapWritableStreamStart<T>;
7
+ close?(): void | Promise<void>;
8
+ }
9
+ export declare class WrapWritableStream<T> extends WritableStream<T> {
10
+ writable: WritableStream<T>;
11
+ private writer;
12
+ constructor(wrapper: WritableStream<T> | WrapWritableStreamStart<T> | WritableStreamWrapper<T>);
13
+ bePipedThroughFrom<U>(transformer: TransformStream<U, T>): WrapWritableStream<U>;
14
+ }
13
15
  //# sourceMappingURL=wrap-writable.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"wrap-writable.d.ts","sourceRoot":"","sources":["../src/wrap-writable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,EAAE,cAAc,EAAoC,MAAM,aAAa,CAAC;AAE/E,MAAM,MAAM,uBAAuB,CAAC,CAAC,IAAI,MAAM,cAAc,CACzD,cAAc,CAAC,CAAC,CAAC,CACpB,CAAC;AAEF,MAAM,WAAW,qBAAqB,CAAC,CAAC;IACpC,KAAK,EAAE,uBAAuB,CAAC,CAAC,CAAC,CAAC;IAClC,KAAK,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAmBD,qBAAa,kBAAkB,CAAC,CAAC,CAAE,SAAQ,cAAc,CAAC,CAAC,CAAC;IACjD,QAAQ,EAAG,cAAc,CAAC,CAAC,CAAC,CAAC;IAEpC,OAAO,CAAC,MAAM,CAAkC;gBAG5C,OAAO,EACD,cAAc,CAAC,CAAC,CAAC,GACjB,uBAAuB,CAAC,CAAC,CAAC,GAC1B,qBAAqB,CAAC,CAAC,CAAC;CAoCrC"}
1
+ {"version":3,"file":"wrap-writable.d.ts","sourceRoot":"","sources":["../src/wrap-writable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,KAAK,EAAE,eAAe,EAA+B,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,MAAM,uBAAuB,CAAC,CAAC,IAAI,MAAM,cAAc,CACzD,cAAc,CAAC,CAAC,CAAC,CACpB,CAAC;AAEF,MAAM,WAAW,qBAAqB,CAAC,CAAC;IACpC,KAAK,EAAE,uBAAuB,CAAC,CAAC,CAAC,CAAC;IAClC,KAAK,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAmBD,qBAAa,kBAAkB,CAAC,CAAC,CAAE,SAAQ,cAAc,CAAC,CAAC,CAAC;IACjD,QAAQ,EAAG,cAAc,CAAC,CAAC,CAAC,CAAC;IAEpC,OAAO,CAAC,MAAM,CAAkC;gBAG5C,OAAO,EACD,cAAc,CAAC,CAAC,CAAC,GACjB,uBAAuB,CAAC,CAAC,CAAC,GAC1B,qBAAqB,CAAC,CAAC,CAAC;IAmC3B,kBAAkB,CAAC,CAAC,EAAE,WAAW,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC;CAYlE"}
@@ -1,53 +1,63 @@
1
- import { WritableStream } from "./stream.js";
2
- async function getWrappedWritableStream(wrapper) {
3
- if ("start" in wrapper) {
4
- return await wrapper.start();
5
- }
6
- else if (typeof wrapper === "function") {
7
- return await wrapper();
8
- }
9
- else {
10
- // Can't use `wrapper instanceof WritableStream`
11
- // Because we want to be compatible with any WritableStream-like objects
12
- return wrapper;
13
- }
14
- }
15
- export class WrapWritableStream extends WritableStream {
16
- writable;
17
- writer;
18
- constructor(wrapper) {
19
- super({
20
- start: async () => {
21
- // `start` is invoked before `ReadableStream`'s constructor finish,
22
- // so using `this` synchronously causes
23
- // "Must call super constructor in derived class before accessing 'this' or returning from derived constructor".
24
- // Queue a microtask to avoid this.
25
- await Promise.resolve();
26
- this.writable = await getWrappedWritableStream(wrapper);
27
- this.writer = this.writable.getWriter();
28
- },
29
- write: async (chunk) => {
30
- // Maintain back pressure
31
- await this.writer.ready;
32
- await this.writer.write(chunk);
33
- },
34
- abort: async (reason) => {
35
- await this.writer.abort(reason);
36
- if ("close" in wrapper) {
37
- await wrapper.close?.();
38
- }
39
- },
40
- close: async () => {
41
- // Close the inner stream first.
42
- // Usually the inner stream is a logical sub-stream over the outer stream,
43
- // closing the outer stream first will make the inner stream incapable of
44
- // sending data in its `close` handler.
45
- await this.writer.close();
46
- if ("close" in wrapper) {
47
- await wrapper.close?.();
48
- }
49
- },
50
- });
51
- }
52
- }
1
+ import { WritableStream } from "./stream.js";
2
+ async function getWrappedWritableStream(wrapper) {
3
+ if ("start" in wrapper) {
4
+ return await wrapper.start();
5
+ }
6
+ else if (typeof wrapper === "function") {
7
+ return await wrapper();
8
+ }
9
+ else {
10
+ // Can't use `wrapper instanceof WritableStream`
11
+ // Because we want to be compatible with any WritableStream-like objects
12
+ return wrapper;
13
+ }
14
+ }
15
+ export class WrapWritableStream extends WritableStream {
16
+ writable;
17
+ writer;
18
+ constructor(wrapper) {
19
+ super({
20
+ start: async () => {
21
+ // `start` is invoked before `ReadableStream`'s constructor finish,
22
+ // so using `this` synchronously causes
23
+ // "Must call super constructor in derived class before accessing 'this' or returning from derived constructor".
24
+ // Queue a microtask to avoid this.
25
+ await Promise.resolve();
26
+ this.writable = await getWrappedWritableStream(wrapper);
27
+ this.writer = this.writable.getWriter();
28
+ },
29
+ write: async (chunk) => {
30
+ await this.writer.write(chunk);
31
+ },
32
+ abort: async (reason) => {
33
+ await this.writer.abort(reason);
34
+ if ("close" in wrapper) {
35
+ await wrapper.close?.();
36
+ }
37
+ },
38
+ close: async () => {
39
+ // Close the inner stream first.
40
+ // Usually the inner stream is a logical sub-stream over the outer stream,
41
+ // closing the outer stream first will make the inner stream incapable of
42
+ // sending data in its `close` handler.
43
+ await this.writer.close();
44
+ if ("close" in wrapper) {
45
+ await wrapper.close?.();
46
+ }
47
+ },
48
+ });
49
+ }
50
+ bePipedThroughFrom(transformer) {
51
+ let promise;
52
+ return new WrapWritableStream({
53
+ start: () => {
54
+ promise = transformer.readable.pipeTo(this);
55
+ return transformer.writable;
56
+ },
57
+ async close() {
58
+ await promise;
59
+ },
60
+ });
61
+ }
62
+ }
53
63
  //# sourceMappingURL=wrap-writable.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"wrap-writable.js","sourceRoot":"","sources":["../src/wrap-writable.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAoC,MAAM,aAAa,CAAC;AAW/E,KAAK,UAAU,wBAAwB,CACnC,OAG8B;IAE9B,IAAI,OAAO,IAAI,OAAO,EAAE;QACpB,OAAO,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;KAChC;SAAM,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE;QACtC,OAAO,MAAM,OAAO,EAAE,CAAC;KAC1B;SAAM;QACH,gDAAgD;QAChD,wEAAwE;QACxE,OAAO,OAAO,CAAC;KAClB;AACL,CAAC;AAED,MAAM,OAAO,kBAAsB,SAAQ,cAAiB;IACjD,QAAQ,CAAqB;IAE5B,MAAM,CAAkC;IAEhD,YACI,OAG8B;QAE9B,KAAK,CAAC;YACF,KAAK,EAAE,KAAK,IAAI,EAAE;gBACd,mEAAmE;gBACnE,uCAAuC;gBACvC,gHAAgH;gBAChH,mCAAmC;gBACnC,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;gBAExB,IAAI,CAAC,QAAQ,GAAG,MAAM,wBAAwB,CAAC,OAAO,CAAC,CAAC;gBACxD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;YAC5C,CAAC;YACD,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;gBACnB,yBAAyB;gBACzB,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;gBACxB,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACnC,CAAC;YACD,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;gBACpB,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBAChC,IAAI,OAAO,IAAI,OAAO,EAAE;oBACpB,MAAM,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;iBAC3B;YACL,CAAC;YACD,KAAK,EAAE,KAAK,IAAI,EAAE;gBACd,gCAAgC;gBAChC,0EAA0E;gBAC1E,yEAAyE;gBACzE,uCAAuC;gBACvC,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;gBAC1B,IAAI,OAAO,IAAI,OAAO,EAAE;oBACpB,MAAM,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;iBAC3B;YACL,CAAC;SACJ,CAAC,CAAC;IACP,CAAC;CACJ"}
1
+ {"version":3,"file":"wrap-writable.js","sourceRoot":"","sources":["../src/wrap-writable.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAW7C,KAAK,UAAU,wBAAwB,CACnC,OAG8B;IAE9B,IAAI,OAAO,IAAI,OAAO,EAAE;QACpB,OAAO,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;KAChC;SAAM,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE;QACtC,OAAO,MAAM,OAAO,EAAE,CAAC;KAC1B;SAAM;QACH,gDAAgD;QAChD,wEAAwE;QACxE,OAAO,OAAO,CAAC;KAClB;AACL,CAAC;AAED,MAAM,OAAO,kBAAsB,SAAQ,cAAiB;IACjD,QAAQ,CAAqB;IAE5B,MAAM,CAAkC;IAEhD,YACI,OAG8B;QAE9B,KAAK,CAAC;YACF,KAAK,EAAE,KAAK,IAAI,EAAE;gBACd,mEAAmE;gBACnE,uCAAuC;gBACvC,gHAAgH;gBAChH,mCAAmC;gBACnC,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;gBAExB,IAAI,CAAC,QAAQ,GAAG,MAAM,wBAAwB,CAAC,OAAO,CAAC,CAAC;gBACxD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;YAC5C,CAAC;YACD,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;gBACnB,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACnC,CAAC;YACD,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;gBACpB,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBAChC,IAAI,OAAO,IAAI,OAAO,EAAE;oBACpB,MAAM,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;iBAC3B;YACL,CAAC;YACD,KAAK,EAAE,KAAK,IAAI,EAAE;gBACd,gCAAgC;gBAChC,0EAA0E;gBAC1E,yEAAyE;gBACzE,uCAAuC;gBACvC,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;gBAC1B,IAAI,OAAO,IAAI,OAAO,EAAE;oBACpB,MAAM,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;iBAC3B;YACL,CAAC;SACJ,CAAC,CAAC;IACP,CAAC;IAEM,kBAAkB,CAAI,WAAkC;QAC3D,IAAI,OAAsB,CAAC;QAC3B,OAAO,IAAI,kBAAkB,CAAI;YAC7B,KAAK,EAAE,GAAG,EAAE;gBACR,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBAC5C,OAAO,WAAW,CAAC,QAAQ,CAAC;YAChC,CAAC;YACD,KAAK,CAAC,KAAK;gBACP,MAAM,OAAO,CAAC;YAClB,CAAC;SACJ,CAAC,CAAC;IACP,CAAC;CACJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yume-chan/stream-extra",
3
- "version": "0.0.18",
3
+ "version": "0.0.19",
4
4
  "description": "Extensions to Web Streams API",
5
5
  "keywords": [
6
6
  "stream",
@@ -26,17 +26,18 @@
26
26
  "types": "esm/index.d.ts",
27
27
  "dependencies": {
28
28
  "@yume-chan/async": "^2.2.0",
29
- "@yume-chan/struct": "^0.0.18",
29
+ "@yume-chan/struct": "^0.0.19",
30
30
  "tslib": "^2.4.1",
31
31
  "web-streams-polyfill": "^4.0.0-beta.3"
32
32
  },
33
33
  "devDependencies": {
34
- "@jest/globals": "^29.3.1",
34
+ "@jest/globals": "^29.5.0",
35
35
  "@yume-chan/eslint-config": "^1.0.0",
36
36
  "@yume-chan/tsconfig": "^1.0.0",
37
37
  "cross-env": "^7.0.3",
38
- "eslint": "^8.31.0",
39
- "jest": "^29.3.1",
38
+ "eslint": "^8.36.0",
39
+ "jest": "^29.5.0",
40
+ "prettier": "^2.8.4",
40
41
  "ts-jest": "^29.0.4",
41
42
  "typescript": "^4.9.4"
42
43
  },
@@ -44,6 +45,6 @@
44
45
  "build": "tsc -b tsconfig.build.json",
45
46
  "build:watch": "tsc -b tsconfig.build.json",
46
47
  "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --coverage",
47
- "lint": "eslint src/**/*.ts --fix"
48
+ "lint": "eslint src/**/*.ts --fix && prettier src/**/*.ts --write --tab-width 4"
48
49
  }
49
50
  }
@@ -1,18 +1,13 @@
1
- import { type ValueOrPromise } from "@yume-chan/struct";
1
+ import type { ValueOrPromise } from "@yume-chan/struct";
2
2
 
3
3
  import {
4
4
  BufferedReadableStream,
5
5
  BufferedReadableStreamEndedError,
6
6
  } from "./buffered.js";
7
- import {
8
- PushReadableStream,
9
- type PushReadableStreamController,
10
- } from "./push-readable.js";
11
- import {
12
- ReadableStream,
13
- WritableStream,
14
- type ReadableWritablePair,
15
- } from "./stream.js";
7
+ import type { PushReadableStreamController } from "./push-readable.js";
8
+ import { PushReadableStream } from "./push-readable.js";
9
+ import type { ReadableWritablePair } from "./stream.js";
10
+ import { ReadableStream, WritableStream } from "./stream.js";
16
11
 
17
12
  // TODO: BufferedTransformStream: find better implementation
18
13
  export class BufferedTransformStream<T>
package/src/buffered.ts CHANGED
@@ -1,8 +1,5 @@
1
1
  import { PushReadableStream } from "./push-readable.js";
2
- import {
3
- type ReadableStream,
4
- type ReadableStreamDefaultReader,
5
- } from "./stream.js";
2
+ import type { ReadableStream, ReadableStreamDefaultReader } from "./stream.js";
6
3
 
7
4
  export class BufferedReadableStreamEndedError extends Error {
8
5
  public constructor() {
@@ -18,8 +15,12 @@ export class BufferedReadableStream {
18
15
  private bufferedOffset = 0;
19
16
  private bufferedLength = 0;
20
17
 
21
- protected readonly stream: ReadableStream<Uint8Array>;
18
+ private _position = 0;
19
+ public get position() {
20
+ return this._position;
21
+ }
22
22
 
23
+ protected readonly stream: ReadableStream<Uint8Array>;
23
24
  protected readonly reader: ReadableStreamDefaultReader<Uint8Array>;
24
25
 
25
26
  public constructor(stream: ReadableStream<Uint8Array>) {
@@ -32,6 +33,7 @@ export class BufferedReadableStream {
32
33
  if (done) {
33
34
  throw new BufferedReadableStreamEndedError();
34
35
  }
36
+ this._position += value.byteLength;
35
37
  return value;
36
38
  }
37
39
 
@@ -0,0 +1,281 @@
1
+ import { PromiseResolver } from "@yume-chan/async";
2
+
3
+ import type { QueuingStrategy, WritableStreamDefaultWriter } from "./stream.js";
4
+ import { ReadableStream, TransformStream, WritableStream } from "./stream.js";
5
+
6
+ interface Task {
7
+ run<T>(callback: () => T): T;
8
+ }
9
+
10
+ interface Console {
11
+ createTask(name: string): Task;
12
+ }
13
+
14
+ interface GlobalEx {
15
+ console: Console;
16
+ }
17
+
18
+ // `createTask` allows browser DevTools to track the call stack across async boundaries.
19
+ const { console } = globalThis as unknown as GlobalEx;
20
+ const createTask: Console["createTask"] =
21
+ console.createTask?.bind(console) ??
22
+ (() => ({
23
+ run(callback) {
24
+ return callback();
25
+ },
26
+ }));
27
+
28
+ export class Consumable<T> {
29
+ private readonly task: Task;
30
+ private readonly resolver: PromiseResolver<void>;
31
+
32
+ public readonly value: T;
33
+ public readonly consumed: Promise<void>;
34
+
35
+ public constructor(value: T) {
36
+ this.task = createTask("Consumable");
37
+ this.value = value;
38
+ this.resolver = new PromiseResolver<void>();
39
+ this.consumed = this.resolver.promise;
40
+ }
41
+
42
+ public consume() {
43
+ this.resolver.resolve();
44
+ }
45
+
46
+ public error(error: any) {
47
+ this.resolver.reject(error);
48
+ }
49
+
50
+ public async tryConsume<U>(callback: (value: T) => U) {
51
+ try {
52
+ // eslint-disable-next-line @typescript-eslint/await-thenable
53
+ const result = await this.task.run(() => callback(this.value));
54
+ this.consume();
55
+ return result;
56
+ } catch (e) {
57
+ this.resolver.reject(e);
58
+ throw e;
59
+ }
60
+ }
61
+ }
62
+
63
+ async function enqueue<T>(
64
+ controller: { enqueue: (chunk: Consumable<T>) => void },
65
+ chunk: T
66
+ ) {
67
+ const output = new Consumable(chunk);
68
+ controller.enqueue(output);
69
+ await output.consumed;
70
+ }
71
+
72
+ export class WrapConsumableStream<T> extends TransformStream<T, Consumable<T>> {
73
+ public constructor() {
74
+ super({
75
+ async transform(chunk, controller) {
76
+ await enqueue(controller, chunk);
77
+ },
78
+ });
79
+ }
80
+ }
81
+
82
+ export class UnwrapConsumableStream<T> extends TransformStream<
83
+ Consumable<T>,
84
+ T
85
+ > {
86
+ public constructor() {
87
+ super({
88
+ transform(chunk, controller) {
89
+ controller.enqueue(chunk.value);
90
+ chunk.consume();
91
+ },
92
+ });
93
+ }
94
+ }
95
+
96
+ export interface ConsumableReadableStreamController<T> {
97
+ enqueue(chunk: T): Promise<void>;
98
+ close(): void;
99
+ error(reason: any): void;
100
+ }
101
+
102
+ export interface ConsumableReadableStreamSource<T> {
103
+ start?(
104
+ controller: ConsumableReadableStreamController<T>
105
+ ): void | PromiseLike<void>;
106
+ pull?(
107
+ controller: ConsumableReadableStreamController<T>
108
+ ): void | PromiseLike<void>;
109
+ cancel?(reason: any): void | PromiseLike<void>;
110
+ }
111
+
112
+ export class ConsumableReadableStream<T> extends ReadableStream<Consumable<T>> {
113
+ public constructor(
114
+ source: ConsumableReadableStreamSource<T>,
115
+ strategy?: QueuingStrategy<T>
116
+ ) {
117
+ let wrappedController:
118
+ | ConsumableReadableStreamController<T>
119
+ | undefined;
120
+
121
+ let wrappedStrategy: QueuingStrategy<Consumable<T>> | undefined;
122
+ if (strategy) {
123
+ wrappedStrategy = {};
124
+ if ("highWaterMark" in strategy) {
125
+ wrappedStrategy.highWaterMark = strategy.highWaterMark;
126
+ }
127
+ if ("size" in strategy) {
128
+ wrappedStrategy.size = (chunk) => {
129
+ return strategy.size!(chunk.value);
130
+ };
131
+ }
132
+ }
133
+
134
+ super(
135
+ {
136
+ async start(controller) {
137
+ wrappedController = {
138
+ async enqueue(chunk) {
139
+ await enqueue(controller, chunk);
140
+ },
141
+ close() {
142
+ controller.close();
143
+ },
144
+ error(reason) {
145
+ controller.error(reason);
146
+ },
147
+ };
148
+
149
+ await source.start?.(wrappedController);
150
+ },
151
+ async pull() {
152
+ await source.pull?.(wrappedController!);
153
+ },
154
+ async cancel(reason) {
155
+ await source.cancel?.(reason);
156
+ },
157
+ },
158
+ wrappedStrategy
159
+ );
160
+ }
161
+ }
162
+
163
+ export interface ConsumableWritableStreamSink<T> {
164
+ start?(): void | PromiseLike<void>;
165
+ write?(chunk: T): void | PromiseLike<void>;
166
+ abort?(reason: any): void | PromiseLike<void>;
167
+ close?(): void | PromiseLike<void>;
168
+ }
169
+
170
+ export class ConsumableWritableStream<T> extends WritableStream<Consumable<T>> {
171
+ public static async write<T>(
172
+ writer: WritableStreamDefaultWriter<Consumable<T>>,
173
+ value: T
174
+ ) {
175
+ const consumable = new Consumable(value);
176
+ await writer.write(consumable);
177
+ await consumable.consumed;
178
+ }
179
+
180
+ public constructor(
181
+ sink: ConsumableWritableStreamSink<T>,
182
+ strategy?: QueuingStrategy<T>
183
+ ) {
184
+ let wrappedStrategy: QueuingStrategy<Consumable<T>> | undefined;
185
+ if (strategy) {
186
+ wrappedStrategy = {};
187
+ if ("highWaterMark" in strategy) {
188
+ wrappedStrategy.highWaterMark = strategy.highWaterMark;
189
+ }
190
+ if ("size" in strategy) {
191
+ wrappedStrategy.size = (chunk) => {
192
+ return strategy.size!(chunk.value);
193
+ };
194
+ }
195
+ }
196
+
197
+ super(
198
+ {
199
+ start() {
200
+ return sink.start?.();
201
+ },
202
+ async write(chunk) {
203
+ await chunk.tryConsume((value) => sink.write?.(value));
204
+ chunk.consume();
205
+ },
206
+ abort(reason) {
207
+ return sink.abort?.(reason);
208
+ },
209
+ close() {
210
+ return sink.close?.();
211
+ },
212
+ },
213
+ wrappedStrategy
214
+ );
215
+ }
216
+ }
217
+
218
+ export interface ConsumableTransformer<I, O> {
219
+ start?(
220
+ controller: ConsumableReadableStreamController<O>
221
+ ): void | PromiseLike<void>;
222
+ transform?(
223
+ chunk: I,
224
+ controller: ConsumableReadableStreamController<O>
225
+ ): void | PromiseLike<void>;
226
+ flush?(
227
+ controller: ConsumableReadableStreamController<O>
228
+ ): void | PromiseLike<void>;
229
+ }
230
+
231
+ export class ConsumableTransformStream<I, O> extends TransformStream<
232
+ Consumable<I>,
233
+ Consumable<O>
234
+ > {
235
+ public constructor(transformer: ConsumableTransformer<I, O>) {
236
+ let wrappedController:
237
+ | ConsumableReadableStreamController<O>
238
+ | undefined;
239
+
240
+ super({
241
+ async start(controller) {
242
+ wrappedController = {
243
+ async enqueue(chunk) {
244
+ await enqueue(controller, chunk);
245
+ },
246
+ close() {
247
+ controller.terminate();
248
+ },
249
+ error(reason) {
250
+ controller.error(reason);
251
+ },
252
+ };
253
+
254
+ await transformer.start?.(wrappedController);
255
+ },
256
+ async transform(chunk) {
257
+ await chunk.tryConsume((value) =>
258
+ transformer.transform?.(value, wrappedController!)
259
+ );
260
+ chunk.consume();
261
+ },
262
+ async flush() {
263
+ await transformer.flush?.(wrappedController!);
264
+ },
265
+ });
266
+ }
267
+ }
268
+
269
+ export class ConsumableInspectStream<T> extends TransformStream<
270
+ Consumable<T>,
271
+ Consumable<T>
272
+ > {
273
+ public constructor(callback: (value: T) => void) {
274
+ super({
275
+ transform(chunk, controller) {
276
+ callback(chunk.value);
277
+ controller.enqueue(chunk);
278
+ },
279
+ });
280
+ }
281
+ }
@@ -0,0 +1,113 @@
1
+ import { ConsumableTransformStream } from "./consumable.js";
2
+
3
+ /**
4
+ * Splits or combines buffers to specified size.
5
+ */
6
+ export class BufferCombiner {
7
+ private _capacity: number;
8
+ private readonly _buffer: Uint8Array;
9
+ private _offset: number;
10
+ private _available: number;
11
+
12
+ public constructor(size: number) {
13
+ this._capacity = size;
14
+ this._buffer = new Uint8Array(size);
15
+ this._offset = 0;
16
+ this._available = size;
17
+ }
18
+
19
+ /**
20
+ * Pushes data to the combiner.
21
+ * @param data The input data to be split or combined.
22
+ * @returns
23
+ * A generator that yields buffers of specified size.
24
+ * It may yield the same buffer multiple times, consume the data before calling `next`.
25
+ */
26
+ public *push(data: Uint8Array): Generator<Uint8Array, void, void> {
27
+ let offset = 0;
28
+ let available = data.byteLength;
29
+
30
+ if (this._offset !== 0) {
31
+ if (available >= this._available) {
32
+ this._buffer.set(
33
+ data.subarray(0, this._available),
34
+ this._offset
35
+ );
36
+ offset += this._available;
37
+ available -= this._available;
38
+
39
+ yield this._buffer;
40
+ this._offset = 0;
41
+ this._available = this._capacity;
42
+
43
+ if (available === 0) {
44
+ return;
45
+ }
46
+ } else {
47
+ this._buffer.set(data, this._offset);
48
+ this._offset += available;
49
+ this._available -= available;
50
+ return;
51
+ }
52
+ }
53
+
54
+ while (available >= this._capacity) {
55
+ const end = offset + this._capacity;
56
+ yield data.subarray(offset, end);
57
+ offset = end;
58
+ available -= this._capacity;
59
+ }
60
+
61
+ if (available > 0) {
62
+ this._buffer.set(data.subarray(offset), this._offset);
63
+ this._offset += available;
64
+ this._available -= available;
65
+ }
66
+ }
67
+
68
+ public flush(): Uint8Array | undefined {
69
+ if (this._offset === 0) {
70
+ return undefined;
71
+ }
72
+
73
+ const output = this._buffer.subarray(0, this._offset);
74
+ this._offset = 0;
75
+ this._available = this._capacity;
76
+ return output;
77
+ }
78
+ }
79
+
80
+ export class DistributionStream extends ConsumableTransformStream<
81
+ Uint8Array,
82
+ Uint8Array
83
+ > {
84
+ public constructor(size: number, combine = false) {
85
+ const combiner = combine ? new BufferCombiner(size) : undefined;
86
+ super({
87
+ async transform(chunk, controller) {
88
+ if (combiner) {
89
+ for (const buffer of combiner.push(chunk)) {
90
+ await controller.enqueue(buffer);
91
+ }
92
+ } else {
93
+ let offset = 0;
94
+ let available = chunk.byteLength;
95
+ while (available > 0) {
96
+ const end = offset + size;
97
+ await controller.enqueue(chunk.subarray(offset, end));
98
+ offset = end;
99
+ available -= size;
100
+ }
101
+ }
102
+ },
103
+ async flush(controller) {
104
+ if (combiner) {
105
+ const data = combiner.flush();
106
+ if (data) {
107
+ await controller.enqueue(data);
108
+ }
109
+ }
110
+ },
111
+ });
112
+ }
113
+ }