speexjs-core 0.7.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.
Files changed (78) hide show
  1. package/CHANGELOG.md +117 -0
  2. package/CONTRIBUTING.md +55 -0
  3. package/PUBLISH.md +45 -0
  4. package/README.md +174 -0
  5. package/ROADMAP.md +72 -0
  6. package/SECURITY.md +35 -0
  7. package/SUMMARY.md +321 -0
  8. package/dist/async/index.d.ts +232 -0
  9. package/dist/async/index.js +366 -0
  10. package/dist/async/index.js.map +1 -0
  11. package/dist/collection/index.d.ts +230 -0
  12. package/dist/collection/index.js +375 -0
  13. package/dist/collection/index.js.map +1 -0
  14. package/dist/color/index.d.ts +128 -0
  15. package/dist/color/index.js +167 -0
  16. package/dist/color/index.js.map +1 -0
  17. package/dist/core/index.d.ts +119 -0
  18. package/dist/core/index.js +324 -0
  19. package/dist/core/index.js.map +1 -0
  20. package/dist/crypto/index.d.ts +84 -0
  21. package/dist/crypto/index.js +144 -0
  22. package/dist/crypto/index.js.map +1 -0
  23. package/dist/date/index.d.ts +588 -0
  24. package/dist/date/index.js +737 -0
  25. package/dist/date/index.js.map +1 -0
  26. package/dist/dep-exray/analyzer/index.d.ts +7 -0
  27. package/dist/dep-exray/analyzer/index.js +68 -0
  28. package/dist/dep-exray/analyzer/index.js.map +1 -0
  29. package/dist/dep-exray/cli.d.ts +1 -0
  30. package/dist/dep-exray/cli.js +441 -0
  31. package/dist/dep-exray/cli.js.map +1 -0
  32. package/dist/dep-exray/index.d.ts +5 -0
  33. package/dist/dep-exray/index.js +454 -0
  34. package/dist/dep-exray/index.js.map +1 -0
  35. package/dist/dep-exray/known-mappings.d.ts +17 -0
  36. package/dist/dep-exray/known-mappings.js +122 -0
  37. package/dist/dep-exray/known-mappings.js.map +1 -0
  38. package/dist/dep-exray/reporter/index.d.ts +5 -0
  39. package/dist/dep-exray/reporter/index.js +89 -0
  40. package/dist/dep-exray/reporter/index.js.map +1 -0
  41. package/dist/dep-exray/scanner/index.d.ts +5 -0
  42. package/dist/dep-exray/scanner/index.js +299 -0
  43. package/dist/dep-exray/scanner/index.js.map +1 -0
  44. package/dist/dep-exray/types.d.ts +38 -0
  45. package/dist/dep-exray/types.js +1 -0
  46. package/dist/dep-exray/types.js.map +1 -0
  47. package/dist/error/index.d.ts +148 -0
  48. package/dist/error/index.js +115 -0
  49. package/dist/error/index.js.map +1 -0
  50. package/dist/index-BgG21uJC.d.ts +166 -0
  51. package/dist/index.d.ts +19 -0
  52. package/dist/index.js +4378 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/io/index.d.ts +39 -0
  55. package/dist/io/index.js +111 -0
  56. package/dist/io/index.js.map +1 -0
  57. package/dist/logger/index.d.ts +1 -0
  58. package/dist/logger/index.js +214 -0
  59. package/dist/logger/index.js.map +1 -0
  60. package/dist/logger/transports.d.ts +1 -0
  61. package/dist/logger/transports.js +122 -0
  62. package/dist/logger/transports.js.map +1 -0
  63. package/dist/math/index.d.ts +362 -0
  64. package/dist/math/index.js +372 -0
  65. package/dist/math/index.js.map +1 -0
  66. package/dist/path/index.d.ts +81 -0
  67. package/dist/path/index.js +134 -0
  68. package/dist/path/index.js.map +1 -0
  69. package/dist/string/index.d.ts +234 -0
  70. package/dist/string/index.js +411 -0
  71. package/dist/string/index.js.map +1 -0
  72. package/dist/type/index.d.ts +85 -0
  73. package/dist/type/index.js +107 -0
  74. package/dist/type/index.js.map +1 -0
  75. package/dist/validation/index.d.ts +203 -0
  76. package/dist/validation/index.js +402 -0
  77. package/dist/validation/index.js.map +1 -0
  78. package/package.json +172 -0
