@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,210 @@
1
+ # @tstdl/base/formats
2
+
3
+ A comprehensive utility module for locale-aware formatting of numbers, dates, times, currencies, and names. It leverages the standard `Intl` API with aggressive memoization to ensure high performance in rendering-heavy applications.
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
+ - **Locale-Aware**: Defaults to `de-DE` (German) but fully configurable.
16
+ - **High Performance**: Uses memoization to cache `Intl.NumberFormat` and `Intl.DateTimeFormat` instances, avoiding expensive instantiation costs.
17
+ - **Type Safety**: Fully typed in TypeScript.
18
+ - **Rich Set of Formatters**:
19
+ - Integers, Decimals, Percentages.
20
+ - Currencies (with and without cents, specific Euro helpers).
21
+ - Dates (Short, Medium, Long, Numeric) and Times.
22
+ - Person Names (First/Last name handling).
23
+ - **Numeric Date Support**: Utilities to format dates stored as integers (e.g., `20231231`).
24
+
25
+ ## Core Concepts
26
+
27
+ ### Global Configuration
28
+
29
+ The module operates with a global locale setting (defaulting to `de-DE`). This is designed for applications that typically run in a single locale context (like a server-side renderer or a client-side SPA). You can configure this once at startup.
30
+
31
+ ### Memoization
32
+
33
+ Creating `Intl.NumberFormat` or `Intl.DateTimeFormat` instances can be slow. This module creates these formatters once per locale/option combination and reuses them. This makes functions like `formatDecimal` or `formatDate` safe to use inside tight loops or render functions.
34
+
35
+ ## 🚀 Basic Usage
36
+
37
+ ### Configuration
38
+
39
+ Set the locale at the entry point of your application.
40
+
41
+ ```ts
42
+ import { configureFormats } from '@tstdl/base/formats';
43
+
44
+ // Set to US English
45
+ configureFormats({ locale: 'en-US' });
46
+ ```
47
+
48
+ ### Number Formatting
49
+
50
+ ```ts
51
+ import { formatInteger, formatDecimal, formatPercent } from '@tstdl/base/formats';
52
+
53
+ // Assuming locale is de-DE
54
+ console.log(formatInteger(12345.67));
55
+ // Output: "12.346" (rounded)
56
+
57
+ console.log(formatDecimal(12345.6789));
58
+ // Output: "12.345,68" (default 2 fraction digits)
59
+
60
+ console.log(formatDecimal(12345.6789, { minimumFractionDigits: 3, maximumFractionDigits: 4 }));
61
+ // Output: "12.345,679" (min 3, max 4 fraction digits)
62
+
63
+ console.log(formatPercent(0.1234));
64
+ // Output: "12,34 %"
65
+ ```
66
+
67
+ ### Currency Formatting
68
+
69
+ ```ts
70
+ import { formatEuro, formatCurrency, formatCurrencyWithoutCents } from '@tstdl/base/formats';
71
+
72
+ console.log(formatEuro(1500.5));
73
+ // Output: "1.500,50 €"
74
+
75
+ console.log(formatCurrency(1500.5, 'USD'));
76
+ // Output: "1.500,50 $" (Format depends on locale, symbol depends on currency)
77
+
78
+ console.log(formatCurrencyWithoutCents(1500.5, 'EUR'));
79
+ // Output: "1.501 €" (Rounded)
80
+ ```
81
+
82
+ ### Date and Time Formatting
83
+
84
+ ```ts
85
+ import { formatDate, formatDateShort, formatTimeShort } from '@tstdl/base/formats';
86
+
87
+ const date = new Date('2023-10-05T14:30:00');
88
+
89
+ console.log(formatDate(date));
90
+ // Output: "05.10.2023" (Standard date format for de-DE)
91
+
92
+ console.log(formatDateShort(date));
93
+ // Output: "05.10.2023"
94
+
95
+ // formatTimeShort expects milliseconds for the time portion
96
+ // (Internally often used with durations or specific times of day)
97
+ console.log(formatTimeShort(date));
98
+ // Output: "14:30"
99
+ ```
100
+
101
+ ## 🔧 Advanced Topics
102
+
103
+ ### Formatting Person Names
104
+
105
+ The `formatPersonName` helper handles objects with `firstName` and `lastName` properties, dealing with nulls and formatting preferences.
106
+
107
+ ```ts
108
+ import { formatPersonName } from '@tstdl/base/formats';
109
+
110
+ const user = { firstName: 'John', lastName: 'Doe' };
111
+ const incompleteUser = { firstName: null, lastName: 'Doe' };
112
+
113
+ console.log(formatPersonName(user));
114
+ // Output: "John Doe"
115
+
116
+ console.log(formatPersonName(user, { lastNameFirst: true }));
117
+ // Output: "Doe, John"
118
+
119
+ console.log(formatPersonName(incompleteUser));
120
+ // Output: "Doe"
121
+
122
+ console.log(formatPersonName(null, { fallback: 'Anonymous' }));
123
+ // Output: "Anonymous"
124
+ ```
125
+
126
+ ### Numeric Dates
127
+
128
+ Often databases store dates as integers (e.g., `20231231`) for indexing efficiency. The `formatNumericDate` function handles this conversion automatically.
129
+
130
+ ```ts
131
+ import { formatNumericDate } from '@tstdl/base/formats';
132
+
133
+ // Formats integer 20231231 as a date
134
+ console.log(formatNumericDate(20231231));
135
+ // Output: "31.12.2023"
136
+ ```
137
+
138
+ ### Custom Number Formatting
139
+
140
+ You can pass standard `Intl.NumberFormatOptions` to `formatNumber` for one-off requirements that aren't covered by the standard helpers.
141
+
142
+ ```ts
143
+ import { formatNumber } from '@tstdl/base/formats';
144
+
145
+ console.log(
146
+ formatNumber(1234.567, {
147
+ style: 'unit',
148
+ unit: 'kilometer',
149
+ maximumFractionDigits: 1,
150
+ }),
151
+ );
152
+ // Output: "1.234,6 km"
153
+ ```
154
+
155
+ ## 📚 API
156
+
157
+ ### Configuration
158
+
159
+ | Function | Description |
160
+ | :-------------------------- | :--------------------------------------------------------------------------------------- |
161
+ | `configureFormats(options)` | Sets the global locale used by all formatters. Default is `de-DE`. Can be a static string or a provider function. |
162
+
163
+ ### Number Formatters
164
+
165
+ | Function | Description |
166
+ | :----------------------------- | :-------------------------------------------------------------- |
167
+ | `formatNumber(value, format?)` | Formats a number using optional `Intl.NumberFormatOptions`. |
168
+ | `formatInteger(value, format?)` | Formats as an integer (0 fraction digits). |
169
+ | `formatDecimal(value, format?)` | Formats as a decimal. Default min/max fraction digits is 2. |
170
+ | `formatPercent(value, format?)` | Formats a ratio (0-1) as a percentage (0-100%). |
171
+
172
+ ### Currency Formatters
173
+
174
+ | Function | Description |
175
+ | :-------------------------------------------- | :------------------------------------------------------- |
176
+ | `formatCurrency(value, currency, format?)` | Formats value as currency (e.g., 'EUR', 'USD'). |
177
+ | `formatCurrencyWithoutCents(value, currency, format?)` | Formats currency rounded to the nearest integer. |
178
+ | `formatEuro(value, format?)` | Shortcut for `formatCurrency(value, 'EUR')`. |
179
+ | `formatEuroWithoutCents(value, format?)` | Shortcut for `formatCurrencyWithoutCents(value, 'EUR')`. |
180
+
181
+ ### Date & Time Formatters
182
+
183
+ | Function | Description |
184
+ | :------------------------------------ | :------------------------------------------------------------------------- |
185
+ | `formatDateTime(value, format?)` | Formats a `Date`, timestamp or `DateTime` object using optional `Intl.DateTimeFormatOptions`. |
186
+ | `formatDate(dateOrTimestamp, format?)` | Formats using `dateShort` options (numeric day, month, 2-digit year). |
187
+ | `formatDateShort(value, format?)` | Alias for `formatDate`. |
188
+ | `formatTimeShort(value, format?)` | Formats a time value to `HH:MM`. |
189
+ | `formatNumericDate(numericDate, options?)` | Converts a `YYYYMMDD` integer to a formatted date string (`dateShort`). |
190
+ | `formatNumericDateShort(numericDate, options?)` | Alias for `formatNumericDate`. |
191
+ | `formatNumericDateLong(numericDate, options?)` | Converts a `YYYYMMDD` integer to a long date format (`dateLong`). |
192
+
193
+ ### Utilities
194
+
195
+ | Function | Description |
196
+ | :----------------------------------- | :--------------------------------------------------------------------------------------------------------- |
197
+ | `formatPersonName(person, options?)` | Formats a name from an object `{ firstName?, lastName? }`. Options include `lastNameFirst` and `fallback`. |
198
+
199
+ ### Constants (Format Options)
200
+
201
+ The module exports standard `Intl` configuration objects used internally. These can be useful if you need to instantiate your own formatters with the same styles.
202
+
203
+ - `integerFormat`
204
+ - `decimalFormat`
205
+ - `decimal1Format`
206
+ - `dateTimeNumeric`, `dateTimeShort`, `dateTimeLong`
207
+ - `dateShort`, `dateMedium`, `dateLong`
208
+ - `timeShort`
209
+ - `currencyFormat`, `currencyFormatWithoutCents`
210
+ - `percentFormat`
@@ -0,0 +1,144 @@
1
+ # Function
2
+
3
+ A utility module providing tools for function manipulation and debugging. It currently focuses on transparently wrapping functions to log execution details, 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
+ - [Custom Logger](#custom-logger)
12
+ - [Enabling Stack Traces](#enabling-stack-traces)
13
+ - [📚 API](#-api)
14
+
15
+ ## ✨ Features
16
+
17
+ - **Transparent Logging**: Wrap any function to automatically log when it is called and what it returns.
18
+ - **Argument Formatting**: Automatically formats primitive arguments and arrays for readable log output.
19
+ - **Custom Loggers**: Integrate with the `@tstdl/base/logger` or any compatible logger interface.
20
+ - **Stack Traces**: Optionally print stack traces on every call for deep debugging.
21
+ - **Metadata Control**: Override the function name used in logs for better context.
22
+
23
+ ## Core Concepts
24
+
25
+ The `function` module provides high-order functions that take an existing function and return a new one with added behavior.
26
+
27
+ ### Function Wrapping
28
+
29
+ The primary utility, `wrapLog`, acts as a functional decorator. It intercepts calls to the target function, logs the arguments, executes the original function, and then logs the result. This is particularly useful for debugging complex logic flows or inspecting data transformations without modifying the original source code with temporary `console.log` statements.
30
+
31
+ ## 🚀 Basic Usage
32
+
33
+ The most common use case is wrapping a function to see its inputs and outputs in the console.
34
+
35
+ ```typescript
36
+ import { wrapLog } from '@tstdl/base/function';
37
+
38
+ function calculateSum(a: number, b: number): number {
39
+ return a + b;
40
+ }
41
+
42
+ // Wrap the function. By default, it uses console.log (via console.log.bind(console))
43
+ const loggedCalculateSum = wrapLog(calculateSum);
44
+
45
+ // Execute the wrapped function
46
+ loggedCalculateSum(5, 10);
47
+
48
+ // Console Output:
49
+ // [call: calculateSum(5, 10)]
50
+ // [return: calculateSum => 15]
51
+ ```
52
+
53
+ ## 🔧 Advanced Topics
54
+
55
+ ### Custom Logger
56
+
57
+ You can direct the output to a specific logger instance (e.g., from `@tstdl/base/logger`) instead of `console.log`. The wrapper uses the `trace` method of the logger.
58
+
59
+ ```typescript
60
+ import { wrapLog } from '@tstdl/base/function';
61
+ import { Logger } from '@tstdl/base/logger';
62
+ import { inject } from '@tstdl/base/injector';
63
+
64
+ class MathService {
65
+ private logger = inject(Logger, 'MathService');
66
+
67
+ add(a: number, b: number): number {
68
+ return a + b;
69
+ }
70
+
71
+ constructor() {
72
+ // Replace the method with a logged version using the service's logger
73
+ this.add = wrapLog(this.add.bind(this), {
74
+ logger: this.logger,
75
+ fnName: 'MathService.add', // Custom name for clearer logs
76
+ }) as any;
77
+ }
78
+ }
79
+ ```
80
+
81
+ ### Enabling Stack Traces
82
+
83
+ For debugging call chains, you can enable the `trace` option. This will output a stack trace every time the function is called.
84
+
85
+ ```typescript
86
+ import { wrapLog } from '@tstdl/base/function';
87
+
88
+ function criticalOperation() {
89
+ // ... logic
90
+ }
91
+
92
+ const debugOperation = wrapLog(criticalOperation, {
93
+ trace: true,
94
+ logResult: false, // Only log the call and trace, ignore the return value
95
+ });
96
+
97
+ debugOperation();
98
+ // Output:
99
+ // [call: criticalOperation()]
100
+ // Trace: ... (stack trace)
101
+ ```
102
+
103
+ ## 📚 API
104
+
105
+ ### Functions
106
+
107
+ | Function | Description |
108
+ | :---------------------- | :--------------------------------------------------------------- |
109
+ | `wrapLog(fn, options?)` | Wraps a function to log its calls, arguments, and return values. |
110
+
111
+ ### Types
112
+
113
+ | Type | Description |
114
+ | :--------------- | :---------------------------------- |
115
+ | `WrapLogOptions` | Configuration object for `wrapLog`. |
116
+
117
+ #### WrapLogOptions
118
+
119
+ ```typescript
120
+ type WrapLogOptions = {
121
+ /**
122
+ * The name to use in log messages. Defaults to fn.name.
123
+ */
124
+ fnName?: string;
125
+
126
+ /**
127
+ * Whether to log the return value of the function.
128
+ * Default: true
129
+ */
130
+ logResult?: boolean;
131
+
132
+ /**
133
+ * A Logger instance to use. If not provided, defaults to console.log.
134
+ * The wrapper calls logger.trace().
135
+ */
136
+ logger?: Logger;
137
+
138
+ /**
139
+ * Whether to print a stack trace (console.trace) on execution.
140
+ * Default: false
141
+ */
142
+ trace?: boolean;
143
+ };
144
+ ```
package/http/README.md ADDED
@@ -0,0 +1,318 @@
1
+ # @tstdl/base/http
2
+
3
+ A powerful, isomorphic, middleware-based HTTP client and server library for TypeScript. This module provides a comprehensive suite of tools for handling HTTP communication, featuring a flexible client with a middleware pipeline and a modern, async-iterable-based server abstraction.
4
+
5
+ ## Table of Contents
6
+
7
+ - [✨ Features](#-features)
8
+ - [Core Concepts](#core-concepts)
9
+ - [HTTP Client](#http-client)
10
+ - [HTTP Server](#http-server)
11
+ - [Isomorphic Utilities](#isomorphic-utilities)
12
+ - [🚀 Basic Usage](#-basic-usage)
13
+ - [Client Setup](#client-setup)
14
+ - [Making Requests](#making-requests)
15
+ - [Server Setup](#server-setup)
16
+ - [Handling Requests](#handling-requests)
17
+ - [🔧 Advanced Topics](#-advanced-topics)
18
+ - [Client Middleware](#client-middleware)
19
+ - [Streaming Data](#streaming-data)
20
+ - [Error Handling](#error-handling)
21
+ - [Automatic Parameter Mapping](#automatic-parameter-mapping)
22
+ - [📚 API](#-api)
23
+
24
+ ## ✨ Features
25
+
26
+ - **Isomorphic Design:** Shared abstractions for client and server, with specific adapters for different environments (e.g., `undici` for Node.js).
27
+ - **Middleware-Driven Client:** Intercept and modify requests/responses using a flexible async middleware pipeline for concerns like logging, authentication, and caching.
28
+ - **Pluggable Client Adapter:** The `HttpClientAdapter` allows swapping the underlying HTTP engine. `UndiciHttpClientAdapter` is provided for high-performance Node.js applications.
29
+ - **Modern Server Abstraction:** The `HttpServer` uses an async iterator pattern (`for await...of`) for elegant and efficient request handling.
30
+ - **Advanced Body Handling:** Seamlessly works with JSON, text, forms (`x-www-form-urlencoded`), `FormData`, binary (`Uint8Array`, `Blob`), and `ReadableStream`.
31
+ - **Automatic Parameter Mapping:** Intelligently maps request parameters to URL path segments, query strings, or the request body.
32
+ - **Typed API:** Strongly typed interfaces for requests, responses, headers, and query parameters to catch errors at compile time.
33
+ - **Utility Classes:** Helpers like `HttpHeaders`, `HttpQuery`, and `HttpForm` simplify common tasks.
34
+
35
+ ## Core Concepts
36
+
37
+ ### HTTP Client
38
+
39
+ The client is designed around three main components: `HttpClient`, `HttpClientAdapter`, and `HttpClientMiddleware`.
40
+
41
+ - **`HttpClient`**: The primary interface for making requests. It manages configuration, default headers, and the middleware pipeline. It offers convenience methods like `getJson()`, `post()`, etc. The flow of a request is: `HttpClient` -> `Middleware Chain` -> `HttpClientAdapter`.
42
+ - **`HttpClientAdapter`**: The "engine" that performs the actual HTTP call. The library is decoupled from any specific implementation. For Node.js, the `UndiciHttpClientAdapter` is provided, which uses the high-performance `undici` library.
43
+ - **`HttpClientMiddleware`**: A function that intercepts a request before it's sent and the response after it's received. Middleware is composed into a pipeline.
44
+
45
+ ### HTTP Server
46
+
47
+ The server implementation is based on the `HttpServer` abstract class.
48
+
49
+ - **`HttpServer`**: An async iterable that yields a `HttpServerRequestContext` for each incoming connection. This design promotes a clean, modern loop for processing requests (`for await (const context of server)`).
50
+ - **`HttpServerRequestContext`**: Encapsulates the `request` (incoming) and a `respond` function (outgoing).
51
+ - **`NodeHttpServer`**: The default implementation of `HttpServer` for Node.js, built on top of the native `node:http` module.
52
+
53
+ ### Isomorphic Utilities
54
+
55
+ - **`HttpHeaders`**: A map-like class for managing HTTP headers with typed getters for common headers (e.g., `contentType`, `contentLength`).
56
+ - **`HttpBody`**: A unified interface for reading request/response bodies. It supports reading as JSON, text, buffer, or stream, regardless of the source (Node stream, Blob, etc.).
57
+
58
+ ## 🚀 Basic Usage
59
+
60
+ ### Client Setup
61
+
62
+ To use the client in a Node.js environment, you need to configure the `UndiciHttpClientAdapter`.
63
+
64
+ ```typescript
65
+ import { HttpClient, configureHttpClient } from '@tstdl/base/http';
66
+ import { configureUndiciHttpClientAdapter } from '@tstdl/base/http/undici';
67
+ import { Injector } from '@tstdl/base/injector';
68
+
69
+ // 1. Configure the adapter (Node.js specific)
70
+ configureUndiciHttpClientAdapter({ register: true });
71
+
72
+ // 2. Configure the client (optional base URL)
73
+ configureHttpClient({
74
+ baseUrl: 'https://api.example.com',
75
+ });
76
+
77
+ // 3. Resolve the client
78
+ const httpClient = Injector.resolve(HttpClient);
79
+ ```
80
+
81
+ ### Making Requests
82
+
83
+ The `HttpClient` provides methods for standard HTTP verbs (`get`, `post`, `put`, `patch`, `delete`, `head`).
84
+
85
+ ```typescript
86
+ // GET JSON
87
+ type User = { id: number; name: string };
88
+ const user = await httpClient.getJson<User>('/users/1');
89
+ console.log(user.name);
90
+
91
+ // POST JSON
92
+ const newUser = { name: 'Alice' };
93
+ const createdUser = await httpClient.postJson<User>('/users', {
94
+ body: { json: newUser },
95
+ });
96
+
97
+ // GET with Query Parameters
98
+ // Result: GET /search?q=typescript&limit=10
99
+ const results = await httpClient.getJson('/search', {
100
+ query: { q: 'typescript', limit: 10 },
101
+ });
102
+ ```
103
+
104
+ ### Server Setup
105
+
106
+ Configure and run the `NodeHttpServer`.
107
+
108
+ ```typescript
109
+ import { HttpServer } from '@tstdl/base/http/server';
110
+ import { configureNodeHttpServer } from '@tstdl/base/http/node';
111
+ import { Injector } from '@tstdl/base/injector';
112
+
113
+ // 1. Register the Node.js server implementation
114
+ configureNodeHttpServer();
115
+
116
+ // 2. Resolve the server
117
+ const httpServer = Injector.resolve(HttpServer);
118
+
119
+ // 3. Start listening
120
+ await httpServer.listen(3000);
121
+ console.log('Server listening on port 3000');
122
+ ```
123
+
124
+ ### Handling Requests
125
+
126
+ Use the async iterator pattern to handle incoming requests sequentially or concurrently.
127
+
128
+ ```typescript
129
+ import { HttpServerResponse } from '@tstdl/base/http/server';
130
+
131
+ // Loop through incoming requests
132
+ for await (const context of httpServer) {
133
+ const { request, respond } = context;
134
+
135
+ // Handle request asynchronously
136
+ (async () => {
137
+ try {
138
+ if (request.url.pathname === '/hello') {
139
+ await respond(
140
+ new HttpServerResponse({
141
+ statusCode: 200,
142
+ body: { text: 'Hello World!' },
143
+ }),
144
+ );
145
+ } else {
146
+ await respond(
147
+ new HttpServerResponse({
148
+ statusCode: 404,
149
+ statusMessage: 'Not Found',
150
+ }),
151
+ );
152
+ }
153
+ } catch (error) {
154
+ console.error(error);
155
+ await respond(new HttpServerResponse({ statusCode: 500 }));
156
+ } finally {
157
+ // Ensure the connection is closed if needed (handled by respond usually)
158
+ await context.close();
159
+ }
160
+ })();
161
+ }
162
+ ```
163
+
164
+ ## 🔧 Advanced Topics
165
+
166
+ ### Client Middleware
167
+
168
+ Middleware allows you to intercept requests and responses. This is useful for authentication, logging, or modifying headers.
169
+
170
+ ```typescript
171
+ import { HttpClient, configureHttpClient, type HttpClientMiddleware, type HttpClientMiddlewareContext, type HttpClientMiddlewareNext } from '@tstdl/base/http';
172
+ import { configureUndiciHttpClientAdapter } from '@tstdl/base/http/undici';
173
+ import { Injector } from '@tstdl/base/injector';
174
+
175
+ const authMiddleware: HttpClientMiddleware = async (context: HttpClientMiddlewareContext, next: HttpClientMiddlewareNext) => {
176
+ // Pre-request logic
177
+ context.request.headers.set('Authorization', 'Bearer my-token');
178
+
179
+ // Execute next middleware / adapter
180
+ await next();
181
+
182
+ // Post-response logic
183
+ if (context.response?.statusCode === 401) {
184
+ console.warn('Unauthorized request!');
185
+ }
186
+ };
187
+
188
+ configureUndiciHttpClientAdapter({ register: true });
189
+ configureHttpClient({
190
+ middleware: [authMiddleware],
191
+ });
192
+
193
+ const client = Injector.resolve(HttpClient);
194
+ ```
195
+
196
+ ### Streaming Data
197
+
198
+ Both the client and server support streaming, which is essential for large files or real-time data.
199
+
200
+ **Client Streaming (Download):**
201
+
202
+ ```typescript
203
+ const stream = httpClient.getBinaryStream('/large-file.zip');
204
+ // stream is a ReadableStream<Uint8Array>
205
+
206
+ for await (const chunk of stream) {
207
+ console.log(`Received chunk of size: ${chunk.length}`);
208
+ }
209
+ ```
210
+
211
+ **Server Streaming (Response):**
212
+
213
+ ```typescript
214
+ import { HttpServerResponse } from '@tstdl/base/http/server';
215
+ import { readableStreamFromPromise } from '@tstdl/base/utils/stream';
216
+
217
+ // ... inside request loop
218
+ await respond(
219
+ new HttpServerResponse({
220
+ statusCode: 200,
221
+ headers: { 'Content-Type': 'text/plain' },
222
+ body: {
223
+ // Create a stream that yields data
224
+ stream: readableStreamFromPromise(async function* () {
225
+ yield new TextEncoder().encode('Chunk 1\n');
226
+ await new Promise((r) => setTimeout(r, 1000));
227
+ yield new TextEncoder().encode('Chunk 2\n');
228
+ }),
229
+ },
230
+ }),
231
+ );
232
+ ```
233
+
234
+ ### Error Handling
235
+
236
+ The `HttpClient` automatically throws an `HttpError` for non-2xx responses if `throwOnNon200` is true (default).
237
+
238
+ ```typescript
239
+ import { HttpError, HttpErrorReason } from '@tstdl/base/http';
240
+
241
+ try {
242
+ await httpClient.get('/non-existent');
243
+ } catch (error) {
244
+ if (error instanceof HttpError) {
245
+ console.log('Reason:', error.reason); // e.g., HttpErrorReason.StatusCode
246
+ console.log('Status:', error.response?.statusCode); // 404
247
+
248
+ // Access the response body if needed
249
+ const body = await error.responseInstance?.body.readAsText();
250
+ console.log('Error Body:', body);
251
+ }
252
+ }
253
+ ```
254
+
255
+ ### Automatic Parameter Mapping
256
+
257
+ The `HttpClientRequest` can automatically map a `parameters` object to the URL path, query string, or body based on the URL pattern and HTTP method.
258
+
259
+ ```typescript
260
+ // URL: /users/:userId
261
+ // Method: GET
262
+ // Result: GET /users/123?details=true
263
+ await httpClient.getJson('/users/:userId', {
264
+ parameters: {
265
+ userId: 123, // Mapped to URL path because :userId exists
266
+ details: true, // Mapped to Query because it's GET and not in URL path
267
+ },
268
+ });
269
+
270
+ // URL: /users
271
+ // Method: POST
272
+ // Result: POST /users with JSON body { name: 'Bob' }
273
+ await httpClient.postJson('/users', {
274
+ parameters: {
275
+ name: 'Bob', // Mapped to Body because it's POST and no URL params match
276
+ },
277
+ });
278
+ ```
279
+
280
+ ## 📚 API
281
+
282
+ ### Configuration Functions
283
+
284
+ | Function | Description |
285
+ | :------------------------------------------ | :------------------------------------------------------------------- |
286
+ | `configureHttpClient(config)` | Configures the global `HttpClient` options, adapter, and middleware. |
287
+ | `configureUndiciHttpClientAdapter(options)` | Configures and registers the `UndiciHttpClientAdapter` (Node.js). |
288
+ | `configureNodeHttpServer(config)` | Registers the `NodeHttpServer` as the default `HttpServer`. |
289
+
290
+ ### Client
291
+
292
+ | Class | Description |
293
+ | :------------------------ | :-------------------------------------------------------------------------------- |
294
+ | `HttpClient` | Main service for making HTTP requests. |
295
+ | `HttpClientRequest` | Represents an outgoing request. Handles parameter mapping and body normalization. |
296
+ | `HttpClientResponse` | Represents an incoming response. Provides access to status, headers, and body. |
297
+ | `HttpClientAdapter` | Abstract base class for HTTP adapters. |
298
+ | `UndiciHttpClientAdapter` | Adapter implementation using `undici` (Node.js). |
299
+
300
+ ### Server
301
+
302
+ | Class | Description |
303
+ | :------------------- | :---------------------------------------------------------------- |
304
+ | `HttpServer` | Abstract base class for HTTP servers. Implements `AsyncIterable`. |
305
+ | `NodeHttpServer` | Server implementation using `node:http`. |
306
+ | `HttpServerRequest` | Represents an incoming request on the server. |
307
+ | `HttpServerResponse` | Represents an outgoing response from the server. |
308
+
309
+ ### Shared / Utilities
310
+
311
+ | Class | Description |
312
+ | :------------- | :----------------------------------------------------------------------- |
313
+ | `HttpHeaders` | Typed wrapper around HTTP headers. |
314
+ | `HttpBody` | Helper to read request/response bodies as Text, JSON, Buffer, or Stream. |
315
+ | `HttpQuery` | Helper for managing URL query parameters. |
316
+ | `HttpForm` | Helper for `application/x-www-form-urlencoded` data. |
317
+ | `HttpError` | Error thrown when requests fail or return non-success status codes. |
318
+ | `CookieParser` | Utility to parse `Cookie` headers. |
@@ -53,7 +53,7 @@ let UndiciHttpClientAdapter = class UndiciHttpClientAdapter extends HttpClientAd
53
53
  try {
54
54
  const response = await request(httpClientRequest.url, {
55
55
  method: httpClientRequest.method,
56
- signal: httpClientRequest.abortSignal.asAbortSignal(),
56
+ signal: httpClientRequest.cancellationSignal.abortSignal,
57
57
  headers: httpClientRequest.headers.asNormalizedObject(),
58
58
  body,
59
59
  headersTimeout: httpClientRequest.timeout,