@rvoh/psychic 3.9.0 → 3.10.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.
- package/dist/cjs/src/psychic-app/index.js +6 -3
- package/dist/cjs/src/server/params.js +78 -7
- package/dist/esm/src/psychic-app/index.js +6 -3
- package/dist/esm/src/server/params.js +78 -7
- package/dist/types/src/psychic-app/index.d.ts +1 -0
- package/dist/types/src/server/params.d.ts +41 -0
- package/package.json +1 -1
|
@@ -44,7 +44,9 @@ export default class PsychicApp {
|
|
|
44
44
|
if (!psychicApp.routesCb)
|
|
45
45
|
throw new PsychicAppInitMissingRoutesCallback();
|
|
46
46
|
if (psychicApp.encryption?.cookies?.current)
|
|
47
|
-
this.checkEncryptionKey('cookies', psychicApp.encryption.cookies.current.key, psychicApp.encryption.cookies.current.algorithm);
|
|
47
|
+
this.checkEncryptionKey('cookies', 'current', psychicApp.encryption.cookies.current.key, psychicApp.encryption.cookies.current.algorithm);
|
|
48
|
+
if (psychicApp.encryption?.cookies?.legacy)
|
|
49
|
+
this.checkEncryptionKey('cookies', 'legacy', psychicApp.encryption.cookies.legacy.key, psychicApp.encryption.cookies.legacy.algorithm);
|
|
48
50
|
await psychicApp.inflections?.();
|
|
49
51
|
dreamApp.on('repl:start', context => {
|
|
50
52
|
const psychicApp = PsychicApp.getOrFail();
|
|
@@ -187,14 +189,15 @@ export default class PsychicApp {
|
|
|
187
189
|
* are not broken by a hard failure.
|
|
188
190
|
*
|
|
189
191
|
* @param encryptionIdentifier - currently must be 'cookies', though this may change in the future
|
|
192
|
+
* @param keyType - whether this is the 'current' or 'legacy' key (named in the error so a misconfigured legacy key is not mistaken for the current one)
|
|
190
193
|
* @param key - the encryption key you want to check
|
|
191
194
|
* @param algorithm - the encryption algorithm you want to check
|
|
192
195
|
*/
|
|
193
|
-
static checkEncryptionKey(encryptionIdentifier, key, algorithm) {
|
|
196
|
+
static checkEncryptionKey(encryptionIdentifier, keyType, key, algorithm) {
|
|
194
197
|
if (Encrypt.validateKey(key, algorithm))
|
|
195
198
|
return;
|
|
196
199
|
const message = `
|
|
197
|
-
Your
|
|
200
|
+
Your ${keyType} key value for ${encryptionIdentifier} encryption is invalid.
|
|
198
201
|
Try setting it to something valid, like:
|
|
199
202
|
${Encrypt.generateKey(algorithm)}
|
|
200
203
|
|
|
@@ -59,8 +59,11 @@ export default class Params {
|
|
|
59
59
|
else if (columnMetadata?.enumValues) {
|
|
60
60
|
const paramValue = params[columnName];
|
|
61
61
|
if (columnMetadata.isArray) {
|
|
62
|
+
// Single-value query arrays are conformed to arrays upstream in
|
|
63
|
+
// conformQueryArrayParamsToOpenapiShape, so a non-array reaching
|
|
64
|
+
// here is a real error rather than something to coerce.
|
|
62
65
|
if (!Array.isArray(paramValue))
|
|
63
|
-
|
|
66
|
+
throw new ParamValidationError(columnName.toString(), ['expected an array of enum values']);
|
|
64
67
|
returnObj[columnName] = paramValue.map(p => {
|
|
65
68
|
return new this(params).cast(columnName.toString(), p, 'string', {
|
|
66
69
|
allowNull: columnMetadata.allowNull,
|
|
@@ -177,10 +180,16 @@ export default class Params {
|
|
|
177
180
|
return paramValue;
|
|
178
181
|
}
|
|
179
182
|
const integerRegexp = /^-?\d+$/;
|
|
183
|
+
// Plain decimal numeric string: optional sign, digits with an optional
|
|
184
|
+
// fractional part (or a bare fractional part), and an optional decimal
|
|
185
|
+
// exponent. Deliberately excludes hex/octal/binary literals and the
|
|
186
|
+
// "Infinity"/"NaN" keywords that `Number()` would otherwise accept.
|
|
187
|
+
const decimalNumberRegexp = /^-?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
180
188
|
switch (expectedType) {
|
|
181
189
|
case 'string':
|
|
182
190
|
if (typeof paramValue !== 'string')
|
|
183
191
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
192
|
+
this.throwUnlessWithinLength(paramName, paramValue, opts);
|
|
184
193
|
if (opts?.enum && !opts.enum.includes(paramValue))
|
|
185
194
|
throw new ParamValidationError(paramName, ['did not match expected enum values']);
|
|
186
195
|
if (opts?.match)
|
|
@@ -191,6 +200,7 @@ export default class Params {
|
|
|
191
200
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
192
201
|
if (!integerRegexp.test(paramValue?.toString()))
|
|
193
202
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
203
|
+
this.throwUnlessWithinRange(paramName, BigInt(paramValue.toString()), opts);
|
|
194
204
|
return paramValue.toString();
|
|
195
205
|
case 'boolean':
|
|
196
206
|
if ([true, 'true', 1, '1'].includes(paramValue))
|
|
@@ -254,25 +264,47 @@ export default class Params {
|
|
|
254
264
|
}
|
|
255
265
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
256
266
|
}
|
|
257
|
-
case 'integer':
|
|
267
|
+
case 'integer': {
|
|
258
268
|
if (typeof paramValue !== 'string' && typeof paramValue !== 'number')
|
|
259
269
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
260
270
|
if (!integerRegexp.test(paramValue?.toString()))
|
|
261
271
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
262
|
-
|
|
272
|
+
const integerValue = parseInt(paramValue, 10);
|
|
273
|
+
// Reject values outside the safe-integer range: a long digit string
|
|
274
|
+
// (e.g. a 40-digit number) would otherwise be silently rounded to a
|
|
275
|
+
// nearby float by parseInt. Genuinely large integers should be cast as
|
|
276
|
+
// `bigint`, which preserves the exact value as a string.
|
|
277
|
+
if (!Number.isSafeInteger(integerValue))
|
|
278
|
+
throw new ParamValidationError(paramName, [
|
|
279
|
+
'expected an integer within the safe integer range (use bigint for larger values)',
|
|
280
|
+
]);
|
|
281
|
+
this.throwUnlessWithinRange(paramName, integerValue, opts);
|
|
282
|
+
return integerValue;
|
|
283
|
+
}
|
|
263
284
|
case 'json':
|
|
264
285
|
if (typeof paramValue !== 'object')
|
|
265
286
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
266
287
|
return paramValue;
|
|
267
|
-
case 'number':
|
|
268
|
-
if (typeof paramValue === 'number')
|
|
288
|
+
case 'number': {
|
|
289
|
+
if (typeof paramValue === 'number') {
|
|
290
|
+
if (!Number.isFinite(paramValue))
|
|
291
|
+
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
292
|
+
this.throwUnlessWithinRange(paramName, paramValue, opts);
|
|
269
293
|
return paramValue;
|
|
294
|
+
}
|
|
270
295
|
if (typeof paramValue === 'string') {
|
|
271
|
-
|
|
296
|
+
// Require a plain decimal numeric string and a finite result. This
|
|
297
|
+
// rejects hex/octal/binary forms ("0x10"), the literals "Infinity"/
|
|
298
|
+
// "NaN", and magnitudes that overflow to Infinity ("1e999"), all of
|
|
299
|
+
// which `Number()` would otherwise silently coerce.
|
|
300
|
+
const numberValue = Number(paramValue);
|
|
301
|
+
if (!decimalNumberRegexp.test(paramValue) || !Number.isFinite(numberValue))
|
|
272
302
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
273
|
-
|
|
303
|
+
this.throwUnlessWithinRange(paramName, numberValue, opts);
|
|
304
|
+
return numberValue;
|
|
274
305
|
}
|
|
275
306
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
307
|
+
}
|
|
276
308
|
case 'null':
|
|
277
309
|
if (paramValue !== null)
|
|
278
310
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
@@ -352,6 +384,45 @@ export default class Params {
|
|
|
352
384
|
return paramValue;
|
|
353
385
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
354
386
|
}
|
|
387
|
+
/**
|
|
388
|
+
* Enforces `minLength`/`maxLength` (inclusive) against an already-coerced
|
|
389
|
+
* string value, throwing {@link ParamValidationError} on violation. Mirrors
|
|
390
|
+
* JSON-schema / OpenAPI string-length constraints.
|
|
391
|
+
*/
|
|
392
|
+
throwUnlessWithinLength(paramName, paramValue, opts) {
|
|
393
|
+
if (opts?.minLength !== undefined && paramValue.length < opts.minLength)
|
|
394
|
+
throw new ParamValidationError(paramName, [
|
|
395
|
+
`expected string with length no less than ${opts.minLength}`,
|
|
396
|
+
]);
|
|
397
|
+
if (opts?.maxLength !== undefined && paramValue.length > opts.maxLength)
|
|
398
|
+
throw new ParamValidationError(paramName, [
|
|
399
|
+
`expected string with length no greater than ${opts.maxLength}`,
|
|
400
|
+
]);
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Enforces `minimum`/`maximum` (inclusive) against an already-coerced numeric
|
|
404
|
+
* value, throwing {@link ParamValidationError} on violation. Accepts a
|
|
405
|
+
* `bigint` so `bigint` casts are checked without precision loss; the bounds
|
|
406
|
+
* themselves are compared as `bigint` when they are integers and otherwise
|
|
407
|
+
* fall back to floating-point comparison. Mirrors JSON-schema / OpenAPI
|
|
408
|
+
* numeric-range constraints.
|
|
409
|
+
*/
|
|
410
|
+
throwUnlessWithinRange(paramName, paramValue, opts) {
|
|
411
|
+
if (opts?.minimum !== undefined && this.isBelowNumericBound(paramValue, opts.minimum))
|
|
412
|
+
throw new ParamValidationError(paramName, [`expected a value no less than ${opts.minimum}`]);
|
|
413
|
+
if (opts?.maximum !== undefined && this.isAboveNumericBound(paramValue, opts.maximum))
|
|
414
|
+
throw new ParamValidationError(paramName, [`expected a value no greater than ${opts.maximum}`]);
|
|
415
|
+
}
|
|
416
|
+
isBelowNumericBound(value, bound) {
|
|
417
|
+
if (typeof value === 'bigint')
|
|
418
|
+
return Number.isInteger(bound) ? value < BigInt(bound) : Number(value) < bound;
|
|
419
|
+
return value < bound;
|
|
420
|
+
}
|
|
421
|
+
isAboveNumericBound(value, bound) {
|
|
422
|
+
if (typeof value === 'bigint')
|
|
423
|
+
return Number.isInteger(bound) ? value > BigInt(bound) : Number(value) > bound;
|
|
424
|
+
return value > bound;
|
|
425
|
+
}
|
|
355
426
|
throwUnlessAllowNull(paramName, paramValue, message, { allowNull = false } = {}) {
|
|
356
427
|
const isNullOrUndefined = [null, undefined].includes(paramValue);
|
|
357
428
|
if (allowNull && isNullOrUndefined)
|
|
@@ -44,7 +44,9 @@ export default class PsychicApp {
|
|
|
44
44
|
if (!psychicApp.routesCb)
|
|
45
45
|
throw new PsychicAppInitMissingRoutesCallback();
|
|
46
46
|
if (psychicApp.encryption?.cookies?.current)
|
|
47
|
-
this.checkEncryptionKey('cookies', psychicApp.encryption.cookies.current.key, psychicApp.encryption.cookies.current.algorithm);
|
|
47
|
+
this.checkEncryptionKey('cookies', 'current', psychicApp.encryption.cookies.current.key, psychicApp.encryption.cookies.current.algorithm);
|
|
48
|
+
if (psychicApp.encryption?.cookies?.legacy)
|
|
49
|
+
this.checkEncryptionKey('cookies', 'legacy', psychicApp.encryption.cookies.legacy.key, psychicApp.encryption.cookies.legacy.algorithm);
|
|
48
50
|
await psychicApp.inflections?.();
|
|
49
51
|
dreamApp.on('repl:start', context => {
|
|
50
52
|
const psychicApp = PsychicApp.getOrFail();
|
|
@@ -187,14 +189,15 @@ export default class PsychicApp {
|
|
|
187
189
|
* are not broken by a hard failure.
|
|
188
190
|
*
|
|
189
191
|
* @param encryptionIdentifier - currently must be 'cookies', though this may change in the future
|
|
192
|
+
* @param keyType - whether this is the 'current' or 'legacy' key (named in the error so a misconfigured legacy key is not mistaken for the current one)
|
|
190
193
|
* @param key - the encryption key you want to check
|
|
191
194
|
* @param algorithm - the encryption algorithm you want to check
|
|
192
195
|
*/
|
|
193
|
-
static checkEncryptionKey(encryptionIdentifier, key, algorithm) {
|
|
196
|
+
static checkEncryptionKey(encryptionIdentifier, keyType, key, algorithm) {
|
|
194
197
|
if (Encrypt.validateKey(key, algorithm))
|
|
195
198
|
return;
|
|
196
199
|
const message = `
|
|
197
|
-
Your
|
|
200
|
+
Your ${keyType} key value for ${encryptionIdentifier} encryption is invalid.
|
|
198
201
|
Try setting it to something valid, like:
|
|
199
202
|
${Encrypt.generateKey(algorithm)}
|
|
200
203
|
|
|
@@ -59,8 +59,11 @@ export default class Params {
|
|
|
59
59
|
else if (columnMetadata?.enumValues) {
|
|
60
60
|
const paramValue = params[columnName];
|
|
61
61
|
if (columnMetadata.isArray) {
|
|
62
|
+
// Single-value query arrays are conformed to arrays upstream in
|
|
63
|
+
// conformQueryArrayParamsToOpenapiShape, so a non-array reaching
|
|
64
|
+
// here is a real error rather than something to coerce.
|
|
62
65
|
if (!Array.isArray(paramValue))
|
|
63
|
-
|
|
66
|
+
throw new ParamValidationError(columnName.toString(), ['expected an array of enum values']);
|
|
64
67
|
returnObj[columnName] = paramValue.map(p => {
|
|
65
68
|
return new this(params).cast(columnName.toString(), p, 'string', {
|
|
66
69
|
allowNull: columnMetadata.allowNull,
|
|
@@ -177,10 +180,16 @@ export default class Params {
|
|
|
177
180
|
return paramValue;
|
|
178
181
|
}
|
|
179
182
|
const integerRegexp = /^-?\d+$/;
|
|
183
|
+
// Plain decimal numeric string: optional sign, digits with an optional
|
|
184
|
+
// fractional part (or a bare fractional part), and an optional decimal
|
|
185
|
+
// exponent. Deliberately excludes hex/octal/binary literals and the
|
|
186
|
+
// "Infinity"/"NaN" keywords that `Number()` would otherwise accept.
|
|
187
|
+
const decimalNumberRegexp = /^-?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
180
188
|
switch (expectedType) {
|
|
181
189
|
case 'string':
|
|
182
190
|
if (typeof paramValue !== 'string')
|
|
183
191
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
192
|
+
this.throwUnlessWithinLength(paramName, paramValue, opts);
|
|
184
193
|
if (opts?.enum && !opts.enum.includes(paramValue))
|
|
185
194
|
throw new ParamValidationError(paramName, ['did not match expected enum values']);
|
|
186
195
|
if (opts?.match)
|
|
@@ -191,6 +200,7 @@ export default class Params {
|
|
|
191
200
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
192
201
|
if (!integerRegexp.test(paramValue?.toString()))
|
|
193
202
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
203
|
+
this.throwUnlessWithinRange(paramName, BigInt(paramValue.toString()), opts);
|
|
194
204
|
return paramValue.toString();
|
|
195
205
|
case 'boolean':
|
|
196
206
|
if ([true, 'true', 1, '1'].includes(paramValue))
|
|
@@ -254,25 +264,47 @@ export default class Params {
|
|
|
254
264
|
}
|
|
255
265
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
256
266
|
}
|
|
257
|
-
case 'integer':
|
|
267
|
+
case 'integer': {
|
|
258
268
|
if (typeof paramValue !== 'string' && typeof paramValue !== 'number')
|
|
259
269
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
260
270
|
if (!integerRegexp.test(paramValue?.toString()))
|
|
261
271
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
262
|
-
|
|
272
|
+
const integerValue = parseInt(paramValue, 10);
|
|
273
|
+
// Reject values outside the safe-integer range: a long digit string
|
|
274
|
+
// (e.g. a 40-digit number) would otherwise be silently rounded to a
|
|
275
|
+
// nearby float by parseInt. Genuinely large integers should be cast as
|
|
276
|
+
// `bigint`, which preserves the exact value as a string.
|
|
277
|
+
if (!Number.isSafeInteger(integerValue))
|
|
278
|
+
throw new ParamValidationError(paramName, [
|
|
279
|
+
'expected an integer within the safe integer range (use bigint for larger values)',
|
|
280
|
+
]);
|
|
281
|
+
this.throwUnlessWithinRange(paramName, integerValue, opts);
|
|
282
|
+
return integerValue;
|
|
283
|
+
}
|
|
263
284
|
case 'json':
|
|
264
285
|
if (typeof paramValue !== 'object')
|
|
265
286
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
266
287
|
return paramValue;
|
|
267
|
-
case 'number':
|
|
268
|
-
if (typeof paramValue === 'number')
|
|
288
|
+
case 'number': {
|
|
289
|
+
if (typeof paramValue === 'number') {
|
|
290
|
+
if (!Number.isFinite(paramValue))
|
|
291
|
+
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
292
|
+
this.throwUnlessWithinRange(paramName, paramValue, opts);
|
|
269
293
|
return paramValue;
|
|
294
|
+
}
|
|
270
295
|
if (typeof paramValue === 'string') {
|
|
271
|
-
|
|
296
|
+
// Require a plain decimal numeric string and a finite result. This
|
|
297
|
+
// rejects hex/octal/binary forms ("0x10"), the literals "Infinity"/
|
|
298
|
+
// "NaN", and magnitudes that overflow to Infinity ("1e999"), all of
|
|
299
|
+
// which `Number()` would otherwise silently coerce.
|
|
300
|
+
const numberValue = Number(paramValue);
|
|
301
|
+
if (!decimalNumberRegexp.test(paramValue) || !Number.isFinite(numberValue))
|
|
272
302
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
273
|
-
|
|
303
|
+
this.throwUnlessWithinRange(paramName, numberValue, opts);
|
|
304
|
+
return numberValue;
|
|
274
305
|
}
|
|
275
306
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
307
|
+
}
|
|
276
308
|
case 'null':
|
|
277
309
|
if (paramValue !== null)
|
|
278
310
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
@@ -352,6 +384,45 @@ export default class Params {
|
|
|
352
384
|
return paramValue;
|
|
353
385
|
throw new ParamValidationError(paramName, [typeToError(expectedType)]);
|
|
354
386
|
}
|
|
387
|
+
/**
|
|
388
|
+
* Enforces `minLength`/`maxLength` (inclusive) against an already-coerced
|
|
389
|
+
* string value, throwing {@link ParamValidationError} on violation. Mirrors
|
|
390
|
+
* JSON-schema / OpenAPI string-length constraints.
|
|
391
|
+
*/
|
|
392
|
+
throwUnlessWithinLength(paramName, paramValue, opts) {
|
|
393
|
+
if (opts?.minLength !== undefined && paramValue.length < opts.minLength)
|
|
394
|
+
throw new ParamValidationError(paramName, [
|
|
395
|
+
`expected string with length no less than ${opts.minLength}`,
|
|
396
|
+
]);
|
|
397
|
+
if (opts?.maxLength !== undefined && paramValue.length > opts.maxLength)
|
|
398
|
+
throw new ParamValidationError(paramName, [
|
|
399
|
+
`expected string with length no greater than ${opts.maxLength}`,
|
|
400
|
+
]);
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Enforces `minimum`/`maximum` (inclusive) against an already-coerced numeric
|
|
404
|
+
* value, throwing {@link ParamValidationError} on violation. Accepts a
|
|
405
|
+
* `bigint` so `bigint` casts are checked without precision loss; the bounds
|
|
406
|
+
* themselves are compared as `bigint` when they are integers and otherwise
|
|
407
|
+
* fall back to floating-point comparison. Mirrors JSON-schema / OpenAPI
|
|
408
|
+
* numeric-range constraints.
|
|
409
|
+
*/
|
|
410
|
+
throwUnlessWithinRange(paramName, paramValue, opts) {
|
|
411
|
+
if (opts?.minimum !== undefined && this.isBelowNumericBound(paramValue, opts.minimum))
|
|
412
|
+
throw new ParamValidationError(paramName, [`expected a value no less than ${opts.minimum}`]);
|
|
413
|
+
if (opts?.maximum !== undefined && this.isAboveNumericBound(paramValue, opts.maximum))
|
|
414
|
+
throw new ParamValidationError(paramName, [`expected a value no greater than ${opts.maximum}`]);
|
|
415
|
+
}
|
|
416
|
+
isBelowNumericBound(value, bound) {
|
|
417
|
+
if (typeof value === 'bigint')
|
|
418
|
+
return Number.isInteger(bound) ? value < BigInt(bound) : Number(value) < bound;
|
|
419
|
+
return value < bound;
|
|
420
|
+
}
|
|
421
|
+
isAboveNumericBound(value, bound) {
|
|
422
|
+
if (typeof value === 'bigint')
|
|
423
|
+
return Number.isInteger(bound) ? value > BigInt(bound) : Number(value) > bound;
|
|
424
|
+
return value > bound;
|
|
425
|
+
}
|
|
355
426
|
throwUnlessAllowNull(paramName, paramValue, message, { allowNull = false } = {}) {
|
|
356
427
|
const isNullOrUndefined = [null, undefined].includes(paramValue);
|
|
357
428
|
if (allowNull && isNullOrUndefined)
|
|
@@ -125,6 +125,7 @@ export default class PsychicApp {
|
|
|
125
125
|
* are not broken by a hard failure.
|
|
126
126
|
*
|
|
127
127
|
* @param encryptionIdentifier - currently must be 'cookies', though this may change in the future
|
|
128
|
+
* @param keyType - whether this is the 'current' or 'legacy' key (named in the error so a misconfigured legacy key is not mistaken for the current one)
|
|
128
129
|
* @param key - the encryption key you want to check
|
|
129
130
|
* @param algorithm - the encryption algorithm you want to check
|
|
130
131
|
*/
|
|
@@ -77,6 +77,23 @@ export default class Params {
|
|
|
77
77
|
private validateOpenapiOrThrow;
|
|
78
78
|
restrict(allowed: string[]): PsychicParamsDictionary;
|
|
79
79
|
private matchRegexOrThrow;
|
|
80
|
+
/**
|
|
81
|
+
* Enforces `minLength`/`maxLength` (inclusive) against an already-coerced
|
|
82
|
+
* string value, throwing {@link ParamValidationError} on violation. Mirrors
|
|
83
|
+
* JSON-schema / OpenAPI string-length constraints.
|
|
84
|
+
*/
|
|
85
|
+
private throwUnlessWithinLength;
|
|
86
|
+
/**
|
|
87
|
+
* Enforces `minimum`/`maximum` (inclusive) against an already-coerced numeric
|
|
88
|
+
* value, throwing {@link ParamValidationError} on violation. Accepts a
|
|
89
|
+
* `bigint` so `bigint` casts are checked without precision loss; the bounds
|
|
90
|
+
* themselves are compared as `bigint` when they are integers and otherwise
|
|
91
|
+
* fall back to floating-point comparison. Mirrors JSON-schema / OpenAPI
|
|
92
|
+
* numeric-range constraints.
|
|
93
|
+
*/
|
|
94
|
+
private throwUnlessWithinRange;
|
|
95
|
+
private isBelowNumericBound;
|
|
96
|
+
private isAboveNumericBound;
|
|
80
97
|
private throwUnlessAllowNull;
|
|
81
98
|
}
|
|
82
99
|
export type ValidatedReturnType<ExpectedType, OptsType> = ExpectedType extends RegExp ? string : ExpectedType extends 'string' ? OptsType extends {
|
|
@@ -113,6 +130,30 @@ export type ParamsCastOptions<EnumType> = {
|
|
|
113
130
|
allowNull?: boolean;
|
|
114
131
|
match?: RegExp;
|
|
115
132
|
enum?: EnumType;
|
|
133
|
+
/**
|
|
134
|
+
* Maximum permitted string length (inclusive). Enforced after coercion for
|
|
135
|
+
* `string` casts (and element-wise for `string[]`). Mirrors JSON-schema /
|
|
136
|
+
* OpenAPI `maxLength`.
|
|
137
|
+
*/
|
|
138
|
+
maxLength?: number;
|
|
139
|
+
/**
|
|
140
|
+
* Minimum permitted string length (inclusive). Enforced after coercion for
|
|
141
|
+
* `string` casts (and element-wise for `string[]`). Mirrors JSON-schema /
|
|
142
|
+
* OpenAPI `minLength`.
|
|
143
|
+
*/
|
|
144
|
+
minLength?: number;
|
|
145
|
+
/**
|
|
146
|
+
* Minimum permitted numeric value (inclusive). Enforced after coercion for
|
|
147
|
+
* `number`/`integer`/`bigint` casts (and element-wise for their array
|
|
148
|
+
* variants). Mirrors JSON-schema / OpenAPI `minimum`.
|
|
149
|
+
*/
|
|
150
|
+
minimum?: number;
|
|
151
|
+
/**
|
|
152
|
+
* Maximum permitted numeric value (inclusive). Enforced after coercion for
|
|
153
|
+
* `number`/`integer`/`bigint` casts (and element-wise for their array
|
|
154
|
+
* variants). Mirrors JSON-schema / OpenAPI `maximum`.
|
|
155
|
+
*/
|
|
156
|
+
maximum?: number;
|
|
116
157
|
};
|
|
117
158
|
interface ParamsForOptsBase<OnlyArray> {
|
|
118
159
|
array?: boolean;
|