go-go-try 7.2.1 → 7.3.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/src/index.ts CHANGED
@@ -1,459 +1,23 @@
1
- export type Success<T> = readonly [undefined, T]
2
- export type Failure<E> = readonly [E, undefined]
3
- export type Result<E, T> = Success<T> | Failure<E>
4
-
5
- /**
6
- * Base interface for tagged errors.
7
- * The `_tag` property enables discriminated union narrowing.
8
- */
9
- export interface TaggedError<T extends string> {
10
- readonly _tag: T
11
- readonly message: string
12
- readonly cause?: unknown
13
- }
14
-
15
- /**
16
- * Creates a tagged error class for discriminated error handling.
17
- *
18
- * @template T The literal type of the tag
19
- * @param tag The string tag to identify this error type (e.g., 'DatabaseError')
20
- * @returns A class constructor for creating tagged errors
21
- *
22
- * @example
23
- * const DatabaseError = taggedError('DatabaseError')
24
- * const NetworkError = taggedError('NetworkError')
25
- *
26
- * type MyError = InstanceType<typeof DatabaseError> | InstanceType<typeof NetworkError>
27
- *
28
- * function fetchUser(id: string): Result<MyError, User> {
29
- * const [err, user] = goTryRaw(fetch(`/users/${id}`), DatabaseError)
30
- * if (err) return failure(err)
31
- * // ...
32
- * }
33
- *
34
- * // Pattern matching on errors
35
- * if (err._tag === 'DatabaseError') {
36
- * // TypeScript knows this is DatabaseError
37
- * }
38
- */
39
- export function taggedError<T extends string>(tag: T) {
40
- return class TaggedErrorClass extends Error implements TaggedError<T> {
41
- readonly _tag: T = tag
42
- readonly cause?: unknown
43
-
44
- constructor(message: string, options?: { cause?: unknown }) {
45
- super(message)
46
- this.name = tag
47
- this.cause = options?.cause
48
- }
49
- }
50
- }
51
-
52
- export type ResultWithDefault<E, T> = readonly [E | undefined, T]
53
-
54
- export type MaybePromise<T> = T | Promise<T>
55
-
56
- export interface GoTryAllOptions {
57
- /**
58
- * Maximum number of concurrent promises.
59
- * Set to 0 (default) for unlimited concurrency (all promises run in parallel).
60
- */
61
- concurrency?: number
62
- }
63
-
64
- export function isSuccess<E, T>(result: Result<E, T>): result is Success<T> {
65
- return result[0] === undefined
66
- }
67
- export function isFailure<E, T>(result: Result<E, T>): result is Failure<E> {
68
- return result[0] !== undefined
69
- }
70
-
71
- export function success<T>(value: T): Success<T> {
72
- return [undefined, value] as const
73
- }
74
-
75
- export function failure<E>(error: E): Failure<E> {
76
- return [error, undefined] as const
77
- }
78
-
79
- function resolveDefault<T>(defaultValue: T | (() => T)): T {
80
- return typeof defaultValue === 'function' ? (defaultValue as () => T)() : defaultValue
81
- }
82
-
83
- /**
84
- * Executes a function, promise, or value and returns a Result type with a fallback default.
85
- * If an error occurs, it returns the error message and the default value.
86
- *
87
- * @template T The type of the successful result
88
- * @param {T | Promise<T> | (() => T | Promise<T>)} value - The value, promise, or function to execute
89
- * @param {T | (() => T)} defaultValue - The default value or a function to compute it (only called on failure)
90
- * @returns {ResultWithDefault<string, T> | Promise<ResultWithDefault<string, T>>} A tuple of [error, value] or Promise thereof
91
- *
92
- * @example
93
- * // With a static default
94
- * const [err, config] = goTryOr(() => JSON.parse('invalid'), { port: 3000 })
95
- * // err is the error message, config is { port: 3000 }
96
- *
97
- * @example
98
- * // With a computed default (lazy evaluation)
99
- * const [err, user] = await goTryOr(fetchUser(id), () => ({
100
- * id: 'anonymous',
101
- * name: 'Guest'
102
- * }))
103
- */
104
- export function goTryOr<T>(fn: () => never, defaultValue: T | (() => T)): ResultWithDefault<string, T>
105
- export function goTryOr<T>(
106
- fn: () => Promise<T>,
107
- defaultValue: T | (() => T),
108
- ): Promise<ResultWithDefault<string, T>>
109
- export function goTryOr<T>(
110
- promise: Promise<T>,
111
- defaultValue: T | (() => T),
112
- ): Promise<ResultWithDefault<string, T>>
113
- export function goTryOr<T>(fn: () => T, defaultValue: T | (() => T)): ResultWithDefault<string, T>
114
- export function goTryOr<T>(value: T, defaultValue: T | (() => T)): ResultWithDefault<string, T>
115
- export function goTryOr<T>(
116
- value: T | Promise<T> | (() => T | Promise<T>),
117
- defaultValue: T | (() => T),
118
- ): ResultWithDefault<string, T> | Promise<ResultWithDefault<string, T>> {
119
- try {
120
- const result =
121
- typeof value === 'function' ? (value as () => T | Promise<T>)() : value
122
-
123
- if (isPromise<T>(result)) {
124
- return result
125
- .then((resolvedValue) => success<T>(resolvedValue))
126
- .catch((err) => [getErrorMessage(err), resolveDefault(defaultValue)] as const)
127
- }
128
-
129
- return success<T>(result)
130
- } catch (err) {
131
- return [getErrorMessage(err), resolveDefault(defaultValue)] as const
132
- }
133
- }
134
-
135
- type PromiseFactory<T> = () => Promise<T>
136
-
137
- async function runWithConcurrency<T extends readonly unknown[]>(
138
- items: { [K in keyof T]: Promise<T[K]> | PromiseFactory<T[K]> },
139
- concurrency: number,
140
- ): Promise<PromiseSettledResult<T[number]>[]> {
141
- if (items.length === 0) {
142
- return []
143
- }
144
-
145
- // Auto-detect factory mode by checking if first item is a function
146
- const isFactoryMode = typeof items[0] === 'function'
147
-
148
- // concurrency of 0 means unlimited (run all in parallel)
149
- if (!isFactoryMode && (concurrency <= 0)) {
150
- return Promise.allSettled(items as Promise<T[number]>[])
151
- }
152
-
153
- const results: PromiseSettledResult<T[number]>[] = new Array(items.length)
154
- let index = 0
155
-
156
- async function worker(): Promise<void> {
157
- while (index < items.length) {
158
- const currentIndex = index++
159
- try {
160
- const item = items[currentIndex]
161
- // If factory mode, call the function; otherwise await the promise directly
162
- const value = isFactoryMode
163
- ? await (item as PromiseFactory<T[number]>)()
164
- : await (item as Promise<T[number]>)
165
- results[currentIndex] = { status: 'fulfilled', value }
166
- } catch (reason) {
167
- results[currentIndex] = { status: 'rejected', reason }
168
- }
169
- }
170
- }
171
-
172
- // Determine number of workers
173
- const workerCount = concurrency <= 0 ? items.length : Math.min(concurrency, items.length)
174
-
175
- // Start workers
176
- const workers: Promise<void>[] = []
177
- for (let i = 0; i < workerCount; i++) {
178
- workers.push(worker())
179
- }
180
-
181
- await Promise.all(workers)
182
- return results
183
- }
184
-
185
- /**
186
- * Executes multiple promises or factory functions in parallel (or with limited concurrency)
187
- * and returns a tuple of [errors, results]. Unlike Promise.all, this doesn't fail fast -
188
- * it waits for all promises to settle.
189
- *
190
- * Accepts either:
191
- * - An array of promises (for simple parallel execution)
192
- * - An array of factory functions that return promises (for lazy execution with concurrency control)
193
- *
194
- * @template T The tuple type of all promise results
195
- * @param {readonly [...{ [K in keyof T]: Promise<T[K]> | (() => Promise<T[K]>) }]} items - Array of promises or factories
196
- * @param {GoTryAllOptions} options - Optional configuration
197
- * @returns {Promise<[{ [K in keyof T]: string | undefined }, { [K in keyof T]: T[K] | undefined }]>}
198
- * A tuple where the first element is a tuple of errors (or undefined) and
199
- * the second element is a tuple of results (or undefined), preserving input order
200
- *
201
- * @example
202
- * // Run all in parallel (default) - with promises
203
- * const [errors, results] = await goTryAll([
204
- * fetchUser(1),
205
- * fetchUser(2),
206
- * fetchUser(3)
207
- * ])
208
- *
209
- * @example
210
- * // Limit concurrency with factory functions (lazy execution)
211
- * const [errors, results] = await goTryAll([
212
- * () => fetchUser(1), // Only called when a slot is available
213
- * () => fetchUser(2), // Only called when a slot is available
214
- * () => fetchUser(3), // Only called when a slot is available
215
- * ], { concurrency: 2 })
216
- */
217
- export async function goTryAll<T extends readonly unknown[]>(
218
- items: { [K in keyof T]: Promise<T[K]> | (() => Promise<T[K]>) },
219
- options?: GoTryAllOptions,
220
- ): Promise<[{ [K in keyof T]: string | undefined }, { [K in keyof T]: T[K] | undefined }]> {
221
- const settled = await runWithConcurrency(items, options?.concurrency ?? 0)
222
-
223
- const errors = [] as { [K in keyof T]: string | undefined }
224
- const results = [] as { [K in keyof T]: T[K] | undefined }
225
-
226
- for (let i = 0; i < settled.length; i++) {
227
- const item = settled[i]!
228
- if (item.status === 'fulfilled') {
229
- ;(errors as (string | undefined)[])[i] = undefined
230
- ;(results as unknown[])[i] = (item as PromiseFulfilledResult<T[number]>).value
231
- } else {
232
- ;(errors as (string | undefined)[])[i] = getErrorMessage((item as PromiseRejectedResult).reason)
233
- ;(results as unknown[])[i] = undefined
234
- }
235
- }
236
-
237
- return [errors, results]
238
- }
239
-
240
- /**
241
- * Like `goTryAll`, but returns raw Error objects instead of error messages.
242
- *
243
- * @template T The tuple type of all promise results
244
- * @param {readonly [...{ [K in keyof T]: Promise<T[K]> | (() => Promise<T[K]>) }]} items - Array of promises or factories
245
- * @param {GoTryAllOptions} options - Optional configuration
246
- * @returns {Promise<[{ [K in keyof T]: Error | undefined }, { [K in keyof T]: T[K] | undefined }]>}
247
- * A tuple where the first element is a tuple of Error objects (or undefined) and
248
- * the second element is a tuple of results (or undefined), preserving input order
249
- */
250
- export async function goTryAllRaw<T extends readonly unknown[]>(
251
- items: { [K in keyof T]: Promise<T[K]> | (() => Promise<T[K]>) },
252
- options?: GoTryAllOptions,
253
- ): Promise<[{ [K in keyof T]: Error | undefined }, { [K in keyof T]: T[K] | undefined }]> {
254
- const settled = await runWithConcurrency(items, options?.concurrency ?? 0)
255
-
256
- const errors = [] as { [K in keyof T]: Error | undefined }
257
- const results = [] as { [K in keyof T]: T[K] | undefined }
258
-
259
- for (let i = 0; i < settled.length; i++) {
260
- const item = settled[i]!
261
- if (item.status === 'fulfilled') {
262
- ;(errors as (Error | undefined)[])[i] = undefined
263
- ;(results as unknown[])[i] = (item as PromiseFulfilledResult<T[number]>).value
264
- } else {
265
- const reason = (item as PromiseRejectedResult).reason
266
- ;(errors as (Error | undefined)[])[i] = isError(reason)
267
- ? reason
268
- : new Error(String(reason))
269
- ;(results as unknown[])[i] = undefined
270
- }
271
- }
272
-
273
- return [errors, results]
274
- }
275
-
276
- function getErrorMessage(error: unknown): string {
277
- if (error === undefined) return 'undefined'
278
-
279
- if (typeof error === 'string') return error
280
-
281
- if (
282
- typeof error === 'object' &&
283
- error !== null &&
284
- 'message' in error &&
285
- typeof (error as Record<string, unknown>).message === 'string'
286
- ) {
287
- return (error as { message: string }).message
288
- }
289
-
290
- try {
291
- return JSON.stringify(error)
292
- } catch {
293
- // fallback in case there's an error stringifying the error
294
- // with circular references for example.
295
- return String(error)
296
- }
297
- }
298
-
299
- function isPromise<T>(value: unknown): value is Promise<T> {
300
- return (
301
- typeof value === 'object' &&
302
- value !== null &&
303
- 'then' in value &&
304
- typeof (value as { then: unknown }).then === 'function'
305
- )
306
- }
307
-
308
- function isError(value: unknown): value is Error {
309
- return value instanceof Error
310
- }
311
-
312
- /**
313
- * Executes a function, promise, or value and returns a Result type.
314
- * If an error occurs, it returns a Failure with the error message as a string.
315
- *
316
- * @template T The type of the successful result
317
- * @param {T | Promise<T> | (() => T | Promise<T>)} value - The value, promise, or function to execute
318
- * @returns {Result<string, T> | Promise<Result<string, T>>} A Result type or a Promise of a Result type
319
- *
320
- * @example
321
- * // With a value
322
- * const [err, result] = goTry(42);
323
- *
324
- * @example
325
- * // With a function
326
- * const [err, result] = goTry(() => JSON.parse('{"key": "value"}'));
327
- *
328
- * @example
329
- * // With a promise
330
- * const [err, result] = await goTry(fetch('https://api.example.com/data'));
331
- */
332
- export function goTry<T>(fn: () => never): Result<string, never>
333
- export function goTry<T>(fn: () => Promise<T>): Promise<Result<string, T>>
334
- export function goTry<T>(promise: Promise<T>): Promise<Result<string, T>>
335
- export function goTry<T>(fn: () => T): Result<string, T>
336
- export function goTry<T>(value: T): Result<string, T>
337
- export function goTry<T>(
338
- value: T | Promise<T> | (() => T | Promise<T>),
339
- ): Result<string, T> | Promise<Result<string, T>> {
340
- try {
341
- const result =
342
- typeof value === 'function' ? (value as () => T | Promise<T>)() : value
343
- if (isPromise<T>(result)) {
344
- return result
345
- .then((resolvedValue) => success<T>(resolvedValue))
346
- .catch((err) => failure<string>(getErrorMessage(err)))
347
- }
348
- return success<T>(result)
349
- } catch (err) {
350
- return failure<string>(getErrorMessage(err))
351
- }
352
- }
353
-
354
- /**
355
- * Type for error constructors that can be used with goTryRaw.
356
- */
357
- export type ErrorConstructor<E> = new (message: string, options?: { cause?: unknown }) => E
358
-
359
- /**
360
- * Creates a union type from multiple tagged error classes.
361
- *
362
- * @template T A tuple of tagged error class types
363
- * @returns A union of all instance types
364
- *
365
- * @example
366
- * const DatabaseError = taggedError('DatabaseError')
367
- * const NetworkError = taggedError('NetworkError')
368
- *
369
- * type AppError = TaggedUnion<[typeof DatabaseError, typeof NetworkError]>
370
- * // Equivalent to: DatabaseError | NetworkError
371
- */
372
- export type TaggedUnion<T extends readonly ErrorConstructor<unknown>[]> =
373
- { [K in keyof T]: T[K] extends ErrorConstructor<infer E> ? E : never }[number]
374
-
375
- /**
376
- * Executes a function, promise, or value and returns a Result type.
377
- * If an error occurs, it returns a Failure with the raw error object.
378
- *
379
- * @template T The type of the successful result
380
- * @template E The type of the error, defaults to Error
381
- * @param {T | Promise<T> | (() => T | Promise<T>)} value - The value, promise, or function to execute
382
- * @param {ErrorConstructor<E>} [ErrorClass] - Optional error constructor to wrap caught errors
383
- * @returns {Result<E, T> | Promise<Result<E, T>>} A Result type or a Promise of a Result type
384
- *
385
- * @example
386
- * // With a value
387
- * const [err, result] = goTryRaw(42);
388
- *
389
- * @example
390
- * // With a function
391
- * const [err, result] = goTryRaw(() => JSON.parse('{"key": "value"}'));
392
- *
393
- * @example
394
- * // With a promise
395
- * const [err, result] = await goTryRaw(fetch('https://api.example.com/data'));
396
- *
397
- * @example
398
- * // With tagged error for discriminated unions
399
- * const DatabaseError = taggedError('DatabaseError');
400
- * const [err, result] = await goTryRaw(fetchData(), DatabaseError);
401
- * // err is InstanceType<typeof DatabaseError> | undefined
402
- */
403
- export function goTryRaw<T, E = Error>(fn: () => never): Result<E, never>
404
- export function goTryRaw<T, E = Error>(fn: () => never, ErrorClass: ErrorConstructor<E>): Result<E, never>
405
- export function goTryRaw<T, E = Error>(
406
- fn: () => Promise<T>,
407
- ): Promise<Result<E, T>>
408
- export function goTryRaw<T, E = Error>(
409
- fn: () => Promise<T>,
410
- ErrorClass: ErrorConstructor<E>,
411
- ): Promise<Result<E, T>>
412
- export function goTryRaw<T, E = Error>(
413
- promise: Promise<T>,
414
- ): Promise<Result<E, T>>
415
- export function goTryRaw<T, E = Error>(
416
- promise: Promise<T>,
417
- ErrorClass: ErrorConstructor<E>,
418
- ): Promise<Result<E, T>>
419
- export function goTryRaw<T, E = Error>(fn: () => T): Result<E, T>
420
- export function goTryRaw<T, E = Error>(fn: () => T, ErrorClass: ErrorConstructor<E>): Result<E, T>
421
- export function goTryRaw<T, E = Error>(value: T): Result<E, T>
422
- export function goTryRaw<T, E = Error>(value: T, ErrorClass: ErrorConstructor<E>): Result<E, T>
423
- export function goTryRaw<T, E = Error>(
424
- value: T | Promise<T> | (() => T | Promise<T>),
425
- ErrorClass?: ErrorConstructor<E>,
426
- ): Result<E, T> | Promise<Result<E, T>> {
427
- // Helper to wrap error in the provided class or return as-is
428
- const wrapError = (err: unknown): E => {
429
- if (ErrorClass) {
430
- if (err === undefined) {
431
- return new ErrorClass('undefined')
432
- }
433
- if (isError(err)) {
434
- return new ErrorClass(err.message, { cause: err })
435
- }
436
- return new ErrorClass(String(err))
437
- }
438
- // Default behavior: return raw error
439
- if (err === undefined) {
440
- return new Error('undefined') as unknown as E
441
- }
442
- return (
443
- isError(err) ? err : new Error(String(err))
444
- ) as unknown as E
445
- }
446
-
447
- try {
448
- const result =
449
- typeof value === 'function' ? (value as () => T | Promise<T>)() : value
450
- if (isPromise<T>(result)) {
451
- return result
452
- .then((resolvedValue) => success<T>(resolvedValue))
453
- .catch((err) => failure<E>(wrapError(err)))
454
- }
455
- return success<T>(result)
456
- } catch (err) {
457
- return failure<E>(wrapError(err))
458
- }
459
- }
1
+ // Export all types
2
+ export type {
3
+ Success,
4
+ Failure,
5
+ Result,
6
+ TaggedError,
7
+ ResultWithDefault,
8
+ MaybePromise,
9
+ GoTryAllOptions,
10
+ ErrorConstructor,
11
+ TaggedUnion,
12
+ } from './types.js'
13
+
14
+ // Export core functions
15
+ export { goTry } from './goTry.js'
16
+ export { goTryRaw } from './goTryRaw.js'
17
+ export { goTryOr } from './goTryOr.js'
18
+ export { goTryAll, goTryAllRaw } from './goTryAll.js'
19
+
20
+ // Export helper functions
21
+ export { taggedError } from './tagged-error.js'
22
+ export { assert } from './assert.js'
23
+ export { isSuccess, isFailure, success, failure, assertNever } from './result-helpers.js'
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Internal utility functions (not exported from index)
3
+ */
4
+
5
+ export type PromiseFactory<T> = () => Promise<T>
6
+
7
+ export function getErrorMessage(error: unknown): string {
8
+ if (error === undefined) return 'undefined'
9
+
10
+ if (typeof error === 'string') return error
11
+
12
+ if (
13
+ typeof error === 'object' &&
14
+ error !== null &&
15
+ 'message' in error &&
16
+ typeof (error as Record<string, unknown>).message === 'string'
17
+ ) {
18
+ return (error as { message: string }).message
19
+ }
20
+
21
+ try {
22
+ return JSON.stringify(error)
23
+ } catch {
24
+ // fallback in case there's an error stringifying the error
25
+ // with circular references for example.
26
+ return String(error)
27
+ }
28
+ }
29
+
30
+ export function isPromise<T>(value: unknown): value is Promise<T> {
31
+ return (
32
+ typeof value === 'object' &&
33
+ value !== null &&
34
+ 'then' in value &&
35
+ typeof (value as { then: unknown }).then === 'function'
36
+ )
37
+ }
38
+
39
+ export function isError(value: unknown): value is Error {
40
+ return value instanceof Error
41
+ }
42
+
43
+ export function resolveDefault<T>(defaultValue: T | (() => T)): T {
44
+ return typeof defaultValue === 'function' ? (defaultValue as () => T)() : defaultValue
45
+ }
@@ -0,0 +1,46 @@
1
+ import type { Result, Success, Failure } from './types.js'
2
+
3
+ export function isSuccess<E, T>(result: Result<E, T>): result is Success<T> {
4
+ return result[0] === undefined
5
+ }
6
+
7
+ export function isFailure<E, T>(result: Result<E, T>): result is Failure<E> {
8
+ return result[0] !== undefined
9
+ }
10
+
11
+ export function success<T>(value: T): Success<T> {
12
+ return [undefined, value] as const
13
+ }
14
+
15
+ export function failure<E>(error: E): Failure<E> {
16
+ return [error, undefined] as const
17
+ }
18
+
19
+ /**
20
+ * Helper for exhaustive switch checks on discriminated unions.
21
+ * If this function is called, it means a case was forgotten in a switch statement.
22
+ * Use this in the `default` case of switch statements handling tagged errors.
23
+ *
24
+ * @param value - The value that should be of type `never` if all cases are handled
25
+ * @throws {Error} Always throws an error indicating unhandled case
26
+ *
27
+ * @example
28
+ * const DatabaseError = taggedError('DatabaseError')
29
+ * const NetworkError = taggedError('NetworkError')
30
+ * type AppError = InstanceType<typeof DatabaseError> | InstanceType<typeof NetworkError>
31
+ *
32
+ * function handleError(err: AppError): string {
33
+ * switch (err._tag) {
34
+ * case 'DatabaseError':
35
+ * return `DB: ${err.message}`
36
+ * case 'NetworkError':
37
+ * return `NET: ${err.message}`
38
+ * default:
39
+ * // TypeScript will error if we forget a case above
40
+ * return assertNever(err)
41
+ * }
42
+ * }
43
+ */
44
+ export function assertNever(value: never): never {
45
+ throw new Error(`Unhandled case: ${String(value)}`)
46
+ }
@@ -0,0 +1,38 @@
1
+ import type { TaggedError } from './types.js'
2
+
3
+ /**
4
+ * Creates a tagged error class for discriminated error handling.
5
+ *
6
+ * @template T The literal type of the tag
7
+ * @param tag The string tag to identify this error type (e.g., 'DatabaseError')
8
+ * @returns A class constructor for creating tagged errors
9
+ *
10
+ * @example
11
+ * const DatabaseError = taggedError('DatabaseError')
12
+ * const NetworkError = taggedError('NetworkError')
13
+ *
14
+ * type MyError = InstanceType<typeof DatabaseError> | InstanceType<typeof NetworkError>
15
+ *
16
+ * function fetchUser(id: string): Result<MyError, User> {
17
+ * const [err, user] = goTryRaw(fetch(`/users/${id}`), DatabaseError)
18
+ * if (err) return failure(err)
19
+ * // ...
20
+ * }
21
+ *
22
+ * // Pattern matching on errors
23
+ * if (err._tag === 'DatabaseError') {
24
+ * // TypeScript knows this is DatabaseError
25
+ * }
26
+ */
27
+ export function taggedError<T extends string>(tag: T) {
28
+ return class TaggedErrorClass extends Error implements TaggedError<T> {
29
+ readonly _tag: T = tag
30
+ readonly cause?: unknown
31
+
32
+ constructor(message: string, options?: { cause?: unknown }) {
33
+ super(message)
34
+ this.name = tag
35
+ this.cause = options?.cause
36
+ }
37
+ }
38
+ }
package/src/types.ts ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Core Result types for go-go-try
3
+ */
4
+
5
+ export type Success<T> = readonly [undefined, T]
6
+ export type Failure<E> = readonly [E, undefined]
7
+ export type Result<E, T> = Success<T> | Failure<E>
8
+
9
+ export type ResultWithDefault<E, T> = readonly [E | undefined, T]
10
+
11
+ export type MaybePromise<T> = T | Promise<T>
12
+
13
+ /**
14
+ * Base interface for tagged errors.
15
+ * The `_tag` property enables discriminated union narrowing.
16
+ */
17
+ export interface TaggedError<T extends string> {
18
+ readonly _tag: T
19
+ readonly message: string
20
+ readonly cause?: unknown
21
+ }
22
+
23
+ export interface GoTryAllOptions {
24
+ /**
25
+ * Maximum number of concurrent promises.
26
+ * Set to 0 (default) for unlimited concurrency (all promises run in parallel).
27
+ */
28
+ concurrency?: number
29
+ }
30
+
31
+ /**
32
+ * Type for error constructors that can be used with goTryRaw.
33
+ */
34
+ export type ErrorConstructor<E> = new (message: string, options?: { cause?: unknown }) => E
35
+
36
+ /**
37
+ * Creates a union type from multiple tagged error classes.
38
+ *
39
+ * @template T A tuple of tagged error class types
40
+ * @returns A union of all instance types
41
+ *
42
+ * @example
43
+ * const DatabaseError = taggedError('DatabaseError')
44
+ * const NetworkError = taggedError('NetworkError')
45
+ *
46
+ * type AppError = TaggedUnion<[typeof DatabaseError, typeof NetworkError]>
47
+ * // Equivalent to: DatabaseError | NetworkError
48
+ */
49
+ export type TaggedUnion<T extends readonly ErrorConstructor<unknown>[]> =
50
+ { [K in keyof T]: T[K] extends ErrorConstructor<infer E> ? E : never }[number]