stream-demux 9.0.0 → 9.0.1
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/index.js +12 -76
- package/package.json +3 -3
- package/stream-consumer.js +172 -0
- package/test/test.js +1 -0
package/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const WritableConsumableStream = require('writable-consumable-stream');
|
|
2
2
|
const DemuxedConsumableStream = require('./demuxed-consumable-stream');
|
|
3
|
+
const StreamConsumer = require('./stream-consumer');
|
|
3
4
|
|
|
4
5
|
class StreamDemux {
|
|
5
6
|
constructor() {
|
|
@@ -120,84 +121,19 @@ class StreamDemux {
|
|
|
120
121
|
return this.getConsumerStatsListAll().length;
|
|
121
122
|
}
|
|
122
123
|
|
|
123
|
-
createConsumer(streamName, timeout) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
packet.value.stream === streamName ||
|
|
133
|
-
packet.value.consumerId === this.id
|
|
134
|
-
) {
|
|
135
|
-
if (packet.value.data.done) {
|
|
136
|
-
this.return();
|
|
137
|
-
}
|
|
138
|
-
return packet.value.data;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
if (packet.done) {
|
|
142
|
-
return packet;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
let consumerClearActiveTimeout = mainStreamConsumer.clearActiveTimeout;
|
|
148
|
-
mainStreamConsumer.clearActiveTimeout = function (packet) {
|
|
149
|
-
let { value, done } = packet;
|
|
150
|
-
if (done) {
|
|
151
|
-
consumerClearActiveTimeout.apply(this, arguments);
|
|
152
|
-
} else if (value && value.stream === streamName) {
|
|
153
|
-
consumerClearActiveTimeout.apply(this, arguments);
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
let consumerGetStats = mainStreamConsumer.getStats;
|
|
158
|
-
mainStreamConsumer.getStats = function () {
|
|
159
|
-
let stats = consumerGetStats.apply(this, arguments);
|
|
160
|
-
stats.stream = streamName;
|
|
161
|
-
return stats;
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
let consumerApplyBackpressure = mainStreamConsumer.applyBackpressure;
|
|
165
|
-
mainStreamConsumer.applyBackpressure = function (packet) {
|
|
166
|
-
if (packet.value) {
|
|
167
|
-
if (
|
|
168
|
-
packet.value.stream === streamName ||
|
|
169
|
-
packet.value.consumerId === this.id
|
|
170
|
-
) {
|
|
171
|
-
consumerApplyBackpressure.apply(this, arguments);
|
|
172
|
-
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
if (packet.done) {
|
|
177
|
-
consumerApplyBackpressure.apply(this, arguments);
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
let consumerReleaseBackpressure = mainStreamConsumer.releaseBackpressure;
|
|
182
|
-
mainStreamConsumer.releaseBackpressure = function (packet) {
|
|
183
|
-
if (packet.value) {
|
|
184
|
-
if (
|
|
185
|
-
packet.value.stream === streamName ||
|
|
186
|
-
packet.value.consumerId === this.id
|
|
187
|
-
) {
|
|
188
|
-
consumerReleaseBackpressure.apply(this, arguments);
|
|
189
|
-
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
if (packet.done) {
|
|
194
|
-
consumerReleaseBackpressure.apply(this, arguments);
|
|
195
|
-
}
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
return mainStreamConsumer;
|
|
124
|
+
createConsumer(streamName, timeout, usabilityMode) {
|
|
125
|
+
return new StreamConsumer(
|
|
126
|
+
this._mainStream,
|
|
127
|
+
this._mainStream.nextConsumerId++,
|
|
128
|
+
this._mainStream.tailNode,
|
|
129
|
+
streamName,
|
|
130
|
+
timeout,
|
|
131
|
+
usabilityMode
|
|
132
|
+
);
|
|
199
133
|
}
|
|
200
134
|
|
|
135
|
+
// Unlike individual consumers, consumable streams support being iterated
|
|
136
|
+
// over by multiple for-await-of loops in parallel.
|
|
201
137
|
stream(streamName) {
|
|
202
138
|
return new DemuxedConsumableStream(this, streamName);
|
|
203
139
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stream-demux",
|
|
3
|
-
"version": "9.0.
|
|
3
|
+
"version": "9.0.1",
|
|
4
4
|
"description": "A consumable stream demultiplexer.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
},
|
|
26
26
|
"homepage": "https://github.com/SocketCluster/stream-demux#readme",
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"mocha": "^
|
|
28
|
+
"mocha": "^10.2.0"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"consumable-stream": "^3.0.0",
|
|
32
|
-
"writable-consumable-stream": "^4.0.
|
|
32
|
+
"writable-consumable-stream": "^4.0.1"
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
class StreamConsumer {
|
|
2
|
+
constructor(mainStream, id, startNode, streamName, timeout, usabilityMode) {
|
|
3
|
+
this.id = id;
|
|
4
|
+
this._backpressure = 0;
|
|
5
|
+
this.currentNode = startNode;
|
|
6
|
+
this.timeout = timeout;
|
|
7
|
+
this.isAlive = true;
|
|
8
|
+
this._mainStream = mainStream;
|
|
9
|
+
this._mainStream.setConsumer(this.id, this);
|
|
10
|
+
this.streamName = streamName;
|
|
11
|
+
this.usabilityMode = !!usabilityMode;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getStats() {
|
|
15
|
+
let stats = {
|
|
16
|
+
id: this.id,
|
|
17
|
+
backpressure: this._backpressure,
|
|
18
|
+
stream: this.streamName
|
|
19
|
+
};
|
|
20
|
+
if (this.timeout != null) {
|
|
21
|
+
stats.timeout = this.timeout;
|
|
22
|
+
}
|
|
23
|
+
return stats;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
_resetBackpressure() {
|
|
27
|
+
this._backpressure = 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
applyBackpressure(packet) {
|
|
31
|
+
this._backpressure++;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
releaseBackpressure(packet) {
|
|
35
|
+
this._backpressure--;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getBackpressure() {
|
|
39
|
+
return this._backpressure;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
clearActiveTimeout() {
|
|
43
|
+
clearTimeout(this._timeoutId);
|
|
44
|
+
delete this._timeoutId;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
write(packet) {
|
|
48
|
+
if (packet.done || packet.value.stream === this.streamName) {
|
|
49
|
+
if (this._timeoutId !== undefined) {
|
|
50
|
+
this.clearActiveTimeout(packet);
|
|
51
|
+
}
|
|
52
|
+
this.applyBackpressure(packet);
|
|
53
|
+
}
|
|
54
|
+
if (this._resolve) {
|
|
55
|
+
this._resolve();
|
|
56
|
+
delete this._resolve;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
kill(value) {
|
|
61
|
+
this._killPacket = {value, done: true};
|
|
62
|
+
if (this._timeoutId !== undefined) {
|
|
63
|
+
this.clearActiveTimeout(this._killPacket);
|
|
64
|
+
}
|
|
65
|
+
this._destroy();
|
|
66
|
+
|
|
67
|
+
if (this._resolve) {
|
|
68
|
+
this._resolve();
|
|
69
|
+
delete this._resolve;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
_destroy() {
|
|
74
|
+
this.isAlive = false;
|
|
75
|
+
this._resetBackpressure();
|
|
76
|
+
this._mainStream.removeConsumer(this.id);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async _waitForNextItem(timeout) {
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
this._resolve = resolve;
|
|
82
|
+
let timeoutId;
|
|
83
|
+
if (timeout != null) {
|
|
84
|
+
// Create the error object in the outer scope in order
|
|
85
|
+
// to get the full stack trace.
|
|
86
|
+
let error = new Error('Stream consumer iteration timed out');
|
|
87
|
+
(async () => {
|
|
88
|
+
let delay = wait(timeout);
|
|
89
|
+
timeoutId = delay.timeoutId;
|
|
90
|
+
await delay.promise;
|
|
91
|
+
error.name = 'TimeoutError';
|
|
92
|
+
delete this._resolve;
|
|
93
|
+
reject(error);
|
|
94
|
+
})();
|
|
95
|
+
}
|
|
96
|
+
this._timeoutId = timeoutId;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async next() {
|
|
101
|
+
this._mainStream.setConsumer(this.id, this);
|
|
102
|
+
|
|
103
|
+
while (true) {
|
|
104
|
+
if (!this.currentNode.next) {
|
|
105
|
+
try {
|
|
106
|
+
await this._waitForNextItem(this.timeout);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
this._destroy();
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (this._killPacket) {
|
|
113
|
+
this._destroy();
|
|
114
|
+
let killPacket = this._killPacket;
|
|
115
|
+
delete this._killPacket;
|
|
116
|
+
|
|
117
|
+
return killPacket;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Skip over nodes which belong to different streams.
|
|
121
|
+
while (
|
|
122
|
+
this.currentNode.next?.data?.value &&
|
|
123
|
+
this.currentNode.next.data.value.stream !== this.streamName &&
|
|
124
|
+
this.currentNode.next.consumerId !== this.id &&
|
|
125
|
+
!this.currentNode.next.data.done
|
|
126
|
+
) {
|
|
127
|
+
this.currentNode = this.currentNode.next;
|
|
128
|
+
this.usabilityMode && await wait(0);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (!this.currentNode.next) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this.currentNode = this.currentNode.next;
|
|
136
|
+
|
|
137
|
+
this.releaseBackpressure(this.currentNode.data);
|
|
138
|
+
|
|
139
|
+
if (this.currentNode.data?.value?.data?.done) {
|
|
140
|
+
this._destroy();
|
|
141
|
+
return this.currentNode.data.value.data;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (this.currentNode.data.done) {
|
|
145
|
+
this._destroy();
|
|
146
|
+
return this.currentNode.data;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return this.currentNode.data.value.data;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return() {
|
|
154
|
+
delete this.currentNode;
|
|
155
|
+
this._destroy();
|
|
156
|
+
return {};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
[Symbol.asyncIterator]() {
|
|
160
|
+
return this;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function wait(timeout) {
|
|
165
|
+
let timeoutId;
|
|
166
|
+
let promise = new Promise((resolve) => {
|
|
167
|
+
timeoutId = setTimeout(resolve, timeout);
|
|
168
|
+
});
|
|
169
|
+
return {timeoutId, promise};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
module.exports = StreamConsumer;
|
package/test/test.js
CHANGED