@yume-chan/stream-extra 0.0.17 → 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 (104) hide show
  1. package/CHANGELOG.json +33 -9
  2. package/CHANGELOG.md +15 -2
  3. package/LICENSE +1 -1
  4. package/README.md +35 -3
  5. package/esm/buffered-transform.d.ts +11 -10
  6. package/esm/buffered-transform.d.ts.map +1 -1
  7. package/esm/buffered-transform.js +55 -49
  8. package/esm/buffered-transform.js.map +1 -1
  9. package/esm/buffered.d.ts +29 -27
  10. package/esm/buffered.d.ts.map +1 -1
  11. package/esm/buffered.js +128 -130
  12. package/esm/buffered.js.map +1 -1
  13. package/esm/chunk.d.ts +7 -4
  14. package/esm/chunk.d.ts.map +1 -1
  15. package/esm/chunk.js +48 -7
  16. package/esm/chunk.js.map +1 -1
  17. package/esm/consumable.d.ts +53 -0
  18. package/esm/consumable.d.ts.map +1 -0
  19. package/esm/consumable.js +178 -0
  20. package/esm/consumable.js.map +1 -0
  21. package/esm/decode-utf8.d.ts +4 -4
  22. package/esm/decode-utf8.d.ts.map +1 -1
  23. package/esm/decode-utf8.js +11 -11
  24. package/esm/decode-utf8.js.map +1 -1
  25. package/esm/distribution.d.ts +24 -0
  26. package/esm/distribution.d.ts.map +1 -0
  27. package/esm/distribution.js +99 -0
  28. package/esm/distribution.js.map +1 -0
  29. package/esm/duplex.d.ts +48 -47
  30. package/esm/duplex.d.ts.map +1 -1
  31. package/esm/duplex.js +90 -88
  32. package/esm/duplex.js.map +1 -1
  33. package/esm/gather-string.d.ts +6 -6
  34. package/esm/gather-string.d.ts.map +1 -1
  35. package/esm/gather-string.js +15 -13
  36. package/esm/gather-string.js.map +1 -1
  37. package/esm/index.d.ts +16 -15
  38. package/esm/index.d.ts.map +1 -1
  39. package/esm/index.js +16 -15
  40. package/esm/index.js.map +1 -1
  41. package/esm/inspect.d.ts +4 -4
  42. package/esm/inspect.js +11 -11
  43. package/esm/pipe-from.d.ts +12 -12
  44. package/esm/pipe-from.d.ts.map +1 -1
  45. package/esm/pipe-from.js +23 -26
  46. package/esm/pipe-from.js.map +1 -1
  47. package/esm/push-readable.d.ts +19 -11
  48. package/esm/push-readable.d.ts.map +1 -1
  49. package/esm/push-readable.js +61 -46
  50. package/esm/push-readable.js.map +1 -1
  51. package/esm/split-string.d.ts +4 -4
  52. package/esm/split-string.d.ts.map +1 -1
  53. package/esm/split-string.js +24 -24
  54. package/esm/split-string.js.map +1 -1
  55. package/esm/stream.d.ts +25 -17
  56. package/esm/stream.d.ts.map +1 -1
  57. package/esm/stream.js +26 -3
  58. package/esm/stream.js.map +1 -1
  59. package/esm/struct-deserialize.d.ts +6 -6
  60. package/esm/struct-deserialize.d.ts.map +1 -1
  61. package/esm/struct-deserialize.js +8 -8
  62. package/esm/struct-deserialize.js.map +1 -1
  63. package/esm/struct-serialize.d.ts +5 -5
  64. package/esm/struct-serialize.d.ts.map +1 -1
  65. package/esm/struct-serialize.js +10 -10
  66. package/esm/struct-serialize.js.map +1 -1
  67. package/esm/trace.d.ts +16 -0
  68. package/esm/trace.d.ts.map +1 -0
  69. package/esm/trace.js +131 -0
  70. package/esm/trace.js.map +1 -0
  71. package/esm/wrap-readable.d.ts +21 -20
  72. package/esm/wrap-readable.d.ts.map +1 -1
  73. package/esm/wrap-readable.js +56 -56
  74. package/esm/wrap-readable.js.map +1 -1
  75. package/esm/wrap-writable.d.ts +14 -12
  76. package/esm/wrap-writable.d.ts.map +1 -1
  77. package/esm/wrap-writable.js +62 -52
  78. package/esm/wrap-writable.js.map +1 -1
  79. package/package.json +13 -9
  80. package/src/buffered-transform.ts +32 -17
  81. package/src/buffered.ts +14 -15
  82. package/src/consumable.ts +281 -0
  83. package/src/decode-utf8.ts +3 -2
  84. package/src/distribution.ts +113 -0
  85. package/src/duplex.ts +27 -10
  86. package/src/gather-string.ts +5 -3
  87. package/src/index.ts +16 -15
  88. package/src/inspect.ts +1 -1
  89. package/src/pipe-from.ts +11 -10
  90. package/src/push-readable.ts +77 -46
  91. package/src/split-string.ts +5 -2
  92. package/src/stream.ts +48 -6
  93. package/src/struct-deserialize.ts +6 -4
  94. package/src/struct-serialize.ts +4 -2
  95. package/src/wrap-readable.ts +30 -12
  96. package/src/wrap-writable.ts +34 -11
  97. package/tsconfig.build.json +1 -1
  98. package/tsconfig.build.tsbuildinfo +1 -1
  99. package/esm/native.d.ts +0 -240
  100. package/esm/native.d.ts.map +0 -1
  101. package/esm/native.js +0 -60
  102. package/esm/native.js.map +0 -1
  103. package/src/chunk.ts +0 -15
  104. package/src/native.ts +0 -362
