@getanthill/datastore
Version:
Event-Sourced Datastore
116 lines • 5.19 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyEventHandler = exports.defaultReducer = void 0;
const ajv_1 = __importDefault(require("ajv"));
const ajv_formats_1 = __importDefault(require("ajv-formats"));
const jsonpatch = __importStar(require("fast-json-patch"));
const lodash_1 = __importDefault(require("lodash"));
const c = __importStar(require("../constants"));
const utils_1 = require("../utils");
const events_1 = require("./events");
const handler_1 = require("./handler");
const schema_1 = require("./schema");
function defaultReducer(state, event, patch = []) {
const now = event.created_at ?? (0, utils_1.getDate)();
delete event.created_at;
/**
* @note cloneDeep is required here to remove `readonly` properties
* applied on the state.
*/
const updatedState = lodash_1.default.cloneDeep({
created_at: now,
...lodash_1.default.mergeWith({}, state, event, utils_1.mergeWithReplacedArrays),
updated_at: now,
});
return jsonpatch.applyPatch(updatedState, patch).newDocument;
}
exports.defaultReducer = defaultReducer;
async function applyEventHandler(schema, eventSchema, state, event, options) {
// fields is validated by the schema up there so all the data sent should be reduced
const { type: eventType, v, json_patch: patch = [], ...fields } = event;
let finalState = state;
const handlers = eventSchema.handlers ?? [
{
is_fhe: eventSchema.is_fhe,
handler: eventSchema.handler,
},
];
/**
* @alpha
*/
for (const handler of handlers) {
const [handlerState, handlerPatch = [], event = {}] = await (0, handler_1.handle)(handler.handler, finalState, fields, options?.modelConfig, handler.is_fhe === true ? options?.fhe : undefined);
finalState = defaultReducer(handlerState, event, [...patch, ...handlerPatch],
//@ts-ignore
schema.model);
}
return finalState;
}
exports.applyEventHandler = applyEventHandler;
exports.default = (schemas, options = {}) => {
const mappedSchemas = (0, schema_1.mapDateTimeFormatToEitherStringOrObject)(schemas);
const mappedModel = (0, schema_1.mapDateTimeFormatToEitherStringOrObject)(options.model ?? {});
const VALIDATOR = new ajv_1.default({
strict: false,
schemas: [mappedSchemas],
useDefaults: true,
});
// @ts-ignore
(0, ajv_formats_1.default)(VALIDATOR);
return async (state, event, validator = VALIDATOR) => {
const schema = validator.getSchema('events');
// Will throw if the schema is wrong
const eventSchema = (0, events_1.validate)(event, schema, validator, options?.throwOnInvalidEvent);
// fields is validated by the schema up there so all the data sent should be reduced
const { type: eventType, v, json_patch: patch = [], ...fields } = event;
const mustBeCreated = eventType !== c.EVENT_TYPE_CREATED &&
eventSchema.is_created !== true &&
eventSchema.upsert !== true;
if (state === null && mustBeCreated === true) {
throw new Error('Entity must be created first');
}
const mustNotBeCreated = eventType === c.EVENT_TYPE_CREATED ||
(eventSchema.upsert !== true && eventSchema.is_created === true);
if (state !== null && mustNotBeCreated === true) {
throw new Error('Entity already created');
}
let finalState;
if ('handler' in eventSchema || 'handlers' in eventSchema) {
finalState = await applyEventHandler(schema, eventSchema, state, event, options);
}
else {
finalState = defaultReducer(state, fields, patch);
}
options?.model &&
(0, utils_1.validateEntity)(validator, finalState, mappedModel, options?.throwOnInvalidEvent);
// @ts-ignore
return (0, utils_1.deepCoerce)(finalState, schemas.model ?? {});
};
};
//# sourceMappingURL=reducer.js.map