@boo-labs/server 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +229 -2
  2. package/index.mjs +23 -0
  3. package/package.json +71 -4
  4. package/source/application/Kernel/index.mjs +1137 -0
  5. package/source/application/Server/index.mjs +1201 -0
  6. package/source/application/configurations/Http2CleartextServerConfiguration/index.mjs +168 -0
  7. package/source/application/configurations/Http2SecureServerConfiguration/index.mjs +172 -0
  8. package/source/application/configurations/HttpServerConfiguration/index.mjs +169 -0
  9. package/source/application/configurations/HttpsServerConfiguration/index.mjs +172 -0
  10. package/source/application/configurations/ServerConfiguration/Options/CORSOptions/CORSOptionsValue.mjs +158 -0
  11. package/source/application/configurations/ServerConfiguration/Options/CORSOptions/index.mjs +87 -0
  12. package/source/application/configurations/ServerConfiguration/Options/CSRFOptions/CSRFOptionsValue.mjs +178 -0
  13. package/source/application/configurations/ServerConfiguration/Options/CSRFOptions/index.mjs +80 -0
  14. package/source/application/configurations/ServerConfiguration/Options/CompressOptions/CompressOptionsValue.mjs +126 -0
  15. package/source/application/configurations/ServerConfiguration/Options/CompressOptions/index.mjs +81 -0
  16. package/source/application/configurations/ServerConfiguration/Options/CookieParserOptions/CookieParserOptionsValue.mjs +107 -0
  17. package/source/application/configurations/ServerConfiguration/Options/CookieParserOptions/index.mjs +82 -0
  18. package/source/application/configurations/ServerConfiguration/Options/CookieSessionOptions/CookieSessionOptionsValue.mjs +182 -0
  19. package/source/application/configurations/ServerConfiguration/Options/CookieSessionOptions/index.mjs +86 -0
  20. package/source/application/configurations/ServerConfiguration/Options/ListenOptions/ListenOptionsValue.mjs +95 -0
  21. package/source/application/configurations/ServerConfiguration/Options/ListenOptions/index.mjs +47 -0
  22. package/source/application/configurations/ServerConfiguration/Options/MiddlewareOption.mjs +102 -0
  23. package/source/application/configurations/ServerConfiguration/Options/Options.mjs +105 -0
  24. package/source/application/configurations/ServerConfiguration/Options/RateLimitOptions/RateLimitOptionsValue.mjs +164 -0
  25. package/source/application/configurations/ServerConfiguration/Options/RateLimitOptions/index.mjs +84 -0
  26. package/source/application/configurations/ServerConfiguration/Options/RequestOptions/RequestOptionsValue.mjs +108 -0
  27. package/source/application/configurations/ServerConfiguration/Options/RequestOptions/index.mjs +47 -0
  28. package/source/application/configurations/ServerConfiguration/Options/ResponseOptions/ResponseOptionsValue.mjs +101 -0
  29. package/source/application/configurations/ServerConfiguration/Options/ResponseOptions/index.mjs +47 -0
  30. package/source/application/configurations/ServerConfiguration/Options/SecurityHeadersOptions/SecurityHeadersOptionsValue.mjs +272 -0
  31. package/source/application/configurations/ServerConfiguration/Options/SecurityHeadersOptions/index.mjs +85 -0
  32. package/source/application/configurations/ServerConfiguration/Options/ServerOption.mjs +50 -0
  33. package/source/application/configurations/ServerConfiguration/Options/StaticOptions/StaticOptionsValue.mjs +164 -0
  34. package/source/application/configurations/ServerConfiguration/Options/StaticOptions/index.mjs +89 -0
  35. package/source/application/configurations/ServerConfiguration/index.mjs +781 -0
  36. package/source/application/resolvers/TransportResolver/index.mjs +119 -0
  37. package/source/application/use-cases/CloseServer/index.mjs +85 -0
  38. package/source/application/use-cases/ListenServer/index.mjs +98 -0
  39. package/source/core/EventSystem/index.mjs +110 -0
  40. package/source/core/Exchange/ExchangeContext.mjs +291 -0
  41. package/source/core/Exchange/Telemetry.mjs +193 -0
  42. package/source/core/Exchange/index.mjs +604 -0
  43. package/source/core/ExchangeLayerBuilder/index.mjs +133 -0
  44. package/source/core/IPCMain/IPCMain.mjs +507 -0
  45. package/source/core/IPCMain/IPCProtocol.mjs +441 -0
  46. package/source/core/IPCMain/IPCServerChannel.mjs +331 -0
  47. package/source/core/IPCMain/WebSocketAdapter.mjs +189 -0
  48. package/source/core/Request/BodyParser/BodyParser.mjs +197 -0
  49. package/source/core/Request/BodyParser/JSONParser.mjs +70 -0
  50. package/source/core/Request/BodyParser/MultipartParser.mjs +612 -0
  51. package/source/core/Request/BodyParser/URLEncodedParser.mjs +71 -0
  52. package/source/core/Request/RequestHeaders.mjs +177 -0
  53. package/source/core/Request/index.mjs +805 -0
  54. package/source/core/Response/ResponseHeaders.mjs +402 -0
  55. package/source/core/Response/index.mjs +1370 -0
  56. package/source/core/middlewares/CORSMiddleware/index.mjs +289 -0
  57. package/source/core/middlewares/CSRFMiddleware/index.mjs +284 -0
  58. package/source/core/middlewares/CompressMiddleware/index.mjs +460 -0
  59. package/source/core/middlewares/CookieParserMiddleware/index.mjs +194 -0
  60. package/source/core/middlewares/CookieSessionMiddleware/index.mjs +460 -0
  61. package/source/core/middlewares/RateLimitMiddleware/index.mjs +245 -0
  62. package/source/core/middlewares/SecurityHeadersMiddleware/index.mjs +151 -0
  63. package/source/core/middlewares/StaticMiddleware/index.mjs +628 -0
  64. package/source/core/routing/ExchangeLayer/ExchangeMatcher.mjs +127 -0
  65. package/source/core/routing/ExchangeLayer/index.mjs +254 -0
  66. package/source/core/routing/Pipeline/index.mjs +157 -0
  67. package/source/core/routing/Router/Route.mjs +184 -0
  68. package/source/core/routing/Router/index.mjs +523 -0
  69. package/source/core/routing/RouterRegister/OptionsMiddleware.mjs +147 -0
  70. package/source/core/routing/RouterRegister/index.mjs +150 -0
  71. package/source/core/routing/RouterSystem/index.mjs +225 -0
  72. package/source/core/value-objects/Address/index.mjs +281 -0
  73. package/source/core/value-objects/Endpoint/index.mjs +293 -0
  74. package/source/index.mjs +62 -0
  75. package/source/infrastructure/adapters/Http2Adapter/index.mjs +144 -0
  76. package/source/infrastructure/adapters/Http2CleartextAdapter/index.mjs +147 -0
  77. package/source/infrastructure/adapters/HttpAdapter/index.mjs +124 -0
  78. package/source/infrastructure/adapters/HttpsAdapter/index.mjs +124 -0
  79. package/source/infrastructure/adapters/TransportAdapter/createWebSocketOpenRequest.mjs +843 -0
  80. package/source/infrastructure/adapters/TransportAdapter/index.mjs +555 -0
  81. package/source/shared/Identifier/index.mjs +671 -0
  82. package/source/shared/constants/symbols/brands/index.mjs +45 -0
  83. package/source/shared/domain/UUIDV7/index.mjs +346 -0
  84. package/source/shared/errors/BaseError/index.mjs +157 -0
  85. package/source/shared/errors/IPCError/index.mjs +97 -0
  86. package/source/shared/errors/MiddlewareError/index.mjs +106 -0
  87. package/source/shared/errors/PathnamePatternError/index.mjs +103 -0
  88. package/source/shared/errors/RequestError/index.mjs +116 -0
  89. package/source/shared/errors/ResponseError/index.mjs +140 -0
  90. package/source/shared/errors/RouterError/index.mjs +90 -0
  91. package/source/shared/errors/ServerConfigurationError/index.mjs +551 -0
  92. package/source/shared/errors/ServerError/index.mjs +127 -0
  93. package/source/shared/errors/WebSocketError/index.mjs +58 -0
  94. package/source/shared/outcome/ErrorSchema.mjs +361 -0
  95. package/source/shared/outcome/Result/index.mjs +239 -0
  96. package/source/shared/outcome/Validation/index.mjs +208 -0
  97. package/source/shared/validations/Middleware/index.mjs +160 -0
  98. package/source/shared/validations/PathnamePattern/index.mjs +256 -0
  99. package/source/shared/validations/Server/NotFoundHandler/index.mjs +94 -0
  100. package/source/shared/validations/Server/Router/index.mjs +68 -0
  101. package/source/shared/validations/Server/ServerConfiguration/index.mjs +68 -0
  102. package/source/shared/validations/Server/SystemErrorHandler/index.mjs +94 -0
  103. package/source/shared/validations/ServerConfiguration/CORSOptions/index.mjs +310 -0
  104. package/source/shared/validations/ServerConfiguration/CSRFOptions/index.mjs +299 -0
  105. package/source/shared/validations/ServerConfiguration/CompressOptions/index.mjs +204 -0
  106. package/source/shared/validations/ServerConfiguration/CookieParserOptions/index.mjs +102 -0
  107. package/source/shared/validations/ServerConfiguration/CookieSessionOptions/index.mjs +332 -0
  108. package/source/shared/validations/ServerConfiguration/ListenOptions/index.mjs +178 -0
  109. package/source/shared/validations/ServerConfiguration/RateLimitOptions/index.mjs +229 -0
  110. package/source/shared/validations/ServerConfiguration/RequestOptions/index.mjs +132 -0
  111. package/source/shared/validations/ServerConfiguration/ResponseOptions/index.mjs +122 -0
  112. package/source/shared/validations/ServerConfiguration/SecurityHeadersOptions/index.mjs +137 -0
  113. package/source/shared/validations/ServerConfiguration/StaticOptions/index.mjs +248 -0
  114. package/types/index.d.ts +719 -0
