@dcrosstech/dct-ts-utils 1.0.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.
@@ -0,0 +1,8 @@
1
+ export * from "./lib/queue.js";
2
+ export * from "./lib/fifoqueue.js";
3
+ export * from "./lib/heap.js";
4
+ export * from "./lib/priorityqueue.js";
5
+ export * from "./lib/stablepriorityqueue.js";
6
+ export * from "./lib/sleep.js";
7
+ export * from "./lib/semaphore.js";
8
+ export * from "./lib/retry.js";
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export * from "./lib/queue.js";
2
+ export * from "./lib/fifoqueue.js";
3
+ export * from "./lib/heap.js";
4
+ export * from "./lib/priorityqueue.js";
5
+ export * from "./lib/stablepriorityqueue.js";
6
+ export * from "./lib/sleep.js";
7
+ export * from "./lib/semaphore.js";
8
+ export * from "./lib/retry.js";
@@ -0,0 +1,12 @@
1
+ import { Queue } from "./queue";
2
+ export declare class FifoQueue<T> implements Queue<T> {
3
+ private q;
4
+ private head;
5
+ size(): number;
6
+ isEmpty(): boolean;
7
+ peek(): T | undefined;
8
+ enqueue(x: T): void;
9
+ dequeue(): T | undefined;
10
+ clear(): void;
11
+ toArray(): T[];
12
+ }
@@ -0,0 +1,31 @@
1
+ export class FifoQueue {
2
+ constructor() {
3
+ this.q = [];
4
+ this.head = 0;
5
+ }
6
+ size() { return this.q.length - this.head; }
7
+ isEmpty() { return this.size() === 0; }
8
+ peek() {
9
+ return this.isEmpty() ? undefined : this.q[this.head];
10
+ }
11
+ enqueue(x) {
12
+ this.q.push(x);
13
+ }
14
+ dequeue() {
15
+ if (this.isEmpty())
16
+ return undefined;
17
+ const x = this.q[this.head++];
18
+ if (this.head > 64 && this.head * 2 > this.q.length) {
19
+ this.q = this.q.slice(this.head);
20
+ this.head = 0;
21
+ }
22
+ return x;
23
+ }
24
+ clear() {
25
+ this.q.length = 0;
26
+ this.head = 0;
27
+ }
28
+ toArray() {
29
+ return this.q.slice(this.head);
30
+ }
31
+ }
@@ -0,0 +1,25 @@
1
+ export interface Heap<T> {
2
+ size(): number;
3
+ isEmpty(): boolean;
4
+ peek(): T | undefined;
5
+ push(value: T): number;
6
+ pop(): T | undefined;
7
+ clear(): void;
8
+ heapify(values: Iterable<T>): this;
9
+ toArray(): T[];
10
+ }
11
+ export declare class BinaryHeap<T> implements Heap<T> {
12
+ private heap;
13
+ private readonly compare;
14
+ constructor(compare?: (a: T, b: T) => number);
15
+ size(): number;
16
+ isEmpty(): boolean;
17
+ peek(): T | undefined;
18
+ push(value: T): number;
19
+ pop(): T | undefined;
20
+ clear(): void;
21
+ heapify(values: Iterable<T>): this;
22
+ toArray(): T[];
23
+ private siftUp;
24
+ private siftDown;
25
+ }
@@ -0,0 +1,73 @@
1
+ export class BinaryHeap {
2
+ constructor(compare) {
3
+ this.heap = [];
4
+ this.compare = compare ?? ((a, b) => a - b);
5
+ }
6
+ size() { return this.heap.length; }
7
+ isEmpty() { return this.heap.length === 0; }
8
+ peek() {
9
+ return this.heap.length ? this.heap[0] : undefined;
10
+ }
11
+ push(value) {
12
+ this.heap.push(value);
13
+ this.siftUp(this.heap.length - 1);
14
+ return this.heap.length;
15
+ }
16
+ pop() {
17
+ const n = this.heap.length;
18
+ if (n === 0)
19
+ return undefined;
20
+ const top = this.heap[0];
21
+ const last = this.heap.pop();
22
+ if (n > 1) {
23
+ this.heap[0] = last;
24
+ this.siftDown(0);
25
+ }
26
+ return top;
27
+ }
28
+ clear() {
29
+ this.heap.length = 0;
30
+ }
31
+ heapify(values) {
32
+ this.heap = Array.from(values);
33
+ for (let i = (this.heap.length >> 1) - 1; i >= 0; i--) {
34
+ this.siftDown(i);
35
+ }
36
+ return this;
37
+ }
38
+ toArray() {
39
+ return this.heap.slice();
40
+ }
41
+ siftUp(i) {
42
+ const { heap, compare } = this;
43
+ const x = heap[i];
44
+ while (i > 0) {
45
+ const p = (i - 1) >> 1;
46
+ const pv = heap[p];
47
+ if (compare(x, pv) >= 0)
48
+ break;
49
+ heap[i] = pv;
50
+ i = p;
51
+ }
52
+ heap[i] = x;
53
+ }
54
+ siftDown(i) {
55
+ const { heap, compare } = this;
56
+ const n = heap.length;
57
+ const x = heap[i];
58
+ while (true) {
59
+ const l = (i << 1) + 1;
60
+ if (l >= n)
61
+ break;
62
+ const r = l + 1;
63
+ let c = l;
64
+ if (r < n && compare(heap[r], heap[l]) < 0)
65
+ c = r;
66
+ if (compare(heap[c], x) >= 0)
67
+ break;
68
+ heap[i] = heap[c];
69
+ i = c;
70
+ }
71
+ heap[i] = x;
72
+ }
73
+ }
@@ -0,0 +1,16 @@
1
+ import { Queue } from "./queue";
2
+ export interface PriorityQueue<T> extends Queue<T> {
3
+ enqueue(value: T, priority?: number): void;
4
+ }
5
+ export declare class HeapPriorityQueue<T> implements PriorityQueue<T> {
6
+ private readonly heap;
7
+ private seq;
8
+ constructor();
9
+ size(): number;
10
+ isEmpty(): boolean;
11
+ peek(): T | undefined;
12
+ enqueue(value: T, priority?: number): void;
13
+ dequeue(): T | undefined;
14
+ clear(): void;
15
+ toArray(): T[];
16
+ }
@@ -0,0 +1,40 @@
1
+ import { BinaryHeap } from "./heap";
2
+ export class HeapPriorityQueue {
3
+ constructor() {
4
+ this.seq = 0;
5
+ this.heap = new BinaryHeap((a, b) => {
6
+ if (a.priority !== b.priority)
7
+ return a.priority - b.priority;
8
+ return a.seq - b.seq; // stable on ties
9
+ });
10
+ }
11
+ size() {
12
+ return this.heap.size();
13
+ }
14
+ isEmpty() {
15
+ return this.heap.isEmpty();
16
+ }
17
+ peek() {
18
+ return this.heap.peek()?.value;
19
+ }
20
+ enqueue(value, priority = 0) {
21
+ if (!Number.isFinite(priority)) {
22
+ throw new TypeError(`priority must be finite; got ${priority}`);
23
+ }
24
+ this.heap.push({
25
+ value,
26
+ priority: Math.trunc(priority),
27
+ seq: this.seq++,
28
+ });
29
+ }
30
+ dequeue() {
31
+ return this.heap.pop()?.value;
32
+ }
33
+ clear() {
34
+ this.heap.clear();
35
+ this.seq = 0;
36
+ }
37
+ toArray() {
38
+ return this.heap.toArray().map(n => n.value);
39
+ }
40
+ }
@@ -0,0 +1,9 @@
1
+ export interface Queue<T> {
2
+ size(): number;
3
+ isEmpty(): boolean;
4
+ peek(): T | undefined;
5
+ enqueue(x: T): void;
6
+ dequeue(): T | undefined;
7
+ clear(): void;
8
+ toArray(): T[];
9
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ export declare class RetryError<Args = unknown> extends Error {
2
+ readonly retryArgs: Args;
3
+ readonly finalError?: Error;
4
+ constructor(message: string, retryArgs: Args, finalError?: Error);
5
+ }
6
+ export declare class TerminalError extends Error {
7
+ constructor(message: string);
8
+ }
9
+ export type BackoffConfig = {
10
+ quantumMs?: number;
11
+ minDelayMs?: number;
12
+ maxDelayMs?: number;
13
+ maxRetries?: number;
14
+ factor?: number;
15
+ maxExponent?: number;
16
+ jitter?: number;
17
+ };
18
+ export declare function executeWithExponentialBackoff<Args, R>(cfg: BackoffConfig | undefined, fn: (args: Args) => Promise<R> | R, initialArgs: Args): Promise<R>;
@@ -0,0 +1,102 @@
1
+ import { sleep } from "./sleep";
2
+ export class RetryError extends Error {
3
+ constructor(message, retryArgs, finalError) {
4
+ super(message);
5
+ this.name = "RetryError";
6
+ this.retryArgs = retryArgs;
7
+ this.finalError = finalError;
8
+ }
9
+ }
10
+ export class TerminalError extends Error {
11
+ constructor(message) {
12
+ super(message);
13
+ this.name = "TerminalError";
14
+ }
15
+ }
16
+ function mustFinite(name, v, extra) {
17
+ if (!Number.isFinite(v))
18
+ throw new TypeError(`${name} must be finite; got ${v}`);
19
+ if (extra)
20
+ throw new TypeError(`${name} ${extra}; got ${v}`);
21
+ }
22
+ function normalizeConfig(cfg) {
23
+ const quantumMs = cfg?.quantumMs ?? 10;
24
+ mustFinite("quantumMs", quantumMs, quantumMs < 0 ? "must be >= 0" : "");
25
+ const q = Math.trunc(quantumMs);
26
+ const minDelayMs = cfg?.minDelayMs ?? q;
27
+ mustFinite("minDelayMs", minDelayMs, minDelayMs < 0 ? "must be >= 0" : "");
28
+ const minD = Math.trunc(minDelayMs);
29
+ const maxDelayMs = cfg?.maxDelayMs;
30
+ if (maxDelayMs !== undefined) {
31
+ mustFinite("maxDelayMs", maxDelayMs, maxDelayMs < 0 ? "must be >= 0" : "");
32
+ }
33
+ const maxD = maxDelayMs === undefined ? undefined : Math.trunc(maxDelayMs);
34
+ const maxRetries = cfg?.maxRetries;
35
+ if (maxRetries !== undefined) {
36
+ mustFinite("maxRetries", maxRetries, maxRetries < 0 ? "must be >= 0" : "");
37
+ }
38
+ const maxR = maxRetries === undefined ? undefined : Math.trunc(maxRetries);
39
+ const factor = cfg?.factor ?? 2;
40
+ mustFinite("factor", factor, factor <= 0 ? "must be > 0" : "");
41
+ const f = factor;
42
+ const maxExponent = cfg?.maxExponent;
43
+ if (maxExponent !== undefined) {
44
+ mustFinite("maxExponent", maxExponent, maxExponent < 0 ? "must be >= 0" : "");
45
+ }
46
+ const maxE = maxExponent === undefined ? undefined : Math.trunc(maxExponent);
47
+ const jitter = cfg?.jitter;
48
+ if (jitter !== undefined) {
49
+ mustFinite("jitter", jitter, jitter < 0 || jitter > 1 ? "must be in [0,1]" : "");
50
+ }
51
+ const j = jitter;
52
+ if (maxD !== undefined && maxD < minD) {
53
+ throw new RangeError(`maxDelayMs (${maxD}) < minDelayMs (${minD})`);
54
+ }
55
+ return { q, minD, maxD, maxR, f, maxE, j };
56
+ }
57
+ function computeDelayMs(q, minD, maxD, factor, maxE, jitter, retryAttempt) {
58
+ const k = maxE === undefined ? retryAttempt : Math.min(retryAttempt, maxE);
59
+ const pow = Math.pow(factor, k);
60
+ const upperSteps = Number.isFinite(pow) ? Math.max(0, Math.floor(pow) - 1) : Number.MAX_SAFE_INTEGER;
61
+ const steps = upperSteps === 0 ? 0 : Math.floor(Math.random() * (upperSteps + 1));
62
+ let d = steps * q;
63
+ if (jitter !== undefined && d > 0) {
64
+ const r = (Math.random() * 2 - 1) * jitter;
65
+ d = d * (1 + r);
66
+ }
67
+ if (d < minD)
68
+ d = minD;
69
+ if (maxD !== undefined && d > maxD)
70
+ d = maxD;
71
+ return Math.trunc(d);
72
+ }
73
+ export async function executeWithExponentialBackoff(cfg, fn, initialArgs) {
74
+ const { q, minD, maxD, maxR, f, maxE, j } = normalizeConfig(cfg);
75
+ let args = initialArgs;
76
+ for (let attemptIndex = 0;; attemptIndex++) {
77
+ try {
78
+ return await fn(args);
79
+ }
80
+ catch (err) {
81
+ if (err instanceof TerminalError)
82
+ throw err;
83
+ let finalOnExhaustion;
84
+ if (err instanceof RetryError) {
85
+ args = err.retryArgs;
86
+ finalOnExhaustion = err.finalError;
87
+ }
88
+ // Out of retries?
89
+ if (maxR !== undefined && attemptIndex >= maxR) {
90
+ if (finalOnExhaustion) {
91
+ throw finalOnExhaustion;
92
+ }
93
+ throw err;
94
+ }
95
+ const retryAttempt = attemptIndex + 1; // 1-based retry count
96
+ const delayMs = computeDelayMs(q, minD, maxD, f, maxE, j, retryAttempt);
97
+ if (delayMs > 0) {
98
+ await sleep(delayMs);
99
+ }
100
+ }
101
+ }
102
+ }
@@ -0,0 +1,33 @@
1
+ export type SemaphoreRunOptions = {
2
+ weight?: number;
3
+ priority?: number;
4
+ };
5
+ export type SemaphoreOptions = {
6
+ usePriorityQueue?: boolean;
7
+ };
8
+ type WorkItem<R> = {
9
+ weight: number;
10
+ priority: number;
11
+ fn: () => Promise<R> | R;
12
+ resolve: (v: R) => void;
13
+ reject: (e: unknown) => void;
14
+ };
15
+ export declare class Semaphore {
16
+ private max;
17
+ private count;
18
+ private inFlightWeight;
19
+ private readonly queue;
20
+ private readonly pqueue;
21
+ constructor(max: number, opts?: SemaphoreOptions, initialCount?: number);
22
+ getMax(): number;
23
+ getCount(): number;
24
+ setMax(newMax: number): void;
25
+ setCount(newCount: number): void;
26
+ run<R>(fn: () => Promise<R> | R, opts?: SemaphoreRunOptions): Promise<R>;
27
+ private drain;
28
+ protected releaseAndScheduleDrain(weight: number): void;
29
+ protected completeResolve<R>(item: WorkItem<R>, value: R): void;
30
+ protected completeReject<R>(item: WorkItem<R>, error: unknown): void;
31
+ private start;
32
+ }
33
+ export {};
@@ -0,0 +1,121 @@
1
+ import { StablePriorityQueue } from "./stablepriorityqueue";
2
+ import { FifoQueue } from "./fifoqueue";
3
+ export class Semaphore {
4
+ constructor(max, opts = {}, initialCount) {
5
+ this.inFlightWeight = 0;
6
+ if (!Number.isFinite(max) || max < 0) {
7
+ throw new TypeError(`max must be finite and >= 0; got ${max}`);
8
+ }
9
+ this.max = Math.trunc(max);
10
+ const start = initialCount ?? this.max;
11
+ if (!Number.isFinite(start)) {
12
+ throw new TypeError(`initialCount must be finite; got ${start}`);
13
+ }
14
+ this.count = Math.trunc(start);
15
+ if (opts.usePriorityQueue) {
16
+ const pq = new StablePriorityQueue();
17
+ this.queue = pq;
18
+ this.pqueue = pq;
19
+ }
20
+ else {
21
+ this.queue = new FifoQueue();
22
+ this.pqueue = null;
23
+ }
24
+ }
25
+ getMax() { return this.max; }
26
+ getCount() { return this.count; }
27
+ setMax(newMax) {
28
+ if (!Number.isFinite(newMax) || newMax < 0) {
29
+ throw new TypeError(`newMax must be finite and >= 0; got ${newMax}`);
30
+ }
31
+ newMax = Math.trunc(newMax);
32
+ if (newMax < this.inFlightWeight) {
33
+ throw new RangeError(`newMax (${newMax}) < inFlightWeight (${this.inFlightWeight})`);
34
+ }
35
+ for (const item of this.queue.toArray()) {
36
+ if (item.weight > newMax) {
37
+ throw new RangeError(`queued weight ${item.weight} exceeds newMax ${newMax}`);
38
+ }
39
+ }
40
+ this.max = newMax;
41
+ this.drain();
42
+ }
43
+ setCount(newCount) {
44
+ if (!Number.isFinite(newCount)) {
45
+ throw new TypeError(`newCount must be finite; got ${newCount}`);
46
+ }
47
+ this.count = Math.trunc(newCount);
48
+ for (const item of this.queue.toArray()) {
49
+ if (item.weight > this.max) {
50
+ throw new RangeError(`queued weight ${item.weight} exceeds max ${this.max}`);
51
+ }
52
+ }
53
+ this.drain();
54
+ }
55
+ run(fn, opts = {}) {
56
+ const weight = Math.trunc(opts.weight ?? 1);
57
+ const priority = Math.trunc(opts.priority ?? 0);
58
+ if (!Number.isFinite(weight) || weight <= 0) {
59
+ throw new TypeError(`weight must be a finite integer > 0; got ${opts.weight}`);
60
+ }
61
+ if (!Number.isFinite(priority)) {
62
+ throw new TypeError(`priority must be finite; got ${opts.priority}`);
63
+ }
64
+ if (weight > this.max) {
65
+ throw new RangeError(`weight (${weight}) exceeds semaphore max (${this.max})`);
66
+ }
67
+ return new Promise((resolve, reject) => {
68
+ const item = { weight, priority, fn, resolve, reject };
69
+ // Don't bypass queued work: only run immediately if queue is empty.
70
+ if (this.queue.isEmpty() && this.count >= weight) {
71
+ this.start(item);
72
+ return;
73
+ }
74
+ if (this.pqueue)
75
+ this.pqueue.enqueue(item, priority);
76
+ else
77
+ this.queue.enqueue(item);
78
+ this.drain();
79
+ });
80
+ }
81
+ drain() {
82
+ while (true) {
83
+ const head = this.queue.peek();
84
+ if (!head)
85
+ return;
86
+ if (this.count < head.weight)
87
+ return;
88
+ this.start(this.queue.dequeue());
89
+ }
90
+ }
91
+ releaseAndScheduleDrain(weight) {
92
+ this.count += weight;
93
+ this.inFlightWeight -= weight;
94
+ this.drain();
95
+ }
96
+ completeResolve(item, value) {
97
+ this.releaseAndScheduleDrain(item.weight);
98
+ item.resolve(value);
99
+ }
100
+ completeReject(item, error) {
101
+ this.releaseAndScheduleDrain(item.weight);
102
+ item.reject(error);
103
+ }
104
+ start(item) {
105
+ this.count -= item.weight;
106
+ this.inFlightWeight += item.weight;
107
+ let r;
108
+ try {
109
+ r = item.fn();
110
+ }
111
+ catch (e) {
112
+ this.completeReject(item, e);
113
+ return;
114
+ }
115
+ if (r && typeof r.then === "function") {
116
+ r.then((v) => this.completeResolve(item, v), (e) => this.completeReject(item, e));
117
+ return;
118
+ }
119
+ this.completeResolve(item, r);
120
+ }
121
+ }
@@ -0,0 +1,10 @@
1
+ export declare class CancellationError extends Error {
2
+ readonly remainingMs: number;
3
+ constructor(message: string, remainingMs: number);
4
+ }
5
+ export interface SleepPromise extends Promise<void> {
6
+ cancel(reason?: unknown): boolean;
7
+ hasCompleted(): boolean;
8
+ remainingTime(): number;
9
+ }
10
+ export declare function sleep(durationMs: number): SleepPromise;
@@ -0,0 +1,60 @@
1
+ export class CancellationError extends Error {
2
+ constructor(message, remainingMs) {
3
+ super(message);
4
+ this.name = "CancellationError";
5
+ this.remainingMs = remainingMs;
6
+ }
7
+ }
8
+ export function sleep(durationMs) {
9
+ if (!Number.isFinite(durationMs)) {
10
+ throw new TypeError(`durationMs must be finite; got ${durationMs}`);
11
+ }
12
+ const delay = Math.max(0, Math.trunc(durationMs));
13
+ const startMs = Date.now();
14
+ let completed = false;
15
+ let cancelled = false;
16
+ let executedAtMs = null;
17
+ let cancelledAtMs = null;
18
+ let timer = null;
19
+ let resolveFn;
20
+ let rejectFn;
21
+ const promise = new Promise((resolve, reject) => {
22
+ resolveFn = resolve;
23
+ rejectFn = reject;
24
+ timer = setTimeout(() => {
25
+ if (cancelled)
26
+ return;
27
+ completed = true;
28
+ executedAtMs = Date.now();
29
+ resolve();
30
+ }, delay);
31
+ });
32
+ function remainingAt(nowMs) {
33
+ if (cancelled && cancelledAtMs != null) {
34
+ return delay - (cancelledAtMs - startMs);
35
+ }
36
+ if (completed) {
37
+ const exec = executedAtMs ?? startMs + delay;
38
+ return -(nowMs - exec);
39
+ }
40
+ const r = delay - (nowMs - startMs);
41
+ return r <= 0 ? 0 : r;
42
+ }
43
+ promise.cancel = (reason) => {
44
+ if (completed || cancelled)
45
+ return false;
46
+ cancelled = true;
47
+ cancelledAtMs = Date.now();
48
+ if (timer) {
49
+ clearTimeout(timer);
50
+ timer = null;
51
+ }
52
+ const remaining = remainingAt(cancelledAtMs);
53
+ rejectFn(reason ??
54
+ new CancellationError(`Sleep cancelled with ${remaining}ms remaining`, remaining));
55
+ return true;
56
+ };
57
+ promise.hasCompleted = () => completed;
58
+ promise.remainingTime = () => remainingAt(Date.now());
59
+ return promise;
60
+ }
@@ -0,0 +1,13 @@
1
+ import { PriorityQueue } from "./priorityqueue";
2
+ export declare class StablePriorityQueue<T> implements PriorityQueue<T> {
3
+ private readonly heap;
4
+ private seq;
5
+ constructor();
6
+ size(): number;
7
+ isEmpty(): boolean;
8
+ peek(): T | undefined;
9
+ enqueue(value: T, priority?: number): void;
10
+ dequeue(): T | undefined;
11
+ clear(): void;
12
+ toArray(): T[];
13
+ }
@@ -0,0 +1,40 @@
1
+ import { BinaryHeap } from "./heap";
2
+ export class StablePriorityQueue {
3
+ constructor() {
4
+ this.seq = 0;
5
+ this.heap = new BinaryHeap((a, b) => {
6
+ if (a.priority !== b.priority)
7
+ return a.priority - b.priority;
8
+ return a.seq - b.seq; // FIFO on ties
9
+ });
10
+ }
11
+ size() {
12
+ return this.heap.size();
13
+ }
14
+ isEmpty() {
15
+ return this.heap.isEmpty();
16
+ }
17
+ peek() {
18
+ return this.heap.peek()?.value;
19
+ }
20
+ enqueue(value, priority = 0) {
21
+ if (!Number.isFinite(priority)) {
22
+ throw new TypeError(`priority must be finite; got ${priority}`);
23
+ }
24
+ this.heap.push({
25
+ value,
26
+ priority: Math.trunc(priority),
27
+ seq: this.seq++,
28
+ });
29
+ }
30
+ dequeue() {
31
+ return this.heap.pop()?.value;
32
+ }
33
+ clear() {
34
+ this.heap.clear();
35
+ this.seq = 0;
36
+ }
37
+ toArray() {
38
+ return this.heap.toArray().map(n => n.value);
39
+ }
40
+ }
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@dcrosstech/dct-ts-utils",
3
+ "version": "1.0.0",
4
+ "description": "Common TypeScript utilities (sleep, semaphore, queues, heaps, exponential backoff)",
5
+ "license": "ISC",
6
+ "author": "David Cross <david@crossfamilyweb.com>",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://git.dcrosstech.com/public/dct-ts-utils"
10
+ },
11
+ "keywords": [
12
+ "sleep",
13
+ "semaphore",
14
+ "exponential-backoff",
15
+ "priority-queue",
16
+ "heap",
17
+ "queue",
18
+ "typescript",
19
+ "async"
20
+ ],
21
+ "type": "module",
22
+ "sideEffects": false,
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "main": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "import": "./dist/index.js"
32
+ },
33
+ "./fifoqueue": {
34
+ "types": "./dist/lib/fifoqueue.d.ts",
35
+ "import": "./dist/lib/fifoqueue.js"
36
+ },
37
+ "./heap": {
38
+ "types": "./dist/lib/heap.d.ts",
39
+ "import": "./dist/lib/heap.js"
40
+ },
41
+ "./priorityqueue": {
42
+ "types": "./dist/lib/priorityqueue.d.ts",
43
+ "import": "./dist/lib/priorityqueue.js"
44
+ },
45
+ "./queue": {
46
+ "types": "./dist/lib/queue.d.ts",
47
+ "import": "./dist/lib/queue.js"
48
+ },
49
+ "./retry": {
50
+ "types": "./dist/lib/retry.d.ts",
51
+ "import": "./dist/lib/retry.js"
52
+ },
53
+ "./semaphore": {
54
+ "types": "./dist/lib/semaphore.d.ts",
55
+ "import": "./dist/lib/semaphore.js"
56
+ },
57
+ "./sleep": {
58
+ "types": "./dist/lib/sleep.d.ts",
59
+ "import": "./dist/lib/sleep.js"
60
+ },
61
+ "./stablepriorityqueue": {
62
+ "types": "./dist/lib/stablepriorityqueue.d.ts",
63
+ "import": "./dist/lib/stablepriorityqueue.js"
64
+ }
65
+ },
66
+ "publishConfig": {
67
+ "access": "public"
68
+ },
69
+ "scripts": {
70
+ "clean": "rm -rf dist",
71
+ "build": "npm run clean && tsc -p tsconfig.json",
72
+ "prepublishOnly": "npm run build",
73
+ "test": "echo \"No tests specified\" && exit 0"
74
+ },
75
+ "devDependencies": {
76
+ "typescript": "^5.4.0"
77
+ }
78
+ }