@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.
Files changed (218) hide show
  1. package/README.md +166 -0
  2. package/ai/genkit/multi-region.plugin.js +5 -3
  3. package/ai/genkit/tests/multi-region.test.d.ts +1 -0
  4. package/ai/genkit/tests/multi-region.test.js +5 -2
  5. package/ai/parser/parser.js +2 -2
  6. package/ai/prompts/build.js +1 -0
  7. package/ai/prompts/instructions-formatter.d.ts +15 -2
  8. package/ai/prompts/instructions-formatter.js +36 -31
  9. package/ai/prompts/prompt-builder.js +5 -5
  10. package/ai/prompts/steering.d.ts +3 -2
  11. package/ai/prompts/steering.js +3 -1
  12. package/ai/tests/instructions-formatter.test.js +1 -0
  13. package/api/README.md +403 -0
  14. package/api/client/client.js +7 -13
  15. package/api/client/tests/api-client.test.js +10 -10
  16. package/api/default-error-handlers.js +1 -1
  17. package/api/response.d.ts +2 -2
  18. package/api/response.js +22 -33
  19. package/api/server/api-controller.d.ts +1 -1
  20. package/api/server/api-controller.js +3 -3
  21. package/api/server/api-request-token.provider.d.ts +1 -0
  22. package/api/server/api-request-token.provider.js +1 -0
  23. package/api/server/middlewares/allowed-methods.middleware.js +2 -1
  24. package/api/server/middlewares/content-type.middleware.js +2 -1
  25. package/api/types.d.ts +3 -2
  26. package/application/README.md +240 -0
  27. package/application/application.d.ts +1 -1
  28. package/application/application.js +3 -3
  29. package/application/providers.d.ts +20 -2
  30. package/application/providers.js +34 -7
  31. package/audit/README.md +267 -0
  32. package/audit/module.d.ts +5 -0
  33. package/audit/module.js +9 -1
  34. package/authentication/README.md +288 -0
  35. package/authentication/client/authentication.service.d.ts +12 -11
  36. package/authentication/client/authentication.service.js +21 -21
  37. package/authentication/client/http-client.middleware.js +2 -2
  38. package/authentication/server/module.d.ts +5 -0
  39. package/authentication/server/module.js +9 -1
  40. package/authentication/tests/authentication.api-controller.test.js +1 -1
  41. package/authentication/tests/authentication.api-request-token.provider.test.js +1 -1
  42. package/authentication/tests/authentication.client-error-handling.test.js +2 -1
  43. package/authentication/tests/authentication.client-service-refresh.test.js +5 -3
  44. package/authentication/tests/authentication.client-service.test.js +1 -1
  45. package/browser/README.md +401 -0
  46. package/cancellation/README.md +156 -0
  47. package/cancellation/tests/coverage.test.d.ts +1 -0
  48. package/cancellation/tests/coverage.test.js +49 -0
  49. package/cancellation/tests/leak.test.js +24 -29
  50. package/cancellation/tests/token.test.d.ts +1 -0
  51. package/cancellation/tests/token.test.js +136 -0
  52. package/cancellation/token.d.ts +53 -177
  53. package/cancellation/token.js +132 -208
  54. package/circuit-breaker/postgres/module.d.ts +1 -0
  55. package/circuit-breaker/postgres/module.js +5 -1
  56. package/context/README.md +174 -0
  57. package/cookie/README.md +161 -0
  58. package/css/README.md +157 -0
  59. package/data-structures/README.md +320 -0
  60. package/decorators/README.md +140 -0
  61. package/distributed-loop/README.md +231 -0
  62. package/distributed-loop/distributed-loop.js +1 -1
  63. package/document-management/README.md +403 -0
  64. package/document-management/server/configure.js +5 -1
  65. package/document-management/server/module.d.ts +1 -1
  66. package/document-management/server/module.js +1 -1
  67. package/document-management/server/services/document-management-ancillary.service.js +1 -1
  68. package/document-management/server/services/document-management.service.js +9 -7
  69. package/document-management/tests/ai-config-hierarchy.test.js +0 -5
  70. package/document-management/tests/document-management-ai-overrides.test.js +0 -1
  71. package/document-management/tests/document-management-core.test.js +2 -7
  72. package/document-management/tests/document-management.api.test.js +6 -7
  73. package/document-management/tests/document-statistics.service.test.js +11 -12
  74. package/document-management/tests/document-validation-ai-overrides.test.js +0 -1
  75. package/document-management/tests/document.service.test.js +3 -3
  76. package/document-management/tests/enum-helpers.test.js +2 -3
  77. package/dom/README.md +213 -0
  78. package/enumerable/README.md +259 -0
  79. package/enumeration/README.md +121 -0
  80. package/errors/README.md +267 -0
  81. package/examples/document-management/main.d.ts +1 -0
  82. package/examples/document-management/main.js +14 -11
  83. package/file/README.md +191 -0
  84. package/formats/README.md +210 -0
  85. package/function/README.md +144 -0
  86. package/http/README.md +318 -0
  87. package/http/client/adapters/undici.adapter.js +1 -1
  88. package/http/client/http-client-request.d.ts +6 -5
  89. package/http/client/http-client-request.js +8 -9
  90. package/http/server/node/node-http-server.js +1 -2
  91. package/image-service/README.md +137 -0
  92. package/injector/README.md +491 -0
  93. package/intl/README.md +113 -0
  94. package/json-path/README.md +182 -0
  95. package/jsx/README.md +154 -0
  96. package/key-value-store/README.md +191 -0
  97. package/key-value-store/postgres/module.d.ts +1 -0
  98. package/key-value-store/postgres/module.js +5 -1
  99. package/lock/README.md +249 -0
  100. package/lock/postgres/module.d.ts +1 -0
  101. package/lock/postgres/module.js +5 -1
  102. package/lock/web/web-lock.js +119 -47
  103. package/logger/README.md +287 -0
  104. package/mail/README.md +256 -0
  105. package/mail/module.d.ts +5 -1
  106. package/mail/module.js +11 -6
  107. package/memory/README.md +144 -0
  108. package/message-bus/README.md +244 -0
  109. package/message-bus/message-bus-base.js +1 -1
  110. package/module/README.md +182 -0
  111. package/module/module.d.ts +1 -1
  112. package/module/module.js +77 -17
  113. package/module/modules/web-server.module.js +3 -4
  114. package/notification/server/module.d.ts +1 -0
  115. package/notification/server/module.js +5 -1
  116. package/notification/tests/notification-flow.test.js +2 -2
  117. package/notification/tests/notification-type.service.test.js +24 -15
  118. package/object-storage/README.md +300 -0
  119. package/openid-connect/README.md +274 -0
  120. package/orm/README.md +423 -0
  121. package/orm/decorators.d.ts +5 -1
  122. package/orm/decorators.js +1 -1
  123. package/orm/server/drizzle/schema-converter.js +17 -30
  124. package/orm/server/encryption.d.ts +0 -1
  125. package/orm/server/encryption.js +1 -4
  126. package/orm/server/index.d.ts +1 -6
  127. package/orm/server/index.js +1 -6
  128. package/orm/server/migration.d.ts +19 -0
  129. package/orm/server/migration.js +72 -0
  130. package/orm/server/repository.d.ts +1 -1
  131. package/orm/server/transaction.d.ts +5 -10
  132. package/orm/server/transaction.js +22 -26
  133. package/orm/server/transactional.js +3 -3
  134. package/orm/tests/database-migration.test.d.ts +1 -0
  135. package/orm/tests/database-migration.test.js +82 -0
  136. package/orm/tests/encryption.test.js +3 -4
  137. package/orm/utils.d.ts +17 -2
  138. package/orm/utils.js +49 -1
  139. package/package.json +9 -6
  140. package/password/README.md +164 -0
  141. package/pdf/README.md +246 -0
  142. package/polyfills.js +1 -0
  143. package/pool/README.md +198 -0
  144. package/process/README.md +237 -0
  145. package/promise/README.md +252 -0
  146. package/promise/cancelable-promise.js +1 -1
  147. package/random/README.md +193 -0
  148. package/rate-limit/postgres/module.d.ts +1 -0
  149. package/rate-limit/postgres/module.js +5 -1
  150. package/reflection/README.md +305 -0
  151. package/reflection/decorator-data.js +11 -12
  152. package/rpc/README.md +386 -0
  153. package/rxjs-utils/README.md +262 -0
  154. package/schema/README.md +342 -0
  155. package/serializer/README.md +342 -0
  156. package/signals/implementation/README.md +134 -0
  157. package/sse/README.md +278 -0
  158. package/task-queue/README.md +293 -0
  159. package/task-queue/postgres/drizzle/{0000_simple_invisible_woman.sql → 0000_wakeful_sunspot.sql} +22 -14
  160. package/task-queue/postgres/drizzle/meta/0000_snapshot.json +160 -82
  161. package/task-queue/postgres/drizzle/meta/_journal.json +2 -2
  162. package/task-queue/postgres/module.d.ts +1 -0
  163. package/task-queue/postgres/module.js +5 -1
  164. package/task-queue/postgres/schemas.d.ts +9 -6
  165. package/task-queue/postgres/schemas.js +4 -3
  166. package/task-queue/postgres/task-queue.d.ts +4 -13
  167. package/task-queue/postgres/task-queue.js +462 -355
  168. package/task-queue/postgres/task.model.d.ts +12 -5
  169. package/task-queue/postgres/task.model.js +51 -25
  170. package/task-queue/task-context.d.ts +2 -2
  171. package/task-queue/task-context.js +8 -8
  172. package/task-queue/task-queue.d.ts +53 -19
  173. package/task-queue/task-queue.js +121 -55
  174. package/task-queue/tests/cascading-cancellations.test.d.ts +1 -0
  175. package/task-queue/tests/cascading-cancellations.test.js +38 -0
  176. package/task-queue/tests/complex.test.js +45 -229
  177. package/task-queue/tests/coverage-branch.test.d.ts +1 -0
  178. package/task-queue/tests/coverage-branch.test.js +407 -0
  179. package/task-queue/tests/coverage-enhancement.test.d.ts +1 -0
  180. package/task-queue/tests/coverage-enhancement.test.js +144 -0
  181. package/task-queue/tests/dag-dependencies.test.d.ts +1 -0
  182. package/task-queue/tests/dag-dependencies.test.js +41 -0
  183. package/task-queue/tests/dependencies.test.js +28 -26
  184. package/task-queue/tests/extensive-dependencies.test.js +64 -139
  185. package/task-queue/tests/fan-out-spawning.test.d.ts +1 -0
  186. package/task-queue/tests/fan-out-spawning.test.js +53 -0
  187. package/task-queue/tests/idempotent-replacement.test.d.ts +1 -0
  188. package/task-queue/tests/idempotent-replacement.test.js +61 -0
  189. package/task-queue/tests/missing-idempotent-tasks.test.d.ts +1 -0
  190. package/task-queue/tests/missing-idempotent-tasks.test.js +38 -0
  191. package/task-queue/tests/queue.test.js +128 -8
  192. package/task-queue/tests/worker.test.js +39 -16
  193. package/task-queue/tests/zombie-parent.test.d.ts +1 -0
  194. package/task-queue/tests/zombie-parent.test.js +45 -0
  195. package/task-queue/tests/zombie-recovery.test.d.ts +1 -0
  196. package/task-queue/tests/zombie-recovery.test.js +51 -0
  197. package/templates/README.md +287 -0
  198. package/test5.js +5 -5
  199. package/testing/README.md +157 -0
  200. package/testing/integration-setup.d.ts +4 -4
  201. package/testing/integration-setup.js +54 -29
  202. package/text/README.md +346 -0
  203. package/text/localization.service.js +2 -2
  204. package/threading/README.md +238 -0
  205. package/types/README.md +311 -0
  206. package/utils/README.md +322 -0
  207. package/utils/async-iterable-helpers/observable-iterable.d.ts +1 -1
  208. package/utils/async-iterable-helpers/observable-iterable.js +4 -8
  209. package/utils/async-iterable-helpers/take-until.js +4 -4
  210. package/utils/backoff.js +89 -30
  211. package/utils/file-reader.js +1 -2
  212. package/utils/retry-with-backoff.js +1 -1
  213. package/utils/timer.d.ts +1 -1
  214. package/utils/timer.js +5 -7
  215. package/utils/timing.d.ts +1 -1
  216. package/utils/timing.js +2 -4
  217. package/utils/z-base32.d.ts +1 -0
  218. package/utils/z-base32.js +1 -0
