{"version":3,"file":"OfflineTransaction.cjs","sources":["../../../src/api/OfflineTransaction.ts"],"sourcesContent":["import { createTransaction, safeRandomUUID } from '@tanstack/db'\nimport type { PendingMutation, Transaction } from '@tanstack/db'\nimport type {\n  CreateOfflineTransactionOptions,\n  OfflineMutationFn,\n  OfflineTransaction as OfflineTransactionType,\n} from '../types'\n\nexport class OfflineTransaction {\n  private offlineId: string\n  private mutationFnName: string\n  private autoCommit: boolean | undefined\n  private idempotencyKey: string\n  private metadata: Record<string, any>\n  private transaction: Transaction | null = null\n  /** The mutation function ran, so the executor settles the waiter. */\n  private persisting = false\n  private persistTransaction: (tx: OfflineTransactionType) => Promise<void>\n  private executor: any // Will be typed properly - reference to OfflineExecutor\n\n  constructor(\n    options: CreateOfflineTransactionOptions,\n    mutationFn: OfflineMutationFn,\n    persistTransaction: (tx: OfflineTransactionType) => Promise<void>,\n    executor: any,\n  ) {\n    this.offlineId = safeRandomUUID()\n    this.mutationFnName = options.mutationFnName\n    // The TanStack DB transaction applies the default.\n    this.autoCommit = options.autoCommit\n    this.idempotencyKey = options.idempotencyKey ?? safeRandomUUID()\n    this.metadata = options.metadata ?? {}\n    this.persistTransaction = persistTransaction\n    this.executor = executor\n  }\n\n  mutate(callback: () => void): Transaction {\n    // One offline transaction is one transaction, and its id is unique among\n    // live transactions. Repeated calls add to it while it is pending. With\n    // autoCommit, it commits after this callback and reports a failure\n    // through `when('settled')`, so a later call throws: it is no longer pending.\n    if (!this.transaction) {\n      this.transaction = createTransaction({\n        id: this.offlineId,\n        autoCommit: this.autoCommit,\n        mutationFn: async () => {\n          this.persisting = true\n          // This is the blocking mutationFn that waits for the executor\n          // First persist the transaction to the outbox\n          const offlineTransaction: OfflineTransactionType = {\n            id: this.offlineId,\n            mutationFnName: this.mutationFnName,\n            mutations: this.transaction!.mutations,\n            keys: this.extractKeys(this.transaction!.mutations),\n            idempotencyKey: this.idempotencyKey,\n            createdAt: new Date(),\n            retryCount: 0,\n            nextAttemptAt: Date.now(),\n            metadata: this.metadata,\n            spanContext: undefined,\n            version: 1,\n          }\n\n          const completionPromise = this.executor.waitForTransactionCompletion(\n            this.offlineId,\n          )\n\n          try {\n            // Persistence also drives the shared queue. This transaction can finish\n            // before that queue drains; observe both promises from the outset.\n            await Promise.race([\n              this.persistTransaction(offlineTransaction),\n              completionPromise,\n            ])\n            // A queue pause (offline or retry) is not transaction completion.\n            await completionPromise\n          } catch (error) {\n            const normalizedError =\n              error instanceof Error ? error : new Error(String(error))\n            this.executor.rejectTransaction(this.offlineId, normalizedError)\n            throw error\n          }\n\n          return\n        },\n        metadata: this.metadata,\n      })\n      // A transaction without mutations completes, and one rolled back before\n      // it commits fails, without its mutation function, so the executor never\n      // settles its waiter. Settle that waiter here with the same outcome. Once\n      // the mutation function runs, only the executor settles it.\n      void this.transaction.when(`settled`).then(\n        () => {\n          if (!this.persisting)\n            this.executor.resolveTransaction(this.offlineId, undefined)\n        },\n        (error: unknown) => {\n          if (!this.persisting)\n            this.executor.rejectTransaction(\n              this.offlineId,\n              error instanceof Error\n                ? error\n                : new Error(`Offline transaction was rolled back`),\n            )\n        },\n      )\n    }\n\n    this.transaction.mutate(() => {\n      callback()\n    })\n\n    return this.transaction\n  }\n\n  async commit(): Promise<Transaction> {\n    if (!this.transaction) {\n      throw new Error(`No mutations to commit. Call mutate() first.`)\n    }\n\n    // The mutationFn persists the transaction and waits for the executor. A\n    // commit that rejects has already rolled the transaction back. A sibling's\n    // failure can also roll it back while it waits, and then commit() resolves,\n    // so `when('settled')` is the receipt.\n    await this.transaction.commit()\n    return this.transaction\n  }\n\n  rollback(): void {\n    if (this.transaction) {\n      this.transaction.rollback()\n    }\n  }\n\n  private extractKeys(mutations: Array<PendingMutation>): Array<string> {\n    return mutations.map((mutation) => mutation.globalKey)\n  }\n\n  get id(): string {\n    return this.offlineId\n  }\n}\n"],"names":["safeRandomUUID","createTransaction"],"mappings":";;;AAQO,MAAM,mBAAmB;AAAA;AAAA,EAY9B,YACE,SACA,YACA,oBACA,UACA;AAXF,SAAQ,cAAkC;AAE1C,SAAQ,aAAa;AAUnB,SAAK,YAAYA,kBAAA;AACjB,SAAK,iBAAiB,QAAQ;AAE9B,SAAK,aAAa,QAAQ;AAC1B,SAAK,iBAAiB,QAAQ,kBAAkBA,GAAAA,eAAA;AAChD,SAAK,WAAW,QAAQ,YAAY,CAAA;AACpC,SAAK,qBAAqB;AAC1B,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,OAAO,UAAmC;AAKxC,QAAI,CAAC,KAAK,aAAa;AACrB,WAAK,cAAcC,qBAAkB;AAAA,QACnC,IAAI,KAAK;AAAA,QACT,YAAY,KAAK;AAAA,QACjB,YAAY,YAAY;AACtB,eAAK,aAAa;AAGlB,gBAAM,qBAA6C;AAAA,YACjD,IAAI,KAAK;AAAA,YACT,gBAAgB,KAAK;AAAA,YACrB,WAAW,KAAK,YAAa;AAAA,YAC7B,MAAM,KAAK,YAAY,KAAK,YAAa,SAAS;AAAA,YAClD,gBAAgB,KAAK;AAAA,YACrB,+BAAe,KAAA;AAAA,YACf,YAAY;AAAA,YACZ,eAAe,KAAK,IAAA;AAAA,YACpB,UAAU,KAAK;AAAA,YACf,aAAa;AAAA,YACb,SAAS;AAAA,UAAA;AAGX,gBAAM,oBAAoB,KAAK,SAAS;AAAA,YACtC,KAAK;AAAA,UAAA;AAGP,cAAI;AAGF,kBAAM,QAAQ,KAAK;AAAA,cACjB,KAAK,mBAAmB,kBAAkB;AAAA,cAC1C;AAAA,YAAA,CACD;AAED,kBAAM;AAAA,UACR,SAAS,OAAO;AACd,kBAAM,kBACJ,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAC1D,iBAAK,SAAS,kBAAkB,KAAK,WAAW,eAAe;AAC/D,kBAAM;AAAA,UACR;AAEA;AAAA,QACF;AAAA,QACA,UAAU,KAAK;AAAA,MAAA,CAChB;AAKD,WAAK,KAAK,YAAY,KAAK,SAAS,EAAE;AAAA,QACpC,MAAM;AACJ,cAAI,CAAC,KAAK;AACR,iBAAK,SAAS,mBAAmB,KAAK,WAAW,MAAS;AAAA,QAC9D;AAAA,QACA,CAAC,UAAmB;AAClB,cAAI,CAAC,KAAK;AACR,iBAAK,SAAS;AAAA,cACZ,KAAK;AAAA,cACL,iBAAiB,QACb,QACA,IAAI,MAAM,qCAAqC;AAAA,YAAA;AAAA,QAEzD;AAAA,MAAA;AAAA,IAEJ;AAEA,SAAK,YAAY,OAAO,MAAM;AAC5B,eAAA;AAAA,IACF,CAAC;AAED,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,SAA+B;AACnC,QAAI,CAAC,KAAK,aAAa;AACrB,YAAM,IAAI,MAAM,8CAA8C;AAAA,IAChE;AAMA,UAAM,KAAK,YAAY,OAAA;AACvB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,WAAiB;AACf,QAAI,KAAK,aAAa;AACpB,WAAK,YAAY,SAAA;AAAA,IACnB;AAAA,EACF;AAAA,EAEQ,YAAY,WAAkD;AACpE,WAAO,UAAU,IAAI,CAAC,aAAa,SAAS,SAAS;AAAA,EACvD;AAAA,EAEA,IAAI,KAAa;AACf,WAAO,KAAK;AAAA,EACd;AACF;;"}