@boo-labs/server 0.0.0-stage → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +287 -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,119 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* @file Defines the transport resolver.
|
|
4
|
+
*
|
|
5
|
+
* @description Resolves the transport adapter constructor associated with a
|
|
6
|
+
* supported transport identifier.
|
|
7
|
+
*
|
|
8
|
+
* @module TransportResolver
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
12
|
+
// » IMPORT LOCAL MODULES
|
|
13
|
+
import HttpAdapter from '#adapters/HttpAdapter';
|
|
14
|
+
import HttpsAdapter from '#adapters/HttpsAdapter';
|
|
15
|
+
import Http2Adapter from '#adapters/Http2Adapter';
|
|
16
|
+
import Http2CleartextAdapter from '#adapters/Http2CleartextAdapter';
|
|
17
|
+
|
|
18
|
+
// ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
19
|
+
/**
|
|
20
|
+
* Maps each transport identifier to its concrete adapter type.
|
|
21
|
+
*
|
|
22
|
+
* @typedef {object} TransportRegistry
|
|
23
|
+
* @property {HttpAdapter} h1 - HTTP/1.1 over cleartext TCP.
|
|
24
|
+
* @property {HttpsAdapter} h1s - HTTP/1.1 over TLS.
|
|
25
|
+
* @property {Http2Adapter} h2 - HTTP/2 over TLS.
|
|
26
|
+
* @property {Http2CleartextAdapter} h2c - HTTP/2 over cleartext TCP.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Identifies the transport implementation handled by an adapter.
|
|
31
|
+
*
|
|
32
|
+
* @typedef {keyof TransportRegistry} Transport
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Generic contract satisfied by every concrete adapter in the registry.
|
|
37
|
+
*
|
|
38
|
+
* @typedef {TransportRegistry[Transport]} TransportAdapterContract
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Represents a transport adapter capable of managing a permitted transport.
|
|
43
|
+
*
|
|
44
|
+
* @typedef {import('#configurations/ServerConfiguration').ServerConfigurationIdentity} ServerConfigurationIdentity
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Represents a transport adapter capable of managing a permitted transport.
|
|
49
|
+
*
|
|
50
|
+
* @typedef {import('#configurations/ServerConfiguration').default<ServerConfigurationIdentity>} ServerConfiguration
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Constructor for a transport adapter that accepts optional transport
|
|
55
|
+
* configuration.
|
|
56
|
+
*
|
|
57
|
+
* @template {Transport} T
|
|
58
|
+
* @typedef {new (options: object|undefined) => TransportRegistry[T]} TransportAdapterConstructor
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
// ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
62
|
+
/**
|
|
63
|
+
* Resolves transport adapter constructors from transport identifiers.
|
|
64
|
+
*
|
|
65
|
+
* @class
|
|
66
|
+
*/
|
|
67
|
+
class TransportResolver {
|
|
68
|
+
/**
|
|
69
|
+
* Prevents direct instantiation of the resolver.
|
|
70
|
+
*
|
|
71
|
+
* @abstract
|
|
72
|
+
* @throws {Error} Always thrown because `TransportResolver` exposes only static behavior.
|
|
73
|
+
*/
|
|
74
|
+
constructor() {
|
|
75
|
+
throw new Error('TransportResolver is a static class and cannot be instantiated');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Resolves the adapter constructor associated with a transport.
|
|
80
|
+
*
|
|
81
|
+
* @param {Transport} transport - Identifier of the transport to resolve.
|
|
82
|
+
* @returns {TransportAdapterConstructor<Transport>} The transport adapter constructor.
|
|
83
|
+
* @throws {Error} When the transport is not supported by this implementation.
|
|
84
|
+
* @example
|
|
85
|
+
* ```js
|
|
86
|
+
* const Adapter = TransportResolver.resolve('h1');
|
|
87
|
+
* const adapter = new Adapter(configuration);
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
static resolve(transport) {
|
|
91
|
+
const Adapter = TransportResolver.#adapters.get(transport);
|
|
92
|
+
|
|
93
|
+
if (!Adapter) {
|
|
94
|
+
throw new Error(`Unsupported transport: ${transport}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return Adapter;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @type {Array<[Transport, TransportAdapterConstructor<Transport>]>}
|
|
102
|
+
*/
|
|
103
|
+
static #ADAPTERS = [
|
|
104
|
+
['h1', HttpAdapter],
|
|
105
|
+
['h1s', HttpsAdapter],
|
|
106
|
+
['h2', Http2Adapter],
|
|
107
|
+
['h2c', Http2CleartextAdapter],
|
|
108
|
+
];
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Transport adapter constructors indexed by transport identifier.
|
|
112
|
+
*
|
|
113
|
+
* @type {Map<Transport, TransportAdapterConstructor<Transport>>}
|
|
114
|
+
*/
|
|
115
|
+
static #adapters = new Map(TransportResolver.#ADAPTERS);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
119
|
+
export default TransportResolver;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* @file Defines the CloseServer application use case.
|
|
4
|
+
*
|
|
5
|
+
* @description CloseServer is responsible for executing the server shutdown
|
|
6
|
+
* operation through an injected server.
|
|
7
|
+
*
|
|
8
|
+
* It delegates the underlying shutdown process to the server and remains
|
|
9
|
+
* independent from any concrete transport implementation.
|
|
10
|
+
*
|
|
11
|
+
* @module CloseServer
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
15
|
+
/**
|
|
16
|
+
* Represents a permitted transport identity.
|
|
17
|
+
*
|
|
18
|
+
* @typedef {import('#adapters/TransportAdapter').TransportIdentity} TransportIdentity
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Represents a transport adapter capable of managing a permitted transport.
|
|
23
|
+
*
|
|
24
|
+
* @typedef {import('#adapters/TransportAdapter').default<TransportIdentity>} TransportAdapter
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Callback invoked after the server has closed.
|
|
29
|
+
*
|
|
30
|
+
* @callback OnCloseHandler
|
|
31
|
+
* @param {Error} [error] - Error reported if the server could not be closed.
|
|
32
|
+
* @returns {void}
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
// ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
36
|
+
/**
|
|
37
|
+
* Application use case for stopping the server.
|
|
38
|
+
*
|
|
39
|
+
* @classdesc Executes the server shutdown operation through an injected server.
|
|
40
|
+
*
|
|
41
|
+
* This class does not manage server state transitions or depend on any
|
|
42
|
+
* concrete transport implementation.
|
|
43
|
+
*/
|
|
44
|
+
class CloseServer {
|
|
45
|
+
/**
|
|
46
|
+
* The server instance whose shutdown operation this use case executes.
|
|
47
|
+
*
|
|
48
|
+
* @type {TransportAdapter}
|
|
49
|
+
*/
|
|
50
|
+
#server;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Creates a new CloseServer use case.
|
|
54
|
+
*
|
|
55
|
+
* @param {TransportAdapter} server - The server used to execute the shutdown operation.
|
|
56
|
+
* @example
|
|
57
|
+
* ```js
|
|
58
|
+
* const closeServer = new CloseServer(new HttpAdapter({
|
|
59
|
+
* keepAliveTimeout: 65000
|
|
60
|
+
* }));
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
constructor(server) {
|
|
64
|
+
this.#server = server;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Executes the server shutdown operation.
|
|
69
|
+
*
|
|
70
|
+
* Delegates the shutdown operation to the configured server.
|
|
71
|
+
*
|
|
72
|
+
* @param {OnCloseHandler} [onCloseHandler] - Callback invoked by Node.js once the server has been successfully closed.
|
|
73
|
+
* @returns {Promise<void>} Resolves once the shutdown operation has completed.
|
|
74
|
+
* @example
|
|
75
|
+
* ```js
|
|
76
|
+
* await closeServer.execute(onCloseHandler);
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
async execute(onCloseHandler) {
|
|
80
|
+
await this.#server.close(onCloseHandler);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
85
|
+
export default CloseServer;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* @file Defines the ListenServer application use case.
|
|
4
|
+
*
|
|
5
|
+
* @description ListenServer is responsible for executing the server listening
|
|
6
|
+
* operation through an injected server.
|
|
7
|
+
*
|
|
8
|
+
* It delegates the underlying network binding process to the server and
|
|
9
|
+
* remains independent from any concrete transport implementation.
|
|
10
|
+
*
|
|
11
|
+
* @module ListenServer
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
15
|
+
/**
|
|
16
|
+
* Represents a permitted transport identity.
|
|
17
|
+
*
|
|
18
|
+
* @typedef {import('#adapters/TransportAdapter').TransportIdentity} TransportIdentity
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Represents a transport adapter capable of managing a permitted transport.
|
|
23
|
+
*
|
|
24
|
+
* @typedef {import('#adapters/TransportAdapter').default<TransportIdentity>} TransportAdapter
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Network configuration used to start the server.
|
|
29
|
+
*
|
|
30
|
+
* @typedef {object} ListenOptions
|
|
31
|
+
* @property {number} [port] - The TCP port on which the server listens.
|
|
32
|
+
* @property {string} [host] - The hostname or IP address on which the server listens.
|
|
33
|
+
* @property {number} [backlog] - The maximum number of pending connections that may be queued while the operating system is waiting to accept them.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Callback invoked when the server has successfully started listening.
|
|
38
|
+
*
|
|
39
|
+
* @callback OnListenHandler
|
|
40
|
+
* @returns {void}
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
// ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
44
|
+
/**
|
|
45
|
+
* Application use case for starting the server.
|
|
46
|
+
*
|
|
47
|
+
* @classdesc Executes the server listening operation through an injected server.
|
|
48
|
+
*
|
|
49
|
+
* This class does not manage server state transitions or depend on any
|
|
50
|
+
* concrete transport implementation.
|
|
51
|
+
*/
|
|
52
|
+
class ListenServer {
|
|
53
|
+
/**
|
|
54
|
+
* The server instance whose listening operation.
|
|
55
|
+
*
|
|
56
|
+
* @type {TransportAdapter}
|
|
57
|
+
*/
|
|
58
|
+
#server;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Creates a new ListenServer use case.
|
|
62
|
+
*
|
|
63
|
+
* @param {TransportAdapter} server - The server responsible for executing the underlying listening operation.
|
|
64
|
+
* @example
|
|
65
|
+
* ```js
|
|
66
|
+
* const listenServer = new ListenServer(new HttpAdapter({
|
|
67
|
+
* keepAliveTimeout: 65000
|
|
68
|
+
* }));
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
constructor(server) {
|
|
72
|
+
this.#server = server;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Executes the server listening operation.
|
|
77
|
+
*
|
|
78
|
+
* Delegates the listening operation to the configured server.
|
|
79
|
+
*
|
|
80
|
+
* @param {ListenOptions} [options] - Network configuration used to start the server.
|
|
81
|
+
* @param {OnListenHandler} [onListenHandler] - Callback invoked once the server has successfully started listening.
|
|
82
|
+
* @returns {Promise<void>} Resolves once the listening operation has been initiated.
|
|
83
|
+
* @example
|
|
84
|
+
* ```js
|
|
85
|
+
* await listenServer.execute({
|
|
86
|
+
* port: process.env.PORT || 8080,
|
|
87
|
+
* host: '0.0.0.0',
|
|
88
|
+
* backlog: 511,
|
|
89
|
+
* }, onListenHandler);
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
async execute(options, onListenHandler) {
|
|
93
|
+
await this.#server.listen(options, onListenHandler);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
98
|
+
export default ListenServer;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* @file Defines the server event system.
|
|
4
|
+
*
|
|
5
|
+
* @description Provides the event bus used by the application runtime to
|
|
6
|
+
* register listeners and emit server lifecycle and exchange events.
|
|
7
|
+
*
|
|
8
|
+
* @module EventSystem
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
12
|
+
// » IMPORT NATIVE NODEJS MODULES
|
|
13
|
+
import EventEmitter from 'node:events';
|
|
14
|
+
|
|
15
|
+
// ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
16
|
+
/**
|
|
17
|
+
* Identifies an event by its string or symbol name.
|
|
18
|
+
*
|
|
19
|
+
* @typedef {string|symbol} EventName
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Represents a listener registered for a kernel event.
|
|
24
|
+
*
|
|
25
|
+
* @typedef {(data?: any) => void} EventListener
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
29
|
+
/**
|
|
30
|
+
* Provides event management for the server runtime.
|
|
31
|
+
*
|
|
32
|
+
* @classdesc EventSystem encapsulates the underlying event emitter and exposes
|
|
33
|
+
* the event operations required by the application layer.
|
|
34
|
+
*/
|
|
35
|
+
class EventSystem {
|
|
36
|
+
#events = new EventEmitter();
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Registers a listener for a server event.
|
|
40
|
+
*
|
|
41
|
+
* @param {EventName} event - Event name.
|
|
42
|
+
* @param {EventListener} listener - Function invoked when the event is emitted.
|
|
43
|
+
* @returns {EventSystem} The current event system instance.
|
|
44
|
+
* @example
|
|
45
|
+
* ```js
|
|
46
|
+
* eventSystem.on('listen', ({ endpoint }) => {
|
|
47
|
+
* console.log(`Server listening at ${endpoint}`);
|
|
48
|
+
* });
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
on(event, listener) {
|
|
52
|
+
this.#events.on(event, listener);
|
|
53
|
+
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Registers a listener that is invoked only once for a server event.
|
|
59
|
+
*
|
|
60
|
+
* @param {EventName} event - Event name.
|
|
61
|
+
* @param {EventListener} listener - Function invoked when the event is emitted.
|
|
62
|
+
* @returns {EventSystem} The current event system instance.
|
|
63
|
+
* @example
|
|
64
|
+
* ```js
|
|
65
|
+
* eventSystem.once('close', () => {
|
|
66
|
+
* console.log('Server closed');
|
|
67
|
+
* });
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
once(event, listener) {
|
|
71
|
+
this.#events.once(event, listener);
|
|
72
|
+
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Removes a listener from a server event.
|
|
78
|
+
*
|
|
79
|
+
* @param {EventName} event - Event name.
|
|
80
|
+
* @param {EventListener} listener - Listener to remove.
|
|
81
|
+
* @returns {EventSystem} The current event system instance.
|
|
82
|
+
* @example
|
|
83
|
+
* ```js
|
|
84
|
+
* eventSystem.off('error', handleError);
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
off(event, listener) {
|
|
88
|
+
this.#events.off(event, listener);
|
|
89
|
+
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Emits a server event.
|
|
95
|
+
*
|
|
96
|
+
* @param {EventName} event - Event name.
|
|
97
|
+
* @param {unknown} [eventData] - Optional data associated with the event.
|
|
98
|
+
* @returns {boolean} True if the event had listeners, false otherwise.
|
|
99
|
+
* @example
|
|
100
|
+
* ```js
|
|
101
|
+
* eventSystem.emit('exchange', exchange);
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
emit(event, eventData) {
|
|
105
|
+
return this.#events.emit(event, eventData);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
110
|
+
export default EventSystem;
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* @file Defines the internal execution context of an HTTP exchange.
|
|
4
|
+
*
|
|
5
|
+
* @description The context groups runtime-only data associated with a
|
|
6
|
+
* single transaction: telemetry, cancellation signal, and correlation
|
|
7
|
+
* identifiers. It is private to `Exchange` and is never exposed to the
|
|
8
|
+
* consumer.
|
|
9
|
+
*
|
|
10
|
+
* The context does not access the native HTTP message. Any HTTP-derived
|
|
11
|
+
* information it needs (currently only the W3C `traceparent` header) is
|
|
12
|
+
* extracted by `Exchange` and passed in at construction time.
|
|
13
|
+
*
|
|
14
|
+
* The outgoing `traceparent` represents the server's own span: the incoming
|
|
15
|
+
* `traceId` is preserved when present, a new `spanId` is generated, and the
|
|
16
|
+
* incoming `trace-flags` are preserved to avoid altering the sampling
|
|
17
|
+
* decision taken upstream.
|
|
18
|
+
*
|
|
19
|
+
* @module ExchangeContext
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
// ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
23
|
+
// » IMPORT NATIVE NODEJS MODULES
|
|
24
|
+
import { randomBytes } from 'node:crypto';
|
|
25
|
+
|
|
26
|
+
// » IMPORT LOCAL MODULES
|
|
27
|
+
import Telemetry from './Telemetry.mjs';
|
|
28
|
+
import UUIDV7 from '#shared/domain/UUIDV7';
|
|
29
|
+
|
|
30
|
+
// ━━ CONSTANTS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
31
|
+
/**
|
|
32
|
+
* W3C Trace Context version supported by this implementation.
|
|
33
|
+
*
|
|
34
|
+
* @type {string}
|
|
35
|
+
*/
|
|
36
|
+
const TRACEPARENT_VERSION = '00';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Default trace flags used when no incoming `traceparent` is available.
|
|
40
|
+
*
|
|
41
|
+
* @type {string}
|
|
42
|
+
*/
|
|
43
|
+
const DEFAULT_TRACE_FLAGS = '01';
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Pattern matching a W3C `traceparent` header value.
|
|
47
|
+
*
|
|
48
|
+
* @type {RegExp}
|
|
49
|
+
*/
|
|
50
|
+
const TRACEPARENT_PATTERN = /^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/i;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Represents an all-zero trace identifier, which is invalid per W3C.
|
|
54
|
+
*
|
|
55
|
+
* @type {string}
|
|
56
|
+
*/
|
|
57
|
+
const INVALID_TRACE_ID = '0'.repeat(32);
|
|
58
|
+
|
|
59
|
+
// ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
60
|
+
/**
|
|
61
|
+
* Options used to construct an exchange context.
|
|
62
|
+
*
|
|
63
|
+
* @typedef {object} ExchangeContextOptions
|
|
64
|
+
* @property {string} [traceparent] - W3C `traceparent` header value received with the request.
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Represents the trace information extracted from a W3C `traceparent`.
|
|
69
|
+
*
|
|
70
|
+
* @typedef {object} TraceContext
|
|
71
|
+
* @property {string} traceId - The 32-character hexadecimal trace identifier.
|
|
72
|
+
* @property {string} traceFlags - The 2-character hexadecimal trace flags.
|
|
73
|
+
*/
|
|
74
|
+
|
|
75
|
+
// ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
76
|
+
/**
|
|
77
|
+
* Groups runtime-only data associated with a single HTTP exchange.
|
|
78
|
+
*
|
|
79
|
+
* @classdesc The context holds telemetry, cancellation signal, and
|
|
80
|
+
* correlation identifiers. It is created by `Exchange` and never exposed
|
|
81
|
+
* to the consumer.
|
|
82
|
+
*/
|
|
83
|
+
class ExchangeContext {
|
|
84
|
+
/**
|
|
85
|
+
* Cancellation controller for the transaction.
|
|
86
|
+
*
|
|
87
|
+
* @type {AbortController}
|
|
88
|
+
*/
|
|
89
|
+
#controller;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Telemetry tracker for the transaction.
|
|
93
|
+
*
|
|
94
|
+
* @type {Telemetry}
|
|
95
|
+
*/
|
|
96
|
+
#telemetry;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Unique identifier for the transaction.
|
|
100
|
+
*
|
|
101
|
+
* @type {string}
|
|
102
|
+
*/
|
|
103
|
+
#id;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* W3C trace identifier.
|
|
107
|
+
*
|
|
108
|
+
* @type {string}
|
|
109
|
+
*/
|
|
110
|
+
#traceId;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* W3C span identifier.
|
|
114
|
+
*
|
|
115
|
+
* @type {string}
|
|
116
|
+
*/
|
|
117
|
+
#spanId;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* W3C trace flags.
|
|
121
|
+
*
|
|
122
|
+
* @type {string}
|
|
123
|
+
*/
|
|
124
|
+
#traceFlags;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Creates an exchange context.
|
|
128
|
+
*
|
|
129
|
+
* @param {ExchangeContextOptions} [options] - The context options.
|
|
130
|
+
* @example
|
|
131
|
+
* ```js
|
|
132
|
+
* const context = new ExchangeContext({ traceparent: '00-...-...-01' });
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
constructor(options = {}) {
|
|
136
|
+
this.#controller = new AbortController();
|
|
137
|
+
this.#telemetry = new Telemetry();
|
|
138
|
+
this.#id = UUIDV7.create();
|
|
139
|
+
|
|
140
|
+
const trace = ExchangeContext.#extractTrace(options.traceparent);
|
|
141
|
+
|
|
142
|
+
this.#traceId = trace?.traceId ?? ExchangeContext.#generateTraceId();
|
|
143
|
+
this.#traceFlags = trace?.traceFlags ?? DEFAULT_TRACE_FLAGS;
|
|
144
|
+
this.#spanId = ExchangeContext.#generateSpanId();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* The cancellation signal for the transaction.
|
|
149
|
+
*
|
|
150
|
+
* @type {AbortSignal}
|
|
151
|
+
*/
|
|
152
|
+
get signal() {
|
|
153
|
+
return this.#controller.signal;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* The telemetry tracker for the transaction.
|
|
158
|
+
*
|
|
159
|
+
* @type {Telemetry}
|
|
160
|
+
*/
|
|
161
|
+
get telemetry() {
|
|
162
|
+
return this.#telemetry;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The unique identifier for the transaction.
|
|
167
|
+
*
|
|
168
|
+
* @type {string}
|
|
169
|
+
*/
|
|
170
|
+
get id() {
|
|
171
|
+
return this.#id;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The W3C trace identifier for the transaction.
|
|
176
|
+
*
|
|
177
|
+
* @type {string}
|
|
178
|
+
*/
|
|
179
|
+
get traceId() {
|
|
180
|
+
return this.#traceId;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* The W3C span identifier for the transaction.
|
|
185
|
+
*
|
|
186
|
+
* @type {string}
|
|
187
|
+
*/
|
|
188
|
+
get spanId() {
|
|
189
|
+
return this.#spanId;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Monotonic timestamp captured when the exchange processing context was created.
|
|
194
|
+
*
|
|
195
|
+
* @type {number}
|
|
196
|
+
*/
|
|
197
|
+
get startedAt() {
|
|
198
|
+
return this.#telemetry.startedAt;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Aborts the transaction.
|
|
203
|
+
*
|
|
204
|
+
* Signals the associated cancellation signal. Callers of `signal` will
|
|
205
|
+
* receive the `abort` event. Subsequent calls have no effect.
|
|
206
|
+
*
|
|
207
|
+
* @returns {void}
|
|
208
|
+
* @example
|
|
209
|
+
* ```js
|
|
210
|
+
* context.abort();
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
abort() {
|
|
214
|
+
this.#controller.abort();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Returns the W3C `traceparent` header value for this transaction.
|
|
219
|
+
*
|
|
220
|
+
* The value represents the server's own span: it preserves the incoming
|
|
221
|
+
* `traceId` and `trace-flags` when present, and always carries a new
|
|
222
|
+
* `spanId`.
|
|
223
|
+
*
|
|
224
|
+
* @returns {string} The formatted `traceparent` header value.
|
|
225
|
+
* @example
|
|
226
|
+
* ```js
|
|
227
|
+
* response.setHeader('traceparent', context.traceparent());
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
230
|
+
traceparent() {
|
|
231
|
+
return `${TRACEPARENT_VERSION}-${this.#traceId}-${this.#spanId}-${this.#traceFlags}`;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Extracts the trace information from a W3C `traceparent` header value.
|
|
236
|
+
*
|
|
237
|
+
* Returns `null` when the value is missing, malformed, uses a version
|
|
238
|
+
* not supported by this implementation, or carries an all-zero trace
|
|
239
|
+
* identifier.
|
|
240
|
+
*
|
|
241
|
+
* @param {string|undefined} value - The `traceparent` header value.
|
|
242
|
+
* @returns {TraceContext|null} The extracted trace information, or `null`.
|
|
243
|
+
*/
|
|
244
|
+
static #extractTrace(value) {
|
|
245
|
+
if (typeof value !== 'string') {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const match = value.match(TRACEPARENT_PATTERN);
|
|
250
|
+
|
|
251
|
+
if (!match) {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (match[1].toLowerCase() !== TRACEPARENT_VERSION) {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const traceId = match[2].toLowerCase();
|
|
260
|
+
|
|
261
|
+
if (traceId === INVALID_TRACE_ID) {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
traceId,
|
|
267
|
+
traceFlags: match[4].toLowerCase(),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Generates a 32-character lowercase hexadecimal trace identifier.
|
|
273
|
+
*
|
|
274
|
+
* @returns {string} The generated trace identifier.
|
|
275
|
+
*/
|
|
276
|
+
static #generateTraceId() {
|
|
277
|
+
return randomBytes(16).toString('hex');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Generates a 16-character lowercase hexadecimal span identifier.
|
|
282
|
+
*
|
|
283
|
+
* @returns {string} The generated span identifier.
|
|
284
|
+
*/
|
|
285
|
+
static #generateSpanId() {
|
|
286
|
+
return randomBytes(8).toString('hex');
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
291
|
+
export default ExchangeContext;
|