@boo-labs/server 1.0.1 → 1.0.2

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 (24) hide show
  1. package/package.json +1 -1
  2. package/source/core/IPCMain/IPCProtocol.mjs +80 -75
  3. package/source/shared/domain/UUIDV7/index.mjs +31 -39
  4. package/source/shared/errors/ServerError/index.mjs +48 -2
  5. package/source/shared/outcome/Result/index.mjs +77 -130
  6. package/source/shared/outcome/Validation/index.mjs +56 -114
  7. package/source/shared/validations/Middleware/index.mjs +32 -23
  8. package/source/shared/validations/PathnamePattern/index.mjs +38 -39
  9. package/source/shared/validations/Server/NotFoundHandler/index.mjs +25 -15
  10. package/source/shared/validations/Server/Router/index.mjs +24 -14
  11. package/source/shared/validations/Server/ServerConfiguration/index.mjs +24 -14
  12. package/source/shared/validations/Server/SystemErrorHandler/index.mjs +25 -15
  13. package/source/shared/validations/ServerConfiguration/CORSOptions/index.mjs +95 -91
  14. package/source/shared/validations/ServerConfiguration/CSRFOptions/index.mjs +96 -96
  15. package/source/shared/validations/ServerConfiguration/CompressOptions/index.mjs +55 -43
  16. package/source/shared/validations/ServerConfiguration/CookieParserOptions/index.mjs +28 -13
  17. package/source/shared/validations/ServerConfiguration/CookieSessionOptions/index.mjs +101 -111
  18. package/source/shared/validations/ServerConfiguration/ListenOptions/index.mjs +55 -43
  19. package/source/shared/validations/ServerConfiguration/RateLimitOptions/index.mjs +71 -61
  20. package/source/shared/validations/ServerConfiguration/RequestOptions/index.mjs +44 -31
  21. package/source/shared/validations/ServerConfiguration/ResponseOptions/index.mjs +39 -25
  22. package/source/shared/validations/ServerConfiguration/SecurityHeadersOptions/index.mjs +27 -14
  23. package/source/shared/validations/ServerConfiguration/StaticOptions/index.mjs +85 -84
  24. package/source/shared/outcome/ErrorSchema.mjs +0 -361
@@ -14,6 +14,20 @@
14
14
  import Validation from '#shared/outcome/Validation';
15
15
  import ServerConfigurationError from '#shared/errors/ServerConfigurationError';
16
16
 
17
+ // ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
18
+ /**
19
+ * A successful {@link Validation} instance.
20
+ *
21
+ * @typedef {import('#shared/outcome/Validation').SuccessValidation} SuccessValidation
22
+ */
23
+
24
+ /**
25
+ * A failed {@link Validation} instance.
26
+ *
27
+ * @template {Error} E
28
+ * @typedef {import('#shared/outcome/Validation').FailureValidation<E>} FailureValidation
29
+ */
30
+
17
31
  // ━━ CONSTANTS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
