UNPKG

@chassisjs/hermes

Version:

Production-Ready TypeScript Outbox Pattern

205 lines (189 loc) 5.99 kB
import crypto from 'node:crypto'; var HermesErrorCode; (function (HermesErrorCode) { HermesErrorCode["Assertion"] = "Assertion"; HermesErrorCode["NotSupportedMongoVersion"] = "NotSupportedMongoVersion"; })(HermesErrorCode || (HermesErrorCode = {})); class HermesError extends Error { code; details; constructor(code, details, message) { super(message); this.code = code; this.details = details; } } class AssertionError extends HermesError { details; constructor(details, message) { super(HermesErrorCode.Assertion, details, message); this.details = details; } } class NotSupportedMongoVersionError extends HermesError { details; constructor(details, message) { super(HermesErrorCode.NotSupportedMongoVersion, details, message); this.details = details; } } const assert = (conditionToBeTruthy, message, details) => { if (!!conditionToBeTruthy === false) { if (message instanceof AssertionError) { throw message; } throw new AssertionError({ forValue: details?.actual, forKey: details?.field, }, message); } }; class CancellationPromise extends Promise { _resolve; _reject; _state = 'pending'; constructor(executor = () => null) { let _resolve = undefined; let _reject = undefined; super((resolve, reject) => { executor(resolve, reject); _resolve = resolve; _reject = reject; }); assert(_resolve); assert(_reject); this._resolve = _resolve; this._reject = _reject; } reject(reason) { this._state = 'rejected'; this._reject(reason); } resolve(value) { this._state = 'resolved'; this._resolve(value); } get isResolved() { return this._state === 'resolved'; } get isRejected() { return this._state === 'rejected'; } get isPending() { return this._state === 'pending'; } static resolved(value) { const promise = new CancellationPromise(); promise.resolve(value); return promise; } static rejected(value) { const promise = new CancellationPromise(); promise.reject(value); return promise; } } function assertDate(value) { if (!(value instanceof Date) || isNaN(new Date(value).getTime())) { throw new AssertionError({ forValue: value, forKey: 'date' }, `Value is not a date`); } } function assertNever(_) { throw new Error(`ASSERT_NEVER`); } const literalObject = (value) => value; const parseNonEmptyString = (arg, error) => { if (!arg) { throw new AssertionError({}, error || `The value is an empty string`); } return arg; }; const createSha256From = (fromValues) => { const hash = crypto.createHash('sha256'); for (const value of fromValues) { hash.update(value.toString()); } return hash.digest('hex'); }; var REGEX = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i; function validate(uuid) { return typeof uuid === 'string' && REGEX.test(uuid); } const parseUuid4 = (value) => { if (!validate(value) || !parseNonEmptyString(value)) { throw new AssertionError({ forValue: 'Uuid4String' }); } return value; }; class Duration { _ms; constructor(_ms) { this._ms = _ms; } get value() { return this._ms; } get ms() { return this._ms; } static from(duration) { return new Duration(duration.ms); } static ofDays(hours) { assert(hours >= 0, `hours has to be greater or equal 0`); return new Duration(hours * 24 * 60 * 60 * 1000); } static ofHours(hours) { assert(hours >= 0, `hours has to be greater or equal 0`); return new Duration(hours * 60 * 60 * 1000); } static ofMinutes(minutes) { assert(minutes >= 0, `minutes has to be greater or equal 0`); return new Duration(minutes * 60 * 1000); } static ofSeconds(seconds) { assert(seconds >= 0, `seconds has to be greater or equal 0`); return new Duration(seconds * 1000); } static ofMiliseconds(miliseconds) { assert(miliseconds >= 0, `miliseconds has to be greater or equal 0`); return new Duration(miliseconds); } } const noop = () => { }; const isNil = (value) => typeof value === 'undefined' || value === null; const swallow = (fn) => { try { const result = fn(); if (result && result instanceof Promise && result.catch) { return result.catch(noop); } else { return; } } catch { } }; const addDisposeOnSigterm = (fn) => { process.on('SIGTERM', () => { swallow(fn); console.info(`[outbox.addDisposeOnSigterm] Outbox consumer has stopped.`); }); process.on('SIGINT', () => { swallow(fn); console.info(`[outbox.addDisposeOnSigterm] Outbox consumer has stopped.`); }); }; const REGEXP = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/; const parseSemVer = (value = '') => { const versions = REGEXP.exec(value); return { major: Number(versions?.[1]) || void 0, minor: Number(versions?.[2]) || void 0, bugfix: Number(versions?.[3]) || void 0, rc: versions?.[4] || void 0, }; }; export { AssertionError, CancellationPromise, Duration, HermesError, HermesErrorCode, NotSupportedMongoVersionError, addDisposeOnSigterm, assert, assertDate, assertNever, createSha256From, isNil, literalObject, noop, parseNonEmptyString, parseSemVer, parseUuid4, swallow }; //# sourceMappingURL=index.mjs.map