superjs-core 0.4.4 → 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 CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.4] - 2026-06-28
4
+
5
+ ### Diubah
6
+ - Semua markdown file dipindah ke packages/core/ dan include di npm package
7
+ - `files` di package.json: nambah `"*.md"` biar dokumentasi ikut ter-publish
8
+
9
+ ## [0.4.3] - 2026-06-28
10
+
11
+ ### Diubah
12
+ - Markdown file dipindah dari root ke packages/core/
13
+
14
+ ## [0.4.2] - 2026-06-28
15
+
16
+ ### Ditambahkan
17
+ - **validation** — `parseNIK()` extract data dari NIK (gender, provinsi, tanggal lahir)
18
+ - **validation** — `isPlatNomor()` validasi plat kendaraan Indonesia (B 1234 CD)
19
+ - **validation** — `isKodepos()` validasi kode pos Indonesia (5 digit)
20
+ - **validation** — `isNoRekening()` validasi nomor rekening bank (8-16 digit)
21
+ - 828 total tests (19 test files)
22
+
23
+ ## [0.4.1] - 2026-06-28
24
+
25
+ ### Diubah
26
+ - Semua dokumentasi pake Bahasa Indonesia
27
+ - Description npm: fokus ke developer Indonesia
28
+
3
29
  ## [0.4.0] - 2026-06-28
4
30
 
5
31
  ### Ditambahkan
package/CONTRIBUTING.md CHANGED
@@ -14,7 +14,7 @@ npx vitest run
14
14
  1. Tambah fungsi di module yang sesuai `packages/core/src/<module>/`
15
15
  2. Export dari `index.ts` module tersebut
16
16
  3. Tambah test di `packages/core/tests/`
17
- 4. Jalankan `npx vitest run` (810 tests harus passing)
17
+ 4. Jalankan `npx vitest run` (828 tests harus passing)
18
18
  5. Jalankan `npx tsup` biar build sukses
19
19
  6. Update `SUMMARY.md` sama `README.md`
20
20
 
package/PUBLISH.md CHANGED
@@ -14,7 +14,7 @@ cd packages/core
14
14
  # 1. Build
15
15
  npx tsup
16
16
 
17
- # 2. Test (810 tests)
17
+ # 2. Test (828 tests)
18
18
  npx vitest run
19
19
 
20
20
  # 3. Bump version
@@ -38,7 +38,7 @@ git push origin master
38
38
  ## Checklist Sebelum Publish
39
39
 
40
40
  - [ ] `npx tsup` — build sukses
41
- - [ ] `npx vitest run` — 810 tests pass
41
+ - [ ] `npx vitest run` — 828 tests pass
42
42
  - [ ] `npm login` — udah login
43
43
  - [ ] Changelog udah diupdate
44
44
  - [ ] README udah sesuai
package/README.md CHANGED
@@ -17,8 +17,12 @@ Satu package buat semua kebutuhan JavaScript lo: utility functions, async helper
17
17
  | Fitur | Fungsi |
18
18
  |-------|--------|
19
19
  | **Validasi NIK** | `isNIK('3201010203940001')` — validasi 16 digit + tanggal lahir |
20
+ | **Parse NIK** | `parseNIK('320101...')` — extract gender, provinsi, tanggal lahir |
20
21
  | **Validasi NPWP** | `isNPWP('12.345.678.9-012.344')` — dengan checksum otomatis |
22
+ | **Validasi Plat Nomor** | `isPlatNomor('B 1234 CD')` — validasi plat kendaraan |
21
23
  | **Validasi Nomor HP** | `isPhone('08123456789')` — support semua prefix Indonesia |
24
+ | **Validasi Kode Pos** | `isKodepos('16110')` — validasi kode pos 5 digit |
25
+ | **Validasi No. Rekening** | `isNoRekening('1234567890')` — validasi 8-16 digit |
22
26
  | **Terbilang** | `terbilang(1500000)` → "satu juta lima ratus ribu" |
23
27
  | **Format Rupiah** | `formatRupiah(1500000)` → "Rp1.500.000" |
24
28
  | **Format Waktu** | `timeAgo(new Date(...))` → "5 detik yang lalu" |
@@ -42,7 +46,7 @@ Satu package buat semua kebutuhan JavaScript lo: utility functions, async helper
42
46
  | **crypto** | hash, base64, generateToken, generateOTP, constantTimeEqual |
