superjs-core 0.4.3 → 0.5.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/CHANGELOG.md +117 -0
- package/CONTRIBUTING.md +55 -0
- package/PUBLISH.md +45 -0
- package/README.md +9 -5
- package/ROADMAP.md +72 -0
- package/SECURITY.md +35 -0
- package/SUMMARY.md +321 -0
- package/dist/async/index.d.ts +106 -1
- package/dist/async/index.js +128 -1
- package/dist/async/index.js.map +1 -1
- package/dist/collection/index.d.ts +135 -1
- package/dist/collection/index.js +145 -1
- package/dist/collection/index.js.map +1 -1
- package/dist/color/index.d.ts +79 -1
- package/dist/color/index.js +101 -0
- package/dist/color/index.js.map +1 -1
- package/dist/date/index.d.ts +316 -1
- package/dist/date/index.js +339 -1
- package/dist/date/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/isNoRekening-CHSpgD4P.d.ts +167 -0
- package/dist/math/index.d.ts +188 -1
- package/dist/math/index.js +199 -1
- package/dist/math/index.js.map +1 -1
- package/dist/string/index.d.ts +71 -1
- package/dist/string/index.js +94 -0
- package/dist/string/index.js.map +1 -1
- package/dist/validation/index.d.ts +22 -150
- package/dist/validation/index.js +27 -0
- package/dist/validation/index.js.map +1 -1
- package/package.json +3 -2
package/dist/async/index.d.ts
CHANGED
|
@@ -83,6 +83,111 @@ declare function memoizeAsync<T extends (...args: unknown[]) => Promise<unknown>
|
|
|
83
83
|
clear: () => void;
|
|
84
84
|
};
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Rate limiter that restricts the number of requests within a sliding time window.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* const limiter = new RateLimiter({ maxRequests: 5, perWindow: 1000 })
|
|
91
|
+
* if (limiter.tryAcquire()) {
|
|
92
|
+
* // Allowed
|
|
93
|
+
* }
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* const limiter = new RateLimiter({ maxRequests: 2, perWindow: 1000 })
|
|
97
|
+
* await limiter.acquire()
|
|
98
|
+
* await limiter.acquire()
|
|
99
|
+
* // Third call waits until the window rolls
|
|
100
|
+
*/
|
|
101
|
+
declare class RateLimiter {
|
|
102
|
+
private _maxRequests;
|
|
103
|
+
private _perWindow;
|
|
104
|
+
private _timestamps;
|
|
105
|
+
constructor(options: {
|
|
106
|
+
maxRequests: number;
|
|
107
|
+
perWindow: number;
|
|
108
|
+
});
|
|
109
|
+
private _prune;
|
|
110
|
+
/**
|
|
111
|
+
* Check if a request is allowed without waiting.
|
|
112
|
+
*/
|
|
113
|
+
tryAcquire(): boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Wait until a request is allowed.
|
|
116
|
+
*/
|
|
117
|
+
acquire(): Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* Current number of pending requests in the window.
|
|
120
|
+
*/
|
|
121
|
+
get pending(): number;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Mutex for exclusive access to a critical section.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* const mutex = new Mutex()
|
|
129
|
+
* const release = await mutex.acquire()
|
|
130
|
+
* try {
|
|
131
|
+
* // Exclusive access
|
|
132
|
+
* } finally {
|
|
133
|
+
* release()
|
|
134
|
+
* }
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* const mutex = new Mutex()
|
|
138
|
+
* const result = await mutex.use(async () => {
|
|
139
|
+
* return await fetch('/api/data')
|
|
140
|
+
* })
|
|
141
|
+
*/
|
|
142
|
+
declare class Mutex {
|
|
143
|
+
private _locked;
|
|
144
|
+
private _waiting;
|
|
145
|
+
/**
|
|
146
|
+
* Acquire the lock. Returns a release function to be called when done.
|
|
147
|
+
*/
|
|
148
|
+
acquire(): Promise<() => void>;
|
|
149
|
+
/**
|
|
150
|
+
* Run a function with the lock held and automatically release it.
|
|
151
|
+
*/
|
|
152
|
+
use<T>(fn: () => Promise<T>): Promise<T>;
|
|
153
|
+
/**
|
|
154
|
+
* Whether the mutex is currently locked.
|
|
155
|
+
*/
|
|
156
|
+
get locked(): boolean;
|
|
157
|
+
private _release;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Process an array of items in sequential batches.
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* const items = [1, 2, 3, 4, 5]
|
|
165
|
+
* const results = await batch(items, 2, async (batch) => {
|
|
166
|
+
* return batch.map(n => n * 2)
|
|
167
|
+
* })
|
|
168
|
+
* // results = [2, 4, 6, 8, 10]
|
|
169
|
+
*/
|
|
170
|
+
declare function batch<T, R>(items: T[], batchSize: number, fn: (batch: T[]) => Promise<R[]>): Promise<R[]>;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Execute async tasks in sequence, passing each result to the next task.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* const result = await waterfall([
|
|
177
|
+
* async (n: number) => n + 1,
|
|
178
|
+
* async (n: number) => n * 2,
|
|
179
|
+
* async (n: number) => `Result: ${n}`,
|
|
180
|
+
* ], 1)
|
|
181
|
+
* // result = "Result: 4"
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* const result = await waterfall([
|
|
185
|
+
* async () => fetch('/api/data').then(r => r.json()),
|
|
186
|
+
* async (data) => process(data),
|
|
187
|
+
* ])
|
|
188
|
+
*/
|
|
189
|
+
declare function waterfall(tasks: Array<(arg: any) => Promise<any>>, initial?: any): Promise<any>;
|
|
190
|
+
|
|
86
191
|
/**
|
|
87
192
|
* Delays execution for the given number of milliseconds.
|
|
88
193
|
*/
|
|
@@ -124,4 +229,4 @@ interface Deferred<T> {
|
|
|
124
229
|
reject: (reason: unknown) => void;
|
|
125
230
|
}
|
|
126
231
|
|
|
127
|
-
export { type Deferred, type MemoizeAsyncOptions, Queue, type QueueOptions, Semaphore, allSettledMap, deferred, memoizeAsync, parallelMap, pipeline, raceWithTimeout, retryAsync, sleep, timeout };
|
|
232
|
+
export { type Deferred, type MemoizeAsyncOptions, Mutex, Queue, type QueueOptions, RateLimiter, Semaphore, allSettledMap, batch, deferred, memoizeAsync, parallelMap, pipeline, raceWithTimeout, retryAsync, sleep, timeout, waterfall };
|
package/dist/async/index.js
CHANGED
|
@@ -146,6 +146,129 @@ function memoizeAsync(fn, options) {
|
|
|
146
146
|
return memoized;
|
|
147
147
|
}
|
|
148
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
|
+
|
|
149
272
|
// src/async/index.ts
|
|
150
273
|
function sleep(ms) {
|
|
151
274
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -224,9 +347,12 @@ function deferred() {
|
|
|
224
347
|
return { promise, resolve, reject };
|
|
225
348
|
}
|
|
226
349
|
export {
|
|
350
|
+
Mutex,
|
|
227
351
|
Queue,
|
|
352
|
+
RateLimiter,
|
|
228
353
|
Semaphore,
|
|
229
354
|
allSettledMap,
|
|
355
|
+
batch,
|
|
230
356
|
deferred,
|
|
231
357
|
memoizeAsync,
|
|
232
358
|
parallelMap,
|
|
@@ -234,6 +360,7 @@ export {
|
|
|
234
360
|
raceWithTimeout,
|
|
235
361
|
retryAsync,
|
|
236
362
|
sleep,
|
|
237
|
-
timeout
|
|
363
|
+
timeout,
|
|
364
|
+
waterfall
|
|
238
365
|
};
|
|
239
366
|
//# sourceMappingURL=index.js.map
|
package/dist/async/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/async/queue.ts","../../src/async/semaphore.ts","../../src/async/memoize.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","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"],"mappings":"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","names":["result"]}
|
|
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"]}
|
|
@@ -92,5 +92,139 @@ declare function deepGet<T = unknown>(obj: unknown, path: string, default_?: T):
|
|
|
92
92
|
* @example deepSet({}, 'a.b.c', 1) // { a: { b: { c: 1 } } }
|
|
93
93
|
*/
|
|
94
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;
|
|
95
229
|
|
|
96
|
-
export { type SortDirection, chunk, deepGet, deepSet, first, flatten, groupBy, isEmpty, keyBy, last, omit, orderBy, pick, pluck, sample, sampleSize, shuffle, slidingWindows, sortBy, topoSort, tumblingWindows, uniq, uniqueBy };
|
|
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 };
|
package/dist/collection/index.js
CHANGED
|
@@ -198,18 +198,155 @@ function deepSet(obj, path, value) {
|
|
|
198
198
|
current[keys[keys.length - 1]] = value;
|
|
199
199
|
return result;
|
|
200
200
|
}
|
|
201
|
+
function partition(items, predicate) {
|
|
202
|
+
const pass = [];
|
|
203
|
+
const fail = [];
|
|
204
|
+
for (const item of items) {
|
|
205
|
+
if (predicate(item)) pass.push(item);
|
|
206
|
+
else fail.push(item);
|
|
207
|
+
}
|
|
208
|
+
return [pass, fail];
|
|
209
|
+
}
|
|
210
|
+
function compact(items) {
|
|
211
|
+
return items.filter(Boolean);
|
|
212
|
+
}
|
|
213
|
+
function difference(a, b) {
|
|
214
|
+
const setB = new Set(b);
|
|
215
|
+
return a.filter((item) => !setB.has(item));
|
|
216
|
+
}
|
|
217
|
+
function intersection(a, b) {
|
|
218
|
+
const setB = new Set(b);
|
|
219
|
+
return a.filter((item) => setB.has(item));
|
|
220
|
+
}
|
|
221
|
+
function union(...arrays) {
|
|
222
|
+
const set = /* @__PURE__ */ new Set();
|
|
223
|
+
for (const arr of arrays) {
|
|
224
|
+
for (const item of arr) {
|
|
225
|
+
set.add(item);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return [...set];
|
|
229
|
+
}
|
|
230
|
+
function zip(...arrays) {
|
|
231
|
+
if (arrays.length === 0) return [];
|
|
232
|
+
const minLen = Math.min(...arrays.map((a) => a.length));
|
|
233
|
+
const result = [];
|
|
234
|
+
for (let i = 0; i < minLen; i++) {
|
|
235
|
+
const tuple = [];
|
|
236
|
+
for (const arr of arrays) {
|
|
237
|
+
tuple.push(arr[i]);
|
|
238
|
+
}
|
|
239
|
+
result.push(tuple);
|
|
240
|
+
}
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
function unzip(paired) {
|
|
244
|
+
if (paired.length === 0) return [];
|
|
245
|
+
const tupleLen = paired.reduce((max, t) => Math.max(max, t.length), 0);
|
|
246
|
+
const result = Array.from({ length: tupleLen }, () => []);
|
|
247
|
+
for (const tuple of paired) {
|
|
248
|
+
for (let i = 0; i < tupleLen; i++) {
|
|
249
|
+
result[i].push(tuple[i]);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
254
|
+
function countBy(items, keyFn) {
|
|
255
|
+
const result = {};
|
|
256
|
+
for (const item of items) {
|
|
257
|
+
const key = keyFn(item);
|
|
258
|
+
result[key] = (result[key] ?? 0) + 1;
|
|
259
|
+
}
|
|
260
|
+
return result;
|
|
261
|
+
}
|
|
262
|
+
function maxBy(items, keyFn) {
|
|
263
|
+
if (items.length === 0) return void 0;
|
|
264
|
+
let maxItem = items[0];
|
|
265
|
+
let maxVal = keyFn(maxItem);
|
|
266
|
+
for (let i = 1; i < items.length; i++) {
|
|
267
|
+
const val = keyFn(items[i]);
|
|
268
|
+
if (val > maxVal) {
|
|
269
|
+
maxVal = val;
|
|
270
|
+
maxItem = items[i];
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return maxItem;
|
|
274
|
+
}
|
|
275
|
+
function minBy(items, keyFn) {
|
|
276
|
+
if (items.length === 0) return void 0;
|
|
277
|
+
let minItem = items[0];
|
|
278
|
+
let minVal = keyFn(minItem);
|
|
279
|
+
for (let i = 1; i < items.length; i++) {
|
|
280
|
+
const val = keyFn(items[i]);
|
|
281
|
+
if (val < minVal) {
|
|
282
|
+
minVal = val;
|
|
283
|
+
minItem = items[i];
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return minItem;
|
|
287
|
+
}
|
|
288
|
+
function sumBy(items, keyFn) {
|
|
289
|
+
let total = 0;
|
|
290
|
+
for (const item of items) {
|
|
291
|
+
total += keyFn(item);
|
|
292
|
+
}
|
|
293
|
+
return total;
|
|
294
|
+
}
|
|
295
|
+
function findIndex(items, predicate, fromIndex = 0) {
|
|
296
|
+
for (let i = fromIndex; i < items.length; i++) {
|
|
297
|
+
if (predicate(items[i])) return i;
|
|
298
|
+
}
|
|
299
|
+
return -1;
|
|
300
|
+
}
|
|
301
|
+
function findLast(items, predicate) {
|
|
302
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
303
|
+
if (predicate(items[i])) return items[i];
|
|
304
|
+
}
|
|
305
|
+
return void 0;
|
|
306
|
+
}
|
|
307
|
+
function drop(items, n = 1) {
|
|
308
|
+
return items.slice(Math.max(0, n));
|
|
309
|
+
}
|
|
310
|
+
function dropRight(items, n = 1) {
|
|
311
|
+
return items.slice(0, Math.max(0, items.length - n));
|
|
312
|
+
}
|
|
313
|
+
function take(items, n = 1) {
|
|
314
|
+
return items.slice(0, Math.max(0, n));
|
|
315
|
+
}
|
|
316
|
+
function takeRight(items, n = 1) {
|
|
317
|
+
return items.slice(Math.max(0, items.length - n));
|
|
318
|
+
}
|
|
319
|
+
function without(items, ...values) {
|
|
320
|
+
const exclude = new Set(values);
|
|
321
|
+
return items.filter((item) => !exclude.has(item));
|
|
322
|
+
}
|
|
323
|
+
function nth(items, index) {
|
|
324
|
+
return index < 0 ? items[items.length + index] : items[index];
|
|
325
|
+
}
|
|
201
326
|
export {
|
|
202
327
|
chunk,
|
|
328
|
+
compact,
|
|
329
|
+
countBy,
|
|
203
330
|
deepGet,
|
|
204
331
|
deepSet,
|
|
332
|
+
difference,
|
|
333
|
+
drop,
|
|
334
|
+
dropRight,
|
|
335
|
+
findIndex,
|
|
336
|
+
findLast,
|
|
205
337
|
first,
|
|
206
338
|
flatten,
|
|
207
339
|
groupBy,
|
|
340
|
+
intersection,
|
|
208
341
|
isEmpty,
|
|
209
342
|
keyBy,
|
|
210
343
|
last,
|
|
344
|
+
maxBy,
|
|
345
|
+
minBy,
|
|
346
|
+
nth,
|
|
211
347
|
omit,
|
|
212
348
|
orderBy,
|
|
349
|
+
partition,
|
|
213
350
|
pick,
|
|
214
351
|
pluck,
|
|
215
352
|
sample,
|
|
@@ -217,9 +354,16 @@ export {
|
|
|
217
354
|
shuffle,
|
|
218
355
|
slidingWindows,
|
|
219
356
|
sortBy,
|
|
357
|
+
sumBy,
|
|
358
|
+
take,
|
|
359
|
+
takeRight,
|
|
220
360
|
topoSort,
|
|
221
361
|
tumblingWindows,
|
|
362
|
+
union,
|
|
222
363
|
uniq,
|
|
223
|
-
uniqueBy
|
|
364
|
+
uniqueBy,
|
|
365
|
+
unzip,
|
|
366
|
+
without,
|
|
367
|
+
zip
|
|
224
368
|
};
|
|
225
369
|
//# sourceMappingURL=index.js.map
|