@askrjs/askr 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +8 -5
  2. package/dist/chunk-64C7W2AE.js +43 -0
  3. package/dist/chunk-64C7W2AE.js.map +1 -0
  4. package/dist/chunk-6FLMH4EL.js +124 -0
  5. package/dist/chunk-6FLMH4EL.js.map +1 -0
  6. package/dist/{chunk-JHOGWTAW.js → chunk-FJUXFA72.js} +2 -2
  7. package/dist/chunk-FJUXFA72.js.map +1 -0
  8. package/dist/{chunk-H3NSVHA7.js → chunk-VRAEBIJ3.js} +7 -5
  9. package/dist/chunk-VRAEBIJ3.js.map +1 -0
  10. package/dist/chunk-WTFWRSHB.js +98 -0
  11. package/dist/chunk-WTFWRSHB.js.map +1 -0
  12. package/dist/chunk-XHKZGJE3.js +2907 -0
  13. package/dist/chunk-XHKZGJE3.js.map +1 -0
  14. package/dist/chunk-Z5ZSTLYF.js +420 -0
  15. package/dist/chunk-Z5ZSTLYF.js.map +1 -0
  16. package/dist/fx/index.cjs +1193 -0
  17. package/dist/fx/index.cjs.map +1 -0
  18. package/dist/fx/index.d.cts +186 -0
  19. package/dist/fx/index.d.ts +186 -0
  20. package/dist/fx/index.js +418 -0
  21. package/dist/fx/index.js.map +1 -0
  22. package/dist/index.cjs +1369 -2525
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +74 -407
  25. package/dist/index.d.ts +74 -407
  26. package/dist/index.js +63 -803
  27. package/dist/index.js.map +1 -1
  28. package/dist/jsx/jsx-dev-runtime.cjs +1 -1
  29. package/dist/jsx/jsx-dev-runtime.cjs.map +1 -1
  30. package/dist/jsx/jsx-dev-runtime.d.cts +3 -2
  31. package/dist/jsx/jsx-dev-runtime.d.ts +3 -2
  32. package/dist/jsx/jsx-dev-runtime.js +1 -1
  33. package/dist/jsx/jsx-runtime.d.cts +2 -1
  34. package/dist/jsx/jsx-runtime.d.ts +2 -1
  35. package/dist/jsx/jsx-runtime.js +1 -1
  36. package/dist/{types-DLTViI21.d.cts → jsx-AzPM8gMS.d.cts} +6 -21
  37. package/dist/{types-DLTViI21.d.ts → jsx-AzPM8gMS.d.ts} +6 -21
  38. package/dist/{navigate-CZEUXFPM.js → navigate-LUVYHYZZ.js} +5 -4
  39. package/dist/resources/index.cjs +1200 -0
  40. package/dist/resources/index.cjs.map +1 -0
  41. package/dist/resources/index.d.cts +21 -0
  42. package/dist/resources/index.d.ts +21 -0
  43. package/dist/resources/index.js +278 -0
  44. package/dist/resources/index.js.map +1 -0
  45. package/dist/{route-USEXGOBT.js → route-BCND6MPK.js} +4 -3
  46. package/dist/{chunk-2ONGHQ7Z.js → router/index.cjs} +775 -643
  47. package/dist/router/index.cjs.map +1 -0
  48. package/dist/router/index.d.cts +64 -0
  49. package/dist/router/index.d.ts +64 -0
  50. package/dist/router/index.js +49 -0
  51. package/dist/router/index.js.map +1 -0
  52. package/dist/router-DaGtH1Sq.d.cts +36 -0
  53. package/dist/router-DaGtH1Sq.d.ts +36 -0
  54. package/dist/ssr/index.cjs +4059 -0
  55. package/dist/ssr/index.cjs.map +1 -0
  56. package/dist/ssr/index.d.cts +123 -0
  57. package/dist/ssr/index.d.ts +123 -0
  58. package/dist/{chunk-OFW6DFBM.js → ssr/index.js} +202 -252
  59. package/dist/ssr/index.js.map +1 -0
  60. package/dist/types-CZ5sWur5.d.cts +23 -0
  61. package/dist/types-CZHOAiC1.d.ts +23 -0
  62. package/package.json +19 -6
  63. package/src/jsx/types.ts +4 -17
  64. package/dist/chunk-2ONGHQ7Z.js.map +0 -1
  65. package/dist/chunk-H3NSVHA7.js.map +0 -1
  66. package/dist/chunk-JHOGWTAW.js.map +0 -1
  67. package/dist/chunk-OFW6DFBM.js.map +0 -1
  68. package/dist/ssr-QJ5NTQR6.js +0 -28
  69. package/dist/ssr-QJ5NTQR6.js.map +0 -1
  70. /package/dist/{navigate-CZEUXFPM.js.map → navigate-LUVYHYZZ.js.map} +0 -0
  71. /package/dist/{route-USEXGOBT.js.map → route-BCND6MPK.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/fx/index.ts","../../src/fx/timing.ts","../../src/dev/invariant.ts","../../src/dev/logger.ts","../../src/runtime/scheduler.ts","../../src/renderer/utils.ts","../../src/renderer/keyed.ts","../../src/common/jsx.ts","../../src/runtime/dev-namespace.ts","../../src/runtime/fastlane.ts","../../src/runtime/component.ts","../../src/fx/noop.ts","../../src/fx/fx.ts"],"sourcesContent":["/**\n * Standard library — pure helpers for common patterns\n * Zero framework coupling\n */\n\nexport {\n debounce,\n throttle,\n once,\n defer,\n raf,\n idle,\n timeout,\n retry,\n type DebounceOptions,\n type ThrottleOptions,\n type RetryOptions,\n} from './timing';\n\nexport {\n debounceEvent,\n throttleEvent,\n rafEvent,\n scheduleTimeout,\n scheduleIdle,\n scheduleRetry,\n} from './fx';\n","/**\n * Timing utilities — pure helpers for common async patterns\n * No framework coupling. No lifecycle awareness.\n */\n\nexport interface DebounceOptions {\n leading?: boolean;\n trailing?: boolean;\n}\n\nexport interface ThrottleOptions {\n leading?: boolean;\n trailing?: boolean;\n}\n\nexport interface RetryOptions {\n maxAttempts?: number;\n delayMs?: number;\n backoff?: (attemptIndex: number) => number;\n}\n\n/**\n * Debounce — delay execution, coalesce rapid calls\n *\n * Useful for: text input, resize, autosave\n *\n * @param fn Function to debounce\n * @param ms Delay in milliseconds\n * @param options trailing (default true), leading\n * @returns Debounced function with cancel() method\n *\n * @example\n * ```ts\n * const save = debounce((text) => api.save(text), 500);\n * input.addEventListener('input', (e) => save(e.target.value));\n * save.cancel(); // stop any pending execution\n * ```\n */\nexport function debounce<T extends (...args: unknown[]) => unknown>(\n fn: T,\n ms: number,\n options?: DebounceOptions\n): T & { cancel(): void } {\n let timeoutId: NodeJS.Timeout | null = null;\n const { leading = false, trailing = true } = options || {};\n let lastArgs: unknown[] | null = null;\n let lastThis: unknown = null;\n let lastCallTime = 0;\n\n const debounced = function (this: unknown, ...args: unknown[]) {\n const callTime = Date.now();\n lastArgs = args;\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n lastThis = this;\n\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n\n if (leading && callTime - lastCallTime >= ms) {\n fn.apply(this, args);\n lastCallTime = callTime;\n }\n\n if (trailing) {\n timeoutId = setTimeout(() => {\n fn.apply(lastThis, lastArgs!);\n timeoutId = null;\n lastCallTime = Date.now();\n }, ms);\n }\n };\n\n debounced.cancel = () => {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n };\n\n return debounced as T & { cancel(): void };\n}\n\n/**\n * Throttle — rate-limit execution, keep first/last\n *\n * Useful for: scroll, mouse move, high-frequency events\n *\n * @param fn Function to throttle\n * @param ms Minimum interval between calls in milliseconds\n * @param options leading (default true), trailing (default true)\n * @returns Throttled function with cancel() method\n *\n * @example\n * ```ts\n * const handleScroll = throttle(updateUI, 100);\n * window.addEventListener('scroll', handleScroll);\n * handleScroll.cancel();\n * ```\n */\nexport function throttle<T extends (...args: unknown[]) => unknown>(\n fn: T,\n ms: number,\n options?: ThrottleOptions\n): T & { cancel(): void } {\n let lastCallTime = 0;\n let timeoutId: NodeJS.Timeout | null = null;\n const { leading = true, trailing = true } = options || {};\n let lastArgs: unknown[] | null = null;\n let lastThis: unknown = null;\n\n const throttled = function (this: unknown, ...args: unknown[]) {\n const callTime = Date.now();\n lastArgs = args;\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n lastThis = this;\n\n if (leading && callTime - lastCallTime >= ms) {\n fn.apply(this, args);\n lastCallTime = callTime;\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n } else if (!leading && lastCallTime === 0) {\n lastCallTime = callTime;\n }\n\n if (trailing && timeoutId === null) {\n timeoutId = setTimeout(\n () => {\n fn.apply(lastThis, lastArgs!);\n lastCallTime = Date.now();\n timeoutId = null;\n },\n ms - (callTime - lastCallTime)\n );\n }\n };\n\n throttled.cancel = () => {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n };\n\n return throttled as T & { cancel(): void };\n}\n\n/**\n * Once — guard against double execution\n *\n * Useful for: init logic, event safety\n *\n * @param fn Function to call at most once\n * @returns Function that executes fn only on first call\n *\n * @example\n * ```ts\n * const init = once(setup);\n * init(); // runs\n * init(); // does nothing\n * init(); // does nothing\n * ```\n */\nexport function once<T extends (...args: unknown[]) => unknown>(fn: T): T {\n let called = false;\n let result: unknown;\n\n return ((...args: unknown[]) => {\n if (!called) {\n called = true;\n result = fn(...args);\n }\n return result;\n }) as T;\n}\n\n/**\n * Defer — schedule on microtask queue\n *\n * Useful for: run-after-current-stack logic\n * More reliable than setTimeout(..., 0)\n *\n * @param fn Function to defer\n *\n * @example\n * ```ts\n * defer(() => update()); // runs after current stack, before next macrotask\n * ```\n */\nexport function defer(fn: () => void): void {\n Promise.resolve().then(fn);\n}\n\n/**\n * RAF — coalesce multiple updates into single frame\n *\n * Useful for: animation, layout work, render updates\n *\n * @param fn Function to schedule on next animation frame\n * @returns Function that schedules fn on requestAnimationFrame\n *\n * @example\n * ```ts\n * const update = raf(render);\n * update(); // schedules on next frame\n * update(); // same frame, no duplicate\n * ```\n */\nexport function raf<T extends (...args: unknown[]) => unknown>(fn: T): T {\n let frameId: number | null = null;\n let lastArgs: unknown[] | null = null;\n let lastThis: unknown = null;\n\n return function (this: unknown, ...args: unknown[]) {\n lastArgs = args;\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n lastThis = this;\n\n if (frameId === null) {\n frameId = requestAnimationFrame(() => {\n fn.apply(lastThis, lastArgs!);\n frameId = null;\n });\n }\n } as T;\n}\n\n/**\n * Idle — schedule low-priority work\n *\n * Useful for: background prep, non-urgent updates\n * Falls back to setTimeout if requestIdleCallback unavailable\n *\n * @param fn Function to call when idle\n * @param options timeout for fallback\n *\n * @example\n * ```ts\n * idle(() => prefetchData());\n * ```\n */\nexport function idle(fn: () => void, options?: { timeout?: number }): void {\n if (typeof requestIdleCallback !== 'undefined') {\n requestIdleCallback(fn, options ? { timeout: options.timeout } : undefined);\n } else {\n // Fallback: defer to microtask, then use setTimeout\n Promise.resolve().then(() => {\n setTimeout(fn, 0);\n });\n }\n}\n\n/**\n * Timeout — Promise-based delay\n *\n * Useful for: readable async code, waiting between retries\n *\n * @param ms Milliseconds to wait\n * @returns Promise that resolves after delay\n *\n * @example\n * ```ts\n * await timeout(300);\n * console.log('300ms later');\n * ```\n */\nexport function timeout(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Retry — attempt function with backoff\n *\n * Useful for: network calls, transient failures\n *\n * @param fn Async function to retry\n * @param options maxAttempts, delayMs, backoff function\n * @returns Promise with final result or error\n *\n * @example\n * ```ts\n * const data = await retry(() => fetch(url), {\n * maxAttempts: 3,\n * delayMs: 100,\n * });\n * ```\n */\nexport async function retry<T>(\n fn: () => Promise<T>,\n options?: RetryOptions\n): Promise<T> {\n const {\n maxAttempts = 3,\n delayMs = 100,\n backoff = (i: number) => delayMs * Math.pow(2, i),\n } = options || {};\n\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error as Error;\n if (attempt < maxAttempts - 1) {\n const delay = backoff(attempt);\n await timeout(delay);\n }\n }\n }\n\n throw lastError || new Error('Retry failed');\n}\n","/**\n * Invariant assertion utilities for correctness checking\n * Production-safe: invariants are enforced at build-time or with minimal overhead\n *\n * Core principle: fail fast when invariants are violated\n * All functions throw descriptive errors for debugging\n */\n\n/**\n * Assert a condition; throw with context if false\n * @internal\n */\nexport function invariant(\n condition: boolean,\n message: string,\n context?: Record<string, unknown>\n): asserts condition {\n if (!condition) {\n const contextStr = context ? '\\n' + JSON.stringify(context, null, 2) : '';\n throw new Error(`[Askr Invariant] ${message}${contextStr}`);\n }\n}\n\n/**\n * Assert object property exists and has correct type\n * @internal\n */\nexport function assertProperty<T extends object, K extends keyof T>(\n obj: T,\n prop: K,\n expectedType?: string\n): asserts obj is T & Required<Pick<T, K>> {\n invariant(prop in obj, `Object missing required property '${String(prop)}'`, {\n object: obj,\n });\n\n if (expectedType) {\n const actualType = typeof obj[prop];\n invariant(\n actualType === expectedType,\n `Property '${String(prop)}' has type '${actualType}', expected '${expectedType}'`,\n { value: obj[prop], expectedType }\n );\n }\n}\n\n/**\n * Assert a reference is not null/undefined\n * @internal\n */\nexport function assertDefined<T>(\n value: T | null | undefined,\n message: string\n): asserts value is T {\n invariant(value !== null && value !== undefined, message, { value });\n}\n\n/**\n * Assert a task runs exactly once atomically\n * Useful for verifying lifecycle events fire precisely when expected\n * @internal\n */\nexport class Once {\n private called = false;\n private calledAt: number | null = null;\n readonly name: string;\n\n constructor(name: string) {\n this.name = name;\n }\n\n check(): boolean {\n return this.called;\n }\n\n mark(): void {\n invariant(\n !this.called,\n `${this.name} called multiple times (previously at ${this.calledAt}ms)`,\n { now: Date.now() }\n );\n this.called = true;\n this.calledAt = Date.now();\n }\n\n reset(): void {\n this.called = false;\n this.calledAt = null;\n }\n}\n\n/**\n * Assert a value falls in an enumerated set\n * @internal\n */\nexport function assertEnum<T extends readonly unknown[]>(\n value: unknown,\n allowedValues: T,\n fieldName: string\n): asserts value is T[number] {\n invariant(\n allowedValues.includes(value),\n `${fieldName} must be one of [${allowedValues.join(', ')}], got ${JSON.stringify(value)}`,\n { value, allowed: allowedValues }\n );\n}\n\n/**\n * Assert execution context (scheduler, component, etc)\n * @internal\n */\nexport function assertContext(\n actual: unknown,\n expected: unknown,\n contextName: string\n): asserts actual is typeof expected {\n invariant(\n actual === expected,\n `Invalid ${contextName} context. Expected ${expected}, got ${actual}`,\n { expected, actual }\n );\n}\n\n/**\n * Assert scheduling precondition (not reentering, not during render, etc)\n * @internal\n */\nexport function assertSchedulingPrecondition(\n condition: boolean,\n violationMessage: string\n): asserts condition {\n invariant(condition, `[Scheduler Precondition] ${violationMessage}`);\n}\n\n/**\n * Assert state precondition\n * @internal\n */\nexport function assertStatePrecondition(\n condition: boolean,\n violationMessage: string\n): asserts condition {\n invariant(condition, `[State Precondition] ${violationMessage}`);\n}\n\n/**\n * Verify AbortController lifecycle\n * @internal\n */\nexport function assertAbortControllerState(\n signal: AbortSignal,\n expectedAborted: boolean,\n context: string\n): void {\n invariant(\n signal.aborted === expectedAborted,\n `AbortSignal ${expectedAborted ? 'should be' : 'should not be'} aborted in ${context}`,\n { actual: signal.aborted, expected: expectedAborted }\n );\n}\n\n/**\n * Guard: throw if callback is null when it shouldn't be\n * Used for notifyUpdate, event handlers, etc.\n * @internal\n */\nexport function assertCallbackAvailable<\n T extends (...args: unknown[]) => unknown,\n>(callback: T | null | undefined, callbackName: string): asserts callback is T {\n invariant(\n callback !== null && callback !== undefined,\n `${callbackName} callback is required but not available`,\n { callback }\n );\n}\n\n/**\n * Verify evaluation generation prevents stale evaluations\n * @internal\n */\nexport function assertEvaluationGeneration(\n current: number,\n latest: number,\n context: string\n): void {\n invariant(\n current === latest,\n `Stale evaluation generation in ${context}: current ${current}, latest ${latest}`,\n { current, latest }\n );\n}\n\n/**\n * Verify mounted flag state\n * @internal\n */\nexport function assertMountedState(\n mounted: boolean,\n expectedMounted: boolean,\n context: string\n): void {\n invariant(\n mounted === expectedMounted,\n `Invalid mounted state in ${context}: expected ${expectedMounted}, got ${mounted}`,\n { mounted, expected: expectedMounted }\n );\n}\n\n/**\n * Verify no null target when rendering\n * @internal\n */\nexport function assertRenderTarget(\n target: Element | null,\n context: string\n): asserts target is Element {\n invariant(target !== null, `Cannot render in ${context}: target is null`, {\n target,\n });\n}\n","/**\n * Centralized logger interface\n * - Keeps production builds silent for debug/warn/info messages\n * - Ensures consistent behavior across the codebase\n * - Protects against missing `console` in some environments\n */\n\nfunction callConsole(method: string, args: unknown[]): void {\n const c = typeof console !== 'undefined' ? (console as unknown) : undefined;\n if (!c) return;\n const fn = (c as Record<string, unknown>)[method];\n if (typeof fn === 'function') {\n try {\n (fn as (...a: unknown[]) => unknown).apply(console, args as unknown[]);\n } catch {\n // ignore logging errors\n }\n }\n}\n\nexport const logger = {\n debug: (...args: unknown[]) => {\n if (process.env.NODE_ENV === 'production') return;\n callConsole('debug', args);\n },\n\n info: (...args: unknown[]) => {\n if (process.env.NODE_ENV === 'production') return;\n callConsole('info', args);\n },\n\n warn: (...args: unknown[]) => {\n if (process.env.NODE_ENV === 'production') return;\n callConsole('warn', args);\n },\n\n error: (...args: unknown[]) => {\n callConsole('error', args);\n },\n};\n","/**\n * Serialized update scheduler — safer design (no inline execution, explicit flush)\n *\n * Key ideas:\n * - Never execute a task inline from `enqueue`.\n * - `flush()` is explicit and non-reentrant.\n * - `runWithSyncProgress()` allows enqueues temporarily but does not run tasks\n * inline; it runs `fn` and then does an explicit `flush()`.\n * - `waitForFlush()` is race-free with a monotonic `flushVersion`.\n */\n\nimport { assertSchedulingPrecondition, invariant } from '../dev/invariant';\nimport { logger } from '../dev/logger';\n\nconst MAX_FLUSH_DEPTH = 50;\n\ntype Task = () => void;\n\nfunction isBulkCommitActive(): boolean {\n try {\n const fb = (\n globalThis as {\n __ASKR_FASTLANE?: { isBulkCommitActive?: () => boolean };\n }\n ).__ASKR_FASTLANE;\n return typeof fb?.isBulkCommitActive === 'function'\n ? !!fb.isBulkCommitActive()\n : false;\n } catch (e) {\n void e;\n return false;\n }\n}\n\nexport class Scheduler {\n private q: Task[] = [];\n private head = 0;\n\n private running = false;\n private inHandler = false;\n private depth = 0;\n private executionDepth = 0; // for compat with existing diagnostics\n\n // Monotonic flush version increments at end of each flush\n private flushVersion = 0;\n\n // Best-effort microtask kick scheduling\n private kickScheduled = false;\n\n // Escape hatch flag for runWithSyncProgress\n private allowSyncProgress = false;\n\n // Waiters waiting for flushVersion >= target\n private waiters: Array<{\n target: number;\n resolve: () => void;\n reject: (err: unknown) => void;\n timer?: ReturnType<typeof setTimeout>;\n }> = [];\n\n // Keep a lightweight taskCount for compatibility/diagnostics\n private taskCount = 0;\n\n enqueue(task: Task): void {\n assertSchedulingPrecondition(\n typeof task === 'function',\n 'enqueue() requires a function'\n );\n\n // Strict rule: during bulk commit, only allow enqueues if runWithSyncProgress enabled\n if (isBulkCommitActive() && !this.allowSyncProgress) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n '[Scheduler] enqueue() during bulk commit (not allowed)'\n );\n }\n return;\n }\n\n // Enqueue task and account counts\n this.q.push(task);\n this.taskCount++;\n\n // Microtask kick: best-effort, but avoid if we are in handler or running or bulk commit\n if (\n !this.running &&\n !this.kickScheduled &&\n !this.inHandler &&\n !isBulkCommitActive()\n ) {\n this.kickScheduled = true;\n queueMicrotask(() => {\n this.kickScheduled = false;\n if (this.running) return;\n if (isBulkCommitActive()) return;\n try {\n this.flush();\n } catch (err) {\n setTimeout(() => {\n throw err;\n });\n }\n });\n }\n }\n\n flush(): void {\n invariant(\n !this.running,\n '[Scheduler] flush() called while already running'\n );\n\n // Dev-only guard: disallow flush during bulk commit unless allowed\n if (process.env.NODE_ENV !== 'production') {\n if (isBulkCommitActive() && !this.allowSyncProgress) {\n throw new Error(\n '[Scheduler] flush() started during bulk commit (not allowed)'\n );\n }\n }\n\n this.running = true;\n this.depth = 0;\n let fatal: unknown = null;\n\n try {\n while (this.head < this.q.length) {\n this.depth++;\n if (\n process.env.NODE_ENV !== 'production' &&\n this.depth > MAX_FLUSH_DEPTH\n ) {\n throw new Error(\n `[Scheduler] exceeded MAX_FLUSH_DEPTH (${MAX_FLUSH_DEPTH}). Likely infinite update loop.`\n );\n }\n\n const task = this.q[this.head++];\n try {\n this.executionDepth++;\n task();\n this.executionDepth--;\n } catch (err) {\n // ensure executionDepth stays balanced\n if (this.executionDepth > 0) this.executionDepth = 0;\n fatal = err;\n break;\n }\n\n // Account for executed task in taskCount\n if (this.taskCount > 0) this.taskCount--;\n }\n } finally {\n this.running = false;\n this.depth = 0;\n this.executionDepth = 0;\n\n // Compact queue\n if (this.head >= this.q.length) {\n this.q.length = 0;\n this.head = 0;\n } else if (this.head > 0) {\n const remaining = this.q.length - this.head;\n // HOT PATH: compact in-place to avoid slice() allocations (GC tail spikes)\n for (let i = 0; i < remaining; i++) {\n this.q[i] = this.q[this.head + i];\n }\n this.q.length = remaining;\n this.head = 0;\n }\n\n // Advance flush epoch and resolve waiters\n this.flushVersion++;\n this.resolveWaiters();\n }\n\n if (fatal) throw fatal;\n }\n\n runWithSyncProgress<T>(fn: () => T): T {\n const prev = this.allowSyncProgress;\n this.allowSyncProgress = true;\n\n const g = globalThis as {\n queueMicrotask?: (...args: unknown[]) => void;\n setTimeout?: (...args: unknown[]) => unknown;\n };\n const origQueueMicrotask = g.queueMicrotask;\n const origSetTimeout = g.setTimeout;\n\n if (process.env.NODE_ENV !== 'production') {\n g.queueMicrotask = () => {\n throw new Error(\n '[Scheduler] queueMicrotask not allowed during runWithSyncProgress'\n );\n };\n g.setTimeout = () => {\n throw new Error(\n '[Scheduler] setTimeout not allowed during runWithSyncProgress'\n );\n };\n }\n\n // Snapshot flushVersion so we can ensure we always complete an epoch\n const startVersion = this.flushVersion;\n\n try {\n const res = fn();\n\n // Flush deterministically if tasks were enqueued (and we're not already running)\n if (!this.running && this.q.length - this.head > 0) {\n this.flush();\n }\n\n if (process.env.NODE_ENV !== 'production') {\n if (this.q.length - this.head > 0) {\n throw new Error(\n '[Scheduler] tasks remain after runWithSyncProgress flush'\n );\n }\n }\n\n return res;\n } finally {\n // Restore guarded globals\n if (process.env.NODE_ENV !== 'production') {\n g.queueMicrotask = origQueueMicrotask;\n g.setTimeout = origSetTimeout;\n }\n\n // If no flush happened during the protected window, complete an epoch so\n // observers (tests) see progress even when fast-lane did synchronous work\n // without enqueuing tasks.\n try {\n if (this.flushVersion === startVersion) {\n this.flushVersion++;\n this.resolveWaiters();\n }\n } catch (e) {\n void e;\n }\n\n this.allowSyncProgress = prev;\n }\n }\n\n waitForFlush(targetVersion?: number, timeoutMs = 2000): Promise<void> {\n const target =\n typeof targetVersion === 'number' ? targetVersion : this.flushVersion + 1;\n if (this.flushVersion >= target) return Promise.resolve();\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n const ns =\n (\n globalThis as unknown as Record<string, unknown> & {\n __ASKR__?: Record<string, unknown>;\n }\n ).__ASKR__ || {};\n const diag = {\n flushVersion: this.flushVersion,\n queueLen: this.q.length - this.head,\n running: this.running,\n inHandler: this.inHandler,\n bulk: isBulkCommitActive(),\n namespace: ns,\n };\n reject(\n new Error(\n `waitForFlush timeout ${timeoutMs}ms: ${JSON.stringify(diag)}`\n )\n );\n }, timeoutMs);\n\n this.waiters.push({ target, resolve, reject, timer });\n });\n }\n\n getState() {\n // Provide the compatibility shape expected by diagnostics/tests\n return {\n queueLength: this.q.length - this.head,\n running: this.running,\n depth: this.depth,\n executionDepth: this.executionDepth,\n taskCount: this.taskCount,\n flushVersion: this.flushVersion,\n // New fields for optional inspection\n inHandler: this.inHandler,\n allowSyncProgress: this.allowSyncProgress,\n };\n }\n\n setInHandler(v: boolean) {\n this.inHandler = v;\n }\n\n isInHandler(): boolean {\n return this.inHandler;\n }\n\n isExecuting(): boolean {\n return this.running || this.executionDepth > 0;\n }\n\n // Clear pending synchronous tasks (used by fastlane enter/exit)\n clearPendingSyncTasks(): number {\n const remaining = this.q.length - this.head;\n if (remaining <= 0) return 0;\n\n if (this.running) {\n this.q.length = this.head;\n this.taskCount = Math.max(0, this.taskCount - remaining);\n queueMicrotask(() => {\n try {\n this.flushVersion++;\n this.resolveWaiters();\n } catch (e) {\n void e;\n }\n });\n return remaining;\n }\n\n this.q.length = 0;\n this.head = 0;\n this.taskCount = Math.max(0, this.taskCount - remaining);\n this.flushVersion++;\n this.resolveWaiters();\n return remaining;\n }\n\n private resolveWaiters() {\n if (this.waiters.length === 0) return;\n const ready: Array<() => void> = [];\n const remaining: typeof this.waiters = [];\n\n for (const w of this.waiters) {\n if (this.flushVersion >= w.target) {\n if (w.timer) clearTimeout(w.timer);\n ready.push(w.resolve);\n } else {\n remaining.push(w);\n }\n }\n\n this.waiters = remaining;\n for (const r of ready) r();\n }\n}\n\nexport const globalScheduler = new Scheduler();\n\nexport function isSchedulerExecuting(): boolean {\n return globalScheduler.isExecuting();\n}\n\nexport function scheduleEventHandler(handler: EventListener): EventListener {\n return (event: Event) => {\n globalScheduler.setInHandler(true);\n try {\n handler.call(null, event);\n } catch (error) {\n logger.error('[Askr] Event handler error:', error);\n } finally {\n globalScheduler.setInHandler(false);\n // If the handler enqueued tasks while we disallowed microtask kicks,\n // ensure we schedule a microtask to flush them now that the handler\n // has completed. This avoids tests timing out waiting for flush.\n const state = globalScheduler.getState();\n if ((state.queueLength ?? 0) > 0 && !state.running) {\n queueMicrotask(() => {\n try {\n if (!globalScheduler.isExecuting()) globalScheduler.flush();\n } catch (err) {\n setTimeout(() => {\n throw err;\n });\n }\n });\n }\n }\n };\n}\n","/**\n * Shared utilities for the renderer module.\n * Consolidates common patterns to reduce code duplication.\n */\n\nimport { globalScheduler } from '../runtime/scheduler';\nimport { logger } from '../dev/logger';\nimport { __ASKR_set, __ASKR_incCounter } from './diag';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ListenerEntry {\n handler: EventListener;\n original: EventListener;\n options?: boolean | AddEventListenerOptions;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Event Handler Utilities\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Parse an event prop name (e.g., 'onClick') to its DOM event name (e.g., 'click')\n */\nexport function parseEventName(propName: string): string | null {\n if (!propName.startsWith('on') || propName.length <= 2) return null;\n return (\n propName.slice(2).charAt(0).toLowerCase() + propName.slice(3).toLowerCase()\n );\n}\n\n/**\n * Get default event listener options for passive events\n */\nexport function getPassiveOptions(\n eventName: string\n): AddEventListenerOptions | undefined {\n if (\n eventName === 'wheel' ||\n eventName === 'scroll' ||\n eventName.startsWith('touch')\n ) {\n return { passive: true };\n }\n return undefined;\n}\n\n/**\n * Create a wrapped event handler that integrates with the scheduler\n */\nexport function createWrappedHandler(\n handler: EventListener,\n flushAfter = false\n): EventListener {\n return (event: Event) => {\n globalScheduler.setInHandler(true);\n try {\n handler(event);\n } catch (error) {\n logger.error('[Askr] Event handler error:', error);\n } finally {\n globalScheduler.setInHandler(false);\n if (flushAfter) {\n // If the handler enqueued tasks while we disallowed microtask kicks,\n // ensure we schedule a microtask to flush them\n const state = globalScheduler.getState();\n if ((state.queueLength ?? 0) > 0 && !state.running) {\n queueMicrotask(() => {\n try {\n if (!globalScheduler.isExecuting()) globalScheduler.flush();\n } catch (err) {\n queueMicrotask(() => {\n throw err;\n });\n }\n });\n }\n }\n }\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Prop/Attribute Utilities\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Props that should be skipped during attribute processing */\nexport function isSkippedProp(key: string): boolean {\n return key === 'children' || key === 'key';\n}\n\n/** Check if prop should be ignored for prop-change detection */\nexport function isIgnoredForPropChanges(key: string): boolean {\n if (key === 'children' || key === 'key') return true;\n if (key.startsWith('on') && key.length > 2) return true;\n if (key.startsWith('data-')) return true;\n return false;\n}\n\n/**\n * Check if an element's current attribute value differs from vnode value\n */\nexport function hasPropChanged(\n el: Element,\n key: string,\n value: unknown\n): boolean {\n try {\n if (key === 'class' || key === 'className') {\n return el.className !== String(value);\n }\n if (key === 'value' || key === 'checked') {\n return (el as HTMLElement & Record<string, unknown>)[key] !== value;\n }\n const attr = el.getAttribute(key);\n if (value === undefined || value === null || value === false) {\n return attr !== null;\n }\n return String(value) !== attr;\n } catch {\n return true;\n }\n}\n\n/**\n * Check if a vnode has non-trivial props (excluding events and data-*)\n */\nexport function hasNonTrivialProps(props: Record<string, unknown>): boolean {\n for (const k of Object.keys(props)) {\n if (isIgnoredForPropChanges(k)) continue;\n return true;\n }\n return false;\n}\n\n/**\n * Check for prop changes between vnode and existing element\n */\nexport function checkPropChanges(\n el: Element,\n props: Record<string, unknown>\n): boolean {\n for (const k of Object.keys(props)) {\n if (isIgnoredForPropChanges(k)) continue;\n if (hasPropChanged(el, k, props[k])) {\n return true;\n }\n }\n return false;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Key Utilities\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Extract key from a vnode object\n */\nexport function extractKey(vnode: unknown): string | number | undefined {\n if (typeof vnode !== 'object' || vnode === null) return undefined;\n const obj = vnode as Record<string, unknown>;\n const rawKey =\n obj.key ?? (obj.props as Record<string, unknown> | undefined)?.key;\n if (rawKey === undefined) return undefined;\n return typeof rawKey === 'symbol'\n ? String(rawKey)\n : (rawKey as string | number);\n}\n\n/**\n * Build a key map from element's children\n */\nexport function buildKeyMapFromChildren(\n parent: Element\n): Map<string | number, Element> {\n const map = new Map<string | number, Element>();\n for (let ch = parent.firstElementChild; ch; ch = ch.nextElementSibling) {\n const k = ch.getAttribute('data-key');\n if (k !== null) {\n map.set(k, ch);\n const n = Number(k);\n if (!Number.isNaN(n)) map.set(n, ch);\n }\n }\n return map;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Diagnostic Utilities\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Record DOM replace operation for diagnostics\n */\nexport function recordDOMReplace(source: string): void {\n try {\n __ASKR_incCounter('__DOM_REPLACE_COUNT');\n __ASKR_set(`__LAST_DOM_REPLACE_STACK_${source}`, new Error().stack);\n } catch {\n // ignore\n }\n}\n\n/**\n * Record fast-path stats for diagnostics\n */\nexport function recordFastPathStats(\n stats: Record<string, unknown>,\n counterName?: string\n): void {\n try {\n __ASKR_set('__LAST_FASTPATH_STATS', stats);\n __ASKR_set('__LAST_FASTPATH_COMMIT_COUNT', 1);\n if (counterName) {\n __ASKR_incCounter(counterName);\n }\n } catch {\n // ignore\n }\n}\n\n/**\n * Conditionally log debug info for fast-path operations\n */\nexport function logFastPathDebug(\n message: string,\n indexOrData?: number | unknown,\n data?: unknown\n): void {\n if (\n process.env.ASKR_FASTPATH_DEBUG === '1' ||\n process.env.ASKR_FASTPATH_DEBUG === 'true'\n ) {\n if (data !== undefined) {\n logger.warn(`[Askr][FASTPATH] ${message}`, indexOrData, data);\n } else if (indexOrData !== undefined) {\n logger.warn(`[Askr][FASTPATH] ${message}`, indexOrData);\n } else {\n logger.warn(`[Askr][FASTPATH] ${message}`);\n }\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Performance Utilities\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Get current high-resolution timestamp\n */\nexport function now(): number {\n return typeof performance !== 'undefined' && performance.now\n ? performance.now()\n : Date.now();\n}\n","import type { VNode } from './types';\nimport {\n extractKey,\n buildKeyMapFromChildren,\n isIgnoredForPropChanges,\n hasPropChanged,\n hasNonTrivialProps,\n} from './utils';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Key Map Registry\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport const keyedElements = new WeakMap<\n Element,\n Map<string | number, Element>\n>();\n\n/**\n * Retrieve existing keyed map for a parent element (runtime use)\n */\nexport function getKeyMapForElement(el: Element) {\n return keyedElements.get(el);\n}\n\n/**\n * Populate a keyed map for an element by scanning its immediate children\n * for `data-key` attributes. Proactive initialization for runtime layers.\n */\nexport function populateKeyMapForElement(parent: Element): void {\n try {\n if (keyedElements.has(parent)) return;\n\n let domMap = buildKeyMapFromChildren(parent);\n\n // Fallback: map by textContent when keys are not materialized as attrs\n if (domMap.size === 0) {\n domMap = new Map();\n const children = Array.from(parent.children);\n for (const ch of children) {\n const text = (ch.textContent || '').trim();\n if (text) {\n domMap.set(text, ch);\n const n = Number(text);\n if (!Number.isNaN(n)) domMap.set(n, ch);\n }\n }\n }\n\n if (domMap.size > 0) keyedElements.set(parent, domMap);\n } catch {\n // ignore\n }\n}\n\n// Track which parents had the reconciler record fast-path stats during the\n// current evaluation, so we can preserve diagnostics across additional\n// reconciliations within the same render pass without leaking between runs.\nexport const _reconcilerRecordedParents = new WeakSet<Element>();\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Fast-Path Eligibility\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface KeyedVnode {\n key: string | number;\n vnode: VNode;\n}\n\n/**\n * Extract keyed vnodes from children array\n */\nfunction extractKeyedVnodes(newChildren: VNode[]): KeyedVnode[] {\n const result: KeyedVnode[] = [];\n for (const child of newChildren) {\n const key = extractKey(child);\n if (key !== undefined) {\n result.push({ key, vnode: child });\n }\n }\n return result;\n}\n\n/**\n * Compute LIS (Longest Increasing Subsequence) length for positions\n */\nfunction computeLISLength(positions: number[]): number {\n const tails: number[] = [];\n for (const pos of positions) {\n if (pos === -1) continue;\n let lo = 0;\n let hi = tails.length;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (tails[mid] < pos) lo = mid + 1;\n else hi = mid;\n }\n if (lo === tails.length) tails.push(pos);\n else tails[lo] = pos;\n }\n return tails.length;\n}\n\n/**\n * Check if any vnode has non-trivial props\n */\nfunction checkVnodesHaveProps(keyedVnodes: KeyedVnode[]): boolean {\n for (const { vnode } of keyedVnodes) {\n if (typeof vnode !== 'object' || vnode === null) continue;\n const vnodeObj = vnode as unknown as { props?: Record<string, unknown> };\n if (vnodeObj.props && hasNonTrivialProps(vnodeObj.props)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Check for prop changes between vnodes and existing elements\n */\nfunction checkVnodePropChanges(\n keyedVnodes: KeyedVnode[],\n oldKeyMap: Map<string | number, Element> | undefined\n): boolean {\n for (const { key, vnode } of keyedVnodes) {\n const el = oldKeyMap?.get(key);\n if (!el || typeof vnode !== 'object' || vnode === null) continue;\n const vnodeObj = vnode as unknown as { props?: Record<string, unknown> };\n const props = vnodeObj.props || {};\n for (const k of Object.keys(props)) {\n if (isIgnoredForPropChanges(k)) continue;\n if (hasPropChanged(el, k, props[k])) {\n return true;\n }\n }\n }\n return false;\n}\n\n/**\n * Determine if keyed reorder fast-path should be used\n */\nexport function isKeyedReorderFastPathEligible(\n parent: Element,\n newChildren: VNode[],\n oldKeyMap: Map<string | number, Element> | undefined\n) {\n const keyedVnodes = extractKeyedVnodes(newChildren);\n const totalKeyed = keyedVnodes.length;\n const newKeyOrder = keyedVnodes.map((kv) => kv.key);\n const oldKeyOrder = oldKeyMap ? Array.from(oldKeyMap.keys()) : [];\n\n // Count moves needed\n let moveCount = 0;\n for (let i = 0; i < newKeyOrder.length; i++) {\n const k = newKeyOrder[i];\n if (i >= oldKeyOrder.length || oldKeyOrder[i] !== k || !oldKeyMap?.has(k)) {\n moveCount++;\n }\n }\n\n // Check move threshold triggers\n const FAST_MOVE_THRESHOLD_ABS = 64;\n const FAST_MOVE_THRESHOLD_REL = 0.1;\n const cheapMoveTrigger =\n totalKeyed >= 128 &&\n oldKeyOrder.length > 0 &&\n moveCount >\n Math.max(\n FAST_MOVE_THRESHOLD_ABS,\n Math.floor(totalKeyed * FAST_MOVE_THRESHOLD_REL)\n );\n\n // Compute LIS trigger for large lists\n let lisTrigger = false;\n let lisLen = 0;\n if (totalKeyed >= 128) {\n const parentChildren = Array.from(parent.children);\n const positions = keyedVnodes.map(({ key }) => {\n const el = oldKeyMap?.get(key);\n return el?.parentElement === parent ? parentChildren.indexOf(el) : -1;\n });\n lisLen = computeLISLength(positions);\n lisTrigger = lisLen < Math.floor(totalKeyed * 0.5);\n }\n\n // Check for props that would prevent fast-path\n const hasPropsPresent = checkVnodesHaveProps(keyedVnodes);\n const hasPropChanges = checkVnodePropChanges(keyedVnodes, oldKeyMap);\n\n const useFastPath =\n (cheapMoveTrigger || lisTrigger) && !hasPropChanges && !hasPropsPresent;\n\n return {\n useFastPath,\n totalKeyed,\n moveCount,\n lisLen,\n hasPropChanges,\n } as const;\n}\n","/**\n * Common call contracts: JSX element shape\n */\n\nimport type { Props } from './props';\n\nexport const ELEMENT_TYPE = Symbol.for('askr.element');\nexport const Fragment = Symbol.for('askr.fragment');\n\nexport interface JSXElement {\n /** Internal element marker (optional for plain vnode objects) */\n $$typeof?: symbol;\n\n /** Element type: string, component, Fragment, etc */\n type: unknown;\n\n /** Props bag */\n props: Props;\n\n /** Optional key (normalized by runtime) */\n key?: string | number | null;\n}\n","/**\n * Dev-only namespace helpers for diagnostics\n *\n * Centralizes the repetitive globalThis.__ASKR__ access pattern\n * used throughout runtime for dev-mode diagnostics.\n */\n\ntype DevNamespace = Record<string, unknown>;\n\n/**\n * Get or create the __ASKR__ dev namespace on globalThis.\n * Returns empty object in production to avoid allocations.\n */\nexport function getDevNamespace(): DevNamespace {\n if (process.env.NODE_ENV === 'production') return {};\n try {\n const g = globalThis as unknown as Record<string, DevNamespace>;\n if (!g.__ASKR__) g.__ASKR__ = {};\n return g.__ASKR__;\n } catch {\n return {};\n }\n}\n\n/**\n * Set a value in the dev namespace (no-op in production).\n */\nexport function setDevValue(key: string, value: unknown): void {\n if (process.env.NODE_ENV === 'production') return;\n try {\n getDevNamespace()[key] = value;\n } catch {\n // ignore\n }\n}\n\n/**\n * Get a value from the dev namespace (returns undefined in production).\n */\nexport function getDevValue<T>(key: string): T | undefined {\n if (process.env.NODE_ENV === 'production') return undefined;\n try {\n return getDevNamespace()[key] as T | undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Delete a value from the dev namespace (no-op in production).\n */\nexport function deleteDevValue(key: string): void {\n if (process.env.NODE_ENV === 'production') return;\n try {\n delete getDevNamespace()[key];\n } catch {\n // ignore\n }\n}\n","import { globalScheduler } from './scheduler';\nimport { logger } from '../dev/logger';\nimport type { ComponentInstance } from './component';\nimport {\n getKeyMapForElement,\n isKeyedReorderFastPathEligible,\n populateKeyMapForElement,\n} from '../renderer/keyed';\nimport { Fragment } from '../common/jsx';\nimport { setDevValue, getDevValue } from './dev-namespace';\n\nlet _bulkCommitActive = false;\nlet _appliedParents: WeakSet<Element> | null = null;\n\nexport function enterBulkCommit(): void {\n _bulkCommitActive = true;\n // Initialize registry of parents that had fast-path applied during this bulk commit\n _appliedParents = new WeakSet<Element>();\n\n // Clear any previously scheduled synchronous scheduler tasks so they don't\n // retrigger evaluations during the committed fast-path.\n try {\n const cleared = globalScheduler.clearPendingSyncTasks?.() ?? 0;\n setDevValue('__ASKR_FASTLANE_CLEARED_TASKS', cleared);\n } catch (err) {\n if (process.env.NODE_ENV !== 'production') throw err;\n }\n}\n\nexport function exitBulkCommit(): void {\n _bulkCommitActive = false;\n // Clear registry to avoid leaking across commits\n _appliedParents = null;\n}\n\nexport function isBulkCommitActive(): boolean {\n return _bulkCommitActive;\n}\n\n// Mark that a fast-path was applied on a parent element during the active\n// bulk commit. No-op if there is no active bulk commit.\nexport function markFastPathApplied(parent: Element): void {\n if (!_appliedParents) return;\n try {\n _appliedParents.add(parent);\n } catch (e) {\n void e;\n }\n}\n\nexport function isFastPathApplied(parent: Element): boolean {\n return !!(_appliedParents && _appliedParents.has(parent));\n}\n\nfunction finalizeReadSubscriptions(instance: ComponentInstance): void {\n const newSet = instance._pendingReadStates ?? new Set();\n const oldSet = instance._lastReadStates ?? new Set();\n const token = instance._currentRenderToken;\n\n if (token === undefined) return;\n\n // Remove subscriptions for states that were read previously but not in this render\n for (const s of oldSet) {\n if (!newSet.has(s)) {\n const readers = (s as { _readers?: Map<ComponentInstance, number> })\n ._readers;\n if (readers) readers.delete(instance);\n }\n }\n\n // Commit token becomes the authoritative token for this instance's last render\n instance.lastRenderToken = token;\n\n // Record subscriptions for states read during this render\n for (const s of newSet) {\n let readers = (s as { _readers?: Map<ComponentInstance, number> })._readers;\n if (!readers) {\n readers = new Map();\n (s as { _readers?: Map<ComponentInstance, number> })._readers = readers;\n }\n readers.set(instance, instance.lastRenderToken ?? 0);\n }\n\n instance._lastReadStates = newSet;\n instance._pendingReadStates = new Set();\n instance._currentRenderToken = undefined;\n}\n\n/**\n * Attempt to execute a runtime fast-lane for a single component's synchronous\n * render result. Returns true if the fast-lane was used and commit was done.\n *\n * Preconditions (checked conservatively):\n * - The render result is an intrinsic element root with keyed children\n * - The renderer's fast-path heuristics indicate to use the fast-path\n * - No mount operations are pending on the component instance\n * - No child vnodes are component functions (avoid async/component mounts)\n */\n\n// Helper to unwrap Fragment vnodes to get the first intrinsic element child\nfunction unwrapFragmentForFastPath(vnode: unknown): unknown {\n if (!vnode || typeof vnode !== 'object' || !('type' in vnode)) return vnode;\n const v = vnode as {\n type: unknown;\n children?: unknown;\n props?: { children?: unknown };\n };\n // Check if it's a Fragment\n if (\n typeof v.type === 'symbol' &&\n (v.type === Fragment || String(v.type) === 'Symbol(askr.fragment)')\n ) {\n const children = v.children || v.props?.children;\n if (Array.isArray(children) && children.length > 0) {\n // Return the first child that's an intrinsic element\n for (const child of children) {\n if (child && typeof child === 'object' && 'type' in child) {\n const c = child as { type: unknown };\n if (typeof c.type === 'string') {\n return child;\n }\n }\n }\n }\n }\n return vnode;\n}\n\nexport function classifyUpdate(instance: ComponentInstance, result: unknown) {\n // Returns a classification describing whether this update is eligible for\n // the reorder-only fast-lane. The classifier mirrors renderer-level\n // heuristics and performs runtime-level checks (mounts, effects, component\n // children) that the renderer cannot reason about.\n\n // Unwrap Fragment to get the actual element vnode for classification\n const unwrappedResult = unwrapFragmentForFastPath(result);\n\n if (\n !unwrappedResult ||\n typeof unwrappedResult !== 'object' ||\n !('type' in unwrappedResult)\n )\n return { useFastPath: false, reason: 'not-vnode' };\n\n const vnode = unwrappedResult as {\n type: unknown;\n children?: unknown;\n props?: { children?: unknown };\n };\n if (vnode == null || typeof vnode.type !== 'string')\n return { useFastPath: false, reason: 'not-intrinsic' };\n\n const parent = instance.target;\n if (!parent) return { useFastPath: false, reason: 'no-root' };\n\n const firstChild = parent.children[0] as Element | undefined;\n if (!firstChild) return { useFastPath: false, reason: 'no-first-child' };\n if (firstChild.tagName.toLowerCase() !== String(vnode.type).toLowerCase())\n return { useFastPath: false, reason: 'root-tag-mismatch' };\n\n const children = vnode.children || vnode.props?.children;\n if (!Array.isArray(children))\n return { useFastPath: false, reason: 'no-children-array' };\n\n // Avoid component child vnodes (they may mount/unmount or trigger async)\n for (const c of children) {\n if (\n typeof c === 'object' &&\n c !== null &&\n 'type' in c &&\n typeof (c as { type?: unknown }).type === 'function'\n ) {\n return { useFastPath: false, reason: 'component-child-present' };\n }\n }\n\n if (instance.mountOperations.length > 0)\n return { useFastPath: false, reason: 'pending-mounts' };\n\n // Ask renderer for keyed reorder eligibility (prop differences & heuristics)\n // Ensure a keyed map is available for the first child by populating it proactively.\n try {\n populateKeyMapForElement(firstChild);\n } catch {\n // ignore\n }\n\n const oldKeyMap = getKeyMapForElement(firstChild);\n const decision = isKeyedReorderFastPathEligible(\n firstChild,\n children,\n oldKeyMap\n );\n\n if (!decision.useFastPath || decision.totalKeyed < 128)\n return { ...decision, useFastPath: false, reason: 'renderer-declined' };\n\n return { ...decision, useFastPath: true } as const;\n}\n\nexport function commitReorderOnly(\n instance: ComponentInstance,\n result: unknown\n): boolean {\n // Performs the minimal, synchronous reorder-only commit.\n const evaluate = (\n globalThis as {\n __ASKR_RENDERER?: {\n evaluate?: (node: unknown, target: Element | null) => void;\n };\n }\n ).__ASKR_RENDERER?.evaluate;\n\n if (typeof evaluate !== 'function') {\n logger.warn(\n '[Tempo][FASTPATH][DEV] renderer.evaluate not available; declining fast-lane'\n );\n return false;\n }\n\n const schedBefore =\n process.env.NODE_ENV !== 'production' ? globalScheduler.getState() : null;\n\n enterBulkCommit();\n\n try {\n globalScheduler.runWithSyncProgress(() => {\n evaluate(result, instance.target);\n\n // Finalize runtime bookkeeping (read subscriptions / tokens)\n try {\n finalizeReadSubscriptions(instance);\n } catch (e) {\n if (process.env.NODE_ENV !== 'production') throw e;\n }\n });\n\n // Clear any synchronous tasks scheduled during the commit\n const clearedAfter = globalScheduler.clearPendingSyncTasks?.() ?? 0;\n setDevValue('__FASTLANE_CLEARED_AFTER', clearedAfter);\n\n // Dev-only invariant checks\n if (process.env.NODE_ENV !== 'production') {\n validateFastLaneInvariants(instance, schedBefore);\n }\n\n return true;\n } finally {\n exitBulkCommit();\n }\n // Dev-only: verify bulk commit flag was properly cleared (after finally to avoid no-unsafe-finally)\n if (process.env.NODE_ENV !== 'production') {\n if (isBulkCommitActive()) {\n throw new Error(\n 'Fast-lane invariant violated: bulk commit flag still set after commit'\n );\n }\n }\n}\n\n/**\n * Validates fast-lane invariants in dev mode.\n * Extracted to reduce complexity in commitReorderOnly.\n */\nfunction validateFastLaneInvariants(\n instance: ComponentInstance,\n schedBefore: ReturnType<typeof globalScheduler.getState> | null\n): void {\n const commitCount = getDevValue<number>('__LAST_FASTPATH_COMMIT_COUNT') ?? 0;\n const invariants = {\n commitCount,\n mountOps: instance.mountOperations.length,\n cleanupFns: instance.cleanupFns.length,\n };\n setDevValue('__LAST_FASTLANE_INVARIANTS', invariants);\n\n if (commitCount !== 1) {\n console.error(\n '[FASTLANE][INV] commitCount',\n commitCount,\n 'diag',\n (globalThis as Record<string, unknown>).__ASKR_DIAG\n );\n throw new Error(\n 'Fast-lane invariant violated: expected exactly one DOM commit during reorder-only commit'\n );\n }\n\n if (invariants.mountOps > 0) {\n throw new Error(\n 'Fast-lane invariant violated: mount operations were registered during bulk commit'\n );\n }\n\n if (invariants.cleanupFns > 0) {\n throw new Error(\n 'Fast-lane invariant violated: cleanup functions were added during bulk commit'\n );\n }\n\n const schedAfter = globalScheduler.getState();\n if (\n schedBefore &&\n schedAfter &&\n schedAfter.taskCount > schedBefore.taskCount\n ) {\n console.error(\n '[FASTLANE] schedBefore, schedAfter',\n schedBefore,\n schedAfter\n );\n console.error('[FASTLANE] enqueue logs', getDevValue('__ENQUEUE_LOGS'));\n throw new Error(\n 'Fast-lane invariant violated: scheduler enqueued leftover work during bulk commit'\n );\n }\n\n // Final quiescence assertion\n let finalState = globalScheduler.getState();\n const executing = globalScheduler.isExecuting();\n let outstandingAfter = Math.max(\n 0,\n finalState.taskCount - (executing ? 1 : 0)\n );\n\n if (outstandingAfter !== 0) {\n // Attempt to clear newly enqueued synchronous tasks\n let attempts = 0;\n while (attempts < 5) {\n const cleared = globalScheduler.clearPendingSyncTasks?.() ?? 0;\n if (cleared === 0) break;\n attempts++;\n }\n finalState = globalScheduler.getState();\n outstandingAfter = Math.max(\n 0,\n finalState.taskCount - (globalScheduler.isExecuting() ? 1 : 0)\n );\n if (outstandingAfter !== 0) {\n console.error(\n '[FASTLANE] Post-commit enqueue logs:',\n getDevValue('__ENQUEUE_LOGS')\n );\n console.error(\n '[FASTLANE] Cleared counts:',\n getDevValue('__FASTLANE_CLEARED_TASKS'),\n getDevValue('__FASTLANE_CLEARED_AFTER')\n );\n throw new Error(\n `Fast-lane invariant violated: scheduler has ${finalState.taskCount} pending task(s) after commit`\n );\n }\n }\n}\n\nexport function tryRuntimeFastLaneSync(\n instance: ComponentInstance,\n result: unknown\n): boolean {\n const cls = classifyUpdate(instance, result);\n if (!cls.useFastPath) {\n // Clear stale fast-path diagnostics\n setDevValue('__LAST_FASTPATH_STATS', undefined);\n setDevValue('__LAST_FASTPATH_COMMIT_COUNT', 0);\n return false;\n }\n\n try {\n return commitReorderOnly(instance, result);\n } catch (err) {\n // Surface dev-only invariant failures, otherwise decline silently\n if (process.env.NODE_ENV !== 'production') throw err;\n return false;\n }\n}\n\n// Expose fastlane bridge on globalThis for environments/tests\nif (typeof globalThis !== 'undefined') {\n (globalThis as Record<string, unknown>).__ASKR_FASTLANE = {\n isBulkCommitActive,\n enterBulkCommit,\n exitBulkCommit,\n tryRuntimeFastLaneSync,\n markFastPathApplied,\n isFastPathApplied,\n };\n}\n","/**\n * Component instance lifecycle management\n * Internal only — users never see this\n */\n\nimport { type State } from './state';\nimport { globalScheduler } from './scheduler';\nimport type { Props } from '../common/props';\nimport type { ComponentFunction } from '../common/component';\nimport {\n // withContext is the sole primitive for context restoration\n withContext,\n type ContextFrame,\n} from './context';\nimport { logger } from '../dev/logger';\nimport { __ASKR_incCounter, __ASKR_set } from '../renderer/diag';\n\nexport type { ComponentFunction } from '../common/component';\n\nexport interface ComponentInstance {\n id: string;\n fn: ComponentFunction;\n props: Props;\n target: Element | null;\n mounted: boolean;\n abortController: AbortController; // Per-component abort lifecycle\n ssr?: boolean; // Set to true for SSR temporary instances\n // Opt-in strict cleanup mode: when true cleanup errors are aggregated and re-thrown\n cleanupStrict?: boolean;\n stateValues: State<unknown>[]; // Persistent state storage across renders\n evaluationGeneration: number; // Prevents stale async evaluation completions\n notifyUpdate: (() => void) | null; // Callback for state updates (persisted on instance)\n // Internal: prebound helpers to avoid per-update closures (allocation hot-path)\n _pendingFlushTask?: () => void; // Clears hasPendingUpdate and triggers notifyUpdate\n _pendingRunTask?: () => void; // Clears hasPendingUpdate and runs component\n _enqueueRun?: () => void; // Batches run requests and enqueues _pendingRunTask\n stateIndexCheck: number; // Track state indices to catch conditional calls\n expectedStateIndices: number[]; // Expected sequence of state indices (frozen after first render)\n firstRenderComplete: boolean; // Flag to detect transition from first to subsequent renders\n mountOperations: Array<\n () => void | (() => void) | Promise<void | (() => void)>\n >; // Operations to run when component mounts\n cleanupFns: Array<() => void>; // Cleanup functions to run on unmount\n hasPendingUpdate: boolean; // Flag to batch state updates (coalescing)\n ownerFrame: ContextFrame | null; // Provider chain for this component (set by Scope, never overwritten)\n isRoot?: boolean;\n\n // Render-tracking for precise subscriptions (internal)\n _currentRenderToken?: number; // Token for the in-progress render (set before render)\n lastRenderToken?: number; // Token of the last *committed* render\n _pendingReadStates?: Set<State<unknown>>; // States read during the in-progress render\n _lastReadStates?: Set<State<unknown>>; // States read during the last committed render\n\n // Placeholder for null-returning components. When a component initially returns\n // null, we create a comment placeholder so updates can replace it with content.\n _placeholder?: Comment;\n}\n\nexport function createComponentInstance(\n id: string,\n fn: ComponentFunction,\n props: Props,\n target: Element | null\n): ComponentInstance {\n const instance: ComponentInstance = {\n id,\n fn,\n props,\n target,\n mounted: false,\n abortController: new AbortController(), // Create per-component\n stateValues: [],\n evaluationGeneration: 0,\n notifyUpdate: null,\n // Prebound helpers (initialized below) to avoid per-update allocations\n _pendingFlushTask: undefined,\n _pendingRunTask: undefined,\n _enqueueRun: undefined,\n stateIndexCheck: -1,\n expectedStateIndices: [],\n firstRenderComplete: false,\n mountOperations: [],\n cleanupFns: [],\n hasPendingUpdate: false,\n ownerFrame: null, // Will be set by renderer when vnode is marked\n ssr: false,\n cleanupStrict: false,\n isRoot: false,\n\n // Render-tracking (for precise state subscriptions)\n _currentRenderToken: undefined,\n lastRenderToken: 0,\n _pendingReadStates: new Set(),\n _lastReadStates: new Set(),\n };\n\n // Initialize prebound helper tasks once per instance to avoid allocations\n instance._pendingRunTask = () => {\n // Clear pending flag when the run task executes\n instance.hasPendingUpdate = false;\n // Execute component run (will set up notifyUpdate before render)\n runComponent(instance);\n };\n\n instance._enqueueRun = () => {\n if (!instance.hasPendingUpdate) {\n instance.hasPendingUpdate = true;\n // Enqueue single run task (coalesces multiple writes)\n globalScheduler.enqueue(instance._pendingRunTask!);\n }\n };\n\n instance._pendingFlushTask = () => {\n // Called by state.set() when we want to flush a pending update\n instance.hasPendingUpdate = false;\n // Trigger a run via enqueue helper — this will schedule the component run\n instance._enqueueRun?.();\n };\n\n return instance;\n}\n\nlet currentInstance: ComponentInstance | null = null;\nlet stateIndex = 0;\n\n// Export for state.ts to access\nexport function getCurrentComponentInstance(): ComponentInstance | null {\n return currentInstance;\n}\n\n// Export for SSR to set temporary instance\nexport function setCurrentComponentInstance(\n instance: ComponentInstance | null\n): void {\n currentInstance = instance;\n}\n\n/**\n * Register a mount operation that will run after the component is mounted\n * Used by operations (task, on, timer, etc) to execute after render completes\n */\nimport { isBulkCommitActive } from './fastlane';\nimport { evaluate, cleanupInstancesUnder } from '../renderer';\n\nexport function registerMountOperation(\n operation: () => void | (() => void) | Promise<void | (() => void)>\n): void {\n const instance = getCurrentComponentInstance();\n if (instance) {\n // If we're in bulk-commit fast lane, registering mount operations is a\n // violation of the fast-lane preconditions. Throw in dev, otherwise ignore\n // silently in production (we must avoid scheduling work during bulk commit).\n if (isBulkCommitActive()) {\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n 'registerMountOperation called during bulk commit fast-lane'\n );\n }\n return;\n }\n instance.mountOperations.push(operation);\n }\n}\n\n/**\n * Execute all mount operations for a component\n * These run after the component is rendered and mounted to the DOM\n */\nfunction executeMountOperations(instance: ComponentInstance): void {\n // Only execute mount operations for root app instance. Child component\n // operations are currently registered but should not be executed (per\n // contract tests). They remain registered for cleanup purposes.\n if (!instance.isRoot) return;\n\n for (const operation of instance.mountOperations) {\n const result = operation();\n if (result instanceof Promise) {\n result.then((cleanup) => {\n if (typeof cleanup === 'function') {\n instance.cleanupFns.push(cleanup);\n }\n });\n } else if (typeof result === 'function') {\n instance.cleanupFns.push(result);\n }\n }\n // Clear the operations array so they don't run again on subsequent renders\n instance.mountOperations = [];\n}\n\nexport function mountInstanceInline(\n instance: ComponentInstance,\n target: Element | null\n): void {\n instance.target = target;\n // Record backref on host element so renderer can clean up when the\n // node is removed. Avoids leaks if the node is detached or replaced.\n try {\n if (target instanceof Element)\n (\n target as Element & { __ASKR_INSTANCE?: ComponentInstance }\n ).__ASKR_INSTANCE = instance;\n } catch (err) {\n void err;\n }\n\n // Ensure notifyUpdate is available for async resource completions that may\n // try to trigger re-render. This mirrors the setup in executeComponent().\n // Use prebound enqueue helper to avoid allocating a new closure\n instance.notifyUpdate = instance._enqueueRun!;\n\n const wasFirstMount = !instance.mounted;\n instance.mounted = true;\n if (wasFirstMount && instance.mountOperations.length > 0) {\n executeMountOperations(instance);\n }\n}\n\n/**\n * Run a component synchronously: execute function, handle result\n * This is the internal workhorse that manages async continuations and generation tracking.\n * Must always be called through the scheduler.\n *\n * ACTOR INVARIANT: This function is enqueued as a task, never called directly.\n */\nlet _globalRenderCounter = 0;\n\nfunction runComponent(instance: ComponentInstance): void {\n // CRITICAL: Ensure notifyUpdate is available for state.set() calls during this render.\n // This must be set before executeComponentSync() runs, not after.\n // Use prebound enqueue helper to avoid allocating per-render closures\n instance.notifyUpdate = instance._enqueueRun!;\n\n // Assign a token for this in-progress render and start a fresh pending-read set\n instance._currentRenderToken = ++_globalRenderCounter;\n instance._pendingReadStates = new Set();\n\n // Atomic rendering: capture DOM state for rollback on error\n const domSnapshot = instance.target ? instance.target.innerHTML : '';\n\n const result = executeComponentSync(instance);\n if (result instanceof Promise) {\n // Async components are not supported. Components must be synchronous and\n // must not return a Promise from their render function.\n throw new Error(\n 'Async components are not supported. Components must be synchronous.'\n );\n } else {\n // Try runtime fast-lane synchronously; if it activates we do not enqueue\n // follow-up work and the commit happens atomically in this task.\n // (Runtime fast-lane has conservative preconditions.)\n const fastlaneBridge = (\n globalThis as {\n __ASKR_FASTLANE?: {\n tryRuntimeFastLaneSync?: (\n instance: unknown,\n result: unknown\n ) => boolean;\n };\n }\n ).__ASKR_FASTLANE;\n try {\n const used = fastlaneBridge?.tryRuntimeFastLaneSync?.(instance, result);\n if (used) return;\n } catch (err) {\n // If invariant check failed in dev, surface the error; otherwise fall back\n if (process.env.NODE_ENV !== 'production') throw err;\n }\n\n // Fallback: enqueue the render/commit normally\n globalScheduler.enqueue(() => {\n // Handle placeholder-based updates: when a component initially returned null,\n // we created a comment placeholder. If it now has content, we need to create\n // a host element and replace the placeholder.\n if (!instance.target && instance._placeholder) {\n // Component previously returned null (has placeholder), check if now has content\n if (result === null || result === undefined) {\n // Still null - nothing to do, keep placeholder\n finalizeReadSubscriptions(instance);\n return;\n }\n\n // Has content now - need to create DOM and replace placeholder\n const placeholder = instance._placeholder;\n const parent = placeholder.parentNode;\n if (!parent) {\n // Placeholder was removed from DOM - can't render\n logger.warn(\n '[Askr] placeholder no longer in DOM, cannot render component'\n );\n return;\n }\n\n // Create a new host element for the content\n const host = document.createElement('div');\n\n // Set up instance for normal updates\n const oldInstance = currentInstance;\n currentInstance = instance;\n try {\n evaluate(result, host);\n\n // Replace placeholder with host\n parent.replaceChild(host, placeholder);\n\n // Set up instance for future updates\n instance.target = host;\n instance._placeholder = undefined;\n (\n host as Element & { __ASKR_INSTANCE?: ComponentInstance }\n ).__ASKR_INSTANCE = instance;\n\n finalizeReadSubscriptions(instance);\n } finally {\n currentInstance = oldInstance;\n }\n return;\n }\n\n if (instance.target) {\n // Keep `oldChildren` in the outer scope so rollback handlers can\n // reference the original node list even if the inner try block\n // throws. This preserves listeners and instance backrefs on rollback.\n let oldChildren: Node[] = [];\n try {\n const wasFirstMount = !instance.mounted;\n // Ensure nested component executions during evaluation have access to\n // the current component instance. This allows nested components to\n // call `state()`, `resource()`, and other runtime helpers which\n // rely on `getCurrentComponentInstance()` being available.\n const oldInstance = currentInstance;\n currentInstance = instance;\n // Capture snapshot of current children (by reference) so we can\n // restore them on render failure without losing event listeners or\n // instance attachments.\n oldChildren = Array.from(instance.target.childNodes);\n\n try {\n evaluate(result, instance.target);\n } catch (e) {\n // If evaluation failed, attempt to cleanup any partially-added nodes\n // and restore the old children to preserve listeners and instances.\n try {\n const newChildren = Array.from(instance.target.childNodes);\n for (const n of newChildren) {\n try {\n cleanupInstancesUnder(n);\n } catch (err) {\n logger.warn(\n '[Askr] error cleaning up failed commit children:',\n err\n );\n }\n }\n } catch (_err) {\n void _err;\n }\n\n // Restore original children by re-inserting the old node references\n // this preserves attached listeners and instance backrefs.\n try {\n __ASKR_incCounter('__DOM_REPLACE_COUNT');\n __ASKR_set(\n '__LAST_DOM_REPLACE_STACK_COMPONENT_RESTORE',\n new Error().stack\n );\n } catch (e) {\n void e;\n }\n instance.target.replaceChildren(...oldChildren);\n throw e;\n } finally {\n currentInstance = oldInstance;\n }\n\n // Commit succeeded — finalize recorded state reads so subscriptions reflect\n // the last *committed* render. This updates per-state reader maps\n // deterministically and synchronously with the commit.\n finalizeReadSubscriptions(instance);\n\n instance.mounted = true;\n // Execute mount operations after first mount (do NOT run these with\n // currentInstance set - they may perform state mutations/registrations)\n if (wasFirstMount && instance.mountOperations.length > 0) {\n executeMountOperations(instance);\n }\n } catch (renderError) {\n // Atomic rendering: rollback on render error. Attempt non-lossy restore of\n // original child node references to preserve listeners/instances.\n try {\n const currentChildren = Array.from(instance.target.childNodes);\n for (const n of currentChildren) {\n try {\n cleanupInstancesUnder(n);\n } catch (err) {\n logger.warn(\n '[Askr] error cleaning up partial children during rollback:',\n err\n );\n }\n }\n } catch (_err) {\n void _err;\n }\n\n try {\n try {\n __ASKR_incCounter('__DOM_REPLACE_COUNT');\n __ASKR_set(\n '__LAST_DOM_REPLACE_STACK_COMPONENT_ROLLBACK',\n new Error().stack\n );\n } catch (e) {\n void e;\n }\n instance.target.replaceChildren(...oldChildren);\n } catch {\n // Fallback to innerHTML restore if replaceChildren fails for some reason.\n instance.target.innerHTML = domSnapshot;\n }\n\n throw renderError;\n }\n }\n });\n }\n}\n\n/**\n * Execute a component's render function synchronously.\n * Returns either a vnode/promise immediately (does NOT render).\n * Rendering happens separately through runComponent.\n */\nexport function renderComponentInline(\n instance: ComponentInstance\n): unknown | Promise<unknown> {\n // Ensure inline executions (rendered during parent's evaluate) still\n // receive a render token and have their state reads finalized so\n // subscriptions are correctly recorded. If this function is called\n // as part of a scheduled run, the token will already be set by\n // runComponent and we should not overwrite it.\n const hadToken = instance._currentRenderToken !== undefined;\n const prevToken = instance._currentRenderToken;\n const prevPendingReads = instance._pendingReadStates;\n if (!hadToken) {\n instance._currentRenderToken = ++_globalRenderCounter;\n instance._pendingReadStates = new Set();\n }\n\n try {\n const result = executeComponentSync(instance);\n // If we set the token for inline execution, finalize subscriptions now\n // because the component is effectively committed as part of the parent's\n // synchronous evaluation.\n if (!hadToken) {\n finalizeReadSubscriptions(instance);\n }\n return result;\n } finally {\n // Restore previous token/read states for nested inline render scenarios\n instance._currentRenderToken = prevToken;\n instance._pendingReadStates = prevPendingReads ?? new Set();\n }\n}\n\nfunction executeComponentSync(\n instance: ComponentInstance\n): unknown | Promise<unknown> {\n // Reset state index tracking for this render\n instance.stateIndexCheck = -1;\n\n // Reset read tracking for all existing state\n for (const state of instance.stateValues) {\n if (state) {\n state._hasBeenRead = false;\n }\n }\n\n // Prepare pending read set for this render (reads will be finalized on commit)\n instance._pendingReadStates = new Set();\n\n currentInstance = instance;\n stateIndex = 0;\n\n try {\n // Track render time in dev mode\n const renderStartTime =\n process.env.NODE_ENV !== 'production' ? Date.now() : 0;\n\n // Create context object with abort signal\n const context = {\n signal: instance.abortController.signal,\n };\n\n // Execute component within its owner frame (provider chain).\n // This ensures all context reads see the correct provider values.\n // We create a new execution frame whose parent is the ownerFrame. The\n // `values` map is lazily allocated to avoid per-render Map allocations\n // for components that do not use context.\n const executionFrame: ContextFrame = {\n parent: instance.ownerFrame,\n values: null,\n };\n const result = withContext(executionFrame, () =>\n instance.fn(instance.props, context)\n );\n\n // Check render time\n const renderTime = Date.now() - renderStartTime;\n if (renderTime > 5) {\n // Warn if render takes more than 5ms\n logger.warn(\n `[askr] Slow render detected: ${renderTime}ms. Consider optimizing component performance.`\n );\n }\n\n // Mark first render complete after successful execution\n // This enables hook order validation on subsequent renders\n if (!instance.firstRenderComplete) {\n instance.firstRenderComplete = true;\n }\n\n // Check for unused state\n for (let i = 0; i < instance.stateValues.length; i++) {\n const state = instance.stateValues[i];\n if (state && !state._hasBeenRead) {\n try {\n const name = instance.fn?.name || '<anonymous>';\n logger.warn(\n `[askr] Unused state variable detected in ${name} at index ${i}. State should be read during render or removed.`\n );\n } catch {\n logger.warn(\n `[askr] Unused state variable detected. State should be read during render or removed.`\n );\n }\n }\n }\n\n return result;\n } finally {\n // Synchronous path: we did not push a fresh frame, so nothing to pop here.\n currentInstance = null;\n }\n}\n\n/**\n * Public entry point: Execute component with full lifecycle (execute + render)\n * Handles both initial mount and re-execution. Always enqueues through scheduler.\n * Single entry point to avoid lifecycle divergence.\n */\nexport function executeComponent(instance: ComponentInstance): void {\n // Create a fresh abort controller on mount to allow remounting\n // (old one may have been aborted during previous cleanup)\n instance.abortController = new AbortController();\n\n // Setup notifyUpdate callback using prebound helper to avoid per-call closures\n instance.notifyUpdate = instance._enqueueRun!;\n\n // Enqueue the initial component run\n globalScheduler.enqueue(() => runComponent(instance));\n}\n\nexport function getCurrentInstance(): ComponentInstance | null {\n return currentInstance;\n}\n\n/**\n * Get the abort signal for the current component\n * Used to cancel async operations on unmount/navigation\n *\n * The signal is guaranteed to be aborted when:\n * - Component unmounts\n * - Navigation occurs (different route)\n * - Parent is destroyed\n *\n * IMPORTANT: getSignal() must be called during component render execution.\n * It captures the current component instance from context.\n *\n * @returns AbortSignal that will be aborted when component unmounts\n * @throws Error if called outside component execution\n *\n * @example\n * ```ts\n * // ✅ Correct: called during render, used in async operation\n * export async function UserPage({ id }: { id: string }) {\n * const signal = getSignal();\n * const user = await fetch(`/api/users/${id}`, { signal });\n * return <div>{user.name}</div>;\n * }\n *\n * // ✅ Correct: passed to event handler\n * export function Button() {\n * const signal = getSignal();\n * return {\n * type: 'button',\n * props: {\n * onClick: async () => {\n * const data = await fetch(url, { signal });\n * }\n * }\n * };\n * }\n *\n * // ❌ Wrong: called outside component context\n * const signal = getSignal(); // Error: not in component\n * ```\n */\nexport function getSignal(): AbortSignal {\n if (!currentInstance) {\n throw new Error(\n 'getSignal() can only be called during component render execution. ' +\n 'Ensure you are calling this from inside your component function.'\n );\n }\n return currentInstance.abortController.signal;\n}\n\n/**\n * Finalize read subscriptions for an instance after a successful commit.\n * - Update per-state readers map to point to this instance's last committed token\n * - Remove this instance from states it no longer reads\n * This is deterministic and runs synchronously with commit to ensure\n * subscribers are only notified when they actually read a state in their\n * last committed render.\n */\nexport function finalizeReadSubscriptions(instance: ComponentInstance): void {\n const newSet = instance._pendingReadStates ?? new Set();\n const oldSet = instance._lastReadStates ?? new Set();\n const token = instance._currentRenderToken;\n\n if (token === undefined) return;\n\n // Remove subscriptions for states that were read previously but not in this render\n for (const s of oldSet) {\n if (!newSet.has(s)) {\n const readers = (s as State<unknown>)._readers;\n if (readers) readers.delete(instance);\n }\n }\n\n // Commit token becomes the authoritative token for this instance's last render\n instance.lastRenderToken = token;\n\n // Record subscriptions for states read during this render\n for (const s of newSet) {\n let readers = (s as State<unknown>)._readers;\n if (!readers) {\n readers = new Map();\n // s is a State object; assign its _readers map\n (s as State<unknown>)._readers = readers;\n }\n readers.set(instance, instance.lastRenderToken ?? 0);\n }\n\n instance._lastReadStates = newSet;\n instance._pendingReadStates = new Set();\n instance._currentRenderToken = undefined;\n}\n\nexport function getNextStateIndex(): number {\n return stateIndex++;\n}\n\n/**\n * Mount a component instance.\n * This is just an alias to executeComponent() to maintain API compatibility.\n * All lifecycle logic is unified in executeComponent().\n */\nexport function mountComponent(instance: ComponentInstance): void {\n executeComponent(instance);\n}\n\n/**\n * Clean up component — abort pending operations\n * Called on unmount or route change\n */\nexport function cleanupComponent(instance: ComponentInstance): void {\n // Execute cleanup functions (from mount effects)\n const cleanupErrors: unknown[] = [];\n for (const cleanup of instance.cleanupFns) {\n try {\n cleanup();\n } catch (err) {\n if (instance.cleanupStrict) {\n cleanupErrors.push(err);\n } else {\n // Preserve previous behavior: log warnings in dev and continue\n if (process.env.NODE_ENV !== 'production') {\n logger.warn('[Askr] cleanup function threw:', err);\n }\n }\n }\n }\n instance.cleanupFns = [];\n if (cleanupErrors.length > 0) {\n // If strict mode, surface all cleanup errors as an AggregateError after attempting all cleanups\n throw new AggregateError(\n cleanupErrors,\n `Cleanup failed for component ${instance.id}`\n );\n }\n\n // Remove deterministic state subscriptions for this instance\n if (instance._lastReadStates) {\n for (const s of instance._lastReadStates) {\n const readers = (s as State<unknown>)._readers;\n if (readers) readers.delete(instance);\n }\n instance._lastReadStates = new Set();\n }\n\n // Abort all pending operations\n instance.abortController.abort();\n\n // Clear update callback to prevent dangling references and stale updates\n instance.notifyUpdate = null;\n\n // Mark instance as unmounted so external tracking (e.g., portal host lists)\n // can deterministically prune stale instances. Not marking this leads to\n // retained \"mounted\" flags across cleanup boundaries which breaks\n // owner selection in the portal fallback.\n instance.mounted = false;\n}\n","// Lightweight shared no-op helpers to avoid double-casts and duplicated definitions\n\nexport const noop: () => void = () => {};\n\nexport const noopEventListener: EventListener & { cancel(): void } =\n Object.assign((_ev?: Event) => {}, { cancel() {} });\n\nexport const noopEventListenerWithFlush: EventListener & {\n cancel(): void;\n flush(): void;\n} = Object.assign((_ev?: Event) => {}, { cancel() {}, flush() {} });\n\nexport const noopCancel: { cancel(): void } = { cancel() {} };\n","import { globalScheduler } from '../runtime/scheduler';\nimport { getCurrentComponentInstance } from '../runtime/component';\nimport { logger } from '../dev/logger';\nimport { noopEventListener, noopEventListenerWithFlush } from './noop';\n\nexport type CancelFn = () => void;\n\n// Platform-specific timer handle types\ntype TimeoutHandle = ReturnType<typeof setTimeout> | null;\n// rAF may fall back to setTimeout in some environments/tests, include both\ntype RafHandle =\n | ReturnType<typeof requestAnimationFrame>\n | ReturnType<typeof setTimeout>\n | null;\n// requestIdleCallback may be unavailable; allow setTimeout fallback handle\ntype IdleHandle =\n | ReturnType<typeof requestIdleCallback>\n | ReturnType<typeof setTimeout>\n | null;\n\nfunction throwIfDuringRender(): void {\n const inst = getCurrentComponentInstance();\n if (inst !== null) {\n throw new Error(\n '[Askr] calling FX handler during render is not allowed. Move calls to event handlers or effects.'\n );\n }\n}\n\n/**\n * Helper: schedule a user callback through the global scheduler\n */\nfunction enqueueUserCallback(fn: () => void) {\n globalScheduler.enqueue(() => {\n try {\n fn();\n } catch (err) {\n // Keep behavior consistent with other scheduler-queued work\n logger.error('[Askr] FX handler error:', err);\n }\n });\n}\n\n// ---------- Event handlers ----------\n\nexport function debounceEvent(\n ms: number,\n handler: EventListener,\n options?: { leading?: boolean; trailing?: boolean }\n): EventListener & { cancel(): void; flush(): void } {\n const { leading = false, trailing = true } = options || {};\n\n const inst = getCurrentComponentInstance();\n // On SSR, event handlers are inert\n if (inst && inst.ssr) {\n return noopEventListenerWithFlush;\n }\n\n let timeoutId: TimeoutHandle = null;\n let lastEvent: Event | null = null;\n let lastCallTime = 0;\n\n const debounced = function (this: unknown, ev: Event) {\n // Disallow using returned handler during render\n throwIfDuringRender();\n\n const now = Date.now();\n lastEvent = ev;\n\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n\n if (leading && now - lastCallTime >= ms) {\n enqueueUserCallback(() => handler.call(null, ev));\n lastCallTime = now;\n }\n\n if (trailing) {\n timeoutId = setTimeout(() => {\n // Schedule through scheduler\n if (lastEvent) {\n enqueueUserCallback(() => handler.call(null, lastEvent!));\n }\n timeoutId = null;\n lastCallTime = Date.now();\n }, ms);\n }\n } as EventListener & { cancel(): void; flush(): void };\n\n debounced.cancel = () => {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n lastEvent = null;\n };\n\n debounced.flush = () => {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n const ev = lastEvent;\n lastEvent = null;\n timeoutId = null;\n if (ev) enqueueUserCallback(() => handler.call(null, ev));\n }\n };\n\n // Auto-cleanup on component unmount\n if (inst) {\n inst.cleanupFns.push(() => {\n debounced.cancel();\n });\n }\n\n return debounced;\n}\n\nexport function throttleEvent(\n ms: number,\n handler: EventListener,\n options?: { leading?: boolean; trailing?: boolean }\n): EventListener & { cancel(): void } {\n const { leading = true, trailing = true } = options || {};\n\n const inst = getCurrentComponentInstance();\n if (inst && inst.ssr) {\n return noopEventListener;\n }\n\n let lastCallTime = 0;\n let timeoutId: TimeoutHandle = null;\n let lastEvent: Event | null = null;\n\n const throttled = function (this: unknown, ev: Event) {\n throwIfDuringRender();\n\n const now = Date.now();\n lastEvent = ev;\n\n if (leading && now - lastCallTime >= ms) {\n enqueueUserCallback(() => handler.call(null, ev));\n lastCallTime = now;\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n } else if (!leading && lastCallTime === 0) {\n lastCallTime = now;\n }\n\n if (trailing && timeoutId === null) {\n const wait = ms - (now - lastCallTime);\n timeoutId = setTimeout(\n () => {\n if (lastEvent)\n enqueueUserCallback(() => handler.call(null, lastEvent!));\n lastCallTime = Date.now();\n timeoutId = null;\n },\n Math.max(0, wait)\n );\n }\n } as EventListener & { cancel(): void };\n\n throttled.cancel = () => {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n lastEvent = null;\n };\n\n if (inst) {\n inst.cleanupFns.push(() => throttled.cancel());\n }\n\n return throttled;\n}\n\nexport function rafEvent(\n handler: EventListener\n): EventListener & { cancel(): void } {\n const inst = getCurrentComponentInstance();\n if (inst && inst.ssr) {\n return noopEventListener;\n }\n\n let frameId: RafHandle = null;\n let lastEvent: Event | null = null;\n\n const scheduleFrame = () => {\n const rAF =\n typeof requestAnimationFrame !== 'undefined'\n ? requestAnimationFrame\n : (cb: FrameRequestCallback) => setTimeout(() => cb(Date.now()), 16);\n\n frameId = rAF(() => {\n frameId = null;\n if (lastEvent) {\n const ev = lastEvent;\n lastEvent = null;\n enqueueUserCallback(() => handler.call(null, ev));\n }\n });\n };\n\n const fn = function (this: unknown, ev: Event) {\n throwIfDuringRender();\n lastEvent = ev;\n if (frameId === null) scheduleFrame();\n } as EventListener & { cancel(): void };\n\n fn.cancel = () => {\n if (frameId !== null) {\n // If frameId is numeric and cancelAnimationFrame is available, use it;\n // otherwise fall back to clearTimeout for the setTimeout fallback.\n if (\n typeof cancelAnimationFrame !== 'undefined' &&\n typeof frameId === 'number'\n ) {\n cancelAnimationFrame(frameId);\n } else {\n clearTimeout(frameId as ReturnType<typeof setTimeout>);\n }\n frameId = null;\n }\n lastEvent = null;\n };\n\n if (inst) inst.cleanupFns.push(() => fn.cancel());\n\n return fn;\n}\n\n// ---------- Scheduled work ----------\n\nexport function scheduleTimeout(ms: number, fn: () => void): CancelFn {\n throwIfDuringRender();\n const inst = getCurrentComponentInstance();\n if (inst && inst.ssr) {\n return () => {};\n }\n\n let id: TimeoutHandle = setTimeout(() => {\n id = null;\n enqueueUserCallback(fn);\n }, ms);\n\n const cancel = () => {\n if (id !== null) {\n clearTimeout(id);\n id = null;\n }\n };\n\n if (inst) inst.cleanupFns.push(cancel);\n return cancel;\n}\n\nexport function scheduleIdle(\n fn: () => void,\n options?: { timeout?: number }\n): CancelFn {\n throwIfDuringRender();\n const inst = getCurrentComponentInstance();\n if (inst && inst.ssr) return () => {};\n\n let id: IdleHandle = null;\n let usingRIC = false;\n\n if (typeof requestIdleCallback !== 'undefined') {\n usingRIC = true;\n id = requestIdleCallback(() => {\n id = null;\n enqueueUserCallback(fn);\n }, options);\n } else {\n // Fallback: schedule on next macrotask\n id = setTimeout(() => {\n id = null;\n enqueueUserCallback(fn);\n }, 0);\n }\n\n const cancel = () => {\n if (id !== null) {\n // If using requestIdleCallback and available, call cancelIdleCallback for numeric ids.\n if (\n usingRIC &&\n typeof cancelIdleCallback !== 'undefined' &&\n typeof id === 'number'\n ) {\n cancelIdleCallback(id);\n } else {\n clearTimeout(id as ReturnType<typeof setTimeout>);\n }\n id = null;\n }\n };\n\n if (inst) inst.cleanupFns.push(cancel);\n return cancel;\n}\n\nexport interface RetryOptions {\n maxAttempts?: number;\n delayMs?: number;\n backoff?: (attemptIndex: number) => number;\n}\n\nexport function scheduleRetry<T>(\n fn: () => Promise<T>,\n options?: RetryOptions\n): { cancel(): void } {\n throwIfDuringRender();\n const inst = getCurrentComponentInstance();\n if (inst && inst.ssr) return { cancel: () => {} };\n\n const {\n maxAttempts = 3,\n delayMs = 100,\n backoff = (i: number) => delayMs * Math.pow(2, i),\n } = options || {};\n\n let cancelled = false;\n\n const attempt = (index: number) => {\n if (cancelled) return;\n // Run user fn inside scheduler\n globalScheduler.enqueue(() => {\n if (cancelled) return;\n // Call fn (it may be async)\n const p = fn();\n p.then(\n () => {\n // Completed successfully\n },\n () => {\n if (cancelled) return;\n if (index + 1 < maxAttempts) {\n const delay = backoff(index);\n // Schedule next attempt via setTimeout so it gets enqueued through scheduleTimeout\n setTimeout(() => {\n attempt(index + 1);\n }, delay);\n }\n }\n ).catch((e) => {\n logger.error('[Askr] scheduleRetry error:', e);\n });\n });\n };\n\n // Start first attempt\n attempt(0);\n\n const cancel = () => {\n cancelled = true;\n };\n\n if (inst) inst.cleanupFns.push(cancel);\n return { cancel };\n}\n"],"mappings":"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","names":["isBulkCommitActive"]}
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Timing utilities — pure helpers for common async patterns
3
+ * No framework coupling. No lifecycle awareness.
4
+ */
5
+ interface DebounceOptions {
6
+ leading?: boolean;
7
+ trailing?: boolean;
8
+ }
9
+ interface ThrottleOptions {
10
+ leading?: boolean;
11
+ trailing?: boolean;
12
+ }
13
+ interface RetryOptions$1 {
14
+ maxAttempts?: number;
15
+ delayMs?: number;
16
+ backoff?: (attemptIndex: number) => number;
17
+ }
18
+ /**
19
+ * Debounce — delay execution, coalesce rapid calls
20
+ *
21
+ * Useful for: text input, resize, autosave
22
+ *
23
+ * @param fn Function to debounce
24
+ * @param ms Delay in milliseconds
25
+ * @param options trailing (default true), leading
26
+ * @returns Debounced function with cancel() method
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * const save = debounce((text) => api.save(text), 500);
31
+ * input.addEventListener('input', (e) => save(e.target.value));
32
+ * save.cancel(); // stop any pending execution
33
+ * ```
34
+ */
35
+ declare function debounce<T extends (...args: unknown[]) => unknown>(fn: T, ms: number, options?: DebounceOptions): T & {
36
+ cancel(): void;
37
+ };
38
+ /**
39
+ * Throttle — rate-limit execution, keep first/last
40
+ *
41
+ * Useful for: scroll, mouse move, high-frequency events
42
+ *
43
+ * @param fn Function to throttle
44
+ * @param ms Minimum interval between calls in milliseconds
45
+ * @param options leading (default true), trailing (default true)
46
+ * @returns Throttled function with cancel() method
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * const handleScroll = throttle(updateUI, 100);
51
+ * window.addEventListener('scroll', handleScroll);
52
+ * handleScroll.cancel();
53
+ * ```
54
+ */
55
+ declare function throttle<T extends (...args: unknown[]) => unknown>(fn: T, ms: number, options?: ThrottleOptions): T & {
56
+ cancel(): void;
57
+ };
58
+ /**
59
+ * Once — guard against double execution
60
+ *
61
+ * Useful for: init logic, event safety
62
+ *
63
+ * @param fn Function to call at most once
64
+ * @returns Function that executes fn only on first call
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * const init = once(setup);
69
+ * init(); // runs
70
+ * init(); // does nothing
71
+ * init(); // does nothing
72
+ * ```
73
+ */
74
+ declare function once<T extends (...args: unknown[]) => unknown>(fn: T): T;
75
+ /**
76
+ * Defer — schedule on microtask queue
77
+ *
78
+ * Useful for: run-after-current-stack logic
79
+ * More reliable than setTimeout(..., 0)
80
+ *
81
+ * @param fn Function to defer
82
+ *
83
+ * @example
84
+ * ```ts
85
+ * defer(() => update()); // runs after current stack, before next macrotask
86
+ * ```
87
+ */
88
+ declare function defer(fn: () => void): void;
89
+ /**
90
+ * RAF — coalesce multiple updates into single frame
91
+ *
92
+ * Useful for: animation, layout work, render updates
93
+ *
94
+ * @param fn Function to schedule on next animation frame
95
+ * @returns Function that schedules fn on requestAnimationFrame
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * const update = raf(render);
100
+ * update(); // schedules on next frame
101
+ * update(); // same frame, no duplicate
102
+ * ```
103
+ */
104
+ declare function raf<T extends (...args: unknown[]) => unknown>(fn: T): T;
105
+ /**
106
+ * Idle — schedule low-priority work
107
+ *
108
+ * Useful for: background prep, non-urgent updates
109
+ * Falls back to setTimeout if requestIdleCallback unavailable
110
+ *
111
+ * @param fn Function to call when idle
112
+ * @param options timeout for fallback
113
+ *
114
+ * @example
115
+ * ```ts
116
+ * idle(() => prefetchData());
117
+ * ```
118
+ */
119
+ declare function idle(fn: () => void, options?: {
120
+ timeout?: number;
121
+ }): void;
122
+ /**
123
+ * Timeout — Promise-based delay
124
+ *
125
+ * Useful for: readable async code, waiting between retries
126
+ *
127
+ * @param ms Milliseconds to wait
128
+ * @returns Promise that resolves after delay
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * await timeout(300);
133
+ * console.log('300ms later');
134
+ * ```
135
+ */
136
+ declare function timeout(ms: number): Promise<void>;
137
+ /**
138
+ * Retry — attempt function with backoff
139
+ *
140
+ * Useful for: network calls, transient failures
141
+ *
142
+ * @param fn Async function to retry
143
+ * @param options maxAttempts, delayMs, backoff function
144
+ * @returns Promise with final result or error
145
+ *
146
+ * @example
147
+ * ```ts
148
+ * const data = await retry(() => fetch(url), {
149
+ * maxAttempts: 3,
150
+ * delayMs: 100,
151
+ * });
152
+ * ```
153
+ */
154
+ declare function retry<T>(fn: () => Promise<T>, options?: RetryOptions$1): Promise<T>;
155
+
156
+ type CancelFn = () => void;
157
+ declare function debounceEvent(ms: number, handler: EventListener, options?: {
158
+ leading?: boolean;
159
+ trailing?: boolean;
160
+ }): EventListener & {
161
+ cancel(): void;
162
+ flush(): void;
163
+ };
164
+ declare function throttleEvent(ms: number, handler: EventListener, options?: {
165
+ leading?: boolean;
166
+ trailing?: boolean;
167
+ }): EventListener & {
168
+ cancel(): void;
169
+ };
170
+ declare function rafEvent(handler: EventListener): EventListener & {
171
+ cancel(): void;
172
+ };
173
+ declare function scheduleTimeout(ms: number, fn: () => void): CancelFn;
174
+ declare function scheduleIdle(fn: () => void, options?: {
175
+ timeout?: number;
176
+ }): CancelFn;
177
+ interface RetryOptions {
178
+ maxAttempts?: number;
179
+ delayMs?: number;
180
+ backoff?: (attemptIndex: number) => number;
181
+ }
182
+ declare function scheduleRetry<T>(fn: () => Promise<T>, options?: RetryOptions): {
183
+ cancel(): void;
184
+ };
185
+
186
+ export { type DebounceOptions, type RetryOptions$1 as RetryOptions, type ThrottleOptions, debounce, debounceEvent, defer, idle, once, raf, rafEvent, retry, scheduleIdle, scheduleRetry, scheduleTimeout, throttle, throttleEvent, timeout };
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Timing utilities — pure helpers for common async patterns
3
+ * No framework coupling. No lifecycle awareness.
4
+ */
5
+ interface DebounceOptions {
6
+ leading?: boolean;
7
+ trailing?: boolean;
8
+ }
9
+ interface ThrottleOptions {
10
+ leading?: boolean;
11
+ trailing?: boolean;
12
+ }
13
+ interface RetryOptions$1 {
14
+ maxAttempts?: number;
15
+ delayMs?: number;
16
+ backoff?: (attemptIndex: number) => number;
17
+ }
18
+ /**
19
+ * Debounce — delay execution, coalesce rapid calls
20
+ *
21
+ * Useful for: text input, resize, autosave
22
+ *
23
+ * @param fn Function to debounce
24
+ * @param ms Delay in milliseconds
25
+ * @param options trailing (default true), leading
26
+ * @returns Debounced function with cancel() method
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * const save = debounce((text) => api.save(text), 500);
31
+ * input.addEventListener('input', (e) => save(e.target.value));
32
+ * save.cancel(); // stop any pending execution
33
+ * ```
34
+ */
35
+ declare function debounce<T extends (...args: unknown[]) => unknown>(fn: T, ms: number, options?: DebounceOptions): T & {
36
+ cancel(): void;
37
+ };
38
+ /**
39
+ * Throttle — rate-limit execution, keep first/last
40
+ *
41
+ * Useful for: scroll, mouse move, high-frequency events
42
+ *
43
+ * @param fn Function to throttle
44
+ * @param ms Minimum interval between calls in milliseconds
45
+ * @param options leading (default true), trailing (default true)
46
+ * @returns Throttled function with cancel() method
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * const handleScroll = throttle(updateUI, 100);
51
+ * window.addEventListener('scroll', handleScroll);
52
+ * handleScroll.cancel();
53
+ * ```
54
+ */
55
+ declare function throttle<T extends (...args: unknown[]) => unknown>(fn: T, ms: number, options?: ThrottleOptions): T & {
56
+ cancel(): void;
57
+ };
58
+ /**
59
+ * Once — guard against double execution
60
+ *
61
+ * Useful for: init logic, event safety
62
+ *
63
+ * @param fn Function to call at most once
64
+ * @returns Function that executes fn only on first call
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * const init = once(setup);
69
+ * init(); // runs
70
+ * init(); // does nothing
71
+ * init(); // does nothing
72
+ * ```
73
+ */
74
+ declare function once<T extends (...args: unknown[]) => unknown>(fn: T): T;
75
+ /**
76
+ * Defer — schedule on microtask queue
77
+ *
78
+ * Useful for: run-after-current-stack logic
79
+ * More reliable than setTimeout(..., 0)
80
+ *
81
+ * @param fn Function to defer
82
+ *
83
+ * @example
84
+ * ```ts
85
+ * defer(() => update()); // runs after current stack, before next macrotask
86
+ * ```
87
+ */
88
+ declare function defer(fn: () => void): void;
89
+ /**
90
+ * RAF — coalesce multiple updates into single frame
91
+ *
92
+ * Useful for: animation, layout work, render updates
93
+ *
94
+ * @param fn Function to schedule on next animation frame
95
+ * @returns Function that schedules fn on requestAnimationFrame
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * const update = raf(render);
100
+ * update(); // schedules on next frame
101
+ * update(); // same frame, no duplicate
102
+ * ```
103
+ */
104
+ declare function raf<T extends (...args: unknown[]) => unknown>(fn: T): T;
105
+ /**
106
+ * Idle — schedule low-priority work
107
+ *
108
+ * Useful for: background prep, non-urgent updates
109
+ * Falls back to setTimeout if requestIdleCallback unavailable
110
+ *
111
+ * @param fn Function to call when idle
112
+ * @param options timeout for fallback
113
+ *
114
+ * @example
115
+ * ```ts
116
+ * idle(() => prefetchData());
117
+ * ```
118
+ */
119
+ declare function idle(fn: () => void, options?: {
120
+ timeout?: number;
121
+ }): void;
122
+ /**
123
+ * Timeout — Promise-based delay
124
+ *
125
+ * Useful for: readable async code, waiting between retries
126
+ *
127
+ * @param ms Milliseconds to wait
128
+ * @returns Promise that resolves after delay
129
+ *
130
+ * @example
131
+ * ```ts
132
+ * await timeout(300);
133
+ * console.log('300ms later');
134
+ * ```
135
+ */
136
+ declare function timeout(ms: number): Promise<void>;
137
+ /**
138
+ * Retry — attempt function with backoff
139
+ *
140
+ * Useful for: network calls, transient failures
141
+ *
142
+ * @param fn Async function to retry
143
+ * @param options maxAttempts, delayMs, backoff function
144
+ * @returns Promise with final result or error
145
+ *
146
+ * @example
147
+ * ```ts
148
+ * const data = await retry(() => fetch(url), {
149
+ * maxAttempts: 3,
150
+ * delayMs: 100,
151
+ * });
152
+ * ```
153
+ */
154
+ declare function retry<T>(fn: () => Promise<T>, options?: RetryOptions$1): Promise<T>;
155
+
156
+ type CancelFn = () => void;
157
+ declare function debounceEvent(ms: number, handler: EventListener, options?: {
158
+ leading?: boolean;
159
+ trailing?: boolean;
160
+ }): EventListener & {
161
+ cancel(): void;
162
+ flush(): void;
163
+ };
164
+ declare function throttleEvent(ms: number, handler: EventListener, options?: {
165
+ leading?: boolean;
166
+ trailing?: boolean;
167
+ }): EventListener & {
168
+ cancel(): void;
169
+ };
170
+ declare function rafEvent(handler: EventListener): EventListener & {
171
+ cancel(): void;
172
+ };
173
+ declare function scheduleTimeout(ms: number, fn: () => void): CancelFn;
174
+ declare function scheduleIdle(fn: () => void, options?: {
175
+ timeout?: number;
176
+ }): CancelFn;
177
+ interface RetryOptions {
178
+ maxAttempts?: number;
179
+ delayMs?: number;
180
+ backoff?: (attemptIndex: number) => number;
181
+ }
182
+ declare function scheduleRetry<T>(fn: () => Promise<T>, options?: RetryOptions): {
183
+ cancel(): void;
184
+ };
185
+
186
+ export { type DebounceOptions, type RetryOptions$1 as RetryOptions, type ThrottleOptions, debounce, debounceEvent, defer, idle, once, raf, rafEvent, retry, scheduleIdle, scheduleRetry, scheduleTimeout, throttle, throttleEvent, timeout };