@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.
Files changed (114) hide show
  1. package/README.md +229 -2
  2. package/index.mjs +23 -0
  3. package/package.json +71 -4
  4. package/source/application/Kernel/index.mjs +1137 -0
  5. package/source/application/Server/index.mjs +1201 -0
  6. package/source/application/configurations/Http2CleartextServerConfiguration/index.mjs +168 -0
  7. package/source/application/configurations/Http2SecureServerConfiguration/index.mjs +172 -0
  8. package/source/application/configurations/HttpServerConfiguration/index.mjs +169 -0
  9. package/source/application/configurations/HttpsServerConfiguration/index.mjs +172 -0
  10. package/source/application/configurations/ServerConfiguration/Options/CORSOptions/CORSOptionsValue.mjs +158 -0
  11. package/source/application/configurations/ServerConfiguration/Options/CORSOptions/index.mjs +87 -0
  12. package/source/application/configurations/ServerConfiguration/Options/CSRFOptions/CSRFOptionsValue.mjs +178 -0
  13. package/source/application/configurations/ServerConfiguration/Options/CSRFOptions/index.mjs +80 -0
  14. package/source/application/configurations/ServerConfiguration/Options/CompressOptions/CompressOptionsValue.mjs +126 -0
  15. package/source/application/configurations/ServerConfiguration/Options/CompressOptions/index.mjs +81 -0
  16. package/source/application/configurations/ServerConfiguration/Options/CookieParserOptions/CookieParserOptionsValue.mjs +107 -0
  17. package/source/application/configurations/ServerConfiguration/Options/CookieParserOptions/index.mjs +82 -0
  18. package/source/application/configurations/ServerConfiguration/Options/CookieSessionOptions/CookieSessionOptionsValue.mjs +182 -0
  19. package/source/application/configurations/ServerConfiguration/Options/CookieSessionOptions/index.mjs +86 -0
  20. package/source/application/configurations/ServerConfiguration/Options/ListenOptions/ListenOptionsValue.mjs +95 -0
  21. package/source/application/configurations/ServerConfiguration/Options/ListenOptions/index.mjs +47 -0
  22. package/source/application/configurations/ServerConfiguration/Options/MiddlewareOption.mjs +102 -0
  23. package/source/application/configurations/ServerConfiguration/Options/Options.mjs +105 -0
  24. package/source/application/configurations/ServerConfiguration/Options/RateLimitOptions/RateLimitOptionsValue.mjs +164 -0
  25. package/source/application/configurations/ServerConfiguration/Options/RateLimitOptions/index.mjs +84 -0
  26. package/source/application/configurations/ServerConfiguration/Options/RequestOptions/RequestOptionsValue.mjs +108 -0
  27. package/source/application/configurations/ServerConfiguration/Options/RequestOptions/index.mjs +47 -0
  28. package/source/application/configurations/ServerConfiguration/Options/ResponseOptions/ResponseOptionsValue.mjs +101 -0
  29. package/source/application/configurations/ServerConfiguration/Options/ResponseOptions/index.mjs +47 -0
  30. package/source/application/configurations/ServerConfiguration/Options/SecurityHeadersOptions/SecurityHeadersOptionsValue.mjs +272 -0
  31. package/source/application/configurations/ServerConfiguration/Options/SecurityHeadersOptions/index.mjs +85 -0
  32. package/source/application/configurations/ServerConfiguration/Options/ServerOption.mjs +50 -0
  33. package/source/application/configurations/ServerConfiguration/Options/StaticOptions/StaticOptionsValue.mjs +164 -0
  34. package/source/application/configurations/ServerConfiguration/Options/StaticOptions/index.mjs +89 -0
  35. package/source/application/configurations/ServerConfiguration/index.mjs +781 -0
  36. package/source/application/resolvers/TransportResolver/index.mjs +119 -0
  37. package/source/application/use-cases/CloseServer/index.mjs +85 -0
  38. package/source/application/use-cases/ListenServer/index.mjs +98 -0
  39. package/source/core/EventSystem/index.mjs +110 -0
  40. package/source/core/Exchange/ExchangeContext.mjs +291 -0
  41. package/source/core/Exchange/Telemetry.mjs +193 -0
  42. package/source/core/Exchange/index.mjs +604 -0
  43. package/source/core/ExchangeLayerBuilder/index.mjs +133 -0
  44. package/source/core/IPCMain/IPCMain.mjs +507 -0
  45. package/source/core/IPCMain/IPCProtocol.mjs +441 -0
  46. package/source/core/IPCMain/IPCServerChannel.mjs +331 -0
  47. package/source/core/IPCMain/WebSocketAdapter.mjs +189 -0
  48. package/source/core/Request/BodyParser/BodyParser.mjs +197 -0
  49. package/source/core/Request/BodyParser/JSONParser.mjs +70 -0
  50. package/source/core/Request/BodyParser/MultipartParser.mjs +612 -0
  51. package/source/core/Request/BodyParser/URLEncodedParser.mjs +71 -0
  52. package/source/core/Request/RequestHeaders.mjs +177 -0
  53. package/source/core/Request/index.mjs +805 -0
  54. package/source/core/Response/ResponseHeaders.mjs +402 -0
  55. package/source/core/Response/index.mjs +1370 -0
  56. package/source/core/middlewares/CORSMiddleware/index.mjs +289 -0
  57. package/source/core/middlewares/CSRFMiddleware/index.mjs +284 -0
  58. package/source/core/middlewares/CompressMiddleware/index.mjs +460 -0
  59. package/source/core/middlewares/CookieParserMiddleware/index.mjs +194 -0
  60. package/source/core/middlewares/CookieSessionMiddleware/index.mjs +460 -0
  61. package/source/core/middlewares/RateLimitMiddleware/index.mjs +245 -0
  62. package/source/core/middlewares/SecurityHeadersMiddleware/index.mjs +151 -0
  63. package/source/core/middlewares/StaticMiddleware/index.mjs +628 -0
  64. package/source/core/routing/ExchangeLayer/ExchangeMatcher.mjs +127 -0
  65. package/source/core/routing/ExchangeLayer/index.mjs +254 -0
  66. package/source/core/routing/Pipeline/index.mjs +157 -0
  67. package/source/core/routing/Router/Route.mjs +184 -0
  68. package/source/core/routing/Router/index.mjs +523 -0
  69. package/source/core/routing/RouterRegister/OptionsMiddleware.mjs +147 -0
  70. package/source/core/routing/RouterRegister/index.mjs +150 -0
  71. package/source/core/routing/RouterSystem/index.mjs +225 -0
  72. package/source/core/value-objects/Address/index.mjs +281 -0
  73. package/source/core/value-objects/Endpoint/index.mjs +293 -0
  74. package/source/index.mjs +62 -0
  75. package/source/infrastructure/adapters/Http2Adapter/index.mjs +144 -0
  76. package/source/infrastructure/adapters/Http2CleartextAdapter/index.mjs +147 -0
  77. package/source/infrastructure/adapters/HttpAdapter/index.mjs +124 -0
  78. package/source/infrastructure/adapters/HttpsAdapter/index.mjs +124 -0
  79. package/source/infrastructure/adapters/TransportAdapter/createWebSocketOpenRequest.mjs +843 -0
  80. package/source/infrastructure/adapters/TransportAdapter/index.mjs +555 -0
  81. package/source/shared/Identifier/index.mjs +671 -0
  82. package/source/shared/constants/symbols/brands/index.mjs +45 -0
  83. package/source/shared/domain/UUIDV7/index.mjs +346 -0
  84. package/source/shared/errors/BaseError/index.mjs +157 -0
  85. package/source/shared/errors/IPCError/index.mjs +97 -0
  86. package/source/shared/errors/MiddlewareError/index.mjs +106 -0
  87. package/source/shared/errors/PathnamePatternError/index.mjs +103 -0
  88. package/source/shared/errors/RequestError/index.mjs +116 -0
  89. package/source/shared/errors/ResponseError/index.mjs +140 -0
  90. package/source/shared/errors/RouterError/index.mjs +90 -0
  91. package/source/shared/errors/ServerConfigurationError/index.mjs +551 -0
  92. package/source/shared/errors/ServerError/index.mjs +127 -0
  93. package/source/shared/errors/WebSocketError/index.mjs +58 -0
  94. package/source/shared/outcome/ErrorSchema.mjs +361 -0
  95. package/source/shared/outcome/Result/index.mjs +239 -0
  96. package/source/shared/outcome/Validation/index.mjs +208 -0
  97. package/source/shared/validations/Middleware/index.mjs +160 -0
  98. package/source/shared/validations/PathnamePattern/index.mjs +256 -0
  99. package/source/shared/validations/Server/NotFoundHandler/index.mjs +94 -0
  100. package/source/shared/validations/Server/Router/index.mjs +68 -0
  101. package/source/shared/validations/Server/ServerConfiguration/index.mjs +68 -0
  102. package/source/shared/validations/Server/SystemErrorHandler/index.mjs +94 -0
  103. package/source/shared/validations/ServerConfiguration/CORSOptions/index.mjs +310 -0
  104. package/source/shared/validations/ServerConfiguration/CSRFOptions/index.mjs +299 -0
  105. package/source/shared/validations/ServerConfiguration/CompressOptions/index.mjs +204 -0
  106. package/source/shared/validations/ServerConfiguration/CookieParserOptions/index.mjs +102 -0
  107. package/source/shared/validations/ServerConfiguration/CookieSessionOptions/index.mjs +332 -0
  108. package/source/shared/validations/ServerConfiguration/ListenOptions/index.mjs +178 -0
  109. package/source/shared/validations/ServerConfiguration/RateLimitOptions/index.mjs +229 -0
  110. package/source/shared/validations/ServerConfiguration/RequestOptions/index.mjs +132 -0
  111. package/source/shared/validations/ServerConfiguration/ResponseOptions/index.mjs +122 -0
  112. package/source/shared/validations/ServerConfiguration/SecurityHeadersOptions/index.mjs +137 -0
  113. package/source/shared/validations/ServerConfiguration/StaticOptions/index.mjs +248 -0
  114. package/types/index.d.ts +719 -0
