@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,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;
|