go-go-try 7.3.0 → 7.4.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 CHANGED
@@ -46,10 +46,10 @@ const [err, todos = []] = await goTry(fetchTodos()) // err is string | undefined
46
46
  if (err) sendToErrorTrackingService(err)
47
47
 
48
48
  // goTry extracts the error message from the error object, if you want the raw error object, use goTryRaw
49
- const [err, value] = goTryRaw(() => JSON.parse('{/}')) // err is Error | undefined, value is T | undefined
49
+ const [err, value] = goTryRaw(() => JSON.parse('{/}')) // err is UnknownError | undefined, value is T | undefined
50
50
 
51
51
  // fetch todos, fallback to empty array, send error to your error tracking service
52
- const [err, todos = []] = await goTryRaw(fetchTodos()) // err is Error | undefined
52
+ const [err, todos = []] = await goTryRaw(fetchTodos()) // err is UnknownError | undefined
53
53
  if (err) sendToErrorTrackingService(err)
54
54
  ```
55
55
 
@@ -233,10 +233,10 @@ type AppErrorVerbose =
233
233
 
234
234
  // Use in functions with typed error returns
235
235
  async function fetchUser(id: string): Promise<Result<AppError, User>> {
236
- const [dbErr, user] = await goTryRaw(queryDatabase(id), DatabaseError)
236
+ const [dbErr, user] = await goTryRaw(queryDatabase(id), { errorClass: DatabaseError })
237
237
  if (dbErr) return failure(dbErr)
238
238
 
239
- const [netErr, enriched] = await goTryRaw(enrichUserData(user!), NetworkError)
239
+ const [netErr, enriched] = await goTryRaw(enrichUserData(user!), { errorClass: NetworkError })
240
240
  if (netErr) return failure(netErr)
241
241
 
242
242
  return [undefined, enriched] as const
@@ -318,32 +318,51 @@ Executes a function, promise, or value and returns a Result type with error mess
318
318
  function goTry<T>(value: T | Promise<T> | (() => T | Promise<T>)): Result<string, T> | Promise<Result<string, T>>
319
319
  ```
320
320
 
321
- ### `goTryRaw<T, E>(value, ErrorClass?)`
321
+ ### `goTryRaw<T, E>(value, options?)`
322
322
 
323
323
  Like `goTry` but returns the raw Error object instead of just the message.
324
324
 
325
- Optionally accepts an error constructor to wrap caught errors - useful with `taggedError` for discriminated unions.
325
+ By default, errors are wrapped in `UnknownError` (a tagged error class). You can customize this behavior with the options object:
326
326
 
327
327
  ```ts
328
- // Without ErrorClass - err is Error | undefined
329
- function goTryRaw<T, E = Error>(value: T | Promise<T> | (() => T | Promise<T>)): Result<E, T> | Promise<Result<E, T>>
328
+ type GoTryRawOptions<E> =
329
+ | { errorClass: ErrorConstructor<E> } // Wrap ALL errors in this class
330
+ | { systemErrorClass: ErrorConstructor<E> } // Only wrap non-tagged errors
331
+ | {} // Use defaults
332
+ ```
333
+
334
+ > **Note:** `errorClass` and `systemErrorClass` are **mutually exclusive**. TypeScript will show an error if you try to pass both.
335
+ > - Use `errorClass` when you want all errors wrapped in a specific type
336
+ > - Use `systemErrorClass` when you want tagged errors to pass through and only wrap untagged errors
337
+
338
+ ```ts
339
+ // Without options - err is UnknownError | undefined
340
+ function goTryRaw<T>(value: T | Promise<T> | (() => T | Promise<T>)): Result<UnknownError, T> | Promise<Result<UnknownError, T>>
330
341
 
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>>
342
+ // With options object - err is E | undefined
343
+ function goTryRaw<T, E>(value: T | Promise<T> | (() => T | Promise<T>), options: GoTryRawOptions<E>): Result<E, T> | Promise<Result<E, T>>
333
344
  ```
334
345
 
335
- **Example:**
346
+ **Examples:**
347
+
336
348
  ```ts
337
349
  const DatabaseError = taggedError('DatabaseError')
350
+ const NetworkError = taggedError('NetworkError')
338
351
 
339
- // Raw error (default)
352
+ // Default - errors wrapped in UnknownError
340
353
  const [err1, data1] = await goTryRaw(fetchData())
341
- // err1 is Error | undefined
354
+ // err1 is UnknownError | undefined
355
+ // err1?._tag === 'UnknownError'
342
356
 
343
- // Tagged error
344
- const [err2, data2] = await goTryRaw(fetchData(), DatabaseError)
357
+ // Options object - wrap all errors
358
+ const [err2, data2] = await goTryRaw(fetchData(), { errorClass: DatabaseError })
345
359
  // err2 is DatabaseError | undefined
346
- // err2._tag is 'DatabaseError' - enables discriminated unions
360
+ // err2?._tag === 'DatabaseError'
361
+
362
+ // Options object - systemErrorClass only wraps non-tagged errors
363
+ const [err3, data3] = await goTryRaw(fetchData(), { systemErrorClass: NetworkError })
364
+ // If fetchData throws a tagged error (e.g., DatabaseError), it passes through
365
+ // If fetchData throws a plain Error, it gets wrapped in NetworkError
347
366
  ```
348
367
 
349
368
  ### `goTryAll<T>(items, options?)`
@@ -453,6 +472,47 @@ console.log(err.name) // 'DatabaseError'
453
472
  console.log(err.cause) // originalError
454
473
  ```
455
474
 
475
+ ### `UnknownError`
476
+
477
+ A default tagged error class used by `goTryRaw` when no options are provided, or when `systemErrorClass` is not specified in the options object.
478
+
479
+ ```ts
480
+ import { UnknownError, goTryRaw } from 'go-go-try'
481
+
482
+ // Errors are automatically wrapped in UnknownError
483
+ const [err, data] = goTryRaw(() => {
484
+ throw new Error('something went wrong')
485
+ })
486
+
487
+ if (err) {
488
+ console.log(err._tag) // 'UnknownError'
489
+ console.log(err.message) // 'something went wrong'
490
+ console.log(err.cause) // original Error
491
+ }
492
+ ```
493
+
494
+ Since `UnknownError` is the default for `systemErrorClass`, you can distinguish between known tagged errors and unexpected system errors without specifying any options:
495
+
496
+ ```ts
497
+ const DatabaseError = taggedError('DatabaseError')
498
+
499
+ function fetchData() {
500
+ // Operations that might throw DatabaseError should use errorClass to wrap them
501
+ const [err1, data1] = goTryRaw(() => queryDatabase(), { errorClass: DatabaseError })
502
+
503
+ // Other operations use the default behavior - non-tagged errors become UnknownError
504
+ const [err2, data2] = goTryRaw(() => parseData(data1))
505
+ // err2 is UnknownError | undefined
506
+
507
+ // Now you can distinguish between known and unknown error types
508
+ if (err1) {
509
+ console.log('Known DB error:', err1.message)
510
+ } else if (err2) {
511
+ console.log('Unexpected error:', err2.message)
512
+ }
513
+ }
514
+ ```
515
+
456
516
  ### `TaggedUnion<T>`
457
517
 
458
518
  Creates a union type from multiple tagged error classes.
@@ -487,11 +547,11 @@ When using `goTryRaw` with different error classes in the same function, TypeScr
487
547
  // No explicit return type needed!
488
548
  async function fetchUserData(id: string) {
489
549
  // First operation might fail with DatabaseError
490
- const [dbErr, user] = await goTryRaw(queryDb(id), DatabaseError)
550
+ const [dbErr, user] = await goTryRaw(queryDb(id), { errorClass: DatabaseError })
491
551
  if (dbErr) return failure(dbErr) // returns Failure<DatabaseError>
492
552
 
493
553
  // Second operation might fail with NetworkError
494
- const [netErr, enriched] = await goTryRaw(enrichUser(user!), NetworkError)
554
+ const [netErr, enriched] = await goTryRaw(enrichUser(user!), { errorClass: NetworkError })
495
555
  if (netErr) return failure(netErr) // returns Failure<NetworkError>
496
556
 
497
557
  return success(enriched) // returns Success<User>
@@ -525,6 +585,12 @@ type Result<E, T> = Success<T> | Failure<E>
525
585
  type TaggedInstance<T> = T extends ErrorConstructor<infer E> ? E : never
526
586
  type TaggedUnion<T extends readonly ErrorConstructor<unknown>[]> =
527
587
  { [K in keyof T]: T[K] extends ErrorConstructor<infer E> ? E : never }[number]
588
+
589
+ // Options for goTryRaw (errorClass and systemErrorClass are mutually exclusive)
590
+ type GoTryRawOptions<E> =
591
+ | { errorClass: ErrorConstructor<E>; systemErrorClass?: never }
592
+ | { errorClass?: never; systemErrorClass: ErrorConstructor<E> }
593
+ | { errorClass?: never; systemErrorClass?: never }
528
594
  ```
529
595
 
530
596
  ## License
package/dist/index.cjs CHANGED
@@ -50,16 +50,46 @@ function goTry(value) {
50
50
  }
51
51
  }
52
52
 
