@syntrologie/adapt-content 2.29.1 → 2.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/runtime.js CHANGED
@@ -77,6 +77,11 @@ var ALLOWED_TAGS = /* @__PURE__ */ new Set([
77
77
  "rect",
78
78
  "g"
79
79
  ]);
80
+ var SYNTRO_ELEMENT_RE = /^syntro-[a-z][a-z0-9-]*$/;
81
+ var CONTAINS_SYNTRO_ELEMENT_RE = /<syntro-[a-z]/i;
82
+ function isAllowedTag(tag) {
83
+ return ALLOWED_TAGS.has(tag) || SYNTRO_ELEMENT_RE.test(tag);
84
+ }
80
85
  function normalizeUrlAttr(value) {
81
86
  const withoutControlCharacters = Array.from(value, (character) => {
82
87
  const codePoint = character.codePointAt(0) ?? 0;
@@ -90,7 +95,7 @@ function isDangerousUrlAttr(name, value) {
90
95
  return normalized.startsWith("javascript:") || normalized.startsWith("vbscript:") || normalized.startsWith("data:");
91
96
  }
92
97
  function sanitizeHtml(html) {
93
- const hasNative = typeof window.Sanitizer === "function";
98
+ const hasNative = typeof window.Sanitizer === "function" && !CONTAINS_SYNTRO_ELEMENT_RE.test(html);
94
99
  if (hasNative) {
95
100
  try {
96
101
  const s = new window.Sanitizer({});
@@ -109,7 +114,7 @@ function sanitizeHtml(html) {
109
114
  while (walker.nextNode()) {
110
115
  const el = walker.currentNode;
111
116
  const tag = el.tagName.toLowerCase();
112
- if (!ALLOWED_TAGS.has(tag)) {
117
+ if (!isAllowedTag(tag)) {
113
118
  toRemove.push(el);
114
119
  continue;
115
120
  }
@@ -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\nfunction normalizeUrlAttr(value: string): string {\n const withoutControlCharacters = Array.from(value, (character) => {\n const codePoint = character.codePointAt(0) ?? 0;\n return codePoint <= 0x1f || codePoint === 0x7f ? '' : character;\n }).join('');\n return withoutControlCharacters.trim().toLowerCase();\n}\n\nfunction isDangerousUrlAttr(name: string, value: string): boolean {\n if (name !== 'href' && name !== 'src' && name !== 'formaction') return false;\n const normalized = normalizeUrlAttr(value);\n return (\n normalized.startsWith('javascript:') ||\n normalized.startsWith('vbscript:') ||\n normalized.startsWith('data:')\n );\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 isEvent = name.startsWith('on');\n\n if (isEvent || isDangerousUrlAttr(name, attr.value)) {\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 searchRoot = anchorEl.parentElement ?? anchorEl;\n const existing = Array.from(searchRoot.querySelectorAll(`[${dedupAttr}]`)).find(\n (candidate) => candidate.getAttribute(dedupAttr) === label\n );\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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",
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\n/**\n * Syntro's own registered custom elements (`<syntro-pdp>`, tile widgets\u2026) are\n * placeable from config HTML (WI-1, 2026-07-29 WooCommerce PDP parity plan):\n * config HTML is oracle-verified (FEAT-1784879212) and an unregistered custom\n * tag is inert, so the element family is safe to pass \u2014 with the SAME\n * attribute scrubbing (on* handlers, dangerous URLs) as every allowed tag.\n * Non-syntro custom elements stay banned.\n */\nconst SYNTRO_ELEMENT_RE = /^syntro-[a-z][a-z0-9-]*$/;\nconst CONTAINS_SYNTRO_ELEMENT_RE = /<syntro-[a-z]/i;\n\nfunction isAllowedTag(tag: string): boolean {\n return ALLOWED_TAGS.has(tag) || SYNTRO_ELEMENT_RE.test(tag);\n}\n\nfunction normalizeUrlAttr(value: string): string {\n const withoutControlCharacters = Array.from(value, (character) => {\n const codePoint = character.codePointAt(0) ?? 0;\n return codePoint <= 0x1f || codePoint === 0x7f ? '' : character;\n }).join('');\n return withoutControlCharacters.trim().toLowerCase();\n}\n\nfunction isDangerousUrlAttr(name: string, value: string): boolean {\n if (name !== 'href' && name !== 'src' && name !== 'formaction') return false;\n const normalized = normalizeUrlAttr(value);\n return (\n normalized.startsWith('javascript:') ||\n normalized.startsWith('vbscript:') ||\n normalized.startsWith('data:')\n );\n}\n\nexport function sanitizeHtml(html: string): string {\n // Try native Sanitizer API first \u2014 EXCEPT when the config places a\n // syntro-* element: the native API's default config strips unknown custom\n // elements and takes explicit allow-lists only, so the pattern-based\n // family rule must run through the fallback walker below.\n const hasNative =\n typeof (window as any).Sanitizer === 'function' && !CONTAINS_SYNTRO_ELEMENT_RE.test(html);\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 (!isAllowedTag(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 isEvent = name.startsWith('on');\n\n if (isEvent || isDangerousUrlAttr(name, attr.value)) {\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 searchRoot = anchorEl.parentElement ?? anchorEl;\n const existing = Array.from(searchRoot.querySelectorAll(`[${dedupAttr}]`)).find(\n (candidate) => candidate.getAttribute(dedupAttr) === label\n );\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":"sanitizer.d.ts","sourceRoot":"","sources":["../src/sanitizer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAmDH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA2DjD"}
1
+ {"version":3,"file":"sanitizer.d.ts","sourceRoot":"","sources":["../src/sanitizer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAkEH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA+DjD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syntrologie/adapt-content",
3
- "version": "2.29.1",
3
+ "version": "2.30.0",
4
4
  "description": "Adaptive Content app - DOM manipulation actions for text, attributes, and styles",
5
5
  "license": "Proprietary",
6
6
  "private": false,