@@ -0,0 +1,366 @@
1
+ // src/async/queue.ts
2
+ var Queue = class {
3
+ _tasks = [];
4
+ _running = 0;
5
+ _paused = false;
6
+ _idleResolve = null;
7
+ _maxConcurrency;
8
+ constructor(options) {
9
+ this._maxConcurrency = options?.concurrency ?? 1;
10
+ if (this._maxConcurrency < 1) this._maxConcurrency = 1;
11
+ }
12
+ get pending() {
13
+ return this._tasks.length;
14
+ }
15
+ get running() {
16
+ return this._running;
17
+ }
18
+ add(task, options) {
19
+ return new Promise((resolve, reject) => {
20
+ this._tasks.push({
21
+ priority: options?.priority ?? 0,
22
+ task,
23
+ resolve,
24
+ reject
25
+ });
26
+ this._tasks.sort((a, b) => b.priority - a.priority);
27
+ this._process();
28
+ });
29
+ }
30
+ pause() {
31
+ this._paused = true;
32
+ }
33
+ resume() {
34
+ this._paused = false;
35
+ this._process();
36
+ }
37
+ clear() {
38
+ const err = new Error("Queue cleared");
39
+ for (const t of this._tasks) t.reject(err);
40
+ this._tasks = [];
41
+ }
42
+ onIdle() {
43
+ if (this._running === 0 && this._tasks.length === 0) return Promise.resolve();
44
+ return new Promise((resolve) => {
45
+ this._idleResolve = resolve;
46
+ });
47
+ }
48
+ _process() {
49
+ if (this._paused) return;
50
+ while (this._running < this._maxConcurrency && this._tasks.length > 0) {
51
+ const item = this._tasks.shift();
52
+ this._running++;
53
+ item.task().then((result) => item.resolve(result)).catch((err) => item.reject(err)).finally(() => {
54
+ this._running--;
55
+ this._process();
56
+ if (this._running === 0 && this._tasks.length === 0 && this._idleResolve) {
57
+ this._idleResolve();
58
+ this._idleResolve = null;
59
+ }
60
+ });
61
+ }
62
+ }
63
+ };
64
+
65
+ // src/async/semaphore.ts
66
+ var Semaphore = class {
67
+ _available;
68
+ _waiting = [];
69
+ constructor(concurrency) {
70
+ if (concurrency < 1) throw new RangeError("Semaphore concurrency must be >= 1");
71
+ this._available = concurrency;
72
+ }
73
+ get available() {
74
+ return this._available;
75
+ }
76
+ acquire() {
77
+ if (this._available > 0) {
78
+ this._available--;
79
+ return Promise.resolve(this._release.bind(this));
80
+ }
81
+ return new Promise((resolve) => {
82
+ this._waiting.push(() => {
83
+ this._available--;
84
+ resolve(this._release.bind(this));
85
+ });
86
+ });
87
+ }
88
+ async use(fn) {
89
+ const release = await this.acquire();
90
+ try {
91
+ return await fn();
92
+ } finally {
93
+ release();
94
+ }
95
+ }
96
+ _release() {
97
+ this._available++;
98
+ if (this._waiting.length > 0) {
99
+ const next = this._waiting.shift();
100
+ if (next) next();
101
+ }
102
+ }
103
+ };
104
+
105
+ // src/async/memoize.ts
106
+ function memoizeAsync(fn, options) {
107
+ const {
108
+ ttl = 6e4,
109
+ staleWhileRevalidate = false,
110
+ maxSize = 100,
111
+ resolver = (...args) => JSON.stringify(args)
112
+ } = options ?? {};
113
+ const cache = /* @__PURE__ */ new Map();
114
+ const memoized = (async (...args) => {
115
+ const key = resolver(...args);
116
+ const now = Date.now();
117
+ const entry = cache.get(key);
118
+ if (entry && now - entry.timestamp < ttl) {
119
+ return entry.value;
120
+ }
121
+ if (entry && staleWhileRevalidate && now - entry.timestamp >= ttl) {
122
+ if (entry.promise) {
123
+ return entry.value;
124
+ }
125
+ entry.promise = fn(...args).then((result2) => {
126
+ entry.value = result2;
127
+ entry.timestamp = now;
128
+ entry.promise = void 0;
129
+ return result2;
130
+ }).catch((err) => {
131
+ entry.promise = void 0;
132
+ throw err;
133
+ });
134
+ return entry.value;
135
+ }
136
+ const result = await fn(...args);
137
+ cache.set(key, { value: result, timestamp: now });
138
+ if (cache.size > maxSize) {
139
+ const firstKey = cache.keys().next().value;
140
+ if (firstKey !== void 0) cache.delete(firstKey);
141
+ }
142
+ return result;
143
+ });
144
+ memoized.cache = cache;
145
+ memoized.clear = () => cache.clear();
146
+ return memoized;
147
+ }
148
+
149
+ // src/async/ratelimit.ts
150
+ var RateLimiter = class {
151
+ _maxRequests;
152
+ _perWindow;
153
+ _timestamps = [];
154
+ constructor(options) {
155
+ if (options.maxRequests < 1) throw new RangeError("maxRequests must be >= 1");
156
+ if (options.perWindow < 1) throw new RangeError("perWindow must be >= 1");
157
+ this._maxRequests = options.maxRequests;
158
+ this._perWindow = options.perWindow;
159
+ }
160
+ _prune() {
161
+ const now = Date.now();
162
+ const cutoff = now - this._perWindow;
163
+ while (this._timestamps.length > 0) {
164
+ const ts = this._timestamps[0];
165
+ if (ts === void 0 || ts > cutoff) break;
166
+ this._timestamps.shift();
167
+ }
168
+ }
169
+ /**
170
+ * Check if a request is allowed without waiting.
171
+ */
172
+ tryAcquire() {
173
+ this._prune();
174
+ if (this._timestamps.length < this._maxRequests) {
175
+ this._timestamps.push(Date.now());
176
+ return true;
177
+ }
178
+ return false;
179
+ }
180
+ /**
181
+ * Wait until a request is allowed.
182
+ */
183
+ async acquire() {
184
+ while (true) {
185
+ this._prune();
186
+ if (this._timestamps.length < this._maxRequests) {
187
+ this._timestamps.push(Date.now());
188
+ return;
189
+ }
190
+ const oldest = this._timestamps[0] ?? Date.now();
191
+ const waitMs = oldest + this._perWindow - Date.now();
192
+ if (waitMs > 0) {
193
+ await new Promise((resolve) => setTimeout(resolve, waitMs));
194
+ }
195
+ }
196
+ }
197
+ /**
198
+ * Current number of pending requests in the window.
199
+ */
200
+ get pending() {
201
+ this._prune();
202
+ return this._timestamps.length;
203
+ }
204
+ };
205
+
206
+ // src/async/mutex.ts
207
+ var Mutex = class {
208
+ _locked = false;
209
+ _waiting = [];
210
+ /**
211
+ * Acquire the lock. Returns a release function to be called when done.
212
+ */
213
+ acquire() {
214
+ if (!this._locked) {
215
+ this._locked = true;
216
+ return Promise.resolve(this._release.bind(this));
217
+ }
218
+ return new Promise((resolve) => {
219
+ this._waiting.push(() => {
220
+ resolve(this._release.bind(this));
221
+ });
222
+ });
223
+ }
224
+ /**
225
+ * Run a function with the lock held and automatically release it.
226
+ */
227
+ async use(fn) {
228
+ const release = await this.acquire();
229
+ try {
230
+ return await fn();
231
+ } finally {
232
+ release();
233
+ }
234
+ }
235
+ /**
236
+ * Whether the mutex is currently locked.
237
+ */
238
+ get locked() {
239
+ return this._locked;
240
+ }
241
+ _release() {
242
+ if (this._waiting.length > 0) {
243
+ const next = this._waiting.shift();
244
+ if (next) next();
245
+ } else {
246
+ this._locked = false;
247
+ }
248
+ }
249
+ };
250
+
251
+ // src/async/batch.ts
252
+ async function batch(items, batchSize, fn) {
253
+ if (batchSize < 1) throw new RangeError("batchSize must be >= 1");
254
+ const results = [];
255
+ for (let i = 0; i < items.length; i += batchSize) {
256
+ const chunk = items.slice(i, i + batchSize);
257
+ const batchResults = await fn(chunk);
258
+ results.push(...batchResults);
259
+ }
260
+ return results;
261
+ }
262
+
263
+ // src/async/waterfall.ts
264
+ async function waterfall(tasks, initial) {
265
+ let result = initial;
266
+ for (const task of tasks) {
267
+ result = await task(result);
268
+ }
269
+ return result;
270
+ }
271
+
272
+ // src/async/index.ts
273
+ function sleep(ms) {
274
+ return new Promise((resolve) => setTimeout(resolve, ms));
275
+ }
276
+ async function timeout(promise, ms, errorMessage) {
277
+ let timer;
278
+ const timeoutPromise = new Promise((_, reject) => {
279
+ timer = setTimeout(() => reject(new Error(errorMessage ?? `Promise timed out after ${ms}ms`)), ms);
280
+ });
281
+ try {
282
+ return await Promise.race([promise, timeoutPromise]);
283
+ } finally {
284
+ if (timer !== void 0) clearTimeout(timer);
285
+ }
286
+ }
287
+ async function raceWithTimeout(promise, ms) {
288
+ let timer;
289
+ const timeoutPromise = new Promise((resolve) => {
290
+ timer = setTimeout(() => resolve("timeout"), ms);
291
+ });
292
+ try {
293
+ return await Promise.race([promise, timeoutPromise]);
294
+ } finally {
295
+ if (timer !== void 0) clearTimeout(timer);
296
+ }
297
+ }
298
+ async function allSettledMap(items, fn) {
299
+ return await Promise.allSettled(items.map(fn));
300
+ }
301
+ async function parallelMap(items, fn, concurrency = Number.POSITIVE_INFINITY) {
302
+ if (concurrency === Number.POSITIVE_INFINITY) {
303
+ return await Promise.all(items.map(fn));
304
+ }
305
+ const results = [];
306
+ const queue = [...items];
307
+ const worker = async () => {
308
+ while (queue.length > 0) {
309
+ const item = queue.shift();
310
+ results.push(await fn(item));
311
+ }
312
+ };
313
+ const workerCount = Math.min(concurrency, items.length);
314
+ const workers = Array.from({ length: workerCount }, () => worker());
315
+ await Promise.all(workers);
316
+ return results;
317
+ }
318
+ async function retryAsync(fn, options) {
319
+ const { attempts = 3, baseDelay = 1e3, maxDelay = 3e4, shouldRetry = () => true } = options ?? {};
320
+ let lastError;
321
+ for (let attempt = 0; attempt < attempts; attempt++) {
322
+ try {
323
+ return await fn();
324
+ } catch (error) {
325
+ lastError = error;
326
+ if (!shouldRetry(error) || attempt >= attempts - 1) break;
327
+ const delay = Math.min(baseDelay * 2 ** attempt + Math.random() * baseDelay, maxDelay);
328
+ await sleep(delay);
329
+ }
330
+ }
331
+ throw lastError;
332
+ }
333
+ async function pipeline(initial, ...fns) {
334
+ let result = initial;
335
+ for (const fn of fns) {
336
+ result = await fn(result);
337
+ }
338
+ return result;
339
+ }
340
+ function deferred() {
341
+ let resolve;
342
+ let reject;
343
+ const promise = new Promise((res, rej) => {
344
+ resolve = res;
345
+ reject = rej;
346
+ });
347
+ return { promise, resolve, reject };
348
+ }
349
+ export {
350
+ Mutex,
351
+ Queue,
352
+ RateLimiter,
353
+ Semaphore,
354
+ allSettledMap,
355
+ batch,
356
+ deferred,
357
+ memoizeAsync,
358
+ parallelMap,
359
+ pipeline,
360
+ raceWithTimeout,
361
+ retryAsync,
362
+ sleep,
363
+ timeout,
364
+ waterfall
365
+ };
366
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/async/queue.ts","../../src/async/semaphore.ts","../../src/async/memoize.ts","../../src/async/ratelimit.ts","../../src/async/mutex.ts","../../src/async/batch.ts","../../src/async/waterfall.ts","../../src/async/index.ts"],"sourcesContent":["export interface QueueOptions {\n concurrency?: number\n}\n\n/**\n * Priority-based async task queue with concurrency control.\n *\n * @example\n * const queue = new Queue({ concurrency: 2 })\n * const result = await queue.add(() => fetch('/api/data'))\n */\nexport class Queue<T = unknown> {\n private _tasks: Array<{\n priority: number\n task: () => Promise<T>\n resolve: (value: T) => void\n reject: (reason: unknown) => void\n }> = []\n private _running = 0\n private _paused = false\n private _idleResolve: (() => void) | null = null\n private _maxConcurrency: number\n\n constructor(options?: QueueOptions) {\n this._maxConcurrency = options?.concurrency ?? 1\n if (this._maxConcurrency < 1) this._maxConcurrency = 1\n }\n\n get pending(): number {\n return this._tasks.length\n }\n\n get running(): number {\n return this._running\n }\n\n add<R>(task: () => Promise<R>, options?: { priority?: number }): Promise<R> {\n return new Promise<R>((resolve, reject) => {\n this._tasks.push({\n priority: options?.priority ?? 0,\n task: task as unknown as () => Promise<T>,\n resolve: resolve as (value: T) => void,\n reject,\n })\n this._tasks.sort((a, b) => b.priority - a.priority)\n this._process()\n })\n }\n\n pause(): void {\n this._paused = true\n }\n\n resume(): void {\n this._paused = false\n this._process()\n }\n\n clear(): void {\n const err = new Error('Queue cleared')\n for (const t of this._tasks) t.reject(err)\n this._tasks = []\n }\n\n onIdle(): Promise<void> {\n if (this._running === 0 && this._tasks.length === 0) return Promise.resolve()\n return new Promise<void>((resolve) => {\n this._idleResolve = resolve\n })\n }\n\n private _process(): void {\n if (this._paused) return\n while (this._running < this._maxConcurrency && this._tasks.length > 0) {\n const item = this._tasks.shift()!\n this._running++\n item\n .task()\n .then((result) => item.resolve(result))\n .catch((err) => item.reject(err))\n .finally(() => {\n this._running--\n this._process()\n if (this._running === 0 && this._tasks.length === 0 && this._idleResolve) {\n this._idleResolve()\n this._idleResolve = null\n }\n })\n }\n }\n}\n\nexport default Queue\n","/**\n * Semaphore for controlling concurrent access to a resource.\n *\n * @example\n * const sem = new Semaphore(2)\n * await sem.use(async () => {\n * // Only 2 callers at a time\n * await doSomething()\n * })\n */\nexport class Semaphore {\n private _available: number\n private _waiting: Array<() => void> = []\n\n constructor(concurrency: number) {\n if (concurrency < 1) throw new RangeError('Semaphore concurrency must be >= 1')\n this._available = concurrency\n }\n\n get available(): number {\n return this._available\n }\n\n acquire(): Promise<() => void> {\n if (this._available > 0) {\n this._available--\n return Promise.resolve(this._release.bind(this))\n }\n return new Promise<() => void>((resolve) => {\n this._waiting.push(() => {\n this._available--\n resolve(this._release.bind(this))\n })\n })\n }\n\n async use<T>(fn: () => Promise<T>): Promise<T> {\n const release = await this.acquire()\n try {\n return await fn()\n } finally {\n release()\n }\n }\n\n private _release(): void {\n this._available++\n if (this._waiting.length > 0) {\n const next = this._waiting.shift()\n if (next) next()\n }\n }\n}\n\nexport default Semaphore\n","/**\n * Options for memoizeAsync.\n */\nexport interface MemoizeAsyncOptions {\n /** Time-to-live in milliseconds (default: 60000) */\n ttl?: number\n /** Return stale value while fetching fresh data (default: false) */\n staleWhileRevalidate?: boolean\n /** Maximum number of cached entries (default: 100) */\n maxSize?: number\n /** Custom cache key resolver. Defaults to JSON.stringify of args. */\n resolver?: (...args: unknown[]) => string\n}\n\ninterface CacheEntry {\n value: unknown\n timestamp: number\n promise?: Promise<unknown>\n}\n\n/**\n * Memoizes an async function with TTL, stale-while-revalidate, and bounded cache.\n *\n * @example\n * const fetchData = memoizeAsync(async (id: string) => {\n * return await api.fetch(id)\n * }, { ttl: 5000, staleWhileRevalidate: true })\n *\n * const data = await fetchData('123') // cached for 5s\n * const data2 = await fetchData('123') // returns cached, refreshes in bg\n */\nexport function memoizeAsync<T extends (...args: unknown[]) => Promise<unknown>>(\n fn: T,\n options?: MemoizeAsyncOptions,\n): T & {\n cache: Map<string, CacheEntry>\n clear: () => void\n} {\n const {\n ttl = 60000,\n staleWhileRevalidate = false,\n maxSize = 100,\n resolver = (...args: unknown[]) => JSON.stringify(args),\n } = options ?? {}\n\n const cache = new Map<string, CacheEntry>()\n\n const memoized = (async (...args: unknown[]): Promise<unknown> => {\n const key = resolver(...args)\n const now = Date.now()\n const entry = cache.get(key)\n\n if (entry && now - entry.timestamp < ttl) {\n return entry.value\n }\n\n if (entry && staleWhileRevalidate && now - entry.timestamp >= ttl) {\n if (entry.promise) {\n return entry.value\n }\n entry.promise = fn(...args)\n .then((result) => {\n entry.value = result\n entry.timestamp = now\n entry.promise = undefined\n return result\n })\n .catch((err) => {\n entry.promise = undefined\n throw err\n })\n return entry.value\n }\n\n const result = await fn(...args)\n cache.set(key, { value: result, timestamp: now })\n\n if (cache.size > maxSize) {\n const firstKey = cache.keys().next().value\n if (firstKey !== undefined) cache.delete(firstKey)\n }\n\n return result\n }) as T & {\n cache: Map<string, CacheEntry>\n clear: () => void\n }\n\n memoized.cache = cache\n memoized.clear = () => cache.clear()\n\n return memoized\n}\n\nexport default memoizeAsync\n","/**\n * Rate limiter that restricts the number of requests within a sliding time window.\n *\n * @example\n * const limiter = new RateLimiter({ maxRequests: 5, perWindow: 1000 })\n * if (limiter.tryAcquire()) {\n * // Allowed\n * }\n *\n * @example\n * const limiter = new RateLimiter({ maxRequests: 2, perWindow: 1000 })\n * await limiter.acquire()\n * await limiter.acquire()\n * // Third call waits until the window rolls\n */\nexport class RateLimiter {\n private _maxRequests: number\n private _perWindow: number\n private _timestamps: number[] = []\n\n constructor(options: { maxRequests: number; perWindow: number }) {\n if (options.maxRequests < 1) throw new RangeError('maxRequests must be >= 1')\n if (options.perWindow < 1) throw new RangeError('perWindow must be >= 1')\n this._maxRequests = options.maxRequests\n this._perWindow = options.perWindow\n }\n\n private _prune(): void {\n const now = Date.now()\n const cutoff = now - this._perWindow\n while (this._timestamps.length > 0) {\n const ts = this._timestamps[0]\n if (ts === undefined || ts > cutoff) break\n this._timestamps.shift()\n }\n }\n\n /**\n * Check if a request is allowed without waiting.\n */\n tryAcquire(): boolean {\n this._prune()\n if (this._timestamps.length < this._maxRequests) {\n this._timestamps.push(Date.now())\n return true\n }\n return false\n }\n\n /**\n * Wait until a request is allowed.\n */\n async acquire(): Promise<void> {\n while (true) {\n this._prune()\n if (this._timestamps.length < this._maxRequests) {\n this._timestamps.push(Date.now())\n return\n }\n const oldest = this._timestamps[0] ?? Date.now()\n const waitMs = oldest + this._perWindow - Date.now()\n if (waitMs > 0) {\n await new Promise<void>(resolve => setTimeout(resolve, waitMs))\n }\n }\n }\n\n /**\n * Current number of pending requests in the window.\n */\n get pending(): number {\n this._prune()\n return this._timestamps.length\n }\n}\n\nexport default RateLimiter\n","/**\n * Mutex for exclusive access to a critical section.\n *\n * @example\n * const mutex = new Mutex()\n * const release = await mutex.acquire()\n * try {\n * // Exclusive access\n * } finally {\n * release()\n * }\n *\n * @example\n * const mutex = new Mutex()\n * const result = await mutex.use(async () => {\n * return await fetch('/api/data')\n * })\n */\nexport class Mutex {\n private _locked = false\n private _waiting: Array<() => void> = []\n\n /**\n * Acquire the lock. Returns a release function to be called when done.\n */\n acquire(): Promise<() => void> {\n if (!this._locked) {\n this._locked = true\n return Promise.resolve(this._release.bind(this))\n }\n return new Promise<() => void>(resolve => {\n this._waiting.push(() => {\n resolve(this._release.bind(this))\n })\n })\n }\n\n /**\n * Run a function with the lock held and automatically release it.\n */\n async use<T>(fn: () => Promise<T>): Promise<T> {\n const release = await this.acquire()\n try {\n return await fn()\n } finally {\n release()\n }\n }\n\n /**\n * Whether the mutex is currently locked.\n */\n get locked(): boolean {\n return this._locked\n }\n\n private _release(): void {\n if (this._waiting.length > 0) {\n const next = this._waiting.shift()\n if (next) next()\n } else {\n this._locked = false\n }\n }\n}\n\nexport default Mutex\n","/**\n * Process an array of items in sequential batches.\n *\n * @example\n * const items = [1, 2, 3, 4, 5]\n * const results = await batch(items, 2, async (batch) => {\n * return batch.map(n => n * 2)\n * })\n * // results = [2, 4, 6, 8, 10]\n */\nexport async function batch<T, R>(\n items: T[],\n batchSize: number,\n fn: (batch: T[]) => Promise<R[]>,\n): Promise<R[]> {\n if (batchSize < 1) throw new RangeError('batchSize must be >= 1')\n\n const results: R[] = []\n for (let i = 0; i < items.length; i += batchSize) {\n const chunk = items.slice(i, i + batchSize)\n const batchResults = await fn(chunk)\n results.push(...batchResults)\n }\n return results\n}\n\nexport default batch\n","/**\n * Execute async tasks in sequence, passing each result to the next task.\n *\n * @example\n * const result = await waterfall([\n * async (n: number) => n + 1,\n * async (n: number) => n * 2,\n * async (n: number) => `Result: ${n}`,\n * ], 1)\n * // result = \"Result: 4\"\n *\n * @example\n * const result = await waterfall([\n * async () => fetch('/api/data').then(r => r.json()),\n * async (data) => process(data),\n * ])\n */\nexport async function waterfall(\n tasks: Array<(arg: any) => Promise<any>>,\n initial?: any,\n): Promise<any> {\n let result = initial\n for (const task of tasks) {\n result = await task(result)\n }\n return result\n}\n\nexport default waterfall\n","import type { RetryOptions } from '../core/index.js'\r\n\r\n/**\r\n * Delays execution for the given number of milliseconds.\r\n */\r\nexport function sleep(ms: number): Promise<void> {\r\n return new Promise(resolve => setTimeout(resolve, ms))\r\n}\r\n\r\n/**\r\n * Rejects a promise if it does not resolve within the specified timeout.\r\n */\r\nexport async function timeout<T>(promise: Promise<T>, ms: number, errorMessage?: string): Promise<T> {\r\n let timer: ReturnType<typeof setTimeout> | undefined\r\n const timeoutPromise = new Promise<never>((_, reject) => {\r\n timer = setTimeout(() => reject(new Error(errorMessage ?? `Promise timed out after ${ms}ms`)), ms)\r\n })\r\n try {\r\n return await Promise.race([promise, timeoutPromise])\r\n } finally {\r\n if (timer !== undefined) clearTimeout(timer)\r\n }\r\n}\r\n\r\n/**\r\n * Races a promise against a timeout, returning 'timeout' if the timer wins.\r\n */\r\nexport async function raceWithTimeout<T>(promise: Promise<T>, ms: number): Promise<T | 'timeout'> {\r\n let timer: ReturnType<typeof setTimeout> | undefined\r\n const timeoutPromise = new Promise<'timeout'>(resolve => {\r\n timer = setTimeout(() => resolve('timeout'), ms)\r\n })\r\n try {\r\n return await Promise.race([promise, timeoutPromise])\r\n } finally {\r\n if (timer !== undefined) clearTimeout(timer)\r\n }\r\n}\r\n\r\n/**\r\n * Maps over an array with an async function, using Promise.allSettled.\r\n * Returns an array of PromiseSettledResult.\r\n */\r\nexport async function allSettledMap<T, R>(\r\n items: T[],\r\n fn: (item: T) => Promise<R>,\r\n): Promise<PromiseSettledResult<R>[]> {\r\n return await Promise.allSettled(items.map(fn))\r\n}\r\n\r\n/**\r\n * Maps over an array with an async function, limiting concurrency.\r\n *\r\n * @param concurrency - Maximum number of concurrent operations (default Infinity)\r\n */\r\nexport async function parallelMap<T, R>(\r\n items: T[],\r\n fn: (item: T) => Promise<R>,\r\n concurrency = Number.POSITIVE_INFINITY,\r\n): Promise<R[]> {\r\n if (concurrency === Number.POSITIVE_INFINITY) {\r\n return await Promise.all(items.map(fn))\r\n }\r\n\r\n const results: R[] = []\r\n const queue = [...items]\r\n\r\n const worker = async (): Promise<void> => {\r\n while (queue.length > 0) {\r\n const item = queue.shift()!\r\n results.push(await fn(item))\r\n }\r\n }\r\n\r\n const workerCount = Math.min(concurrency, items.length)\r\n const workers = Array.from({ length: workerCount }, () => worker())\r\n await Promise.all(workers)\r\n return results\r\n}\r\n\r\n/**\r\n * Retries an async function with exponential backoff.\r\n */\r\nexport async function retryAsync<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T> {\r\n const { attempts = 3, baseDelay = 1000, maxDelay = 30000, shouldRetry = () => true } = options ?? {}\r\n let lastError: unknown\r\n for (let attempt = 0; attempt < attempts; attempt++) {\r\n try {\r\n return await fn()\r\n } catch (error) {\r\n lastError = error\r\n if (!shouldRetry(error) || attempt >= attempts - 1) break\r\n const delay = Math.min(baseDelay * 2 ** attempt + Math.random() * baseDelay, maxDelay)\r\n await sleep(delay)\r\n }\r\n }\r\n throw lastError\r\n}\r\n\r\n/**\r\n * Composes async functions, passing the result of each to the next.\r\n */\r\nexport async function pipeline<T>(initial: T, ...fns: Array<(arg: T) => Promise<T>>): Promise<T> {\r\n let result = initial\r\n for (const fn of fns) {\r\n result = await fn(result)\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * Creates a deferred promise with external resolve and reject methods.\r\n */\r\nexport function deferred<T>(): Deferred<T> {\r\n let resolve!: (value: T) => void\r\n let reject!: (reason: unknown) => void\r\n const promise = new Promise<T>((res, rej) => {\r\n resolve = res\r\n reject = rej\r\n })\r\n return { promise, resolve, reject }\r\n}\r\n\r\nexport interface Deferred<T> {\r\n promise: Promise<T>\r\n resolve: (value: T) => void\r\n reject: (reason: unknown) => void\r\n}\r\n\r\n// ─── Queue, Semaphore, memoizeAsync ─────────────────────\r\nexport { Queue } from './queue.js'\r\nexport type { QueueOptions } from './queue.js'\r\nexport { Semaphore } from './semaphore.js'\r\nexport { memoizeAsync } from './memoize.js'\r\nexport type { MemoizeAsyncOptions } from './memoize.js'\r\n\r\n// ─── RateLimiter, Mutex, batch, waterfall ───────────────\r\nexport { RateLimiter } from './ratelimit.js'\r\nexport { Mutex } from './mutex.js'\r\nexport { batch } from './batch.js'\r\nexport { waterfall } from './waterfall.js'\r\n"],"mappings":"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","names":["result"]}
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Groups an array of items by a key extracted from each item.
3
+ *
4
+ * @example groupBy([{ type: 'a' }, { type: 'b' }, { type: 'a' }], x => x.type)
5
+ * // => { a: [{ type: 'a' }, { type: 'a' }], b: [{ type: 'b' }] }
6
+ */
7
+ declare function groupBy<T, K extends string | number | symbol>(items: T[], keyFn: (item: T) => K): Record<K, T[]>;
8
+ /**
9
+ * Creates an object keyed by the result of keyFn for each item.
10
+ *
11
+ * @example keyBy([{ id: 1, name: 'a' }, { id: 2, name: 'b' }], x => x.id)
12
+ * // => { 1: { id: 1, name: 'a' }, 2: { id: 2, name: 'b' } }
13
+ */
14
+ declare function keyBy<T, K extends string | number | symbol>(items: T[], keyFn: (item: T) => K): Record<K, T>;
15
+ /**
16
+ * Returns a copy of the object with the specified keys omitted.
17
+ */
18
+ declare function omit<T extends Record<string, unknown>, K extends keyof T>(obj: T, keys: K[]): Omit<T, K>;
19
+ /**
20
+ * Returns a copy of the object with only the specified keys.
21
+ */
22
+ declare function pick<T extends Record<string, unknown>, K extends keyof T>(obj: T, keys: K[]): Pick<T, K>;
23
+ /**
24
+ * Extracts a property value from each item in an array.
25
+ */
26
+ declare function pluck<T, K extends keyof T>(items: T[], key: K): T[K][];
27
+ /**
28
+ * Randomizes array order in-place using the Fisher-Yates algorithm.
29
+ */
30
+ declare function shuffle<T>(items: T[]): T[];
31
+ /**
32
+ * Returns a random element from the array, or undefined if empty.
33
+ */
34
+ declare function sample<T>(items: T[]): T | undefined;
35
+ /**
36
+ * Returns an array of `size` random elements (without duplicates).
37
+ */
38
+ declare function sampleSize<T>(items: T[], size: number): T[];
39
+ /**
40
+ * Splits an array into chunks of the specified size.
41
+ */
42
+ declare function chunk<T>(items: T[], size: number): T[][];
43
+ declare function sortBy<T>(items: T[], ...criteria: Array<(item: T) => unknown>): T[];
44
+ /**
45
+ * Returns a sorted copy of the array by a single key and direction.
46
+ */
47
+ declare function orderBy<T>(items: T[], key: (item: T) => unknown, direction?: SortDirection): T[];
48
+ /**
49
+ * Returns unique elements based on the result of keyFn.
50
+ */
51
+ declare function uniqueBy<T>(items: T[], keyFn: (item: T) => unknown): T[];
52
+ /**
53
+ * Flattens an array of arrays one level.
54
+ */
55
+ declare function flatten<T>(items: T[][]): T[];
56
+ /**
57
+ * Returns an array with unique primitive values.
58
+ */
59
+ declare function uniq<T>(items: T[]): T[];
60
+ /**
61
+ * Returns the first element, or undefined if empty.
62
+ */
63
+ declare function first<T>(items: T[]): T | undefined;
64
+ /**
65
+ * Returns the last element, or undefined if empty.
66
+ */
67
+ declare function last<T>(items: T[]): T | undefined;
68
+ /**
69
+ * Checks if a value is empty.
70
+ * Works for arrays, objects, strings, Map, and Set.
71
+ */
72
+ declare function isEmpty(value: unknown): boolean;
73
+ type SortDirection = 'asc' | 'desc';
74
+ declare function topoSort<T extends {
75
+ id: string;
76
+ dependencies?: string[];
77
+ }>(items: T[]): T[];
78
+ declare function slidingWindows<T>(items: T[], size: number, step?: number): T[][];
79
+ declare function tumblingWindows<T>(items: T[], size: number): T[][];
80
+ /**
81
+ * Gets a nested value from an object using a dot-separated path.
82
+ *
83
+ * @example deepGet({ a: { b: 2 } }, 'a.b') // 2
84
+ * @example deepGet({ a: { b: 2 } }, 'a.c') // undefined
85
+ */
86
+ declare function deepGet<T = unknown>(obj: unknown, path: string, default_?: T): T | undefined;
87
+ /**
88
+ * Sets a nested value in an object using a dot-separated path.
89
+ * Creates intermediate objects/arrays as needed.
90
+ *
91
+ * @example deepSet({ a: { b: 2 } }, 'a.b', 3) // { a: { b: 3 } }
92
+ * @example deepSet({}, 'a.b.c', 1) // { a: { b: { c: 1 } } }
93
+ */
94
+ declare function deepSet<T extends Record<string, unknown>>(obj: T, path: string, value: unknown): T;
95
+ /**
96
+ * Splits an array into two groups: those that pass the predicate and those that don't.
97
+ *
98
+ * @example partition([1, 2, 3, 4, 5], n => n % 2 === 0)
99
+ * // => [[2, 4], [1, 3, 5]]
100
+ */
101
+ declare function partition<T>(items: T[], predicate: (item: T) => boolean): [T[], T[]];
102
+ /**
103
+ * Removes all falsy values from an array (false, null, 0, '', undefined, NaN).
104
+ *
105
+ * @example compact([0, 1, false, 2, '', 3, null, undefined, NaN])
106
+ * // => [1, 2, 3]
107
+ */
108
+ declare function compact<T>(items: (T | false | null | 0 | '' | undefined)[]): T[];
109
+ /**
110
+ * Returns elements in array A that are not in array B (uses SameValueZero).
111
+ *
112
+ * @example difference([1, 2, 3, 4], [2, 4])
113
+ * // => [1, 3]
114
+ */
115
+ declare function difference<T>(a: T[], b: T[]): T[];
116
+ /**
117
+ * Returns elements present in all given arrays (uses SameValueZero).
118
+ *
119
+ * @example intersection([1, 2, 3], [2, 3, 4])
120
+ * // => [2, 3]
121
+ */
122
+ declare function intersection<T>(a: T[], b: T[]): T[];
123
+ /**
124
+ * Returns unique elements from all given arrays.
125
+ *
126
+ * @example union([1, 2], [2, 3], [3, 4])
127
+ * // => [1, 2, 3, 4]
128
+ */
129
+ declare function union<T>(...arrays: T[][]): T[];
130
+ /**
131
+ * Merges arrays element-wise into tuples, stopping at the shortest array.
132
+ *
133
+ * @example zip(['a', 'b', 'c'], [1, 2])
134
+ * // => [['a', 1], ['b', 2]]
135
+ */
136
+ declare function zip<T, U>(a: T[], b: U[]): [T, U][];
137
+ declare function zip<T, U, V>(a: T[], b: U[], c: V[]): [T, U, V][];
138
+ /**
139
+ * Inverse of zip: splits an array of tuples back into individual arrays.
140
+ *
141
+ * @example unzip([['a', 1], ['b', 2]])
142
+ * // => [['a', 'b'], [1, 2]]
143
+ */
144
+ declare function unzip<T>(paired: T[][]): T[][];
145
+ /**
146
+ * Counts occurrences of each key produced by the key function.
147
+ *
148
+ * @example countBy([1, 2, 3, 4, 5], n => n % 2 === 0 ? 'even' : 'odd')
149
+ * // => { odd: 3, even: 2 }
150
+ */
151
+ declare function countBy<T, K extends string | number | symbol>(items: T[], keyFn: (item: T) => K): Record<K, number>;
152
+ /**
153
+ * Returns the element with the maximum value by the key function.
154
+ *
155
+ * @example maxBy([{ name: 'a', score: 10 }, { name: 'b', score: 20 }], x => x.score)
156
+ * // => { name: 'b', score: 20 }
157
+ */
158
+ declare function maxBy<T>(items: T[], keyFn: (item: T) => number): T | undefined;
159
+ /**
160
+ * Returns the element with the minimum value by the key function.
161
+ *
162
+ * @example minBy([{ name: 'a', score: 10 }, { name: 'b', score: 20 }], x => x.score)
163
+ * // => { name: 'a', score: 10 }
164
+ */
165
+ declare function minBy<T>(items: T[], keyFn: (item: T) => number): T | undefined;
166
+ /**
167
+ * Returns the sum of values produced by the key function.
168
+ *
169
+ * @example sumBy([{ n: 1 }, { n: 2 }, { n: 3 }], x => x.n)
170
+ * // => 6
171
+ */
172
+ declare function sumBy<T>(items: T[], keyFn: (item: T) => number): number;
173
+ /**
174
+ * Returns the index of the first element satisfying the predicate, or -1.
175
+ *
176
+ * @example findIndex([1, 3, 5, 8, 10], n => n % 2 === 0)
177
+ * // => 3
178
+ */
179
+ declare function findIndex<T>(items: T[], predicate: (item: T) => boolean, fromIndex?: number): number;
180
+ /**
181
+ * Finds the last element satisfying the predicate.
182
+ *
183
+ * @example findLast([1, 2, 3, 4, 5], n => n % 2 === 0)
184
+ * // => 4
185
+ */
186
+ declare function findLast<T>(items: T[], predicate: (item: T) => boolean): T | undefined;
187
+ /**
188
+ * Drops the first n elements from the array.
189
+ *
190
+ * @example drop([1, 2, 3, 4, 5], 2)
191
+ * // => [3, 4, 5]
192
+ */
193
+ declare function drop<T>(items: T[], n?: number): T[];
194
+ /**
195
+ * Drops the last n elements from the array.
196
+ *
197
+ * @example dropRight([1, 2, 3, 4, 5], 2)
198
+ * // => [1, 2, 3]
199
+ */
200
+ declare function dropRight<T>(items: T[], n?: number): T[];
201
+ /**
202
+ * Takes the first n elements from the array.
203
+ *
204
+ * @example take([1, 2, 3, 4, 5], 2)
205
+ * // => [1, 2]
206
+ */
207
+ declare function take<T>(items: T[], n?: number): T[];
208
+ /**
209
+ * Takes the last n elements from the array.
210
+ *
211
+ * @example takeRight([1, 2, 3, 4, 5], 2)
212
+ * // => [4, 5]
213
+ */
214
+ declare function takeRight<T>(items: T[], n?: number): T[];
215
+ /**
216
+ * Removes specified values from the array (uses SameValueZero).
217
+ *
218
+ * @example without([1, 2, 1, 3, 1, 4], 1, 3)
219
+ * // => [2, 4]
220
+ */
221
+ declare function without<T>(items: T[], ...values: T[]): T[];
222
+ /**
223
+ * Gets the element at the given index. Supports negative indexing.
224
+ *
225
+ * @example nth([1, 2, 3], 1) // => 2
226
+ * @example nth([1, 2, 3], -1) // => 3
227
+ */
228
+ declare function nth<T>(items: T[], index: number): T | undefined;
229
+
230
+ export { type SortDirection, chunk, compact, countBy, deepGet, deepSet, difference, drop, dropRight, findIndex, findLast, first, flatten, groupBy, intersection, isEmpty, keyBy, last, maxBy, minBy, nth, omit, orderBy, partition, pick, pluck, sample, sampleSize, shuffle, slidingWindows, sortBy, sumBy, take, takeRight, topoSort, tumblingWindows, union, uniq, uniqueBy, unzip, without, zip };