go-go-try 6.2.0 → 7.1.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/.github/workflows/main.yml +73 -21
- package/.husky/pre-commit +1 -0
- package/CONTRIBUTING.md +87 -0
- package/README.md +278 -5
- package/dist/index.cjs +80 -0
- package/dist/index.d.cts +93 -1
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +93 -1
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +78 -1
- package/package.json +61 -53
- package/src/index.test.ts +547 -1
- package/src/index.ts +207 -0
- package/vitest.config.ts +10 -0
|
@@ -1,24 +1,76 @@
|
|
|
1
1
|
name: CI
|
|
2
|
+
|
|
2
3
|
on:
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, master]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
5
13
|
jobs:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
test:
|
|
15
|
+
name: Node.js ${{ matrix.node-version }}
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
strategy:
|
|
18
|
+
fail-fast: false
|
|
19
|
+
matrix:
|
|
20
|
+
node-version:
|
|
21
|
+
- 22
|
|
22
|
+
- 20
|
|
23
|
+
- 18
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
|
|
27
|
+
- name: Setup Node.js ${{ matrix.node-version }}
|
|
28
|
+
uses: actions/setup-node@v4
|
|
29
|
+
with:
|
|
30
|
+
node-version: ${{ matrix.node-version }}
|
|
31
|
+
cache: npm
|
|
32
|
+
|
|
33
|
+
- name: Install dependencies
|
|
34
|
+
run: npm ci
|
|
35
|
+
|
|
36
|
+
- name: Build
|
|
37
|
+
run: npm run build
|
|
38
|
+
|
|
39
|
+
- name: Run tests
|
|
40
|
+
run: npx vitest run --coverage
|
|
41
|
+
|
|
42
|
+
- name: Upload coverage to Codecov
|
|
43
|
+
if: matrix.node-version == 22
|
|
44
|
+
uses: codecov/codecov-action@v4
|
|
45
|
+
with:
|
|
46
|
+
files: ./coverage/lcov.info
|
|
47
|
+
fail_ci_if_error: false
|
|
48
|
+
|
|
49
|
+
publish:
|
|
50
|
+
name: Publish to npm
|
|
51
|
+
needs: test
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
|
|
54
|
+
permissions:
|
|
55
|
+
contents: read
|
|
56
|
+
id-token: write
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
|
|
60
|
+
- name: Setup Node.js
|
|
61
|
+
uses: actions/setup-node@v4
|
|
62
|
+
with:
|
|
63
|
+
node-version: 22
|
|
64
|
+
registry-url: https://registry.npmjs.org
|
|
65
|
+
cache: npm
|
|
66
|
+
|
|
67
|
+
- name: Install dependencies
|
|
68
|
+
run: npm ci
|
|
69
|
+
|
|
70
|
+
- name: Build
|
|
71
|
+
run: npm run build
|
|
72
|
+
|
|
73
|
+
- name: Publish with provenance
|
|
74
|
+
run: npm publish --provenance --access public
|
|
75
|
+
env:
|
|
76
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
npx lint-staged
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Contributing to go-go-try
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to go-go-try! This document provides guidelines for contributing to the project.
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
1. Fork and clone the repository
|
|
8
|
+
2. Install dependencies:
|
|
9
|
+
```bash
|
|
10
|
+
npm install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Development Workflow
|
|
14
|
+
|
|
15
|
+
### Building
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm run build
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Testing
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Run all tests
|
|
25
|
+
npm test
|
|
26
|
+
|
|
27
|
+
# Run tests in watch mode
|
|
28
|
+
npx vitest
|
|
29
|
+
|
|
30
|
+
# Run tests with coverage
|
|
31
|
+
npx vitest run --coverage
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Linting
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm run lint
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Coding Style
|
|
41
|
+
|
|
42
|
+
- Use TypeScript for all new code
|
|
43
|
+
- Follow the existing code style (enforced by Biome)
|
|
44
|
+
- Write comprehensive tests for new features
|
|
45
|
+
- Ensure all tests pass before submitting PR
|
|
46
|
+
|
|
47
|
+
## Commit Guidelines
|
|
48
|
+
|
|
49
|
+
- Use clear, descriptive commit messages
|
|
50
|
+
- Reference issue numbers when applicable
|
|
51
|
+
- Keep commits focused and atomic
|
|
52
|
+
|
|
53
|
+
## Pull Request Process
|
|
54
|
+
|
|
55
|
+
1. Ensure your branch is up to date with `main`
|
|
56
|
+
2. Run the full test suite: `npm test`
|
|
57
|
+
3. Update documentation if needed
|
|
58
|
+
4. Submit PR with a clear description of changes
|
|
59
|
+
5. Wait for CI checks to pass
|
|
60
|
+
6. Address any review feedback
|
|
61
|
+
|
|
62
|
+
## Adding New Features
|
|
63
|
+
|
|
64
|
+
When adding new features:
|
|
65
|
+
|
|
66
|
+
1. Add type definitions first
|
|
67
|
+
2. Implement the feature with full type safety
|
|
68
|
+
3. Add comprehensive tests covering:
|
|
69
|
+
- Happy path
|
|
70
|
+
- Error cases
|
|
71
|
+
- Edge cases (null, undefined, etc.)
|
|
72
|
+
4. Update README.md with usage examples
|
|
73
|
+
5. Update CHANGELOG.md
|
|
74
|
+
|
|
75
|
+
## Reporting Bugs
|
|
76
|
+
|
|
77
|
+
When reporting bugs, please include:
|
|
78
|
+
|
|
79
|
+
- Clear description of the issue
|
|
80
|
+
- Steps to reproduce
|
|
81
|
+
- Expected vs actual behavior
|
|
82
|
+
- Node.js and TypeScript versions
|
|
83
|
+
- Minimal code example if possible
|
|
84
|
+
|
|
85
|
+
## Questions?
|
|
86
|
+
|
|
87
|
+
Feel free to open an issue for any questions or discussions.
|
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ Why not just `try`/`catch`?
|
|
|
30
30
|
npm install go-go-try
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
## Usage
|
|
33
|
+
## Basic Usage
|
|
34
34
|
|
|
35
35
|
```ts
|
|
36
36
|
import { goTry, goTryRaw } from 'go-go-try'
|
|
@@ -53,13 +53,286 @@ const [err, todos = []] = await goTryRaw(fetchTodos()) // err is Error | undefin
|
|
|
53
53
|
if (err) sendToErrorTrackingService(err)
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
## Advanced Usage
|
|
57
|
+
|
|
58
|
+
### Sequential Async Operations
|
|
59
|
+
|
|
60
|
+
Chain multiple async operations with clean error handling:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { goTry } from 'go-go-try'
|
|
64
|
+
|
|
65
|
+
async function fetchUserData(userId: string) {
|
|
66
|
+
// Fetch user
|
|
67
|
+
const [fetchErr, user] = await goTry(fetch(`/api/users/${userId}`))
|
|
68
|
+
if (fetchErr) return [fetchErr, undefined] as const
|
|
69
|
+
|
|
70
|
+
// Parse response
|
|
71
|
+
const [parseErr, data] = await goTry(user!.json())
|
|
72
|
+
if (parseErr) return [parseErr, undefined] as const
|
|
73
|
+
|
|
74
|
+
// Validate/transform
|
|
75
|
+
const [validateErr, validated] = goTry(() => validateUser(data!))
|
|
76
|
+
if (validateErr) return [validateErr, undefined] as const
|
|
77
|
+
|
|
78
|
+
return [undefined, validated] as const
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const [err, user] = await fetchUserData('123')
|
|
82
|
+
if (err) {
|
|
83
|
+
console.error('Failed to fetch user:', err)
|
|
84
|
+
} else {
|
|
85
|
+
console.log('User:', user)
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Parallel Execution with `goTryAll`
|
|
90
|
+
|
|
91
|
+
Execute multiple promises in parallel:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { goTryAll } from 'go-go-try'
|
|
95
|
+
|
|
96
|
+
const [errors, results] = await goTryAll([
|
|
97
|
+
fetchUser(userId),
|
|
98
|
+
fetchPosts(userId),
|
|
99
|
+
fetchComments(userId)
|
|
100
|
+
])
|
|
101
|
+
|
|
102
|
+
// errors is [string | undefined, string | undefined, string | undefined]
|
|
103
|
+
// results is [User | undefined, Posts | undefined, Comments | undefined]
|
|
104
|
+
|
|
105
|
+
const [user, posts, comments] = results
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Safe Unwrapping with `goTryOr`
|
|
109
|
+
|
|
110
|
+
Like `goTry`, but returns a default value on failure instead of `undefined`:
|
|
111
|
+
|
|
112
|
+
> **Note:** For static default values, you can use destructuring instead:
|
|
113
|
+
> ```ts
|
|
114
|
+
> // These are equivalent for static defaults:
|
|
115
|
+
> const [err, config = {port: 3000}] = goTry(() => JSON.parse(configString))
|
|
116
|
+
> const [err, config] = goTryOr(() => JSON.parse(configString), {port: 3000})
|
|
117
|
+
> ```
|
|
118
|
+
> Use `goTryOr` when you need **lazy evaluation** (the default is only computed on failure):
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { goTryOr } from 'go-go-try'
|
|
122
|
+
|
|
123
|
+
// ✅ Use goTryOr with a function for lazy evaluation - default only computed on failure
|
|
124
|
+
const [err, user] = await goTryOr(fetchUser(id), () => ({
|
|
125
|
+
id: 'anonymous',
|
|
126
|
+
name: 'Guest',
|
|
127
|
+
createdAt: new Date() // This won't run on success
|
|
128
|
+
}))
|
|
129
|
+
|
|
130
|
+
// ❌ Avoid: wasteful - createDefault() runs even on success
|
|
131
|
+
const [err, config = createDefault()] = goTry(loadConfig())
|
|
132
|
+
|
|
133
|
+
// ✅ Better: lazy - createDefault() only runs on failure
|
|
134
|
+
const [err, config] = goTryOr(loadConfig(), () => createDefault())
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Express/Fastify Error Handling
|
|
138
|
+
|
|
139
|
+
Use in API route handlers:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
import { goTry } from 'go-go-try'
|
|
143
|
+
import express from 'express'
|
|
144
|
+
|
|
145
|
+
const app = express()
|
|
146
|
+
|
|
147
|
+
app.post('/users', async (req, res) => {
|
|
148
|
+
const [err, user] = await goTry(createUser(req.body))
|
|
149
|
+
|
|
150
|
+
if (err) {
|
|
151
|
+
return res.status(400).json({ error: err })
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
res.json(user)
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
// Batch endpoint
|
|
158
|
+
app.post('/batch', async (req, res) => {
|
|
159
|
+
const [errors, results] = await goTryAll(
|
|
160
|
+
req.body.operations.map(op => processOperation(op))
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
const hasErrors = errors.some(e => e !== undefined)
|
|
164
|
+
|
|
165
|
+
res.status(hasErrors ? 207 : 200).json({
|
|
166
|
+
results,
|
|
167
|
+
errors: errors.filter(Boolean)
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Type Guards
|
|
173
|
+
|
|
174
|
+
Narrow types using `isSuccess` and `isFailure`:
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
import { goTry, isSuccess, isFailure } from 'go-go-try'
|
|
178
|
+
|
|
179
|
+
const result = goTry(() => riskyOperation())
|
|
180
|
+
|
|
181
|
+
if (isSuccess(result)) {
|
|
182
|
+
// result[1] is typed as T (not T | undefined)
|
|
183
|
+
console.log(result[1])
|
|
184
|
+
} else if (isFailure(result)) {
|
|
185
|
+
// result[0] is typed as E (not E | undefined)
|
|
186
|
+
console.error(result[0])
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
You can also narrow types by destructuring and checking the error:
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
const [err, value] = goTry(() => riskyOperation())
|
|
194
|
+
|
|
195
|
+
if (err === undefined) {
|
|
196
|
+
// value is typed as T (not T | undefined)
|
|
197
|
+
console.log(value)
|
|
198
|
+
} else {
|
|
199
|
+
// err is typed as string (not string | undefined)
|
|
200
|
+
console.error(err)
|
|
201
|
+
// value is typed as undefined in this branch
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Helper Functions
|
|
206
|
+
|
|
207
|
+
Build custom utilities on top of the primitives:
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
import { goTry, success, failure, type Result } from 'go-go-try'
|
|
211
|
+
|
|
212
|
+
// Custom validation helper
|
|
213
|
+
function validateEmail(email: string): Result<string, string> {
|
|
214
|
+
if (!email.includes('@')) {
|
|
215
|
+
return failure('Invalid email format')
|
|
216
|
+
}
|
|
217
|
+
return success(email.toLowerCase().trim())
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Usage
|
|
221
|
+
const [err, normalizedEmail] = validateEmail('User@Example.COM')
|
|
222
|
+
if (err) {
|
|
223
|
+
console.error(err) // Doesn't trigger
|
|
224
|
+
} else {
|
|
225
|
+
console.log(normalizedEmail) // 'user@example.com'
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
56
229
|
## API
|
|
57
230
|
|
|
58
|
-
|
|
231
|
+
### `goTry<T>(value)`
|
|
232
|
+
|
|
233
|
+
Executes a function, promise, or value and returns a Result type with error message as string.
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
function goTry<T>(value: T | Promise<T> | (() => T | Promise<T>)): Result<string, T> | Promise<Result<string, T>>
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### `goTryRaw<T, E>(value)`
|
|
240
|
+
|
|
241
|
+
Like `goTry` but returns the raw Error object instead of just the message.
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
function goTryRaw<T, E = Error>(value: T | Promise<T> | (() => T | Promise<T>)): Result<E, T> | Promise<Result<E, T>>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### `goTryAll<T>(items, options?)`
|
|
248
|
+
|
|
249
|
+
Executes multiple promises or factory functions with optional concurrency limit. Returns a tuple of `[errors, results]` with fixed tuple types preserving input order.
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
interface GoTryAllOptions {
|
|
253
|
+
concurrency?: number // 0 = unlimited (default), 1 = sequential, N = max concurrent
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function goTryAll<T extends readonly unknown[]>(
|
|
257
|
+
items: { [K in keyof T]: Promise<T[K]> | (() => Promise<T[K]>) },
|
|
258
|
+
options?: GoTryAllOptions
|
|
259
|
+
): Promise<[{ [K in keyof T]: string | undefined }, { [K in keyof T]: T[K] | undefined }]>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**Promise mode** (pass promises directly):
|
|
263
|
+
```ts
|
|
264
|
+
// Run all in parallel (default):
|
|
265
|
+
const [errors, results] = await goTryAll([
|
|
266
|
+
fetchUser(1), // Promise<User>
|
|
267
|
+
fetchUser(2), // Promise<User>
|
|
268
|
+
fetchUser(3), // Promise<User>
|
|
269
|
+
])
|
|
270
|
+
// errors: [string | undefined, string | undefined, string | undefined]
|
|
271
|
+
// results: [User | undefined, User | undefined, User | undefined]
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**Factory mode** (pass functions that return promises):
|
|
275
|
+
```ts
|
|
276
|
+
// True lazy execution - factories only called when a slot is available
|
|
277
|
+
const [errors, results] = await goTryAll([
|
|
278
|
+
() => fetchUser(1), // Only called when concurrency slot available
|
|
279
|
+
() => fetchUser(2), // Only called when concurrency slot available
|
|
280
|
+
() => fetchUser(3), // Only called when concurrency slot available
|
|
281
|
+
() => fetchUser(4), // Only called when concurrency slot available
|
|
282
|
+
], { concurrency: 2 })
|
|
283
|
+
|
|
284
|
+
// Use factory mode when you need to:
|
|
285
|
+
// - Rate limit API calls (don't start HTTP requests until allowed)
|
|
286
|
+
// - Control database connection limits
|
|
287
|
+
// - Limit expensive computation resources
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### `goTryAllRaw<T>(items, options?)`
|
|
291
|
+
|
|
292
|
+
Like `goTryAll`, but returns raw Error objects instead of error messages.
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
function goTryAllRaw<T extends readonly unknown[]>(
|
|
296
|
+
items: { [K in keyof T]: Promise<T[K]> | (() => Promise<T[K]>) },
|
|
297
|
+
options?: GoTryAllOptions
|
|
298
|
+
): Promise<[{ [K in keyof T]: Error | undefined }, { [K in keyof T]: T[K] | undefined }]>
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### `goTryOr<T>(value, defaultValue)`
|
|
302
|
+
|
|
303
|
+
Like `goTry`, but returns a default value on failure instead of `undefined`.
|
|
304
|
+
The default can be either a static value or a function (for lazy evaluation).
|
|
305
|
+
|
|
306
|
+
```ts
|
|
307
|
+
function goTryOr<T>(value: T | Promise<T> | (() => T | Promise<T>), defaultValue: T | (() => T)): Result<string, T> | Promise<Result<string, T>>
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### `isSuccess(result)` / `isFailure(result)`
|
|
311
|
+
|
|
312
|
+
Type guards to check result status.
|
|
313
|
+
|
|
314
|
+
```ts
|
|
315
|
+
function isSuccess<E, T>(result: Result<E, T>): result is Success<T>
|
|
316
|
+
function isFailure<E, T>(result: Result<E, T>): result is Failure<E>
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### `success(value)` / `failure(error)`
|
|
59
320
|
|
|
60
|
-
|
|
321
|
+
Helper functions to create Result tuples.
|
|
61
322
|
|
|
62
|
-
|
|
323
|
+
```ts
|
|
324
|
+
function success<T>(value: T): Success<T>
|
|
325
|
+
function failure<E>(error: E): Failure<E>
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Types
|
|
329
|
+
|
|
330
|
+
```ts
|
|
331
|
+
type Success<T> = readonly [undefined, T]
|
|
332
|
+
type Failure<E> = readonly [E, undefined]
|
|
333
|
+
type Result<E, T> = Success<T> | Failure<E>
|
|
334
|
+
```
|
|
63
335
|
|
|
336
|
+
## License
|
|
64
337
|
|
|
65
|
-
|
|
338
|
+
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -12,6 +12,83 @@ function success(value) {
|
|
|
12
12
|
function failure(error) {
|
|
13
13
|
return [error, void 0];
|
|
14
14
|
}
|
|
15
|
+
function resolveDefault(defaultValue) {
|
|
16
|
+
return typeof defaultValue === "function" ? defaultValue() : defaultValue;
|
|
17
|
+
}
|
|
18
|
+
function goTryOr(value, defaultValue) {
|
|
19
|
+
try {
|
|
20
|
+
const result = typeof value === "function" ? value() : value;
|
|
21
|
+
if (isPromise(result)) {
|
|
22
|
+
return result.then((resolvedValue) => success(resolvedValue)).catch((err) => [getErrorMessage(err), resolveDefault(defaultValue)]);
|
|
23
|
+
}
|
|
24
|
+
return success(result);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
return [getErrorMessage(err), resolveDefault(defaultValue)];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async function runWithConcurrency(items, concurrency) {
|
|
30
|
+
if (items.length === 0) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
const isFactoryMode = typeof items[0] === "function";
|
|
34
|
+
if (!isFactoryMode && concurrency <= 0) {
|
|
35
|
+
return Promise.allSettled(items);
|
|
36
|
+
}
|
|
37
|
+
const results = new Array(items.length);
|
|
38
|
+
let index = 0;
|
|
39
|
+
async function worker() {
|
|
40
|
+
while (index < items.length) {
|
|
41
|
+
const currentIndex = index++;
|
|
42
|
+
try {
|
|
43
|
+
const item = items[currentIndex];
|
|
44
|
+
const value = isFactoryMode ? await item() : await item;
|
|
45
|
+
results[currentIndex] = { status: "fulfilled", value };
|
|
46
|
+
} catch (reason) {
|
|
47
|
+
results[currentIndex] = { status: "rejected", reason };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const workerCount = concurrency <= 0 ? items.length : Math.min(concurrency, items.length);
|
|
52
|
+
const workers = [];
|
|
53
|
+
for (let i = 0; i < workerCount; i++) {
|
|
54
|
+
workers.push(worker());
|
|
55
|
+
}
|
|
56
|
+
await Promise.all(workers);
|
|
57
|
+
return results;
|
|
58
|
+
}
|
|
59
|
+
async function goTryAll(items, options) {
|
|
60
|
+
const settled = await runWithConcurrency(items, options?.concurrency ?? 0);
|
|
61
|
+
const errors = [];
|
|
62
|
+
const results = [];
|
|
63
|
+
for (let i = 0; i < settled.length; i++) {
|
|
64
|
+
const item = settled[i];
|
|
65
|
+
if (item.status === "fulfilled") {
|
|
66
|
+
errors[i] = void 0;
|
|
67
|
+
results[i] = item.value;
|
|
68
|
+
} else {
|
|
69
|
+
errors[i] = getErrorMessage(item.reason);
|
|
70
|
+
results[i] = void 0;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return [errors, results];
|
|
74
|
+
}
|
|
75
|
+
async function goTryAllRaw(items, options) {
|
|
76
|
+
const settled = await runWithConcurrency(items, options?.concurrency ?? 0);
|
|
77
|
+
const errors = [];
|
|
78
|
+
const results = [];
|
|
79
|
+
for (let i = 0; i < settled.length; i++) {
|
|
80
|
+
const item = settled[i];
|
|
81
|
+
if (item.status === "fulfilled") {
|
|
82
|
+
errors[i] = void 0;
|
|
83
|
+
results[i] = item.value;
|
|
84
|
+
} else {
|
|
85
|
+
const reason = item.reason;
|
|
86
|
+
errors[i] = isError(reason) ? reason : new Error(String(reason));
|
|
87
|
+
results[i] = void 0;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return [errors, results];
|
|
91
|
+
}
|
|
15
92
|
function getErrorMessage(error) {
|
|
16
93
|
if (error === void 0) return "undefined";
|
|
17
94
|
if (typeof error === "string") return error;
|
|
@@ -64,6 +141,9 @@ function goTryRaw(value) {
|
|
|
64
141
|
|
|
65
142
|
exports.failure = failure;
|
|
66
143
|
exports.goTry = goTry;
|
|
144
|
+
exports.goTryAll = goTryAll;
|
|
145
|
+
exports.goTryAllRaw = goTryAllRaw;
|
|
146
|
+
exports.goTryOr = goTryOr;
|
|
67
147
|
exports.goTryRaw = goTryRaw;
|
|
68
148
|
exports.isFailure = isFailure;
|
|
69
149
|
exports.isSuccess = isSuccess;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,11 +1,101 @@
|
|
|
1
1
|
type Success<T> = readonly [undefined, T];
|
|
2
2
|
type Failure<E> = readonly [E, undefined];
|
|
3
3
|
type Result<E, T> = Success<T> | Failure<E>;
|
|
4
|
+
type ResultWithDefault<E, T> = readonly [E | undefined, T];
|
|
4
5
|
type MaybePromise<T> = T | Promise<T>;
|
|
6
|
+
interface GoTryAllOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Maximum number of concurrent promises.
|
|
9
|
+
* Set to 0 (default) for unlimited concurrency (all promises run in parallel).
|
|
10
|
+
*/
|
|
11
|
+
concurrency?: number;
|
|
12
|
+
}
|
|
5
13
|
declare function isSuccess<E, T>(result: Result<E, T>): result is Success<T>;
|
|
6
14
|
declare function isFailure<E, T>(result: Result<E, T>): result is Failure<E>;
|
|
7
15
|
declare function success<T>(value: T): Success<T>;
|
|
8
16
|
declare function failure<E>(error: E): Failure<E>;
|
|
17
|
+
/**
|
|
18
|
+
* Executes a function, promise, or value and returns a Result type with a fallback default.
|
|
19
|
+
* If an error occurs, it returns the error message and the default value.
|
|
20
|
+
*
|
|
21
|
+
* @template T The type of the successful result
|
|
22
|
+
* @param {T | Promise<T> | (() => T | Promise<T>)} value - The value, promise, or function to execute
|
|
23
|
+
* @param {T | (() => T)} defaultValue - The default value or a function to compute it (only called on failure)
|
|
24
|
+
* @returns {ResultWithDefault<string, T> | Promise<ResultWithDefault<string, T>>} A tuple of [error, value] or Promise thereof
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // With a static default
|
|
28
|
+
* const [err, config] = goTryOr(() => JSON.parse('invalid'), { port: 3000 })
|
|
29
|
+
* // err is the error message, config is { port: 3000 }
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // With a computed default (lazy evaluation)
|
|
33
|
+
* const [err, user] = await goTryOr(fetchUser(id), () => ({
|
|
34
|
+
* id: 'anonymous',
|
|
35
|
+
* name: 'Guest'
|
|
36
|
+
* }))
|
|
37
|
+
*/
|
|
38
|
+
declare function goTryOr<T>(fn: () => never, defaultValue: T | (() => T)): ResultWithDefault<string, T>;
|
|
39
|
+
declare function goTryOr<T>(fn: () => Promise<T>, defaultValue: T | (() => T)): Promise<ResultWithDefault<string, T>>;
|
|
40
|
+
declare function goTryOr<T>(promise: Promise<T>, defaultValue: T | (() => T)): Promise<ResultWithDefault<string, T>>;
|
|
41
|
+
declare function goTryOr<T>(fn: () => T, defaultValue: T | (() => T)): ResultWithDefault<string, T>;
|
|
42
|
+
declare function goTryOr<T>(value: T, defaultValue: T | (() => T)): ResultWithDefault<string, T>;
|
|
43
|
+
/**
|
|
44
|
+
* Executes multiple promises or factory functions in parallel (or with limited concurrency)
|
|
45
|
+
* and returns a tuple of [errors, results]. Unlike Promise.all, this doesn't fail fast -
|
|
46
|
+
* it waits for all promises to settle.
|
|
47
|
+
*
|
|
48
|
+
* Accepts either:
|
|
49
|
+
* - An array of promises (for simple parallel execution)
|
|
50
|
+
* - An array of factory functions that return promises (for lazy execution with concurrency control)
|
|
51
|
+
*
|
|
52
|
+
* @template T The tuple type of all promise results
|
|
53
|
+
* @param {readonly [...{ [K in keyof T]: Promise<T[K]> | (() => Promise<T[K]>) }]} items - Array of promises or factories
|
|
54
|
+
* @param {GoTryAllOptions} options - Optional configuration
|
|
55
|
+
* @returns {Promise<[{ [K in keyof T]: string | undefined }, { [K in keyof T]: T[K] | undefined }]>}
|
|
56
|
+
* A tuple where the first element is a tuple of errors (or undefined) and
|
|
57
|
+
* the second element is a tuple of results (or undefined), preserving input order
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* // Run all in parallel (default) - with promises
|
|
61
|
+
* const [errors, results] = await goTryAll([
|
|
62
|
+
* fetchUser(1),
|
|
63
|
+
* fetchUser(2),
|
|
64
|
+
* fetchUser(3)
|
|
65
|
+
* ])
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* // Limit concurrency with factory functions (lazy execution)
|
|
69
|
+
* const [errors, results] = await goTryAll([
|
|
70
|
+
* () => fetchUser(1), // Only called when a slot is available
|
|
71
|
+
* () => fetchUser(2), // Only called when a slot is available
|
|
72
|
+
* () => fetchUser(3), // Only called when a slot is available
|
|
73
|
+
* ], { concurrency: 2 })
|
|
74
|
+
*/
|
|
75
|
+
declare function goTryAll<T extends readonly unknown[]>(items: {
|
|
76
|
+
[K in keyof T]: Promise<T[K]> | (() => Promise<T[K]>);
|
|
77
|
+
}, options?: GoTryAllOptions): Promise<[{
|
|
78
|
+
[K in keyof T]: string | undefined;
|
|
79
|
+
}, {
|
|
80
|
+
[K in keyof T]: T[K] | undefined;
|
|
81
|
+
}]>;
|
|
82
|
+
/**
|
|
83
|
+
* Like `goTryAll`, but returns raw Error objects instead of error messages.
|
|
84
|
+
*
|
|
85
|
+
* @template T The tuple type of all promise results
|
|
86
|
+
* @param {readonly [...{ [K in keyof T]: Promise<T[K]> | (() => Promise<T[K]>) }]} items - Array of promises or factories
|
|
87
|
+
* @param {GoTryAllOptions} options - Optional configuration
|
|
88
|
+
* @returns {Promise<[{ [K in keyof T]: Error | undefined }, { [K in keyof T]: T[K] | undefined }]>}
|
|
89
|
+
* A tuple where the first element is a tuple of Error objects (or undefined) and
|
|
90
|
+
* the second element is a tuple of results (or undefined), preserving input order
|
|
91
|
+
*/
|
|
92
|
+
declare function goTryAllRaw<T extends readonly unknown[]>(items: {
|
|
93
|
+
[K in keyof T]: Promise<T[K]> | (() => Promise<T[K]>);
|
|
94
|
+
}, options?: GoTryAllOptions): Promise<[{
|
|
95
|
+
[K in keyof T]: Error | undefined;
|
|
96
|
+
}, {
|
|
97
|
+
[K in keyof T]: T[K] | undefined;
|
|
98
|
+
}]>;
|
|
9
99
|
/**
|
|
10
100
|
* Executes a function, promise, or value and returns a Result type.
|
|
11
101
|
* If an error occurs, it returns a Failure with the error message as a string.
|
|
@@ -58,4 +148,6 @@ declare function goTryRaw<T, E = Error>(promise: Promise<T>): Promise<Result<E,
|
|
|
58
148
|
declare function goTryRaw<T, E = Error>(fn: () => T): Result<E, T>;
|
|
59
149
|
declare function goTryRaw<T, E = Error>(value: T): Result<E, T>;
|
|
60
150
|
|
|
61
|
-
export {
|
|
151
|
+
export { failure, goTry, goTryAll, goTryAllRaw, goTryOr, goTryRaw, isFailure, isSuccess, success };
|
|
152
|
+
export type { Failure, GoTryAllOptions, MaybePromise, Result, ResultWithDefault, Success };
|
|
153
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","sources":["../src/index.ts"],"mappings":"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","names":[]}
|