UNPKG

@tanstack/db

Version:

A reactive client store for building super fast apps on sync

122 lines (121 loc) • 4.32 kB
import { createTransaction } from "./transactions.js"; import { normalizeError } from "./utils/error.js"; import { DebounceCallDroppedError, ThrottleCallDroppedError, QueueCapacityExceededError, QueueDisposedError, PacedTransactionManualCommitError } from "./errors.js"; function createPacedMutations(config) { const { onMutate, mutationFn, strategy, ...transactionConfig } = config; let activeTransaction = null; const strategyCommits = /* @__PURE__ */ new WeakMap(); let optimisticFrame; function getTransaction(isolated = false) { if (!isolated && activeTransaction?.state === `pending`) return activeTransaction; const transaction = createTransaction({ ...transactionConfig, mutationFn, autoCommit: false }); strategyCommits.set(transaction, transaction.commit.bind(transaction)); transaction.commit = () => { throw new PacedTransactionManualCommitError(); }; if (!isolated) activeTransaction = transaction; return transaction; } function commit(transaction, onStarted) { if (activeTransaction === transaction) activeTransaction = null; if (transaction.state === `failed`) return transaction; if (transaction.state !== `pending`) { throw new Error( `Strategy callback called but transaction is in state "${transaction.state}". Expected "pending".` ); } const strategyCommit = strategyCommits.get(transaction); if (!strategyCommit) throw new Error(`Paced transaction has no strategy-owned commit`); const completion = strategyCommit(); onStarted?.(completion); completion.catch(() => { }); return transaction; } function applyOptimistic(transaction, variables, newlyCreated) { const parent = optimisticFrame; if (parent?.transaction === transaction) parent.admittedNestedCall = true; const frame = { transaction, admittedNestedCall: false }; optimisticFrame = frame; try { transaction.mutate(() => onMutate(variables)); } catch (error) { if (newlyCreated || frame.admittedNestedCall) { void transaction.isPersisted.promise.catch(() => { }); transaction.rollback({ error: normalizeError(error), isSecondaryRollback: !frame.admittedNestedCall }); if (activeTransaction === transaction) activeTransaction = null; } throw error; } finally { optimisticFrame = parent; } } function mutate(variables) { if (strategy._type === `debounce` || strategy._type === `throttle`) { let transaction2; let completion; const onAdmit = () => { if (transaction2) return transaction2; const previous2 = activeTransaction; transaction2 = getTransaction(); applyOptimistic(transaction2, variables, transaction2 !== previous2); return transaction2; }; const admitted = strategy.execute( () => { return commit(onAdmit(), (started) => { completion = started; }); }, onAdmit, () => completion ); if (admitted !== false) return onAdmit(); const dropped = getTransaction(true); applyOptimistic(dropped, variables, true); dropped.rollback({ error: strategy._type === `debounce` ? new DebounceCallDroppedError() : new ThrottleCallDroppedError(), isSecondaryRollback: true }); return dropped; } const previous = activeTransaction; const transaction = getTransaction(strategy._type === `queue`); applyOptimistic(transaction, variables, transaction !== previous); try { let completion; const admitted = strategy.execute( () => commit(transaction, (started) => { completion = started; }), void 0, () => completion ); if (strategy._type === `queue` && admitted === false) { transaction.rollback({ error: new QueueCapacityExceededError(), isSecondaryRollback: true }); } } catch (error) { if (!(error instanceof QueueDisposedError)) throw error; transaction.rollback({ error, isSecondaryRollback: true }); } return transaction; } return mutate; } export { createPacedMutations }; //# sourceMappingURL=paced-mutations.js.map