resultar 3.5.1 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +149 -18
- package/dist/index.d.ts +900 -760
- package/dist/index.js +286 -7
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -192,6 +192,10 @@ var ResultNamespace = class {
|
|
|
192
192
|
return value instanceof Ok || value instanceof Err;
|
|
193
193
|
}
|
|
194
194
|
/**
|
|
195
|
+
* Alias for the top-level `safeTry` generator workflow.
|
|
196
|
+
*/
|
|
197
|
+
static gen = safeTry;
|
|
198
|
+
/**
|
|
195
199
|
* Creates a `Result` by running a function that might throw.
|
|
196
200
|
* If the function throws, the error will be caught and returned as an `Err`.
|
|
197
201
|
* If the function succeeds, the value will be wrapped in an `Ok`.
|
|
@@ -302,6 +306,7 @@ var ResultNamespace = class {
|
|
|
302
306
|
return forEachResult(items, f, options);
|
|
303
307
|
}
|
|
304
308
|
};
|
|
309
|
+
/** Static helpers for creating, combining, and iterating over Result values. */
|
|
305
310
|
const Result = ResultNamespace;
|
|
306
311
|
var ResultVariant = class extends Pipeable {
|
|
307
312
|
filterOrElse(predicate, onFalse) {
|
|
@@ -992,11 +997,7 @@ const runConcurrentResultAsyncItems = (options) => Promise.try(async () => {
|
|
|
992
997
|
};
|
|
993
998
|
const schedule = () => {
|
|
994
999
|
if (settled) return;
|
|
995
|
-
|
|
996
|
-
finishIfDone();
|
|
997
|
-
return;
|
|
998
|
-
}
|
|
999
|
-
while (!state.iteratorDone && state.activeCount < concurrency) {
|
|
1000
|
+
while (!state.stopScheduling && !state.iteratorDone && state.activeCount < concurrency) {
|
|
1000
1001
|
const next = readNext();
|
|
1001
1002
|
if (next === void 0) return;
|
|
1002
1003
|
if (next.done === true) {
|
|
@@ -1019,7 +1020,6 @@ const runConcurrentResultAsyncItems = (options) => Promise.try(async () => {
|
|
|
1019
1020
|
if (settled) return;
|
|
1020
1021
|
state.activeCount -= 1;
|
|
1021
1022
|
schedule();
|
|
1022
|
-
finishIfDone();
|
|
1023
1023
|
});
|
|
1024
1024
|
} catch (error) {
|
|
1025
1025
|
rejectOnce(error);
|
|
@@ -1436,6 +1436,7 @@ const callAsyncSideEffect = async (effect) => {
|
|
|
1436
1436
|
await effect();
|
|
1437
1437
|
} catch {}
|
|
1438
1438
|
};
|
|
1439
|
+
/** Async-disposable wrapper that finalizes a `ResultAsync` exactly once. */
|
|
1439
1440
|
var DisposableResultAsync = class {
|
|
1440
1441
|
innerPromise;
|
|
1441
1442
|
finalizer;
|
|
@@ -2026,4 +2027,282 @@ const runPromise = (result) => result.unwrapOrThrow();
|
|
|
2026
2027
|
/** Compatibility alias. Prefer `tryResultAsync` in new code. */
|
|
2027
2028
|
const tryCatchAsync = tryResultAsync;
|
|
2028
2029
|
//#endregion
|
|
2029
|
-
|
|
2030
|
+
//#region src/result-task.ts
|
|
2031
|
+
const success = (value) => ({
|
|
2032
|
+
_tag: "Success",
|
|
2033
|
+
value
|
|
2034
|
+
});
|
|
2035
|
+
const failure = (cause) => ({
|
|
2036
|
+
_tag: "Failure",
|
|
2037
|
+
cause
|
|
2038
|
+
});
|
|
2039
|
+
const failed = (error) => failure({
|
|
2040
|
+
_tag: "Fail",
|
|
2041
|
+
error
|
|
2042
|
+
});
|
|
2043
|
+
const died = (defect) => failure({
|
|
2044
|
+
_tag: "Die",
|
|
2045
|
+
defect
|
|
2046
|
+
});
|
|
2047
|
+
const throwDefect = (defect) => {
|
|
2048
|
+
throw defect;
|
|
2049
|
+
};
|
|
2050
|
+
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
2051
|
+
const createMissingServiceError = (identifier) => {
|
|
2052
|
+
const error = /* @__PURE__ */ new Error(`Missing ResultTask service: ${identifier}`);
|
|
2053
|
+
error.name = "MissingServiceError";
|
|
2054
|
+
return error;
|
|
2055
|
+
};
|
|
2056
|
+
const isResultTaskYield = (value) => isRecord(value) && value["_tag"] === "ResultTask" && value["task"] instanceof ResultTask;
|
|
2057
|
+
const isServiceYield = (value) => isRecord(value) && value["_tag"] === "Service" && value["tag"] instanceof ServiceTagValue;
|
|
2058
|
+
const lookupService = (tag, context) => {
|
|
2059
|
+
if (context.services.has(tag.key)) return context.services.get(tag.key);
|
|
2060
|
+
return context.namedServices.get(tag.identifier);
|
|
2061
|
+
};
|
|
2062
|
+
const executeTaskSafely = async (task, executeTask) => {
|
|
2063
|
+
try {
|
|
2064
|
+
return await executeTask(task);
|
|
2065
|
+
} catch (error) {
|
|
2066
|
+
return { error };
|
|
2067
|
+
}
|
|
2068
|
+
};
|
|
2069
|
+
const closeGenerator = async (iterator, exit, runtime) => {
|
|
2070
|
+
try {
|
|
2071
|
+
let step = iterator.return(void 0);
|
|
2072
|
+
let finalExit = exit;
|
|
2073
|
+
while (step.done === false) if (isResultTaskYield(step.value)) {
|
|
2074
|
+
const outcome = await executeTaskSafely(step.value.task, runtime.executeTask);
|
|
2075
|
+
if ("error" in outcome) {
|
|
2076
|
+
finalExit = died(outcome.error);
|
|
2077
|
+
step = iterator.return(void 0);
|
|
2078
|
+
} else if (outcome._tag === "Failure") {
|
|
2079
|
+
finalExit = outcome;
|
|
2080
|
+
step = iterator.return(void 0);
|
|
2081
|
+
} else step = iterator.next(outcome);
|
|
2082
|
+
} else if (isServiceYield(step.value)) {
|
|
2083
|
+
const service = lookupService(step.value.tag, runtime.context);
|
|
2084
|
+
if (service === void 0) {
|
|
2085
|
+
finalExit = died(createMissingServiceError(step.value.tag.identifier));
|
|
2086
|
+
step = iterator.return(void 0);
|
|
2087
|
+
} else step = iterator.next(service);
|
|
2088
|
+
} else {
|
|
2089
|
+
finalExit = died(/* @__PURE__ */ new TypeError("ResultTask.gen yielded an unsupported value"));
|
|
2090
|
+
step = iterator.return(void 0);
|
|
2091
|
+
}
|
|
2092
|
+
return finalExit;
|
|
2093
|
+
} catch (error) {
|
|
2094
|
+
return died(error);
|
|
2095
|
+
}
|
|
2096
|
+
};
|
|
2097
|
+
const withServices = (context, services) => ({
|
|
2098
|
+
namedServices: context.namedServices,
|
|
2099
|
+
services,
|
|
2100
|
+
signal: context.signal
|
|
2101
|
+
});
|
|
2102
|
+
const withNamedServices = (context, namedServices) => ({
|
|
2103
|
+
namedServices,
|
|
2104
|
+
services: context.services,
|
|
2105
|
+
signal: context.signal
|
|
2106
|
+
});
|
|
2107
|
+
var ServiceTagValue = class {
|
|
2108
|
+
_tag = "ServiceTag";
|
|
2109
|
+
identifier;
|
|
2110
|
+
key;
|
|
2111
|
+
constructor(identifier) {
|
|
2112
|
+
this.identifier = identifier;
|
|
2113
|
+
this.key = Symbol(identifier);
|
|
2114
|
+
}
|
|
2115
|
+
*[Symbol.iterator]() {
|
|
2116
|
+
return yield {
|
|
2117
|
+
_tag: "Service",
|
|
2118
|
+
tag: this
|
|
2119
|
+
};
|
|
2120
|
+
}
|
|
2121
|
+
};
|
|
2122
|
+
/**
|
|
2123
|
+
* A lazy, composable workflow that can succeed with `A` or fail with `E`.
|
|
2124
|
+
*
|
|
2125
|
+
* Constructing a `ResultTask` never runs its work. Use `runResult`, `runExit`, or `runPromise` at an
|
|
2126
|
+
* explicit application boundary to execute it.
|
|
2127
|
+
*/
|
|
2128
|
+
var ResultTask = class ResultTask extends Pipeable {
|
|
2129
|
+
execute;
|
|
2130
|
+
constructor(execute) {
|
|
2131
|
+
super();
|
|
2132
|
+
this.execute = execute;
|
|
2133
|
+
}
|
|
2134
|
+
/** Creates a task that succeeds with `value` when it is executed. */
|
|
2135
|
+
static succeed(value) {
|
|
2136
|
+
return new ResultTask(() => Promise.resolve(success(value)));
|
|
2137
|
+
}
|
|
2138
|
+
/** Creates a task that fails with `error` when it is executed. */
|
|
2139
|
+
static fail(error) {
|
|
2140
|
+
return new ResultTask(() => Promise.resolve(failed(error)));
|
|
2141
|
+
}
|
|
2142
|
+
/** Lifts an already computed Result into a lazy task. */
|
|
2143
|
+
static fromResult(result) {
|
|
2144
|
+
return new ResultTask(() => Promise.resolve(result.isOk() ? success(result.value) : failed(result.error)));
|
|
2145
|
+
}
|
|
2146
|
+
/** Creates a task for synchronous work. Thrown values are defects, not typed failures. */
|
|
2147
|
+
static sync(evaluate) {
|
|
2148
|
+
return new ResultTask(() => Promise.resolve(success(evaluate())));
|
|
2149
|
+
}
|
|
2150
|
+
/** Creates a task that maps synchronous throws into the typed error channel. */
|
|
2151
|
+
static try(options) {
|
|
2152
|
+
return new ResultTask(() => {
|
|
2153
|
+
try {
|
|
2154
|
+
return Promise.resolve(success(options.try()));
|
|
2155
|
+
} catch (error) {
|
|
2156
|
+
return Promise.resolve(failed(options.catch(error)));
|
|
2157
|
+
}
|
|
2158
|
+
});
|
|
2159
|
+
}
|
|
2160
|
+
/** Creates a lazy task that maps synchronous throws and promise rejections into `E`. */
|
|
2161
|
+
static tryPromise(options) {
|
|
2162
|
+
return new ResultTask(async (context) => {
|
|
2163
|
+
try {
|
|
2164
|
+
return success(await options.try(context.signal));
|
|
2165
|
+
} catch (error) {
|
|
2166
|
+
return failed(options.catch(error));
|
|
2167
|
+
}
|
|
2168
|
+
});
|
|
2169
|
+
}
|
|
2170
|
+
/** Creates a service tag that can be requested with `yield*` inside `ResultTask.gen`. */
|
|
2171
|
+
static service(identifier) {
|
|
2172
|
+
return new ServiceTagValue(identifier);
|
|
2173
|
+
}
|
|
2174
|
+
/** Builds a task from a generator and short-circuits on the first failed task. */
|
|
2175
|
+
static gen(body) {
|
|
2176
|
+
return new ResultTask(async (context) => {
|
|
2177
|
+
try {
|
|
2178
|
+
const iterator = body();
|
|
2179
|
+
const executeTask = (task) => task.execute(context);
|
|
2180
|
+
let step = iterator.next();
|
|
2181
|
+
while (step.done === false) if (isResultTaskYield(step.value)) {
|
|
2182
|
+
const outcome = await executeTaskSafely(step.value.task, executeTask);
|
|
2183
|
+
if ("error" in outcome) return await closeGenerator(iterator, died(outcome.error), {
|
|
2184
|
+
context,
|
|
2185
|
+
executeTask
|
|
2186
|
+
});
|
|
2187
|
+
if (outcome._tag === "Failure") return await closeGenerator(iterator, outcome, {
|
|
2188
|
+
context,
|
|
2189
|
+
executeTask
|
|
2190
|
+
});
|
|
2191
|
+
step = iterator.next(outcome);
|
|
2192
|
+
} else if (isServiceYield(step.value)) {
|
|
2193
|
+
const service = lookupService(step.value.tag, context);
|
|
2194
|
+
if (service === void 0) return await closeGenerator(iterator, died(createMissingServiceError(step.value.tag.identifier)), {
|
|
2195
|
+
context,
|
|
2196
|
+
executeTask
|
|
2197
|
+
});
|
|
2198
|
+
step = iterator.next(service);
|
|
2199
|
+
} else return await closeGenerator(iterator, died(/* @__PURE__ */ new TypeError("ResultTask.gen yielded an unsupported value")), {
|
|
2200
|
+
context,
|
|
2201
|
+
executeTask
|
|
2202
|
+
});
|
|
2203
|
+
return success(step.value);
|
|
2204
|
+
} catch (error) {
|
|
2205
|
+
return died(error);
|
|
2206
|
+
}
|
|
2207
|
+
});
|
|
2208
|
+
}
|
|
2209
|
+
/** Maps the success value without executing the task. */
|
|
2210
|
+
map(f) {
|
|
2211
|
+
return new ResultTask(async (context) => {
|
|
2212
|
+
const exit = await this.execute(context);
|
|
2213
|
+
if (exit._tag === "Failure") return exit;
|
|
2214
|
+
return success(f(exit.value));
|
|
2215
|
+
});
|
|
2216
|
+
}
|
|
2217
|
+
/** Chains another task from the success value without executing either task immediately. */
|
|
2218
|
+
flatMap(f) {
|
|
2219
|
+
return new ResultTask(async (context) => {
|
|
2220
|
+
const exit = await this.execute(context);
|
|
2221
|
+
if (exit._tag === "Failure") return exit;
|
|
2222
|
+
return f(exit.value).execute(context);
|
|
2223
|
+
});
|
|
2224
|
+
}
|
|
2225
|
+
/** Alias for `flatMap`, matching the existing Resultar vocabulary. */
|
|
2226
|
+
andThen(f) {
|
|
2227
|
+
return this.flatMap(f);
|
|
2228
|
+
}
|
|
2229
|
+
/** Recovers from a typed failure without recovering from a runtime defect. */
|
|
2230
|
+
catchAll(f) {
|
|
2231
|
+
return new ResultTask(async (context) => {
|
|
2232
|
+
const exit = await this.execute(context);
|
|
2233
|
+
if (exit._tag === "Success") return exit;
|
|
2234
|
+
if (exit.cause._tag === "Die") return exit;
|
|
2235
|
+
return f(exit.cause.error).execute(context);
|
|
2236
|
+
});
|
|
2237
|
+
}
|
|
2238
|
+
/** Enables `yield* task` inside `ResultTask.gen` workflows. */
|
|
2239
|
+
*[Symbol.iterator]() {
|
|
2240
|
+
const exit = yield {
|
|
2241
|
+
_tag: "ResultTask",
|
|
2242
|
+
task: this
|
|
2243
|
+
};
|
|
2244
|
+
if (exit._tag === "Success") return exit.value;
|
|
2245
|
+
return throwDefect(/* @__PURE__ */ new Error("ResultTask generator received a failed task result"));
|
|
2246
|
+
}
|
|
2247
|
+
/** Provides one required service and removes it from the task's requirements. */
|
|
2248
|
+
static provideService(task, tag, service) {
|
|
2249
|
+
return new ResultTask(async (context) => {
|
|
2250
|
+
const services = new Map([...context.services, [tag.key, service]]);
|
|
2251
|
+
return task.execute(withServices(context, services));
|
|
2252
|
+
});
|
|
2253
|
+
}
|
|
2254
|
+
/** Provides all requirements using an object keyed by service identifier. */
|
|
2255
|
+
static provideServices(task, services) {
|
|
2256
|
+
return new ResultTask(async (context) => {
|
|
2257
|
+
const namedServices = new Map(context.namedServices);
|
|
2258
|
+
for (const [identifier, service] of Object.entries(services)) namedServices.set(identifier, service);
|
|
2259
|
+
return task.execute(withNamedServices(context, namedServices));
|
|
2260
|
+
});
|
|
2261
|
+
}
|
|
2262
|
+
/** Maps the success value using the canonical functional form. */
|
|
2263
|
+
static map(task, f) {
|
|
2264
|
+
return task.map(f);
|
|
2265
|
+
}
|
|
2266
|
+
/** Chains a task using the canonical functional form. */
|
|
2267
|
+
static flatMap(task, f) {
|
|
2268
|
+
return task.flatMap(f);
|
|
2269
|
+
}
|
|
2270
|
+
/** Recovers from a typed failure using the canonical functional form. */
|
|
2271
|
+
static catchAll(task, f) {
|
|
2272
|
+
return task.catchAll(f);
|
|
2273
|
+
}
|
|
2274
|
+
static async runExitInternal(task, options) {
|
|
2275
|
+
const signal = options?.signal ?? new AbortController().signal;
|
|
2276
|
+
const suppliedServices = options !== void 0 && "services" in options ? options.services : void 0;
|
|
2277
|
+
const context = {
|
|
2278
|
+
namedServices: new Map(Object.entries(suppliedServices ?? {})),
|
|
2279
|
+
services: /* @__PURE__ */ new Map(),
|
|
2280
|
+
signal
|
|
2281
|
+
};
|
|
2282
|
+
try {
|
|
2283
|
+
return await task.execute(context);
|
|
2284
|
+
} catch (error) {
|
|
2285
|
+
return died(error);
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
static async runResultInternal(task, options) {
|
|
2289
|
+
const exit = await ResultTask.runExitInternal(task, options);
|
|
2290
|
+
if (exit._tag === "Success") return ok(exit.value);
|
|
2291
|
+
if (exit.cause._tag === "Fail") return err(exit.cause.error);
|
|
2292
|
+
return throwDefect(exit.cause.defect);
|
|
2293
|
+
}
|
|
2294
|
+
/** Runs a task and preserves success, typed failure, and runtime defects in an `Exit`. */
|
|
2295
|
+
static runExit(task, ...args) {
|
|
2296
|
+
return ResultTask.runExitInternal(task, args[0]);
|
|
2297
|
+
}
|
|
2298
|
+
/** Runs a task and returns a Result, rejecting only when a runtime defect occurs. */
|
|
2299
|
+
static runResult(task, ...args) {
|
|
2300
|
+
return ResultTask.runResultInternal(task, args[0]);
|
|
2301
|
+
}
|
|
2302
|
+
/** Runs a task and returns the success value, rejecting on typed failures or defects. */
|
|
2303
|
+
static async runPromise(task, ...args) {
|
|
2304
|
+
return (await ResultTask.runResultInternal(task, args[0])).unwrapOrThrow();
|
|
2305
|
+
}
|
|
2306
|
+
};
|
|
2307
|
+
//#endregion
|
|
2308
|
+
export { AbortError, DisposableResult, DisposableResultAsync, Result, ResultAsync, ResultTask, createTaggedError, tryResult as default, tryResult as try, tryResult, err, errAsync, findCause, fromCallback, fromPromise, fromSafePromise, fromThrowable, fromThrowableAsync, isAbortError, isError, isRedacted, matchError, matchErrorPartial, ok, okAsync, redact, revealRedacted, runPromise, runSync, safeTry, taggedEnum, tryResultAsync as tryAsync, tryResultAsync, tryCatch, tryCatchAsync, unit, unitAsync };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "resultar",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.0",
|
|
4
4
|
"description": "Result pattern for TypeScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"error-handling",
|
|
@@ -41,16 +41,16 @@
|
|
|
41
41
|
}
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@stryker-mutator/core": "
|
|
45
|
-
"@stryker-mutator/vitest-runner": "
|
|
46
|
-
"@types/node": "26.
|
|
47
|
-
"@vitest/coverage-v8": "4.1.
|
|
48
|
-
"@vitest/ui": "4.1.
|
|
49
|
-
"fallow": "3.
|
|
50
|
-
"tsx": "4.23.
|
|
44
|
+
"@stryker-mutator/core": "10.0.0",
|
|
45
|
+
"@stryker-mutator/vitest-runner": "10.0.0",
|
|
46
|
+
"@types/node": "26.4.0",
|
|
47
|
+
"@vitest/coverage-v8": "4.1.11",
|
|
48
|
+
"@vitest/ui": "4.1.11",
|
|
49
|
+
"fallow": "3.20.0",
|
|
50
|
+
"tsx": "4.23.13",
|
|
51
51
|
"typescript": "7.0.2",
|
|
52
|
-
"vite-plus": "0.
|
|
53
|
-
"vitest": "4.1.
|
|
52
|
+
"vite-plus": "0.3.0",
|
|
53
|
+
"vitest": "4.1.11"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"typescript": ">=7.0.0"
|