@boo-labs/server 0.0.0-stage → 1.0.0
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.
- package/README.md +229 -2
- package/index.mjs +23 -0
- package/package.json +71 -4
- package/source/application/Kernel/index.mjs +1137 -0
- package/source/application/Server/index.mjs +1201 -0
- package/source/application/configurations/Http2CleartextServerConfiguration/index.mjs +168 -0
- package/source/application/configurations/Http2SecureServerConfiguration/index.mjs +172 -0
- package/source/application/configurations/HttpServerConfiguration/index.mjs +169 -0
- package/source/application/configurations/HttpsServerConfiguration/index.mjs +172 -0
- package/source/application/configurations/ServerConfiguration/Options/CORSOptions/CORSOptionsValue.mjs +158 -0
- package/source/application/configurations/ServerConfiguration/Options/CORSOptions/index.mjs +87 -0
- package/source/application/configurations/ServerConfiguration/Options/CSRFOptions/CSRFOptionsValue.mjs +178 -0
- package/source/application/configurations/ServerConfiguration/Options/CSRFOptions/index.mjs +80 -0
- package/source/application/configurations/ServerConfiguration/Options/CompressOptions/CompressOptionsValue.mjs +126 -0
- package/source/application/configurations/ServerConfiguration/Options/CompressOptions/index.mjs +81 -0
- package/source/application/configurations/ServerConfiguration/Options/CookieParserOptions/CookieParserOptionsValue.mjs +107 -0
- package/source/application/configurations/ServerConfiguration/Options/CookieParserOptions/index.mjs +82 -0
- package/source/application/configurations/ServerConfiguration/Options/CookieSessionOptions/CookieSessionOptionsValue.mjs +182 -0
- package/source/application/configurations/ServerConfiguration/Options/CookieSessionOptions/index.mjs +86 -0
- package/source/application/configurations/ServerConfiguration/Options/ListenOptions/ListenOptionsValue.mjs +95 -0
- package/source/application/configurations/ServerConfiguration/Options/ListenOptions/index.mjs +47 -0
- package/source/application/configurations/ServerConfiguration/Options/MiddlewareOption.mjs +102 -0
- package/source/application/configurations/ServerConfiguration/Options/Options.mjs +105 -0
- package/source/application/configurations/ServerConfiguration/Options/RateLimitOptions/RateLimitOptionsValue.mjs +164 -0
- package/source/application/configurations/ServerConfiguration/Options/RateLimitOptions/index.mjs +84 -0
- package/source/application/configurations/ServerConfiguration/Options/RequestOptions/RequestOptionsValue.mjs +108 -0
- package/source/application/configurations/ServerConfiguration/Options/RequestOptions/index.mjs +47 -0
- package/source/application/configurations/ServerConfiguration/Options/ResponseOptions/ResponseOptionsValue.mjs +101 -0
- package/source/application/configurations/ServerConfiguration/Options/ResponseOptions/index.mjs +47 -0
- package/source/application/configurations/ServerConfiguration/Options/SecurityHeadersOptions/SecurityHeadersOptionsValue.mjs +272 -0
- package/source/application/configurations/ServerConfiguration/Options/SecurityHeadersOptions/index.mjs +85 -0
- package/source/application/configurations/ServerConfiguration/Options/ServerOption.mjs +50 -0
- package/source/application/configurations/ServerConfiguration/Options/StaticOptions/StaticOptionsValue.mjs +164 -0
- package/source/application/configurations/ServerConfiguration/Options/StaticOptions/index.mjs +89 -0
- package/source/application/configurations/ServerConfiguration/index.mjs +781 -0
- package/source/application/resolvers/TransportResolver/index.mjs +119 -0
- package/source/application/use-cases/CloseServer/index.mjs +85 -0
- package/source/application/use-cases/ListenServer/index.mjs +98 -0
- package/source/core/EventSystem/index.mjs +110 -0
- package/source/core/Exchange/ExchangeContext.mjs +291 -0
- package/source/core/Exchange/Telemetry.mjs +193 -0
- package/source/core/Exchange/index.mjs +604 -0
- package/source/core/ExchangeLayerBuilder/index.mjs +133 -0
- package/source/core/IPCMain/IPCMain.mjs +507 -0
- package/source/core/IPCMain/IPCProtocol.mjs +441 -0
- package/source/core/IPCMain/IPCServerChannel.mjs +331 -0
- package/source/core/IPCMain/WebSocketAdapter.mjs +189 -0
- package/source/core/Request/BodyParser/BodyParser.mjs +197 -0
- package/source/core/Request/BodyParser/JSONParser.mjs +70 -0
- package/source/core/Request/BodyParser/MultipartParser.mjs +612 -0
- package/source/core/Request/BodyParser/URLEncodedParser.mjs +71 -0
- package/source/core/Request/RequestHeaders.mjs +177 -0
- package/source/core/Request/index.mjs +805 -0
- package/source/core/Response/ResponseHeaders.mjs +402 -0
- package/source/core/Response/index.mjs +1370 -0
- package/source/core/middlewares/CORSMiddleware/index.mjs +289 -0
- package/source/core/middlewares/CSRFMiddleware/index.mjs +284 -0
- package/source/core/middlewares/CompressMiddleware/index.mjs +460 -0
- package/source/core/middlewares/CookieParserMiddleware/index.mjs +194 -0
- package/source/core/middlewares/CookieSessionMiddleware/index.mjs +460 -0
- package/source/core/middlewares/RateLimitMiddleware/index.mjs +245 -0
- package/source/core/middlewares/SecurityHeadersMiddleware/index.mjs +151 -0
- package/source/core/middlewares/StaticMiddleware/index.mjs +628 -0
- package/source/core/routing/ExchangeLayer/ExchangeMatcher.mjs +127 -0
- package/source/core/routing/ExchangeLayer/index.mjs +254 -0
- package/source/core/routing/Pipeline/index.mjs +157 -0
- package/source/core/routing/Router/Route.mjs +184 -0
- package/source/core/routing/Router/index.mjs +523 -0
- package/source/core/routing/RouterRegister/OptionsMiddleware.mjs +147 -0
- package/source/core/routing/RouterRegister/index.mjs +150 -0
- package/source/core/routing/RouterSystem/index.mjs +225 -0
- package/source/core/value-objects/Address/index.mjs +281 -0
- package/source/core/value-objects/Endpoint/index.mjs +293 -0
- package/source/index.mjs +62 -0
- package/source/infrastructure/adapters/Http2Adapter/index.mjs +144 -0
- package/source/infrastructure/adapters/Http2CleartextAdapter/index.mjs +147 -0
- package/source/infrastructure/adapters/HttpAdapter/index.mjs +124 -0
- package/source/infrastructure/adapters/HttpsAdapter/index.mjs +124 -0
- package/source/infrastructure/adapters/TransportAdapter/createWebSocketOpenRequest.mjs +843 -0
- package/source/infrastructure/adapters/TransportAdapter/index.mjs +555 -0
- package/source/shared/Identifier/index.mjs +671 -0
- package/source/shared/constants/symbols/brands/index.mjs +45 -0
- package/source/shared/domain/UUIDV7/index.mjs +346 -0
- package/source/shared/errors/BaseError/index.mjs +157 -0
- package/source/shared/errors/IPCError/index.mjs +97 -0
- package/source/shared/errors/MiddlewareError/index.mjs +106 -0
- package/source/shared/errors/PathnamePatternError/index.mjs +103 -0
- package/source/shared/errors/RequestError/index.mjs +116 -0
- package/source/shared/errors/ResponseError/index.mjs +140 -0
- package/source/shared/errors/RouterError/index.mjs +90 -0
- package/source/shared/errors/ServerConfigurationError/index.mjs +551 -0
- package/source/shared/errors/ServerError/index.mjs +127 -0
- package/source/shared/errors/WebSocketError/index.mjs +58 -0
- package/source/shared/outcome/ErrorSchema.mjs +361 -0
- package/source/shared/outcome/Result/index.mjs +239 -0
- package/source/shared/outcome/Validation/index.mjs +208 -0
- package/source/shared/validations/Middleware/index.mjs +160 -0
- package/source/shared/validations/PathnamePattern/index.mjs +256 -0
- package/source/shared/validations/Server/NotFoundHandler/index.mjs +94 -0
- package/source/shared/validations/Server/Router/index.mjs +68 -0
- package/source/shared/validations/Server/ServerConfiguration/index.mjs +68 -0
- package/source/shared/validations/Server/SystemErrorHandler/index.mjs +94 -0
- package/source/shared/validations/ServerConfiguration/CORSOptions/index.mjs +310 -0
- package/source/shared/validations/ServerConfiguration/CSRFOptions/index.mjs +299 -0
- package/source/shared/validations/ServerConfiguration/CompressOptions/index.mjs +204 -0
- package/source/shared/validations/ServerConfiguration/CookieParserOptions/index.mjs +102 -0
- package/source/shared/validations/ServerConfiguration/CookieSessionOptions/index.mjs +332 -0
- package/source/shared/validations/ServerConfiguration/ListenOptions/index.mjs +178 -0
- package/source/shared/validations/ServerConfiguration/RateLimitOptions/index.mjs +229 -0
- package/source/shared/validations/ServerConfiguration/RequestOptions/index.mjs +132 -0
- package/source/shared/validations/ServerConfiguration/ResponseOptions/index.mjs +122 -0
- package/source/shared/validations/ServerConfiguration/SecurityHeadersOptions/index.mjs +137 -0
- package/source/shared/validations/ServerConfiguration/StaticOptions/index.mjs +248 -0
- package/types/index.d.ts +719 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Defines the internal error type used for WebSocket failures.
|
|
3
|
+
*
|
|
4
|
+
* @description WebSocketError provides structured errors for WebSocket protocol
|
|
5
|
+
* and connection failures, including errors that map to a WebSocket close code.
|
|
6
|
+
*
|
|
7
|
+
* @private
|
|
8
|
+
* @module WebSocketError
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import BaseError from '../BaseError/index.mjs';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {import('../BaseError/index.mjs').ErrorOptions} ErrorOptions
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Internal error type used for WebSocket protocol and connection failures.
|
|
19
|
+
*
|
|
20
|
+
* @augments BaseError
|
|
21
|
+
* @private
|
|
22
|
+
*/
|
|
23
|
+
class WebSocketError extends BaseError {
|
|
24
|
+
/**
|
|
25
|
+
* Creates a new {@link WebSocketError}.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} identifier - A predefined WebSocket error identifier or a custom error message.
|
|
28
|
+
* @param {ErrorOptions} [options] - Additional error options.
|
|
29
|
+
* @example
|
|
30
|
+
* ```js
|
|
31
|
+
* throw new WebSocketError('WS_MESSAGE_TOO_BIG', {
|
|
32
|
+
* values: {
|
|
33
|
+
* size: payloadLength,
|
|
34
|
+
* maxSize: maxMessageSize,
|
|
35
|
+
* },
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
constructor(identifier, options = {}) {
|
|
40
|
+
super(identifier, options);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Message template catalog indexed by WebSocket error identifier.
|
|
45
|
+
*
|
|
46
|
+
* @type {Map<string, string>}
|
|
47
|
+
* @private
|
|
48
|
+
* @static
|
|
49
|
+
*/
|
|
50
|
+
static MESSAGES = new Map([
|
|
51
|
+
[
|
|
52
|
+
'WS_MESSAGE_TOO_BIG',
|
|
53
|
+
'WebSocket payload size ({size} bytes) exceeds maximum allowed message size ({maxSize} bytes).',
|
|
54
|
+
],
|
|
55
|
+
]);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default WebSocketError;
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Defines the structured schema used to describe operation errors.
|
|
3
|
+
*
|
|
4
|
+
* @description ErrorSchema normalizes literal-message and code-based error
|
|
5
|
+
* definitions into a consistent schema used to instantiate Error objects.
|
|
6
|
+
*
|
|
7
|
+
* @module ErrorSchema
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
11
|
+
// » IMPORT NATIVE NODEJS MODULES
|
|
12
|
+
import { inspect } from 'node:util';
|
|
13
|
+
|
|
14
|
+
// » IMPORT THIRD PARTY MODULES
|
|
15
|
+
|
|
16
|
+
// » IMPORT LOCAL MODULES
|
|
17
|
+
|
|
18
|
+
// ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
19
|
+
/**
|
|
20
|
+
* Represents an Error constructor.
|
|
21
|
+
*
|
|
22
|
+
* @typedef {new (identifier: string, options?: Record<string, unknown>) => Error} ErrorConstructor
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Represents options passed to a native Error constructor.
|
|
27
|
+
*
|
|
28
|
+
* @typedef {object} NativeErrorOptions
|
|
29
|
+
* @property {unknown} [cause] - Cause associated with the error.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Represents a native Error schema definition.
|
|
34
|
+
*
|
|
35
|
+
* @typedef {object} NativeErrorSchema
|
|
36
|
+
* @property {ErrorConstructor} error - Error constructor used to create the error.
|
|
37
|
+
* @property {string} message - Literal error message.
|
|
38
|
+
* @property {NativeErrorOptions} [options] - Options passed to the Error constructor.
|
|
39
|
+
* @property {false} custom - Indicates that the constructor represents a native Error.
|
|
40
|
+
* @property {string} code - Error identifier.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Represents options passed to a custom Error constructor.
|
|
45
|
+
*
|
|
46
|
+
* @typedef {object} CustomErrorOptions
|
|
47
|
+
* @property {Record<string, unknown>} [values] - Values used to resolve message placeholders.
|
|
48
|
+
* @property {Record<string, unknown>} [details] - Additional contextual information about the error.
|
|
49
|
+
* @property {unknown} [cause] - Cause associated with the error.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Represents a custom Error schema definition.
|
|
54
|
+
*
|
|
55
|
+
* @typedef {object} CustomErrorSchema
|
|
56
|
+
* @property {ErrorConstructor} error - Error constructor used to create the error.
|
|
57
|
+
* @property {string} code - Error code used to resolve the error message and error identifier.
|
|
58
|
+
* @property {string} [message] - Literal error message.
|
|
59
|
+
* @property {CustomErrorOptions} [options] - Options passed to the Error constructor.
|
|
60
|
+
* @property {true} custom - Indicates that the constructor represents a custom Error.
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Represents a supported Error schema definition.
|
|
65
|
+
*
|
|
66
|
+
* @typedef {NativeErrorSchema|CustomErrorSchema} ErrorSchemaDefinition
|
|
67
|
+
* @exports ErrorSchemaDefinition
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Represents the normalized state of an ErrorSchema.
|
|
72
|
+
*
|
|
73
|
+
* @typedef {object} ErrorSchemaState
|
|
74
|
+
* @property {string} [message] - Literal error message.
|
|
75
|
+
* @property {string} type - Error constructor name.
|
|
76
|
+
* @property {Record<string, unknown>} options - Options passed to the Error constructor.
|
|
77
|
+
* @property {string} code - Error identifier.
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
// ━━ FUNCTIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
81
|
+
/**
|
|
82
|
+
* Determines whether a value is a plain object.
|
|
83
|
+
*
|
|
84
|
+
* @private
|
|
85
|
+
* @param {unknown} value - Value to check.
|
|
86
|
+
* @returns {boolean} Whether the value is a plain object.
|
|
87
|
+
*/
|
|
88
|
+
const isObject = value => Object.prototype.toString.call(value) === '[object Object]';
|
|
89
|
+
|
|
90
|
+
// ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
91
|
+
/**
|
|
92
|
+
* Defines a structured error schema and creates its corresponding Error.
|
|
93
|
+
*
|
|
94
|
+
* @classdesc ErrorSchema accepts either a literal error message or a domain
|
|
95
|
+
* error code. Message-based schemas create errors directly from the message,
|
|
96
|
+
* while code-based schemas preserve the code, values, and details required by
|
|
97
|
+
* the consuming error system.
|
|
98
|
+
*/
|
|
99
|
+
class ErrorSchema {
|
|
100
|
+
/**
|
|
101
|
+
* Normalized error schema definition.
|
|
102
|
+
*
|
|
103
|
+
* @type {ErrorSchemaState}
|
|
104
|
+
*/
|
|
105
|
+
#schema;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Error constructor used to reconstruct the error.
|
|
109
|
+
*
|
|
110
|
+
* @type {typeof Error}
|
|
111
|
+
*/
|
|
112
|
+
#error;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Indicates whether the error uses the custom Error flow.
|
|
116
|
+
*
|
|
117
|
+
* @type {boolean}
|
|
118
|
+
*/
|
|
119
|
+
#custom;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Creates an error schema from an Error definition.
|
|
123
|
+
*
|
|
124
|
+
* The schema must provide an Error constructor. Native Errors require a
|
|
125
|
+
* message and code, while custom Errors require a code and may provide a message.
|
|
126
|
+
*
|
|
127
|
+
* @param {ErrorSchemaDefinition} schema - Error schema configuration.
|
|
128
|
+
* @throws {TypeError} If `error` is not an Error constructor.
|
|
129
|
+
* @throws {TypeError} If a native Error does not provide a message.
|
|
130
|
+
* @throws {TypeError} If message is not a string.
|
|
131
|
+
* @throws {TypeError} If a custom Error provides neither message nor code.
|
|
132
|
+
* @example
|
|
133
|
+
* ```js
|
|
134
|
+
* const schema = new ErrorSchema({
|
|
135
|
+
* error: TypeError,
|
|
136
|
+
* message: 'Invalid request.'
|
|
137
|
+
* });
|
|
138
|
+
*
|
|
139
|
+
* schema.error;
|
|
140
|
+
* ```
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```js
|
|
144
|
+
* const schema = new ErrorSchema({
|
|
145
|
+
* error: RequestError,
|
|
146
|
+
* code: 'INVALID_REQUEST',
|
|
147
|
+
* options: {
|
|
148
|
+
* values: { field: 'email' },
|
|
149
|
+
* details: { source: 'RequestValidator' }
|
|
150
|
+
* },
|
|
151
|
+
* custom: true
|
|
152
|
+
* });
|
|
153
|
+
*
|
|
154
|
+
* schema.error;
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
constructor(schema) {
|
|
158
|
+
if (!isObject(schema)) {
|
|
159
|
+
throw new TypeError('Schema options must be an object.');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (!('error' in schema)) {
|
|
163
|
+
throw new TypeError('ErrorSchema error constructor is required.');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (!ErrorSchema.isErrorConstructor(schema.error)) {
|
|
167
|
+
throw new TypeError('Expected an Error constructor');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const custom = schema.custom ?? false;
|
|
171
|
+
|
|
172
|
+
if (!custom && !('message' in schema)) {
|
|
173
|
+
throw new TypeError('Message is required for a native Error');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (!custom && !('code' in schema)) {
|
|
177
|
+
throw new TypeError('Code is required for Error Schema');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if ('message' in schema && typeof schema.message !== 'string') {
|
|
181
|
+
throw new TypeError('Message must be a string');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (custom && !('message' in schema) && !('code' in schema)) {
|
|
185
|
+
throw new TypeError('Message or code is required for a Custom Error');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
this.#error = schema.error;
|
|
189
|
+
this.#custom = custom;
|
|
190
|
+
|
|
191
|
+
this.#schema = Object.freeze({
|
|
192
|
+
message: schema.message,
|
|
193
|
+
type: schema.error.name,
|
|
194
|
+
options: schema.options ?? {},
|
|
195
|
+
code: schema.code,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Normalized error schema.
|
|
201
|
+
*
|
|
202
|
+
* @readonly
|
|
203
|
+
* @type {object}
|
|
204
|
+
*/
|
|
205
|
+
get schema() {
|
|
206
|
+
return this.#schema;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Creates a new Error instance from the configured schema.
|
|
211
|
+
*
|
|
212
|
+
* @readonly
|
|
213
|
+
* @type {Error}
|
|
214
|
+
*/
|
|
215
|
+
get error() {
|
|
216
|
+
const { message, options, code } = this.#schema;
|
|
217
|
+
|
|
218
|
+
if (this.#custom) {
|
|
219
|
+
return new this.#error(message ?? code, options);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return new this.#error(message, options);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Custom string tag for type identification.
|
|
227
|
+
*
|
|
228
|
+
* @readonly
|
|
229
|
+
* @type {string}
|
|
230
|
+
*/
|
|
231
|
+
get [Symbol.toStringTag]() {
|
|
232
|
+
return `${this.constructor.name}`;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Creates a plain object representation of the error schema.
|
|
237
|
+
*
|
|
238
|
+
* @returns {ErrorSchemaState} Normalized error schema.
|
|
239
|
+
* @example
|
|
240
|
+
* ```js
|
|
241
|
+
* const schema = new ErrorSchema({
|
|
242
|
+
* error: TypeError,
|
|
243
|
+
* message: 'Invalid middleware.',
|
|
244
|
+
* code: 'INVALID_MIDDLEWARE',
|
|
245
|
+
* custom: false
|
|
246
|
+
* });
|
|
247
|
+
*
|
|
248
|
+
* console.log(schema.toObject());
|
|
249
|
+
* // {
|
|
250
|
+
* // message: 'Invalid middleware.',
|
|
251
|
+
* // type: 'TypeError',
|
|
252
|
+
* // options: {},
|
|
253
|
+
* // code: 'INVALID_MIDDLEWARE'
|
|
254
|
+
* // }
|
|
255
|
+
* ```
|
|
256
|
+
*/
|
|
257
|
+
toObject() {
|
|
258
|
+
return { ...this.#schema };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Creates a JSON-compatible representation of the error schema.
|
|
263
|
+
*
|
|
264
|
+
* @returns {ErrorSchemaState} Normalized error schema.
|
|
265
|
+
* @example
|
|
266
|
+
* ```js
|
|
267
|
+
* const schema = new ErrorSchema({
|
|
268
|
+
* error: RequestError,
|
|
269
|
+
* code: 'INVALID_REQUEST',
|
|
270
|
+
* options: {
|
|
271
|
+
* values: { field: 'email' },
|
|
272
|
+
* details: { source: 'RequestValidator' }
|
|
273
|
+
* },
|
|
274
|
+
* custom: true
|
|
275
|
+
* });
|
|
276
|
+
*
|
|
277
|
+
* console.log(JSON.stringify(schema));
|
|
278
|
+
* // {"message":undefined,"type":"RequestError",
|
|
279
|
+
* // "options":{"values":{"field":"email"},
|
|
280
|
+
* // "details":{"source":"RequestValidator"}},
|
|
281
|
+
* // "code":"INVALID_REQUEST"}
|
|
282
|
+
* ```
|
|
283
|
+
*/
|
|
284
|
+
toJSON() {
|
|
285
|
+
const schema = this.toObject();
|
|
286
|
+
const options = schema.options;
|
|
287
|
+
|
|
288
|
+
const base = {
|
|
289
|
+
message: schema.message,
|
|
290
|
+
type: schema.type,
|
|
291
|
+
code: schema.code,
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
if ('cause' in options && options.cause instanceof Error) {
|
|
295
|
+
const { cause, ...rest } = options;
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
...base,
|
|
299
|
+
options: {
|
|
300
|
+
...rest,
|
|
301
|
+
cause: {
|
|
302
|
+
name: cause.name,
|
|
303
|
+
message: cause.message,
|
|
304
|
+
stack: cause.stack,
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
...base,
|
|
312
|
+
options,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Custom inspect implementation for Node.js `util.inspect()`.
|
|
318
|
+
*
|
|
319
|
+
* @returns {string} Custom string representation for debugging.
|
|
320
|
+
* @example
|
|
321
|
+
* ```js
|
|
322
|
+
* const schema = new ErrorSchema({
|
|
323
|
+
* error: TypeError,
|
|
324
|
+
* message: 'Invalid middleware.',
|
|
325
|
+
* code: 'INVALID_MIDDLEWARE',
|
|
326
|
+
* custom: false
|
|
327
|
+
* });
|
|
328
|
+
*
|
|
329
|
+
* console.log(inspect(schema));
|
|
330
|
+
* // ErrorSchema({
|
|
331
|
+
* // message: 'Invalid middleware.',
|
|
332
|
+
* // type: 'TypeError',
|
|
333
|
+
* // options: {},
|
|
334
|
+
* // code: 'INVALID_MIDDLEWARE'
|
|
335
|
+
* // })
|
|
336
|
+
* ```
|
|
337
|
+
*/
|
|
338
|
+
[inspect.custom]() {
|
|
339
|
+
return `${this.constructor.name}(${inspect(this.toObject(), { colors: true, depth: 2 })})`;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Determines whether a value is an Error constructor.
|
|
344
|
+
*
|
|
345
|
+
* @param {unknown} error - Value to check.
|
|
346
|
+
* @returns {boolean} Whether `error` is a constructor that inherits from `Error`.
|
|
347
|
+
* @example
|
|
348
|
+
* ```js
|
|
349
|
+
* ErrorSchema.isErrorConstructor(TypeError); // true
|
|
350
|
+
* ErrorSchema.isErrorConstructor(RangeError); // true
|
|
351
|
+
* ErrorSchema.isErrorConstructor(Math); // false
|
|
352
|
+
* ```
|
|
353
|
+
*/
|
|
354
|
+
static isErrorConstructor(error) {
|
|
355
|
+
if (error === Error) return true;
|
|
356
|
+
if (typeof error !== 'function') return false;
|
|
357
|
+
return Object.prototype.isPrototypeOf.call(Error.prototype, error.prototype);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
// ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
361
|
+
export default ErrorSchema;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* @file Core result module for representing strict operation outcomes.
|
|
4
|
+
*
|
|
5
|
+
* @description Provides the {@link Result} class for explicitly representing
|
|
6
|
+
* successful and failed operations without relying on ambiguous `undefined`
|
|
7
|
+
* states.
|
|
8
|
+
*
|
|
9
|
+
* @module Result
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
13
|
+
// » IMPORT LOCAL MODULES
|
|
14
|
+
import ErrorSchema from '../ErrorSchema.mjs';
|
|
15
|
+
|
|
16
|
+
// ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
17
|
+
/**
|
|
18
|
+
* Represents options passed to a native Error constructor.
|
|
19
|
+
*
|
|
20
|
+
* @typedef {object} NativeErrorOptions
|
|
21
|
+
* @property {unknown} [cause] - Cause associated with the error.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Represents a native Error schema definition.
|
|
26
|
+
*
|
|
27
|
+
* @typedef {object} NativeErrorSchema
|
|
28
|
+
* @property {typeof Error} error - Error constructor used to create the error.
|
|
29
|
+
* @property {string} message - Literal error message.
|
|
30
|
+
* @property {NativeErrorOptions} [options] - Options passed to the Error constructor.
|
|
31
|
+
* @property {false} custom - Indicates that the constructor represents a native Error.
|
|
32
|
+
* @property {string} code - Error identifier.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Represents options passed to a custom Error constructor.
|
|
37
|
+
*
|
|
38
|
+
* @typedef {object} CustomErrorOptions
|
|
39
|
+
* @property {Record<string, unknown>} [values] - Values used to resolve message placeholders.
|
|
40
|
+
* @property {Record<string, unknown>} [details] - Additional contextual information about the error.
|
|
41
|
+
* @property {unknown} [cause] - Cause associated with the error.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Represents a custom Error schema definition.
|
|
46
|
+
*
|
|
47
|
+
* @typedef {object} CustomErrorSchema
|
|
48
|
+
* @property {typeof Error} error - Error constructor used to create the error.
|
|
49
|
+
* @property {string} code - Error code used to resolve the error message and error identifier.
|
|
50
|
+
* @property {string} [message] - Literal error message.
|
|
51
|
+
* @property {CustomErrorOptions} [options] - Options passed to the Error constructor.
|
|
52
|
+
* @property {true} custom - Indicates that the constructor represents a custom Error.
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Represents a supported Error schema definition.
|
|
57
|
+
*
|
|
58
|
+
* @typedef {NativeErrorSchema|CustomErrorSchema} ErrorSchemaDefinition
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Represents the successful state of a {@link Result} instance.
|
|
63
|
+
*
|
|
64
|
+
* @template T
|
|
65
|
+
* @typedef {object} ResultSuccess
|
|
66
|
+
* @property {true} ok - Indicates that the operation succeeded.
|
|
67
|
+
* @property {T} value - Successful value produced by the operation.
|
|
68
|
+
* @property {undefined} [error] - Always undefined for a successful result.
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Represents the failed state of a {@link Result} instance.
|
|
73
|
+
*
|
|
74
|
+
* @typedef {object} ResultFailure
|
|
75
|
+
* @property {false} ok - Indicates that the operation failed.
|
|
76
|
+
* @property {undefined} [value] - Always undefined for a failed result.
|
|
77
|
+
* @property {ErrorSchema} error - Error schema describing the operation failure.
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Represents the internal state of a {@link Result} instance.
|
|
82
|
+
*
|
|
83
|
+
* @template T
|
|
84
|
+
* @typedef {ResultSuccess<T>|ResultFailure} ResultState
|
|
85
|
+
*/
|
|
86
|
+
|
|
87
|
+
// ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
88
|
+
/**
|
|
89
|
+
* Represents the strict outcome of an operation that may yield a value or an
|
|
90
|
+
* error.
|
|
91
|
+
*
|
|
92
|
+
* @template T
|
|
93
|
+
*/
|
|
94
|
+
class Result {
|
|
95
|
+
/**
|
|
96
|
+
* Stores the internal result state.
|
|
97
|
+
*
|
|
98
|
+
* @type {ResultState<T>}
|
|
99
|
+
*/
|
|
100
|
+
#result;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Creates a Result instance.
|
|
104
|
+
*
|
|
105
|
+
* @param {boolean} ok - Whether the operation succeeded.
|
|
106
|
+
* @param {T} [value] - The successful value.
|
|
107
|
+
* @param {ErrorSchemaDefinition} [error] - Required when ok is false.
|
|
108
|
+
* @throws {TypeError} If the combination of ok, value, and error is invalid.
|
|
109
|
+
* @example
|
|
110
|
+
* ```js
|
|
111
|
+
* const result = new Result(true, { id: '123' });
|
|
112
|
+
* ```
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```js
|
|
116
|
+
* const result = new Result(false, undefined, {
|
|
117
|
+
* error: RangeError,
|
|
118
|
+
* message: 'Value is out of range.',
|
|
119
|
+
* custom: false,
|
|
120
|
+
* code: 'ERR_RANGE',
|
|
121
|
+
* });
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
constructor(ok, value, error) {
|
|
125
|
+
if (typeof ok !== 'boolean') {
|
|
126
|
+
throw new TypeError('Result status (ok) must be a boolean.');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (ok === true) {
|
|
130
|
+
if (value === undefined) {
|
|
131
|
+
throw new TypeError('A successful Result must contain a value.');
|
|
132
|
+
}
|
|
133
|
+
if (error !== undefined) {
|
|
134
|
+
throw new TypeError('A successful Result cannot contain an error.');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
this.#result = { ok, value, error };
|
|
138
|
+
} else {
|
|
139
|
+
if (error === undefined) {
|
|
140
|
+
throw new TypeError('A failed Result must contain an error.');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (value !== undefined) {
|
|
144
|
+
throw new TypeError('A failed Result cannot contain a value.');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
this.#result = {
|
|
148
|
+
ok,
|
|
149
|
+
value: undefined,
|
|
150
|
+
error: new ErrorSchema(error),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Indicates whether the operation succeeded.
|
|
157
|
+
*
|
|
158
|
+
* @readonly
|
|
159
|
+
* @type {boolean}
|
|
160
|
+
*/
|
|
161
|
+
get ok() {
|
|
162
|
+
return this.#result.ok;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The successful value of the operation.
|
|
167
|
+
*
|
|
168
|
+
* @readonly
|
|
169
|
+
* @type {T}
|
|
170
|
+
* @throws {Error} If the result is not ok.
|
|
171
|
+
*/
|
|
172
|
+
get value() {
|
|
173
|
+
if (this.#result.ok === false) {
|
|
174
|
+
throw new Error('Cannot access value of a failed Result.');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return this.#result.value;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* The instantiated error of the failed operation.
|
|
182
|
+
*
|
|
183
|
+
* @readonly
|
|
184
|
+
* @type {Error}
|
|
185
|
+
* @throws {Error} If the result is ok.
|
|
186
|
+
*/
|
|
187
|
+
get error() {
|
|
188
|
+
if (this.#result.ok === true) {
|
|
189
|
+
throw new Error('Cannot access error of a successful Result.');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return this.#result.error.error;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Creates a successful {@link Result}.
|
|
197
|
+
*
|
|
198
|
+
* @template U
|
|
199
|
+
* @param {U} [value] - The successful value.
|
|
200
|
+
* @returns {Result<U>} A successful Result instance.
|
|
201
|
+
* @example
|
|
202
|
+
* ```js
|
|
203
|
+
* const result = Result.success({ id: '123' });
|
|
204
|
+
*
|
|
205
|
+
* if (result.ok) {
|
|
206
|
+
* console.log(result.value.id);
|
|
207
|
+
* }
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
static success(value) {
|
|
211
|
+
return new Result(true, value);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Creates a failed {@link Result}.
|
|
216
|
+
*
|
|
217
|
+
* @param {ErrorSchemaDefinition} error - The error details describing the failure.
|
|
218
|
+
* @returns {Result<never>} A failed Result instance.
|
|
219
|
+
* @example
|
|
220
|
+
* ```js
|
|
221
|
+
* const result = Result.failure({
|
|
222
|
+
* error: RangeError,
|
|
223
|
+
* message: 'Value is out of range.',
|
|
224
|
+
* custom: false,
|
|
225
|
+
* code: 'ERR_RANGE',
|
|
226
|
+
* });
|
|
227
|
+
*
|
|
228
|
+
* if (!result.ok) {
|
|
229
|
+
* console.log(result.error);
|
|
230
|
+
* }
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
static failure(error) {
|
|
234
|
+
return /** @type {Result<never>} */ (new Result(false, undefined, error));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
239
|
+
export default Result;
|