53
- function goTryRaw(value, ErrorClass) {
53
+ function taggedError(tag) {
54
+ return class TaggedErrorClass extends Error {
55
+ constructor(message, options) {
56
+ super(message);
57
+ this._tag = tag;
58
+ this.name = tag;
59
+ this.cause = options?.cause;
60
+ }
61
+ };
62
+ }
63
+
64
+ const UnknownError = taggedError("UnknownError");
65
+
66
+ function isTaggedError(err) {
67
+ return isError(err) && "_tag" in err && typeof err._tag === "string";
68
+ }
69
+ function goTryRaw(value, options) {
70
+ const { errorClass, systemErrorClass } = options || {};
71
+ const actualSystemErrorClass = systemErrorClass ?? UnknownError;
54
72
  const wrapError = (err) => {
55
- if (ErrorClass) {
73
+ if (errorClass) {
74
+ if (err === void 0) {
75
+ return new errorClass("undefined");
76
+ }
77
+ if (isError(err)) {
78
+ return new errorClass(err.message, { cause: err });
79
+ }
80
+ return new errorClass(String(err));
81
+ }
82
+ if (actualSystemErrorClass) {
83
+ if (isTaggedError(err)) {
84
+ return err;
85
+ }
56
86
  if (err === void 0) {
57
- return new ErrorClass("undefined");
87
+ return new actualSystemErrorClass("undefined");
58
88
  }
59
89
  if (isError(err)) {
60
- return new ErrorClass(err.message, { cause: err });
90
+ return new actualSystemErrorClass(err.message, { cause: err });
61
91
  }
62
- return new ErrorClass(String(err));
92
+ return new actualSystemErrorClass(String(err));
63
93
  }
64
94
  if (err === void 0) {
65
95
  return new Error("undefined");
@@ -153,17 +183,6 @@ async function goTryAllRaw(items, options) {
153
183
  return [errors, results];
154
184
  }
155
185
 
156
- function taggedError(tag) {
157
- return class TaggedErrorClass extends Error {
158
- constructor(message, options) {
159
- super(message);
160
- this._tag = tag;
161
- this.name = tag;
162
- this.cause = options?.cause;
163
- }
164
- };
165
- }
166
-
167
186
  function assert(condition, errorOrClass, message) {
168
187
  if (!condition) {
169
188
  if (typeof errorOrClass === "string") {
@@ -176,6 +195,7 @@ function assert(condition, errorOrClass, message) {
176
195
  }
177
196
  }
178
197
 
198
+ exports.UnknownError = UnknownError;
179
199
  exports.assert = assert;
180
200
  exports.assertNever = assertNever;
181
201
  exports.failure = failure;
package/dist/index.d.cts CHANGED
@@ -28,6 +28,20 @@ interface GoTryAllOptions {
28
28
  type ErrorConstructor<E> = new (message: string, options?: {
29
29
  cause?: unknown;
30
30
  }) => E;
31
+ /**
32
+ * Options for goTryRaw function.
33
+ * errorClass and systemErrorClass are mutually exclusive - you can only provide one.
34
+ */
35
+ type GoTryRawOptions<E = Error> = {
36
+ errorClass: ErrorConstructor<E>;
37
+ systemErrorClass?: never;
38
+ } | {
39
+ errorClass?: never;
40
+ systemErrorClass: ErrorConstructor<E>;
41
+ } | {
42
+ errorClass?: never;
43
+ systemErrorClass?: never;
44
+ };
31
45
  /**
32
46
  * Creates a union type from multiple tagged error classes.
33
47
  *
@@ -71,14 +85,44 @@ declare function goTry<T>(promise: Promise<T>): Promise<Result<string, T>>;
71
85
  declare function goTry<T>(fn: () => T): Result<string, T>;
72
86
  declare function goTry<T>(value: T): Result<string, T>;
73
87
 
88
+ /**
89
+ * Default system error class for errors that aren't already wrapped in a tagged error class.
90
+ *
91
+ * @example
92
+ * // By default, goTryRaw wraps unknown errors in UnknownError
93
+ * const [err, result] = goTryRaw(() => mightThrow())
94
+ * if (err) {
95
+ * console.log(err._tag) // 'UnknownError'
96
+ * }
97
+ *
98
+ * @example
99
+ * // Use a custom system error class
100
+ * const SystemError = taggedError('SystemError')
101
+ * const [err, result] = goTryRaw(() => mightThrow(), {
102
+ * systemErrorClass: SystemError
103
+ * })
104
+ */
105
+ declare const UnknownError: {
106
+ new (message: string, options?: {
107
+ cause?: unknown;
108
+ } | undefined): {
109
+ readonly _tag: "UnknownError";
110
+ readonly cause?: unknown;
111
+ name: string;
112
+ message: string;
113
+ stack?: string;
114
+ };
115
+ isError(error: unknown): error is Error;
116
+ };
117
+
74
118
  /**
75
119
  * Executes a function, promise, or value and returns a Result type.
76
120
  * If an error occurs, it returns a Failure with the raw error object.
77
121
  *
78
122
  * @template T The type of the successful result
79
- * @template E The type of the error, defaults to Error
123
+ * @template E The type of the error
80
124
  * @param {T | Promise<T> | (() => T | Promise<T>)} value - The value, promise, or function to execute
81
- * @param {ErrorConstructor<E>} [ErrorClass] - Optional error constructor to wrap caught errors
125
+ * @param {GoTryRawOptions<E>} [options] - Optional options object
82
126
  * @returns {Result<E, T> | Promise<Result<E, T>>} A Result type or a Promise of a Result type
83
127
  *
84
128
  * @example
@@ -94,21 +138,26 @@ declare function goTry<T>(value: T): Result<string, T>;
94
138
  * const [err, result] = await goTryRaw(fetch('https://api.example.com/data'));
95
139
  *
96
140
  * @example
97
- * // With tagged error for discriminated unions
141
+ * // With options object - wrap all errors
98
142
  * const DatabaseError = taggedError('DatabaseError');
99
- * const [err, result] = await goTryRaw(fetchData(), DatabaseError);
100
- * // err is InstanceType<typeof DatabaseError> | undefined
143
+ * const [err, result] = await goTryRaw(fetchData(), { errorClass: DatabaseError });
144
+ *
145
+ * @example
146
+ * // With options object - systemErrorClass only wraps non-tagged errors
147
+ * const [err, result] = await goTryRaw(fetchData(), { systemErrorClass: UnknownError });
148
+ * // Errors thrown as tagged errors pass through
149
+ * // Other errors are wrapped in UnknownError
101
150
  */
102
- declare function goTryRaw<T, E = Error>(fn: () => never): Result<E, never>;
103
- declare function goTryRaw<T, E = Error>(fn: () => never, ErrorClass: ErrorConstructor<E>): Result<E, never>;
104
- declare function goTryRaw<T, E = Error>(fn: () => Promise<T>): Promise<Result<E, T>>;
105
- declare function goTryRaw<T, E = Error>(fn: () => Promise<T>, ErrorClass: ErrorConstructor<E>): Promise<Result<E, T>>;
106
- declare function goTryRaw<T, E = Error>(promise: Promise<T>): Promise<Result<E, T>>;
107
- declare function goTryRaw<T, E = Error>(promise: Promise<T>, ErrorClass: ErrorConstructor<E>): Promise<Result<E, T>>;
108
- declare function goTryRaw<T, E = Error>(fn: () => T): Result<E, T>;
109
- declare function goTryRaw<T, E = Error>(fn: () => T, ErrorClass: ErrorConstructor<E>): Result<E, T>;
110
- declare function goTryRaw<T, E = Error>(value: T): Result<E, T>;
111
- declare function goTryRaw<T, E = Error>(value: T, ErrorClass: ErrorConstructor<E>): Result<E, T>;
151
+ declare function goTryRaw<T>(fn: () => never): Result<Error, never>;
152
+ declare function goTryRaw<T, E = InstanceType<typeof UnknownError>>(fn: () => never, options: GoTryRawOptions<E>): Result<E, never>;
153
+ declare function goTryRaw<T>(fn: () => Promise<T>): Promise<Result<Error, T>>;
154
+ declare function goTryRaw<T, E = InstanceType<typeof UnknownError>>(fn: () => Promise<T>, options: GoTryRawOptions<E>): Promise<Result<E, T>>;
155
+ declare function goTryRaw<T>(promise: Promise<T>): Promise<Result<Error, T>>;
156
+ declare function goTryRaw<T, E = InstanceType<typeof UnknownError>>(promise: Promise<T>, options: GoTryRawOptions<E>): Promise<Result<E, T>>;
157
+ declare function goTryRaw<T>(fn: () => T): Result<Error, T>;
158
+ declare function goTryRaw<T, E = InstanceType<typeof UnknownError>>(fn: () => T, options: GoTryRawOptions<E>): Result<E, T>;
159
+ declare function goTryRaw<T>(value: T): Result<Error, T>;
160
+ declare function goTryRaw<T, E = InstanceType<typeof UnknownError>>(value: T, options: GoTryRawOptions<E>): Result<E, T>;
112
161
 
113
162
  /**
114
163
  * Executes a function, promise, or value and returns a Result type with a fallback default.
@@ -301,6 +350,6 @@ declare function failure<E>(error: E): Failure<E>;
301
350
  */
302
351
  declare function assertNever(value: never): never;
303
352
 
304
- export { assert, assertNever, failure, goTry, goTryAll, goTryAllRaw, goTryOr, goTryRaw, isFailure, isSuccess, success, taggedError };
305
- export type { ErrorConstructor, Failure, GoTryAllOptions, MaybePromise, Result, ResultWithDefault, Success, TaggedError, TaggedUnion };
353
+ export { UnknownError, assert, assertNever, failure, goTry, goTryAll, goTryAllRaw, goTryOr, goTryRaw, isFailure, isSuccess, success, taggedError };
354
+ export type { ErrorConstructor, Failure, GoTryAllOptions, GoTryRawOptions, MaybePromise, Result, ResultWithDefault, Success, TaggedError, TaggedUnion };
306
355
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","sources":["../src/types.ts","../src/goTry.ts","../src/goTryRaw.ts","../src/goTryOr.ts","../src/goTryAll.ts","../src/tagged-error.ts","../src/assert.ts","../src/result-helpers.ts"],"mappings":"AAAA;;;AAIM,KAAM,OAAO;AACb,KAAM,OAAO;AACb,KAAM,MAAM,SAAS,OAAO,MAAM,OAAO;AAEzC,KAAM,iBAAiB;AAEvB,KAAM,YAAY,UAAU,OAAO;AAEzC;;;;AAIM,UAAW,WAAW;;;;;AAMtB,UAAW,eAAe;;;;;;;AAQhC;;;AAGM,KAAM,gBAAgB;;;AAE5B;;;;;;;;;;;;;AAaM,KAAM,WAAW,oBAAoB,gBAAgB;iCAC1B,gBAAgB;;;AC7CjD;;;;;;;;;;;;;;;;;;;;AAoBA,iBAAgB,KAAK,sBAAsB,MAAM;AACjD,iBAAgB,KAAK,cAAc,OAAO,MAAM,OAAO,CAAC,MAAM;AAC9D,iBAAgB,KAAK,aAAa,OAAO,MAAM,OAAO,CAAC,MAAM;AAC7D,iBAAgB,KAAK,kBAAkB,MAAM;AAC7C,iBAAgB,KAAK,eAAe,MAAM;;ACxB1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,iBAAgB,QAAQ,QAAQ,KAAK,oBAAoB,MAAM;AAC/D,iBAAgB,QAAQ,QAAQ,KAAK,+BAA+B,gBAAgB,MAAM,MAAM;AAChG,iBAAgB,QAAQ,QAAQ,KAAK,YACzB,OAAO,MAChB,OAAO,CAAC,MAAM;AACjB,iBAAgB,QAAQ,QAAQ,KAAK,YACzB,OAAO,iBACL,gBAAgB,MAC3B,OAAO,CAAC,MAAM;AACjB,iBAAgB,QAAQ,QAAQ,KAAK,WAC1B,OAAO,MACf,OAAO,CAAC,MAAM;AACjB,iBAAgB,QAAQ,QAAQ,KAAK,WAC1B,OAAO,iBACJ,gBAAgB,MAC3B,OAAO,CAAC,MAAM;AACjB,iBAAgB,QAAQ,QAAQ,KAAK,gBAAgB,MAAM;AAC3D,iBAAgB,QAAQ,QAAQ,KAAK,2BAA2B,gBAAgB,MAAM,MAAM;AAC5F,iBAAgB,QAAQ,QAAQ,KAAK,aAAa,MAAM;AACxD,iBAAgB,QAAQ,QAAQ,KAAK,wBAAwB,gBAAgB,MAAM,MAAM;;AC/CzF;;;;;;;;;;;;;;;;;;;;;AAqBA,iBAAgB,OAAO,mDAAmD,iBAAiB;AAC3F,iBAAgB,OAAO,cACX,OAAO,mCAEhB,OAAO,CAAC,iBAAiB;AAC5B,iBAAgB,OAAO,aACZ,OAAO,mCAEf,OAAO,CAAC,iBAAiB;AAC5B,iBAAgB,OAAO,+CAA+C,iBAAiB;AACvF,iBAAgB,OAAO,4CAA4C,iBAAiB;;ACgBpF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,iBAAsB,QAAQ;oBACH,OAAO,gBAAgB,OAAO;aAC7C,eAAe,GACxB,OAAO;;;;;AAoBV;;;;;;;;;;AAUA,iBAAsB,WAAW;oBACN,OAAO,gBAAgB,OAAO;aAC7C,eAAe,GACxB,OAAO;oBAAoB,KAAK;;;;;ACrHnC;;;;;;;;;;;;;;;;;;;;;;;;AAwBA,iBAAgB,WAAW;;;;;;;;;;;;;AC1B3B;;;;;;;;;;;;;;;;;;;;;;AAsBA,iBAAgB,MAAM,4BAA4B,KAAK;AAEvD;;;;;;;;;;;;;;AAcA,iBAAgB,MAAM,WAAW,KAAK;;ACpCtC,iBAAgB,SAAS,eAAe,MAAM,mBAAmB,OAAO;AAIxE,iBAAgB,SAAS,eAAe,MAAM,mBAAmB,OAAO;AAIxE,iBAAgB,OAAO,eAAe,OAAO;AAI7C,iBAAgB,OAAO,eAAe,OAAO;AAI7C;;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,iBAAgB,WAAW","names":[]}
1
+ {"version":3,"file":"index.d.cts","sources":["../src/types.ts","../src/goTry.ts","../src/unknown-error.ts","../src/goTryRaw.ts","../src/goTryOr.ts","../src/goTryAll.ts","../src/tagged-error.ts","../src/assert.ts","../src/result-helpers.ts"],"mappings":"AAAA;;;AAIM,KAAM,OAAO;AACb,KAAM,OAAO;AACb,KAAM,MAAM,SAAS,OAAO,MAAM,OAAO;AAEzC,KAAM,iBAAiB;AAEvB,KAAM,YAAY,UAAU,OAAO;AAEzC;;;;AAIM,UAAW,WAAW;;;;;AAMtB,UAAW,eAAe;;;;;;;AAQhC;;;AAGM,KAAM,gBAAgB;;;AAO5B;;;;AAIM,KAAM,eAAe,KAAK,KAAK;gBACnB,gBAAgB;;;;sBACU,gBAAgB;;;;;AAG5D;;;;;;;;;;;;;AAaM,KAAM,WAAW,oBAAoB,gBAAgB;iCAC1B,gBAAgB;;;AC3DjD;;;;;;;;;;;;;;;;;;;;AAoBA,iBAAgB,KAAK,sBAAsB,MAAM;AACjD,iBAAgB,KAAK,cAAc,OAAO,MAAM,OAAO,CAAC,MAAM;AAC9D,iBAAgB,KAAK,aAAa,OAAO,MAAM,OAAO,CAAC,MAAM;AAC7D,iBAAgB,KAAK,kBAAkB,MAAM;AAC7C,iBAAgB,KAAK,eAAe,MAAM;;AC1B1C;;;;;;;;;;;;;;;;;AAiBA,cAAa,YAAY;;;;;;;;;;;;;ACPzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,iBAAgB,QAAQ,sBAAsB,MAAM,CAAC,KAAK;AAC1D,iBAAgB,QAAQ,QAAQ,YAAY,QAAQ,YAAY,6BAA6B,eAAe,MAAM,MAAM;AACxH,iBAAgB,QAAQ,cACZ,OAAO,MAChB,OAAO,CAAC,MAAM,CAAC,KAAK;AACvB,iBAAgB,QAAQ,QAAQ,YAAY,QAAQ,YAAY,aACpD,OAAO,cACR,eAAe,MACvB,OAAO,CAAC,MAAM;AACjB,iBAAgB,QAAQ,aACb,OAAO,MACf,OAAO,CAAC,MAAM,CAAC,KAAK;AACvB,iBAAgB,QAAQ,QAAQ,YAAY,QAAQ,YAAY,YACrD,OAAO,cACP,eAAe,MACvB,OAAO,CAAC,MAAM;AACjB,iBAAgB,QAAQ,kBAAkB,MAAM,CAAC,KAAK;AACtD,iBAAgB,QAAQ,QAAQ,YAAY,QAAQ,YAAY,yBAAyB,eAAe,MAAM,MAAM;AACpH,iBAAgB,QAAQ,eAAe,MAAM,CAAC,KAAK;AACnD,iBAAgB,QAAQ,QAAQ,YAAY,QAAQ,YAAY,sBAAsB,eAAe,MAAM,MAAM;;AC5DjH;;;;;;;;;;;;;;;;;;;;;AAqBA,iBAAgB,OAAO,mDAAmD,iBAAiB;AAC3F,iBAAgB,OAAO,cACX,OAAO,mCAEhB,OAAO,CAAC,iBAAiB;AAC5B,iBAAgB,OAAO,aACZ,OAAO,mCAEf,OAAO,CAAC,iBAAiB;AAC5B,iBAAgB,OAAO,+CAA+C,iBAAiB;AACvF,iBAAgB,OAAO,4CAA4C,iBAAiB;;ACgBpF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,iBAAsB,QAAQ;oBACH,OAAO,gBAAgB,OAAO;aAC7C,eAAe,GACxB,OAAO;;;;;AAoBV;;;;;;;;;;AAUA,iBAAsB,WAAW;oBACN,OAAO,gBAAgB,OAAO;aAC7C,eAAe,GACxB,OAAO;oBAAoB,KAAK;;;;;ACrHnC;;;;;;;;;;;;;;;;;;;;;;;;AAwBA,iBAAgB,WAAW;;;;;;;;;;;;;AC1B3B;;;;;;;;;;;;;;;;;;;;;;AAsBA,iBAAgB,MAAM,4BAA4B,KAAK;AAEvD;;;;;;;;;;;;;;AAcA,iBAAgB,MAAM,WAAW,KAAK;;ACpCtC,iBAAgB,SAAS,eAAe,MAAM,mBAAmB,OAAO;AAIxE,iBAAgB,SAAS,eAAe,MAAM,mBAAmB,OAAO;AAIxE,iBAAgB,OAAO,eAAe,OAAO;AAI7C,iBAAgB,OAAO,eAAe,OAAO;AAI7C;;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,iBAAgB,WAAW","names":[]}
package/dist/index.d.mts CHANGED
@@ -28,6 +28,20 @@ interface GoTryAllOptions {
28
28
  type ErrorConstructor<E> = new (message: string, options?: {
29
29
  cause?: unknown;
30
30
  }) => E;
31
+ /**
32
+ * Options for goTryRaw function.
33
+ * errorClass and systemErrorClass are mutually exclusive - you can only provide one.
34
+ */
35
+ type GoTryRawOptions<E = Error> = {
36
+ errorClass: ErrorConstructor<E>;
37
+ systemErrorClass?: never;
38
+ } | {
39
+ errorClass?: never;
40
+ systemErrorClass: ErrorConstructor<E>;
41
+ } | {
42
+ errorClass?: never;
43
+ systemErrorClass?: never;
44
+ };
31
45
  /**
32
46
  * Creates a union type from multiple tagged error classes.
33
47
  *
@@ -71,14 +85,44 @@ declare function goTry<T>(promise: Promise<T>): Promise<Result<string, T>>;
71
85
  declare function goTry<T>(fn: () => T): Result<string, T>;
72
86
  declare function goTry<T>(value: T): Result<string, T>;
73
87
 
88
+ /**
89
+ * Default system error class for errors that aren't already wrapped in a tagged error class.
90
+ *
91
+ * @example
92
+ * // By default, goTryRaw wraps unknown errors in UnknownError
93
+ * const [err, result] = goTryRaw(() => mightThrow())
94
+ * if (err) {
95
+ * console.log(err._tag) // 'UnknownError'
96
+ * }
97
+ *
98
+ * @example
99
+ * // Use a custom system error class
100
+ * const SystemError = taggedError('SystemError')
101
+ * const [err, result] = goTryRaw(() => mightThrow(), {
102
+ * systemErrorClass: SystemError
103
+ * })
104
+ */
105
+ declare const UnknownError: {
106
+ new (message: string, options?: {
107
+ cause?: unknown;
108
+ } | undefined): {
109
+ readonly _tag: "UnknownError";
110
+ readonly cause?: unknown;
111
+ name: string;
112
+ message: string;
113
+ stack?: string;
114
+ };
115
+ isError(error: unknown): error is Error;
116
+ };
117
+
74
118
  /**
75
119
  * Executes a function, promise, or value and returns a Result type.
76
120
  * If an error occurs, it returns a Failure with the raw error object.
77
121
  *
78
122
  * @template T The type of the successful result
79
- * @template E The type of the error, defaults to Error
123
+ * @template E The type of the error
80
124
  * @param {T | Promise<T> | (() => T | Promise<T>)} value - The value, promise, or function to execute
81
- * @param {ErrorConstructor<E>} [ErrorClass] - Optional error constructor to wrap caught errors
125
+ * @param {GoTryRawOptions<E>} [options] - Optional options object
82
126
  * @returns {Result<E, T> | Promise<Result<E, T>>} A Result type or a Promise of a Result type
83
127
  *
84
128
  * @example
@@ -94,21 +138,26 @@ declare function goTry<T>(value: T): Result<string, T>;
94
138
  * const [err, result] = await goTryRaw(fetch('https://api.example.com/data'));
95
139
  *
96
140
  * @example
97
- * // With tagged error for discriminated unions
141
+ * // With options object - wrap all errors
98
142
  * const DatabaseError = taggedError('DatabaseError');
99
- * const [err, result] = await goTryRaw(fetchData(), DatabaseError);
100
- * // err is InstanceType<typeof DatabaseError> | undefined
143
+ * const [err, result] = await goTryRaw(fetchData(), { errorClass: DatabaseError });
144
+ *
145
+ * @example
146
+ * // With options object - systemErrorClass only wraps non-tagged errors
147
+ * const [err, result] = await goTryRaw(fetchData(), { systemErrorClass: UnknownError });
148
+ * // Errors thrown as tagged errors pass through
149
+ * // Other errors are wrapped in UnknownError
101
150
  */
102
- declare function goTryRaw<T, E = Error>(fn: () => never): Result<E, never>;
103
- declare function goTryRaw<T, E = Error>(fn: () => never, ErrorClass: ErrorConstructor<E>): Result<E, never>;
104
- declare function goTryRaw<T, E = Error>(fn: () => Promise<T>): Promise<Result<E, T>>;
105
- declare function goTryRaw<T, E = Error>(fn: () => Promise<T>, ErrorClass: ErrorConstructor<E>): Promise<Result<E, T>>;
106
- declare function goTryRaw<T, E = Error>(promise: Promise<T>): Promise<Result<E, T>>;
107
- declare function goTryRaw<T, E = Error>(promise: Promise<T>, ErrorClass: ErrorConstructor<E>): Promise<Result<E, T>>;
108
- declare function goTryRaw<T, E = Error>(fn: () => T): Result<E, T>;
109
- declare function goTryRaw<T, E = Error>(fn: () => T, ErrorClass: ErrorConstructor<E>): Result<E, T>;
110
- declare function goTryRaw<T, E = Error>(value: T): Result<E, T>;
111
- declare function goTryRaw<T, E = Error>(value: T, ErrorClass: ErrorConstructor<E>): Result<E, T>;
151
+ declare function goTryRaw<T>(fn: () => never): Result<Error, never>;
152
+ declare function goTryRaw<T, E = InstanceType<typeof UnknownError>>(fn: () => never, options: GoTryRawOptions<E>): Result<E, never>;
153
+ declare function goTryRaw<T>(fn: () => Promise<T>): Promise<Result<Error, T>>;
154
+ declare function goTryRaw<T, E = InstanceType<typeof UnknownError>>(fn: () => Promise<T>, options: GoTryRawOptions<E>): Promise<Result<E, T>>;
155
+ declare function goTryRaw<T>(promise: Promise<T>): Promise<Result<Error, T>>;
156
+ declare function goTryRaw<T, E = InstanceType<typeof UnknownError>>(promise: Promise<T>, options: GoTryRawOptions<E>): Promise<Result<E, T>>;
157
+ declare function goTryRaw<T>(fn: () => T): Result<Error, T>;
158
+ declare function goTryRaw<T, E = InstanceType<typeof UnknownError>>(fn: () => T, options: GoTryRawOptions<E>): Result<E, T>;
159
+ declare function goTryRaw<T>(value: T): Result<Error, T>;
160
+ declare function goTryRaw<T, E = InstanceType<typeof UnknownError>>(value: T, options: GoTryRawOptions<E>): Result<E, T>;
112
161
 
113
162
  /**
114
163
  * Executes a function, promise, or value and returns a Result type with a fallback default.
@@ -301,6 +350,6 @@ declare function failure<E>(error: E): Failure<E>;
301
350
  */
302
351
  declare function assertNever(value: never): never;
303
352
 
304
- export { assert, assertNever, failure, goTry, goTryAll, goTryAllRaw, goTryOr, goTryRaw, isFailure, isSuccess, success, taggedError };
305
- export type { ErrorConstructor, Failure, GoTryAllOptions, MaybePromise, Result, ResultWithDefault, Success, TaggedError, TaggedUnion };
353
+ export { UnknownError, assert, assertNever, failure, goTry, goTryAll, goTryAllRaw, goTryOr, goTryRaw, isFailure, isSuccess, success, taggedError };
354
+ export type { ErrorConstructor, Failure, GoTryAllOptions, GoTryRawOptions, MaybePromise, Result, ResultWithDefault, Success, TaggedError, TaggedUnion };
306
355
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sources":["../src/types.ts","../src/goTry.ts","../src/goTryRaw.ts","../src/goTryOr.ts","../src/goTryAll.ts","../src/tagged-error.ts","../src/assert.ts","../src/result-helpers.ts"],"mappings":"AAAA;;;AAIM,KAAM,OAAO;AACb,KAAM,OAAO;AACb,KAAM,MAAM,SAAS,OAAO,MAAM,OAAO;AAEzC,KAAM,iBAAiB;AAEvB,KAAM,YAAY,UAAU,OAAO;AAEzC;;;;AAIM,UAAW,WAAW;;;;;AAMtB,UAAW,eAAe;;;;;;;AAQhC;;;AAGM,KAAM,gBAAgB;;;AAE5B;;;;;;;;;;;;;AAaM,KAAM,WAAW,oBAAoB,gBAAgB;iCAC1B,gBAAgB;;;AC7CjD;;;;;;;;;;;;;;;;;;;;AAoBA,iBAAgB,KAAK,sBAAsB,MAAM;AACjD,iBAAgB,KAAK,cAAc,OAAO,MAAM,OAAO,CAAC,MAAM;AAC9D,iBAAgB,KAAK,aAAa,OAAO,MAAM,OAAO,CAAC,MAAM;AAC7D,iBAAgB,KAAK,kBAAkB,MAAM;AAC7C,iBAAgB,KAAK,eAAe,MAAM;;ACxB1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,iBAAgB,QAAQ,QAAQ,KAAK,oBAAoB,MAAM;AAC/D,iBAAgB,QAAQ,QAAQ,KAAK,+BAA+B,gBAAgB,MAAM,MAAM;AAChG,iBAAgB,QAAQ,QAAQ,KAAK,YACzB,OAAO,MAChB,OAAO,CAAC,MAAM;AACjB,iBAAgB,QAAQ,QAAQ,KAAK,YACzB,OAAO,iBACL,gBAAgB,MAC3B,OAAO,CAAC,MAAM;AACjB,iBAAgB,QAAQ,QAAQ,KAAK,WAC1B,OAAO,MACf,OAAO,CAAC,MAAM;AACjB,iBAAgB,QAAQ,QAAQ,KAAK,WAC1B,OAAO,iBACJ,gBAAgB,MAC3B,OAAO,CAAC,MAAM;AACjB,iBAAgB,QAAQ,QAAQ,KAAK,gBAAgB,MAAM;AAC3D,iBAAgB,QAAQ,QAAQ,KAAK,2BAA2B,gBAAgB,MAAM,MAAM;AAC5F,iBAAgB,QAAQ,QAAQ,KAAK,aAAa,MAAM;AACxD,iBAAgB,QAAQ,QAAQ,KAAK,wBAAwB,gBAAgB,MAAM,MAAM;;AC/CzF;;;;;;;;;;;;;;;;;;;;;AAqBA,iBAAgB,OAAO,mDAAmD,iBAAiB;AAC3F,iBAAgB,OAAO,cACX,OAAO,mCAEhB,OAAO,CAAC,iBAAiB;AAC5B,iBAAgB,OAAO,aACZ,OAAO,mCAEf,OAAO,CAAC,iBAAiB;AAC5B,iBAAgB,OAAO,+CAA+C,iBAAiB;AACvF,iBAAgB,OAAO,4CAA4C,iBAAiB;;ACgBpF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,iBAAsB,QAAQ;oBACH,OAAO,gBAAgB,OAAO;aAC7C,eAAe,GACxB,OAAO;;;;;AAoBV;;;;;;;;;;AAUA,iBAAsB,WAAW;oBACN,OAAO,gBAAgB,OAAO;aAC7C,eAAe,GACxB,OAAO;oBAAoB,KAAK;;;;;ACrHnC;;;;;;;;;;;;;;;;;;;;;;;;AAwBA,iBAAgB,WAAW;;;;;;;;;;;;;AC1B3B;;;;;;;;;;;;;;;;;;;;;;AAsBA,iBAAgB,MAAM,4BAA4B,KAAK;AAEvD;;;;;;;;;;;;;;AAcA,iBAAgB,MAAM,WAAW,KAAK;;ACpCtC,iBAAgB,SAAS,eAAe,MAAM,mBAAmB,OAAO;AAIxE,iBAAgB,SAAS,eAAe,MAAM,mBAAmB,OAAO;AAIxE,iBAAgB,OAAO,eAAe,OAAO;AAI7C,iBAAgB,OAAO,eAAe,OAAO;AAI7C;;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,iBAAgB,WAAW","names":[]}
1
+ {"version":3,"file":"index.d.mts","sources":["../src/types.ts","../src/goTry.ts","../src/unknown-error.ts","../src/goTryRaw.ts","../src/goTryOr.ts","../src/goTryAll.ts","../src/tagged-error.ts","../src/assert.ts","../src/result-helpers.ts"],"mappings":"AAAA;;;AAIM,KAAM,OAAO;AACb,KAAM,OAAO;AACb,KAAM,MAAM,SAAS,OAAO,MAAM,OAAO;AAEzC,KAAM,iBAAiB;AAEvB,KAAM,YAAY,UAAU,OAAO;AAEzC;;;;AAIM,UAAW,WAAW;;;;;AAMtB,UAAW,eAAe;;;;;;;AAQhC;;;AAGM,KAAM,gBAAgB;;;AAO5B;;;;AAIM,KAAM,eAAe,KAAK,KAAK;gBACnB,gBAAgB;;;;sBACU,gBAAgB;;;;;AAG5D;;;;;;;;;;;;;AAaM,KAAM,WAAW,oBAAoB,gBAAgB;iCAC1B,gBAAgB;;;AC3DjD;;;;;;;;;;;;;;;;;;;;AAoBA,iBAAgB,KAAK,sBAAsB,MAAM;AACjD,iBAAgB,KAAK,cAAc,OAAO,MAAM,OAAO,CAAC,MAAM;AAC9D,iBAAgB,KAAK,aAAa,OAAO,MAAM,OAAO,CAAC,MAAM;AAC7D,iBAAgB,KAAK,kBAAkB,MAAM;AAC7C,iBAAgB,KAAK,eAAe,MAAM;;AC1B1C;;;;;;;;;;;;;;;;;AAiBA,cAAa,YAAY;;;;;;;;;;;;;ACPzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,iBAAgB,QAAQ,sBAAsB,MAAM,CAAC,KAAK;AAC1D,iBAAgB,QAAQ,QAAQ,YAAY,QAAQ,YAAY,6BAA6B,eAAe,MAAM,MAAM;AACxH,iBAAgB,QAAQ,cACZ,OAAO,MAChB,OAAO,CAAC,MAAM,CAAC,KAAK;AACvB,iBAAgB,QAAQ,QAAQ,YAAY,QAAQ,YAAY,aACpD,OAAO,cACR,eAAe,MACvB,OAAO,CAAC,MAAM;AACjB,iBAAgB,QAAQ,aACb,OAAO,MACf,OAAO,CAAC,MAAM,CAAC,KAAK;AACvB,iBAAgB,QAAQ,QAAQ,YAAY,QAAQ,YAAY,YACrD,OAAO,cACP,eAAe,MACvB,OAAO,CAAC,MAAM;AACjB,iBAAgB,QAAQ,kBAAkB,MAAM,CAAC,KAAK;AACtD,iBAAgB,QAAQ,QAAQ,YAAY,QAAQ,YAAY,yBAAyB,eAAe,MAAM,MAAM;AACpH,iBAAgB,QAAQ,eAAe,MAAM,CAAC,KAAK;AACnD,iBAAgB,QAAQ,QAAQ,YAAY,QAAQ,YAAY,sBAAsB,eAAe,MAAM,MAAM;;AC5DjH;;;;;;;;;;;;;;;;;;;;;AAqBA,iBAAgB,OAAO,mDAAmD,iBAAiB;AAC3F,iBAAgB,OAAO,cACX,OAAO,mCAEhB,OAAO,CAAC,iBAAiB;AAC5B,iBAAgB,OAAO,aACZ,OAAO,mCAEf,OAAO,CAAC,iBAAiB;AAC5B,iBAAgB,OAAO,+CAA+C,iBAAiB;AACvF,iBAAgB,OAAO,4CAA4C,iBAAiB;;ACgBpF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,iBAAsB,QAAQ;oBACH,OAAO,gBAAgB,OAAO;aAC7C,eAAe,GACxB,OAAO;;;;;AAoBV;;;;;;;;;;AAUA,iBAAsB,WAAW;oBACN,OAAO,gBAAgB,OAAO;aAC7C,eAAe,GACxB,OAAO;oBAAoB,KAAK;;;;;ACrHnC;;;;;;;;;;;;;;;;;;;;;;;;AAwBA,iBAAgB,WAAW;;;;;;;;;;;;;AC1B3B;;;;;;;;;;;;;;;;;;;;;;AAsBA,iBAAgB,MAAM,4BAA4B,KAAK;AAEvD;;;;;;;;;;;;;;AAcA,iBAAgB,MAAM,WAAW,KAAK;;ACpCtC,iBAAgB,SAAS,eAAe,MAAM,mBAAmB,OAAO;AAIxE,iBAAgB,SAAS,eAAe,MAAM,mBAAmB,OAAO;AAIxE,iBAAgB,OAAO,eAAe,OAAO;AAI7C,iBAAgB,OAAO,eAAe,OAAO;AAI7C;;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,iBAAgB,WAAW","names":[]}
package/dist/index.mjs CHANGED
@@ -48,16 +48,46 @@ function goTry(value) {
48
48
  }
49
49
  }
50
50
 
51
- function goTryRaw(value, ErrorClass) {
51
+ function taggedError(tag) {
52
+ return class TaggedErrorClass extends Error {
53
+ constructor(message, options) {
54
+ super(message);
55
+ this._tag = tag;
56
+ this.name = tag;
57
+ this.cause = options?.cause;
58
+ }
59
+ };
60
+ }
61
+
62
+ const UnknownError = taggedError("UnknownError");
63
+
64
+ function isTaggedError(err) {
65
+ return isError(err) && "_tag" in err && typeof err._tag === "string";
66
+ }
67
+ function goTryRaw(value, options) {
68
+ const { errorClass, systemErrorClass } = options || {};
69
+ const actualSystemErrorClass = systemErrorClass ?? UnknownError;
52
70
  const wrapError = (err) => {
53
- if (ErrorClass) {
71
+ if (errorClass) {
72
+ if (err === void 0) {
73
+ return new errorClass("undefined");
74
+ }
75
+ if (isError(err)) {
76
+ return new errorClass(err.message, { cause: err });
77
+ }
78
+ return new errorClass(String(err));
79
+ }
80
+ if (actualSystemErrorClass) {
81
+ if (isTaggedError(err)) {
82
+ return err;
83
+ }
54
84
  if (err === void 0) {
55
- return new ErrorClass("undefined");
85
+ return new actualSystemErrorClass("undefined");
56
86
  }
57
87
  if (isError(err)) {
58
- return new ErrorClass(err.message, { cause: err });
88
+ return new actualSystemErrorClass(err.message, { cause: err });
59
89
  }
60
- return new ErrorClass(String(err));
90
+ return new actualSystemErrorClass(String(err));
61
91
  }
62
92
  if (err === void 0) {
63
93
  return new Error("undefined");
@@ -151,17 +181,6 @@ async function goTryAllRaw(items, options) {
151
181
  return [errors, results];
152
182
  }
153
183
 
154
- function taggedError(tag) {
155
- return class TaggedErrorClass extends Error {
156
- constructor(message, options) {
157
- super(message);
158
- this._tag = tag;
159
- this.name = tag;
160
- this.cause = options?.cause;
161
- }
162
- };
163
- }
164
-
165
184
  function assert(condition, errorOrClass, message) {
166
185
  if (!condition) {
167
186
  if (typeof errorOrClass === "string") {
@@ -174,4 +193,4 @@ function assert(condition, errorOrClass, message) {
174
193
  }
175
194
  }
176
195
 
177
- export { assert, assertNever, failure, goTry, goTryAll, goTryAllRaw, goTryOr, goTryRaw, isFailure, isSuccess, success, taggedError };
196
+ export { UnknownError, assert, assertNever, failure, goTry, goTryAll, goTryAllRaw, goTryOr, goTryRaw, isFailure, isSuccess, success, taggedError };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "go-go-try",
3
- "version": "7.3.0",
3
+ "version": "7.4.0",
4
4
  "description": "Tries to execute a sync/async function, returns a result tuple",
5
5
  "license": "MIT",
6
6
  "repository": "thelinuxlich/go-go-try",
@@ -48,11 +48,11 @@
48
48
  ],
49
49
  "devDependencies": {
50
50
  "@ark/attest": "^0.56.0",
51
- "@biomejs/biome": "^2.3.14",
51
+ "@biomejs/biome": "^2.4.2",
52
52
  "@vitest/coverage-v8": "^4.0.18",
53
53
  "husky": "^9.1.7",
54
54
  "lint-staged": "^16.2.7",
55
- "pkgroll": "^2.23.0",
55
+ "pkgroll": "^2.26.2",
56
56
  "tsx": "^4.21.0",
57
57
  "typescript": "^5.9.3",
58
58
  "vitest": "^4.0.18"
package/src/goTryRaw.ts CHANGED
@@ -1,15 +1,23 @@
1
- import type { Result, ErrorConstructor } from './types.js'
1
+ import type { Result, GoTryRawOptions } from './types.js'
2
2
  import { success, failure } from './result-helpers.js'
3
3
  import { isPromise, isError } from './internals.js'
4
+ import { UnknownError } from './unknown-error.js'
5
+
6
+ /**
7
+ * Checks if a value is a tagged error (has a _tag property).
8
+ */
9
+ function isTaggedError(err: unknown): err is { _tag: string } {
10
+ return isError(err) && '_tag' in err && typeof (err as { _tag?: unknown })._tag === 'string'
11
+ }
4
12
 
5
13
  /**
6
14
  * Executes a function, promise, or value and returns a Result type.
7
15
  * If an error occurs, it returns a Failure with the raw error object.
8
16
  *
9
17
  * @template T The type of the successful result
10
- * @template E The type of the error, defaults to Error
18
+ * @template E The type of the error
11
19
  * @param {T | Promise<T> | (() => T | Promise<T>)} value - The value, promise, or function to execute
12
- * @param {ErrorConstructor<E>} [ErrorClass] - Optional error constructor to wrap caught errors
20
+ * @param {GoTryRawOptions<E>} [options] - Optional options object
13
21
  * @returns {Result<E, T> | Promise<Result<E, T>>} A Result type or a Promise of a Result type
14
22
  *
15
23
  * @example
@@ -25,47 +33,76 @@ import { isPromise, isError } from './internals.js'
25
33
  * const [err, result] = await goTryRaw(fetch('https://api.example.com/data'));
26
34
  *
27
35
  * @example
28
- * // With tagged error for discriminated unions
36
+ * // With options object - wrap all errors
29
37
  * const DatabaseError = taggedError('DatabaseError');
30
- * const [err, result] = await goTryRaw(fetchData(), DatabaseError);
31
- * // err is InstanceType<typeof DatabaseError> | undefined
38
+ * const [err, result] = await goTryRaw(fetchData(), { errorClass: DatabaseError });
39
+ *
40
+ * @example
41
+ * // With options object - systemErrorClass only wraps non-tagged errors
42
+ * const [err, result] = await goTryRaw(fetchData(), { systemErrorClass: UnknownError });
43
+ * // Errors thrown as tagged errors pass through
44
+ * // Other errors are wrapped in UnknownError
32
45
  */
33
- export function goTryRaw<T, E = Error>(fn: () => never): Result<E, never>
34
- export function goTryRaw<T, E = Error>(fn: () => never, ErrorClass: ErrorConstructor<E>): Result<E, never>
35
- export function goTryRaw<T, E = Error>(
46
+ export function goTryRaw<T>(fn: () => never): Result<Error, never>
47
+ export function goTryRaw<T, E = InstanceType<typeof UnknownError>>(fn: () => never, options: GoTryRawOptions<E>): Result<E, never>
48
+ export function goTryRaw<T>(
36
49
  fn: () => Promise<T>,
37
- ): Promise<Result<E, T>>
38
- export function goTryRaw<T, E = Error>(
50
+ ): Promise<Result<Error, T>>
51
+ export function goTryRaw<T, E = InstanceType<typeof UnknownError>>(
39
52
  fn: () => Promise<T>,
40
- ErrorClass: ErrorConstructor<E>,
53
+ options: GoTryRawOptions<E>,
41
54
  ): Promise<Result<E, T>>
42
- export function goTryRaw<T, E = Error>(
55
+ export function goTryRaw<T>(
43
56
  promise: Promise<T>,
44
- ): Promise<Result<E, T>>
45
- export function goTryRaw<T, E = Error>(
57
+ ): Promise<Result<Error, T>>
58
+ export function goTryRaw<T, E = InstanceType<typeof UnknownError>>(
46
59
  promise: Promise<T>,
47
- ErrorClass: ErrorConstructor<E>,
60
+ options: GoTryRawOptions<E>,
48
61
  ): Promise<Result<E, T>>
49
- export function goTryRaw<T, E = Error>(fn: () => T): Result<E, T>
50
- export function goTryRaw<T, E = Error>(fn: () => T, ErrorClass: ErrorConstructor<E>): Result<E, T>
51
- export function goTryRaw<T, E = Error>(value: T): Result<E, T>
52
- export function goTryRaw<T, E = Error>(value: T, ErrorClass: ErrorConstructor<E>): Result<E, T>
62
+ export function goTryRaw<T>(fn: () => T): Result<Error, T>
63
+ export function goTryRaw<T, E = InstanceType<typeof UnknownError>>(fn: () => T, options: GoTryRawOptions<E>): Result<E, T>
64
+ export function goTryRaw<T>(value: T): Result<Error, T>
65
+ export function goTryRaw<T, E = InstanceType<typeof UnknownError>>(value: T, options: GoTryRawOptions<E>): Result<E, T>
53
66
  export function goTryRaw<T, E = Error>(
54
67
  value: T | Promise<T> | (() => T | Promise<T>),
55
- ErrorClass?: ErrorConstructor<E>,
68
+ options?: GoTryRawOptions<E>,
56
69
  ): Result<E, T> | Promise<Result<E, T>> {
57
- // Helper to wrap error in the provided class or return as-is
70
+ const { errorClass, systemErrorClass } = options || {}
71
+
72
+ // Determine the actual system error class to use
73
+ // Default to UnknownError when systemErrorClass is not specified
74
+ const actualSystemErrorClass = systemErrorClass ?? UnknownError
75
+
76
+ // Helper to wrap error based on the options
58
77
  const wrapError = (err: unknown): E => {
59
- if (ErrorClass) {
78
+ // If errorClass is specified, wrap all errors with it
79
+ if (errorClass) {
60
80
  if (err === undefined) {
61
- return new ErrorClass('undefined')
81
+ return new errorClass('undefined')
62
82
  }
63
83
  if (isError(err)) {
64
- return new ErrorClass(err.message, { cause: err })
84
+ return new errorClass(err.message, { cause: err })
65
85
  }
66
- return new ErrorClass(String(err))
86
+ return new errorClass(String(err))
67
87
  }
68
- // Default behavior: return raw error
88
+
89
+ // If actualSystemErrorClass is specified, only wrap non-tagged errors
90
+ if (actualSystemErrorClass) {
91
+ if (isTaggedError(err)) {
92
+ return err as unknown as E
93
+ }
94
+
95
+ // Wrap non-tagged errors with systemErrorClass
96
+ if (err === undefined) {
97
+ return new actualSystemErrorClass('undefined') as unknown as E
98
+ }
99
+ if (isError(err)) {
100
+ return new actualSystemErrorClass(err.message, { cause: err }) as unknown as E
101
+ }
102
+ return new actualSystemErrorClass(String(err)) as unknown as E
103
+ }
104
+
105
+ // No options - original behavior: return raw error
69
106
  if (err === undefined) {
70
107
  return new Error('undefined') as unknown as E
71
108
  }
package/src/index.test.ts CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  isSuccess,
16
16
  success,
17
17
  taggedError,
18
+ UnknownError,
18
19
  } from './index.js'
19
20
 
20
21
  test(`value returned by callback is used when callback doesn't throw`, async () => {
@@ -319,8 +320,12 @@ describe('edge cases', () => {
319
320
  assert.equal(value1, undefined)
320
321
  assert.equal(value2, undefined)
321
322
  assert.equal(err1, 'custom error')
322
- assert.equal(err2 instanceof CustomError, true)
323
- assert.equal((err2 as CustomError).code, 500)
323
+ // goTryRaw now wraps errors in UnknownError by default
324
+ assert.equal(err2 instanceof UnknownError, true)
325
+ assert.equal((err2 as InstanceType<typeof UnknownError>)?._tag, 'UnknownError')
326
+ assert.equal(err2?.message, 'custom error')
327
+ // Original error is preserved in cause
328
+ assert.equal(((err2 as unknown as { cause?: unknown })?.cause as CustomError)?.code, 500)
324
329
  })
325
330
 
326
331
  test('throwing a string', () => {
@@ -367,38 +372,41 @@ describe('assert helper', () => {
367
372
  })
368
373
 
369
374
  test('throws with string message when condition is false', () => {
375
+ let caught = false
370
376
  try {
371
377
  assertTry(false, 'custom error message')
372
- // Should not reach here
373
- assert.equal(true, false)
374
378
  } catch (err) {
379
+ caught = true
375
380
  assert.ok(err instanceof Error)
376
381
  assert.equal((err as Error).message, 'custom error message')
377
382
  }
383
+ assert.equal(caught, true)
378
384
  })
379
385
 
380
386
  test('throws with Error instance when condition is false', () => {
381
387
  const customError = new Error('custom error instance')
388
+ let caught = false
382
389
  try {
383
390
  assertTry(false, customError)
384
- // Should not reach here
385
- assert.equal(true, false)
386
391
  } catch (err) {
392
+ caught = true
387
393
  assert.equal(err, customError)
388
394
  }
395
+ assert.equal(caught, true)
389
396
  })
390
397
 
391
398
  test('throws with tagged error when condition is false', () => {
392
399
  const DatabaseError = taggedError('DatabaseError')
400
+ let caught = false
393
401
  try {
394
402
  assertTry(false, new DatabaseError('database connection failed'))
395
- // Should not reach here
396
- assert.equal(true, false)
397
403
  } catch (err) {
404
+ caught = true
398
405
  assert.ok(err instanceof DatabaseError)
399
406
  assert.equal((err as InstanceType<typeof DatabaseError>)._tag, 'DatabaseError')
400
407
  assert.equal((err as Error).message, 'database connection failed')
401
408
  }
409
+ assert.equal(caught, true)
402
410
  })
403
411
 
404
412
  test('type narrowing works with Result types using err === undefined', () => {
@@ -434,7 +442,7 @@ describe('assert helper', () => {
434
442
 
435
443
  test('type narrowing works with tagged errors', () => {
436
444
  const DatabaseError = taggedError('DatabaseError')
437
- const [err, user] = goTryRaw(() => ({ id: '123', name: 'John' }), DatabaseError)
445
+ const [err, user] = goTryRaw(() => ({ id: '123', name: 'John' }), { errorClass: DatabaseError })
438
446
 
439
447
  // Before assert
440
448
  attest<InstanceType<typeof DatabaseError> | undefined>(err)
@@ -452,13 +460,13 @@ describe('assert helper', () => {
452
460
  const DatabaseError = taggedError('DatabaseError')
453
461
 
454
462
  function fetchUserOldStyle(): Result<InstanceType<typeof DatabaseError>, { id: string }> {
455
- const [err, user] = goTryRaw(() => ({ id: '123' }), DatabaseError)
463
+ const [err, user] = goTryRaw(() => ({ id: '123' }), { errorClass: DatabaseError })
456
464
  if (err) return failure(err) // Old style
457
465
  return [undefined, user] as const
458
466
  }
459
467
 
460
468
  function fetchUserNewStyle(): Result<InstanceType<typeof DatabaseError>, { id: string }> {
461
- const [err, user] = goTryRaw(() => ({ id: '123' }), DatabaseError)
469
+ const [err, user] = goTryRaw(() => ({ id: '123' }), { errorClass: DatabaseError })
462
470
  assertTry(err === undefined, new DatabaseError('Failed to fetch user'))
463
471
  // TypeScript now knows user is defined
464
472
  return [undefined, user] as const
@@ -526,7 +534,7 @@ describe('assert helper', () => {
526
534
 
527
535
  test('shorter syntax with tagged errors provides type narrowing', () => {
528
536
  const DatabaseError = taggedError('DatabaseError')
529
- const [err, user] = goTryRaw(() => ({ id: '123', name: 'John' }), DatabaseError)
537
+ const [err, user] = goTryRaw(() => ({ id: '123', name: 'John' }), { errorClass: DatabaseError })
530
538
 
531
539
  // Before assert
532
540
  attest<InstanceType<typeof DatabaseError> | undefined>(err)
@@ -1187,8 +1195,8 @@ describe('taggedError', () => {
1187
1195
  }
1188
1196
 
1189
1197
  // Wrap in functions so goTryRaw can catch the errors
1190
- const [dbErr, dbResult] = goTryRaw(fetchFromDb, DatabaseError)
1191
- const [netErr, netResult] = goTryRaw(fetchFromNetwork, NetworkError)
1198
+ const [dbErr, dbResult] = goTryRaw(fetchFromDb, { errorClass: DatabaseError })
1199
+ const [netErr, netResult] = goTryRaw(fetchFromNetwork, { errorClass: NetworkError })
1192
1200
 
1193
1201
  // Type narrowing via discriminated union
1194
1202
  if (dbErr) {
@@ -1214,14 +1222,14 @@ describe('taggedError', () => {
1214
1222
  async function fetchUser(id: string): Promise<Result<AppError, { id: string; name: string }>> {
1215
1223
  const [dbErr, user] = await goTryRaw(
1216
1224
  Promise.resolve({ id, name: 'John' }),
1217
- DatabaseError,
1225
+ { errorClass: DatabaseError },
1218
1226
  )
1219
1227
  if (dbErr) return failure<AppError>(dbErr)
1220
1228
  return [undefined, user] as const
1221
1229
  }
1222
1230
 
1223
1231
  async function fetchData(): Promise<Result<AppError, string>> {
1224
- const [netErr, data] = await goTryRaw(Promise.resolve('data'), NetworkError)
1232
+ const [netErr, data] = await goTryRaw(Promise.resolve('data'), { errorClass: NetworkError })
1225
1233
  if (netErr) return failure<AppError>(netErr)
1226
1234
  return [undefined, data] as const
1227
1235
  }
@@ -1374,7 +1382,7 @@ describe('TaggedUnion type helper', () => {
1374
1382
  async function fetchData(): Promise<Result<AppError, string>> {
1375
1383
  const [err, data] = await goTryRaw(
1376
1384
  Promise.reject(new Error('timeout')),
1377
- NetworkError,
1385
+ { errorClass: NetworkError },
1378
1386
  )
1379
1387
  if (err) return failure<AppError>(err)
1380
1388
  return [undefined, data] as const
@@ -1398,14 +1406,14 @@ describe('inferred return types with tagged errors', () => {
1398
1406
  // First operation might fail with DatabaseError
1399
1407
  const [dbErr, user] = await goTryRaw(
1400
1408
  Promise.resolve({ id, name: 'John' }),
1401
- DatabaseError,
1409
+ { errorClass: DatabaseError },
1402
1410
  )
1403
1411
  if (dbErr) return failure(dbErr)
1404
1412
 
1405
1413
  // Second operation might fail with NetworkError
1406
1414
  const [netErr, enriched] = await goTryRaw(
1407
1415
  Promise.resolve({ ...user!, email: 'john@example.com' }),
1408
- NetworkError,
1416
+ { errorClass: NetworkError },
1409
1417
  )
1410
1418
  if (netErr) return failure(netErr)
1411
1419
 
@@ -1443,14 +1451,14 @@ describe('inferred return types with tagged errors', () => {
1443
1451
  // No explicit return type annotation
1444
1452
  function processConfig(input: string) {
1445
1453
  // Parse step
1446
- const [parseErr, parsed] = goTryRaw(() => JSON.parse(input), ParseError)
1454
+ const [parseErr, parsed] = goTryRaw(() => JSON.parse(input), { errorClass: ParseError })
1447
1455
  if (parseErr) return failure(parseErr)
1448
1456
 
1449
1457
  // Validate step
1450
1458
  const [validateErr, validated] = goTryRaw(() => {
1451
1459
  if (!parsed!.port) throw new Error('Missing port')
1452
1460
  return parsed as { port: number }
1453
- }, ValidateError)
1461
+ }, { errorClass: ValidateError })
1454
1462
  if (validateErr) return failure(validateErr)
1455
1463
 
1456
1464
  return [undefined, validated] as const
@@ -1481,19 +1489,19 @@ describe('inferred return types with tagged errors', () => {
1481
1489
  async function complexOperation(shouldFail: 'a' | 'b' | 'c' | 'none') {
1482
1490
  const [errA, valA] = await goTryRaw(
1483
1491
  shouldFail === 'a' ? Promise.reject(new Error('a')) : Promise.resolve('step1'),
1484
- ErrorA,
1492
+ { errorClass: ErrorA },
1485
1493
  )
1486
1494
  if (errA) return failure(errA)
1487
1495
 
1488
1496
  const [errB, valB] = await goTryRaw(
1489
1497
  shouldFail === 'b' ? Promise.reject(new Error('b')) : Promise.resolve('step2'),
1490
- ErrorB,
1498
+ { errorClass: ErrorB },
1491
1499
  )
1492
1500
  if (errB) return failure(errB)
1493
1501
 
1494
1502
  const [errC, valC] = await goTryRaw(
1495
1503
  shouldFail === 'c' ? Promise.reject(new Error('c')) : Promise.resolve('step3'),
1496
- ErrorC,
1504
+ { errorClass: ErrorC },
1497
1505
  )
1498
1506
  if (errC) return failure(errC)
1499
1507
 
@@ -1530,3 +1538,204 @@ describe('inferred return types with tagged errors', () => {
1530
1538
  )
1531
1539
  })
1532
1540
  })
1541
+
1542
+
1543
+ describe('UnknownError', () => {
1544
+ test('UnknownError is exported as a tagged error class', () => {
1545
+ const err = new UnknownError('something went wrong')
1546
+ assert.equal(err._tag, 'UnknownError')
1547
+ assert.equal(err.message, 'something went wrong')
1548
+ assert.equal(err.name, 'UnknownError')
1549
+ assert.ok(err instanceof Error)
1550
+ assert.ok(err instanceof UnknownError)
1551
+ })
1552
+
1553
+ test('UnknownError supports cause option', () => {
1554
+ const cause = new Error('original error')
1555
+ const err = new UnknownError('wrapped error', { cause })
1556
+ assert.equal(err.cause, cause)
1557
+ })
1558
+
1559
+ test('goTryRaw defaults to UnknownError for system errors', () => {
1560
+ const fn = () => {
1561
+ throw new Error('system error')
1562
+ }
1563
+
1564
+ const [err, value] = goTryRaw(fn)
1565
+
1566
+ assert.equal(value, undefined)
1567
+ assert.ok(err instanceof UnknownError)
1568
+ assert.equal(err._tag, 'UnknownError')
1569
+ assert.equal(err.message, 'system error')
1570
+ })
1571
+
1572
+ test('goTryRaw defaults to UnknownError for thrown strings', () => {
1573
+ const fn = () => {
1574
+ throw 'string error'
1575
+ }
1576
+
1577
+ const [err, value] = goTryRaw(fn)
1578
+
1579
+ assert.equal(value, undefined)
1580
+ assert.ok(err instanceof UnknownError)
1581
+ assert.equal(err._tag, 'UnknownError')
1582
+ assert.equal(err.message, 'string error')
1583
+ })
1584
+
1585
+ test('goTryRaw defaults to UnknownError for thrown undefined', () => {
1586
+ const fn = () => {
1587
+ throw undefined
1588
+ }
1589
+
1590
+ const [err, value] = goTryRaw(fn)
1591
+
1592
+ assert.equal(value, undefined)
1593
+ assert.ok(err instanceof UnknownError)
1594
+ assert.equal(err._tag, 'UnknownError')
1595
+ assert.equal(err.message, 'undefined')
1596
+ })
1597
+
1598
+ test('goTryRaw with async defaults to UnknownError', async () => {
1599
+ const promise = Promise.reject(new Error('async error'))
1600
+
1601
+ const [err, value] = await goTryRaw(promise)
1602
+
1603
+ assert.equal(value, undefined)
1604
+ assert.ok(err instanceof UnknownError)
1605
+ assert.equal(err._tag, 'UnknownError')
1606
+ assert.equal(err.message, 'async error')
1607
+ })
1608
+ })
1609
+
1610
+ describe('goTryRaw with options object', () => {
1611
+ test('errorClass wraps all errors including tagged ones', () => {
1612
+ const DatabaseError = taggedError('DatabaseError')
1613
+ const NetworkError = taggedError('NetworkError')
1614
+
1615
+ // When a DatabaseError is thrown, it gets wrapped in NetworkError
1616
+ const fn = () => {
1617
+ throw new DatabaseError('db connection failed')
1618
+ }
1619
+
1620
+ const [err, value] = goTryRaw(fn, { errorClass: NetworkError })
1621
+
1622
+ assert.equal(value, undefined)
1623
+ assert.ok(err instanceof NetworkError)
1624
+ assert.equal(err._tag, 'NetworkError')
1625
+ assert.equal(err.message, 'db connection failed')
1626
+ // Original error is preserved in cause
1627
+ assert.ok(err.cause instanceof DatabaseError)
1628
+ })
1629
+
1630
+ test('errorClass wraps non-tagged errors', () => {
1631
+ const NetworkError = taggedError('NetworkError')
1632
+
1633
+ const fn = () => {
1634
+ throw new Error('plain error')
1635
+ }
1636
+
1637
+ const [err, value] = goTryRaw(fn, { errorClass: NetworkError })
1638
+
1639
+ assert.equal(value, undefined)
1640
+ assert.ok(err instanceof NetworkError)
1641
+ assert.equal(err._tag, 'NetworkError')
1642
+ assert.equal(err.message, 'plain error')
1643
+ })
1644
+
1645
+ test('systemErrorClass only wraps non-tagged errors', () => {
1646
+ const DatabaseError = taggedError('DatabaseError')
1647
+ const SystemError = taggedError('SystemError')
1648
+
1649
+ // Tagged errors should pass through
1650
+ const fnTagged = () => {
1651
+ throw new DatabaseError('db error')
1652
+ }
1653
+
1654
+ const [err1, value1] = goTryRaw(fnTagged, { systemErrorClass: SystemError })
1655
+
1656
+ assert.equal(value1, undefined)
1657
+ assert.ok(err1 instanceof DatabaseError)
1658
+ assert.equal(err1._tag, 'DatabaseError')
1659
+
1660
+ // Non-tagged errors should be wrapped
1661
+ const fnPlain = () => {
1662
+ throw new Error('system error')
1663
+ }
1664
+
1665
+ const [err2, value2] = goTryRaw(fnPlain, { systemErrorClass: SystemError })
1666
+
1667
+ assert.equal(value2, undefined)
1668
+ assert.ok(err2 instanceof SystemError)
1669
+ assert.equal(err2._tag, 'SystemError')
1670
+ assert.equal(err2.message, 'system error')
1671
+ })
1672
+
1673
+ test('systemErrorClass defaults to UnknownError when not specified', () => {
1674
+ const fn = () => {
1675
+ throw new Error('plain error')
1676
+ }
1677
+
1678
+ const [err, value] = goTryRaw(fn, {})
1679
+
1680
+ assert.equal(value, undefined)
1681
+ assert.ok(err instanceof UnknownError)
1682
+ assert.equal(err._tag, 'UnknownError')
1683
+ })
1684
+
1685
+ test('async with options object', async () => {
1686
+ const DatabaseError = taggedError('DatabaseError')
1687
+
1688
+ const promise = Promise.reject(new Error('async error'))
1689
+
1690
+ const [err, value] = await goTryRaw(promise, { errorClass: DatabaseError })
1691
+
1692
+ assert.equal(value, undefined)
1693
+ assert.ok(err instanceof DatabaseError)
1694
+ assert.equal(err._tag, 'DatabaseError')
1695
+ })
1696
+
1697
+ })
1698
+
1699
+ describe('goTryRaw options type tests', () => {
1700
+ test('systemErrorClass preserves tagged errors', () => {
1701
+ const DatabaseError = taggedError('DatabaseError')
1702
+ const SystemError = taggedError('SystemError')
1703
+
1704
+ // Wrap in a function so goTryRaw can catch the error
1705
+ const [err, _value] = goTryRaw(() => {
1706
+ throw new DatabaseError('db error')
1707
+ }, { systemErrorClass: SystemError })
1708
+
1709
+ // Type is systemErrorClass since TypeScript cannot know which tagged errors
1710
+ // might be thrown at runtime (tagged errors pass through, others get wrapped)
1711
+ attest<InstanceType<typeof SystemError> | undefined>(err)
1712
+
1713
+ // But at runtime, tagged errors are preserved
1714
+ if (err) {
1715
+ assert.equal(err._tag, 'DatabaseError')
1716
+ }
1717
+ })
1718
+
1719
+ test('errorClass wraps all errors to specified type', () => {
1720
+ const DatabaseError = taggedError('DatabaseError')
1721
+
1722
+ const [err, value] = goTryRaw(() => 'test', { errorClass: DatabaseError })
1723
+
1724
+ attest<InstanceType<typeof DatabaseError> | undefined>(err)
1725
+ attest<string | undefined>(value)
1726
+ })
1727
+
1728
+ test('no options defaults to Error type (backward compatible)', () => {
1729
+ const [err, value] = goTryRaw(() => 'test')
1730
+
1731
+ attest<Error | undefined>(err)
1732
+ attest<string | undefined>(value)
1733
+ })
1734
+
1735
+ test('empty options object defaults to UnknownError type', () => {
1736
+ const [err, value] = goTryRaw(() => 'test', {})
1737
+
1738
+ attest<InstanceType<typeof UnknownError> | undefined>(err)
1739
+ attest<string | undefined>(value)
1740
+ })
1741
+ })
package/src/index.ts CHANGED
@@ -9,6 +9,7 @@ export type {
9
9
  GoTryAllOptions,
10
10
  ErrorConstructor,
11
11
  TaggedUnion,
12
+ GoTryRawOptions,
12
13
  } from './types.js'
13
14
 
14
15
  // Export core functions
@@ -21,3 +22,6 @@ export { goTryAll, goTryAllRaw } from './goTryAll.js'
21
22
  export { taggedError } from './tagged-error.js'
22
23
  export { assert } from './assert.js'
23
24
  export { isSuccess, isFailure, success, failure, assertNever } from './result-helpers.js'
25
+
26
+ // Export UnknownError tagged error
27
+ export { UnknownError } from './unknown-error.js'
package/src/types.ts CHANGED
@@ -33,6 +33,20 @@ export interface GoTryAllOptions {
33
33
  */
34
34
  export type ErrorConstructor<E> = new (message: string, options?: { cause?: unknown }) => E
35
35
 
36
+ /**
37
+ * Checks if a value is a tagged error (has a _tag property).
38
+ */
39
+ export type IsTaggedError<T> = T extends { _tag: string } ? true : false
40
+
41
+ /**
42
+ * Options for goTryRaw function.
43
+ * errorClass and systemErrorClass are mutually exclusive - you can only provide one.
44
+ */
45
+ export type GoTryRawOptions<E = Error> =
46
+ | { errorClass: ErrorConstructor<E>; systemErrorClass?: never }
47
+ | { errorClass?: never; systemErrorClass: ErrorConstructor<E> }
48
+ | { errorClass?: never; systemErrorClass?: never }
49
+
36
50
  /**
37
51
  * Creates a union type from multiple tagged error classes.
38
52
  *
@@ -0,0 +1,20 @@
1
+ import { taggedError } from './tagged-error.js'
2
+
3
+ /**
4
+ * Default system error class for errors that aren't already wrapped in a tagged error class.
5
+ *
6
+ * @example
7
+ * // By default, goTryRaw wraps unknown errors in UnknownError
8
+ * const [err, result] = goTryRaw(() => mightThrow())
9
+ * if (err) {
10
+ * console.log(err._tag) // 'UnknownError'
11
+ * }
12
+ *
13
+ * @example
14
+ * // Use a custom system error class
15
+ * const SystemError = taggedError('SystemError')
16
+ * const [err, result] = goTryRaw(() => mightThrow(), {
17
+ * systemErrorClass: SystemError
18
+ * })
19
+ */
20
+ export const UnknownError = taggedError('UnknownError')