@serveryyswys/node.operit 0.0.0-stage → 0.1.2
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/BUILTINS.json +233 -0
- package/DESIGN.md +513 -0
- package/LICENSE +674 -0
- package/README.md +301 -2
- package/dist/assert/strict.d.ts +2 -0
- package/dist/assert/strict.js +7 -0
- package/dist/assert.d.ts +3 -0
- package/dist/assert.js +483 -0
- package/dist/buffer.d.ts +60 -0
- package/dist/buffer.js +524 -0
- package/dist/child_process.d.ts +56 -0
- package/dist/child_process.js +371 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +19 -0
- package/dist/crypto.d.ts +76 -0
- package/dist/crypto.js +312 -0
- package/dist/dns/promises.d.ts +34 -0
- package/dist/dns/promises.js +190 -0
- package/dist/dns.d.ts +88 -0
- package/dist/dns.js +259 -0
- package/dist/events.d.ts +40 -0
- package/dist/events.js +237 -0
- package/dist/fs/promises.d.ts +159 -0
- package/dist/fs/promises.js +129 -0
- package/dist/fs.d.ts +221 -0
- package/dist/fs.js +944 -0
- package/dist/http.d.ts +92 -0
- package/dist/http.js +319 -0
- package/dist/https.d.ts +296 -0
- package/dist/https.js +50 -0
- package/dist/index.d.ts +2422 -0
- package/dist/index.js +57 -0
- package/dist/lib/bridge.d.ts +60 -0
- package/dist/lib/bridge.js +205 -0
- package/dist/lib/bytes.d.ts +20 -0
- package/dist/lib/bytes.js +239 -0
- package/dist/lib/child-process-types.d.ts +45 -0
- package/dist/lib/child-process-types.js +2 -0
- package/dist/lib/cwd.d.ts +2 -0
- package/dist/lib/cwd.js +32 -0
- package/dist/lib/deep-equal.d.ts +3 -0
- package/dist/lib/deep-equal.js +223 -0
- package/dist/lib/deflate.d.ts +2 -0
- package/dist/lib/deflate.js +234 -0
- package/dist/lib/dns-driver.d.ts +14 -0
- package/dist/lib/dns-driver.js +85 -0
- package/dist/lib/dns-types.d.ts +22 -0
- package/dist/lib/dns-types.js +2 -0
- package/dist/lib/errors.d.ts +48 -0
- package/dist/lib/errors.js +424 -0
- package/dist/lib/fs-driver.d.ts +70 -0
- package/dist/lib/fs-driver.js +364 -0
- package/dist/lib/fs-env.d.ts +30 -0
- package/dist/lib/fs-env.js +157 -0
- package/dist/lib/fs-types.d.ts +19 -0
- package/dist/lib/fs-types.js +2 -0
- package/dist/lib/hashes.d.ts +27 -0
- package/dist/lib/hashes.js +432 -0
- package/dist/lib/http-driver.d.ts +30 -0
- package/dist/lib/http-driver.js +72 -0
- package/dist/lib/http-types.d.ts +18 -0
- package/dist/lib/http-types.js +2 -0
- package/dist/lib/inflate.d.ts +6 -0
- package/dist/lib/inflate.js +284 -0
- package/dist/lib/path-types.d.ts +37 -0
- package/dist/lib/path-types.js +2 -0
- package/dist/lib/paths.d.ts +8 -0
- package/dist/lib/paths.js +106 -0
- package/dist/lib/perf-hooks-types.d.ts +18 -0
- package/dist/lib/perf-hooks-types.js +2 -0
- package/dist/lib/process-driver.d.ts +31 -0
- package/dist/lib/process-driver.js +102 -0
- package/dist/lib/punycode.d.ts +8 -0
- package/dist/lib/punycode.js +179 -0
- package/dist/lib/stream-types.d.ts +29 -0
- package/dist/lib/stream-types.js +2 -0
- package/dist/lib/timers-types.d.ts +18 -0
- package/dist/lib/timers-types.js +2 -0
- package/dist/lib/url-search-params.d.ts +26 -0
- package/dist/lib/url-search-params.js +281 -0
- package/dist/lib/url-types.d.ts +44 -0
- package/dist/lib/url-types.js +2 -0
- package/dist/lib/url-whatwg.d.ts +78 -0
- package/dist/lib/url-whatwg.js +857 -0
- package/dist/lib/zlib-types.d.ts +12 -0
- package/dist/lib/zlib-types.js +2 -0
- package/dist/module.d.ts +8 -0
- package/dist/module.js +72 -0
- package/dist/os.d.ts +80 -0
- package/dist/os.js +247 -0
- package/dist/path/posix.d.ts +2 -0
- package/dist/path/posix.js +7 -0
- package/dist/path/win32.d.ts +2 -0
- package/dist/path/win32.js +10 -0
- package/dist/path.d.ts +3 -0
- package/dist/path.js +1222 -0
- package/dist/perf_hooks.d.ts +36 -0
- package/dist/perf_hooks.js +144 -0
- package/dist/process.d.ts +69 -0
- package/dist/process.js +230 -0
- package/dist/querystring.d.ts +15 -0
- package/dist/querystring.js +209 -0
- package/dist/stream/promises.d.ts +5 -0
- package/dist/stream/promises.js +10 -0
- package/dist/stream.d.ts +84 -0
- package/dist/stream.js +875 -0
- package/dist/string_decoder.d.ts +15 -0
- package/dist/string_decoder.js +237 -0
- package/dist/timers/promises.d.ts +19 -0
- package/dist/timers/promises.js +74 -0
- package/dist/timers.d.ts +21 -0
- package/dist/timers.js +41 -0
- package/dist/tty.d.ts +9 -0
- package/dist/tty.js +25 -0
- package/dist/unsupported.d.ts +12 -0
- package/dist/unsupported.js +17 -0
- package/dist/url.d.ts +28 -0
- package/dist/url.js +486 -0
- package/dist/util.d.ts +74 -0
- package/dist/util.js +515 -0
- package/dist/zlib.d.ts +88 -0
- package/dist/zlib.js +371 -0
- package/package.json +29 -4
package/dist/stream.js
ADDED
|
@@ -0,0 +1,875 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* Node stream 模块的移植:Readable / Writable / Duplex / Transform / PassThrough,
|
|
4
|
+
* 以及 pipeline / finished(回调与 Promise 两种)。
|
|
5
|
+
*
|
|
6
|
+
* 实现取舍:
|
|
7
|
+
* - 调度用 queueMicrotask(QuickJS 不一定有 process.nextTick),语义上贴近 Node 的 nextTick;
|
|
8
|
+
* 因此事件时序与 Node 是"同类但不必逐拍相同",测试验证的是**语义**(数据、顺序、事件、
|
|
9
|
+
* 背压返回值、错误传播),不是逐拍时序。
|
|
10
|
+
* - 不实现 objectMode 的完整语义差异(只影响 highWaterMark 的计数单位),
|
|
11
|
+
* 也不实现 cork 的深度合并优化。
|
|
12
|
+
* - 未实现:stream.Duplex 的 allowHalfOpen 完整语义、compose、addAbortSignal。
|
|
13
|
+
*/
|
|
14
|
+
const EventEmitter = require("./events");
|
|
15
|
+
const buffer_1 = require("./buffer");
|
|
16
|
+
function schedule(fn) {
|
|
17
|
+
queueMicrotask(fn);
|
|
18
|
+
}
|
|
19
|
+
function isThenable(value) {
|
|
20
|
+
return value !== null && typeof value === 'object' && typeof value.then === 'function';
|
|
21
|
+
}
|
|
22
|
+
const readableStates = new WeakMap();
|
|
23
|
+
const writableStates = new WeakMap();
|
|
24
|
+
function readableStateOf(stream, options, highWaterMark) {
|
|
25
|
+
let state = readableStates.get(stream);
|
|
26
|
+
if (state === undefined) {
|
|
27
|
+
state = {
|
|
28
|
+
chunks: [],
|
|
29
|
+
length: 0,
|
|
30
|
+
flowing: null,
|
|
31
|
+
ended: false,
|
|
32
|
+
endEmitted: false,
|
|
33
|
+
reading: false,
|
|
34
|
+
destroyed: false,
|
|
35
|
+
objectMode: options !== undefined && options.objectMode === true,
|
|
36
|
+
highWaterMark: options !== undefined && typeof options.highWaterMark === 'number' ? options.highWaterMark : 16384,
|
|
37
|
+
encoding: options !== undefined && typeof options.encoding === 'string' ? options.encoding : null,
|
|
38
|
+
pipes: [],
|
|
39
|
+
scheduled: false
|
|
40
|
+
};
|
|
41
|
+
void highWaterMark;
|
|
42
|
+
readableStates.set(stream, state);
|
|
43
|
+
}
|
|
44
|
+
return state;
|
|
45
|
+
}
|
|
46
|
+
function writableStateOf(stream, options) {
|
|
47
|
+
let state = writableStates.get(stream);
|
|
48
|
+
if (state === undefined) {
|
|
49
|
+
state = {
|
|
50
|
+
queue: [],
|
|
51
|
+
length: 0,
|
|
52
|
+
writing: false,
|
|
53
|
+
corked: 0,
|
|
54
|
+
ending: false,
|
|
55
|
+
finished: false,
|
|
56
|
+
destroyed: false,
|
|
57
|
+
objectMode: options !== undefined && (options.objectMode === true || options.writableObjectMode === true),
|
|
58
|
+
highWaterMark: options !== undefined && typeof options.highWaterMark === 'number' ? options.highWaterMark : 16384,
|
|
59
|
+
needDrain: false,
|
|
60
|
+
finalizing: false
|
|
61
|
+
};
|
|
62
|
+
writableStates.set(stream, state);
|
|
63
|
+
}
|
|
64
|
+
return state;
|
|
65
|
+
}
|
|
66
|
+
function toChunk(chunk, encoding, objectMode) {
|
|
67
|
+
if (objectMode) {
|
|
68
|
+
return chunk;
|
|
69
|
+
}
|
|
70
|
+
if (typeof chunk === 'string') {
|
|
71
|
+
return buffer_1.Buffer.from(chunk, (encoding === null ? 'utf8' : encoding));
|
|
72
|
+
}
|
|
73
|
+
if (chunk instanceof Uint8Array) {
|
|
74
|
+
return chunk;
|
|
75
|
+
}
|
|
76
|
+
if (chunk instanceof ArrayBuffer) {
|
|
77
|
+
return new Uint8Array(chunk);
|
|
78
|
+
}
|
|
79
|
+
throw new TypeError('The "chunk" argument must be of type string or an instance of Buffer or Uint8Array.');
|
|
80
|
+
}
|
|
81
|
+
function decodeChunk(chunk, encoding) {
|
|
82
|
+
return encoding === null ? '' : buffer_1.Buffer.from(chunk).toString(encoding);
|
|
83
|
+
}
|
|
84
|
+
// ------------------------------------------------------------------ Readable
|
|
85
|
+
class Readable extends EventEmitter {
|
|
86
|
+
constructor(options) {
|
|
87
|
+
super();
|
|
88
|
+
this.readable = true;
|
|
89
|
+
readableStateOf(this, options);
|
|
90
|
+
if (options !== undefined) {
|
|
91
|
+
if (typeof options.read === 'function') {
|
|
92
|
+
this._read = options.read;
|
|
93
|
+
}
|
|
94
|
+
if (typeof options.destroy === 'function') {
|
|
95
|
+
this._destroy = options.destroy;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// 子类覆写
|
|
100
|
+
_read(size) {
|
|
101
|
+
void size;
|
|
102
|
+
const state = readableStateOf(this);
|
|
103
|
+
if (!state.ended) {
|
|
104
|
+
const error = new Error('The _read() method is not implemented');
|
|
105
|
+
error.code = 'ERR_METHOD_NOT_IMPLEMENTED';
|
|
106
|
+
this.destroy(error);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
_destroy(error, callback) {
|
|
110
|
+
callback(error);
|
|
111
|
+
}
|
|
112
|
+
push(chunk, encoding) {
|
|
113
|
+
const state = readableStateOf(this);
|
|
114
|
+
if (state.destroyed) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
if (chunk === null) {
|
|
118
|
+
state.ended = true;
|
|
119
|
+
scheduleReadable(this);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
const value = toChunk(chunk, encoding === undefined ? state.encoding : encoding, state.objectMode);
|
|
123
|
+
state.chunks.push(value);
|
|
124
|
+
state.length += value.length;
|
|
125
|
+
if (state.flowing === true) {
|
|
126
|
+
scheduleReadable(this);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
this.emit('readable');
|
|
130
|
+
}
|
|
131
|
+
return state.length < state.highWaterMark;
|
|
132
|
+
}
|
|
133
|
+
unshift(chunk) {
|
|
134
|
+
const state = readableStateOf(this);
|
|
135
|
+
const value = toChunk(chunk, state.encoding, state.objectMode);
|
|
136
|
+
state.chunks.unshift(value);
|
|
137
|
+
state.length += value.length;
|
|
138
|
+
}
|
|
139
|
+
read(size) {
|
|
140
|
+
const state = readableStateOf(this);
|
|
141
|
+
if (state.chunks.length === 0) {
|
|
142
|
+
if (state.ended) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
if (!state.reading) {
|
|
146
|
+
state.reading = true;
|
|
147
|
+
try {
|
|
148
|
+
this._read(state.highWaterMark);
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
state.reading = false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (state.chunks.length === 0) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const wanted = size === undefined || size === null || size <= 0 ? state.length : size;
|
|
159
|
+
if (state.objectMode) {
|
|
160
|
+
const first = state.chunks.shift();
|
|
161
|
+
state.length -= first.length;
|
|
162
|
+
return first;
|
|
163
|
+
}
|
|
164
|
+
if (wanted >= state.length) {
|
|
165
|
+
const all = state.chunks.length === 1 ? state.chunks[0] : concat(state.chunks, state.length);
|
|
166
|
+
state.chunks = [];
|
|
167
|
+
state.length = 0;
|
|
168
|
+
return all;
|
|
169
|
+
}
|
|
170
|
+
// 只取前 wanted 个字节
|
|
171
|
+
const out = new Uint8Array(wanted);
|
|
172
|
+
let filled = 0;
|
|
173
|
+
while (filled < wanted && state.chunks.length > 0) {
|
|
174
|
+
const head = state.chunks[0];
|
|
175
|
+
const take = Math.min(head.length, wanted - filled);
|
|
176
|
+
out.set(head.subarray(0, take), filled);
|
|
177
|
+
filled += take;
|
|
178
|
+
if (take === head.length) {
|
|
179
|
+
state.chunks.shift();
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
state.chunks[0] = head.subarray(take);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
state.length -= filled;
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
setEncoding(encoding) {
|
|
189
|
+
const state = readableStateOf(this);
|
|
190
|
+
state.encoding = encoding;
|
|
191
|
+
return this;
|
|
192
|
+
}
|
|
193
|
+
pause() {
|
|
194
|
+
const state = readableStateOf(this);
|
|
195
|
+
state.flowing = false;
|
|
196
|
+
this.emit('pause');
|
|
197
|
+
return this;
|
|
198
|
+
}
|
|
199
|
+
resume() {
|
|
200
|
+
const state = readableStateOf(this);
|
|
201
|
+
state.flowing = true;
|
|
202
|
+
this.emit('resume');
|
|
203
|
+
scheduleReadable(this);
|
|
204
|
+
return this;
|
|
205
|
+
}
|
|
206
|
+
isPaused() {
|
|
207
|
+
return readableStateOf(this).flowing === false;
|
|
208
|
+
}
|
|
209
|
+
pipe(destination, options) {
|
|
210
|
+
const state = readableStateOf(this);
|
|
211
|
+
const shouldEnd = options === undefined || options.end !== false;
|
|
212
|
+
state.pipes.push(destination);
|
|
213
|
+
const onData = (chunk) => {
|
|
214
|
+
if (destination.write(chunk) === false) {
|
|
215
|
+
this.pause();
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
const onDrain = () => {
|
|
219
|
+
if (state.flowing === false) {
|
|
220
|
+
this.resume();
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
const onEnd = () => {
|
|
224
|
+
destination.removeListener('drain', onDrain);
|
|
225
|
+
if (shouldEnd) {
|
|
226
|
+
destination.end();
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
const onError = (error) => {
|
|
230
|
+
destination.emit('error', error);
|
|
231
|
+
};
|
|
232
|
+
this.on('data', onData);
|
|
233
|
+
destination.on('drain', onDrain);
|
|
234
|
+
this.once('end', onEnd);
|
|
235
|
+
this.once('error', onError);
|
|
236
|
+
this.resume();
|
|
237
|
+
return destination;
|
|
238
|
+
}
|
|
239
|
+
unpipe(destination) {
|
|
240
|
+
const state = readableStateOf(this);
|
|
241
|
+
state.pipes = destination === undefined
|
|
242
|
+
? []
|
|
243
|
+
: state.pipes.filter(function (item) { return item !== destination; });
|
|
244
|
+
return this;
|
|
245
|
+
}
|
|
246
|
+
destroy(error) {
|
|
247
|
+
const state = readableStateOf(this);
|
|
248
|
+
if (state.destroyed) {
|
|
249
|
+
return this;
|
|
250
|
+
}
|
|
251
|
+
state.destroyed = true;
|
|
252
|
+
this._destroy(error === undefined ? null : error, (finalError) => {
|
|
253
|
+
if (finalError !== undefined && finalError !== null) {
|
|
254
|
+
this.emit('error', finalError);
|
|
255
|
+
}
|
|
256
|
+
this.emit('close');
|
|
257
|
+
});
|
|
258
|
+
return this;
|
|
259
|
+
}
|
|
260
|
+
on(event, listener) {
|
|
261
|
+
super.on(event, listener);
|
|
262
|
+
if (event === 'data') {
|
|
263
|
+
this.resume();
|
|
264
|
+
}
|
|
265
|
+
return this;
|
|
266
|
+
}
|
|
267
|
+
[Symbol.asyncIterator]() {
|
|
268
|
+
const stream = this;
|
|
269
|
+
const queue = [];
|
|
270
|
+
let done = false;
|
|
271
|
+
let failure = null;
|
|
272
|
+
let waiting = null;
|
|
273
|
+
const flush = () => {
|
|
274
|
+
if (waiting === null) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const resolve = waiting;
|
|
278
|
+
if (queue.length > 0) {
|
|
279
|
+
waiting = null;
|
|
280
|
+
resolve({ value: queue.shift(), done: false });
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (failure !== null) {
|
|
284
|
+
waiting = null;
|
|
285
|
+
const error = failure;
|
|
286
|
+
failure = null;
|
|
287
|
+
resolve(Promise.reject(error));
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
if (done) {
|
|
291
|
+
waiting = null;
|
|
292
|
+
resolve({ value: undefined, done: true });
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
stream.on('data', function (chunk) {
|
|
296
|
+
queue.push(chunk);
|
|
297
|
+
flush();
|
|
298
|
+
});
|
|
299
|
+
stream.on('end', function () {
|
|
300
|
+
done = true;
|
|
301
|
+
flush();
|
|
302
|
+
});
|
|
303
|
+
stream.on('error', function (error) {
|
|
304
|
+
failure = error;
|
|
305
|
+
flush();
|
|
306
|
+
});
|
|
307
|
+
return {
|
|
308
|
+
next() {
|
|
309
|
+
if (queue.length > 0) {
|
|
310
|
+
return Promise.resolve({ value: queue.shift(), done: false });
|
|
311
|
+
}
|
|
312
|
+
if (failure !== null) {
|
|
313
|
+
const error = failure;
|
|
314
|
+
failure = null;
|
|
315
|
+
return Promise.reject(error);
|
|
316
|
+
}
|
|
317
|
+
if (done) {
|
|
318
|
+
return Promise.resolve({ value: undefined, done: true });
|
|
319
|
+
}
|
|
320
|
+
return new Promise(function (resolve) {
|
|
321
|
+
waiting = resolve;
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
static from(iterable) {
|
|
327
|
+
const source = iterable;
|
|
328
|
+
const isAsync = typeof source[Symbol.asyncIterator] === 'function';
|
|
329
|
+
const iterator = isAsync
|
|
330
|
+
? source[Symbol.asyncIterator].call(source)
|
|
331
|
+
: source[Symbol.iterator].call(source);
|
|
332
|
+
let busy = false;
|
|
333
|
+
const stream = new Readable({
|
|
334
|
+
objectMode: true,
|
|
335
|
+
read: function () {
|
|
336
|
+
if (busy) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
busy = true;
|
|
340
|
+
const step = () => {
|
|
341
|
+
const result = isAsync
|
|
342
|
+
? iterator.next()
|
|
343
|
+
: iterator.next();
|
|
344
|
+
if (isThenable(result)) {
|
|
345
|
+
result.then(function (resolved) {
|
|
346
|
+
busy = false;
|
|
347
|
+
if (resolved.done === true) {
|
|
348
|
+
stream.push(null);
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
stream.push(resolved.value);
|
|
352
|
+
}
|
|
353
|
+
}, function (error) {
|
|
354
|
+
busy = false;
|
|
355
|
+
stream.destroy(error);
|
|
356
|
+
});
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const value = result;
|
|
360
|
+
busy = false;
|
|
361
|
+
if (value.done === true) {
|
|
362
|
+
stream.push(null);
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
stream.push(value.value);
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
step();
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
return stream;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function concat(chunks, total) {
|
|
375
|
+
const out = new Uint8Array(total);
|
|
376
|
+
let offset = 0;
|
|
377
|
+
for (let i = 0; i < chunks.length; i += 1) {
|
|
378
|
+
out.set(chunks[i], offset);
|
|
379
|
+
offset += chunks[i].length;
|
|
380
|
+
}
|
|
381
|
+
return out;
|
|
382
|
+
}
|
|
383
|
+
/** 把缓冲里的数据按需推给 'data' 监听器,并在结束时补一次 'end'。 */
|
|
384
|
+
function scheduleReadable(stream) {
|
|
385
|
+
const state = readableStateOf(stream);
|
|
386
|
+
if (state.scheduled || state.destroyed) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
state.scheduled = true;
|
|
390
|
+
schedule(function () {
|
|
391
|
+
state.scheduled = false;
|
|
392
|
+
flow(stream);
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
function takeBufferedChunk(state) {
|
|
396
|
+
if (state.chunks.length === 0) {
|
|
397
|
+
return undefined;
|
|
398
|
+
}
|
|
399
|
+
const chunk = state.chunks.shift();
|
|
400
|
+
if (chunk === undefined) {
|
|
401
|
+
return undefined;
|
|
402
|
+
}
|
|
403
|
+
state.length -= typeof chunk.length === 'number' ? chunk.length : 1;
|
|
404
|
+
return chunk;
|
|
405
|
+
}
|
|
406
|
+
function flow(stream) {
|
|
407
|
+
const state = readableStateOf(stream);
|
|
408
|
+
if (state.destroyed) {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
while (state.flowing === true && state.chunks.length > 0) {
|
|
412
|
+
// 一次只发一块:Node 是每个 push() 对应一次 'data',不是把缓冲拼起来发
|
|
413
|
+
const chunk = takeBufferedChunk(state);
|
|
414
|
+
if (chunk === undefined) {
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
if (state.encoding !== null && !state.objectMode) {
|
|
418
|
+
stream.emit('data', decodeChunk(chunk, state.encoding));
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
stream.emit('data', chunk);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (state.chunks.length === 0 && !state.ended && state.flowing === true) {
|
|
425
|
+
if (!state.reading) {
|
|
426
|
+
state.reading = true;
|
|
427
|
+
try {
|
|
428
|
+
stream._read(state.highWaterMark);
|
|
429
|
+
}
|
|
430
|
+
finally {
|
|
431
|
+
state.reading = false;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if (state.chunks.length > 0) {
|
|
435
|
+
scheduleReadable(stream);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (state.ended && state.chunks.length === 0 && !state.endEmitted) {
|
|
440
|
+
if (state.flowing !== false) {
|
|
441
|
+
state.endEmitted = true;
|
|
442
|
+
stream.emit('end');
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
// ------------------------------------------------------------------ Writable
|
|
447
|
+
const writableMethods = {
|
|
448
|
+
write(chunk, encoding, callback) {
|
|
449
|
+
const stream = this;
|
|
450
|
+
const state = writableStateOf(stream);
|
|
451
|
+
let chunkEncoding = state.objectMode ? null : 'utf8';
|
|
452
|
+
let done = function () { };
|
|
453
|
+
if (typeof encoding === 'function') {
|
|
454
|
+
done = encoding;
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
if (typeof encoding === 'string') {
|
|
458
|
+
chunkEncoding = encoding;
|
|
459
|
+
}
|
|
460
|
+
if (typeof callback === 'function') {
|
|
461
|
+
done = callback;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (state.destroyed) {
|
|
465
|
+
const error = new Error('Cannot call write after a stream was destroyed');
|
|
466
|
+
error.code = 'ERR_STREAM_DESTROYED';
|
|
467
|
+
schedule(function () { done(error); });
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
const bytes = toChunk(chunk, chunkEncoding, state.objectMode);
|
|
471
|
+
state.queue.push({ chunk: bytes, callback: done });
|
|
472
|
+
state.length += bytes.length;
|
|
473
|
+
processWritableQueue(stream);
|
|
474
|
+
const canContinue = state.length < state.highWaterMark;
|
|
475
|
+
state.needDrain = !canContinue;
|
|
476
|
+
return canContinue;
|
|
477
|
+
},
|
|
478
|
+
end(chunk, encoding, callback) {
|
|
479
|
+
const stream = this;
|
|
480
|
+
const state = writableStateOf(stream);
|
|
481
|
+
let done;
|
|
482
|
+
if (typeof chunk === 'function') {
|
|
483
|
+
done = chunk;
|
|
484
|
+
}
|
|
485
|
+
else if (typeof encoding === 'function') {
|
|
486
|
+
done = encoding;
|
|
487
|
+
}
|
|
488
|
+
else if (typeof callback === 'function') {
|
|
489
|
+
done = callback;
|
|
490
|
+
}
|
|
491
|
+
if (typeof chunk !== 'function' && chunk !== undefined && chunk !== null) {
|
|
492
|
+
this.write(chunk, encoding);
|
|
493
|
+
}
|
|
494
|
+
state.ending = true;
|
|
495
|
+
if (done !== undefined) {
|
|
496
|
+
stream.once('finish', done);
|
|
497
|
+
}
|
|
498
|
+
processWritableQueue(stream);
|
|
499
|
+
return this;
|
|
500
|
+
},
|
|
501
|
+
cork() {
|
|
502
|
+
const state = writableStateOf(this);
|
|
503
|
+
state.corked += 1;
|
|
504
|
+
},
|
|
505
|
+
uncork() {
|
|
506
|
+
const state = writableStateOf(this);
|
|
507
|
+
if (state.corked > 0) {
|
|
508
|
+
state.corked -= 1;
|
|
509
|
+
}
|
|
510
|
+
processWritableQueue(this);
|
|
511
|
+
},
|
|
512
|
+
setDefaultEncoding(encoding) {
|
|
513
|
+
void encoding;
|
|
514
|
+
return this;
|
|
515
|
+
},
|
|
516
|
+
destroy(error) {
|
|
517
|
+
const state = writableStateOf(this);
|
|
518
|
+
const stream = this;
|
|
519
|
+
if (state.destroyed) {
|
|
520
|
+
return this;
|
|
521
|
+
}
|
|
522
|
+
state.destroyed = true;
|
|
523
|
+
const target = this;
|
|
524
|
+
const finish = (finalError) => {
|
|
525
|
+
if (finalError !== undefined && finalError !== null) {
|
|
526
|
+
stream.emit('error', finalError);
|
|
527
|
+
}
|
|
528
|
+
stream.emit('close');
|
|
529
|
+
};
|
|
530
|
+
if (typeof target._destroy === 'function') {
|
|
531
|
+
target._destroy(error === undefined ? null : error, finish);
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
finish(error === undefined ? null : error);
|
|
535
|
+
}
|
|
536
|
+
return this;
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
/**
|
|
540
|
+
* 队列排空后结束可写侧:先跑 _final(Transform 就是靠它在 _flush 后 push(null)),
|
|
541
|
+
* 再发 'finish'。漏掉 _final 会让 Transform 的可读侧永远不结束 —— 这个坑踩过。
|
|
542
|
+
*/
|
|
543
|
+
function finalizeWritable(stream) {
|
|
544
|
+
const state = writableStateOf(stream);
|
|
545
|
+
const target = stream;
|
|
546
|
+
const emitFinish = function (error) {
|
|
547
|
+
state.finalizing = false;
|
|
548
|
+
if (error !== undefined && error !== null) {
|
|
549
|
+
stream.emit('error', error);
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
state.finished = true;
|
|
553
|
+
stream.emit('finish');
|
|
554
|
+
};
|
|
555
|
+
if (typeof target._final !== 'function') {
|
|
556
|
+
state.finalizing = true;
|
|
557
|
+
emitFinish(null);
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
state.finalizing = true;
|
|
561
|
+
target._final(emitFinish);
|
|
562
|
+
}
|
|
563
|
+
function processWritableQueue(stream) {
|
|
564
|
+
const state = writableStateOf(stream);
|
|
565
|
+
if (state.writing || state.corked > 0) {
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
const next = state.queue.shift();
|
|
569
|
+
if (next === undefined) {
|
|
570
|
+
if (state.ending && !state.finished && !state.finalizing) {
|
|
571
|
+
finalizeWritable(stream);
|
|
572
|
+
}
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
state.writing = true;
|
|
576
|
+
// 在途的这一块也要计入 length —— Node 的 write() 返回值与 drain 时机都依赖这点
|
|
577
|
+
const target = stream;
|
|
578
|
+
const afterWrite = function (error) {
|
|
579
|
+
state.writing = false;
|
|
580
|
+
state.length -= next.chunk.length;
|
|
581
|
+
if (error !== undefined && error !== null) {
|
|
582
|
+
stream.emit('error', error);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
next.callback(null);
|
|
586
|
+
if (state.needDrain && state.length < state.highWaterMark) {
|
|
587
|
+
state.needDrain = false;
|
|
588
|
+
stream.emit('drain');
|
|
589
|
+
}
|
|
590
|
+
processWritableQueue(stream);
|
|
591
|
+
};
|
|
592
|
+
if (typeof target._write !== 'function') {
|
|
593
|
+
const error = new Error('The _write() method is not implemented');
|
|
594
|
+
error.code = 'ERR_METHOD_NOT_IMPLEMENTED';
|
|
595
|
+
stream.emit('error', error);
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
target._write(next.chunk, 'buffer', afterWrite);
|
|
599
|
+
}
|
|
600
|
+
class Writable extends EventEmitter {
|
|
601
|
+
constructor(options) {
|
|
602
|
+
super();
|
|
603
|
+
this.writable = true;
|
|
604
|
+
writableStateOf(this, options);
|
|
605
|
+
if (options !== undefined) {
|
|
606
|
+
if (typeof options.write === 'function') {
|
|
607
|
+
this._write = options.write;
|
|
608
|
+
}
|
|
609
|
+
if (typeof options.final === 'function') {
|
|
610
|
+
this._final = options.final;
|
|
611
|
+
}
|
|
612
|
+
if (typeof options.destroy === 'function') {
|
|
613
|
+
this._destroy = options.destroy;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
function applyWritable(target) {
|
|
619
|
+
const source = writableMethods;
|
|
620
|
+
Object.keys(source).forEach(function (key) {
|
|
621
|
+
target[key] = source[key];
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
applyWritable(Writable.prototype);
|
|
625
|
+
// ------------------------------------------------------------------ Duplex
|
|
626
|
+
class Duplex extends Readable {
|
|
627
|
+
constructor(options) {
|
|
628
|
+
super(options);
|
|
629
|
+
this.writable = true;
|
|
630
|
+
writableStateOf(this, options);
|
|
631
|
+
if (options !== undefined) {
|
|
632
|
+
if (typeof options.write === 'function') {
|
|
633
|
+
this._write = options.write;
|
|
634
|
+
}
|
|
635
|
+
if (typeof options.final === 'function') {
|
|
636
|
+
this._final = options.final;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
applyWritable(Duplex.prototype);
|
|
642
|
+
// ------------------------------------------------------------------ Transform
|
|
643
|
+
class Transform extends Duplex {
|
|
644
|
+
constructor(options) {
|
|
645
|
+
super(options);
|
|
646
|
+
if (options !== undefined) {
|
|
647
|
+
if (typeof options.transform === 'function') {
|
|
648
|
+
this._transform = options.transform;
|
|
649
|
+
}
|
|
650
|
+
if (typeof options.flush === 'function') {
|
|
651
|
+
this._flush = options.flush;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
_transform(chunk, encoding, callback) {
|
|
656
|
+
void chunk;
|
|
657
|
+
void encoding;
|
|
658
|
+
const error = new Error('The _transform() method is not implemented');
|
|
659
|
+
error.code = 'ERR_METHOD_NOT_IMPLEMENTED';
|
|
660
|
+
callback(error);
|
|
661
|
+
}
|
|
662
|
+
/** Transform 的可读侧由 _write 推入,不去主动拉数据,因此这里是空的。 */
|
|
663
|
+
_read(size) {
|
|
664
|
+
void size;
|
|
665
|
+
}
|
|
666
|
+
_flush(callback) {
|
|
667
|
+
callback();
|
|
668
|
+
}
|
|
669
|
+
_write(chunk, encoding, callback) {
|
|
670
|
+
const self = this;
|
|
671
|
+
self._transform(chunk, encoding, function (error, data) {
|
|
672
|
+
if (error !== undefined && error !== null) {
|
|
673
|
+
callback(error);
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
if (data !== undefined && data !== null) {
|
|
677
|
+
self.push(data);
|
|
678
|
+
}
|
|
679
|
+
callback(null);
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
_final(callback) {
|
|
683
|
+
const self = this;
|
|
684
|
+
self._flush(function (error) {
|
|
685
|
+
if (error !== undefined && error !== null) {
|
|
686
|
+
callback(error);
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
self.push(null);
|
|
690
|
+
callback(null);
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
// ------------------------------------------------------------------ PassThrough
|
|
695
|
+
class PassThrough extends Transform {
|
|
696
|
+
_transform(chunk, encoding, callback) {
|
|
697
|
+
void encoding;
|
|
698
|
+
callback(null, chunk);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
// ------------------------------------------------------------------ pipeline / finished
|
|
702
|
+
function finished(stream, callback) {
|
|
703
|
+
const target = stream;
|
|
704
|
+
let done = false;
|
|
705
|
+
const onError = (error) => {
|
|
706
|
+
if (done) {
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
done = true;
|
|
710
|
+
cleanup();
|
|
711
|
+
callback(error);
|
|
712
|
+
};
|
|
713
|
+
const onEnd = () => {
|
|
714
|
+
if (done) {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
done = true;
|
|
718
|
+
cleanup();
|
|
719
|
+
callback(null);
|
|
720
|
+
};
|
|
721
|
+
const cleanup = () => {
|
|
722
|
+
target.removeListener('error', onError);
|
|
723
|
+
target.removeListener('end', onEnd);
|
|
724
|
+
target.removeListener('finish', onEnd);
|
|
725
|
+
target.removeListener('close', onEnd);
|
|
726
|
+
};
|
|
727
|
+
target.on('error', onError);
|
|
728
|
+
target.once('end', onEnd);
|
|
729
|
+
target.once('finish', onEnd);
|
|
730
|
+
target.once('close', onEnd);
|
|
731
|
+
return cleanup;
|
|
732
|
+
}
|
|
733
|
+
function isReadableStream(value) {
|
|
734
|
+
return value !== null && typeof value === 'object' && typeof value.on === 'function' &&
|
|
735
|
+
typeof value.read === 'function';
|
|
736
|
+
}
|
|
737
|
+
function isWritableStream(value) {
|
|
738
|
+
return value !== null && typeof value === 'object' && typeof value.write === 'function' &&
|
|
739
|
+
typeof value.end === 'function';
|
|
740
|
+
}
|
|
741
|
+
function pipeline(...args) {
|
|
742
|
+
const callback = args.length > 0 && typeof args[args.length - 1] === 'function'
|
|
743
|
+
? args.pop()
|
|
744
|
+
: undefined;
|
|
745
|
+
if (callback === undefined) {
|
|
746
|
+
// 与 Node 一致:stream.pipeline 必须给回调,Promise 形式走 stream/promises
|
|
747
|
+
const error = new TypeError('The "streams[stream.length - 1]" property must be of type function.');
|
|
748
|
+
error.code = 'ERR_INVALID_ARG_TYPE';
|
|
749
|
+
throw error;
|
|
750
|
+
}
|
|
751
|
+
const streams = args;
|
|
752
|
+
const transforms = streams.filter(function (item) {
|
|
753
|
+
return typeof item !== 'function';
|
|
754
|
+
});
|
|
755
|
+
if (transforms.length === 0) {
|
|
756
|
+
if (callback !== undefined) {
|
|
757
|
+
schedule(function () { callback(null); });
|
|
758
|
+
return undefined;
|
|
759
|
+
}
|
|
760
|
+
return Promise.resolve();
|
|
761
|
+
}
|
|
762
|
+
const last = transforms[transforms.length - 1];
|
|
763
|
+
const failures = [];
|
|
764
|
+
const promise = new Promise(function (resolve, reject) {
|
|
765
|
+
let settled = false;
|
|
766
|
+
const fail = (error) => {
|
|
767
|
+
if (settled) {
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
settled = true;
|
|
771
|
+
const value = error;
|
|
772
|
+
if (callback !== undefined) {
|
|
773
|
+
callback(value);
|
|
774
|
+
resolve();
|
|
775
|
+
}
|
|
776
|
+
else {
|
|
777
|
+
reject(value);
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
if (callback !== undefined) {
|
|
781
|
+
finished(last, function (error) {
|
|
782
|
+
if (error !== undefined && error !== null) {
|
|
783
|
+
fail(error);
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
if (settled) {
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
settled = true;
|
|
790
|
+
callback(null);
|
|
791
|
+
resolve();
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
else {
|
|
795
|
+
finished(last, function (error) {
|
|
796
|
+
if (error !== undefined && error !== null) {
|
|
797
|
+
fail(error);
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
if (!settled) {
|
|
801
|
+
settled = true;
|
|
802
|
+
resolve();
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
for (let i = 0; i < transforms.length; i += 1) {
|
|
807
|
+
const current = transforms[i];
|
|
808
|
+
current.on('error', fail);
|
|
809
|
+
if (i + 1 < transforms.length) {
|
|
810
|
+
transforms[i].pipe(transforms[i + 1]);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
void failures;
|
|
814
|
+
});
|
|
815
|
+
promise.catch(function (error) { callback(error); });
|
|
816
|
+
return last;
|
|
817
|
+
}
|
|
818
|
+
/** stream/promises 用的 Promise 版:不走 pipeline 的回调校验。 */
|
|
819
|
+
function pipelinePromise(...streams) {
|
|
820
|
+
const list = streams;
|
|
821
|
+
return new Promise(function (resolve, reject) {
|
|
822
|
+
if (list.length === 0) {
|
|
823
|
+
resolve();
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
let settled = false;
|
|
827
|
+
// 参数收成 unknown:既能喂给 finished,也能直接当 EventEmitter 的错误监听器
|
|
828
|
+
const settle = function (error) {
|
|
829
|
+
if (settled) {
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
settled = true;
|
|
833
|
+
if (error !== undefined && error !== null) {
|
|
834
|
+
reject(error);
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
resolve();
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
finished(list[list.length - 1], settle);
|
|
841
|
+
for (let i = 0; i < list.length; i += 1) {
|
|
842
|
+
list[i].on('error', settle);
|
|
843
|
+
if (i + 1 < list.length) {
|
|
844
|
+
list[i].pipe(list[i + 1]);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
function finishedPromise(stream) {
|
|
850
|
+
return new Promise(function (resolve, reject) {
|
|
851
|
+
finished(stream, function (error) {
|
|
852
|
+
if (error !== undefined && error !== null) {
|
|
853
|
+
reject(error);
|
|
854
|
+
}
|
|
855
|
+
else {
|
|
856
|
+
resolve();
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
// ------------------------------------------------------------------ 导出
|
|
862
|
+
class Stream extends EventEmitter {
|
|
863
|
+
}
|
|
864
|
+
Stream.Readable = Readable;
|
|
865
|
+
Stream.Writable = Writable;
|
|
866
|
+
Stream.Duplex = Duplex;
|
|
867
|
+
Stream.Transform = Transform;
|
|
868
|
+
Stream.PassThrough = PassThrough;
|
|
869
|
+
Stream.Stream = Stream;
|
|
870
|
+
Stream.pipeline = pipeline;
|
|
871
|
+
Stream.finished = finished;
|
|
872
|
+
Stream.promises = { pipeline: pipelinePromise, finished: finishedPromise };
|
|
873
|
+
Stream.isReadable = isReadableStream;
|
|
874
|
+
Stream.isWritable = isWritableStream;
|
|
875
|
+
module.exports = Stream;
|