@f0rbit/corpus 0.1.8 → 0.2.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/dist/backend/base.d.ts +23 -0
- package/dist/backend/base.d.ts.map +1 -0
- package/dist/backend/base.js +100 -0
- package/dist/backend/file.d.ts.map +1 -1
- package/dist/backend/file.js +38 -76
- package/dist/backend/memory.d.ts.map +1 -1
- package/dist/backend/memory.js +18 -69
- package/dist/backends.d.ts +1 -0
- package/dist/backends.d.ts.map +1 -1
- package/dist/backends.js +1 -0
- package/dist/cloudflare.d.ts +1 -0
- package/dist/cloudflare.d.ts.map +1 -1
- package/dist/cloudflare.js +1 -0
- package/dist/concurrency.d.ts +78 -0
- package/dist/concurrency.d.ts.map +1 -0
- package/dist/concurrency.js +108 -0
- package/dist/index.d.ts +13 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -10
- package/dist/observations/client.d.ts.map +1 -1
- package/dist/observations/client.js +13 -24
- package/dist/observations/index.d.ts +1 -1
- package/dist/observations/index.d.ts.map +1 -1
- package/dist/observations/storage.d.ts +33 -4
- package/dist/observations/storage.d.ts.map +1 -1
- package/dist/observations/storage.js +63 -43
- package/dist/observations/types.d.ts +21 -15
- package/dist/observations/types.d.ts.map +1 -1
- package/dist/result.d.ts +280 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +319 -0
- package/dist/types.d.ts +33 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +39 -6
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +47 -15
- package/package.json +1 -1
package/dist/result.d.ts
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Result
|
|
3
|
+
* @description Extended utilities for working with Result types.
|
|
4
|
+
*
|
|
5
|
+
* Provides functional utilities for error handling without exceptions:
|
|
6
|
+
* - Pattern matching with `match`
|
|
7
|
+
* - Safe unwrapping with `unwrap_or`, `unwrap`, `unwrap_err`
|
|
8
|
+
* - Exception-to-Result conversion with `try_catch`, `try_catch_async`
|
|
9
|
+
* - Fetch wrapper with `fetch_result`
|
|
10
|
+
* - Composable pipelines with `pipe`
|
|
11
|
+
*/
|
|
12
|
+
import { type Result } from "./types";
|
|
13
|
+
/**
|
|
14
|
+
* Pattern match on a Result, extracting the value with appropriate handler.
|
|
15
|
+
*
|
|
16
|
+
* @param result - The Result to match on
|
|
17
|
+
* @param on_ok - Handler for success case
|
|
18
|
+
* @param on_err - Handler for error case
|
|
19
|
+
* @returns The return value of the matching handler
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const result = await fetchUser(id)
|
|
24
|
+
* const message = match(
|
|
25
|
+
* result,
|
|
26
|
+
* user => `Hello, ${user.name}!`,
|
|
27
|
+
* error => `Failed: ${error.message}`
|
|
28
|
+
* )
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare const match: <T, E, R>(result: Result<T, E>, on_ok: (value: T) => R, on_err: (error: E) => R) => R;
|
|
32
|
+
/**
|
|
33
|
+
* Extract value from Result, returning default if error.
|
|
34
|
+
*
|
|
35
|
+
* @param result - The Result to unwrap
|
|
36
|
+
* @param default_value - Value to return if Result is an error
|
|
37
|
+
* @returns The success value or default
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* const users = unwrap_or(await fetchUsers(), [])
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare const unwrap_or: <T, E>(result: Result<T, E>, default_value: T) => T;
|
|
45
|
+
/**
|
|
46
|
+
* Extract value from Result, throwing if error.
|
|
47
|
+
* Use only when you're certain the Result is Ok, or in tests.
|
|
48
|
+
*
|
|
49
|
+
* @param result - The Result to unwrap
|
|
50
|
+
* @returns The success value
|
|
51
|
+
* @throws Error if Result is an error
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* // In tests
|
|
56
|
+
* const user = unwrap(await createUser(data))
|
|
57
|
+
* expect(user.name).toBe('Alice')
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export declare const unwrap: <T, E>(result: Result<T, E>) => T;
|
|
61
|
+
/**
|
|
62
|
+
* Extract error from Result, throwing if Ok.
|
|
63
|
+
* Use only when you're certain the Result is Err, or in tests.
|
|
64
|
+
*
|
|
65
|
+
* @param result - The Result to unwrap
|
|
66
|
+
* @returns The error value
|
|
67
|
+
* @throws Error if Result is Ok
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* // In tests
|
|
72
|
+
* const error = unwrap_err(await createUser(invalidData))
|
|
73
|
+
* expect(error.kind).toBe('validation_error')
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export declare const unwrap_err: <T, E>(result: Result<T, E>) => E;
|
|
77
|
+
/**
|
|
78
|
+
* Execute a function and convert exceptions to Result.
|
|
79
|
+
*
|
|
80
|
+
* @param fn - Function to execute
|
|
81
|
+
* @param on_error - Transform caught exception to error type
|
|
82
|
+
* @returns Result containing success value or transformed error
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* const result = try_catch(
|
|
87
|
+
* () => JSON.parse(input),
|
|
88
|
+
* e => ({ kind: 'parse_error', message: format_error(e) })
|
|
89
|
+
* )
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export declare const try_catch: <T, E>(fn: () => T, on_error: (e: unknown) => E) => Result<T, E>;
|
|
93
|
+
/**
|
|
94
|
+
* Execute an async function and convert exceptions to Result.
|
|
95
|
+
*
|
|
96
|
+
* @param fn - Async function to execute
|
|
97
|
+
* @param on_error - Transform caught exception to error type
|
|
98
|
+
* @returns Promise of Result containing success value or transformed error
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* const result = await try_catch_async(
|
|
103
|
+
* () => db.query('SELECT * FROM users'),
|
|
104
|
+
* e => ({ kind: 'database_error', cause: e })
|
|
105
|
+
* )
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export declare const try_catch_async: <T, E>(fn: () => Promise<T>, on_error: (e: unknown) => E) => Promise<Result<T, E>>;
|
|
109
|
+
/**
|
|
110
|
+
* Error types for fetch operations.
|
|
111
|
+
*/
|
|
112
|
+
export type FetchError = {
|
|
113
|
+
type: "network";
|
|
114
|
+
cause: unknown;
|
|
115
|
+
} | {
|
|
116
|
+
type: "http";
|
|
117
|
+
status: number;
|
|
118
|
+
status_text: string;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Fetch wrapper that returns Result instead of throwing.
|
|
122
|
+
*
|
|
123
|
+
* @param input - URL or Request to fetch
|
|
124
|
+
* @param init - Fetch options
|
|
125
|
+
* @param on_error - Transform FetchError to your error type
|
|
126
|
+
* @param parse_body - Custom body parser (defaults to JSON)
|
|
127
|
+
* @returns Promise of Result with parsed response or error
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```ts
|
|
131
|
+
* const result = await fetch_result(
|
|
132
|
+
* 'https://api.example.com/users',
|
|
133
|
+
* { headers: { Authorization: `Bearer ${token}` } },
|
|
134
|
+
* e => e.type === 'http' ? `HTTP ${e.status}` : 'Network error'
|
|
135
|
+
* )
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
export declare const fetch_result: <T, E>(input: string | URL | Request, init: RequestInit | undefined, on_error: (e: FetchError) => E, parse_body?: (response: Response) => Promise<T>) => Promise<Result<T, E>>;
|
|
139
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
140
|
+
/**
|
|
141
|
+
* A composable pipeline for chaining Result operations.
|
|
142
|
+
*
|
|
143
|
+
* All operations are lazy - nothing executes until `.result()` or `.unwrap_or()` is called.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```ts
|
|
147
|
+
* const user = await pipe(fetchUser(id))
|
|
148
|
+
* .map(user => user.profile)
|
|
149
|
+
* .flat_map(profile => fetchAvatar(profile.avatar_id))
|
|
150
|
+
* .map(avatar => avatar.url)
|
|
151
|
+
* .unwrap_or('/default-avatar.png')
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
export type Pipe<T, E> = {
|
|
155
|
+
/** Transform the success value */
|
|
156
|
+
map: <U>(fn: (value: T) => U) => Pipe<U, E>;
|
|
157
|
+
/** Transform the success value with an async function */
|
|
158
|
+
map_async: <U>(fn: (value: T) => Promise<U>) => Pipe<U, E>;
|
|
159
|
+
/** Chain with another Result-returning operation */
|
|
160
|
+
flat_map: <U>(fn: (value: T) => MaybePromise<Result<U, E>>) => Pipe<U, E>;
|
|
161
|
+
/** Transform the error value */
|
|
162
|
+
map_err: <F>(fn: (error: E) => F) => Pipe<T, F>;
|
|
163
|
+
/** Execute side effect on success (logging, metrics) */
|
|
164
|
+
tap: (fn: (value: T) => MaybePromise<void>) => Pipe<T, E>;
|
|
165
|
+
/** Execute side effect on error */
|
|
166
|
+
tap_err: (fn: (error: E) => MaybePromise<void>) => Pipe<T, E>;
|
|
167
|
+
/** Extract value with fallback */
|
|
168
|
+
unwrap_or: (default_value: T) => Promise<T>;
|
|
169
|
+
/** Get the underlying Result */
|
|
170
|
+
result: () => Promise<Result<T, E>>;
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Create a composable pipeline from a Result or Promise<Result>.
|
|
174
|
+
*
|
|
175
|
+
* @param initial - Starting Result value (sync or async)
|
|
176
|
+
* @returns A Pipe for chaining operations
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```ts
|
|
180
|
+
* // From existing Result
|
|
181
|
+
* const result = await pipe(ok(42))
|
|
182
|
+
* .map(n => n * 2)
|
|
183
|
+
* .result()
|
|
184
|
+
*
|
|
185
|
+
* // From async operation
|
|
186
|
+
* const user = await pipe(fetchUser(id))
|
|
187
|
+
* .flat_map(u => fetchProfile(u.id))
|
|
188
|
+
* .result()
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
export declare const pipe: {
|
|
192
|
+
<T, E>(initial: MaybePromise<Result<T, E>>): Pipe<T, E>;
|
|
193
|
+
ok<T>(value: T): Pipe<T, never>;
|
|
194
|
+
err<E>(error: E): Pipe<never, E>;
|
|
195
|
+
try<T, E>(fn: () => Promise<T>, on_error: (e: unknown) => E): Pipe<T, E>;
|
|
196
|
+
fetch<T, E>(input: string | URL | Request, init: RequestInit | undefined, on_error: (e: FetchError) => E, parse_body?: (response: Response) => Promise<T>): Pipe<T, E>;
|
|
197
|
+
};
|
|
198
|
+
/**
|
|
199
|
+
* Extract value from Result, returning null for any error.
|
|
200
|
+
* Use for "fetch single resource" patterns where not-found is expected.
|
|
201
|
+
*
|
|
202
|
+
* @param result - The Result to convert
|
|
203
|
+
* @returns The value or null
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```ts
|
|
207
|
+
* const user = to_nullable(await store.get(userId))
|
|
208
|
+
* if (!user) return <NotFound />
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
export declare const to_nullable: <T, E>(result: Result<T, E>) => T | null;
|
|
212
|
+
/**
|
|
213
|
+
* Extract value from Result, returning fallback for any error.
|
|
214
|
+
* Use for list endpoints where empty array is acceptable.
|
|
215
|
+
*
|
|
216
|
+
* @param result - The Result to convert
|
|
217
|
+
* @param fallback - Value to return on error
|
|
218
|
+
* @returns The value or fallback
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```ts
|
|
222
|
+
* const items = to_fallback(await store.list(), [])
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
export declare const to_fallback: <T, E>(result: Result<T, E>, fallback: T) => T;
|
|
226
|
+
/**
|
|
227
|
+
* Return null if error matches predicate, otherwise throw the error.
|
|
228
|
+
* Use for 404-as-null pattern specifically.
|
|
229
|
+
*
|
|
230
|
+
* @param result - The Result to check
|
|
231
|
+
* @param predicate - Returns true for expected errors (e.g., not_found)
|
|
232
|
+
* @returns The value or null for expected errors
|
|
233
|
+
* @throws The error if predicate returns false
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* ```ts
|
|
237
|
+
* const user = null_on(
|
|
238
|
+
* await store.get(id),
|
|
239
|
+
* e => e.kind === 'not_found'
|
|
240
|
+
* )
|
|
241
|
+
* ```
|
|
242
|
+
*/
|
|
243
|
+
export declare const null_on: <T, E>(result: Result<T, E>, predicate: (error: E) => boolean) => T | null;
|
|
244
|
+
/**
|
|
245
|
+
* Return fallback if error matches predicate, otherwise throw.
|
|
246
|
+
*
|
|
247
|
+
* @param result - The Result to check
|
|
248
|
+
* @param predicate - Returns true for expected errors
|
|
249
|
+
* @param fallback - Value to return for expected errors
|
|
250
|
+
* @returns The value or fallback for expected errors
|
|
251
|
+
* @throws The error if predicate returns false
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```ts
|
|
255
|
+
* const count = fallback_on(
|
|
256
|
+
* await store.count(),
|
|
257
|
+
* e => e.kind === 'not_found',
|
|
258
|
+
* 0
|
|
259
|
+
* )
|
|
260
|
+
* ```
|
|
261
|
+
*/
|
|
262
|
+
export declare const fallback_on: <T, E>(result: Result<T, E>, predicate: (error: E) => boolean, fallback: T) => T;
|
|
263
|
+
/**
|
|
264
|
+
* Format an unknown error to a string message.
|
|
265
|
+
*
|
|
266
|
+
* @param e - Any error value
|
|
267
|
+
* @returns A string representation
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```ts
|
|
271
|
+
* try {
|
|
272
|
+
* riskyOperation()
|
|
273
|
+
* } catch (e) {
|
|
274
|
+
* console.error(format_error(e)) // Handles Error, string, or anything
|
|
275
|
+
* }
|
|
276
|
+
* ```
|
|
277
|
+
*/
|
|
278
|
+
export declare const format_error: (e: unknown) => string;
|
|
279
|
+
export {};
|
|
280
|
+
//# sourceMappingURL=result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../result.ts"],"names":[],"mappings":"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"}
|
package/dist/result.js
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module Result
|
|
3
|
+
* @description Extended utilities for working with Result types.
|
|
4
|
+
*
|
|
5
|
+
* Provides functional utilities for error handling without exceptions:
|
|
6
|
+
* - Pattern matching with `match`
|
|
7
|
+
* - Safe unwrapping with `unwrap_or`, `unwrap`, `unwrap_err`
|
|
8
|
+
* - Exception-to-Result conversion with `try_catch`, `try_catch_async`
|
|
9
|
+
* - Fetch wrapper with `fetch_result`
|
|
10
|
+
* - Composable pipelines with `pipe`
|
|
11
|
+
*/
|
|
12
|
+
import { ok, err } from "./types";
|
|
13
|
+
/**
|
|
14
|
+
* Pattern match on a Result, extracting the value with appropriate handler.
|
|
15
|
+
*
|
|
16
|
+
* @param result - The Result to match on
|
|
17
|
+
* @param on_ok - Handler for success case
|
|
18
|
+
* @param on_err - Handler for error case
|
|
19
|
+
* @returns The return value of the matching handler
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const result = await fetchUser(id)
|
|
24
|
+
* const message = match(
|
|
25
|
+
* result,
|
|
26
|
+
* user => `Hello, ${user.name}!`,
|
|
27
|
+
* error => `Failed: ${error.message}`
|
|
28
|
+
* )
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export const match = (result, on_ok, on_err) => {
|
|
32
|
+
if (result.ok)
|
|
33
|
+
return on_ok(result.value);
|
|
34
|
+
return on_err(result.error);
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Extract value from Result, returning default if error.
|
|
38
|
+
*
|
|
39
|
+
* @param result - The Result to unwrap
|
|
40
|
+
* @param default_value - Value to return if Result is an error
|
|
41
|
+
* @returns The success value or default
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* const users = unwrap_or(await fetchUsers(), [])
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export const unwrap_or = (result, default_value) => (result.ok ? result.value : default_value);
|
|
49
|
+
/**
|
|
50
|
+
* Extract value from Result, throwing if error.
|
|
51
|
+
* Use only when you're certain the Result is Ok, or in tests.
|
|
52
|
+
*
|
|
53
|
+
* @param result - The Result to unwrap
|
|
54
|
+
* @returns The success value
|
|
55
|
+
* @throws Error if Result is an error
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* // In tests
|
|
60
|
+
* const user = unwrap(await createUser(data))
|
|
61
|
+
* expect(user.name).toBe('Alice')
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export const unwrap = (result) => {
|
|
65
|
+
if (!result.ok)
|
|
66
|
+
throw new Error(`unwrap called on error result: ${JSON.stringify(result.error)}`);
|
|
67
|
+
return result.value;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Extract error from Result, throwing if Ok.
|
|
71
|
+
* Use only when you're certain the Result is Err, or in tests.
|
|
72
|
+
*
|
|
73
|
+
* @param result - The Result to unwrap
|
|
74
|
+
* @returns The error value
|
|
75
|
+
* @throws Error if Result is Ok
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* // In tests
|
|
80
|
+
* const error = unwrap_err(await createUser(invalidData))
|
|
81
|
+
* expect(error.kind).toBe('validation_error')
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export const unwrap_err = (result) => {
|
|
85
|
+
if (result.ok)
|
|
86
|
+
throw new Error(`unwrap_err called on ok result: ${JSON.stringify(result.value)}`);
|
|
87
|
+
return result.error;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Execute a function and convert exceptions to Result.
|
|
91
|
+
*
|
|
92
|
+
* @param fn - Function to execute
|
|
93
|
+
* @param on_error - Transform caught exception to error type
|
|
94
|
+
* @returns Result containing success value or transformed error
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```ts
|
|
98
|
+
* const result = try_catch(
|
|
99
|
+
* () => JSON.parse(input),
|
|
100
|
+
* e => ({ kind: 'parse_error', message: format_error(e) })
|
|
101
|
+
* )
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export const try_catch = (fn, on_error) => {
|
|
105
|
+
try {
|
|
106
|
+
return ok(fn());
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
return err(on_error(e));
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Execute an async function and convert exceptions to Result.
|
|
114
|
+
*
|
|
115
|
+
* @param fn - Async function to execute
|
|
116
|
+
* @param on_error - Transform caught exception to error type
|
|
117
|
+
* @returns Promise of Result containing success value or transformed error
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```ts
|
|
121
|
+
* const result = await try_catch_async(
|
|
122
|
+
* () => db.query('SELECT * FROM users'),
|
|
123
|
+
* e => ({ kind: 'database_error', cause: e })
|
|
124
|
+
* )
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export const try_catch_async = async (fn, on_error) => {
|
|
128
|
+
try {
|
|
129
|
+
return ok(await fn());
|
|
130
|
+
}
|
|
131
|
+
catch (e) {
|
|
132
|
+
return err(on_error(e));
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Fetch wrapper that returns Result instead of throwing.
|
|
137
|
+
*
|
|
138
|
+
* @param input - URL or Request to fetch
|
|
139
|
+
* @param init - Fetch options
|
|
140
|
+
* @param on_error - Transform FetchError to your error type
|
|
141
|
+
* @param parse_body - Custom body parser (defaults to JSON)
|
|
142
|
+
* @returns Promise of Result with parsed response or error
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```ts
|
|
146
|
+
* const result = await fetch_result(
|
|
147
|
+
* 'https://api.example.com/users',
|
|
148
|
+
* { headers: { Authorization: `Bearer ${token}` } },
|
|
149
|
+
* e => e.type === 'http' ? `HTTP ${e.status}` : 'Network error'
|
|
150
|
+
* )
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
export const fetch_result = async (input, init, on_error, parse_body = r => r.json()) => {
|
|
154
|
+
try {
|
|
155
|
+
const response = await fetch(input, init);
|
|
156
|
+
if (!response.ok) {
|
|
157
|
+
return err(on_error({ type: "http", status: response.status, status_text: response.statusText }));
|
|
158
|
+
}
|
|
159
|
+
return ok(await parse_body(response));
|
|
160
|
+
}
|
|
161
|
+
catch (e) {
|
|
162
|
+
return err(on_error({ type: "network", cause: e }));
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
const create_pipe = (promised) => ({
|
|
166
|
+
map: (fn) => create_pipe(promised.then((r) => {
|
|
167
|
+
if (r.ok)
|
|
168
|
+
return ok(fn(r.value));
|
|
169
|
+
return err(r.error);
|
|
170
|
+
})),
|
|
171
|
+
map_async: (fn) => create_pipe(promised.then(async (r) => {
|
|
172
|
+
if (r.ok)
|
|
173
|
+
return ok(await fn(r.value));
|
|
174
|
+
return err(r.error);
|
|
175
|
+
})),
|
|
176
|
+
flat_map: (fn) => create_pipe(promised.then((r) => {
|
|
177
|
+
if (r.ok)
|
|
178
|
+
return fn(r.value);
|
|
179
|
+
return err(r.error);
|
|
180
|
+
})),
|
|
181
|
+
map_err: (fn) => create_pipe(promised.then((r) => {
|
|
182
|
+
if (r.ok)
|
|
183
|
+
return ok(r.value);
|
|
184
|
+
return err(fn(r.error));
|
|
185
|
+
})),
|
|
186
|
+
tap: (fn) => create_pipe(promised.then(async (r) => {
|
|
187
|
+
if (r.ok)
|
|
188
|
+
await fn(r.value);
|
|
189
|
+
return r;
|
|
190
|
+
})),
|
|
191
|
+
tap_err: (fn) => create_pipe(promised.then(async (r) => {
|
|
192
|
+
if (!r.ok)
|
|
193
|
+
await fn(r.error);
|
|
194
|
+
return r;
|
|
195
|
+
})),
|
|
196
|
+
unwrap_or: (default_value) => promised.then(r => (r.ok ? r.value : default_value)),
|
|
197
|
+
result: () => promised,
|
|
198
|
+
});
|
|
199
|
+
/**
|
|
200
|
+
* Create a composable pipeline from a Result or Promise<Result>.
|
|
201
|
+
*
|
|
202
|
+
* @param initial - Starting Result value (sync or async)
|
|
203
|
+
* @returns A Pipe for chaining operations
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```ts
|
|
207
|
+
* // From existing Result
|
|
208
|
+
* const result = await pipe(ok(42))
|
|
209
|
+
* .map(n => n * 2)
|
|
210
|
+
* .result()
|
|
211
|
+
*
|
|
212
|
+
* // From async operation
|
|
213
|
+
* const user = await pipe(fetchUser(id))
|
|
214
|
+
* .flat_map(u => fetchProfile(u.id))
|
|
215
|
+
* .result()
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
export const pipe = (initial) => create_pipe(Promise.resolve(initial));
|
|
219
|
+
/** Create a pipe starting with an Ok value */
|
|
220
|
+
pipe.ok = (value) => pipe(ok(value));
|
|
221
|
+
/** Create a pipe starting with an Err value */
|
|
222
|
+
pipe.err = (error) => pipe(err(error));
|
|
223
|
+
/** Create a pipe from a function that may throw */
|
|
224
|
+
pipe.try = (fn, on_error) => pipe(try_catch_async(fn, on_error));
|
|
225
|
+
/** Create a pipe from a fetch operation */
|
|
226
|
+
pipe.fetch = (input, init, on_error, parse_body) => pipe(fetch_result(input, init, on_error, parse_body));
|
|
227
|
+
/**
|
|
228
|
+
* Extract value from Result, returning null for any error.
|
|
229
|
+
* Use for "fetch single resource" patterns where not-found is expected.
|
|
230
|
+
*
|
|
231
|
+
* @param result - The Result to convert
|
|
232
|
+
* @returns The value or null
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```ts
|
|
236
|
+
* const user = to_nullable(await store.get(userId))
|
|
237
|
+
* if (!user) return <NotFound />
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
export const to_nullable = (result) => (result.ok ? result.value : null);
|
|
241
|
+
/**
|
|
242
|
+
* Extract value from Result, returning fallback for any error.
|
|
243
|
+
* Use for list endpoints where empty array is acceptable.
|
|
244
|
+
*
|
|
245
|
+
* @param result - The Result to convert
|
|
246
|
+
* @param fallback - Value to return on error
|
|
247
|
+
* @returns The value or fallback
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```ts
|
|
251
|
+
* const items = to_fallback(await store.list(), [])
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
export const to_fallback = (result, fallback) => (result.ok ? result.value : fallback);
|
|
255
|
+
/**
|
|
256
|
+
* Return null if error matches predicate, otherwise throw the error.
|
|
257
|
+
* Use for 404-as-null pattern specifically.
|
|
258
|
+
*
|
|
259
|
+
* @param result - The Result to check
|
|
260
|
+
* @param predicate - Returns true for expected errors (e.g., not_found)
|
|
261
|
+
* @returns The value or null for expected errors
|
|
262
|
+
* @throws The error if predicate returns false
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* ```ts
|
|
266
|
+
* const user = null_on(
|
|
267
|
+
* await store.get(id),
|
|
268
|
+
* e => e.kind === 'not_found'
|
|
269
|
+
* )
|
|
270
|
+
* ```
|
|
271
|
+
*/
|
|
272
|
+
export const null_on = (result, predicate) => {
|
|
273
|
+
if (result.ok)
|
|
274
|
+
return result.value;
|
|
275
|
+
if (predicate(result.error))
|
|
276
|
+
return null;
|
|
277
|
+
throw result.error;
|
|
278
|
+
};
|
|
279
|
+
/**
|
|
280
|
+
* Return fallback if error matches predicate, otherwise throw.
|
|
281
|
+
*
|
|
282
|
+
* @param result - The Result to check
|
|
283
|
+
* @param predicate - Returns true for expected errors
|
|
284
|
+
* @param fallback - Value to return for expected errors
|
|
285
|
+
* @returns The value or fallback for expected errors
|
|
286
|
+
* @throws The error if predicate returns false
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* ```ts
|
|
290
|
+
* const count = fallback_on(
|
|
291
|
+
* await store.count(),
|
|
292
|
+
* e => e.kind === 'not_found',
|
|
293
|
+
* 0
|
|
294
|
+
* )
|
|
295
|
+
* ```
|
|
296
|
+
*/
|
|
297
|
+
export const fallback_on = (result, predicate, fallback) => {
|
|
298
|
+
if (result.ok)
|
|
299
|
+
return result.value;
|
|
300
|
+
if (predicate(result.error))
|
|
301
|
+
return fallback;
|
|
302
|
+
throw result.error;
|
|
303
|
+
};
|
|
304
|
+
/**
|
|
305
|
+
* Format an unknown error to a string message.
|
|
306
|
+
*
|
|
307
|
+
* @param e - Any error value
|
|
308
|
+
* @returns A string representation
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```ts
|
|
312
|
+
* try {
|
|
313
|
+
* riskyOperation()
|
|
314
|
+
* } catch (e) {
|
|
315
|
+
* console.error(format_error(e)) // Handles Error, string, or anything
|
|
316
|
+
* }
|
|
317
|
+
* ```
|
|
318
|
+
*/
|
|
319
|
+
export const format_error = (e) => (e instanceof Error ? e.message : String(e));
|
package/dist/types.d.ts
CHANGED
|
@@ -214,10 +214,12 @@ export type Snapshot<T = unknown> = {
|
|
|
214
214
|
meta: SnapshotMeta;
|
|
215
215
|
data: T;
|
|
216
216
|
};
|
|
217
|
+
/** @internal */
|
|
217
218
|
export type DataHandle = {
|
|
218
219
|
stream: () => ReadableStream<Uint8Array>;
|
|
219
220
|
bytes: () => Promise<Uint8Array>;
|
|
220
221
|
};
|
|
222
|
+
/** @internal */
|
|
221
223
|
export type MetadataClient = {
|
|
222
224
|
get: (store_id: string, version: string) => Promise<Result<SnapshotMeta, CorpusError>>;
|
|
223
225
|
put: (meta: SnapshotMeta) => Promise<Result<void, CorpusError>>;
|
|
@@ -227,6 +229,7 @@ export type MetadataClient = {
|
|
|
227
229
|
get_children: (parent_store_id: string, parent_version: string) => AsyncIterable<SnapshotMeta>;
|
|
228
230
|
find_by_hash: (store_id: string, content_hash: string) => Promise<SnapshotMeta | null>;
|
|
229
231
|
};
|
|
232
|
+
/** @internal */
|
|
230
233
|
export type DataClient = {
|
|
231
234
|
get: (data_key: string) => Promise<Result<DataHandle, CorpusError>>;
|
|
232
235
|
put: (data_key: string, data: ReadableStream<Uint8Array> | Uint8Array) => Promise<Result<void, CorpusError>>;
|
|
@@ -301,6 +304,34 @@ export type Codec<T> = {
|
|
|
301
304
|
encode: (value: T) => Uint8Array;
|
|
302
305
|
decode: (bytes: Uint8Array) => T;
|
|
303
306
|
};
|
|
307
|
+
/**
|
|
308
|
+
* Structural type for schema validators (Zod, Valibot, or custom).
|
|
309
|
+
*
|
|
310
|
+
* Any object with a `parse(data: unknown) => T` method satisfies this interface.
|
|
311
|
+
* Used by `json_codec()` to validate data on decode without importing a specific library.
|
|
312
|
+
*
|
|
313
|
+
* @category Types
|
|
314
|
+
* @group Codec Types
|
|
315
|
+
* @example
|
|
316
|
+
* ```ts
|
|
317
|
+
* import { z } from 'zod'
|
|
318
|
+
*
|
|
319
|
+
* // Zod schemas satisfy Parser<T>
|
|
320
|
+
* const UserSchema = z.object({ name: z.string() })
|
|
321
|
+
* const codec = json_codec(UserSchema) // works!
|
|
322
|
+
*
|
|
323
|
+
* // Custom parsers work too
|
|
324
|
+
* const myParser: Parser<number> = {
|
|
325
|
+
* parse: (data) => {
|
|
326
|
+
* if (typeof data !== 'number') throw new Error('Expected number')
|
|
327
|
+
* return data
|
|
328
|
+
* }
|
|
329
|
+
* }
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
export type Parser<T> = {
|
|
333
|
+
parse: (data: unknown) => T;
|
|
334
|
+
};
|
|
304
335
|
/**
|
|
305
336
|
* A typed store for managing versioned data snapshots.
|
|
306
337
|
*
|
|
@@ -335,6 +366,7 @@ export type PutOpts = {
|
|
|
335
366
|
};
|
|
336
367
|
/**
|
|
337
368
|
* Context passed to data_key_fn for generating custom storage paths.
|
|
369
|
+
* @internal
|
|
338
370
|
*/
|
|
339
371
|
export type DataKeyContext = {
|
|
340
372
|
store_id: string;
|
|
@@ -397,6 +429,7 @@ export type DefineStoreOpts = {
|
|
|
397
429
|
* ```
|
|
398
430
|
*/
|
|
399
431
|
export declare function define_store<Id extends string, T>(id: Id, codec: Codec<T>, opts?: DefineStoreOpts | string): StoreDefinition<Id, T>;
|
|
432
|
+
/** @internal */
|
|
400
433
|
export type CorpusBuilder<Stores extends Record<string, Store<any>> = {}> = {
|
|
401
434
|
with_backend: (backend: Backend) => CorpusBuilder<Stores>;
|
|
402
435
|
with_store: <Id extends string, T>(definition: StoreDefinition<Id, T>) => CorpusBuilder<Stores & Record<Id, Store<T>>>;
|