swr-catalyst 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +457 -0
  3. package/dist/errors/MutationErrors/index.d.ts +120 -0
  4. package/dist/errors/MutationErrors/types.d.ts +98 -0
  5. package/dist/errors/index.d.ts +2 -0
  6. package/dist/hooks/index.d.ts +4 -0
  7. package/dist/hooks/shared/helpers/applyOptimisticUpdate/index.d.ts +46 -0
  8. package/dist/hooks/shared/helpers/createMutationError/index.d.ts +46 -0
  9. package/dist/hooks/shared/helpers/executeMutation/index.d.ts +51 -0
  10. package/dist/hooks/shared/helpers/index.d.ts +4 -0
  11. package/dist/hooks/shared/helpers/rollbackOptimisticUpdate/index.d.ts +31 -0
  12. package/dist/hooks/shared/index.d.ts +1 -0
  13. package/dist/hooks/useSWRCreate/index.d.ts +89 -0
  14. package/dist/hooks/useSWRCreate/index.test.d.ts +1 -0
  15. package/dist/hooks/useSWRCreate/types.d.ts +27 -0
  16. package/dist/hooks/useSWRDelete/index.d.ts +103 -0
  17. package/dist/hooks/useSWRDelete/index.test.d.ts +1 -0
  18. package/dist/hooks/useSWRDelete/types.d.ts +45 -0
  19. package/dist/hooks/useSWRUpdate/index.d.ts +121 -0
  20. package/dist/hooks/useSWRUpdate/index.test.d.ts +1 -0
  21. package/dist/hooks/useSWRUpdate/types.d.ts +41 -0
  22. package/dist/hooks/useStableKey/index.d.ts +52 -0
  23. package/dist/hooks/useStableKey/index.test.d.ts +1 -0
  24. package/dist/hooks/useStableKey/utils/deepEqual/index.d.ts +11 -0
  25. package/dist/hooks/useStableKey/utils/index.d.ts +1 -0
  26. package/dist/index.d.ts +4 -0
  27. package/dist/swr-catalyst.js +465 -0
  28. package/dist/swr-catalyst.umd.cjs +1 -0
  29. package/dist/types/index.d.ts +103 -0
  30. package/dist/utils/extractSWRKey/index.d.ts +40 -0
  31. package/dist/utils/extractSWRKey/index.test.d.ts +1 -0
  32. package/dist/utils/index.d.ts +7 -0
  33. package/dist/utils/mutateByGroup/index.d.ts +51 -0
  34. package/dist/utils/mutateByGroup/index.test.d.ts +1 -0
  35. package/dist/utils/mutateById/index.d.ts +50 -0
  36. package/dist/utils/mutateById/index.test.d.ts +1 -0
  37. package/dist/utils/resetCache/index.d.ts +48 -0
  38. package/dist/utils/resetCache/index.test.d.ts +1 -0
  39. package/dist/utils/swrGetCache/index.d.ts +49 -0
  40. package/dist/utils/swrGetCache/index.test.d.ts +1 -0
  41. package/dist/utils/swrMutate/index.d.ts +47 -0
  42. package/dist/utils/swrMutate/index.test.d.ts +1 -0
  43. package/dist/utils/to/index.d.ts +49 -0
  44. package/dist/utils/to/index.test.d.ts +1 -0
  45. package/package.json +56 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Pedro Barbosa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,457 @@
