@syntrologie/adapt-content 2.25.2 → 2.27.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.
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EAEd,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,aAAa,EACd,MAAM,SAAS,CAAC;AAMjB;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,gBAAgB,CAqI9D,CAAC;AAmBF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,aAAa,CAsCxD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,aAAa,CA6DxD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,cAAc,CAmC1D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,iBAAiB,CAmChE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,cAAc,CAoD1D,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;EAOZ,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;CAMnB,CAAC"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EAEd,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,aAAa,EACd,MAAM,SAAS,CAAC;AAMjB;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,gBAAgB,CAqI9D,CAAC;AAmBF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,aAAa,CAsCxD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,aAAa,CA6DxD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,cAAc,CAmC1D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,iBAAiB,CAmChE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,cAAc,CAgE1D,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;EAOZ,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;CAMnB,CAAC"}
package/dist/runtime.js CHANGED
@@ -393,7 +393,13 @@ var executeSetStyle = async (action, context) => {
393
393
  originalStyles.set(prop, current);
394
394
  }
395
395
  for (const [prop, value] of Object.entries(action.styles)) {
396
- anchorEl.style.setProperty(prop, value);
396
+ const el = anchorEl;
397
+ el.style.setProperty(prop, value);
398
+ if (value !== "" && el.style.getPropertyValue(prop) === "") {
399
+ console.warn(
400
+ `[adaptive-content] setStyle: '${prop}: ${value}' was silently dropped \u2014 '${prop}' is not a recognized CSS property. Use kebab-case (e.g. 'justify-content' not 'justifyContent').`
401
+ );
402
+ }
397
403
  }
