{"version":3,"file":"paced-mutations.cjs","sources":["../../src/paced-mutations.ts"],"sourcesContent":["import { createTransaction } from './transactions'\nimport type { MutationFn, Transaction } from './types'\nimport type { Strategy } from './strategies/types'\n\n/**\n * Configuration for creating a paced mutations manager\n */\nexport interface PacedMutationsConfig<\n  TVariables = unknown,\n  T extends object = Record<string, unknown>,\n> {\n  /**\n   * Callback to apply optimistic updates immediately.\n   * Receives the variables passed to the mutate function.\n   */\n  onMutate: (variables: TVariables) => void\n  /**\n   * Function to execute the mutation on the server.\n   * Receives the transaction parameters containing all merged mutations.\n   */\n  mutationFn: MutationFn<T>\n  /**\n   * Strategy for controlling mutation execution timing\n   * Examples: debounceStrategy, queueStrategy, throttleStrategy\n   */\n  strategy: Strategy\n  /**\n   * Custom metadata to associate with transactions\n   */\n  metadata?: Record<string, unknown>\n}\n\n/**\n * Creates a paced mutations manager with pluggable timing strategies.\n *\n * This function provides a way to control when and how optimistic mutations\n * are persisted to the backend, using strategies like debouncing, queuing,\n * or throttling. The optimistic updates are applied immediately via `onMutate`,\n * and the actual persistence is controlled by the strategy.\n *\n * The returned function accepts variables of type TVariables and returns a\n * Transaction object that can be awaited to know when persistence completes\n * or to handle errors.\n *\n * @param config - Configuration including onMutate, mutationFn and strategy\n * @returns A function that accepts variables and returns a Transaction\n *\n * @example\n * ```ts\n * // Debounced mutations for auto-save\n * const updateTodo = createPacedMutations<string>({\n *   onMutate: (text) => {\n *     // Apply optimistic update immediately\n *     collection.update(id, draft => { draft.text = text })\n *   },\n *   mutationFn: async ({ transaction }) => {\n *     await api.save(transaction.mutations)\n *   },\n *   strategy: debounceStrategy({ wait: 500 })\n * })\n *\n * // Call with variables, returns a transaction\n * const tx = updateTodo('New text')\n *\n * // Await persistence or handle errors\n * await tx.isPersisted.promise\n * ```\n *\n * @example\n * ```ts\n * // Queue strategy for sequential processing\n * const addTodo = createPacedMutations<{ text: string }>({\n *   onMutate: ({ text }) => {\n *     collection.insert({ id: uuid(), text, completed: false })\n *   },\n *   mutationFn: async ({ transaction }) => {\n *     await api.save(transaction.mutations)\n *   },\n *   strategy: queueStrategy({\n *     wait: 200,\n *     addItemsTo: 'back',\n *     getItemsFrom: 'front'\n *   })\n * })\n * ```\n */\nexport function createPacedMutations<\n  TVariables = unknown,\n  T extends object = Record<string, unknown>,\n>(\n  config: PacedMutationsConfig<TVariables, T>,\n): (variables: TVariables) => Transaction<T> {\n  const { onMutate, mutationFn, strategy, ...transactionConfig } = config\n\n  // The currently active transaction (pending, not yet persisting)\n  let activeTransaction: Transaction<T> | null = null\n\n  // Commit callback that the strategy will call when it's time to persist\n  const commitCallback = () => {\n    if (!activeTransaction) {\n      throw new Error(\n        `Strategy callback called but no active transaction exists. This indicates a bug in the strategy implementation.`,\n      )\n    }\n\n    if (activeTransaction.state !== `pending`) {\n      throw new Error(\n        `Strategy callback called but active transaction is in state \"${activeTransaction.state}\". Expected \"pending\".`,\n      )\n    }\n\n    const txToCommit = activeTransaction\n\n    // Clear active transaction reference before committing\n    activeTransaction = null\n\n    // Commit the transaction\n    txToCommit.commit().catch(() => {\n      // Errors are handled via transaction.isPersisted.promise\n      // This catch prevents unhandled promise rejections\n    })\n\n    return txToCommit\n  }\n\n  /**\n   * Executes a mutation with the given variables. Creates a new transaction if none is active,\n   * or adds to the existing active transaction. The strategy controls when\n   * the transaction is actually committed.\n   */\n  function mutate(variables: TVariables): Transaction<T> {\n    // Create a new transaction if we don't have an active one\n    if (!activeTransaction || activeTransaction.state !== `pending`) {\n      activeTransaction = createTransaction<T>({\n        ...transactionConfig,\n        mutationFn,\n        autoCommit: false,\n      })\n    }\n\n    // Execute onMutate with variables to apply optimistic updates\n    activeTransaction.mutate(() => {\n      onMutate(variables)\n    })\n\n    // Save reference before calling strategy.execute\n    const txToReturn = activeTransaction\n\n    // For queue strategy, pass a function that commits the captured transaction\n    // This prevents the error when commitCallback tries to access the cleared activeTransaction\n    if (strategy._type === `queue`) {\n      const capturedTx = activeTransaction\n      activeTransaction = null // Clear so next mutation creates a new transaction\n      strategy.execute(() => {\n        capturedTx.commit().catch(() => {\n          // Errors are handled via transaction.isPersisted.promise\n        })\n        return capturedTx\n      })\n    } else {\n      // For debounce/throttle, use commitCallback which manages activeTransaction\n      strategy.execute(commitCallback)\n    }\n\n    return txToReturn\n  }\n\n  return mutate\n}\n"],"names":["createTransaction"],"mappings":";;;AAsFO,SAAS,qBAId,QAC2C;AAC3C,QAAM,EAAE,UAAU,YAAY,UAAU,GAAG,sBAAsB;AAGjE,MAAI,oBAA2C;AAG/C,QAAM,iBAAiB,MAAM;AAC3B,QAAI,CAAC,mBAAmB;AACtB,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ;AAEA,QAAI,kBAAkB,UAAU,WAAW;AACzC,YAAM,IAAI;AAAA,QACR,gEAAgE,kBAAkB,KAAK;AAAA,MAAA;AAAA,IAE3F;AAEA,UAAM,aAAa;AAGnB,wBAAoB;AAGpB,eAAW,SAAS,MAAM,MAAM;AAAA,IAGhC,CAAC;AAED,WAAO;AAAA,EACT;AAOA,WAAS,OAAO,WAAuC;AAErD,QAAI,CAAC,qBAAqB,kBAAkB,UAAU,WAAW;AAC/D,0BAAoBA,aAAAA,kBAAqB;AAAA,QACvC,GAAG;AAAA,QACH;AAAA,QACA,YAAY;AAAA,MAAA,CACb;AAAA,IACH;AAGA,sBAAkB,OAAO,MAAM;AAC7B,eAAS,SAAS;AAAA,IACpB,CAAC;AAGD,UAAM,aAAa;AAInB,QAAI,SAAS,UAAU,SAAS;AAC9B,YAAM,aAAa;AACnB,0BAAoB;AACpB,eAAS,QAAQ,MAAM;AACrB,mBAAW,SAAS,MAAM,MAAM;AAAA,QAEhC,CAAC;AACD,eAAO;AAAA,MACT,CAAC;AAAA,IACH,OAAO;AAEL,eAAS,QAAQ,cAAc;AAAA,IACjC;AAEA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;;"}