@@ -0,0 +1,158 @@
1
+ // @ts-check
2
+ /**
3
+ * @file Defines the CORSOptionsValue value object.
4
+ *
5
+ * @description Normalizes and validates CORS middleware configuration used
6
+ * by CORSMiddleware. When options are omitted, the value is undefined.
7
+ *
8
+ * @module CORSOptionsValue
9
+ */
10
+
11
+ // ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
12
+ // » IMPORT LOCAL MODULES
13
+ import { assertCORSOptions } from '#shared/validations/ServerConfiguration/CORSOptions';
14
+
15
+ // ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
16
+ /**
17
+ * Domain error thrown by server configuration validation.
18
+ *
19
+ * @typedef {import('#shared/errors/ServerConfigurationError').default} ServerConfigurationError
20
+ */
21
+
22
+ /**
23
+ * Represents the CORS options accepted by the server configuration.
24
+ *
25
+ * @typedef {object} CORSOptionsInput
26
+ * @property {true|string|string[]} [origin] - Allowed origin(s). Defaults to `'*'`.
27
+ * @property {string[]} [methods] - Allowed HTTP methods for preflight responses. Defaults to a standard set.
28
+ * @property {string[]} [allowedHeaders] - Headers the client is allowed to send. When omitted, the value of Access-Control-Request-Headers is reflected.
29
+ * @property {string[]} [exposedHeaders] - Headers the client is allowed to read from the response.
30
+ * @property {boolean} [credentials] - Whether the response may include credentials. Defaults to `false`.
31
+ * @property {number} [maxAge] - How long (in seconds) the preflight response may be cached.
32
+ * @property {boolean} [preflightContinue] - Whether to call `next()` on successful preflight instead of ending the response. Defaults to `false`.
33
+ * @property {number} [optionsSuccessStatus] - Status code used for successful preflight responses. Defaults to `204`.
34
+ */
35
+
36
+ /**
37
+ * Represents the normalized CORS options.
38
+ *
39
+ * @typedef {object} CORSOptionsData
40
+ * @property {true|string|string[]} origin - Allowed origin(s).
41
+ * @property {Readonly<string[]>} methods - Allowed HTTP methods for preflight responses.
42
+ * @property {Readonly<string[]>|undefined} allowedHeaders - Headers the client is allowed to send, or `undefined` to reflect the request headers.
43
+ * @property {Readonly<string[]>|undefined} exposedHeaders - Headers the client is allowed to read from the response.
44
+ * @property {boolean} credentials - Whether the response may include credentials.
45
+ * @property {number|undefined} maxAge - How long (in seconds) the preflight response may be cached.
46
+ * @property {boolean} preflightContinue - Whether to call `next()` on successful preflight instead of ending the response.
47
+ * @property {number} optionsSuccessStatus - Status code used for successful preflight responses.
48
+ */
49
+
50
+ // ━━ CONSTANTS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
51
+ /**
52
+ * Default allowed origin.
53
+ *
54
+ * @type {true|string|string[]}
55
+ */
56
+ const DEFAULT_ORIGIN = '*';
57
+
58
+ /**
59
+ * Default allowed HTTP methods for preflight responses.
60
+ *
61
+ * @type {readonly string[]}
62
+ */
63
+ const DEFAULT_METHODS = Object.freeze(['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE']);
64
+
65
+ /**
66
+ * Default credentials behavior.
67
+ *
68
+ * @type {boolean}
69
+ */
70
+ const DEFAULT_CREDENTIALS = false;
71
+
72
+ /**
73
+ * Default preflight continuation behavior.
74
+ *
75
+ * @type {boolean}
76
+ */
77
+ const DEFAULT_PREFLIGHT_CONTINUE = false;
78
+
79
+ /**
80
+ * Default status code used for successful preflight responses.
81
+ *
82
+ * @type {number}
83
+ */
84
+ const DEFAULT_OPTIONS_SUCCESS_STATUS = 204;
85
+
86
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
87
+ /**
88
+ * Immutable value object representing normalized CORS middleware options.
89
+ *
90
+ * @classdesc Validates and normalizes CORS middleware options.
91
+ */
92
+ class CORSOptionsValue {
93
+ /**
94
+ * The normalized CORS options.
95
+ *
96
+ * @type {Readonly<CORSOptionsData>|undefined}
97
+ */
98
+ #value;
99
+
100
+ /**
101
+ * Creates a new {@link CORSOptionsValue}.
102
+ *
103
+ * @param {CORSOptionsInput} [options] - The CORS middleware options.
104
+ * @throws {ServerConfigurationError} If `options` is invalid.
105
+ * @example
106
+ * ```js
107
+ * const options = new CORSOptionsValue({ origin: true, credentials: true });
108
+ *
109
+ * options.value;
110
+ * // => {
111
+ * // origin: true,
112
+ * // methods: ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE'],
113
+ * // allowedHeaders: undefined,
114
+ * // exposedHeaders: undefined,
115
+ * // credentials: true,
116
+ * // maxAge: undefined,
117
+ * // preflightContinue: false,
118
+ * // optionsSuccessStatus: 204,
119
+ * // }
120
+ * ```
121
+ */
122
+ constructor(options) {
123
+ assertCORSOptions(options);
124
+
125
+ this.#value =
126
+ options === undefined
127
+ ? options
128
+ : Object.freeze({
129
+ origin: options.origin ?? DEFAULT_ORIGIN,
130
+ methods: Object.freeze([...(options.methods ?? DEFAULT_METHODS)]),
131
+ allowedHeaders:
132
+ options.allowedHeaders === undefined
133
+ ? undefined
134
+ : Object.freeze([...options.allowedHeaders]),
135
+ exposedHeaders:
136
+ options.exposedHeaders === undefined
137
+ ? undefined
138
+ : Object.freeze([...options.exposedHeaders]),
139
+ credentials: options.credentials ?? DEFAULT_CREDENTIALS,
140
+ maxAge: options.maxAge,
141
+ preflightContinue: options.preflightContinue ?? DEFAULT_PREFLIGHT_CONTINUE,
142
+ optionsSuccessStatus: options.optionsSuccessStatus ?? DEFAULT_OPTIONS_SUCCESS_STATUS,
143
+ });
144
+ }
145
+
146
+ /**
147
+ * The normalized CORS options.
148
+ *
149
+ * @readonly
150
+ * @type {Readonly<CORSOptionsData>|undefined}
151
+ */
152
+ get value() {
153
+ return this.#value;
154
+ }
155
+ }
156
+
157
+ // ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
158
+ export default CORSOptionsValue;
@@ -0,0 +1,87 @@
1
+ // @ts-check
2
+ /**
3
+ * @file Defines the CORSOptions middleware configuration option.
4
+ *
5
+ * @description CORSOptions wraps a normalized CORSOptionsValue into the
6
+ * middleware configuration option hierarchy.
7
+ *
8
+ * @module CORSOptions
9
+ */
10
+
11
+ // ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
12
+ // » IMPORT LOCAL MODULES
13
+ import MiddlewareOption from '../MiddlewareOption.mjs';
14
+ import CORSOptionsValue from './CORSOptionsValue.mjs';
15
+
16
+ // ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
17
+ /**
18
+ * Represents the CORS options accepted by the server configuration.
19
+ *
20
+ * @typedef {import('./CORSOptionsValue.mjs').CORSOptionsInput} CORSOptionsInput
21
+ */
22
+
23
+ /**
24
+ * Represents the normalized CORS options.
25
+ *
26
+ * @typedef {import('./CORSOptionsValue.mjs').CORSOptionsData} CORSOptionsData
27
+ */
28
+
29
+ /**
30
+ * Represents one middleware configuration consumed by the exchange layer builder.
31
+ *
32
+ * @typedef {import('../MiddlewareOption.mjs').MiddlewareConfiguration} MiddlewareConfiguration
33
+ */
34
+
35
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
36
+ /**
37
+ * Middleware configuration option wrapping a normalized CORS configuration.
38
+ *
39
+ * @augments {MiddlewareOption<CORSOptionsData|undefined>}
40
+ */
41
+ class CORSOptions extends MiddlewareOption {
42
+ /**
43
+ * Creates a new {@link CORSOptions}.
44
+ *
45
+ * @param {CORSOptionsInput} [options] - The CORS middleware options.
46
+ * @example
47
+ * ```js
48
+ * const CORSOptions = new CORSOptions({
49
+ * origin: true,
50
+ * methods: ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE'],
51
+ * credentials: true,
52
+ * maxAge: 86400,
53
+ * preflightContinue: false,
54
+ * optionsSuccessStatus: 204,
55
+ * });
56
+ * ```
57
+ */
58
+ constructor(options) {
59
+ super('cors', new CORSOptionsValue(options));
60
+ }
61
+
62
+ /**
63
+ * Composes the CORS middleware configuration consumed by the exchange
64
+ * layer builder.
65
+ *
66
+ * CORS is a cross-cutting concern applied to every request regardless of
67
+ * pathname or method, so both `pathnamePattern` and `method` are
68
+ * `undefined`.
69
+ *
70
+ * @returns {MiddlewareConfiguration} The composed middleware configuration.
71
+ * @example
72
+ * ```js
73
+ * const configuration = CORSOptions.compose();
74
+ * ```
75
+ */
76
+ compose() {
77
+ return {
78
+ name: this.name,
79
+ options: this.value,
80
+ pathnamePattern: undefined,
81
+ method: undefined,
82
+ };
83
+ }
84
+ }
85
+
86
+ // ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
87
+ export default CORSOptions;
@@ -0,0 +1,178 @@
1
+ // @ts-check
2
+ /**
3
+ * @file Defines the CSRFOptionsValue value object.
4
+ *
5
+ * @description Normalizes and validates CSRF middleware configuration used
6
+ * by CsrfMiddleware. When options are omitted, the value is undefined and the
7
+ * middleware is not registered.
8
+ *
9
+ * @module CSRFOptionsValue
10
+ */
11
+
12
+ // ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
13
+ // » IMPORT LOCAL MODULES
14
+ import { assertCSRFOptions } from '#shared/validations/ServerConfiguration/CSRFOptions';
15
+
16
+ // ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
17
+ /**
18
+ * Domain error thrown by server configuration validation.
19
+ *
20
+ * @typedef {import('#shared/errors/ServerConfigurationError').default} ServerConfigurationError
21
+ */
22
+
23
+ /**
24
+ * Represents the CSRF options accepted by the server configuration.
25
+ *
26
+ * @typedef {object} CSRFOptionsInput
27
+ * @property {string} [cookieName] - Name of the CSRF cookie.
28
+ * @property {string} [headerName] - Name of the request header that must carry the token.
29
+ * @property {string[]} [safeMethods] - HTTP methods that skip CSRF validation.
30
+ * @property {number} [tokenBytes] - Number of random bytes used to generate the token.
31
+ * @property {string} [path] - Cookie Path attribute.
32
+ * @property {string} [domain] - Cookie Domain attribute.
33
+ * @property {boolean} [secure] - Whether the cookie is marked Secure.
34
+ * @property {'Strict'|'Lax'|'None'} [sameSite] - Cookie SameSite attribute.
35
+ */
36
+
37
+ /**
38
+ * Represents the normalized CSRF options.
39
+ *
40
+ * @typedef {object} CSRFOptionsData
41
+ * @property {string} cookieName - Name of the CSRF cookie.
42
+ * @property {string} headerName - Name of the request header that must carry the token.
43
+ * @property {Readonly<string[]>} safeMethods - HTTP methods that skip CSRF validation.
44
+ * @property {number} tokenBytes - Number of random bytes used to generate the token.
45
+ * @property {string} path - Cookie Path attribute.
46
+ * @property {string|undefined} domain - Cookie Domain attribute.
47
+ * @property {boolean} secure - Whether the cookie is marked Secure.
48
+ * @property {'Strict'|'Lax'|'None'} sameSite - Cookie SameSite attribute.
49
+ */
50
+
51
+ // ━━ CONSTANTS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
52
+ /**
53
+ * Default CSRF cookie name.
54
+ *
55
+ * @type {string}
56
+ */
57
+ const DEFAULT_COOKIE_NAME = 'csrf-token';
58
+
59
+ /**
60
+ * Default request header name.
61
+ *
62
+ * @type {string}
63
+ */
64
+ const DEFAULT_HEADER_NAME = 'x-csrf-token';
65
+
66
+ /**
67
+ * Default safe HTTP methods.
68
+ *
69
+ * @type {readonly string[]}
70
+ */
71
+ const DEFAULT_SAFE_METHODS = Object.freeze(['GET', 'HEAD', 'OPTIONS']);
72
+
73
+ /**
74
+ * Default number of random bytes used to generate the token.
75
+ *
76
+ * @type {number}
77
+ */
78
+ const DEFAULT_TOKEN_BYTES = 32;
79
+
80
+ /**
81
+ * Default cookie path attribute.
82
+ *
83
+ * @type {string}
84
+ */
85
+ const DEFAULT_PATH = '/';
86
+
87
+ /**
88
+ * Default Secure attribute.
89
+ *
90
+ * @type {boolean}
91
+ */
92
+ const DEFAULT_SECURE = true;
93
+
94
+ /**
95
+ * Default SameSite attribute.
96
+ *
97
+ * @type {'Strict'|'Lax'|'None'}
98
+ */
99
+ const DEFAULT_SAME_SITE = 'Lax';
100
+
101
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
102
+ /**
103
+ * Immutable value object representing normalized CSRF middleware options.
104
+ *
105
+ * @classdesc Validates and normalizes CSRF middleware options. When options
106
+ * are provided, defaults are applied for any omitted property. When options
107
+ * are omitted, the value is undefined.
108
+ *
109
+ * @example
110
+ * ```js
111
+ * const options = new CSRFOptionsValue({ cookieName: 'csrf' });
112
+ *
113
+ * options.value;
114
+ * // => {
115
+ * // cookieName: 'csrf',
116
+ * // headerName: 'x-csrf-token',
117
+ * // safeMethods: ['GET', 'HEAD', 'OPTIONS'],
118
+ * // tokenBytes: 32,
119
+ * // path: '/',
120
+ * // domain: undefined,
121
+ * // secure: true,
122
+ * // sameSite: 'Lax',
123
+ * // }
124
+ * ```
125
+ *
126
+ * @example
127
+ * ```js
128
+ * const options = new CSRFOptionsValue();
129
+ *
130
+ * options.value;
131
+ * // => undefined
132
+ * ```
133
+ */
134
+ class CSRFOptionsValue {
135
+ /**
136
+ * The normalized CSRF options.
137
+ *
138
+ * @type {Readonly<CSRFOptionsData>|undefined}
139
+ */
140
+ #value;
141
+
142
+ /**
143
+ * Creates a new {@link CSRFOptionsValue}.
144
+ *
145
+ * @param {CSRFOptionsInput} [options] - The CSRF middleware options.
146
+ * @throws {ServerConfigurationError} If `options` is invalid.
147
+ */
148
+ constructor(options) {
149
+ assertCSRFOptions(options);
150
+
151
+ this.#value =
152
+ options === undefined
153
+ ? options
154
+ : Object.freeze({
155
+ cookieName: options.cookieName ?? DEFAULT_COOKIE_NAME,
156
+ headerName: options.headerName ?? DEFAULT_HEADER_NAME,
157
+ safeMethods: Object.freeze([...(options.safeMethods ?? DEFAULT_SAFE_METHODS)]),
158
+ tokenBytes: options.tokenBytes ?? DEFAULT_TOKEN_BYTES,
159
+ path: options.path ?? DEFAULT_PATH,
160
+ domain: options.domain,
161
+ secure: options.secure ?? DEFAULT_SECURE,
162
+ sameSite: options.sameSite ?? DEFAULT_SAME_SITE,
163
+ });
164
+ }
165
+
166
+ /**
167
+ * The normalized CSRF options.
168
+ *
169
+ * @readonly
170
+ * @type {Readonly<CSRFOptionsData>|undefined}
171
+ */
172
+ get value() {
173
+ return this.#value;
174
+ }
175
+ }
176
+
177
+ // ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
178
+ export default CSRFOptionsValue;
@@ -0,0 +1,80 @@
1
+ // @ts-check
2
+ /**
3
+ * @file Defines the CSRFOptions middleware configuration option.
4
+ *
5
+ * @description CSRFOptions wraps a normalized CSRFOptionsValue into the
6
+ * middleware configuration option hierarchy.
7
+ *
8
+ * @module CSRFOptions
9
+ */
10
+
11
+ // ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
12
+ // » IMPORT LOCAL MODULES
13
+ import MiddlewareOption from '../MiddlewareOption.mjs';
14
+ import CSRFOptionsValue from './CSRFOptionsValue.mjs';
15
+
16
+ // ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
17
+ /**
18
+ * Represents the CSRF options accepted by the server configuration.
19
+ *
20
+ * @typedef {import('./CSRFOptionsValue.mjs').CSRFOptionsInput} CSRFOptionsInput
21
+ */
22
+
23
+ /**
24
+ * Represents the normalized CSRF options.
25
+ *
26
+ * @typedef {import('./CSRFOptionsValue.mjs').CSRFOptionsData} CSRFOptionsData
27
+ */
28
+
29
+ /**
30
+ * Represents one middleware configuration consumed by the exchange layer builder.
31
+ *
32
+ * @typedef {import('../MiddlewareOption.mjs').MiddlewareConfiguration} MiddlewareConfiguration
33
+ */
34
+
35
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
36
+ /**
37
+ * Middleware configuration option wrapping a normalized CSRF configuration.
38
+ *
39
+ * @augments {MiddlewareOption<CSRFOptionsData|undefined>}
40
+ */
41
+ class CSRFOptions extends MiddlewareOption {
42
+ /**
43
+ * Creates a new {@link CSRFOptions}.
44
+ *
45
+ * @param {CSRFOptionsInput} [options] - The CSRF middleware options.
46
+ * @example
47
+ * ```js
48
+ * const CSRFOptions = new CSRFOptions({ cookieName: 'csrf' });
49
+ * ```
50
+ */
51
+ constructor(options) {
52
+ super('csrf', new CSRFOptionsValue(options));
53
+ }
54
+
55
+ /**
56
+ * Composes the CSRF middleware configuration consumed by the exchange
57
+ * layer builder.
58
+ *
59
+ * CSRF protection is a cross-cutting concern applied to every request
60
+ * regardless of pathname or method, so both `pathnamePattern` and `method`
61
+ * are `undefined`.
62
+ *
63
+ * @returns {MiddlewareConfiguration} The composed middleware configuration.
64
+ * @example
65
+ * ```js
66
+ * const configuration = CSRFOptions.compose();
67
+ * ```
68
+ */
69
+ compose() {
70
+ return {
71
+ name: this.name,
72
+ options: this.value,
73
+ pathnamePattern: undefined,
74
+ method: undefined,
75
+ };
76
+ }
77
+ }
78
+
79
+ // ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
80
+ export default CSRFOptions;
@@ -0,0 +1,126 @@
1
+ // @ts-check
2
+ /**
3
+ * @file Defines the CompressOptionsValue value object.
4
+ *
5
+ * @description Normalizes and validates compression middleware configuration
6
+ * used by CompressMiddleware. When options are omitted, the value is
7
+ * undefined and the middleware is not registered.
8
+ *
9
+ * @module CompressOptionsValue
10
+ */
11
+
12
+ // ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
13
+ // » IMPORT LOCAL MODULES
14
+ import { assertCompressOptions } from '#shared/validations/ServerConfiguration/CompressOptions';
15
+
16
+ // ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
17
+ /**
18
+ * Domain error thrown by server configuration validation.
19
+ *
20
+ * @typedef {import('#shared/errors/ServerConfigurationError').default} ServerConfigurationError
21
+ */
22
+
23
+ /**
24
+ * Represents the compression options accepted by the server configuration.
25
+ *
26
+ * @typedef {object} CompressOptionsInput
27
+ * @property {number} [threshold] - Minimum body size in bytes required to compress. Use `0` to compress any non-empty body.
28
+ * @property {number} [level] - Compression level passed to the underlying zlib implementation.
29
+ * @property {string[]} [types] - Compressible media types. When omitted, the default allow-list is used.
30
+ */
31
+
32
+ /**
33
+ * Represents the normalized compression options.
34
+ *
35
+ * @typedef {object} CompressOptionsData
36
+ * @property {number} threshold - Minimum body size in bytes required to compress. Use `0` to compress any non-empty body.
37
+ * @property {number} level - Compression level passed to the underlying zlib implementation.
38
+ * @property {Readonly<string[]>|undefined} types - Compressible media types. When `undefined`, the default allow-list is used.
39
+ */
40
+
41
+ // ━━ CONSTANTS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
42
+ /**
43
+ * Default minimum body size in bytes required to compress.
44
+ *
45
+ * @type {number}
46
+ */
47
+ const DEFAULT_THRESHOLD = 1024;
48
+
49
+ /**
50
+ * Default compression level.
51
+ *
52
+ * `-1` delegates to the zlib default for the selected algorithm.
53
+ *
54
+ * @type {number}
55
+ */
56
+ const DEFAULT_LEVEL = -1;
57
+
58
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
59
+ /**
60
+ * Immutable value object representing normalized compression middleware
61
+ * options.
62
+ *
63
+ * @classdesc Validates and normalizes compression middleware options. When
64
+ * options are provided, defaults are applied for any omitted property. When
65
+ * options are omitted, the value is undefined.
66
+ *
67
+ * @example
68
+ * ```js
69
+ * const options = new CompressOptionsValue({ threshold: 2048, level: 6 });
70
+ *
71
+ * options.value;
72
+ * // => {
73
+ * // threshold: 2048,
74
+ * // level: 6,
75
+ * // types: undefined,
76
+ * // }
77
+ * ```
78
+ *
79
+ * @example
80
+ * ```js
81
+ * const options = new CompressOptionsValue();
82
+ *
83
+ * options.value;
84
+ * // => undefined
85
+ * ```
86
+ */
87
+ class CompressOptionsValue {
88
+ /**
89
+ * The normalized compression options.
90
+ *
91
+ * @type {Readonly<CompressOptionsData>|undefined}
92
+ */
93
+ #value;
94
+
95
+ /**
96
+ * Creates a new {@link CompressOptionsValue}.
97
+ *
98
+ * @param {CompressOptionsInput} [options] - The compression middleware options.
99
+ * @throws {ServerConfigurationError} If `options` is invalid.
100
+ */
101
+ constructor(options) {
102
+ assertCompressOptions(options);
103
+
104
+ this.#value =
105
+ options === undefined
106
+ ? options
107
+ : Object.freeze({
108
+ threshold: options.threshold ?? DEFAULT_THRESHOLD,
109
+ level: options.level ?? DEFAULT_LEVEL,
110
+ types: options.types === undefined ? undefined : Object.freeze([...options.types]),
111
+ });
112
+ }
113
+
114
+ /**
115
+ * The normalized compression options.
116
+ *
117
+ * @readonly
118
+ * @type {Readonly<CompressOptionsData>|undefined}
119
+ */
120
+ get value() {
121
+ return this.#value;
122
+ }
123
+ }
124
+
125
+ // ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
126
+ export default CompressOptionsValue;