@zerothrow/core
Version:
Core ZeroThrow functionality - Rust-style Result<T,E> for TypeScript
386 lines (379 loc) • 11.9 kB
JavaScript
var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// src/core-exports.ts
var core_exports_exports = {};
__export(core_exports_exports, {
ZeroError: () => ZeroError,
attempt: () => attempt,
collect: () => collect2,
collectAsync: () => collectAsync2,
enhance: () => enhance,
err: () => err2,
firstSuccess: () => firstSuccess2,
fromAsync: () => fromAsync,
isErr: () => isErr,
isOk: () => isOk,
isResult: () => isResult,
ok: () => ok,
pipe: () => pipe2,
try: () => attempt,
tryAsync: () => tryAsync,
wrap: () => wrap
});
// src/error.ts
var ZeroError = class _ZeroError extends Error {
code;
context;
constructor(code, message, opts = {}) {
super(message, { cause: opts.cause });
this.code = code;
this.context = opts.context;
this.name = "ZeroError";
Object.setPrototypeOf(this, new.target.prototype);
}
/**
* Override toString to include full error chain with context
*/
toString() {
let result = `${this.name} [${String(this.code)}]: ${this.message}`;
if (this.context && Object.keys(this.context).length > 0) {
result += `
Context: ${JSON.stringify(this.context, null, 2).replace(/\n/g, "\n ")}`;
}
let currentCause = this.cause;
let depth = 1;
while (currentCause instanceof Error) {
const indent = " ".repeat(depth);
const causeName = currentCause.name || "Error";
result += `
${indent}Caused by: ${causeName}: ${currentCause.message}`;
if (currentCause instanceof _ZeroError && currentCause.context) {
const contextStr = JSON.stringify(
currentCause.context,
null,
2
).replace(/\n/g, `
${indent} `);
result += `
${indent} Context: ${contextStr}`;
}
currentCause = currentCause.cause;
depth++;
}
return result;
}
/**
* Get the full stack trace including all causes
*/
getFullStack() {
let fullStack = this.stack || "";
let currentCause = this.cause;
while (currentCause instanceof Error) {
fullStack += "\n\nCaused by:\n" + (currentCause.stack || currentCause.toString());
currentCause = currentCause.cause;
}
return fullStack;
}
};
// src/result.ts
function createResult(base) {
const result = base;
result.andThen = function(fn) {
if (!result.ok) return createResult({ ok: false, error: result.error });
return fn(result.value);
};
result.mapErr = function(fn) {
if (result.ok) return createResult({ ok: true, value: result.value });
return createResult({ ok: false, error: fn(result.error) });
};
result.map = function(fn) {
if (!result.ok) return createResult({ ok: false, error: result.error });
return createResult({ ok: true, value: fn(result.value) });
};
result.orElse = function(fallback) {
if (result.ok) return result;
return fallback();
};
result.unwrapOr = function(fallback) {
return result.ok ? result.value : fallback;
};
result.unwrapOrThrow = function() {
if (!result.ok) throw result.error;
return result.value;
};
result.tap = function(fn) {
if (result.ok) fn(result.value);
return result;
};
result.tapErr = function(fn) {
if (!result.ok) fn(result.error);
return result;
};
result.finally = function(fn) {
if (result.ok) fn(result.value);
else fn();
return result;
};
result.void = function() {
if (!result.ok) return createResult({ ok: false, error: result.error });
return createResult({ ok: true, value: void 0 });
};
return result;
}
var ok = (value) => createResult({ ok: true, value });
var err = (error) => createResult({ ok: false, error });
var normalise = (e) => {
if (e instanceof ZeroError) return e;
if (e instanceof Error) {
if ("code" in e && typeof e.code === "string" && "context" in e) {
return e;
}
return new ZeroError("UNKNOWN_ERR", e.message, { cause: e });
}
return new ZeroError("UNKNOWN_ERR", String(e));
};
function wrap(cause, code, msg, ctx) {
const errorCode = code ?? (cause instanceof ZeroError ? cause.code : "code" in cause && cause.code ? cause.code : "WRAPPED_ERROR");
const message = msg ?? cause.message;
return new ZeroError(errorCode, message, {
cause,
...ctx !== void 0 && { context: ctx }
});
}
// src/combinators.ts
function pipe(...fns) {
return (input) => fns.reduce((acc, fn) => fn(acc), input);
}
function collect(results) {
const values = new Array(results.length);
let index = 0;
for (const result of results) {
if (!result.ok) return result;
values[index++] = result.value;
}
return ok(values);
}
async function collectAsync(promises) {
const results = await Promise.all(promises);
return collect(results);
}
function firstSuccess(attempts) {
if (attempts.length === 0) {
return err(new ZeroError("ALL_FAILED", "All alternatives failed"));
}
for (const attempt2 of attempts) {
const result = attempt2();
if (result.ok) return result;
}
return err(new ZeroError("ALL_FAILED", "All alternatives failed"));
}
// src/core-exports.ts
function err2(errorOrCode, message) {
if (typeof errorOrCode === "string") {
return err(new ZeroError(errorOrCode, message || errorOrCode));
}
return err(errorOrCode);
}
function attempt(fnOrOps, mapError) {
if (Array.isArray(fnOrOps)) {
return attemptBatch(fnOrOps, mapError);
}
try {
const result = fnOrOps();
if (result && typeof result === "object" && "then" in result) {
return result.then(
(value) => ok(value),
(error) => {
const base = normalise(error);
return err(mapError ? mapError(base) : base);
}
);
}
return ok(result);
} catch (e) {
const base = normalise(e);
return err(mapError ? mapError(base) : base);
}
}
async function attemptBatch(fns, map) {
const results = [];
for (const fn of fns) {
const promisedResult = Promise.resolve().then(() => fn()).then(
(value) => ok(value),
(error) => {
const base = normalise(error);
return err(map ? map(base) : base);
}
);
const result = await promisedResult;
if (!result.ok) {
return result;
}
results.push(result.value);
}
return ok(results);
}
async function tryAsync(fn) {
try {
const value = await fn();
return ok(value);
} catch (e) {
const base = normalise(e);
return err(base);
}
}
function enhance(promise) {
const enhanced = promise;
enhanced.andThen = function(fn) {
return enhance(this.then((result) => {
if (!result.ok) return err(result.error);
const fnResult = fn(result.value);
if (fnResult && typeof fnResult === "object" && "then" in fnResult) {
return fnResult;
}
return fnResult;
}));
};
enhanced.map = function(fn) {
return enhance(this.then((result) => result.map(fn)));
};
enhanced.mapErr = function(fn) {
return enhance(this.then((result) => result.mapErr(fn)));
};
enhanced.orElse = function(fallback) {
return enhance(this.then((result) => result.orElse(fallback)));
};
enhanced.unwrapOr = function(fallback) {
return this.then((result) => result.unwrapOr(fallback));
};
enhanced.unwrapOrThrow = function() {
return this.then((result) => result.unwrapOrThrow());
};
enhanced.tap = function(fn) {
return enhance(this.then((result) => result.tap(fn)));
};
enhanced.tapErr = function(fn) {
return enhance(this.then((result) => result.tapErr(fn)));
};
enhanced.finally = function(fn) {
return enhance(this.then((result) => result.finally(fn)));
};
enhanced.void = function() {
return enhance(this.then((result) => result.void()));
};
return enhanced;
}
function fromAsync(fn) {
return enhance(fn());
}
var pipe2 = pipe;
var collect2 = collect;
var collectAsync2 = collectAsync;
var firstSuccess2 = firstSuccess;
function isResult(value) {
if (value === null || typeof value !== "object" || !("ok" in value) || typeof value.ok !== "boolean") {
return false;
}
const v = value;
if (v.ok === true) {
return "value" in v;
} else {
return "error" in v && v.error instanceof globalThis.Error;
}
}
function isOk(value) {
return isResult(value) && value.ok === true;
}
function isErr(value) {
return isResult(value) && value.ok === false;
}
// src/dev/error-formatter.ts
var COLORS = {
red: "\x1B[31m",
green: "\x1B[32m",
yellow: "\x1B[33m",
blue: "\x1B[34m",
gray: "\x1B[90m",
reset: "\x1B[0m",
bold: "\x1B[1m"
};
var ErrorFormatter = class {
options;
constructor(options = {}) {
const supportsColor = options.colors ?? (typeof process === "object" && typeof process.stdout === "object" && process.stdout.isTTY);
this.options = {
colors: supportsColor,
stackTrace: true,
details: true,
timestamp: true,
...options
};
}
formatZeroError(error) {
const lines = [];
const { colors } = this.options;
const codeStr = typeof error.code === "symbol" ? String(error.code) : String(error.code);
const header = colors ? `${COLORS.red}${COLORS.bold}[${codeStr}]${COLORS.reset} ${COLORS.red}${error.message}${COLORS.reset}` : `[${codeStr}] ${error.message}`;
lines.push(header);
if (error.context && "statusCode" in error.context) {
const statusLine = colors ? `${COLORS.gray}Status Code: ${COLORS.yellow}${error.context["statusCode"]}${COLORS.reset}` : `Status Code: ${error.context["statusCode"]}`;
lines.push(statusLine);
}
if (this.options.timestamp) {
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
const timestampLine = colors ? `${COLORS.gray}Timestamp: ${timestamp}${COLORS.reset}` : `Timestamp: ${timestamp}`;
lines.push(timestampLine);
}
if (this.options.details && error.context) {
lines.push("");
const detailsHeader = colors ? `${COLORS.blue}Context:${COLORS.reset}` : "Context:";
lines.push(detailsHeader);
const detailsStr = JSON.stringify(error.context, null, 2);
const detailsLines = detailsStr.split("\n").map(
(line) => colors ? `${COLORS.gray} ${line}${COLORS.reset}` : ` ${line}`
);
lines.push(...detailsLines);
}
if (this.options.stackTrace && error.stack) {
lines.push("");
const stackHeader = colors ? `${COLORS.blue}Stack Trace:${COLORS.reset}` : "Stack Trace:";
lines.push(stackHeader);
const stackLines = (error.stack ? error.stack.split("\n").slice(1) : []).map((line) => colors ? `${COLORS.gray}${line}${COLORS.reset}` : line);
lines.push(...stackLines);
}
return lines.join("\n");
}
formatResult(result) {
if (result.ok) {
const { colors } = this.options;
const valueStr = JSON.stringify(result.value);
return colors ? `${COLORS.green}\u2713 Success: ${valueStr}${COLORS.reset}` : `\u2713 Success: ${valueStr}`;
} else {
const error = result.error;
if (error instanceof core_exports_exports.ZeroError) {
return this.formatZeroError(error);
} else {
const { colors } = this.options;
const errorStr = error instanceof Error ? error.message : String(error);
return colors ? `${COLORS.red}\u2717 Error: ${errorStr}${COLORS.reset}` : `\u2717 Error: ${errorStr}`;
}
}
}
// Console helper methods
logError(error) {
console.error(this.formatZeroError(error));
}
logResult(result) {
console.log(this.formatResult(result));
}
};
var errorFormatter = new ErrorFormatter();
function createErrorFormatter(options) {
return new ErrorFormatter(options);
}
export { ErrorFormatter, createErrorFormatter, errorFormatter };
//# sourceMappingURL=error-formatter.js.map
//# sourceMappingURL=error-formatter.js.map