18
32
  /**
19
33
  * Property names recognized as security header options.
@@ -50,6 +64,7 @@ function isPlainObject(value) {
50
64
  return typeof value === 'object' && value !== null && !Array.isArray(value);
51
65
  }
52
66
 
67
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
53
68
  /**
54
69
  * Validates security headers options.
55
70
  *
@@ -63,7 +78,8 @@ function isPlainObject(value) {
63
78
  * Unknown properties are ignored.
64
79
  *
65
80
  * @param {unknown} options - Security headers options to validate.
66
- * @returns {Validation} Validation result.
81
+ * @returns {SuccessValidation|FailureValidation<ServerConfigurationError>} Validation result.
82
+ *
67
83
  * @example
68
84
  * ```js
69
85
  * const validation = validateSecurityHeadersOptions({ xFrameOptions: 'DENY' });
@@ -86,12 +102,11 @@ export function validateSecurityHeadersOptions(options) {
86
102
  }
87
103
 
88
104
  if (!isPlainObject(options)) {
89
- return Validation.failure({
90
- error: ServerConfigurationError,
91
- code: 'INVALID_SECURITY_HEADERS_OPTIONS',
92
- custom: true,
93
- options: { values: { received: typeof options } },
94
- });
105
+ return Validation.failure(
106
+ new ServerConfigurationError('INVALID_SECURITY_HEADERS_OPTIONS', {
107
+ values: { received: typeof options },
108
+ }),
109
+ );
95
110
  }
96
111
 
97
112
  for (const key of SECURITY_HEADER_KEYS) {
@@ -102,15 +117,12 @@ export function validateSecurityHeadersOptions(options) {
102
117
  const value = options[key];
103
118
 
104
119
  if (typeof value !== 'string' && value !== false) {
105
- return Validation.failure({
106
- error: ServerConfigurationError,
107
- code: 'INVALID_SECURITY_HEADERS_OPTION_TYPE',
108
- custom: true,
109
- options: {
120
+ return Validation.failure(
121
+ new ServerConfigurationError('INVALID_SECURITY_HEADERS_OPTION_TYPE', {
110
122
  values: { key, received: typeof value },
111
123
  details: { key, value },
112
- },
113
- });
124
+ }),
125
+ );
114
126
  }
115
127
  }
116
128
 
@@ -123,6 +135,7 @@ export function validateSecurityHeadersOptions(options) {
123
135
  * @param {unknown} options - Security headers options to validate.
124
136
  * @throws {ServerConfigurationError} If `options` is not a plain object.
125
137
  * @throws {ServerConfigurationError} If a recognized security header option is not a string or `false`.
138
+ *
126
139
  * @example
127
140
  * ```js
128
141
  * assertSecurityHeadersOptions({ xFrameOptions: 'DENY' });
@@ -14,6 +14,20 @@
14
14
  import Validation from '#shared/outcome/Validation';
15
15
  import ServerConfigurationError from '#shared/errors/ServerConfigurationError';
16
16
 
17
+ // ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
18
+ /**
19
+ * A successful {@link Validation} instance.
20
+ *
21
+ * @typedef {import('#shared/outcome/Validation').SuccessValidation} SuccessValidation
22
+ */
23
+
24
+ /**
25
+ * A failed {@link Validation} instance.
26
+ *
27
+ * @template {Error} E
28
+ * @typedef {import('#shared/outcome/Validation').FailureValidation<E>} FailureValidation
29
+ */
30
+
17
31
  // ━━ FUNCTIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
