( config: PacedMutationsConfig<TVariables, T>, )
| 85 | * ``` |
| 86 | */ |
| 87 | export function createPacedMutations< |
| 88 | TVariables = unknown, |
| 89 | T extends object = Record<string, unknown>, |
| 90 | >( |
| 91 | config: PacedMutationsConfig<TVariables, T>, |
| 92 | ): (variables: TVariables) => Transaction<T> { |
| 93 | const { onMutate, mutationFn, strategy, ...transactionConfig } = config |
| 94 | |
| 95 | // The currently active transaction (pending, not yet persisting) |
| 96 | let activeTransaction: Transaction<T> | null = null |
| 97 | |
| 98 | // Commit callback that the strategy will call when it's time to persist |
| 99 | const commitCallback = () => { |
| 100 | if (!activeTransaction) { |
| 101 | throw new Error( |
| 102 | `Strategy callback called but no active transaction exists. This indicates a bug in the strategy implementation.`, |
| 103 | ) |
| 104 | } |
| 105 | |
| 106 | if (activeTransaction.state !== `pending`) { |
| 107 | throw new Error( |
| 108 | `Strategy callback called but active transaction is in state "${activeTransaction.state}". Expected "pending".`, |
| 109 | ) |
| 110 | } |
| 111 | |
| 112 | const txToCommit = activeTransaction |
| 113 | |
| 114 | // Clear active transaction reference before committing |
| 115 | activeTransaction = null |
| 116 | |
| 117 | // Commit the transaction |
| 118 | txToCommit.commit().catch(() => { |
| 119 | // Errors are handled via transaction.isPersisted.promise |
| 120 | // This catch prevents unhandled promise rejections |
| 121 | }) |
| 122 | |
| 123 | return txToCommit |
| 124 | } |
| 125 | |
| 126 | /** |
| 127 | * Executes a mutation with the given variables. Creates a new transaction if none is active, |
| 128 | * or adds to the existing active transaction. The strategy controls when |
| 129 | * the transaction is actually committed. |
| 130 | */ |
| 131 | function mutate(variables: TVariables): Transaction<T> { |
| 132 | // Create a new transaction if we don't have an active one |
| 133 | if (!activeTransaction || activeTransaction.state !== `pending`) { |
| 134 | activeTransaction = createTransaction<T>({ |
| 135 | ...transactionConfig, |
| 136 | mutationFn, |
| 137 | autoCommit: false, |
| 138 | }) |
| 139 | } |
| 140 | |
| 141 | // Execute onMutate with variables to apply optimistic updates |
| 142 | activeTransaction.mutate(() => { |
| 143 | onMutate(variables) |
| 144 | }) |
no outgoing calls
no test coverage detected