@randajan/revert
Version:
A minimalist utility for running sequential steps with automatic rollback on failure.
161 lines (156 loc) • 4.7 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/async/index.js
var async_exports = {};
__export(async_exports, {
Revertable: () => Revertable,
attempt: () => attempt,
default: () => async_default,
revertable: () => revertable,
sleep: () => sleep
});
module.exports = __toCommonJS(async_exports);
// src/uni.js
var _passModes = ["omit", "keep", "reduce"];
var verifyFn = (argMsg, fn, req = false) => {
if (typeof fn === "function") {
return fn;
}
if (fn == null && !req) {
return;
}
throw new Error(`${argMsg} must be typeof function`);
};
var verifyPassMode = (passMode) => {
if (_passModes.includes(passMode)) {
return passMode;
}
throw new Error(`Option pass '${passMode}' must be one of '${_passModes.join("', '")}'`);
};
var defaultLogFormat = (kind, data, dir, s, c) => {
const symbol = kind === "error" ? dir ? "\u2500" : "\u292B" : dir ? "\u2193" : "\u2191";
return `${symbol} ${s}/${c} [${kind}] ${data?.message || data}`;
};
// src/async/utils.js
var revertable = async (value, steps, fn, onError) => {
let dir = true, s = 1;
const r = { status: "pass", pass: value };
while (s > 0 && s <= steps) {
try {
r.pass = await fn(r.pass, dir, s, steps);
} catch (err) {
if (onError) {
await onError(err, dir, s, steps);
}
r.status = dir ? "undo" : "fail";
r[r.status] = err;
r[r.status + "Step"] = s;
if (dir) {
dir = false;
} else {
break;
}
}
s += dir * 2 - 1;
}
if (r.pass === void 0) {
delete r.pass;
}
return Object.freeze(r);
};
var sleep = async (ms) => new Promise((res) => setTimeout(res, ms));
var attempt = async (exec, attemptCount = 3, delay = 2e3) => {
let a = 1, e;
while (true) {
try {
return await exec(a);
} catch (err) {
e = err;
}
if (a >= attemptCount) {
throw e;
}
await sleep(delay);
a++;
}
};
// src/async/index.js
var wrapWithLogMsg = (passMode, msg, fn) => {
if (!fn) {
return;
}
return async (a1, a2, ...a) => {
await (passMode === "omit" ? a1 : a2)(msg);
return fn(a1, a2, ...a);
};
};
var Revertable = class extends Array {
constructor({ logger, logFormat, pass = "omit" }) {
super();
Object.defineProperty(this, "passMode", { value: verifyPassMode(pass) });
logger = verifyFn("Option logger", logger);
if (logger) {
logFormat = verifyFn("Option logFormat", logFormat) || defaultLogFormat;
Object.defineProperty(this, "logger", {
value: (data, kind, dir, s, c) => logger(logFormat(kind, data, dir, s, c), kind, data, dir, s, c)
});
}
}
push(fwd, rwd) {
super.push(Object.freeze({
fwd: verifyFn("fwd", fwd, true),
rwd: verifyFn("rwd", rwd)
}));
return this;
}
pushNamed(fwdName, fwd, rwdName, rwd) {
const { logger, passMode } = this;
if (!logger) {
throw new Error("pushNamed(...) requires opt.logger to be provided");
}
return this.push(
wrapWithLogMsg(passMode, fwdName, verifyFn("fwd", fwd, true)),
wrapWithLogMsg(passMode, rwdName, verifyFn("rwd", rwd))
);
}
async run(value) {
const { logger, passMode, length } = this;
const onError = logger ? (err, dir, s, c) => logger(err, "error", dir, s, c) : void 0;
const omit = passMode == "omit";
return revertable(!omit ? value : void 0, length, async (value2, dir, s, c) => {
const { fwd, rwd } = this[s - 1];
const wd = dir ? fwd : rwd;
if (!wd) {
return !omit ? value2 : void 0;
}
const a = [];
if (!omit) {
a.push(value2);
}
if (logger) {
a.push((msg, kind = "info") => logger(msg, kind, dir, s, c));
}
const r = await wd(...a, s, c);
if (!omit) {
return passMode == "keep" ? value2 : r;
}
}, onError);
}
};
var async_default = (opt) => new Revertable(opt);
//# sourceMappingURL=index.js.map