{"version":3,"sources":["jsdelivr-header.js","/npm/idmp@4.1.0/src/index.ts"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;kBCwDW,O,+HAAA,C,SAiBT,EAAA,SAAA,GADS,OAET,IAAA,2BACA,IAAA,mBACA,IAAA,6BACA,IAAA,+BACA,IAAA,mCACA,IAAA,yCACA,IAAA,qB,iDARS,CAYX,CAXE,CAWF,GAAwB,IAClB,EAAS,IACT,EAAa,OACb,EAAA,OACA,OAAW,EACX,EAAgB,WAEhB,EAA4C,a,KAkIhD,EACE,IASK,MAAA,WAAA,GAAA,OAAA,EAAA,EAAA,gBAAA,GAAA,gBAAA,IAAA,gBAAA,EAAA,UAAA,GAAA,GACL,OACA,WACA,QA1Cc,EA0Cd,EAzCE,EAAS,EAAe,EACrB,EAAA,I,GAyCL,gBACA,gBACA,gBACA,EAAA,IAAA,IACD,UA/CG,IAAY,C,EAuDhB,EAAgB,IAChB,IAAA,EAA2B,OAC3B,MAAY,EAAA,EAAA,GACZ,IACE,EAAA,EAAA,UAAsC,EAAA,EAAA,EAAA,U,SA2CxC,EAAA,CAAA,EAAA,EACE,KAOF,IAAM,EAEJ,OAAA,IAQW,MAAA,WAAA,gBAAA,gBAAwC,SAAA,gBAAA,EAAA,EAAA,UAAA,EAAA,GACnD,EAAA,GAAA,EAAA,IAAA,CACA,EACA,EAAE,OACH,IAID,MAAwB,EAAA,EAAA,GAsBhB,EAAA,KACN,EAAM,EAAE,QAAA,EAAsB,O,yCAqCxB,EAAY,KAClB,MAAI,EAAA,EAAA,EAAA,aAAA,OACJ,IAAA,EAlSY,IAAA,EAAW,EAoSvB,EAAa,EAAA,GAIb,EAAa,IAAG,SAAY,MAxShB,EAySW,EAzSA,EAySA,EAAM,EAAA,aAAwB,OAAA,EAA7C,EAzSsC,EAAI,EAAI,EAAI,GA2S1D,QAAgB,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,EAAA,aAAA,GAAA,GAAA,EAAA,EAAA,iB,MAQP,EAAyB,QAAA,SAAQ,CAAqB,EAAA,KA+D3D,IA7DF,EAAA,EAAA,qBAA6B,EAAA,EAAA,mBACvB,GA4DJ,EAAA,EAAA,UAAA,EACE,SADF,CAWA,GAAA,EAAW,CAEX,GAAA,EAAA,QAAA,OACS,EAAA,iBAAA,cACP,IAIW,EAAA,EAAA,gBAAA,IAAA,aAAA,EAAA,OAAA,cACX,GAAA,GAGJ,CACQ,EAAE,EAAA,UAAiB,EAAA,QACzB,EAAM,EAAE,QAAa,EAAA,QAErB,EAAM,EAAE,aAAA,KAAA,CAAA,EACa,IACjB,EAAA,EAAA,qBAA6B,MAAA,IAKjB,EAAA,EAAA,cAAA,EAlJF,MAClB,MAAA,EAAa,EAAO,EAAA,aAAU,OAC5B,QAAQ,EAAa,EAAA,EAAA,IAAM,EAC3B,EAAA,EAAA,aAA6B,GAAA,GAAA,EAAA,EAAA,eAgB/B,EAAK,EAAA,aAAA,GAED,IAAgB,EAAA,EAAA,SAAA,GAAA,K,EACR,EAAA,G,KA6HE,GACN,EAAA,EAAA,QACmB,EAAA,QAAA,IACb,OAAE,I,QACR,EAAM,EAAE,QAAkB,EAAA,SAC1B,EAAA,EAAQ,gBAAE,IAEV,EAAU,EAAA,YAEH,EAAA,EAAA,YAAA,EAAA,KAEH,EAAA,EAAA,CACA,YACD,WAAC,EAAA,EAAA,cAQF,I,qDAEF,KAIN,EAAA,EAAA,UAAA,EAAA,SAAA,EAAA,EAAA,aAAA,KAAA,CAAA,EAAA,GApDA,MAHE,EAAA,EAAA,EAAA,cAuDF,I,sBAoBN,EAAK,SA9Ra,KAGhB,QAAA,KAAiB,OAAA,KAAA,GAAA,EAAA,G","file":"/npm/idmp@4.1.0/dist/index.js/+esm","sourceRoot":"","sourcesContent":["/**\n * Bundled by jsDelivr using Rollup v2.79.2 and Terser v5.39.0.\n * Original file: /npm/idmp@4.1.0/dist/index.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","/**\n * IdmpGlobalKey type - Represents the key used to identify and store promises globally\n * Can be a string, number, symbol, or falsy value (false, null, undefined)\n */\ntype IdmpGlobalKey = string | number | symbol | false | null | undefined\n\n/**\n * IdmpPromise type - Function that returns a Promise of generic type T\n */\ntype IdmpPromise<T> = () => Promise<T>\n\n/**\n * Configuration options for IDMP (Intelligent Deduplication of Multiple Promises)\n */\ninterface IdmpOptions {\n  /**\n   * Maximum number of retry attempts for failed promises\n   * @default 30\n   */\n  maxRetry?: number\n\n  /**\n   * Minimum delay between retry attempts in milliseconds\n   * @default 50\n   */\n  minRetryDelay?: number\n\n  /**\n   * Maximum delay between retry attempts in milliseconds\n   * @default 5000\n   */\n  maxRetryDelay?: number\n\n  /**\n   * Maximum cache age in milliseconds\n   * After this period, the cached promise result will be cleared\n   * @default 3000\n   * @max 604800000 (7 days)\n   */\n  maxAge?: number\n\n  signal?: AbortSignal\n\n  /**\n   * Function executed before each retry attempt\n   * @param err - The error object that caused the retry\n   * @param extra - Additional context information\n   * @param extra.globalKey - The key identifying this promise in the global store\n   * @param extra.retryCount - Current retry attempt number\n   */\n  onBeforeRetry?: (\n    err: any,\n    extra: {\n      globalKey: IdmpGlobalKey\n      retryCount: number\n    },\n  ) => void\n}\n\n/**\n * Internal status enum for promise states\n */\nconst enum Status {\n  /** Promise hasn't been initiated yet */\n  UNSENT = 0,\n  /** Promise is in progress */\n  OPENING = 1,\n  /** Promise was aborted (reserved for future use) */\n  ABORTED = 2,\n  /** Promise was rejected */\n  REJECTED = 3,\n  /** Promise was resolved successfully */\n  RESOLVED = 4,\n}\n\n/**\n * Internal keys enum for accessing cache store properties\n */\nconst enum K {\n  retryCount = 0,\n  status,\n  pendingList,\n  resolvedData,\n  rejectionError,\n  cachedPromiseFunc,\n  timerId,\n  _originalErrorStack,\n}\n\n// Constants\nconst DEFAULT_MAX_AGE = 3000\nconst _7days = 604800000\nconst noop = () => {}\nconst UNDEFINED = undefined\nconst $timeout = setTimeout\nconst $clearTimeout = clearTimeout\n\nconst getMax = (a: number, b: number): number => (a > b ? a : b)\nconst getMin = (a: number, b: number): number => (a < b ? a : b)\n\n/**\n * Makes an object's properties read-only to prevent mutation\n * @param obj - Object to make read-only\n * @param key - Property key to make read-only\n * @param value - Value to assign to the property\n * @param visited - WeakSet to track visited objects and prevent circular references\n * @returns true if successful, false otherwise\n */\nconst defineReactive = (\n  obj: any,\n  key: string | symbol,\n  value: any,\n  visited: WeakSet<any>,\n): boolean => {\n  try {\n    Object.prototype.toString.call(value)\n    readonly(value, visited)\n\n    Object.defineProperty(obj, key, {\n      configurable: false,\n      enumerable: Object.getOwnPropertyDescriptor(obj, key)?.enumerable ?? true,\n      get: () => value,\n      set: (newValue) => {\n        const msg = `[idmp error] The data is read-only, set ${key.toString()}=${JSON.stringify(\n          newValue,\n        )} is not allow`\n        console.error(`%c ${msg}`, 'font-weight: lighter; color: red', newValue)\n        throw new Error(msg)\n      },\n    })\n    return true\n  } catch {\n    /* istanbul ignore next */\n    return false\n  }\n}\n\n/**\n * Recursively makes an object and its properties read-only\n * @param obj - Object to make read-only\n * @param visited - WeakSet to track visited objects and prevent circular references\n * @returns The read-only object, or original object if readonly fails\n */\nconst readonly = <T>(obj: T, visited?: WeakSet<any>): T => {\n  try {\n    if (obj == null || typeof obj !== 'object') return obj\n\n    const protoType = Object.prototype.toString.call(obj)\n    if (!['[object Object]', '[object Array]'].includes(protoType)) return obj\n\n    const isImmerDraft = (obj: any) => !!obj[Symbol.for('immer-state')]\n    if (isImmerDraft(obj)) return obj\n\n    const proto = Object.getPrototypeOf(obj)\n    if (\n      proto !== Object.prototype &&\n      proto !== Array.prototype &&\n      proto !== null\n    ) {\n      /* istanbul ignore next */\n      return obj\n    }\n\n    // Initialize visited set on first call\n    if (!visited) {\n      visited = new WeakSet()\n    }\n\n    // Check if object has already been processed to prevent circular references\n    if (visited.has(obj)) return obj\n\n    // Mark object as visited before processing\n    visited.add(obj)\n\n    Object.keys(obj).forEach((key) => {\n      try {\n        const descriptor = Object.getOwnPropertyDescriptor(obj, key)\n\n        if (!descriptor || descriptor.configurable === false) return\n        if (descriptor.get || descriptor.set) return\n\n        defineReactive(obj, key, (obj as any)[key], visited!)\n      } catch {}\n    })\n\n    return obj\n  } catch {\n    /* istanbul ignore next */\n    return obj\n  }\n}\n\n/**\n * Ensures maxAge is within valid range (0 to 7 days)\n * @param maxAge - The requested max age in milliseconds\n * @returns A valid max age value\n */\nconst getRange = (maxAge: number) => {\n  if (maxAge < 0) return 0\n  if (maxAge > _7days) return _7days\n  return maxAge\n}\n\n/**\n * Global store for caching promises and their results\n * Contains tracking information for each promise identified by globalKey\n */\nlet _globalStore: Record<\n  string | symbol,\n  [\n    number, // [K.retryCount]: number\n    Status, // [K.status]: Status\n    Array<any>, // [K.pendingList]: Array<any>\n    any | undefined, // [K.resolvedData]: any | undefined\n    any | undefined, // [K.rejectionError]: any | undefined\n    any, // [K.cachedPromiseFunc]: any\n    number | undefined, // [K.timerId]: number | undefined\n    string, // [K._originalErrorStack]: string\n  ]\n> = {}\n\n/**\n * Normalizes options, applying defaults as needed\n * @param options - User-provided options\n * @returns Normalized options object\n */\nconst getOptions = (options?: IdmpOptions) => {\n  const {\n    maxRetry = 30,\n    maxAge: paramMaxAge = DEFAULT_MAX_AGE,\n    minRetryDelay = 50,\n    maxRetryDelay = 5000,\n    onBeforeRetry = noop,\n    signal,\n  } = options || {}\n\n  const maxAge = getRange(paramMaxAge)\n  return {\n    maxRetry,\n    maxAge,\n    minRetryDelay,\n    maxRetryDelay,\n    onBeforeRetry,\n    f: paramMaxAge === 1 / 0, // Infinity\n    signal,\n  }\n}\n\n/**\n * Clears the cached result for a specific key\n * @param globalKey - The key to clear from cache\n */\nconst flush = (globalKey: IdmpGlobalKey) => {\n  if (!globalKey) return\n  const cache = _globalStore[globalKey]\n  if (!cache) return\n  cache[K.timerId] && $clearTimeout(cache[K.timerId])\n  ;(_globalStore[globalKey] as unknown) = UNDEFINED\n}\n\n/**\n * Clears all cached results\n */\nconst flushAll = () => {\n  for (let key of Object.keys(_globalStore)) {\n    flush(key)\n  }\n  _globalStore = {}\n}\n\n/**\n * Main IDMP function - Intelligent Deduplication of Multiple Promises\n *\n * Ensures that multiple calls to the same asynchronous operation (identified by globalKey)\n * will reuse the same promise, avoiding duplicate network requests or operations.\n * Includes automatic retry logic, caching, and request deduplication.\n *\n * @param globalKey - Unique identifier for this promise operation\n * @param promiseFunc - Function that returns the promise to execute\n * @param options - Configuration options\n * @returns Promise resolving to the result of promiseFunc\n *\n * @example\n * ```typescript\n * // Basic usage\n * const data = await idmp('user-123', () => fetchUserData(123));\n *\n * // With options\n * const data = await idmp('user-123', () => fetchUserData(123), {\n *   maxRetry: 5,\n *   maxAge: 60000, // 1 minute cache\n *   onBeforeRetry: (err, {retryCount}) => console.log(`Retry #${retryCount}`)\n * });\n * ```\n */\nconst idmp = <T>(\n  globalKey: IdmpGlobalKey,\n  promiseFunc: IdmpPromise<T>,\n  options?: IdmpOptions,\n): Promise<T> => {\n  if (process.env.NODE_ENV !== 'production') {\n    options = readonly(options)\n  }\n\n  if (!globalKey) {\n    return promiseFunc()\n  }\n\n  const {\n    maxRetry,\n    minRetryDelay,\n    maxRetryDelay,\n    maxAge,\n    onBeforeRetry,\n    f: isFiniteParamMaxAge,\n    signal,\n  } = getOptions(options)\n\n  _globalStore[globalKey] = _globalStore[globalKey] || [\n    0, // [K.retryCount]: number\n    Status.UNSENT, // [K.status]: Status\n    [], // [K.pendingList]: Array<any>\n  ]\n\n  const cache = _globalStore[globalKey]\n\n  let callStackLocation = ''\n  const printLogs = (...msg: any[]) => {\n    if (typeof window === 'undefined') return\n    try {\n      if (localStorage.idmp_debug === 'false') return\n    } catch {}\n    /* istanbul ignore else -- Fallback for envs without console.groupCollapsed */\n    if (console.groupCollapsed) {\n      console.groupCollapsed(...msg)\n      console.log('globalKey:', globalKey)\n      console.log('callStackLocation:', callStackLocation)\n      console.log('data:', cache[K.resolvedData])\n      console.groupEnd()\n    } else {\n      console.log(...msg)\n    }\n  }\n\n  /**\n   * Resets the promise state for retrying\n   */\n  const reset = () => {\n    cache[K.status] = Status.UNSENT\n    cache[K.resolvedData] = cache[K.rejectionError] = UNDEFINED\n  }\n\n  /**\n   * Resolves all pending promises with the cached result\n   */\n  const doResolves = () => {\n    const len = cache[K.pendingList].length\n    for (let i = 0; i < len; ++i) {\n      cache[K.pendingList][i][0](cache[K.resolvedData])\n      if (process.env.NODE_ENV !== 'production') {\n        if (i === 0) {\n          printLogs(\n            `%c[idmp debug] ${globalKey?.toString()} from origin`,\n            'font-weight: lighter',\n          )\n        } else {\n          printLogs(\n            `%c[idmp debug] ${globalKey?.toString()} from cache`,\n            'color: gray; font-weight: lighter',\n          )\n        }\n      }\n    }\n    cache[K.pendingList] = []\n\n    if (!isFiniteParamMaxAge) {\n      cache[K.timerId] = $timeout(() => {\n        flush(globalKey)\n      }, maxAge) as unknown as number\n    }\n  }\n\n  /**\n   * Rejects all pending promises with the cached error\n   */\n  const doRejects = () => {\n    const len = cache[K.pendingList].length\n    let maxLen\n    maxLen = len - maxRetry\n\n    if (maxLen < 0 || !isFinite(len)) {\n      maxLen = getMax(1, cache[K.pendingList].length - 3)\n    }\n\n    for (let i = 0; i < maxLen; ++i) {\n      cache[K.pendingList][i][1](cache[K.rejectionError])\n    }\n    flush(globalKey)\n  }\n\n  /**\n   * Creates and manages the actual promise execution\n   */\n  const executePromise = () =>\n    new Promise<T>((resolve, reject) => {\n      !cache[K.cachedPromiseFunc] && (cache[K.cachedPromiseFunc] = promiseFunc)\n\n      if (process.env.NODE_ENV !== 'production') {\n        try {\n          if (cache[K.retryCount] === 0) {\n            throw new Error()\n          }\n        } catch (err: any) {\n          const getCodeLine = (stack: string, offset = 0): string => {\n            if (typeof globalKey === 'symbol') return ''\n            try {\n              let arr = (stack as any)\n                .split('\\n')\n                .filter((o: string) => o.includes(':'))\n\n              let idx = Infinity\n              $0: for (let key of [\n                'idmp/src/index.ts',\n                'idmp/',\n                'idmp\\\\',\n                'idmp',\n              ]) {\n                let _idx = arr.length - 1\n                $1: for (; _idx >= 0; --_idx) {\n                  if (arr[_idx].indexOf(key) > -1) {\n                    idx = _idx\n                    break $0\n                  }\n                }\n              }\n\n              const line = arr[idx + offset + 1] || ''\n              if (line.includes('idmp')) return line\n              /* istanbul ignore next */\n              return ''\n            } catch {\n              /* istanbul ignore next */\n              return ''\n            }\n          }\n\n          callStackLocation = getCodeLine(err.stack, 1).split(' ').pop() || ''\n          !cache[K._originalErrorStack] &&\n            (cache[K._originalErrorStack] = err.stack)\n\n          if (cache[K._originalErrorStack] !== err.stack) {\n            const line1 = getCodeLine(cache[K._originalErrorStack])\n            const line2 = getCodeLine(err.stack)\n\n            if (line1 && line2 && line1 !== line2) {\n              console.error(\n                `[idmp warn] the same key \\`${globalKey.toString()}\\` may be used multiple times in different places\\n(It may be a misjudgment and can be ignored):\\nsee https://github.com/ha0z1/idmp?tab=readme-ov-file#implementation \\n${[\n                  `1.${line1} ${cache[K._originalErrorStack]}`,\n                  '------------',\n                  `2.${line2} ${err.stack}`,\n                ].join('\\n')}`,\n              )\n            }\n          }\n        }\n      }\n\n      if (cache[K.status] === Status.RESOLVED) {\n        if (process.env.NODE_ENV !== 'production') {\n          printLogs(\n            `%c[idmp debug] \\`${globalKey?.toString()}\\` from cache`,\n            'color: gray;font-weight: lighter',\n          )\n        }\n        resolve(cache[K.resolvedData])\n        return\n      }\n\n      if (signal) {\n        if (signal.aborted) return\n\n        signal.addEventListener('abort', () => {\n          reset()\n          cache[K.rejectionError] = new DOMException(\n            signal.reason,\n            'AbortError',\n          )\n          doRejects()\n        })\n      }\n\n      if (cache[K.status] === Status.UNSENT) {\n        cache[K.status] = Status.OPENING\n        cache[K.pendingList].push([resolve, reject])\n\n        cache[K.cachedPromiseFunc]()\n          .then((data: T) => {\n            if (process.env.NODE_ENV !== 'production') {\n              cache[K.resolvedData] = readonly<T>(data)\n            } else {\n              cache[K.resolvedData] = data\n            }\n            doResolves()\n            cache[K.status] = Status.RESOLVED\n          })\n          .catch((err: any) => {\n            cache[K.status] = Status.REJECTED\n            cache[K.rejectionError] = err\n            ++cache[K.retryCount]\n\n            if (cache[K.retryCount] > maxRetry) {\n              doRejects()\n            } else {\n              onBeforeRetry(err, {\n                globalKey,\n                retryCount: cache[K.retryCount],\n              })\n              reset()\n              // Exponential Backoff Algorithm\n              const delay = getMin(\n                maxRetryDelay,\n                minRetryDelay * 2 ** (cache[K.retryCount] - 1),\n              )\n\n              $timeout(executePromise, delay)\n            }\n          })\n      } else if (cache[K.status] === Status.OPENING) {\n        cache[K.pendingList].push([resolve, reject])\n      }\n    })\n\n  return executePromise()\n}\n\n/**\n * Clear the cached result for a specific key\n */\nidmp.flush = flush\n\n/**\n * Clear all cached results\n */\nidmp.flushAll = flushAll\n\n/**\n * Type definition for the idmp function including its methods\n */\ntype Idmp = typeof idmp\nconst _s = process.env.NODE_ENV !== 'production' ? _globalStore : UNDEFINED\nidmp._s = _s\nexport default idmp\nexport {\n  getOptions,\n  idmp,\n  type Idmp,\n  type IdmpGlobalKey,\n  type IdmpOptions,\n  type IdmpPromise,\n}\n"]}