@depthbomb/common 1.3.2 → 2.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.
- package/README.md +286 -1
- package/dist/collections.cjs +1 -0
- package/dist/{queue.d.cts → collections.d.cts} +28 -5
- package/dist/{queue.d.mts → collections.d.mts} +28 -5
- package/dist/collections.mjs +1 -0
- package/dist/{fn.d.cts → functional.d.cts} +1 -1
- package/dist/{fn.d.mts → functional.d.mts} +1 -1
- package/dist/guards.cjs +1 -0
- package/dist/guards.d.cts +7 -0
- package/dist/guards.d.mts +7 -0
- package/dist/guards.mjs +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +12 -7
- package/dist/index.d.mts +12 -7
- package/dist/index.mjs +1 -1
- package/dist/lazy.cjs +1 -1
- package/dist/lazy.d.cts +1 -5
- package/dist/lazy.d.mts +1 -5
- package/dist/lazy.mjs +1 -1
- package/dist/number.cjs +1 -0
- package/dist/number.d.cts +8 -0
- package/dist/number.d.mts +8 -0
- package/dist/number.mjs +1 -0
- package/dist/promise.cjs +1 -0
- package/dist/promise.d.cts +48 -0
- package/dist/promise.d.mts +48 -0
- package/dist/promise.mjs +1 -0
- package/dist/random.cjs +1 -0
- package/dist/random.d.cts +11 -0
- package/dist/random.d.mts +11 -0
- package/dist/random.mjs +1 -0
- package/dist/state.cjs +1 -0
- package/dist/state.d.cts +88 -0
- package/dist/state.d.mts +88 -0
- package/dist/state.mjs +1 -0
- package/dist/timing.cjs +1 -0
- package/dist/timing.d.cts +71 -0
- package/dist/timing.d.mts +71 -0
- package/dist/timing.mjs +1 -0
- package/dist/typing.cjs +1 -0
- package/dist/typing.d.cts +26 -0
- package/dist/typing.d.mts +26 -0
- package/dist/typing.mjs +1 -0
- package/dist/url.cjs +1 -0
- package/dist/{urllib.d.cts → url.d.cts} +12 -2
- package/dist/{urllib.d.mts → url.d.mts} +12 -2
- package/dist/url.mjs +1 -0
- package/package.json +103 -83
- package/dist/async.cjs +0 -1
- package/dist/async.d.cts +0 -44
- package/dist/async.d.mts +0 -44
- package/dist/async.mjs +0 -1
- package/dist/queue.cjs +0 -1
- package/dist/queue.mjs +0 -1
- package/dist/types.cjs +0 -1
- package/dist/types.d.cts +0 -9
- package/dist/types.d.mts +0 -9
- package/dist/types.mjs +0 -1
- package/dist/urllib.cjs +0 -1
- package/dist/urllib.mjs +0 -1
- /package/dist/{fn.cjs → functional.cjs} +0 -0
- /package/dist/{fn.mjs → functional.mjs} +0 -0
package/README.md
CHANGED
|
@@ -6,4 +6,289 @@ A set of common utilities for TypeScript/JavaScript that I use in my projects.
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## Modules
|
|
10
|
+
|
|
11
|
+
### `timing`
|
|
12
|
+
|
|
13
|
+
Timing and timeout flow-control helpers.
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import {
|
|
17
|
+
timeout,
|
|
18
|
+
rejectionTimeout,
|
|
19
|
+
pollUntil,
|
|
20
|
+
withTimeout,
|
|
21
|
+
retry,
|
|
22
|
+
abortAfter,
|
|
23
|
+
withAbort,
|
|
24
|
+
raceSignals,
|
|
25
|
+
RetryJitter,
|
|
26
|
+
} from '@depthbomb/common/timing';
|
|
27
|
+
|
|
28
|
+
await timeout(250);
|
|
29
|
+
await rejectionTimeout(100).catch(() => {});
|
|
30
|
+
|
|
31
|
+
let ready = false;
|
|
32
|
+
setTimeout(() => { ready = true; }, 200);
|
|
33
|
+
await pollUntil(() => ready, 50, 1_000);
|
|
34
|
+
|
|
35
|
+
const value = await withTimeout(Promise.resolve('ok'), 500);
|
|
36
|
+
|
|
37
|
+
const data = await retry(
|
|
38
|
+
async (attempt) => {
|
|
39
|
+
const response = await fetch('https://example.com/data');
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
throw new Error(`request failed on attempt ${attempt}`);
|
|
42
|
+
}
|
|
43
|
+
return response.json();
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
attempts: 4,
|
|
47
|
+
baseMs: 100,
|
|
48
|
+
maxMs: 1_000,
|
|
49
|
+
jitter: RetryJitter.Full,
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const signal = abortAfter(1_000);
|
|
54
|
+
await withAbort(fetch('https://example.com/slow'), signal);
|
|
55
|
+
|
|
56
|
+
const parent = new AbortController();
|
|
57
|
+
const child = abortAfter(500);
|
|
58
|
+
const combined = raceSignals(parent.signal, child);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### `promise`
|
|
62
|
+
|
|
63
|
+
Promise composition helpers, including detailed settled results and concurrency-limited execution.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { allSettledSuccessful, allSettledDetailed, sequential, pool, pMap } from '@depthbomb/common/promise';
|
|
67
|
+
|
|
68
|
+
const successful = await allSettledSuccessful([
|
|
69
|
+
Promise.resolve(1),
|
|
70
|
+
Promise.reject(new Error('x')),
|
|
71
|
+
Promise.resolve(2)
|
|
72
|
+
]); // [1, 2]
|
|
73
|
+
|
|
74
|
+
const ordered = await sequential([
|
|
75
|
+
async () => 'first',
|
|
76
|
+
async () => 'second'
|
|
77
|
+
]); // ['first', 'second']
|
|
78
|
+
|
|
79
|
+
const detailed = await allSettledDetailed([
|
|
80
|
+
Promise.resolve('ok'),
|
|
81
|
+
Promise.reject(new Error('x')),
|
|
82
|
+
]); // { results, fulfilled: ['ok'], rejected: [Error('x')] }
|
|
83
|
+
|
|
84
|
+
const pooled = await pool([
|
|
85
|
+
async () => 1,
|
|
86
|
+
async () => 2,
|
|
87
|
+
async () => 3,
|
|
88
|
+
], { concurrency: 2 });
|
|
89
|
+
|
|
90
|
+
const mapped = await pMap([1, 2, 3], async (v) => v * 10, { concurrency: 2 });
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### `decorators`
|
|
94
|
+
|
|
95
|
+
TypeScript decorators for reusable behavior. Currently includes a TTL-based `cache` decorator for memoizing method results per instance and argument set.
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { cache } from '@depthbomb/common/decorators';
|
|
99
|
+
|
|
100
|
+
class Service {
|
|
101
|
+
calls = 0;
|
|
102
|
+
|
|
103
|
+
@cache(1_000)
|
|
104
|
+
getUser(id: string) {
|
|
105
|
+
this.calls++;
|
|
106
|
+
return { id, calls: this.calls };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const s = new Service();
|
|
111
|
+
s.getUser('1');
|
|
112
|
+
s.getUser('1'); // cached for 1 second
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### `state`
|
|
116
|
+
|
|
117
|
+
State primitives: `ResettableValue`, `Flag`, `resettableLazy`, and `resettableLazyAsync`.
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
import { Flag, ResettableValue, resettableLazy, resettableLazyAsync } from '@depthbomb/common/state';
|
|
121
|
+
|
|
122
|
+
const flag = new Flag();
|
|
123
|
+
flag.setTrue();
|
|
124
|
+
flag.toggle();
|
|
125
|
+
flag.reset();
|
|
126
|
+
|
|
127
|
+
const retries = new ResettableValue(3);
|
|
128
|
+
retries.set(1);
|
|
129
|
+
retries.reset(); // back to 3
|
|
130
|
+
|
|
131
|
+
const counter = resettableLazy(() => Math.random());
|
|
132
|
+
const first = counter.get();
|
|
133
|
+
counter.reset();
|
|
134
|
+
const second = counter.get(); // recomputed
|
|
135
|
+
|
|
136
|
+
const tokenCache = resettableLazyAsync(async () => {
|
|
137
|
+
const response = await fetch('https://example.com/token');
|
|
138
|
+
return response.text();
|
|
139
|
+
});
|
|
140
|
+
const token = await tokenCache.get();
|
|
141
|
+
tokenCache.reset();
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### `functional`
|
|
145
|
+
|
|
146
|
+
General function utilities. Currently includes `once`, which ensures a function runs only on its first invocation and then reuses the same result.
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
import { once } from '@depthbomb/common/functional';
|
|
150
|
+
|
|
151
|
+
const init = once(() => ({ startedAt: Date.now() }));
|
|
152
|
+
|
|
153
|
+
const a = init();
|
|
154
|
+
const b = init();
|
|
155
|
+
console.log(a === b); // true
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### `lazy`
|
|
159
|
+
|
|
160
|
+
Lazy initialization utilities for sync and async values.
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
import { lazy, lazyAsync } from '@depthbomb/common/lazy';
|
|
164
|
+
|
|
165
|
+
const getConfig = lazy(() => ({ env: 'prod' }));
|
|
166
|
+
const config = getConfig(); // factory runs once
|
|
167
|
+
|
|
168
|
+
const getToken = lazyAsync(async () => 'token');
|
|
169
|
+
const token = await getToken(); // promise created once
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### `collections`
|
|
173
|
+
|
|
174
|
+
A lightweight generic FIFO queue with `enqueue`, `dequeue`, `peek`, iteration support, and internal compaction to keep long-running usage efficient. Also includes `BoundedQueue` for fixed-capacity use cases.
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
import { Queue, BoundedQueue, BoundedQueueOverflow } from '@depthbomb/common/collections';
|
|
178
|
+
|
|
179
|
+
const q = new Queue<number>([1, 2]);
|
|
180
|
+
q.enqueue(3);
|
|
181
|
+
|
|
182
|
+
console.log(q.peek()); // 1
|
|
183
|
+
console.log(q.dequeue()); // 1
|
|
184
|
+
console.log(q.toArray()); // [2, 3]
|
|
185
|
+
|
|
186
|
+
const bounded = new BoundedQueue<number>({ maxSize: 3, overflow: BoundedQueueOverflow.DropOldest }, [1, 2, 3]);
|
|
187
|
+
bounded.enqueue(4);
|
|
188
|
+
console.log(bounded.toArray()); // [2, 3, 4]
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### `number`
|
|
192
|
+
|
|
193
|
+
Numeric helpers for clamping, range checks, rounding, and aggregation.
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
import { clamp, inRange, roundTo, sum, average } from '@depthbomb/common/number';
|
|
197
|
+
|
|
198
|
+
const bounded = clamp(12, 0, 10); // 10
|
|
199
|
+
const valid = inRange(5, 1, 10); // true
|
|
200
|
+
const rounded = roundTo(3.14159, 2); // 3.14
|
|
201
|
+
const total = sum([1, 2, 3, 4]); // 10
|
|
202
|
+
const mean = average([1, 2, 3, 4]); // 2.5
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### `random`
|
|
206
|
+
|
|
207
|
+
Cross-environment random helpers for ranges and selection.
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
import { randomFloat, randomInt, pickRandom, pickWeighted } from '@depthbomb/common/random';
|
|
211
|
+
|
|
212
|
+
const f = randomFloat(5, 10); // 5 <= f < 10
|
|
213
|
+
const i = randomInt(1, 6); // inclusive
|
|
214
|
+
const choice = pickRandom(['red', 'green', 'blue']);
|
|
215
|
+
const weighted = pickWeighted([
|
|
216
|
+
{ value: 'small', weight: 1 },
|
|
217
|
+
{ value: 'medium', weight: 3 },
|
|
218
|
+
{ value: 'large', weight: 6 },
|
|
219
|
+
]);
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### `guards`
|
|
223
|
+
|
|
224
|
+
Runtime type guards for common narrowing patterns.
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
import { isRecord, isNonEmptyString, isNumber, isDateLike } from '@depthbomb/common/guards';
|
|
228
|
+
|
|
229
|
+
const input: unknown = { name: 'Ada', createdAt: '2026-01-01' };
|
|
230
|
+
|
|
231
|
+
if (isRecord(input) && isNonEmptyString(input.name) && isDateLike(input.createdAt)) {
|
|
232
|
+
// narrowed shape at runtime
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const maybeCount: unknown = 42;
|
|
236
|
+
if (isNumber(maybeCount)) {
|
|
237
|
+
console.log(maybeCount + 1);
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### `typing`
|
|
242
|
+
|
|
243
|
+
Shared type aliases and type-oriented helpers such as `Awaitable`, `Maybe`, `Nullable`, `Result`, `cast`, `assume`, `typedEntries`, `ok`, `err`, `isOk`, `mapOk`, `mapErr`, and `tryCatchAsync`.
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
import {
|
|
247
|
+
cast, assume, typedEntries,
|
|
248
|
+
ok, err, isOk, mapOk, mapErr, tryCatchAsync,
|
|
249
|
+
type Awaitable, type Maybe, type Result
|
|
250
|
+
} from '@depthbomb/common/typing';
|
|
251
|
+
|
|
252
|
+
const v = cast<object, { id: string }>({ id: 'a' });
|
|
253
|
+
|
|
254
|
+
const unknownValue: unknown = 'hello';
|
|
255
|
+
assume<string>(unknownValue); // assertion helper
|
|
256
|
+
|
|
257
|
+
const entries = typedEntries({ a: 1, b: 2 }); // typed key/value tuples
|
|
258
|
+
|
|
259
|
+
const maybeName: Maybe<string> = undefined;
|
|
260
|
+
const task: Awaitable<number> = Promise.resolve(1);
|
|
261
|
+
|
|
262
|
+
const initial: Result<number, string> = ok(2);
|
|
263
|
+
const doubled = mapOk(initial, (value) => value * 2);
|
|
264
|
+
const message = mapErr(err('bad'), (e) => `error: ${e}`);
|
|
265
|
+
|
|
266
|
+
const parsed = await tryCatchAsync(async () => JSON.parse('{"x":1}'));
|
|
267
|
+
if (isOk(parsed)) {
|
|
268
|
+
console.log(parsed.value.x);
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### `url`
|
|
273
|
+
|
|
274
|
+
URL-focused utilities centered around `URLPath`, an ergonomic wrapper for URL parsing, path composition, query/hash manipulation, and request dispatch via `fetch`.
|
|
275
|
+
|
|
276
|
+
```ts
|
|
277
|
+
import { URLPath, url } from '@depthbomb/common/url';
|
|
278
|
+
|
|
279
|
+
const api = new URLPath('https://example.com/api');
|
|
280
|
+
const usersUrl = api
|
|
281
|
+
.joinpath('users', '42')
|
|
282
|
+
.withQuery({ include: ['roles', 'profile'] })
|
|
283
|
+
.withQueryPatch({ page: 1 })
|
|
284
|
+
.appendQuery({ include: 'permissions' })
|
|
285
|
+
.withoutEmptyQuery()
|
|
286
|
+
.withHash('details');
|
|
287
|
+
|
|
288
|
+
const userPath = url`/users/${'john/doe'}/posts/${'my first post'}`;
|
|
289
|
+
|
|
290
|
+
console.log(usersUrl.toString());
|
|
291
|
+
// https://example.com/api/users/42?include=roles&include=profile#details
|
|
292
|
+
console.log(userPath);
|
|
293
|
+
// /users/john%2Fdoe/posts/my%20first%20post
|
|
294
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=function(e){return e[e.DropOldest=0]=`DropOldest`,e[e.DropNewest=1]=`DropNewest`,e[e.Throw=2]=`Throw`,e}({});var t=class{#e=[];#t=0;constructor(e){e&&(this.#e=[...e])}get size(){return this.#e.length-this.#t}get isEmpty(){return this.size===0}enqueue(e){this.#e.push(e)}dequeue(){if(this.#t>=this.#e.length)return;let e=this.#e[this.#t++];return this.#t>=64&&this.#t*2>=this.#e.length&&(this.#e=this.#e.slice(this.#t),this.#t=0),e}peek(){return this.#e[this.#t]}clear(){this.#e=[],this.#t=0}[Symbol.iterator](){return this.toArray()[Symbol.iterator]()}toArray(){return this.#e.slice(this.#t)}},n=class{#e;#t;#n;constructor(n,r){if(!Number.isInteger(n.maxSize)||n.maxSize<1)throw Error(`maxSize must be an integer >= 1`);if(this.#e=n.maxSize,this.#t=n.overflow??e.DropOldest,this.#n=new t,r)for(let e of r)this.enqueue(e)}get size(){return this.#n.size}get isEmpty(){return this.#n.isEmpty}get isFull(){return this.size>=this.#e}get maxSize(){return this.#e}get overflow(){return this.#t}enqueue(t){if(!this.isFull){this.#n.enqueue(t);return}if(this.#t!==e.DropNewest){if(this.#t===e.Throw)throw Error(`BoundedQueue overflow`);this.#n.dequeue(),this.#n.enqueue(t)}}dequeue(){return this.#n.dequeue()}peek(){return this.#n.peek()}clear(){this.#n.clear()}[Symbol.iterator](){return this.#n[Symbol.iterator]()}toArray(){return this.#n.toArray()}};exports.BoundedQueue=n,exports.BoundedQueueOverflow=e,exports.Queue=t;
|
|
@@ -1,9 +1,17 @@
|
|
|
1
|
-
import { Maybe } from "./
|
|
1
|
+
import { Maybe } from "./typing.cjs";
|
|
2
2
|
|
|
3
|
-
//#region src/
|
|
3
|
+
//#region src/collections.d.ts
|
|
4
|
+
interface IBoundedQueueOptions {
|
|
5
|
+
maxSize: number;
|
|
6
|
+
overflow?: BoundedQueueOverflow;
|
|
7
|
+
}
|
|
8
|
+
declare const enum BoundedQueueOverflow {
|
|
9
|
+
DropOldest = 0,
|
|
10
|
+
DropNewest = 1,
|
|
11
|
+
Throw = 2
|
|
12
|
+
}
|
|
4
13
|
declare class Queue<T> {
|
|
5
|
-
private
|
|
6
|
-
private head;
|
|
14
|
+
#private;
|
|
7
15
|
constructor(initial?: Iterable<T>);
|
|
8
16
|
/**
|
|
9
17
|
* Get the number of items in the queue.
|
|
@@ -48,5 +56,20 @@ declare class Queue<T> {
|
|
|
48
56
|
*/
|
|
49
57
|
toArray(): T[];
|
|
50
58
|
}
|
|
59
|
+
declare class BoundedQueue<T> {
|
|
60
|
+
#private;
|
|
61
|
+
constructor(options: IBoundedQueueOptions, initial?: Iterable<T>);
|
|
62
|
+
get size(): number;
|
|
63
|
+
get isEmpty(): boolean;
|
|
64
|
+
get isFull(): boolean;
|
|
65
|
+
get maxSize(): number;
|
|
66
|
+
get overflow(): BoundedQueueOverflow;
|
|
67
|
+
enqueue(item: T): void;
|
|
68
|
+
dequeue(): Maybe<T>;
|
|
69
|
+
peek(): Maybe<T>;
|
|
70
|
+
clear(): void;
|
|
71
|
+
[Symbol.iterator](): Iterator<T>;
|
|
72
|
+
toArray(): T[];
|
|
73
|
+
}
|
|
51
74
|
//#endregion
|
|
52
|
-
export { Queue };
|
|
75
|
+
export { BoundedQueue, BoundedQueueOverflow, IBoundedQueueOptions, Queue };
|
|
@@ -1,9 +1,17 @@
|
|
|
1
|
-
import { Maybe } from "./
|
|
1
|
+
import { Maybe } from "./typing.mjs";
|
|
2
2
|
|
|
3
|
-
//#region src/
|
|
3
|
+
//#region src/collections.d.ts
|
|
4
|
+
interface IBoundedQueueOptions {
|
|
5
|
+
maxSize: number;
|
|
6
|
+
overflow?: BoundedQueueOverflow;
|
|
7
|
+
}
|
|
8
|
+
declare const enum BoundedQueueOverflow {
|
|
9
|
+
DropOldest = 0,
|
|
10
|
+
DropNewest = 1,
|
|
11
|
+
Throw = 2
|
|
12
|
+
}
|
|
4
13
|
declare class Queue<T> {
|
|
5
|
-
private
|
|
6
|
-
private head;
|
|
14
|
+
#private;
|
|
7
15
|
constructor(initial?: Iterable<T>);
|
|
8
16
|
/**
|
|
9
17
|
* Get the number of items in the queue.
|
|
@@ -48,5 +56,20 @@ declare class Queue<T> {
|
|
|
48
56
|
*/
|
|
49
57
|
toArray(): T[];
|
|
50
58
|
}
|
|
59
|
+
declare class BoundedQueue<T> {
|
|
60
|
+
#private;
|
|
61
|
+
constructor(options: IBoundedQueueOptions, initial?: Iterable<T>);
|
|
62
|
+
get size(): number;
|
|
63
|
+
get isEmpty(): boolean;
|
|
64
|
+
get isFull(): boolean;
|
|
65
|
+
get maxSize(): number;
|
|
66
|
+
get overflow(): BoundedQueueOverflow;
|
|
67
|
+
enqueue(item: T): void;
|
|
68
|
+
dequeue(): Maybe<T>;
|
|
69
|
+
peek(): Maybe<T>;
|
|
70
|
+
clear(): void;
|
|
71
|
+
[Symbol.iterator](): Iterator<T>;
|
|
72
|
+
toArray(): T[];
|
|
73
|
+
}
|
|
51
74
|
//#endregion
|
|
52
|
-
export { Queue };
|
|
75
|
+
export { BoundedQueue, BoundedQueueOverflow, IBoundedQueueOptions, Queue };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
let e=function(e){return e[e.DropOldest=0]=`DropOldest`,e[e.DropNewest=1]=`DropNewest`,e[e.Throw=2]=`Throw`,e}({});var t=class{#e=[];#t=0;constructor(e){e&&(this.#e=[...e])}get size(){return this.#e.length-this.#t}get isEmpty(){return this.size===0}enqueue(e){this.#e.push(e)}dequeue(){if(this.#t>=this.#e.length)return;let e=this.#e[this.#t++];return this.#t>=64&&this.#t*2>=this.#e.length&&(this.#e=this.#e.slice(this.#t),this.#t=0),e}peek(){return this.#e[this.#t]}clear(){this.#e=[],this.#t=0}[Symbol.iterator](){return this.toArray()[Symbol.iterator]()}toArray(){return this.#e.slice(this.#t)}},n=class{#e;#t;#n;constructor(n,r){if(!Number.isInteger(n.maxSize)||n.maxSize<1)throw Error(`maxSize must be an integer >= 1`);if(this.#e=n.maxSize,this.#t=n.overflow??e.DropOldest,this.#n=new t,r)for(let e of r)this.enqueue(e)}get size(){return this.#n.size}get isEmpty(){return this.#n.isEmpty}get isFull(){return this.size>=this.#e}get maxSize(){return this.#e}get overflow(){return this.#t}enqueue(t){if(!this.isFull){this.#n.enqueue(t);return}if(this.#t!==e.DropNewest){if(this.#t===e.Throw)throw Error(`BoundedQueue overflow`);this.#n.dequeue(),this.#n.enqueue(t)}}dequeue(){return this.#n.dequeue()}peek(){return this.#n.peek()}clear(){this.#n.clear()}[Symbol.iterator](){return this.#n[Symbol.iterator]()}toArray(){return this.#n.toArray()}};export{n as BoundedQueue,e as BoundedQueueOverflow,t as Queue};
|
package/dist/guards.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){return!Number.isNaN(e.getTime())}function t(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function n(e){return typeof e==`string`&&e.length>0}function r(e){return typeof e==`number`&&Number.isFinite(e)}function i(t){return t instanceof Date?e(t):typeof t==`number`?Number.isFinite(t)&&e(new Date(t)):typeof t==`string`?t.length>0&&e(new Date(t)):!1}exports.isDateLike=i,exports.isNonEmptyString=n,exports.isNumber=r,exports.isRecord=t;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
//#region src/guards.d.ts
|
|
2
|
+
declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
3
|
+
declare function isNonEmptyString(value: unknown): value is string;
|
|
4
|
+
declare function isNumber(value: unknown): value is number;
|
|
5
|
+
declare function isDateLike(value: unknown): value is Date | number | string;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { isDateLike, isNonEmptyString, isNumber, isRecord };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
//#region src/guards.d.ts
|
|
2
|
+
declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
3
|
+
declare function isNonEmptyString(value: unknown): value is string;
|
|
4
|
+
declare function isNumber(value: unknown): value is number;
|
|
5
|
+
declare function isDateLike(value: unknown): value is Date | number | string;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { isDateLike, isNonEmptyString, isNumber, isRecord };
|
package/dist/guards.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e){return!Number.isNaN(e.getTime())}function t(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function n(e){return typeof e==`string`&&e.length>0}function r(e){return typeof e==`number`&&Number.isFinite(e)}function i(t){return t instanceof Date?e(t):typeof t==`number`?Number.isFinite(t)&&e(new Date(t)):typeof t==`string`?t.length>0&&e(new Date(t)):!1}export{i as isDateLike,n as isNonEmptyString,r as isNumber,t as isRecord};
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./collections.cjs`),t=require(`./decorators.cjs`),n=require(`./functional.cjs`),r=require(`./guards.cjs`),i=require(`./timing.cjs`),a=require(`./promise.cjs`),o=require(`./lazy.cjs`),s=require(`./number.cjs`),c=require(`./random.cjs`),l=require(`./state.cjs`),u=require(`./typing.cjs`),d=require(`./url.cjs`);exports.BoundedQueue=e.BoundedQueue,exports.BoundedQueueOverflow=e.BoundedQueueOverflow,exports.Flag=l.Flag,exports.Queue=e.Queue,exports.ResettableValue=l.ResettableValue,exports.RetryJitter=i.RetryJitter,exports.URLPath=d.URLPath,exports.abortAfter=i.abortAfter,exports.allSettledDetailed=a.allSettledDetailed,exports.allSettledSuccessful=a.allSettledSuccessful,exports.assume=u.assume,exports.average=s.average,exports.cache=t.cache,exports.cast=u.cast,exports.clamp=s.clamp,exports.err=u.err,exports.inRange=s.inRange,exports.isDateLike=r.isDateLike,exports.isNonEmptyString=r.isNonEmptyString,exports.isNumber=r.isNumber,exports.isOk=u.isOk,exports.isRecord=r.isRecord,exports.lazy=o.lazy,exports.lazyAsync=o.lazyAsync,exports.mapErr=u.mapErr,exports.mapOk=u.mapOk,exports.ok=u.ok,exports.once=n.once,exports.pMap=a.pMap,exports.pickRandom=c.pickRandom,exports.pickWeighted=c.pickWeighted,exports.pollUntil=i.pollUntil,exports.pool=a.pool,exports.raceSignals=i.raceSignals,exports.randomFloat=c.randomFloat,exports.randomInt=c.randomInt,exports.rejectionTimeout=i.rejectionTimeout,exports.resettableLazy=l.resettableLazy,exports.resettableLazyAsync=l.resettableLazyAsync,exports.retry=i.retry,exports.roundTo=s.roundTo,exports.sequential=a.sequential,exports.sum=s.sum,exports.timeout=i.timeout,exports.tryCatchAsync=u.tryCatchAsync,exports.typedEntries=u.typedEntries,exports.url=d.url,exports.withAbort=i.withAbort,exports.withTimeout=i.withTimeout;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Awaitable, Maybe, Nullable, Result, assume, cast, err, isOk, mapErr, mapOk, ok, tryCatchAsync, typedEntries } from "./typing.cjs";
|
|
2
|
+
import { BoundedQueue, BoundedQueueOverflow, IBoundedQueueOptions, Queue } from "./collections.cjs";
|
|
2
3
|
import { cache } from "./decorators.cjs";
|
|
3
|
-
import { once } from "./
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
4
|
+
import { once } from "./functional.cjs";
|
|
5
|
+
import { isDateLike, isNonEmptyString, isNumber, isRecord } from "./guards.cjs";
|
|
6
|
+
import { IRetryOptions, RetryJitter, abortAfter, pollUntil, raceSignals, rejectionTimeout, retry, timeout, withAbort, withTimeout } from "./timing.cjs";
|
|
7
|
+
import { IConcurrencyOptions, SettledDetailed, allSettledDetailed, allSettledSuccessful, pMap, pool, sequential } from "./promise.cjs";
|
|
8
|
+
import { lazy, lazyAsync } from "./lazy.cjs";
|
|
9
|
+
import { average, clamp, inRange, roundTo, sum } from "./number.cjs";
|
|
10
|
+
import { IWeightedItem, pickRandom, pickWeighted, randomFloat, randomInt } from "./random.cjs";
|
|
11
|
+
import { Flag, ResettableValue, resettableLazy, resettableLazyAsync } from "./state.cjs";
|
|
12
|
+
import { QueryObject, QueryValue, URLLike, URLPath, url } from "./url.cjs";
|
|
13
|
+
export { Awaitable, BoundedQueue, BoundedQueueOverflow, Flag, IBoundedQueueOptions, IConcurrencyOptions, IRetryOptions, IWeightedItem, Maybe, Nullable, QueryObject, QueryValue, Queue, ResettableValue, Result, RetryJitter, SettledDetailed, URLLike, URLPath, abortAfter, allSettledDetailed, allSettledSuccessful, assume, average, cache, cast, clamp, err, inRange, isDateLike, isNonEmptyString, isNumber, isOk, isRecord, lazy, lazyAsync, mapErr, mapOk, ok, once, pMap, pickRandom, pickWeighted, pollUntil, pool, raceSignals, randomFloat, randomInt, rejectionTimeout, resettableLazy, resettableLazyAsync, retry, roundTo, sequential, sum, timeout, tryCatchAsync, typedEntries, url, withAbort, withTimeout };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Awaitable, Maybe, Nullable, Result, assume, cast, err, isOk, mapErr, mapOk, ok, tryCatchAsync, typedEntries } from "./typing.mjs";
|
|
2
|
+
import { BoundedQueue, BoundedQueueOverflow, IBoundedQueueOptions, Queue } from "./collections.mjs";
|
|
2
3
|
import { cache } from "./decorators.mjs";
|
|
3
|
-
import { once } from "./
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
4
|
+
import { once } from "./functional.mjs";
|
|
5
|
+
import { isDateLike, isNonEmptyString, isNumber, isRecord } from "./guards.mjs";
|
|
6
|
+
import { IRetryOptions, RetryJitter, abortAfter, pollUntil, raceSignals, rejectionTimeout, retry, timeout, withAbort, withTimeout } from "./timing.mjs";
|
|
7
|
+
import { IConcurrencyOptions, SettledDetailed, allSettledDetailed, allSettledSuccessful, pMap, pool, sequential } from "./promise.mjs";
|
|
8
|
+
import { lazy, lazyAsync } from "./lazy.mjs";
|
|
9
|
+
import { average, clamp, inRange, roundTo, sum } from "./number.mjs";
|
|
10
|
+
import { IWeightedItem, pickRandom, pickWeighted, randomFloat, randomInt } from "./random.mjs";
|
|
11
|
+
import { Flag, ResettableValue, resettableLazy, resettableLazyAsync } from "./state.mjs";
|
|
12
|
+
import { QueryObject, QueryValue, URLLike, URLPath, url } from "./url.mjs";
|
|
13
|
+
export { Awaitable, BoundedQueue, BoundedQueueOverflow, Flag, IBoundedQueueOptions, IConcurrencyOptions, IRetryOptions, IWeightedItem, Maybe, Nullable, QueryObject, QueryValue, Queue, ResettableValue, Result, RetryJitter, SettledDetailed, URLLike, URLPath, abortAfter, allSettledDetailed, allSettledSuccessful, assume, average, cache, cast, clamp, err, inRange, isDateLike, isNonEmptyString, isNumber, isOk, isRecord, lazy, lazyAsync, mapErr, mapOk, ok, once, pMap, pickRandom, pickWeighted, pollUntil, pool, raceSignals, randomFloat, randomInt, rejectionTimeout, resettableLazy, resettableLazyAsync, retry, roundTo, sequential, sum, timeout, tryCatchAsync, typedEntries, url, withAbort, withTimeout };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{BoundedQueue as e,BoundedQueueOverflow as t,Queue as n}from"./collections.mjs";import{cache as r}from"./decorators.mjs";import{once as i}from"./functional.mjs";import{isDateLike as a,isNonEmptyString as o,isNumber as s,isRecord as c}from"./guards.mjs";import{RetryJitter as l,abortAfter as u,pollUntil as d,raceSignals as f,rejectionTimeout as p,retry as m,timeout as h,withAbort as g,withTimeout as _}from"./timing.mjs";import{allSettledDetailed as v,allSettledSuccessful as y,pMap as b,pool as x,sequential as S}from"./promise.mjs";import{lazy as C,lazyAsync as w}from"./lazy.mjs";import{average as T,clamp as E,inRange as D,roundTo as O,sum as k}from"./number.mjs";import{pickRandom as A,pickWeighted as j,randomFloat as M,randomInt as N}from"./random.mjs";import{Flag as P,ResettableValue as F,resettableLazy as I,resettableLazyAsync as L}from"./state.mjs";import{assume as R,cast as z,err as B,isOk as V,mapErr as H,mapOk as U,ok as W,tryCatchAsync as G,typedEntries as K}from"./typing.mjs";import{URLPath as q,url as J}from"./url.mjs";export{e as BoundedQueue,t as BoundedQueueOverflow,P as Flag,n as Queue,F as ResettableValue,l as RetryJitter,q as URLPath,u as abortAfter,v as allSettledDetailed,y as allSettledSuccessful,R as assume,T as average,r as cache,z as cast,E as clamp,B as err,D as inRange,a as isDateLike,o as isNonEmptyString,s as isNumber,V as isOk,c as isRecord,C as lazy,w as lazyAsync,H as mapErr,U as mapOk,W as ok,i as once,b as pMap,A as pickRandom,j as pickWeighted,d as pollUntil,x as pool,f as raceSignals,M as randomFloat,N as randomInt,p as rejectionTimeout,I as resettableLazy,L as resettableLazyAsync,m as retry,O as roundTo,S as sequential,k as sum,h as timeout,G as tryCatchAsync,K as typedEntries,J as url,g as withAbort,_ as withTimeout};
|
package/dist/lazy.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){let t,n=!1;return()=>(n||=(t=e(),!0),t)}function t(e){let t;return()=>(t||=e(),t)}
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){let t,n=!1;return()=>(n||=(t=e(),!0),t)}function t(e){let t;return()=>(t||=e(),t)}exports.lazy=e,exports.lazyAsync=t;
|
package/dist/lazy.d.cts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
//#region src/lazy.d.ts
|
|
2
2
|
declare function lazy<T>(factory: () => T): () => T;
|
|
3
3
|
declare function lazyAsync<T>(factory: () => Promise<T>): () => Promise<T>;
|
|
4
|
-
declare function resettableLazy<T>(factory: () => T): {
|
|
5
|
-
get: () => T;
|
|
6
|
-
reset: () => void;
|
|
7
|
-
};
|
|
8
4
|
//#endregion
|
|
9
|
-
export { lazy, lazyAsync
|
|
5
|
+
export { lazy, lazyAsync };
|
package/dist/lazy.d.mts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
//#region src/lazy.d.ts
|
|
2
2
|
declare function lazy<T>(factory: () => T): () => T;
|
|
3
3
|
declare function lazyAsync<T>(factory: () => Promise<T>): () => Promise<T>;
|
|
4
|
-
declare function resettableLazy<T>(factory: () => T): {
|
|
5
|
-
get: () => T;
|
|
6
|
-
reset: () => void;
|
|
7
|
-
};
|
|
8
4
|
//#endregion
|
|
9
|
-
export { lazy, lazyAsync
|
|
5
|
+
export { lazy, lazyAsync };
|
package/dist/lazy.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e){let t,n=!1;return()=>(n||=(t=e(),!0),t)}function t(e){let t;return()=>(t||=e(),t)}
|
|
1
|
+
function e(e){let t,n=!1;return()=>(n||=(t=e(),!0),t)}function t(e){let t;return()=>(t||=e(),t)}export{e as lazy,t as lazyAsync};
|
package/dist/number.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e,t,n){if(t>n)throw Error(`min must be <= max`);return Math.min(Math.max(e,t),n)}function t(e,t,n,r=!0){if(t>n)throw Error(`min must be <= max`);return r?e>=t&&e<=n:e>t&&e<n}function n(e,t=0){if(!Number.isInteger(t)||t<0)throw Error(`decimals must be an integer >= 0`);let n=10**t;return Math.round((e+2**-52)*n)/n}function r(e){let t=0;for(let n of e)t+=n;return t}function i(e){let t=0,n=0;for(let r of e)t+=r,n++;if(n===0)throw Error(`cannot compute average of empty iterable`);return t/n}exports.average=i,exports.clamp=e,exports.inRange=t,exports.roundTo=n,exports.sum=r;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/number.d.ts
|
|
2
|
+
declare function clamp(value: number, min: number, max: number): number;
|
|
3
|
+
declare function inRange(value: number, min: number, max: number, inclusive?: boolean): boolean;
|
|
4
|
+
declare function roundTo(value: number, decimals?: number): number;
|
|
5
|
+
declare function sum(values: Iterable<number>): number;
|
|
6
|
+
declare function average(values: Iterable<number>): number;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { average, clamp, inRange, roundTo, sum };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/number.d.ts
|
|
2
|
+
declare function clamp(value: number, min: number, max: number): number;
|
|
3
|
+
declare function inRange(value: number, min: number, max: number, inclusive?: boolean): boolean;
|
|
4
|
+
declare function roundTo(value: number, decimals?: number): number;
|
|
5
|
+
declare function sum(values: Iterable<number>): number;
|
|
6
|
+
declare function average(values: Iterable<number>): number;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { average, clamp, inRange, roundTo, sum };
|
package/dist/number.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e,t,n){if(t>n)throw Error(`min must be <= max`);return Math.min(Math.max(e,t),n)}function t(e,t,n,r=!0){if(t>n)throw Error(`min must be <= max`);return r?e>=t&&e<=n:e>t&&e<n}function n(e,t=0){if(!Number.isInteger(t)||t<0)throw Error(`decimals must be an integer >= 0`);let n=10**t;return Math.round((e+2**-52)*n)/n}function r(e){let t=0;for(let n of e)t+=n;return t}function i(e){let t=0,n=0;for(let r of e)t+=r,n++;if(n===0)throw Error(`cannot compute average of empty iterable`);return t/n}export{i as average,e as clamp,t as inRange,n as roundTo,r as sum};
|
package/dist/promise.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});async function e(e){return(await Promise.allSettled(e)).filter(e=>e.status===`fulfilled`).map(e=>e.value)}async function t(e){let t=await Promise.allSettled(e),n=[],r=[];for(let e of t){if(e.status===`fulfilled`){n.push(e.value);continue}r.push(e.reason)}return{results:t,fulfilled:n,rejected:r}}async function n(e){let t=[];for(let n of e)t.push(await n());return t}function r(e){if(!Number.isInteger(e)||e<1)throw Error(`concurrency must be an integer >= 1`);return e}async function i(e,t={}){if(e.length===0)return[];let n=r(t.concurrency??e.length),i=Array(e.length),a=0;async function o(){for(;;){let t=a++;if(t>=e.length)return;i[t]=await e[t]()}}let s=Math.min(n,e.length),c=Array.from({length:s},()=>o());return await Promise.all(c),i}async function a(e,t,n={}){let r=[],a=0;for(let n of e){let e=a++;r.push(()=>t(n,e))}return i(r,n)}exports.allSettledDetailed=t,exports.allSettledSuccessful=e,exports.pMap=a,exports.pool=i,exports.sequential=n;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Awaitable } from "./typing.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/promise.d.ts
|
|
4
|
+
interface IConcurrencyOptions {
|
|
5
|
+
concurrency?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Waits for all promises to settle and returns an array of successful results.
|
|
9
|
+
*
|
|
10
|
+
* @param promises An array of promises to wait for
|
|
11
|
+
* @returns A promise that resolves to an array of successful results
|
|
12
|
+
*/
|
|
13
|
+
declare function allSettledSuccessful<T>(promises: Array<Awaitable<T>>): Promise<T[]>;
|
|
14
|
+
interface SettledDetailed<T> {
|
|
15
|
+
results: Array<PromiseSettledResult<T>>;
|
|
16
|
+
fulfilled: T[];
|
|
17
|
+
rejected: unknown[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Waits for all values to settle and returns full settled results plus split fulfilled/rejected lists.
|
|
21
|
+
*
|
|
22
|
+
* @param promises Values or promises to settle.
|
|
23
|
+
*/
|
|
24
|
+
declare function allSettledDetailed<T>(promises: Array<Awaitable<T>>): Promise<SettledDetailed<T>>;
|
|
25
|
+
/**
|
|
26
|
+
* Executes an array of asynchronous tasks sequentially.
|
|
27
|
+
*
|
|
28
|
+
* @param tasks An array of functions that return promises
|
|
29
|
+
* @returns A promise that resolves to an array of results from the input tasks
|
|
30
|
+
*/
|
|
31
|
+
declare function sequential<T>(tasks: Array<() => Awaitable<T>>): Promise<T[]>;
|
|
32
|
+
/**
|
|
33
|
+
* Runs asynchronous tasks with a concurrency limit while preserving result order.
|
|
34
|
+
*
|
|
35
|
+
* @param tasks Task functions to execute.
|
|
36
|
+
* @param options Concurrency options.
|
|
37
|
+
*/
|
|
38
|
+
declare function pool<T>(tasks: Array<() => Awaitable<T>>, options?: IConcurrencyOptions): Promise<T[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Maps values using an async mapper with a concurrency limit.
|
|
41
|
+
*
|
|
42
|
+
* @param values Input values.
|
|
43
|
+
* @param mapper Mapping function.
|
|
44
|
+
* @param options Concurrency options.
|
|
45
|
+
*/
|
|
46
|
+
declare function pMap<T, U>(values: Iterable<T>, mapper: (value: T, index: number) => Awaitable<U>, options?: IConcurrencyOptions): Promise<U[]>;
|
|
47
|
+
//#endregion
|
|
48
|
+
export { IConcurrencyOptions, SettledDetailed, allSettledDetailed, allSettledSuccessful, pMap, pool, sequential };
|