@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
@@ -0,0 +1,140 @@
1
+ # Decorators
2
+
3
+ A collection of TypeScript decorators to enhance classes and methods with declarative behavior. Currently provides a robust logging decorator for tracing method execution, arguments, and return values.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Features](#-features)
8
+ - [Core Concepts](#core-concepts)
9
+ - [Basic Usage](#-basic-usage)
10
+ - [Advanced Topics](#-advanced-topics)
11
+ - [API](#-api)
12
+
13
+ ## ✨ Features
14
+
15
+ - **Method Logging**: Decorate individual methods to log their execution.
16
+ - **Class Logging**: Decorate an entire class to automatically log all its methods.
17
+ - **Configurable**: Customize what gets logged (arguments, return values, errors, execution time).
18
+ - **Smart Application**: Prevents double-wrapping if a method is decorated individually within a decorated class.
19
+
20
+ ## Core Concepts
21
+
22
+ This module leverages TypeScript decorators to implement cross-cutting concerns like logging without cluttering your business logic.
23
+
24
+ ### The `@Log` Decorator
25
+
26
+ The `@Log` decorator wraps the target method(s) using the `wrapLog` utility from the `@tstdl/base/function` module. It intercepts the method call, logs the entry (optionally with arguments), executes the original method, and logs the exit (optionally with the result or error and execution duration).
27
+
28
+ It supports both:
29
+
30
+ 1. **Method Decorator**: Wraps a single method.
31
+ 2. **Class Decorator**: Iterates through the class prototype and static properties to wrap all functions found.
32
+
33
+ ## 🚀 Basic Usage
34
+
35
+ ### Decorating a Method
36
+
37
+ Apply `@Log` to a specific method to trace its execution.
38
+
39
+ ```ts
40
+ import { Log } from '@tstdl/base/decorators';
41
+
42
+ class Calculator {
43
+ @Log()
44
+ add(a: number, b: number): number {
45
+ return a + b;
46
+ }
47
+ }
48
+
49
+ const calc = new Calculator();
50
+ calc.add(5, 3);
51
+ // Logs output similar to:
52
+ // Call: Calculator.add(5, 3)
53
+ // Return: Calculator.add => 8 (took 0.1ms)
54
+ ```
55
+
56
+ ### Decorating a Class
57
+
58
+ Apply `@Log` to a class to automatically wrap all its static and instance methods.
59
+
60
+ ```ts
61
+ import { Log } from '@tstdl/base/decorators';
62
+
63
+ @Log()
64
+ class UserService {
65
+ static create(name: string) {
66
+ console.log(`Creating user ${name}`);
67
+ }
68
+
69
+ getUser(id: string) {
70
+ return { id, name: 'Alice' };
71
+ }
72
+
73
+ deleteUser(id: string) {
74
+ // ...
75
+ }
76
+ }
77
+
78
+ // All methods above will be logged when called.
79
+ UserService.create('Bob');
80
+ const service = new UserService();
81
+ service.getUser('123');
82
+ ```
83
+
84
+ ## 🔧 Advanced Topics
85
+
86
+ ### Configuration Options
87
+
88
+ The `Log` decorator accepts an options object (passed to `wrapLog`) to control the logging behavior.
89
+
90
+ ```ts
91
+ import { Log } from '@tstdl/base/decorators';
92
+
93
+ class DataService {
94
+ @Log({
95
+ logArgs: true, // Log arguments passed to the function
96
+ logResult: false, // Do not log the return value (e.g. if it's huge)
97
+ logError: true, // Log errors if thrown
98
+ logTime: true, // Log execution duration
99
+ })
100
+ async fetchData(query: string): Promise<any> {
101
+ // ... complex logic
102
+ return {
103
+ /* large object */
104
+ };
105
+ }
106
+ }
107
+ ```
108
+
109
+ ### Mixing Class and Method Decorators
110
+
111
+ You can apply `@Log` to the class and provide specific configurations for individual methods. The decorator logic ensures methods are not wrapped twice; the method-level decorator takes precedence during the wrapping process.
112
+
113
+ ```ts
114
+ import { Log } from '@tstdl/base/decorators';
115
+
116
+ @Log({ logResult: false }) // Default: don't log results for methods in this class
117
+ class ReportGenerator {
118
+ generateSummary() {
119
+ // Inherits class config: logs execution but not result
120
+ return 'Summary...';
121
+ }
122
+
123
+ @Log({ logResult: true }) // Override: log result for this specific method
124
+ generateDetailedReport() {
125
+ return 'Detailed Report...';
126
+ }
127
+ }
128
+ ```
129
+
130
+ ## 📚 API
131
+
132
+ | Export | Type | Description |
133
+ | :------------------------------ | :-------- | :--------------------------------------------------------------------------- |
134
+ | `Log(options?: WrapLogOptions)` | Decorator | A decorator factory that wraps a class or method with logging functionality. |
135
+
136
+ ### Types
137
+
138
+ | Type | Description |
139
+ | :--------------- | :----------------------------------------------------------------------------------- |
140
+ | `WrapLogOptions` | Configuration object for the logging wrapper (imported from `@tstdl/base/function`). |
@@ -0,0 +1,231 @@
1
+ # Distributed Loop
2
+
3
+ A robust mechanism for running periodic tasks synchronized across distributed systems. It ensures that a specific loop executes on only one instance of your application at a time, preventing race conditions and duplicate processing in clustered environments.
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
+ - [Cancellation with Signals](#cancellation-with-signals)
12
+ - [Manual Control](#manual-control)
13
+ - [Error Handling](#error-handling)
14
+ - [📚 API](#-api)
15
+
16
+ ## ✨ Features
17
+
18
+ - **Distributed Synchronization**: Uses a `LockProvider` to ensure at most one active execution across a cluster.
19
+ - **Interval Control**: Run tasks at precise intervals (e.g., every 5 seconds).
20
+ - **Flexible Timing**: Distinguishes between execution interval and polling accuracy.
21
+ - **Graceful Shutdown**: Native support for `CancellationSignal` and manual `stop()` via `LoopController`.
22
+ - **Dependency Injection**: Can be resolved via `DistributedLoopProvider` or injected directly with a key.
23
+
24
+ ## Core Concepts
25
+
26
+ ### The Problem
27
+
28
+ In a microservices or clustered architecture, you often need to run periodic background tasks (e.g., sending emails, cleaning up cache, fetching external data). If you simply use `setInterval` on every instance, the task will run multiple times simultaneously, leading to data corruption or wasted resources.
29
+
30
+ ### The Solution
31
+
32
+ The `DistributedLoop` solves this by acquiring a distributed lock before executing the task.
33
+
34
+ 1. **Lock Acquisition**: The loop attempts to acquire a lock (via `LockProvider`) associated with a unique key.
35
+ 2. **Execution**: If the lock is acquired, the task runs.
36
+ 3. **Wait**: If the lock is held by another instance, the current instance waits and retries.
37
+ 4. **Release**: The lock is held only during the execution of the task logic (plus the interval wait time in the current implementation logic).
38
+
39
+ ### Components
40
+
41
+ - **`DistributedLoopProvider`**: A factory service to create named loops.
42
+ - **`DistributedLoop`**: The class managing the run loop, timing, and locking.
43
+ - **`LoopController`**: An interface returned when starting a loop, allowing you to stop it or await its completion.
44
+
45
+ ## 🚀 Basic Usage
46
+
47
+ ### 1. Configure LockProvider
48
+
49
+ Ensure you have a `LockProvider` (e.g., `PostgresLockProvider`) configured in your dependency injection container. The `DistributedLoop` uses it to synchronize across instances.
50
+
51
+ ### 2. Inject the Provider
52
+
53
+ Inject `DistributedLoopProvider` into your service for dynamic loop creation.
54
+
55
+ ```typescript
56
+ import { DistributedLoopProvider } from '@tstdl/base/distributed-loop';
57
+ import { Singleton, inject } from '@tstdl/base/injector';
58
+
59
+ @Singleton()
60
+ export class BackgroundWorker {
61
+ private readonly loopProvider = inject(DistributedLoopProvider);
62
+
63
+ start() {
64
+ // Create a named loop. The key 'data-sync' ensures uniqueness across the cluster.
65
+ const loop = this.loopProvider.get('data-sync');
66
+
67
+ // Run the loop
68
+ // Interval: 5000ms (5 seconds)
69
+ // Accuracy: 100ms (Check for stop/lock every 100ms)
70
+ loop.run(5000, 100, async (controller) => {
71
+ console.log('Running distributed task...');
72
+ await this.performSync();
73
+ });
74
+ }
75
+
76
+ private async performSync() {
77
+ // Your business logic here
78
+ }
79
+ }
80
+ ```
81
+
82
+ ### 3. Direct Injection
83
+
84
+ If your loop key is static, you can inject `DistributedLoop` directly.
85
+
86
+ ```typescript
87
+ import { DistributedLoop } from '@tstdl/base/distributed-loop';
88
+ import { Singleton, inject } from '@tstdl/base/injector';
89
+
90
+ @Singleton()
91
+ export class StaticWorker {
92
+ // Inject with a static key argument
93
+ private readonly loop = inject(DistributedLoop, 'static-worker-loop');
94
+
95
+ start() {
96
+ this.loop.run(60000, 1000, async () => {
97
+ console.log('Running static worker task...');
98
+ });
99
+ }
100
+ }
101
+ ```
102
+
103
+ ## 🔧 Advanced Topics
104
+
105
+ ### Timing and Accuracy
106
+
107
+ The `run` method takes both an `interval` and an `accuracy`.
108
+
109
+ - **Interval**: The target time between the start of one iteration and the start of the next.
110
+ - **Accuracy**: The maximum time the application waits between checking for the lock or a stop signal. A smaller accuracy makes the loop more responsive to stops and other instances releasing the lock, but increases the load on the `LockProvider`.
111
+
112
+ **Note**: To maintain exclusive access between iterations, the loop holds the lock for most of the interval, releasing it only for a short window defined by the accuracy. This ensures that in a cluster, the same instance is likely to keep the lock unless it becomes unavailable.
113
+
114
+ ### Cancellation with Signals
115
+
116
+ You can pass a `CancellationSignal` to the `run` method to tie the loop's lifecycle to a broader context (like the application shutdown signal).
117
+
118
+ ```typescript
119
+ import { DistributedLoopProvider } from '@tstdl/base/distributed-loop';
120
+ import { Application } from '@tstdl/base/application';
121
+ import { inject } from '@tstdl/base/injector';
122
+
123
+ async function main() {
124
+ const app = inject(Application);
125
+ const loopProvider = inject(DistributedLoopProvider);
126
+ const loop = loopProvider.get('cleanup-job');
127
+
128
+ // Pass the application's shutdown signal
129
+ loop.run(
130
+ 60000, // Run every minute
131
+ 1000, // 1 second accuracy
132
+ async () => {
133
+ console.log('Cleaning up...');
134
+ },
135
+ app.shutdownSignal, // Loop stops automatically when app shuts down
136
+ );
137
+ }
138
+ ```
139
+
140
+ ### Manual Control
141
+
142
+ The `run` method returns a `LoopController` which gives you manual control over the loop instance.
143
+
144
+ ```typescript
145
+ import { DistributedLoopProvider } from '@tstdl/base/distributed-loop';
146
+ import { inject } from '@tstdl/base/injector';
147
+ import { timeout } from '@tstdl/base/utils';
148
+
149
+ async function testLoop() {
150
+ const provider = inject(DistributedLoopProvider);
151
+ const loop = provider.get('test-loop');
152
+
153
+ const controller = loop.run(1000, 100, async () => {
154
+ console.log('Tick');
155
+ });
156
+
157
+ // Let it run for 5 seconds
158
+ await timeout(5000);
159
+
160
+ // Stop the loop manually
161
+ await controller.stop();
162
+ console.log('Loop stopped');
163
+ }
164
+ ```
165
+
166
+ ### Error Handling
167
+
168
+ The `DistributedLoop` does not automatically trap errors thrown inside your loop function. If your function throws an unhandled error:
169
+ 1. The current iteration stops immediately.
170
+ 2. The loop terminates.
171
+ 3. The `LoopController.$stopped` promise resolves.
172
+ 4. The error propagates as an unhandled rejection.
173
+
174
+ **Best Practice**: Always wrap your business logic in a `try-catch` block within the loop function to prevent the entire loop from stopping on a single failure.
175
+
176
+ ```typescript
177
+ loop.run(5000, 100, async () => {
178
+ try {
179
+ await riskyOperation();
180
+ } catch (error) {
181
+ console.error('Error in distributed loop iteration:', error);
182
+ // Loop continues to next iteration since error is caught
183
+ }
184
+ });
185
+ ```
186
+
187
+ ## 📚 API
188
+
189
+ ### `DistributedLoopProvider`
190
+
191
+ The factory class for creating distributed loops.
192
+
193
+ | Method | Description |
194
+ | :---------------------------------- | :------------------------------------------------------------------------ |
195
+ | `get(key: string): DistributedLoop` | Creates and returns a new `DistributedLoop` instance identified by `key`. |
196
+
197
+ ### `DistributedLoop`
198
+
199
+ The class responsible for executing the synchronized loop.
200
+
201
+ | Method | Description |
202
+ | :--------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
203
+ | `constructor(key: string, lockProvider: LockProvider)` | Creates a new loop instance. Usually handled by `DistributedLoopProvider` or DI. |
204
+ | `run(interval: number, accuracy: number, func: LoopFunction, cancellationSignal?: CancellationSignal): LoopController` | Starts the loop. <br>• `interval`: Time in ms between executions.<br>• `accuracy`: Polling interval in ms for lock/stop checks and release window.<br>• `func`: The function to execute.<br>• `cancellationSignal`: Optional signal to stop. |
205
+
206
+ ### `LoopController`
207
+
208
+ Interface returned by `run()` to control the active loop.
209
+
210
+ | Property/Method | Type | Description |
211
+ | :-------------- | :-------------------- | :------------------------------------------------------- |
212
+ | `$stopped` | `Promise<void>` | A promise that resolves when the loop has fully stopped. |
213
+ | `stop()` | `() => Promise<void>` | Signals the loop to stop and waits for it to finish. |
214
+
215
+ ### Types
216
+
217
+ #### `LoopFunction`
218
+
219
+ Type definition for the function executed by the loop.
220
+
221
+ ```typescript
222
+ type LoopFunction = (controller: LoopController) => any | Promise<any>;
223
+ ```
224
+
225
+ #### `DistributedLoopArgument`
226
+
227
+ The argument type used for injecting a `DistributedLoop` with a key.
228
+
229
+ ```typescript
230
+ type DistributedLoopArgument = string;
231
+ ```
@@ -42,7 +42,7 @@ let DistributedLoop = class DistributedLoop {
42
42
  */
43
43
  run(interval, accuracy, func, cancellationSignal) {
44
44
  const $stopped = new DeferredPromise();
45
- const stopToken = cancellationSignal?.createChild() ?? new CancellationToken();
45
+ const stopToken = new CancellationToken(cancellationSignal);
46
46
  const stopFunction = async () => {
47
47
  if (!stopToken.isSet) {
48
48
  stopToken.set();