arstotzka 0.13.3 → 0.14.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";var A=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var _=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var P=(t,n)=>{for(var o in n)A(t,o,{get:n[o],enumerable:!0})},D=(t,n,o,a)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of _(n))!N.call(t,r)&&r!==o&&A(t,r,{get:()=>n[r],enumerable:!(a=k(n,r))||a.enumerable});return t};var Y=t=>D(A({},"__esModule",{value:!0}),t);var B={};P(B,{ANY_OF:()=>K,ARRAY_OF:()=>L,DYNAMIC:()=>U,ERRORS:()=>F,OPTIONAL:()=>R,isTruthy:()=>q,validate:()=>V});module.exports=Y(B);var F={noProperty:"Required property not present",typeMismatch:"Provided type is not allowed by schema",customFail:"Custom validation function failed",extraProperty:"Provided object contains properties not present in schema",exceptionOnCustom:"Exception thrown during constraint validation",notArray:"Tried using ARRAY_OF constraint on non-array value",targetIsNull:"Passed object or array item is null",functionExpected:"Expected function as dynamic constraint",objectExpected:"Expected object",anyFailed:"None of ANY_OF constraints are met",parsingError:"Schema parsing error",deadError:"This error never occurs (i guess)"},j=t=>n=>typeof n==t,M=t=>Array.isArray(t),S=Symbol(),x=Symbol(),E=Symbol(),O=Symbol(),T=Symbol(),$={allErrors:!0,allowExtraProperties:!0};function V(t,n={},o={}){o=Object.assign($,o);let a=[],[r,p]=m(n,a);p.length>0&&console.error("Flags can't be used at schema's root");let s=b("",t,r,o);return s.push(...a),s.forEach(y=>{y.propertyName?.startsWith(".")&&(y.propertyName=y.propertyName.slice(1))}),s}function m(t,n){function o(e){if(!e)return null;if(typeof e=="object"&&"type"in e&&e.type===S)return e;if(Array.isArray(e))return e.map(o).filter(c=>c);switch(typeof e){case"string":return e=="array"?f(M,"typeMismatch",e):f(j(e),"typeMismatch",e);case"function":return f(e,"customFail",e);case"object":return f(T,"deadError",e);case"symbol":return e}return n.push(u("<schema>","parsingError",void 0,e)),null}let a=o(t)||[];function r(e){return Array.isArray(e)?e:[e]}let p=r(a),s=p.filter(e=>typeof e!="symbol"&&e.type==S),y=p.filter(e=>typeof e=="symbol");return[s,y]}function b(t,n,o,a){let r=[];function p(s){return typeof s=="function"}for(let s of o){let y=s.validation;if(p(y)){let[e,c]=v(()=>y(n));e?c||r.push(u(t,s.failMessageId,"[function]",n)):r.push(u(t,"exceptionOnCustom",void 0,c?.toString()));continue}switch(s.validation){case T:{if(typeof n!="object"||!n){r.push(u(t,"objectExpected","object",n));break}let e=Object.keys(n),c=Object.keys(s.expected);for(let i of c){let[l,h]=m(s.expected[i],r);if(!e.includes(i)){h.includes(R)||r.push(u(`${d(t)}.${i}`,"noProperty"));continue}let g=b(`${d(t)}.${i}`,n[i],l,a);r.push(...g)}if(!a.allowExtraProperties){let i=e.filter(l=>!c.includes(l));i.length>0&&r.push(...i.map(l=>u(`${d(t)}.${l}`,"extraProperty")))}break}case x:{if(!Array.isArray(n)){r.push(u(t,"notArray","array",typeof n));break}let[e,c]=m(s.expected,r),i=0;for(let l of n){let h=b(`${d(t)}[${i}]`,l,e,a);r.push(...h),++i}break}case E:{let e=s.expected,c=[],i=!1,l=0;for(let h of e){let g=[],[I,G]=m(h,g),C=b(`${d(t)}.<any#${l}>`,n,I,a);if(C.push(...g),++l,c.push(C),C.length==0){i=!0;break}}i||r.push(u(t,"anyFailed",void 0,c));break}case O:{let e=s.expected;if(typeof e!="function"){r.push(u(t,"functionExpected","function",typeof e));break}let[c,i]=m(e(n),r),l=b(t,n,c,a);r.push(...l);break}}if(!a.allErrors&&r.length>0)break}return r}function v(t){try{return[!0,t()]}catch(n){return[!1,n]}}function d(t){return typeof t=="symbol"?"[symbol]":String(t)}function u(t,n,o,a){return{propertyName:d(t),id:n,message:F[n],expected:o,got:a}}function f(t,n,o){return{type:S,validation:t,failMessageId:n,expected:o}}var R=Symbol();function L(t){return arguments.length>1&&console.error("Got more than one argument to ARRAY_OF. Did you mean to pass an array of constraints?"),f(x,"deadError",t)}function K(t){return arguments.length>1?f(E,"deadError",Array.from(arguments)):Array.isArray(t)?f(E,"deadError",t):f(E,"deadError",[t])}function U(t){return f(O,"deadError",t)}function q(t){return!!t}0&&(module.exports={ANY_OF,ARRAY_OF,DYNAMIC,ERRORS,OPTIONAL,isTruthy,validate});
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export const ERRORS = {\n\tnoProperty: \"Required property not present\",\n\ttypeMismatch: \"Provided type is not allowed by schema\",\n\tcustomFail: \"Custom validation function failed\",\n\textraProperty: \"Provided object contains properties not present in schema\",\n\texceptionOnCustom: \"Exception thrown during constraint validation\",\n\tnotArray: \"Tried using ARRAY_OF constraint on non-array value\",\n\ttargetIsNull: \"Passed object or array item is null\",\n\tfunctionExpected: \"Expected function as dynamic constraint\",\n\tobjectExpected: \"Expected object\",\n\tanyFailed: \"None of ANY_OF constraints are met\",\n\tparsingError: \"Schema parsing error\",\n\tdeadError: \"This error never occurs (i guess)\"\n};\n\nconst TYPE = (t: string) => (x: any) => typeof x == t;\nconst IS_ARRAY = (x: any) => Array.isArray(x);\n\nconst CONSTRAINT = Symbol();\nconst FC_ARRAY = Symbol(); // https://www.youtube.com/watch?v=qSqXGeJJBaI\nconst FC_ANY = Symbol();\nconst FC_DYNAMIC = Symbol();\nconst FC_NESTED = Symbol();\n\nconst VALIDATION_DEFAULTS = {\n\tallErrors: true,\n\tallowExtraProperties: true\n};\n\n/**\n* @param options Validation options:\n*\n* - allErrors (true) : return all errors instead of interrupting after first fail\n* - allowExtraProperties (true) : If false, adds specific error to a list for every property of target object not present in schema\n* @return Array of errors\n */\nexport function validate(target: any, schema: Schema = {}, options: ValidationOptions = {}) {\n\toptions = Object.assign(VALIDATION_DEFAULTS, options)\n\n\tconst parseErrors: Error[] = [];\n\tconst [constraints, flags] = parseSchema(schema, parseErrors);\n\n\tif (flags.length > 0) {\n\t\tconsole.error(\"Flags can't be used at schema's root\")\n\t}\n\n\tconst errors = checkValue(\"\", target, constraints, options);\n\terrors.push(...parseErrors)\n\terrors.forEach(e => {\n\t\tif (e.propertyName?.startsWith(\".\"))\n\t\t\te.propertyName = e.propertyName.slice(1);\n\t});\n\n\treturn errors;\n}\n\nfunction parseSchema(schemaProperty: Schema, errors: Error[]): [Constraint[], OptionalFlag[]] {\n\tfunction translate(raw: Schema): Constraint | Constraint[] | OptionalFlag | null {\n\t\tif (!raw) return null;\n\n\t\tif (\n\t\t\ttypeof raw === \"object\" &&\n\t\t\t\"type\" in raw &&\n\t\t\traw.type === CONSTRAINT\n\t\t)\n\t\t\treturn raw as Constraint;\n\n\t\tif (Array.isArray(raw)) {\n\t\t\treturn raw.map(translate).filter(raw => raw) as Constraint | Constraint[] | OptionalFlag;\n\t\t} else {\n\t\t\tswitch (typeof raw) {\n\t\t\t\tcase (\"string\"): {\n\t\t\t\t\tif (raw == \"array\")\n\t\t\t\t\t\treturn constraint(IS_ARRAY, \"typeMismatch\", raw);\n\t\t\t\t\telse\n\t\t\t\t\t\treturn constraint(TYPE(raw as TypeofValueSchema), \"typeMismatch\", raw);\n\t\t\t\t}\n\t\t\t\tcase (\"function\"): {\n\t\t\t\t\treturn constraint(raw as FunctionSchema, \"customFail\", raw);\n\t\t\t\t}\n\t\t\t\tcase (\"object\"): {\n\t\t\t\t\treturn constraint(FC_NESTED, \"deadError\", raw);\n\t\t\t\t}\n\t\t\t\tcase (\"symbol\"): {\n\t\t\t\t\treturn raw as OptionalFlag;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\terrors.push(error(\"<schema>\", \"parsingError\", undefined, raw));\n\t\treturn null;\n\t}\n\n\tconst intermediate = translate(schemaProperty) || [];\n\n\tfunction wrap(\n\t\ttranslated: OptionalFlag | Constraint | Constraint[]\n\t): (OptionalFlag | Constraint)[] {\n\t\tif (!Array.isArray(translated))\n\t\t\treturn [translated];\n\t\treturn translated;\n\t}\n\n\tconst wrapped = wrap(intermediate);\n\tconst constraints = wrapped.filter(i => typeof i !== \"symbol\" && i.type == CONSTRAINT);\n\tconst flags = wrapped.filter(i => typeof i === \"symbol\");\n\n\treturn [\n\t\tconstraints as Constraint[],\n\t\tflags as OptionalFlag[]\n\t];\n}\n\nfunction checkValue(\n\tpropertyName: keyof any,\n\tvalue: any,\n\tconstraints: Constraint[],\n\toptions: ValidationOptions\n): Error[] {\n\tconst errors = [];\n\n\tfunction constraintFunctional(v: Constraint[\"validation\"]): v is (value: any) => any {\n\t\treturn typeof v == \"function\";\n\t}\n\n\tfor (let constraint of constraints) {\n\t\tconst validationCall = constraint.validation;\n\t\tif (constraintFunctional(validationCall)) {\n\t\t\tconst [success, validationResult] = nothrow(() => validationCall(value));\n\t\t\tif (!success) {\n\t\t\t\terrors.push(error(propertyName, \"exceptionOnCustom\", undefined, validationResult?.toString()));\n\t\t\t} else if (!validationResult) {\n\t\t\t\terrors.push(error(propertyName, constraint.failMessageId, \"[function]\", value));\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tswitch(constraint.validation) {\n\t\t\tcase FC_NESTED: {\n\t\t\t\tif (typeof value != \"object\" || !value) {\n\t\t\t\t\terrors.push(error(propertyName, \"objectExpected\", \"object\", value));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tconst targetKeys = Object.keys(value);\n\t\t\t\tconst schemaKeys = Object.keys(constraint.expected!);\n\n\t\t\t\tfor (let key of schemaKeys) {\n\t\t\t\t\t// @ts-ignore // FC_NESTED guarantees expected scheme to be an object\n\t\t\t\t\tconst [subConstraints, flags] = parseSchema(constraint.expected[key], errors);\n\n\t\t\t\t\tif (!targetKeys.includes(key)) {\n\t\t\t\t\t\tif (!flags.includes(OPTIONAL)) {\n\t\t\t\t\t\t\terrors.push(error(`${stringifyKey(propertyName)}.${key}`, \"noProperty\"));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst subErrors = checkValue(`${stringifyKey(propertyName)}.${key}`, value[key], subConstraints, options);\n\t\t\t\t\terrors.push(...subErrors);\n\t\t\t\t}\n\n\t\t\t\tif (!options.allowExtraProperties) {\n\t\t\t\t\tconst extraProperties = targetKeys.filter(k => !schemaKeys.includes(k));\n\t\t\t\t\tif (extraProperties.length > 0) {\n\t\t\t\t\t\terrors.push(...extraProperties.map(k => error(`${stringifyKey(propertyName)}.${k}`, \"extraProperty\")))\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase FC_ARRAY: {\n\t\t\t\tif (!Array.isArray(value)) {\n\t\t\t\t\terrors.push(error(propertyName, \"notArray\", \"array\", typeof value))\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tconst [subConstraints, flags] = parseSchema(constraint.expected, errors);\n\n\t\t\t\tlet indexCounter = 0;\n\t\t\t\tfor (let item of value) {\n\t\t\t\t\tconst subErrors = checkValue(`${stringifyKey(propertyName)}[${indexCounter}]`, item, subConstraints, options);\n\t\t\t\t\terrors.push(...subErrors);\n\t\t\t\t\t++indexCounter;\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase FC_ANY: {\n\t\t\t\tconst subSchemas = constraint.expected as Schema[];\n\n\t\t\t\tconst subErrors = [];\n\t\t\t\tlet passed = false;\n\t\t\t\tlet counter = 0;\n\n\t\t\t\tfor (let subSchema of subSchemas) {\n\t\t\t\t\tconst subParseErrors: Error[] = [];\n\t\t\t\t\tconst [subConstraints, flags] = parseSchema(subSchema, subParseErrors);\n\t\t\t\t\tconst caseErrors = checkValue(`${stringifyKey(propertyName)}.<any#${counter}>`, value, subConstraints, options);\n\t\t\t\t\tcaseErrors.push(...subParseErrors);\n\t\t\t\t\t++counter;\n\t\t\t\t\tsubErrors.push(caseErrors);\n\t\t\t\t\tif (caseErrors.length == 0) {\n\t\t\t\t\t\tpassed = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!passed) errors.push(error(propertyName, \"anyFailed\", undefined, subErrors));\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase FC_DYNAMIC: {\n\t\t\t\tconst constraintCallback = constraint.expected;\n\n\t\t\t\tif (typeof constraintCallback != \"function\") {\n\t\t\t\t\terrors.push(error(propertyName, \"functionExpected\", \"function\", typeof constraintCallback))\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tconst [subConstraints, flags] = parseSchema(constraintCallback(value), errors)\n\t\t\t\tconst subErrors = checkValue(propertyName, value, subConstraints, options);\n\t\t\t\terrors.push(...subErrors);\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!options.allErrors && errors.length > 0) break;\n\t}\n\treturn errors;\n}\n\nfunction nothrow<T>(cb: () => T): [true, T] | [false, unknown] {\n\ttry {\n\t\treturn [true, cb()];\n\t} catch (e) {\n\t\treturn [false, e];\n\t}\n}\n\nfunction stringifyKey(k: keyof any): string {\n\treturn typeof k === \"symbol\" ? \"[symbol]\" : String(k);\n}\n\nfunction error(\n\tpropertyName: keyof any,\n\tid: ErrorID,\n\texpected?: string,\n\tgot?: any\n) {\n\treturn {\n\t\tpropertyName: stringifyKey(propertyName),\n\t\tid,\n\t\tmessage: ERRORS[id],\n\t\texpected,\n\t\tgot\n\t};\n}\n\nfunction constraint(\n\tf: Constraint[\"validation\"],\n\tfailMessageId: Constraint[\"failMessageId\"],\n\texpected: Constraint[\"expected\"]\n): Constraint {\n\treturn {\n\t\ttype: CONSTRAINT,\n\t\tvalidation: f,\n\t\tfailMessageId: failMessageId,\n\t\texpected: expected\n\t};\n}\n\nexport const OPTIONAL = Symbol();\nexport function ARRAY_OF(constraints: Schema) {\n\tif (arguments.length > 1) {\n\t\tconsole.error(\"Got more than one argument to ARRAY_OF. Did you mean to pass an array of constraints?\");\n\t}\n\treturn constraint(FC_ARRAY, \"deadError\", constraints);\n}\nexport function ANY_OF(constraints: Schema[]) {\n\tif (arguments.length > 1) {\n\t\treturn constraint(FC_ANY, \"deadError\", Array.from(arguments));\n\t} else {\n\t\tif (Array.isArray(constraints)) {\n\t\t\treturn constraint(FC_ANY, \"deadError\", constraints);\n\t\t} else {\n\t\t\treturn constraint(FC_ANY, \"deadError\", [constraints]);\n\t\t}\n\t}\n}\nexport function DYNAMIC(constraints: Schema) {\n\treturn constraint(FC_DYNAMIC, \"deadError\", constraints);\n}\n\nexport function isTruthy<T>(value: T): value is NonNullable<T> {\n\treturn Boolean(value);\n}\n\nexport type ErrorID = keyof typeof ERRORS;\n\ntype TypeofValueSchema = \n\t| \"bigint\"\n\t| \"boolean\"\n\t| \"function\"\n\t| \"number\"\n\t| \"object\"\n\t| \"string\"\n\t| \"symbol\"\n\t| \"undefined\"\n\t| \"array\"; // array is special case\ntype FunctionSchema = (value: any) => boolean | any;\ntype OptionalFlag = typeof OPTIONAL;\n\ntype ForbiddenConstraintFlag =\n\t| typeof FC_ARRAY\n\t| typeof FC_ANY\n\t| typeof FC_DYNAMIC\n\t| typeof FC_NESTED\ntype Constraint = {\n\ttype: typeof CONSTRAINT,\n\tvalidation:\n\t\t| ((value: any) => any)\n\t\t| ForbiddenConstraintFlag,\n\tfailMessageId: ErrorID,\n\texpected: Schema\n}\n\nexport type Schema = \n\t| TypeofValueSchema\n\t| FunctionSchema\n\t| OptionalFlag\n\t| { [property: keyof any]: Schema }\n\t| Schema[]\n\t| Constraint;\n\ntype ValidationOptions = Partial<typeof VALIDATION_DEFAULTS>;\n\ntype Error = {\n\tpropertyName: string,\n\tid: ErrorID,\n\tmessage: string,\n\texpected: any,\n\tgot: any\n};"],"mappings":"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","names":["index_exports","__export","ANY_OF","ARRAY_OF","DYNAMIC","ERRORS","OPTIONAL","isTruthy","validate","__toCommonJS","TYPE","x","IS_ARRAY","CONSTRAINT","FC_ARRAY","FC_ANY","FC_DYNAMIC","FC_NESTED","VALIDATION_DEFAULTS","target","schema","options","parseErrors","constraints","flags","parseSchema","errors","checkValue","e","schemaProperty","translate","raw","constraint","error","intermediate","wrap","translated","wrapped","i","propertyName","value","constraintFunctional","v","validationCall","success","validationResult","nothrow","targetKeys","schemaKeys","key","subConstraints","stringifyKey","subErrors","extraProperties","k","indexCounter","item","subSchemas","passed","counter","subSchema","subParseErrors","caseErrors","constraintCallback","cb","id","expected","got","f","failMessageId"]}
@@ -0,0 +1,60 @@
1
+ declare const ERRORS: {
2
+ noProperty: string;
3
+ typeMismatch: string;
4
+ customFail: string;
5
+ extraProperty: string;
6
+ exceptionOnCustom: string;
7
+ notArray: string;
8
+ targetIsNull: string;
9
+ functionExpected: string;
10
+ objectExpected: string;
11
+ anyFailed: string;
12
+ parsingError: string;
13
+ deadError: string;
14
+ };
15
+ declare const CONSTRAINT: unique symbol;
16
+ declare const FC_ARRAY: unique symbol;
17
+ declare const FC_ANY: unique symbol;
18
+ declare const FC_DYNAMIC: unique symbol;
19
+ declare const FC_NESTED: unique symbol;
20
+ declare const VALIDATION_DEFAULTS: {
21
+ allErrors: boolean;
22
+ allowExtraProperties: boolean;
23
+ };
24
+ /**
25
+ * @param options Validation options:
26
+ *
27
+ * - allErrors (true) : return all errors instead of interrupting after first fail
28
+ * - allowExtraProperties (true) : If false, adds specific error to a list for every property of target object not present in schema
29
+ * @return Array of errors
30
+ */
31
+ declare function validate(target: any, schema?: Schema, options?: ValidationOptions): Error[];
32
+ declare const OPTIONAL: unique symbol;
33
+ declare function ARRAY_OF(constraints: Schema): Constraint;
34
+ declare function ANY_OF(constraints: Schema[]): Constraint;
35
+ declare function DYNAMIC(constraints: Schema): Constraint;
36
+ declare function isTruthy<T>(value: T): value is NonNullable<T>;
37
+ type ErrorID = keyof typeof ERRORS;
38
+ type TypeofValueSchema = "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" | "array";
39
+ type FunctionSchema = (value: any) => boolean | any;
40
+ type OptionalFlag = typeof OPTIONAL;
41
+ type ForbiddenConstraintFlag = typeof FC_ARRAY | typeof FC_ANY | typeof FC_DYNAMIC | typeof FC_NESTED;
42
+ type Constraint = {
43
+ type: typeof CONSTRAINT;
44
+ validation: ((value: any) => any) | ForbiddenConstraintFlag;
45
+ failMessageId: ErrorID;
46
+ expected: Schema;
47
+ };
48
+ type Schema = TypeofValueSchema | FunctionSchema | OptionalFlag | {
49
+ [property: keyof any]: Schema;
50
+ } | Schema[] | Constraint;
51
+ type ValidationOptions = Partial<typeof VALIDATION_DEFAULTS>;
52
+ type Error = {
53
+ propertyName: string;
54
+ id: ErrorID;
55
+ message: string;
56
+ expected: any;
57
+ got: any;
58
+ };
59
+
60
+ export { ANY_OF, ARRAY_OF, DYNAMIC, ERRORS, type ErrorID, OPTIONAL, type Schema, isTruthy, validate };
@@ -0,0 +1,60 @@
1
+ declare const ERRORS: {
2
+ noProperty: string;
3
+ typeMismatch: string;
4
+ customFail: string;
5
+ extraProperty: string;
6
+ exceptionOnCustom: string;
7
+ notArray: string;
8
+ targetIsNull: string;
9
+ functionExpected: string;
10
+ objectExpected: string;
11
+ anyFailed: string;
12
+ parsingError: string;
13
+ deadError: string;
14
+ };
15
+ declare const CONSTRAINT: unique symbol;
16
+ declare const FC_ARRAY: unique symbol;
17
+ declare const FC_ANY: unique symbol;
18
+ declare const FC_DYNAMIC: unique symbol;
19
+ declare const FC_NESTED: unique symbol;
20
+ declare const VALIDATION_DEFAULTS: {
21
+ allErrors: boolean;
22
+ allowExtraProperties: boolean;
23
+ };
24
+ /**
25
+ * @param options Validation options:
26
+ *
27
+ * - allErrors (true) : return all errors instead of interrupting after first fail
28
+ * - allowExtraProperties (true) : If false, adds specific error to a list for every property of target object not present in schema
29
+ * @return Array of errors
30
+ */
31
+ declare function validate(target: any, schema?: Schema, options?: ValidationOptions): Error[];
32
+ declare const OPTIONAL: unique symbol;
33
+ declare function ARRAY_OF(constraints: Schema): Constraint;
34
+ declare function ANY_OF(constraints: Schema[]): Constraint;
35
+ declare function DYNAMIC(constraints: Schema): Constraint;
36
+ declare function isTruthy<T>(value: T): value is NonNullable<T>;
37
+ type ErrorID = keyof typeof ERRORS;
38
+ type TypeofValueSchema = "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" | "array";
39
+ type FunctionSchema = (value: any) => boolean | any;
40
+ type OptionalFlag = typeof OPTIONAL;
41
+ type ForbiddenConstraintFlag = typeof FC_ARRAY | typeof FC_ANY | typeof FC_DYNAMIC | typeof FC_NESTED;
42
+ type Constraint = {
43
+ type: typeof CONSTRAINT;
44
+ validation: ((value: any) => any) | ForbiddenConstraintFlag;
45
+ failMessageId: ErrorID;
46
+ expected: Schema;
47
+ };
48
+ type Schema = TypeofValueSchema | FunctionSchema | OptionalFlag | {
49
+ [property: keyof any]: Schema;
50
+ } | Schema[] | Constraint;
51
+ type ValidationOptions = Partial<typeof VALIDATION_DEFAULTS>;
52
+ type Error = {
53
+ propertyName: string;
54
+ id: ErrorID;
55
+ message: string;
56
+ expected: any;
57
+ got: any;
58
+ };
59
+
60
+ export { ANY_OF, ARRAY_OF, DYNAMIC, ERRORS, type ErrorID, OPTIONAL, type Schema, isTruthy, validate };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ var T={noProperty:"Required property not present",typeMismatch:"Provided type is not allowed by schema",customFail:"Custom validation function failed",extraProperty:"Provided object contains properties not present in schema",exceptionOnCustom:"Exception thrown during constraint validation",notArray:"Tried using ARRAY_OF constraint on non-array value",targetIsNull:"Passed object or array item is null",functionExpected:"Expected function as dynamic constraint",objectExpected:"Expected object",anyFailed:"None of ANY_OF constraints are met",parsingError:"Schema parsing error",deadError:"This error never occurs (i guess)"},R=t=>n=>typeof n==t,I=t=>Array.isArray(t),A=Symbol(),S=Symbol(),E=Symbol(),F=Symbol(),x=Symbol(),k={allErrors:!0,allowExtraProperties:!0};function D(t,n={},c={}){c=Object.assign(k,c);let s=[],[r,p]=m(n,s);p.length>0&&console.error("Flags can't be used at schema's root");let o=b("",t,r,c);return o.push(...s),o.forEach(y=>{y.propertyName?.startsWith(".")&&(y.propertyName=y.propertyName.slice(1))}),o}function m(t,n){function c(e){if(!e)return null;if(typeof e=="object"&&"type"in e&&e.type===A)return e;if(Array.isArray(e))return e.map(c).filter(i=>i);switch(typeof e){case"string":return e=="array"?f(I,"typeMismatch",e):f(R(e),"typeMismatch",e);case"function":return f(e,"customFail",e);case"object":return f(x,"deadError",e);case"symbol":return e}return n.push(u("<schema>","parsingError",void 0,e)),null}let s=c(t)||[];function r(e){return Array.isArray(e)?e:[e]}let p=r(s),o=p.filter(e=>typeof e!="symbol"&&e.type==A),y=p.filter(e=>typeof e=="symbol");return[o,y]}function b(t,n,c,s){let r=[];function p(o){return typeof o=="function"}for(let o of c){let y=o.validation;if(p(y)){let[e,i]=_(()=>y(n));e?i||r.push(u(t,o.failMessageId,"[function]",n)):r.push(u(t,"exceptionOnCustom",void 0,i?.toString()));continue}switch(o.validation){case x:{if(typeof n!="object"||!n){r.push(u(t,"objectExpected","object",n));break}let e=Object.keys(n),i=Object.keys(o.expected);for(let a of i){let[l,h]=m(o.expected[a],r);if(!e.includes(a)){h.includes(N)||r.push(u(`${d(t)}.${a}`,"noProperty"));continue}let g=b(`${d(t)}.${a}`,n[a],l,s);r.push(...g)}if(!s.allowExtraProperties){let a=e.filter(l=>!i.includes(l));a.length>0&&r.push(...a.map(l=>u(`${d(t)}.${l}`,"extraProperty")))}break}case S:{if(!Array.isArray(n)){r.push(u(t,"notArray","array",typeof n));break}let[e,i]=m(o.expected,r),a=0;for(let l of n){let h=b(`${d(t)}[${a}]`,l,e,s);r.push(...h),++a}break}case E:{let e=o.expected,i=[],a=!1,l=0;for(let h of e){let g=[],[O,P]=m(h,g),C=b(`${d(t)}.<any#${l}>`,n,O,s);if(C.push(...g),++l,i.push(C),C.length==0){a=!0;break}}a||r.push(u(t,"anyFailed",void 0,i));break}case F:{let e=o.expected;if(typeof e!="function"){r.push(u(t,"functionExpected","function",typeof e));break}let[i,a]=m(e(n),r),l=b(t,n,i,s);r.push(...l);break}}if(!s.allErrors&&r.length>0)break}return r}function _(t){try{return[!0,t()]}catch(n){return[!1,n]}}function d(t){return typeof t=="symbol"?"[symbol]":String(t)}function u(t,n,c,s){return{propertyName:d(t),id:n,message:T[n],expected:c,got:s}}function f(t,n,c){return{type:A,validation:t,failMessageId:n,expected:c}}var N=Symbol();function Y(t){return arguments.length>1&&console.error("Got more than one argument to ARRAY_OF. Did you mean to pass an array of constraints?"),f(S,"deadError",t)}function j(t){return arguments.length>1?f(E,"deadError",Array.from(arguments)):Array.isArray(t)?f(E,"deadError",t):f(E,"deadError",[t])}function M(t){return f(F,"deadError",t)}function $(t){return!!t}export{j as ANY_OF,Y as ARRAY_OF,M as DYNAMIC,T as ERRORS,N as OPTIONAL,$ as isTruthy,D as validate};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export const ERRORS = {\n\tnoProperty: \"Required property not present\",\n\ttypeMismatch: \"Provided type is not allowed by schema\",\n\tcustomFail: \"Custom validation function failed\",\n\textraProperty: \"Provided object contains properties not present in schema\",\n\texceptionOnCustom: \"Exception thrown during constraint validation\",\n\tnotArray: \"Tried using ARRAY_OF constraint on non-array value\",\n\ttargetIsNull: \"Passed object or array item is null\",\n\tfunctionExpected: \"Expected function as dynamic constraint\",\n\tobjectExpected: \"Expected object\",\n\tanyFailed: \"None of ANY_OF constraints are met\",\n\tparsingError: \"Schema parsing error\",\n\tdeadError: \"This error never occurs (i guess)\"\n};\n\nconst TYPE = (t: string) => (x: any) => typeof x == t;\nconst IS_ARRAY = (x: any) => Array.isArray(x);\n\nconst CONSTRAINT = Symbol();\nconst FC_ARRAY = Symbol(); // https://www.youtube.com/watch?v=qSqXGeJJBaI\nconst FC_ANY = Symbol();\nconst FC_DYNAMIC = Symbol();\nconst FC_NESTED = Symbol();\n\nconst VALIDATION_DEFAULTS = {\n\tallErrors: true,\n\tallowExtraProperties: true\n};\n\n/**\n* @param options Validation options:\n*\n* - allErrors (true) : return all errors instead of interrupting after first fail\n* - allowExtraProperties (true) : If false, adds specific error to a list for every property of target object not present in schema\n* @return Array of errors\n */\nexport function validate(target: any, schema: Schema = {}, options: ValidationOptions = {}) {\n\toptions = Object.assign(VALIDATION_DEFAULTS, options)\n\n\tconst parseErrors: Error[] = [];\n\tconst [constraints, flags] = parseSchema(schema, parseErrors);\n\n\tif (flags.length > 0) {\n\t\tconsole.error(\"Flags can't be used at schema's root\")\n\t}\n\n\tconst errors = checkValue(\"\", target, constraints, options);\n\terrors.push(...parseErrors)\n\terrors.forEach(e => {\n\t\tif (e.propertyName?.startsWith(\".\"))\n\t\t\te.propertyName = e.propertyName.slice(1);\n\t});\n\n\treturn errors;\n}\n\nfunction parseSchema(schemaProperty: Schema, errors: Error[]): [Constraint[], OptionalFlag[]] {\n\tfunction translate(raw: Schema): Constraint | Constraint[] | OptionalFlag | null {\n\t\tif (!raw) return null;\n\n\t\tif (\n\t\t\ttypeof raw === \"object\" &&\n\t\t\t\"type\" in raw &&\n\t\t\traw.type === CONSTRAINT\n\t\t)\n\t\t\treturn raw as Constraint;\n\n\t\tif (Array.isArray(raw)) {\n\t\t\treturn raw.map(translate).filter(raw => raw) as Constraint | Constraint[] | OptionalFlag;\n\t\t} else {\n\t\t\tswitch (typeof raw) {\n\t\t\t\tcase (\"string\"): {\n\t\t\t\t\tif (raw == \"array\")\n\t\t\t\t\t\treturn constraint(IS_ARRAY, \"typeMismatch\", raw);\n\t\t\t\t\telse\n\t\t\t\t\t\treturn constraint(TYPE(raw as TypeofValueSchema), \"typeMismatch\", raw);\n\t\t\t\t}\n\t\t\t\tcase (\"function\"): {\n\t\t\t\t\treturn constraint(raw as FunctionSchema, \"customFail\", raw);\n\t\t\t\t}\n\t\t\t\tcase (\"object\"): {\n\t\t\t\t\treturn constraint(FC_NESTED, \"deadError\", raw);\n\t\t\t\t}\n\t\t\t\tcase (\"symbol\"): {\n\t\t\t\t\treturn raw as OptionalFlag;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\terrors.push(error(\"<schema>\", \"parsingError\", undefined, raw));\n\t\treturn null;\n\t}\n\n\tconst intermediate = translate(schemaProperty) || [];\n\n\tfunction wrap(\n\t\ttranslated: OptionalFlag | Constraint | Constraint[]\n\t): (OptionalFlag | Constraint)[] {\n\t\tif (!Array.isArray(translated))\n\t\t\treturn [translated];\n\t\treturn translated;\n\t}\n\n\tconst wrapped = wrap(intermediate);\n\tconst constraints = wrapped.filter(i => typeof i !== \"symbol\" && i.type == CONSTRAINT);\n\tconst flags = wrapped.filter(i => typeof i === \"symbol\");\n\n\treturn [\n\t\tconstraints as Constraint[],\n\t\tflags as OptionalFlag[]\n\t];\n}\n\nfunction checkValue(\n\tpropertyName: keyof any,\n\tvalue: any,\n\tconstraints: Constraint[],\n\toptions: ValidationOptions\n): Error[] {\n\tconst errors = [];\n\n\tfunction constraintFunctional(v: Constraint[\"validation\"]): v is (value: any) => any {\n\t\treturn typeof v == \"function\";\n\t}\n\n\tfor (let constraint of constraints) {\n\t\tconst validationCall = constraint.validation;\n\t\tif (constraintFunctional(validationCall)) {\n\t\t\tconst [success, validationResult] = nothrow(() => validationCall(value));\n\t\t\tif (!success) {\n\t\t\t\terrors.push(error(propertyName, \"exceptionOnCustom\", undefined, validationResult?.toString()));\n\t\t\t} else if (!validationResult) {\n\t\t\t\terrors.push(error(propertyName, constraint.failMessageId, \"[function]\", value));\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tswitch(constraint.validation) {\n\t\t\tcase FC_NESTED: {\n\t\t\t\tif (typeof value != \"object\" || !value) {\n\t\t\t\t\terrors.push(error(propertyName, \"objectExpected\", \"object\", value));\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tconst targetKeys = Object.keys(value);\n\t\t\t\tconst schemaKeys = Object.keys(constraint.expected!);\n\n\t\t\t\tfor (let key of schemaKeys) {\n\t\t\t\t\t// @ts-ignore // FC_NESTED guarantees expected scheme to be an object\n\t\t\t\t\tconst [subConstraints, flags] = parseSchema(constraint.expected[key], errors);\n\n\t\t\t\t\tif (!targetKeys.includes(key)) {\n\t\t\t\t\t\tif (!flags.includes(OPTIONAL)) {\n\t\t\t\t\t\t\terrors.push(error(`${stringifyKey(propertyName)}.${key}`, \"noProperty\"));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst subErrors = checkValue(`${stringifyKey(propertyName)}.${key}`, value[key], subConstraints, options);\n\t\t\t\t\terrors.push(...subErrors);\n\t\t\t\t}\n\n\t\t\t\tif (!options.allowExtraProperties) {\n\t\t\t\t\tconst extraProperties = targetKeys.filter(k => !schemaKeys.includes(k));\n\t\t\t\t\tif (extraProperties.length > 0) {\n\t\t\t\t\t\terrors.push(...extraProperties.map(k => error(`${stringifyKey(propertyName)}.${k}`, \"extraProperty\")))\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase FC_ARRAY: {\n\t\t\t\tif (!Array.isArray(value)) {\n\t\t\t\t\terrors.push(error(propertyName, \"notArray\", \"array\", typeof value))\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tconst [subConstraints, flags] = parseSchema(constraint.expected, errors);\n\n\t\t\t\tlet indexCounter = 0;\n\t\t\t\tfor (let item of value) {\n\t\t\t\t\tconst subErrors = checkValue(`${stringifyKey(propertyName)}[${indexCounter}]`, item, subConstraints, options);\n\t\t\t\t\terrors.push(...subErrors);\n\t\t\t\t\t++indexCounter;\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase FC_ANY: {\n\t\t\t\tconst subSchemas = constraint.expected as Schema[];\n\n\t\t\t\tconst subErrors = [];\n\t\t\t\tlet passed = false;\n\t\t\t\tlet counter = 0;\n\n\t\t\t\tfor (let subSchema of subSchemas) {\n\t\t\t\t\tconst subParseErrors: Error[] = [];\n\t\t\t\t\tconst [subConstraints, flags] = parseSchema(subSchema, subParseErrors);\n\t\t\t\t\tconst caseErrors = checkValue(`${stringifyKey(propertyName)}.<any#${counter}>`, value, subConstraints, options);\n\t\t\t\t\tcaseErrors.push(...subParseErrors);\n\t\t\t\t\t++counter;\n\t\t\t\t\tsubErrors.push(caseErrors);\n\t\t\t\t\tif (caseErrors.length == 0) {\n\t\t\t\t\t\tpassed = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!passed) errors.push(error(propertyName, \"anyFailed\", undefined, subErrors));\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase FC_DYNAMIC: {\n\t\t\t\tconst constraintCallback = constraint.expected;\n\n\t\t\t\tif (typeof constraintCallback != \"function\") {\n\t\t\t\t\terrors.push(error(propertyName, \"functionExpected\", \"function\", typeof constraintCallback))\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tconst [subConstraints, flags] = parseSchema(constraintCallback(value), errors)\n\t\t\t\tconst subErrors = checkValue(propertyName, value, subConstraints, options);\n\t\t\t\terrors.push(...subErrors);\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!options.allErrors && errors.length > 0) break;\n\t}\n\treturn errors;\n}\n\nfunction nothrow<T>(cb: () => T): [true, T] | [false, unknown] {\n\ttry {\n\t\treturn [true, cb()];\n\t} catch (e) {\n\t\treturn [false, e];\n\t}\n}\n\nfunction stringifyKey(k: keyof any): string {\n\treturn typeof k === \"symbol\" ? \"[symbol]\" : String(k);\n}\n\nfunction error(\n\tpropertyName: keyof any,\n\tid: ErrorID,\n\texpected?: string,\n\tgot?: any\n) {\n\treturn {\n\t\tpropertyName: stringifyKey(propertyName),\n\t\tid,\n\t\tmessage: ERRORS[id],\n\t\texpected,\n\t\tgot\n\t};\n}\n\nfunction constraint(\n\tf: Constraint[\"validation\"],\n\tfailMessageId: Constraint[\"failMessageId\"],\n\texpected: Constraint[\"expected\"]\n): Constraint {\n\treturn {\n\t\ttype: CONSTRAINT,\n\t\tvalidation: f,\n\t\tfailMessageId: failMessageId,\n\t\texpected: expected\n\t};\n}\n\nexport const OPTIONAL = Symbol();\nexport function ARRAY_OF(constraints: Schema) {\n\tif (arguments.length > 1) {\n\t\tconsole.error(\"Got more than one argument to ARRAY_OF. Did you mean to pass an array of constraints?\");\n\t}\n\treturn constraint(FC_ARRAY, \"deadError\", constraints);\n}\nexport function ANY_OF(constraints: Schema[]) {\n\tif (arguments.length > 1) {\n\t\treturn constraint(FC_ANY, \"deadError\", Array.from(arguments));\n\t} else {\n\t\tif (Array.isArray(constraints)) {\n\t\t\treturn constraint(FC_ANY, \"deadError\", constraints);\n\t\t} else {\n\t\t\treturn constraint(FC_ANY, \"deadError\", [constraints]);\n\t\t}\n\t}\n}\nexport function DYNAMIC(constraints: Schema) {\n\treturn constraint(FC_DYNAMIC, \"deadError\", constraints);\n}\n\nexport function isTruthy<T>(value: T): value is NonNullable<T> {\n\treturn Boolean(value);\n}\n\nexport type ErrorID = keyof typeof ERRORS;\n\ntype TypeofValueSchema = \n\t| \"bigint\"\n\t| \"boolean\"\n\t| \"function\"\n\t| \"number\"\n\t| \"object\"\n\t| \"string\"\n\t| \"symbol\"\n\t| \"undefined\"\n\t| \"array\"; // array is special case\ntype FunctionSchema = (value: any) => boolean | any;\ntype OptionalFlag = typeof OPTIONAL;\n\ntype ForbiddenConstraintFlag =\n\t| typeof FC_ARRAY\n\t| typeof FC_ANY\n\t| typeof FC_DYNAMIC\n\t| typeof FC_NESTED\ntype Constraint = {\n\ttype: typeof CONSTRAINT,\n\tvalidation:\n\t\t| ((value: any) => any)\n\t\t| ForbiddenConstraintFlag,\n\tfailMessageId: ErrorID,\n\texpected: Schema\n}\n\nexport type Schema = \n\t| TypeofValueSchema\n\t| FunctionSchema\n\t| OptionalFlag\n\t| { [property: keyof any]: Schema }\n\t| Schema[]\n\t| Constraint;\n\ntype ValidationOptions = Partial<typeof VALIDATION_DEFAULTS>;\n\ntype Error = {\n\tpropertyName: string,\n\tid: ErrorID,\n\tmessage: string,\n\texpected: any,\n\tgot: any\n};"],"mappings":"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","names":["ERRORS","TYPE","x","IS_ARRAY","CONSTRAINT","FC_ARRAY","FC_ANY","FC_DYNAMIC","FC_NESTED","VALIDATION_DEFAULTS","validate","target","schema","options","parseErrors","constraints","flags","parseSchema","errors","checkValue","e","schemaProperty","translate","raw","constraint","error","intermediate","wrap","translated","wrapped","i","propertyName","value","constraintFunctional","v","validationCall","success","validationResult","nothrow","targetKeys","schemaKeys","key","subConstraints","OPTIONAL","stringifyKey","subErrors","extraProperties","k","indexCounter","item","subSchemas","passed","counter","subSchema","subParseErrors","caseErrors","constraintCallback","cb","id","expected","got","f","failMessageId","ARRAY_OF","ANY_OF","DYNAMIC","isTruthy"]}
package/license.md CHANGED
@@ -1,15 +1,15 @@
1
- DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2
- Version 2, December 2004
3
-
4
- Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
5
-
6
- Everyone is permitted to copy and distribute verbatim or modified
7
- copies of this license document, and changing it is allowed as long
8
- as the name is changed.
9
-
10
- DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
-
13
- 0. You just DO WHAT THE FUCK YOU WANT TO.
14
-
1
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2
+ Version 2, December 2004
3
+
4
+ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
5
+
6
+ Everyone is permitted to copy and distribute verbatim or modified
7
+ copies of this license document, and changing it is allowed as long
8
+ as the name is changed.
9
+
10
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
+
13
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
14
+
15
15
 
