@boo-labs/server 1.0.0 → 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 (26) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +125 -67
  3. package/package.json +1 -1
  4. package/source/core/IPCMain/IPCProtocol.mjs +80 -75
  5. package/source/shared/domain/UUIDV7/index.mjs +31 -39
  6. package/source/shared/errors/ServerError/index.mjs +48 -2
  7. package/source/shared/outcome/Result/index.mjs +77 -130
  8. package/source/shared/outcome/Validation/index.mjs +56 -114
  9. package/source/shared/validations/Middleware/index.mjs +32 -23
  10. package/source/shared/validations/PathnamePattern/index.mjs +38 -39
  11. package/source/shared/validations/Server/NotFoundHandler/index.mjs +25 -15
  12. package/source/shared/validations/Server/Router/index.mjs +24 -14
  13. package/source/shared/validations/Server/ServerConfiguration/index.mjs +24 -14
  14. package/source/shared/validations/Server/SystemErrorHandler/index.mjs +25 -15
  15. package/source/shared/validations/ServerConfiguration/CORSOptions/index.mjs +95 -91
  16. package/source/shared/validations/ServerConfiguration/CSRFOptions/index.mjs +96 -96
  17. package/source/shared/validations/ServerConfiguration/CompressOptions/index.mjs +55 -43
  18. package/source/shared/validations/ServerConfiguration/CookieParserOptions/index.mjs +28 -13
  19. package/source/shared/validations/ServerConfiguration/CookieSessionOptions/index.mjs +101 -111
  20. package/source/shared/validations/ServerConfiguration/ListenOptions/index.mjs +55 -43
  21. package/source/shared/validations/ServerConfiguration/RateLimitOptions/index.mjs +71 -61
  22. package/source/shared/validations/ServerConfiguration/RequestOptions/index.mjs +44 -31
  23. package/source/shared/validations/ServerConfiguration/ResponseOptions/index.mjs +39 -25
  24. package/source/shared/validations/ServerConfiguration/SecurityHeadersOptions/index.mjs +27 -14
  25. package/source/shared/validations/ServerConfiguration/StaticOptions/index.mjs +85 -84
  26. 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
  * Minimum valid HTTP status code for a successful preflight response.
@@ -65,6 +79,7 @@ function everyNonEmptyString(values) {
65
79
  return values.every(isNonEmptyString);
66
80
  }
67
81
 
