wellcrafted 0.22.0 → 0.23.1
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/README.md +184 -38
- package/dist/error/index.d.ts +93 -39
- package/dist/error/index.d.ts.map +1 -1
- package/dist/error/index.js +30 -10
- package/dist/error/index.js.map +1 -1
- package/dist/query/index.d.ts +3 -3
- package/dist/query/index.js +6 -6
- package/dist/query/index.js.map +1 -1
- package/package.json +55 -55
package/README.md
CHANGED
|
@@ -62,6 +62,34 @@ const { ApiError, ApiErr } = createTaggedError("ApiError");
|
|
|
62
62
|
// ApiError() creates error object, ApiErr() creates Err-wrapped error
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
+
### 🔄 Query Integration
|
|
66
|
+
Seamless TanStack Query integration with dual interfaces
|
|
67
|
+
```typescript
|
|
68
|
+
import { createQueryFactories } from "wellcrafted/query";
|
|
69
|
+
import { QueryClient } from "@tanstack/query-core";
|
|
70
|
+
|
|
71
|
+
const queryClient = new QueryClient();
|
|
72
|
+
const { defineQuery, defineMutation } = createQueryFactories(queryClient);
|
|
73
|
+
|
|
74
|
+
// Define operations that return Result types
|
|
75
|
+
const userQuery = defineQuery({
|
|
76
|
+
queryKey: ['users', userId],
|
|
77
|
+
resultQueryFn: () => getUserFromAPI(userId) // Returns Result<User, ApiError>
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Use reactively in components with automatic state management
|
|
81
|
+
const query = createQuery(userQuery.options());
|
|
82
|
+
// query.data, query.error, query.isPending all managed automatically
|
|
83
|
+
|
|
84
|
+
// Or use imperatively for direct execution (perfect for event handlers)
|
|
85
|
+
const { data, error } = await userQuery.fetch();
|
|
86
|
+
if (error) {
|
|
87
|
+
showErrorToast(error.message);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// Use data...
|
|
91
|
+
```
|
|
92
|
+
|
|
65
93
|
## Installation
|
|
66
94
|
|
|
67
95
|
```bash
|
|
@@ -81,7 +109,7 @@ type ApiError = ReturnType<typeof ApiError>;
|
|
|
81
109
|
// Wrap any throwing operation
|
|
82
110
|
const { data, error } = await tryAsync({
|
|
83
111
|
try: () => fetch('/api/user').then(r => r.json()),
|
|
84
|
-
|
|
112
|
+
catch: (error) => ApiErr({
|
|
85
113
|
message: "Failed to fetch user",
|
|
86
114
|
context: { endpoint: '/api/user' },
|
|
87
115
|
cause: error
|
|
@@ -145,13 +173,12 @@ Mix and match utilities
|
|
|
145
173
|
The Result type makes error handling explicit and type-safe:
|
|
146
174
|
|
|
147
175
|
```typescript
|
|
148
|
-
// The entire implementation
|
|
149
176
|
type Ok<T> = { data: T; error: null };
|
|
150
177
|
type Err<E> = { error: E; data: null };
|
|
151
178
|
type Result<T, E> = Ok<T> | Err<E>;
|
|
152
179
|
```
|
|
153
180
|
|
|
154
|
-
|
|
181
|
+
This creates a discriminated union where TypeScript automatically narrows types:
|
|
155
182
|
|
|
156
183
|
```typescript
|
|
157
184
|
if (result.error) {
|
|
@@ -182,7 +209,7 @@ if (error) {
|
|
|
182
209
|
// Synchronous
|
|
183
210
|
const result = trySync({
|
|
184
211
|
try: () => JSON.parse(jsonString),
|
|
185
|
-
|
|
212
|
+
catch: (error) => Err({
|
|
186
213
|
name: "ParseError",
|
|
187
214
|
message: "Invalid JSON",
|
|
188
215
|
context: { input: jsonString },
|
|
@@ -193,7 +220,7 @@ const result = trySync({
|
|
|
193
220
|
// Asynchronous
|
|
194
221
|
const result = await tryAsync({
|
|
195
222
|
try: () => fetch(url),
|
|
196
|
-
|
|
223
|
+
catch: (error) => Err({
|
|
197
224
|
name: "NetworkError",
|
|
198
225
|
message: "Request failed",
|
|
199
226
|
context: { url },
|
|
@@ -202,62 +229,162 @@ const result = await tryAsync({
|
|
|
202
229
|
});
|
|
203
230
|
```
|
|
204
231
|
|
|
205
|
-
### Service Layer Example
|
|
232
|
+
### Real-World Service + Query Layer Example
|
|
206
233
|
|
|
207
234
|
```typescript
|
|
208
|
-
|
|
235
|
+
// 1. Service Layer - Pure business logic
|
|
209
236
|
import { createTaggedError } from "wellcrafted/error";
|
|
237
|
+
import { tryAsync, Result } from "wellcrafted/result";
|
|
210
238
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const { DatabaseError, DatabaseErr } = createTaggedError("DatabaseError");
|
|
239
|
+
const { RecorderServiceError, RecorderServiceErr } = createTaggedError("RecorderServiceError");
|
|
240
|
+
type RecorderServiceError = ReturnType<typeof RecorderServiceError>;
|
|
214
241
|
|
|
215
|
-
|
|
216
|
-
|
|
242
|
+
export function createRecorderService() {
|
|
243
|
+
let isRecording = false;
|
|
244
|
+
let currentBlob: Blob | null = null;
|
|
217
245
|
|
|
218
|
-
// Factory function pattern - no classes!
|
|
219
|
-
export function createUserService(db: Database) {
|
|
220
246
|
return {
|
|
221
|
-
async
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
context: { field: 'email', value: input.email },
|
|
247
|
+
async startRecording(): Promise<Result<void, RecorderServiceError>> {
|
|
248
|
+
if (isRecording) {
|
|
249
|
+
return RecorderServiceErr({
|
|
250
|
+
message: "Already recording",
|
|
251
|
+
context: { currentState: 'recording' },
|
|
227
252
|
cause: undefined
|
|
228
253
|
});
|
|
229
254
|
}
|
|
230
255
|
|
|
231
256
|
return tryAsync({
|
|
232
|
-
try: () =>
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
257
|
+
try: async () => {
|
|
258
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
259
|
+
const recorder = new MediaRecorder(stream);
|
|
260
|
+
// ... recording setup
|
|
261
|
+
isRecording = true;
|
|
262
|
+
},
|
|
263
|
+
catch: (error) => RecorderServiceErr({
|
|
264
|
+
message: "Failed to start recording",
|
|
265
|
+
context: { permissions: 'microphone' },
|
|
236
266
|
cause: error
|
|
237
267
|
})
|
|
238
268
|
});
|
|
239
269
|
},
|
|
240
270
|
|
|
241
|
-
async
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
271
|
+
async stopRecording(): Promise<Result<Blob, RecorderServiceError>> {
|
|
272
|
+
if (!isRecording) {
|
|
273
|
+
return RecorderServiceErr({
|
|
274
|
+
message: "Not currently recording",
|
|
275
|
+
context: { currentState: 'idle' },
|
|
276
|
+
cause: undefined
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Stop recording and return blob...
|
|
281
|
+
isRecording = false;
|
|
282
|
+
return Ok(currentBlob!);
|
|
250
283
|
}
|
|
251
284
|
};
|
|
252
285
|
}
|
|
253
286
|
|
|
254
|
-
//
|
|
255
|
-
|
|
287
|
+
// 2. Query Layer - Adds caching, reactivity, and UI error handling
|
|
288
|
+
import { createQueryFactories } from "wellcrafted/query";
|
|
289
|
+
|
|
290
|
+
const { defineQuery, defineMutation } = createQueryFactories(queryClient);
|
|
291
|
+
|
|
292
|
+
export const recorder = {
|
|
293
|
+
getRecorderState: defineQuery({
|
|
294
|
+
queryKey: ['recorder', 'state'],
|
|
295
|
+
resultQueryFn: async () => {
|
|
296
|
+
const { data, error } = await services.recorder.getState();
|
|
297
|
+
if (error) {
|
|
298
|
+
// Transform service error to UI-friendly error
|
|
299
|
+
return Err({
|
|
300
|
+
title: "❌ Failed to get recorder state",
|
|
301
|
+
description: error.message,
|
|
302
|
+
action: { type: 'retry' }
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
return Ok(data);
|
|
306
|
+
},
|
|
307
|
+
refetchInterval: 1000, // Poll for state changes
|
|
308
|
+
}),
|
|
309
|
+
|
|
310
|
+
startRecording: defineMutation({
|
|
311
|
+
mutationKey: ['recorder', 'start'],
|
|
312
|
+
resultMutationFn: async () => {
|
|
313
|
+
const { error } = await services.recorder.startRecording();
|
|
314
|
+
if (error) {
|
|
315
|
+
return Err({
|
|
316
|
+
title: "❌ Failed to start recording",
|
|
317
|
+
description: error.message,
|
|
318
|
+
action: { type: 'more-details', error }
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Optimistically update cache
|
|
323
|
+
queryClient.setQueryData(['recorder', 'state'], 'recording');
|
|
324
|
+
return Ok(undefined);
|
|
325
|
+
}
|
|
326
|
+
})
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// 3. Component Usage - Choose reactive or imperative based on needs
|
|
330
|
+
// Reactive: Automatic state management
|
|
331
|
+
const recorderState = createQuery(recorder.getRecorderState.options());
|
|
332
|
+
|
|
333
|
+
// Imperative: Direct execution for event handlers
|
|
334
|
+
async function handleStartRecording() {
|
|
335
|
+
const { error } = await recorder.startRecording.execute();
|
|
336
|
+
if (error) {
|
|
337
|
+
showToast(error.title, { description: error.description });
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Smart Return Type Narrowing
|
|
343
|
+
|
|
344
|
+
The `catch` parameter in `trySync` and `tryAsync` enables smart return type narrowing based on your error handling strategy:
|
|
345
|
+
|
|
346
|
+
### Recovery Pattern (Always Succeeds)
|
|
347
|
+
```typescript
|
|
348
|
+
// When catch always returns Ok<T>, function returns Ok<T>
|
|
349
|
+
const alwaysSucceeds = trySync({
|
|
350
|
+
try: () => JSON.parse(riskyJson),
|
|
351
|
+
catch: () => Ok({ fallback: "default" }) // Always recover with fallback
|
|
352
|
+
});
|
|
353
|
+
// alwaysSucceeds: Ok<object> - No error checking needed!
|
|
354
|
+
console.log(alwaysSucceeds.data); // Safe to access directly
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Propagation Pattern (May Fail)
|
|
358
|
+
```typescript
|
|
359
|
+
// When catch can return Err<E>, function returns Result<T, E>
|
|
360
|
+
const mayFail = trySync({
|
|
361
|
+
try: () => JSON.parse(riskyJson),
|
|
362
|
+
catch: (error) => Err(ParseError({ message: "Invalid JSON", cause: error }))
|
|
363
|
+
});
|
|
364
|
+
// mayFail: Result<object, ParseError> - Must check for errors
|
|
365
|
+
if (isOk(mayFail)) {
|
|
366
|
+
console.log(mayFail.data); // Only safe after checking
|
|
367
|
+
}
|
|
368
|
+
```
|
|
256
369
|
|
|
257
|
-
|
|
258
|
-
|
|
370
|
+
### Mixed Strategy (Conditional Recovery)
|
|
371
|
+
```typescript
|
|
372
|
+
const smartParse = trySync({
|
|
373
|
+
try: () => JSON.parse(input),
|
|
374
|
+
catch: (error) => {
|
|
375
|
+
// Recover from empty input
|
|
376
|
+
if (input.trim() === "") {
|
|
377
|
+
return Ok({}); // Return Ok<T> for fallback
|
|
378
|
+
}
|
|
379
|
+
// Propagate other errors
|
|
380
|
+
return Err(ParseError({ message: "Parse failed", cause: error }));
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
// smartParse: Result<object, ParseError> - Mixed handling = Result type
|
|
259
384
|
```
|
|
260
385
|
|
|
386
|
+
This eliminates unnecessary error checking when you always recover, while still requiring proper error handling when failures are possible.
|
|
387
|
+
|
|
261
388
|
## Why wellcrafted?
|
|
262
389
|
|
|
263
390
|
JavaScript's `try-catch` has fundamental problems:
|
|
@@ -447,11 +574,16 @@ function useUser(id: number) {
|
|
|
447
574
|
|
|
448
575
|
| | wellcrafted | fp-ts | Effect | neverthrow |
|
|
449
576
|
|---|---|---|---|---|
|
|
577
|
+
| **Bundle Size** | < 2KB | ~30KB | ~50KB | ~5KB |
|
|
450
578
|
| **Learning Curve** | Minimal | Steep | Steep | Moderate |
|
|
451
579
|
| **Syntax** | Native async/await | Pipe operators | Generators | Method chains |
|
|
452
|
-
| **Bundle Size** | < 2KB | ~30KB | ~50KB | ~5KB |
|
|
453
580
|
| **Type Safety** | ✅ Full | ✅ Full | ✅ Full | ✅ Full |
|
|
454
581
|
| **Serializable Errors** | ✅ Built-in | ❌ Classes | ❌ Classes | ❌ Classes |
|
|
582
|
+
| **Runtime Overhead** | Zero | Minimal | Moderate | Minimal |
|
|
583
|
+
|
|
584
|
+
## Advanced Usage
|
|
585
|
+
|
|
586
|
+
For comprehensive examples, service layer patterns, framework integrations, and migration guides, see the **[full documentation →](https://docs.wellcrafted.dev)**
|
|
455
587
|
|
|
456
588
|
## API Reference
|
|
457
589
|
|
|
@@ -462,18 +594,32 @@ function useUser(id: number) {
|
|
|
462
594
|
- **`isErr(result)`** - Type guard for failure
|
|
463
595
|
- **`trySync(options)`** - Wrap throwing function
|
|
464
596
|
- **`tryAsync(options)`** - Wrap async function
|
|
597
|
+
- **`unwrap(result)`** - Extract data or throw error
|
|
598
|
+
- **`resolve(value)`** - Handle values that may or may not be Results
|
|
465
599
|
- **`partitionResults(results)`** - Split array into oks/errs
|
|
466
600
|
|
|
601
|
+
### Query Functions
|
|
602
|
+
- **`createQueryFactories(client)`** - Create query/mutation factories for TanStack Query
|
|
603
|
+
- **`defineQuery(options)`** - Define a query with dual interface (`.options()` + `.fetch()`)
|
|
604
|
+
- **`defineMutation(options)`** - Define a mutation with dual interface (`.options()` + `.execute()`)
|
|
605
|
+
|
|
467
606
|
### Error Functions
|
|
468
607
|
- **`createTaggedError(name)`** - Creates error factory functions
|
|
469
608
|
- Returns two functions: `{ErrorName}` and `{ErrorName}Err`
|
|
470
609
|
- The first creates plain error objects
|
|
471
610
|
- The second creates Err-wrapped errors
|
|
611
|
+
- **`extractErrorMessage(error)`** - Extract readable message from unknown error
|
|
472
612
|
|
|
473
613
|
### Types
|
|
474
614
|
- **`Result<T, E>`** - Union of Ok<T> | Err<E>
|
|
615
|
+
- **`Ok<T>`** - Success result type
|
|
616
|
+
- **`Err<E>`** - Error result type
|
|
475
617
|
- **`TaggedError<T>`** - Structured error type
|
|
476
618
|
- **`Brand<T, B>`** - Branded type wrapper
|
|
619
|
+
- **`ExtractOkFromResult<R>`** - Extract Ok variant from Result union
|
|
620
|
+
- **`ExtractErrFromResult<R>`** - Extract Err variant from Result union
|
|
621
|
+
- **`UnwrapOk<R>`** - Extract success value type from Result
|
|
622
|
+
- **`UnwrapErr<R>`** - Extract error value type from Result
|
|
477
623
|
|
|
478
624
|
## License
|
|
479
625
|
|
package/dist/error/index.d.ts
CHANGED
|
@@ -2,40 +2,84 @@ import { Err } from "../result-DRq8PMRe.js";
|
|
|
2
2
|
|
|
3
3
|
//#region src/error/types.d.ts
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* Base error structure for all errors in the Result system.
|
|
7
|
-
*
|
|
8
|
-
* Provides a consistent structure for error objects that are JSON-serializable
|
|
9
|
-
* and contain comprehensive debugging information.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```ts
|
|
13
|
-
* const error: BaseError = {
|
|
14
|
-
* name: "DatabaseError",
|
|
15
|
-
* message: "Connection failed",
|
|
16
|
-
* context: { host: "localhost", port: 5432 },
|
|
17
|
-
* cause: originalError
|
|
18
|
-
* };
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
type BaseError = Readonly<{
|
|
22
|
-
name: string;
|
|
23
|
-
message: string;
|
|
24
|
-
context?: Record<string, unknown>;
|
|
25
|
-
cause: unknown;
|
|
26
|
-
}>;
|
|
27
5
|
/**
|
|
28
6
|
* Creates a tagged error type for type-safe error handling.
|
|
29
7
|
* Uses the `name` property as a discriminator for tagged unions.
|
|
30
8
|
*
|
|
9
|
+
* The `cause` property enables error chaining, creating a JSON-serializable
|
|
10
|
+
* call stack. Each error wraps its cause, building a complete trace of how
|
|
11
|
+
* an error propagated through your application layers.
|
|
12
|
+
*
|
|
31
13
|
* Error types should follow the convention of ending with "Error" suffix.
|
|
32
14
|
* The Err data structure wraps these error types in the Result system.
|
|
33
15
|
*
|
|
34
16
|
* @example
|
|
35
17
|
* ```ts
|
|
36
|
-
*
|
|
37
|
-
* type
|
|
18
|
+
* // Simple error without cause
|
|
19
|
+
* type ValidationError = TaggedError<"ValidationError">;
|
|
20
|
+
* const validationError: ValidationError = {
|
|
21
|
+
* name: "ValidationError",
|
|
22
|
+
* message: "Input is required"
|
|
23
|
+
* };
|
|
38
24
|
*
|
|
25
|
+
* // Type-safe error chaining with specific cause types
|
|
26
|
+
* type NetworkError = TaggedError<"NetworkError">;
|
|
27
|
+
* type DatabaseError = TaggedError<"DatabaseError", NetworkError>;
|
|
28
|
+
* type ServiceError = TaggedError<"ServiceError", DatabaseError>;
|
|
29
|
+
* type APIError = TaggedError<"APIError", ServiceError>;
|
|
30
|
+
*
|
|
31
|
+
* const networkError: NetworkError = {
|
|
32
|
+
* name: "NetworkError",
|
|
33
|
+
* message: "Socket timeout after 5000ms",
|
|
34
|
+
* context: { host: "db.example.com", port: 5432, timeout: 5000 }
|
|
35
|
+
* };
|
|
36
|
+
*
|
|
37
|
+
* const dbError: DatabaseError = {
|
|
38
|
+
* name: "DatabaseError",
|
|
39
|
+
* message: "Failed to connect to database",
|
|
40
|
+
* context: { operation: "connect", retries: 3 },
|
|
41
|
+
* cause: networkError // TypeScript enforces this must be NetworkError
|
|
42
|
+
* };
|
|
43
|
+
*
|
|
44
|
+
* const serviceError: ServiceError = {
|
|
45
|
+
* name: "UserServiceError",
|
|
46
|
+
* message: "Could not fetch user profile",
|
|
47
|
+
* context: { userId: "123", method: "getUserById" },
|
|
48
|
+
* cause: dbError // TypeScript enforces this must be DatabaseError
|
|
49
|
+
* };
|
|
50
|
+
*
|
|
51
|
+
* const apiError: APIError = {
|
|
52
|
+
* name: "APIError",
|
|
53
|
+
* message: "Internal server error",
|
|
54
|
+
* context: { endpoint: "/api/users/123", statusCode: 500 },
|
|
55
|
+
* cause: serviceError // TypeScript enforces this must be ServiceError
|
|
56
|
+
* };
|
|
57
|
+
*
|
|
58
|
+
* // The entire error chain is JSON-serializable and type-safe
|
|
59
|
+
* console.log(JSON.stringify(apiError, null, 2));
|
|
60
|
+
* // Outputs:
|
|
61
|
+
* // {
|
|
62
|
+
* // "name": "APIError",
|
|
63
|
+
* // "message": "Internal server error",
|
|
64
|
+
* // "context": { "endpoint": "/api/users/123", "statusCode": 500 },
|
|
65
|
+
* // "cause": {
|
|
66
|
+
* // "name": "UserServiceError",
|
|
67
|
+
* // "message": "Could not fetch user profile",
|
|
68
|
+
* // "context": { "userId": "123", "method": "getUserById" },
|
|
69
|
+
* // "cause": {
|
|
70
|
+
* // "name": "DatabaseError",
|
|
71
|
+
* // "message": "Failed to connect to database",
|
|
72
|
+
* // "context": { "operation": "connect", "retries": 3 },
|
|
73
|
+
* // "cause": {
|
|
74
|
+
* // "name": "NetworkError",
|
|
75
|
+
* // "message": "Socket timeout after 5000ms",
|
|
76
|
+
* // "context": { "host": "db.example.com", "port": 5432, "timeout": 5000 }
|
|
77
|
+
* // }
|
|
78
|
+
* // }
|
|
79
|
+
* // }
|
|
80
|
+
* // }
|
|
81
|
+
*
|
|
82
|
+
* // Discriminated unions still work
|
|
39
83
|
* function handleError(error: ValidationError | NetworkError) {
|
|
40
84
|
* switch (error.name) {
|
|
41
85
|
* case "ValidationError": // TypeScript knows this is ValidationError
|
|
@@ -51,8 +95,7 @@ type BaseError = Readonly<{
|
|
|
51
95
|
* return Err({
|
|
52
96
|
* name: "ValidationError",
|
|
53
97
|
* message: "Input is required",
|
|
54
|
-
* context: { input }
|
|
55
|
-
* cause: null,
|
|
98
|
+
* context: { input }
|
|
56
99
|
* });
|
|
57
100
|
* }
|
|
58
101
|
* return Ok(input);
|
|
@@ -63,17 +106,19 @@ type BaseError = Readonly<{
|
|
|
63
106
|
* if (!isAuthenticated) {
|
|
64
107
|
* return Err({
|
|
65
108
|
* name: "AuthError",
|
|
66
|
-
* message: "User not authenticated"
|
|
67
|
-
* cause: null,
|
|
109
|
+
* message: "User not authenticated"
|
|
68
110
|
* });
|
|
69
111
|
* }
|
|
70
112
|
* return Ok(currentUser);
|
|
71
113
|
* }
|
|
72
114
|
* ```
|
|
73
115
|
*/
|
|
74
|
-
type TaggedError<
|
|
75
|
-
|
|
76
|
-
|
|
116
|
+
type TaggedError<TName extends string = string, TCause extends TaggedError<string, any> = TaggedError<string, any>> = Readonly<{
|
|
117
|
+
name: TName;
|
|
118
|
+
message: string;
|
|
119
|
+
context?: Record<string, unknown>;
|
|
120
|
+
cause?: TCause;
|
|
121
|
+
}>;
|
|
77
122
|
//# sourceMappingURL=types.d.ts.map
|
|
78
123
|
//#endregion
|
|
79
124
|
//#region src/error/utils.d.ts
|
|
@@ -116,7 +161,7 @@ declare function extractErrorMessage(error: unknown): string;
|
|
|
116
161
|
/**
|
|
117
162
|
* Input type for creating a tagged error (everything except the name)
|
|
118
163
|
*/
|
|
119
|
-
type TaggedErrorWithoutName<
|
|
164
|
+
type TaggedErrorWithoutName<TName extends string, TCause extends TaggedError<string, any> = TaggedError<string, any>> = Omit<TaggedError<TName, TCause>, "name">;
|
|
120
165
|
/**
|
|
121
166
|
* Replaces the "Error" suffix with "Err" suffix in error type names.
|
|
122
167
|
*
|
|
@@ -141,7 +186,7 @@ type ReplaceErrorWithErr<T extends `${string}Error`> = T extends `${infer TBase}
|
|
|
141
186
|
* - One that creates plain TaggedError objects (named with "Error" suffix)
|
|
142
187
|
* - One that creates Err-wrapped TaggedError objects (named with "Err" suffix)
|
|
143
188
|
*/
|
|
144
|
-
type TaggedErrorFactories<TErrorName extends `${string}Error
|
|
189
|
+
type TaggedErrorFactories<TErrorName extends `${string}Error`, TCause extends TaggedError<string, any> = TaggedError<string, any>> = { [K in TErrorName]: (input: TaggedErrorWithoutName<K, TCause>) => TaggedError<K, TCause> } & { [K in ReplaceErrorWithErr<TErrorName>]: (input: TaggedErrorWithoutName<TErrorName, TCause>) => Err<TaggedError<TErrorName, TCause>> };
|
|
145
190
|
/**
|
|
146
191
|
* Returns two different factory functions for tagged errors.
|
|
147
192
|
*
|
|
@@ -159,26 +204,35 @@ type TaggedErrorFactories<TErrorName extends `${string}Error`> = { [K in TErrorN
|
|
|
159
204
|
*
|
|
160
205
|
* @example
|
|
161
206
|
* ```ts
|
|
207
|
+
* // Simple error without typed cause
|
|
162
208
|
* const { NetworkError, NetworkErr } = createTaggedError('NetworkError');
|
|
163
209
|
*
|
|
164
210
|
* // NetworkError: Creates just the error object
|
|
165
211
|
* const error = NetworkError({
|
|
166
212
|
* message: 'Connection failed',
|
|
167
|
-
* context: { url: 'https://api.example.com' }
|
|
168
|
-
* cause: undefined
|
|
213
|
+
* context: { url: 'https://api.example.com' }
|
|
169
214
|
* });
|
|
170
215
|
* // Returns: { name: 'NetworkError', message: 'Connection failed', ... }
|
|
171
216
|
*
|
|
172
217
|
* // NetworkErr: Creates error and wraps in Err result
|
|
173
218
|
* return NetworkErr({
|
|
174
219
|
* message: 'Connection failed',
|
|
175
|
-
* context: { url: 'https://api.example.com' }
|
|
176
|
-
* cause: undefined
|
|
220
|
+
* context: { url: 'https://api.example.com' }
|
|
177
221
|
* });
|
|
178
222
|
* // Returns: Err({ name: 'NetworkError', message: 'Connection failed', ... })
|
|
223
|
+
*
|
|
224
|
+
* // Type-safe error chaining with specific cause types
|
|
225
|
+
* type NetworkError = TaggedError<"NetworkError">;
|
|
226
|
+
* const { DatabaseError, DatabaseErr } = createTaggedError<"DatabaseError", NetworkError>('DatabaseError');
|
|
227
|
+
*
|
|
228
|
+
* const networkError: NetworkError = { name: "NetworkError", message: "Timeout" };
|
|
229
|
+
* const dbError = DatabaseError({
|
|
230
|
+
* message: 'Connection failed',
|
|
231
|
+
* cause: networkError // TypeScript enforces NetworkError type
|
|
232
|
+
* });
|
|
179
233
|
* ```
|
|
180
234
|
*/
|
|
181
|
-
declare function createTaggedError<TErrorName extends `${string}Error
|
|
235
|
+
declare function createTaggedError<TErrorName extends `${string}Error`, TCause extends TaggedError<string, any> = TaggedError<string, any>>(name: TErrorName): TaggedErrorFactories<TErrorName, TCause>;
|
|
182
236
|
//#endregion
|
|
183
|
-
export {
|
|
237
|
+
export { TaggedError, createTaggedError, extractErrorMessage };
|
|
184
238
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/error/types.ts","../../src/error/utils.ts"],"sourcesContent":[],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/error/types.ts","../../src/error/utils.ts"],"sourcesContent":[],"mappings":";;;;;;;AA+GA;;;;;;;;AAGY;;;;AC5EZ;AAkDC;;;;;;;;AAQO;AAAA;;;;AAmBmC;AAAA;;;;;;;;;;;;;;;;;;;;AAmBlC;AAgDT;;;;;;;;AAGyC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KD1E7B,0DAEI,2BAA2B,4BACvC;QACG;;YAEI;UACF;;;;;;AAPT;;;;;;;;AAGY;;;;AC5EZ;AAkDC;;;;;;;;AAQO;AAAA;;;;AAmBmC;AAAA;;;;;;;AAclC,iBA3FO,mBAAA,CA2FP,KAAA,EAAA,OAAA,CAAA,EAAA,MAAA;;;;KApCJ,sBAuCsB,CAAA,cAAA,MAAA,EAAA,eArCX,WAqCW,CAAA,MAAA,EAAA,GAAA,CAAA,GArCgB,WAqChB,CAAA,MAAA,EAAA,GAAA,CAAA,CAAA,GApCvB,IAoCuB,CApClB,WAoCkB,CApCN,KAoCM,EApCC,MAoCD,CAAA,EAAA,MAAA,CAAA;;;;;;;;;AAElB;AAgDT;;;;;;;KApEK,mBAuEgB,CAAA,UAAA,GAAA,MAAA,OAAA,CAAA,GAtEpB,CAsEoB,SAAA,GAAA,KAAA,MAAA,OAAA,GAAA,GAtEiB,KAsEjB,KAAA,GAAA,KAAA;AAAoB;;;;;;;KA7DpC,yEAEW,2BAA2B,oCAEpC,qBACE,uBAAuB,GAAG,YAC7B,YAAY,GAAG,oBAEd,oBAAoB,sBAClB,uBAAuB,YAAY,YACtC,IAAI,YAAY,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgDlB,sEAEA,2BAA2B,gCACnC,aAAa,qBAAqB,YAAY"}
|
package/dist/error/index.js
CHANGED
|
@@ -39,11 +39,22 @@ import { Err } from "../result-B1iWFqM9.js";
|
|
|
39
39
|
function extractErrorMessage(error) {
|
|
40
40
|
if (error instanceof Error) return error.message;
|
|
41
41
|
if (typeof error === "string") return error;
|
|
42
|
-
if (typeof error === "
|
|
42
|
+
if (typeof error === "number" || typeof error === "boolean" || typeof error === "bigint") return String(error);
|
|
43
|
+
if (typeof error === "symbol") return error.toString();
|
|
44
|
+
if (error === null) return "null";
|
|
45
|
+
if (error === void 0) return "undefined";
|
|
46
|
+
if (Array.isArray(error)) return JSON.stringify(error);
|
|
47
|
+
if (typeof error === "object") {
|
|
43
48
|
const errorObj = error;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
const messageProps = [
|
|
50
|
+
"message",
|
|
51
|
+
"error",
|
|
52
|
+
"description",
|
|
53
|
+
"title",
|
|
54
|
+
"reason",
|
|
55
|
+
"details"
|
|
56
|
+
];
|
|
57
|
+
for (const prop of messageProps) if (prop in errorObj && typeof errorObj[prop] === "string") return errorObj[prop];
|
|
47
58
|
try {
|
|
48
59
|
return JSON.stringify(error);
|
|
49
60
|
} catch {
|
|
@@ -69,29 +80,38 @@ function extractErrorMessage(error) {
|
|
|
69
80
|
*
|
|
70
81
|
* @example
|
|
71
82
|
* ```ts
|
|
83
|
+
* // Simple error without typed cause
|
|
72
84
|
* const { NetworkError, NetworkErr } = createTaggedError('NetworkError');
|
|
73
85
|
*
|
|
74
86
|
* // NetworkError: Creates just the error object
|
|
75
87
|
* const error = NetworkError({
|
|
76
88
|
* message: 'Connection failed',
|
|
77
|
-
* context: { url: 'https://api.example.com' }
|
|
78
|
-
* cause: undefined
|
|
89
|
+
* context: { url: 'https://api.example.com' }
|
|
79
90
|
* });
|
|
80
91
|
* // Returns: { name: 'NetworkError', message: 'Connection failed', ... }
|
|
81
92
|
*
|
|
82
93
|
* // NetworkErr: Creates error and wraps in Err result
|
|
83
94
|
* return NetworkErr({
|
|
84
95
|
* message: 'Connection failed',
|
|
85
|
-
* context: { url: 'https://api.example.com' }
|
|
86
|
-
* cause: undefined
|
|
96
|
+
* context: { url: 'https://api.example.com' }
|
|
87
97
|
* });
|
|
88
98
|
* // Returns: Err({ name: 'NetworkError', message: 'Connection failed', ... })
|
|
99
|
+
*
|
|
100
|
+
* // Type-safe error chaining with specific cause types
|
|
101
|
+
* type NetworkError = TaggedError<"NetworkError">;
|
|
102
|
+
* const { DatabaseError, DatabaseErr } = createTaggedError<"DatabaseError", NetworkError>('DatabaseError');
|
|
103
|
+
*
|
|
104
|
+
* const networkError: NetworkError = { name: "NetworkError", message: "Timeout" };
|
|
105
|
+
* const dbError = DatabaseError({
|
|
106
|
+
* message: 'Connection failed',
|
|
107
|
+
* cause: networkError // TypeScript enforces NetworkError type
|
|
108
|
+
* });
|
|
89
109
|
* ```
|
|
90
110
|
*/
|
|
91
111
|
function createTaggedError(name) {
|
|
92
112
|
const errorConstructor = (error) => ({
|
|
93
|
-
|
|
94
|
-
|
|
113
|
+
name,
|
|
114
|
+
...error
|
|
95
115
|
});
|
|
96
116
|
const errName = name.replace(/Error$/, "Err");
|
|
97
117
|
const errConstructor = (error) => Err(errorConstructor(error));
|
package/dist/error/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["error: unknown","name: TErrorName","error: TaggedErrorWithoutName<TErrorName>"],"sources":["../../src/error/utils.ts"],"sourcesContent":["import type { TaggedError } from \"./types.js\";\nimport { Err } from \"../result/result.js\";\n\n/**\n * Extracts a readable error message from an unknown error value\n *\n * This utility is commonly used in mapErr functions when converting\n * unknown errors to typed error objects in the Result system.\n *\n * @param error - The unknown error to extract a message from\n * @returns A string representation of the error\n *\n * @example\n * ```ts\n * // With native Error\n * const error = new Error(\"Something went wrong\");\n * const message = extractErrorMessage(error); // \"Something went wrong\"\n *\n * // With string error\n * const stringError = \"String error\";\n * const message2 = extractErrorMessage(stringError); // \"String error\"\n *\n * // With object error\n * const unknownError = { code: 500, details: \"Server error\" };\n * const message3 = extractErrorMessage(unknownError); // '{\"code\":500,\"details\":\"Server error\"}'\n *\n * // Used in mapErr function\n * const result = await tryAsync({\n * try: () => riskyOperation(),\n * mapErr: (error) => Err({\n * name: \"NetworkError\",\n * message: extractErrorMessage(error),\n * context: { operation: \"riskyOperation\" },\n * cause: error,\n * }),\n * });\n * ```\n */\nexport function extractErrorMessage(error: unknown): string {\n\tif (error instanceof Error) {\n\t\treturn error.message;\n\t}\n\n\tif (typeof error === \"string\")
|
|
1
|
+
{"version":3,"file":"index.js","names":["error: unknown","name: TErrorName","error: TaggedErrorWithoutName<TErrorName, TCause>"],"sources":["../../src/error/utils.ts"],"sourcesContent":["import type { TaggedError } from \"./types.js\";\nimport { Err } from \"../result/result.js\";\n\n/**\n * Extracts a readable error message from an unknown error value\n *\n * This utility is commonly used in mapErr functions when converting\n * unknown errors to typed error objects in the Result system.\n *\n * @param error - The unknown error to extract a message from\n * @returns A string representation of the error\n *\n * @example\n * ```ts\n * // With native Error\n * const error = new Error(\"Something went wrong\");\n * const message = extractErrorMessage(error); // \"Something went wrong\"\n *\n * // With string error\n * const stringError = \"String error\";\n * const message2 = extractErrorMessage(stringError); // \"String error\"\n *\n * // With object error\n * const unknownError = { code: 500, details: \"Server error\" };\n * const message3 = extractErrorMessage(unknownError); // '{\"code\":500,\"details\":\"Server error\"}'\n *\n * // Used in mapErr function\n * const result = await tryAsync({\n * try: () => riskyOperation(),\n * mapErr: (error) => Err({\n * name: \"NetworkError\",\n * message: extractErrorMessage(error),\n * context: { operation: \"riskyOperation\" },\n * cause: error,\n * }),\n * });\n * ```\n */\nexport function extractErrorMessage(error: unknown): string {\n\t// Handle Error instances\n\tif (error instanceof Error) {\n\t\treturn error.message;\n\t}\n\n\t// Handle primitives\n\tif (typeof error === \"string\") return error;\n\tif (\n\t\ttypeof error === \"number\" ||\n\t\ttypeof error === \"boolean\" ||\n\t\ttypeof error === \"bigint\"\n\t)\n\t\treturn String(error);\n\tif (typeof error === \"symbol\") return error.toString();\n\tif (error === null) return \"null\";\n\tif (error === undefined) return \"undefined\";\n\n\t// Handle arrays\n\tif (Array.isArray(error)) return JSON.stringify(error);\n\n\t// Handle plain objects\n\tif (typeof error === \"object\") {\n\t\tconst errorObj = error as Record<string, unknown>;\n\n\t\t// Check common error properties\n\t\tconst messageProps = [\n\t\t\t\"message\",\n\t\t\t\"error\",\n\t\t\t\"description\",\n\t\t\t\"title\",\n\t\t\t\"reason\",\n\t\t\t\"details\",\n\t\t] as const;\n\t\tfor (const prop of messageProps) {\n\t\t\tif (prop in errorObj && typeof errorObj[prop] === \"string\") {\n\t\t\t\treturn errorObj[prop];\n\t\t\t}\n\t\t}\n\n\t\t// Fallback to JSON stringification\n\t\ttry {\n\t\t\treturn JSON.stringify(error);\n\t\t} catch {\n\t\t\treturn String(error);\n\t\t}\n\t}\n\n\t// Final fallback\n\treturn String(error);\n}\n\n/**\n * Input type for creating a tagged error (everything except the name)\n */\ntype TaggedErrorWithoutName<\n\tTName extends string,\n\tTCause extends TaggedError<string, any> = TaggedError<string, any>,\n> = Omit<TaggedError<TName, TCause>, \"name\">;\n\n/**\n * Replaces the \"Error\" suffix with \"Err\" suffix in error type names.\n *\n * This utility type is used to create companion function names for error constructors\n * that return Err-wrapped results, maintaining consistent naming conventions.\n *\n * @template T - An error type name that must end with \"Error\"\n * @returns The type name with \"Error\" replaced by \"Err\"\n *\n * @example\n * ```ts\n * type NetworkErr = ReplaceErrorWithErr<\"NetworkError\">; // \"NetworkErr\"\n * type ValidationErr = ReplaceErrorWithErr<\"ValidationError\">; // \"ValidationErr\"\n * type AuthErr = ReplaceErrorWithErr<\"AuthError\">; // \"AuthErr\"\n * ```\n */\ntype ReplaceErrorWithErr<T extends `${string}Error`> =\n\tT extends `${infer TBase}Error` ? `${TBase}Err` : never;\n\n/**\n * Return type for createTaggedError - contains both factory functions.\n *\n * Provides two factory functions:\n * - One that creates plain TaggedError objects (named with \"Error\" suffix)\n * - One that creates Err-wrapped TaggedError objects (named with \"Err\" suffix)\n */\ntype TaggedErrorFactories<\n\tTErrorName extends `${string}Error`,\n\tTCause extends TaggedError<string, any> = TaggedError<string, any>,\n> = {\n\t[K in TErrorName]: (\n\t\tinput: TaggedErrorWithoutName<K, TCause>,\n\t) => TaggedError<K, TCause>;\n} & {\n\t[K in ReplaceErrorWithErr<TErrorName>]: (\n\t\tinput: TaggedErrorWithoutName<TErrorName, TCause>,\n\t) => Err<TaggedError<TErrorName, TCause>>;\n};\n\n/**\n * Returns two different factory functions for tagged errors.\n *\n * Given an error name like \"NetworkError\", this returns:\n * - `NetworkError`: Creates a plain TaggedError object\n * - `NetworkErr`: Creates a TaggedError object wrapped in an Err result\n *\n * The naming pattern automatically replaces the \"Error\" suffix with \"Err\" suffix\n * for the Result-wrapped version.\n *\n * @param name - The name of the error type (must end with \"Error\")\n * @returns An object with two factory functions:\n * - [name]: Function that creates plain TaggedError objects\n * - [name with \"Error\" replaced by \"Err\"]: Function that creates Err-wrapped TaggedError objects\n *\n * @example\n * ```ts\n * // Simple error without typed cause\n * const { NetworkError, NetworkErr } = createTaggedError('NetworkError');\n *\n * // NetworkError: Creates just the error object\n * const error = NetworkError({\n * message: 'Connection failed',\n * context: { url: 'https://api.example.com' }\n * });\n * // Returns: { name: 'NetworkError', message: 'Connection failed', ... }\n *\n * // NetworkErr: Creates error and wraps in Err result\n * return NetworkErr({\n * message: 'Connection failed',\n * context: { url: 'https://api.example.com' }\n * });\n * // Returns: Err({ name: 'NetworkError', message: 'Connection failed', ... })\n *\n * // Type-safe error chaining with specific cause types\n * type NetworkError = TaggedError<\"NetworkError\">;\n * const { DatabaseError, DatabaseErr } = createTaggedError<\"DatabaseError\", NetworkError>('DatabaseError');\n *\n * const networkError: NetworkError = { name: \"NetworkError\", message: \"Timeout\" };\n * const dbError = DatabaseError({\n * message: 'Connection failed',\n * cause: networkError // TypeScript enforces NetworkError type\n * });\n * ```\n */\nexport function createTaggedError<\n\tTErrorName extends `${string}Error`,\n\tTCause extends TaggedError<string, any> = TaggedError<string, any>,\n>(name: TErrorName): TaggedErrorFactories<TErrorName, TCause> {\n\tconst errorConstructor = (\n\t\terror: TaggedErrorWithoutName<TErrorName, TCause>,\n\t): TaggedError<TErrorName, TCause> => ({ name, ...error });\n\n\tconst errName = name.replace(\n\t\t/Error$/,\n\t\t\"Err\",\n\t) as ReplaceErrorWithErr<TErrorName>;\n\tconst errConstructor = (error: TaggedErrorWithoutName<TErrorName, TCause>) =>\n\t\tErr(errorConstructor(error));\n\n\treturn {\n\t\t[name]: errorConstructor,\n\t\t[errName]: errConstructor,\n\t} as TaggedErrorFactories<TErrorName, TCause>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAgB,oBAAoBA,OAAwB;AAE3D,KAAI,iBAAiB,MACpB,QAAO,MAAM;AAId,YAAW,UAAU,SAAU,QAAO;AACtC,YACQ,UAAU,mBACV,UAAU,oBACV,UAAU,SAEjB,QAAO,OAAO,MAAM;AACrB,YAAW,UAAU,SAAU,QAAO,MAAM,UAAU;AACtD,KAAI,UAAU,KAAM,QAAO;AAC3B,KAAI,iBAAqB,QAAO;AAGhC,KAAI,MAAM,QAAQ,MAAM,CAAE,QAAO,KAAK,UAAU,MAAM;AAGtD,YAAW,UAAU,UAAU;EAC9B,MAAM,WAAW;EAGjB,MAAM,eAAe;GACpB;GACA;GACA;GACA;GACA;GACA;EACA;AACD,OAAK,MAAM,QAAQ,aAClB,KAAI,QAAQ,mBAAmB,SAAS,UAAU,SACjD,QAAO,SAAS;AAKlB,MAAI;AACH,UAAO,KAAK,UAAU,MAAM;EAC5B,QAAO;AACP,UAAO,OAAO,MAAM;EACpB;CACD;AAGD,QAAO,OAAO,MAAM;AACpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8FD,SAAgB,kBAGdC,MAA4D;CAC7D,MAAM,mBAAmB,CACxBC,WACsC;EAAE;EAAM,GAAG;CAAO;CAEzD,MAAM,UAAU,KAAK,QACpB,UACA,MACA;CACD,MAAM,iBAAiB,CAACA,UACvB,IAAI,iBAAiB,MAAM,CAAC;AAE7B,QAAO;GACL,OAAO;GACP,UAAU;CACX;AACD"}
|
package/dist/query/index.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ type DefineQueryInput<TQueryFnData = unknown, TError = DefaultError, TData = TQu
|
|
|
24
24
|
* Output of defineQuery function.
|
|
25
25
|
*
|
|
26
26
|
* Provides both reactive and imperative interfaces for data fetching:
|
|
27
|
-
* - `options()`: Returns config for use with
|
|
27
|
+
* - `options()`: Returns config for use with useQuery() or createQuery()
|
|
28
28
|
* - `fetch()`: Always attempts to fetch data (from cache if fresh, network if stale)
|
|
29
29
|
* - `ensure()`: Guarantees data availability, preferring cached data (recommended for preloaders)
|
|
30
30
|
*
|
|
@@ -58,7 +58,7 @@ type DefineMutationInput<TData, TError, TVariables = void, TContext = unknown> =
|
|
|
58
58
|
* Output of defineMutation function.
|
|
59
59
|
*
|
|
60
60
|
* Provides both reactive and imperative interfaces for data mutations:
|
|
61
|
-
* - `options()`: Returns config for use with
|
|
61
|
+
* - `options()`: Returns config for use with useMutation() or createMutation()
|
|
62
62
|
* - `execute()`: Directly executes the mutation and returns a Result
|
|
63
63
|
*
|
|
64
64
|
* @template TData - The type of data returned by the mutation
|
|
@@ -105,7 +105,7 @@ type DefineMutationOutput<TData, TError, TVariables = void, TContext = unknown>
|
|
|
105
105
|
* });
|
|
106
106
|
*
|
|
107
107
|
* // Use in components
|
|
108
|
-
* const query = createQuery(userQuery.options
|
|
108
|
+
* const query = createQuery(userQuery.options);
|
|
109
109
|
*
|
|
110
110
|
* // Or imperatively
|
|
111
111
|
* const { data, error } = await userQuery.fetch();
|
package/dist/query/index.js
CHANGED
|
@@ -37,7 +37,7 @@ import "../result-DfuKgZo9.js";
|
|
|
37
37
|
* });
|
|
38
38
|
*
|
|
39
39
|
* // Use in components
|
|
40
|
-
* const query = createQuery(userQuery.options
|
|
40
|
+
* const query = createQuery(userQuery.options);
|
|
41
41
|
*
|
|
42
42
|
* // Or imperatively
|
|
43
43
|
* const { data, error } = await userQuery.fetch();
|
|
@@ -52,7 +52,7 @@ function createQueryFactories(queryClient) {
|
|
|
52
52
|
*
|
|
53
53
|
* ## Why use defineQuery?
|
|
54
54
|
*
|
|
55
|
-
* 1. **Dual Interface**: Provides both reactive (`.options
|
|
55
|
+
* 1. **Dual Interface**: Provides both reactive (`.options`) and imperative (`.fetch()`) APIs
|
|
56
56
|
* 2. **Automatic Error Handling**: Service functions return `Result<T, E>` types which are automatically
|
|
57
57
|
* unwrapped by TanStack Query, giving you proper error states in your components
|
|
58
58
|
* 3. **Type Safety**: Full TypeScript support with proper inference for data and error types
|
|
@@ -69,7 +69,7 @@ function createQueryFactories(queryClient) {
|
|
|
69
69
|
* @param options.* - Any other TanStack Query options (staleTime, refetchInterval, etc.)
|
|
70
70
|
*
|
|
71
71
|
* @returns Query definition object with three methods:
|
|
72
|
-
* - `options()`: Returns config for use with
|
|
72
|
+
* - `options()`: Returns config for use with useQuery() or createQuery()
|
|
73
73
|
* - `fetch()`: Always attempts to fetch data (from cache if fresh, network if stale)
|
|
74
74
|
* - `ensure()`: Guarantees data availability, preferring cached data (recommended for preloaders)
|
|
75
75
|
*
|
|
@@ -83,7 +83,7 @@ function createQueryFactories(queryClient) {
|
|
|
83
83
|
* });
|
|
84
84
|
*
|
|
85
85
|
* // Step 2a: Use reactively in a Svelte component
|
|
86
|
-
* const query = createQuery(userQuery.options
|
|
86
|
+
* const query = createQuery(userQuery.options);
|
|
87
87
|
* // $query.data is User | undefined
|
|
88
88
|
* // $query.error is ApiError | null
|
|
89
89
|
*
|
|
@@ -163,7 +163,7 @@ function createQueryFactories(queryClient) {
|
|
|
163
163
|
* @param options.* - Any other TanStack Mutation options (onSuccess, onError, etc.)
|
|
164
164
|
*
|
|
165
165
|
* @returns Mutation definition object with two methods:
|
|
166
|
-
* - `options()`: Returns config for use with
|
|
166
|
+
* - `options()`: Returns config for use with useMutation() or createMutation()
|
|
167
167
|
* - `execute()`: Directly executes the mutation and returns a Result
|
|
168
168
|
*
|
|
169
169
|
* @example
|
|
@@ -186,7 +186,7 @@ function createQueryFactories(queryClient) {
|
|
|
186
186
|
* });
|
|
187
187
|
*
|
|
188
188
|
* // Step 2a: Use reactively in a component
|
|
189
|
-
* const mutation = createMutation(createRecording.options
|
|
189
|
+
* const mutation = createMutation(createRecording.options);
|
|
190
190
|
* // Call with: $mutation.mutate(recordingData)
|
|
191
191
|
*
|
|
192
192
|
* // Step 2b: Use imperatively in an action
|
package/dist/query/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["queryClient: QueryClient","options: DefineQueryInput<\n\t\t\tTQueryFnData,\n\t\t\tTError,\n\t\t\tTData,\n\t\t\tTQueryData,\n\t\t\tTQueryKey\n\t\t>","options: DefineMutationInput<TData, TError, TVariables, TContext>","variables: TVariables","options: MutationOptions<TData, TError, TVariables, TContext>"],"sources":["../../src/query/utils.ts"],"sourcesContent":["import type {\n\tDefaultError,\n\tMutationFunction,\n\tMutationKey,\n\tMutationOptions,\n\tQueryClient,\n\tQueryFunction,\n\tQueryKey,\n\tQueryObserverOptions,\n} from \"@tanstack/query-core\";\nimport { Err, Ok, type Result, resolve } from \"../result/index.js\";\n\n/**\n * Input options for defining a query.\n *\n * Extends TanStack Query's QueryObserverOptions but replaces queryFn with resultQueryFn.\n * This type represents the configuration for creating a query definition with both\n * reactive and imperative interfaces for data fetching.\n *\n * @template TQueryFnData - The type of data returned by the query function\n * @template TError - The type of error that can be thrown\n * @template TData - The type of data returned by the query (after select transform)\n * @template TQueryKey - The type of the query key\n */\nexport type DefineQueryInput<\n\tTQueryFnData = unknown,\n\tTError = DefaultError,\n\tTData = TQueryFnData,\n\tTQueryData = TQueryFnData,\n\tTQueryKey extends QueryKey = QueryKey,\n> = Omit<\n\tQueryObserverOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>,\n\t\"queryFn\"\n> & {\n\tqueryKey: TQueryKey;\n\tresultQueryFn: QueryFunction<Result<TQueryFnData, TError>, TQueryKey>;\n};\n\n/**\n * Output of defineQuery function.\n *\n * Provides both reactive and imperative interfaces for data fetching:\n * - `options()`: Returns config for use with createQuery() in components\n * - `fetch()`: Always attempts to fetch data (from cache if fresh, network if stale)\n * - `ensure()`: Guarantees data availability, preferring cached data (recommended for preloaders)\n *\n * @template TQueryFnData - The type of data returned by the query function\n * @template TError - The type of error that can be thrown\n * @template TData - The type of data returned by the query (after select transform)\n * @template TQueryKey - The type of the query key\n */\nexport type DefineQueryOutput<\n\tTQueryFnData = unknown,\n\tTError = DefaultError,\n\tTData = TQueryFnData,\n\tTQueryData = TQueryFnData,\n\tTQueryKey extends QueryKey = QueryKey,\n> = {\n\toptions: () => QueryObserverOptions<\n\t\tTQueryFnData,\n\t\tTError,\n\t\tTData,\n\t\tTQueryData,\n\t\tTQueryKey\n\t>;\n\tfetch: () => Promise<Result<TQueryData, TError>>;\n\tensure: () => Promise<Result<TQueryData, TError>>;\n};\n\n/**\n * Input options for defining a mutation.\n *\n * Extends TanStack Query's MutationOptions but replaces mutationFn with resultMutationFn.\n * This type represents the configuration for creating a mutation definition with both\n * reactive and imperative interfaces for data mutations.\n *\n * @template TData - The type of data returned by the mutation\n * @template TError - The type of error that can be thrown\n * @template TVariables - The type of variables passed to the mutation\n * @template TContext - The type of context data for optimistic updates\n */\nexport type DefineMutationInput<\n\tTData,\n\tTError,\n\tTVariables = void,\n\tTContext = unknown,\n> = Omit<MutationOptions<TData, TError, TVariables, TContext>, \"mutationFn\"> & {\n\tmutationKey: MutationKey;\n\tresultMutationFn: MutationFunction<Result<TData, TError>, TVariables>;\n};\n\n/**\n * Output of defineMutation function.\n *\n * Provides both reactive and imperative interfaces for data mutations:\n * - `options()`: Returns config for use with createMutation() in Svelte components\n * - `execute()`: Directly executes the mutation and returns a Result\n *\n * @template TData - The type of data returned by the mutation\n * @template TError - The type of error that can be thrown\n * @template TVariables - The type of variables passed to the mutation\n * @template TContext - The type of context data for optimistic updates\n */\nexport type DefineMutationOutput<\n\tTData,\n\tTError,\n\tTVariables = void,\n\tTContext = unknown,\n> = {\n\toptions: () => MutationOptions<TData, TError, TVariables, TContext>;\n\texecute: (variables: TVariables) => Promise<Result<TData, TError>>;\n};\n\n/**\n * Creates factory functions for defining queries and mutations bound to a specific QueryClient.\n *\n * This factory pattern allows you to create isolated query/mutation definitions that are\n * bound to a specific QueryClient instance, enabling:\n * - Multiple query clients in the same application\n * - Testing with isolated query clients\n * - Framework-agnostic query definitions\n * - Proper separation of concerns between query logic and client instances\n *\n * The returned functions handle Result types automatically, unwrapping them for TanStack Query\n * while maintaining type safety throughout your application.\n *\n * @param queryClient - The QueryClient instance to bind the factories to\n * @returns An object containing defineQuery and defineMutation functions bound to the provided client\n *\n * @example\n * ```typescript\n * // Create your query client\n * const queryClient = new QueryClient({\n * defaultOptions: {\n * queries: { staleTime: 5 * 60 * 1000 }\n * }\n * });\n *\n * // Create the factory functions\n * const { defineQuery, defineMutation } = createQueryFactories(queryClient);\n *\n * // Now use defineQuery and defineMutation as before\n * const userQuery = defineQuery({\n * queryKey: ['user', userId],\n * resultQueryFn: () => services.getUser(userId)\n * });\n *\n * // Use in components\n * const query = createQuery(userQuery.options());\n *\n * // Or imperatively\n * const { data, error } = await userQuery.fetch();\n * ```\n */\nexport function createQueryFactories(queryClient: QueryClient) {\n\t/**\n\t * Creates a query definition that bridges the gap between pure service functions and reactive UI components.\n\t *\n\t * This factory function is the cornerstone of our data fetching architecture. It wraps service calls\n\t * with TanStack Query superpowers while maintaining type safety through Result types.\n\t *\n\t * ## Why use defineQuery?\n\t *\n\t * 1. **Dual Interface**: Provides both reactive (`.options()`) and imperative (`.fetch()`) APIs\n\t * 2. **Automatic Error Handling**: Service functions return `Result<T, E>` types which are automatically\n\t * unwrapped by TanStack Query, giving you proper error states in your components\n\t * 3. **Type Safety**: Full TypeScript support with proper inference for data and error types\n\t * 4. **Consistency**: Every query in the app follows the same pattern, making it easy to understand\n\t *\n\t * @template TQueryFnData - The type of data returned by the query function\n\t * @template TError - The type of error that can be thrown\n\t * @template TData - The type of data returned by the query (after select transform)\n\t * @template TQueryKey - The type of the query key\n\t *\n\t * @param options - Query configuration object\n\t * @param options.queryKey - Unique key for this query (used for caching and refetching)\n\t * @param options.resultQueryFn - Function that fetches data and returns a Result type\n\t * @param options.* - Any other TanStack Query options (staleTime, refetchInterval, etc.)\n\t *\n\t * @returns Query definition object with three methods:\n\t * - `options()`: Returns config for use with createQuery() in Svelte components\n\t * - `fetch()`: Always attempts to fetch data (from cache if fresh, network if stale)\n\t * - `ensure()`: Guarantees data availability, preferring cached data (recommended for preloaders)\n\t *\n\t * @example\n\t * ```typescript\n\t * // Step 1: Define your query in the query layer\n\t * const userQuery = defineQuery({\n\t * queryKey: ['users', userId],\n\t * resultQueryFn: () => services.getUser(userId), // Returns Result<User, ApiError>\n\t * staleTime: 5 * 60 * 1000, // Consider data fresh for 5 minutes\n\t * });\n\t *\n\t * // Step 2a: Use reactively in a Svelte component\n\t * const query = createQuery(userQuery.options());\n\t * // $query.data is User | undefined\n\t * // $query.error is ApiError | null\n\t *\n\t * // Step 2b: Use imperatively in preloaders (recommended)\n\t * export const load = async () => {\n\t * const { data, error } = await userQuery.ensure();\n\t * if (error) throw error;\n\t * return { user: data };\n\t * };\n\t *\n\t * // Step 2c: Use imperatively for explicit refresh\n\t * async function refreshUser() {\n\t * const { data, error } = await userQuery.fetch();\n\t * if (error) {\n\t * console.error('Failed to fetch user:', error);\n\t * }\n\t * }\n\t * ```\n\t */\n\tconst defineQuery = <\n\t\tTQueryFnData = unknown,\n\t\tTError = DefaultError,\n\t\tTData = TQueryFnData,\n\t\tTQueryData = TQueryFnData,\n\t\tTQueryKey extends QueryKey = QueryKey,\n\t>(\n\t\toptions: DefineQueryInput<\n\t\t\tTQueryFnData,\n\t\t\tTError,\n\t\t\tTData,\n\t\t\tTQueryData,\n\t\t\tTQueryKey\n\t\t>,\n\t): DefineQueryOutput<TQueryFnData, TError, TData, TQueryData, TQueryKey> => {\n\t\tconst newOptions = {\n\t\t\t...options,\n\t\t\tqueryFn: async (context) => {\n\t\t\t\tlet result = options.resultQueryFn(context);\n\t\t\t\tif (result instanceof Promise) result = await result;\n\t\t\t\treturn resolve(result);\n\t\t\t},\n\t\t} satisfies QueryObserverOptions<\n\t\t\tTQueryFnData,\n\t\t\tTError,\n\t\t\tTData,\n\t\t\tTQueryData,\n\t\t\tTQueryKey\n\t\t>;\n\n\t\treturn {\n\t\t\t/**\n\t\t\t * Returns the query options for reactive usage with TanStack Query hooks.\n\t\t\t * Use this with `createQuery()` in Svelte components for automatic subscriptions.\n\t\t\t * @returns The query options object configured for TanStack Query\n\t\t\t */\n\t\t\toptions: () => newOptions,\n\n\t\t\t/**\n\t\t\t * Fetches data for this query using queryClient.fetchQuery().\n\t\t\t *\n\t\t\t * This method ALWAYS evaluates freshness and will refetch if data is stale.\n\t\t\t * It wraps TanStack Query's fetchQuery method, which returns cached data if fresh\n\t\t\t * or makes a network request if the data is stale or missing.\n\t\t\t *\n\t\t\t * **When to use fetch():**\n\t\t\t * - When you explicitly want to check data freshness\n\t\t\t * - For user-triggered refresh actions\n\t\t\t * - When you need the most up-to-date data\n\t\t\t *\n\t\t\t * **For preloaders, use ensure() instead** - it's more efficient for initial data loading.\n\t\t\t *\n\t\t\t * @returns Promise that resolves with a Result containing either the data or an error\n\t\t\t *\n\t\t\t * @example\n\t\t\t * // Good for user-triggered refresh\n\t\t\t * const { data, error } = await userQuery.fetch();\n\t\t\t * if (error) {\n\t\t\t * console.error('Failed to load user:', error);\n\t\t\t * }\n\t\t\t */\n\t\t\tasync fetch(): Promise<Result<TQueryData, TError>> {\n\t\t\t\ttry {\n\t\t\t\t\treturn Ok(\n\t\t\t\t\t\tawait queryClient.fetchQuery<\n\t\t\t\t\t\t\tTQueryFnData,\n\t\t\t\t\t\t\tTError,\n\t\t\t\t\t\t\tTQueryData,\n\t\t\t\t\t\t\tTQueryKey\n\t\t\t\t\t\t>({\n\t\t\t\t\t\t\tqueryKey: newOptions.queryKey,\n\t\t\t\t\t\t\tqueryFn: newOptions.queryFn,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn Err(error as TError);\n\t\t\t\t}\n\t\t\t},\n\n\t\t\t/**\n\t\t\t * Ensures data is available for this query using queryClient.ensureQueryData().\n\t\t\t *\n\t\t\t * This method PRIORITIZES cached data and only calls fetchQuery internally if no cached\n\t\t\t * data exists. It wraps TanStack Query's ensureQueryData method, which is perfect for\n\t\t\t * guaranteeing data availability with minimal network requests.\n\t\t\t *\n\t\t\t * **This is the RECOMMENDED method for preloaders** because:\n\t\t\t * - It returns cached data immediately if available\n\t\t\t * - It updates the query client cache properly\n\t\t\t * - It minimizes network requests during navigation\n\t\t\t * - It ensures components have data ready when they mount\n\t\t\t *\n\t\t\t * **When to use ensure():**\n\t\t\t * - Route preloaders and data loading functions\n\t\t\t * - Initial component data requirements\n\t\t\t * - When cached data is acceptable for immediate display\n\t\t\t *\n\t\t\t * @returns Promise that resolves with a Result containing either the data or an error\n\t\t\t *\n\t\t\t * @example\n\t\t\t * // Perfect for preloaders\n\t\t\t * export const load = async () => {\n\t\t\t * const { data, error } = await userQuery.ensure();\n\t\t\t * if (error) {\n\t\t\t * throw error;\n\t\t\t * }\n\t\t\t * return { user: data };\n\t\t\t * };\n\t\t\t */\n\t\t\tasync ensure(): Promise<Result<TQueryData, TError>> {\n\t\t\t\ttry {\n\t\t\t\t\treturn Ok(\n\t\t\t\t\t\tawait queryClient.ensureQueryData<\n\t\t\t\t\t\t\tTQueryFnData,\n\t\t\t\t\t\t\tTError,\n\t\t\t\t\t\t\tTQueryData,\n\t\t\t\t\t\t\tTQueryKey\n\t\t\t\t\t\t>({\n\t\t\t\t\t\t\tqueryKey: newOptions.queryKey,\n\t\t\t\t\t\t\tqueryFn: newOptions.queryFn,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn Err(error as TError);\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t};\n\n\t/**\n\t * Creates a mutation definition for operations that modify data (create, update, delete).\n\t *\n\t * This factory function is the mutation counterpart to defineQuery. It provides a clean way to\n\t * wrap service functions that perform side effects, while maintaining the same dual interface\n\t * pattern for maximum flexibility.\n\t *\n\t * ## Why use defineMutation?\n\t *\n\t * 1. **Dual Interface**: Just like queries, mutations can be used reactively or imperatively\n\t * 2. **Direct Execution**: The `.execute()` method lets you run mutations without creating hooks,\n\t * perfect for event handlers and non-component code\n\t * 3. **Consistent Error Handling**: Service functions return `Result<T, E>` types, ensuring\n\t * errors are handled consistently throughout the app\n\t * 4. **Cache Management**: Mutations often update the cache after success (see examples)\n\t *\n\t * @template TData - The type of data returned by the mutation\n\t * @template TError - The type of error that can be thrown\n\t * @template TVariables - The type of variables passed to the mutation\n\t * @template TContext - The type of context data for optimistic updates\n\t *\n\t * @param options - Mutation configuration object\n\t * @param options.mutationKey - Unique key for this mutation (used for tracking in-flight state)\n\t * @param options.resultMutationFn - Function that performs the mutation and returns a Result type\n\t * @param options.* - Any other TanStack Mutation options (onSuccess, onError, etc.)\n\t *\n\t * @returns Mutation definition object with two methods:\n\t * - `options()`: Returns config for use with createMutation() in Svelte components\n\t * - `execute()`: Directly executes the mutation and returns a Result\n\t *\n\t * @example\n\t * ```typescript\n\t * // Step 1: Define your mutation with cache updates\n\t * const createRecording = defineMutation({\n\t * mutationKey: ['recordings', 'create'],\n\t * resultMutationFn: async (recording: Recording) => {\n\t * // Call the service\n\t * const result = await services.db.createRecording(recording);\n\t * if (result.error) return Err(result.error);\n\t *\n\t * // Update cache on success\n\t * queryClient.setQueryData(['recordings'], (old) =>\n\t * [...(old || []), recording]\n\t * );\n\t *\n\t * return Ok(result.data);\n\t * }\n\t * });\n\t *\n\t * // Step 2a: Use reactively in a component\n\t * const mutation = createMutation(createRecording.options());\n\t * // Call with: $mutation.mutate(recordingData)\n\t *\n\t * // Step 2b: Use imperatively in an action\n\t * async function saveRecording(data: Recording) {\n\t * const { error } = await createRecording.execute(data);\n\t * if (error) {\n\t * notify.error.execute({ title: 'Failed to save', description: error.message });\n\t * } else {\n\t * notify.success.execute({ title: 'Recording saved!' });\n\t * }\n\t * }\n\t * ```\n\t *\n\t * @tip The imperative `.execute()` method is especially useful for:\n\t * - Event handlers that need to await the result\n\t * - Sequential operations that depend on each other\n\t * - Non-component code that needs to trigger mutations\n\t */\n\tconst defineMutation = <TData, TError, TVariables = void, TContext = unknown>(\n\t\toptions: DefineMutationInput<TData, TError, TVariables, TContext>,\n\t): DefineMutationOutput<TData, TError, TVariables, TContext> => {\n\t\tconst newOptions = {\n\t\t\t...options,\n\t\t\tmutationFn: async (variables: TVariables) => {\n\t\t\t\treturn resolve(await options.resultMutationFn(variables));\n\t\t\t},\n\t\t} satisfies MutationOptions<TData, TError, TVariables, TContext>;\n\n\t\treturn {\n\t\t\t/**\n\t\t\t * Returns the mutation options for reactive usage with TanStack Query hooks.\n\t\t\t * Use this with `createMutation()` in Svelte components for reactive mutation state.\n\t\t\t * @returns The mutation options object configured for TanStack Query\n\t\t\t */\n\t\t\toptions: () => newOptions,\n\t\t\t/**\n\t\t\t * Bypasses the reactive mutation hooks and executes the mutation imperatively.\n\t\t\t *\n\t\t\t * This is the recommended way to trigger mutations from:\n\t\t\t * - Button click handlers\n\t\t\t * - Form submissions\n\t\t\t * - Keyboard shortcuts\n\t\t\t * - Any non-component code\n\t\t\t *\n\t\t\t * The method automatically wraps the result in a Result type, so you always\n\t\t\t * get back `{ data, error }` for consistent error handling.\n\t\t\t *\n\t\t\t * @param variables - The variables to pass to the mutation function\n\t\t\t * @returns Promise that resolves with a Result containing either the data or an error\n\t\t\t *\n\t\t\t * @example\n\t\t\t * // In an event handler\n\t\t\t * async function handleSubmit(formData: FormData) {\n\t\t\t * const { data, error } = await createUser.execute(formData);\n\t\t\t * if (error) {\n\t\t\t * notify.error.execute({ title: 'Failed to create user', description: error.message });\n\t\t\t * return;\n\t\t\t * }\n\t\t\t * goto(`/users/${data.id}`);\n\t\t\t * }\n\t\t\t */\n\t\t\tasync execute(variables: TVariables) {\n\t\t\t\ttry {\n\t\t\t\t\treturn Ok(await executeMutation(queryClient, newOptions, variables));\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn Err(error as TError);\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t};\n\n\treturn {\n\t\tdefineQuery,\n\t\tdefineMutation,\n\t};\n}\n\n/**\n * Internal helper that executes a mutation directly using the query client's mutation cache.\n *\n * This is what powers the `.execute()` method on mutations. It bypasses the reactive\n * mutation hooks and runs the mutation imperatively, which is perfect for event handlers\n * and other imperative code.\n *\n * @internal\n * @template TData - The type of data returned by the mutation\n * @template TError - The type of error that can be thrown\n * @template TVariables - The type of variables passed to the mutation\n * @template TContext - The type of context data\n * @param queryClient - The query client instance to use\n * @param options - The mutation options including mutationFn and mutationKey\n * @param variables - The variables to pass to the mutation function\n * @returns Promise that resolves with the mutation result\n */\nfunction executeMutation<TData, TError, TVariables, TContext>(\n\tqueryClient: QueryClient,\n\toptions: MutationOptions<TData, TError, TVariables, TContext>,\n\tvariables: TVariables,\n) {\n\tconst mutation = queryClient.getMutationCache().build(queryClient, options);\n\treturn mutation.execute(variables);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0JA,SAAgB,qBAAqBA,aAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4D9D,MAAM,cAAc,CAOnBC,YAO2E;EAC3E,MAAM,aAAa;GAClB,GAAG;GACH,SAAS,OAAO,YAAY;IAC3B,IAAI,SAAS,QAAQ,cAAc,QAAQ;AAC3C,QAAI,kBAAkB,QAAS,UAAS,MAAM;AAC9C,WAAO,QAAQ,OAAO;GACtB;EACD;AAQD,SAAO;GAMN,SAAS,MAAM;GAyBf,MAAM,QAA6C;AAClD,QAAI;AACH,YAAO,GACN,MAAM,YAAY,WAKhB;MACD,UAAU,WAAW;MACrB,SAAS,WAAW;KACpB,EAAC,CACF;IACD,SAAQ,OAAO;AACf,YAAO,IAAI,MAAgB;IAC3B;GACD;GAgCD,MAAM,SAA8C;AACnD,QAAI;AACH,YAAO,GACN,MAAM,YAAY,gBAKhB;MACD,UAAU,WAAW;MACrB,SAAS,WAAW;KACpB,EAAC,CACF;IACD,SAAQ,OAAO;AACf,YAAO,IAAI,MAAgB;IAC3B;GACD;EACD;CACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuED,MAAM,iBAAiB,CACtBC,YAC+D;EAC/D,MAAM,aAAa;GAClB,GAAG;GACH,YAAY,OAAOC,cAA0B;AAC5C,WAAO,QAAQ,MAAM,QAAQ,iBAAiB,UAAU,CAAC;GACzD;EACD;AAED,SAAO;GAMN,SAAS,MAAM;GA2Bf,MAAM,QAAQA,WAAuB;AACpC,QAAI;AACH,YAAO,GAAG,MAAM,gBAAgB,aAAa,YAAY,UAAU,CAAC;IACpE,SAAQ,OAAO;AACf,YAAO,IAAI,MAAgB;IAC3B;GACD;EACD;CACD;AAED,QAAO;EACN;EACA;CACA;AACD;;;;;;;;;;;;;;;;;;AAmBD,SAAS,gBACRH,aACAI,SACAD,WACC;CACD,MAAM,WAAW,YAAY,kBAAkB,CAAC,MAAM,aAAa,QAAQ;AAC3E,QAAO,SAAS,QAAQ,UAAU;AAClC"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["queryClient: QueryClient","options: DefineQueryInput<\n\t\t\tTQueryFnData,\n\t\t\tTError,\n\t\t\tTData,\n\t\t\tTQueryData,\n\t\t\tTQueryKey\n\t\t>","options: DefineMutationInput<TData, TError, TVariables, TContext>","variables: TVariables","options: MutationOptions<TData, TError, TVariables, TContext>"],"sources":["../../src/query/utils.ts"],"sourcesContent":["import type {\n\tDefaultError,\n\tMutationFunction,\n\tMutationKey,\n\tMutationOptions,\n\tQueryClient,\n\tQueryFunction,\n\tQueryKey,\n\tQueryObserverOptions,\n} from \"@tanstack/query-core\";\nimport { Err, Ok, type Result, resolve } from \"../result/index.js\";\n\n/**\n * Input options for defining a query.\n *\n * Extends TanStack Query's QueryObserverOptions but replaces queryFn with resultQueryFn.\n * This type represents the configuration for creating a query definition with both\n * reactive and imperative interfaces for data fetching.\n *\n * @template TQueryFnData - The type of data returned by the query function\n * @template TError - The type of error that can be thrown\n * @template TData - The type of data returned by the query (after select transform)\n * @template TQueryKey - The type of the query key\n */\nexport type DefineQueryInput<\n\tTQueryFnData = unknown,\n\tTError = DefaultError,\n\tTData = TQueryFnData,\n\tTQueryData = TQueryFnData,\n\tTQueryKey extends QueryKey = QueryKey,\n> = Omit<\n\tQueryObserverOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>,\n\t\"queryFn\"\n> & {\n\tqueryKey: TQueryKey;\n\tresultQueryFn: QueryFunction<Result<TQueryFnData, TError>, TQueryKey>;\n};\n\n/**\n * Output of defineQuery function.\n *\n * Provides both reactive and imperative interfaces for data fetching:\n * - `options()`: Returns config for use with useQuery() or createQuery()\n * - `fetch()`: Always attempts to fetch data (from cache if fresh, network if stale)\n * - `ensure()`: Guarantees data availability, preferring cached data (recommended for preloaders)\n *\n * @template TQueryFnData - The type of data returned by the query function\n * @template TError - The type of error that can be thrown\n * @template TData - The type of data returned by the query (after select transform)\n * @template TQueryKey - The type of the query key\n */\nexport type DefineQueryOutput<\n\tTQueryFnData = unknown,\n\tTError = DefaultError,\n\tTData = TQueryFnData,\n\tTQueryData = TQueryFnData,\n\tTQueryKey extends QueryKey = QueryKey,\n> = {\n\toptions: () => QueryObserverOptions<\n\t\tTQueryFnData,\n\t\tTError,\n\t\tTData,\n\t\tTQueryData,\n\t\tTQueryKey\n\t>;\n\tfetch: () => Promise<Result<TQueryData, TError>>;\n\tensure: () => Promise<Result<TQueryData, TError>>;\n};\n\n/**\n * Input options for defining a mutation.\n *\n * Extends TanStack Query's MutationOptions but replaces mutationFn with resultMutationFn.\n * This type represents the configuration for creating a mutation definition with both\n * reactive and imperative interfaces for data mutations.\n *\n * @template TData - The type of data returned by the mutation\n * @template TError - The type of error that can be thrown\n * @template TVariables - The type of variables passed to the mutation\n * @template TContext - The type of context data for optimistic updates\n */\nexport type DefineMutationInput<\n\tTData,\n\tTError,\n\tTVariables = void,\n\tTContext = unknown,\n> = Omit<MutationOptions<TData, TError, TVariables, TContext>, \"mutationFn\"> & {\n\tmutationKey: MutationKey;\n\tresultMutationFn: MutationFunction<Result<TData, TError>, TVariables>;\n};\n\n/**\n * Output of defineMutation function.\n *\n * Provides both reactive and imperative interfaces for data mutations:\n * - `options()`: Returns config for use with useMutation() or createMutation()\n * - `execute()`: Directly executes the mutation and returns a Result\n *\n * @template TData - The type of data returned by the mutation\n * @template TError - The type of error that can be thrown\n * @template TVariables - The type of variables passed to the mutation\n * @template TContext - The type of context data for optimistic updates\n */\nexport type DefineMutationOutput<\n\tTData,\n\tTError,\n\tTVariables = void,\n\tTContext = unknown,\n> = {\n\toptions: () => MutationOptions<TData, TError, TVariables, TContext>;\n\texecute: (variables: TVariables) => Promise<Result<TData, TError>>;\n};\n\n/**\n * Creates factory functions for defining queries and mutations bound to a specific QueryClient.\n *\n * This factory pattern allows you to create isolated query/mutation definitions that are\n * bound to a specific QueryClient instance, enabling:\n * - Multiple query clients in the same application\n * - Testing with isolated query clients\n * - Framework-agnostic query definitions\n * - Proper separation of concerns between query logic and client instances\n *\n * The returned functions handle Result types automatically, unwrapping them for TanStack Query\n * while maintaining type safety throughout your application.\n *\n * @param queryClient - The QueryClient instance to bind the factories to\n * @returns An object containing defineQuery and defineMutation functions bound to the provided client\n *\n * @example\n * ```typescript\n * // Create your query client\n * const queryClient = new QueryClient({\n * defaultOptions: {\n * queries: { staleTime: 5 * 60 * 1000 }\n * }\n * });\n *\n * // Create the factory functions\n * const { defineQuery, defineMutation } = createQueryFactories(queryClient);\n *\n * // Now use defineQuery and defineMutation as before\n * const userQuery = defineQuery({\n * queryKey: ['user', userId],\n * resultQueryFn: () => services.getUser(userId)\n * });\n *\n * // Use in components\n * const query = createQuery(userQuery.options);\n *\n * // Or imperatively\n * const { data, error } = await userQuery.fetch();\n * ```\n */\nexport function createQueryFactories(queryClient: QueryClient) {\n\t/**\n\t * Creates a query definition that bridges the gap between pure service functions and reactive UI components.\n\t *\n\t * This factory function is the cornerstone of our data fetching architecture. It wraps service calls\n\t * with TanStack Query superpowers while maintaining type safety through Result types.\n\t *\n\t * ## Why use defineQuery?\n\t *\n\t * 1. **Dual Interface**: Provides both reactive (`.options`) and imperative (`.fetch()`) APIs\n\t * 2. **Automatic Error Handling**: Service functions return `Result<T, E>` types which are automatically\n\t * unwrapped by TanStack Query, giving you proper error states in your components\n\t * 3. **Type Safety**: Full TypeScript support with proper inference for data and error types\n\t * 4. **Consistency**: Every query in the app follows the same pattern, making it easy to understand\n\t *\n\t * @template TQueryFnData - The type of data returned by the query function\n\t * @template TError - The type of error that can be thrown\n\t * @template TData - The type of data returned by the query (after select transform)\n\t * @template TQueryKey - The type of the query key\n\t *\n\t * @param options - Query configuration object\n\t * @param options.queryKey - Unique key for this query (used for caching and refetching)\n\t * @param options.resultQueryFn - Function that fetches data and returns a Result type\n\t * @param options.* - Any other TanStack Query options (staleTime, refetchInterval, etc.)\n\t *\n\t * @returns Query definition object with three methods:\n\t * - `options()`: Returns config for use with useQuery() or createQuery()\n\t * - `fetch()`: Always attempts to fetch data (from cache if fresh, network if stale)\n\t * - `ensure()`: Guarantees data availability, preferring cached data (recommended for preloaders)\n\t *\n\t * @example\n\t * ```typescript\n\t * // Step 1: Define your query in the query layer\n\t * const userQuery = defineQuery({\n\t * queryKey: ['users', userId],\n\t * resultQueryFn: () => services.getUser(userId), // Returns Result<User, ApiError>\n\t * staleTime: 5 * 60 * 1000, // Consider data fresh for 5 minutes\n\t * });\n\t *\n\t * // Step 2a: Use reactively in a Svelte component\n\t * const query = createQuery(userQuery.options);\n\t * // $query.data is User | undefined\n\t * // $query.error is ApiError | null\n\t *\n\t * // Step 2b: Use imperatively in preloaders (recommended)\n\t * export const load = async () => {\n\t * const { data, error } = await userQuery.ensure();\n\t * if (error) throw error;\n\t * return { user: data };\n\t * };\n\t *\n\t * // Step 2c: Use imperatively for explicit refresh\n\t * async function refreshUser() {\n\t * const { data, error } = await userQuery.fetch();\n\t * if (error) {\n\t * console.error('Failed to fetch user:', error);\n\t * }\n\t * }\n\t * ```\n\t */\n\tconst defineQuery = <\n\t\tTQueryFnData = unknown,\n\t\tTError = DefaultError,\n\t\tTData = TQueryFnData,\n\t\tTQueryData = TQueryFnData,\n\t\tTQueryKey extends QueryKey = QueryKey,\n\t>(\n\t\toptions: DefineQueryInput<\n\t\t\tTQueryFnData,\n\t\t\tTError,\n\t\t\tTData,\n\t\t\tTQueryData,\n\t\t\tTQueryKey\n\t\t>,\n\t): DefineQueryOutput<TQueryFnData, TError, TData, TQueryData, TQueryKey> => {\n\t\tconst newOptions = {\n\t\t\t...options,\n\t\t\tqueryFn: async (context) => {\n\t\t\t\tlet result = options.resultQueryFn(context);\n\t\t\t\tif (result instanceof Promise) result = await result;\n\t\t\t\treturn resolve(result);\n\t\t\t},\n\t\t} satisfies QueryObserverOptions<\n\t\t\tTQueryFnData,\n\t\t\tTError,\n\t\t\tTData,\n\t\t\tTQueryData,\n\t\t\tTQueryKey\n\t\t>;\n\n\t\treturn {\n\t\t\t/**\n\t\t\t * Returns the query options for reactive usage with TanStack Query hooks.\n\t\t\t * Use this with `useQuery()` or `createQuery()` for automatic subscriptions.\n\t\t\t * @returns The query options object configured for TanStack Query\n\t\t\t */\n\t\t\toptions: () => newOptions,\n\n\t\t\t/**\n\t\t\t * Fetches data for this query using queryClient.fetchQuery().\n\t\t\t *\n\t\t\t * This method ALWAYS evaluates freshness and will refetch if data is stale.\n\t\t\t * It wraps TanStack Query's fetchQuery method, which returns cached data if fresh\n\t\t\t * or makes a network request if the data is stale or missing.\n\t\t\t *\n\t\t\t * **When to use fetch():**\n\t\t\t * - When you explicitly want to check data freshness\n\t\t\t * - For user-triggered refresh actions\n\t\t\t * - When you need the most up-to-date data\n\t\t\t *\n\t\t\t * **For preloaders, use ensure() instead** - it's more efficient for initial data loading.\n\t\t\t *\n\t\t\t * @returns Promise that resolves with a Result containing either the data or an error\n\t\t\t *\n\t\t\t * @example\n\t\t\t * // Good for user-triggered refresh\n\t\t\t * const { data, error } = await userQuery.fetch();\n\t\t\t * if (error) {\n\t\t\t * console.error('Failed to load user:', error);\n\t\t\t * }\n\t\t\t */\n\t\t\tasync fetch(): Promise<Result<TQueryData, TError>> {\n\t\t\t\ttry {\n\t\t\t\t\treturn Ok(\n\t\t\t\t\t\tawait queryClient.fetchQuery<\n\t\t\t\t\t\t\tTQueryFnData,\n\t\t\t\t\t\t\tTError,\n\t\t\t\t\t\t\tTQueryData,\n\t\t\t\t\t\t\tTQueryKey\n\t\t\t\t\t\t>({\n\t\t\t\t\t\t\tqueryKey: newOptions.queryKey,\n\t\t\t\t\t\t\tqueryFn: newOptions.queryFn,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn Err(error as TError);\n\t\t\t\t}\n\t\t\t},\n\n\t\t\t/**\n\t\t\t * Ensures data is available for this query using queryClient.ensureQueryData().\n\t\t\t *\n\t\t\t * This method PRIORITIZES cached data and only calls fetchQuery internally if no cached\n\t\t\t * data exists. It wraps TanStack Query's ensureQueryData method, which is perfect for\n\t\t\t * guaranteeing data availability with minimal network requests.\n\t\t\t *\n\t\t\t * **This is the RECOMMENDED method for preloaders** because:\n\t\t\t * - It returns cached data immediately if available\n\t\t\t * - It updates the query client cache properly\n\t\t\t * - It minimizes network requests during navigation\n\t\t\t * - It ensures components have data ready when they mount\n\t\t\t *\n\t\t\t * **When to use ensure():**\n\t\t\t * - Route preloaders and data loading functions\n\t\t\t * - Initial component data requirements\n\t\t\t * - When cached data is acceptable for immediate display\n\t\t\t *\n\t\t\t * @returns Promise that resolves with a Result containing either the data or an error\n\t\t\t *\n\t\t\t * @example\n\t\t\t * // Perfect for preloaders\n\t\t\t * export const load = async () => {\n\t\t\t * const { data, error } = await userQuery.ensure();\n\t\t\t * if (error) {\n\t\t\t * throw error;\n\t\t\t * }\n\t\t\t * return { user: data };\n\t\t\t * };\n\t\t\t */\n\t\t\tasync ensure(): Promise<Result<TQueryData, TError>> {\n\t\t\t\ttry {\n\t\t\t\t\treturn Ok(\n\t\t\t\t\t\tawait queryClient.ensureQueryData<\n\t\t\t\t\t\t\tTQueryFnData,\n\t\t\t\t\t\t\tTError,\n\t\t\t\t\t\t\tTQueryData,\n\t\t\t\t\t\t\tTQueryKey\n\t\t\t\t\t\t>({\n\t\t\t\t\t\t\tqueryKey: newOptions.queryKey,\n\t\t\t\t\t\t\tqueryFn: newOptions.queryFn,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn Err(error as TError);\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t};\n\n\t/**\n\t * Creates a mutation definition for operations that modify data (create, update, delete).\n\t *\n\t * This factory function is the mutation counterpart to defineQuery. It provides a clean way to\n\t * wrap service functions that perform side effects, while maintaining the same dual interface\n\t * pattern for maximum flexibility.\n\t *\n\t * ## Why use defineMutation?\n\t *\n\t * 1. **Dual Interface**: Just like queries, mutations can be used reactively or imperatively\n\t * 2. **Direct Execution**: The `.execute()` method lets you run mutations without creating hooks,\n\t * perfect for event handlers and non-component code\n\t * 3. **Consistent Error Handling**: Service functions return `Result<T, E>` types, ensuring\n\t * errors are handled consistently throughout the app\n\t * 4. **Cache Management**: Mutations often update the cache after success (see examples)\n\t *\n\t * @template TData - The type of data returned by the mutation\n\t * @template TError - The type of error that can be thrown\n\t * @template TVariables - The type of variables passed to the mutation\n\t * @template TContext - The type of context data for optimistic updates\n\t *\n\t * @param options - Mutation configuration object\n\t * @param options.mutationKey - Unique key for this mutation (used for tracking in-flight state)\n\t * @param options.resultMutationFn - Function that performs the mutation and returns a Result type\n\t * @param options.* - Any other TanStack Mutation options (onSuccess, onError, etc.)\n\t *\n\t * @returns Mutation definition object with two methods:\n\t * - `options()`: Returns config for use with useMutation() or createMutation()\n\t * - `execute()`: Directly executes the mutation and returns a Result\n\t *\n\t * @example\n\t * ```typescript\n\t * // Step 1: Define your mutation with cache updates\n\t * const createRecording = defineMutation({\n\t * mutationKey: ['recordings', 'create'],\n\t * resultMutationFn: async (recording: Recording) => {\n\t * // Call the service\n\t * const result = await services.db.createRecording(recording);\n\t * if (result.error) return Err(result.error);\n\t *\n\t * // Update cache on success\n\t * queryClient.setQueryData(['recordings'], (old) =>\n\t * [...(old || []), recording]\n\t * );\n\t *\n\t * return Ok(result.data);\n\t * }\n\t * });\n\t *\n\t * // Step 2a: Use reactively in a component\n\t * const mutation = createMutation(createRecording.options);\n\t * // Call with: $mutation.mutate(recordingData)\n\t *\n\t * // Step 2b: Use imperatively in an action\n\t * async function saveRecording(data: Recording) {\n\t * const { error } = await createRecording.execute(data);\n\t * if (error) {\n\t * notify.error.execute({ title: 'Failed to save', description: error.message });\n\t * } else {\n\t * notify.success.execute({ title: 'Recording saved!' });\n\t * }\n\t * }\n\t * ```\n\t *\n\t * @tip The imperative `.execute()` method is especially useful for:\n\t * - Event handlers that need to await the result\n\t * - Sequential operations that depend on each other\n\t * - Non-component code that needs to trigger mutations\n\t */\n\tconst defineMutation = <TData, TError, TVariables = void, TContext = unknown>(\n\t\toptions: DefineMutationInput<TData, TError, TVariables, TContext>,\n\t): DefineMutationOutput<TData, TError, TVariables, TContext> => {\n\t\tconst newOptions = {\n\t\t\t...options,\n\t\t\tmutationFn: async (variables: TVariables) => {\n\t\t\t\treturn resolve(await options.resultMutationFn(variables));\n\t\t\t},\n\t\t} satisfies MutationOptions<TData, TError, TVariables, TContext>;\n\n\t\treturn {\n\t\t\t/**\n\t\t\t * Returns the mutation options for reactive usage with TanStack Query hooks.\n\t\t\t * Use this with `useMutation()` or `createMutation()` for reactive mutation state.\n\t\t\t * @returns The mutation options object configured for TanStack Query\n\t\t\t */\n\t\t\toptions: () => newOptions,\n\t\t\t/**\n\t\t\t * Bypasses the reactive mutation hooks and executes the mutation imperatively.\n\t\t\t *\n\t\t\t * This is the recommended way to trigger mutations from:\n\t\t\t * - Button click handlers\n\t\t\t * - Form submissions\n\t\t\t * - Keyboard shortcuts\n\t\t\t * - Any non-component code\n\t\t\t *\n\t\t\t * The method automatically wraps the result in a Result type, so you always\n\t\t\t * get back `{ data, error }` for consistent error handling.\n\t\t\t *\n\t\t\t * @param variables - The variables to pass to the mutation function\n\t\t\t * @returns Promise that resolves with a Result containing either the data or an error\n\t\t\t *\n\t\t\t * @example\n\t\t\t * // In an event handler\n\t\t\t * async function handleSubmit(formData: FormData) {\n\t\t\t * const { data, error } = await createUser.execute(formData);\n\t\t\t * if (error) {\n\t\t\t * notify.error.execute({ title: 'Failed to create user', description: error.message });\n\t\t\t * return;\n\t\t\t * }\n\t\t\t * goto(`/users/${data.id}`);\n\t\t\t * }\n\t\t\t */\n\t\t\tasync execute(variables: TVariables) {\n\t\t\t\ttry {\n\t\t\t\t\treturn Ok(await executeMutation(queryClient, newOptions, variables));\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn Err(error as TError);\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t};\n\n\treturn {\n\t\tdefineQuery,\n\t\tdefineMutation,\n\t};\n}\n\n/**\n * Internal helper that executes a mutation directly using the query client's mutation cache.\n *\n * This is what powers the `.execute()` method on mutations. It bypasses the reactive\n * mutation hooks and runs the mutation imperatively, which is perfect for event handlers\n * and other imperative code.\n *\n * @internal\n * @template TData - The type of data returned by the mutation\n * @template TError - The type of error that can be thrown\n * @template TVariables - The type of variables passed to the mutation\n * @template TContext - The type of context data\n * @param queryClient - The query client instance to use\n * @param options - The mutation options including mutationFn and mutationKey\n * @param variables - The variables to pass to the mutation function\n * @returns Promise that resolves with the mutation result\n */\nfunction executeMutation<TData, TError, TVariables, TContext>(\n\tqueryClient: QueryClient,\n\toptions: MutationOptions<TData, TError, TVariables, TContext>,\n\tvariables: TVariables,\n) {\n\tconst mutation = queryClient.getMutationCache().build(queryClient, options);\n\treturn mutation.execute(variables);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0JA,SAAgB,qBAAqBA,aAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4D9D,MAAM,cAAc,CAOnBC,YAO2E;EAC3E,MAAM,aAAa;GAClB,GAAG;GACH,SAAS,OAAO,YAAY;IAC3B,IAAI,SAAS,QAAQ,cAAc,QAAQ;AAC3C,QAAI,kBAAkB,QAAS,UAAS,MAAM;AAC9C,WAAO,QAAQ,OAAO;GACtB;EACD;AAQD,SAAO;GAMN,SAAS,MAAM;GAyBf,MAAM,QAA6C;AAClD,QAAI;AACH,YAAO,GACN,MAAM,YAAY,WAKhB;MACD,UAAU,WAAW;MACrB,SAAS,WAAW;KACpB,EAAC,CACF;IACD,SAAQ,OAAO;AACf,YAAO,IAAI,MAAgB;IAC3B;GACD;GAgCD,MAAM,SAA8C;AACnD,QAAI;AACH,YAAO,GACN,MAAM,YAAY,gBAKhB;MACD,UAAU,WAAW;MACrB,SAAS,WAAW;KACpB,EAAC,CACF;IACD,SAAQ,OAAO;AACf,YAAO,IAAI,MAAgB;IAC3B;GACD;EACD;CACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuED,MAAM,iBAAiB,CACtBC,YAC+D;EAC/D,MAAM,aAAa;GAClB,GAAG;GACH,YAAY,OAAOC,cAA0B;AAC5C,WAAO,QAAQ,MAAM,QAAQ,iBAAiB,UAAU,CAAC;GACzD;EACD;AAED,SAAO;GAMN,SAAS,MAAM;GA2Bf,MAAM,QAAQA,WAAuB;AACpC,QAAI;AACH,YAAO,GAAG,MAAM,gBAAgB,aAAa,YAAY,UAAU,CAAC;IACpE,SAAQ,OAAO;AACf,YAAO,IAAI,MAAgB;IAC3B;GACD;EACD;CACD;AAED,QAAO;EACN;EACA;CACA;AACD;;;;;;;;;;;;;;;;;;AAmBD,SAAS,gBACRH,aACAI,SACAD,WACC;CACD,MAAM,WAAW,YAAY,kBAAkB,CAAC,MAAM,aAAa,QAAQ;AAC3E,QAAO,SAAS,QAAQ,UAAU;AAClC"}
|
package/package.json
CHANGED
|
@@ -1,56 +1,56 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
2
|
+
"name": "wellcrafted",
|
|
3
|
+
"version": "0.23.1",
|
|
4
|
+
"description": "Delightful TypeScript patterns for elegant, type-safe applications",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist",
|
|
8
|
+
"README.md",
|
|
9
|
+
"LICENSE"
|
|
10
|
+
],
|
|
11
|
+
"exports": {
|
|
12
|
+
"./result": {
|
|
13
|
+
"types": "./dist/result/index.d.ts",
|
|
14
|
+
"import": "./dist/result/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./error": {
|
|
17
|
+
"types": "./dist/error/index.d.ts",
|
|
18
|
+
"import": "./dist/error/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./brand": {
|
|
21
|
+
"types": "./dist/brand.d.ts",
|
|
22
|
+
"import": "./dist/brand.js"
|
|
23
|
+
},
|
|
24
|
+
"./query": {
|
|
25
|
+
"types": "./dist/query/index.d.ts",
|
|
26
|
+
"import": "./dist/query/index.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsdown",
|
|
31
|
+
"format": "biome format --write .",
|
|
32
|
+
"lint": "biome lint --write .",
|
|
33
|
+
"release": "pnpm run build && changeset version && changeset publish"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"typescript",
|
|
37
|
+
"delightful",
|
|
38
|
+
"elegant",
|
|
39
|
+
"type-safe",
|
|
40
|
+
"well-crafted",
|
|
41
|
+
"polished",
|
|
42
|
+
"utilities",
|
|
43
|
+
"result",
|
|
44
|
+
"error-handling",
|
|
45
|
+
"brand-types"
|
|
46
|
+
],
|
|
47
|
+
"author": "",
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@biomejs/biome": "^2.3.3",
|
|
51
|
+
"@changesets/cli": "^2.27.10",
|
|
52
|
+
"@tanstack/query-core": "^5.82.0",
|
|
53
|
+
"tsdown": "^0.12.5",
|
|
54
|
+
"typescript": "^5.8.3"
|
|
55
|
+
}
|
|
56
|
+
}
|