@tstdl/base 0.93.139 → 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.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/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/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 -45
- 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/intl/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# @tstdl/base/intl
|
|
2
|
+
|
|
3
|
+
The `intl` module provides utilities for handling internationalization tasks, specifically focusing on robust, locale-aware number parsing. It allows you to convert formatted number strings (e.g., "1.234,56" in German) back into standard JavaScript numbers by correctly identifying decimal and group separators based on the locale.
|
|
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
|
+
- [Loose Parsing](#loose-parsing)
|
|
12
|
+
- [Non-Latin Numerals](#non-latin-numerals)
|
|
13
|
+
- [Performance & Memoization](#performance--memoization)
|
|
14
|
+
- [📚 API](#-api)
|
|
15
|
+
|
|
16
|
+
## ✨ Features
|
|
17
|
+
|
|
18
|
+
- **Locale-Aware Parsing**: Handles `,`, `.`, and spaces as separators depending on the locale.
|
|
19
|
+
- **Group Separator Support**: Automatically handles thousands separators (e.g., `1,000` vs `1.000`).
|
|
20
|
+
- **Loose Mode**: Strips non-numeric characters (like currency symbols or units) before parsing.
|
|
21
|
+
- **Numeral System Support**: Automatically maps non-Latin numerals (e.g., Arabic-Indic `١٢٣`) to standard digits.
|
|
22
|
+
- **Memoization**: Optimized performance through cached parser instances.
|
|
23
|
+
|
|
24
|
+
## Core Concepts
|
|
25
|
+
|
|
26
|
+
Standard JavaScript functions like `parseFloat()` or `Number()` are designed for machine-readable formats (usually English-based, using `.` for decimals and no thousands separators). They often fail or produce incorrect results when dealing with user input in other locales (e.g., `parseFloat('1.234,56')` results in `1.234` instead of `1234.56`).
|
|
27
|
+
|
|
28
|
+
The `NumberParser` class uses the native `Intl.NumberFormat` API to inspect the formatting rules of a given locale. It normalizes the input string by removing group separators and converting decimal separators to `.` before using the standard `Number()` constructor.
|
|
29
|
+
|
|
30
|
+
## 🚀 Basic Usage
|
|
31
|
+
|
|
32
|
+
The most common way to use this module is via the `parseNumber` helper function.
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { parseNumber } from '@tstdl/base/intl';
|
|
36
|
+
|
|
37
|
+
// US English: Comma for thousands, Dot for decimals
|
|
38
|
+
const usValue = parseNumber('en-US', '12,345.67');
|
|
39
|
+
console.log(usValue); // 12345.67
|
|
40
|
+
|
|
41
|
+
// German: Dot for thousands, Comma for decimals
|
|
42
|
+
const deValue = parseNumber('de-DE', '12.345,67');
|
|
43
|
+
console.log(deValue); // 12345.67
|
|
44
|
+
|
|
45
|
+
// French: Space for thousands, Comma for decimals
|
|
46
|
+
const frValue = parseNumber('fr-FR', '12 345,67');
|
|
47
|
+
console.log(frValue); // 12345.67
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 🔧 Advanced Topics
|
|
51
|
+
|
|
52
|
+
### Loose Parsing
|
|
53
|
+
|
|
54
|
+
When dealing with user input or scraped data, strings often contain currency symbols, units, or other text. The `loose` parameter (default `false`) instructs the parser to strip out any characters that are not valid numerals or separators for the target locale before parsing.
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { parseNumber } from '@tstdl/base/intl';
|
|
58
|
+
|
|
59
|
+
// Standard parsing fails on non-numeric characters
|
|
60
|
+
const strict = parseNumber('en-US', '$1,234.56 USD');
|
|
61
|
+
console.log(strict); // NaN
|
|
62
|
+
|
|
63
|
+
// Loose parsing strips the currency symbol and text
|
|
64
|
+
const loose = parseNumber('en-US', '$1,234.56 USD', true);
|
|
65
|
+
console.log(loose); // 1234.56
|
|
66
|
+
|
|
67
|
+
// Works with locale-specific formatting
|
|
68
|
+
const looseDe = parseNumber('de-DE', '1.234,56 €', true);
|
|
69
|
+
console.log(looseDe); // 1234.56
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Non-Latin Numerals
|
|
73
|
+
|
|
74
|
+
The module automatically handles locales that use non-Latin numeral systems by mapping them back to standard digits.
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { parseNumber } from '@tstdl/base/intl';
|
|
78
|
+
|
|
79
|
+
// Arabic (Egypt): Uses Arabic-Indic numerals
|
|
80
|
+
const arValue = parseNumber('ar-EG', '١٢٬٣٤٥٫٦٧');
|
|
81
|
+
console.log(arValue); // 12345.67
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Performance & Memoization
|
|
85
|
+
|
|
86
|
+
Creating a `NumberParser` instance involves some overhead. To mitigate this, the module exports `getNumberParser`, which memoizes instances based on the locale string. The `parseNumber` helper uses this internally.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { getNumberParser } from '@tstdl/base/intl';
|
|
90
|
+
|
|
91
|
+
// Uses the memoized instance (Recommended)
|
|
92
|
+
const parser = getNumberParser('en-GB');
|
|
93
|
+
const value = parser.parse('10,000.50');
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## 📚 API
|
|
97
|
+
|
|
98
|
+
### Functions
|
|
99
|
+
|
|
100
|
+
| Function | Description |
|
|
101
|
+
| :--- | :--- |
|
|
102
|
+
| `parseNumber(locale: string, value: string, loose?: boolean): number` | Parses a localized string into a number. |
|
|
103
|
+
| `getNumberParser(locale: string): NumberParser` | Returns a memoized `NumberParser` instance for the specified locale. |
|
|
104
|
+
|
|
105
|
+
### Classes
|
|
106
|
+
|
|
107
|
+
#### `NumberParser`
|
|
108
|
+
|
|
109
|
+
| Method / Property | Type | Description |
|
|
110
|
+
| :--- | :--- | :--- |
|
|
111
|
+
| `constructor(locale: string)` | `void` | Creates a new parser for the given locale. |
|
|
112
|
+
| `locale` | `string` | The locale string this parser was initialized with. |
|
|
113
|
+
| `parse(value: string, loose?: boolean): number` | `number` | Parses the string. If `loose` is true, strips invalid characters first. |
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# @tstdl/base/json-path
|
|
2
|
+
|
|
3
|
+
A robust utility module for parsing, encoding, and manipulating JSON Path strings. It provides both functional helpers and a fluent class interface to convert between string representations (e.g., `$.users[0].name`) and structural arrays of property keys.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [✨ Features](#-features)
|
|
8
|
+
- [Core Concepts](#core-concepts)
|
|
9
|
+
- [🚀 Basic Usage](#-basic-usage)
|
|
10
|
+
- [Using the JsonPath Class](#using-the-jsonpath-class)
|
|
11
|
+
- [Functional Encoding & Decoding](#functional-encoding--decoding)
|
|
12
|
+
- [🔧 Advanced Topics](#-advanced-topics)
|
|
13
|
+
- [Formatting Options](#formatting-options)
|
|
14
|
+
- [Working with Symbols](#working-with-symbols)
|
|
15
|
+
- [Iteration](#iteration)
|
|
16
|
+
- [📚 API](#-api)
|
|
17
|
+
|
|
18
|
+
## ✨ Features
|
|
19
|
+
|
|
20
|
+
- **Two-way Conversion**: reliable parsing of JSON path strings into node arrays and encoding arrays back to strings.
|
|
21
|
+
- **Fluent Interface**: An immutable `JsonPath` class for building and modifying paths.
|
|
22
|
+
- **Symbol Support**: Handles `Symbol` property keys in paths.
|
|
23
|
+
- **Configurable Formatting**: Control over dot notation vs. bracket notation and root prefixes.
|
|
24
|
+
- **Type Safety**: Built with TypeScript for strong typing support.
|
|
25
|
+
|
|
26
|
+
## Core Concepts
|
|
27
|
+
|
|
28
|
+
A **JSON Path** is a string representing a location within a JSON object or array.
|
|
29
|
+
|
|
30
|
+
- **Nodes**: The individual segments of a path. A node can be a `string`, `number` (array index), or `symbol`.
|
|
31
|
+
- **Root**: The `$` character typically represents the root of the object.
|
|
32
|
+
- **Notation**:
|
|
33
|
+
- **Dot Notation**: `.property` (used for valid identifiers).
|
|
34
|
+
- **Bracket Notation**: `['property']` or `[0]` (used for special characters, numbers, or when forced).
|
|
35
|
+
|
|
36
|
+
## 🚀 Basic Usage
|
|
37
|
+
|
|
38
|
+
### Using the JsonPath Class
|
|
39
|
+
|
|
40
|
+
The `JsonPath` class provides an object-oriented, immutable way to manage paths.
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { JsonPath } from '@tstdl/base/json-path';
|
|
44
|
+
|
|
45
|
+
// Create from a string
|
|
46
|
+
const path = new JsonPath('$.users[0].profile');
|
|
47
|
+
// or using static from
|
|
48
|
+
const pathFromStr = JsonPath.from('$.users[0].profile');
|
|
49
|
+
|
|
50
|
+
console.log(path.nodes); // ['users', 0, 'profile']
|
|
51
|
+
|
|
52
|
+
// Create from an array of nodes
|
|
53
|
+
const pathFromNodes = new JsonPath(['users', 0, 'profile']);
|
|
54
|
+
console.log(pathFromNodes.toString()); // "$.users[0].profile"
|
|
55
|
+
|
|
56
|
+
// Use the ROOT constant for the root path ($)
|
|
57
|
+
const root = JsonPath.ROOT;
|
|
58
|
+
console.log(root.toString()); // "$"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Path Manipulation
|
|
62
|
+
|
|
63
|
+
`JsonPath` instances are immutable. Methods that modify the path return a new instance.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { JsonPath } from '@tstdl/base/json-path';
|
|
67
|
+
|
|
68
|
+
const base = new JsonPath('$.data');
|
|
69
|
+
|
|
70
|
+
// Add nodes
|
|
71
|
+
const child = base.add('items').add(5);
|
|
72
|
+
console.log(child.toString()); // "$.data.items[5]"
|
|
73
|
+
|
|
74
|
+
// Slice nodes
|
|
75
|
+
const sliced = child.slice(1, 2);
|
|
76
|
+
console.log(sliced.toString()); // "$.items" (note: dollar prefix added by default)
|
|
77
|
+
|
|
78
|
+
// Change options on an existing path
|
|
79
|
+
const bracketed = child.options({ forceBrackets: true });
|
|
80
|
+
console.log(bracketed.toString()); // "$['data']['items'][5]"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Functional Encoding & Decoding
|
|
84
|
+
|
|
85
|
+
You can use the standalone functions if you don't need the class wrapper.
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import { encodeJsonPath, decodeJsonPath } from '@tstdl/base/json-path';
|
|
89
|
+
|
|
90
|
+
// Encoding: Array -> String
|
|
91
|
+
const encoded = encodeJsonPath(['store', 'book', 0, 'title']);
|
|
92
|
+
console.log(encoded); // "$.store.book[0].title"
|
|
93
|
+
|
|
94
|
+
// Decoding: String -> Array
|
|
95
|
+
const decoded = decodeJsonPath('$.store.book[0].title');
|
|
96
|
+
console.log(decoded); // ["store", "book", 0, "title"]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## 🔧 Advanced Topics
|
|
100
|
+
|
|
101
|
+
### Formatting Options
|
|
102
|
+
|
|
103
|
+
You can customize how the path string is generated using `JsonPathOptions`.
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import { JsonPath, encodeJsonPath } from '@tstdl/base/json-path';
|
|
107
|
+
|
|
108
|
+
const nodes = ['foo', 'bar', 0];
|
|
109
|
+
|
|
110
|
+
// Option 1: Force brackets for everything
|
|
111
|
+
const bracketPath = encodeJsonPath(nodes, { forceBrackets: true });
|
|
112
|
+
console.log(bracketPath); // "$['foo']['bar'][0]"
|
|
113
|
+
|
|
114
|
+
// Option 2: Treat array indices as object properties (dot notation)
|
|
115
|
+
const dotPath = encodeJsonPath(nodes, { treatArrayAsObject: true });
|
|
116
|
+
console.log(dotPath); // "$.foo.bar.0"
|
|
117
|
+
|
|
118
|
+
// Option 3: Remove the root '$' prefix
|
|
119
|
+
const noRootPath = encodeJsonPath(nodes, { dollar: false });
|
|
120
|
+
console.log(noRootPath); // "foo.bar[0]"
|
|
121
|
+
|
|
122
|
+
// Using options with the class
|
|
123
|
+
const path = new JsonPath(['a', 'b'], { forceBrackets: true });
|
|
124
|
+
console.log(path.toString()); // "$['a']['b']"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Working with Symbols
|
|
128
|
+
|
|
129
|
+
The module supports `Symbol` keys, which are encoded using the syntax `[Symbol(description)]`.
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import { JsonPath, decodeJsonPath } from '@tstdl/base/json-path';
|
|
133
|
+
|
|
134
|
+
const sym = Symbol('uniqueId'); // Note: not using Symbol.for here for the example
|
|
135
|
+
const path = new JsonPath(['data', sym]);
|
|
136
|
+
|
|
137
|
+
console.log(path.toString()); // "$.data[Symbol(uniqueId)]"
|
|
138
|
+
|
|
139
|
+
// Decoding can map back to specific symbol instances via JsonPathContext
|
|
140
|
+
const decodedNodes = decodeJsonPath('$.data[Symbol(uniqueId)]', { symbols: [sym] });
|
|
141
|
+
const decodedSym = decodedNodes[1] as symbol;
|
|
142
|
+
|
|
143
|
+
console.log(decodedSym === sym); // true
|
|
144
|
+
|
|
145
|
+
// If no context is provided, it falls back to Symbol.for(description)
|
|
146
|
+
const fallbackNodes = decodeJsonPath('$.data[Symbol(uniqueId)]');
|
|
147
|
+
const fallbackSym = fallbackNodes[1] as symbol;
|
|
148
|
+
|
|
149
|
+
console.log(fallbackSym === Symbol.for('uniqueId')); // true
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Iteration
|
|
153
|
+
|
|
154
|
+
The `JsonPath` class implements `Iterable`, allowing you to loop over its nodes directly.
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
import { JsonPath } from '@tstdl/base/json-path';
|
|
158
|
+
|
|
159
|
+
const path = new JsonPath('$.a.b.c');
|
|
160
|
+
|
|
161
|
+
for (const node of path) {
|
|
162
|
+
console.log(node);
|
|
163
|
+
}
|
|
164
|
+
// Output:
|
|
165
|
+
// "a"
|
|
166
|
+
// "b"
|
|
167
|
+
// "c"
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## 📚 API
|
|
171
|
+
|
|
172
|
+
| Export | Type | Description |
|
|
173
|
+
| :------------------ | :--------- | :------------------------------------------------------------------------------ |
|
|
174
|
+
| `JsonPath` | `class` | Main class for building and manipulating JSON paths. Implements `Iterable`. |
|
|
175
|
+
| `JsonPathNode` | `type` | Alias for `PropertyKey` (`string \| number \| symbol`). |
|
|
176
|
+
| `JsonPathInput` | `type` | `string \| JsonPath \| Iterable<JsonPathNode>` |
|
|
177
|
+
| `JsonPathOptions` | `type` | Configuration object for encoding behavior (`forceBrackets`, `dollar`, etc.). |
|
|
178
|
+
| `JsonPathContext` | `type` | Context for decoding (e.g., to map `Symbol` descriptions). |
|
|
179
|
+
| `encodeJsonPath` | `function` | Converts an array of nodes into a JSON Path string. |
|
|
180
|
+
| `decodeJsonPath` | `function` | Parses a JSON Path string into an array of nodes. |
|
|
181
|
+
| `isJsonPath` | `function` | Returns `true` if the string matches the JSON Path pattern. |
|
|
182
|
+
| `isJsonPath` (stat) | `method` | Static method on `JsonPath` class equivalent to the standalone function. |
|
package/jsx/README.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# @tstdl/base/jsx
|
|
2
|
+
|
|
3
|
+
A lightweight utility module for server-side rendering (SSR) of JSX/TSX content using Preact. It provides a unified API for synchronously and asynchronously rendering components and Virtual DOM nodes to HTML strings.
|
|
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
|
+
- [Asynchronous Rendering](#asynchronous-rendering)
|
|
12
|
+
- [Checking Component Types](#checking-component-types)
|
|
13
|
+
- [📚 API](#-api)
|
|
14
|
+
|
|
15
|
+
## ✨ Features
|
|
16
|
+
|
|
17
|
+
- **Unified Rendering API**: Render Functional Components, Class Components, or raw VNodes using the same function signature.
|
|
18
|
+
- **Server-Side Rendering (SSR)**: Generate HTML strings from JSX for emails, PDF generation, or initial page loads.
|
|
19
|
+
- **Async Support**: Native support for asynchronous rendering (e.g., for Suspense boundaries).
|
|
20
|
+
- **Type Guards**: Utilities to distinguish between Component Classes and Functional Components at runtime.
|
|
21
|
+
|
|
22
|
+
## Core Concepts
|
|
23
|
+
|
|
24
|
+
This module wraps `preact-render-to-string` to provide a strictly typed, developer-friendly interface for generating HTML from Preact components.
|
|
25
|
+
|
|
26
|
+
### Rendering Strategy
|
|
27
|
+
|
|
28
|
+
The module distinguishes between rendering a **Component Definition** (passing the function/class and its props separately) and a **Virtual Node** (an already instantiated `<Component />`). This allows for cleaner code when you have the component and data separate (e.g., in controller logic).
|
|
29
|
+
|
|
30
|
+
### Preact Integration
|
|
31
|
+
|
|
32
|
+
Under the hood, this library relies on Preact. This ensures the rendering process is fast and lightweight, making it ideal for high-throughput server environments.
|
|
33
|
+
|
|
34
|
+
## 🚀 Basic Usage
|
|
35
|
+
|
|
36
|
+
The most common use case is rendering a functional component to a string.
|
|
37
|
+
|
|
38
|
+
### Rendering a Functional Component
|
|
39
|
+
|
|
40
|
+
You can pass the component function and its properties directly to `renderJsx`.
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { renderJsx } from '@tstdl/base/jsx';
|
|
44
|
+
|
|
45
|
+
type HelloProps = {
|
|
46
|
+
name: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
function Hello({ name }: HelloProps) {
|
|
50
|
+
return <div>Hello, {name}!</div>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Pass component and props separately
|
|
54
|
+
const html = renderJsx(Hello, { name: 'World' });
|
|
55
|
+
|
|
56
|
+
console.log(html);
|
|
57
|
+
// Output: <div>Hello, World!</div>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Rendering a Class Component
|
|
61
|
+
|
|
62
|
+
Class components are also supported.
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import { Component } from 'preact';
|
|
66
|
+
import { renderJsx } from '@tstdl/base/jsx';
|
|
67
|
+
|
|
68
|
+
type Props = { title: string };
|
|
69
|
+
|
|
70
|
+
class MyComponent extends Component<Props> {
|
|
71
|
+
render() {
|
|
72
|
+
return <h1>{this.props.title}</h1>;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const html = renderJsx(MyComponent, { title: 'Hello from Class' });
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Rendering a VNode
|
|
80
|
+
|
|
81
|
+
If you already have a JSX element (VNode), you can pass it directly.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { renderJsx } from '@tstdl/base/jsx';
|
|
85
|
+
|
|
86
|
+
const element = (
|
|
87
|
+
<section>
|
|
88
|
+
<h1>Static Content</h1>
|
|
89
|
+
<p>This is a VNode.</p>
|
|
90
|
+
</section>
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const html = renderJsx(element);
|
|
94
|
+
console.log(html);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 🔧 Advanced Topics
|
|
98
|
+
|
|
99
|
+
### Asynchronous Rendering
|
|
100
|
+
|
|
101
|
+
If your component tree involves asynchronous operations (like `Suspense`), use `renderJsxAsync`. This returns a `Promise` that resolves to the HTML string once the tree has fully settled.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { renderJsxAsync } from '@tstdl/base/jsx';
|
|
105
|
+
|
|
106
|
+
async function generateReport() {
|
|
107
|
+
// Assume ReportComponent uses Suspense or async logic
|
|
108
|
+
const html = await renderJsxAsync(ReportComponent, { data: [1, 2, 3] });
|
|
109
|
+
return html;
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Checking Component Types
|
|
114
|
+
|
|
115
|
+
When building higher-order components or framework utilities, you might need to distinguish between a Class Component and a Functional Component. The `isComponentClass` type guard handles this check, which can be tricky due to how classes are transpiled.
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { Component } from 'preact';
|
|
119
|
+
import { isComponentClass } from '@tstdl/base/jsx';
|
|
120
|
+
|
|
121
|
+
class MyClassComponent extends Component {
|
|
122
|
+
render() {
|
|
123
|
+
return <div>Class</div>;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function MyFuncComponent() {
|
|
128
|
+
return <div>Function</div>;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
console.log(isComponentClass(MyClassComponent)); // true
|
|
132
|
+
console.log(isComponentClass(MyFuncComponent)); // false
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Configuration
|
|
136
|
+
|
|
137
|
+
To use JSX/TSX in your project with this module, ensure your `tsconfig.json` is configured to use Preact as the JSX factory.
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"compilerOptions": {
|
|
142
|
+
"jsx": "react-jsx",
|
|
143
|
+
"jsxImportSource": "preact"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## 📚 API
|
|
149
|
+
|
|
150
|
+
| Export | Type | Description |
|
|
151
|
+
| :----------------- | :------- | :---------------------------------------------------------------------------------------------------------------------------- |
|
|
152
|
+
| `renderJsx` | Function | Synchronously renders a component or VNode to an HTML string. Overloads support passing props separately or a VNode directly. |
|
|
153
|
+
| `renderJsxAsync` | Function | Asynchronously renders a component or VNode to an HTML string (Promise). Useful for Suspense-enabled trees. |
|
|
154
|
+
| `isComponentClass` | Function | Type guard that returns `true` if the provided template is a Preact Component Class. |
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Key-Value Store
|
|
2
|
+
|
|
3
|
+
A flexible, type-safe, and backend-agnostic module for persistent key-value storage, scoped by module names to prevent collisions.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [✨ Features](#-features)
|
|
8
|
+
- [Core Concepts](#core-concepts)
|
|
9
|
+
- [🚀 Basic Usage](#-basic-usage)
|
|
10
|
+
- [Configuration](#configuration)
|
|
11
|
+
- [Getting a Store Instance](#getting-a-store-instance)
|
|
12
|
+
- [Type Safety](#type-safety)
|
|
13
|
+
- [Basic Operations](#basic-operations)
|
|
14
|
+
- [🔧 Advanced Topics](#-advanced-topics)
|
|
15
|
+
- [Atomic Get or Set](#atomic-get-or-set)
|
|
16
|
+
- [Batch Operations](#batch-operations)
|
|
17
|
+
- [Transaction Support (Postgres)](#transaction-support-postgres)
|
|
18
|
+
- [📚 API](#-api)
|
|
19
|
+
|
|
20
|
+
## ✨ Features
|
|
21
|
+
|
|
22
|
+
- **Type-Safe**: Full TypeScript support for defining the shape of your stored data.
|
|
23
|
+
- **Module Scoping**: Automatically namespaces keys (e.g., `user-settings`, `cache`) to prevent conflicts.
|
|
24
|
+
- **Backend Agnostic**: Abstract API allows swapping storage engines without changing application code.
|
|
25
|
+
- **PostgreSQL Support**: Includes a robust, transaction-safe implementation using Drizzle ORM.
|
|
26
|
+
- **Dependency Injection**: Seamlessly integrates with the `@tstdl/base/injector`.
|
|
27
|
+
|
|
28
|
+
## Core Concepts
|
|
29
|
+
|
|
30
|
+
### KeyValueStore
|
|
31
|
+
|
|
32
|
+
The `KeyValueStore<KV>` is the primary abstract class. It defines the contract for all storage operations (`get`, `set`, `delete`, etc.). It is generic, allowing you to define an interface `KV` representing the valid keys and value types for a specific store.
|
|
33
|
+
|
|
34
|
+
### Module Scoping
|
|
35
|
+
|
|
36
|
+
Every store instance is initialized with a `module` string. This acts as a namespace in the underlying database. For example, a key named `theme` in the `user-settings` module is distinct from a key named `theme` in the `admin-panel` module.
|
|
37
|
+
|
|
38
|
+
### Persistence
|
|
39
|
+
|
|
40
|
+
This module provides a PostgreSQL implementation (`PostgresKeyValueStore`) that persists data to a `key_value` table. It handles serialization (JSON) and deserialization automatically.
|
|
41
|
+
|
|
42
|
+
## 🚀 Basic Usage
|
|
43
|
+
|
|
44
|
+
### Configuration
|
|
45
|
+
|
|
46
|
+
To use the PostgreSQL backend, you must configure it during your application's bootstrap phase. This registers the implementation in the dependency injection container.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { Application } from '@tstdl/base/application';
|
|
50
|
+
import { configurePostgresKeyValueStore, migratePostgresKeyValueStoreSchema } from '@tstdl/base/key-value-store/postgres';
|
|
51
|
+
|
|
52
|
+
async function bootstrap() {
|
|
53
|
+
// Register the Postgres implementation with default settings
|
|
54
|
+
configurePostgresKeyValueStore();
|
|
55
|
+
|
|
56
|
+
// Or provide a custom database configuration
|
|
57
|
+
configurePostgresKeyValueStore({
|
|
58
|
+
database: {
|
|
59
|
+
connection: 'postgresql://user:password@localhost:5432/db'
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function main() {
|
|
65
|
+
// Ensure the database schema is up to date (creates the 'key_value_store' schema and 'key_value' table)
|
|
66
|
+
await migratePostgresKeyValueStoreSchema();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
Application.run({ bootstrap }, main);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Getting a Store Instance
|
|
73
|
+
|
|
74
|
+
You can inject the store into your services. The `KeyValueStore` requires an argument (the module name) when being resolved.
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { inject, Singleton } from '@tstdl/base/injector';
|
|
78
|
+
import { KeyValueStore } from '@tstdl/base/key-value-store';
|
|
79
|
+
|
|
80
|
+
@Singleton()
|
|
81
|
+
export class MyService {
|
|
82
|
+
// Inject a store scoped to 'my-feature'
|
|
83
|
+
readonly store = inject(KeyValueStore, 'my-feature');
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Type Safety
|
|
88
|
+
|
|
89
|
+
Define an interface for your data to ensure type safety across your application.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { inject, Singleton } from '@tstdl/base/injector';
|
|
93
|
+
import { KeyValueStore } from '@tstdl/base/key-value-store';
|
|
94
|
+
|
|
95
|
+
type UserSettings = {
|
|
96
|
+
theme: 'dark' | 'light';
|
|
97
|
+
notificationsEnabled: boolean;
|
|
98
|
+
loginCount: number;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
@Singleton()
|
|
102
|
+
export class UserSettingsService {
|
|
103
|
+
// The store is now typed. 'theme' must be 'dark' | 'light', etc.
|
|
104
|
+
readonly settings = inject<KeyValueStore<UserSettings>>(KeyValueStore, 'user-settings');
|
|
105
|
+
|
|
106
|
+
async updateTheme(theme: 'dark' | 'light'): Promise<void> {
|
|
107
|
+
await this.settings.set('theme', theme);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Basic Operations
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
// Set a value
|
|
116
|
+
await store.set('theme', 'dark');
|
|
117
|
+
|
|
118
|
+
// Get a value (returns undefined if not found)
|
|
119
|
+
const theme = await store.get('theme');
|
|
120
|
+
|
|
121
|
+
// Get with a default value
|
|
122
|
+
const notifications = await store.get('notificationsEnabled', true);
|
|
123
|
+
|
|
124
|
+
// Delete a key
|
|
125
|
+
await store.delete('theme');
|
|
126
|
+
|
|
127
|
+
// Clear all keys in this module
|
|
128
|
+
await store.clear();
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## 🔧 Advanced Topics
|
|
132
|
+
|
|
133
|
+
### Atomic Get or Set
|
|
134
|
+
|
|
135
|
+
The `getOrSet` method is useful for caching or initializing values. It retrieves the value if it exists; otherwise, it sets the provided value and returns it. In the PostgreSQL implementation, this operation is atomic and transaction-safe.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
// If 'loginCount' exists, return it.
|
|
139
|
+
// If not, set it to 0 and return 0.
|
|
140
|
+
const count = await store.getOrSet('loginCount', 0);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Batch Operations
|
|
144
|
+
|
|
145
|
+
You can set or delete multiple keys efficiently in a single operation.
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
// Set multiple values
|
|
149
|
+
await store.setMany({
|
|
150
|
+
theme: 'light',
|
|
151
|
+
notificationsEnabled: false,
|
|
152
|
+
loginCount: 1,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Delete multiple keys
|
|
156
|
+
await store.deleteMany(['theme', 'notificationsEnabled']);
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Transaction Support (Postgres)
|
|
160
|
+
|
|
161
|
+
The `PostgresKeyValueStore` implementation is transaction-safe. It extends `Transactional`, which means it automatically participates in existing transactions managed by the ORM.
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
await database.transaction(async (tx) => {
|
|
165
|
+
// These operations will run within the same database transaction
|
|
166
|
+
await store.withTransaction(tx).set('theme', 'dark');
|
|
167
|
+
await otherRepository.withTransaction(tx).save(entity);
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## 📚 API
|
|
172
|
+
|
|
173
|
+
### `KeyValueStore<KV>`
|
|
174
|
+
|
|
175
|
+
| Method | Signature | Description |
|
|
176
|
+
| :----------- | :-------------------------------------------------------- | :--------------------------------------------------------------- |
|
|
177
|
+
| `get` | `get<K>(key: K): Promise<KV[K] \| undefined>` | Gets the value of a key. |
|
|
178
|
+
| `get` | `get<K, D>(key: K, defaultValue: D): Promise<KV[K] \| D>` | Gets the value of a key, returning `defaultValue` if missing. |
|
|
179
|
+
| `set` | `set<K>(key: K, value: KV[K]): Promise<void>` | Sets the value of a key. |
|
|
180
|
+
| `getOrSet` | `getOrSet<K>(key: K, value: KV[K]): Promise<KV[K]>` | Gets a value, or sets and returns the provided value if missing. |
|
|
181
|
+
| `setMany` | `setMany(keyValues: Partial<KV>): Promise<void>` | Sets multiple key-value pairs at once. |
|
|
182
|
+
| `delete` | `delete(key: keyof KV): Promise<boolean>` | Deletes a key. Returns `true` if deleted. |
|
|
183
|
+
| `deleteMany` | `deleteMany(keys: (keyof KV)[]): Promise<void>` | Deletes multiple keys. |
|
|
184
|
+
| `clear` | `clear(): Promise<void>` | Removes all keys associated with the current module. |
|
|
185
|
+
|
|
186
|
+
### Postgres Configuration
|
|
187
|
+
|
|
188
|
+
| Function | Description |
|
|
189
|
+
| :---------------------------------------- | :------------------------------------------------------------------ |
|
|
190
|
+
| `configurePostgresKeyValueStore(config?)` | Registers the PostgreSQL backend. Accepts optional database config. |
|
|
191
|
+
| `migratePostgresKeyValueStoreSchema()` | Runs Drizzle migrations to create/update the `key_value` table. |
|