@adcp/sdk 13.0.0-rc.2 → 13.0.0-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -14
- package/dist/lib/index.d.mts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/protocols/mcp-modern.d.ts.map +1 -1
- package/dist/lib/protocols/mcp-modern.js +14 -0
- package/dist/lib/protocols/mcp-modern.js.map +1 -1
- package/dist/lib/protocols/mcp-modern.mjs +14 -0
- package/dist/lib/protocols/mcp-modern.mjs.map +1 -1
- package/dist/lib/registry/index.d.mts +5 -25
- package/dist/lib/registry/index.d.ts +5 -25
- package/dist/lib/registry/index.d.ts.map +1 -1
- package/dist/lib/registry/index.js +5 -130
- package/dist/lib/registry/index.js.map +1 -1
- package/dist/lib/registry/index.mjs +5 -130
- package/dist/lib/registry/index.mjs.map +1 -1
- package/dist/lib/registry/sync.d.mts +8 -2
- package/dist/lib/registry/sync.d.ts +8 -2
- package/dist/lib/registry/sync.d.ts.map +1 -1
- package/dist/lib/registry/sync.js +8 -2
- package/dist/lib/registry/sync.js.map +1 -1
- package/dist/lib/registry/sync.mjs +8 -2
- package/dist/lib/registry/sync.mjs.map +1 -1
- package/dist/lib/registry/types.d.mts +15 -29
- package/dist/lib/registry/types.d.ts +15 -29
- package/dist/lib/registry/types.d.ts.map +1 -1
- package/dist/lib/registry/types.generated.d.mts +20 -5
- package/dist/lib/registry/types.generated.d.ts +20 -5
- package/dist/lib/registry/types.generated.d.ts.map +1 -1
- package/dist/lib/registry/types.generated.js.map +1 -1
- package/dist/lib/registry/types.js.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/runner.js +40 -6
- package/dist/lib/testing/storyboard/runner.js.map +1 -1
- package/dist/lib/testing/storyboard/runner.mjs +40 -6
- package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
- package/dist/lib/types/core.generated.d.mts +105 -58
- package/dist/lib/types/core.generated.d.ts +105 -58
- package/dist/lib/types/core.generated.d.ts.map +1 -1
- package/dist/lib/types/core.generated.js.map +1 -1
- package/dist/lib/types/schemas.generated.d.mts +2352 -473
- package/dist/lib/types/schemas.generated.d.ts +2352 -473
- package/dist/lib/types/schemas.generated.d.ts.map +1 -1
- package/dist/lib/types/schemas.generated.js +78 -45
- package/dist/lib/types/schemas.generated.js.map +1 -1
- package/dist/lib/types/schemas.generated.mjs +71 -45
- package/dist/lib/types/schemas.generated.mjs.map +1 -1
- package/dist/lib/types/tools.generated.d.mts +48 -44
- package/dist/lib/types/tools.generated.d.ts +48 -44
- package/dist/lib/types/tools.generated.d.ts.map +1 -1
- package/dist/lib/types/tools.generated.js.map +1 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.js.map +1 -1
- package/dist/lib/version.mjs +3 -3
- package/dist/lib/version.mjs.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/registry/sync.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\nimport type { RegistryClient } from './index';\nimport type {\n AgentSearchResult,\n AgentCompliance,\n AuthorizationEntry,\n AgentSearchResponse,\n FeedFreshness,\n} from './types.generated';\nimport type { CatalogEvent, FeedResponse, PropertyEventPayload, PropertyIdentifier, ResolvedBrand } from './types';\nimport type { FeedStreamQuery } from './feed-stream';\nimport {\n FeedStreamCursorExpiredError,\n FeedStreamUnsupportedError,\n FeedStreamHttpError,\n sanitizeStreamText,\n} from './feed-stream';\nimport type { CursorStore } from './cursor-store';\nimport { InMemoryCursorStore } from './cursor-store';\n\n// ====== Configuration ======\n\nexport interface RegistrySyncConfig {\n /** RegistryClient instance to use for API calls. */\n client: RegistryClient;\n /**\n * Feed transport for tailing changes after bootstrap:\n * - `'auto'` (default): try the SSE stream, fall back to polling on an\n * unsupported endpoint (404/406), proxy/network failure, or stream parse\n * failure.\n * - `'stream'`: SSE only; reconnect on failure and never fall back to polling.\n * - `'poll'`: polling only.\n */\n transport?: 'auto' | 'stream' | 'poll';\n /** Polling interval in milliseconds. Default: 30000 (30s). */\n pollIntervalMs?: number;\n /**\n * Comma-separated event type filter (glob, e.g. `authorization.*`) applied to\n * every feed read (bootstrap drain, polling, and SSE).\n *\n * A persisted cursor is scoped to the logical `types` subscription it was\n * minted under: a cursor from a broader subscription can skip events that were\n * filtered out previously. This is NOT enforced by the cursor store (the store\n * holds only the cursor string), so if you change `types` you must\n * `reset()` before `start()`. With a persistent {@link FileCursorStore} that\n * survives restarts, changing `types` between deploys against the same store\n * path will silently resume from a foreign cursor — point each subscription at\n * its own store path, or clear the store when the filter changes.\n */\n types?: string;\n /** Max events requested per feed page (polling and SSE). Default: 1000. */\n feedPageLimit?: number;\n /**\n * Server-side caught-up interval hint for the SSE stream, in **seconds** (5–60,\n * default 15) — maps to the `poll_interval_seconds` query param. Note the unit:\n * `pollIntervalMs` and `streamIdleTimeoutMs` are milliseconds.\n */\n streamPollIntervalSeconds?: number;\n /**\n * Client-side idle watchdog: reconnect the stream if no feed page or heartbeat\n * arrives within this window. Must exceed the server poll interval.\n * Default: 90000 (90s).\n */\n streamIdleTimeoutMs?: number;\n /** Reconnect backoff floor in ms. Default: 1000. */\n streamReconnectMinMs?: number;\n /** Reconnect backoff ceiling in ms. Default: 30000. */\n streamReconnectMaxMs?: number;\n /**\n * Consecutive stream failures (with no successful message in between) before\n * `'auto'` mode falls back to polling. Default: 3. Ignored in `'stream'` mode.\n */\n maxStreamFailures?: number;\n /** Choose which indexes to maintain. */\n indexes?: {\n /** Agent inventory profiles. Default: true. */\n agents?: boolean;\n /** Authorization entries (agent→domain mappings). Default: true. */\n authorizations?: boolean;\n /** Property feed records (property_rid→property and publisher domain→properties). Default: true. */\n properties?: boolean;\n /** Ordered brand hierarchy chains (self → parents → house). Default: true. */\n brandHierarchies?: boolean;\n };\n /** Optional cursor store for persisting the feed cursor between restarts. */\n cursorStore?: CursorStore;\n /** Called when RegistrySync intentionally ignores an event family it does not index. */\n onIgnoredEvent?: (event: CatalogEvent, reason: string) => void;\n /** Called on errors during polling/bootstrap. */\n onError?: (error: Error) => void;\n}\n\nexport type RegistrySyncState = 'idle' | 'bootstrapping' | 'syncing' | 'error';\n\n/** Active feed transport once syncing. */\nexport type RegistrySyncTransport = 'stream' | 'poll';\n\n/**\n * Outcome of one SSE connection, driving the reconnect loop.\n * - `closed`: the server ended the stream cleanly (EOF), no error.\n * - `reconnect`: a transport/parse failure or a server `feed_stream_error` —\n * counts toward `auto` polling fallback.\n */\ntype StreamDisposition = 'closed' | 'reconnect' | 'rebootstrap' | 'fallback' | 'fatal' | 'stopped';\ntype RegistrySyncComplianceStatus = AgentCompliance['status'] | 'opted_out';\ntype AuthorizationRemoveOptions = {\n matchByRowId?: boolean;\n fallbackToTupleOnMissingRowId?: boolean;\n};\n\n// ====== Event types ======\n\nexport interface RegistrySyncEvents {\n bootstrap: [{ agentCount: number; authorizationCount: number }];\n sync: [{ cursor: string; eventsApplied: number }];\n /** Emitted for each event applied during polling. Not emitted during bootstrap. */\n event: [{ event: CatalogEvent }];\n error: [{ error: Error }];\n /** Emitted when an agent's compliance status changes. */\n compliance_changed: [\n { agentUrl: string; previousStatus: RegistrySyncComplianceStatus; currentStatus: RegistrySyncComplianceStatus },\n ];\n stateChange: [{ from: RegistrySyncState; to: RegistrySyncState }];\n /** Emitted whenever a feed page or heartbeat carries freshness metadata, for lag monitoring. */\n freshness: [{ freshness: FeedFreshness }];\n /** Emitted when the active feed transport changes (e.g. stream → polling fallback). */\n transport: [{ transport: RegistrySyncTransport }];\n /** Emitted when an event is intentionally not indexed by RegistrySync. */\n ignoredEvent: [{ event: CatalogEvent; reason: string }];\n}\n\n// ====== Agent filter for client-side search ======\n\nexport interface AgentFilter {\n type?: string;\n channels?: string[];\n markets?: string[];\n categories?: string[];\n property_types?: string[];\n tags?: string[];\n delivery_types?: string[];\n has_tmp?: boolean;\n min_properties?: number;\n compliance_status?: RegistrySyncComplianceStatus[];\n}\n\nexport type RegistrySyncProperty = {\n /** Stable registry property RID. */\n property_rid: string;\n /** Publisher domain that owns the property, when supplied by the feed. */\n publisher_domain?: string;\n identifiers?: PropertyIdentifier[];\n classification?: string;\n source?: PropertyEventPayload['source'];\n property?: PropertyEventPayload['property'];\n changed_fields?: string[];\n last_resolved_at?: string;\n reactivated_at?: string;\n reason?: string;\n evidence?: string;\n [key: string]: unknown;\n};\n\n// ====== RegistrySync ======\n\n/**\n * In-memory replica of the AdCP registry.\n *\n * Bootstraps from the agent search endpoint, then tails the change feed —\n * Server-Sent Events by default (`transport: 'auto'`), falling back to polling\n * `/api/registry/feed` when streaming is unavailable — to keep its indexes\n * current for zero-latency lookups.\n *\n * **Staleness:** lookups (`getAgent`, `isAuthorized`, the authorization getters)\n * return the last synced state. After a transient failure the engine keeps\n * reconnecting/polling, but a fatal error (e.g. `401`) leaves `state === 'error'`\n * while the indexes still hold their last values. For decisions where staleness\n * is unsafe (e.g. authorization enforcement), gate on `state` and\n * `getLagSeconds()` / `getFreshness()` rather than trusting a lookup blindly.\n * Index size is bounded only by registry trust — a replica mirrors the whole\n * registry, so an untrusted/compromised feed could grow memory without limit.\n *\n * @example\n * ```ts\n * const client = new RegistryClient({ apiKey: 'sk_...' });\n * const sync = new RegistrySync({ client }); // transport: 'auto' (SSE, polling fallback)\n *\n * // Zero-latency lookups\n * sync.on('event', ({ event }) => console.log('registry change:', event.event_type));\n *\n * // Lag monitoring for the SSE feed\n * sync.on('transport', ({ transport }) => console.log('feed transport:', transport));\n * sync.on('freshness', ({ freshness }) => {\n * if ((freshness.lag_seconds ?? 0) > 300) console.warn('registry feed lag > 5m');\n * });\n *\n * await sync.start();\n * const agent = sync.getAgent('https://ads.example.com');\n * const authorized = sync.isAuthorized('https://ads.example.com', 'publisher.com');\n * const ctv = sync.findAgents({ channels: ['ctv'], markets: ['US'] });\n * sync.stop();\n * ```\n */\nexport class RegistrySync extends EventEmitter<RegistrySyncEvents> {\n private readonly client: RegistryClient;\n private readonly transportMode: 'auto' | 'stream' | 'poll';\n private readonly pollIntervalMs: number;\n private readonly types: string | undefined;\n private readonly feedPageLimit: number;\n private readonly streamPollIntervalSeconds: number;\n private readonly streamIdleTimeoutMs: number;\n private readonly streamReconnectMinMs: number;\n private readonly streamReconnectMaxMs: number;\n private readonly maxStreamFailures: number;\n private readonly indexAgents: boolean;\n private readonly indexAuthorizations: boolean;\n private readonly indexProperties: boolean;\n private readonly indexBrandHierarchies: boolean;\n private readonly ignoredEventHandler: ((event: CatalogEvent, reason: string) => void) | undefined;\n private readonly errorHandler: ((error: Error) => void) | undefined;\n private readonly cursorStore: CursorStore;\n\n private _state: RegistrySyncState = 'idle';\n private cursor: string | null = null;\n private pollTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Streaming state\n private activeTransport: RegistrySyncTransport | null = null;\n private streamController: AbortController | null = null;\n private streamIdleTimer: ReturnType<typeof setTimeout> | null = null;\n private lastFreshness: FeedFreshness | null = null;\n /**\n * Incremented by stop()/reset() and each start(). Async work (bootstrap,\n * rebootstrap, the stream loop) captures its generation and bails the moment\n * it no longer matches — so a stop() that lands mid-bootstrap is honored and\n * a stale loop can never resume after the caller stopped or restarted.\n */\n private generation = 0;\n\n // Indexes\n private agents = new Map<string, AgentSearchResult>();\n private authByDomain = new Map<string, AuthorizationEntry[]>();\n private authByAgent = new Map<string, AuthorizationEntry[]>();\n private authLocationsById = new Map<string, { agentKey: string; domainKey: string }>();\n private propertiesByRid = new Map<string, RegistrySyncProperty>();\n private propertyRidsByDomain = new Map<string, Set<string>>();\n private propertyAliasesByRid = new Map<string, string>();\n private brandAncestorsByDomain = new Map<string, string[]>();\n private brandHierarchyByDomain = new Map<string, ResolvedBrand[]>();\n private brandHierarchyKeysByEntity = new Map<string, Set<string>>();\n private brandHierarchyEntityByKey = new Map<string, string>();\n\n constructor(config: RegistrySyncConfig) {\n super();\n this.client = config.client;\n this.transportMode = config.transport ?? 'auto';\n this.pollIntervalMs = config.pollIntervalMs ?? 30_000;\n this.types = config.types;\n this.feedPageLimit = config.feedPageLimit ?? 1000;\n // Fail closed on out-of-range values rather than letting the registry reject\n // them with a 400 that the reconnect/poll loop would hot-retry forever.\n if (!Number.isInteger(this.feedPageLimit) || this.feedPageLimit < 1 || this.feedPageLimit > 10_000) {\n throw new Error('feedPageLimit must be an integer between 1 and 10000');\n }\n this.streamPollIntervalSeconds = config.streamPollIntervalSeconds ?? 15;\n if (\n !Number.isInteger(this.streamPollIntervalSeconds) ||\n this.streamPollIntervalSeconds < 5 ||\n this.streamPollIntervalSeconds > 60\n ) {\n throw new Error('streamPollIntervalSeconds must be an integer between 5 and 60');\n }\n this.streamIdleTimeoutMs = config.streamIdleTimeoutMs ?? 90_000;\n this.streamReconnectMinMs = config.streamReconnectMinMs ?? 1_000;\n this.streamReconnectMaxMs = config.streamReconnectMaxMs ?? 30_000;\n this.maxStreamFailures = config.maxStreamFailures ?? 3;\n this.indexAgents = config.indexes?.agents !== false;\n this.indexAuthorizations = config.indexes?.authorizations !== false;\n this.indexProperties = config.indexes?.properties !== false;\n this.indexBrandHierarchies = config.indexes?.brandHierarchies !== false;\n this.cursorStore = config.cursorStore ?? new InMemoryCursorStore();\n this.ignoredEventHandler = config.onIgnoredEvent;\n this.errorHandler = config.onError;\n }\n\n // ====== Lifecycle ======\n\n /** Bootstrap from the registry and begin tailing the event feed. */\n async start(): Promise<void> {\n if (this._state === 'syncing' || this._state === 'bootstrapping') return;\n const gen = ++this.generation;\n await this.bootstrap(gen);\n // A stop()/reset() (or another start()) during bootstrap bumps the\n // generation; beginSync re-validates both generation and state before\n // starting the sync loop.\n this.beginSync(gen);\n }\n\n /** Stop tailing the feed. In-memory state is preserved. */\n stop(): void {\n // Invalidate any in-flight bootstrap/rebootstrap/stream loop, even while\n // state is still 'bootstrapping'.\n this.generation++;\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n this.abortStream();\n this.activeTransport = null;\n if (this._state === 'syncing' || this._state === 'bootstrapping') {\n this.setState('idle');\n }\n }\n\n /**\n * Stop, clear all in-memory state, and drop the persisted cursor. Call start()\n * again to re-bootstrap from scratch. Because this clears the stored cursor,\n * it is the correct way to switch the `types` subscription: `reset()` then\n * `start()` begins a fresh subscription rather than resuming a cursor minted\n * under the previous filter.\n */\n async reset(): Promise<void> {\n this.stop();\n this.clearIndexes();\n this.cursor = null;\n this.lastFreshness = null;\n await this.cursorStore.clearCursor();\n this.setState('idle');\n }\n\n /** Begin tailing the feed using the configured transport. Bootstrap leaves state at 'syncing'. */\n private beginSync(gen: number): void {\n if (gen !== this.generation || this._state !== 'syncing') return;\n if (this.transportMode === 'poll') {\n this.setTransport('poll');\n this.schedulePoll(gen);\n return;\n }\n this.setTransport('stream');\n void this.runStream(gen);\n }\n\n // ====== Agent Lookups ======\n\n /** Get an agent by URL. */\n getAgent(url: string): AgentSearchResult | undefined {\n return this.agents.get(url);\n }\n\n /** Get all agents in the index. */\n getAgents(): AgentSearchResult[] {\n return [...this.agents.values()];\n }\n\n /** Find agents matching a filter. All specified dimensions use AND; values within arrays use OR. */\n findAgents(filter: AgentFilter): AgentSearchResult[] {\n return this.getAgents().filter(agent => {\n const p = agent.inventory_profile;\n if (filter.type && agent.type !== filter.type) return false;\n if (filter.channels?.length && !filter.channels.some(c => p.channels.includes(c))) return false;\n if (filter.markets?.length && !filter.markets.some(m => p.markets.includes(m))) return false;\n if (filter.categories?.length && !filter.categories.some(c => p.categories.includes(c))) return false;\n if (filter.property_types?.length && !filter.property_types.some(t => p.property_types.includes(t))) return false;\n if (filter.tags?.length && !filter.tags.some(t => p.tags.includes(t))) return false;\n if (filter.delivery_types?.length && !filter.delivery_types.some(d => p.delivery_types.includes(d))) return false;\n if (filter.has_tmp != null && p.has_tmp !== filter.has_tmp) return false;\n if (filter.min_properties != null && p.property_count < filter.min_properties) return false;\n if (filter.compliance_status?.length) {\n const status = (agent.compliance_summary?.status ?? 'unknown') as RegistrySyncComplianceStatus;\n if (!filter.compliance_status.includes(status)) return false;\n }\n return true;\n });\n }\n\n // ====== Authorization Lookups ======\n\n /** Get all authorizations for a publisher domain. */\n getAuthorizationsForDomain(domain: string): AuthorizationEntry[] {\n return this.authByDomain.get(this.authDomainKey(domain)) ?? [];\n }\n\n /** Get all authorizations for an agent. */\n getAuthorizationsForAgent(agentUrl: string): AuthorizationEntry[] {\n const entries: AuthorizationEntry[] = [];\n for (const key of this.authAgentKeysForUrl(agentUrl)) {\n for (const entry of this.authByAgent.get(key) ?? []) {\n if (!entries.includes(entry)) entries.push(entry);\n }\n }\n return entries;\n }\n\n /**\n * Check if an agent has any authorization for a publisher domain.\n * Does not evaluate property_id scoping, time bounds, or effective dates.\n * For scoped checks, use getAuthorizationsForDomain() and inspect entries directly.\n *\n * Returns the last synced state — see the class-level staleness note. For\n * enforcement, confirm `state === 'syncing'` and an acceptable `getLagSeconds()`\n * before trusting an allow decision, so a stalled feed can't serve a stale\n * authorization after a missed revocation.\n */\n isAuthorized(agentUrl: string, domain: string): boolean {\n const entries = this.authByDomain.get(this.authDomainKey(domain));\n return entries != null && entries.some(entry => this.authorizationAgentMatches(entry, agentUrl));\n }\n\n // ====== Property Lookups ======\n\n /** Get a property feed record by property_rid. Merged aliases resolve to their canonical RID. */\n getProperty(rid: string): RegistrySyncProperty | undefined {\n const property = this.propertiesByRid.get(this.resolvePropertyRid(rid));\n return property ? this.cloneProperty(property) : undefined;\n }\n\n /** Get all property feed records for a publisher domain. */\n getPropertiesForDomain(domain: string): RegistrySyncProperty[] {\n const rids = this.propertyRidsByDomain.get(this.normalizeDomainKey(domain));\n if (!rids) return [];\n const properties: RegistrySyncProperty[] = [];\n for (const rid of rids) {\n const property = this.propertiesByRid.get(this.resolvePropertyRid(rid));\n if (property) properties.push(this.cloneProperty(property));\n }\n return properties;\n }\n\n // ====== Brand Hierarchy Lookups ======\n\n /**\n * Get the ordered corporate ancestor domain chain for a brand domain.\n *\n * The returned array includes the resolved brand itself as the first entry and\n * the house domain as the last entry when known.\n */\n getAncestors(domain: string): string[] {\n return [...(this.brandAncestorsByDomain.get(this.normalizeDomainKey(domain)) ?? [])];\n }\n\n /** Get the ordered resolved brand chain for a brand domain, when the feed supplied it. */\n getBrandHierarchy(domain: string): ResolvedBrand[] {\n return (this.brandHierarchyByDomain.get(this.normalizeDomainKey(domain)) ?? []).map(brand => ({ ...brand }));\n }\n\n // ====== State ======\n\n get state(): RegistrySyncState {\n return this._state;\n }\n\n getCursor(): string | null {\n return this.cursor;\n }\n\n getStats(): { agents: number; authorizations: number; properties: number; brandHierarchies: number } {\n let authCount = 0;\n for (const entries of this.authByDomain.values()) authCount += entries.length;\n return {\n agents: this.agents.size,\n authorizations: authCount,\n properties: this.propertiesByRid.size,\n brandHierarchies: this.brandHierarchyKeysByEntity.size,\n };\n }\n\n /** The active feed transport once syncing, or null when idle. */\n getTransport(): RegistrySyncTransport | null {\n return this.activeTransport;\n }\n\n /**\n * Latest feed freshness metadata, or null if the registry has not reported it\n * yet (e.g. before the first feed page/heartbeat lands). For push updates,\n * prefer the `freshness` event over polling this right after `start()`.\n */\n getFreshness(): FeedFreshness | null {\n return this.lastFreshness;\n }\n\n /** Latest feed lag in seconds, or null when unavailable. Convenience over getFreshness(). */\n getLagSeconds(): number | null {\n return this.lastFreshness?.lag_seconds ?? null;\n }\n\n // ====== Private: Bootstrap ======\n\n private async bootstrap(gen: number): Promise<void> {\n this.setState('bootstrapping');\n try {\n // Restore cursor from store if available\n const storedCursor = await this.cursorStore.getCursor();\n if (storedCursor) {\n this.cursor = storedCursor;\n }\n\n // Paginate searchAgents to load all agents\n if (this.indexAgents) {\n let cursor: string | undefined;\n do {\n const query: Record<string, unknown> = { limit: 200 };\n if (cursor) query.cursor = cursor;\n const res: AgentSearchResponse = await this.client.searchAgents(query as any);\n for (const agent of res.results) {\n this.agents.set(agent.url, agent);\n }\n cursor = res.has_more && res.cursor ? res.cursor : undefined;\n } while (cursor);\n }\n\n // Get initial feed cursor and apply any events\n await this.drainFeed(gen);\n\n // A stop()/reset() landed mid-bootstrap: do not flip to 'syncing' or emit.\n if (gen !== this.generation) return;\n\n this.setState('syncing');\n this.emit('bootstrap', {\n agentCount: this.agents.size,\n authorizationCount: this.getStats().authorizations,\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n // If we were stopped mid-bootstrap, swallow the abort rather than parking\n // the engine in 'error'.\n if (gen !== this.generation) return;\n this.setState('error');\n this.errorHandler?.(error);\n this.emit('error', { error });\n throw error;\n }\n }\n\n /** Clear all state, drop the persisted cursor, and bootstrap again from scratch. */\n private async rebootstrap(gen: number): Promise<void> {\n this.clearIndexes();\n this.cursor = null;\n await this.cursorStore.clearCursor();\n await this.bootstrap(gen);\n }\n\n // ====== Private: Polling ======\n\n private schedulePoll(gen: number): void {\n this.pollTimer = setTimeout(() => this.pollLoop(gen), this.pollIntervalMs);\n }\n\n private async pollLoop(gen: number): Promise<void> {\n if (gen !== this.generation) return;\n try {\n await this.poll(gen);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.emit('error', { error });\n this.errorHandler?.(error);\n }\n // Schedule next poll even after errors (will retry), unless stopped/superseded.\n if (gen === this.generation && this._state === 'syncing') {\n this.schedulePoll(gen);\n }\n }\n\n private async poll(gen: number): Promise<void> {\n let totalEventsApplied = 0;\n let hasMore = true;\n\n while (hasMore) {\n const feed: FeedResponse = await this.client.getFeed(this.feedQuery());\n\n if (feed.cursor_expired) {\n // Cursor aged out of retention: drop state and re-bootstrap, then resume.\n try {\n await this.rebootstrap(gen);\n } catch {\n // bootstrap() already emitted 'error' and parked state in 'error'.\n // Restore 'syncing' so pollLoop reschedules and retries (mirrors the\n // stream path, which also keeps trying after a failed rebootstrap).\n if (gen === this.generation && this._state === 'error') this.setState('syncing');\n }\n return;\n }\n\n for (const event of feed.events) {\n this.applyEvent(event);\n this.emit('event', { event });\n totalEventsApplied++;\n }\n\n this.observeFreshness(feed.freshness);\n\n if (feed.cursor) {\n this.cursor = feed.cursor;\n }\n\n hasMore = feed.has_more && feed.cursor != null;\n }\n\n await this.persistCursor();\n\n if (totalEventsApplied > 0) {\n this.emit('sync', { cursor: this.cursor!, eventsApplied: totalEventsApplied });\n }\n }\n\n /**\n * Drain all available feed pages. Used during bootstrap (does not emit 'event' per event).\n */\n private async drainFeed(gen: number): Promise<void> {\n let hasMore = true;\n let recoveredFromExpiry = false;\n while (hasMore) {\n const feed: FeedResponse = await this.client.getFeed(this.feedQuery());\n if (feed.cursor_expired) {\n // Stored cursor aged out of retention: drop it and retry once from the\n // start of the window. A second expiry (now cursor-less) means a\n // misbehaving server — stop draining rather than hot-looping.\n this.cursor = null;\n await this.cursorStore.clearCursor();\n if (recoveredFromExpiry) break;\n recoveredFromExpiry = true;\n continue;\n }\n for (const event of feed.events) {\n this.applyEvent(event);\n }\n this.observeFreshness(feed.freshness);\n if (feed.cursor) this.cursor = feed.cursor;\n hasMore = feed.has_more && feed.cursor != null;\n }\n // A stop()/reset() during the drain bumps the generation — don't persist a\n // cursor a later start() would read back.\n if (gen === this.generation) await this.persistCursor();\n }\n\n // ====== Private: Streaming ======\n\n private async runStream(gen: number): Promise<void> {\n // `consecutiveFailures` drives 'auto' polling fallback. It counts connections\n // that ended in a transport/parse failure — a heartbeat does NOT reset it\n // (else heartbeat → malformed-frame → reconnect could loop forever and never\n // fall back, despite the parse-failure fallback contract). It resets only on\n // real feed progress or a clean close that delivered something.\n // `consecutiveRebootstraps` bounds a tight cursor_expired loop.\n let consecutiveFailures = 0;\n let consecutiveRebootstraps = 0;\n\n while (gen === this.generation && this.transportMode !== 'poll') {\n const controller = new AbortController();\n this.streamController = controller;\n let feedApplied = false;\n let receivedAny = false;\n let disposition: StreamDisposition;\n\n try {\n disposition = await this.streamConnection(gen, controller.signal, isFeed => {\n receivedAny = true;\n if (isFeed) feedApplied = true;\n });\n } catch (err) {\n // Aborted by stop()/reset() (which bumps the generation): exit quietly.\n if (controller.signal.aborted && gen !== this.generation) {\n return;\n }\n const error = err instanceof Error ? err : new Error(String(err));\n disposition = this.classifyStreamError(error);\n if (disposition === 'reconnect' || disposition === 'fatal') {\n this.emit('error', { error });\n this.errorHandler?.(error);\n }\n } finally {\n this.clearStreamIdleTimer();\n }\n\n if (gen !== this.generation) return;\n // Applying a feed page proves the stream works end to end.\n if (feedApplied) {\n consecutiveFailures = 0;\n consecutiveRebootstraps = 0;\n }\n\n if (disposition === 'stopped') return;\n\n if (disposition === 'fatal') {\n // Permanent error (e.g. 400/401): retrying cannot help. Park in 'error'.\n this.setState('error');\n this.activeTransport = null;\n return;\n }\n\n if (disposition === 'rebootstrap') {\n let ok = false;\n try {\n await this.rebootstrap(gen);\n ok = true;\n } catch {\n // bootstrap() already emitted 'error' and parked state in 'error'.\n }\n if (gen !== this.generation) return;\n if (!ok) {\n consecutiveFailures++;\n if (this.shouldFallBack(consecutiveFailures)) {\n this.fallBackToPolling(gen);\n return;\n }\n // Recover: bootstrap left us in 'error'. In 'stream' mode the contract\n // is to keep reconnecting, so restore 'syncing' and back off.\n this.setState('syncing');\n await this.delay(this.reconnectBackoffMs(consecutiveFailures), controller.signal);\n if (gen !== this.generation) return;\n continue;\n }\n // Successful re-bootstrap. Back off proportionally to repeated expiries\n // so a server stuck emitting cursor_expired can't drive a tight loop.\n consecutiveRebootstraps++;\n await this.delay(this.reconnectBackoffMs(consecutiveRebootstraps), controller.signal);\n if (gen !== this.generation) return;\n continue;\n }\n\n if (disposition === 'fallback') {\n this.fallBackToPolling(gen);\n return;\n }\n\n if (disposition === 'closed' && receivedAny) {\n // Clean close after real activity (feed or heartbeat): the transport\n // works; reconnect promptly without counting it as a failure.\n consecutiveFailures = 0;\n await this.delay(this.reconnectBackoffMs(0), controller.signal);\n if (gen !== this.generation) return;\n continue;\n }\n\n // 'reconnect' (transport/parse failure or server feed_stream_error), or a\n // clean close that delivered nothing — count toward fallback so 'auto'\n // degrades to polling rather than looping on a stream that never delivers.\n consecutiveFailures++;\n if (this.shouldFallBack(consecutiveFailures)) {\n this.fallBackToPolling(gen);\n return;\n }\n await this.delay(this.reconnectBackoffMs(consecutiveFailures), controller.signal);\n if (gen !== this.generation) return;\n }\n }\n\n /**\n * Consume one SSE connection. Invokes `onMessage(isFeed)` for each feed page\n * (true) or heartbeat (false) received. Returns the disposition for the\n * reconnect loop: `closed` on a clean server EOF, `reconnect` on a server\n * `feed_stream_error`, `rebootstrap` on `cursor_expired`, `stopped` if the\n * engine was stopped mid-stream.\n */\n private async streamConnection(\n gen: number,\n signal: AbortSignal,\n onMessage: (isFeed: boolean) => void\n ): Promise<StreamDisposition> {\n this.armStreamIdleTimer();\n const query: FeedStreamQuery = {\n cursor: this.cursor ?? undefined,\n types: this.types,\n limit: this.feedPageLimit,\n pollIntervalSeconds: this.streamPollIntervalSeconds,\n };\n\n for await (const msg of this.client.streamFeed(query, { signal })) {\n if (gen !== this.generation) return 'stopped';\n this.armStreamIdleTimer();\n\n if (msg.type === 'feed') {\n onMessage(true);\n await this.applyStreamPage(msg.page);\n } else if (msg.type === 'heartbeat') {\n onMessage(false);\n // Heartbeats keep the connection alive and expose freshness; they do NOT\n // advance the cursor and do NOT count as feed progress for fallback.\n this.observeFreshness(msg.heartbeat.freshness);\n } else {\n // 'error' — the server closes the stream after this frame.\n if (msg.error.error === 'cursor_expired') {\n return 'rebootstrap';\n }\n // Registry-supplied strings are untrusted: escape before logging/emitting.\n const code = sanitizeStreamText(msg.error.error);\n const detail = msg.error.message ? ` (${sanitizeStreamText(msg.error.message)})` : '';\n const error = new Error(`registry feed stream error: ${code}${detail}`);\n this.emit('error', { error });\n this.errorHandler?.(error);\n return 'reconnect';\n }\n }\n\n // Stream closed cleanly by the server — reconnect from the last cursor.\n return 'closed';\n }\n\n /** Apply one SSE feed page: events, freshness, then advance + persist the cursor. */\n private async applyStreamPage(page: FeedResponse): Promise<void> {\n let applied = 0;\n for (const event of page.events) {\n this.applyEvent(event);\n this.emit('event', { event });\n applied++;\n }\n this.observeFreshness(page.freshness);\n\n // Advance the cursor only after the full page is applied, so a mid-page\n // disconnect resumes from the last fully-applied page.\n if (page.cursor) {\n this.cursor = page.cursor;\n try {\n await this.persistCursor();\n } catch (err) {\n // Persisting is best-effort; replay after restart is idempotent.\n const error = err instanceof Error ? err : new Error(String(err));\n this.emit('error', { error });\n this.errorHandler?.(error);\n }\n }\n\n if (applied > 0) {\n this.emit('sync', { cursor: this.cursor!, eventsApplied: applied });\n }\n }\n\n private classifyStreamError(error: Error): StreamDisposition {\n if (error instanceof FeedStreamCursorExpiredError) return 'rebootstrap';\n if (error instanceof FeedStreamUnsupportedError) {\n // Endpoint absent (older registry) or a proxy returned non-stream content.\n return this.transportMode === 'auto' ? 'fallback' : 'reconnect';\n }\n if (error instanceof FeedStreamHttpError && (error.status === 400 || error.status === 401)) {\n // Permanent client-side error: a malformed request or bad credentials.\n // Retrying (or polling, which hits the same status) cannot recover.\n return 'fatal';\n }\n // Other HTTP errors, parse failures, idle timeouts, network/abort errors.\n return 'reconnect';\n }\n\n private shouldFallBack(consecutiveFailures: number): boolean {\n return this.transportMode === 'auto' && consecutiveFailures >= this.maxStreamFailures;\n }\n\n private fallBackToPolling(gen: number): void {\n if (gen !== this.generation || this._state !== 'syncing') return;\n this.abortStream();\n this.setTransport('poll');\n this.schedulePoll(gen);\n }\n\n private armStreamIdleTimer(): void {\n this.clearStreamIdleTimer();\n const controller = this.streamController;\n if (!controller) return;\n this.streamIdleTimer = setTimeout(() => {\n controller.abort(new Error('registry feed stream idle timeout'));\n }, this.streamIdleTimeoutMs);\n this.streamIdleTimer.unref?.();\n }\n\n private clearStreamIdleTimer(): void {\n if (this.streamIdleTimer) {\n clearTimeout(this.streamIdleTimer);\n this.streamIdleTimer = null;\n }\n }\n\n private abortStream(): void {\n this.clearStreamIdleTimer();\n if (this.streamController) {\n this.streamController.abort();\n this.streamController = null;\n }\n }\n\n private reconnectBackoffMs(attempt: number): number {\n const exp = this.streamReconnectMinMs * 2 ** Math.max(0, attempt - 1);\n return Math.min(this.streamReconnectMaxMs, Math.max(this.streamReconnectMinMs, exp));\n }\n\n private delay(ms: number, signal: AbortSignal): Promise<void> {\n if (signal.aborted) return Promise.resolve();\n return new Promise(resolve => {\n const onAbort = () => {\n clearTimeout(timer);\n resolve();\n };\n const timer = setTimeout(() => {\n signal.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n timer.unref?.();\n signal.addEventListener('abort', onAbort, { once: true });\n });\n }\n\n // ====== Private: Shared feed helpers ======\n\n private feedQuery(): { cursor?: string; types?: string; limit: number } {\n const query: { cursor?: string; types?: string; limit: number } = { limit: this.feedPageLimit };\n if (this.cursor) query.cursor = this.cursor;\n if (this.types) query.types = this.types;\n return query;\n }\n\n private observeFreshness(freshness: FeedFreshness | undefined): void {\n if (!freshness) return;\n this.lastFreshness = freshness;\n this.emit('freshness', { freshness });\n }\n\n private async persistCursor(): Promise<void> {\n if (this.cursor) {\n await this.cursorStore.setCursor(this.cursor);\n }\n }\n\n private setTransport(transport: RegistrySyncTransport): void {\n if (this.activeTransport === transport) return;\n this.activeTransport = transport;\n this.emit('transport', { transport });\n }\n\n // ====== Private: Event Application ======\n\n private applyEvent(event: CatalogEvent): void {\n const payload = this.asRecord(event.payload);\n\n switch (event.event_type) {\n case 'agent.discovered':\n case 'agent.profile_updated': {\n if (!this.indexAgents) break;\n const existing = this.agents.get(event.entity_id);\n const inventoryProfile = this.isAgentInventoryProfile(payload.inventory_profile)\n ? payload.inventory_profile\n : undefined;\n if (existing && inventoryProfile) {\n this.agents.set(event.entity_id, {\n ...existing,\n inventory_profile: inventoryProfile,\n });\n } else if (!existing) {\n // New agent: use payload data or create stub\n this.agents.set(event.entity_id, {\n url: event.entity_id,\n name: typeof payload.name === 'string' ? payload.name : event.entity_id,\n type: typeof payload.type === 'string' ? payload.type : 'unknown',\n inventory_profile: inventoryProfile ?? this.emptyInventoryProfile(),\n match: { score: 0, matched_filters: [] },\n });\n }\n break;\n }\n\n case 'agent.removed': {\n if (this.indexAgents) this.agents.delete(event.entity_id);\n if (this.indexAuthorizations) this.removeAuthorizationsForAgent(event.entity_id);\n break;\n }\n\n case 'authorization.granted': {\n if (!this.indexAuthorizations) break;\n if (!this.isAuthorizationPayload(payload)) break;\n if (typeof payload.authorization_type !== 'string') break;\n this.upsertAuthorizationEntry(this.authorizationEntryFromEvent(payload));\n break;\n }\n\n case 'authorization.modified': {\n if (!this.indexAuthorizations) break;\n if (!this.isAuthorizationPayload(payload)) break;\n const entry = this.authorizationEntryFromEvent(payload);\n if (!entry.id && !entry.authorization_type) break;\n this.upsertAuthorizationEntry(entry, { fallbackToTupleOnMissingRowId: true });\n break;\n }\n\n case 'authorization.revoked': {\n if (!this.indexAuthorizations) break;\n if (!this.isAuthorizationPayload(payload)) break;\n const entry = this.authorizationEntryFromEvent(payload);\n this.removeAuthorizationEntries(entry, {\n matchByRowId: Boolean(entry.id || !entry.authorization_type),\n fallbackToTupleOnMissingRowId: true,\n });\n break;\n }\n\n case 'agent.compliance_changed': {\n if (this.indexAgents) {\n const existing = this.agents.get(event.entity_id);\n const summary = payload.compliance_summary;\n if (existing && this.isAgentCompliance(summary)) {\n this.agents.set(event.entity_id, {\n ...existing,\n compliance_summary: summary,\n });\n }\n }\n this.emit('compliance_changed', {\n agentUrl: event.entity_id,\n previousStatus: (typeof payload.previous_status === 'string'\n ? payload.previous_status\n : 'unknown') as RegistrySyncComplianceStatus,\n currentStatus: (typeof payload.current_status === 'string'\n ? payload.current_status\n : 'unknown') as RegistrySyncComplianceStatus,\n });\n break;\n }\n\n case 'property.created':\n case 'property.updated':\n case 'property.reactivated':\n case 'property.stale': {\n if (this.indexProperties) this.upsertPropertyEvent(payload);\n break;\n }\n\n case 'property.merged': {\n if (this.indexProperties) this.handlePropertyMerge(payload);\n break;\n }\n\n case 'brand.hierarchy_updated':\n case 'brand.updated':\n case 'brand.resolved': {\n if (this.indexBrandHierarchies) this.applyBrandHierarchyEvent(event.entity_id, payload);\n break;\n }\n\n case 'brand.removed':\n case 'brand.deleted': {\n if (this.indexBrandHierarchies) this.deleteBrandHierarchy(event.entity_id, payload);\n break;\n }\n\n case 'collection.created':\n case 'collection.updated':\n case 'collection.merged':\n case 'collection.removed': {\n this.ignoreEvent(event, 'collection.* indexing requires typed collection schemas and a reference consumer');\n break;\n }\n\n case 'publisher.adagents_changed':\n case 'publisher.adagents_discovered': {\n this.ignoreEvent(event, 'publisher.* indexing requires a reference consumer');\n break;\n }\n\n default:\n this.ignoreEvent(event, 'unknown or unsupported registry feed event type');\n break;\n }\n }\n\n // ====== Private: Helpers ======\n\n private clearIndexes(): void {\n this.agents.clear();\n this.authByDomain.clear();\n this.authByAgent.clear();\n this.authLocationsById.clear();\n this.propertiesByRid.clear();\n this.propertyRidsByDomain.clear();\n this.propertyAliasesByRid.clear();\n this.brandAncestorsByDomain.clear();\n this.brandHierarchyByDomain.clear();\n this.brandHierarchyKeysByEntity.clear();\n this.brandHierarchyEntityByKey.clear();\n }\n\n private ignoreEvent(event: CatalogEvent, reason: string): void {\n this.ignoredEventHandler?.(event, reason);\n this.emit('ignoredEvent', { event, reason });\n }\n\n private upsertAuthorizationEntry(\n entry: AuthorizationEntry,\n options?: { fallbackToTupleOnMissingRowId?: boolean }\n ): void {\n const normalizedEntry = this.normalizeAuthorizationEntry(entry);\n this.removeAuthorizationEntries(normalizedEntry, {\n matchByRowId: Boolean(normalizedEntry.id),\n fallbackToTupleOnMissingRowId: options?.fallbackToTupleOnMissingRowId,\n });\n\n const domainKey = this.authDomainKey(normalizedEntry.publisher_domain);\n const domainEntries = this.authByDomain.get(domainKey) ?? [];\n domainEntries.push(normalizedEntry);\n this.authByDomain.set(domainKey, domainEntries);\n\n const agentKey = this.authAgentKey(normalizedEntry);\n for (const key of this.authAgentKeys(normalizedEntry)) {\n const agentEntries = this.authByAgent.get(key) ?? [];\n agentEntries.push(normalizedEntry);\n this.authByAgent.set(key, agentEntries);\n }\n\n if (normalizedEntry.id) {\n this.authLocationsById.set(normalizedEntry.id, {\n agentKey,\n domainKey,\n });\n }\n }\n\n private normalizeAuthorizationEntry(entry: AuthorizationEntry): AuthorizationEntry {\n if (entry.effective_until || !entry.effective_to) return entry;\n return { ...entry, effective_until: entry.effective_to };\n }\n\n private authorizationEntryFromEvent(entry: AuthorizationEntry): AuthorizationEntry {\n return this.normalizeAuthorizationEntry(entry);\n }\n\n private removeAuthorizationsForAgent(agentUrl: string): void {\n const agentKeys = this.authAgentKeysForUrl(agentUrl);\n for (const [key, entries] of this.authByAgent) {\n if (entries.some(entry => this.authorizationAgentMatches(entry, agentUrl))) agentKeys.add(key);\n }\n\n for (const agentKey of agentKeys) {\n const entries = [...(this.authByAgent.get(agentKey) ?? [])];\n for (const entry of entries) {\n this.removeAuthorizationEntries(entry, {\n matchByRowId: Boolean(entry.id),\n fallbackToTupleOnMissingRowId: true,\n });\n }\n this.authByAgent.delete(agentKey);\n }\n }\n\n private removeAuthorizationEntries(target: AuthorizationEntry, options?: AuthorizationRemoveOptions): void {\n const normalizedTarget = this.normalizeAuthorizationEntry(target);\n const domainKeys = new Set<string>([this.authDomainKey(normalizedTarget.publisher_domain)]);\n const agentKeys = this.authAgentKeys(normalizedTarget);\n let effectiveOptions = options;\n\n if (normalizedTarget.id && options?.matchByRowId) {\n const existingLocation = this.authLocationsById.get(normalizedTarget.id);\n if (!existingLocation && options.fallbackToTupleOnMissingRowId) {\n effectiveOptions = { ...options, matchByRowId: false };\n }\n if (existingLocation) {\n domainKeys.add(existingLocation.domainKey);\n agentKeys.add(existingLocation.agentKey);\n }\n }\n\n for (const domain of domainKeys) {\n const entries = this.authByDomain.get(domain);\n if (!entries) continue;\n for (const entry of entries) {\n if (this.authorizationMatches(entry, normalizedTarget, effectiveOptions)) {\n for (const key of this.authAgentKeys(entry)) agentKeys.add(key);\n }\n }\n }\n\n for (const domain of domainKeys) {\n this.removeAuthorizationEntriesFromBucket(this.authByDomain, domain, normalizedTarget, effectiveOptions);\n }\n\n for (const agentUrl of agentKeys) {\n this.removeAuthorizationEntriesFromBucket(this.authByAgent, agentUrl, normalizedTarget, effectiveOptions);\n }\n }\n\n private removeAuthorizationEntriesFromBucket(\n bucket: Map<string, AuthorizationEntry[]>,\n key: string,\n target: AuthorizationEntry,\n options?: AuthorizationRemoveOptions\n ): void {\n const entries = bucket.get(key);\n if (!entries) return;\n\n const filtered: AuthorizationEntry[] = [];\n for (const entry of entries) {\n if (this.authorizationMatches(entry, target, options)) {\n if (entry.id) this.authLocationsById.delete(entry.id);\n } else {\n filtered.push(entry);\n }\n }\n if (filtered.length > 0) bucket.set(key, filtered);\n else bucket.delete(key);\n }\n\n private authorizationMatches(\n entry: AuthorizationEntry,\n target: AuthorizationEntry,\n options?: AuthorizationRemoveOptions\n ): boolean {\n if (target.id && options?.matchByRowId) return entry.id === target.id;\n if (!this.authorizationEntriesShareAgent(entry, target)) return false;\n if (this.authDomainKey(entry.publisher_domain) !== this.authDomainKey(target.publisher_domain)) return false;\n if (target.authorization_type) return entry.authorization_type === target.authorization_type;\n return true;\n }\n\n private authorizationAgentMatches(entry: AuthorizationEntry, agentUrl: string): boolean {\n return this.authAgentKeysOverlap(this.authAgentKeys(entry), this.authAgentKeysForUrl(agentUrl));\n }\n\n private authorizationEntriesShareAgent(\n left: Pick<AuthorizationEntry, 'agent_url' | 'agent_url_canonical'>,\n right: Pick<AuthorizationEntry, 'agent_url' | 'agent_url_canonical'>\n ): boolean {\n return this.authAgentKeysOverlap(this.authAgentKeys(left), this.authAgentKeys(right));\n }\n\n private authDomainKey(domain: string): string {\n return this.normalizeDomainKey(domain);\n }\n\n private authAgentKey(entry: Pick<AuthorizationEntry, 'agent_url' | 'agent_url_canonical'>): string {\n return this.authAgentKeyFromUrl(entry.agent_url);\n }\n\n private authAgentKeys(entry: Pick<AuthorizationEntry, 'agent_url' | 'agent_url_canonical'>): Set<string> {\n const keys = this.authAgentKeysForUrl(entry.agent_url);\n if (entry.agent_url_canonical?.trim()) {\n for (const key of this.authAgentKeysForUrl(entry.agent_url_canonical)) keys.add(key);\n }\n return keys;\n }\n\n private authAgentKeysForUrl(agentUrl: string): Set<string> {\n const trimmed = agentUrl.trim();\n const keys = new Set<string>();\n if (trimmed) keys.add(trimmed);\n keys.add(this.authAgentKeyFromUrl(agentUrl));\n return keys;\n }\n\n private authAgentKeysOverlap(left: Set<string>, right: Set<string>): boolean {\n for (const key of left) {\n if (right.has(key)) return true;\n }\n return false;\n }\n\n private authAgentKeyFromUrl(agentUrl: string): string {\n const trimmed = agentUrl.trim();\n if (!trimmed) return trimmed;\n\n try {\n const parsed = new URL(trimmed);\n if (parsed.host) return `${parsed.protocol.toLowerCase()}//${parsed.host.toLowerCase()}`;\n } catch {\n // Fall through to best-effort normalization for malformed or scheme-less values.\n }\n\n const schemeEnd = trimmed.indexOf('://');\n const authorityStart = schemeEnd > 0 ? schemeEnd + 3 : 0;\n let authorityEnd = trimmed.length;\n for (const delimiter of ['/', '?', '#'] as const) {\n const index = trimmed.indexOf(delimiter, authorityStart);\n if (index >= 0 && index < authorityEnd) authorityEnd = index;\n }\n if (authorityEnd === authorityStart) return trimmed.toLowerCase();\n\n const scheme = schemeEnd > 0 ? `${trimmed.slice(0, authorityStart).toLowerCase()}` : '';\n return `${scheme}${trimmed.slice(authorityStart, authorityEnd).toLowerCase()}`;\n }\n\n private upsertPropertyEvent(payload: Record<string, unknown>): void {\n const rid = this.propertyRidFromPayload(payload);\n if (!rid) return;\n const canonicalRid = this.resolvePropertyRid(rid);\n const existing = this.propertiesByRid.get(canonicalRid);\n const next = this.propertyFromPayload(canonicalRid, payload, existing);\n if (!next) return;\n\n this.setPropertyEntry(next, existing);\n }\n\n private handlePropertyMerge(payload: Record<string, unknown>): void {\n if (typeof payload.alias_rid !== 'string' || typeof payload.canonical_rid !== 'string') return;\n const aliasRid = this.resolvePropertyRid(payload.alias_rid);\n const canonicalRid = this.resolvePropertyRid(payload.canonical_rid);\n if (!aliasRid || !canonicalRid || aliasRid === canonicalRid) return;\n\n const aliasEntry = this.propertiesByRid.get(aliasRid);\n const canonicalEntry = this.propertiesByRid.get(canonicalRid);\n const payloadEntry = this.propertyFromPayload(canonicalRid, payload);\n this.propertyAliasesByRid.set(payload.alias_rid, canonicalRid);\n this.propertyAliasesByRid.set(aliasRid, canonicalRid);\n\n if (!aliasEntry && !canonicalEntry && !payloadEntry) return;\n\n const merged: RegistrySyncProperty = {\n ...(aliasEntry ? this.cloneProperty(aliasEntry) : {}),\n ...(canonicalEntry ? this.cloneProperty(canonicalEntry) : {}),\n ...(payloadEntry ? this.cloneProperty(payloadEntry) : {}),\n property_rid: canonicalRid,\n };\n if (!merged.publisher_domain) {\n merged.publisher_domain = canonicalEntry?.publisher_domain ?? aliasEntry?.publisher_domain;\n }\n\n this.deletePropertyEntry(aliasRid);\n this.setPropertyEntry(merged, canonicalEntry);\n }\n\n private propertyFromPayload(\n rid: string,\n payload: Record<string, unknown>,\n existing?: RegistrySyncProperty\n ): RegistrySyncProperty | null {\n const publisherDomain = this.propertyDomainFromPayload(payload) ?? existing?.publisher_domain;\n const next: RegistrySyncProperty = existing ? this.cloneProperty(existing) : { property_rid: rid };\n let hasPayloadFields = false;\n\n for (const [key, value] of Object.entries(payload)) {\n if (value === undefined || key === 'alias_rid' || key === 'canonical_rid') continue;\n hasPayloadFields = true;\n next[key] = this.cloneJsonValue(value);\n }\n next.property_rid = rid;\n if (publisherDomain) next.publisher_domain = publisherDomain;\n\n return hasPayloadFields || publisherDomain || existing ? next : null;\n }\n\n private setPropertyEntry(entry: RegistrySyncProperty, previous?: RegistrySyncProperty): void {\n if (previous?.publisher_domain && previous.publisher_domain !== entry.publisher_domain) {\n this.removePropertyRidFromDomain(entry.property_rid, previous.publisher_domain);\n }\n this.propertiesByRid.set(entry.property_rid, this.cloneProperty(entry));\n if (entry.publisher_domain) {\n const key = this.normalizeDomainKey(entry.publisher_domain);\n const rids = this.propertyRidsByDomain.get(key) ?? new Set<string>();\n rids.add(entry.property_rid);\n this.propertyRidsByDomain.set(key, rids);\n }\n }\n\n private deletePropertyEntry(rid: string): void {\n const entry = this.propertiesByRid.get(rid);\n if (entry?.publisher_domain) this.removePropertyRidFromDomain(rid, entry.publisher_domain);\n this.propertiesByRid.delete(rid);\n }\n\n private removePropertyRidFromDomain(rid: string, domain: string): void {\n const key = this.normalizeDomainKey(domain);\n const rids = this.propertyRidsByDomain.get(key);\n if (!rids) return;\n rids.delete(rid);\n if (rids.size === 0) this.propertyRidsByDomain.delete(key);\n }\n\n private propertyRidFromPayload(payload: Record<string, unknown>): string | null {\n if (typeof payload.property_rid === 'string' && payload.property_rid.trim()) return payload.property_rid;\n if (typeof payload.rid === 'string' && payload.rid.trim()) return payload.rid;\n if (typeof payload.id === 'string' && payload.id.trim()) return payload.id;\n return null;\n }\n\n private propertyDomainFromPayload(payload: Record<string, unknown>): string | undefined {\n if (typeof payload.publisher_domain === 'string' && payload.publisher_domain.trim())\n return payload.publisher_domain;\n const property = this.asRecord(payload.property);\n return typeof property.publisher_domain === 'string' && property.publisher_domain.trim()\n ? property.publisher_domain\n : undefined;\n }\n\n private stringValue(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value : undefined;\n }\n\n private resolvePropertyRid(rid: string): string {\n let current = rid;\n const seen = new Set<string>();\n while (this.propertyAliasesByRid.has(current) && !seen.has(current)) {\n seen.add(current);\n current = this.propertyAliasesByRid.get(current)!;\n }\n return current;\n }\n\n private cloneProperty(property: RegistrySyncProperty): RegistrySyncProperty {\n return this.cloneJsonValue(property) as RegistrySyncProperty;\n }\n\n private applyBrandHierarchyEvent(entityId: string, payload: Record<string, unknown>): void {\n const resolvedChain = this.extractResolvedBrandChain(payload.chain);\n const domainChain =\n resolvedChain.length > 0\n ? resolvedChain.map(brand => this.domainFromBrand(brand)).filter((domain): domain is string => domain != null)\n : (this.extractDomainChain(payload.chain) ??\n this.extractDomainChain(payload.ancestor_domains) ??\n this.extractDomainChain(payload.domains));\n\n if (!domainChain || domainChain.length === 0) return;\n\n const keys = new Set<string>();\n keys.add(this.normalizeDomainKey(entityId));\n for (const field of ['domain', 'canonical_domain', 'canonical_id'] as const) {\n const value = payload[field];\n if (typeof value === 'string' && value.trim()) keys.add(this.normalizeDomainKey(value));\n }\n keys.add(this.normalizeDomainKey(domainChain[0]!));\n\n const entityKey = this.normalizeDomainKey(entityId);\n const entitiesToClear = new Set<string>([entityKey]);\n for (const key of keys) {\n const existingEntity = this.brandHierarchyEntityByKey.get(key);\n if (existingEntity) entitiesToClear.add(existingEntity);\n }\n for (const key of entitiesToClear) this.clearBrandHierarchyEntity(key);\n\n for (const key of keys) {\n this.brandAncestorsByDomain.set(key, [...domainChain]);\n if (resolvedChain.length > 0)\n this.brandHierarchyByDomain.set(\n key,\n resolvedChain.map(brand => ({ ...brand }))\n );\n else this.brandHierarchyByDomain.delete(key);\n this.brandHierarchyEntityByKey.set(key, entityKey);\n }\n this.brandHierarchyKeysByEntity.set(entityKey, keys);\n }\n\n private deleteBrandHierarchy(entityId: string, payload: Record<string, unknown>): void {\n const keys = new Set<string>([this.normalizeDomainKey(entityId)]);\n for (const field of ['domain', 'canonical_domain', 'canonical_id'] as const) {\n const value = payload[field];\n if (typeof value === 'string' && value.trim()) keys.add(this.normalizeDomainKey(value));\n }\n const entitiesToClear = new Set<string>();\n for (const key of keys) {\n const existingEntity = this.brandHierarchyEntityByKey.get(key);\n if (existingEntity) entitiesToClear.add(existingEntity);\n }\n if (entitiesToClear.size > 0) {\n for (const key of entitiesToClear) this.clearBrandHierarchyEntity(key);\n return;\n }\n for (const key of keys) {\n this.brandAncestorsByDomain.delete(key);\n this.brandHierarchyByDomain.delete(key);\n this.brandHierarchyEntityByKey.delete(key);\n }\n }\n\n private clearBrandHierarchyEntity(entityKey: string): void {\n const keys = this.brandHierarchyKeysByEntity.get(entityKey);\n if (!keys) return;\n for (const key of keys) {\n this.brandAncestorsByDomain.delete(key);\n this.brandHierarchyByDomain.delete(key);\n this.brandHierarchyEntityByKey.delete(key);\n }\n this.brandHierarchyKeysByEntity.delete(entityKey);\n }\n\n private asRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : {};\n }\n\n private emptyInventoryProfile(): AgentSearchResult['inventory_profile'] {\n return {\n channels: [],\n property_types: [],\n markets: [],\n categories: [],\n category_taxonomy: 'iab_content_3.0',\n tags: [],\n delivery_types: [],\n property_count: 0,\n publisher_count: 0,\n has_tmp: false,\n };\n }\n\n private isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every(item => typeof item === 'string');\n }\n\n private isAgentInventoryProfile(value: unknown): value is AgentSearchResult['inventory_profile'] {\n const profile = this.asRecord(value);\n return (\n this.isStringArray(profile.channels) &&\n this.isStringArray(profile.property_types) &&\n this.isStringArray(profile.markets) &&\n this.isStringArray(profile.categories) &&\n (typeof profile.category_taxonomy === 'string' || profile.category_taxonomy === null) &&\n this.isStringArray(profile.tags) &&\n this.isStringArray(profile.delivery_types) &&\n (profile.format_ids === undefined || this.isStringArray(profile.format_ids)) &&\n typeof profile.property_count === 'number' &&\n typeof profile.publisher_count === 'number' &&\n typeof profile.has_tmp === 'boolean'\n );\n }\n\n private isAuthorizationPayload(value: unknown): value is AuthorizationEntry {\n const payload = this.asRecord(value);\n return (\n (payload.id === undefined || this.stringValue(payload.id) !== undefined) &&\n this.stringValue(payload.agent_url) !== undefined &&\n this.stringValue(payload.publisher_domain) !== undefined &&\n (payload.authorization_type === undefined || this.stringValue(payload.authorization_type) !== undefined) &&\n (payload.property_ids === undefined || this.isStringArray(payload.property_ids)) &&\n (payload.property_tags === undefined || this.isStringArray(payload.property_tags)) &&\n (payload.placement_ids === undefined || this.isStringArray(payload.placement_ids)) &&\n (payload.placement_tags === undefined || this.isStringArray(payload.placement_tags)) &&\n (payload.countries === undefined || this.isStringArray(payload.countries)) &&\n (payload.effective_from === undefined || typeof payload.effective_from === 'string') &&\n (payload.effective_until === undefined || typeof payload.effective_until === 'string') &&\n (payload.effective_to === undefined || typeof payload.effective_to === 'string')\n );\n }\n\n private isAgentCompliance(value: unknown): value is AgentCompliance {\n const summary = this.asRecord(value);\n const statuses = new Set(['passing', 'degraded', 'failing', 'unknown', 'opted_out']);\n const lifecycleStages = new Set(['development', 'testing', 'production', 'deprecated']);\n if (summary.status === 'opted_out') {\n return (\n (summary.lifecycle_stage === undefined ||\n (typeof summary.lifecycle_stage === 'string' && lifecycleStages.has(summary.lifecycle_stage))) &&\n (summary.tracks === undefined ||\n (summary.tracks != null && typeof summary.tracks === 'object' && !Array.isArray(summary.tracks))) &&\n (summary.streak_days === undefined || typeof summary.streak_days === 'number') &&\n (summary.last_checked_at === undefined ||\n typeof summary.last_checked_at === 'string' ||\n summary.last_checked_at === null) &&\n (summary.headline === undefined || typeof summary.headline === 'string' || summary.headline === null)\n );\n }\n return (\n typeof summary.status === 'string' &&\n statuses.has(summary.status) &&\n typeof summary.lifecycle_stage === 'string' &&\n lifecycleStages.has(summary.lifecycle_stage) &&\n summary.tracks != null &&\n typeof summary.tracks === 'object' &&\n !Array.isArray(summary.tracks) &&\n typeof summary.streak_days === 'number' &&\n (typeof summary.last_checked_at === 'string' || summary.last_checked_at === null) &&\n (typeof summary.headline === 'string' || summary.headline === null)\n );\n }\n\n private isResolvedBrand(value: unknown): value is ResolvedBrand {\n const brand = this.asRecord(value);\n return typeof brand.canonical_id === 'string' && typeof brand.canonical_domain === 'string';\n }\n\n private cloneJsonValue<T>(value: T): T {\n if (Array.isArray(value)) return value.map(item => this.cloneJsonValue(item)) as T;\n if (value && typeof value === 'object') {\n const copy: Record<string, unknown> = {};\n for (const [key, nested] of Object.entries(value)) {\n copy[key] = this.cloneJsonValue(nested);\n }\n return copy as T;\n }\n return value;\n }\n\n private extractResolvedBrandChain(value: unknown): ResolvedBrand[] {\n if (!Array.isArray(value)) return [];\n return value.filter(item => this.isResolvedBrand(item));\n }\n\n private extractDomainChain(value: unknown): string[] | null {\n if (!Array.isArray(value)) return null;\n const domains: string[] = [];\n for (const item of value) {\n if (typeof item === 'string' && item.trim()) {\n domains.push(item);\n continue;\n }\n if (item && typeof item === 'object' && !Array.isArray(item)) {\n const domain = this.domainFromBrand(item as Partial<ResolvedBrand>);\n if (domain) domains.push(domain);\n }\n }\n return domains.length > 0 ? domains : null;\n }\n\n private domainFromBrand(brand: Partial<ResolvedBrand>): string | null {\n return brand.canonical_domain ?? brand.canonical_id ?? null;\n }\n\n private normalizeDomainKey(domain: string): string {\n return domain.trim().toLowerCase();\n }\n\n private setState(next: RegistrySyncState): void {\n const from = this._state;\n if (from === next) return;\n this._state = next;\n this.emit('stateChange', { from, to: next });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAA6B;AAW7B,yBAKO;AAEP,0BAAoC;AAyL7B,MAAM,qBAAqB,gCAAiC;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,SAA4B;AAAA,EAC5B,SAAwB;AAAA,EACxB,YAAkD;AAAA;AAAA,EAGlD,kBAAgD;AAAA,EAChD,mBAA2C;AAAA,EAC3C,kBAAwD;AAAA,EACxD,gBAAsC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOtC,aAAa;AAAA;AAAA,EAGb,SAAS,oBAAI,IAA+B;AAAA,EAC5C,eAAe,oBAAI,IAAkC;AAAA,EACrD,cAAc,oBAAI,IAAkC;AAAA,EACpD,oBAAoB,oBAAI,IAAqD;AAAA,EAC7E,kBAAkB,oBAAI,IAAkC;AAAA,EACxD,uBAAuB,oBAAI,IAAyB;AAAA,EACpD,uBAAuB,oBAAI,IAAoB;AAAA,EAC/C,yBAAyB,oBAAI,IAAsB;AAAA,EACnD,yBAAyB,oBAAI,IAA6B;AAAA,EAC1D,6BAA6B,oBAAI,IAAyB;AAAA,EAC1D,4BAA4B,oBAAI,IAAoB;AAAA,EAE5D,YAAY,QAA4B;AACtC,UAAM;AACN,SAAK,SAAS,OAAO;AACrB,SAAK,gBAAgB,OAAO,aAAa;AACzC,SAAK,iBAAiB,OAAO,kBAAkB;AAC/C,SAAK,QAAQ,OAAO;AACpB,SAAK,gBAAgB,OAAO,iBAAiB;AAG7C,QAAI,CAAC,OAAO,UAAU,KAAK,aAAa,KAAK,KAAK,gBAAgB,KAAK,KAAK,gBAAgB,KAAQ;AAClG,YAAM,IAAI,MAAM,sDAAsD;AAAA,IACxE;AACA,SAAK,4BAA4B,OAAO,6BAA6B;AACrE,QACE,CAAC,OAAO,UAAU,KAAK,yBAAyB,KAChD,KAAK,4BAA4B,KACjC,KAAK,4BAA4B,IACjC;AACA,YAAM,IAAI,MAAM,+DAA+D;AAAA,IACjF;AACA,SAAK,sBAAsB,OAAO,uBAAuB;AACzD,SAAK,uBAAuB,OAAO,wBAAwB;AAC3D,SAAK,uBAAuB,OAAO,wBAAwB;AAC3D,SAAK,oBAAoB,OAAO,qBAAqB;AACrD,SAAK,cAAc,OAAO,SAAS,WAAW;AAC9C,SAAK,sBAAsB,OAAO,SAAS,mBAAmB;AAC9D,SAAK,kBAAkB,OAAO,SAAS,eAAe;AACtD,SAAK,wBAAwB,OAAO,SAAS,qBAAqB;AAClE,SAAK,cAAc,OAAO,eAAe,IAAI,wCAAoB;AACjE,SAAK,sBAAsB,OAAO;AAClC,SAAK,eAAe,OAAO;AAAA,EAC7B;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AAC3B,QAAI,KAAK,WAAW,aAAa,KAAK,WAAW,gBAAiB;AAClE,UAAM,MAAM,EAAE,KAAK;AACnB,UAAM,KAAK,UAAU,GAAG;AAIxB,SAAK,UAAU,GAAG;AAAA,EACpB;AAAA;AAAA,EAGA,OAAa;AAGX,SAAK;AACL,QAAI,KAAK,WAAW;AAClB,mBAAa,KAAK,SAAS;AAC3B,WAAK,YAAY;AAAA,IACnB;AACA,SAAK,YAAY;AACjB,SAAK,kBAAkB;AACvB,QAAI,KAAK,WAAW,aAAa,KAAK,WAAW,iBAAiB;AAChE,WAAK,SAAS,MAAM;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,QAAuB;AAC3B,SAAK,KAAK;AACV,SAAK,aAAa;AAClB,SAAK,SAAS;AACd,SAAK,gBAAgB;AACrB,UAAM,KAAK,YAAY,YAAY;AACnC,SAAK,SAAS,MAAM;AAAA,EACtB;AAAA;AAAA,EAGQ,UAAU,KAAmB;AACnC,QAAI,QAAQ,KAAK,cAAc,KAAK,WAAW,UAAW;AAC1D,QAAI,KAAK,kBAAkB,QAAQ;AACjC,WAAK,aAAa,MAAM;AACxB,WAAK,aAAa,GAAG;AACrB;AAAA,IACF;AACA,SAAK,aAAa,QAAQ;AAC1B,SAAK,KAAK,UAAU,GAAG;AAAA,EACzB;AAAA;AAAA;AAAA,EAKA,SAAS,KAA4C;AACnD,WAAO,KAAK,OAAO,IAAI,GAAG;AAAA,EAC5B;AAAA;AAAA,EAGA,YAAiC;AAC/B,WAAO,CAAC,GAAG,KAAK,OAAO,OAAO,CAAC;AAAA,EACjC;AAAA;AAAA,EAGA,WAAW,QAA0C;AACnD,WAAO,KAAK,UAAU,EAAE,OAAO,WAAS;AACtC,YAAM,IAAI,MAAM;AAChB,UAAI,OAAO,QAAQ,MAAM,SAAS,OAAO,KAAM,QAAO;AACtD,UAAI,OAAO,UAAU,UAAU,CAAC,OAAO,SAAS,KAAK,OAAK,EAAE,SAAS,SAAS,CAAC,CAAC,EAAG,QAAO;AAC1F,UAAI,OAAO,SAAS,UAAU,CAAC,OAAO,QAAQ,KAAK,OAAK,EAAE,QAAQ,SAAS,CAAC,CAAC,EAAG,QAAO;AACvF,UAAI,OAAO,YAAY,UAAU,CAAC,OAAO,WAAW,KAAK,OAAK,EAAE,WAAW,SAAS,CAAC,CAAC,EAAG,QAAO;AAChG,UAAI,OAAO,gBAAgB,UAAU,CAAC,OAAO,eAAe,KAAK,OAAK,EAAE,eAAe,SAAS,CAAC,CAAC,EAAG,QAAO;AAC5G,UAAI,OAAO,MAAM,UAAU,CAAC,OAAO,KAAK,KAAK,OAAK,EAAE,KAAK,SAAS,CAAC,CAAC,EAAG,QAAO;AAC9E,UAAI,OAAO,gBAAgB,UAAU,CAAC,OAAO,eAAe,KAAK,OAAK,EAAE,eAAe,SAAS,CAAC,CAAC,EAAG,QAAO;AAC5G,UAAI,OAAO,WAAW,QAAQ,EAAE,YAAY,OAAO,QAAS,QAAO;AACnE,UAAI,OAAO,kBAAkB,QAAQ,EAAE,iBAAiB,OAAO,eAAgB,QAAO;AACtF,UAAI,OAAO,mBAAmB,QAAQ;AACpC,cAAM,SAAU,MAAM,oBAAoB,UAAU;AACpD,YAAI,CAAC,OAAO,kBAAkB,SAAS,MAAM,EAAG,QAAO;AAAA,MACzD;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAAA;AAAA;AAAA,EAKA,2BAA2B,QAAsC;AAC/D,WAAO,KAAK,aAAa,IAAI,KAAK,cAAc,MAAM,CAAC,KAAK,CAAC;AAAA,EAC/D;AAAA;AAAA,EAGA,0BAA0B,UAAwC;AAChE,UAAM,UAAgC,CAAC;AACvC,eAAW,OAAO,KAAK,oBAAoB,QAAQ,GAAG;AACpD,iBAAW,SAAS,KAAK,YAAY,IAAI,GAAG,KAAK,CAAC,GAAG;AACnD,YAAI,CAAC,QAAQ,SAAS,KAAK,EAAG,SAAQ,KAAK,KAAK;AAAA,MAClD;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,aAAa,UAAkB,QAAyB;AACtD,UAAM,UAAU,KAAK,aAAa,IAAI,KAAK,cAAc,MAAM,CAAC;AAChE,WAAO,WAAW,QAAQ,QAAQ,KAAK,WAAS,KAAK,0BAA0B,OAAO,QAAQ,CAAC;AAAA,EACjG;AAAA;AAAA;AAAA,EAKA,YAAY,KAA+C;AACzD,UAAM,WAAW,KAAK,gBAAgB,IAAI,KAAK,mBAAmB,GAAG,CAAC;AACtE,WAAO,WAAW,KAAK,cAAc,QAAQ,IAAI;AAAA,EACnD;AAAA;AAAA,EAGA,uBAAuB,QAAwC;AAC7D,UAAM,OAAO,KAAK,qBAAqB,IAAI,KAAK,mBAAmB,MAAM,CAAC;AAC1E,QAAI,CAAC,KAAM,QAAO,CAAC;AACnB,UAAM,aAAqC,CAAC;AAC5C,eAAW,OAAO,MAAM;AACtB,YAAM,WAAW,KAAK,gBAAgB,IAAI,KAAK,mBAAmB,GAAG,CAAC;AACtE,UAAI,SAAU,YAAW,KAAK,KAAK,cAAc,QAAQ,CAAC;AAAA,IAC5D;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,aAAa,QAA0B;AACrC,WAAO,CAAC,GAAI,KAAK,uBAAuB,IAAI,KAAK,mBAAmB,MAAM,CAAC,KAAK,CAAC,CAAE;AAAA,EACrF;AAAA;AAAA,EAGA,kBAAkB,QAAiC;AACjD,YAAQ,KAAK,uBAAuB,IAAI,KAAK,mBAAmB,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,YAAU,EAAE,GAAG,MAAM,EAAE;AAAA,EAC7G;AAAA;AAAA,EAIA,IAAI,QAA2B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,YAA2B;AACzB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,WAAqG;AACnG,QAAI,YAAY;AAChB,eAAW,WAAW,KAAK,aAAa,OAAO,EAAG,cAAa,QAAQ;AACvE,WAAO;AAAA,MACL,QAAQ,KAAK,OAAO;AAAA,MACpB,gBAAgB;AAAA,MAChB,YAAY,KAAK,gBAAgB;AAAA,MACjC,kBAAkB,KAAK,2BAA2B;AAAA,IACpD;AAAA,EACF;AAAA;AAAA,EAGA,eAA6C;AAC3C,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,eAAqC;AACnC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,gBAA+B;AAC7B,WAAO,KAAK,eAAe,eAAe;AAAA,EAC5C;AAAA;AAAA,EAIA,MAAc,UAAU,KAA4B;AAClD,SAAK,SAAS,eAAe;AAC7B,QAAI;AAEF,YAAM,eAAe,MAAM,KAAK,YAAY,UAAU;AACtD,UAAI,cAAc;AAChB,aAAK,SAAS;AAAA,MAChB;AAGA,UAAI,KAAK,aAAa;AACpB,YAAI;AACJ,WAAG;AACD,gBAAM,QAAiC,EAAE,OAAO,IAAI;AACpD,cAAI,OAAQ,OAAM,SAAS;AAC3B,gBAAM,MAA2B,MAAM,KAAK,OAAO,aAAa,KAAY;AAC5E,qBAAW,SAAS,IAAI,SAAS;AAC/B,iBAAK,OAAO,IAAI,MAAM,KAAK,KAAK;AAAA,UAClC;AACA,mBAAS,IAAI,YAAY,IAAI,SAAS,IAAI,SAAS;AAAA,QACrD,SAAS;AAAA,MACX;AAGA,YAAM,KAAK,UAAU,GAAG;AAGxB,UAAI,QAAQ,KAAK,WAAY;AAE7B,WAAK,SAAS,SAAS;AACvB,WAAK,KAAK,aAAa;AAAA,QACrB,YAAY,KAAK,OAAO;AAAA,QACxB,oBAAoB,KAAK,SAAS,EAAE;AAAA,MACtC,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,YAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAGhE,UAAI,QAAQ,KAAK,WAAY;AAC7B,WAAK,SAAS,OAAO;AACrB,WAAK,eAAe,KAAK;AACzB,WAAK,KAAK,SAAS,EAAE,MAAM,CAAC;AAC5B,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA,EAGA,MAAc,YAAY,KAA4B;AACpD,SAAK,aAAa;AAClB,SAAK,SAAS;AACd,UAAM,KAAK,YAAY,YAAY;AACnC,UAAM,KAAK,UAAU,GAAG;AAAA,EAC1B;AAAA;AAAA,EAIQ,aAAa,KAAmB;AACtC,SAAK,YAAY,WAAW,MAAM,KAAK,SAAS,GAAG,GAAG,KAAK,cAAc;AAAA,EAC3E;AAAA,EAEA,MAAc,SAAS,KAA4B;AACjD,QAAI,QAAQ,KAAK,WAAY;AAC7B,QAAI;AACF,YAAM,KAAK,KAAK,GAAG;AAAA,IACrB,SAAS,KAAK;AACZ,YAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,WAAK,KAAK,SAAS,EAAE,MAAM,CAAC;AAC5B,WAAK,eAAe,KAAK;AAAA,IAC3B;AAEA,QAAI,QAAQ,KAAK,cAAc,KAAK,WAAW,WAAW;AACxD,WAAK,aAAa,GAAG;AAAA,IACvB;AAAA,EACF;AAAA,EAEA,MAAc,KAAK,KAA4B;AAC7C,QAAI,qBAAqB;AACzB,QAAI,UAAU;AAEd,WAAO,SAAS;AACd,YAAM,OAAqB,MAAM,KAAK,OAAO,QAAQ,KAAK,UAAU,CAAC;AAErE,UAAI,KAAK,gBAAgB;AAEvB,YAAI;AACF,gBAAM,KAAK,YAAY,GAAG;AAAA,QAC5B,QAAQ;AAIN,cAAI,QAAQ,KAAK,cAAc,KAAK,WAAW,QAAS,MAAK,SAAS,SAAS;AAAA,QACjF;AACA;AAAA,MACF;AAEA,iBAAW,SAAS,KAAK,QAAQ;AAC/B,aAAK,WAAW,KAAK;AACrB,aAAK,KAAK,SAAS,EAAE,MAAM,CAAC;AAC5B;AAAA,MACF;AAEA,WAAK,iBAAiB,KAAK,SAAS;AAEpC,UAAI,KAAK,QAAQ;AACf,aAAK,SAAS,KAAK;AAAA,MACrB;AAEA,gBAAU,KAAK,YAAY,KAAK,UAAU;AAAA,IAC5C;AAEA,UAAM,KAAK,cAAc;AAEzB,QAAI,qBAAqB,GAAG;AAC1B,WAAK,KAAK,QAAQ,EAAE,QAAQ,KAAK,QAAS,eAAe,mBAAmB,CAAC;AAAA,IAC/E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,UAAU,KAA4B;AAClD,QAAI,UAAU;AACd,QAAI,sBAAsB;AAC1B,WAAO,SAAS;AACd,YAAM,OAAqB,MAAM,KAAK,OAAO,QAAQ,KAAK,UAAU,CAAC;AACrE,UAAI,KAAK,gBAAgB;AAIvB,aAAK,SAAS;AACd,cAAM,KAAK,YAAY,YAAY;AACnC,YAAI,oBAAqB;AACzB,8BAAsB;AACtB;AAAA,MACF;AACA,iBAAW,SAAS,KAAK,QAAQ;AAC/B,aAAK,WAAW,KAAK;AAAA,MACvB;AACA,WAAK,iBAAiB,KAAK,SAAS;AACpC,UAAI,KAAK,OAAQ,MAAK,SAAS,KAAK;AACpC,gBAAU,KAAK,YAAY,KAAK,UAAU;AAAA,IAC5C;AAGA,QAAI,QAAQ,KAAK,WAAY,OAAM,KAAK,cAAc;AAAA,EACxD;AAAA;AAAA,EAIA,MAAc,UAAU,KAA4B;AAOlD,QAAI,sBAAsB;AAC1B,QAAI,0BAA0B;AAE9B,WAAO,QAAQ,KAAK,cAAc,KAAK,kBAAkB,QAAQ;AAC/D,YAAM,aAAa,IAAI,gBAAgB;AACvC,WAAK,mBAAmB;AACxB,UAAI,cAAc;AAClB,UAAI,cAAc;AAClB,UAAI;AAEJ,UAAI;AACF,sBAAc,MAAM,KAAK,iBAAiB,KAAK,WAAW,QAAQ,YAAU;AAC1E,wBAAc;AACd,cAAI,OAAQ,eAAc;AAAA,QAC5B,CAAC;AAAA,MACH,SAAS,KAAK;AAEZ,YAAI,WAAW,OAAO,WAAW,QAAQ,KAAK,YAAY;AACxD;AAAA,QACF;AACA,cAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,sBAAc,KAAK,oBAAoB,KAAK;AAC5C,YAAI,gBAAgB,eAAe,gBAAgB,SAAS;AAC1D,eAAK,KAAK,SAAS,EAAE,MAAM,CAAC;AAC5B,eAAK,eAAe,KAAK;AAAA,QAC3B;AAAA,MACF,UAAE;AACA,aAAK,qBAAqB;AAAA,MAC5B;AAEA,UAAI,QAAQ,KAAK,WAAY;AAE7B,UAAI,aAAa;AACf,8BAAsB;AACtB,kCAA0B;AAAA,MAC5B;AAEA,UAAI,gBAAgB,UAAW;AAE/B,UAAI,gBAAgB,SAAS;AAE3B,aAAK,SAAS,OAAO;AACrB,aAAK,kBAAkB;AACvB;AAAA,MACF;AAEA,UAAI,gBAAgB,eAAe;AACjC,YAAI,KAAK;AACT,YAAI;AACF,gBAAM,KAAK,YAAY,GAAG;AAC1B,eAAK;AAAA,QACP,QAAQ;AAAA,QAER;AACA,YAAI,QAAQ,KAAK,WAAY;AAC7B,YAAI,CAAC,IAAI;AACP;AACA,cAAI,KAAK,eAAe,mBAAmB,GAAG;AAC5C,iBAAK,kBAAkB,GAAG;AAC1B;AAAA,UACF;AAGA,eAAK,SAAS,SAAS;AACvB,gBAAM,KAAK,MAAM,KAAK,mBAAmB,mBAAmB,GAAG,WAAW,MAAM;AAChF,cAAI,QAAQ,KAAK,WAAY;AAC7B;AAAA,QACF;AAGA;AACA,cAAM,KAAK,MAAM,KAAK,mBAAmB,uBAAuB,GAAG,WAAW,MAAM;AACpF,YAAI,QAAQ,KAAK,WAAY;AAC7B;AAAA,MACF;AAEA,UAAI,gBAAgB,YAAY;AAC9B,aAAK,kBAAkB,GAAG;AAC1B;AAAA,MACF;AAEA,UAAI,gBAAgB,YAAY,aAAa;AAG3C,8BAAsB;AACtB,cAAM,KAAK,MAAM,KAAK,mBAAmB,CAAC,GAAG,WAAW,MAAM;AAC9D,YAAI,QAAQ,KAAK,WAAY;AAC7B;AAAA,MACF;AAKA;AACA,UAAI,KAAK,eAAe,mBAAmB,GAAG;AAC5C,aAAK,kBAAkB,GAAG;AAC1B;AAAA,MACF;AACA,YAAM,KAAK,MAAM,KAAK,mBAAmB,mBAAmB,GAAG,WAAW,MAAM;AAChF,UAAI,QAAQ,KAAK,WAAY;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,iBACZ,KACA,QACA,WAC4B;AAC5B,SAAK,mBAAmB;AACxB,UAAM,QAAyB;AAAA,MAC7B,QAAQ,KAAK,UAAU;AAAA,MACvB,OAAO,KAAK;AAAA,MACZ,OAAO,KAAK;AAAA,MACZ,qBAAqB,KAAK;AAAA,IAC5B;AAEA,qBAAiB,OAAO,KAAK,OAAO,WAAW,OAAO,EAAE,OAAO,CAAC,GAAG;AACjE,UAAI,QAAQ,KAAK,WAAY,QAAO;AACpC,WAAK,mBAAmB;AAExB,UAAI,IAAI,SAAS,QAAQ;AACvB,kBAAU,IAAI;AACd,cAAM,KAAK,gBAAgB,IAAI,IAAI;AAAA,MACrC,WAAW,IAAI,SAAS,aAAa;AACnC,kBAAU,KAAK;AAGf,aAAK,iBAAiB,IAAI,UAAU,SAAS;AAAA,MAC/C,OAAO;AAEL,YAAI,IAAI,MAAM,UAAU,kBAAkB;AACxC,iBAAO;AAAA,QACT;AAEA,cAAM,WAAO,uCAAmB,IAAI,MAAM,KAAK;AAC/C,cAAM,SAAS,IAAI,MAAM,UAAU,SAAK,uCAAmB,IAAI,MAAM,OAAO,CAAC,MAAM;AACnF,cAAM,QAAQ,IAAI,MAAM,+BAA+B,IAAI,GAAG,MAAM,EAAE;AACtE,aAAK,KAAK,SAAS,EAAE,MAAM,CAAC;AAC5B,aAAK,eAAe,KAAK;AACzB,eAAO;AAAA,MACT;AAAA,IACF;AAGA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MAAc,gBAAgB,MAAmC;AAC/D,QAAI,UAAU;AACd,eAAW,SAAS,KAAK,QAAQ;AAC/B,WAAK,WAAW,KAAK;AACrB,WAAK,KAAK,SAAS,EAAE,MAAM,CAAC;AAC5B;AAAA,IACF;AACA,SAAK,iBAAiB,KAAK,SAAS;AAIpC,QAAI,KAAK,QAAQ;AACf,WAAK,SAAS,KAAK;AACnB,UAAI;AACF,cAAM,KAAK,cAAc;AAAA,MAC3B,SAAS,KAAK;AAEZ,cAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,aAAK,KAAK,SAAS,EAAE,MAAM,CAAC;AAC5B,aAAK,eAAe,KAAK;AAAA,MAC3B;AAAA,IACF;AAEA,QAAI,UAAU,GAAG;AACf,WAAK,KAAK,QAAQ,EAAE,QAAQ,KAAK,QAAS,eAAe,QAAQ,CAAC;AAAA,IACpE;AAAA,EACF;AAAA,EAEQ,oBAAoB,OAAiC;AAC3D,QAAI,iBAAiB,gDAA8B,QAAO;AAC1D,QAAI,iBAAiB,+CAA4B;AAE/C,aAAO,KAAK,kBAAkB,SAAS,aAAa;AAAA,IACtD;AACA,QAAI,iBAAiB,2CAAwB,MAAM,WAAW,OAAO,MAAM,WAAW,MAAM;AAG1F,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,eAAe,qBAAsC;AAC3D,WAAO,KAAK,kBAAkB,UAAU,uBAAuB,KAAK;AAAA,EACtE;AAAA,EAEQ,kBAAkB,KAAmB;AAC3C,QAAI,QAAQ,KAAK,cAAc,KAAK,WAAW,UAAW;AAC1D,SAAK,YAAY;AACjB,SAAK,aAAa,MAAM;AACxB,SAAK,aAAa,GAAG;AAAA,EACvB;AAAA,EAEQ,qBAA2B;AACjC,SAAK,qBAAqB;AAC1B,UAAM,aAAa,KAAK;AACxB,QAAI,CAAC,WAAY;AACjB,SAAK,kBAAkB,WAAW,MAAM;AACtC,iBAAW,MAAM,IAAI,MAAM,mCAAmC,CAAC;AAAA,IACjE,GAAG,KAAK,mBAAmB;AAC3B,SAAK,gBAAgB,QAAQ;AAAA,EAC/B;AAAA,EAEQ,uBAA6B;AACnC,QAAI,KAAK,iBAAiB;AACxB,mBAAa,KAAK,eAAe;AACjC,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AAAA,EAEQ,cAAoB;AAC1B,SAAK,qBAAqB;AAC1B,QAAI,KAAK,kBAAkB;AACzB,WAAK,iBAAiB,MAAM;AAC5B,WAAK,mBAAmB;AAAA,IAC1B;AAAA,EACF;AAAA,EAEQ,mBAAmB,SAAyB;AAClD,UAAM,MAAM,KAAK,uBAAuB,KAAK,KAAK,IAAI,GAAG,UAAU,CAAC;AACpE,WAAO,KAAK,IAAI,KAAK,sBAAsB,KAAK,IAAI,KAAK,sBAAsB,GAAG,CAAC;AAAA,EACrF;AAAA,EAEQ,MAAM,IAAY,QAAoC;AAC5D,QAAI,OAAO,QAAS,QAAO,QAAQ,QAAQ;AAC3C,WAAO,IAAI,QAAQ,aAAW;AAC5B,YAAM,UAAU,MAAM;AACpB,qBAAa,KAAK;AAClB,gBAAQ;AAAA,MACV;AACA,YAAM,QAAQ,WAAW,MAAM;AAC7B,eAAO,oBAAoB,SAAS,OAAO;AAC3C,gBAAQ;AAAA,MACV,GAAG,EAAE;AACL,YAAM,QAAQ;AACd,aAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,IAC1D,CAAC;AAAA,EACH;AAAA;AAAA,EAIQ,YAAgE;AACtE,UAAM,QAA4D,EAAE,OAAO,KAAK,cAAc;AAC9F,QAAI,KAAK,OAAQ,OAAM,SAAS,KAAK;AACrC,QAAI,KAAK,MAAO,OAAM,QAAQ,KAAK;AACnC,WAAO;AAAA,EACT;AAAA,EAEQ,iBAAiB,WAA4C;AACnE,QAAI,CAAC,UAAW;AAChB,SAAK,gBAAgB;AACrB,SAAK,KAAK,aAAa,EAAE,UAAU,CAAC;AAAA,EACtC;AAAA,EAEA,MAAc,gBAA+B;AAC3C,QAAI,KAAK,QAAQ;AACf,YAAM,KAAK,YAAY,UAAU,KAAK,MAAM;AAAA,IAC9C;AAAA,EACF;AAAA,EAEQ,aAAa,WAAwC;AAC3D,QAAI,KAAK,oBAAoB,UAAW;AACxC,SAAK,kBAAkB;AACvB,SAAK,KAAK,aAAa,EAAE,UAAU,CAAC;AAAA,EACtC;AAAA;AAAA,EAIQ,WAAW,OAA2B;AAC5C,UAAM,UAAU,KAAK,SAAS,MAAM,OAAO;AAE3C,YAAQ,MAAM,YAAY;AAAA,MACxB,KAAK;AAAA,MACL,KAAK,yBAAyB;AAC5B,YAAI,CAAC,KAAK,YAAa;AACvB,cAAM,WAAW,KAAK,OAAO,IAAI,MAAM,SAAS;AAChD,cAAM,mBAAmB,KAAK,wBAAwB,QAAQ,iBAAiB,IAC3E,QAAQ,oBACR;AACJ,YAAI,YAAY,kBAAkB;AAChC,eAAK,OAAO,IAAI,MAAM,WAAW;AAAA,YAC/B,GAAG;AAAA,YACH,mBAAmB;AAAA,UACrB,CAAC;AAAA,QACH,WAAW,CAAC,UAAU;AAEpB,eAAK,OAAO,IAAI,MAAM,WAAW;AAAA,YAC/B,KAAK,MAAM;AAAA,YACX,MAAM,OAAO,QAAQ,SAAS,WAAW,QAAQ,OAAO,MAAM;AAAA,YAC9D,MAAM,OAAO,QAAQ,SAAS,WAAW,QAAQ,OAAO;AAAA,YACxD,mBAAmB,oBAAoB,KAAK,sBAAsB;AAAA,YAClE,OAAO,EAAE,OAAO,GAAG,iBAAiB,CAAC,EAAE;AAAA,UACzC,CAAC;AAAA,QACH;AACA;AAAA,MACF;AAAA,MAEA,KAAK,iBAAiB;AACpB,YAAI,KAAK,YAAa,MAAK,OAAO,OAAO,MAAM,SAAS;AACxD,YAAI,KAAK,oBAAqB,MAAK,6BAA6B,MAAM,SAAS;AAC/E;AAAA,MACF;AAAA,MAEA,KAAK,yBAAyB;AAC5B,YAAI,CAAC,KAAK,oBAAqB;AAC/B,YAAI,CAAC,KAAK,uBAAuB,OAAO,EAAG;AAC3C,YAAI,OAAO,QAAQ,uBAAuB,SAAU;AACpD,aAAK,yBAAyB,KAAK,4BAA4B,OAAO,CAAC;AACvE;AAAA,MACF;AAAA,MAEA,KAAK,0BAA0B;AAC7B,YAAI,CAAC,KAAK,oBAAqB;AAC/B,YAAI,CAAC,KAAK,uBAAuB,OAAO,EAAG;AAC3C,cAAM,QAAQ,KAAK,4BAA4B,OAAO;AACtD,YAAI,CAAC,MAAM,MAAM,CAAC,MAAM,mBAAoB;AAC5C,aAAK,yBAAyB,OAAO,EAAE,+BAA+B,KAAK,CAAC;AAC5E;AAAA,MACF;AAAA,MAEA,KAAK,yBAAyB;AAC5B,YAAI,CAAC,KAAK,oBAAqB;AAC/B,YAAI,CAAC,KAAK,uBAAuB,OAAO,EAAG;AAC3C,cAAM,QAAQ,KAAK,4BAA4B,OAAO;AACtD,aAAK,2BAA2B,OAAO;AAAA,UACrC,cAAc,QAAQ,MAAM,MAAM,CAAC,MAAM,kBAAkB;AAAA,UAC3D,+BAA+B;AAAA,QACjC,CAAC;AACD;AAAA,MACF;AAAA,MAEA,KAAK,4BAA4B;AAC/B,YAAI,KAAK,aAAa;AACpB,gBAAM,WAAW,KAAK,OAAO,IAAI,MAAM,SAAS;AAChD,gBAAM,UAAU,QAAQ;AACxB,cAAI,YAAY,KAAK,kBAAkB,OAAO,GAAG;AAC/C,iBAAK,OAAO,IAAI,MAAM,WAAW;AAAA,cAC/B,GAAG;AAAA,cACH,oBAAoB;AAAA,YACtB,CAAC;AAAA,UACH;AAAA,QACF;AACA,aAAK,KAAK,sBAAsB;AAAA,UAC9B,UAAU,MAAM;AAAA,UAChB,gBAAiB,OAAO,QAAQ,oBAAoB,WAChD,QAAQ,kBACR;AAAA,UACJ,eAAgB,OAAO,QAAQ,mBAAmB,WAC9C,QAAQ,iBACR;AAAA,QACN,CAAC;AACD;AAAA,MACF;AAAA,MAEA,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,kBAAkB;AACrB,YAAI,KAAK,gBAAiB,MAAK,oBAAoB,OAAO;AAC1D;AAAA,MACF;AAAA,MAEA,KAAK,mBAAmB;AACtB,YAAI,KAAK,gBAAiB,MAAK,oBAAoB,OAAO;AAC1D;AAAA,MACF;AAAA,MAEA,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,kBAAkB;AACrB,YAAI,KAAK,sBAAuB,MAAK,yBAAyB,MAAM,WAAW,OAAO;AACtF;AAAA,MACF;AAAA,MAEA,KAAK;AAAA,MACL,KAAK,iBAAiB;AACpB,YAAI,KAAK,sBAAuB,MAAK,qBAAqB,MAAM,WAAW,OAAO;AAClF;AAAA,MACF;AAAA,MAEA,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,sBAAsB;AACzB,aAAK,YAAY,OAAO,kFAAkF;AAC1G;AAAA,MACF;AAAA,MAEA,KAAK;AAAA,MACL,KAAK,iCAAiC;AACpC,aAAK,YAAY,OAAO,oDAAoD;AAC5E;AAAA,MACF;AAAA,MAEA;AACE,aAAK,YAAY,OAAO,iDAAiD;AACzE;AAAA,IACJ;AAAA,EACF;AAAA;AAAA,EAIQ,eAAqB;AAC3B,SAAK,OAAO,MAAM;AAClB,SAAK,aAAa,MAAM;AACxB,SAAK,YAAY,MAAM;AACvB,SAAK,kBAAkB,MAAM;AAC7B,SAAK,gBAAgB,MAAM;AAC3B,SAAK,qBAAqB,MAAM;AAChC,SAAK,qBAAqB,MAAM;AAChC,SAAK,uBAAuB,MAAM;AAClC,SAAK,uBAAuB,MAAM;AAClC,SAAK,2BAA2B,MAAM;AACtC,SAAK,0BAA0B,MAAM;AAAA,EACvC;AAAA,EAEQ,YAAY,OAAqB,QAAsB;AAC7D,SAAK,sBAAsB,OAAO,MAAM;AACxC,SAAK,KAAK,gBAAgB,EAAE,OAAO,OAAO,CAAC;AAAA,EAC7C;AAAA,EAEQ,yBACN,OACA,SACM;AACN,UAAM,kBAAkB,KAAK,4BAA4B,KAAK;AAC9D,SAAK,2BAA2B,iBAAiB;AAAA,MAC/C,cAAc,QAAQ,gBAAgB,EAAE;AAAA,MACxC,+BAA+B,SAAS;AAAA,IAC1C,CAAC;AAED,UAAM,YAAY,KAAK,cAAc,gBAAgB,gBAAgB;AACrE,UAAM,gBAAgB,KAAK,aAAa,IAAI,SAAS,KAAK,CAAC;AAC3D,kBAAc,KAAK,eAAe;AAClC,SAAK,aAAa,IAAI,WAAW,aAAa;AAE9C,UAAM,WAAW,KAAK,aAAa,eAAe;AAClD,eAAW,OAAO,KAAK,cAAc,eAAe,GAAG;AACrD,YAAM,eAAe,KAAK,YAAY,IAAI,GAAG,KAAK,CAAC;AACnD,mBAAa,KAAK,eAAe;AACjC,WAAK,YAAY,IAAI,KAAK,YAAY;AAAA,IACxC;AAEA,QAAI,gBAAgB,IAAI;AACtB,WAAK,kBAAkB,IAAI,gBAAgB,IAAI;AAAA,QAC7C;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEQ,4BAA4B,OAA+C;AACjF,QAAI,MAAM,mBAAmB,CAAC,MAAM,aAAc,QAAO;AACzD,WAAO,EAAE,GAAG,OAAO,iBAAiB,MAAM,aAAa;AAAA,EACzD;AAAA,EAEQ,4BAA4B,OAA+C;AACjF,WAAO,KAAK,4BAA4B,KAAK;AAAA,EAC/C;AAAA,EAEQ,6BAA6B,UAAwB;AAC3D,UAAM,YAAY,KAAK,oBAAoB,QAAQ;AACnD,eAAW,CAAC,KAAK,OAAO,KAAK,KAAK,aAAa;AAC7C,UAAI,QAAQ,KAAK,WAAS,KAAK,0BAA0B,OAAO,QAAQ,CAAC,EAAG,WAAU,IAAI,GAAG;AAAA,IAC/F;AAEA,eAAW,YAAY,WAAW;AAChC,YAAM,UAAU,CAAC,GAAI,KAAK,YAAY,IAAI,QAAQ,KAAK,CAAC,CAAE;AAC1D,iBAAW,SAAS,SAAS;AAC3B,aAAK,2BAA2B,OAAO;AAAA,UACrC,cAAc,QAAQ,MAAM,EAAE;AAAA,UAC9B,+BAA+B;AAAA,QACjC,CAAC;AAAA,MACH;AACA,WAAK,YAAY,OAAO,QAAQ;AAAA,IAClC;AAAA,EACF;AAAA,EAEQ,2BAA2B,QAA4B,SAA4C;AACzG,UAAM,mBAAmB,KAAK,4BAA4B,MAAM;AAChE,UAAM,aAAa,oBAAI,IAAY,CAAC,KAAK,cAAc,iBAAiB,gBAAgB,CAAC,CAAC;AAC1F,UAAM,YAAY,KAAK,cAAc,gBAAgB;AACrD,QAAI,mBAAmB;AAEvB,QAAI,iBAAiB,MAAM,SAAS,cAAc;AAChD,YAAM,mBAAmB,KAAK,kBAAkB,IAAI,iBAAiB,EAAE;AACvE,UAAI,CAAC,oBAAoB,QAAQ,+BAA+B;AAC9D,2BAAmB,EAAE,GAAG,SAAS,cAAc,MAAM;AAAA,MACvD;AACA,UAAI,kBAAkB;AACpB,mBAAW,IAAI,iBAAiB,SAAS;AACzC,kBAAU,IAAI,iBAAiB,QAAQ;AAAA,MACzC;AAAA,IACF;AAEA,eAAW,UAAU,YAAY;AAC/B,YAAM,UAAU,KAAK,aAAa,IAAI,MAAM;AAC5C,UAAI,CAAC,QAAS;AACd,iBAAW,SAAS,SAAS;AAC3B,YAAI,KAAK,qBAAqB,OAAO,kBAAkB,gBAAgB,GAAG;AACxE,qBAAW,OAAO,KAAK,cAAc,KAAK,EAAG,WAAU,IAAI,GAAG;AAAA,QAChE;AAAA,MACF;AAAA,IACF;AAEA,eAAW,UAAU,YAAY;AAC/B,WAAK,qCAAqC,KAAK,cAAc,QAAQ,kBAAkB,gBAAgB;AAAA,IACzG;AAEA,eAAW,YAAY,WAAW;AAChC,WAAK,qCAAqC,KAAK,aAAa,UAAU,kBAAkB,gBAAgB;AAAA,IAC1G;AAAA,EACF;AAAA,EAEQ,qCACN,QACA,KACA,QACA,SACM;AACN,UAAM,UAAU,OAAO,IAAI,GAAG;AAC9B,QAAI,CAAC,QAAS;AAEd,UAAM,WAAiC,CAAC;AACxC,eAAW,SAAS,SAAS;AAC3B,UAAI,KAAK,qBAAqB,OAAO,QAAQ,OAAO,GAAG;AACrD,YAAI,MAAM,GAAI,MAAK,kBAAkB,OAAO,MAAM,EAAE;AAAA,MACtD,OAAO;AACL,iBAAS,KAAK,KAAK;AAAA,MACrB;AAAA,IACF;AACA,QAAI,SAAS,SAAS,EAAG,QAAO,IAAI,KAAK,QAAQ;AAAA,QAC5C,QAAO,OAAO,GAAG;AAAA,EACxB;AAAA,EAEQ,qBACN,OACA,QACA,SACS;AACT,QAAI,OAAO,MAAM,SAAS,aAAc,QAAO,MAAM,OAAO,OAAO;AACnE,QAAI,CAAC,KAAK,+BAA+B,OAAO,MAAM,EAAG,QAAO;AAChE,QAAI,KAAK,cAAc,MAAM,gBAAgB,MAAM,KAAK,cAAc,OAAO,gBAAgB,EAAG,QAAO;AACvG,QAAI,OAAO,mBAAoB,QAAO,MAAM,uBAAuB,OAAO;AAC1E,WAAO;AAAA,EACT;AAAA,EAEQ,0BAA0B,OAA2B,UAA2B;AACtF,WAAO,KAAK,qBAAqB,KAAK,cAAc,KAAK,GAAG,KAAK,oBAAoB,QAAQ,CAAC;AAAA,EAChG;AAAA,EAEQ,+BACN,MACA,OACS;AACT,WAAO,KAAK,qBAAqB,KAAK,cAAc,IAAI,GAAG,KAAK,cAAc,KAAK,CAAC;AAAA,EACtF;AAAA,EAEQ,cAAc,QAAwB;AAC5C,WAAO,KAAK,mBAAmB,MAAM;AAAA,EACvC;AAAA,EAEQ,aAAa,OAA8E;AACjG,WAAO,KAAK,oBAAoB,MAAM,SAAS;AAAA,EACjD;AAAA,EAEQ,cAAc,OAAmF;AACvG,UAAM,OAAO,KAAK,oBAAoB,MAAM,SAAS;AACrD,QAAI,MAAM,qBAAqB,KAAK,GAAG;AACrC,iBAAW,OAAO,KAAK,oBAAoB,MAAM,mBAAmB,EAAG,MAAK,IAAI,GAAG;AAAA,IACrF;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,oBAAoB,UAA+B;AACzD,UAAM,UAAU,SAAS,KAAK;AAC9B,UAAM,OAAO,oBAAI,IAAY;AAC7B,QAAI,QAAS,MAAK,IAAI,OAAO;AAC7B,SAAK,IAAI,KAAK,oBAAoB,QAAQ,CAAC;AAC3C,WAAO;AAAA,EACT;AAAA,EAEQ,qBAAqB,MAAmB,OAA6B;AAC3E,eAAW,OAAO,MAAM;AACtB,UAAI,MAAM,IAAI,GAAG,EAAG,QAAO;AAAA,IAC7B;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,oBAAoB,UAA0B;AACpD,UAAM,UAAU,SAAS,KAAK;AAC9B,QAAI,CAAC,QAAS,QAAO;AAErB,QAAI;AACF,YAAM,SAAS,IAAI,IAAI,OAAO;AAC9B,UAAI,OAAO,KAAM,QAAO,GAAG,OAAO,SAAS,YAAY,CAAC,KAAK,OAAO,KAAK,YAAY,CAAC;AAAA,IACxF,QAAQ;AAAA,IAER;AAEA,UAAM,YAAY,QAAQ,QAAQ,KAAK;AACvC,UAAM,iBAAiB,YAAY,IAAI,YAAY,IAAI;AACvD,QAAI,eAAe,QAAQ;AAC3B,eAAW,aAAa,CAAC,KAAK,KAAK,GAAG,GAAY;AAChD,YAAM,QAAQ,QAAQ,QAAQ,WAAW,cAAc;AACvD,UAAI,SAAS,KAAK,QAAQ,aAAc,gBAAe;AAAA,IACzD;AACA,QAAI,iBAAiB,eAAgB,QAAO,QAAQ,YAAY;AAEhE,UAAM,SAAS,YAAY,IAAI,GAAG,QAAQ,MAAM,GAAG,cAAc,EAAE,YAAY,CAAC,KAAK;AACrF,WAAO,GAAG,MAAM,GAAG,QAAQ,MAAM,gBAAgB,YAAY,EAAE,YAAY,CAAC;AAAA,EAC9E;AAAA,EAEQ,oBAAoB,SAAwC;AAClE,UAAM,MAAM,KAAK,uBAAuB,OAAO;AAC/C,QAAI,CAAC,IAAK;AACV,UAAM,eAAe,KAAK,mBAAmB,GAAG;AAChD,UAAM,WAAW,KAAK,gBAAgB,IAAI,YAAY;AACtD,UAAM,OAAO,KAAK,oBAAoB,cAAc,SAAS,QAAQ;AACrE,QAAI,CAAC,KAAM;AAEX,SAAK,iBAAiB,MAAM,QAAQ;AAAA,EACtC;AAAA,EAEQ,oBAAoB,SAAwC;AAClE,QAAI,OAAO,QAAQ,cAAc,YAAY,OAAO,QAAQ,kBAAkB,SAAU;AACxF,UAAM,WAAW,KAAK,mBAAmB,QAAQ,SAAS;AAC1D,UAAM,eAAe,KAAK,mBAAmB,QAAQ,aAAa;AAClE,QAAI,CAAC,YAAY,CAAC,gBAAgB,aAAa,aAAc;AAE7D,UAAM,aAAa,KAAK,gBAAgB,IAAI,QAAQ;AACpD,UAAM,iBAAiB,KAAK,gBAAgB,IAAI,YAAY;AAC5D,UAAM,eAAe,KAAK,oBAAoB,cAAc,OAAO;AACnE,SAAK,qBAAqB,IAAI,QAAQ,WAAW,YAAY;AAC7D,SAAK,qBAAqB,IAAI,UAAU,YAAY;AAEpD,QAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,aAAc;AAErD,UAAM,SAA+B;AAAA,MACnC,GAAI,aAAa,KAAK,cAAc,UAAU,IAAI,CAAC;AAAA,MACnD,GAAI,iBAAiB,KAAK,cAAc,cAAc,IAAI,CAAC;AAAA,MAC3D,GAAI,eAAe,KAAK,cAAc,YAAY,IAAI,CAAC;AAAA,MACvD,cAAc;AAAA,IAChB;AACA,QAAI,CAAC,OAAO,kBAAkB;AAC5B,aAAO,mBAAmB,gBAAgB,oBAAoB,YAAY;AAAA,IAC5E;AAEA,SAAK,oBAAoB,QAAQ;AACjC,SAAK,iBAAiB,QAAQ,cAAc;AAAA,EAC9C;AAAA,EAEQ,oBACN,KACA,SACA,UAC6B;AAC7B,UAAM,kBAAkB,KAAK,0BAA0B,OAAO,KAAK,UAAU;AAC7E,UAAM,OAA6B,WAAW,KAAK,cAAc,QAAQ,IAAI,EAAE,cAAc,IAAI;AACjG,QAAI,mBAAmB;AAEvB,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,UAAI,UAAU,UAAa,QAAQ,eAAe,QAAQ,gBAAiB;AAC3E,yBAAmB;AACnB,WAAK,GAAG,IAAI,KAAK,eAAe,KAAK;AAAA,IACvC;AACA,SAAK,eAAe;AACpB,QAAI,gBAAiB,MAAK,mBAAmB;AAE7C,WAAO,oBAAoB,mBAAmB,WAAW,OAAO;AAAA,EAClE;AAAA,EAEQ,iBAAiB,OAA6B,UAAuC;AAC3F,QAAI,UAAU,oBAAoB,SAAS,qBAAqB,MAAM,kBAAkB;AACtF,WAAK,4BAA4B,MAAM,cAAc,SAAS,gBAAgB;AAAA,IAChF;AACA,SAAK,gBAAgB,IAAI,MAAM,cAAc,KAAK,cAAc,KAAK,CAAC;AACtE,QAAI,MAAM,kBAAkB;AAC1B,YAAM,MAAM,KAAK,mBAAmB,MAAM,gBAAgB;AAC1D,YAAM,OAAO,KAAK,qBAAqB,IAAI,GAAG,KAAK,oBAAI,IAAY;AACnE,WAAK,IAAI,MAAM,YAAY;AAC3B,WAAK,qBAAqB,IAAI,KAAK,IAAI;AAAA,IACzC;AAAA,EACF;AAAA,EAEQ,oBAAoB,KAAmB;AAC7C,UAAM,QAAQ,KAAK,gBAAgB,IAAI,GAAG;AAC1C,QAAI,OAAO,iBAAkB,MAAK,4BAA4B,KAAK,MAAM,gBAAgB;AACzF,SAAK,gBAAgB,OAAO,GAAG;AAAA,EACjC;AAAA,EAEQ,4BAA4B,KAAa,QAAsB;AACrE,UAAM,MAAM,KAAK,mBAAmB,MAAM;AAC1C,UAAM,OAAO,KAAK,qBAAqB,IAAI,GAAG;AAC9C,QAAI,CAAC,KAAM;AACX,SAAK,OAAO,GAAG;AACf,QAAI,KAAK,SAAS,EAAG,MAAK,qBAAqB,OAAO,GAAG;AAAA,EAC3D;AAAA,EAEQ,uBAAuB,SAAiD;AAC9E,QAAI,OAAO,QAAQ,iBAAiB,YAAY,QAAQ,aAAa,KAAK,EAAG,QAAO,QAAQ;AAC5F,QAAI,OAAO,QAAQ,QAAQ,YAAY,QAAQ,IAAI,KAAK,EAAG,QAAO,QAAQ;AAC1E,QAAI,OAAO,QAAQ,OAAO,YAAY,QAAQ,GAAG,KAAK,EAAG,QAAO,QAAQ;AACxE,WAAO;AAAA,EACT;AAAA,EAEQ,0BAA0B,SAAsD;AACtF,QAAI,OAAO,QAAQ,qBAAqB,YAAY,QAAQ,iBAAiB,KAAK;AAChF,aAAO,QAAQ;AACjB,UAAM,WAAW,KAAK,SAAS,QAAQ,QAAQ;AAC/C,WAAO,OAAO,SAAS,qBAAqB,YAAY,SAAS,iBAAiB,KAAK,IACnF,SAAS,mBACT;AAAA,EACN;AAAA,EAEQ,YAAY,OAAoC;AACtD,WAAO,OAAO,UAAU,YAAY,MAAM,KAAK,IAAI,QAAQ;AAAA,EAC7D;AAAA,EAEQ,mBAAmB,KAAqB;AAC9C,QAAI,UAAU;AACd,UAAM,OAAO,oBAAI,IAAY;AAC7B,WAAO,KAAK,qBAAqB,IAAI,OAAO,KAAK,CAAC,KAAK,IAAI,OAAO,GAAG;AACnE,WAAK,IAAI,OAAO;AAChB,gBAAU,KAAK,qBAAqB,IAAI,OAAO;AAAA,IACjD;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,cAAc,UAAsD;AAC1E,WAAO,KAAK,eAAe,QAAQ;AAAA,EACrC;AAAA,EAEQ,yBAAyB,UAAkB,SAAwC;AACzF,UAAM,gBAAgB,KAAK,0BAA0B,QAAQ,KAAK;AAClE,UAAM,cACJ,cAAc,SAAS,IACnB,cAAc,IAAI,WAAS,KAAK,gBAAgB,KAAK,CAAC,EAAE,OAAO,CAAC,WAA6B,UAAU,IAAI,IAC1G,KAAK,mBAAmB,QAAQ,KAAK,KACtC,KAAK,mBAAmB,QAAQ,gBAAgB,KAChD,KAAK,mBAAmB,QAAQ,OAAO;AAE7C,QAAI,CAAC,eAAe,YAAY,WAAW,EAAG;AAE9C,UAAM,OAAO,oBAAI,IAAY;AAC7B,SAAK,IAAI,KAAK,mBAAmB,QAAQ,CAAC;AAC1C,eAAW,SAAS,CAAC,UAAU,oBAAoB,cAAc,GAAY;AAC3E,YAAM,QAAQ,QAAQ,KAAK;AAC3B,UAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAG,MAAK,IAAI,KAAK,mBAAmB,KAAK,CAAC;AAAA,IACxF;AACA,SAAK,IAAI,KAAK,mBAAmB,YAAY,CAAC,CAAE,CAAC;AAEjD,UAAM,YAAY,KAAK,mBAAmB,QAAQ;AAClD,UAAM,kBAAkB,oBAAI,IAAY,CAAC,SAAS,CAAC;AACnD,eAAW,OAAO,MAAM;AACtB,YAAM,iBAAiB,KAAK,0BAA0B,IAAI,GAAG;AAC7D,UAAI,eAAgB,iBAAgB,IAAI,cAAc;AAAA,IACxD;AACA,eAAW,OAAO,gBAAiB,MAAK,0BAA0B,GAAG;AAErE,eAAW,OAAO,MAAM;AACtB,WAAK,uBAAuB,IAAI,KAAK,CAAC,GAAG,WAAW,CAAC;AACrD,UAAI,cAAc,SAAS;AACzB,aAAK,uBAAuB;AAAA,UAC1B;AAAA,UACA,cAAc,IAAI,YAAU,EAAE,GAAG,MAAM,EAAE;AAAA,QAC3C;AAAA,UACG,MAAK,uBAAuB,OAAO,GAAG;AAC3C,WAAK,0BAA0B,IAAI,KAAK,SAAS;AAAA,IACnD;AACA,SAAK,2BAA2B,IAAI,WAAW,IAAI;AAAA,EACrD;AAAA,EAEQ,qBAAqB,UAAkB,SAAwC;AACrF,UAAM,OAAO,oBAAI,IAAY,CAAC,KAAK,mBAAmB,QAAQ,CAAC,CAAC;AAChE,eAAW,SAAS,CAAC,UAAU,oBAAoB,cAAc,GAAY;AAC3E,YAAM,QAAQ,QAAQ,KAAK;AAC3B,UAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAG,MAAK,IAAI,KAAK,mBAAmB,KAAK,CAAC;AAAA,IACxF;AACA,UAAM,kBAAkB,oBAAI,IAAY;AACxC,eAAW,OAAO,MAAM;AACtB,YAAM,iBAAiB,KAAK,0BAA0B,IAAI,GAAG;AAC7D,UAAI,eAAgB,iBAAgB,IAAI,cAAc;AAAA,IACxD;AACA,QAAI,gBAAgB,OAAO,GAAG;AAC5B,iBAAW,OAAO,gBAAiB,MAAK,0BAA0B,GAAG;AACrE;AAAA,IACF;AACA,eAAW,OAAO,MAAM;AACtB,WAAK,uBAAuB,OAAO,GAAG;AACtC,WAAK,uBAAuB,OAAO,GAAG;AACtC,WAAK,0BAA0B,OAAO,GAAG;AAAA,IAC3C;AAAA,EACF;AAAA,EAEQ,0BAA0B,WAAyB;AACzD,UAAM,OAAO,KAAK,2BAA2B,IAAI,SAAS;AAC1D,QAAI,CAAC,KAAM;AACX,eAAW,OAAO,MAAM;AACtB,WAAK,uBAAuB,OAAO,GAAG;AACtC,WAAK,uBAAuB,OAAO,GAAG;AACtC,WAAK,0BAA0B,OAAO,GAAG;AAAA,IAC3C;AACA,SAAK,2BAA2B,OAAO,SAAS;AAAA,EAClD;AAAA,EAEQ,SAAS,OAAyC;AACxD,WAAO,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAAK,QAAoC,CAAC;AAAA,EAC7G;AAAA,EAEQ,wBAAgE;AACtE,WAAO;AAAA,MACL,UAAU,CAAC;AAAA,MACX,gBAAgB,CAAC;AAAA,MACjB,SAAS,CAAC;AAAA,MACV,YAAY,CAAC;AAAA,MACb,mBAAmB;AAAA,MACnB,MAAM,CAAC;AAAA,MACP,gBAAgB,CAAC;AAAA,MACjB,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EAEQ,cAAc,OAAmC;AACvD,WAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,MAAM,UAAQ,OAAO,SAAS,QAAQ;AAAA,EAC7E;AAAA,EAEQ,wBAAwB,OAAiE;AAC/F,UAAM,UAAU,KAAK,SAAS,KAAK;AACnC,WACE,KAAK,cAAc,QAAQ,QAAQ,KACnC,KAAK,cAAc,QAAQ,cAAc,KACzC,KAAK,cAAc,QAAQ,OAAO,KAClC,KAAK,cAAc,QAAQ,UAAU,MACpC,OAAO,QAAQ,sBAAsB,YAAY,QAAQ,sBAAsB,SAChF,KAAK,cAAc,QAAQ,IAAI,KAC/B,KAAK,cAAc,QAAQ,cAAc,MACxC,QAAQ,eAAe,UAAa,KAAK,cAAc,QAAQ,UAAU,MAC1E,OAAO,QAAQ,mBAAmB,YAClC,OAAO,QAAQ,oBAAoB,YACnC,OAAO,QAAQ,YAAY;AAAA,EAE/B;AAAA,EAEQ,uBAAuB,OAA6C;AAC1E,UAAM,UAAU,KAAK,SAAS,KAAK;AACnC,YACG,QAAQ,OAAO,UAAa,KAAK,YAAY,QAAQ,EAAE,MAAM,WAC9D,KAAK,YAAY,QAAQ,SAAS,MAAM,UACxC,KAAK,YAAY,QAAQ,gBAAgB,MAAM,WAC9C,QAAQ,uBAAuB,UAAa,KAAK,YAAY,QAAQ,kBAAkB,MAAM,YAC7F,QAAQ,iBAAiB,UAAa,KAAK,cAAc,QAAQ,YAAY,OAC7E,QAAQ,kBAAkB,UAAa,KAAK,cAAc,QAAQ,aAAa,OAC/E,QAAQ,kBAAkB,UAAa,KAAK,cAAc,QAAQ,aAAa,OAC/E,QAAQ,mBAAmB,UAAa,KAAK,cAAc,QAAQ,cAAc,OACjF,QAAQ,cAAc,UAAa,KAAK,cAAc,QAAQ,SAAS,OACvE,QAAQ,mBAAmB,UAAa,OAAO,QAAQ,mBAAmB,cAC1E,QAAQ,oBAAoB,UAAa,OAAO,QAAQ,oBAAoB,cAC5E,QAAQ,iBAAiB,UAAa,OAAO,QAAQ,iBAAiB;AAAA,EAE3E;AAAA,EAEQ,kBAAkB,OAA0C;AAClE,UAAM,UAAU,KAAK,SAAS,KAAK;AACnC,UAAM,WAAW,oBAAI,IAAI,CAAC,WAAW,YAAY,WAAW,WAAW,WAAW,CAAC;AACnF,UAAM,kBAAkB,oBAAI,IAAI,CAAC,eAAe,WAAW,cAAc,YAAY,CAAC;AACtF,QAAI,QAAQ,WAAW,aAAa;AAClC,cACG,QAAQ,oBAAoB,UAC1B,OAAO,QAAQ,oBAAoB,YAAY,gBAAgB,IAAI,QAAQ,eAAe,OAC5F,QAAQ,WAAW,UACjB,QAAQ,UAAU,QAAQ,OAAO,QAAQ,WAAW,YAAY,CAAC,MAAM,QAAQ,QAAQ,MAAM,OAC/F,QAAQ,gBAAgB,UAAa,OAAO,QAAQ,gBAAgB,cACpE,QAAQ,oBAAoB,UAC3B,OAAO,QAAQ,oBAAoB,YACnC,QAAQ,oBAAoB,UAC7B,QAAQ,aAAa,UAAa,OAAO,QAAQ,aAAa,YAAY,QAAQ,aAAa;AAAA,IAEpG;AACA,WACE,OAAO,QAAQ,WAAW,YAC1B,SAAS,IAAI,QAAQ,MAAM,KAC3B,OAAO,QAAQ,oBAAoB,YACnC,gBAAgB,IAAI,QAAQ,eAAe,KAC3C,QAAQ,UAAU,QAClB,OAAO,QAAQ,WAAW,YAC1B,CAAC,MAAM,QAAQ,QAAQ,MAAM,KAC7B,OAAO,QAAQ,gBAAgB,aAC9B,OAAO,QAAQ,oBAAoB,YAAY,QAAQ,oBAAoB,UAC3E,OAAO,QAAQ,aAAa,YAAY,QAAQ,aAAa;AAAA,EAElE;AAAA,EAEQ,gBAAgB,OAAwC;AAC9D,UAAM,QAAQ,KAAK,SAAS,KAAK;AACjC,WAAO,OAAO,MAAM,iBAAiB,YAAY,OAAO,MAAM,qBAAqB;AAAA,EACrF;AAAA,EAEQ,eAAkB,OAAa;AACrC,QAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,IAAI,UAAQ,KAAK,eAAe,IAAI,CAAC;AAC5E,QAAI,SAAS,OAAO,UAAU,UAAU;AACtC,YAAM,OAAgC,CAAC;AACvC,iBAAW,CAAC,KAAK,MAAM,KAAK,OAAO,QAAQ,KAAK,GAAG;AACjD,aAAK,GAAG,IAAI,KAAK,eAAe,MAAM;AAAA,MACxC;AACA,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,0BAA0B,OAAiC;AACjE,QAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO,CAAC;AACnC,WAAO,MAAM,OAAO,UAAQ,KAAK,gBAAgB,IAAI,CAAC;AAAA,EACxD;AAAA,EAEQ,mBAAmB,OAAiC;AAC1D,QAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO;AAClC,UAAM,UAAoB,CAAC;AAC3B,eAAW,QAAQ,OAAO;AACxB,UAAI,OAAO,SAAS,YAAY,KAAK,KAAK,GAAG;AAC3C,gBAAQ,KAAK,IAAI;AACjB;AAAA,MACF;AACA,UAAI,QAAQ,OAAO,SAAS,YAAY,CAAC,MAAM,QAAQ,IAAI,GAAG;AAC5D,cAAM,SAAS,KAAK,gBAAgB,IAA8B;AAClE,YAAI,OAAQ,SAAQ,KAAK,MAAM;AAAA,MACjC;AAAA,IACF;AACA,WAAO,QAAQ,SAAS,IAAI,UAAU;AAAA,EACxC;AAAA,EAEQ,gBAAgB,OAA8C;AACpE,WAAO,MAAM,oBAAoB,MAAM,gBAAgB;AAAA,EACzD;AAAA,EAEQ,mBAAmB,QAAwB;AACjD,WAAO,OAAO,KAAK,EAAE,YAAY;AAAA,EACnC;AAAA,EAEQ,SAAS,MAA+B;AAC9C,UAAM,OAAO,KAAK;AAClB,QAAI,SAAS,KAAM;AACnB,SAAK,SAAS;AACd,SAAK,KAAK,eAAe,EAAE,MAAM,IAAI,KAAK,CAAC;AAAA,EAC7C;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/registry/sync.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\nimport type { RegistryClient } from './index';\nimport type {\n AgentSearchResult,\n AgentCompliance,\n AuthorizationEntry,\n AgentSearchResponse,\n FeedFreshness,\n} from './types.generated';\nimport type { CatalogEvent, FeedResponse, PropertyEventPayload, PropertyIdentifier, ResolvedBrand } from './types';\nimport type { FeedStreamQuery } from './feed-stream';\nimport {\n FeedStreamCursorExpiredError,\n FeedStreamUnsupportedError,\n FeedStreamHttpError,\n sanitizeStreamText,\n} from './feed-stream';\nimport type { CursorStore } from './cursor-store';\nimport { InMemoryCursorStore } from './cursor-store';\n\n// ====== Configuration ======\n\nexport interface RegistrySyncConfig {\n /** RegistryClient instance to use for API calls. */\n client: RegistryClient;\n /**\n * Feed transport for tailing changes after bootstrap:\n * - `'auto'` (default): try the SSE stream, fall back to polling on an\n * unsupported endpoint (404/406), proxy/network failure, or stream parse\n * failure.\n * - `'stream'`: SSE only; reconnect on failure and never fall back to polling.\n * - `'poll'`: polling only.\n */\n transport?: 'auto' | 'stream' | 'poll';\n /** Polling interval in milliseconds. Default: 30000 (30s). */\n pollIntervalMs?: number;\n /**\n * Comma-separated event type filter (glob, e.g. `authorization.*`) applied to\n * every feed read (bootstrap drain, polling, and SSE).\n *\n * A persisted cursor is scoped to the logical `types` subscription it was\n * minted under: a cursor from a broader subscription can skip events that were\n * filtered out previously. This is NOT enforced by the cursor store (the store\n * holds only the cursor string), so if you change `types` you must\n * `reset()` before `start()`. With a persistent {@link FileCursorStore} that\n * survives restarts, changing `types` between deploys against the same store\n * path will silently resume from a foreign cursor — point each subscription at\n * its own store path, or clear the store when the filter changes.\n */\n types?: string;\n /** Max events requested per feed page (polling and SSE). Default: 1000. */\n feedPageLimit?: number;\n /**\n * Server-side caught-up interval hint for the SSE stream, in **seconds** (5–60,\n * default 15) — maps to the `poll_interval_seconds` query param. Note the unit:\n * `pollIntervalMs` and `streamIdleTimeoutMs` are milliseconds.\n */\n streamPollIntervalSeconds?: number;\n /**\n * Client-side idle watchdog: reconnect the stream if no feed page or heartbeat\n * arrives within this window. Must exceed the server poll interval.\n * Default: 90000 (90s).\n */\n streamIdleTimeoutMs?: number;\n /** Reconnect backoff floor in ms. Default: 1000. */\n streamReconnectMinMs?: number;\n /** Reconnect backoff ceiling in ms. Default: 30000. */\n streamReconnectMaxMs?: number;\n /**\n * Consecutive stream failures (with no successful message in between) before\n * `'auto'` mode falls back to polling. Default: 3. Ignored in `'stream'` mode.\n */\n maxStreamFailures?: number;\n /** Choose which indexes to maintain. */\n indexes?: {\n /** Agent inventory profiles. Default: true. */\n agents?: boolean;\n /** Authorization entries (agent→domain mappings). Default: true. */\n authorizations?: boolean;\n /** Property feed records (property_rid→property and publisher domain→properties). Default: true. */\n properties?: boolean;\n /** Ordered brand hierarchy chains (self → parents → house). Default: true. */\n brandHierarchies?: boolean;\n };\n /** Optional cursor store for persisting the feed cursor between restarts. */\n cursorStore?: CursorStore;\n /** Called when RegistrySync intentionally ignores an event family it does not index. */\n onIgnoredEvent?: (event: CatalogEvent, reason: string) => void;\n /** Called on errors during polling/bootstrap. */\n onError?: (error: Error) => void;\n}\n\nexport type RegistrySyncState = 'idle' | 'bootstrapping' | 'syncing' | 'error';\n\n/** Active feed transport once syncing. */\nexport type RegistrySyncTransport = 'stream' | 'poll';\n\n/**\n * Outcome of one SSE connection, driving the reconnect loop.\n * - `closed`: the server ended the stream cleanly (EOF), no error.\n * - `reconnect`: a transport/parse failure or a server `feed_stream_error` —\n * counts toward `auto` polling fallback.\n */\ntype StreamDisposition = 'closed' | 'reconnect' | 'rebootstrap' | 'fallback' | 'fatal' | 'stopped';\ntype RegistrySyncComplianceStatus = AgentCompliance['status'] | 'opted_out';\ntype AuthorizationRemoveOptions = {\n matchByRowId?: boolean;\n fallbackToTupleOnMissingRowId?: boolean;\n};\n\n// ====== Event types ======\n\nexport interface RegistrySyncEvents {\n bootstrap: [{ agentCount: number; authorizationCount: number }];\n sync: [{ cursor: string; eventsApplied: number }];\n /** Emitted for each event applied during polling. Not emitted during bootstrap. */\n event: [{ event: CatalogEvent }];\n error: [{ error: Error }];\n /** Emitted when an agent's compliance status changes. */\n compliance_changed: [\n { agentUrl: string; previousStatus: RegistrySyncComplianceStatus; currentStatus: RegistrySyncComplianceStatus },\n ];\n stateChange: [{ from: RegistrySyncState; to: RegistrySyncState }];\n /** Emitted whenever a feed page or heartbeat carries freshness metadata, for lag monitoring. */\n freshness: [{ freshness: FeedFreshness }];\n /** Emitted when the active feed transport changes (e.g. stream → polling fallback). */\n transport: [{ transport: RegistrySyncTransport }];\n /** Emitted when an event is intentionally not indexed by RegistrySync. */\n ignoredEvent: [{ event: CatalogEvent; reason: string }];\n}\n\n// ====== Agent filter for client-side search ======\n\nexport interface AgentFilter {\n type?: string;\n channels?: string[];\n markets?: string[];\n categories?: string[];\n property_types?: string[];\n tags?: string[];\n delivery_types?: string[];\n has_tmp?: boolean;\n min_properties?: number;\n compliance_status?: RegistrySyncComplianceStatus[];\n}\n\nexport type RegistrySyncProperty = {\n /** Stable registry property RID. */\n property_rid: string;\n /** Publisher domain that owns the property, when supplied by the feed. */\n publisher_domain?: string;\n identifiers?: PropertyIdentifier[];\n classification?: string;\n source?: PropertyEventPayload['source'];\n property?: PropertyEventPayload['property'];\n changed_fields?: string[];\n last_resolved_at?: string;\n reactivated_at?: string;\n reason?: string;\n evidence?: string;\n [key: string]: unknown;\n};\n\n// ====== RegistrySync ======\n\n/**\n * In-memory replica of the AdCP registry.\n *\n * Bootstraps from the agent search endpoint, then tails the change feed —\n * Server-Sent Events by default (`transport: 'auto'`), falling back to polling\n * `/api/registry/feed` when streaming is unavailable — to keep its indexes\n * current for zero-latency lookups.\n *\n * **Staleness:** lookups (`getAgent`, `isAuthorized`, the authorization getters)\n * return the last synced state. After a transient failure the engine keeps\n * reconnecting/polling, but a fatal error (e.g. `401`) leaves `state === 'error'`\n * while the indexes still hold their last values. For decisions where staleness\n * is unsafe (e.g. authorization enforcement), gate on `state` and\n * `getLagSeconds()` / `getFreshness()` rather than trusting a lookup blindly.\n * Index size is bounded only by registry trust — a replica mirrors the whole\n * registry, so an untrusted/compromised feed could grow memory without limit.\n *\n * @example\n * ```ts\n * const client = new RegistryClient({ apiKey: 'sk_...' });\n * const sync = new RegistrySync({ client }); // transport: 'auto' (SSE, polling fallback)\n *\n * // Zero-latency lookups\n * sync.on('event', ({ event }) => console.log('registry change:', event.event_type));\n *\n * // Lag monitoring for the SSE feed\n * sync.on('transport', ({ transport }) => console.log('feed transport:', transport));\n * sync.on('freshness', ({ freshness }) => {\n * if ((freshness.lag_seconds ?? 0) > 300) console.warn('registry feed lag > 5m');\n * });\n *\n * await sync.start();\n * const agent = sync.getAgent('https://ads.example.com');\n * const authorized = sync.isAuthorized('https://ads.example.com', 'publisher.com');\n * const ctv = sync.findAgents({ channels: ['ctv'], markets: ['US'] });\n * sync.stop();\n * ```\n */\nexport class RegistrySync extends EventEmitter<RegistrySyncEvents> {\n private readonly client: RegistryClient;\n private readonly transportMode: 'auto' | 'stream' | 'poll';\n private readonly pollIntervalMs: number;\n private readonly types: string | undefined;\n private readonly feedPageLimit: number;\n private readonly streamPollIntervalSeconds: number;\n private readonly streamIdleTimeoutMs: number;\n private readonly streamReconnectMinMs: number;\n private readonly streamReconnectMaxMs: number;\n private readonly maxStreamFailures: number;\n private readonly indexAgents: boolean;\n private readonly indexAuthorizations: boolean;\n private readonly indexProperties: boolean;\n private readonly indexBrandHierarchies: boolean;\n private readonly ignoredEventHandler: ((event: CatalogEvent, reason: string) => void) | undefined;\n private readonly errorHandler: ((error: Error) => void) | undefined;\n private readonly cursorStore: CursorStore;\n\n private _state: RegistrySyncState = 'idle';\n private cursor: string | null = null;\n private pollTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Streaming state\n private activeTransport: RegistrySyncTransport | null = null;\n private streamController: AbortController | null = null;\n private streamIdleTimer: ReturnType<typeof setTimeout> | null = null;\n private lastFreshness: FeedFreshness | null = null;\n /**\n * Incremented by stop()/reset() and each start(). Async work (bootstrap,\n * rebootstrap, the stream loop) captures its generation and bails the moment\n * it no longer matches — so a stop() that lands mid-bootstrap is honored and\n * a stale loop can never resume after the caller stopped or restarted.\n */\n private generation = 0;\n\n // Indexes\n private agents = new Map<string, AgentSearchResult>();\n private authByDomain = new Map<string, AuthorizationEntry[]>();\n private authByAgent = new Map<string, AuthorizationEntry[]>();\n private authLocationsById = new Map<string, { agentKey: string; domainKey: string }>();\n private propertiesByRid = new Map<string, RegistrySyncProperty>();\n private propertyRidsByDomain = new Map<string, Set<string>>();\n private propertyAliasesByRid = new Map<string, string>();\n private brandAncestorsByDomain = new Map<string, string[]>();\n private brandHierarchyByDomain = new Map<string, ResolvedBrand[]>();\n private brandHierarchyKeysByEntity = new Map<string, Set<string>>();\n private brandHierarchyEntityByKey = new Map<string, string>();\n\n constructor(config: RegistrySyncConfig) {\n super();\n this.client = config.client;\n this.transportMode = config.transport ?? 'auto';\n this.pollIntervalMs = config.pollIntervalMs ?? 30_000;\n this.types = config.types;\n this.feedPageLimit = config.feedPageLimit ?? 1000;\n // Fail closed on out-of-range values rather than letting the registry reject\n // them with a 400 that the reconnect/poll loop would hot-retry forever.\n if (!Number.isInteger(this.feedPageLimit) || this.feedPageLimit < 1 || this.feedPageLimit > 10_000) {\n throw new Error('feedPageLimit must be an integer between 1 and 10000');\n }\n this.streamPollIntervalSeconds = config.streamPollIntervalSeconds ?? 15;\n if (\n !Number.isInteger(this.streamPollIntervalSeconds) ||\n this.streamPollIntervalSeconds < 5 ||\n this.streamPollIntervalSeconds > 60\n ) {\n throw new Error('streamPollIntervalSeconds must be an integer between 5 and 60');\n }\n this.streamIdleTimeoutMs = config.streamIdleTimeoutMs ?? 90_000;\n this.streamReconnectMinMs = config.streamReconnectMinMs ?? 1_000;\n this.streamReconnectMaxMs = config.streamReconnectMaxMs ?? 30_000;\n this.maxStreamFailures = config.maxStreamFailures ?? 3;\n this.indexAgents = config.indexes?.agents !== false;\n this.indexAuthorizations = config.indexes?.authorizations !== false;\n this.indexProperties = config.indexes?.properties !== false;\n this.indexBrandHierarchies = config.indexes?.brandHierarchies !== false;\n this.cursorStore = config.cursorStore ?? new InMemoryCursorStore();\n this.ignoredEventHandler = config.onIgnoredEvent;\n this.errorHandler = config.onError;\n }\n\n // ====== Lifecycle ======\n\n /** Bootstrap from the registry and begin tailing the event feed. */\n async start(): Promise<void> {\n if (this._state === 'syncing' || this._state === 'bootstrapping') return;\n const gen = ++this.generation;\n await this.bootstrap(gen);\n // A stop()/reset() (or another start()) during bootstrap bumps the\n // generation; beginSync re-validates both generation and state before\n // starting the sync loop.\n this.beginSync(gen);\n }\n\n /** Stop tailing the feed. In-memory state is preserved. */\n stop(): void {\n // Invalidate any in-flight bootstrap/rebootstrap/stream loop, even while\n // state is still 'bootstrapping'.\n this.generation++;\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n this.abortStream();\n this.activeTransport = null;\n if (this._state === 'syncing' || this._state === 'bootstrapping') {\n this.setState('idle');\n }\n }\n\n /**\n * Stop, clear all in-memory state, and drop the persisted cursor. Call start()\n * again to re-bootstrap from scratch. Because this clears the stored cursor,\n * it is the correct way to switch the `types` subscription: `reset()` then\n * `start()` begins a fresh subscription rather than resuming a cursor minted\n * under the previous filter.\n */\n async reset(): Promise<void> {\n this.stop();\n this.clearIndexes();\n this.cursor = null;\n this.lastFreshness = null;\n await this.cursorStore.clearCursor();\n this.setState('idle');\n }\n\n /** Begin tailing the feed using the configured transport. Bootstrap leaves state at 'syncing'. */\n private beginSync(gen: number): void {\n if (gen !== this.generation || this._state !== 'syncing') return;\n if (this.transportMode === 'poll') {\n this.setTransport('poll');\n this.schedulePoll(gen);\n return;\n }\n this.setTransport('stream');\n void this.runStream(gen);\n }\n\n // ====== Agent Lookups ======\n\n /** Get an agent by URL. */\n getAgent(url: string): AgentSearchResult | undefined {\n return this.agents.get(url);\n }\n\n /** Get all agents in the index. */\n getAgents(): AgentSearchResult[] {\n return [...this.agents.values()];\n }\n\n /** Find agents matching a filter. All specified dimensions use AND; values within arrays use OR. */\n findAgents(filter: AgentFilter): AgentSearchResult[] {\n return this.getAgents().filter(agent => {\n const p = agent.inventory_profile;\n if (filter.type && agent.type !== filter.type) return false;\n if (filter.channels?.length && !filter.channels.some(c => p.channels.includes(c))) return false;\n if (filter.markets?.length && !filter.markets.some(m => p.markets.includes(m))) return false;\n if (filter.categories?.length && !filter.categories.some(c => p.categories.includes(c))) return false;\n if (filter.property_types?.length && !filter.property_types.some(t => p.property_types.includes(t))) return false;\n if (filter.tags?.length && !filter.tags.some(t => p.tags.includes(t))) return false;\n if (filter.delivery_types?.length && !filter.delivery_types.some(d => p.delivery_types.includes(d))) return false;\n if (filter.has_tmp != null && p.has_tmp !== filter.has_tmp) return false;\n if (filter.min_properties != null && p.property_count < filter.min_properties) return false;\n if (filter.compliance_status?.length) {\n const status = (agent.compliance_summary?.status ?? 'unknown') as RegistrySyncComplianceStatus;\n if (!filter.compliance_status.includes(status)) return false;\n }\n return true;\n });\n }\n\n // ====== Authorization Lookups ======\n\n /** Get all authorizations for a publisher domain. */\n getAuthorizationsForDomain(domain: string): AuthorizationEntry[] {\n return this.authByDomain.get(this.authDomainKey(domain)) ?? [];\n }\n\n /** Get all authorizations for an agent. */\n getAuthorizationsForAgent(agentUrl: string): AuthorizationEntry[] {\n const entries: AuthorizationEntry[] = [];\n for (const key of this.authAgentKeysForUrl(agentUrl)) {\n for (const entry of this.authByAgent.get(key) ?? []) {\n if (!entries.includes(entry)) entries.push(entry);\n }\n }\n return entries;\n }\n\n /**\n * Check if an agent has any authorization for a publisher domain.\n * Does not evaluate property_id scoping, time bounds, or effective dates.\n * For scoped checks, use getAuthorizationsForDomain() and inspect entries directly.\n *\n * Returns the last synced state — see the class-level staleness note. For\n * enforcement, confirm `state === 'syncing'` and an acceptable `getLagSeconds()`\n * before trusting an allow decision, so a stalled feed can't serve a stale\n * authorization after a missed revocation.\n */\n isAuthorized(agentUrl: string, domain: string): boolean {\n const entries = this.authByDomain.get(this.authDomainKey(domain));\n return entries != null && entries.some(entry => this.authorizationAgentMatches(entry, agentUrl));\n }\n\n // ====== Property Lookups ======\n\n /** Get a property feed record by property_rid. Merged aliases resolve to their canonical RID. */\n getProperty(rid: string): RegistrySyncProperty | undefined {\n const property = this.propertiesByRid.get(this.resolvePropertyRid(rid));\n return property ? this.cloneProperty(property) : undefined;\n }\n\n /** Get all property feed records for a publisher domain. */\n getPropertiesForDomain(domain: string): RegistrySyncProperty[] {\n const rids = this.propertyRidsByDomain.get(this.normalizeDomainKey(domain));\n if (!rids) return [];\n const properties: RegistrySyncProperty[] = [];\n for (const rid of rids) {\n const property = this.propertiesByRid.get(this.resolvePropertyRid(rid));\n if (property) properties.push(this.cloneProperty(property));\n }\n return properties;\n }\n\n // ====== Brand Hierarchy Lookups ======\n\n /**\n * Get the ordered corporate ancestor domain chain for a brand domain.\n *\n * The returned array includes the resolved brand itself as the first entry and\n * the house domain as the last entry when known. This compatibility view is\n * populated only when older/self-hosted feeds emit brand hierarchy events.\n * Treat it as topology for display/indexing, never as relationship\n * authorization evidence.\n */\n getAncestors(domain: string): string[] {\n return [...(this.brandAncestorsByDomain.get(this.normalizeDomainKey(domain)) ?? [])];\n }\n\n /**\n * Get the ordered resolved brand chain when an older/self-hosted feed supplied\n * it. This topology-only compatibility view is not authorization evidence.\n */\n getBrandHierarchy(domain: string): ResolvedBrand[] {\n return (this.brandHierarchyByDomain.get(this.normalizeDomainKey(domain)) ?? []).map(brand => ({ ...brand }));\n }\n\n // ====== State ======\n\n get state(): RegistrySyncState {\n return this._state;\n }\n\n getCursor(): string | null {\n return this.cursor;\n }\n\n getStats(): { agents: number; authorizations: number; properties: number; brandHierarchies: number } {\n let authCount = 0;\n for (const entries of this.authByDomain.values()) authCount += entries.length;\n return {\n agents: this.agents.size,\n authorizations: authCount,\n properties: this.propertiesByRid.size,\n brandHierarchies: this.brandHierarchyKeysByEntity.size,\n };\n }\n\n /** The active feed transport once syncing, or null when idle. */\n getTransport(): RegistrySyncTransport | null {\n return this.activeTransport;\n }\n\n /**\n * Latest feed freshness metadata, or null if the registry has not reported it\n * yet (e.g. before the first feed page/heartbeat lands). For push updates,\n * prefer the `freshness` event over polling this right after `start()`.\n */\n getFreshness(): FeedFreshness | null {\n return this.lastFreshness;\n }\n\n /** Latest feed lag in seconds, or null when unavailable. Convenience over getFreshness(). */\n getLagSeconds(): number | null {\n return this.lastFreshness?.lag_seconds ?? null;\n }\n\n // ====== Private: Bootstrap ======\n\n private async bootstrap(gen: number): Promise<void> {\n this.setState('bootstrapping');\n try {\n // Restore cursor from store if available\n const storedCursor = await this.cursorStore.getCursor();\n if (storedCursor) {\n this.cursor = storedCursor;\n }\n\n // Paginate searchAgents to load all agents\n if (this.indexAgents) {\n let cursor: string | undefined;\n do {\n const query: Record<string, unknown> = { limit: 200 };\n if (cursor) query.cursor = cursor;\n const res: AgentSearchResponse = await this.client.searchAgents(query as any);\n for (const agent of res.results) {\n this.agents.set(agent.url, agent);\n }\n cursor = res.has_more && res.cursor ? res.cursor : undefined;\n } while (cursor);\n }\n\n // Get initial feed cursor and apply any events\n await this.drainFeed(gen);\n\n // A stop()/reset() landed mid-bootstrap: do not flip to 'syncing' or emit.\n if (gen !== this.generation) return;\n\n this.setState('syncing');\n this.emit('bootstrap', {\n agentCount: this.agents.size,\n authorizationCount: this.getStats().authorizations,\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n // If we were stopped mid-bootstrap, swallow the abort rather than parking\n // the engine in 'error'.\n if (gen !== this.generation) return;\n this.setState('error');\n this.errorHandler?.(error);\n this.emit('error', { error });\n throw error;\n }\n }\n\n /** Clear all state, drop the persisted cursor, and bootstrap again from scratch. */\n private async rebootstrap(gen: number): Promise<void> {\n this.clearIndexes();\n this.cursor = null;\n await this.cursorStore.clearCursor();\n await this.bootstrap(gen);\n }\n\n // ====== Private: Polling ======\n\n private schedulePoll(gen: number): void {\n this.pollTimer = setTimeout(() => this.pollLoop(gen), this.pollIntervalMs);\n }\n\n private async pollLoop(gen: number): Promise<void> {\n if (gen !== this.generation) return;\n try {\n await this.poll(gen);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.emit('error', { error });\n this.errorHandler?.(error);\n }\n // Schedule next poll even after errors (will retry), unless stopped/superseded.\n if (gen === this.generation && this._state === 'syncing') {\n this.schedulePoll(gen);\n }\n }\n\n private async poll(gen: number): Promise<void> {\n let totalEventsApplied = 0;\n let hasMore = true;\n\n while (hasMore) {\n const feed: FeedResponse = await this.client.getFeed(this.feedQuery());\n\n if (feed.cursor_expired) {\n // Cursor aged out of retention: drop state and re-bootstrap, then resume.\n try {\n await this.rebootstrap(gen);\n } catch {\n // bootstrap() already emitted 'error' and parked state in 'error'.\n // Restore 'syncing' so pollLoop reschedules and retries (mirrors the\n // stream path, which also keeps trying after a failed rebootstrap).\n if (gen === this.generation && this._state === 'error') this.setState('syncing');\n }\n return;\n }\n\n for (const event of feed.events) {\n this.applyEvent(event);\n this.emit('event', { event });\n totalEventsApplied++;\n }\n\n this.observeFreshness(feed.freshness);\n\n if (feed.cursor) {\n this.cursor = feed.cursor;\n }\n\n hasMore = feed.has_more && feed.cursor != null;\n }\n\n await this.persistCursor();\n\n if (totalEventsApplied > 0) {\n this.emit('sync', { cursor: this.cursor!, eventsApplied: totalEventsApplied });\n }\n }\n\n /**\n * Drain all available feed pages. Used during bootstrap (does not emit 'event' per event).\n */\n private async drainFeed(gen: number): Promise<void> {\n let hasMore = true;\n let recoveredFromExpiry = false;\n while (hasMore) {\n const feed: FeedResponse = await this.client.getFeed(this.feedQuery());\n if (feed.cursor_expired) {\n // Stored cursor aged out of retention: drop it and retry once from the\n // start of the window. A second expiry (now cursor-less) means a\n // misbehaving server — stop draining rather than hot-looping.\n this.cursor = null;\n await this.cursorStore.clearCursor();\n if (recoveredFromExpiry) break;\n recoveredFromExpiry = true;\n continue;\n }\n for (const event of feed.events) {\n this.applyEvent(event);\n }\n this.observeFreshness(feed.freshness);\n if (feed.cursor) this.cursor = feed.cursor;\n hasMore = feed.has_more && feed.cursor != null;\n }\n // A stop()/reset() during the drain bumps the generation — don't persist a\n // cursor a later start() would read back.\n if (gen === this.generation) await this.persistCursor();\n }\n\n // ====== Private: Streaming ======\n\n private async runStream(gen: number): Promise<void> {\n // `consecutiveFailures` drives 'auto' polling fallback. It counts connections\n // that ended in a transport/parse failure — a heartbeat does NOT reset it\n // (else heartbeat → malformed-frame → reconnect could loop forever and never\n // fall back, despite the parse-failure fallback contract). It resets only on\n // real feed progress or a clean close that delivered something.\n // `consecutiveRebootstraps` bounds a tight cursor_expired loop.\n let consecutiveFailures = 0;\n let consecutiveRebootstraps = 0;\n\n while (gen === this.generation && this.transportMode !== 'poll') {\n const controller = new AbortController();\n this.streamController = controller;\n let feedApplied = false;\n let receivedAny = false;\n let disposition: StreamDisposition;\n\n try {\n disposition = await this.streamConnection(gen, controller.signal, isFeed => {\n receivedAny = true;\n if (isFeed) feedApplied = true;\n });\n } catch (err) {\n // Aborted by stop()/reset() (which bumps the generation): exit quietly.\n if (controller.signal.aborted && gen !== this.generation) {\n return;\n }\n const error = err instanceof Error ? err : new Error(String(err));\n disposition = this.classifyStreamError(error);\n if (disposition === 'reconnect' || disposition === 'fatal') {\n this.emit('error', { error });\n this.errorHandler?.(error);\n }\n } finally {\n this.clearStreamIdleTimer();\n }\n\n if (gen !== this.generation) return;\n // Applying a feed page proves the stream works end to end.\n if (feedApplied) {\n consecutiveFailures = 0;\n consecutiveRebootstraps = 0;\n }\n\n if (disposition === 'stopped') return;\n\n if (disposition === 'fatal') {\n // Permanent error (e.g. 400/401): retrying cannot help. Park in 'error'.\n this.setState('error');\n this.activeTransport = null;\n return;\n }\n\n if (disposition === 'rebootstrap') {\n let ok = false;\n try {\n await this.rebootstrap(gen);\n ok = true;\n } catch {\n // bootstrap() already emitted 'error' and parked state in 'error'.\n }\n if (gen !== this.generation) return;\n if (!ok) {\n consecutiveFailures++;\n if (this.shouldFallBack(consecutiveFailures)) {\n this.fallBackToPolling(gen);\n return;\n }\n // Recover: bootstrap left us in 'error'. In 'stream' mode the contract\n // is to keep reconnecting, so restore 'syncing' and back off.\n this.setState('syncing');\n await this.delay(this.reconnectBackoffMs(consecutiveFailures), controller.signal);\n if (gen !== this.generation) return;\n continue;\n }\n // Successful re-bootstrap. Back off proportionally to repeated expiries\n // so a server stuck emitting cursor_expired can't drive a tight loop.\n consecutiveRebootstraps++;\n await this.delay(this.reconnectBackoffMs(consecutiveRebootstraps), controller.signal);\n if (gen !== this.generation) return;\n continue;\n }\n\n if (disposition === 'fallback') {\n this.fallBackToPolling(gen);\n return;\n }\n\n if (disposition === 'closed' && receivedAny) {\n // Clean close after real activity (feed or heartbeat): the transport\n // works; reconnect promptly without counting it as a failure.\n consecutiveFailures = 0;\n await this.delay(this.reconnectBackoffMs(0), controller.signal);\n if (gen !== this.generation) return;\n continue;\n }\n\n // 'reconnect' (transport/parse failure or server feed_stream_error), or a\n // clean close that delivered nothing — count toward fallback so 'auto'\n // degrades to polling rather than looping on a stream that never delivers.\n consecutiveFailures++;\n if (this.shouldFallBack(consecutiveFailures)) {\n this.fallBackToPolling(gen);\n return;\n }\n await this.delay(this.reconnectBackoffMs(consecutiveFailures), controller.signal);\n if (gen !== this.generation) return;\n }\n }\n\n /**\n * Consume one SSE connection. Invokes `onMessage(isFeed)` for each feed page\n * (true) or heartbeat (false) received. Returns the disposition for the\n * reconnect loop: `closed` on a clean server EOF, `reconnect` on a server\n * `feed_stream_error`, `rebootstrap` on `cursor_expired`, `stopped` if the\n * engine was stopped mid-stream.\n */\n private async streamConnection(\n gen: number,\n signal: AbortSignal,\n onMessage: (isFeed: boolean) => void\n ): Promise<StreamDisposition> {\n this.armStreamIdleTimer();\n const query: FeedStreamQuery = {\n cursor: this.cursor ?? undefined,\n types: this.types,\n limit: this.feedPageLimit,\n pollIntervalSeconds: this.streamPollIntervalSeconds,\n };\n\n for await (const msg of this.client.streamFeed(query, { signal })) {\n if (gen !== this.generation) return 'stopped';\n this.armStreamIdleTimer();\n\n if (msg.type === 'feed') {\n onMessage(true);\n await this.applyStreamPage(msg.page);\n } else if (msg.type === 'heartbeat') {\n onMessage(false);\n // Heartbeats keep the connection alive and expose freshness; they do NOT\n // advance the cursor and do NOT count as feed progress for fallback.\n this.observeFreshness(msg.heartbeat.freshness);\n } else {\n // 'error' — the server closes the stream after this frame.\n if (msg.error.error === 'cursor_expired') {\n return 'rebootstrap';\n }\n // Registry-supplied strings are untrusted: escape before logging/emitting.\n const code = sanitizeStreamText(msg.error.error);\n const detail = msg.error.message ? ` (${sanitizeStreamText(msg.error.message)})` : '';\n const error = new Error(`registry feed stream error: ${code}${detail}`);\n this.emit('error', { error });\n this.errorHandler?.(error);\n return 'reconnect';\n }\n }\n\n // Stream closed cleanly by the server — reconnect from the last cursor.\n return 'closed';\n }\n\n /** Apply one SSE feed page: events, freshness, then advance + persist the cursor. */\n private async applyStreamPage(page: FeedResponse): Promise<void> {\n let applied = 0;\n for (const event of page.events) {\n this.applyEvent(event);\n this.emit('event', { event });\n applied++;\n }\n this.observeFreshness(page.freshness);\n\n // Advance the cursor only after the full page is applied, so a mid-page\n // disconnect resumes from the last fully-applied page.\n if (page.cursor) {\n this.cursor = page.cursor;\n try {\n await this.persistCursor();\n } catch (err) {\n // Persisting is best-effort; replay after restart is idempotent.\n const error = err instanceof Error ? err : new Error(String(err));\n this.emit('error', { error });\n this.errorHandler?.(error);\n }\n }\n\n if (applied > 0) {\n this.emit('sync', { cursor: this.cursor!, eventsApplied: applied });\n }\n }\n\n private classifyStreamError(error: Error): StreamDisposition {\n if (error instanceof FeedStreamCursorExpiredError) return 'rebootstrap';\n if (error instanceof FeedStreamUnsupportedError) {\n // Endpoint absent (older registry) or a proxy returned non-stream content.\n return this.transportMode === 'auto' ? 'fallback' : 'reconnect';\n }\n if (error instanceof FeedStreamHttpError && (error.status === 400 || error.status === 401)) {\n // Permanent client-side error: a malformed request or bad credentials.\n // Retrying (or polling, which hits the same status) cannot recover.\n return 'fatal';\n }\n // Other HTTP errors, parse failures, idle timeouts, network/abort errors.\n return 'reconnect';\n }\n\n private shouldFallBack(consecutiveFailures: number): boolean {\n return this.transportMode === 'auto' && consecutiveFailures >= this.maxStreamFailures;\n }\n\n private fallBackToPolling(gen: number): void {\n if (gen !== this.generation || this._state !== 'syncing') return;\n this.abortStream();\n this.setTransport('poll');\n this.schedulePoll(gen);\n }\n\n private armStreamIdleTimer(): void {\n this.clearStreamIdleTimer();\n const controller = this.streamController;\n if (!controller) return;\n this.streamIdleTimer = setTimeout(() => {\n controller.abort(new Error('registry feed stream idle timeout'));\n }, this.streamIdleTimeoutMs);\n this.streamIdleTimer.unref?.();\n }\n\n private clearStreamIdleTimer(): void {\n if (this.streamIdleTimer) {\n clearTimeout(this.streamIdleTimer);\n this.streamIdleTimer = null;\n }\n }\n\n private abortStream(): void {\n this.clearStreamIdleTimer();\n if (this.streamController) {\n this.streamController.abort();\n this.streamController = null;\n }\n }\n\n private reconnectBackoffMs(attempt: number): number {\n const exp = this.streamReconnectMinMs * 2 ** Math.max(0, attempt - 1);\n return Math.min(this.streamReconnectMaxMs, Math.max(this.streamReconnectMinMs, exp));\n }\n\n private delay(ms: number, signal: AbortSignal): Promise<void> {\n if (signal.aborted) return Promise.resolve();\n return new Promise(resolve => {\n const onAbort = () => {\n clearTimeout(timer);\n resolve();\n };\n const timer = setTimeout(() => {\n signal.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n timer.unref?.();\n signal.addEventListener('abort', onAbort, { once: true });\n });\n }\n\n // ====== Private: Shared feed helpers ======\n\n private feedQuery(): { cursor?: string; types?: string; limit: number } {\n const query: { cursor?: string; types?: string; limit: number } = { limit: this.feedPageLimit };\n if (this.cursor) query.cursor = this.cursor;\n if (this.types) query.types = this.types;\n return query;\n }\n\n private observeFreshness(freshness: FeedFreshness | undefined): void {\n if (!freshness) return;\n this.lastFreshness = freshness;\n this.emit('freshness', { freshness });\n }\n\n private async persistCursor(): Promise<void> {\n if (this.cursor) {\n await this.cursorStore.setCursor(this.cursor);\n }\n }\n\n private setTransport(transport: RegistrySyncTransport): void {\n if (this.activeTransport === transport) return;\n this.activeTransport = transport;\n this.emit('transport', { transport });\n }\n\n // ====== Private: Event Application ======\n\n private applyEvent(event: CatalogEvent): void {\n const payload = this.asRecord(event.payload);\n\n switch (event.event_type) {\n case 'agent.discovered':\n case 'agent.profile_updated': {\n if (!this.indexAgents) break;\n const existing = this.agents.get(event.entity_id);\n const inventoryProfile = this.isAgentInventoryProfile(payload.inventory_profile)\n ? payload.inventory_profile\n : undefined;\n if (existing && inventoryProfile) {\n this.agents.set(event.entity_id, {\n ...existing,\n inventory_profile: inventoryProfile,\n });\n } else if (!existing) {\n // New agent: use payload data or create stub\n this.agents.set(event.entity_id, {\n url: event.entity_id,\n name: typeof payload.name === 'string' ? payload.name : event.entity_id,\n type: typeof payload.type === 'string' ? payload.type : 'unknown',\n inventory_profile: inventoryProfile ?? this.emptyInventoryProfile(),\n match: { score: 0, matched_filters: [] },\n });\n }\n break;\n }\n\n case 'agent.removed': {\n if (this.indexAgents) this.agents.delete(event.entity_id);\n if (this.indexAuthorizations) this.removeAuthorizationsForAgent(event.entity_id);\n break;\n }\n\n case 'authorization.granted': {\n if (!this.indexAuthorizations) break;\n if (!this.isAuthorizationPayload(payload)) break;\n if (typeof payload.authorization_type !== 'string') break;\n this.upsertAuthorizationEntry(this.authorizationEntryFromEvent(payload));\n break;\n }\n\n case 'authorization.modified': {\n if (!this.indexAuthorizations) break;\n if (!this.isAuthorizationPayload(payload)) break;\n const entry = this.authorizationEntryFromEvent(payload);\n if (!entry.id && !entry.authorization_type) break;\n this.upsertAuthorizationEntry(entry, { fallbackToTupleOnMissingRowId: true });\n break;\n }\n\n case 'authorization.revoked': {\n if (!this.indexAuthorizations) break;\n if (!this.isAuthorizationPayload(payload)) break;\n const entry = this.authorizationEntryFromEvent(payload);\n this.removeAuthorizationEntries(entry, {\n matchByRowId: Boolean(entry.id || !entry.authorization_type),\n fallbackToTupleOnMissingRowId: true,\n });\n break;\n }\n\n case 'agent.compliance_changed': {\n if (this.indexAgents) {\n const existing = this.agents.get(event.entity_id);\n const summary = payload.compliance_summary;\n if (existing && this.isAgentCompliance(summary)) {\n this.agents.set(event.entity_id, {\n ...existing,\n compliance_summary: summary,\n });\n }\n }\n this.emit('compliance_changed', {\n agentUrl: event.entity_id,\n previousStatus: (typeof payload.previous_status === 'string'\n ? payload.previous_status\n : 'unknown') as RegistrySyncComplianceStatus,\n currentStatus: (typeof payload.current_status === 'string'\n ? payload.current_status\n : 'unknown') as RegistrySyncComplianceStatus,\n });\n break;\n }\n\n case 'property.created':\n case 'property.updated':\n case 'property.reactivated':\n case 'property.stale': {\n if (this.indexProperties) this.upsertPropertyEvent(payload);\n break;\n }\n\n case 'property.merged': {\n if (this.indexProperties) this.handlePropertyMerge(payload);\n break;\n }\n\n case 'brand.hierarchy_updated':\n case 'brand.updated':\n case 'brand.resolved': {\n if (this.indexBrandHierarchies) this.applyBrandHierarchyEvent(event.entity_id, payload);\n break;\n }\n\n case 'brand.removed':\n case 'brand.deleted': {\n if (this.indexBrandHierarchies) this.deleteBrandHierarchy(event.entity_id, payload);\n break;\n }\n\n case 'collection.created':\n case 'collection.updated':\n case 'collection.merged':\n case 'collection.removed': {\n this.ignoreEvent(event, 'collection.* indexing requires typed collection schemas and a reference consumer');\n break;\n }\n\n case 'publisher.adagents_changed':\n case 'publisher.adagents_discovered': {\n this.ignoreEvent(event, 'publisher.* indexing requires a reference consumer');\n break;\n }\n\n default:\n this.ignoreEvent(event, 'unknown or unsupported registry feed event type');\n break;\n }\n }\n\n // ====== Private: Helpers ======\n\n private clearIndexes(): void {\n this.agents.clear();\n this.authByDomain.clear();\n this.authByAgent.clear();\n this.authLocationsById.clear();\n this.propertiesByRid.clear();\n this.propertyRidsByDomain.clear();\n this.propertyAliasesByRid.clear();\n this.brandAncestorsByDomain.clear();\n this.brandHierarchyByDomain.clear();\n this.brandHierarchyKeysByEntity.clear();\n this.brandHierarchyEntityByKey.clear();\n }\n\n private ignoreEvent(event: CatalogEvent, reason: string): void {\n this.ignoredEventHandler?.(event, reason);\n this.emit('ignoredEvent', { event, reason });\n }\n\n private upsertAuthorizationEntry(\n entry: AuthorizationEntry,\n options?: { fallbackToTupleOnMissingRowId?: boolean }\n ): void {\n const normalizedEntry = this.normalizeAuthorizationEntry(entry);\n this.removeAuthorizationEntries(normalizedEntry, {\n matchByRowId: Boolean(normalizedEntry.id),\n fallbackToTupleOnMissingRowId: options?.fallbackToTupleOnMissingRowId,\n });\n\n const domainKey = this.authDomainKey(normalizedEntry.publisher_domain);\n const domainEntries = this.authByDomain.get(domainKey) ?? [];\n domainEntries.push(normalizedEntry);\n this.authByDomain.set(domainKey, domainEntries);\n\n const agentKey = this.authAgentKey(normalizedEntry);\n for (const key of this.authAgentKeys(normalizedEntry)) {\n const agentEntries = this.authByAgent.get(key) ?? [];\n agentEntries.push(normalizedEntry);\n this.authByAgent.set(key, agentEntries);\n }\n\n if (normalizedEntry.id) {\n this.authLocationsById.set(normalizedEntry.id, {\n agentKey,\n domainKey,\n });\n }\n }\n\n private normalizeAuthorizationEntry(entry: AuthorizationEntry): AuthorizationEntry {\n if (entry.effective_until || !entry.effective_to) return entry;\n return { ...entry, effective_until: entry.effective_to };\n }\n\n private authorizationEntryFromEvent(entry: AuthorizationEntry): AuthorizationEntry {\n return this.normalizeAuthorizationEntry(entry);\n }\n\n private removeAuthorizationsForAgent(agentUrl: string): void {\n const agentKeys = this.authAgentKeysForUrl(agentUrl);\n for (const [key, entries] of this.authByAgent) {\n if (entries.some(entry => this.authorizationAgentMatches(entry, agentUrl))) agentKeys.add(key);\n }\n\n for (const agentKey of agentKeys) {\n const entries = [...(this.authByAgent.get(agentKey) ?? [])];\n for (const entry of entries) {\n this.removeAuthorizationEntries(entry, {\n matchByRowId: Boolean(entry.id),\n fallbackToTupleOnMissingRowId: true,\n });\n }\n this.authByAgent.delete(agentKey);\n }\n }\n\n private removeAuthorizationEntries(target: AuthorizationEntry, options?: AuthorizationRemoveOptions): void {\n const normalizedTarget = this.normalizeAuthorizationEntry(target);\n const domainKeys = new Set<string>([this.authDomainKey(normalizedTarget.publisher_domain)]);\n const agentKeys = this.authAgentKeys(normalizedTarget);\n let effectiveOptions = options;\n\n if (normalizedTarget.id && options?.matchByRowId) {\n const existingLocation = this.authLocationsById.get(normalizedTarget.id);\n if (!existingLocation && options.fallbackToTupleOnMissingRowId) {\n effectiveOptions = { ...options, matchByRowId: false };\n }\n if (existingLocation) {\n domainKeys.add(existingLocation.domainKey);\n agentKeys.add(existingLocation.agentKey);\n }\n }\n\n for (const domain of domainKeys) {\n const entries = this.authByDomain.get(domain);\n if (!entries) continue;\n for (const entry of entries) {\n if (this.authorizationMatches(entry, normalizedTarget, effectiveOptions)) {\n for (const key of this.authAgentKeys(entry)) agentKeys.add(key);\n }\n }\n }\n\n for (const domain of domainKeys) {\n this.removeAuthorizationEntriesFromBucket(this.authByDomain, domain, normalizedTarget, effectiveOptions);\n }\n\n for (const agentUrl of agentKeys) {\n this.removeAuthorizationEntriesFromBucket(this.authByAgent, agentUrl, normalizedTarget, effectiveOptions);\n }\n }\n\n private removeAuthorizationEntriesFromBucket(\n bucket: Map<string, AuthorizationEntry[]>,\n key: string,\n target: AuthorizationEntry,\n options?: AuthorizationRemoveOptions\n ): void {\n const entries = bucket.get(key);\n if (!entries) return;\n\n const filtered: AuthorizationEntry[] = [];\n for (const entry of entries) {\n if (this.authorizationMatches(entry, target, options)) {\n if (entry.id) this.authLocationsById.delete(entry.id);\n } else {\n filtered.push(entry);\n }\n }\n if (filtered.length > 0) bucket.set(key, filtered);\n else bucket.delete(key);\n }\n\n private authorizationMatches(\n entry: AuthorizationEntry,\n target: AuthorizationEntry,\n options?: AuthorizationRemoveOptions\n ): boolean {\n if (target.id && options?.matchByRowId) return entry.id === target.id;\n if (!this.authorizationEntriesShareAgent(entry, target)) return false;\n if (this.authDomainKey(entry.publisher_domain) !== this.authDomainKey(target.publisher_domain)) return false;\n if (target.authorization_type) return entry.authorization_type === target.authorization_type;\n return true;\n }\n\n private authorizationAgentMatches(entry: AuthorizationEntry, agentUrl: string): boolean {\n return this.authAgentKeysOverlap(this.authAgentKeys(entry), this.authAgentKeysForUrl(agentUrl));\n }\n\n private authorizationEntriesShareAgent(\n left: Pick<AuthorizationEntry, 'agent_url' | 'agent_url_canonical'>,\n right: Pick<AuthorizationEntry, 'agent_url' | 'agent_url_canonical'>\n ): boolean {\n return this.authAgentKeysOverlap(this.authAgentKeys(left), this.authAgentKeys(right));\n }\n\n private authDomainKey(domain: string): string {\n return this.normalizeDomainKey(domain);\n }\n\n private authAgentKey(entry: Pick<AuthorizationEntry, 'agent_url' | 'agent_url_canonical'>): string {\n return this.authAgentKeyFromUrl(entry.agent_url);\n }\n\n private authAgentKeys(entry: Pick<AuthorizationEntry, 'agent_url' | 'agent_url_canonical'>): Set<string> {\n const keys = this.authAgentKeysForUrl(entry.agent_url);\n if (entry.agent_url_canonical?.trim()) {\n for (const key of this.authAgentKeysForUrl(entry.agent_url_canonical)) keys.add(key);\n }\n return keys;\n }\n\n private authAgentKeysForUrl(agentUrl: string): Set<string> {\n const trimmed = agentUrl.trim();\n const keys = new Set<string>();\n if (trimmed) keys.add(trimmed);\n keys.add(this.authAgentKeyFromUrl(agentUrl));\n return keys;\n }\n\n private authAgentKeysOverlap(left: Set<string>, right: Set<string>): boolean {\n for (const key of left) {\n if (right.has(key)) return true;\n }\n return false;\n }\n\n private authAgentKeyFromUrl(agentUrl: string): string {\n const trimmed = agentUrl.trim();\n if (!trimmed) return trimmed;\n\n try {\n const parsed = new URL(trimmed);\n if (parsed.host) return `${parsed.protocol.toLowerCase()}//${parsed.host.toLowerCase()}`;\n } catch {\n // Fall through to best-effort normalization for malformed or scheme-less values.\n }\n\n const schemeEnd = trimmed.indexOf('://');\n const authorityStart = schemeEnd > 0 ? schemeEnd + 3 : 0;\n let authorityEnd = trimmed.length;\n for (const delimiter of ['/', '?', '#'] as const) {\n const index = trimmed.indexOf(delimiter, authorityStart);\n if (index >= 0 && index < authorityEnd) authorityEnd = index;\n }\n if (authorityEnd === authorityStart) return trimmed.toLowerCase();\n\n const scheme = schemeEnd > 0 ? `${trimmed.slice(0, authorityStart).toLowerCase()}` : '';\n return `${scheme}${trimmed.slice(authorityStart, authorityEnd).toLowerCase()}`;\n }\n\n private upsertPropertyEvent(payload: Record<string, unknown>): void {\n const rid = this.propertyRidFromPayload(payload);\n if (!rid) return;\n const canonicalRid = this.resolvePropertyRid(rid);\n const existing = this.propertiesByRid.get(canonicalRid);\n const next = this.propertyFromPayload(canonicalRid, payload, existing);\n if (!next) return;\n\n this.setPropertyEntry(next, existing);\n }\n\n private handlePropertyMerge(payload: Record<string, unknown>): void {\n if (typeof payload.alias_rid !== 'string' || typeof payload.canonical_rid !== 'string') return;\n const aliasRid = this.resolvePropertyRid(payload.alias_rid);\n const canonicalRid = this.resolvePropertyRid(payload.canonical_rid);\n if (!aliasRid || !canonicalRid || aliasRid === canonicalRid) return;\n\n const aliasEntry = this.propertiesByRid.get(aliasRid);\n const canonicalEntry = this.propertiesByRid.get(canonicalRid);\n const payloadEntry = this.propertyFromPayload(canonicalRid, payload);\n this.propertyAliasesByRid.set(payload.alias_rid, canonicalRid);\n this.propertyAliasesByRid.set(aliasRid, canonicalRid);\n\n if (!aliasEntry && !canonicalEntry && !payloadEntry) return;\n\n const merged: RegistrySyncProperty = {\n ...(aliasEntry ? this.cloneProperty(aliasEntry) : {}),\n ...(canonicalEntry ? this.cloneProperty(canonicalEntry) : {}),\n ...(payloadEntry ? this.cloneProperty(payloadEntry) : {}),\n property_rid: canonicalRid,\n };\n if (!merged.publisher_domain) {\n merged.publisher_domain = canonicalEntry?.publisher_domain ?? aliasEntry?.publisher_domain;\n }\n\n this.deletePropertyEntry(aliasRid);\n this.setPropertyEntry(merged, canonicalEntry);\n }\n\n private propertyFromPayload(\n rid: string,\n payload: Record<string, unknown>,\n existing?: RegistrySyncProperty\n ): RegistrySyncProperty | null {\n const publisherDomain = this.propertyDomainFromPayload(payload) ?? existing?.publisher_domain;\n const next: RegistrySyncProperty = existing ? this.cloneProperty(existing) : { property_rid: rid };\n let hasPayloadFields = false;\n\n for (const [key, value] of Object.entries(payload)) {\n if (value === undefined || key === 'alias_rid' || key === 'canonical_rid') continue;\n hasPayloadFields = true;\n next[key] = this.cloneJsonValue(value);\n }\n next.property_rid = rid;\n if (publisherDomain) next.publisher_domain = publisherDomain;\n\n return hasPayloadFields || publisherDomain || existing ? next : null;\n }\n\n private setPropertyEntry(entry: RegistrySyncProperty, previous?: RegistrySyncProperty): void {\n if (previous?.publisher_domain && previous.publisher_domain !== entry.publisher_domain) {\n this.removePropertyRidFromDomain(entry.property_rid, previous.publisher_domain);\n }\n this.propertiesByRid.set(entry.property_rid, this.cloneProperty(entry));\n if (entry.publisher_domain) {\n const key = this.normalizeDomainKey(entry.publisher_domain);\n const rids = this.propertyRidsByDomain.get(key) ?? new Set<string>();\n rids.add(entry.property_rid);\n this.propertyRidsByDomain.set(key, rids);\n }\n }\n\n private deletePropertyEntry(rid: string): void {\n const entry = this.propertiesByRid.get(rid);\n if (entry?.publisher_domain) this.removePropertyRidFromDomain(rid, entry.publisher_domain);\n this.propertiesByRid.delete(rid);\n }\n\n private removePropertyRidFromDomain(rid: string, domain: string): void {\n const key = this.normalizeDomainKey(domain);\n const rids = this.propertyRidsByDomain.get(key);\n if (!rids) return;\n rids.delete(rid);\n if (rids.size === 0) this.propertyRidsByDomain.delete(key);\n }\n\n private propertyRidFromPayload(payload: Record<string, unknown>): string | null {\n if (typeof payload.property_rid === 'string' && payload.property_rid.trim()) return payload.property_rid;\n if (typeof payload.rid === 'string' && payload.rid.trim()) return payload.rid;\n if (typeof payload.id === 'string' && payload.id.trim()) return payload.id;\n return null;\n }\n\n private propertyDomainFromPayload(payload: Record<string, unknown>): string | undefined {\n if (typeof payload.publisher_domain === 'string' && payload.publisher_domain.trim())\n return payload.publisher_domain;\n const property = this.asRecord(payload.property);\n return typeof property.publisher_domain === 'string' && property.publisher_domain.trim()\n ? property.publisher_domain\n : undefined;\n }\n\n private stringValue(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value : undefined;\n }\n\n private resolvePropertyRid(rid: string): string {\n let current = rid;\n const seen = new Set<string>();\n while (this.propertyAliasesByRid.has(current) && !seen.has(current)) {\n seen.add(current);\n current = this.propertyAliasesByRid.get(current)!;\n }\n return current;\n }\n\n private cloneProperty(property: RegistrySyncProperty): RegistrySyncProperty {\n return this.cloneJsonValue(property) as RegistrySyncProperty;\n }\n\n private applyBrandHierarchyEvent(entityId: string, payload: Record<string, unknown>): void {\n const resolvedChain = this.extractResolvedBrandChain(payload.chain);\n const domainChain =\n resolvedChain.length > 0\n ? resolvedChain.map(brand => this.domainFromBrand(brand)).filter((domain): domain is string => domain != null)\n : (this.extractDomainChain(payload.chain) ??\n this.extractDomainChain(payload.ancestor_domains) ??\n this.extractDomainChain(payload.domains));\n\n if (!domainChain || domainChain.length === 0) return;\n\n const keys = new Set<string>();\n keys.add(this.normalizeDomainKey(entityId));\n for (const field of ['domain', 'canonical_domain', 'canonical_id'] as const) {\n const value = payload[field];\n if (typeof value === 'string' && value.trim()) keys.add(this.normalizeDomainKey(value));\n }\n keys.add(this.normalizeDomainKey(domainChain[0]!));\n\n const entityKey = this.normalizeDomainKey(entityId);\n const entitiesToClear = new Set<string>([entityKey]);\n for (const key of keys) {\n const existingEntity = this.brandHierarchyEntityByKey.get(key);\n if (existingEntity) entitiesToClear.add(existingEntity);\n }\n for (const key of entitiesToClear) this.clearBrandHierarchyEntity(key);\n\n for (const key of keys) {\n this.brandAncestorsByDomain.set(key, [...domainChain]);\n if (resolvedChain.length > 0)\n this.brandHierarchyByDomain.set(\n key,\n resolvedChain.map(brand => ({ ...brand }))\n );\n else this.brandHierarchyByDomain.delete(key);\n this.brandHierarchyEntityByKey.set(key, entityKey);\n }\n this.brandHierarchyKeysByEntity.set(entityKey, keys);\n }\n\n private deleteBrandHierarchy(entityId: string, payload: Record<string, unknown>): void {\n const keys = new Set<string>([this.normalizeDomainKey(entityId)]);\n for (const field of ['domain', 'canonical_domain', 'canonical_id'] as const) {\n const value = payload[field];\n if (typeof value === 'string' && value.trim()) keys.add(this.normalizeDomainKey(value));\n }\n const entitiesToClear = new Set<string>();\n for (const key of keys) {\n const existingEntity = this.brandHierarchyEntityByKey.get(key);\n if (existingEntity) entitiesToClear.add(existingEntity);\n }\n if (entitiesToClear.size > 0) {\n for (const key of entitiesToClear) this.clearBrandHierarchyEntity(key);\n return;\n }\n for (const key of keys) {\n this.brandAncestorsByDomain.delete(key);\n this.brandHierarchyByDomain.delete(key);\n this.brandHierarchyEntityByKey.delete(key);\n }\n }\n\n private clearBrandHierarchyEntity(entityKey: string): void {\n const keys = this.brandHierarchyKeysByEntity.get(entityKey);\n if (!keys) return;\n for (const key of keys) {\n this.brandAncestorsByDomain.delete(key);\n this.brandHierarchyByDomain.delete(key);\n this.brandHierarchyEntityByKey.delete(key);\n }\n this.brandHierarchyKeysByEntity.delete(entityKey);\n }\n\n private asRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : {};\n }\n\n private emptyInventoryProfile(): AgentSearchResult['inventory_profile'] {\n return {\n channels: [],\n property_types: [],\n markets: [],\n categories: [],\n category_taxonomy: 'iab_content_3.0',\n tags: [],\n delivery_types: [],\n property_count: 0,\n publisher_count: 0,\n has_tmp: false,\n };\n }\n\n private isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every(item => typeof item === 'string');\n }\n\n private isAgentInventoryProfile(value: unknown): value is AgentSearchResult['inventory_profile'] {\n const profile = this.asRecord(value);\n return (\n this.isStringArray(profile.channels) &&\n this.isStringArray(profile.property_types) &&\n this.isStringArray(profile.markets) &&\n this.isStringArray(profile.categories) &&\n (typeof profile.category_taxonomy === 'string' || profile.category_taxonomy === null) &&\n this.isStringArray(profile.tags) &&\n this.isStringArray(profile.delivery_types) &&\n (profile.format_ids === undefined || this.isStringArray(profile.format_ids)) &&\n typeof profile.property_count === 'number' &&\n typeof profile.publisher_count === 'number' &&\n typeof profile.has_tmp === 'boolean'\n );\n }\n\n private isAuthorizationPayload(value: unknown): value is AuthorizationEntry {\n const payload = this.asRecord(value);\n return (\n (payload.id === undefined || this.stringValue(payload.id) !== undefined) &&\n this.stringValue(payload.agent_url) !== undefined &&\n this.stringValue(payload.publisher_domain) !== undefined &&\n (payload.authorization_type === undefined || this.stringValue(payload.authorization_type) !== undefined) &&\n (payload.property_ids === undefined || this.isStringArray(payload.property_ids)) &&\n (payload.property_tags === undefined || this.isStringArray(payload.property_tags)) &&\n (payload.placement_ids === undefined || this.isStringArray(payload.placement_ids)) &&\n (payload.placement_tags === undefined || this.isStringArray(payload.placement_tags)) &&\n (payload.countries === undefined || this.isStringArray(payload.countries)) &&\n (payload.effective_from === undefined || typeof payload.effective_from === 'string') &&\n (payload.effective_until === undefined || typeof payload.effective_until === 'string') &&\n (payload.effective_to === undefined || typeof payload.effective_to === 'string')\n );\n }\n\n private isAgentCompliance(value: unknown): value is AgentCompliance {\n const summary = this.asRecord(value);\n const statuses = new Set(['passing', 'degraded', 'failing', 'unknown', 'opted_out']);\n const lifecycleStages = new Set(['development', 'testing', 'production', 'deprecated']);\n if (summary.status === 'opted_out') {\n return (\n (summary.lifecycle_stage === undefined ||\n (typeof summary.lifecycle_stage === 'string' && lifecycleStages.has(summary.lifecycle_stage))) &&\n (summary.tracks === undefined ||\n (summary.tracks != null && typeof summary.tracks === 'object' && !Array.isArray(summary.tracks))) &&\n (summary.streak_days === undefined || typeof summary.streak_days === 'number') &&\n (summary.last_checked_at === undefined ||\n typeof summary.last_checked_at === 'string' ||\n summary.last_checked_at === null) &&\n (summary.headline === undefined || typeof summary.headline === 'string' || summary.headline === null)\n );\n }\n return (\n typeof summary.status === 'string' &&\n statuses.has(summary.status) &&\n typeof summary.lifecycle_stage === 'string' &&\n lifecycleStages.has(summary.lifecycle_stage) &&\n summary.tracks != null &&\n typeof summary.tracks === 'object' &&\n !Array.isArray(summary.tracks) &&\n typeof summary.streak_days === 'number' &&\n (typeof summary.last_checked_at === 'string' || summary.last_checked_at === null) &&\n (typeof summary.headline === 'string' || summary.headline === null)\n );\n }\n\n private isResolvedBrand(value: unknown): value is ResolvedBrand {\n const brand = this.asRecord(value);\n return typeof brand.canonical_id === 'string' && typeof brand.canonical_domain === 'string';\n }\n\n private cloneJsonValue<T>(value: T): T {\n if (Array.isArray(value)) return value.map(item => this.cloneJsonValue(item)) as T;\n if (value && typeof value === 'object') {\n const copy: Record<string, unknown> = {};\n for (const [key, nested] of Object.entries(value)) {\n copy[key] = this.cloneJsonValue(nested);\n }\n return copy as T;\n }\n return value;\n }\n\n private extractResolvedBrandChain(value: unknown): ResolvedBrand[] {\n if (!Array.isArray(value)) return [];\n return value.filter(item => this.isResolvedBrand(item));\n }\n\n private extractDomainChain(value: unknown): string[] | null {\n if (!Array.isArray(value)) return null;\n const domains: string[] = [];\n for (const item of value) {\n if (typeof item === 'string' && item.trim()) {\n domains.push(item);\n continue;\n }\n if (item && typeof item === 'object' && !Array.isArray(item)) {\n const domain = this.domainFromBrand(item as Partial<ResolvedBrand>);\n if (domain) domains.push(domain);\n }\n }\n return domains.length > 0 ? domains : null;\n }\n\n private domainFromBrand(brand: Partial<ResolvedBrand>): string | null {\n return brand.canonical_domain ?? brand.canonical_id ?? null;\n }\n\n private normalizeDomainKey(domain: string): string {\n return domain.trim().toLowerCase();\n }\n\n private setState(next: RegistrySyncState): void {\n const from = this._state;\n if (from === next) return;\n this._state = next;\n this.emit('stateChange', { from, to: next });\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -204,12 +204,18 @@ class RegistrySync extends EventEmitter {
|
|
|
204
204
|
* Get the ordered corporate ancestor domain chain for a brand domain.
|
|
205
205
|
*
|
|
206
206
|
* The returned array includes the resolved brand itself as the first entry and
|
|
207
|
-
* the house domain as the last entry when known.
|
|
207
|
+
* the house domain as the last entry when known. This compatibility view is
|
|
208
|
+
* populated only when older/self-hosted feeds emit brand hierarchy events.
|
|
209
|
+
* Treat it as topology for display/indexing, never as relationship
|
|
210
|
+
* authorization evidence.
|
|
208
211
|
*/
|
|
209
212
|
getAncestors(domain) {
|
|
210
213
|
return [...this.brandAncestorsByDomain.get(this.normalizeDomainKey(domain)) ?? []];
|
|
211
214
|
}
|
|
212
|
-
/**
|
|
215
|
+
/**
|
|
216
|
+
* Get the ordered resolved brand chain when an older/self-hosted feed supplied
|
|
217
|
+
* it. This topology-only compatibility view is not authorization evidence.
|
|
218
|
+
*/
|
|
213
219
|
getBrandHierarchy(domain) {
|
|
214
220
|
return (this.brandHierarchyByDomain.get(this.normalizeDomainKey(domain)) ?? []).map((brand) => ({ ...brand }));
|
|
215
221
|
}
|