@tstdl/base 0.93.139 → 0.93.141
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 +166 -0
- package/ai/genkit/multi-region.plugin.js +5 -3
- package/ai/genkit/tests/multi-region.test.d.ts +1 -0
- package/ai/genkit/tests/multi-region.test.js +5 -2
- package/ai/parser/parser.js +2 -2
- package/ai/prompts/build.js +1 -0
- package/ai/prompts/instructions-formatter.d.ts +15 -2
- package/ai/prompts/instructions-formatter.js +36 -31
- package/ai/prompts/prompt-builder.js +5 -5
- package/ai/prompts/steering.d.ts +3 -2
- package/ai/prompts/steering.js +3 -1
- package/ai/tests/instructions-formatter.test.js +1 -0
- package/api/README.md +403 -0
- package/api/client/client.js +7 -13
- package/api/client/tests/api-client.test.js +10 -10
- package/api/default-error-handlers.js +1 -1
- package/api/response.d.ts +2 -2
- package/api/response.js +22 -33
- package/api/server/api-controller.d.ts +1 -1
- package/api/server/api-controller.js +3 -3
- package/api/server/api-request-token.provider.d.ts +1 -0
- package/api/server/api-request-token.provider.js +1 -0
- package/api/server/middlewares/allowed-methods.middleware.js +2 -1
- package/api/server/middlewares/content-type.middleware.js +2 -1
- package/api/types.d.ts +3 -2
- package/application/README.md +240 -0
- package/application/application.d.ts +1 -1
- package/application/application.js +3 -3
- package/application/providers.d.ts +20 -2
- package/application/providers.js +34 -7
- package/audit/README.md +267 -0
- package/audit/module.d.ts +5 -0
- package/audit/module.js +9 -1
- package/authentication/README.md +288 -0
- package/authentication/client/authentication.service.d.ts +12 -11
- package/authentication/client/authentication.service.js +21 -21
- package/authentication/client/http-client.middleware.js +2 -2
- package/authentication/server/module.d.ts +5 -0
- package/authentication/server/module.js +9 -1
- package/authentication/tests/authentication.api-controller.test.js +1 -1
- package/authentication/tests/authentication.api-request-token.provider.test.js +1 -1
- package/authentication/tests/authentication.client-error-handling.test.js +2 -1
- package/authentication/tests/authentication.client-service-refresh.test.js +5 -3
- package/authentication/tests/authentication.client-service.test.js +1 -1
- package/browser/README.md +401 -0
- package/cancellation/README.md +156 -0
- package/cancellation/tests/coverage.test.d.ts +1 -0
- package/cancellation/tests/coverage.test.js +49 -0
- package/cancellation/tests/leak.test.js +24 -29
- package/cancellation/tests/token.test.d.ts +1 -0
- package/cancellation/tests/token.test.js +136 -0
- package/cancellation/token.d.ts +53 -177
- package/cancellation/token.js +132 -208
- package/circuit-breaker/postgres/module.d.ts +1 -0
- package/circuit-breaker/postgres/module.js +5 -1
- package/context/README.md +174 -0
- package/cookie/README.md +161 -0
- package/css/README.md +157 -0
- package/data-structures/README.md +320 -0
- package/decorators/README.md +140 -0
- package/distributed-loop/README.md +231 -0
- package/distributed-loop/distributed-loop.js +1 -1
- package/document-management/README.md +403 -0
- package/document-management/server/configure.js +5 -1
- package/document-management/server/module.d.ts +1 -1
- package/document-management/server/module.js +1 -1
- package/document-management/server/services/document-management-ancillary.service.js +1 -1
- package/document-management/server/services/document-management.service.js +9 -7
- package/document-management/tests/ai-config-hierarchy.test.js +0 -5
- package/document-management/tests/document-management-ai-overrides.test.js +0 -1
- package/document-management/tests/document-management-core.test.js +2 -7
- package/document-management/tests/document-management.api.test.js +6 -7
- package/document-management/tests/document-statistics.service.test.js +11 -12
- package/document-management/tests/document-validation-ai-overrides.test.js +0 -1
- package/document-management/tests/document.service.test.js +3 -3
- package/document-management/tests/enum-helpers.test.js +2 -3
- package/dom/README.md +213 -0
- package/enumerable/README.md +259 -0
- package/enumeration/README.md +121 -0
- package/errors/README.md +267 -0
- package/examples/document-management/main.d.ts +1 -0
- package/examples/document-management/main.js +14 -11
- package/file/README.md +191 -0
- package/formats/README.md +210 -0
- package/function/README.md +144 -0
- package/http/README.md +318 -0
- package/http/client/adapters/undici.adapter.js +1 -1
- package/http/client/http-client-request.d.ts +6 -5
- package/http/client/http-client-request.js +8 -9
- package/http/server/node/node-http-server.js +1 -2
- package/image-service/README.md +137 -0
- package/injector/README.md +491 -0
- package/intl/README.md +113 -0
- package/json-path/README.md +182 -0
- package/jsx/README.md +154 -0
- package/key-value-store/README.md +191 -0
- package/key-value-store/postgres/module.d.ts +1 -0
- package/key-value-store/postgres/module.js +5 -1
- package/lock/README.md +249 -0
- package/lock/postgres/module.d.ts +1 -0
- package/lock/postgres/module.js +5 -1
- package/lock/web/web-lock.js +119 -47
- package/logger/README.md +287 -0
- package/mail/README.md +256 -0
- package/mail/module.d.ts +5 -1
- package/mail/module.js +11 -6
- package/memory/README.md +144 -0
- package/message-bus/README.md +244 -0
- package/message-bus/message-bus-base.js +1 -1
- package/module/README.md +182 -0
- package/module/module.d.ts +1 -1
- package/module/module.js +77 -17
- package/module/modules/web-server.module.js +3 -4
- package/notification/server/module.d.ts +1 -0
- package/notification/server/module.js +5 -1
- package/notification/tests/notification-flow.test.js +2 -2
- package/notification/tests/notification-type.service.test.js +24 -15
- package/object-storage/README.md +300 -0
- package/openid-connect/README.md +274 -0
- package/orm/README.md +423 -0
- package/orm/decorators.d.ts +5 -1
- package/orm/decorators.js +1 -1
- package/orm/server/drizzle/schema-converter.js +17 -30
- package/orm/server/encryption.d.ts +0 -1
- package/orm/server/encryption.js +1 -4
- package/orm/server/index.d.ts +1 -6
- package/orm/server/index.js +1 -6
- package/orm/server/migration.d.ts +19 -0
- package/orm/server/migration.js +72 -0
- package/orm/server/repository.d.ts +1 -1
- package/orm/server/transaction.d.ts +5 -10
- package/orm/server/transaction.js +22 -26
- package/orm/server/transactional.js +3 -3
- package/orm/tests/database-migration.test.d.ts +1 -0
- package/orm/tests/database-migration.test.js +82 -0
- package/orm/tests/encryption.test.js +3 -4
- package/orm/utils.d.ts +17 -2
- package/orm/utils.js +49 -1
- package/package.json +9 -6
- package/password/README.md +164 -0
- package/pdf/README.md +246 -0
- package/polyfills.js +1 -0
- package/pool/README.md +198 -0
- package/process/README.md +237 -0
- package/promise/README.md +252 -0
- package/promise/cancelable-promise.js +1 -1
- package/random/README.md +193 -0
- package/rate-limit/postgres/module.d.ts +1 -0
- package/rate-limit/postgres/module.js +5 -1
- package/reflection/README.md +305 -0
- package/reflection/decorator-data.js +11 -12
- package/rpc/README.md +386 -0
- package/rxjs-utils/README.md +262 -0
- package/schema/README.md +342 -0
- package/serializer/README.md +342 -0
- package/signals/implementation/README.md +134 -0
- package/sse/README.md +278 -0
- package/task-queue/README.md +293 -0
- package/task-queue/postgres/drizzle/{0000_simple_invisible_woman.sql → 0000_wakeful_sunspot.sql} +22 -14
- package/task-queue/postgres/drizzle/meta/0000_snapshot.json +160 -82
- package/task-queue/postgres/drizzle/meta/_journal.json +2 -2
- package/task-queue/postgres/module.d.ts +1 -0
- package/task-queue/postgres/module.js +5 -1
- package/task-queue/postgres/schemas.d.ts +9 -6
- package/task-queue/postgres/schemas.js +4 -3
- package/task-queue/postgres/task-queue.d.ts +4 -13
- package/task-queue/postgres/task-queue.js +462 -355
- package/task-queue/postgres/task.model.d.ts +12 -5
- package/task-queue/postgres/task.model.js +51 -25
- package/task-queue/task-context.d.ts +2 -2
- package/task-queue/task-context.js +8 -8
- package/task-queue/task-queue.d.ts +53 -19
- package/task-queue/task-queue.js +121 -55
- package/task-queue/tests/cascading-cancellations.test.d.ts +1 -0
- package/task-queue/tests/cascading-cancellations.test.js +38 -0
- package/task-queue/tests/complex.test.js +45 -229
- package/task-queue/tests/coverage-branch.test.d.ts +1 -0
- package/task-queue/tests/coverage-branch.test.js +407 -0
- package/task-queue/tests/coverage-enhancement.test.d.ts +1 -0
- package/task-queue/tests/coverage-enhancement.test.js +144 -0
- package/task-queue/tests/dag-dependencies.test.d.ts +1 -0
- package/task-queue/tests/dag-dependencies.test.js +41 -0
- package/task-queue/tests/dependencies.test.js +28 -26
- package/task-queue/tests/extensive-dependencies.test.js +64 -139
- package/task-queue/tests/fan-out-spawning.test.d.ts +1 -0
- package/task-queue/tests/fan-out-spawning.test.js +53 -0
- package/task-queue/tests/idempotent-replacement.test.d.ts +1 -0
- package/task-queue/tests/idempotent-replacement.test.js +61 -0
- package/task-queue/tests/missing-idempotent-tasks.test.d.ts +1 -0
- package/task-queue/tests/missing-idempotent-tasks.test.js +38 -0
- package/task-queue/tests/queue.test.js +128 -8
- package/task-queue/tests/worker.test.js +39 -16
- package/task-queue/tests/zombie-parent.test.d.ts +1 -0
- package/task-queue/tests/zombie-parent.test.js +45 -0
- package/task-queue/tests/zombie-recovery.test.d.ts +1 -0
- package/task-queue/tests/zombie-recovery.test.js +51 -0
- package/templates/README.md +287 -0
- package/test5.js +5 -5
- package/testing/README.md +157 -0
- package/testing/integration-setup.d.ts +4 -4
- package/testing/integration-setup.js +54 -29
- package/text/README.md +346 -0
- package/text/localization.service.js +2 -2
- package/threading/README.md +238 -0
- package/types/README.md +311 -0
- package/utils/README.md +322 -0
- package/utils/async-iterable-helpers/observable-iterable.d.ts +1 -1
- package/utils/async-iterable-helpers/observable-iterable.js +4 -8
- package/utils/async-iterable-helpers/take-until.js +4 -4
- package/utils/backoff.js +89 -30
- package/utils/file-reader.js +1 -2
- package/utils/retry-with-backoff.js +1 -1
- package/utils/timer.d.ts +1 -1
- package/utils/timer.js +5 -7
- package/utils/timing.d.ts +1 -1
- package/utils/timing.js +2 -4
- package/utils/z-base32.d.ts +1 -0
- package/utils/z-base32.js +1 -0
package/utils/README.md
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# @tstdl/base/utils
|
|
2
|
+
|
|
3
|
+
A comprehensive, modular, and tree-shakeable utility library for TypeScript, providing a rich set of tools for common programming tasks. It features extensive support for asynchronous iterables, object manipulation, cryptography, streams, and robust type safety.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [✨ Features](#-features)
|
|
8
|
+
- [Core Concepts](#core-concepts)
|
|
9
|
+
- [🚀 Basic Usage](#-basic-usage)
|
|
10
|
+
- [🔧 Advanced Topics](#-advanced-topics)
|
|
11
|
+
- [Async Iterable Pipelines](#async-iterable-pipelines)
|
|
12
|
+
- [Parallel Processing](#parallel-processing)
|
|
13
|
+
- [Deep Object Merging & Decycling](#deep-object-merging--decycling)
|
|
14
|
+
- [Resilient Operations (Backoff & Retry)](#resilient-operations-backoff--retry)
|
|
15
|
+
- [Cryptography Wrappers](#cryptography-wrappers)
|
|
16
|
+
- [Stream Utilities](#stream-utilities)
|
|
17
|
+
- [📚 API](#-api)
|
|
18
|
+
|
|
19
|
+
## ✨ Features
|
|
20
|
+
|
|
21
|
+
- **Async Iterable Helpers**: A complete suite of functional operators (`map`, `filter`, `reduce`, `batch`, etc.) for `AsyncIterable`, enabling powerful streaming data pipelines.
|
|
22
|
+
- **Parallel Processing**: Utilities to process async iterables concurrently (`parallelMap`, `parallelForEach`).
|
|
23
|
+
- **Object Manipulation**: Advanced helpers for deep merging, circular reference handling (`decycle`/`recycle`), and type-safe property path access.
|
|
24
|
+
- **Cryptography**: Simplified, promise-based wrappers around the Web Crypto API for encryption, hashing, and signing.
|
|
25
|
+
- **Resilience Patterns**: Built-in `backoffLoop` and `retryAsync` for handling unstable operations with exponential backoff and jitter.
|
|
26
|
+
- **Stream Utilities**: Tools to convert between Streams, Iterables, and Buffers, including `readBinaryStream` and `toBytesStream`.
|
|
27
|
+
- **Type Safety**: Extensive collection of type guards (`isString`, `isDefined`) and assertion functions (`assertDefinedPass`) to narrow types at runtime.
|
|
28
|
+
- **Zero Dependencies**: Lightweight and self-contained.
|
|
29
|
+
|
|
30
|
+
## Core Concepts
|
|
31
|
+
|
|
32
|
+
### Iterable-First Design
|
|
33
|
+
|
|
34
|
+
The library prioritizes `Iterable` and `AsyncIterable` interfaces. This allows for memory-efficient, lazy evaluation of data sequences. Instead of loading large datasets into arrays, you can process them item-by-item as they flow through a pipeline.
|
|
35
|
+
|
|
36
|
+
### Tree-Shakeability
|
|
37
|
+
|
|
38
|
+
Every utility is exported in a way that allows modern bundlers to strip out unused code. You can import everything from the root `@tstdl/base/utils`, and only what you use will end up in your production build.
|
|
39
|
+
|
|
40
|
+
### Type Guards & Assertions
|
|
41
|
+
|
|
42
|
+
We provide pairs of functions for type checking:
|
|
43
|
+
|
|
44
|
+
- **Guards** (`isString`): Return a boolean and narrow the type in TypeScript.
|
|
45
|
+
- **Assertions** (`assertString`): Throw an `AssertionError` if the check fails, asserting the type.
|
|
46
|
+
- **Pass-through Assertions** (`assertStringPass`): Assert the type and return the value, allowing for inline checks.
|
|
47
|
+
|
|
48
|
+
## 🚀 Basic Usage
|
|
49
|
+
|
|
50
|
+
Import the utilities you need directly from the package root.
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { isDefined, timeout, mapAsync, toArrayAsync, randomString } from '@tstdl/base/utils';
|
|
54
|
+
|
|
55
|
+
async function main() {
|
|
56
|
+
// 1. Simple Type Guard
|
|
57
|
+
const value: string | undefined = 'Hello';
|
|
58
|
+
if (isDefined(value)) {
|
|
59
|
+
console.log(value.toUpperCase()); // value is typed as string here
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 2. Async Iterable Helper
|
|
63
|
+
async function* numberGenerator() {
|
|
64
|
+
yield 1;
|
|
65
|
+
yield 2;
|
|
66
|
+
yield 3;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const doubled = await toArrayAsync(
|
|
70
|
+
mapAsync(numberGenerator(), async (num) => {
|
|
71
|
+
await timeout(100); // Wait 100ms
|
|
72
|
+
return num * 2;
|
|
73
|
+
}),
|
|
74
|
+
);
|
|
75
|
+
console.log(doubled); // [2, 4, 6]
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 🔧 Advanced Topics
|
|
80
|
+
|
|
81
|
+
### Async Iterable Pipelines
|
|
82
|
+
|
|
83
|
+
Chain multiple operations to process data streams efficiently. This is similar to RxJS or LINQ but built on standard `AsyncIterable`.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { range, filterAsync, mapAsync, batchAsync, toArrayAsync } from '@tstdl/base/utils';
|
|
87
|
+
|
|
88
|
+
// Create a pipeline
|
|
89
|
+
const pipeline = batchAsync(
|
|
90
|
+
mapAsync(
|
|
91
|
+
filterAsync(
|
|
92
|
+
// Source: Sync iterable converted to async automatically
|
|
93
|
+
range(1, 100),
|
|
94
|
+
(x) => x % 2 === 0, // Keep evens
|
|
95
|
+
),
|
|
96
|
+
async (x) => x * 10, // Multiply by 10
|
|
97
|
+
),
|
|
98
|
+
5, // Batch into arrays of 5 items
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Execute the pipeline
|
|
102
|
+
const batches = await toArrayAsync(pipeline);
|
|
103
|
+
// Result: [[20, 40, 60, 80, 100], [120, ...], ...]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Parallel Processing
|
|
107
|
+
|
|
108
|
+
When processing items involves IO (like network requests), processing them sequentially is slow. Use `parallelMap` or `parallelForEach` to control concurrency.
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { parallelMap, toArrayAsync } from '@tstdl/base/utils';
|
|
112
|
+
|
|
113
|
+
const userIds = [1, 2, 3, 4, 5];
|
|
114
|
+
|
|
115
|
+
const users = await toArrayAsync(
|
|
116
|
+
parallelMap(
|
|
117
|
+
userIds,
|
|
118
|
+
3, // Concurrency: Process 3 items at a time
|
|
119
|
+
true, // Keep Order: Ensure output order matches input order
|
|
120
|
+
async (id) => {
|
|
121
|
+
const response = await fetch(`/users/${id}`);
|
|
122
|
+
return response.json();
|
|
123
|
+
},
|
|
124
|
+
),
|
|
125
|
+
);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Deep Object Merging & Decycling
|
|
129
|
+
|
|
130
|
+
Handle complex objects with circular references or deep structures.
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { mergeDeep, decycle, recycle } from '@tstdl/base/utils';
|
|
134
|
+
|
|
135
|
+
// Deep Merge
|
|
136
|
+
const defaultSettings = { theme: { color: 'blue', font: 'arial' } };
|
|
137
|
+
const userSettings = { theme: { color: 'red' } };
|
|
138
|
+
|
|
139
|
+
const settings = mergeDeep(defaultSettings, userSettings);
|
|
140
|
+
// { theme: { color: 'red', font: 'arial' } }
|
|
141
|
+
|
|
142
|
+
// Circular References
|
|
143
|
+
const a: any = { name: 'A' };
|
|
144
|
+
const b: any = { name: 'B', parent: a };
|
|
145
|
+
a.child = b;
|
|
146
|
+
|
|
147
|
+
// JSON.stringify(a) would throw. Use decycle:
|
|
148
|
+
const safe = decycle(a);
|
|
149
|
+
const json = JSON.stringify(safe);
|
|
150
|
+
|
|
151
|
+
// Restore later
|
|
152
|
+
const restored = recycle(JSON.parse(json));
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Resilient Operations (Backoff & Retry)
|
|
156
|
+
|
|
157
|
+
Handle transient failures and hanging requests using `backoffLoop` and `retryWithBackoff`.
|
|
158
|
+
|
|
159
|
+
#### backoffLoop
|
|
160
|
+
|
|
161
|
+
Manual control over a retry loop.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { backoffLoop } from '@tstdl/base/utils';
|
|
165
|
+
|
|
166
|
+
await backoffLoop(
|
|
167
|
+
async (controller) => {
|
|
168
|
+
try {
|
|
169
|
+
await fetch('https://api.example.com/unstable');
|
|
170
|
+
controller.break(); // Success, exit loop
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.warn('Request failed, retrying...');
|
|
173
|
+
controller.backoff(); // Wait (exponentially increasing delay) then retry
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
strategy: 'exponential',
|
|
178
|
+
initialDelay: 500,
|
|
179
|
+
maximumDelay: 5000,
|
|
180
|
+
jitter: 0.1,
|
|
181
|
+
},
|
|
182
|
+
);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### retryWithBackoff
|
|
186
|
+
|
|
187
|
+
A higher-order utility that combines retries, exponential backoff, jitter, and timeouts. It also automatically respects `Retry-After` headers for HTTP errors.
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import { retryWithBackoff } from '@tstdl/base/utils';
|
|
191
|
+
|
|
192
|
+
const result = await retryWithBackoff(
|
|
193
|
+
async (signal) => {
|
|
194
|
+
// signal is set if the attempt times out or is cancelled
|
|
195
|
+
return await fetch('https://api.example.com/data', { signal: signal.asAbortSignal() });
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
timeout: 5000, // Timeout for EACH attempt
|
|
199
|
+
retry: {
|
|
200
|
+
shouldRetry: (error, attempt) => attempt < 3 && isTransientError(error),
|
|
201
|
+
backoff: { strategy: 'exponential', initialDelay: 1000 },
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
);
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Cryptography Wrappers
|
|
208
|
+
|
|
209
|
+
Simplified, promise-based access to modern crypto standards.
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
import { digest, generatePbkdf2Key, encrypt, decrypt, encodeBase64 } from '@tstdl/base/utils';
|
|
213
|
+
|
|
214
|
+
// Hashing
|
|
215
|
+
const hash = await digest('SHA-256', 'Hello World');
|
|
216
|
+
console.log(await hash.toHex());
|
|
217
|
+
|
|
218
|
+
// Encryption
|
|
219
|
+
const key = await generatePbkdf2Key(true);
|
|
220
|
+
const encrypted = encrypt('AES-GCM', key, 'Secret Message');
|
|
221
|
+
console.log(await encrypted.toBase64());
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Stream Utilities
|
|
225
|
+
|
|
226
|
+
Bridge the gap between Node.js Streams, Web Streams, and Iterables.
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
import { readBinaryStream, toBytesStream } from '@tstdl/base/utils';
|
|
230
|
+
|
|
231
|
+
// Read a Web ReadableStream into a single Uint8Array
|
|
232
|
+
const response = await fetch('https://example.com/image.png');
|
|
233
|
+
const buffer = await readBinaryStream(response.body!);
|
|
234
|
+
|
|
235
|
+
// Convert a stream of arbitrary chunks to a byte stream
|
|
236
|
+
const byteStream = toBytesStream(someObjectStream);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## 📚 API
|
|
240
|
+
|
|
241
|
+
### Async Iterable Helpers
|
|
242
|
+
|
|
243
|
+
| Function | Description |
|
|
244
|
+
| :---------------------------------- | :-------------------------------------------------------- |
|
|
245
|
+
| `mapAsync` | Transform items asynchronously. |
|
|
246
|
+
| `filterAsync` | Filter items asynchronously. |
|
|
247
|
+
| `reduceAsync` | Reduce items to a single value. |
|
|
248
|
+
| `batchAsync` | Group items into arrays of a specific size. |
|
|
249
|
+
| `bufferAsync` | Buffer items to handle backpressure or bursts. |
|
|
250
|
+
| `takeAsync` / `skipAsync` | Take or skip a specific number of items. |
|
|
251
|
+
| `takeWhileAsync` / `takeUntilAsync` | Take items based on a predicate or signal. |
|
|
252
|
+
| `distinctAsync` | Filter out duplicate items. |
|
|
253
|
+
| `groupAsync` | Group items by a key selector. |
|
|
254
|
+
| `toArrayAsync` | Consume the iterable and return an array. |
|
|
255
|
+
| `firstAsync` / `lastAsync` | Get the first or last item. |
|
|
256
|
+
| `drainAsync` | Consume the iterable completely without returning values. |
|
|
257
|
+
|
|
258
|
+
### Parallel Processing
|
|
259
|
+
|
|
260
|
+
| Function | Description |
|
|
261
|
+
| :---------------- | :------------------------------------------------- |
|
|
262
|
+
| `parallelMap` | Map items with specified concurrency. |
|
|
263
|
+
| `parallelFilter` | Filter items with specified concurrency. |
|
|
264
|
+
| `parallelForEach` | Execute a function for each item with concurrency. |
|
|
265
|
+
| `parallelGroup` | Group items with concurrency. |
|
|
266
|
+
|
|
267
|
+
### Object Utilities
|
|
268
|
+
|
|
269
|
+
| Function | Description |
|
|
270
|
+
| :------------------------------ | :--------------------------------------------------------------- |
|
|
271
|
+
| `mergeDeep` | Deeply merge objects with array handling options. |
|
|
272
|
+
| `decycle` / `recycle` | Handle circular references for serialization. |
|
|
273
|
+
| `pick` / `omit` | Create new objects with selected/omitted keys. |
|
|
274
|
+
| `mapObject` / `mapObjectValues` | Map keys or values of an object. |
|
|
275
|
+
| `filterObject` | Filter object properties by value or key. |
|
|
276
|
+
| `getPropertyNameProxy` | Type-safe way to get property paths (e.g., `user.profile.name`). |
|
|
277
|
+
| `lazyProperty` | Define a property that initializes on first access. |
|
|
278
|
+
|
|
279
|
+
### Type Guards & Assertions
|
|
280
|
+
|
|
281
|
+
| Function | Description |
|
|
282
|
+
| :---------------------------- | :------------------------------------------------------ |
|
|
283
|
+
| `isDefined` / `assertDefined` | Check for `null` or `undefined`. |
|
|
284
|
+
| `isString` / `assertString` | Check for string type. |
|
|
285
|
+
| `isNumber` / `assertNumber` | Check for number type (excluding NaN). |
|
|
286
|
+
| `isArray` / `assertArray` | Check for array. |
|
|
287
|
+
| `isObject` / `assertObject` | Check for non-null object. |
|
|
288
|
+
| `assert` | Generic assertion function. |
|
|
289
|
+
| `assert*Pass` | Assert and return the value (e.g., `assertStringPass`). |
|
|
290
|
+
|
|
291
|
+
### Timing & Async
|
|
292
|
+
|
|
293
|
+
| Function | Description |
|
|
294
|
+
| :------------------ | :---------------------------------------- |
|
|
295
|
+
| `timeout` | Promise-based delay. |
|
|
296
|
+
| `cancelableTimeout` | Delay that can be cancelled via a signal. |
|
|
297
|
+
| `withTimeout` | Race a promise against a timeout. |
|
|
298
|
+
| `backoffLoop` | Run a loop with backoff logic. |
|
|
299
|
+
| `retryWithBackoff` | Higher-order retry utility with backoff. |
|
|
300
|
+
| `asyncHook` | Create an async event hook registry. |
|
|
301
|
+
| `Timer` | High-resolution timer for benchmarking. |
|
|
302
|
+
|
|
303
|
+
### Cryptography & Encoding
|
|
304
|
+
|
|
305
|
+
| Function | Description |
|
|
306
|
+
| :------------------------------ | :-------------------------------------- |
|
|
307
|
+
| `encrypt` / `decrypt` | AES encryption wrappers. |
|
|
308
|
+
| `digest` | SHA hashing wrapper. |
|
|
309
|
+
| `sign` / `verify` | Digital signatures (HMAC, ECDSA, etc.). |
|
|
310
|
+
| `encodeBase64` / `decodeBase64` | Base64 encoding utilities. |
|
|
311
|
+
| `encodeHex` / `decodeHex` | Hex encoding utilities. |
|
|
312
|
+
| `encodeUtf8` / `decodeText` | String/Buffer encoding utilities. |
|
|
313
|
+
|
|
314
|
+
### Miscellaneous
|
|
315
|
+
|
|
316
|
+
| Function | Description |
|
|
317
|
+
| :------------------------------- | :---------------------------------------- |
|
|
318
|
+
| `randomString` / `randomInt` | Random value generation. |
|
|
319
|
+
| `formatDuration` / `formatBytes` | Formatting utilities. |
|
|
320
|
+
| `tryIgnore` | Try a function, return fallback on error. |
|
|
321
|
+
| `buildUrl` | Construct URLs with query parameters. |
|
|
322
|
+
| `memoize` | Function memoization. |
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Observable } from 'rxjs';
|
|
2
2
|
export declare function observableAsyncIterable<T>(observable: Observable<T>): AsyncIterableIterator<T>;
|
|
@@ -1,30 +1,26 @@
|
|
|
1
|
+
import { firstValueFrom, merge } from 'rxjs';
|
|
1
2
|
import { CancellationToken } from '../../cancellation/token.js';
|
|
2
|
-
import { merge, take } from 'rxjs';
|
|
3
3
|
import { ObservableArray } from '../../collections/observable/observable-array.js';
|
|
4
4
|
export async function* observableAsyncIterable(observable) {
|
|
5
5
|
const buffer = new ObservableArray();
|
|
6
6
|
const completeToken = new CancellationToken();
|
|
7
7
|
const errorToken = new CancellationToken();
|
|
8
|
-
let error;
|
|
9
8
|
const subscription = observable.subscribe({
|
|
10
9
|
next: (value) => buffer.add(value),
|
|
11
10
|
complete: () => completeToken.set(),
|
|
12
|
-
error: (
|
|
13
|
-
error = _error;
|
|
14
|
-
errorToken.set();
|
|
15
|
-
}
|
|
11
|
+
error: (error) => errorToken.set(error),
|
|
16
12
|
});
|
|
17
13
|
try {
|
|
18
14
|
while (buffer.length > 0 || !completeToken.isSet) {
|
|
19
15
|
if (buffer.length == 0) {
|
|
20
|
-
await merge(buffer.add$, completeToken
|
|
16
|
+
await firstValueFrom(merge(buffer.add$, completeToken, errorToken));
|
|
21
17
|
}
|
|
22
18
|
while (buffer.length > 0) {
|
|
23
19
|
const item = buffer.removeFirst();
|
|
24
20
|
yield item;
|
|
25
21
|
}
|
|
26
22
|
if (errorToken.isSet) {
|
|
27
|
-
throw
|
|
23
|
+
throw errorToken.reason;
|
|
28
24
|
}
|
|
29
25
|
}
|
|
30
26
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { firstValueFrom, map, race } from 'rxjs';
|
|
1
|
+
import { firstValueFrom, from, map, race } from 'rxjs';
|
|
2
2
|
import { isAsyncIterable } from './is-async-iterable.js';
|
|
3
3
|
export function takeUntilAsync(iterable, cancellationSignal) {
|
|
4
4
|
return isAsyncIterable(iterable)
|
|
@@ -20,7 +20,7 @@ async function* sync(iterable, cancellationSignal) {
|
|
|
20
20
|
}
|
|
21
21
|
async function* async(iterable, cancellationSignal) {
|
|
22
22
|
const iterator = iterable[Symbol.asyncIterator]();
|
|
23
|
-
const cancel$ = cancellationSignal.
|
|
23
|
+
const cancel$ = from(cancellationSignal).pipe(map(() => ({ done: true, value: undefined })));
|
|
24
24
|
try {
|
|
25
25
|
while (true) {
|
|
26
26
|
const result = await firstValueFrom(race([iterator.next(), cancel$]));
|
|
@@ -31,10 +31,10 @@ async function* async(iterable, cancellationSignal) {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
catch (error) {
|
|
34
|
-
(iterator.throw ?? iterator.return)?.(error);
|
|
34
|
+
await (iterator.throw ?? iterator.return)?.(error);
|
|
35
35
|
}
|
|
36
36
|
finally {
|
|
37
|
-
iterator.return?.();
|
|
37
|
+
await iterator.return?.();
|
|
38
38
|
}
|
|
39
39
|
return undefined;
|
|
40
40
|
}
|
package/utils/backoff.js
CHANGED
|
@@ -1,3 +1,55 @@
|
|
|
1
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
|
+
if (value !== null && value !== void 0) {
|
|
3
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
4
|
+
var dispose, inner;
|
|
5
|
+
if (async) {
|
|
6
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
7
|
+
dispose = value[Symbol.asyncDispose];
|
|
8
|
+
}
|
|
9
|
+
if (dispose === void 0) {
|
|
10
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.dispose];
|
|
12
|
+
if (async) inner = dispose;
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
15
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
16
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
17
|
+
}
|
|
18
|
+
else if (async) {
|
|
19
|
+
env.stack.push({ async: true });
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
24
|
+
return function (env) {
|
|
25
|
+
function fail(e) {
|
|
26
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
27
|
+
env.hasError = true;
|
|
28
|
+
}
|
|
29
|
+
var r, s = 0;
|
|
30
|
+
function next() {
|
|
31
|
+
while (r = env.stack.pop()) {
|
|
32
|
+
try {
|
|
33
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
34
|
+
if (r.dispose) {
|
|
35
|
+
var result = r.dispose.call(r.value);
|
|
36
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
37
|
+
}
|
|
38
|
+
else s |= 1;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
fail(e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
45
|
+
if (env.hasError) throw env.error;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
50
|
+
var e = new Error(message);
|
|
51
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
|
+
});
|
|
1
53
|
import { CancellationToken } from '../cancellation/token.js';
|
|
2
54
|
import { NEVER } from 'rxjs';
|
|
3
55
|
import { randomFloat } from './math.js';
|
|
@@ -109,37 +161,44 @@ export async function autoBackoffLoop(loopFunction, options = {}) {
|
|
|
109
161
|
* @param options Additional options for backoff configuration and cancellation.
|
|
110
162
|
*/
|
|
111
163
|
export async function backoffLoop(loopFunction, options = {}) {
|
|
112
|
-
const {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
164
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
165
|
+
try {
|
|
166
|
+
const { cancellationSignal, ...backoffOptions } = options;
|
|
167
|
+
const backoffHelper = new BackoffHelper(backoffOptions);
|
|
168
|
+
const loopToken = __addDisposableResource(env_1, new CancellationToken(cancellationSignal), false);
|
|
169
|
+
let shouldBackoff = false;
|
|
170
|
+
let customBackoffDelay;
|
|
171
|
+
const controller = {
|
|
172
|
+
backoff: (delay) => {
|
|
173
|
+
shouldBackoff = true;
|
|
174
|
+
customBackoffDelay = delay;
|
|
175
|
+
},
|
|
176
|
+
break: () => loopToken.set(),
|
|
177
|
+
};
|
|
178
|
+
while (loopToken.isUnset) {
|
|
179
|
+
await loopFunction(controller, loopToken, backoffHelper.status);
|
|
180
|
+
// Exit immediately if the loop function requested a break.
|
|
181
|
+
if (loopToken.isSet) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (shouldBackoff) {
|
|
185
|
+
shouldBackoff = false;
|
|
186
|
+
const delay = customBackoffDelay ?? backoffHelper.getNextDelay();
|
|
187
|
+
await cancelableTimeout(delay, loopToken);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
// If an iteration completes successfully without a backoff request, reset the delay.
|
|
191
|
+
backoffHelper.reset();
|
|
192
|
+
}
|
|
141
193
|
}
|
|
142
194
|
}
|
|
195
|
+
catch (e_1) {
|
|
196
|
+
env_1.error = e_1;
|
|
197
|
+
env_1.hasError = true;
|
|
198
|
+
}
|
|
199
|
+
finally {
|
|
200
|
+
__disposeResources(env_1);
|
|
201
|
+
}
|
|
143
202
|
}
|
|
144
203
|
/**
|
|
145
204
|
* Creates an async generator that yields a callback to trigger a backoff.
|
|
@@ -182,7 +241,7 @@ export async function* backoffGenerator(options = {}) {
|
|
|
182
241
|
const continueToken = backoffOptions?.continueToken;
|
|
183
242
|
if (isDefined(continueToken)) {
|
|
184
243
|
timeoutSignal = isDefined(cancellationSignal)
|
|
185
|
-
? cancellationSignal.
|
|
244
|
+
? cancellationSignal.inherit(continueToken)
|
|
186
245
|
: continueToken;
|
|
187
246
|
}
|
|
188
247
|
};
|
package/utils/file-reader.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { DetailsError } from '../errors/details.error.js';
|
|
2
1
|
export async function readAsText(blob, encoding) {
|
|
3
2
|
return await setup((reader) => reader.readAsText(blob, encoding));
|
|
4
3
|
}
|
|
@@ -12,7 +11,7 @@ async function setup(dispatcher) {
|
|
|
12
11
|
return await new Promise((resolve, reject) => {
|
|
13
12
|
const reader = new FileReader();
|
|
14
13
|
reader.onload = () => resolve(reader.result);
|
|
15
|
-
reader.onerror = () => reject(new
|
|
14
|
+
reader.onerror = () => reject(new Error('FileReader error', { cause: reader.error }));
|
|
16
15
|
dispatcher(reader);
|
|
17
16
|
});
|
|
18
17
|
}
|
|
@@ -15,7 +15,7 @@ export async function retryWithBackoff(func, options = {}) {
|
|
|
15
15
|
await backoffLoop(async (controller, loopSignal, status) => {
|
|
16
16
|
try {
|
|
17
17
|
const attemptSignal = isDefined(signal)
|
|
18
|
-
? signal.
|
|
18
|
+
? signal.inherit(loopSignal)
|
|
19
19
|
: loopSignal;
|
|
20
20
|
if (isDefined(timeout)) {
|
|
21
21
|
result = await withTimeout(timeout, async () => await func(attemptSignal));
|
package/utils/timer.d.ts
CHANGED
package/utils/timer.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isDefined, isUndefined } from './type-guards.js';
|
|
1
2
|
const nsPerSec = 1e9;
|
|
2
3
|
const nsPerMs = 1e6;
|
|
3
4
|
const nsPerUs = 1e3;
|
|
@@ -41,12 +42,10 @@ export class Timer {
|
|
|
41
42
|
return timer.milliseconds;
|
|
42
43
|
}
|
|
43
44
|
start() {
|
|
44
|
-
|
|
45
|
-
this.begin = getBegin();
|
|
46
|
-
}
|
|
45
|
+
this.begin ??= getBegin();
|
|
47
46
|
}
|
|
48
47
|
stop() {
|
|
49
|
-
if (this.begin
|
|
48
|
+
if (isDefined(this.begin)) {
|
|
50
49
|
const nanoseconds = this.read();
|
|
51
50
|
this.elapsedNanoseconds += nanoseconds;
|
|
52
51
|
this.begin = undefined;
|
|
@@ -61,8 +60,7 @@ export class Timer {
|
|
|
61
60
|
this.elapsedNanoseconds = 0;
|
|
62
61
|
}
|
|
63
62
|
get nanoseconds() {
|
|
64
|
-
|
|
65
|
-
return result;
|
|
63
|
+
return this.elapsedNanoseconds + this.read();
|
|
66
64
|
}
|
|
67
65
|
get microseconds() {
|
|
68
66
|
return this.nanoseconds / nsPerUs;
|
|
@@ -74,7 +72,7 @@ export class Timer {
|
|
|
74
72
|
return this.nanoseconds / nsPerSec;
|
|
75
73
|
}
|
|
76
74
|
read() {
|
|
77
|
-
if (this.begin
|
|
75
|
+
if (isUndefined(this.begin)) {
|
|
78
76
|
return 0;
|
|
79
77
|
}
|
|
80
78
|
const result = getDuration(this.begin);
|
package/utils/timing.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Observable } from 'rxjs';
|
|
2
|
-
import { CancellationSignal } from '../cancellation/token.js';
|
|
2
|
+
import type { CancellationSignal } from '../cancellation/token.js';
|
|
3
3
|
import { type ValueOrProvider } from './value-or-provider.js';
|
|
4
4
|
/** Timeout for specified duration */
|
|
5
5
|
export declare function timeout(milliseconds?: number, options?: {
|
package/utils/timing.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { firstValueFrom, map, race, timer } from 'rxjs';
|
|
2
|
-
import { CancellationSignal } from '../cancellation/token.js';
|
|
1
|
+
import { firstValueFrom, from, map, race, timer } from 'rxjs';
|
|
3
2
|
import { TimeoutError } from '../errors/timeout.error.js';
|
|
4
3
|
import { _throw } from './throw.js';
|
|
5
4
|
import { resolveValueOrProvider } from './value-or-provider.js';
|
|
@@ -21,10 +20,9 @@ export async function timeoutUntil(timestamp) {
|
|
|
21
20
|
}
|
|
22
21
|
/** Timeout for specified duration */
|
|
23
22
|
export async function cancelableTimeout(milliseconds, cancelSignal) {
|
|
24
|
-
const observable = (cancelSignal instanceof CancellationSignal) ? cancelSignal.set$ : cancelSignal;
|
|
25
23
|
return await firstValueFrom(race([
|
|
26
24
|
timer(milliseconds).pipe(map(() => 'timeout')),
|
|
27
|
-
|
|
25
|
+
from(cancelSignal).pipe(map(() => 'canceled')),
|
|
28
26
|
]));
|
|
29
27
|
}
|
|
30
28
|
/** Timeout until specified time */
|
package/utils/z-base32.d.ts
CHANGED
package/utils/z-base32.js
CHANGED