go-go-try 7.2.1 → 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/AGENTS.md +81 -23
- package/README.md +84 -18
- package/dist/index.cjs +104 -61
- package/dist/index.d.cts +217 -94
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +217 -94
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +102 -62
- package/package.json +3 -3
- package/src/assert.ts +59 -0
- package/src/goTry.ts +45 -0
- package/src/goTryAll.ts +141 -0
- package/src/goTryOr.ts +55 -0
- package/src/goTryRaw.ts +126 -0
- package/src/index.test.ts +411 -22
- package/src/index.ts +27 -459
- package/src/internals.ts +45 -0
- package/src/result-helpers.ts +46 -0
- package/src/tagged-error.ts +38 -0
- package/src/types.ts +64 -0
- package/src/unknown-error.ts +20 -0
package/AGENTS.md
CHANGED
|
@@ -5,25 +5,35 @@
|
|
|
5
5
|
**go-go-try** is a TypeScript utility library for error handling inspired by Go's error handling pattern. It provides a functional approach to try/catch operations by returning a tuple `[error, value]` instead of throwing exceptions.
|
|
6
6
|
|
|
7
7
|
- **Name**: go-go-try
|
|
8
|
-
- **Version**:
|
|
8
|
+
- **Version**: 7.2.1
|
|
9
9
|
- **License**: MIT
|
|
10
10
|
- **Repository**: thelinuxlich/go-go-try
|
|
11
11
|
- **Node.js Requirements**: >= 16
|
|
12
12
|
|
|
13
|
+
### Key Features
|
|
14
|
+
|
|
15
|
+
- Zero runtime dependencies
|
|
16
|
+
- Dual package support (CommonJS and ESM)
|
|
17
|
+
- Full TypeScript support with precise type inference
|
|
18
|
+
- Support for sync/async functions, promises, and direct values
|
|
19
|
+
- Parallel execution utilities with optional concurrency control
|
|
20
|
+
- Tagged errors for discriminated union pattern matching
|
|
21
|
+
|
|
13
22
|
## Technology Stack
|
|
14
23
|
|
|
15
|
-
- **Language**: TypeScript 5.
|
|
24
|
+
- **Language**: TypeScript 5.9.3
|
|
16
25
|
- **Build Tool**: [pkgroll](https://github.com/privatenumber/pkgroll) - A zero-config TypeScript package bundler
|
|
17
26
|
- **Linter**: [Biome](https://biomejs.dev/) - Fast linter and formatter
|
|
18
27
|
- **Test Framework**: [Vitest](https://vitest.dev/) - Vite-native unit test framework
|
|
19
28
|
- **Type Testing**: [@ark/attest](https://github.com/arktypeio/arktype) - Runtime type assertions for TypeScript
|
|
29
|
+
- **Git Hooks**: [Husky](https://typicode.github.io/husky/) + [lint-staged](https://github.com/lint-staged/lint-staged)
|
|
20
30
|
|
|
21
31
|
## Project Structure
|
|
22
32
|
|
|
23
33
|
```
|
|
24
34
|
.
|
|
25
35
|
├── src/
|
|
26
|
-
│ ├── index.ts # Main source file - exports
|
|
36
|
+
│ ├── index.ts # Main source file - exports all functions and types
|
|
27
37
|
│ └── index.test.ts # Comprehensive test suite with runtime and type tests
|
|
28
38
|
├── dist/ # Build output (generated by pkgroll)
|
|
29
39
|
│ ├── index.cjs # CommonJS build
|
|
@@ -32,10 +42,12 @@
|
|
|
32
42
|
│ └── index.d.mts # ES Module type definitions
|
|
33
43
|
├── .github/workflows/
|
|
34
44
|
│ └── main.yml # CI configuration for GitHub Actions
|
|
45
|
+
├── .husky/
|
|
46
|
+
│ └── pre-commit # Git pre-commit hook (runs lint-staged)
|
|
35
47
|
├── .attest/ # Ark attest cache directory
|
|
36
48
|
├── package.json # Package configuration with dual CJS/ESM exports
|
|
37
49
|
├── tsconfig.json # TypeScript strict configuration
|
|
38
|
-
├── vitest.config.ts # Vitest configuration with type checking
|
|
50
|
+
├── vitest.config.ts # Vitest configuration with type checking and coverage
|
|
39
51
|
├── setupVitest.ts # Vitest global setup for @ark/attest
|
|
40
52
|
└── README.md # User-facing documentation
|
|
41
53
|
```
|
|
@@ -51,17 +63,20 @@ npm run lint
|
|
|
51
63
|
|
|
52
64
|
# Run the full test suite (build + lint + vitest)
|
|
53
65
|
npm test
|
|
66
|
+
|
|
67
|
+
# Run tests with coverage report
|
|
68
|
+
npm run test:coverage
|
|
54
69
|
```
|
|
55
70
|
|
|
56
|
-
The test script is a composite that:
|
|
71
|
+
The `npm test` script is a composite that:
|
|
57
72
|
1. Builds the project
|
|
58
73
|
2. Runs the linter
|
|
59
|
-
3. Executes Vitest tests
|
|
74
|
+
3. Executes Vitest tests with type checking
|
|
60
75
|
|
|
61
76
|
## Code Style Guidelines
|
|
62
77
|
|
|
63
|
-
- **Linter**: Biome is used for linting and formatting
|
|
64
|
-
- **
|
|
78
|
+
- **Linter**: Biome is used for linting and formatting with default configuration (no biome.json present)
|
|
79
|
+
- **Pre-commit**: Husky runs `lint-staged` which lints all staged `.ts` files via Biome
|
|
65
80
|
- **Strict TypeScript**: The `tsconfig.json` enforces strict mode with additional checks:
|
|
66
81
|
- `noUnusedLocals`: true
|
|
67
82
|
- `noUnusedParameters`: true
|
|
@@ -78,8 +93,8 @@ Tests are co-located with source code in `src/index.test.ts` using Vitest.
|
|
|
78
93
|
|
|
79
94
|
### Test Types
|
|
80
95
|
|
|
81
|
-
1. **Runtime Tests**: Standard unit tests verifying behavior
|
|
82
|
-
2. **Type Tests**: Using `@ark/attest` to verify TypeScript type inference at runtime
|
|
96
|
+
1. **Runtime Tests**: Standard unit tests verifying behavior using Vitest's `assert` and `test`
|
|
97
|
+
2. **Type Tests**: Using `@ark/attest` to verify TypeScript type inference at runtime with `attest<T>(value)`
|
|
83
98
|
|
|
84
99
|
### Key Testing Patterns
|
|
85
100
|
|
|
@@ -107,33 +122,60 @@ npx vitest run
|
|
|
107
122
|
|
|
108
123
|
# Run tests in watch mode (during development)
|
|
109
124
|
npx vitest
|
|
125
|
+
|
|
126
|
+
# Run with coverage
|
|
127
|
+
npx vitest run --coverage
|
|
110
128
|
```
|
|
111
129
|
|
|
112
|
-
###
|
|
130
|
+
### Coverage Configuration
|
|
113
131
|
|
|
114
|
-
|
|
132
|
+
Coverage is provided by `@vitest/coverage-v8` with reporters: text, json, html, and lcov. Excluded paths:
|
|
133
|
+
- `node_modules/`
|
|
134
|
+
- `dist/`
|
|
135
|
+
- `**/*.test.ts`
|
|
136
|
+
- `setupVitest.ts`
|
|
115
137
|
|
|
116
138
|
## API Design
|
|
117
139
|
|
|
118
140
|
### Core Functions
|
|
119
141
|
|
|
120
|
-
|
|
121
|
-
|
|
142
|
+
| Function | Description | Error Type |
|
|
143
|
+
|----------|-------------|------------|
|
|
144
|
+
| `goTry<T>(value)` | Returns `[string \| undefined, T \| undefined]` | Error message string |
|
|
145
|
+
| `goTryRaw<T, E>(value, ErrorClass?)` | Returns `[E \| undefined, T \| undefined]` | Raw Error object or tagged error |
|
|
146
|
+
| `goTryOr<T>(value, defaultValue)` | Returns `[string \| undefined, T]` | Error message with fallback default |
|
|
147
|
+
| `goTryAll<T>(items, options?)` | Parallel execution, returns `[errors[], results[]]` | Error message strings |
|
|
148
|
+
| `goTryAllRaw<T>(items, options?)` | Parallel execution, returns `[Error[], results[]]` | Raw Error objects |
|
|
149
|
+
|
|
150
|
+
### Input Handling
|
|
151
|
+
|
|
152
|
+
All `goTry*` functions accept:
|
|
153
|
+
- Direct values
|
|
154
|
+
- Functions (sync or async)
|
|
155
|
+
- Promises
|
|
122
156
|
|
|
123
157
|
### Type Helpers
|
|
124
158
|
|
|
125
|
-
- **`Result<E, T>`**: The tuple type `[E
|
|
159
|
+
- **`Result<E, T>`**: The tuple type `[E \| undefined, T \| undefined]`
|
|
126
160
|
- **`Success<T>`**: `[undefined, T]`
|
|
127
161
|
- **`Failure<E>`**: `[E, undefined]`
|
|
162
|
+
- **`TaggedError<T>`**: Interface for discriminated errors with `_tag` property
|
|
163
|
+
- **`TaggedUnion<T>`**: Creates union type from multiple tagged error classes
|
|
128
164
|
- **`isSuccess(result)`**: Type guard to check if result is success
|
|
129
165
|
- **`isFailure(result)`**: Type guard to check if result is failure
|
|
166
|
+
- **`success(value)`**: Helper to create Success tuple
|
|
167
|
+
- **`failure(error)`**: Helper to create Failure tuple
|
|
130
168
|
|
|
131
|
-
###
|
|
169
|
+
### Tagged Errors
|
|
132
170
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
171
|
+
The `taggedError(tag)` function creates error classes with a `_tag` property for discriminated union pattern matching:
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
const DatabaseError = taggedError('DatabaseError')
|
|
175
|
+
const err = new DatabaseError('connection failed')
|
|
176
|
+
// err._tag === 'DatabaseError'
|
|
177
|
+
// err instanceof Error === true
|
|
178
|
+
```
|
|
137
179
|
|
|
138
180
|
## Dual Package Support
|
|
139
181
|
|
|
@@ -141,6 +183,7 @@ The package supports both CommonJS and ESM consumers:
|
|
|
141
183
|
|
|
142
184
|
```json
|
|
143
185
|
{
|
|
186
|
+
"type": "module",
|
|
144
187
|
"main": "./dist/index.cjs",
|
|
145
188
|
"module": "./dist/index.mjs",
|
|
146
189
|
"types": "./dist/index.d.mts",
|
|
@@ -153,17 +196,28 @@ The package supports both CommonJS and ESM consumers:
|
|
|
153
196
|
|
|
154
197
|
## CI/CD
|
|
155
198
|
|
|
156
|
-
GitHub Actions workflow (`.github/workflows/main.yml`)
|
|
199
|
+
GitHub Actions workflow (`.github/workflows/main.yml`):
|
|
157
200
|
|
|
158
|
-
|
|
159
|
-
-
|
|
201
|
+
### Test Job
|
|
202
|
+
- Runs on every push and PR to `main`/`master`
|
|
203
|
+
- Tests against Node.js versions: 18, 20, 22
|
|
204
|
+
- Steps: checkout → setup Node → install → build → test with coverage
|
|
160
205
|
- `fail-fast: false` to see results for all Node versions
|
|
206
|
+
- Coverage uploaded to Codecov (Node 22 only)
|
|
207
|
+
|
|
208
|
+
### Publish Job
|
|
209
|
+
- Runs only on tag pushes (refs/tags/v*)
|
|
210
|
+
- Depends on successful test job
|
|
211
|
+
- Publishes to npm with provenance
|
|
212
|
+
- Requires `NPM_TOKEN` secret
|
|
161
213
|
|
|
162
214
|
## Security Considerations
|
|
163
215
|
|
|
164
216
|
- Zero runtime dependencies - reduces supply chain attack surface
|
|
165
217
|
- Dev dependencies are locked via `package-lock.json`
|
|
166
218
|
- Uses `type: "module"` for native ESM support
|
|
219
|
+
- npm publishing uses provenance for supply chain security
|
|
220
|
+
- CI has minimal permissions (`contents: read`, `id-token: write`)
|
|
167
221
|
|
|
168
222
|
## Development Workflow
|
|
169
223
|
|
|
@@ -171,6 +225,7 @@ GitHub Actions workflow (`.github/workflows/main.yml`) runs on every push and PR
|
|
|
171
225
|
2. Add/update tests in `src/index.test.ts`
|
|
172
226
|
3. Run `npm test` to verify build, lint, and tests pass
|
|
173
227
|
4. The CI will test against multiple Node.js versions on push
|
|
228
|
+
5. To release: push a version tag (e.g., `v7.2.1`) to trigger npm publish
|
|
174
229
|
|
|
175
230
|
## Notes for AI Agents
|
|
176
231
|
|
|
@@ -180,3 +235,6 @@ GitHub Actions workflow (`.github/workflows/main.yml`) runs on every push and PR
|
|
|
180
235
|
- Maintain dual CJS/ESM compatibility when making changes
|
|
181
236
|
- Follow the existing function overload patterns for type inference
|
|
182
237
|
- Error handling should preserve the Go-style tuple return pattern
|
|
238
|
+
- Use `taggedError()` for creating discriminated error types
|
|
239
|
+
- The `goTryAll` function supports both promise arrays and factory function arrays for lazy execution
|
|
240
|
+
- When adding new functions, include both runtime tests and type tests with `attest()`
|
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
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
329
|
-
|
|
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
|
|
332
|
-
function goTryRaw<T, E>(value: T | Promise<T> | (() => T | Promise<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
|
-
**
|
|
346
|
+
**Examples:**
|
|
347
|
+
|
|
336
348
|
```ts
|
|
337
349
|
const DatabaseError = taggedError('DatabaseError')
|
|
350
|
+
const NetworkError = taggedError('NetworkError')
|
|
338
351
|
|
|
339
|
-
//
|
|
352
|
+
// Default - errors wrapped in UnknownError
|
|
340
353
|
const [err1, data1] = await goTryRaw(fetchData())
|
|
341
|
-
// err1 is
|
|
354
|
+
// err1 is UnknownError | undefined
|
|
355
|
+
// err1?._tag === 'UnknownError'
|
|
342
356
|
|
|
343
|
-
//
|
|
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
|
|
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
|
@@ -1,15 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
function taggedError(tag) {
|
|
4
|
-
return class TaggedErrorClass extends Error {
|
|
5
|
-
constructor(message, options) {
|
|
6
|
-
super(message);
|
|
7
|
-
this._tag = tag;
|
|
8
|
-
this.name = tag;
|
|
9
|
-
this.cause = options?.cause;
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
3
|
function isSuccess(result) {
|
|
14
4
|
return result[0] === void 0;
|
|
15
5
|
}
|
|
@@ -22,9 +12,101 @@ function success(value) {
|
|
|
22
12
|
function failure(error) {
|
|
23
13
|
return [error, void 0];
|
|
24
14
|
}
|
|
15
|
+
function assertNever(value) {
|
|
16
|
+
throw new Error(`Unhandled case: ${String(value)}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getErrorMessage(error) {
|
|
20
|
+
if (error === void 0) return "undefined";
|
|
21
|
+
if (typeof error === "string") return error;
|
|
22
|
+
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
|
23
|
+
return error.message;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
return JSON.stringify(error);
|
|
27
|
+
} catch {
|
|
28
|
+
return String(error);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function isPromise(value) {
|
|
32
|
+
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
|
|
33
|
+
}
|
|
34
|
+
function isError(value) {
|
|
35
|
+
return value instanceof Error;
|
|
36
|
+
}
|
|
25
37
|
function resolveDefault(defaultValue) {
|
|
26
38
|
return typeof defaultValue === "function" ? defaultValue() : defaultValue;
|
|
27
39
|
}
|
|
40
|
+
|
|
41
|
+
function goTry(value) {
|
|
42
|
+
try {
|
|
43
|
+
const result = typeof value === "function" ? value() : value;
|
|
44
|
+
if (isPromise(result)) {
|
|
45
|
+
return result.then((resolvedValue) => success(resolvedValue)).catch((err) => failure(getErrorMessage(err)));
|
|
46
|
+
}
|
|
47
|
+
return success(result);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
return failure(getErrorMessage(err));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
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;
|
|
72
|
+
const wrapError = (err) => {
|
|
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
|
+
}
|
|
86
|
+
if (err === void 0) {
|
|
87
|
+
return new actualSystemErrorClass("undefined");
|
|
88
|
+
}
|
|
89
|
+
if (isError(err)) {
|
|
90
|
+
return new actualSystemErrorClass(err.message, { cause: err });
|
|
91
|
+
}
|
|
92
|
+
return new actualSystemErrorClass(String(err));
|
|
93
|
+
}
|
|
94
|
+
if (err === void 0) {
|
|
95
|
+
return new Error("undefined");
|
|
96
|
+
}
|
|
97
|
+
return isError(err) ? err : new Error(String(err));
|
|
98
|
+
};
|
|
99
|
+
try {
|
|
100
|
+
const result = typeof value === "function" ? value() : value;
|
|
101
|
+
if (isPromise(result)) {
|
|
102
|
+
return result.then((resolvedValue) => success(resolvedValue)).catch((err) => failure(wrapError(err)));
|
|
103
|
+
}
|
|
104
|
+
return success(result);
|
|
105
|
+
} catch (err) {
|
|
106
|
+
return failure(wrapError(err));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
28
110
|
function goTryOr(value, defaultValue) {
|
|
29
111
|
try {
|
|
30
112
|
const result = typeof value === "function" ? value() : value;
|
|
@@ -36,6 +118,7 @@ function goTryOr(value, defaultValue) {
|
|
|
36
118
|
return [getErrorMessage(err), resolveDefault(defaultValue)];
|
|
37
119
|
}
|
|
38
120
|
}
|
|
121
|
+
|
|
39
122
|
async function runWithConcurrency(items, concurrency) {
|
|
40
123
|
if (items.length === 0) {
|
|
41
124
|
return [];
|
|
@@ -99,62 +182,22 @@ async function goTryAllRaw(items, options) {
|
|
|
99
182
|
}
|
|
100
183
|
return [errors, results];
|
|
101
184
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
try {
|
|
109
|
-
return JSON.stringify(error);
|
|
110
|
-
} catch {
|
|
111
|
-
return String(error);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
function isPromise(value) {
|
|
115
|
-
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
|
|
116
|
-
}
|
|
117
|
-
function isError(value) {
|
|
118
|
-
return value instanceof Error;
|
|
119
|
-
}
|
|
120
|
-
function goTry(value) {
|
|
121
|
-
try {
|
|
122
|
-
const result = typeof value === "function" ? value() : value;
|
|
123
|
-
if (isPromise(result)) {
|
|
124
|
-
return result.then((resolvedValue) => success(resolvedValue)).catch((err) => failure(getErrorMessage(err)));
|
|
125
|
-
}
|
|
126
|
-
return success(result);
|
|
127
|
-
} catch (err) {
|
|
128
|
-
return failure(getErrorMessage(err));
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
function goTryRaw(value, ErrorClass) {
|
|
132
|
-
const wrapError = (err) => {
|
|
133
|
-
if (ErrorClass) {
|
|
134
|
-
if (err === void 0) {
|
|
135
|
-
return new ErrorClass("undefined");
|
|
136
|
-
}
|
|
137
|
-
if (isError(err)) {
|
|
138
|
-
return new ErrorClass(err.message, { cause: err });
|
|
139
|
-
}
|
|
140
|
-
return new ErrorClass(String(err));
|
|
141
|
-
}
|
|
142
|
-
if (err === void 0) {
|
|
143
|
-
return new Error("undefined");
|
|
185
|
+
|
|
186
|
+
function assert(condition, errorOrClass, message) {
|
|
187
|
+
if (!condition) {
|
|
188
|
+
if (typeof errorOrClass === "string") {
|
|
189
|
+
throw new Error(errorOrClass);
|
|
144
190
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
try {
|
|
148
|
-
const result = typeof value === "function" ? value() : value;
|
|
149
|
-
if (isPromise(result)) {
|
|
150
|
-
return result.then((resolvedValue) => success(resolvedValue)).catch((err) => failure(wrapError(err)));
|
|
191
|
+
if (typeof errorOrClass === "function" && message !== void 0) {
|
|
192
|
+
throw new errorOrClass(message);
|
|
151
193
|
}
|
|
152
|
-
|
|
153
|
-
} catch (err) {
|
|
154
|
-
return failure(wrapError(err));
|
|
194
|
+
throw errorOrClass;
|
|
155
195
|
}
|
|
156
196
|
}
|
|
157
197
|
|
|
198
|
+
exports.UnknownError = UnknownError;
|
|
199
|
+
exports.assert = assert;
|
|
200
|
+
exports.assertNever = assertNever;
|
|
158
201
|
exports.failure = failure;
|
|
159
202
|
exports.goTry = goTry;
|
|
160
203
|
exports.goTryAll = goTryAll;
|