@boo-labs/server 0.0.0-stage → 1.0.1

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 (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +287 -2
  3. package/index.mjs +23 -0
  4. package/package.json +71 -4
  5. package/source/application/Kernel/index.mjs +1137 -0
  6. package/source/application/Server/index.mjs +1201 -0
  7. package/source/application/configurations/Http2CleartextServerConfiguration/index.mjs +168 -0
  8. package/source/application/configurations/Http2SecureServerConfiguration/index.mjs +172 -0
  9. package/source/application/configurations/HttpServerConfiguration/index.mjs +169 -0
  10. package/source/application/configurations/HttpsServerConfiguration/index.mjs +172 -0
  11. package/source/application/configurations/ServerConfiguration/Options/CORSOptions/CORSOptionsValue.mjs +158 -0
  12. package/source/application/configurations/ServerConfiguration/Options/CORSOptions/index.mjs +87 -0
  13. package/source/application/configurations/ServerConfiguration/Options/CSRFOptions/CSRFOptionsValue.mjs +178 -0
  14. package/source/application/configurations/ServerConfiguration/Options/CSRFOptions/index.mjs +80 -0
  15. package/source/application/configurations/ServerConfiguration/Options/CompressOptions/CompressOptionsValue.mjs +126 -0
  16. package/source/application/configurations/ServerConfiguration/Options/CompressOptions/index.mjs +81 -0
  17. package/source/application/configurations/ServerConfiguration/Options/CookieParserOptions/CookieParserOptionsValue.mjs +107 -0
  18. package/source/application/configurations/ServerConfiguration/Options/CookieParserOptions/index.mjs +82 -0
  19. package/source/application/configurations/ServerConfiguration/Options/CookieSessionOptions/CookieSessionOptionsValue.mjs +182 -0
  20. package/source/application/configurations/ServerConfiguration/Options/CookieSessionOptions/index.mjs +86 -0
  21. package/source/application/configurations/ServerConfiguration/Options/ListenOptions/ListenOptionsValue.mjs +95 -0
  22. package/source/application/configurations/ServerConfiguration/Options/ListenOptions/index.mjs +47 -0
  23. package/source/application/configurations/ServerConfiguration/Options/MiddlewareOption.mjs +102 -0
  24. package/source/application/configurations/ServerConfiguration/Options/Options.mjs +105 -0
  25. package/source/application/configurations/ServerConfiguration/Options/RateLimitOptions/RateLimitOptionsValue.mjs +164 -0
  26. package/source/application/configurations/ServerConfiguration/Options/RateLimitOptions/index.mjs +84 -0
  27. package/source/application/configurations/ServerConfiguration/Options/RequestOptions/RequestOptionsValue.mjs +108 -0
  28. package/source/application/configurations/ServerConfiguration/Options/RequestOptions/index.mjs +47 -0
  29. package/source/application/configurations/ServerConfiguration/Options/ResponseOptions/ResponseOptionsValue.mjs +101 -0
  30. package/source/application/configurations/ServerConfiguration/Options/ResponseOptions/index.mjs +47 -0
  31. package/source/application/configurations/ServerConfiguration/Options/SecurityHeadersOptions/SecurityHeadersOptionsValue.mjs +272 -0
  32. package/source/application/configurations/ServerConfiguration/Options/SecurityHeadersOptions/index.mjs +85 -0
  33. package/source/application/configurations/ServerConfiguration/Options/ServerOption.mjs +50 -0
  34. package/source/application/configurations/ServerConfiguration/Options/StaticOptions/StaticOptionsValue.mjs +164 -0
  35. package/source/application/configurations/ServerConfiguration/Options/StaticOptions/index.mjs +89 -0
  36. package/source/application/configurations/ServerConfiguration/index.mjs +781 -0
  37. package/source/application/resolvers/TransportResolver/index.mjs +119 -0
  38. package/source/application/use-cases/CloseServer/index.mjs +85 -0
  39. package/source/application/use-cases/ListenServer/index.mjs +98 -0
  40. package/source/core/EventSystem/index.mjs +110 -0
  41. package/source/core/Exchange/ExchangeContext.mjs +291 -0
  42. package/source/core/Exchange/Telemetry.mjs +193 -0
  43. package/source/core/Exchange/index.mjs +604 -0
  44. package/source/core/ExchangeLayerBuilder/index.mjs +133 -0
  45. package/source/core/IPCMain/IPCMain.mjs +507 -0
  46. package/source/core/IPCMain/IPCProtocol.mjs +441 -0
  47. package/source/core/IPCMain/IPCServerChannel.mjs +331 -0
  48. package/source/core/IPCMain/WebSocketAdapter.mjs +189 -0
  49. package/source/core/Request/BodyParser/BodyParser.mjs +197 -0
  50. package/source/core/Request/BodyParser/JSONParser.mjs +70 -0
  51. package/source/core/Request/BodyParser/MultipartParser.mjs +612 -0
  52. package/source/core/Request/BodyParser/URLEncodedParser.mjs +71 -0
  53. package/source/core/Request/RequestHeaders.mjs +177 -0
  54. package/source/core/Request/index.mjs +805 -0
  55. package/source/core/Response/ResponseHeaders.mjs +402 -0
  56. package/source/core/Response/index.mjs +1370 -0
  57. package/source/core/middlewares/CORSMiddleware/index.mjs +289 -0
  58. package/source/core/middlewares/CSRFMiddleware/index.mjs +284 -0
  59. package/source/core/middlewares/CompressMiddleware/index.mjs +460 -0
  60. package/source/core/middlewares/CookieParserMiddleware/index.mjs +194 -0
  61. package/source/core/middlewares/CookieSessionMiddleware/index.mjs +460 -0
  62. package/source/core/middlewares/RateLimitMiddleware/index.mjs +245 -0
  63. package/source/core/middlewares/SecurityHeadersMiddleware/index.mjs +151 -0
  64. package/source/core/middlewares/StaticMiddleware/index.mjs +628 -0
  65. package/source/core/routing/ExchangeLayer/ExchangeMatcher.mjs +127 -0
  66. package/source/core/routing/ExchangeLayer/index.mjs +254 -0
  67. package/source/core/routing/Pipeline/index.mjs +157 -0
  68. package/source/core/routing/Router/Route.mjs +184 -0
  69. package/source/core/routing/Router/index.mjs +523 -0
  70. package/source/core/routing/RouterRegister/OptionsMiddleware.mjs +147 -0
  71. package/source/core/routing/RouterRegister/index.mjs +150 -0
  72. package/source/core/routing/RouterSystem/index.mjs +225 -0
  73. package/source/core/value-objects/Address/index.mjs +281 -0
  74. package/source/core/value-objects/Endpoint/index.mjs +293 -0
  75. package/source/index.mjs +62 -0
  76. package/source/infrastructure/adapters/Http2Adapter/index.mjs +144 -0
  77. package/source/infrastructure/adapters/Http2CleartextAdapter/index.mjs +147 -0
  78. package/source/infrastructure/adapters/HttpAdapter/index.mjs +124 -0
  79. package/source/infrastructure/adapters/HttpsAdapter/index.mjs +124 -0
  80. package/source/infrastructure/adapters/TransportAdapter/createWebSocketOpenRequest.mjs +843 -0
  81. package/source/infrastructure/adapters/TransportAdapter/index.mjs +555 -0
  82. package/source/shared/Identifier/index.mjs +671 -0
  83. package/source/shared/constants/symbols/brands/index.mjs +45 -0
  84. package/source/shared/domain/UUIDV7/index.mjs +346 -0
  85. package/source/shared/errors/BaseError/index.mjs +157 -0
  86. package/source/shared/errors/IPCError/index.mjs +97 -0
  87. package/source/shared/errors/MiddlewareError/index.mjs +106 -0
  88. package/source/shared/errors/PathnamePatternError/index.mjs +103 -0
  89. package/source/shared/errors/RequestError/index.mjs +116 -0
  90. package/source/shared/errors/ResponseError/index.mjs +140 -0
  91. package/source/shared/errors/RouterError/index.mjs +90 -0
  92. package/source/shared/errors/ServerConfigurationError/index.mjs +551 -0
  93. package/source/shared/errors/ServerError/index.mjs +127 -0
  94. package/source/shared/errors/WebSocketError/index.mjs +58 -0
  95. package/source/shared/outcome/ErrorSchema.mjs +361 -0
  96. package/source/shared/outcome/Result/index.mjs +239 -0
  97. package/source/shared/outcome/Validation/index.mjs +208 -0
  98. package/source/shared/validations/Middleware/index.mjs +160 -0
  99. package/source/shared/validations/PathnamePattern/index.mjs +256 -0
  100. package/source/shared/validations/Server/NotFoundHandler/index.mjs +94 -0
  101. package/source/shared/validations/Server/Router/index.mjs +68 -0
  102. package/source/shared/validations/Server/ServerConfiguration/index.mjs +68 -0
  103. package/source/shared/validations/Server/SystemErrorHandler/index.mjs +94 -0
  104. package/source/shared/validations/ServerConfiguration/CORSOptions/index.mjs +310 -0
  105. package/source/shared/validations/ServerConfiguration/CSRFOptions/index.mjs +299 -0
  106. package/source/shared/validations/ServerConfiguration/CompressOptions/index.mjs +204 -0
  107. package/source/shared/validations/ServerConfiguration/CookieParserOptions/index.mjs +102 -0
  108. package/source/shared/validations/ServerConfiguration/CookieSessionOptions/index.mjs +332 -0
  109. package/source/shared/validations/ServerConfiguration/ListenOptions/index.mjs +178 -0
  110. package/source/shared/validations/ServerConfiguration/RateLimitOptions/index.mjs +229 -0
  111. package/source/shared/validations/ServerConfiguration/RequestOptions/index.mjs +132 -0
  112. package/source/shared/validations/ServerConfiguration/ResponseOptions/index.mjs +122 -0
  113. package/source/shared/validations/ServerConfiguration/SecurityHeadersOptions/index.mjs +137 -0
  114. package/source/shared/validations/ServerConfiguration/StaticOptions/index.mjs +248 -0
  115. package/types/index.d.ts +719 -0
@@ -0,0 +1,346 @@
1
+ // @ts-check
2
+ /**
3
+ * @file Provides a UUID version 7 implementation.
4
+ *
5
+ * @description Defines the UUIDV7 class for creating, validating, and working
6
+ * with RFC 9562-compliant UUID version 7 identifiers.
7
+ *
8
+ * UUID v7 embeds a 48-bit Unix timestamp in milliseconds in the first
9
+ * six bytes of the identifier, followed by randomly generated bits.
10
+ * This provides identifiers that are practically unique and naturally
11
+ * sortable by creation time.
12
+ *
13
+ * @module UUIDV7
14
+ */
15
+
16
+ // ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
17
+ // » IMPORT NATIVE NODEJS MODULES
18
+ import { randomBytes } from 'node:crypto';
19
+
20
+ // » IMPORT THIRD PARTY MODULES
21
+
22
+ // » IMPORT LOCAL MODULES
23
+ import Validation from '#shared/outcome/Validation';
24
+
25
+ // ━━ CONSTANTS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
26
+ /**
27
+ * Matches the canonical hexadecimal representation of a UUID version 7.
28
+ *
29
+ * The expression validates the UUID structure, version field, and RFC 9562
30
+ * variant bits.
31
+ *
32
+ * @type {RegExp}
33
+ */
34
+ const UUIDV7_REGEXP = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
35
+
36
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
37
+ /**
38
+ * Represents a UUID version 7.
39
+ *
40
+ * UUID v7 combines a Unix timestamp in milliseconds with randomly generated
41
+ * bits, producing identifiers that are practically unique and chronologically
42
+ * sortable.
43
+ */
44
+ class UUIDV7 {
45
+ #uuid;
46
+
47
+ /**
48
+ * Creates a UUID v7 instance.
49
+ *
50
+ * When no timestamp is provided, the current time is used. A `Date` instance
51
+ * uses its Unix timestamp, while an existing UUID v7 uses the timestamp
52
+ * encoded in its first 48 bits.
53
+ *
54
+ * @param {Date|string|undefined} inputId - Timestamp source or existing UUID v7.
55
+ * @throws {TypeError} If the input type is invalid.
56
+ * @throws {RangeError} If the input value is invalid.
57
+ *
58
+ * @example
59
+ * ```js
60
+ * const id = new UUIDV7();
61
+ * ```
62
+ *
63
+ * @example
64
+ * ```js
65
+ * const id = new UUIDV7(new Date('2026-09-07T10:00:00Z'));
66
+ * ```
67
+ *
68
+ * @example
69
+ * ```js
70
+ * const source = new UUIDV7();
71
+ * const id = new UUIDV7(source.value);
72
+ * ```
73
+ */
74
+ constructor(inputId = undefined) {
75
+ UUIDV7.assert(inputId);
76
+
77
+ const milliseconds = UUIDV7.#normalizeTimestamp(inputId);
78
+
79
+ this.#uuid = UUIDV7.#generateUUID(milliseconds);
80
+ }
81
+
82
+ /**
83
+ * The UUID value.
84
+ *
85
+ * @readonly
86
+ * @type {string}
87
+ */
88
+ get value() {
89
+ return this.#uuid;
90
+ }
91
+
92
+ /**
93
+ * The UUID value without separators.
94
+ *
95
+ * @readonly
96
+ * @type {string}
97
+ */
98
+ get hexadecimal() {
99
+ return this.#uuid.replace(/-/g, '');
100
+ }
101
+
102
+ /**
103
+ * The Unix timestamp encoded in the UUID.
104
+ *
105
+ * @readonly
106
+ * @type {number}
107
+ */
108
+ get timestamp() {
109
+ return Number.parseInt(this.hexadecimal.slice(0, 12), 16);
110
+ }
111
+
112
+ /**
113
+ * Returns the Unix timestamp encoded in the UUID as a Date instance.
114
+ *
115
+ * @returns {Date} Date represented by the UUID timestamp.
116
+ * @example
117
+ * ```js
118
+ * const id = new UUIDV7();
119
+ * const date = id.toDate();
120
+ * ```
121
+ */
122
+ toDate() {
123
+ return new Date(this.timestamp);
124
+ }
125
+
126
+ /**
127
+ * Returns the UUID as a string.
128
+ *
129
+ * @returns {string} UUID in canonical hexadecimal format.
130
+ * @example
131
+ * ```js
132
+ * const id = new UUIDV7();
133
+ * console.log(id.toString());
134
+ * ```
135
+ */
136
+ toString() {
137
+ return this.#uuid;
138
+ }
139
+
140
+ /**
141
+ * Returns the UUID as a JSON value.
142
+ *
143
+ * @returns {string} UUID in canonical hexadecimal format.
144
+ *
145
+ * @example
146
+ * ```js
147
+ * const id = new UUIDV7();
148
+ * console.log(JSON.stringify(id));
149
+ * ```
150
+ */
151
+ toJSON() {
152
+ return this.#uuid;
153
+ }
154
+
155
+ /**
156
+ * Normalizes a timestamp source into Unix milliseconds.
157
+ *
158
+ * Accepted values are:
159
+ * - `undefined`, which resolves to the current time.
160
+ * - A `Date` instance, which resolves to its Unix timestamp.
161
+ * - A valid UUID v7 string, from which the embedded timestamp is extracted.
162
+ *
163
+ * @param {Date|string|undefined} inputId - Timestamp source.
164
+ * @returns {number} Unix timestamp in milliseconds.
165
+ */
166
+ static #normalizeTimestamp(inputId) {
167
+ if (inputId === undefined) {
168
+ return Date.now();
169
+ }
170
+
171
+ if (inputId instanceof Date) {
172
+ return inputId.getTime();
173
+ }
174
+
175
+ const hexadecimal = inputId.replace(/-/g, '');
176
+
177
+ return Number.parseInt(hexadecimal.substring(0, 12), 16);
178
+ }
179
+
180
+ /**
181
+ * Generates a UUID version 7 from a Unix timestamp.
182
+ *
183
+ * The generated identifier contains:
184
+ * - A 48-bit Unix timestamp in milliseconds.
185
+ * - The UUID version 7 field.
186
+ * - The RFC 9562 variant field.
187
+ * - Random bits filling the remaining identifier space.
188
+ *
189
+ * @param {number} milliseconds - Unix timestamp in milliseconds.
190
+ * @returns {string} UUID v7 in canonical hexadecimal format.
191
+ */
192
+ static #generateUUID(milliseconds) {
193
+ const bytes = randomBytes(16);
194
+
195
+ // ━━ 48-bit Unix timestamp (bytes 0-5) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
196
+ bytes[0] = Math.floor(milliseconds / 0x10000000000) & 0xff;
197
+ bytes[1] = Math.floor(milliseconds / 0x100000000) & 0xff;
198
+ bytes[2] = Math.floor(milliseconds / 0x1000000) & 0xff;
199
+ bytes[3] = Math.floor(milliseconds / 0x10000) & 0xff;
200
+ bytes[4] = Math.floor(milliseconds / 0x100) & 0xff;
201
+ bytes[5] = milliseconds & 0xff;
202
+
203
+ // ━━ Version 7 + random bits ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
204
+ bytes[6] = (bytes[6] & 0x0f) | 0x70;
205
+
206
+ // ━━ RFC 9562 variant + random bits ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
207
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
208
+
209
+ // ━━ Canonical format ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
210
+ const hexadecimal = bytes.toString('hex');
211
+
212
+ return `${hexadecimal.slice(0, 8)}-${hexadecimal.slice(8, 12)}-${hexadecimal.slice(12, 16)}-${hexadecimal.slice(16, 20)}-${hexadecimal.slice(20)}`;
213
+ }
214
+
215
+ /**
216
+ * Creates a new UUID v7 string using the current time.
217
+ *
218
+ * Convenience shortcut equivalent to `new UUIDV7().toString()`.
219
+ *
220
+ * @returns {string} UUID v7 in canonical hexadecimal format.
221
+ *
222
+ * @example
223
+ * ```js
224
+ * const id = UUIDV7.create();
225
+ * ```
226
+ */
227
+ static create() {
228
+ return new UUIDV7().toString();
229
+ }
230
+
231
+ /**
232
+ * Validates a value that can be used as a UUID v7 timestamp source.
233
+ *
234
+ * `undefined` represents the current time, a `Date` represents its Unix
235
+ * timestamp, and a UUID v7 string represents the timestamp encoded in that
236
+ * UUID.
237
+ *
238
+ * @param {*} value - Value to validate.
239
+ * @returns {Validation} Validation result.
240
+ * @example
241
+ * ```js
242
+ * const result = UUIDV7.validate();
243
+ * ```
244
+ */
245
+ static validate(value) {
246
+ if (value === undefined) {
247
+ return Validation.success();
248
+ }
249
+
250
+ if (value instanceof Date) {
251
+ if (Number.isNaN(value.getTime())) {
252
+ return Validation.failure({
253
+ error: RangeError,
254
+ message: 'Invalid timestamp.',
255
+ code: 'INVALID_TIMESTAMP',
256
+ custom: false,
257
+ });
258
+ }
259
+ return Validation.success();
260
+ }
261
+
262
+ if (typeof value === 'string') {
263
+ if (!UUIDV7_REGEXP.test(value)) {
264
+ return Validation.failure({
265
+ error: RangeError,
266
+ message: 'Invalid UUID v7.',
267
+ code: 'INVALID_UUID',
268
+ custom: false,
269
+ });
270
+ }
271
+ return Validation.success();
272
+ }
273
+
274
+ return Validation.failure({
275
+ error: TypeError,
276
+ message: 'Timestamp must be a Date, string, or undefined.',
277
+ code: 'INVALID_TIMESTAMP_SOURCE',
278
+ custom: false,
279
+ });
280
+ }
281
+
282
+ /**
283
+ * Validates that a value is a valid UUID v7 string.
284
+ *
285
+ * @param {unknown} value - Value to validate.
286
+ * @returns {Validation} Validation result.
287
+ * @example
288
+ * ```js
289
+ * const result = UUIDV7.validateUUIDString('019937f2-1234-7abc-8def-123456789abc');
290
+ * ```
291
+ */
292
+ static validateUUIDString(value) {
293
+ if (value === undefined) {
294
+ return Validation.failure({
295
+ error: ReferenceError,
296
+ message: 'UUID must be a string.',
297
+ custom: false,
298
+ code: 'MISSING_UUID',
299
+ });
300
+ }
301
+
302
+ if (typeof value !== 'string') {
303
+ return Validation.failure({
304
+ error: TypeError,
305
+ message: 'UUID must be a string.',
306
+ custom: false,
307
+ code: 'INVALID_UUID_TYPE',
308
+ });
309
+ }
310
+
311
+ if (!UUIDV7_REGEXP.test(value)) {
312
+ return Validation.failure({
313
+ error: RangeError,
314
+ message: 'Invalid UUID v7 format.',
315
+ custom: false,
316
+ code: 'INVALID_UUID',
317
+ });
318
+ }
319
+
320
+ return Validation.success();
321
+ }
322
+
323
+ /**
324
+ * Asserts that a value can be used as a UUID v7 timestamp source.
325
+ *
326
+ * Unlike {@link UUIDV7.validate}, this method throws when validation fails.
327
+ *
328
+ * @param {unknown} value - Value to validate.
329
+ * @throws {TypeError} If the value has an unsupported type.
330
+ * @throws {RangeError} If the value is invalid.
331
+ * @example
332
+ * ```js
333
+ * UUIDV7.assert(new Date());
334
+ * ```
335
+ */
336
+ static assert(value) {
337
+ const validation = UUIDV7.validate(value);
338
+
339
+ if (!validation.ok) {
340
+ throw validation.error;
341
+ }
342
+ }
343
+ }
344
+
345
+ // ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
346
+ export default UUIDV7;
@@ -0,0 +1,157 @@
1
+ /**
2
+ * @file Defines the base class for structured application errors.
3
+ *
4
+ * @description BaseError serves as the foundation for application-specific errors.
5
+ * It provides a consistent interface for error handling with support for:
6
+ *
7
+ * - Predefined error identifiers and custom error messages.
8
+ * - Message templating with placeholders.
9
+ * - Error chaining via the `cause` property.
10
+ * - Contextual information via `details`.
11
+ * - A consistent error structure across the application.
12
+ *
13
+ * Subclasses should define their own `MESSAGES` map with domain-specific
14
+ * error identifiers and message templates.
15
+ *
16
+ * @private
17
+ * @module BaseError
18
+ */
19
+
20
+ // ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
21
+ /**
22
+ * A symbolic identifier used to resolve an application error definition.
23
+ *
24
+ * @typedef {string} ErrorIdentifier
25
+ */
26
+
27
+ /**
28
+ * Options for creating an error instance.
29
+ *
30
+ * @typedef {object} ErrorOptions
31
+ * @property {Record<string, unknown>} [values] - Values for template placeholders in the error message.
32
+ * @property {unknown} [details] - Additional contextual information about the error.
33
+ * @property {Error} [cause] - The original error that caused this error (for error chaining).
34
+ */
35
+
36
+ /**
37
+ * Normalized error definition used internally to construct an error instance.
38
+ *
39
+ * @typedef {object} ErrorDefinition
40
+ * @property {string} message - The final error message.
41
+ * @property {ErrorIdentifier} [code] - The resolved error identifier.
42
+ * @property {unknown} [details] - Additional error details.
43
+ * @property {{ cause: Error }} [errorOptions] - Options for the Error constructor (e.g., cause).
44
+ */
45
+
46
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
47
+ /**
48
+ * Base class for application-specific errors.
49
+ *
50
+ * This class supports predefined error identifiers as well as custom error
51
+ * messages. When a known error identifier is provided, it is automatically
52
+ * resolved to its corresponding message template.
53
+ *
54
+ * @augments Error
55
+ * @private
56
+ */
57
+ class BaseError extends Error {
58
+ /**
59
+ * Creates a new {@link BaseError}.
60
+ *
61
+ * @param {ErrorIdentifier} identifier - A predefined error identifier or a custom error message.
62
+ * @param {ErrorOptions} [options] - Additional information used to format the message, attach domain details, or preserve the original cause.
63
+ * @example
64
+ * ```js
65
+ * class ValidationError extends BaseError {}
66
+ * throw new ValidationError('INVALID_VALUE');
67
+ * ```
68
+ */
69
+ constructor(identifier, options = {}) {
70
+ const definition = new.target.resolveDefinition(identifier, options);
71
+
72
+ super(definition.message, definition.errorOptions);
73
+
74
+ this.name = new.target.name;
75
+
76
+ if (definition.code !== undefined) {
77
+ /**
78
+ * Predefined error identifier, when available.
79
+ *
80
+ * @type {ErrorIdentifier}
81
+ */
82
+ this.code = definition.code;
83
+ }
84
+
85
+ if (definition.details !== undefined) {
86
+ /**
87
+ * Additional contextual information about the error.
88
+ *
89
+ * @type {unknown}
90
+ */
91
+ this.details = definition.details;
92
+ }
93
+
94
+ Error.captureStackTrace?.(this, new.target);
95
+ }
96
+
97
+ /**
98
+ * Replaces placeholder tokens in a predefined message.
99
+ *
100
+ * Placeholders are in the format `{key}` and are replaced with the
101
+ * corresponding value from the `values` object.
102
+ *
103
+ * @private
104
+ * @static
105
+ * @param {string} message - The message template containing placeholders.
106
+ * @param {Record<string, unknown>} [values] - Key-value pairs for placeholder replacement.
107
+ * @returns {string} The formatted message with placeholders replaced.
108
+ */
109
+ static #formatMessage(message, values = {}) {
110
+ return message.replace(/\{(\w+)\}/g, (_, key) => String(values[key] ?? `{${key}}`));
111
+ }
112
+
113
+ /**
114
+ * Resolves an error identifier into a normalized error definition.
115
+ *
116
+ * This method handles both predefined error identifiers and custom error messages.
117
+ * When a predefined identifier is found, it resolves to the corresponding message
118
+ * and applies any placeholders.
119
+ *
120
+ * @private
121
+ * @static
122
+ * @param {ErrorIdentifier} identifier - The error identifier or custom message.
123
+ * @param {ErrorOptions} [options] - Additional error options.
124
+ * @returns {ErrorDefinition} Normalized error definition.
125
+ */
126
+ static resolveDefinition(identifier, options) {
127
+ const entry = this.MESSAGES.get(identifier);
128
+
129
+ if (entry === undefined) {
130
+ return {
131
+ message: identifier,
132
+ };
133
+ }
134
+
135
+ return {
136
+ message: BaseError.#formatMessage(entry, options.values),
137
+ code: identifier,
138
+ details: options.details,
139
+ errorOptions: options.cause !== undefined ? { cause: options.cause } : undefined,
140
+ };
141
+ }
142
+
143
+ /**
144
+ * Message template catalog indexed by error identifier.
145
+ *
146
+ * Subclasses should override this property with their own
147
+ * identifier-to-message mappings.
148
+ *
149
+ * @type {Map<ErrorIdentifier, string>}
150
+ * @private
151
+ * @static
152
+ */
153
+ static MESSAGES = new Map();
154
+ }
155
+
156
+ // ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
157
+ export default BaseError;
@@ -0,0 +1,97 @@
1
+ /**
2
+ * @file Defines the internal error type used for IPC subsystem failures.
3
+ *
4
+ * @description This class supports predefined error identifiers as well
5
+ * as custom error messages. When a known error identifier is provided,
6
+ * it is automatically resolved to its corresponding message.
7
+ *
8
+ * @private
9
+ * @module IPCError
10
+ */
11
+
12
+ // ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
13
+ // » IMPORT LOCAL MODULES
14
+ import BaseError from '../BaseError/index.mjs';
15
+
16
+ // ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
17
+ /**
18
+ * @template {string} T
19
+ * @typedef {T | (string & {})} LiteralUnion
20
+ */
21
+
22
+ /**
23
+ * @typedef {LiteralUnion<
24
+ * 'IPC_INVALID_CHANNEL' |
25
+ * 'IPC_INVALID_HANDLER' |
26
+ * 'IPC_INVALID_LISTENER' |
27
+ * 'IPC_CLOSED' |
28
+ * 'IPC_CHANNEL_NOT_FOUND'
29
+ * >} IPCErrorIdentifier
30
+ */
31
+
32
+ /**
33
+ * Options for creating an error instance.
34
+ *
35
+ * @typedef {object} IPCErrorOptions
36
+ * @property {Record<string, unknown>} [values] - Values for template placeholders in the error message.
37
+ * @property {unknown} [details] - Additional contextual information about the error.
38
+ * @property {Error} [cause] - The original error that caused this error (for error chaining).
39
+ */
40
+
41
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
42
+ /**
43
+ * Error thrown when an IPC subsystem operation cannot be completed.
44
+ *
45
+ * This error type covers failures related to IPC handler registration,
46
+ * listener management, and IPC lifecycle operations.
47
+ *
48
+ * @augments BaseError
49
+ * @private
50
+ */
51
+ class IPCError extends BaseError {
52
+ /**
53
+ * Creates a new {@link IPCError}.
54
+ *
55
+ * @param {IPCErrorIdentifier} identifier - A predefined error identifier or a custom error message.
56
+ * @param {IPCErrorOptions} [options] - Additional information used to format the message, attach domain details, or preserve the original cause.
57
+ * @example
58
+ * ```js
59
+ * throw new IPCError('IPC_INVALID_CHANNEL');
60
+ * ```
61
+ * @example
62
+ * ```js
63
+ * throw new IPCError('IPC_CHANNEL_NOT_FOUND', {
64
+ * values: { channel: 'get-user' },
65
+ * });
66
+ * ```
67
+ */
68
+ constructor(identifier, options = {}) {
69
+ super(identifier, options);
70
+ }
71
+
72
+ /**
73
+ * Collection of predefined IPC error messages.
74
+ *
75
+ * Available error identifiers:
76
+ *
77
+ * - `IPC_INVALID_CHANNEL`.
78
+ * - `IPC_INVALID_HANDLER`.
79
+ * - `IPC_INVALID_LISTENER`.
80
+ * - `IPC_CLOSED`.
81
+ * - `IPC_CHANNEL_NOT_FOUND`.
82
+ *
83
+ * @private
84
+ * @static
85
+ * @type {Map<IPCErrorIdentifier, string>}
86
+ */
87
+ static MESSAGES = new Map([
88
+ ['IPC_INVALID_CHANNEL', 'Channel must be a non-empty string.'],
89
+ ['IPC_INVALID_HANDLER', 'Handler must be a function.'],
90
+ ['IPC_INVALID_LISTENER', 'Listener must be a function.'],
91
+ ['IPC_CLOSED', 'IPCMain has been closed.'],
92
+ ['IPC_CHANNEL_NOT_FOUND', 'No handler registered for channel "{channel}".'],
93
+ ]);
94
+ }
95
+
96
+ // ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
97
+ export default IPCError;
@@ -0,0 +1,106 @@
1
+ /**
2
+ * @file Defines domain-specific errors for middleware validation.
3
+ *
4
+ * @description MiddlewareError extends BaseError to provide structured,
5
+ * identifiable errors specific to middleware validation. It centralizes
6
+ * error messages and supports template placeholders for dynamic context.
7
+ *
8
+ * @module MiddlewareError
9
+ */
10
+
11
+ // ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
12
+ // » IMPORT LOCAL MODULES
13
+ import BaseError from '../BaseError/index.mjs';
14
+
15
+ // ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
16
+ /**
17
+ * @template {string} T
18
+ * @typedef {T | (string & {})} LiteralUnion
19
+ */
20
+
21
+ /**
22
+ * Identifies a predefined middleware error or allows a custom error message.
23
+ *
24
+ * @typedef {LiteralUnion<
25
+ * 'INVALID_MIDDLEWARE_TYPE' |
26
+ * 'INVALID_MIDDLEWARE_ARITY' |
27
+ * 'INVALID_MIDDLEWARES_TYPE'
28
+ * >} MiddlewareErrorIdentifier
29
+ */
30
+
31
+ /**
32
+ * Options for creating an error instance.
33
+ *
34
+ * @typedef {object} MiddlewareErrorOptions
35
+ * @property {Record<string, unknown>} [values] - Values for template placeholders in the error message.
36
+ * @property {unknown} [details] - Additional contextual information about the error.
37
+ * @property {Error} [cause] - The original error that caused this error (for error chaining).
38
+ */
39
+
40
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
41
+ /**
42
+ * Error thrown when middleware cannot be validated.
43
+ *
44
+ * @classdesc This error type provides identifiable failures related to
45
+ * middleware validation.
46
+ *
47
+ * @augments BaseError
48
+ * @private
49
+ */
50
+ class MiddlewareError extends BaseError {
51
+ /**
52
+ * Creates a new {@link MiddlewareError}.
53
+ *
54
+ * @param {MiddlewareErrorIdentifier} identifier - A predefined error identifier or a custom error message.
55
+ * @param {MiddlewareErrorOptions} [options] - Additional information used to format the message, attach domain details, or preserve the original cause.
56
+ * @example
57
+ * ```js
58
+ * throw new MiddlewareError('INVALID_MIDDLEWARE_TYPE', {
59
+ * values: {
60
+ * expected: 'function',
61
+ * received: 'string',
62
+ * },
63
+ * });
64
+ * ```
65
+ * @example
66
+ * ```js
67
+ * throw new MiddlewareError('INVALID_MIDDLEWARE_ARITY', {
68
+ * values: {
69
+ * expected: 2,
70
+ * received: 1,
71
+ * },
72
+ * });
73
+ * ```
74
+ */
75
+ constructor(identifier, options = {}) {
76
+ super(identifier, options);
77
+ }
78
+
79
+ /**
80
+ * Collection of predefined middleware error messages.
81
+ *
82
+ * Available error identifiers:
83
+ *
84
+ * - `'INVALID_MIDDLEWARE_TYPE'`.
85
+ * - `'INVALID_MIDDLEWARE_ARITY'`.
86
+ * - `'INVALID_MIDDLEWARES_TYPE'`.
87
+ *
88
+ * @private
89
+ * @static
90
+ * @type {Map<MiddlewareErrorIdentifier, string>}
91
+ */
92
+ static MESSAGES = new Map([
93
+ ['INVALID_MIDDLEWARE_TYPE', 'Expected middleware to be a {expected}, but received {received}.'],
94
+ [
95
+ 'INVALID_MIDDLEWARE_ARITY',
96
+ 'Middleware must declare exactly {expected} parameters, but received {received}.',
97
+ ],
98
+ [
99
+ 'INVALID_MIDDLEWARES_TYPE',
100
+ 'Expected middlewares to be an {expected}, but received {received}.',
101
+ ],
102
+ ]);
103
+ }
104
+
105
+ // ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
106
+ export default MiddlewareError;