@promistream/from-node-stream 0.1.7 → 0.1.9
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/package.json +11 -6
- package/src/index.js +27 -4
- package/test.js +123 -2
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promistream/from-node-stream",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"main": "src/index.js",
|
|
5
|
-
"repository":
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://codeberg.org/promistream/from-node-stream.git"
|
|
8
|
+
},
|
|
6
9
|
"author": "Sven Slootweg <admin@cryto.net>",
|
|
7
10
|
"license": "WTFPL OR CC0-1.0",
|
|
8
11
|
"dependencies": {
|
|
@@ -15,7 +18,7 @@
|
|
|
15
18
|
"@promistream/propagate-abort": "^0.1.7",
|
|
16
19
|
"@promistream/propagate-peek": "^0.1.2",
|
|
17
20
|
"@promistream/simple-sink": "^0.3.5",
|
|
18
|
-
"@promistream/simple-source": "^0.1.
|
|
21
|
+
"@promistream/simple-source": "^0.1.11",
|
|
19
22
|
"debug-instanced": "^1.0.0",
|
|
20
23
|
"push-buffer": "^1.5.7",
|
|
21
24
|
"single-concurrent": "^1.0.0",
|
|
@@ -23,15 +26,17 @@
|
|
|
23
26
|
},
|
|
24
27
|
"devDependencies": {
|
|
25
28
|
"@promistream/buffered-map": "^0.1.1",
|
|
26
|
-
"@promistream/collect": "^0.1.
|
|
29
|
+
"@promistream/collect": "^0.1.4",
|
|
27
30
|
"@promistream/debug": "^0.1.2",
|
|
28
31
|
"@promistream/from-iterable": "^0.1.1",
|
|
29
32
|
"@promistream/map": "^0.1.10",
|
|
30
|
-
"@promistream/parallelize": "^0.1.
|
|
33
|
+
"@promistream/parallelize": "^0.1.10",
|
|
31
34
|
"@promistream/pipe": "^0.1.8",
|
|
32
35
|
"@promistream/range-numbers": "^0.1.2",
|
|
36
|
+
"@promistream/spy": "^0.1.0",
|
|
37
|
+
"@promistream/test-abort": "^0.1.0",
|
|
33
38
|
"bluebird": "^3.7.2",
|
|
34
39
|
"p-defer": "^4.0.1",
|
|
35
40
|
"through2": "^4.0.2"
|
|
36
41
|
}
|
|
37
|
-
}
|
|
42
|
+
}
|
package/src/index.js
CHANGED
|
@@ -105,8 +105,12 @@ function convertFromReadable(stream) {
|
|
|
105
105
|
onRequest: () => {
|
|
106
106
|
return read();
|
|
107
107
|
},
|
|
108
|
-
onAbort: () => {
|
|
109
|
-
|
|
108
|
+
onAbort: (reason) => {
|
|
109
|
+
if (reason === true) {
|
|
110
|
+
return abort();
|
|
111
|
+
} else {
|
|
112
|
+
return abort(reason);
|
|
113
|
+
}
|
|
110
114
|
}
|
|
111
115
|
});
|
|
112
116
|
}
|
|
@@ -119,11 +123,26 @@ function isWritableStreamEnded(stream) {
|
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
function wireWritable(debug, stream) {
|
|
122
|
-
let errorEncountered;
|
|
126
|
+
let errorEncountered, lastAbort;
|
|
127
|
+
let aborted = false;
|
|
128
|
+
|
|
129
|
+
function abortPromistream(reason) {
|
|
130
|
+
/* If an abort/end of the underlying Node stream occurred for a reason that's entirely unrelated to our current pipeline - most notably, this happens if the error originates from the *read* side of a non-transform Duplex stream - then we want to make sure that we tear down the Promistream pipeline on this side too. */
|
|
131
|
+
if (lastAbort != null && !aborted) {
|
|
132
|
+
aborted = true;
|
|
133
|
+
lastAbort(reason);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
123
136
|
|
|
124
137
|
stream.on("error", (error) => {
|
|
125
138
|
debug("Logging writable interface error:", error);
|
|
126
139
|
errorEncountered = ensureValidError(error);
|
|
140
|
+
abortPromistream(error);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
stream.on("end", () => {
|
|
144
|
+
debug("Reached end of underlying Writable stream");
|
|
145
|
+
abortPromistream(true);
|
|
127
146
|
});
|
|
128
147
|
|
|
129
148
|
async function maybeWaitForDrain() {
|
|
@@ -145,6 +164,7 @@ function wireWritable(debug, stream) {
|
|
|
145
164
|
await maybeWaitForDrain();
|
|
146
165
|
if (!isStdioStream(stream) && !isWritableStreamEnded(stream)) {
|
|
147
166
|
debug("Calling .destroy on Writable stream");
|
|
167
|
+
aborted = true;
|
|
148
168
|
stream.destroy(error);
|
|
149
169
|
}
|
|
150
170
|
},
|
|
@@ -152,10 +172,13 @@ function wireWritable(debug, stream) {
|
|
|
152
172
|
await maybeWaitForDrain();
|
|
153
173
|
if (!isStdioStream(stream) && !isWritableStreamEnded(stream)) {
|
|
154
174
|
debug("Calling .end on Writable stream");
|
|
175
|
+
aborted = true;
|
|
155
176
|
stream.end();
|
|
156
177
|
}
|
|
157
178
|
},
|
|
158
179
|
tryWrite: async function (value, abort) {
|
|
180
|
+
lastAbort = abort;
|
|
181
|
+
|
|
159
182
|
if (isWritableStreamEnded(stream)) {
|
|
160
183
|
debug("Could not write because underlying stream has ended");
|
|
161
184
|
if (errorEncountered != null) {
|
|
@@ -223,7 +246,7 @@ function convertFromTransform(stream) {
|
|
|
223
246
|
// This is essentially a 'background process' of sorts, with manual error handling wiring.
|
|
224
247
|
try {
|
|
225
248
|
while (endMarker == null) {
|
|
226
|
-
await tryWrite(await lastSource.read());
|
|
249
|
+
await tryWrite(await lastSource.read(), lastSource.abort);
|
|
227
250
|
}
|
|
228
251
|
} catch (error) {
|
|
229
252
|
if (endMarker == null && (isEndOfStream(error) || isAborted(error))) { // FIXME: isExhausted?
|
package/test.js
CHANGED
|
@@ -13,7 +13,10 @@ const fromIterable = require("@promistream/from-iterable");
|
|
|
13
13
|
const fromNodeStream = require(".");
|
|
14
14
|
const collect = require("@promistream/collect");
|
|
15
15
|
const parallelize = require("@promistream/parallelize");
|
|
16
|
+
const testAbort = require("@promistream/test-abort");
|
|
16
17
|
const debug = require("@promistream/debug");
|
|
18
|
+
const { once } = require("node:events");
|
|
19
|
+
const spy = require("@promistream/spy");
|
|
17
20
|
|
|
18
21
|
test("readable stream", async () => {
|
|
19
22
|
let testFile = path.join(__dirname, "pnpm-lock.yaml"); // Big-ish file, so we're likely to read in multiple chunks
|
|
@@ -24,7 +27,38 @@ test("readable stream", async () => {
|
|
|
24
27
|
collect()
|
|
25
28
|
]).read();
|
|
26
29
|
|
|
27
|
-
assert.deepStrictEqual(result, [ expected ]);
|
|
30
|
+
assert.deepStrictEqual(result, [ expected ]);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("readable stream with error abort", { timeout: 500 }, async () => {
|
|
34
|
+
let testFile = path.join(__dirname, "pnpm-lock.yaml");
|
|
35
|
+
let nodeStream = fs.createReadStream(testFile);
|
|
36
|
+
|
|
37
|
+
let errorFromNodeStreamPromise = once(nodeStream, "error");
|
|
38
|
+
|
|
39
|
+
assert.rejects(async () => {
|
|
40
|
+
let result = await pipe([
|
|
41
|
+
fromNodeStream.fromReadable(nodeStream),
|
|
42
|
+
testAbort(0, new Error("sample error")),
|
|
43
|
+
collect()
|
|
44
|
+
]).read();
|
|
45
|
+
}, { message: "sample error" });
|
|
46
|
+
|
|
47
|
+
let [ errorFromNodeStream ] = await errorFromNodeStreamPromise;
|
|
48
|
+
assert.strictEqual(errorFromNodeStream.message, "sample error");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
test("readable stream with happy abort", { timeout: 500 }, async () => {
|
|
53
|
+
let testFile = path.join(__dirname, "pnpm-lock.yaml");
|
|
54
|
+
|
|
55
|
+
let result = await pipe([
|
|
56
|
+
fromNodeStream.fromReadable(fs.createReadStream(testFile)),
|
|
57
|
+
testAbort(0, true),
|
|
58
|
+
collect()
|
|
59
|
+
]).read();
|
|
60
|
+
|
|
61
|
+
assert.deepStrictEqual(result, []);
|
|
28
62
|
});
|
|
29
63
|
|
|
30
64
|
test("writable stream", async () => {
|
|
@@ -52,6 +86,34 @@ test("writable stream", async () => {
|
|
|
52
86
|
assert.strictEqual(endReached, true);
|
|
53
87
|
});
|
|
54
88
|
|
|
89
|
+
test("writable stream with error abort", async () => {
|
|
90
|
+
let buffer = Buffer.alloc(400000);
|
|
91
|
+
|
|
92
|
+
let writableBuffer = Buffer.alloc(0);
|
|
93
|
+
let endReached = false;
|
|
94
|
+
let nodeWritable = new stream.Writable({
|
|
95
|
+
write(chunk, _encoding, callback) {
|
|
96
|
+
writableBuffer = Buffer.concat([ writableBuffer, chunk ]);
|
|
97
|
+
callback();
|
|
98
|
+
},
|
|
99
|
+
final(callback) {
|
|
100
|
+
endReached = true;
|
|
101
|
+
callback();
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
await assert.rejects(async () => {
|
|
106
|
+
await pipe([
|
|
107
|
+
fromIterable([ buffer, buffer, buffer ]),
|
|
108
|
+
testAbort(0, new Error("sample error")),
|
|
109
|
+
fromNodeStream(nodeWritable)
|
|
110
|
+
]).read();
|
|
111
|
+
}, { message: "sample error" });
|
|
112
|
+
|
|
113
|
+
assert.strictEqual(writableBuffer.length, 0);
|
|
114
|
+
assert.strictEqual(endReached, false);
|
|
115
|
+
});
|
|
116
|
+
|
|
55
117
|
test("transform stream", async () => {
|
|
56
118
|
let result = await pipe([
|
|
57
119
|
fromIterable([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]),
|
|
@@ -73,7 +135,7 @@ test("transform stream", async () => {
|
|
|
73
135
|
});
|
|
74
136
|
|
|
75
137
|
test("transform stream with abort", async () => {
|
|
76
|
-
assert.rejects(async () => {
|
|
138
|
+
await assert.rejects(async () => {
|
|
77
139
|
let pipeline = pipe([
|
|
78
140
|
fromIterable([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]),
|
|
79
141
|
fromNodeStream(new stream.Transform({
|
|
@@ -116,6 +178,18 @@ test("transform stream, parallel", async () => {
|
|
|
116
178
|
});
|
|
117
179
|
|
|
118
180
|
/* FIXME: Do network socket test with both happy abort and error abort */
|
|
181
|
+
/* TODO: Flaky test behaviour was observed for the below test:
|
|
182
|
+
|
|
183
|
+
✖ network socket (9.891608ms)
|
|
184
|
+
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
|
|
185
|
+
+ actual - expected
|
|
186
|
+
|
|
187
|
+
+ Buffer(0) [Uint8Array] []
|
|
188
|
+
- Buffer(2) [Uint8Array] [
|
|
189
|
+
- 99,
|
|
190
|
+
- 100
|
|
191
|
+
- ]
|
|
192
|
+
*/
|
|
119
193
|
test("network socket", async () => {
|
|
120
194
|
let { resolve, promise } = pDefer();
|
|
121
195
|
|
|
@@ -157,3 +231,50 @@ test("network socket", async () => {
|
|
|
157
231
|
server.close();
|
|
158
232
|
}
|
|
159
233
|
});
|
|
234
|
+
|
|
235
|
+
async function wait10() {
|
|
236
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
test("network socket with error abort", { timeout: 500 }, async () => {
|
|
240
|
+
let { resolve, promise } = pDefer();
|
|
241
|
+
|
|
242
|
+
let server = net.createServer((socket) => {
|
|
243
|
+
let serverRead = fromNodeStream.fromReadable(socket);
|
|
244
|
+
let serverWrite = fromNodeStream.fromWritable(socket);
|
|
245
|
+
resolve({ serverRead, serverWrite });
|
|
246
|
+
});
|
|
247
|
+
server.listen(14292);
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
let client = net.connect({ host: "localhost", port: 14292 });
|
|
251
|
+
let clientRead = fromNodeStream.fromReadable(client);
|
|
252
|
+
let clientWrite = fromNodeStream.fromWritable(client);
|
|
253
|
+
let { serverRead, serverWrite } = await promise;
|
|
254
|
+
|
|
255
|
+
let serverPipe = pipe([ serverRead, collect() ]);
|
|
256
|
+
let serverWritePipe = pipe([
|
|
257
|
+
fromIterable([ Buffer.from("c"), Buffer.from("d") ]),
|
|
258
|
+
testAbort(0, new Error("sample error")),
|
|
259
|
+
serverWrite
|
|
260
|
+
]);
|
|
261
|
+
|
|
262
|
+
let clientPipe = pipe([ clientRead, collect() ]);
|
|
263
|
+
let clientWritePipe = pipe([
|
|
264
|
+
fromIterable([ Buffer.from("a"), Buffer.from("b") ]),
|
|
265
|
+
/* Give the client side some time to notice that the server side closed the connection */
|
|
266
|
+
spy(async(value) => { await wait10(); return value; }),
|
|
267
|
+
clientWrite
|
|
268
|
+
]);
|
|
269
|
+
|
|
270
|
+
let results = await Promise.all([
|
|
271
|
+
assert.rejects(() => serverPipe.read(), { message: "sample error" }),
|
|
272
|
+
assert.rejects(() => serverWritePipe.read(), { message: "sample error" }),
|
|
273
|
+
/* The other side of the connection closes normally, like it would for any disconnection */
|
|
274
|
+
clientPipe.read(),
|
|
275
|
+
clientWritePipe.read(),
|
|
276
|
+
]);
|
|
277
|
+
} finally {
|
|
278
|
+
server.close();
|
|
279
|
+
}
|
|
280
|
+
});
|