go-go-try 7.4.0 → 8.0.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.
@@ -1 +1 @@
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":[]}
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/ensure.ts","../src/result-helpers.ts","../src/goElse.ts"],"mappings":"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","names":["ResultWithDefault","ErrorConstructor"]}
package/dist/index.mjs CHANGED
@@ -26,7 +26,7 @@ function getErrorMessage(error) {
26
26
  return String(error);
27
27
  }
28
28
  }
29
- function isPromise(value) {
29
+ function isPromise$1(value) {
30
30
  return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
31
31
  }
32
32
  function isError(value) {
@@ -39,7 +39,7 @@ function resolveDefault(defaultValue) {
39
39
  function goTry(value) {
40
40
  try {
41
41
  const result = typeof value === "function" ? value() : value;
42
- if (isPromise(result)) {
42
+ if (isPromise$1(result)) {
43
43
  return result.then((resolvedValue) => success(resolvedValue)).catch((err) => failure(getErrorMessage(err)));
44
44
  }
45
45
  return success(result);
@@ -61,7 +61,7 @@ function taggedError(tag) {
61
61
 
62
62
  const UnknownError = taggedError("UnknownError");
63
63
 
64
- function isTaggedError(err) {
64
+ function isTaggedError$1(err) {
65
65
  return isError(err) && "_tag" in err && typeof err._tag === "string";
66
66
  }
67
67
  function goTryRaw(value, options) {
@@ -78,7 +78,7 @@ function goTryRaw(value, options) {
78
78
  return new errorClass(String(err));
79
79
  }
80
80
  if (actualSystemErrorClass) {
81
- if (isTaggedError(err)) {
81
+ if (isTaggedError$1(err)) {
82
82
  return err;
83
83
  }
84
84
  if (err === void 0) {
@@ -96,7 +96,7 @@ function goTryRaw(value, options) {
96
96
  };
97
97
  try {
98
98
  const result = typeof value === "function" ? value() : value;
99
- if (isPromise(result)) {
99
+ if (isPromise$1(result)) {
100
100
  return result.then((resolvedValue) => success(resolvedValue)).catch((err) => failure(wrapError(err)));
101
101
  }
102
102
  return success(result);
@@ -108,7 +108,7 @@ function goTryRaw(value, options) {
108
108
  function goTryOr(value, defaultValue) {
109
109
  try {
110
110
  const result = typeof value === "function" ? value() : value;
111
- if (isPromise(result)) {
111
+ if (isPromise$1(result)) {
112
112
  return result.then((resolvedValue) => success(resolvedValue)).catch((err) => [getErrorMessage(err), resolveDefault(defaultValue)]);
113
113
  }
114
114
  return success(result);
@@ -117,6 +117,31 @@ function goTryOr(value, defaultValue) {
117
117
  }
118
118
  }
119
119
 
120
+ function isTaggedError(err) {
121
+ return isError(err) && "_tag" in err && typeof err._tag === "string";
122
+ }
123
+ function wrapError(err, errorClass, systemErrorClass) {
124
+ if (errorClass) {
125
+ if (err === void 0) {
126
+ return new errorClass("undefined");
127
+ }
128
+ if (isError(err)) {
129
+ return new errorClass(err.message, { cause: err });
130
+ }
131
+ return new errorClass(String(err));
132
+ }
133
+ const actualSystemErrorClass = systemErrorClass ?? UnknownError;
134
+ if (isTaggedError(err)) {
135
+ return err;
136
+ }
137
+ if (err === void 0) {
138
+ return new actualSystemErrorClass("undefined");
139
+ }
140
+ if (isError(err)) {
141
+ return new actualSystemErrorClass(err.message, { cause: err });
142
+ }
143
+ return new actualSystemErrorClass(String(err));
144
+ }
120
145
  async function runWithConcurrency(items, concurrency) {
121
146
  if (items.length === 0) {
122
147
  return [];
@@ -164,6 +189,7 @@ async function goTryAll(items, options) {
164
189
  return [errors, results];
165
190
  }
166
191
  async function goTryAllRaw(items, options) {
192
+ const { errorClass, systemErrorClass } = options || {};
167
193
  const settled = await runWithConcurrency(items, options?.concurrency ?? 0);
168
194
  const errors = [];
169
195
  const results = [];
@@ -174,7 +200,7 @@ async function goTryAllRaw(items, options) {
174
200
  results[i] = item.value;
175
201
  } else {
176
202
  const reason = item.reason;
177
- errors[i] = isError(reason) ? reason : new Error(String(reason));
203
+ errors[i] = wrapError(reason, errorClass, systemErrorClass);
178
204
  results[i] = void 0;
179
205
  }
180
206
  }
@@ -193,4 +219,62 @@ function assert(condition, errorOrClass, message) {
193
219
  }
194
220
  }
195
221
 
196
- export { UnknownError, assert, assertNever, failure, goTry, goTryAll, goTryAllRaw, goTryOr, goTryRaw, isFailure, isSuccess, success, taggedError };
222
+ function isPromise(value) {
223
+ return value instanceof Promise;
224
+ }
225
+ function isErrorConstructor(error) {
226
+ return typeof error === "function" && error.prototype !== void 0 && error.prototype instanceof Error;
227
+ }
228
+ function throwError(value, error) {
229
+ if (isErrorConstructor(error)) {
230
+ throw new error(String(value), { cause: value });
231
+ }
232
+ throw error(value);
233
+ }
234
+ function ensure(value, predicate, error = UnknownError) {
235
+ if (typeof value === "function") {
236
+ const result = value();
237
+ if (isPromise(result)) {
238
+ return result.then((resolved) => {
239
+ if (!predicate(resolved)) {
240
+ throwError(resolved, error);
241
+ }
242
+ return resolved;
243
+ });
244
+ }
245
+ if (!predicate(result)) {
246
+ throwError(result, error);
247
+ }
248
+ return result;
249
+ }
250
+ if (isPromise(value)) {
251
+ return value.then((resolved) => {
252
+ if (!predicate(resolved)) {
253
+ throwError(resolved, error);
254
+ }
255
+ return resolved;
256
+ });
257
+ }
258
+ if (!predicate(value)) {
259
+ throwError(value, error);
260
+ }
261
+ return value;
262
+ }
263
+
264
+ function goElse(value, defaultValue) {
265
+ try {
266
+ const result = typeof value === "function" ? value() : value;
267
+ if (isPromise$1(result)) {
268
+ return result.then((resolvedValue) => [void 0, resolvedValue]).catch((err) => {
269
+ const error = err instanceof Error ? err : new Error(String(err));
270
+ return [error, resolveDefault(defaultValue)];
271
+ });
272
+ }
273
+ return [void 0, result];
274
+ } catch (err) {
275
+ const error = err instanceof Error ? err : new Error(String(err));
276
+ return [error, resolveDefault(defaultValue)];
277
+ }
278
+ }
279
+
280
+ export { UnknownError, assert, assertNever, ensure, failure, goTryRaw as go, goTryAllRaw as goAll, goElse, 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.4.0",
3
+ "version": "8.0.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",
@@ -0,0 +1,116 @@
1
+ import { describe, test, expect } from 'vitest'
2
+ import { ensure } from './ensure.js'
3
+ import { taggedError } from './tagged-error.js'
4
+ import { UnknownError } from './unknown-error.js'
5
+
6
+ describe('ensure', () => {
7
+ test('returns value when predicate passes', () => {
8
+ const result = ensure(42, (n) => n > 0, () => new Error('negative'))
9
+ expect(result).toBe(42)
10
+ })
11
+
12
+ test('uses UnknownError when error parameter is omitted', () => {
13
+ expect(() => ensure(-1, (n) => n > 0)).toThrow(UnknownError)
14
+ })
15
+
16
+ test('uses UnknownError with async when error parameter is omitted', async () => {
17
+ await expect(ensure(Promise.resolve(-1), (n) => n > 0)).rejects.toThrow(UnknownError)
18
+ })
19
+
20
+ test('returns value when predicate passes with error class', () => {
21
+ const MyError = taggedError('MyError')
22
+ const result = ensure(42, (n) => n > 0, MyError)
23
+ expect(result).toBe(42)
24
+ })
25
+
26
+ test('returns value with complex predicate', () => {
27
+ const result = ensure([1, 2, 3], (arr) => arr.length > 0, () => new Error('empty'))
28
+ expect(result).toEqual([1, 2, 3])
29
+ })
30
+
31
+ test('throws error when predicate fails', () => {
32
+ expect(() => ensure(-1, (n) => n > 0, (n) => new Error(`${n} is negative`))).toThrow('-1 is negative')
33
+ })
34
+
35
+ test('throws with access to value in error factory', () => {
36
+ const obj = { status: 404 }
37
+ expect(() =>
38
+ ensure(
39
+ obj,
40
+ (o) => o.status === 200,
41
+ (o) => new Error(`HTTP ${o.status}`)
42
+ )
43
+ ).toThrow('HTTP 404')
44
+ })
45
+
46
+ test('throws with error class constructor', () => {
47
+ const RequestFailed = taggedError('RequestFailed')
48
+ const obj = { status: 404 }
49
+ expect(() =>
50
+ ensure(
51
+ obj,
52
+ (o: { status: number }) => o.status === 200,
53
+ RequestFailed
54
+ )
55
+ ).toThrow()
56
+ try {
57
+ ensure(obj, (o: { status: number }) => o.status === 200, RequestFailed)
58
+ } catch (e) {
59
+ expect(e).toBeInstanceOf(RequestFailed)
60
+ expect((e as Error & { cause?: unknown }).cause).toBe(obj)
61
+ }
62
+ })
63
+
64
+ test('narrows type when predicate is type guard', () => {
65
+ interface User {
66
+ name: string
67
+ }
68
+ const maybeUser: unknown = { name: 'Alice' }
69
+
70
+ const user = ensure(
71
+ maybeUser as unknown,
72
+ (u): u is User => typeof u === 'object' && u !== null && 'name' in u,
73
+ () => new Error('Not a user')
74
+ )
75
+
76
+ // TypeScript knows this is User, not unknown
77
+ expect((user as User).name).toBe('Alice')
78
+ })
79
+
80
+ test('works with async values (promise passed directly)', async () => {
81
+ const result = await ensure(Promise.resolve(42), (n) => n > 0, () => new Error('negative'))
82
+ expect(result).toBe(42)
83
+ })
84
+
85
+ test('throws with async values when predicate fails', async () => {
86
+ await expect(
87
+ ensure(Promise.resolve(-1), (n) => n > 0, (n) => new Error(`${n} is negative`))
88
+ ).rejects.toThrow('-1 is negative')
89
+ })
90
+
91
+ test('works with sync functions', () => {
92
+ const result = ensure(() => 42, (n) => n > 0, () => new Error('negative'))
93
+ expect(result).toBe(42)
94
+ })
95
+
96
+ test('throws with sync functions when predicate fails', () => {
97
+ expect(() =>
98
+ ensure(() => -1, (n) => n > 0, (n) => new Error(`${n} is negative`))
99
+ ).toThrow('-1 is negative')
100
+ })
101
+
102
+ test('works with async functions', async () => {
103
+ const result = await ensure(
104
+ () => Promise.resolve(42),
105
+ (n) => n > 0,
106
+ () => new Error('negative')
107
+ )
108
+ expect(result).toBe(42)
109
+ })
110
+
111
+ test('throws with async functions when predicate fails', async () => {
112
+ await expect(
113
+ ensure(() => Promise.resolve(-1), (n) => n > 0, (n) => new Error(`${n} is negative`))
114
+ ).rejects.toThrow('-1 is negative')
115
+ })
116
+ })
package/src/ensure.ts ADDED
@@ -0,0 +1,137 @@
1
+ import { UnknownError } from './unknown-error.js'
2
+
3
+ /**
4
+ * Error class constructor type.
5
+ */
6
+ type ErrorConstructor<E extends Error = Error> = new (
7
+ message: string,
8
+ options?: { cause?: unknown },
9
+ ) => E
10
+
11
+ /**
12
+ * Helper to check if value is a Promise.
13
+ */
14
+ function isPromise<T>(value: unknown): value is Promise<T> {
15
+ return value instanceof Promise
16
+ }
17
+
18
+ /**
19
+ * Type guard to check if error is an Error class constructor.
20
+ */
21
+ function isErrorConstructor<E extends Error>(
22
+ error: ErrorConstructor<E> | ((value: unknown) => E) | ((value: never) => E),
23
+ ): error is ErrorConstructor<E> {
24
+ return (
25
+ typeof error === 'function' &&
26
+ error.prototype !== undefined &&
27
+ error.prototype instanceof Error
28
+ )
29
+ }
30
+
31
+ /**
32
+ * Helper to throw error from class or factory.
33
+ */
34
+ function throwError<T, E extends Error>(
35
+ value: T,
36
+ error: ErrorConstructor<E> | ((value: never) => E),
37
+ ): never {
38
+ if (isErrorConstructor(error)) {
39
+ // It's an Error class constructor
40
+ throw new error(String(value), { cause: value })
41
+ }
42
+ // It's a factory function - cast to any to handle type variance
43
+ throw (error as (value: T) => E)(value)
44
+ }
45
+
46
+ /**
47
+ * Ensures a value satisfies a predicate, throwing an error if not.
48
+ * Returns the value if the predicate passes.
49
+ *
50
+ * Accepts sync values, promises, or functions - just like `go`.
51
+ *
52
+ * The error can be either:
53
+ * - An Error class constructor (instantiated with the value as cause)
54
+ * - A function that creates and returns an Error
55
+ * - If omitted, defaults to UnknownError
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * // With sync value (uses UnknownError by default)
60
+ * ensure(42, n => n > 0)
61
+ *
62
+ * // With promise (awaited internally)
63
+ * const res = await ensure(fetch('/api'), r => r.ok, RequestFailedError)
64
+ *
65
+ * // With function
66
+ * const res = ensure(() => parseInt('42'), n => !isNaN(n), Error)
67
+ *
68
+ * // With error factory function
69
+ * const res = ensure(
70
+ * await fetch('/api'),
71
+ * r => r.ok,
72
+ * r => new Error(`HTTP ${r.status}`)
73
+ * )
74
+ * ```
75
+ */
76
+ // Promise overloads first (more specific)
77
+ export function ensure<T>(
78
+ value: Promise<T>,
79
+ predicate: (value: T) => boolean,
80
+ error?: ErrorConstructor<Error> | ((value: T) => Error),
81
+ ): Promise<T>
82
+ // Function returning Promise
83
+ export function ensure<T>(
84
+ fn: () => Promise<T>,
85
+ predicate: (value: T) => boolean,
86
+ error?: ErrorConstructor<Error> | ((value: T) => Error),
87
+ ): Promise<T>
88
+ // Sync function
89
+ export function ensure<T>(
90
+ fn: () => T,
91
+ predicate: (value: T) => boolean,
92
+ error?: ErrorConstructor<Error> | ((value: T) => Error),
93
+ ): T
94
+ // Sync value (catch-all)
95
+ export function ensure<T>(
96
+ value: T,
97
+ predicate: (value: T) => boolean,
98
+ error?: ErrorConstructor<Error> | ((value: T) => Error),
99
+ ): T
100
+ export function ensure<T>(
101
+ value: T | Promise<T> | (() => T | Promise<T>),
102
+ predicate: (value: T) => boolean,
103
+ error: ErrorConstructor<Error> | ((value: T) => Error) = UnknownError,
104
+ ): T | Promise<T> {
105
+ // Handle function
106
+ if (typeof value === 'function') {
107
+ const result = (value as () => T | Promise<T>)()
108
+ if (isPromise<T>(result)) {
109
+ return result.then((resolved) => {
110
+ if (!predicate(resolved)) {
111
+ throwError(resolved, error)
112
+ }
113
+ return resolved
114
+ })
115
+ }
116
+ if (!predicate(result)) {
117
+ throwError(result, error)
118
+ }
119
+ return result
120
+ }
121
+
122
+ // Handle promise
123
+ if (isPromise<T>(value)) {
124
+ return value.then((resolved) => {
125
+ if (!predicate(resolved)) {
126
+ throwError(resolved, error)
127
+ }
128
+ return resolved
129
+ })
130
+ }
131
+
132
+ // Handle sync value
133
+ if (!predicate(value)) {
134
+ throwError(value, error)
135
+ }
136
+ return value
137
+ }
package/src/go.test.ts ADDED
@@ -0,0 +1,107 @@
1
+ import { describe, test, expect } from 'vitest'
2
+ import { go, goAll, goElse } from './index.js'
3
+
4
+ describe('go (alias for goTryRaw)', () => {
5
+ test('returns value on success', async () => {
6
+ const result = await go(Promise.resolve(42))
7
+ expect(result).toEqual([undefined, 42])
8
+ })
9
+
10
+ test('returns error on failure', async () => {
11
+ const error = new Error('oops')
12
+ const result = await go(Promise.reject(error))
13
+ // goTryRaw wraps errors in UnknownError by default
14
+ expect(result[0]).toBeInstanceOf(Error)
15
+ expect(result[0]?.message).toBe('oops')
16
+ expect(result[1]).toBeUndefined()
17
+ })
18
+
19
+ test('works with non-promise values', async () => {
20
+ const result = await go(42)
21
+ expect(result).toEqual([undefined, 42])
22
+ })
23
+
24
+ test('works with sync functions', async () => {
25
+ const result = await go(() => 'hello')
26
+ expect(result).toEqual([undefined, 'hello'])
27
+ })
28
+
29
+ test('catches sync errors', async () => {
30
+ const result = await go(() => {
31
+ throw new Error('sync error')
32
+ })
33
+ expect(result[0]?.message).toBe('sync error')
34
+ expect(result[1]).toBeUndefined()
35
+ })
36
+
37
+ test('is exported from index', () => {
38
+ // Verified by the import at the top of the file
39
+ expect(go).toBeDefined()
40
+ })
41
+ })
42
+
43
+ describe('goAll (alias for goTryAllRaw)', () => {
44
+ test('returns all results', async () => {
45
+ const [errors, results] = await goAll([
46
+ () => Promise.resolve(1),
47
+ () => Promise.resolve(2),
48
+ () => Promise.resolve(3),
49
+ ])
50
+ expect(errors).toEqual([undefined, undefined, undefined])
51
+ expect(results).toEqual([1, 2, 3])
52
+ })
53
+
54
+ test('handles mixed success and failure', async () => {
55
+ const [errors, results] = await goAll([
56
+ () => Promise.resolve(1),
57
+ () => Promise.reject(new Error('failed')),
58
+ () => Promise.resolve(3),
59
+ ])
60
+ expect(errors[0]).toBeUndefined()
61
+ expect(errors[1]).toBeInstanceOf(Error)
62
+ expect(errors[2]).toBeUndefined()
63
+ expect(results).toEqual([1, undefined, 3])
64
+ })
65
+
66
+ test('is exported from index', () => {
67
+ expect(goAll).toBeDefined()
68
+ })
69
+ })
70
+
71
+ describe('goElse', () => {
72
+ test('returns value on success', async () => {
73
+ const [err, value] = await goElse(Promise.resolve(42), 0)
74
+ expect(err).toBeUndefined()
75
+ expect(value).toBe(42)
76
+ })
77
+
78
+ test('returns default on failure with Error object', async () => {
79
+ const originalError = new Error('oops')
80
+ const [err, value] = await goElse(() => Promise.reject(originalError), 'default')
81
+ expect(err).toBeInstanceOf(Error)
82
+ expect(err?.message).toBe('oops')
83
+ expect(value).toBe('default')
84
+ })
85
+
86
+ test('returns default on sync error with Error object', async () => {
87
+ const [err, value] = await goElse(() => {
88
+ throw new Error('sync error')
89
+ }, 'fallback')
90
+ expect(err).toBeInstanceOf(Error)
91
+ expect(err?.message).toBe('sync error')
92
+ expect(value).toBe('fallback')
93
+ })
94
+
95
+ test('wraps non-Error throws in Error', async () => {
96
+ const [err, value] = await goElse(() => {
97
+ throw 'string error'
98
+ }, 'fallback')
99
+ expect(err).toBeInstanceOf(Error)
100
+ expect(err?.message).toBe('string error')
101
+ expect(value).toBe('fallback')
102
+ })
103
+
104
+ test('is exported from index', () => {
105
+ expect(goElse).toBeDefined()
106
+ })
107
+ })
package/src/go.ts ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Alias for goTryRaw - the most common usage of go-go-try.
3
+ *
4
+ * @example
5
+ * ```typescript
6
+ * const [err, data] = await go(fetch('/api'))
7
+ * const [err2, result] = await go(() => computeSomething())
8
+ * ```
9
+ */
10
+ export { goTryRaw as go } from './goTryRaw.js'
11
+
12
+ /**
13
+ * Alias for goTryAllRaw - run multiple operations in parallel.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const results = await goAll([
18
+ * fetch('/api/users'),
19
+ * fetch('/api/posts'),
20
+ * fetch('/api/comments')
21
+ * ])
22
+ * // results is [Result<Error, Response>, Result<Error, Response>, Result<Error, Response>]
23
+ * ```
24
+ */
25
+ export { goTryAllRaw as goAll } from './goTryAll.js'
26
+
27
+ /**
28
+ * Alias for goElse - returns a default value on error.
29
+ * Returns the actual Error object (not just message) on failure.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const [err, data] = await goElse(fetch('/api'), { users: [] })
34
+ * // err is Error | undefined, data is the response or { users: [] }
35
+ * ```
36
+ */
37
+ export { goElse } from './goElse.js'
package/src/goElse.ts ADDED
@@ -0,0 +1,74 @@
1
+ import { isPromise, resolveDefault } from './internals.js'
2
+
3
+ /**
4
+ * Result type with Error and a default value.
5
+ * On error, returns [Error, DefaultT]
6
+ * On success, returns [undefined, T]
7
+ */
8
+ export type ResultWithDefault<E, T, D = T> = readonly [E, D] | readonly [undefined, T]
9
+
10
+ /**
11
+ * Executes a function, promise, or value and returns a Result type with a fallback default.
12
+ * If an error occurs, it returns the actual Error object and the default value.
13
+ *
14
+ * @template T The type of the successful result
15
+ * @template D The type of the default value (defaults to T)
16
+ * @param {T | Promise<T> | (() => T | Promise<T>)} value - The value, promise, or function to execute
17
+ * @param {D | (() => D)} defaultValue - The default value or a function to compute it (only called on failure)
18
+ * @returns {ResultWithDefault<Error, T, D> | Promise<ResultWithDefault<Error, T, D>>} A tuple of [error, value] or Promise thereof
19
+ *
20
+ * @example
21
+ * // With a static default
22
+ * const [err, config] = goElse(() => JSON.parse('invalid'), { port: 3000 })
23
+ * // err is the Error object, config is { port: 3000 }
24
+ *
25
+ * @example
26
+ * // With a computed default (lazy evaluation)
27
+ * const [err, user] = await goElse(fetchUser(id), () => ({
28
+ * id: 'anonymous',
29
+ * name: 'Guest'
30
+ * }))
31
+ */
32
+ export function goElse<T, D = T>(
33
+ fn: () => never,
34
+ defaultValue: D | (() => D),
35
+ ): ResultWithDefault<Error, never, D>
36
+ export function goElse<T, D = T>(
37
+ fn: () => Promise<T>,
38
+ defaultValue: D | (() => D),
39
+ ): Promise<ResultWithDefault<Error, T, D>>
40
+ export function goElse<T, D = T>(
41
+ promise: Promise<T>,
42
+ defaultValue: D | (() => D),
43
+ ): Promise<ResultWithDefault<Error, T, D>>
44
+ export function goElse<T, D = T>(
45
+ fn: () => T,
46
+ defaultValue: D | (() => D),
47
+ ): ResultWithDefault<Error, T, D>
48
+ export function goElse<T, D = T>(
49
+ value: T,
50
+ defaultValue: D | (() => D),
51
+ ): ResultWithDefault<Error, T, D>
52
+ export function goElse<T, D = T>(
53
+ value: T | Promise<T> | (() => T | Promise<T>),
54
+ defaultValue: D | (() => D),
55
+ ): ResultWithDefault<Error, T, D> | Promise<ResultWithDefault<Error, T, D>> {
56
+ try {
57
+ const result =
58
+ typeof value === 'function' ? (value as () => T | Promise<T>)() : value
59
+
60
+ if (isPromise<T>(result)) {
61
+ return result
62
+ .then((resolvedValue): ResultWithDefault<Error, T, D> => [undefined, resolvedValue])
63
+ .catch((err): ResultWithDefault<Error, T, D> => {
64
+ const error = err instanceof Error ? err : new Error(String(err))
65
+ return [error, resolveDefault(defaultValue)]
66
+ })
67
+ }
68
+
69
+ return [undefined, result]
70
+ } catch (err) {
71
+ const error = err instanceof Error ? err : new Error(String(err))
72
+ return [error, resolveDefault(defaultValue)]
73
+ }
74
+ }