@nxtedition/scheduler 3.0.12 → 3.1.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/README.md +75 -1
- package/lib/index.d.ts +5 -30
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +4 -272
- package/lib/index.js.map +1 -0
- package/lib/queue.d.ts +38 -0
- package/lib/queue.d.ts.map +1 -0
- package/lib/queue.js +138 -0
- package/lib/queue.js.map +1 -0
- package/lib/scheduler.d.ts +17 -0
- package/lib/scheduler.d.ts.map +1 -0
- package/lib/scheduler.js +113 -0
- package/lib/scheduler.js.map +1 -0
- package/lib/throttle.d.ts +20 -0
- package/lib/throttle.d.ts.map +1 -0
- package/lib/throttle.js +107 -0
- package/lib/throttle.js.map +1 -0
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @nxtedition/scheduler
|
|
2
2
|
|
|
3
|
-
A high-performance, priority-based task scheduler for Node.js with support for concurrency limiting and multi-worker coordination via `SharedArrayBuffer`.
|
|
3
|
+
A high-performance, priority-based task scheduler for Node.js with support for concurrency limiting, byte-rate throttling, and multi-worker coordination via `SharedArrayBuffer`.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -157,6 +157,80 @@ Create shared state for cross-worker scheduling.
|
|
|
157
157
|
|
|
158
158
|
Parse a string or number into a normalized priority value.
|
|
159
159
|
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Throttle
|
|
163
|
+
|
|
164
|
+
`Throttle` is a token-bucket rate limiter that controls how many **bytes per second** are processed. It shares the same priority system as `Scheduler`.
|
|
165
|
+
|
|
166
|
+
### Basic
|
|
167
|
+
|
|
168
|
+
```js
|
|
169
|
+
import { Throttle } from '@nxtedition/scheduler'
|
|
170
|
+
|
|
171
|
+
// Allow 1 MB/s
|
|
172
|
+
const throttle = new Throttle({ bytesPerSecond: 1_000_000 })
|
|
173
|
+
|
|
174
|
+
// Refill tokens every 10ms
|
|
175
|
+
setInterval(() => throttle.refill(), 10)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Multi-Worker Coordination
|
|
179
|
+
|
|
180
|
+
### Streaming
|
|
181
|
+
|
|
182
|
+
`throttle.stream()` returns a Node.js `Transform` stream that enforces backpressure — it won't call the next `write` until tokens are available:
|
|
183
|
+
|
|
184
|
+
```js
|
|
185
|
+
import { createReadStream, createWriteStream } from 'node:fs'
|
|
186
|
+
import { pipeline } from 'node:stream/promises'
|
|
187
|
+
|
|
188
|
+
const throttle = new Throttle({ bytesPerSecond: 1_000_000 }) // 1 MB/s
|
|
189
|
+
setInterval(() => throttle.refill(), 10)
|
|
190
|
+
|
|
191
|
+
await pipeline(createReadStream('input.mp4'), throttle.stream(), createWriteStream('output.mp4'))
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Priority
|
|
195
|
+
|
|
196
|
+
Both `run()` and `stream()` accept a priority. Higher-priority work drains first when tokens are available:
|
|
197
|
+
|
|
198
|
+
```js
|
|
199
|
+
// Low-priority stream (yields to higher-priority work)
|
|
200
|
+
const stream = throttle.stream('low')
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Low-Level API
|
|
204
|
+
|
|
205
|
+
```js
|
|
206
|
+
throttle.acquire(
|
|
207
|
+
() => {
|
|
208
|
+
sendPacket(data)
|
|
209
|
+
},
|
|
210
|
+
data.byteLength,
|
|
211
|
+
'normal',
|
|
212
|
+
)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
`acquire` returns `true` if the callback ran immediately (tokens were available), or `false` if it was queued.
|
|
216
|
+
|
|
217
|
+
## API
|
|
218
|
+
|
|
219
|
+
### `new Throttle(opts)`
|
|
220
|
+
|
|
221
|
+
- `opts.bytesPerSecond` — bytes per second (default: `Infinity`)
|
|
222
|
+
- `opts` may also be a `SharedArrayBuffer` created by `Throttle.makeSharedState()`
|
|
223
|
+
|
|
224
|
+
### `throttle.acquire(fn, bytes, priority?): boolean`
|
|
225
|
+
|
|
226
|
+
Low-level acquisition. Returns `true` if `fn` ran immediately, `false` if queued.
|
|
227
|
+
|
|
228
|
+
### `throttle.stream(priority?): Transform`
|
|
229
|
+
|
|
230
|
+
Returns a `Transform` stream that rate-limits data passing through it. Each chunk consumes `chunk.length` tokens.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
160
234
|
## License
|
|
161
235
|
|
|
162
236
|
MIT
|
package/lib/index.d.ts
CHANGED
|
@@ -1,30 +1,5 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
static readonly LOWER = -2;
|
|
7
|
-
static readonly LOW = -1;
|
|
8
|
-
static readonly NORMAL = 0;
|
|
9
|
-
static readonly HIGH = 1;
|
|
10
|
-
static readonly HIGHER = 2;
|
|
11
|
-
static readonly HIGHEST = 3;
|
|
12
|
-
get concurrency(): number;
|
|
13
|
-
get stats(): {
|
|
14
|
-
deferred: number;
|
|
15
|
-
running: number;
|
|
16
|
-
pending: number;
|
|
17
|
-
queues: {
|
|
18
|
-
count: number;
|
|
19
|
-
}[];
|
|
20
|
-
};
|
|
21
|
-
static makeSharedState(concurrency: number): SharedArrayBuffer;
|
|
22
|
-
constructor(opts: SharedArrayBuffer | {
|
|
23
|
-
concurrency?: number;
|
|
24
|
-
});
|
|
25
|
-
run<T>(fn: () => Promise<T> | T, priority?: Priority): Promise<T>;
|
|
26
|
-
run<T, U>(fn: (opaque: U) => Promise<T> | T, priority: Priority, opaque: U): Promise<T>;
|
|
27
|
-
acquire(fn: () => unknown, priority?: Priority): void;
|
|
28
|
-
acquire<U>(fn: (opaque: U) => unknown, priority: Priority, opaque: U): void;
|
|
29
|
-
release(): void;
|
|
30
|
-
}
|
|
1
|
+
export { Scheduler } from './scheduler.ts';
|
|
2
|
+
export { Throttle } from './throttle.ts';
|
|
3
|
+
export type { Priority } from './queue.ts';
|
|
4
|
+
export { parsePriority } from './queue.ts';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA"}
|
package/lib/index.js
CHANGED
|
@@ -1,272 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class FastQueue {
|
|
7
|
-
idx = 0
|
|
8
|
-
cnt = 0
|
|
9
|
-
arr = []
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
export function parsePriority(p ) {
|
|
29
|
-
if (typeof p === 'number') {
|
|
30
|
-
// Do nothing...
|
|
31
|
-
} else if (p === 'lowest') {
|
|
32
|
-
return -3
|
|
33
|
-
} else if (p === 'lower') {
|
|
34
|
-
return -2
|
|
35
|
-
} else if (p === 'low') {
|
|
36
|
-
return -1
|
|
37
|
-
} else if (p === 'normal') {
|
|
38
|
-
return 0
|
|
39
|
-
} else if (p === 'high') {
|
|
40
|
-
return 1
|
|
41
|
-
} else if (p === 'higher') {
|
|
42
|
-
return 2
|
|
43
|
-
} else if (p === 'highest') {
|
|
44
|
-
return 3
|
|
45
|
-
} else {
|
|
46
|
-
p = Number(p)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (typeof p !== 'number' || Number.isNaN(p)) {
|
|
50
|
-
return 0
|
|
51
|
-
} else if (p < -3) {
|
|
52
|
-
return -3
|
|
53
|
-
} else if (p > 3) {
|
|
54
|
-
return 3
|
|
55
|
-
} else {
|
|
56
|
-
return Math.trunc(p)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export class Scheduler {
|
|
61
|
-
static LOWEST = -3
|
|
62
|
-
static LOWER = -2
|
|
63
|
-
static LOW = -1
|
|
64
|
-
static NORMAL = 0
|
|
65
|
-
static HIGH = 1
|
|
66
|
-
static HIGHER = 2
|
|
67
|
-
static HIGHEST = 3
|
|
68
|
-
|
|
69
|
-
#concurrency
|
|
70
|
-
#stateView
|
|
71
|
-
|
|
72
|
-
#running = 0
|
|
73
|
-
#pending = 0
|
|
74
|
-
#counter = 0
|
|
75
|
-
#releasing = false
|
|
76
|
-
#deferred = 0
|
|
77
|
-
|
|
78
|
-
#queues = [
|
|
79
|
-
new FastQueue(), // 0 lowest
|
|
80
|
-
new FastQueue(), // 1 lower
|
|
81
|
-
new FastQueue(), // 2 low
|
|
82
|
-
new FastQueue(), // 3 normal
|
|
83
|
-
new FastQueue(), // 4 high
|
|
84
|
-
new FastQueue(), // 5 higher
|
|
85
|
-
new FastQueue(), // 6 highest
|
|
86
|
-
]
|
|
87
|
-
|
|
88
|
-
get concurrency() {
|
|
89
|
-
return this.#concurrency
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
get stats() {
|
|
93
|
-
return {
|
|
94
|
-
deferred: this.#deferred,
|
|
95
|
-
running: this.#running,
|
|
96
|
-
pending: this.#pending,
|
|
97
|
-
queues: this.#queues.map((q) => ({ count: q.cnt })),
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
static makeSharedState(concurrency ) {
|
|
102
|
-
if (concurrency != null && (concurrency < 0 || !Number.isInteger(concurrency))) {
|
|
103
|
-
throw new Error('Invalid concurrency')
|
|
104
|
-
}
|
|
105
|
-
const stateBuffer = new SharedArrayBuffer(64)
|
|
106
|
-
const stateView = new Int32Array(stateBuffer)
|
|
107
|
-
Atomics.store(stateView, CONCURRENCY_INDEX, concurrency ?? -1)
|
|
108
|
-
return stateBuffer
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
constructor(opts ) {
|
|
112
|
-
if (opts instanceof SharedArrayBuffer) {
|
|
113
|
-
this.#stateView = new Int32Array(opts)
|
|
114
|
-
this.#concurrency = Atomics.load(this.#stateView, CONCURRENCY_INDEX)
|
|
115
|
-
if (this.#concurrency === -1) {
|
|
116
|
-
this.#concurrency = Infinity
|
|
117
|
-
}
|
|
118
|
-
} else {
|
|
119
|
-
this.#concurrency = opts?.concurrency ?? Infinity
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
run (
|
|
125
|
-
fn ,
|
|
126
|
-
priority = Scheduler.NORMAL,
|
|
127
|
-
opaque ,
|
|
128
|
-
) {
|
|
129
|
-
return new Promise ((resolve, reject) => {
|
|
130
|
-
this.acquire(
|
|
131
|
-
async (o) => {
|
|
132
|
-
try {
|
|
133
|
-
resolve(await fn(o))
|
|
134
|
-
} catch (err) {
|
|
135
|
-
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
|
136
|
-
reject(err)
|
|
137
|
-
} finally {
|
|
138
|
-
this.release()
|
|
139
|
-
}
|
|
140
|
-
},
|
|
141
|
-
priority,
|
|
142
|
-
opaque,
|
|
143
|
-
)
|
|
144
|
-
})
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
acquire (fn , priority = Scheduler.NORMAL, opaque ) {
|
|
150
|
-
const p = parsePriority(priority)
|
|
151
|
-
const queue = this.#queues[p + 3]
|
|
152
|
-
|
|
153
|
-
if (this.#stateView) {
|
|
154
|
-
// Make sure we are always running at least one local job even if we might globally over subscribe.
|
|
155
|
-
if (this.#running < 1) {
|
|
156
|
-
Atomics.add(this.#stateView, RUNNING_INDEX, 1)
|
|
157
|
-
this.#running += 1
|
|
158
|
-
fn(opaque)
|
|
159
|
-
return
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// We use non atomic access here as an optimization and treat the concurrency limit as a soft limit.
|
|
163
|
-
if (this.#stateView[RUNNING_INDEX] < this.#concurrency) {
|
|
164
|
-
Atomics.add(this.#stateView, RUNNING_INDEX, 1)
|
|
165
|
-
this.#running += 1
|
|
166
|
-
fn(opaque)
|
|
167
|
-
return
|
|
168
|
-
}
|
|
169
|
-
} else if ((this.#running < 1 && this.#concurrency > 0) || this.#running < this.#concurrency) {
|
|
170
|
-
this.#running += 1
|
|
171
|
-
fn(opaque)
|
|
172
|
-
return
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
queue.arr.push(fn, opaque)
|
|
176
|
-
queue.cnt += 1
|
|
177
|
-
|
|
178
|
-
this.#deferred += 1
|
|
179
|
-
this.#pending += 1
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
release() {
|
|
183
|
-
let running
|
|
184
|
-
if (this.#running > 0) {
|
|
185
|
-
running = this.#stateView
|
|
186
|
-
? Atomics.sub(this.#stateView, RUNNING_INDEX, 1) - 1
|
|
187
|
-
: this.#running - 1
|
|
188
|
-
this.#running -= 1
|
|
189
|
-
} else {
|
|
190
|
-
// Gracefully handle user error...
|
|
191
|
-
running = this.#stateView ? Atomics.load(this.#stateView, RUNNING_INDEX) : this.#running
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (this.#pending === 0 || this.#releasing) {
|
|
195
|
-
return
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
try {
|
|
199
|
-
this.#releasing = true
|
|
200
|
-
while (this.#pending > 0 && (this.#running === 0 || running < this.#concurrency)) {
|
|
201
|
-
let queue = null
|
|
202
|
-
|
|
203
|
-
let idx = this.#queues.length - 1
|
|
204
|
-
|
|
205
|
-
// Avoid starvation by randomizing the starting queue.
|
|
206
|
-
{
|
|
207
|
-
this.#counter = (this.#counter + 1) & maxInt
|
|
208
|
-
if (this.#counter & 0b0000001) {
|
|
209
|
-
idx = 6 // highest: 50%
|
|
210
|
-
} else if (this.#counter & 0b0000010) {
|
|
211
|
-
idx = 5 // higher: 25%
|
|
212
|
-
} else if (this.#counter & 0b0000100) {
|
|
213
|
-
idx = 4 // high: 12.5%
|
|
214
|
-
} else if (this.#counter & 0b0001000) {
|
|
215
|
-
idx = 3 // normal: 6.25%
|
|
216
|
-
} else if (this.#counter & 0b0010000) {
|
|
217
|
-
idx = 2 // low: 3.125%
|
|
218
|
-
} else if (this.#counter & 0b0100000) {
|
|
219
|
-
idx = 1 // lower: 1.5625%
|
|
220
|
-
} else if (this.#counter & 0b1000000) {
|
|
221
|
-
idx = 0 // lowest: 0.78%
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
for (let n = idx; n >= 0 && (queue == null || queue.cnt === 0); n--) {
|
|
226
|
-
queue = this.#queues[n]
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
for (let n = this.#queues.length - 1; n > idx && (queue == null || queue.cnt === 0); n--) {
|
|
230
|
-
queue = this.#queues[n]
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (queue == null || queue.cnt === 0) {
|
|
234
|
-
throw new Error('Invariant violation: pending > 0 but no tasks in queues')
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const fn = queue.arr[queue.idx]
|
|
238
|
-
queue.arr[queue.idx++] = null
|
|
239
|
-
const opaque = queue.arr[queue.idx]
|
|
240
|
-
queue.arr[queue.idx++] = null
|
|
241
|
-
queue.cnt -= 1
|
|
242
|
-
|
|
243
|
-
if (queue.cnt === 0) {
|
|
244
|
-
queue.idx = 0
|
|
245
|
-
queue.arr.length = 0
|
|
246
|
-
} else if (queue.idx > 1024) {
|
|
247
|
-
queue.arr.splice(0, queue.idx)
|
|
248
|
-
queue.idx = 0
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
this.#pending -= 1
|
|
252
|
-
this.#running += 1
|
|
253
|
-
|
|
254
|
-
if (this.#stateView) {
|
|
255
|
-
Atomics.add(this.#stateView, RUNNING_INDEX, 1)
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
fn(opaque)
|
|
259
|
-
|
|
260
|
-
// Re-read running after fn() in case it synchronously called release()
|
|
261
|
-
running = this.#stateView ? Atomics.load(this.#stateView, RUNNING_INDEX) : this.#running
|
|
262
|
-
}
|
|
263
|
-
} catch (err) {
|
|
264
|
-
// Throwing here is undefined behavior...
|
|
265
|
-
queueMicrotask(() => {
|
|
266
|
-
throw new Error('Scheduler task error', { cause: err })
|
|
267
|
-
})
|
|
268
|
-
} finally {
|
|
269
|
-
this.#releasing = false
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
1
|
+
export { Scheduler } from "./scheduler.js";
|
|
2
|
+
export { Throttle } from "./throttle.js";
|
|
3
|
+
export { parsePriority } from "./queue.js";
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA"}
|
package/lib/queue.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export declare class FastQueue {
|
|
2
|
+
idx: number;
|
|
3
|
+
cnt: number;
|
|
4
|
+
arr: Array<unknown>;
|
|
5
|
+
}
|
|
6
|
+
export type NumberPriority = -3 | -2 | -1 | 0 | 1 | 2 | 3;
|
|
7
|
+
export type Priority = NumberPriority | 'lowest' | 'lower' | 'low' | 'normal' | 'high' | 'higher' | 'highest';
|
|
8
|
+
export declare function parsePriority(p: string | number): NumberPriority;
|
|
9
|
+
export declare class Queue {
|
|
10
|
+
#private;
|
|
11
|
+
static readonly LOWEST = -3;
|
|
12
|
+
static readonly LOWER = -2;
|
|
13
|
+
static readonly LOW = -1;
|
|
14
|
+
static readonly NORMAL = 0;
|
|
15
|
+
static readonly HIGH = 1;
|
|
16
|
+
static readonly HIGHER = 2;
|
|
17
|
+
static readonly HIGHEST = 3;
|
|
18
|
+
protected stateView?: Int32Array;
|
|
19
|
+
protected running: number;
|
|
20
|
+
protected pending: number;
|
|
21
|
+
protected counter: number;
|
|
22
|
+
protected releasing: boolean;
|
|
23
|
+
protected deferred: number;
|
|
24
|
+
protected queues: FastQueue[];
|
|
25
|
+
get concurrency(): number;
|
|
26
|
+
get stats(): {
|
|
27
|
+
pending: number;
|
|
28
|
+
queues: {
|
|
29
|
+
count: number;
|
|
30
|
+
}[];
|
|
31
|
+
};
|
|
32
|
+
protected getNextQueue(): FastQueue | null;
|
|
33
|
+
static makeSharedState(concurrency: number): SharedArrayBuffer;
|
|
34
|
+
constructor(opts: SharedArrayBuffer | {
|
|
35
|
+
concurrency?: number;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAEA,qBAAa,SAAS;IACpB,GAAG,SAAI;IACP,GAAG,SAAI;IACP,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,CAAK;CACzB;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;AAEzD,MAAM,MAAM,QAAQ,GAChB,cAAc,GACd,QAAQ,GACR,OAAO,GACP,KAAK,GACL,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,SAAS,CAAA;AAEb,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,cAAc,CA8BhE;AAID,qBAAa,KAAK;;IAChB,MAAM,CAAC,QAAQ,CAAC,MAAM,MAAK;IAC3B,MAAM,CAAC,QAAQ,CAAC,KAAK,MAAK;IAC1B,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAK;IACxB,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAI;IAC1B,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAI;IACxB,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAI;IAC1B,MAAM,CAAC,QAAQ,CAAC,OAAO,KAAI;IAG3B,SAAS,CAAC,SAAS,CAAC,EAAE,UAAU,CAAA;IAEhC,SAAS,CAAC,OAAO,SAAI;IACrB,SAAS,CAAC,OAAO,SAAI;IACrB,SAAS,CAAC,OAAO,SAAI;IACrB,SAAS,CAAC,SAAS,UAAQ;IAC3B,SAAS,CAAC,QAAQ,SAAI;IAGtB,SAAS,CAAC,MAAM,cAQf;IAED,IAAI,WAAW,WAEd;IAED,IAAI,KAAK;;;;;MAKR;IAED,SAAS,CAAC,YAAY;IAkCtB,MAAM,CAAC,eAAe,CAAC,WAAW,EAAE,MAAM;gBAU9B,IAAI,EAAE,iBAAiB,GAAG;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE;CAW/D"}
|
package/lib/queue.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
const CONCURRENCY_INDEX = 1;
|
|
2
|
+
export class FastQueue {
|
|
3
|
+
idx = 0;
|
|
4
|
+
cnt = 0;
|
|
5
|
+
arr = [];
|
|
6
|
+
}
|
|
7
|
+
export function parsePriority(p) {
|
|
8
|
+
if (typeof p === 'number') {
|
|
9
|
+
// Do nothing...
|
|
10
|
+
}
|
|
11
|
+
else if (p === 'lowest') {
|
|
12
|
+
return -3;
|
|
13
|
+
}
|
|
14
|
+
else if (p === 'lower') {
|
|
15
|
+
return -2;
|
|
16
|
+
}
|
|
17
|
+
else if (p === 'low') {
|
|
18
|
+
return -1;
|
|
19
|
+
}
|
|
20
|
+
else if (p === 'normal') {
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
else if (p === 'high') {
|
|
24
|
+
return 1;
|
|
25
|
+
}
|
|
26
|
+
else if (p === 'higher') {
|
|
27
|
+
return 2;
|
|
28
|
+
}
|
|
29
|
+
else if (p === 'highest') {
|
|
30
|
+
return 3;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
p = Number(p);
|
|
34
|
+
}
|
|
35
|
+
if (typeof p !== 'number' || Number.isNaN(p)) {
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
else if (p < -3) {
|
|
39
|
+
return -3;
|
|
40
|
+
}
|
|
41
|
+
else if (p > 3) {
|
|
42
|
+
return 3;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
return Math.trunc(p);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const maxInt = 2147483647;
|
|
49
|
+
export class Queue {
|
|
50
|
+
static LOWEST = -3;
|
|
51
|
+
static LOWER = -2;
|
|
52
|
+
static LOW = -1;
|
|
53
|
+
static NORMAL = 0;
|
|
54
|
+
static HIGH = 1;
|
|
55
|
+
static HIGHER = 2;
|
|
56
|
+
static HIGHEST = 3;
|
|
57
|
+
#concurrency;
|
|
58
|
+
stateView;
|
|
59
|
+
running = 0;
|
|
60
|
+
pending = 0;
|
|
61
|
+
counter = 0;
|
|
62
|
+
releasing = false;
|
|
63
|
+
deferred = 0;
|
|
64
|
+
// Queues are stored in order of priority, so index == priority + 3
|
|
65
|
+
queues = [
|
|
66
|
+
new FastQueue(), // 0 lowest
|
|
67
|
+
new FastQueue(), // 1 lower
|
|
68
|
+
new FastQueue(), // 2 low
|
|
69
|
+
new FastQueue(), // 3 normal
|
|
70
|
+
new FastQueue(), // 4 high
|
|
71
|
+
new FastQueue(), // 5 higher
|
|
72
|
+
new FastQueue(), // 6 highest
|
|
73
|
+
];
|
|
74
|
+
get concurrency() {
|
|
75
|
+
return this.#concurrency;
|
|
76
|
+
}
|
|
77
|
+
get stats() {
|
|
78
|
+
return {
|
|
79
|
+
pending: this.pending,
|
|
80
|
+
queues: this.queues.map((q) => ({ count: q.cnt })),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
getNextQueue() {
|
|
84
|
+
let queue = null;
|
|
85
|
+
this.counter = (this.counter + 1) & maxInt;
|
|
86
|
+
let idx = this.queues.length - 1;
|
|
87
|
+
if (this.counter & 0b0000001) {
|
|
88
|
+
idx = 6; // highest: 50%
|
|
89
|
+
}
|
|
90
|
+
else if (this.counter & 0b0000010) {
|
|
91
|
+
idx = 5; // higher: 25%
|
|
92
|
+
}
|
|
93
|
+
else if (this.counter & 0b0000100) {
|
|
94
|
+
idx = 4; // high: 12.5%
|
|
95
|
+
}
|
|
96
|
+
else if (this.counter & 0b0001000) {
|
|
97
|
+
idx = 3; // normal: 6.25%
|
|
98
|
+
}
|
|
99
|
+
else if (this.counter & 0b0010000) {
|
|
100
|
+
idx = 2; // low: 3.125%
|
|
101
|
+
}
|
|
102
|
+
else if (this.counter & 0b0100000) {
|
|
103
|
+
idx = 1; // lower: 1.5625%
|
|
104
|
+
}
|
|
105
|
+
else if (this.counter & 0b1000000) {
|
|
106
|
+
idx = 0; // lowest: 0.78%
|
|
107
|
+
}
|
|
108
|
+
for (let n = idx; n >= 0 && (queue == null || queue.cnt === 0); n--) {
|
|
109
|
+
queue = this.queues[n];
|
|
110
|
+
}
|
|
111
|
+
for (let n = this.queues.length - 1; n > idx && (queue == null || queue.cnt === 0); n--) {
|
|
112
|
+
queue = this.queues[n];
|
|
113
|
+
}
|
|
114
|
+
return queue;
|
|
115
|
+
}
|
|
116
|
+
static makeSharedState(concurrency) {
|
|
117
|
+
if (concurrency != null && (concurrency < 0 || !Number.isInteger(concurrency))) {
|
|
118
|
+
throw new Error('Invalid concurrency');
|
|
119
|
+
}
|
|
120
|
+
const stateBuffer = new SharedArrayBuffer(64);
|
|
121
|
+
const stateView = new Int32Array(stateBuffer);
|
|
122
|
+
Atomics.store(stateView, CONCURRENCY_INDEX, concurrency ?? -1);
|
|
123
|
+
return stateBuffer;
|
|
124
|
+
}
|
|
125
|
+
constructor(opts) {
|
|
126
|
+
if (opts instanceof SharedArrayBuffer) {
|
|
127
|
+
this.stateView = new Int32Array(opts);
|
|
128
|
+
this.#concurrency = Atomics.load(this.stateView, CONCURRENCY_INDEX);
|
|
129
|
+
if (this.#concurrency === -1) {
|
|
130
|
+
this.#concurrency = Infinity;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
this.#concurrency = opts?.concurrency ?? Infinity;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=queue.js.map
|
package/lib/queue.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Queue, type Priority } from './queue.ts';
|
|
2
|
+
export declare class Scheduler extends Queue {
|
|
3
|
+
run<T>(fn: () => Promise<T> | T, priority?: Priority): Promise<T>;
|
|
4
|
+
run<T, U>(fn: (opaque: U) => Promise<T> | T, priority: Priority, opaque: U): Promise<T>;
|
|
5
|
+
acquire(fn: () => unknown, priority?: Priority): void;
|
|
6
|
+
acquire<U>(fn: (opaque: U) => unknown, priority: Priority, opaque: U): void;
|
|
7
|
+
release(): void;
|
|
8
|
+
get stats(): {
|
|
9
|
+
deferred: number;
|
|
10
|
+
running: number;
|
|
11
|
+
pending: number;
|
|
12
|
+
queues: {
|
|
13
|
+
count: number;
|
|
14
|
+
}[];
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=scheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAA;AAI3E,qBAAa,SAAU,SAAQ,KAAK;IAClC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC;IACjE,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAwBvF,OAAO,CAAC,EAAE,EAAE,MAAM,OAAO,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI;IACrD,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI;IAkC3E,OAAO;IA6DP,IAAI,KAAK;;;;;;;MAOR;CACF"}
|
package/lib/scheduler.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { FastQueue, parsePriority, Queue } from "./queue.js";
|
|
2
|
+
const RUNNING_INDEX = 0;
|
|
3
|
+
export class Scheduler extends Queue {
|
|
4
|
+
run(fn, priority = Queue.NORMAL, opaque) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
this.acquire(async (o) => {
|
|
7
|
+
try {
|
|
8
|
+
resolve(await fn(o));
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
|
12
|
+
reject(err);
|
|
13
|
+
}
|
|
14
|
+
finally {
|
|
15
|
+
this.release();
|
|
16
|
+
}
|
|
17
|
+
}, priority, opaque);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
acquire(fn, priority = Queue.NORMAL, opaque) {
|
|
21
|
+
const p = parsePriority(priority);
|
|
22
|
+
const queue = this.queues[p + 3];
|
|
23
|
+
if (this.stateView) {
|
|
24
|
+
// Make sure we are always running at least one local job even if we might globally over subscribe.
|
|
25
|
+
if (this.running < 1) {
|
|
26
|
+
Atomics.add(this.stateView, RUNNING_INDEX, 1);
|
|
27
|
+
this.running += 1;
|
|
28
|
+
fn(opaque);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
// We use non atomic access here as an optimization and treat the concurrency limit as a soft limit.
|
|
32
|
+
if (this.stateView[RUNNING_INDEX] < this.concurrency) {
|
|
33
|
+
Atomics.add(this.stateView, RUNNING_INDEX, 1);
|
|
34
|
+
this.running += 1;
|
|
35
|
+
fn(opaque);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else if ((this.running < 1 && this.concurrency > 0) || this.running < this.concurrency) {
|
|
40
|
+
this.running += 1;
|
|
41
|
+
fn(opaque);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
queue.arr.push(fn, opaque);
|
|
45
|
+
queue.cnt += 1;
|
|
46
|
+
this.deferred += 1;
|
|
47
|
+
this.pending += 1;
|
|
48
|
+
}
|
|
49
|
+
release() {
|
|
50
|
+
let running;
|
|
51
|
+
if (this.running > 0) {
|
|
52
|
+
running = this.stateView
|
|
53
|
+
? Atomics.sub(this.stateView, RUNNING_INDEX, 1) - 1
|
|
54
|
+
: this.running - 1;
|
|
55
|
+
this.running -= 1;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// Gracefully handle user error...
|
|
59
|
+
running = this.stateView ? Atomics.load(this.stateView, RUNNING_INDEX) : this.running;
|
|
60
|
+
}
|
|
61
|
+
if (this.pending === 0 || this.releasing) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
this.releasing = true;
|
|
66
|
+
while (this.pending > 0 && (this.running === 0 || running < this.concurrency)) {
|
|
67
|
+
const queue = this.getNextQueue();
|
|
68
|
+
if (queue == null || queue.cnt === 0) {
|
|
69
|
+
throw new Error('Invariant violation: pending > 0 but no tasks in queues');
|
|
70
|
+
}
|
|
71
|
+
const fn = queue.arr[queue.idx];
|
|
72
|
+
queue.arr[queue.idx++] = null;
|
|
73
|
+
const opaque = queue.arr[queue.idx];
|
|
74
|
+
queue.arr[queue.idx++] = null;
|
|
75
|
+
queue.cnt -= 1;
|
|
76
|
+
if (queue.cnt === 0) {
|
|
77
|
+
queue.idx = 0;
|
|
78
|
+
queue.arr.length = 0;
|
|
79
|
+
}
|
|
80
|
+
else if (queue.idx > 1024) {
|
|
81
|
+
queue.arr.splice(0, queue.idx);
|
|
82
|
+
queue.idx = 0;
|
|
83
|
+
}
|
|
84
|
+
this.pending -= 1;
|
|
85
|
+
this.running += 1;
|
|
86
|
+
if (this.stateView) {
|
|
87
|
+
Atomics.add(this.stateView, RUNNING_INDEX, 1);
|
|
88
|
+
}
|
|
89
|
+
fn(opaque);
|
|
90
|
+
// Re-read running after fn() in case it synchronously called release()
|
|
91
|
+
running = this.stateView ? Atomics.load(this.stateView, RUNNING_INDEX) : this.running;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
// Throwing here is undefined behavior...
|
|
96
|
+
queueMicrotask(() => {
|
|
97
|
+
throw new Error('Scheduler task error', { cause: err });
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
this.releasing = false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
get stats() {
|
|
105
|
+
return {
|
|
106
|
+
deferred: this.deferred,
|
|
107
|
+
running: this.running,
|
|
108
|
+
pending: this.pending,
|
|
109
|
+
queues: this.queues.map((q) => ({ count: q.cnt })),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Transform } from 'node:stream';
|
|
2
|
+
import { Queue, type Priority } from './queue.ts';
|
|
3
|
+
export declare class Throttle extends Queue {
|
|
4
|
+
#private;
|
|
5
|
+
constructor(opts: SharedArrayBuffer | {
|
|
6
|
+
bytesPerSecond?: number;
|
|
7
|
+
});
|
|
8
|
+
static makeSharedState(bytesPerSecond: number): SharedArrayBuffer;
|
|
9
|
+
stream(priority?: Priority): Transform;
|
|
10
|
+
acquire(fn: () => unknown, bytes: number, priority?: Priority): boolean;
|
|
11
|
+
get stats(): {
|
|
12
|
+
tokens: number;
|
|
13
|
+
pending: number;
|
|
14
|
+
queues: {
|
|
15
|
+
count: number;
|
|
16
|
+
}[];
|
|
17
|
+
};
|
|
18
|
+
refill(): void;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=throttle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle.d.ts","sourceRoot":"","sources":["../src/throttle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAA4B,KAAK,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAA;AAI3E,qBAAa,QAAS,SAAQ,KAAK;;gBAMrB,IAAI,EAAE,iBAAiB,GAAG;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE;IAIjE,MAAM,CAAC,eAAe,CAAC,cAAc,EAAE,MAAM;IAI7C,MAAM,CAAC,QAAQ,GAAE,QAAuB;IAcxC,OAAO,CAAC,EAAE,EAAE,MAAM,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,GAAE,QAAuB,GAAG,OAAO;IA2BrF,IAAI,KAAK;;;;;;MAMR;IAcD,MAAM;CAqDP"}
|
package/lib/throttle.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Transform } from 'node:stream';
|
|
2
|
+
import { FastQueue, parsePriority, Queue } from "./queue.js";
|
|
3
|
+
const TOKENS_INDEX = 2;
|
|
4
|
+
export class Throttle extends Queue {
|
|
5
|
+
#singleThreadTokens = 0;
|
|
6
|
+
// Set last refill time to zero will mean that we start with a full bucket
|
|
7
|
+
#lastRefillTime = 0;
|
|
8
|
+
#drainRunning = false;
|
|
9
|
+
constructor(opts) {
|
|
10
|
+
super(opts instanceof SharedArrayBuffer ? opts : { concurrency: opts?.bytesPerSecond });
|
|
11
|
+
}
|
|
12
|
+
static makeSharedState(bytesPerSecond) {
|
|
13
|
+
return super.makeSharedState(bytesPerSecond);
|
|
14
|
+
}
|
|
15
|
+
stream(priority = Queue.NORMAL) {
|
|
16
|
+
return new Transform({
|
|
17
|
+
transform: (chunk, _encoding, callback) => {
|
|
18
|
+
this.acquire(() => {
|
|
19
|
+
callback(null, chunk);
|
|
20
|
+
}, chunk.length, priority);
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
acquire(fn, bytes, priority = Queue.NORMAL) {
|
|
25
|
+
// There is a possiblity that this check passes for two threads, but should only have passed for one of them.
|
|
26
|
+
// Given that the concurrency limit is a soft limit, this is not a problem and will just mean that we might briefly exceed the concurrency limit until the next refill.
|
|
27
|
+
if (this.pending === 0 && this.#tokens >= bytes) {
|
|
28
|
+
// Nothing in the queue, and enough tokens to run immediately.
|
|
29
|
+
this.#tokens -= bytes;
|
|
30
|
+
fn();
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
const p = parsePriority(priority);
|
|
34
|
+
const queue = this.queues[p + 3];
|
|
35
|
+
this.pending += 1;
|
|
36
|
+
this.deferred += 1;
|
|
37
|
+
queue.arr.push(fn, bytes);
|
|
38
|
+
queue.cnt += 1;
|
|
39
|
+
if (!this.#drainRunning) {
|
|
40
|
+
this.#drainRunning = true;
|
|
41
|
+
setImmediate(() => this.#drain());
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
get stats() {
|
|
46
|
+
return {
|
|
47
|
+
tokens: this.#tokens,
|
|
48
|
+
pending: this.pending,
|
|
49
|
+
queues: this.queues.map((q) => ({ count: q.cnt })),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
get #tokens() {
|
|
53
|
+
return this.stateView ? this.stateView[TOKENS_INDEX] : this.#singleThreadTokens;
|
|
54
|
+
}
|
|
55
|
+
set #tokens(value) {
|
|
56
|
+
if (this.stateView) {
|
|
57
|
+
Atomics.store(this.stateView, TOKENS_INDEX, value);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
this.#singleThreadTokens = value;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
refill() {
|
|
64
|
+
const currentToken = this.#tokens;
|
|
65
|
+
if (currentToken >= this.concurrency) {
|
|
66
|
+
// We are already at or above capacity, so just update the last refill time and return.
|
|
67
|
+
this.#lastRefillTime = performance.now();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const now = performance.now();
|
|
71
|
+
const elapsed = (now - this.#lastRefillTime) / 1000;
|
|
72
|
+
this.#tokens = Math.min(this.concurrency, currentToken + elapsed * this.concurrency);
|
|
73
|
+
this.#lastRefillTime = now;
|
|
74
|
+
}
|
|
75
|
+
#drain() {
|
|
76
|
+
while (this.pending > 0) {
|
|
77
|
+
const queue = this.getNextQueue();
|
|
78
|
+
if (queue == null || queue.cnt === 0) {
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
const bytes = queue.arr[queue.idx + 1];
|
|
82
|
+
const currentTokens = this.#tokens;
|
|
83
|
+
if (currentTokens < bytes) {
|
|
84
|
+
setTimeout(() => this.#drain(), 10);
|
|
85
|
+
// return to avoid setting drainRunning to false as we will continue draining after the timeout
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const fn = queue.arr[queue.idx];
|
|
89
|
+
queue.arr[queue.idx++] = null;
|
|
90
|
+
queue.arr[queue.idx++] = null;
|
|
91
|
+
queue.cnt -= 1;
|
|
92
|
+
this.#tokens -= bytes;
|
|
93
|
+
this.pending -= 1;
|
|
94
|
+
if (queue.cnt === 0) {
|
|
95
|
+
queue.idx = 0;
|
|
96
|
+
queue.arr.length = 0;
|
|
97
|
+
}
|
|
98
|
+
else if (queue.idx > 1024) {
|
|
99
|
+
queue.arr.splice(0, queue.idx);
|
|
100
|
+
queue.idx = 0;
|
|
101
|
+
}
|
|
102
|
+
fn();
|
|
103
|
+
}
|
|
104
|
+
this.#drainRunning = false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=throttle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle.js","sourceRoot":"","sources":["../src/throttle.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/scheduler",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./lib/index.js"
|
|
9
|
+
},
|
|
7
10
|
"files": [
|
|
8
11
|
"lib",
|
|
9
12
|
"README.md",
|
|
@@ -14,7 +17,7 @@
|
|
|
14
17
|
"access": "public"
|
|
15
18
|
},
|
|
16
19
|
"scripts": {
|
|
17
|
-
"build": "rimraf lib && tsc
|
|
20
|
+
"build": "rimraf lib && tsc",
|
|
18
21
|
"prepublishOnly": "yarn build",
|
|
19
22
|
"typecheck": "tsc --noEmit",
|
|
20
23
|
"test": "yarn build && node --test",
|
|
@@ -28,5 +31,5 @@
|
|
|
28
31
|
"rimraf": "^6.1.3",
|
|
29
32
|
"typescript": "^5.9.3"
|
|
30
33
|
},
|
|
31
|
-
"gitHead": "
|
|
34
|
+
"gitHead": "e9a232c708d1b9fbda3a040be054d79dfb64a26b"
|
|
32
35
|
}
|