@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,441 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* @file Defines the IPC wire protocol.
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* Validates incoming IPC requests and serializes outgoing IPC responses.
|
|
7
|
+
*
|
|
8
|
+
* IPCProtocol is the sole owner of wire-format normalization. Higher layers
|
|
9
|
+
* pass raw handler errors to this protocol instead of constructing IPC error
|
|
10
|
+
* payloads themselves.
|
|
11
|
+
*
|
|
12
|
+
* @module IPCProtocol
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
16
|
+
// » IMPORT LOCAL MODULES
|
|
17
|
+
import Result from '#shared/outcome/Result';
|
|
18
|
+
|
|
19
|
+
// ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
20
|
+
/**
|
|
21
|
+
* Represents an IPC invocation request.
|
|
22
|
+
*
|
|
23
|
+
* An invocation expects a matching response carrying the same `id`.
|
|
24
|
+
*
|
|
25
|
+
* @typedef {object} IPCInvokeRequest
|
|
26
|
+
* @property {'invoke'} type - Identifies the request as an invocation.
|
|
27
|
+
* @property {string} channel - IPC channel targeted by the request.
|
|
28
|
+
* @property {string} id - Unique invocation identifier.
|
|
29
|
+
* @property {unknown} [data] - Application-defined data associated with the request.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Represents an IPC event request.
|
|
34
|
+
*
|
|
35
|
+
* An event does not expect a response.
|
|
36
|
+
*
|
|
37
|
+
* @typedef {object} IPCEventRequest
|
|
38
|
+
* @property {'event'} type - Identifies the request as an event.
|
|
39
|
+
* @property {string} channel - IPC channel targeted by the request.
|
|
40
|
+
* @property {unknown} [data] - Application-defined data associated with the request.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Represents an IPC request transmitted through the wire protocol.
|
|
45
|
+
*
|
|
46
|
+
* @typedef {IPCInvokeRequest|IPCEventRequest} IPCRequest
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Represents the normalized error payload exposed through the IPC wire format.
|
|
51
|
+
*
|
|
52
|
+
* @typedef {object} IPCError
|
|
53
|
+
* @property {string} code - Machine-readable error code.
|
|
54
|
+
* @property {string} message - Human-readable error message.
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Represents a successful IPC response containing a handler result.
|
|
59
|
+
*
|
|
60
|
+
* @typedef {object} IPCSuccessResponse
|
|
61
|
+
* @property {'response'} type - Identifies the message as an IPC response.
|
|
62
|
+
* @property {string} id - Invocation identifier associated with the request.
|
|
63
|
+
* @property {unknown} result - Handler result returned to the caller.
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Represents a failed IPC response containing a normalized error.
|
|
68
|
+
*
|
|
69
|
+
* @typedef {object} IPCErrorResponse
|
|
70
|
+
* @property {'response'} type - Identifies the message as an IPC response.
|
|
71
|
+
* @property {string} id - Invocation identifier associated with the request.
|
|
72
|
+
* @property {IPCError} error - Normalized error returned to the caller.
|
|
73
|
+
*/
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Represents an IPC response transmitted through the wire protocol.
|
|
77
|
+
*
|
|
78
|
+
* @typedef {IPCSuccessResponse|IPCErrorResponse} IPCResponse
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
// ━━ CONSTANTS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
82
|
+
/**
|
|
83
|
+
* Error code used when an incoming IPC payload cannot be decoded as a valid
|
|
84
|
+
* request.
|
|
85
|
+
*
|
|
86
|
+
* @type {string}
|
|
87
|
+
*/
|
|
88
|
+
const IPC_BAD_MESSAGE = 'IPC_BAD_MESSAGE';
|
|
89
|
+
|
|
90
|
+
// ━━ FUNCTIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
91
|
+
/**
|
|
92
|
+
* Determines whether a value is a non-null plain object.
|
|
93
|
+
*
|
|
94
|
+
* @private
|
|
95
|
+
* @param {unknown} value - Value to inspect.
|
|
96
|
+
* @returns {value is Record<string, unknown>} `true` if the value is a plain object.
|
|
97
|
+
*/
|
|
98
|
+
function isPlainObject(value) {
|
|
99
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
103
|
+
/**
|
|
104
|
+
* Implements the IPC wire protocol for decoding requests and serializing responses.
|
|
105
|
+
*
|
|
106
|
+
* @classdesc IPCProtocol validates incoming IPC requests and normalizes outgoing response
|
|
107
|
+
* payloads without exposing wire-format concerns to higher layers.
|
|
108
|
+
*
|
|
109
|
+
*/
|
|
110
|
+
class IPCProtocol {
|
|
111
|
+
/**
|
|
112
|
+
* Decodes and validates an incoming IPC request.
|
|
113
|
+
*
|
|
114
|
+
* @param {string} data - JSON-encoded IPC request to decode.
|
|
115
|
+
* @returns {IPCRequest} Decoded and validated IPC request.
|
|
116
|
+
* @throws {TypeError} If the payload is not valid JSON.
|
|
117
|
+
* @throws {Error} If the payload does not satisfy the IPC request contract.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```js
|
|
121
|
+
* const request = protocol.decode(
|
|
122
|
+
* '{"type":"invoke","channel":"users:get","id":"42"}',
|
|
123
|
+
* );
|
|
124
|
+
*
|
|
125
|
+
* console.log(request.channel);
|
|
126
|
+
* // 'users:get'
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
decode(data) {
|
|
130
|
+
const result = this.#ensureRequest(this.#parse(data));
|
|
131
|
+
|
|
132
|
+
if (!result.ok) {
|
|
133
|
+
throw result.error;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return result.value;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Serializes a validated IPC response into its wire representation.
|
|
141
|
+
*
|
|
142
|
+
* @param {IPCResponse} response - Validated IPC response to serialize.
|
|
143
|
+
* @returns {string} JSON-encoded IPC response.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```js
|
|
147
|
+
* const data = protocol.encode({
|
|
148
|
+
* type: 'response',
|
|
149
|
+
* id: '42',
|
|
150
|
+
* result: { name: 'Victor' },
|
|
151
|
+
* });
|
|
152
|
+
*
|
|
153
|
+
* console.log(data);
|
|
154
|
+
* // '{"type":"response","id":"42","result":{"name":"Victor"}}'
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
encode(response) {
|
|
158
|
+
return JSON.stringify(response);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Creates and serializes a successful IPC response.
|
|
163
|
+
*
|
|
164
|
+
* @param {string} id - Invocation identifier associated with the request.
|
|
165
|
+
* @param {unknown} result - Handler result returned to the caller.
|
|
166
|
+
* @returns {string} JSON-encoded successful IPC response.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```js
|
|
170
|
+
* const data = protocol.success('42', { name: 'Victor' });
|
|
171
|
+
*
|
|
172
|
+
* console.log(data);
|
|
173
|
+
* // '{"type":"response","id":"42","result":{"name":"Victor"}}'
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
success(id, result) {
|
|
177
|
+
return this.encode({
|
|
178
|
+
type: 'response',
|
|
179
|
+
id,
|
|
180
|
+
result,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Creates and serializes an IPC error response from an arbitrary error value.
|
|
186
|
+
*
|
|
187
|
+
* The supplied error is normalized to the IPC wire error format before the
|
|
188
|
+
* response is serialized.
|
|
189
|
+
*
|
|
190
|
+
* @param {string} id - Invocation identifier associated with the request.
|
|
191
|
+
* @param {unknown} error - Error value produced while handling the request.
|
|
192
|
+
* @returns {string} JSON-encoded IPC error response.
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* ```js
|
|
196
|
+
* const data = protocol.error('42', new Error('User not found'));
|
|
197
|
+
*
|
|
198
|
+
* console.log(data);
|
|
199
|
+
* // '{"type":"response","id":"42","error":{"code":"IPC_HANDLER_ERROR","message":"User not found"}}'
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
error(id, error) {
|
|
203
|
+
return this.encode({
|
|
204
|
+
type: 'response',
|
|
205
|
+
id,
|
|
206
|
+
error: this.#normalizeError(error),
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Creates and serializes an IPC error response for a malformed request.
|
|
212
|
+
*
|
|
213
|
+
* This method is used when a valid invocation identifier can still be
|
|
214
|
+
* recovered from the malformed payload.
|
|
215
|
+
*
|
|
216
|
+
* @param {string} id - Invocation identifier recovered from the request.
|
|
217
|
+
* @param {string} message - Description of the malformed request.
|
|
218
|
+
* @returns {string} JSON-encoded IPC error response.
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```js
|
|
222
|
+
* const data = protocol.badMessage('42', 'IPC channel must be a non-empty string.');
|
|
223
|
+
*
|
|
224
|
+
* console.log(data);
|
|
225
|
+
* // '{"type":"response","id":"42","error":{"code":"IPC_BAD_MESSAGE","message":"IPC channel must be a non-empty string."}}'
|
|
226
|
+
* ```
|
|
227
|
+
*/
|
|
228
|
+
badMessage(id, message) {
|
|
229
|
+
return this.encode({
|
|
230
|
+
type: 'response',
|
|
231
|
+
id,
|
|
232
|
+
error: {
|
|
233
|
+
code: IPC_BAD_MESSAGE,
|
|
234
|
+
message,
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Ensures a parsed IPC payload is a valid request.
|
|
241
|
+
*
|
|
242
|
+
* Returns a successful {@link Result} containing the normalized request, or
|
|
243
|
+
* a failure describing the first structural violation encountered.
|
|
244
|
+
*
|
|
245
|
+
* @param {unknown} message - Parsed IPC payload.
|
|
246
|
+
* @returns {Result<IPCRequest>} Successful result containing the normalized request, or a failure describing the validation error.
|
|
247
|
+
*/
|
|
248
|
+
#ensureRequest(message) {
|
|
249
|
+
if (!isPlainObject(message)) {
|
|
250
|
+
return Result.failure({
|
|
251
|
+
error: TypeError,
|
|
252
|
+
message: 'IPC message must be a plain object.',
|
|
253
|
+
code: 'IPC_INVALID_MESSAGE',
|
|
254
|
+
custom: false,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (!('type' in message)) {
|
|
259
|
+
return Result.failure({
|
|
260
|
+
error: ReferenceError,
|
|
261
|
+
message: 'IPC message requires a "type" property.',
|
|
262
|
+
code: 'IPC_MISSING_TYPE',
|
|
263
|
+
custom: false,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (message.type === 'invoke') {
|
|
268
|
+
return this.#ensureInvokeRequest(message);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (message.type === 'event') {
|
|
272
|
+
return this.#ensureEventRequest(message);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return Result.failure({
|
|
276
|
+
error: TypeError,
|
|
277
|
+
message: 'IPC message type must be "invoke" or "event".',
|
|
278
|
+
code: 'IPC_INVALID_TYPE',
|
|
279
|
+
custom: false,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Ensures a parsed IPC payload is a valid invocation request.
|
|
285
|
+
*
|
|
286
|
+
* @param {Record<string, unknown>} message - Parsed IPC payload.
|
|
287
|
+
* @returns {Result<IPCInvokeRequest>} Successful result containing the normalized request, or a failure describing the validation error.
|
|
288
|
+
*/
|
|
289
|
+
#ensureInvokeRequest(message) {
|
|
290
|
+
if (!('channel' in message)) {
|
|
291
|
+
return Result.failure({
|
|
292
|
+
error: ReferenceError,
|
|
293
|
+
message: 'IPC message requires a "channel" property.',
|
|
294
|
+
code: 'IPC_MISSING_CHANNEL',
|
|
295
|
+
custom: false,
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (typeof message.channel !== 'string') {
|
|
300
|
+
return Result.failure({
|
|
301
|
+
error: TypeError,
|
|
302
|
+
message: 'IPC channel must be a string.',
|
|
303
|
+
code: 'IPC_INVALID_CHANNEL_TYPE',
|
|
304
|
+
custom: false,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (message.channel.trim().length === 0) {
|
|
309
|
+
return Result.failure({
|
|
310
|
+
error: RangeError,
|
|
311
|
+
message: 'IPC channel must be a non-empty string.',
|
|
312
|
+
code: 'IPC_INVALID_CHANNEL_RANGE',
|
|
313
|
+
custom: false,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (!('id' in message)) {
|
|
318
|
+
return Result.failure({
|
|
319
|
+
error: ReferenceError,
|
|
320
|
+
message: 'IPC invocation requires an "id" property.',
|
|
321
|
+
code: 'IPC_MISSING_ID',
|
|
322
|
+
custom: false,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (typeof message.id !== 'string') {
|
|
327
|
+
return Result.failure({
|
|
328
|
+
error: TypeError,
|
|
329
|
+
message: 'IPC invocation id must be a string.',
|
|
330
|
+
code: 'IPC_INVALID_ID_TYPE',
|
|
331
|
+
custom: false,
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (message.id.trim().length === 0) {
|
|
336
|
+
return Result.failure({
|
|
337
|
+
error: RangeError,
|
|
338
|
+
message: 'IPC invocation id must be a non-empty string.',
|
|
339
|
+
code: 'IPC_INVALID_ID_RANGE',
|
|
340
|
+
custom: false,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return Result.success({
|
|
345
|
+
type: 'invoke',
|
|
346
|
+
channel: message.channel,
|
|
347
|
+
id: message.id,
|
|
348
|
+
data: message.data,
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Ensures a parsed IPC payload is a valid event request.
|
|
354
|
+
*
|
|
355
|
+
* @param {Record<string, unknown>} message - Parsed IPC payload.
|
|
356
|
+
* @returns {Result<IPCEventRequest>} Successful result containing the normalized request, or a failure describing the validation error.
|
|
357
|
+
*/
|
|
358
|
+
#ensureEventRequest(message) {
|
|
359
|
+
if (!('channel' in message)) {
|
|
360
|
+
return Result.failure({
|
|
361
|
+
error: ReferenceError,
|
|
362
|
+
message: 'IPC message requires a "channel" property.',
|
|
363
|
+
code: 'IPC_MISSING_CHANNEL',
|
|
364
|
+
custom: false,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (typeof message.channel !== 'string') {
|
|
369
|
+
return Result.failure({
|
|
370
|
+
error: TypeError,
|
|
371
|
+
message: 'IPC channel must be a string.',
|
|
372
|
+
code: 'IPC_INVALID_CHANNEL_TYPE',
|
|
373
|
+
custom: false,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (message.channel.trim().length === 0) {
|
|
378
|
+
return Result.failure({
|
|
379
|
+
error: RangeError,
|
|
380
|
+
message: 'IPC channel must be a non-empty string.',
|
|
381
|
+
code: 'IPC_INVALID_CHANNEL_RANGE',
|
|
382
|
+
custom: false,
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return Result.success({
|
|
387
|
+
type: 'event',
|
|
388
|
+
channel: message.channel,
|
|
389
|
+
data: message.data,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Normalizes an arbitrary thrown value into the IPC wire error format.
|
|
395
|
+
*
|
|
396
|
+
* Objects containing `code` and `message` preserve both values after
|
|
397
|
+
* converting them to strings. Other values use the default
|
|
398
|
+
* `IPC_HANDLER_ERROR` code.
|
|
399
|
+
*
|
|
400
|
+
* @param {unknown} error - Thrown value to normalize.
|
|
401
|
+
* @returns {IPCError} Normalized IPC wire error.
|
|
402
|
+
*/
|
|
403
|
+
#normalizeError(error) {
|
|
404
|
+
if (error && typeof error === 'object' && 'code' in error && 'message' in error) {
|
|
405
|
+
return {
|
|
406
|
+
code: String(Reflect.get(error, 'code')),
|
|
407
|
+
message: String(Reflect.get(error, 'message')),
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (error instanceof Error) {
|
|
412
|
+
return {
|
|
413
|
+
code: 'IPC_HANDLER_ERROR',
|
|
414
|
+
message: error.message,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return {
|
|
419
|
+
code: 'IPC_HANDLER_ERROR',
|
|
420
|
+
message: String(error),
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Parses JSON and throws a TypeError when the payload is not valid JSON.
|
|
426
|
+
*
|
|
427
|
+
* @param {string} data - Raw IPC payload.
|
|
428
|
+
* @returns {unknown} The parsed value.
|
|
429
|
+
* @throws {TypeError} If the payload is not valid JSON.
|
|
430
|
+
*/
|
|
431
|
+
#parse(data) {
|
|
432
|
+
try {
|
|
433
|
+
return JSON.parse(data);
|
|
434
|
+
} catch (error) {
|
|
435
|
+
throw new TypeError('IPC message must be valid JSON', { cause: error });
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
441
|
+
export default IPCProtocol;
|