@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,121 @@
1
+ # @tstdl/base/enumeration
2
+
3
+ A lightweight utility for creating and registering named, type-safe enumerations in TypeScript using plain objects. It provides runtime name introspection capabilities often missing from native TypeScript enums, making it ideal for framework development, serialization, and logging.
4
+
5
+ ## Table of Contents
6
+
7
+ - [✨ Features](#-features)
8
+ - [Core Concepts](#core-concepts)
9
+ - [🚀 Basic Usage](#-basic-usage)
10
+ - [Defining a String Enum](#defining-a-string-enum)
11
+ - [Defining a Numeric Enum](#defining-a-numeric-enum)
12
+ - [🔧 Advanced Topics](#-advanced-topics)
13
+ - [Retrieving an Enum's Name](#retrieving-an-enums-name)
14
+ - [Working with Enum Values (Utilities)](#working-with-enum-values-utilities)
15
+ - [💡 Best Practices](#-best-practices)
16
+ - [📚 API](#-api)
17
+
18
+ ## ✨ Features
19
+
20
+ - **Named Enums:** Associate a string name with an enum-like object, retrievable at runtime.
21
+ - **Type-Safe:** Leverages TypeScript's `const` generics to create precise, type-safe union types from plain objects.
22
+ - **Mixed Types:** Supports both string and numeric values within the same enumeration structure.
23
+ - **Lightweight:** A minimal implementation with zero external dependencies.
24
+ - **Memory-Efficient:** Uses a `WeakMap` to store enum names, allowing for automatic garbage collection if the enum object is no longer referenced.
25
+
26
+ ## Core Concepts
27
+
28
+ Native TypeScript `enum`s have limitations, especially for framework development. Their declared name is lost at compile time (transpiled away), and numeric enums have complex runtime behavior (reverse mappings). This makes it difficult for systems like ORMs, serializers, or validators to identify an enum type by a consistent name at runtime.
29
+
30
+ This module provides a simple and robust alternative. The `defineEnum` function allows you to use a standard JavaScript object as an enum. It registers the object with a unique string name in a global, memory-safe registry (`WeakMap`). This name can be retrieved at runtime using `getEnumName`, enabling powerful metaprogramming scenarios where you need to reference the enum's "type" by name.
31
+
32
+ ## 🚀 Basic Usage
33
+
34
+ ### Defining a String Enum
35
+
36
+ Use the `defineEnum` function to create your enumeration object and register its name. The `EnumType` utility creates a corresponding TypeScript type for the enum values.
37
+
38
+ ```typescript
39
+ import { defineEnum, type EnumType } from '@tstdl/base/enumeration';
40
+
41
+ export const OrderStatus = defineEnum('OrderStatus', {
42
+ Pending: 'pending',
43
+ Processing: 'processing',
44
+ Shipped: 'shipped',
45
+ Delivered: 'delivered',
46
+ });
47
+
48
+ export type OrderStatus = EnumType<typeof OrderStatus>;
49
+
50
+ const status: OrderStatus = OrderStatus.Shipped; // Type is "pending" | "processing" | "shipped" | "delivered"
51
+ ```
52
+
53
+ ### Defining a Numeric Enum
54
+
55
+ The utility works equally well with numeric values.
56
+
57
+ ```typescript
58
+ import { defineEnum, type EnumType } from '@tstdl/base/enumeration';
59
+
60
+ export const Priority = defineEnum('Priority', {
61
+ Low: 0,
62
+ Medium: 1,
63
+ High: 2,
64
+ Urgent: 3,
65
+ });
66
+
67
+ export type Priority = EnumType<typeof Priority>;
68
+
69
+ const taskPriority: Priority = Priority.High; // Type is 0 | 1 | 2 | 3
70
+ ```
71
+
72
+ ## 🔧 Advanced Topics
73
+
74
+ ### Retrieving an Enum's Name
75
+
76
+ You can retrieve the registered name of an enum object. This is particularly useful for logging, error messages, or when building libraries that need to serialize the type name of an enum.
77
+
78
+ ```typescript
79
+ import { defineEnum, getEnumName, tryGetEnumName } from '@tstdl/base/enumeration';
80
+
81
+ const UserRole = defineEnum('UserRole', {
82
+ Admin: 'admin',
83
+ Editor: 'editor',
84
+ });
85
+
86
+ const name = getEnumName(UserRole); // "UserRole"
87
+ const safeName = tryGetEnumName({}); // undefined
88
+ ```
89
+
90
+ ### Working with Enum Values (Utilities)
91
+
92
+ For practical operations like listing all values or finding a key by value, use the utility functions provided in `#/utils/enum.js`.
93
+
94
+ ```typescript
95
+ import { enumValues, enumKeys, enumValueName } from '@tstdl/base/utils/enum';
96
+ import { OrderStatus } from './order-status.js';
97
+
98
+ const allStatuses = enumValues(OrderStatus); // ["pending", "processing", ...]
99
+ const allKeys = enumKeys(OrderStatus); // ["Pending", "Processing", ...]
100
+ const name = enumValueName(OrderStatus, 'pending'); // "Pending"
101
+ ```
102
+
103
+ ## 💡 Best Practices
104
+
105
+ 1. **Always use `defineEnum`:** Ensure every enum-like object in the codebase is wrapped in `defineEnum` to maintain consistency and support runtime introspection.
106
+ 2. **Export both Value and Type:** Export the constant object and a type alias with the same name for a better developer experience.
107
+ ```typescript
108
+ export const MyEnum = defineEnum('MyEnum', { ... });
109
+ export type MyEnum = EnumType<typeof MyEnum>;
110
+ ```
111
+ 3. **Unique Names:** Ensure the string name passed to `defineEnum` is unique across the application, typically matching the variable name.
112
+ 4. **Avoid Native Enums:** Prefer this pattern over native TypeScript `enum` to avoid unexpected runtime behavior and missing metadata.
113
+
114
+ ## 📚 API
115
+
116
+ | Member | Signature | Description |
117
+ | ------------------ | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
118
+ | `defineEnum()` | `function defineEnum<const T extends EnumerationObject>(name: string, enumObject: T): T` | Registers an `enumObject` with a given `name` and returns it. The `<const T>` generic ensures that the returned type is inferred as a set of literal types, providing strong type safety. |
119
+ | `getEnumName()` | `function getEnumName(enumeration: EnumerationObject): string` | Retrieves the registered name for an enum object. Throws an `Error` if the enum is not registered. |
120
+ | `tryGetEnumName()` | `function tryGetEnumName(enumeration: EnumerationObject): string \| undefined` | Safely retrieves the registered name for an enum object. Returns `undefined` if not registered. |
121
+ | `EnumType` | `type EnumType<T extends EnumerationObject> = T[keyof T]` | A utility type that creates a union type from an enum-like object's values. |
@@ -0,0 +1,267 @@
1
+ # Errors
2
+
3
+ A comprehensive collection of custom, robust, and extensible error classes for TypeScript applications. It provides a standardized hierarchy for common failure scenarios, performance optimizations, and built-in localization support.
4
+
5
+ ## Table of Contents
6
+
7
+ - [✨ Features](#-features)
8
+ - [Core Concepts](#core-concepts)
9
+ - [The CustomError Base](#the-customerror-base)
10
+ - [Standardized Error Types](#standardized-error-types)
11
+ - [Localization](#localization)
12
+ - [🚀 Basic Usage](#-basic-usage)
13
+ - [🔧 Advanced Topics](#-advanced-topics)
14
+ - [Creating Custom Errors](#creating-custom-errors)
15
+ - [Attaching Details to Errors](#attaching-details-to-errors)
16
+ - [Aggregating Multiple Errors](#aggregating-multiple-errors)
17
+ - [Formatting and Serialization](#formatting-and-serialization)
18
+ - [Performance Optimization (Fast Mode)](#performance-optimization-fast-mode)
19
+ - [Unwrapping Errors](#unwrapping-errors)
20
+ - [📚 API](#-api)
21
+
22
+ ## ✨ Features
23
+
24
+ - **Extensible Base Class:** `CustomError` simplifies creating domain-specific errors with support for causes and stack trace management.
25
+ - **Standard Library:** Includes a rich set of pre-defined errors for HTTP status codes (400, 401, 403, 404, etc.) and common application failures.
26
+ - **Type Safety:** Designed for use with `instanceof` checks to allow precise error handling logic.
27
+ - **Contextual Data:** `DetailsError` allows attaching arbitrary structured data to an error.
28
+ - **Error Aggregation:** `MultiError` groups multiple errors into a single throwable object.
29
+ - **Performance Mode:** Optional `fast` mode skips stack trace generation for high-throughput scenarios.
30
+ - **Localization:** Built-in English and German localization maps for user-facing error messages.
31
+
32
+ ## Core Concepts
33
+
34
+ ### The CustomError Base
35
+
36
+ All errors in this module inherit from `CustomError`. This abstract class extends the native JavaScript `Error` and adds:
37
+
38
+ - **Static `errorName`**: A reliable identifier for the error type, safe against code minification.
39
+ - **Options Object**: A constructor argument to set the message, cause, stack, and performance flags.
40
+
41
+ ### Standardized Error Types
42
+
43
+ The module provides specific classes for common scenarios, such as:
44
+
45
+ - **Authentication/Authorization**: `UnauthorizedError`, `ForbiddenError`, `InvalidCredentialsError`, `InvalidTokenError`.
46
+ - **Request Handling**: `BadRequestError`, `NotFoundError`, `MethodNotAllowedError`, `UnsupportedMediaTypeError`, `MaxBytesExceededError`.
47
+ - **System State**: `NotImplementedError`, `NotSupportedError`, `TimeoutError`, `AssertionError`.
48
+
49
+ ### Localization
50
+
51
+ The module exports `englishTstdlErrorsLocalization` and `germanTstdlErrorsLocalization`. These objects map error classes to user-friendly headers and messages, making it easier to display errors in a UI without hardcoding strings.
52
+
53
+ ## 🚀 Basic Usage
54
+
55
+ The most common use case is throwing standard errors to interrupt control flow when a specific condition is not met.
56
+
57
+ ```ts
58
+ import { NotFoundError, ForbiddenError } from '@tstdl/base/errors';
59
+
60
+ interface User {
61
+ id: string;
62
+ isAdmin: boolean;
63
+ }
64
+
65
+ function getUser(id: string, currentUser: User): User {
66
+ // Simulate a database lookup
67
+ const user = database.find(id);
68
+
69
+ if (!user) {
70
+ throw new NotFoundError(`User with ID "${id}" was not found.`);
71
+ }
72
+
73
+ if (!currentUser.isAdmin && currentUser.id !== user.id) {
74
+ throw new ForbiddenError('You do not have permission to view this user.');
75
+ }
76
+
77
+ return user;
78
+ }
79
+
80
+ // Mock database for the example
81
+ const database = {
82
+ find: (id: string) => (id === '123' ? { id: '123', isAdmin: false } : null),
83
+ };
84
+ ```
85
+
86
+ Handling errors using `instanceof` ensures you only catch and process specific types.
87
+
88
+ ```ts
89
+ import { NotFoundError, ForbiddenError } from '@tstdl/base/errors';
90
+
91
+ try {
92
+ getUser('999', { id: '123', isAdmin: false });
93
+ } catch (error) {
94
+ if (error instanceof NotFoundError) {
95
+ console.error('Resource missing:', error.message);
96
+ } else if (error instanceof ForbiddenError) {
97
+ console.error('Access denied:', error.message);
98
+ } else {
99
+ console.error('Unexpected error:', error);
100
+ throw error; // Re-throw unknown errors
101
+ }
102
+ }
103
+ ```
104
+
105
+ ## 🔧 Advanced Topics
106
+
107
+ ### Creating Custom Errors
108
+
109
+ Extend `CustomError` to define domain-specific errors. Define a static `errorName` to ensure the error can be reliably identified even if class names are mangled during build processes.
110
+
111
+ ```ts
112
+ import { CustomError } from '@tstdl/base/errors';
113
+
114
+ export class InsufficientFundsError extends CustomError {
115
+ static readonly errorName = 'InsufficientFundsError';
116
+
117
+ constructor(accountId: string, required: number, available: number) {
118
+ super({
119
+ message: `Account ${accountId} has insufficient funds. Required: ${required}, Available: ${available}.`,
120
+ });
121
+ }
122
+ }
123
+
124
+ // Usage
125
+ throw new InsufficientFundsError('ACC-123', 100.0, 50.0);
126
+ ```
127
+
128
+ ### Attaching Details to Errors
129
+
130
+ Use `DetailsError` when you need to pass structured data along with the error message. This is useful for validation errors or passing context to an error handler.
131
+
132
+ ```ts
133
+ import { DetailsError } from '@tstdl/base/errors';
134
+
135
+ type ValidationFailure = {
136
+ field: string;
137
+ reason: string;
138
+ };
139
+
140
+ function validateProfile(profile: any): void {
141
+ const failures: ValidationFailure[] = [];
142
+
143
+ if (!profile.username) {
144
+ failures.push({ field: 'username', reason: 'Required' });
145
+ }
146
+
147
+ if (failures.length > 0) {
148
+ throw new DetailsError('Validation failed', failures);
149
+ }
150
+ }
151
+
152
+ try {
153
+ validateProfile({});
154
+ } catch (error) {
155
+ if (error instanceof DetailsError) {
156
+ console.log('Validation details:', error.details);
157
+ }
158
+ }
159
+ ```
160
+
161
+ ### Aggregating Multiple Errors
162
+
163
+ `MultiError` is useful when an operation performs multiple independent tasks (like processing a batch of files) and you want to report all failures at the end.
164
+
165
+ ```ts
166
+ import { MultiError } from '@tstdl/base/errors';
167
+
168
+ function processItems(items: string[]): void {
169
+ const errors: Error[] = [];
170
+
171
+ for (const item of items) {
172
+ try {
173
+ // potentially failing operation
174
+ if (item === 'fail') throw new Error('Failed item');
175
+ } catch (error) {
176
+ if (error instanceof Error) {
177
+ errors.push(error);
178
+ }
179
+ }
180
+ }
181
+
182
+ if (errors.length > 0) {
183
+ throw new MultiError(errors, `Processed items with ${errors.length} errors.`);
184
+ }
185
+ }
186
+ ```
187
+
188
+ ### Formatting and Serialization
189
+
190
+ When logging errors or sending them over the wire (e.g., in a REST API response), you often need a structured or stringified representation. The `serializeError` and `formatError` functions handle this, including support for nested causes and `MultiError` aggregation.
191
+
192
+ ```ts
193
+ import { formatError, serializeError, ForbiddenError } from '@tstdl/base/errors';
194
+
195
+ const error = new ForbiddenError('Access Denied', { cause: new Error('Permission bit 0 not set') });
196
+
197
+ // Get a structured JSON-cloneable object
198
+ const serialized = serializeError(error);
199
+
200
+ // Get a formatted string with indentation and causes
201
+ const formatted = formatError(error, { includeStack: true });
202
+ console.log(formatted);
203
+ /*
204
+ ForbiddenError: Access Denied
205
+ Caused by:
206
+ Error: Permission bit 0 not set
207
+ at Object.<anonymous> ...
208
+ */
209
+ ```
210
+
211
+ ### Performance Optimization (Fast Mode)
212
+
213
+ Generating a stack trace is an expensive operation in JavaScript. If you are using exceptions for control flow in a high-performance loop (e.g., a parser) and do not need the stack trace, you can use the `fast: true` option.
214
+
215
+ ```ts
216
+ import { CustomError } from '@tstdl/base/errors';
217
+
218
+ class ParserError extends CustomError {
219
+ static readonly errorName = 'ParserError';
220
+
221
+ constructor(message: string) {
222
+ // fast: true skips super() call to Error, avoiding stack trace generation
223
+ super({ message, fast: true });
224
+ }
225
+ }
226
+ ```
227
+
228
+ ### Unwrapping Errors
229
+
230
+ Sometimes errors are wrapped in other objects or generic error containers. `unwrapError` helps extract the original underlying error.
231
+
232
+ ```ts
233
+ import { unwrapError } from '@tstdl/base/errors';
234
+
235
+ const originalError = new Error('Database connection failed');
236
+ const wrappedError = { reason: originalError };
237
+
238
+ const error = unwrapError(wrappedError);
239
+ console.log(error.message); // "Database connection failed"
240
+ ```
241
+
242
+ ## 📚 API
243
+
244
+ | Class / Function | Description |
245
+ | :------------------------------- | :-------------------------------------------------------------------------------------------------------------- |
246
+ | `CustomError` | Abstract base class for all errors. Accepts `CustomErrorOptions` (`name`, `message`, `cause`, `stack`, `fast`). |
247
+ | `ApiError` | Wraps an `ErrorResponse` from an API, exposing the `response` and `details`. |
248
+ | `AssertionError` | Indicates a failed assertion check. |
249
+ | `BadRequestError` | Indicates a client-side error (HTTP 400). Default message: "bad request". |
250
+ | `DetailsError<T>` | An error that carries an additional `details` payload of type `T`. |
251
+ | `ForbiddenError` | Indicates the client is authenticated but lacks permissions (HTTP 403). Default message: "forbidden". |
252
+ | `InvalidCredentialsError` | Indicates authentication failed due to bad credentials. Default message: "Invalid credentials.". |
253
+ | `InvalidTokenError` | Indicates an authentication token is invalid or expired. Default message: "invalid token". |
254
+ | `MaxBytesExceededError` | Indicates a size limit was exceeded. Includes static `fromBytes(bytes)` factory. |
255
+ | `MethodNotAllowedError` | Indicates the HTTP method is not supported for the resource (HTTP 405). |
256
+ | `MultiError` | Aggregates an array of `Error` objects into a single error. |
257
+ | `NotFoundError` | Indicates a resource could not be found (HTTP 404). Default message: "not found". |
258
+ | `NotImplementedError` | Indicates functionality is not yet implemented (HTTP 501). |
259
+ | `NotSupportedError` | Indicates an operation or value is not supported. Includes static `fromEnum(...)` factory. |
260
+ | `TimeoutError` | Indicates an operation exceeded its time limit. |
261
+ | `UnauthorizedError` | Indicates authentication is required (HTTP 401). Default message: "Unauthorized". |
262
+ | `UnsupportedMediaTypeError` | Indicates the payload format is not supported (HTTP 415). |
263
+ | `unwrapError(error)` | Utility function to extract the underlying error from a wrapper object (checks `rejection`, `reason`, `error`). |
264
+ | `serializeError(error, options)` | Serializes an error into a structured JSON-cloneable `SerializedError` object. |
265
+ | `formatError(error, options)` | Formats an error into a human-readable string, including causes and sub-errors. |
266
+ | `englishTstdlErrorsLocalization` | Localization object containing English headers and messages for standard errors. |
267
+ | `germanTstdlErrorsLocalization` | Localization object containing German headers and messages for standard errors. |
@@ -1,3 +1,4 @@
1
+ /** biome-ignore-all lint/nursery/noExcessiveClassesPerFile: <explanation> */
1
2
  import '../../polyfills.js';
2
3
  import { DocumentManagementAuthorizationService, type DocumentCollection, type DocumentCollectionMetadata } from '../../document-management/index.js';
3
4
  import { DocumentManagementAncillaryService } from '../../document-management/server/index.js';
@@ -1,3 +1,4 @@
1
+ /** biome-ignore-all lint/nursery/noExcessiveClassesPerFile: <explanation> */
1
2
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
3
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
4
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -10,22 +11,23 @@ import { MockApiRequestTokenProvider } from '../../api/server/api-request-token.
10
11
  import { configureApiServer } from '../../api/server/module.js';
11
12
  import { Application } from '../../application/application.js';
12
13
  import { provideInitializer, provideModule, provideSignalHandler } from '../../application/index.js';
13
- import { configurePostgresCircuitBreaker, migratePostgresCircuitBreaker } from '../../circuit-breaker/postgres/module.js';
14
+ import { configureAudit } from '../../audit/module.js';
15
+ import { configurePostgresCircuitBreaker } from '../../circuit-breaker/postgres/module.js';
14
16
  import { DocumentManagementAuthorizationService } from '../../document-management/index.js';
15
17
  import { configureDocumentManagement } from '../../document-management/server/configure.js';
16
18
  import { DocumentCategoryTypeService, DocumentCollectionService, DocumentManagementAncillaryService, DocumentManagementApiController, DocumentRequestService } from '../../document-management/server/index.js';
17
- import { migrateDocumentManagementSchema } from '../../document-management/server/module.js';
18
19
  import { DocumentManagementService } from '../../document-management/server/services/document-management.service.js';
19
20
  import { configureNodeHttpServer } from '../../http/server/node/module.js';
20
- import { Injector, Singleton } from '../../injector/index.js';
21
- import { inject, injectManyAsync, runInInjectionContext } from '../../injector/inject.js';
21
+ import { Singleton } from '../../injector/index.js';
22
+ import { injectManyAsync } from '../../injector/inject.js';
22
23
  import { PrettyPrintLogFormatter, provideConsoleLogTransport } from '../../logger/index.js';
23
24
  import { configureLocalMessageBus } from '../../message-bus/index.js';
24
25
  import { WebServerModule } from '../../module/index.js';
25
26
  import { configureS3ObjectStorage } from '../../object-storage/s3/index.js';
26
- import { configureOrm } from '../../orm/server/index.js';
27
+ import { configureOrm, provideDatabaseMigrator } from '../../orm/server/index.js';
28
+ import { configurePostgresRateLimiter } from '../../rate-limit/postgres/module.js';
27
29
  import { configureDefaultSignalsImplementation } from '../../signals/implementation/configure.js';
28
- import { configurePostgresTaskQueue, migratePostgresTaskQueueSchema } from '../../task-queue/postgres/index.js';
30
+ import { configurePostgresTaskQueue } from '../../task-queue/postgres/index.js';
29
31
  import { boolean, positiveInteger, string } from '../../utils/config-parser.js';
30
32
  import { ExampleAiProviderService } from './ai-provider.js';
31
33
  import { TstdlCategoryParents, TstdlDocumentCategoryLabels, TstdlDocumentPropertyConfiguration, TstdlDocumentTypeCategories, TstdlDocumentTypeLabels, TstdlDocumentTypeProperties } from './categories-and-types.js';
@@ -84,11 +86,13 @@ AllowAllDocumentManagementAuthorizationService = __decorate([
84
86
  Singleton()
85
87
  ], AllowAllDocumentManagementAuthorizationService);
86
88
  export { AllowAllDocumentManagementAuthorizationService };
87
- async function bootstrap() {
88
- const injector = inject(Injector);
89
+ function bootstrap() {
90
+ configureAudit();
89
91
  configureNodeHttpServer();
90
92
  configurePostgresTaskQueue();
91
93
  configurePostgresCircuitBreaker();
94
+ configurePostgresTaskQueue();
95
+ configurePostgresRateLimiter();
92
96
  configureLocalMessageBus();
93
97
  configureDefaultSignalsImplementation();
94
98
  configureGenkit({
@@ -119,6 +123,7 @@ async function bootstrap() {
119
123
  bucketPerModule: config.s3.bucketPerModule,
120
124
  accessKey: config.s3.accessKey,
121
125
  secretKey: config.s3.secretKey,
126
+ forcePathStyle: true,
122
127
  });
123
128
  configureApiServer({
124
129
  controllers: [DocumentManagementApiController],
@@ -139,9 +144,6 @@ async function bootstrap() {
139
144
  location: config.ai.vertex.location,
140
145
  },
141
146
  });
142
- await runInInjectionContext(injector, migrateDocumentManagementSchema);
143
- await runInInjectionContext(injector, migratePostgresCircuitBreaker);
144
- await runInInjectionContext(injector, migratePostgresTaskQueueSchema);
145
147
  }
146
148
  async function main() {
147
149
  const tenantId = '00000000-0000-0000-0000-000000000000';
@@ -171,4 +173,5 @@ Application.run('DocumentManagementTest', [
171
173
  provideModule(main, WebServerModule),
172
174
  provideSignalHandler(),
173
175
  provideConsoleLogTransport(PrettyPrintLogFormatter),
176
+ provideDatabaseMigrator(),
174
177
  ]);
package/file/README.md ADDED
@@ -0,0 +1,191 @@
1
+ # File Module
2
+
3
+ A utility module for robust MIME type detection and server-side temporary file management. It provides tools to identify file types from various sources and a safe, RAII-style wrapper for handling temporary files in Node.js.
4
+
5
+ ## Table of Contents
6
+
7
+ - [✨ Features](#-features)
8
+ - [Core Concepts](#core-concepts)
9
+ - [🚀 Basic Usage](#-basic-usage)
10
+ - [Detecting MIME Types](#detecting-mime-types)
11
+ - [Getting Extensions](#getting-extensions)
12
+ - [🔧 Advanced Topics](#-advanced-topics)
13
+ - [Managing Temporary Files](#managing-temporary-files)
14
+ - [📚 API](#-api)
15
+
16
+ ## ✨ Features
17
+
18
+ - **Robust MIME Detection**: Identify file types by checking magic numbers (binary signatures) rather than just file extensions.
19
+ - **Versatile Input**: Supports detection from file paths, `Uint8Array` buffers, and `ReadableStream`s.
20
+ - **Extension Lookup**: Retrieve valid file extensions for a specific MIME type.
21
+ - **Automatic Cleanup**: Server-side `TemporaryFile` class implements `AsyncDisposable` for automatic file deletion when the scope exits.
22
+ - **Stream Support**: seamless integration with Node.js and Web Streams for writing to and reading from temporary files.
23
+
24
+ ## Core Concepts
25
+
26
+ ### MIME Type Detection
27
+
28
+ The module uses the `file-type` library under the hood to inspect the binary signature of a file. This is more secure and accurate than relying on filenames. It supports a wide range of formats including images, video, audio, and archives.
29
+
30
+ ### Temporary File Management (Server)
31
+
32
+ When processing file uploads or generating reports, you often need to store data on disk temporarily. The `TemporaryFile` class wraps Node.js filesystem operations. It generates a unique path in the OS's temporary directory and ensures the file is deleted when you are done with it, leveraging TypeScript's `using` keyword (Explicit Resource Management).
33
+
34
+ ## 🚀 Basic Usage
35
+
36
+ ### Detecting MIME Types
37
+
38
+ You can detect the MIME type of a file using a buffer or a stream.
39
+
40
+ ```typescript
41
+ import { getMimeType } from '@tstdl/base/file';
42
+ import { readFile } from 'node:fs/promises';
43
+
44
+ async function checkFiles() {
45
+ // 1. From a Uint8Array / Buffer
46
+ const buffer = await readFile('/path/to/document.pdf');
47
+ const mimeFromBuffer = await getMimeType(buffer);
48
+ console.log(mimeFromBuffer); // 'application/pdf'
49
+
50
+ // 2. With a fallback if detection fails
51
+ const mimeWithFallback = await getMimeType(buffer, 'application/octet-stream');
52
+ console.log(mimeWithFallback);
53
+ }
54
+ ```
55
+
56
+ To detect MIME types from a file path (Node.js only), use the server-specific module:
57
+
58
+ ```typescript
59
+ import { getMimeTypeFromFile } from '@tstdl/base/file/server';
60
+
61
+ async function checkPath() {
62
+ const mimeFromPath = await getMimeTypeFromFile('/path/to/image.png');
63
+ console.log(mimeFromPath); // 'image/png'
64
+ }
65
+ ```
66
+
67
+ ### Getting Extensions
68
+
69
+ Retrieve the list of known extensions for a given MIME type.
70
+
71
+ ```typescript
72
+ import { getMimeTypeExtensions } from '@tstdl/base/file';
73
+
74
+ const extensions = getMimeTypeExtensions('image/jpeg');
75
+ console.log(extensions); // ['jpeg', 'jpg', 'jpe']
76
+
77
+ const pdfExt = getMimeTypeExtensions('application/pdf');
78
+ console.log(pdfExt); // ['pdf']
79
+ ```
80
+
81
+ ## 🔧 Advanced Topics
82
+
83
+ ### Managing Temporary Files
84
+
85
+ The `TemporaryFile` class is located in the `server` submodule. It is designed to be used with the `await using` syntax to guarantee cleanup.
86
+
87
+ > **Note:** This feature requires a Node.js environment.
88
+
89
+ #### Basic Usage
90
+
91
+ The most common way to create a temporary file is from a stream, buffer, or string.
92
+
93
+ ```typescript
94
+ import { TemporaryFile } from '@tstdl/base/file/server';
95
+
96
+ async function processUpload(uploadStream: ReadableStream<Uint8Array>) {
97
+ // Create a temp file from a stream. The file is created in os.tmpdir() with a random UUID name.
98
+ // Optional: pass an extension as the second argument.
99
+ await using tempFile = await TemporaryFile.from(uploadStream, '.png');
100
+
101
+ console.log(`Processing file at: ${tempFile.path}`);
102
+ console.log(`File size: ${await tempFile.size()} bytes`);
103
+
104
+ // Read content back if needed
105
+ const content = await tempFile.readText();
106
+
107
+ // Perform operations (e.g., virus scan, image processing, parsing)
108
+ await performHeavyProcessing(tempFile.path);
109
+ } // <--- tempFile.delete() is automatically called here!
110
+ ```
111
+
112
+ #### Manual Control and Extensions
113
+
114
+ You can create an empty temporary file and specify an extension.
115
+
116
+ ```typescript
117
+ import { TemporaryFile } from '@tstdl/base/file/server';
118
+
119
+ async function generateReport() {
120
+ await using tempFile = TemporaryFile.create('.pdf');
121
+
122
+ await tempFile.write('Report Content');
123
+ // ... do something with tempFile.path
124
+ }
125
+ ```
126
+
127
+ #### Persistent Files
128
+
129
+ If you want to keep the file even after the scope ends (e.g., if you successfully moved it to a final destination), use `keep()` or `moveTo()`.
130
+
131
+ ```typescript
132
+ import { TemporaryFile } from '@tstdl/base/file/server';
133
+
134
+ async function savePermanent(stream: ReadableStream<Uint8Array>, destination: string) {
135
+ await using tempFile = await TemporaryFile.from(stream);
136
+
137
+ // ... validate file ...
138
+
139
+ // Move the file and mark it to be kept (not deleted on disposal)
140
+ await tempFile.moveTo(destination, true);
141
+ }
142
+ ```
143
+
144
+ #### Adopting Existing Files
145
+
146
+ You can wrap an existing file in `TemporaryFile` to ensure it gets deleted when done.
147
+
148
+ ```typescript
149
+ import { TemporaryFile } from '@tstdl/base/file/server';
150
+
151
+ async function processExisting(path: string) {
152
+ await using tempFile = TemporaryFile.adopt(path);
153
+ // path will be deleted at the end of the scope
154
+ }
155
+ ```
156
+
157
+ ## 📚 API
158
+
159
+ ### `@tstdl/base/file`
160
+
161
+ | Export | Type | Description |
162
+ | :---------------------- | :------- | :-------------------------------------------------------------------------------------------------------------------- |
163
+ | `getMimeType` | Function | Async function to detect MIME type from `Uint8Array` or `ReadableStream`. Accepts an optional fallback. |
164
+ | `getMimeTypeExtensions` | Function | Returns an array of file extensions associated with a MIME type string. |
165
+ | `mimeTypes` | Object | A dictionary mapping MIME types to arrays of file extensions. |
166
+ | `mimeTypesMap` | Map | A `Map` version of the `mimeTypes` object for efficient lookup. |
167
+
168
+ ### `@tstdl/base/file/server`
169
+
170
+ | Export | Type | Description |
171
+ | :--------------------- | :------- | :------------------------------------------------------------ |
172
+ | `getMimeTypeFromFile` | Function | Async function to detect MIME type from a string file path (Node.js only). |
173
+ | `TemporaryFile` | Class | A wrapper for temporary files implementing `AsyncDisposable`. |
174
+
175
+ #### `TemporaryFile` Class
176
+
177
+ | Method/Property | Description |
178
+ | :------------------------ | :----------------------------------------------------------------------------------------------------------- |
179
+ | `path` | Getter for the absolute file path. |
180
+ | `static create(ext?)` | Creates a new empty `TemporaryFile` instance with an optional extension. |
181
+ | `static adopt(path)` | Creates a `TemporaryFile` instance wrapping an existing file path. |
182
+ | `static from(content, ext?)` | Creates a `TemporaryFile` and writes the provided content (string, Uint8Array, or Stream) to it, with an optional extension. |
183
+ | `read()` | Reads the file content as a `Uint8Array`. |
184
+ | `readText()` | Reads the file content as a UTF-8 string. |
185
+ | `readStream()` | Returns a `ReadableStream<Uint8Array>` of the file content. |
186
+ | `write(content)` | Overwrites the file with the provided content. |
187
+ | `moveTo(path, keep?)` | Moves the file to a new location. If `keep` is true, it won't be deleted on disposal. |
188
+ | `keep()` | Prevents the file from being deleted when the object is disposed. |
189
+ | `delete()` | Manually deletes the file. |
190
+ | `size()` | Returns the size of the file in bytes. |
191
+ | `[Symbol.asyncDispose]()` | Automatically calls `delete()` when used with `await using`, unless `keep()` was called. |