@@ -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":"AACA,OAAO,EAAE,cAAc,EAA+B,MAAM,aAAa,CAAC;AAS1E,KAAK,UAAU,wBAAwB,CACnC,OAAkF;IAElF,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,YAAmB,OAAkF;QACjG,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.17",
3
+ "version": "0.0.19",
4
4
  "description": "Extensions to Web Streams API",
5
5
  "keywords": [
6
6
  "stream",
@@ -26,21 +26,25 @@
26
26
  "types": "esm/index.d.ts",
27
27
  "dependencies": {
28
28
  "@yume-chan/async": "^2.2.0",
29
- "@yume-chan/struct": "^0.0.17",
30
- "tslib": "^2.4.0",
29
+ "@yume-chan/struct": "^0.0.19",
30
+ "tslib": "^2.4.1",
31
31
  "web-streams-polyfill": "^4.0.0-beta.3"
32
32
  },
33
33
  "devDependencies": {
34
- "@jest/globals": "^28.1.2",
35
- "@yume-chan/ts-package-builder": "^1.0.0",
34
+ "@jest/globals": "^29.5.0",
35
+ "@yume-chan/eslint-config": "^1.0.0",
36
+ "@yume-chan/tsconfig": "^1.0.0",
36
37
  "cross-env": "^7.0.3",
37
- "jest": "^28.1.2",
38
- "ts-jest": "^28.0.5",
39
- "typescript": "^4.7.4"
38
+ "eslint": "^8.36.0",
39
+ "jest": "^29.5.0",
40
+ "prettier": "^2.8.4",
41
+ "ts-jest": "^29.0.4",
42
+ "typescript": "^4.9.4"
40
43
  },
41
44
  "scripts": {
42
45
  "build": "tsc -b tsconfig.build.json",
43
46
  "build:watch": "tsc -b tsconfig.build.json",
44
- "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --coverage"
47
+ "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --coverage",
48
+ "lint": "eslint src/**/*.ts --fix && prettier src/**/*.ts --write --tab-width 4"
45
49
  }
46
50
  }
@@ -1,24 +1,39 @@
1
- import type { ValueOrPromise } from '@yume-chan/struct';
2
- import { BufferedReadableStream, BufferedReadableStreamEndedError } from './buffered.js';
3
- import { PushReadableStream, PushReadableStreamController } from './push-readable.js';
4
- import { ReadableStream, ReadableWritablePair, WritableStream } from './stream.js';
1
+ import type { ValueOrPromise } from "@yume-chan/struct";
2
+
3
+ import {
4
+ BufferedReadableStream,
5
+ BufferedReadableStreamEndedError,
6
+ } from "./buffered.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";
5
11
 
6
12
  // TODO: BufferedTransformStream: find better implementation
