normul
Version:
Normul is a tiny TypeScript/JavaScript library for data normalization and transformation
171 lines • 4.81 kB
JavaScript
export class Schema {
modifiers = [];
normalize(input) {
const ctx = {
path: [],
issues: [],
};
const data = this.invokeNormalize(this, input, ctx);
return {
data,
issues: ctx.issues,
};
}
invokeNormalize(schema, input, ctx) {
let currentModifierIndex = schema.modifiers.length - 1;
const next = (input, ctx) => {
if (currentModifierIndex >= 0) {
const fn = schema.modifiers[currentModifierIndex--];
return fn(input, ctx, next);
}
return schema._normalize(input, ctx);
};
return next(input, ctx);
}
makeIssue(options) {
const { ctx, ...rest } = options;
ctx.issues.push({
path: [...ctx.path],
...rest,
});
}
get optional() {
const result = this.clone();
result.modifiers.push((input, ctx, next) => {
if (input === undefined) {
return undefined;
}
return next(input, ctx);
});
return result;
}
get nullable() {
const result = this.clone();
result.modifiers.push((input, ctx, next) => {
if (input === null) {
return null;
}
return next(input, ctx);
});
return result;
}
default(value) {
const result = this.clone();
result.modifiers.push((input, ctx, next) => {
if (input === undefined || input === null) {
this.makeIssue({
ctx,
message: 'Using default value',
level: 'info',
});
return value;
}
return next(input, ctx);
});
return result;
}
preprocess(fn) {
const result = this.clone();
result.modifiers.push((input, ctx, next) => {
let data = input;
try {
data = fn(input);
}
catch (error) {
console.error(error);
this.makeIssue({
ctx,
message: `Caught exception in preprocess: ${error}`,
level: 'error',
});
}
return next(data, ctx);
});
return result;
}
transform(fn) {
const result = this.clone();
result.modifiers.push((input, ctx, next) => {
const data = next(input, ctx);
try {
return fn(data);
}
catch (error) {
console.error(error);
this.makeIssue({
ctx,
message: `Caught exception in transform: ${error}`,
level: 'error',
});
return data;
}
});
return result;
}
type() {
return new TypeSchema(this);
}
get any() {
return new AnySchema(this);
}
get unknown() {
return new UnknownSchema(this);
}
fallback(value) {
const result = this.clone();
result.modifiers.push((input, ctx, next) => {
const innerCtx = {
issues: [],
path: [],
};
const data = next(input, innerCtx);
ctx.issues.push(...innerCtx.issues);
const shouldFallback = innerCtx.issues.some((issue) => {
return (issue.level !== 'info' &&
issue.path.length === 0);
});
if (shouldFallback) {
this.makeIssue({
ctx,
message: 'Using fallback value',
level: 'info',
});
return value;
}
return data;
});
return result;
}
clone() {
const Constructor = this.constructor;
const result = new Constructor(...this.cloneArgs());
result.modifiers = [...this.modifiers];
this.cloneProps(result);
return result;
}
cloneArgs() {
return [];
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
cloneProps(target) {
//
}
}
export class TypeSchema extends Schema {
inner;
constructor(inner) {
super();
this.inner = inner;
}
_normalize(input, ctx) {
return this.inner ?
this.invokeNormalize(this.inner, input, ctx) :
input;
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export class AnySchema extends TypeSchema {
}
export class UnknownSchema extends TypeSchema {
}
//# sourceMappingURL=Schema.js.map