@chassisjs/hermes
Version:
Production-Ready TypeScript Outbox Pattern
216 lines (199 loc) • 6.19 kB
JavaScript
;
var crypto = require('node:crypto');
exports.HermesErrorCode = void 0;
(function (HermesErrorCode) {
HermesErrorCode["Assertion"] = "Assertion";
HermesErrorCode["NotSupportedMongoVersion"] = "NotSupportedMongoVersion";
})(exports.HermesErrorCode || (exports.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(exports.HermesErrorCode.Assertion, details, message);
this.details = details;
}
}
class NotSupportedMongoVersionError extends HermesError {
details;
constructor(details, message) {
super(exports.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`);
}
}
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');
};
function assertNever(x) {
throw new Error(`ASSERT_NEVER`);
}
const literalObject = (value) => value;
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 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,
};
};
exports.AssertionError = AssertionError;
exports.CancellationPromise = CancellationPromise;
exports.Duration = Duration;
exports.HermesError = HermesError;
exports.NotSupportedMongoVersionError = NotSupportedMongoVersionError;
exports.addDisposeOnSigterm = addDisposeOnSigterm;
exports.assert = assert;
exports.assertDate = assertDate;
exports.assertNever = assertNever;
exports.createSha256From = createSha256From;
exports.isNil = isNil;
exports.literalObject = literalObject;
exports.noop = noop;
exports.parseNonEmptyString = parseNonEmptyString;
exports.parseSemVer = parseSemVer;
exports.parseUuid4 = parseUuid4;
exports.swallow = swallow;
//# sourceMappingURL=index.cjs.map