go-go-try 7.0.0 → 7.2.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/AGENTS.md ADDED
@@ -0,0 +1,182 @@
1
+ # go-go-try - Agent Guide
2
+
3
+ ## Project Overview
4
+
5
+ **go-go-try** is a TypeScript utility library for error handling inspired by Go's error handling pattern. It provides a functional approach to try/catch operations by returning a tuple `[error, value]` instead of throwing exceptions.
6
+
7
+ - **Name**: go-go-try
8
+ - **Version**: 6.2.0
9
+ - **License**: MIT
10
+ - **Repository**: thelinuxlich/go-go-try
11
+ - **Node.js Requirements**: >= 16
12
+
13
+ ## Technology Stack
14
+
15
+ - **Language**: TypeScript 5.8.3
16
+ - **Build Tool**: [pkgroll](https://github.com/privatenumber/pkgroll) - A zero-config TypeScript package bundler
17
+ - **Linter**: [Biome](https://biomejs.dev/) - Fast linter and formatter
18
+ - **Test Framework**: [Vitest](https://vitest.dev/) - Vite-native unit test framework
19
+ - **Type Testing**: [@ark/attest](https://github.com/arktypeio/arktype) - Runtime type assertions for TypeScript
20
+
21
+ ## Project Structure
22
+
23
+ ```
24
+ .
25
+ ├── src/
26
+ │ ├── index.ts # Main source file - exports goTry, goTryRaw, and utility types
27
+ │ └── index.test.ts # Comprehensive test suite with runtime and type tests
28
+ ├── dist/ # Build output (generated by pkgroll)
29
+ │ ├── index.cjs # CommonJS build
30
+ │ ├── index.mjs # ES Module build
31
+ │ ├── index.d.cts # CommonJS type definitions
32
+ │ └── index.d.mts # ES Module type definitions
33
+ ├── .github/workflows/
34
+ │ └── main.yml # CI configuration for GitHub Actions
35
+ ├── .attest/ # Ark attest cache directory
36
+ ├── package.json # Package configuration with dual CJS/ESM exports
37
+ ├── tsconfig.json # TypeScript strict configuration
38
+ ├── vitest.config.ts # Vitest configuration with type checking enabled
39
+ ├── setupVitest.ts # Vitest global setup for @ark/attest
40
+ └── README.md # User-facing documentation
41
+ ```
42
+
43
+ ## Build and Test Commands
44
+
45
+ ```bash
46
+ # Build the project (generates dist/ with CJS, ESM, and type definitions)
47
+ npm run build
48
+
49
+ # Run linting with auto-fix
50
+ npm run lint
51
+
52
+ # Run the full test suite (build + lint + vitest)
53
+ npm test
54
+ ```
55
+
56
+ The test script is a composite that:
57
+ 1. Builds the project
58
+ 2. Runs the linter
59
+ 3. Executes Vitest tests
60
+
61
+ ## Code Style Guidelines
62
+
63
+ - **Linter**: Biome is used for linting and formatting
64
+ - **Configuration**: Uses Biome's default configuration (no biome.json present)
65
+ - **Strict TypeScript**: The `tsconfig.json` enforces strict mode with additional checks:
66
+ - `noUnusedLocals`: true
67
+ - `noUnusedParameters`: true
68
+ - `allowUnreachableCode`: false
69
+ - `noUncheckedIndexedAccess`: true
70
+ - `noFallthroughCasesInSwitch`: true
71
+ - `forceConsistentCasingInFileNames`: true
72
+
73
+ ## Testing Instructions
74
+
75
+ ### Test Structure
76
+
77
+ Tests are co-located with source code in `src/index.test.ts` using Vitest.
78
+
79
+ ### Test Types
80
+
81
+ 1. **Runtime Tests**: Standard unit tests verifying behavior
82
+ 2. **Type Tests**: Using `@ark/attest` to verify TypeScript type inference at runtime
83
+
84
+ ### Key Testing Patterns
85
+
86
+ ```typescript
87
+ // Runtime test
88
+ import { assert, test } from 'vitest'
89
+ test('description', () => {
90
+ const result = goTry(() => 'value')
91
+ assert.equal(result[1], 'value')
92
+ })
93
+
94
+ // Type test
95
+ import { attest } from '@ark/attest'
96
+ test('types are correct', () => {
97
+ const result = goTry('value')
98
+ attest<Result<string, string>>(result)
99
+ })
100
+ ```
101
+
102
+ ### Running Tests
103
+
104
+ ```bash
105
+ # Run all tests with type checking
106
+ npx vitest run
107
+
108
+ # Run tests in watch mode (during development)
109
+ npx vitest
110
+ ```
111
+
112
+ ### Global Setup
113
+
114
+ The `setupVitest.ts` file configures `@ark/attest` for type assertions in tests.
115
+
116
+ ## API Design
117
+
118
+ ### Core Functions
119
+
120
+ - **`goTry<T>(value)`**: Returns `[string | undefined, T | undefined]` - error is the message string
121
+ - **`goTryRaw<T, E>(value)`**: Returns `[E | undefined, T | undefined]` - error is the raw Error object
122
+
123
+ ### Type Helpers
124
+
125
+ - **`Result<E, T>`**: The tuple type `[E | undefined, T | undefined]`
126
+ - **`Success<T>`**: `[undefined, T]`
127
+ - **`Failure<E>`**: `[E, undefined]`
128
+ - **`isSuccess(result)`**: Type guard to check if result is success
129
+ - **`isFailure(result)`**: Type guard to check if result is failure
130
+
131
+ ### Input Handling
132
+
133
+ Both functions accept:
134
+ - Direct values
135
+ - Functions (sync or async)
136
+ - Promises
137
+
138
+ ## Dual Package Support
139
+
140
+ The package supports both CommonJS and ESM consumers:
141
+
142
+ ```json
143
+ {
144
+ "main": "./dist/index.cjs",
145
+ "module": "./dist/index.mjs",
146
+ "types": "./dist/index.d.mts",
147
+ "exports": {
148
+ "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" },
149
+ "import": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" }
150
+ }
151
+ }
152
+ ```
153
+
154
+ ## CI/CD
155
+
156
+ GitHub Actions workflow (`.github/workflows/main.yml`) runs on every push and PR:
157
+
158
+ - Tests against Node.js versions: 12, 14, 16, 18
159
+ - Uses `yarn install` and `yarn test`
160
+ - `fail-fast: false` to see results for all Node versions
161
+
162
+ ## Security Considerations
163
+
164
+ - Zero runtime dependencies - reduces supply chain attack surface
165
+ - Dev dependencies are locked via `package-lock.json`
166
+ - Uses `type: "module"` for native ESM support
167
+
168
+ ## Development Workflow
169
+
170
+ 1. Make changes to `src/index.ts`
171
+ 2. Add/update tests in `src/index.test.ts`
172
+ 3. Run `npm test` to verify build, lint, and tests pass
173
+ 4. The CI will test against multiple Node.js versions on push
174
+
175
+ ## Notes for AI Agents
176
+
177
+ - Always run `npm test` after making changes to ensure build, lint, and tests pass
178
+ - Type tests with `@ark/attest` are as important as runtime tests
179
+ - The library has zero dependencies - avoid adding any
180
+ - Maintain dual CJS/ESM compatibility when making changes
181
+ - Follow the existing function overload patterns for type inference
182
+ - Error handling should preserve the Go-style tuple return pattern
package/README.md CHANGED
@@ -99,8 +99,8 @@ const [errors, results] = await goTryAll([
99
99
  fetchComments(userId)
100
100
  ])
101
101
 
102
- // errors is (string | undefined)[]
103
- // results is (User | Posts | Comments | undefined)[]
102
+ // errors is [string | undefined, string | undefined, string | undefined]
103
+ // results is [User | undefined, Posts | undefined, Comments | undefined]
104
104
 
105
105
  const [user, posts, comments] = results
106
106
  ```
@@ -202,6 +202,88 @@ if (err === undefined) {
202
202
  }
203
203
  ```
204
204
 
205
+ ### Tagged Errors for Discriminated Unions
206
+
207
+ Create typed errors with a `_tag` property for pattern matching and discriminated unions:
208
+
209
+ ```ts
210
+ import { taggedError, goTryRaw, failure, type Result } from 'go-go-try'
211
+
212
+ // Define error types
213
+ const DatabaseError = taggedError('DatabaseError')
214
+ const NetworkError = taggedError('NetworkError')
215
+ const ValidationError = taggedError('ValidationError')
216
+
217
+ // Create a union type
218
+ import type { TaggedUnion } from 'go-go-try'
219
+
220
+ // Option 1: Using TaggedUnion helper (cleaner)
221
+ const DatabaseError = taggedError('DatabaseError')
222
+ const NetworkError = taggedError('NetworkError')
223
+ const ValidationError = taggedError('ValidationError')
224
+
225
+ type AppError = TaggedUnion<[typeof DatabaseError, typeof NetworkError, typeof ValidationError]>
226
+ // Equivalent to: DatabaseError | NetworkError | ValidationError
227
+
228
+ // Option 2: Using InstanceType (standard TypeScript)
229
+ type AppErrorVerbose =
230
+ | InstanceType<typeof DatabaseError>
231
+ | InstanceType<typeof NetworkError>
232
+ | InstanceType<typeof ValidationError>
233
+
234
+ // Use in functions with typed error returns
235
+ async function fetchUser(id: string): Promise<Result<AppError, User>> {
236
+ const [dbErr, user] = await goTryRaw(queryDatabase(id), DatabaseError)
237
+ if (dbErr) return failure(dbErr)
238
+
239
+ const [netErr, enriched] = await goTryRaw(enrichUserData(user!), NetworkError)
240
+ if (netErr) return failure(netErr)
241
+
242
+ return [undefined, enriched] as const
243
+ }
244
+
245
+ // Pattern matching on errors
246
+ const [err, user] = await fetchUser('123')
247
+ if (err) {
248
+ switch (err._tag) {
249
+ case 'DatabaseError':
250
+ console.error('Database failed:', err.message)
251
+ break
252
+ case 'NetworkError':
253
+ console.error('Network issue:', err.message)
254
+ break
255
+ case 'ValidationError':
256
+ console.error('Invalid data:', err.message)
257
+ break
258
+ }
259
+ }
260
+
261
+ // Exhaustive switch with compile-time safety
262
+ function assertNever(value: never): never {
263
+ throw new Error(`Unhandled case: ${String(value)}`)
264
+ }
265
+
266
+ function handleError(err: AppError): string {
267
+ switch (err._tag) {
268
+ case 'DatabaseError':
269
+ return `DB: ${err.message}`
270
+ case 'NetworkError':
271
+ return `NET: ${err.message}`
272
+ case 'ValidationError':
273
+ return `VAL: ${err.message}`
274
+ default:
275
+ // TypeScript will error here if any case is missing above
276
+ return assertNever(err)
277
+ }
278
+ }
279
+ ```
280
+
281
+ The `taggedError` function creates an error class with:
282
+ - `_tag`: A readonly string literal for discriminated unions
283
+ - `message`: The error message
284
+ - `cause`: Optional cause for error chaining
285
+ - `name`: Set to the tag value
286
+
205
287
  ### Helper Functions
206
288
 
207
289
  Build custom utilities on top of the primitives:
@@ -236,22 +318,86 @@ Executes a function, promise, or value and returns a Result type with error mess
236
318
  function goTry<T>(value: T | Promise<T> | (() => T | Promise<T>)): Result<string, T> | Promise<Result<string, T>>
237
319
  ```
238
320
 
239
- ### `goTryRaw<T, E>(value)`
321
+ ### `goTryRaw<T, E>(value, ErrorClass?)`
240
322
 
241
323
  Like `goTry` but returns the raw Error object instead of just the message.
242
324
 
325
+ Optionally accepts an error constructor to wrap caught errors - useful with `taggedError` for discriminated unions.
326
+
243
327
  ```ts
328
+ // Without ErrorClass - err is Error | undefined
244
329
  function goTryRaw<T, E = Error>(value: T | Promise<T> | (() => T | Promise<T>)): Result<E, T> | Promise<Result<E, T>>
330
+
331
+ // With ErrorClass - err is E | undefined (e.g., DatabaseError | undefined)
332
+ function goTryRaw<T, E>(value: T | Promise<T> | (() => T | Promise<T>), ErrorClass: ErrorConstructor<E>): Result<E, T> | Promise<Result<E, T>>
245
333
  ```
246
334
 
247
- ### `goTryAll<T>(promises)`
335
+ **Example:**
336
+ ```ts
337
+ const DatabaseError = taggedError('DatabaseError')
248
338
 
249
- Executes multiple promises in parallel. Returns a tuple of `[errors, results]`.
339
+ // Raw error (default)
340
+ const [err1, data1] = await goTryRaw(fetchData())
341
+ // err1 is Error | undefined
342
+
343
+ // Tagged error
344
+ const [err2, data2] = await goTryRaw(fetchData(), DatabaseError)
345
+ // err2 is DatabaseError | undefined
346
+ // err2._tag is 'DatabaseError' - enables discriminated unions
347
+ ```
348
+
349
+ ### `goTryAll<T>(items, options?)`
350
+
351
+ Executes multiple promises or factory functions with optional concurrency limit. Returns a tuple of `[errors, results]` with fixed tuple types preserving input order.
250
352
 
251
353
  ```ts
354
+ interface GoTryAllOptions {
355
+ concurrency?: number // 0 = unlimited (default), 1 = sequential, N = max concurrent
356
+ }
357
+
252
358
  function goTryAll<T extends readonly unknown[]>(
253
- promises: { [K in keyof T]: Promise<T[K]> }
254
- ): Promise<[string[] | undefined, { [K in keyof T]: T[K] | undefined }]>
359
+ items: { [K in keyof T]: Promise<T[K]> | (() => Promise<T[K]>) },
360
+ options?: GoTryAllOptions
361
+ ): Promise<[{ [K in keyof T]: string | undefined }, { [K in keyof T]: T[K] | undefined }]>
362
+ ```
363
+
364
+ **Promise mode** (pass promises directly):
365
+ ```ts
366
+ // Run all in parallel (default):
367
+ const [errors, results] = await goTryAll([
368
+ fetchUser(1), // Promise<User>
369
+ fetchUser(2), // Promise<User>
370
+ fetchUser(3), // Promise<User>
371
+ ])
372
+ // errors: [string | undefined, string | undefined, string | undefined]
373
+ // results: [User | undefined, User | undefined, User | undefined]
374
+ ```
375
+
376
+ **Factory mode** (pass functions that return promises):
377
+ ```ts
378
+ // True lazy execution - factories only called when a slot is available
379
+ const [errors, results] = await goTryAll([
380
+ () => fetchUser(1), // Only called when concurrency slot available
381
+ () => fetchUser(2), // Only called when concurrency slot available
382
+ () => fetchUser(3), // Only called when concurrency slot available
383
+ () => fetchUser(4), // Only called when concurrency slot available
384
+ ], { concurrency: 2 })
385
+
386
+ // Use factory mode when you need to:
387
+ // - Rate limit API calls (don't start HTTP requests until allowed)
388
+ // - Control database connection limits
389
+ // - Limit expensive computation resources
390
+ ```
391
+
392
+ ### `goTryAllRaw<T>(items, options?)`
393
+
394
+ Like `goTryAll`, but returns raw Error objects instead of error messages.
395
+
396
+ ```ts
397
+ function goTryAllRaw<T extends readonly unknown[]>(
398
+ items: { [K in keyof T]: Promise<T[K]> | (() => Promise<T[K]>) },
399
+ options?: GoTryAllOptions
400
+ ): Promise<[{ [K in keyof T]: Error | undefined }, { [K in keyof T]: T[K] | undefined }]>
255
401
  ```
256
402
 
257
403
  ### `goTryOr<T>(value, defaultValue)`
@@ -281,12 +427,120 @@ function success<T>(value: T): Success<T>
281
427
  function failure<E>(error: E): Failure<E>
282
428
  ```
283
429
 
430
+ ### `taggedError<T>(tag)`
431
+
432
+ Creates a tagged error class for discriminated error handling. Returns a class constructor that extends `Error` and includes a readonly `_tag` property.
433
+
434
+ ```ts
435
+ function taggedError<T extends string>(tag: T): TaggedErrorClass<T>
436
+
437
+ // Returned class interface:
438
+ class TaggedErrorClass<T> extends Error implements TaggedError<T> {
439
+ readonly _tag: T
440
+ readonly cause?: unknown
441
+ constructor(message: string, options?: { cause?: unknown })
442
+ }
443
+ ```
444
+
445
+ **Example:**
446
+ ```ts
447
+ const DatabaseError = taggedError('DatabaseError')
448
+ const err = new DatabaseError('connection failed', { cause: originalError })
449
+
450
+ console.log(err._tag) // 'DatabaseError'
451
+ console.log(err.message) // 'connection failed'
452
+ console.log(err.name) // 'DatabaseError'
453
+ console.log(err.cause) // originalError
454
+ ```
455
+
456
+ ### `TaggedInstance<T>`
457
+
458
+ Extracts the instance type from a tagged error class. Cleaner alternative to `InstanceType<typeof ErrorClass>`.
459
+
460
+ ```ts
461
+ type TaggedInstance<T extends ErrorConstructor<unknown>> =
462
+ T extends ErrorConstructor<infer E> ? E : never
463
+ ```
464
+
465
+ **Example:**
466
+ ```ts
467
+ const DatabaseError = taggedError('DatabaseError')
468
+ type DbError = TaggedInstance<typeof DatabaseError>
469
+ // Equivalent to: InstanceType<typeof DatabaseError>
470
+ ```
471
+
472
+ ### `TaggedUnion<T>`
473
+
474
+ Creates a union type from multiple tagged error classes.
475
+
476
+ ```ts
477
+ type TaggedUnion<T extends readonly ErrorConstructor<unknown>[]> =
478
+ { [K in keyof T]: T[K] extends ErrorConstructor<infer E> ? E : never }[number]
479
+ ```
480
+
481
+ **Example:**
482
+ ```ts
483
+ const DatabaseError = taggedError('DatabaseError')
484
+ const NetworkError = taggedError('NetworkError')
485
+ const ValidationError = taggedError('ValidationError')
486
+
487
+ // Before (verbose):
488
+ type AppErrorVerbose =
489
+ | InstanceType<typeof DatabaseError>
490
+ | InstanceType<typeof NetworkError>
491
+ | InstanceType<typeof ValidationError>
492
+
493
+ // After (clean):
494
+ type AppError = TaggedUnion<[typeof DatabaseError, typeof NetworkError, typeof ValidationError]>
495
+ // Results in: DatabaseError | NetworkError | ValidationError
496
+ ```
497
+
498
+ #### Automatic Union Inference
499
+
500
+ When using `goTryRaw` with different error classes in the same function, TypeScript **automatically infers** the union type without needing explicit type annotations:
501
+
502
+ ```ts
503
+ // No explicit return type needed!
504
+ async function fetchUserData(id: string) {
505
+ // First operation might fail with DatabaseError
506
+ const [dbErr, user] = await goTryRaw(queryDb(id), DatabaseError)
507
+ if (dbErr) return failure(dbErr) // returns Failure<DatabaseError>
508
+
509
+ // Second operation might fail with NetworkError
510
+ const [netErr, enriched] = await goTryRaw(enrichUser(user!), NetworkError)
511
+ if (netErr) return failure(netErr) // returns Failure<NetworkError>
512
+
513
+ return success(enriched) // returns Success<User>
514
+ }
515
+
516
+ // TypeScript infers: Promise<Result<DatabaseError | NetworkError, User>>
517
+ // No TaggedUnion or explicit types needed!
518
+ ```
519
+
520
+ The inferred union enables exhaustive pattern matching:
521
+
522
+ ```ts
523
+ const [err, user] = await fetchUserData('123')
524
+ if (err) {
525
+ switch (err._tag) {
526
+ case 'DatabaseError': /* handle db error */ break
527
+ case 'NetworkError': /* handle network error */ break
528
+ default: assertNever(err) // compile-time safety
529
+ }
530
+ }
531
+ ```
532
+
284
533
  ## Types
285
534
 
286
535
  ```ts
287
536
  type Success<T> = readonly [undefined, T]
288
537
  type Failure<E> = readonly [E, undefined]
289
538
  type Result<E, T> = Success<T> | Failure<E>
539
+
540
+ // Error type helpers
541
+ type TaggedInstance<T> = T extends ErrorConstructor<infer E> ? E : never
542
+ type TaggedUnion<T extends readonly ErrorConstructor<unknown>[]> =
543
+ { [K in keyof T]: T[K] extends ErrorConstructor<infer E> ? E : never }[number]
290
544
  ```
291
545
 
292
546
  ## License
package/dist/index.cjs CHANGED
@@ -1,5 +1,15 @@
1
1
  'use strict';
2
2
 
3
+ function taggedError(tag) {
4
+ return class TaggedErrorClass extends Error {
5
+ constructor(message, options) {
6
+ super(message);
7
+ this._tag = tag;
8
+ this.name = tag;
9
+ this.cause = options?.cause;
10
+ }
11
+ };
12
+ }
3
13
  function isSuccess(result) {
4
14
  return result[0] === void 0;
5
15
  }
@@ -26,34 +36,65 @@ function goTryOr(value, defaultValue) {
26
36
  return [getErrorMessage(err), resolveDefault(defaultValue)];
27
37
  }
28
38
  }
29
- async function goTryAll(promises) {
30
- const settled = await Promise.allSettled(promises);
39
+ async function runWithConcurrency(items, concurrency) {
40
+ if (items.length === 0) {
41
+ return [];
42
+ }
43
+ const isFactoryMode = typeof items[0] === "function";
44
+ if (!isFactoryMode && concurrency <= 0) {
45
+ return Promise.allSettled(items);
46
+ }
47
+ const results = new Array(items.length);
48
+ let index = 0;
49
+ async function worker() {
50
+ while (index < items.length) {
51
+ const currentIndex = index++;
52
+ try {
53
+ const item = items[currentIndex];
54
+ const value = isFactoryMode ? await item() : await item;
55
+ results[currentIndex] = { status: "fulfilled", value };
56
+ } catch (reason) {
57
+ results[currentIndex] = { status: "rejected", reason };
58
+ }
59
+ }
60
+ }
61
+ const workerCount = concurrency <= 0 ? items.length : Math.min(concurrency, items.length);
62
+ const workers = [];
63
+ for (let i = 0; i < workerCount; i++) {
64
+ workers.push(worker());
65
+ }
66
+ await Promise.all(workers);
67
+ return results;
68
+ }
69
+ async function goTryAll(items, options) {
70
+ const settled = await runWithConcurrency(items, options?.concurrency ?? 0);
31
71
  const errors = [];
32
72
  const results = [];
33
- for (const item of settled) {
73
+ for (let i = 0; i < settled.length; i++) {
74
+ const item = settled[i];
34
75
  if (item.status === "fulfilled") {
35
- errors.push(void 0);
36
- results.push(item.value);
76
+ errors[i] = void 0;
77
+ results[i] = item.value;
37
78
  } else {
38
- errors.push(getErrorMessage(item.reason));
39
- results.push(void 0);
79
+ errors[i] = getErrorMessage(item.reason);
80
+ results[i] = void 0;
40
81
  }
41
82
  }
42
83
  return [errors, results];
43
84
  }
44
- async function goTrySettled(promises) {
45
- const settled = await Promise.allSettled(promises);
85
+ async function goTryAllRaw(items, options) {
86
+ const settled = await runWithConcurrency(items, options?.concurrency ?? 0);
46
87
  const errors = [];
47
88
  const results = [];
48
- for (const item of settled) {
89
+ for (let i = 0; i < settled.length; i++) {
90
+ const item = settled[i];
49
91
  if (item.status === "fulfilled") {
50
- errors.push(void 0);
51
- results.push(item.value);
92
+ errors[i] = void 0;
93
+ results[i] = item.value;
52
94
  } else {
53
- errors.push(
54
- isError(item.reason) ? item.reason : new Error(String(item.reason))
55
- );
56
- results.push(void 0);
95
+ const reason = item.reason;
96
+ errors[i] = isError(reason) ? reason : new Error(String(reason));
97
+ results[i] = void 0;
57
98
  }
58
99
  }
59
100
  return [errors, results];
@@ -87,33 +128,40 @@ function goTry(value) {
87
128
  return failure(getErrorMessage(err));
88
129
  }
89
130
  }
90
- function goTryRaw(value) {
131
+ function goTryRaw(value, ErrorClass) {
132
+ const wrapError = (err) => {
133
+ if (ErrorClass) {
134
+ if (err === void 0) {
135
+ return new ErrorClass("undefined");
136
+ }
137
+ if (isError(err)) {
138
+ return new ErrorClass(err.message, { cause: err });
139
+ }
140
+ return new ErrorClass(String(err));
141
+ }
142
+ if (err === void 0) {
143
+ return new Error("undefined");
144
+ }
145
+ return isError(err) ? err : new Error(String(err));
146
+ };
91
147
  try {
92
148
  const result = typeof value === "function" ? value() : value;
93
149
  if (isPromise(result)) {
94
- return result.then((resolvedValue) => success(resolvedValue)).catch((err) => {
95
- if (err === void 0) {
96
- return failure(new Error("undefined"));
97
- }
98
- return failure(
99
- isError(err) ? err : new Error(String(err))
100
- );
101
- });
150
+ return result.then((resolvedValue) => success(resolvedValue)).catch((err) => failure(wrapError(err)));
102
151
  }
103
152
  return success(result);
104
153
  } catch (err) {
105
- return failure(
106
- isError(err) ? err : new Error(String(err))
107
- );
154
+ return failure(wrapError(err));
108
155
  }
109
156
  }
110
157
 
111
158
  exports.failure = failure;
112
159
  exports.goTry = goTry;
113
160
  exports.goTryAll = goTryAll;
161
+ exports.goTryAllRaw = goTryAllRaw;
114
162
  exports.goTryOr = goTryOr;
115
163
  exports.goTryRaw = goTryRaw;
116
- exports.goTrySettled = goTrySettled;
117
164
  exports.isFailure = isFailure;
118
165
  exports.isSuccess = isSuccess;
119
166
  exports.success = success;
167
+ exports.taggedError = taggedError;