chrono-forge
Version:
A comprehensive framework for building resilient Temporal workflows, advanced state management, and real-time streaming activities in TypeScript. Designed for a seamless developer experience with powerful abstractions, dynamic orchestration, and full cont
153 lines (152 loc) • 6.42 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleDeleteEntities = exports.handleUpdateEntities = exports.createUpdateStatement = exports.normalizeEntities = exports.initialState = exports.defaultState = void 0;
exports.reducer = reducer;
const normalizr_1 = require("normalizr");
const immutability_helper_1 = __importDefault(require("immutability-helper"));
const actions_1 = require("./actions");
exports.defaultState = {};
exports.initialState = {};
const normalizeEntities = (data, entitySchema) => {
const schema = typeof entitySchema === 'string' ? (0, actions_1.getSchema)(entitySchema) : entitySchema;
const { entities } = (0, normalizr_1.normalize)(data, Array.isArray(data) ? [schema] : schema);
return entities;
};
exports.normalizeEntities = normalizeEntities;
const createUpdateStatement = (state, normalizedEntities) => Object.entries(normalizedEntities).reduce((acc, [entityName, entityGroup]) => {
acc[entityName] = state[entityName]
? Object.entries(entityGroup).reduce((subAcc, [entityId, entityData]) => {
subAcc[entityId] = state[entityName][entityId] ? { $merge: entityData } : { $set: entityData };
return subAcc;
}, {})
: { $set: entityGroup };
return acc;
}, {});
exports.createUpdateStatement = createUpdateStatement;
const handleUpdateEntities = (state, entities, strategy = '$merge') => Object.entries(entities).reduce((acc, [entityName, entityGroup]) => {
switch (strategy) {
case '$replace':
acc[entityName] = replaceStrategy(entityGroup);
break;
case '$set':
acc[entityName] = setStrategy(entityGroup, state[entityName]);
break;
case '$merge':
acc[entityName] = mergeStrategy(state, entityName, entityGroup);
break;
case '$unset':
acc[entityName] = unsetStrategy(entityGroup);
break;
case '$push':
case '$unshift':
acc[entityName] = arrayOperation(state[entityName], entityGroup, strategy);
break;
case '$splice':
acc[entityName] = spliceStrategy(entityGroup);
break;
case '$apply':
acc[entityName] = applyStrategy(entityGroup);
break;
default:
throw new Error(`Invalid strategy: ${strategy}`);
}
return acc;
}, {});
exports.handleUpdateEntities = handleUpdateEntities;
const handleDeleteEntities = (state, entities) => Object.fromEntries(Object.entries(entities).map(([entityName, entityGroup]) => {
const entityIds = Object.keys(entityGroup);
return [
entityName,
{
$unset: entityIds
}
];
}));
exports.handleDeleteEntities = handleDeleteEntities;
const replaceStrategy = (entityGroup) => Object.entries(entityGroup).reduce((acc, [entityId, entityData]) => {
acc[entityId] = { $set: entityData };
return acc;
}, {});
const setStrategy = (entityGroup, stateArray) => Object.entries(entityGroup).reduce((acc, [entityId, entityData]) => {
acc[entityId] = acc[entityId] || {};
Object.entries(entityData).forEach(([fieldName, fieldValue]) => {
if (fieldValue === entityId) {
return;
}
acc[entityId][fieldName] = { $set: fieldValue };
});
return acc;
}, {});
const mergeStrategy = (state, entityName, entityGroup) => ({
...(0, exports.createUpdateStatement)(state, { [entityName]: entityGroup })[entityName]
});
const unsetStrategy = (entityGroup) => {
return Object.entries(entityGroup).reduce((acc, [entityId, entityData]) => {
acc[entityId] = acc[entityId] || {};
acc[entityId].$unset = acc[entityId].$unset || [];
acc[entityId].$unset = [
...acc[entityId].$unset,
...Object.entries(entityData)
.filter(([_, value]) => value !== entityId)
.map(([key]) => key)
];
return acc;
}, {});
};
const arrayOperation = (entityState, entityGroup, operation) => Object.entries(entityGroup).reduce((acc, [entityId, entity]) => {
Object.entries(entity).forEach(([fieldName, value]) => {
if (entityId === value) {
return;
}
const currentArray = entityState[entityId][fieldName] ?? [];
if (!Array.isArray(currentArray)) {
throw new Error(`Expected array for ${operation} operation on entityId '${entityId}.${fieldName}'`);
}
acc[entityId] = acc[entityId] || {};
acc[entityId][fieldName] = { [operation]: Array.isArray(value) ? value : [value] };
});
return acc;
}, {});
const spliceStrategy = (entityGroup) => Object.fromEntries(Object.entries(entityGroup).map(([entityId, entity]) => [
entityId,
Object.fromEntries(Object.entries(entity)
.filter(([_, value]) => value !== entityId)
.map(([fieldName, value]) => {
if (!Array.isArray(value)) {
throw new Error(`Expected array for $splice operation on entityId '${entityId}.${fieldName}'`);
}
return [fieldName, { $splice: value }];
}))
]));
const applyStrategy = (entityGroup) => Object.fromEntries(Object.entries(entityGroup).map(([entityId, entity]) => [
entityId,
typeof entity === 'function'
? { $apply: entity }
: Object.fromEntries(Object.entries(entity)
.filter(([_, value]) => value !== entityId)
.map(([key, value]) => [key, { $apply: value }]))
]));
function reducer(state = exports.initialState, action) {
const { entities = {}, strategy } = action;
switch (action.type) {
case actions_1.UPDATE_ENTITIES:
case actions_1.UPDATE_ENTITIES_PARTIAL: {
return (0, immutability_helper_1.default)(state, (0, exports.handleUpdateEntities)(state, entities, strategy));
}
case actions_1.DELETE_ENTITIES: {
return (0, immutability_helper_1.default)(state, (0, exports.handleDeleteEntities)(state, entities));
}
case actions_1.SET_STATE: {
return (0, immutability_helper_1.default)(state, {
$set: entities
});
}
default: {
return state;
}
}
}
exports.default = reducer;