@vielzeug/toolkit 1.0.14 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +546 -0
- package/dist/async/attempt.cjs.map +1 -0
- package/dist/async/attempt.js.map +1 -0
- package/dist/async/defer.cjs +2 -0
- package/dist/async/defer.cjs.map +1 -0
- package/dist/async/defer.js +10 -0
- package/dist/async/defer.js.map +1 -0
- package/dist/async/delay.cjs.map +1 -0
- package/dist/async/delay.js.map +1 -0
- package/dist/async/parallel.cjs.map +1 -0
- package/dist/async/parallel.js.map +1 -0
- package/dist/async/pool.cjs +2 -0
- package/dist/async/pool.cjs.map +1 -0
- package/dist/async/pool.js +22 -0
- package/dist/async/pool.js.map +1 -0
- package/dist/async/predict.cjs.map +1 -0
- package/dist/async/predict.js.map +1 -0
- package/dist/async/queue.cjs +2 -0
- package/dist/async/queue.cjs.map +1 -0
- package/dist/async/queue.js +57 -0
- package/dist/async/queue.js.map +1 -0
- package/dist/async/race.cjs +2 -0
- package/dist/async/race.cjs.map +1 -0
- package/dist/async/race.js +8 -0
- package/dist/async/race.js.map +1 -0
- package/dist/async/retry.cjs.map +1 -0
- package/dist/{function → async}/retry.js +4 -4
- package/dist/async/retry.js.map +1 -0
- package/dist/async/sleep.cjs +2 -0
- package/dist/async/sleep.cjs.map +1 -0
- package/dist/{function → async}/sleep.js +1 -1
- package/dist/async/sleep.js.map +1 -0
- package/dist/async/waitFor.cjs +2 -0
- package/dist/async/waitFor.cjs.map +1 -0
- package/dist/async/waitFor.js +37 -0
- package/dist/async/waitFor.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +175 -0
- package/dist/index.js +242 -230
- package/dist/index.js.map +1 -1
- package/dist/logit/dist/logit.cjs +1 -1
- package/dist/logit/dist/logit.cjs.map +1 -1
- package/dist/logit/dist/logit.js +147 -64
- package/dist/logit/dist/logit.js.map +1 -1
- package/dist/object/cache.cjs +2 -0
- package/dist/object/cache.cjs.map +1 -0
- package/dist/object/cache.js +63 -0
- package/dist/object/cache.js.map +1 -0
- package/package.json +1 -1
- package/dist/function/attempt.cjs.map +0 -1
- package/dist/function/attempt.js.map +0 -1
- package/dist/function/delay.cjs.map +0 -1
- package/dist/function/delay.js.map +0 -1
- package/dist/function/parallel.cjs.map +0 -1
- package/dist/function/parallel.js.map +0 -1
- package/dist/function/predict.cjs.map +0 -1
- package/dist/function/predict.js.map +0 -1
- package/dist/function/retry.cjs.map +0 -1
- package/dist/function/retry.js.map +0 -1
- package/dist/function/sleep.cjs +0 -2
- package/dist/function/sleep.cjs.map +0 -1
- package/dist/function/sleep.js.map +0 -1
- /package/dist/{function → async}/attempt.cjs +0 -0
- /package/dist/{function → async}/attempt.js +0 -0
- /package/dist/{function → async}/delay.cjs +0 -0
- /package/dist/{function → async}/delay.js +0 -0
- /package/dist/{function → async}/parallel.cjs +0 -0
- /package/dist/{function → async}/parallel.js +0 -0
- /package/dist/{function → async}/predict.cjs +0 -0
- /package/dist/{function → async}/predict.js +0 -0
- /package/dist/{function → async}/retry.cjs +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
# @vielzeug/toolkit
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
<img src="../../docs/public/logo-utils.svg" alt="Toolkit Logo" width="120" />
|
|
5
|
+
|
|
6
|
+
<p><strong>A comprehensive, type-safe utility library for modern JavaScript and TypeScript.</strong></p>
|
|
7
|
+
|
|
8
|
+
[](https://www.npmjs.com/package/@vielzeug/toolkit)
|
|
9
|
+
[](https://www.npmjs.com/package/@vielzeug/toolkit)
|
|
10
|
+
[](https://opensource.org/licenses/MIT)
|
|
11
|
+
[](https://www.typescriptlang.org/)
|
|
12
|
+
[](https://bundlephobia.com/package/@vielzeug/toolkit)
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- 🎯 **119 Type-Safe Utilities** - Covering arrays, objects, strings, async operations, and more
|
|
19
|
+
- 📦 **Tree-Shakeable** - Import only what you need, minimize bundle size
|
|
20
|
+
- 🔒 **Zero Dependencies** - No supply chain risks, no version conflicts
|
|
21
|
+
- 💪 **Full TypeScript Support** - Complete type inference and safety
|
|
22
|
+
- ⚡ **Async-First** - Built-in support for promises and async operations
|
|
23
|
+
- 🧪 **Battle-Tested** - >95% test coverage, production-ready
|
|
24
|
+
- 🌐 **Isomorphic** - Works in both browser and Node.js environments
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
::: code-group
|
|
29
|
+
|
|
30
|
+
```bash [pnpm]
|
|
31
|
+
pnpm add @vielzeug/toolkit
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```bash [npm]
|
|
35
|
+
npm install @vielzeug/toolkit
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```bash [yarn]
|
|
39
|
+
yarn add @vielzeug/toolkit
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
:::
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { chunk, map, retry, pool } from '@vielzeug/toolkit';
|
|
48
|
+
|
|
49
|
+
// Array operations
|
|
50
|
+
const batches = chunk([1, 2, 3, 4, 5], 2);
|
|
51
|
+
// [[1, 2], [3, 4], [5]]
|
|
52
|
+
|
|
53
|
+
// Async map with automatic Promise handling
|
|
54
|
+
const users = await map([1, 2, 3], async (id) => fetchUser(id));
|
|
55
|
+
|
|
56
|
+
// Retry with exponential backoff
|
|
57
|
+
const data = await retry(() => fetch('/api/data').then((r) => r.json()), { times: 3, delay: 1000, backoff: 2 });
|
|
58
|
+
|
|
59
|
+
// Rate limiting with promise pool
|
|
60
|
+
const apiPool = pool(5); // Max 5 concurrent requests
|
|
61
|
+
const results = await Promise.all(urls.map((url) => apiPool(() => fetch(url))));
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Categories
|
|
65
|
+
|
|
66
|
+
### 📊 Array (25 utilities)
|
|
67
|
+
|
|
68
|
+
Transform, filter, group, and manipulate arrays with full type safety.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { group, uniq, flatten, sort } from '@vielzeug/toolkit';
|
|
72
|
+
|
|
73
|
+
// Group by property
|
|
74
|
+
const grouped = group(users, (user) => user.role);
|
|
75
|
+
|
|
76
|
+
// Remove duplicates
|
|
77
|
+
const unique = uniq([1, 2, 2, 3, 3, 3]);
|
|
78
|
+
|
|
79
|
+
// Flatten nested arrays
|
|
80
|
+
const flat = flatten([
|
|
81
|
+
[1, 2],
|
|
82
|
+
[3, [4, 5]],
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
// Sort with custom comparator
|
|
86
|
+
const sorted = sort(items, (a, b) => a.price - b.price);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Available utilities:**
|
|
90
|
+
`aggregate`, `alternate`, `arrange`, `chunk`, `compact`, `contains`, `every`, `filter`, `find`, `findIndex`, `findLast`, `flatten`, `group`, `list`, `map`, `pick`, `reduce`, `remoteList`, `search`, `select`, `shift`, `some`, `sort`, `substitute`, `uniq`
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
### ⚡ Async (11 utilities)
|
|
95
|
+
|
|
96
|
+
Promise utilities, concurrency control, retries, and async patterns.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { parallel, queue, waitFor, race, defer } from '@vielzeug/toolkit';
|
|
100
|
+
|
|
101
|
+
// Process with controlled concurrency
|
|
102
|
+
await parallel(3, urls, async (url) => fetch(url));
|
|
103
|
+
|
|
104
|
+
// Task queue with monitoring
|
|
105
|
+
const taskQueue = queue({ concurrency: 5 });
|
|
106
|
+
taskQueue.add(() => processTask());
|
|
107
|
+
await taskQueue.onIdle();
|
|
108
|
+
|
|
109
|
+
// Wait for condition
|
|
110
|
+
await waitFor(() => document.querySelector('#app') !== null);
|
|
111
|
+
|
|
112
|
+
// Race with minimum delay (better UX)
|
|
113
|
+
const data = await race(fetchData(), 500);
|
|
114
|
+
|
|
115
|
+
// Externally-controlled promise
|
|
116
|
+
const deferred = defer<string>();
|
|
117
|
+
setTimeout(() => deferred.resolve('Done!'), 1000);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Available utilities:**
|
|
121
|
+
`attempt`, `defer`, `delay`, `parallel`, `pool`, `predict`, `queue`, `race`, `retry`, `sleep`, `waitFor`
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
### 📅 Date (3 utilities)
|
|
126
|
+
|
|
127
|
+
Date manipulation and time calculations.
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { interval, timeDiff, expires } from '@vielzeug/toolkit';
|
|
131
|
+
|
|
132
|
+
// Calculate time intervals
|
|
133
|
+
const days = interval(new Date('2024-01-01'), new Date('2024-12-31'), 'days');
|
|
134
|
+
|
|
135
|
+
// Time difference
|
|
136
|
+
const diff = timeDiff(date1, date2);
|
|
137
|
+
|
|
138
|
+
// Check expiration
|
|
139
|
+
if (expires(expiryDate)) {
|
|
140
|
+
// Handle expired
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Available utilities:**
|
|
145
|
+
`expires`, `interval`, `timeDiff`
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
### ⚙️ Function (14 utilities)
|
|
150
|
+
|
|
151
|
+
Function composition, memoization, and execution control.
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { debounce, throttle, memo, pipe, curry } from '@vielzeug/toolkit';
|
|
155
|
+
|
|
156
|
+
// Debounce user input
|
|
157
|
+
const search = debounce((query) => fetchResults(query), 300);
|
|
158
|
+
|
|
159
|
+
// Throttle scroll events
|
|
160
|
+
const onScroll = throttle(() => updateUI(), 100);
|
|
161
|
+
|
|
162
|
+
// Memoize expensive calculations
|
|
163
|
+
const fibonacci = memo((n) => (n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2)));
|
|
164
|
+
|
|
165
|
+
// Function composition
|
|
166
|
+
const transform = pipe(
|
|
167
|
+
(x) => x * 2,
|
|
168
|
+
(x) => x + 1,
|
|
169
|
+
(x) => x.toString(),
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// Currying
|
|
173
|
+
const add = curry((a, b, c) => a + b + c);
|
|
174
|
+
const add5 = add(5);
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Available utilities:**
|
|
178
|
+
`assert`, `assertParams`, `compare`, `compareBy`, `compose`, `curry`, `debounce`, `fp`, `memo`, `once`, `pipe`, `proxy`, `prune`, `throttle`, `worker`
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
### 🔢 Math (17 utilities)
|
|
183
|
+
|
|
184
|
+
Mathematical operations, statistics, and calculations.
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import { average, median, clamp, range, distribute } from '@vielzeug/toolkit';
|
|
188
|
+
|
|
189
|
+
// Calculate average
|
|
190
|
+
const avg = average([1, 2, 3, 4, 5]); // 3
|
|
191
|
+
|
|
192
|
+
// Find median
|
|
193
|
+
const med = median([1, 2, 3, 4, 5]); // 3
|
|
194
|
+
|
|
195
|
+
// Clamp value to range
|
|
196
|
+
const clamped = clamp(150, 0, 100); // 100
|
|
197
|
+
|
|
198
|
+
// Generate range
|
|
199
|
+
const numbers = range(1, 10); // [1, 2, 3, ..., 10]
|
|
200
|
+
|
|
201
|
+
// Distribute amount
|
|
202
|
+
const shares = distribute(100, 3); // [33.33, 33.33, 33.34]
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Available utilities:**
|
|
206
|
+
`abs`, `add`, `allocate`, `average`, `boil`, `clamp`, `distribute`, `divide`, `max`, `median`, `min`, `multiply`, `range`, `rate`, `round`, `subtract`, `sum`
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
### 💰 Money (3 utilities)
|
|
211
|
+
|
|
212
|
+
Currency formatting and exchange calculations.
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import { currency, exchange } from '@vielzeug/toolkit';
|
|
216
|
+
|
|
217
|
+
// Format currency
|
|
218
|
+
const formatted = currency(1234.56, 'USD'); // "$1,234.56"
|
|
219
|
+
|
|
220
|
+
// Currency exchange
|
|
221
|
+
const converted = exchange(100, 'USD', 'EUR', 0.85); // 85
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Available utilities:**
|
|
225
|
+
`currency`, `exchange`, `types`
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
### 📦 Object (10 utilities)
|
|
230
|
+
|
|
231
|
+
Deep merging, cloning, diffing, and object manipulation.
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import { merge, clone, diff, path, seek } from '@vielzeug/toolkit';
|
|
235
|
+
|
|
236
|
+
// Deep merge
|
|
237
|
+
const merged = merge(obj1, obj2);
|
|
238
|
+
|
|
239
|
+
// Deep clone
|
|
240
|
+
const copy = clone(original);
|
|
241
|
+
|
|
242
|
+
// Diff objects
|
|
243
|
+
const changes = diff(oldObj, newObj);
|
|
244
|
+
|
|
245
|
+
// Get nested value
|
|
246
|
+
const value = path(obj, 'user.address.city');
|
|
247
|
+
|
|
248
|
+
// Search object
|
|
249
|
+
const results = seek(data, (val) => val > 100);
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Available utilities:**
|
|
253
|
+
`cache`, `clone`, `diff`, `entries`, `keys`, `merge`, `parseJSON`, `path`, `seek`, `values`
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
### 🎲 Random (4 utilities)
|
|
258
|
+
|
|
259
|
+
Random values, shuffling, and UUID generation.
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
import { random, shuffle, draw, uuid } from '@vielzeug/toolkit';
|
|
263
|
+
|
|
264
|
+
// Random number
|
|
265
|
+
const num = random(1, 100);
|
|
266
|
+
|
|
267
|
+
// Shuffle array
|
|
268
|
+
const shuffled = shuffle([1, 2, 3, 4, 5]);
|
|
269
|
+
|
|
270
|
+
// Draw random items
|
|
271
|
+
const winners = draw(participants, 3);
|
|
272
|
+
|
|
273
|
+
// Generate UUID
|
|
274
|
+
const id = uuid(); // "550e8400-e29b-41d4-a716-446655440000"
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**Available utilities:**
|
|
278
|
+
`draw`, `random`, `shuffle`, `uuid`
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
### 📝 String (6 utilities)
|
|
283
|
+
|
|
284
|
+
String formatting, case conversion, and similarity.
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import { camelCase, kebabCase, pascalCase, snakeCase, truncate, similarity } from '@vielzeug/toolkit';
|
|
288
|
+
|
|
289
|
+
// Case conversions
|
|
290
|
+
camelCase('hello world'); // "helloWorld"
|
|
291
|
+
kebabCase('hello world'); // "hello-world"
|
|
292
|
+
pascalCase('hello world'); // "HelloWorld"
|
|
293
|
+
snakeCase('hello world'); // "hello_world"
|
|
294
|
+
|
|
295
|
+
// Truncate text
|
|
296
|
+
truncate('Long text here', 10); // "Long text..."
|
|
297
|
+
|
|
298
|
+
// String similarity
|
|
299
|
+
similarity('hello', 'hallo'); // 0.8
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
**Available utilities:**
|
|
303
|
+
`camelCase`, `kebabCase`, `pascalCase`, `similarity`, `snakeCase`, `truncate`
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
### ✅ Typed (27 utilities)
|
|
308
|
+
|
|
309
|
+
Type guards, type checking, and validation.
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
import { isString, isArray, isPromise, isEmpty, isEqual } from '@vielzeug/toolkit';
|
|
313
|
+
|
|
314
|
+
// Type guards with narrowing
|
|
315
|
+
if (isString(value)) {
|
|
316
|
+
// TypeScript knows value is string
|
|
317
|
+
console.log(value.toUpperCase());
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Check arrays
|
|
321
|
+
if (isArray(data)) {
|
|
322
|
+
data.forEach((item) => console.log(item));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Promise detection
|
|
326
|
+
if (isPromise(result)) {
|
|
327
|
+
await result;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Empty check
|
|
331
|
+
if (isEmpty(obj)) {
|
|
332
|
+
// Handle empty
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Deep equality
|
|
336
|
+
if (isEqual(obj1, obj2)) {
|
|
337
|
+
// Objects are equal
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Available utilities:**
|
|
342
|
+
`ge`, `gt`, `is`, `isArray`, `isBoolean`, `isDate`, `isDefined`, `isEmpty`, `isEqual`, `isEven`, `isFunction`, `isNegative`, `isNil`, `isNumber`, `isObject`, `isOdd`, `isPositive`, `isPrimitive`, `isPromise`, `isRegex`, `isString`, `isWithin`, `isZero`, `le`, `lt`, `typeOf`
|
|
343
|
+
|
|
344
|
+
## Real-World Examples
|
|
345
|
+
|
|
346
|
+
### API Rate Limiting with Retry
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
import { pool, retry, predict } from '@vielzeug/toolkit';
|
|
350
|
+
|
|
351
|
+
// Create rate-limited pool
|
|
352
|
+
const apiPool = pool(10); // Max 10 concurrent requests
|
|
353
|
+
|
|
354
|
+
async function fetchWithRetry(url: string) {
|
|
355
|
+
return apiPool(() =>
|
|
356
|
+
retry(
|
|
357
|
+
() =>
|
|
358
|
+
predict(
|
|
359
|
+
async (signal) => {
|
|
360
|
+
const response = await fetch(url, { signal });
|
|
361
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
362
|
+
return response.json();
|
|
363
|
+
},
|
|
364
|
+
{ timeout: 5000 },
|
|
365
|
+
),
|
|
366
|
+
{ times: 3, delay: 1000, backoff: 2 },
|
|
367
|
+
),
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Process many URLs with rate limiting and retry
|
|
372
|
+
const results = await Promise.all(urls.map((url) => fetchWithRetry(url)));
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Batch Processing with Progress
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
import { chunk, parallel, sleep } from '@vielzeug/toolkit';
|
|
379
|
+
|
|
380
|
+
async function processBatch(items: any[], batchSize = 10) {
|
|
381
|
+
const batches = chunk(items, batchSize);
|
|
382
|
+
const results = [];
|
|
383
|
+
|
|
384
|
+
for (let i = 0; i < batches.length; i++) {
|
|
385
|
+
console.log(`Processing batch ${i + 1}/${batches.length}`);
|
|
386
|
+
|
|
387
|
+
const batchResults = await parallel(3, batches[i], async (item) => {
|
|
388
|
+
return await processItem(item);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
results.push(...batchResults);
|
|
392
|
+
|
|
393
|
+
// Delay between batches
|
|
394
|
+
if (i < batches.length - 1) {
|
|
395
|
+
await sleep(1000);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return results;
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Form Validation Pipeline
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
import { pipe, curry, isEmpty, isString } from '@vielzeug/toolkit';
|
|
407
|
+
|
|
408
|
+
const required = (value: any) => {
|
|
409
|
+
if (isEmpty(value)) throw new Error('Required field');
|
|
410
|
+
return value;
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
const minLength = curry((min: number, value: string) => {
|
|
414
|
+
if (!isString(value) || value.length < min) {
|
|
415
|
+
throw new Error(`Minimum ${min} characters`);
|
|
416
|
+
}
|
|
417
|
+
return value;
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
const email = (value: string) => {
|
|
421
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
|
|
422
|
+
throw new Error('Invalid email');
|
|
423
|
+
}
|
|
424
|
+
return value;
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
// Create validation pipeline
|
|
428
|
+
const validateEmail = pipe(required, minLength(5), email);
|
|
429
|
+
|
|
430
|
+
try {
|
|
431
|
+
const validEmail = validateEmail(userInput);
|
|
432
|
+
} catch (error) {
|
|
433
|
+
console.error(error.message);
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
## Performance & Bundle Size
|
|
438
|
+
|
|
439
|
+
### Tree-Shaking
|
|
440
|
+
|
|
441
|
+
Toolkit is designed for optimal tree-shaking. Import only what you use:
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
444
|
+
// ✅ Good - Only includes chunk function (~0.5KB gzipped)
|
|
445
|
+
import { chunk } from '@vielzeug/toolkit';
|
|
446
|
+
|
|
447
|
+
// ⚠️ Avoid - Imports entire library (~35KB gzipped)
|
|
448
|
+
import * as toolkit from '@vielzeug/toolkit';
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### Bundle Size by Category
|
|
452
|
+
|
|
453
|
+
| Category | Utilities | Approx. Size (gzipped) |
|
|
454
|
+
| -------- | --------- | ---------------------- |
|
|
455
|
+
| Array | 25 | ~8KB |
|
|
456
|
+
| Async | 11 | ~3KB |
|
|
457
|
+
| Date | 3 | ~1KB |
|
|
458
|
+
| Function | 14 | ~5KB |
|
|
459
|
+
| Math | 17 | ~4KB |
|
|
460
|
+
| Money | 3 | ~1KB |
|
|
461
|
+
| Object | 10 | ~3KB |
|
|
462
|
+
| Random | 4 | ~1KB |
|
|
463
|
+
| String | 6 | ~2KB |
|
|
464
|
+
| Typed | 27 | ~3KB |
|
|
465
|
+
|
|
466
|
+
> **Note**: Individual utilities are typically **0.1-0.8 KB gzipped** each.
|
|
467
|
+
|
|
468
|
+
## TypeScript Support
|
|
469
|
+
|
|
470
|
+
Full TypeScript support with complete type inference:
|
|
471
|
+
|
|
472
|
+
```typescript
|
|
473
|
+
import { map, filter, group } from '@vielzeug/toolkit';
|
|
474
|
+
|
|
475
|
+
const numbers = [1, 2, 3, 4, 5];
|
|
476
|
+
|
|
477
|
+
// Type inferred as number[]
|
|
478
|
+
const doubled = map(numbers, (n) => n * 2);
|
|
479
|
+
|
|
480
|
+
// Type inferred as number[]
|
|
481
|
+
const evens = filter(numbers, (n) => n % 2 === 0);
|
|
482
|
+
|
|
483
|
+
// Type inferred as Record<string, User[]>
|
|
484
|
+
const byRole = group(users, (u) => u.role);
|
|
485
|
+
|
|
486
|
+
// Async operations automatically return Promise
|
|
487
|
+
const results = await map(ids, async (id) => fetchUser(id));
|
|
488
|
+
// Type: Promise<User[]>
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
## Comparison with Alternatives
|
|
492
|
+
|
|
493
|
+
| Feature | Toolkit | Lodash | Ramda | Native JS |
|
|
494
|
+
| ---------------------- | ---------------- | ----------------- | ----------------- | ---------- |
|
|
495
|
+
| TypeScript Support | ✅ First-class | ⚠️ Via @types | ⚠️ Via @types | ❌ Limited |
|
|
496
|
+
| Tree-shakeable | ✅ By default | ⚠️ lodash-es only | ✅ Yes | N/A |
|
|
497
|
+
| Bundle Size (min+gzip) | ~0.1-1KB/utility | ~24KB (full) | ~12KB (full) | 0KB |
|
|
498
|
+
| Dependencies | 0 | 0 | 0 | N/A |
|
|
499
|
+
| Async Support | ✅ Built-in | ❌ Limited | ❌ Limited | ⚠️ Manual |
|
|
500
|
+
| Learning Curve | Low | Low | High (FP focused) | Low |
|
|
501
|
+
|
|
502
|
+
## Documentation
|
|
503
|
+
|
|
504
|
+
- **[Full Documentation](https://helmuthdu.github.io/vielzeug/toolkit/)**
|
|
505
|
+
- **[API Reference](https://helmuthdu.github.io/vielzeug/toolkit/api)**
|
|
506
|
+
- **[Usage Guide](https://helmuthdu.github.io/vielzeug/toolkit/usage)**
|
|
507
|
+
- **[Examples](https://helmuthdu.github.io/vielzeug/toolkit/examples/array)**
|
|
508
|
+
|
|
509
|
+
## Contributing
|
|
510
|
+
|
|
511
|
+
We welcome contributions! Please see our [Contributing Guide](../../CONTRIBUTING.md) for details.
|
|
512
|
+
|
|
513
|
+
### Development
|
|
514
|
+
|
|
515
|
+
```bash
|
|
516
|
+
# Install dependencies
|
|
517
|
+
pnpm install
|
|
518
|
+
|
|
519
|
+
# Build the package
|
|
520
|
+
pnpm build
|
|
521
|
+
|
|
522
|
+
# Run tests
|
|
523
|
+
pnpm test
|
|
524
|
+
|
|
525
|
+
# Run tests in watch mode
|
|
526
|
+
pnpm test:watch
|
|
527
|
+
|
|
528
|
+
# Type check
|
|
529
|
+
pnpm typecheck
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
## License
|
|
533
|
+
|
|
534
|
+
MIT © [Helmuth Saatkamp](https://github.com/helmuthdu/vielzeug)
|
|
535
|
+
|
|
536
|
+
## Related Packages
|
|
537
|
+
|
|
538
|
+
Part of the [@vielzeug](https://helmuthdu.github.io/vielzeug/) monorepo:
|
|
539
|
+
|
|
540
|
+
- **[@vielzeug/deposit](../deposit)** - Type-safe local storage with schemas and expiration
|
|
541
|
+
- **[@vielzeug/fetchit](../fetchit)** - Advanced HTTP client with caching and retries
|
|
542
|
+
- **[@vielzeug/formit](../formit)** - Type-safe form state and validation
|
|
543
|
+
- **[@vielzeug/i18nit](../i18nit)** - Internationalization with TypeScript
|
|
544
|
+
- **[@vielzeug/logit](../logit)** - Beautiful console logging
|
|
545
|
+
- **[@vielzeug/permit](../permit)** - Role-based access control
|
|
546
|
+
- **[@vielzeug/validit](../validit)** - Type-safe validation schemas
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attempt.cjs","sources":["../../src/async/attempt.ts"],"sourcesContent":["import { Logit } from '@vielzeug/logit';\nimport type { Fn } from '../types';\nimport { predict } from './predict';\nimport { retry } from './retry';\n\ntype AttemptOptions = {\n identifier?: string;\n retries?: number;\n silent?: boolean;\n timeout?: number;\n};\n\n/**\n * Attempts to execute a function with advanced error handling and retry logic.\n *\n * @example\n * ```ts\n * const unreliableFunction = async () => {\n * if (Math.random() < 0.7) throw new Error ('Random failure');\n * return 'Success!';\n * };\n *\n * await attempt(\n * unreliableFunction,\n * { retries: 3, silent: false, timeout: 5000 }); // Success! (or undefined if all attempts failed)\n * ```\n *\n * @param fn - The function to be executed.\n * @param [options] - Configuration options for the attempt.\n * @param [options.identifier] - Custom identifier for logging purposes.\n * @param [options.retries=0] - Number of retry attempts if the function fails.\n * @param [options.silent=false] - If true, suppresses error logging.\n * @param [options.timeout=7000] - Timeout in milliseconds for function execution.\n *\n * @returns The result of the function or undefined if it failed.\n */\nexport async function attempt<T extends Fn, R = Awaited<ReturnType<T>>>(\n fn: T,\n { silent = false, retries = 0, timeout = 7000, identifier = fn.name || 'anonymous function' }: AttemptOptions = {},\n): Promise<R | undefined> {\n try {\n return await retry(() => predict<R>(() => fn(), { timeout }), { times: retries + 1 });\n } catch (err) {\n if (!silent) {\n Logit.error(`attempt(${identifier}) -> all attempts failed`, { cause: err });\n }\n return undefined;\n }\n}\n"],"names":["attempt","fn","silent","retries","timeout","identifier","retry","predict","err","Logit"],"mappings":"+KAoCA,eAAsBA,EACpBC,EACA,CAAE,OAAAC,EAAS,GAAO,QAAAC,EAAU,EAAG,QAAAC,EAAU,IAAM,WAAAC,EAAaJ,EAAG,MAAQ,oBAAA,EAAyC,CAAA,EACxF,CACxB,GAAI,CACF,OAAO,MAAMK,EAAAA,MAAM,IAAMC,EAAAA,QAAW,IAAMN,EAAA,EAAM,CAAE,QAAAG,CAAA,CAAS,EAAG,CAAE,MAAOD,EAAU,EAAG,CACtF,OAASK,EAAK,CACPN,GACHO,QAAM,MAAM,WAAWJ,CAAU,2BAA4B,CAAE,MAAOG,EAAK,EAE7E,MACF,CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attempt.js","sources":["../../src/async/attempt.ts"],"sourcesContent":["import { Logit } from '@vielzeug/logit';\nimport type { Fn } from '../types';\nimport { predict } from './predict';\nimport { retry } from './retry';\n\ntype AttemptOptions = {\n identifier?: string;\n retries?: number;\n silent?: boolean;\n timeout?: number;\n};\n\n/**\n * Attempts to execute a function with advanced error handling and retry logic.\n *\n * @example\n * ```ts\n * const unreliableFunction = async () => {\n * if (Math.random() < 0.7) throw new Error ('Random failure');\n * return 'Success!';\n * };\n *\n * await attempt(\n * unreliableFunction,\n * { retries: 3, silent: false, timeout: 5000 }); // Success! (or undefined if all attempts failed)\n * ```\n *\n * @param fn - The function to be executed.\n * @param [options] - Configuration options for the attempt.\n * @param [options.identifier] - Custom identifier for logging purposes.\n * @param [options.retries=0] - Number of retry attempts if the function fails.\n * @param [options.silent=false] - If true, suppresses error logging.\n * @param [options.timeout=7000] - Timeout in milliseconds for function execution.\n *\n * @returns The result of the function or undefined if it failed.\n */\nexport async function attempt<T extends Fn, R = Awaited<ReturnType<T>>>(\n fn: T,\n { silent = false, retries = 0, timeout = 7000, identifier = fn.name || 'anonymous function' }: AttemptOptions = {},\n): Promise<R | undefined> {\n try {\n return await retry(() => predict<R>(() => fn(), { timeout }), { times: retries + 1 });\n } catch (err) {\n if (!silent) {\n Logit.error(`attempt(${identifier}) -> all attempts failed`, { cause: err });\n }\n return undefined;\n }\n}\n"],"names":["attempt","fn","silent","retries","timeout","identifier","retry","predict","err","Logit"],"mappings":";;;AAoCA,eAAsBA,EACpBC,GACA,EAAE,QAAAC,IAAS,IAAO,SAAAC,IAAU,GAAG,SAAAC,IAAU,KAAM,YAAAC,IAAaJ,EAAG,QAAQ,qBAAA,IAAyC,CAAA,GACxF;AACxB,MAAI;AACF,WAAO,MAAMK,EAAM,MAAMC,EAAW,MAAMN,EAAA,GAAM,EAAE,SAAAG,EAAA,CAAS,GAAG,EAAE,OAAOD,IAAU,GAAG;AAAA,EACtF,SAASK,GAAK;AACZ,IAAKN,KACHO,EAAM,MAAM,WAAWJ,CAAU,4BAA4B,EAAE,OAAOG,GAAK;AAE7E;AAAA,EACF;AACF;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defer.cjs","sources":["../../src/async/defer.ts"],"sourcesContent":["/**\n * Creates a deferred promise with resolve and reject methods exposed.\n * Useful for creating promises that are resolved/rejected externally.\n *\n * @example\n * ```ts\n * const deferred = defer<string>();\n *\n * setTimeout(() => {\n * deferred.resolve('Done!');\n * }, 1000);\n *\n * const result = await deferred.promise; // 'Done!'\n * ```\n *\n * @returns Object with promise and resolve/reject methods\n */\nexport function defer<T = void>(): {\n promise: Promise<T>;\n resolve: (value: T | PromiseLike<T>) => void;\n reject: (reason?: unknown) => void;\n} {\n let resolve!: (value: T | PromiseLike<T>) => void;\n let reject!: (reason?: unknown) => void;\n\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n return { promise, reject, resolve };\n}\n"],"names":["defer","resolve","reject","res","rej"],"mappings":"gFAiBO,SAASA,GAId,CACA,IAAIC,EACAC,EAOJ,MAAO,CAAE,QALO,IAAI,QAAW,CAACC,EAAKC,IAAQ,CAC3CH,EAAUE,EACVD,EAASE,CACX,CAAC,EAEiB,OAAAF,EAAQ,QAAAD,CAAA,CAC5B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defer.js","sources":["../../src/async/defer.ts"],"sourcesContent":["/**\n * Creates a deferred promise with resolve and reject methods exposed.\n * Useful for creating promises that are resolved/rejected externally.\n *\n * @example\n * ```ts\n * const deferred = defer<string>();\n *\n * setTimeout(() => {\n * deferred.resolve('Done!');\n * }, 1000);\n *\n * const result = await deferred.promise; // 'Done!'\n * ```\n *\n * @returns Object with promise and resolve/reject methods\n */\nexport function defer<T = void>(): {\n promise: Promise<T>;\n resolve: (value: T | PromiseLike<T>) => void;\n reject: (reason?: unknown) => void;\n} {\n let resolve!: (value: T | PromiseLike<T>) => void;\n let reject!: (reason?: unknown) => void;\n\n const promise = new Promise<T>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n return { promise, reject, resolve };\n}\n"],"names":["defer","resolve","reject","res","rej"],"mappings":"AAiBO,SAASA,IAId;AACA,MAAIC,GACAC;AAOJ,SAAO,EAAE,SALO,IAAI,QAAW,CAACC,GAAKC,MAAQ;AAC3C,IAAAH,IAAUE,GACVD,IAASE;AAAA,EACX,CAAC,GAEiB,QAAAF,GAAQ,SAAAD,EAAA;AAC5B;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delay.cjs","sources":["../../src/async/delay.ts"],"sourcesContent":["import type { Fn } from '../types';\nimport { sleep } from './sleep';\n\n/**\n * Delays the execution of a function by a specified amount of time.\n *\n * @example\n * ```ts\n * const log = () => console.log('Hello, world!');\n *\n * delay(log, 1000); // logs 'Hello, world!' after 1 second\n * ```\n *\n * @param fn - The function to be delayed.\n * @param delay - The amount of time to delay the function execution, in milliseconds. Default is 700.\n *\n * @returns A Promise that resolves with the result of the function execution.\n */\nexport async function delay<T extends Fn>(fn: T, delay = 700) {\n await sleep(delay);\n\n return fn();\n}\n"],"names":["delay","fn","sleep"],"mappings":"+GAkBA,eAAsBA,EAAoBC,EAAOD,EAAQ,IAAK,CAC5D,aAAME,EAAAA,MAAMF,CAAK,EAEVC,EAAA,CACT"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delay.js","sources":["../../src/async/delay.ts"],"sourcesContent":["import type { Fn } from '../types';\nimport { sleep } from './sleep';\n\n/**\n * Delays the execution of a function by a specified amount of time.\n *\n * @example\n * ```ts\n * const log = () => console.log('Hello, world!');\n *\n * delay(log, 1000); // logs 'Hello, world!' after 1 second\n * ```\n *\n * @param fn - The function to be delayed.\n * @param delay - The amount of time to delay the function execution, in milliseconds. Default is 700.\n *\n * @returns A Promise that resolves with the result of the function execution.\n */\nexport async function delay<T extends Fn>(fn: T, delay = 700) {\n await sleep(delay);\n\n return fn();\n}\n"],"names":["delay","fn","sleep"],"mappings":";AAkBA,eAAsBA,EAAoBC,GAAOD,IAAQ,KAAK;AAC5D,eAAME,EAAMF,CAAK,GAEVC,EAAA;AACT;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parallel.cjs","sources":["../../src/async/parallel.ts"],"sourcesContent":["/**\n * Processes an array with an async callback with controlled parallelism.\n * Similar to Promise.all, but limits how many items are processed concurrently.\n * Returns an ordered array of results.\n *\n * @example\n * ```ts\n * // Process 3 items at a time\n * const results = await parallel(3, [1, 2, 3, 4, 5], async (n) => {\n * await delay(100);\n * return n * 2;\n * });\n * // [2, 4, 6, 8, 10]\n *\n * // With abort signal\n * const controller = new AbortController();\n * const results = await parallel(2, items, async (item) => {\n * return processItem(item);\n * }, controller.signal);\n * ```\n *\n * @param limit - Maximum number of concurrent operations (must be >= 1)\n * @param array - Array of items to process\n * @param callback - Async function to process each item\n * @param signal - Optional AbortSignal to cancel processing\n * @returns Promise resolving to an ordered array of results\n * @throws {Error} If limit is less than 1\n * @throws {DOMException} If aborted via signal\n */\nexport async function parallel<T, R>(\n limit: number,\n array: T[],\n callback: (item: T, index: number, array: T[]) => Promise<R>,\n signal?: AbortSignal,\n): Promise<R[]> {\n if (limit < 1) {\n throw new Error('Limit must be at least 1');\n }\n\n if (signal?.aborted) {\n throw new DOMException('Aborted', 'AbortError');\n }\n\n const results: R[] = new Array(array.length);\n let currentIndex = 0;\n let hasError = false;\n let error: unknown;\n\n // Check for abort\n const checkAbort = () => {\n if (signal?.aborted) {\n hasError = true;\n error = new DOMException('Aborted', 'AbortError');\n return true;\n }\n return false;\n };\n\n // Worker function that processes items from the queue\n const worker = async (): Promise<void> => {\n while (currentIndex < array.length && !hasError) {\n if (checkAbort()) {\n break;\n }\n\n const index = currentIndex++;\n\n try {\n results[index] = await callback(array[index], index, array);\n } catch (err) {\n hasError = true;\n error = err;\n break;\n }\n }\n };\n\n // Create workers up to the limit\n const workers: Promise<void>[] = [];\n const workerCount = Math.min(limit, array.length);\n\n for (let i = 0; i < workerCount; i++) {\n workers.push(worker());\n }\n\n // Wait for all workers to complete\n await Promise.all(workers);\n\n // If there was an error, throw it\n if (hasError) {\n throw error;\n }\n\n return results;\n}\n"],"names":["parallel","limit","array","callback","signal","results","currentIndex","hasError","error","checkAbort","worker","index","err","workers","workerCount","i"],"mappings":"gFA6BA,eAAsBA,EACpBC,EACAC,EACAC,EACAC,EACc,CACd,GAAIH,EAAQ,EACV,MAAM,IAAI,MAAM,0BAA0B,EAG5C,GAAIG,GAAQ,QACV,MAAM,IAAI,aAAa,UAAW,YAAY,EAGhD,MAAMC,EAAe,IAAI,MAAMH,EAAM,MAAM,EAC3C,IAAII,EAAe,EACfC,EAAW,GACXC,EAGJ,MAAMC,EAAa,IACbL,GAAQ,SACVG,EAAW,GACXC,EAAQ,IAAI,aAAa,UAAW,YAAY,EACzC,IAEF,GAIHE,EAAS,SAA2B,CACxC,KAAOJ,EAAeJ,EAAM,QAAU,CAACK,GACjC,CAAAE,KAD2C,CAK/C,MAAME,EAAQL,IAEd,GAAI,CACFD,EAAQM,CAAK,EAAI,MAAMR,EAASD,EAAMS,CAAK,EAAGA,EAAOT,CAAK,CAC5D,OAASU,EAAK,CACZL,EAAW,GACXC,EAAQI,EACR,KACF,CACF,CACF,EAGMC,EAA2B,CAAA,EAC3BC,EAAc,KAAK,IAAIb,EAAOC,EAAM,MAAM,EAEhD,QAASa,EAAI,EAAGA,EAAID,EAAaC,IAC/BF,EAAQ,KAAKH,GAAQ,EAOvB,GAHA,MAAM,QAAQ,IAAIG,CAAO,EAGrBN,EACF,MAAMC,EAGR,OAAOH,CACT"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parallel.js","sources":["../../src/async/parallel.ts"],"sourcesContent":["/**\n * Processes an array with an async callback with controlled parallelism.\n * Similar to Promise.all, but limits how many items are processed concurrently.\n * Returns an ordered array of results.\n *\n * @example\n * ```ts\n * // Process 3 items at a time\n * const results = await parallel(3, [1, 2, 3, 4, 5], async (n) => {\n * await delay(100);\n * return n * 2;\n * });\n * // [2, 4, 6, 8, 10]\n *\n * // With abort signal\n * const controller = new AbortController();\n * const results = await parallel(2, items, async (item) => {\n * return processItem(item);\n * }, controller.signal);\n * ```\n *\n * @param limit - Maximum number of concurrent operations (must be >= 1)\n * @param array - Array of items to process\n * @param callback - Async function to process each item\n * @param signal - Optional AbortSignal to cancel processing\n * @returns Promise resolving to an ordered array of results\n * @throws {Error} If limit is less than 1\n * @throws {DOMException} If aborted via signal\n */\nexport async function parallel<T, R>(\n limit: number,\n array: T[],\n callback: (item: T, index: number, array: T[]) => Promise<R>,\n signal?: AbortSignal,\n): Promise<R[]> {\n if (limit < 1) {\n throw new Error('Limit must be at least 1');\n }\n\n if (signal?.aborted) {\n throw new DOMException('Aborted', 'AbortError');\n }\n\n const results: R[] = new Array(array.length);\n let currentIndex = 0;\n let hasError = false;\n let error: unknown;\n\n // Check for abort\n const checkAbort = () => {\n if (signal?.aborted) {\n hasError = true;\n error = new DOMException('Aborted', 'AbortError');\n return true;\n }\n return false;\n };\n\n // Worker function that processes items from the queue\n const worker = async (): Promise<void> => {\n while (currentIndex < array.length && !hasError) {\n if (checkAbort()) {\n break;\n }\n\n const index = currentIndex++;\n\n try {\n results[index] = await callback(array[index], index, array);\n } catch (err) {\n hasError = true;\n error = err;\n break;\n }\n }\n };\n\n // Create workers up to the limit\n const workers: Promise<void>[] = [];\n const workerCount = Math.min(limit, array.length);\n\n for (let i = 0; i < workerCount; i++) {\n workers.push(worker());\n }\n\n // Wait for all workers to complete\n await Promise.all(workers);\n\n // If there was an error, throw it\n if (hasError) {\n throw error;\n }\n\n return results;\n}\n"],"names":["parallel","limit","array","callback","signal","results","currentIndex","hasError","error","checkAbort","worker","index","err","workers","workerCount","i"],"mappings":"AA6BA,eAAsBA,EACpBC,GACAC,GACAC,GACAC,GACc;AACd,MAAIH,IAAQ;AACV,UAAM,IAAI,MAAM,0BAA0B;AAG5C,MAAIG,GAAQ;AACV,UAAM,IAAI,aAAa,WAAW,YAAY;AAGhD,QAAMC,IAAe,IAAI,MAAMH,EAAM,MAAM;AAC3C,MAAII,IAAe,GACfC,IAAW,IACXC;AAGJ,QAAMC,IAAa,MACbL,GAAQ,WACVG,IAAW,IACXC,IAAQ,IAAI,aAAa,WAAW,YAAY,GACzC,MAEF,IAIHE,IAAS,YAA2B;AACxC,WAAOJ,IAAeJ,EAAM,UAAU,CAACK,KACjC,CAAAE,OAD2C;AAK/C,YAAME,IAAQL;AAEd,UAAI;AACF,QAAAD,EAAQM,CAAK,IAAI,MAAMR,EAASD,EAAMS,CAAK,GAAGA,GAAOT,CAAK;AAAA,MAC5D,SAASU,GAAK;AACZ,QAAAL,IAAW,IACXC,IAAQI;AACR;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAGMC,IAA2B,CAAA,GAC3BC,IAAc,KAAK,IAAIb,GAAOC,EAAM,MAAM;AAEhD,WAASa,IAAI,GAAGA,IAAID,GAAaC;AAC/B,IAAAF,EAAQ,KAAKH,GAAQ;AAOvB,MAHA,MAAM,QAAQ,IAAIG,CAAO,GAGrBN;AACF,UAAMC;AAGR,SAAOH;AACT;"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("../function/assert.cjs");function u(e){a.assert(e>=1,"Pool limit must be at least 1",{args:{limit:e},type:RangeError});let t=0;const n=[],r=()=>{n.length>0&&t<e&&n.shift()?.()};return async o=>{for(;t>=e;)await new Promise(s=>n.push(s));t++;try{return await o()}finally{t--,r()}}}exports.pool=u;
|
|
2
|
+
//# sourceMappingURL=pool.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pool.cjs","sources":["../../src/async/pool.ts"],"sourcesContent":["import { assert } from '../function/assert';\n\n/**\n * Creates a promise pool that limits the number of concurrent promises.\n * Useful for rate limiting API calls or controlling resource usage.\n *\n * @example\n * ```ts\n * const requestPool = pool(3);\n *\n * const results = await Promise.all([\n * requestPool(() => fetch('/api/1')),\n * requestPool(() => fetch('/api/2')),\n * requestPool(() => fetch('/api/3')),\n * requestPool(() => fetch('/api/4')), // Will wait for one of the above to finish\n * ]);\n * ```\n *\n * @param limit - Maximum number of concurrent promises\n * @returns Function that accepts a promise-returning function and executes it when a slot is available\n */\nexport function pool(limit: number): <T>(fn: () => Promise<T>) => Promise<T> {\n assert(limit >= 1, 'Pool limit must be at least 1', { args: { limit }, type: RangeError });\n\n let activeCount = 0;\n const queue: Array<() => void> = [];\n\n const dequeue = (): void => {\n if (queue.length > 0 && activeCount < limit) {\n const next = queue.shift();\n next?.();\n }\n };\n\n return async <T>(fn: () => Promise<T>): Promise<T> => {\n while (activeCount >= limit) {\n await new Promise<void>((resolve) => queue.push(resolve));\n }\n\n activeCount++;\n\n try {\n return await fn();\n } finally {\n activeCount--;\n dequeue();\n }\n };\n}\n"],"names":["pool","limit","assert","activeCount","queue","dequeue","fn","resolve"],"mappings":"0HAqBO,SAASA,EAAKC,EAAwD,CAC3EC,SAAOD,GAAS,EAAG,gCAAiC,CAAE,KAAM,CAAE,MAAAA,CAAA,EAAS,KAAM,WAAY,EAEzF,IAAIE,EAAc,EAClB,MAAMC,EAA2B,CAAA,EAE3BC,EAAU,IAAY,CACtBD,EAAM,OAAS,GAAKD,EAAcF,GACvBG,EAAM,MAAA,IACnB,CAEJ,EAEA,MAAO,OAAUE,GAAqC,CACpD,KAAOH,GAAeF,GACpB,MAAM,IAAI,QAAeM,GAAYH,EAAM,KAAKG,CAAO,CAAC,EAG1DJ,IAEA,GAAI,CACF,OAAO,MAAMG,EAAA,CACf,QAAA,CACEH,IACAE,EAAA,CACF,CACF,CACF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { assert as s } from "../function/assert.js";
|
|
2
|
+
function c(e) {
|
|
3
|
+
s(e >= 1, "Pool limit must be at least 1", { args: { limit: e }, type: RangeError });
|
|
4
|
+
let t = 0;
|
|
5
|
+
const n = [], r = () => {
|
|
6
|
+
n.length > 0 && t < e && n.shift()?.();
|
|
7
|
+
};
|
|
8
|
+
return async (o) => {
|
|
9
|
+
for (; t >= e; )
|
|
10
|
+
await new Promise((a) => n.push(a));
|
|
11
|
+
t++;
|
|
12
|
+
try {
|
|
13
|
+
return await o();
|
|
14
|
+
} finally {
|
|
15
|
+
t--, r();
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
c as pool
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=pool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pool.js","sources":["../../src/async/pool.ts"],"sourcesContent":["import { assert } from '../function/assert';\n\n/**\n * Creates a promise pool that limits the number of concurrent promises.\n * Useful for rate limiting API calls or controlling resource usage.\n *\n * @example\n * ```ts\n * const requestPool = pool(3);\n *\n * const results = await Promise.all([\n * requestPool(() => fetch('/api/1')),\n * requestPool(() => fetch('/api/2')),\n * requestPool(() => fetch('/api/3')),\n * requestPool(() => fetch('/api/4')), // Will wait for one of the above to finish\n * ]);\n * ```\n *\n * @param limit - Maximum number of concurrent promises\n * @returns Function that accepts a promise-returning function and executes it when a slot is available\n */\nexport function pool(limit: number): <T>(fn: () => Promise<T>) => Promise<T> {\n assert(limit >= 1, 'Pool limit must be at least 1', { args: { limit }, type: RangeError });\n\n let activeCount = 0;\n const queue: Array<() => void> = [];\n\n const dequeue = (): void => {\n if (queue.length > 0 && activeCount < limit) {\n const next = queue.shift();\n next?.();\n }\n };\n\n return async <T>(fn: () => Promise<T>): Promise<T> => {\n while (activeCount >= limit) {\n await new Promise<void>((resolve) => queue.push(resolve));\n }\n\n activeCount++;\n\n try {\n return await fn();\n } finally {\n activeCount--;\n dequeue();\n }\n };\n}\n"],"names":["pool","limit","assert","activeCount","queue","dequeue","fn","resolve"],"mappings":";AAqBO,SAASA,EAAKC,GAAwD;AAC3E,EAAAC,EAAOD,KAAS,GAAG,iCAAiC,EAAE,MAAM,EAAE,OAAAA,EAAA,GAAS,MAAM,YAAY;AAEzF,MAAIE,IAAc;AAClB,QAAMC,IAA2B,CAAA,GAE3BC,IAAU,MAAY;AAC1B,IAAID,EAAM,SAAS,KAAKD,IAAcF,KACvBG,EAAM,MAAA,IACnB;AAAA,EAEJ;AAEA,SAAO,OAAUE,MAAqC;AACpD,WAAOH,KAAeF;AACpB,YAAM,IAAI,QAAc,CAACM,MAAYH,EAAM,KAAKG,CAAO,CAAC;AAG1D,IAAAJ;AAEA,QAAI;AACF,aAAO,MAAMG,EAAA;AAAA,IACf,UAAA;AACE,MAAAH,KACAE,EAAA;AAAA,IACF;AAAA,EACF;AACF;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"predict.cjs","sources":["../../src/async/predict.ts"],"sourcesContent":["/**\n * Creates a Promise that can be aborted using an AbortController.\n *\n * @example\n * ```ts\n * const slowFn = () => new Promise(resolve => setTimeout(() => resolve('slow'), 10000));\n * const fastFn = () => new Promise(resolve => setTimeout(() => resolve('fast'), 5000));\n *\n * predict(slowFn); // rejects after 7 seconds\n * predict(fastFn); // resolves with 'fast' after 5 seconds\n * ```\n *\n * @param fn - The function to execute, which receives an AbortSignal.\n * @param options - The options for the abortable function.\n * @param [options.signal] - The AbortSignal to use for aborting the Promise.\n * @param [options.timeout=7000] - The timeout in milliseconds after which the Promise will be aborted.\n *\n * @returns - A Promise that resolves with the result of the callback or rejects if aborted.\n */\nexport function predict<T>(\n fn: (signal: AbortSignal) => Promise<T>,\n options: { signal?: AbortSignal; timeout?: number } = {},\n): Promise<T> {\n const { signal, timeout = 7000 } = options;\n const abortSignal = signal ? AbortSignal.any([AbortSignal.timeout(timeout), signal]) : AbortSignal.timeout(timeout);\n\n return Promise.race([\n fn(abortSignal),\n new Promise<never>((_, reject) => {\n abortSignal.addEventListener('abort', () => reject(new Error('Operation aborted')), { once: true });\n }),\n ]);\n}\n"],"names":["predict","fn","options","signal","timeout","abortSignal","_","reject"],"mappings":"gFAmBO,SAASA,EACdC,EACAC,EAAsD,GAC1C,CACZ,KAAM,CAAE,OAAAC,EAAQ,QAAAC,EAAU,GAAA,EAASF,EAC7BG,EAAcF,EAAS,YAAY,IAAI,CAAC,YAAY,QAAQC,CAAO,EAAGD,CAAM,CAAC,EAAI,YAAY,QAAQC,CAAO,EAElH,OAAO,QAAQ,KAAK,CAClBH,EAAGI,CAAW,EACd,IAAI,QAAe,CAACC,EAAGC,IAAW,CAChCF,EAAY,iBAAiB,QAAS,IAAME,EAAO,IAAI,MAAM,mBAAmB,CAAC,EAAG,CAAE,KAAM,EAAA,CAAM,CACpG,CAAC,CAAA,CACF,CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"predict.js","sources":["../../src/async/predict.ts"],"sourcesContent":["/**\n * Creates a Promise that can be aborted using an AbortController.\n *\n * @example\n * ```ts\n * const slowFn = () => new Promise(resolve => setTimeout(() => resolve('slow'), 10000));\n * const fastFn = () => new Promise(resolve => setTimeout(() => resolve('fast'), 5000));\n *\n * predict(slowFn); // rejects after 7 seconds\n * predict(fastFn); // resolves with 'fast' after 5 seconds\n * ```\n *\n * @param fn - The function to execute, which receives an AbortSignal.\n * @param options - The options for the abortable function.\n * @param [options.signal] - The AbortSignal to use for aborting the Promise.\n * @param [options.timeout=7000] - The timeout in milliseconds after which the Promise will be aborted.\n *\n * @returns - A Promise that resolves with the result of the callback or rejects if aborted.\n */\nexport function predict<T>(\n fn: (signal: AbortSignal) => Promise<T>,\n options: { signal?: AbortSignal; timeout?: number } = {},\n): Promise<T> {\n const { signal, timeout = 7000 } = options;\n const abortSignal = signal ? AbortSignal.any([AbortSignal.timeout(timeout), signal]) : AbortSignal.timeout(timeout);\n\n return Promise.race([\n fn(abortSignal),\n new Promise<never>((_, reject) => {\n abortSignal.addEventListener('abort', () => reject(new Error('Operation aborted')), { once: true });\n }),\n ]);\n}\n"],"names":["predict","fn","options","signal","timeout","abortSignal","_","reject"],"mappings":"AAmBO,SAASA,EACdC,GACAC,IAAsD,IAC1C;AACZ,QAAM,EAAE,QAAAC,GAAQ,SAAAC,IAAU,IAAA,IAASF,GAC7BG,IAAcF,IAAS,YAAY,IAAI,CAAC,YAAY,QAAQC,CAAO,GAAGD,CAAM,CAAC,IAAI,YAAY,QAAQC,CAAO;AAElH,SAAO,QAAQ,KAAK;AAAA,IAClBH,EAAGI,CAAW;AAAA,IACd,IAAI,QAAe,CAACC,GAAGC,MAAW;AAChC,MAAAF,EAAY,iBAAiB,SAAS,MAAME,EAAO,IAAI,MAAM,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAA,CAAM;AAAA,IACpG,CAAC;AAAA,EAAA,CACF;AACH;"}
|