sdj-esm
Version:
Self Described JSON - ESM
249 lines (229 loc) • 9.52 kB
text/typescript
/*
Copyright (c) 2023-2024 Will Rudolph <@willrudolph.com>
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import {DEF_MAX_STR_LEN, ESDJ_TYPE, MAX_OBJ_ARY_LEN, STR_LEN_LNG, STR_LEN_SHRT} from "./statics.js";
import type {FuncJsonValueValidator, JIValue} from "./interfaces.js";
import {each, isArray, isBoolean, isNumber, isObject, isString, isUndefined} from "lodash-es";
import type {IntAny} from "./internal.js";
import {getRegEx} from "../util/regex.js";
export const validSDKey = (checkVal: JIValue) => {
const regEx = getRegEx("sdKey");
return isString(checkVal) ? regEx.test(<string>checkVal) : false;
};
export const validUtcDate: FuncJsonValueValidator = (checkVal: JIValue):boolean =>
Boolean(isNumber(checkVal));
export const validBoolean: FuncJsonValueValidator = (checkVal: JIValue): boolean => {
let rtnValid = false;
if (isString(checkVal)) {
// No allowed "true"/"FALSE" as strings for uniformity and by spec.
rtnValid = false;
} else if (isBoolean(checkVal)) {
rtnValid = true;
}
return rtnValid;
};
export const getStrValidLenFunc = (maxLength: number): FuncJsonValueValidator =>
(checkVal: JIValue): boolean => Boolean(isString(checkVal) && (<string>checkVal).length < maxLength);
export const getValidNumberFunc = (preciseType: string, posOnly: boolean = false): FuncJsonValueValidator =>
(checkVal: JIValue): boolean => {
let rtnValid = false;
if (isNumber(checkVal)) {
rtnValid = true;
switch (preciseType) {
case "float":
rtnValid = Number.isFinite(checkVal);
break;
case "int":
rtnValid = Number.isFinite(checkVal) && Math.floor(checkVal) === checkVal;
break;
default:
// 'none' is also default
}
}
if (rtnValid && posOnly) {
rtnValid = (<number>checkVal >= 0);
}
return rtnValid;
};
export const getValidObjFunc = (objType: string, itemType: string): FuncJsonValueValidator =>
(checkVal: JIValue): boolean => {
let rtnValid = false,
typeArray = ["string", "boolean", "number", "integer", "int-pos"],
validFuncArray= [getStrValidLenFunc(DEF_MAX_STR_LEN), validBoolean, getValidNumberFunc("none"),
getValidNumberFunc("int"), getValidNumberFunc("int", true)],
funcIdx = typeArray.indexOf(itemType),
activeFunc: FuncJsonValueValidator | undefined =
(funcIdx !== -1) ? validFuncArray[typeArray.indexOf(itemType)] : undefined;
if (typeArray.indexOf(itemType) === -1 || !activeFunc) {
throw new Error("--- Internal Dev error resolve missing:" + itemType);
}
// Yes, for now we will verify each array value
if (objType === "array" && isArray(checkVal)) {
// Yes, for now we will verify each key value
const len = (<IntAny[]>checkVal).length;
if (len && len < MAX_OBJ_ARY_LEN) {
rtnValid = true;
each(<IntAny[]>checkVal, (idxValue: IntAny) => {
if (activeFunc) {
if (!activeFunc(<JIValue>idxValue)) {
rtnValid = false;
return false;
}
}
return true;
});
}
} else if (objType === "object" && isObject(checkVal)) {
const len = Object.keys(<object>checkVal).length;
// stupid long key names are not allowed either
if (len && len < MAX_OBJ_ARY_LEN) {
rtnValid = true;
each(<object>checkVal, (value: JIValue, key: string) => {
if (activeFunc) {
if ((!isString(key) || (<string>key).length > DEF_MAX_STR_LEN) || !activeFunc(value)) {
rtnValid = false;
return false;
}
}
return true;
});
}
}
return rtnValid;
};
/*
Description arrays [extendIds, sdItems, parentIds, childIds]
are all limited to unique positive number arrays
validIntArray only confirms positive integer array is present, not uniqueness
*/
export const validIntArray = (checkValue: number[] | undefined): boolean => {
const rtnBool = (checkValue && isArray(checkValue) && checkValue.length > 0);
return (rtnBool) ? getValidObjFunc("array", "int-pos")(<number[]>checkValue) : false;
};
// Fixed name of Std SDJ Items; with all common "alternate spellings" referenced that will be checked and forced
// to common/standardized (first value)
// This is to prevent libraries and individual files from ever close (or near) default SDJ Item validation/confirmation
// also checks "-" -> "_"
// This check occurs after the standard regex for validator values (regEx "typeLexName"),
export interface DefaultInitItem {
item: ESDJ_TYPE;
similarNames: string[];
valid: FuncJsonValueValidator;
}
export const SdjDefaultTypes: DefaultInitItem[] = [{
// sdKey; force close 'name' type to use sdKey
item: ESDJ_TYPE.KEY,
similarNames: ["sd-k", "sd--key", "sd_-key", "sd-_key", "s-d-k", "s-d-key", "sd-ke"],
valid: validSDKey
}, {
// sdId; force close 'name' type to use sdId
item: ESDJ_TYPE.ID,
similarNames: ["sd-id", "sd--id", "sd-_id", "sd_-id", "sd-i", "s-d-id", "s-d_id", "s_d-id"],
valid: getValidNumberFunc("int")
}, {
// standard JS number
item: ESDJ_TYPE.NUM,
similarNames: ["number", "num-b", "num-ber", "num-b"],
valid: getValidNumberFunc("none"),
}, {
// number forced/confirmed to integer
item: ESDJ_TYPE.INT,
similarNames: ["intr", "integr", "integ", "integer", "num-int", "int-num", "intnum", "numint", "numbint", "intnumb"],
valid: getValidNumberFunc("int"),
}, {
// number parsed for floating point
item: ESDJ_TYPE.FLT,
similarNames: ["flop", "float", "fltpt", "num-flt", "num-float", "floatnum", "fltnum"],
valid: getValidNumberFunc("float"),
}, {
// standard boolean
item: ESDJ_TYPE.BOOL,
similarNames: ["boolean", "bolean", "boolea", "boole", "b-oo", "bo-l"],
valid: validBoolean
}, {
// string default - max length 64 characters
item: ESDJ_TYPE.STR,
similarNames: ["string", "stringdef", "str-def", "string-def"],
valid: getStrValidLenFunc(DEF_MAX_STR_LEN),
}, {
// string large - max 1000 characters
item: ESDJ_TYPE.STRL,
similarNames: ["str-lg", "st-lg", "str-l", "string-lg", "string-l"],
valid: getStrValidLenFunc(STR_LEN_LNG)
}, {
// string short - max length 16 characters
item: ESDJ_TYPE.STRS,
similarNames: ["str-sh", "st-sm", "str-sm", "str-s", "string-sm", "string-s", "string-sh"],
valid: getStrValidLenFunc(STR_LEN_SHRT)
}, {
// UTC date format - throws out old/bad dates
item: ESDJ_TYPE.UTC,
similarNames: ["date-utc", "dateutc", "utcdate", "utc-date", "u-date", "date-u"],
valid: validUtcDate
}, {
// Array of boolean values [true,false,..]
item: ESDJ_TYPE.ARY_BOOL,
similarNames: ["ary-bol", "arraybol", "array-bol"],
valid: getValidObjFunc("array", "boolean")
}, {
// Array of number values [33, 4, 5.3, .322, 0, -2...]
item: ESDJ_TYPE.ARY_NUM,
similarNames: ["ary-num", "arraynum", "array-num"],
valid: getValidObjFunc("array", "number")
}, {
// TODO Confirm Short and long as they use the same function
// Array string Short - ["alpha", "beta", "gamma",...] - each entry max string length of 16 characters
item: ESDJ_TYPE.ARY_STR_SH,
similarNames: ["arystrsht", "arraystr", "array-str", "ary-str-s","array-str-s"],
valid: getValidObjFunc("array", "string")
}, {
// Array string Default - ["alpha..", "beta..", "gamma...", ...] - each entry max string length of 64 characters
item: ESDJ_TYPE.ART_STR_DF,
similarNames: ["arystr", "ary-str", "array-def", "arystrdef", "array-str-d"],
valid: getValidObjFunc("array", "string"),
}, {
// Obj:key Strings - {"alpha": "text1", "beta": "text2", ...} each entry max string length of 64 characters
item: ESDJ_TYPE.OBJ_STR,
similarNames: ["obj-str", "object-str", "objectstr"],
valid: getValidObjFunc("object", "string")
}, {
// Obj:key Booleans - {"alpha": true, "beta": false, ...}
item: ESDJ_TYPE.OBJ_BOOL,
similarNames: ["obj-bool", "object-bool", "objectbool", "obj-bool"],
valid: getValidObjFunc("object", "boolean"),
}, {
// Obj:key Number(JS)s - {"alpha": 1, "beta": 2, ..}
item: ESDJ_TYPE.OBJ_NUM,
similarNames: ["obj-num", "object-num", "objectnum", "obj-number"],
valid: getValidObjFunc("object", "number"),
}, {
// SdIndex Key validator
item: ESDJ_TYPE.INDEX,
similarNames: ["sdjidx", "sdidx,", "sd-idx", "sdj-idx", "sd--idx", "sdinx", "sdjindx", "sdjindex"],
valid: getValidNumberFunc("int")
}
];
export const rtnSdjItemName = (nameStr: string): string | undefined => {
let rtnName: string | undefined = undefined;
each(SdjDefaultTypes, (itemType: DefaultInitItem) => {
if (itemType.item === nameStr) {
return false;
} else {
each (itemType.similarNames, (strVal: string ) => {
const underScoreCheck = strVal.replace("-", "_");
if (strVal === nameStr || nameStr === underScoreCheck) {
rtnName = itemType.item;
return false;
} else {
return true;
}
});
return !!isUndefined(rtnName);
}
});
return rtnName;
};
export const validStrArray = getValidObjFunc("array", "string");