{"version":3,"file":"async.cjs","names":[],"sources":["../src/async/async-template.ts","../src/async/endless-raf.ts","../src/async/set-abortable-interval.ts","../src/async/set-abortable-timeout.ts","../src/async/sleep.ts","../src/async/wait-async.ts"],"sourcesContent":["import type { MaybePromise } from 'yummies/types';\n\nimport type { AsyncTemplatePiece } from './async-template-piece.js';\n\n/**\n * Tagged template that builds an async iterable of string chunks (like a streaming template engine).\n *\n * Static template parts are yielded as-is. Each interpolated “piece” can be a primitive, a nested array of pieces,\n * a `Promise` of a piece (including a resolved primitive), an async iterable of primitives, or a\n * **zero-arg function** whose result is processed the same way (see {@link AsyncTemplatePiece} / `MaybeFn`).\n *\n * Interpolation handling matches `processTemplatePiece`: `null`, `undefined`, and `false` add nothing; any other value\n * becomes text via `String(...)`; functions are called once with no arguments before further processing. Collect chunks\n * with `for await...of` or utilities like `Array.fromAsync` where available.\n *\n * @param strings - Cooked template segments from the tag (`strings[0]`, `strings[1]`, …).\n * @param pieces - Values between segments (`pieces[i]` sits between `strings[i]` and `strings[i + 1]`).\n * @returns An async generator yielding string fragments in document order.\n *\n * @example\n * Plain values and promises:\n * ```ts\n * const gen = asyncTemplate`Hello, ${'world'}! Status: ${Promise.resolve(200)}`;\n * let out = '';\n * for await (const chunk of gen) out += chunk;\n * // out === 'Hello, world! Status: 200'\n * ```\n *\n * @example\n * Falsy pieces are omitted (`null`, `undefined`, `false`); arrays flatten recursively:\n * ```ts\n * const gen = asyncTemplate`${null}${false}A${['B', ['C']]}`;\n * const out = await Array.fromAsync(gen).then((parts) => parts.join(''));\n * // out === 'ABC'\n * ```\n *\n * @example\n * Stream from an async iterable (e.g. chunked upstream text):\n * ```ts\n * async function* lines() {\n *   yield 'line1\\n';\n *   yield 'line2\\n';\n * }\n * const gen = asyncTemplate`Header\\n${lines()}Footer`;\n * ```\n *\n * @example\n * Lazy piece via a thunk (invoked as `piece()`):\n * ```ts\n * let n = 0;\n * const gen = asyncTemplate`count=${() => ++n}`;\n * // first consumption yields \"count=1\", etc.\n * ```\n */\nexport async function* asyncTemplate(\n  strings: TemplateStringsArray,\n  ...pieces: MaybePromise<AsyncTemplatePiece>[]\n) {\n  for (let i = 0; i < strings.length; i++) {\n    yield strings[i];\n\n    if (i < pieces.length) {\n      const piece = pieces[i];\n      yield* processTemplatePiece(piece);\n    }\n  }\n}\n\n/**\n * Resolves a template piece into yielded string chunks.\n *\n * Skips output when `value === null || value === undefined || value === false`.\n * Otherwise awaits promises, flattens arrays, streams async iterables, invokes **functions** with no arguments and\n * recurses on the return value, and uses `String(value)` for primitives.\n */\nasync function* processTemplatePiece(\n  piece: MaybePromise<AsyncTemplatePiece>,\n): AsyncGenerator<string, void, unknown> {\n  if (piece === null || piece === undefined || piece === false) {\n    return;\n  }\n\n  if (piece instanceof Promise) {\n    const resolved = await piece;\n    yield* processTemplatePiece(resolved);\n    return;\n  }\n\n  if (Array.isArray(piece)) {\n    for (const item of piece) {\n      yield* processTemplatePiece(item);\n    }\n    return;\n  }\n\n  if (\n    typeof piece === 'object' &&\n    piece !== null &&\n    typeof (piece as AsyncIterable<unknown>)[Symbol.asyncIterator] ===\n      'function'\n  ) {\n    for await (const item of piece as AsyncIterable<AsyncTemplatePiece>) {\n      yield* processTemplatePiece(item);\n    }\n    return;\n  }\n\n  if (typeof piece === 'function') {\n    yield* processTemplatePiece(piece());\n    return;\n  }\n\n  yield String(piece);\n}\n","/**\n * Runs a loop driven by `requestAnimationFrame`: on each frame, `quitFunction` is called first.\n * If it returns a truthy value, the loop stops and no further frames are scheduled.\n * If it returns falsy or nothing, the next frame is scheduled recursively.\n *\n * Use this for per-frame work (animations, layout reads after paint) without managing\n * `cancelAnimationFrame` manually — returning `true` from `quitFunction` is the exit condition.\n *\n * When `asMicrotask` is `true`, scheduling the next RAF is deferred with `queueMicrotask`\n * so other microtasks can run before the frame is requested (useful when you need to\n * batch DOM updates or let reactive frameworks flush first).\n *\n * @param quitFunction - Invoked each animation frame. Return `true` to stop the loop.\n * @param asMicrotask - If `true`, wrap the `requestAnimationFrame` call in `queueMicrotask`.\n *\n * @example\n * Stop after 60 frames (~1s at 60Hz):\n * ```ts\n * let frames = 0;\n * endlessRAF(() => {\n *   frames++;\n *   updateSomething(frames);\n *   return frames >= 60;\n * });\n * ```\n *\n * @example\n * Run until an element is removed or a flag is set:\n * ```ts\n * let running = true;\n * endlessRAF(() => {\n *   if (!running || !document.body.contains(el)) return true;\n *   draw(el);\n * }, true);\n * ```\n */\nexport const endlessRAF = (\n  quitFunction: () => boolean | void,\n  asMicrotask?: boolean,\n) => {\n  if (quitFunction()) return;\n\n  const raf = () =>\n    requestAnimationFrame(() => endlessRAF(quitFunction, asMicrotask));\n\n  if (asMicrotask) {\n    queueMicrotask(raf);\n  } else {\n    raf();\n  }\n};\n","/**\n * Like `setInterval`, but when `signal` aborts, the interval is cleared with `clearInterval`\n * and `callback` stops being called. If `signal` is omitted, behaves like a normal interval\n * (you must clear it yourself).\n *\n * @param callback - Invoked every `delayInMs` milliseconds until aborted or cleared.\n * @param delayInMs - Interval period in milliseconds (same as `setInterval`).\n * @param signal - Aborting stops the interval and removes the abort listener path from keeping work alive.\n *\n * @example\n * ```ts\n * const controller = new AbortController();\n * setAbortableInterval(() => console.log('tick'), 1000, controller.signal);\n * // stop: controller.abort();\n * ```\n *\n * @example\n * ```ts\n * const ac = new AbortController();\n * setAbortableInterval(syncStatus, 30_000, ac.signal);\n * window.addEventListener('beforeunload', () => ac.abort());\n * ```\n */\nexport function setAbortableInterval(\n  callback: VoidFunction,\n  delayInMs?: number,\n  signal?: AbortSignal,\n) {\n  let timer: number | null = null;\n\n  const handleAbort = () => {\n    if (timer == null) {\n      return;\n    }\n    clearInterval(timer);\n    timer = null;\n  };\n\n  signal?.addEventListener('abort', handleAbort, { once: true });\n\n  timer = setInterval(callback, delayInMs);\n}\n","/**\n * Like `setTimeout`, but if `signal` aborts before the delay fires, the timer is cleared\n * and `callback` is never run. If the callback runs normally, the abort listener is removed.\n *\n * Does nothing special if `signal` is omitted — behaves like a plain timeout.\n *\n * @param callback - Function to run once after `delayInMs` (same as `setTimeout` callback).\n * @param delayInMs - Milliseconds to wait. Passed through to `setTimeout` (browser/Node semantics apply).\n * @param signal - When aborted, clears the pending timeout so `callback` is not invoked.\n *\n * @example\n * ```ts\n * const controller = new AbortController();\n * setAbortableTimeout(() => console.log('done'), 500, controller.signal);\n * // later: controller.abort(); // timeout cancelled, log never runs\n * ```\n *\n * @example\n * Zero-delay scheduling that can still be cancelled before the macrotask runs:\n * ```ts\n * const ac = new AbortController();\n * setAbortableTimeout(() => startIntro(), 0, ac.signal);\n * // e.g. on teardown: ac.abort();\n * ```\n */\nexport function setAbortableTimeout(\n  callback: VoidFunction,\n  delayInMs?: number,\n  signal?: AbortSignal,\n) {\n  let internalTimer: number | null = null;\n\n  const handleAbort = () => {\n    if (internalTimer == null) {\n      return;\n    }\n    clearTimeout(internalTimer);\n    internalTimer = null;\n  };\n\n  signal?.addEventListener('abort', handleAbort, { once: true });\n\n  internalTimer = setTimeout(() => {\n    signal?.removeEventListener('abort', handleAbort);\n    callback();\n  }, delayInMs);\n}\n","/**\n * Returns a promise that resolves after `time` milliseconds.\n *\n * When `signal` is passed and becomes aborted before the delay elapses, the promise\n * rejects with `signal.reason` (same as native `fetch` / `AbortController` usage).\n * The timeout is cleared on abort so no resolve happens after cancellation.\n *\n * @param time - Delay in milliseconds. Defaults to `0` (next macrotask tick, same idea as `setTimeout(0)`).\n * @param signal - Optional `AbortSignal` to cancel the wait early.\n * @returns A promise that resolves with `void` when the delay completes, or rejects if aborted.\n *\n * @example\n * Basic pause in an async function:\n * ```ts\n * await sleep(250);\n * console.log('after 250ms');\n * ```\n *\n * @example\n * Cancellable delay tied to component unmount or user action:\n * ```ts\n * const ac = new AbortController();\n * try {\n *   await sleep(5000, ac.signal);\n * } catch (e) {\n *   // aborted — e is signal.reason\n * }\n * ac.abort('user cancelled');\n * ```\n */\nexport const sleep = (time: number = 0, signal?: AbortSignal) =>\n  new Promise<void>((resolve, reject) => {\n    if (signal) {\n      const abortListener = () => {\n        clearTimeout(timerId);\n        reject(signal?.reason);\n      };\n      const timerId = setTimeout(() => {\n        signal.removeEventListener('abort', abortListener);\n        resolve();\n      }, time);\n      signal.addEventListener('abort', abortListener, { once: true });\n    } else {\n      setTimeout(resolve, time);\n    }\n  });\n","import { sleep } from './sleep.js';\n\n/**\n * Creates a promise that resolves after the specified number of milliseconds.\n *\n * @deprecated Use `sleep` instead.\n * @param ms Delay in milliseconds.\n * @returns Promise\n */\nexport const waitAsync = (ms = 1000) => sleep(ms);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsDA,gBAAuB,cACrB,SACA,GAAG,QACH;CACA,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACvC,MAAM,QAAQ;EAEd,IAAI,IAAI,OAAO,QAAQ;GACrB,MAAM,QAAQ,OAAO;GACrB,OAAO,qBAAqB,KAAK;EACnC;CACF;AACF;;;;;;;;AASA,gBAAgB,qBACd,OACuC;CACvC,IAAI,UAAU,QAAQ,UAAU,KAAA,KAAa,UAAU,OACrD;CAGF,IAAI,iBAAiB,SAAS;EAE5B,OAAO,qBAAqB,MADL,KACa;EACpC;CACF;CAEA,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxB,KAAK,MAAM,QAAQ,OACjB,OAAO,qBAAqB,IAAI;EAElC;CACF;CAEA,IACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAAiC,OAAO,mBAC9C,YACF;EACA,WAAW,MAAM,QAAQ,OACvB,OAAO,qBAAqB,IAAI;EAElC;CACF;CAEA,IAAI,OAAO,UAAU,YAAY;EAC/B,OAAO,qBAAqB,MAAM,CAAC;EACnC;CACF;CAEA,MAAM,OAAO,KAAK;AACpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC7EA,IAAa,cACX,cACA,gBACG;CACH,IAAI,aAAa,GAAG;CAEpB,MAAM,YACJ,4BAA4B,WAAW,cAAc,WAAW,CAAC;CAEnE,IAAI,aACF,eAAe,GAAG;MAElB,IAAI;AAER;;;;;;;;;;;;;;;;;;;;;;;;;;AC3BA,SAAgB,qBACd,UACA,WACA,QACA;CACA,IAAI,QAAuB;CAE3B,MAAM,oBAAoB;EACxB,IAAI,SAAS,MACX;EAEF,cAAc,KAAK;EACnB,QAAQ;CACV;CAEA,QAAQ,iBAAiB,SAAS,aAAa,EAAE,MAAM,KAAK,CAAC;CAE7D,QAAQ,YAAY,UAAU,SAAS;AACzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChBA,SAAgB,oBACd,UACA,WACA,QACA;CACA,IAAI,gBAA+B;CAEnC,MAAM,oBAAoB;EACxB,IAAI,iBAAiB,MACnB;EAEF,aAAa,aAAa;EAC1B,gBAAgB;CAClB;CAEA,QAAQ,iBAAiB,SAAS,aAAa,EAAE,MAAM,KAAK,CAAC;CAE7D,gBAAgB,iBAAiB;EAC/B,QAAQ,oBAAoB,SAAS,WAAW;EAChD,SAAS;CACX,GAAG,SAAS;AACd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChBA,IAAa,SAAS,OAAe,GAAG,WACtC,IAAI,SAAe,SAAS,WAAW;CACrC,IAAI,QAAQ;EACV,MAAM,sBAAsB;GAC1B,aAAa,OAAO;GACpB,OAAO,QAAQ,MAAM;EACvB;EACA,MAAM,UAAU,iBAAiB;GAC/B,OAAO,oBAAoB,SAAS,aAAa;GACjD,QAAQ;EACV,GAAG,IAAI;EACP,OAAO,iBAAiB,SAAS,eAAe,EAAE,MAAM,KAAK,CAAC;CAChE,OACE,WAAW,SAAS,IAAI;AAE5B,CAAC;;;;;;;;;;ACpCH,IAAa,aAAa,KAAK,QAAS,MAAM,EAAE"}