18
32
  /**
19
33
  * Determines whether a value is a non-null plain object.
@@ -26,6 +40,7 @@ function isPlainObject(value) {
26
40
  return typeof value === 'object' && value !== null && !Array.isArray(value);
27
41
  }
28
42
 
43
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
29
44
  /**
30
45
  * Validates static middleware options.
31
46
  *
@@ -43,7 +58,8 @@ function isPlainObject(value) {
43
58
  * 10. `dotfiles`, when present, must be a boolean.
44
59
  *
45
60
  * @param {unknown} options - Static options to validate.
46
- * @returns {Validation} Validation result.
61
+ * @returns {SuccessValidation|FailureValidation<ServerConfigurationError>} Validation result.
62
+ *
47
63
  * @example
48
64
  * ```js
49
65
  * const validation = validateStaticOptions({ root: '/var/www/public' });
@@ -66,41 +82,34 @@ export function validateStaticOptions(options) {
66
82
  }
67
83
 
68
84
  if (!isPlainObject(options)) {
69
- return Validation.failure({
70
- error: ServerConfigurationError,
71
- code: 'INVALID_STATIC_OPTIONS',
72
- custom: true,
73
- options: { values: { received: typeof options } },
74
- });
85
+ return Validation.failure(
86
+ new ServerConfigurationError('INVALID_STATIC_OPTIONS', {
87
+ values: { received: typeof options },
88
+ }),
89
+ );
75
90
  }
76
91
 
77
92
  if ('root' in options === false) {
78
- return Validation.failure({
79
- error: ServerConfigurationError,
80
- code: 'MISSING_STATIC_ROOT',
81
- custom: true,
82
- });
93
+ return Validation.failure(new ServerConfigurationError('MISSING_STATIC_ROOT'));
83
94
  }
84
95
 
85
96
  if ('root' in options) {
86
97
  const { root } = options;
87
98
 
88
99
  if (typeof root !== 'string') {
89
- return Validation.failure({
90
- error: ServerConfigurationError,
91
- code: 'INVALID_STATIC_ROOT_TYPE',
92
- custom: true,
93
- options: { values: { received: typeof root } },
94
- });
100
+ return Validation.failure(
101
+ new ServerConfigurationError('INVALID_STATIC_ROOT_TYPE', {
102
+ values: { received: typeof root },
103
+ }),
104
+ );
95
105
  }
96
106
 
97
107
  if (root.length === 0) {
98
- return Validation.failure({
99
- error: ServerConfigurationError,
100
- code: 'INVALID_STATIC_ROOT_VALUE',
101
- custom: true,
102
- options: { details: { root } },
103
- });
108
+ return Validation.failure(
109
+ new ServerConfigurationError('INVALID_STATIC_ROOT_VALUE', {
110
+ details: { root },
111
+ }),
112
+ );
104
113
  }
105
114
  }
106
115
 
@@ -108,22 +117,20 @@ export function validateStaticOptions(options) {
108
117
  const { index } = options;
109
118
 
110
119
  if (!Array.isArray(index)) {
111
- return Validation.failure({
112
- error: ServerConfigurationError,
113
- code: 'INVALID_STATIC_INDEX_TYPE',
114
- custom: true,
115
- options: { values: { received: typeof index } },
116
- });
120
+ return Validation.failure(
121
+ new ServerConfigurationError('INVALID_STATIC_INDEX_TYPE', {
122
+ values: { received: typeof index },
123
+ }),
124
+ );
117
125
  }
118
126
 
119
127
  for (const item of index) {
120
128
  if (typeof item !== 'string' || item.length === 0) {
121
- return Validation.failure({
122
- error: ServerConfigurationError,
123
- code: 'INVALID_STATIC_INDEX_ITEM',
124
- custom: true,
125
- options: { details: { item } },
126
- });
129
+ return Validation.failure(
130
+ new ServerConfigurationError('INVALID_STATIC_INDEX_ITEM', {
131
+ details: { item },
132
+ }),
133
+ );
127
134
  }
128
135
  }
129
136
  }
@@ -132,54 +139,50 @@ export function validateStaticOptions(options) {
132
139
  const { maxAge } = options;
133
140
 
134
141
  if (typeof maxAge !== 'number') {
135
- return Validation.failure({
136
- error: ServerConfigurationError,
137
- code: 'INVALID_STATIC_MAX_AGE_TYPE',
138
- custom: true,
139
- options: { values: { received: typeof maxAge } },
140
- });
142
+ return Validation.failure(
143
+ new ServerConfigurationError('INVALID_STATIC_MAX_AGE_TYPE', {
144
+ values: { received: typeof maxAge },
145
+ }),
146
+ );
141
147
  }
142
148
 
143
149
  if (!Number.isInteger(maxAge) || maxAge < 0) {
144
- return Validation.failure({
145
- error: ServerConfigurationError,
146
- code: 'INVALID_STATIC_MAX_AGE_VALUE',
147
- custom: true,
148
- options: { details: { maxAge } },
149
- });
150
+ return Validation.failure(
151
+ new ServerConfigurationError('INVALID_STATIC_MAX_AGE_VALUE', {
152
+ values: { maxAge },
153
+ details: { maxAge },
154
+ }),
155
+ );
150
156
  }
151
157
  }
152
158
 
153
159
  if ('etag' in options) {
154
160
  if (typeof options.etag !== 'boolean') {
155
- return Validation.failure({
156
- error: ServerConfigurationError,
157
- code: 'INVALID_STATIC_ETAG_TYPE',
158
- custom: true,
159
- options: { values: { received: typeof options.etag } },
160
- });
161
+ return Validation.failure(
162
+ new ServerConfigurationError('INVALID_STATIC_ETAG_TYPE', {
163
+ values: { received: typeof options.etag },
164
+ }),
165
+ );
161
166
  }
162
167
  }
163
168
 
164
169
  if ('lastModified' in options) {
165
170
  if (typeof options.lastModified !== 'boolean') {
166
- return Validation.failure({
167
- error: ServerConfigurationError,
168
- code: 'INVALID_STATIC_LAST_MODIFIED_TYPE',
169
- custom: true,
170
- options: { values: { received: typeof options.lastModified } },
171
- });
171
+ return Validation.failure(
172
+ new ServerConfigurationError('INVALID_STATIC_LAST_MODIFIED_TYPE', {
173
+ values: { received: typeof options.lastModified },
174
+ }),
175
+ );
172
176
  }
173
177
  }
174
178
 
175
179
  if ('fallthrough' in options) {
176
180
  if (typeof options.fallthrough !== 'boolean') {
177
- return Validation.failure({
178
- error: ServerConfigurationError,
179
- code: 'INVALID_STATIC_FALLTHROUGH_TYPE',
180
- custom: true,
181
- options: { values: { received: typeof options.fallthrough } },
182
- });
181
+ return Validation.failure(
182
+ new ServerConfigurationError('INVALID_STATIC_FALLTHROUGH_TYPE', {
183
+ values: { received: typeof options.fallthrough },
184
+ }),
185
+ );
183
186
  }
184
187
  }
185
188
 
@@ -187,34 +190,31 @@ export function validateStaticOptions(options) {
187
190
  const { extensions } = options;
188
191
 
189
192
  if (!Array.isArray(extensions)) {
190
- return Validation.failure({
191
- error: ServerConfigurationError,
192
- code: 'INVALID_STATIC_EXTENSIONS_TYPE',
193
- custom: true,
194
- options: { values: { received: typeof extensions } },
195
- });
193
+ return Validation.failure(
194
+ new ServerConfigurationError('INVALID_STATIC_EXTENSIONS_TYPE', {
195
+ values: { received: typeof extensions },
196
+ }),
197
+ );
196
198
  }
197
199
 
198
200
  for (const item of extensions) {
199
201
  if (typeof item !== 'string' || item.length === 0) {
200
- return Validation.failure({
201
- error: ServerConfigurationError,
202
- code: 'INVALID_STATIC_EXTENSIONS_ITEM',
203
- custom: true,
204
- options: { details: { item } },
205
- });
202
+ return Validation.failure(
203
+ new ServerConfigurationError('INVALID_STATIC_EXTENSIONS_ITEM', {
204
+ details: { item },
205
+ }),
206
+ );
206
207
  }
207
208
  }
208
209
  }
209
210
 
210
211
  if ('dotfiles' in options) {
211
212
  if (typeof options.dotfiles !== 'boolean') {
212
- return Validation.failure({
213
- error: ServerConfigurationError,
214
- code: 'INVALID_STATIC_DOTFILES_TYPE',
215
- custom: true,
216
- options: { values: { received: typeof options.dotfiles } },
217
- });
213
+ return Validation.failure(
214
+ new ServerConfigurationError('INVALID_STATIC_DOTFILES_TYPE', {
215
+ values: { received: typeof options.dotfiles },
216
+ }),
217
+ );
218
218
  }
219
219
  }
220
220
 
@@ -234,6 +234,7 @@ export function validateStaticOptions(options) {
234
234
  * @throws {ServerConfigurationError} If `fallthrough` is not a boolean.
235
235
  * @throws {ServerConfigurationError} If `extensions` is not an array of non-empty strings.
236
236
  * @throws {ServerConfigurationError} If `dotfiles` is not a boolean.
237
+ *
237
238
  * @example
238
239
  * ```js
239
240
  * assertStaticOptions({ root: '/var/www/public' });