{"version":3,"file":"dispose-defer.cjs","names":["isPromise","isString","DefaultLogger"],"sources":["../../src/common/dispose-defer.ts"],"sourcesContent":["import type { DisposerFunction } from './dispose-types'\nimport type { LoggerInterface } from './log/log-base'\nimport { arrayFilterInPlace } from './data/array'\nimport { isString } from './data/is'\nimport { isPromise } from './exec/promise'\nimport { DefaultLogger } from './log/log'\n\nexport function polyfillUsing() {\n  try {\n    // @ts-expect-error just a polyfill\n    Symbol.dispose ??= Symbol('Symbol.dispose')\n    // @ts-expect-error just a polyfill\n    Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose')\n  }\n  catch (_) { }\n}\n\n// Symbol.dispose ??= Symbol('Symbol.dispose')\n// Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose')\n\n/** A disposable entry: either a function or an object with a dispose() method. */\ntype DisposableEntry = DisposerFunction | { dispose: DisposerFunction }\n\n/** Different kinds of implementations have grown, this should unify them  */\nfunction callDisposable(disposable: DisposableEntry): Promise<void> | void {\n  let result\n\n  if (typeof disposable === 'function')\n    result = disposable()\n  else if (typeof disposable.dispose === 'function')\n    result = disposable.dispose()\n\n  if (isPromise(result))\n    return result\n}\n\nexport interface UseDisposeConfig {\n  name?: string\n  log?: LoggerInterface\n}\n\nexport function useDispose(opt?: string | UseDisposeConfig | LoggerInterface) {\n  if (opt != null) {\n    if (isString(opt))\n      opt = { name: opt }\n    else if ('debug' in opt && 'label' in opt)\n      opt = { name: opt.label, log: opt }\n  }\n\n  polyfillUsing()\n\n  const name = opt?.name\n  const log = opt?.log ?? DefaultLogger('zeed:dispose')\n\n  let _disposed = 0\n\n  const tracked: DisposableEntry[] = []\n\n  function untrack(disposable: DisposableEntry): Promise<void> | void {\n    if (disposable != null && tracked.includes(disposable)) {\n      arrayFilterInPlace(tracked, el => el !== disposable)\n      const result = callDisposable(disposable)\n      if (isPromise(result))\n        return result\n    }\n  }\n\n  function track(obj?: DisposableEntry): DisposerFunction | undefined {\n    if (obj == null)\n      return\n    tracked.unshift(obj) // LIFO\n    return () => untrack(obj)\n  }\n\n  /** Dispose all tracked entries */\n  function dispose(strictSync = false): Promise<any> | void {\n    if (name)\n      log.debug(`dispose \"${name}\": ${tracked.length} entries`)\n\n    _disposed += 1\n\n    const promises: any[] = []\n    while (tracked.length > 0) {\n      const fn = tracked[0]\n      const result = untrack(fn) // LIFO\n      if (isPromise(result)) {\n        if (strictSync)\n          throw new Error(`Async disposable found: ${fn} -> ${result}`)\n        else\n          promises.push(result)\n      }\n    }\n\n    if (promises.length > 0)\n      return Promise.all(promises)\n  }\n\n  /** Dispose all tracked entries in synchronous way. */\n  function disposeSync(): void {\n    dispose(true)\n  }\n\n  return Object.assign(dispose, {\n    /** Counter that increments each time dispose has been called */\n    get disposed() {\n      return _disposed\n    },\n\n    add: track,\n    remove: untrack,\n\n    /** @deprecated use add */\n    track,\n\n    /** @deprecated use remove */\n    untrack,\n\n    dispose,\n    disposeSync,\n    sync: disposeSync,\n\n    /** @deprecated use dispose */\n    exec: dispose,\n    getSize() {\n      return tracked.length\n    },\n    isDisposed() {\n      return _disposed > 0 && tracked.length === 0\n    },\n\n    [Symbol.dispose]() {\n      return dispose()\n    },\n\n    async [Symbol.asyncDispose]() {\n      return await dispose()\n    },\n\n  })\n}\n\nexport type UseDispose = ReturnType<typeof useDispose>\n\nexport function useDefer(\n  config: {\n    mode?: 'lifo' | 'fifo'\n  } = {},\n) {\n  const { mode = 'fifo' } = config\n  const steps: DisposableEntry[] = []\n\n  polyfillUsing()\n\n  /**\n   * Executes all steps. If all steps are not Promises, they are executed immediately,\n   * otherwise a Promise is returned.\n   * Note: useDefer defaults to FIFO order; use mode: 'lifo' for LIFO (stack) order.\n   */\n  const exec = async (expectSync = false) => {\n    while (steps.length > 0) {\n      const step = steps[0]\n      arrayFilterInPlace(steps, el => el !== step)\n      if (typeof step === 'function') {\n        const result = step()\n        if (isPromise(result)) {\n          if (expectSync) {\n            throw new Error(\n              `Expected sync only function, but found async: ${step}`,\n            )\n          }\n          await result\n        }\n      }\n      else if (typeof step.dispose === 'function') {\n        // Handle objects with a dispose() method (consistent with useDispose)\n        const result = step.dispose()\n        if (isPromise(result)) {\n          if (expectSync) {\n            throw new Error(\n              `Expected sync only function, but found async: ${step}`,\n            )\n          }\n          await result\n        }\n      }\n      else {\n        throw new TypeError(`Unhandled disposable: ${step}`)\n      }\n    }\n  }\n\n  const add = (obj: DisposableEntry) => {\n    if (mode === 'lifo')\n      steps.unshift(obj)\n    else\n      steps.push(obj)\n  }\n\n  return Object.assign(exec, {\n    add,\n    exec,\n    getSize() {\n      return steps.length\n    },\n  })\n}\n\nexport type UseDefer = ReturnType<typeof useDefer>\n"],"mappings":"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"}