7
- export class BufferedTransformStream<T> implements ReadableWritablePair<T, Uint8Array> {
13
+ export class BufferedTransformStream<T>
14
+ implements ReadableWritablePair<T, Uint8Array>
15
+ {
8
16
  private _readable: ReadableStream<T>;
9
- public get readable() { return this._readable; }
17
+ public get readable() {
18
+ return this._readable;
19
+ }
10
20
 
11
21
  private _writable: WritableStream<Uint8Array>;
12
- public get writable() { return this._writable; }
22
+ public get writable() {
23
+ return this._writable;
24
+ }
13
25
 
14
- constructor(transform: (stream: BufferedReadableStream) => ValueOrPromise<T>) {
26
+ constructor(
27
+ transform: (stream: BufferedReadableStream) => ValueOrPromise<T>
28
+ ) {
15
29
  // Convert incoming chunks to a `BufferedReadableStream`
16
30
  let sourceStreamController!: PushReadableStreamController<Uint8Array>;
17
31
 
18
- const buffered = new BufferedReadableStream(new PushReadableStream<Uint8Array>(
19
- controller =>
20
- sourceStreamController = controller,
21
- ));
32
+ const buffered = new BufferedReadableStream(
33
+ new PushReadableStream<Uint8Array>((controller) => {
34
+ sourceStreamController = controller;
35
+ })
36
+ );
22
37
 
23
38
  this._readable = new ReadableStream<T>({
24
39
  async pull(controller) {
@@ -26,9 +41,9 @@ export class BufferedTransformStream<T> implements ReadableWritablePair<T, Uint8
26
41
  const value = await transform(buffered);
27
42
  controller.enqueue(value);
28
43
  } catch (e) {
29
- // TODO: BufferedTransformStream: The semantic of stream ending is not clear
30
- // If the `transform` started but did not finish, it should really be an error?
31
- // But we can't detect that, unless there is a `peek` method on buffered stream.
44
+ // Treat `BufferedReadableStreamEndedError` as a normal end.
45
+ // If the `transform` method doesn't have enough data to return a value,
46
+ // it should throw another error to indicate that.
32
47
  if (e instanceof BufferedReadableStreamEndedError) {
33
48
  controller.close();
34
49
  return;
@@ -39,8 +54,8 @@ export class BufferedTransformStream<T> implements ReadableWritablePair<T, Uint8
39
54
  cancel: (reason) => {
40
55
  // Propagate cancel to the source stream
41
56
  // So future writes will be rejected
42
- buffered.cancel(reason);
43
- }
57
+ return buffered.cancel(reason);
58
+ },
44
59
  });
45
60
 
46
61
  this._writable = new WritableStream({
package/src/buffered.ts CHANGED
@@ -3,7 +3,7 @@ import type { ReadableStream, ReadableStreamDefaultReader } from "./stream.js";
3
3
 
4
4
  export class BufferedReadableStreamEndedError extends Error {
5
5
  public constructor() {
6
- super('Stream ended');
6
+ super("Stream ended");
7
7
 
8
8
  // Fix Error's prototype chain when compiling to ES5
9
9
  Object.setPrototypeOf(this, new.target.prototype);
@@ -15,8 +15,12 @@ export class BufferedReadableStream {
15
15
  private bufferedOffset = 0;
16
16
  private bufferedLength = 0;
17
17
 
18
- protected readonly stream: ReadableStream<Uint8Array>;
18
+ private _position = 0;
19
+ public get position() {
20
+ return this._position;
21
+ }
19
22
 
23
+ protected readonly stream: ReadableStream<Uint8Array>;
20
24
  protected readonly reader: ReadableStreamDefaultReader<Uint8Array>;
21
25
 
22
26
  public constructor(stream: ReadableStream<Uint8Array>) {
@@ -29,6 +33,7 @@ export class BufferedReadableStream {
29
33
  if (done) {
30
34
  throw new BufferedReadableStreamEndedError();
31
35
  }
36
+ this._position += value.byteLength;
32
37
  return value;
33
38
  }
34
39
 
@@ -115,23 +120,17 @@ export class BufferedReadableStream {
115
120
  */
116
121
  public release(): ReadableStream<Uint8Array> {
117
122
  if (this.buffered) {
118
- return new PushReadableStream<Uint8Array>(async controller => {
123
+ return new PushReadableStream<Uint8Array>(async (controller) => {
119
124
  // Put the remaining data back to the stream
120
125
  await controller.enqueue(this.buffered!);
121
126
 
122
127
  // Manually pipe the stream
123
128
  while (true) {
124
- try {
125
- const { done, value } = await this.reader.read();
126
- if (done) {
127
- controller.close();
128
- break;
129
- } else {
130
- await controller.enqueue(value);
131
- }
132
- } catch (e) {
133
- controller.error(e);
134
- break;
129
+ const { done, value } = await this.reader.read();
130
+ if (done) {
131
+ return;
132
+ } else {
133
+ await controller.enqueue(value);
135
134
  }
136
135
  }
137
136
  });
@@ -142,7 +141,7 @@ export class BufferedReadableStream {
142
141
  }
143
142
  }
144
143
 
145
- public cancel(reason?: any) {
144
+ public cancel(reason?: unknown) {
146
145
  return this.reader.cancel(reason);
147
146
  }
148
147
  }
@@ -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
+ }
@@ -1,7 +1,8 @@
1
- import { decodeUtf8 } from '@yume-chan/struct';
1
+ import { decodeUtf8 } from "@yume-chan/struct";
2
+
2
3
  import { TransformStream } from "./stream.js";
3
4
 
4
- export class DecodeUtf8Stream extends TransformStream<Uint8Array, string>{
5
+ export class DecodeUtf8Stream extends TransformStream<Uint8Array, string> {
5
6
  public constructor() {
6
7
  super({
7
8
  transform(chunk, controller) {