@miso.ai/server-commons 0.6.3 → 0.6.4-beta.0
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 +1 -1
- package/src/control/base.js +41 -0
- package/src/control/collective.js +38 -0
- package/src/control/create.js +22 -0
- package/src/control/index.js +5 -0
- package/src/control/throttle.js +28 -0
- package/src/control/watermark.js +34 -0
- package/src/stream/index.js +1 -0
- package/src/stream/parallel-transform.js +82 -0
package/package.json
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import EventEmitter from 'events';
|
|
2
|
+
|
|
3
|
+
export default class TaskControl {
|
|
4
|
+
|
|
5
|
+
constructor() {
|
|
6
|
+
this._events = new EventEmitter();
|
|
7
|
+
this._ready = true;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
on(event, listener) {
|
|
11
|
+
this._events.on(event, listener);
|
|
12
|
+
return () => this._events.off(event, listener);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
once(event, listener) {
|
|
16
|
+
this._events.once(event, listener);
|
|
17
|
+
return () => this._events.off(event, listener);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
open(id, data) {}
|
|
21
|
+
|
|
22
|
+
close(id) {}
|
|
23
|
+
|
|
24
|
+
get ready() {
|
|
25
|
+
return this._ready;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
_setReady(value) {
|
|
29
|
+
value = !!value
|
|
30
|
+
if (this._ready === value) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
this._ready = value;
|
|
34
|
+
this._events.emit('ready', value);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
destroy() {
|
|
38
|
+
this._events.removeAllListeners();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import TaskControl from './base.js';
|
|
2
|
+
|
|
3
|
+
export default class CollectiveTaskControl extends TaskControl {
|
|
4
|
+
|
|
5
|
+
constructor(...members) {
|
|
6
|
+
super();
|
|
7
|
+
this._members = members;
|
|
8
|
+
this._unsubscribes = members.map((control) => control.on('ready', () => this._syncReady()));
|
|
9
|
+
this._syncReady();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
open(id, data) {
|
|
13
|
+
for (const control of this._members) {
|
|
14
|
+
control.open(id, data);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
close(id) {
|
|
19
|
+
for (const control of this._members) {
|
|
20
|
+
control.close(id);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
_syncReady() {
|
|
25
|
+
this._setReady(this._members.every((control) => control.ready));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
destroy() {
|
|
29
|
+
for (const unsubscribe of this._unsubscribes) {
|
|
30
|
+
unsubscribe();
|
|
31
|
+
}
|
|
32
|
+
for (const control of this._members) {
|
|
33
|
+
control.destroy();
|
|
34
|
+
}
|
|
35
|
+
super.destroy();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import TaskControl from './base.js';
|
|
2
|
+
import CollectiveTaskControl from './collective.js';
|
|
3
|
+
import WaterMarkTaskControl from './watermark.js';
|
|
4
|
+
import ThrottledTaskControl from './throttle.js';
|
|
5
|
+
|
|
6
|
+
export function createTaskControl({
|
|
7
|
+
highWaterMark,
|
|
8
|
+
throttle,
|
|
9
|
+
} = {}) {
|
|
10
|
+
const controls = [];
|
|
11
|
+
if (highWaterMark) {
|
|
12
|
+
controls.push(new WaterMarkTaskControl({
|
|
13
|
+
highWaterMark,
|
|
14
|
+
}));
|
|
15
|
+
}
|
|
16
|
+
if (throttle) {
|
|
17
|
+
controls.push(new ThrottledTaskControl({
|
|
18
|
+
interval: throttle,
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
return controls.length > 0 ? new CollectiveTaskControl(...controls) : new TaskControl();
|
|
22
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default as TaskControl } from './base.js';
|
|
2
|
+
export { default as CollectiveTaskControl } from './collective.js';
|
|
3
|
+
export { default as WaterMarkTaskControl } from './watermark.js';
|
|
4
|
+
export { default as ThrottledTaskControl } from './throttle.js';
|
|
5
|
+
export { createTaskControl } from './create.js';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import TaskControl from './base.js';
|
|
2
|
+
|
|
3
|
+
export default class ThrottledTaskControl extends TaskControl {
|
|
4
|
+
|
|
5
|
+
constructor({
|
|
6
|
+
interval,
|
|
7
|
+
}) {
|
|
8
|
+
super();
|
|
9
|
+
if (typeof interval !== 'number' || interval <= 0) {
|
|
10
|
+
throw new Error(`interval must be a positive number: ${interval}`);
|
|
11
|
+
}
|
|
12
|
+
this._interval = interval;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
get interval() {
|
|
16
|
+
return this._interval;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
open(id, data) {
|
|
20
|
+
this._setReady(false);
|
|
21
|
+
this._timeoutId && clearTimeout(this._timeoutId);
|
|
22
|
+
this._timeoutId = setTimeout(() => {
|
|
23
|
+
this._setReady(true);
|
|
24
|
+
this._timeoutId = undefined;
|
|
25
|
+
}, this._interval);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import TaskControl from './base.js';
|
|
2
|
+
|
|
3
|
+
export default class WaterMarkTaskControl extends TaskControl{
|
|
4
|
+
|
|
5
|
+
constructor({
|
|
6
|
+
highWaterMark,
|
|
7
|
+
}) {
|
|
8
|
+
super();
|
|
9
|
+
if (!Number.isInteger(highWaterMark) || highWaterMark <= 0) {
|
|
10
|
+
throw new Error('highWaterMark must be a positive integer');
|
|
11
|
+
}
|
|
12
|
+
this._highWaterMark = highWaterMark;
|
|
13
|
+
this._waterLevel = 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get waterLevel() {
|
|
17
|
+
return this._waterLevel;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
open(id, data) {
|
|
21
|
+
this._waterLevel++;
|
|
22
|
+
this._syncReady();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
close(id) {
|
|
26
|
+
this._waterLevel--;
|
|
27
|
+
this._syncReady();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
_syncReady() {
|
|
31
|
+
this._setReady(this._waterLevel < this._highWaterMark);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
}
|
package/src/stream/index.js
CHANGED
|
@@ -5,3 +5,4 @@ export { default as BufferedWriteStream } from './buffered-write.js';
|
|
|
5
5
|
export { default as OutputStream } from './output.js';
|
|
6
6
|
export { default as LogUpdateStream } from './log-update.js';
|
|
7
7
|
export { default as DiffStream } from './diff.js';
|
|
8
|
+
export { default as ParallelTransform } from './parallel-transform.js';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Duplex } from 'stream';
|
|
2
|
+
import { v4 as uuid } from 'uuid';
|
|
3
|
+
import { createTaskControl } from '../control/index.js';
|
|
4
|
+
|
|
5
|
+
export default class ParallelTransform extends Duplex {
|
|
6
|
+
|
|
7
|
+
constructor({
|
|
8
|
+
transform,
|
|
9
|
+
controls,
|
|
10
|
+
...options
|
|
11
|
+
} = {}) {
|
|
12
|
+
super(options);
|
|
13
|
+
if (transform !== undefined && typeof transform !== 'function') {
|
|
14
|
+
throw new Error('transform must be a function');
|
|
15
|
+
}
|
|
16
|
+
this._transformFn = transform;
|
|
17
|
+
this._control = createTaskControl(controls);
|
|
18
|
+
|
|
19
|
+
this._next = undefined;
|
|
20
|
+
this._pushPaused = false;
|
|
21
|
+
this._outputBuffer;
|
|
22
|
+
|
|
23
|
+
this._control.on('ready', (ready) => ready && this._callNext());
|
|
24
|
+
this.on('drain', () => this._handleDrain());
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async _transformFn(chunk, encoding) {
|
|
28
|
+
throw new Error('transform function not defined');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
_write(chunk, encoding, next) {
|
|
32
|
+
const id = uuid();
|
|
33
|
+
this._next = next;
|
|
34
|
+
this._control.open(id, [chunk, encoding]);
|
|
35
|
+
(async () => {
|
|
36
|
+
try {
|
|
37
|
+
const output = await this._transformFn(chunk, encoding);
|
|
38
|
+
this._control.close(id);
|
|
39
|
+
this._writeOutput(output);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
this._error(error);
|
|
42
|
+
}
|
|
43
|
+
})();
|
|
44
|
+
this._callNext();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
_callNext() {
|
|
48
|
+
if (this._next && this._control.ready) {
|
|
49
|
+
const next = this._next;
|
|
50
|
+
this._next = undefined;
|
|
51
|
+
next();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
_writeOutput(output) {
|
|
56
|
+
if (this._outputBuffer) {
|
|
57
|
+
this._outputBuffer.push(output);
|
|
58
|
+
} else if (!this.push(output)) {
|
|
59
|
+
this._outputBuffer = [];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
_handleDrain() {
|
|
64
|
+
if (!this._outputBuffer) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const buffer = this._outputBuffer;
|
|
68
|
+
for (let i = 0, len = buffer.length; i < len; i++) {
|
|
69
|
+
if (!this.push(buffer[i])) {
|
|
70
|
+
this._outputBuffer = buffer.slice(i + 1);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
_read() {}
|
|
77
|
+
|
|
78
|
+
_error(error) {
|
|
79
|
+
this.emit('error', error);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
}
|