mutate.ts
Overview
The mutate.ts file provides a core utility function internalMutate designed to update and revalidate cached data in an advanced caching and data fetching system, such as SWR (stale-while-revalidate). It supports optimistic updates, rollback on errors, and flexible mutation strategies, including synchronous or asynchronous updates, key filtering for batch mutations, and custom mutation logic.
This function is responsible for managing mutation lifecycles including:
Applying optimistic data updates to the cache
Handling asynchronous mutation results and race conditions
Revalidating data after mutations
Optionally rolling back changes on errors
Broadcasting mutation and revalidation events to other parts of the system
internalMutate is a foundational building block that interacts deeply with the caching layer, global mutation state, and event system to maintain consistency and reliability of cached data.
Detailed Explanations
Types
KeyFilter:
A function type (key?: Arguments) => boolean used to filter cache keys for batch mutations.MutateState:
Extends the cached State<Data, any> with an optional_cfield representing the previously committed data (before optimistic updates).
Function: internalMutate
async function internalMutate<Data>(
cache: Cache,
_key: KeyFilter | Arguments,
_data?: Data | Promise<Data | undefined> | MutatorCallback<Data>,
_opts?: boolean | MutatorOptions<Data>
): Promise<Data | undefined | Array<Data | undefined>>
Purpose
Mutates the cached data for a given cache key or multiple keys filtered by a KeyFilter. It updates cache entries optimistically or synchronously/asynchronously with the provided data or mutation callback, then optionally revalidates the cache entry.
Parameters
Parameter | Type | Description |
|---|---|---|
|
| The cache instance where data is stored and mutated. |
| `KeyFilter \ | Arguments` |
| `Data \ | Promise<Data |
| `boolean \ | MutatorOptions` (optional) |
Return Value
If
_keyis a key filter function, returnsPromise<Array<Data | undefined>>resolving to an array of mutation results for all matched keys.Otherwise, returns
Promise<Data | undefined>resolving to the mutation result for the specified key.
Usage Examples
// 1. Simple mutation with new data and default options
await internalMutate(cache, ['user', 1], { name: 'Alice' });
// 2. Mutation with optimistic update and rollback on error
await internalMutate(
cache,
['user', 1],
async (currentData) => {
// Perform async update (e.g. API call)
const updatedData = await fetchUpdate();
return updatedData;
},
{
optimisticData: { name: 'Loading...' },
rollbackOnError: true,
}
);
// 3. Batch mutation for all keys matching a filter
await internalMutate(
cache,
(key) => Array.isArray(key) && key[0] === 'user',
{ active: false }
);
Implementation Details
Key Features
Key Serialization:
Usesserializeto convert cache keys (arguments) into string keys for cache lookup.Key Filtering for Batch Mutation:
When_keyis a function, iterates through all cache keys, applies the filter, and mutates all matched keys concurrently.Optimistic Update Support:
AcceptsoptimisticDataoption to immediately update the cache with a provisional value before mutation resolves.Rollback on Error:
If mutation fails androllbackOnErroris enabled, reverts cache to last committed state.Race Condition Handling:
Uses timestamping (MUTATION[key]) to prevent stale mutations from overriding newer data when multiple mutations occur concurrently.Revalidation:
After mutation, optionally triggers revalidation via registered revalidators and clears deduplication markers (FETCH,PRELOAD).Cache Helper:
The function usescreateCacheHelperto get getter and setter helpers for the cache state.Error Handling:
Errors during mutation can be thrown or swallowed based onthrowOnErroroption.
Workflow (Mutate By Key)
Serialize the key.
Obtain cache getter/setter helpers.
Retrieve global mutation and event state.
If no new data provided, trigger revalidation only.
Otherwise, prepare mutation:
Record mutation start timestamp.
Apply optimistic data if provided.
If
_datais a function, call it with current committed data.Await promise if
_datais async.Check mutation timestamp to avoid race condition updates.
On error, optionally rollback and restore committed data.
Populate cache with mutation result if enabled.
Update mutation end timestamp.
Trigger revalidation and broadcast updates.
Return mutation result or throw error.
Interaction with Other System Parts
Cache Layer:
Directly reads and writes cached data states viacacheand helper functions.Serialization (
serialize):
Converts complex keys into strings to uniquely identify cache entries.Global State (
SWRGlobalState):
Maintains mutation timestamps and registered revalidation event callbacks per key.Revalidation Events (
revalidateEvents):
Triggers events signaling cache updates to refresh dependent components.Timestamp Management (
getTimestamp):
Ensures mutation ordering and prevents race conditions by timestamp comparison.Utilities (
shared):
Type checks and object merging utilities to manage options and data safely.
Mermaid Diagram: Function Flowchart
flowchart TD
A[internalMutate Entry] --> B{Is _key a function?}
B -- Yes --> C[Filter cache keys using _key]
C --> D[For each matched key: mutateByKey]
D --> E[Return Promise.all of results]
B -- No --> F[mutateByKey(_key)]
F --> G[Serialize key]
G --> H{Is key valid?}
H -- No --> I[Return undefined]
H -- Yes --> J[Get cache getter/setter]
J --> K{Is _data provided?}
K -- No --> L[Start revalidation only]
L --> M[Return revalidated data]
K -- Yes --> N[Prepare mutation state]
N --> O{Is optimisticData set?}
O -- Yes --> P[Apply optimistic data update]
O -- No --> Q[Continue]
P --> Q
Q --> R{Is _data a function?}
R -- Yes --> S[Call _data with committed data]
R -- No --> T[Use _data as is]
S --> U{Is _data promise-like?}
T --> U
U -- Yes --> V[Await _data promise]
U -- No --> W[Continue]
V --> X{Mutation timestamp valid?}
W --> X
X -- No --> Y[Return data without cache update]
X -- Yes --> Z{Is error and rollbackOnError?}
Z -- Yes --> AA[Rollback to committed data]
Z -- No --> AB[Update cache with data]
AA --> AB
AB --> AC[Update mutation end timestamp]
AC --> AD[Start revalidation and broadcast]
AD --> AE{Is error and throwOnError?}
AE -- Yes --> AF[Throw error]
AE -- No --> AG[Return data]
Y --> AG
Summary
mutate.ts implements a robust, flexible mutation mechanism for a caching/data fetching system. It provides fine-grained control over cache updates, supports optimistic UI patterns, handles async updates safely, and ensures data consistency through revalidation and error rollback strategies. This file is a critical component that interacts with cache, global mutation state, and event systems to maintain a smooth and reliable data mutation workflow.
If you need documentation for additional files or further elaborations on integration points, please let me know!