cocochex
v1.0.4
Published
Tiny, dependency-free schema validator and transformer for plain JavaScript objects.
Readme
cocochex
Tiny, dependency-free schema validator and transformer for plain JavaScript objects.
cocochex checks data against a schema and can also transform values through custom validator functions. It supports:
- primitive type checks (
string,number,boolean,object,array,function,null,undefined) - exact value matching
- nested object schemas
- list validation
- multi-type (union-like) schemas
- required key enforcement (including nested keys)
- default values generated by functions
Installation
npm install cocochexQuick Start
import cocochex from "cocochex";
const input = {
a: 1,
b: "hello",
c: [1, "two", 3],
d: { x: 10, y: 20, z: [null, 0, 2] }
};
const output = cocochex(input, {
a: "number",
b: "string",
c: ["string", "number"], // each item must be a string or number
d: {
x: "number",
y: 20,
z: [0, v => v || "default"] // each item: exact 0, or transform via function
}
});
console.log(output);
// {
// a: 1,
// b: 'hello',
// c: [ 1, 'two', 3 ],
// d: { x: 10, y: 20, z: [ 'default', 0, 2 ] }
// }Invalid data throws immediately:
// c contains false — not a string or number
cocochex({ c: [1, 2, false] }, { c: ["string", "number"] });
// throws: false is invalid in "c". allowed types or values are: Type<string>, Type<number>.API
cocochex(params: any, struct: any, required?: string[]): anyparams: input value to validate/transform.struct: schema definition.required: list of required key paths. Default is[].
Returns the validated (and possibly transformed) value.
Important Behavior
- Validation is recursive.
- Object validation expects plain objects only.
- Arrays are not accepted where
objectis expected. nullis not accepted as object.
- Arrays are not accepted where
- The function mutates object and array values in place when applying validators/defaults.
- Custom validator functions can return a transformed value or throw an error.
Schema Reference
1. Primitive Type Token
Use one of:
"string""number""boolean""object"(plain object only)"array""function""null""undefined"
Example:
{ age: "number", name: "string", onChange: "function", deletedAt: "null" }2. Exact Value
Any non-function, non-schema-list value can be used as an exact match.
{ status: "active", retries: 3, enabled: true }3. Custom Validator Function
If the schema node is a function, it receives the current value and must:
- return a value (validated/transformed), or
- throw an error
{ id: v => {
if (typeof v !== "string" || v.length < 8) {
throw new Error("id must be a string with length >= 8");
}
return v.toLowerCase();
}}4. Nested Object Schema
{
user: {
name: "string",
profile: {
age: "number"
}
}
}5. List of Single-Type/Single-Rule Values
A single-item schema list validates each item in an input array against that one rule.
{ tags: ["string"] }
{ scores: ["number"] }
{ ids: [v => String(v)] }If the input is not an array, validation fails.
Edge cases:
["string"]means "must be an array, and every item must be string".[v => ...]means "must be an array, and apply this function to every item".[v => ...]does not mean default value.
// Fails: input is not an array
cocochex({ check: 1 }, { check: [v => `given value is ${v}`] });
// throws: Type <number> is invalid in "check". Expected a list.
// Passes: function runs for each array item
cocochex(
{ check: [1, "Hello", false] },
{ check: [v => `given value is ${v}`] }
);
// => { check: ["given value is 1", "given value is Hello", "given value is false"] }6. Multi-Type (Union-Like) Schema
A multi-item schema list tries each option until one passes.
{ value: ["string", "number"] }
{ token: ["string", null] }
{ payload: [
{ kind: "A", data: { x: "number" } },
{ kind: "B", data: { y: "string" } }
]}When the input value is an array and the schema is a union of non-list rules
(for example ["string", "number"]), each array item is validated against the
union. So this schema accepts either:
- a single scalar value matching one option, or
- an array where each item matches at least one option.
cocochex({ check: 1 }, { check: ["string", "number"] });
// => { check: 1 }
cocochex({ check: [1, "2"] }, { check: ["string", "number"] });
// => { check: [1, "2"] }
cocochex({ check: [1, "2", true] }, { check: ["string", "number"] });
// throws: true is invalid in "check". allowed types or values are: Type<string>, Type<number>.7. Default Value via Function (Missing Key)
For object properties only:
- when a key is missing
- and that key's schema is a list with more than one item whose last item is a function
then that function is called with no arguments and its return value is set as default.
{
createdAt: ["string", () => new Date().toISOString()]
}Important distinction:
[type1, type2]means union validation.[type1, fn]means union validation with function fallback (and function default when key is missing).[fn]means list-item rule only; it does not set a default for missing key.
// Missing key + [fn] => no default inserted
cocochex({}, { items: [() => true] });
// => {}
// Missing key + [type, fn] => default inserted via fn()
cocochex({}, { check: ["string", v => `setting default value, ignored given value is ${v}`] });
// => { check: "setting default value, ignored given value is undefined" }
// Existing scalar + [type, fn] => try type first, then fn fallback if type does not match
cocochex({ check: 1 }, { check: ["string", v => `no match. given value is ${v}`] });
// => { check: "no match. given value is 1" }
// Existing array + [type, fn] => per-item union behavior
cocochex({ check: [1, "Hello", false] }, { check: ["string", v => `no match. given value is ${v}`] });
// => { check: ["no match. given value is 1", "Hello", "no match. given value is false"] }Required Keys
Use the third argument to enforce required keys.
cocochex(data, schema, ["id", "profile[email]"]);Path format for nested keys uses bracket notation built from the parent path:
- top-level:
id - nested:
profile[email] - deeper nested:
profile[address][city]
If a required key is missing, an error is thrown:
Key "profile[email]" is required.Error Style
Typical failures throw strings or validator-thrown errors, for example:
Data schema does not match.Type <string> is invalid in "items". Expected a list of Type <number>."foo" is invalid in "value". allowed types or values are: Type<number>, "ok".
Recommendation: wrap calls in try/catch and normalize errors in your application layer.
More Examples
Union + Nested
const schema = {
mode: ["dev", "prod"],
source: [
{ type: "file", path: "string" },
{ type: "url", href: "string" }
]
};Transform Array Items
const schema = {
amounts: [v => {
const n = Number(v);
if (Number.isNaN(n)) throw new Error("invalid amount");
return n;
}]
};Missing Optional Key with Default
const input = {};
const schema = {
role: ["string", () => "guest"]
};
cocochex(input, schema); // => { role: "guest" }TypeScript Notes
The package ships declaration files and can be imported in ESM or CJS environments.
- ESM import:
import cocochex from "cocochex";- CJS require:
const cocochex = require("cocochex");Current function signature is intentionally generic (any) to allow flexible schemas.
