@tstdl/base 0.93.138 → 0.93.140
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.js +2 -2
- package/audit/README.md +267 -0
- 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/tests/authentication.client-error-handling.test.js +2 -1
- package/authentication/tests/authentication.client-service-refresh.test.js +5 -3
- 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.d.ts +1 -0
- package/cancellation/tests/leak.test.js +35 -0
- 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 -201
- 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/services/document-management.service.js +9 -7
- 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.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/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/injector/injector.d.ts +1 -0
- package/injector/injector.js +17 -5
- package/injector/tests/leak.test.d.ts +1 -0
- package/injector/tests/leak.test.js +45 -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/lock/README.md +249 -0
- package/lock/web/web-lock.js +119 -47
- package/logger/README.md +287 -0
- package/mail/README.md +256 -0
- 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 +1 -1
- 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/package.json +8 -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/reflection/README.md +305 -0
- 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 +300 -0
- package/task-queue/postgres/task-queue.d.ts +2 -1
- package/task-queue/postgres/task-queue.js +32 -2
- package/task-queue/task-context.js +1 -1
- package/task-queue/task-queue.d.ts +17 -0
- package/task-queue/task-queue.js +103 -44
- package/task-queue/tests/complex.test.js +4 -4
- package/task-queue/tests/dependencies.test.js +4 -2
- package/task-queue/tests/queue.test.js +111 -0
- package/task-queue/tests/worker.test.js +21 -13
- package/templates/README.md +287 -0
- package/testing/README.md +157 -0
- package/text/README.md +346 -0
- 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/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/errors/README.md
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# Errors
|
|
2
|
+
|
|
3
|
+
A comprehensive collection of custom, robust, and extensible error classes for TypeScript applications. It provides a standardized hierarchy for common failure scenarios, performance optimizations, and built-in localization support.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [✨ Features](#-features)
|
|
8
|
+
- [Core Concepts](#core-concepts)
|
|
9
|
+
- [The CustomError Base](#the-customerror-base)
|
|
10
|
+
- [Standardized Error Types](#standardized-error-types)
|
|
11
|
+
- [Localization](#localization)
|
|
12
|
+
- [🚀 Basic Usage](#-basic-usage)
|
|
13
|
+
- [🔧 Advanced Topics](#-advanced-topics)
|
|
14
|
+
- [Creating Custom Errors](#creating-custom-errors)
|
|
15
|
+
- [Attaching Details to Errors](#attaching-details-to-errors)
|
|
16
|
+
- [Aggregating Multiple Errors](#aggregating-multiple-errors)
|
|
17
|
+
- [Formatting and Serialization](#formatting-and-serialization)
|
|
18
|
+
- [Performance Optimization (Fast Mode)](#performance-optimization-fast-mode)
|
|
19
|
+
- [Unwrapping Errors](#unwrapping-errors)
|
|
20
|
+
- [📚 API](#-api)
|
|
21
|
+
|
|
22
|
+
## ✨ Features
|
|
23
|
+
|
|
24
|
+
- **Extensible Base Class:** `CustomError` simplifies creating domain-specific errors with support for causes and stack trace management.
|
|
25
|
+
- **Standard Library:** Includes a rich set of pre-defined errors for HTTP status codes (400, 401, 403, 404, etc.) and common application failures.
|
|
26
|
+
- **Type Safety:** Designed for use with `instanceof` checks to allow precise error handling logic.
|
|
27
|
+
- **Contextual Data:** `DetailsError` allows attaching arbitrary structured data to an error.
|
|
28
|
+
- **Error Aggregation:** `MultiError` groups multiple errors into a single throwable object.
|
|
29
|
+
- **Performance Mode:** Optional `fast` mode skips stack trace generation for high-throughput scenarios.
|
|
30
|
+
- **Localization:** Built-in English and German localization maps for user-facing error messages.
|
|
31
|
+
|
|
32
|
+
## Core Concepts
|
|
33
|
+
|
|
34
|
+
### The CustomError Base
|
|
35
|
+
|
|
36
|
+
All errors in this module inherit from `CustomError`. This abstract class extends the native JavaScript `Error` and adds:
|
|
37
|
+
|
|
38
|
+
- **Static `errorName`**: A reliable identifier for the error type, safe against code minification.
|
|
39
|
+
- **Options Object**: A constructor argument to set the message, cause, stack, and performance flags.
|
|
40
|
+
|
|
41
|
+
### Standardized Error Types
|
|
42
|
+
|
|
43
|
+
The module provides specific classes for common scenarios, such as:
|
|
44
|
+
|
|
45
|
+
- **Authentication/Authorization**: `UnauthorizedError`, `ForbiddenError`, `InvalidCredentialsError`, `InvalidTokenError`.
|
|
46
|
+
- **Request Handling**: `BadRequestError`, `NotFoundError`, `MethodNotAllowedError`, `UnsupportedMediaTypeError`, `MaxBytesExceededError`.
|
|
47
|
+
- **System State**: `NotImplementedError`, `NotSupportedError`, `TimeoutError`, `AssertionError`.
|
|
48
|
+
|
|
49
|
+
### Localization
|
|
50
|
+
|
|
51
|
+
The module exports `englishTstdlErrorsLocalization` and `germanTstdlErrorsLocalization`. These objects map error classes to user-friendly headers and messages, making it easier to display errors in a UI without hardcoding strings.
|
|
52
|
+
|
|
53
|
+
## 🚀 Basic Usage
|
|
54
|
+
|
|
55
|
+
The most common use case is throwing standard errors to interrupt control flow when a specific condition is not met.
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { NotFoundError, ForbiddenError } from '@tstdl/base/errors';
|
|
59
|
+
|
|
60
|
+
interface User {
|
|
61
|
+
id: string;
|
|
62
|
+
isAdmin: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getUser(id: string, currentUser: User): User {
|
|
66
|
+
// Simulate a database lookup
|
|
67
|
+
const user = database.find(id);
|
|
68
|
+
|
|
69
|
+
if (!user) {
|
|
70
|
+
throw new NotFoundError(`User with ID "${id}" was not found.`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!currentUser.isAdmin && currentUser.id !== user.id) {
|
|
74
|
+
throw new ForbiddenError('You do not have permission to view this user.');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return user;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Mock database for the example
|
|
81
|
+
const database = {
|
|
82
|
+
find: (id: string) => (id === '123' ? { id: '123', isAdmin: false } : null),
|
|
83
|
+
};
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Handling errors using `instanceof` ensures you only catch and process specific types.
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import { NotFoundError, ForbiddenError } from '@tstdl/base/errors';
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
getUser('999', { id: '123', isAdmin: false });
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (error instanceof NotFoundError) {
|
|
95
|
+
console.error('Resource missing:', error.message);
|
|
96
|
+
} else if (error instanceof ForbiddenError) {
|
|
97
|
+
console.error('Access denied:', error.message);
|
|
98
|
+
} else {
|
|
99
|
+
console.error('Unexpected error:', error);
|
|
100
|
+
throw error; // Re-throw unknown errors
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## 🔧 Advanced Topics
|
|
106
|
+
|
|
107
|
+
### Creating Custom Errors
|
|
108
|
+
|
|
109
|
+
Extend `CustomError` to define domain-specific errors. Define a static `errorName` to ensure the error can be reliably identified even if class names are mangled during build processes.
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { CustomError } from '@tstdl/base/errors';
|
|
113
|
+
|
|
114
|
+
export class InsufficientFundsError extends CustomError {
|
|
115
|
+
static readonly errorName = 'InsufficientFundsError';
|
|
116
|
+
|
|
117
|
+
constructor(accountId: string, required: number, available: number) {
|
|
118
|
+
super({
|
|
119
|
+
message: `Account ${accountId} has insufficient funds. Required: ${required}, Available: ${available}.`,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Usage
|
|
125
|
+
throw new InsufficientFundsError('ACC-123', 100.0, 50.0);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Attaching Details to Errors
|
|
129
|
+
|
|
130
|
+
Use `DetailsError` when you need to pass structured data along with the error message. This is useful for validation errors or passing context to an error handler.
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
import { DetailsError } from '@tstdl/base/errors';
|
|
134
|
+
|
|
135
|
+
type ValidationFailure = {
|
|
136
|
+
field: string;
|
|
137
|
+
reason: string;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
function validateProfile(profile: any): void {
|
|
141
|
+
const failures: ValidationFailure[] = [];
|
|
142
|
+
|
|
143
|
+
if (!profile.username) {
|
|
144
|
+
failures.push({ field: 'username', reason: 'Required' });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (failures.length > 0) {
|
|
148
|
+
throw new DetailsError('Validation failed', failures);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
validateProfile({});
|
|
154
|
+
} catch (error) {
|
|
155
|
+
if (error instanceof DetailsError) {
|
|
156
|
+
console.log('Validation details:', error.details);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Aggregating Multiple Errors
|
|
162
|
+
|
|
163
|
+
`MultiError` is useful when an operation performs multiple independent tasks (like processing a batch of files) and you want to report all failures at the end.
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
import { MultiError } from '@tstdl/base/errors';
|
|
167
|
+
|
|
168
|
+
function processItems(items: string[]): void {
|
|
169
|
+
const errors: Error[] = [];
|
|
170
|
+
|
|
171
|
+
for (const item of items) {
|
|
172
|
+
try {
|
|
173
|
+
// potentially failing operation
|
|
174
|
+
if (item === 'fail') throw new Error('Failed item');
|
|
175
|
+
} catch (error) {
|
|
176
|
+
if (error instanceof Error) {
|
|
177
|
+
errors.push(error);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (errors.length > 0) {
|
|
183
|
+
throw new MultiError(errors, `Processed items with ${errors.length} errors.`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Formatting and Serialization
|
|
189
|
+
|
|
190
|
+
When logging errors or sending them over the wire (e.g., in a REST API response), you often need a structured or stringified representation. The `serializeError` and `formatError` functions handle this, including support for nested causes and `MultiError` aggregation.
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
import { formatError, serializeError, ForbiddenError } from '@tstdl/base/errors';
|
|
194
|
+
|
|
195
|
+
const error = new ForbiddenError('Access Denied', { cause: new Error('Permission bit 0 not set') });
|
|
196
|
+
|
|
197
|
+
// Get a structured JSON-cloneable object
|
|
198
|
+
const serialized = serializeError(error);
|
|
199
|
+
|
|
200
|
+
// Get a formatted string with indentation and causes
|
|
201
|
+
const formatted = formatError(error, { includeStack: true });
|
|
202
|
+
console.log(formatted);
|
|
203
|
+
/*
|
|
204
|
+
ForbiddenError: Access Denied
|
|
205
|
+
Caused by:
|
|
206
|
+
Error: Permission bit 0 not set
|
|
207
|
+
at Object.<anonymous> ...
|
|
208
|
+
*/
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Performance Optimization (Fast Mode)
|
|
212
|
+
|
|
213
|
+
Generating a stack trace is an expensive operation in JavaScript. If you are using exceptions for control flow in a high-performance loop (e.g., a parser) and do not need the stack trace, you can use the `fast: true` option.
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
import { CustomError } from '@tstdl/base/errors';
|
|
217
|
+
|
|
218
|
+
class ParserError extends CustomError {
|
|
219
|
+
static readonly errorName = 'ParserError';
|
|
220
|
+
|
|
221
|
+
constructor(message: string) {
|
|
222
|
+
// fast: true skips super() call to Error, avoiding stack trace generation
|
|
223
|
+
super({ message, fast: true });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Unwrapping Errors
|
|
229
|
+
|
|
230
|
+
Sometimes errors are wrapped in other objects or generic error containers. `unwrapError` helps extract the original underlying error.
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
import { unwrapError } from '@tstdl/base/errors';
|
|
234
|
+
|
|
235
|
+
const originalError = new Error('Database connection failed');
|
|
236
|
+
const wrappedError = { reason: originalError };
|
|
237
|
+
|
|
238
|
+
const error = unwrapError(wrappedError);
|
|
239
|
+
console.log(error.message); // "Database connection failed"
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## 📚 API
|
|
243
|
+
|
|
244
|
+
| Class / Function | Description |
|
|
245
|
+
| :------------------------------- | :-------------------------------------------------------------------------------------------------------------- |
|
|
246
|
+
| `CustomError` | Abstract base class for all errors. Accepts `CustomErrorOptions` (`name`, `message`, `cause`, `stack`, `fast`). |
|
|
247
|
+
| `ApiError` | Wraps an `ErrorResponse` from an API, exposing the `response` and `details`. |
|
|
248
|
+
| `AssertionError` | Indicates a failed assertion check. |
|
|
249
|
+
| `BadRequestError` | Indicates a client-side error (HTTP 400). Default message: "bad request". |
|
|
250
|
+
| `DetailsError<T>` | An error that carries an additional `details` payload of type `T`. |
|
|
251
|
+
| `ForbiddenError` | Indicates the client is authenticated but lacks permissions (HTTP 403). Default message: "forbidden". |
|
|
252
|
+
| `InvalidCredentialsError` | Indicates authentication failed due to bad credentials. Default message: "Invalid credentials.". |
|
|
253
|
+
| `InvalidTokenError` | Indicates an authentication token is invalid or expired. Default message: "invalid token". |
|
|
254
|
+
| `MaxBytesExceededError` | Indicates a size limit was exceeded. Includes static `fromBytes(bytes)` factory. |
|
|
255
|
+
| `MethodNotAllowedError` | Indicates the HTTP method is not supported for the resource (HTTP 405). |
|
|
256
|
+
| `MultiError` | Aggregates an array of `Error` objects into a single error. |
|
|
257
|
+
| `NotFoundError` | Indicates a resource could not be found (HTTP 404). Default message: "not found". |
|
|
258
|
+
| `NotImplementedError` | Indicates functionality is not yet implemented (HTTP 501). |
|
|
259
|
+
| `NotSupportedError` | Indicates an operation or value is not supported. Includes static `fromEnum(...)` factory. |
|
|
260
|
+
| `TimeoutError` | Indicates an operation exceeded its time limit. |
|
|
261
|
+
| `UnauthorizedError` | Indicates authentication is required (HTTP 401). Default message: "Unauthorized". |
|
|
262
|
+
| `UnsupportedMediaTypeError` | Indicates the payload format is not supported (HTTP 415). |
|
|
263
|
+
| `unwrapError(error)` | Utility function to extract the underlying error from a wrapper object (checks `rejection`, `reason`, `error`). |
|
|
264
|
+
| `serializeError(error, options)` | Serializes an error into a structured JSON-cloneable `SerializedError` object. |
|
|
265
|
+
| `formatError(error, options)` | Formats an error into a human-readable string, including causes and sub-errors. |
|
|
266
|
+
| `englishTstdlErrorsLocalization` | Localization object containing English headers and messages for standard errors. |
|
|
267
|
+
| `germanTstdlErrorsLocalization` | Localization object containing German headers and messages for standard errors. |
|
package/file/README.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# File Module
|
|
2
|
+
|
|
3
|
+
A utility module for robust MIME type detection and server-side temporary file management. It provides tools to identify file types from various sources and a safe, RAII-style wrapper for handling temporary files in Node.js.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [✨ Features](#-features)
|
|
8
|
+
- [Core Concepts](#core-concepts)
|
|
9
|
+
- [🚀 Basic Usage](#-basic-usage)
|
|
10
|
+
- [Detecting MIME Types](#detecting-mime-types)
|
|
11
|
+
- [Getting Extensions](#getting-extensions)
|
|
12
|
+
- [🔧 Advanced Topics](#-advanced-topics)
|
|
13
|
+
- [Managing Temporary Files](#managing-temporary-files)
|
|
14
|
+
- [📚 API](#-api)
|
|
15
|
+
|
|
16
|
+
## ✨ Features
|
|
17
|
+
|
|
18
|
+
- **Robust MIME Detection**: Identify file types by checking magic numbers (binary signatures) rather than just file extensions.
|
|
19
|
+
- **Versatile Input**: Supports detection from file paths, `Uint8Array` buffers, and `ReadableStream`s.
|
|
20
|
+
- **Extension Lookup**: Retrieve valid file extensions for a specific MIME type.
|
|
21
|
+
- **Automatic Cleanup**: Server-side `TemporaryFile` class implements `AsyncDisposable` for automatic file deletion when the scope exits.
|
|
22
|
+
- **Stream Support**: seamless integration with Node.js and Web Streams for writing to and reading from temporary files.
|
|
23
|
+
|
|
24
|
+
## Core Concepts
|
|
25
|
+
|
|
26
|
+
### MIME Type Detection
|
|
27
|
+
|
|
28
|
+
The module uses the `file-type` library under the hood to inspect the binary signature of a file. This is more secure and accurate than relying on filenames. It supports a wide range of formats including images, video, audio, and archives.
|
|
29
|
+
|
|
30
|
+
### Temporary File Management (Server)
|
|
31
|
+
|
|
32
|
+
When processing file uploads or generating reports, you often need to store data on disk temporarily. The `TemporaryFile` class wraps Node.js filesystem operations. It generates a unique path in the OS's temporary directory and ensures the file is deleted when you are done with it, leveraging TypeScript's `using` keyword (Explicit Resource Management).
|
|
33
|
+
|
|
34
|
+
## 🚀 Basic Usage
|
|
35
|
+
|
|
36
|
+
### Detecting MIME Types
|
|
37
|
+
|
|
38
|
+
You can detect the MIME type of a file using a buffer or a stream.
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { getMimeType } from '@tstdl/base/file';
|
|
42
|
+
import { readFile } from 'node:fs/promises';
|
|
43
|
+
|
|
44
|
+
async function checkFiles() {
|
|
45
|
+
// 1. From a Uint8Array / Buffer
|
|
46
|
+
const buffer = await readFile('/path/to/document.pdf');
|
|
47
|
+
const mimeFromBuffer = await getMimeType(buffer);
|
|
48
|
+
console.log(mimeFromBuffer); // 'application/pdf'
|
|
49
|
+
|
|
50
|
+
// 2. With a fallback if detection fails
|
|
51
|
+
const mimeWithFallback = await getMimeType(buffer, 'application/octet-stream');
|
|
52
|
+
console.log(mimeWithFallback);
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
To detect MIME types from a file path (Node.js only), use the server-specific module:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { getMimeTypeFromFile } from '@tstdl/base/file/server';
|
|
60
|
+
|
|
61
|
+
async function checkPath() {
|
|
62
|
+
const mimeFromPath = await getMimeTypeFromFile('/path/to/image.png');
|
|
63
|
+
console.log(mimeFromPath); // 'image/png'
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Getting Extensions
|
|
68
|
+
|
|
69
|
+
Retrieve the list of known extensions for a given MIME type.
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { getMimeTypeExtensions } from '@tstdl/base/file';
|
|
73
|
+
|
|
74
|
+
const extensions = getMimeTypeExtensions('image/jpeg');
|
|
75
|
+
console.log(extensions); // ['jpeg', 'jpg', 'jpe']
|
|
76
|
+
|
|
77
|
+
const pdfExt = getMimeTypeExtensions('application/pdf');
|
|
78
|
+
console.log(pdfExt); // ['pdf']
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## 🔧 Advanced Topics
|
|
82
|
+
|
|
83
|
+
### Managing Temporary Files
|
|
84
|
+
|
|
85
|
+
The `TemporaryFile` class is located in the `server` submodule. It is designed to be used with the `await using` syntax to guarantee cleanup.
|
|
86
|
+
|
|
87
|
+
> **Note:** This feature requires a Node.js environment.
|
|
88
|
+
|
|
89
|
+
#### Basic Usage
|
|
90
|
+
|
|
91
|
+
The most common way to create a temporary file is from a stream, buffer, or string.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { TemporaryFile } from '@tstdl/base/file/server';
|
|
95
|
+
|
|
96
|
+
async function processUpload(uploadStream: ReadableStream<Uint8Array>) {
|
|
97
|
+
// Create a temp file from a stream. The file is created in os.tmpdir() with a random UUID name.
|
|
98
|
+
// Optional: pass an extension as the second argument.
|
|
99
|
+
await using tempFile = await TemporaryFile.from(uploadStream, '.png');
|
|
100
|
+
|
|
101
|
+
console.log(`Processing file at: ${tempFile.path}`);
|
|
102
|
+
console.log(`File size: ${await tempFile.size()} bytes`);
|
|
103
|
+
|
|
104
|
+
// Read content back if needed
|
|
105
|
+
const content = await tempFile.readText();
|
|
106
|
+
|
|
107
|
+
// Perform operations (e.g., virus scan, image processing, parsing)
|
|
108
|
+
await performHeavyProcessing(tempFile.path);
|
|
109
|
+
} // <--- tempFile.delete() is automatically called here!
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### Manual Control and Extensions
|
|
113
|
+
|
|
114
|
+
You can create an empty temporary file and specify an extension.
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
import { TemporaryFile } from '@tstdl/base/file/server';
|
|
118
|
+
|
|
119
|
+
async function generateReport() {
|
|
120
|
+
await using tempFile = TemporaryFile.create('.pdf');
|
|
121
|
+
|
|
122
|
+
await tempFile.write('Report Content');
|
|
123
|
+
// ... do something with tempFile.path
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### Persistent Files
|
|
128
|
+
|
|
129
|
+
If you want to keep the file even after the scope ends (e.g., if you successfully moved it to a final destination), use `keep()` or `moveTo()`.
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { TemporaryFile } from '@tstdl/base/file/server';
|
|
133
|
+
|
|
134
|
+
async function savePermanent(stream: ReadableStream<Uint8Array>, destination: string) {
|
|
135
|
+
await using tempFile = await TemporaryFile.from(stream);
|
|
136
|
+
|
|
137
|
+
// ... validate file ...
|
|
138
|
+
|
|
139
|
+
// Move the file and mark it to be kept (not deleted on disposal)
|
|
140
|
+
await tempFile.moveTo(destination, true);
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### Adopting Existing Files
|
|
145
|
+
|
|
146
|
+
You can wrap an existing file in `TemporaryFile` to ensure it gets deleted when done.
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import { TemporaryFile } from '@tstdl/base/file/server';
|
|
150
|
+
|
|
151
|
+
async function processExisting(path: string) {
|
|
152
|
+
await using tempFile = TemporaryFile.adopt(path);
|
|
153
|
+
// path will be deleted at the end of the scope
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## 📚 API
|
|
158
|
+
|
|
159
|
+
### `@tstdl/base/file`
|
|
160
|
+
|
|
161
|
+
| Export | Type | Description |
|
|
162
|
+
| :---------------------- | :------- | :-------------------------------------------------------------------------------------------------------------------- |
|
|
163
|
+
| `getMimeType` | Function | Async function to detect MIME type from `Uint8Array` or `ReadableStream`. Accepts an optional fallback. |
|
|
164
|
+
| `getMimeTypeExtensions` | Function | Returns an array of file extensions associated with a MIME type string. |
|
|
165
|
+
| `mimeTypes` | Object | A dictionary mapping MIME types to arrays of file extensions. |
|
|
166
|
+
| `mimeTypesMap` | Map | A `Map` version of the `mimeTypes` object for efficient lookup. |
|
|
167
|
+
|
|
168
|
+
### `@tstdl/base/file/server`
|
|
169
|
+
|
|
170
|
+
| Export | Type | Description |
|
|
171
|
+
| :--------------------- | :------- | :------------------------------------------------------------ |
|
|
172
|
+
| `getMimeTypeFromFile` | Function | Async function to detect MIME type from a string file path (Node.js only). |
|
|
173
|
+
| `TemporaryFile` | Class | A wrapper for temporary files implementing `AsyncDisposable`. |
|
|
174
|
+
|
|
175
|
+
#### `TemporaryFile` Class
|
|
176
|
+
|
|
177
|
+
| Method/Property | Description |
|
|
178
|
+
| :------------------------ | :----------------------------------------------------------------------------------------------------------- |
|
|
179
|
+
| `path` | Getter for the absolute file path. |
|
|
180
|
+
| `static create(ext?)` | Creates a new empty `TemporaryFile` instance with an optional extension. |
|
|
181
|
+
| `static adopt(path)` | Creates a `TemporaryFile` instance wrapping an existing file path. |
|
|
182
|
+
| `static from(content, ext?)` | Creates a `TemporaryFile` and writes the provided content (string, Uint8Array, or Stream) to it, with an optional extension. |
|
|
183
|
+
| `read()` | Reads the file content as a `Uint8Array`. |
|
|
184
|
+
| `readText()` | Reads the file content as a UTF-8 string. |
|
|
185
|
+
| `readStream()` | Returns a `ReadableStream<Uint8Array>` of the file content. |
|
|
186
|
+
| `write(content)` | Overwrites the file with the provided content. |
|
|
187
|
+
| `moveTo(path, keep?)` | Moves the file to a new location. If `keep` is true, it won't be deleted on disposal. |
|
|
188
|
+
| `keep()` | Prevents the file from being deleted when the object is disposed. |
|
|
189
|
+
| `delete()` | Manually deletes the file. |
|
|
190
|
+
| `size()` | Returns the size of the file in bytes. |
|
|
191
|
+
| `[Symbol.asyncDispose]()` | Automatically calls `delete()` when used with `await using`, unless `keep()` was called. |
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# @tstdl/base/formats
|
|
2
|
+
|
|
3
|
+
A comprehensive utility module for locale-aware formatting of numbers, dates, times, currencies, and names. It leverages the standard `Intl` API with aggressive memoization to ensure high performance in rendering-heavy applications.
|
|
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
|
+
- [📚 API](#-api)
|
|
12
|
+
|
|
13
|
+
## ✨ Features
|
|
14
|
+
|
|
15
|
+
- **Locale-Aware**: Defaults to `de-DE` (German) but fully configurable.
|
|
16
|
+
- **High Performance**: Uses memoization to cache `Intl.NumberFormat` and `Intl.DateTimeFormat` instances, avoiding expensive instantiation costs.
|
|
17
|
+
- **Type Safety**: Fully typed in TypeScript.
|
|
18
|
+
- **Rich Set of Formatters**:
|
|
19
|
+
- Integers, Decimals, Percentages.
|
|
20
|
+
- Currencies (with and without cents, specific Euro helpers).
|
|
21
|
+
- Dates (Short, Medium, Long, Numeric) and Times.
|
|
22
|
+
- Person Names (First/Last name handling).
|
|
23
|
+
- **Numeric Date Support**: Utilities to format dates stored as integers (e.g., `20231231`).
|
|
24
|
+
|
|
25
|
+
## Core Concepts
|
|
26
|
+
|
|
27
|
+
### Global Configuration
|
|
28
|
+
|
|
29
|
+
The module operates with a global locale setting (defaulting to `de-DE`). This is designed for applications that typically run in a single locale context (like a server-side renderer or a client-side SPA). You can configure this once at startup.
|
|
30
|
+
|
|
31
|
+
### Memoization
|
|
32
|
+
|
|
33
|
+
Creating `Intl.NumberFormat` or `Intl.DateTimeFormat` instances can be slow. This module creates these formatters once per locale/option combination and reuses them. This makes functions like `formatDecimal` or `formatDate` safe to use inside tight loops or render functions.
|
|
34
|
+
|
|
35
|
+
## 🚀 Basic Usage
|
|
36
|
+
|
|
37
|
+
### Configuration
|
|
38
|
+
|
|
39
|
+
Set the locale at the entry point of your application.
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { configureFormats } from '@tstdl/base/formats';
|
|
43
|
+
|
|
44
|
+
// Set to US English
|
|
45
|
+
configureFormats({ locale: 'en-US' });
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Number Formatting
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { formatInteger, formatDecimal, formatPercent } from '@tstdl/base/formats';
|
|
52
|
+
|
|
53
|
+
// Assuming locale is de-DE
|
|
54
|
+
console.log(formatInteger(12345.67));
|
|
55
|
+
// Output: "12.346" (rounded)
|
|
56
|
+
|
|
57
|
+
console.log(formatDecimal(12345.6789));
|
|
58
|
+
// Output: "12.345,68" (default 2 fraction digits)
|
|
59
|
+
|
|
60
|
+
console.log(formatDecimal(12345.6789, { minimumFractionDigits: 3, maximumFractionDigits: 4 }));
|
|
61
|
+
// Output: "12.345,679" (min 3, max 4 fraction digits)
|
|
62
|
+
|
|
63
|
+
console.log(formatPercent(0.1234));
|
|
64
|
+
// Output: "12,34 %"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Currency Formatting
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { formatEuro, formatCurrency, formatCurrencyWithoutCents } from '@tstdl/base/formats';
|
|
71
|
+
|
|
72
|
+
console.log(formatEuro(1500.5));
|
|
73
|
+
// Output: "1.500,50 €"
|
|
74
|
+
|
|
75
|
+
console.log(formatCurrency(1500.5, 'USD'));
|
|
76
|
+
// Output: "1.500,50 $" (Format depends on locale, symbol depends on currency)
|
|
77
|
+
|
|
78
|
+
console.log(formatCurrencyWithoutCents(1500.5, 'EUR'));
|
|
79
|
+
// Output: "1.501 €" (Rounded)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Date and Time Formatting
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { formatDate, formatDateShort, formatTimeShort } from '@tstdl/base/formats';
|
|
86
|
+
|
|
87
|
+
const date = new Date('2023-10-05T14:30:00');
|
|
88
|
+
|
|
89
|
+
console.log(formatDate(date));
|
|
90
|
+
// Output: "05.10.2023" (Standard date format for de-DE)
|
|
91
|
+
|
|
92
|
+
console.log(formatDateShort(date));
|
|
93
|
+
// Output: "05.10.2023"
|
|
94
|
+
|
|
95
|
+
// formatTimeShort expects milliseconds for the time portion
|
|
96
|
+
// (Internally often used with durations or specific times of day)
|
|
97
|
+
console.log(formatTimeShort(date));
|
|
98
|
+
// Output: "14:30"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## 🔧 Advanced Topics
|
|
102
|
+
|
|
103
|
+
### Formatting Person Names
|
|
104
|
+
|
|
105
|
+
The `formatPersonName` helper handles objects with `firstName` and `lastName` properties, dealing with nulls and formatting preferences.
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { formatPersonName } from '@tstdl/base/formats';
|
|
109
|
+
|
|
110
|
+
const user = { firstName: 'John', lastName: 'Doe' };
|
|
111
|
+
const incompleteUser = { firstName: null, lastName: 'Doe' };
|
|
112
|
+
|
|
113
|
+
console.log(formatPersonName(user));
|
|
114
|
+
// Output: "John Doe"
|
|
115
|
+
|
|
116
|
+
console.log(formatPersonName(user, { lastNameFirst: true }));
|
|
117
|
+
// Output: "Doe, John"
|
|
118
|
+
|
|
119
|
+
console.log(formatPersonName(incompleteUser));
|
|
120
|
+
// Output: "Doe"
|
|
121
|
+
|
|
122
|
+
console.log(formatPersonName(null, { fallback: 'Anonymous' }));
|
|
123
|
+
// Output: "Anonymous"
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Numeric Dates
|
|
127
|
+
|
|
128
|
+
Often databases store dates as integers (e.g., `20231231`) for indexing efficiency. The `formatNumericDate` function handles this conversion automatically.
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import { formatNumericDate } from '@tstdl/base/formats';
|
|
132
|
+
|
|
133
|
+
// Formats integer 20231231 as a date
|
|
134
|
+
console.log(formatNumericDate(20231231));
|
|
135
|
+
// Output: "31.12.2023"
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Custom Number Formatting
|
|
139
|
+
|
|
140
|
+
You can pass standard `Intl.NumberFormatOptions` to `formatNumber` for one-off requirements that aren't covered by the standard helpers.
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
import { formatNumber } from '@tstdl/base/formats';
|
|
144
|
+
|
|
145
|
+
console.log(
|
|
146
|
+
formatNumber(1234.567, {
|
|
147
|
+
style: 'unit',
|
|
148
|
+
unit: 'kilometer',
|
|
149
|
+
maximumFractionDigits: 1,
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
// Output: "1.234,6 km"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## 📚 API
|
|
156
|
+
|
|
157
|
+
### Configuration
|
|
158
|
+
|
|
159
|
+
| Function | Description |
|
|
160
|
+
| :-------------------------- | :--------------------------------------------------------------------------------------- |
|
|
161
|
+
| `configureFormats(options)` | Sets the global locale used by all formatters. Default is `de-DE`. Can be a static string or a provider function. |
|
|
162
|
+
|
|
163
|
+
### Number Formatters
|
|
164
|
+
|
|
165
|
+
| Function | Description |
|
|
166
|
+
| :----------------------------- | :-------------------------------------------------------------- |
|
|
167
|
+
| `formatNumber(value, format?)` | Formats a number using optional `Intl.NumberFormatOptions`. |
|
|
168
|
+
| `formatInteger(value, format?)` | Formats as an integer (0 fraction digits). |
|
|
169
|
+
| `formatDecimal(value, format?)` | Formats as a decimal. Default min/max fraction digits is 2. |
|
|
170
|
+
| `formatPercent(value, format?)` | Formats a ratio (0-1) as a percentage (0-100%). |
|
|
171
|
+
|
|
172
|
+
### Currency Formatters
|
|
173
|
+
|
|
174
|
+
| Function | Description |
|
|
175
|
+
| :-------------------------------------------- | :------------------------------------------------------- |
|
|
176
|
+
| `formatCurrency(value, currency, format?)` | Formats value as currency (e.g., 'EUR', 'USD'). |
|
|
177
|
+
| `formatCurrencyWithoutCents(value, currency, format?)` | Formats currency rounded to the nearest integer. |
|
|
178
|
+
| `formatEuro(value, format?)` | Shortcut for `formatCurrency(value, 'EUR')`. |
|
|
179
|
+
| `formatEuroWithoutCents(value, format?)` | Shortcut for `formatCurrencyWithoutCents(value, 'EUR')`. |
|
|
180
|
+
|
|
181
|
+
### Date & Time Formatters
|
|
182
|
+
|
|
183
|
+
| Function | Description |
|
|
184
|
+
| :------------------------------------ | :------------------------------------------------------------------------- |
|
|
185
|
+
| `formatDateTime(value, format?)` | Formats a `Date`, timestamp or `DateTime` object using optional `Intl.DateTimeFormatOptions`. |
|
|
186
|
+
| `formatDate(dateOrTimestamp, format?)` | Formats using `dateShort` options (numeric day, month, 2-digit year). |
|
|
187
|
+
| `formatDateShort(value, format?)` | Alias for `formatDate`. |
|
|
188
|
+
| `formatTimeShort(value, format?)` | Formats a time value to `HH:MM`. |
|
|
189
|
+
| `formatNumericDate(numericDate, options?)` | Converts a `YYYYMMDD` integer to a formatted date string (`dateShort`). |
|
|
190
|
+
| `formatNumericDateShort(numericDate, options?)` | Alias for `formatNumericDate`. |
|
|
191
|
+
| `formatNumericDateLong(numericDate, options?)` | Converts a `YYYYMMDD` integer to a long date format (`dateLong`). |
|
|
192
|
+
|
|
193
|
+
### Utilities
|
|
194
|
+
|
|
195
|
+
| Function | Description |
|
|
196
|
+
| :----------------------------------- | :--------------------------------------------------------------------------------------------------------- |
|
|
197
|
+
| `formatPersonName(person, options?)` | Formats a name from an object `{ firstName?, lastName? }`. Options include `lastNameFirst` and `fallback`. |
|
|
198
|
+
|
|
199
|
+
### Constants (Format Options)
|
|
200
|
+
|
|
201
|
+
The module exports standard `Intl` configuration objects used internally. These can be useful if you need to instantiate your own formatters with the same styles.
|
|
202
|
+
|
|
203
|
+
- `integerFormat`
|
|
204
|
+
- `decimalFormat`
|
|
205
|
+
- `decimal1Format`
|
|
206
|
+
- `dateTimeNumeric`, `dateTimeShort`, `dateTimeLong`
|
|
207
|
+
- `dateShort`, `dateMedium`, `dateLong`
|
|
208
|
+
- `timeShort`
|
|
209
|
+
- `currencyFormat`, `currencyFormatWithoutCents`
|
|
210
|
+
- `percentFormat`
|