typescript-result
Version:
Supercharge your TypeScript error handling with a powerful Result type that transforms chaotic try-catch blocks into elegant, type-safe code.
560 lines (537 loc) • 19.4 kB
JavaScript
"use strict";
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);
var index_exports = {};
__export(index_exports, {
AsyncResult: () => AsyncResult,
NonExhaustiveError: () => NonExhaustiveError,
Result: () => Result2,
assertUnreachable: () => assertUnreachable
});
module.exports = __toCommonJS(index_exports);
function isPromise(value) {
if (value === null || value === void 0) {
return false;
}
if (typeof value !== "object") {
return false;
}
return value instanceof Promise || "then" in value;
}
function isFunction(value) {
return typeof value === "function";
}
function isAsyncFn(fn) {
return fn.constructor.name === "AsyncFunction";
}
function isGenerator(obj) {
return typeof obj === "object" && obj !== null && typeof obj.next === "function" && typeof obj.throw === "function" && typeof obj.return === "function" && typeof obj[Symbol.iterator] === "function" && obj[Symbol.iterator]() === obj;
}
function isAsyncGenerator(obj) {
return typeof obj === "object" && obj !== null && typeof obj.next === "function" && typeof obj.throw === "function" && typeof obj.return === "function" && typeof obj[Symbol.asyncIterator] === "function" && obj[Symbol.asyncIterator]() === obj;
}
function assertUnreachable(value) {
throw new Error(`Unreachable case: ${value}`);
}
var NonExhaustiveError = class extends Error {
constructor(error) {
super("Not all error cases were handled");
this.error = error;
}
};
var Matcher = class {
constructor(error) {
this.error = error;
}
cases=[];
defaultHandler=void 0;
when(value, ...args) {
const cases = [ value, ...args.slice(0, -1) ];
const handler = args.at(-1);
this.cases.push(...cases.map(value2 => ({
value: value2,
handler: handler
})));
return this;
}
else=handler => {
if (this.defaultHandler) {
throw new Error("already registered an 'else' handler");
}
this.defaultHandler = handler;
return this;
};
run=() => {
const isAsync = this.cases.some(item => isAsyncFn(item.handler));
for (const item of this.cases) {
const match = isFunction(item.value) && this.error instanceof item.value || item.value === this.error;
if (match) {
const value = item.handler(this.error);
return isPromise(value) ? value : isAsync ? Promise.resolve(value) : value;
}
}
if (this.defaultHandler) {
return this.defaultHandler(this.error);
}
throw new NonExhaustiveError(this.error);
};
};
var AsyncResult = class _AsyncResult extends Promise {
constructor(executor) {
super(executor);
}
* [Symbol.iterator]() {
return yield this;
}
get isAsyncResult() {
return true;
}
async toTuple() {
const result = await (this);
return result.toTuple();
}
async errorOrNull() {
const result = await (this);
return result.errorOrNull();
}
async getOrNull() {
const result = await (this);
return result.getOrNull();
}
async getOrDefault(defaultValue) {
const result = await (this);
return result.getOrDefault(defaultValue);
}
async getOrElse(onFailure) {
const result = await (this);
return result.getOrElse(onFailure);
}
async getOrThrow() {
const result = await (this);
return result.getOrThrow();
}
async fold(onSuccess, onFailure) {
const result = await (this);
return result.fold(onSuccess, onFailure);
}
onFailure(action) {
return new _AsyncResult((resolve, reject) => this.then(async result => {
try {
if (!result.ok) {
await action(result.error);
}
resolve(result);
} catch (e) {
reject(e);
}
}).catch(reject));
}
onSuccess(action) {
return new _AsyncResult((resolve, reject) => this.then(async result => {
try {
if (result.ok) {
await action(result.value);
}
resolve(result);
} catch (error) {
reject(error);
}
}).catch(reject));
}
map(transform) {
return new _AsyncResult((resolve, reject) => {
this.then(async result => resolve(await result.map(transform))).catch(reject);
});
}
mapCatching(transformValue, transformError) {
return new _AsyncResult((resolve, reject) => {
this.map(transformValue).then(result => resolve(result)).catch(error => {
try {
resolve(ResultFactory.error(transformError ? transformError(error) : error));
} catch (err) {
reject(err);
}
});
});
}
mapError(transform) {
return new _AsyncResult((resolve, reject) => this.then(async result => {
try {
resolve(result.mapError(transform));
} catch (error) {
reject(error);
}
}).catch(reject));
}
recover(onFailure) {
return new _AsyncResult((resolve, reject) => this.then(async result => {
try {
const outcome = await result.recover(onFailure);
resolve(outcome);
} catch (error) {
reject(error);
}
}).catch(reject));
}
recoverCatching(onFailure, transformError) {
return new _AsyncResult((resolve, reject) => this.then(result => {
resolve(result.recoverCatching(onFailure, transformError));
}).catch(reject));
}
toString() {
return "AsyncResult";
}
static error(error) {
return new _AsyncResult(resolve => resolve(ResultFactory.error(error)));
}
static ok(value) {
return new _AsyncResult(resolve => resolve(ResultFactory.ok(value)));
}
static fromPromise(promise) {
return new _AsyncResult((resolve, reject) => {
promise.then(value => resolve(ResultFactory.isResult(value) ? value : ResultFactory.ok(value))).catch(reject);
});
}
static fromPromiseCatching(promise, transform) {
return new _AsyncResult(resolve => {
promise.then(value => resolve(ResultFactory.isResult(value) ? value : ResultFactory.ok(value))).catch(caughtError => {
resolve(ResultFactory.error(transform?.(caughtError) ?? caughtError));
});
});
}
};
var Result = class {
constructor(_value, _error) {
this._value = _value;
this._error = _error;
}
* [Symbol.iterator]() {
return yield this;
}
get isResult() {
return true;
}
get value() {
return this._value;
}
get error() {
return this._error;
}
get success() {
return this.error === void 0;
}
get failure() {
return this.error !== void 0;
}
get ok() {
return this.success;
}
isOk() {
return this.success;
}
isError() {
return this.failure;
}
toTuple() {
return [ this._value ?? null, this._error ?? null ];
}
errorOrNull() {
return this.failure ? this._error : null;
}
getOrNull() {
return this.success ? this._value : null;
}
getOrDefault(defaultValue) {
return this.success ? this._value : defaultValue;
}
getOrElse(onFailure) {
if (isAsyncFn(onFailure)) {
return this.success ? Promise.resolve(this._value) : onFailure(this._error);
}
return this.success ? this._value : onFailure(this._error);
}
getOrThrow() {
if (this.success) {
return this._value;
}
throw this._error;
}
fold(onSuccess, onFailure) {
const isAsync = isAsyncFn(onSuccess) || isAsyncFn(onFailure);
const outcome = this.success ? onSuccess(this._value) : onFailure(this._error);
return isAsync && !isPromise(outcome) ? Promise.resolve(outcome) : outcome;
}
match() {
return this.failure ? new Matcher(this._error) : void 0;
}
onFailure(action) {
const isAsync = isAsyncFn(action);
if (this.failure) {
const outcome = action(this._error);
if (isAsync) {
return new AsyncResult(resolve => {
outcome.then(() => resolve(ResultFactory.error(this._error)));
});
}
return this;
}
return isAsync ? AsyncResult.ok(this._value) : this;
}
onSuccess(action) {
const isAsync = isAsyncFn(action);
if (this.success) {
const outcome = action(this._value);
if (isAsync) {
return new AsyncResult(resolve => {
outcome.then(() => resolve(ResultFactory.ok(this._value)));
});
}
return this;
}
return isAsync ? AsyncResult.error(this._error) : this;
}
map(transform) {
return this.success ? ResultFactory.run(() => transform(this._value)) : isAsyncFn(transform) ? AsyncResult.error(this._error) : this;
}
mapCatching(transformValue, transformError) {
return this.success ? ResultFactory.try(() => transformValue(this._value), transformError) : this;
}
mapError(transform) {
if (this.success) {
return this;
}
return ResultFactory.error(transform(this._error));
}
recover(onFailure) {
return this.success ? isAsyncFn(onFailure) ? AsyncResult.ok(this._value) : this : ResultFactory.run(() => onFailure(this._error));
}
recoverCatching(onFailure, transformError) {
return this.success ? isAsyncFn(onFailure) ? AsyncResult.ok(this._value) : this : ResultFactory.try(() => onFailure(this._error), transformError);
}
toString() {
if (this.success) {
return `Result.ok(${this._value})`;
}
return `Result.error(${this.error})`;
}
};
var ResultFactory = class _ResultFactory {
constructor() {}
static ok(value) {
return new Result(value, void 0);
}
static error(error) {
return new Result(void 0, error);
}
static isResult(possibleResult) {
return possibleResult instanceof Result;
}
static isAsyncResult(possibleAsyncResult) {
return possibleAsyncResult instanceof AsyncResult;
}
static run(fn) {
const returnValue = fn();
if (isGenerator(returnValue) || isAsyncGenerator(returnValue)) {
return _ResultFactory.handleGenerator(returnValue);
}
if (isPromise(returnValue)) {
return AsyncResult.fromPromise(returnValue);
}
return _ResultFactory.isResult(returnValue) ? returnValue : _ResultFactory.ok(returnValue);
}
static allInternal(items, opts) {
const runner = opts.catching ? _ResultFactory.try : _ResultFactory.run;
const flattened = [];
let isAsync = items.some(isPromise);
let hasFailure = false;
for (const item of items) {
if (isFunction(item)) {
if (hasFailure) {
continue;
}
const returnValue = runner(item);
if (_ResultFactory.isResult(returnValue) && !returnValue.ok) {
hasFailure = true;
if (!isAsync) {
return returnValue;
}
}
if (_ResultFactory.isAsyncResult(returnValue)) {
isAsync = true;
}
flattened.push(returnValue);
} else if (_ResultFactory.isResult(item)) {
if (!item.ok) {
hasFailure = true;
if (!isAsync) {
return item;
}
}
flattened.push(item);
} else if (_ResultFactory.isAsyncResult(item)) {
isAsync = true;
flattened.push(item);
} else if (isPromise(item)) {
isAsync = true;
flattened.push(opts.catching ? AsyncResult.fromPromiseCatching(item) : AsyncResult.fromPromise(item));
} else {
flattened.push(_ResultFactory.ok(item));
}
}
if (isAsync) {
return new AsyncResult((resolve, reject) => {
const asyncResults = [];
const asyncIndexes = [];
for (let i = 0; i < flattened.length; i++) {
const item = flattened[i];
if (_ResultFactory.isAsyncResult(item)) {
asyncResults.push(item);
asyncIndexes.push(i);
}
}
Promise.all(asyncResults).then(resolvedResults => {
const merged = [ ...flattened ];
for (let i = 0; i < resolvedResults.length; i++) {
merged[asyncIndexes[i]] = resolvedResults[i];
}
const firstFailedResult = merged.find(resolvedResult => !resolvedResult.ok);
if (firstFailedResult) {
resolve(firstFailedResult);
return;
}
resolve(_ResultFactory.ok(merged.map(result => result.getOrNull())));
}).catch(reason => {
reject(reason);
});
});
}
return _ResultFactory.ok(flattened.map(result => result.getOrNull()));
}
static all(...items) {
return _ResultFactory.allInternal(items, {
catching: false
});
}
static allCatching(...items) {
return _ResultFactory.allInternal(items, {
catching: true
});
}
static wrap(fn, transformError) {
return function wrapped(...args) {
return _ResultFactory.try(() => fn(...args), transformError);
};
}
static try(fn, transform) {
try {
const returnValue = fn();
if (isGenerator(returnValue)) {
return _ResultFactory.handleGenerator(returnValue);
}
if (isAsyncGenerator(returnValue)) {
const asyncResult = _ResultFactory.handleGenerator(returnValue);
return AsyncResult.fromPromiseCatching(asyncResult, transform);
}
if (isPromise(returnValue)) {
return AsyncResult.fromPromiseCatching(returnValue, transform);
}
return _ResultFactory.isResult(returnValue) ? returnValue : _ResultFactory.ok(returnValue);
} catch (caughtError) {
return _ResultFactory.error(transform?.(caughtError) ?? caughtError);
}
}
static fromAsync(valueOrFn) {
return _ResultFactory.run(typeof valueOrFn === "function" ? valueOrFn : () => valueOrFn);
}
static fromAsyncCatching(valueOrFn, transformError) {
return _ResultFactory.try(typeof valueOrFn === "function" ? valueOrFn : () => valueOrFn, transformError);
}
static handleGenerator(it) {
function handleResult(result2) {
if (!result2.ok) {
return iterate(it.return(result2));
}
return iterate(it.next(result2.value));
}
function handleStep(step) {
if (step.done) {
if (step.value instanceof Result || step.value instanceof AsyncResult) {
return step.value;
}
return _ResultFactory.ok(step.value);
}
if (step.value instanceof Result) {
return handleResult(step.value);
}
if (step.value instanceof AsyncResult) {
return step.value.then(handleResult);
}
return iterate(it.next(step.value));
}
function iterate(iteratorResult) {
return isPromise(iteratorResult) ? iteratorResult.then(handleStep) : handleStep(iteratorResult);
}
const result = iterate(it.next());
return isPromise(result) ? AsyncResult.fromPromise(result) : result;
}
static gen(generatorOrSelfOrFn, fn) {
const it = isGenerator(generatorOrSelfOrFn) || isAsyncGenerator(generatorOrSelfOrFn) ? generatorOrSelfOrFn : typeof generatorOrSelfOrFn === "function" ? generatorOrSelfOrFn() : fn?.apply(generatorOrSelfOrFn);
return _ResultFactory.handleGenerator(it);
}
static genCatching(generatorOrSelfOrFn, transformValueOrError, transformError) {
const isGen = isGenerator(generatorOrSelfOrFn) || isAsyncGenerator(generatorOrSelfOrFn);
const self = typeof generatorOrSelfOrFn === "function" || isGen ? void 0 : generatorOrSelfOrFn;
const tValue = typeof generatorOrSelfOrFn === "function" ? generatorOrSelfOrFn : transformValueOrError;
const tError = typeof generatorOrSelfOrFn === "function" || isGen ? transformValueOrError : transformError;
try {
const it = isGen ? generatorOrSelfOrFn : self ? tValue.apply(generatorOrSelfOrFn) : tValue();
const result = _ResultFactory.handleGenerator(it);
if (_ResultFactory.isAsyncResult(result)) {
return result.catch(error => AsyncResult.error(tError?.(error) ?? error));
}
return result;
} catch (error) {
return _ResultFactory.error(tError?.(error) ?? error);
}
}
static assertOk(result) {
if (!result.ok) {
throw new Error("Expected a successful result, but got an error instead");
}
}
static assertError(result) {
if (result.ok) {
throw new Error("Expected a failed result, but got a value instead");
}
}
static [Symbol.hasInstance](instance) {
return instance instanceof Result;
}
};
var Result2 = ResultFactory;
0 && (module.exports = {
AsyncResult: AsyncResult,
NonExhaustiveError: NonExhaustiveError,
Result: Result,
assertUnreachable: assertUnreachable
});//# sourceMappingURL=index.cjs.map