{"version":3,"file":"transactions.cjs","sources":["../../src/transactions.ts"],"sourcesContent":["import { createDeferred } from './deferred'\nimport './duplicate-instance-check'\nimport {\n  MissingMutationFunctionError,\n  TransactionAlreadyCompletedRollbackError,\n  TransactionNotPendingCommitError,\n  TransactionNotPendingMutateError,\n} from './errors'\nimport { transactionScopedScheduler } from './scheduler.js'\nimport type { Deferred } from './deferred'\nimport type {\n  MutationFn,\n  PendingMutation,\n  TransactionConfig,\n  TransactionState,\n  TransactionWithMutations,\n} from './types'\n\nconst transactions: Array<Transaction<any>> = []\nlet transactionStack: Array<Transaction<any>> = []\n\nlet sequenceNumber = 0\n\n/**\n * Merges two pending mutations for the same item within a transaction\n *\n * Merge behavior truth table:\n * - (insert, update) → insert (merge changes, keep empty original)\n * - (insert, delete) → null (cancel both mutations)\n * - (update, delete) → delete (delete dominates)\n * - (update, update) → update (replace with latest, union changes)\n * - (delete, delete) → delete (replace with latest)\n * - (insert, insert) → insert (replace with latest)\n *\n * Note: (delete, update) and (delete, insert) should never occur as the collection\n * layer prevents operations on deleted items within the same transaction.\n *\n * @param existing - The existing mutation in the transaction\n * @param incoming - The new mutation being applied\n * @returns The merged mutation, or null if both should be removed\n */\nfunction mergePendingMutations<T extends object>(\n  existing: PendingMutation<T>,\n  incoming: PendingMutation<T>,\n): PendingMutation<T> | null {\n  // Truth table implementation\n  switch (`${existing.type}-${incoming.type}` as const) {\n    case `insert-update`: {\n      // Update after insert: keep as insert but merge changes\n      // For insert-update, the key should remain the same since collections don't allow key changes\n      return {\n        ...existing,\n        type: `insert` as const,\n        original: {},\n        modified: incoming.modified,\n        changes: { ...existing.changes, ...incoming.changes },\n        // Keep existing keys (key changes not allowed in updates)\n        key: existing.key,\n        globalKey: existing.globalKey,\n        // Merge metadata (last-write-wins)\n        metadata: incoming.metadata ?? existing.metadata,\n        syncMetadata: { ...existing.syncMetadata, ...incoming.syncMetadata },\n        // Update tracking info\n        mutationId: incoming.mutationId,\n        updatedAt: incoming.updatedAt,\n      }\n    }\n\n    case `insert-delete`:\n      // Delete after insert: cancel both mutations\n      return null\n\n    case `update-delete`:\n      // Delete after update: delete dominates\n      return incoming\n\n    case `update-update`: {\n      // Update after update: replace with latest, union changes\n      return {\n        ...incoming,\n        // Keep original from first update\n        original: existing.original,\n        // Union the changes from both updates\n        changes: { ...existing.changes, ...incoming.changes },\n        // Merge metadata\n        metadata: incoming.metadata ?? existing.metadata,\n        syncMetadata: { ...existing.syncMetadata, ...incoming.syncMetadata },\n      }\n    }\n\n    case `delete-delete`:\n    case `insert-insert`:\n      // Same type: replace with latest\n      return incoming\n\n    default: {\n      // Exhaustiveness check\n      const _exhaustive: never = `${existing.type}-${incoming.type}` as never\n      throw new Error(`Unhandled mutation combination: ${_exhaustive}`)\n    }\n  }\n}\n\n/**\n * Creates a new transaction for grouping multiple collection operations\n * @param config - Transaction configuration with mutation function\n * @returns A new Transaction instance\n * @example\n * // Basic transaction usage\n * const tx = createTransaction({\n *   mutationFn: async ({ transaction }) => {\n *     // Send all mutations to API\n *     await api.saveChanges(transaction.mutations)\n *   }\n * })\n *\n * tx.mutate(() => {\n *   collection.insert({ id: \"1\", text: \"Buy milk\" })\n *   collection.update(\"2\", draft => { draft.completed = true })\n * })\n *\n * await tx.isPersisted.promise\n *\n * @example\n * // Handle transaction errors\n * try {\n *   const tx = createTransaction({\n *     mutationFn: async () => { throw new Error(\"API failed\") }\n *   })\n *\n *   tx.mutate(() => {\n *     collection.insert({ id: \"1\", text: \"New item\" })\n *   })\n *\n *   await tx.isPersisted.promise\n * } catch (error) {\n *   console.log('Transaction failed:', error)\n * }\n *\n * @example\n * // Manual commit control\n * const tx = createTransaction({\n *   autoCommit: false,\n *   mutationFn: async () => {\n *     // API call\n *   }\n * })\n *\n * tx.mutate(() => {\n *   collection.insert({ id: \"1\", text: \"Item\" })\n * })\n *\n * // Commit later\n * await tx.commit()\n */\nexport function createTransaction<T extends object = Record<string, unknown>>(\n  config: TransactionConfig<T>,\n): Transaction<T> {\n  const newTransaction = new Transaction<T>(config)\n  transactions.push(newTransaction)\n  return newTransaction\n}\n\n/**\n * Gets the currently active ambient transaction, if any\n * Used internally by collection operations to join existing transactions\n * @returns The active transaction or undefined if none is active\n * @example\n * // Check if operations will join an ambient transaction\n * const ambientTx = getActiveTransaction()\n * if (ambientTx) {\n *   console.log('Operations will join transaction:', ambientTx.id)\n * }\n */\nexport function getActiveTransaction(): Transaction | undefined {\n  if (transactionStack.length > 0) {\n    return transactionStack.slice(-1)[0]\n  } else {\n    return undefined\n  }\n}\n\nfunction registerTransaction(tx: Transaction<any>) {\n  // Clear any stale work that may have been left behind if a previous mutate\n  // scope aborted before we could flush.\n  transactionScopedScheduler.clear(tx.id)\n  transactionStack.push(tx)\n}\n\nfunction unregisterTransaction(tx: Transaction<any>) {\n  // Always flush pending work for this transaction before removing it from\n  // the ambient stack – this runs even if the mutate callback throws.\n  // If flush throws (e.g., due to a job error), we still clean up the stack.\n  try {\n    transactionScopedScheduler.flush(tx.id)\n  } finally {\n    transactionStack = transactionStack.filter((t) => t.id !== tx.id)\n  }\n}\n\nfunction removeFromPendingList(tx: Transaction<any>) {\n  const index = transactions.findIndex((t) => t.id === tx.id)\n  if (index !== -1) {\n    transactions.splice(index, 1)\n  }\n}\n\nclass Transaction<T extends object = Record<string, unknown>> {\n  public id: string\n  public state: TransactionState\n  public mutationFn: MutationFn<T>\n  public mutations: Array<PendingMutation<T>>\n  public isPersisted: Deferred<Transaction<T>>\n  public autoCommit: boolean\n  public createdAt: Date\n  public sequenceNumber: number\n  public metadata: Record<string, unknown>\n  public error?: {\n    message: string\n    error: Error\n  }\n\n  constructor(config: TransactionConfig<T>) {\n    if (typeof config.mutationFn === `undefined`) {\n      throw new MissingMutationFunctionError()\n    }\n    this.id = config.id ?? crypto.randomUUID()\n    this.mutationFn = config.mutationFn\n    this.state = `pending`\n    this.mutations = []\n    this.isPersisted = createDeferred<Transaction<T>>()\n    this.autoCommit = config.autoCommit ?? true\n    this.createdAt = new Date()\n    this.sequenceNumber = sequenceNumber++\n    this.metadata = config.metadata ?? {}\n  }\n\n  setState(newState: TransactionState) {\n    this.state = newState\n\n    if (newState === `completed` || newState === `failed`) {\n      removeFromPendingList(this)\n    }\n  }\n\n  /**\n   * Execute collection operations within this transaction\n   * @param callback - Function containing collection operations to group together. If the\n   * callback returns a Promise, the transaction context will remain active until the promise\n   * settles, allowing optimistic writes after `await` boundaries.\n   * @returns This transaction for chaining\n   * @example\n   * // Group multiple operations\n   * const tx = createTransaction({ mutationFn: async () => {\n   *   // Send to API\n   * }})\n   *\n   * tx.mutate(() => {\n   *   collection.insert({ id: \"1\", text: \"Buy milk\" })\n   *   collection.update(\"2\", draft => { draft.completed = true })\n   *   collection.delete(\"3\")\n   * })\n   *\n   * await tx.isPersisted.promise\n   *\n   * @example\n   * // Handle mutate errors\n   * try {\n   *   tx.mutate(() => {\n   *     collection.insert({ id: \"invalid\" }) // This might throw\n   *   })\n   * } catch (error) {\n   *   console.log('Mutation failed:', error)\n   * }\n   *\n   * @example\n   * // Manual commit control\n   * const tx = createTransaction({ autoCommit: false, mutationFn: async () => {} })\n   *\n   * tx.mutate(() => {\n   *   collection.insert({ id: \"1\", text: \"Item\" })\n   * })\n   *\n   * // Commit later when ready\n   * await tx.commit()\n   */\n  mutate(callback: () => void): Transaction<T> {\n    if (this.state !== `pending`) {\n      throw new TransactionNotPendingMutateError()\n    }\n\n    registerTransaction(this)\n\n    try {\n      callback()\n    } finally {\n      unregisterTransaction(this)\n    }\n\n    if (this.autoCommit) {\n      this.commit().catch(() => {\n        // Errors from autoCommit are handled via isPersisted.promise\n        // This catch prevents unhandled promise rejections\n      })\n    }\n\n    return this\n  }\n\n  /**\n   * Apply new mutations to this transaction, intelligently merging with existing mutations\n   *\n   * When mutations operate on the same item (same globalKey), they are merged according to\n   * the following rules:\n   *\n   * - **insert + update** → insert (merge changes, keep empty original)\n   * - **insert + delete** → removed (mutations cancel each other out)\n   * - **update + delete** → delete (delete dominates)\n   * - **update + update** → update (union changes, keep first original)\n   * - **same type** → replace with latest\n   *\n   * This merging reduces over-the-wire churn and keeps the optimistic local view\n   * aligned with user intent.\n   *\n   * @param mutations - Array of new mutations to apply\n   */\n  applyMutations(mutations: Array<PendingMutation<any>>): void {\n    for (const newMutation of mutations) {\n      const existingIndex = this.mutations.findIndex(\n        (m) => m.globalKey === newMutation.globalKey,\n      )\n\n      if (existingIndex >= 0) {\n        const existingMutation = this.mutations[existingIndex]!\n        const mergeResult = mergePendingMutations(existingMutation, newMutation)\n\n        if (mergeResult === null) {\n          // Remove the mutation (e.g., delete after insert cancels both)\n          this.mutations.splice(existingIndex, 1)\n        } else {\n          // Replace with merged mutation\n          this.mutations[existingIndex] = mergeResult\n        }\n      } else {\n        // Insert new mutation\n        this.mutations.push(newMutation)\n      }\n    }\n  }\n\n  /**\n   * Rollback the transaction and any conflicting transactions\n   * @param config - Configuration for rollback behavior\n   * @returns This transaction for chaining\n   * @example\n   * // Manual rollback\n   * const tx = createTransaction({ mutationFn: async () => {\n   *   // Send to API\n   * }})\n   *\n   * tx.mutate(() => {\n   *   collection.insert({ id: \"1\", text: \"Buy milk\" })\n   * })\n   *\n   * // Rollback if needed\n   * if (shouldCancel) {\n   *   tx.rollback()\n   * }\n   *\n   * @example\n   * // Handle rollback cascade (automatic)\n   * const tx1 = createTransaction({ mutationFn: async () => {} })\n   * const tx2 = createTransaction({ mutationFn: async () => {} })\n   *\n   * tx1.mutate(() => collection.update(\"1\", draft => { draft.value = \"A\" }))\n   * tx2.mutate(() => collection.update(\"1\", draft => { draft.value = \"B\" })) // Same item\n   *\n   * tx1.rollback() // This will also rollback tx2 due to conflict\n   *\n   * @example\n   * // Handle rollback in error scenarios\n   * try {\n   *   await tx.isPersisted.promise\n   * } catch (error) {\n   *   console.log('Transaction was rolled back:', error)\n   *   // Transaction automatically rolled back on mutation function failure\n   * }\n   */\n  rollback(config?: { isSecondaryRollback?: boolean }): Transaction<T> {\n    const isSecondaryRollback = config?.isSecondaryRollback ?? false\n    if (this.state === `completed`) {\n      throw new TransactionAlreadyCompletedRollbackError()\n    }\n\n    this.setState(`failed`)\n\n    // See if there's any other transactions w/ mutations on the same ids\n    // and roll them back as well.\n    if (!isSecondaryRollback) {\n      const mutationIds = new Set()\n      this.mutations.forEach((m) => mutationIds.add(m.globalKey))\n      for (const t of transactions) {\n        t.state === `pending` &&\n          t.mutations.some((m) => mutationIds.has(m.globalKey)) &&\n          t.rollback({ isSecondaryRollback: true })\n      }\n    }\n\n    // Reject the promise\n    this.isPersisted.reject(this.error?.error)\n    this.touchCollection()\n\n    return this\n  }\n\n  // Tell collection that something has changed with the transaction\n  touchCollection(): void {\n    const hasCalled = new Set()\n    for (const mutation of this.mutations) {\n      if (!hasCalled.has(mutation.collection.id)) {\n        mutation.collection._state.onTransactionStateChange()\n\n        // Only call commitPendingTransactions if there are pending sync transactions\n        if (mutation.collection._state.pendingSyncedTransactions.length > 0) {\n          mutation.collection._state.commitPendingTransactions()\n        }\n\n        hasCalled.add(mutation.collection.id)\n      }\n    }\n  }\n\n  /**\n   * Commit the transaction and execute the mutation function\n   * @returns Promise that resolves to this transaction when complete\n   * @example\n   * // Manual commit (when autoCommit is false)\n   * const tx = createTransaction({\n   *   autoCommit: false,\n   *   mutationFn: async ({ transaction }) => {\n   *     await api.saveChanges(transaction.mutations)\n   *   }\n   * })\n   *\n   * tx.mutate(() => {\n   *   collection.insert({ id: \"1\", text: \"Buy milk\" })\n   * })\n   *\n   * await tx.commit() // Manually commit\n   *\n   * @example\n   * // Handle commit errors\n   * try {\n   *   const tx = createTransaction({\n   *     mutationFn: async () => { throw new Error(\"API failed\") }\n   *   })\n   *\n   *   tx.mutate(() => {\n   *     collection.insert({ id: \"1\", text: \"Item\" })\n   *   })\n   *\n   *   await tx.commit()\n   * } catch (error) {\n   *   console.log('Commit failed, transaction rolled back:', error)\n   * }\n   *\n   * @example\n   * // Check transaction state after commit\n   * await tx.commit()\n   * console.log(tx.state) // \"completed\" or \"failed\"\n   */\n  async commit(): Promise<Transaction<T>> {\n    if (this.state !== `pending`) {\n      throw new TransactionNotPendingCommitError()\n    }\n\n    this.setState(`persisting`)\n\n    if (this.mutations.length === 0) {\n      this.setState(`completed`)\n      this.isPersisted.resolve(this)\n\n      return this\n    }\n\n    // Run mutationFn\n    try {\n      // At this point we know there's at least one mutation\n      // We've already verified mutations is non-empty, so this cast is safe\n      // Use a direct type assertion instead of object spreading to preserve the original type\n      await this.mutationFn({\n        transaction: this as unknown as TransactionWithMutations<T>,\n      })\n\n      this.setState(`completed`)\n      this.touchCollection()\n\n      this.isPersisted.resolve(this)\n    } catch (error) {\n      // Preserve the original error for rethrowing\n      const originalError =\n        error instanceof Error ? error : new Error(String(error))\n\n      // Update transaction with error information\n      this.error = {\n        message: originalError.message,\n        error: originalError,\n      }\n\n      // rollback the transaction\n      this.rollback()\n\n      // Re-throw the original error to preserve identity and stack\n      throw originalError\n    }\n\n    return this\n  }\n\n  /**\n   * Compare two transactions by their createdAt time and sequence number in order\n   * to sort them in the order they were created.\n   * @param other - The other transaction to compare to\n   * @returns -1 if this transaction was created before the other, 1 if it was created after, 0 if they were created at the same time\n   */\n  compareCreatedAt(other: Transaction<any>): number {\n    const createdAtComparison =\n      this.createdAt.getTime() - other.createdAt.getTime()\n    if (createdAtComparison !== 0) {\n      return createdAtComparison\n    }\n    return this.sequenceNumber - other.sequenceNumber\n  }\n}\n\nexport type { Transaction }\n"],"names":["transactionScopedScheduler","MissingMutationFunctionError","createDeferred","TransactionNotPendingMutateError","TransactionAlreadyCompletedRollbackError","TransactionNotPendingCommitError"],"mappings":"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"}