{"version":3,"file":"execution-CpbjNYTj.mjs","names":[],"sources":["../src/core/execution.ts"],"sourcesContent":["import type { ExecutionOptions } from \"./types.ts\";\n\nexport const DEFAULT_CONCURRENCY = 3;\nexport const MAX_CONCURRENCY = 10;\n\n/** Longest time budget an agent surface accepts through `timeoutSeconds`. */\nexport const MAX_AGENT_TIMEOUT_SECONDS = 3_600;\n\nconst MAX_TIMER_DELAY_MS = 2_147_483_647;\nconst executionBudget = Symbol(\"executionBudget\");\n\ntype BudgetedExecutionOptions = ExecutionOptions & {\n  readonly [executionBudget]: ExecutionBudget;\n};\n\n/** Ties each deadline controller to the signal an operation holds, composed or not. */\nconst deadlineControllers = new WeakMap<AbortSignal, AbortController>();\n\n/**\n * Creates one signal and concurrency budget for an operation and all nested work.\n * @param options - Public execution controls or an existing internal budget.\n * @returns {TOptions & BudgetedExecutionOptions} Options carrying the shared internal budget.\n */\nexport function withExecutionBudget<TOptions extends ExecutionOptions>(\n  options?: Readonly<TOptions>,\n): TOptions & BudgetedExecutionOptions {\n  const existing = (options as Readonly<BudgetedExecutionOptions> | undefined)?.[executionBudget];\n  if (existing) {\n    return {\n      ...options,\n      signal: existing.signal,\n      deadline: undefined,\n      [executionBudget]: existing,\n    } as TOptions & BudgetedExecutionOptions;\n  }\n\n  const signal = operationSignal(options);\n  const budget = new ExecutionBudget(\n    normalizedConcurrency(options?.concurrency),\n    signal,\n    options?.signal,\n  );\n  return { ...options, signal, deadline: undefined, [executionBudget]: budget } as TOptions &\n    BudgetedExecutionOptions;\n}\n\n/**\n * Creates a signal that observes the caller and one absolute operation deadline.\n * @param options - Caller cancellation, deadline, and concurrency controls.\n * @returns {Readonly<AbortSignal> | undefined} Caller or composed deadline signal.\n */\nexport function operationSignal(\n  options?: Readonly<ExecutionOptions>,\n): Readonly<AbortSignal> | undefined {\n  const existing = (options as Readonly<BudgetedExecutionOptions> | undefined)?.[executionBudget];\n  if (existing) return existing.signal;\n  normalizedConcurrency(options?.concurrency);\n\n  const controller = deadlineController(options?.deadline);\n  if (!controller) return options?.signal;\n  const signal = options?.signal\n    ? AbortSignal.any([options.signal, controller.signal])\n    : controller.signal;\n  deadlineControllers.set(signal, controller);\n  return signal;\n}\n\n/**\n * Removes scheduler-only state before options cross a provider boundary.\n * @param options - Internal operation options.\n * @returns {TOptions} Provider-visible options with only the effective signal retained.\n */\nexport function providerRequestOptions<TOptions extends ExecutionOptions>(\n  options: Readonly<TOptions>,\n): TOptions {\n  const {\n    [executionBudget]: _budget,\n    deadline: _deadline,\n    concurrency: _concurrency,\n    signal,\n    ...requestOptions\n  } = options as Readonly<TOptions & Partial<BudgetedExecutionOptions>>;\n  return {\n    ...requestOptions,\n    ...(signal === undefined ? {} : { signal }),\n  } as TOptions;\n}\n\n/**\n * Throws before another unit of network work starts after cancellation or timeout.\n * @param signal - Effective operation signal.\n */\nexport function throwIfAborted(signal?: Readonly<AbortSignal>): void {\n  signal?.throwIfAborted();\n}\n\n/**\n * Throws when the caller cancelled the operation, not when only its deadline passed.\n * @param options - Options carrying the operation budget.\n */\nexport function throwIfCancelled(options?: Readonly<ExecutionOptions>): void {\n  const budget = (options as Readonly<BudgetedExecutionOptions> | undefined)?.[executionBudget];\n  throwIfAborted(budget === undefined ? options?.signal : budget.callerSignal);\n}\n\n/**\n * Turns the time budget an agent passes into the absolute deadline the library takes.\n * @param seconds - Whole seconds from now, or undefined for no deadline.\n * @returns {number | undefined} Unix timestamp in milliseconds.\n */\nexport function deadlineAfterSeconds(seconds: number | undefined): number | undefined {\n  if (seconds === undefined) return undefined;\n  if (!Number.isSafeInteger(seconds) || seconds < 1 || seconds > MAX_AGENT_TIMEOUT_SECONDS) {\n    throw new RangeError(\n      `timeoutSeconds must be an integer between 1 and ${MAX_AGENT_TIMEOUT_SECONDS}`,\n    );\n  }\n  return Date.now() + seconds * 1000;\n}\n\n/**\n * Runs ordered work through the operation's shared concurrency budget.\n * @param items - Ordered work inputs.\n * @param worker - Function that performs one bounded unit of work.\n * @param options - Execution controls or an existing internal budget.\n * @returns {Promise<PromiseSettledResult<TResult>[]>} Settled outcomes in input order.\n */\nexport async function settleWithConcurrency<T, TResult>(\n  items: readonly T[],\n  worker: (item: T, index: number, signal?: Readonly<AbortSignal>) => Promise<TResult>,\n  options?: Readonly<ExecutionOptions>,\n): Promise<PromiseSettledResult<TResult>[]> {\n  const budgeted = withExecutionBudget(options);\n  return Promise.allSettled(\n    items.map((item, index) =>\n      budgeted[executionBudget].run(() => worker(item, index, budgeted.signal)),\n    ),\n  );\n}\n\nclass ExecutionBudget {\n  readonly signal: Readonly<AbortSignal> | undefined;\n  readonly callerSignal: Readonly<AbortSignal> | undefined;\n  readonly #concurrency: number;\n  readonly #queue: Array<{\n    run: () => Promise<unknown>;\n    resolve: (value: unknown) => void;\n    reject: (reason: unknown) => void;\n  }> = [];\n  #active = 0;\n  #listeningForAbort = false;\n\n  constructor(\n    concurrency: number,\n    signal?: Readonly<AbortSignal>,\n    callerSignal?: Readonly<AbortSignal>,\n  ) {\n    this.#concurrency = concurrency;\n    this.signal = signal;\n    this.callerSignal = callerSignal;\n  }\n\n  run<TResult>(task: () => Promise<TResult>): Promise<TResult> {\n    try {\n      throwIfAborted(this.signal);\n    } catch (error) {\n      return Promise.reject(error);\n    }\n\n    return new Promise<TResult>((resolve, reject) => {\n      this.#queue.push({\n        run: task,\n        resolve: (value) => resolve(value as TResult),\n        reject,\n      });\n      this.#listenForAbort();\n      this.#drain();\n    });\n  }\n\n  #drain(): void {\n    if (this.signal?.aborted) {\n      this.#rejectQueued(this.signal.reason);\n      return;\n    }\n\n    while (this.#active < this.#concurrency) {\n      const queued = this.#queue.shift();\n      if (!queued) break;\n      this.#active += 1;\n      void Promise.resolve()\n        .then(() => {\n          throwIfAborted(this.signal);\n          return queued.run();\n        })\n        .then(queued.resolve, queued.reject)\n        .finally(() => {\n          this.#active -= 1;\n          this.#drain();\n        });\n    }\n    if (this.#queue.length === 0) this.#stopListeningForAbort();\n  }\n\n  #listenForAbort(): void {\n    if (!this.signal || this.#listeningForAbort) return;\n    this.signal.addEventListener(\"abort\", this.#onAbort, { once: true });\n    this.#listeningForAbort = true;\n  }\n\n  #stopListeningForAbort(): void {\n    if (!this.signal || !this.#listeningForAbort) return;\n    this.signal.removeEventListener(\"abort\", this.#onAbort);\n    this.#listeningForAbort = false;\n  }\n\n  readonly #onAbort = (): void => {\n    this.#listeningForAbort = false;\n    this.#rejectQueued(this.signal?.reason);\n  };\n\n  #rejectQueued(reason: unknown): void {\n    let queued = this.#queue.shift();\n    while (queued) {\n      queued.reject(reason ?? new DOMException(\"The operation was aborted\", \"AbortError\"));\n      queued = this.#queue.shift();\n    }\n    this.#stopListeningForAbort();\n  }\n}\n\nfunction deadlineController(deadline?: number): AbortController | undefined {\n  if (deadline === undefined) return undefined;\n  if (!Number.isFinite(deadline)) throw new RangeError(\"deadline must be a finite Unix timestamp\");\n\n  const controller = new AbortController();\n  scheduleDeadline(new WeakRef(controller), deadline);\n  return controller;\n}\n\n/**\n * Arms the deadline timer through a weak reference, so a finished operation drops its\n * controller and signal graph before the timer fires.\n * @param controller - Weakly held controller of the deadline signal.\n * @param deadline - Absolute Unix timestamp in milliseconds.\n */\nfunction scheduleDeadline(controller: Readonly<WeakRef<AbortController>>, deadline: number): void {\n  const remaining = Math.ceil(deadline - Date.now());\n  if (remaining <= 0) {\n    controller\n      .deref()\n      ?.abort(new DOMException(\"The operation deadline was exceeded\", \"TimeoutError\"));\n    return;\n  }\n  const timer = setTimeout(\n    () => scheduleDeadline(controller, deadline),\n    Math.min(remaining, MAX_TIMER_DELAY_MS),\n  );\n  timer.unref();\n}\n\nfunction normalizedConcurrency(concurrency = DEFAULT_CONCURRENCY): number {\n  if (!Number.isSafeInteger(concurrency) || concurrency < 1 || concurrency > MAX_CONCURRENCY) {\n    throw new RangeError(`concurrency must be an integer between 1 and ${MAX_CONCURRENCY}`);\n  }\n  return concurrency;\n}\n"],"mappings":"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"}