@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.
Files changed (138) 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.js +2 -2
  28. package/audit/README.md +267 -0
  29. package/authentication/README.md +288 -0
  30. package/authentication/client/authentication.service.d.ts +12 -11
  31. package/authentication/client/authentication.service.js +21 -21
  32. package/authentication/client/http-client.middleware.js +2 -2
  33. package/authentication/tests/authentication.client-error-handling.test.js +2 -1
  34. package/authentication/tests/authentication.client-service-refresh.test.js +5 -3
  35. package/browser/README.md +401 -0
  36. package/cancellation/README.md +156 -0
  37. package/cancellation/tests/coverage.test.d.ts +1 -0
  38. package/cancellation/tests/coverage.test.js +49 -0
  39. package/cancellation/tests/leak.test.d.ts +1 -0
  40. package/cancellation/tests/leak.test.js +35 -0
  41. package/cancellation/tests/token.test.d.ts +1 -0
  42. package/cancellation/tests/token.test.js +136 -0
  43. package/cancellation/token.d.ts +53 -177
  44. package/cancellation/token.js +132 -201
  45. package/context/README.md +174 -0
  46. package/cookie/README.md +161 -0
  47. package/css/README.md +157 -0
  48. package/data-structures/README.md +320 -0
  49. package/decorators/README.md +140 -0
  50. package/distributed-loop/README.md +231 -0
  51. package/distributed-loop/distributed-loop.js +1 -1
  52. package/document-management/README.md +403 -0
  53. package/document-management/server/services/document-management.service.js +9 -7
  54. package/document-management/tests/document-management-core.test.js +2 -7
  55. package/document-management/tests/document-management.api.test.js +6 -7
  56. package/document-management/tests/document-statistics.service.test.js +11 -12
  57. package/document-management/tests/document.service.test.js +3 -3
  58. package/document-management/tests/enum-helpers.test.js +2 -3
  59. package/dom/README.md +213 -0
  60. package/enumerable/README.md +259 -0
  61. package/enumeration/README.md +121 -0
  62. package/errors/README.md +267 -0
  63. package/file/README.md +191 -0
  64. package/formats/README.md +210 -0
  65. package/function/README.md +144 -0
  66. package/http/README.md +318 -0
  67. package/http/client/adapters/undici.adapter.js +1 -1
  68. package/http/client/http-client-request.d.ts +6 -5
  69. package/http/client/http-client-request.js +8 -9
  70. package/http/server/node/node-http-server.js +1 -2
  71. package/image-service/README.md +137 -0
  72. package/injector/README.md +491 -0
  73. package/injector/injector.d.ts +1 -0
  74. package/injector/injector.js +17 -5
  75. package/injector/tests/leak.test.d.ts +1 -0
  76. package/injector/tests/leak.test.js +45 -0
  77. package/intl/README.md +113 -0
  78. package/json-path/README.md +182 -0
  79. package/jsx/README.md +154 -0
  80. package/key-value-store/README.md +191 -0
  81. package/lock/README.md +249 -0
  82. package/lock/web/web-lock.js +119 -47
  83. package/logger/README.md +287 -0
  84. package/mail/README.md +256 -0
  85. package/memory/README.md +144 -0
  86. package/message-bus/README.md +244 -0
  87. package/message-bus/message-bus-base.js +1 -1
  88. package/module/README.md +182 -0
  89. package/module/module.d.ts +1 -1
  90. package/module/module.js +77 -17
  91. package/module/modules/web-server.module.js +1 -1
  92. package/notification/tests/notification-type.service.test.js +24 -15
  93. package/object-storage/README.md +300 -0
  94. package/openid-connect/README.md +274 -0
  95. package/orm/README.md +423 -0
  96. package/package.json +8 -6
  97. package/password/README.md +164 -0
  98. package/pdf/README.md +246 -0
  99. package/polyfills.js +1 -0
  100. package/pool/README.md +198 -0
  101. package/process/README.md +237 -0
  102. package/promise/README.md +252 -0
  103. package/promise/cancelable-promise.js +1 -1
  104. package/random/README.md +193 -0
  105. package/reflection/README.md +305 -0
  106. package/rpc/README.md +386 -0
  107. package/rxjs-utils/README.md +262 -0
  108. package/schema/README.md +342 -0
  109. package/serializer/README.md +342 -0
  110. package/signals/implementation/README.md +134 -0
  111. package/sse/README.md +278 -0
  112. package/task-queue/README.md +300 -0
  113. package/task-queue/postgres/task-queue.d.ts +2 -1
  114. package/task-queue/postgres/task-queue.js +32 -2
  115. package/task-queue/task-context.js +1 -1
  116. package/task-queue/task-queue.d.ts +17 -0
  117. package/task-queue/task-queue.js +103 -44
  118. package/task-queue/tests/complex.test.js +4 -4
  119. package/task-queue/tests/dependencies.test.js +4 -2
  120. package/task-queue/tests/queue.test.js +111 -0
  121. package/task-queue/tests/worker.test.js +21 -13
  122. package/templates/README.md +287 -0
  123. package/testing/README.md +157 -0
  124. package/text/README.md +346 -0
  125. package/threading/README.md +238 -0
  126. package/types/README.md +311 -0
  127. package/utils/README.md +322 -0
  128. package/utils/async-iterable-helpers/observable-iterable.d.ts +1 -1
  129. package/utils/async-iterable-helpers/observable-iterable.js +4 -8
  130. package/utils/async-iterable-helpers/take-until.js +4 -4
  131. package/utils/backoff.js +89 -30
  132. package/utils/retry-with-backoff.js +1 -1
  133. package/utils/timer.d.ts +1 -1
  134. package/utils/timer.js +5 -7
  135. package/utils/timing.d.ts +1 -1
  136. package/utils/timing.js +2 -4
  137. package/utils/z-base32.d.ts +1 -0
  138. package/utils/z-base32.js +1 -0