@@ -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. |
@@ -2,6 +2,7 @@ import { Injector } from '../../injector/index.js';
2
2
  import { type DatabaseConfig } from '../../orm/server/index.js';
3
3
  export declare class PostgresKeyValueStoreModuleConfig {
4
4
  database?: DatabaseConfig;
5
+ autoMigrate?: boolean;
5
6
  }
6
7
  /**
7
8
  * configure key-value store module
@@ -1,10 +1,11 @@
1
1
  import { inject, Injector } from '../../injector/index.js';
2
- import { Database, migrate } from '../../orm/server/index.js';
2
+ import { Database, migrate, registerDatabaseMigration } from '../../orm/server/index.js';
3
3
  import { isDefined } from '../../utils/type-guards.js';
4
4
  import { KeyValueStore } from '../key-value.store.js';
5
5
  import { PostgresKeyValueStore } from './key-value-store.service.js';
6
6
  export class PostgresKeyValueStoreModuleConfig {
7
7
  database;
8
+ autoMigrate;
8
9
  }
9
10
  /**
10
11
  * configure key-value store module
@@ -15,6 +16,9 @@ export function configurePostgresKeyValueStore({ injector, ...config } = {}) {
15
16
  targetInjector.register(PostgresKeyValueStoreModuleConfig, { useValue: config });
16
17
  }
17
18
  targetInjector.registerSingleton(KeyValueStore, { useToken: PostgresKeyValueStore });
19
+ if (config.autoMigrate != false) {
20
+ registerDatabaseMigration('PostgresKeyValueStore', migratePostgresKeyValueStoreSchema, { injector });
21
+ }
18
22
  }
19
23
  export async function migratePostgresKeyValueStoreSchema() {
20
24
  const connection = inject(PostgresKeyValueStoreModuleConfig, undefined, { optional: true })?.database?.connection;