go-go-try
Version:
Tries to execute a sync/async function, returns a result tuple
299 lines (286 loc) • 8.45 kB
JavaScript
;
function isSuccess(result) {
return result[0] === void 0;
}
function isFailure(result) {
return result[0] !== void 0;
}
function success(value) {
return [void 0, value];
}
function failure(error) {
return [error, void 0];
}
function assertNever(value) {
throw new Error(`Unhandled case: ${String(value)}`);
}
function getErrorMessage(error) {
if (error === void 0) return "undefined";
if (typeof error === "string") return error;
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
return error.message;
}
try {
return JSON.stringify(error);
} catch {
return String(error);
}
}
function isPromise$1(value) {
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
}
function isError(value) {
return value instanceof Error;
}
function resolveDefault(defaultValue) {
return typeof defaultValue === "function" ? defaultValue() : defaultValue;
}
function goTry(value) {
try {
const result = typeof value === "function" ? value() : value;
if (isPromise$1(result)) {
return result.then((resolvedValue) => success(resolvedValue)).catch((err) => failure(getErrorMessage(err)));
}
return success(result);
} catch (err) {
return failure(getErrorMessage(err));
}
}
function taggedError(tag) {
return class TaggedErrorClass extends Error {
constructor(message, options) {
super(message);
this._tag = tag;
this.name = tag;
this.cause = options?.cause;
}
};
}
const UnknownError = taggedError("UnknownError");
function isTaggedError$1(err) {
return isError(err) && "_tag" in err && typeof err._tag === "string";
}
function goTryRaw(value, options) {
const { errorClass, systemErrorClass } = options || {};
const actualSystemErrorClass = systemErrorClass ?? UnknownError;
const wrapError = (err) => {
if (errorClass) {
if (err === void 0) {
return new errorClass("undefined");
}
if (isError(err)) {
return new errorClass(err.message, { cause: err });
}
return new errorClass(String(err));
}
if (actualSystemErrorClass) {
if (isTaggedError$1(err)) {
return err;
}
if (err === void 0) {
return new actualSystemErrorClass("undefined");
}
if (isError(err)) {
return new actualSystemErrorClass(err.message, { cause: err });
}
return new actualSystemErrorClass(String(err));
}
if (err === void 0) {
return new Error("undefined");
}
return isError(err) ? err : new Error(String(err));
};
try {
const result = typeof value === "function" ? value() : value;
if (isPromise$1(result)) {
return result.then((resolvedValue) => success(resolvedValue)).catch((err) => failure(wrapError(err)));
}
return success(result);
} catch (err) {
return failure(wrapError(err));
}
}
function goTryOr(value, defaultValue) {
try {
const result = typeof value === "function" ? value() : value;
if (isPromise$1(result)) {
return result.then((resolvedValue) => success(resolvedValue)).catch((err) => [getErrorMessage(err), resolveDefault(defaultValue)]);
}
return success(result);
} catch (err) {
return [getErrorMessage(err), resolveDefault(defaultValue)];
}
}
function isTaggedError(err) {
return isError(err) && "_tag" in err && typeof err._tag === "string";
}
function wrapError(err, errorClass, systemErrorClass) {
if (errorClass) {
if (err === void 0) {
return new errorClass("undefined");
}
if (isError(err)) {
return new errorClass(err.message, { cause: err });
}
return new errorClass(String(err));
}
const actualSystemErrorClass = systemErrorClass ?? UnknownError;
if (isTaggedError(err)) {
return err;
}
if (err === void 0) {
return new actualSystemErrorClass("undefined");
}
if (isError(err)) {
return new actualSystemErrorClass(err.message, { cause: err });
}
return new actualSystemErrorClass(String(err));
}
async function runWithConcurrency(items, concurrency) {
if (items.length === 0) {
return [];
}
const isFactoryMode = typeof items[0] === "function";
if (!isFactoryMode && concurrency <= 0) {
return Promise.allSettled(items);
}
const results = new Array(items.length);
let index = 0;
async function worker() {
while (index < items.length) {
const currentIndex = index++;
try {
const item = items[currentIndex];
const value = isFactoryMode ? await item() : await item;
results[currentIndex] = { status: "fulfilled", value };
} catch (reason) {
results[currentIndex] = { status: "rejected", reason };
}
}
}
const workerCount = concurrency <= 0 ? items.length : Math.min(concurrency, items.length);
const workers = [];
for (let i = 0; i < workerCount; i++) {
workers.push(worker());
}
await Promise.all(workers);
return results;
}
async function goTryAll(items, options) {
const settled = await runWithConcurrency(items, options?.concurrency ?? 0);
const errors = [];
const results = [];
for (let i = 0; i < settled.length; i++) {
const item = settled[i];
if (item.status === "fulfilled") {
errors[i] = void 0;
results[i] = item.value;
} else {
errors[i] = getErrorMessage(item.reason);
results[i] = void 0;
}
}
return [errors, results];
}
async function goTryAllRaw(items, options) {
const { errorClass, systemErrorClass } = options || {};
const settled = await runWithConcurrency(items, options?.concurrency ?? 0);
const errors = [];
const results = [];
for (let i = 0; i < settled.length; i++) {
const item = settled[i];
if (item.status === "fulfilled") {
errors[i] = void 0;
results[i] = item.value;
} else {
const reason = item.reason;
errors[i] = wrapError(reason, errorClass, systemErrorClass);
results[i] = void 0;
}
}
return [errors, results];
}
function assert(condition, errorOrClass, message) {
if (!condition) {
if (typeof errorOrClass === "string") {
throw new Error(errorOrClass);
}
if (typeof errorOrClass === "function" && message !== void 0) {
throw new errorOrClass(message);
}
throw errorOrClass;
}
}
function isPromise(value) {
return value instanceof Promise;
}
function isErrorConstructor(error) {
return typeof error === "function" && error.prototype !== void 0 && error.prototype instanceof Error;
}
function throwError(value, error) {
if (isErrorConstructor(error)) {
throw new error(String(value), { cause: value });
}
throw error(value);
}
function ensure(value, predicate, error = UnknownError) {
if (typeof value === "function") {
const result = value();
if (isPromise(result)) {
return result.then((resolved) => {
if (!predicate(resolved)) {
throwError(resolved, error);
}
return resolved;
});
}
if (!predicate(result)) {
throwError(result, error);
}
return result;
}
if (isPromise(value)) {
return value.then((resolved) => {
if (!predicate(resolved)) {
throwError(resolved, error);
}
return resolved;
});
}
if (!predicate(value)) {
throwError(value, error);
}
return value;
}
function goElse(value, defaultValue) {
try {
const result = typeof value === "function" ? value() : value;
if (isPromise$1(result)) {
return result.then((resolvedValue) => [void 0, resolvedValue]).catch((err) => {
const error = err instanceof Error ? err : new Error(String(err));
return [error, resolveDefault(defaultValue)];
});
}
return [void 0, result];
} catch (err) {
const error = err instanceof Error ? err : new Error(String(err));
return [error, resolveDefault(defaultValue)];
}
}
exports.UnknownError = UnknownError;
exports.assert = assert;
exports.assertNever = assertNever;
exports.ensure = ensure;
exports.failure = failure;
exports.go = goTryRaw;
exports.goAll = goTryAllRaw;
exports.goElse = goElse;
exports.goTry = goTry;
exports.goTryAll = goTryAll;
exports.goTryAllRaw = goTryAllRaw;
exports.goTryOr = goTryOr;
exports.goTryRaw = goTryRaw;
exports.isFailure = isFailure;
exports.isSuccess = isSuccess;
exports.success = success;
exports.taggedError = taggedError;