@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.
Files changed (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +287 -2
  3. package/index.mjs +23 -0
  4. package/package.json +71 -4
  5. package/source/application/Kernel/index.mjs +1137 -0
  6. package/source/application/Server/index.mjs +1201 -0
  7. package/source/application/configurations/Http2CleartextServerConfiguration/index.mjs +168 -0
  8. package/source/application/configurations/Http2SecureServerConfiguration/index.mjs +172 -0
  9. package/source/application/configurations/HttpServerConfiguration/index.mjs +169 -0
  10. package/source/application/configurations/HttpsServerConfiguration/index.mjs +172 -0
  11. package/source/application/configurations/ServerConfiguration/Options/CORSOptions/CORSOptionsValue.mjs +158 -0
  12. package/source/application/configurations/ServerConfiguration/Options/CORSOptions/index.mjs +87 -0
  13. package/source/application/configurations/ServerConfiguration/Options/CSRFOptions/CSRFOptionsValue.mjs +178 -0
  14. package/source/application/configurations/ServerConfiguration/Options/CSRFOptions/index.mjs +80 -0
  15. package/source/application/configurations/ServerConfiguration/Options/CompressOptions/CompressOptionsValue.mjs +126 -0
  16. package/source/application/configurations/ServerConfiguration/Options/CompressOptions/index.mjs +81 -0
  17. package/source/application/configurations/ServerConfiguration/Options/CookieParserOptions/CookieParserOptionsValue.mjs +107 -0
  18. package/source/application/configurations/ServerConfiguration/Options/CookieParserOptions/index.mjs +82 -0
  19. package/source/application/configurations/ServerConfiguration/Options/CookieSessionOptions/CookieSessionOptionsValue.mjs +182 -0
  20. package/source/application/configurations/ServerConfiguration/Options/CookieSessionOptions/index.mjs +86 -0
  21. package/source/application/configurations/ServerConfiguration/Options/ListenOptions/ListenOptionsValue.mjs +95 -0
  22. package/source/application/configurations/ServerConfiguration/Options/ListenOptions/index.mjs +47 -0
  23. package/source/application/configurations/ServerConfiguration/Options/MiddlewareOption.mjs +102 -0
  24. package/source/application/configurations/ServerConfiguration/Options/Options.mjs +105 -0
  25. package/source/application/configurations/ServerConfiguration/Options/RateLimitOptions/RateLimitOptionsValue.mjs +164 -0
  26. package/source/application/configurations/ServerConfiguration/Options/RateLimitOptions/index.mjs +84 -0
  27. package/source/application/configurations/ServerConfiguration/Options/RequestOptions/RequestOptionsValue.mjs +108 -0
  28. package/source/application/configurations/ServerConfiguration/Options/RequestOptions/index.mjs +47 -0
  29. package/source/application/configurations/ServerConfiguration/Options/ResponseOptions/ResponseOptionsValue.mjs +101 -0
  30. package/source/application/configurations/ServerConfiguration/Options/ResponseOptions/index.mjs +47 -0
  31. package/source/application/configurations/ServerConfiguration/Options/SecurityHeadersOptions/SecurityHeadersOptionsValue.mjs +272 -0
  32. package/source/application/configurations/ServerConfiguration/Options/SecurityHeadersOptions/index.mjs +85 -0
  33. package/source/application/configurations/ServerConfiguration/Options/ServerOption.mjs +50 -0
  34. package/source/application/configurations/ServerConfiguration/Options/StaticOptions/StaticOptionsValue.mjs +164 -0
  35. package/source/application/configurations/ServerConfiguration/Options/StaticOptions/index.mjs +89 -0
  36. package/source/application/configurations/ServerConfiguration/index.mjs +781 -0
  37. package/source/application/resolvers/TransportResolver/index.mjs +119 -0
  38. package/source/application/use-cases/CloseServer/index.mjs +85 -0
  39. package/source/application/use-cases/ListenServer/index.mjs +98 -0
  40. package/source/core/EventSystem/index.mjs +110 -0
  41. package/source/core/Exchange/ExchangeContext.mjs +291 -0
  42. package/source/core/Exchange/Telemetry.mjs +193 -0
  43. package/source/core/Exchange/index.mjs +604 -0
  44. package/source/core/ExchangeLayerBuilder/index.mjs +133 -0
  45. package/source/core/IPCMain/IPCMain.mjs +507 -0
  46. package/source/core/IPCMain/IPCProtocol.mjs +441 -0
  47. package/source/core/IPCMain/IPCServerChannel.mjs +331 -0
  48. package/source/core/IPCMain/WebSocketAdapter.mjs +189 -0
  49. package/source/core/Request/BodyParser/BodyParser.mjs +197 -0
  50. package/source/core/Request/BodyParser/JSONParser.mjs +70 -0
  51. package/source/core/Request/BodyParser/MultipartParser.mjs +612 -0
  52. package/source/core/Request/BodyParser/URLEncodedParser.mjs +71 -0
  53. package/source/core/Request/RequestHeaders.mjs +177 -0
  54. package/source/core/Request/index.mjs +805 -0
  55. package/source/core/Response/ResponseHeaders.mjs +402 -0
  56. package/source/core/Response/index.mjs +1370 -0
  57. package/source/core/middlewares/CORSMiddleware/index.mjs +289 -0
  58. package/source/core/middlewares/CSRFMiddleware/index.mjs +284 -0
  59. package/source/core/middlewares/CompressMiddleware/index.mjs +460 -0
  60. package/source/core/middlewares/CookieParserMiddleware/index.mjs +194 -0
  61. package/source/core/middlewares/CookieSessionMiddleware/index.mjs +460 -0
  62. package/source/core/middlewares/RateLimitMiddleware/index.mjs +245 -0
  63. package/source/core/middlewares/SecurityHeadersMiddleware/index.mjs +151 -0
  64. package/source/core/middlewares/StaticMiddleware/index.mjs +628 -0
  65. package/source/core/routing/ExchangeLayer/ExchangeMatcher.mjs +127 -0
  66. package/source/core/routing/ExchangeLayer/index.mjs +254 -0
  67. package/source/core/routing/Pipeline/index.mjs +157 -0
  68. package/source/core/routing/Router/Route.mjs +184 -0
  69. package/source/core/routing/Router/index.mjs +523 -0
  70. package/source/core/routing/RouterRegister/OptionsMiddleware.mjs +147 -0
  71. package/source/core/routing/RouterRegister/index.mjs +150 -0
  72. package/source/core/routing/RouterSystem/index.mjs +225 -0
  73. package/source/core/value-objects/Address/index.mjs +281 -0
  74. package/source/core/value-objects/Endpoint/index.mjs +293 -0
  75. package/source/index.mjs +62 -0
  76. package/source/infrastructure/adapters/Http2Adapter/index.mjs +144 -0
  77. package/source/infrastructure/adapters/Http2CleartextAdapter/index.mjs +147 -0
  78. package/source/infrastructure/adapters/HttpAdapter/index.mjs +124 -0
  79. package/source/infrastructure/adapters/HttpsAdapter/index.mjs +124 -0
  80. package/source/infrastructure/adapters/TransportAdapter/createWebSocketOpenRequest.mjs +843 -0
  81. package/source/infrastructure/adapters/TransportAdapter/index.mjs +555 -0
  82. package/source/shared/Identifier/index.mjs +671 -0
  83. package/source/shared/constants/symbols/brands/index.mjs +45 -0
  84. package/source/shared/domain/UUIDV7/index.mjs +346 -0
  85. package/source/shared/errors/BaseError/index.mjs +157 -0
  86. package/source/shared/errors/IPCError/index.mjs +97 -0
  87. package/source/shared/errors/MiddlewareError/index.mjs +106 -0
  88. package/source/shared/errors/PathnamePatternError/index.mjs +103 -0
  89. package/source/shared/errors/RequestError/index.mjs +116 -0
  90. package/source/shared/errors/ResponseError/index.mjs +140 -0
  91. package/source/shared/errors/RouterError/index.mjs +90 -0
  92. package/source/shared/errors/ServerConfigurationError/index.mjs +551 -0
  93. package/source/shared/errors/ServerError/index.mjs +127 -0
  94. package/source/shared/errors/WebSocketError/index.mjs +58 -0
  95. package/source/shared/outcome/ErrorSchema.mjs +361 -0
  96. package/source/shared/outcome/Result/index.mjs +239 -0
  97. package/source/shared/outcome/Validation/index.mjs +208 -0
  98. package/source/shared/validations/Middleware/index.mjs +160 -0
  99. package/source/shared/validations/PathnamePattern/index.mjs +256 -0
  100. package/source/shared/validations/Server/NotFoundHandler/index.mjs +94 -0
  101. package/source/shared/validations/Server/Router/index.mjs +68 -0
  102. package/source/shared/validations/Server/ServerConfiguration/index.mjs +68 -0
  103. package/source/shared/validations/Server/SystemErrorHandler/index.mjs +94 -0
  104. package/source/shared/validations/ServerConfiguration/CORSOptions/index.mjs +310 -0
  105. package/source/shared/validations/ServerConfiguration/CSRFOptions/index.mjs +299 -0
  106. package/source/shared/validations/ServerConfiguration/CompressOptions/index.mjs +204 -0
  107. package/source/shared/validations/ServerConfiguration/CookieParserOptions/index.mjs +102 -0
  108. package/source/shared/validations/ServerConfiguration/CookieSessionOptions/index.mjs +332 -0
  109. package/source/shared/validations/ServerConfiguration/ListenOptions/index.mjs +178 -0
  110. package/source/shared/validations/ServerConfiguration/RateLimitOptions/index.mjs +229 -0
  111. package/source/shared/validations/ServerConfiguration/RequestOptions/index.mjs +132 -0
  112. package/source/shared/validations/ServerConfiguration/ResponseOptions/index.mjs +122 -0
  113. package/source/shared/validations/ServerConfiguration/SecurityHeadersOptions/index.mjs +137 -0
  114. package/source/shared/validations/ServerConfiguration/StaticOptions/index.mjs +248 -0
  115. package/types/index.d.ts +719 -0
@@ -0,0 +1,102 @@
1
+ // @ts-check
2
+ /**
3
+ * @file Defines the abstract base for middleware configuration options.
4
+ *
5
+ * @description MiddlewareOption identifies configuration options that
6
+ * represent middlewares (cors, static, securityHeaders, rateLimit).
7
+ *
8
+ * @module MiddlewareOption
9
+ */
10
+
11
+ // ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
12
+ // » IMPORT LOCAL MODULES
13
+ import Options from './Options.mjs';
14
+
15
+ // ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
16
+ /**
17
+ * Represents a router HTTP method supported by the routing layer.
18
+ *
19
+ * @typedef {'GET'|'HEAD'|'POST'|'PUT'|'PATCH'|'DELETE'|'OPTIONS'|'QUERY'|'TRACE'} HTTPMethod
20
+ */
21
+ /**
22
+ * Represents one middleware configuration consumed by the exchange layer builder.
23
+ *
24
+ * @typedef {object} MiddlewareConfiguration
25
+ * @property {string} name - Middleware name.
26
+ * @property {Record<string, unknown>|undefined} options - Middleware configuration options.
27
+ * @property {string|undefined} pathnamePattern - URL pathname pattern to match.
28
+ * @property {HTTPMethod|undefined} method - HTTP method to match.
29
+ */
30
+
31
+ /**
32
+ * Represents a value object wrapping a normalized configuration value.
33
+ *
34
+ * @template {Record<string, unknown>|undefined} TValue
35
+ * @typedef {import('./Options.mjs').ValueObject<TValue>} ValueObject
36
+ */
37
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
38
+ /**
39
+ * Abstract base class for middleware configuration options.
40
+ *
41
+ * @abstract
42
+ * @template {Record<string, unknown>|undefined} TValue
43
+ * @augments Options<TValue>
44
+ */
45
+ class MiddlewareOption extends Options {
46
+ /**
47
+ * Creates a server middleware configuration option instance.
48
+ *
49
+ * @abstract
50
+ * @param {string} name - Option name.
51
+ * @param {ValueObject<TValue>} value - Normalized option value.
52
+ * @throws {TypeError} If MiddlewareOption is instantiated directly.
53
+ */
54
+ constructor(name, value) {
55
+ if (new.target === MiddlewareOption) {
56
+ throw new TypeError('MiddlewareOption is an abstract class and cannot be instantiated');
57
+ }
58
+
59
+ super('middleware', name, value.value);
60
+ }
61
+
62
+ /**
63
+ * The URL pathname pattern the middleware is scoped to.
64
+ *
65
+ * Defaults to `undefined`, meaning the middleware applies to all paths.
66
+ *
67
+ * @readonly
68
+ * @type {string|undefined}
69
+ */
70
+ get pathnamePattern() {
71
+ return undefined;
72
+ }
73
+
74
+ /**
75
+ * The HTTP method the middleware is scoped to.
76
+ *
77
+ * Defaults to `undefined`, meaning the middleware applies to all methods.
78
+ *
79
+ * @readonly
80
+ * @type {HTTPMethod|undefined}
81
+ */
82
+ get method() {
83
+ return undefined;
84
+ }
85
+
86
+ /**
87
+ * Composes the middleware configuration consumed by the exchange layer
88
+ * builder.
89
+ *
90
+ * Subclasses must implement this method.
91
+ *
92
+ * @abstract
93
+ * @returns {MiddlewareConfiguration} The composed middleware configuration.
94
+ * @throws {TypeError} If the subclass does not implement `compose`.
95
+ */
96
+ compose() {
97
+ throw new TypeError('MiddlewareOption.compose must be implemented by subclasses');
98
+ }
99
+ }
100
+
101
+ // ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
102
+ export default MiddlewareOption;
@@ -0,0 +1,105 @@
1
+ // @ts-check
2
+ /**
3
+ * @file Defines the abstract base for configuration options.
4
+ *
5
+ * @description Options provides the common contract shared by configuration
6
+ * option value objects. It cannot be instantiated directly.
7
+ *
8
+ * @module Options
9
+ */
10
+
11
+ // ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
12
+ /**
13
+ * Represents the kind of configuration option.
14
+ *
15
+ * @typedef {'server'|'middleware'} OptionType
16
+ */
17
+
18
+ /**
19
+ * Represents a value object wrapping a normalized configuration value.
20
+ *
21
+ * @template {Record<string, unknown>|undefined} TValue
22
+ * @typedef {{ value: TValue }} ValueObject
23
+ */
24
+
25
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
26
+ /**
27
+ * Abstract base class for configuration options.
28
+ *
29
+ * @template {Record<string, unknown>|undefined} TValue
30
+ * @abstract
31
+ */
32
+ class Options {
33
+ /**
34
+ * The option kind.
35
+ *
36
+ * @type {OptionType}
37
+ */
38
+ #type;
39
+
40
+ /**
41
+ * The option name.
42
+ *
43
+ * @type {string}
44
+ */
45
+ #name;
46
+
47
+ /**
48
+ * The option value.
49
+ *
50
+ * @type {TValue}
51
+ */
52
+ #value;
53
+
54
+ /**
55
+ * Creates a configuration option instance.
56
+ *
57
+ * @abstract
58
+ * @param {OptionType} type - Option kind.
59
+ * @param {string} name - Option name.
60
+ * @param {TValue} value - Normalized option value.
61
+ * @throws {TypeError} If Options is instantiated directly.
62
+ */
63
+ constructor(type, name, value) {
64
+ if (new.target === Options) {
65
+ throw new TypeError('Options is an abstract class and cannot be instantiated');
66
+ }
67
+
68
+ this.#type = type;
69
+ this.#name = name;
70
+ this.#value = value;
71
+ }
72
+
73
+ /**
74
+ * The option kind.
75
+ *
76
+ * @readonly
77
+ * @type {OptionType}
78
+ */
79
+ get type() {
80
+ return this.#type;
81
+ }
82
+
83
+ /**
84
+ * The option name.
85
+ *
86
+ * @readonly
87
+ * @type {string}
88
+ */
89
+ get name() {
90
+ return this.#name;
91
+ }
92
+
93
+ /**
94
+ * The option value.
95
+ *
96
+ * @readonly
97
+ * @type {TValue}
98
+ */
99
+ get value() {
100
+ return this.#value;
101
+ }
102
+ }
103
+
104
+ // ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
105
+ export default Options;
@@ -0,0 +1,164 @@
1
+ // @ts-check
2
+ /**
3
+ * @file Defines the RateLimitOptionsValue value object.
4
+ *
5
+ * @description Normalizes and validates rate limit middleware configuration
6
+ * used by RateLimitMiddleware. When options are omitted, the value is
7
+ * undefined and the middleware is not registered.
8
+ *
9
+ * @module RateLimitOptionsValue
10
+ */
11
+
12
+ // ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
13
+ // » IMPORT LOCAL MODULES
14
+ import { assertRateLimitOptions } from '#shared/validations/ServerConfiguration/RateLimitOptions';
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 rate limit options accepted by the server configuration.
25
+ *
26
+ * @typedef {object} RateLimitOptionsInput
27
+ * @property {number} [windowMs] - Duration of the rate-limit window in milliseconds.
28
+ * @property {number} [max] - Maximum number of requests allowed per window per client.
29
+ * @property {number} [statusCode] - HTTP status code returned when the limit is exceeded.
30
+ * @property {Record<string, unknown>} [message] - JSON body sent when the limit is exceeded.
31
+ * @property {boolean} [standardHeaders] - Whether to send the modern `RateLimit-*` headers.
32
+ * @property {boolean} [legacyHeaders] - Whether to send the legacy `X-RateLimit-*` headers.
33
+ */
34
+
35
+ /**
36
+ * Represents the normalized rate limit options.
37
+ *
38
+ * @typedef {object} RateLimitOptionsData
39
+ * @property {number} windowMs - Duration of the rate-limit window in milliseconds.
40
+ * @property {number} max - Maximum number of requests allowed per window per client.
41
+ * @property {number} statusCode - HTTP status code returned when the limit is exceeded.
42
+ * @property {Readonly<Record<string, unknown>>} message - JSON body sent when the limit is exceeded.
43
+ * @property {boolean} standardHeaders - Whether to send the modern `RateLimit-*` headers.
44
+ * @property {boolean} legacyHeaders - Whether to send the legacy `X-RateLimit-*` headers.
45
+ */
46
+
47
+ // ━━ CONSTANTS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
48
+ /**
49
+ * Default duration of the rate-limit window in milliseconds.
50
+ *
51
+ * @type {number}
52
+ */
53
+ const DEFAULT_WINDOW_MS = 60_000;
54
+
55
+ /**
56
+ * Default maximum number of requests allowed per window per client.
57
+ *
58
+ * @type {number}
59
+ */
60
+ const DEFAULT_MAX = 100;
61
+
62
+ /**
63
+ * Default HTTP status code returned when the limit is exceeded.
64
+ *
65
+ * @type {number}
66
+ */
67
+ const DEFAULT_STATUS_CODE = 429;
68
+
69
+ /**
70
+ * Default JSON body sent when the limit is exceeded.
71
+ *
72
+ * @type {Readonly<Record<string, unknown>>}
73
+ */
74
+ const DEFAULT_MESSAGE = Object.freeze({ error: 'Too Many Requests' });
75
+
76
+ /**
77
+ * Default behavior for modern `RateLimit-*` headers.
78
+ *
79
+ * @type {boolean}
80
+ */
81
+ const DEFAULT_STANDARD_HEADERS = true;
82
+
83
+ /**
84
+ * Default behavior for legacy `X-RateLimit-*` headers.
85
+ *
86
+ * @type {boolean}
87
+ */
88
+ const DEFAULT_LEGACY_HEADERS = false;
89
+
90
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
91
+ /**
92
+ * Immutable value object representing normalized rate limit middleware
93
+ * options.
94
+ *
95
+ * @classdesc Validates and normalizes rate limit middleware options. When
96
+ * options are provided, defaults are applied for any omitted property. When
97
+ * options are omitted, the value is undefined.
98
+ *
99
+ * @example
100
+ * ```js
101
+ * const options = new RateLimitOptionsValue({ windowMs: 60_000, max: 100 });
102
+ *
103
+ * options.value;
104
+ * // => {
105
+ * // windowMs: 60_000,
106
+ * // max: 100,
107
+ * // statusCode: 429,
108
+ * // message: { error: 'Too Many Requests' },
109
+ * // standardHeaders: true,
110
+ * // legacyHeaders: false,
111
+ * // }
112
+ * ```
113
+ *
114
+ * @example
115
+ * ```js
116
+ * const options = new RateLimitOptionsValue();
117
+ *
118
+ * options.value;
119
+ * // => undefined
120
+ * ```
121
+ */
122
+ class RateLimitOptionsValue {
123
+ /**
124
+ * The normalized rate limit options.
125
+ *
126
+ * @type {Readonly<RateLimitOptionsData>|undefined}
127
+ */
128
+ #value;
129
+
130
+ /**
131
+ * Creates a new {@link RateLimitOptionsValue}.
132
+ *
133
+ * @param {RateLimitOptionsInput} [options] - The rate limit middleware options.
134
+ * @throws {ServerConfigurationError} If `options` is invalid.
135
+ */
136
+ constructor(options) {
137
+ assertRateLimitOptions(options);
138
+
139
+ this.#value =
140
+ options === undefined
141
+ ? options
142
+ : Object.freeze({
143
+ windowMs: options.windowMs ?? DEFAULT_WINDOW_MS,
144
+ max: options.max ?? DEFAULT_MAX,
145
+ statusCode: options.statusCode ?? DEFAULT_STATUS_CODE,
146
+ message: Object.freeze({ ...(options.message ?? DEFAULT_MESSAGE) }),
147
+ standardHeaders: options.standardHeaders ?? DEFAULT_STANDARD_HEADERS,
148
+ legacyHeaders: options.legacyHeaders ?? DEFAULT_LEGACY_HEADERS,
149
+ });
150
+ }
151
+
152
+ /**
153
+ * The normalized rate limit options.
154
+ *
155
+ * @readonly
156
+ * @type {Readonly<RateLimitOptionsData>|undefined}
157
+ */
158
+ get value() {
159
+ return this.#value;
160
+ }
161
+ }
162
+
163
+ // ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
164
+ export default RateLimitOptionsValue;
@@ -0,0 +1,84 @@
1
+ // @ts-check
2
+ /**
3
+ * @file Defines the RateLimitOptions middleware configuration option.
4
+ *
5
+ * @description RateLimitOptions wraps a normalized RateLimitOptionsValue into
6
+ * the middleware configuration option hierarchy.
7
+ *
8
+ * @module RateLimitOptions
9
+ */
10
+
11
+ // ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
12
+ // » IMPORT LOCAL MODULES
13
+ import MiddlewareOption from '../MiddlewareOption.mjs';
14
+ import RateLimitOptionsValue from './RateLimitOptionsValue.mjs';
15
+
16
+ // ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
17
+ /**
18
+ * Represents the rate limit options accepted by the server configuration.
19
+ *
20
+ * @typedef {import('./RateLimitOptionsValue.mjs').RateLimitOptionsInput} RateLimitOptionsInput
21
+ */
22
+
23
+ /**
24
+ * Represents the normalized rate limit options.
25
+ *
26
+ * @typedef {import('./RateLimitOptionsValue.mjs').RateLimitOptionsData} RateLimitOptionsData
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 rate limit
38
+ * configuration.
39
+ *
40
+ * @augments {MiddlewareOption<RateLimitOptionsData|undefined>}
41
+ */
42
+ class RateLimitOptions extends MiddlewareOption {
43
+ /**
44
+ * Creates a new {@link RateLimitOptions}.
45
+ *
46
+ * @param {RateLimitOptionsInput} [options] - The rate limit middleware options.
47
+ * @example
48
+ * ```js
49
+ * const rateLimitOptions = new RateLimitOptions({
50
+ * windowMs: 60_000,
51
+ * max: 100,
52
+ * });
53
+ * ```
54
+ */
55
+ constructor(options) {
56
+ super('rateLimit', new RateLimitOptionsValue(options));
57
+ }
58
+
59
+ /**
60
+ * Composes the rate limit middleware configuration consumed by the
61
+ * exchange layer builder.
62
+ *
63
+ * Rate limiting is a cross-cutting concern applied to every request
64
+ * regardless of pathname or method, so both `pathnamePattern` and `method`
65
+ * are `undefined`.
66
+ *
67
+ * @returns {MiddlewareConfiguration} The composed middleware configuration.
68
+ * @example
69
+ * ```js
70
+ * const configuration = rateLimitOptions.compose();
71
+ * ```
72
+ */
73
+ compose() {
74
+ return {
75
+ name: this.name,
76
+ options: this.value,
77
+ pathnamePattern: undefined,
78
+ method: undefined,
79
+ };
80
+ }
81
+ }
82
+
83
+ // ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
84
+ export default RateLimitOptions;
@@ -0,0 +1,108 @@
1
+ // @ts-check
2
+ /**
3
+ * @file Defines the RequestOptionsValue value object.
4
+ *
5
+ * @description Normalizes and validates request configuration used by
6
+ * ServerConfiguration. When options are omitted, the value is undefined.
7
+ *
8
+ * @module RequestOptionsValue
9
+ */
10
+
11
+ // ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
12
+ // » IMPORT LOCAL MODULES
13
+ import { assertRequestOptions } from '#shared/validations/ServerConfiguration/RequestOptions';
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
+ * Whether (and how much) to trust proxy headers.
24
+ *
25
+ * @typedef {boolean|number} TrustProxyOption
26
+ */
27
+
28
+ /**
29
+ * Represents the request options accepted by the server configuration.
30
+ *
31
+ * @typedef {object} RequestOptionsInput
32
+ * @property {number} [bodyLimit] - Maximum number of bytes allowed for the request body.
33
+ * @property {TrustProxyOption} [trustProxy] - Whether (and how much) to trust proxy headers.
34
+ */
35
+
36
+ /**
37
+ * Represents the normalized request options.
38
+ *
39
+ * @typedef {object} RequestOptionsData
40
+ * @property {number} [bodyLimit] - Maximum number of bytes allowed for the request body.
41
+ * @property {TrustProxyOption} [trustProxy] - Whether (and how much) to trust proxy headers.
42
+ */
43
+
44
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
45
+ /**
46
+ * Immutable value object representing normalized request options.
47
+ *
48
+ * @classdesc Validates and normalizes request options for the server.
49
+ * When options are omitted, the value is undefined.
50
+ */
51
+ class RequestOptionsValue {
52
+ /**
53
+ * Normalized request options.
54
+ *
55
+ * @type {Readonly<RequestOptionsData>|undefined}
56
+ */
57
+ #value;
58
+
59
+ /**
60
+ * Creates a new {@link RequestOptionsValue}.
61
+ *
62
+ * @param {RequestOptionsInput} [options] - The request options.
63
+ * @throws {ServerConfigurationError} If `options` is not a plain object.
64
+ * @throws {ServerConfigurationError} If `bodyLimit` is defined and is not a number.
65
+ * @throws {ServerConfigurationError} If `bodyLimit` is defined and is not a positive safe integer.
66
+ * @throws {ServerConfigurationError} If `trustProxy` is defined and is not a boolean or number.
67
+ * @throws {ServerConfigurationError} If numeric `trustProxy` is defined and is negative or not an integer.
68
+ * @example
69
+ * ```js
70
+ * const options = new RequestOptionsValue({ bodyLimit: 1_000_000, trustProxy: 1 });
71
+ *
72
+ * options.value;
73
+ * // => { bodyLimit: 1_000_000, trustProxy: 1 }
74
+ * ```
75
+ *
76
+ * @example
77
+ * ```js
78
+ * const options = new RequestOptionsValue();
79
+ *
80
+ * options.value;
81
+ * // => undefined
82
+ * ```
83
+ */
84
+ constructor(options) {
85
+ assertRequestOptions(options);
86
+
87
+ this.#value =
88
+ options === undefined
89
+ ? options
90
+ : Object.freeze({
91
+ bodyLimit: options.bodyLimit,
92
+ trustProxy: options.trustProxy,
93
+ });
94
+ }
95
+
96
+ /**
97
+ * The request options.
98
+ *
99
+ * @readonly
100
+ * @type {Readonly<RequestOptionsData>|undefined}
101
+ */
102
+ get value() {
103
+ return this.#value;
104
+ }
105
+ }
106
+
107
+ // ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
108
+ export default RequestOptionsValue;
@@ -0,0 +1,47 @@
1
+ // @ts-check
2
+ /**
3
+ * @file Defines the RequestOptions server configuration option.
4
+ *
5
+ * @description RequestOptions wraps a normalized RequestOptionsValue into the
6
+ * server configuration option hierarchy.
7
+ *
8
+ * @module RequestOptions
9
+ */
10
+
11
+ // ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
12
+ // » IMPORT LOCAL MODULES
13
+ import ServerOption from '../ServerOption.mjs';
14
+ import RequestOptionsValue from './RequestOptionsValue.mjs';
15
+
16
+ // ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
17
+ /**
18
+ * Represents the request options accepted by the server configuration.
19
+ *
20
+ * @typedef {import('./RequestOptionsValue.mjs').RequestOptionsInput} RequestOptionsInput
21
+ */
22
+
23
+ /**
24
+ * Represents the normalized request options.
25
+ *
26
+ * @typedef {import('./RequestOptionsValue.mjs').RequestOptionsData} RequestOptionsData
27
+ */
28
+
29
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
30
+ /**
31
+ * Server configuration option wrapping a normalized request configuration.
32
+ *
33
+ * @augments {ServerOption<RequestOptionsData|undefined>}
34
+ */
35
+ class RequestOptions extends ServerOption {
36
+ /**
37
+ * Creates a new {@link RequestOptions}.
38
+ *
39
+ * @param {RequestOptionsInput} [options] - The request options.
40
+ */
41
+ constructor(options) {
42
+ super('request', new RequestOptionsValue(options));
43
+ }
44
+ }
45
+
46
+ // ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
47
+ export default RequestOptions;