@@ -0,0 +1,555 @@
1
+ // @ts-check
2
+ /**
3
+ * @file Defines the transport adapter.
4
+ *
5
+ * @description Declares the abstract contract that transport adapters must
6
+ * implement to integrate a network server with the application.
7
+ *
8
+ * The adapter isolates the application from the underlying transport
9
+ * implementation. Concrete adapters are responsible for translating this
10
+ * contract into operations provided by a specific runtime or server implementation.
11
+ *
12
+ * @module TransportAdapter
13
+ */
14
+
15
+ // ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
16
+ // » IMPORT LOCAL MODULES
17
+ import Address from '#value-objects/Address';
18
+ import Endpoint from '#value-objects/Endpoint';
19
+ import createWebSocketOpenRequest from './createWebSocketOpenRequest.mjs';
20
+
21
+ // ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
22
+ /**
23
+ * Represents a Node.js HTTP server.
24
+ *
25
+ * @typedef {import('node:http').Server} HttpServer
26
+ */
27
+
28
+ /**
29
+ * Represents a Node.js HTTPS server.
30
+ *
31
+ * @typedef {import('node:https').Server} HttpsServer
32
+ */
33
+
34
+ /**
35
+ * Represents a Node.js HTTP/2 server.
36
+ *
37
+ * @typedef {import('node:http2').Http2Server} Http2Server
38
+ */
39
+
40
+ /**
41
+ * Represents a Node.js secure HTTP/2 server.
42
+ *
43
+ * @typedef {import('node:http2').Http2SecureServer} Http2SecureServer
44
+ */
45
+
46
+ /**
47
+ * Represents an incoming HTTP/1.1 request message from Node.js.
48
+ *
49
+ * @typedef {import('node:http').IncomingMessage} IncomingMessage
50
+ */
51
+
52
+ /**
53
+ * Represents a Node.js HTTP/1.1 response.
54
+ *
55
+ * @typedef {import('node:http').ServerResponse} ServerResponse
56
+ */
57
+
58
+ /**
59
+ * Represents an incoming HTTP/2 request.
60
+ *
61
+ * @typedef {import('node:http2').Http2ServerRequest} Http2ServerRequest
62
+ */
63
+
64
+ /**
65
+ * Represents an HTTP/2 server response.
66
+ *
67
+ * @typedef {import('node:http2').Http2ServerResponse} Http2ServerResponse
68
+ */
69
+
70
+ /**
71
+ * Represents a server-side HTTP/2 stream.
72
+ *
73
+ * @typedef {import('node:http2').ServerHttp2Stream} ServerHttp2Stream
74
+ */
75
+
76
+ /**
77
+ * Represents the headers received with an incoming HTTP/2 request.
78
+ *
79
+ * @typedef {import('node:http2').IncomingHttpHeaders} IncomingHttpHeaders
80
+ */
81
+
82
+ /**
83
+ * Callback that handles an incoming HTTP/1.1 request.
84
+ *
85
+ * @callback Http1Handler
86
+ * @param {IncomingMessage} request - The incoming HTTP/1.1 request message.
87
+ * @param {ServerResponse} response - The HTTP/1.1 server response.
88
+ * @returns {void}
89
+ */
90
+
91
+ /**
92
+ * Callback that handles an incoming HTTP/2 request.
93
+ *
94
+ * @callback Http2Handler
95
+ * @param {Http2ServerRequest} request - The incoming HTTP/2 request message.
96
+ * @param {Http2ServerResponse} response - The HTTP/2 server response.
97
+ * @returns {void}
98
+ */
99
+
100
+ /**
101
+ * Identity for a plain HTTP/1.1 server.
102
+ *
103
+ * Represents a permitted transport identity for a Node.js HTTP server
104
+ * operating over HTTP/1.1 cleartext.
105
+ *
106
+ * @typedef {object} TransportIdentityH1
107
+ * @property {HttpServer} server - The underlying Node.js HTTP server instance.
108
+ * @property {'http'} protocol - The application-level protocol (`http`).
109
+ * @property {'h1'} transport - The transport identifier for HTTP/1.1 cleartext (`h1`).
110
+ * @property {Http1Handler} requestHandler - Callback invoked for each incoming HTTP request.
111
+ */
112
+
113
+ /**
114
+ * Identity for a secure HTTPS/1.1 server.
115
+ *
116
+ * Represents a permitted transport identity for a Node.js HTTPS server
117
+ * operating over HTTP/1.1 with TLS.
118
+ *
119
+ * @typedef {object} TransportIdentityH1S
120
+ * @property {HttpsServer} server - The underlying Node.js HTTPS server instance.
121
+ * @property {'https'} protocol - The application-level protocol (`https`).
122
+ * @property {'h1s'} transport - The transport identifier for HTTP/1.1 over TLS (`h1s`).
123
+ * @property {Http1Handler} requestHandler - Callback invoked for each incoming HTTP request.
124
+ */
125
+
126
+ /**
127
+ * Identity for a cleartext HTTP/2 server (h2c).
128
+ *
129
+ * Represents a permitted transport identity for a Node.js HTTP/2 server
130
+ * operating over cleartext.
131
+ *
132
+ * @typedef {object} TransportIdentityH2C
133
+ * @property {Http2Server} server - The underlying Node.js HTTP/2 cleartext server instance.
134
+ * @property {'http'} protocol - The application-level protocol (`http`).
135
+ * @property {'h2c'} transport - The transport identifier for HTTP/2 cleartext (`h2c`).
136
+ * @property {Http2Handler | Http1Handler} requestHandler - Callback invoked for each incoming request.
137
+ */
138
+
139
+ /**
140
+ * Identity for a secure HTTP/2 server (h2).
141
+ *
142
+ * Represents a permitted transport identity for a Node.js HTTP/2 server
143
+ * operating over TLS.
144
+ *
145
+ * @typedef {object} TransportIdentityH2
146
+ * @property {Http2SecureServer} server - The underlying Node.js secure HTTP/2 server instance.
147
+ * @property {'https'} protocol - The application-level protocol (`https`).
148
+ * @property {'h2'} transport - The transport identifier for HTTP/2 over TLS (`h2`).
149
+ * @property {Http2Handler | Http1Handler} requestHandler - Callback invoked for each incoming request.
150
+ */
151
+
152
+ /**
153
+ * Represents a permitted transport identity.
154
+ *
155
+ * Each identity correlates a Node.js server type with its protocol,
156
+ * transport, and permitted request handler contract.
157
+ *
158
+ * @typedef {TransportIdentityH1 | TransportIdentityH1S | TransportIdentityH2C | TransportIdentityH2} TransportIdentity
159
+ */
160
+
161
+ /**
162
+ * Listener for incoming WebSocket messages.
163
+ *
164
+ * @callback WebSocketMessageListener
165
+ * @param {string|Buffer} data - Incoming text or binary message.
166
+ * @returns {void}
167
+ */
168
+
169
+ /**
170
+ * Listener for WebSocket close events.
171
+ *
172
+ * @callback WebSocketCloseListener
173
+ * @returns {void}
174
+ */
175
+
176
+ /**
177
+ * Listener for WebSocket error events.
178
+ *
179
+ * @callback WebSocketErrorListener
180
+ * @param {Error} error - Error reported by the WebSocket connection.
181
+ * @returns {void}
182
+ */
183
+
184
+ /**
185
+ * Registers a WebSocket event listener.
186
+ *
187
+ * @typedef {(
188
+ * ((event: 'message', listener: WebSocketMessageListener) => void) &
189
+ * ((event: 'close', listener: WebSocketCloseListener) => void) &
190
+ * ((event: 'error', listener: WebSocketErrorListener) => void)
191
+ * )} WebSocketOn
192
+ */
193
+
194
+ /**
195
+ * Represents an established WebSocket connection contract.
196
+ *
197
+ * @typedef {object} WebSocketConnection
198
+ * @property {(data: string|Buffer) => void} send - Sends a text or binary WebSocket message.
199
+ * @property {WebSocketOn} on - Registers a listener for WebSocket events.
200
+ * @property {() => void} close - Closes the WebSocket connection.
201
+ * @property {number} readyState - Current transport-defined WebSocket ready state. Read-only.
202
+ */
203
+
204
+ /**
205
+ * Transport-level request used to accept or reject a WebSocket handshake.
206
+ *
207
+ * @typedef {object} WebSocketOpenRequest
208
+ * @property {string} url - The requested WebSocket URL.
209
+ * @property {Record<string, string|string[]|undefined>} headers - Headers received during the WebSocket handshake.
210
+ * @property {() => WebSocketConnection|Promise<WebSocketConnection>} accept - Accepts the handshake and resolves to the established connection.
211
+ * @property {(code?: number, message?: string) => void} reject - Rejects the WebSocket handshake.
212
+ */
213
+
214
+ /**
215
+ * Callback invoked once the server has been closed.
216
+ *
217
+ * Receives the error reported by the underlying Node.js server when the
218
+ * shutdown operation fails, or `undefined` when it succeeds.
219
+ *
220
+ * @callback OnCloseHandler
221
+ * @param {Error} [error] - Error reported if the server could not be closed.
222
+ * @returns {void}
223
+ */
224
+
225
+ /**
226
+ * Callback invoked once the server has started listening.
227
+ *
228
+ * Receives no arguments. It signals that the server has successfully bound
229
+ * to its configured network address and is ready to accept connections.
230
+ *
231
+ * @callback OnListenHandler
232
+ * @returns {void}
233
+ */
234
+
235
+ /**
236
+ * Callback invoked when a WebSocket handshake request is received.
237
+ *
238
+ * @callback OnWebSocketHandler
239
+ * @param {WebSocketOpenRequest} openRequest - WebSocket handshake request.
240
+ * @returns {void}
241
+ */
242
+
243
+ /**
244
+ * Network configuration used to start a server.
245
+ *
246
+ * @typedef {object} ListenOptions
247
+ * @property {number} [port] - The TCP port on which the server listens.
248
+ * @property {string} [host] - The hostname or IP address on which the server listens.
249
+ * @property {number} [backlog] - The maximum number of pending connections that may be queued while the operating system is waiting to accept them.
250
+ */
251
+
252
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
253
+ /**
254
+ * Abstract port for a server transport adapter.
255
+ *
256
+ * @template {TransportIdentity} TIdentity
257
+ *
258
+ * @classdesc The `TransportAdapter` defines the transport-level operations
259
+ * required by the application to control and inspect a server without depending
260
+ * on a concrete transport implementation.
261
+ *
262
+ * Concrete implementations must extend this class and provide the actual
263
+ * transport operations.
264
+ *
265
+ * The class cannot be instantiated directly.
266
+ */
267
+ class TransportAdapter {
268
+ /**
269
+ * Underlying Node.js server instance.
270
+ *
271
+ * @type {TIdentity['server']}
272
+ */
273
+ #server;
274
+
275
+ /**
276
+ * Protocol identifier used by this adapter.
277
+ *
278
+ * @type {TIdentity['protocol']}
279
+ */
280
+ #protocol;
281
+
282
+ /**
283
+ * Transport identifier used by this adapter.
284
+ *
285
+ * @type {TIdentity['transport']}
286
+ */
287
+ #transport;
288
+
289
+ /**
290
+ * Creates a transport adapter instance.
291
+ *
292
+ * Concrete adapters must invoke this constructor through `super()` before
293
+ * initializing their own transport-specific state.
294
+ *
295
+ * @abstract
296
+ * @param {TIdentity['server']} server - The native Node.js server managed by the adapter.
297
+ * @param {TIdentity['protocol']} protocol - The protocol exposed by the adapter.
298
+ * @param {TIdentity['transport']} transport - The transport implementation represented by the adapter.
299
+ * @throws {Error} Thrown when `TransportAdapter` is instantiated directly.
300
+ */
301
+ constructor(server, protocol, transport) {
302
+ if (new.target === TransportAdapter) {
303
+ throw new Error('TransportAdapter is abstract and cannot be instantiated directly');
304
+ }
305
+ this.#server = server;
306
+ this.#protocol = protocol;
307
+ this.#transport = transport;
308
+ }
309
+
310
+ /**
311
+ * The protocol identifier represented by {@link TIdentity['protocol']}.
312
+ *
313
+ * @type {TIdentity['protocol']}
314
+ */
315
+ get protocol() {
316
+ return this.#protocol;
317
+ }
318
+
319
+ /**
320
+ * The transport identifier represented by {@link TIdentity['transport']}.
321
+ *
322
+ * @type {TIdentity['transport']}
323
+ */
324
+ get transport() {
325
+ return this.#transport;
326
+ }
327
+
328
+ /**
329
+ * Indicates whether the underlying server is currently listening.
330
+ *
331
+ * The value is `true` when the server is accepting incoming connections;
332
+ * otherwise, `false`.
333
+ *
334
+ * @type {boolean}
335
+ */
336
+ get listening() {
337
+ return this.#server.listening;
338
+ }
339
+
340
+ /**
341
+ * Returns the endpoint on which the server is currently listening.
342
+ *
343
+ * The endpoint combines the adapter's protocol with the network address
344
+ * and port reported by the underlying Node.js server.
345
+ *
346
+ * If the server is not currently listening, `null` is returned.
347
+ *
348
+ * @returns {Endpoint|null} The server endpoint, or `null` when the server is not listening.
349
+ * @example
350
+ * ```js
351
+ * adapter.endpoint();
352
+ * ```
353
+ */
354
+ endpoint() {
355
+ const address = this.address();
356
+
357
+ if (address === null) {
358
+ return null;
359
+ }
360
+
361
+ return new Endpoint({
362
+ protocol: this.#protocol,
363
+ address,
364
+ });
365
+ }
366
+
367
+ /**
368
+ * Returns the network address currently used by the server.
369
+ *
370
+ * The native Node.js address representation is converted into an
371
+ * `Address` value object so that transport-specific address details
372
+ * do not leak outside the adapter.
373
+ *
374
+ * If the server is not currently listening, `null` is returned.
375
+ *
376
+ * @returns {Address|null} The server network address, or `null` when the server is not listening.
377
+ * @example
378
+ * ```js
379
+ * adapter.address();
380
+ * ```
381
+ */
382
+ address() {
383
+ if (!this.#server.listening) {
384
+ return null;
385
+ }
386
+ const address = this.#server.address();
387
+
388
+ if (address === null || typeof address === 'string') {
389
+ return null;
390
+ }
391
+ return new Address(address);
392
+ }
393
+
394
+ /**
395
+ * Registers a handler for incoming requests.
396
+ *
397
+ * The handler is invoked for each incoming request received by the
398
+ * underlying Node.js server.
399
+ *
400
+ * @param {TIdentity['requestHandler']} onRequestHandler - Callback invoked for each incoming request.
401
+ * @example
402
+ * ```js
403
+ * adapter.onRequest(onRequestHandler);
404
+ * ```
405
+ */
406
+ onRequest(onRequestHandler) {
407
+ this.#server.on('request', onRequestHandler);
408
+ }
409
+
410
+ /**
411
+ * Starts the server and begins listening for incoming connections.
412
+ *
413
+ * The supplied network configuration is translated into the corresponding
414
+ * arguments expected by Node.js' `server.listen()` method.
415
+ *
416
+ * @param {ListenOptions} [options] - Network configuration used to start the server.
417
+ * @param {OnListenHandler} [onListenHandler] - Callback invoked once the server has successfully started listening.
418
+ * @returns {Promise<void>} Resolves once the server has successfully started listening.
419
+ * @throws {Error} Rejects when the underlying server fails to start listening.
420
+ * @example
421
+ * ```js
422
+ * adapter.listen({
423
+ * port: process.env.PORT || 8080,
424
+ * host: '0.0.0.0',
425
+ * backlog: 511,
426
+ * }, () => console.log('Server listening'));
427
+ * ```
428
+ */
429
+ listen(options, onListenHandler) {
430
+ return new Promise((resolve, reject) => {
431
+ /**
432
+ * Handles startup errors emitted by the underlying server.
433
+ * Ensures proper cleanup by removing the `listening` event listener
434
+ * before rejecting the Promise.
435
+ *
436
+ * @param {Error} error - The error object emitted by the server during startup.
437
+ */
438
+ const onError = error => {
439
+ this.#server.off('listening', onListening);
440
+ reject(error);
441
+ };
442
+
443
+ /**
444
+ * Handles the successful startup of the server.
445
+ * Ensures proper cleanup by removing the `error` event listener
446
+ * before resolving the Promise.
447
+ */
448
+ const onListening = () => {
449
+ this.#server.off('error', onError);
450
+
451
+ if (onListenHandler) {
452
+ onListenHandler();
453
+ }
454
+
455
+ resolve();
456
+ };
457
+
458
+ this.#server.once('error', onError);
459
+ this.#server.once('listening', onListening);
460
+
461
+ try {
462
+ this.#server.listen(options?.port, options?.host, options?.backlog);
463
+ } catch (error) {
464
+ this.#server.off('error', onError);
465
+ this.#server.off('listening', onListening);
466
+ reject(error);
467
+ }
468
+ });
469
+ }
470
+
471
+ /**
472
+ * Stops the server and releases its listening resources.
473
+ *
474
+ * The operation is delegated directly to the underlying Node.js server.
475
+ *
476
+ * @param {OnCloseHandler} [onCloseHandler] - Callback invoked once the server has been closed.
477
+ * @returns {Promise<void>} Resolves once the server has been successfully closed.
478
+ * @throws {Error} Rejects when the server cannot be closed successfully.
479
+ * @example
480
+ * ```js
481
+ * adapter.close();
482
+ * ```
483
+ */
484
+ close(onCloseHandler) {
485
+ return new Promise((resolve, reject) => {
486
+ this.#server.close(error => {
487
+ if (onCloseHandler) {
488
+ onCloseHandler(error);
489
+ }
490
+
491
+ if (error) {
492
+ return reject(error);
493
+ }
494
+
495
+ resolve();
496
+ });
497
+ });
498
+ }
499
+
500
+ /**
501
+ * Registers a handler for WebSocket upgrade requests.
502
+ *
503
+ * Concrete adapters inherit the transport-specific WebSocket handshake
504
+ * handling provided by this method.
505
+ *
506
+ * @param {OnWebSocketHandler} onWebSocketHandler - Callback invoked when a WebSocket handshake request is received.
507
+ * @throws {Error} Thrown when the transport does not support WebSockets.
508
+ * @example
509
+ * ```js
510
+ * adapter.onWebSocket(openRequest => {
511
+ * openRequest.accept();
512
+ * });
513
+ * ```
514
+ */
515
+ onWebSocket(onWebSocketHandler) {
516
+ this.#resolveOnWebSocket(onWebSocketHandler);
517
+ }
518
+
519
+ /**
520
+ * Resolves the WebSocket handshake mechanism supported by the transport.
521
+ *
522
+ * @param {OnWebSocketHandler} onWebSocketHandler - Callback invoked when a WebSocket handshake request is received.
523
+ * @throws {Error} Thrown when the transport does not support WebSockets.
524
+ */
525
+ #resolveOnWebSocket(onWebSocketHandler) {
526
+ if (this.#transport === 'h1' || this.#transport === 'h1s') {
527
+ this.#server.on('upgrade', (request, socket, head) => {
528
+ const openRequest = createWebSocketOpenRequest(request, socket, head);
529
+ onWebSocketHandler(openRequest);
530
+ });
531
+ } else if (this.#transport === 'h2' || this.#transport === 'h2c') {
532
+ /**
533
+ * Handles an incoming HTTP/2 stream.
534
+ *
535
+ * @private
536
+ * @param {ServerHttp2Stream} stream - HTTP/2 stream associated with the request.
537
+ * @param {IncomingHttpHeaders} headers - HTTP/2 request headers.
538
+ */
539
+ const onStream = (stream, headers) => {
540
+ if (headers[':method'] === 'CONNECT') {
541
+ stream.respond({ ':status': 501 });
542
+ stream.end();
543
+ }
544
+ };
545
+
546
+ this.#server.on('session', session => {
547
+ session.on('stream', onStream);
548
+ });
549
+ } else {
550
+ throw new Error(`WebSocket is not supported by transport '${this.#transport}'`);
551
+ }
552
+ }
553
+ }
554
+ // ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
555
+ export default TransportAdapter;