@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,147 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* @file Defines the middleware for handling automatic OPTIONS requests.
|
|
4
|
+
*
|
|
5
|
+
* @description Provides automatic OPTIONS responses for routes by exposing the
|
|
6
|
+
* HTTP methods registered for each pathname pattern.
|
|
7
|
+
*
|
|
8
|
+
* @module OptionsMiddleware
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
12
|
+
/**
|
|
13
|
+
* Represents a single HTTP transaction.
|
|
14
|
+
*
|
|
15
|
+
* @typedef {import('#Exchange').default} Exchange
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Represents an HTTP method supported by the routing layer.
|
|
20
|
+
*
|
|
21
|
+
* @typedef {import('#routing/ExchangeLayer').HTTPMethod} HTTPMethod
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
// ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
25
|
+
/**
|
|
26
|
+
* Handles automatic OPTIONS responses for a route.
|
|
27
|
+
*
|
|
28
|
+
* @classdesc `OptionsMiddleware` generates an OPTIONS response containing the
|
|
29
|
+
* HTTP methods registered for a pathname pattern.
|
|
30
|
+
*/
|
|
31
|
+
class OptionsMiddleware {
|
|
32
|
+
/**
|
|
33
|
+
* The HTTP methods registered for the route.
|
|
34
|
+
*
|
|
35
|
+
* `undefined` represents all methods during registration and is removed when
|
|
36
|
+
* the methods are resolved.
|
|
37
|
+
*
|
|
38
|
+
* @type {Set<HTTPMethod|undefined>}
|
|
39
|
+
*/
|
|
40
|
+
#methods = new Set();
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The cached resolved HTTP methods string.
|
|
44
|
+
*
|
|
45
|
+
* @type {string|null}
|
|
46
|
+
*/
|
|
47
|
+
#methodsCache = null;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Creates an instance of OptionsMiddleware.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```js
|
|
54
|
+
* const middleware = new OptionsMiddleware();
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
constructor() {
|
|
58
|
+
this.handle = this.handle.bind(this);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The resolved HTTP methods string.
|
|
63
|
+
*
|
|
64
|
+
* @type {string}
|
|
65
|
+
*/
|
|
66
|
+
get methods() {
|
|
67
|
+
if (this.#methodsCache !== null) {
|
|
68
|
+
return this.#methodsCache;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const methods = new Set(this.#methods);
|
|
72
|
+
methods.delete(undefined);
|
|
73
|
+
|
|
74
|
+
if (methods.size === 0) {
|
|
75
|
+
this.#methodsCache = OptionsMiddleware.#METHODS.join(', ');
|
|
76
|
+
} else {
|
|
77
|
+
methods.add('OPTIONS');
|
|
78
|
+
this.#methodsCache = [...methods].join(', ');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return this.#methodsCache;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Adds an HTTP method to the allowed methods.
|
|
86
|
+
*
|
|
87
|
+
* `undefined` represents all methods during registration.
|
|
88
|
+
*
|
|
89
|
+
* @param {HTTPMethod|undefined} method - The HTTP method to allow.
|
|
90
|
+
* @example
|
|
91
|
+
* ```js
|
|
92
|
+
* middleware.add('GET');
|
|
93
|
+
* middleware.add('POST');
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
add(method) {
|
|
97
|
+
this.#methods.add(method);
|
|
98
|
+
this.#methodsCache = null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Handles an OPTIONS request and terminates the exchange.
|
|
103
|
+
*
|
|
104
|
+
* @param {Exchange} exchange - The current exchange.
|
|
105
|
+
* @param {Function} _next - The unused continuation function.
|
|
106
|
+
* @example
|
|
107
|
+
* ```js
|
|
108
|
+
* const middleware = new OptionsMiddleware();
|
|
109
|
+
* middleware.add('GET');
|
|
110
|
+
* middleware.add('POST');
|
|
111
|
+
* middleware.handle(exchange, next);
|
|
112
|
+
*
|
|
113
|
+
* // Response:
|
|
114
|
+
* // 204 No Content
|
|
115
|
+
* // Allow: GET, POST
|
|
116
|
+
* // Access-Control-Allow-Methods: GET, POST
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
handle(exchange, _next) {
|
|
120
|
+
exchange.response
|
|
121
|
+
.status(204)
|
|
122
|
+
.setHeader('Allow', this.methods)
|
|
123
|
+
.setHeader('Access-Control-Allow-Methods', this.methods)
|
|
124
|
+
.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization')
|
|
125
|
+
.end();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The HTTP methods supported by the server.
|
|
130
|
+
*
|
|
131
|
+
* @type {Readonly<Array<HTTPMethod>>}
|
|
132
|
+
*/
|
|
133
|
+
static #METHODS = Object.freeze([
|
|
134
|
+
'GET',
|
|
135
|
+
'HEAD',
|
|
136
|
+
'POST',
|
|
137
|
+
'PUT',
|
|
138
|
+
'PATCH',
|
|
139
|
+
'DELETE',
|
|
140
|
+
'OPTIONS',
|
|
141
|
+
'QUERY',
|
|
142
|
+
'TRACE',
|
|
143
|
+
]);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
147
|
+
export default OptionsMiddleware;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* @file Defines the route register.
|
|
4
|
+
*
|
|
5
|
+
* @description Maintains route registrations and builds automatic OPTIONS
|
|
6
|
+
* middleware for each pathname pattern.
|
|
7
|
+
*
|
|
8
|
+
* @module RouteRegister
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
12
|
+
// » IMPORT LOCAL MODULES
|
|
13
|
+
import OptionsMiddleware from './OptionsMiddleware.mjs';
|
|
14
|
+
|
|
15
|
+
// ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
16
|
+
/**
|
|
17
|
+
* Represents a single HTTP transaction.
|
|
18
|
+
*
|
|
19
|
+
* @typedef {import('#Exchange').default} Exchange
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Represents an HTTP method supported by the routing layer.
|
|
24
|
+
*
|
|
25
|
+
* @typedef {import('#routing/ExchangeLayer').HTTPMethod} HTTPMethod
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Represents an exchange middleware function.
|
|
30
|
+
*
|
|
31
|
+
* @typedef {import('#routing/ExchangeLayer').MiddlewareFunction} MiddlewareFunction
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Route registration containing an optional HTTP method and pathname pattern.
|
|
36
|
+
*
|
|
37
|
+
* @typedef {[HTTPMethod|undefined, string|undefined]} RouteRegistration
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Collection of route registrations.
|
|
42
|
+
*
|
|
43
|
+
* @typedef {RouteRegistration[]} RouteRegistrations
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Represents the options used to create an exchange layer.
|
|
48
|
+
*
|
|
49
|
+
* @typedef {object} ExchangeLayerOptions
|
|
50
|
+
* @property {'OPTIONS'} method - HTTP method to match for the automatic OPTIONS layer.
|
|
51
|
+
* @property {string} pathnamePattern - URL pathname pattern to match.
|
|
52
|
+
* @property {MiddlewareFunction} middleware - Middleware executed when the exchange matches the layer criteria.
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
// ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
56
|
+
/**
|
|
57
|
+
* Maintains registered routes and their automatic OPTIONS middleware.
|
|
58
|
+
*
|
|
59
|
+
* @classdesc `RouteRegister` groups registered HTTP methods by pathname pattern
|
|
60
|
+
* and creates an OPTIONS route for each new pathname pattern.
|
|
61
|
+
*/
|
|
62
|
+
class RouteRegister {
|
|
63
|
+
/**
|
|
64
|
+
* OPTIONS middleware keyed by pathname pattern.
|
|
65
|
+
*
|
|
66
|
+
* @type {Map<string, OptionsMiddleware>}
|
|
67
|
+
*/
|
|
68
|
+
#middlewares = new Map();
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Registers a route by its HTTP method and pathname pattern.
|
|
72
|
+
*
|
|
73
|
+
* On the first registration of a pathname pattern, creates an
|
|
74
|
+
* `OptionsMiddleware` and returns the OPTIONS layer options. On later
|
|
75
|
+
* registrations of the same pattern, only adds the method to the existing
|
|
76
|
+
* middleware and returns `undefined`.
|
|
77
|
+
*
|
|
78
|
+
* @param {RouteRegistration} registration - The route registration to add.
|
|
79
|
+
* @returns {ExchangeLayerOptions|undefined} The OPTIONS layer options when the pathname pattern is new; otherwise `undefined`.
|
|
80
|
+
* @example
|
|
81
|
+
* ```js
|
|
82
|
+
* register(['GET', '/users']);
|
|
83
|
+
* // → { method: 'OPTIONS', pathnamePattern: '/users', middleware: handle }
|
|
84
|
+
*
|
|
85
|
+
* register(['POST', '/users']);
|
|
86
|
+
* // → undefined (methods updated)
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
register([method, pathnamePattern]) {
|
|
90
|
+
if (pathnamePattern === undefined) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const optionsMiddleware = this.#middlewares.get(pathnamePattern);
|
|
95
|
+
|
|
96
|
+
if (optionsMiddleware !== undefined) {
|
|
97
|
+
optionsMiddleware.add(method);
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const middleware = new OptionsMiddleware();
|
|
102
|
+
middleware.add(method);
|
|
103
|
+
this.#middlewares.set(pathnamePattern, middleware);
|
|
104
|
+
return {
|
|
105
|
+
method: 'OPTIONS',
|
|
106
|
+
pathnamePattern,
|
|
107
|
+
middleware: middleware.handle,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Registers multiple routes.
|
|
113
|
+
*
|
|
114
|
+
* Only the first registration of each pathname pattern contributes an OPTIONS
|
|
115
|
+
* layer. Later registrations for the same pattern update the existing
|
|
116
|
+
* middleware and are omitted from the result.
|
|
117
|
+
*
|
|
118
|
+
* @param {RouteRegistrations} registrations - The route registrations to add.
|
|
119
|
+
* @returns {ExchangeLayerOptions[]} The new OPTIONS layer options.
|
|
120
|
+
* @example
|
|
121
|
+
* ```js
|
|
122
|
+
* registerBatch([
|
|
123
|
+
* ['GET', '/users'],
|
|
124
|
+
* ['POST', '/users'],
|
|
125
|
+
* ['GET', '/posts'],
|
|
126
|
+
* ]);
|
|
127
|
+
* // → [
|
|
128
|
+
* // { method: 'OPTIONS', pathnamePattern: '/users', middleware: handle },
|
|
129
|
+
* // { method: 'OPTIONS', pathnamePattern: '/posts', middleware: handle },
|
|
130
|
+
* // ]
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
registerBatch(registrations) {
|
|
134
|
+
/** @type {ExchangeLayerOptions[]} */
|
|
135
|
+
const options = [];
|
|
136
|
+
|
|
137
|
+
for (const registration of registrations) {
|
|
138
|
+
const layerOptions = this.register(registration);
|
|
139
|
+
|
|
140
|
+
if (layerOptions !== undefined) {
|
|
141
|
+
options.push(layerOptions);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return options;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
150
|
+
export default RouteRegister;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* @file Provides the routing system for route registration and execution.
|
|
4
|
+
*
|
|
5
|
+
* @module RouterSystem
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
9
|
+
// » IMPORT LOCAL MODULES
|
|
10
|
+
import Pipeline from '#routing/Pipeline';
|
|
11
|
+
import RouteRegister from '#routing/RouterRegister';
|
|
12
|
+
import ExchangeLayer from '#routing/ExchangeLayer';
|
|
13
|
+
|
|
14
|
+
// ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
15
|
+
/**
|
|
16
|
+
* Represents a single HTTP transaction.
|
|
17
|
+
*
|
|
18
|
+
* @typedef {import('#Exchange').default} Exchange
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Defines HTTP middleware pipelines by pathname pattern and method.
|
|
23
|
+
*
|
|
24
|
+
* @typedef {import('#routing/Router').default} Router
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Represents an exchange middleware function.
|
|
29
|
+
*
|
|
30
|
+
* @typedef {import('#routing/ExchangeLayer').MiddlewareFunction} MiddlewareFunction
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Represents the fallback handler invoked when no layer resolves the exchange.
|
|
35
|
+
*
|
|
36
|
+
* @typedef {import('#routing/Pipeline').NotFoundHandler} NotFoundHandler
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Collection of route registrations.
|
|
41
|
+
*
|
|
42
|
+
* @typedef {import('#routing/RouterRegister').RouteRegistrations} RouteRegistrations
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Defines the criteria and middleware used by an exchange layer.
|
|
47
|
+
*
|
|
48
|
+
* @typedef {import('#routing/ExchangeLayer').ExchangeLayerOptions} ExchangeLayerOptions
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Represents an HTTP method supported by the routing layer.
|
|
53
|
+
*
|
|
54
|
+
* @typedef {import('#routing/ExchangeLayer').HTTPMethod} HTTPMethod
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Collection of layer options.
|
|
59
|
+
*
|
|
60
|
+
* @typedef {ExchangeLayerOptions[]} ExchangeLayersOptions
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
// ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
64
|
+
/**
|
|
65
|
+
* Coordinates route registration and layer execution.
|
|
66
|
+
*
|
|
67
|
+
* @classdesc `RouterSystem` maintains the routing pipeline and translates
|
|
68
|
+
* route registrations and layer options into executable exchange layers.
|
|
69
|
+
*/
|
|
70
|
+
class RouterSystem {
|
|
71
|
+
/**
|
|
72
|
+
* Pipeline containing the executable exchange layers.
|
|
73
|
+
*
|
|
74
|
+
* @type {Pipeline}
|
|
75
|
+
*/
|
|
76
|
+
#pipeline = new Pipeline();
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Route register used to maintain registered routes and compose automatic
|
|
80
|
+
* OPTIONS layer options.
|
|
81
|
+
*
|
|
82
|
+
* @type {RouteRegister}
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
#routes = new RouteRegister();
|
|
86
|
+
/**
|
|
87
|
+
* Registers multiple exchange layers from layer options.
|
|
88
|
+
*
|
|
89
|
+
* @param {ExchangeLayersOptions} layersOptions - The layer options used to create
|
|
90
|
+
* executable exchange layers.
|
|
91
|
+
* @example
|
|
92
|
+
* ```js
|
|
93
|
+
* routerSystem.registerLayerOptionsBatch([
|
|
94
|
+
* { method: undefined, pathnamePattern: '/api', middleware: middleware },
|
|
95
|
+
* ]);
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
registerLayerOptionsBatch(layersOptions) {
|
|
99
|
+
this.#registerLayerBatch(layersOptions);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Registers a route entry by transforming method, pathname pattern, and middlewares
|
|
104
|
+
* into executable exchange layers and route registrations.
|
|
105
|
+
*
|
|
106
|
+
* @param {HTTPMethod|undefined} method - The HTTP method to match.
|
|
107
|
+
* @param {string|undefined} pathnamePattern - The URL pathname pattern to match.
|
|
108
|
+
* @param {MiddlewareFunction[]} middlewares - The middleware functions to execute.
|
|
109
|
+
* @example
|
|
110
|
+
* ```js
|
|
111
|
+
* routerSystem.registerRouteEntry('GET', '/users', [authenticate, getUsers]);
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
registerRouteEntry(method, pathnamePattern, middlewares) {
|
|
115
|
+
const layersOptions = middlewares.map(middleware => ({
|
|
116
|
+
method,
|
|
117
|
+
pathnamePattern,
|
|
118
|
+
middleware,
|
|
119
|
+
}));
|
|
120
|
+
|
|
121
|
+
/** @type {RouteRegistrations} */
|
|
122
|
+
const routes = layersOptions.map(layerOptions => [
|
|
123
|
+
layerOptions.method,
|
|
124
|
+
layerOptions.pathnamePattern,
|
|
125
|
+
]);
|
|
126
|
+
|
|
127
|
+
this.#registerRouteBatch(routes);
|
|
128
|
+
this.#registerLayerBatch(layersOptions);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Registers a mounted router by prefixing its routes and middlewares with a base pathname pattern.
|
|
133
|
+
*
|
|
134
|
+
* @param {string} basePathnamePattern - The base URL pathname pattern to prefix.
|
|
135
|
+
* @param {Router} router - The router instance to mount (must implement a `compose()` method).
|
|
136
|
+
* @example
|
|
137
|
+
* ```js
|
|
138
|
+
* const userRouter = new Router();
|
|
139
|
+
* userRouter.get('/', getUsers);
|
|
140
|
+
* routerSystem.registerMountEntry('/api/users', userRouter);
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
registerMountEntry(basePathnamePattern, router) {
|
|
144
|
+
const { middlewares, layersOptions } = router.compose();
|
|
145
|
+
|
|
146
|
+
const options = [
|
|
147
|
+
...middlewares.map(middleware => ({
|
|
148
|
+
method: undefined,
|
|
149
|
+
pathnamePattern: basePathnamePattern,
|
|
150
|
+
middleware,
|
|
151
|
+
})),
|
|
152
|
+
...layersOptions.map(({ method, pathnamePattern, middleware }) => ({
|
|
153
|
+
method,
|
|
154
|
+
pathnamePattern: `${basePathnamePattern}${pathnamePattern}`,
|
|
155
|
+
middleware,
|
|
156
|
+
})),
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
/** @type {RouteRegistrations} */
|
|
160
|
+
const routes = options.map(({ method, pathnamePattern }) => [method, pathnamePattern]);
|
|
161
|
+
|
|
162
|
+
this.#registerRouteBatch(routes);
|
|
163
|
+
this.#registerLayerBatch(options);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Executes the routing pipeline for an exchange.
|
|
168
|
+
*
|
|
169
|
+
* @param {Exchange} exchange - The exchange to process.
|
|
170
|
+
* @param {NotFoundHandler} notFoundHandler - The fallback async function to execute if no layer resolves the exchange.
|
|
171
|
+
* @returns {Promise<void>} A promise fulfilled when routing execution completes.
|
|
172
|
+
* @example
|
|
173
|
+
* ```js
|
|
174
|
+
* await routerSystem.executePipeline(exchange, async (exc) => {
|
|
175
|
+
* exc.response.status(404).json({ error: 'Not Found' });
|
|
176
|
+
* });
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
executePipeline(exchange, notFoundHandler) {
|
|
180
|
+
return this.#pipeline.execute(exchange, notFoundHandler);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Registers multiple routes and adds their automatic OPTIONS layers to
|
|
185
|
+
* the routing pipeline.
|
|
186
|
+
*
|
|
187
|
+
* @param {RouteRegistrations} routes - The route registrations to add.
|
|
188
|
+
* @example
|
|
189
|
+
* ```js
|
|
190
|
+
* routerSystem.registerRouteBatch([
|
|
191
|
+
* ['GET', '/users'],
|
|
192
|
+
* ['POST', '/users'],
|
|
193
|
+
* ]);
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
#registerRouteBatch(routes) {
|
|
197
|
+
const layersOptions = this.#routes.registerBatch(routes);
|
|
198
|
+
|
|
199
|
+
const layers = layersOptions.map(layerOptions => new ExchangeLayer(layerOptions));
|
|
200
|
+
|
|
201
|
+
this.#pipeline.addBatch(layers);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Registers multiple `ExchangeLayer` instances created from the provided layer
|
|
206
|
+
* options and adds them to the routing pipeline.
|
|
207
|
+
*
|
|
208
|
+
* @param {ExchangeLayersOptions} layersOptions - The layer options used to create the `ExchangeLayer` instances.
|
|
209
|
+
* @example
|
|
210
|
+
* ```js
|
|
211
|
+
* routerSystem.registerLayerBatch([
|
|
212
|
+
* { method: 'GET', pathnamePattern: '/users', middleware: getUsers },
|
|
213
|
+
* { method: 'POST', pathnamePattern: '/users', middleware: createUser },
|
|
214
|
+
* ]);
|
|
215
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
#registerLayerBatch(layersOptions) {
|
|
218
|
+
const layers = layersOptions.map(layerOptions => new ExchangeLayer(layerOptions));
|
|
219
|
+
|
|
220
|
+
this.#pipeline.addBatch(layers);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
225
|
+
export default RouterSystem;
|