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 +26 -0
- package/CONTRIBUTING.md +1 -1
- package/PUBLISH.md +2 -2
- package/README.md +9 -5
- package/ROADMAP.md +1 -1
- package/SUMMARY.md +6 -2
- 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 +1 -1
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` (
|
|
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 (
|
|
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` —
|
|
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
|
|
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 (
|
|
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
|
-
|
|
|
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/ #
|
|
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.
|
|
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 |
|
|
300
|
+
| Semua module | 19 | **828** ✅ |
|
|
297
301
|
|
|
298
302
|
---
|
|
299
303
|
|
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 };
|