82
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
68
83
  /**
69
84
  * Validates CORS options.
70
85
  *
@@ -82,7 +97,8 @@ function everyNonEmptyString(values) {
82
97
  * 10. `optionsSuccessStatus`, if present, must be an integer between 100 and 599.
83
98
  *
84
99
  * @param {unknown} options - CORS options to validate.
85
- * @returns {Validation} Validation result.
100
+ * @returns {SuccessValidation|FailureValidation<ServerConfigurationError>} Validation result.
101
+ *
86
102
  * @example
87
103
  * ```js
88
104
  * const validation = validateCORSOptions({ origin: true });
@@ -105,12 +121,11 @@ export function validateCORSOptions(options) {
105
121
  }
106
122
 
107
123
  if (!isPlainObject(options)) {
108
- return Validation.failure({
109
- error: ServerConfigurationError,
110
- code: 'INVALID_CORS_OPTIONS',
111
- custom: true,
112
- options: { values: { received: typeof options } },
113
- });
124
+ return Validation.failure(
125
+ new ServerConfigurationError('INVALID_CORS_OPTIONS', {
126
+ values: { received: typeof options },
127
+ }),
128
+ );
114
129
  }
115
130
 
116
131
  if ('origin' in options) {
@@ -123,20 +138,18 @@ export function validateCORSOptions(options) {
123
138
 
124
139
  if (!isValidOriginType) {
125
140
  if (Array.isArray(origin)) {
126
- return Validation.failure({
127
- error: ServerConfigurationError,
128
- code: 'INVALID_CORS_ORIGIN_ITEM',
129
- custom: true,
130
- options: { details: { origin } },
131
- });
141
+ return Validation.failure(
142
+ new ServerConfigurationError('INVALID_CORS_ORIGIN_ITEM', {
143
+ details: { origin },
144
+ }),
145
+ );
132
146
  }
133
147
 
134
- return Validation.failure({
135
- error: ServerConfigurationError,
136
- code: 'INVALID_CORS_ORIGIN_TYPE',
137
- custom: true,
138
- options: { values: { received: typeof origin } },
139
- });
148
+ return Validation.failure(
149
+ new ServerConfigurationError('INVALID_CORS_ORIGIN_TYPE', {
150
+ values: { received: typeof origin },
151
+ }),
152
+ );
140
153
  }
141
154
  }
142
155
 
@@ -144,21 +157,19 @@ export function validateCORSOptions(options) {
144
157
  const { methods } = options;
145
158
 
146
159
  if (!Array.isArray(methods)) {
147
- return Validation.failure({
148
- error: ServerConfigurationError,
149
- code: 'INVALID_CORS_METHODS_TYPE',
150
- custom: true,
151
- options: { values: { received: typeof methods } },
152
- });
160
+ return Validation.failure(
161
+ new ServerConfigurationError('INVALID_CORS_METHODS_TYPE', {
162
+ values: { received: typeof methods },
163
+ }),
164
+ );
153
165
  }
154
166
 
155
167
  if (!everyNonEmptyString(methods)) {
156
- return Validation.failure({
157
- error: ServerConfigurationError,
158
- code: 'INVALID_CORS_METHODS_ITEM',
159
- custom: true,
160
- options: { details: { methods } },
161
- });
168
+ return Validation.failure(
169
+ new ServerConfigurationError('INVALID_CORS_METHODS_ITEM', {
170
+ details: { methods },
171
+ }),
172
+ );
162
173
  }
163
174
  }
164
175
 
@@ -166,21 +177,19 @@ export function validateCORSOptions(options) {
166
177
  const { allowedHeaders } = options;
167
178
 
168
179
  if (!Array.isArray(allowedHeaders)) {
169
- return Validation.failure({
170
- error: ServerConfigurationError,
171
- code: 'INVALID_CORS_ALLOWED_HEADERS_TYPE',
172
- custom: true,
173
- options: { values: { received: typeof allowedHeaders } },
174
- });
180
+ return Validation.failure(
181
+ new ServerConfigurationError('INVALID_CORS_ALLOWED_HEADERS_TYPE', {
182
+ values: { received: typeof allowedHeaders },
183
+ }),
184
+ );
175
185
  }
176
186
 
177
187
  if (!everyNonEmptyString(allowedHeaders)) {
178
- return Validation.failure({
179
- error: ServerConfigurationError,
180
- code: 'INVALID_CORS_ALLOWED_HEADERS_ITEM',
181
- custom: true,
182
- options: { details: { allowedHeaders } },
183
- });
188
+ return Validation.failure(
189
+ new ServerConfigurationError('INVALID_CORS_ALLOWED_HEADERS_ITEM', {
190
+ details: { allowedHeaders },
191
+ }),
192
+ );
184
193
  }
185
194
  }
186
195
 
@@ -188,21 +197,19 @@ export function validateCORSOptions(options) {
188
197
  const { exposedHeaders } = options;
189
198
 
190
199
  if (!Array.isArray(exposedHeaders)) {
191
- return Validation.failure({
192
- error: ServerConfigurationError,
193
- code: 'INVALID_CORS_EXPOSED_HEADERS_TYPE',
194
- custom: true,
195
- options: { values: { received: typeof exposedHeaders } },
196
- });
200
+ return Validation.failure(
201
+ new ServerConfigurationError('INVALID_CORS_EXPOSED_HEADERS_TYPE', {
202
+ values: { received: typeof exposedHeaders },
203
+ }),
204
+ );
197
205
  }
198
206
 
199
207
  if (!everyNonEmptyString(exposedHeaders)) {
200
- return Validation.failure({
201
- error: ServerConfigurationError,
202
- code: 'INVALID_CORS_EXPOSED_HEADERS_ITEM',
203
- custom: true,
204
- options: { details: { exposedHeaders } },
205
- });
208
+ return Validation.failure(
209
+ new ServerConfigurationError('INVALID_CORS_EXPOSED_HEADERS_ITEM', {
210
+ details: { exposedHeaders },
211
+ }),
212
+ );
206
213
  }
207
214
  }
208
215
 
@@ -210,12 +217,11 @@ export function validateCORSOptions(options) {
210
217
  const { credentials } = options;
211
218
 
212
219
  if (typeof credentials !== 'boolean') {
213
- return Validation.failure({
214
- error: ServerConfigurationError,
215
- code: 'INVALID_CORS_CREDENTIALS_TYPE',
216
- custom: true,
217
- options: { values: { received: typeof credentials } },
218
- });
220
+ return Validation.failure(
221
+ new ServerConfigurationError('INVALID_CORS_CREDENTIALS_TYPE', {
222
+ values: { received: typeof credentials },
223
+ }),
224
+ );
219
225
  }
220
226
  }
221
227
 
@@ -223,21 +229,20 @@ export function validateCORSOptions(options) {
223
229
  const { maxAge } = options;
224
230
 
225
231
  if (typeof maxAge !== 'number') {
226
- return Validation.failure({
227
- error: ServerConfigurationError,
228
- code: 'INVALID_CORS_MAX_AGE_TYPE',
229
- custom: true,
230
- options: { values: { received: typeof maxAge } },
231
- });
232
+ return Validation.failure(
233
+ new ServerConfigurationError('INVALID_CORS_MAX_AGE_TYPE', {
234
+ values: { received: typeof maxAge },
235
+ }),
236
+ );
232
237
  }
233
238
 
234
239
  if (!Number.isInteger(maxAge) || maxAge < 0) {
235
- return Validation.failure({
236
- error: ServerConfigurationError,
237
- code: 'INVALID_CORS_MAX_AGE_RANGE',
238
- custom: true,
239
- options: { details: { maxAge } },
240
- });
240
+ return Validation.failure(
241
+ new ServerConfigurationError('INVALID_CORS_MAX_AGE_RANGE', {
242
+ values: { maxAge },
243
+ details: { maxAge },
244
+ }),
245
+ );
241
246
  }
242
247
  }
243
248
 
@@ -245,12 +250,11 @@ export function validateCORSOptions(options) {
245
250
  const { preflightContinue } = options;
246
251
 
247
252
  if (typeof preflightContinue !== 'boolean') {
248
- return Validation.failure({
249
- error: ServerConfigurationError,
250
- code: 'INVALID_CORS_PREFLIGHT_CONTINUE_TYPE',
251
- custom: true,
252
- options: { values: { received: typeof preflightContinue } },
253
- });
253
+ return Validation.failure(
254
+ new ServerConfigurationError('INVALID_CORS_PREFLIGHT_CONTINUE_TYPE', {
255
+ values: { received: typeof preflightContinue },
256
+ }),
257
+ );
254
258
  }
255
259
  }
256
260
 
@@ -258,12 +262,11 @@ export function validateCORSOptions(options) {
258
262
  const { optionsSuccessStatus } = options;
259
263
 
260
264
  if (typeof optionsSuccessStatus !== 'number') {
261
- return Validation.failure({
262
- error: ServerConfigurationError,
263
- code: 'INVALID_CORS_OPTIONS_SUCCESS_STATUS_TYPE',
264
- custom: true,
265
- options: { values: { received: typeof optionsSuccessStatus } },
266
- });
265
+ return Validation.failure(
266
+ new ServerConfigurationError('INVALID_CORS_OPTIONS_SUCCESS_STATUS_TYPE', {
267
+ values: { received: typeof optionsSuccessStatus },
268
+ }),
269
+ );
267
270
  }
268
271
 
269
272
  if (
@@ -271,12 +274,12 @@ export function validateCORSOptions(options) {
271
274
  optionsSuccessStatus < MIN_OPTIONS_SUCCESS_STATUS ||
272
275
  optionsSuccessStatus > MAX_OPTIONS_SUCCESS_STATUS
273
276
  ) {
274
- return Validation.failure({
275
- error: ServerConfigurationError,
276
- code: 'INVALID_CORS_OPTIONS_SUCCESS_STATUS_RANGE',
277
- custom: true,
278
- options: { details: { optionsSuccessStatus } },
279
- });
277
+ return Validation.failure(
278
+ new ServerConfigurationError('INVALID_CORS_OPTIONS_SUCCESS_STATUS_RANGE', {
279
+ values: { optionsSuccessStatus },
280
+ details: { optionsSuccessStatus },
281
+ }),
282
+ );
280
283
  }
281
284
  }
282
285
 
@@ -296,6 +299,7 @@ export function validateCORSOptions(options) {
296
299
  * @throws {ServerConfigurationError} If `maxAge` is not a non-negative integer.
297
300
  * @throws {ServerConfigurationError} If `preflightContinue` is not a boolean.
298
301
  * @throws {ServerConfigurationError} If `optionsSuccessStatus` is not an integer between 100 and 599.
302
+ *
299
303
  * @example
300
304
  * ```js
301
305
  * assertCORSOptions({ origin: true, credentials: true });
@@ -13,6 +13,20 @@
13
13
  import Validation from '#shared/outcome/Validation';
14
14
  import ServerConfigurationError from '#shared/errors/ServerConfigurationError';
15
15
 
16
+ // ━━ TYPE DEFINITIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
17
+ /**
18
+ * A successful {@link Validation} instance.
19
+ *
20
+ * @typedef {import('#shared/outcome/Validation').SuccessValidation} SuccessValidation
21
+ */
22
+
23
+ /**
24
+ * A failed {@link Validation} instance.
25
+ *
26
+ * @template {Error} E
27
+ * @typedef {import('#shared/outcome/Validation').FailureValidation<E>} FailureValidation
28
+ */
29
+
16
30
  // ━━ CONSTANTS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