1
+ # swr-catalyst
2
+
3
+ <div align="center">
4
+
5
+ **A lightweight, type-safe library for effortless data mutations with SWR**
6
+
7
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue)](https://www.typescriptlang.org/)
9
+ [![Bundle Size](https://img.shields.io/bundlephobia/minzip/swr-catalyst)](https://bundlephobia.com/package/swr-catalyst)
10
+
11
+ [Features](#features) • [Installation](#installation) • [Quick Start](#quick-start) • [API Reference](#api-reference)
12
+
13
+ </div>
14
+
15
+ ---
16
+
17
+ ## Why swr-catalyst?
18
+
19
+ SWR excels at data fetching, but mutations (create, update, delete) require significant boilerplate—especially with optimistic updates. **swr-catalyst** eliminates this repetition with declarative hooks that handle:
20
+
21
+ - ✅ **Optimistic UI updates** with automatic rollback
22
+ - ✅ **Loading and error states** out of the box
23
+ - ✅ **Type-safe mutations** with full TypeScript support
24
+ - ✅ **Advanced cache management** utilities
25
+ - ✅ **Zero configuration** required
26
+
27
+ ## Features
28
+
29
+ - 🎯 **Three core hooks**: `useSWRCreate`, `useSWRUpdate`, `useSWRDelete`
30
+ - 🚀 **Optimistic updates**: Instant UI feedback with automatic error rollback
31
+ - 📦 **Tiny footprint**: ~3KB minified + gzipped
32
+ - 🔒 **Type-safe**: Full TypeScript support with generics
33
+ - 🛠️ **Cache utilities**: Batch operations with `mutateById`, `mutateByGroup`, `resetCache`
34
+ - 🔑 **Structured keys**: Uses typed `SWRKey` objects for enhanced cache management
35
+ - ⚡ **Smart error handling**: Custom `MutationError` class with helpful context
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ npm install swr-catalyst
41
+ ```
42
+
43
+ ```bash
44
+ pnpm add swr-catalyst
45
+ ```
46
+
47
+ ```bash
48
+ yarn add swr-catalyst
49
+ ```
50
+
51
+ > **Note:** This library requires `react` and `swr` as peer dependencies. Make sure you have them installed in your project.
52
+
53
+ ## Quick Start
54
+
55
+ ### Basic CRUD Operations
56
+
57
+ ```tsx
58
+ import { useSWRCreate, useSWRUpdate, useSWRDelete } from 'swr-catalyst';
59
+
60
+ // Create
61
+ const { trigger: createTodo, isMutating, error } = useSWRCreate(
62
+ { id: 'todos', data: '/api/todos' },
63
+ async (newTodo) => api.post('/todos', newTodo)
64
+ );
65
+
66
+ await createTodo({ title: 'Buy milk' });
67
+
68
+ // Update
69
+ const { trigger: updateTodo } = useSWRUpdate(
70
+ { id: 'todos', data: '/api/todos' },
71
+ async (id, data) => api.patch(`/todos/${id}`, data)
72
+ );
73
+
74
+ await updateTodo(1, { completed: true });
75
+
76
+ // Delete
77
+ const { trigger: deleteTodo } = useSWRDelete(
78
+ { id: 'todos', data: '/api/todos' },
79
+ async (id) => api.delete(`/todos/${id}`)
80
+ );
81
+
82
+ await deleteTodo(1);
83
+ ```
84
+
85
+ ### Optimistic Updates
86
+
87
+ Make your UI feel instant with optimistic updates:
88
+
89
+ ```tsx
90
+ import { useSWRCreate } from 'swr-catalyst';
91
+
92
+ const { trigger: addTodo } = useSWRCreate(
93
+ { id: 'todos', data: '/api/todos' },
94
+ createTodoAPI,
95
+ {
96
+ optimisticUpdate: (currentTodos, newTodo) => [
97
+ ...(currentTodos || []),
98
+ { ...newTodo, id: `temp-${Date.now()}` }
99
+ ],
100
+ rollbackOnError: true // Automatically reverts on failure
101
+ }
102
+ );
103
+
104
+ // UI updates immediately, syncs with server in background
105
+ await addTodo({ title: 'New task' });
106
+ ```
107
+
108
+ ### Loading and Error States
109
+
110
+ ```tsx
111
+ import { useSWRCreate } from 'swr-catalyst';
112
+
113
+ function AddTodoForm() {
114
+ const { trigger: addTodo, isMutating, error } = useSWRCreate(
115
+ { id: 'todos', data: '/api/todos' },
116
+ createTodoAPI
117
+ );
118
+
119
+ const handleSubmit = async (e: React.FormEvent) => {
120
+ e.preventDefault();
121
+ const formData = new FormData(e.currentTarget);
122
+
123
+ try {
124
+ await addTodo({ title: formData.get('title') });
125
+ e.currentTarget.reset();
126
+ } catch (err) {
127
+ // Error state is automatically updated
128
+ }
129
+ };
130
+
131
+ return (
132
+ <form onSubmit={handleSubmit}>
133
+ <input name="title" type="text" disabled={isMutating} />
134
+ <button type="submit" disabled={isMutating}>
135
+ {isMutating ? 'Adding...' : 'Add Todo'}
136
+ </button>
137
+ {error && <p className="error">Failed to add todo: {error.message}</p>}
138
+ </form>
139
+ );
140
+ }
141
+ ```
142
+
143
+ ### Error Handling
144
+
145
+ All hooks return a custom `MutationError` when mutations fail. This error class provides rich context and helpful methods for handling failures gracefully.
146
+
147
+ #### Properties
148
+
149
+ - `name`: Always `"MutationError"`
150
+ - `message`: Human-readable error description
151
+ - `context`: Object containing mutation details:
152
+ - `operation`: The type of mutation (`"create"`, `"update"`, or `"delete"`)
153
+ - `key`: The `SWRKey` that was being mutated
154
+ - `data`: The payload data (for create/update operations)
155
+ - `id`: The resource ID (for update/delete operations)
156
+ - `timestamp`: When the error occurred (milliseconds since epoch)
157
+ - `originalError`: The underlying error that caused the failure
158
+ - `stack`: Error stack trace
159
+
160
+ #### Methods
161
+
162
+ **`getUserMessage(): string`**
163
+
164
+ Returns a user-friendly error message suitable for displaying in UI.
165
+
166
+ ```typescript
167
+ const error = mutationError.getUserMessage();
168
+ // Returns: "Failed to add todos. Please try again."
169
+ ```
170
+
171
+ **`isNetworkError(): boolean`**
172
+
173
+ Checks if the error was caused by network-related issues (connection failures, timeouts, etc.).
174
+
175
+ ```typescript
176
+ if (error.isNetworkError()) {
177
+ toast.error('Network error. Check your connection and try again.', {
178
+ action: { label: 'Retry', onClick: handleRetry }
179
+ });
180
+ }
181
+ ```
182
+
183
+ **`isValidationError(): boolean`**
184
+
185
+ Checks if the error was caused by validation failures (invalid data, required fields, etc.).
186
+
187
+ ```typescript
188
+ if (error.isValidationError()) {
189
+ // Show validation-specific message
190
+ toast.error('Please check your input and try again.');
191
+ } else {
192
+ // Show generic error with retry option
193
+ toast.error('Something went wrong', { action: 'Retry' });
194
+ }
195
+ ```
196
+
197
+ **`toJSON(): object`**
198
+
199
+ Serializes the error to a JSON-compatible object for logging and error tracking services.
200
+
201
+ ```typescript
202
+ // Send to error tracking service
203
+ Sentry.captureException(mutationError.toJSON());
204
+
205
+ // Log for debugging
206
+ console.error('Mutation failed:', mutationError.toJSON());
207
+ ```
208
+
209
+ #### Complete Example
210
+
211
+ ```typescript
212
+ import { useSWRCreate, MutationError } from 'swr-catalyst';
213
+
214
+ function TodoForm() {
215
+ const { trigger, error } = useSWRCreate(
216
+ { id: 'todos', data: '/api/todos' },
217
+ createTodoAPI
218
+ );
219
+
220
+ const handleSubmit = async (data) => {
221
+ try {
222
+ await trigger(data);
223
+ } catch (err) {
224
+ if (err instanceof MutationError) {
225
+ // Use helper methods for better UX
226
+ if (err.isNetworkError()) {
227
+ toast.error('Network error. Please check your connection.');
228
+ } else if (err.isValidationError()) {
229
+ toast.error(err.getUserMessage());
230
+ } else {
231
+ // Generic error handling
232
+ toast.error('Something went wrong. Please try again.');
233
+ }
234
+
235
+ // Log detailed error for debugging
236
+ console.error('Mutation context:', err.context);
237
+ console.error('Original error:', err.originalError);
238
+ }
239
+ }
240
+ };
241
+
242
+ // Or use the error state directly
243
+ if (error) {
244
+ return <Alert>{error.getUserMessage()}</Alert>;
245
+ }
246
+
247
+ return <form onSubmit={handleSubmit}>...</form>;
248
+ }
249
+ ```
250
+
251
+ ## Key Structure
252
+
253
+ **Important:** swr-catalyst requires a specific key structure for all hooks and utilities to work properly.
254
+
255
+ ### SWRKey Type
256
+
257
+ ```typescript
258
+ type SWRKey<T = unknown> = {
259
+ id: string; // Required: Unique identifier for the cache entry
260
+ group?: string; // Optional: Group name for batch operations
261
+ data: T; // Required: The actual SWR key (URL, array, etc.)
262
+ } | null;
263
+ ```
264
+
265
+ ### Why Structured Keys?
266
+
267
+ The structured key format enables powerful cache management features:
268
+
269
+ - **`id`**: Allows `mutateById()` to update specific cache entries
270
+ - **`group`**: Enables `mutateByGroup()` to batch-update related caches
271
+ - **`data`**: The actual key passed to SWR's fetcher function
272
+
273
+ ## Key Management
274
+
275
+ The library handles key stability internally using deep equality comparison, so you don't need to wrap the key object yourself. The `useStableKey` hook automatically detects when values change, even for complex nested objects.
276
+
277
+ ```typescript
278
+ // ✅ Simple primitive value for data
279
+ const { trigger: createTodo } = useSWRCreate(
280
+ { id: 'todos', data: '/api/todos' },
281
+ createTodoAPI
282
+ );
283
+
284
+ // ✅ Complex object data - no useMemo needed!
285
+ // The library handles deep equality automatically
286
+ const { trigger: createTodo } = useSWRCreate(
287
+ { id: 'todos', data: { url: '/api/todos', userId: user.id } },
288
+ createTodoAPI
289
+ );
290
+
291
+ // ✅ Even deeply nested objects work automatically
292
+ const { trigger: createTodo } = useSWRCreate(
293
+ {
294
+ id: 'todos',
295
+ data: {
296
+ url: '/api/todos',
297
+ params: { userId: user.id, filter: 'active' }
298
+ }
299
+ },
300
+ createTodoAPI
301
+ );
302
+ ```
303
+
304
+ ## API Reference
305
+
306
+ This library exports a set of hooks and utility functions to streamline your data mutation workflow.
307
+
308
+ ### Hooks
309
+
310
+ #### `useSWRCreate(key, createFunction, options)`
311
+
312
+ A hook for creating new data. It handles optimistic updates, loading states, and revalidates the cache upon success.
313
+
314
+ * **`key`**: A `SWRKey` object that will be revalidated after creation.
315
+ * **`createFunction`**: An async function `(data) => Promise<NewData>` that performs the API call.
316
+ * **`options`** (optional): Configuration for optimistic updates (`optimisticUpdate`, `rollbackOnError`).
317
+
318
+ **Returns:** `{ trigger, isMutating, error }`
319
+
320
+ #### `useSWRUpdate(key, updateFunction, options)`
321
+
322
+ A hook for updating existing data.
323
+
324
+ * **`key`**: A `SWRKey` object that will be revalidated after the update.
325
+ * **`updateFunction`**: An async function `(id, data) => Promise<UpdatedData>` that performs the API call.
326
+ * **`options`** (optional): Configuration for optimistic updates.
327
+
328
+ **Returns:** `{ trigger, isMutating, error }`
329
+
330
+ #### `useSWRDelete(key, deleteFunction, options)`
331
+
332
+ A hook for deleting data.
333
+
334
+ * **`key`**: A `SWRKey` object that will be revalidated after deletion.
335
+ * **`deleteFunction`**: An async function `(id) => Promise<void>` that performs the API call.
336
+ * **`options`** (optional): Configuration for optimistic updates.
337
+
338
+ **Returns:** `{ trigger, isMutating, error }`
339
+
340
+ #### `useStableKey(key)`
341
+
342
+ A utility hook that memoizes an `SWRKey` using deep equality comparison. This prevents unnecessary re-renders in child components when a key's object reference changes but its values do not. It is used internally by all mutation hooks.
343
+
344
+ * **`key`**: The `SWRKey` object to stabilize.
345
+
346
+ **Returns:** A memoized `SWRKey` with a stable reference.
347
+
348
+ ### Utilities
349
+
350
+ #### `mutateById(ids, newData, options)`
351
+
352
+ Mutates all SWR cache entries whose key `id` matches one or more provided IDs.
353
+
354
+ * **`ids`**: A single ID string or an array of IDs.
355
+ * **`newData`** (optional): The new data to set for the matched keys. If omitted, matching keys are revalidated.
356
+ * **`options`** (optional): SWR mutator options (`revalidate`, `populateCache`, etc.).
357
+
358
+ **Example:**
359
+ ```typescript
360
+ // Revalidate all caches related to 'user' and 'profile'
361
+ await mutateById(['user', 'profile']);
362
+ ```
363
+
364
+ #### `mutateByGroup(groups, newData, options)`
365
+
366
+ Mutates all SWR cache entries whose key `group` matches one or more provided group names.
367
+
368
+ * **`groups`**: A single group string or an array of groups.
369
+ * **`newData`** (optional): The new data to set for the matched keys. If omitted, matching keys are revalidated.
370
+ * **`options`** (optional): SWR mutator options.
371
+
372
+ **Example:**
373
+ ```typescript
374
+ // Update all caches in the 'user-data' group without revalidating
375
+ await mutateByGroup('user-data', updatedData, { revalidate: false });
376
+ ```
377
+
378
+ #### `resetCache(preservedKeys)`
379
+
380
+ Clears the entire SWR cache, with an option to preserve specific entries by their `id`.
381
+
382
+ * **`preservedKeys`** (optional): A single ID string or an array of IDs to exclude from the reset.
383
+
384
+ **Example:**
385
+ ```typescript
386
+ // On logout, clear all data except for public content
387
+ await resetCache(['public-posts', 'app-config']);
388
+ ```
389
+
390
+ #### `to(promise)`
391
+
392
+ Wraps any promise and converts it into a `[data, error]` tuple, inspired by Go's error handling style. This avoids `try/catch` blocks for cleaner async code.
393
+
394
+ * **Returns:** A promise that always resolves with `[data, null]` on success or `[null, error]` on failure.
395
+
396
+ **Example:**
397
+ ```tsx
398
+ import { to } from 'swr-catalyst';
399
+
400
+ const [result, error] = await to(createTodo({ title: 'New item' }));
401
+
402
+ if (error) {
403
+ console.error('Mutation failed:', error);
404
+ }
405
+ ```
406
+
407
+ #### `swrMutate(mutate, key, data, shouldRevalidate)`
408
+
409
+ A type-safe wrapper around SWR's `mutate` function that correctly handles the structured `SWRKey` object.
410
+
411
+ * **`mutate`**: The `mutate` function from `useSWRConfig()`.
412
+ * **`key`**: The `SWRKey` to mutate.
413
+ * **`data`** (optional): The new data to update the cache with.
414
+ * **`shouldRevalidate`** (optional): Whether to revalidate after mutation.
415
+
416
+ **Example:**
417
+ ```typescript
418
+ // Perform an optimistic update without revalidation
419
+ await swrMutate(mutate, key, optimisticData, false);
420
+ ```
421
+
422
+ #### `swrGetCache(cache, key)`
423
+
424
+ A type-safe wrapper around SWR's `cache.get()` method that correctly handles the structured `SWRKey` object.
425
+
426
+ * **`cache`**: The `cache` object from `useSWRConfig()`.
427
+ * **`key`**: The `SWRKey` to look up.
428
+
429
+ **Returns:** The cached data or `undefined`.
430
+
431
+ **Example:**
432
+ ```typescript
433
+ const { cache } = useSWRConfig();
434
+ const cachedTodos = swrGetCache(cache, { id: 'todos', data: '/api/todos' });
435
+ ```
436
+
437
+ ## Bundle Size
438
+
439
+ | Package | Minified | Minified + Gzipped |
440
+ |---------|----------|-------------------|
441
+ | swr-catalyst | ~11.7KB | ~3.2KB |
442
+
443
+ Zero dependencies beyond peer dependencies (`react` and `swr`).
444
+
445
+ ## Contributing
446
+
447
+ Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details.
448
+
449
+ ## License
450
+
451
+ MIT © [Pedro Barbosa](https://github.com/pedroab0)
452
+
453
+ ---
454
+
455
+ <div align="center">
456
+ Made with ❤️ for the SWR community
457
+ </div>
@@ -0,0 +1,120 @@
1
+ import { MutationErrorContext } from './types';
2
+ /**
3
+ * Custom error class for mutation operations.
4
+ * Provides consistent error handling with context across all hooks and utilities.
5
+ */
6
+ export declare class MutationError extends Error {
7
+ readonly name = "MutationError";
8
+ readonly context: MutationErrorContext;
9
+ readonly originalError: unknown;
10
+ /**
11
+ * Creates a new MutationError with context and original error information.
12
+ *
13
+ * @param message - Human-readable error message describing what failed
14
+ * @param context - Contextual information about the mutation operation
15
+ * @param context.operation - Type of mutation: 'create', 'update', or 'delete'
16
+ * @param context.key - The SWR cache key that was being mutated
17
+ * @param context.data - The data that was being sent (for create/update)
18
+ * @param context.id - The ID of the resource (for update/delete)
19
+ * @param context.timestamp - When the error occurred (milliseconds since epoch)
20
+ * @param originalError - The underlying error that caused the mutation to fail
21
+ *
22
+ * @example
23
+ * throw new MutationError(
24
+ * 'Failed to create todo',
25
+ * {
26
+ * operation: 'create',
27
+ * key: { id: 'todos', data: '/api/todos' },
28
+ * data: { title: 'New todo' },
29
+ * timestamp: Date.now()
30
+ * },
31
+ * new Error('Network request failed')
32
+ * );
33
+ */
34
+ constructor(message: string, context: MutationErrorContext, originalError: unknown);
35
+ /**
36
+ * Returns a user-friendly error message suitable for displaying in UI.
37
+ *
38
+ * Converts technical operation names to user-friendly verbs and includes
39
+ * the resource name from the cache key.
40
+ *
41
+ * @returns A formatted error message like "Failed to add todos. Please try again."
42
+ *
43
+ * @example
44
+ * const error = new MutationError(
45
+ * 'Failed to create resource "todos"',
46
+ * { operation: 'create', key: { id: 'todos' }, timestamp: Date.now() },
47
+ * originalErr
48
+ * );
49
+ * console.log(error.getUserMessage()); // "Failed to add todos. Please try again."
50
+ */
51
+ getUserMessage(): string;
52
+ /**
53
+ * Serializes the error to a JSON-compatible object for logging and monitoring.
54
+ *
55
+ * Useful for sending error details to error tracking services like Sentry,
56
+ * LogRocket, or custom logging systems.
57
+ *
58
+ * @returns An object containing all error details including context and original error
59
+ *
60
+ * @example
61
+ * const errorData = mutationError.toJSON();
62
+ * Sentry.captureException(errorData);
63
+ *
64
+ * @example
65
+ * // Returns:
66
+ * // {
67
+ * // name: 'MutationError',
68
+ * // message: 'Failed to create resource "todos"',
69
+ * // context: { operation: 'create', key: {...}, ... },
70
+ * // originalError: { name: 'Error', message: '...', stack: '...' },
71
+ * // stack: '...'
72
+ * // }
73
+ */
74
+ toJSON(): {
75
+ name: string;
76
+ message: string;
77
+ context: MutationErrorContext;
78
+ originalError: string | {
79
+ name: string;
80
+ message: string;
81
+ stack: string | undefined;
82
+ };
83
+ stack: string | undefined;
84
+ };
85
+ /**
86
+ * Determines if the error was caused by network-related issues.
87
+ *
88
+ * Checks the original error's name and message for common network error indicators
89
+ * like 'NetworkError', 'fetch', 'network', or 'timeout'.
90
+ *
91
+ * @returns `true` if the error appears to be network-related, `false` otherwise
92
+ *
93
+ * @example
94
+ * if (error.isNetworkError()) {
95
+ * // Show retry button
96
+ * toast.error('Network error. Please try again.', {
97
+ * action: { label: 'Retry', onClick: handleRetry }
98
+ * });
99
+ * }
100
+ */
101
+ isNetworkError(): boolean;
102
+ /**
103
+ * Determines if the error was caused by validation failures.
104
+ *
105
+ * Checks the original error's message for common validation error indicators
106
+ * like 'validation', 'invalid', or 'required'.
107
+ *
108
+ * @returns `true` if the error appears to be validation-related, `false` otherwise
109
+ *
110
+ * @example
111
+ * if (error.isValidationError()) {
112
+ * // Show validation message without retry option
113
+ * toast.error(error.getUserMessage());
114
+ * } else {
115
+ * // Show generic error with retry
116
+ * toast.error('Something went wrong', { action: 'Retry' });
117
+ * }
118
+ */
119
+ isValidationError(): boolean;
120
+ }
@@ -0,0 +1,98 @@
1
+ import { SWRKey } from '../../types';
2
+ /**
3
+ * The type of mutation operation that was being performed.
4
+ *
5
+ * @remarks
6
+ * Used in error context to identify what operation failed and generate appropriate error messages.
7
+ */
8
+ export type MutationOperation = "create" | "update" | "delete";
9
+ /**
10
+ * Contextual information about a failed mutation operation.
11
+ *
12
+ * This context is attached to every MutationError to provide detailed information
13
+ * about what operation failed, which resource was involved, and when it happened.
14
+ * This data is useful for error tracking, debugging, and user feedback.
15
+ *
16
+ * @example
17
+ * // Context for a failed create operation
18
+ * {
19
+ * operation: 'create',
20
+ * key: { id: 'todos', data: '/api/todos' },
21
+ * data: { title: 'New todo' },
22
+ * timestamp: 1698345600000
23
+ * }
24
+ *
25
+ * @example
26
+ * // Context for a failed update operation
27
+ * {
28
+ * operation: 'update',
29
+ * key: { id: 'todos', data: '/api/todos' },
30
+ * data: { title: 'Updated title' },
31
+ * id: 123,
32
+ * timestamp: 1698345600000
33
+ * }
34
+ *
35
+ * @example
36
+ * // Context for a failed delete operation
37
+ * {
38
+ * operation: 'delete',
39
+ * key: { id: 'todos', data: '/api/todos' },
40
+ * id: 123,
41
+ * timestamp: 1698345600000
42
+ * }
43
+ */
44
+ export type MutationErrorContext = {
45
+ /**
46
+ * The type of mutation that failed: 'create', 'update', or 'delete'.
47
+ */
48
+ operation: MutationOperation;
49
+ /**
50
+ * The structured SWR cache key that was being mutated.
51
+ * Contains the resource ID, optional group, and data endpoint.
52
+ * Can be null if the error occurred outside of a specific cache context.
53
+ */
54
+ key: SWRKey | null;
55
+ /**
56
+ * The data that was being sent to the server.
57
+ * Present for 'create' and 'update' operations.
58
+ *
59
+ * @example { title: 'New todo', completed: false }
60
+ */
61
+ data?: unknown;
62
+ /**
63
+ * The ID of the resource being modified.
64
+ * Present for 'update' and 'delete' operations.
65
+ *
66
+ * @example 123, "todo-abc", "user-456"
67
+ */
68
+ id?: string | number;
69
+ /**
70
+ * Timestamp (milliseconds since Unix epoch) when the error occurred.
71
+ * Useful for error tracking and debugging timing-related issues.
72
+ *
73
+ * @example 1698345600000
74
+ */
75
+ timestamp: number;
76
+ };
77
+ /**
78
+ * Extended Error constructor type with V8's captureStackTrace method.
79
+ *
80
+ * This type is used to properly type the Error constructor when calling
81
+ * the V8-specific `captureStackTrace` method, which is not part of the
82
+ * standard ECMAScript Error type but is available in V8-based environments
83
+ * (Node.js, Chrome, Edge, etc.).
84
+ *
85
+ * @remarks
86
+ * This is an internal type used for better stack trace handling in MutationError.
87
+ * The type assertion is necessary because TypeScript's built-in Error type doesn't
88
+ * include V8-specific extensions.
89
+ */
90
+ export type ErrorConstructorWithStackTrace = typeof Error & {
91
+ /**
92
+ * V8-specific method to capture a stack trace.
93
+ *
94
+ * @param targetObject - The object to attach the stack trace to
95
+ * @param constructorOpt - Optional constructor to exclude from the stack trace
96
+ */
97
+ captureStackTrace(targetObject: object, constructorOpt?: unknown): void;
98
+ };
@@ -0,0 +1,2 @@
1
+ export { MutationError } from './MutationErrors';
2
+ export type { MutationErrorContext, MutationOperation, } from './MutationErrors/types';
@@ -0,0 +1,4 @@
1
+ export { useStableKey } from './useStableKey';
2
+ export { useSWRCreate } from './useSWRCreate';
3
+ export { useSWRDelete } from './useSWRDelete';
4
+ export { useSWRUpdate } from './useSWRUpdate';