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
@@ -0,0 +1,46 @@
1
+ import { Cache, ScopedMutator } from 'swr';
2
+ import { SWRKey } from '../../../../types';
3
+ /**
4
+ * Applies an optimistic update to the SWR cache before the mutation completes.
5
+ *
6
+ * This function immediately updates the cache with optimistic data, allowing the UI
7
+ * to reflect changes instantly before the server responds. It saves the original data
8
+ * so it can be rolled back if the mutation fails.
9
+ *
10
+ * @template TData - The type of data being mutated
11
+ * @template TCache - The type of the cached data
12
+ *
13
+ * @param cache - The SWR cache instance from useSWRConfig()
14
+ * @param mutate - The SWR mutate function from useSWRConfig()
15
+ * @param stableKey - The stable SWR cache key to update
16
+ * @param payload - Configuration object containing:
17
+ * - `data`: The new data being sent to the server
18
+ * - `optimisticUpdateFn`: Function that receives current cache data and new data,
19
+ * and returns the optimistically updated cache
20
+ *
21
+ * @returns The original cache data before the optimistic update, for potential rollback
22
+ *
23
+ * @example
24
+ * // Add new todo optimistically
25
+ * const originalData = await applyOptimisticUpdate(cache, mutate, key, {
26
+ * data: newTodo,
27
+ * optimisticUpdateFn: (todos, newTodo) => [...todos, { ...newTodo, id: 'temp' }]
28
+ * });
29
+ *
30
+ * @example
31
+ * // Update todo optimistically
32
+ * const originalData = await applyOptimisticUpdate(cache, mutate, key, {
33
+ * data: { id: 123, title: 'Updated' },
34
+ * optimisticUpdateFn: (todos, update) =>
35
+ * todos.map(t => t.id === update.id ? { ...t, ...update } : t)
36
+ * });
37
+ *
38
+ * @remarks
39
+ * - This function is used internally by mutation hooks when `optimisticUpdate` option is provided
40
+ * - The mutation is applied without revalidation (false flag) to prevent immediate server fetch
41
+ * - Always save the return value to enable rollback on error
42
+ */
43
+ export declare function applyOptimisticUpdate<TData, TCache>(cache: Cache, mutate: ScopedMutator, stableKey: SWRKey, payload: {
44
+ data: TData;
45
+ optimisticUpdateFn: (currentData: TCache | undefined, newData: TData) => TCache;
46
+ }): Promise<TCache | undefined>;
@@ -0,0 +1,46 @@
1
+ import { MutationError } from '../../../../errors';
2
+ import { SWRKey } from '../../../../types';
3
+ /**
4
+ * Creates a standardized MutationError with contextual information.
5
+ *
6
+ * This helper function constructs a consistent error object across all mutation hooks,
7
+ * including the operation type, resource information, and original error. It generates
8
+ * a descriptive error message that includes the operation, resource name, and any ID.
9
+ *
10
+ * @param operation - The type of mutation operation: 'create', 'update', or 'delete'
11
+ * @param stableKey - The SWR cache key that was being mutated (or null if unknown)
12
+ * @param err - The original error thrown by the mutation function
13
+ * @param additionalContext - Optional additional context:
14
+ * - `data`: The data being sent (for create/update operations)
15
+ * - `id`: The resource ID (for update/delete operations)
16
+ *
17
+ * @returns A new MutationError instance with full context
18
+ *
19
+ * @example
20
+ * // Create error for failed creation
21
+ * const error = createMutationError('create', key, err, {
22
+ * data: { title: 'New todo' }
23
+ * });
24
+ * // Error message: "Failed to create resource "todos". Network error"
25
+ *
26
+ * @example
27
+ * // Create error for failed update with ID
28
+ * const error = createMutationError('update', key, err, {
29
+ * data: { title: 'Updated' },
30
+ * id: 123
31
+ * });
32
+ * // Error message: "Failed to update resource "todos" with ID 123. Validation error"
33
+ *
34
+ * @example
35
+ * // Create error for failed deletion
36
+ * const error = createMutationError('delete', key, err, { id: 123 });
37
+ * // Error message: "Failed to delete resource "todos" with ID 123. Not found"
38
+ *
39
+ * @remarks
40
+ * This function is used internally by all mutation hooks to create consistent error objects.
41
+ * The resulting MutationError includes helper methods like `isNetworkError()` and `getUserMessage()`.
42
+ */
43
+ export declare function createMutationError(operation: "create" | "update" | "delete", stableKey: SWRKey, err: unknown, additionalContext?: {
44
+ data?: unknown;
45
+ id?: string | number;
46
+ }): MutationError;
@@ -0,0 +1,51 @@
1
+ import { ScopedMutator } from 'swr';
2
+ import { MutationError } from '../../../../errors';
3
+ import { SWRKey } from '../../../../types';
4
+ /**
5
+ * Executes a mutation function with automatic error handling and cache rollback.
6
+ *
7
+ * This helper function orchestrates the mutation execution flow:
8
+ * 1. Executes the mutation function
9
+ * 2. On success: Revalidates the cache
10
+ * 3. On error: Optionally rolls back optimistic updates, calls error handler, and re-throws
11
+ *
12
+ * @template TResult - The type of data returned by the mutation
13
+ * @template TCache - The type of the cached data
14
+ *
15
+ * @param mutationFn - The async function to execute (API call)
16
+ * @param options - Configuration object:
17
+ * - `mutate`: The SWR mutate function from useSWRConfig()
18
+ * - `stableKey`: The stable SWR cache key
19
+ * - `shouldRollback`: Whether to rollback optimistic update on error
20
+ * - `originalData`: The original cache data to restore on rollback
21
+ * - `onError`: Callback function to handle errors (typically sets error state)
22
+ *
23
+ * @returns The result from the mutation function
24
+ * @throws Re-throws the MutationError after handling rollback and error callback
25
+ *
26
+ * @example
27
+ * // Used internally by mutation hooks
28
+ * const result = await executeMutation(
29
+ * () => api.post('/todos', newTodo),
30
+ * {
31
+ * mutate,
32
+ * stableKey,
33
+ * shouldRollback: rollbackOnError && !!optimisticUpdate,
34
+ * originalData,
35
+ * onError: (err) => setError(err)
36
+ * }
37
+ * );
38
+ *
39
+ * @remarks
40
+ * - This function is used internally by mutation hooks to reduce code duplication
41
+ * - Automatically revalidates cache on success
42
+ * - Handles rollback only if both shouldRollback is true and originalData exists
43
+ * - Always calls onError callback before re-throwing to allow state updates
44
+ */
45
+ export declare function executeMutation<TResult, TCache>(mutationFn: () => Promise<TResult>, options: {
46
+ mutate: ScopedMutator;
47
+ stableKey: SWRKey;
48
+ shouldRollback: boolean;
49
+ originalData?: TCache;
50
+ onError: (error: MutationError) => void;
51
+ }): Promise<TResult>;
@@ -0,0 +1,4 @@
1
+ export { applyOptimisticUpdate } from './applyOptimisticUpdate';
2
+ export { createMutationError } from './createMutationError';
3
+ export { executeMutation } from './executeMutation';
4
+ export { rollbackOptimisticUpdate } from './rollbackOptimisticUpdate';
@@ -0,0 +1,31 @@
1
+ import { ScopedMutator } from 'swr';
2
+ import { SWRKey } from '../../../../types';
3
+ /**
4
+ * Rolls back an optimistic update by restoring the original cache data.
5
+ *
6
+ * This function is called when a mutation fails and `rollbackOnError` is true.
7
+ * It restores the cache to its state before the optimistic update was applied,
8
+ * ensuring the UI accurately reflects the server state.
9
+ *
10
+ * @param mutate - The SWR mutate function from useSWRConfig()
11
+ * @param stableKey - The stable SWR cache key to restore
12
+ * @param originalData - The original cache data to restore (saved from applyOptimisticUpdate)
13
+ *
14
+ * @returns Promise that resolves when the rollback is complete
15
+ *
16
+ * @example
17
+ * // Used internally by mutation hooks on error
18
+ * try {
19
+ * const result = await apiCall();
20
+ * } catch (error) {
21
+ * // Restore original data if mutation fails
22
+ * await rollbackOptimisticUpdate(mutate, key, originalData);
23
+ * throw error;
24
+ * }
25
+ *
26
+ * @remarks
27
+ * - This function is automatically called by mutation hooks when `rollbackOnError: true` (default)
28
+ * - The mutation is applied without revalidation (false flag) for immediate UI update
29
+ * - Set `rollbackOnError: false` in mutation options to keep optimistic data on error
30
+ */
31
+ export declare function rollbackOptimisticUpdate(mutate: ScopedMutator, stableKey: SWRKey, originalData: unknown): Promise<void>;
@@ -0,0 +1 @@
1
+ export * from './helpers';
@@ -0,0 +1,89 @@
1
+ import { MutateOptions, SWRKey } from '../../types';
2
+ import { CreateFunction } from './types';
3
+ /**
4
+ * A hook for creating new data with SWR cache management and optimistic updates.
5
+ *
6
+ * @template TData - The type of data being created (e.g., `{ title: string }` for a new todo)
7
+ * @template TCache - The type of the cached data (e.g., `Todo[]` for a list of todos)
8
+ * @template TError - The type of error that might be thrown (defaults to `Error`)
9
+ *
10
+ * @param key - The structured SWR cache key to revalidate after creation.
11
+ * Must be an object with `id`, optional `group`, and `data` properties, or `null`.
12
+ * **Important:** The `key` object should have a stable reference (use `useMemo` if creating inline).
13
+ * The `data` property should be a primitive value (string, number) or a stable reference.
14
+ * Example: `{ id: 'todos', group: 'lists', data: '/api/todos' }`
15
+ *
16
+ * @param createFunction - The async function that performs the API call.
17
+ * Should accept the new data and return the created item from the server.
18
+ * Example: `async (newTodo) => api.post('/todos', newTodo)`
19
+ *
20
+ * @param options - Optional configuration:
21
+ * - `optimisticUpdate`: Function to update cache immediately before API call completes.
22
+ * Receives `(currentData, newData)` and should return the optimistically updated cache.
23
+ * Example: `(todos, newTodo) => [...todos, { ...newTodo, id: 'temp-id' }]`
24
+ * - `rollbackOnError`: Whether to revert optimistic update if API fails (default: `true`)
25
+ *
26
+ * @returns An object containing:
27
+ * - `trigger`: Async function to call when creating data. Returns the created data from server.
28
+ * - `isMutating`: Boolean indicating if mutation is in progress (useful for loading states)
29
+ * - `error`: Error object if mutation failed, otherwise `null`
30
+ *
31
+ * @example
32
+ * // Basic usage with structured key
33
+ * const { trigger, isMutating, error } = useSWRCreate(
34
+ * { id: 'todos', group: 'lists', data: '/api/todos' },
35
+ * async (newTodo) => api.post('/todos', newTodo)
36
+ * );
37
+ *
38
+ * await trigger({ title: 'Buy milk' });
39
+ *
40
+ * @example
41
+ * // With optimistic updates
42
+ * const { trigger, isMutating } = useSWRCreate(
43
+ * { id: 'todos', data: '/api/todos' },
44
+ * createTodoAPI,
45
+ * {
46
+ * optimisticUpdate: (todos, newTodo) => [
47
+ * ...todos,
48
+ * { ...newTodo, id: 'temp-' + Date.now() }
49
+ * ],
50
+ * rollbackOnError: true
51
+ * }
52
+ * );
53
+ *
54
+ * // UI updates immediately, then syncs with server
55
+ * await trigger({ title: 'New task', completed: false });
56
+ *
57
+ * @example
58
+ * // With error handling
59
+ * const { trigger, error } = useSWRCreate(
60
+ * { id: 'todos', data: '/api/todos' },
61
+ * createTodoAPI
62
+ * );
63
+ *
64
+ * try {
65
+ * const created = await trigger({ title: 'New todo' });
66
+ * console.log('Created with ID:', created.id);
67
+ * } catch (err) {
68
+ * console.error('Failed to create:', err);
69
+ * }
70
+ *
71
+ * // Or use the error state
72
+ * if (error) {
73
+ * return <div>Error: {error.message}</div>;
74
+ * }
75
+ *
76
+ * @example
77
+ * // Using with group for batch cache operations
78
+ * const { trigger } = useSWRCreate(
79
+ * { id: 'user-todos', group: 'user-data', data: '/api/user/todos' },
80
+ * createTodoAPI
81
+ * );
82
+ *
83
+ * // Later, you can use mutateByGroup('user-data') to revalidate all related caches
84
+ */
85
+ export declare function useSWRCreate<TData = unknown, TCache = unknown, TError = Error>(key: SWRKey<TData>, createFunction: CreateFunction<TData, TCache>, options?: MutateOptions<TCache, TData>): {
86
+ trigger: (data: TData) => Promise<TCache | undefined>;
87
+ isMutating: boolean;
88
+ error: TError | null;
89
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Function type for creating a new resource via API.
3
+ *
4
+ * @template TData - The type of data being sent to create the resource
5
+ * @template TResult - The type of data returned from the API
6
+ *
7
+ * @param data - The data for the new resource
8
+ * @returns Promise resolving to the created resource from the server
9
+ *
10
+ * @example
11
+ * // Basic create function
12
+ * const createTodo: CreateFunction<TodoInput, Todo> = async (data) => {
13
+ * const response = await fetch('/api/todos', {
14
+ * method: 'POST',
15
+ * body: JSON.stringify(data),
16
+ * });
17
+ * return response.json();
18
+ * };
19
+ *
20
+ * @example
21
+ * // With axios
22
+ * const createUser: CreateFunction<UserInput, User> = async (data) => {
23
+ * const { data: user } = await axios.post('/api/users', data);
24
+ * return user;
25
+ * };
26
+ */
27
+ export type CreateFunction<TData, TResult> = (data: TData) => Promise<TResult>;
@@ -0,0 +1,103 @@
1
+ import { MutateOptions, SWRKey } from '../../types';
2
+ import { DeleteFunction } from './types';
3
+ /**
4
+ * A hook for deleting data with SWR cache management and optimistic updates.
5
+ *
6
+ * @template TCache - The type of the cached data (e.g., `Todo[]` for a list of todos)
7
+ * @template TResult - The type of the result returned by the delete API (e.g., `{ success: boolean }`)
8
+ * @template TError - The type of error that might be thrown (defaults to `Error`)
9
+ *
10
+ * @param key - The structured SWR cache key to revalidate after deletion.
11
+ * Must be an object with `id`, optional `group`, and `data` properties, or `null`.
12
+ * **Important:** The `key` object should have a stable reference (use `useMemo` if creating inline).
13
+ * The `data` property should be a primitive value (string, number) or a stable reference.
14
+ * Example: `{ id: 'todos', group: 'lists', data: '/api/todos' }`
15
+ *
16
+ * @param deleteFunction - The async function that performs the API delete call.
17
+ * Should accept an ID and return the result from the server.
18
+ * Example: `async (todoId) => api.delete(\`/todos/\${todoId}\`)`
19
+ *
20
+ * @param options - Optional configuration:
21
+ * - `optimisticUpdate`: Function to update cache immediately before API call completes.
22
+ * Receives `(currentData, itemId)` and should return the optimistically updated cache.
23
+ * Example: `(todos, id) => todos.filter(todo => todo.id !== id)`
24
+ * - `rollbackOnError`: Whether to revert optimistic update if API fails (default: `true`)
25
+ *
26
+ * @returns An object containing:
27
+ * - `trigger`: Async function to call when deleting data. Accepts the ID and returns the API result.
28
+ * - `isMutating`: Boolean indicating if mutation is in progress (useful for loading states)
29
+ * - `error`: Error object if mutation failed, otherwise `null`
30
+ *
31
+ * @example
32
+ * // Basic usage with structured key
33
+ * const { trigger, isMutating, error } = useSWRDelete(
34
+ * { id: 'todos', group: 'lists', data: '/api/todos' },
35
+ * async (todoId) => api.delete(\`/todos/\${todoId}\`)
36
+ * );
37
+ *
38
+ * await trigger(123);
39
+ *
40
+ * @example
41
+ * // With optimistic updates (instant removal from UI)
42
+ * const { trigger, isMutating } = useSWRDelete(
43
+ * { id: 'todos', data: '/api/todos' },
44
+ * deleteTodoAPI,
45
+ * {
46
+ * optimisticUpdate: (todos, todoId) =>
47
+ * todos.filter(todo => todo.id !== todoId),
48
+ * rollbackOnError: true
49
+ * }
50
+ * );
51
+ *
52
+ * // Todo disappears immediately, then syncs with server
53
+ * await trigger(todoId);
54
+ *
55
+ * @example
56
+ * // With error handling
57
+ * const { trigger, error } = useSWRDelete(
58
+ * { id: 'todos', data: '/api/todos' },
59
+ * deleteTodoAPI
60
+ * );
61
+ *
62
+ * try {
63
+ * const result = await trigger(todoId);
64
+ * console.log('Deleted:', result);
65
+ * } catch (err) {
66
+ * console.error('Failed to delete:', err);
67
+ * }
68
+ *
69
+ * // Or use the error state
70
+ * if (error) {
71
+ * return <div>Error: {error.message}</div>;
72
+ * }
73
+ *
74
+ * @example
75
+ * // With loading state for button
76
+ * const { trigger, isMutating } = useSWRDelete(
77
+ * { id: 'todos', data: '/api/todos' },
78
+ * deleteTodoAPI
79
+ * );
80
+ *
81
+ * return (
82
+ * <button
83
+ * onClick={() => trigger(todoId)}
84
+ * disabled={isMutating}
85
+ * >
86
+ * {isMutating ? 'Deleting...' : 'Delete'}
87
+ * </button>
88
+ * );
89
+ *
90
+ * @example
91
+ * // Using with group for batch cache operations
92
+ * const { trigger } = useSWRDelete(
93
+ * { id: 'user-todos', group: 'user-data', data: '/api/user/todos' },
94
+ * deleteTodoAPI
95
+ * );
96
+ *
97
+ * // Later, you can use mutateByGroup('user-data') to revalidate all related caches
98
+ */
99
+ export declare function useSWRDelete<TCache = unknown, TResult = unknown, TError = Error>(key: SWRKey, deleteFunction: DeleteFunction<TResult>, options?: MutateOptions<TCache, string | number>): {
100
+ trigger: (id: string | number) => Promise<TResult | undefined>;
101
+ isMutating: boolean;
102
+ error: TError | null;
103
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Function type for deleting a resource via API.
3
+ *
4
+ * @template TResult - The type of data returned from the API (e.g., confirmation message, deleted item)
5
+ *
6
+ * @param id - The unique identifier of the resource to delete
7
+ * @returns Promise resolving to the API response (often a success message or the deleted item)
8
+ *
9
+ * @example
10
+ * // Basic delete function
11
+ * const deleteTodo: DeleteFunction<{ success: boolean }> = async (id) => {
12
+ * const response = await fetch(`/api/todos/${id}`, {
13
+ * method: 'DELETE',
14
+ * });
15
+ * return response.json();
16
+ * };
17
+ *
18
+ * @example
19
+ * // With axios, returning deleted item
20
+ * const deleteUser: DeleteFunction<User> = async (id) => {
21
+ * const { data } = await axios.delete(`/api/users/${id}`);
22
+ * return data;
23
+ * };
24
+ *
25
+ * @example
26
+ * // With error handling
27
+ * const deletePost: DeleteFunction<void> = async (id) => {
28
+ * const response = await fetch(`/api/posts/${id}`, {
29
+ * method: 'DELETE',
30
+ * });
31
+ * if (!response.ok) {
32
+ * throw new Error(`Failed to delete: ${response.statusText}`);
33
+ * }
34
+ * };
35
+ *
36
+ * @example
37
+ * // Returning just status code
38
+ * const deleteComment: DeleteFunction<number> = async (id) => {
39
+ * const response = await fetch(`/api/comments/${id}`, {
40
+ * method: 'DELETE',
41
+ * });
42
+ * return response.status;
43
+ * };
44
+ */
45
+ export type DeleteFunction<TResult> = (id: string | number) => Promise<TResult>;
@@ -0,0 +1,121 @@
1
+ import { MutateOptions, SWRKey } from '../../types';
2
+ import { UpdateFunction } from './types';
3
+ /**
4
+ * A hook for updating existing data with SWR cache management and optimistic updates.
5
+ *
6
+ * @template TData - The type of data being updated (e.g., `{ title: string }` for updating a todo)
7
+ * @template TCache - The type of the cached data (e.g., `Todo[]` for a list of todos)
8
+ * @template TError - The type of error that might be thrown (defaults to `Error`)
9
+ *
10
+ * @param key - The structured SWR cache key to revalidate after update.
11
+ * Must be an object with `id`, optional `group`, and `data` properties, or `null`.
12
+ * **Important:** The `key` object should have a stable reference (use `useMemo` if creating inline).
13
+ * The `data` property should be a primitive value (string, number) or a stable reference.
14
+ * Example: `{ id: 'todos', group: 'lists', data: '/api/todos' }`
15
+ *
16
+ * @param updateFunction - The async function that performs the API update call.
17
+ * Should accept an ID and the update data, returning the updated item from the server.
18
+ * Example: `async (todoId, updates) => api.patch(\`/todos/\${todoId}\`, updates)`
19
+ *
20
+ * @param options - Optional configuration:
21
+ * - `optimisticUpdate`: Function to update cache immediately before API call completes.
22
+ * Receives `(currentData, { id, data })` and should return the optimistically updated cache.
23
+ * Example: `(todos, { id, data }) => todos.map(t => t.id === id ? { ...t, ...data } : t)`
24
+ * - `rollbackOnError`: Whether to revert optimistic update if API fails (default: `true`)
25
+ *
26
+ * @returns An object containing:
27
+ * - `trigger`: Async function to call when updating data. Accepts ID and data, returns updated item.
28
+ * - `isMutating`: Boolean indicating if mutation is in progress (useful for loading states)
29
+ * - `error`: Error object if mutation failed, otherwise `null`
30
+ *
31
+ * @example
32
+ * // Basic usage with structured key
33
+ * const { trigger, isMutating, error } = useSWRUpdate(
34
+ * { id: 'todos', group: 'lists', data: '/api/todos' },
35
+ * async (todoId, updates) => api.patch(\`/todos/\${todoId}\`, updates)
36
+ * );
37
+ *
38
+ * await trigger(123, { completed: true });
39
+ *
40
+ * @example
41
+ * // With optimistic updates (instant UI update)
42
+ * const { trigger, isMutating } = useSWRUpdate(
43
+ * { id: 'todos', data: '/api/todos' },
44
+ * updateTodoAPI,
45
+ * {
46
+ * optimisticUpdate: (todos, { id, data }) =>
47
+ * todos.map(todo =>
48
+ * todo.id === id ? { ...todo, ...data } : todo
49
+ * ),
50
+ * rollbackOnError: true
51
+ * }
52
+ * );
53
+ *
54
+ * // Todo updates immediately, then syncs with server
55
+ * await trigger(todoId, { title: 'Updated title' });
56
+ *
57
+ * @example
58
+ * // With error handling
59
+ * const { trigger, error } = useSWRUpdate(
60
+ * { id: 'todos', data: '/api/todos' },
61
+ * updateTodoAPI
62
+ * );
63
+ *
64
+ * try {
65
+ * const updated = await trigger(todoId, { completed: true });
66
+ * console.log('Updated:', updated);
67
+ * } catch (err) {
68
+ * console.error('Failed to update:', err);
69
+ * }
70
+ *
71
+ * // Or use the error state
72
+ * if (error) {
73
+ * return <div>Error: {error.message}</div>;
74
+ * }
75
+ *
76
+ * @example
77
+ * // With loading state for inline editing
78
+ * const { trigger, isMutating } = useSWRUpdate(
79
+ * { id: 'todos', data: '/api/todos' },
80
+ * updateTodoAPI
81
+ * );
82
+ *
83
+ * return (
84
+ * <input
85
+ * onBlur={(e) => trigger(todoId, { title: e.target.value })}
86
+ * disabled={isMutating}
87
+ * />
88
+ * );
89
+ *
90
+ * @example
91
+ * // Toggle boolean field
92
+ * const { trigger } = useSWRUpdate(
93
+ * { id: 'todos', data: '/api/todos' },
94
+ * updateTodoAPI,
95
+ * {
96
+ * optimisticUpdate: (todos, { id, data }) =>
97
+ * todos.map(t => t.id === id ? { ...t, ...data } : t)
98
+ * }
99
+ * );
100
+ *
101
+ * const toggleComplete = (todo) => {
102
+ * trigger(todo.id, { completed: !todo.completed });
103
+ * };
104
+ *
105
+ * @example
106
+ * // Using with group for batch cache operations
107
+ * const { trigger } = useSWRUpdate(
108
+ * { id: 'user-todos', group: 'user-data', data: '/api/user/todos' },
109
+ * updateTodoAPI
110
+ * );
111
+ *
112
+ * // Later, you can use mutateByGroup('user-data') to revalidate all related caches
113
+ */
114
+ export declare function useSWRUpdate<TData = unknown, TCache = unknown, TError = Error>(key: SWRKey, updateFunction: UpdateFunction<TData, TCache>, options?: MutateOptions<TCache, {
115
+ id: string | number;
116
+ data: TData;
117
+ }>): {
118
+ trigger: (id: string | number, data: TData) => Promise<TCache | undefined>;
119
+ isMutating: boolean;
120
+ error: TError | null;
121
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Function type for updating an existing resource via API.
3
+ *
4
+ * @template TData - The type of data being sent to update the resource
5
+ * @template TResult - The type of data returned from the API
6
+ *
7
+ * @param id - The unique identifier of the resource to update
8
+ * @param data - The partial or complete data to update the resource with
9
+ * @returns Promise resolving to the updated resource from the server
10
+ *
11
+ * @example
12
+ * // Basic update function with PATCH
13
+ * const updateTodo: UpdateFunction<TodoUpdate, Todo> = async (id, data) => {
14
+ * const response = await fetch(`/api/todos/${id}`, {
15
+ * method: 'PATCH',
16
+ * body: JSON.stringify(data),
17
+ * });
18
+ * return response.json();
19
+ * };
20
+ *
21
+ * @example
22
+ * // With axios and PUT
23
+ * const updateUser: UpdateFunction<UserUpdate, User> = async (id, data) => {
24
+ * const { data: user } = await axios.put(`/api/users/${id}`, data);
25
+ * return user;
26
+ * };
27
+ *
28
+ * @example
29
+ * // With error handling
30
+ * const updatePost: UpdateFunction<PostUpdate, Post> = async (id, data) => {
31
+ * const response = await fetch(`/api/posts/${id}`, {
32
+ * method: 'PATCH',
33
+ * body: JSON.stringify(data),
34
+ * });
35
+ * if (!response.ok) {
36
+ * throw new Error(`Failed to update post: ${response.statusText}`);
37
+ * }
38
+ * return response.json();
39
+ * };
40
+ */
41
+ export type UpdateFunction<TData, TResult> = (id: string | number, data: TData) => Promise<TResult>;
@@ -0,0 +1,52 @@
1
+ import { SWRKey } from '../../types';
2
+ /**
3
+ * Creates a stable reference for SWR keys to prevent unnecessary re-renders.
4
+ *
5
+ * This hook memoizes the SWR key based on its individual properties (id, group, data)
6
+ * using deep equality comparison. This ensures that the key object remains stable
7
+ * across renders when its properties haven't changed by value, not just by reference.
8
+ *
9
+ * **Key Feature:** Handles object data with deep comparison, so you don't need to
10
+ * wrap complex data in `useMemo` yourself. The hook automatically detects when the
11
+ * actual values change vs when only the object reference changes.
12
+ *
13
+ * @template T - The type of the data property in the SWR key
14
+ *
15
+ * @param key - The SWR key object to stabilize, or null if not ready
16
+ *
17
+ * @returns A memoized SWR key with stable reference, or null if key is null or invalid
18
+ *
19
+ * @example
20
+ * // Works with primitive data (string URLs)
21
+ * const { trigger } = useSWRCreate(
22
+ * { id: 'todos', data: '/api/todos' },
23
+ * createTodo
24
+ * );
25
+ *
26
+ * @example
27
+ * // Also works with object data - no useMemo needed!
28
+ * function TodoList({ userId }: { userId: number }) {
29
+ * const { trigger } = useSWRCreate(
30
+ * // This is now safe! Hook handles object comparison
31
+ * { id: 'todos', data: { url: '/api/todos', userId } },
32
+ * createTodo
33
+ * );
34
+ * }
35
+ *
36
+ * @example
37
+ * // Stable reference even with dynamic query params
38
+ * function FilteredList({ filter }: { filter: string }) {
39
+ * const { trigger } = useSWRCreate(
40
+ * // Hook detects when filter value changes, not just object reference
41
+ * { id: 'todos', data: { url: '/api/todos', params: { filter } } },
42
+ * createTodo
43
+ * );
44
+ * }
45
+ *
46
+ * @remarks
47
+ * - Uses deep equality comparison via JSON serialization for object data
48
+ * - Falls back to reference equality for non-serializable values
49
+ * - Small performance cost (~1ms) for deep comparison on each render
50
+ * - This hook is used internally by all mutation hooks (useSWRCreate, useSWRUpdate, useSWRDelete)
51
+ */
52
+ export declare function useStableKey<T = unknown>(key: SWRKey<T> | null): SWRKey<T> | null;