17
31
  /**
18
32
  * Valid values for the SameSite cookie attribute.
@@ -56,6 +70,7 @@ function everyNonEmptyString(values) {
56
70
  return values.every(isNonEmptyString);
57
71
  }
58
72
 
73
+ // ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
59
74
  /**
60
75
  * Validates CSRF options.
61
76
  *
@@ -74,7 +89,8 @@ function everyNonEmptyString(values) {
74
89
  * 11. `sameSite: 'None'` requires an effective `secure: true`.
75
90
  *
76
91
  * @param {unknown} options - CSRF options to validate.
77
- * @returns {Validation} Validation result.
92
+ * @returns {SuccessValidation|FailureValidation<ServerConfigurationError>} Validation result.
93
+ *
78
94
  * @example
79
95
  * ```js
80
96
  * const validation = validateCSRFOptions({ cookieName: 'csrf' });
@@ -97,33 +113,30 @@ export function validateCSRFOptions(options) {
97
113
  }
98
114
 
99
115
  if (!isPlainObject(options)) {
100
- return Validation.failure({
101
- error: ServerConfigurationError,
102
- code: 'INVALID_CSRF_OPTIONS',
103
- custom: true,
104
- options: { values: { received: typeof options } },
105
- });
116
+ return Validation.failure(
117
+ new ServerConfigurationError('INVALID_CSRF_OPTIONS', {
118
+ values: { received: typeof options },
119
+ }),
120
+ );
106
121
  }
107
122
 
108
123
  if ('cookieName' in options) {
109
124
  const { cookieName } = options;
110
125
 
111
126
  if (typeof cookieName !== 'string') {
112
- return Validation.failure({
113
- error: ServerConfigurationError,
114
- code: 'INVALID_CSRF_COOKIE_NAME_TYPE',
115
- custom: true,
116
- options: { values: { received: typeof cookieName } },
117
- });
127
+ return Validation.failure(
128
+ new ServerConfigurationError('INVALID_CSRF_COOKIE_NAME_TYPE', {
129
+ values: { received: typeof cookieName },
130
+ }),
131
+ );
118
132
  }
119
133
 
120
134
  if (cookieName.length === 0) {
121
- return Validation.failure({
122
- error: ServerConfigurationError,
123
- code: 'INVALID_CSRF_COOKIE_NAME_VALUE',
124
- custom: true,
125
- options: { details: { cookieName } },
126
- });
135
+ return Validation.failure(
136
+ new ServerConfigurationError('INVALID_CSRF_COOKIE_NAME_VALUE', {
137
+ details: { cookieName },
138
+ }),
139
+ );
127
140
  }
128
141
  }
129
142
 
@@ -131,21 +144,19 @@ export function validateCSRFOptions(options) {
131
144
  const { headerName } = options;
132
145
 
133
146
  if (typeof headerName !== 'string') {
134
- return Validation.failure({
135
- error: ServerConfigurationError,
136
- code: 'INVALID_CSRF_HEADER_NAME_TYPE',
137
- custom: true,
138
- options: { values: { received: typeof headerName } },
139
- });
147
+ return Validation.failure(
148
+ new ServerConfigurationError('INVALID_CSRF_HEADER_NAME_TYPE', {
149
+ values: { received: typeof headerName },
150
+ }),
151
+ );
140
152
  }
141
153
 
142
154
  if (headerName.length === 0) {
143
- return Validation.failure({
144
- error: ServerConfigurationError,
145
- code: 'INVALID_CSRF_HEADER_NAME_VALUE',
146
- custom: true,
147
- options: { details: { headerName } },
148
- });
155
+ return Validation.failure(
156
+ new ServerConfigurationError('INVALID_CSRF_HEADER_NAME_VALUE', {
157
+ details: { headerName },
158
+ }),
159
+ );
149
160
  }
150
161
  }
151
162
 
@@ -153,21 +164,19 @@ export function validateCSRFOptions(options) {
153
164
  const { safeMethods } = options;
154
165
 
155
166
  if (!Array.isArray(safeMethods)) {
156
- return Validation.failure({
157
- error: ServerConfigurationError,
158
- code: 'INVALID_CSRF_SAFE_METHODS_TYPE',
159
- custom: true,
160
- options: { values: { received: typeof safeMethods } },
161
- });
167
+ return Validation.failure(
168
+ new ServerConfigurationError('INVALID_CSRF_SAFE_METHODS_TYPE', {
169
+ values: { received: typeof safeMethods },
170
+ }),
171
+ );
162
172
  }
163
173
 
164
174
  if (!everyNonEmptyString(safeMethods)) {
165
- return Validation.failure({
166
- error: ServerConfigurationError,
167
- code: 'INVALID_CSRF_SAFE_METHODS_ITEM',
168
- custom: true,
169
- options: { details: { safeMethods } },
170
- });
175
+ return Validation.failure(
176
+ new ServerConfigurationError('INVALID_CSRF_SAFE_METHODS_ITEM', {
177
+ details: { safeMethods },
178
+ }),
179
+ );
171
180
  }
172
181
  }
173
182
 
@@ -175,21 +184,20 @@ export function validateCSRFOptions(options) {
175
184
  const { tokenBytes } = options;
176
185
 
177
186
  if (typeof tokenBytes !== 'number') {
178
- return Validation.failure({
179
- error: ServerConfigurationError,
180
- code: 'INVALID_CSRF_TOKEN_BYTES_TYPE',
181
- custom: true,
182
- options: { values: { received: typeof tokenBytes } },
183
- });
187
+ return Validation.failure(
188
+ new ServerConfigurationError('INVALID_CSRF_TOKEN_BYTES_TYPE', {
189
+ values: { received: typeof tokenBytes },
190
+ }),
191
+ );
184
192
  }
185
193
 
186
194
  if (!Number.isInteger(tokenBytes) || tokenBytes <= 0) {
187
- return Validation.failure({
188
- error: ServerConfigurationError,
189
- code: 'INVALID_CSRF_TOKEN_BYTES_RANGE',
190
- custom: true,
191
- options: { details: { tokenBytes } },
192
- });
195
+ return Validation.failure(
196
+ new ServerConfigurationError('INVALID_CSRF_TOKEN_BYTES_RANGE', {
197
+ values: { tokenBytes },
198
+ details: { tokenBytes },
199
+ }),
200
+ );
193
201
  }
194
202
  }
195
203
 
@@ -197,21 +205,19 @@ export function validateCSRFOptions(options) {
197
205
  const { path } = options;
198
206
 
199
207
  if (typeof path !== 'string') {
200
- return Validation.failure({
201
- error: ServerConfigurationError,
202
- code: 'INVALID_CSRF_PATH_TYPE',
203
- custom: true,
204
- options: { values: { received: typeof path } },
205
- });
208
+ return Validation.failure(
209
+ new ServerConfigurationError('INVALID_CSRF_PATH_TYPE', {
210
+ values: { received: typeof path },
211
+ }),
212
+ );
206
213
  }
207
214
 
208
215
  if (path.length === 0) {
209
- return Validation.failure({
210
- error: ServerConfigurationError,
211
- code: 'INVALID_CSRF_PATH_VALUE',
212
- custom: true,
213
- options: { details: { path } },
214
- });
216
+ return Validation.failure(
217
+ new ServerConfigurationError('INVALID_CSRF_PATH_VALUE', {
218
+ details: { path },
219
+ }),
220
+ );
215
221
  }
216
222
  }
217
223
 
@@ -219,21 +225,19 @@ export function validateCSRFOptions(options) {
219
225
  const { domain } = options;
220
226
 
221
227
  if (typeof domain !== 'string') {
222
- return Validation.failure({
223
- error: ServerConfigurationError,
224
- code: 'INVALID_CSRF_DOMAIN_TYPE',
225
- custom: true,
226
- options: { values: { received: typeof domain } },
227
- });
228
+ return Validation.failure(
229
+ new ServerConfigurationError('INVALID_CSRF_DOMAIN_TYPE', {
230
+ values: { received: typeof domain },
231
+ }),
232
+ );
228
233
  }
229
234
 
230
235
  if (domain.length === 0) {
231
- return Validation.failure({
232
- error: ServerConfigurationError,
233
- code: 'INVALID_CSRF_DOMAIN_VALUE',
234
- custom: true,
235
- options: { details: { domain } },
236
- });
236
+ return Validation.failure(
237
+ new ServerConfigurationError('INVALID_CSRF_DOMAIN_VALUE', {
238
+ details: { domain },
239
+ }),
240
+ );
237
241
  }
238
242
  }
239
243
 
@@ -241,12 +245,11 @@ export function validateCSRFOptions(options) {
241
245
  const { secure } = options;
242
246
 
243
247
  if (typeof secure !== 'boolean') {
244
- return Validation.failure({
245
- error: ServerConfigurationError,
246
- code: 'INVALID_CSRF_SECURE_TYPE',
247
- custom: true,
248
- options: { values: { received: typeof secure } },
249
- });
248
+ return Validation.failure(
249
+ new ServerConfigurationError('INVALID_CSRF_SECURE_TYPE', {
250
+ values: { received: typeof secure },
251
+ }),
252
+ );
250
253
  }
251
254
  }
252
255
 
@@ -254,12 +257,12 @@ export function validateCSRFOptions(options) {
254
257
  const { sameSite } = options;
255
258
 
256
259
  if (sameSite !== undefined && !SAME_SITE_VALUES.includes(String(sameSite))) {
257
- return Validation.failure({
258
- error: ServerConfigurationError,
259
- code: 'INVALID_CSRF_SAME_SITE_VALUE',
260
- custom: true,
261
- options: { details: { sameSite } },
262
- });
260
+ return Validation.failure(
261
+ new ServerConfigurationError('INVALID_CSRF_SAME_SITE_VALUE', {
262
+ values: { received: sameSite },
263
+ details: { sameSite },
264
+ }),
265
+ );
263
266
  }
264
267
  }
265
268
 
@@ -268,11 +271,7 @@ export function validateCSRFOptions(options) {
268
271
  const effectiveSecure = options.secure ?? true;
269
272
 
270
273
  if (effectiveSameSite === 'None' && effectiveSecure === false) {
271
- return Validation.failure({
272
- error: ServerConfigurationError,
273
- code: 'CSRF_NONE_REQUIRES_SECURE',
274
- custom: true,
275
- });
274
+ return Validation.failure(new ServerConfigurationError('CSRF_NONE_REQUIRES_SECURE'));
276
275
  }
277
276
 
278
277
  return Validation.success();
@@ -285,6 +284,7 @@ export function validateCSRFOptions(options) {
285
284
  * @throws {ServerConfigurationError} If `options` is not a plain object.
286
285
  * @throws {ServerConfigurationError} If any option is invalid.
287
286
  * @throws {ServerConfigurationError} If `sameSite: 'None'` is used without `secure: true`.
287
+ *
288
288
  * @example
289
289
  * ```js
290
290
  * assertCSRFOptions({ cookieName: 'csrf' });