{"version":3,"file":"index.d.ts","sources":["../../lib/index.ts"],"sourcesContent":["/**\n * Promise.all with automatic dependency optimization and full type inference\n *\n * Usage:\n * const { a, b, c } = await all({\n *   a() { return 1 },\n *   async b() { return 'hello' },\n *   async c() { return (await this.$.a) + 10 }\n * })\n */\n\n// Extract the resolved return type from task functions\ntype TaskResult<T> = T extends (...args: any[]) => infer R ? Awaited<R> : never\n\n// The $ proxy type - all task results as promises\ntype DepProxy<T extends Record<string, (...args: any[]) => any>> = {\n  readonly [K in keyof T]: Promise<TaskResult<T[K]>>\n}\n\n// Context available to each task via `this`\ntype TaskContext<T extends Record<string, (...args: any[]) => any>> = {\n  $: DepProxy<T>\n  $signal: AbortSignal\n}\n\n// Result type - all tasks resolved to their return values\ntype AllResult<T extends Record<string, (...args: any[]) => any>> = {\n  [K in keyof T]: TaskResult<T[K]>\n}\n\n// Settled result types for allSettled\ntype SettledFulfilled<T> = {\n  status: 'fulfilled'\n  value: T\n}\n\ntype SettledRejected = {\n  status: 'rejected'\n  reason: any\n}\n\ntype SettledResult<T> = SettledFulfilled<T> | SettledRejected\n\n// Result type for allSettled - all tasks as settled results\ntype AllSettledResult<T extends Record<string, (...args: any[]) => any>> = {\n  [K in keyof T]: SettledResult<TaskResult<T[K]>>\n}\n\n// Options for all() and allSettled()\ntype ExecutionOptions = {\n  debug?: boolean\n  signal?: AbortSignal\n}\n\n// Internal options for executeTasksInternal\ntype InternalExecutionOptions = ExecutionOptions & {\n  flowMode?: boolean\n}\n\n// Tracking info for debug mode\ntype TaskTiming = {\n  name: string\n  startTime: number\n  endTime: number\n  duration: number\n  dependencies: string[]\n  status: 'fulfilled' | 'rejected'\n  waitPeriods: Array<{ start: number; end: number }>\n}\n\n/**\n * Generate ASCII waterfall chart for task execution\n */\nfunction generateWaterfallChart(timings: TaskTiming[]): string {\n  if (timings.length === 0) return ''\n\n  const startTime = Math.min(...timings.map((t) => t.startTime))\n  const endTime = Math.max(...timings.map((t) => t.endTime))\n  const totalDuration = endTime - startTime\n\n  // Find longest task name for padding\n  const maxNameLength = Math.max(...timings.map((t) => t.name.length))\n  const maxDepsLength = Math.max(\n    ...timings.map((t) =>\n      t.dependencies.length > 0 ? t.dependencies.join(', ').length : 0,\n    ),\n    4, // minimum for \"Deps\" header\n  )\n\n  // Calculate scale (how many ms per character)\n  const chartWidth = 60\n  const scale = totalDuration / chartWidth\n  const threshold = 0.5\n\n  const totalDurationString = totalDuration.toFixed(2)\n\n  let output = '\\n'\n  output +=\n    '╔════════════════════════════════════════════════════════════════════════════════╗\\n'\n  output +=\n    '║                           Task Execution Waterfall                             ║\\n'\n  output +=\n    '╠════════════════════════════════════════════════════════════════════════════════╣\\n'\n  output += `║ Total Duration: ${totalDurationString}ms${' '.repeat(\n    61 - totalDurationString.length,\n  )}║\\n`\n  output +=\n    '╚════════════════════════════════════════════════════════════════════════════════╝\\n\\n'\n\n  // Header\n  output += `${'Task'.padEnd(maxNameLength)} │ ${'Deps'.padEnd(\n    maxDepsLength,\n  )} │ Duration │ Timeline\\n`\n  output += `${'─'.repeat(maxNameLength)}─┼─${'─'.repeat(\n    maxDepsLength,\n  )}─┼──────────┼─${'─'.repeat(chartWidth)}\\n`\n\n  // Sort by start time\n  const sortedTimings = [...timings].sort((a, b) => a.startTime - b.startTime)\n\n  for (const timing of sortedTimings) {\n    const name = timing.name.padEnd(maxNameLength)\n    const deps = (\n      timing.dependencies.length > 0 ? timing.dependencies.join(', ') : '-'\n    ).padEnd(maxDepsLength)\n    const duration = `${timing.duration.toFixed(1)}ms`.padStart(8)\n\n    // Build timeline character by character\n    const timeline: string[] = []\n    const relativeStart = timing.startTime - startTime\n    const relativeEnd = timing.endTime - startTime\n\n    for (let i = 0; i < chartWidth; i++) {\n      // Add threshold to avoid execution delay (i.e. 0.1ms) while the task\n      // starts at the same time.\n      const timePos = i * scale + threshold\n\n      if (timePos < relativeStart || timePos >= relativeEnd) {\n        // Before task starts or after task ends\n        timeline.push(' ')\n      } else {\n        // Task is executing in this time range\n        // Check if this position is in a wait period\n        const absoluteTime = startTime + timePos\n        const isWaiting = timing.waitPeriods.some(\n          (wait) => absoluteTime >= wait.start && absoluteTime < wait.end,\n        )\n\n        if (isWaiting) {\n          // Waiting on dependency\n          timeline.push('░')\n        } else {\n          // Active execution\n          timeline.push(timing.status === 'fulfilled' ? '█' : '▓')\n        }\n      }\n    }\n\n    output += `${name} │ ${deps} │ ${duration} │ ${timeline.join('')}\\n`\n  }\n\n  output += '\\n'\n  output +=\n    'Legend: █ = active (fulfilled), ▓ = active (rejected), ░ = waiting on dependency\\n'\n  output += '\\n'\n\n  return output\n}\n\n/**\n * Internal core implementation for executing tasks with automatic dependency resolution.\n * This is shared between `all`, `allSettled`, and `flow`.\n */\nfunction executeTasksInternal<T extends Record<string, any>>(\n  tasks: T,\n  handleSettled: boolean,\n  options: InternalExecutionOptions = {},\n): Promise<any> {\n  const taskNames = Object.keys(tasks) as (keyof T)[]\n  const results = new Map<keyof T, any>()\n  const errors = new Map<keyof T, any>()\n  const resolvers = new Map<\n    keyof T,\n    [(value: any) => void, (reason?: any) => void][]\n  >()\n  const returnValue: Record<string, any> = {}\n\n  // Flow mode tracking\n  let flowEnded = false\n  let flowEndValue: any = undefined\n\n  // Create internal abort controller for auto-abort on failure and external signal propagation\n  const internalController = new AbortController()\n\n  // Controller to manage cleanup of the external signal listener\n  const cleanupController = new AbortController()\n\n  // If external signal is provided, propagate its abort to internal controller\n  if (options.signal) {\n    if (options.signal.aborted) {\n      internalController.abort(options.signal.reason)\n    } else {\n      options.signal.addEventListener(\n        'abort',\n        () => internalController.abort(options.signal!.reason),\n        { once: true, signal: cleanupController.signal },\n      )\n    }\n  }\n\n  // Debug tracking\n  const timings: TaskTiming[] = []\n  const taskStartTimes = new Map<keyof T, number>()\n  const taskDependencies = new Map<keyof T, Set<string>>()\n  const taskWaitPeriods = new Map<\n    keyof T,\n    Array<{ start: number; end: number }>\n  >()\n\n  const waitForDep = (taskName: keyof T, depName: keyof T): Promise<any> => {\n    if (!(depName in tasks)) {\n      return Promise.reject(new Error(`Unknown task \"${String(depName)}\"`))\n    }\n\n    // In flow mode, if flow has ended, reject with FlowAbortedError\n    if (options.flowMode && flowEnded) {\n      return Promise.reject(new FlowAbortedError())\n    }\n\n    // Track dependency for debug mode\n    if (options.debug) {\n      if (!taskDependencies.has(taskName)) {\n        taskDependencies.set(taskName, new Set())\n      }\n      taskDependencies.get(taskName)!.add(String(depName))\n    }\n\n    let basePromise: Promise<any>\n\n    if (results.has(depName)) {\n      basePromise = Promise.resolve(results.get(depName))\n    } else if (errors.has(depName)) {\n      basePromise = Promise.reject(errors.get(depName))\n    } else {\n      basePromise = new Promise((resolve, reject) => {\n        if (!resolvers.has(depName)) {\n          resolvers.set(depName, [])\n        }\n        resolvers.get(depName)!.push([resolve, reject])\n      })\n    }\n\n    // Wrap promise to track wait time in debug mode\n    if (options.debug) {\n      const waitStart = performance.now()\n      return basePromise.then(\n        (value) => {\n          const waitEnd = performance.now()\n          if (!taskWaitPeriods.has(taskName)) {\n            taskWaitPeriods.set(taskName, [])\n          }\n          taskWaitPeriods\n            .get(taskName)!\n            .push({ start: waitStart, end: waitEnd })\n          return value\n        },\n        (error) => {\n          const waitEnd = performance.now()\n          if (!taskWaitPeriods.has(taskName)) {\n            taskWaitPeriods.set(taskName, [])\n          }\n          taskWaitPeriods\n            .get(taskName)!\n            .push({ start: waitStart, end: waitEnd })\n          throw error\n        },\n      )\n    }\n\n    return basePromise\n  }\n\n  const handleResult = (name: keyof T, value: any) => {\n    results.set(name, value)\n    if (handleSettled) {\n      returnValue[name as string] = { status: 'fulfilled', value }\n    } else if (!options.flowMode) {\n      returnValue[name as string] = value\n    }\n    if (resolvers.has(name)) {\n      for (const [resolve] of resolvers.get(name)!) {\n        resolve(value)\n      }\n    }\n  }\n\n  const handleError = (name: keyof T, err: any) => {\n    errors.set(name, err)\n    if (handleSettled) {\n      returnValue[name as string] = { status: 'rejected', reason: err }\n    }\n    if (resolvers.has(name)) {\n      for (const [, reject] of resolvers.get(name)!) {\n        reject(err)\n      }\n    }\n  }\n\n  // Run all tasks in parallel\n  const promises = taskNames.map(async (name) => {\n    try {\n      const taskFn = tasks[name]\n      if (typeof taskFn !== 'function') {\n        throw new Error(`Task \"${String(name)}\" is not a function`)\n      }\n\n      // Track start time for debug mode\n      if (options.debug) {\n        taskStartTimes.set(name, performance.now())\n      }\n\n      // Create a unique dep proxy for each task to track dependencies\n      const depProxy = new Proxy({} as DepProxy<T>, {\n        get(_, depName: string) {\n          return waitForDep(name, depName as keyof T)\n        },\n      })\n\n      // Create $end function for flow mode\n      const $end = options.flowMode\n        ? (value: any): never => {\n            if (!flowEnded) {\n              flowEnded = true\n              flowEndValue = value\n            }\n            throw new FlowEndError(value)\n          }\n        : undefined\n\n      const context: any = {\n        $: depProxy,\n        $signal: internalController.signal,\n      }\n\n      if (options.flowMode && $end) {\n        context.$end = $end\n      }\n\n      const result = await taskFn.call(context)\n\n      // Track end time and create timing record\n      if (options.debug) {\n        const endTime = performance.now()\n        const startTime = taskStartTimes.get(name)!\n        timings.push({\n          name: String(name),\n          startTime,\n          endTime,\n          duration: endTime - startTime,\n          dependencies: Array.from(taskDependencies.get(name) || []),\n          status: 'fulfilled',\n          waitPeriods: taskWaitPeriods.get(name) || [],\n        })\n      }\n\n      handleResult(name, result)\n    } catch (err) {\n      // In flow mode, handle FlowEndError and FlowAbortedError specially\n      if (options.flowMode) {\n        if (err instanceof FlowEndError) {\n          // This is intentional early exit, don't propagate as error\n          return\n        }\n        if (err instanceof FlowAbortedError) {\n          // Flow was ended by another task, silently ignore\n          return\n        }\n      }\n\n      // Track end time for failed tasks too\n      if (options.debug) {\n        const endTime = performance.now()\n        const startTime = taskStartTimes.get(name)!\n        timings.push({\n          name: String(name),\n          startTime,\n          endTime,\n          duration: endTime - startTime,\n          dependencies: Array.from(taskDependencies.get(name) || []),\n          status: 'rejected',\n          waitPeriods: taskWaitPeriods.get(name) || [],\n        })\n      }\n\n      handleError(name, err)\n      if (!handleSettled) {\n        // Abort other tasks when one fails (only for all(), not allSettled())\n        internalController.abort(err)\n        throw err\n      }\n    }\n  })\n\n  const finalPromise = options.flowMode\n    ? // For flow mode, use allSettled and handle flow end\n      Promise.allSettled(promises).then((results) => {\n        cleanupController.abort()\n\n        // Check if external signal was aborted\n        if (options.signal?.aborted) {\n          throw options.signal.reason || new Error('Aborted')\n        }\n\n        // Check if any task had a real error\n        for (const result of results) {\n          if (result.status === 'rejected') {\n            throw result.reason\n          }\n        }\n\n        // If flow ended early, return that value\n        if (flowEnded) {\n          return flowEndValue\n        }\n\n        // No task called $end() - return undefined\n        return undefined\n      })\n    : handleSettled\n      ? // For allSettled, wait for all promises to settle (never rejects)\n        Promise.allSettled(promises).then(() => returnValue)\n      : // For all, reject on first error (like Promise.all)\n        Promise.all(promises).then(() => returnValue)\n\n  // Cleanup external signal listener when tasks complete\n  const withCleanup = options.flowMode\n    ? finalPromise\n    : finalPromise.finally(() => {\n        cleanupController.abort()\n      })\n\n  // Output waterfall chart in debug mode\n  if (options.debug) {\n    return withCleanup.then(\n      (result) => {\n        console.log(generateWaterfallChart(timings))\n        return result\n      },\n      (error) => {\n        console.log(generateWaterfallChart(timings))\n        throw error\n      },\n    )\n  }\n\n  return withCleanup\n}\n\n/**\n * Execute tasks with automatic dependency resolution.\n *\n * @example\n * const { a, b, c } = await all({\n *   async a() { return 1 },\n *   async b() { return 'hello' },\n *   async c() { return (await this.$.a) + 10 }\n * })\n *\n * @example\n * // With debug mode\n * const result = await all({\n *   async a() { return 1 },\n *   async b() { return (await this.$.a) + 10 }\n * }, { debug: true })\n *\n * @example\n * // With auto-abort on failure - this.$signal is aborted when any sibling task fails\n * const result = await all({\n *   async a() { return fetchWithSignal(this.$signal) },\n *   async b() { throw new Error('fails') }, // This will abort tasks a and c\n *   async c() { return fetchWithSignal(this.$signal) }\n * })\n *\n * @example\n * // With external signal\n * const controller = new AbortController()\n * const result = await all({\n *   async a() { return fetchWithSignal(this.$signal) }\n * }, { signal: controller.signal })\n */\nexport function all<T extends Record<string, any>>(\n  tasks: T &\n    ThisType<{\n      $: {\n        [K in keyof T]: ReturnType<T[K]> extends Promise<infer R>\n          ? Promise<R>\n          : Promise<ReturnType<T[K]>>\n      }\n      $signal: AbortSignal\n    }> & {\n      [K in keyof T as T[K] extends Function\n        ? K\n        : `Error: task \\`${K & string}\\` is not a function`]-?: T[K]\n    },\n  options?: ExecutionOptions,\n): Promise<AllResult<T>> {\n  return executeTasksInternal(tasks, false, options) as Promise<AllResult<T>>\n}\n\n/**\n * Execute tasks with automatic dependency resolution, returning settled results for all tasks.\n * Unlike `all`, this will never reject - failed tasks will be included in the result with their error.\n *\n * @example\n * const { a, b, c } = await allSettled({\n *   async a() { return 1 },\n *   async b() { throw new Error('failed') },\n *   async c() { return (await this.$.a) + 10 }\n * })\n * // a: { status: 'fulfilled', value: 1 }\n * // b: { status: 'rejected', reason: Error('failed') }\n * // c: { status: 'fulfilled', value: 11 }\n *\n * @example\n * // With debug mode\n * const result = await allSettled({\n *   async a() { return 1 },\n *   async b() { throw new Error('failed') }\n * }, { debug: true })\n */\nexport function allSettled<T extends Record<string, any>>(\n  tasks: T &\n    ThisType<{\n      $: {\n        [K in keyof T]: ReturnType<T[K]> extends Promise<infer R>\n          ? Promise<R>\n          : Promise<ReturnType<T[K]>>\n      }\n      $signal: AbortSignal\n    }> & {\n      [P in keyof T]: T[P] extends (...args: any[]) => any ? T[P] : never\n    },\n  options?: ExecutionOptions,\n): Promise<AllSettledResult<T>> {\n  return executeTasksInternal(tasks, true, options) as Promise<\n    AllSettledResult<T>\n  >\n}\n\n/**\n * Custom error class for early exit via $end()\n * @internal\n */\nclass FlowEndError extends Error {\n  constructor(public readonly value: any) {\n    super('Flow ended early')\n    this.name = 'FlowEndError'\n  }\n}\n\n/**\n * Custom error class for aborted dependency access\n * @internal\n */\nclass FlowAbortedError extends Error {\n  constructor() {\n    super('Flow has been ended, cannot access dependencies')\n    this.name = 'FlowAbortedError'\n  }\n}\n\n// Context available to each task in flow via `this`\ntype FlowTaskContext<\n  T extends Record<string, (...args: any[]) => any>,\n  R,\n> = {\n  $: DepProxy<T>\n  $signal: AbortSignal\n  $end: (value: R) => never\n}\n\n/**\n * Execute tasks with automatic dependency resolution and support for early exit.\n * The first task to call `this.$end(value)` determines the return value.\n *\n * @example\n * // Early exit from first task\n * const f = await flow<number>({\n *   async task1() {\n *     this.$end(42)  // Immediately ends, f = 42\n *     return 1       // Never reached\n *   },\n *   async task2() {\n *     const r = await this.$.task1  // Throws (silently caught)\n *     return r + 10\n *   },\n * })\n * // f = 42\n *\n * @example\n * // Conditional early exit\n * const f = await flow<string>({\n *   async task1() {\n *     const cached = await checkCache()\n *     if (cached) this.$end(cached)  // Early exit if cached\n *     return await fetchFromApi()\n *   },\n *   async task2() {\n *     const data = await this.$.task1\n *     this.$end(transform(data))\n *   },\n * })\n *\n * @example\n * // Race between tasks\n * const f = await flow<string>({\n *   async fast() {\n *     await sleep(100)\n *     this.$end('fast won')\n *   },\n *   async slow() {\n *     await sleep(1000)\n *     this.$end('slow won')\n *   },\n * })\n * // f = 'fast won'\n */\nexport function flow<R, T extends Record<string, any> = Record<string, any>>(\n  tasks: T &\n    ThisType<{\n      $: {\n        [K in keyof T]: ReturnType<T[K]> extends Promise<infer U>\n          ? Promise<U>\n          : Promise<ReturnType<T[K]>>\n      }\n      $signal: AbortSignal\n      $end: (value: R) => never\n    }> & {\n      [K in keyof T as T[K] extends Function\n        ? K\n        : `Error: task \\`${K & string}\\` is not a function`]-?: T[K]\n    },\n  options?: ExecutionOptions,\n): Promise<R | undefined> {\n  return executeTasksInternal(tasks, false, {\n    ...options,\n    flowMode: true,\n  }) as Promise<R | undefined>\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAA,UAAA,8CAAA,OAAA;AACA,KAAA,SAAA,WAAA,MAAA;AACA,oBAAA,UAAA;AACA;AACA,KAAA,gBAAA;AACA;AACA;AACA;AACA,KAAA,eAAA;AACA;AACA;AACA;AACA,KAAA,aAAA,MAAA,gBAAA,MAAA,eAAA;AACA,KAAA,gBAAA,WAAA,MAAA;AACA,oBAAA,aAAA,CAAA,UAAA;AACA;AACA,KAAA,gBAAA;AACA;AACA,aAAA,WAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,iBAAA,GAAA,WAAA,MAAA,0BAAA,QAAA;AACP;AACA,wBAAA,UAAA,eAAA,OAAA,YAAA,OAAA,MAAA,OAAA,CAAA,UAAA;AACA;AACA,aAAA,WAAA;AACA;AACA,kCAAA,QAAA;AACA,aAAA,gBAAA,GAAA,OAAA,CAAA,SAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,iBAAA,UAAA,WAAA,MAAA,0BAAA,QAAA;AACP;AACA,wBAAA,UAAA,eAAA,OAAA,YAAA,OAAA,MAAA,OAAA,CAAA,UAAA;AACA;AACA,aAAA,WAAA;AACA;AACA;AACA,aAAA,gBAAA,GAAA,OAAA,CAAA,gBAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,iBAAA,IAAA,cAAA,MAAA,gBAAA,MAAA,0BAAA,QAAA;AACP;AACA,wBAAA,UAAA,eAAA,OAAA,YAAA,OAAA,MAAA,OAAA,CAAA,UAAA;AACA;AACA,aAAA,WAAA;AACA;AACA;AACA,kCAAA,QAAA;AACA,aAAA,gBAAA,GAAA,OAAA;;;;"}