package/text/README.md ADDED
@@ -0,0 +1,346 @@
1
+ # @tstdl/base/text
2
+
3
+ A powerful, reactive, and type-safe text localization module for TypeScript applications, built on signals. It provides seamless integration for internationalization (i18n) with automatic UI updates, parameter interpolation, and strong compile-time safety.
4
+
5
+ ## Table of Contents
6
+
7
+ - [✨ Features](#-features)
8
+ - [Core Concepts](#core-concepts)
9
+ - [LocalizationService](#localizationservice)
10
+ - [Localization Definitions](#localization-definitions)
11
+ - [DynamicText](#dynamictext)
12
+ - [Type-Safe Keys](#type-safe-keys)
13
+ - [🚀 Basic Usage](#-basic-usage)
14
+ - [1. Define Localization Structure](#1-define-localization-structure)
15
+ - [2. Create Localization Data](#2-create-localization-data)
16
+ - [3. Register and Use](#3-register-and-use)
17
+ - [🔧 Advanced Topics](#-advanced-topics)
18
+ - [Reactive Localization with Signals](#reactive-localization-with-signals)
19
+ - [Parameterized Text](#parameterized-text)
20
+ - [Localization Functions](#localization-functions)
21
+ - [Localizing Enums](#localizing-enums)
22
+ - [Resolving Nested Dynamic Text](#resolving-nested-dynamic-text)
23
+ - [Common Localizations](#common-localizations)
24
+ - [📚 API](#-api)
25
+
26
+ ## ✨ Features
27
+
28
+ - **Type-Safe Keys**: Leverage TypeScript proxies to provide compile-time safety and autocompletion for localization keys, eliminating magic strings.
29
+ - **Reactive Architecture**: Built on Signals, allowing text to automatically update whenever the active language changes without manual subscription management.
30
+ - **Dynamic Text Support**: Uniformly handle static strings, Signals, and Observables as localizable content.
31
+ - **Parameter Interpolation**: Easily inject values into translation strings (e.g., `Hello {{name}}`).
32
+ - **Functional Localization**: Use functions for complex logic like pluralization or conditional formatting.
33
+ - **Enum Support**: First-class utilities for localizing TypeScript enums.
34
+ - **RxJS Compatibility**: Includes Observable-based alternatives for all reactive methods.
35
+
36
+ ## Core Concepts
37
+
38
+ ### LocalizationService
39
+
40
+ The `LocalizationService` is the singleton central hub. It manages the active language, stores translation data, and performs the actual text resolution. It exposes reactive signals for the current language and available languages.
41
+
42
+ ### Localization Definitions
43
+
44
+ A `Localization` object defines the translations for a specific language. It consists of:
45
+
46
+ - **`language`**: Metadata like code (`en`) and name (`English`).
47
+ - **`keys`**: A nested object structure containing the translation strings or functions.
48
+ - **`enums`**: Definitions for translating TypeScript enums.
49
+
50
+ ### DynamicText
51
+
52
+ `DynamicText` is a type alias for `ReactiveValue<LocalizableText>`. It represents a piece of text that might be:
53
+
54
+ 1. A static string or localization key.
55
+ 2. A `Signal` emitting localization keys.
56
+ 3. An `Observable` emitting localization keys.
57
+
58
+ The module provides utilities to "resolve" a `DynamicText` into a `Signal<string>` that updates if either the source value changes _or_ the active language changes.
59
+
60
+ ### Type-Safe Keys
61
+
62
+ Instead of using dot-notation strings (e.g., `'home.welcome'`), this module uses a proxy object generated by `getLocalizationKeys()`. This allows you to pass around references to keys (e.g., `keys.home.welcome`) that TypeScript understands, enabling refactoring support and compile-time checks.
63
+
64
+ ## 🚀 Basic Usage
65
+
66
+ ### 1. Define Localization Structure
67
+
68
+ Define the shape of your localization keys using TypeScript types. This ensures all languages implement the same keys.
69
+
70
+ ```typescript
71
+ import { type Localization, type LocalizeItem, getLocalizationKeys } from '@tstdl/base/text';
72
+
73
+ // Define the structure
74
+ type AppLocalization = Localization<{
75
+ app: {
76
+ title: LocalizeItem;
77
+ greeting: LocalizeItem;
78
+ };
79
+ }>;
80
+
81
+ // Create the type-safe key proxy
82
+ export const keys = getLocalizationKeys<AppLocalization>();
83
+ ```
84
+
85
+ ### 2. Create Localization Data
86
+
87
+ Implement the localization for your supported languages.
88
+
89
+ ```typescript
90
+ import { type AppLocalization } from './localization-types'; // from step 1
91
+
92
+ export const english: AppLocalization = {
93
+ language: { code: 'en', name: 'English' },
94
+ keys: {
95
+ app: {
96
+ title: 'My Awesome App',
97
+ greeting: 'Hello World',
98
+ },
99
+ },
100
+ enums: [],
101
+ };
102
+
103
+ export const german: AppLocalization = {
104
+ language: { code: 'de', name: 'Deutsch' },
105
+ keys: {
106
+ app: {
107
+ title: 'Meine Tolle App',
108
+ greeting: 'Hallo Welt',
109
+ },
110
+ },
111
+ enums: [],
112
+ };
113
+ ```
114
+
115
+ ### 3. Register and Use
116
+
117
+ Inject the service, register the languages, and resolve text.
118
+
119
+ ```typescript
120
+ import { inject } from '@tstdl/base/injector';
121
+ import { LocalizationService } from '@tstdl/base/text';
122
+ import { keys } from './localization-types';
123
+ import { english, german } from './localizations';
124
+
125
+ const localizationService = inject(LocalizationService);
126
+
127
+ // Register languages
128
+ localizationService.registerLocalization(english, german);
129
+
130
+ // Set active language
131
+ localizationService.setLanguage('en');
132
+
133
+ // Resolve text reactively (returns a Signal)
134
+ const titleSignal = localizationService.localize(keys.app.title);
135
+
136
+ console.log(titleSignal()); // "My Awesome App"
137
+
138
+ // Switch language
139
+ localizationService.setLanguage('de');
140
+
141
+ // Signal updates automatically
142
+ console.log(titleSignal()); // "Meine Tolle App"
143
+ ```
144
+
145
+ ### Modular Localizations
146
+
147
+ `registerLocalization` automatically merges new keys and enums into existing language definitions if the language code matches. This allows different parts of your application or different modules to contribute translations to the same language incrementally.
148
+
149
+ ### Handling Missing Keys and Parameters
150
+
151
+ - **Missing Key**: If a localization key is missing for the active language, the service returns the key wrapped in double underscores (e.g., `__app.title__`) and logs a warning.
152
+ - **Missing Parameter**: If a parameter is missing in the data object but required by the template string, it is replaced by the parameter name wrapped in double underscores (e.g., `Hello, __name__!`).
153
+ - **Missing Observable Value**: When resolving an `Observable` based `DynamicText` using `resolveDynamicText`, the initial value before the first emission is `[MISSING LOCALIZATION KEY]`.
154
+
155
+ ## 🔧 Advanced Topics
156
+
157
+ ### Reactive Localization with Signals
158
+
159
+ The `resolveDynamicText` function is the primary tool for handling text in reactive applications. It accepts static values, Signals, or Observables.
160
+
161
+ ```typescript
162
+ import { signal } from '@tstdl/base/signals';
163
+ import { resolveDynamicText } from '@tstdl/base/text';
164
+ import { keys } from './localization-types';
165
+
166
+ // A signal that determines which key to show
167
+ const statusSignal = signal(keys.status.online);
168
+
169
+ // Result is a signal that updates if statusSignal changes OR language changes
170
+ const textSignal = resolveDynamicText(statusSignal);
171
+ ```
172
+
173
+ ### Parameterized Text
174
+
175
+ You can define parameters in your strings using `{{ paramName }}` syntax. Use `localizationData` to bind values to these parameters.
176
+
177
+ **Definition:**
178
+
179
+ ```typescript
180
+ type AppLocalization = Localization<{
181
+ messages: {
182
+ welcome: LocalizeItem<{ name: string }>;
183
+ };
184
+ }>;
185
+
186
+ const en: AppLocalization = {
187
+ // ...
188
+ keys: {
189
+ messages: {
190
+ welcome: 'Welcome, {{name}}!',
191
+ },
192
+ },
193
+ // ...
194
+ };
195
+ ```
196
+
197
+ **Usage:**
198
+
199
+ ```typescript
200
+ import { localizationData, resolveDynamicText } from '@tstdl/base/text';
201
+ import { keys } from './localization-types';
202
+
203
+ // Create a data object binding the key and parameters
204
+ const data = localizationData(keys.messages.welcome, { name: 'Alice' });
205
+
206
+ const text = resolveDynamicText(data);
207
+ console.log(text()); // "Welcome, Alice!"
208
+ ```
209
+
210
+ ### Localization Functions
211
+
212
+ For complex logic (like pluralization or conditional formatting), use a function instead of a string. Functions receive the parameters and a context object containing the `LocalizationService`.
213
+
214
+ ```typescript
215
+ const en: AppLocalization = {
216
+ // ...
217
+ keys: {
218
+ items: {
219
+ count: ({ count }, { localizationService }) => (count === 1 ? '1 Item' : `${count} Items`),
220
+ },
221
+ },
222
+ // ...
223
+ };
224
+
225
+ // Usage
226
+ const text = resolveDynamicText(localizationData(keys.items.count, { count: 5 }));
227
+ console.log(text()); // "5 Items"
228
+ ```
229
+
230
+ ### Localizing Enums
231
+
232
+ The module provides specific helpers for `enum` types to ensure type safety and ease of use.
233
+
234
+ ```typescript
235
+ import { defineEnum, type EnumType } from '@tstdl/base/enumeration';
236
+ import { enumerationLocalization, localizeEnum } from '@tstdl/base/text';
237
+
238
+ // 1. Define Enum
239
+ const Status = defineEnum('Status', {
240
+ Active: 'active',
241
+ Inactive: 'inactive',
242
+ });
243
+ type Status = EnumType<typeof Status>;
244
+
245
+ // 2. Add to Localization Type
246
+ type AppLocalization = Localization<
247
+ {
248
+ /* ... keys ... */
249
+ },
250
+ [typeof Status] // Add enum type here
251
+ >;
252
+
253
+ // 3. Define Translations
254
+ const en: AppLocalization = {
255
+ // ...
256
+ enums: [
257
+ enumerationLocalization(Status, 'Status', {
258
+ [Status.Active]: 'Active User',
259
+ [Status.Inactive]: 'Inactive User',
260
+ }),
261
+ ],
262
+ };
263
+
264
+ // 4. Usage
265
+ const statusText = localizationService.localizeEnum(Status, Status.Active);
266
+ console.log(statusText()); // "Active User"
267
+ ```
268
+
269
+ ### Resolving Nested Dynamic Text
270
+
271
+ When working with lists of objects where one property is a `DynamicText` (e.g., a navigation menu or a list of options), `resolveNestedDynamicTexts` helps transform the entire array efficiently.
272
+
273
+ ```typescript
274
+ import { resolveNestedDynamicTexts, type DynamicText } from '@tstdl/base/text';
275
+
276
+ type MenuItem = {
277
+ id: string;
278
+ label: DynamicText; // Can be a string, key, or localizationData
279
+ };
280
+
281
+ const menuItems: MenuItem[] = [
282
+ { id: 'home', label: keys.nav.home },
283
+ { id: 'settings', label: keys.nav.settings },
284
+ ];
285
+
286
+ // Returns a Signal<Array<{ id: string, label: string }>>
287
+ // The 'label' property is now the resolved string
288
+ const resolvedMenu = resolveNestedDynamicTexts(menuItems, 'label');
289
+ ```
290
+
291
+ ### Common Localizations
292
+
293
+ The module includes a set of common localizations (Yes, No, Ok, Cancel, etc.) that you can merge into your application to avoid repetition.
294
+
295
+ ```typescript
296
+ import { englishTstdlCommonLocalization, germanTstdlCommonLocalization } from '@tstdl/base/text';
297
+
298
+ localizationService.registerLocalization(englishTstdlCommonLocalization, germanTstdlCommonLocalization);
299
+ ```
300
+
301
+ ## 📚 API
302
+
303
+ ### LocalizationService
304
+
305
+ | Method | Description |
306
+ | :--------------------------------------- | :-------------------------------------------------------------------------- |
307
+ | `registerLocalization(...localizations)` | Registers one or more localization definitions. Merges if language exists. |
308
+ | `setLanguage(code)` | Sets the active language by code or `Language` object. |
309
+ | `getLanguage(code)` | Retrieves a registered `Language` by its code. |
310
+ | `hasLanguage(code)` | Checks if a language is registered. |
311
+ | `localize(data)` | Returns a `Signal<string>` for the given key/data. |
312
+ | `localize$(data)` | Returns an `Observable<string>` for the given key/data. |
313
+ | `localizeOnce(data)` | Returns a `string` (non-reactive) for the current language. |
314
+ | `localizeEnum(enum, value)` | Returns a `Signal<string>` for the enum value. |
315
+ | `localizeEnum$(enum, value)` | Returns an `Observable<string>` for the enum value. |
316
+ | `localizeEnumOnce(enum, value)` | Returns a `string` (non-reactive) for the enum value. |
317
+ | `activeLanguage` | Read-only signal of the currently active `Language`. |
318
+ | `availableLanguages` | Read-only signal of all registered `Language`s. |
319
+
320
+ ### Utilities
321
+
322
+ | Function | Description |
323
+ | :------------------------------------ | :-------------------------------------------------------------------------- |
324
+ | `getLocalizationKeys<T>()` | Returns a proxy object for type-safe access to localization keys. |
325
+ | `localizationData(key, params)` | Creates a typed object containing a key and its parameters. |
326
+ | `resolveDynamicText(text)` | Converts a `DynamicText` into a `Signal<string>`. |
327
+ | `resolveDynamicText$(text)` | Converts a `DynamicText` into an `Observable<string>`. |
328
+ | `resolveDynamicTexts(texts)` | Converts an array of `DynamicText` into a `Signal<string[]>`. |
329
+ | `resolveDynamicTexts$(texts)` | Converts an array of `DynamicText` into an `Observable<string[]>`. |
330
+ | `resolveNestedDynamicText(obj, key)` | Resolves a `DynamicText` property within an object (Signal). |
331
+ | `resolveNestedDynamicText$(obj, key)` | Resolves a `DynamicText` property within an object (Observable). |
332
+ | `resolveNestedDynamicTexts(arr, key)` | Resolves a `DynamicText` property within an array of objects (Signal). |
333
+ | `resolveNestedDynamicTexts$(arr, key)`| Resolves a `DynamicText` property within an array of objects (Observable). |
334
+ | `enumerationLocalization(...)` | Helper to define enum translations in a `Localization` object. |
335
+ | `autoEnumerationLocalization(enum)` | Generates default translations using enum key names as values. |
336
+
337
+ ### Types
338
+
339
+ | Type | Description |
340
+ | :--------------------- | :--------------------------------------------------------------------------------- |
341
+ | `Localization` | The structure for defining translations (language, keys, enums). |
342
+ | `LocalizeItem<Params>` | A translation value: either a string or a `LocalizeFunction`. |
343
+ | `LocalizeFunction<P>` | A function returning a string, receiving parameters and `LocalizeFunctionContext`. |
344
+ | `DynamicText` | `ReactiveValue<LocalizableText>`. The standard input type for localizable content. |
345
+ | `LocalizableText` | `string | LocalizationData`. |
346
+ | `LocalizationData` | Key, `LocalizationDataObject`, or `EnumLocalizationKey`. |
@@ -0,0 +1,238 @@
1
+ # @tstdl/base/threading
2
+
3
+ The `threading` module provides a unified, type-safe abstraction for multi-threading in both Node.js and browser environments. It utilizes a managed thread pool and an RPC mechanism to easily offload CPU-intensive tasks to worker threads without manually managing message passing.
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [✨ Features](#-features)
8
+ 2. [Core Concepts](#core-concepts)
9
+ 3. [🚀 Basic Usage](#-basic-usage)
10
+ 4. [🔧 Advanced Topics](#-advanced-topics)
11
+ - [Named Workers (Multiple Functions)](#named-workers-multiple-functions)
12
+ - [Configuration](#configuration)
13
+ 5. [📚 API](#-api)
14
+
15
+ ## ✨ Features
16
+
17
+ - **Cross-Platform:** Works seamlessly in Node.js (using `worker_threads`) and Browsers (using `Web Workers`).
18
+ - **Thread Pooling:** Automatically manages a pool of workers to reuse resources and limit concurrency.
19
+ - **RPC Communication:** Call worker functions directly as if they were local asynchronous functions, handling argument and return value serialization automatically.
20
+ - **Type Safety:** leveraging TypeScript to ensure the main thread invokes worker functions with correct arguments and return types.
21
+ - **Resource Management:** Implements `AsyncDisposable` for clean teardown of thread pools.
22
+
23
+ ## Core Concepts
24
+
25
+ ### Thread Pool
26
+
27
+ The `ThreadPool` is the main orchestrator running on the main thread. It spawns a specified number of worker threads based on a provided script URL. It manages the lifecycle of these workers and distributes tasks to them.
28
+
29
+ ### RPC (Remote Procedure Call)
30
+
31
+ Instead of manually posting messages (`postMessage`) and listening for events, this module uses an RPC layer. You "expose" a function inside the worker, and the `ThreadPool` creates a "remote" proxy for that function. When you call the proxy, arguments are sent to the worker, the function executes, and the result is sent back.
32
+
33
+ ### Worker Script
34
+
35
+ This is the separate entry point file that runs inside the thread. It imports `exposeThreadWorker` to make specific functions available to the pool.
36
+
37
+ ## 🚀 Basic Usage
38
+
39
+ To use the threading module, you need two parts: the worker script (the code running in the thread) and the main application code (which creates the pool).
40
+
41
+ ### 1. Create the Worker (`worker.ts`)
42
+
43
+ Define your CPU-intensive function, export its type (for type safety in the main thread), and expose it.
44
+
45
+ ```ts
46
+ import { exposeThreadWorker } from '@tstdl/base/threading';
47
+
48
+ // 1. Define the function to run in the thread
49
+ function heavyComputation(input: number): number {
50
+ // Simulate heavy work
51
+ const start = Date.now();
52
+ while (Date.now() - start < 500);
53
+ return input * 2;
54
+ }
55
+
56
+ // 2. Export the type so the main thread knows the signature
57
+ export type HeavyComputation = typeof heavyComputation;
58
+
59
+ // 3. Expose the function as the default handler
60
+ exposeThreadWorker(heavyComputation);
61
+ ```
62
+
63
+ ### 2. Use the Pool (`main.ts`)
64
+
65
+ Instantiate the `ThreadPool` pointing to the compiled worker script.
66
+
67
+ ```ts
68
+ import { ThreadPool } from '@tstdl/base/threading';
69
+ import { Logger } from '@tstdl/base/logger';
70
+ import type { HeavyComputation } from './worker.js'; // Import type only
71
+
72
+ async function main() {
73
+ const logger = new Logger('App');
74
+
75
+ // Point to the compiled JS file of the worker
76
+ const workerUrl = new URL('./worker.js', import.meta.url);
77
+
78
+ // Create the pool
79
+ const pool = new ThreadPool(workerUrl, logger);
80
+
81
+ try {
82
+ // Get a type-safe processor function
83
+ // The generic argument <HeavyComputation> ensures type safety
84
+ const process = pool.getProcessor<HeavyComputation>();
85
+
86
+ // Run the task
87
+ const result = await process(21);
88
+ console.log(`Result: ${result}`); // Result: 42
89
+ } finally {
90
+ // Clean up threads
91
+ await pool.dispose();
92
+ }
93
+ }
94
+
95
+ main();
96
+ ```
97
+
98
+ ## 🔧 Advanced Topics
99
+
100
+ ### Named Workers (Multiple Functions)
101
+
102
+ You can expose multiple functions from a single worker script by giving them unique names.
103
+
104
+ **Worker (`multi-worker.ts`):**
105
+
106
+ ```ts
107
+ import { exposeThreadWorker } from '@tstdl/base/threading';
108
+
109
+ function add(a: number, b: number): number {
110
+ return a + b;
111
+ }
112
+
113
+ function subtract(a: number, b: number): number {
114
+ return a - b;
115
+ }
116
+
117
+ export type Add = typeof add;
118
+ export type Subtract = typeof subtract;
119
+
120
+ // Expose with specific names
121
+ exposeThreadWorker(add, 'add');
122
+ exposeThreadWorker(subtract, 'subtract');
123
+ ```
124
+
125
+ **Main (`main.ts`):**
126
+
127
+ ```ts
128
+ import { ThreadPool } from '@tstdl/base/threading';
129
+ import { Logger } from '@tstdl/base/logger';
130
+ import type { Add, Subtract } from './multi-worker.js';
131
+
132
+ async function main() {
133
+ const logger = new Logger('App');
134
+ const pool = new ThreadPool(new URL('./multi-worker.js', import.meta.url), logger);
135
+
136
+ // Get processors by name
137
+ const addRemote = pool.getProcessor<Add>('add');
138
+ const subtractRemote = pool.getProcessor<Subtract>('subtract');
139
+
140
+ const sum = await addRemote(5, 3);
141
+ const diff = await subtractRemote(10, 4);
142
+
143
+ console.log({ sum, diff }); // { sum: 8, diff: 6 }
144
+
145
+ await pool.dispose();
146
+ }
147
+ ```
148
+
149
+ ### Best Practices
150
+
151
+ - **Offload CPU-Intensive Work Only:** Threading has overhead due to serialization and communication. Use it for heavy computations (e.g., image processing, complex math, large data parsing) rather than simple asynchronous I/O.
152
+ - **Minimize Data Transfer:** Arguments and return values are serialized (using the Structured Clone algorithm). Large objects can slow down communication.
153
+ - **Clean Up Resources:** Always call `dispose()` or use `[Symbol.asyncDispose]` (e.g., via `await using`) to ensure worker threads are terminated when they are no longer needed.
154
+ - **Worker Script Paths:** In Node.js, ensure the worker URL points to the _compiled_ `.js` file, especially when using TypeScript.
155
+
156
+ ### Configuration
157
+
158
+ You can configure the underlying worker options and the number of threads in the pool.
159
+
160
+ ```ts
161
+ import { ThreadPool } from '@tstdl/base/threading';
162
+ import { Logger } from '@tstdl/base/logger';
163
+
164
+ const pool = new ThreadPool(new URL('./worker.js', import.meta.url), new Logger('Pool'), {
165
+ // Number of workers to spawn.
166
+ // Defaults to half of CPU cores (via hardwareConcurrency / 2) or 4.
167
+ threadCount: 4,
168
+
169
+ // Node.js specific options (passed to node:worker_threads Worker constructor)
170
+ workerData: { some: 'data' },
171
+
172
+ // Browser specific options (passed to Web Worker constructor)
173
+ type: 'module',
174
+ });
175
+ ```
176
+
177
+ ## 📚 API
178
+
179
+ ### Classes
180
+
181
+ | Class | Description |
182
+ | :----------- | :--------------------------------------------------------------------------------------------------------------------------------------- |
183
+ | `ThreadPool` | Manages a pool of worker threads. Implements `AsyncDisposable` for clean resource cleanup. Provides type-safe access to worker functions. |
184
+
185
+ #### `ThreadPool`
186
+
187
+ ```ts
188
+ class ThreadPool implements AsyncDisposable {
189
+ /** The URL or path to the worker script. */
190
+ readonly url: string | URL;
191
+
192
+ /** Configuration options used for this pool. */
193
+ readonly options: ThreadOptions | undefined;
194
+
195
+ constructor(url: string | URL, logger: Logger, options?: ThreadOptions);
196
+
197
+ /**
198
+ * Returns a type-safe function that, when called, executes the task on an available worker.
199
+ * This is the preferred way to interact with the pool.
200
+ * @param name The name of the exposed function in the worker (default: 'default').
201
+ */
202
+ getProcessor<T extends ThreadWorker>(name?: string): (...args: Parameters<T>) => Promise<ReturnType<T>>;
203
+
204
+ /**
205
+ * Directly executes a task on an available worker.
206
+ * @param name The name of the exposed function in the worker.
207
+ * @param args Arguments to pass to the worker function.
208
+ */
209
+ process<T extends ThreadWorker>(name: string, ...args: Parameters<T>): Promise<ReturnType<T>>;
210
+
211
+ /**
212
+ * Terminates all workers in the pool and releases resources.
213
+ */
214
+ dispose(): Promise<void>;
215
+ }
216
+ ```
217
+
218
+ ### Functions
219
+
220
+ | Function | Description |
221
+ | :------------------- | :--------------------------------------------------------------------------------- |
222
+ | `exposeThreadWorker` | Exposes a function inside a worker script so it can be called by the `ThreadPool`. |
223
+
224
+ #### `exposeThreadWorker`
225
+
226
+ ```ts
227
+ function exposeThreadWorker<T extends ThreadWorker>(worker: T, name?: string): void;
228
+ ```
229
+
230
+ - `worker`: The function (synchronous or asynchronous) to expose.
231
+ - `name`: An optional name for the function. Defaults to `'default'`.
232
+
233
+ ### Types
234
+
235
+ | Type | Definition | Description |
236
+ | :-------------- | :--------------------------------------------------------------------------------- | :---------------------------------------------------------- |
237
+ | `ThreadWorker` | `(...args: any[]) => any \| Promise<any>` | Represents the signature of a function running in a thread. |
238
+ | `ThreadOptions` | `(WorkerOptions \| NodeWorkerThreads.WorkerOptions) & { threadCount?: number }` | Configuration options for the `ThreadPool`. |