{"version":3,"file":"index.mjs","sources":[""],"sourcesContent":["import { _getAbortError, _getAbortReason, _isAbortSignal } from \"../_abort/index.ts\";\nimport { getAbortSignal } from \"../getAbortSignal/index.ts\";\nimport type { TAbortSignalControls } from \"../getAbortSignal/index.ts\";\n\nexport type TGetLatestAsyncFnOptions = {\n  signal?: AbortSignal | null;\n};\n\nexport type TLatestAsyncFn<TArgs extends any[], TResult> = ((\n  ...args: TArgs\n) => Promise<Awaited<TResult>>) & {\n  cancel: (reason?: unknown) => void;\n  pending: () => boolean;\n};\n\nexport type TLatestAsyncSource<TArgs extends any[], TResult> = (\n  signal: AbortSignal,\n  ...args: TArgs\n) => TResult;\n\nexport type TGetLatestAsyncFnArgs = Parameters<typeof getLatestAsyncFn>;\n\nexport type TGetLatestAsyncFnReturn = ReturnType<typeof getLatestAsyncFn>;\n\n/**\n * Wraps an async operation so a new call aborts the previous pending call.\n * The source function receives a per-call signal as its first argument.\n * @template TArgs,TResult\n * @param {TLatestAsyncSource<TArgs, TResult>} fn Async source function\n * @param {TGetLatestAsyncFnOptions} [options={}] Shared lifecycle signal\n * @returns {TLatestAsyncFn<TArgs, TResult>} Latest-only async function with cancel controls\n * @throws {TypeError} getLatestAsyncFn: arguments are invalid\n * @example\n * const search = getLatestAsyncFn(async (signal, query: string) => {\n *   const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`, { signal });\n *   return response.json();\n * });\n * await search(\"roses\");\n * @example\n * // Abort the active request when a component is disposed\n * const lifecycle = new AbortController();\n * const load = getLatestAsyncFn(loadProduct, { signal: lifecycle.signal });\n * lifecycle.abort();\n */\nexport const getLatestAsyncFn = <TArgs extends any[], TResult>(\n  fn: TLatestAsyncSource<TArgs, TResult>,\n  options: TGetLatestAsyncFnOptions = {}\n): TLatestAsyncFn<TArgs, TResult> => {\n  if (typeof fn !== \"function\") {\n    throw new TypeError(\"getLatestAsyncFn: fn must be a function\");\n  }\n  if (!options || typeof options !== \"object\" || Array.isArray(options)) {\n    throw new TypeError(\"getLatestAsyncFn: options must be a plain object\");\n  }\n  const { signal: lifecycleSignal = null } = options;\n  if (lifecycleSignal !== null && !_isAbortSignal(lifecycleSignal)) {\n    throw new TypeError(\"getLatestAsyncFn: signal must be an AbortSignal or null\");\n  }\n\n  let current: TAbortSignalControls | null = null;\n  const latest = function latestAsyncFunction(\n    this: unknown,\n    ...args: TArgs\n  ): Promise<Awaited<TResult>> {\n    current?.abort(_getAbortError(\"The operation was superseded\"));\n\n    const controls = getAbortSignal({ signals: [ lifecycleSignal ] });\n    current = controls;\n    if (controls.signal.aborted) {\n      current = null;\n      controls.dispose();\n      return Promise.reject(_getAbortReason(controls.signal));\n    }\n\n    let removeAbortListener = (): void => {};\n    const aborted = new Promise<never>((resolve, reject) => {\n      const onAbort = (): void => reject(_getAbortReason(controls.signal));\n      controls.signal.addEventListener(\"abort\", onAbort, { once: true });\n      removeAbortListener = (): void => controls.signal.removeEventListener(\"abort\", onAbort);\n    });\n    const task = Promise.resolve().then(async () => {\n      if (controls.signal.aborted) {\n        throw _getAbortReason(controls.signal);\n      }\n      return await fn.apply(this, [ controls.signal, ...args ]);\n    });\n\n    return Promise.race([ task, aborted ]).finally(() => {\n      removeAbortListener();\n      if (current === controls) {\n        current = null;\n      }\n      controls.dispose();\n    }) as Promise<Awaited<TResult>>;\n  } as TLatestAsyncFn<TArgs, TResult>;\n\n  latest.cancel = (reason?: unknown): void => {\n    current?.abort(reason ?? _getAbortError());\n  };\n  latest.pending = (): boolean => current !== null;\n\n  return latest;\n};\n"],"names":["getLatestAsyncFn","fn","options","TypeError","Array","isArray","signal","lifecycleSignal","_isAbortSignal","current","latest","latestAsyncFunction","args","abort","_getAbortError","controls","getAbortSignal","signals","aborted","dispose","Promise","reject","_getAbortReason","removeAbortListener","resolve","onAbort","addEventListener","once","removeEventListener","task","then","async","apply","this","race","finally","cancel","reason","pending"],"mappings":"uIA4CO,MAAMA,iBAAmB,CAC9BC,GACAC,QAAoC,MAEpC,UAAWD,KAAO,WAChB,MAAM,IAAIE,UAAU,2CAEtB,IAAKD,gBAAkBA,UAAY,UAAYE,MAAMC,QAAQH,SAC3D,MAAM,IAAIC,UAAU,oDAEtB,MAAQG,OAAQC,gBAAkB,MAASL,QAC3C,GAAIK,kBAAoB,OAASC,eAAeD,iBAC9C,MAAM,IAAIJ,UAAU,2DAGtB,IAAIM,QAAuC,KAC3C,MAAMC,OAAS,SAASC,uBAEnBC,MAEHH,SAASI,MAAMC,eAAe,iCAE9B,MAAMC,SAAWC,eAAe,CAAEC,QAAS,CAAEV,mBAC7CE,QAAUM,SACV,GAAIA,SAAST,OAAOY,QAAS,CAC3BT,QAAU,KACVM,SAASI,UACT,OAAOC,QAAQC,OAAOC,gBAAgBP,SAAST,QACjD,CAEA,IAAIiB,oBAAsB,OAC1B,MAAML,QAAU,IAAIE,QAAe,CAACI,QAASH,UAC3C,MAAMI,QAAU,IAAYJ,OAAOC,gBAAgBP,SAAST,SAC5DS,SAAST,OAAOoB,iBAAiB,QAASD,QAAS,CAAEE,KAAM,OAC3DJ,oBAAsB,IAAYR,SAAST,OAAOsB,oBAAoB,QAASH,WAEjF,MAAMI,KAAOT,QAAQI,UAAUM,KAAKC,UAClC,GAAIhB,SAAST,OAAOY,QAClB,MAAMI,gBAAgBP,SAAST,QAEjC,aAAaL,GAAG+B,MAAMC,KAAM,CAAElB,SAAST,UAAWM,SAGpD,OAAOQ,QAAQc,KAAK,CAAEL,KAAMX,UAAWiB,QAAQ,KAC7CZ,sBACA,GAAId,UAAYM,SACdN,QAAU,KAEZM,SAASI,WAEb,EAEAT,OAAO0B,OAAUC,SACf5B,SAASI,MAAMwB,QAAUvB,mBAE3BJ,OAAO4B,QAAU,IAAe7B,UAAY,KAE5C,OAAOC"}