@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,144 @@
1
+ # @tstdl/base/memory
2
+
3
+ Utilities for advanced memory management and garbage collection observation in TypeScript. This module provides enhanced wrappers around the native `FinalizationRegistry` API, enabling multiple cleanup handlers per object and reactive garbage collection events via RxJS.
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
+ - [Multiple Handlers](#multiple-handlers)
12
+ - [Observable Finalization Registry](#observable-finalization-registry)
13
+ - [Unregistering Handlers](#unregistering-handlers)
14
+ - [📚 API](#-api)
15
+
16
+ ## ✨ Features
17
+
18
+ - **Multiple Finalization Handlers**: Attach multiple independent cleanup callbacks to a single object instance without conflicts.
19
+ - **Reactive GC Events**: Use `ObservableFinalizationRegistry` to receive RxJS notifications when objects are garbage collected.
20
+ - **Type-Safe Context**: Pass strongly-typed data to your finalization handlers to aid in cleanup (e.g., closing handles, logging IDs).
21
+ - **Abstraction**: Hides the complexity of managing `FinalizationRegistry` instances and tokens.
22
+
23
+ ## Core Concepts
24
+
25
+ JavaScript's `FinalizationRegistry` allows you to request a callback when an object is garbage collected. However, managing these registries can be cumbersome, especially if different parts of your application need to attach different cleanup logic to the same object.
26
+
27
+ The **Memory** module solves this by:
28
+
29
+ 1. **Centralizing Registry Management**: `registerFinalization` uses a shared internal registry and a mapping system to allow multiple handlers for one object.
30
+ 2. **Bridging to RxJS**: `ObservableFinalizationRegistry` turns the callback-based API into a stream-based API, making it easier to integrate with reactive architectures.
31
+
32
+ > **Note**: Garbage collection is non-deterministic. There is no guarantee _when_ or _if_ the cleanup handlers will run. These tools should be used for auxiliary cleanup (like releasing external resources or debugging leaks), not for critical program logic.
33
+
34
+ ## 🚀 Basic Usage
35
+
36
+ The most common use case is attaching a cleanup function to an object. This function will run after the object has been garbage collected.
37
+
38
+ ```ts
39
+ import { registerFinalization } from '@tstdl/base/memory';
40
+
41
+ class Resource {
42
+ id: number;
43
+
44
+ constructor(id: number) {
45
+ this.id = id;
46
+ }
47
+ }
48
+
49
+ // 1. Create an object
50
+ let myResource: Resource | null = new Resource(123);
51
+
52
+ // 2. Register a cleanup handler
53
+ // We pass the ID as data because we can't access 'myResource' inside the handler
54
+ // (it will be gone by then).
55
+ registerFinalization(
56
+ myResource,
57
+ (id) => {
58
+ console.log(`Resource with ID ${id} was garbage collected.`);
59
+ },
60
+ 123,
61
+ );
62
+
63
+ // 3. Dereference the object to make it eligible for GC
64
+ myResource = null;
65
+
66
+ // ... Sometime later, when GC runs:
67
+ // Output: "Resource with ID 123 was garbage collected."
68
+ ```
69
+
70
+ > **Warning**: Never pass the object being registered as the `data` payload, nor any object that holds a strong reference to it. Doing so will create a strong reference cycle and prevent the object from ever being garbage collected.
71
+
72
+ ## 🔧 Advanced Topics
73
+
74
+ ### Multiple Handlers
75
+
76
+ Unlike the native `FinalizationRegistry`, which only allows one "held value" per registration, `registerFinalization` allows you to attach any number of handlers to the same object.
77
+
78
+ ```ts
79
+ import { registerFinalization } from '@tstdl/base/memory';
80
+
81
+ const obj = { name: 'MultiHandler' };
82
+
83
+ registerFinalization(obj, () => console.log('Handler 1: Cleanup successful.'));
84
+ registerFinalization(obj, () => console.log('Handler 2: Notifying external system.'));
85
+
86
+ // Both handlers will run independently when 'obj' is collected.
87
+ ```
88
+
89
+ ### Observable Finalization Registry
90
+
91
+ If you prefer working with RxJS Observables, `ObservableFinalizationRegistry` emits values to a stream whenever a registered object is reclaimed.
92
+
93
+ ```ts
94
+ import { ObservableFinalizationRegistry } from '@tstdl/base/memory';
95
+
96
+ // Create a registry that expects a string token
97
+ const registry = new ObservableFinalizationRegistry<string>();
98
+
99
+ // Subscribe to the stream
100
+ const subscription = registry.finalize$.subscribe((token) => {
101
+ console.log(`Object associated with token "${token}" was collected.`);
102
+ });
103
+
104
+ // Register an object
105
+ let tempObject: object | null = {};
106
+ registry.register(tempObject, 'unique-token-abc');
107
+
108
+ // Dereference
109
+ tempObject = null;
110
+
111
+ // ... Sometime later:
112
+ // Output: "Object associated with token "unique-token-abc" was collected."
113
+ ```
114
+
115
+ ### Unregistering Handlers
116
+
117
+ You can remove a specific cleanup handler if it is no longer needed (e.g., if the resource was manually closed).
118
+
119
+ ```ts
120
+ import { registerFinalization, unregisterFinalization } from '@tstdl/base/memory';
121
+
122
+ const data = { value: 'important' };
123
+
124
+ const cleanup = () => {
125
+ console.log('Cleanup data');
126
+ };
127
+
128
+ // Register
129
+ registerFinalization(data, cleanup);
130
+
131
+ // Unregister specifically this handler
132
+ unregisterFinalization(data, cleanup);
133
+
134
+ // Even if 'data' is garbage collected now, 'cleanup' will NOT run.
135
+ ```
136
+
137
+ ## 📚 API
138
+
139
+ | Export | Type | Description |
140
+ | :------------------------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
141
+ | `registerFinalization` | Function | Registers a callback to be invoked after the target object is garbage collected. Supports multiple handlers and a data payload. |
142
+ | `unregisterFinalization` | Function | Unregisters a specific callback for a target object without affecting other registered handlers for the same object. |
143
+ | `ObservableFinalizationRegistry` | Class | Extends native `FinalizationRegistry`. Exposes a `finalize$` Observable that emits the `heldValue` when an object is collected. |
144
+ | `FinalizationHandler<D>` | Type | Type definition for the callback function: `(data: D) => any`. `D` defaults to `any` and is inferred when using `registerFinalization`. |
@@ -0,0 +1,244 @@
1
+ # Message Bus
2
+
3
+ A flexible, RxJS-based message bus module for decoupled in-process and cross-context communication, designed for seamless integration with dependency injection.
4
+
5
+ ## Table of Contents
6
+
7
+ - [✨ Features](#-features)
8
+ - [Core Concepts](#core-concepts)
9
+ - [Implementations](#implementations)
10
+ - [Message Observables](#message-observables)
11
+ - [🚀 Basic Usage](#-basic-usage)
12
+ - [1. Configuration](#1-configuration)
13
+ - [2. Injection & Usage](#2-injection--usage)
14
+ - [🔧 Advanced Topics](#-advanced-topics)
15
+ - [Cross-Context Communication (BroadcastChannel)](#cross-context-communication-broadcastchannel)
16
+ - [Fire and Forget](#fire-and-forget)
17
+ - [Resource Management](#resource-management)
18
+ - [📚 API](#-api)
19
+
20
+ ## ✨ Features
21
+
22
+ - **Decoupled Communication**: Enables components to communicate via named channels without direct references.
23
+ - **Reactive Architecture**: Built on **RxJS**, providing powerful operators for filtering and transforming message streams.
24
+ - **Dual Modes & Cross-Platform**:
25
+ - **Local**: High-performance in-memory communication using RxJS Subjects.
26
+ - **BroadcastChannel**: Cross-context communication (tabs, windows, workers) using the standard Web API.
27
+ - **DI Integration**: Implements `Resolvable`, allowing direct injection with channel names as arguments.
28
+ - **Type Safety**: Fully supports generic types for message payloads.
29
+ - **Smart Filtering**: Distinguishes between messages from _other_ instances and _all_ messages (including self).
30
+ - **Memory Safe**: Implements `AsyncDisposable` for proper cleanup of subscriptions and channels. Uses `WeakRefMap` internally to prevent memory leaks on unused channels.
31
+
32
+ ## Core Concepts
33
+
34
+ ### Implementations
35
+
36
+ The module provides two strategies, selectable via configuration at application startup:
37
+
38
+ 1. **`LocalMessageBus`**:
39
+ - **Scope**: Single JavaScript environment (e.g., one browser tab, one Node.js process).
40
+ - **Mechanism**: Shared RxJS `Subject`.
41
+ - **Use Case**: Component-to-component communication, global event bus within an app.
42
+
43
+ 2. **`BroadcastChannelMessageBus`**:
44
+ - **Scope**: Same-origin browsing contexts (e.g., multiple tabs, iframes, workers).
45
+ - **Mechanism**: Wraps the `BroadcastChannel` API.
46
+ - **Use Case**: Synchronizing state across tabs (e.g., logout in one tab affects all others).
47
+
48
+ ### Message Observables
49
+
50
+ Every `MessageBus` instance exposes two streams to handle different scenarios:
51
+
52
+ - **`messages$`**: Emits messages published by **other** instances on the same channel. It filters out messages sent by _this_ specific instance. Use this to react to external events.
53
+ - **`allMessages$`**: Emits **every** message on the channel, including those published by _this_ instance. Use this for logging, global state updates, or UI synchronization that must reflect local actions immediately.
54
+
55
+ ## 🚀 Basic Usage
56
+
57
+ ### 1. Configuration
58
+
59
+ Register the desired implementation provider in your application bootstrap.
60
+
61
+ **For in-process communication (Local):**
62
+
63
+ ```typescript
64
+ import { configureLocalMessageBus } from '@tstdl/base/message-bus';
65
+
66
+ // In your bootstrap/main file
67
+ configureLocalMessageBus();
68
+ ```
69
+
70
+ ### 2. Injection & Usage
71
+
72
+ Inject the `MessageBus` by passing the channel name as the argument.
73
+
74
+ ```typescript
75
+ import { inject } from '@tstdl/base/injector';
76
+ import { MessageBus } from '@tstdl/base/message-bus';
77
+
78
+ // 1. Define your message type
79
+ type UserCreatedEvent = { id: string; email: string };
80
+
81
+ export class UserService {
82
+ // 2. Inject the bus for a specific channel ('user-events')
83
+ private readonly userBus = inject(MessageBus<UserCreatedEvent>, 'user-events');
84
+
85
+ async createUser(email: string) {
86
+ const id = '123';
87
+ // ... creation logic ...
88
+
89
+ // 3. Publish a message
90
+ await this.userBus.publish({ id, email });
91
+ }
92
+ }
93
+
94
+ export class NotificationService {
95
+ private readonly userBus = inject(MessageBus<UserCreatedEvent>, 'user-events');
96
+
97
+ constructor() {
98
+ // 4. Subscribe to messages
99
+ this.userBus.messages$.subscribe((event) => {
100
+ console.log(`New user created: ${event.email}`);
101
+ });
102
+ }
103
+ }
104
+ ```
105
+
106
+ ### 3. Alternative: Manual Resolution
107
+
108
+ If you're not using dependency injection for a specific class or need to resolve a channel dynamically, use the `MessageBusProvider`.
109
+
110
+ ```typescript
111
+ import { inject } from '@tstdl/base/injector';
112
+ import { MessageBusProvider } from '@tstdl/base/message-bus';
113
+
114
+ const provider = inject(MessageBusProvider);
115
+ const bus = provider.get<string>('dynamic-channel');
116
+
117
+ await bus.publish('Hello world');
118
+ ```
119
+
120
+ ## 🔧 Advanced Topics
121
+
122
+ ### Reactive Patterns
123
+
124
+ Since `MessageBus` is built on RxJS, you can use any operators to handle your message streams.
125
+
126
+ ```typescript
127
+ import { filter, bufferTime } from 'rxjs';
128
+
129
+ // Only handle specific events
130
+ this.userBus.messages$.pipe(
131
+ filter(event => event.email.endsWith('@example.com'))
132
+ ).subscribe(event => { ... });
133
+
134
+ // Batch events for processing
135
+ this.userBus.messages$.pipe(
136
+ bufferTime(1000),
137
+ filter(batch => batch.length > 0)
138
+ ).subscribe(batch => {
139
+ console.log(`Processed ${batch.length} events this second.`);
140
+ });
141
+ ```
142
+
143
+ ### Cross-Context Communication (BroadcastChannel)
144
+
145
+ To enable communication between different tabs or windows (browsers) or between multiple workers/processes (Node.js), use the `BroadcastChannel` configuration.
146
+
147
+ ```typescript
148
+ import { configureBroadcastChannelMessageBus } from '@tstdl/base/message-bus';
149
+
150
+ // Use this instead of configureLocalMessageBus
151
+ configureBroadcastChannelMessageBus();
152
+ ```
153
+
154
+ > **Note:** `BroadcastChannel` is natively supported in modern browsers and Node.js 18+. For older environments, a polyfill may be required.
155
+
156
+ ### Custom Injector Configuration
157
+
158
+ When configuring the `LocalMessageBus`, you can optionally specify which injector to use. This is useful in complex setups or when using multiple containers.
159
+
160
+ ```typescript
161
+ import { configureLocalMessageBus } from '@tstdl/base/message-bus';
162
+ import { myCustomInjector } from './my-context';
163
+
164
+ configureLocalMessageBus({ injector: myCustomInjector });
165
+ ```
166
+
167
+ **Example: Logout Synchronization**
168
+
169
+ ```typescript
170
+ import { inject } from '@tstdl/base/injector';
171
+ import { MessageBus } from '@tstdl/base/message-bus';
172
+
173
+ type AuthEvent = { type: 'logout' };
174
+
175
+ class AuthService {
176
+ private readonly authBus = inject(MessageBus<AuthEvent>, 'auth');
177
+
178
+ async logout() {
179
+ // Perform logout
180
+ await this.authBus.publish({ type: 'logout' });
181
+ window.location.reload();
182
+ }
183
+
184
+ listenForExternalLogout() {
185
+ // 'messages$' only emits if *another* tab publishes the event
186
+ this.authBus.messages$.subscribe((event) => {
187
+ if (event.type === 'logout') {
188
+ console.log('Logout triggered in another tab.');
189
+ window.location.reload();
190
+ }
191
+ });
192
+ }
193
+ }
194
+ ```
195
+
196
+ ### Fire and Forget
197
+
198
+ The standard `publish` method returns a `Promise` that resolves when the message has been dispatched (e.g., posted to the BroadcastChannel). If you do not need to wait for this, use `publishAndForget`.
199
+
200
+ ```typescript
201
+ // Non-blocking publish
202
+ this.messageBus.publishAndForget({ type: 'ping' });
203
+ ```
204
+
205
+ ### Resource Management
206
+
207
+ `MessageBus` implements `AsyncDisposable`. When a bus is no longer needed, it should be disposed to close underlying channels (like `BroadcastChannel`) and complete internal subjects.
208
+
209
+ If you are using the dependency injection system with scoped providers, this is handled automatically. If you create instances manually or hold them in long-lived services, ensure you dispose of them.
210
+
211
+ ```typescript
212
+ await bus[Symbol.asyncDispose]();
213
+ ```
214
+
215
+ ## 📚 API
216
+
217
+ ### Classes & Interfaces
218
+
219
+ | Name | Description |
220
+ | :------------------------------------ | :---------------------------------------------------- |
221
+ | `MessageBus<T>` | Abstract base class for a message bus instance. |
222
+ | `MessageBusBase<T>` | Helper class for implementing custom message buses. |
223
+ | `MessageBusProvider` | Abstract factory for creating `MessageBus` instances. |
224
+ | `LocalMessageBus<T>` | Implementation for in-process communication. |
225
+ | `LocalMessageBusProvider` | Provider implementation for `LocalMessageBus`. |
226
+ | `BroadcastChannelMessageBus<T>` | Implementation for cross-context communication. |
227
+ | `BroadcastChannelMessageBusProvider` | Provider implementation for `BroadcastChannelMessageBus`. |
228
+
229
+ ### `MessageBus<T>` Members
230
+
231
+ | Member | Type | Description |
232
+ | :-------------------------- | :-------------- | :------------------------------------------- |
233
+ | `messages$` | `Observable<T>` | Stream of messages from **other** instances. |
234
+ | `allMessages$` | `Observable<T>` | Stream of **all** messages (including self). |
235
+ | `publish(message)` | `Promise<void>` | Publishes a message asynchronously. |
236
+ | `publishAndForget(message)` | `void` | Publishes a message without waiting. |
237
+ | `dispose()` | `Promise<void>` | Manually disposes the bus. |
238
+
239
+ ### Configuration Functions
240
+
241
+ | Function | Description |
242
+ | :-------------------------------------- | :-------------------------------------------------------------------- |
243
+ | `configureLocalMessageBus()` | Registers `LocalMessageBus` as the default implementation. |
244
+ | `configureBroadcastChannelMessageBus()` | Registers `BroadcastChannelMessageBus` as the default implementation. |
@@ -13,7 +13,7 @@ export class MessageBusBase extends MessageBus {
13
13
  this.logger = logger;
14
14
  this.publishSubject = new Subject();
15
15
  this.disposeToken = new CancellationToken();
16
- this.messages$ = defer(() => this._messages$).pipe(takeUntil(this.disposeToken.set$), share());
16
+ this.messages$ = defer(() => this._messages$).pipe(takeUntil(this.disposeToken), share());
17
17
  this.allMessages$ = merge(this.messages$, this.publishSubject);
18
18
  }
19
19
  publishAndForget(message) {
@@ -0,0 +1,182 @@
1
+ # Application Modules
2
+
3
+ The `@tstdl/base/module` library provides a robust framework for building modular applications with managed lifecycles. It defines a standard contract for components that need to be started, monitored, and gracefully stopped, such as background workers, API servers, or custom services.
4
+
5
+ ## Table of Contents
6
+
7
+ - [✨ Features](#-features)
8
+ - [Core Concepts](#core-concepts)
9
+ - [The Module Class](#the-module-class)
10
+ - [Lifecycle States](#lifecycle-states)
11
+ - [🚀 Basic Usage](#-basic-usage)
12
+ - [Creating a Custom Module](#creating-a-custom-module)
13
+ - [Manual Execution](#manual-execution)
14
+ - [🔧 Advanced Topics](#-advanced-topics)
15
+ - [FunctionModule](#functionmodule)
16
+ - [WebServerModule](#webservermodule)
17
+ - [📚 API](#-api)
18
+
19
+ ## ✨ Features
20
+
21
+ - **Standardized Lifecycle**: Uniform `run()` and `stop()` methods for all application components.
22
+ - **State Management**: Built-in tracking of module states (`Running`, `Stopping`, `Stopped`, `Erroneous`).
23
+ - **Graceful Shutdown**: Integrated `CancellationSignal` support for clean resource cleanup.
24
+ - **Resource Management**: Implemented `AsyncDisposable` for automatic cleanup when using the `using` keyword.
25
+ - **Ready-to-Use Modules**: Includes pre-built modules for common tasks like running functions or web servers.
26
+ - **Type Safety**: Fully typed with TypeScript for reliable development.
27
+
28
+ ## Core Concepts
29
+
30
+ ### The Module Class
31
+
32
+ The abstract `Module` class is the foundation of this library. It handles the internal state machine and synchronization required to start and stop services safely.
33
+
34
+ When you extend `Module`, you implement a single protected method: `_run(cancellationSignal)`. This method contains your main logic. The base class ensures that `_run` is called only when the module is stopped and handles the propagation of the cancellation signal when `stop()` is called.
35
+
36
+ ### Lifecycle States
37
+
38
+ A module is always in one of the following states, defined by the `ModuleState` enum:
39
+
40
+ 1. **Stopped**: The initial state. The module is ready to run.
41
+ 2. **Running**: The `_run` method is currently executing.
42
+ 3. **Stopping**: A stop has been requested, and the cancellation signal has been triggered, but the `_run` promise has not yet resolved.
43
+ 4. **Erroneous**: The module threw an unhandled exception during execution.
44
+
45
+ ## 🚀 Basic Usage
46
+
47
+ ### Creating a Custom Module
48
+
49
+ To create a module, extend the `Module` class and implement the `_run` method. Use the provided `CancellationSignal` to detect when the module should stop.
50
+
51
+ ```typescript
52
+ import { CancellationSignal } from '@tstdl/base/cancellation';
53
+ import { Module } from '@tstdl/base/module';
54
+ import { cancelableTimeout } from '@tstdl/base/utils';
55
+
56
+ export class DataProcessorModule extends Module {
57
+ constructor() {
58
+ super('DataProcessor');
59
+ }
60
+
61
+ protected async _run(cancellationSignal: CancellationSignal): Promise<void> {
62
+ console.log('DataProcessor started.');
63
+
64
+ // Run a loop until the signal is set (stop requested)
65
+ while (cancellationSignal.isUnset) {
66
+ console.log('Processing data batch...');
67
+
68
+ // Simulate work that can be cancelled
69
+ await cancelableTimeout(1000, cancellationSignal);
70
+ }
71
+
72
+ console.log('DataProcessor stopping gracefully.');
73
+ }
74
+ }
75
+ ```
76
+
77
+ ### Manual Execution
78
+
79
+ While modules are often managed by an `Application` runner (from `@tstdl/base/application`), you can run them manually.
80
+
81
+ ```typescript
82
+ import { DataProcessorModule } from './data-processor.module.js';
83
+ import { timeout } from '@tstdl/base/utils';
84
+
85
+ async function main() {
86
+ const module = new DataProcessorModule();
87
+
88
+ // Start the module (non-blocking)
89
+ const runPromise = module.run();
90
+
91
+ console.log(`Module state: ${module.state}`); // Running
92
+
93
+ // Let it run for a bit
94
+ await timeout(3000);
95
+
96
+ // Stop the module
97
+ console.log('Stopping module...');
98
+ await module.stop();
99
+
100
+ // Wait for the run promise to complete to ensure cleanup is done
101
+ await runPromise;
102
+
103
+ console.log(`Module state: ${module.state}`); // Stopped
104
+ }
105
+
106
+ main();
107
+ ```
108
+
109
+ ## 🔧 Advanced Topics
110
+
111
+ ### FunctionModule
112
+
113
+ The `FunctionModule` is a generic wrapper that allows you to treat a simple asynchronous function as a full-fledged module. This is useful for simple background tasks or scripts that don't require a dedicated class.
114
+
115
+ It is typically used in conjunction with the dependency injection system or the `Application` runner (from `@tstdl/base/application`), which handles the instantiation and argument injection.
116
+
117
+ ```typescript
118
+ import { Application, provideModule } from '@tstdl/base/application';
119
+ import { CancellationSignal } from '@tstdl/base/cancellation';
120
+
121
+ async function myTask(signal: CancellationSignal) {
122
+ console.log('Function module running');
123
+
124
+ while (signal.isUnset) {
125
+ // ... logic
126
+ await timeout(1000);
127
+ }
128
+ }
129
+
130
+ // Run the task as a module within an application
131
+ Application.run('MyApplication', [
132
+ provideModule(myTask)
133
+ ]);
134
+ ```
135
+
136
+ ### WebServerModule
137
+
138
+ The `WebServerModule` is a specialized module that integrates with `@tstdl/base/http` and `@tstdl/base/api` to run an HTTP server. It automatically registers API controllers and handles the server lifecycle.
139
+
140
+ It is designed to be used with the Dependency Injection system.
141
+
142
+ **Configuration:**
143
+
144
+ You can configure the port using `configureWebServerModule`.
145
+
146
+ ```typescript
147
+ import { configureWebServerModule, WebServerModule } from '@tstdl/base/module';
148
+ import { Application, provideModule } from '@tstdl/base/application';
149
+ import { configureNodeHttpServer } from '@tstdl/base/http/node';
150
+ import { configureApiServer } from '@tstdl/base/api/server';
151
+
152
+ // 1. Configure the environment
153
+ function bootstrap() {
154
+ configureNodeHttpServer(); // Select Node.js HTTP implementation
155
+ configureApiServer({
156
+ controllers: [
157
+ /* ... your controllers ... */
158
+ ],
159
+ });
160
+
161
+ // Configure the WebServerModule specific settings
162
+ configureWebServerModule({ port: 8080 });
163
+ }
164
+
165
+ // 2. Run the application with the WebServerModule
166
+ Application.run('MyApiServer', [
167
+ provideModule(WebServerModule),
168
+ // ... other providers
169
+ ]);
170
+ ```
171
+
172
+ ## 📚 API
173
+
174
+ | Type | Name | Description |
175
+ | :----------- | :----------------------------- | :----------------------------------------------------------------------------------------------------------- |
176
+ | **Class** | `Module` | Abstract base class for all modules. Handles state transitions and cancellation signals. Also implements `AsyncDisposable`. |
177
+ | **Enum** | `ModuleState` | Enumeration of module states: `Running`, `Stopping`, `Stopped`, `Erroneous`. |
178
+ | **Class** | `FunctionModule` | A generic module implementation that executes a provided function. |
179
+ | **Type** | `FunctionModuleFunction` | Signature for functions used with `FunctionModule`: `(signal: CancellationSignal) => void \| Promise<void>`. |
180
+ | **Class** | `WebServerModule` | A module that runs an HTTP server and serves registered API controllers. |
181
+ | **Type** | `WebServerModuleConfiguration` | Configuration object for `WebServerModule` (e.g., `{ port: number }`). |
182
+ | **Function** | `configureWebServerModule` | Helper function to set the global configuration for the `WebServerModule`. |
@@ -11,7 +11,7 @@ export type ModuleState = EnumType<typeof ModuleState>;
11
11
  export declare abstract class Module implements AsyncDisposable {
12
12
  private runPromise;
13
13
  private _state;
14
- protected readonly cancellationToken: CancellationToken;
14
+ protected cancellationToken: CancellationToken;
15
15
  readonly name: string;
16
16
  get state(): ModuleState;
17
17
  private get stateString();