integreat
Version:
Node.js integration layer
145 lines • 5.76 kB
JavaScript
import mapAny from 'map-any';
import { nanoid } from 'nanoid';
import booleanFn from './castFns/boolean.js';
import dateFn from './castFns/date.js';
import integerFn from './castFns/integer.js';
import numberFn from './castFns/number.js';
import objectFn from './castFns/object.js';
import nonPrimitiveFn from './castFns/nonPrimitive.js';
import stringFn from './castFns/string.js';
import { isObject, isFieldDefinition, isShape, isNotNullOrUndefined, } from '../utils/is.js';
import unwrapValue from '../utils/unwrapValue.js';
import { ensureArray } from '../utils/array.js';
function castFnFromType(type, schemas) {
switch (type) {
case 'string':
return stringFn;
case 'integer':
return integerFn;
case 'number':
case 'float':
return numberFn;
case 'boolean':
return booleanFn;
case 'date':
return dateFn;
case 'object':
return objectFn;
case 'unknown':
return (value) => value;
default:
return nonPrimitiveFn(type, schemas);
}
}
const typeFromDef = (prop) => isFieldDefinition(prop)
? prop.$type
: typeof prop === 'string'
? prop
: undefined;
const hasArrayNotation = (key) => typeof key === 'string' && key.endsWith('[]');
const removeArrayNotation = (key) => hasArrayNotation(key) ? key.slice(0, key.length - 2) : key;
function createCastFn(def, schemas) {
if (isFieldDefinition(def)) {
if (def?.const !== undefined) {
return () => () => def.const;
}
const castFn = castFnFromType(removeArrayNotation(def.$type), schemas);
return (isRev, noDefaults = false) => function castValue(rawValue) {
const value = unwrapValue(rawValue);
if (value === undefined) {
return noDefaults ? undefined : def.default;
}
else {
return castFn(value, isRev);
}
};
}
else if (isShape(def)) {
return createShapeCast(def, undefined, schemas);
}
else {
return undefined;
}
}
const unwrapSingleArrayItem = (fn) => (value) => Array.isArray(value) && value.length === 1 ? fn(value[0]) : fn(value);
const handleArray = (fn, isArrayExpected, type) => isArrayExpected
? (isRev, noDefaults) => (value) => value === undefined
? undefined
: ensureArray(value).map(fn(isRev, noDefaults))
: type === 'unknown'
? (isRev, noDefaults) => fn(isRev, noDefaults)
: (isRev, noDefaults) => unwrapSingleArrayItem(fn(isRev, noDefaults));
function getDates(shouldHaveCreatedAt, shouldHaveUpdatedAt, createdAt, updatedAt, noDefaults) {
if (noDefaults) {
return {};
}
const nextCreatedAt = shouldHaveCreatedAt
? createdAt
? createdAt
: (updatedAt ?? new Date())
: undefined;
const nextUpdatedAt = shouldHaveUpdatedAt
? updatedAt
? updatedAt
: (nextCreatedAt ?? new Date())
: undefined;
return {
...(nextCreatedAt ? { createdAt: nextCreatedAt } : {}),
...(nextUpdatedAt ? { updatedAt: nextUpdatedAt } : {}),
};
}
function createCastFnHandlingArrays(key, def, schemas) {
const cast = createCastFn(def, schemas);
if (cast) {
const type = typeFromDef(def);
return handleArray(cast, hasArrayNotation(key) || hasArrayNotation(type), type);
}
else {
return undefined;
}
}
const completeItemBeforeCast = (shouldHaveCreatedAt, shouldHaveUpdatedAt, doGenerateId) => (item, noDefaults) => ({
...item,
id: item.id ?? (doGenerateId && !noDefaults ? nanoid() : null),
...getDates(shouldHaveCreatedAt, shouldHaveUpdatedAt, item.createdAt, item.updatedAt, noDefaults),
});
const castField = (item, isRev, noDefaults) => ([key, cast]) => [
key,
cast(isRev, noDefaults)(item[key]),
];
const fieldHasValue = ([_, value]) => value !== undefined;
const createFieldCast = (schemas) => ([key, def]) => [removeArrayNotation(key), createCastFnHandlingArrays(key, def, schemas)];
const entryHasCastFn = (entry) => entry[1] !== undefined;
const includeType = (isRev, type) => !isRev && typeof type === 'string' ? [['$type', type]] : [];
const includeIsNewAndIsDeleted = (item) => [
...(item.isNew === true ? [['isNew', true]] : []),
...(item.isDeleted === true ? [['isDeleted', true]] : []),
];
const createCastItemFn = (completeItem, fields, type) => (isRev, noDefaults) => function castItem(rawItem) {
if (!isObject(rawItem)) {
return undefined;
}
const item = completeItem(rawItem, noDefaults);
return Object.fromEntries([
...fields.map(castField(item, isRev, noDefaults)).filter(fieldHasValue),
...includeType(isRev, type),
...includeIsNewAndIsDeleted(item),
]);
};
function createShapeCast(shape, type, schemas, doGenerateId = false) {
const fields = Object.entries(shape)
.map(createFieldCast(schemas))
.filter(entryHasCastFn);
const shouldHaveCreatedAt = !!shape.createdAt;
const shouldHaveUpdatedAt = !!shape.updatedAt;
const completeItem = completeItemBeforeCast(shouldHaveCreatedAt, shouldHaveUpdatedAt, doGenerateId);
return createCastItemFn(completeItem, fields, type);
}
export default function createCast(shape, type, schemas = new Map(), doGenerateId = false) {
const castShape = createShapeCast(shape, type, schemas, doGenerateId);
return function castItem(data, isRev = false, noDefaults = false) {
const casted = mapAny(castShape(isRev, noDefaults), data);
return Array.isArray(casted) ? casted.filter(isNotNullOrUndefined) : casted;
};
}
//# sourceMappingURL=createCast.js.map