@clipboard-health/util-ts 3.8.1 → 3.9.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 +110 -15
- package/package.json +1 -1
- package/src/index.d.ts +1 -0
- package/src/index.js +1 -0
- package/src/index.js.map +1 -1
- package/src/lib/errors/serviceError.d.ts +10 -0
- package/src/lib/errors/serviceError.js +11 -0
- package/src/lib/errors/serviceError.js.map +1 -1
- package/src/lib/functional/either.d.ts +2 -2
- package/src/lib/functional/option.d.ts +2 -2
- package/src/lib/functional/pipe.js +2 -2
- package/src/lib/functional/serviceResult.d.ts +145 -12
- package/src/lib/functional/serviceResult.js +186 -16
- package/src/lib/functional/serviceResult.js.map +1 -1
- package/src/lib/strings/parseJson.d.ts +16 -0
- package/src/lib/strings/parseJson.js +22 -0
- package/src/lib/strings/parseJson.js.map +1 -0
- package/src/lib/strings/stringify.d.ts +1 -1
- package/src/lib/strings/stringify.js +5 -2
- package/src/lib/strings/stringify.js.map +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,9 @@ TypeScript utilities.
|
|
|
8
8
|
- [Usage](#usage)
|
|
9
9
|
- [ServiceError](#serviceerror)
|
|
10
10
|
- [ServiceResult](#serviceresult)
|
|
11
|
+
- [`tryCatchAsync`](#trycatchasync)
|
|
12
|
+
- [`tryCatch`](#trycatch)
|
|
13
|
+
- [`fromSafeParseReturnType`](#fromsafeparsereturntype)
|
|
11
14
|
- [Functional](#functional)
|
|
12
15
|
- [`pipe`](#pipe)
|
|
13
16
|
- [`option`](#option)
|
|
@@ -29,28 +32,28 @@ See `./src/lib` for each utility.
|
|
|
29
32
|
<embedex source="packages/util-ts/examples/serviceError.ts">
|
|
30
33
|
|
|
31
34
|
```ts
|
|
32
|
-
import { deepEqual,
|
|
35
|
+
import { deepEqual, strictEqual } from "node:assert/strict";
|
|
33
36
|
|
|
34
37
|
import { ERROR_CODES, ServiceError } from "@clipboard-health/util-ts";
|
|
35
38
|
import { z } from "zod";
|
|
36
39
|
|
|
37
40
|
{
|
|
38
41
|
const error = new ServiceError("boom");
|
|
39
|
-
|
|
42
|
+
strictEqual(error.toString(), `ServiceError[${error.id}]: [internal]: boom`);
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
try {
|
|
43
46
|
throw new Error("boom");
|
|
44
47
|
} catch (error) {
|
|
45
48
|
const serviceError = ServiceError.fromUnknown(error);
|
|
46
|
-
|
|
49
|
+
strictEqual(serviceError.toString(), `ServiceError[${serviceError.id}]: [internal]: boom`);
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
{
|
|
50
53
|
const serviceError = ServiceError.fromZodError(
|
|
51
54
|
new z.ZodError([{ code: "custom", path: ["foo"], message: "boom" }]),
|
|
52
55
|
);
|
|
53
|
-
|
|
56
|
+
strictEqual(serviceError.toString(), `ServiceError[${serviceError.id}]: [badRequest]: boom`);
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
{
|
|
@@ -58,7 +61,7 @@ try {
|
|
|
58
61
|
issues: [{ message: "boom" }],
|
|
59
62
|
cause: new Error("Original error"),
|
|
60
63
|
});
|
|
61
|
-
|
|
64
|
+
strictEqual(errorWithCause.toString(), `ServiceError[${errorWithCause.id}]: [internal]: boom`);
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
{
|
|
@@ -78,7 +81,7 @@ try {
|
|
|
78
81
|
cause: new Error("Original error"),
|
|
79
82
|
});
|
|
80
83
|
|
|
81
|
-
|
|
84
|
+
strictEqual(
|
|
82
85
|
multipleIssues.toString(),
|
|
83
86
|
`ServiceError[${multipleIssues.id}]: [badRequest]: Invalid email format; [unprocessableEntity]: Phone number too short`,
|
|
84
87
|
);
|
|
@@ -120,9 +123,10 @@ try {
|
|
|
120
123
|
import { ok } from "node:assert/strict";
|
|
121
124
|
|
|
122
125
|
import {
|
|
123
|
-
either as E,
|
|
124
126
|
ERROR_CODES,
|
|
125
127
|
failure,
|
|
128
|
+
isFailure,
|
|
129
|
+
isSuccess,
|
|
126
130
|
type ServiceResult,
|
|
127
131
|
success,
|
|
128
132
|
} from "@clipboard-health/util-ts";
|
|
@@ -142,8 +146,99 @@ function validateUser(params: { email: string; phone: string }): ServiceResult<{
|
|
|
142
146
|
return success({ id: "user-123" });
|
|
143
147
|
}
|
|
144
148
|
|
|
145
|
-
ok(
|
|
146
|
-
ok(
|
|
149
|
+
ok(isFailure(validateUser({ email: "invalidEmail", phone: "invalidPhoneNumber" })));
|
|
150
|
+
ok(isSuccess(validateUser({ email: "user@example.com", phone: "555-555-5555" })));
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
</embedex>
|
|
154
|
+
|
|
155
|
+
#### `tryCatchAsync`
|
|
156
|
+
|
|
157
|
+
<embedex source="packages/util-ts/examples/tryCatchAsync.ts">
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
import { ok, strictEqual } from "node:assert/strict";
|
|
161
|
+
|
|
162
|
+
import { isFailure, isSuccess, ServiceError, tryCatchAsync } from "@clipboard-health/util-ts";
|
|
163
|
+
|
|
164
|
+
async function example() {
|
|
165
|
+
const successResult = await tryCatchAsync(
|
|
166
|
+
async () => {
|
|
167
|
+
const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");
|
|
168
|
+
return (await response.json()) as { id: number };
|
|
169
|
+
},
|
|
170
|
+
(error) => new ServiceError(`Failed to fetch: ${String(error)}`),
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
ok(isSuccess(successResult));
|
|
174
|
+
strictEqual(successResult.value.id, 1);
|
|
175
|
+
|
|
176
|
+
const failureResult = await tryCatchAsync(
|
|
177
|
+
async () => await Promise.reject(new Error("Network error")),
|
|
178
|
+
(error) => new ServiceError(`Failed to fetch: ${String(error)}`),
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
ok(isFailure(failureResult));
|
|
182
|
+
strictEqual(failureResult.error.issues[0]?.message, "Failed to fetch: Error: Network error");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// eslint-disable-next-line unicorn/prefer-top-level-await
|
|
186
|
+
void example();
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
</embedex>
|
|
190
|
+
|
|
191
|
+
#### `tryCatch`
|
|
192
|
+
|
|
193
|
+
<embedex source="packages/util-ts/examples/tryCatch.ts">
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
import { ok, strictEqual } from "node:assert/strict";
|
|
197
|
+
|
|
198
|
+
import { isFailure, isSuccess, parseJson, ServiceError, tryCatch } from "@clipboard-health/util-ts";
|
|
199
|
+
|
|
200
|
+
const successResult = tryCatch(
|
|
201
|
+
() => parseJson<{ name: string }>('{"name": "John"}'),
|
|
202
|
+
(error) => new ServiceError(`Parse error: ${String(error)}`),
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
ok(isSuccess(successResult));
|
|
206
|
+
strictEqual(successResult.value.name, "John");
|
|
207
|
+
|
|
208
|
+
const failureResult = tryCatch(
|
|
209
|
+
() => parseJson("invalid json"),
|
|
210
|
+
(error) => new ServiceError(`Parse error: ${String(error)}`),
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
ok(isFailure(failureResult));
|
|
214
|
+
ok(failureResult.error.issues[0]?.message?.includes("Parse error"));
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
</embedex>
|
|
218
|
+
|
|
219
|
+
#### `fromSafeParseReturnType`
|
|
220
|
+
|
|
221
|
+
<embedex source="packages/util-ts/examples/fromSafeParseReturnType.ts">
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
import { ok, strictEqual } from "node:assert/strict";
|
|
225
|
+
|
|
226
|
+
import { fromSafeParseReturnType, isFailure, isSuccess } from "@clipboard-health/util-ts";
|
|
227
|
+
import { z } from "zod";
|
|
228
|
+
|
|
229
|
+
const schema = z.object({ name: z.string(), age: z.number() });
|
|
230
|
+
|
|
231
|
+
const validData = { name: "John", age: 30 };
|
|
232
|
+
const successResult = fromSafeParseReturnType(schema.safeParse(validData));
|
|
233
|
+
|
|
234
|
+
ok(isSuccess(successResult));
|
|
235
|
+
strictEqual(successResult.value.name, "John");
|
|
236
|
+
|
|
237
|
+
const invalidData = { name: "John", age: "thirty" };
|
|
238
|
+
const failureResult = fromSafeParseReturnType(schema.safeParse(invalidData));
|
|
239
|
+
|
|
240
|
+
ok(isFailure(failureResult));
|
|
241
|
+
ok(failureResult.error.issues.length > 0);
|
|
147
242
|
```
|
|
148
243
|
|
|
149
244
|
</embedex>
|
|
@@ -155,7 +250,7 @@ ok(E.isRight(validateUser({ email: "user@example.com", phone: "555-555-5555" }))
|
|
|
155
250
|
<embedex source="packages/util-ts/examples/pipe.ts">
|
|
156
251
|
|
|
157
252
|
```ts
|
|
158
|
-
import {
|
|
253
|
+
import { strictEqual } from "node:assert/strict";
|
|
159
254
|
|
|
160
255
|
import { pipe } from "@clipboard-health/util-ts";
|
|
161
256
|
|
|
@@ -167,7 +262,7 @@ const result = pipe(
|
|
|
167
262
|
(array) => array.join(" "),
|
|
168
263
|
);
|
|
169
264
|
|
|
170
|
-
|
|
265
|
+
strictEqual(result, "Hello World");
|
|
171
266
|
```
|
|
172
267
|
|
|
173
268
|
</embedex>
|
|
@@ -177,7 +272,7 @@ equal(result, "Hello World");
|
|
|
177
272
|
<embedex source="packages/util-ts/examples/option.ts">
|
|
178
273
|
|
|
179
274
|
```ts
|
|
180
|
-
import {
|
|
275
|
+
import { strictEqual } from "node:assert/strict";
|
|
181
276
|
|
|
182
277
|
import { option as O, pipe } from "@clipboard-health/util-ts";
|
|
183
278
|
|
|
@@ -199,7 +294,7 @@ const result = pipe(
|
|
|
199
294
|
),
|
|
200
295
|
);
|
|
201
296
|
|
|
202
|
-
|
|
297
|
+
strictEqual(result, "Result is 0.1");
|
|
203
298
|
```
|
|
204
299
|
|
|
205
300
|
</embedex>
|
|
@@ -209,7 +304,7 @@ equal(result, "Result is 0.1");
|
|
|
209
304
|
<embedex source="packages/util-ts/examples/either.ts">
|
|
210
305
|
|
|
211
306
|
```ts
|
|
212
|
-
import {
|
|
307
|
+
import { strictEqual } from "node:assert/strict";
|
|
213
308
|
|
|
214
309
|
import { either as E, pipe } from "@clipboard-health/util-ts";
|
|
215
310
|
|
|
@@ -231,7 +326,7 @@ const result = pipe(
|
|
|
231
326
|
),
|
|
232
327
|
);
|
|
233
328
|
|
|
234
|
-
|
|
329
|
+
strictEqual(result, "Result is 0.1");
|
|
235
330
|
```
|
|
236
331
|
|
|
237
332
|
</embedex>
|
package/package.json
CHANGED
package/src/index.d.ts
CHANGED
package/src/index.js
CHANGED
|
@@ -9,5 +9,6 @@ tslib_1.__exportStar(require("./lib/functional"), exports);
|
|
|
9
9
|
tslib_1.__exportStar(require("./lib/logger"), exports);
|
|
10
10
|
tslib_1.__exportStar(require("./lib/nullish"), exports);
|
|
11
11
|
tslib_1.__exportStar(require("./lib/strings"), exports);
|
|
12
|
+
tslib_1.__exportStar(require("./lib/strings/parseJson"), exports);
|
|
12
13
|
tslib_1.__exportStar(require("./lib/types"), exports);
|
|
13
14
|
//# sourceMappingURL=index.js.map
|
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/util-ts/src/index.ts"],"names":[],"mappings":";;;AAAA,uDAA6B;AAC7B,2DAAiC;AACjC,uDAA6B;AAC7B,0DAAgC;AAChC,2DAAiC;AACjC,uDAA6B;AAC7B,wDAA8B;AAC9B,wDAA8B;AAC9B,sDAA4B"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/util-ts/src/index.ts"],"names":[],"mappings":";;;AAAA,uDAA6B;AAC7B,2DAAiC;AACjC,uDAA6B;AAC7B,0DAAgC;AAChC,2DAAiC;AACjC,uDAA6B;AAC7B,wDAA8B;AAC9B,wDAA8B;AAC9B,kEAAwC;AACxC,sDAA4B"}
|
|
@@ -107,6 +107,16 @@ export declare class ServiceError extends Error {
|
|
|
107
107
|
static fromZodError(error: ZodError, options?: {
|
|
108
108
|
source?: ErrorSource;
|
|
109
109
|
}): ServiceError;
|
|
110
|
+
/**
|
|
111
|
+
* Merges multiple ServiceErrors into a single ServiceError.
|
|
112
|
+
* Combines all issues from the input errors and uses the highest status code.
|
|
113
|
+
*
|
|
114
|
+
* @param error - The primary error
|
|
115
|
+
* @param errors - Additional ServiceErrors
|
|
116
|
+
* @returns New ServiceError containing all issues from input errors
|
|
117
|
+
*/
|
|
118
|
+
static merge(error: unknown, ...errors: readonly unknown[]): ServiceError;
|
|
119
|
+
static merge(error: ServiceError, ...errors: readonly ServiceError[]): ServiceError;
|
|
110
120
|
/**
|
|
111
121
|
* Creates a ServiceError from a JSON:API error object
|
|
112
122
|
* @see {@link https://jsonapi.org/format/#error-objects}
|
|
@@ -86,6 +86,17 @@ class ServiceError extends Error {
|
|
|
86
86
|
source: options?.source,
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
|
+
static merge(error, ...errors) {
|
|
90
|
+
const firstError = error instanceof ServiceError ? error : ServiceError.fromUnknown(error);
|
|
91
|
+
if (errors.length === 0) {
|
|
92
|
+
return firstError;
|
|
93
|
+
}
|
|
94
|
+
const additionalErrors = errors.map((error) => error instanceof ServiceError ? error : ServiceError.fromUnknown(error));
|
|
95
|
+
return new ServiceError({
|
|
96
|
+
cause: firstError.cause ?? firstError,
|
|
97
|
+
issues: [...firstError.issues, ...additionalErrors.flatMap((error) => error.issues)],
|
|
98
|
+
});
|
|
99
|
+
}
|
|
89
100
|
/**
|
|
90
101
|
* Creates a ServiceError from a JSON:API error object
|
|
91
102
|
* @see {@link https://jsonapi.org/format/#error-objects}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serviceError.js","sourceRoot":"","sources":["../../../../../../packages/util-ts/src/lib/errors/serviceError.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"serviceError.js","sourceRoot":"","sources":["../../../../../../packages/util-ts/src/lib/errors/serviceError.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,7 +17,7 @@ export type Right<A> = Readonly<{
|
|
|
17
17
|
* <embedex source="packages/util-ts/examples/either.ts">
|
|
18
18
|
*
|
|
19
19
|
* ```ts
|
|
20
|
-
* import {
|
|
20
|
+
* import { strictEqual } from "node:assert/strict";
|
|
21
21
|
*
|
|
22
22
|
* import { either as E, pipe } from "@clipboard-health/util-ts";
|
|
23
23
|
*
|
|
@@ -39,7 +39,7 @@ export type Right<A> = Readonly<{
|
|
|
39
39
|
* ),
|
|
40
40
|
* );
|
|
41
41
|
*
|
|
42
|
-
*
|
|
42
|
+
* strictEqual(result, "Result is 0.1");
|
|
43
43
|
* ```
|
|
44
44
|
*
|
|
45
45
|
* </embedex>
|
|
@@ -12,7 +12,7 @@ export type Some<A> = Readonly<{
|
|
|
12
12
|
* <embedex source="packages/util-ts/examples/option.ts">
|
|
13
13
|
*
|
|
14
14
|
* ```ts
|
|
15
|
-
* import {
|
|
15
|
+
* import { strictEqual } from "node:assert/strict";
|
|
16
16
|
*
|
|
17
17
|
* import { option as O, pipe } from "@clipboard-health/util-ts";
|
|
18
18
|
*
|
|
@@ -34,7 +34,7 @@ export type Some<A> = Readonly<{
|
|
|
34
34
|
* ),
|
|
35
35
|
* );
|
|
36
36
|
*
|
|
37
|
-
*
|
|
37
|
+
* strictEqual(result, "Result is 0.1");
|
|
38
38
|
* ```
|
|
39
39
|
*
|
|
40
40
|
* </embedex>
|
|
@@ -12,7 +12,7 @@ exports.pipe = pipe;
|
|
|
12
12
|
* <embedex source="packages/util-ts/examples/pipe.ts">
|
|
13
13
|
*
|
|
14
14
|
* ```ts
|
|
15
|
-
* import {
|
|
15
|
+
* import { strictEqual } from "node:assert/strict";
|
|
16
16
|
*
|
|
17
17
|
* import { pipe } from "@clipboard-health/util-ts";
|
|
18
18
|
*
|
|
@@ -24,7 +24,7 @@ exports.pipe = pipe;
|
|
|
24
24
|
* (array) => array.join(" "),
|
|
25
25
|
* );
|
|
26
26
|
*
|
|
27
|
-
*
|
|
27
|
+
* strictEqual(result, "Hello World");
|
|
28
28
|
* ```
|
|
29
29
|
*
|
|
30
30
|
* </embedex>
|
|
@@ -1,25 +1,158 @@
|
|
|
1
|
-
import { either as E } from "@clipboard-health/util-ts";
|
|
2
1
|
import { type SafeParseReturnType } from "zod";
|
|
3
2
|
import { ServiceError, type ServiceErrorParams } from "../errors/serviceError";
|
|
3
|
+
import { type Either, type Left, type Right } from "./either";
|
|
4
|
+
export interface Success<A> {
|
|
5
|
+
readonly isSuccess: true;
|
|
6
|
+
readonly value: A;
|
|
7
|
+
}
|
|
8
|
+
export interface Failure<E> {
|
|
9
|
+
readonly isSuccess: false;
|
|
10
|
+
readonly error: E;
|
|
11
|
+
}
|
|
4
12
|
/**
|
|
5
13
|
* Represents the result of a service operation that may fail.
|
|
6
14
|
* @template A The type of the successful result value
|
|
7
15
|
*/
|
|
8
|
-
export type ServiceResult<A> =
|
|
16
|
+
export type ServiceResult<A> = Either<ServiceError, A> & (Success<A> | Failure<ServiceError>);
|
|
9
17
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* @template A The type of the success value
|
|
13
|
-
* @param value The value to wrap in a successful result
|
|
14
|
-
* @returns A `ServiceResult` containing the success value
|
|
18
|
+
* Alias for {@link right} that duplicates the `right` property to `value` and `isRight` to
|
|
19
|
+
* `isSuccess`.
|
|
15
20
|
*/
|
|
16
21
|
export declare function success<A>(value: A): ServiceResult<A>;
|
|
17
22
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* @template A The type of the success value
|
|
21
|
-
* @param params The parameters to create the `ServiceError`
|
|
22
|
-
* @returns A `ServiceResult` containing the error
|
|
23
|
+
* Alias for {@link left} that duplicates the `left` property to `error` and `isLeft` to
|
|
24
|
+
* `isSuccess`.
|
|
23
25
|
*/
|
|
24
26
|
export declare function failure<A = never>(params: ServiceErrorParams | ServiceError): ServiceResult<A>;
|
|
27
|
+
/**
|
|
28
|
+
* Alias for {@link isLeft}.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isFailure<A>(result: ServiceResult<A>): result is Left<ServiceError> & Failure<ServiceError>;
|
|
31
|
+
/**
|
|
32
|
+
* Alias for {@link isRight}.
|
|
33
|
+
*/
|
|
34
|
+
export declare function isSuccess<A>(result: ServiceResult<A>): result is Right<A> & Success<A>;
|
|
35
|
+
/**
|
|
36
|
+
* Alias for {@link mapLeft}
|
|
37
|
+
*/
|
|
38
|
+
export declare function mapFailure<G>(f: (left: ServiceError) => G): <A>(result: ServiceResult<A>) => Either<G, A>;
|
|
39
|
+
/**
|
|
40
|
+
* Converts a promise returning function into a ServiceResult by handling potential rejections.
|
|
41
|
+
* If the promise returning function resolves successfully, returns a success ServiceResult.
|
|
42
|
+
* If the promise rejects, calls the onError function to convert the error into a ServiceError.
|
|
43
|
+
*
|
|
44
|
+
* @template A The type of the value the promise resolves to
|
|
45
|
+
* @param f Function returning a Promise to execute. Passing a function allows catching synchronous throws
|
|
46
|
+
* @param onError Maps unknown errors to a ServiceError
|
|
47
|
+
* @returns A promise that resolves to a ServiceResult<A>
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* <embedex source="packages/util-ts/examples/tryCatchAsync.ts">
|
|
51
|
+
*
|
|
52
|
+
* ```ts
|
|
53
|
+
* import { ok, strictEqual } from "node:assert/strict";
|
|
54
|
+
*
|
|
55
|
+
* import { isFailure, isSuccess, ServiceError, tryCatchAsync } from "@clipboard-health/util-ts";
|
|
56
|
+
*
|
|
57
|
+
* async function example() {
|
|
58
|
+
* const successResult = await tryCatchAsync(
|
|
59
|
+
* async () => {
|
|
60
|
+
* const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");
|
|
61
|
+
* return (await response.json()) as { id: number };
|
|
62
|
+
* },
|
|
63
|
+
* (error) => new ServiceError(`Failed to fetch: ${String(error)}`),
|
|
64
|
+
* );
|
|
65
|
+
*
|
|
66
|
+
* ok(isSuccess(successResult));
|
|
67
|
+
* strictEqual(successResult.value.id, 1);
|
|
68
|
+
*
|
|
69
|
+
* const failureResult = await tryCatchAsync(
|
|
70
|
+
* async () => await Promise.reject(new Error("Network error")),
|
|
71
|
+
* (error) => new ServiceError(`Failed to fetch: ${String(error)}`),
|
|
72
|
+
* );
|
|
73
|
+
*
|
|
74
|
+
* ok(isFailure(failureResult));
|
|
75
|
+
* strictEqual(failureResult.error.issues[0]?.message, "Failed to fetch: Error: Network error");
|
|
76
|
+
* }
|
|
77
|
+
*
|
|
78
|
+
* // eslint-disable-next-line unicorn/prefer-top-level-await
|
|
79
|
+
* void example();
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* </embedex>
|
|
83
|
+
*/
|
|
84
|
+
export declare function tryCatchAsync<A>(f: () => Promise<A>, onError: (error: unknown) => ServiceError): Promise<ServiceResult<A>>;
|
|
85
|
+
/**
|
|
86
|
+
* Wraps a synchronous function that might throw into a ServiceResult.
|
|
87
|
+
* If the function executes successfully, returns a success ServiceResult.
|
|
88
|
+
* If the function throws, calls the onError function to convert the error into a ServiceError.
|
|
89
|
+
*
|
|
90
|
+
* @template A The return type of the function
|
|
91
|
+
* @param f The function to execute safely
|
|
92
|
+
* @param onError Function to convert unknown errors into ServiceError
|
|
93
|
+
* @returns A ServiceResult<A>
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* <embedex source="packages/util-ts/examples/tryCatch.ts">
|
|
97
|
+
*
|
|
98
|
+
* ```ts
|
|
99
|
+
* import { ok, strictEqual } from "node:assert/strict";
|
|
100
|
+
*
|
|
101
|
+
* import { isFailure, isSuccess, parseJson, ServiceError, tryCatch } from "@clipboard-health/util-ts";
|
|
102
|
+
*
|
|
103
|
+
* const successResult = tryCatch(
|
|
104
|
+
* () => parseJson<{ name: string }>('{"name": "John"}'),
|
|
105
|
+
* (error) => new ServiceError(`Parse error: ${String(error)}`),
|
|
106
|
+
* );
|
|
107
|
+
*
|
|
108
|
+
* ok(isSuccess(successResult));
|
|
109
|
+
* strictEqual(successResult.value.name, "John");
|
|
110
|
+
*
|
|
111
|
+
* const failureResult = tryCatch(
|
|
112
|
+
* () => parseJson("invalid json"),
|
|
113
|
+
* (error) => new ServiceError(`Parse error: ${String(error)}`),
|
|
114
|
+
* );
|
|
115
|
+
*
|
|
116
|
+
* ok(isFailure(failureResult));
|
|
117
|
+
* ok(failureResult.error.issues[0]?.message?.includes("Parse error"));
|
|
118
|
+
* ```
|
|
119
|
+
*
|
|
120
|
+
* </embedex>
|
|
121
|
+
*/
|
|
122
|
+
export declare function tryCatch<A>(f: () => A, onError: (error: unknown) => ServiceError): ServiceResult<A>;
|
|
123
|
+
/**
|
|
124
|
+
* Converts a Zod SafeParseReturnType into a ServiceResult.
|
|
125
|
+
* If the parse was successful, returns a success ServiceResult with the parsed data.
|
|
126
|
+
* If the parse failed, returns a failure ServiceResult with the validation errors.
|
|
127
|
+
*
|
|
128
|
+
* @template A The type of the successfully parsed value
|
|
129
|
+
* @param value The SafeParseReturnType from a Zod schema parse
|
|
130
|
+
* @returns A ServiceResult<A>
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* <embedex source="packages/util-ts/examples/fromSafeParseReturnType.ts">
|
|
134
|
+
*
|
|
135
|
+
* ```ts
|
|
136
|
+
* import { ok, strictEqual } from "node:assert/strict";
|
|
137
|
+
*
|
|
138
|
+
* import { fromSafeParseReturnType, isFailure, isSuccess } from "@clipboard-health/util-ts";
|
|
139
|
+
* import { z } from "zod";
|
|
140
|
+
*
|
|
141
|
+
* const schema = z.object({ name: z.string(), age: z.number() });
|
|
142
|
+
*
|
|
143
|
+
* const validData = { name: "John", age: 30 };
|
|
144
|
+
* const successResult = fromSafeParseReturnType(schema.safeParse(validData));
|
|
145
|
+
*
|
|
146
|
+
* ok(isSuccess(successResult));
|
|
147
|
+
* strictEqual(successResult.value.name, "John");
|
|
148
|
+
*
|
|
149
|
+
* const invalidData = { name: "John", age: "thirty" };
|
|
150
|
+
* const failureResult = fromSafeParseReturnType(schema.safeParse(invalidData));
|
|
151
|
+
*
|
|
152
|
+
* ok(isFailure(failureResult));
|
|
153
|
+
* ok(failureResult.error.issues.length > 0);
|
|
154
|
+
* ```
|
|
155
|
+
*
|
|
156
|
+
* </embedex>
|
|
157
|
+
*/
|
|
25
158
|
export declare function fromSafeParseReturnType<A>(value: SafeParseReturnType<unknown, A>): ServiceResult<A>;
|
|
@@ -2,32 +2,202 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.success = success;
|
|
4
4
|
exports.failure = failure;
|
|
5
|
+
exports.isFailure = isFailure;
|
|
6
|
+
exports.isSuccess = isSuccess;
|
|
7
|
+
exports.mapFailure = mapFailure;
|
|
8
|
+
exports.tryCatchAsync = tryCatchAsync;
|
|
9
|
+
exports.tryCatch = tryCatch;
|
|
5
10
|
exports.fromSafeParseReturnType = fromSafeParseReturnType;
|
|
6
|
-
const util_ts_1 = require("@clipboard-health/util-ts");
|
|
7
11
|
const serviceError_1 = require("../errors/serviceError");
|
|
12
|
+
const either_1 = require("./either");
|
|
8
13
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* @template A The type of the success value
|
|
12
|
-
* @param value The value to wrap in a successful result
|
|
13
|
-
* @returns A `ServiceResult` containing the success value
|
|
14
|
+
* Alias for {@link right} that duplicates the `right` property to `value` and `isRight` to
|
|
15
|
+
* `isSuccess`.
|
|
14
16
|
*/
|
|
15
17
|
function success(value) {
|
|
16
|
-
|
|
18
|
+
const base = (0, either_1.right)(value);
|
|
19
|
+
Object.defineProperties(base, {
|
|
20
|
+
isSuccess: { get: () => true, enumerable: true },
|
|
21
|
+
value: { get: () => base.right, enumerable: true },
|
|
22
|
+
});
|
|
23
|
+
return Object.freeze(base);
|
|
17
24
|
}
|
|
18
25
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* @template A The type of the success value
|
|
22
|
-
* @param params The parameters to create the `ServiceError`
|
|
23
|
-
* @returns A `ServiceResult` containing the error
|
|
26
|
+
* Alias for {@link left} that duplicates the `left` property to `error` and `isLeft` to
|
|
27
|
+
* `isSuccess`.
|
|
24
28
|
*/
|
|
25
29
|
function failure(params) {
|
|
26
|
-
|
|
30
|
+
const error = params instanceof serviceError_1.ServiceError ? params : new serviceError_1.ServiceError(params);
|
|
31
|
+
const base = (0, either_1.left)(error);
|
|
32
|
+
Object.defineProperties(base, {
|
|
33
|
+
isSuccess: { get: () => false, enumerable: true },
|
|
34
|
+
error: { get: () => base.left, enumerable: true },
|
|
35
|
+
});
|
|
36
|
+
return Object.freeze(base);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Alias for {@link isLeft}.
|
|
40
|
+
*/
|
|
41
|
+
function isFailure(result) {
|
|
42
|
+
return !result.isSuccess;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Alias for {@link isRight}.
|
|
46
|
+
*/
|
|
47
|
+
function isSuccess(result) {
|
|
48
|
+
return result.isSuccess;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Alias for {@link mapLeft}
|
|
52
|
+
*/
|
|
53
|
+
function mapFailure(f) {
|
|
54
|
+
return (0, either_1.mapLeft)(f);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Converts a promise returning function into a ServiceResult by handling potential rejections.
|
|
58
|
+
* If the promise returning function resolves successfully, returns a success ServiceResult.
|
|
59
|
+
* If the promise rejects, calls the onError function to convert the error into a ServiceError.
|
|
60
|
+
*
|
|
61
|
+
* @template A The type of the value the promise resolves to
|
|
62
|
+
* @param f Function returning a Promise to execute. Passing a function allows catching synchronous throws
|
|
63
|
+
* @param onError Maps unknown errors to a ServiceError
|
|
64
|
+
* @returns A promise that resolves to a ServiceResult<A>
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* <embedex source="packages/util-ts/examples/tryCatchAsync.ts">
|
|
68
|
+
*
|
|
69
|
+
* ```ts
|
|
70
|
+
* import { ok, strictEqual } from "node:assert/strict";
|
|
71
|
+
*
|
|
72
|
+
* import { isFailure, isSuccess, ServiceError, tryCatchAsync } from "@clipboard-health/util-ts";
|
|
73
|
+
*
|
|
74
|
+
* async function example() {
|
|
75
|
+
* const successResult = await tryCatchAsync(
|
|
76
|
+
* async () => {
|
|
77
|
+
* const response = await fetch("https://jsonplaceholder.typicode.com/posts/1");
|
|
78
|
+
* return (await response.json()) as { id: number };
|
|
79
|
+
* },
|
|
80
|
+
* (error) => new ServiceError(`Failed to fetch: ${String(error)}`),
|
|
81
|
+
* );
|
|
82
|
+
*
|
|
83
|
+
* ok(isSuccess(successResult));
|
|
84
|
+
* strictEqual(successResult.value.id, 1);
|
|
85
|
+
*
|
|
86
|
+
* const failureResult = await tryCatchAsync(
|
|
87
|
+
* async () => await Promise.reject(new Error("Network error")),
|
|
88
|
+
* (error) => new ServiceError(`Failed to fetch: ${String(error)}`),
|
|
89
|
+
* );
|
|
90
|
+
*
|
|
91
|
+
* ok(isFailure(failureResult));
|
|
92
|
+
* strictEqual(failureResult.error.issues[0]?.message, "Failed to fetch: Error: Network error");
|
|
93
|
+
* }
|
|
94
|
+
*
|
|
95
|
+
* // eslint-disable-next-line unicorn/prefer-top-level-await
|
|
96
|
+
* void example();
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* </embedex>
|
|
100
|
+
*/
|
|
101
|
+
async function tryCatchAsync(f, onError) {
|
|
102
|
+
try {
|
|
103
|
+
return success(await f());
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
return handleError(error, onError);
|
|
107
|
+
}
|
|
27
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Wraps a synchronous function that might throw into a ServiceResult.
|
|
111
|
+
* If the function executes successfully, returns a success ServiceResult.
|
|
112
|
+
* If the function throws, calls the onError function to convert the error into a ServiceError.
|
|
113
|
+
*
|
|
114
|
+
* @template A The return type of the function
|
|
115
|
+
* @param f The function to execute safely
|
|
116
|
+
* @param onError Function to convert unknown errors into ServiceError
|
|
117
|
+
* @returns A ServiceResult<A>
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* <embedex source="packages/util-ts/examples/tryCatch.ts">
|
|
121
|
+
*
|
|
122
|
+
* ```ts
|
|
123
|
+
* import { ok, strictEqual } from "node:assert/strict";
|
|
124
|
+
*
|
|
125
|
+
* import { isFailure, isSuccess, parseJson, ServiceError, tryCatch } from "@clipboard-health/util-ts";
|
|
126
|
+
*
|
|
127
|
+
* const successResult = tryCatch(
|
|
128
|
+
* () => parseJson<{ name: string }>('{"name": "John"}'),
|
|
129
|
+
* (error) => new ServiceError(`Parse error: ${String(error)}`),
|
|
130
|
+
* );
|
|
131
|
+
*
|
|
132
|
+
* ok(isSuccess(successResult));
|
|
133
|
+
* strictEqual(successResult.value.name, "John");
|
|
134
|
+
*
|
|
135
|
+
* const failureResult = tryCatch(
|
|
136
|
+
* () => parseJson("invalid json"),
|
|
137
|
+
* (error) => new ServiceError(`Parse error: ${String(error)}`),
|
|
138
|
+
* );
|
|
139
|
+
*
|
|
140
|
+
* ok(isFailure(failureResult));
|
|
141
|
+
* ok(failureResult.error.issues[0]?.message?.includes("Parse error"));
|
|
142
|
+
* ```
|
|
143
|
+
*
|
|
144
|
+
* </embedex>
|
|
145
|
+
*/
|
|
146
|
+
function tryCatch(f, onError) {
|
|
147
|
+
try {
|
|
148
|
+
return success(f());
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
return handleError(error, onError);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Converts a Zod SafeParseReturnType into a ServiceResult.
|
|
156
|
+
* If the parse was successful, returns a success ServiceResult with the parsed data.
|
|
157
|
+
* If the parse failed, returns a failure ServiceResult with the validation errors.
|
|
158
|
+
*
|
|
159
|
+
* @template A The type of the successfully parsed value
|
|
160
|
+
* @param value The SafeParseReturnType from a Zod schema parse
|
|
161
|
+
* @returns A ServiceResult<A>
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* <embedex source="packages/util-ts/examples/fromSafeParseReturnType.ts">
|
|
165
|
+
*
|
|
166
|
+
* ```ts
|
|
167
|
+
* import { ok, strictEqual } from "node:assert/strict";
|
|
168
|
+
*
|
|
169
|
+
* import { fromSafeParseReturnType, isFailure, isSuccess } from "@clipboard-health/util-ts";
|
|
170
|
+
* import { z } from "zod";
|
|
171
|
+
*
|
|
172
|
+
* const schema = z.object({ name: z.string(), age: z.number() });
|
|
173
|
+
*
|
|
174
|
+
* const validData = { name: "John", age: 30 };
|
|
175
|
+
* const successResult = fromSafeParseReturnType(schema.safeParse(validData));
|
|
176
|
+
*
|
|
177
|
+
* ok(isSuccess(successResult));
|
|
178
|
+
* strictEqual(successResult.value.name, "John");
|
|
179
|
+
*
|
|
180
|
+
* const invalidData = { name: "John", age: "thirty" };
|
|
181
|
+
* const failureResult = fromSafeParseReturnType(schema.safeParse(invalidData));
|
|
182
|
+
*
|
|
183
|
+
* ok(isFailure(failureResult));
|
|
184
|
+
* ok(failureResult.error.issues.length > 0);
|
|
185
|
+
* ```
|
|
186
|
+
*
|
|
187
|
+
* </embedex>
|
|
188
|
+
*/
|
|
28
189
|
function fromSafeParseReturnType(value) {
|
|
29
|
-
return value.success
|
|
30
|
-
|
|
31
|
-
|
|
190
|
+
return value.success ? success(value.data) : failure(serviceError_1.ServiceError.fromZodError(value.error));
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Handles possible throws in onError functions.
|
|
194
|
+
*/
|
|
195
|
+
function handleError(error, onError) {
|
|
196
|
+
try {
|
|
197
|
+
return failure(onError(error));
|
|
198
|
+
}
|
|
199
|
+
catch (mappingError) {
|
|
200
|
+
return failure(serviceError_1.ServiceError.merge(error, mappingError));
|
|
201
|
+
}
|
|
32
202
|
}
|
|
33
203
|
//# sourceMappingURL=serviceResult.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serviceResult.js","sourceRoot":"","sources":["../../../../../../packages/util-ts/src/lib/functional/serviceResult.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"serviceResult.js","sourceRoot":"","sources":["../../../../../../packages/util-ts/src/lib/functional/serviceResult.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a JSON string and returns the parsed value with optional (unsafe) type assertion.
|
|
3
|
+
*
|
|
4
|
+
* @template T - The expected type of the parsed JSON value
|
|
5
|
+
* @param value - The JSON string to parse
|
|
6
|
+
* @param reviver - A function that transforms the result before returning it
|
|
7
|
+
* @returns The parsed JSON value cast to type T
|
|
8
|
+
* @throws {SyntaxError} When the JSON string is malformed
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const data = parseJson<{ name: string }>('{"name": "John"}');
|
|
13
|
+
* console.log(data.name); // "John"
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseJson<T = unknown>(value: string, reviver?: (this: unknown, key: string, value: unknown) => unknown): T;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseJson = parseJson;
|
|
4
|
+
/**
|
|
5
|
+
* Parses a JSON string and returns the parsed value with optional (unsafe) type assertion.
|
|
6
|
+
*
|
|
7
|
+
* @template T - The expected type of the parsed JSON value
|
|
8
|
+
* @param value - The JSON string to parse
|
|
9
|
+
* @param reviver - A function that transforms the result before returning it
|
|
10
|
+
* @returns The parsed JSON value cast to type T
|
|
11
|
+
* @throws {SyntaxError} When the JSON string is malformed
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const data = parseJson<{ name: string }>('{"name": "John"}');
|
|
16
|
+
* console.log(data.name); // "John"
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
function parseJson(value, reviver) {
|
|
20
|
+
return JSON.parse(value, reviver);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=parseJson.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseJson.js","sourceRoot":"","sources":["../../../../../../packages/util-ts/src/lib/strings/parseJson.ts"],"names":[],"mappings":";;AAeA,8BAKC;AApBD;;;;;;;;;;;;;;GAcG;AACH,SAAgB,SAAS,CACvB,KAAa,EACb,OAAiE;IAEjE,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAM,CAAC;AACzC,CAAC"}
|
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
* @returns A JSON string representation of the value
|
|
6
6
|
* @throws {TypeError} When the value contains circular references
|
|
7
7
|
*/
|
|
8
|
-
export declare function stringify(value: unknown): string;
|
|
8
|
+
export declare function stringify(value: unknown, replacer?: (this: unknown, key: string, value: unknown) => unknown, space?: string | number): string;
|
|
@@ -8,7 +8,10 @@ exports.stringify = stringify;
|
|
|
8
8
|
* @returns A JSON string representation of the value
|
|
9
9
|
* @throws {TypeError} When the value contains circular references
|
|
10
10
|
*/
|
|
11
|
-
function stringify(value) {
|
|
12
|
-
return JSON.stringify(value,
|
|
11
|
+
function stringify(value, replacer, space) {
|
|
12
|
+
return JSON.stringify(value, replacer ?? defaultReplacer, space);
|
|
13
|
+
}
|
|
14
|
+
function defaultReplacer(_key, value) {
|
|
15
|
+
return typeof value === "bigint" ? String(value) : value;
|
|
13
16
|
}
|
|
14
17
|
//# sourceMappingURL=stringify.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stringify.js","sourceRoot":"","sources":["../../../../../../packages/util-ts/src/lib/strings/stringify.ts"],"names":[],"mappings":";;AAOA,
|
|
1
|
+
{"version":3,"file":"stringify.js","sourceRoot":"","sources":["../../../../../../packages/util-ts/src/lib/strings/stringify.ts"],"names":[],"mappings":";;AAOA,8BAMC;AAbD;;;;;;GAMG;AACH,SAAgB,SAAS,CACvB,KAAc,EACd,QAAkE,EAClE,KAAuB;IAEvB,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,QAAQ,IAAI,eAAe,EAAE,KAAK,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,eAAe,CAAgB,IAAY,EAAE,KAAc;IAClE,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAC3D,CAAC"}
|