398
404
  context.publishEvent("action.applied", {
399
405
  id: context.generateId(),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/reconciliation-guard.ts", "../src/sanitizer.ts", "../src/runtime.ts"],
4
- "sourcesContent": ["/**\n * Reconciliation Guard - MutationObserver defense against React DOM removal.\n *\n * When the Syntrologie SDK inserts DOM nodes into a React-managed subtree\n * (via content:insertHtml), React's reconciliation will silently remove them\n * on the next render because they don't exist in React's virtual DOM.\n *\n * This guard watches for the removal of our inserted container and re-inserts\n * it using a debounced retry mechanism with a maximum retry count to prevent\n * infinite loops (e.g., in React StrictMode which double-invokes effects).\n */\n\nexport interface ReconciliationGuardOptions {\n /** Maximum re-insertion attempts before giving up. Default: 3 */\n maxRetries?: number;\n /** Debounce interval in ms to coalesce rapid removals. Default: 50 */\n debounceMs?: number;\n}\n\n/**\n * Watch for a container element being removed from the DOM by an external\n * framework (React, Vue, etc.) and call `reinsertFn` to re-insert it.\n *\n * @param container The element we inserted (has data-syntro-action-id)\n * @param anchor The anchor element our container is positioned relative to\n * @param reinsertFn Called when the container is removed \u2014 should re-insert it\n * @param opts Configuration for retry limits and debounce timing\n * @returns Cleanup function that disconnects the observer\n */\nexport function guardAgainstReconciliation(\n container: HTMLElement,\n anchor: HTMLElement,\n reinsertFn: () => void,\n opts?: ReconciliationGuardOptions\n): () => void {\n const maxRetries = opts?.maxRetries ?? 3;\n const debounceMs = opts?.debounceMs ?? 50;\n\n // Find the nearest parent to observe. Prefer container's parent, then anchor's.\n const observeTarget = container.parentElement ?? anchor.parentElement;\n if (!observeTarget) return () => {};\n\n let retries = 0;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n let disconnected = false;\n\n const observer = new MutationObserver((mutations) => {\n if (disconnected) return;\n\n for (const mutation of mutations) {\n for (const removed of mutation.removedNodes) {\n // Check if the removed node is our container\n if (removed !== container) continue;\n\n if (retries >= maxRetries) {\n observer.disconnect();\n disconnected = true;\n return;\n }\n\n // Debounce to coalesce rapid React re-renders\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (disconnected) return;\n // If the anchor has been removed from the DOM (SPA navigation),\n // the page has been torn down \u2014 stop fighting React's reconciler.\n // Re-inserting into a dying subtree triggers removeChild crashes.\n if (!anchor.isConnected) {\n observer.disconnect();\n disconnected = true;\n return;\n }\n retries++;\n try {\n reinsertFn();\n } catch {\n // Re-insertion failed \u2014 stop trying\n observer.disconnect();\n disconnected = true;\n }\n }, debounceMs);\n\n return; // Found our container, no need to check further\n }\n }\n });\n\n observer.observe(observeTarget, { childList: true, subtree: true });\n\n return () => {\n disconnected = true;\n observer.disconnect();\n if (debounceTimer) clearTimeout(debounceTimer);\n };\n}\n", "/**\n * HTML Sanitizer\n *\n * Sanitizes HTML to prevent XSS attacks.\n * Uses native Sanitizer API when available, falls back to whitelist approach.\n */\n\nconst ALLOWED_TAGS = new Set([\n 'b',\n 'strong',\n 'i',\n 'em',\n 'u',\n 'span',\n 'div',\n 'p',\n 'br',\n 'ul',\n 'ol',\n 'li',\n 'code',\n 'pre',\n 'small',\n 'sup',\n 'sub',\n 'a',\n 'button',\n // SVG elements (for inline Lucide icons in config HTML)\n 'svg',\n 'path',\n 'circle',\n 'line',\n 'polyline',\n 'polygon',\n 'rect',\n 'g',\n]);\n\nexport function sanitizeHtml(html: string): string {\n // Try native Sanitizer API first\n const hasNative = typeof (window as any).Sanitizer === 'function';\n if (hasNative) {\n try {\n const s = new (window as any).Sanitizer({});\n const frag = s.sanitizeToFragment(html);\n const div = document.createElement('div');\n div.append(frag);\n return div.innerHTML;\n } catch {\n // Fall through to manual sanitizer\n }\n }\n\n // Conservative fallback sanitizer\n const tpl = document.createElement('template');\n tpl.innerHTML = html;\n const root = tpl.content;\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null);\n const toRemove: Element[] = [];\n\n while (walker.nextNode()) {\n const el = walker.currentNode as Element;\n const tag = el.tagName.toLowerCase();\n\n if (!ALLOWED_TAGS.has(tag)) {\n toRemove.push(el);\n continue;\n }\n\n // Remove dangerous attributes\n for (const attr of Array.from(el.attributes)) {\n const name = attr.name.toLowerCase();\n const value = attr.value.trim().toLowerCase();\n const isEvent = name.startsWith('on');\n const isUrlAttr = name === 'href' || name === 'src' || name === 'formaction';\n const isDangerousUrl =\n isUrlAttr &&\n (value.startsWith('javascript:') ||\n value.startsWith('vbscript:') ||\n value.startsWith('data:text/html'));\n\n if (isEvent || isDangerousUrl) {\n el.removeAttribute(attr.name);\n }\n }\n }\n\n // Strip SVG elements that contained script children (XSS vector)\n const svgs = Array.from(root.querySelectorAll('svg'));\n for (const svg of svgs) {\n if (toRemove.some((el) => svg.contains(el) && el.tagName.toLowerCase() === 'script')) {\n toRemove.push(svg);\n }\n }\n\n // Remove disallowed elements but keep their children\n for (const el of toRemove) {\n while (el.firstChild) {\n el.parentNode?.insertBefore(el.firstChild, el);\n }\n el.remove();\n }\n\n return tpl.innerHTML;\n}\n", "/**\n * Adaptive Content - Runtime Module\n *\n * DOM manipulation actions: insertHtml, setText, setAttr, addClass, removeClass, setStyle.\n * These follow the hostPatcher snapshot pattern for safe reversibility.\n */\n\nimport { guardAgainstReconciliation } from './reconciliation-guard';\nimport { sanitizeHtml } from './sanitizer';\nimport type {\n ActionExecutor,\n AddClassAction,\n ExecutorResult,\n InsertHtmlAction,\n RemoveClassAction,\n SetAttrAction,\n SetStyleAction,\n SetTextAction,\n} from './types';\n\n// ============================================================================\n// Executors\n// ============================================================================\n\n/**\n * Execute an insertHtml action\n */\nexport const executeInsertHtml: ActionExecutor<InsertHtmlAction> = async (\n action,\n context\n): Promise<ExecutorResult> => {\n let anchorEl = context.resolveAnchor(action.anchorId);\n if (!anchorEl && context.waitForAnchor) {\n anchorEl = await context.waitForAnchor(action.anchorId, 3000);\n }\n if (!anchorEl) {\n console.warn(`[adaptive-content] Anchor not found after waiting: ${action.anchorId.selector}`);\n return { cleanup: () => {} };\n }\n\n // Sanitize HTML content using context utility\n const sanitizedHtml = sanitizeHtml(action.html);\n\n // Dedup: if a container for this action already exists, remove it first.\n // Uses the action label as a stable identifier across re-applications.\n // Check both inside the anchor (prepend/append) and in the parent (before/after).\n const dedupAttr = 'data-syntro-insert-label';\n const label = action.label;\n if (label) {\n const escapedLabel = CSS.escape(label);\n const searchRoot = anchorEl.parentElement ?? anchorEl;\n const existing = searchRoot.querySelector(`[${dedupAttr}=\"${escapedLabel}\"]`);\n if (existing) existing.remove();\n }\n\n // Create container for inserted content\n const container = document.createElement('div');\n container.setAttribute('data-syntro-action-id', context.generateId());\n if (label) container.setAttribute(dedupAttr, label);\n container.innerHTML = sanitizedHtml;\n\n // Keep track of original state for replace position\n let originalContent: string | null = null;\n\n switch (action.position) {\n case 'before':\n anchorEl.insertAdjacentElement('beforebegin', container);\n break;\n case 'after':\n anchorEl.insertAdjacentElement('afterend', container);\n break;\n case 'prepend':\n anchorEl.insertBefore(container, anchorEl.firstChild);\n break;\n case 'append':\n anchorEl.appendChild(container);\n break;\n case 'replace':\n originalContent = anchorEl.innerHTML;\n anchorEl.replaceWith(container);\n break;\n }\n\n // Deep-link click handler \u2014 opens canvas + publishes deep-link event\n let deepLinkHandler: (() => void) | null = null;\n if (action.deepLink) {\n const { tileId, itemId } = action.deepLink;\n deepLinkHandler = () => {\n const handle = (window as any).SynOS?.handle;\n if (handle) {\n handle.open();\n handle.runtime?.events?.publish('notification.deep_link', { tileId, itemId });\n }\n };\n container.style.cursor = 'pointer';\n container.addEventListener('click', deepLinkHandler);\n }\n\n context.publishEvent('action.applied', {\n id: context.generateId(),\n kind: 'content:insertHtml',\n anchorId: action.anchorId,\n position: action.position,\n });\n\n // Guard against React reconciliation removing our container.\n // The reinsert function re-applies the same insertion strategy.\n const reinsertFn = () => {\n switch (action.position) {\n case 'before':\n anchorEl.insertAdjacentElement('beforebegin', container);\n break;\n case 'after':\n anchorEl.insertAdjacentElement('afterend', container);\n break;\n case 'prepend':\n anchorEl.insertBefore(container, anchorEl.firstChild);\n break;\n case 'append':\n anchorEl.appendChild(container);\n break;\n case 'replace':\n // Cannot re-insert for replace \u2014 anchor was already replaced\n break;\n }\n };\n\n const guardCleanup = guardAgainstReconciliation(container, anchorEl, reinsertFn);\n\n return {\n cleanup: () => {\n if (deepLinkHandler) {\n container.removeEventListener('click', deepLinkHandler);\n }\n guardCleanup();\n // Skip DOM mutations if nodes are already detached (SPA navigation)\n if (!container.isConnected) return;\n try {\n if (action.position === 'replace' && originalContent !== null) {\n // Restore original element\n const restoredEl = document.createElement(anchorEl.tagName);\n restoredEl.innerHTML = originalContent;\n // Copy attributes\n Array.from(anchorEl.attributes).forEach((attr) => {\n restoredEl.setAttribute(attr.name, attr.value);\n });\n container.replaceWith(restoredEl);\n } else {\n container.remove();\n }\n } catch {\n // DOM nodes already removed by host framework \u2014 safe to ignore\n }\n },\n updateFn: (changes) => {\n if ('html' in changes && typeof changes.html === 'string') {\n container.innerHTML = sanitizeHtml(changes.html);\n }\n },\n };\n};\n\n/**\n * Walk the DOM to find the deepest descendant that uniquely carries\n * text content. Avoids destroying sibling elements (icons, images)\n * when setting text on a container like a button.\n */\nfunction findTextTarget(el: Element): Element {\n if (el.children.length === 0) return el;\n const textChildren = Array.from(el.children).filter((child) => child.textContent?.trim());\n if (textChildren.length === 1) {\n const child = textChildren[0];\n if (child.textContent?.trim() === el.textContent?.trim()) {\n return findTextTarget(child);\n }\n }\n return el;\n}\n\n/**\n * Execute a setText action\n */\nexport const executeSetText: ActionExecutor<SetTextAction> = async (\n action,\n context\n): Promise<ExecutorResult> => {\n let anchorEl = context.resolveAnchor(action.anchorId);\n if (!anchorEl && context.waitForAnchor) {\n anchorEl = await context.waitForAnchor(action.anchorId, 3000);\n }\n if (!anchorEl) {\n console.warn(`[adaptive-content] Anchor not found after waiting: ${action.anchorId.selector}`);\n return { cleanup: () => {} };\n }\n\n const textTarget = findTextTarget(anchorEl);\n\n // Snapshot original text\n const originalText = textTarget.textContent ?? '';\n\n // Set new text\n textTarget.textContent = action.text;\n\n context.publishEvent('action.applied', {\n id: context.generateId(),\n kind: 'content:setText',\n anchorId: action.anchorId,\n });\n\n return {\n cleanup: () => {\n if (!anchorEl.isConnected) return;\n textTarget.textContent = originalText;\n },\n updateFn: (changes) => {\n if ('text' in changes && typeof changes.text === 'string') {\n textTarget.textContent = changes.text;\n }\n },\n };\n};\n\n/**\n * Execute a setAttr action\n */\nexport const executeSetAttr: ActionExecutor<SetAttrAction> = async (\n action,\n context\n): Promise<ExecutorResult> => {\n let anchorEl = context.resolveAnchor(action.anchorId);\n if (!anchorEl && context.waitForAnchor) {\n anchorEl = await context.waitForAnchor(action.anchorId, 3000);\n }\n if (!anchorEl) {\n console.warn(`[adaptive-content] Anchor not found after waiting: ${action.anchorId.selector}`);\n return { cleanup: () => {} };\n }\n\n // Block dangerous attributes (case-insensitive)\n const lowerAttr = action.attr.toLowerCase();\n if (lowerAttr.startsWith('on')) {\n throw new Error(`Dangerous attribute not allowed: ${action.attr}`);\n }\n\n // Block dangerous URIs in URL-bearing attributes\n const isUrlAttr = lowerAttr === 'href' || lowerAttr === 'src' || lowerAttr === 'formaction';\n if (isUrlAttr) {\n const lowerValue = action.value.trim().toLowerCase();\n if (\n lowerValue.startsWith('javascript:') ||\n lowerValue.startsWith('vbscript:') ||\n lowerValue.startsWith('data:text/html')\n ) {\n throw new Error(`Dangerous URL not allowed in ${action.attr}: ${action.value}`);\n }\n }\n\n // Snapshot original attribute value\n const originalValue = anchorEl.getAttribute(action.attr);\n const hadAttribute = anchorEl.hasAttribute(action.attr);\n\n // Set new attribute\n anchorEl.setAttribute(action.attr, action.value);\n\n context.publishEvent('action.applied', {\n id: context.generateId(),\n kind: 'content:setAttr',\n anchorId: action.anchorId,\n attr: action.attr,\n });\n\n return {\n cleanup: () => {\n if (!anchorEl.isConnected) return;\n if (hadAttribute && originalValue !== null) {\n anchorEl.setAttribute(action.attr, originalValue);\n } else {\n anchorEl.removeAttribute(action.attr);\n }\n },\n updateFn: (changes) => {\n if ('value' in changes && typeof changes.value === 'string') {\n anchorEl.setAttribute(action.attr, changes.value);\n }\n },\n };\n};\n\n/**\n * Execute an addClass action\n */\nexport const executeAddClass: ActionExecutor<AddClassAction> = async (\n action,\n context\n): Promise<ExecutorResult> => {\n let anchorEl = context.resolveAnchor(action.anchorId);\n if (!anchorEl && context.waitForAnchor) {\n anchorEl = await context.waitForAnchor(action.anchorId, 3000);\n }\n if (!anchorEl) {\n console.warn(`[adaptive-content] Anchor not found after waiting: ${action.anchorId.selector}`);\n return { cleanup: () => {} };\n }\n\n // Check if class was already present\n const hadClass = anchorEl.classList.contains(action.className);\n\n // Add class\n anchorEl.classList.add(action.className);\n\n context.publishEvent('action.applied', {\n id: context.generateId(),\n kind: 'content:addClass',\n anchorId: action.anchorId,\n className: action.className,\n });\n\n return {\n cleanup: () => {\n if (!anchorEl.isConnected) return;\n // Only remove if we added it\n if (!hadClass) {\n anchorEl.classList.remove(action.className);\n }\n },\n };\n};\n\n/**\n * Execute a removeClass action\n */\nexport const executeRemoveClass: ActionExecutor<RemoveClassAction> = async (\n action,\n context\n): Promise<ExecutorResult> => {\n let anchorEl = context.resolveAnchor(action.anchorId);\n if (!anchorEl && context.waitForAnchor) {\n anchorEl = await context.waitForAnchor(action.anchorId, 3000);\n }\n if (!anchorEl) {\n console.warn(`[adaptive-content] Anchor not found after waiting: ${action.anchorId.selector}`);\n return { cleanup: () => {} };\n }\n\n // Check if class was present\n const hadClass = anchorEl.classList.contains(action.className);\n\n // Remove class\n anchorEl.classList.remove(action.className);\n\n context.publishEvent('action.applied', {\n id: context.generateId(),\n kind: 'content:removeClass',\n anchorId: action.anchorId,\n className: action.className,\n });\n\n return {\n cleanup: () => {\n if (!anchorEl.isConnected) return;\n // Only re-add if we removed it\n if (hadClass) {\n anchorEl.classList.add(action.className);\n }\n },\n };\n};\n\n/**\n * Execute a setStyle action\n */\nexport const executeSetStyle: ActionExecutor<SetStyleAction> = async (\n action,\n context\n): Promise<ExecutorResult> => {\n let anchorEl = context.resolveAnchor(action.anchorId);\n if (!anchorEl && context.waitForAnchor) {\n anchorEl = await context.waitForAnchor(action.anchorId, 3000);\n }\n if (!anchorEl) {\n console.warn(`[adaptive-content] Anchor not found after waiting: ${action.anchorId.selector}`);\n return { cleanup: () => {} };\n }\n\n // Snapshot original styles\n const originalStyles = new Map<string, string>();\n for (const prop of Object.keys(action.styles)) {\n const current = (anchorEl as HTMLElement).style.getPropertyValue(prop);\n originalStyles.set(prop, current);\n }\n\n // Apply new styles\n for (const [prop, value] of Object.entries(action.styles)) {\n (anchorEl as HTMLElement).style.setProperty(prop, value);\n }\n\n context.publishEvent('action.applied', {\n id: context.generateId(),\n kind: 'content:setStyle',\n anchorId: action.anchorId,\n styles: Object.keys(action.styles),\n });\n\n return {\n cleanup: () => {\n if (!anchorEl.isConnected) return;\n // Restore original styles\n for (const [prop, originalValue] of originalStyles) {\n if (originalValue) {\n (anchorEl as HTMLElement).style.setProperty(prop, originalValue);\n } else {\n (anchorEl as HTMLElement).style.removeProperty(prop);\n }\n }\n },\n updateFn: (changes) => {\n if ('styles' in changes && typeof changes.styles === 'object' && changes.styles) {\n for (const [prop, value] of Object.entries(changes.styles as Record<string, string>)) {\n (anchorEl as HTMLElement).style.setProperty(prop, value);\n }\n }\n },\n };\n};\n\n// ============================================================================\n// Executor Definitions for Registration\n// ============================================================================\n\n/**\n * All executors provided by this app.\n * These are registered with the runtime's ExecutorRegistry.\n */\nexport const executors = [\n { kind: 'content:insertHtml', executor: executeInsertHtml },\n { kind: 'content:setText', executor: executeSetText },\n { kind: 'content:setAttr', executor: executeSetAttr },\n { kind: 'content:addClass', executor: executeAddClass },\n { kind: 'content:removeClass', executor: executeRemoveClass },\n { kind: 'content:setStyle', executor: executeSetStyle },\n] as const;\n\n/**\n * App runtime manifest.\n */\nexport const runtime = {\n id: 'adaptive-content',\n version: '1.0.0',\n name: 'Content',\n description: 'DOM manipulation for text, attributes, and styles',\n executors,\n};\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Reconciliation Guard - MutationObserver defense against React DOM removal.\n *\n * When the Syntrologie SDK inserts DOM nodes into a React-managed subtree\n * (via content:insertHtml), React's reconciliation will silently remove them\n * on the next render because they don't exist in React's virtual DOM.\n *\n * This guard watches for the removal of our inserted container and re-inserts\n * it using a debounced retry mechanism with a maximum retry count to prevent\n * infinite loops (e.g., in React StrictMode which double-invokes effects).\n */\n\nexport interface ReconciliationGuardOptions {\n /** Maximum re-insertion attempts before giving up. Default: 3 */\n maxRetries?: number;\n /** Debounce interval in ms to coalesce rapid removals. Default: 50 */\n debounceMs?: number;\n}\n\n/**\n * Watch for a container element being removed from the DOM by an external\n * framework (React, Vue, etc.) and call `reinsertFn` to re-insert it.\n *\n * @param container The element we inserted (has data-syntro-action-id)\n * @param anchor The anchor element our container is positioned relative to\n * @param reinsertFn Called when the container is removed \u2014 should re-insert it\n * @param opts Configuration for retry limits and debounce timing\n * @returns Cleanup function that disconnects the observer\n */\nexport function guardAgainstReconciliation(\n container: HTMLElement,\n anchor: HTMLElement,\n reinsertFn: () => void,\n opts?: ReconciliationGuardOptions\n): () => void {\n const maxRetries = opts?.maxRetries ?? 3;\n const debounceMs = opts?.debounceMs ?? 50;\n\n // Find the nearest parent to observe. Prefer container's parent, then anchor's.\n const observeTarget = container.parentElement ?? anchor.parentElement;\n if (!observeTarget) return () => {};\n\n let retries = 0;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n let disconnected = false;\n\n const observer = new MutationObserver((mutations) => {\n if (disconnected) return;\n\n for (const mutation of mutations) {\n for (const removed of mutation.removedNodes) {\n // Check if the removed node is our container\n if (removed !== container) continue;\n\n if (retries >= maxRetries) {\n observer.disconnect();\n disconnected = true;\n return;\n }\n\n // Debounce to coalesce rapid React re-renders\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (disconnected) return;\n // If the anchor has been removed from the DOM (SPA navigation),\n // the page has been torn down \u2014 stop fighting React's reconciler.\n // Re-inserting into a dying subtree triggers removeChild crashes.\n if (!anchor.isConnected) {\n observer.disconnect();\n disconnected = true;\n return;\n }\n retries++;\n try {\n reinsertFn();\n } catch {\n // Re-insertion failed \u2014 stop trying\n observer.disconnect();\n disconnected = true;\n }\n }, debounceMs);\n\n return; // Found our container, no need to check further\n }\n }\n });\n\n observer.observe(observeTarget, { childList: true, subtree: true });\n\n return () => {\n disconnected = true;\n observer.disconnect();\n if (debounceTimer) clearTimeout(debounceTimer);\n };\n}\n", "/**\n * HTML Sanitizer\n *\n * Sanitizes HTML to prevent XSS attacks.\n * Uses native Sanitizer API when available, falls back to whitelist approach.\n */\n\nconst ALLOWED_TAGS = new Set([\n 'b',\n 'strong',\n 'i',\n 'em',\n 'u',\n 'span',\n 'div',\n 'p',\n 'br',\n 'ul',\n 'ol',\n 'li',\n 'code',\n 'pre',\n 'small',\n 'sup',\n 'sub',\n 'a',\n 'button',\n // SVG elements (for inline Lucide icons in config HTML)\n 'svg',\n 'path',\n 'circle',\n 'line',\n 'polyline',\n 'polygon',\n 'rect',\n 'g',\n]);\n\nexport function sanitizeHtml(html: string): string {\n // Try native Sanitizer API first\n const hasNative = typeof (window as any).Sanitizer === 'function';\n if (hasNative) {\n try {\n const s = new (window as any).Sanitizer({});\n const frag = s.sanitizeToFragment(html);\n const div = document.createElement('div');\n div.append(frag);\n return div.innerHTML;\n } catch {\n // Fall through to manual sanitizer\n }\n }\n\n // Conservative fallback sanitizer\n const tpl = document.createElement('template');\n tpl.innerHTML = html;\n const root = tpl.content;\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null);\n const toRemove: Element[] = [];\n\n while (walker.nextNode()) {\n const el = walker.currentNode as Element;\n const tag = el.tagName.toLowerCase();\n\n if (!ALLOWED_TAGS.has(tag)) {\n toRemove.push(el);\n continue;\n }\n\n // Remove dangerous attributes\n for (const attr of Array.from(el.attributes)) {\n const name = attr.name.toLowerCase();\n const value = attr.value.trim().toLowerCase();\n const isEvent = name.startsWith('on');\n const isUrlAttr = name === 'href' || name === 'src' || name === 'formaction';\n const isDangerousUrl =\n isUrlAttr &&\n (value.startsWith('javascript:') ||\n value.startsWith('vbscript:') ||\n value.startsWith('data:text/html'));\n\n if (isEvent || isDangerousUrl) {\n el.removeAttribute(attr.name);\n }\n }\n }\n\n // Strip SVG elements that contained script children (XSS vector)\n const svgs = Array.from(root.querySelectorAll('svg'));\n for (const svg of svgs) {\n if (toRemove.some((el) => svg.contains(el) && el.tagName.toLowerCase() === 'script')) {\n toRemove.push(svg);\n }\n }\n\n // Remove disallowed elements but keep their children\n for (const el of toRemove) {\n while (el.firstChild) {\n el.parentNode?.insertBefore(el.firstChild, el);\n }\n el.remove();\n }\n\n return tpl.innerHTML;\n}\n", "/**\n * Adaptive Content - Runtime Module\n *\n * DOM manipulation actions: insertHtml, setText, setAttr, addClass, removeClass, setStyle.\n * These follow the hostPatcher snapshot pattern for safe reversibility.\n */\n\nimport { guardAgainstReconciliation } from './reconciliation-guard';\nimport { sanitizeHtml } from './sanitizer';\nimport type {\n ActionExecutor,\n AddClassAction,\n ExecutorResult,\n InsertHtmlAction,\n RemoveClassAction,\n SetAttrAction,\n SetStyleAction,\n SetTextAction,\n} from './types';\n\n// ============================================================================\n// Executors\n// ============================================================================\n\n/**\n * Execute an insertHtml action\n */\nexport const executeInsertHtml: ActionExecutor<InsertHtmlAction> = async (\n action,\n context\n): Promise<ExecutorResult> => {\n let anchorEl = context.resolveAnchor(action.anchorId);\n if (!anchorEl && context.waitForAnchor) {\n anchorEl = await context.waitForAnchor(action.anchorId, 3000);\n }\n if (!anchorEl) {\n console.warn(`[adaptive-content] Anchor not found after waiting: ${action.anchorId.selector}`);\n return { cleanup: () => {} };\n }\n\n // Sanitize HTML content using context utility\n const sanitizedHtml = sanitizeHtml(action.html);\n\n // Dedup: if a container for this action already exists, remove it first.\n // Uses the action label as a stable identifier across re-applications.\n // Check both inside the anchor (prepend/append) and in the parent (before/after).\n const dedupAttr = 'data-syntro-insert-label';\n const label = action.label;\n if (label) {\n const escapedLabel = CSS.escape(label);\n const searchRoot = anchorEl.parentElement ?? anchorEl;\n const existing = searchRoot.querySelector(`[${dedupAttr}=\"${escapedLabel}\"]`);\n if (existing) existing.remove();\n }\n\n // Create container for inserted content\n const container = document.createElement('div');\n container.setAttribute('data-syntro-action-id', context.generateId());\n if (label) container.setAttribute(dedupAttr, label);\n container.innerHTML = sanitizedHtml;\n\n // Keep track of original state for replace position\n let originalContent: string | null = null;\n\n switch (action.position) {\n case 'before':\n anchorEl.insertAdjacentElement('beforebegin', container);\n break;\n case 'after':\n anchorEl.insertAdjacentElement('afterend', container);\n break;\n case 'prepend':\n anchorEl.insertBefore(container, anchorEl.firstChild);\n break;\n case 'append':\n anchorEl.appendChild(container);\n break;\n case 'replace':\n originalContent = anchorEl.innerHTML;\n anchorEl.replaceWith(container);\n break;\n }\n\n // Deep-link click handler \u2014 opens canvas + publishes deep-link event\n let deepLinkHandler: (() => void) | null = null;\n if (action.deepLink) {\n const { tileId, itemId } = action.deepLink;\n deepLinkHandler = () => {\n const handle = (window as any).SynOS?.handle;\n if (handle) {\n handle.open();\n handle.runtime?.events?.publish('notification.deep_link', { tileId, itemId });\n }\n };\n container.style.cursor = 'pointer';\n container.addEventListener('click', deepLinkHandler);\n }\n\n context.publishEvent('action.applied', {\n id: context.generateId(),\n kind: 'content:insertHtml',\n anchorId: action.anchorId,\n position: action.position,\n });\n\n // Guard against React reconciliation removing our container.\n // The reinsert function re-applies the same insertion strategy.\n const reinsertFn = () => {\n switch (action.position) {\n case 'before':\n anchorEl.insertAdjacentElement('beforebegin', container);\n break;\n case 'after':\n anchorEl.insertAdjacentElement('afterend', container);\n break;\n case 'prepend':\n anchorEl.insertBefore(container, anchorEl.firstChild);\n break;\n case 'append':\n anchorEl.appendChild(container);\n break;\n case 'replace':\n // Cannot re-insert for replace \u2014 anchor was already replaced\n break;\n }\n };\n\n const guardCleanup = guardAgainstReconciliation(container, anchorEl, reinsertFn);\n\n return {\n cleanup: () => {\n if (deepLinkHandler) {\n container.removeEventListener('click', deepLinkHandler);\n }\n guardCleanup();\n // Skip DOM mutations if nodes are already detached (SPA navigation)\n if (!container.isConnected) return;\n try {\n if (action.position === 'replace' && originalContent !== null) {\n // Restore original element\n const restoredEl = document.createElement(anchorEl.tagName);\n restoredEl.innerHTML = originalContent;\n // Copy attributes\n Array.from(anchorEl.attributes).forEach((attr) => {\n restoredEl.setAttribute(attr.name, attr.value);\n });\n container.replaceWith(restoredEl);\n } else {\n container.remove();\n }\n } catch {\n // DOM nodes already removed by host framework \u2014 safe to ignore\n }\n },\n updateFn: (changes) => {\n if ('html' in changes && typeof changes.html === 'string') {\n container.innerHTML = sanitizeHtml(changes.html);\n }\n },\n };\n};\n\n/**\n * Walk the DOM to find the deepest descendant that uniquely carries\n * text content. Avoids destroying sibling elements (icons, images)\n * when setting text on a container like a button.\n */\nfunction findTextTarget(el: Element): Element {\n if (el.children.length === 0) return el;\n const textChildren = Array.from(el.children).filter((child) => child.textContent?.trim());\n if (textChildren.length === 1) {\n const child = textChildren[0];\n if (child.textContent?.trim() === el.textContent?.trim()) {\n return findTextTarget(child);\n }\n }\n return el;\n}\n\n/**\n * Execute a setText action\n */\nexport const executeSetText: ActionExecutor<SetTextAction> = async (\n action,\n context\n): Promise<ExecutorResult> => {\n let anchorEl = context.resolveAnchor(action.anchorId);\n if (!anchorEl && context.waitForAnchor) {\n anchorEl = await context.waitForAnchor(action.anchorId, 3000);\n }\n if (!anchorEl) {\n console.warn(`[adaptive-content] Anchor not found after waiting: ${action.anchorId.selector}`);\n return { cleanup: () => {} };\n }\n\n const textTarget = findTextTarget(anchorEl);\n\n // Snapshot original text\n const originalText = textTarget.textContent ?? '';\n\n // Set new text\n textTarget.textContent = action.text;\n\n context.publishEvent('action.applied', {\n id: context.generateId(),\n kind: 'content:setText',\n anchorId: action.anchorId,\n });\n\n return {\n cleanup: () => {\n if (!anchorEl.isConnected) return;\n textTarget.textContent = originalText;\n },\n updateFn: (changes) => {\n if ('text' in changes && typeof changes.text === 'string') {\n textTarget.textContent = changes.text;\n }\n },\n };\n};\n\n/**\n * Execute a setAttr action\n */\nexport const executeSetAttr: ActionExecutor<SetAttrAction> = async (\n action,\n context\n): Promise<ExecutorResult> => {\n let anchorEl = context.resolveAnchor(action.anchorId);\n if (!anchorEl && context.waitForAnchor) {\n anchorEl = await context.waitForAnchor(action.anchorId, 3000);\n }\n if (!anchorEl) {\n console.warn(`[adaptive-content] Anchor not found after waiting: ${action.anchorId.selector}`);\n return { cleanup: () => {} };\n }\n\n // Block dangerous attributes (case-insensitive)\n const lowerAttr = action.attr.toLowerCase();\n if (lowerAttr.startsWith('on')) {\n throw new Error(`Dangerous attribute not allowed: ${action.attr}`);\n }\n\n // Block dangerous URIs in URL-bearing attributes\n const isUrlAttr = lowerAttr === 'href' || lowerAttr === 'src' || lowerAttr === 'formaction';\n if (isUrlAttr) {\n const lowerValue = action.value.trim().toLowerCase();\n if (\n lowerValue.startsWith('javascript:') ||\n lowerValue.startsWith('vbscript:') ||\n lowerValue.startsWith('data:text/html')\n ) {\n throw new Error(`Dangerous URL not allowed in ${action.attr}: ${action.value}`);\n }\n }\n\n // Snapshot original attribute value\n const originalValue = anchorEl.getAttribute(action.attr);\n const hadAttribute = anchorEl.hasAttribute(action.attr);\n\n // Set new attribute\n anchorEl.setAttribute(action.attr, action.value);\n\n context.publishEvent('action.applied', {\n id: context.generateId(),\n kind: 'content:setAttr',\n anchorId: action.anchorId,\n attr: action.attr,\n });\n\n return {\n cleanup: () => {\n if (!anchorEl.isConnected) return;\n if (hadAttribute && originalValue !== null) {\n anchorEl.setAttribute(action.attr, originalValue);\n } else {\n anchorEl.removeAttribute(action.attr);\n }\n },\n updateFn: (changes) => {\n if ('value' in changes && typeof changes.value === 'string') {\n anchorEl.setAttribute(action.attr, changes.value);\n }\n },\n };\n};\n\n/**\n * Execute an addClass action\n */\nexport const executeAddClass: ActionExecutor<AddClassAction> = async (\n action,\n context\n): Promise<ExecutorResult> => {\n let anchorEl = context.resolveAnchor(action.anchorId);\n if (!anchorEl && context.waitForAnchor) {\n anchorEl = await context.waitForAnchor(action.anchorId, 3000);\n }\n if (!anchorEl) {\n console.warn(`[adaptive-content] Anchor not found after waiting: ${action.anchorId.selector}`);\n return { cleanup: () => {} };\n }\n\n // Check if class was already present\n const hadClass = anchorEl.classList.contains(action.className);\n\n // Add class\n anchorEl.classList.add(action.className);\n\n context.publishEvent('action.applied', {\n id: context.generateId(),\n kind: 'content:addClass',\n anchorId: action.anchorId,\n className: action.className,\n });\n\n return {\n cleanup: () => {\n if (!anchorEl.isConnected) return;\n // Only remove if we added it\n if (!hadClass) {\n anchorEl.classList.remove(action.className);\n }\n },\n };\n};\n\n/**\n * Execute a removeClass action\n */\nexport const executeRemoveClass: ActionExecutor<RemoveClassAction> = async (\n action,\n context\n): Promise<ExecutorResult> => {\n let anchorEl = context.resolveAnchor(action.anchorId);\n if (!anchorEl && context.waitForAnchor) {\n anchorEl = await context.waitForAnchor(action.anchorId, 3000);\n }\n if (!anchorEl) {\n console.warn(`[adaptive-content] Anchor not found after waiting: ${action.anchorId.selector}`);\n return { cleanup: () => {} };\n }\n\n // Check if class was present\n const hadClass = anchorEl.classList.contains(action.className);\n\n // Remove class\n anchorEl.classList.remove(action.className);\n\n context.publishEvent('action.applied', {\n id: context.generateId(),\n kind: 'content:removeClass',\n anchorId: action.anchorId,\n className: action.className,\n });\n\n return {\n cleanup: () => {\n if (!anchorEl.isConnected) return;\n // Only re-add if we removed it\n if (hadClass) {\n anchorEl.classList.add(action.className);\n }\n },\n };\n};\n\n/**\n * Execute a setStyle action\n */\nexport const executeSetStyle: ActionExecutor<SetStyleAction> = async (\n action,\n context\n): Promise<ExecutorResult> => {\n let anchorEl = context.resolveAnchor(action.anchorId);\n if (!anchorEl && context.waitForAnchor) {\n anchorEl = await context.waitForAnchor(action.anchorId, 3000);\n }\n if (!anchorEl) {\n console.warn(`[adaptive-content] Anchor not found after waiting: ${action.anchorId.selector}`);\n return { cleanup: () => {} };\n }\n\n // Snapshot original styles\n const originalStyles = new Map<string, string>();\n for (const prop of Object.keys(action.styles)) {\n const current = (anchorEl as HTMLElement).style.getPropertyValue(prop);\n originalStyles.set(prop, current);\n }\n\n // Apply new styles\n //\n // setProperty() silently no-ops on invalid CSS property names (camelCase keys\n // like `justifyContent`, typos, etc). Validation rejects these at config-load\n // time, but in case a malformed config slips through we read back the value\n // and warn so the failure isn't invisible. (BUG-1779388834)\n for (const [prop, value] of Object.entries(action.styles)) {\n const el = anchorEl as HTMLElement;\n el.style.setProperty(prop, value);\n if (value !== '' && el.style.getPropertyValue(prop) === '') {\n console.warn(\n `[adaptive-content] setStyle: '${prop}: ${value}' was silently dropped \u2014 ` +\n `'${prop}' is not a recognized CSS property. Use kebab-case (e.g. 'justify-content' not 'justifyContent').`\n );\n }\n }\n\n context.publishEvent('action.applied', {\n id: context.generateId(),\n kind: 'content:setStyle',\n anchorId: action.anchorId,\n styles: Object.keys(action.styles),\n });\n\n return {\n cleanup: () => {\n if (!anchorEl.isConnected) return;\n // Restore original styles\n for (const [prop, originalValue] of originalStyles) {\n if (originalValue) {\n (anchorEl as HTMLElement).style.setProperty(prop, originalValue);\n } else {\n (anchorEl as HTMLElement).style.removeProperty(prop);\n }\n }\n },\n updateFn: (changes) => {\n if ('styles' in changes && typeof changes.styles === 'object' && changes.styles) {\n for (const [prop, value] of Object.entries(changes.styles as Record<string, string>)) {\n (anchorEl as HTMLElement).style.setProperty(prop, value);\n }\n }\n },\n };\n};\n\n// ============================================================================\n// Executor Definitions for Registration\n// ============================================================================\n\n/**\n * All executors provided by this app.\n * These are registered with the runtime's ExecutorRegistry.\n */\nexport const executors = [\n { kind: 'content:insertHtml', executor: executeInsertHtml },\n { kind: 'content:setText', executor: executeSetText },\n { kind: 'content:setAttr', executor: executeSetAttr },\n { kind: 'content:addClass', executor: executeAddClass },\n { kind: 'content:removeClass', executor: executeRemoveClass },\n { kind: 'content:setStyle', executor: executeSetStyle },\n] as const;\n\n/**\n * App runtime manifest.\n */\nexport const runtime = {\n id: 'adaptive-content',\n version: '1.0.0',\n name: 'Content',\n description: 'DOM manipulation for text, attributes, and styles',\n executors,\n};\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,eAAO,MAAM,YAAY;IAErB,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAwBxB,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA+B9B,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA0B1B,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAqDtB,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA8BtB,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgCtB,CAAC;AAEJ,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAMzD,eAAO,MAAM,0BAA0B;;;;;;;;;;;;CAyBtC,CAAC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,eAAO,MAAM,YAAY;IAErB,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAwBxB,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA+B9B,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAoC1B,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAqDtB,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA8BtB,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgCtB,CAAC;AAEJ,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAMzD,eAAO,MAAM,0BAA0B;;;;;;;;;;;;CAyBtC,CAAC"}
package/dist/schema.js CHANGED
@@ -5,6 +5,7 @@ var AnchorIdZ = z.object({
5
5
  route: z.union([z.string(), z.array(z.string())])
6
6
  }).strict();
7
7
  var AuthoringFieldsZ = {
8
+ id: z.string().optional().describe('Stable action identifier (e.g. "act_3db6a14d2ab0").'),
8
9
  title: z.string().max(200).optional().describe("Authoring-only: short label shown on the action plan dashboard. Stripped before serving to the runtime SDK."),
9
10
  description: z.string().max(1e3).optional().describe("Authoring-only: one-sentence explanation of what this action does and why. Stripped before serving to the runtime SDK."),
10
11
  validation: z.array(z.string().max(500)).max(10).optional().describe("Authoring-only: ordered steps a reviewer can follow to trigger this action and visually confirm it works. Each entry is one step. Stripped before serving to the runtime SDK.")
@@ -207,7 +208,15 @@ var configSchema = z2.object({
207
208
  summary: z2.string().describe(
208
209
  "Human-readable description of this change, used for logging and debugging."
209
210
  ),
210
- styles: z2.record(z2.string()).describe(
211
+ styles: z2.record(
212
+ // CSS property names must be kebab-case — `element.style.setProperty()`
213
+ // silently drops camelCase keys at runtime. (BUG-1779388834)
214
+ z2.string().regex(
215
+ /^(?:-?[a-z][a-z0-9-]*|--[a-zA-Z0-9_-]+)$/,
216
+ "Invalid CSS property name. Use kebab-case (e.g. 'font-size', not 'fontSize')."
217
+ ),
218
+ z2.string()
219
+ ).describe(
211
220
  'Map of CSS property \u2192 value pairs applied as inline styles (e.g. {"background-color": "#1e40af", "padding": "2rem"}).'
212
221
  )
213
222
  }).describe("A single style change: sets inline CSS properties on a matched element.")
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../sdk-contracts/dist/schemas.js", "../src/schema.ts"],
4
- "sourcesContent": ["/**\n * Shared Zod schemas for decision strategies, conditions, and event scoping.\n *\n * These are the canonical definitions \u2014 runtime-sdk and all adaptive packages\n * should import from here instead of duplicating.\n */\nimport { z } from 'zod';\n// =============================================================================\n// ANCHOR ID SCHEMA\n// =============================================================================\nexport const AnchorIdZ = z\n .object({\n selector: z.string(),\n route: z.union([z.string(), z.array(z.string())]),\n})\n .strict();\n// =============================================================================\n// AUTHORING FIELDS \u2014 title / description / validation\n//\n// These live on every action *during authoring* (LLM produces them; dashboard\n// displays them; reviewers QA-verify with them) but are stripped before the\n// runtime SDK receives the config. Stripping happens server-side in\n// `to_runtime_config` (platform/backend/app/domains/experiments/helpers.py).\n// They appear in the JSON Schema (and therefore in the tactician's prompt)\n// because the LLM needs to know they are valid action properties \u2014 otherwise\n// schema validation would reject what the prompt commands.\n//\n// Each action variant should `.extend(AuthoringFieldsZ)` alongside any\n// triggerWhen/condition extensions. Validation is an ordered list of\n// human-readable step strings \u2014 reviewers scan it as a checklist.\n// =============================================================================\nexport const AuthoringFieldsZ = {\n title: z\n .string()\n .max(200)\n .optional()\n .describe('Authoring-only: short label shown on the action plan dashboard. Stripped before serving to the runtime SDK.'),\n description: z\n .string()\n .max(1000)\n .optional()\n .describe('Authoring-only: one-sentence explanation of what this action does and why. Stripped before serving to the runtime SDK.'),\n validation: z\n .array(z.string().max(500))\n .max(10)\n .optional()\n .describe('Authoring-only: ordered steps a reviewer can follow to trigger this action and visually confirm it works. Each entry is one step. Stripped before serving to the runtime SDK.'),\n};\n// =============================================================================\n// TRIGGER VOCABULARY \u2014 canonical lists of valid event names, metric keys, etc.\n// These flow through to the JSON schema as enums and are used by the LLM prompt.\n// =============================================================================\n/** Events that can be counted in event_count conditions. */\nexport const COUNTABLE_EVENTS = [\n // User interactions (from PostHog autocapture normalization)\n 'ui.click',\n 'ui.scroll',\n 'ui.input',\n 'ui.change',\n 'ui.submit',\n // Behavioral detectors (from event-processor)\n 'ui.hover',\n 'ui.idle',\n 'ui.scroll_thrash',\n 'ui.focus_bounce',\n // Navigation\n 'nav.page_view',\n 'nav.page_leave',\n // Derived behavioral signals\n 'behavior.rage_click',\n 'behavior.hesitation',\n 'behavior.confusion',\n];\nexport const CountableEventZ = z\n .enum(COUNTABLE_EVENTS)\n .describe('Event name to count. ui.* = user interactions and behavioral detectors, nav.* = page navigation, behavior.* = derived behavioral signals.');\n/** Valid session metric keys. */\nexport const SESSION_METRIC_KEYS = ['time_on_page', 'page_views', 'scroll_depth'];\nexport const SessionMetricKeyZ = z\n .enum(SESSION_METRIC_KEYS)\n .describe('Session metric key. time_on_page = seconds on current page, page_views = pages visited this session, scroll_depth = 0-100 percentage.');\n/** Element chain match field prefixes for counter filters. */\nexport const ELEMENT_MATCH_FIELDS = ['tag_name', '$el_text'];\n// Note: attr__* is a dynamic prefix (attr__data-id, attr__class, attr__href, etc.)\n// and cannot be enumerated. The match key is either one of ELEMENT_MATCH_FIELDS\n// or starts with \"attr__\".\n// =============================================================================\n// CONDITION SCHEMAS\n// =============================================================================\nexport const PageUrlConditionZ = z\n .object({\n type: z.literal('page_url'),\n url: z.string().describe('URL path to match (e.g. \"/pricing\", \"/dashboard\")'),\n})\n .describe('Fires when the current page URL matches. Use for page-specific actions. ' +\n 'Example: {\"type\": \"page_url\", \"url\": \"/pricing\"}');\nexport const RouteConditionZ = z\n .object({\n type: z.literal('route'),\n routeId: z.string().describe('Named route ID from the route filter'),\n})\n .describe('Fires when the current route matches a named route ID.');\nexport const AnchorVisibleConditionZ = z\n .object({\n type: z.literal('anchor_visible'),\n anchorId: z.string().describe('CSS selector of the anchor element'),\n state: z\n .enum(['visible', 'present', 'absent'])\n .describe('\"visible\" = in viewport, \"present\" = in DOM, \"absent\" = not in DOM'),\n})\n .describe(\"Fires based on a DOM element's visibility state. \" +\n 'Example: {\"type\": \"anchor_visible\", \"anchorId\": \"#cta-button\", \"state\": \"visible\"}');\nexport const EventOccurredConditionZ = z\n .object({\n type: z.literal('event_occurred'),\n eventName: z.string().describe('Event name (e.g. \"ui.click\", \"$pageview\")'),\n withinMs: z.number().optional().describe('Time window in ms. Omit = any time this session.'),\n})\n .describe('Fires when a specific event has occurred during this session. ' +\n 'Example: {\"type\": \"event_occurred\", \"eventName\": \"ui.click\", \"withinMs\": 5000}');\nexport const StateEqualsConditionZ = z\n .object({\n type: z.literal('state_equals'),\n key: z\n .string()\n .describe('Key in the SDK persistent state store (localStorage). Only valid for keys the host app explicitly sets via syntro.state.set().'),\n value: z.unknown().describe('Expected value to match against'),\n})\n .describe('Checks the SDK persistent state store (localStorage). ONLY for host-app state set via syntro.state.set() \u2014 ' +\n 'NOT for user attributes like region, device, or UTM params (those are handled by segment targeting). ' +\n 'Do NOT use this for targeting. If you do not know the valid state keys, do not use this condition type.');\nexport const ViewportConditionZ = z\n .object({\n type: z.literal('viewport'),\n minWidth: z.number().optional().describe('Minimum viewport width in pixels'),\n maxWidth: z.number().optional().describe('Maximum viewport width in pixels'),\n minHeight: z.number().optional().describe('Minimum viewport height in pixels'),\n maxHeight: z.number().optional().describe('Maximum viewport height in pixels'),\n})\n .describe('Fires based on viewport (screen) size. Use for responsive behavior. ' +\n 'Example: {\"type\": \"viewport\", \"minWidth\": 768} \u2014 fires on tablet and larger.');\nexport const SessionMetricConditionZ = z\n .object({\n type: z.literal('session_metric'),\n key: SessionMetricKeyZ,\n operator: z.enum(['gte', 'lte', 'eq', 'gt', 'lt']),\n threshold: z.number().describe('Numeric threshold to compare against'),\n})\n .describe('Fires when a session metric crosses a threshold. Valid keys: \"time_on_page\" (seconds), ' +\n '\"page_views\" (count), \"scroll_depth\" (0-100). ' +\n 'Example: {\"type\": \"session_metric\", \"key\": \"time_on_page\", \"operator\": \"gte\", \"threshold\": 30}');\nexport const DismissedConditionZ = z\n .object({\n type: z.literal('dismissed'),\n key: z.string().describe('Dismissal key (usually a tile or action ID)'),\n inverted: z\n .boolean()\n .optional()\n .describe('When true, fires if NOT dismissed (default behavior)'),\n})\n .describe('Checks if an item has been dismissed by the user. Use with inverted: true to show only if not dismissed.');\nexport const CooldownActiveConditionZ = z\n .object({\n type: z.literal('cooldown_active'),\n key: z.string().describe('Cooldown key'),\n inverted: z.boolean().optional().describe('When true, fires if cooldown is NOT active'),\n})\n .describe('Checks if a cooldown timer is currently active. Use to prevent showing the same intervention too frequently.');\nexport const FrequencyLimitConditionZ = z\n .object({\n type: z.literal('frequency_limit'),\n key: z.string().describe('Frequency counter key'),\n limit: z.number().describe('Maximum allowed count'),\n inverted: z.boolean().optional().describe('When true, fires if limit NOT reached'),\n})\n .describe('Checks if a frequency limit has been reached. Use to cap how many times an action fires per session.');\nexport const MatchOpZ = z\n .object({\n equals: z.union([z.string(), z.number(), z.boolean()]).optional(),\n contains: z.string().optional(),\n})\n .describe('Match operator for counter filters. Exactly one of equals or contains must be specified.');\nexport const CounterDefZ = z\n .object({\n events: z\n .array(CountableEventZ)\n .min(1)\n .describe('Event names to count. Use values from the countable events enum.'),\n match: z\n .record(z.string(), MatchOpZ)\n .optional()\n .describe('Property filters. Keys are event prop names or element-chain fields ' +\n '(tag_name, $el_text, attr__*). All entries AND together.'),\n})\n .describe('Defines what events to count. Registered as an accumulator predicate at config-load time.');\nexport const EventCountConditionZ = z\n .object({\n type: z.literal('event_count'),\n key: z.string().describe('Unique key for this counter (used for accumulator registration)'),\n operator: z.enum(['gte', 'lte', 'eq', 'gt', 'lt']),\n count: z.number().int().min(0).describe('Target count threshold'),\n withinMs: z\n .number()\n .positive()\n .optional()\n .describe('Time window in ms. Omit = count across entire session.'),\n counter: CounterDefZ.optional().describe('Inline counter definition. Defines what events to count.'),\n})\n .describe('Fires when accumulated event count crosses a threshold. Most powerful trigger type. ' +\n 'Example: {\"type\": \"event_count\", \"key\": \"pricing-clicks\", \"operator\": \"gte\", \"count\": 3, ' +\n '\"counter\": {\"events\": [\"ui.click\"], \"match\": {\"attr__data-cta\": {\"contains\": \"pricing\"}}}}');\nexport const ConditionZ = z.discriminatedUnion('type', [\n PageUrlConditionZ,\n RouteConditionZ,\n AnchorVisibleConditionZ,\n EventOccurredConditionZ,\n StateEqualsConditionZ,\n ViewportConditionZ,\n SessionMetricConditionZ,\n DismissedConditionZ,\n CooldownActiveConditionZ,\n FrequencyLimitConditionZ,\n EventCountConditionZ,\n]);\n// =============================================================================\n// STRATEGY SCHEMAS\n// =============================================================================\nexport const RuleZ = z\n .object({\n conditions: z\n .array(ConditionZ)\n .describe('Array of conditions \u2014 ALL must match (AND logic) for this rule to fire.'),\n value: z\n .unknown()\n .describe('Value returned when all conditions match. For triggerWhen: true = fire the action.'),\n})\n .describe('A single rule. ALL conditions must match (AND logic). Rules in a strategy are evaluated ' +\n 'top-to-bottom \u2014 first rule where all conditions match wins and returns its value.');\nexport const RuleStrategyZ = z\n .object({\n type: z.literal('rules'),\n rules: z\n .array(RuleZ)\n .describe('Ordered list of rules. Evaluated top-to-bottom \u2014 first match wins.'),\n default: z\n .unknown()\n .describe('Fallback value when no rule matches. For triggerWhen: false = do not fire by default.'),\n})\n .describe('Rule-based strategy. Evaluates rules top-to-bottom. First rule where ALL conditions match ' +\n 'returns its value. If no rule matches, returns default. ' +\n 'For triggerWhen: set value=true on matching rules, default=false.');\nexport const ScoreStrategyZ = z\n .object({\n type: z.literal('score'),\n field: z.string(),\n threshold: z.number(),\n above: z.unknown(),\n below: z.unknown(),\n})\n .describe('Score-based strategy. Compares a field value against a threshold.');\nexport const ModelStrategyZ = z\n .object({\n type: z.literal('model'),\n modelId: z.string(),\n inputs: z.array(z.string()),\n outputMapping: z.record(z.string(), z.unknown()),\n default: z.unknown(),\n})\n .describe('ML model strategy. Sends inputs to a model and maps outputs.');\nexport const ExternalStrategyZ = z\n .object({\n type: z.literal('external'),\n endpoint: z.string(),\n method: z.enum(['GET', 'POST']).optional(),\n default: z.unknown(),\n timeoutMs: z.number().optional(),\n})\n .describe('External API strategy. Calls an endpoint to determine the value.');\nexport const DecisionStrategyZ = z.discriminatedUnion('type', [\n RuleStrategyZ,\n ScoreStrategyZ,\n ModelStrategyZ,\n ExternalStrategyZ,\n]);\n/** Canonical Zod schema for the optional triggerWhen field on actions and adaptive items. */\nexport const TriggerWhenZ = DecisionStrategyZ.nullable().optional();\n// =============================================================================\n// TRIGGER DOCUMENTATION \u2014 examples and match field docs\n// Exported as constants so the schema generator can inject them into the\n// JSON schema. The Python prompt builder reads them from the schema.\n// =============================================================================\n/** Complete triggerWhen examples showing the full rules wrapper structure. */\nexport const TRIGGER_EXAMPLES = [\n {\n name: 'Click count on a specific element',\n description: 'Fire when user clicks an element with data-id=\"hero-cta\" 2+ times',\n triggerWhen: {\n type: 'rules',\n rules: [\n {\n conditions: [\n {\n type: 'event_count',\n key: 'cta-clicks',\n operator: 'gte',\n count: 2,\n counter: {\n events: ['ui.click'],\n match: { 'attr__data-id': { equals: 'hero-cta' } },\n },\n },\n ],\n value: true,\n },\n ],\n default: false,\n },\n },\n {\n name: 'Time on page threshold',\n description: 'Fire after user spends 30+ seconds on the page',\n triggerWhen: {\n type: 'rules',\n rules: [\n {\n conditions: [\n {\n type: 'session_metric',\n key: 'time_on_page',\n operator: 'gte',\n threshold: 30,\n },\n ],\n value: true,\n },\n ],\n default: false,\n },\n },\n {\n name: 'Element visible in viewport',\n description: 'Fire when a DOM element becomes visible',\n triggerWhen: {\n type: 'rules',\n rules: [\n {\n conditions: [\n {\n type: 'anchor_visible',\n anchorId: '#pricing-section',\n state: 'visible',\n },\n ],\n value: true,\n },\n ],\n default: false,\n },\n },\n {\n name: 'No trigger (fire immediately)',\n description: 'Action fires as soon as the segment matches \u2014 no in-session condition needed',\n triggerWhen: null,\n },\n];\n/** Documentation for counter.match field keys. */\nexport const MATCH_FIELD_DOCS = {\n tag_name: 'HTML tag name (e.g. \"button\", \"a\", \"input\")',\n $el_text: 'Visible text content of the element',\n 'attr__*': 'HTML attribute prefixed with attr__. Example: attr__data-id matches the data-id attribute, ' +\n 'attr__class matches the class attribute, attr__href matches the href attribute.',\n};\n// =============================================================================\n// EVENT SCOPE SCHEMA\n// =============================================================================\n/** Scopes a widget to specific events/URLs. */\nexport const EventScopeZ = z.object({\n events: z.array(z.string()),\n urlContains: z.string().optional(),\n props: z.record(z.union([z.string(), z.number(), z.boolean()])).optional(),\n});\n// =============================================================================\n// NOTIFY SCHEMA\n// =============================================================================\n/** Toast notification config for triggerWhen transitions. */\nexport const NotifyZ = z\n .object({\n title: z.string().optional(),\n body: z.string().optional(),\n icon: z.string().optional(),\n})\n .nullable()\n .optional();\n", "/**\n * Adaptive Content - Config Schema\n *\n * Zod schema for validating content app configuration.\n */\n\nimport { AnchorIdZ } from '@syntrologie/sdk-contracts';\nimport { z } from 'zod';\n\n/**\n * Content app config schema.\n * Defines the structure for content modifications in a canvas config.\n */\nexport const configSchema = z\n .object({\n /** Text replacements */\n textReplacements: z\n .array(\n z\n .object({\n anchorId: AnchorIdZ.describe(\n 'Route and CSS selector identifying the element whose text content to replace.'\n ),\n summary: z\n .string()\n .describe(\n 'Human-readable description of this change, used for logging and debugging.'\n ),\n text: z\n .string()\n .describe('New text content to set on the element (replaces existing text).'),\n })\n .describe('A single text replacement: replaces the text content of a matched element.')\n )\n .optional()\n .describe(\n 'Replaces the visible text of DOM elements. Use for copy changes, headline personalization, or CTA label swaps.'\n ),\n\n /** Attribute modifications */\n attributeChanges: z\n .array(\n z\n .object({\n anchorId: AnchorIdZ.describe(\n 'Route and CSS selector identifying the element whose attribute to set.'\n ),\n summary: z\n .string()\n .describe(\n 'Human-readable description of this change, used for logging and debugging.'\n ),\n attr: z\n .string()\n .min(\n 1,\n 'Attribute name must not be empty \u2014 setAttribute(\"\", ...) throws InvalidCharacterError in all browsers.'\n )\n .describe(\n 'HTML attribute name to set (e.g. \"href\", \"src\", \"data-experiment\"). Event handler attributes are blocked.'\n ),\n value: z.string().describe('New attribute value to apply.'),\n })\n .describe('A single attribute change: sets one HTML attribute on a matched element.')\n )\n .optional()\n .describe(\n 'Sets HTML attributes on DOM elements. Use for href swaps, src changes, or adding data-* experiment markers. Event handlers (onclick, onerror, etc.) are blocked.'\n ),\n\n /** Style modifications */\n styleChanges: z\n .array(\n z\n .object({\n anchorId: AnchorIdZ.describe(\n 'Route and CSS selector identifying the element to restyle.'\n ),\n summary: z\n .string()\n .describe(\n 'Human-readable description of this change, used for logging and debugging.'\n ),\n styles: z\n .record(z.string())\n .describe(\n 'Map of CSS property \u2192 value pairs applied as inline styles (e.g. {\"background-color\": \"#1e40af\", \"padding\": \"2rem\"}).'\n ),\n })\n .describe('A single style change: sets inline CSS properties on a matched element.')\n )\n .optional()\n .describe(\n 'Sets inline CSS styles on DOM elements. Use for color, spacing, or visibility changes without modifying the stylesheet.'\n ),\n\n /** HTML insertions */\n htmlInsertions: z\n .array(\n z\n .object({\n anchorId: AnchorIdZ.describe(\n 'Route and CSS selector identifying the reference element for the insertion.'\n ),\n summary: z\n .string()\n .describe(\n 'Human-readable description of this change, used for logging and debugging.'\n ),\n html: z\n .string()\n .min(\n 1,\n 'HTML string must not be empty \u2014 an empty html with position \"replace\" silently swaps the anchor element for an empty div.'\n )\n .describe(\n 'HTML string to insert. The SDK sanitizes it before injection \u2014 no script tags or event handlers (onclick, onerror, etc.). NEVER embed JavaScript \u2014 use deepLink for canvas navigation instead.'\n ),\n position: z\n .enum(['before', 'after', 'prepend', 'append', 'replace'])\n .describe(\n '\"before\"/\"after\" inserts adjacent to the element; \"prepend\"/\"append\" inserts inside at the start/end; \"replace\" swaps the entire element.'\n ),\n deepLink: z\n .object({\n tileId: z\n .string()\n .describe('ID of the canvas tile to open when the inserted element is clicked.'),\n itemId: z\n .string()\n .optional()\n .describe(\n 'Optional specific item within the tile (e.g. a FAQ question ID) to scroll to after opening.'\n ),\n })\n .optional()\n .describe(\n 'Makes the entire inserted element clickable to open the canvas panel and navigate to a specific tile. The SDK wires up click handlers, sets cursor to pointer, and publishes a notification.deep_link event. This is the ONLY correct way to connect inserted HTML to canvas tiles \u2014 NEVER use onclick handlers or window.SynOS in HTML strings.'\n ),\n })\n .describe(\n 'A single HTML insertion: injects a sanitized HTML fragment relative to a matched element. Use deepLink to connect inserted buttons/links to canvas tiles.'\n )\n )\n .optional()\n .describe(\n 'Inserts sanitized HTML fragments relative to DOM elements. Use for adding badges, banners, or supplementary content. Do NOT embed JavaScript \u2014 use deepLink for canvas navigation.'\n ),\n\n /** Class additions */\n classAdditions: z\n .array(\n z\n .object({\n anchorId: AnchorIdZ.describe(\n 'Route and CSS selector identifying the element to add the class to.'\n ),\n summary: z\n .string()\n .describe(\n 'Human-readable description of this change, used for logging and debugging.'\n ),\n className: z\n .string()\n .min(\n 1,\n 'Class name must not be empty \u2014 classList.add(\"\") throws DOMException in all browsers.'\n )\n .describe(\n 'CSS class name to add to the element\\'s class list (e.g. \"highlighted\", \"is-active\").'\n ),\n })\n .describe('A single class addition: appends one CSS class to a matched element.')\n )\n .optional()\n .describe(\n 'Adds CSS classes to DOM elements. Use to toggle visibility, apply highlight styles, or trigger CSS animations already defined in the host stylesheet.'\n ),\n\n /** Class removals */\n classRemovals: z\n .array(\n z\n .object({\n anchorId: AnchorIdZ.describe(\n 'Route and CSS selector identifying the element to remove the class from.'\n ),\n summary: z\n .string()\n .describe(\n 'Human-readable description of this change, used for logging and debugging.'\n ),\n className: z\n .string()\n .min(\n 1,\n 'Class name must not be empty \u2014 classList.remove(\"\") throws DOMException in all browsers.'\n )\n .describe(\n 'CSS class name to remove from the element\\'s class list (e.g. \"hidden\", \"disabled\").'\n ),\n })\n .describe('A single class removal: removes one CSS class from a matched element.')\n )\n .optional()\n .describe(\n 'Removes CSS classes from DOM elements. Use to reveal hidden elements, remove disabled states, or undo class-based styling.'\n ),\n })\n .describe(\n 'Props for adaptive-content DOM mutation operations. Each field targets a list of elements by anchorId and applies a specific type of change (text, attribute, style, HTML, or class).'\n );\n\nexport type ContentConfig = z.infer<typeof configSchema>;\n\n// ============================================================================\n// Capabilities Documentation (injected into JSON Schema for LLM prompts)\n// ============================================================================\n\nexport const CAPABILITIES_DOCUMENTATION = {\n packageId: 'adaptive-content',\n description:\n 'DOM content modification capabilities for text, attributes, styles, HTML, and classes.',\n whenToUse: [\n { goal: \"Replace an element's text\", action: 'content:setText' },\n { goal: 'Change an HTML attribute (href, src, data-*)', action: 'content:setAttr' },\n { goal: 'Modify inline styles (color, size, spacing)', action: 'content:setStyle' },\n { goal: 'Inject new HTML before/after/inside an element', action: 'content:insertHtml' },\n { goal: 'Add a CSS class (show/hide, animate)', action: 'content:addClass' },\n { goal: 'Remove a CSS class', action: 'content:removeClass' },\n ],\n conventions: [\n {\n name: 'Deep-linking inserted content to canvas tiles',\n description:\n 'Use the deepLink property on content:insertHtml to make inserted elements open the canvas panel and navigate to a specific tile when clicked. NEVER use onclick handlers or window.SynOS in HTML strings \u2014 they are sanitized and stripped. deepLink is the only supported mechanism.',\n },\n {\n name: 'Blocked attributes',\n description:\n 'Event handler attributes (onclick, onerror, onload, etc.) are blocked by content:setAttr. Use data-* attributes for experiment markers instead.',\n },\n ],\n events: [],\n};\n"],
5
- "mappings": ";AAMA,SAAS,SAAS;AAIX,IAAM,YAAY,EACpB,OAAO;AAAA,EACR,UAAU,EAAE,OAAO;AAAA,EACnB,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AACpD,CAAC,EACI,OAAO;AAgBL,IAAM,mBAAmB;AAAA,EAC5B,OAAO,EACF,OAAO,EACP,IAAI,GAAG,EACP,SAAS,EACT,SAAS,6GAA6G;AAAA,EAC3H,aAAa,EACR,OAAO,EACP,IAAI,GAAI,EACR,SAAS,EACT,SAAS,wHAAwH;AAAA,EACtI,YAAY,EACP,MAAM,EAAE,OAAO,EAAE,IAAI,GAAG,CAAC,EACzB,IAAI,EAAE,EACN,SAAS,EACT,SAAS,+KAA+K;AACjM;AAMO,IAAM,mBAAmB;AAAA;AAAA,EAE5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AACJ;AACO,IAAM,kBAAkB,EAC1B,KAAK,gBAAgB,EACrB,SAAS,2IAA2I;AAElJ,IAAM,sBAAsB,CAAC,gBAAgB,cAAc,cAAc;AACzE,IAAM,oBAAoB,EAC5B,KAAK,mBAAmB,EACxB,SAAS,uIAAuI;AAS9I,IAAM,oBAAoB,EAC5B,OAAO;AAAA,EACR,MAAM,EAAE,QAAQ,UAAU;AAAA,EAC1B,KAAK,EAAE,OAAO,EAAE,SAAS,mDAAmD;AAChF,CAAC,EACI,SAAS,0HACwC;AAC/C,IAAM,kBAAkB,EAC1B,OAAO;AAAA,EACR,MAAM,EAAE,QAAQ,OAAO;AAAA,EACvB,SAAS,EAAE,OAAO,EAAE,SAAS,sCAAsC;AACvE,CAAC,EACI,SAAS,wDAAwD;AAC/D,IAAM,0BAA0B,EAClC,OAAO;AAAA,EACR,MAAM,EAAE,QAAQ,gBAAgB;AAAA,EAChC,UAAU,EAAE,OAAO,EAAE,SAAS,oCAAoC;AAAA,EAClE,OAAO,EACF,KAAK,CAAC,WAAW,WAAW,QAAQ,CAAC,EACrC,SAAS,oEAAoE;AACtF,CAAC,EACI,SAAS,qIAC0E;AACjF,IAAM,0BAA0B,EAClC,OAAO;AAAA,EACR,MAAM,EAAE,QAAQ,gBAAgB;AAAA,EAChC,WAAW,EAAE,OAAO,EAAE,SAAS,2CAA2C;AAAA,EAC1E,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kDAAkD;AAC/F,CAAC,EACI,SAAS,8IACsE;AAC7E,IAAM,wBAAwB,EAChC,OAAO;AAAA,EACR,MAAM,EAAE,QAAQ,cAAc;AAAA,EAC9B,KAAK,EACA,OAAO,EACP,SAAS,gIAAgI;AAAA,EAC9I,OAAO,EAAE,QAAQ,EAAE,SAAS,iCAAiC;AACjE,CAAC,EACI,SAAS,8TAE+F;AACtG,IAAM,qBAAqB,EAC7B,OAAO;AAAA,EACR,MAAM,EAAE,QAAQ,UAAU;AAAA,EAC1B,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kCAAkC;AAAA,EAC3E,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kCAAkC;AAAA,EAC3E,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,mCAAmC;AAAA,EAC7E,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,mCAAmC;AACjF,CAAC,EACI,SAAS,uJACoE;AAC3E,IAAM,0BAA0B,EAClC,OAAO;AAAA,EACR,MAAM,EAAE,QAAQ,gBAAgB;AAAA,EAChC,KAAK;AAAA,EACL,UAAU,EAAE,KAAK,CAAC,OAAO,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,EACjD,WAAW,EAAE,OAAO,EAAE,SAAS,sCAAsC;AACzE,CAAC,EACI,SAAS,qOAEsF;AAC7F,IAAM,sBAAsB,EAC9B,OAAO;AAAA,EACR,MAAM,EAAE,QAAQ,WAAW;AAAA,EAC3B,KAAK,EAAE,OAAO,EAAE,SAAS,6CAA6C;AAAA,EACtE,UAAU,EACL,QAAQ,EACR,SAAS,EACT,SAAS,sDAAsD;AACxE,CAAC,EACI,SAAS,0GAA0G;AACjH,IAAM,2BAA2B,EACnC,OAAO;AAAA,EACR,MAAM,EAAE,QAAQ,iBAAiB;AAAA,EACjC,KAAK,EAAE,OAAO,EAAE,SAAS,cAAc;AAAA,EACvC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAC1F,CAAC,EACI,SAAS,8GAA8G;AACrH,IAAM,2BAA2B,EACnC,OAAO;AAAA,EACR,MAAM,EAAE,QAAQ,iBAAiB;AAAA,EACjC,KAAK,EAAE,OAAO,EAAE,SAAS,uBAAuB;AAAA,EAChD,OAAO,EAAE,OAAO,EAAE,SAAS,uBAAuB;AAAA,EAClD,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,uCAAuC;AACrF,CAAC,EACI,SAAS,sGAAsG;AAC7G,IAAM,WAAW,EACnB,OAAO;AAAA,EACR,QAAQ,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,CAAC,EAAE,SAAS;AAAA,EAChE,UAAU,EAAE,OAAO,EAAE,SAAS;AAClC,CAAC,EACI,SAAS,0FAA0F;AACjG,IAAM,cAAc,EACtB,OAAO;AAAA,EACR,QAAQ,EACH,MAAM,eAAe,EACrB,IAAI,CAAC,EACL,SAAS,kEAAkE;AAAA,EAChF,OAAO,EACF,OAAO,EAAE,OAAO,GAAG,QAAQ,EAC3B,SAAS,EACT,SAAS,8HACgD;AAClE,CAAC,EACI,SAAS,2FAA2F;AAClG,IAAM,uBAAuB,EAC/B,OAAO;AAAA,EACR,MAAM,EAAE,QAAQ,aAAa;AAAA,EAC7B,KAAK,EAAE,OAAO,EAAE,SAAS,iEAAiE;AAAA,EAC1F,UAAU,EAAE,KAAK,CAAC,OAAO,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,EACjD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,SAAS,wBAAwB;AAAA,EAChE,UAAU,EACL,OAAO,EACP,SAAS,EACT,SAAS,EACT,SAAS,wDAAwD;AAAA,EACtE,SAAS,YAAY,SAAS,EAAE,SAAS,0DAA0D;AACvG,CAAC,EACI,SAAS,yQAEkF;AACzF,IAAM,aAAa,EAAE,mBAAmB,QAAQ;AAAA,EACnD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAIM,IAAM,QAAQ,EAChB,OAAO;AAAA,EACR,YAAY,EACP,MAAM,UAAU,EAChB,SAAS,8EAAyE;AAAA,EACvF,OAAO,EACF,QAAQ,EACR,SAAS,oFAAoF;AACtG,CAAC,EACI,SAAS,gLACyE;AAChF,IAAM,gBAAgB,EACxB,OAAO;AAAA,EACR,MAAM,EAAE,QAAQ,OAAO;AAAA,EACvB,OAAO,EACF,MAAM,KAAK,EACX,SAAS,yEAAoE;AAAA,EAClF,SAAS,EACJ,QAAQ,EACR,SAAS,uFAAuF;AACzG,CAAC,EACI,SAAS,qNAEyD;AAChE,IAAM,iBAAiB,EACzB,OAAO;AAAA,EACR,MAAM,EAAE,QAAQ,OAAO;AAAA,EACvB,OAAO,EAAE,OAAO;AAAA,EAChB,WAAW,EAAE,OAAO;AAAA,EACpB,OAAO,EAAE,QAAQ;AAAA,EACjB,OAAO,EAAE,QAAQ;AACrB,CAAC,EACI,SAAS,mEAAmE;AAC1E,IAAM,iBAAiB,EACzB,OAAO;AAAA,EACR,MAAM,EAAE,QAAQ,OAAO;AAAA,EACvB,SAAS,EAAE,OAAO;AAAA,EAClB,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,EAC1B,eAAe,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;AAAA,EAC/C,SAAS,EAAE,QAAQ;AACvB,CAAC,EACI,SAAS,8DAA8D;AACrE,IAAM,oBAAoB,EAC5B,OAAO;AAAA,EACR,MAAM,EAAE,QAAQ,UAAU;AAAA,EAC1B,UAAU,EAAE,OAAO;AAAA,EACnB,QAAQ,EAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS;AAAA,EACzC,SAAS,EAAE,QAAQ;AAAA,EACnB,WAAW,EAAE,OAAO,EAAE,SAAS;AACnC,CAAC,EACI,SAAS,kEAAkE;AACzE,IAAM,oBAAoB,EAAE,mBAAmB,QAAQ;AAAA,EAC1D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAEM,IAAM,eAAe,kBAAkB,SAAS,EAAE,SAAS;AA2F3D,IAAM,cAAc,EAAE,OAAO;AAAA,EAChC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,EAC1B,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS;AAC7E,CAAC;AAKM,IAAM,UAAU,EAClB,OAAO;AAAA,EACR,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,EAC1B,MAAM,EAAE,OAAO,EAAE,SAAS;AAC9B,CAAC,EACI,SAAS,EACT,SAAS;;;ACjYd,SAAS,KAAAA,UAAS;AAMX,IAAM,eAAeA,GACzB,OAAO;AAAA;AAAA,EAEN,kBAAkBA,GACf;AAAA,IACCA,GACG,OAAO;AAAA,MACN,UAAU,UAAU;AAAA,QAClB;AAAA,MACF;AAAA,MACA,SAASA,GACN,OAAO,EACP;AAAA,QACC;AAAA,MACF;AAAA,MACF,MAAMA,GACH,OAAO,EACP,SAAS,kEAAkE;AAAA,IAChF,CAAC,EACA,SAAS,4EAA4E;AAAA,EAC1F,EACC,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA;AAAA,EAGF,kBAAkBA,GACf;AAAA,IACCA,GACG,OAAO;AAAA,MACN,UAAU,UAAU;AAAA,QAClB;AAAA,MACF;AAAA,MACA,SAASA,GACN,OAAO,EACP;AAAA,QACC;AAAA,MACF;AAAA,MACF,MAAMA,GACH,OAAO,EACP;AAAA,QACC;AAAA,QACA;AAAA,MACF,EACC;AAAA,QACC;AAAA,MACF;AAAA,MACF,OAAOA,GAAE,OAAO,EAAE,SAAS,+BAA+B;AAAA,IAC5D,CAAC,EACA,SAAS,0EAA0E;AAAA,EACxF,EACC,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA;AAAA,EAGF,cAAcA,GACX;AAAA,IACCA,GACG,OAAO;AAAA,MACN,UAAU,UAAU;AAAA,QAClB;AAAA,MACF;AAAA,MACA,SAASA,GACN,OAAO,EACP;AAAA,QACC;AAAA,MACF;AAAA,MACF,QAAQA,GACL,OAAOA,GAAE,OAAO,CAAC,EACjB;AAAA,QACC;AAAA,MACF;AAAA,IACJ,CAAC,EACA,SAAS,yEAAyE;AAAA,EACvF,EACC,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA;AAAA,EAGF,gBAAgBA,GACb;AAAA,IACCA,GACG,OAAO;AAAA,MACN,UAAU,UAAU;AAAA,QAClB;AAAA,MACF;AAAA,MACA,SAASA,GACN,OAAO,EACP;AAAA,QACC;AAAA,MACF;AAAA,MACF,MAAMA,GACH,OAAO,EACP;AAAA,QACC;AAAA,QACA;AAAA,MACF,EACC;AAAA,QACC;AAAA,MACF;AAAA,MACF,UAAUA,GACP,KAAK,CAAC,UAAU,SAAS,WAAW,UAAU,SAAS,CAAC,EACxD;AAAA,QACC;AAAA,MACF;AAAA,MACF,UAAUA,GACP,OAAO;AAAA,QACN,QAAQA,GACL,OAAO,EACP,SAAS,qEAAqE;AAAA,QACjF,QAAQA,GACL,OAAO,EACP,SAAS,EACT;AAAA,UACC;AAAA,QACF;AAAA,MACJ,CAAC,EACA,SAAS,EACT;AAAA,QACC;AAAA,MACF;AAAA,IACJ,CAAC,EACA;AAAA,MACC;AAAA,IACF;AAAA,EACJ,EACC,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA;AAAA,EAGF,gBAAgBA,GACb;AAAA,IACCA,GACG,OAAO;AAAA,MACN,UAAU,UAAU;AAAA,QAClB;AAAA,MACF;AAAA,MACA,SAASA,GACN,OAAO,EACP;AAAA,QACC;AAAA,MACF;AAAA,MACF,WAAWA,GACR,OAAO,EACP;AAAA,QACC;AAAA,QACA;AAAA,MACF,EACC;AAAA,QACC;AAAA,MACF;AAAA,IACJ,CAAC,EACA,SAAS,sEAAsE;AAAA,EACpF,EACC,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA;AAAA,EAGF,eAAeA,GACZ;AAAA,IACCA,GACG,OAAO;AAAA,MACN,UAAU,UAAU;AAAA,QAClB;AAAA,MACF;AAAA,MACA,SAASA,GACN,OAAO,EACP;AAAA,QACC;AAAA,MACF;AAAA,MACF,WAAWA,GACR,OAAO,EACP;AAAA,QACC;AAAA,QACA;AAAA,MACF,EACC;AAAA,QACC;AAAA,MACF;AAAA,IACJ,CAAC,EACA,SAAS,uEAAuE;AAAA,EACrF,EACC,SAAS,EACT;AAAA,IACC;AAAA,EACF;AACJ,CAAC,EACA;AAAA,EACC;AACF;AAQK,IAAM,6BAA6B;AAAA,EACxC,WAAW;AAAA,EACX,aACE;AAAA,EACF,WAAW;AAAA,IACT,EAAE,MAAM,6BAA6B,QAAQ,kBAAkB;AAAA,IAC/D,EAAE,MAAM,gDAAgD,QAAQ,kBAAkB;AAAA,IAClF,EAAE,MAAM,+CAA+C,QAAQ,mBAAmB;AAAA,IAClF,EAAE,MAAM,kDAAkD,QAAQ,qBAAqB;AAAA,IACvF,EAAE,MAAM,wCAAwC,QAAQ,mBAAmB;AAAA,IAC3E,EAAE,MAAM,sBAAsB,QAAQ,sBAAsB;AAAA,EAC9D;AAAA,EACA,aAAa;AAAA,IACX;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,QAAQ,CAAC;AACX;",
4
+ "sourcesContent": ["/**\n * Shared Zod schemas for decision strategies, conditions, and event scoping.\n *\n * These are the canonical definitions \u2014 runtime-sdk and all adaptive packages\n * should import from here instead of duplicating.\n */\nimport { z } from 'zod';\n// =============================================================================\n// ANCHOR ID SCHEMA\n// =============================================================================\nexport const AnchorIdZ = z\n .object({\n selector: z.string(),\n route: z.union([z.string(), z.array(z.string())]),\n})\n .strict();\n// =============================================================================\n// AUTHORING FIELDS \u2014 id / title / description / validation\n//\n// Shared fields every action carries. `id` is the action identifier the\n// runtime uses to dispatch, dedupe, and drop/replace actions \u2014 it is NOT\n// stripped before serving. `title` / `description` / `validation` are\n// authoring-only metadata stripped server-side in `to_runtime_config`\n// (platform/backend/app/domains/experiments/helpers.py).\n//\n// They all appear in the JSON Schema (and therefore in the tactician's\n// prompt) because the LLM needs to know they are valid action properties \u2014\n// otherwise schema validation would reject what the prompt commands.\n//\n// Each action variant should `.extend(AuthoringFieldsZ)` alongside any\n// triggerWhen/condition extensions.\n// =============================================================================\nexport const AuthoringFieldsZ = {\n id: z.string().optional().describe('Stable action identifier (e.g. \"act_3db6a14d2ab0\").'),\n title: z\n .string()\n .max(200)\n .optional()\n .describe('Authoring-only: short label shown on the action plan dashboard. Stripped before serving to the runtime SDK.'),\n description: z\n .string()\n .max(1000)\n .optional()\n .describe('Authoring-only: one-sentence explanation of what this action does and why. Stripped before serving to the runtime SDK.'),\n validation: z\n .array(z.string().max(500))\n .max(10)\n .optional()\n .describe('Authoring-only: ordered steps a reviewer can follow to trigger this action and visually confirm it works. Each entry is one step. Stripped before serving to the runtime SDK.'),\n};\n// =============================================================================\n// TRIGGER VOCABULARY \u2014 canonical lists of valid event names, metric keys, etc.\n// These flow through to the JSON schema as enums and are used by the LLM prompt.\n// =============================================================================\n/** Events that can be counted in event_count conditions. */\nexport const COUNTABLE_EVENTS = [\n // User interactions (from PostHog autocapture normalization)\n 'ui.click',\n 'ui.scroll',\n 'ui.input',\n 'ui.change',\n 'ui.submit',\n // Behavioral detectors (from event-processor)\n 'ui.hover',\n 'ui.idle',\n 'ui.scroll_thrash',\n 'ui.focus_bounce',\n // Navigation\n 'nav.page_view',\n 'nav.page_leave',\n // Derived behavioral signals\n 'behavior.rage_click',\n 'behavior.hesitation',\n 'behavior.confusion',\n];\nexport const CountableEventZ = z\n .enum(COUNTABLE_EVENTS)\n .describe('Event name to count. ui.* = user interactions and behavioral detectors, nav.* = page navigation, behavior.* = derived behavioral signals.');\n/** Valid session metric keys. */\nexport const SESSION_METRIC_KEYS = ['time_on_page', 'page_views', 'scroll_depth'];\nexport const SessionMetricKeyZ = z\n .enum(SESSION_METRIC_KEYS)\n .describe('Session metric key. time_on_page = seconds on current page, page_views = pages visited this session, scroll_depth = 0-100 percentage.');\n/** Element chain match field prefixes for counter filters. */\nexport const ELEMENT_MATCH_FIELDS = ['tag_name', '$el_text'];\n// Note: attr__* is a dynamic prefix (attr__data-id, attr__class, attr__href, etc.)\n// and cannot be enumerated. The match key is either one of ELEMENT_MATCH_FIELDS\n// or starts with \"attr__\".\n// =============================================================================\n// CONDITION SCHEMAS\n// =============================================================================\nexport const PageUrlConditionZ = z\n .object({\n type: z.literal('page_url'),\n url: z.string().describe('URL path to match (e.g. \"/pricing\", \"/dashboard\")'),\n})\n .describe('Fires when the current page URL matches. Use for page-specific actions. ' +\n 'Example: {\"type\": \"page_url\", \"url\": \"/pricing\"}');\nexport const RouteConditionZ = z\n .object({\n type: z.literal('route'),\n routeId: z.string().describe('Named route ID from the route filter'),\n})\n .describe('Fires when the current route matches a named route ID.');\nexport const AnchorVisibleConditionZ = z\n .object({\n type: z.literal('anchor_visible'),\n anchorId: z.string().describe('CSS selector of the anchor element'),\n state: z\n .enum(['visible', 'present', 'absent'])\n .describe('\"visible\" = in viewport, \"present\" = in DOM, \"absent\" = not in DOM'),\n})\n .describe(\"Fires based on a DOM element's visibility state. \" +\n 'Example: {\"type\": \"anchor_visible\", \"anchorId\": \"#cta-button\", \"state\": \"visible\"}');\nexport const EventOccurredConditionZ = z\n .object({\n type: z.literal('event_occurred'),\n eventName: z.string().describe('Event name (e.g. \"ui.click\", \"$pageview\")'),\n withinMs: z.number().optional().describe('Time window in ms. Omit = any time this session.'),\n})\n .describe('Fires when a specific event has occurred during this session. ' +\n 'Example: {\"type\": \"event_occurred\", \"eventName\": \"ui.click\", \"withinMs\": 5000}');\nexport const StateEqualsConditionZ = z\n .object({\n type: z.literal('state_equals'),\n key: z\n .string()\n .describe('Key in the SDK persistent state store (localStorage). Only valid for keys the host app explicitly sets via syntro.state.set().'),\n value: z.unknown().describe('Expected value to match against'),\n})\n .describe('Checks the SDK persistent state store (localStorage). ONLY for host-app state set via syntro.state.set() \u2014 ' +\n 'NOT for user attributes like region, device, or UTM params (those are handled by segment targeting). ' +\n 'Do NOT use this for targeting. If you do not know the valid state keys, do not use this condition type.');\nexport const ViewportConditionZ = z\n .object({\n type: z.literal('viewport'),\n minWidth: z.number().optional().describe('Minimum viewport width in pixels'),\n maxWidth: z.number().optional().describe('Maximum viewport width in pixels'),\n minHeight: z.number().optional().describe('Minimum viewport height in pixels'),\n maxHeight: z.number().optional().describe('Maximum viewport height in pixels'),\n})\n .describe('Fires based on viewport (screen) size. Use for responsive behavior. ' +\n 'Example: {\"type\": \"viewport\", \"minWidth\": 768} \u2014 fires on tablet and larger.');\nexport const SessionMetricConditionZ = z\n .object({\n type: z.literal('session_metric'),\n key: SessionMetricKeyZ,\n operator: z.enum(['gte', 'lte', 'eq', 'gt', 'lt']),\n threshold: z.number().describe('Numeric threshold to compare against'),\n})\n .describe('Fires when a session metric crosses a threshold. Valid keys: \"time_on_page\" (seconds), ' +\n '\"page_views\" (count), \"scroll_depth\" (0-100). ' +\n 'Example: {\"type\": \"session_metric\", \"key\": \"time_on_page\", \"operator\": \"gte\", \"threshold\": 30}');\nexport const DismissedConditionZ = z\n .object({\n type: z.literal('dismissed'),\n key: z.string().describe('Dismissal key (usually a tile or action ID)'),\n inverted: z\n .boolean()\n .optional()\n .describe('When true, fires if NOT dismissed (default behavior)'),\n})\n .describe('Checks if an item has been dismissed by the user. Use with inverted: true to show only if not dismissed.');\nexport const CooldownActiveConditionZ = z\n .object({\n type: z.literal('cooldown_active'),\n key: z.string().describe('Cooldown key'),\n inverted: z.boolean().optional().describe('When true, fires if cooldown is NOT active'),\n})\n .describe('Checks if a cooldown timer is currently active. Use to prevent showing the same intervention too frequently.');\nexport const FrequencyLimitConditionZ = z\n .object({\n type: z.literal('frequency_limit'),\n key: z.string().describe('Frequency counter key'),\n limit: z.number().describe('Maximum allowed count'),\n inverted: z.boolean().optional().describe('When true, fires if limit NOT reached'),\n})\n .describe('Checks if a frequency limit has been reached. Use to cap how many times an action fires per session.');\nexport const MatchOpZ = z\n .object({\n equals: z.union([z.string(), z.number(), z.boolean()]).optional(),\n contains: z.string().optional(),\n})\n .describe('Match operator for counter filters. Exactly one of equals or contains must be specified.');\nexport const CounterDefZ = z\n .object({\n events: z\n .array(CountableEventZ)\n .min(1)\n .describe('Event names to count. Use values from the countable events enum.'),\n match: z\n .record(z.string(), MatchOpZ)\n .optional()\n .describe('Property filters. Keys are event prop names or element-chain fields ' +\n '(tag_name, $el_text, attr__*). All entries AND together.'),\n})\n .describe('Defines what events to count. Registered as an accumulator predicate at config-load time.');\nexport const EventCountConditionZ = z\n .object({\n type: z.literal('event_count'),\n key: z.string().describe('Unique key for this counter (used for accumulator registration)'),\n operator: z.enum(['gte', 'lte', 'eq', 'gt', 'lt']),\n count: z.number().int().min(0).describe('Target count threshold'),\n withinMs: z\n .number()\n .positive()\n .optional()\n .describe('Time window in ms. Omit = count across entire session.'),\n counter: CounterDefZ.optional().describe('Inline counter definition. Defines what events to count.'),\n})\n .describe('Fires when accumulated event count crosses a threshold. Most powerful trigger type. ' +\n 'Example: {\"type\": \"event_count\", \"key\": \"pricing-clicks\", \"operator\": \"gte\", \"count\": 3, ' +\n '\"counter\": {\"events\": [\"ui.click\"], \"match\": {\"attr__data-cta\": {\"contains\": \"pricing\"}}}}');\nexport const ConditionZ = z.discriminatedUnion('type', [\n PageUrlConditionZ,\n RouteConditionZ,\n AnchorVisibleConditionZ,\n EventOccurredConditionZ,\n StateEqualsConditionZ,\n ViewportConditionZ,\n SessionMetricConditionZ,\n DismissedConditionZ,\n CooldownActiveConditionZ,\n FrequencyLimitConditionZ,\n EventCountConditionZ,\n]);\n// =============================================================================\n// STRATEGY SCHEMAS\n// =============================================================================\nexport const RuleZ = z\n .object({\n conditions: z\n .array(ConditionZ)\n .describe('Array of conditions \u2014 ALL must match (AND logic) for this rule to fire.'),\n value: z\n .unknown()\n .describe('Value returned when all conditions match. For triggerWhen: true = fire the action.'),\n})\n .describe('A single rule. ALL conditions must match (AND logic). Rules in a strategy are evaluated ' +\n 'top-to-bottom \u2014 first rule where all conditions match wins and returns its value.');\nexport const RuleStrategyZ = z\n .object({\n type: z.literal('rules'),\n rules: z\n .array(RuleZ)\n .describe('Ordered list of rules. Evaluated top-to-bottom \u2014 first match wins.'),\n default: z\n .unknown()\n .describe('Fallback value when no rule matches. For triggerWhen: false = do not fire by default.'),\n})\n .describe('Rule-based strategy. Evaluates rules top-to-bottom. First rule where ALL conditions match ' +\n 'returns its value. If no rule matches, returns default. ' +\n 'For triggerWhen: set value=true on matching rules, default=false.');\nexport const ScoreStrategyZ = z\n .object({\n type: z.literal('score'),\n field: z.string(),\n threshold: z.number(),\n above: z.unknown(),\n below: z.unknown(),\n})\n .describe('Score-based strategy. Compares a field value against a threshold.');\nexport const ModelStrategyZ = z\n .object({\n type: z.literal('model'),\n modelId: z.string(),\n inputs: z.array(z.string()),\n outputMapping: z.record(z.string(), z.unknown()),\n default: z.unknown(),\n})\n .describe('ML model strategy. Sends inputs to a model and maps outputs.');\nexport const ExternalStrategyZ = z\n .object({\n type: z.literal('external'),\n endpoint: z.string(),\n method: z.enum(['GET', 'POST']).optional(),\n default: z.unknown(),\n timeoutMs: z.number().optional(),\n})\n .describe('External API strategy. Calls an endpoint to determine the value.');\nexport const DecisionStrategyZ = z.discriminatedUnion('type', [\n RuleStrategyZ,\n ScoreStrategyZ,\n ModelStrategyZ,\n ExternalStrategyZ,\n]);\n/** Canonical Zod schema for the optional triggerWhen field on actions and adaptive items. */\nexport const TriggerWhenZ = DecisionStrategyZ.nullable().optional();\n// =============================================================================\n// TRIGGER DOCUMENTATION \u2014 examples and match field docs\n// Exported as constants so the schema generator can inject them into the\n// JSON schema. The Python prompt builder reads them from the schema.\n// =============================================================================\n/** Complete triggerWhen examples showing the full rules wrapper structure. */\nexport const TRIGGER_EXAMPLES = [\n {\n name: 'Click count on a specific element',\n description: 'Fire when user clicks an element with data-id=\"hero-cta\" 2+ times',\n triggerWhen: {\n type: 'rules',\n rules: [\n {\n conditions: [\n {\n type: 'event_count',\n key: 'cta-clicks',\n operator: 'gte',\n count: 2,\n counter: {\n events: ['ui.click'],\n match: { 'attr__data-id': { equals: 'hero-cta' } },\n },\n },\n ],\n value: true,\n },\n ],\n default: false,\n },\n },\n {\n name: 'Time on page threshold',\n description: 'Fire after user spends 30+ seconds on the page',\n triggerWhen: {\n type: 'rules',\n rules: [\n {\n conditions: [\n {\n type: 'session_metric',\n key: 'time_on_page',\n operator: 'gte',\n threshold: 30,\n },\n ],\n value: true,\n },\n ],\n default: false,\n },\n },\n {\n name: 'Element visible in viewport',\n description: 'Fire when a DOM element becomes visible',\n triggerWhen: {\n type: 'rules',\n rules: [\n {\n conditions: [\n {\n type: 'anchor_visible',\n anchorId: '#pricing-section',\n state: 'visible',\n },\n ],\n value: true,\n },\n ],\n default: false,\n },\n },\n {\n name: 'No trigger (fire immediately)',\n description: 'Action fires as soon as the segment matches \u2014 no in-session condition needed',\n triggerWhen: null,\n },\n];\n/** Documentation for counter.match field keys. */\nexport const MATCH_FIELD_DOCS = {\n tag_name: 'HTML tag name (e.g. \"button\", \"a\", \"input\")',\n $el_text: 'Visible text content of the element',\n 'attr__*': 'HTML attribute prefixed with attr__. Example: attr__data-id matches the data-id attribute, ' +\n 'attr__class matches the class attribute, attr__href matches the href attribute.',\n};\n// =============================================================================\n// EVENT SCOPE SCHEMA\n// =============================================================================\n/** Scopes a widget to specific events/URLs. */\nexport const EventScopeZ = z.object({\n events: z.array(z.string()),\n urlContains: z.string().optional(),\n props: z.record(z.union([z.string(), z.number(), z.boolean()])).optional(),\n});\n// =============================================================================\n// NOTIFY SCHEMA\n// =============================================================================\n/** Toast notification config for triggerWhen transitions. */\nexport const NotifyZ = z\n .object({\n title: z.string().optional(),\n body: z.string().optional(),\n icon: z.string().optional(),\n})\n .nullable()\n .optional();\n", "/**\n * Adaptive Content - Config Schema\n *\n * Zod schema for validating content app configuration.\n */\n\nimport { AnchorIdZ } from '@syntrologie/sdk-contracts';\nimport { z } from 'zod';\n\n/**\n * Content app config schema.\n * Defines the structure for content modifications in a canvas config.\n */\nexport const configSchema = z\n .object({\n /** Text replacements */\n textReplacements: z\n .array(\n z\n .object({\n anchorId: AnchorIdZ.describe(\n 'Route and CSS selector identifying the element whose text content to replace.'\n ),\n summary: z\n .string()\n .describe(\n 'Human-readable description of this change, used for logging and debugging.'\n ),\n text: z\n .string()\n .describe('New text content to set on the element (replaces existing text).'),\n })\n .describe('A single text replacement: replaces the text content of a matched element.')\n )\n .optional()\n .describe(\n 'Replaces the visible text of DOM elements. Use for copy changes, headline personalization, or CTA label swaps.'\n ),\n\n /** Attribute modifications */\n attributeChanges: z\n .array(\n z\n .object({\n anchorId: AnchorIdZ.describe(\n 'Route and CSS selector identifying the element whose attribute to set.'\n ),\n summary: z\n .string()\n .describe(\n 'Human-readable description of this change, used for logging and debugging.'\n ),\n attr: z\n .string()\n .min(\n 1,\n 'Attribute name must not be empty \u2014 setAttribute(\"\", ...) throws InvalidCharacterError in all browsers.'\n )\n .describe(\n 'HTML attribute name to set (e.g. \"href\", \"src\", \"data-experiment\"). Event handler attributes are blocked.'\n ),\n value: z.string().describe('New attribute value to apply.'),\n })\n .describe('A single attribute change: sets one HTML attribute on a matched element.')\n )\n .optional()\n .describe(\n 'Sets HTML attributes on DOM elements. Use for href swaps, src changes, or adding data-* experiment markers. Event handlers (onclick, onerror, etc.) are blocked.'\n ),\n\n /** Style modifications */\n styleChanges: z\n .array(\n z\n .object({\n anchorId: AnchorIdZ.describe(\n 'Route and CSS selector identifying the element to restyle.'\n ),\n summary: z\n .string()\n .describe(\n 'Human-readable description of this change, used for logging and debugging.'\n ),\n styles: z\n .record(\n // CSS property names must be kebab-case \u2014 `element.style.setProperty()`\n // silently drops camelCase keys at runtime. (BUG-1779388834)\n z\n .string()\n .regex(\n /^(?:-?[a-z][a-z0-9-]*|--[a-zA-Z0-9_-]+)$/,\n \"Invalid CSS property name. Use kebab-case (e.g. 'font-size', not 'fontSize').\"\n ),\n z.string()\n )\n .describe(\n 'Map of CSS property \u2192 value pairs applied as inline styles (e.g. {\"background-color\": \"#1e40af\", \"padding\": \"2rem\"}).'\n ),\n })\n .describe('A single style change: sets inline CSS properties on a matched element.')\n )\n .optional()\n .describe(\n 'Sets inline CSS styles on DOM elements. Use for color, spacing, or visibility changes without modifying the stylesheet.'\n ),\n\n /** HTML insertions */\n htmlInsertions: z\n .array(\n z\n .object({\n anchorId: AnchorIdZ.describe(\n 'Route and CSS selector identifying the reference element for the insertion.'\n ),\n summary: z\n .string()\n .describe(\n 'Human-readable description of this change, used for logging and debugging.'\n ),\n html: z\n .string()\n .min(\n 1,\n 'HTML string must not be empty \u2014 an empty html with position \"replace\" silently swaps the anchor element for an empty div.'\n )\n .describe(\n 'HTML string to insert. The SDK sanitizes it before injection \u2014 no script tags or event handlers (onclick, onerror, etc.). NEVER embed JavaScript \u2014 use deepLink for canvas navigation instead.'\n ),\n position: z\n .enum(['before', 'after', 'prepend', 'append', 'replace'])\n .describe(\n '\"before\"/\"after\" inserts adjacent to the element; \"prepend\"/\"append\" inserts inside at the start/end; \"replace\" swaps the entire element.'\n ),\n deepLink: z\n .object({\n tileId: z\n .string()\n .describe('ID of the canvas tile to open when the inserted element is clicked.'),\n itemId: z\n .string()\n .optional()\n .describe(\n 'Optional specific item within the tile (e.g. a FAQ question ID) to scroll to after opening.'\n ),\n })\n .optional()\n .describe(\n 'Makes the entire inserted element clickable to open the canvas panel and navigate to a specific tile. The SDK wires up click handlers, sets cursor to pointer, and publishes a notification.deep_link event. This is the ONLY correct way to connect inserted HTML to canvas tiles \u2014 NEVER use onclick handlers or window.SynOS in HTML strings.'\n ),\n })\n .describe(\n 'A single HTML insertion: injects a sanitized HTML fragment relative to a matched element. Use deepLink to connect inserted buttons/links to canvas tiles.'\n )\n )\n .optional()\n .describe(\n 'Inserts sanitized HTML fragments relative to DOM elements. Use for adding badges, banners, or supplementary content. Do NOT embed JavaScript \u2014 use deepLink for canvas navigation.'\n ),\n\n /** Class additions */\n classAdditions: z\n .array(\n z\n .object({\n anchorId: AnchorIdZ.describe(\n 'Route and CSS selector identifying the element to add the class to.'\n ),\n summary: z\n .string()\n .describe(\n 'Human-readable description of this change, used for logging and debugging.'\n ),\n className: z\n .string()\n .min(\n 1,\n 'Class name must not be empty \u2014 classList.add(\"\") throws DOMException in all browsers.'\n )\n .describe(\n 'CSS class name to add to the element\\'s class list (e.g. \"highlighted\", \"is-active\").'\n ),\n })\n .describe('A single class addition: appends one CSS class to a matched element.')\n )\n .optional()\n .describe(\n 'Adds CSS classes to DOM elements. Use to toggle visibility, apply highlight styles, or trigger CSS animations already defined in the host stylesheet.'\n ),\n\n /** Class removals */\n classRemovals: z\n .array(\n z\n .object({\n anchorId: AnchorIdZ.describe(\n 'Route and CSS selector identifying the element to remove the class from.'\n ),\n summary: z\n .string()\n .describe(\n 'Human-readable description of this change, used for logging and debugging.'\n ),\n className: z\n .string()\n .min(\n 1,\n 'Class name must not be empty \u2014 classList.remove(\"\") throws DOMException in all browsers.'\n )\n .describe(\n 'CSS class name to remove from the element\\'s class list (e.g. \"hidden\", \"disabled\").'\n ),\n })\n .describe('A single class removal: removes one CSS class from a matched element.')\n )\n .optional()\n .describe(\n 'Removes CSS classes from DOM elements. Use to reveal hidden elements, remove disabled states, or undo class-based styling.'\n ),\n })\n .describe(\n 'Props for adaptive-content DOM mutation operations. Each field targets a list of elements by anchorId and applies a specific type of change (text, attribute, style, HTML, or class).'\n );\n\nexport type ContentConfig = z.infer<typeof configSchema>;\n\n// ============================================================================\n// Capabilities Documentation (injected into JSON Schema for LLM prompts)\n// ============================================================================\n\nexport const CAPABILITIES_DOCUMENTATION = {\n packageId: 'adaptive-content',\n description:\n 'DOM content modification capabilities for text, attributes, styles, HTML, and classes.',\n whenToUse: [\n { goal: \"Replace an element's text\", action: 'content:setText' },\n { goal: 'Change an HTML attribute (href, src, data-*)', action: 'content:setAttr' },\n { goal: 'Modify inline styles (color, size, spacing)', action: 'content:setStyle' },\n { goal: 'Inject new HTML before/after/inside an element', action: 'content:insertHtml' },\n { goal: 'Add a CSS class (show/hide, animate)', action: 'content:addClass' },\n { goal: 'Remove a CSS class', action: 'content:removeClass' },\n ],\n conventions: [\n {\n name: 'Deep-linking inserted content to canvas tiles',\n description:\n 'Use the deepLink property on content:insertHtml to make inserted elements open the canvas panel and navigate to a specific tile when clicked. NEVER use onclick handlers or window.SynOS in HTML strings \u2014 they are sanitized and stripped. deepLink is the only supported mechanism.',\n },\n {\n name: 'Blocked attributes',\n description:\n 'Event handler attributes (onclick, onerror, onload, etc.) are blocked by content:setAttr. Use data-* attributes for experiment markers instead.',\n },\n ],\n events: [],\n};\n"],
5
+ "mappings": "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",
6
6
  "names": ["z"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syntrologie/adapt-content",
3
- "version": "2.25.2",
3
+ "version": "2.27.0",
4
4
  "description": "Adaptive Content app - DOM manipulation actions for text, attributes, and styles",
5
5
  "license": "Proprietary",
6
6
  "private": false,