@askrjs/askr 0.0.43 → 0.0.45

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 (78) hide show
  1. package/dist/benchmark.js +2 -2
  2. package/dist/bin/askr-ssg.js.map +1 -1
  3. package/dist/boot/index.js.map +1 -1
  4. package/dist/common/env.js.map +1 -1
  5. package/dist/common/route-activity.js.map +1 -1
  6. package/dist/common/ssr.d.ts +22 -1
  7. package/dist/common/ssr.d.ts.map +1 -1
  8. package/dist/common/ssr.js +9 -1
  9. package/dist/common/ssr.js.map +1 -1
  10. package/dist/control/case.js.map +1 -1
  11. package/dist/control/show.js.map +1 -1
  12. package/dist/data/index.d.ts.map +1 -1
  13. package/dist/data/index.js.map +1 -1
  14. package/dist/foundations/icon/icon.js.map +1 -1
  15. package/dist/foundations/structures/layer.js.map +1 -1
  16. package/dist/foundations/structures/portal.js.map +1 -1
  17. package/dist/fx/fx.js.map +1 -1
  18. package/dist/fx/timing.js.map +1 -1
  19. package/dist/renderer/children.js.map +1 -1
  20. package/dist/renderer/cleanup.js.map +1 -1
  21. package/dist/renderer/dom.js.map +1 -1
  22. package/dist/renderer/evaluate.js.map +1 -1
  23. package/dist/renderer/fastpath.js.map +1 -1
  24. package/dist/renderer/for-commit.js.map +1 -1
  25. package/dist/renderer/keyed.js.map +1 -1
  26. package/dist/renderer/reconcile.js.map +1 -1
  27. package/dist/renderer/utils.js.map +1 -1
  28. package/dist/router/match.js.map +1 -1
  29. package/dist/router/navigate.js.map +1 -1
  30. package/dist/router/policy.js.map +1 -1
  31. package/dist/router/route.d.ts +4 -0
  32. package/dist/router/route.d.ts.map +1 -1
  33. package/dist/router/route.js +4 -1
  34. package/dist/router/route.js.map +1 -1
  35. package/dist/runtime/component.d.ts +1 -0
  36. package/dist/runtime/component.d.ts.map +1 -1
  37. package/dist/runtime/component.js +16 -8
  38. package/dist/runtime/component.js.map +1 -1
  39. package/dist/runtime/context.js.map +1 -1
  40. package/dist/runtime/derive.js.map +1 -1
  41. package/dist/runtime/dev-namespace.js.map +1 -1
  42. package/dist/runtime/effect.js.map +1 -1
  43. package/dist/runtime/events.js.map +1 -1
  44. package/dist/runtime/fastlane.js.map +1 -1
  45. package/dist/runtime/for.js.map +1 -1
  46. package/dist/runtime/operations.js.map +1 -1
  47. package/dist/runtime/readable.d.ts +2 -1
  48. package/dist/runtime/readable.d.ts.map +1 -1
  49. package/dist/runtime/readable.js +41 -10
  50. package/dist/runtime/readable.js.map +1 -1
  51. package/dist/runtime/resource-cell.js.map +1 -1
  52. package/dist/runtime/scheduler.js.map +1 -1
  53. package/dist/runtime/selector.js.map +1 -1
  54. package/dist/ssg/batch-render.js +27 -5
  55. package/dist/ssg/batch-render.js.map +1 -1
  56. package/dist/ssg/create-static-gen.d.ts.map +1 -1
  57. package/dist/ssg/create-static-gen.js +6 -4
  58. package/dist/ssg/create-static-gen.js.map +1 -1
  59. package/dist/ssg/generate-metadata.js.map +1 -1
  60. package/dist/ssg/incremental-manifest.js.map +1 -1
  61. package/dist/ssg/index.d.ts +2 -1
  62. package/dist/ssg/resolve-ssg-data.js +18 -1
  63. package/dist/ssg/resolve-ssg-data.js.map +1 -1
  64. package/dist/ssg/types.d.ts +3 -0
  65. package/dist/ssg/types.d.ts.map +1 -1
  66. package/dist/ssg/write-static-files.js.map +1 -1
  67. package/dist/ssr/context.d.ts +1 -2
  68. package/dist/ssr/context.d.ts.map +1 -1
  69. package/dist/ssr/context.js.map +1 -1
  70. package/dist/ssr/escape.js.map +1 -1
  71. package/dist/ssr/index.d.ts +10 -11
  72. package/dist/ssr/index.d.ts.map +1 -1
  73. package/dist/ssr/index.js +104 -68
  74. package/dist/ssr/index.js.map +1 -1
  75. package/dist/ssr/render-keys.js.map +1 -1
  76. package/dist/ssr/render-resolved.d.ts +1 -1
  77. package/dist/testing/index.js.map +1 -1
  78. package/package.json +4 -3
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/data/index.ts"],"sourcesContent":["import { globalScheduler } from '../runtime/scheduler';\nimport {\n claimHookIndex,\n getCurrentComponentInstance,\n type ComponentInstance,\n} from '../runtime/component';\nimport {\n markReadableDerivedSubscribersDirty,\n markReactivePropsDirtySource,\n notifyReadableReaders,\n recordReadableRead,\n type ReadableSource,\n} from '../runtime/readable';\nimport { getRenderContext } from '../ssr/context';\nimport { logger } from '../dev/logger';\nimport { emitInvalidation } from './invalidation-listeners';\nimport {\n documentVisible,\n routeActive,\n timer,\n windowFocused,\n type ActivityPredicate,\n} from '../runtime/operations';\n\nexport type QueryConsistency =\n | 'fresh'\n | 'stale'\n | 'refreshing'\n | 'pending-write';\n\nexport type QueryStaleReason = 'aborted' | 'error' | 'inconsistent';\n\nexport interface InvalidateOptions {\n markPendingWrite?: boolean;\n}\n\nexport interface InvalidateOnIntervalOptions extends InvalidateOptions {\n intervalMs: number;\n activeOn?: string | readonly string[];\n visibleOnly?: boolean;\n focusedOnly?: boolean;\n}\n\nexport type QueryKeyPart =\n | string\n | number\n | boolean\n | null\n | undefined\n | readonly QueryKeyPart[]\n | { readonly [key: string]: QueryKeyPart };\n\nexport interface QueryScope {\n key(...parts: QueryKeyPart[]): string;\n prefix(...parts: QueryKeyPart[]): string;\n invalidate(parts: readonly QueryKeyPart[], options?: InvalidateOptions): void;\n}\n\ntype QueryControls = {\n refresh(): Promise<void>;\n};\n\ntype QueryLoading = {\n data: null;\n error: null;\n loading: true;\n refreshing: false;\n stale: false;\n consistency: 'fresh';\n staleReason: null;\n};\n\ntype QueryFresh<T> = {\n data: T;\n error: null;\n loading: false;\n refreshing: false;\n stale: false;\n consistency: 'fresh';\n staleReason: null;\n};\n\ntype QueryRefreshing<T> = {\n data: T;\n error: null;\n loading: false;\n refreshing: true;\n stale: true;\n consistency: 'refreshing';\n staleReason: null;\n};\n\ntype QueryPendingWrite<T> = {\n data: T;\n error: null;\n loading: false;\n refreshing: true;\n stale: true;\n consistency: 'pending-write';\n staleReason: null;\n};\n\ntype QueryStaleValue<T> = {\n data: T;\n error: null;\n loading: false;\n refreshing: false;\n stale: true;\n consistency: 'stale';\n staleReason: 'aborted' | 'inconsistent';\n};\n\ntype QueryStaleErrorWithValue<T> = {\n data: T;\n error: {};\n loading: false;\n refreshing: false;\n stale: true;\n consistency: 'stale';\n staleReason: 'error';\n};\n\ntype QueryStaleError = {\n data: null;\n error: {};\n loading: false;\n refreshing: false;\n stale: true;\n consistency: 'stale';\n staleReason: 'error';\n};\n\nexport type Query<T extends {}> = QueryControls &\n (\n | QueryLoading\n | QueryFresh<T>\n | QueryRefreshing<T>\n | QueryPendingWrite<T>\n | QueryStaleValue<T>\n | QueryStaleErrorWithValue<T>\n | QueryStaleError\n );\n\ntype MutationControls<TInput, TResult> = {\n execute(input: TInput): Promise<TResult>;\n abort(): void;\n reset(): void;\n};\n\ntype MutationIdle = {\n status: 'idle';\n pending: false;\n error: null;\n result: null;\n};\n\ntype MutationPending = {\n status: 'pending';\n pending: true;\n error: null;\n result: null;\n};\n\ntype MutationSuccess<TResult> = {\n status: 'success';\n pending: false;\n error: null;\n result: TResult;\n};\n\ntype MutationError = {\n status: 'error';\n pending: false;\n error: {};\n result: null;\n};\n\nexport type Mutation<TInput, TResult> = MutationControls<TInput, TResult> &\n (MutationIdle | MutationPending | MutationSuccess<TResult> | MutationError);\n\ntype MutationRecord<TResult> = {\n status: 'idle' | 'pending' | 'success' | 'error';\n error: {} | null;\n result: TResult | null;\n};\n\ntype QueryOptions<T> = {\n key: string;\n fetch: (ctx: { signal: AbortSignal }) => Promise<T>;\n isConsistent?: (data: T) => boolean;\n reconcile?: (data: T, ctx: { key: string }) => Promise<boolean> | boolean;\n};\n\ntype MutationOptions<TInput, TResult> = {\n action: (input: TInput, ctx: { signal: AbortSignal }) => Promise<TResult>;\n affects?: (input: TInput, result: TResult) => string[];\n afterSuccess?: 'invalidate';\n};\n\ntype QueryState<T> = {\n data: T | null;\n error: {} | null;\n loading: boolean;\n refreshing: boolean;\n stale: boolean;\n consistency: QueryConsistency;\n staleReason: QueryStaleReason | null;\n};\n\ntype QuerySlot = {\n key: string;\n cell: QueryCell<unknown>;\n};\n\ntype QueryDefinitionField = 'fetch' | 'isConsistent' | 'reconcile';\n\nconst RECONCILE_MAX_ATTEMPTS = 3;\nconst RECONCILE_RETRY_DELAY_MS = 25;\nconst globalQueryCache = new Map<string, QueryCell<unknown>>();\nconst querySlotsByInstance = new WeakMap<\n ComponentInstance,\n Map<number, QuerySlot>\n>();\nconst mutationSlotsByInstance = new WeakMap<\n ComponentInstance,\n Map<number, MutationCell<unknown, unknown>>\n>();\nconst queryCleanupRegistered = new WeakSet<ComponentInstance>();\nconst mutationCleanupRegistered = new WeakSet<ComponentInstance>();\n\nfunction createReadableSource(): ReadableSource<unknown> {\n return (() => undefined) as ReadableSource<unknown>;\n}\n\nfunction getQueryCache(): Map<string, QueryCell<unknown>> {\n return (\n (getRenderContext()?.queryCache as Map<\n string,\n QueryCell<unknown>\n > | null) ?? globalQueryCache\n );\n}\n\nfunction getQuerySlotStore(\n instance: ComponentInstance\n): Map<number, QuerySlot> {\n let store = querySlotsByInstance.get(instance);\n if (!store) {\n store = new Map();\n querySlotsByInstance.set(instance, store);\n }\n return store;\n}\n\nfunction getMutationSlotStore(\n instance: ComponentInstance\n): Map<number, MutationCell<unknown, unknown>> {\n let store = mutationSlotsByInstance.get(instance);\n if (!store) {\n store = new Map();\n mutationSlotsByInstance.set(instance, store);\n }\n return store;\n}\n\nfunction ensureQueryCleanup(instance: ComponentInstance): void {\n if (queryCleanupRegistered.has(instance)) {\n return;\n }\n\n queryCleanupRegistered.add(instance);\n instance.cleanupFns.push(() => {\n const slots = querySlotsByInstance.get(instance);\n if (!slots) {\n queryCleanupRegistered.delete(instance);\n return;\n }\n\n for (const [hookIndex, slot] of slots) {\n slot.cell.detach(instance, hookIndex);\n }\n\n slots.clear();\n querySlotsByInstance.delete(instance);\n queryCleanupRegistered.delete(instance);\n });\n}\n\nfunction ensureMutationCleanup(instance: ComponentInstance): void {\n if (mutationCleanupRegistered.has(instance)) {\n return;\n }\n\n mutationCleanupRegistered.add(instance);\n instance.cleanupFns.push(() => {\n const slots = mutationSlotsByInstance.get(instance);\n if (!slots) {\n mutationCleanupRegistered.delete(instance);\n return;\n }\n\n for (const cell of slots.values()) {\n cell.abort();\n }\n\n slots.clear();\n mutationSlotsByInstance.delete(instance);\n mutationCleanupRegistered.delete(instance);\n });\n}\n\nfunction notifySource(source: ReadableSource<unknown>): void {\n markReadableDerivedSubscribersDirty(source);\n markReactivePropsDirtySource(source);\n notifyReadableReaders(source);\n}\n\nfunction isAbortError(error: unknown, signal: AbortSignal): boolean {\n return (\n signal.aborted ||\n (error instanceof Error && error.name === 'AbortError') ||\n (typeof DOMException !== 'undefined' &&\n error instanceof DOMException &&\n error.name === 'AbortError')\n );\n}\n\nfunction normalizeAsyncDataError(error: unknown, fallbackMessage: string): {} {\n return error ?? new Error(fallbackMessage);\n}\n\nfunction invalidateQueries(prefix: string, markPendingWrite: boolean): void {\n emitInvalidation({ prefix, markPendingWrite });\n\n const cache = getQueryCache();\n\n for (const [key, query] of cache) {\n if (!key.startsWith(prefix)) {\n continue;\n }\n\n if (markPendingWrite) {\n query.markPendingWrite();\n }\n\n void query.refresh();\n }\n}\n\nclass QueryCell<T> {\n private readonly source = createReadableSource();\n private readonly key: string;\n private readonly cache: Map<string, QueryCell<unknown>>;\n private options: QueryOptions<T>;\n private controller: AbortController | null = null;\n private generation = 0;\n private startQueued = false;\n private pendingRefresh: Promise<void> | null = null;\n private pendingRefreshResolve: (() => void) | null = null;\n private reconcileAttemptCount = 0;\n private destroyed = false;\n private ownerCount = 0;\n private readonly owners = new Map<ComponentInstance, Set<number>>();\n private readonly warnedDefinitionConflictKeys = new Set<string>();\n\n private state: QueryState<T> = {\n data: null,\n error: null,\n loading: true,\n refreshing: false,\n stale: false,\n consistency: 'fresh',\n staleReason: null,\n };\n\n constructor(\n options: QueryOptions<T>,\n key: string,\n cache: Map<string, QueryCell<unknown>>\n ) {\n this.options = options;\n this.key = key;\n this.cache = cache;\n }\n\n attach(instance: ComponentInstance, hookIndex: number): void {\n let hooks = this.owners.get(instance);\n if (!hooks) {\n hooks = new Set();\n this.owners.set(instance, hooks);\n }\n\n if (hooks.has(hookIndex)) {\n return;\n }\n\n hooks.add(hookIndex);\n this.ownerCount += 1;\n }\n\n detach(instance: ComponentInstance, hookIndex: number): void {\n const hooks = this.owners.get(instance);\n if (!hooks || !hooks.delete(hookIndex)) {\n return;\n }\n\n this.ownerCount -= 1;\n if (hooks.size === 0) {\n this.owners.delete(instance);\n }\n\n if (this.ownerCount <= 0) {\n this.destroy();\n }\n }\n\n warnOnConflictingDefinition(options: QueryOptions<T>): void {\n const conflicts = this.getDefinitionConflicts(options);\n if (conflicts.length === 0) {\n return;\n }\n\n const conflictKey = conflicts.join(',');\n if (this.warnedDefinitionConflictKeys.has(conflictKey)) {\n return;\n }\n\n this.warnedDefinitionConflictKeys.add(conflictKey);\n\n const callbackLabel =\n conflicts.length === 1\n ? `callback \\`${conflicts[0]}\\``\n : `callbacks ${conflicts.map((field) => `\\`${field}\\``).join(', ')}`;\n\n logger.warn(\n `[askr] Conflicting shared query definition for key \"${this.key}\". ` +\n `Shared queries are canonical by key, so reuse the same ${callbackLabel} ` +\n 'for every reader of that key.'\n );\n }\n\n private destroy(): void {\n if (this.destroyed) {\n return;\n }\n\n this.destroyed = true;\n this.controller?.abort();\n this.controller = null;\n this.startQueued = false;\n this.reconcileAttemptCount = 0;\n this.ownerCount = 0;\n this.owners.clear();\n this.cache.delete(this.key);\n this.finishPendingRefresh();\n }\n\n private getDefinitionConflicts(\n options: QueryOptions<T>\n ): QueryDefinitionField[] {\n const conflicts: QueryDefinitionField[] = [];\n\n if (this.options.fetch !== options.fetch) {\n conflicts.push('fetch');\n }\n\n if (this.options.isConsistent !== options.isConsistent) {\n conflicts.push('isConsistent');\n }\n\n if (this.options.reconcile !== options.reconcile) {\n conflicts.push('reconcile');\n }\n\n return conflicts;\n }\n\n get data(): T | null {\n recordReadableRead(this.source);\n return this.state.data;\n }\n\n get error(): {} | null {\n recordReadableRead(this.source);\n return this.state.error;\n }\n\n get loading(): boolean {\n recordReadableRead(this.source);\n return this.state.loading;\n }\n\n get refreshing(): boolean {\n recordReadableRead(this.source);\n return this.state.refreshing;\n }\n\n get stale(): boolean {\n recordReadableRead(this.source);\n return this.state.stale;\n }\n\n get consistency(): QueryConsistency {\n recordReadableRead(this.source);\n return this.state.consistency;\n }\n\n get staleReason(): QueryStaleReason | null {\n recordReadableRead(this.source);\n return this.state.staleReason;\n }\n\n ensureStarted(): void {\n if (\n this.destroyed ||\n this.state.data !== null ||\n this.pendingRefresh ||\n this.startQueued\n ) {\n return;\n }\n\n this.queueStart('initial');\n }\n\n refresh(): Promise<void> {\n if (this.destroyed) {\n return Promise.resolve();\n }\n\n if (this.pendingRefresh) {\n return this.pendingRefresh;\n }\n\n this.queueStart('manual');\n return this.pendingRefresh ?? Promise.resolve();\n }\n\n markPendingWrite(): void {\n if (this.destroyed) {\n return;\n }\n\n if (this.state.data === null) {\n return;\n }\n\n this.setState({\n loading: false,\n error: null,\n refreshing: true,\n stale: true,\n consistency: 'pending-write',\n staleReason: null,\n });\n }\n\n private queueStart(\n reason: 'initial' | 'manual' | 'invalidate' | 'pending-write'\n ): void {\n if (this.destroyed) {\n return;\n }\n\n this.startQueued = true;\n this.pendingRefresh = new Promise<void>((resolve) => {\n this.pendingRefreshResolve = resolve;\n globalScheduler.enqueue(() => {\n this.startQueued = false;\n if (this.destroyed) {\n this.finishPendingRefresh();\n return;\n }\n void this.start(reason).finally(() => {\n this.finishPendingRefresh();\n });\n });\n });\n }\n\n private finishPendingRefresh(): void {\n const resolve = this.pendingRefreshResolve;\n this.pendingRefresh = null;\n this.pendingRefreshResolve = null;\n resolve?.();\n }\n\n private setState(next: Partial<QueryState<T>>): void {\n if (this.destroyed) {\n return;\n }\n\n this.state = {\n ...this.state,\n ...next,\n };\n notifySource(this.source);\n }\n\n private async start(\n reason: 'initial' | 'manual' | 'invalidate' | 'pending-write'\n ): Promise<void> {\n if (this.destroyed) {\n return;\n }\n\n this.generation += 1;\n const generation = this.generation;\n\n this.controller?.abort();\n const controller = new AbortController();\n this.controller = controller;\n\n const hasData = this.state.data !== null;\n this.setState({\n loading: !hasData,\n refreshing: hasData,\n stale: hasData,\n consistency:\n reason === 'pending-write'\n ? 'pending-write'\n : hasData\n ? 'refreshing'\n : 'fresh',\n error: null,\n staleReason: null,\n });\n\n let nextData: T;\n try {\n nextData = await this.options.fetch({ signal: controller.signal });\n } catch (error) {\n if (\n this.destroyed ||\n this.generation !== generation ||\n this.controller !== controller\n ) {\n return;\n }\n\n if (isAbortError(error, controller.signal)) {\n this.setState(\n hasData\n ? {\n loading: false,\n refreshing: false,\n stale: true,\n consistency: 'stale',\n error: null,\n staleReason: 'aborted',\n }\n : {\n loading: true,\n refreshing: false,\n stale: false,\n consistency: 'fresh',\n error: null,\n staleReason: null,\n }\n );\n return;\n }\n\n this.setState({\n loading: false,\n refreshing: false,\n stale: true,\n consistency: 'stale',\n error: normalizeAsyncDataError(error, 'Unknown query error'),\n staleReason: 'error',\n });\n return;\n }\n\n if (\n this.destroyed ||\n this.generation !== generation ||\n this.controller !== controller\n ) {\n return;\n }\n\n const isConsistent = this.options.isConsistent?.(nextData) ?? true;\n if (!isConsistent) {\n this.setState({\n data: nextData,\n loading: false,\n refreshing: false,\n stale: true,\n consistency: 'stale',\n staleReason: 'inconsistent',\n });\n await this.reconcile(nextData);\n return;\n }\n\n this.reconcileAttemptCount = 0;\n this.setState({\n data: nextData,\n loading: false,\n refreshing: false,\n stale: false,\n consistency: 'fresh',\n error: null,\n staleReason: null,\n });\n }\n\n private async reconcile(data: T): Promise<void> {\n const shouldRetry =\n this.options.reconcile?.(data, { key: this.options.key }) ?? false;\n\n if (!shouldRetry || this.destroyed) {\n return;\n }\n\n this.reconcileAttemptCount += 1;\n if (this.reconcileAttemptCount > RECONCILE_MAX_ATTEMPTS) {\n this.setState({\n consistency: 'stale',\n refreshing: false,\n staleReason: 'inconsistent',\n });\n return;\n }\n\n await new Promise<void>((resolve) =>\n setTimeout(resolve, RECONCILE_RETRY_DELAY_MS)\n );\n if (this.destroyed || this.state.consistency === 'fresh') {\n return;\n }\n\n await this.refresh();\n }\n}\n\nclass MutationCell<TInput, TResult> {\n private readonly source = createReadableSource();\n private action: MutationOptions<TInput, TResult>['action'];\n private affects?: MutationOptions<TInput, TResult>['affects'];\n private afterSuccess?: MutationOptions<TInput, TResult>['afterSuccess'];\n private controller: AbortController | null = null;\n private generation = 0;\n\n private state: MutationRecord<TResult> = {\n status: 'idle',\n error: null,\n result: null,\n };\n\n constructor(options: MutationOptions<TInput, TResult>) {\n this.action = options.action;\n this.affects = options.affects;\n this.afterSuccess = options.afterSuccess;\n }\n\n setOptions(options: MutationOptions<TInput, TResult>): void {\n this.action = options.action;\n this.affects = options.affects;\n this.afterSuccess = options.afterSuccess;\n }\n\n get status(): 'idle' | 'pending' | 'success' | 'error' {\n recordReadableRead(this.source);\n return this.state.status;\n }\n\n get pending(): boolean {\n recordReadableRead(this.source);\n return this.state.status === 'pending';\n }\n\n get error(): {} | null {\n recordReadableRead(this.source);\n return this.state.error;\n }\n\n get result(): TResult | null {\n recordReadableRead(this.source);\n return this.state.result;\n }\n\n private setState(next: Partial<MutationRecord<TResult>>): void {\n this.state = {\n ...this.state,\n ...next,\n };\n notifySource(this.source);\n }\n\n async execute(input: TInput): Promise<TResult> {\n this.generation += 1;\n const generation = this.generation;\n\n this.controller?.abort();\n const controller = new AbortController();\n this.controller = controller;\n\n this.setState({ status: 'pending', error: null, result: null });\n\n let result: TResult;\n try {\n result = await this.action(input, { signal: controller.signal });\n } catch (error) {\n if (this.generation !== generation || this.controller !== controller) {\n throw error;\n }\n\n if (isAbortError(error, controller.signal)) {\n this.setState({ status: 'idle', error: null });\n throw error;\n }\n\n this.setState({\n status: 'error',\n error: normalizeAsyncDataError(error, 'Unknown mutation error'),\n });\n throw error;\n }\n\n if (this.generation !== generation || this.controller !== controller) {\n return result;\n }\n\n this.setState({ status: 'success', error: null, result });\n\n if (this.afterSuccess === 'invalidate') {\n const prefixes = this.affects?.(input, result) ?? [];\n for (const prefix of new Set(prefixes)) {\n invalidateQueries(prefix, true);\n }\n }\n\n return result;\n }\n\n abort(): void {\n if (this.state.status !== 'pending') {\n return;\n }\n\n this.generation += 1;\n this.controller?.abort();\n this.controller = null;\n this.setState({ status: 'idle', error: null, result: null });\n }\n\n reset(): void {\n this.generation += 1;\n this.controller?.abort();\n this.controller = null;\n this.setState({ status: 'idle', error: null, result: null });\n }\n}\n\nexport function createQuery<T extends {}>(options: QueryOptions<T>): Query<T> {\n const instance = getCurrentComponentInstance();\n const cache = getQueryCache();\n if (!instance) {\n let cell = cache.get(options.key) as QueryCell<T> | undefined;\n if (!cell) {\n cell = new QueryCell(options, options.key, cache);\n cache.set(options.key, cell as QueryCell<unknown>);\n cell.ensureStarted();\n } else {\n cell.warnOnConflictingDefinition(options);\n }\n return cell as unknown as Query<T>;\n }\n\n const hookIndex = claimHookIndex(instance, 'query');\n ensureQueryCleanup(instance);\n\n const slotStore = getQuerySlotStore(instance);\n const existingSlot = slotStore.get(hookIndex);\n if (existingSlot && existingSlot.key === options.key) {\n (existingSlot.cell as QueryCell<T>).warnOnConflictingDefinition(options);\n return existingSlot.cell as unknown as Query<T>;\n }\n\n if (existingSlot) {\n existingSlot.cell.detach(instance, hookIndex);\n }\n\n let cell = cache.get(options.key) as QueryCell<T> | undefined;\n if (!cell) {\n cell = new QueryCell(options, options.key, cache);\n cache.set(options.key, cell as QueryCell<unknown>);\n cell.ensureStarted();\n } else {\n cell.warnOnConflictingDefinition(options);\n }\n\n slotStore.set(hookIndex, {\n key: options.key,\n cell: cell as QueryCell<unknown>,\n });\n cell.attach(instance, hookIndex);\n return cell as unknown as Query<T>;\n}\n\nexport function invalidate(prefix: string, options?: InvalidateOptions): void {\n invalidateQueries(prefix, options?.markPendingWrite ?? false);\n}\n\nfunction serializeQueryKeyNumber(part: number): string {\n if (Number.isNaN(part)) {\n return 'NaN';\n }\n\n if (Object.is(part, -0)) {\n return '-0';\n }\n\n if (part === Infinity) {\n return 'Infinity';\n }\n\n if (part === -Infinity) {\n return '-Infinity';\n }\n\n return String(part);\n}\n\nfunction serializeQueryKeyPart(part: QueryKeyPart): string {\n if (typeof (part as unknown) === 'symbol') {\n throw new Error('[Askr] queryScope() key parts cannot be symbols.');\n }\n\n if (part === null) {\n return 'null';\n }\n\n if (part === undefined) {\n return 'undefined';\n }\n\n switch (typeof part) {\n case 'string':\n return `s=${encodeURIComponent(part)}`;\n case 'number':\n return `n=${serializeQueryKeyNumber(part)}`;\n case 'boolean':\n return `b=${part ? '1' : '0'}`;\n case 'object':\n if (Array.isArray(part)) {\n return `a[${part.map((item) => serializeQueryKeyPart(item)).join(',')}]`;\n }\n\n return `o{${Object.keys(part)\n .sort()\n .map(\n (key) =>\n `${encodeURIComponent(key)}=${serializeQueryKeyPart(part[key])}`\n )\n .join(',')}}`;\n default:\n return String(part);\n }\n}\n\nexport function queryScope(namespace: string): QueryScope {\n if (typeof namespace !== 'string' || namespace.trim().length === 0) {\n throw new Error('[Askr] queryScope() requires a non-empty namespace string.');\n }\n\n const build = (parts: readonly QueryKeyPart[]): string =>\n [namespace, ...parts].map(serializeQueryKeyPart).join(':') + ':';\n\n return {\n key(...parts) {\n return build(parts);\n },\n\n prefix(...parts) {\n return build(parts);\n },\n\n invalidate(parts, options) {\n invalidate(build(parts), options);\n },\n };\n}\n\nconst INVALIDATE_ON_INTERVAL_OPTIONS_ERROR =\n '[Askr] invalidateOnInterval() requires an options object with a finite numeric intervalMs.';\n\nexport function invalidateOnInterval(\n prefix: string,\n options: InvalidateOnIntervalOptions\n): void {\n if (\n !options ||\n typeof options !== 'object' ||\n typeof options.intervalMs !== 'number' ||\n !Number.isFinite(options.intervalMs)\n ) {\n throw new Error(INVALIDATE_ON_INTERVAL_OPTIONS_ERROR);\n }\n\n const when: ActivityPredicate[] = [];\n\n if (options.activeOn) {\n when.push(routeActive(options.activeOn));\n }\n\n if (options.visibleOnly) {\n when.push(documentVisible());\n }\n\n if (options.focusedOnly) {\n when.push(windowFocused());\n }\n\n timer(\n options.intervalMs,\n () => {\n invalidate(prefix, {\n markPendingWrite: options.markPendingWrite,\n });\n },\n when.length > 0 ? { when } : undefined\n );\n}\n\nexport function createMutation<TInput, TResult>(\n options: MutationOptions<TInput, TResult>\n): Mutation<TInput, TResult> {\n const instance = getCurrentComponentInstance();\n\n if (!instance) {\n return new MutationCell(options) as unknown as Mutation<TInput, TResult>;\n }\n\n const hookIndex = claimHookIndex(instance, 'mutation');\n ensureMutationCleanup(instance);\n\n const slotStore = getMutationSlotStore(instance);\n const existing = slotStore.get(hookIndex) as\n | MutationCell<TInput, TResult>\n | undefined;\n if (existing) {\n existing.setOptions(options);\n return existing as unknown as Mutation<TInput, TResult>;\n }\n\n const created = new MutationCell(options);\n slotStore.set(hookIndex, created as MutationCell<unknown, unknown>);\n return created as unknown as Mutation<TInput, TResult>;\n}\n"],"mappings":";;;;;;;;AAwNA,MAAM,yBAAyB;AAC/B,MAAM,2BAA2B;AACjC,MAAM,mCAAmB,IAAI,IAAgC;AAC7D,MAAM,uCAAuB,IAAI,QAG/B;AACF,MAAM,0CAA0B,IAAI,QAGlC;AACF,MAAM,yCAAyB,IAAI,QAA2B;AAC9D,MAAM,4CAA4B,IAAI,QAA2B;AAEjE,SAAS,uBAAgD;CACvD,cAAc;AAChB;AAEA,SAAS,gBAAiD;CACxD,OACG,iBAAiB,GAAG,cAGR;AAEjB;AAEA,SAAS,kBACP,UACwB;CACxB,IAAI,QAAQ,qBAAqB,IAAI,QAAQ;CAC7C,IAAI,CAAC,OAAO;EACV,wBAAQ,IAAI,IAAI;EAChB,qBAAqB,IAAI,UAAU,KAAK;CAC1C;CACA,OAAO;AACT;AAEA,SAAS,qBACP,UAC6C;CAC7C,IAAI,QAAQ,wBAAwB,IAAI,QAAQ;CAChD,IAAI,CAAC,OAAO;EACV,wBAAQ,IAAI,IAAI;EAChB,wBAAwB,IAAI,UAAU,KAAK;CAC7C;CACA,OAAO;AACT;AAEA,SAAS,mBAAmB,UAAmC;CAC7D,IAAI,uBAAuB,IAAI,QAAQ,GACrC;CAGF,uBAAuB,IAAI,QAAQ;CACnC,SAAS,WAAW,WAAW;EAC7B,MAAM,QAAQ,qBAAqB,IAAI,QAAQ;EAC/C,IAAI,CAAC,OAAO;GACV,uBAAuB,OAAO,QAAQ;GACtC;EACF;EAEA,KAAK,MAAM,CAAC,WAAW,SAAS,OAC9B,KAAK,KAAK,OAAO,UAAU,SAAS;EAGtC,MAAM,MAAM;EACZ,qBAAqB,OAAO,QAAQ;EACpC,uBAAuB,OAAO,QAAQ;CACxC,CAAC;AACH;AAEA,SAAS,sBAAsB,UAAmC;CAChE,IAAI,0BAA0B,IAAI,QAAQ,GACxC;CAGF,0BAA0B,IAAI,QAAQ;CACtC,SAAS,WAAW,WAAW;EAC7B,MAAM,QAAQ,wBAAwB,IAAI,QAAQ;EAClD,IAAI,CAAC,OAAO;GACV,0BAA0B,OAAO,QAAQ;GACzC;EACF;EAEA,KAAK,MAAM,QAAQ,MAAM,OAAO,GAC9B,KAAK,MAAM;EAGb,MAAM,MAAM;EACZ,wBAAwB,OAAO,QAAQ;EACvC,0BAA0B,OAAO,QAAQ;CAC3C,CAAC;AACH;AAEA,SAAS,aAAa,QAAuC;CAC3D,oCAAoC,MAAM;CAC1C,6BAA6B,MAAM;CACnC,sBAAsB,MAAM;AAC9B;AAEA,SAAS,aAAa,OAAgB,QAA8B;CAClE,OACE,OAAO,WACN,iBAAiB,SAAS,MAAM,SAAS,gBACzC,OAAO,iBAAiB,eACvB,iBAAiB,gBACjB,MAAM,SAAS;AAErB;AAEA,SAAS,wBAAwB,OAAgB,iBAA6B;CAC5E,OAAO,SAAS,IAAI,MAAM,eAAe;AAC3C;AAEA,SAAS,kBAAkB,QAAgB,kBAAiC;CAC1E,iBAAiB;EAAE;EAAQ;CAAiB,CAAC;CAE7C,MAAM,QAAQ,cAAc;CAE5B,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO;EAChC,IAAI,CAAC,IAAI,WAAW,MAAM,GACxB;EAGF,IAAI,kBACF,MAAM,iBAAiB;EAGzB,AAAK,MAAM,QAAQ;CACrB;AACF;AAEA,IAAM,YAAN,MAAmB;CA0BjB,YACE,SACA,KACA,OACA;gBA7BwB,qBAAqB;oBAIF;oBACxB;qBACC;wBACyB;+BACM;+BACrB;mBACZ;oBACC;gCACK,IAAI,IAAoC;sDAClB,IAAI,IAAY;eAEjC;GAC7B,MAAM;GACN,OAAO;GACP,SAAS;GACT,YAAY;GACZ,OAAO;GACP,aAAa;GACb,aAAa;EACf;EAOE,KAAK,UAAU;EACf,KAAK,MAAM;EACX,KAAK,QAAQ;CACf;CAEA,OAAO,UAA6B,WAAyB;EAC3D,IAAI,QAAQ,KAAK,OAAO,IAAI,QAAQ;EACpC,IAAI,CAAC,OAAO;GACV,wBAAQ,IAAI,IAAI;GAChB,KAAK,OAAO,IAAI,UAAU,KAAK;EACjC;EAEA,IAAI,MAAM,IAAI,SAAS,GACrB;EAGF,MAAM,IAAI,SAAS;EACnB,KAAK,cAAc;CACrB;CAEA,OAAO,UAA6B,WAAyB;EAC3D,MAAM,QAAQ,KAAK,OAAO,IAAI,QAAQ;EACtC,IAAI,CAAC,SAAS,CAAC,MAAM,OAAO,SAAS,GACnC;EAGF,KAAK,cAAc;EACnB,IAAI,MAAM,SAAS,GACjB,KAAK,OAAO,OAAO,QAAQ;EAG7B,IAAI,KAAK,cAAc,GACrB,KAAK,QAAQ;CAEjB;CAEA,4BAA4B,SAAgC;EAC1D,MAAM,YAAY,KAAK,uBAAuB,OAAO;EACrD,IAAI,UAAU,WAAW,GACvB;EAGF,MAAM,cAAc,UAAU,KAAK,GAAG;EACtC,IAAI,KAAK,6BAA6B,IAAI,WAAW,GACnD;EAGF,KAAK,6BAA6B,IAAI,WAAW;EAEjD,MAAM,gBACJ,UAAU,WAAW,IACjB,cAAc,UAAU,GAAG,MAC3B,aAAa,UAAU,KAAK,UAAU,KAAK,MAAM,GAAG,EAAE,KAAK,IAAI;EAErE,OAAO,KACL,uDAAuD,KAAK,IAAI,4DACJ,cAAc,+BAE5E;CACF;CAEA,UAAwB;EACtB,IAAI,KAAK,WACP;EAGF,KAAK,YAAY;EACjB,KAAK,YAAY,MAAM;EACvB,KAAK,aAAa;EAClB,KAAK,cAAc;EACnB,KAAK,wBAAwB;EAC7B,KAAK,aAAa;EAClB,KAAK,OAAO,MAAM;EAClB,KAAK,MAAM,OAAO,KAAK,GAAG;EAC1B,KAAK,qBAAqB;CAC5B;CAEA,uBACE,SACwB;EACxB,MAAM,YAAoC,CAAC;EAE3C,IAAI,KAAK,QAAQ,UAAU,QAAQ,OACjC,UAAU,KAAK,OAAO;EAGxB,IAAI,KAAK,QAAQ,iBAAiB,QAAQ,cACxC,UAAU,KAAK,cAAc;EAG/B,IAAI,KAAK,QAAQ,cAAc,QAAQ,WACrC,UAAU,KAAK,WAAW;EAG5B,OAAO;CACT;CAEA,IAAI,OAAiB;EACnB,mBAAmB,KAAK,MAAM;EAC9B,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,QAAmB;EACrB,mBAAmB,KAAK,MAAM;EAC9B,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,UAAmB;EACrB,mBAAmB,KAAK,MAAM;EAC9B,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,aAAsB;EACxB,mBAAmB,KAAK,MAAM;EAC9B,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,QAAiB;EACnB,mBAAmB,KAAK,MAAM;EAC9B,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,cAAgC;EAClC,mBAAmB,KAAK,MAAM;EAC9B,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,cAAuC;EACzC,mBAAmB,KAAK,MAAM;EAC9B,OAAO,KAAK,MAAM;CACpB;CAEA,gBAAsB;EACpB,IACE,KAAK,aACL,KAAK,MAAM,SAAS,QACpB,KAAK,kBACL,KAAK,aAEL;EAGF,KAAK,WAAW,SAAS;CAC3B;CAEA,UAAyB;EACvB,IAAI,KAAK,WACP,OAAO,QAAQ,QAAQ;EAGzB,IAAI,KAAK,gBACP,OAAO,KAAK;EAGd,KAAK,WAAW,QAAQ;EACxB,OAAO,KAAK,kBAAkB,QAAQ,QAAQ;CAChD;CAEA,mBAAyB;EACvB,IAAI,KAAK,WACP;EAGF,IAAI,KAAK,MAAM,SAAS,MACtB;EAGF,KAAK,SAAS;GACZ,SAAS;GACT,OAAO;GACP,YAAY;GACZ,OAAO;GACP,aAAa;GACb,aAAa;EACf,CAAC;CACH;CAEA,WACE,QACM;EACN,IAAI,KAAK,WACP;EAGF,KAAK,cAAc;EACnB,KAAK,iBAAiB,IAAI,SAAe,YAAY;GACnD,KAAK,wBAAwB;GAC7B,gBAAgB,cAAc;IAC5B,KAAK,cAAc;IACnB,IAAI,KAAK,WAAW;KAClB,KAAK,qBAAqB;KAC1B;IACF;IACA,AAAK,KAAK,MAAM,MAAM,EAAE,cAAc;KACpC,KAAK,qBAAqB;IAC5B,CAAC;GACH,CAAC;EACH,CAAC;CACH;CAEA,uBAAqC;EACnC,MAAM,UAAU,KAAK;EACrB,KAAK,iBAAiB;EACtB,KAAK,wBAAwB;EAC7B,UAAU;CACZ;CAEA,SAAiB,MAAoC;EACnD,IAAI,KAAK,WACP;EAGF,KAAK,QAAQ;GACX,GAAG,KAAK;GACR,GAAG;EACL;EACA,aAAa,KAAK,MAAM;CAC1B;CAEA,MAAc,MACZ,QACe;EACf,IAAI,KAAK,WACP;EAGF,KAAK,cAAc;EACnB,MAAM,aAAa,KAAK;EAExB,KAAK,YAAY,MAAM;EACvB,MAAM,aAAa,IAAI,gBAAgB;EACvC,KAAK,aAAa;EAElB,MAAM,UAAU,KAAK,MAAM,SAAS;EACpC,KAAK,SAAS;GACZ,SAAS,CAAC;GACV,YAAY;GACZ,OAAO;GACP,aACE,WAAW,kBACP,kBACA,UACE,eACA;GACR,OAAO;GACP,aAAa;EACf,CAAC;EAED,IAAI;EACJ,IAAI;GACF,WAAW,MAAM,KAAK,QAAQ,MAAM,EAAE,QAAQ,WAAW,OAAO,CAAC;EACnE,SAAS,OAAO;GACd,IACE,KAAK,aACL,KAAK,eAAe,cACpB,KAAK,eAAe,YAEpB;GAGF,IAAI,aAAa,OAAO,WAAW,MAAM,GAAG;IAC1C,KAAK,SACH,UACI;KACE,SAAS;KACT,YAAY;KACZ,OAAO;KACP,aAAa;KACb,OAAO;KACP,aAAa;IACf,IACA;KACE,SAAS;KACT,YAAY;KACZ,OAAO;KACP,aAAa;KACb,OAAO;KACP,aAAa;IACf,CACN;IACA;GACF;GAEA,KAAK,SAAS;IACZ,SAAS;IACT,YAAY;IACZ,OAAO;IACP,aAAa;IACb,OAAO,wBAAwB,OAAO,qBAAqB;IAC3D,aAAa;GACf,CAAC;GACD;EACF;EAEA,IACE,KAAK,aACL,KAAK,eAAe,cACpB,KAAK,eAAe,YAEpB;EAIF,IAAI,EADiB,KAAK,QAAQ,eAAe,QAAQ,KAAK,OAC3C;GACjB,KAAK,SAAS;IACZ,MAAM;IACN,SAAS;IACT,YAAY;IACZ,OAAO;IACP,aAAa;IACb,aAAa;GACf,CAAC;GACD,MAAM,KAAK,UAAU,QAAQ;GAC7B;EACF;EAEA,KAAK,wBAAwB;EAC7B,KAAK,SAAS;GACZ,MAAM;GACN,SAAS;GACT,YAAY;GACZ,OAAO;GACP,aAAa;GACb,OAAO;GACP,aAAa;EACf,CAAC;CACH;CAEA,MAAc,UAAU,MAAwB;EAI9C,IAAI,EAFF,KAAK,QAAQ,YAAY,MAAM,EAAE,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,UAE3C,KAAK,WACvB;EAGF,KAAK,yBAAyB;EAC9B,IAAI,KAAK,wBAAwB,wBAAwB;GACvD,KAAK,SAAS;IACZ,aAAa;IACb,YAAY;IACZ,aAAa;GACf,CAAC;GACD;EACF;EAEA,MAAM,IAAI,SAAe,YACvB,WAAW,SAAS,wBAAwB,CAC9C;EACA,IAAI,KAAK,aAAa,KAAK,MAAM,gBAAgB,SAC/C;EAGF,MAAM,KAAK,QAAQ;CACrB;AACF;AAEA,IAAM,eAAN,MAAoC;CAclC,YAAY,SAA2C;gBAb7B,qBAAqB;oBAIF;oBACxB;eAEoB;GACvC,QAAQ;GACR,OAAO;GACP,QAAQ;EACV;EAGE,KAAK,SAAS,QAAQ;EACtB,KAAK,UAAU,QAAQ;EACvB,KAAK,eAAe,QAAQ;CAC9B;CAEA,WAAW,SAAiD;EAC1D,KAAK,SAAS,QAAQ;EACtB,KAAK,UAAU,QAAQ;EACvB,KAAK,eAAe,QAAQ;CAC9B;CAEA,IAAI,SAAmD;EACrD,mBAAmB,KAAK,MAAM;EAC9B,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,UAAmB;EACrB,mBAAmB,KAAK,MAAM;EAC9B,OAAO,KAAK,MAAM,WAAW;CAC/B;CAEA,IAAI,QAAmB;EACrB,mBAAmB,KAAK,MAAM;EAC9B,OAAO,KAAK,MAAM;CACpB;CAEA,IAAI,SAAyB;EAC3B,mBAAmB,KAAK,MAAM;EAC9B,OAAO,KAAK,MAAM;CACpB;CAEA,SAAiB,MAA8C;EAC7D,KAAK,QAAQ;GACX,GAAG,KAAK;GACR,GAAG;EACL;EACA,aAAa,KAAK,MAAM;CAC1B;CAEA,MAAM,QAAQ,OAAiC;EAC7C,KAAK,cAAc;EACnB,MAAM,aAAa,KAAK;EAExB,KAAK,YAAY,MAAM;EACvB,MAAM,aAAa,IAAI,gBAAgB;EACvC,KAAK,aAAa;EAElB,KAAK,SAAS;GAAE,QAAQ;GAAW,OAAO;GAAM,QAAQ;EAAK,CAAC;EAE9D,IAAI;EACJ,IAAI;GACF,SAAS,MAAM,KAAK,OAAO,OAAO,EAAE,QAAQ,WAAW,OAAO,CAAC;EACjE,SAAS,OAAO;GACd,IAAI,KAAK,eAAe,cAAc,KAAK,eAAe,YACxD,MAAM;GAGR,IAAI,aAAa,OAAO,WAAW,MAAM,GAAG;IAC1C,KAAK,SAAS;KAAE,QAAQ;KAAQ,OAAO;IAAK,CAAC;IAC7C,MAAM;GACR;GAEA,KAAK,SAAS;IACZ,QAAQ;IACR,OAAO,wBAAwB,OAAO,wBAAwB;GAChE,CAAC;GACD,MAAM;EACR;EAEA,IAAI,KAAK,eAAe,cAAc,KAAK,eAAe,YACxD,OAAO;EAGT,KAAK,SAAS;GAAE,QAAQ;GAAW,OAAO;GAAM;EAAO,CAAC;EAExD,IAAI,KAAK,iBAAiB,cAAc;GACtC,MAAM,WAAW,KAAK,UAAU,OAAO,MAAM,KAAK,CAAC;GACnD,KAAK,MAAM,UAAU,IAAI,IAAI,QAAQ,GACnC,kBAAkB,QAAQ,IAAI;EAElC;EAEA,OAAO;CACT;CAEA,QAAc;EACZ,IAAI,KAAK,MAAM,WAAW,WACxB;EAGF,KAAK,cAAc;EACnB,KAAK,YAAY,MAAM;EACvB,KAAK,aAAa;EAClB,KAAK,SAAS;GAAE,QAAQ;GAAQ,OAAO;GAAM,QAAQ;EAAK,CAAC;CAC7D;CAEA,QAAc;EACZ,KAAK,cAAc;EACnB,KAAK,YAAY,MAAM;EACvB,KAAK,aAAa;EAClB,KAAK,SAAS;GAAE,QAAQ;GAAQ,OAAO;GAAM,QAAQ;EAAK,CAAC;CAC7D;AACF;AAEA,SAAgB,YAA0B,SAAoC;CAC5E,MAAM,WAAW,4BAA4B;CAC7C,MAAM,QAAQ,cAAc;CAC5B,IAAI,CAAC,UAAU;EACb,IAAI,OAAO,MAAM,IAAI,QAAQ,GAAG;EAChC,IAAI,CAAC,MAAM;GACT,OAAO,IAAI,UAAU,SAAS,QAAQ,KAAK,KAAK;GAChD,MAAM,IAAI,QAAQ,KAAK,IAA0B;GACjD,KAAK,cAAc;EACrB,OACE,KAAK,4BAA4B,OAAO;EAE1C,OAAO;CACT;CAEA,MAAM,YAAY,eAAe,UAAU,OAAO;CAClD,mBAAmB,QAAQ;CAE3B,MAAM,YAAY,kBAAkB,QAAQ;CAC5C,MAAM,eAAe,UAAU,IAAI,SAAS;CAC5C,IAAI,gBAAgB,aAAa,QAAQ,QAAQ,KAAK;EACpD,aAAc,KAAsB,4BAA4B,OAAO;EACvE,OAAO,aAAa;CACtB;CAEA,IAAI,cACF,aAAa,KAAK,OAAO,UAAU,SAAS;CAG9C,IAAI,OAAO,MAAM,IAAI,QAAQ,GAAG;CAChC,IAAI,CAAC,MAAM;EACT,OAAO,IAAI,UAAU,SAAS,QAAQ,KAAK,KAAK;EAChD,MAAM,IAAI,QAAQ,KAAK,IAA0B;EACjD,KAAK,cAAc;CACrB,OACE,KAAK,4BAA4B,OAAO;CAG1C,UAAU,IAAI,WAAW;EACvB,KAAK,QAAQ;EACP;CACR,CAAC;CACD,KAAK,OAAO,UAAU,SAAS;CAC/B,OAAO;AACT;AAEA,SAAgB,WAAW,QAAgB,SAAmC;CAC5E,kBAAkB,QAAQ,SAAS,oBAAoB,KAAK;AAC9D;AAEA,SAAS,wBAAwB,MAAsB;CACrD,IAAI,OAAO,MAAM,IAAI,GACnB,OAAO;CAGT,IAAI,OAAO,GAAG,MAAM,EAAE,GACpB,OAAO;CAGT,IAAI,SAAS,UACX,OAAO;CAGT,IAAI,SAAS,WACX,OAAO;CAGT,OAAO,OAAO,IAAI;AACpB;AAEA,SAAS,sBAAsB,MAA4B;CACzD,IAAI,OAAQ,SAAqB,UAC/B,MAAM,IAAI,MAAM,kDAAkD;CAGpE,IAAI,SAAS,MACX,OAAO;CAGT,IAAI,SAAS,QACX,OAAO;CAGT,QAAQ,OAAO,MAAf;EACE,KAAK,UACH,OAAO,KAAK,mBAAmB,IAAI;EACrC,KAAK,UACH,OAAO,KAAK,wBAAwB,IAAI;EAC1C,KAAK,WACH,OAAO,KAAK,OAAO,MAAM;EAC3B,KAAK;GACH,IAAI,MAAM,QAAQ,IAAI,GACpB,OAAO,KAAK,KAAK,KAAK,SAAS,sBAAsB,IAAI,CAAC,EAAE,KAAK,GAAG,EAAE;GAGxE,OAAO,KAAK,OAAO,KAAK,IAAI,EACzB,KAAK,EACL,KACE,QACC,GAAG,mBAAmB,GAAG,EAAE,GAAG,sBAAsB,KAAK,IAAI,GACjE,EACC,KAAK,GAAG,EAAE;EACf,SACE,OAAO,OAAO,IAAI;CACtB;AACF;AAEA,SAAgB,WAAW,WAA+B;CACxD,IAAI,OAAO,cAAc,YAAY,UAAU,KAAK,EAAE,WAAW,GAC/D,MAAM,IAAI,MAAM,4DAA4D;CAG9E,MAAM,SAAS,UACb,CAAC,WAAW,GAAG,KAAK,EAAE,IAAI,qBAAqB,EAAE,KAAK,GAAG,IAAI;CAE/D,OAAO;EACL,IAAI,GAAG,OAAO;GACZ,OAAO,MAAM,KAAK;EACpB;EAEA,OAAO,GAAG,OAAO;GACf,OAAO,MAAM,KAAK;EACpB;EAEA,WAAW,OAAO,SAAS;GACzB,WAAW,MAAM,KAAK,GAAG,OAAO;EAClC;CACF;AACF;AAEA,MAAM,uCACJ;AAEF,SAAgB,qBACd,QACA,SACM;CACN,IACE,CAAC,WACD,OAAO,YAAY,YACnB,OAAO,QAAQ,eAAe,YAC9B,CAAC,OAAO,SAAS,QAAQ,UAAU,GAEnC,MAAM,IAAI,MAAM,oCAAoC;CAGtD,MAAM,OAA4B,CAAC;CAEnC,IAAI,QAAQ,UACV,KAAK,KAAK,YAAY,QAAQ,QAAQ,CAAC;CAGzC,IAAI,QAAQ,aACV,KAAK,KAAK,gBAAgB,CAAC;CAG7B,IAAI,QAAQ,aACV,KAAK,KAAK,cAAc,CAAC;CAG3B,MACE,QAAQ,kBACF;EACJ,WAAW,QAAQ,EACjB,kBAAkB,QAAQ,iBAC5B,CAAC;CACH,GACA,KAAK,SAAS,IAAI,EAAE,KAAK,IAAI,MAC/B;AACF;AAEA,SAAgB,eACd,SAC2B;CAC3B,MAAM,WAAW,4BAA4B;CAE7C,IAAI,CAAC,UACH,OAAO,IAAI,aAAa,OAAO;CAGjC,MAAM,YAAY,eAAe,UAAU,UAAU;CACrD,sBAAsB,QAAQ;CAE9B,MAAM,YAAY,qBAAqB,QAAQ;CAC/C,MAAM,WAAW,UAAU,IAAI,SAAS;CAGxC,IAAI,UAAU;EACZ,SAAS,WAAW,OAAO;EAC3B,OAAO;CACT;CAEA,MAAM,UAAU,IAAI,aAAa,OAAO;CACxC,UAAU,IAAI,WAAW,OAAyC;CAClE,OAAO;AACT"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/data/index.ts"],"sourcesContent":["import { globalScheduler } from '../runtime/scheduler';\nimport {\n claimHookIndex,\n getCurrentComponentInstance,\n type ComponentInstance,\n} from '../runtime/component';\nimport {\n markReadableDerivedSubscribersDirty,\n markReactivePropsDirtySource,\n notifyReadableReaders,\n recordReadableRead,\n type ReadableSource,\n} from '../runtime/readable';\nimport { getRenderContext } from '../ssr/context';\nimport { logger } from '../dev/logger';\nimport { emitInvalidation } from './invalidation-listeners';\nimport {\n documentVisible,\n routeActive,\n timer,\n windowFocused,\n type ActivityPredicate,\n} from '../runtime/operations';\n\nexport type QueryConsistency =\n | 'fresh'\n | 'stale'\n | 'refreshing'\n | 'pending-write';\n\nexport type QueryStaleReason = 'aborted' | 'error' | 'inconsistent';\n\nexport interface InvalidateOptions {\n markPendingWrite?: boolean;\n}\n\nexport interface InvalidateOnIntervalOptions extends InvalidateOptions {\n intervalMs: number;\n activeOn?: string | readonly string[];\n visibleOnly?: boolean;\n focusedOnly?: boolean;\n}\n\nexport type QueryKeyPart =\n | string\n | number\n | boolean\n | null\n | undefined\n | readonly QueryKeyPart[]\n | { readonly [key: string]: QueryKeyPart };\n\nexport interface QueryScope {\n key(...parts: QueryKeyPart[]): string;\n prefix(...parts: QueryKeyPart[]): string;\n invalidate(parts: readonly QueryKeyPart[], options?: InvalidateOptions): void;\n}\n\ntype QueryControls = {\n refresh(): Promise<void>;\n};\n\ntype QueryLoading = {\n data: null;\n error: null;\n loading: true;\n refreshing: false;\n stale: false;\n consistency: 'fresh';\n staleReason: null;\n};\n\ntype QueryFresh<T> = {\n data: T;\n error: null;\n loading: false;\n refreshing: false;\n stale: false;\n consistency: 'fresh';\n staleReason: null;\n};\n\ntype QueryRefreshing<T> = {\n data: T;\n error: null;\n loading: false;\n refreshing: true;\n stale: true;\n consistency: 'refreshing';\n staleReason: null;\n};\n\ntype QueryPendingWrite<T> = {\n data: T;\n error: null;\n loading: false;\n refreshing: true;\n stale: true;\n consistency: 'pending-write';\n staleReason: null;\n};\n\ntype QueryStaleValue<T> = {\n data: T;\n error: null;\n loading: false;\n refreshing: false;\n stale: true;\n consistency: 'stale';\n staleReason: 'aborted' | 'inconsistent';\n};\n\ntype QueryStaleErrorWithValue<T> = {\n data: T;\n error: {};\n loading: false;\n refreshing: false;\n stale: true;\n consistency: 'stale';\n staleReason: 'error';\n};\n\ntype QueryStaleError = {\n data: null;\n error: {};\n loading: false;\n refreshing: false;\n stale: true;\n consistency: 'stale';\n staleReason: 'error';\n};\n\nexport type Query<T extends {}> = QueryControls &\n (\n | QueryLoading\n | QueryFresh<T>\n | QueryRefreshing<T>\n | QueryPendingWrite<T>\n | QueryStaleValue<T>\n | QueryStaleErrorWithValue<T>\n | QueryStaleError\n );\n\ntype MutationControls<TInput, TResult> = {\n execute(input: TInput): Promise<TResult>;\n abort(): void;\n reset(): void;\n};\n\ntype MutationIdle = {\n status: 'idle';\n pending: false;\n error: null;\n result: null;\n};\n\ntype MutationPending = {\n status: 'pending';\n pending: true;\n error: null;\n result: null;\n};\n\ntype MutationSuccess<TResult> = {\n status: 'success';\n pending: false;\n error: null;\n result: TResult;\n};\n\ntype MutationError = {\n status: 'error';\n pending: false;\n error: {};\n result: null;\n};\n\nexport type Mutation<TInput, TResult> = MutationControls<TInput, TResult> &\n (MutationIdle | MutationPending | MutationSuccess<TResult> | MutationError);\n\ntype MutationRecord<TResult> = {\n status: 'idle' | 'pending' | 'success' | 'error';\n error: {} | null;\n result: TResult | null;\n};\n\ntype QueryOptions<T> = {\n key: string;\n fetch: (ctx: { signal: AbortSignal }) => Promise<T>;\n isConsistent?: (data: T) => boolean;\n reconcile?: (data: T, ctx: { key: string }) => Promise<boolean> | boolean;\n};\n\ntype MutationOptions<TInput, TResult> = {\n action: (input: TInput, ctx: { signal: AbortSignal }) => Promise<TResult>;\n affects?: (input: TInput, result: TResult) => string[];\n afterSuccess?: 'invalidate';\n};\n\ntype QueryState<T> = {\n data: T | null;\n error: {} | null;\n loading: boolean;\n refreshing: boolean;\n stale: boolean;\n consistency: QueryConsistency;\n staleReason: QueryStaleReason | null;\n};\n\ntype QuerySlot = {\n key: string;\n cell: QueryCell<unknown>;\n};\n\ntype QueryDefinitionField = 'fetch' | 'isConsistent' | 'reconcile';\n\nconst RECONCILE_MAX_ATTEMPTS = 3;\nconst RECONCILE_RETRY_DELAY_MS = 25;\nconst globalQueryCache = new Map<string, QueryCell<unknown>>();\nconst querySlotsByInstance = new WeakMap<\n ComponentInstance,\n Map<number, QuerySlot>\n>();\nconst mutationSlotsByInstance = new WeakMap<\n ComponentInstance,\n Map<number, MutationCell<unknown, unknown>>\n>();\nconst queryCleanupRegistered = new WeakSet<ComponentInstance>();\nconst mutationCleanupRegistered = new WeakSet<ComponentInstance>();\n\nfunction createReadableSource(): ReadableSource<unknown> {\n return (() => undefined) as ReadableSource<unknown>;\n}\n\nfunction getQueryCache(): Map<string, QueryCell<unknown>> {\n return (\n (getRenderContext()?.queryCache as Map<\n string,\n QueryCell<unknown>\n > | null) ?? globalQueryCache\n );\n}\n\nfunction getQuerySlotStore(\n instance: ComponentInstance\n): Map<number, QuerySlot> {\n let store = querySlotsByInstance.get(instance);\n if (!store) {\n store = new Map();\n querySlotsByInstance.set(instance, store);\n }\n return store;\n}\n\nfunction getMutationSlotStore(\n instance: ComponentInstance\n): Map<number, MutationCell<unknown, unknown>> {\n let store = mutationSlotsByInstance.get(instance);\n if (!store) {\n store = new Map();\n mutationSlotsByInstance.set(instance, store);\n }\n return store;\n}\n\nfunction ensureQueryCleanup(instance: ComponentInstance): void {\n if (queryCleanupRegistered.has(instance)) {\n return;\n }\n\n queryCleanupRegistered.add(instance);\n instance.cleanupFns.push(() => {\n const slots = querySlotsByInstance.get(instance);\n if (!slots) {\n queryCleanupRegistered.delete(instance);\n return;\n }\n\n for (const [hookIndex, slot] of slots) {\n slot.cell.detach(instance, hookIndex);\n }\n\n slots.clear();\n querySlotsByInstance.delete(instance);\n queryCleanupRegistered.delete(instance);\n });\n}\n\nfunction ensureMutationCleanup(instance: ComponentInstance): void {\n if (mutationCleanupRegistered.has(instance)) {\n return;\n }\n\n mutationCleanupRegistered.add(instance);\n instance.cleanupFns.push(() => {\n const slots = mutationSlotsByInstance.get(instance);\n if (!slots) {\n mutationCleanupRegistered.delete(instance);\n return;\n }\n\n for (const cell of slots.values()) {\n cell.abort();\n }\n\n slots.clear();\n mutationSlotsByInstance.delete(instance);\n mutationCleanupRegistered.delete(instance);\n });\n}\n\nfunction notifySource(source: ReadableSource<unknown>): void {\n markReadableDerivedSubscribersDirty(source);\n markReactivePropsDirtySource(source);\n notifyReadableReaders(source);\n}\n\nfunction isAbortError(error: unknown, signal: AbortSignal): boolean {\n return (\n signal.aborted ||\n (error instanceof Error && error.name === 'AbortError') ||\n (typeof DOMException !== 'undefined' &&\n error instanceof DOMException &&\n error.name === 'AbortError')\n );\n}\n\nfunction normalizeAsyncDataError(error: unknown, fallbackMessage: string): {} {\n return error ?? new Error(fallbackMessage);\n}\n\nfunction invalidateQueries(prefix: string, markPendingWrite: boolean): void {\n emitInvalidation({ prefix, markPendingWrite });\n\n const cache = getQueryCache();\n\n for (const [key, query] of cache) {\n if (!key.startsWith(prefix)) {\n continue;\n }\n\n if (markPendingWrite) {\n query.markPendingWrite();\n }\n\n void query.refresh();\n }\n}\n\nclass QueryCell<T> {\n private readonly source = createReadableSource();\n private readonly key: string;\n private readonly cache: Map<string, QueryCell<unknown>>;\n private options: QueryOptions<T>;\n private controller: AbortController | null = null;\n private generation = 0;\n private startQueued = false;\n private pendingRefresh: Promise<void> | null = null;\n private pendingRefreshResolve: (() => void) | null = null;\n private reconcileAttemptCount = 0;\n private destroyed = false;\n private ownerCount = 0;\n private readonly owners = new Map<ComponentInstance, Set<number>>();\n private readonly warnedDefinitionConflictKeys = new Set<string>();\n\n private state: QueryState<T> = {\n data: null,\n error: null,\n loading: true,\n refreshing: false,\n stale: false,\n consistency: 'fresh',\n staleReason: null,\n };\n\n constructor(\n options: QueryOptions<T>,\n key: string,\n cache: Map<string, QueryCell<unknown>>\n ) {\n this.options = options;\n this.key = key;\n this.cache = cache;\n }\n\n attach(instance: ComponentInstance, hookIndex: number): void {\n let hooks = this.owners.get(instance);\n if (!hooks) {\n hooks = new Set();\n this.owners.set(instance, hooks);\n }\n\n if (hooks.has(hookIndex)) {\n return;\n }\n\n hooks.add(hookIndex);\n this.ownerCount += 1;\n }\n\n detach(instance: ComponentInstance, hookIndex: number): void {\n const hooks = this.owners.get(instance);\n if (!hooks || !hooks.delete(hookIndex)) {\n return;\n }\n\n this.ownerCount -= 1;\n if (hooks.size === 0) {\n this.owners.delete(instance);\n }\n\n if (this.ownerCount <= 0) {\n this.destroy();\n }\n }\n\n warnOnConflictingDefinition(options: QueryOptions<T>): void {\n const conflicts = this.getDefinitionConflicts(options);\n if (conflicts.length === 0) {\n return;\n }\n\n const conflictKey = conflicts.join(',');\n if (this.warnedDefinitionConflictKeys.has(conflictKey)) {\n return;\n }\n\n this.warnedDefinitionConflictKeys.add(conflictKey);\n\n const callbackLabel =\n conflicts.length === 1\n ? `callback \\`${conflicts[0]}\\``\n : `callbacks ${conflicts.map((field) => `\\`${field}\\``).join(', ')}`;\n\n logger.warn(\n `[askr] Conflicting shared query definition for key \"${this.key}\". ` +\n `Shared queries are canonical by key, so reuse the same ${callbackLabel} ` +\n 'for every reader of that key.'\n );\n }\n\n private destroy(): void {\n if (this.destroyed) {\n return;\n }\n\n this.destroyed = true;\n this.controller?.abort();\n this.controller = null;\n this.startQueued = false;\n this.reconcileAttemptCount = 0;\n this.ownerCount = 0;\n this.owners.clear();\n this.cache.delete(this.key);\n this.finishPendingRefresh();\n }\n\n private getDefinitionConflicts(\n options: QueryOptions<T>\n ): QueryDefinitionField[] {\n const conflicts: QueryDefinitionField[] = [];\n\n if (this.options.fetch !== options.fetch) {\n conflicts.push('fetch');\n }\n\n if (this.options.isConsistent !== options.isConsistent) {\n conflicts.push('isConsistent');\n }\n\n if (this.options.reconcile !== options.reconcile) {\n conflicts.push('reconcile');\n }\n\n return conflicts;\n }\n\n get data(): T | null {\n recordReadableRead(this.source);\n return this.state.data;\n }\n\n get error(): {} | null {\n recordReadableRead(this.source);\n return this.state.error;\n }\n\n get loading(): boolean {\n recordReadableRead(this.source);\n return this.state.loading;\n }\n\n get refreshing(): boolean {\n recordReadableRead(this.source);\n return this.state.refreshing;\n }\n\n get stale(): boolean {\n recordReadableRead(this.source);\n return this.state.stale;\n }\n\n get consistency(): QueryConsistency {\n recordReadableRead(this.source);\n return this.state.consistency;\n }\n\n get staleReason(): QueryStaleReason | null {\n recordReadableRead(this.source);\n return this.state.staleReason;\n }\n\n ensureStarted(): void {\n if (\n this.destroyed ||\n this.state.data !== null ||\n this.pendingRefresh ||\n this.startQueued\n ) {\n return;\n }\n\n this.queueStart('initial');\n }\n\n refresh(): Promise<void> {\n if (this.destroyed) {\n return Promise.resolve();\n }\n\n if (this.pendingRefresh) {\n return this.pendingRefresh;\n }\n\n this.queueStart('manual');\n return this.pendingRefresh ?? Promise.resolve();\n }\n\n markPendingWrite(): void {\n if (this.destroyed) {\n return;\n }\n\n if (this.state.data === null) {\n return;\n }\n\n this.setState({\n loading: false,\n error: null,\n refreshing: true,\n stale: true,\n consistency: 'pending-write',\n staleReason: null,\n });\n }\n\n private queueStart(\n reason: 'initial' | 'manual' | 'invalidate' | 'pending-write'\n ): void {\n if (this.destroyed) {\n return;\n }\n\n this.startQueued = true;\n this.pendingRefresh = new Promise<void>((resolve) => {\n this.pendingRefreshResolve = resolve;\n globalScheduler.enqueue(() => {\n this.startQueued = false;\n if (this.destroyed) {\n this.finishPendingRefresh();\n return;\n }\n void this.start(reason).finally(() => {\n this.finishPendingRefresh();\n });\n });\n });\n }\n\n private finishPendingRefresh(): void {\n const resolve = this.pendingRefreshResolve;\n this.pendingRefresh = null;\n this.pendingRefreshResolve = null;\n resolve?.();\n }\n\n private setState(next: Partial<QueryState<T>>): void {\n if (this.destroyed) {\n return;\n }\n\n this.state = {\n ...this.state,\n ...next,\n };\n notifySource(this.source);\n }\n\n private async start(\n reason: 'initial' | 'manual' | 'invalidate' | 'pending-write'\n ): Promise<void> {\n if (this.destroyed) {\n return;\n }\n\n this.generation += 1;\n const generation = this.generation;\n\n this.controller?.abort();\n const controller = new AbortController();\n this.controller = controller;\n\n const hasData = this.state.data !== null;\n this.setState({\n loading: !hasData,\n refreshing: hasData,\n stale: hasData,\n consistency:\n reason === 'pending-write'\n ? 'pending-write'\n : hasData\n ? 'refreshing'\n : 'fresh',\n error: null,\n staleReason: null,\n });\n\n let nextData: T;\n try {\n nextData = await this.options.fetch({ signal: controller.signal });\n } catch (error) {\n if (\n this.destroyed ||\n this.generation !== generation ||\n this.controller !== controller\n ) {\n return;\n }\n\n if (isAbortError(error, controller.signal)) {\n this.setState(\n hasData\n ? {\n loading: false,\n refreshing: false,\n stale: true,\n consistency: 'stale',\n error: null,\n staleReason: 'aborted',\n }\n : {\n loading: true,\n refreshing: false,\n stale: false,\n consistency: 'fresh',\n error: null,\n staleReason: null,\n }\n );\n return;\n }\n\n this.setState({\n loading: false,\n refreshing: false,\n stale: true,\n consistency: 'stale',\n error: normalizeAsyncDataError(error, 'Unknown query error'),\n staleReason: 'error',\n });\n return;\n }\n\n if (\n this.destroyed ||\n this.generation !== generation ||\n this.controller !== controller\n ) {\n return;\n }\n\n const isConsistent = this.options.isConsistent?.(nextData) ?? true;\n if (!isConsistent) {\n this.setState({\n data: nextData,\n loading: false,\n refreshing: false,\n stale: true,\n consistency: 'stale',\n staleReason: 'inconsistent',\n });\n await this.reconcile(nextData);\n return;\n }\n\n this.reconcileAttemptCount = 0;\n this.setState({\n data: nextData,\n loading: false,\n refreshing: false,\n stale: false,\n consistency: 'fresh',\n error: null,\n staleReason: null,\n });\n }\n\n private async reconcile(data: T): Promise<void> {\n const shouldRetry =\n this.options.reconcile?.(data, { key: this.options.key }) ?? false;\n\n if (!shouldRetry || this.destroyed) {\n return;\n }\n\n this.reconcileAttemptCount += 1;\n if (this.reconcileAttemptCount > RECONCILE_MAX_ATTEMPTS) {\n this.setState({\n consistency: 'stale',\n refreshing: false,\n staleReason: 'inconsistent',\n });\n return;\n }\n\n await new Promise<void>((resolve) =>\n setTimeout(resolve, RECONCILE_RETRY_DELAY_MS)\n );\n if (this.destroyed || this.state.consistency === 'fresh') {\n return;\n }\n\n await this.refresh();\n }\n}\n\nclass MutationCell<TInput, TResult> {\n private readonly source = createReadableSource();\n private action: MutationOptions<TInput, TResult>['action'];\n private affects?: MutationOptions<TInput, TResult>['affects'];\n private afterSuccess?: MutationOptions<TInput, TResult>['afterSuccess'];\n private controller: AbortController | null = null;\n private generation = 0;\n\n private state: MutationRecord<TResult> = {\n status: 'idle',\n error: null,\n result: null,\n };\n\n constructor(options: MutationOptions<TInput, TResult>) {\n this.action = options.action;\n this.affects = options.affects;\n this.afterSuccess = options.afterSuccess;\n }\n\n setOptions(options: MutationOptions<TInput, TResult>): void {\n this.action = options.action;\n this.affects = options.affects;\n this.afterSuccess = options.afterSuccess;\n }\n\n get status(): 'idle' | 'pending' | 'success' | 'error' {\n recordReadableRead(this.source);\n return this.state.status;\n }\n\n get pending(): boolean {\n recordReadableRead(this.source);\n return this.state.status === 'pending';\n }\n\n get error(): {} | null {\n recordReadableRead(this.source);\n return this.state.error;\n }\n\n get result(): TResult | null {\n recordReadableRead(this.source);\n return this.state.result;\n }\n\n private setState(next: Partial<MutationRecord<TResult>>): void {\n this.state = {\n ...this.state,\n ...next,\n };\n notifySource(this.source);\n }\n\n async execute(input: TInput): Promise<TResult> {\n this.generation += 1;\n const generation = this.generation;\n\n this.controller?.abort();\n const controller = new AbortController();\n this.controller = controller;\n\n this.setState({ status: 'pending', error: null, result: null });\n\n let result: TResult;\n try {\n result = await this.action(input, { signal: controller.signal });\n } catch (error) {\n if (this.generation !== generation || this.controller !== controller) {\n throw error;\n }\n\n if (isAbortError(error, controller.signal)) {\n this.setState({ status: 'idle', error: null });\n throw error;\n }\n\n this.setState({\n status: 'error',\n error: normalizeAsyncDataError(error, 'Unknown mutation error'),\n });\n throw error;\n }\n\n if (this.generation !== generation || this.controller !== controller) {\n return result;\n }\n\n this.setState({ status: 'success', error: null, result });\n\n if (this.afterSuccess === 'invalidate') {\n const prefixes = this.affects?.(input, result) ?? [];\n for (const prefix of new Set(prefixes)) {\n invalidateQueries(prefix, true);\n }\n }\n\n return result;\n }\n\n abort(): void {\n if (this.state.status !== 'pending') {\n return;\n }\n\n this.generation += 1;\n this.controller?.abort();\n this.controller = null;\n this.setState({ status: 'idle', error: null, result: null });\n }\n\n reset(): void {\n this.generation += 1;\n this.controller?.abort();\n this.controller = null;\n this.setState({ status: 'idle', error: null, result: null });\n }\n}\n\nexport function createQuery<T extends {}>(options: QueryOptions<T>): Query<T> {\n const instance = getCurrentComponentInstance();\n const cache = getQueryCache();\n if (!instance) {\n let cell = cache.get(options.key) as QueryCell<T> | undefined;\n if (!cell) {\n cell = new QueryCell(options, options.key, cache);\n cache.set(options.key, cell as QueryCell<unknown>);\n cell.ensureStarted();\n } else {\n cell.warnOnConflictingDefinition(options);\n }\n return cell as unknown as Query<T>;\n }\n\n const hookIndex = claimHookIndex(instance, 'query');\n ensureQueryCleanup(instance);\n\n const slotStore = getQuerySlotStore(instance);\n const existingSlot = slotStore.get(hookIndex);\n if (existingSlot && existingSlot.key === options.key) {\n (existingSlot.cell as QueryCell<T>).warnOnConflictingDefinition(options);\n return existingSlot.cell as unknown as Query<T>;\n }\n\n if (existingSlot) {\n existingSlot.cell.detach(instance, hookIndex);\n }\n\n let cell = cache.get(options.key) as QueryCell<T> | undefined;\n if (!cell) {\n cell = new QueryCell(options, options.key, cache);\n cache.set(options.key, cell as QueryCell<unknown>);\n cell.ensureStarted();\n } else {\n cell.warnOnConflictingDefinition(options);\n }\n\n slotStore.set(hookIndex, {\n key: options.key,\n cell: cell as QueryCell<unknown>,\n });\n cell.attach(instance, hookIndex);\n return cell as unknown as Query<T>;\n}\n\nexport function invalidate(prefix: string, options?: InvalidateOptions): void {\n invalidateQueries(prefix, options?.markPendingWrite ?? false);\n}\n\nfunction serializeQueryKeyNumber(part: number): string {\n if (Number.isNaN(part)) {\n return 'NaN';\n }\n\n if (Object.is(part, -0)) {\n return '-0';\n }\n\n if (part === Infinity) {\n return 'Infinity';\n }\n\n if (part === -Infinity) {\n return '-Infinity';\n }\n\n return String(part);\n}\n\nfunction serializeQueryKeyPart(part: QueryKeyPart): string {\n if (typeof (part as unknown) === 'symbol') {\n throw new Error('[Askr] queryScope() key parts cannot be symbols.');\n }\n\n if (part === null) {\n return 'null';\n }\n\n if (part === undefined) {\n return 'undefined';\n }\n\n switch (typeof part) {\n case 'string':\n return `s=${encodeURIComponent(part)}`;\n case 'number':\n return `n=${serializeQueryKeyNumber(part)}`;\n case 'boolean':\n return `b=${part ? '1' : '0'}`;\n case 'object':\n if (Array.isArray(part)) {\n return `a[${part.map((item) => serializeQueryKeyPart(item)).join(',')}]`;\n }\n\n return `o{${Object.keys(part)\n .sort()\n .map(\n (key) =>\n `${encodeURIComponent(key)}=${serializeQueryKeyPart(part[key])}`\n )\n .join(',')}}`;\n default:\n return String(part);\n }\n}\n\nexport function queryScope(namespace: string): QueryScope {\n if (typeof namespace !== 'string' || namespace.trim().length === 0) {\n throw new Error(\n '[Askr] queryScope() requires a non-empty namespace string.'\n );\n }\n\n const build = (parts: readonly QueryKeyPart[]): string =>\n [namespace, ...parts].map(serializeQueryKeyPart).join(':') + ':';\n\n return {\n key(...parts) {\n return build(parts);\n },\n\n prefix(...parts) {\n return build(parts);\n },\n\n invalidate(parts, options) {\n invalidate(build(parts), options);\n },\n };\n}\n\nconst INVALIDATE_ON_INTERVAL_OPTIONS_ERROR =\n '[Askr] invalidateOnInterval() requires an options object with a finite numeric intervalMs.';\n\nexport function invalidateOnInterval(\n prefix: string,\n options: InvalidateOnIntervalOptions\n): void {\n if (\n !options ||\n typeof options !== 'object' ||\n typeof options.intervalMs !== 'number' ||\n !Number.isFinite(options.intervalMs)\n ) {\n throw new Error(INVALIDATE_ON_INTERVAL_OPTIONS_ERROR);\n }\n\n const when: ActivityPredicate[] = [];\n\n if (options.activeOn) {\n when.push(routeActive(options.activeOn));\n }\n\n if (options.visibleOnly) {\n when.push(documentVisible());\n }\n\n if (options.focusedOnly) {\n when.push(windowFocused());\n }\n\n timer(\n options.intervalMs,\n () => {\n invalidate(prefix, {\n markPendingWrite: options.markPendingWrite,\n });\n },\n when.length > 0 ? { when } : undefined\n );\n}\n\nexport function createMutation<TInput, TResult>(\n options: MutationOptions<TInput, TResult>\n): Mutation<TInput, TResult> {\n const instance = getCurrentComponentInstance();\n\n if (!instance) {\n return new MutationCell(options) as unknown as Mutation<TInput, TResult>;\n }\n\n const hookIndex = claimHookIndex(instance, 'mutation');\n ensureMutationCleanup(instance);\n\n const slotStore = getMutationSlotStore(instance);\n const existing = slotStore.get(hookIndex) as\n | MutationCell<TInput, TResult>\n | undefined;\n if (existing) {\n existing.setOptions(options);\n return existing as unknown as Mutation<TInput, TResult>;\n }\n\n const created = new MutationCell(options);\n slotStore.set(hookIndex, created as MutationCell<unknown, unknown>);\n return created as unknown as Mutation<TInput, TResult>;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"icon.js","names":[],"sources":["../../../src/foundations/icon/icon.tsx"],"sourcesContent":["/* eslint-disable askr/no-hardcoded-theme-tokens -- Icon exposes a stable CSS variable contract consumed by themes. */\nimport { jsx, jsxs } from '../../jsx/jsx-runtime';\nimport type { JSXElement } from '../../common/jsx';\nimport type { IconProps, IconSizeToken, IconStyleObject } from './icon.types';\n\nconst ICON_SIZE_TOKENS: readonly IconSizeToken[] = ['sm', 'md', 'lg', 'xl'];\n\nexport function isIconSizeToken(value: unknown): value is IconSizeToken {\n return (\n typeof value === 'string' &&\n ICON_SIZE_TOKENS.includes(value as IconSizeToken)\n );\n}\n\nexport function normalizeIconSizeValue(size: number | string): string {\n if (typeof size === 'number') return `${size}px`;\n return size;\n}\n\nexport function resolveIconSizeVariable(size: number | string): string {\n if (isIconSizeToken(size)) {\n return `var(--ak-icon-size-${size}, var(--ak-icon-size-md, 1.25rem))`;\n }\n return normalizeIconSizeValue(size);\n}\n\nexport function resolveIconStrokeWidthVariable(\n strokeWidth: number,\n sizeToken: IconSizeToken | undefined\n): string {\n if (sizeToken) {\n return `var(--ak-icon-stroke-width-${sizeToken}, var(--ak-icon-stroke-width-md, ${strokeWidth}))`;\n }\n return `var(--ak-icon-stroke-width-md, ${strokeWidth})`;\n}\n\nfunction camelToKebab(key: string): string {\n return key.replace(/([A-Z])/g, (match) => `-${match.toLowerCase()}`);\n}\n\nexport function serializeIconStyle(\n style: string | IconStyleObject | undefined\n): string {\n if (!style) return '';\n if (typeof style === 'string') return style.trim();\n return Object.entries(style)\n .filter(([, value]) => value !== undefined && value !== null)\n .map(([key, value]) => `${camelToKebab(key)}:${String(value)}`)\n .join(';');\n}\n\nexport function joinIconStyle(\n ...styles: Array<string | undefined>\n): string | undefined {\n const merged = styles.map((style) => style?.trim()).filter(Boolean);\n return merged.length > 0 ? merged.join(';') : undefined;\n}\n\nexport function getIconContractProps({\n size = 20,\n strokeWidth = 2,\n color = 'currentColor',\n title,\n style,\n iconName,\n}: Pick<\n IconProps,\n 'color' | 'iconName' | 'size' | 'strokeWidth' | 'style' | 'title'\n>) {\n const sizeToken = isIconSizeToken(size) ? size : undefined;\n const decorative = title ? undefined : 'true';\n const resolvedSize = resolveIconSizeVariable(size);\n const resolvedStrokeWidth = resolveIconStrokeWidthVariable(\n strokeWidth,\n sizeToken\n );\n const iconStyle = joinIconStyle(\n `--ak-icon-size:${resolvedSize}`,\n `--ak-icon-stroke-width:${resolvedStrokeWidth}`,\n 'display:inline-block',\n 'flex-shrink:0',\n 'width:var(--ak-icon-size)',\n 'height:var(--ak-icon-size)',\n 'stroke-width:var(--ak-icon-stroke-width)',\n serializeIconStyle(style)\n );\n\n return {\n sizeToken,\n decorative,\n iconStyle,\n attrs: {\n xmlns: 'http://www.w3.org/2000/svg',\n width: '24',\n height: '24',\n fill: 'none',\n stroke: color,\n 'stroke-width': 'var(--ak-icon-stroke-width)',\n role: 'img',\n 'aria-hidden': title ? undefined : 'true',\n style: iconStyle,\n 'data-slot': 'icon',\n 'data-icon': iconName,\n 'data-size': sizeToken,\n 'data-decorative': decorative,\n 'data-color': color === 'currentColor' ? 'current' : undefined,\n },\n };\n}\n\nexport function IconBase({\n size = 20,\n strokeWidth = 2,\n color = 'currentColor',\n title,\n class: className,\n style,\n iconName,\n children,\n ref,\n ...rest\n}: IconProps): JSXElement {\n const { attrs } = getIconContractProps({\n size,\n strokeWidth,\n color,\n title,\n style,\n iconName,\n });\n\n const finalChildren = title\n ? [jsx('title', { children: title }), children]\n : children;\n\n return jsxs('svg', {\n ...rest,\n ...attrs,\n viewBox: rest.viewBox ?? '0 0 24 24',\n 'stroke-linecap': rest['stroke-linecap'] ?? 'round',\n 'stroke-linejoin': rest['stroke-linejoin'] ?? 'round',\n class: className,\n ref,\n children: finalChildren,\n });\n}\n"],"mappings":";;AAKA,MAAM,mBAA6C;CAAC;CAAM;CAAM;CAAM;AAAI;AAE1E,SAAgB,gBAAgB,OAAwC;CACtE,OACE,OAAO,UAAU,YACjB,iBAAiB,SAAS,KAAsB;AAEpD;AAEA,SAAgB,uBAAuB,MAA+B;CACpE,IAAI,OAAO,SAAS,UAAU,OAAO,GAAG,KAAK;CAC7C,OAAO;AACT;AAEA,SAAgB,wBAAwB,MAA+B;CACrE,IAAI,gBAAgB,IAAI,GACtB,OAAO,sBAAsB,KAAK;CAEpC,OAAO,uBAAuB,IAAI;AACpC;AAEA,SAAgB,+BACd,aACA,WACQ;CACR,IAAI,WACF,OAAO,8BAA8B,UAAU,mCAAmC,YAAY;CAEhG,OAAO,kCAAkC,YAAY;AACvD;AAEA,SAAS,aAAa,KAAqB;CACzC,OAAO,IAAI,QAAQ,aAAa,UAAU,IAAI,MAAM,YAAY,GAAG;AACrE;AAEA,SAAgB,mBACd,OACQ;CACR,IAAI,CAAC,OAAO,OAAO;CACnB,IAAI,OAAO,UAAU,UAAU,OAAO,MAAM,KAAK;CACjD,OAAO,OAAO,QAAQ,KAAK,EACxB,QAAQ,GAAG,WAAW,UAAU,UAAa,UAAU,IAAI,EAC3D,KAAK,CAAC,KAAK,WAAW,GAAG,aAAa,GAAG,EAAE,GAAG,OAAO,KAAK,GAAG,EAC7D,KAAK,GAAG;AACb;AAEA,SAAgB,cACd,GAAG,QACiB;CACpB,MAAM,SAAS,OAAO,KAAK,UAAU,OAAO,KAAK,CAAC,EAAE,OAAO,OAAO;CAClE,OAAO,OAAO,SAAS,IAAI,OAAO,KAAK,GAAG,IAAI;AAChD;AAEA,SAAgB,qBAAqB,EACnC,OAAO,IACP,cAAc,GACd,QAAQ,gBACR,OACA,OACA,YAIC;CACD,MAAM,YAAY,gBAAgB,IAAI,IAAI,OAAO;CACjD,MAAM,aAAa,QAAQ,SAAY;CACvC,MAAM,eAAe,wBAAwB,IAAI;CACjD,MAAM,sBAAsB,+BAC1B,aACA,SACF;CACA,MAAM,YAAY,cAChB,kBAAkB,gBAClB,0BAA0B,uBAC1B,wBACA,iBACA,6BACA,8BACA,4CACA,mBAAmB,KAAK,CAC1B;CAEA,OAAO;EACL;EACA;EACA;EACA,OAAO;GACL,OAAO;GACP,OAAO;GACP,QAAQ;GACR,MAAM;GACN,QAAQ;GACR,gBAAgB;GAChB,MAAM;GACN,eAAe,QAAQ,SAAY;GACnC,OAAO;GACP,aAAa;GACb,aAAa;GACb,aAAa;GACb,mBAAmB;GACnB,cAAc,UAAU,iBAAiB,YAAY;EACvD;CACF;AACF;AAEA,SAAgB,SAAS,EACvB,OAAO,IACP,cAAc,GACd,QAAQ,gBACR,OACA,OAAO,WACP,OACA,UACA,UACA,KACA,GAAG,QACqB;CACxB,MAAM,EAAE,UAAU,qBAAqB;EACrC;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAED,MAAM,gBAAgB,QAClB,CAAC,IAAI,SAAS,EAAE,UAAU,MAAM,CAAC,GAAG,QAAQ,IAC5C;CAEJ,OAAO,KAAK,OAAO;EACjB,GAAG;EACH,GAAG;EACH,SAAS,KAAK,WAAW;EACzB,kBAAkB,KAAK,qBAAqB;EAC5C,mBAAmB,KAAK,sBAAsB;EAC9C,OAAO;EACP;EACA,UAAU;CACZ,CAAC;AACH"}
1
+ {"version":3,"file":"icon.js","names":[],"sources":["../../../src/foundations/icon/icon.tsx"],"sourcesContent":["/* eslint-disable askr/no-hardcoded-theme-tokens -- Icon exposes a stable CSS variable contract consumed by themes. */\nimport { jsx, jsxs } from '../../jsx/jsx-runtime';\nimport type { JSXElement } from '../../common/jsx';\nimport type { IconProps, IconSizeToken, IconStyleObject } from './icon.types';\n\nconst ICON_SIZE_TOKENS: readonly IconSizeToken[] = ['sm', 'md', 'lg', 'xl'];\n\nexport function isIconSizeToken(value: unknown): value is IconSizeToken {\n return (\n typeof value === 'string' &&\n ICON_SIZE_TOKENS.includes(value as IconSizeToken)\n );\n}\n\nexport function normalizeIconSizeValue(size: number | string): string {\n if (typeof size === 'number') return `${size}px`;\n return size;\n}\n\nexport function resolveIconSizeVariable(size: number | string): string {\n if (isIconSizeToken(size)) {\n return `var(--ak-icon-size-${size}, var(--ak-icon-size-md, 1.25rem))`;\n }\n return normalizeIconSizeValue(size);\n}\n\nexport function resolveIconStrokeWidthVariable(\n strokeWidth: number,\n sizeToken: IconSizeToken | undefined\n): string {\n if (sizeToken) {\n return `var(--ak-icon-stroke-width-${sizeToken}, var(--ak-icon-stroke-width-md, ${strokeWidth}))`;\n }\n return `var(--ak-icon-stroke-width-md, ${strokeWidth})`;\n}\n\nfunction camelToKebab(key: string): string {\n return key.replace(/([A-Z])/g, (match) => `-${match.toLowerCase()}`);\n}\n\nexport function serializeIconStyle(\n style: string | IconStyleObject | undefined\n): string {\n if (!style) return '';\n if (typeof style === 'string') return style.trim();\n return Object.entries(style)\n .filter(([, value]) => value !== undefined && value !== null)\n .map(([key, value]) => `${camelToKebab(key)}:${String(value)}`)\n .join(';');\n}\n\nexport function joinIconStyle(\n ...styles: Array<string | undefined>\n): string | undefined {\n const merged = styles.map((style) => style?.trim()).filter(Boolean);\n return merged.length > 0 ? merged.join(';') : undefined;\n}\n\nexport function getIconContractProps({\n size = 20,\n strokeWidth = 2,\n color = 'currentColor',\n title,\n style,\n iconName,\n}: Pick<\n IconProps,\n 'color' | 'iconName' | 'size' | 'strokeWidth' | 'style' | 'title'\n>) {\n const sizeToken = isIconSizeToken(size) ? size : undefined;\n const decorative = title ? undefined : 'true';\n const resolvedSize = resolveIconSizeVariable(size);\n const resolvedStrokeWidth = resolveIconStrokeWidthVariable(\n strokeWidth,\n sizeToken\n );\n const iconStyle = joinIconStyle(\n `--ak-icon-size:${resolvedSize}`,\n `--ak-icon-stroke-width:${resolvedStrokeWidth}`,\n 'display:inline-block',\n 'flex-shrink:0',\n 'width:var(--ak-icon-size)',\n 'height:var(--ak-icon-size)',\n 'stroke-width:var(--ak-icon-stroke-width)',\n serializeIconStyle(style)\n );\n\n return {\n sizeToken,\n decorative,\n iconStyle,\n attrs: {\n xmlns: 'http://www.w3.org/2000/svg',\n width: '24',\n height: '24',\n fill: 'none',\n stroke: color,\n 'stroke-width': 'var(--ak-icon-stroke-width)',\n role: 'img',\n 'aria-hidden': title ? undefined : 'true',\n style: iconStyle,\n 'data-slot': 'icon',\n 'data-icon': iconName,\n 'data-size': sizeToken,\n 'data-decorative': decorative,\n 'data-color': color === 'currentColor' ? 'current' : undefined,\n },\n };\n}\n\nexport function IconBase({\n size = 20,\n strokeWidth = 2,\n color = 'currentColor',\n title,\n class: className,\n style,\n iconName,\n children,\n ref,\n ...rest\n}: IconProps): JSXElement {\n const { attrs } = getIconContractProps({\n size,\n strokeWidth,\n color,\n title,\n style,\n iconName,\n });\n\n const finalChildren = title\n ? [jsx('title', { children: title }), children]\n : children;\n\n return jsxs('svg', {\n ...rest,\n ...attrs,\n viewBox: rest.viewBox ?? '0 0 24 24',\n 'stroke-linecap': rest['stroke-linecap'] ?? 'round',\n 'stroke-linejoin': rest['stroke-linejoin'] ?? 'round',\n class: className,\n ref,\n children: finalChildren,\n });\n}\n"],"mappings":";;AAKA,MAAM,mBAA6C;CAAC;CAAM;CAAM;CAAM;AAAI;AAE1E,SAAgB,gBAAgB,OAAwC;CACtE,OACE,OAAO,UAAU,YACjB,iBAAiB,SAAS,KAAsB;AAEpD;AAEA,SAAgB,uBAAuB,MAA+B;CACpE,IAAI,OAAO,SAAS,UAAU,OAAO,GAAG,KAAK;CAC7C,OAAO;AACT;AAEA,SAAgB,wBAAwB,MAA+B;CACrE,IAAI,gBAAgB,IAAI,GACtB,OAAO,sBAAsB,KAAK;CAEpC,OAAO,uBAAuB,IAAI;AACpC;AAEA,SAAgB,+BACd,aACA,WACQ;CACR,IAAI,WACF,OAAO,8BAA8B,UAAU,mCAAmC,YAAY;CAEhG,OAAO,kCAAkC,YAAY;AACvD;AAEA,SAAS,aAAa,KAAqB;CACzC,OAAO,IAAI,QAAQ,aAAa,UAAU,IAAI,MAAM,YAAY,GAAG;AACrE;AAEA,SAAgB,mBACd,OACQ;CACR,IAAI,CAAC,OAAO,OAAO;CACnB,IAAI,OAAO,UAAU,UAAU,OAAO,MAAM,KAAK;CACjD,OAAO,OAAO,QAAQ,KAAK,CAAC,CACzB,QAAQ,GAAG,WAAW,UAAU,UAAa,UAAU,IAAI,CAAC,CAC5D,KAAK,CAAC,KAAK,WAAW,GAAG,aAAa,GAAG,EAAE,GAAG,OAAO,KAAK,GAAG,CAAC,CAC9D,KAAK,GAAG;AACb;AAEA,SAAgB,cACd,GAAG,QACiB;CACpB,MAAM,SAAS,OAAO,KAAK,UAAU,OAAO,KAAK,CAAC,CAAC,CAAC,OAAO,OAAO;CAClE,OAAO,OAAO,SAAS,IAAI,OAAO,KAAK,GAAG,IAAI;AAChD;AAEA,SAAgB,qBAAqB,EACnC,OAAO,IACP,cAAc,GACd,QAAQ,gBACR,OACA,OACA,YAIC;CACD,MAAM,YAAY,gBAAgB,IAAI,IAAI,OAAO;CACjD,MAAM,aAAa,QAAQ,SAAY;CACvC,MAAM,eAAe,wBAAwB,IAAI;CACjD,MAAM,sBAAsB,+BAC1B,aACA,SACF;CACA,MAAM,YAAY,cAChB,kBAAkB,gBAClB,0BAA0B,uBAC1B,wBACA,iBACA,6BACA,8BACA,4CACA,mBAAmB,KAAK,CAC1B;CAEA,OAAO;EACL;EACA;EACA;EACA,OAAO;GACL,OAAO;GACP,OAAO;GACP,QAAQ;GACR,MAAM;GACN,QAAQ;GACR,gBAAgB;GAChB,MAAM;GACN,eAAe,QAAQ,SAAY;GACnC,OAAO;GACP,aAAa;GACb,aAAa;GACb,aAAa;GACb,mBAAmB;GACnB,cAAc,UAAU,iBAAiB,YAAY;EACvD;CACF;AACF;AAEA,SAAgB,SAAS,EACvB,OAAO,IACP,cAAc,GACd,QAAQ,gBACR,OACA,OAAO,WACP,OACA,UACA,UACA,KACA,GAAG,QACqB;CACxB,MAAM,EAAE,UAAU,qBAAqB;EACrC;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAED,MAAM,gBAAgB,QAClB,CAAC,IAAI,SAAS,EAAE,UAAU,MAAM,CAAC,GAAG,QAAQ,IAC5C;CAEJ,OAAO,KAAK,OAAO;EACjB,GAAG;EACH,GAAG;EACH,SAAS,KAAK,WAAW;EACzB,kBAAkB,KAAK,qBAAqB;EAC5C,mBAAmB,KAAK,sBAAsB;EAC9C,OAAO;EACP;EACA,UAAU;CACZ,CAAC;AACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"layer.js","names":[],"sources":["../../../src/foundations/structures/layer.ts"],"sourcesContent":["/**\n * createLayer\n *\n * Manages stacking order and coordination for overlays (modals, popovers, etc).\n *\n * INVARIANTS:\n * 1. Layers are ordered by registration time (FIFO)\n * 2. Only the top layer handles Escape key\n * 3. Only the top layer handles outside pointer events\n * 4. Nested layers are supported\n * 5. Does not implement portals (orthogonal concern)\n * 6. No automatic DOM insertion (caller controls mounting)\n *\n * DESIGN:\n * - Returns a layer manager with register/unregister API\n * - Each layer has a unique ID and can query if it's the top layer\n * - Escape and outside pointer coordination via callbacks\n * - No z-index management (CSS concern)\n *\n * USAGE:\n * const manager = createLayer();\n *\n * const layer = manager.register({\n * onEscape: () => { ... },\n * onOutsidePointer: () => { ... }\n * });\n *\n * layer.isTop(); // true if this is the topmost layer\n * layer.unregister();\n */\n\nexport interface LayerOptions {\n /**\n * Called when Escape is pressed and this is the top layer\n */\n onEscape?: () => void;\n\n /**\n * Called when pointer event occurs outside and this is the top layer\n */\n onOutsidePointer?: (e: PointerEvent) => void;\n\n /**\n * Optional node reference for outside pointer detection\n */\n node?: Node | null;\n}\n\nexport interface Layer {\n /**\n * Unique layer ID\n */\n id: number;\n\n /**\n * Check if this layer is the topmost\n */\n isTop(): boolean;\n\n /**\n * Remove this layer from the stack\n */\n unregister(): void;\n}\n\nexport interface LayerManager {\n /**\n * Register a new layer\n */\n register(options: LayerOptions): Layer;\n\n /**\n * Get all active layers in order\n */\n layers(): ReadonlyArray<Layer>;\n\n /**\n * Manually trigger escape handling on the top layer\n */\n handleEscape(): void;\n\n /**\n * Manually trigger outside pointer handling on the top layer\n */\n handleOutsidePointer(e: PointerEvent): void;\n}\n\nexport function createLayer(): LayerManager {\n const stack: Array<{\n id: number;\n options: LayerOptions;\n }> = [];\n\n let nextId = 1;\n\n function register(options: LayerOptions): Layer {\n const id = nextId++;\n const entry = { id, options };\n stack.push(entry);\n\n function isTop(): boolean {\n return stack[stack.length - 1]?.id === id;\n }\n\n function unregister(): void {\n const index = stack.findIndex((e) => e.id === id);\n if (index !== -1) {\n stack.splice(index, 1);\n }\n }\n\n return {\n id,\n isTop,\n unregister,\n };\n }\n\n function layers(): ReadonlyArray<Layer> {\n return stack.map((entry) => ({\n id: entry.id,\n isTop: () => stack[stack.length - 1]?.id === entry.id,\n unregister: () => {\n const index = stack.findIndex((e) => e.id === entry.id);\n if (index !== -1) {\n stack.splice(index, 1);\n }\n },\n }));\n }\n\n function handleEscape(): void {\n const top = stack[stack.length - 1];\n if (top) {\n top.options.onEscape?.();\n }\n }\n\n function handleOutsidePointer(e: PointerEvent): void {\n const top = stack[stack.length - 1];\n if (!top) return;\n\n const node = top.options.node;\n if (node && e.target instanceof Node) {\n // Check if the event target is outside the layer node\n if (!node.contains(e.target)) {\n top.options.onOutsidePointer?.(e);\n }\n } else {\n // No node provided, always trigger\n top.options.onOutsidePointer?.(e);\n }\n }\n\n return {\n register,\n layers,\n handleEscape,\n handleOutsidePointer,\n };\n}\n\n/**\n * USAGE EXAMPLE:\n *\n * const layerManager = createLayer();\n *\n * function Modal({ onClose }) {\n * const modalRef = ref<HTMLDivElement>();\n *\n * const layer = layerManager.register({\n * node: modalRef.current,\n * onEscape: onClose,\n * onOutsidePointer: onClose,\n * });\n *\n * onUnmount(() => layer.unregister());\n *\n * return (\n * <div ref={modalRef}>\n * <h1>Modal</h1>\n * {layer.isTop() && <p>I am on top!</p>}\n * </div>\n * );\n * }\n */\n"],"mappings":";AAuFA,SAAgB,cAA4B;CAC1C,MAAM,QAGD,CAAC;CAEN,IAAI,SAAS;CAEb,SAAS,SAAS,SAA8B;EAC9C,MAAM,KAAK;EACX,MAAM,QAAQ;GAAE;GAAI;EAAQ;EAC5B,MAAM,KAAK,KAAK;EAEhB,SAAS,QAAiB;GACxB,OAAO,MAAM,MAAM,SAAS,IAAI,OAAO;EACzC;EAEA,SAAS,aAAmB;GAC1B,MAAM,QAAQ,MAAM,WAAW,MAAM,EAAE,OAAO,EAAE;GAChD,IAAI,UAAU,IACZ,MAAM,OAAO,OAAO,CAAC;EAEzB;EAEA,OAAO;GACL;GACA;GACA;EACF;CACF;CAEA,SAAS,SAA+B;EACtC,OAAO,MAAM,KAAK,WAAW;GAC3B,IAAI,MAAM;GACV,aAAa,MAAM,MAAM,SAAS,IAAI,OAAO,MAAM;GACnD,kBAAkB;IAChB,MAAM,QAAQ,MAAM,WAAW,MAAM,EAAE,OAAO,MAAM,EAAE;IACtD,IAAI,UAAU,IACZ,MAAM,OAAO,OAAO,CAAC;GAEzB;EACF,EAAE;CACJ;CAEA,SAAS,eAAqB;EAC5B,MAAM,MAAM,MAAM,MAAM,SAAS;EACjC,IAAI,KACF,IAAI,QAAQ,WAAW;CAE3B;CAEA,SAAS,qBAAqB,GAAuB;EACnD,MAAM,MAAM,MAAM,MAAM,SAAS;EACjC,IAAI,CAAC,KAAK;EAEV,MAAM,OAAO,IAAI,QAAQ;EACzB,IAAI,QAAQ,EAAE,kBAAkB,MAE9B;OAAI,CAAC,KAAK,SAAS,EAAE,MAAM,GACzB,IAAI,QAAQ,mBAAmB,CAAC;EAClC,OAGA,IAAI,QAAQ,mBAAmB,CAAC;CAEpC;CAEA,OAAO;EACL;EACA;EACA;EACA;CACF;AACF"}
1
+ {"version":3,"file":"layer.js","names":[],"sources":["../../../src/foundations/structures/layer.ts"],"sourcesContent":["/**\n * createLayer\n *\n * Manages stacking order and coordination for overlays (modals, popovers, etc).\n *\n * INVARIANTS:\n * 1. Layers are ordered by registration time (FIFO)\n * 2. Only the top layer handles Escape key\n * 3. Only the top layer handles outside pointer events\n * 4. Nested layers are supported\n * 5. Does not implement portals (orthogonal concern)\n * 6. No automatic DOM insertion (caller controls mounting)\n *\n * DESIGN:\n * - Returns a layer manager with register/unregister API\n * - Each layer has a unique ID and can query if it's the top layer\n * - Escape and outside pointer coordination via callbacks\n * - No z-index management (CSS concern)\n *\n * USAGE:\n * const manager = createLayer();\n *\n * const layer = manager.register({\n * onEscape: () => { ... },\n * onOutsidePointer: () => { ... }\n * });\n *\n * layer.isTop(); // true if this is the topmost layer\n * layer.unregister();\n */\n\nexport interface LayerOptions {\n /**\n * Called when Escape is pressed and this is the top layer\n */\n onEscape?: () => void;\n\n /**\n * Called when pointer event occurs outside and this is the top layer\n */\n onOutsidePointer?: (e: PointerEvent) => void;\n\n /**\n * Optional node reference for outside pointer detection\n */\n node?: Node | null;\n}\n\nexport interface Layer {\n /**\n * Unique layer ID\n */\n id: number;\n\n /**\n * Check if this layer is the topmost\n */\n isTop(): boolean;\n\n /**\n * Remove this layer from the stack\n */\n unregister(): void;\n}\n\nexport interface LayerManager {\n /**\n * Register a new layer\n */\n register(options: LayerOptions): Layer;\n\n /**\n * Get all active layers in order\n */\n layers(): ReadonlyArray<Layer>;\n\n /**\n * Manually trigger escape handling on the top layer\n */\n handleEscape(): void;\n\n /**\n * Manually trigger outside pointer handling on the top layer\n */\n handleOutsidePointer(e: PointerEvent): void;\n}\n\nexport function createLayer(): LayerManager {\n const stack: Array<{\n id: number;\n options: LayerOptions;\n }> = [];\n\n let nextId = 1;\n\n function register(options: LayerOptions): Layer {\n const id = nextId++;\n const entry = { id, options };\n stack.push(entry);\n\n function isTop(): boolean {\n return stack[stack.length - 1]?.id === id;\n }\n\n function unregister(): void {\n const index = stack.findIndex((e) => e.id === id);\n if (index !== -1) {\n stack.splice(index, 1);\n }\n }\n\n return {\n id,\n isTop,\n unregister,\n };\n }\n\n function layers(): ReadonlyArray<Layer> {\n return stack.map((entry) => ({\n id: entry.id,\n isTop: () => stack[stack.length - 1]?.id === entry.id,\n unregister: () => {\n const index = stack.findIndex((e) => e.id === entry.id);\n if (index !== -1) {\n stack.splice(index, 1);\n }\n },\n }));\n }\n\n function handleEscape(): void {\n const top = stack[stack.length - 1];\n if (top) {\n top.options.onEscape?.();\n }\n }\n\n function handleOutsidePointer(e: PointerEvent): void {\n const top = stack[stack.length - 1];\n if (!top) return;\n\n const node = top.options.node;\n if (node && e.target instanceof Node) {\n // Check if the event target is outside the layer node\n if (!node.contains(e.target)) {\n top.options.onOutsidePointer?.(e);\n }\n } else {\n // No node provided, always trigger\n top.options.onOutsidePointer?.(e);\n }\n }\n\n return {\n register,\n layers,\n handleEscape,\n handleOutsidePointer,\n };\n}\n\n/**\n * USAGE EXAMPLE:\n *\n * const layerManager = createLayer();\n *\n * function Modal({ onClose }) {\n * const modalRef = ref<HTMLDivElement>();\n *\n * const layer = layerManager.register({\n * node: modalRef.current,\n * onEscape: onClose,\n * onOutsidePointer: onClose,\n * });\n *\n * onUnmount(() => layer.unregister());\n *\n * return (\n * <div ref={modalRef}>\n * <h1>Modal</h1>\n * {layer.isTop() && <p>I am on top!</p>}\n * </div>\n * );\n * }\n */\n"],"mappings":";AAuFA,SAAgB,cAA4B;CAC1C,MAAM,QAGD,CAAC;CAEN,IAAI,SAAS;CAEb,SAAS,SAAS,SAA8B;EAC9C,MAAM,KAAK;EACX,MAAM,QAAQ;GAAE;GAAI;EAAQ;EAC5B,MAAM,KAAK,KAAK;EAEhB,SAAS,QAAiB;GACxB,OAAO,MAAM,MAAM,SAAS,EAAE,EAAE,OAAO;EACzC;EAEA,SAAS,aAAmB;GAC1B,MAAM,QAAQ,MAAM,WAAW,MAAM,EAAE,OAAO,EAAE;GAChD,IAAI,UAAU,IACZ,MAAM,OAAO,OAAO,CAAC;EAEzB;EAEA,OAAO;GACL;GACA;GACA;EACF;CACF;CAEA,SAAS,SAA+B;EACtC,OAAO,MAAM,KAAK,WAAW;GAC3B,IAAI,MAAM;GACV,aAAa,MAAM,MAAM,SAAS,EAAE,EAAE,OAAO,MAAM;GACnD,kBAAkB;IAChB,MAAM,QAAQ,MAAM,WAAW,MAAM,EAAE,OAAO,MAAM,EAAE;IACtD,IAAI,UAAU,IACZ,MAAM,OAAO,OAAO,CAAC;GAEzB;EACF,EAAE;CACJ;CAEA,SAAS,eAAqB;EAC5B,MAAM,MAAM,MAAM,MAAM,SAAS;EACjC,IAAI,KACF,IAAI,QAAQ,WAAW;CAE3B;CAEA,SAAS,qBAAqB,GAAuB;EACnD,MAAM,MAAM,MAAM,MAAM,SAAS;EACjC,IAAI,CAAC,KAAK;EAEV,MAAM,OAAO,IAAI,QAAQ;EACzB,IAAI,QAAQ,EAAE,kBAAkB,MAE9B;OAAI,CAAC,KAAK,SAAS,EAAE,MAAM,GACzB,IAAI,QAAQ,mBAAmB,CAAC;EAClC,OAGA,IAAI,QAAQ,mBAAmB,CAAC;CAEpC;CAEA,OAAO;EACL;EACA;EACA;EACA;CACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"portal.js","names":[],"sources":["../../../src/foundations/structures/portal.tsx"],"sourcesContent":["/**\n * Portal / Host primitive.\n *\n * Foundations remain runtime-agnostic: a portal is an explicit read/write slot.\n * Scheduling and attachment are owned by the runtime when `createPortalSlot`\n * exists; otherwise this falls back to a local slot (deterministic, but does\n * not schedule updates).\n *\n * POLICY DECISIONS (LOCKED):\n *\n * 1. Local Mutable State\n * Foundations may use local mutable state ONLY to model deterministic slots,\n * never to coordinate timing, effects, or ordering. The fallback mode uses\n * closure-local `mounted` and `value` variables which are non-escaping and\n * deterministic.\n *\n * 2. Return Type Philosophy\n * Portal call signatures return `unknown` (intentionally opaque). The runtime\n * owns the concrete type. This prevents foundations from assuming JSX.Element\n * or DOM node types, maintaining runtime-agnostic portability.\n */\n\nimport {\n markReactivePropsDirtySource,\n markReadableDerivedSubscribersDirty,\n notifyReadableReaders,\n recordReadableRead,\n type ReadableSource,\n} from '../../runtime/readable';\nimport {\n getCurrentComponentInstance,\n getCurrentPortalScope,\n type ComponentInstance,\n} from '../../runtime/component';\nimport type { RenderableChild } from '../../common/vnode';\n\nexport interface Portal<T extends RenderableChild = RenderableChild> {\n /** Mount point — rendered exactly once */\n (): unknown;\n\n /** Render content into the portal */\n render(props: { children?: T }): unknown;\n}\n\nexport interface PortalProps {\n children?: RenderableChild;\n}\n\nfunction createPortalSlot<T>(): {\n read(): unknown;\n write(value: T | undefined): void;\n} {\n let currentValue: T | undefined;\n\n const source = (() => {\n recordReadableRead(source);\n return currentValue;\n }) as ReadableSource<T | undefined>;\n\n return {\n read() {\n return source();\n },\n write(value: T | undefined) {\n if (Object.is(currentValue, value)) {\n return;\n }\n\n currentValue = value;\n markReadableDerivedSubscribersDirty(source);\n markReactivePropsDirtySource(source);\n notifyReadableReaders(source);\n },\n };\n}\n\nexport function definePortal<\n T extends RenderableChild = RenderableChild,\n>(): Portal<T> {\n // Using `typeof createPortalSlot` is safe even if the identifier is not\n // defined at runtime (it returns 'undefined' rather than throwing).\n if (typeof createPortalSlot === 'function') {\n const slot = createPortalSlot<T>();\n\n function PortalHost() {\n return slot.read();\n }\n\n PortalHost.render = function PortalRender(props: { children?: T }) {\n slot.write(props.children);\n return null;\n };\n\n return PortalHost as Portal<T>;\n }\n\n // Deterministic local fallback (SSR/tests). No runtime scheduling.\n // Writes are accepted only after the host has rendered at least once.\n //\n // CRITICAL BEHAVIOR:\n // - Writes update local state but do NOT trigger re-renders\n // - The portal host will reflect changes only when the component tree\n // re-renders for other reasons (e.g., parent state change)\n // - This is safe for SSR and tests where rendering is synchronous\n // and externally controlled\n // - In runtime mode, createPortalSlot handles scheduling automatically\n let mounted = false;\n let value: T | undefined;\n\n function PortalHostFallback() {\n mounted = true;\n return value as unknown;\n }\n\n PortalHostFallback.render = function PortalRenderFallback(props: {\n children?: T;\n }) {\n if (!mounted) return null;\n value = props.children;\n return null;\n };\n\n return PortalHostFallback as Portal<T>;\n}\n\ntype DefaultPortalState = {\n portal: Portal<RenderableChild>;\n owner: ComponentInstance | null;\n cleanupOwners: WeakSet<ComponentInstance>;\n};\n\nlet _defaultPortalStates = new Map<object, DefaultPortalState>();\nlet _hasPendingDefaultPortalValue = false;\nlet _pendingDefaultPortalValue: RenderableChild | undefined = undefined;\n\nexport function _resetDefaultPortal(): void {\n _defaultPortalStates = new Map<object, DefaultPortalState>();\n _hasPendingDefaultPortalValue = false;\n _pendingDefaultPortalValue = undefined;\n}\n\nexport function disposeDefaultPortalScope(scope: object | null): void {\n if (!scope) {\n return;\n }\n\n const state = _defaultPortalStates.get(scope);\n if (state) {\n state.portal.render({ children: undefined });\n }\n\n _defaultPortalStates.delete(scope);\n}\n\nfunction isComponentPortalScope(scope: object): scope is ComponentInstance {\n return Array.isArray((scope as ComponentInstance).cleanupFns);\n}\n\nfunction isStaleDefaultPortalScope(scope: object): boolean {\n if (!isComponentPortalScope(scope)) {\n return false;\n }\n\n if (scope.target && scope.target.isConnected === false) {\n return true;\n }\n\n return scope.mounted === false;\n}\n\nfunction pruneStaleDefaultPortalScopes(): void {\n if (_defaultPortalStates.size === 0) {\n return;\n }\n\n for (const scope of Array.from(_defaultPortalStates.keys())) {\n if (isStaleDefaultPortalScope(scope)) {\n disposeDefaultPortalScope(scope);\n }\n }\n}\n\nfunction getSingleDefaultPortalScope(): object | null {\n pruneStaleDefaultPortalScopes();\n\n if (_defaultPortalStates.size !== 1) {\n return null;\n }\n\n return _defaultPortalStates.keys().next().value ?? null;\n}\n\nfunction getDefaultPortalState(scope: object): DefaultPortalState {\n let state = _defaultPortalStates.get(scope);\n if (!state) {\n state = {\n portal: definePortal<RenderableChild>(),\n owner: null,\n cleanupOwners: new WeakSet<ComponentInstance>(),\n };\n _defaultPortalStates.set(scope, state);\n }\n return state;\n}\n\nfunction resolveDefaultPortalScope(\n owner: ComponentInstance | null\n): object | null {\n return (\n owner?.portalScope ??\n getCurrentPortalScope() ??\n getSingleDefaultPortalScope()\n );\n}\n\nfunction writeDefaultPortal(\n props: PortalProps,\n owner: ComponentInstance | null\n): void {\n const scope = resolveDefaultPortalScope(owner);\n if (!scope) {\n if (_defaultPortalStates.size !== 0) {\n return;\n }\n\n _hasPendingDefaultPortalValue = true;\n _pendingDefaultPortalValue = props.children;\n return;\n }\n\n const state = getDefaultPortalState(scope);\n _hasPendingDefaultPortalValue = false;\n _pendingDefaultPortalValue = undefined;\n state.portal.render(props);\n state.owner = owner;\n}\n\nfunction applyPendingDefaultPortalValue(scope: object): void {\n if (!_hasPendingDefaultPortalValue) {\n return;\n }\n\n const state = getDefaultPortalState(scope);\n state.portal.render({ children: _pendingDefaultPortalValue });\n state.owner = null;\n _hasPendingDefaultPortalValue = false;\n _pendingDefaultPortalValue = undefined;\n}\n\nfunction registerDefaultPortalOwner(owner: ComponentInstance): void {\n const scope = resolveDefaultPortalScope(owner);\n if (!scope) {\n return;\n }\n\n const state = getDefaultPortalState(scope);\n if (state.cleanupOwners.has(owner)) {\n return;\n }\n\n state.cleanupOwners.add(owner);\n owner.cleanupFns.push(() => {\n const currentState = _defaultPortalStates.get(scope);\n if (!currentState) {\n return;\n }\n\n if (currentState.owner === owner) {\n currentState.portal.render({ children: undefined });\n currentState.owner = null;\n }\n currentState.cleanupOwners.delete(owner);\n });\n}\n\nexport function clearDefaultPortalForInstance(\n instance: ComponentInstance\n): void {\n const scope = instance.portalScope;\n if (!scope) {\n return;\n }\n\n const state = _defaultPortalStates.get(scope);\n if (!state) {\n return;\n }\n\n _hasPendingDefaultPortalValue = false;\n _pendingDefaultPortalValue = undefined;\n state.portal.render({ children: undefined });\n state.owner = null;\n}\n\nexport const DefaultPortal: Portal<RenderableChild> = (() => {\n function Host() {\n const scope = resolveDefaultPortalScope(getCurrentComponentInstance());\n if (!scope) {\n return null;\n }\n\n applyPendingDefaultPortalValue(scope);\n const v = getDefaultPortalState(scope).portal();\n return v === undefined ? null : v;\n }\n Host.render = function Render(props: { children?: RenderableChild }) {\n writeDefaultPortal(props, getCurrentComponentInstance());\n return null;\n };\n return Host as Portal<RenderableChild>;\n})();\n\nexport function Portal(props: PortalProps): null {\n const owner = getCurrentComponentInstance();\n if (owner) {\n registerDefaultPortalOwner(owner);\n }\n writeDefaultPortal(props, owner);\n return null;\n}\n\n/**\n * NOTE:\n * createPortalSlot is a runtime primitive.\n * It owns scheduling, consistency, and SSR behavior.\n */\n"],"mappings":"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"}
1
+ {"version":3,"file":"portal.js","names":[],"sources":["../../../src/foundations/structures/portal.tsx"],"sourcesContent":["/**\n * Portal / Host primitive.\n *\n * Foundations remain runtime-agnostic: a portal is an explicit read/write slot.\n * Scheduling and attachment are owned by the runtime when `createPortalSlot`\n * exists; otherwise this falls back to a local slot (deterministic, but does\n * not schedule updates).\n *\n * POLICY DECISIONS (LOCKED):\n *\n * 1. Local Mutable State\n * Foundations may use local mutable state ONLY to model deterministic slots,\n * never to coordinate timing, effects, or ordering. The fallback mode uses\n * closure-local `mounted` and `value` variables which are non-escaping and\n * deterministic.\n *\n * 2. Return Type Philosophy\n * Portal call signatures return `unknown` (intentionally opaque). The runtime\n * owns the concrete type. This prevents foundations from assuming JSX.Element\n * or DOM node types, maintaining runtime-agnostic portability.\n */\n\nimport {\n markReactivePropsDirtySource,\n markReadableDerivedSubscribersDirty,\n notifyReadableReaders,\n recordReadableRead,\n type ReadableSource,\n} from '../../runtime/readable';\nimport {\n getCurrentComponentInstance,\n getCurrentPortalScope,\n type ComponentInstance,\n} from '../../runtime/component';\nimport type { RenderableChild } from '../../common/vnode';\n\nexport interface Portal<T extends RenderableChild = RenderableChild> {\n /** Mount point — rendered exactly once */\n (): unknown;\n\n /** Render content into the portal */\n render(props: { children?: T }): unknown;\n}\n\nexport interface PortalProps {\n children?: RenderableChild;\n}\n\nfunction createPortalSlot<T>(): {\n read(): unknown;\n write(value: T | undefined): void;\n} {\n let currentValue: T | undefined;\n\n const source = (() => {\n recordReadableRead(source);\n return currentValue;\n }) as ReadableSource<T | undefined>;\n\n return {\n read() {\n return source();\n },\n write(value: T | undefined) {\n if (Object.is(currentValue, value)) {\n return;\n }\n\n currentValue = value;\n markReadableDerivedSubscribersDirty(source);\n markReactivePropsDirtySource(source);\n notifyReadableReaders(source);\n },\n };\n}\n\nexport function definePortal<\n T extends RenderableChild = RenderableChild,\n>(): Portal<T> {\n // Using `typeof createPortalSlot` is safe even if the identifier is not\n // defined at runtime (it returns 'undefined' rather than throwing).\n if (typeof createPortalSlot === 'function') {\n const slot = createPortalSlot<T>();\n\n function PortalHost() {\n return slot.read();\n }\n\n PortalHost.render = function PortalRender(props: { children?: T }) {\n slot.write(props.children);\n return null;\n };\n\n return PortalHost as Portal<T>;\n }\n\n // Deterministic local fallback (SSR/tests). No runtime scheduling.\n // Writes are accepted only after the host has rendered at least once.\n //\n // CRITICAL BEHAVIOR:\n // - Writes update local state but do NOT trigger re-renders\n // - The portal host will reflect changes only when the component tree\n // re-renders for other reasons (e.g., parent state change)\n // - This is safe for SSR and tests where rendering is synchronous\n // and externally controlled\n // - In runtime mode, createPortalSlot handles scheduling automatically\n let mounted = false;\n let value: T | undefined;\n\n function PortalHostFallback() {\n mounted = true;\n return value as unknown;\n }\n\n PortalHostFallback.render = function PortalRenderFallback(props: {\n children?: T;\n }) {\n if (!mounted) return null;\n value = props.children;\n return null;\n };\n\n return PortalHostFallback as Portal<T>;\n}\n\ntype DefaultPortalState = {\n portal: Portal<RenderableChild>;\n owner: ComponentInstance | null;\n cleanupOwners: WeakSet<ComponentInstance>;\n};\n\nlet _defaultPortalStates = new Map<object, DefaultPortalState>();\nlet _hasPendingDefaultPortalValue = false;\nlet _pendingDefaultPortalValue: RenderableChild | undefined = undefined;\n\nexport function _resetDefaultPortal(): void {\n _defaultPortalStates = new Map<object, DefaultPortalState>();\n _hasPendingDefaultPortalValue = false;\n _pendingDefaultPortalValue = undefined;\n}\n\nexport function disposeDefaultPortalScope(scope: object | null): void {\n if (!scope) {\n return;\n }\n\n const state = _defaultPortalStates.get(scope);\n if (state) {\n state.portal.render({ children: undefined });\n }\n\n _defaultPortalStates.delete(scope);\n}\n\nfunction isComponentPortalScope(scope: object): scope is ComponentInstance {\n return Array.isArray((scope as ComponentInstance).cleanupFns);\n}\n\nfunction isStaleDefaultPortalScope(scope: object): boolean {\n if (!isComponentPortalScope(scope)) {\n return false;\n }\n\n if (scope.target && scope.target.isConnected === false) {\n return true;\n }\n\n return scope.mounted === false;\n}\n\nfunction pruneStaleDefaultPortalScopes(): void {\n if (_defaultPortalStates.size === 0) {\n return;\n }\n\n for (const scope of Array.from(_defaultPortalStates.keys())) {\n if (isStaleDefaultPortalScope(scope)) {\n disposeDefaultPortalScope(scope);\n }\n }\n}\n\nfunction getSingleDefaultPortalScope(): object | null {\n pruneStaleDefaultPortalScopes();\n\n if (_defaultPortalStates.size !== 1) {\n return null;\n }\n\n return _defaultPortalStates.keys().next().value ?? null;\n}\n\nfunction getDefaultPortalState(scope: object): DefaultPortalState {\n let state = _defaultPortalStates.get(scope);\n if (!state) {\n state = {\n portal: definePortal<RenderableChild>(),\n owner: null,\n cleanupOwners: new WeakSet<ComponentInstance>(),\n };\n _defaultPortalStates.set(scope, state);\n }\n return state;\n}\n\nfunction resolveDefaultPortalScope(\n owner: ComponentInstance | null\n): object | null {\n return (\n owner?.portalScope ??\n getCurrentPortalScope() ??\n getSingleDefaultPortalScope()\n );\n}\n\nfunction writeDefaultPortal(\n props: PortalProps,\n owner: ComponentInstance | null\n): void {\n const scope = resolveDefaultPortalScope(owner);\n if (!scope) {\n if (_defaultPortalStates.size !== 0) {\n return;\n }\n\n _hasPendingDefaultPortalValue = true;\n _pendingDefaultPortalValue = props.children;\n return;\n }\n\n const state = getDefaultPortalState(scope);\n _hasPendingDefaultPortalValue = false;\n _pendingDefaultPortalValue = undefined;\n state.portal.render(props);\n state.owner = owner;\n}\n\nfunction applyPendingDefaultPortalValue(scope: object): void {\n if (!_hasPendingDefaultPortalValue) {\n return;\n }\n\n const state = getDefaultPortalState(scope);\n state.portal.render({ children: _pendingDefaultPortalValue });\n state.owner = null;\n _hasPendingDefaultPortalValue = false;\n _pendingDefaultPortalValue = undefined;\n}\n\nfunction registerDefaultPortalOwner(owner: ComponentInstance): void {\n const scope = resolveDefaultPortalScope(owner);\n if (!scope) {\n return;\n }\n\n const state = getDefaultPortalState(scope);\n if (state.cleanupOwners.has(owner)) {\n return;\n }\n\n state.cleanupOwners.add(owner);\n owner.cleanupFns.push(() => {\n const currentState = _defaultPortalStates.get(scope);\n if (!currentState) {\n return;\n }\n\n if (currentState.owner === owner) {\n currentState.portal.render({ children: undefined });\n currentState.owner = null;\n }\n currentState.cleanupOwners.delete(owner);\n });\n}\n\nexport function clearDefaultPortalForInstance(\n instance: ComponentInstance\n): void {\n const scope = instance.portalScope;\n if (!scope) {\n return;\n }\n\n const state = _defaultPortalStates.get(scope);\n if (!state) {\n return;\n }\n\n _hasPendingDefaultPortalValue = false;\n _pendingDefaultPortalValue = undefined;\n state.portal.render({ children: undefined });\n state.owner = null;\n}\n\nexport const DefaultPortal: Portal<RenderableChild> = (() => {\n function Host() {\n const scope = resolveDefaultPortalScope(getCurrentComponentInstance());\n if (!scope) {\n return null;\n }\n\n applyPendingDefaultPortalValue(scope);\n const v = getDefaultPortalState(scope).portal();\n return v === undefined ? null : v;\n }\n Host.render = function Render(props: { children?: RenderableChild }) {\n writeDefaultPortal(props, getCurrentComponentInstance());\n return null;\n };\n return Host as Portal<RenderableChild>;\n})();\n\nexport function Portal(props: PortalProps): null {\n const owner = getCurrentComponentInstance();\n if (owner) {\n registerDefaultPortalOwner(owner);\n }\n writeDefaultPortal(props, owner);\n return null;\n}\n\n/**\n * NOTE:\n * createPortalSlot is a runtime primitive.\n * It owns scheduling, consistency, and SSR behavior.\n */\n"],"mappings":"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"}
package/dist/fx/fx.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"fx.js","names":[],"sources":["../../src/fx/fx.ts"],"sourcesContent":["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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"}
1
+ {"version":3,"file":"fx.js","names":[],"sources":["../../src/fx/fx.ts"],"sourcesContent":["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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"}
@@ -1 +1 @@
1
- {"version":3,"file":"timing.js","names":[],"sources":["../../src/fx/timing.ts"],"sourcesContent":["/**\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\ntype AnyFn = (...args: never[]) => unknown;\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 AnyFn>(\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 AnyFn>(\n fn: T,\n ms: number,\n options?: ThrottleOptions\n): T & { cancel(): void } {\n let lastCallTime: number | null = null;\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 const timeSinceLastCall =\n lastCallTime === null\n ? Number.POSITIVE_INFINITY\n : callTime - lastCallTime;\n lastArgs = args;\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n lastThis = this;\n\n if (leading && timeSinceLastCall >= ms) {\n fn.apply(this, args);\n lastCallTime = callTime;\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n return;\n }\n\n if (!leading && lastCallTime === null) {\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 lastCallTime === null ? ms : Math.max(0, 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 AnyFn>(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 AnyFn>(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"],"mappings":"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"}
1
+ {"version":3,"file":"timing.js","names":[],"sources":["../../src/fx/timing.ts"],"sourcesContent":["/**\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\ntype AnyFn = (...args: never[]) => unknown;\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 AnyFn>(\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 AnyFn>(\n fn: T,\n ms: number,\n options?: ThrottleOptions\n): T & { cancel(): void } {\n let lastCallTime: number | null = null;\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 const timeSinceLastCall =\n lastCallTime === null\n ? Number.POSITIVE_INFINITY\n : callTime - lastCallTime;\n lastArgs = args;\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n lastThis = this;\n\n if (leading && timeSinceLastCall >= ms) {\n fn.apply(this, args);\n lastCallTime = callTime;\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n return;\n }\n\n if (!leading && lastCallTime === null) {\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 lastCallTime === null ? ms : Math.max(0, 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 AnyFn>(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 AnyFn>(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"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"children.js","names":[],"sources":["../../src/renderer/children.ts"],"sourcesContent":["import { setDevValue, incDevCounter } from '../runtime/dev-namespace';\nimport { getRuntimeEnv } from './env';\nimport { keyedElements } from './keyed';\nimport { teardownNodeSubtree } from './cleanup';\nimport { createDOMNode, updateElementFromVnode } from './dom';\nimport { _isDOMElement, type DOMElement, type VNode } from './types';\nimport {\n hasNonTrivialProps,\n logFastPathDebug,\n now,\n recordDOMReplace,\n recordFastPathStats,\n tagNamesEqualIgnoreCase,\n} from './utils';\n\nfunction upperCommonTagName(tag: string): string | null {\n switch (tag) {\n case 'div':\n return 'DIV';\n case 'span':\n return 'SPAN';\n case 'p':\n return 'P';\n case 'a':\n return 'A';\n case 'button':\n return 'BUTTON';\n case 'input':\n return 'INPUT';\n case 'ul':\n return 'UL';\n case 'ol':\n return 'OL';\n case 'li':\n return 'LI';\n default:\n return null;\n }\n}\n\nfunction tagsEqualIgnoreCase(\n elementTagName: string,\n vnodeType: string\n): boolean {\n if (elementTagName === vnodeType) return true;\n\n const upperCommon = upperCommonTagName(vnodeType);\n if (upperCommon !== null && elementTagName === upperCommon) return true;\n\n return tagNamesEqualIgnoreCase(elementTagName, vnodeType);\n}\n\nexport function performBulkPositionalKeyedTextUpdate(\n parent: Element,\n keyedVnodes: Array<{ key: string | number; vnode: VNode }>\n) {\n const total = keyedVnodes.length;\n let reused = 0;\n let updatedKeys = 0;\n const start = now();\n const env = getRuntimeEnv();\n const debugFastPath =\n env.ASKR_FASTPATH_DEBUG === '1' || env.ASKR_FASTPATH_DEBUG === 'true';\n\n for (let index = 0; index < total; index += 1) {\n const { key, vnode } = keyedVnodes[index];\n const child = parent.children[index] as Element | undefined;\n\n if (\n child &&\n _isDOMElement(vnode) &&\n typeof (vnode as DOMElement).type === 'string'\n ) {\n const vnodeType = (vnode as DOMElement).type as string;\n\n if (tagsEqualIgnoreCase(child.tagName, vnodeType)) {\n const children =\n (vnode as DOMElement).children ||\n (vnode as DOMElement).props?.children;\n\n if (debugFastPath) {\n logFastPathDebug('positional idx', index, {\n chTag: child.tagName,\n vnodeType,\n chChildNodes: child.childNodes.length,\n childrenType: Array.isArray(children) ? 'array' : typeof children,\n });\n }\n\n updateTextContent(child, children, vnode as DOMElement);\n setDataKey(child, key, () => (updatedKeys += 1));\n reused += 1;\n continue;\n }\n\n if (debugFastPath) {\n logFastPathDebug('positional tag mismatch', index, {\n chTag: child.tagName,\n vnodeType,\n });\n }\n } else if (debugFastPath) {\n logFastPathDebug('positional missing or invalid', index, { ch: !!child });\n }\n\n replaceNodeAtPosition(parent, index, vnode);\n }\n\n const elapsed = now() - start;\n updateKeyedElementsMap(parent, keyedVnodes);\n\n const stats = { n: total, reused, updatedKeys, t: elapsed } as const;\n recordFastPathStats(stats, 'bulkKeyedPositionalHits');\n\n return stats;\n}\n\nfunction updateTextContent(\n el: Element,\n children: unknown,\n vnode: DOMElement\n): void {\n if (typeof children === 'string' || typeof children === 'number') {\n setTextNodeData(el, String(children));\n if (vnode.props && hasNonTrivialProps(vnode.props)) {\n updateElementFromVnode(el, vnode, false);\n }\n return;\n }\n\n if (\n Array.isArray(children) &&\n children.length === 1 &&\n (typeof children[0] === 'string' || typeof children[0] === 'number')\n ) {\n setTextNodeData(el, String(children[0]));\n if (vnode.props && hasNonTrivialProps(vnode.props)) {\n updateElementFromVnode(el, vnode, false);\n }\n return;\n }\n\n if (!tryUpdateTwoChildTextPattern(el, vnode)) {\n updateElementFromVnode(el, vnode);\n }\n}\n\nfunction tryUpdateTwoChildTextPattern(\n parentEl: Element,\n vnode: DOMElement\n): boolean {\n const vnodeChildren = vnode.props?.children ?? vnode.children;\n if (!Array.isArray(vnodeChildren) || vnodeChildren.length !== 2) return false;\n\n const firstChild = vnodeChildren[0];\n const secondChild = vnodeChildren[1];\n if (!_isDOMElement(firstChild) || !_isDOMElement(secondChild)) return false;\n if (\n typeof firstChild.type !== 'string' ||\n typeof secondChild.type !== 'string'\n ) {\n return false;\n }\n\n const firstElement = parentEl.children[0] as Element | undefined;\n const secondElement = parentEl.children[1] as Element | undefined;\n if (!firstElement || !secondElement) return false;\n if (parentEl.children.length !== 2 || parentEl.childNodes.length !== 2) {\n return false;\n }\n\n if (!tagsEqualIgnoreCase(firstElement.tagName, firstChild.type)) return false;\n if (!tagsEqualIgnoreCase(secondElement.tagName, secondChild.type))\n return false;\n\n const firstText = (firstChild.children ||\n firstChild.props?.children) as unknown;\n const secondText = (secondChild.children ||\n secondChild.props?.children) as unknown;\n\n if (typeof firstText === 'string' || typeof firstText === 'number') {\n setTextNodeData(firstElement, String(firstText));\n } else if (\n Array.isArray(firstText) &&\n firstText.length === 1 &&\n (typeof firstText[0] === 'string' || typeof firstText[0] === 'number')\n ) {\n setTextNodeData(firstElement, String(firstText[0]));\n } else {\n return false;\n }\n\n if (typeof secondText === 'string' || typeof secondText === 'number') {\n setTextNodeData(secondElement, String(secondText));\n } else if (\n Array.isArray(secondText) &&\n secondText.length === 1 &&\n (typeof secondText[0] === 'string' || typeof secondText[0] === 'number')\n ) {\n setTextNodeData(secondElement, String(secondText[0]));\n } else {\n return false;\n }\n\n return true;\n}\n\nfunction setTextNodeData(el: Element, text: string): void {\n if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {\n const textNode = el.firstChild as Text;\n if (textNode.data !== text) textNode.data = text;\n } else {\n el.textContent = text;\n }\n}\n\nfunction setDataKey(\n el: Element,\n key: string | number,\n onSet: () => void\n): void {\n try {\n const next = String(key);\n if (el.getAttribute('data-key') === next) return;\n el.setAttribute('data-key', next);\n onSet();\n } catch {\n // Ignore errors setting data-key\n }\n}\n\nfunction replaceNodeAtPosition(\n parent: Element,\n index: number,\n vnode: VNode\n): void {\n const dom = createDOMNode(vnode);\n if (dom) {\n const existing = parent.children[index];\n if (existing) {\n teardownNodeSubtree(existing);\n parent.replaceChild(dom, existing);\n } else {\n parent.appendChild(dom);\n }\n }\n}\n\nfunction updateKeyedElementsMap(\n parent: Element,\n keyedVnodes: Array<{ key: string | number; vnode: VNode }>\n): void {\n try {\n const existing = keyedElements.get(parent);\n const newKeyMap = existing\n ? (existing.clear(), existing)\n : new Map<string | number, Element>();\n\n for (let index = 0; index < keyedVnodes.length; index += 1) {\n const key = keyedVnodes[index].key;\n const child = parent.children[index] as Element | undefined;\n if (child) newKeyMap.set(key, child);\n }\n\n keyedElements.set(parent, newKeyMap);\n } catch {\n // Ignore errors updating key map\n }\n}\n\nexport function performBulkTextReplace(parent: Element, newChildren: VNode[]) {\n const start = now();\n const existing = Array.from(parent.childNodes);\n const finalNodes: Node[] = [];\n let reused = 0;\n let created = 0;\n\n for (let index = 0; index < newChildren.length; index += 1) {\n const result = processChildNode(\n newChildren[index],\n existing[index],\n finalNodes\n );\n if (result === 'reused') reused += 1;\n else if (result === 'created') created += 1;\n }\n\n const tBuild = now() - start;\n const tCommit = commitBulkReplace(parent, finalNodes);\n\n keyedElements.delete(parent);\n\n const stats = {\n n: newChildren.length,\n reused,\n created,\n tBuild,\n tCommit,\n } as const;\n recordBulkTextStats(stats);\n\n return stats;\n}\n\nfunction processChildNode(\n vnode: VNode,\n existingNode: ChildNode | undefined,\n finalNodes: Node[]\n): 'reused' | 'created' | 'skipped' {\n if (typeof vnode === 'string' || typeof vnode === 'number') {\n return processTextVnode(String(vnode), existingNode, finalNodes);\n }\n\n if (typeof vnode === 'object' && vnode !== null && 'type' in vnode) {\n return processElementVnode(vnode, existingNode, finalNodes);\n }\n\n return 'skipped';\n}\n\nfunction processTextVnode(\n text: string,\n existingNode: ChildNode | undefined,\n finalNodes: Node[]\n): 'reused' | 'created' {\n if (existingNode && existingNode.nodeType === 3) {\n (existingNode as Text).data = text;\n finalNodes.push(existingNode);\n return 'reused';\n }\n\n finalNodes.push(document.createTextNode(text));\n return 'created';\n}\n\nfunction processElementVnode(\n vnode: VNode,\n existingNode: ChildNode | undefined,\n finalNodes: Node[]\n): 'reused' | 'created' | 'skipped' {\n const vnodeObj = vnode as unknown as { type?: unknown };\n\n if (typeof vnodeObj.type === 'string') {\n const tag = vnodeObj.type;\n if (\n existingNode &&\n existingNode.nodeType === 1 &&\n tagsEqualIgnoreCase((existingNode as Element).tagName, tag)\n ) {\n updateElementFromVnode(existingNode as Element, vnode);\n finalNodes.push(existingNode);\n return 'reused';\n }\n }\n\n const dom = createDOMNode(vnode);\n if (dom) {\n finalNodes.push(dom);\n return 'created';\n }\n\n return 'skipped';\n}\n\nfunction commitBulkReplace(parent: Element, nodes: Node[]): number {\n const startedAt = Date.now();\n const fragment = document.createDocumentFragment();\n\n for (let index = 0; index < nodes.length; index += 1) {\n fragment.appendChild(nodes[index]);\n }\n\n try {\n for (let node = parent.firstChild; node; ) {\n const next = node.nextSibling;\n teardownNodeSubtree(node);\n node = next;\n }\n } catch {\n // SLOW PATH: cleanup failure\n }\n\n recordDOMReplace('bulk-text-replace');\n parent.replaceChildren(fragment);\n return Date.now() - startedAt;\n}\n\nfunction recordBulkTextStats(stats: {\n n: number;\n reused: number;\n created: number;\n tBuild: number;\n tCommit: number;\n}): void {\n try {\n setDevValue('__LAST_BULK_TEXT_FASTPATH_STATS', stats);\n setDevValue('__LAST_FASTPATH_STATS', stats);\n setDevValue('__LAST_FASTPATH_COMMIT_COUNT', 1);\n incDevCounter('bulkTextFastpathHits');\n } catch {\n // Ignore stats errors\n }\n}\n\nexport function isBulkTextFastPathEligible(\n parent: Element,\n newChildren: VNode[]\n) {\n const env = getRuntimeEnv();\n const threshold = Number(env.ASKR_BULK_TEXT_THRESHOLD) || 1024;\n const requiredFraction = 0.8;\n\n const total = Array.isArray(newChildren) ? newChildren.length : 0;\n\n if (total < threshold) {\n recordBulkDiag({\n phase: 'bulk-unkeyed-eligible',\n reason: 'too-small',\n total,\n threshold,\n });\n return false;\n }\n\n const result = countSimpleChildren(newChildren);\n if (result.componentFound !== undefined) {\n recordBulkDiag({\n phase: 'bulk-unkeyed-eligible',\n reason: 'component-child',\n index: result.componentFound,\n });\n return false;\n }\n\n const fraction = result.simple / total;\n const eligible =\n fraction >= requiredFraction && parent.childNodes.length >= total;\n\n recordBulkDiag({\n phase: 'bulk-unkeyed-eligible',\n total,\n simple: result.simple,\n fraction,\n requiredFraction,\n eligible,\n });\n\n return eligible;\n}\n\nfunction countSimpleChildren(children: VNode[]): {\n simple: number;\n componentFound?: number;\n} {\n let simple = 0;\n\n for (let index = 0; index < children.length; index += 1) {\n const child = children[index];\n\n if (typeof child === 'string' || typeof child === 'number') {\n simple += 1;\n continue;\n }\n\n if (typeof child === 'object' && child !== null && 'type' in child) {\n const dom = child as DOMElement;\n\n if (typeof dom.type === 'function') {\n return { simple, componentFound: index };\n }\n\n if (typeof dom.type === 'string' && isSimpleElement(dom)) {\n simple += 1;\n }\n }\n }\n\n return { simple };\n}\n\nfunction isSimpleElement(dom: DOMElement): boolean {\n const children = dom.props?.children ?? dom.children;\n\n if (children === null || children === undefined) return true;\n\n if (typeof children === 'string' || typeof children === 'number') {\n return true;\n }\n\n if (\n Array.isArray(children) &&\n children.length === 1 &&\n (typeof children[0] === 'string' || typeof children[0] === 'number')\n ) {\n return true;\n }\n\n return false;\n}\n\nfunction recordBulkDiag(data: Record<string, unknown>): void {\n const env = getRuntimeEnv();\n if (env.NODE_ENV !== 'production' || env.ASKR_FASTPATH_DEBUG === '1') {\n try {\n setDevValue('__BULK_DIAG', data);\n } catch {\n // Ignore\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"children.js","names":[],"sources":["../../src/renderer/children.ts"],"sourcesContent":["import { setDevValue, incDevCounter } from '../runtime/dev-namespace';\nimport { getRuntimeEnv } from './env';\nimport { keyedElements } from './keyed';\nimport { teardownNodeSubtree } from './cleanup';\nimport { createDOMNode, updateElementFromVnode } from './dom';\nimport { _isDOMElement, type DOMElement, type VNode } from './types';\nimport {\n hasNonTrivialProps,\n logFastPathDebug,\n now,\n recordDOMReplace,\n recordFastPathStats,\n tagNamesEqualIgnoreCase,\n} from './utils';\n\nfunction upperCommonTagName(tag: string): string | null {\n switch (tag) {\n case 'div':\n return 'DIV';\n case 'span':\n return 'SPAN';\n case 'p':\n return 'P';\n case 'a':\n return 'A';\n case 'button':\n return 'BUTTON';\n case 'input':\n return 'INPUT';\n case 'ul':\n return 'UL';\n case 'ol':\n return 'OL';\n case 'li':\n return 'LI';\n default:\n return null;\n }\n}\n\nfunction tagsEqualIgnoreCase(\n elementTagName: string,\n vnodeType: string\n): boolean {\n if (elementTagName === vnodeType) return true;\n\n const upperCommon = upperCommonTagName(vnodeType);\n if (upperCommon !== null && elementTagName === upperCommon) return true;\n\n return tagNamesEqualIgnoreCase(elementTagName, vnodeType);\n}\n\nexport function performBulkPositionalKeyedTextUpdate(\n parent: Element,\n keyedVnodes: Array<{ key: string | number; vnode: VNode }>\n) {\n const total = keyedVnodes.length;\n let reused = 0;\n let updatedKeys = 0;\n const start = now();\n const env = getRuntimeEnv();\n const debugFastPath =\n env.ASKR_FASTPATH_DEBUG === '1' || env.ASKR_FASTPATH_DEBUG === 'true';\n\n for (let index = 0; index < total; index += 1) {\n const { key, vnode } = keyedVnodes[index];\n const child = parent.children[index] as Element | undefined;\n\n if (\n child &&\n _isDOMElement(vnode) &&\n typeof (vnode as DOMElement).type === 'string'\n ) {\n const vnodeType = (vnode as DOMElement).type as string;\n\n if (tagsEqualIgnoreCase(child.tagName, vnodeType)) {\n const children =\n (vnode as DOMElement).children ||\n (vnode as DOMElement).props?.children;\n\n if (debugFastPath) {\n logFastPathDebug('positional idx', index, {\n chTag: child.tagName,\n vnodeType,\n chChildNodes: child.childNodes.length,\n childrenType: Array.isArray(children) ? 'array' : typeof children,\n });\n }\n\n updateTextContent(child, children, vnode as DOMElement);\n setDataKey(child, key, () => (updatedKeys += 1));\n reused += 1;\n continue;\n }\n\n if (debugFastPath) {\n logFastPathDebug('positional tag mismatch', index, {\n chTag: child.tagName,\n vnodeType,\n });\n }\n } else if (debugFastPath) {\n logFastPathDebug('positional missing or invalid', index, { ch: !!child });\n }\n\n replaceNodeAtPosition(parent, index, vnode);\n }\n\n const elapsed = now() - start;\n updateKeyedElementsMap(parent, keyedVnodes);\n\n const stats = { n: total, reused, updatedKeys, t: elapsed } as const;\n recordFastPathStats(stats, 'bulkKeyedPositionalHits');\n\n return stats;\n}\n\nfunction updateTextContent(\n el: Element,\n children: unknown,\n vnode: DOMElement\n): void {\n if (typeof children === 'string' || typeof children === 'number') {\n setTextNodeData(el, String(children));\n if (vnode.props && hasNonTrivialProps(vnode.props)) {\n updateElementFromVnode(el, vnode, false);\n }\n return;\n }\n\n if (\n Array.isArray(children) &&\n children.length === 1 &&\n (typeof children[0] === 'string' || typeof children[0] === 'number')\n ) {\n setTextNodeData(el, String(children[0]));\n if (vnode.props && hasNonTrivialProps(vnode.props)) {\n updateElementFromVnode(el, vnode, false);\n }\n return;\n }\n\n if (!tryUpdateTwoChildTextPattern(el, vnode)) {\n updateElementFromVnode(el, vnode);\n }\n}\n\nfunction tryUpdateTwoChildTextPattern(\n parentEl: Element,\n vnode: DOMElement\n): boolean {\n const vnodeChildren = vnode.props?.children ?? vnode.children;\n if (!Array.isArray(vnodeChildren) || vnodeChildren.length !== 2) return false;\n\n const firstChild = vnodeChildren[0];\n const secondChild = vnodeChildren[1];\n if (!_isDOMElement(firstChild) || !_isDOMElement(secondChild)) return false;\n if (\n typeof firstChild.type !== 'string' ||\n typeof secondChild.type !== 'string'\n ) {\n return false;\n }\n\n const firstElement = parentEl.children[0] as Element | undefined;\n const secondElement = parentEl.children[1] as Element | undefined;\n if (!firstElement || !secondElement) return false;\n if (parentEl.children.length !== 2 || parentEl.childNodes.length !== 2) {\n return false;\n }\n\n if (!tagsEqualIgnoreCase(firstElement.tagName, firstChild.type)) return false;\n if (!tagsEqualIgnoreCase(secondElement.tagName, secondChild.type))\n return false;\n\n const firstText = (firstChild.children ||\n firstChild.props?.children) as unknown;\n const secondText = (secondChild.children ||\n secondChild.props?.children) as unknown;\n\n if (typeof firstText === 'string' || typeof firstText === 'number') {\n setTextNodeData(firstElement, String(firstText));\n } else if (\n Array.isArray(firstText) &&\n firstText.length === 1 &&\n (typeof firstText[0] === 'string' || typeof firstText[0] === 'number')\n ) {\n setTextNodeData(firstElement, String(firstText[0]));\n } else {\n return false;\n }\n\n if (typeof secondText === 'string' || typeof secondText === 'number') {\n setTextNodeData(secondElement, String(secondText));\n } else if (\n Array.isArray(secondText) &&\n secondText.length === 1 &&\n (typeof secondText[0] === 'string' || typeof secondText[0] === 'number')\n ) {\n setTextNodeData(secondElement, String(secondText[0]));\n } else {\n return false;\n }\n\n return true;\n}\n\nfunction setTextNodeData(el: Element, text: string): void {\n if (el.childNodes.length === 1 && el.firstChild?.nodeType === 3) {\n const textNode = el.firstChild as Text;\n if (textNode.data !== text) textNode.data = text;\n } else {\n el.textContent = text;\n }\n}\n\nfunction setDataKey(\n el: Element,\n key: string | number,\n onSet: () => void\n): void {\n try {\n const next = String(key);\n if (el.getAttribute('data-key') === next) return;\n el.setAttribute('data-key', next);\n onSet();\n } catch {\n // Ignore errors setting data-key\n }\n}\n\nfunction replaceNodeAtPosition(\n parent: Element,\n index: number,\n vnode: VNode\n): void {\n const dom = createDOMNode(vnode);\n if (dom) {\n const existing = parent.children[index];\n if (existing) {\n teardownNodeSubtree(existing);\n parent.replaceChild(dom, existing);\n } else {\n parent.appendChild(dom);\n }\n }\n}\n\nfunction updateKeyedElementsMap(\n parent: Element,\n keyedVnodes: Array<{ key: string | number; vnode: VNode }>\n): void {\n try {\n const existing = keyedElements.get(parent);\n const newKeyMap = existing\n ? (existing.clear(), existing)\n : new Map<string | number, Element>();\n\n for (let index = 0; index < keyedVnodes.length; index += 1) {\n const key = keyedVnodes[index].key;\n const child = parent.children[index] as Element | undefined;\n if (child) newKeyMap.set(key, child);\n }\n\n keyedElements.set(parent, newKeyMap);\n } catch {\n // Ignore errors updating key map\n }\n}\n\nexport function performBulkTextReplace(parent: Element, newChildren: VNode[]) {\n const start = now();\n const existing = Array.from(parent.childNodes);\n const finalNodes: Node[] = [];\n let reused = 0;\n let created = 0;\n\n for (let index = 0; index < newChildren.length; index += 1) {\n const result = processChildNode(\n newChildren[index],\n existing[index],\n finalNodes\n );\n if (result === 'reused') reused += 1;\n else if (result === 'created') created += 1;\n }\n\n const tBuild = now() - start;\n const tCommit = commitBulkReplace(parent, finalNodes);\n\n keyedElements.delete(parent);\n\n const stats = {\n n: newChildren.length,\n reused,\n created,\n tBuild,\n tCommit,\n } as const;\n recordBulkTextStats(stats);\n\n return stats;\n}\n\nfunction processChildNode(\n vnode: VNode,\n existingNode: ChildNode | undefined,\n finalNodes: Node[]\n): 'reused' | 'created' | 'skipped' {\n if (typeof vnode === 'string' || typeof vnode === 'number') {\n return processTextVnode(String(vnode), existingNode, finalNodes);\n }\n\n if (typeof vnode === 'object' && vnode !== null && 'type' in vnode) {\n return processElementVnode(vnode, existingNode, finalNodes);\n }\n\n return 'skipped';\n}\n\nfunction processTextVnode(\n text: string,\n existingNode: ChildNode | undefined,\n finalNodes: Node[]\n): 'reused' | 'created' {\n if (existingNode && existingNode.nodeType === 3) {\n (existingNode as Text).data = text;\n finalNodes.push(existingNode);\n return 'reused';\n }\n\n finalNodes.push(document.createTextNode(text));\n return 'created';\n}\n\nfunction processElementVnode(\n vnode: VNode,\n existingNode: ChildNode | undefined,\n finalNodes: Node[]\n): 'reused' | 'created' | 'skipped' {\n const vnodeObj = vnode as unknown as { type?: unknown };\n\n if (typeof vnodeObj.type === 'string') {\n const tag = vnodeObj.type;\n if (\n existingNode &&\n existingNode.nodeType === 1 &&\n tagsEqualIgnoreCase((existingNode as Element).tagName, tag)\n ) {\n updateElementFromVnode(existingNode as Element, vnode);\n finalNodes.push(existingNode);\n return 'reused';\n }\n }\n\n const dom = createDOMNode(vnode);\n if (dom) {\n finalNodes.push(dom);\n return 'created';\n }\n\n return 'skipped';\n}\n\nfunction commitBulkReplace(parent: Element, nodes: Node[]): number {\n const startedAt = Date.now();\n const fragment = document.createDocumentFragment();\n\n for (let index = 0; index < nodes.length; index += 1) {\n fragment.appendChild(nodes[index]);\n }\n\n try {\n for (let node = parent.firstChild; node; ) {\n const next = node.nextSibling;\n teardownNodeSubtree(node);\n node = next;\n }\n } catch {\n // SLOW PATH: cleanup failure\n }\n\n recordDOMReplace('bulk-text-replace');\n parent.replaceChildren(fragment);\n return Date.now() - startedAt;\n}\n\nfunction recordBulkTextStats(stats: {\n n: number;\n reused: number;\n created: number;\n tBuild: number;\n tCommit: number;\n}): void {\n try {\n setDevValue('__LAST_BULK_TEXT_FASTPATH_STATS', stats);\n setDevValue('__LAST_FASTPATH_STATS', stats);\n setDevValue('__LAST_FASTPATH_COMMIT_COUNT', 1);\n incDevCounter('bulkTextFastpathHits');\n } catch {\n // Ignore stats errors\n }\n}\n\nexport function isBulkTextFastPathEligible(\n parent: Element,\n newChildren: VNode[]\n) {\n const env = getRuntimeEnv();\n const threshold = Number(env.ASKR_BULK_TEXT_THRESHOLD) || 1024;\n const requiredFraction = 0.8;\n\n const total = Array.isArray(newChildren) ? newChildren.length : 0;\n\n if (total < threshold) {\n recordBulkDiag({\n phase: 'bulk-unkeyed-eligible',\n reason: 'too-small',\n total,\n threshold,\n });\n return false;\n }\n\n const result = countSimpleChildren(newChildren);\n if (result.componentFound !== undefined) {\n recordBulkDiag({\n phase: 'bulk-unkeyed-eligible',\n reason: 'component-child',\n index: result.componentFound,\n });\n return false;\n }\n\n const fraction = result.simple / total;\n const eligible =\n fraction >= requiredFraction && parent.childNodes.length >= total;\n\n recordBulkDiag({\n phase: 'bulk-unkeyed-eligible',\n total,\n simple: result.simple,\n fraction,\n requiredFraction,\n eligible,\n });\n\n return eligible;\n}\n\nfunction countSimpleChildren(children: VNode[]): {\n simple: number;\n componentFound?: number;\n} {\n let simple = 0;\n\n for (let index = 0; index < children.length; index += 1) {\n const child = children[index];\n\n if (typeof child === 'string' || typeof child === 'number') {\n simple += 1;\n continue;\n }\n\n if (typeof child === 'object' && child !== null && 'type' in child) {\n const dom = child as DOMElement;\n\n if (typeof dom.type === 'function') {\n return { simple, componentFound: index };\n }\n\n if (typeof dom.type === 'string' && isSimpleElement(dom)) {\n simple += 1;\n }\n }\n }\n\n return { simple };\n}\n\nfunction isSimpleElement(dom: DOMElement): boolean {\n const children = dom.props?.children ?? dom.children;\n\n if (children === null || children === undefined) return true;\n\n if (typeof children === 'string' || typeof children === 'number') {\n return true;\n }\n\n if (\n Array.isArray(children) &&\n children.length === 1 &&\n (typeof children[0] === 'string' || typeof children[0] === 'number')\n ) {\n return true;\n }\n\n return false;\n}\n\nfunction recordBulkDiag(data: Record<string, unknown>): void {\n const env = getRuntimeEnv();\n if (env.NODE_ENV !== 'production' || env.ASKR_FASTPATH_DEBUG === '1') {\n try {\n setDevValue('__BULK_DIAG', data);\n } catch {\n // Ignore\n }\n }\n}\n"],"mappings":"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"}