payload-live-preview 2.3.1 → 2.4.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/README.md +13 -46
- package/dist/adapters/astro/index.js +1 -1
- package/dist/adapters/astro/index.js.map +1 -1
- package/dist/adapters/astro/middleware-entry.js +1 -1
- package/dist/adapters/astro/middleware-entry.js.map +1 -1
- package/dist/adapters/nextjs/client.d.ts +28 -0
- package/dist/adapters/nextjs/client.js +3 -0
- package/dist/adapters/nextjs/client.js.map +1 -0
- package/dist/adapters/nextjs/index.d.ts +34 -12
- package/dist/adapters/nextjs/index.js +1 -1
- package/dist/adapters/nextjs/index.js.map +1 -1
- package/dist/adapters/nuxt/client.d.ts +29 -0
- package/dist/adapters/nuxt/client.js +2 -0
- package/dist/adapters/nuxt/client.js.map +1 -0
- package/dist/adapters/nuxt/index.js +1 -1
- package/dist/adapters/nuxt/index.js.map +1 -1
- package/dist/adapters/nuxt/module.d.ts +23 -3
- package/dist/adapters/nuxt/module.js +1 -1
- package/dist/adapters/nuxt/module.js.map +1 -1
- package/dist/adapters/react/index.d.ts +2 -2
- package/dist/adapters/react/index.js +1 -1
- package/dist/adapters/react/index.js.map +1 -1
- package/dist/adapters/sveltekit/client.d.ts +37 -0
- package/dist/adapters/sveltekit/client.js +2 -0
- package/dist/adapters/sveltekit/client.js.map +1 -0
- package/dist/adapters/sveltekit/index.js +1 -1
- package/dist/adapters/sveltekit/index.js.map +1 -1
- package/dist/adapters/vue/index.d.ts +2 -2
- package/dist/adapters/vue/index.js +1 -1
- package/dist/adapters/vue/index.js.map +1 -1
- package/dist/client.cjs +1 -1
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/core.cjs +1 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +22 -14
- package/dist/core.d.ts +22 -14
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/{document-session-BuYuO3ae.d.ts → document-session-oK92QjES.d.ts} +7 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lean.cjs +1 -1
- package/dist/lean.cjs.map +1 -1
- package/dist/lean.js +1 -1
- package/dist/lean.js.map +1 -1
- package/dist/lexical.cjs.map +1 -1
- package/dist/lexical.js.map +1 -1
- package/dist/plugins.cjs +1 -1
- package/dist/plugins.cjs.map +1 -1
- package/dist/plugins.js +1 -1
- package/dist/plugins.js.map +1 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.js.map +1 -1
- package/dist/structural.cjs.map +1 -1
- package/dist/structural.js.map +1 -1
- package/package.json +23 -1
package/dist/structural.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"structural.cjs","names":["diagnostic","safeConsoleWarn","args","console","warn","value","then","Promise","resolve","catch","noWriteCallbacks","WeakSet","markNoWriteCallback","callback","add","SLOT","Symbol","for","trustedHtml","html","policy","policies","realm","globalThis","existing","created","shared","override","auto","api","candidate","trustedTypes","createPolicy","factory","createHTML","input","resolvePolicy","SAFE_PROTOCOLS","Set","RELATIVE_PATH","PROTOCOL_RELATIVE","PARSER_IGNORED","SCHEME_GAPS","SCHEME","isSafeUrl","url","trimmed","trim","replace","length","startsWith","test","parsed","URL","has","protocol","scheme","exec","toLowerCase","spellsUnsafeScheme","reported","reportDroppedAttribute","name","message","reason","ALLOWED_TAGS","FORM_CONTROLS","REMOVE_COMPLETELY","ATTR_GLOBAL","ATTR_ARIA_PREFIX","ATTR_DATA_PREFIX","ATTR_BY_TAG","a","img","picture","source","audio","video","time","abbr","q","blockquote","table","th","td","col","colgroup","ol","li","code","pre","URL_ATTRIBUTES","SanitizerEnvironmentError","Error","TEMPLATE_ATTRIBUTES","DOCUMENT_SLOT","sanitizeHtmlWithPolicy","options","doc","document","__INLINE_BUILD__","resolveDocument","environmentError","resolved","instancePolicy","mode","attrMap","Map","tag","attrs","Object","entries","set","allowedTags","attrByTag","allowFormControls","templateMode","allowedData","allowed","additionalAllowedTags","forEach","additionalAllowedAttributes","merged","get","attr","allowedDataAttributes","map","markup","pass","template","createElement","innerHTML","fragment","content","sanitizeFragment","written","serialize","key","env","process","isDevelopmentProcess","holder","warned","warnOnce","String","writer","ownerDocument","first","firstChild","append","node","child","Array","from","childNodes","nodeType","sanitizeElement","remove","element","tagName","replaceWith","tagAttrs","attributes","removeAttribute","isSafeSrcset","split","every","setAttribute","sanitizeAttributes","anchor","href","getAttribute","isExternalHttpUrl","hasAttribute","hardenAnchor","HTML_ESCAPES","freeze","HTML_ESCAPE_PATTERN","escapeHtml","text","char","BLOCKED_ATTRIBUTES","isWritableAttribute","PLACEHOLDER_PATTERN","safeStringify","JSON","stringify","getId","item","id","getBlockType","blockType","diffArray","prev","next","patches","prevIndexById","i","consumedFromPrev","to","push","kind","index","prevItem","shallowEqual","oldValue","diffById","min","Math","diffByPosition","b","is","aRec","bRec","aKeys","keys","bKeys","ISLAND_ATTRIBUTE","OWNED_ATTRIBUTE","isMorphBoundary","includes","isIslandRoot","isIslandBoundary","editable","ISLAND_IDENTITY","renderedSlots","WeakMap","isAstroIsland","slotMarkup","island","slot","querySelectorAll","parentElement","closest","retainIslandBoundary","live","rendered","slots","constructor","defaultView","customElements","isArray","observedAttributes","observesProps","props","STATE_ATTRIBUTES","keyOf","attribute","keyAttributes","morphElement","sameKind","boundary","liveBoundary","renderedBoundary","retainBoundary","focus","captureFocus","syncAttributes","retainChildrenOf","liveKeyed","parent","keyed","duplicate","children","slice","indexOf","onDuplicateKey","indexKeyed","keyedElements","values","liveUnkeyed","filter","claimed","usedKeys","renderedNodes","cursor","unkeyedIndex","kept","Element","reconcile","nextSibling","replaceChild","following","isBlankText","insertBefore","morphChildren","restoreFocus","nodeValue","Node","TEXT_NODE","namespaceURI","active","activeElement","HTMLElement","contains","selection","HTMLInputElement","HTMLTextAreaElement","selectionStart","selectionEnd","snapshot","isConnected","preventScroll","start","end","setSelectionRange","TEMPLATE_EXTRA_TAGS","TEMPLATE_EXTRA_ATTRIBUTES","CUSTOM_TAG_PATTERN","optionsByTemplate","KEY_ATTRIBUTE","NESTED_KEY_ATTRIBUTE","NESTED_TEMPLATE_ATTRIBUTE","createStructuralStore","applyStructuralPatches","plan","prepareStructuralPlan","container","commitStructuralPlan","store","morph","isManagedNestedSlot","_live","nextItems","forceRender","sanitizerPolicy","memory","items","everyItem","sharedItemAttributes","templateKeys","collectTemplateKeys","initialChildren","keyedChildren","indexByAttribute","reserved","readKey","takeIndexedElement","replaces","renders","renderItem","nestedSlots","prepareNestedSlots","prepareReconciliation","patch","createPatchPlan","mutated","retained","liveSlot","renderedSlot","synchronizeAttributes","before","getMemory","clear","rememberItem","oldItem","newItem","nextValue","prevValue","oldSlotsByKey","findDirectNestedSlots","plans","newSlot","oldSlot","nextTemplate","previousTemplate","nextNested","readNestedArray","resolvedNext","templateChanged","ancestor","target","nested","elements","indexed","bucket","pop","knownKeys","safe","record","placeholder","prototype","hasOwnProperty","call","hasOwn","Reflect","interpolateArrayTemplate","cached","delete","lruGet","tags","match","matchAll","oldest","size","limit","lruTrim","templateSanitizeOptions","host","firstElementChild","inheritItemAttributes","readDomKeySnapshot","itemKey","warnDuplicateKey","owner","fieldName","codes","states","warnedContainers","warnedKeys","render","rawValue","context","isEmptyValue","arrayTemplate","warnMissingTemplate","previousState","domSnapshot","position","__payloadDomPosition","previous","current","sameDomKeySnapshot","needsForcedRender","nextKeys","seen","find","some","previousKeys","stable","warnAboutKeys","applied","dependsOn","maps","dependents","dependent","result","names","raw"],"sources":["../src/core/diagnostics.ts","../src/core/thenable.ts","../src/core/internal-outcome.ts","../src/security/trusted-types.ts","../src/security/url-validator.ts","../src/security/sanitizer-report.ts","../src/security/sanitizer.ts","../src/types/dev-warning.ts","../src/security/escape.ts","../src/core/attribute-binding.ts","../src/core/array-template.ts","../src/field-types/utils.ts","../src/schema/diff.ts","../src/core/islands.ts","../src/core/morph.ts","../src/core/template-sanitize.ts","../src/core/structural-applier.ts","../src/core/lru.ts","../src/field-types/structural-array.ts","../src/core/dependencies.ts"],"sourcesContent":["import { observeThenableResult } from './thenable';\n\n/**\n * Diagnostics are observability, never control flow: a consumer logger may\n * throw or return a rejected thenable, and reporting a failure must not become\n * a second failure. Invocation stays synchronous for ordinary loggers.\n */\nexport function isolateDiagnostic(\n diagnostic: (...args: unknown[]) => unknown,\n): (...args: unknown[]) => void {\n return (...args): void => {\n try {\n observeThenableResult(diagnostic(...args));\n } catch {\n // Diagnostics must not become a second failure boundary.\n }\n };\n}\n\n/** Shared allocation-free default for disabled diagnostic channels. */\nexport function noopDiagnostic(): void {\n // Intentionally empty.\n}\n\n// Access, invocation and thenable inspection all happen inside the wrapper, so\n// a missing or hostile console cannot escape.\nexport const safeConsoleError = isolateDiagnostic((...args) =>\n (console.error as (...values: unknown[]) => unknown)(...args),\n);\n\nexport const safeConsoleWarn = isolateDiagnostic((...args) =>\n (console.warn as (...values: unknown[]) => unknown)(...args),\n);\n\nexport const safeConsoleDebug = isolateDiagnostic((...args) =>\n // eslint-disable-next-line no-console -- best-effort development diagnostics\n (console.debug as (...values: unknown[]) => unknown)(...args),\n);\n","/**\n * Detect an invalid async result in a synchronous extension point.\n *\n * Rejected native Promises otherwise become unhandled after the caller falls\n * back synchronously. Assimilating the result through a handled Promise keeps\n * that contract violation observable only through the runtime's explicit\n * error channel.\n */\nexport function observeThenableResult(value: unknown): boolean {\n if ((typeof value !== 'object' || value === null) && typeof value !== 'function') return false;\n if (typeof (value as { readonly then?: unknown }).then !== 'function') return false;\n void Promise.resolve(value).catch(() => undefined);\n return true;\n}\n","/**\n * Public renderer callbacks return `void`, but a JavaScript implementation may\n * return anything. Only callbacks marked here may reserve exact `false` as\n * \"no write\", so a custom renderer's incidental boolean cannot change the\n * lifecycle's applied counts.\n */\n\ntype Callback = (...args: never[]) => unknown;\n\nconst noWriteCallbacks = new WeakSet<Callback>();\n\n/** Mark a package-owned callback whose exact `false` return means no write. */\nexport function markNoWriteCallback<T extends Callback>(callback: T): T {\n noWriteCallbacks.add(callback);\n return callback;\n}\n\n/** Whether `callback` owns the internal exact-false sentinel contract. */\nexport function usesNoWriteOutcome(callback: Callback): boolean {\n return noWriteCallbacks.has(callback);\n}\n\n/** Check a renderer object without detaching its public method at the call site. */\nexport function rendererUsesNoWriteOutcome(renderer: { readonly render: Callback }): boolean {\n return noWriteCallbacks.has(renderer.render);\n}\n","/**\n * Trusted Types: every HTML sink routes through one policy, `payload-live-preview`,\n * whose `createHTML` is the identity — what reaches a sink is already sanitized\n * or escaped. Without the API, strings pass through untouched.\n */\n\n/** The subset of the Trusted Types API this module needs. */\nexport interface TrustedHtmlPolicyLike {\n createHTML(input: string): unknown;\n}\n\ninterface TrustedTypesFactoryLike {\n createPolicy(\n name: string,\n rules: { createHTML: (input: string) => string },\n ): TrustedHtmlPolicyLike;\n}\n\n/** @internal */\nexport const TRUSTED_TYPES_POLICY_NAME = 'payload-live-preview';\n\n// Held on the realm, not in this module: every package entry is its own bundle\n// with its own copy of this file, and a page's `trusted-types` directive allows\n// the name once. A second copy that asked for it again would be refused and fall\n// back to strings; instead every copy shares the one policy, and a policy set\n// through one entry reaches the others.\nconst SLOT: unique symbol = Symbol.for('payload-live-preview.trusted-types');\ninterface SharedPolicies {\n override?: TrustedHtmlPolicyLike | null | undefined;\n auto?: TrustedHtmlPolicyLike | null | undefined;\n}\ninterface Realm {\n [SLOT]?: SharedPolicies | undefined;\n}\nfunction shared(): SharedPolicies {\n const realm = globalThis as Realm;\n const existing = realm[SLOT];\n if (existing !== undefined) return existing;\n const created: SharedPolicies = {};\n realm[SLOT] = created;\n return created;\n}\n\n/**\n * Use `policy` for every sink, `null` to assign plain strings, `undefined` to\n * return to the auto-created package policy.\n */\nexport function setTrustedTypesPolicy(policy: TrustedHtmlPolicyLike | null | undefined): void {\n shared().override = policy;\n}\n\nfunction factory(): TrustedTypesFactoryLike | undefined {\n const candidate = (globalThis as { trustedTypes?: unknown }).trustedTypes;\n if (typeof candidate !== 'object' || candidate === null) return undefined;\n const create = (candidate as { createPolicy?: unknown }).createPolicy;\n return typeof create === 'function' ? (candidate as TrustedTypesFactoryLike) : undefined;\n}\n\nfunction resolvePolicy(): TrustedHtmlPolicyLike | null {\n const policies = shared();\n if (policies.override !== undefined) return policies.override;\n if (policies.auto !== undefined) return policies.auto;\n const api = factory();\n try {\n policies.auto =\n api === undefined\n ? null\n : api.createPolicy(TRUSTED_TYPES_POLICY_NAME, { createHTML: (input) => input });\n } catch {\n // The site's `trusted-types` directive does not list this name; the\n // sink assignment will surface the enforcement error.\n policies.auto = null;\n }\n return policies.auto;\n}\n\n/** `html` as a `TrustedHTML` when a policy exists, else the string. Typed `string` for `innerHTML`. @internal */\nexport function trustedHtml(html: string): string {\n const policy = resolvePolicy();\n if (policy === null) return html;\n return policy.createHTML(html) as string;\n}\n\n/** Test hook: forget the auto-created policy. */\nexport function __resetTrustedTypesForTests(): void {\n (globalThis as Realm)[SLOT] = undefined;\n}\n","/**\n * URL safety: the one scheme allow-list the runtime, the client and the\n * sanitizer share. Empty strings are not safe (`<a href=\"\">` is never emitted).\n */\n\nconst SAFE_PROTOCOLS: ReadonlySet<string> = new Set(['http:', 'https:', 'mailto:', 'tel:']);\n\nconst RELATIVE_PATH = /^(?:\\.{1,2}\\/|[a-zA-Z0-9_-]+\\/?)/;\n\n// The URL parser treats `\\` as `/` for special schemes, so `/\\evil.com`\n// resolves to another origin rather than to a same-origin path.\nconst PROTOCOL_RELATIVE = /^[\\\\/]{2}/;\n\n/** The URL parser drops these anywhere in the input before it looks at anything. */\nconst PARSER_IGNORED = /[\\t\\n\\r]/g;\n\n/**\n * What is dropped from a value before it is read for a scheme: JavaScript's\n * whitespace and DOMPurify's `ATTR_WHITESPACE` together. That is ASCII\n * controls and spaces, the no-break spaces, the byte-order mark and the Unicode\n * space and format ranges. The URL parser keeps most of these, so `java\n * script:` is a relative path to it; a sanitizer in front of an older parser\n * reads a scheme through the gaps, and the union stays at least as strict as\n * each of the two.\n */\n/* eslint-disable no-control-regex -- the class takes in DOMPurify's, which has controls */\nconst SCHEME_GAPS = /[\\s\\u0000-\\u001f\\u180e\\u2000-\\u2029]/g;\n/* eslint-enable no-control-regex */\nconst SCHEME = /^([a-z][a-z0-9+.-]*):/i;\n\n/** Whether `value`, with the gaps closed, starts with a scheme the allow-list does not hold. */\nfunction spellsUnsafeScheme(value: string): boolean {\n const scheme = SCHEME.exec(value.replace(SCHEME_GAPS, ''))?.[1];\n return scheme !== undefined && !SAFE_PROTOCOLS.has(`${scheme.toLowerCase()}:`);\n}\n\n/** `true` only for absolute `http`/`https`/`mailto`/`tel` URLs, protocol-relative URLs, paths, `#`/`?` fragments and relative paths. */\nexport function isSafeUrl(url: unknown): boolean {\n if (typeof url !== 'string') return false;\n const trimmed = url.trim().replace(PARSER_IGNORED, '');\n if (trimmed.length === 0) return false;\n if (trimmed.startsWith('#') || trimmed.startsWith('?')) return true;\n if (PROTOCOL_RELATIVE.test(trimmed)) return true;\n if (trimmed.startsWith('/')) return true;\n try {\n const parsed = new URL(trimmed);\n return SAFE_PROTOCOLS.has(parsed.protocol);\n } catch {\n return RELATIVE_PATH.test(trimmed) && !spellsUnsafeScheme(trimmed);\n }\n}\n\n/** Whether a URL points at another HTTP(S) origin, protocol-relative forms included, and so needs `noopener` hardening; every URL these two patterns match is one `isSafeUrl` admits. */\nexport function isExternalHttpUrl(url: string): boolean {\n const trimmed = url.trim().replace(PARSER_IGNORED, '');\n return /^https?:\\/\\//i.test(trimmed) || PROTOCOL_RELATIVE.test(trimmed);\n}\n\n/** The safe scheme set, for tests and introspection. */\nexport const SAFE_URL_PROTOCOLS: ReadonlySet<string> = SAFE_PROTOCOLS;\n","/**\n * LP0409 — what the strict sanitizer removed that the 1.x default kept.\n *\n * `sanitizerPolicy` defaults to `'strict'` since 2.0; before that it was\n * `'compat'`, which let `id` and every `data-*` through, and `name` only where\n * a per-tag list allowed it. The difference\n * only shows when a binding *writes* markup, so a project that upgrades and\n * whose rich text carries its own hooks loses them at the moment an editor\n * types — silently, and in the preview only. Measured on a real consumer: a\n * `data-partner-scroll` attribute driving a CSS selector disappeared on the\n * first write, with nothing said anywhere.\n *\n * So it is said here: once per attribute name per page, with the reason that\n * applies to that name rather than one sentence covering three different\n * decisions. This is a report, never a behaviour — the attribute stays removed.\n */\n\n/**\n * `console.warn` behind a try/catch rather than `@core/diagnostics`: `security`\n * is a leaf layer and may not import `core` (scripts/architecture-rules.ts), and\n * the shared helper's extra work — tolerating a logger that returns a thenable —\n * is for consumer-supplied loggers. This sink is fixed.\n */\nfunction warn(message: string): void {\n try {\n console.warn(message);\n } catch {\n // A diagnostic must never become a second failure.\n }\n}\n\nconst reported = new Set<string>();\n\n/** The binding prefix, restated rather than imported: this module must not pull the applier in. */\nconst BINDING_PREFIX = 'data-payload-';\n\nfunction reason(name: string): string {\n if (name.startsWith(BINDING_PREFIX)) {\n return (\n 'A binding inside CMS content is refused by design — an editor could otherwise ' +\n 'aim a write at any element on the page (docs/security.md)'\n );\n }\n if (name === 'id' || name === 'name') {\n return (\n 'CMS content that can name an element can shadow a global of that name — ' +\n 'DOM clobbering, refused by design (docs/security.md)'\n );\n }\n return (\n 'Put it on an element the template owns, or set ' +\n \"`sanitizerPolicy: 'compat'` to keep every `data-*` as 1.x did\"\n );\n}\n\n/**\n * Report one removal. Only for attributes `'compat'` would have kept: everything\n * else the sanitizer drops was refused in 1.x too and is not news to anyone.\n */\nexport function reportDroppedAttribute(name: string): void {\n if (reported.has(name)) return;\n reported.add(name);\n warn(\n `[live-preview] LP0409: the strict sanitizer removed \\`${name}\\` from written markup; ` +\n `the 1.x default (\\`sanitizerPolicy: 'compat'\\`) kept it. ${reason(name)}.`,\n );\n}\n\n/** Test seam: the report is once per page, and a test is not a page. */\nexport function resetDroppedAttributeReports(): void {\n reported.clear();\n}\n","/**\n * HTML sanitizer built on the host parser (`<template>`): allow-listed tags\n * and attributes, validated URLs, hardened external links, no `style`.\n * Needs a DOM; string-only sanitizers are historically brittle.\n */\n\nimport { warnOnce } from '@/types/dev-warning';\nimport { trustedHtml } from './trusted-types';\nimport { isSafeUrl, isExternalHttpUrl } from './url-validator';\nimport { reportDroppedAttribute } from './sanitizer-report';\n\ndeclare const __INLINE_BUILD__: boolean | undefined;\n\n/** Tags kept with their content. Others are unwrapped unless in `REMOVE_COMPLETELY`. */\nconst ALLOWED_TAGS: ReadonlySet<string> = new Set([\n 'p',\n 'br',\n 'strong',\n 'b',\n 'em',\n 'i',\n 'u',\n 's',\n 'strike',\n 'mark',\n 'small',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'ul',\n 'ol',\n 'li',\n 'dl',\n 'dt',\n 'dd',\n 'blockquote',\n 'code',\n 'pre',\n 'kbd',\n 'samp',\n 'var',\n 'a',\n 'span',\n 'div',\n 'section',\n 'article',\n 'aside',\n 'header',\n 'footer',\n 'nav',\n 'figure',\n 'figcaption',\n 'img',\n 'picture',\n 'source',\n 'audio',\n 'video',\n 'sub',\n 'sup',\n 'hr',\n 'time',\n 'abbr',\n 'cite',\n 'q',\n 'table',\n 'thead',\n 'tbody',\n 'tfoot',\n 'tr',\n 'th',\n 'td',\n 'caption',\n 'colgroup',\n 'col',\n]);\n\n/** Tags `allowFormControls` re-admits for author templates; `form` is deliberately not one. */\nconst FORM_CONTROLS: ReadonlySet<string> = new Set([\n 'input',\n 'button',\n 'textarea',\n 'select',\n 'option',\n 'label',\n]);\n\n/** Removed with their children: the content itself is the hazard. */\nconst REMOVE_COMPLETELY: ReadonlySet<string> = new Set([\n 'script',\n 'style',\n 'iframe',\n 'object',\n 'embed',\n 'link',\n 'meta',\n 'base',\n 'form',\n 'input',\n 'button',\n 'select',\n 'textarea',\n 'svg',\n 'math',\n 'template',\n 'frame',\n 'frameset',\n 'noframes',\n 'noscript',\n]);\n\nconst ATTR_GLOBAL: ReadonlySet<string> = new Set([\n 'id',\n 'class',\n 'lang',\n 'dir',\n 'title',\n 'role',\n 'tabindex',\n]);\n\nconst ATTR_ARIA_PREFIX = 'aria-';\nconst ATTR_DATA_PREFIX = 'data-';\nconst BINDING_DATA_PREFIX = 'data-payload-';\n\nconst ATTR_BY_TAG: Readonly<Record<string, ReadonlySet<string>>> = {\n a: new Set(['href', 'target', 'rel', 'download', 'hreflang', 'type']),\n img: new Set(['src', 'srcset', 'sizes', 'alt', 'width', 'height', 'loading', 'decoding']),\n picture: new Set([]),\n source: new Set(['src', 'srcset', 'sizes', 'type', 'media']),\n audio: new Set(['src', 'controls', 'autoplay', 'loop', 'muted', 'preload']),\n video: new Set([\n 'src',\n 'poster',\n 'controls',\n 'autoplay',\n 'loop',\n 'muted',\n 'preload',\n 'width',\n 'height',\n 'playsinline',\n ]),\n time: new Set(['datetime']),\n abbr: new Set(['title']),\n q: new Set(['cite']),\n blockquote: new Set(['cite']),\n table: new Set(['summary']),\n th: new Set(['colspan', 'rowspan', 'scope', 'headers', 'abbr']),\n td: new Set(['colspan', 'rowspan', 'headers']),\n col: new Set(['span']),\n colgroup: new Set(['span']),\n ol: new Set(['start', 'reversed', 'type']),\n li: new Set(['value']),\n code: new Set(['class']),\n pre: new Set(['class']),\n};\n\n// Checked however the attribute was admitted: `additionalAllowedAttributes` can re-admit the form ones.\nconst URL_ATTRIBUTES: ReadonlySet<string> = new Set([\n 'href',\n 'src',\n 'cite',\n 'poster',\n 'action',\n 'formaction',\n]);\n\n/** Parses `sanitizeHtml` spends before it gives up on an output that will not settle. */\nconst MAX_PASSES = 6;\n\n// Numeric nodeType constants keep an injected SSR document independent of a\n// browser-global `Node` constructor.\nconst ELEMENT_NODE = 1;\nconst COMMENT_NODE = 8;\n\n/** Thrown by `sanitizeHtml()` without a `document`, or with one that cannot write what it cleaned. */\nexport class SanitizerEnvironmentError extends Error {\n override readonly name = 'SanitizerEnvironmentError';\n}\n\n/**\n * `'strict'` (default) strips `id`, `name` and `data-payload-*` and passes\n * other `data-*` only via `allowedDataAttributes`; `'compat'` keeps them.\n */\nexport type SanitizerPolicyMode = 'compat' | 'strict';\n\nexport interface SanitizeOptions {\n /** Overrides the module default set by `setSanitizerPolicy()`. */\n readonly policy?: SanitizerPolicyMode;\n /** `data-*` attributes (full names) that pass under `'strict'`; `data-payload-*` never does. */\n readonly allowedDataAttributes?: readonly string[];\n /**\n * Keep `input`, `button`, `textarea`, `select`, `option` and `label` — only\n * for markup the page author wrote, never for CMS content. `form`,\n * handlers, `style` and unsafe URLs stay dropped.\n */\n readonly allowFormControls?: boolean;\n /** Extra tags to allow. Lower-case, untrimmed. */\n readonly additionalAllowedTags?: readonly string[];\n /**\n * Extra per-tag attributes, lower-case. Cannot re-admit handlers, `style`, or —\n * under `'strict'` — `id`, `name` and `data-payload-*`; their URLs are checked.\n */\n readonly additionalAllowedAttributes?: Readonly<Record<string, readonly string[]>>;\n /**\n * The input is a page-author item template, so `'strict'` keeps the\n * applier's reconciliation attributes (`TEMPLATE_ATTRIBUTES`) and nothing\n * else — every other `data-payload-*` is still stripped, so a template can\n * never add a binding.\n */\n readonly templateMode?: boolean;\n /**\n * The DOM to parse with, for this call: an SSR document such as linkedom's\n * or jsdom's. Wins over `setSanitizerDocument()` and over the global\n * `document`, and two callers with two documents never share one.\n */\n readonly document?: SanitizerDocument;\n}\n\n/** What `templateMode` keeps under `'strict'`. */\nconst TEMPLATE_ATTRIBUTES: ReadonlySet<string> = new Set([\n 'id',\n 'name',\n 'data-payload-key',\n 'data-payload-morph',\n 'data-payload-nested-key',\n 'data-payload-nested-template',\n]);\n\ninterface ResolvedPolicy {\n readonly allowedTags: ReadonlySet<string>;\n readonly allowFormControls: boolean;\n readonly templateMode: boolean;\n readonly mode: SanitizerPolicyMode;\n readonly allowedData: ReadonlySet<string>;\n readonly attrByTag: ReadonlyMap<string, ReadonlySet<string>>;\n}\n\nlet defaultMode: SanitizerPolicyMode = 'strict';\n\n/**\n * The default for calls without an explicit `policy`, per package entry. A\n * runtime carries its own `sanitizerPolicy` and never writes here (ADR 0002).\n */\nexport function setSanitizerPolicy(mode: SanitizerPolicyMode): void {\n defaultMode = mode;\n}\n\n/** `instancePolicy` sits between a per-call `options.policy` and the process default. */\nfunction resolvePolicy(\n options: SanitizeOptions | undefined,\n instancePolicy: SanitizerPolicyMode | undefined,\n): ResolvedPolicy {\n const mode = options?.policy ?? instancePolicy ?? defaultMode;\n if (!options) {\n const attrMap = new Map<string, ReadonlySet<string>>();\n for (const [tag, attrs] of Object.entries(ATTR_BY_TAG)) attrMap.set(tag, attrs);\n return {\n allowedTags: ALLOWED_TAGS,\n attrByTag: attrMap,\n allowFormControls: false,\n templateMode: false,\n mode,\n allowedData: new Set(),\n };\n }\n const allowed = new Set(ALLOWED_TAGS);\n options.additionalAllowedTags?.forEach((tag) => allowed.add(tag));\n if (options.allowFormControls === true) for (const tag of FORM_CONTROLS) allowed.add(tag);\n const attrMap = new Map<string, ReadonlySet<string>>();\n for (const [tag, attrs] of Object.entries(ATTR_BY_TAG)) attrMap.set(tag, attrs);\n for (const [tag, attrs] of Object.entries(options.additionalAllowedAttributes ?? {})) {\n const merged = new Set(attrMap.get(tag));\n for (const attr of attrs) merged.add(attr);\n attrMap.set(tag, merged);\n }\n return {\n allowedTags: allowed,\n attrByTag: attrMap,\n allowFormControls: options.allowFormControls === true,\n templateMode: options.templateMode === true,\n mode,\n allowedData: new Set(options.allowedDataAttributes?.map((name) => name.toLowerCase())),\n };\n}\n\n/** The `Document` surface the sanitizer needs; jsdom, linkedom and happy-dom all provide it. */\nexport interface SanitizerDocument {\n createElement: (tagName: string) => {\n innerHTML: string;\n readonly content: ParentNode;\n };\n}\n\n// Held on the realm, not in this module: every package entry is its own bundle\n// with its own copy of this file, and a document supplied through the root has\n// to reach the rich text `payload-live-preview/lexical` renders.\nconst DOCUMENT_SLOT: unique symbol = Symbol.for('payload-live-preview.sanitizer-document');\n\ninterface DocumentSlot {\n [DOCUMENT_SLOT]?: SanitizerDocument | undefined;\n}\n\n/**\n * Supply a `Document` for SSR, e.g. linkedom's `parseHTML(...).document`;\n * `null` clears it, for every entry. The fallback for a call that names no\n * document of its own.\n *\n * @deprecated Since 2.1 a call names its document: `sanitizeHtml(html, { document })`,\n * `lexicalToHtml(content, { document })`. The process-wide slot stays until 3.0\n * (ADR 0007 ledger).\n */\nexport function setSanitizerDocument(doc: SanitizerDocument | null): void {\n warnOnce(\n 'set-sanitizer-document',\n '`setSanitizerDocument()` is removed in 3.0: pass the document per call, ' +\n '`sanitizeHtml(html, { document })` or `lexicalToHtml(content, { document })` (ADR 0026).',\n );\n (globalThis as DocumentSlot)[DOCUMENT_SLOT] = doc ?? undefined;\n}\n\n/**\n * Sanitize `html`; malformed input is recovered by the parser first. The\n * result is a fixed point: sanitising it again changes nothing.\n *\n * @throws {SanitizerEnvironmentError} when no DOM is available.\n */\nexport function sanitizeHtml(html: string, options?: SanitizeOptions): string {\n return sanitizeHtmlWithPolicy(html, undefined, options);\n}\n\n/**\n * The runtime's entry: `policy` is one instance's, applied unless `options`\n * carry their own, so two clients on a page never share a policy through\n * this module. Not public — a direct caller has `options.policy`.\n */\nexport function sanitizeHtmlWithPolicy(\n html: string,\n policy: SanitizerPolicyMode | undefined,\n options?: SanitizeOptions,\n): string {\n const doc = options?.document ?? resolveDocument();\n if (!doc) throw environmentError();\n if (html === '') return '';\n\n const resolved = resolvePolicy(options, policy);\n // Unwrapping an unknown tag can leave a tree no parser produces (an `a` in an\n // `a`), and the consumer's `innerHTML` rebuilds it as the parser would. So\n // what is written is parsed and sanitised again until a pass changes nothing\n // (ADR 0016, 2026-10-02): the string returned is the one the consumer gets.\n let markup = html;\n for (let pass = 0; pass < MAX_PASSES; pass += 1) {\n const template = doc.createElement('template');\n // The sanitizer's own parse is a sink too: under Trusted Types it needs the policy.\n template.innerHTML = trustedHtml(markup);\n // Read once, and never write the template back: linkedom's `content` is a\n // copy of the template's children, so the template still held the parse\n // and the input came back unsanitised (2.2.1).\n const fragment = template.content;\n sanitizeFragment(fragment, resolved);\n const written = serialize(fragment);\n if (written === markup) return written;\n markup = written;\n }\n warnOnce(\n 'sanitizer-unsettled',\n `sanitizeHtml: the output did not settle after ${String(MAX_PASSES)} passes, so it returned an empty string (ADR 0016).`,\n );\n return '';\n}\n\n// Read the define at each branch rather than through a helper: a bundler\n// folds the substituted literal in place, but will not inline a call, and the\n// SSR-only class and override must not reach the browser runtime.\nfunction environmentError(): Error {\n if (typeof __INLINE_BUILD__ !== 'undefined' && __INLINE_BUILD__) {\n return new Error('sanitizeHtml needs a DOM');\n }\n return new SanitizerEnvironmentError(\n 'sanitizeHtml needs a DOM; pass one per call during SSR: { document } (linkedom, jsdom).',\n );\n}\n\n/**\n * Writes the cleaned nodes themselves, through an element of their own\n * document, where a browser keeps them inert. No such element, or one that does\n * not end up holding them, would write something else: refused, not returned.\n */\nfunction serialize(fragment: ParentNode): string {\n const writer = fragment.ownerDocument?.createElement('div');\n if (writer === undefined) throw environmentError();\n const first = fragment.firstChild;\n writer.append(fragment);\n if (writer.firstChild !== first) throw environmentError();\n return writer.innerHTML;\n}\n\nfunction resolveDocument(): SanitizerDocument | undefined {\n if (typeof __INLINE_BUILD__ === 'undefined' || !__INLINE_BUILD__) {\n const override = (globalThis as DocumentSlot)[DOCUMENT_SLOT];\n if (override) return override;\n }\n if (typeof document === 'undefined') return undefined;\n return document;\n}\n\n/** Whether a DOM is available — the global or an injected one — for callers that treat sanitizing as optional. */\nexport function hasSanitizerDocument(): boolean {\n return resolveDocument() !== undefined;\n}\n\nfunction sanitizeFragment(node: ParentNode, policy: ResolvedPolicy): void {\n // Snapshot: children are mutated during the walk.\n for (const child of Array.from(node.childNodes)) {\n if (child.nodeType === ELEMENT_NODE) {\n sanitizeElement(child as Element, policy);\n } else if (child.nodeType === COMMENT_NODE) {\n child.remove();\n }\n }\n}\n\nfunction sanitizeElement(element: Element, policy: ResolvedPolicy): void {\n const tag = element.tagName.toLowerCase();\n\n if (REMOVE_COMPLETELY.has(tag) && !(policy.allowFormControls && FORM_CONTROLS.has(tag))) {\n element.remove();\n return;\n }\n\n if (!policy.allowedTags.has(tag)) {\n sanitizeFragment(element, policy);\n element.replaceWith(...element.childNodes);\n return;\n }\n\n sanitizeAttributes(element, tag, policy);\n if (tag === 'a') hardenAnchor(element);\n sanitizeFragment(element, policy);\n}\n\nfunction sanitizeAttributes(element: Element, tag: string, policy: ResolvedPolicy): void {\n const tagAttrs = policy.attrByTag.get(tag);\n\n for (const attr of Array.from(element.attributes)) {\n const name = attr.name.toLowerCase();\n if (name.startsWith('on') || name === 'style') {\n element.removeAttribute(attr.name);\n continue;\n }\n // A parsed element's `is` value is immutable and the serialiser writes it\n // back even after the attribute is gone, so removing it would hand the\n // re-parse the page's customised built-in by name. Emptied instead: an\n // empty `is` names nothing (docs/security.md §5c).\n if (name === 'is') {\n element.setAttribute('is', '');\n continue;\n }\n if (policy.mode === 'strict') {\n if (policy.templateMode && TEMPLATE_ATTRIBUTES.has(name)) continue;\n // DOM clobbering and binding injection (docs/security.md §5c). Checked\n // before the allow-list so an extension cannot re-admit them.\n if (name === 'id' || name === 'name' || name.startsWith(BINDING_DATA_PREFIX)) {\n element.removeAttribute(attr.name);\n // `compat` kept `name` only where a per-tag list allowed it, so only\n // there did the upgrade change anything to report.\n if (name !== 'name' || tagAttrs?.has(name) === true) reportDroppedAttribute(name);\n continue;\n }\n if (name.startsWith(ATTR_DATA_PREFIX)) {\n if (!policy.allowedData.has(name)) {\n element.removeAttribute(attr.name);\n reportDroppedAttribute(name);\n }\n continue;\n }\n if (ATTR_GLOBAL.has(name) || name.startsWith(ATTR_ARIA_PREFIX)) continue;\n } else if (\n ATTR_GLOBAL.has(name) ||\n name.startsWith(ATTR_ARIA_PREFIX) ||\n name.startsWith(ATTR_DATA_PREFIX)\n ) {\n continue;\n }\n if (tagAttrs?.has(name)) {\n if (URL_ATTRIBUTES.has(name) && !isSafeUrl(attr.value)) {\n element.removeAttribute(attr.name);\n } else if (name === 'srcset' && !isSafeSrcset(attr.value)) {\n element.removeAttribute(attr.name);\n } else if (name === 'ping' && !attr.value.trim().split(/\\s+/u).every(isSafeUrl)) {\n // `ping` is a list of URLs; one unsafe target refuses the attribute.\n element.removeAttribute(attr.name);\n }\n continue;\n }\n element.removeAttribute(attr.name);\n }\n}\n\n/** Every `srcset` candidate must pass; partial rewriting would silently change what renders. */\nfunction isSafeSrcset(value: string): boolean {\n for (const candidate of value.split(',')) {\n const trimmed = candidate.trim();\n if (trimmed.length === 0) continue;\n // The URL ends at the first whitespace; what follows is the descriptor. The\n // candidate as a whole must pass too: a scheme can be spelled across that gap.\n const [url = trimmed] = trimmed.split(/\\s/, 1);\n if (!isSafeUrl(url) || !isSafeUrl(trimmed)) return false;\n }\n return true;\n}\n\nfunction hardenAnchor(anchor: Element): void {\n const href = anchor.getAttribute('href');\n if (href === null || !isExternalHttpUrl(href)) return;\n anchor.setAttribute('rel', 'noopener noreferrer');\n if (!anchor.hasAttribute('target')) anchor.setAttribute('target', '_blank');\n}\n\n/** The built-in allow-lists, for documentation and tests. */\nexport const SANITIZER_POLICY = Object.freeze({\n allowedTags: ALLOWED_TAGS,\n removeCompletely: REMOVE_COMPLETELY,\n globalAttributes: ATTR_GLOBAL,\n attributesByTag: ATTR_BY_TAG,\n urlAttributes: URL_ATTRIBUTES,\n});\n","/**\n * One development warning per process. The set of issued keys lives on\n * `globalThis` so two package copies in one process still warn once.\n */\n\nconst WARNED_KEY = '__payloadLivePreviewDeprecationsWarned';\n\n/** `true` outside a production build; `false` when `NODE_ENV` is production or unknown. */\nexport function isDevelopmentProcess(): boolean {\n try {\n const env = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process\n ?.env;\n if (env === undefined) return false;\n return env['NODE_ENV'] !== 'production';\n } catch {\n return false;\n }\n}\n\n/** Warn once per process for `key`, outside production only. */\nexport function warnOnce(key: string, message: string): void {\n if (!isDevelopmentProcess()) return;\n const holder = globalThis as unknown as Record<string, Set<string> | undefined>;\n const warned = (holder[WARNED_KEY] ??= new Set<string>());\n if (warned.has(key)) return;\n warned.add(key);\n try {\n console.warn(`[payload-live-preview] ${message}`);\n } catch {\n // A console that throws must not break a request.\n }\n}\n\n/**\n * The deprecated options the adapters and the inline generator both accept;\n * an adapter hands them on to the generator, and the shared keys keep that to\n * one warning each (ADR 0026).\n */\nexport function warnDeprecatedOptions(options: {\n readonly defaults?: string | undefined;\n readonly onUnboundChange?: string | undefined;\n}): void {\n if (options.defaults === 'v1') {\n warnOnce(\n 'defaults-v1',\n \"`defaults: 'v1'` is removed in 3.0: `pll migrate` writes the 1.x rows it stands for \" +\n 'into your options, where they keep working (ADR 0026).',\n );\n }\n if (options.onUnboundChange !== undefined) {\n warnOnce(\n 'on-unbound-change',\n \"`onUnboundChange` is removed in 3.0: name it `onUnfaithfulPatch`, where 'route' is \" +\n \"'escalate'; `pll migrate` renames it (ADR 0026).\",\n );\n }\n}\n\n/** Test hook: forget every warning issued so far. */\nexport function resetDevWarnings(): void {\n const holder = globalThis as unknown as Record<string, Set<string> | undefined>;\n holder[WARNED_KEY] = undefined;\n}\n","/** HTML and CSS escaping primitives (OWASP XSS Prevention Cheat Sheet). DOM-free. */\n\nconst HTML_ESCAPES: Readonly<Record<string, string>> = Object.freeze({\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n '/': '/',\n '`': '`',\n '=': '=',\n});\n\nconst HTML_ESCAPE_PATTERN = /[&<>\"'/`=]/g;\n\n/** Escape `text` for an element body or a *quoted* attribute value; strings only, and unquoted attributes are not covered. */\nexport function escapeHtml(text: string): string {\n // The character class and the map are coupled: every match is a key.\n // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style\n return text.replace(HTML_ESCAPE_PATTERN, (char) => HTML_ESCAPES[char] as string);\n}\n\n/** Escape a value for a CSS `url(...)` literal; this stops the break-out, not the scheme, so pair it with `isSafeUrl()`. */\nexport function escapeCssUrl(value: string): string {\n return value.replace(/['\"()\\\\]/g, '\\\\$&');\n}\n\nconst ATTR_ESCAPES: Readonly<Record<string, string>> = Object.freeze({\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n});\n\nconst ATTR_ESCAPE_PATTERN = /[&<>\"']/g;\n\n/** Escape a value for a *quoted* HTML attribute, leaving URL characters intact; it validates no scheme, so run `isSafeUrl()` on `href`/`src` first. @internal */\nexport function escapeHtmlAttribute(value: string): string {\n // The character class and the map are coupled: every match is a key.\n // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style\n return value.replace(ATTR_ESCAPE_PATTERN, (char) => ATTR_ESCAPES[char] as string);\n}\n\n/** `escapeHtml()` then newlines to `<br>`; the order is what makes it safe. */\nexport function escapeAndLinebreak(text: string): string {\n return escapeHtml(text).replace(/\\r\\n|\\r|\\n/g, '<br>');\n}\n","/**\n * Policed writes for `data-payload-attribute` bindings. The value is\n * remote-controlled, so event handlers, `style`, `srcdoc`, `formaction`/`form`,\n * `id`/`name` (DOM clobbering) and `srcset` (multi-URL syntax) are refused,\n * URL-bearing attributes must pass `isSafeUrl`, and non-scalars never write.\n */\n\nimport { isSafeUrl } from '@security/url-validator';\n\nconst BLOCKED_ATTRIBUTES: ReadonlySet<string> = new Set([\n 'style',\n 'srcdoc',\n 'formaction',\n 'form',\n 'id',\n 'name',\n 'is',\n 'srcset',\n 'imagesrcset',\n]);\n\nconst URL_ATTRIBUTES: ReadonlySet<string> = new Set([\n 'href',\n 'src',\n 'poster',\n 'cite',\n 'action',\n 'xlink:href',\n 'data',\n]);\n\nexport type AttributeApplyResult = 'applied' | 'blocked';\n\n/**\n * Whether this package may write an attribute of this name at all — event\n * handlers, `style`, the two DOM-clobbering names and the multi-URL ones\n * excluded. One rule, two callers: the binding below writes a remote value into\n * it, and the array renderers copy one from the page's own markup onto an item\n * they rebuilt (`src/core/array-template.ts`).\n */\nexport function isWritableAttribute(name: string): boolean {\n return !name.startsWith('on') && !BLOCKED_ATTRIBUTES.has(name);\n}\n\n/** Returns `'blocked'` without touching the DOM when the write is refused. */\nexport function applyAttributeBinding(\n element: Element,\n attribute: string,\n value: unknown,\n): AttributeApplyResult {\n const name = attribute.toLowerCase().trim();\n if (name.length === 0) return 'blocked';\n if (!isWritableAttribute(name)) return 'blocked';\n\n if (value === null || value === undefined) {\n element.removeAttribute(name);\n return 'applied';\n }\n if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') {\n return 'blocked';\n }\n const stringValue = String(value);\n\n if (URL_ATTRIBUTES.has(name) && !isSafeUrl(stringValue)) return 'blocked';\n\n element.setAttribute(name, stringValue);\n return 'applied';\n}\n","/**\n * One array item out of one template: the interpolation, and the attributes the\n * template cannot know about.\n *\n * A replacement value is data, not template syntax: one `replace()` per key\n * would re-interpret `{{index}}` text that an earlier value introduced, and\n * would apply `String.replace`'s `$` semantics to CMS content.\n */\n\nimport { escapeHtml } from '@security/escape';\nimport { isWritableAttribute } from './attribute-binding';\n\nconst PLACEHOLDER_PATTERN = /\\{\\{([\\s\\S]*?)\\}\\}/g;\n\n/** The runtime's own annotations; a rebuilt item gets its own, never a copy. */\nconst BINDING_ATTRIBUTE_PREFIX = 'data-payload-';\n\n/** An attribute name and its value, as they travel from an old item to a new one. */\nexport type InheritedAttribute = readonly [name: string, value: string];\n\nfunction hasOwn(record: object, key: PropertyKey): boolean {\n return Object.prototype.hasOwnProperty.call(record, key);\n}\n\n/**\n * Every key any item carries. A placeholder naming one of these is a field of\n * this array, so a row that happens to lack it renders as nothing; a\n * placeholder no row can fill is a typo in the template and stays visible.\n */\nexport function collectTemplateKeys(items: readonly unknown[]): ReadonlySet<string> {\n const keys = new Set<string>();\n for (const item of items) {\n if (typeof item !== 'object' || item === null) continue;\n for (const key of Object.keys(item)) keys.add(key);\n }\n return keys;\n}\n\nexport function interpolateArrayTemplate(\n template: string,\n item: unknown,\n index: number,\n stringify: (value: unknown) => string,\n knownKeys?: ReadonlySet<string>,\n): string {\n const record = typeof item === 'object' && item !== null ? item : null;\n\n return template.replace(PLACEHOLDER_PATTERN, (placeholder, key: string) => {\n // Preserve the established object-field precedence when an object itself\n // has an `index` or `value` property. Otherwise these are reserved tokens.\n if (record !== null && hasOwn(record, key)) {\n return escapeHtml(stringify(Reflect.get(record, key)));\n }\n if (record === null && key === 'value') return escapeHtml(stringify(item));\n if (key === 'index') return String(index);\n // A field other rows carry: this one simply has none. Writing the\n // placeholder would print template syntax on the page, which is what an\n // editor sees the moment a row is added and not every field is filled in.\n if (record !== null && knownKeys?.has(key) === true) return '';\n return placeholder;\n });\n}\n\n/**\n * What the items already in `container` all carry, and an item rebuilt from the\n * template therefore has to carry too.\n *\n * A framework's scoped styles hang on a marker its compiler writes into the\n * markup — Astro's `data-astro-cid-…`, Vue's `data-v-…`, Svelte's class — and\n * the author's `data-payload-array-template` has none of it. The rebuilt list\n * then renders unstyled next to the one the server sent, while the write itself\n * succeeds, so nothing in the runtime can notice. Naming no framework is the\n * point: what identifies these attributes is that every item of the list has\n * them with the same value, which per-row state by definition does not.\n */\nexport function sharedItemAttributes(container: Element): readonly InheritedAttribute[] {\n const items = container.children;\n const first = items[0];\n if (first === undefined) return [];\n const shared: InheritedAttribute[] = [];\n for (const { name, value } of Array.from(first.attributes)) {\n if (!isWritableAttribute(name) || name.startsWith(BINDING_ATTRIBUTE_PREFIX)) continue;\n let everyItem = true;\n for (let index = 1; index < items.length && everyItem; index += 1) {\n everyItem = items[index]?.getAttribute(name) === value;\n }\n if (everyItem) shared.push([name, value]);\n }\n return shared;\n}\n\n/** Give a rebuilt item what its predecessors shared; the template's own value wins. */\nexport function inheritItemAttributes(item: Element, shared: readonly InheritedAttribute[]): void {\n for (const [name, value] of shared) {\n if (!item.hasAttribute(name)) item.setAttribute(name, value);\n }\n}\n","/** Value coercion shared by the field renderers. */\n\n/** Display string for any value: primitives as-is, objects as JSON, `null`/`undefined` as `''`. */\nexport function safeStringify(value: unknown): string {\n if (value === null || value === undefined) return '';\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value);\n }\n try {\n return JSON.stringify(value);\n } catch {\n return '';\n }\n}\n\n/** The three values every renderer treats as \"clear\" (docs/renderers.md, value semantics). */\nexport function isEmptyValue(value: unknown): value is null | undefined | '' {\n return value === null || value === undefined || value === '';\n}\n","/**\n * Structural diff for array and blocks fields. Items with an `id` are keyed\n * (moves are detected); without ids the diff is positional.\n * @internal\n */\n\nexport type ArrayPatch =\n | { readonly kind: 'insert'; readonly index: number; readonly value: unknown }\n | { readonly kind: 'remove'; readonly index: number; readonly value: unknown }\n | {\n readonly kind: 'move';\n readonly from: number;\n readonly to: number;\n readonly value: unknown;\n }\n | { readonly kind: 'update'; readonly index: number; readonly value: unknown }\n | {\n readonly kind: 'replace';\n readonly index: number;\n readonly oldValue: unknown;\n readonly value: unknown;\n };\n\nfunction getId(item: unknown): string | number | undefined {\n if (typeof item !== 'object' || item === null) return undefined;\n const id = (item as Record<string, unknown>)['id'];\n if (typeof id === 'string' || typeof id === 'number') return id;\n return undefined;\n}\n\nfunction getBlockType(item: unknown): string | undefined {\n if (typeof item !== 'object' || item === null) return undefined;\n const blockType = (item as Record<string, unknown>)['blockType'];\n return typeof blockType === 'string' ? blockType : undefined;\n}\n\n/** Patches that turn `prev` into `next`; a changed `blockType` is a `replace`, never an `update`. */\nexport function diffArray(\n prev: readonly unknown[],\n next: readonly unknown[],\n): readonly ArrayPatch[] {\n const haveIds = next.every((item) => getId(item) !== undefined);\n if (haveIds) return diffById(prev, next);\n return diffByPosition(prev, next);\n}\n\nfunction diffById(prev: readonly unknown[], next: readonly unknown[]): readonly ArrayPatch[] {\n const patches: ArrayPatch[] = [];\n const prevIndexById = new Map<string | number, number>();\n for (let i = 0; i < prev.length; i += 1) {\n const id = getId(prev[i]);\n if (id !== undefined) prevIndexById.set(id, i);\n }\n\n const consumedFromPrev = new Set<number>();\n for (let to = 0; to < next.length; to += 1) {\n const item = next[to];\n const id = getId(item);\n if (id === undefined) {\n patches.push({ kind: 'insert', index: to, value: item });\n continue;\n }\n const from = prevIndexById.get(id);\n if (from === undefined) {\n patches.push({ kind: 'insert', index: to, value: item });\n continue;\n }\n consumedFromPrev.add(from);\n const prevItem = prev[from];\n if (getBlockType(prevItem) !== getBlockType(item)) {\n patches.push({ kind: 'replace', index: to, oldValue: prevItem, value: item });\n continue;\n }\n if (from !== to) {\n patches.push({ kind: 'move', from, to, value: item });\n }\n if (!shallowEqual(prevItem, item)) {\n patches.push({ kind: 'update', index: to, value: item });\n }\n }\n\n for (let i = 0; i < prev.length; i += 1) {\n if (consumedFromPrev.has(i)) continue;\n patches.push({ kind: 'remove', index: i, value: prev[i] });\n }\n return patches;\n}\n\nfunction diffByPosition(prev: readonly unknown[], next: readonly unknown[]): readonly ArrayPatch[] {\n const patches: ArrayPatch[] = [];\n const min = Math.min(prev.length, next.length);\n for (let i = 0; i < min; i += 1) {\n if (!shallowEqual(prev[i], next[i])) {\n patches.push({ kind: 'update', index: i, value: next[i] });\n }\n }\n if (next.length > prev.length) {\n for (let i = prev.length; i < next.length; i += 1) {\n patches.push({ kind: 'insert', index: i, value: next[i] });\n }\n }\n if (prev.length > next.length) {\n for (let i = next.length; i < prev.length; i += 1) {\n patches.push({ kind: 'remove', index: i, value: prev[i] });\n }\n }\n return patches;\n}\n\n// Shallow on purpose: any nested reference change is an update, and the\n// renderer reconciles deeper differences itself.\nfunction shallowEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;\n const aRec = a as Record<string, unknown>;\n const bRec = b as Record<string, unknown>;\n const aKeys = Object.keys(aRec);\n const bKeys = Object.keys(bRec);\n if (aKeys.length !== bKeys.length) return false;\n for (const key of aKeys) {\n if (!Object.is(aRec[key], bRec[key])) return false;\n }\n return true;\n}\n","/**\n * Hydrated islands own their subtree, so the runtime never patches inside one.\n * Instead every flush that carried a change dispatches `payload-live-preview:update`\n * on each island root with the update in `detail`, whether or not a binding\n * outside the islands was written. `data-payload-island=\"patch\"` opts back in.\n * Server HTML reaches an Astro island only through Astro's own props handoff\n * (ADR 0021). @internal\n */\n\nexport const ISLAND_EVENT = 'payload-live-preview:update';\n/** @internal */\nexport const ISLAND_ATTRIBUTE = 'data-payload-island';\nexport const ISLAND_SELECTOR = `astro-island, [${ISLAND_ATTRIBUTE}]`;\n/** The opt-out for a subtree the site scripts itself: the morph never enters it. @internal */\nexport const OWNED_ATTRIBUTE = 'data-payload-owned';\n/**\n * The opt-in for a custom element: present on the live and on the rendered host\n * (its value is ignored), the morph edits the host's attributes and light DOM\n * instead of retaining it whole (ADR 0008 §10). @internal\n */\nexport const MORPH_ATTRIBUTE = 'data-payload-morph';\n\n/**\n * The ownership rule (ADR 0008 §4): a subtree the morph never enters. Custom\n * elements own their subtree unless the template opts in with\n * `data-payload-morph` (§10), islands are a framework's, `contenteditable`\n * is the visitor's, `data-payload-owned` is the site's. The coordinators hand\n * this rule to the engine as `MorphOptions.boundary`; the engine itself only\n * pairs, edits and keeps focus (§9). @beta\n */\nexport function isMorphBoundary(element: Element): boolean {\n const customElement = element.tagName.toLowerCase().includes('-');\n if (customElement && !element.hasAttribute(MORPH_ATTRIBUTE)) return true;\n if (isIslandBoundary(element) || element.hasAttribute(OWNED_ATTRIBUTE)) return true;\n const editable = element.getAttribute('contenteditable');\n return editable !== null && editable !== 'false';\n}\n\nexport interface IslandUpdateDetail {\n readonly fields: Readonly<Record<string, unknown>>;\n readonly revision: number;\n readonly receivedAt: number;\n readonly locale: string | undefined;\n /**\n * Call once the island has rendered this revision, for instance from an\n * effect after its framework committed. Until every island handed the\n * revision has called it, `inspect().revisions.display` says `unconfirmed`\n * (ADR 0023). Repeated, late or superseded calls do nothing.\n */\n readonly displayed: () => void;\n}\n\nfunction isIslandRoot(element: Element): boolean {\n return element.tagName.toLowerCase() === 'astro-island' || element.hasAttribute(ISLAND_ATTRIBUTE);\n}\n\nfunction islandAllowsPatching(island: Element): boolean {\n return island.getAttribute(ISLAND_ATTRIBUTE) === 'patch';\n}\n\n/** Whether `element` itself is an island root that did not opt into patching. */\nexport function isIslandBoundary(element: Element): boolean {\n return isIslandRoot(element) && !islandAllowsPatching(element);\n}\n\n/** Whether `element` (or an ancestor) is an island that did not opt into patching. @internal */\nexport function isInsideIsland(element: Element): boolean {\n let current: Element | null = element;\n while (current !== null) {\n if (isIslandBoundary(current)) return true;\n current = current.parentElement;\n }\n return false;\n}\n\n/** Island roots under `root` that receive update events. */\nexport function collectIslands(root: ParentNode): Element[] {\n const islands: Element[] = [];\n for (const island of root.querySelectorAll(ISLAND_SELECTOR)) {\n if (!islandAllowsPatching(island)) islands.push(island);\n }\n return islands;\n}\n\n/** Astro keeps `ssr` until the framework listener has finished hydrating. @internal */\nexport function isAwaitingIslandHydration(island: Element): boolean {\n return island.tagName.toLowerCase() === 'astro-island' && island.hasAttribute('ssr');\n}\n\nexport function dispatchIslandUpdate(\n islands: readonly Element[],\n detail: IslandUpdateDetail,\n isCurrent: () => boolean,\n): number {\n let dispatched = 0;\n for (const island of islands) {\n if (!isCurrent()) break;\n island.dispatchEvent(\n new CustomEvent<IslandUpdateDetail>(ISLAND_EVENT, { detail, bubbles: false }),\n );\n dispatched += 1;\n if (!isCurrent()) break;\n }\n return dispatched;\n}\n\n/** What decides whether a live `astro-island` still renders the rendered one's component (ADR 0021). */\nconst ISLAND_IDENTITY = [\n 'component-url',\n 'component-export',\n 'renderer-url',\n 'client',\n 'opts',\n 'before-hydration-url',\n] as const;\n/** Slot markup each island was last rendered with; Astro reads slots once, at hydration. */\nconst renderedSlots = new WeakMap<Element, string>();\nconst unmountedIslands = new WeakSet<Element>();\n\nfunction isAstroIsland(element: Element): boolean {\n return element.tagName.toLowerCase() === 'astro-island';\n}\n\nfunction slotMarkup(island: Element): string {\n let markup = '';\n for (const slot of island.querySelectorAll('astro-slot, template[data-astro-template]')) {\n if (slot.parentElement?.closest('astro-island') !== island) continue;\n const name = slot.getAttribute('name') ?? slot.getAttribute('data-astro-template') ?? '';\n markup += `${name}\\u0000${slot.innerHTML}\\u0000`;\n }\n return markup;\n}\n\n/** Only an upgraded island whose class observes `props` re-hydrates in place. */\nfunction observesProps(island: Element): boolean {\n const constructor = island.ownerDocument.defaultView?.customElements.get('astro-island') as\n (CustomElementConstructor & { readonly observedAttributes?: unknown }) | undefined;\n return (\n constructor !== undefined &&\n island instanceof constructor &&\n Array.isArray(constructor.observedAttributes) &&\n constructor.observedAttributes.includes('props')\n );\n}\n\n/**\n * The package's decision for a boundary pair (ADR 0021). A boundary other than\n * an Astro island stays whole (ADR 0008 §4). An island that still renders the\n * same component, with the same slots, keeps its framework state: it takes the\n * rendered props the way Astro's router hands them to a persisted island,\n * `ssr` first, then `props`. Anything else is replaced, and hydrates fresh.\n * @beta\n */\nexport function retainIslandBoundary(live: Element, rendered: Element): boolean {\n if (!isAstroIsland(live) || !isAstroIsland(rendered)) return true;\n const slots = slotMarkup(rendered);\n const retained =\n ISLAND_IDENTITY.every((name) => live.getAttribute(name) === rendered.getAttribute(name)) &&\n (renderedSlots.get(live) ?? slotMarkup(live)) === slots &&\n (live.getAttribute('props') === rendered.getAttribute('props') || observesProps(live));\n if (!retained) {\n renderedSlots.set(rendered, slots);\n return false;\n }\n renderedSlots.set(live, slots);\n const props = rendered.getAttribute('props');\n if (props !== live.getAttribute('props')) {\n live.setAttribute('ssr', '');\n if (props === null) live.removeAttribute('props');\n else live.setAttribute('props', props);\n }\n return true;\n}\n\n/** The Astro islands under `root`, to compare before and after a morph. @internal */\nexport function astroIslandsIn(root: ParentNode): Element[] {\n return Array.from(root.querySelectorAll('astro-island'));\n}\n\n/**\n * Astro's framework clients release their roots on `astro:unmount`, which its\n * router sends after a swap. A morph that disconnects an island sends it once\n * itself, so a removed React root does not outlive its markup. @internal\n */\nexport function releaseDisconnectedIslands(islands: readonly Element[]): void {\n for (const island of islands) {\n if (island.isConnected || unmountedIslands.has(island)) continue;\n unmountedIslands.add(island);\n island.dispatchEvent(new CustomEvent('astro:unmount'));\n }\n}\n\n/**\n * Why an inserted island cannot start: fragment scripts never run, so the page\n * itself must have loaded Astro's island element and this client directive.\n * @internal\n */\nexport function islandStartBlocker(island: Element): 'element' | 'directive' | undefined {\n const view = island.ownerDocument.defaultView;\n if (view?.customElements.get('astro-island') === undefined) return 'element';\n const directive = island.getAttribute('client');\n const astro: unknown = Reflect.get(view, 'Astro');\n if (directive === null || typeof astro !== 'object' || astro === null) return 'directive';\n return Reflect.get(astro, directive) === undefined ? 'directive' : undefined;\n}\n","/**\n * Keyed DOM morph, the engine: edits a live element toward a freshly rendered\n * one while retaining compatible, paired live nodes. Those nodes keep their\n * live properties; an incompatible or unpaired node is replaced and loses\n * them. It pairs children by key or position, synchronises attributes with the\n * state exception and keeps focus across a retained move; which subtrees it\n * never enters is the coordinator's rule, handed in as\n * `MorphOptions.boundary` and defaulting to the package's (`isMorphBoundary`\n * in islands.ts). See ADR 0008, §9 for the line between the two. A custom\n * element is such a boundary unless both of its sides carry\n * `data-payload-morph` (§10).\n */\n\nimport { isMorphBoundary } from './islands';\n\n/** Attributes the CMS controls only when the template names them (ADR 0008 §3). */\nconst STATE_ATTRIBUTES: ReadonlySet<string> = new Set(['open', 'value', 'checked', 'selected']);\n\n/** @beta */\nexport interface MorphOptions {\n /** Attributes that key child elements for pairing, tried in order; unkeyed children pair by position. */\n readonly keyAttributes: readonly string[];\n /** Called once per parent when two children share a key; the later ones pair by position. */\n readonly onDuplicateKey?: (parent: Element, key: string) => void;\n /** Elements whose attributes are synchronised but whose children are left alone (nested structural slots). */\n readonly retainChildrenOf?: (live: Element, rendered: Element) => boolean;\n /**\n * The ownership rule: a structurally compatible live/rendered pair for\n * which this returns `true` on both sides is retained whole and never\n * entered (ADR 0008 §4). The package's rule — custom elements, islands,\n * `contenteditable`, `data-payload-owned` — is the default. A supplied rule\n * replaces that default; compose it with the exported `isMorphBoundary`\n * predicate to add package-compatible boundaries.\n */\n readonly boundary?: (element: Element) => boolean;\n /**\n * Decides a live/rendered pair that are both boundaries: `true` keeps the\n * live one, after any handoff to its owner; `false` replaces it with the\n * rendered one. Without it every such pair is kept whole (ADR 0008 §4). The\n * package's coordinators hand in `retainIslandBoundary`, under which an\n * Astro island that still renders the same component takes the rendered\n * props through Astro's own handoff (ADR 0021).\n */\n readonly retainBoundary?: (live: Element, rendered: Element) => boolean;\n}\n\n/** Empty attribute values (boolean markers such as `data-payload-island`) are not keys. */\nfunction keyOf(element: Element, options: MorphOptions): string | undefined {\n for (const attribute of options.keyAttributes) {\n const value = element.getAttribute(attribute);\n if (value !== null && value.length > 0) return `${attribute}=${value}`;\n }\n return undefined;\n}\n\n/** Whether `live` can be edited toward `rendered` instead of being replaced by it, under `boundary` (the package's rule by default). @internal */\nexport function isMorphCompatible(\n live: Element,\n rendered: Element,\n boundary: (element: Element) => boolean = isMorphBoundary,\n): boolean {\n return (\n live.tagName === rendered.tagName &&\n live.namespaceURI === rendered.namespaceURI &&\n !boundary(live) &&\n !boundary(rendered)\n );\n}\n\n/**\n * Morph `live` toward `rendered`. Returns `live` when it was retained, or\n * `rendered` when the two are incompatible and the caller must replace.\n * `rendered` is consumed: its children may move into `live`.\n *\n * @beta\n */\nexport function morphElement(live: Element, rendered: Element, options: MorphOptions): Element {\n if (!sameKind(live, rendered)) return rendered;\n const boundary = options.boundary ?? isMorphBoundary;\n const liveBoundary = boundary(live);\n const renderedBoundary = boundary(rendered);\n if (liveBoundary || renderedBoundary) {\n if (!liveBoundary || !renderedBoundary) return rendered;\n return (options.retainBoundary?.(live, rendered) ?? true) ? live : rendered;\n }\n const focus = captureFocus(live);\n syncAttributes(live, rendered);\n if (options.retainChildrenOf?.(live, rendered) !== true) morphChildren(live, rendered, options);\n restoreFocus(focus);\n return live;\n}\n\n/** Match attributes, except that state-bearing ones change only when `rendered` carries them. */\nexport function syncAttributes(live: Element, rendered: Element): void {\n for (const attribute of Array.from(live.attributes)) {\n if (rendered.hasAttribute(attribute.name)) continue;\n if (STATE_ATTRIBUTES.has(attribute.name.toLowerCase())) continue;\n live.removeAttribute(attribute.name);\n }\n for (const attribute of Array.from(rendered.attributes)) {\n if (live.getAttribute(attribute.name) !== attribute.value) {\n live.setAttribute(attribute.name, attribute.value);\n }\n }\n}\n\nfunction morphChildren(live: Element, rendered: Element, options: MorphOptions): void {\n const liveKeyed = indexKeyed(live, options);\n const keyedElements = new Set<Node>(liveKeyed.values());\n const liveUnkeyed = Array.from(live.childNodes).filter((node) => !keyedElements.has(node));\n const claimed = new Set<Node>();\n const usedKeys = new Set<string>();\n const renderedNodes = Array.from(rendered.childNodes);\n let cursor: Node | null = live.firstChild;\n let unkeyedIndex = 0;\n\n for (let i = 0; i < renderedNodes.length; i += 1) {\n const next = renderedNodes[i] as Node;\n let kept: Node | null = null;\n let key = next instanceof Element ? keyOf(next, options) : undefined;\n // A repeated key on the rendered side pairs positionally like a live duplicate.\n if (key !== undefined && usedKeys.has(key)) key = undefined;\n if (key !== undefined) {\n usedKeys.add(key);\n const candidate = liveKeyed.get(key);\n if (candidate !== undefined && !claimed.has(candidate)) {\n kept = reconcile(candidate, next, options) === candidate ? candidate : null;\n if (kept === null) {\n candidate.replaceWith(next);\n claimed.add(next);\n cursor = next.nextSibling;\n continue;\n }\n }\n } else {\n while (unkeyedIndex < liveUnkeyed.length) {\n const candidate = liveUnkeyed[unkeyedIndex] as Node;\n unkeyedIndex += 1;\n if (claimed.has(candidate)) continue;\n if (sameKind(candidate, next)) {\n kept = reconcile(candidate, next, options) === candidate ? candidate : null;\n if (kept === null) {\n live.replaceChild(next, candidate);\n claimed.add(next);\n cursor = next.nextSibling;\n }\n break;\n }\n if (candidate instanceof Element) {\n // `next` is a text/comment, or an element whose kind the following\n // rendered node matches: an insertion, so the live element stays for it.\n const following = renderedNodes[i + 1];\n if (\n !(next instanceof Element) ||\n (following !== undefined && sameKind(candidate, following))\n ) {\n unkeyedIndex -= 1;\n break;\n }\n live.replaceChild(next, candidate);\n claimed.add(next);\n cursor = next.nextSibling;\n break;\n }\n if (!(next instanceof Element)) {\n // Text vs comment: replace rather than rewrite one node type's value into the other.\n live.replaceChild(next, candidate);\n claimed.add(next);\n cursor = next.nextSibling;\n break;\n }\n // A surplus live text/comment before the element `next` needs; removed below.\n }\n if (kept === null && claimed.has(next)) continue;\n }\n\n const node: Node = kept ?? next;\n claimed.add(node);\n // Whitespace-only text the template did not render is surplus. Stepping the\n // cursor over it keeps a retained element where it is: `insertBefore` on a\n // node already in the tree is a remove-and-insert, which blurs a focused input.\n while (cursor !== null && cursor !== node && !claimed.has(cursor) && isBlankText(cursor)) {\n cursor = cursor.nextSibling;\n }\n if (node !== cursor) live.insertBefore(node, cursor);\n cursor = node.nextSibling;\n }\n\n for (const child of Array.from(live.childNodes)) {\n if (!claimed.has(child)) child.remove();\n }\n}\n\n/** Retain `candidate` when it can be edited toward `next`; otherwise report `next`. */\nfunction reconcile(candidate: Node, next: Node, options: MorphOptions): Node {\n if (candidate instanceof Element && next instanceof Element) {\n return morphElement(candidate, next, options);\n }\n if (candidate.nodeValue !== next.nodeValue) candidate.nodeValue = next.nodeValue;\n return candidate;\n}\n\nfunction isBlankText(node: Node): boolean {\n return node.nodeType === Node.TEXT_NODE && (node.nodeValue ?? '').trim().length === 0;\n}\n\nfunction sameKind(a: Node, b: Node): boolean {\n if (a.nodeType !== b.nodeType) return false;\n if (a instanceof Element && b instanceof Element) {\n return a.tagName === b.tagName && a.namespaceURI === b.namespaceURI;\n }\n return true;\n}\n\n/** First element per key; later duplicates are left as they are and pair positionally. */\nfunction indexKeyed(parent: Element, options: MorphOptions): Map<string, Element> {\n const keyed = new Map<string, Element>();\n let duplicate: string | undefined;\n for (const child of Array.from(parent.children)) {\n const key = keyOf(child, options);\n if (key === undefined) continue;\n if (keyed.has(key)) {\n duplicate ??= key.slice(key.indexOf('=') + 1);\n continue;\n }\n keyed.set(key, child);\n }\n if (duplicate !== undefined) options.onDuplicateKey?.(parent, duplicate);\n return keyed;\n}\n\n/** @internal */\nexport interface FocusSnapshot {\n readonly element: HTMLElement;\n readonly selection: readonly [number | null, number | null] | null;\n}\n\n/**\n * A keyed move is a remove-and-insert, which blurs; remember what had focus.\n * The structural applier moves items itself and uses the same pair around\n * its commit, so a move it makes keeps focus the way a move the morph makes\n * does (ADR 0008 §1). @internal\n */\nexport function captureFocus(live: Element): FocusSnapshot | null {\n const active = live.ownerDocument.activeElement;\n if (!(active instanceof HTMLElement) || !live.contains(active)) return null;\n const selection =\n active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement\n ? ([active.selectionStart, active.selectionEnd] as const)\n : null;\n return { element: active, selection };\n}\n\n/** @internal */\nexport function restoreFocus(snapshot: FocusSnapshot | null): void {\n if (snapshot === null) return;\n const { element, selection } = snapshot;\n if (element.ownerDocument.activeElement === element || !element.isConnected) return;\n try {\n element.focus({ preventScroll: true });\n const [start, end] = selection ?? [null, null];\n if (start !== null && end !== null) {\n (element as HTMLInputElement).setSelectionRange(start, end);\n }\n } catch {\n // Not focusable any more; nothing to restore.\n }\n}\n","/**\n * Sanitizer options for item templates (`data-payload-array-template`,\n * nested templates). A template is the page author's markup and every\n * interpolated value is escaped to text before the sanitizer runs, so it may\n * carry form controls and custom elements the rich-text policy refuses.\n * Event handlers, `style` and unsafe URLs are still stripped.\n *\n * Some entries below are already admitted by the sanitizer through another\n * route — `video` and `audio` by the tag allow-list, `select` by\n * `allowFormControls`, `name` by `templateMode`, and `controls`, `muted`,\n * `loop`, `poster` and `src` by the per-tag attribute map. They are kept\n * because this list states what a template may contain, and an allow-list\n * that depends on a second list staying unchanged is the more fragile of the\n * two arrangements. Mutating those entries changes nothing observable, so\n * they survive mutation testing by construction; the behaviour they describe\n * is pinned in `tests/unit/core/template-sanitize.test.ts` instead.\n */\n\nimport type { SanitizeOptions } from '@security/sanitizer';\nimport { lruGet, lruSet } from './lru';\n\nconst TEMPLATE_EXTRA_TAGS: readonly string[] = [\n 'input',\n 'textarea',\n 'select',\n 'option',\n 'button',\n 'label',\n 'details',\n 'summary',\n 'dialog',\n 'video',\n 'audio',\n 'progress',\n 'meter',\n];\n\nconst TEMPLATE_EXTRA_ATTRIBUTES: readonly string[] = [\n 'type',\n 'name',\n 'placeholder',\n 'open',\n 'disabled',\n 'readonly',\n 'required',\n 'checked',\n 'selected',\n 'value',\n 'min',\n 'max',\n 'step',\n 'rows',\n 'cols',\n 'for',\n 'controls',\n 'muted',\n 'loop',\n 'poster',\n 'src',\n];\n\nconst CUSTOM_TAG_PATTERN = /<([a-z][a-z0-9]*(?:-[a-z0-9]+)+)\\b/gi;\n\n// 64 like the intl cache: more than a page declares, and a nested template is\n// interpolated with its parent's, so an outer value or `{{index}}` that lands\n// in it mints a key per item and per keystroke (ADR 0003 §3).\nexport const TEMPLATE_CACHE_LIMIT = 64;\nconst optionsByTemplate = new Map<string, SanitizeOptions>();\n\n/** Options for one template; memoised per template string. */\nexport function templateSanitizeOptions(template: string): SanitizeOptions {\n const cached = lruGet(optionsByTemplate, template);\n if (cached !== undefined) return cached;\n const tags = new Set(TEMPLATE_EXTRA_TAGS);\n for (const match of template.matchAll(CUSTOM_TAG_PATTERN)) {\n const tag = match[1]?.toLowerCase();\n if (tag !== undefined) tags.add(tag);\n }\n const attributes: Record<string, readonly string[]> = {};\n for (const tag of tags) attributes[tag] = TEMPLATE_EXTRA_ATTRIBUTES;\n const options: SanitizeOptions = {\n additionalAllowedTags: [...tags],\n additionalAllowedAttributes: attributes,\n allowFormControls: true,\n templateMode: true,\n };\n return lruSet(optionsByTemplate, template, options, TEMPLATE_CACHE_LIMIT);\n}\n","/**\n * Applies an `ArrayPatch[]` diff to a container as keyed DOM operations\n * instead of rebuilding the list, so consumer state on surviving children is\n * kept. Items are paired by `data-payload-key` (the item `id`); items without\n * one pair by position. Nested arrays reconcile recursively through\n * `data-payload-nested-key` slots. See ADR 0008.\n */\n\nimport { sanitizeHtmlWithPolicy, type SanitizerPolicyMode } from '@security/sanitizer';\nimport { trustedHtml } from '@security/trusted-types';\nimport {\n collectTemplateKeys,\n inheritItemAttributes,\n interpolateArrayTemplate,\n sharedItemAttributes,\n type InheritedAttribute,\n} from './array-template';\nimport { safeStringify } from '@field-types/utils';\nimport { diffArray, type ArrayPatch } from '@schema/diff';\nimport { isMorphBoundary, retainIslandBoundary } from './islands';\nimport { captureFocus, restoreFocus, morphElement } from './morph';\nimport { templateSanitizeOptions } from './template-sanitize';\n\n/** @internal */\nexport const KEY_ATTRIBUTE = 'data-payload-key';\nconst NESTED_KEY_ATTRIBUTE = 'data-payload-nested-key';\nconst NESTED_TEMPLATE_ATTRIBUTE = 'data-payload-nested-template';\n\n/** Last value seen per item key, per container; owned by the caller so instances never share it. @internal */\nexport type StructuralStore = WeakMap<Element, Map<string, unknown>>;\n\n/** @internal */\nexport function createStructuralStore(): StructuralStore {\n return new WeakMap<Element, Map<string, unknown>>();\n}\n\n/** @internal */\nexport interface StructuralApplyOptions {\n readonly template: string;\n readonly container: Element;\n readonly patches: readonly ArrayPatch[];\n /** The full new array; inserts and replaces render by index from it. */\n readonly nextItems: readonly unknown[];\n /** Pass the same store across renders so nested arrays diff incrementally. */\n readonly store: StructuralStore;\n /** Re-render existing items even when unchanged. */\n readonly forceRender?: boolean;\n /** The instance's policy for item markup; absent, the process default applies. */\n readonly sanitizerPolicy?: SanitizerPolicyMode | undefined;\n /** Edit a changed item in place instead of replacing it. Default `true`. */\n readonly morph?: boolean;\n /** Reported once per container when two items share a key (`LP0405`). */\n readonly onDuplicateKey?: (container: Element, key: string) => void;\n}\n\n/**\n * Returns `true` for a DOM mutation, `false` for an already-current tree, and\n * `null` when an item's markup has no sanitized element root — preflighted\n * before any mutation, so the previous DOM and memory stay intact.\n * @internal\n */\nexport function applyStructuralPatches(options: StructuralApplyOptions): boolean | null {\n const plan = prepareStructuralPlan(options.container, options.patches, options);\n if (plan === null) return null;\n return commitStructuralPlan(plan, options.store, {\n morph: options.morph ?? true,\n onDuplicateKey: options.onDuplicateKey,\n });\n}\n\n// Plan records are tuples: many are created per recursive render, and named\n// keys would repeat in the inline bundle.\ntype StructuralPlan = readonly [\n container: Element,\n entries: readonly ReconciliationEntry[],\n nextItems: readonly unknown[],\n];\ntype ReconciliationEntry = readonly [\n index: number,\n live: Element | null,\n rendered: Element | undefined,\n nestedSlots: readonly NestedSlotPlan[],\n /** A `replace` patch: a different item, never morphed toward. */\n replace: boolean,\n];\ntype NestedSlotPlan = readonly [\n live: Element | null,\n rendered: Element,\n children: StructuralPlan | undefined,\n];\n\n// One per container, not per item, so — unlike the tuples above — named keys\n// cost the inline bundle nothing per render.\ntype RenderEnv = Pick<\n StructuralApplyOptions,\n 'template' | 'nextItems' | 'forceRender' | 'store' | 'sanitizerPolicy'\n>;\n\ninterface CommitOptions {\n readonly morph: boolean;\n readonly onDuplicateKey: ((container: Element, key: string) => void) | undefined;\n}\n\n/** A rendered slot that still carries a nested template keeps its children for its own plan. */\nfunction isManagedNestedSlot(_live: Element, rendered: Element): boolean {\n return (\n rendered.hasAttribute(NESTED_KEY_ATTRIBUTE) && rendered.hasAttribute(NESTED_TEMPLATE_ATTRIBUTE)\n );\n}\n\n/** Materialise the whole recursive replacement tree before touching live DOM. */\nfunction prepareStructuralPlan(\n container: Element,\n patches: readonly ArrayPatch[],\n env: RenderEnv,\n): StructuralPlan | null {\n const entries = prepareReconciliation(container, createPatchPlan(patches), env);\n return entries === null ? null : [container, entries, env.nextItems];\n}\n\nfunction commitStructuralPlan(\n plan: StructuralPlan,\n store: StructuralStore,\n options: CommitOptions,\n): boolean {\n const [container, entries, nextItems] = plan;\n const claimed = new Set<Element>();\n let mutated = false;\n // Placing a retained item is a remove-and-insert, which blurs whatever the\n // visitor had focused inside it; the morph does the same for a move it\n // makes and restores afterwards, so the commit does too.\n const focus = captureFocus(container);\n for (const [index, live, rendered, nestedSlots, replace] of entries) {\n let node = live;\n if (rendered !== undefined) {\n // Morph a compatible live item in place; otherwise transplant its live\n // nested slots into the rendered item and swap.\n const retained =\n options.morph && !replace && live !== null\n ? morphElement(live, rendered, {\n keyAttributes: [KEY_ATTRIBUTE, NESTED_KEY_ATTRIBUTE],\n boundary: isMorphBoundary,\n retainBoundary: retainIslandBoundary,\n retainChildrenOf: isManagedNestedSlot,\n ...(options.onDuplicateKey !== undefined\n ? { onDuplicateKey: options.onDuplicateKey }\n : {}),\n }) === live\n : false;\n if (!retained) {\n for (const [liveSlot, renderedSlot] of nestedSlots) {\n if (liveSlot === null) continue;\n synchronizeAttributes(liveSlot, renderedSlot);\n renderedSlot.replaceWith(liveSlot);\n }\n }\n for (const [, , children] of nestedSlots) {\n if (children !== undefined) commitStructuralPlan(children, store, options);\n }\n if (retained) {\n mutated = true;\n } else {\n if (live !== null) {\n live.replaceWith(rendered);\n mutated = true;\n }\n node = rendered;\n }\n }\n if (node === null) continue;\n const before = container.children[index] ?? null;\n if (node !== before) {\n container.insertBefore(node, before);\n mutated = true;\n }\n claimed.add(node);\n }\n // `nextItems` is the source of truth; this also drops SSR children the first value did not carry.\n for (const child of Array.from(container.children)) {\n if (claimed.has(child)) continue;\n child.remove();\n mutated = true;\n }\n restoreFocus(focus);\n const memory = getMemory(container, store);\n memory.clear();\n for (const value of nextItems) rememberItem(memory, value);\n return mutated;\n}\n\n/**\n * Pair every final item with a live child (by key, else by position) and\n * render what needs rendering. Patch indices describe `next`, live indices\n * move during reconciliation, so keyed nodes are resolved from the DOM first.\n */\nfunction prepareReconciliation(\n container: Element,\n plan: PatchPlan,\n env: RenderEnv,\n): readonly ReconciliationEntry[] | null {\n const { template, nextItems, forceRender = false, store, sanitizerPolicy } = env;\n const memory = store.get(container);\n // Read before anything is rendered: what the current items share is what the\n // template cannot say, and an inserted item has no predecessor of its own.\n const shared = sharedItemAttributes(container);\n const templateKeys = collectTemplateKeys(nextItems);\n const initialChildren = Array.from(container.children);\n const keyedChildren = indexByAttribute(initialChildren, KEY_ATTRIBUTE);\n const reserved = new Set<Element>();\n const entries: ReconciliationEntry[] = [];\n for (let index = 0; index < nextItems.length; index += 1) {\n const value = nextItems[index];\n const key = readKey(value);\n let live =\n key === undefined\n ? (initialChildren[index] ?? null)\n : (takeIndexedElement(keyedChildren, key) ?? null);\n if (live !== null && reserved.has(live)) live = null;\n if (live !== null) reserved.add(live);\n const replace = plan.replaces.has(index);\n const needsRender = forceRender || plan.renders.has(index) || live === null;\n const rendered = needsRender\n ? renderItem(\n container.ownerDocument,\n template,\n value,\n index,\n sanitizerPolicy,\n shared,\n templateKeys,\n )\n : undefined;\n if (rendered === null) return null;\n const nestedSlots =\n rendered === undefined\n ? []\n : prepareNestedSlots(\n replace ? null : live,\n rendered,\n value,\n key === undefined ? undefined : memory?.get(key),\n env,\n );\n if (nestedSlots === null) return null;\n entries.push([index, live, rendered, nestedSlots, replace]);\n }\n return entries;\n}\n\ninterface PatchPlan {\n readonly renders: ReadonlySet<number>;\n readonly replaces: ReadonlySet<number>;\n}\n\nfunction createPatchPlan(patches: readonly ArrayPatch[]): PatchPlan {\n const renders = new Set<number>();\n const replaces = new Set<number>();\n for (const patch of patches) {\n if (patch.kind === 'insert' || patch.kind === 'update') renders.add(patch.index);\n else if (patch.kind === 'replace') {\n renders.add(patch.index);\n replaces.add(patch.index);\n }\n }\n return { renders, replaces };\n}\n\n/** Plan each direct nested slot; a failure at any depth aborts before commit. */\nfunction prepareNestedSlots(\n oldItem: Element | null,\n newItem: Element,\n nextValue: unknown,\n prevValue: unknown,\n env: RenderEnv,\n): readonly NestedSlotPlan[] | null {\n const oldSlots = oldItem === null ? [] : findDirectNestedSlots(oldItem);\n const oldSlotsByKey = indexByAttribute(oldSlots, NESTED_KEY_ATTRIBUTE);\n const plans: NestedSlotPlan[] = [];\n for (const newSlot of findDirectNestedSlots(newItem)) {\n const key = newSlot.getAttribute(NESTED_KEY_ATTRIBUTE);\n if (key === null) continue;\n const oldSlot = takeIndexedElement(oldSlotsByKey, key) ?? null;\n const nextTemplate = newSlot.getAttribute(NESTED_TEMPLATE_ATTRIBUTE);\n // Without a nested template the slot's rendered static content is authoritative.\n const liveSlot = nextTemplate ? oldSlot : null;\n const previousTemplate = liveSlot?.getAttribute(NESTED_TEMPLATE_ATTRIBUTE) ?? null;\n let children: StructuralPlan | undefined;\n if (nextTemplate) {\n const nextNested = readNestedArray(nextValue, key);\n if (nextNested !== undefined || liveSlot !== null) {\n const resolvedNext = nextNested ?? [];\n const prevNested = liveSlot === null ? [] : (readNestedArray(prevValue, key) ?? []);\n const patches = diffArray(prevNested, resolvedNext);\n const templateChanged = liveSlot !== null && previousTemplate !== nextTemplate;\n if (patches.length > 0 || templateChanged) {\n children =\n prepareStructuralPlan(liveSlot ?? newSlot, patches, {\n template: nextTemplate,\n nextItems: resolvedNext,\n forceRender: templateChanged,\n store: env.store,\n sanitizerPolicy: env.sanitizerPolicy,\n }) ?? undefined;\n if (children === undefined) return null;\n }\n }\n }\n plans.push([liveSlot, newSlot, children]);\n }\n return plans;\n}\n\n/** Only the outermost nested slots: a transplanted slot owns the recursion below it. */\nfunction findDirectNestedSlots(item: Element): readonly Element[] {\n return Array.from(item.querySelectorAll(`[${NESTED_KEY_ATTRIBUTE}]`)).filter((slot) => {\n let ancestor = slot.parentElement;\n while (ancestor !== null && ancestor !== item) {\n if (ancestor.hasAttribute(NESTED_KEY_ATTRIBUTE)) return false;\n ancestor = ancestor.parentElement;\n }\n return ancestor === item;\n });\n}\n\nfunction synchronizeAttributes(target: Element, source: Element): void {\n for (const attribute of Array.from(target.attributes)) {\n if (!source.hasAttribute(attribute.name)) target.removeAttribute(attribute.name);\n }\n for (const attribute of Array.from(source.attributes)) {\n if (target.getAttribute(attribute.name) !== attribute.value) {\n target.setAttribute(attribute.name, attribute.value);\n }\n }\n}\n\nfunction readNestedArray(value: unknown, key: string): readonly unknown[] | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const nested = (value as Record<string, unknown>)[key];\n return Array.isArray(nested) ? (nested as readonly unknown[]) : undefined;\n}\n\nfunction getMemory(container: Element, store: StructuralStore): Map<string, unknown> {\n let map = store.get(container);\n if (!map) {\n map = new Map();\n store.set(container, map);\n }\n return map;\n}\n\nfunction rememberItem(memory: Map<string, unknown>, value: unknown): void {\n const key = readKey(value);\n if (key !== undefined) memory.set(key, value);\n}\n\nfunction readKey(value: unknown): string | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const id = (value as Record<string, unknown>)['id'];\n return typeof id === 'string' || typeof id === 'number' ? String(id) : undefined;\n}\n\nfunction indexByAttribute(elements: readonly Element[], attribute: string): Map<string, Element[]> {\n const indexed = new Map<string, Element[]>();\n // Reverse order so pop() yields duplicates in DOM order.\n for (let index = elements.length - 1; index >= 0; index -= 1) {\n const element = elements[index];\n if (element === undefined) continue;\n const key = element.getAttribute(attribute);\n if (key === null) continue;\n const bucket = indexed.get(key);\n if (bucket === undefined) indexed.set(key, [element]);\n else bucket.push(element);\n }\n return indexed;\n}\n\nfunction takeIndexedElement(indexed: Map<string, Element[]>, key: string): Element | undefined {\n return indexed.get(key)?.pop();\n}\n\n/** Render one item; its `id` is stamped as `data-payload-key` so later diffs find it. */\nfunction renderItem(\n ownerDocument: Document,\n template: string,\n value: unknown,\n index: number,\n policy: SanitizerPolicyMode | undefined,\n shared: readonly InheritedAttribute[],\n knownKeys: ReadonlySet<string>,\n): Element | null {\n const filled = interpolateArrayTemplate(template, value, index, safeStringify, knownKeys);\n const safe = sanitizeHtmlWithPolicy(filled, policy, templateSanitizeOptions(template));\n const host = ownerDocument.createElement('template');\n host.innerHTML = trustedHtml(safe);\n const first = host.content.firstElementChild;\n if (!first) return null;\n // Before the morph, which strips from the live item every attribute the\n // rendered one lacks: the marker has to be on this side to survive.\n inheritItemAttributes(first, shared);\n const key = readKey(value);\n if (key !== undefined) first.setAttribute(KEY_ATTRIBUTE, key);\n return first;\n}\n","/**\n * LRU discipline for a module-scoped lookup `Map` (ADR 0003 §3). Functions\n * over the caller's own `Map` rather than a wrapper class: the inline bundle\n * mangles function names but not property names, so a class would pay for\n * every `this.entries` in full. A `Map` iterates in insertion order, which a\n * touch on every hit turns into recency order. Values are never `undefined`,\n * and a write follows a miss — a key already present keeps its position.\n */\n\n/** Read and touch: a hit becomes the newest entry. */\nexport function lruGet<K, V>(map: Map<K, V>, key: K): V | undefined {\n const value = map.get(key);\n if (value !== undefined) {\n map.delete(key);\n map.set(key, value);\n }\n return value;\n}\n\n/** Write as the newest entry, dropping the oldest ones past `limit`; returns `value`. */\nexport function lruSet<K, V>(map: Map<K, V>, key: K, value: V, limit: number): V {\n map.set(key, value);\n lruTrim(map, limit);\n return value;\n}\n\n/** Drop the oldest entries until `limit` holds. */\nexport function lruTrim<K>(map: Map<K, unknown>, limit: number): void {\n for (const oldest of map.keys()) {\n if (map.size <= limit) break;\n map.delete(oldest);\n }\n}\n","/**\n * `structural-array` renderer (`data-payload-structural`): diffs the previous\n * and next item lists and patches the container through the keyed morph\n * (ADR 0008). All diff memory lives in the closure, one per client.\n */\n\nimport { safeConsoleWarn } from '@core/diagnostics';\nimport { markNoWriteCallback } from '@core/internal-outcome';\nimport {\n applyStructuralPatches,\n createStructuralStore,\n KEY_ATTRIBUTE,\n type StructuralStore,\n} from '@core/structural-applier';\nimport { diffArray } from '@schema/diff';\nimport type { FieldRenderer } from '@core/types';\nimport { isEmptyValue } from './utils';\n\ninterface StructuralRenderState {\n readonly values: readonly unknown[];\n readonly template: string;\n readonly domSnapshot: readonly (string | null)[];\n}\n\ntype WarnedCodes = WeakMap<Element, Set<string>>;\n\n/** @internal */\nexport function createStructuralArrayRenderer(): FieldRenderer {\n const states = new WeakMap<Element, StructuralRenderState>();\n const warnedContainers = new WeakSet<Element>();\n const warnedKeys: WarnedCodes = new WeakMap();\n const store: StructuralStore = createStructuralStore();\n\n return {\n name: 'structural-array',\n render: /* @__PURE__ */ markNoWriteCallback((target, rawValue, context) => {\n const value = isEmptyValue(rawValue) ? [] : rawValue;\n if (!Array.isArray(value)) return false;\n const container = target.element;\n const template = target.arrayTemplate;\n if (template === undefined || template.length === 0) {\n warnMissingTemplate(warnedContainers, container, target.fieldName);\n return false;\n }\n const previousState = states.get(container);\n const domSnapshot = readDomKeySnapshot(container);\n const previous = previousState?.values ?? valuesFromDomSnapshot(domSnapshot);\n const patches = diffArray(previous, value);\n const forceRender = needsForcedRender(previousState, template, domSnapshot);\n if (patches.length === 0 && !forceRender) {\n states.set(container, { values: value.slice(), template, domSnapshot });\n return false;\n }\n warnAboutKeys(warnedKeys, container, target.fieldName, previousState?.values, value);\n const applied = applyStructuralPatches({\n template,\n container,\n patches,\n nextItems: value,\n store,\n forceRender,\n sanitizerPolicy: context.sanitizerPolicy,\n onDuplicateKey: (owner, key) => {\n warnDuplicateKey(warnedKeys, owner, target.fieldName, key);\n },\n });\n if (applied === null) return false;\n states.set(container, {\n values: value.slice(),\n template,\n domSnapshot: readDomKeySnapshot(container),\n });\n return applied ? undefined : false;\n }),\n };\n}\n\nfunction needsForcedRender(\n previous: StructuralRenderState | undefined,\n template: string,\n domSnapshot: readonly (string | null)[],\n): boolean {\n if (previous === undefined) return true;\n return previous.template !== template || !sameDomKeySnapshot(previous.domSnapshot, domSnapshot);\n}\n\n// SSR markup exists before any in-memory snapshot; its direct-child keys let\n// the first update reconcile instead of appending a second copy.\nfunction readDomKeySnapshot(container: Element): readonly (string | null)[] {\n return Array.from(container.children, (child) => child.getAttribute(KEY_ATTRIBUTE));\n}\n\nfunction valuesFromDomSnapshot(snapshot: readonly (string | null)[]): readonly unknown[] {\n return snapshot.map((key, position) =>\n key === null ? { __payloadDomPosition: position } : { id: key },\n );\n}\n\nfunction sameDomKeySnapshot(\n previous: readonly (string | null)[],\n current: readonly (string | null)[],\n): boolean {\n if (previous.length !== current.length) return false;\n return previous.every((key, index) => key === current[index]);\n}\n\nfunction warnMissingTemplate(\n warnedContainers: WeakSet<Element>,\n container: Element,\n fieldName: string,\n): void {\n if (warnedContainers.has(container)) return;\n warnedContainers.add(container);\n safeConsoleWarn(\n `[live-preview] LP0403: Skipping structural update for \"${fieldName}\": ` +\n `<${container.tagName.toLowerCase()} data-payload-structural> needs ` +\n `data-payload-array-template (for example \"<li>{{label}}</li>\").`,\n );\n}\n\nfunction itemKey(value: unknown): string | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const id = (value as Record<string, unknown>)['id'];\n return typeof id === 'string' || typeof id === 'number' ? String(id) : undefined;\n}\n\n/** Key problems degrade the morph, they do not break it; each is announced once per container (ADR 0008 §5). */\nfunction warnAboutKeys(\n warned: WarnedCodes,\n container: Element,\n fieldName: string,\n previous: readonly unknown[] | undefined,\n next: readonly unknown[],\n): void {\n const nextKeys = next.map(itemKey);\n const seen = new Set<string>();\n const duplicate = nextKeys.find((key) => {\n if (key === undefined) return false;\n if (seen.has(key)) return true;\n seen.add(key);\n return false;\n });\n if (duplicate !== undefined) warnDuplicateKey(warned, container, fieldName, duplicate);\n if (nextKeys.some((key) => key === undefined)) {\n warnOnce(\n warned,\n container,\n 'LP0404',\n `LP0404: an item of \"${fieldName}\" has no id and pairs by ` +\n 'position, so an insert re-renders every row after it. Give items a stable id.',\n );\n }\n if (previous?.length === next.length && next.length > 1) {\n const previousKeys = new Set(previous.map(itemKey).filter((key) => key !== undefined));\n const stable = nextKeys.some((key) => key !== undefined && previousKeys.has(key));\n if (previousKeys.size > 0 && !stable) {\n warnOnce(\n warned,\n container,\n 'LP0406',\n `LP0406: every key of \"${fieldName}\" changed at once; the ` +\n 'source generates ids per message, so no item can be retained across updates.',\n );\n }\n }\n}\n\n// Reached from the pre-diff check and from the morph's nested-slot check; one site, one message.\nfunction warnDuplicateKey(\n warned: WarnedCodes,\n owner: Element,\n fieldName: string,\n key: string,\n): void {\n warnOnce(\n warned,\n owner,\n 'LP0405',\n `LP0405: two items of \"${fieldName}\" share the key \"${key}\"; later ones pair by ` +\n 'position. Make `id` unique per item.',\n );\n}\n\nfunction warnOnce(warned: WarnedCodes, container: Element, code: string, message: string): void {\n let codes = warned.get(container);\n if (codes === undefined) {\n codes = new Set();\n warned.set(container, codes);\n }\n if (codes.has(code)) return;\n codes.add(code);\n safeConsoleWarn(`[live-preview] ${message}`);\n}\n","/**\n * One source → dependents map for `skipUnchanged`. Markup declares it the\n * other way round (`data-payload-depends=\"price\"` on the element bound to\n * `priceLabel`) because an author names what a binding needs; the runtime\n * option is keyed by source because the scheduler asks what changed.\n * @internal\n */\n\nexport type DependencyMap = Readonly<Record<string, readonly string[]>>;\n\n/**\n * Field names from an attribute value. Separators are commas and\n * whitespace; empty entries are dropped; order is kept, duplicates removed.\n * @internal\n */\nexport function parseDependencyList(value: string | null | undefined): readonly string[] {\n if (value === null || value === undefined) return [];\n const seen = new Set<string>();\n const names: string[] = [];\n for (const raw of value.split(/[\\s,]+/)) {\n const name = raw.trim();\n if (name.length === 0 || seen.has(name)) continue;\n seen.add(name);\n names.push(name);\n }\n return names;\n}\n\n/**\n * Merge dependency maps (source → dependents). Later maps add to earlier\n * ones; a dependent is listed once per source.\n * @internal\n */\nexport function mergeDependencyMaps(...maps: readonly DependencyMap[]): DependencyMap {\n const merged = new Map<string, Set<string>>();\n for (const map of maps) {\n for (const [source, dependents] of Object.entries(map)) {\n let bucket = merged.get(source);\n if (bucket === undefined) {\n bucket = new Set();\n merged.set(source, bucket);\n }\n for (const dependent of dependents) bucket.add(dependent);\n }\n }\n const result: Record<string, readonly string[]> = {};\n for (const [source, dependents] of merged) result[source] = [...dependents];\n return result;\n}\n\n/**\n * The map an element's `data-payload-depends` contributes: every declared\n * source points at this binding's field.\n * @internal\n */\nexport function dependencyMapFromBinding(\n fieldName: string,\n dependsOn: readonly string[],\n): DependencyMap {\n const map: Record<string, readonly string[]> = {};\n for (const source of dependsOn) {\n if (source === fieldName) continue;\n map[source] = [fieldName];\n }\n return map;\n}\n"],"mappings":"aA8BO,IAtBLA,EAsBWC,GAtBXD,EAsB+C,IAAIE,IAClDC,QAAQC,QAA8CF,GArBhD,IAAIA,KACT,KCFoB,iBADcG,EDIVL,KAAcE,KCHI,OAAVG,GAAoC,mBAAVA,IACD,mBAA/CA,EAAsCC,MAC7CC,QAAQC,QAAQH,GAAOI,MAAM,ODEhC,OAEA,CCPG,IAA+BJ,ICChCK,iBAAA,IAAuBC,QAGtB,SAASC,EAAwCC,GAEtD,OADAH,EAAiBI,IAAID,GACdA,CACT,CCIO,IAODE,iBAAsBC,OAAOC,IAAI,sCAmDhC,SAASC,EAAYC,GAC1B,MAAMC,EApBR,WACE,MAAMC,EAzBR,WACE,MAAMC,EAAQC,WACRC,EAAWF,EAAMP,GACvB,QAAiB,IAAbS,EAAwB,OAAOA,EACnC,MAAMC,EAA0B,GAEhC,OADAH,EAAMP,GAAQU,EACPA,CACT,CAkBmBC,GACjB,QAA0B,IAAtBL,EAASM,SAAwB,OAAON,EAASM,SACrD,QAAsB,IAAlBN,EAASO,KAAoB,OAAOP,EAASO,KACjD,MAAMC,EAXR,WACE,MAAMC,EAAaP,WAA0CQ,aAC7D,GAAyB,iBAAdD,GAAwC,OAAdA,EAErC,MAAyB,mBADTA,EAAyCE,aAClBF,OAAwC,CACjF,CAMcG,GACZ,IACEZ,EAASO,UACC,IAARC,EACI,KACAA,EAAIG,aAhD2B,uBAgDa,CAAEE,WAAaC,GAAUA,GAC7E,OAGEd,EAASO,KAAO,IAClB,CACA,OAAOP,EAASO,IAClB,CAIiBQ,GACf,OAAe,OAAXhB,EAAwBD,EACrBC,EAAOc,WAAWf,EAC3B,CC5EA,IAAMkB,iBAAA,IAA0CC,IAAI,CAAC,QAAS,SAAU,UAAW,SAE7EC,EAAgB,mCAIhBC,EAAoB,YAGpBC,EAAiB,YAYjBC,EAAc,wCAEdC,EAAS,yBASR,SAASC,UAAUC,GACxB,GAAmB,iBAARA,EAAkB,OAAO,EACpC,MAAMC,EAAUD,EAAIE,OAAOC,QAAQP,EAAgB,IACnD,GAAuB,IAAnBK,EAAQG,OAAc,OAAO,EACjC,GAAIH,EAAQI,WAAW,MAAQJ,EAAQI,WAAW,KAAM,OAAO,EAC/D,GAAIV,EAAkBW,KAAKL,GAAU,OAAO,EAC5C,GAAIA,EAAQI,WAAW,KAAM,OAAO,EACpC,IACE,MAAME,EAAS,IAAIC,IAAIP,GACvB,OAAOT,EAAeiB,IAAIF,EAAOG,SACnC,OACE,OAAOhB,EAAcY,KAAKL,KAjB9B,SAA4BzC,GAC1B,MAAMmD,EAASb,EAAOc,KAAKpD,EAAM2C,QAAQN,EAAa,OAAO,GAC7D,YAAkB,IAAXc,IAAyBnB,EAAeiB,IAAI,GAAGE,EAAOE,iBAC/D,CAc2CC,CAAmBb,EAC5D,CACF,CCnBA,IAAMc,iBAAA,IAAetB,IA4Bd,SAASuB,EAAuBC,GACjCF,EAASN,IAAIQ,KACjBF,EAAS9C,IAAIgD,GAtCf,SAAcC,GACZ,IACE5D,QAAQC,KAAK2D,EACf,OAEA,CACF,CAiCE3D,CACE,yDAAyD0D,qFA3B7D,SAAgBA,GACd,OAAIA,EAAKZ,WAHY,iBAKjB,0IAIS,OAATY,GAA0B,SAATA,EAEjB,+HAKF,8GAGJ,CAWkEE,CAAOF,OAEzE,CCpDA,IAAMG,iBAAA,IAAwC3B,IAAI,CAChD,IACA,KACA,SACA,IACA,KACA,IACA,IACA,IACA,SACA,OACA,QACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,aACA,OACA,MACA,MACA,OACA,MACA,IACA,OACA,MACA,UACA,UACA,QACA,SACA,SACA,MACA,SACA,aACA,MACA,UACA,SACA,QACA,QACA,MACA,MACA,KACA,OACA,OACA,OACA,IACA,QACA,QACA,QACA,QACA,KACA,KACA,KACA,UACA,WACA,QAII4B,iBAAA,IAAyC5B,IAAI,CACjD,QACA,SACA,WACA,SACA,SACA,UAII6B,iBAAA,IAA6C7B,IAAI,CACrD,SACA,QACA,SACA,SACA,QACA,OACA,OACA,OACA,OACA,QACA,SACA,SACA,WACA,MACA,OACA,WACA,QACA,WACA,WACA,aAGI8B,iBAAA,IAAuC9B,IAAI,CAC/C,KACA,QACA,OACA,MACA,QACA,OACA,aAGI+B,EAAmB,QACnBC,EAAmB,QAGnBC,EAA6D,CACjEC,iBAAG,IAAIlC,IAAI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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"structural.cjs","names":["diagnostic","safeConsoleWarn","args","console","warn","value","then","Promise","resolve","catch","noWriteCallbacks","WeakSet","markNoWriteCallback","callback","add","SLOT","Symbol","for","trustedHtml","html","policy","policies","realm","globalThis","existing","created","shared","override","auto","api","candidate","trustedTypes","createPolicy","factory","createHTML","input","resolvePolicy","SAFE_PROTOCOLS","Set","RELATIVE_PATH","PROTOCOL_RELATIVE","PARSER_IGNORED","SCHEME_GAPS","SCHEME","isSafeUrl","url","trimmed","trim","replace","length","startsWith","test","parsed","URL","has","protocol","scheme","exec","toLowerCase","spellsUnsafeScheme","reported","reportDroppedAttribute","name","message","reason","ALLOWED_TAGS","FORM_CONTROLS","REMOVE_COMPLETELY","ATTR_GLOBAL","ATTR_ARIA_PREFIX","ATTR_DATA_PREFIX","ATTR_BY_TAG","a","img","picture","source","audio","video","time","abbr","q","blockquote","table","th","td","col","colgroup","ol","li","code","pre","URL_ATTRIBUTES","SanitizerEnvironmentError","Error","TEMPLATE_ATTRIBUTES","DOCUMENT_SLOT","sanitizeHtmlWithPolicy","options","doc","document","__INLINE_BUILD__","resolveDocument","environmentError","resolved","instancePolicy","mode","attrMap","Map","tag","attrs","Object","entries","set","allowedTags","attrByTag","allowFormControls","templateMode","allowedData","allowed","additionalAllowedTags","forEach","additionalAllowedAttributes","merged","get","attr","allowedDataAttributes","map","markup","pass","template","createElement","innerHTML","fragment","content","sanitizeFragment","written","serialize","key","env","process","isDevelopmentProcess","holder","warned","warnOnce","String","writer","ownerDocument","first","firstChild","append","node","child","Array","from","childNodes","nodeType","sanitizeElement","remove","element","tagName","replaceWith","tagAttrs","attributes","removeAttribute","isSafeSrcset","split","every","setAttribute","sanitizeAttributes","anchor","href","getAttribute","isExternalHttpUrl","hasAttribute","hardenAnchor","HTML_ESCAPES","freeze","HTML_ESCAPE_PATTERN","escapeHtml","text","char","BLOCKED_ATTRIBUTES","isWritableAttribute","PLACEHOLDER_PATTERN","safeStringify","JSON","stringify","getId","item","id","getBlockType","blockType","diffArray","prev","next","patches","prevIndexById","i","consumedFromPrev","to","push","kind","index","prevItem","shallowEqual","oldValue","diffById","min","Math","diffByPosition","b","is","aRec","bRec","aKeys","keys","bKeys","ISLAND_ATTRIBUTE","OWNED_ATTRIBUTE","isMorphBoundary","includes","isIslandRoot","isIslandBoundary","editable","ISLAND_IDENTITY","renderedSlots","WeakMap","isAstroIsland","slotMarkup","island","slot","querySelectorAll","parentElement","closest","retainIslandBoundary","live","rendered","slots","constructor","defaultView","customElements","isArray","observedAttributes","observesProps","props","STATE_ATTRIBUTES","keyOf","attribute","keyAttributes","morphElement","sameKind","boundary","liveBoundary","renderedBoundary","retainBoundary","focus","captureFocus","syncAttributes","retainChildrenOf","liveKeyed","parent","keyed","duplicate","children","slice","indexOf","onDuplicateKey","indexKeyed","keyedElements","values","liveUnkeyed","filter","claimed","usedKeys","renderedNodes","cursor","unkeyedIndex","kept","Element","reconcile","nextSibling","replaceChild","following","isBlankText","insertBefore","morphChildren","restoreFocus","nodeValue","Node","TEXT_NODE","namespaceURI","active","activeElement","HTMLElement","contains","selection","HTMLInputElement","HTMLTextAreaElement","selectionStart","selectionEnd","snapshot","isConnected","preventScroll","start","end","setSelectionRange","TEMPLATE_EXTRA_TAGS","TEMPLATE_EXTRA_ATTRIBUTES","CUSTOM_TAG_PATTERN","optionsByTemplate","KEY_ATTRIBUTE","NESTED_KEY_ATTRIBUTE","NESTED_TEMPLATE_ATTRIBUTE","createStructuralStore","applyStructuralPatches","plan","prepareStructuralPlan","container","commitStructuralPlan","store","morph","isManagedNestedSlot","_live","nextItems","forceRender","sanitizerPolicy","memory","items","everyItem","sharedItemAttributes","templateKeys","collectTemplateKeys","initialChildren","keyedChildren","indexByAttribute","reserved","readKey","takeIndexedElement","replaces","renders","renderItem","nestedSlots","prepareNestedSlots","prepareReconciliation","patch","createPatchPlan","mutated","retained","liveSlot","renderedSlot","synchronizeAttributes","before","getMemory","clear","rememberItem","oldItem","newItem","nextValue","prevValue","oldSlotsByKey","findDirectNestedSlots","plans","newSlot","oldSlot","nextTemplate","previousTemplate","nextNested","readNestedArray","resolvedNext","templateChanged","ancestor","target","nested","elements","indexed","bucket","pop","knownKeys","safe","record","placeholder","prototype","hasOwnProperty","call","hasOwn","Reflect","interpolateArrayTemplate","cached","delete","lruGet","tags","match","matchAll","oldest","size","limit","lruTrim","templateSanitizeOptions","host","firstElementChild","inheritItemAttributes","readDomKeySnapshot","itemKey","warnDuplicateKey","owner","fieldName","codes","states","warnedContainers","warnedKeys","render","rawValue","context","isEmptyValue","arrayTemplate","warnMissingTemplate","previousState","domSnapshot","position","__payloadDomPosition","previous","current","sameDomKeySnapshot","needsForcedRender","nextKeys","seen","find","some","previousKeys","stable","warnAboutKeys","applied","dependsOn","maps","dependents","dependent","result","names","raw"],"sources":["../src/core/diagnostics.ts","../src/core/thenable.ts","../src/core/internal-outcome.ts","../src/security/trusted-types.ts","../src/security/url-validator.ts","../src/security/sanitizer-report.ts","../src/security/sanitizer.ts","../src/types/dev-warning.ts","../src/security/escape.ts","../src/core/attribute-binding.ts","../src/core/array-template.ts","../src/field-types/utils.ts","../src/schema/diff.ts","../src/core/islands.ts","../src/core/morph.ts","../src/core/template-sanitize.ts","../src/core/structural-applier.ts","../src/core/lru.ts","../src/field-types/structural-array.ts","../src/core/dependencies.ts"],"sourcesContent":["import { observeThenableResult } from './thenable';\n\n/**\n * Diagnostics are observability, never control flow: a consumer logger may\n * throw or return a rejected thenable, and reporting a failure must not become\n * a second failure. Invocation stays synchronous for ordinary loggers.\n */\nexport function isolateDiagnostic(\n diagnostic: (...args: unknown[]) => unknown,\n): (...args: unknown[]) => void {\n return (...args): void => {\n try {\n observeThenableResult(diagnostic(...args));\n } catch {\n // Diagnostics must not become a second failure boundary.\n }\n };\n}\n\n/** Shared allocation-free default for disabled diagnostic channels. */\nexport function noopDiagnostic(): void {\n // Intentionally empty.\n}\n\n// Access, invocation and thenable inspection all happen inside the wrapper, so\n// a missing or hostile console cannot escape.\nexport const safeConsoleError = isolateDiagnostic((...args) =>\n (console.error as (...values: unknown[]) => unknown)(...args),\n);\n\nexport const safeConsoleWarn = isolateDiagnostic((...args) =>\n (console.warn as (...values: unknown[]) => unknown)(...args),\n);\n\nexport const safeConsoleDebug = isolateDiagnostic((...args) =>\n // eslint-disable-next-line no-console -- best-effort development diagnostics\n (console.debug as (...values: unknown[]) => unknown)(...args),\n);\n","/**\n * Detect an invalid async result in a synchronous extension point.\n *\n * Rejected native Promises otherwise become unhandled after the caller falls\n * back synchronously. Assimilating the result through a handled Promise keeps\n * that contract violation observable only through the runtime's explicit\n * error channel.\n */\nexport function observeThenableResult(value: unknown): boolean {\n if ((typeof value !== 'object' || value === null) && typeof value !== 'function') return false;\n if (typeof (value as { readonly then?: unknown }).then !== 'function') return false;\n void Promise.resolve(value).catch(() => undefined);\n return true;\n}\n","/**\n * Public renderer callbacks return `void`, but a JavaScript implementation may\n * return anything. Only callbacks marked here may reserve exact `false` as\n * \"no write\", so a custom renderer's incidental boolean cannot change the\n * lifecycle's applied counts.\n */\n\ntype Callback = (...args: never[]) => unknown;\n\nconst noWriteCallbacks = new WeakSet<Callback>();\n\n/** Mark a package-owned callback whose exact `false` return means no write. */\nexport function markNoWriteCallback<T extends Callback>(callback: T): T {\n noWriteCallbacks.add(callback);\n return callback;\n}\n\n/** Whether `callback` owns the internal exact-false sentinel contract. */\nexport function usesNoWriteOutcome(callback: Callback): boolean {\n return noWriteCallbacks.has(callback);\n}\n\n/** Check a renderer object without detaching its public method at the call site. */\nexport function rendererUsesNoWriteOutcome(renderer: { readonly render: Callback }): boolean {\n return noWriteCallbacks.has(renderer.render);\n}\n","/**\n * Trusted Types: every HTML sink routes through one policy, `payload-live-preview`,\n * whose `createHTML` is the identity — what reaches a sink is already sanitized\n * or escaped. Without the API, strings pass through untouched.\n */\n\n/** The subset of the Trusted Types API this module needs. */\nexport interface TrustedHtmlPolicyLike {\n createHTML(input: string): unknown;\n}\n\ninterface TrustedTypesFactoryLike {\n createPolicy(\n name: string,\n rules: { createHTML: (input: string) => string },\n ): TrustedHtmlPolicyLike;\n}\n\n/** @internal */\nexport const TRUSTED_TYPES_POLICY_NAME = 'payload-live-preview';\n\n// Held on the realm, not in this module: every package entry is its own bundle\n// with its own copy of this file, and a page's `trusted-types` directive allows\n// the name once. A second copy that asked for it again would be refused and fall\n// back to strings; instead every copy shares the one policy, and a policy set\n// through one entry reaches the others.\nconst SLOT: unique symbol = Symbol.for('payload-live-preview.trusted-types');\ninterface SharedPolicies {\n override?: TrustedHtmlPolicyLike | null | undefined;\n auto?: TrustedHtmlPolicyLike | null | undefined;\n}\ninterface Realm {\n [SLOT]?: SharedPolicies | undefined;\n}\nfunction shared(): SharedPolicies {\n const realm = globalThis as Realm;\n const existing = realm[SLOT];\n if (existing !== undefined) return existing;\n const created: SharedPolicies = {};\n realm[SLOT] = created;\n return created;\n}\n\n/**\n * Use `policy` for every sink, `null` to assign plain strings, `undefined` to\n * return to the auto-created package policy.\n */\nexport function setTrustedTypesPolicy(policy: TrustedHtmlPolicyLike | null | undefined): void {\n shared().override = policy;\n}\n\nfunction factory(): TrustedTypesFactoryLike | undefined {\n const candidate = (globalThis as { trustedTypes?: unknown }).trustedTypes;\n if (typeof candidate !== 'object' || candidate === null) return undefined;\n const create = (candidate as { createPolicy?: unknown }).createPolicy;\n return typeof create === 'function' ? (candidate as TrustedTypesFactoryLike) : undefined;\n}\n\nfunction resolvePolicy(): TrustedHtmlPolicyLike | null {\n const policies = shared();\n if (policies.override !== undefined) return policies.override;\n if (policies.auto !== undefined) return policies.auto;\n const api = factory();\n try {\n policies.auto =\n api === undefined\n ? null\n : api.createPolicy(TRUSTED_TYPES_POLICY_NAME, { createHTML: (input) => input });\n } catch {\n // The site's `trusted-types` directive does not list this name; the\n // sink assignment will surface the enforcement error.\n policies.auto = null;\n }\n return policies.auto;\n}\n\n/** `html` as a `TrustedHTML` when a policy exists, else the string. Typed `string` for `innerHTML`. @internal */\nexport function trustedHtml(html: string): string {\n const policy = resolvePolicy();\n if (policy === null) return html;\n return policy.createHTML(html) as string;\n}\n\n/** Test hook: forget the auto-created policy. */\nexport function __resetTrustedTypesForTests(): void {\n (globalThis as Realm)[SLOT] = undefined;\n}\n","/**\n * URL safety: the one scheme allow-list the runtime, the client and the\n * sanitizer share. Empty strings are not safe (`<a href=\"\">` is never emitted).\n */\n\nconst SAFE_PROTOCOLS: ReadonlySet<string> = new Set(['http:', 'https:', 'mailto:', 'tel:']);\n\nconst RELATIVE_PATH = /^(?:\\.{1,2}\\/|[a-zA-Z0-9_-]+\\/?)/;\n\n// The URL parser treats `\\` as `/` for special schemes, so `/\\evil.com`\n// resolves to another origin rather than to a same-origin path.\nconst PROTOCOL_RELATIVE = /^[\\\\/]{2}/;\n\n/** The URL parser drops these anywhere in the input before it looks at anything. */\nconst PARSER_IGNORED = /[\\t\\n\\r]/g;\n\n/**\n * What is dropped from a value before it is read for a scheme: JavaScript's\n * whitespace and DOMPurify's `ATTR_WHITESPACE` together. That is ASCII\n * controls and spaces, the no-break spaces, the byte-order mark and the Unicode\n * space and format ranges. The URL parser keeps most of these, so `java\n * script:` is a relative path to it; a sanitizer in front of an older parser\n * reads a scheme through the gaps, and the union stays at least as strict as\n * each of the two.\n */\n/* eslint-disable no-control-regex -- the class takes in DOMPurify's, which has controls */\nconst SCHEME_GAPS = /[\\s\\u0000-\\u001f\\u180e\\u2000-\\u2029]/g;\n/* eslint-enable no-control-regex */\nconst SCHEME = /^([a-z][a-z0-9+.-]*):/i;\n\n/** Whether `value`, with the gaps closed, starts with a scheme the allow-list does not hold. */\nfunction spellsUnsafeScheme(value: string): boolean {\n const scheme = SCHEME.exec(value.replace(SCHEME_GAPS, ''))?.[1];\n return scheme !== undefined && !SAFE_PROTOCOLS.has(`${scheme.toLowerCase()}:`);\n}\n\n/** `true` only for absolute `http`/`https`/`mailto`/`tel` URLs, protocol-relative URLs, paths, `#`/`?` fragments and relative paths. */\nexport function isSafeUrl(url: unknown): boolean {\n if (typeof url !== 'string') return false;\n const trimmed = url.trim().replace(PARSER_IGNORED, '');\n if (trimmed.length === 0) return false;\n if (trimmed.startsWith('#') || trimmed.startsWith('?')) return true;\n if (PROTOCOL_RELATIVE.test(trimmed)) return true;\n if (trimmed.startsWith('/')) return true;\n try {\n const parsed = new URL(trimmed);\n return SAFE_PROTOCOLS.has(parsed.protocol);\n } catch {\n return RELATIVE_PATH.test(trimmed) && !spellsUnsafeScheme(trimmed);\n }\n}\n\n/** Whether a URL points at another HTTP(S) origin, protocol-relative forms included, and so needs `noopener` hardening; every URL these two patterns match is one `isSafeUrl` admits. */\nexport function isExternalHttpUrl(url: string): boolean {\n const trimmed = url.trim().replace(PARSER_IGNORED, '');\n return /^https?:\\/\\//i.test(trimmed) || PROTOCOL_RELATIVE.test(trimmed);\n}\n\n/** The safe scheme set, for tests and introspection. */\nexport const SAFE_URL_PROTOCOLS: ReadonlySet<string> = SAFE_PROTOCOLS;\n","/**\n * LP0409 — what the strict sanitizer removed that the 1.x default kept.\n *\n * `sanitizerPolicy` defaults to `'strict'` since 2.0; before that it was\n * `'compat'`, which let `id` and every `data-*` through, and `name` only where\n * a per-tag list allowed it. The difference\n * only shows when a binding *writes* markup, so a project that upgrades and\n * whose rich text carries its own hooks loses them at the moment an editor\n * types — silently, and in the preview only. Measured on a real consumer: a\n * `data-partner-scroll` attribute driving a CSS selector disappeared on the\n * first write, with nothing said anywhere.\n *\n * So it is said here: once per attribute name per page, with the reason that\n * applies to that name rather than one sentence covering three different\n * decisions. This is a report, never a behaviour — the attribute stays removed.\n */\n\n/**\n * `console.warn` behind a try/catch rather than `@core/diagnostics`: `security`\n * is a leaf layer and may not import `core` (scripts/architecture-rules.ts), and\n * the shared helper's extra work — tolerating a logger that returns a thenable —\n * is for consumer-supplied loggers. This sink is fixed.\n */\nfunction warn(message: string): void {\n try {\n console.warn(message);\n } catch {\n // A diagnostic must never become a second failure.\n }\n}\n\nconst reported = new Set<string>();\n\n/** The binding prefix, restated rather than imported: this module must not pull the applier in. */\nconst BINDING_PREFIX = 'data-payload-';\n\nfunction reason(name: string): string {\n if (name.startsWith(BINDING_PREFIX)) {\n return (\n 'A binding inside CMS content is refused by design — an editor could otherwise ' +\n 'aim a write at any element on the page (docs/security.md)'\n );\n }\n if (name === 'id' || name === 'name') {\n return (\n 'CMS content that can name an element can shadow a global of that name — ' +\n 'DOM clobbering, refused by design (docs/security.md)'\n );\n }\n return (\n 'Put it on an element the template owns, or set ' +\n \"`sanitizerPolicy: 'compat'` to keep every `data-*` as 1.x did\"\n );\n}\n\n/**\n * Report one removal. Only for attributes `'compat'` would have kept: everything\n * else the sanitizer drops was refused in 1.x too and is not news to anyone.\n */\nexport function reportDroppedAttribute(name: string): void {\n if (reported.has(name)) return;\n reported.add(name);\n warn(\n `[live-preview] LP0409: the strict sanitizer removed \\`${name}\\` from written markup; ` +\n `the 1.x default (\\`sanitizerPolicy: 'compat'\\`) kept it. ${reason(name)}.`,\n );\n}\n\n/** Test seam: the report is once per page, and a test is not a page. */\nexport function resetDroppedAttributeReports(): void {\n reported.clear();\n}\n","/**\n * HTML sanitizer built on the host parser (`<template>`): allow-listed tags\n * and attributes, validated URLs, hardened external links, no `style`.\n * Needs a DOM; string-only sanitizers are historically brittle.\n */\n\nimport { warnOnce } from '@/types/dev-warning';\nimport { trustedHtml } from './trusted-types';\nimport { isSafeUrl, isExternalHttpUrl } from './url-validator';\nimport { reportDroppedAttribute } from './sanitizer-report';\n\ndeclare const __INLINE_BUILD__: boolean | undefined;\n\n/** Tags kept with their content. Others are unwrapped unless in `REMOVE_COMPLETELY`. */\nconst ALLOWED_TAGS: ReadonlySet<string> = new Set([\n 'p',\n 'br',\n 'strong',\n 'b',\n 'em',\n 'i',\n 'u',\n 's',\n 'strike',\n 'mark',\n 'small',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'ul',\n 'ol',\n 'li',\n 'dl',\n 'dt',\n 'dd',\n 'blockquote',\n 'code',\n 'pre',\n 'kbd',\n 'samp',\n 'var',\n 'a',\n 'span',\n 'div',\n 'section',\n 'article',\n 'aside',\n 'header',\n 'footer',\n 'nav',\n 'figure',\n 'figcaption',\n 'img',\n 'picture',\n 'source',\n 'audio',\n 'video',\n 'sub',\n 'sup',\n 'hr',\n 'time',\n 'abbr',\n 'cite',\n 'q',\n 'table',\n 'thead',\n 'tbody',\n 'tfoot',\n 'tr',\n 'th',\n 'td',\n 'caption',\n 'colgroup',\n 'col',\n]);\n\n/** Tags `allowFormControls` re-admits for author templates; `form` is deliberately not one. */\nconst FORM_CONTROLS: ReadonlySet<string> = new Set([\n 'input',\n 'button',\n 'textarea',\n 'select',\n 'option',\n 'label',\n]);\n\n/** Removed with their children: the content itself is the hazard. */\nconst REMOVE_COMPLETELY: ReadonlySet<string> = new Set([\n 'script',\n 'style',\n 'iframe',\n 'object',\n 'embed',\n 'link',\n 'meta',\n 'base',\n 'form',\n 'input',\n 'button',\n 'select',\n 'textarea',\n 'svg',\n 'math',\n 'template',\n 'frame',\n 'frameset',\n 'noframes',\n 'noscript',\n]);\n\nconst ATTR_GLOBAL: ReadonlySet<string> = new Set([\n 'id',\n 'class',\n 'lang',\n 'dir',\n 'title',\n 'role',\n 'tabindex',\n]);\n\nconst ATTR_ARIA_PREFIX = 'aria-';\nconst ATTR_DATA_PREFIX = 'data-';\nconst BINDING_DATA_PREFIX = 'data-payload-';\n\nconst ATTR_BY_TAG: Readonly<Record<string, ReadonlySet<string>>> = {\n a: new Set(['href', 'target', 'rel', 'download', 'hreflang', 'type']),\n img: new Set(['src', 'srcset', 'sizes', 'alt', 'width', 'height', 'loading', 'decoding']),\n picture: new Set([]),\n source: new Set(['src', 'srcset', 'sizes', 'type', 'media']),\n audio: new Set(['src', 'controls', 'autoplay', 'loop', 'muted', 'preload']),\n video: new Set([\n 'src',\n 'poster',\n 'controls',\n 'autoplay',\n 'loop',\n 'muted',\n 'preload',\n 'width',\n 'height',\n 'playsinline',\n ]),\n time: new Set(['datetime']),\n abbr: new Set(['title']),\n q: new Set(['cite']),\n blockquote: new Set(['cite']),\n table: new Set(['summary']),\n th: new Set(['colspan', 'rowspan', 'scope', 'headers', 'abbr']),\n td: new Set(['colspan', 'rowspan', 'headers']),\n col: new Set(['span']),\n colgroup: new Set(['span']),\n ol: new Set(['start', 'reversed', 'type']),\n li: new Set(['value']),\n code: new Set(['class']),\n pre: new Set(['class']),\n};\n\n// Checked however the attribute was admitted: `additionalAllowedAttributes` can re-admit the form ones.\nconst URL_ATTRIBUTES: ReadonlySet<string> = new Set([\n 'href',\n 'src',\n 'cite',\n 'poster',\n 'action',\n 'formaction',\n]);\n\n/** Parses `sanitizeHtml` spends before it gives up on an output that will not settle. */\nconst MAX_PASSES = 6;\n\n// Numeric nodeType constants keep an injected SSR document independent of a\n// browser-global `Node` constructor.\nconst ELEMENT_NODE = 1;\nconst COMMENT_NODE = 8;\n\n/** Thrown by `sanitizeHtml()` without a `document`, or with one that cannot write what it cleaned. */\nexport class SanitizerEnvironmentError extends Error {\n override readonly name = 'SanitizerEnvironmentError';\n}\n\n/**\n * `'strict'` (default) strips `id`, `name` and `data-payload-*` and passes\n * other `data-*` only via `allowedDataAttributes`; `'compat'` keeps them.\n */\nexport type SanitizerPolicyMode = 'compat' | 'strict';\n\nexport interface SanitizeOptions {\n /** Overrides the module default set by `setSanitizerPolicy()`. */\n readonly policy?: SanitizerPolicyMode;\n /** `data-*` attributes (full names) that pass under `'strict'`; `data-payload-*` never does. */\n readonly allowedDataAttributes?: readonly string[];\n /**\n * Keep `input`, `button`, `textarea`, `select`, `option` and `label` — only\n * for markup the page author wrote, never for CMS content. `form`,\n * handlers, `style` and unsafe URLs stay dropped.\n */\n readonly allowFormControls?: boolean;\n /** Extra tags to allow. Lower-case, untrimmed. */\n readonly additionalAllowedTags?: readonly string[];\n /**\n * Extra per-tag attributes, lower-case. Cannot re-admit handlers, `style`, or —\n * under `'strict'` — `id`, `name` and `data-payload-*`; their URLs are checked.\n */\n readonly additionalAllowedAttributes?: Readonly<Record<string, readonly string[]>>;\n /**\n * The input is a page-author item template, so `'strict'` keeps the\n * applier's reconciliation attributes (`TEMPLATE_ATTRIBUTES`) and nothing\n * else — every other `data-payload-*` is still stripped, so a template can\n * never add a binding.\n */\n readonly templateMode?: boolean;\n /**\n * The DOM to parse with, for this call: an SSR document such as linkedom's\n * or jsdom's. Wins over `setSanitizerDocument()` and over the global\n * `document`, and two callers with two documents never share one.\n */\n readonly document?: SanitizerDocument;\n}\n\n/** What `templateMode` keeps under `'strict'`. */\nconst TEMPLATE_ATTRIBUTES: ReadonlySet<string> = new Set([\n 'id',\n 'name',\n 'data-payload-key',\n 'data-payload-morph',\n 'data-payload-nested-key',\n 'data-payload-nested-template',\n]);\n\ninterface ResolvedPolicy {\n readonly allowedTags: ReadonlySet<string>;\n readonly allowFormControls: boolean;\n readonly templateMode: boolean;\n readonly mode: SanitizerPolicyMode;\n readonly allowedData: ReadonlySet<string>;\n readonly attrByTag: ReadonlyMap<string, ReadonlySet<string>>;\n}\n\nlet defaultMode: SanitizerPolicyMode = 'strict';\n\n/**\n * The default for calls without an explicit `policy`, per package entry. A\n * runtime carries its own `sanitizerPolicy` and never writes here (ADR 0002).\n */\nexport function setSanitizerPolicy(mode: SanitizerPolicyMode): void {\n defaultMode = mode;\n}\n\n/** `instancePolicy` sits between a per-call `options.policy` and the process default. */\nfunction resolvePolicy(\n options: SanitizeOptions | undefined,\n instancePolicy: SanitizerPolicyMode | undefined,\n): ResolvedPolicy {\n const mode = options?.policy ?? instancePolicy ?? defaultMode;\n if (!options) {\n const attrMap = new Map<string, ReadonlySet<string>>();\n for (const [tag, attrs] of Object.entries(ATTR_BY_TAG)) attrMap.set(tag, attrs);\n return {\n allowedTags: ALLOWED_TAGS,\n attrByTag: attrMap,\n allowFormControls: false,\n templateMode: false,\n mode,\n allowedData: new Set(),\n };\n }\n const allowed = new Set(ALLOWED_TAGS);\n options.additionalAllowedTags?.forEach((tag) => allowed.add(tag));\n if (options.allowFormControls === true) for (const tag of FORM_CONTROLS) allowed.add(tag);\n const attrMap = new Map<string, ReadonlySet<string>>();\n for (const [tag, attrs] of Object.entries(ATTR_BY_TAG)) attrMap.set(tag, attrs);\n for (const [tag, attrs] of Object.entries(options.additionalAllowedAttributes ?? {})) {\n const merged = new Set(attrMap.get(tag));\n for (const attr of attrs) merged.add(attr);\n attrMap.set(tag, merged);\n }\n return {\n allowedTags: allowed,\n attrByTag: attrMap,\n allowFormControls: options.allowFormControls === true,\n templateMode: options.templateMode === true,\n mode,\n allowedData: new Set(options.allowedDataAttributes?.map((name) => name.toLowerCase())),\n };\n}\n\n/** The `Document` surface the sanitizer needs; jsdom, linkedom and happy-dom all provide it. */\nexport interface SanitizerDocument {\n createElement: (tagName: string) => {\n innerHTML: string;\n readonly content: ParentNode;\n };\n}\n\n// Held on the realm, not in this module: every package entry is its own bundle\n// with its own copy of this file, and a document supplied through the root has\n// to reach the rich text `payload-live-preview/lexical` renders.\nconst DOCUMENT_SLOT: unique symbol = Symbol.for('payload-live-preview.sanitizer-document');\n\ninterface DocumentSlot {\n [DOCUMENT_SLOT]?: SanitizerDocument | undefined;\n}\n\n/**\n * Supply a `Document` for SSR, e.g. linkedom's `parseHTML(...).document`;\n * `null` clears it, for every entry. The fallback for a call that names no\n * document of its own.\n *\n * @deprecated Since 2.1 a call names its document: `sanitizeHtml(html, { document })`,\n * `lexicalToHtml(content, { document })`. The process-wide slot stays until 3.0\n * (ADR 0007 ledger).\n */\nexport function setSanitizerDocument(doc: SanitizerDocument | null): void {\n warnOnce(\n 'set-sanitizer-document',\n '`setSanitizerDocument()` is removed in 3.0: pass the document per call, ' +\n '`sanitizeHtml(html, { document })` or `lexicalToHtml(content, { document })` (ADR 0026).',\n );\n (globalThis as DocumentSlot)[DOCUMENT_SLOT] = doc ?? undefined;\n}\n\n/**\n * Sanitize `html`; malformed input is recovered by the parser first. The\n * result is a fixed point: sanitising it again changes nothing.\n *\n * @throws {SanitizerEnvironmentError} when no DOM is available.\n */\nexport function sanitizeHtml(html: string, options?: SanitizeOptions): string {\n return sanitizeHtmlWithPolicy(html, undefined, options);\n}\n\n/**\n * The runtime's entry: `policy` is one instance's, applied unless `options`\n * carry their own, so two clients on a page never share a policy through\n * this module. Not public — a direct caller has `options.policy`.\n */\nexport function sanitizeHtmlWithPolicy(\n html: string,\n policy: SanitizerPolicyMode | undefined,\n options?: SanitizeOptions,\n): string {\n const doc = options?.document ?? resolveDocument();\n if (!doc) throw environmentError();\n if (html === '') return '';\n\n const resolved = resolvePolicy(options, policy);\n // Unwrapping an unknown tag can leave a tree no parser produces (an `a` in an\n // `a`), and the consumer's `innerHTML` rebuilds it as the parser would. So\n // what is written is parsed and sanitised again until a pass changes nothing\n // (ADR 0016, 2026-10-02): the string returned is the one the consumer gets.\n let markup = html;\n for (let pass = 0; pass < MAX_PASSES; pass += 1) {\n const template = doc.createElement('template');\n // The sanitizer's own parse is a sink too: under Trusted Types it needs the policy.\n template.innerHTML = trustedHtml(markup);\n // Read once, and never write the template back: linkedom's `content` is a\n // copy of the template's children, so the template still held the parse\n // and the input came back unsanitised (2.2.1).\n const fragment = template.content;\n sanitizeFragment(fragment, resolved);\n const written = serialize(fragment);\n if (written === markup) return written;\n markup = written;\n }\n warnOnce(\n 'sanitizer-unsettled',\n `sanitizeHtml: the output did not settle after ${String(MAX_PASSES)} passes, so it returned an empty string (ADR 0016).`,\n );\n return '';\n}\n\n// Read the define at each branch rather than through a helper: a bundler\n// folds the substituted literal in place, but will not inline a call, and the\n// SSR-only class and override must not reach the browser runtime.\nfunction environmentError(): Error {\n if (typeof __INLINE_BUILD__ !== 'undefined' && __INLINE_BUILD__) {\n return new Error('sanitizeHtml needs a DOM');\n }\n return new SanitizerEnvironmentError(\n 'sanitizeHtml needs a DOM; pass one per call during SSR: { document } (linkedom, jsdom).',\n );\n}\n\n/**\n * Writes the cleaned nodes themselves, through an element of their own\n * document, where a browser keeps them inert. No such element, or one that does\n * not end up holding them, would write something else: refused, not returned.\n */\nfunction serialize(fragment: ParentNode): string {\n const writer = fragment.ownerDocument?.createElement('div');\n if (writer === undefined) throw environmentError();\n const first = fragment.firstChild;\n writer.append(fragment);\n if (writer.firstChild !== first) throw environmentError();\n return writer.innerHTML;\n}\n\nfunction resolveDocument(): SanitizerDocument | undefined {\n if (typeof __INLINE_BUILD__ === 'undefined' || !__INLINE_BUILD__) {\n const override = (globalThis as DocumentSlot)[DOCUMENT_SLOT];\n if (override) return override;\n }\n if (typeof document === 'undefined') return undefined;\n return document;\n}\n\n/** Whether a DOM is available — the global or an injected one — for callers that treat sanitizing as optional. */\nexport function hasSanitizerDocument(): boolean {\n return resolveDocument() !== undefined;\n}\n\nfunction sanitizeFragment(node: ParentNode, policy: ResolvedPolicy): void {\n // Snapshot: children are mutated during the walk.\n for (const child of Array.from(node.childNodes)) {\n if (child.nodeType === ELEMENT_NODE) {\n sanitizeElement(child as Element, policy);\n } else if (child.nodeType === COMMENT_NODE) {\n child.remove();\n }\n }\n}\n\nfunction sanitizeElement(element: Element, policy: ResolvedPolicy): void {\n const tag = element.tagName.toLowerCase();\n\n if (REMOVE_COMPLETELY.has(tag) && !(policy.allowFormControls && FORM_CONTROLS.has(tag))) {\n element.remove();\n return;\n }\n\n if (!policy.allowedTags.has(tag)) {\n sanitizeFragment(element, policy);\n element.replaceWith(...element.childNodes);\n return;\n }\n\n sanitizeAttributes(element, tag, policy);\n if (tag === 'a') hardenAnchor(element);\n sanitizeFragment(element, policy);\n}\n\nfunction sanitizeAttributes(element: Element, tag: string, policy: ResolvedPolicy): void {\n const tagAttrs = policy.attrByTag.get(tag);\n\n for (const attr of Array.from(element.attributes)) {\n const name = attr.name.toLowerCase();\n if (name.startsWith('on') || name === 'style') {\n element.removeAttribute(attr.name);\n continue;\n }\n // A parsed element's `is` value is immutable and the serialiser writes it\n // back even after the attribute is gone, so removing it would hand the\n // re-parse the page's customised built-in by name. Emptied instead: an\n // empty `is` names nothing (docs/security.md §5c).\n if (name === 'is') {\n element.setAttribute('is', '');\n continue;\n }\n if (policy.mode === 'strict') {\n if (policy.templateMode && TEMPLATE_ATTRIBUTES.has(name)) continue;\n // DOM clobbering and binding injection (docs/security.md §5c). Checked\n // before the allow-list so an extension cannot re-admit them.\n if (name === 'id' || name === 'name' || name.startsWith(BINDING_DATA_PREFIX)) {\n element.removeAttribute(attr.name);\n // `compat` kept `name` only where a per-tag list allowed it, so only\n // there did the upgrade change anything to report.\n if (name !== 'name' || tagAttrs?.has(name) === true) reportDroppedAttribute(name);\n continue;\n }\n if (name.startsWith(ATTR_DATA_PREFIX)) {\n if (!policy.allowedData.has(name)) {\n element.removeAttribute(attr.name);\n reportDroppedAttribute(name);\n }\n continue;\n }\n if (ATTR_GLOBAL.has(name) || name.startsWith(ATTR_ARIA_PREFIX)) continue;\n } else if (\n ATTR_GLOBAL.has(name) ||\n name.startsWith(ATTR_ARIA_PREFIX) ||\n name.startsWith(ATTR_DATA_PREFIX)\n ) {\n continue;\n }\n if (tagAttrs?.has(name)) {\n if (URL_ATTRIBUTES.has(name) && !isSafeUrl(attr.value)) {\n element.removeAttribute(attr.name);\n } else if (name === 'srcset' && !isSafeSrcset(attr.value)) {\n element.removeAttribute(attr.name);\n } else if (name === 'ping' && !attr.value.trim().split(/\\s+/u).every(isSafeUrl)) {\n // `ping` is a list of URLs; one unsafe target refuses the attribute.\n element.removeAttribute(attr.name);\n }\n continue;\n }\n element.removeAttribute(attr.name);\n }\n}\n\n/** Every `srcset` candidate must pass; partial rewriting would silently change what renders. */\nfunction isSafeSrcset(value: string): boolean {\n for (const candidate of value.split(',')) {\n const trimmed = candidate.trim();\n if (trimmed.length === 0) continue;\n // The URL ends at the first whitespace; what follows is the descriptor. The\n // candidate as a whole must pass too: a scheme can be spelled across that gap.\n const [url = trimmed] = trimmed.split(/\\s/, 1);\n if (!isSafeUrl(url) || !isSafeUrl(trimmed)) return false;\n }\n return true;\n}\n\nfunction hardenAnchor(anchor: Element): void {\n const href = anchor.getAttribute('href');\n if (href === null || !isExternalHttpUrl(href)) return;\n anchor.setAttribute('rel', 'noopener noreferrer');\n if (!anchor.hasAttribute('target')) anchor.setAttribute('target', '_blank');\n}\n\n/** The built-in allow-lists, for documentation and tests. */\nexport const SANITIZER_POLICY = Object.freeze({\n allowedTags: ALLOWED_TAGS,\n removeCompletely: REMOVE_COMPLETELY,\n globalAttributes: ATTR_GLOBAL,\n attributesByTag: ATTR_BY_TAG,\n urlAttributes: URL_ATTRIBUTES,\n});\n","/**\n * One development warning per process. The set of issued keys lives on\n * `globalThis` so two package copies in one process still warn once.\n */\n\nconst WARNED_KEY = '__payloadLivePreviewDeprecationsWarned';\n\n/** `true` outside a production build; `false` when `NODE_ENV` is production or unknown. */\nexport function isDevelopmentProcess(): boolean {\n try {\n const env = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process\n ?.env;\n if (env === undefined) return false;\n return env['NODE_ENV'] !== 'production';\n } catch {\n return false;\n }\n}\n\n/** Warn once per process for `key`, outside production only. */\nexport function warnOnce(key: string, message: string): void {\n if (!isDevelopmentProcess()) return;\n const holder = globalThis as unknown as Record<string, Set<string> | undefined>;\n const warned = (holder[WARNED_KEY] ??= new Set<string>());\n if (warned.has(key)) return;\n warned.add(key);\n try {\n console.warn(`[payload-live-preview] ${message}`);\n } catch {\n // A console that throws must not break a request.\n }\n}\n\n/**\n * The deprecated options the adapters and the inline generator both accept;\n * an adapter hands them on to the generator, and the shared keys keep that to\n * one warning each (ADR 0026).\n */\nexport function warnDeprecatedOptions(options: {\n readonly defaults?: string | undefined;\n readonly onUnboundChange?: string | undefined;\n}): void {\n if (options.defaults === 'v1') {\n warnOnce(\n 'defaults-v1',\n \"`defaults: 'v1'` is removed in 3.0: `pll migrate` writes the 1.x rows it stands for \" +\n 'into your options, where they keep working (ADR 0026).',\n );\n }\n if (options.onUnboundChange !== undefined) {\n warnOnce(\n 'on-unbound-change',\n \"`onUnboundChange` is removed in 3.0: name it `onUnfaithfulPatch`, where 'route' is \" +\n \"'escalate'; `pll migrate` renames it (ADR 0026).\",\n );\n }\n}\n\n/**\n * The adapter and generator options whose 2.x default 3.0 changes, said once\n * per process in development (ADR 0026, 2026-11-03). A browser-only client\n * hears nothing, as for the removals above.\n */\nexport function warnChangingDefaults(options: {\n readonly mergeTimeoutMs?: number | undefined;\n}): void {\n if (options.mergeTimeoutMs === undefined) {\n warnOnce(\n 'merge-deadline',\n '`mergeTimeoutMs` is not set, so a merge waits as long as the server takes; 3.0 gives it a ' +\n 'default deadline. Set `mergeTimeoutMs` now, or `0` to keep none (ADR 0007, row 27).',\n );\n }\n}\n\n/** Test hook: forget every warning issued so far. */\nexport function resetDevWarnings(): void {\n const holder = globalThis as unknown as Record<string, Set<string> | undefined>;\n holder[WARNED_KEY] = undefined;\n}\n","/** HTML and CSS escaping primitives (OWASP XSS Prevention Cheat Sheet). DOM-free. */\n\nconst HTML_ESCAPES: Readonly<Record<string, string>> = Object.freeze({\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n '/': '/',\n '`': '`',\n '=': '=',\n});\n\nconst HTML_ESCAPE_PATTERN = /[&<>\"'/`=]/g;\n\n/** Escape `text` for an element body or a *quoted* attribute value; strings only, and unquoted attributes are not covered. */\nexport function escapeHtml(text: string): string {\n // The character class and the map are coupled: every match is a key.\n // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style\n return text.replace(HTML_ESCAPE_PATTERN, (char) => HTML_ESCAPES[char] as string);\n}\n\n/** Escape a value for a CSS `url(...)` literal; this stops the break-out, not the scheme, so pair it with `isSafeUrl()`. */\nexport function escapeCssUrl(value: string): string {\n return value.replace(/['\"()\\\\]/g, '\\\\$&');\n}\n\nconst ATTR_ESCAPES: Readonly<Record<string, string>> = Object.freeze({\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n});\n\nconst ATTR_ESCAPE_PATTERN = /[&<>\"']/g;\n\n/** Escape a value for a *quoted* HTML attribute, leaving URL characters intact; it validates no scheme, so run `isSafeUrl()` on `href`/`src` first. @internal */\nexport function escapeHtmlAttribute(value: string): string {\n // The character class and the map are coupled: every match is a key.\n // eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style\n return value.replace(ATTR_ESCAPE_PATTERN, (char) => ATTR_ESCAPES[char] as string);\n}\n\n/** `escapeHtml()` then newlines to `<br>`; the order is what makes it safe. */\nexport function escapeAndLinebreak(text: string): string {\n return escapeHtml(text).replace(/\\r\\n|\\r|\\n/g, '<br>');\n}\n","/**\n * Policed writes for `data-payload-attribute` bindings. The value is\n * remote-controlled, so event handlers, `style`, `srcdoc`, `formaction`/`form`,\n * `id`/`name` (DOM clobbering) and `srcset` (multi-URL syntax) are refused,\n * URL-bearing attributes must pass `isSafeUrl`, and non-scalars never write.\n */\n\nimport { isSafeUrl } from '@security/url-validator';\n\nconst BLOCKED_ATTRIBUTES: ReadonlySet<string> = new Set([\n 'style',\n 'srcdoc',\n 'formaction',\n 'form',\n 'id',\n 'name',\n 'is',\n 'srcset',\n 'imagesrcset',\n]);\n\nconst URL_ATTRIBUTES: ReadonlySet<string> = new Set([\n 'href',\n 'src',\n 'poster',\n 'cite',\n 'action',\n 'xlink:href',\n 'data',\n]);\n\nexport type AttributeApplyResult = 'applied' | 'blocked';\n\n/**\n * Whether this package may write an attribute of this name at all — event\n * handlers, `style`, the two DOM-clobbering names and the multi-URL ones\n * excluded. One rule, two callers: the binding below writes a remote value into\n * it, and the array renderers copy one from the page's own markup onto an item\n * they rebuilt (`src/core/array-template.ts`).\n */\nexport function isWritableAttribute(name: string): boolean {\n return !name.startsWith('on') && !BLOCKED_ATTRIBUTES.has(name);\n}\n\n/** Returns `'blocked'` without touching the DOM when the write is refused. */\nexport function applyAttributeBinding(\n element: Element,\n attribute: string,\n value: unknown,\n): AttributeApplyResult {\n const name = attribute.toLowerCase().trim();\n if (name.length === 0) return 'blocked';\n if (!isWritableAttribute(name)) return 'blocked';\n\n if (value === null || value === undefined) {\n element.removeAttribute(name);\n return 'applied';\n }\n if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') {\n return 'blocked';\n }\n const stringValue = String(value);\n\n if (URL_ATTRIBUTES.has(name) && !isSafeUrl(stringValue)) return 'blocked';\n\n element.setAttribute(name, stringValue);\n return 'applied';\n}\n","/**\n * One array item out of one template: the interpolation, and the attributes the\n * template cannot know about.\n *\n * A replacement value is data, not template syntax: one `replace()` per key\n * would re-interpret `{{index}}` text that an earlier value introduced, and\n * would apply `String.replace`'s `$` semantics to CMS content.\n */\n\nimport { escapeHtml } from '@security/escape';\nimport { isWritableAttribute } from './attribute-binding';\n\nconst PLACEHOLDER_PATTERN = /\\{\\{([\\s\\S]*?)\\}\\}/g;\n\n/** The runtime's own annotations; a rebuilt item gets its own, never a copy. */\nconst BINDING_ATTRIBUTE_PREFIX = 'data-payload-';\n\n/** An attribute name and its value, as they travel from an old item to a new one. */\nexport type InheritedAttribute = readonly [name: string, value: string];\n\nfunction hasOwn(record: object, key: PropertyKey): boolean {\n return Object.prototype.hasOwnProperty.call(record, key);\n}\n\n/**\n * Every key any item carries. A placeholder naming one of these is a field of\n * this array, so a row that happens to lack it renders as nothing; a\n * placeholder no row can fill is a typo in the template and stays visible.\n */\nexport function collectTemplateKeys(items: readonly unknown[]): ReadonlySet<string> {\n const keys = new Set<string>();\n for (const item of items) {\n if (typeof item !== 'object' || item === null) continue;\n for (const key of Object.keys(item)) keys.add(key);\n }\n return keys;\n}\n\nexport function interpolateArrayTemplate(\n template: string,\n item: unknown,\n index: number,\n stringify: (value: unknown) => string,\n knownKeys?: ReadonlySet<string>,\n): string {\n const record = typeof item === 'object' && item !== null ? item : null;\n\n return template.replace(PLACEHOLDER_PATTERN, (placeholder, key: string) => {\n // Preserve the established object-field precedence when an object itself\n // has an `index` or `value` property. Otherwise these are reserved tokens.\n if (record !== null && hasOwn(record, key)) {\n return escapeHtml(stringify(Reflect.get(record, key)));\n }\n if (record === null && key === 'value') return escapeHtml(stringify(item));\n if (key === 'index') return String(index);\n // A field other rows carry: this one simply has none. Writing the\n // placeholder would print template syntax on the page, which is what an\n // editor sees the moment a row is added and not every field is filled in.\n if (record !== null && knownKeys?.has(key) === true) return '';\n return placeholder;\n });\n}\n\n/**\n * What the items already in `container` all carry, and an item rebuilt from the\n * template therefore has to carry too.\n *\n * A framework's scoped styles hang on a marker its compiler writes into the\n * markup — Astro's `data-astro-cid-…`, Vue's `data-v-…`, Svelte's class — and\n * the author's `data-payload-array-template` has none of it. The rebuilt list\n * then renders unstyled next to the one the server sent, while the write itself\n * succeeds, so nothing in the runtime can notice. Naming no framework is the\n * point: what identifies these attributes is that every item of the list has\n * them with the same value, which per-row state by definition does not.\n */\nexport function sharedItemAttributes(container: Element): readonly InheritedAttribute[] {\n const items = container.children;\n const first = items[0];\n if (first === undefined) return [];\n const shared: InheritedAttribute[] = [];\n for (const { name, value } of Array.from(first.attributes)) {\n if (!isWritableAttribute(name) || name.startsWith(BINDING_ATTRIBUTE_PREFIX)) continue;\n let everyItem = true;\n for (let index = 1; index < items.length && everyItem; index += 1) {\n everyItem = items[index]?.getAttribute(name) === value;\n }\n if (everyItem) shared.push([name, value]);\n }\n return shared;\n}\n\n/** Give a rebuilt item what its predecessors shared; the template's own value wins. */\nexport function inheritItemAttributes(item: Element, shared: readonly InheritedAttribute[]): void {\n for (const [name, value] of shared) {\n if (!item.hasAttribute(name)) item.setAttribute(name, value);\n }\n}\n","/** Value coercion shared by the field renderers. */\n\n/** Display string for any value: primitives as-is, objects as JSON, `null`/`undefined` as `''`. */\nexport function safeStringify(value: unknown): string {\n if (value === null || value === undefined) return '';\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value);\n }\n try {\n return JSON.stringify(value);\n } catch {\n return '';\n }\n}\n\n/** The three values every renderer treats as \"clear\" (docs/renderers.md, value semantics). */\nexport function isEmptyValue(value: unknown): value is null | undefined | '' {\n return value === null || value === undefined || value === '';\n}\n","/**\n * Structural diff for array and blocks fields. Items with an `id` are keyed\n * (moves are detected); without ids the diff is positional.\n * @internal\n */\n\nexport type ArrayPatch =\n | { readonly kind: 'insert'; readonly index: number; readonly value: unknown }\n | { readonly kind: 'remove'; readonly index: number; readonly value: unknown }\n | {\n readonly kind: 'move';\n readonly from: number;\n readonly to: number;\n readonly value: unknown;\n }\n | { readonly kind: 'update'; readonly index: number; readonly value: unknown }\n | {\n readonly kind: 'replace';\n readonly index: number;\n readonly oldValue: unknown;\n readonly value: unknown;\n };\n\nfunction getId(item: unknown): string | number | undefined {\n if (typeof item !== 'object' || item === null) return undefined;\n const id = (item as Record<string, unknown>)['id'];\n if (typeof id === 'string' || typeof id === 'number') return id;\n return undefined;\n}\n\nfunction getBlockType(item: unknown): string | undefined {\n if (typeof item !== 'object' || item === null) return undefined;\n const blockType = (item as Record<string, unknown>)['blockType'];\n return typeof blockType === 'string' ? blockType : undefined;\n}\n\n/** Patches that turn `prev` into `next`; a changed `blockType` is a `replace`, never an `update`. */\nexport function diffArray(\n prev: readonly unknown[],\n next: readonly unknown[],\n): readonly ArrayPatch[] {\n const haveIds = next.every((item) => getId(item) !== undefined);\n if (haveIds) return diffById(prev, next);\n return diffByPosition(prev, next);\n}\n\nfunction diffById(prev: readonly unknown[], next: readonly unknown[]): readonly ArrayPatch[] {\n const patches: ArrayPatch[] = [];\n const prevIndexById = new Map<string | number, number>();\n for (let i = 0; i < prev.length; i += 1) {\n const id = getId(prev[i]);\n if (id !== undefined) prevIndexById.set(id, i);\n }\n\n const consumedFromPrev = new Set<number>();\n for (let to = 0; to < next.length; to += 1) {\n const item = next[to];\n const id = getId(item);\n if (id === undefined) {\n patches.push({ kind: 'insert', index: to, value: item });\n continue;\n }\n const from = prevIndexById.get(id);\n if (from === undefined) {\n patches.push({ kind: 'insert', index: to, value: item });\n continue;\n }\n consumedFromPrev.add(from);\n const prevItem = prev[from];\n if (getBlockType(prevItem) !== getBlockType(item)) {\n patches.push({ kind: 'replace', index: to, oldValue: prevItem, value: item });\n continue;\n }\n if (from !== to) {\n patches.push({ kind: 'move', from, to, value: item });\n }\n if (!shallowEqual(prevItem, item)) {\n patches.push({ kind: 'update', index: to, value: item });\n }\n }\n\n for (let i = 0; i < prev.length; i += 1) {\n if (consumedFromPrev.has(i)) continue;\n patches.push({ kind: 'remove', index: i, value: prev[i] });\n }\n return patches;\n}\n\nfunction diffByPosition(prev: readonly unknown[], next: readonly unknown[]): readonly ArrayPatch[] {\n const patches: ArrayPatch[] = [];\n const min = Math.min(prev.length, next.length);\n for (let i = 0; i < min; i += 1) {\n if (!shallowEqual(prev[i], next[i])) {\n patches.push({ kind: 'update', index: i, value: next[i] });\n }\n }\n if (next.length > prev.length) {\n for (let i = prev.length; i < next.length; i += 1) {\n patches.push({ kind: 'insert', index: i, value: next[i] });\n }\n }\n if (prev.length > next.length) {\n for (let i = next.length; i < prev.length; i += 1) {\n patches.push({ kind: 'remove', index: i, value: prev[i] });\n }\n }\n return patches;\n}\n\n// Shallow on purpose: any nested reference change is an update, and the\n// renderer reconciles deeper differences itself.\nfunction shallowEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;\n const aRec = a as Record<string, unknown>;\n const bRec = b as Record<string, unknown>;\n const aKeys = Object.keys(aRec);\n const bKeys = Object.keys(bRec);\n if (aKeys.length !== bKeys.length) return false;\n for (const key of aKeys) {\n if (!Object.is(aRec[key], bRec[key])) return false;\n }\n return true;\n}\n","/**\n * Hydrated islands own their subtree, so the runtime never patches inside one.\n * Instead every flush that carried a change dispatches `payload-live-preview:update`\n * on each island root with the update in `detail`, whether or not a binding\n * outside the islands was written. `data-payload-island=\"patch\"` opts back in.\n * Server HTML reaches an Astro island only through Astro's own props handoff\n * (ADR 0021). @internal\n */\n\nexport const ISLAND_EVENT = 'payload-live-preview:update';\n/** @internal */\nexport const ISLAND_ATTRIBUTE = 'data-payload-island';\nexport const ISLAND_SELECTOR = `astro-island, [${ISLAND_ATTRIBUTE}]`;\n/** The opt-out for a subtree the site scripts itself: the morph never enters it. @internal */\nexport const OWNED_ATTRIBUTE = 'data-payload-owned';\n/**\n * The opt-in for a custom element: present on the live and on the rendered host\n * (its value is ignored), the morph edits the host's attributes and light DOM\n * instead of retaining it whole (ADR 0008 §10). @internal\n */\nexport const MORPH_ATTRIBUTE = 'data-payload-morph';\n\n/**\n * The ownership rule (ADR 0008 §4): a subtree the morph never enters. Custom\n * elements own their subtree unless the template opts in with\n * `data-payload-morph` (§10), islands are a framework's, `contenteditable`\n * is the visitor's, `data-payload-owned` is the site's. The coordinators hand\n * this rule to the engine as `MorphOptions.boundary`; the engine itself only\n * pairs, edits and keeps focus (§9). @beta\n */\nexport function isMorphBoundary(element: Element): boolean {\n const customElement = element.tagName.toLowerCase().includes('-');\n if (customElement && !element.hasAttribute(MORPH_ATTRIBUTE)) return true;\n if (isIslandBoundary(element) || element.hasAttribute(OWNED_ATTRIBUTE)) return true;\n const editable = element.getAttribute('contenteditable');\n return editable !== null && editable !== 'false';\n}\n\nexport interface IslandUpdateDetail {\n readonly fields: Readonly<Record<string, unknown>>;\n readonly revision: number;\n readonly receivedAt: number;\n readonly locale: string | undefined;\n /**\n * Call once the island has rendered this revision, for instance from an\n * effect after its framework committed. Until every island handed the\n * revision has called it, `inspect().revisions.display` says `unconfirmed`\n * (ADR 0023). Repeated, late or superseded calls do nothing.\n */\n readonly displayed: () => void;\n}\n\nfunction isIslandRoot(element: Element): boolean {\n return element.tagName.toLowerCase() === 'astro-island' || element.hasAttribute(ISLAND_ATTRIBUTE);\n}\n\nfunction islandAllowsPatching(island: Element): boolean {\n return island.getAttribute(ISLAND_ATTRIBUTE) === 'patch';\n}\n\n/** Whether `element` itself is an island root that did not opt into patching. */\nexport function isIslandBoundary(element: Element): boolean {\n return isIslandRoot(element) && !islandAllowsPatching(element);\n}\n\n/** Whether `element` (or an ancestor) is an island that did not opt into patching. @internal */\nexport function isInsideIsland(element: Element): boolean {\n let current: Element | null = element;\n while (current !== null) {\n if (isIslandBoundary(current)) return true;\n current = current.parentElement;\n }\n return false;\n}\n\n/** Island roots under `root` that receive update events. */\nexport function collectIslands(root: ParentNode): Element[] {\n const islands: Element[] = [];\n for (const island of root.querySelectorAll(ISLAND_SELECTOR)) {\n if (!islandAllowsPatching(island)) islands.push(island);\n }\n return islands;\n}\n\n/** Astro keeps `ssr` until the framework listener has finished hydrating. @internal */\nexport function isAwaitingIslandHydration(island: Element): boolean {\n return island.tagName.toLowerCase() === 'astro-island' && island.hasAttribute('ssr');\n}\n\nexport function dispatchIslandUpdate(\n islands: readonly Element[],\n detail: IslandUpdateDetail,\n isCurrent: () => boolean,\n): number {\n let dispatched = 0;\n for (const island of islands) {\n if (!isCurrent()) break;\n island.dispatchEvent(\n new CustomEvent<IslandUpdateDetail>(ISLAND_EVENT, { detail, bubbles: false }),\n );\n dispatched += 1;\n if (!isCurrent()) break;\n }\n return dispatched;\n}\n\n/** What decides whether a live `astro-island` still renders the rendered one's component (ADR 0021). */\nconst ISLAND_IDENTITY = [\n 'component-url',\n 'component-export',\n 'renderer-url',\n 'client',\n 'opts',\n 'before-hydration-url',\n] as const;\n/** Slot markup each island was last rendered with; Astro reads slots once, at hydration. */\nconst renderedSlots = new WeakMap<Element, string>();\nconst unmountedIslands = new WeakSet<Element>();\n\nfunction isAstroIsland(element: Element): boolean {\n return element.tagName.toLowerCase() === 'astro-island';\n}\n\nfunction slotMarkup(island: Element): string {\n let markup = '';\n for (const slot of island.querySelectorAll('astro-slot, template[data-astro-template]')) {\n if (slot.parentElement?.closest('astro-island') !== island) continue;\n const name = slot.getAttribute('name') ?? slot.getAttribute('data-astro-template') ?? '';\n markup += `${name}\\u0000${slot.innerHTML}\\u0000`;\n }\n return markup;\n}\n\n/** Only an upgraded island whose class observes `props` re-hydrates in place. */\nfunction observesProps(island: Element): boolean {\n const constructor = island.ownerDocument.defaultView?.customElements.get('astro-island') as\n (CustomElementConstructor & { readonly observedAttributes?: unknown }) | undefined;\n return (\n constructor !== undefined &&\n island instanceof constructor &&\n Array.isArray(constructor.observedAttributes) &&\n constructor.observedAttributes.includes('props')\n );\n}\n\n/**\n * The package's decision for a boundary pair (ADR 0021). A boundary other than\n * an Astro island stays whole (ADR 0008 §4). An island that still renders the\n * same component, with the same slots, keeps its framework state: it takes the\n * rendered props the way Astro's router hands them to a persisted island,\n * `ssr` first, then `props`. Anything else is replaced, and hydrates fresh.\n * @beta\n */\nexport function retainIslandBoundary(live: Element, rendered: Element): boolean {\n if (!isAstroIsland(live) || !isAstroIsland(rendered)) return true;\n const slots = slotMarkup(rendered);\n const retained =\n ISLAND_IDENTITY.every((name) => live.getAttribute(name) === rendered.getAttribute(name)) &&\n (renderedSlots.get(live) ?? slotMarkup(live)) === slots &&\n (live.getAttribute('props') === rendered.getAttribute('props') || observesProps(live));\n if (!retained) {\n renderedSlots.set(rendered, slots);\n return false;\n }\n renderedSlots.set(live, slots);\n const props = rendered.getAttribute('props');\n if (props !== live.getAttribute('props')) {\n live.setAttribute('ssr', '');\n if (props === null) live.removeAttribute('props');\n else live.setAttribute('props', props);\n }\n return true;\n}\n\n/** The Astro islands under `root`, to compare before and after a morph. @internal */\nexport function astroIslandsIn(root: ParentNode): Element[] {\n return Array.from(root.querySelectorAll('astro-island'));\n}\n\n/**\n * Astro's framework clients release their roots on `astro:unmount`, which its\n * router sends after a swap. A morph that disconnects an island sends it once\n * itself, so a removed React root does not outlive its markup. @internal\n */\nexport function releaseDisconnectedIslands(islands: readonly Element[]): void {\n for (const island of islands) {\n if (island.isConnected || unmountedIslands.has(island)) continue;\n unmountedIslands.add(island);\n island.dispatchEvent(new CustomEvent('astro:unmount'));\n }\n}\n\n/**\n * Why an inserted island cannot start: fragment scripts never run, so the page\n * itself must have loaded Astro's island element and this client directive.\n * @internal\n */\nexport function islandStartBlocker(island: Element): 'element' | 'directive' | undefined {\n const view = island.ownerDocument.defaultView;\n if (view?.customElements.get('astro-island') === undefined) return 'element';\n const directive = island.getAttribute('client');\n const astro: unknown = Reflect.get(view, 'Astro');\n if (directive === null || typeof astro !== 'object' || astro === null) return 'directive';\n return Reflect.get(astro, directive) === undefined ? 'directive' : undefined;\n}\n","/**\n * Keyed DOM morph, the engine: edits a live element toward a freshly rendered\n * one while retaining compatible, paired live nodes. Those nodes keep their\n * live properties; an incompatible or unpaired node is replaced and loses\n * them. It pairs children by key or position, synchronises attributes with the\n * state exception and keeps focus across a retained move; which subtrees it\n * never enters is the coordinator's rule, handed in as\n * `MorphOptions.boundary` and defaulting to the package's (`isMorphBoundary`\n * in islands.ts). See ADR 0008, §9 for the line between the two. A custom\n * element is such a boundary unless both of its sides carry\n * `data-payload-morph` (§10).\n */\n\nimport { isMorphBoundary } from './islands';\n\n/** Attributes the CMS controls only when the template names them (ADR 0008 §3). */\nconst STATE_ATTRIBUTES: ReadonlySet<string> = new Set(['open', 'value', 'checked', 'selected']);\n\n/** @beta */\nexport interface MorphOptions {\n /** Attributes that key child elements for pairing, tried in order; unkeyed children pair by position. */\n readonly keyAttributes: readonly string[];\n /** Called once per parent when two children share a key; the later ones pair by position. */\n readonly onDuplicateKey?: (parent: Element, key: string) => void;\n /** Elements whose attributes are synchronised but whose children are left alone (nested structural slots). */\n readonly retainChildrenOf?: (live: Element, rendered: Element) => boolean;\n /**\n * The ownership rule: a structurally compatible live/rendered pair for\n * which this returns `true` on both sides is retained whole and never\n * entered (ADR 0008 §4). The package's rule — custom elements, islands,\n * `contenteditable`, `data-payload-owned` — is the default. A supplied rule\n * replaces that default; compose it with the exported `isMorphBoundary`\n * predicate to add package-compatible boundaries.\n */\n readonly boundary?: (element: Element) => boolean;\n /**\n * Decides a live/rendered pair that are both boundaries: `true` keeps the\n * live one, after any handoff to its owner; `false` replaces it with the\n * rendered one. Without it every such pair is kept whole (ADR 0008 §4). The\n * package's coordinators hand in `retainIslandBoundary`, under which an\n * Astro island that still renders the same component takes the rendered\n * props through Astro's own handoff (ADR 0021).\n */\n readonly retainBoundary?: (live: Element, rendered: Element) => boolean;\n}\n\n/** Empty attribute values (boolean markers such as `data-payload-island`) are not keys. */\nfunction keyOf(element: Element, options: MorphOptions): string | undefined {\n for (const attribute of options.keyAttributes) {\n const value = element.getAttribute(attribute);\n if (value !== null && value.length > 0) return `${attribute}=${value}`;\n }\n return undefined;\n}\n\n/** Whether `live` can be edited toward `rendered` instead of being replaced by it, under `boundary` (the package's rule by default). @internal */\nexport function isMorphCompatible(\n live: Element,\n rendered: Element,\n boundary: (element: Element) => boolean = isMorphBoundary,\n): boolean {\n return (\n live.tagName === rendered.tagName &&\n live.namespaceURI === rendered.namespaceURI &&\n !boundary(live) &&\n !boundary(rendered)\n );\n}\n\n/**\n * Morph `live` toward `rendered`. Returns `live` when it was retained, or\n * `rendered` when the two are incompatible and the caller must replace.\n * `rendered` is consumed: its children may move into `live`.\n *\n * @beta\n */\nexport function morphElement(live: Element, rendered: Element, options: MorphOptions): Element {\n if (!sameKind(live, rendered)) return rendered;\n const boundary = options.boundary ?? isMorphBoundary;\n const liveBoundary = boundary(live);\n const renderedBoundary = boundary(rendered);\n if (liveBoundary || renderedBoundary) {\n if (!liveBoundary || !renderedBoundary) return rendered;\n return (options.retainBoundary?.(live, rendered) ?? true) ? live : rendered;\n }\n const focus = captureFocus(live);\n syncAttributes(live, rendered);\n if (options.retainChildrenOf?.(live, rendered) !== true) morphChildren(live, rendered, options);\n restoreFocus(focus);\n return live;\n}\n\n/** Match attributes, except that state-bearing ones change only when `rendered` carries them. */\nexport function syncAttributes(live: Element, rendered: Element): void {\n for (const attribute of Array.from(live.attributes)) {\n if (rendered.hasAttribute(attribute.name)) continue;\n if (STATE_ATTRIBUTES.has(attribute.name.toLowerCase())) continue;\n live.removeAttribute(attribute.name);\n }\n for (const attribute of Array.from(rendered.attributes)) {\n if (live.getAttribute(attribute.name) !== attribute.value) {\n live.setAttribute(attribute.name, attribute.value);\n }\n }\n}\n\nfunction morphChildren(live: Element, rendered: Element, options: MorphOptions): void {\n const liveKeyed = indexKeyed(live, options);\n const keyedElements = new Set<Node>(liveKeyed.values());\n const liveUnkeyed = Array.from(live.childNodes).filter((node) => !keyedElements.has(node));\n const claimed = new Set<Node>();\n const usedKeys = new Set<string>();\n const renderedNodes = Array.from(rendered.childNodes);\n let cursor: Node | null = live.firstChild;\n let unkeyedIndex = 0;\n\n for (let i = 0; i < renderedNodes.length; i += 1) {\n const next = renderedNodes[i] as Node;\n let kept: Node | null = null;\n let key = next instanceof Element ? keyOf(next, options) : undefined;\n // A repeated key on the rendered side pairs positionally like a live duplicate.\n if (key !== undefined && usedKeys.has(key)) key = undefined;\n if (key !== undefined) {\n usedKeys.add(key);\n const candidate = liveKeyed.get(key);\n if (candidate !== undefined && !claimed.has(candidate)) {\n kept = reconcile(candidate, next, options) === candidate ? candidate : null;\n if (kept === null) {\n candidate.replaceWith(next);\n claimed.add(next);\n cursor = next.nextSibling;\n continue;\n }\n }\n } else {\n while (unkeyedIndex < liveUnkeyed.length) {\n const candidate = liveUnkeyed[unkeyedIndex] as Node;\n unkeyedIndex += 1;\n if (claimed.has(candidate)) continue;\n if (sameKind(candidate, next)) {\n kept = reconcile(candidate, next, options) === candidate ? candidate : null;\n if (kept === null) {\n live.replaceChild(next, candidate);\n claimed.add(next);\n cursor = next.nextSibling;\n }\n break;\n }\n if (candidate instanceof Element) {\n // `next` is a text/comment, or an element whose kind the following\n // rendered node matches: an insertion, so the live element stays for it.\n const following = renderedNodes[i + 1];\n if (\n !(next instanceof Element) ||\n (following !== undefined && sameKind(candidate, following))\n ) {\n unkeyedIndex -= 1;\n break;\n }\n live.replaceChild(next, candidate);\n claimed.add(next);\n cursor = next.nextSibling;\n break;\n }\n if (!(next instanceof Element)) {\n // Text vs comment: replace rather than rewrite one node type's value into the other.\n live.replaceChild(next, candidate);\n claimed.add(next);\n cursor = next.nextSibling;\n break;\n }\n // A surplus live text/comment before the element `next` needs; removed below.\n }\n if (kept === null && claimed.has(next)) continue;\n }\n\n const node: Node = kept ?? next;\n claimed.add(node);\n // Whitespace-only text the template did not render is surplus. Stepping the\n // cursor over it keeps a retained element where it is: `insertBefore` on a\n // node already in the tree is a remove-and-insert, which blurs a focused input.\n while (cursor !== null && cursor !== node && !claimed.has(cursor) && isBlankText(cursor)) {\n cursor = cursor.nextSibling;\n }\n if (node !== cursor) live.insertBefore(node, cursor);\n cursor = node.nextSibling;\n }\n\n for (const child of Array.from(live.childNodes)) {\n if (!claimed.has(child)) child.remove();\n }\n}\n\n/** Retain `candidate` when it can be edited toward `next`; otherwise report `next`. */\nfunction reconcile(candidate: Node, next: Node, options: MorphOptions): Node {\n if (candidate instanceof Element && next instanceof Element) {\n return morphElement(candidate, next, options);\n }\n if (candidate.nodeValue !== next.nodeValue) candidate.nodeValue = next.nodeValue;\n return candidate;\n}\n\nfunction isBlankText(node: Node): boolean {\n return node.nodeType === Node.TEXT_NODE && (node.nodeValue ?? '').trim().length === 0;\n}\n\nfunction sameKind(a: Node, b: Node): boolean {\n if (a.nodeType !== b.nodeType) return false;\n if (a instanceof Element && b instanceof Element) {\n return a.tagName === b.tagName && a.namespaceURI === b.namespaceURI;\n }\n return true;\n}\n\n/** First element per key; later duplicates are left as they are and pair positionally. */\nfunction indexKeyed(parent: Element, options: MorphOptions): Map<string, Element> {\n const keyed = new Map<string, Element>();\n let duplicate: string | undefined;\n for (const child of Array.from(parent.children)) {\n const key = keyOf(child, options);\n if (key === undefined) continue;\n if (keyed.has(key)) {\n duplicate ??= key.slice(key.indexOf('=') + 1);\n continue;\n }\n keyed.set(key, child);\n }\n if (duplicate !== undefined) options.onDuplicateKey?.(parent, duplicate);\n return keyed;\n}\n\n/** @internal */\nexport interface FocusSnapshot {\n readonly element: HTMLElement;\n readonly selection: readonly [number | null, number | null] | null;\n}\n\n/**\n * A keyed move is a remove-and-insert, which blurs; remember what had focus.\n * The structural applier moves items itself and uses the same pair around\n * its commit, so a move it makes keeps focus the way a move the morph makes\n * does (ADR 0008 §1). @internal\n */\nexport function captureFocus(live: Element): FocusSnapshot | null {\n const active = live.ownerDocument.activeElement;\n if (!(active instanceof HTMLElement) || !live.contains(active)) return null;\n const selection =\n active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement\n ? ([active.selectionStart, active.selectionEnd] as const)\n : null;\n return { element: active, selection };\n}\n\n/** @internal */\nexport function restoreFocus(snapshot: FocusSnapshot | null): void {\n if (snapshot === null) return;\n const { element, selection } = snapshot;\n if (element.ownerDocument.activeElement === element || !element.isConnected) return;\n try {\n element.focus({ preventScroll: true });\n const [start, end] = selection ?? [null, null];\n if (start !== null && end !== null) {\n (element as HTMLInputElement).setSelectionRange(start, end);\n }\n } catch {\n // Not focusable any more; nothing to restore.\n }\n}\n","/**\n * Sanitizer options for item templates (`data-payload-array-template`,\n * nested templates). A template is the page author's markup and every\n * interpolated value is escaped to text before the sanitizer runs, so it may\n * carry form controls and custom elements the rich-text policy refuses.\n * Event handlers, `style` and unsafe URLs are still stripped.\n *\n * Some entries below are already admitted by the sanitizer through another\n * route — `video` and `audio` by the tag allow-list, `select` by\n * `allowFormControls`, `name` by `templateMode`, and `controls`, `muted`,\n * `loop`, `poster` and `src` by the per-tag attribute map. They are kept\n * because this list states what a template may contain, and an allow-list\n * that depends on a second list staying unchanged is the more fragile of the\n * two arrangements. Mutating those entries changes nothing observable, so\n * they survive mutation testing by construction; the behaviour they describe\n * is pinned in `tests/unit/core/template-sanitize.test.ts` instead.\n */\n\nimport type { SanitizeOptions } from '@security/sanitizer';\nimport { lruGet, lruSet } from './lru';\n\nconst TEMPLATE_EXTRA_TAGS: readonly string[] = [\n 'input',\n 'textarea',\n 'select',\n 'option',\n 'button',\n 'label',\n 'details',\n 'summary',\n 'dialog',\n 'video',\n 'audio',\n 'progress',\n 'meter',\n];\n\nconst TEMPLATE_EXTRA_ATTRIBUTES: readonly string[] = [\n 'type',\n 'name',\n 'placeholder',\n 'open',\n 'disabled',\n 'readonly',\n 'required',\n 'checked',\n 'selected',\n 'value',\n 'min',\n 'max',\n 'step',\n 'rows',\n 'cols',\n 'for',\n 'controls',\n 'muted',\n 'loop',\n 'poster',\n 'src',\n];\n\nconst CUSTOM_TAG_PATTERN = /<([a-z][a-z0-9]*(?:-[a-z0-9]+)+)\\b/gi;\n\n// 64 like the intl cache: more than a page declares, and a nested template is\n// interpolated with its parent's, so an outer value or `{{index}}` that lands\n// in it mints a key per item and per keystroke (ADR 0003 §3).\nexport const TEMPLATE_CACHE_LIMIT = 64;\nconst optionsByTemplate = new Map<string, SanitizeOptions>();\n\n/** Options for one template; memoised per template string. */\nexport function templateSanitizeOptions(template: string): SanitizeOptions {\n const cached = lruGet(optionsByTemplate, template);\n if (cached !== undefined) return cached;\n const tags = new Set(TEMPLATE_EXTRA_TAGS);\n for (const match of template.matchAll(CUSTOM_TAG_PATTERN)) {\n const tag = match[1]?.toLowerCase();\n if (tag !== undefined) tags.add(tag);\n }\n const attributes: Record<string, readonly string[]> = {};\n for (const tag of tags) attributes[tag] = TEMPLATE_EXTRA_ATTRIBUTES;\n const options: SanitizeOptions = {\n additionalAllowedTags: [...tags],\n additionalAllowedAttributes: attributes,\n allowFormControls: true,\n templateMode: true,\n };\n return lruSet(optionsByTemplate, template, options, TEMPLATE_CACHE_LIMIT);\n}\n","/**\n * Applies an `ArrayPatch[]` diff to a container as keyed DOM operations\n * instead of rebuilding the list, so consumer state on surviving children is\n * kept. Items are paired by `data-payload-key` (the item `id`); items without\n * one pair by position. Nested arrays reconcile recursively through\n * `data-payload-nested-key` slots. See ADR 0008.\n */\n\nimport { sanitizeHtmlWithPolicy, type SanitizerPolicyMode } from '@security/sanitizer';\nimport { trustedHtml } from '@security/trusted-types';\nimport {\n collectTemplateKeys,\n inheritItemAttributes,\n interpolateArrayTemplate,\n sharedItemAttributes,\n type InheritedAttribute,\n} from './array-template';\nimport { safeStringify } from '@field-types/utils';\nimport { diffArray, type ArrayPatch } from '@schema/diff';\nimport { isMorphBoundary, retainIslandBoundary } from './islands';\nimport { captureFocus, restoreFocus, morphElement } from './morph';\nimport { templateSanitizeOptions } from './template-sanitize';\n\n/** @internal */\nexport const KEY_ATTRIBUTE = 'data-payload-key';\nconst NESTED_KEY_ATTRIBUTE = 'data-payload-nested-key';\nconst NESTED_TEMPLATE_ATTRIBUTE = 'data-payload-nested-template';\n\n/** Last value seen per item key, per container; owned by the caller so instances never share it. @internal */\nexport type StructuralStore = WeakMap<Element, Map<string, unknown>>;\n\n/** @internal */\nexport function createStructuralStore(): StructuralStore {\n return new WeakMap<Element, Map<string, unknown>>();\n}\n\n/** @internal */\nexport interface StructuralApplyOptions {\n readonly template: string;\n readonly container: Element;\n readonly patches: readonly ArrayPatch[];\n /** The full new array; inserts and replaces render by index from it. */\n readonly nextItems: readonly unknown[];\n /** Pass the same store across renders so nested arrays diff incrementally. */\n readonly store: StructuralStore;\n /** Re-render existing items even when unchanged. */\n readonly forceRender?: boolean;\n /** The instance's policy for item markup; absent, the process default applies. */\n readonly sanitizerPolicy?: SanitizerPolicyMode | undefined;\n /** Edit a changed item in place instead of replacing it. Default `true`. */\n readonly morph?: boolean;\n /** Reported once per container when two items share a key (`LP0405`). */\n readonly onDuplicateKey?: (container: Element, key: string) => void;\n}\n\n/**\n * Returns `true` for a DOM mutation, `false` for an already-current tree, and\n * `null` when an item's markup has no sanitized element root — preflighted\n * before any mutation, so the previous DOM and memory stay intact.\n * @internal\n */\nexport function applyStructuralPatches(options: StructuralApplyOptions): boolean | null {\n const plan = prepareStructuralPlan(options.container, options.patches, options);\n if (plan === null) return null;\n return commitStructuralPlan(plan, options.store, {\n morph: options.morph ?? true,\n onDuplicateKey: options.onDuplicateKey,\n });\n}\n\n// Plan records are tuples: many are created per recursive render, and named\n// keys would repeat in the inline bundle.\ntype StructuralPlan = readonly [\n container: Element,\n entries: readonly ReconciliationEntry[],\n nextItems: readonly unknown[],\n];\ntype ReconciliationEntry = readonly [\n index: number,\n live: Element | null,\n rendered: Element | undefined,\n nestedSlots: readonly NestedSlotPlan[],\n /** A `replace` patch: a different item, never morphed toward. */\n replace: boolean,\n];\ntype NestedSlotPlan = readonly [\n live: Element | null,\n rendered: Element,\n children: StructuralPlan | undefined,\n];\n\n// One per container, not per item, so — unlike the tuples above — named keys\n// cost the inline bundle nothing per render.\ntype RenderEnv = Pick<\n StructuralApplyOptions,\n 'template' | 'nextItems' | 'forceRender' | 'store' | 'sanitizerPolicy'\n>;\n\ninterface CommitOptions {\n readonly morph: boolean;\n readonly onDuplicateKey: ((container: Element, key: string) => void) | undefined;\n}\n\n/** A rendered slot that still carries a nested template keeps its children for its own plan. */\nfunction isManagedNestedSlot(_live: Element, rendered: Element): boolean {\n return (\n rendered.hasAttribute(NESTED_KEY_ATTRIBUTE) && rendered.hasAttribute(NESTED_TEMPLATE_ATTRIBUTE)\n );\n}\n\n/** Materialise the whole recursive replacement tree before touching live DOM. */\nfunction prepareStructuralPlan(\n container: Element,\n patches: readonly ArrayPatch[],\n env: RenderEnv,\n): StructuralPlan | null {\n const entries = prepareReconciliation(container, createPatchPlan(patches), env);\n return entries === null ? null : [container, entries, env.nextItems];\n}\n\nfunction commitStructuralPlan(\n plan: StructuralPlan,\n store: StructuralStore,\n options: CommitOptions,\n): boolean {\n const [container, entries, nextItems] = plan;\n const claimed = new Set<Element>();\n let mutated = false;\n // Placing a retained item is a remove-and-insert, which blurs whatever the\n // visitor had focused inside it; the morph does the same for a move it\n // makes and restores afterwards, so the commit does too.\n const focus = captureFocus(container);\n for (const [index, live, rendered, nestedSlots, replace] of entries) {\n let node = live;\n if (rendered !== undefined) {\n // Morph a compatible live item in place; otherwise transplant its live\n // nested slots into the rendered item and swap.\n const retained =\n options.morph && !replace && live !== null\n ? morphElement(live, rendered, {\n keyAttributes: [KEY_ATTRIBUTE, NESTED_KEY_ATTRIBUTE],\n boundary: isMorphBoundary,\n retainBoundary: retainIslandBoundary,\n retainChildrenOf: isManagedNestedSlot,\n ...(options.onDuplicateKey !== undefined\n ? { onDuplicateKey: options.onDuplicateKey }\n : {}),\n }) === live\n : false;\n if (!retained) {\n for (const [liveSlot, renderedSlot] of nestedSlots) {\n if (liveSlot === null) continue;\n synchronizeAttributes(liveSlot, renderedSlot);\n renderedSlot.replaceWith(liveSlot);\n }\n }\n for (const [, , children] of nestedSlots) {\n if (children !== undefined) commitStructuralPlan(children, store, options);\n }\n if (retained) {\n mutated = true;\n } else {\n if (live !== null) {\n live.replaceWith(rendered);\n mutated = true;\n }\n node = rendered;\n }\n }\n if (node === null) continue;\n const before = container.children[index] ?? null;\n if (node !== before) {\n container.insertBefore(node, before);\n mutated = true;\n }\n claimed.add(node);\n }\n // `nextItems` is the source of truth; this also drops SSR children the first value did not carry.\n for (const child of Array.from(container.children)) {\n if (claimed.has(child)) continue;\n child.remove();\n mutated = true;\n }\n restoreFocus(focus);\n const memory = getMemory(container, store);\n memory.clear();\n for (const value of nextItems) rememberItem(memory, value);\n return mutated;\n}\n\n/**\n * Pair every final item with a live child (by key, else by position) and\n * render what needs rendering. Patch indices describe `next`, live indices\n * move during reconciliation, so keyed nodes are resolved from the DOM first.\n */\nfunction prepareReconciliation(\n container: Element,\n plan: PatchPlan,\n env: RenderEnv,\n): readonly ReconciliationEntry[] | null {\n const { template, nextItems, forceRender = false, store, sanitizerPolicy } = env;\n const memory = store.get(container);\n // Read before anything is rendered: what the current items share is what the\n // template cannot say, and an inserted item has no predecessor of its own.\n const shared = sharedItemAttributes(container);\n const templateKeys = collectTemplateKeys(nextItems);\n const initialChildren = Array.from(container.children);\n const keyedChildren = indexByAttribute(initialChildren, KEY_ATTRIBUTE);\n const reserved = new Set<Element>();\n const entries: ReconciliationEntry[] = [];\n for (let index = 0; index < nextItems.length; index += 1) {\n const value = nextItems[index];\n const key = readKey(value);\n let live =\n key === undefined\n ? (initialChildren[index] ?? null)\n : (takeIndexedElement(keyedChildren, key) ?? null);\n if (live !== null && reserved.has(live)) live = null;\n if (live !== null) reserved.add(live);\n const replace = plan.replaces.has(index);\n const needsRender = forceRender || plan.renders.has(index) || live === null;\n const rendered = needsRender\n ? renderItem(\n container.ownerDocument,\n template,\n value,\n index,\n sanitizerPolicy,\n shared,\n templateKeys,\n )\n : undefined;\n if (rendered === null) return null;\n const nestedSlots =\n rendered === undefined\n ? []\n : prepareNestedSlots(\n replace ? null : live,\n rendered,\n value,\n key === undefined ? undefined : memory?.get(key),\n env,\n );\n if (nestedSlots === null) return null;\n entries.push([index, live, rendered, nestedSlots, replace]);\n }\n return entries;\n}\n\ninterface PatchPlan {\n readonly renders: ReadonlySet<number>;\n readonly replaces: ReadonlySet<number>;\n}\n\nfunction createPatchPlan(patches: readonly ArrayPatch[]): PatchPlan {\n const renders = new Set<number>();\n const replaces = new Set<number>();\n for (const patch of patches) {\n if (patch.kind === 'insert' || patch.kind === 'update') renders.add(patch.index);\n else if (patch.kind === 'replace') {\n renders.add(patch.index);\n replaces.add(patch.index);\n }\n }\n return { renders, replaces };\n}\n\n/** Plan each direct nested slot; a failure at any depth aborts before commit. */\nfunction prepareNestedSlots(\n oldItem: Element | null,\n newItem: Element,\n nextValue: unknown,\n prevValue: unknown,\n env: RenderEnv,\n): readonly NestedSlotPlan[] | null {\n const oldSlots = oldItem === null ? [] : findDirectNestedSlots(oldItem);\n const oldSlotsByKey = indexByAttribute(oldSlots, NESTED_KEY_ATTRIBUTE);\n const plans: NestedSlotPlan[] = [];\n for (const newSlot of findDirectNestedSlots(newItem)) {\n const key = newSlot.getAttribute(NESTED_KEY_ATTRIBUTE);\n if (key === null) continue;\n const oldSlot = takeIndexedElement(oldSlotsByKey, key) ?? null;\n const nextTemplate = newSlot.getAttribute(NESTED_TEMPLATE_ATTRIBUTE);\n // Without a nested template the slot's rendered static content is authoritative.\n const liveSlot = nextTemplate ? oldSlot : null;\n const previousTemplate = liveSlot?.getAttribute(NESTED_TEMPLATE_ATTRIBUTE) ?? null;\n let children: StructuralPlan | undefined;\n if (nextTemplate) {\n const nextNested = readNestedArray(nextValue, key);\n if (nextNested !== undefined || liveSlot !== null) {\n const resolvedNext = nextNested ?? [];\n const prevNested = liveSlot === null ? [] : (readNestedArray(prevValue, key) ?? []);\n const patches = diffArray(prevNested, resolvedNext);\n const templateChanged = liveSlot !== null && previousTemplate !== nextTemplate;\n if (patches.length > 0 || templateChanged) {\n children =\n prepareStructuralPlan(liveSlot ?? newSlot, patches, {\n template: nextTemplate,\n nextItems: resolvedNext,\n forceRender: templateChanged,\n store: env.store,\n sanitizerPolicy: env.sanitizerPolicy,\n }) ?? undefined;\n if (children === undefined) return null;\n }\n }\n }\n plans.push([liveSlot, newSlot, children]);\n }\n return plans;\n}\n\n/** Only the outermost nested slots: a transplanted slot owns the recursion below it. */\nfunction findDirectNestedSlots(item: Element): readonly Element[] {\n return Array.from(item.querySelectorAll(`[${NESTED_KEY_ATTRIBUTE}]`)).filter((slot) => {\n let ancestor = slot.parentElement;\n while (ancestor !== null && ancestor !== item) {\n if (ancestor.hasAttribute(NESTED_KEY_ATTRIBUTE)) return false;\n ancestor = ancestor.parentElement;\n }\n return ancestor === item;\n });\n}\n\nfunction synchronizeAttributes(target: Element, source: Element): void {\n for (const attribute of Array.from(target.attributes)) {\n if (!source.hasAttribute(attribute.name)) target.removeAttribute(attribute.name);\n }\n for (const attribute of Array.from(source.attributes)) {\n if (target.getAttribute(attribute.name) !== attribute.value) {\n target.setAttribute(attribute.name, attribute.value);\n }\n }\n}\n\nfunction readNestedArray(value: unknown, key: string): readonly unknown[] | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const nested = (value as Record<string, unknown>)[key];\n return Array.isArray(nested) ? (nested as readonly unknown[]) : undefined;\n}\n\nfunction getMemory(container: Element, store: StructuralStore): Map<string, unknown> {\n let map = store.get(container);\n if (!map) {\n map = new Map();\n store.set(container, map);\n }\n return map;\n}\n\nfunction rememberItem(memory: Map<string, unknown>, value: unknown): void {\n const key = readKey(value);\n if (key !== undefined) memory.set(key, value);\n}\n\nfunction readKey(value: unknown): string | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const id = (value as Record<string, unknown>)['id'];\n return typeof id === 'string' || typeof id === 'number' ? String(id) : undefined;\n}\n\nfunction indexByAttribute(elements: readonly Element[], attribute: string): Map<string, Element[]> {\n const indexed = new Map<string, Element[]>();\n // Reverse order so pop() yields duplicates in DOM order.\n for (let index = elements.length - 1; index >= 0; index -= 1) {\n const element = elements[index];\n if (element === undefined) continue;\n const key = element.getAttribute(attribute);\n if (key === null) continue;\n const bucket = indexed.get(key);\n if (bucket === undefined) indexed.set(key, [element]);\n else bucket.push(element);\n }\n return indexed;\n}\n\nfunction takeIndexedElement(indexed: Map<string, Element[]>, key: string): Element | undefined {\n return indexed.get(key)?.pop();\n}\n\n/** Render one item; its `id` is stamped as `data-payload-key` so later diffs find it. */\nfunction renderItem(\n ownerDocument: Document,\n template: string,\n value: unknown,\n index: number,\n policy: SanitizerPolicyMode | undefined,\n shared: readonly InheritedAttribute[],\n knownKeys: ReadonlySet<string>,\n): Element | null {\n const filled = interpolateArrayTemplate(template, value, index, safeStringify, knownKeys);\n const safe = sanitizeHtmlWithPolicy(filled, policy, templateSanitizeOptions(template));\n const host = ownerDocument.createElement('template');\n host.innerHTML = trustedHtml(safe);\n const first = host.content.firstElementChild;\n if (!first) return null;\n // Before the morph, which strips from the live item every attribute the\n // rendered one lacks: the marker has to be on this side to survive.\n inheritItemAttributes(first, shared);\n const key = readKey(value);\n if (key !== undefined) first.setAttribute(KEY_ATTRIBUTE, key);\n return first;\n}\n","/**\n * LRU discipline for a module-scoped lookup `Map` (ADR 0003 §3). Functions\n * over the caller's own `Map` rather than a wrapper class: the inline bundle\n * mangles function names but not property names, so a class would pay for\n * every `this.entries` in full. A `Map` iterates in insertion order, which a\n * touch on every hit turns into recency order. Values are never `undefined`,\n * and a write follows a miss — a key already present keeps its position.\n */\n\n/** Read and touch: a hit becomes the newest entry. */\nexport function lruGet<K, V>(map: Map<K, V>, key: K): V | undefined {\n const value = map.get(key);\n if (value !== undefined) {\n map.delete(key);\n map.set(key, value);\n }\n return value;\n}\n\n/** Write as the newest entry, dropping the oldest ones past `limit`; returns `value`. */\nexport function lruSet<K, V>(map: Map<K, V>, key: K, value: V, limit: number): V {\n map.set(key, value);\n lruTrim(map, limit);\n return value;\n}\n\n/** Drop the oldest entries until `limit` holds. */\nexport function lruTrim<K>(map: Map<K, unknown>, limit: number): void {\n for (const oldest of map.keys()) {\n if (map.size <= limit) break;\n map.delete(oldest);\n }\n}\n","/**\n * `structural-array` renderer (`data-payload-structural`): diffs the previous\n * and next item lists and patches the container through the keyed morph\n * (ADR 0008). All diff memory lives in the closure, one per client.\n */\n\nimport { safeConsoleWarn } from '@core/diagnostics';\nimport { markNoWriteCallback } from '@core/internal-outcome';\nimport {\n applyStructuralPatches,\n createStructuralStore,\n KEY_ATTRIBUTE,\n type StructuralStore,\n} from '@core/structural-applier';\nimport { diffArray } from '@schema/diff';\nimport type { FieldRenderer } from '@core/types';\nimport { isEmptyValue } from './utils';\n\ninterface StructuralRenderState {\n readonly values: readonly unknown[];\n readonly template: string;\n readonly domSnapshot: readonly (string | null)[];\n}\n\ntype WarnedCodes = WeakMap<Element, Set<string>>;\n\n/** @internal */\nexport function createStructuralArrayRenderer(): FieldRenderer {\n const states = new WeakMap<Element, StructuralRenderState>();\n const warnedContainers = new WeakSet<Element>();\n const warnedKeys: WarnedCodes = new WeakMap();\n const store: StructuralStore = createStructuralStore();\n\n return {\n name: 'structural-array',\n render: /* @__PURE__ */ markNoWriteCallback((target, rawValue, context) => {\n const value = isEmptyValue(rawValue) ? [] : rawValue;\n if (!Array.isArray(value)) return false;\n const container = target.element;\n const template = target.arrayTemplate;\n if (template === undefined || template.length === 0) {\n warnMissingTemplate(warnedContainers, container, target.fieldName);\n return false;\n }\n const previousState = states.get(container);\n const domSnapshot = readDomKeySnapshot(container);\n const previous = previousState?.values ?? valuesFromDomSnapshot(domSnapshot);\n const patches = diffArray(previous, value);\n const forceRender = needsForcedRender(previousState, template, domSnapshot);\n if (patches.length === 0 && !forceRender) {\n states.set(container, { values: value.slice(), template, domSnapshot });\n return false;\n }\n warnAboutKeys(warnedKeys, container, target.fieldName, previousState?.values, value);\n const applied = applyStructuralPatches({\n template,\n container,\n patches,\n nextItems: value,\n store,\n forceRender,\n sanitizerPolicy: context.sanitizerPolicy,\n onDuplicateKey: (owner, key) => {\n warnDuplicateKey(warnedKeys, owner, target.fieldName, key);\n },\n });\n if (applied === null) return false;\n states.set(container, {\n values: value.slice(),\n template,\n domSnapshot: readDomKeySnapshot(container),\n });\n return applied ? undefined : false;\n }),\n };\n}\n\nfunction needsForcedRender(\n previous: StructuralRenderState | undefined,\n template: string,\n domSnapshot: readonly (string | null)[],\n): boolean {\n if (previous === undefined) return true;\n return previous.template !== template || !sameDomKeySnapshot(previous.domSnapshot, domSnapshot);\n}\n\n// SSR markup exists before any in-memory snapshot; its direct-child keys let\n// the first update reconcile instead of appending a second copy.\nfunction readDomKeySnapshot(container: Element): readonly (string | null)[] {\n return Array.from(container.children, (child) => child.getAttribute(KEY_ATTRIBUTE));\n}\n\nfunction valuesFromDomSnapshot(snapshot: readonly (string | null)[]): readonly unknown[] {\n return snapshot.map((key, position) =>\n key === null ? { __payloadDomPosition: position } : { id: key },\n );\n}\n\nfunction sameDomKeySnapshot(\n previous: readonly (string | null)[],\n current: readonly (string | null)[],\n): boolean {\n if (previous.length !== current.length) return false;\n return previous.every((key, index) => key === current[index]);\n}\n\nfunction warnMissingTemplate(\n warnedContainers: WeakSet<Element>,\n container: Element,\n fieldName: string,\n): void {\n if (warnedContainers.has(container)) return;\n warnedContainers.add(container);\n safeConsoleWarn(\n `[live-preview] LP0403: Skipping structural update for \"${fieldName}\": ` +\n `<${container.tagName.toLowerCase()} data-payload-structural> needs ` +\n `data-payload-array-template (for example \"<li>{{label}}</li>\").`,\n );\n}\n\nfunction itemKey(value: unknown): string | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const id = (value as Record<string, unknown>)['id'];\n return typeof id === 'string' || typeof id === 'number' ? String(id) : undefined;\n}\n\n/** Key problems degrade the morph, they do not break it; each is announced once per container (ADR 0008 §5). */\nfunction warnAboutKeys(\n warned: WarnedCodes,\n container: Element,\n fieldName: string,\n previous: readonly unknown[] | undefined,\n next: readonly unknown[],\n): void {\n const nextKeys = next.map(itemKey);\n const seen = new Set<string>();\n const duplicate = nextKeys.find((key) => {\n if (key === undefined) return false;\n if (seen.has(key)) return true;\n seen.add(key);\n return false;\n });\n if (duplicate !== undefined) warnDuplicateKey(warned, container, fieldName, duplicate);\n if (nextKeys.some((key) => key === undefined)) {\n warnOnce(\n warned,\n container,\n 'LP0404',\n `LP0404: an item of \"${fieldName}\" has no id and pairs by ` +\n 'position, so an insert re-renders every row after it. Give items a stable id.',\n );\n }\n if (previous?.length === next.length && next.length > 1) {\n const previousKeys = new Set(previous.map(itemKey).filter((key) => key !== undefined));\n const stable = nextKeys.some((key) => key !== undefined && previousKeys.has(key));\n if (previousKeys.size > 0 && !stable) {\n warnOnce(\n warned,\n container,\n 'LP0406',\n `LP0406: every key of \"${fieldName}\" changed at once; the ` +\n 'source generates ids per message, so no item can be retained across updates.',\n );\n }\n }\n}\n\n// Reached from the pre-diff check and from the morph's nested-slot check; one site, one message.\nfunction warnDuplicateKey(\n warned: WarnedCodes,\n owner: Element,\n fieldName: string,\n key: string,\n): void {\n warnOnce(\n warned,\n owner,\n 'LP0405',\n `LP0405: two items of \"${fieldName}\" share the key \"${key}\"; later ones pair by ` +\n 'position. Make `id` unique per item.',\n );\n}\n\nfunction warnOnce(warned: WarnedCodes, container: Element, code: string, message: string): void {\n let codes = warned.get(container);\n if (codes === undefined) {\n codes = new Set();\n warned.set(container, codes);\n }\n if (codes.has(code)) return;\n codes.add(code);\n safeConsoleWarn(`[live-preview] ${message}`);\n}\n","/**\n * One source → dependents map for `skipUnchanged`. Markup declares it the\n * other way round (`data-payload-depends=\"price\"` on the element bound to\n * `priceLabel`) because an author names what a binding needs; the runtime\n * option is keyed by source because the scheduler asks what changed.\n * @internal\n */\n\nexport type DependencyMap = Readonly<Record<string, readonly string[]>>;\n\n/**\n * Field names from an attribute value. Separators are commas and\n * whitespace; empty entries are dropped; order is kept, duplicates removed.\n * @internal\n */\nexport function parseDependencyList(value: string | null | undefined): readonly string[] {\n if (value === null || value === undefined) return [];\n const seen = new Set<string>();\n const names: string[] = [];\n for (const raw of value.split(/[\\s,]+/)) {\n const name = raw.trim();\n if (name.length === 0 || seen.has(name)) continue;\n seen.add(name);\n names.push(name);\n }\n return names;\n}\n\n/**\n * Merge dependency maps (source → dependents). Later maps add to earlier\n * ones; a dependent is listed once per source.\n * @internal\n */\nexport function mergeDependencyMaps(...maps: readonly DependencyMap[]): DependencyMap {\n const merged = new Map<string, Set<string>>();\n for (const map of maps) {\n for (const [source, dependents] of Object.entries(map)) {\n let bucket = merged.get(source);\n if (bucket === undefined) {\n bucket = new Set();\n merged.set(source, bucket);\n }\n for (const dependent of dependents) bucket.add(dependent);\n }\n }\n const result: Record<string, readonly string[]> = {};\n for (const [source, dependents] of merged) result[source] = [...dependents];\n return result;\n}\n\n/**\n * The map an element's `data-payload-depends` contributes: every declared\n * source points at this binding's field.\n * @internal\n */\nexport function dependencyMapFromBinding(\n fieldName: string,\n dependsOn: readonly string[],\n): DependencyMap {\n const map: Record<string, readonly string[]> = {};\n for (const source of dependsOn) {\n if (source === fieldName) continue;\n map[source] = [fieldName];\n }\n return map;\n}\n"],"mappings":"aA8BO,IAtBLA,EAsBWC,GAtBXD,EAsB+C,IAAIE,IAClDC,QAAQC,QAA8CF,GArBhD,IAAIA,KACT,KCFoB,iBADcG,EDIVL,KAAcE,KCHI,OAAVG,GAAoC,mBAAVA,IACD,mBAA/CA,EAAsCC,MAC7CC,QAAQC,QAAQH,GAAOI,MAAM,ODEhC,OAEA,CCPG,IAA+BJ,ICChCK,iBAAA,IAAuBC,QAGtB,SAASC,EAAwCC,GAEtD,OADAH,EAAiBI,IAAID,GACdA,CACT,CCIO,IAODE,iBAAsBC,OAAOC,IAAI,sCAmDhC,SAASC,EAAYC,GAC1B,MAAMC,EApBR,WACE,MAAMC,EAzBR,WACE,MAAMC,EAAQC,WACRC,EAAWF,EAAMP,GACvB,QAAiB,IAAbS,EAAwB,OAAOA,EACnC,MAAMC,EAA0B,GAEhC,OADAH,EAAMP,GAAQU,EACPA,CACT,CAkBmBC,GACjB,QAA0B,IAAtBL,EAASM,SAAwB,OAAON,EAASM,SACrD,QAAsB,IAAlBN,EAASO,KAAoB,OAAOP,EAASO,KACjD,MAAMC,EAXR,WACE,MAAMC,EAAaP,WAA0CQ,aAC7D,GAAyB,iBAAdD,GAAwC,OAAdA,EAErC,MAAyB,mBADTA,EAAyCE,aAClBF,OAAwC,CACjF,CAMcG,GACZ,IACEZ,EAASO,UACC,IAARC,EACI,KACAA,EAAIG,aAhD2B,uBAgDa,CAAEE,WAAaC,GAAUA,GAC7E,OAGEd,EAASO,KAAO,IAClB,CACA,OAAOP,EAASO,IAClB,CAIiBQ,GACf,OAAe,OAAXhB,EAAwBD,EACrBC,EAAOc,WAAWf,EAC3B,CC5EA,IAAMkB,iBAAA,IAA0CC,IAAI,CAAC,QAAS,SAAU,UAAW,SAE7EC,EAAgB,mCAIhBC,EAAoB,YAGpBC,EAAiB,YAYjBC,EAAc,wCAEdC,EAAS,yBASR,SAASC,UAAUC,GACxB,GAAmB,iBAARA,EAAkB,OAAO,EACpC,MAAMC,EAAUD,EAAIE,OAAOC,QAAQP,EAAgB,IACnD,GAAuB,IAAnBK,EAAQG,OAAc,OAAO,EACjC,GAAIH,EAAQI,WAAW,MAAQJ,EAAQI,WAAW,KAAM,OAAO,EAC/D,GAAIV,EAAkBW,KAAKL,GAAU,OAAO,EAC5C,GAAIA,EAAQI,WAAW,KAAM,OAAO,EACpC,IACE,MAAME,EAAS,IAAIC,IAAIP,GACvB,OAAOT,EAAeiB,IAAIF,EAAOG,SACnC,OACE,OAAOhB,EAAcY,KAAKL,KAjB9B,SAA4BzC,GAC1B,MAAMmD,EAASb,EAAOc,KAAKpD,EAAM2C,QAAQN,EAAa,OAAO,GAC7D,YAAkB,IAAXc,IAAyBnB,EAAeiB,IAAI,GAAGE,EAAOE,iBAC/D,CAc2CC,CAAmBb,EAC5D,CACF,CCnBA,IAAMc,iBAAA,IAAetB,IA4Bd,SAASuB,EAAuBC,GACjCF,EAASN,IAAIQ,KACjBF,EAAS9C,IAAIgD,GAtCf,SAAcC,GACZ,IACE5D,QAAQC,KAAK2D,EACf,OAEA,CACF,CAiCE3D,CACE,yDAAyD0D,qFA3B7D,SAAgBA,GACd,OAAIA,EAAKZ,WAHY,iBAKjB,0IAIS,OAATY,GAA0B,SAATA,EAEjB,+HAKF,8GAGJ,CAWkEE,CAAOF,OAEzE,CCpDA,IAAMG,iBAAA,IAAwC3B,IAAI,CAChD,IACA,KACA,SACA,IACA,KACA,IACA,IACA,IACA,SACA,OACA,QACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,aACA,OACA,MACA,MACA,OACA,MACA,IACA,OACA,MACA,UACA,UACA,QACA,SACA,SACA,MACA,SACA,aACA,MACA,UACA,SACA,QACA,QACA,MACA,MACA,KACA,OACA,OACA,OACA,IACA,QACA,QACA,QACA,QACA,KACA,KACA,KACA,UACA,WACA,QAII4B,iBAAA,IAAyC5B,IAAI,CACjD,QACA,SACA,WACA,SACA,SACA,UAII6B,iBAAA,IAA6C7B,IAAI,CACrD,SACA,QACA,SACA,SACA,QACA,OACA,OACA,OACA,OACA,QACA,SACA,SACA,WACA,MACA,OACA,WACA,QACA,WACA,WACA,aAGI8B,iBAAA,IAAuC9B,IAAI,CAC/C,KACA,QACA,OACA,MACA,QACA,OACA,aAGI+B,EAAmB,QACnBC,EAAmB,QAGnBC,EAA6D,CACjEC,iBAAG,IAAIlC,IAAI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","ignoreList":[]}
|