43
47
  | **path** | join, resolve, basename, dirname, extname, normalize |
44
48
  | **color** | hexToRgb, rgbToHex, lighten, darken, contrastRatio, meetsWCAG |
45
- | **validation** | **isNIK**, **isNPWP**, **isPhone("id")**, isEmail, isURL |
49
+ | **validation** | **isNIK**, **parseNIK**, **isNPWP**, **isPlatNomor**, **isPhone**, **isKodepos**, **isNoRekening**, isEmail, isURL |
46
50
  | **error** | createError (typed + HTTP status), TypedError, MultiError |
47
51
  | **logger** | Logger class, child loggers, console/JSON/file transports |
48
52
  | **dep-exray** | scanProject, generateReport, analyzeUsage, CLI: `npx dep-exray .` |
@@ -118,7 +122,7 @@ git clone https://github.com/superdevids/superjs.git
118
122
  cd superjs/packages/core
119
123
  npm install
120
124
  npx tsup # Build
121
- npx vitest run # Test (810 tests)
125
+ npx vitest run # Test (828 tests)
122
126
  npx dep-exray . # Scan project sendiri
123
127
  ```
124
128
 
@@ -128,7 +132,7 @@ npx dep-exray . # Scan project sendiri
128
132
 
129
133
  | File Tes | Jumlah |
130
134
  |----------|--------|
131
- | 18 file | **810** passing ✅ |
135
+ | 19 file | **828** passing ✅ |
132
136
 
133
137
  ---
134
138
 
@@ -148,11 +152,11 @@ packages/core/
148
152
  │ ├── crypto/ # hash, generateToken, base64
149
153
  │ ├── path/ # join, resolve, basename
150
154
  │ ├── color/ # hexToRgb, lighten, darken, contrastRatio
151
- │ ├── validation/ # isNIK, isNPWP, isPhone, isEmail, isURL
155
+ │ ├── validation/ # isNIK, parseNIK, isNPWP, isPlatNomor, isPhone, isKodepos, isNoRekening, isEmail, isURL
152
156
  │ ├── error/ # createError, TypedError, MultiError
153
157
  │ ├── logger/ # Logger, transports
154
158
  │ └── dep-exray/ # Dependency scanner
155
- ├── tests/ # 810 tests
159
+ ├── tests/ # 828 tests
156
160
  ├── dist/ # Hasil build
157
161
  └── package.json
158
162
  ```
package/ROADMAP.md CHANGED
@@ -3,7 +3,7 @@
3
3
  ## v0.4.0 — Modul Indonesia & Error Foundation ✅ (Selesai)
4
4
 
5
5
  ### Modul Baru
6
- - ✅ **validation** — `isNIK()`, `isNPWP()`, `isPhone("id")`, `isEmail()`, `isURL()` — zero-dep, khusus Indonesia
6
+ - ✅ **validation** — `isNIK()`, `parseNIK()`, `isNPWP()`, `isPlatNomor()`, `isPhone("id")`, `isKodepos()`, `isNoRekening()`, `isEmail()`, `isURL()` — zero-dep, khusus Indonesia
7
7
  - ✅ **error** — `createError()` factory, `MultiError`, typed error codes
8
8
  - ✅ **logger** — Structured logger zero-dep, child loggers, file transport
9
9
  - ✅ **color** — `hexToRgb()`, `lighten()`, `darken()`, `contrastRatio()`, `meetsWCAG()`
package/SUMMARY.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # superjs-core — Ringkasan Fitur Lengkap
2
2
 
3
- > **Versi:** 0.4.0 | **License:** MIT | **Zero runtime dependencies**
3
+ > **Versi:** 0.4.4 | **License:** MIT | **Zero runtime dependencies**
4
4
 
