@stacksjs/validation 0.75.114 → 0.75.115

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/input.d.ts CHANGED
@@ -8,6 +8,8 @@ export declare function parseBooleanInput(value: unknown, field: string): boolea
8
8
  export declare function validateModelInput(model: InputValidatableModel, values: Record<string, unknown>): void;
9
9
  /** Exact enum membership without turning objects or numbers into text. */
10
10
  export declare function parseEnumInput<const T extends readonly string[]>(value: unknown, field: string, choices: T): T[number];
11
+ /** Validate a bounded enum list as an ordered set, without silently dropping invalid entries. */
12
+ export declare function parseEnumListInput<const T extends readonly string[]>(value: unknown, field: string, choices: T, minItems?: number, maxItems?: number): T[number][];
11
13
  /** Decode SQL/form booleans. The caller explicitly chooses the fallback for absent or corrupt data. */
12
14
  export declare function storedBoolean(value: unknown, fallback: boolean): boolean;
13
15
  /** The nullable adapter for record serializers and optional identifiers. */
package/dist/input.js CHANGED
@@ -1 +1 @@
1
- export class InputValidationError extends Error{status=422}export async function readJsonObject(request){let body;try{body=await request.json()}catch{throw new InputValidationError("Send a valid JSON object")}if(!body||typeof body!=="object"||Array.isArray(body))throw new InputValidationError("Send a JSON object");return body}export function parsePositiveId(value){if(typeof value!=="string"&&typeof value!=="number")throw new InputValidationError("Invalid identifier");const id=Number(value);if(!Number.isSafeInteger(id)||id<=0)throw new InputValidationError("Invalid identifier");return id}export function parseTextInput(value,field,required=!1,maxLength){if(value==null&&!required)return null;if(typeof value!=="string")throw new InputValidationError(`${field} must be text`);const text=value.trim();if(maxLength!==void 0&&text.length>maxLength)throw new InputValidationError(`${field} is too long`);if(!text&&required)throw new InputValidationError(`${field} is required`);return text||null}export function parseNumberInput(value,field,min,max,integer=!1){if(value==null||value==="")return null;if(typeof value!=="number"&&typeof value!=="string"||typeof value==="string"&&!value.trim())throw new InputValidationError(`${field} must be a number`);const number=Number(value);if(!Number.isFinite(number)||number<min||number>max||integer&&!Number.isSafeInteger(number))throw new InputValidationError(`${field} is out of range`);return number}export function parseBooleanInput(value,field){if(value===void 0)return;if(value===!0||value==="true"||value===1||value==="1")return!0;if(value===!1||value==="false"||value===0||value==="0")return!1;throw new InputValidationError(`${field} must be a boolean`)}export function validateModelInput(model,values){const attributes=model.getDefinition().attributes??{};for(const[field,value]of Object.entries(values)){const attribute=Object.prototype.hasOwnProperty.call(attributes,field)?field:Object.keys(attributes).find((key)=>key===field||key.replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/([A-Z])([A-Z][a-z])/g,"$1_$2").toLowerCase()===field),rule=attribute?attributes[attribute]?.validation?.rule:void 0;if(rule&&!rule.validate(value).valid)throw new InputValidationError(`Invalid ${field.replaceAll("_"," ")}`)}}export function parseEnumInput(value,field,choices){if(typeof value!=="string"||!choices.includes(value))throw new InputValidationError(`${field} must be ${choices.join(", ")}`);return value}export function storedBoolean(value,fallback){if(value==null)return fallback;try{return parseBooleanInput(value,"Stored value")??fallback}catch(error){if(error instanceof InputValidationError)return fallback;throw error}}export function positiveIdOrNull(value){try{return parsePositiveId(value)}catch(error){if(error instanceof InputValidationError)return null;throw error}}
1
+ export class InputValidationError extends Error{status=422}export async function readJsonObject(request){let body;try{body=await request.json()}catch{throw new InputValidationError("Send a valid JSON object")}if(!body||typeof body!=="object"||Array.isArray(body))throw new InputValidationError("Send a JSON object");return body}export function parsePositiveId(value){if(typeof value!=="string"&&typeof value!=="number")throw new InputValidationError("Invalid identifier");const id=Number(value);if(!Number.isSafeInteger(id)||id<=0)throw new InputValidationError("Invalid identifier");return id}export function parseTextInput(value,field,required=!1,maxLength){if(value==null&&!required)return null;if(typeof value!=="string")throw new InputValidationError(`${field} must be text`);const text=value.trim();if(maxLength!==void 0&&text.length>maxLength)throw new InputValidationError(`${field} is too long`);if(!text&&required)throw new InputValidationError(`${field} is required`);return text||null}export function parseNumberInput(value,field,min,max,integer=!1){if(value==null||value==="")return null;if(typeof value!=="number"&&typeof value!=="string"||typeof value==="string"&&!value.trim())throw new InputValidationError(`${field} must be a number`);const number=Number(value);if(!Number.isFinite(number)||number<min||number>max||integer&&!Number.isSafeInteger(number))throw new InputValidationError(`${field} is out of range`);return number}export function parseBooleanInput(value,field){if(value===void 0)return;if(value===!0||value==="true"||value===1||value==="1")return!0;if(value===!1||value==="false"||value===0||value==="0")return!1;throw new InputValidationError(`${field} must be a boolean`)}export function validateModelInput(model,values){const attributes=model.getDefinition().attributes??{};for(const[field,value]of Object.entries(values)){const attribute=Object.prototype.hasOwnProperty.call(attributes,field)?field:Object.keys(attributes).find((key)=>key===field||key.replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/([A-Z])([A-Z][a-z])/g,"$1_$2").toLowerCase()===field),rule=attribute?attributes[attribute]?.validation?.rule:void 0;if(rule&&!rule.validate(value).valid)throw new InputValidationError(`Invalid ${field.replaceAll("_"," ")}`)}}export function parseEnumInput(value,field,choices){if(typeof value!=="string"||!choices.includes(value))throw new InputValidationError(`${field} must be ${choices.join(", ")}`);return value}export function parseEnumListInput(value,field,choices,minItems=1,maxItems=100){if(!Number.isSafeInteger(minItems)||!Number.isSafeInteger(maxItems)||minItems<0||maxItems<minItems)throw RangeError("Invalid enum list bounds");if(!Array.isArray(value)||value.length<minItems||value.length>maxItems)throw new InputValidationError(`${field} must contain ${minItems} to ${maxItems} entries`);return[...new Set(value.map((item)=>parseEnumInput(item,field,choices)))]}export function storedBoolean(value,fallback){if(value==null)return fallback;try{return parseBooleanInput(value,"Stored value")??fallback}catch(error){if(error instanceof InputValidationError)return fallback;throw error}}export function positiveIdOrNull(value){try{return parsePositiveId(value)}catch(error){if(error instanceof InputValidationError)return null;throw error}}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/validation",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.75.114",
5
+ "version": "0.75.115",
6
6
  "description": "The Stacks Validation ways.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -64,18 +64,18 @@
64
64
  "prepublishOnly": "bun run build"
65
65
  },
66
66
  "dependencies": {
67
- "@stacksjs/error-handling": "0.75.114",
68
- "@stacksjs/path": "0.75.114",
69
- "@stacksjs/strings": "0.75.114",
67
+ "@stacksjs/error-handling": "0.75.115",
68
+ "@stacksjs/path": "0.75.115",
69
+ "@stacksjs/strings": "0.75.115",
70
70
  "@stacksjs/ts-validation": "^0.5.6",
71
- "@stacksjs/types": "0.75.114",
72
- "@stacksjs/utils": "0.75.114"
71
+ "@stacksjs/types": "0.75.115",
72
+ "@stacksjs/utils": "0.75.115"
73
73
  },
74
74
  "devDependencies": {
75
75
  "better-dx": "^0.2.24"
76
76
  },
77
77
  "peerDependencies": {
78
- "@stacksjs/database": "0.75.114"
78
+ "@stacksjs/database": "0.75.115"
79
79
  },
80
80
  "peerDependenciesMeta": {
81
81
  "@stacksjs/database": {