@yume-chan/adb 0.0.11 → 0.0.14
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.
- package/CHANGELOG.json +39 -0
- package/CHANGELOG.md +23 -1
- package/README.md +24 -17
- package/esm/adb.d.ts +10 -9
- package/esm/adb.d.ts.map +1 -1
- package/esm/adb.js +87 -64
- package/esm/adb.js.map +1 -1
- package/esm/auth.d.ts +6 -6
- package/esm/auth.d.ts.map +1 -1
- package/esm/auth.js +8 -6
- package/esm/auth.js.map +1 -1
- package/esm/backend.d.ts +2 -2
- package/esm/commands/install.d.ts.map +1 -1
- package/esm/commands/install.js +5 -7
- package/esm/commands/install.js.map +1 -1
- package/esm/commands/reverse.d.ts +7 -10
- package/esm/commands/reverse.d.ts.map +1 -1
- package/esm/commands/reverse.js +9 -15
- package/esm/commands/reverse.js.map +1 -1
- package/esm/commands/subprocess/index.d.ts +19 -20
- package/esm/commands/subprocess/index.d.ts.map +1 -1
- package/esm/commands/subprocess/index.js +36 -31
- package/esm/commands/subprocess/index.js.map +1 -1
- package/esm/commands/subprocess/legacy.d.ts +14 -7
- package/esm/commands/subprocess/legacy.d.ts.map +1 -1
- package/esm/commands/subprocess/legacy.js +14 -5
- package/esm/commands/subprocess/legacy.js.map +1 -1
- package/esm/commands/subprocess/protocol.d.ts +3 -2
- package/esm/commands/subprocess/protocol.d.ts.map +1 -1
- package/esm/commands/subprocess/protocol.js +6 -4
- package/esm/commands/subprocess/protocol.js.map +1 -1
- package/esm/commands/subprocess/protocols/index.d.ts +4 -0
- package/esm/commands/subprocess/protocols/index.d.ts.map +1 -0
- package/esm/commands/subprocess/protocols/index.js +4 -0
- package/esm/commands/subprocess/protocols/index.js.map +1 -0
- package/esm/commands/subprocess/protocols/none.d.ts +35 -0
- package/esm/commands/subprocess/protocols/none.d.ts.map +1 -0
- package/esm/commands/subprocess/protocols/none.js +53 -0
- package/esm/commands/subprocess/protocols/none.js.map +1 -0
- package/esm/commands/subprocess/protocols/shell.d.ts +39 -0
- package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -0
- package/esm/commands/subprocess/protocols/shell.js +155 -0
- package/esm/commands/subprocess/protocols/shell.js.map +1 -0
- package/esm/commands/subprocess/protocols/types.d.ts +50 -0
- package/esm/commands/subprocess/protocols/types.d.ts.map +1 -0
- package/esm/commands/subprocess/protocols/types.js +2 -0
- package/esm/commands/subprocess/protocols/types.js.map +1 -0
- package/esm/commands/subprocess/types.d.ts +9 -6
- package/esm/commands/subprocess/types.d.ts.map +1 -1
- package/esm/commands/sync/list.d.ts +25 -1
- package/esm/commands/sync/list.d.ts.map +1 -1
- package/esm/commands/sync/list.js +40 -5
- package/esm/commands/sync/list.js.map +1 -1
- package/esm/commands/sync/pull.d.ts.map +1 -1
- package/esm/commands/sync/pull.js +6 -3
- package/esm/commands/sync/pull.js.map +1 -1
- package/esm/commands/sync/request.d.ts +1 -0
- package/esm/commands/sync/request.d.ts.map +1 -1
- package/esm/commands/sync/request.js +1 -0
- package/esm/commands/sync/request.js.map +1 -1
- package/esm/commands/sync/response.d.ts +2 -1
- package/esm/commands/sync/response.d.ts.map +1 -1
- package/esm/commands/sync/response.js +2 -1
- package/esm/commands/sync/response.js.map +1 -1
- package/esm/commands/sync/stat.d.ts +13 -1
- package/esm/commands/sync/stat.d.ts.map +1 -1
- package/esm/commands/sync/stat.js +20 -7
- package/esm/commands/sync/stat.js.map +1 -1
- package/esm/commands/sync/sync.d.ts +13 -35
- package/esm/commands/sync/sync.d.ts.map +1 -1
- package/esm/commands/sync/sync.js +39 -9
- package/esm/commands/sync/sync.js.map +1 -1
- package/esm/crypto.d.ts +23 -10
- package/esm/crypto.d.ts.map +1 -1
- package/esm/crypto.js +64 -46
- package/esm/crypto.js.map +1 -1
- package/esm/features.d.ts +4 -2
- package/esm/features.d.ts.map +1 -1
- package/esm/features.js +6 -2
- package/esm/features.js.map +1 -1
- package/esm/packet.d.ts +21 -2
- package/esm/packet.d.ts.map +1 -1
- package/esm/packet.js +2 -2
- package/esm/packet.js.map +1 -1
- package/esm/socket/dispatcher.d.ts +34 -23
- package/esm/socket/dispatcher.d.ts.map +1 -1
- package/esm/socket/dispatcher.js +92 -70
- package/esm/socket/dispatcher.js.map +1 -1
- package/esm/socket/socket.d.ts +29 -8
- package/esm/socket/socket.d.ts.map +1 -1
- package/esm/socket/socket.js +41 -27
- package/esm/socket/socket.js.map +1 -1
- package/esm/stream/detect.d.ts +1 -17
- package/esm/stream/detect.d.ts.map +1 -1
- package/esm/stream/detect.js +4 -5
- package/esm/stream/detect.js.map +1 -1
- package/esm/stream/detect.polyfill.d.ts.map +1 -1
- package/esm/stream/detect.polyfill.js +4 -1
- package/esm/stream/detect.polyfill.js.map +1 -1
- package/esm/stream/transform.d.ts +37 -32
- package/esm/stream/transform.d.ts.map +1 -1
- package/esm/stream/transform.js +106 -92
- package/esm/stream/transform.js.map +1 -1
- package/esm/utils/encoding.d.ts.map +1 -1
- package/esm/utils/encoding.js.map +1 -1
- package/esm/utils/index.d.ts +1 -1
- package/esm/utils/index.d.ts.map +1 -1
- package/esm/utils/index.js +1 -1
- package/esm/utils/index.js.map +1 -1
- package/package.json +6 -6
- package/src/adb.ts +105 -76
- package/src/auth.ts +21 -18
- package/src/backend.ts +2 -2
- package/src/commands/install.ts +7 -9
- package/src/commands/reverse.ts +13 -24
- package/src/commands/subprocess/index.ts +57 -42
- package/src/commands/subprocess/protocols/index.ts +3 -0
- package/src/commands/subprocess/{legacy.ts → protocols/none.ts} +20 -10
- package/src/commands/subprocess/{protocol.ts → protocols/shell.ts} +22 -17
- package/src/commands/subprocess/{types.ts → protocols/types.ts} +13 -9
- package/src/commands/sync/list.ts +51 -5
- package/src/commands/sync/pull.ts +6 -3
- package/src/commands/sync/request.ts +1 -0
- package/src/commands/sync/response.ts +8 -2
- package/src/commands/sync/stat.ts +36 -9
- package/src/commands/sync/sync.ts +55 -19
- package/src/crypto.ts +72 -56
- package/src/features.ts +6 -2
- package/src/packet.ts +17 -6
- package/src/socket/dispatcher.ts +133 -88
- package/src/socket/socket.ts +61 -48
- package/src/stream/detect.polyfill.ts +4 -1
- package/src/stream/detect.ts +4 -24
- package/src/stream/transform.ts +133 -116
- package/src/utils/index.ts +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/src/utils/encoding.ts +0 -13
package/src/socket/socket.ts
CHANGED
|
@@ -1,32 +1,24 @@
|
|
|
1
1
|
import { PromiseResolver } from "@yume-chan/async";
|
|
2
|
+
import type { Disposable } from "@yume-chan/event";
|
|
2
3
|
import { AdbCommand } from '../packet.js';
|
|
3
|
-
import { ChunkStream, DuplexStreamFactory, pipeFrom, ReadableStream,
|
|
4
|
-
import type { AdbPacketDispatcher } from './dispatcher.js';
|
|
4
|
+
import { ChunkStream, DuplexStreamFactory, pipeFrom, PushReadableStream, type PushReadableStreamController, type ReadableStream, type ReadableWritablePair, type WritableStream } from '../stream/index.js';
|
|
5
|
+
import type { AdbPacketDispatcher, Closeable } from './dispatcher.js';
|
|
5
6
|
|
|
6
7
|
export interface AdbSocketInfo {
|
|
7
8
|
localId: number;
|
|
8
9
|
remoteId: number;
|
|
9
10
|
|
|
10
11
|
localCreated: boolean;
|
|
11
|
-
|
|
12
12
|
serviceString: string;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export interface AdbSocketConstructionOptions {
|
|
15
|
+
export interface AdbSocketConstructionOptions extends AdbSocketInfo {
|
|
16
16
|
dispatcher: AdbPacketDispatcher;
|
|
17
17
|
|
|
18
|
-
localId: number;
|
|
19
|
-
|
|
20
|
-
remoteId: number;
|
|
21
|
-
|
|
22
|
-
localCreated: boolean;
|
|
23
|
-
|
|
24
|
-
serviceString: string;
|
|
25
|
-
|
|
26
18
|
highWaterMark?: number | undefined;
|
|
27
19
|
}
|
|
28
20
|
|
|
29
|
-
export class
|
|
21
|
+
export class AdbSocketController implements AdbSocketInfo, ReadableWritablePair<Uint8Array, Uint8Array>, Closeable, Disposable {
|
|
30
22
|
private readonly dispatcher!: AdbPacketDispatcher;
|
|
31
23
|
|
|
32
24
|
public readonly localId!: number;
|
|
@@ -46,6 +38,9 @@ export class AdbSocket implements AdbSocketInfo {
|
|
|
46
38
|
private _closed = false;
|
|
47
39
|
public get closed() { return this._closed; }
|
|
48
40
|
|
|
41
|
+
private _socket: AdbSocket;
|
|
42
|
+
public get socket() { return this._socket; }
|
|
43
|
+
|
|
49
44
|
public constructor(options: AdbSocketConstructionOptions) {
|
|
50
45
|
Object.assign(this, options);
|
|
51
46
|
|
|
@@ -55,17 +50,32 @@ export class AdbSocket implements AdbSocketInfo {
|
|
|
55
50
|
|
|
56
51
|
this._factory = new DuplexStreamFactory<Uint8Array, Uint8Array>({
|
|
57
52
|
close: async () => {
|
|
58
|
-
await this.
|
|
53
|
+
await this.dispatcher.sendPacket(
|
|
54
|
+
AdbCommand.Close,
|
|
55
|
+
this.localId,
|
|
56
|
+
this.remoteId
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// Don't `dispose` here, we need to wait for `CLSE` response packet.
|
|
60
|
+
return false;
|
|
59
61
|
},
|
|
60
|
-
|
|
62
|
+
dispose: () => {
|
|
63
|
+
this._closed = true;
|
|
61
64
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
highWaterMark: options.highWaterMark ?? 16 * 1024,
|
|
66
|
-
size(chunk) { return chunk.byteLength; }
|
|
65
|
+
// Error out the pending writes
|
|
66
|
+
this._writePromise?.reject(new Error('Socket closed'));
|
|
67
|
+
},
|
|
67
68
|
});
|
|
68
69
|
|
|
70
|
+
this._readable = this._factory.wrapReadable(
|
|
71
|
+
new PushReadableStream(controller => {
|
|
72
|
+
this._readableController = controller;
|
|
73
|
+
}, {
|
|
74
|
+
highWaterMark: options.highWaterMark ?? 16 * 1024,
|
|
75
|
+
size(chunk) { return chunk.byteLength; }
|
|
76
|
+
})
|
|
77
|
+
);
|
|
78
|
+
|
|
69
79
|
this.writable = pipeFrom(
|
|
70
80
|
this._factory.createWritable({
|
|
71
81
|
write: async (chunk) => {
|
|
@@ -80,50 +90,53 @@ export class AdbSocket implements AdbSocketInfo {
|
|
|
80
90
|
await this._writePromise.promise;
|
|
81
91
|
},
|
|
82
92
|
}),
|
|
83
|
-
new ChunkStream(this.dispatcher.maxPayloadSize)
|
|
93
|
+
new ChunkStream(this.dispatcher.options.maxPayloadSize)
|
|
84
94
|
);
|
|
95
|
+
|
|
96
|
+
this._socket = new AdbSocket(this);
|
|
85
97
|
}
|
|
86
98
|
|
|
87
|
-
/**
|
|
88
|
-
* @internal
|
|
89
|
-
*/
|
|
90
99
|
public async enqueue(packet: Uint8Array) {
|
|
91
100
|
await this._readableController.enqueue(packet);
|
|
92
101
|
}
|
|
93
102
|
|
|
94
|
-
/**
|
|
95
|
-
* @internal
|
|
96
|
-
*/
|
|
97
103
|
public ack() {
|
|
98
104
|
this._writePromise?.resolve();
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
public async close(): Promise<void> {
|
|
102
|
-
|
|
103
|
-
this._writePromise?.reject(new Error('Socket closed'));
|
|
104
|
-
|
|
105
|
-
if (!this._closed) {
|
|
106
|
-
this._closed = true;
|
|
107
|
-
|
|
108
|
-
// Only send close packet when `close` is called before `dispose`
|
|
109
|
-
// (the client initiated the close)
|
|
110
|
-
await this.dispatcher.sendPacket(
|
|
111
|
-
AdbCommand.Close,
|
|
112
|
-
this.localId,
|
|
113
|
-
this.remoteId
|
|
114
|
-
);
|
|
115
|
-
}
|
|
108
|
+
this._factory.close();
|
|
116
109
|
}
|
|
117
110
|
|
|
118
|
-
/**
|
|
119
|
-
* @internal
|
|
120
|
-
*/
|
|
121
111
|
public dispose() {
|
|
122
|
-
this.
|
|
112
|
+
this._factory.dispose();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
123
115
|
|
|
124
|
-
|
|
116
|
+
/**
|
|
117
|
+
* AdbSocket is a duplex stream.
|
|
118
|
+
*
|
|
119
|
+
* To close it, call either `socket.close()`,
|
|
120
|
+
* `socket.readable.cancel()`, `socket.readable.getReader().cancel()`,
|
|
121
|
+
* `socket.writable.abort()`, `socket.writable.getWriter().abort()`,
|
|
122
|
+
* `socket.writable.close()` or `socket.writable.getWriter().close()`.
|
|
123
|
+
*/
|
|
124
|
+
export class AdbSocket implements AdbSocketInfo, ReadableWritablePair<Uint8Array, Uint8Array>{
|
|
125
|
+
private _controller: AdbSocketController;
|
|
126
|
+
|
|
127
|
+
public get localId(): number { return this._controller.localId; }
|
|
128
|
+
public get remoteId(): number { return this._controller.remoteId; }
|
|
129
|
+
public get localCreated(): boolean { return this._controller.localCreated; }
|
|
130
|
+
public get serviceString(): string { return this._controller.serviceString; }
|
|
131
|
+
|
|
132
|
+
public get readable(): ReadableStream<Uint8Array> { return this._controller.readable; }
|
|
133
|
+
public get writable(): WritableStream<Uint8Array> { return this._controller.writable; }
|
|
134
|
+
|
|
135
|
+
public constructor(controller: AdbSocketController) {
|
|
136
|
+
this._controller = controller;
|
|
137
|
+
}
|
|
125
138
|
|
|
126
|
-
|
|
127
|
-
this.close();
|
|
139
|
+
public close() {
|
|
140
|
+
return this._controller.close();
|
|
128
141
|
}
|
|
129
142
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
// cspell: ignore ponyfill
|
|
2
2
|
|
|
3
3
|
import type { AbortSignal } from "web-streams-polyfill";
|
|
4
|
-
// TODO:
|
|
4
|
+
// TODO: Upgrade to `web-streams-polyfill@4.0.0-beta.2` once released.
|
|
5
|
+
// `web-streams-polyfill@4.0.0-beta.1` changed the default export to ponyfill,
|
|
6
|
+
// But it forgot to include `type` export so it's unusable.
|
|
7
|
+
// See https://github.com/MattiasBuelens/web-streams-polyfill/pull/107
|
|
5
8
|
export * from 'web-streams-polyfill';
|
|
6
9
|
|
|
7
10
|
/** A controller object that allows you to abort one or more DOM requests as and when desired. */
|
package/src/stream/detect.ts
CHANGED
|
@@ -1,25 +1,5 @@
|
|
|
1
|
-
// cspell: ignore
|
|
1
|
+
// cspell: ignore vercel
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
//
|
|
5
|
-
export * from '
|
|
6
|
-
|
|
7
|
-
/** A controller object that allows you to abort one or more DOM requests as and when desired. */
|
|
8
|
-
export interface AbortController {
|
|
9
|
-
/**
|
|
10
|
-
* Returns the AbortSignal object associated with this object.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
readonly signal: AbortSignal;
|
|
14
|
-
/**
|
|
15
|
-
* Invoking this method will set this object's AbortSignal's aborted flag and signal to any observers that the associated activity is to be aborted.
|
|
16
|
-
*/
|
|
17
|
-
abort(): void;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export let AbortController: {
|
|
21
|
-
prototype: AbortController;
|
|
22
|
-
new(): AbortController;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
({ AbortController } = globalThis as any);
|
|
3
|
+
// Always use polyfilled version because
|
|
4
|
+
// Vercel doesn't support Node.js 16 (`streams/web` module) yet
|
|
5
|
+
export * from './detect.polyfill.js';
|
package/src/stream/transform.ts
CHANGED
|
@@ -1,93 +1,57 @@
|
|
|
1
1
|
import { PromiseResolver } from "@yume-chan/async";
|
|
2
2
|
import type Struct from "@yume-chan/struct";
|
|
3
|
-
import type { StructValueType } from "@yume-chan/struct";
|
|
3
|
+
import type { StructValueType, ValueOrPromise } from "@yume-chan/struct";
|
|
4
4
|
import { decodeUtf8 } from "../utils/index.js";
|
|
5
5
|
import { BufferedStream, BufferedStreamEndedError } from "./buffered.js";
|
|
6
|
-
import { AbortController, AbortSignal, ReadableStream, ReadableStreamDefaultReader, TransformStream, WritableStream, WritableStreamDefaultWriter, type QueuingStrategy, type ReadableStreamDefaultController, type ReadableWritablePair, type UnderlyingSink
|
|
6
|
+
import { AbortController, AbortSignal, ReadableStream, ReadableStreamDefaultReader, TransformStream, WritableStream, WritableStreamDefaultWriter, type QueuingStrategy, type ReadableStreamDefaultController, type ReadableWritablePair, type UnderlyingSink } from "./detect.js";
|
|
7
7
|
|
|
8
8
|
export interface DuplexStreamFactoryOptions {
|
|
9
|
-
|
|
9
|
+
close?: (() => ValueOrPromise<boolean | void>) | undefined;
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
dispose?: (() => void | Promise<void>) | undefined;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* A factory for creating a duplex stream.
|
|
16
|
+
*
|
|
17
|
+
* It can create multiple `ReadableStream`s and `WritableStream`s,
|
|
18
|
+
* when any of them is closed, all other streams will be closed as well.
|
|
19
|
+
*/
|
|
14
20
|
export class DuplexStreamFactory<R, W> {
|
|
15
21
|
private readableControllers: ReadableStreamDefaultController<R>[] = [];
|
|
16
|
-
|
|
22
|
+
|
|
23
|
+
private _writableClosed = false;
|
|
24
|
+
public get writableClosed() { return this._writableClosed; }
|
|
17
25
|
|
|
18
26
|
private _closed = new PromiseResolver<void>();
|
|
19
27
|
public get closed() { return this._closed.promise; }
|
|
20
28
|
|
|
21
29
|
private options: DuplexStreamFactoryOptions;
|
|
22
30
|
|
|
23
|
-
private _closeRequestedByReadable = false;
|
|
24
|
-
private _writableClosed = false;
|
|
25
|
-
|
|
26
31
|
public constructor(options?: DuplexStreamFactoryOptions) {
|
|
27
32
|
this.options = options ?? {};
|
|
28
33
|
}
|
|
29
34
|
|
|
30
|
-
public
|
|
31
|
-
return new
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
public wrapReadable(readable: ReadableStream<R>): WrapReadableStream<R> {
|
|
36
|
+
return new WrapReadableStream<R>({
|
|
37
|
+
start: (controller) => {
|
|
38
|
+
this.readableControllers.push(controller);
|
|
39
|
+
return readable;
|
|
40
|
+
},
|
|
41
|
+
cancel: async () => {
|
|
42
|
+
// cancel means the local peer closes the connection first.
|
|
36
43
|
await this.close();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
source({
|
|
40
|
-
abortSignal: controller.abortSignal,
|
|
41
|
-
async enqueue(chunk) {
|
|
42
|
-
await controller.enqueue(chunk);
|
|
43
|
-
},
|
|
44
|
-
close: async () => {
|
|
45
|
-
controller.close();
|
|
46
|
-
this._closeRequestedByReadable = true;
|
|
47
|
-
await this.close();
|
|
48
|
-
},
|
|
49
|
-
error: async (e?: any) => {
|
|
50
|
-
controller.error(e);
|
|
51
|
-
this._closeRequestedByReadable = true;
|
|
52
|
-
await this.close();
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
}, strategy);
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
public createWrapReadable(readable: ReadableStream<R>): WrapReadableStream<R, ReadableStream<R>, void> {
|
|
59
|
-
return new WrapReadableStream<R, ReadableStream<R>, void>({
|
|
60
|
-
async start() {
|
|
61
|
-
return {
|
|
62
|
-
readable,
|
|
63
|
-
state: undefined,
|
|
64
|
-
};
|
|
65
44
|
},
|
|
66
45
|
close: async () => {
|
|
67
|
-
|
|
68
|
-
await this.
|
|
46
|
+
// stream end means the remote peer closed the connection first.
|
|
47
|
+
await this.dispose();
|
|
69
48
|
},
|
|
70
49
|
});
|
|
71
50
|
}
|
|
72
51
|
|
|
73
|
-
public createReadable(source?: UnderlyingSource<R>, strategy?: QueuingStrategy<R>): ReadableStream<R> {
|
|
74
|
-
return new ReadableStream<R>({
|
|
75
|
-
start: async (controller) => {
|
|
76
|
-
this.readableControllers.push(controller);
|
|
77
|
-
await source?.start?.(controller);
|
|
78
|
-
},
|
|
79
|
-
pull: (controller) => {
|
|
80
|
-
return source?.pull?.(controller);
|
|
81
|
-
},
|
|
82
|
-
cancel: async (reason) => {
|
|
83
|
-
await source?.cancel?.(reason);
|
|
84
|
-
this._closeRequestedByReadable = true;
|
|
85
|
-
await this.close();
|
|
86
|
-
},
|
|
87
|
-
}, strategy);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
52
|
public createWritable(sink: UnderlyingSink<W>, strategy?: QueuingStrategy<W>): WritableStream<W> {
|
|
53
|
+
// `WritableStream` has no way to tell if the remote peer has closed the connection.
|
|
54
|
+
// So it only triggers `close`.
|
|
91
55
|
return new WritableStream<W>({
|
|
92
56
|
start: async (controller) => {
|
|
93
57
|
await sink.start?.(controller);
|
|
@@ -99,41 +63,37 @@ export class DuplexStreamFactory<R, W> {
|
|
|
99
63
|
|
|
100
64
|
await sink.write?.(chunk, controller);
|
|
101
65
|
},
|
|
102
|
-
close: async () => {
|
|
103
|
-
await sink.close?.();
|
|
104
|
-
this.close();
|
|
105
|
-
},
|
|
106
66
|
abort: async (reason) => {
|
|
107
67
|
await sink.abort?.(reason);
|
|
108
68
|
await this.close();
|
|
109
69
|
},
|
|
70
|
+
close: async () => {
|
|
71
|
+
await sink.close?.();
|
|
72
|
+
await this.close();
|
|
73
|
+
},
|
|
110
74
|
}, strategy);
|
|
111
75
|
}
|
|
112
76
|
|
|
113
|
-
public async
|
|
114
|
-
this.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
for (const controller of this.readableControllers) {
|
|
118
|
-
try {
|
|
119
|
-
controller.close();
|
|
120
|
-
} catch { }
|
|
77
|
+
public async close() {
|
|
78
|
+
if (this._writableClosed) {
|
|
79
|
+
return;
|
|
121
80
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
} catch { }
|
|
81
|
+
this._writableClosed = true;
|
|
82
|
+
if (await this.options.close?.() !== false) {
|
|
83
|
+
// `close` can return `false` to disable automatic `dispose`.
|
|
84
|
+
await this.dispose();
|
|
127
85
|
}
|
|
128
86
|
}
|
|
129
87
|
|
|
130
|
-
public async
|
|
88
|
+
public async dispose() {
|
|
131
89
|
this._writableClosed = true;
|
|
90
|
+
this._closed.resolve();
|
|
132
91
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
await this.closeReadableStreams();
|
|
92
|
+
for (const controller of this.readableControllers) {
|
|
93
|
+
try { controller.close(); } catch { }
|
|
136
94
|
}
|
|
95
|
+
|
|
96
|
+
await this.options.dispose?.();
|
|
137
97
|
}
|
|
138
98
|
}
|
|
139
99
|
|
|
@@ -147,21 +107,21 @@ export class DecodeUtf8Stream extends TransformStream<Uint8Array, string>{
|
|
|
147
107
|
}
|
|
148
108
|
}
|
|
149
109
|
|
|
150
|
-
export class GatherStringStream extends
|
|
110
|
+
export class GatherStringStream extends WritableStream<string>{
|
|
151
111
|
// Optimization: rope (concat strings) is faster than `[].join('')`
|
|
152
112
|
private _result = '';
|
|
153
113
|
public get result() { return this._result; }
|
|
154
114
|
|
|
155
115
|
public constructor() {
|
|
156
116
|
super({
|
|
157
|
-
|
|
117
|
+
write: (chunk) => {
|
|
158
118
|
this._result += chunk;
|
|
159
119
|
},
|
|
160
120
|
});
|
|
161
121
|
}
|
|
162
122
|
}
|
|
163
123
|
|
|
164
|
-
// TODO:
|
|
124
|
+
// TODO: StructTransformStream: Looking for better implementation
|
|
165
125
|
export class StructDeserializeStream<T extends Struct<any, any, any, any>>
|
|
166
126
|
implements ReadableWritablePair<Uint8Array, StructValueType<T>>{
|
|
167
127
|
private _readable: ReadableStream<StructValueType<T>>;
|
|
@@ -219,25 +179,43 @@ export class StructSerializeStream<T extends Struct<any, any, any, any>>
|
|
|
219
179
|
}
|
|
220
180
|
}
|
|
221
181
|
|
|
222
|
-
export
|
|
223
|
-
|
|
224
|
-
|
|
182
|
+
export type WrapWritableStreamStart<T> = () => ValueOrPromise<WritableStream<T>>;
|
|
183
|
+
|
|
184
|
+
export interface WritableStreamWrapper<T> {
|
|
185
|
+
start: WrapWritableStreamStart<T>;
|
|
186
|
+
close?(): Promise<void>;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function getWrappedWritableStream<T>(
|
|
190
|
+
wrapper: WritableStream<T> | WrapWritableStreamStart<T> | WritableStreamWrapper<T>
|
|
191
|
+
) {
|
|
192
|
+
if ('start' in wrapper) {
|
|
193
|
+
return await wrapper.start();
|
|
194
|
+
} else if (typeof wrapper === 'function') {
|
|
195
|
+
return await wrapper();
|
|
196
|
+
} else {
|
|
197
|
+
// Can't use `wrapper instanceof WritableStream`
|
|
198
|
+
// Because we want to be compatible with any WritableStream-like objects
|
|
199
|
+
return wrapper;
|
|
200
|
+
}
|
|
225
201
|
}
|
|
226
202
|
|
|
227
|
-
export class WrapWritableStream<T
|
|
228
|
-
public writable!:
|
|
203
|
+
export class WrapWritableStream<T> extends WritableStream<T> {
|
|
204
|
+
public writable!: WritableStream<T>;
|
|
229
205
|
|
|
230
206
|
private writer!: WritableStreamDefaultWriter<T>;
|
|
231
207
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
public constructor(wrapper: WritableStreamWrapper<T, W, S>) {
|
|
208
|
+
public constructor(wrapper: WritableStream<T> | WrapWritableStreamStart<T> | WritableStreamWrapper<T>) {
|
|
235
209
|
super({
|
|
236
210
|
start: async () => {
|
|
237
|
-
|
|
238
|
-
this
|
|
239
|
-
this
|
|
240
|
-
this.
|
|
211
|
+
// `start` is invoked before `ReadableStream`'s constructor finish,
|
|
212
|
+
// so using `this` synchronously causes
|
|
213
|
+
// "Must call super constructor in derived class before accessing 'this' or returning from derived constructor".
|
|
214
|
+
// Queue a microtask to avoid this.
|
|
215
|
+
await Promise.resolve();
|
|
216
|
+
|
|
217
|
+
this.writable = await getWrappedWritableStream(wrapper);
|
|
218
|
+
this.writer = this.writable.getWriter();
|
|
241
219
|
},
|
|
242
220
|
write: async (chunk) => {
|
|
243
221
|
// Maintain back pressure
|
|
@@ -246,45 +224,84 @@ export class WrapWritableStream<T, W extends WritableStream<T>, S> extends Writa
|
|
|
246
224
|
},
|
|
247
225
|
abort: async (reason) => {
|
|
248
226
|
await this.writer.abort(reason);
|
|
249
|
-
wrapper
|
|
227
|
+
if ('close' in wrapper) {
|
|
228
|
+
await wrapper.close?.();
|
|
229
|
+
}
|
|
250
230
|
},
|
|
251
231
|
close: async () => {
|
|
232
|
+
// Close the inner stream first.
|
|
233
|
+
// Usually the inner stream is a logical sub-stream over the outer stream,
|
|
234
|
+
// closing the outer stream first will make the inner stream incapable of
|
|
235
|
+
// sending data in its `close` handler.
|
|
252
236
|
await this.writer.close();
|
|
253
|
-
|
|
237
|
+
if ('close' in wrapper) {
|
|
238
|
+
await wrapper.close?.();
|
|
239
|
+
}
|
|
254
240
|
},
|
|
255
241
|
});
|
|
256
242
|
}
|
|
257
243
|
}
|
|
258
244
|
|
|
259
|
-
export
|
|
260
|
-
|
|
261
|
-
|
|
245
|
+
export type WrapReadableStreamStart<T> = (controller: ReadableStreamDefaultController<T>) => ValueOrPromise<ReadableStream<T>>;
|
|
246
|
+
|
|
247
|
+
export interface ReadableStreamWrapper<T> {
|
|
248
|
+
start: WrapReadableStreamStart<T>;
|
|
249
|
+
cancel?(reason?: any): ValueOrPromise<void>;
|
|
250
|
+
close?(): ValueOrPromise<void>;
|
|
262
251
|
}
|
|
263
252
|
|
|
264
|
-
|
|
265
|
-
|
|
253
|
+
function getWrappedReadableStream<T>(
|
|
254
|
+
wrapper: ReadableStream<T> | WrapReadableStreamStart<T> | ReadableStreamWrapper<T>,
|
|
255
|
+
controller: ReadableStreamDefaultController<T>
|
|
256
|
+
) {
|
|
257
|
+
if ('start' in wrapper) {
|
|
258
|
+
return wrapper.start(controller);
|
|
259
|
+
} else if (typeof wrapper === 'function') {
|
|
260
|
+
return wrapper(controller);
|
|
261
|
+
} else {
|
|
262
|
+
// Can't use `wrapper instanceof ReadableStream`
|
|
263
|
+
// Because we want to be compatible with any ReadableStream-like objects
|
|
264
|
+
return wrapper;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
266
267
|
|
|
267
|
-
|
|
268
|
+
/**
|
|
269
|
+
* This class has multiple usages:
|
|
270
|
+
*
|
|
271
|
+
* 1. Get notified when the stream is cancelled or closed.
|
|
272
|
+
* 2. Synchronously create a `ReadableStream` by asynchronously return another `ReadableStream`.
|
|
273
|
+
* 3. Convert native `ReadableStream`s to polyfilled ones so they can `pipe` between.
|
|
274
|
+
*/
|
|
275
|
+
export class WrapReadableStream<T> extends ReadableStream<T>{
|
|
276
|
+
public readable!: ReadableStream<T>;
|
|
268
277
|
|
|
269
|
-
private
|
|
278
|
+
private reader!: ReadableStreamDefaultReader<T>;
|
|
270
279
|
|
|
271
|
-
public constructor(wrapper:
|
|
280
|
+
public constructor(wrapper: ReadableStream<T> | WrapReadableStreamStart<T> | ReadableStreamWrapper<T>) {
|
|
272
281
|
super({
|
|
273
|
-
start: async () => {
|
|
274
|
-
|
|
275
|
-
this
|
|
276
|
-
this
|
|
277
|
-
this.
|
|
282
|
+
start: async (controller) => {
|
|
283
|
+
// `start` is invoked before `ReadableStream`'s constructor finish,
|
|
284
|
+
// so using `this` synchronously causes
|
|
285
|
+
// "Must call super constructor in derived class before accessing 'this' or returning from derived constructor".
|
|
286
|
+
// Queue a microtask to avoid this.
|
|
287
|
+
await Promise.resolve();
|
|
288
|
+
|
|
289
|
+
this.readable = await getWrappedReadableStream(wrapper, controller);
|
|
290
|
+
this.reader = this.readable.getReader();
|
|
278
291
|
},
|
|
279
292
|
cancel: async (reason) => {
|
|
280
293
|
await this.reader.cancel(reason);
|
|
281
|
-
wrapper
|
|
294
|
+
if ('cancel' in wrapper) {
|
|
295
|
+
await wrapper.cancel?.(reason);
|
|
296
|
+
}
|
|
282
297
|
},
|
|
283
298
|
pull: async (controller) => {
|
|
284
299
|
const result = await this.reader.read();
|
|
285
300
|
if (result.done) {
|
|
286
|
-
wrapper.close?.(this.state);
|
|
287
301
|
controller.close();
|
|
302
|
+
if ('close' in wrapper) {
|
|
303
|
+
await wrapper.close?.();
|
|
304
|
+
}
|
|
288
305
|
} else {
|
|
289
306
|
controller.enqueue(result.value);
|
|
290
307
|
}
|
|
@@ -394,7 +411,7 @@ export class PushReadableStream<T> extends ReadableStream<T> {
|
|
|
394
411
|
source({
|
|
395
412
|
abortSignal: canceled.signal,
|
|
396
413
|
async enqueue(chunk) {
|
|
397
|
-
// Only when the stream
|
|
414
|
+
// Only when the stream is errored, `desiredSize` will be `null`.
|
|
398
415
|
// But since `null <= 0` is `true`
|
|
399
416
|
// (`null <= 0` is evaluated as `!(null > 0)` => `!false` => `true`),
|
|
400
417
|
// not handling it will cause a deadlock.
|
|
@@ -419,8 +436,8 @@ export class PushReadableStream<T> extends ReadableStream<T> {
|
|
|
419
436
|
waterMarkLow?.resolve();
|
|
420
437
|
},
|
|
421
438
|
cancel: async (reason) => {
|
|
422
|
-
waterMarkLow?.reject(reason);
|
|
423
439
|
canceled.abort();
|
|
440
|
+
waterMarkLow?.reject(reason);
|
|
424
441
|
},
|
|
425
442
|
}, strategy);
|
|
426
443
|
}
|
package/src/utils/index.ts
CHANGED