5
5
  ```
6
6
  npm install superjs-core
@@ -226,8 +226,12 @@ npm install superjs-core
226
226
  | Fungsi | Deskripsi |
227
227
  |--------|-----------|
228
228
  | `isNIK(value)` | **Validasi NIK** 16 digit + tanggal lahir |
229
+ | `parseNIK(value)` | **Extract data dari NIK** — gender, provinsi, tanggal lahir |
229
230
  | `isNPWP(value)` | **Validasi NPWP** + checksum |
231
+ | `isPlatNomor(value)` | **Validasi plat kendaraan** RI (B 1234 CD) |
230
232
  | `isPhone(value, country?)` | **Validasi nomor HP Indonesia** |
233
+ | `isKodepos(value)` | **Validasi kode pos** 5 digit |
234
+ | `isNoRekening(value)` | **Validasi nomor rekening** bank (8-16 digit) |
231
235
  | `isEmail(value)` | Validasi email RFC-compliant |
232
236
  | `isURL(value)` | Validasi URL (http/https) |
233
237
 
@@ -293,7 +297,7 @@ npm install superjs-core
293
297
 
294
298
  | Module | Test Files | Tests |
295
299
  |--------|-----------|-------|
296
- | Semua module | 18 | **810** ✅ |
300
+ | Semua module | 19 | **828** ✅ |
297
301
 
298
302
  ---
299
303
 
@@ -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 };
@@ -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
@@ -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":";AAWO,IAAM,QAAN,MAAyB;AAAA,EACtB,SAKH,CAAC;AAAA,EACE,WAAW;AAAA,EACX,UAAU;AAAA,EACV,eAAoC;AAAA,EACpC;AAAA,EAER,YAAY,SAAwB;AAClC,SAAK,kBAAkB,SAAS,eAAe;AAC/C,QAAI,KAAK,kBAAkB,EAAG,MAAK,kBAAkB;AAAA,EACvD;AAAA,EAEA,IAAI,UAAkB;AACpB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,IAAI,UAAkB;AACpB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAO,MAAwB,SAA6C;AAC1E,WAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACzC,WAAK,OAAO,KAAK;AAAA,QACf,UAAU,SAAS,YAAY;AAAA,QAC/B;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AACD,WAAK,OAAO,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;AAClD,WAAK,SAAS;AAAA,IAChB,CAAC;AAAA,EACH;AAAA,EAEA,QAAc;AACZ,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,SAAe;AACb,SAAK,UAAU;AACf,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,QAAc;AACZ,UAAM,MAAM,IAAI,MAAM,eAAe;AACrC,eAAW,KAAK,KAAK,OAAQ,GAAE,OAAO,GAAG;AACzC,SAAK,SAAS,CAAC;AAAA,EACjB;AAAA,EAEA,SAAwB;AACtB,QAAI,KAAK,aAAa,KAAK,KAAK,OAAO,WAAW,EAAG,QAAO,QAAQ,QAAQ;AAC5E,WAAO,IAAI,QAAc,CAAC,YAAY;AACpC,WAAK,eAAe;AAAA,IACtB,CAAC;AAAA,EACH;AAAA,EAEQ,WAAiB;AACvB,QAAI,KAAK,QAAS;AAClB,WAAO,KAAK,WAAW,KAAK,mBAAmB,KAAK,OAAO,SAAS,GAAG;AACrE,YAAM,OAAO,KAAK,OAAO,MAAM;AAC/B,WAAK;AACL,WACG,KAAK,EACL,KAAK,CAAC,WAAW,KAAK,QAAQ,MAAM,CAAC,EACrC,MAAM,CAAC,QAAQ,KAAK,OAAO,GAAG,CAAC,EAC/B,QAAQ,MAAM;AACb,aAAK;AACL,aAAK,SAAS;AACd,YAAI,KAAK,aAAa,KAAK,KAAK,OAAO,WAAW,KAAK,KAAK,cAAc;AACxE,eAAK,aAAa;AAClB,eAAK,eAAe;AAAA,QACtB;AAAA,MACF,CAAC;AAAA,IACL;AAAA,EACF;AACF;;;AChFO,IAAM,YAAN,MAAgB;AAAA,EACb;AAAA,EACA,WAA8B,CAAC;AAAA,EAEvC,YAAY,aAAqB;AAC/B,QAAI,cAAc,EAAG,OAAM,IAAI,WAAW,oCAAoC;AAC9E,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,IAAI,YAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,UAA+B;AAC7B,QAAI,KAAK,aAAa,GAAG;AACvB,WAAK;AACL,aAAO,QAAQ,QAAQ,KAAK,SAAS,KAAK,IAAI,CAAC;AAAA,IACjD;AACA,WAAO,IAAI,QAAoB,CAAC,YAAY;AAC1C,WAAK,SAAS,KAAK,MAAM;AACvB,aAAK;AACL,gBAAQ,KAAK,SAAS,KAAK,IAAI,CAAC;AAAA,MAClC,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,IAAO,IAAkC;AAC7C,UAAM,UAAU,MAAM,KAAK,QAAQ;AACnC,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,UAAE;AACA,cAAQ;AAAA,IACV;AAAA,EACF;AAAA,EAEQ,WAAiB;AACvB,SAAK;AACL,QAAI,KAAK,SAAS,SAAS,GAAG;AAC5B,YAAM,OAAO,KAAK,SAAS,MAAM;AACjC,UAAI,KAAM,MAAK;AAAA,IACjB;AAAA,EACF;AACF;;;ACrBO,SAAS,aACd,IACA,SAIA;AACA,QAAM;AAAA,IACJ,MAAM;AAAA,IACN,uBAAuB;AAAA,IACvB,UAAU;AAAA,IACV,WAAW,IAAI,SAAoB,KAAK,UAAU,IAAI;AAAA,EACxD,IAAI,WAAW,CAAC;AAEhB,QAAM,QAAQ,oBAAI,IAAwB;AAE1C,QAAM,YAAY,UAAU,SAAsC;AAChE,UAAM,MAAM,SAAS,GAAG,IAAI;AAC5B,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,QAAQ,MAAM,IAAI,GAAG;AAE3B,QAAI,SAAS,MAAM,MAAM,YAAY,KAAK;AACxC,aAAO,MAAM;AAAA,IACf;AAEA,QAAI,SAAS,wBAAwB,MAAM,MAAM,aAAa,KAAK;AACjE,UAAI,MAAM,SAAS;AACjB,eAAO,MAAM;AAAA,MACf;AACA,YAAM,UAAU,GAAG,GAAG,IAAI,EACvB,KAAK,CAACA,YAAW;AAChB,cAAM,QAAQA;AACd,cAAM,YAAY;AAClB,cAAM,UAAU;AAChB,eAAOA;AAAA,MACT,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,cAAM,UAAU;AAChB,cAAM;AAAA,MACR,CAAC;AACH,aAAO,MAAM;AAAA,IACf;AAEA,UAAM,SAAS,MAAM,GAAG,GAAG,IAAI;AAC/B,UAAM,IAAI,KAAK,EAAE,OAAO,QAAQ,WAAW,IAAI,CAAC;AAEhD,QAAI,MAAM,OAAO,SAAS;AACxB,YAAM,WAAW,MAAM,KAAK,EAAE,KAAK,EAAE;AACrC,UAAI,aAAa,OAAW,OAAM,OAAO,QAAQ;AAAA,IACnD;AAEA,WAAO;AAAA,EACT;AAKA,WAAS,QAAQ;AACjB,WAAS,QAAQ,MAAM,MAAM,MAAM;AAEnC,SAAO;AACT;;;ACvFO,SAAS,MAAM,IAA2B;AAC/C,SAAO,IAAI,QAAQ,aAAW,WAAW,SAAS,EAAE,CAAC;AACvD;AAKA,eAAsB,QAAW,SAAqB,IAAY,cAAmC;AACnG,MAAI;AACJ,QAAM,iBAAiB,IAAI,QAAe,CAAC,GAAG,WAAW;AACvD,YAAQ,WAAW,MAAM,OAAO,IAAI,MAAM,gBAAgB,2BAA2B,EAAE,IAAI,CAAC,GAAG,EAAE;AAAA,EACnG,CAAC;AACD,MAAI;AACF,WAAO,MAAM,QAAQ,KAAK,CAAC,SAAS,cAAc,CAAC;AAAA,EACrD,UAAE;AACA,QAAI,UAAU,OAAW,cAAa,KAAK;AAAA,EAC7C;AACF;AAKA,eAAsB,gBAAmB,SAAqB,IAAoC;AAChG,MAAI;AACJ,QAAM,iBAAiB,IAAI,QAAmB,aAAW;AACvD,YAAQ,WAAW,MAAM,QAAQ,SAAS,GAAG,EAAE;AAAA,EACjD,CAAC;AACD,MAAI;AACF,WAAO,MAAM,QAAQ,KAAK,CAAC,SAAS,cAAc,CAAC;AAAA,EACrD,UAAE;AACA,QAAI,UAAU,OAAW,cAAa,KAAK;AAAA,EAC7C;AACF;AAMA,eAAsB,cACpB,OACA,IACoC;AACpC,SAAO,MAAM,QAAQ,WAAW,MAAM,IAAI,EAAE,CAAC;AAC/C;AAOA,eAAsB,YACpB,OACA,IACA,cAAc,OAAO,mBACP;AACd,MAAI,gBAAgB,OAAO,mBAAmB;AAC5C,WAAO,MAAM,QAAQ,IAAI,MAAM,IAAI,EAAE,CAAC;AAAA,EACxC;AAEA,QAAM,UAAe,CAAC;AACtB,QAAM,QAAQ,CAAC,GAAG,KAAK;AAEvB,QAAM,SAAS,YAA2B;AACxC,WAAO,MAAM,SAAS,GAAG;AACvB,YAAM,OAAO,MAAM,MAAM;AACzB,cAAQ,KAAK,MAAM,GAAG,IAAI,CAAC;AAAA,IAC7B;AAAA,EACF;AAEA,QAAM,cAAc,KAAK,IAAI,aAAa,MAAM,MAAM;AACtD,QAAM,UAAU,MAAM,KAAK,EAAE,QAAQ,YAAY,GAAG,MAAM,OAAO,CAAC;AAClE,QAAM,QAAQ,IAAI,OAAO;AACzB,SAAO;AACT;AAKA,eAAsB,WAAc,IAAsB,SAAoC;AAC5F,QAAM,EAAE,WAAW,GAAG,YAAY,KAAM,WAAW,KAAO,cAAc,MAAM,KAAK,IAAI,WAAW,CAAC;AACnG,MAAI;AACJ,WAAS,UAAU,GAAG,UAAU,UAAU,WAAW;AACnD,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,SAAS,OAAO;AACd,kBAAY;AACZ,UAAI,CAAC,YAAY,KAAK,KAAK,WAAW,WAAW,EAAG;AACpD,YAAM,QAAQ,KAAK,IAAI,YAAY,KAAK,UAAU,KAAK,OAAO,IAAI,WAAW,QAAQ;AACrF,YAAM,MAAM,KAAK;AAAA,IACnB;AAAA,EACF;AACA,QAAM;AACR;AAKA,eAAsB,SAAY,YAAe,KAAgD;AAC/F,MAAI,SAAS;AACb,aAAW,MAAM,KAAK;AACpB,aAAS,MAAM,GAAG,MAAM;AAAA,EAC1B;AACA,SAAO;AACT;AAKO,SAAS,WAA2B;AACzC,MAAI;AACJ,MAAI;AACJ,QAAM,UAAU,IAAI,QAAW,CAAC,KAAK,QAAQ;AAC3C,cAAU;AACV,aAAS;AAAA,EACX,CAAC;AACD,SAAO,EAAE,SAAS,SAAS,OAAO;AACpC;","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":";AAWO,IAAM,QAAN,MAAyB;AAAA,EACtB,SAKH,CAAC;AAAA,EACE,WAAW;AAAA,EACX,UAAU;AAAA,EACV,eAAoC;AAAA,EACpC;AAAA,EAER,YAAY,SAAwB;AAClC,SAAK,kBAAkB,SAAS,eAAe;AAC/C,QAAI,KAAK,kBAAkB,EAAG,MAAK,kBAAkB;AAAA,EACvD;AAAA,EAEA,IAAI,UAAkB;AACpB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,IAAI,UAAkB;AACpB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAO,MAAwB,SAA6C;AAC1E,WAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACzC,WAAK,OAAO,KAAK;AAAA,QACf,UAAU,SAAS,YAAY;AAAA,QAC/B;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AACD,WAAK,OAAO,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;AAClD,WAAK,SAAS;AAAA,IAChB,CAAC;AAAA,EACH;AAAA,EAEA,QAAc;AACZ,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,SAAe;AACb,SAAK,UAAU;AACf,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,QAAc;AACZ,UAAM,MAAM,IAAI,MAAM,eAAe;AACrC,eAAW,KAAK,KAAK,OAAQ,GAAE,OAAO,GAAG;AACzC,SAAK,SAAS,CAAC;AAAA,EACjB;AAAA,EAEA,SAAwB;AACtB,QAAI,KAAK,aAAa,KAAK,KAAK,OAAO,WAAW,EAAG,QAAO,QAAQ,QAAQ;AAC5E,WAAO,IAAI,QAAc,CAAC,YAAY;AACpC,WAAK,eAAe;AAAA,IACtB,CAAC;AAAA,EACH;AAAA,EAEQ,WAAiB;AACvB,QAAI,KAAK,QAAS;AAClB,WAAO,KAAK,WAAW,KAAK,mBAAmB,KAAK,OAAO,SAAS,GAAG;AACrE,YAAM,OAAO,KAAK,OAAO,MAAM;AAC/B,WAAK;AACL,WACG,KAAK,EACL,KAAK,CAAC,WAAW,KAAK,QAAQ,MAAM,CAAC,EACrC,MAAM,CAAC,QAAQ,KAAK,OAAO,GAAG,CAAC,EAC/B,QAAQ,MAAM;AACb,aAAK;AACL,aAAK,SAAS;AACd,YAAI,KAAK,aAAa,KAAK,KAAK,OAAO,WAAW,KAAK,KAAK,cAAc;AACxE,eAAK,aAAa;AAClB,eAAK,eAAe;AAAA,QACtB;AAAA,MACF,CAAC;AAAA,IACL;AAAA,EACF;AACF;;;AChFO,IAAM,YAAN,MAAgB;AAAA,EACb;AAAA,EACA,WAA8B,CAAC;AAAA,EAEvC,YAAY,aAAqB;AAC/B,QAAI,cAAc,EAAG,OAAM,IAAI,WAAW,oCAAoC;AAC9E,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,IAAI,YAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,UAA+B;AAC7B,QAAI,KAAK,aAAa,GAAG;AACvB,WAAK;AACL,aAAO,QAAQ,QAAQ,KAAK,SAAS,KAAK,IAAI,CAAC;AAAA,IACjD;AACA,WAAO,IAAI,QAAoB,CAAC,YAAY;AAC1C,WAAK,SAAS,KAAK,MAAM;AACvB,aAAK;AACL,gBAAQ,KAAK,SAAS,KAAK,IAAI,CAAC;AAAA,MAClC,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,IAAO,IAAkC;AAC7C,UAAM,UAAU,MAAM,KAAK,QAAQ;AACnC,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,UAAE;AACA,cAAQ;AAAA,IACV;AAAA,EACF;AAAA,EAEQ,WAAiB;AACvB,SAAK;AACL,QAAI,KAAK,SAAS,SAAS,GAAG;AAC5B,YAAM,OAAO,KAAK,SAAS,MAAM;AACjC,UAAI,KAAM,MAAK;AAAA,IACjB;AAAA,EACF;AACF;;;ACrBO,SAAS,aACd,IACA,SAIA;AACA,QAAM;AAAA,IACJ,MAAM;AAAA,IACN,uBAAuB;AAAA,IACvB,UAAU;AAAA,IACV,WAAW,IAAI,SAAoB,KAAK,UAAU,IAAI;AAAA,EACxD,IAAI,WAAW,CAAC;AAEhB,QAAM,QAAQ,oBAAI,IAAwB;AAE1C,QAAM,YAAY,UAAU,SAAsC;AAChE,UAAM,MAAM,SAAS,GAAG,IAAI;AAC5B,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,QAAQ,MAAM,IAAI,GAAG;AAE3B,QAAI,SAAS,MAAM,MAAM,YAAY,KAAK;AACxC,aAAO,MAAM;AAAA,IACf;AAEA,QAAI,SAAS,wBAAwB,MAAM,MAAM,aAAa,KAAK;AACjE,UAAI,MAAM,SAAS;AACjB,eAAO,MAAM;AAAA,MACf;AACA,YAAM,UAAU,GAAG,GAAG,IAAI,EACvB,KAAK,CAACA,YAAW;AAChB,cAAM,QAAQA;AACd,cAAM,YAAY;AAClB,cAAM,UAAU;AAChB,eAAOA;AAAA,MACT,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,cAAM,UAAU;AAChB,cAAM;AAAA,MACR,CAAC;AACH,aAAO,MAAM;AAAA,IACf;AAEA,UAAM,SAAS,MAAM,GAAG,GAAG,IAAI;AAC/B,UAAM,IAAI,KAAK,EAAE,OAAO,QAAQ,WAAW,IAAI,CAAC;AAEhD,QAAI,MAAM,OAAO,SAAS;AACxB,YAAM,WAAW,MAAM,KAAK,EAAE,KAAK,EAAE;AACrC,UAAI,aAAa,OAAW,OAAM,OAAO,QAAQ;AAAA,IACnD;AAEA,WAAO;AAAA,EACT;AAKA,WAAS,QAAQ;AACjB,WAAS,QAAQ,MAAM,MAAM,MAAM;AAEnC,SAAO;AACT;;;AC7EO,IAAM,cAAN,MAAkB;AAAA,EACf;AAAA,EACA;AAAA,EACA,cAAwB,CAAC;AAAA,EAEjC,YAAY,SAAqD;AAC/D,QAAI,QAAQ,cAAc,EAAG,OAAM,IAAI,WAAW,0BAA0B;AAC5E,QAAI,QAAQ,YAAY,EAAG,OAAM,IAAI,WAAW,wBAAwB;AACxE,SAAK,eAAe,QAAQ;AAC5B,SAAK,aAAa,QAAQ;AAAA,EAC5B;AAAA,EAEQ,SAAe;AACrB,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,MAAM,KAAK;AAC1B,WAAO,KAAK,YAAY,SAAS,GAAG;AAClC,YAAM,KAAK,KAAK,YAAY,CAAC;AAC7B,UAAI,OAAO,UAAa,KAAK,OAAQ;AACrC,WAAK,YAAY,MAAM;AAAA,IACzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAsB;AACpB,SAAK,OAAO;AACZ,QAAI,KAAK,YAAY,SAAS,KAAK,cAAc;AAC/C,WAAK,YAAY,KAAK,KAAK,IAAI,CAAC;AAChC,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAyB;AAC7B,WAAO,MAAM;AACX,WAAK,OAAO;AACZ,UAAI,KAAK,YAAY,SAAS,KAAK,cAAc;AAC/C,aAAK,YAAY,KAAK,KAAK,IAAI,CAAC;AAChC;AAAA,MACF;AACA,YAAM,SAAS,KAAK,YAAY,CAAC,KAAK,KAAK,IAAI;AAC/C,YAAM,SAAS,SAAS,KAAK,aAAa,KAAK,IAAI;AACnD,UAAI,SAAS,GAAG;AACd,cAAM,IAAI,QAAc,aAAW,WAAW,SAAS,MAAM,CAAC;AAAA,MAChE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,UAAkB;AACpB,SAAK,OAAO;AACZ,WAAO,KAAK,YAAY;AAAA,EAC1B;AACF;;;ACxDO,IAAM,QAAN,MAAY;AAAA,EACT,UAAU;AAAA,EACV,WAA8B,CAAC;AAAA;AAAA;AAAA;AAAA,EAKvC,UAA+B;AAC7B,QAAI,CAAC,KAAK,SAAS;AACjB,WAAK,UAAU;AACf,aAAO,QAAQ,QAAQ,KAAK,SAAS,KAAK,IAAI,CAAC;AAAA,IACjD;AACA,WAAO,IAAI,QAAoB,aAAW;AACxC,WAAK,SAAS,KAAK,MAAM;AACvB,gBAAQ,KAAK,SAAS,KAAK,IAAI,CAAC;AAAA,MAClC,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAO,IAAkC;AAC7C,UAAM,UAAU,MAAM,KAAK,QAAQ;AACnC,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,UAAE;AACA,cAAQ;AAAA,IACV;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,SAAkB;AACpB,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,WAAiB;AACvB,QAAI,KAAK,SAAS,SAAS,GAAG;AAC5B,YAAM,OAAO,KAAK,SAAS,MAAM;AACjC,UAAI,KAAM,MAAK;AAAA,IACjB,OAAO;AACL,WAAK,UAAU;AAAA,IACjB;AAAA,EACF;AACF;;;ACtDA,eAAsB,MACpB,OACA,WACA,IACc;AACd,MAAI,YAAY,EAAG,OAAM,IAAI,WAAW,wBAAwB;AAEhE,QAAM,UAAe,CAAC;AACtB,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,WAAW;AAChD,UAAM,QAAQ,MAAM,MAAM,GAAG,IAAI,SAAS;AAC1C,UAAM,eAAe,MAAM,GAAG,KAAK;AACnC,YAAQ,KAAK,GAAG,YAAY;AAAA,EAC9B;AACA,SAAO;AACT;;;ACPA,eAAsB,UACpB,OACA,SACc;AACd,MAAI,SAAS;AACb,aAAW,QAAQ,OAAO;AACxB,aAAS,MAAM,KAAK,MAAM;AAAA,EAC5B;AACA,SAAO;AACT;;;ACrBO,SAAS,MAAM,IAA2B;AAC/C,SAAO,IAAI,QAAQ,aAAW,WAAW,SAAS,EAAE,CAAC;AACvD;AAKA,eAAsB,QAAW,SAAqB,IAAY,cAAmC;AACnG,MAAI;AACJ,QAAM,iBAAiB,IAAI,QAAe,CAAC,GAAG,WAAW;AACvD,YAAQ,WAAW,MAAM,OAAO,IAAI,MAAM,gBAAgB,2BAA2B,EAAE,IAAI,CAAC,GAAG,EAAE;AAAA,EACnG,CAAC;AACD,MAAI;AACF,WAAO,MAAM,QAAQ,KAAK,CAAC,SAAS,cAAc,CAAC;AAAA,EACrD,UAAE;AACA,QAAI,UAAU,OAAW,cAAa,KAAK;AAAA,EAC7C;AACF;AAKA,eAAsB,gBAAmB,SAAqB,IAAoC;AAChG,MAAI;AACJ,QAAM,iBAAiB,IAAI,QAAmB,aAAW;AACvD,YAAQ,WAAW,MAAM,QAAQ,SAAS,GAAG,EAAE;AAAA,EACjD,CAAC;AACD,MAAI;AACF,WAAO,MAAM,QAAQ,KAAK,CAAC,SAAS,cAAc,CAAC;AAAA,EACrD,UAAE;AACA,QAAI,UAAU,OAAW,cAAa,KAAK;AAAA,EAC7C;AACF;AAMA,eAAsB,cACpB,OACA,IACoC;AACpC,SAAO,MAAM,QAAQ,WAAW,MAAM,IAAI,EAAE,CAAC;AAC/C;AAOA,eAAsB,YACpB,OACA,IACA,cAAc,OAAO,mBACP;AACd,MAAI,gBAAgB,OAAO,mBAAmB;AAC5C,WAAO,MAAM,QAAQ,IAAI,MAAM,IAAI,EAAE,CAAC;AAAA,EACxC;AAEA,QAAM,UAAe,CAAC;AACtB,QAAM,QAAQ,CAAC,GAAG,KAAK;AAEvB,QAAM,SAAS,YAA2B;AACxC,WAAO,MAAM,SAAS,GAAG;AACvB,YAAM,OAAO,MAAM,MAAM;AACzB,cAAQ,KAAK,MAAM,GAAG,IAAI,CAAC;AAAA,IAC7B;AAAA,EACF;AAEA,QAAM,cAAc,KAAK,IAAI,aAAa,MAAM,MAAM;AACtD,QAAM,UAAU,MAAM,KAAK,EAAE,QAAQ,YAAY,GAAG,MAAM,OAAO,CAAC;AAClE,QAAM,QAAQ,IAAI,OAAO;AACzB,SAAO;AACT;AAKA,eAAsB,WAAc,IAAsB,SAAoC;AAC5F,QAAM,EAAE,WAAW,GAAG,YAAY,KAAM,WAAW,KAAO,cAAc,MAAM,KAAK,IAAI,WAAW,CAAC;AACnG,MAAI;AACJ,WAAS,UAAU,GAAG,UAAU,UAAU,WAAW;AACnD,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,SAAS,OAAO;AACd,kBAAY;AACZ,UAAI,CAAC,YAAY,KAAK,KAAK,WAAW,WAAW,EAAG;AACpD,YAAM,QAAQ,KAAK,IAAI,YAAY,KAAK,UAAU,KAAK,OAAO,IAAI,WAAW,QAAQ;AACrF,YAAM,MAAM,KAAK;AAAA,IACnB;AAAA,EACF;AACA,QAAM;AACR;AAKA,eAAsB,SAAY,YAAe,KAAgD;AAC/F,MAAI,SAAS;AACb,aAAW,MAAM,KAAK;AACpB,aAAS,MAAM,GAAG,MAAM;AAAA,EAC1B;AACA,SAAO;AACT;AAKO,SAAS,WAA2B;AACzC,MAAI;AACJ,MAAI;AACJ,QAAM,UAAU,IAAI,QAAW,CAAC,KAAK,QAAQ;AAC3C,cAAU;AACV,aAAS;AAAA,EACX,CAAC;AACD,SAAO,EAAE,SAAS,SAAS,OAAO;AACpC;","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 };