@chassisjs/hermes
Version:
Production-Ready TypeScript Outbox Pattern
205 lines (189 loc) • 5.99 kB
JavaScript
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