package/package.json CHANGED
@@ -1,11 +1,22 @@
1
1
  {
2
2
  "name": "arstotzka",
3
- "version": "0.13.3",
3
+ "version": "0.14.2",
4
4
  "description": "JS validation utility",
5
- "main": "index.js",
5
+ "main": "./dist/index.js",
6
6
  "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "main": "./dist/index.cjs",
10
+ "module": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
7
17
  "scripts": {
8
- "test": "node tests.js"
18
+ "build": "tsup src/index.ts",
19
+ "test": "node src/tests.js"
9
20
  },
10
21
  "keywords": [
11
22
  "JSON",
@@ -25,5 +36,9 @@
25
36
  "bugs": {
26
37
  "url": "https://github.com/MilesVII/arstotzka/issues"
27
38
  },
28
- "homepage": "https://github.com/MilesVII/arstotzka#readme"
39
+ "homepage": "https://github.com/MilesVII/arstotzka#readme",
40
+ "devDependencies": {
41
+ "tsup": "^8.5.0",
42
+ "typescript": "^5.9.3"
43
+ }
29
44
  }
package/readme.md CHANGED
@@ -1,92 +1,92 @@
1
- # Arstotzka
2
- JS data validation tool featuring laconic schema format and a sexy name.
3
-
4
- ## Usage
5
-
6
- See detailed usage example in **[usage.js](https://github.com/MilesVII/arstotzka/blob/master/usage.js)**
7
-
8
- ```
9
- const schema = {
10
- id: "number",
11
- username: ["string", x => x.length < 10],
12
- post: "string",
13
- comments: Arstotzka.ARRAY_OF({
14
- author: ["string", Arstotzka.OPTIONAL],
15
- text: "string"
16
- })
17
- }
18
-
19
- const goodData = {
20
- id: 1337,
21
- username: "mr.hands",
22
- post: "Henlo there",
23
- comments: [
24
- {author: "Johnny", text: "henlo"},
25
- {text: "hey hey"},
26
- {author: "miles", text: "birb"},
27
- ]
28
- }
29
-
30
- console.log(Arstotzka.validate(goodData, schema, {allowExtraProperties: false})); // Prints an array of errors
31
- ```
32
-
33
- ### Import:
34
- Arstotzka is an ES6 module, so:
35
- ```
36
- import * as Arstotzka from "arstotzka";
37
- ```
38
- or, if you are okay with polluting namespace:
39
- ```
40
- import { validate, OPTIONAL, ARRAY_OF, ANY_OF, DYNAMIC } from "arstotzka";
41
- ```
42
-
43
-
44
- ### Schema format:
45
- Schema is a value that can be either of
46
- - **string**: such schema will make validator check coresponding value's type with [typeof operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof), with exception of "array" constraint -- validator will [treat this type](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) as special case;
47
- `"string"`, `"number"`, `"array"`
48
-
49
- - **function**: such schema will make validator call it, pass corresponding value to it, and log an error if returned value is falsy;
50
- `x => !isNaN(x)`, `x => x.length < 10`
51
-
52
- - **object**: object schema requires corresponding value to also be an object, and will recursively match it's properties against provided value;
53
- `{name: "string", age: x => x.length > 21}`
54
-
55
- - **array**, which elements are any of above. That will require a value it matched against to fullfill *every* requirement;
56
- `["string", x => x != x.trim()]`, `["number", x => x >= 0, x => x % 1 === 0]`
57
-
58
- - **Arstotzka.ARRAY_OF()**: the function accepts any of above and returns a special constraint appliable to an array of values;
59
- `Arstotzka.ARRAY_OF("number")`, `Arstotzka.ARRAY_OF({id: "number", text: "string"})`, `Arstotzka.ARRAY_OF(["number", x => x > 0])`, `Arstotzka.ARRAY_OF(Arstotzka.ARRAY_OF("number"))`
60
-
61
- - **Arstotzka.ANY_OF()**: the function accepts array or vararg of schemas and returns a special constraint, which will produce a specific error only if every provided schema is violated;
62
- `Arstotzka.ANY_OF(["number", ["string", x => !isNaN(parseInt(x))]])`
63
-
64
- - **Arstotzka.DYNAMIC()**: the function accepts a callback that should return a valid schema, allowing to define schema at runtime;
65
- `Arstotzka.DYNAMIC(x => dynSchema[x.type])`
66
-
67
- - **Arstotzka.OPTIONAL**: unlike others, this schema doesn't imply any requirements, but prevents validator from logging an error in case it's property is not present in target object;
68
-
69
- Applying a schema to a property that is an object can be done by combining **object** schema with anything via **array** schema;
70
-
71
-
72
- ### Available options:
73
- - **allErrors** (default is `true`) : If false, will return errors as soon as encountered, interrupting validation
74
- - **allowExtraProperties** (default is `true`) : If false, adds specific error to a list for every property of target object not present in schema
75
-
76
-
77
- ### Error format
78
- ```
79
- { // Example error item:
80
- propertyName: 'age', // Name of a property that failed validation
81
- id: 'typeMismatch', // String describing type of an error. Can be used to localize error message
82
- message: 'Provided type is not allowed by schema', // Error message that coressponds to error id
83
- expected: 'number', // Arbitrary-purpose fields
84
- got: null
85
- }
86
- ```
87
-
88
- All error ids and messages can be found at `Arstotzka.ERRORS`
89
-
90
-
91
- ## License
92
- Shared under WTFPL license.
1
+ # Arstotzka
2
+ JS data validation tool featuring laconic schema format and a sexy name.
3
+
4
+ ## Usage
5
+
6
+ See detailed usage example in **[usage.js](https://github.com/MilesVII/arstotzka/blob/master/usage.js)**
7
+
8
+ ```
9
+ const schema = {
10
+ id: "number",
11
+ username: ["string", x => x.length < 10],
12
+ post: "string",
13
+ comments: Arstotzka.ARRAY_OF({
14
+ author: ["string", Arstotzka.OPTIONAL],
15
+ text: "string"
16
+ })
17
+ }
18
+
19
+ const goodData = {
20
+ id: 1337,
21
+ username: "mr.hands",
22
+ post: "Henlo there",
23
+ comments: [
24
+ {author: "Johnny", text: "henlo"},
25
+ {text: "hey hey"},
26
+ {author: "miles", text: "birb"},
27
+ ]
28
+ }
29
+
30
+ console.log(Arstotzka.validate(goodData, schema, {allowExtraProperties: false})); // Prints an array of errors
31
+ ```
32
+
33
+ ### Import:
34
+ Arstotzka is an ES6 module, so:
35
+ ```
36
+ import * as Arstotzka from "arstotzka";
37
+ ```
38
+ or, if you are okay with polluting namespace:
39
+ ```
40
+ import { validate, OPTIONAL, ARRAY_OF, ANY_OF, DYNAMIC } from "arstotzka";
41
+ ```
42
+
43
+
44
+ ### Schema format:
45
+ Schema is a value that can be either of
46
+ - **string**: such schema will make validator check coresponding value's type with [typeof operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof), with exception of "array" constraint -- validator will [treat this type](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) as special case;
47
+ `"string"`, `"number"`, `"array"`
48
+
49
+ - **function**: such schema will make validator call it, pass corresponding value to it, and log an error if returned value is falsy;
50
+ `x => !isNaN(x)`, `x => x.length < 10`
51
+
52
+ - **object**: object schema requires corresponding value to also be an object, and will recursively match it's properties against provided value;
53
+ `{name: "string", age: x => x.length > 21}`
54
+
55
+ - **array**, which elements are any of above. That will require a value it matched against to fullfill *every* requirement;
56
+ `["string", x => x != x.trim()]`, `["number", x => x >= 0, x => x % 1 === 0]`
57
+
58
+ - **Arstotzka.ARRAY_OF()**: the function accepts any of above and returns a special constraint appliable to an array of values;
59
+ `Arstotzka.ARRAY_OF("number")`, `Arstotzka.ARRAY_OF({id: "number", text: "string"})`, `Arstotzka.ARRAY_OF(["number", x => x > 0])`, `Arstotzka.ARRAY_OF(Arstotzka.ARRAY_OF("number"))`
60
+
61
+ - **Arstotzka.ANY_OF()**: the function accepts array or vararg of schemas and returns a special constraint, which will produce a specific error only if every provided schema is violated;
62
+ `Arstotzka.ANY_OF(["number", ["string", x => !isNaN(parseInt(x))]])`
63
+
64
+ - **Arstotzka.DYNAMIC()**: the function accepts a callback that should return a valid schema, allowing to define schema at runtime;
65
+ `Arstotzka.DYNAMIC(x => dynSchema[x.type])`
66
+
67
+ - **Arstotzka.OPTIONAL**: unlike others, this schema doesn't imply any requirements, but prevents validator from logging an error in case it's property is not present in target object;
68
+
69
+ Applying a schema to a property that is an object can be done by combining **object** schema with anything via **array** schema;
70
+
71
+
72
+ ### Available options:
73
+ - **allErrors** (default is `true`) : If false, will return errors as soon as encountered, interrupting validation
74
+ - **allowExtraProperties** (default is `true`) : If false, adds specific error to a list for every property of target object not present in schema
75
+
76
+
77
+ ### Error format
78
+ ```
79
+ { // Example error item:
80
+ propertyName: 'age', // Name of a property that failed the validation
81
+ id: 'typeMismatch', // String describing type of an error. Can be used to localize error message
82
+ message: 'Provided type is not allowed by schema', // Error message that coressponds to error id
83
+ expected: 'number', // Arbitrary-purpose fields
84
+ got: null
85
+ }
86
+ ```
87
+
88
+ All error ids and messages can be found at `Arstotzka.ERRORS`
89
+
90
+
91
+ ## License
92
+ Shared under WTFPL license.
package/index.js DELETED
@@ -1,252 +0,0 @@
1
- export const ERRORS = {
2
- noProperty: "Required property not present",
3
- typeMismatch: "Provided type is not allowed by schema",
4
- customFail: "Custom validation function failed",
5
- extraProperty: "Provided object contains properties not present in schema",
6
- exceptionOnCustom: "Exception thrown during constraint validation",
7
- notArray: "Tried using ARRAY_OF constraint on non-array value",
8
- targetIsNull: "Passed object or array item is null",
9
- functionExpected: "Expected function as dynamic constraint",
10
- objectExpected: "Expected object",
11
- anyFailed: "None of ANY_OF constraints are met",
12
- parsingError: "Schema parsing error"
13
- };
14
-
15
- export const OPTIONAL = Symbol();
16
- export function ARRAY_OF(constraints){
17
- if (arguments.length > 1){
18
- console.error("Got more than one argument to ARRAY_OF. Did you mean to pass an array of constraints?");
19
- }
20
- return constraint(FC_ARRAY, null, constraints);
21
- }
22
- export function ANY_OF(constraints){
23
- if (arguments.length > 1){
24
- return constraint(FC_ANY, null, Array.from(arguments));
25
- } else {
26
- if (Array.isArray(constraints)){
27
- return constraint(FC_ANY, null, constraints);
28
- } else {
29
- return constraint(FC_ANY, null, [constraints]);
30
- }
31
- }
32
-
33
- }
34
- export function DYNAMIC(constraints){
35
- return constraint(FC_DYNAMIC, null, constraints);
36
- }
37
-
38
- const TYPE = t => x => typeof x == t;
39
- const IS_ARRAY = x => Array.isArray(x);
40
-
41
- const CONSTRAINT = Symbol();
42
- const FC_ARRAY = Symbol(); // https://www.youtube.com/watch?v=qSqXGeJJBaI
43
- const FC_ANY = Symbol();
44
- const FC_DYNAMIC = Symbol();
45
- const FC_NESTED = Symbol();
46
-
47
- const VALIDATION_DEFAULTS = {
48
- allErrors: true,
49
- allowExtraProperties: true
50
- };
51
-
52
- function safe(cb){
53
- try {
54
- return [true, cb()];
55
- } catch (e) {
56
- return [false, e];
57
- }
58
- }
59
-
60
- function error(propertyName, id, expected, got){
61
- return {
62
- propertyName,
63
- id,
64
- message: ERRORS[id],
65
- expected,
66
- got
67
- };
68
- }
69
-
70
- function constraint(f, failMessageId, expected){
71
- return {
72
- type: CONSTRAINT,
73
- validation: f,
74
- failMessageId: failMessageId,
75
- expected: expected
76
- };
77
- }
78
-
79
- function parseSchema(schemaProperty, errors){
80
- function unify(raw){
81
- if (raw !== null && raw !== undefined){
82
- if (raw.type == CONSTRAINT) return raw;
83
-
84
- if (Array.isArray(raw)){
85
- return raw.map(c => unify(c)).filter(raw => raw !== null);
86
- } else {
87
- switch (typeof raw){
88
- case ("string"): {
89
- if (raw == "array")
90
- return constraint(IS_ARRAY, "typeMismatch", raw);
91
- else
92
- return constraint(TYPE(raw), "typeMismatch", raw);
93
- }
94
- case ("function"): {
95
- return constraint(raw, "customFail", undefined);
96
- }
97
- case ("object"): {
98
- return constraint(FC_NESTED, null, raw);
99
- }
100
- case ("symbol"): {
101
- return raw;
102
- }
103
- }
104
- }
105
- }
106
- errors.push(error("<schema>", "parsingError", undefined, raw));
107
- return null;
108
- }
109
-
110
- let intermediate = unify(schemaProperty) || [];
111
- if (!Array.isArray(intermediate))
112
- intermediate = [intermediate];
113
- return [
114
- intermediate.filter(i => i.type == CONSTRAINT),
115
- intermediate.filter(i => i.type != CONSTRAINT && typeof i == "symbol")
116
- ];
117
- }
118
-
119
- function checkValue(propertyName, value, constraints, options){
120
- const errors = [];
121
- for (let constraint of constraints){
122
- if (typeof constraint.validation == "function"){
123
- const [success, validationResult] = safe(() => constraint.validation(value));
124
- if (!success){
125
- errors.push(error(propertyName, "exceptionOnCustom", undefined, validationResult.toString()));
126
- } else if (!validationResult){
127
- errors.push(error(propertyName, constraint.failMessageId, constraint.expected, value));
128
- }
129
- continue;
130
- }
131
-
132
- switch(constraint.validation){
133
- case FC_NESTED: {
134
- if (typeof value != "object" || value === null || value == undefined){
135
- errors.push(error(propertyName, "objectExpected", "object", value));
136
- break;
137
- }
138
-
139
- const targetKeys = Object.keys(value);
140
- const schemaKeys = Object.keys(constraint.expected);
141
-
142
- for (let key of schemaKeys){
143
- const [subConstraints, flags] = parseSchema(constraint.expected[key], errors);
144
-
145
- if (!targetKeys.includes(key)){
146
- if (!flags.includes(OPTIONAL)){
147
- errors.push(error(`${propertyName}.${key}`, "noProperty"));
148
- }
149
- continue;
150
- }
151
-
152
- const subErrors = checkValue(`${propertyName}.${key}`, value[key], subConstraints, options);
153
- errors.push(...subErrors);
154
- }
155
-
156
- if (!options.allowExtraProperties){
157
- const extraProperties = targetKeys.filter(k => !schemaKeys.includes(k));
158
- if (extraProperties.length > 0){
159
- errors.push(...extraProperties.map(k => error(`${propertyName}.${k}`, "extraProperty")))
160
- }
161
- }
162
-
163
- break;
164
- }
165
- case FC_ARRAY: {
166
- if (!Array.isArray(value)){
167
- errors.push(error(propertyName, "notArray", "array", typeof value))
168
- break;
169
- }
170
-
171
- const [subConstraints, flags] = parseSchema(constraint.expected, errors);
172
-
173
- let indexCounter = 0;
174
- for (let item of value){
175
- const subErrors = checkValue(`${propertyName}[${indexCounter}]`, item, subConstraints, options);
176
- errors.push(...subErrors);
177
- ++indexCounter;
178
- }
179
-
180
- break;
181
- }
182
- case FC_ANY: {
183
- const subSchemas = constraint.expected;
184
-
185
- const subErrors = [];
186
- let passed = false;
187
- let counter = 0;
188
-
189
- for (let subSchema of subSchemas){
190
- const subParseErrors = [];
191
- const [subConstraints, flags] = parseSchema(subSchema, subParseErrors);
192
- const caseErrors = checkValue(`${propertyName}.<any#${counter}>`, value, subConstraints, options);
193
- caseErrors.push(...subParseErrors);
194
- ++counter;
195
- subErrors.push(caseErrors);
196
- if (caseErrors.length == 0) {
197
- passed = true;
198
- break;
199
- }
200
- }
201
-
202
- if (!passed) errors.push(error(propertyName, "anyFailed", undefined, subErrors));
203
-
204
- break;
205
- }
206
- case FC_DYNAMIC: {
207
- const constraintCallback = constraint.expected;
208
-
209
- if (typeof constraintCallback != "function"){
210
- errors.push(error(propertyName, "functionExpected", "function", typeof constraintCallback))
211
- break;
212
- }
213
-
214
- const [subConstraints, flags] = parseSchema(constraintCallback(value), errors)
215
- const subErrors = checkValue(propertyName, value, subConstraints, options);
216
- errors.push(...subErrors);
217
-
218
- break;
219
- }
220
- }
221
-
222
- if (!options.allErrors && errors.length > 0) break;
223
- }
224
- return errors;
225
- }
226
-
227
- /**
228
- * @param options Validation options:
229
- *
230
- * - allErrors (true) : return all errors instead of interrupting after first fail
231
- * - allowExtraProperties (true) : If false, adds specific error to a list for every property of target object not present in schema
232
- * @return Array of errors
233
- */
234
- export function validate(target, schema = {}, options = {}){
235
- options = Object.assign(VALIDATION_DEFAULTS, options)
236
-
237
- const parseErrors = [];
238
- const [constraints, flags] = parseSchema(schema, parseErrors);
239
-
240
- if (flags.length > 0) {
241
- console.error("Flags can't be used at schema's root")
242
- }
243
-
244
- const errors = checkValue("", target, constraints, options);
245
- errors.push(...parseErrors)
246
- errors.forEach(e => {
247
- if (e.propertyName?.startsWith("."))
248
- e.propertyName = e.propertyName.slice(1);
249
- });
250
-
251
- return errors;
252
- }
package/tests.js DELETED
@@ -1,115 +0,0 @@
1
- import * as Arstotzka from "./index.js";
2
-
3
- const schema = {
4
- title: "string",
5
- array: [Arstotzka.ARRAY_OF("number"), x => x.length > 1],
6
- arrayOfObjs: Arstotzka.ARRAY_OF({
7
- id: "number",
8
- name: "string"
9
- }),
10
- notArray: Arstotzka.ARRAY_OF("string"),
11
- arrayOof: Arstotzka.ARRAY_OF(Arstotzka.ARRAY_OF("number")),
12
- positiveArray: Arstotzka.ARRAY_OF(x => x > 0),
13
- nested: {
14
- parseableNumber: [x => !isNaN(parseInt(x, 10))],
15
- anotherNest: [{
16
- phrase: "string",
17
- wordCount: "number"
18
- }, x => x.phrase.split(" ").length == x.wordCount],
19
- missing: []
20
- },
21
- invalidValidator: [x => null.invalid],
22
- optional: ["number", Arstotzka.OPTIONAL]
23
- };
24
-
25
- const testSubject0 = {
26
- title: "1337",
27
- array: [1, 2, 3],
28
- arrayOfObjs: [
29
- {id: 0, name: "_"},
30
- {id: 1, name: "second"},
31
- {id: 2, name: "3"},
32
- ],
33
- notArray: [],
34
- arrayOof: [
35
- [1, 2, 3],
36
- [4, 5, 6],
37
- [1, 2]
38
- ],
39
- positiveArray: [Infinity, 1, 4, 5],
40
- nested: {
41
- parseableNumber: "777",
42
- anotherNest: {
43
- phrase:"henlo",
44
- wordCount: 1
45
- },
46
- missing: 0
47
- },
48
- invalidValidator: "uhm"
49
- };
50
-
51
- const testSubject1 = {
52
- title: 1337,
53
- array: [1, null, 3],
54
- arrayOfObjs: [
55
- {id: 0, name: "_"},
56
- {id: 0,},
57
- {id: "0", name: "_"}
58
- ],
59
- notArray: "[1, 2, 3]",
60
- arrayOof: [
61
- [1, 2, 3],
62
- [4, "5", 6],
63
- null,
64
- [1, 2]
65
- ],
66
- positiveArray: [0, 1, 4, -5],
67
- nested: {
68
- parseableNumber: "seven",
69
- extraProperty: "hey",
70
- anotherNest: {
71
- phrase:"henlo",
72
- wordCount: 2
73
- }
74
- },
75
- invalidValidator: "uhm"
76
- };
77
-
78
- const schema1 = Arstotzka.DYNAMIC(x => dynSchema[x.type]);
79
- const dynSchema = [
80
- {
81
- type: "number",
82
- zero: "string"
83
- },
84
- {
85
- type: "number",
86
- one: ["number", x => x === 1]
87
- }
88
- ];
89
- const testSubject2 = {
90
- type: 0,
91
- zero: "0"
92
- };
93
-
94
- const parseableIntSchema = Arstotzka.ANY_OF(
95
- "number",
96
- ["string", x => !isNaN(parseInt(x))]
97
- );
98
-
99
- // [target, schema, error count]
100
- const tests = [
101
- [null, "array", 1], // null values and non-object constraints
102
- [testSubject0, schema, 1], // Only an error caused by invalid validator
103
- [testSubject1, schema, 13], // Full of errors
104
- ["miles", "string", 0], // Any value can be validated, not only objects
105
- [7, "string", 1], // Same but failed
106
- [testSubject2, schema1, 0], // Dynamic schema
107
- ["7", parseableIntSchema, 0], // Schema with ANY_OF
108
- [testSubject0, null, 1] // Invalid schema
109
- ];
110
-
111
- const testResults = tests
112
- .map(t => Arstotzka.validate(t[0], t[1]).length == t[2])
113
- .map((testPassed, i) => `Test ${i}: ${testPassed ? "PASSED" : "FAILED"}`)
114
- .join("\n");
115
- console.log(testResults);
package/usage.js DELETED
@@ -1,80 +0,0 @@
1
- import * as Arstotzka from "./index.js";
2
-
3
- // This value will be validated against schema described below
4
- const value = {
5
- title: "1337",
6
- array: [1, 2, 3],
7
- arrayOfObjs: [
8
- {id: 0, name: "_"},
9
- {id: 1, name: "second"},
10
- {id: 2, name: "3"},
11
- ],
12
- matrix: [
13
- [1, 2, 3],
14
- [4, 5, 6],
15
- [1, 2, 0]
16
- ],
17
- positiveArray: [Infinity, 1, 4, 5],
18
- nested: {
19
- parseableNumber: "777",
20
- anotherNest: {
21
- phrase:"henlo there",
22
- wordCount: 2
23
- },
24
- justBeThere: 0
25
- },
26
- booleanOrNumber: 0,
27
- dynamic: 69
28
- };
29
-
30
- const schema = { // Require value to be an object
31
- // Require typeof value.title to be "string"
32
- title: "string",
33
-
34
- //Require value.array to be an array of numbers AND to be longer than 1 element
35
- array: [Arstotzka.ARRAY_OF("number"), x => x.length > 1],
36
-
37
- // Require each element of value.arrayOfObjs to be valid according to provided schema
38
- arrayOfObjs: Arstotzka.ARRAY_OF({
39
- id: "number",
40
- name: "string"
41
- }),
42
-
43
- // Require each element of value.matrix to be an array of numbers
44
- matrix: Arstotzka.ARRAY_OF(Arstotzka.ARRAY_OF("number")),
45
-
46
- // Require each element of value.positiveArray be larger than zero, regardless of type
47
- positiveArray: Arstotzka.ARRAY_OF(x => x > 0),
48
-
49
- // Require value.nested to be an object
50
- nested: {
51
- // Require value.nested.parseableNumber to make this function return true
52
- parseableNumber: x => !isNaN(parseInt(x, 10)),
53
- // Require value.nested.anotherNest to be an object AND make provided function return true
54
- anotherNest: [{
55
- phrase: "string",
56
- wordCount: "number"
57
- }, x => x.phrase.split(" ").length == x.wordCount],
58
- // Require value.nested.justBeThere to be present
59
- justBeThere: []
60
- },
61
-
62
- // Require value.optional to be of type "number", but only if present
63
- optional: ["number", Arstotzka.OPTIONAL],
64
-
65
- // Require value.booleanOrNumber to be either boolean, or 0 or 1
66
- booleanOrNumber: Arstotzka.ANY_OF("boolean", x => x === 0 || x === 1),
67
-
68
- // Require value.dynamic to be valid according to schema returned from provided callback (always "number" in that case)
69
- dynamic: Arstotzka.DYNAMIC(x => "number")
70
- };
71
-
72
- // You can call validate() without providing any options. Below are all options with default values
73
- const optionalValidationOptions = {
74
- allowExtraProperties: true,
75
- allErrors: true
76
- };
77
-
78
- const errors = Arstotzka.validate(value, schema, optionalValidationOptions);
79
-
80
- console.log(errors); // Empty, meaning validation is passed