@clipboard-health/util-ts 5.13.2 → 5.14.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 +76 -0
- package/package.json +1 -1
- package/src/lib/errors/serviceError.d.ts +3 -1
- package/src/lib/errors/serviceError.js +0 -7
- package/src/lib/errors/serviceError.js.map +1 -1
- package/src/lib/functional/index.d.ts +1 -0
- package/src/lib/functional/index.js +1 -0
- package/src/lib/functional/index.js.map +1 -1
- package/src/lib/functional/matchServiceResult.d.ts +102 -0
- package/src/lib/functional/matchServiceResult.js +94 -0
- package/src/lib/functional/matchServiceResult.js.map +1 -0
- package/src/lib/functional/serviceResult.d.ts +10 -8
- package/src/lib/functional/serviceResult.js +0 -3
- package/src/lib/functional/serviceResult.js.map +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ TypeScript utilities.
|
|
|
8
8
|
- [Usage](#usage)
|
|
9
9
|
- [ServiceError](#serviceerror)
|
|
10
10
|
- [ServiceResult](#serviceresult)
|
|
11
|
+
- [`matchServiceResult`](#matchserviceresult)
|
|
11
12
|
- [`tryCatchAsync`](#trycatchasync)
|
|
12
13
|
- [`tryCatch`](#trycatch)
|
|
13
14
|
- [`fromSafeParseReturnType`](#fromsafeparsereturntype)
|
|
@@ -117,6 +118,10 @@ try {
|
|
|
117
118
|
|
|
118
119
|
### ServiceResult
|
|
119
120
|
|
|
121
|
+
`ServiceResult<A, E>` represents either a successful `A` value or a `ServiceError` subtype `E`.
|
|
122
|
+
The error type defaults to `ServiceError` for compatibility with existing callers. Pass an explicit
|
|
123
|
+
union when callers need to branch on the errors an operation can return.
|
|
124
|
+
|
|
120
125
|
<embedex source="packages/util-ts/examples/serviceResult.ts">
|
|
121
126
|
|
|
122
127
|
```ts
|
|
@@ -152,6 +157,77 @@ ok(isSuccess(validateUser({ email: "user@example.com", phone: "555-555-5555" }))
|
|
|
152
157
|
|
|
153
158
|
</embedex>
|
|
154
159
|
|
|
160
|
+
#### `matchServiceResult`
|
|
161
|
+
|
|
162
|
+
`matchServiceResult` maps a typed `ServiceResult` to one output while requiring a handler for every
|
|
163
|
+
literal `_tag` in its error union. Adding or removing an error type therefore updates every exhaustive
|
|
164
|
+
match at compile time. Callers that do not need exhaustive handling can continue using `isFailure` and
|
|
165
|
+
`isSuccess`.
|
|
166
|
+
|
|
167
|
+
<embedex source="packages/util-ts/examples/matchServiceResult.ts">
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
import { strictEqual } from "node:assert/strict";
|
|
171
|
+
|
|
172
|
+
import {
|
|
173
|
+
failure,
|
|
174
|
+
matchServiceResult,
|
|
175
|
+
ServiceError,
|
|
176
|
+
type ServiceResult,
|
|
177
|
+
success,
|
|
178
|
+
} from "@clipboard-health/util-ts";
|
|
179
|
+
|
|
180
|
+
interface User {
|
|
181
|
+
id: string;
|
|
182
|
+
name: string;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
class InvalidUserIdError extends ServiceError {
|
|
186
|
+
public readonly _tag = "InvalidUserIdError" as const;
|
|
187
|
+
|
|
188
|
+
public constructor() {
|
|
189
|
+
super("User ID is required");
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
class UserNotFoundError extends ServiceError {
|
|
194
|
+
public readonly _tag = "UserNotFoundError" as const;
|
|
195
|
+
|
|
196
|
+
public readonly userId: string;
|
|
197
|
+
|
|
198
|
+
public constructor({ userId }: { userId: string }) {
|
|
199
|
+
super(`User ${userId} was not found`);
|
|
200
|
+
this.userId = userId;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
type FindUserError = InvalidUserIdError | UserNotFoundError;
|
|
205
|
+
|
|
206
|
+
function findUser({ userId }: { userId: string }): ServiceResult<User, FindUserError> {
|
|
207
|
+
if (userId.length === 0) {
|
|
208
|
+
return failure(new InvalidUserIdError());
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (userId === "missing") {
|
|
212
|
+
return failure(new UserNotFoundError({ userId }));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return success({ id: userId, name: "Ada" });
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const message = matchServiceResult(findUser({ userId: "missing" }), {
|
|
219
|
+
onSuccess: (user) => `Found ${user.name}`,
|
|
220
|
+
onError: {
|
|
221
|
+
InvalidUserIdError: (error) => error.message,
|
|
222
|
+
UserNotFoundError: (error) => `User ${error.userId} was not found`,
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
strictEqual(message, "User missing was not found");
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
</embedex>
|
|
230
|
+
|
|
155
231
|
#### `tryCatchAsync`
|
|
156
232
|
|
|
157
233
|
<embedex source="packages/util-ts/examples/tryCatchAsync.ts">
|
package/package.json
CHANGED
|
@@ -97,6 +97,7 @@ export declare class ServiceError extends Error {
|
|
|
97
97
|
* @returns If the value is already a `ServiceError`, returns it unchanged. Otherwise, convert it
|
|
98
98
|
* to a `ServiceError`.
|
|
99
99
|
*/
|
|
100
|
+
static fromUnknown<E extends ServiceError>(value: E): E;
|
|
100
101
|
static fromUnknown(value: unknown): ServiceError;
|
|
101
102
|
/**
|
|
102
103
|
* Converts a ZodError to a `ServiceError`.
|
|
@@ -115,7 +116,8 @@ export declare class ServiceError extends Error {
|
|
|
115
116
|
* @param errors - Additional ServiceErrors
|
|
116
117
|
* @returns New ServiceError containing all issues from input errors
|
|
117
118
|
*/
|
|
118
|
-
static merge(error:
|
|
119
|
+
static merge<E extends ServiceError>(error: E): E;
|
|
120
|
+
static merge(error: ServiceError, additionalError: ServiceError, ...errors: readonly ServiceError[]): ServiceError;
|
|
119
121
|
static merge(error: unknown, ...errors: readonly unknown[]): ServiceError;
|
|
120
122
|
/**
|
|
121
123
|
* Creates a ServiceError from a JSON:API error object
|
|
@@ -55,13 +55,6 @@ const ERROR_METADATA = {
|
|
|
55
55
|
* Error class for service-level errors, convertible to JSON:API errors.
|
|
56
56
|
*/
|
|
57
57
|
class ServiceError extends Error {
|
|
58
|
-
/**
|
|
59
|
-
* Converts an unknown value to a `ServiceError`. Typically called from `catch` blocks.
|
|
60
|
-
*
|
|
61
|
-
* @param value - The value to convert, which can be any type
|
|
62
|
-
* @returns If the value is already a `ServiceError`, returns it unchanged. Otherwise, convert it
|
|
63
|
-
* to a `ServiceError`.
|
|
64
|
-
*/
|
|
65
58
|
static fromUnknown(value) {
|
|
66
59
|
if (value instanceof ServiceError) {
|
|
67
60
|
return value;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serviceError.js","sourceRoot":"","sources":["../../../../../../packages/util-ts/src/lib/errors/serviceError.ts"],"names":[],"mappings":";;;;AAEA,8CAA2C;AAC3C,uCAAoC;AAEpC;;GAEG;AACU,QAAA,WAAW,GAAG;IACzB,UAAU,EAAE,YAAY;IACxB,YAAY,EAAE,cAAc;IAC5B,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,UAAU;IACpB,mBAAmB,EAAE,qBAAqB;IAC1C,eAAe,EAAE,iBAAiB;IAClC,QAAQ,EAAE,UAAU;CACZ,CAAC;AAKX,MAAM,cAAc,GAAG;IACrB,UAAU,EAAE;QACV,MAAM,EAAE,GAAG;QACX,KAAK,EAAE,8BAA8B;KACtC;IACD,YAAY,EAAE;QACZ,MAAM,EAAE,GAAG;QACX,KAAK,EAAE,gCAAgC;KACxC;IACD,SAAS,EAAE;QACT,MAAM,EAAE,GAAG;QACX,KAAK,EAAE,2BAA2B;KACnC;IACD,QAAQ,EAAE;QACR,MAAM,EAAE,GAAG;QACX,KAAK,EAAE,oBAAoB;KAC5B;IACD,QAAQ,EAAE;QACR,MAAM,EAAE,GAAG;QACX,KAAK,EAAE,4BAA4B;KACpC;IACD,mBAAmB,EAAE;QACnB,MAAM,EAAE,GAAG;QACX,KAAK,EAAE,2BAA2B;KACnC;IACD,eAAe,EAAE;QACf,MAAM,EAAE,GAAG;QACX,KAAK,EAAE,wBAAwB;KAChC;IACD,QAAQ,EAAE;QACR,MAAM,EAAE,GAAG;QACX,KAAK,EAAE,uBAAuB;KAC/B;CACO,CAAC;AAoDX;;GAEG;AACH,kBAA0B,SAAQ,KAAK;
|
|
1
|
+
{"version":3,"file":"serviceError.js","sourceRoot":"","sources":["../../../../../../packages/util-ts/src/lib/errors/serviceError.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,7 @@ exports.option = exports.either = void 0;
|
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
tslib_1.__exportStar(require("./cbhResponse"), exports);
|
|
6
6
|
exports.either = tslib_1.__importStar(require("./either"));
|
|
7
|
+
tslib_1.__exportStar(require("./matchServiceResult"), exports);
|
|
7
8
|
exports.option = tslib_1.__importStar(require("./option"));
|
|
8
9
|
tslib_1.__exportStar(require("./pipe"), exports);
|
|
9
10
|
tslib_1.__exportStar(require("./serviceResult"), exports);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/util-ts/src/lib/functional/index.ts"],"names":[],"mappings":";;;;AAAA,wDAA8B;AAC9B,QAAY,MAAM,6CAAiB;AACnC,QAAY,MAAM,6CAAiB;AACnC,iDAAuB;AACvB,0DAAgC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/util-ts/src/lib/functional/index.ts"],"names":[],"mappings":";;;;AAAA,wDAA8B;AAC9B,QAAY,MAAM,6CAAiB;AACnC,+DAAqC;AACrC,QAAY,MAAM,6CAAiB;AACnC,iDAAuB;AACvB,0DAAgC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { ServiceError } from "../errors/serviceError";
|
|
2
|
+
import { type ServiceResult } from "./serviceResult";
|
|
3
|
+
/** A service error with a literal discriminant used for exhaustive matching. */
|
|
4
|
+
export type TaggedServiceError = ServiceError & {
|
|
5
|
+
readonly _tag: string;
|
|
6
|
+
};
|
|
7
|
+
/** A handler for every tagged member of a ServiceResult error union. */
|
|
8
|
+
export type ServiceResultErrorHandlers<E extends TaggedServiceError> = {
|
|
9
|
+
readonly [Tag in E["_tag"]]: (error: Extract<E, {
|
|
10
|
+
readonly _tag: Tag;
|
|
11
|
+
}>) => unknown;
|
|
12
|
+
};
|
|
13
|
+
type ErrorHandlerOutput<Handlers> = {
|
|
14
|
+
readonly [Tag in keyof Handlers]: Handlers[Tag] extends (...arguments_: infer _Arguments) => infer Output ? Output : never;
|
|
15
|
+
}[keyof Handlers];
|
|
16
|
+
type ExactErrorHandlers<E extends TaggedServiceError, Handlers extends ServiceResultErrorHandlers<E>> = Handlers & Record<Exclude<keyof Handlers, E["_tag"]>, never>;
|
|
17
|
+
type IsUnion<Value, Whole = Value> = Value extends unknown ? [Whole] extends [Value] ? false : true : never;
|
|
18
|
+
type IsSingleLiteralTag<Tag extends string> = [Tag] extends [never] ? false : string extends Tag ? false : IsUnion<Tag> extends true ? false : true;
|
|
19
|
+
type InvalidLiteralTagMember<E extends TaggedServiceError> = E extends unknown ? IsSingleLiteralTag<E["_tag"]> extends true ? never : E : never;
|
|
20
|
+
type LiteralErrorTagConstraint<E extends TaggedServiceError> = [
|
|
21
|
+
InvalidLiteralTagMember<E>
|
|
22
|
+
] extends [never] ? unknown : never;
|
|
23
|
+
/**
|
|
24
|
+
* Exhaustively maps a ServiceResult with tagged errors to a single output value.
|
|
25
|
+
*
|
|
26
|
+
* Each member of the error union must expose exactly one literal `_tag`.
|
|
27
|
+
*
|
|
28
|
+
* Adding a member to the error union requires adding its `_tag` handler before
|
|
29
|
+
* the call will compile.
|
|
30
|
+
*
|
|
31
|
+
* @throws {TypeError} If a runtime caller omits the handler for a failure's tag.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* <embedex source="packages/util-ts/examples/matchServiceResult.ts">
|
|
35
|
+
*
|
|
36
|
+
* ```ts
|
|
37
|
+
* import { strictEqual } from "node:assert/strict";
|
|
38
|
+
*
|
|
39
|
+
* import {
|
|
40
|
+
* failure,
|
|
41
|
+
* matchServiceResult,
|
|
42
|
+
* ServiceError,
|
|
43
|
+
* type ServiceResult,
|
|
44
|
+
* success,
|
|
45
|
+
* } from "@clipboard-health/util-ts";
|
|
46
|
+
*
|
|
47
|
+
* interface User {
|
|
48
|
+
* id: string;
|
|
49
|
+
* name: string;
|
|
50
|
+
* }
|
|
51
|
+
*
|
|
52
|
+
* class InvalidUserIdError extends ServiceError {
|
|
53
|
+
* public readonly _tag = "InvalidUserIdError" as const;
|
|
54
|
+
*
|
|
55
|
+
* public constructor() {
|
|
56
|
+
* super("User ID is required");
|
|
57
|
+
* }
|
|
58
|
+
* }
|
|
59
|
+
*
|
|
60
|
+
* class UserNotFoundError extends ServiceError {
|
|
61
|
+
* public readonly _tag = "UserNotFoundError" as const;
|
|
62
|
+
*
|
|
63
|
+
* public readonly userId: string;
|
|
64
|
+
*
|
|
65
|
+
* public constructor({ userId }: { userId: string }) {
|
|
66
|
+
* super(`User ${userId} was not found`);
|
|
67
|
+
* this.userId = userId;
|
|
68
|
+
* }
|
|
69
|
+
* }
|
|
70
|
+
*
|
|
71
|
+
* type FindUserError = InvalidUserIdError | UserNotFoundError;
|
|
72
|
+
*
|
|
73
|
+
* function findUser({ userId }: { userId: string }): ServiceResult<User, FindUserError> {
|
|
74
|
+
* if (userId.length === 0) {
|
|
75
|
+
* return failure(new InvalidUserIdError());
|
|
76
|
+
* }
|
|
77
|
+
*
|
|
78
|
+
* if (userId === "missing") {
|
|
79
|
+
* return failure(new UserNotFoundError({ userId }));
|
|
80
|
+
* }
|
|
81
|
+
*
|
|
82
|
+
* return success({ id: userId, name: "Ada" });
|
|
83
|
+
* }
|
|
84
|
+
*
|
|
85
|
+
* const message = matchServiceResult(findUser({ userId: "missing" }), {
|
|
86
|
+
* onSuccess: (user) => `Found ${user.name}`,
|
|
87
|
+
* onError: {
|
|
88
|
+
* InvalidUserIdError: (error) => error.message,
|
|
89
|
+
* UserNotFoundError: (error) => `User ${error.userId} was not found`,
|
|
90
|
+
* },
|
|
91
|
+
* });
|
|
92
|
+
*
|
|
93
|
+
* strictEqual(message, "User missing was not found");
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* </embedex>
|
|
97
|
+
*/
|
|
98
|
+
export declare function matchServiceResult<A, E extends TaggedServiceError, SuccessOutput, const Handlers extends ServiceResultErrorHandlers<E>>(result: ServiceResult<A, E> & LiteralErrorTagConstraint<E>, options: {
|
|
99
|
+
readonly onSuccess: (value: A) => SuccessOutput;
|
|
100
|
+
readonly onError: ExactErrorHandlers<E, Handlers>;
|
|
101
|
+
}): SuccessOutput | ErrorHandlerOutput<Handlers>;
|
|
102
|
+
export {};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.matchServiceResult = matchServiceResult;
|
|
4
|
+
const serviceResult_1 = require("./serviceResult");
|
|
5
|
+
/**
|
|
6
|
+
* Exhaustively maps a ServiceResult with tagged errors to a single output value.
|
|
7
|
+
*
|
|
8
|
+
* Each member of the error union must expose exactly one literal `_tag`.
|
|
9
|
+
*
|
|
10
|
+
* Adding a member to the error union requires adding its `_tag` handler before
|
|
11
|
+
* the call will compile.
|
|
12
|
+
*
|
|
13
|
+
* @throws {TypeError} If a runtime caller omits the handler for a failure's tag.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* <embedex source="packages/util-ts/examples/matchServiceResult.ts">
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { strictEqual } from "node:assert/strict";
|
|
20
|
+
*
|
|
21
|
+
* import {
|
|
22
|
+
* failure,
|
|
23
|
+
* matchServiceResult,
|
|
24
|
+
* ServiceError,
|
|
25
|
+
* type ServiceResult,
|
|
26
|
+
* success,
|
|
27
|
+
* } from "@clipboard-health/util-ts";
|
|
28
|
+
*
|
|
29
|
+
* interface User {
|
|
30
|
+
* id: string;
|
|
31
|
+
* name: string;
|
|
32
|
+
* }
|
|
33
|
+
*
|
|
34
|
+
* class InvalidUserIdError extends ServiceError {
|
|
35
|
+
* public readonly _tag = "InvalidUserIdError" as const;
|
|
36
|
+
*
|
|
37
|
+
* public constructor() {
|
|
38
|
+
* super("User ID is required");
|
|
39
|
+
* }
|
|
40
|
+
* }
|
|
41
|
+
*
|
|
42
|
+
* class UserNotFoundError extends ServiceError {
|
|
43
|
+
* public readonly _tag = "UserNotFoundError" as const;
|
|
44
|
+
*
|
|
45
|
+
* public readonly userId: string;
|
|
46
|
+
*
|
|
47
|
+
* public constructor({ userId }: { userId: string }) {
|
|
48
|
+
* super(`User ${userId} was not found`);
|
|
49
|
+
* this.userId = userId;
|
|
50
|
+
* }
|
|
51
|
+
* }
|
|
52
|
+
*
|
|
53
|
+
* type FindUserError = InvalidUserIdError | UserNotFoundError;
|
|
54
|
+
*
|
|
55
|
+
* function findUser({ userId }: { userId: string }): ServiceResult<User, FindUserError> {
|
|
56
|
+
* if (userId.length === 0) {
|
|
57
|
+
* return failure(new InvalidUserIdError());
|
|
58
|
+
* }
|
|
59
|
+
*
|
|
60
|
+
* if (userId === "missing") {
|
|
61
|
+
* return failure(new UserNotFoundError({ userId }));
|
|
62
|
+
* }
|
|
63
|
+
*
|
|
64
|
+
* return success({ id: userId, name: "Ada" });
|
|
65
|
+
* }
|
|
66
|
+
*
|
|
67
|
+
* const message = matchServiceResult(findUser({ userId: "missing" }), {
|
|
68
|
+
* onSuccess: (user) => `Found ${user.name}`,
|
|
69
|
+
* onError: {
|
|
70
|
+
* InvalidUserIdError: (error) => error.message,
|
|
71
|
+
* UserNotFoundError: (error) => `User ${error.userId} was not found`,
|
|
72
|
+
* },
|
|
73
|
+
* });
|
|
74
|
+
*
|
|
75
|
+
* strictEqual(message, "User missing was not found");
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* </embedex>
|
|
79
|
+
*/
|
|
80
|
+
function matchServiceResult(result, options) {
|
|
81
|
+
if ((0, serviceResult_1.isSuccess)(result)) {
|
|
82
|
+
return options.onSuccess(result.value);
|
|
83
|
+
}
|
|
84
|
+
// TypeScript cannot correlate an indexed handler with the matching member of
|
|
85
|
+
// a discriminated union. ExactErrorHandlers guarantees this relationship.
|
|
86
|
+
const handlers = options.onError;
|
|
87
|
+
const tag = result.error._tag;
|
|
88
|
+
const handler = (Object.hasOwn(handlers, tag) ? handlers[tag] : undefined);
|
|
89
|
+
if (handler === undefined) {
|
|
90
|
+
throw new TypeError(`Missing ServiceResult error handler for ${result.error._tag}`);
|
|
91
|
+
}
|
|
92
|
+
return handler(result.error);
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=matchServiceResult.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matchServiceResult.js","sourceRoot":"","sources":["../../../../../../packages/util-ts/src/lib/functional/matchServiceResult.ts"],"names":[],"mappings":";;;AACA,mDAAgE;AAiDhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0EG;AACH,4BAME,MAA0D,EAC1D,OAGC;IAED,IAAI,IAAA,yBAAS,EAAC,MAAM,CAAC,EAAE,CAAC;QACtB,OAAO,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;IAED,6EAA6E;IAC7E,0EAA0E;IAC1E,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAwC,CAAC;IAClE,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,IAAiB,CAAC;IAC3C,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAE5D,CAAC;IAEd,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,SAAS,CAAC,2CAA2C,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IACtF,CAAC;IAED,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -10,39 +10,41 @@ export interface Failure<E> {
|
|
|
10
10
|
readonly error: E;
|
|
11
11
|
}
|
|
12
12
|
export type SuccessResult<A> = Right<A> & Success<A>;
|
|
13
|
-
export type FailureResult = Left<
|
|
13
|
+
export type FailureResult<E extends ServiceError = ServiceError> = Left<E> & Failure<E>;
|
|
14
14
|
/**
|
|
15
15
|
* Represents the result of a service operation that may fail.
|
|
16
16
|
*
|
|
17
17
|
* The type is also an {@link Either}, allowing functions built for {@link Either}.
|
|
18
18
|
*
|
|
19
19
|
* @template A The type of the successful result value
|
|
20
|
+
* @template E The type of the failed result error
|
|
20
21
|
*/
|
|
21
|
-
export type ServiceResult<A> = SuccessResult<A> | FailureResult
|
|
22
|
+
export type ServiceResult<A, E extends ServiceError = ServiceError> = SuccessResult<A> | FailureResult<E>;
|
|
22
23
|
/**
|
|
23
24
|
* Creates a successful ServiceResult.
|
|
24
25
|
*
|
|
25
26
|
* Call with no argument for a `ServiceResult<void>`; the value is `undefined`.
|
|
26
27
|
*/
|
|
27
|
-
export declare function success(): ServiceResult<void>;
|
|
28
|
-
export declare function success<A>(value: A): ServiceResult<A>;
|
|
28
|
+
export declare function success<E extends ServiceError = ServiceError>(): ServiceResult<void, E>;
|
|
29
|
+
export declare function success<A, E extends ServiceError = ServiceError>(value: A): ServiceResult<A, E>;
|
|
29
30
|
/**
|
|
30
31
|
* Creates a failed ServiceResult.
|
|
31
32
|
*/
|
|
32
|
-
export declare function failure(
|
|
33
|
+
export declare function failure<E extends ServiceError>(error: E): FailureResult<E>;
|
|
34
|
+
export declare function failure(params: ServiceErrorParams): FailureResult;
|
|
33
35
|
/**
|
|
34
36
|
* Type guard for failure results.
|
|
35
37
|
*/
|
|
36
|
-
export declare function isFailure<A>(result: ServiceResult<A>): result is
|
|
38
|
+
export declare function isFailure<A, E extends ServiceError = ServiceError>(result: ServiceResult<A, E>): result is FailureResult<E>;
|
|
37
39
|
/**
|
|
38
40
|
* Type guard for success results.
|
|
39
41
|
*/
|
|
40
|
-
export declare function isSuccess<A>(result: ServiceResult<A>): result is
|
|
42
|
+
export declare function isSuccess<A, E extends ServiceError = ServiceError>(result: ServiceResult<A, E>): result is SuccessResult<A>;
|
|
41
43
|
/**
|
|
42
44
|
* Alias for {@link mapLeft}.
|
|
43
45
|
* Note: returns an {@link Either}, not a ServiceResult.
|
|
44
46
|
*/
|
|
45
|
-
export declare function mapFailure<G>(f: (left:
|
|
47
|
+
export declare function mapFailure<G, E extends ServiceError = ServiceError>(f: (left: E) => G): <A>(result: ServiceResult<A, E>) => Either<G, A>;
|
|
46
48
|
/**
|
|
47
49
|
* Converts a promise returning function into a ServiceResult by handling potential rejections.
|
|
48
50
|
* If the promise returning function resolves successfully, returns a success ServiceResult.
|
|
@@ -18,9 +18,6 @@ function success(value) {
|
|
|
18
18
|
value,
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
|
-
/**
|
|
22
|
-
* Creates a failed ServiceResult.
|
|
23
|
-
*/
|
|
24
21
|
function failure(params) {
|
|
25
22
|
const error = params instanceof serviceError_1.ServiceError ? params : new serviceError_1.ServiceError(params);
|
|
26
23
|
return Object.freeze({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serviceResult.js","sourceRoot":"","sources":["../../../../../../packages/util-ts/src/lib/functional/serviceResult.ts"],"names":[],"mappings":";;;;;;;;;;AAEA,yDAA+E;AAC/E,qCAAuE;
|
|
1
|
+
{"version":3,"file":"serviceResult.js","sourceRoot":"","sources":["../../../../../../packages/util-ts/src/lib/functional/serviceResult.ts"],"names":[],"mappings":";;;;;;;;;;AAEA,yDAA+E;AAC/E,qCAAuE;AAkCvE,iBACE,KAAS;IAET,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,OAAO,EAAE,IAAI;QACb,SAAS,EAAE,IAAI;QACf,KAAK,EAAE,KAAK;QACZ,KAAK;KACG,CAAC,CAAC;AACd,CAAC;AAOD,iBAAwB,MAAyC;IAC/D,MAAM,KAAK,GAAG,MAAM,YAAY,2BAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,2BAAY,CAAC,MAAM,CAAC,CAAC;IACjF,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,OAAO,EAAE,KAAK;QACd,SAAS,EAAE,KAAK;QAChB,IAAI,EAAE,KAAK;QACX,KAAK;KACG,CAAC,CAAC;AACd,CAAC;AAED;;GAEG;AACH,mBACE,MAA2B;IAE3B,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC;AAC3B,CAAC;AAED;;GAEG;AACH,mBACE,MAA2B;IAE3B,OAAO,MAAM,CAAC,SAAS,CAAC;AAC1B,CAAC;AAED;;;GAGG;AACH,oBACE,CAAiB;IAEjB,OAAO,IAAA,gBAAO,EAAC,CAAC,CAAC,CAAC;AACpB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACI,KAAK,wBACV,CAAmB,EACnB,OAAyC;IAEzC,IAAI,CAAC;QACH,OAAO,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACrC,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,kBACE,CAAU,EACV,OAAyC;IAEzC,IAAI,CAAC;QACH,OAAO,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IACtB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACrC,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,iCACE,KAAsC;IAEtC,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,2BAAY,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;AAC/F,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAClB,KAAc,EACd,OAAyC;IAEzC,IAAI,CAAC;QACH,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,YAAY,EAAE,CAAC;QACtB,OAAO,OAAO,CAAC,2BAAY,CAAC,KAAK,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC;IAC1D,CAAC;AACH,CAAC"}
|