@mahmoudxyz/hassan
Version:
Type-safe data mapper for TypeScript with zero runtime overhead
115 lines • 3.55 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.h = void 0;
exports.createMapper = createMapper;
exports.h = {
direct: (value) => ({
__type: 'direct',
value,
}),
when: (condition, thenValue, elseValue) => {
const mapping = {
__type: 'conditional',
when: condition,
then: thenValue,
};
if (elseValue !== undefined) {
mapping.else = elseValue;
}
return mapping;
},
};
const isValidObject = (input) => {
return input !== null && typeof input === 'object' && !Array.isArray(input);
};
const isDirectValue = (value) => {
return (typeof value === 'object' &&
value !== null &&
value.__type === 'direct');
};
const isConditionalMapping = (value) => {
return (typeof value === 'object' &&
value !== null &&
value.__type === 'conditional');
};
const isTransformFunction = (value) => {
return typeof value === 'function';
};
const setNestedProperty = (obj, path, value) => {
const keys = path.split('.');
let current = obj;
for (let i = 0; i < keys.length - 1; i++) {
const key = keys[i];
if (!(key in current) || !isValidObject(current[key])) {
current[key] = {};
}
current = current[key];
}
current[keys[keys.length - 1]] = value;
};
const getNestedProperty = (obj, path) => {
const keys = path.split('.');
let current = obj;
for (const key of keys) {
if (!isValidObject(current) || !(key in current)) {
return undefined;
}
current = current[key];
}
return current;
};
const resolveMappingValue = (source, mapping, context) => {
if (isDirectValue(mapping)) {
return mapping.value;
}
if (isTransformFunction(mapping)) {
return mapping(source, context);
}
if (mapping.includes('.')) {
return getNestedProperty(source, mapping);
}
return source[mapping];
};
const mapProperty = (source, targetKey, mapping, context) => {
if (isConditionalMapping(mapping)) {
const conditionResult = mapping.when(source, context);
if (conditionResult) {
const resolvedValue = resolveMappingValue(source, mapping.then, context);
return [targetKey, resolvedValue];
}
else if (mapping.else !== undefined) {
const resolvedValue = resolveMappingValue(source, mapping.else, context);
return [targetKey, resolvedValue];
}
return null;
}
const resolvedValue = resolveMappingValue(source, mapping, context);
return resolvedValue !== undefined ? [targetKey, resolvedValue] : null;
};
const buildMappedObject = (source, mappings, context) => {
const result = {};
for (const [targetKey, mapping] of Object.entries(mappings)) {
const mappedProperty = mapProperty(source, targetKey, mapping, context);
if (mappedProperty !== null) {
const [key, value] = mappedProperty;
if (key.includes('.')) {
setNestedProperty(result, key, value);
}
else {
result[key] = value;
}
}
}
return result;
};
function createMapper(mappings) {
return {
map: (input, context) => {
if (!isValidObject(input)) {
return input;
}
return buildMappedObject(input, mappings, context);
},
};
}
//# sourceMappingURL=mapper.js.map