@walkeros/web-source-browser 4.3.0-next-1784055686454 → 4.3.0

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/dist/dev.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/schemas/index.ts","../src/schemas/settings.ts","../src/schemas/primitives.ts","../src/schemas/tagger.ts","../src/examples/index.ts","../src/examples/env.ts","../src/examples/step.ts","../src/examples/trigger.ts","../src/trigger.ts","../src/walker.ts","../src/triggerVisible.ts","../src/translation.ts"],"sourcesContent":["import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\nimport { TaggerSchema } from './tagger';\n\n// Export primitives\nexport * from './primitives';\n\n// Export Zod schemas and types\nexport { SettingsSchema, type Settings } from './settings';\nexport { TaggerSchema, type TaggerConfig } from './tagger';\n\n// JSON Schema exports (for website PropertyTable and explorer RJSF)\nexport const settings = zodToSchema(SettingsSchema);\nexport const tagger = zodToSchema(TaggerSchema);\n","import { z } from '@walkeros/core/dev';\nimport {\n DataAttributePrefix,\n JavaScriptVarName,\n ScopeSelector,\n} from './primitives';\n\n/**\n * ELB Layer configuration schema\n * Note: Runtime type can be boolean | string | Elb.Layer\n */\nconst ElbLayerConfigSchema = z.union([\n z.boolean(),\n z.string(),\n z.any().describe('Elb.Layer array for async command queuing'),\n]);\n\n/**\n * Browser source settings schema\n */\nexport const SettingsSchema = z.object({\n prefix: DataAttributePrefix.default('data-elb').describe(\n 'Prefix for data attributes (default: data-elb)',\n ),\n\n scope: ScopeSelector.describe(\n 'DOM scope for event tracking (default: document)',\n ),\n\n pageview: z\n .boolean()\n .default(true)\n .describe('Enable automatic pageview tracking'),\n\n capture: z\n .boolean()\n .default(true)\n .describe(\n 'Read click/submit triggers in the capture phase so tagged elements are resolved at click time before app handlers run (set false for the previous bubble-phase behavior)',\n ),\n\n elb: JavaScriptVarName.default('elb').describe(\n 'Name for global elb function',\n ),\n\n name: z.string().describe('Custom name for source instance').optional(),\n\n elbLayer: ElbLayerConfigSchema.default('elbLayer').describe(\n 'Enable elbLayer for async command queuing (boolean, string, or Elb.Layer)',\n ),\n});\n\n// Note: We export the inferred type, but types/index.ts will override\n// specific fields with non-serializable types\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\n/**\n * Data attribute prefix\n * Used for DOM event tracking\n */\nexport const DataAttributePrefix = z\n .string()\n .min(1)\n .regex(/^[a-z][a-z0-9-]*$/, 'Must be lowercase kebab-case')\n .describe('Prefix for data attributes on DOM elements');\n\n/**\n * JavaScript variable name\n * Used for global function names\n */\nexport const JavaScriptVarName = z\n .string()\n .min(1)\n .regex(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/, 'Must be a valid JavaScript identifier')\n .describe('JavaScript variable name');\n\n/**\n * DOM scope selector\n * Note: Runtime type is Element | Document (non-serializable)\n */\nexport const ScopeSelector = z\n .string()\n .describe('DOM scope for event tracking (CSS selector or \"document\")')\n .optional();\n","import { z } from '@walkeros/core/dev';\nimport { DataAttributePrefix } from './primitives';\n\n/**\n * Tagger configuration schema\n * Used for automatic data attribute generation\n */\nexport const TaggerSchema = z.object({\n prefix: DataAttributePrefix.default('data-elb').describe(\n 'Custom prefix for generated data attributes',\n ),\n});\n\nexport type TaggerConfig = z.infer<typeof TaggerSchema>;\n","export * as env from './env';\nexport * as step from './step';\nexport { createTrigger, trigger } from './trigger';\n","import type { Env } from '../types';\nimport type { Elb, Logger } from '@walkeros/core';\n\n/**\n * Example environment configurations for browser source\n *\n * These environments provide standardized mock structures for testing\n * browser event capture without requiring an actual DOM environment.\n */\n\n// Simple no-op function for mocking\nconst noop = () => {};\n\n// Create a properly typed elb/push/command function that returns a promise with PushResult\nconst createMockElbFn = (): Elb.Fn => {\n const fn = (() =>\n Promise.resolve({\n ok: true,\n })) as Elb.Fn;\n return fn;\n};\n\n// Simple no-op logger for demo purposes\nconst noopLogger: Logger.Instance = {\n error: noop,\n warn: noop,\n info: noop,\n debug: noop,\n throw: (message: string | Error) => {\n throw typeof message === 'string' ? new Error(message) : message;\n },\n json: noop,\n scope: () => noopLogger,\n};\n\n/**\n * Mock window object with common browser APIs\n */\nconst createMockWindow = () => ({\n addEventListener: noop,\n removeEventListener: noop,\n location: {\n href: 'https://example.com/page',\n pathname: '/page',\n search: '?query=test',\n hash: '#section',\n host: 'example.com',\n hostname: 'example.com',\n origin: 'https://example.com',\n protocol: 'https:',\n },\n document: {},\n navigator: {\n language: 'en-US',\n userAgent: 'Mozilla/5.0 (Test)',\n },\n screen: {\n width: 1920,\n height: 1080,\n },\n innerWidth: 1920,\n innerHeight: 1080,\n pageXOffset: 0,\n pageYOffset: 0,\n scrollX: 0,\n scrollY: 0,\n});\n\n/**\n * Mock document object with DOM methods\n */\nconst createMockDocument = () => ({\n addEventListener: noop,\n removeEventListener: noop,\n querySelector: noop,\n querySelectorAll: () => [],\n getElementById: noop,\n getElementsByClassName: () => [],\n getElementsByTagName: () => [],\n createElement: () => ({\n setAttribute: noop,\n getAttribute: noop,\n addEventListener: noop,\n removeEventListener: noop,\n }),\n body: {\n appendChild: noop,\n removeChild: noop,\n },\n documentElement: {\n scrollTop: 0,\n scrollLeft: 0,\n },\n readyState: 'complete',\n title: 'Test Page',\n referrer: '',\n cookie: '',\n});\n\n/**\n * Standard mock environment for testing browser source\n *\n * Use this for testing event capture, DOM interactions, and pageview tracking\n * without requiring a real browser environment.\n */\nexport const push: Env = {\n get push() {\n return createMockElbFn();\n },\n get command() {\n return createMockElbFn();\n },\n get elb() {\n return createMockElbFn();\n },\n get window() {\n return createMockWindow() as unknown as typeof window;\n },\n get document() {\n return createMockDocument() as unknown as typeof document;\n },\n logger: noopLogger,\n};\n","import type { Flow } from '@walkeros/core';\n\nexport const pageView: Flow.StepExample = {\n title: 'Page view',\n description:\n 'A page load trigger captures the current URL, title, and referrer as a walker page view event.',\n trigger: {\n type: 'load',\n options: {\n url: 'https://example.com/docs',\n title: 'Documentation',\n referrer: 'https://example.com/',\n },\n },\n in: '',\n out: [\n [\n 'elb',\n {\n name: 'page view',\n data: {\n domain: 'example.com',\n title: 'Documentation',\n referrer: 'https://example.com/',\n id: '/docs',\n },\n context: {},\n globals: {},\n nested: undefined,\n custom: undefined,\n trigger: 'load',\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/docs',\n referrer: 'https://example.com/',\n },\n },\n ],\n ],\n};\n\nexport const clickEvent: Flow.StepExample = {\n title: 'Click event',\n description:\n 'A button click with walker data attributes is captured as an entity action event with the mapped label data.',\n trigger: { type: 'click', options: 'button' },\n in: '<button data-elb=\"cta\" data-elb-cta=\"label:Sign Up\" data-elbaction=\"click:click\">Sign Up</button>',\n out: [\n [\n 'elb',\n {\n name: 'cta click',\n entity: 'cta',\n action: 'click',\n data: { label: 'Sign Up' },\n context: {},\n globals: {},\n nested: [],\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/',\n referrer: '',\n },\n trigger: 'click',\n },\n ],\n ],\n};\n\nexport const submitEvent: Flow.StepExample = {\n title: 'Submit event',\n description:\n 'A form submit with walker data attributes produces a signup complete event carrying the selected plan.',\n trigger: { type: 'submit', options: 'form' },\n in: '<form data-elb=\"signup\" data-elb-signup=\"plan:premium\" data-elbaction=\"submit:complete\"></form>',\n out: [\n [\n 'elb',\n {\n name: 'signup complete',\n entity: 'signup',\n action: 'complete',\n data: { plan: 'premium' },\n context: {},\n globals: {},\n nested: [],\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/',\n referrer: '',\n },\n trigger: 'submit',\n },\n ],\n ],\n};\n\nexport const impressionEvent: Flow.StepExample = {\n title: 'Impression event',\n description:\n 'An intersection observer impression on a div with walker attributes emits a banner view event.',\n trigger: { type: 'impression', options: 'div' },\n in: '<div data-elb=\"banner\" data-elb-banner=\"type:promotional;position:sidebar\" data-elbaction=\"impression:view\"></div>',\n out: [\n [\n 'elb',\n {\n name: 'banner view',\n entity: 'banner',\n action: 'view',\n data: { type: 'promotional', position: 'sidebar' },\n context: {},\n globals: {},\n nested: [],\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/',\n referrer: '',\n },\n trigger: 'impression',\n },\n ],\n ],\n};\n\nexport const nestedEntities: Flow.StepExample = {\n title: 'Nested entities',\n description:\n 'A page load produces a page view plus a product view whose nested size entity is captured on the product event.',\n trigger: { type: 'load' },\n in: '<div data-elb=\"product\" data-elb-product=\"id:SKU-42;name:Sneakers\" data-elbaction=\"load:view\"><div data-elb=\"size\" data-elb-size=\"selected:large;inStock:true\"></div></div>',\n out: [\n [\n 'elb',\n {\n name: 'page view',\n data: {\n domain: 'example.com',\n title: '',\n referrer: '',\n id: '/',\n },\n context: {},\n globals: {},\n nested: undefined,\n custom: undefined,\n trigger: 'load',\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/',\n referrer: '',\n },\n },\n ],\n [\n 'elb',\n {\n name: 'product view',\n entity: 'product',\n action: 'view',\n data: { id: 'SKU-42', name: 'Sneakers' },\n context: {},\n globals: {},\n nested: [\n {\n entity: 'size',\n data: { selected: 'large', inStock: true },\n context: {},\n nested: [],\n },\n ],\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/',\n referrer: '',\n },\n trigger: 'load',\n },\n ],\n ],\n};\n\nexport const dataAttributeTypes: Flow.StepExample = {\n title: 'Data attribute types',\n description:\n 'Walker data attributes parse scalar, boolean, and array values into typed fields on the emitted event.',\n trigger: { type: 'click', options: 'div' },\n in: '<div data-elb=\"product\" data-elb-product=\"price:99.99;available:true;colors[]:red;colors[]:blue\" data-elbaction=\"click:select\"></div>',\n out: [\n [\n 'elb',\n {\n name: 'product select',\n entity: 'product',\n action: 'select',\n data: { price: 99.99, available: true, colors: ['red', 'blue'] },\n context: {},\n globals: {},\n nested: [],\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/',\n referrer: '',\n },\n trigger: 'click',\n },\n ],\n ],\n};\n\nexport const contextAndGlobals: Flow.StepExample = {\n title: 'Context and globals',\n description:\n 'Walker context and globals attributes attach ambient metadata to events emitted by the browser source.',\n trigger: { type: 'click', options: '[data-elb=\"cta\"]' },\n in: '<div data-elbcontext=\"test:engagement_flow\" data-elbglobals=\"language:en;plan:premium\"><div data-elb=\"cta\" data-elb-cta=\"label:Try Now\" data-elbaction=\"click:signup\">Try Now</div></div>',\n out: [\n [\n 'elb',\n {\n name: 'cta signup',\n entity: 'cta',\n action: 'signup',\n data: { label: 'Try Now' },\n context: { test: ['engagement_flow', 0] },\n globals: { language: 'en', plan: 'premium' },\n nested: [],\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/',\n referrer: '',\n },\n trigger: 'click',\n },\n ],\n ],\n};\n","import type { Trigger, Collector } from '@walkeros/core';\nimport { startFlow } from '@walkeros/collector';\nimport { handleTrigger } from '../trigger';\nimport type { Context } from '../types';\n\ntype BrowserTriggerType =\n | 'load'\n | 'click'\n | 'submit'\n | 'hover'\n | 'scroll'\n | 'impression'\n | 'visible'\n | 'wait'\n | 'pulse';\n\ninterface LoadOptions {\n url?: string;\n title?: string;\n referrer?: string;\n}\n\ninterface BrowserInput {\n trigger: string;\n url?: string;\n title?: string;\n referrer?: string;\n element?: string;\n attributes?: Record<string, string>;\n children?: Record<string, string>[];\n context?: Record<string, unknown>;\n globals?: Record<string, unknown>;\n}\n\n/**\n * Browser source createTrigger.\n *\n * Injects HTML into the DOM, lazily starts the flow, then calls\n * handleTrigger directly — the same convergence point as production.\n * For load triggers, the source's DOM scan during startFlow handles\n * event capture automatically.\n *\n * @example\n * const { trigger } = await createTrigger(config);\n * await trigger('click', 'button')('<button data-elb=\"cta\" data-elbaction=\"click:cta click\">Click</button>');\n *\n * @example\n * // Impression trigger — handleTrigger reads data-elbaction directly\n * await trigger('impression', '[data-elb=\"promo\"]')('<div data-elb=\"promo\" data-elbaction=\"visible:promo seen\">Ad</div>');\n */\nconst createTrigger: Trigger.CreateFn<string, void> = async (\n config: Collector.InitConfig,\n options?: unknown,\n) => {\n const sourceId = (options as { sourceId?: string } | undefined)?.sourceId || 'browser';\n let flow: Trigger.FlowHandle | undefined;\n const doc = document;\n const win = window;\n\n const trigger: Trigger.Fn<string, void> =\n (type?: string, opts?: unknown) => async (content: string) => {\n // 1. Set up environment for load triggers (URL, title, referrer)\n if (type === 'load' || !type) {\n const loadOpts =\n typeof opts === 'object' && opts !== null\n ? (opts as LoadOptions)\n : {};\n if (loadOpts.url) {\n const urlObj = new URL(loadOpts.url);\n win.history.replaceState({}, '', urlObj.pathname);\n }\n if (loadOpts.title) doc.title = loadOpts.title;\n if (loadOpts.referrer) {\n Object.defineProperty(doc, 'referrer', {\n value: loadOpts.referrer,\n configurable: true,\n });\n }\n }\n\n // 2. Inject HTML content into DOM — replaces to create fresh state\n if (content) doc.body.innerHTML = content;\n\n // 3. Lazy startFlow — first call initializes the flow\n // Default run: true so sources scan the DOM and set up listeners\n if (!flow) {\n const result = await startFlow({ ...config, run: config.run ?? true });\n flow = { collector: result.collector, elb: result.elb };\n }\n\n // 4. For load triggers, source already scanned DOM during init — done\n if (!type || type === 'load') return;\n\n // 5. Find target element\n const selector = typeof opts === 'string' ? opts : undefined;\n const target = selector ? doc.querySelector(selector) : null;\n\n if (!target) {\n console.warn(`Trigger: element not found for selector \"${selector}\"`);\n return;\n }\n\n // 6. Build context from live source instance\n const source = flow.collector.sources[sourceId];\n if (!source) {\n console.warn(`Trigger: source \"${sourceId}\" not found in collector`);\n return;\n }\n\n const context: Context = {\n elb: flow.elb,\n settings: source.config.settings as Context['settings'],\n };\n\n // 7. Call handleTrigger directly — same convergence point as production\n await handleTrigger(context, target, type);\n };\n\n return {\n get flow() {\n return flow;\n },\n trigger,\n };\n};\n\n/**\n * Browser source trigger (legacy).\n * Converts step example `in` data into DOM elements and dispatches events.\n */\nconst trigger = (\n input: unknown,\n env: Record<string, unknown>,\n): void | (() => void) => {\n if (!input || typeof input !== 'object') return;\n const data = input as BrowserInput;\n const doc = env.document as Document;\n const win = env.window as Window & typeof globalThis;\n\n const injectDOM = () => {\n if (!data.attributes) return;\n\n const tag = data.element?.match(/^(\\w+)/)?.[1] || 'div';\n const el = doc.createElement(tag);\n\n for (const [key, value] of Object.entries(data.attributes)) {\n el.setAttribute(key, value);\n }\n\n if (data.children) {\n for (const childAttrs of data.children) {\n const child = doc.createElement('div');\n for (const [key, value] of Object.entries(childAttrs)) {\n child.setAttribute(key, value);\n }\n el.appendChild(child);\n }\n }\n\n doc.body.appendChild(el);\n return el;\n };\n\n const isLoad = !data.trigger || data.trigger === 'load';\n\n if (isLoad) {\n if (data.url) {\n const urlObj = new URL(data.url);\n win.history.replaceState({}, '', urlObj.pathname);\n }\n if (data.title) {\n doc.title = data.title;\n }\n if (data.referrer) {\n Object.defineProperty(doc, 'referrer', { value: data.referrer });\n }\n injectDOM();\n return;\n }\n\n // Interactive triggers: inject DOM and dispatch event AFTER source init\n return () => {\n const el = injectDOM();\n if (!el) return;\n\n switch (data.trigger) {\n case 'click':\n el.dispatchEvent(new MouseEvent('click', { bubbles: true }));\n break;\n case 'submit':\n el.dispatchEvent(new Event('submit', { bubbles: true }));\n break;\n case 'hover':\n el.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));\n break;\n case 'scroll':\n win.dispatchEvent(new Event('scroll'));\n break;\n }\n };\n};\n\nexport { createTrigger, trigger };\n","import type { WalkerOS, Collector } from '@walkeros/core';\nimport type { Walker } from '@walkeros/web-core';\nimport type { Scope, InitScope, Settings, Context, ScopeState } from './types';\nimport { throttle, tryCatch } from '@walkeros/core';\nimport { Const, onApply } from '@walkeros/collector';\nimport { elb as elbOrg, getAttribute } from '@walkeros/web-core';\nimport {\n getElbAttributeName,\n getEvents,\n getPageViewData,\n getTriggerActions,\n queryAllComposed,\n} from './walker';\nimport {\n initVisibilityTracking,\n triggerVisible,\n destroyVisibilityTracking,\n unobserveElement,\n} from './triggerVisible';\nimport { translateToCoreCollector } from './translation';\n\n// Module-level state is intentional. The walker.js browser source is\n// single-instance per window: one elb queue, one set of DOM listeners.\n// If multi-instance ever becomes a real requirement (e.g., micro-frontends\n// with isolated walker queues), refactor this into a per-instance closure\n// returned by createSource() and route every trigger function through it.\n//\n// Root click/submit delegation lives on an instance-level controller, aborted\n// only by destroyTriggers (or re-arming initGlobalTrigger). It is deliberately\n// SEPARATE from the per-scope controllers below: a sub-scope re-init must\n// never tear down page-wide click/submit.\nlet rootAbortController: AbortController | undefined;\n\n// Iterable on purpose: destroyTriggers must enumerate every active scope to\n// reach sub-scope intervals/observers (a WeakMap could not back full teardown).\n// Note: this holds strong references to init'd scope nodes (including detached\n// DOM elements). A node init'd via `walker init <el>` and later removed from\n// the DOM without a re-init or source destroy is retained until re-init\n// replaces its entry or destroyTriggers clears the Map. Bounded by the\n// source/page session and accepted per plan §4.\nconst scopeStates = new Map<InitScope, ScopeState>();\n\n// Per-element teardown record. The reaper reads this to release exactly ONE\n// element's resources (its pulse intervals, its wait timeouts, its hover\n// listener, its visibility observation, its scroll entry) without touching the\n// rest of the scope. `scope` is the owning scope key: it selects both the owner\n// bucket (via scopeStates) and the unobserveElement key, so an element removed\n// inside a different scope's subtree is still released from its true owner.\nexport interface ElementRegistration {\n scope: InitScope;\n intervalIds: ReturnType<typeof setInterval>[];\n timeoutIds: ReturnType<typeof setTimeout>[];\n hoverAbort?: AbortController;\n scroll: boolean; // presence flag: the element itself keys into bucket.scrollElements\n observed: boolean; // presence flag: the element itself keys into bucket.observed\n}\n\n// Source-wide registry: the single authority for \"has ANY scope already wired\n// this element?\". A WeakMap is not enumerable, so the per-scope\n// ScopeState.registered set is its enumerable companion — reset/teardown iterate\n// that set to release a scope's elements from here. Invariant:\n// el ∈ bucket.registered ⟺ elementRegistry.get(el).scope === that bucket's scope.\nconst elementRegistry = new WeakMap<HTMLElement, ElementRegistration>();\n\n// Register an element as wired by `scope`, in both the enumerable per-scope set\n// and the source-wide registry. No-op (returns false) if any scope already\n// registered it (cross-scope dedup) or the scope has no live bucket. The owning\n// bucket is derived from scopeStates, never passed in, so the\n// registered/registry invariant is structural — a caller cannot register an\n// element into a bucket that mismatches `scope`.\nexport function registerElement(\n scope: InitScope,\n el: HTMLElement,\n registration: ElementRegistration,\n): boolean {\n if (elementRegistry.has(el)) return false;\n const bucket = scopeStates.get(scope);\n if (!bucket) return false;\n registration.scope = scope;\n bucket.registered.add(el);\n elementRegistry.set(el, registration);\n return true;\n}\n\n// Source-wide \"already wired?\" check: true if ANY scope registered the element.\nexport function isRegistered(el: HTMLElement): boolean {\n return elementRegistry.has(el);\n}\n\n// Release one element and un-register it, so it can register again on re-add or\n// re-scan. Self-derives the OWNING bucket from registration.scope (never a\n// caller's scope): an element owned by the document scope but removed inside a\n// sub-scope observer's subtree is still released from its true owner, keeping\n// the registered/registry invariant intact.\nexport function reapElement(el: HTMLElement): void {\n const registration = elementRegistry.get(el);\n if (!registration) return;\n\n const bucket = scopeStates.get(registration.scope);\n\n // Clear each id AND splice it from the owner's flat arrays, so a long\n // inject/remove session does not accrue dead ids there.\n registration.intervalIds.forEach((id) => {\n clearInterval(id);\n if (bucket) {\n const index = bucket.intervalIds.indexOf(id);\n if (index !== -1) bucket.intervalIds.splice(index, 1);\n }\n });\n registration.timeoutIds.forEach((id) => {\n clearTimeout(id);\n if (bucket) {\n const index = bucket.timeoutIds.indexOf(id);\n if (index !== -1) bucket.timeoutIds.splice(index, 1);\n }\n });\n\n registration.hoverAbort?.abort();\n\n if (registration.observed) {\n unobserveElement(registration.scope, el);\n bucket?.observed.delete(el);\n }\n\n if (registration.scroll && bucket) {\n // scrollElements is a [HTMLElement, number] tuple array — remove EVERY entry\n // for this element. A multi-scroll element (data-elbaction=\"scroll:50;scroll:75\")\n // pushes one tuple per trigger, so removing only the first would strand the\n // rest and fire a phantom scroll on the detached element.\n bucket.scrollElements = bucket.scrollElements.filter(\n ([element]) => element !== el,\n );\n }\n\n bucket?.registered.delete(el);\n elementRegistry.delete(el);\n}\n\n// Tear down a scope's prior state (if any), then install a fresh empty bucket.\nexport function resetScope(scope: InitScope): ScopeState {\n const previous = scopeStates.get(scope);\n if (previous) {\n previous.abort.abort(); // removes scroll listener (hover is per-element)\n // The flat clears below are the defensive baseline: they blanket-clear\n // every id/observation the scope holds. The reap loop that follows is a\n // superset per element — it additionally aborts hover, deletes the registry\n // entry, and splices the flat arrays — and it re-clears/re-unobserves the\n // same resources, which is idempotent and harmless. Both are kept so a\n // future resource that lands in a flat array without going through\n // registerScanElement (→ `registered`) is still torn down here.\n previous.intervalIds.forEach((id) => clearInterval(id));\n previous.timeoutIds.forEach((id) => clearTimeout(id));\n previous.observed.forEach((element) => unobserveElement(scope, element));\n // Release each element's per-element resources (crucially its hover\n // listener) and clear the registry so the elements can re-register on the\n // fresh scan. Snapshot the set — reapElement deletes from it as it goes.\n // reapElement self-derives this same still-current bucket (the fresh one is\n // not installed until below).\n [...previous.registered].forEach((element) => reapElement(element));\n previous.mutationObservers.forEach((observer) => observer.disconnect());\n }\n\n const fresh: ScopeState = {\n abort: new AbortController(),\n intervalIds: [],\n timeoutIds: [],\n scrollElements: [],\n observed: new Set(),\n registered: new Set(),\n mutationObservers: [],\n };\n scopeStates.set(scope, fresh);\n return fresh;\n}\n\n// Test-only accessor: exposes a scope's live bucket so suites can assert its\n// observer/registration counts and spy on real observer instances. Not\n// re-exported through src/index.ts, so the public package surface is unchanged.\nexport function getScopeState(scope: InitScope): ScopeState | undefined {\n return scopeStates.get(scope);\n}\n\n// Reset function for testing: clears each active scope's scroll state.\nexport function resetScrollListener() {\n scopeStates.forEach((state) => {\n state.scrollElements = [];\n state.scrollListener = undefined;\n });\n}\n\nexport const createElb: (customLayer?: unknown) => unknown = (customLayer?) => {\n return (\n customLayer\n ? function () {\n (customLayer as unknown[]).push(arguments);\n }\n : elbOrg\n ) as unknown;\n};\n\nexport const Triggers: { [key: string]: Walker.Trigger } = {\n Click: 'click',\n Custom: 'custom',\n Hover: 'hover',\n Load: 'load',\n Pulse: 'pulse',\n Scroll: 'scroll',\n Submit: 'submit',\n Impression: 'impression',\n Visible: 'visible',\n Wait: 'wait',\n} as const;\n\nexport async function ready(\n fn: (context: Context, settings: Settings) => void,\n context: Context,\n settings: Settings,\n): Promise<void> {\n const readyFn = () => {\n fn(context, settings);\n };\n\n const scope = settings.scope;\n if (!scope) {\n readyFn();\n return;\n }\n const doc = (scope as Element).ownerDocument || (scope as Document);\n if (doc.readyState !== 'loading') {\n readyFn();\n } else {\n doc.addEventListener('DOMContentLoaded', readyFn);\n }\n}\n\n// Called once during source initialization to setup global listeners\nexport function initTriggers(context: Context, settings: Settings) {\n if (!settings.scope) return; // Skip if no scope available\n const requiredSettings = settings as Required<Settings>;\n initGlobalTrigger(context, requiredSettings);\n}\n\n// Called on each walker run to process load triggers\nexport function processLoadTriggers(context: Context, settings: Settings) {\n if (!settings.scope) return; // Skip if no scope available\n const requiredSettings = settings as Required<Settings>;\n initScopeTrigger(context, requiredSettings);\n}\n\nexport function initGlobalTrigger(context: Context, settings: Settings): void {\n const scope = settings.scope;\n\n if (!scope) return;\n\n // Abort any previously registered root listeners before re-registering.\n // This controller owns ONLY the root click/submit delegation.\n if (rootAbortController) rootAbortController.abort();\n rootAbortController = new AbortController();\n const { signal } = rootAbortController;\n\n scope.addEventListener(\n 'click',\n tryCatch(function (this: Scope, ev: unknown) {\n triggerClick.call(this, context, ev as MouseEvent);\n }) as EventListener,\n { signal, capture: settings.capture },\n );\n scope.addEventListener(\n 'submit',\n tryCatch(function (this: Scope, ev: unknown) {\n triggerSubmit.call(this, context, ev as SubmitEvent);\n }) as EventListener,\n { signal, capture: settings.capture },\n );\n}\n\n// Full teardown: aborts the root click/submit controller, then enumerates the\n// scope registry so EVERY active scope (document and sub-scopes alike) has its\n// hover/scroll listeners aborted, intervals/timeouts cleared, and shared\n// per-document observer disconnected. Safe to call before any init.\nexport function destroyTriggers(_settings?: Settings): void {\n if (rootAbortController) {\n rootAbortController.abort();\n rootAbortController = undefined;\n }\n\n // Two passes over the scope registry. `visibilityStates` is keyed per owner\n // document (one shared observer state), while `scopeStates` holds many\n // entries per document (the document scope plus every `walker init <el>`\n // sub-scope). unobserveElement reads that shared per-document state, so every\n // scope's armed dwell timers must be cancelled BEFORE any scope tears the\n // shared state down; otherwise the first same-document destroy deletes the\n // state and later scopes' unobserve calls no-op, leaking their timers.\n scopeStates.forEach((state, scope) => {\n state.abort.abort();\n // Stop watching observe containers first, so no mutation callback fires\n // mid-teardown against a half-cleared scope.\n state.mutationObservers.forEach((observer) => observer.disconnect());\n // Flat clears are the defensive baseline superset (see resetScope): they\n // blanket-clear the scope's ids/observations. The reap loop below is the\n // per-element superset — additionally aborting hover and deleting registry\n // entries — and its re-clear/re-unobserve is idempotent.\n state.intervalIds.forEach((id) => clearInterval(id));\n state.timeoutIds.forEach((id) => clearTimeout(id));\n // Cancel armed visibility dwell timers and detach observed elements while\n // the shared per-document state still exists. Mirrors resetScope's cleanup.\n state.observed.forEach((element) => unobserveElement(scope, element));\n // Release per-element resources. Hover listeners now live on per-element\n // controllers, so state.abort.abort() above no longer covers them — reap\n // each registered element to abort its hoverAbort and clear its registry\n // entry. Snapshot the set — reapElement deletes from it as it goes.\n [...state.registered].forEach((element) => reapElement(element));\n });\n\n scopeStates.forEach((_state, scope) => {\n // Normalizes to the owner document and disconnects the shared observer;\n // the second call for a same-document sub-scope is a harmless no-op.\n destroyVisibilityTracking(scope);\n });\n\n scopeStates.clear();\n}\n\n// Scope is the single carrier: it lives only in context.settings.scope. The\n// optional second argument is kept for signature compatibility (the Context\n// `initScope` slot calls this with a single, scope-aligned context) but is not\n// read. `walker init <el>` and `walker run` (document) are the same path,\n// differing only in the scope the carrier holds. Re-running on a scope tears\n// down that scope's prior state, then attaches fresh = one fresh init.\nexport function initScopeTrigger(context: Context, _settings?: Settings) {\n const scope = context.settings.scope;\n if (!scope) return;\n\n const bucket = resetScope(scope);\n\n // Idempotent \"ensure\" of the per-document observer (one per ownerDocument).\n // Never disconnect on a sub-scope re-init — resetScope already unobserved\n // this scope's elements above.\n initVisibilityTracking(scope, 1000);\n\n // default data-elbaction\n const selectorAction = getElbAttributeName(\n context.settings.prefix,\n Const.Commands.Action,\n false,\n );\n const doc = (scope as Element).ownerDocument || (scope as Document);\n const win = doc.defaultView;\n // The element self-check only applies to an Element sub-scope. A ShadowRoot\n // scope has no getAttribute, so calling handleActionElem on it would throw;\n // it is scanned by queryAllComposed below instead. A Document scope has\n // scope === doc, so it never enters this branch.\n if (scope !== doc && win && scope instanceof win.Element) {\n // Handle the elements action(s), too\n registerScanElement(context, scope, selectorAction, bucket, scope);\n }\n\n // Handle all children action(s)\n queryAllComposed(scope, `[${selectorAction}]`, (elem) => {\n registerScanElement(context, elem, selectorAction, bucket, scope);\n });\n\n if (bucket.scrollElements.length) scroll(context, scope, bucket);\n\n // Watch [<prefix>observe] containers so tagged content injected later is\n // auto-registered (and reaped on removal) without a manual `walker init`.\n observeContainers(context, scope, bucket, selectorAction);\n}\n\n// Attach a MutationObserver to each OUTERMOST [<prefix>observe] container per\n// tree in this scope. Nesting-skip: a container contained by another candidate\n// is skipped, because a subtree:true observer already sees descendant additions\n// and nested observers double-report a single deep insertion. Node.contains does\n// not cross shadow boundaries, so a shadow-hosted container is never \"contained\"\n// by a light-DOM ancestor and correctly keeps its own observer.\nfunction observeContainers(\n context: Context,\n scope: InitScope,\n bucket: ScopeState,\n selectorAction: string,\n) {\n const doc = (scope as Element).ownerDocument || (scope as Document);\n const win = doc.defaultView;\n // Guard like createObserver's win.IntersectionObserver check so a window-less\n // environment simply no-ops instead of throwing.\n if (!win || !win.MutationObserver) return;\n\n const selectorObserve = getElbAttributeName(\n context.settings.prefix,\n 'observe',\n false,\n );\n\n // Candidate containers: the scope element itself (sub-scope self-check) plus\n // every discovered descendant, across open shadow roots.\n const containers: Element[] = [];\n if (\n scope !== doc &&\n scope instanceof win.Element &&\n scope.matches(`[${selectorObserve}]`)\n ) {\n containers.push(scope);\n }\n queryAllComposed(scope, `[${selectorObserve}]`, (el) => containers.push(el));\n\n containers.forEach((container) => {\n // Outermost-per-tree, but scope-LOCAL: `contains` only dedups candidates\n // discovered by THIS scope. Two scopes observing overlapping trees (a\n // document-scope container and a `walker init <el>` sub-scope container over\n // the same subtree) are NOT deduped here — the source-wide element registry\n // (isRegistered) makes their overlapping adds safe instead (Amendment 3, a\n // documented v1 limitation).\n const nested = containers.some(\n (other) => other !== container && other.contains(container),\n );\n if (nested) return;\n\n const observer = new win.MutationObserver(\n tryCatch((records: MutationRecord[]) => {\n // Re-read the LIVE bucket every callback: a resetScope between async\n // callbacks installs a fresh bucket and aborts this one; if the scope\n // was destroyed, bail. Never close over the creation-time bucket.\n const live = scopeStates.get(scope);\n if (!live) return;\n\n records.forEach((record) => {\n record.addedNodes.forEach((node) =>\n handleAddedNode(context, node, selectorAction, live, scope),\n );\n // Re-arm the scroll listener once per record: an added node may have\n // registered a scroll trigger into live.scrollElements. scroll() guards\n // duplicate listeners, so once-per-record is sufficient and cheap.\n if (live.scrollElements.length) scroll(context, scope, live);\n record.removedNodes.forEach((node) =>\n handleRemovedNode(context, node),\n );\n });\n }),\n );\n observer.observe(container, { childList: true, subtree: true });\n bucket.mutationObservers.push(observer);\n });\n}\n\nexport async function handleTrigger(\n context: Context,\n element: Element,\n trigger: string,\n // @TODO add triggerParams to filter for specific trigger\n): Promise<unknown[]> {\n const events = getEvents(element, trigger, context.settings.prefix);\n return Promise.all(\n events.map((event: Walker.Event) =>\n translateToCoreCollector(context, {\n name: `${event.entity} ${event.action}`,\n ...event,\n trigger,\n }),\n ),\n );\n}\n\n// Discover + register a single scanned node. Returns the built registration\n// (or undefined if the node was skipped), so callers/tests can inspect the\n// per-element resources it recorded. Guards up front: element nodes only\n// (nodeType === 1, narrowed cast-free via `instanceof win.HTMLElement`), not\n// already registered by any scope, and carrying the action attribute.\nexport function registerScanElement(\n context: Context,\n node: Node,\n selectorAction: string,\n bucket: ScopeState,\n scope: InitScope,\n): ElementRegistration | undefined {\n if (node.nodeType !== 1) return undefined;\n const win = node.ownerDocument?.defaultView;\n if (!win || !(node instanceof win.HTMLElement)) return undefined;\n if (isRegistered(node)) return undefined;\n if (!getAttribute(node, selectorAction)) return undefined;\n\n const registration: ElementRegistration = {\n scope,\n intervalIds: [],\n timeoutIds: [],\n scroll: false,\n observed: false,\n };\n handleActionElem(context, node, selectorAction, bucket, registration);\n registerElement(scope, node, registration);\n return registration;\n}\n\n// The removed-node path the MutationObserver callback invokes. Element nodes\n// only (text/comment have no .matches/.querySelectorAll and would throw). Reaps\n// `removedEl` itself and each tagged descendant that ANY scope registered.\n// reapElement is single-arg and self-derives the OWNING bucket from\n// registration.scope (Amendment 2): a document-scope-owned element removed\n// inside a sub-scope observer's subtree is released from its true owner, not the\n// remover's scope — never pass a caller's scope/bucket here.\nexport function handleRemovedNode(context: Context, removedEl: Node): void {\n if (removedEl.nodeType !== 1) return;\n const win = removedEl.ownerDocument?.defaultView;\n if (!win || !(removedEl instanceof win.HTMLElement)) return;\n\n const selectorAction = getElbAttributeName(\n context.settings.prefix,\n Const.Commands.Action,\n false,\n );\n\n if (isRegistered(removedEl)) reapElement(removedEl);\n queryAllComposed(removedEl, `[${selectorAction}]`, (el) => {\n if (el instanceof win.HTMLElement && isRegistered(el)) reapElement(el);\n });\n}\n\n// The added-node path the MutationObserver callback invokes, mirroring\n// handleRemovedNode. HTMLElement nodes only: this narrowing is deliberate, not\n// merely a text/comment guard. Text/comment nodes lack .matches/.querySelectorAll\n// and would throw, but an SVGElement HAS them — it is excluded on purpose because\n// the whole registration pipeline (the elementRegistry WeakMap, the bucket\n// HTMLElement sets, handleActionElem) is HTMLElement-typed. Do not widen to\n// Element. addedNodes holds only the top-level inserted node, so walk its tagged\n// descendants too. Registers into the passed live bucket (the caller re-reads it\n// per callback); unlike the reaper, the add path must be told where to register.\nfunction handleAddedNode(\n context: Context,\n node: Node,\n selectorAction: string,\n bucket: ScopeState,\n scope: InitScope,\n): void {\n const win = node.ownerDocument?.defaultView;\n if (!win || !(node instanceof win.HTMLElement)) return;\n registerScanElement(context, node, selectorAction, bucket, scope);\n queryAllComposed(node, `[${selectorAction}]`, (el) => {\n registerScanElement(context, el, selectorAction, bucket, scope);\n });\n}\n\nfunction handleActionElem(\n context: Context,\n elem: HTMLElement,\n selectorAction: string,\n bucket: ScopeState,\n registration: ElementRegistration,\n) {\n const actionAttr = getAttribute(elem, selectorAction);\n\n if (!actionAttr) return;\n\n // TriggersActionGroups ([trigger: string]: TriggerActions)\n Object.values(getTriggerActions(actionAttr)).forEach((triggerActions) =>\n // TriggerActions (Array<TriggerAction>)\n triggerActions.forEach((triggerAction: Walker.TriggerActions[0]) => {\n // TriggerAction ({ trigger, triggerParams, action, actionParams })\n switch (triggerAction.trigger) {\n case Triggers.Hover:\n triggerHover(context, elem, registration);\n break;\n case Triggers.Load:\n triggerLoad(context, elem);\n break;\n case Triggers.Pulse:\n triggerPulse(\n context,\n elem,\n bucket,\n registration,\n triggerAction.triggerParams,\n );\n break;\n case Triggers.Scroll:\n triggerScroll(\n elem,\n bucket,\n registration,\n triggerAction.triggerParams,\n );\n break;\n case Triggers.Impression:\n triggerVisible(context, elem);\n bucket.observed.add(elem);\n registration.observed = true;\n break;\n case Triggers.Visible:\n triggerVisible(context, elem, { multiple: true });\n bucket.observed.add(elem);\n registration.observed = true;\n break;\n case Triggers.Wait:\n triggerWait(\n context,\n elem,\n bucket,\n registration,\n triggerAction.triggerParams,\n );\n break;\n }\n }),\n );\n}\n\n/**\n * Get the actual event target, piercing open shadow DOM boundaries.\n * Uses composedPath() to find the real target inside shadow roots.\n * For closed shadow DOM, falls back to the host element (by design).\n */\nfunction getComposedTarget(ev: Event): Element | undefined {\n const path = ev.composedPath?.();\n const target = path?.length ? path[0] : ev.target;\n if (!target || typeof target !== 'object' || !('tagName' in target))\n return undefined;\n return target as Element;\n}\n\nfunction triggerClick(context: Context, ev: MouseEvent) {\n const target = getComposedTarget(ev);\n if (target) handleTrigger(context, target, Triggers.Click);\n}\n\nfunction triggerHover(\n context: Context,\n elem: HTMLElement,\n registration: ElementRegistration,\n) {\n // Per-element controller: the reaper aborts exactly this element's hover\n // listener without tearing down the scope. Reuse one controller per element\n // so multiple hover actions on the same element share (and are all released\n // by) a single abort.\n if (!registration.hoverAbort) registration.hoverAbort = new AbortController();\n elem.addEventListener(\n 'mouseenter',\n tryCatch(function (this: Document, ev: MouseEvent) {\n const target = getComposedTarget(ev);\n if (target) handleTrigger(context, target, Triggers.Hover);\n }),\n { signal: registration.hoverAbort.signal },\n );\n}\n\nfunction triggerLoad(context: Context, elem: HTMLElement) {\n handleTrigger(context, elem, Triggers.Load);\n}\n\nfunction triggerPulse(\n context: Context,\n elem: HTMLElement,\n bucket: ScopeState,\n registration: ElementRegistration,\n triggerParams: string = '',\n) {\n const doc = elem.ownerDocument;\n const intervalId = setInterval(\n () => {\n // Only trigger when tab is active\n if (!doc.hidden) handleTrigger(context, elem, Triggers.Pulse);\n },\n parseInt(triggerParams || '') || 15000,\n );\n bucket.intervalIds.push(intervalId);\n registration.intervalIds.push(intervalId);\n}\n\nfunction triggerScroll(\n elem: HTMLElement,\n bucket: ScopeState,\n registration: ElementRegistration,\n triggerParams: string = '',\n) {\n // Scroll depth in percent, default 50%\n const depth = parseInt(triggerParams || '') || 50;\n\n // Ignore invalid parameters\n if (depth < 0 || depth > 100) return;\n\n bucket.scrollElements.push([elem, depth]);\n registration.scroll = true;\n}\n\nfunction triggerSubmit(context: Context, ev: SubmitEvent) {\n const target = getComposedTarget(ev);\n if (target) handleTrigger(context, target, Triggers.Submit);\n}\n\nfunction triggerWait(\n context: Context,\n elem: HTMLElement,\n bucket: ScopeState,\n registration: ElementRegistration,\n triggerParams: string = '',\n) {\n const timeoutId = setTimeout(\n () => handleTrigger(context, elem, Triggers.Wait),\n parseInt(triggerParams || '') || 15000,\n );\n bucket.timeoutIds.push(timeoutId);\n registration.timeoutIds.push(timeoutId);\n}\n\nfunction scroll(context: Context, scope: InitScope, bucket: ScopeState) {\n const doc = (scope as Element).ownerDocument || (scope as Document);\n const win = doc.defaultView!;\n const scrolling = (\n scrollElements: Walker.ScrollElements,\n context: Context,\n ) => {\n return scrollElements.filter(([element, depth]: [Element, number]) => {\n // getBoundingClientRect is viewport-relative and composes across shadow\n // boundaries, so scroll depth is correct for shadow-nested elements.\n const rect = (element as HTMLElement).getBoundingClientRect();\n\n // Skip calculations if not in viewport yet (element top below the fold)\n if (win.innerHeight < rect.top) return true;\n\n // Height of the elements box as 100 percent base\n const elemHeight = rect.height || element.clientHeight;\n // Distance from the viewport top to the elements bottom\n const elemBottom = rect.top + elemHeight;\n // Height of the non-visible pixels below the visible screen\n const hidden = elemBottom - win.innerHeight;\n // Visible percentage of the element\n const scrollDepth = (1 - hidden / (elemHeight || 1)) * 100;\n\n // Check if the elements visibility skipped the required border\n if (scrollDepth >= depth) {\n // Enough scrolling, it's time\n handleTrigger(context, element, Triggers.Scroll);\n\n // Remove the element from scrollEvents\n return false;\n }\n\n // Keep observing the element\n return true;\n });\n };\n\n // Don't add unnecessary scroll listeners. The bucket is fresh per init, so\n // the scope's prior scroll listener was already aborted via its controller.\n if (!bucket.scrollListener) {\n bucket.scrollListener = throttle(function () {\n bucket.scrollElements = scrolling.call(\n scope,\n bucket.scrollElements,\n context,\n );\n });\n\n // A ShadowRoot never receives page scroll events, so a listener on it would\n // never fire; attach to the owner document instead. Element/Document scopes\n // keep their existing target.\n const scrollTarget = scope instanceof win.ShadowRoot ? doc : scope;\n scrollTarget.addEventListener('scroll', bucket.scrollListener, {\n signal: bucket.abort.signal,\n });\n }\n}\n","import type { WalkerOS } from '@walkeros/core';\nimport type { Walker } from '@walkeros/web-core';\nimport type { Scope } from './types';\nimport { assign, castValue, isArray, trim } from '@walkeros/core';\nimport { Const } from '@walkeros/collector';\nimport { getAttribute } from '@walkeros/web-core';\n\nexport function getElbAttributeName(\n prefix: string,\n name?: string,\n isProperty = true,\n): string {\n // separate dynamic properties from walker Const.Commands\n const separator = isProperty ? '-' : '';\n name = name != undefined ? separator + name : '';\n return prefix + name;\n}\n\nexport function getElbValues(\n prefix: string,\n element: Element,\n name: string,\n isProperty = true,\n): WalkerOS.Properties {\n const attributeValue =\n getAttribute(element, getElbAttributeName(prefix, name, isProperty)) || '';\n\n const elbValues = splitAttribute(attributeValue).reduce((values, str) => {\n let [key, val]: Walker.KeyVal = splitKeyVal(str);\n\n if (!key) return values;\n\n // Handle keys without value\n if (!val) {\n // Manually remove the : from key on empty values\n if (key.endsWith(':')) key = key.slice(0, -1);\n val = '';\n }\n\n // Dynamic values\n if (val.startsWith('#')) {\n val = val.slice(1); // Remove # symbol\n try {\n // Read property value from element\n let dynamicValue = (element as Element)[val as keyof Element];\n if (!dynamicValue && val === 'selected') {\n // Try to read selected value with chance of error\n dynamicValue = (element as HTMLSelectElement).options[\n (element as HTMLSelectElement).selectedIndex\n ].text;\n }\n\n val = String(dynamicValue);\n } catch (error) {\n val = '';\n }\n }\n\n if (key.endsWith('[]')) {\n key = key.slice(0, -2); // Remove [] symbol\n if (!isArray(values[key])) values[key] = [];\n (values[key] as WalkerOS.PropertyType[]).push(castValue(val));\n } else {\n values[key] = castValue(val);\n }\n\n return values;\n }, {} as WalkerOS.Properties);\n\n return elbValues;\n}\n\nexport function getAllEvents(\n scope: Scope,\n prefix: string = Const.Commands.Prefix,\n): Walker.Events {\n const actualScope = scope;\n if (!actualScope) return [];\n let events: Walker.Events = [];\n const action = Const.Commands.Action;\n const actionSelector = `[${getElbAttributeName(prefix, action, false)}]`;\n\n const processElementEvents = (elem: Element) => {\n Object.keys(getElbValues(prefix, elem, action, false)).forEach(\n (trigger) => {\n events = events.concat(getEvents(elem, trigger, prefix));\n },\n );\n };\n\n // Check if the scope element itself has action attributes\n const doc =\n (actualScope as Element).ownerDocument || (actualScope as Document);\n if (\n actualScope !== doc &&\n (actualScope as Element).matches?.(actionSelector)\n ) {\n processElementEvents(actualScope as Element);\n }\n\n queryAllComposed(actualScope, actionSelector, processElementEvents);\n\n return events;\n}\n\nexport function getEvents(\n target: Element,\n trigger: string,\n prefix: string = Const.Commands.Prefix,\n): Walker.Events {\n const events: Walker.Events = [];\n\n // Check for actions and get entity collection strategy\n const { actions, nearestOnly } = resolveAttributes(prefix, target, trigger);\n\n // Stop if there's no valid action combo\n if (!actions.length) return events;\n\n actions.forEach((triggerAction) => {\n const filter = splitAttribute(triggerAction.actionParams || '', ',').reduce(\n (filter, param) => {\n filter[trim(param)] = true;\n return filter;\n },\n {} as Walker.Filter,\n );\n\n // Get entities - using nearestOnly flag to determine collection strategy\n const entities = getEntities(prefix, target, filter, nearestOnly);\n\n // Use page as default entity if no one was set\n if (!entities.length) {\n const entity = 'page';\n // Only use explicit page properties and ignore generic properties\n const entitySelector = `[${getElbAttributeName(prefix, entity)}]`;\n\n // Get matching properties from the element and its parents\n const [data, context] = getThisAndParentProperties(\n target,\n entitySelector,\n prefix,\n entity,\n );\n\n entities.push({\n entity, // page\n data, // Consider only upper data\n nested: [], // Skip nested in this faked page case\n context,\n });\n }\n\n // Return a list of full events\n entities.forEach((entity) => {\n events.push({\n entity: entity.entity,\n action: triggerAction.action,\n data: entity.data,\n trigger,\n context: entity.context,\n nested: entity.nested ?? [],\n });\n });\n });\n\n return events;\n}\n\nexport function getGlobals(\n prefix: string = Const.Commands.Prefix,\n scope?: Scope,\n): WalkerOS.Properties {\n if (!scope) return {};\n const globalsName = getElbAttributeName(\n prefix,\n Const.Commands.Globals,\n false,\n );\n const globalSelector = `[${globalsName}]`;\n let values = {};\n\n queryAll(scope, globalSelector, (element) => {\n values = assign(\n values,\n getElbValues(prefix, element, Const.Commands.Globals, false),\n );\n });\n\n return values;\n}\n\nexport function getUser(\n prefix: string = Const.Commands.Prefix,\n scope?: Scope,\n): WalkerOS.User {\n if (!scope) return {};\n const userName = getElbAttributeName(prefix, Const.Commands.User, false);\n const userSelector = `[${userName}]`;\n let values: WalkerOS.User = {};\n\n queryAll(scope, userSelector, (element) => {\n values = assign(\n values,\n getElbValues(prefix, element, Const.Commands.User, false),\n );\n });\n\n return values;\n}\n\nexport function getPageViewData(\n prefix: string,\n scope: Scope,\n): [WalkerOS.Properties, WalkerOS.OrderedProperties] {\n // static page view\n const doc = (scope as Element).ownerDocument || (scope as Document);\n const win = doc.defaultView!;\n const loc = win.location;\n const page = 'page';\n const scopeElement =\n 'body' in scope ? (scope as Document).body : (scope as Element);\n const [data, context] = getThisAndParentProperties(\n scopeElement,\n `[${getElbAttributeName(prefix, page)}]`,\n prefix,\n page,\n );\n data.domain = loc.hostname;\n data.title = doc.title;\n data.referrer = doc.referrer;\n\n if (loc.search) data.search = loc.search;\n if (loc.hash) data.hash = loc.hash;\n\n // @TODO get all nested entities\n return [data, context];\n}\n\nexport function getTriggerActions(str: string): Walker.TriggersActionGroups {\n const values: Walker.TriggersActionGroups = {};\n\n const attributes = splitAttribute(str);\n\n attributes.forEach((str) => {\n const [triggerAttr, actionAttr] = splitKeyVal(str);\n const [trigger, triggerParams] = parseAttribute(triggerAttr);\n\n if (!trigger) return;\n\n let [action, actionParams] = parseAttribute(actionAttr || '');\n\n // Shortcut if trigger and action are the same (click:click)\n action = action || trigger;\n\n if (!values[trigger]) values[trigger] = [];\n\n values[trigger].push({ trigger, triggerParams, action, actionParams });\n });\n\n return values;\n}\n\nexport function getEntities(\n prefix: string,\n target: Element,\n filter?: Walker.Filter,\n nearestOnly = false,\n): WalkerOS.Entities {\n const entities: WalkerOS.Entities = [];\n let element = target as Node['parentElement'];\n\n // Unset empty filter object\n filter = Object.keys(filter || {}).length !== 0 ? filter : undefined;\n\n while (element) {\n const entity = getEntity(prefix, element, target, filter);\n if (entity) {\n entities.push(entity);\n if (nearestOnly) break; // Stop after first entity for data-elbaction\n }\n\n element = getParent(prefix, element);\n }\n\n return entities;\n}\n\nfunction getEntity(\n prefix: string,\n element: Element,\n origin?: Element,\n filter?: Walker.Filter,\n): WalkerOS.Entity | null {\n const entity = getAttribute(element, getElbAttributeName(prefix));\n\n // It's not a (valid) entity element or should be filtered\n if (!entity || (filter && !filter[entity])) return null;\n\n const scopeElems = [element]; // All related elements\n const dataSelector = `[${getElbAttributeName(\n prefix,\n entity,\n )}],[${getElbAttributeName(prefix, '')}]`; // [data-elb-entity,data-elb-]\n const linkName = getElbAttributeName(prefix, Const.Commands.Link, false); // data-elblink\n\n let data: WalkerOS.Properties = {};\n const nested: WalkerOS.Entities = [];\n const [parentData, context] = getThisAndParentProperties(\n origin || element,\n dataSelector,\n prefix,\n entity,\n true, // collect scoped generic (data-elb_) for real entities\n );\n\n // Add linked elements (data-elblink)\n queryAllComposed(element, `[${linkName}]`, (link) => {\n const [linkId, linkState]: Walker.KeyVal = splitKeyVal(\n getAttribute(link, linkName),\n );\n\n // Get all linked child elements if link is a parent\n // Note: Searches entire document including shadow roots.\n // Acceptable because link-parent usage is rare in practice.\n if (linkState === 'parent')\n queryAllComposed(\n element.ownerDocument.body,\n `[${linkName}=\"${linkId}:child\"]`,\n (wormhole) => {\n scopeElems.push(wormhole);\n\n // A linked child can also be an entity\n const nestedEntity = getEntity(prefix, wormhole);\n if (nestedEntity) nested.push(nestedEntity);\n },\n );\n });\n\n // Get all property elements including linked elements\n const propertyElems: Array<Element> = [];\n scopeElems.forEach((elem) => {\n // Also check for property on same level\n if (elem.matches(dataSelector)) propertyElems.push(elem);\n\n queryAllComposed(elem, dataSelector, (elem) => propertyElems.push(elem));\n });\n\n // Get properties\n let genericData: WalkerOS.Properties = {};\n propertyElems.forEach((child) => {\n // Eventually override closer properties\n genericData = assign(genericData, getElbValues(prefix, child, ''));\n data = assign(data, getElbValues(prefix, child, entity));\n });\n\n // Merge properties with the hierarchy generic > data > parent\n data = assign(assign(genericData, data), parentData);\n\n // Get nested entities\n scopeElems.forEach((elem) => {\n queryAllComposed(\n elem,\n `[${getElbAttributeName(prefix)}]`,\n (nestedEntityElement) => {\n const nestedEntity = getEntity(prefix, nestedEntityElement);\n if (nestedEntity) nested.push(nestedEntity);\n },\n );\n });\n\n return { entity, data, context, nested };\n}\n\nfunction getParent(prefix: string, elem: HTMLElement): HTMLElement | null {\n const linkName = getElbAttributeName(prefix, Const.Commands.Link, false); // data-elblink\n\n // Link\n if (elem.matches(`[${linkName}]`)) {\n const [linkId, linkState]: Walker.KeyVal = splitKeyVal(\n getAttribute(elem, linkName),\n );\n if (linkState === 'child') {\n // Link-parent lookup does not cross shadow boundaries.\n // Uses simple queryAll (no shadow recursion) since this runs\n // during per-event entity traversal.\n const doc = elem.ownerDocument;\n let found: HTMLElement | null = null;\n queryAll(doc, `[${linkName}=\"${linkId}:parent\"]`, (el) => {\n if (!found) found = el as HTMLElement;\n });\n return found;\n }\n }\n\n // Shadow DOM traversal\n const win = elem.ownerDocument.defaultView!;\n if (\n !elem.parentElement &&\n elem.getRootNode &&\n elem.getRootNode() instanceof win.ShadowRoot\n ) {\n return (elem.getRootNode() as ShadowRoot).host as HTMLElement;\n }\n\n return elem.parentElement;\n}\n\nfunction getThisAndParentProperties(\n element: Element,\n entitySelector: string,\n prefix: string,\n type: string,\n collectScoped = false,\n): [data: WalkerOS.Properties, context: WalkerOS.OrderedProperties] {\n let data: WalkerOS.Properties = {};\n const context: WalkerOS.OrderedProperties = {};\n let parent = element as Node['parentElement'];\n const contextSelector = `[${getElbAttributeName(\n prefix,\n Const.Commands.Context,\n false,\n )}]`;\n const scopedName = Const.Commands.Scoped; // '_' -> data-elb_\n const scopedSelector = `[${getElbAttributeName(prefix, scopedName, false)}]`;\n\n // Get all bubbling-up properties with decreasing priority\n let contextI = 0; // Context counter\n while (parent) {\n // Scoped generic (data-elb_): same generic tier, bubble-up only.\n // The synthetic page entity ignores generics, so it skips scoped too.\n // Guard on scopedName so a falsy command constant degrades to \"no scoped\n // collection\" instead of an empty suffix that would match the bare prefix.\n if (collectScoped && scopedName && parent.matches(scopedSelector)) {\n data = assign(getElbValues(prefix, parent, scopedName, false), data);\n }\n\n // Properties\n if (parent.matches(entitySelector)) {\n // Get higher properties first\n data = assign(getElbValues(prefix, parent, ''), data); // Generic\n data = assign(getElbValues(prefix, parent, type), data); // Explicit\n }\n\n // Context\n if (parent.matches(contextSelector)) {\n Object.entries(\n getElbValues(prefix, parent, Const.Commands.Context, false),\n ).forEach(([key, val]) => {\n // Don't override context with same but higher key\n if (val && !context[key]) context[key] = [val, contextI];\n });\n\n // Increase context counter with each parent level\n ++contextI;\n }\n\n parent = getParent(prefix, parent);\n }\n\n return [data, context];\n}\n\nfunction queryAll(\n scope: Document | Element,\n selector: string,\n fn: (element: Element) => void,\n): void {\n scope.querySelectorAll(selector).forEach(fn);\n}\n\n// Shadow-aware version that recurses into open shadow roots.\n// Use for discovery (init) and entity-scoped queries (small subtrees).\n// Avoid for per-event document-scoped queries (e.g. getGlobals).\nexport function queryAllComposed(\n scope: Document | Element | ShadowRoot,\n selector: string,\n fn: (element: Element) => void,\n): void {\n scope.querySelectorAll(selector).forEach(fn);\n\n const doc = (scope as Element).ownerDocument || (scope as Document);\n const win = doc.defaultView!;\n if (scope instanceof win.Element && scope.shadowRoot) {\n queryAllComposed(scope.shadowRoot, selector, fn);\n }\n scope.querySelectorAll('*').forEach((el) => {\n if (el.shadowRoot) {\n queryAllComposed(el.shadowRoot, selector, fn);\n }\n });\n}\n\nfunction resolveAttributes(\n prefix: string,\n target: Element,\n trigger: string,\n): { actions: Walker.TriggerActions; nearestOnly: boolean } {\n let element = target as Node['parentElement'];\n\n while (element) {\n // Check for data-elbactions first (takes precedence)\n const multiAttribute = getAttribute(\n element,\n getElbAttributeName(prefix, Const.Commands.Actions, false),\n );\n\n if (multiAttribute) {\n const triggerActions = getTriggerActions(multiAttribute);\n if (triggerActions[trigger]) {\n return { actions: triggerActions[trigger], nearestOnly: false };\n }\n }\n\n // Check for data-elbaction (nearest entity only)\n const singleAttribute = getAttribute(\n element,\n getElbAttributeName(prefix, Const.Commands.Action, false),\n );\n\n if (singleAttribute) {\n const triggerActions = getTriggerActions(singleAttribute);\n // Action found on element or is not a click trigger\n if (triggerActions[trigger] || trigger !== 'click') {\n return { actions: triggerActions[trigger] || [], nearestOnly: true };\n }\n }\n\n element = getParent(prefix, element);\n }\n\n return { actions: [], nearestOnly: false };\n}\n\nfunction splitAttribute(str: string, separator = ';'): Walker.Attributes {\n const values: Walker.Attributes = [];\n\n if (!str) return values;\n\n const reg = new RegExp(`(?:[^${separator}']+|'[^']*')+`, 'ig');\n return str.match(reg) || [];\n}\n\nfunction splitKeyVal(str: string): Walker.KeyVal {\n const [key, value] = str.split(/:(.+)/, 2);\n return [trim(key), trim(value)];\n}\n\nfunction parseAttribute(str: string): Walker.KeyVal {\n // action(a, b, c)\n const [key, value] = str.split('(', 2);\n const param = value ? value.slice(0, -1) : ''; // Remove the )\n // key = 'action'\n // param = 'a, b, c'\n return [key, param];\n}\n","import type { Context, InitScope } from './types';\nimport { tryCatch } from '@walkeros/core';\nimport { isVisible } from '@walkeros/web-core';\nimport { handleTrigger, Triggers } from './trigger';\n\n// Fraction of the element, or of the viewport, whichever is smaller, that must\n// be on screen along each axis.\nconst COVERAGE = 0.5;\n\n/**\n * Decide from the observer entry alone whether an element is showing enough of\n * itself to start the dwell clock. Reads no DOM and forces no layout: every\n * rect here was computed by the browser during this frame's layout pass.\n *\n * `intersectionRatio` cannot be used directly. It is a fraction of the\n * element's own area, so an element bigger than the viewport along an axis can\n * never show COVERAGE of itself along it: a 716px card in a 450px viewport\n * tops out at 0.63, and an 8000px section tops out at 0.1. Clamping the\n * requirement to `min(element, viewport)` per axis makes the bar reachable for\n * any geometry, and gives the property customers actually expect: shrinking the\n * viewport can only make the trigger fire earlier, never later.\n *\n * The clamp is per axis rather than by area on purpose. An area clamp collapses\n * back to the element-relative rule whenever the element's area is smaller than\n * the viewport's, which is exactly the wide-but-short case (a 3000x80 row tops\n * out at ratio 0.48 and never fires).\n */\nexport function isEligible(\n entry: IntersectionObserverEntry,\n win: Window,\n): boolean {\n if (!entry.isIntersecting) return false;\n\n const box = entry.boundingClientRect;\n // A zero-area target reports intersectionRatio 1 while it is intersecting\n // (spec 2.2.12). It has nothing on screen; never start a dwell for it.\n if (box.width <= 0 || box.height <= 0) return false;\n\n // rootBounds is null when the target lives in a cross-origin iframe. Falling\n // back to the window keeps the arithmetic finite: a NaN would compare false\n // and silently disable the trigger for every such element.\n const rootWidth = entry.rootBounds ? entry.rootBounds.width : win.innerWidth;\n const rootHeight = entry.rootBounds\n ? entry.rootBounds.height\n : win.innerHeight;\n if (rootWidth <= 0 || rootHeight <= 0) return false;\n\n const visible = entry.intersectionRect;\n const needWidth = COVERAGE * Math.min(box.width, rootWidth);\n const needHeight = COVERAGE * Math.min(box.height, rootHeight);\n\n return visible.width >= needWidth && visible.height >= needHeight;\n}\n\n// Threshold grid for the observer.\n//\n// An entry is queued only when the crossed threshold INDEX changes; a bare ratio\n// change queues nothing. The grid is therefore not a performance dial, it is the\n// SAMPLING RATE of the predicate: the trigger can only notice an element became\n// eligible at a grid crossing, which quantizes the arm point UPWARD to the next\n// threshold.\n//\n// A coarse grid makes that error large. Eligibility for an element of height H in\n// a viewport of height V lands at ratio COVERAGE * V/H, which for a 716px card in\n// a 450px viewport is 0.314. A grid whose next step above that is 0.4 would not\n// arm until 287px was showing, against a true bar of 225px.\n//\n// A uniform 1% grid keeps the error within about 1% of the element and costs\n// effectively nothing: the observer queues at most ONE entry per target per frame\n// however many thresholds there are.\nconst THRESHOLDS = Array.from({ length: 101 }, (_, index) => index / 100);\n\ninterface ElementConfig {\n multiple: boolean;\n blocked: boolean;\n context: Context;\n trigger: string;\n}\n\ninterface VisibilityState {\n observer?: IntersectionObserver;\n resizeObserver?: ResizeObserver;\n timers: WeakMap<HTMLElement, number>;\n duration: number;\n configs: WeakMap<HTMLElement, ElementConfig[]>;\n // Elements last seen without a layout box. A 0x0 target reports ratio 1, so\n // growing one into view never changes the threshold index and the observer\n // stays silent; the ResizeObserver re-observes to force a fresh entry.\n zeroArea: WeakSet<HTMLElement>;\n}\n\n// Module-level visibility state keyed by the owner document. Both an element\n// sub-scope and its document normalize to the same key (see getScopeKey), so\n// there is exactly one IntersectionObserver per document: create-key and\n// lookup-key can never diverge, and iframes still get their own observer.\nconst visibilityStates = new WeakMap<Document | Element, VisibilityState>();\n\n/**\n * Normalize any scope (document or element) to its owner document. The\n * IntersectionObserver root is always the viewport, so per-sub-scope observers\n * would be meaningless; keying by ownerDocument gives one shared observer per\n * document and keeps the create/lookup keys aligned.\n */\nexport function getScopeKey(scope: InitScope): Document | Element {\n return (scope as Element).ownerDocument || (scope as Document);\n}\n\nfunction createObserver(\n scope: Document | Element,\n): IntersectionObserver | undefined {\n const doc = (scope as Element).ownerDocument || (scope as Document);\n const win = doc.defaultView;\n if (!win || !win.IntersectionObserver) return undefined;\n\n return tryCatch(\n () =>\n new win.IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => handleIntersection(scope, entry));\n },\n { rootMargin: '0px', threshold: THRESHOLDS },\n ),\n () => undefined,\n )();\n}\n\nfunction handleIntersection(\n scope: Document | Element,\n entry: IntersectionObserverEntry,\n): void {\n const target = entry.target as HTMLElement;\n const state = visibilityStates.get(getScopeKey(scope));\n if (!state) return;\n\n const win = target.ownerDocument.defaultView;\n if (!win) return;\n\n const configs = state.configs.get(target);\n // An entry can be queued before an unobserve lands. Never arm a dwell for an\n // element the module has already released.\n if (!configs || !configs.length) return;\n\n const existingTimer = state.timers.get(target);\n\n if (isEligible(entry, win)) {\n // Every trigger on this element has fired and is waiting to re-enter.\n if (configs.every((config) => config.blocked)) return;\n\n if (!existingTimer) {\n const timer = win.setTimeout(() => {\n fire(getScopeKey(scope), target);\n }, state.duration);\n state.timers.set(target, timer);\n }\n return;\n }\n\n // Below the bar: cancel the armed dwell and re-arm the repeating triggers.\n if (existingTimer) {\n clearTimeout(existingTimer);\n state.timers.delete(target);\n }\n configs.forEach((config) => {\n if (config.multiple) config.blocked = false;\n });\n}\n\nasync function fire(\n scope: Document | Element,\n target: HTMLElement,\n): Promise<void> {\n const state = visibilityStates.get(scope);\n if (!state) return;\n\n // Release the timer on EVERY path, before anything can bail. Leaving a fired\n // timer id behind makes `!existingTimer` false forever, which permanently\n // prevents the element from arming again: it is dead for the page session.\n state.timers.delete(target);\n\n const doc = target.ownerDocument;\n const win = doc.defaultView;\n if (!win) return;\n\n // Never count an impression the user could not possibly have seen. Re-arm\n // rather than drop: a page opened in a background tab has every above-the-fold\n // element already eligible, and its geometry will never change again, so the\n // observer would queue no further entry and the impression would be lost for\n // the whole session even after the user foregrounds the tab and reads the\n // page. Background timers are throttled to roughly one per second, so the\n // retry is cheap, and it stops as soon as the tab is visible.\n if (doc.visibilityState !== 'visible') {\n state.timers.set(\n target,\n win.setTimeout(() => {\n fire(scope, target);\n }, state.duration),\n );\n return;\n }\n\n // The one and only occlusion check, uncached, on one element.\n if (!isVisible(target, win, doc)) return;\n\n const configs = state.configs.get(target);\n if (!configs) return;\n\n const pending = configs.filter((config) => !config.blocked);\n if (!pending.length) return;\n\n // Commit state BEFORE awaiting. handleTrigger can outlast the dwell on a slow\n // destination, and a late write would clobber state that the not-eligible\n // branch has since reset, swallowing the element's next event.\n pending.forEach((config) => {\n if (config.multiple) config.blocked = true;\n });\n\n const remaining = configs.filter((config) => config.multiple);\n if (remaining.length) {\n state.configs.set(target, remaining);\n } else {\n // Single-shot impressions are done. Unobserve before the await so a second\n // dwell cannot arm and double-fire while the destination is in flight.\n unobserveElement(scope, target);\n }\n\n await Promise.all(\n pending.map((config) =>\n handleTrigger(config.context, target, config.trigger),\n ),\n );\n}\n\nexport function unobserveElement(scope: InitScope, element: HTMLElement): void {\n const state = visibilityStates.get(getScopeKey(scope));\n if (!state) return;\n\n state.observer?.unobserve(element);\n state.resizeObserver?.unobserve(element);\n state.configs.delete(element);\n state.zeroArea.delete(element);\n\n const timer = state.timers.get(element);\n if (timer) {\n clearTimeout(timer);\n state.timers.delete(element);\n }\n}\n\n/**\n * Rescue the one case an IntersectionObserver cannot report.\n *\n * A zero-area target reports intersectionRatio 1 while it intersects (spec\n * 2.2.12). When a framework mounts an element empty and renders it a frame\n * later, the ratio goes 1 -> 1, the threshold index never changes, and the\n * observer queues NO entry - at any threshold density. Re-observing resets the\n * observer's per-target bookkeeping (previousThresholdIndex back to -1), which\n * forces a fresh entry on the next frame.\n *\n * Gated on the zero -> laid-out transition, so a resizing animation cannot turn\n * this into a per-frame re-observe storm.\n */\nfunction createResizeObserver(\n scope: Document | Element,\n): ResizeObserver | undefined {\n const doc = (scope as Element).ownerDocument || (scope as Document);\n const win = doc.defaultView;\n if (!win || !win.ResizeObserver) return undefined;\n\n return tryCatch(\n () =>\n new win.ResizeObserver((entries) => {\n const state = visibilityStates.get(scope);\n if (!state?.observer) return;\n\n entries.forEach((entry) => {\n const target = entry.target as HTMLElement;\n const { width, height } = entry.contentRect;\n const isZero = width <= 0 || height <= 0;\n\n if (isZero) {\n state.zeroArea.add(target);\n return;\n }\n\n if (!state.zeroArea.has(target)) return;\n state.zeroArea.delete(target);\n\n state.observer?.unobserve(target);\n state.observer?.observe(target);\n });\n }),\n () => undefined,\n )();\n}\n\nexport function initVisibilityTracking(\n scope: InitScope,\n duration = 1000,\n): void {\n const key = getScopeKey(scope);\n if (visibilityStates.has(key)) return;\n\n visibilityStates.set(key, {\n observer: createObserver(key),\n resizeObserver: createResizeObserver(key),\n timers: new WeakMap(),\n duration,\n configs: new WeakMap(),\n zeroArea: new WeakSet(),\n });\n}\n\nexport function triggerVisible(\n context: Context,\n element: HTMLElement,\n config: { multiple?: boolean } = { multiple: false },\n): void {\n const scope = context.settings.scope;\n if (!scope) return;\n const state = visibilityStates.get(getScopeKey(scope));\n if (!state?.observer || !element) return;\n\n const multiple = config.multiple ?? false;\n const configs = state.configs.get(element) ?? [];\n configs.push({\n multiple,\n blocked: false,\n context,\n trigger: multiple ? Triggers.Visible : Triggers.Impression,\n });\n state.configs.set(element, configs);\n\n // One element can carry both `impression` and `visible`; observe it once.\n if (configs.length > 1) return;\n\n const box = element.getBoundingClientRect();\n if (box.width <= 0 || box.height <= 0) state.zeroArea.add(element);\n\n state.observer.observe(element);\n state.resizeObserver?.observe(element);\n}\n\nexport function destroyVisibilityTracking(scope?: InitScope): void {\n if (!scope) return;\n const key = getScopeKey(scope);\n const state = visibilityStates.get(key);\n if (!state) return;\n\n state.observer?.disconnect();\n state.resizeObserver?.disconnect();\n visibilityStates.delete(key);\n}\n","import type { WalkerOS, Elb, Collector } from '@walkeros/core';\nimport { isString, isObject, isElementOrDocument } from '@walkeros/core';\nimport { createPushResult } from '@walkeros/collector';\nimport type {\n BrowserPushData,\n BrowserPushOptions,\n BrowserPushContext,\n} from './types/elb';\nimport type { Context, Settings, InitScope, Scope } from './types';\nimport { getEntities, getGlobals } from './walker';\n\n/**\n * Translation layer that converts flexible browser source inputs\n * to the strict core collector format\n */\nexport function translateToCoreCollector(\n context: Context,\n eventOrCommand: unknown,\n data?: BrowserPushData,\n options?: BrowserPushOptions,\n pushContext?: BrowserPushContext,\n nested?: WalkerOS.Entities,\n custom?: WalkerOS.Properties,\n): Promise<Elb.PushResult> {\n const { elb, settings } = context;\n\n // Handle walker commands. `walker init` is browser-only (re-scans a DOM\n // scope for `data-elb*` tags and fires load triggers) and is dispatched\n // here because the collector must stay DOM-free. All other walker\n // commands route through elb to commonHandleCommand on the collector.\n // `initScope` arrives on `context` from the source factory to avoid a\n // static import of `./trigger` (which would form a load-time cycle).\n if (isString(eventOrCommand) && eventOrCommand.startsWith('walker ')) {\n if (eventOrCommand === 'walker init' && context.initScope) {\n const scopes = normalizeInitScopes(data, settings);\n for (const scope of scopes) {\n // Scope is a single carrier: build a scope-aligned context so the\n // trigger pipeline reads the new scope from context.settings.scope\n // everywhere (no diverging positional scope param).\n context.initScope({ ...context, settings: { ...settings, scope } });\n }\n return Promise.resolve(createPushResult({ ok: true }));\n }\n return elb(eventOrCommand, data as WalkerOS.Properties);\n }\n\n // Handle event objects - add source and globals if missing\n if (isObject(eventOrCommand)) {\n const event = eventOrCommand;\n if (!event.source && settings.scope) {\n const scopeDoc = ((settings.scope as Element).ownerDocument ||\n settings.scope) as Document;\n const scopeWin = scopeDoc.defaultView!;\n event.source = getBrowserSource(scopeWin, scopeDoc);\n }\n\n // Add globals if not already present\n if (!event.globals && settings.scope) {\n event.globals = isGlobalsScope(settings.scope)\n ? getGlobals(settings.prefix, settings.scope)\n : {};\n }\n\n return elb(event);\n }\n\n // Extract entity name from event string\n const [entity] = String(\n isObject(eventOrCommand) ? eventOrCommand.name : eventOrCommand,\n ).split(' ');\n\n // Get data and context either from elements or parameters\n let eventData = isObject(data) ? (data as WalkerOS.Properties) : {};\n let eventContext: WalkerOS.OrderedProperties = {};\n\n let elemParameter: undefined | Element;\n let dataIsElem = false;\n\n // Check if data parameter is an element\n if (isElementOrDocument(data)) {\n elemParameter = data as Element;\n dataIsElem = true;\n }\n\n // Check if contextData parameter is an element\n if (isElementOrDocument(pushContext)) {\n elemParameter = pushContext as Element;\n } else if (isObject(pushContext) && Object.keys(pushContext).length) {\n eventContext = pushContext as WalkerOS.OrderedProperties;\n }\n\n // Extract data from element if provided\n if (elemParameter) {\n const entityObj = getEntities(\n settings.prefix || 'data-elb',\n elemParameter,\n ).find((obj) => obj.entity === entity);\n if (entityObj) {\n if (dataIsElem) eventData = entityObj.data;\n if (entityObj.context) eventContext = entityObj.context;\n }\n }\n\n // Derive win/doc from scope for browser-specific APIs\n const scopeDoc = settings.scope\n ? (((settings.scope as Element).ownerDocument ||\n settings.scope) as Document)\n : undefined;\n const scopeWin = scopeDoc?.defaultView;\n\n // Special handling for page events\n if (entity === 'page' && scopeWin) {\n eventData.id = eventData.id || scopeWin.location.pathname;\n }\n\n // Collect globals from the DOM scope\n const eventGlobals =\n settings.scope && isGlobalsScope(settings.scope)\n ? getGlobals(settings.prefix, settings.scope)\n : {};\n\n // Build unified event from various elb usage patterns\n const event: WalkerOS.DeepPartialEvent = {\n name: String(eventOrCommand || ''),\n data: eventData,\n context: eventContext,\n globals: eventGlobals,\n nested,\n custom,\n trigger: isString(options) ? options : '',\n source:\n scopeWin && scopeDoc ? getBrowserSource(scopeWin, scopeDoc) : undefined,\n };\n\n return elb(event);\n}\n\n/**\n * Create source information for browser events\n */\nfunction getBrowserSource(win: Window, doc: Document): WalkerOS.Source {\n return {\n type: 'browser',\n platform: 'web',\n url: win.location.href,\n referrer: doc.referrer,\n };\n}\n\n/**\n * Local type guard: narrows `unknown` to `Element | Document`. Prefers the\n * native `instanceof` check (works in browsers and JSDOM) and falls back to\n * the WhatWG DOM `nodeType` property for realms where the global Element or\n * Document constructors are not in scope (cross-frame, certain test\n * runners). Returns an accurate `Element | Document` union so callers do\n * not need casts.\n */\nfunction isDomScope(value: unknown): value is InitScope {\n if (!value || typeof value !== 'object') return false;\n if (typeof Element !== 'undefined' && value instanceof Element) return true;\n if (typeof Document !== 'undefined' && value instanceof Document) return true;\n if (typeof ShadowRoot !== 'undefined' && value instanceof ShadowRoot)\n return true;\n if ('nodeType' in value) {\n const nodeType = value.nodeType;\n // 1 = ELEMENT_NODE, 9 = DOCUMENT_NODE, 11 = DOCUMENT_FRAGMENT_NODE (the\n // node type of a ShadowRoot) per the WhatWG DOM standard. Accepting 11\n // lets `walker init` target a retained closed shadow root, which discovery\n // can never reach from the document.\n return nodeType === 1 || nodeType === 9 || nodeType === 11;\n }\n return false;\n}\n\n/**\n * getGlobals accepts the narrow Scope (Document | Element). Scope is kept narrow\n * deliberately: widening it to ShadowRoot would ripple into its sibling scans\n * getPageViewData/getAllEvents, whose bodies call .matches/.body/getAttribute\n * and throw on a ShadowRoot. So a ShadowRoot init scope contributes no globals\n * ({}) by design; this guard narrows the widened carrier back to what getGlobals\n * accepts.\n */\nfunction isGlobalsScope(scope: InitScope): scope is Scope {\n return scope.nodeType !== 11; // exclude ShadowRoot (DOCUMENT_FRAGMENT_NODE)\n}\n\n/**\n * Normalize the `data` argument of `elb('walker init', data)` to an array\n * of Element/Document/ShadowRoot scopes. Falls back to `settings.scope` (or\n * `document` if settings.scope is not a DOM node) when no scope is provided.\n */\nfunction normalizeInitScopes(\n data: unknown,\n settings: Settings,\n): Array<InitScope> {\n if (isDomScope(data)) return [data];\n if (Array.isArray(data)) {\n const filtered: Array<InitScope> = [];\n for (const entry of data) {\n if (isDomScope(entry)) filtered.push(entry);\n }\n return filtered;\n }\n if (typeof data === 'undefined') {\n const fallback = settings.scope;\n if (isDomScope(fallback)) return [fallback];\n if (typeof globalThis.document !== 'undefined') {\n return [globalThis.document];\n }\n }\n return [];\n}\n"],"mappings":";;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAS,mBAAmB;;;ACA5B,SAAS,KAAAA,UAAS;;;ACAlB,SAAS,SAAS;AAMX,IAAM,sBAAsB,EAChC,OAAO,EACP,IAAI,CAAC,EACL,MAAM,qBAAqB,8BAA8B,EACzD,SAAS,4CAA4C;AAMjD,IAAM,oBAAoB,EAC9B,OAAO,EACP,IAAI,CAAC,EACL,MAAM,8BAA8B,uCAAuC,EAC3E,SAAS,0BAA0B;AAM/B,IAAM,gBAAgB,EAC1B,OAAO,EACP,SAAS,2DAA2D,EACpE,SAAS;;;ADlBZ,IAAM,uBAAuBC,GAAE,MAAM;AAAA,EACnCA,GAAE,QAAQ;AAAA,EACVA,GAAE,OAAO;AAAA,EACTA,GAAE,IAAI,EAAE,SAAS,2CAA2C;AAC9D,CAAC;AAKM,IAAM,iBAAiBA,GAAE,OAAO;AAAA,EACrC,QAAQ,oBAAoB,QAAQ,UAAU,EAAE;AAAA,IAC9C;AAAA,EACF;AAAA,EAEA,OAAO,cAAc;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,UAAUA,GACP,QAAQ,EACR,QAAQ,IAAI,EACZ,SAAS,oCAAoC;AAAA,EAEhD,SAASA,GACN,QAAQ,EACR,QAAQ,IAAI,EACZ;AAAA,IACC;AAAA,EACF;AAAA,EAEF,KAAK,kBAAkB,QAAQ,KAAK,EAAE;AAAA,IACpC;AAAA,EACF;AAAA,EAEA,MAAMA,GAAE,OAAO,EAAE,SAAS,iCAAiC,EAAE,SAAS;AAAA,EAEtE,UAAU,qBAAqB,QAAQ,UAAU,EAAE;AAAA,IACjD;AAAA,EACF;AACF,CAAC;;;AElDD,SAAS,KAAAC,UAAS;AAOX,IAAM,eAAeC,GAAE,OAAO;AAAA,EACnC,QAAQ,oBAAoB,QAAQ,UAAU,EAAE;AAAA,IAC9C;AAAA,EACF;AACF,CAAC;;;AHCM,IAAM,WAAW,YAAY,cAAc;AAC3C,IAAM,SAAS,YAAY,YAAY;;;AIb9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA;AAAA;AAAA;AAWA,IAAM,OAAO,MAAM;AAAC;AAGpB,IAAM,kBAAkB,MAAc;AACpC,QAAM,MAAM,MACV,QAAQ,QAAQ;AAAA,IACd,IAAI;AAAA,EACN,CAAC;AACH,SAAO;AACT;AAGA,IAAM,aAA8B;AAAA,EAClC,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO,CAAC,YAA4B;AAClC,UAAM,OAAO,YAAY,WAAW,IAAI,MAAM,OAAO,IAAI;AAAA,EAC3D;AAAA,EACA,MAAM;AAAA,EACN,OAAO,MAAM;AACf;AAKA,IAAM,mBAAmB,OAAO;AAAA,EAC9B,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,UAAU;AAAA,IACR,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,UAAU;AAAA,EACZ;AAAA,EACA,UAAU,CAAC;AAAA,EACX,WAAW;AAAA,IACT,UAAU;AAAA,IACV,WAAW;AAAA,EACb;AAAA,EACA,QAAQ;AAAA,IACN,OAAO;AAAA,IACP,QAAQ;AAAA,EACV;AAAA,EACA,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,aAAa;AAAA,EACb,aAAa;AAAA,EACb,SAAS;AAAA,EACT,SAAS;AACX;AAKA,IAAM,qBAAqB,OAAO;AAAA,EAChC,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,eAAe;AAAA,EACf,kBAAkB,MAAM,CAAC;AAAA,EACzB,gBAAgB;AAAA,EAChB,wBAAwB,MAAM,CAAC;AAAA,EAC/B,sBAAsB,MAAM,CAAC;AAAA,EAC7B,eAAe,OAAO;AAAA,IACpB,cAAc;AAAA,IACd,cAAc;AAAA,IACd,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,EACvB;AAAA,EACA,MAAM;AAAA,IACJ,aAAa;AAAA,IACb,aAAa;AAAA,EACf;AAAA,EACA,iBAAiB;AAAA,IACf,WAAW;AAAA,IACX,YAAY;AAAA,EACd;AAAA,EACA,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,UAAU;AAAA,EACV,QAAQ;AACV;AAQO,IAAM,OAAY;AAAA,EACvB,IAAI,OAAO;AACT,WAAO,gBAAgB;AAAA,EACzB;AAAA,EACA,IAAI,UAAU;AACZ,WAAO,gBAAgB;AAAA,EACzB;AAAA,EACA,IAAI,MAAM;AACR,WAAO,gBAAgB;AAAA,EACzB;AAAA,EACA,IAAI,SAAS;AACX,WAAO,iBAAiB;AAAA,EAC1B;AAAA,EACA,IAAI,WAAW;AACb,WAAO,mBAAmB;AAAA,EAC5B;AAAA,EACA,QAAQ;AACV;;;AC1HA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEO,IAAM,WAA6B;AAAA,EACxC,OAAO;AAAA,EACP,aACE;AAAA,EACF,SAAS;AAAA,IACP,MAAM;AAAA,IACN,SAAS;AAAA,MACP,KAAK;AAAA,MACL,OAAO;AAAA,MACP,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,IAAI;AAAA,EACJ,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,UACJ,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,UAAU;AAAA,UACV,IAAI;AAAA,QACN;AAAA,QACA,SAAS,CAAC;AAAA,QACV,SAAS,CAAC;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,UAAU;AAAA,UACV,KAAK;AAAA,UACL,UAAU;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,aAA+B;AAAA,EAC1C,OAAO;AAAA,EACP,aACE;AAAA,EACF,SAAS,EAAE,MAAM,SAAS,SAAS,SAAS;AAAA,EAC5C,IAAI;AAAA,EACJ,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,MAAM,EAAE,OAAO,UAAU;AAAA,QACzB,SAAS,CAAC;AAAA,QACV,SAAS,CAAC;AAAA,QACV,QAAQ,CAAC;AAAA,QACT,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,UAAU;AAAA,UACV,KAAK;AAAA,UACL,UAAU;AAAA,QACZ;AAAA,QACA,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,cAAgC;AAAA,EAC3C,OAAO;AAAA,EACP,aACE;AAAA,EACF,SAAS,EAAE,MAAM,UAAU,SAAS,OAAO;AAAA,EAC3C,IAAI;AAAA,EACJ,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,MAAM,EAAE,MAAM,UAAU;AAAA,QACxB,SAAS,CAAC;AAAA,QACV,SAAS,CAAC;AAAA,QACV,QAAQ,CAAC;AAAA,QACT,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,UAAU;AAAA,UACV,KAAK;AAAA,UACL,UAAU;AAAA,QACZ;AAAA,QACA,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,kBAAoC;AAAA,EAC/C,OAAO;AAAA,EACP,aACE;AAAA,EACF,SAAS,EAAE,MAAM,cAAc,SAAS,MAAM;AAAA,EAC9C,IAAI;AAAA,EACJ,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,MAAM,EAAE,MAAM,eAAe,UAAU,UAAU;AAAA,QACjD,SAAS,CAAC;AAAA,QACV,SAAS,CAAC;AAAA,QACV,QAAQ,CAAC;AAAA,QACT,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,UAAU;AAAA,UACV,KAAK;AAAA,UACL,UAAU;AAAA,QACZ;AAAA,QACA,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,iBAAmC;AAAA,EAC9C,OAAO;AAAA,EACP,aACE;AAAA,EACF,SAAS,EAAE,MAAM,OAAO;AAAA,EACxB,IAAI;AAAA,EACJ,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,UACJ,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,UAAU;AAAA,UACV,IAAI;AAAA,QACN;AAAA,QACA,SAAS,CAAC;AAAA,QACV,SAAS,CAAC;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,UAAU;AAAA,UACV,KAAK;AAAA,UACL,UAAU;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,MAAM,EAAE,IAAI,UAAU,MAAM,WAAW;AAAA,QACvC,SAAS,CAAC;AAAA,QACV,SAAS,CAAC;AAAA,QACV,QAAQ;AAAA,UACN;AAAA,YACE,QAAQ;AAAA,YACR,MAAM,EAAE,UAAU,SAAS,SAAS,KAAK;AAAA,YACzC,SAAS,CAAC;AAAA,YACV,QAAQ,CAAC;AAAA,UACX;AAAA,QACF;AAAA,QACA,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,UAAU;AAAA,UACV,KAAK;AAAA,UACL,UAAU;AAAA,QACZ;AAAA,QACA,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,qBAAuC;AAAA,EAClD,OAAO;AAAA,EACP,aACE;AAAA,EACF,SAAS,EAAE,MAAM,SAAS,SAAS,MAAM;AAAA,EACzC,IAAI;AAAA,EACJ,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,MAAM,EAAE,OAAO,OAAO,WAAW,MAAM,QAAQ,CAAC,OAAO,MAAM,EAAE;AAAA,QAC/D,SAAS,CAAC;AAAA,QACV,SAAS,CAAC;AAAA,QACV,QAAQ,CAAC;AAAA,QACT,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,UAAU;AAAA,UACV,KAAK;AAAA,UACL,UAAU;AAAA,QACZ;AAAA,QACA,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,oBAAsC;AAAA,EACjD,OAAO;AAAA,EACP,aACE;AAAA,EACF,SAAS,EAAE,MAAM,SAAS,SAAS,mBAAmB;AAAA,EACtD,IAAI;AAAA,EACJ,KAAK;AAAA,IACH;AAAA,MACE;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,MAAM,EAAE,OAAO,UAAU;AAAA,QACzB,SAAS,EAAE,MAAM,CAAC,mBAAmB,CAAC,EAAE;AAAA,QACxC,SAAS,EAAE,UAAU,MAAM,MAAM,UAAU;AAAA,QAC3C,QAAQ,CAAC;AAAA,QACT,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,UAAU;AAAA,UACV,KAAK;AAAA,UACL,UAAU;AAAA,QACZ;AAAA,QACA,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;;;ACnPA,SAAS,iBAAiB;;;ACE1B,SAAS,UAAU,YAAAC,iBAAgB;AACnC,SAAS,SAAAC,cAAsB;AAC/B,SAAS,OAAO,QAAQ,gBAAAC,qBAAoB;;;ACF5C,SAAS,QAAQ,WAAW,SAAS,YAAY;AACjD,SAAS,aAAa;AACtB,SAAS,oBAAoB;AAEtB,SAAS,oBACd,QACA,MACA,aAAa,MACL;AAER,QAAM,YAAY,aAAa,MAAM;AACrC,SAAO,QAAQ,SAAY,YAAY,OAAO;AAC9C,SAAO,SAAS;AAClB;AAEO,SAAS,aACd,QACA,SACA,MACA,aAAa,MACQ;AACrB,QAAM,iBACJ,aAAa,SAAS,oBAAoB,QAAQ,MAAM,UAAU,CAAC,KAAK;AAE1E,QAAM,YAAY,eAAe,cAAc,EAAE,OAAO,CAAC,QAAQ,QAAQ;AACvE,QAAI,CAAC,KAAK,GAAG,IAAmB,YAAY,GAAG;AAE/C,QAAI,CAAC,IAAK,QAAO;AAGjB,QAAI,CAAC,KAAK;AAER,UAAI,IAAI,SAAS,GAAG,EAAG,OAAM,IAAI,MAAM,GAAG,EAAE;AAC5C,YAAM;AAAA,IACR;AAGA,QAAI,IAAI,WAAW,GAAG,GAAG;AACvB,YAAM,IAAI,MAAM,CAAC;AACjB,UAAI;AAEF,YAAI,eAAgB,QAAoB,GAAoB;AAC5D,YAAI,CAAC,gBAAgB,QAAQ,YAAY;AAEvC,yBAAgB,QAA8B,QAC3C,QAA8B,aACjC,EAAE;AAAA,QACJ;AAEA,cAAM,OAAO,YAAY;AAAA,MAC3B,SAAS,OAAO;AACd,cAAM;AAAA,MACR;AAAA,IACF;AAEA,QAAI,IAAI,SAAS,IAAI,GAAG;AACtB,YAAM,IAAI,MAAM,GAAG,EAAE;AACrB,UAAI,CAAC,QAAQ,OAAO,GAAG,CAAC,EAAG,QAAO,GAAG,IAAI,CAAC;AAC1C,MAAC,OAAO,GAAG,EAA8B,KAAK,UAAU,GAAG,CAAC;AAAA,IAC9D,OAAO;AACL,aAAO,GAAG,IAAI,UAAU,GAAG;AAAA,IAC7B;AAEA,WAAO;AAAA,EACT,GAAG,CAAC,CAAwB;AAE5B,SAAO;AACT;AAmCO,SAAS,UACd,QACAC,UACA,SAAiB,MAAM,SAAS,QACjB;AACf,QAAM,SAAwB,CAAC;AAG/B,QAAM,EAAE,SAAS,YAAY,IAAI,kBAAkB,QAAQ,QAAQA,QAAO;AAG1E,MAAI,CAAC,QAAQ,OAAQ,QAAO;AAE5B,UAAQ,QAAQ,CAAC,kBAAkB;AACjC,UAAM,SAAS,eAAe,cAAc,gBAAgB,IAAI,GAAG,EAAE;AAAA,MACnE,CAACC,SAAQ,UAAU;AACjB,QAAAA,QAAO,KAAK,KAAK,CAAC,IAAI;AACtB,eAAOA;AAAA,MACT;AAAA,MACA,CAAC;AAAA,IACH;AAGA,UAAM,WAAW,YAAY,QAAQ,QAAQ,QAAQ,WAAW;AAGhE,QAAI,CAAC,SAAS,QAAQ;AACpB,YAAM,SAAS;AAEf,YAAM,iBAAiB,IAAI,oBAAoB,QAAQ,MAAM,CAAC;AAG9D,YAAM,CAAC,MAAM,OAAO,IAAI;AAAA,QACtB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,eAAS,KAAK;AAAA,QACZ;AAAA;AAAA,QACA;AAAA;AAAA,QACA,QAAQ,CAAC;AAAA;AAAA,QACT;AAAA,MACF,CAAC;AAAA,IACH;AAGA,aAAS,QAAQ,CAAC,WAAW;AAzJjC;AA0JM,aAAO,KAAK;AAAA,QACV,QAAQ,OAAO;AAAA,QACf,QAAQ,cAAc;AAAA,QACtB,MAAM,OAAO;AAAA,QACb,SAAAD;AAAA,QACA,SAAS,OAAO;AAAA,QAChB,SAAQ,YAAO,WAAP,YAAiB,CAAC;AAAA,MAC5B,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AACT;AAEO,SAAS,WACd,SAAiB,MAAM,SAAS,QAChC,OACqB;AACrB,MAAI,CAAC,MAAO,QAAO,CAAC;AACpB,QAAM,cAAc;AAAA,IAClB;AAAA,IACA,MAAM,SAAS;AAAA,IACf;AAAA,EACF;AACA,QAAM,iBAAiB,IAAI,WAAW;AACtC,MAAI,SAAS,CAAC;AAEd,WAAS,OAAO,gBAAgB,CAAC,YAAY;AAC3C,aAAS;AAAA,MACP;AAAA,MACA,aAAa,QAAQ,SAAS,MAAM,SAAS,SAAS,KAAK;AAAA,IAC7D;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAiDO,SAAS,kBAAkB,KAA0C;AAC1E,QAAM,SAAsC,CAAC;AAE7C,QAAM,aAAa,eAAe,GAAG;AAErC,aAAW,QAAQ,CAACE,SAAQ;AAC1B,UAAM,CAAC,aAAa,UAAU,IAAI,YAAYA,IAAG;AACjD,UAAM,CAACC,UAAS,aAAa,IAAI,eAAe,WAAW;AAE3D,QAAI,CAACA,SAAS;AAEd,QAAI,CAAC,QAAQ,YAAY,IAAI,eAAe,cAAc,EAAE;AAG5D,aAAS,UAAUA;AAEnB,QAAI,CAAC,OAAOA,QAAO,EAAG,QAAOA,QAAO,IAAI,CAAC;AAEzC,WAAOA,QAAO,EAAE,KAAK,EAAE,SAAAA,UAAS,eAAe,QAAQ,aAAa,CAAC;AAAA,EACvE,CAAC;AAED,SAAO;AACT;AAEO,SAAS,YACd,QACA,QACA,QACA,cAAc,OACK;AACnB,QAAM,WAA8B,CAAC;AACrC,MAAI,UAAU;AAGd,WAAS,OAAO,KAAK,UAAU,CAAC,CAAC,EAAE,WAAW,IAAI,SAAS;AAE3D,SAAO,SAAS;AACd,UAAM,SAAS,UAAU,QAAQ,SAAS,QAAQ,MAAM;AACxD,QAAI,QAAQ;AACV,eAAS,KAAK,MAAM;AACpB,UAAI,YAAa;AAAA,IACnB;AAEA,cAAU,UAAU,QAAQ,OAAO;AAAA,EACrC;AAEA,SAAO;AACT;AAEA,SAAS,UACP,QACA,SACA,QACA,QACwB;AACxB,QAAM,SAAS,aAAa,SAAS,oBAAoB,MAAM,CAAC;AAGhE,MAAI,CAAC,UAAW,UAAU,CAAC,OAAO,MAAM,EAAI,QAAO;AAEnD,QAAM,aAAa,CAAC,OAAO;AAC3B,QAAM,eAAe,IAAI;AAAA,IACvB;AAAA,IACA;AAAA,EACF,CAAC,MAAM,oBAAoB,QAAQ,EAAE,CAAC;AACtC,QAAM,WAAW,oBAAoB,QAAQ,MAAM,SAAS,MAAM,KAAK;AAEvE,MAAI,OAA4B,CAAC;AACjC,QAAM,SAA4B,CAAC;AACnC,QAAM,CAAC,YAAY,OAAO,IAAI;AAAA,IAC5B,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,EACF;AAGA,mBAAiB,SAAS,IAAI,QAAQ,KAAK,CAAC,SAAS;AACnD,UAAM,CAAC,QAAQ,SAAS,IAAmB;AAAA,MACzC,aAAa,MAAM,QAAQ;AAAA,IAC7B;AAKA,QAAI,cAAc;AAChB;AAAA,QACE,QAAQ,cAAc;AAAA,QACtB,IAAI,QAAQ,KAAK,MAAM;AAAA,QACvB,CAAC,aAAa;AACZ,qBAAW,KAAK,QAAQ;AAGxB,gBAAM,eAAe,UAAU,QAAQ,QAAQ;AAC/C,cAAI,aAAc,QAAO,KAAK,YAAY;AAAA,QAC5C;AAAA,MACF;AAAA,EACJ,CAAC;AAGD,QAAM,gBAAgC,CAAC;AACvC,aAAW,QAAQ,CAAC,SAAS;AAE3B,QAAI,KAAK,QAAQ,YAAY,EAAG,eAAc,KAAK,IAAI;AAEvD,qBAAiB,MAAM,cAAc,CAACC,UAAS,cAAc,KAAKA,KAAI,CAAC;AAAA,EACzE,CAAC;AAGD,MAAI,cAAmC,CAAC;AACxC,gBAAc,QAAQ,CAAC,UAAU;AAE/B,kBAAc,OAAO,aAAa,aAAa,QAAQ,OAAO,EAAE,CAAC;AACjE,WAAO,OAAO,MAAM,aAAa,QAAQ,OAAO,MAAM,CAAC;AAAA,EACzD,CAAC;AAGD,SAAO,OAAO,OAAO,aAAa,IAAI,GAAG,UAAU;AAGnD,aAAW,QAAQ,CAAC,SAAS;AAC3B;AAAA,MACE;AAAA,MACA,IAAI,oBAAoB,MAAM,CAAC;AAAA,MAC/B,CAAC,wBAAwB;AACvB,cAAM,eAAe,UAAU,QAAQ,mBAAmB;AAC1D,YAAI,aAAc,QAAO,KAAK,YAAY;AAAA,MAC5C;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,EAAE,QAAQ,MAAM,SAAS,OAAO;AACzC;AAEA,SAAS,UAAU,QAAgB,MAAuC;AACxE,QAAM,WAAW,oBAAoB,QAAQ,MAAM,SAAS,MAAM,KAAK;AAGvE,MAAI,KAAK,QAAQ,IAAI,QAAQ,GAAG,GAAG;AACjC,UAAM,CAAC,QAAQ,SAAS,IAAmB;AAAA,MACzC,aAAa,MAAM,QAAQ;AAAA,IAC7B;AACA,QAAI,cAAc,SAAS;AAIzB,YAAM,MAAM,KAAK;AACjB,UAAI,QAA4B;AAChC,eAAS,KAAK,IAAI,QAAQ,KAAK,MAAM,aAAa,CAAC,OAAO;AACxD,YAAI,CAAC,MAAO,SAAQ;AAAA,MACtB,CAAC;AACD,aAAO;AAAA,IACT;AAAA,EACF;AAGA,QAAM,MAAM,KAAK,cAAc;AAC/B,MACE,CAAC,KAAK,iBACN,KAAK,eACL,KAAK,YAAY,aAAa,IAAI,YAClC;AACA,WAAQ,KAAK,YAAY,EAAiB;AAAA,EAC5C;AAEA,SAAO,KAAK;AACd;AAEA,SAAS,2BACP,SACA,gBACA,QACA,MACA,gBAAgB,OACkD;AAClE,MAAI,OAA4B,CAAC;AACjC,QAAM,UAAsC,CAAC;AAC7C,MAAI,SAAS;AACb,QAAM,kBAAkB,IAAI;AAAA,IAC1B;AAAA,IACA,MAAM,SAAS;AAAA,IACf;AAAA,EACF,CAAC;AACD,QAAM,aAAa,MAAM,SAAS;AAClC,QAAM,iBAAiB,IAAI,oBAAoB,QAAQ,YAAY,KAAK,CAAC;AAGzE,MAAI,WAAW;AACf,SAAO,QAAQ;AAKb,QAAI,iBAAiB,cAAc,OAAO,QAAQ,cAAc,GAAG;AACjE,aAAO,OAAO,aAAa,QAAQ,QAAQ,YAAY,KAAK,GAAG,IAAI;AAAA,IACrE;AAGA,QAAI,OAAO,QAAQ,cAAc,GAAG;AAElC,aAAO,OAAO,aAAa,QAAQ,QAAQ,EAAE,GAAG,IAAI;AACpD,aAAO,OAAO,aAAa,QAAQ,QAAQ,IAAI,GAAG,IAAI;AAAA,IACxD;AAGA,QAAI,OAAO,QAAQ,eAAe,GAAG;AACnC,aAAO;AAAA,QACL,aAAa,QAAQ,QAAQ,MAAM,SAAS,SAAS,KAAK;AAAA,MAC5D,EAAE,QAAQ,CAAC,CAAC,KAAK,GAAG,MAAM;AAExB,YAAI,OAAO,CAAC,QAAQ,GAAG,EAAG,SAAQ,GAAG,IAAI,CAAC,KAAK,QAAQ;AAAA,MACzD,CAAC;AAGD,QAAE;AAAA,IACJ;AAEA,aAAS,UAAU,QAAQ,MAAM;AAAA,EACnC;AAEA,SAAO,CAAC,MAAM,OAAO;AACvB;AAEA,SAAS,SACP,OACA,UACA,IACM;AACN,QAAM,iBAAiB,QAAQ,EAAE,QAAQ,EAAE;AAC7C;AAKO,SAAS,iBACd,OACA,UACA,IACM;AACN,QAAM,iBAAiB,QAAQ,EAAE,QAAQ,EAAE;AAE3C,QAAM,MAAO,MAAkB,iBAAkB;AACjD,QAAM,MAAM,IAAI;AAChB,MAAI,iBAAiB,IAAI,WAAW,MAAM,YAAY;AACpD,qBAAiB,MAAM,YAAY,UAAU,EAAE;AAAA,EACjD;AACA,QAAM,iBAAiB,GAAG,EAAE,QAAQ,CAAC,OAAO;AAC1C,QAAI,GAAG,YAAY;AACjB,uBAAiB,GAAG,YAAY,UAAU,EAAE;AAAA,IAC9C;AAAA,EACF,CAAC;AACH;AAEA,SAAS,kBACP,QACA,QACAD,UAC0D;AAC1D,MAAI,UAAU;AAEd,SAAO,SAAS;AAEd,UAAM,iBAAiB;AAAA,MACrB;AAAA,MACA,oBAAoB,QAAQ,MAAM,SAAS,SAAS,KAAK;AAAA,IAC3D;AAEA,QAAI,gBAAgB;AAClB,YAAM,iBAAiB,kBAAkB,cAAc;AACvD,UAAI,eAAeA,QAAO,GAAG;AAC3B,eAAO,EAAE,SAAS,eAAeA,QAAO,GAAG,aAAa,MAAM;AAAA,MAChE;AAAA,IACF;AAGA,UAAM,kBAAkB;AAAA,MACtB;AAAA,MACA,oBAAoB,QAAQ,MAAM,SAAS,QAAQ,KAAK;AAAA,IAC1D;AAEA,QAAI,iBAAiB;AACnB,YAAM,iBAAiB,kBAAkB,eAAe;AAExD,UAAI,eAAeA,QAAO,KAAKA,aAAY,SAAS;AAClD,eAAO,EAAE,SAAS,eAAeA,QAAO,KAAK,CAAC,GAAG,aAAa,KAAK;AAAA,MACrE;AAAA,IACF;AAEA,cAAU,UAAU,QAAQ,OAAO;AAAA,EACrC;AAEA,SAAO,EAAE,SAAS,CAAC,GAAG,aAAa,MAAM;AAC3C;AAEA,SAAS,eAAe,KAAa,YAAY,KAAwB;AACvE,QAAM,SAA4B,CAAC;AAEnC,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,MAAM,IAAI,OAAO,QAAQ,SAAS,iBAAiB,IAAI;AAC7D,SAAO,IAAI,MAAM,GAAG,KAAK,CAAC;AAC5B;AAEA,SAAS,YAAY,KAA4B;AAC/C,QAAM,CAAC,KAAK,KAAK,IAAI,IAAI,MAAM,SAAS,CAAC;AACzC,SAAO,CAAC,KAAK,GAAG,GAAG,KAAK,KAAK,CAAC;AAChC;AAEA,SAAS,eAAe,KAA4B;AAElD,QAAM,CAAC,KAAK,KAAK,IAAI,IAAI,MAAM,KAAK,CAAC;AACrC,QAAM,QAAQ,QAAQ,MAAM,MAAM,GAAG,EAAE,IAAI;AAG3C,SAAO,CAAC,KAAK,KAAK;AACpB;;;ACziBA,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAoE1B,IAAM,aAAa,MAAM,KAAK,EAAE,QAAQ,IAAI,GAAG,CAAC,GAAG,UAAU,QAAQ,GAAG;;;ACrExE,SAAS,UAAU,UAAU,2BAA2B;AACxD,SAAS,wBAAwB;AAa1B,SAAS,yBACd,SACA,gBACA,MACA,SACA,aACA,QACA,QACyB;AACzB,QAAM,EAAE,KAAK,UAAAE,UAAS,IAAI;AAQ1B,MAAI,SAAS,cAAc,KAAK,eAAe,WAAW,SAAS,GAAG;AACpE,QAAI,mBAAmB,iBAAiB,QAAQ,WAAW;AACzD,YAAM,SAAS,oBAAoB,MAAMA,SAAQ;AACjD,iBAAW,SAAS,QAAQ;AAI1B,gBAAQ,UAAU,EAAE,GAAG,SAAS,UAAU,EAAE,GAAGA,WAAU,MAAM,EAAE,CAAC;AAAA,MACpE;AACA,aAAO,QAAQ,QAAQ,iBAAiB,EAAE,IAAI,KAAK,CAAC,CAAC;AAAA,IACvD;AACA,WAAO,IAAI,gBAAgB,IAA2B;AAAA,EACxD;AAGA,MAAI,SAAS,cAAc,GAAG;AAC5B,UAAMC,SAAQ;AACd,QAAI,CAACA,OAAM,UAAUD,UAAS,OAAO;AACnC,YAAME,YAAaF,UAAS,MAAkB,iBAC5CA,UAAS;AACX,YAAMG,YAAWD,UAAS;AAC1B,MAAAD,OAAM,SAAS,iBAAiBE,WAAUD,SAAQ;AAAA,IACpD;AAGA,QAAI,CAACD,OAAM,WAAWD,UAAS,OAAO;AACpC,MAAAC,OAAM,UAAU,eAAeD,UAAS,KAAK,IACzC,WAAWA,UAAS,QAAQA,UAAS,KAAK,IAC1C,CAAC;AAAA,IACP;AAEA,WAAO,IAAIC,MAAK;AAAA,EAClB;AAGA,QAAM,CAAC,MAAM,IAAI;AAAA,IACf,SAAS,cAAc,IAAI,eAAe,OAAO;AAAA,EACnD,EAAE,MAAM,GAAG;AAGX,MAAI,YAAY,SAAS,IAAI,IAAK,OAA+B,CAAC;AAClE,MAAI,eAA2C,CAAC;AAEhD,MAAI;AACJ,MAAI,aAAa;AAGjB,MAAI,oBAAoB,IAAI,GAAG;AAC7B,oBAAgB;AAChB,iBAAa;AAAA,EACf;AAGA,MAAI,oBAAoB,WAAW,GAAG;AACpC,oBAAgB;AAAA,EAClB,WAAW,SAAS,WAAW,KAAK,OAAO,KAAK,WAAW,EAAE,QAAQ;AACnE,mBAAe;AAAA,EACjB;AAGA,MAAI,eAAe;AACjB,UAAM,YAAY;AAAA,MAChBD,UAAS,UAAU;AAAA,MACnB;AAAA,IACF,EAAE,KAAK,CAAC,QAAQ,IAAI,WAAW,MAAM;AACrC,QAAI,WAAW;AACb,UAAI,WAAY,aAAY,UAAU;AACtC,UAAI,UAAU,QAAS,gBAAe,UAAU;AAAA,IAClD;AAAA,EACF;AAGA,QAAM,WAAWA,UAAS,QACnBA,UAAS,MAAkB,iBAC5BA,UAAS,QACX;AACJ,QAAM,WAAW,qCAAU;AAG3B,MAAI,WAAW,UAAU,UAAU;AACjC,cAAU,KAAK,UAAU,MAAM,SAAS,SAAS;AAAA,EACnD;AAGA,QAAM,eACJA,UAAS,SAAS,eAAeA,UAAS,KAAK,IAC3C,WAAWA,UAAS,QAAQA,UAAS,KAAK,IAC1C,CAAC;AAGP,QAAM,QAAmC;AAAA,IACvC,MAAM,OAAO,kBAAkB,EAAE;AAAA,IACjC,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA,SAAS,SAAS,OAAO,IAAI,UAAU;AAAA,IACvC,QACE,YAAY,WAAW,iBAAiB,UAAU,QAAQ,IAAI;AAAA,EAClE;AAEA,SAAO,IAAI,KAAK;AAClB;AAKA,SAAS,iBAAiB,KAAa,KAAgC;AACrE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU;AAAA,IACV,KAAK,IAAI,SAAS;AAAA,IAClB,UAAU,IAAI;AAAA,EAChB;AACF;AAUA,SAAS,WAAW,OAAoC;AACtD,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,MAAI,OAAO,YAAY,eAAe,iBAAiB,QAAS,QAAO;AACvE,MAAI,OAAO,aAAa,eAAe,iBAAiB,SAAU,QAAO;AACzE,MAAI,OAAO,eAAe,eAAe,iBAAiB;AACxD,WAAO;AACT,MAAI,cAAc,OAAO;AACvB,UAAM,WAAW,MAAM;AAKvB,WAAO,aAAa,KAAK,aAAa,KAAK,aAAa;AAAA,EAC1D;AACA,SAAO;AACT;AAUA,SAAS,eAAe,OAAkC;AACxD,SAAO,MAAM,aAAa;AAC5B;AAOA,SAAS,oBACP,MACAA,WACkB;AAClB,MAAI,WAAW,IAAI,EAAG,QAAO,CAAC,IAAI;AAClC,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,UAAM,WAA6B,CAAC;AACpC,eAAW,SAAS,MAAM;AACxB,UAAI,WAAW,KAAK,EAAG,UAAS,KAAK,KAAK;AAAA,IAC5C;AACA,WAAO;AAAA,EACT;AACA,MAAI,OAAO,SAAS,aAAa;AAC/B,UAAM,WAAWA,UAAS;AAC1B,QAAI,WAAW,QAAQ,EAAG,QAAO,CAAC,QAAQ;AAC1C,QAAI,OAAO,WAAW,aAAa,aAAa;AAC9C,aAAO,CAAC,WAAW,QAAQ;AAAA,IAC7B;AAAA,EACF;AACA,SAAO,CAAC;AACV;;;AHyOA,eAAsB,cACpB,SACA,SACAI,UAEoB;AACpB,QAAM,SAAS,UAAU,SAASA,UAAS,QAAQ,SAAS,MAAM;AAClE,SAAO,QAAQ;AAAA,IACb,OAAO;AAAA,MAAI,CAAC,UACV,yBAAyB,SAAS;AAAA,QAChC,MAAM,GAAG,MAAM,MAAM,IAAI,MAAM,MAAM;AAAA,QACrC,GAAG;AAAA,QACH,SAAAA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;;;AD1ZA,IAAM,gBAAgD,OACpD,QACA,YACG;AACH,QAAM,YAAY,mCAA+C,aAAY;AAC7E,MAAI;AACJ,QAAM,MAAM;AACZ,QAAM,MAAM;AAEZ,QAAMC,WACJ,CAAC,MAAe,SAAmB,OAAO,YAAoB;AA5DlE;AA8DM,QAAI,SAAS,UAAU,CAAC,MAAM;AAC5B,YAAM,WACJ,OAAO,SAAS,YAAY,SAAS,OAChC,OACD,CAAC;AACP,UAAI,SAAS,KAAK;AAChB,cAAM,SAAS,IAAI,IAAI,SAAS,GAAG;AACnC,YAAI,QAAQ,aAAa,CAAC,GAAG,IAAI,OAAO,QAAQ;AAAA,MAClD;AACA,UAAI,SAAS,MAAO,KAAI,QAAQ,SAAS;AACzC,UAAI,SAAS,UAAU;AACrB,eAAO,eAAe,KAAK,YAAY;AAAA,UACrC,OAAO,SAAS;AAAA,UAChB,cAAc;AAAA,QAChB,CAAC;AAAA,MACH;AAAA,IACF;AAGA,QAAI,QAAS,KAAI,KAAK,YAAY;AAIlC,QAAI,CAAC,MAAM;AACT,YAAM,SAAS,MAAM,UAAU,EAAE,GAAG,QAAQ,MAAK,YAAO,QAAP,YAAc,KAAK,CAAC;AACrE,aAAO,EAAE,WAAW,OAAO,WAAW,KAAK,OAAO,IAAI;AAAA,IACxD;AAGA,QAAI,CAAC,QAAQ,SAAS,OAAQ;AAG9B,UAAM,WAAW,OAAO,SAAS,WAAW,OAAO;AACnD,UAAM,SAAS,WAAW,IAAI,cAAc,QAAQ,IAAI;AAExD,QAAI,CAAC,QAAQ;AACX,cAAQ,KAAK,4CAA4C,QAAQ,GAAG;AACpE;AAAA,IACF;AAGA,UAAM,SAAS,KAAK,UAAU,QAAQ,QAAQ;AAC9C,QAAI,CAAC,QAAQ;AACX,cAAQ,KAAK,oBAAoB,QAAQ,0BAA0B;AACnE;AAAA,IACF;AAEA,UAAM,UAAmB;AAAA,MACvB,KAAK,KAAK;AAAA,MACV,UAAU,OAAO,OAAO;AAAA,IAC1B;AAGA,UAAM,cAAc,SAAS,QAAQ,IAAI;AAAA,EAC3C;AAEF,SAAO;AAAA,IACL,IAAI,OAAO;AACT,aAAO;AAAA,IACT;AAAA,IACA,SAAAA;AAAA,EACF;AACF;AAMA,IAAM,UAAU,CACd,OACA,QACwB;AACxB,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU;AACzC,QAAM,OAAO;AACb,QAAM,MAAM,IAAI;AAChB,QAAM,MAAM,IAAI;AAEhB,QAAM,YAAY,MAAM;AA3I1B;AA4II,QAAI,CAAC,KAAK,WAAY;AAEtB,UAAM,QAAM,gBAAK,YAAL,mBAAc,MAAM,cAApB,mBAAgC,OAAM;AAClD,UAAM,KAAK,IAAI,cAAc,GAAG;AAEhC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,UAAU,GAAG;AAC1D,SAAG,aAAa,KAAK,KAAK;AAAA,IAC5B;AAEA,QAAI,KAAK,UAAU;AACjB,iBAAW,cAAc,KAAK,UAAU;AACtC,cAAM,QAAQ,IAAI,cAAc,KAAK;AACrC,mBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,UAAU,GAAG;AACrD,gBAAM,aAAa,KAAK,KAAK;AAAA,QAC/B;AACA,WAAG,YAAY,KAAK;AAAA,MACtB;AAAA,IACF;AAEA,QAAI,KAAK,YAAY,EAAE;AACvB,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,CAAC,KAAK,WAAW,KAAK,YAAY;AAEjD,MAAI,QAAQ;AACV,QAAI,KAAK,KAAK;AACZ,YAAM,SAAS,IAAI,IAAI,KAAK,GAAG;AAC/B,UAAI,QAAQ,aAAa,CAAC,GAAG,IAAI,OAAO,QAAQ;AAAA,IAClD;AACA,QAAI,KAAK,OAAO;AACd,UAAI,QAAQ,KAAK;AAAA,IACnB;AACA,QAAI,KAAK,UAAU;AACjB,aAAO,eAAe,KAAK,YAAY,EAAE,OAAO,KAAK,SAAS,CAAC;AAAA,IACjE;AACA,cAAU;AACV;AAAA,EACF;AAGA,SAAO,MAAM;AACX,UAAM,KAAK,UAAU;AACrB,QAAI,CAAC,GAAI;AAET,YAAQ,KAAK,SAAS;AAAA,MACpB,KAAK;AACH,WAAG,cAAc,IAAI,WAAW,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;AAC3D;AAAA,MACF,KAAK;AACH,WAAG,cAAc,IAAI,MAAM,UAAU,EAAE,SAAS,KAAK,CAAC,CAAC;AACvD;AAAA,MACF,KAAK;AACH,WAAG,cAAc,IAAI,WAAW,cAAc,EAAE,SAAS,KAAK,CAAC,CAAC;AAChE;AAAA,MACF,KAAK;AACH,YAAI,cAAc,IAAI,MAAM,QAAQ,CAAC;AACrC;AAAA,IACJ;AAAA,EACF;AACF;","names":["z","z","z","z","tryCatch","Const","getAttribute","trigger","filter","str","trigger","elem","settings","event","scopeDoc","scopeWin","trigger","trigger"]}
1
+ {"version":3,"sources":["../src/schemas/index.ts","../src/schemas/settings.ts","../src/schemas/primitives.ts","../src/schemas/tagger.ts","../src/examples/index.ts","../src/examples/env.ts","../src/examples/step.ts","../src/examples/trigger.ts","../src/trigger.ts","../src/walker.ts","../src/triggerVisible.ts","../src/translation.ts"],"sourcesContent":["import { zodToSchema } from '@walkeros/core/dev';\nimport { SettingsSchema } from './settings';\nimport { TaggerSchema } from './tagger';\n\n// Export primitives\nexport * from './primitives';\n\n// Export Zod schemas and types\nexport { SettingsSchema, type Settings } from './settings';\nexport { TaggerSchema, type TaggerConfig } from './tagger';\n\n// JSON Schema exports (for website PropertyTable and explorer RJSF)\nexport const settings = zodToSchema(SettingsSchema);\nexport const tagger = zodToSchema(TaggerSchema);\n","import { z } from '@walkeros/core/dev';\nimport {\n DataAttributePrefix,\n JavaScriptVarName,\n ScopeSelector,\n} from './primitives';\n\n/**\n * ELB Layer configuration schema\n * Note: Runtime type can be boolean | string | Elb.Layer\n */\nconst ElbLayerConfigSchema = z.union([\n z.boolean(),\n z.string(),\n z.any().describe('Elb.Layer array for async command queuing'),\n]);\n\n/**\n * Browser source settings schema\n */\nexport const SettingsSchema = z.object({\n prefix: DataAttributePrefix.default('data-elb').describe(\n 'Prefix for data attributes (default: data-elb)',\n ),\n\n scope: ScopeSelector.describe(\n 'DOM scope for event tracking (default: document)',\n ),\n\n pageview: z\n .boolean()\n .default(true)\n .describe('Enable automatic pageview tracking'),\n\n capture: z\n .boolean()\n .default(true)\n .describe(\n 'Read click/submit triggers in the capture phase so tagged elements are resolved at click time before app handlers run (set false for the previous bubble-phase behavior)',\n ),\n\n elb: JavaScriptVarName.default('elb').describe(\n 'Name for global elb function',\n ),\n\n name: z.string().describe('Custom name for source instance').optional(),\n\n elbLayer: ElbLayerConfigSchema.default('elbLayer').describe(\n 'Enable elbLayer for async command queuing (boolean, string, or Elb.Layer)',\n ),\n});\n\n// Note: We export the inferred type, but types/index.ts will override\n// specific fields with non-serializable types\nexport type Settings = z.infer<typeof SettingsSchema>;\n","import { z } from '@walkeros/core/dev';\n\n/**\n * Data attribute prefix\n * Used for DOM event tracking\n */\nexport const DataAttributePrefix = z\n .string()\n .min(1)\n .regex(/^[a-z][a-z0-9-]*$/, 'Must be lowercase kebab-case')\n .describe('Prefix for data attributes on DOM elements');\n\n/**\n * JavaScript variable name\n * Used for global function names\n */\nexport const JavaScriptVarName = z\n .string()\n .min(1)\n .regex(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/, 'Must be a valid JavaScript identifier')\n .describe('JavaScript variable name');\n\n/**\n * DOM scope selector\n * Note: Runtime type is Element | Document (non-serializable)\n */\nexport const ScopeSelector = z\n .string()\n .describe('DOM scope for event tracking (CSS selector or \"document\")')\n .optional();\n","import { z } from '@walkeros/core/dev';\nimport { DataAttributePrefix } from './primitives';\n\n/**\n * Tagger configuration schema\n * Used for automatic data attribute generation\n */\nexport const TaggerSchema = z.object({\n prefix: DataAttributePrefix.default('data-elb').describe(\n 'Custom prefix for generated data attributes',\n ),\n});\n\nexport type TaggerConfig = z.infer<typeof TaggerSchema>;\n","export * as env from './env';\nexport * as step from './step';\nexport { createTrigger, trigger } from './trigger';\n","import type { Env } from '../types';\nimport type { Elb, Logger } from '@walkeros/core';\n\n/**\n * Example environment configurations for browser source\n *\n * These environments provide standardized mock structures for testing\n * browser event capture without requiring an actual DOM environment.\n */\n\n// Simple no-op function for mocking\nconst noop = () => {};\n\n// Create a properly typed elb/push/command function that returns a promise with PushResult\nconst createMockElbFn = (): Elb.Fn => {\n const fn = (() =>\n Promise.resolve({\n ok: true,\n })) as Elb.Fn;\n return fn;\n};\n\n// Simple no-op logger for demo purposes\nconst noopLogger: Logger.Instance = {\n error: noop,\n warn: noop,\n info: noop,\n debug: noop,\n throw: (message: string | Error) => {\n throw typeof message === 'string' ? new Error(message) : message;\n },\n json: noop,\n scope: () => noopLogger,\n};\n\n/**\n * Mock window object with common browser APIs\n */\nconst createMockWindow = () => ({\n addEventListener: noop,\n removeEventListener: noop,\n location: {\n href: 'https://example.com/page',\n pathname: '/page',\n search: '?query=test',\n hash: '#section',\n host: 'example.com',\n hostname: 'example.com',\n origin: 'https://example.com',\n protocol: 'https:',\n },\n document: {},\n navigator: {\n language: 'en-US',\n userAgent: 'Mozilla/5.0 (Test)',\n },\n screen: {\n width: 1920,\n height: 1080,\n },\n innerWidth: 1920,\n innerHeight: 1080,\n pageXOffset: 0,\n pageYOffset: 0,\n scrollX: 0,\n scrollY: 0,\n});\n\n/**\n * Mock document object with DOM methods\n */\nconst createMockDocument = () => ({\n addEventListener: noop,\n removeEventListener: noop,\n querySelector: noop,\n querySelectorAll: () => [],\n getElementById: noop,\n getElementsByClassName: () => [],\n getElementsByTagName: () => [],\n createElement: () => ({\n setAttribute: noop,\n getAttribute: noop,\n addEventListener: noop,\n removeEventListener: noop,\n }),\n body: {\n appendChild: noop,\n removeChild: noop,\n },\n documentElement: {\n scrollTop: 0,\n scrollLeft: 0,\n },\n readyState: 'complete',\n title: 'Test Page',\n referrer: '',\n cookie: '',\n});\n\n/**\n * Standard mock environment for testing browser source\n *\n * Use this for testing event capture, DOM interactions, and pageview tracking\n * without requiring a real browser environment.\n */\nexport const push: Env = {\n get push() {\n return createMockElbFn();\n },\n get command() {\n return createMockElbFn();\n },\n get elb() {\n return createMockElbFn();\n },\n get window() {\n return createMockWindow() as unknown as typeof window;\n },\n get document() {\n return createMockDocument() as unknown as typeof document;\n },\n logger: noopLogger,\n};\n","import type { Flow } from '@walkeros/core';\n\nexport const pageView: Flow.StepExample = {\n title: 'Page view',\n description:\n 'A page load trigger captures the current URL, title, and referrer as a walker page view event.',\n trigger: {\n type: 'load',\n options: {\n url: 'https://example.com/docs',\n title: 'Documentation',\n referrer: 'https://example.com/',\n },\n },\n in: '',\n out: [\n [\n 'elb',\n {\n name: 'page view',\n data: {\n domain: 'example.com',\n title: 'Documentation',\n referrer: 'https://example.com/',\n id: '/docs',\n },\n context: {},\n globals: {},\n nested: undefined,\n custom: undefined,\n trigger: 'load',\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/docs',\n referrer: 'https://example.com/',\n },\n },\n ],\n ],\n};\n\nexport const clickEvent: Flow.StepExample = {\n title: 'Click event',\n description:\n 'A button click with walker data attributes is captured as an entity action event with the mapped label data.',\n trigger: { type: 'click', options: 'button' },\n in: '<button data-elb=\"cta\" data-elb-cta=\"label:Sign Up\" data-elbaction=\"click:click\">Sign Up</button>',\n out: [\n [\n 'elb',\n {\n name: 'cta click',\n entity: 'cta',\n action: 'click',\n data: { label: 'Sign Up' },\n context: {},\n globals: {},\n nested: [],\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/',\n referrer: '',\n },\n trigger: 'click',\n },\n ],\n ],\n};\n\nexport const submitEvent: Flow.StepExample = {\n title: 'Submit event',\n description:\n 'A form submit with walker data attributes produces a signup complete event carrying the selected plan.',\n trigger: { type: 'submit', options: 'form' },\n in: '<form data-elb=\"signup\" data-elb-signup=\"plan:premium\" data-elbaction=\"submit:complete\"></form>',\n out: [\n [\n 'elb',\n {\n name: 'signup complete',\n entity: 'signup',\n action: 'complete',\n data: { plan: 'premium' },\n context: {},\n globals: {},\n nested: [],\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/',\n referrer: '',\n },\n trigger: 'submit',\n },\n ],\n ],\n};\n\nexport const impressionEvent: Flow.StepExample = {\n title: 'Impression event',\n description:\n 'An intersection observer impression on a div with walker attributes emits a banner view event.',\n trigger: { type: 'impression', options: 'div' },\n in: '<div data-elb=\"banner\" data-elb-banner=\"type:promotional;position:sidebar\" data-elbaction=\"impression:view\"></div>',\n out: [\n [\n 'elb',\n {\n name: 'banner view',\n entity: 'banner',\n action: 'view',\n data: { type: 'promotional', position: 'sidebar' },\n context: {},\n globals: {},\n nested: [],\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/',\n referrer: '',\n },\n trigger: 'impression',\n },\n ],\n ],\n};\n\nexport const nestedEntities: Flow.StepExample = {\n title: 'Nested entities',\n description:\n 'A page load produces a page view plus a product view whose nested size entity is captured on the product event.',\n trigger: { type: 'load' },\n in: '<div data-elb=\"product\" data-elb-product=\"id:SKU-42;name:Sneakers\" data-elbaction=\"load:view\"><div data-elb=\"size\" data-elb-size=\"selected:large;inStock:true\"></div></div>',\n out: [\n [\n 'elb',\n {\n name: 'page view',\n data: {\n domain: 'example.com',\n title: '',\n referrer: '',\n id: '/',\n },\n context: {},\n globals: {},\n nested: undefined,\n custom: undefined,\n trigger: 'load',\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/',\n referrer: '',\n },\n },\n ],\n [\n 'elb',\n {\n name: 'product view',\n entity: 'product',\n action: 'view',\n data: { id: 'SKU-42', name: 'Sneakers' },\n context: {},\n globals: {},\n nested: [\n {\n entity: 'size',\n data: { selected: 'large', inStock: true },\n context: {},\n nested: [],\n },\n ],\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/',\n referrer: '',\n },\n trigger: 'load',\n },\n ],\n ],\n};\n\nexport const dataAttributeTypes: Flow.StepExample = {\n title: 'Data attribute types',\n description:\n 'Walker data attributes parse scalar, boolean, and array values into typed fields on the emitted event.',\n trigger: { type: 'click', options: 'div' },\n in: '<div data-elb=\"product\" data-elb-product=\"price:99.99;available:true;colors[]:red;colors[]:blue\" data-elbaction=\"click:select\"></div>',\n out: [\n [\n 'elb',\n {\n name: 'product select',\n entity: 'product',\n action: 'select',\n data: { price: 99.99, available: true, colors: ['red', 'blue'] },\n context: {},\n globals: {},\n nested: [],\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/',\n referrer: '',\n },\n trigger: 'click',\n },\n ],\n ],\n};\n\nexport const contextAndGlobals: Flow.StepExample = {\n title: 'Context and globals',\n description:\n 'Walker context and globals attributes attach ambient metadata to events emitted by the browser source.',\n trigger: { type: 'click', options: '[data-elb=\"cta\"]' },\n in: '<div data-elbcontext=\"test:engagement_flow\" data-elbglobals=\"language:en;plan:premium\"><div data-elb=\"cta\" data-elb-cta=\"label:Try Now\" data-elbaction=\"click:signup\">Try Now</div></div>',\n out: [\n [\n 'elb',\n {\n name: 'cta signup',\n entity: 'cta',\n action: 'signup',\n data: { label: 'Try Now' },\n context: { test: ['engagement_flow', 0] },\n globals: { language: 'en', plan: 'premium' },\n nested: [],\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/',\n referrer: '',\n },\n trigger: 'click',\n },\n ],\n ],\n};\n","import type { Trigger, Collector } from '@walkeros/core';\nimport { startFlow } from '@walkeros/collector';\nimport { handleTrigger } from '../trigger';\nimport type { Context } from '../types';\n\ntype BrowserTriggerType =\n | 'load'\n | 'click'\n | 'submit'\n | 'hover'\n | 'scroll'\n | 'impression'\n | 'visible'\n | 'wait'\n | 'pulse';\n\ninterface LoadOptions {\n url?: string;\n title?: string;\n referrer?: string;\n}\n\ninterface BrowserInput {\n trigger: string;\n url?: string;\n title?: string;\n referrer?: string;\n element?: string;\n attributes?: Record<string, string>;\n children?: Record<string, string>[];\n context?: Record<string, unknown>;\n globals?: Record<string, unknown>;\n}\n\n/**\n * Browser source createTrigger.\n *\n * Injects HTML into the DOM, lazily starts the flow, then calls\n * handleTrigger directly — the same convergence point as production.\n * For load triggers, the source's DOM scan during startFlow handles\n * event capture automatically.\n *\n * @example\n * const { trigger } = await createTrigger(config);\n * await trigger('click', 'button')('<button data-elb=\"cta\" data-elbaction=\"click:cta click\">Click</button>');\n *\n * @example\n * // Impression trigger — handleTrigger reads data-elbaction directly\n * await trigger('impression', '[data-elb=\"promo\"]')('<div data-elb=\"promo\" data-elbaction=\"visible:promo seen\">Ad</div>');\n */\nconst createTrigger: Trigger.CreateFn<string, void> = async (\n config: Collector.InitConfig,\n options?: unknown,\n) => {\n const sourceId = (options as { sourceId?: string } | undefined)?.sourceId || 'browser';\n let flow: Trigger.FlowHandle | undefined;\n const doc = document;\n const win = window;\n\n const trigger: Trigger.Fn<string, void> =\n (type?: string, opts?: unknown) => async (content: string) => {\n // 1. Set up environment for load triggers (URL, title, referrer)\n if (type === 'load' || !type) {\n const loadOpts =\n typeof opts === 'object' && opts !== null\n ? (opts as LoadOptions)\n : {};\n if (loadOpts.url) {\n const urlObj = new URL(loadOpts.url);\n win.history.replaceState({}, '', urlObj.pathname);\n }\n if (loadOpts.title) doc.title = loadOpts.title;\n if (loadOpts.referrer) {\n Object.defineProperty(doc, 'referrer', {\n value: loadOpts.referrer,\n configurable: true,\n });\n }\n }\n\n // 2. Inject HTML content into DOM — replaces to create fresh state\n if (content) doc.body.innerHTML = content;\n\n // 3. Lazy startFlow — first call initializes the flow\n // Default run: true so sources scan the DOM and set up listeners\n if (!flow) {\n const result = await startFlow({ ...config, run: config.run ?? true });\n flow = { collector: result.collector, elb: result.elb };\n }\n\n // 4. For load triggers, source already scanned DOM during init — done\n if (!type || type === 'load') return;\n\n // 5. Find target element\n const selector = typeof opts === 'string' ? opts : undefined;\n const target = selector ? doc.querySelector(selector) : null;\n\n if (!target) {\n console.warn(`Trigger: element not found for selector \"${selector}\"`);\n return;\n }\n\n // 6. Build context from live source instance\n const source = flow.collector.sources[sourceId];\n if (!source) {\n console.warn(`Trigger: source \"${sourceId}\" not found in collector`);\n return;\n }\n\n const context: Context = {\n elb: flow.elb,\n settings: source.config.settings as Context['settings'],\n };\n\n // 7. Call handleTrigger directly — same convergence point as production\n await handleTrigger(context, target, type);\n };\n\n return {\n get flow() {\n return flow;\n },\n trigger,\n };\n};\n\n/**\n * Browser source trigger (legacy).\n * Converts step example `in` data into DOM elements and dispatches events.\n */\nconst trigger = (\n input: unknown,\n env: Record<string, unknown>,\n): void | (() => void) => {\n if (!input || typeof input !== 'object') return;\n const data = input as BrowserInput;\n const doc = env.document as Document;\n const win = env.window as Window & typeof globalThis;\n\n const injectDOM = () => {\n if (!data.attributes) return;\n\n const tag = data.element?.match(/^(\\w+)/)?.[1] || 'div';\n const el = doc.createElement(tag);\n\n for (const [key, value] of Object.entries(data.attributes)) {\n el.setAttribute(key, value);\n }\n\n if (data.children) {\n for (const childAttrs of data.children) {\n const child = doc.createElement('div');\n for (const [key, value] of Object.entries(childAttrs)) {\n child.setAttribute(key, value);\n }\n el.appendChild(child);\n }\n }\n\n doc.body.appendChild(el);\n return el;\n };\n\n const isLoad = !data.trigger || data.trigger === 'load';\n\n if (isLoad) {\n if (data.url) {\n const urlObj = new URL(data.url);\n win.history.replaceState({}, '', urlObj.pathname);\n }\n if (data.title) {\n doc.title = data.title;\n }\n if (data.referrer) {\n Object.defineProperty(doc, 'referrer', { value: data.referrer });\n }\n injectDOM();\n return;\n }\n\n // Interactive triggers: inject DOM and dispatch event AFTER source init\n return () => {\n const el = injectDOM();\n if (!el) return;\n\n switch (data.trigger) {\n case 'click':\n el.dispatchEvent(new MouseEvent('click', { bubbles: true }));\n break;\n case 'submit':\n el.dispatchEvent(new Event('submit', { bubbles: true }));\n break;\n case 'hover':\n el.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));\n break;\n case 'scroll':\n win.dispatchEvent(new Event('scroll'));\n break;\n }\n };\n};\n\nexport { createTrigger, trigger };\n","import type { WalkerOS, Collector } from '@walkeros/core';\nimport type { Walker } from '@walkeros/web-core';\nimport type { Scope, InitScope, Settings, Context, ScopeState } from './types';\nimport { throttle, tryCatch } from '@walkeros/core';\nimport { Const, onApply } from '@walkeros/collector';\nimport { elb as elbOrg, getAttribute } from '@walkeros/web-core';\nimport {\n getElbAttributeName,\n getEvents,\n getPageViewData,\n getTriggerActions,\n queryAllComposed,\n} from './walker';\nimport {\n initVisibilityTracking,\n triggerVisible,\n destroyVisibilityTracking,\n unobserveElement,\n} from './triggerVisible';\nimport { translateToCoreCollector } from './translation';\n\n// Module-level state is intentional. The walker.js browser source is\n// single-instance per window: one elb queue, one set of DOM listeners.\n// If multi-instance ever becomes a real requirement (e.g., micro-frontends\n// with isolated walker queues), refactor this into a per-instance closure\n// returned by createSource() and route every trigger function through it.\n//\n// Root click/submit delegation lives on an instance-level controller, aborted\n// only by destroyTriggers (or re-arming initGlobalTrigger). It is deliberately\n// SEPARATE from the per-scope controllers below: a sub-scope re-init must\n// never tear down page-wide click/submit.\nlet rootAbortController: AbortController | undefined;\n\n// Iterable on purpose: destroyTriggers must enumerate every active scope to\n// reach sub-scope intervals/observers (a WeakMap could not back full teardown).\n// Note: this holds strong references to init'd scope nodes (including detached\n// DOM elements). A node init'd via `walker init <el>` and later removed from\n// the DOM without a re-init or source destroy is retained until re-init\n// replaces its entry or destroyTriggers clears the Map. Bounded by the\n// source/page session and accepted per plan §4.\nconst scopeStates = new Map<InitScope, ScopeState>();\n\n// Per-element teardown record. The reaper reads this to release exactly ONE\n// element's resources (its pulse intervals, its wait timeouts, its hover\n// listener, its visibility observation, its scroll entry) without touching the\n// rest of the scope. `scope` is the owning scope key: it selects both the owner\n// bucket (via scopeStates) and the unobserveElement key, so an element removed\n// inside a different scope's subtree is still released from its true owner.\nexport interface ElementRegistration {\n scope: InitScope;\n intervalIds: ReturnType<typeof setInterval>[];\n timeoutIds: ReturnType<typeof setTimeout>[];\n hoverAbort?: AbortController;\n scroll: boolean; // presence flag: the element itself keys into bucket.scrollElements\n observed: boolean; // presence flag: the element itself keys into bucket.observed\n}\n\n// Source-wide registry: the single authority for \"has ANY scope already wired\n// this element?\". A WeakMap is not enumerable, so the per-scope\n// ScopeState.registered set is its enumerable companion — reset/teardown iterate\n// that set to release a scope's elements from here. Invariant:\n// el ∈ bucket.registered ⟺ elementRegistry.get(el).scope === that bucket's scope.\nconst elementRegistry = new WeakMap<HTMLElement, ElementRegistration>();\n\n// Register an element as wired by `scope`, in both the enumerable per-scope set\n// and the source-wide registry. No-op (returns false) if any scope already\n// registered it (cross-scope dedup) or the scope has no live bucket. The owning\n// bucket is derived from scopeStates, never passed in, so the\n// registered/registry invariant is structural — a caller cannot register an\n// element into a bucket that mismatches `scope`.\nexport function registerElement(\n scope: InitScope,\n el: HTMLElement,\n registration: ElementRegistration,\n): boolean {\n if (elementRegistry.has(el)) return false;\n const bucket = scopeStates.get(scope);\n if (!bucket) return false;\n registration.scope = scope;\n bucket.registered.add(el);\n elementRegistry.set(el, registration);\n return true;\n}\n\n// Source-wide \"already wired?\" check: true if ANY scope registered the element.\nexport function isRegistered(el: HTMLElement): boolean {\n return elementRegistry.has(el);\n}\n\n// Release one element and un-register it, so it can register again on re-add or\n// re-scan. Self-derives the OWNING bucket from registration.scope (never a\n// caller's scope): an element owned by the document scope but removed inside a\n// sub-scope observer's subtree is still released from its true owner, keeping\n// the registered/registry invariant intact.\nexport function reapElement(el: HTMLElement): void {\n const registration = elementRegistry.get(el);\n if (!registration) return;\n\n const bucket = scopeStates.get(registration.scope);\n\n // Clear each id AND splice it from the owner's flat arrays, so a long\n // inject/remove session does not accrue dead ids there.\n registration.intervalIds.forEach((id) => {\n clearInterval(id);\n if (bucket) {\n const index = bucket.intervalIds.indexOf(id);\n if (index !== -1) bucket.intervalIds.splice(index, 1);\n }\n });\n registration.timeoutIds.forEach((id) => {\n clearTimeout(id);\n if (bucket) {\n const index = bucket.timeoutIds.indexOf(id);\n if (index !== -1) bucket.timeoutIds.splice(index, 1);\n }\n });\n\n registration.hoverAbort?.abort();\n\n if (registration.observed) {\n unobserveElement(registration.scope, el);\n bucket?.observed.delete(el);\n }\n\n if (registration.scroll && bucket) {\n // scrollElements is a [HTMLElement, number] tuple array — remove EVERY entry\n // for this element. A multi-scroll element (data-elbaction=\"scroll:50;scroll:75\")\n // pushes one tuple per trigger, so removing only the first would strand the\n // rest and fire a phantom scroll on the detached element.\n bucket.scrollElements = bucket.scrollElements.filter(\n ([element]) => element !== el,\n );\n }\n\n bucket?.registered.delete(el);\n elementRegistry.delete(el);\n}\n\n// Tear down a scope's prior state (if any), then install a fresh empty bucket.\nexport function resetScope(scope: InitScope): ScopeState {\n const previous = scopeStates.get(scope);\n if (previous) {\n previous.abort.abort(); // removes scroll listener (hover is per-element)\n // The flat clears below are the defensive baseline: they blanket-clear\n // every id/observation the scope holds. The reap loop that follows is a\n // superset per element — it additionally aborts hover, deletes the registry\n // entry, and splices the flat arrays — and it re-clears/re-unobserves the\n // same resources, which is idempotent and harmless. Both are kept so a\n // future resource that lands in a flat array without going through\n // registerScanElement (→ `registered`) is still torn down here.\n previous.intervalIds.forEach((id) => clearInterval(id));\n previous.timeoutIds.forEach((id) => clearTimeout(id));\n previous.observed.forEach((element) => unobserveElement(scope, element));\n // Release each element's per-element resources (crucially its hover\n // listener) and clear the registry so the elements can re-register on the\n // fresh scan. Snapshot the set — reapElement deletes from it as it goes.\n // reapElement self-derives this same still-current bucket (the fresh one is\n // not installed until below).\n [...previous.registered].forEach((element) => reapElement(element));\n previous.mutationObservers.forEach((observer) => observer.disconnect());\n }\n\n const fresh: ScopeState = {\n abort: new AbortController(),\n intervalIds: [],\n timeoutIds: [],\n scrollElements: [],\n observed: new Set(),\n registered: new Set(),\n mutationObservers: [],\n };\n scopeStates.set(scope, fresh);\n return fresh;\n}\n\n// Test-only accessor: exposes a scope's live bucket so suites can assert its\n// observer/registration counts and spy on real observer instances. Not\n// re-exported through src/index.ts, so the public package surface is unchanged.\nexport function getScopeState(scope: InitScope): ScopeState | undefined {\n return scopeStates.get(scope);\n}\n\n// Reset function for testing: clears each active scope's scroll state.\nexport function resetScrollListener() {\n scopeStates.forEach((state) => {\n state.scrollElements = [];\n state.scrollListener = undefined;\n });\n}\n\nexport const createElb: (customLayer?: unknown) => unknown = (customLayer?) => {\n return (\n customLayer\n ? function () {\n (customLayer as unknown[]).push(arguments);\n }\n : elbOrg\n ) as unknown;\n};\n\nexport const Triggers: { [key: string]: Walker.Trigger } = {\n Click: 'click',\n Custom: 'custom',\n Hover: 'hover',\n Load: 'load',\n Pulse: 'pulse',\n Scroll: 'scroll',\n Submit: 'submit',\n Impression: 'impression',\n Visible: 'visible',\n Wait: 'wait',\n} as const;\n\nexport async function ready(\n fn: (context: Context, settings: Settings) => void,\n context: Context,\n settings: Settings,\n): Promise<void> {\n const readyFn = () => {\n fn(context, settings);\n };\n\n const scope = settings.scope;\n if (!scope) {\n readyFn();\n return;\n }\n const doc = (scope as Element).ownerDocument || (scope as Document);\n if (doc.readyState !== 'loading') {\n readyFn();\n } else {\n doc.addEventListener('DOMContentLoaded', readyFn);\n }\n}\n\n// Called once during source initialization to setup global listeners\nexport function initTriggers(context: Context, settings: Settings) {\n if (!settings.scope) return; // Skip if no scope available\n const requiredSettings = settings as Required<Settings>;\n initGlobalTrigger(context, requiredSettings);\n}\n\n// Called on each walker run to process load triggers\nexport function processLoadTriggers(context: Context, settings: Settings) {\n if (!settings.scope) return; // Skip if no scope available\n const requiredSettings = settings as Required<Settings>;\n initScopeTrigger(context, requiredSettings);\n}\n\nexport function initGlobalTrigger(context: Context, settings: Settings): void {\n const scope = settings.scope;\n\n if (!scope) return;\n\n // Abort any previously registered root listeners before re-registering.\n // This controller owns ONLY the root click/submit delegation.\n if (rootAbortController) rootAbortController.abort();\n rootAbortController = new AbortController();\n const { signal } = rootAbortController;\n\n scope.addEventListener(\n 'click',\n tryCatch(function (this: Scope, ev: unknown) {\n triggerClick.call(this, context, ev as MouseEvent);\n }) as EventListener,\n { signal, capture: settings.capture },\n );\n scope.addEventListener(\n 'submit',\n tryCatch(function (this: Scope, ev: unknown) {\n triggerSubmit.call(this, context, ev as SubmitEvent);\n }) as EventListener,\n { signal, capture: settings.capture },\n );\n}\n\n// Full teardown: aborts the root click/submit controller, then enumerates the\n// scope registry so EVERY active scope (document and sub-scopes alike) has its\n// hover/scroll listeners aborted, intervals/timeouts cleared, and shared\n// per-document observer disconnected. Safe to call before any init.\nexport function destroyTriggers(_settings?: Settings): void {\n if (rootAbortController) {\n rootAbortController.abort();\n rootAbortController = undefined;\n }\n\n // Two passes over the scope registry. `visibilityStates` is keyed per owner\n // document (one shared observer state), while `scopeStates` holds many\n // entries per document (the document scope plus every `walker init <el>`\n // sub-scope). unobserveElement reads that shared per-document state, so every\n // scope's armed dwell timers must be cancelled BEFORE any scope tears the\n // shared state down; otherwise the first same-document destroy deletes the\n // state and later scopes' unobserve calls no-op, leaking their timers.\n scopeStates.forEach((state, scope) => {\n state.abort.abort();\n // Stop watching observe containers first, so no mutation callback fires\n // mid-teardown against a half-cleared scope.\n state.mutationObservers.forEach((observer) => observer.disconnect());\n // Flat clears are the defensive baseline superset (see resetScope): they\n // blanket-clear the scope's ids/observations. The reap loop below is the\n // per-element superset — additionally aborting hover and deleting registry\n // entries — and its re-clear/re-unobserve is idempotent.\n state.intervalIds.forEach((id) => clearInterval(id));\n state.timeoutIds.forEach((id) => clearTimeout(id));\n // Cancel armed visibility dwell timers and detach observed elements while\n // the shared per-document state still exists. Mirrors resetScope's cleanup.\n state.observed.forEach((element) => unobserveElement(scope, element));\n // Release per-element resources. Hover listeners now live on per-element\n // controllers, so state.abort.abort() above no longer covers them — reap\n // each registered element to abort its hoverAbort and clear its registry\n // entry. Snapshot the set — reapElement deletes from it as it goes.\n [...state.registered].forEach((element) => reapElement(element));\n });\n\n scopeStates.forEach((_state, scope) => {\n // Normalizes to the owner document and disconnects the shared observer;\n // the second call for a same-document sub-scope is a harmless no-op.\n destroyVisibilityTracking(scope);\n });\n\n scopeStates.clear();\n}\n\n// Scope is the single carrier: it lives only in context.settings.scope. The\n// optional second argument is kept for signature compatibility (the Context\n// `initScope` slot calls this with a single, scope-aligned context) but is not\n// read. `walker init <el>` and `walker run` (document) are the same path,\n// differing only in the scope the carrier holds. Re-running on a scope tears\n// down that scope's prior state, then attaches fresh = one fresh init.\nexport function initScopeTrigger(context: Context, _settings?: Settings) {\n const scope = context.settings.scope;\n if (!scope) return;\n\n const bucket = resetScope(scope);\n\n // Idempotent \"ensure\" of the per-document observer (one per ownerDocument).\n // Never disconnect on a sub-scope re-init — resetScope already unobserved\n // this scope's elements above.\n initVisibilityTracking(scope, 1000);\n\n // default data-elbaction\n const selectorAction = getElbAttributeName(\n context.settings.prefix,\n Const.Commands.Action,\n false,\n );\n const doc = (scope as Element).ownerDocument || (scope as Document);\n const win = doc.defaultView;\n // The element self-check only applies to an Element sub-scope. A ShadowRoot\n // scope has no getAttribute, so calling handleActionElem on it would throw;\n // it is scanned by queryAllComposed below instead. A Document scope has\n // scope === doc, so it never enters this branch.\n if (scope !== doc && win && scope instanceof win.Element) {\n // Handle the elements action(s), too\n registerScanElement(context, scope, selectorAction, bucket, scope);\n }\n\n // Handle all children action(s)\n queryAllComposed(scope, `[${selectorAction}]`, (elem) => {\n registerScanElement(context, elem, selectorAction, bucket, scope);\n });\n\n if (bucket.scrollElements.length) scroll(context, scope, bucket);\n\n // Watch [<prefix>observe] containers so tagged content injected later is\n // auto-registered (and reaped on removal) without a manual `walker init`.\n observeContainers(context, scope, bucket, selectorAction);\n}\n\n// Attach a MutationObserver to each OUTERMOST [<prefix>observe] container per\n// tree in this scope. Nesting-skip: a container contained by another candidate\n// is skipped, because a subtree:true observer already sees descendant additions\n// and nested observers double-report a single deep insertion. Node.contains does\n// not cross shadow boundaries, so a shadow-hosted container is never \"contained\"\n// by a light-DOM ancestor and correctly keeps its own observer.\nfunction observeContainers(\n context: Context,\n scope: InitScope,\n bucket: ScopeState,\n selectorAction: string,\n) {\n const doc = (scope as Element).ownerDocument || (scope as Document);\n const win = doc.defaultView;\n // Guard like createObserver's win.IntersectionObserver check so a window-less\n // environment simply no-ops instead of throwing.\n if (!win || !win.MutationObserver) return;\n\n const selectorObserve = getElbAttributeName(\n context.settings.prefix,\n 'observe',\n false,\n );\n\n // Candidate containers: the scope element itself (sub-scope self-check) plus\n // every discovered descendant, across open shadow roots.\n const containers: Element[] = [];\n if (\n scope !== doc &&\n scope instanceof win.Element &&\n scope.matches(`[${selectorObserve}]`)\n ) {\n containers.push(scope);\n }\n queryAllComposed(scope, `[${selectorObserve}]`, (el) => containers.push(el));\n\n containers.forEach((container) => {\n // Outermost-per-tree, but scope-LOCAL: `contains` only dedups candidates\n // discovered by THIS scope. Two scopes observing overlapping trees (a\n // document-scope container and a `walker init <el>` sub-scope container over\n // the same subtree) are NOT deduped here — the source-wide element registry\n // (isRegistered) makes their overlapping adds safe instead (Amendment 3, a\n // documented v1 limitation).\n const nested = containers.some(\n (other) => other !== container && other.contains(container),\n );\n if (nested) return;\n\n const observer = new win.MutationObserver(\n tryCatch((records: MutationRecord[]) => {\n // Re-read the LIVE bucket every callback: a resetScope between async\n // callbacks installs a fresh bucket and aborts this one; if the scope\n // was destroyed, bail. Never close over the creation-time bucket.\n const live = scopeStates.get(scope);\n if (!live) return;\n\n records.forEach((record) => {\n record.addedNodes.forEach((node) =>\n handleAddedNode(context, node, selectorAction, live, scope),\n );\n // Re-arm the scroll listener once per record: an added node may have\n // registered a scroll trigger into live.scrollElements. scroll() guards\n // duplicate listeners, so once-per-record is sufficient and cheap.\n if (live.scrollElements.length) scroll(context, scope, live);\n record.removedNodes.forEach((node) =>\n handleRemovedNode(context, node),\n );\n });\n }),\n );\n observer.observe(container, { childList: true, subtree: true });\n bucket.mutationObservers.push(observer);\n });\n}\n\nexport async function handleTrigger(\n context: Context,\n element: Element,\n trigger: string,\n // @TODO add triggerParams to filter for specific trigger\n): Promise<unknown[]> {\n const events = getEvents(element, trigger, context.settings.prefix);\n return Promise.all(\n events.map((event: Walker.Event) =>\n translateToCoreCollector(context, {\n name: `${event.entity} ${event.action}`,\n ...event,\n trigger,\n }),\n ),\n );\n}\n\n// Discover + register a single scanned node. Returns the built registration\n// (or undefined if the node was skipped), so callers/tests can inspect the\n// per-element resources it recorded. Guards up front: element nodes only\n// (nodeType === 1, narrowed cast-free via `instanceof win.HTMLElement`), not\n// already registered by any scope, and carrying the action attribute.\nexport function registerScanElement(\n context: Context,\n node: Node,\n selectorAction: string,\n bucket: ScopeState,\n scope: InitScope,\n): ElementRegistration | undefined {\n if (node.nodeType !== 1) return undefined;\n const win = node.ownerDocument?.defaultView;\n if (!win || !(node instanceof win.HTMLElement)) return undefined;\n if (isRegistered(node)) return undefined;\n if (!getAttribute(node, selectorAction)) return undefined;\n\n const registration: ElementRegistration = {\n scope,\n intervalIds: [],\n timeoutIds: [],\n scroll: false,\n observed: false,\n };\n handleActionElem(context, node, selectorAction, bucket, registration);\n registerElement(scope, node, registration);\n return registration;\n}\n\n// The removed-node path the MutationObserver callback invokes. Element nodes\n// only (text/comment have no .matches/.querySelectorAll and would throw). Reaps\n// `removedEl` itself and each tagged descendant that ANY scope registered.\n// reapElement is single-arg and self-derives the OWNING bucket from\n// registration.scope (Amendment 2): a document-scope-owned element removed\n// inside a sub-scope observer's subtree is released from its true owner, not the\n// remover's scope — never pass a caller's scope/bucket here.\nexport function handleRemovedNode(context: Context, removedEl: Node): void {\n if (removedEl.nodeType !== 1) return;\n const win = removedEl.ownerDocument?.defaultView;\n if (!win || !(removedEl instanceof win.HTMLElement)) return;\n\n const selectorAction = getElbAttributeName(\n context.settings.prefix,\n Const.Commands.Action,\n false,\n );\n\n if (isRegistered(removedEl)) reapElement(removedEl);\n queryAllComposed(removedEl, `[${selectorAction}]`, (el) => {\n if (el instanceof win.HTMLElement && isRegistered(el)) reapElement(el);\n });\n}\n\n// The added-node path the MutationObserver callback invokes, mirroring\n// handleRemovedNode. HTMLElement nodes only: this narrowing is deliberate, not\n// merely a text/comment guard. Text/comment nodes lack .matches/.querySelectorAll\n// and would throw, but an SVGElement HAS them — it is excluded on purpose because\n// the whole registration pipeline (the elementRegistry WeakMap, the bucket\n// HTMLElement sets, handleActionElem) is HTMLElement-typed. Do not widen to\n// Element. addedNodes holds only the top-level inserted node, so walk its tagged\n// descendants too. Registers into the passed live bucket (the caller re-reads it\n// per callback); unlike the reaper, the add path must be told where to register.\nfunction handleAddedNode(\n context: Context,\n node: Node,\n selectorAction: string,\n bucket: ScopeState,\n scope: InitScope,\n): void {\n const win = node.ownerDocument?.defaultView;\n if (!win || !(node instanceof win.HTMLElement)) return;\n registerScanElement(context, node, selectorAction, bucket, scope);\n queryAllComposed(node, `[${selectorAction}]`, (el) => {\n registerScanElement(context, el, selectorAction, bucket, scope);\n });\n}\n\nfunction handleActionElem(\n context: Context,\n elem: HTMLElement,\n selectorAction: string,\n bucket: ScopeState,\n registration: ElementRegistration,\n) {\n const actionAttr = getAttribute(elem, selectorAction);\n\n if (!actionAttr) return;\n\n // TriggersActionGroups ([trigger: string]: TriggerActions)\n Object.values(getTriggerActions(actionAttr)).forEach((triggerActions) =>\n // TriggerActions (Array<TriggerAction>)\n triggerActions.forEach((triggerAction: Walker.TriggerActions[0]) => {\n // TriggerAction ({ trigger, triggerParams, action, actionParams })\n switch (triggerAction.trigger) {\n case Triggers.Hover:\n triggerHover(context, elem, registration);\n break;\n case Triggers.Load:\n triggerLoad(context, elem);\n break;\n case Triggers.Pulse:\n triggerPulse(\n context,\n elem,\n bucket,\n registration,\n triggerAction.triggerParams,\n );\n break;\n case Triggers.Scroll:\n triggerScroll(\n elem,\n bucket,\n registration,\n triggerAction.triggerParams,\n );\n break;\n case Triggers.Impression:\n triggerVisible(context, elem);\n bucket.observed.add(elem);\n registration.observed = true;\n break;\n case Triggers.Visible:\n triggerVisible(context, elem, { multiple: true });\n bucket.observed.add(elem);\n registration.observed = true;\n break;\n case Triggers.Wait:\n triggerWait(\n context,\n elem,\n bucket,\n registration,\n triggerAction.triggerParams,\n );\n break;\n }\n }),\n );\n}\n\n/**\n * Get the actual event target, piercing open shadow DOM boundaries.\n * Uses composedPath() to find the real target inside shadow roots.\n * For closed shadow DOM, falls back to the host element (by design).\n */\nfunction getComposedTarget(ev: Event): Element | undefined {\n const path = ev.composedPath?.();\n const target = path?.length ? path[0] : ev.target;\n if (!target || typeof target !== 'object' || !('tagName' in target))\n return undefined;\n return target as Element;\n}\n\nfunction triggerClick(context: Context, ev: MouseEvent) {\n const target = getComposedTarget(ev);\n if (target) handleTrigger(context, target, Triggers.Click);\n}\n\nfunction triggerHover(\n context: Context,\n elem: HTMLElement,\n registration: ElementRegistration,\n) {\n // Per-element controller: the reaper aborts exactly this element's hover\n // listener without tearing down the scope. Reuse one controller per element\n // so multiple hover actions on the same element share (and are all released\n // by) a single abort.\n if (!registration.hoverAbort) registration.hoverAbort = new AbortController();\n elem.addEventListener(\n 'mouseenter',\n tryCatch(function (this: Document, ev: MouseEvent) {\n const target = getComposedTarget(ev);\n if (target) handleTrigger(context, target, Triggers.Hover);\n }),\n { signal: registration.hoverAbort.signal },\n );\n}\n\nfunction triggerLoad(context: Context, elem: HTMLElement) {\n handleTrigger(context, elem, Triggers.Load);\n}\n\nfunction triggerPulse(\n context: Context,\n elem: HTMLElement,\n bucket: ScopeState,\n registration: ElementRegistration,\n triggerParams: string = '',\n) {\n const doc = elem.ownerDocument;\n const intervalId = setInterval(\n () => {\n // Only trigger when tab is active\n if (!doc.hidden) handleTrigger(context, elem, Triggers.Pulse);\n },\n parseInt(triggerParams || '') || 15000,\n );\n bucket.intervalIds.push(intervalId);\n registration.intervalIds.push(intervalId);\n}\n\nfunction triggerScroll(\n elem: HTMLElement,\n bucket: ScopeState,\n registration: ElementRegistration,\n triggerParams: string = '',\n) {\n // Scroll depth in percent, default 50%\n const depth = parseInt(triggerParams || '') || 50;\n\n // Ignore invalid parameters\n if (depth < 0 || depth > 100) return;\n\n bucket.scrollElements.push([elem, depth]);\n registration.scroll = true;\n}\n\nfunction triggerSubmit(context: Context, ev: SubmitEvent) {\n const target = getComposedTarget(ev);\n if (target) handleTrigger(context, target, Triggers.Submit);\n}\n\nfunction triggerWait(\n context: Context,\n elem: HTMLElement,\n bucket: ScopeState,\n registration: ElementRegistration,\n triggerParams: string = '',\n) {\n const timeoutId = setTimeout(\n () => handleTrigger(context, elem, Triggers.Wait),\n parseInt(triggerParams || '') || 15000,\n );\n bucket.timeoutIds.push(timeoutId);\n registration.timeoutIds.push(timeoutId);\n}\n\nfunction scroll(context: Context, scope: InitScope, bucket: ScopeState) {\n const doc = (scope as Element).ownerDocument || (scope as Document);\n const win = doc.defaultView!;\n const scrolling = (\n scrollElements: Walker.ScrollElements,\n context: Context,\n ) => {\n return scrollElements.filter(([element, depth]: [Element, number]) => {\n // getBoundingClientRect is viewport-relative and composes across shadow\n // boundaries, so scroll depth is correct for shadow-nested elements.\n const rect = (element as HTMLElement).getBoundingClientRect();\n\n // Skip calculations if not in viewport yet (element top below the fold)\n if (win.innerHeight < rect.top) return true;\n\n // Height of the elements box as 100 percent base\n const elemHeight = rect.height || element.clientHeight;\n // Distance from the viewport top to the elements bottom\n const elemBottom = rect.top + elemHeight;\n // Height of the non-visible pixels below the visible screen\n const hidden = elemBottom - win.innerHeight;\n // Visible percentage of the element\n const scrollDepth = (1 - hidden / (elemHeight || 1)) * 100;\n\n // Check if the elements visibility skipped the required border\n if (scrollDepth >= depth) {\n // Enough scrolling, it's time\n handleTrigger(context, element, Triggers.Scroll);\n\n // Remove the element from scrollEvents\n return false;\n }\n\n // Keep observing the element\n return true;\n });\n };\n\n // Don't add unnecessary scroll listeners. The bucket is fresh per init, so\n // the scope's prior scroll listener was already aborted via its controller.\n if (!bucket.scrollListener) {\n bucket.scrollListener = throttle(function () {\n bucket.scrollElements = scrolling.call(\n scope,\n bucket.scrollElements,\n context,\n );\n });\n\n // A ShadowRoot never receives page scroll events, so a listener on it would\n // never fire; attach to the owner document instead. Element/Document scopes\n // keep their existing target.\n const scrollTarget = scope instanceof win.ShadowRoot ? doc : scope;\n scrollTarget.addEventListener('scroll', bucket.scrollListener, {\n signal: bucket.abort.signal,\n });\n }\n}\n","import type { WalkerOS } from '@walkeros/core';\nimport type { Walker } from '@walkeros/web-core';\nimport type { Scope } from './types';\nimport { assign, castValue, isArray, trim } from '@walkeros/core';\nimport { Const } from '@walkeros/collector';\nimport { getAttribute } from '@walkeros/web-core';\n\nexport function getElbAttributeName(\n prefix: string,\n name?: string,\n isProperty = true,\n): string {\n // separate dynamic properties from walker Const.Commands\n const separator = isProperty ? '-' : '';\n name = name != undefined ? separator + name : '';\n return prefix + name;\n}\n\nexport function getElbValues(\n prefix: string,\n element: Element,\n name: string,\n isProperty = true,\n): WalkerOS.Properties {\n const attributeValue =\n getAttribute(element, getElbAttributeName(prefix, name, isProperty)) || '';\n\n const elbValues = splitAttribute(attributeValue).reduce((values, str) => {\n let [key, val]: Walker.KeyVal = splitKeyVal(str);\n\n if (!key) return values;\n\n // Handle keys without value\n if (!val) {\n // Manually remove the : from key on empty values\n if (key.endsWith(':')) key = key.slice(0, -1);\n val = '';\n }\n\n // Dynamic values\n if (val.startsWith('#')) {\n val = val.slice(1); // Remove # symbol\n try {\n // Read property value from element\n let dynamicValue = (element as Element)[val as keyof Element];\n if (!dynamicValue && val === 'selected') {\n // Try to read selected value with chance of error\n dynamicValue = (element as HTMLSelectElement).options[\n (element as HTMLSelectElement).selectedIndex\n ].text;\n }\n\n val = String(dynamicValue);\n } catch (error) {\n val = '';\n }\n }\n\n if (key.endsWith('[]')) {\n key = key.slice(0, -2); // Remove [] symbol\n if (!isArray(values[key])) values[key] = [];\n (values[key] as WalkerOS.PropertyType[]).push(castValue(val));\n } else {\n values[key] = castValue(val);\n }\n\n return values;\n }, {} as WalkerOS.Properties);\n\n return elbValues;\n}\n\nexport function getAllEvents(\n scope: Scope,\n prefix: string = Const.Commands.Prefix,\n): Walker.Events {\n const actualScope = scope;\n if (!actualScope) return [];\n let events: Walker.Events = [];\n const action = Const.Commands.Action;\n const actionSelector = `[${getElbAttributeName(prefix, action, false)}]`;\n\n const processElementEvents = (elem: Element) => {\n Object.keys(getElbValues(prefix, elem, action, false)).forEach(\n (trigger) => {\n events = events.concat(getEvents(elem, trigger, prefix));\n },\n );\n };\n\n // Check if the scope element itself has action attributes\n const doc =\n (actualScope as Element).ownerDocument || (actualScope as Document);\n if (\n actualScope !== doc &&\n (actualScope as Element).matches?.(actionSelector)\n ) {\n processElementEvents(actualScope as Element);\n }\n\n queryAllComposed(actualScope, actionSelector, processElementEvents);\n\n return events;\n}\n\nexport function getEvents(\n target: Element,\n trigger: string,\n prefix: string = Const.Commands.Prefix,\n): Walker.Events {\n const events: Walker.Events = [];\n\n // Check for actions and get entity collection strategy\n const { actions, nearestOnly } = resolveAttributes(prefix, target, trigger);\n\n // Stop if there's no valid action combo\n if (!actions.length) return events;\n\n actions.forEach((triggerAction) => {\n const filter = splitAttribute(triggerAction.actionParams || '', ',').reduce(\n (filter, param) => {\n filter[trim(param)] = true;\n return filter;\n },\n {} as Walker.Filter,\n );\n\n // Get entities - using nearestOnly flag to determine collection strategy\n const entities = getEntities(prefix, target, filter, nearestOnly);\n\n // Use page as default entity if no one was set\n if (!entities.length) {\n const entity = 'page';\n // Only use explicit page properties and ignore generic properties\n const entitySelector = `[${getElbAttributeName(prefix, entity)}]`;\n\n // Get matching properties from the element and its parents\n const [data, context] = getThisAndParentProperties(\n target,\n entitySelector,\n prefix,\n entity,\n );\n\n entities.push({\n entity, // page\n data, // Consider only upper data\n nested: [], // Skip nested in this faked page case\n context,\n });\n }\n\n // Return a list of full events\n entities.forEach((entity) => {\n events.push({\n entity: entity.entity,\n action: triggerAction.action,\n data: entity.data,\n trigger,\n context: entity.context,\n nested: entity.nested ?? [],\n });\n });\n });\n\n return events;\n}\n\nexport function getGlobals(\n prefix: string = Const.Commands.Prefix,\n scope?: Scope,\n): WalkerOS.Properties {\n if (!scope) return {};\n const globalsName = getElbAttributeName(\n prefix,\n Const.Commands.Globals,\n false,\n );\n const globalSelector = `[${globalsName}]`;\n let values = {};\n\n queryAll(scope, globalSelector, (element) => {\n values = assign(\n values,\n getElbValues(prefix, element, Const.Commands.Globals, false),\n );\n });\n\n return values;\n}\n\nexport function getUser(\n prefix: string = Const.Commands.Prefix,\n scope?: Scope,\n): WalkerOS.User {\n if (!scope) return {};\n const userName = getElbAttributeName(prefix, Const.Commands.User, false);\n const userSelector = `[${userName}]`;\n let values: WalkerOS.User = {};\n\n queryAll(scope, userSelector, (element) => {\n values = assign(\n values,\n getElbValues(prefix, element, Const.Commands.User, false),\n );\n });\n\n return values;\n}\n\nexport function getPageViewData(\n prefix: string,\n scope: Scope,\n): [WalkerOS.Properties, WalkerOS.OrderedProperties] {\n // static page view\n const doc = (scope as Element).ownerDocument || (scope as Document);\n const win = doc.defaultView!;\n const loc = win.location;\n const page = 'page';\n const scopeElement =\n 'body' in scope ? (scope as Document).body : (scope as Element);\n const [data, context] = getThisAndParentProperties(\n scopeElement,\n `[${getElbAttributeName(prefix, page)}]`,\n prefix,\n page,\n );\n data.domain = loc.hostname;\n data.title = doc.title;\n data.referrer = doc.referrer;\n\n if (loc.search) data.search = loc.search;\n if (loc.hash) data.hash = loc.hash;\n\n // @TODO get all nested entities\n return [data, context];\n}\n\nexport function getTriggerActions(str: string): Walker.TriggersActionGroups {\n const values: Walker.TriggersActionGroups = {};\n\n const attributes = splitAttribute(str);\n\n attributes.forEach((str) => {\n const [triggerAttr, actionAttr] = splitKeyVal(str);\n const [trigger, triggerParams] = parseAttribute(triggerAttr);\n\n if (!trigger) return;\n\n let [action, actionParams] = parseAttribute(actionAttr || '');\n\n // Shortcut if trigger and action are the same (click:click)\n action = action || trigger;\n\n if (!values[trigger]) values[trigger] = [];\n\n values[trigger].push({ trigger, triggerParams, action, actionParams });\n });\n\n return values;\n}\n\nexport function getEntities(\n prefix: string,\n target: Element,\n filter?: Walker.Filter,\n nearestOnly = false,\n): WalkerOS.Entities {\n const entities: WalkerOS.Entities = [];\n let element = target as Node['parentElement'];\n\n // Unset empty filter object\n filter = Object.keys(filter || {}).length !== 0 ? filter : undefined;\n\n while (element) {\n const entity = getEntity(prefix, element, target, filter);\n if (entity) {\n entities.push(entity);\n if (nearestOnly) break; // Stop after first entity for data-elbaction\n }\n\n element = getParent(prefix, element);\n }\n\n return entities;\n}\n\nfunction getEntity(\n prefix: string,\n element: Element,\n origin?: Element,\n filter?: Walker.Filter,\n): WalkerOS.Entity | null {\n const entity = getAttribute(element, getElbAttributeName(prefix));\n\n // It's not a (valid) entity element or should be filtered\n if (!entity || (filter && !filter[entity])) return null;\n\n const scopeElems = [element]; // All related elements\n const dataSelector = `[${getElbAttributeName(\n prefix,\n entity,\n )}],[${getElbAttributeName(prefix, '')}]`; // [data-elb-entity,data-elb-]\n const linkName = getElbAttributeName(prefix, Const.Commands.Link, false); // data-elblink\n\n let data: WalkerOS.Properties = {};\n const nested: WalkerOS.Entities = [];\n const [parentData, context] = getThisAndParentProperties(\n origin || element,\n dataSelector,\n prefix,\n entity,\n true, // collect scoped generic (data-elb_) for real entities\n );\n\n // Add linked elements (data-elblink)\n queryAllComposed(element, `[${linkName}]`, (link) => {\n const [linkId, linkState]: Walker.KeyVal = splitKeyVal(\n getAttribute(link, linkName),\n );\n\n // Get all linked child elements if link is a parent\n // Note: Searches entire document including shadow roots.\n // Acceptable because link-parent usage is rare in practice.\n if (linkState === 'parent')\n queryAllComposed(\n element.ownerDocument.body,\n `[${linkName}=\"${linkId}:child\"]`,\n (wormhole) => {\n scopeElems.push(wormhole);\n\n // A linked child can also be an entity\n const nestedEntity = getEntity(prefix, wormhole);\n if (nestedEntity) nested.push(nestedEntity);\n },\n );\n });\n\n // Get all property elements including linked elements\n const propertyElems: Array<Element> = [];\n scopeElems.forEach((elem) => {\n // Also check for property on same level\n if (elem.matches(dataSelector)) propertyElems.push(elem);\n\n queryAllComposed(elem, dataSelector, (elem) => propertyElems.push(elem));\n });\n\n // Get properties\n let genericData: WalkerOS.Properties = {};\n propertyElems.forEach((child) => {\n // Eventually override closer properties\n genericData = assign(genericData, getElbValues(prefix, child, ''));\n data = assign(data, getElbValues(prefix, child, entity));\n });\n\n // Merge properties with the hierarchy generic > data > parent\n data = assign(assign(genericData, data), parentData);\n\n // Get nested entities\n scopeElems.forEach((elem) => {\n queryAllComposed(\n elem,\n `[${getElbAttributeName(prefix)}]`,\n (nestedEntityElement) => {\n const nestedEntity = getEntity(prefix, nestedEntityElement);\n if (nestedEntity) nested.push(nestedEntity);\n },\n );\n });\n\n return { entity, data, context, nested };\n}\n\nfunction getParent(prefix: string, elem: HTMLElement): HTMLElement | null {\n const linkName = getElbAttributeName(prefix, Const.Commands.Link, false); // data-elblink\n\n // Link\n if (elem.matches(`[${linkName}]`)) {\n const [linkId, linkState]: Walker.KeyVal = splitKeyVal(\n getAttribute(elem, linkName),\n );\n if (linkState === 'child') {\n // Link-parent lookup does not cross shadow boundaries.\n // Uses simple queryAll (no shadow recursion) since this runs\n // during per-event entity traversal.\n const doc = elem.ownerDocument;\n let found: HTMLElement | null = null;\n queryAll(doc, `[${linkName}=\"${linkId}:parent\"]`, (el) => {\n if (!found) found = el as HTMLElement;\n });\n return found;\n }\n }\n\n // Shadow DOM traversal\n const win = elem.ownerDocument.defaultView!;\n if (\n !elem.parentElement &&\n elem.getRootNode &&\n elem.getRootNode() instanceof win.ShadowRoot\n ) {\n return (elem.getRootNode() as ShadowRoot).host as HTMLElement;\n }\n\n return elem.parentElement;\n}\n\nfunction getThisAndParentProperties(\n element: Element,\n entitySelector: string,\n prefix: string,\n type: string,\n collectScoped = false,\n): [data: WalkerOS.Properties, context: WalkerOS.OrderedProperties] {\n let data: WalkerOS.Properties = {};\n const context: WalkerOS.OrderedProperties = {};\n let parent = element as Node['parentElement'];\n const contextSelector = `[${getElbAttributeName(\n prefix,\n Const.Commands.Context,\n false,\n )}]`;\n const scopedName = Const.Commands.Scoped; // '_' -> data-elb_\n const scopedSelector = `[${getElbAttributeName(prefix, scopedName, false)}]`;\n\n // Get all bubbling-up properties with decreasing priority\n let contextI = 0; // Context counter\n while (parent) {\n // Scoped generic (data-elb_): same generic tier, bubble-up only.\n // The synthetic page entity ignores generics, so it skips scoped too.\n // Guard on scopedName so a falsy command constant degrades to \"no scoped\n // collection\" instead of an empty suffix that would match the bare prefix.\n if (collectScoped && scopedName && parent.matches(scopedSelector)) {\n data = assign(getElbValues(prefix, parent, scopedName, false), data);\n }\n\n // Properties\n if (parent.matches(entitySelector)) {\n // Get higher properties first\n data = assign(getElbValues(prefix, parent, ''), data); // Generic\n data = assign(getElbValues(prefix, parent, type), data); // Explicit\n }\n\n // Context\n if (parent.matches(contextSelector)) {\n Object.entries(\n getElbValues(prefix, parent, Const.Commands.Context, false),\n ).forEach(([key, val]) => {\n // Don't override context with same but higher key\n if (val && !context[key]) context[key] = [val, contextI];\n });\n\n // Increase context counter with each parent level\n ++contextI;\n }\n\n parent = getParent(prefix, parent);\n }\n\n return [data, context];\n}\n\nfunction queryAll(\n scope: Document | Element,\n selector: string,\n fn: (element: Element) => void,\n): void {\n scope.querySelectorAll(selector).forEach(fn);\n}\n\n// Shadow-aware version that recurses into open shadow roots.\n// Use for discovery (init) and entity-scoped queries (small subtrees).\n// Avoid for per-event document-scoped queries (e.g. getGlobals).\nexport function queryAllComposed(\n scope: Document | Element | ShadowRoot,\n selector: string,\n fn: (element: Element) => void,\n): void {\n scope.querySelectorAll(selector).forEach(fn);\n\n const doc = (scope as Element).ownerDocument || (scope as Document);\n const win = doc.defaultView!;\n if (scope instanceof win.Element && scope.shadowRoot) {\n queryAllComposed(scope.shadowRoot, selector, fn);\n }\n scope.querySelectorAll('*').forEach((el) => {\n if (el.shadowRoot) {\n queryAllComposed(el.shadowRoot, selector, fn);\n }\n });\n}\n\nfunction resolveAttributes(\n prefix: string,\n target: Element,\n trigger: string,\n): { actions: Walker.TriggerActions; nearestOnly: boolean } {\n let element = target as Node['parentElement'];\n\n while (element) {\n // Check for data-elbactions first (takes precedence)\n const multiAttribute = getAttribute(\n element,\n getElbAttributeName(prefix, Const.Commands.Actions, false),\n );\n\n if (multiAttribute) {\n const triggerActions = getTriggerActions(multiAttribute);\n if (triggerActions[trigger]) {\n return { actions: triggerActions[trigger], nearestOnly: false };\n }\n }\n\n // Check for data-elbaction (nearest entity only)\n const singleAttribute = getAttribute(\n element,\n getElbAttributeName(prefix, Const.Commands.Action, false),\n );\n\n if (singleAttribute) {\n const triggerActions = getTriggerActions(singleAttribute);\n // Action found on element or is not a click trigger\n if (triggerActions[trigger] || trigger !== 'click') {\n return { actions: triggerActions[trigger] || [], nearestOnly: true };\n }\n }\n\n element = getParent(prefix, element);\n }\n\n return { actions: [], nearestOnly: false };\n}\n\nfunction splitAttribute(str: string, separator = ';'): Walker.Attributes {\n const values: Walker.Attributes = [];\n\n if (!str) return values;\n\n const reg = new RegExp(`(?:[^${separator}']+|'[^']*')+`, 'ig');\n return str.match(reg) || [];\n}\n\nfunction splitKeyVal(str: string): Walker.KeyVal {\n const [key, value] = str.split(/:(.+)/, 2);\n return [trim(key), trim(value)];\n}\n\nfunction parseAttribute(str: string): Walker.KeyVal {\n // action(a, b, c)\n const [key, value] = str.split('(', 2);\n const param = value ? value.slice(0, -1) : ''; // Remove the )\n // key = 'action'\n // param = 'a, b, c'\n return [key, param];\n}\n","import type { Context, InitScope } from './types';\nimport { tryCatch } from '@walkeros/core';\nimport { isVisible } from '@walkeros/web-core';\nimport { handleTrigger, Triggers } from './trigger';\n\n// Fraction of the element, or of the viewport, whichever is smaller, that must\n// be on screen along each axis.\nconst COVERAGE = 0.5;\n\n/**\n * Decide from the observer entry alone whether an element is showing enough of\n * itself to start the dwell clock. Reads no DOM and forces no layout: every\n * rect here was computed by the browser during this frame's layout pass.\n *\n * `intersectionRatio` cannot be used directly. It is a fraction of the\n * element's own area, so an element bigger than the viewport along an axis can\n * never show COVERAGE of itself along it: a 716px card in a 450px viewport\n * tops out at 0.63, and an 8000px section tops out at 0.1. Clamping the\n * requirement to `min(element, viewport)` per axis makes the bar reachable for\n * any geometry, and gives the property customers actually expect: shrinking the\n * viewport can only make the trigger fire earlier, never later.\n *\n * The clamp is per axis rather than by area on purpose. An area clamp collapses\n * back to the element-relative rule whenever the element's area is smaller than\n * the viewport's, which is exactly the wide-but-short case (a 3000x80 row tops\n * out at ratio 0.48 and never fires).\n */\nexport function isEligible(\n entry: IntersectionObserverEntry,\n win: Window,\n): boolean {\n if (!entry.isIntersecting) return false;\n\n const box = entry.boundingClientRect;\n // A zero-area target reports intersectionRatio 1 while it is intersecting\n // (spec 2.2.12). It has nothing on screen; never start a dwell for it.\n if (box.width <= 0 || box.height <= 0) return false;\n\n // rootBounds is null when the target lives in a cross-origin iframe. Falling\n // back to the window keeps the arithmetic finite: a NaN would compare false\n // and silently disable the trigger for every such element.\n const rootWidth = entry.rootBounds ? entry.rootBounds.width : win.innerWidth;\n const rootHeight = entry.rootBounds\n ? entry.rootBounds.height\n : win.innerHeight;\n if (rootWidth <= 0 || rootHeight <= 0) return false;\n\n const visible = entry.intersectionRect;\n const needWidth = COVERAGE * Math.min(box.width, rootWidth);\n const needHeight = COVERAGE * Math.min(box.height, rootHeight);\n\n return visible.width >= needWidth && visible.height >= needHeight;\n}\n\n// Threshold grid for the observer.\n//\n// An entry is queued only when the crossed threshold INDEX changes; a bare ratio\n// change queues nothing. The grid is therefore not a performance dial, it is the\n// SAMPLING RATE of the predicate: the trigger can only notice an element became\n// eligible at a grid crossing, which quantizes the arm point UPWARD to the next\n// threshold.\n//\n// A coarse grid makes that error large. Eligibility for an element of height H in\n// a viewport of height V lands at ratio COVERAGE * V/H, which for a 716px card in\n// a 450px viewport is 0.314. A grid whose next step above that is 0.4 would not\n// arm until 287px was showing, against a true bar of 225px.\n//\n// A uniform 1% grid keeps the error within about 1% of the element and costs\n// effectively nothing: the observer queues at most ONE entry per target per frame\n// however many thresholds there are.\nconst THRESHOLDS = Array.from({ length: 101 }, (_, index) => index / 100);\n\ninterface ElementConfig {\n multiple: boolean;\n blocked: boolean;\n context: Context;\n trigger: string;\n}\n\ninterface VisibilityState {\n observer?: IntersectionObserver;\n resizeObserver?: ResizeObserver;\n timers: WeakMap<HTMLElement, number>;\n duration: number;\n configs: WeakMap<HTMLElement, ElementConfig[]>;\n // Elements last seen without a layout box. A 0x0 target reports ratio 1, so\n // growing one into view never changes the threshold index and the observer\n // stays silent; the ResizeObserver re-observes to force a fresh entry.\n zeroArea: WeakSet<HTMLElement>;\n}\n\n// Module-level visibility state keyed by the owner document. Both an element\n// sub-scope and its document normalize to the same key (see getScopeKey), so\n// there is exactly one IntersectionObserver per document: create-key and\n// lookup-key can never diverge, and iframes still get their own observer.\nconst visibilityStates = new WeakMap<Document | Element, VisibilityState>();\n\n/**\n * Normalize any scope (document or element) to its owner document. The\n * IntersectionObserver root is always the viewport, so per-sub-scope observers\n * would be meaningless; keying by ownerDocument gives one shared observer per\n * document and keeps the create/lookup keys aligned.\n */\nexport function getScopeKey(scope: InitScope): Document | Element {\n return (scope as Element).ownerDocument || (scope as Document);\n}\n\nfunction createObserver(\n scope: Document | Element,\n): IntersectionObserver | undefined {\n const doc = (scope as Element).ownerDocument || (scope as Document);\n const win = doc.defaultView;\n if (!win || !win.IntersectionObserver) return undefined;\n\n return tryCatch(\n () =>\n new win.IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => handleIntersection(scope, entry));\n },\n { rootMargin: '0px', threshold: THRESHOLDS },\n ),\n () => undefined,\n )();\n}\n\nfunction handleIntersection(\n scope: Document | Element,\n entry: IntersectionObserverEntry,\n): void {\n const target = entry.target as HTMLElement;\n const state = visibilityStates.get(getScopeKey(scope));\n if (!state) return;\n\n const win = target.ownerDocument.defaultView;\n if (!win) return;\n\n const configs = state.configs.get(target);\n // An entry can be queued before an unobserve lands. Never arm a dwell for an\n // element the module has already released.\n if (!configs || !configs.length) return;\n\n const existingTimer = state.timers.get(target);\n\n if (isEligible(entry, win)) {\n // Every trigger on this element has fired and is waiting to re-enter.\n if (configs.every((config) => config.blocked)) return;\n\n if (!existingTimer) {\n const timer = win.setTimeout(() => {\n fire(getScopeKey(scope), target);\n }, state.duration);\n state.timers.set(target, timer);\n }\n return;\n }\n\n // Below the bar: cancel the armed dwell and re-arm the repeating triggers.\n if (existingTimer) {\n clearTimeout(existingTimer);\n state.timers.delete(target);\n }\n configs.forEach((config) => {\n if (config.multiple) config.blocked = false;\n });\n}\n\nasync function fire(\n scope: Document | Element,\n target: HTMLElement,\n fromHidden = false,\n): Promise<void> {\n const state = visibilityStates.get(scope);\n if (!state) return;\n\n // Release the timer on EVERY path, before anything can bail. Leaving a fired\n // timer id behind makes `!existingTimer` false forever, which permanently\n // prevents the element from arming again: it is dead for the page session.\n state.timers.delete(target);\n\n const doc = target.ownerDocument;\n const win = doc.defaultView;\n if (!win) return;\n\n // Never count an impression the user could not possibly have seen. Re-arm\n // rather than drop: a page opened in a background tab has every above-the-fold\n // element already eligible, and its geometry will never change again, so the\n // observer would queue no further entry and the impression would be lost for\n // the whole session even after the user foregrounds the tab and reads the\n // page. Background timers are throttled to roughly one per second, so the\n // retry is cheap, and it stops as soon as the tab is visible.\n if (doc.visibilityState !== 'visible') {\n state.timers.set(\n target,\n win.setTimeout(() => {\n fire(scope, target, true);\n }, state.duration),\n );\n return;\n }\n\n // The expiring timer was armed while the tab was hidden, so its dwell was\n // spent hidden no matter when the user foregrounded: firing here could count\n // an impression after ~0ms of actually visible time. Require one fresh,\n // fully visible dwell instead.\n if (fromHidden) {\n state.timers.set(\n target,\n win.setTimeout(() => {\n fire(scope, target);\n }, state.duration),\n );\n return;\n }\n\n // The one and only occlusion check, uncached, on one element. A failed check\n // re-arms rather than drops: occlusion can clear without any geometry or\n // intersection change (an overlay dismissed, a carousel slide swapped in\n // place), so the observer would queue no entry and the impression would be\n // lost for the session. The retry stops on its own when the element leaves\n // the eligible band (the below-the-bar branch cancels the timer) or is\n // unobserved.\n if (!isVisible(target, win, doc)) {\n state.timers.set(\n target,\n win.setTimeout(() => {\n fire(scope, target);\n }, state.duration),\n );\n return;\n }\n\n const configs = state.configs.get(target);\n if (!configs) return;\n\n const pending = configs.filter((config) => !config.blocked);\n if (!pending.length) return;\n\n // Commit state BEFORE awaiting. handleTrigger can outlast the dwell on a slow\n // destination, and a late write would clobber state that the not-eligible\n // branch has since reset, swallowing the element's next event.\n pending.forEach((config) => {\n if (config.multiple) config.blocked = true;\n });\n\n const remaining = configs.filter((config) => config.multiple);\n if (remaining.length) {\n state.configs.set(target, remaining);\n } else {\n // Single-shot impressions are done. Unobserve before the await so a second\n // dwell cannot arm and double-fire while the destination is in flight.\n unobserveElement(scope, target);\n }\n\n await Promise.all(\n pending.map((config) =>\n handleTrigger(config.context, target, config.trigger),\n ),\n );\n}\n\nexport function unobserveElement(scope: InitScope, element: HTMLElement): void {\n const state = visibilityStates.get(getScopeKey(scope));\n if (!state) return;\n\n state.observer?.unobserve(element);\n state.resizeObserver?.unobserve(element);\n state.configs.delete(element);\n state.zeroArea.delete(element);\n\n const timer = state.timers.get(element);\n if (timer) {\n clearTimeout(timer);\n state.timers.delete(element);\n }\n}\n\n/**\n * Rescue the one case an IntersectionObserver cannot report.\n *\n * A zero-area target reports intersectionRatio 1 while it intersects (spec\n * 2.2.12). When a framework mounts an element empty and renders it a frame\n * later, the ratio goes 1 -> 1, the threshold index never changes, and the\n * observer queues NO entry - at any threshold density. Re-observing resets the\n * observer's per-target bookkeeping (previousThresholdIndex back to -1), which\n * forces a fresh entry on the next frame.\n *\n * Gated on the zero -> laid-out transition, so a resizing animation cannot turn\n * this into a per-frame re-observe storm.\n */\nfunction createResizeObserver(\n scope: Document | Element,\n): ResizeObserver | undefined {\n const doc = (scope as Element).ownerDocument || (scope as Document);\n const win = doc.defaultView;\n if (!win || !win.ResizeObserver) return undefined;\n\n return tryCatch(\n () =>\n new win.ResizeObserver((entries) => {\n const state = visibilityStates.get(scope);\n if (!state?.observer) return;\n\n entries.forEach((entry) => {\n const target = entry.target as HTMLElement;\n const { width, height } = entry.contentRect;\n const isZero = width <= 0 || height <= 0;\n\n if (isZero) {\n state.zeroArea.add(target);\n return;\n }\n\n if (!state.zeroArea.has(target)) return;\n state.zeroArea.delete(target);\n\n state.observer?.unobserve(target);\n state.observer?.observe(target);\n });\n }),\n () => undefined,\n )();\n}\n\nexport function initVisibilityTracking(\n scope: InitScope,\n duration = 1000,\n): void {\n const key = getScopeKey(scope);\n if (visibilityStates.has(key)) return;\n\n visibilityStates.set(key, {\n observer: createObserver(key),\n resizeObserver: createResizeObserver(key),\n timers: new WeakMap(),\n duration,\n configs: new WeakMap(),\n zeroArea: new WeakSet(),\n });\n}\n\nexport function triggerVisible(\n context: Context,\n element: HTMLElement,\n config: { multiple?: boolean } = { multiple: false },\n): void {\n const scope = context.settings.scope;\n if (!scope) return;\n const state = visibilityStates.get(getScopeKey(scope));\n if (!state?.observer || !element) return;\n\n const multiple = config.multiple ?? false;\n const configs = state.configs.get(element) ?? [];\n const hadConfigs = configs.length > 0;\n const trigger = multiple ? Triggers.Visible : Triggers.Impression;\n // Overlapping scans re-register the same element (a document run, then\n // `walker init` on a container that was already inside it). Replace the\n // same-trigger registration instead of appending, or every re-scan would\n // add another config and one dwell would fire duplicate events. An element\n // carries at most one `impression` plus one `visible` config.\n const entry: ElementConfig = { multiple, blocked: false, context, trigger };\n const existing = configs.findIndex(\n (existingConfig) => existingConfig.trigger === trigger,\n );\n if (existing >= 0) configs[existing] = entry;\n else configs.push(entry);\n state.configs.set(element, configs);\n\n // One element can carry both `impression` and `visible`; observe it once.\n if (hadConfigs) return;\n\n const box = element.getBoundingClientRect();\n if (box.width <= 0 || box.height <= 0) state.zeroArea.add(element);\n\n state.observer.observe(element);\n state.resizeObserver?.observe(element);\n}\n\nexport function destroyVisibilityTracking(scope?: InitScope): void {\n if (!scope) return;\n const key = getScopeKey(scope);\n const state = visibilityStates.get(key);\n if (!state) return;\n\n state.observer?.disconnect();\n state.resizeObserver?.disconnect();\n visibilityStates.delete(key);\n}\n","import type { WalkerOS, Elb, Collector } from '@walkeros/core';\nimport { isString, isObject, isElementOrDocument } from '@walkeros/core';\nimport { createPushResult } from '@walkeros/collector';\nimport type {\n BrowserPushData,\n BrowserPushOptions,\n BrowserPushContext,\n} from './types/elb';\nimport type { Context, Settings, InitScope, Scope } from './types';\nimport { getEntities, getGlobals } from './walker';\n\n/**\n * Translation layer that converts flexible browser source inputs\n * to the strict core collector format\n */\nexport function translateToCoreCollector(\n context: Context,\n eventOrCommand: unknown,\n data?: BrowserPushData,\n options?: BrowserPushOptions,\n pushContext?: BrowserPushContext,\n nested?: WalkerOS.Entities,\n custom?: WalkerOS.Properties,\n): Promise<Elb.PushResult> {\n const { elb, settings } = context;\n\n // Handle walker commands. `walker init` is browser-only (re-scans a DOM\n // scope for `data-elb*` tags and fires load triggers) and is dispatched\n // here because the collector must stay DOM-free. All other walker\n // commands route through elb to commonHandleCommand on the collector.\n // `initScope` arrives on `context` from the source factory to avoid a\n // static import of `./trigger` (which would form a load-time cycle).\n if (isString(eventOrCommand) && eventOrCommand.startsWith('walker ')) {\n if (eventOrCommand === 'walker init' && context.initScope) {\n const scopes = normalizeInitScopes(data, settings);\n for (const scope of scopes) {\n // Scope is a single carrier: build a scope-aligned context so the\n // trigger pipeline reads the new scope from context.settings.scope\n // everywhere (no diverging positional scope param).\n context.initScope({ ...context, settings: { ...settings, scope } });\n }\n return Promise.resolve(createPushResult({ ok: true }));\n }\n return elb(eventOrCommand, data as WalkerOS.Properties);\n }\n\n // Handle event objects - add source and globals if missing\n if (isObject(eventOrCommand)) {\n const event = eventOrCommand;\n if (!event.source && settings.scope) {\n const scopeDoc = ((settings.scope as Element).ownerDocument ||\n settings.scope) as Document;\n const scopeWin = scopeDoc.defaultView!;\n event.source = getBrowserSource(scopeWin, scopeDoc);\n }\n\n // Add globals if not already present\n if (!event.globals && settings.scope) {\n event.globals = isGlobalsScope(settings.scope)\n ? getGlobals(settings.prefix, settings.scope)\n : {};\n }\n\n return elb(event);\n }\n\n // Extract entity name from event string\n const [entity] = String(\n isObject(eventOrCommand) ? eventOrCommand.name : eventOrCommand,\n ).split(' ');\n\n // Get data and context either from elements or parameters\n let eventData = isObject(data) ? (data as WalkerOS.Properties) : {};\n let eventContext: WalkerOS.OrderedProperties = {};\n\n let elemParameter: undefined | Element;\n let dataIsElem = false;\n\n // Check if data parameter is an element\n if (isElementOrDocument(data)) {\n elemParameter = data as Element;\n dataIsElem = true;\n }\n\n // Check if contextData parameter is an element\n if (isElementOrDocument(pushContext)) {\n elemParameter = pushContext as Element;\n } else if (isObject(pushContext) && Object.keys(pushContext).length) {\n eventContext = pushContext as WalkerOS.OrderedProperties;\n }\n\n // Extract data from element if provided\n if (elemParameter) {\n const entityObj = getEntities(\n settings.prefix || 'data-elb',\n elemParameter,\n ).find((obj) => obj.entity === entity);\n if (entityObj) {\n if (dataIsElem) eventData = entityObj.data;\n if (entityObj.context) eventContext = entityObj.context;\n }\n }\n\n // Derive win/doc from scope for browser-specific APIs\n const scopeDoc = settings.scope\n ? (((settings.scope as Element).ownerDocument ||\n settings.scope) as Document)\n : undefined;\n const scopeWin = scopeDoc?.defaultView;\n\n // Special handling for page events\n if (entity === 'page' && scopeWin) {\n eventData.id = eventData.id || scopeWin.location.pathname;\n }\n\n // Collect globals from the DOM scope\n const eventGlobals =\n settings.scope && isGlobalsScope(settings.scope)\n ? getGlobals(settings.prefix, settings.scope)\n : {};\n\n // Build unified event from various elb usage patterns\n const event: WalkerOS.DeepPartialEvent = {\n name: String(eventOrCommand || ''),\n data: eventData,\n context: eventContext,\n globals: eventGlobals,\n nested,\n custom,\n trigger: isString(options) ? options : '',\n source:\n scopeWin && scopeDoc ? getBrowserSource(scopeWin, scopeDoc) : undefined,\n };\n\n return elb(event);\n}\n\n/**\n * Create source information for browser events\n */\nfunction getBrowserSource(win: Window, doc: Document): WalkerOS.Source {\n return {\n type: 'browser',\n platform: 'web',\n url: win.location.href,\n referrer: doc.referrer,\n };\n}\n\n/**\n * Local type guard: narrows `unknown` to `Element | Document`. Prefers the\n * native `instanceof` check (works in browsers and JSDOM) and falls back to\n * the WhatWG DOM `nodeType` property for realms where the global Element or\n * Document constructors are not in scope (cross-frame, certain test\n * runners). Returns an accurate `Element | Document` union so callers do\n * not need casts.\n */\nfunction isDomScope(value: unknown): value is InitScope {\n if (!value || typeof value !== 'object') return false;\n if (typeof Element !== 'undefined' && value instanceof Element) return true;\n if (typeof Document !== 'undefined' && value instanceof Document) return true;\n if (typeof ShadowRoot !== 'undefined' && value instanceof ShadowRoot)\n return true;\n if ('nodeType' in value) {\n const nodeType = value.nodeType;\n // 1 = ELEMENT_NODE, 9 = DOCUMENT_NODE, 11 = DOCUMENT_FRAGMENT_NODE (the\n // node type of a ShadowRoot) per the WhatWG DOM standard. Accepting 11\n // lets `walker init` target a retained closed shadow root, which discovery\n // can never reach from the document.\n return nodeType === 1 || nodeType === 9 || nodeType === 11;\n }\n return false;\n}\n\n/**\n * getGlobals accepts the narrow Scope (Document | Element). Scope is kept narrow\n * deliberately: widening it to ShadowRoot would ripple into its sibling scans\n * getPageViewData/getAllEvents, whose bodies call .matches/.body/getAttribute\n * and throw on a ShadowRoot. So a ShadowRoot init scope contributes no globals\n * ({}) by design; this guard narrows the widened carrier back to what getGlobals\n * accepts.\n */\nfunction isGlobalsScope(scope: InitScope): scope is Scope {\n return scope.nodeType !== 11; // exclude ShadowRoot (DOCUMENT_FRAGMENT_NODE)\n}\n\n/**\n * Normalize the `data` argument of `elb('walker init', data)` to an array\n * of Element/Document/ShadowRoot scopes. Falls back to `settings.scope` (or\n * `document` if settings.scope is not a DOM node) when no scope is provided.\n */\nfunction normalizeInitScopes(\n data: unknown,\n settings: Settings,\n): Array<InitScope> {\n if (isDomScope(data)) return [data];\n if (Array.isArray(data)) {\n const filtered: Array<InitScope> = [];\n for (const entry of data) {\n if (isDomScope(entry)) filtered.push(entry);\n }\n return filtered;\n }\n if (typeof data === 'undefined') {\n const fallback = settings.scope;\n if (isDomScope(fallback)) return [fallback];\n if (typeof globalThis.document !== 'undefined') {\n return [globalThis.document];\n }\n }\n return [];\n}\n"],"mappings":"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","names":["z","z","z","z","tryCatch","Const","getAttribute","trigger","filter","str","trigger","elem","settings","event","scopeDoc","scopeWin","trigger","trigger"]}