@dcrosstech/dct-ts-utils 1.0.0 → 1.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/README.md +227 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# @dcrosstech/dct-ts-utils
|
|
2
|
+
|
|
3
|
+
A small collection of TypeScript-first async and data-structure utilities,
|
|
4
|
+
designed to simple and extendible; focused primarily on clean and simple
|
|
5
|
+
implementations without prerequisites, and simple usese.
|
|
6
|
+
|
|
7
|
+
This package is ESM-only.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- Cancellable sleep() with remaining-time inspection
|
|
14
|
+
- Weighted semaphore with optional priority scheduling
|
|
15
|
+
- FIFO and stable priority queues
|
|
16
|
+
- Binary heap implementation
|
|
17
|
+
- Exponential backoff executor with:
|
|
18
|
+
- randomized exponential delay (Ethernet-style windowing)
|
|
19
|
+
- retry-state propagation
|
|
20
|
+
- explicit terminal failure control
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
npm install @dcrosstech/dct-ts-utils
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Imports
|
|
31
|
+
|
|
32
|
+
You can import from the package root:
|
|
33
|
+
|
|
34
|
+
import { Semaphore, sleep } from "@dcrosstech/dct-ts-utils";
|
|
35
|
+
|
|
36
|
+
Or import individual modules directly
|
|
37
|
+
|
|
38
|
+
import { Semaphore } from "@dcrosstech/dct-ts-utils/semaphore";
|
|
39
|
+
import { sleep } from "@dcrosstech/dct-ts-utils/sleep";
|
|
40
|
+
import { executeWithExponentialBackoff } from "@dcrosstech/dct-ts-utils/retry";
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Module Usage Examples
|
|
45
|
+
|
|
46
|
+
### sleep
|
|
47
|
+
|
|
48
|
+
A cancellable async sleep primitive.
|
|
49
|
+
|
|
50
|
+
import { sleep } from "@dcrosstech/dct-ts-utils";
|
|
51
|
+
|
|
52
|
+
const s = sleep(1000);
|
|
53
|
+
|
|
54
|
+
setTimeout(() => {
|
|
55
|
+
s.cancel();
|
|
56
|
+
}, 100);
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
await s;
|
|
60
|
+
} catch (e) {
|
|
61
|
+
// sleep was cancelled
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
Available helpers:
|
|
65
|
+
- cancel(reason?)
|
|
66
|
+
- hasCompleted()
|
|
67
|
+
- remainingTime()
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
### Semaphore
|
|
72
|
+
|
|
73
|
+
A weighted semaphore that runs functions without requiring explicit acquire/release calls.
|
|
74
|
+
|
|
75
|
+
import { Semaphore } from "@dcrosstech/dct-ts-utils";
|
|
76
|
+
|
|
77
|
+
const sem = new Semaphore(2);
|
|
78
|
+
|
|
79
|
+
await Promise.all([
|
|
80
|
+
sem.run(async () => {
|
|
81
|
+
// runs immediately
|
|
82
|
+
}),
|
|
83
|
+
sem.run(async () => {
|
|
84
|
+
// runs immediately
|
|
85
|
+
}),
|
|
86
|
+
sem.run(async () => {
|
|
87
|
+
// queued until capacity is available
|
|
88
|
+
}),
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
With weights and priority scheduling:
|
|
92
|
+
|
|
93
|
+
const sem = new Semaphore(3, { usePriorityQueue: true });
|
|
94
|
+
|
|
95
|
+
sem.run(workA, { weight: 2, priority: 10 });
|
|
96
|
+
sem.run(workB, { weight: 1, priority: 0 });
|
|
97
|
+
|
|
98
|
+
The semaphore:
|
|
99
|
+
- never bypasses the queue head (prevents starvation)
|
|
100
|
+
- supports dynamic capacity changes
|
|
101
|
+
- allows negative transient counts when adjusting capacity
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### Queue and FifoQueue
|
|
106
|
+
|
|
107
|
+
Minimal FIFO queue abstraction.
|
|
108
|
+
|
|
109
|
+
import { FifoQueue } from "@dcrosstech/dct-ts-utils";
|
|
110
|
+
|
|
111
|
+
const q = new FifoQueue<number>();
|
|
112
|
+
|
|
113
|
+
q.enqueue(1);
|
|
114
|
+
q.enqueue(2);
|
|
115
|
+
|
|
116
|
+
q.dequeue(); // 1
|
|
117
|
+
q.dequeue(); // 2
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### StablePriorityQueue
|
|
122
|
+
|
|
123
|
+
A priority queue with FIFO ordering among equal-priority elements.
|
|
124
|
+
|
|
125
|
+
import { StablePriorityQueue } from "@dcrosstech/dct-ts-utils";
|
|
126
|
+
|
|
127
|
+
const q = new StablePriorityQueue<string>();
|
|
128
|
+
|
|
129
|
+
q.enqueue("low", 10);
|
|
130
|
+
q.enqueue("high", 0);
|
|
131
|
+
q.enqueue("also-high", 0);
|
|
132
|
+
|
|
133
|
+
q.dequeue(); // "high"
|
|
134
|
+
q.dequeue(); // "also-high"
|
|
135
|
+
q.dequeue(); // "low"
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
### BinaryHeap
|
|
140
|
+
|
|
141
|
+
An array-backed binary heap with a custom comparator.
|
|
142
|
+
|
|
143
|
+
import { BinaryHeap } from "@dcrosstech/dct-ts-utils";
|
|
144
|
+
|
|
145
|
+
const heap = new BinaryHeap<number>((a, b) => a - b);
|
|
146
|
+
|
|
147
|
+
heap.push(3);
|
|
148
|
+
heap.push(1);
|
|
149
|
+
heap.push(2);
|
|
150
|
+
|
|
151
|
+
heap.pop(); // 1
|
|
152
|
+
heap.pop(); // 2
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
### Exponential Backoff Executor
|
|
157
|
+
|
|
158
|
+
Retry an operation with randomized exponential backoff.
|
|
159
|
+
|
|
160
|
+
import {
|
|
161
|
+
executeWithExponentialBackoff,
|
|
162
|
+
RetryError,
|
|
163
|
+
TerminalError
|
|
164
|
+
} from "@dcrosstech/dct-ts-utils/retry";
|
|
165
|
+
|
|
166
|
+
await executeWithExponentialBackoff(
|
|
167
|
+
{
|
|
168
|
+
quantumMs: 10,
|
|
169
|
+
maxRetries: 5
|
|
170
|
+
},
|
|
171
|
+
async (state) => {
|
|
172
|
+
if (state.transientFailure) {
|
|
173
|
+
throw new RetryError("retrying", nextState(state));
|
|
174
|
+
}
|
|
175
|
+
if (state.fatalFailure) {
|
|
176
|
+
throw new TerminalError("fatal error");
|
|
177
|
+
}
|
|
178
|
+
return state.result;
|
|
179
|
+
},
|
|
180
|
+
initialState
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
Supports:
|
|
184
|
+
- unbounded or capped retries
|
|
185
|
+
- Ethernet-style randomized backoff windows
|
|
186
|
+
- state propagation between retries
|
|
187
|
+
- terminal error substitution on retry exhaustion
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
### Semaphore fairness
|
|
192
|
+
The semaphore never bypasses the queue head.
|
|
193
|
+
If the head item cannot run due to insufficient capacity, later items are not allowed to run, even if they would fit.
|
|
194
|
+
|
|
195
|
+
This prevents starvation and preserves ordering guarantees.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
### Exponential backoff model
|
|
200
|
+
The retry executor uses an Ethernet-style exponential backoff:
|
|
201
|
+
|
|
202
|
+
- On retry n, choose a random integer in [0 .. factor^k - 1]
|
|
203
|
+
- Multiply by a fixed time quantum
|
|
204
|
+
- Enforce minimum and optional maximum delay
|
|
205
|
+
|
|
206
|
+
This avoids thundering-herd behavior while remaining simple and predictable.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Compatibility
|
|
211
|
+
|
|
212
|
+
- Node.js ≥ 16
|
|
213
|
+
- ESM-only (no require() support)
|
|
214
|
+
- TypeScript typings included
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## License
|
|
219
|
+
|
|
220
|
+
ISC
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Author
|
|
225
|
+
|
|
226
|
+
David Cross
|
|
227
|
+
https://git.dcrosstech.com/public/dct-ts-utils
|
package/package.json
CHANGED