{"version":3,"file":"debounceStrategy.cjs","sources":["../../../src/strategies/debounceStrategy.ts"],"sourcesContent":["import { createSerialPacer } from './serial-pacer'\nimport { runWithCommitCompletion } from './commit-completion'\nimport type { DebounceStrategy, DebounceStrategyOptions } from './types'\nimport type { Transaction } from '../transactions'\n\n/**\n * Creates a debounce strategy that delays transaction execution until after\n * a period of inactivity.\n *\n * Ideal for scenarios like search inputs or auto-save fields where you want\n * to wait for the user to stop typing before persisting changes.\n *\n * @param options - Configuration for the debounce behavior\n * @returns A debounce strategy instance\n *\n * @example\n * ```ts\n * const mutate = usePacedMutations({\n *   onMutate: (value) => {\n *     collection.update(id, draft => { draft.value = value })\n *   },\n *   mutationFn: async ({ transaction }) => {\n *     await api.save(transaction.mutations)\n *   },\n *   strategy: debounceStrategy({ wait: 500 })\n * })\n * ```\n */\nexport function debounceStrategy(\n  options: DebounceStrategyOptions,\n): DebounceStrategy {\n  const leading = options.leading ?? false\n  const trailing = options.trailing ?? true\n  const serial = createSerialPacer(0)\n  let canLead = true\n  let leadingPending = false\n  let leadingOwner: object | undefined\n  let timeout: ReturnType<typeof setTimeout> | undefined\n  const trailingRuns = new Map<\n    object,\n    { run: () => Promise<unknown>; isCanceled: () => boolean }\n  >()\n\n  return {\n    _type: `debounce`,\n    options,\n    execute: <T extends object = Record<string, unknown>>(\n      fn: () => Transaction<T>,\n      onAdmit?: () => Transaction<T> | void,\n      onCommit?: () => Promise<unknown> | undefined,\n    ) => {\n      const run = () => runWithCommitCompletion(fn, onCommit)\n      const leadingCall = leading && canLead\n      const admitted = leadingCall || trailing\n      // Reserve the edge before optimistic mutation can reenter execute.\n      const wasLeadAvailable = canLead\n      canLead = false\n      let owner: object = fn\n      let transaction: Transaction<T> | undefined\n      try {\n        if (admitted) {\n          const admittedOwner = onAdmit?.()\n          if (admittedOwner) {\n            transaction = admittedOwner\n            owner = admittedOwner\n          }\n        }\n      } catch (error) {\n        // A nested admitted call may have installed its own quiet timer.\n        if (timeout === undefined) canLead = wasLeadAvailable\n        throw error\n      }\n      // A replacement transaction after rollback cannot inherit a canceled\n      // leading callback's eligibility. It needs its own quiet-edge schedule.\n      const joinsPendingLeading =\n        leadingPending && !leadingCall && owner === leadingOwner\n      if (timeout !== undefined) clearTimeout(timeout)\n      // A new call renews the quiet period, including after an earlier timer\n      // became eligible while persistence was held.\n      if (trailing)\n        for (const pendingOwner of trailingRuns.keys())\n          serial.cancel(pendingOwner)\n      if (trailing && admitted && !leadingCall && !joinsPendingLeading) {\n        trailingRuns.set(owner, {\n          run,\n          isCanceled: () => transaction?.state === `failed`,\n        })\n      }\n      timeout = setTimeout(() => {\n        timeout = undefined\n        canLead = true\n        for (const [pendingOwner, pending] of trailingRuns) {\n          if (pending.isCanceled()) {\n            trailingRuns.delete(pendingOwner)\n            continue\n          }\n          serial.schedule(() => {\n            trailingRuns.delete(pendingOwner)\n            return pending.run()\n          }, pendingOwner)\n        }\n      }, options.wait)\n      if (leadingCall) {\n        // A same-manager reentrant trailing call joins this leading transaction.\n        // A different manager's trailing transaction keeps the shared edge.\n        trailingRuns.delete(owner)\n        leadingPending = true\n        leadingOwner = owner\n        serial.schedule(() => {\n          leadingPending = false\n          leadingOwner = undefined\n          return run()\n        }, owner)\n      }\n      if (!admitted) return false\n      return\n    },\n    cleanup: () => {\n      // Admitted work retains its timer and persistence obligation.\n    },\n  }\n}\n"],"names":["createSerialPacer","runWithCommitCompletion"],"mappings":";;;;AA4BO,SAAS,iBACd,SACkB;AAClB,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,SAASA,YAAAA,kBAAkB,CAAC;AAClC,MAAI,UAAU;AACd,MAAI,iBAAiB;AACrB,MAAI;AACJ,MAAI;AACJ,QAAM,mCAAmB,IAAA;AAKzB,SAAO;AAAA,IACL,OAAO;AAAA,IACP;AAAA,IACA,SAAS,CACP,IACA,SACA,aACG;AACH,YAAM,MAAM,MAAMC,yCAAwB,IAAI,QAAQ;AACtD,YAAM,cAAc,WAAW;AAC/B,YAAM,WAAW,eAAe;AAEhC,YAAM,mBAAmB;AACzB,gBAAU;AACV,UAAI,QAAgB;AACpB,UAAI;AACJ,UAAI;AACF,YAAI,UAAU;AACZ,gBAAM,gBAAgB,UAAA;AACtB,cAAI,eAAe;AACjB,0BAAc;AACd,oBAAQ;AAAA,UACV;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AAEd,YAAI,YAAY,OAAW,WAAU;AACrC,cAAM;AAAA,MACR;AAGA,YAAM,sBACJ,kBAAkB,CAAC,eAAe,UAAU;AAC9C,UAAI,YAAY,OAAW,cAAa,OAAO;AAG/C,UAAI;AACF,mBAAW,gBAAgB,aAAa,KAAA;AACtC,iBAAO,OAAO,YAAY;AAC9B,UAAI,YAAY,YAAY,CAAC,eAAe,CAAC,qBAAqB;AAChE,qBAAa,IAAI,OAAO;AAAA,UACtB;AAAA,UACA,YAAY,MAAM,aAAa,UAAU;AAAA,QAAA,CAC1C;AAAA,MACH;AACA,gBAAU,WAAW,MAAM;AACzB,kBAAU;AACV,kBAAU;AACV,mBAAW,CAAC,cAAc,OAAO,KAAK,cAAc;AAClD,cAAI,QAAQ,cAAc;AACxB,yBAAa,OAAO,YAAY;AAChC;AAAA,UACF;AACA,iBAAO,SAAS,MAAM;AACpB,yBAAa,OAAO,YAAY;AAChC,mBAAO,QAAQ,IAAA;AAAA,UACjB,GAAG,YAAY;AAAA,QACjB;AAAA,MACF,GAAG,QAAQ,IAAI;AACf,UAAI,aAAa;AAGf,qBAAa,OAAO,KAAK;AACzB,yBAAiB;AACjB,uBAAe;AACf,eAAO,SAAS,MAAM;AACpB,2BAAiB;AACjB,yBAAe;AACf,iBAAO,IAAA;AAAA,QACT,GAAG,KAAK;AAAA,MACV;AACA,UAAI,CAAC,SAAU,QAAO;AACtB;AAAA,IACF;AAAA,IACA,SAAS,MAAM;AAAA,IAEf;AAAA,EAAA;AAEJ;;"}