@tanstack/db
Version:
A reactive client store for building super fast apps on sync
1 lines • 46.7 kB
Source Map (JSON)
{"version":3,"file":"lifecycle.cjs","sources":["../../../src/collection/lifecycle.ts"],"sourcesContent":["import {\n CollectionInErrorStateError,\n CollectionPreloadAbortedError,\n CollectionStateError,\n InvalidCollectionStatusTransitionError,\n} from '../errors'\nimport {\n safeCancelIdleCallback,\n safeRequestIdleCallback,\n} from '../utils/browser-polyfills'\nimport { runAllCallbacks } from '../utils/callbacks'\nimport { createDeferred } from '../deferred'\nimport { codedMessage, devBuild } from '../error-message'\nimport { isFailedStatus } from '../live-query-primitives.js'\nimport { CleanupQueue, cleanupNow } from './cleanup-queue'\nimport type { Deferred } from '../deferred'\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\nimport type {\n CollectionConfig,\n CollectionErrorInfo,\n CollectionStatus,\n HealthProblem,\n} from '../types'\nimport type { CollectionEventsManager } from './events'\nimport type { CollectionIndexesManager } from './indexes'\nimport type { CollectionChangesManager } from './changes'\nimport type { CollectionSyncManager } from './sync'\nimport type { CollectionStateManager } from './state'\n\n/**\n * Floor applied to the GC delay of a collection that started syncing before\n * anything subscribed. Adapters build their live query while rendering and\n * subscribe when that render commits. This grace period reduces cleanup\n * during that gap; a later subscriber can still restart sync. Adapters pass\n * a near-zero `gcTime` to make teardown on unmount immediate. Does not apply\n * to the timer armed when the last subscriber leaves, which still honours\n * `gcTime` exactly.\n */\nexport const UNSUBSCRIBED_GC_FLOOR_MS = 50\n\n/** A Collection's gcTime when its config names none (5 minutes). */\nexport const DEFAULT_GC_TIME_MS = 300000\n\nexport class CollectionLifecycleManager<\n TOutput extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TInput extends object = TOutput,\n> {\n private config: CollectionConfig<TOutput, TKey, TSchema, any>\n private id: string\n private indexes!: CollectionIndexesManager<TOutput, TKey, TSchema, TInput>\n private events!: CollectionEventsManager\n private changes!: CollectionChangesManager<TOutput, TKey, TSchema, TInput>\n private sync!: CollectionSyncManager<TOutput, TKey, TSchema, TInput>\n private state!: CollectionStateManager<TOutput, TKey, TSchema, TInput>\n\n public status: CollectionStatus = `idle`\n public hasBeenReady = false\n public hasReceivedFirstCommit = false\n private readyFailureSink: ((failure: { error: unknown }) => void) | undefined\n public onFirstReadyCallbacks: Array<() => void> = []\n private idleCallbackId: number | null = null\n // After the first ready, the sync adapter's problem is health tree detail:\n // `failureReason` while it still tries, `stoppedError` once it called fail().\n // A live query's request failures are kept apart, so settling a request\n // cannot clear the sync adapter's problem. The failure reason lists every\n // reportError() of its sync run since it began.\n private failureReason: HealthProblem | null = null\n // A reload's new run reported the failure reason, so its completion keeps it.\n private failureReasonFromReload = false\n private stoppedError: HealthProblem | null = null\n // The cause of status `error` or `crashed`, kept so readers see the same\n // object until the sync run ends.\n private statusError: HealthProblem | null = null\n private requestErrors = new RequestErrors()\n // A reload's new run reported the request errors, so its completion keeps\n // them.\n private requestErrorsFromReload = false\n // fail() ends the sync adapter's reporting for its sync run; a reload or\n // cleanup starts a new one.\n private adapterStopped = false\n // A reload's new sync run, until it completes or fails.\n private reloadRun: Deferred<void> | undefined\n // A reload is moving the Collection out of `error`.\n private recovering = false\n private gcGraceUntil = 0\n private cleanupConfig: () => void\n private statusRevision = 0\n private cleaningUp = false\n private cleanupPromise: Promise<void> | null = null\n private readonly cleanupStartCallbacks = new Set<() => void>()\n\n /**\n * Creates a new CollectionLifecycleManager instance\n */\n constructor(\n config: CollectionConfig<TOutput, TKey, TSchema, any>,\n id: string,\n cleanupConfig: () => void = () => {},\n ) {\n this.config = config\n this.id = id\n this.cleanupConfig = cleanupConfig\n }\n\n setDeps(deps: {\n indexes: CollectionIndexesManager<TOutput, TKey, TSchema, TInput>\n events: CollectionEventsManager\n changes: CollectionChangesManager<TOutput, TKey, TSchema, TInput>\n sync: CollectionSyncManager<TOutput, TKey, TSchema, TInput>\n state: CollectionStateManager<TOutput, TKey, TSchema, TInput>\n }) {\n this.indexes = deps.indexes\n this.events = deps.events\n this.changes = deps.changes\n this.sync = deps.sync\n this.state = deps.state\n }\n\n /**\n * Validates state transitions to prevent invalid status changes\n */\n public validateStatusTransition(\n from: CollectionStatus,\n to: CollectionStatus,\n ): void {\n if (from === to) {\n // Allow same state transitions\n return\n }\n const validTransitions: Record<\n CollectionStatus,\n Array<CollectionStatus>\n > = {\n idle: [`loading`, `error`, `crashed`, `cleaned-up`],\n loading: [`ready`, `error`, `crashed`, `cleaned-up`],\n // After the first ready only a crash changes the status; a sync\n // problem is health tree detail.\n ready: [`cleaned-up`, `crashed`],\n // A failed first load is final for its sync run. Only a reload leaves\n // it for `ready` (see `recovering`).\n error: [`cleaned-up`, `idle`, `crashed`],\n // Nothing recovers a crash in place; only cleanup leaves it.\n crashed: [`cleaned-up`],\n 'cleaned-up': [`loading`, `error`, `crashed`],\n }\n\n if (\n !validTransitions[from].includes(to) &&\n !(this.recovering && from === `error` && to === `ready`)\n ) {\n throw new InvalidCollectionStatusTransitionError(from, to, this.id)\n }\n }\n\n /**\n * Safely update the collection status with validation\n * @private\n */\n public setStatus(\n newStatus: CollectionStatus,\n allowReady: boolean = false,\n beforeEmit?: () => void,\n ): void {\n if (newStatus === `ready` && !allowReady) {\n // setStatus('ready') is an internal method that should not be called directly\n // Instead, use markReady to transition to ready triggering the necessary events\n // and side effects.\n throw new CollectionStateError(\n devBuild() && process.env.NODE_ENV !== `production`\n ? `You can't directly call \"setStatus('ready'). You must use markReady instead.`\n : codedMessage(208),\n )\n }\n this.validateStatusTransition(this.status, newStatus)\n // Only an actual replacement run transfers later cleanup to a new run.\n // Validation, deferred requests and failed admission retain the old promise.\n if (newStatus === `loading`) this.cleanupPromise = null\n const revision = ++this.statusRevision\n const previousStatus = this.status\n this.status = newStatus\n beforeEmit?.()\n\n // Emit event\n const isCurrent = () => this.statusRevision === revision\n this.events.emitStatusChange(newStatus, previousStatus, isCurrent)\n // The status is part of the health tree. status:change fires first, and\n // the tree is already current when either event fires.\n if (isCurrent()) this.events.emitHealthChange()\n }\n\n /**\n * Validates that the collection is in a usable state for data operations\n * @private\n */\n public validateCollectionUsable(operation: string): void {\n switch (this.status) {\n case `error`:\n case `crashed`:\n throw new CollectionInErrorStateError(operation, this.id)\n case `cleaned-up`:\n // Automatically restart the collection when operations are called on cleaned-up collections\n this.sync.startSync()\n break\n }\n }\n\n /**\n * Mark the collection as ready for use\n * This is called by sync implementations to explicitly signal that the collection is ready,\n * providing a more intuitive alternative to using commits for readiness signaling\n * @private - Should only be called by sync implementations\n *\n * `beforeEffects` runs after the status reads `ready` and before status\n * listeners and ready callbacks, so their writes follow it.\n */\n public markReady(beforeEffects?: () => void): void {\n // A crash ended the sync run from core's side, and fail() from the\n // adapter's: after either, and once the run is ready, markReady() does\n // nothing.\n if (this.status === `crashed` || this.adapterStopped) return\n // A reload's new run is ready: the reload completes.\n if (this.reloadRun) {\n this.completeReload(beforeEffects)\n return\n }\n const failure = this.applyReadyTransition(beforeEffects)\n if (!failure) return\n if (this.readyFailureSink) this.readyFailureSink(failure)\n else throw failure.error\n }\n\n /**\n * @internal Hold ready-effect failures while a sync function runs, whether\n * it calls markReady or commits a truncate. The returned function stops\n * holding them and returns the first one.\n */\n public deferReadyFailures(): () => { error: unknown } | undefined {\n let first: { error: unknown } | undefined\n this.readyFailureSink = (failure) => {\n first ??= failure\n }\n return () => {\n this.readyFailureSink = undefined\n return first\n }\n }\n\n private applyReadyTransition(\n beforeEffects?: () => void,\n ): { error: unknown } | undefined {\n this.validateStatusTransition(this.status, `ready`)\n // A successful initial sync establishes a ready snapshot, as does a\n // reload of a failed first load.\n if (this.status === `loading` || this.status === `error`) {\n const readyRevision = this.statusRevision + 1\n this.setStatus(`ready`, true, beforeEffects)\n\n // A status listener can synchronously supersede this transition, even\n // when it restarts the Collection back to ready before returning.\n if (\n (this.status as CollectionStatus) !== `ready` ||\n this.statusRevision !== readyRevision\n ) {\n return undefined\n }\n\n const readyEffects: Array<() => void> = []\n\n // Call any registered first ready callbacks (only on first time becoming ready)\n if (!this.hasBeenReady) {\n this.hasBeenReady = true\n\n // Also mark as having received first commit for backwards compatibility\n this.hasReceivedFirstCommit = true\n\n readyEffects.push(...this.onFirstReadyCallbacks)\n this.onFirstReadyCallbacks = []\n }\n // Notify dependents when markReady is called, after status is set\n // This ensures live queries get notified when their dependencies become ready\n readyEffects.push(() => this.changes.emitEmptyReadyEvent())\n try {\n runAllCallbacks(readyEffects)\n } catch (error) {\n return { error }\n }\n }\n return undefined\n }\n\n /**\n * The sync adapter stopped. Before the first ready this fails the sync\n * run's first load; after it, the rows stay and the error shows until a\n * reload. A second fail() in the same sync run changes nothing: the adapter\n * already stopped, and the first error stays.\n */\n public fail(error: unknown): void {\n if (this.status === `crashed` || this.adapterStopped) return\n this.adapterStopped = true\n const reload = this.reloadRun\n this.reloadRun = undefined\n if (this.status === `ready`) {\n // The rows stay; a reload that failed shows its new error instead.\n this.failureReason = null\n this.stoppedError = problemOf([describeProblem(error)])\n this.events.emitHealthChange()\n } else if (reload && this.status === `error`) {\n // A reload of a failed first load keeps status `error` with the new\n // cause.\n this.statusError = problemOf([describeProblem(error)])\n this.events.emitHealthChange()\n } else {\n this.validateStatusTransition(this.status, `error`)\n this.statusError = problemOf([describeProblem(error)])\n this.setStatus(`error`)\n }\n reload?.reject(error)\n }\n\n /** Whether the current sync run's adapter called `fail()`. */\n public get stoppedThisRun(): boolean {\n return this.adapterStopped\n }\n\n /**\n * Whether the current sync run is in its first load: `loading`, or a reload\n * of a failed first load that is not ready yet. A failure now fails that\n * first load.\n */\n public get inFirstLoad(): boolean {\n return (\n this.status === `loading` || (this.reloading && this.status === `error`)\n )\n }\n\n /** Whether a reload's new sync run is in flight. */\n public get reloading(): boolean {\n return this.reloadRun !== undefined\n }\n\n /**\n * A reload starts a new sync run while everything visible stays: the rows,\n * the status, and the current problem. The node shows `reloading`.\n */\n public beginReload(): Promise<void> {\n if (this.reloadRun) return this.reloadRun.promise\n const run = createDeferred<void>()\n void run.promise.catch(() => undefined)\n this.reloadRun = run\n this.failureReasonFromReload = false\n this.requestErrorsFromReload = false\n this.adapterStopped = false\n this.events.emitHealthChange()\n return run.promise\n }\n\n /**\n * The reload's new run replaced the rows with a snapshot or a truncate, or\n * marked ready: the retired run's problems clear, the new run's stay, and a\n * failed first load becomes ready.\n */\n public completeReload(beforeEffects?: () => void): void {\n const run = this.reloadRun\n if (!run) return\n this.reloadRun = undefined\n if (!this.failureReasonFromReload) this.failureReason = null\n this.stoppedError = null\n if (!this.requestErrorsFromReload) this.requestErrors = new RequestErrors()\n if (this.status === `ready`) this.events.emitHealthChange()\n else {\n // A failed first load, or a reload during the first load, becomes\n // ready with the new run's rows.\n this.statusError = null\n this.recovering = true\n try {\n this.markReady(beforeEffects)\n } catch (error) {\n // A ready callback that throws reaches the adapter's markReady(); the\n // reload succeeded once the status is ready.\n if ((this.status as CollectionStatus) === `ready`) run.resolve()\n else run.reject(error)\n throw error\n } finally {\n this.recovering = false\n }\n }\n run.resolve()\n }\n\n /** A reload retired the sync run; subscriptions detach its demand. */\n public retireSubscriptionDemand(): void {\n for (const subscription of [...this.changes.changeSubscriptions])\n subscription.retireSyncRun()\n }\n\n /** The reload ended without a commit, for example by cleanup. */\n public abandonReload(error: unknown): void {\n if (!this.endReload(error)) return\n if (this.status !== `cleaned-up`) this.events.emitHealthChange()\n }\n\n private endReload(error: unknown): boolean {\n const run = this.reloadRun\n if (!run) return false\n this.reloadRun = undefined\n run.reject(error)\n return true\n }\n\n /**\n * Local storage or an internal invariant broke. The Collection can no\n * longer serve reads or accept writes, from any active status. Nothing\n * recovers a crash in place: only cleanup, which discards all state, leaves\n * it.\n */\n public crash(error?: unknown): void {\n if (this.status === `crashed`) return\n this.validateStatusTransition(this.status, `crashed`)\n // A crash ends a reload in flight with it; its status change announces\n // the tree.\n this.endReload(error)\n this.statusError = problemOf([describeProblem(error)])\n this.setStatus(`crashed`)\n }\n\n /** The Collection's own problems, as health tree detail. */\n public get problems(): {\n failureReason: HealthProblem | null\n error: HealthProblem | null\n } {\n const error = isFailedStatus(this.status)\n ? this.statusError\n : (this.stoppedError ?? this.requestErrors.problem)\n return { failureReason: error ? null : this.failureReason, error }\n }\n\n /** The cause of status `error` or `crashed`, as health tree detail. */\n public get statusProblem(): HealthProblem | null {\n return isFailedStatus(this.status) ? this.statusError : null\n }\n\n /** The sync adapter still tries after a problem. Status and rows do not change. */\n public reportError(error: unknown): void {\n if (this.status === `crashed`) return\n this.assertAdapterActive(`reportError`)\n this.assertReady(`reportError`)\n // A reload's new run that reports starts its own list: the retired run's\n // failures leave with that run.\n const kept =\n this.reloading && !this.failureReasonFromReload\n ? []\n : (this.failureReason?.errors ?? [])\n // One error value is one failure: one already listed is still open, so\n // it keeps its place and its `at`, and nothing changes.\n if (kept.some((failure) => failure.cause === error)) return\n this.failureReason = problemOf([...kept, describeProblem(error)])\n this.failureReasonFromReload = this.reloading\n this.events.emitHealthChange()\n }\n\n /**\n * The source synced successfully again. With no error it clears the\n * failure reason; with one it removes the failure whose cause is that\n * value, and the failure reason clears once none is left. With nothing to\n * remove it does nothing. `undefined` is no error: it clears every\n * failure, also one that `reportError(undefined)` listed.\n */\n public reportHealthy(error?: unknown): void {\n if (this.status === `crashed`) return\n this.assertAdapterActive(`reportHealthy`)\n this.assertReady(`reportHealthy`)\n const listed = this.failureReason?.errors ?? []\n const open =\n error === undefined\n ? []\n : listed.filter((failure) => failure.cause !== error)\n if (open.length === listed.length) return\n this.failureReason = open.length > 0 ? problemOf(open) : null\n this.events.emitHealthChange()\n }\n\n /**\n * @internal A live query's request failed after initial-query readiness,\n * with each of its failures in failure order.\n */\n public reportRequestError(key: string, errors: Array<unknown>): void {\n // A reload's new run that reports starts its own list: the retired run's\n // request errors leave with that run.\n if (this.reloading && !this.requestErrorsFromReload) {\n this.requestErrors = new RequestErrors()\n this.requestErrorsFromReload = true\n }\n if (this.requestErrors.fail(key, errors)) this.events.emitHealthChange()\n }\n\n /** @internal The failed request settled or was released. */\n public clearRequestError(key: string): void {\n if (this.requestErrors.settle(key)) this.events.emitHealthChange()\n }\n\n /** fail() ends the sync adapter's reporting for its sync run. */\n private assertAdapterActive(operation: string): void {\n if (this.adapterStopped) {\n throw new CollectionStateError(\n devBuild() && process.env.NODE_ENV !== `production`\n ? `${operation}() was called after fail() in the same sync run of Collection \"${this.id}\". fail() means the sync adapter stopped; a reload starts a new sync run.`\n : codedMessage(259, { operation, id: this.id }),\n )\n }\n }\n\n private assertReady(operation: string): void {\n if (this.status !== `ready`) {\n throw new CollectionStateError(\n devBuild() && process.env.NODE_ENV !== `production`\n ? `${operation}() reports on sync after the first ready, but Collection \"${this.id}\" is ${this.status}. Before the first ready, retry without reporting, or call fail() when the sync adapter stops.`\n : codedMessage(257, { operation, id: this.id, status: this.status }),\n )\n }\n }\n\n /** Return the cause of status `error` or `crashed` in this sync run, if any. */\n public getSyncError(): unknown {\n return this.statusError?.cause\n }\n\n /** Cleanup discards the current projection until a new sync run starts. */\n public hasStartedCleanup(): boolean {\n return this.cleaningUp || this.status === `cleaned-up`\n }\n\n public assertCanStartSync(): void {\n if (this.cleaningUp) {\n throw new CollectionStateError(\n devBuild() && process.env.NODE_ENV !== `production`\n ? `Cannot start collection \"${this.id}\" during cleanup. Restart after cleanup() completes.`\n : codedMessage(209, { id: this.id }),\n )\n }\n }\n\n public isCleaningUp(): boolean {\n return this.cleaningUp\n }\n\n /**\n * Observe the synchronous start of cleanup without treating it as terminal\n * resource settlement. Internal dependents use this to retire work before\n * an asynchronous adapter cleanup publishes `cleaned-up`.\n */\n public onCleanupStart(callback: () => void): () => void {\n this.cleanupStartCallbacks.add(callback)\n if (this.cleaningUp) {\n try {\n callback()\n } catch (error) {\n // Registration did not return its ownership handle. Do not retain an\n // observer that its caller has no way to unsubscribe.\n this.cleanupStartCallbacks.delete(callback)\n throw error\n }\n }\n return () => this.cleanupStartCallbacks.delete(callback)\n }\n\n /**\n * Start the garbage collection timer for a collection with no subscribers\n * Called when sync starts outside a subscription\n */\n public startGCTimerIfUnsubscribed(): void {\n this.gcGraceUntil = cleanupNow() + UNSUBSCRIBED_GC_FLOOR_MS\n this.startGCTimer(true)\n }\n\n // A persisting transaction holds the Collection: collecting it would drop\n // the rows the transaction shows and end the sync run its handler may wait on.\n private canGarbageCollect(): boolean {\n return (\n !this.cleaningUp &&\n this.changes.activeSubscribersCount === 0 &&\n !this.sync.hasPendingPreload &&\n !this.state.hasPersistingTransaction()\n )\n }\n\n /**\n * Renew inactive retention. Settlement keeps the remaining startup/preload\n * handoff; a real last unsubscribe clears it and uses gcTime exactly.\n */\n public startGCTimer(preserveHandoff = false): void {\n if (!preserveHandoff) this.gcGraceUntil = 0\n if (this.status === `idle` || this.status === `cleaned-up`) return\n this.cancelGCTimer()\n if (!this.canGarbageCollect()) return\n\n const gcTime = this.config.gcTime ?? DEFAULT_GC_TIME_MS\n\n // If gcTime is 0, negative, or non-finite (Infinity, -Infinity, NaN), GC is disabled.\n // Note: setTimeout with Infinity coerces to 0 via ToInt32, causing immediate GC,\n // so we must explicitly check for non-finite values here.\n if (gcTime <= 0 || !Number.isFinite(gcTime)) {\n return\n }\n\n CleanupQueue.getInstance().schedule(\n this,\n Math.max(gcTime, this.gcGraceUntil - cleanupNow()),\n () => {\n if (this.canGarbageCollect()) {\n // Schedule cleanup during idle time to avoid blocking the UI thread\n this.scheduleIdleCleanup()\n }\n },\n )\n }\n\n /**\n * Cancel the garbage collection timer\n * Called when the collection becomes active again\n */\n public cancelGCTimer(): void {\n CleanupQueue.getInstance().cancel(this)\n // Also cancel any pending idle cleanup\n if (this.idleCallbackId !== null) {\n safeCancelIdleCallback(this.idleCallbackId)\n this.idleCallbackId = null\n }\n }\n\n /**\n * Schedule cleanup to run during browser idle time\n * This prevents blocking the UI thread during cleanup operations\n */\n private scheduleIdleCleanup(): void {\n // Cancel any existing idle callback\n if (this.idleCallbackId !== null) {\n safeCancelIdleCallback(this.idleCallbackId)\n }\n\n // Schedule cleanup with a timeout of 1 second\n // This ensures cleanup happens even if the browser is busy\n this.idleCallbackId = safeRequestIdleCallback(\n (deadline) => {\n // Keep the callback ID only when cleanup schedules another idle period.\n if (this.canGarbageCollect()) {\n if (!(deadline.timeRemaining() > 0 || deadline.didTimeout)) {\n this.scheduleIdleCleanup()\n return\n }\n // Automatic GC has no caller to receive cleanup rejection. Core still\n // finalizes the lifecycle; explicit cleanup returns adapter diagnostics.\n void this.beginCleanup().catch(() => undefined)\n }\n this.idleCallbackId = null\n },\n { timeout: 1000 },\n )\n }\n\n private beginCleanup(): Promise<void> {\n if (this.cleanupPromise) return this.cleanupPromise\n\n const completion = createDeferred<void>()\n this.cleanupPromise = completion.promise\n this.cleaningUp = true\n const localFailures: Array<unknown> = []\n let synchronousSyncFailure: { error: unknown } | undefined\n let syncCleanupComplete = true\n\n const attempt = (callback: () => void): void => {\n try {\n callback()\n } catch (error) {\n localFailures.push(error)\n }\n }\n\n // Dependents must stop using the discarded sync run immediately, while\n // the public status and cleanup promise still wait for adapter settlement.\n for (const callback of [...this.cleanupStartCallbacks]) {\n attempt(callback)\n }\n\n const finish = (syncFailure?: { error: unknown }) => {\n // Sync cleanup either completes synchronously or calls this once after\n // its promise settles. Reentrant callers share cleanupPromise above.\n this.cleaningUp = false\n this.statusError = null\n attempt(() => this.setStatus(`cleaned-up`))\n\n // Active collection subscriptions still depend on lifecycle events.\n // Once the last subscriber leaves, its GC cleanup clears the handlers.\n if (this.changes.activeSubscribersCount === 0) {\n attempt(() => this.events.cleanup())\n }\n\n // Keep cleanup observably asynchronous even when every release is\n // synchronous. Existing callers may use this turn to let optimistic\n // settlement finish before starting the next operation.\n const failures = syncFailure\n ? [syncFailure.error, ...localFailures]\n : localFailures\n const failure =\n failures.length === 0\n ? undefined\n : {\n error:\n failures.length === 1\n ? failures[0]\n : new AggregateError(\n failures,\n devBuild() && process.env.NODE_ENV !== `production`\n ? syncFailure\n ? `Adapter cleanup and local teardown both failed`\n : `Multiple local teardown steps failed`\n : codedMessage(173, {\n syncFailure: syncFailure !== undefined,\n }),\n { cause: failures[0] },\n ),\n }\n void Promise.resolve()\n .then(() => Promise.resolve())\n .then(() => {\n if (this.cleanupPromise === completion.promise) {\n this.cleanupPromise = null\n }\n if (failure) completion.reject(failure.error)\n else completion.resolve()\n })\n }\n\n // Sync cleanup invalidates the sync run before invoking the adapter. The\n // remaining managers retire their local state synchronously while the\n // adapter's resource-release promise is pending.\n attempt(() => this.cleanupConfig())\n try {\n syncCleanupComplete = this.sync.cleanup(finish)\n } catch (error) {\n synchronousSyncFailure = { error }\n }\n\n attempt(() => this.state.cleanup())\n attempt(() => this.changes.cleanup())\n attempt(() => this.indexes.cleanup())\n this.hasBeenReady = false\n // Problems belong to the discarded sync run. The cause of a `crashed` or\n // `error` status stays while that status shows; finish() clears it.\n const before = this.problems\n const reloading = this.reloading\n this.adapterStopped = false\n this.failureReason = null\n this.stoppedError = null\n this.requestErrors = new RequestErrors()\n // An abandoned reload announces the tree; otherwise announce dropped\n // problems here, since the status changes only when cleanup settles.\n this.abandonReload(new CollectionPreloadAbortedError())\n const after = this.problems\n if (\n !reloading &&\n (before.error !== after.error ||\n before.failureReason !== after.failureReason)\n )\n this.events.emitHealthChange()\n // Cleanup is not readiness. Sync cleanup rejects pending preload callers;\n // first-ready listeners belong to the discarded run.\n this.onFirstReadyCallbacks = []\n\n if (syncCleanupComplete) finish(synchronousSyncFailure)\n return completion.promise\n }\n\n /**\n * Register a callback to be executed when the collection first becomes ready\n * Useful for preloading collections\n * @param callback Function to call when the collection first becomes ready\n */\n public onFirstReady(callback: () => void): () => void {\n // If already ready, call immediately\n if (this.hasBeenReady) {\n callback()\n return () => {}\n }\n\n this.onFirstReadyCallbacks.push(callback)\n return () => {\n const index = this.onFirstReadyCallbacks.indexOf(callback)\n if (index !== -1) {\n this.onFirstReadyCallbacks.splice(index, 1)\n }\n }\n }\n\n public cleanup(): Promise<void> {\n this.cancelGCTimer()\n\n // Return this exact retirement operation even if an event listener starts\n // a replacement run and cleanup while the first promise is still pending.\n return this.beginCleanup()\n }\n}\n\n/** @internal Requests that failed after initial-query readiness, by issuer. */\nexport class RequestErrors {\n // Each failed request's errors.\n private readonly failed = new Map<string, Array<unknown>>()\n // Each failure still failing, by its error, with how many failed requests\n // list it. Two requests that failed with one error share one failure, and\n // it keeps its place among the failures: insertion order is failure order.\n private readonly open = new Map<\n unknown,\n { failure: CollectionErrorInfo; requests: number }\n >()\n problem: HealthProblem | null = null\n\n /** Returns whether the problem changed. */\n fail(key: string, errors: Array<unknown>): boolean {\n // The request's failures count before its earlier ones leave, so a\n // failure it lists again stays open: it keeps its place and its `at`.\n let changed = false\n for (const error of errors) {\n const entry = this.open.get(error)\n if (entry) entry.requests++\n else {\n this.open.set(error, { failure: describeProblem(error), requests: 1 })\n changed = true\n }\n }\n changed = this.release(key) || changed\n this.failed.set(key, errors)\n return changed && this.show()\n }\n\n /** Returns whether the problem changed. */\n settle(key: string): boolean {\n return this.release(key) && this.show()\n }\n\n // Removes a request's failures; returns whether one stopped failing.\n private release(key: string): boolean {\n let changed = false\n for (const error of this.failed.get(key) ?? []) {\n const entry = this.open.get(error)!\n if (--entry.requests === 0) changed = this.open.delete(error)\n }\n this.failed.delete(key)\n return changed\n }\n\n // The newest failure still failing shows, and the problem lists every\n // failure still failing, oldest first. It is a new object only when that\n // list changed.\n private show(): true {\n const errors = Array.from(this.open.values(), ({ failure }) => failure)\n this.problem = errors.length > 0 ? problemOf(errors) : null\n return true\n }\n}\n\n/**\n * @internal A health problem: its newest failure's fields, with every\n * failure of its frame listed, oldest first.\n */\nexport function problemOf(\n errors: ReadonlyArray<CollectionErrorInfo>,\n): HealthProblem {\n const newest = errors[errors.length - 1]!\n return withCause({ ...newest, errors }, newest.cause)\n}\n\n// The original value stays available but out of JSON.\nfunction withCause<T extends object>(\n info: T,\n cause: unknown,\n): T & { cause: unknown } {\n return Object.defineProperty(info, `cause`, {\n value: cause,\n enumerable: false,\n }) as T & { cause: unknown }\n}\n\n/** @internal Health tree detail for a problem value. */\nexport function describeProblem(error: unknown): CollectionErrorInfo {\n // Any value can be a problem. One that cannot describe itself, such as an\n // object without a prototype or a throwing getter, keeps what was read.\n let name = `Error`\n let message = `Collection is in an error state`\n let code: unknown\n try {\n if (error instanceof Error) {\n name = error.name\n message = error.message\n } else if (error !== undefined) message = String(error)\n code = (error as { code?: unknown } | null)?.code\n } catch {\n // The problem still shows, with the generic parts.\n }\n return withCause(\n {\n name,\n message,\n ...(typeof code === `string` ? { code } : {}),\n at: Date.now(),\n },\n error,\n )\n}\n"],"names":["idleCallbackId","failureReasonFromReload","stoppedError","statusError","requestErrors","requestErrorsFromReload","adapterStopped","recovering","gcGraceUntil","statusRevision","cleaningUp","cleanupPromise","cleanupStartCallbacks","cleanupConfig","InvalidCollectionStatusTransitionError","CollectionStateError","devBuild","codedMessage","CollectionInErrorStateError","reloadRun","applyReadyTransition","readyFailureSink","runAllCallbacks","createDeferred","endReload","isFailedStatus","assertAdapterActive","assertReady","errors","cleanupNow","canGarbageCollect","CleanupQueue","scheduleIdleCleanup","safeCancelIdleCallback","safeRequestIdleCallback","beginCleanup","CollectionPreloadAbortedError","show"],"mappings":";;;;;;;;;AAsCO,MAAM,2BAA2B;AAGjC,MAAM,qBAAqB;AAE3B,MAAM,2BAKX;AAAA;AAAA;AAAA;AAAA,EAgDA,YACE,QACA,IACA,gBAA4B,MAAM;AAAA,EAAC,GACnC;AA3CF,SAAO,SAA2B;AAClC,SAAO,eAAe;AACtB,SAAO,yBAAyB;AAEhC,SAAO,wBAA2C,CAAA;AAClD,SAAQA,KAAgC;AAMxC,SAAQ,gBAAsC;AAE9C,SAAQC,KAA0B;AAClC,SAAQC,KAAqC;AAG7C,SAAQC,KAAoC;AAC5C,SAAQC,KAAgB,IAAI,cAAA;AAG5B,SAAQC,KAA0B;AAGlC,SAAQC,KAAiB;AAIzB,SAAQC,KAAa;AACrB,SAAQC,KAAe;AAEvB,SAAQC,KAAiB;AACzB,SAAQC,KAAa;AACrB,SAAQC,KAAuC;AAC/C,SAAiBC,yBAA4B,IAAA;AAU3C,SAAK,SAAS;AACd,SAAK,KAAK;AACV,SAAKC,KAAgB;AAAA,EACvB;AAAA,EAEA,QAAQ,MAML;AACD,SAAK,UAAU,KAAK;AACpB,SAAK,SAAS,KAAK;AACnB,SAAK,UAAU,KAAK;AACpB,SAAK,OAAO,KAAK;AACjB,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKO,yBACL,MACA,IACM;AACN,QAAI,SAAS,IAAI;AAEf;AAAA,IACF;AACA,UAAM,mBAGF;AAAA,MACF,MAAM,CAAC,WAAW,SAAS,WAAW,YAAY;AAAA,MAClD,SAAS,CAAC,SAAS,SAAS,WAAW,YAAY;AAAA;AAAA;AAAA,MAGnD,OAAO,CAAC,cAAc,SAAS;AAAA;AAAA;AAAA,MAG/B,OAAO,CAAC,cAAc,QAAQ,SAAS;AAAA;AAAA,MAEvC,SAAS,CAAC,YAAY;AAAA,MACtB,cAAc,CAAC,WAAW,SAAS,SAAS;AAAA,IAAA;AAG9C,QACE,CAAC,iBAAiB,IAAI,EAAE,SAAS,EAAE,KACnC,EAAE,KAAKN,MAAc,SAAS,WAAW,OAAO,UAChD;AACA,YAAM,IAAIO,OAAAA,uCAAuC,MAAM,IAAI,KAAK,EAAE;AAAA,IACpE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,UACL,WACA,aAAsB,OACtB,YACM;AACN,QAAI,cAAc,WAAW,CAAC,YAAY;AAIxC,YAAM,IAAIC,OAAAA;AAAAA,QACRC,sBAAA,KAAc,QAAQ,IAAI,aAAa,eACnC,iFACAC,aAAAA,aAAa,GAAG;AAAA,MAAA;AAAA,IAExB;AACA,SAAK,yBAAyB,KAAK,QAAQ,SAAS;AAGpD,QAAI,cAAc,UAAW,MAAKN,KAAiB;AACnD,UAAM,WAAW,EAAE,KAAKF;AACxB,UAAM,iBAAiB,KAAK;AAC5B,SAAK,SAAS;AACd,iBAAA;AAGA,UAAM,YAAY,MAAM,KAAKA,OAAmB;AAChD,SAAK,OAAO,iBAAiB,WAAW,gBAAgB,SAAS;AAGjE,QAAI,UAAA,EAAa,MAAK,OAAO,iBAAA;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,yBAAyB,WAAyB;AACvD,YAAQ,KAAK,QAAA;AAAA,MACX,KAAK;AAAA,MACL,KAAK;AACH,cAAM,IAAIS,OAAAA,4BAA4B,WAAW,KAAK,EAAE;AAAA,MAC1D,KAAK;AAEH,aAAK,KAAK,UAAA;AACV;AAAA,IAAA;AAAA,EAEN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWO,UAAU,eAAkC;AAIjD,QAAI,KAAK,WAAW,aAAa,KAAKZ,GAAgB;AAEtD,QAAI,KAAKa,IAAW;AAClB,WAAK,eAAe,aAAa;AACjC;AAAA,IACF;AACA,UAAM,UAAU,KAAKC,GAAqB,aAAa;AACvD,QAAI,CAAC,QAAS;AACd,QAAI,KAAKC,GAAkB,MAAKA,GAAiB,OAAO;AAAA,eAC7C,QAAQ;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,qBAA2D;AAChE,QAAI;AACJ,SAAKA,KAAmB,CAAC,YAAY;AACnC,gBAAU;AAAA,IACZ;AACA,WAAO,MAAM;AACX,WAAKA,KAAmB;AACxB,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQD,GACN,eACgC;AAChC,SAAK,yBAAyB,KAAK,QAAQ,OAAO;AAGlD,QAAI,KAAK,WAAW,aAAa,KAAK,WAAW,SAAS;AACxD,YAAM,gBAAgB,KAAKX,KAAiB;AAC5C,WAAK,UAAU,SAAS,MAAM,aAAa;AAI3C,UACG,KAAK,WAAgC,WACtC,KAAKA,OAAmB,eACxB;AACA,eAAO;AAAA,MACT;AAEA,YAAM,eAAkC,CAAA;AAGxC,UAAI,CAAC,KAAK,cAAc;AACtB,aAAK,eAAe;AAGpB,aAAK,yBAAyB;AAE9B,qBAAa,KAAK,GAAG,KAAK,qBAAqB;AAC/C,aAAK,wBAAwB,CAAA;AAAA,MAC/B;AAGA,mBAAa,KAAK,MAAM,KAAK,QAAQ,qBAAqB;AAC1D,UAAI;AACFa,kBAAAA,gBAAgB,YAAY;AAAA,MAC9B,SAAS,OAAO;AACd,eAAO,EAAE,MAAA;AAAA,MACX;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQO,KAAK,OAAsB;AAChC,QAAI,KAAK,WAAW,aAAa,KAAKhB,GAAgB;AACtD,SAAKA,KAAiB;AACtB,UAAM,SAAS,KAAKa;AACpB,SAAKA,KAAY;AACjB,QAAI,KAAK,WAAW,SAAS;AAE3B,WAAK,gBAAgB;AACrB,WAAKjB,KAAe,UAAU,CAAC,gBAAgB,KAAK,CAAC,CAAC;AACtD,WAAK,OAAO,iBAAA;AAAA,IACd,WAAW,UAAU,KAAK,WAAW,SAAS;AAG5C,WAAKC,KAAc,UAAU,CAAC,gBAAgB,KAAK,CAAC,CAAC;AACrD,WAAK,OAAO,iBAAA;AAAA,IACd,OAAO;AACL,WAAK,yBAAyB,KAAK,QAAQ,OAAO;AAClD,WAAKA,KAAc,UAAU,CAAC,gBAAgB,KAAK,CAAC,CAAC;AACrD,WAAK,UAAU,OAAO;AAAA,IACxB;AACA,YAAQ,OAAO,KAAK;AAAA,EACtB;AAAA;AAAA,EAGA,IAAW,iBAA0B;AACnC,WAAO,KAAKG;AAAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAW,cAAuB;AAChC,WACE,KAAK,WAAW,aAAc,KAAK,aAAa,KAAK,WAAW;AAAA,EAEpE;AAAA;AAAA,EAGA,IAAW,YAAqB;AAC9B,WAAO,KAAKa,OAAc;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,cAA6B;AAClC,QAAI,KAAKA,GAAW,QAAO,KAAKA,GAAU;AAC1C,UAAM,MAAMI,SAAAA,eAAA;AACZ,SAAK,IAAI,QAAQ,MAAM,MAAM,MAAS;AACtC,SAAKJ,KAAY;AACjB,SAAKlB,KAA0B;AAC/B,SAAKI,KAA0B;AAC/B,SAAKC,KAAiB;AACtB,SAAK,OAAO,iBAAA;AACZ,WAAO,IAAI;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,eAAe,eAAkC;AACtD,UAAM,MAAM,KAAKa;AACjB,QAAI,CAAC,IAAK;AACV,SAAKA,KAAY;AACjB,QAAI,CAAC,KAAKlB,GAAyB,MAAK,gBAAgB;AACxD,SAAKC,KAAe;AACpB,QAAI,CAAC,KAAKG,GAAyB,MAAKD,KAAgB,IAAI,cAAA;AAC5D,QAAI,KAAK,WAAW,QAAS,MAAK,OAAO,iBAAA;AAAA,SACpC;AAGH,WAAKD,KAAc;AACnB,WAAKI,KAAa;AAClB,UAAI;AACF,aAAK,UAAU,aAAa;AAAA,MAC9B,SAAS,OAAO;AAGd,YAAK,KAAK,WAAgC,QAAS,KAAI,QAAA;AAAA,YAClD,KAAI,OAAO,KAAK;AACrB,cAAM;AAAA,MACR,UAAA;AACE,aAAKA,KAAa;AAAA,MACpB;AAAA,IACF;AACA,QAAI,QAAA;AAAA,EACN;AAAA;AAAA,EAGO,2BAAiC;AACtC,eAAW,gBAAgB,CAAC,GAAG,KAAK,QAAQ,mBAAmB;AAC7D,mBAAa,cAAA;AAAA,EACjB;AAAA;AAAA,EAGO,cAAc,OAAsB;AACzC,QAAI,CAAC,KAAKiB,GAAU,KAAK,EAAG;AAC5B,QAAI,KAAK,WAAW,aAAc,MAAK,OAAO,iBAAA;AAAA,EAChD;AAAA,EAEQA,GAAU,OAAyB;AACzC,UAAM,MAAM,KAAKL;AACjB,QAAI,CAAC,IAAK,QAAO;AACjB,SAAKA,KAAY;AACjB,QAAI,OAAO,KAAK;AAChB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQO,MAAM,OAAuB;AAClC,QAAI,KAAK,WAAW,UAAW;AAC/B,SAAK,yBAAyB,KAAK,QAAQ,SAAS;AAGpD,SAAKK,GAAU,KAAK;AACpB,SAAKrB,KAAc,UAAU,CAAC,gBAAgB,KAAK,CAAC,CAAC;AACrD,SAAK,UAAU,SAAS;AAAA,EAC1B;AAAA;AAAA,EAGA,IAAW,WAGT;AACA,UAAM,QAAQsB,oBAAAA,eAAe,KAAK,MAAM,IACpC,KAAKtB,KACJ,KAAKD,MAAgB,KAAKE,GAAc;AAC7C,WAAO,EAAE,eAAe,QAAQ,OAAO,KAAK,eAAe,MAAA;AAAA,EAC7D;AAAA;AAAA,EAGA,IAAW,gBAAsC;AAC/C,WAAOqB,oBAAAA,eAAe,KAAK,MAAM,IAAI,KAAKtB,KAAc;AAAA,EAC1D;AAAA;AAAA,EAGO,YAAY,OAAsB;AACvC,QAAI,KAAK,WAAW,UAAW;AAC/B,SAAKuB,GAAoB,aAAa;AACtC,SAAKC,GAAY,aAAa;AAG9B,UAAM,OACJ,KAAK,aAAa,CAAC,KAAK1B,KACpB,CAAA,IACC,KAAK,eAAe,UAAU,CAAA;AAGrC,QAAI,KAAK,KAAK,CAAC,YAAY,QAAQ,UAAU,KAAK,EAAG;AACrD,SAAK,gBAAgB,UAAU,CAAC,GAAG,MAAM,gBAAgB,KAAK,CAAC,CAAC;AAChE,SAAKA,KAA0B,KAAK;AACpC,SAAK,OAAO,iBAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASO,cAAc,OAAuB;AAC1C,QAAI,KAAK,WAAW,UAAW;AAC/B,SAAKyB,GAAoB,eAAe;AACxC,SAAKC,GAAY,eAAe;AAChC,UAAM,SAAS,KAAK,eAAe,UAAU,CAAA;AAC7C,UAAM,OACJ,UAAU,SACN,KACA,OAAO,OAAO,CAAC,YAAY,QAAQ,UAAU,KAAK;AACxD,QAAI,KAAK,WAAW,OAAO,OAAQ;AACnC,SAAK,gBAAgB,KAAK,SAAS,IAAI,UAAU,IAAI,IAAI;AACzD,SAAK,OAAO,iBAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,mBAAmB,KAAaC,SAA8B;AAGnE,QAAI,KAAK,aAAa,CAAC,KAAKvB,IAAyB;AACnD,WAAKD,KAAgB,IAAI,cAAA;AACzB,WAAKC,KAA0B;AAAA,IACjC;AACA,QAAI,KAAKD,GAAc,KAAK,KAAKwB,OAAM,EAAG,MAAK,OAAO,iBAAA;AAAA,EACxD;AAAA;AAAA,EAGO,kBAAkB,KAAmB;AAC1C,QAAI,KAAKxB,GAAc,OAAO,GAAG,EAAG,MAAK,OAAO,iBAAA;AAAA,EAClD;AAAA;AAAA,EAGQsB,GAAoB,WAAyB;AACnD,QAAI,KAAKpB,IAAgB;AACvB,YAAM,IAAIS,OAAAA;AAAAA,QACRC,2BAAc,QAAQ,IAAI,aAAa,eACnC,GAAG,SAAS,kEAAkE,KAAK,EAAE,8EACrFC,0BAAa,KAAK,EAAE,WAAW,IAAI,KAAK,IAAI;AAAA,MAAA;AAAA,IAEpD;AAAA,EACF;AAAA,EAEQU,GAAY,WAAyB;AAC3C,QAAI,KAAK,WAAW,SAAS;AAC3B,YAAM,IAAIZ,OAAAA;AAAAA,QACRC,aAAAA,cAAc,QAAQ,IAAI,aAAa,eACnC,GAAG,SAAS,6DAA6D,KAAK,EAAE,QAAQ,KAAK,MAAM,mGACnGC,aAAAA,aAAa,KAAK,EAAE,WAAW,IAAI,KAAK,IAAI,QAAQ,KAAK,OAAA,CAAQ;AAAA,MAAA;AAAA,IAEzE;AAAA,EACF;AAAA;AAAA,EAGO,eAAwB;AAC7B,WAAO,KAAKd,IAAa;AAAA,EAC3B;AAAA;AAAA,EAGO,oBAA6B;AAClC,WAAO,KAAKO,MAAc,KAAK,WAAW;AAAA,EAC5C;AAAA,EAEO,qBAA2B;AAChC,QAAI,KAAKA,IAAY;AACnB,YAAM,IAAIK,OAAAA;AAAAA,QACRC,aAAAA,cAAc,QAAQ,IAAI,aAAa,eACnC,4BAA4B,KAAK,EAAE,yDACnCC,aAAAA,aAAa,KAAK,EAAE,IAAI,KAAK,IAAI;AAAA,MAAA;AAAA,IAEzC;AAAA,EACF;AAAA,EAEO,eAAwB;AAC7B,WAAO,KAAKP;AAAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,eAAe,UAAkC;AACtD,SAAKE,GAAsB,IAAI,QAAQ;AACvC,QAAI,KAAKF,IAAY;AACnB,UAAI;AACF,iBAAA;AAAA,MACF,SAAS,OAAO;AAGd,aAAKE,GAAsB,OAAO,QAAQ;AAC1C,cAAM;AAAA,MACR;AAAA,IACF;AACA,WAAO,MAAM,KAAKA,GAAsB,OAAO,QAAQ;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,6BAAmC;AACxC,SAAKJ,KAAeqB,aAAAA,eAAe;AACnC,SAAK,aAAa,IAAI;AAAA,EACxB;AAAA;AAAA;AAAA,EAIQC,KAA6B;AACnC,WACE,CAAC,KAAKpB,MACN,KAAK,QAAQ,2BAA2B,KACxC,CAAC,KAAK,KAAK,qBACX,CAAC,KAAK,MAAM,yBAAA;AAAA,EAEhB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,aAAa,kBAAkB,OAAa;AACjD,QAAI,CAAC,gBAAiB,MAAKF,KAAe;AAC1C,QAAI,KAAK,WAAW,UAAU,KAAK,WAAW,aAAc;AAC5D,SAAK,cAAA;AACL,QAAI,CAAC,KAAKsB,KAAqB;AAE/B,UAAM,SAAS,KAAK,OAAO,UAAU;AAKrC,QAAI,UAAU,KAAK,CAAC,OAAO,SAAS,MAAM,GAAG;AAC3C;AAAA,IACF;AAEAC,iBAAAA,aAAa,cAAc;AAAA,MACzB;AAAA,MACA,KAAK,IAAI,QAAQ,KAAKvB,KAAeqB,aAAAA,YAAY;AAAA,MACjD,MAAM;AACJ,YAAI,KAAKC,MAAqB;AAE5B,eAAKE,GAAA;AAAA,QACP;AAAA,MACF;AAAA,IAAA;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,gBAAsB;AAC3BD,iBAAAA,aAAa,YAAA,EAAc,OAAO,IAAI;AAEtC,QAAI,KAAK/B,OAAmB,MAAM;AAChCiC,uBAAAA,uBAAuB,KAAKjC,EAAc;AAC1C,WAAKA,KAAiB;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQgC,KAA4B;AAElC,QAAI,KAAKhC,OAAmB,MAAM;AAChCiC,uBAAAA,uBAAuB,KAAKjC,EAAc;AAAA,IAC5C;AAIA,SAAKA,KAAiBkC,iBAAAA;AAAAA,MACpB,CAAC,aAAa;AAEZ,YAAI,KAAKJ,MAAqB;AAC5B,cAAI,EAAE,SAAS,cAAA,IAAkB,KAAK,SAAS,aAAa;AAC1D,iBAAKE,GAAA;AACL;AAAA,UACF;AAGA,eAAK,KAAKG,GAAA,EAAe,MAAM,MAAM,MAAS;AAAA,QAChD;AACA,aAAKnC,KAAiB;AAAA,MACxB;AAAA,MACA,EAAE,SAAS,IAAA;AAAA,IAAK;AAAA,EAEpB;AAAA,EAEQmC,KAA8B;AACpC,QAAI,KAAKxB,GAAgB,QAAO,KAAKA;AAErC,UAAM,aAAaY,SAAAA,eAAA;AACnB,SAAKZ,KAAiB,WAAW;AACjC,SAAKD,KAAa;AAClB,UAAM,gBAAgC,CAAA;AACtC,QAAI;AACJ,QAAI,sBAAsB;AAE1B,UAAM,UAAU,CAAC,aAA+B;AAC9C,UAAI;AACF,iBAAA;AAAA,MACF,SAAS,OAAO;AACd,sBAAc,KAAK,KAAK;AAAA,MAC1B;AAAA,IACF;AAIA,eAAW,YAAY,CAAC,GAAG,KAAKE,EAAqB,GAAG;AACtD,cAAQ,QAAQ;AAAA,IAClB;AAEA,UAAM,SAAS,CAAC,gBAAqC;AAGnD,WAAKF,KAAa;AAClB,WAAKP,KAAc;AACnB,cAAQ,MAAM,KAAK,UAAU,YAAY,CAAC;AAI1C,UAAI,KAAK,QAAQ,2BAA2B,GAAG;AAC7C,gBAAQ,MAAM,KAAK,OAAO,QAAA,CAAS;AAAA,MACrC;AAKA,YAAM,WAAW,cACb,CAAC,YAAY,OAAO,GAAG,aAAa,IACpC;AACJ,YAAM,UACJ,SAAS,WAAW,IAChB,SACA;AAAA,QACE,OACE,SAAS,WAAW,IAChB,SAAS,CAAC,IACV,IAAI;AAAA,UACF;AAAA,UACAa,aAAAA,SAAA,KAAc,QAAQ,IAAI,aAAa,eACnC,cACE,mDACA,yCACFC,aAAAA,aAAa,KAAK;AAAA,YAChB,aAAa,gBAAgB;AAAA,UAAA,CAC9B;AAAA,UACL,EAAE,OAAO,SAAS,CAAC,EAAA;AAAA,QAAE;AAAA,MACvB;AAEd,WAAK,QAAQ,QAAA,EACV,KAAK,MAAM,QAAQ,QAAA,CAAS,EAC5B,KAAK,MAAM;AACV,YAAI,KAAKN,OAAmB,WAAW,SAAS;AAC9C,eAAKA,KAAiB;AAAA,QACxB;AACA,YAAI,QAAS,YAAW,OAAO,QAAQ,KAAK;AAAA,wBAC5B,QAAA;AAAA,MAClB,CAAC;AAAA,IACL;AAKA,YAAQ,MAAM,KAAKE,IAAe;AAClC,QAAI;AACF,4BAAsB,KAAK,KAAK,QAAQ,MAAM;AAAA,IAChD,SAAS,OAAO;AACd,+BAAyB,EAAE,MAAA;AAAA,IAC7B;AAEA,YAAQ,MAAM,KAAK,MAAM,QAAA,CAAS;AAClC,YAAQ,MAAM,KAAK,QAAQ,QAAA,CAAS;AACpC,YAAQ,MAAM,KAAK,QAAQ,QAAA,CAAS;AACpC,SAAK,eAAe;AAGpB,UAAM,SAAS,KAAK;AACpB,UAAM,YAAY,KAAK;AACvB,SAAKP,KAAiB;AACtB,SAAK,gBAAgB;AACrB,SAAKJ,KAAe;AACpB,SAAKE,KAAgB,IAAI,cAAA;AAGzB,SAAK,cAAc,IAAIgC,OAAAA,+BAA+B;AACtD,UAAM,QAAQ,KAAK;AACnB,QACE,CAAC,cACA,OAAO,UAAU,MAAM,SACtB,OAAO,kBAAkB,MAAM;AAEjC,WAAK,OAAO,iBAAA;AAGd,SAAK,wBAAwB,CAAA;AAE7B,QAAI,4BAA4B,sBAAsB;AACtD,WAAO,WAAW;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,aAAa,UAAkC;AAEpD,QAAI,KAAK,cAAc;AACrB,eAAA;AACA,aAAO,MAAM;AAAA,MAAC;AAAA,IAChB;AAEA,SAAK,sBAAsB,KAAK,QAAQ;AACxC,WAAO,MAAM;AACX,YAAM,QAAQ,KAAK,sBAAsB,QAAQ,QAAQ;AACzD,UAAI,UAAU,IAAI;AAChB,aAAK,sBAAsB,OAAO,OAAO,CAAC;AAAA,MAC5C;AAAA,IACF;AAAA,EACF;AAAA,EAEO,UAAyB;AAC9B,SAAK,cAAA;AAIL,WAAO,KAAKD,GAAA;AAAA,EACd;AACF;AAGO,MAAM,cAAc;AAAA,EAApB,cAAA;AAEL,SAAiB,6BAAa,IAAA;AAI9B,SAAiB,2BAAW,IAAA;AAI5B,SAAA,UAAgC;AAAA,EAAA;AAAA;AAAA,EAGhC,KAAK,KAAaP,SAAiC;AAGjD,QAAI,UAAU;AACd,eAAW,SAASA,SAAQ;AAC1B,YAAM,QAAQ,KAAK,KAAK,IAAI,KAAK;AACjC,UAAI,MAAO,OAAM;AAAA,WACZ;AACH,aAAK,KAAK,IAAI,OAAO,EAAE,SAAS,gBAAgB,KAAK,GAAG,UAAU,GAAG;AACrE,kBAAU;AAAA,MACZ;AAAA,IACF;AACA,cAAU,KAAK,QAAQ,GAAG,KAAK;AAC/B,SAAK,OAAO,IAAI,KAAKA,OAAM;AAC3B,WAAO,WAAW,KAAKS,GAAA;AAAA,EACzB;AAAA;AAAA,EAGA,OAAO,KAAsB;AAC3B,WAAO,KAAK,QAAQ,GAAG,KAAK,KAAKA,GAAA;AAAA,EACnC;AAAA;AAAA,EAGQ,QAAQ,KAAsB;AACpC,QAAI,UAAU;AACd,eAAW,SAAS,KAAK,OAAO,IAAI,GAAG,KAAK,IAAI;AAC9C,YAAM,QAAQ,KAAK,KAAK,IAAI,KAAK;AACjC,UAAI,EAAE,MAAM,aAAa,aAAa,KAAK,KAAK,OAAO,KAAK;AAAA,IAC9D;AACA,SAAK,OAAO,OAAO,GAAG;AACtB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQA,KAAa;AACnB,UAAMT,UAAS,MAAM,KAAK,KAAK,KAAK,OAAA,GAAU,CAAC,EAAE,QAAA,MAAc,OAAO;AACtE,SAAK,UAAUA,QAAO,SAAS,IAAI,UAAUA,OAAM,IAAI;AACvD,WAAO;AAAA,EACT;AACF;AAMO,SAAS,UACdA,SACe;AACf,QAAM,SAASA,QAAOA,QAAO,SAAS,CAAC;AACvC,SAAO,UAAU,EAAE,GAAG,QAAQ,QAAAA,QAAA,GAAU,OAAO,KAAK;AACtD;AAGA,SAAS,UACP,MACA,OACwB;AACxB,SAAO,OAAO,eAAe,MAAM,SAAS;AAAA,IAC1C,OAAO;AAAA,IACP,YAAY;AAAA,EAAA,CACb;AACH;AAGO,SAAS,gBAAgB,OAAqC;AAGnE,MAAI,OAAO;AACX,MAAI,UAAU;AACd,MAAI;AACJ,MAAI;AACF,QAAI,iBAAiB,OAAO;AAC1B,aAAO,MAAM;AACb,gBAAU,MAAM;AAAA,IAClB,WAAW,UAAU,OAAW,WAAU,OAAO,KAAK;AACtD,WAAQ,OAAqC;AAAA,EAC/C,QAAQ;AAAA,EAER;AACA,SAAO;AAAA,IACL;AAAA,MACE;AAAA,MACA;AAAA,MACA,GAAI,OAAO,SAAS,WAAW,EAAE,KAAA,IAAS,CAAA;AAAA,MAC1C,IAAI,KAAK,IAAA;AAAA,IAAI;AAAA,IAEf;AAAA,EAAA;AAEJ;;;;;;;"}