payload-live-preview 2.3.2 → 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 +7 -1
- package/dist/core.d.ts +7 -1
- 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/lexical.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lexical.js","names":["warnOnce","key","message","env","globalThis","process","isDevelopmentProcess","holder","warned","Set","has","add","console","warn","SLOT","Symbol","for","trustedHtml","html","policy","policies","realm","existing","created","shared","override","auto","api","candidate","trustedTypes","createPolicy","factory","createHTML","input","resolvePolicy","SAFE_PROTOCOLS","RELATIVE_PATH","PROTOCOL_RELATIVE","PARSER_IGNORED","SCHEME_GAPS","SCHEME","isSafeUrl","url","trimmed","trim","replace","length","startsWith","test","parsed","URL","protocol","value","scheme","exec","toLowerCase","spellsUnsafeScheme","isExternalHttpUrl","reported","reportDroppedAttribute","name","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","setSanitizerDocument","doc","sanitizeHtml","options","document","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","String","sanitizeHtmlWithPolicy","__INLINE_BUILD__","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","hasAttribute","hardenAnchor","HTML_ESCAPES","freeze","HTML_ESCAPE_PATTERN","escapeHtml","text","char","ATTR_ESCAPES","ATTR_ESCAPE_PATTERN","escapeHtmlAttribute","TextFormat","BOLD","ITALIC","STRIKETHROUGH","UNDERLINE","CODE","SUBSCRIPT","SUPERSCRIPT","HIGHLIGHT","ALIGNMENT_NUMERIC","ALIGNMENT_STRING","resolveAlignment","fmt","format","resolveIndent","indent","dirAttribute","direction","layoutClassAttribute","align","classes","push","join","VALID_TAGS","asRecord","readMedia","sanitizeIdent","nonEmpty","PLACEHOLDER","BLANK_TARGET","linkRenderer","ctx","children","renderChildren","fields","relation","patterned","internalLinkPattern","pattern","parts","read","part","encodeURIComponent","matchAll","_match","fillPattern","slug","id","collection","locale","internalUrl","resolveUrl","titleAttr","target","targetAttribute","extractText","out","dimensionAttributes","width","height","typeAttribute","mimeType","registry","registerBlockRenderer","blockType","renderer","lookupBlockRenderer","registeredBlockTypes","keys","renderBlockNode","baseClass","custom","onUnrenderedBlock","spanAttribute","BUILTIN_NODE_RENDERERS","paragraph","heading","tagRaw","list","listType","startAttr","checkAttr","listitem","valueAttr","link","autolink","quote","language","linebreak","horizontalrule","tab","upload","mime","renderVideo","renderAudio","alt","renderImage","label","filename","renderFallbackLink","relationship","relationTo","title","pickRelationLabel","classAttr","block","inlineBlock","tablerow","tablecell","header","register","type","lookup","warnedNoSanitizer","isLexicalContent","root","isArray","lexicalToHtml","context","renderNode","createContext","sanitize","requireDocument","warnNoSanitizerOnce","lexicalToPlainText","extractPlainText","str","safeUrlOrUndefined","raw","captionHtml","caption","calloutRenderer","importance","richText","body","readLexicalChildren","imageBlockRenderer","media","videoBlockRenderer","typeAttr","poster","codeBlockRenderer","langClass","ctaBlockRenderer","lead","leadHtml","buttons","renderCtaAnchor","secondaryLabel","secondaryHref","primary","targetAttr","registerDefaultBlocks"],"sources":["../src/types/dev-warning.ts","../src/security/trusted-types.ts","../src/security/url-validator.ts","../src/security/sanitizer-report.ts","../src/security/sanitizer.ts","../src/security/escape.ts","../src/lexical/types.ts","../src/lexical/utils.ts","../src/lexical/nodes/paragraph.ts","../src/lexical/nodes/heading.ts","../src/lexical/value-shapes.ts","../src/lexical/nodes/link.ts","../src/lexical/nodes/code.ts","../src/lexical/nodes/upload.ts","../src/lexical/nodes/relationship.ts","../src/lexical/blocks/registry.ts","../src/lexical/nodes/block.ts","../src/lexical/nodes/table.ts","../src/lexical/nodes/builtin.ts","../src/lexical/nodes/text.ts","../src/lexical/nodes/list.ts","../src/lexical/nodes/quote.ts","../src/lexical/nodes/linebreak.ts","../src/lexical/registry.ts","../src/lexical/render.ts","../src/lexical/blocks/defaults.ts"],"sourcesContent":["/**\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","/**\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","/** 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 * Lexical document types. Only `type` and `children` are required; every\n * other property is forwarded opaquely to the renderer for that node type.\n */\n\n/** Lexical text-format bitmask; flags combine (`BOLD | ITALIC === 3`). @internal */\nexport const TextFormat = {\n BOLD: 1,\n ITALIC: 2,\n STRIKETHROUGH: 4,\n UNDERLINE: 8,\n CODE: 16,\n SUBSCRIPT: 32,\n SUPERSCRIPT: 64,\n HIGHLIGHT: 128,\n} as const;\n\nexport interface LexicalNode {\n readonly type: string;\n readonly version?: number;\n readonly format?: number | string;\n readonly indent?: number;\n readonly direction?: 'ltr' | 'rtl' | null;\n readonly children?: readonly LexicalNode[];\n readonly text?: string;\n readonly [extra: string]: unknown;\n}\n\nexport interface LexicalRoot {\n readonly root: {\n readonly type?: string;\n readonly children: readonly LexicalNode[];\n readonly direction?: 'ltr' | 'rtl' | null;\n readonly format?: number | string;\n readonly indent?: number;\n readonly [extra: string]: unknown;\n };\n}\n","/** Attribute helpers shared by the block-level node renderers. */\n\nimport type { LexicalNode } from './types';\n\nconst ALIGNMENT_NUMERIC: Readonly<Record<number, string>> = {\n 1: 'left',\n 2: 'center',\n 3: 'right',\n 4: 'justify',\n};\n\nconst ALIGNMENT_STRING = new Set(['left', 'center', 'right', 'justify', 'start', 'end']);\n\n/** Alignment of a block node; Payload emits it as a string or as Lexical's numeric format. */\nexport function resolveAlignment(node: LexicalNode): string | undefined {\n const fmt = node.format;\n if (typeof fmt === 'string' && ALIGNMENT_STRING.has(fmt)) return fmt;\n if (typeof fmt === 'number') return ALIGNMENT_NUMERIC[fmt];\n return undefined;\n}\n\nexport function resolveIndent(node: LexicalNode): number {\n const indent = node.indent;\n return typeof indent === 'number' && indent > 0 ? indent : 0;\n}\n\nexport function dirAttribute(node: LexicalNode): string {\n if (node.direction === 'rtl') return ' dir=\"rtl\"';\n if (node.direction === 'ltr') return ' dir=\"ltr\"';\n return '';\n}\n\n/**\n * Alignment and indent as `lp-align-*` / `lp-indent-N` classes. The sanitizer\n * strips `style` under every policy, so inline CSS would never reach the page.\n */\nexport function layoutClassAttribute(node: LexicalNode): string {\n const align = resolveAlignment(node);\n const indent = resolveIndent(node);\n const classes: string[] = [];\n if (align !== undefined) classes.push(`lp-align-${align}`);\n if (indent > 0) classes.push(`lp-indent-${String(indent)}`);\n return classes.length === 0 ? '' : ` class=\"${classes.join(' ')}\"`;\n}\n","import type { NodeRenderer } from '../registry';\nimport { dirAttribute, layoutClassAttribute } from '../utils';\n\nconst paragraphRenderer: NodeRenderer = (node, ctx): string => {\n const children = ctx.renderChildren(node.children ?? []);\n return `<p${dirAttribute(node)}${layoutClassAttribute(node)}>${children}</p>`;\n};\n\nexport { paragraphRenderer };\n","/** `heading` renderer; an unknown `tag` (transient editor states emit them) falls back to `h2`. */\n\nimport type { NodeRenderer } from '../registry';\nimport { dirAttribute, layoutClassAttribute } from '../utils';\n\nconst VALID_TAGS = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']);\n\nconst headingRenderer: NodeRenderer = (node, ctx): string => {\n const tagRaw = typeof node['tag'] === 'string' ? node['tag'].toLowerCase() : 'h2';\n const tag = VALID_TAGS.has(tagRaw) ? tagRaw : 'h2';\n const children = ctx.renderChildren(node.children ?? []);\n return `<${tag}${dirAttribute(node)}${layoutClassAttribute(node)}>${children}</${tag}>`;\n};\n\nexport { headingRenderer };\n","/**\n * Value-shape helpers shared by the Lexical node renderers and the field\n * renderers. They live here because `field-types` may import `lexical`, never\n * the reverse (scripts/architecture-policy.ts).\n */\n\nexport interface MediaShape {\n readonly id?: string | number;\n readonly url?: string;\n readonly alt?: string;\n readonly filename?: string;\n readonly mimeType?: string;\n readonly width?: number;\n readonly height?: number;\n readonly sizes?: MediaSizes;\n /** Payload 4's name for `sizes`. */\n readonly variants?: MediaSizes;\n}\n\ntype MediaSizes = Readonly<\n Record<\n string,\n {\n readonly url?: string;\n readonly width?: number;\n readonly height?: number;\n readonly mimeType?: string;\n }\n >\n>;\n\nexport interface RelationShape {\n readonly id?: string | number;\n readonly title?: string;\n readonly name?: string;\n readonly slug?: string;\n readonly url?: string;\n}\n\nexport function asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nexport function readMedia(value: unknown): MediaShape | undefined {\n return asRecord(value);\n}\n\n/**\n * A media document's URL as the preview should load it. Unless its config sets\n * `serverURL`, Payload returns a path on its own origin (`/api/media/file/a.png`);\n * its admin resolves the path against the server, and the preview, framed on\n * another origin, would otherwise ask itself for the file. Only such a path is\n * Payload's to place: any other URL, and every URL when no server is known, is\n * written as delivered. A result on another origin than the server's (`/\\host`\n * is one, to the URL parser) is discarded.\n */\nexport function resolveMediaUrl(url: string, serverURL: string | undefined): string {\n if (serverURL === undefined || serverURL === '' || !url.startsWith('/')) return url;\n try {\n const server = new URL(serverURL);\n const resolved = new URL(url, server);\n return resolved.origin === server.origin ? resolved.href : url;\n } catch {\n return url;\n }\n}\n\n/** Reduce `value` to `[a-z0-9_-]` so it can be a class-name fragment without escaping. */\nexport function sanitizeIdent(value: string): string {\n return value.replace(/[^a-zA-Z0-9_-]/g, '-').toLowerCase();\n}\n\nfunction nonEmpty(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\n/** Label precedence for a populated relation: `title`, `name`, `slug`, then `id`. */\nexport function pickRelationLabel(value: RelationShape): string | undefined {\n const label = nonEmpty(value.title) ?? nonEmpty(value.name) ?? nonEmpty(value.slug);\n if (label !== undefined) return label;\n return value.id === undefined ? undefined : String(value.id);\n}\n","/**\n * `link` / `autolink` renderer. Payload 3.x serialises links as\n * `{ fields: { linkType, url, newTab, doc } }`; vanilla Lexical puts `url`\n * and `target` on the node itself. Both shapes are read, Payload's first.\n *\n * An internal link's address is a route only the site knows. The document's own\n * `url` wins, then `internalLinkPattern`, then `/<slug>`, which is all that can be\n * said without either.\n */\n\nimport { escapeHtml, escapeHtmlAttribute } from '@security/escape';\nimport { isExternalHttpUrl, isSafeUrl } from '@security/url-validator';\nimport type { NodeRenderer, RenderNodeContext } from '../registry';\nimport type { LexicalNode } from '../types';\nimport { asRecord } from '../value-shapes';\n\nconst PLACEHOLDER = /\\{(slug|id|collection|locale)\\}/gu;\nconst BLANK_TARGET = ' target=\"_blank\" rel=\"noopener noreferrer\"';\n\nconst linkRenderer: NodeRenderer = (node, ctx): string => {\n const children = ctx.renderChildren(node.children ?? []);\n const fields = asRecord(node['fields']);\n const url = resolveUrl(node, fields, ctx);\n if (url === undefined || !isSafeUrl(url)) return children;\n const titleAttr =\n typeof node['title'] === 'string' ? ` title=\"${escapeHtml(node['title'])}\"` : '';\n return `<a href=\"${escapeHtmlAttribute(url)}\"${targetAttribute(node, fields, url)}${titleAttr}>${children}</a>`;\n};\n\nfunction resolveUrl(\n node: LexicalNode,\n fields: Record<string, unknown> | undefined,\n ctx: RenderNodeContext,\n): string | undefined {\n if (fields?.['linkType'] === 'internal') return internalUrl(fields['doc'], ctx);\n const url = fields?.['url'] ?? node['url'];\n return typeof url === 'string' && url.length > 0 ? url : undefined;\n}\n\n/** Without a pattern an internal link needs the populated document; a bare id offers no route. */\nfunction internalUrl(doc: unknown, ctx: RenderNodeContext): string | undefined {\n const relation = asRecord(doc);\n const value = asRecord(relation?.['value']);\n const url = value?.['url'];\n if (typeof url === 'string' && url.length > 0) return url;\n const patterned =\n ctx.internalLinkPattern === undefined\n ? undefined\n : fillPattern(ctx.internalLinkPattern, {\n slug: value?.['slug'],\n id: value === undefined ? relation?.['value'] : value['id'],\n collection: relation?.['relationTo'],\n locale: ctx.locale,\n });\n if (patterned !== undefined) return patterned;\n const slug = value?.['slug'];\n return typeof slug === 'string' && slug.length > 0 ? `/${slug}` : undefined;\n}\n\n/** `undefined` when a placeholder the pattern names has no value: half a route is a wrong one. */\nfunction fillPattern(pattern: string, parts: Record<string, unknown>): string | undefined {\n const read = (name: string): string | undefined => {\n const part = parts[name];\n return (typeof part === 'string' && part.length > 0) || typeof part === 'number'\n ? encodeURIComponent(part)\n : undefined;\n };\n for (const [, name = ''] of pattern.matchAll(PLACEHOLDER)) {\n if (read(name) === undefined) return undefined;\n }\n return pattern.replace(PLACEHOLDER, (_match, name: string) => read(name) ?? '');\n}\n\n/** `_blank` always carries `noopener noreferrer`, whichever side asked for it. */\nfunction targetAttribute(\n node: LexicalNode,\n fields: Record<string, unknown> | undefined,\n url: string,\n): string {\n if (fields?.['newTab'] === true || isExternalHttpUrl(url)) return BLANK_TARGET;\n const target = typeof node['target'] === 'string' ? node['target'] : '';\n if (target === '') return '';\n if (target === '_blank') return BLANK_TARGET;\n return ` target=\"${escapeHtml(target)}\"`;\n}\n\nexport { linkRenderer };\n","/** `code` (fenced block) and `code-highlight` (token span) renderers. */\n\nimport { escapeHtml } from '@security/escape';\nimport type { NodeRenderer } from '../registry';\nimport type { LexicalNode } from '../types';\nimport { sanitizeIdent } from '../value-shapes';\n\nconst codeRenderer: NodeRenderer = (node): string => {\n const language = sanitizeIdent(typeof node['language'] === 'string' ? node['language'] : '');\n const langClass = language === '' ? '' : ` class=\"language-${language}\"`;\n return `<pre><code${langClass}>${escapeHtml(extractText(node))}</code></pre>`;\n};\n\nconst codeHighlightRenderer: NodeRenderer = (node): string => {\n const text = typeof node.text === 'string' ? node.text : '';\n const highlight =\n typeof node['highlightType'] === 'string'\n ? ` class=\"token-${sanitizeIdent(node['highlightType'])}\"`\n : '';\n return `<span${highlight}>${escapeHtml(text)}</span>`;\n};\n\nexport { codeRenderer, codeHighlightRenderer };\n\nfunction extractText(node: LexicalNode): string {\n if (typeof node.text === 'string') return node.text;\n if (!node.children) return '';\n let out = '';\n for (const child of node.children) out += extractText(child);\n return out;\n}\n","/**\n * `upload` renderer: `<img>` for image (or unknown) MIME types, `<video>` /\n * `<audio>` for media, a download link otherwise. An unsafe URL renders nothing.\n */\n\nimport { escapeHtml, escapeHtmlAttribute } from '@security/escape';\nimport { isSafeUrl } from '@security/url-validator';\nimport type { NodeRenderer } from '../registry';\nimport { readMedia, type MediaShape } from '../value-shapes';\n\nconst uploadRenderer: NodeRenderer = (node): string => {\n const value = readMedia(node['value']);\n if (value === undefined) return '';\n const url = value.url;\n if (typeof url !== 'string' || !isSafeUrl(url)) return '';\n\n const mime = typeof value.mimeType === 'string' ? value.mimeType : '';\n if (mime.startsWith('video/')) return renderVideo(value, url);\n if (mime.startsWith('audio/')) return renderAudio(value, url);\n if (mime === '' || mime.startsWith('image/')) return renderImage(value, url);\n return renderFallbackLink(value, url);\n};\n\nexport { uploadRenderer };\n\nfunction dimensionAttributes(value: MediaShape): string {\n const width = typeof value.width === 'number' ? ` width=\"${String(value.width)}\"` : '';\n const height = typeof value.height === 'number' ? ` height=\"${String(value.height)}\"` : '';\n return `${width}${height}`;\n}\n\nfunction typeAttribute(value: MediaShape): string {\n return typeof value.mimeType === 'string' ? ` type=\"${escapeHtmlAttribute(value.mimeType)}\"` : '';\n}\n\nfunction renderImage(value: MediaShape, url: string): string {\n const alt = typeof value.alt === 'string' ? escapeHtml(value.alt) : '';\n return `<img src=\"${escapeHtmlAttribute(url)}\" alt=\"${alt}\"${dimensionAttributes(value)} loading=\"lazy\" decoding=\"async\">`;\n}\n\nfunction renderVideo(value: MediaShape, url: string): string {\n return `<video controls${dimensionAttributes(value)}><source src=\"${escapeHtmlAttribute(url)}\"${typeAttribute(value)}></video>`;\n}\n\nfunction renderAudio(value: MediaShape, url: string): string {\n return `<audio controls><source src=\"${escapeHtmlAttribute(url)}\"${typeAttribute(value)}></audio>`;\n}\n\nfunction renderFallbackLink(value: MediaShape, url: string): string {\n const label = typeof value.filename === 'string' ? escapeHtml(value.filename) : escapeHtml(url);\n return `<a href=\"${escapeHtmlAttribute(url)}\" target=\"_blank\" rel=\"noopener noreferrer\">${label}</a>`;\n}\n","/** `relationship` renderer: a link when the populated document carries a URL, a span otherwise. */\n\nimport { escapeHtml, escapeHtmlAttribute } from '@security/escape';\nimport { isSafeUrl } from '@security/url-validator';\nimport type { NodeRenderer } from '../registry';\nimport { asRecord, pickRelationLabel, sanitizeIdent, type RelationShape } from '../value-shapes';\n\nconst relationshipRenderer: NodeRenderer = (node): string => {\n const relationTo = typeof node['relationTo'] === 'string' ? node['relationTo'] : '';\n const value = asRecord(node['value']) as RelationShape | undefined;\n const label = escapeHtml((value && pickRelationLabel(value)) ?? `#${relationTo}`);\n const slug = sanitizeIdent(relationTo);\n const classAttr =\n slug === '' ? ' class=\"lp-relation\"' : ` class=\"lp-relation lp-relation--${slug}\"`;\n const url = value?.url;\n if (typeof url === 'string' && isSafeUrl(url)) {\n return `<a href=\"${escapeHtmlAttribute(url)}\"${classAttr}>${label}</a>`;\n }\n return `<span${classAttr}>${label}</span>`;\n};\n\nexport { relationshipRenderer };\n","/** Block-renderer registry keyed by Payload block slug; consulted by the `block` and `inlineBlock` nodes. */\n\nimport type { LexicalNode } from '../types';\n\nexport interface BlockRenderContext {\n /** Render nested Lexical content through the node registry. */\n readonly renderChildren: (children: readonly LexicalNode[]) => string;\n}\n\n/** Receives the block's `fields` (with `blockType`) and returns HTML. */\nexport type BlockRenderer = (\n fields: Record<string, unknown>,\n context: BlockRenderContext,\n) => string;\n\nconst registry = new Map<string, BlockRenderer>();\n\n/** Register or replace the renderer for `blockType`. */\nexport function registerBlockRenderer(blockType: string, renderer: BlockRenderer): void {\n registry.set(blockType, renderer);\n}\n\n/** @internal */\nexport function lookupBlockRenderer(blockType: string): BlockRenderer | undefined {\n return registry.get(blockType);\n}\n\n/** @internal */\nexport function registeredBlockTypes(): readonly string[] {\n return [...registry.keys()];\n}\n\n/** Test-only: clear the registry. */\nexport function __resetBlockRegistryForTests(): void {\n registry.clear();\n}\n","/**\n * `block` and `inlineBlock` renderers for Payload's BlocksFeature. The block\n * registry is consulted first; without a renderer for the slug the node becomes\n * an empty, class-tagged element a consumer can style or replace.\n *\n * That element is a placeholder, not a rendering. `UNRENDERED_BLOCK_SELECTOR`\n * names it so the write path can keep the markup the server already rendered\n * for the block in its place, instead of replacing a figure and its image with\n * an empty div (LP0410). Whether that succeeds is decided there, not here: this\n * renderer only tells the context which slug it had no renderer for, and the\n * write says what became of it (LP0410 kept, LP0413 lost).\n */\n\nimport { lookupBlockRenderer } from '../blocks/registry';\nimport type { NodeRenderer, RenderNodeContext } from '../registry';\nimport type { LexicalNode } from '../types';\nimport { asRecord, sanitizeIdent } from '../value-shapes';\n\n/**\n * The placeholders above, in the rendered tree. Both are empty by construction,\n * and a registered renderer's output is not: it carries the block's content and\n * its own class (`lp-block-callout`, …).\n */\nexport const UNRENDERED_BLOCK_SELECTOR = '.lp-block:empty,.lp-inline-block:empty';\n\nfunction renderBlockNode(\n node: LexicalNode,\n ctx: RenderNodeContext,\n tag: 'div' | 'span',\n baseClass: string,\n): string {\n const fields = asRecord(node['fields']) ?? {};\n const blockType = typeof fields['blockType'] === 'string' ? fields['blockType'] : '';\n const slug = sanitizeIdent(blockType);\n const classes = slug === '' ? baseClass : `${baseClass} ${baseClass}--${slug}`;\n if (blockType !== '') {\n const custom = lookupBlockRenderer(blockType) ?? lookupBlockRenderer(slug);\n if (custom) return custom(fields, { renderChildren: ctx.renderChildren });\n // The real slug is known only here — the class carries `sanitizeIdent(slug)`,\n // and a hint that names the wrong one to register is no hint.\n ctx.onUnrenderedBlock?.(blockType, classes, fields);\n }\n return `<${tag} class=\"${classes}\"></${tag}>`;\n}\n\nconst blockRenderer: NodeRenderer = (node, ctx) => renderBlockNode(node, ctx, 'div', 'lp-block');\n\nconst inlineBlockRenderer: NodeRenderer = (node, ctx) =>\n renderBlockNode(node, ctx, 'span', 'lp-inline-block');\n\nexport { blockRenderer, inlineBlockRenderer };\n","/** `table` / `tablerow` / `tablecell` renderers for the `@lexical/table` shapes Payload's table feature emits. */\n\nimport type { NodeRenderer } from '../registry';\nimport { dirAttribute } from '../utils';\n\n// `@lexical/table` TableCellHeaderStates: ROW marks a cell in a header row, COLUMN one in a header column.\nconst HEADER_ROW = 1;\nconst HEADER_COLUMN = 2;\n\nconst tableRenderer: NodeRenderer = (node, ctx) =>\n `<table${dirAttribute(node)}><tbody>${ctx.renderChildren(node.children ?? [])}</tbody></table>`;\n\nconst tableRowRenderer: NodeRenderer = (node, ctx) =>\n `<tr>${ctx.renderChildren(node.children ?? [])}</tr>`;\n\nconst tableCellRenderer: NodeRenderer = (node, ctx): string => {\n const header = typeof node['headerState'] === 'number' ? node['headerState'] : 0;\n const tag = (header & (HEADER_ROW | HEADER_COLUMN)) === 0 ? 'td' : 'th';\n const scope =\n header === HEADER_ROW ? ' scope=\"col\"' : header === HEADER_COLUMN ? ' scope=\"row\"' : '';\n return `<${tag}${scope}${spanAttribute('colspan', node['colSpan'])}${spanAttribute('rowspan', node['rowSpan'])}>${ctx.renderChildren(node.children ?? [])}</${tag}>`;\n};\n\nfunction spanAttribute(name: string, value: unknown): string {\n return typeof value === 'number' && value > 1 ? ` ${name}=\"${String(value)}\"` : '';\n}\n\nexport { tableRenderer, tableRowRenderer, tableCellRenderer };\n","import type { NodeRenderer } from '../registry';\nimport { textRenderer } from './text';\nimport { paragraphRenderer } from './paragraph';\nimport { headingRenderer } from './heading';\nimport { listRenderer, listItemRenderer } from './list';\nimport { linkRenderer } from './link';\nimport { quoteRenderer } from './quote';\nimport { codeRenderer, codeHighlightRenderer } from './code';\nimport { linebreakRenderer, horizontalRuleRenderer, tabRenderer } from './linebreak';\nimport { uploadRenderer } from './upload';\nimport { relationshipRenderer } from './relationship';\nimport { blockRenderer, inlineBlockRenderer } from './block';\nimport { tableCellRenderer, tableRenderer, tableRowRenderer } from './table';\n\n// A value table instead of `register()` calls at import time: a consumer that\n// never renders Lexical content must not carry these under `sideEffects: false`.\nexport const BUILTIN_NODE_RENDERERS: Readonly<Record<string, NodeRenderer>> = {\n text: textRenderer,\n paragraph: paragraphRenderer,\n heading: headingRenderer,\n list: listRenderer,\n listitem: listItemRenderer,\n link: linkRenderer,\n autolink: linkRenderer,\n quote: quoteRenderer,\n code: codeRenderer,\n 'code-highlight': codeHighlightRenderer,\n linebreak: linebreakRenderer,\n horizontalrule: horizontalRuleRenderer,\n tab: tabRenderer,\n upload: uploadRenderer,\n relationship: relationshipRenderer,\n block: blockRenderer,\n inlineBlock: inlineBlockRenderer,\n table: tableRenderer,\n tablerow: tableRowRenderer,\n tablecell: tableCellRenderer,\n};\n","/** `text` renderer. Format flags wrap in a fixed order so equal input gives byte-equal output. */\n\nimport { escapeHtml } from '@security/escape';\nimport type { LexicalNode } from '../types';\nimport { TextFormat } from '../types';\nimport type { NodeRenderer } from '../registry';\n\nconst textRenderer: NodeRenderer = (node: LexicalNode): string => {\n const raw = typeof node.text === 'string' ? node.text : '';\n let out = escapeHtml(raw);\n const format = typeof node.format === 'number' ? node.format : 0;\n if ((format & TextFormat.CODE) !== 0) out = `<code>${out}</code>`;\n if ((format & TextFormat.BOLD) !== 0) out = `<strong>${out}</strong>`;\n if ((format & TextFormat.ITALIC) !== 0) out = `<em>${out}</em>`;\n if ((format & TextFormat.UNDERLINE) !== 0) out = `<u>${out}</u>`;\n if ((format & TextFormat.STRIKETHROUGH) !== 0) out = `<s>${out}</s>`;\n if ((format & TextFormat.SUBSCRIPT) !== 0) out = `<sub>${out}</sub>`;\n if ((format & TextFormat.SUPERSCRIPT) !== 0) out = `<sup>${out}</sup>`;\n if ((format & TextFormat.HIGHLIGHT) !== 0) out = `<mark>${out}</mark>`;\n return out;\n};\n\nexport { textRenderer };\n","/** `list` / `listitem` renderers: `bullet` → `<ul>`, `number` → `<ol>`, `check` → `<ul role=\"list\">` with `aria-checked` items. */\n\nimport { escapeHtml } from '@security/escape';\nimport type { NodeRenderer } from '../registry';\nimport { dirAttribute, layoutClassAttribute } from '../utils';\n\nconst listRenderer: NodeRenderer = (node, ctx): string => {\n const listType = typeof node['listType'] === 'string' ? node['listType'] : 'bullet';\n const tag = listType === 'number' ? 'ol' : 'ul';\n const startAttr =\n listType === 'number' && typeof node['start'] === 'number' && node['start'] > 1\n ? ` start=\"${String(node['start'])}\"`\n : '';\n const checkAttr = listType === 'check' ? ' role=\"list\"' : '';\n const children = ctx.renderChildren(node.children ?? []);\n return `<${tag}${dirAttribute(node)}${layoutClassAttribute(node)}${startAttr}${checkAttr}>${children}</${tag}>`;\n};\n\nconst listItemRenderer: NodeRenderer = (node, ctx): string => {\n const children = ctx.renderChildren(node.children ?? []);\n if (typeof node['checked'] === 'boolean') {\n const state = node['checked'] ? 'true' : 'false';\n return `<li role=\"checkbox\" aria-checked=\"${state}\"${layoutClassAttribute(node)}>${children}</li>`;\n }\n const valueAttr =\n typeof node['value'] === 'number' ? ` value=\"${escapeHtml(String(node['value']))}\"` : '';\n return `<li${dirAttribute(node)}${layoutClassAttribute(node)}${valueAttr}>${children}</li>`;\n};\n\nexport { listRenderer, listItemRenderer };\n","import type { NodeRenderer } from '../registry';\nimport { dirAttribute, layoutClassAttribute } from '../utils';\n\nconst quoteRenderer: NodeRenderer = (node, ctx): string => {\n const children = ctx.renderChildren(node.children ?? []);\n return `<blockquote${dirAttribute(node)}${layoutClassAttribute(node)}>${children}</blockquote>`;\n};\n\nexport { quoteRenderer };\n","import type { NodeRenderer } from '../registry';\n\nconst linebreakRenderer: NodeRenderer = () => '<br>';\nconst horizontalRuleRenderer: NodeRenderer = () => '<hr>';\nconst tabRenderer: NodeRenderer = () => '\\t';\n\nexport { linebreakRenderer, horizontalRuleRenderer, tabRenderer };\n","/** Node-renderer registry: consumer registrations layered over the built-in table. */\n\nimport type { LexicalNode } from './types';\nimport { BUILTIN_NODE_RENDERERS } from './nodes/builtin';\n\nexport interface RenderNodeContext {\n /** Render child nodes through the registry. */\n readonly renderChildren: (children: readonly LexicalNode[]) => string;\n /** Text alignment of a block node, or `undefined`. */\n readonly resolveAlignment: (node: LexicalNode) => string | undefined;\n /** Indent level of a node, `0` when none. */\n readonly resolveIndent: (node: LexicalNode) => number;\n /**\n * Where a `block` or `inlineBlock` without a renderer is reported, with the\n * slug Payload sent and the class of the placeholder written in its place.\n * `undefined` on a plain `lexicalToHtml()` call; the `richText` write sets it.\n */\n readonly onUnrenderedBlock?:\n ((blockType: string, placeholderClass: string, fields: unknown) => void) | undefined;\n /** The route of an internal link, with `{slug}`, `{id}`, `{collection}` and `{locale}`. */\n readonly internalLinkPattern?: string | undefined;\n /** What `{locale}` reads. */\n readonly locale?: string | undefined;\n}\n\nexport type NodeRenderer = (node: LexicalNode, context: RenderNodeContext) => string;\n\nconst registry = new Map<string, NodeRenderer>();\n\n/** Register or replace the renderer for `type`. */\nexport function register(type: string, renderer: NodeRenderer): void {\n registry.set(type, renderer);\n}\n\n/** @internal */\nexport function lookup(type: string): NodeRenderer | undefined {\n return registry.get(type) ?? BUILTIN_NODE_RENDERERS[type];\n}\n\nexport function registeredTypes(): readonly string[] {\n return [...new Set([...Object.keys(BUILTIN_NODE_RENDERERS), ...registry.keys()])];\n}\n\n/** Test-only: drop consumer registrations. */\nexport function __resetForTests(): void {\n registry.clear();\n}\n","/**\n * Lexical → HTML. Each node dispatches to the renderer registered for its\n * `type`; an unknown node renders its children so content is never lost.\n */\n\nimport { sanitizeHtml, hasSanitizerDocument, type SanitizerDocument } from '@security/sanitizer';\nimport { lookup, type RenderNodeContext } from './registry';\nimport type { LexicalNode, LexicalRoot } from './types';\nimport { resolveAlignment, resolveIndent } from './utils';\n\nexport interface LexicalRenderOptions {\n /** Pass the result through `sanitizeHtml()` (default `true`). */\n readonly sanitize?: boolean;\n /**\n * The DOM the sanitizer parses with, for this call: an SSR document such as\n * linkedom's or jsdom's. Without it the document `setSanitizerDocument()`\n * supplied is used, then the global one; with neither the result is\n * unsanitised and warns once.\n */\n readonly document?: SanitizerDocument;\n /**\n * Throw instead of returning unsanitised HTML when no document is available:\n * the `SanitizerEnvironmentError` `sanitizeHtml()` throws (ADR 0025). Off in\n * 2.x, where the call warns once and returns the markup; the default in 3.0.\n */\n readonly requireDocument?: boolean;\n /**\n * Called for every `block` or `inlineBlock` the registry has no renderer\n * for, with the block's slug as Payload sent it, the class its empty\n * placeholder carries and the block's fields. The `richText` write listens: it is the one caller\n * that later knows whether the server's markup for the block survived, so\n * the diagnostic is spoken there (LP0410, LP0413), not here.\n */\n readonly onUnrenderedBlock?: (\n blockType: string,\n placeholderClass: string,\n fields: unknown,\n ) => void;\n /**\n * The route of an internal link whose document has no `url` of its own, such\n * as `/{locale}/{slug}/`. `{slug}`, `{id}`, `{collection}` and `{locale}` are\n * filled in and encoded; with one of them empty the link falls back to `/<slug>`.\n */\n readonly internalLinkPattern?: string | undefined;\n /** What `{locale}` reads in `internalLinkPattern`. */\n readonly locale?: string | undefined;\n}\n\nlet warnedNoSanitizer = false;\n\n/** @internal */\nexport function isLexicalContent(value: unknown): value is LexicalRoot {\n if (typeof value !== 'object' || value === null) return false;\n if (!('root' in value)) return false;\n const root = value.root;\n if (typeof root !== 'object' || root === null) return false;\n if (!('children' in root)) return false;\n return Array.isArray(root.children);\n}\n\n/** Render a Lexical document to HTML; without a document the result is unsanitised and warns once, or throws under `requireDocument`. */\nexport function lexicalToHtml(content: LexicalRoot, options: LexicalRenderOptions = {}): string {\n if (!isLexicalContent(content)) return '';\n const html = createContext(options).renderChildren(content.root.children);\n if (options.sanitize === false) return html;\n if (options.document !== undefined) return sanitizeHtml(html, { document: options.document });\n // Without a DOM this throws the sanitizer's own error.\n if (options.requireDocument === true || hasSanitizerDocument()) return sanitizeHtml(html);\n warnNoSanitizerOnce();\n return html;\n}\n\n/** Plain text of a document, paragraphs separated by `\\n`. */\nexport function lexicalToPlainText(content: LexicalRoot): string {\n if (!isLexicalContent(content)) return '';\n return content.root.children.map(extractPlainText).join('\\n').trim();\n}\n\n/** Test-only: allow the SSR warning to fire again. */\nexport function __resetSanitizerWarningForTests(): void {\n warnedNoSanitizer = false;\n}\n\n/**\n * One context per document: `renderChildren` closes over it so a nested\n * block — a `block` inside a list item, an `inlineBlock` inside a paragraph —\n * reports to the same listener as a top-level one.\n */\nfunction createContext(options: LexicalRenderOptions): RenderNodeContext {\n const context: RenderNodeContext = {\n renderChildren: (children) => {\n let out = '';\n for (const child of children) out += renderNode(child, context);\n return out;\n },\n resolveAlignment,\n resolveIndent,\n onUnrenderedBlock: options.onUnrenderedBlock,\n internalLinkPattern: options.internalLinkPattern,\n locale: options.locale,\n };\n return context;\n}\n\nfunction renderNode(node: LexicalNode, context: RenderNodeContext): string {\n const renderer = lookup(node.type);\n if (renderer) return renderer(node, context);\n return node.children !== undefined ? context.renderChildren(node.children) : '';\n}\n\nfunction extractPlainText(node: LexicalNode): string {\n if (typeof node.text === 'string') return node.text;\n if (node.type === 'linebreak') return '\\n';\n if (node.children === undefined) return '';\n let out = '';\n for (const child of node.children) out += extractPlainText(child);\n return out;\n}\n\n// This layer may not import `@core/diagnostics` (architecture policy), so the\n// warn-once lives here; a hostile or absent console must not break rendering.\nfunction warnNoSanitizerOnce(): void {\n if (warnedNoSanitizer) return;\n warnedNoSanitizer = true;\n try {\n console.warn(\n '[live-preview] lexicalToHtml() has no sanitizer document and returned unsanitised HTML; ' +\n 'pass { document } (linkedom, jsdom) per call. 3.0 throws here instead: opt in now with ' +\n '{ requireDocument: true }. See \"HTML sanitization\" in docs/security.md.',\n );\n } catch {\n // Diagnostics never become a second failure.\n }\n}\n","/**\n * Opt-in renderers for the block patterns most Payload sites ship (callout,\n * image, video, code, cta). Each reads well-known field names only, escapes\n * every value, validates URLs and renders nothing when a required field is\n * missing. Register with `registerDefaultBlocks()`.\n */\n\nimport { escapeHtml, escapeHtmlAttribute } from '@security/escape';\nimport { isExternalHttpUrl, isSafeUrl } from '@security/url-validator';\nimport type { LexicalNode } from '../types';\nimport { readMedia, sanitizeIdent, type MediaShape } from '../value-shapes';\nimport { registerBlockRenderer, type BlockRenderer } from './registry';\n\nfunction str(fields: Record<string, unknown>, key: string): string | undefined {\n const value = fields[key];\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction safeUrlOrUndefined(fields: Record<string, unknown>, key: string): string | undefined {\n const raw = str(fields, key);\n return raw !== undefined && isSafeUrl(raw) ? raw : undefined;\n}\n\nfunction captionHtml(fields: Record<string, unknown>): string {\n const caption = str(fields, 'caption');\n return caption !== undefined ? `<figcaption>${escapeHtml(caption)}</figcaption>` : '';\n}\n\nfunction dimensionAttributes(media: MediaShape | undefined): string {\n const width = typeof media?.width === 'number' ? ` width=\"${String(media.width)}\"` : '';\n const height = typeof media?.height === 'number' ? ` height=\"${String(media.height)}\"` : '';\n return `${width}${height}`;\n}\n\n/** Fields: `text` or a Lexical `body`/`content`, optional `title`, `importance` (default `info`). */\nconst calloutRenderer: BlockRenderer = (fields, ctx) => {\n const importance = sanitizeIdent(str(fields, 'importance') ?? 'info');\n const title = str(fields, 'title');\n const text = str(fields, 'text');\n const richText = readLexicalChildren(fields);\n\n const titleHtml = title !== undefined ? `<strong>${escapeHtml(title)}</strong>` : '';\n const bodyHtml =\n richText !== undefined\n ? ctx.renderChildren(richText)\n : text !== undefined\n ? `<p>${escapeHtml(text)}</p>`\n : '';\n return `<aside class=\"lp-block-callout lp-block-callout--${importance}\">${titleHtml}${bodyHtml}</aside>`;\n};\n\n/** Fields: `image` (media) or `imageUrl`, `alt`, optional `caption`. */\nconst imageBlockRenderer: BlockRenderer = (fields) => {\n const media = readMedia(fields['image']);\n const url = media?.url ?? safeUrlOrUndefined(fields, 'imageUrl');\n if (url === undefined || !isSafeUrl(url)) return '';\n const alt = media?.alt ?? str(fields, 'alt') ?? '';\n return `<figure class=\"lp-block-image\"><img src=\"${escapeHtmlAttribute(url)}\" alt=\"${escapeHtmlAttribute(alt)}\"${dimensionAttributes(media)} loading=\"lazy\" decoding=\"async\">${captionHtml(fields)}</figure>`;\n};\n\n/** Fields: `video` (media) or `videoUrl`, `mimeType`, optional `poster` and `caption`. */\nconst videoBlockRenderer: BlockRenderer = (fields) => {\n const media = readMedia(fields['video']);\n const url = media?.url ?? safeUrlOrUndefined(fields, 'videoUrl');\n if (url === undefined || !isSafeUrl(url)) return '';\n const mime = media?.mimeType ?? str(fields, 'mimeType');\n const typeAttr = mime !== undefined ? ` type=\"${escapeHtmlAttribute(mime)}\"` : '';\n const poster = safeUrlOrUndefined(fields, 'poster');\n const posterAttr = poster !== undefined ? ` poster=\"${escapeHtmlAttribute(poster)}\"` : '';\n return `<figure class=\"lp-block-video\"><video controls${posterAttr}><source src=\"${escapeHtmlAttribute(url)}\"${typeAttr}></video>${captionHtml(fields)}</figure>`;\n};\n\n/** Fields: `code` or `content`, optional `language` and `caption`. */\nconst codeBlockRenderer: BlockRenderer = (fields) => {\n const code = str(fields, 'code') ?? str(fields, 'content');\n if (code === undefined) return '';\n const language = sanitizeIdent(str(fields, 'language') ?? '');\n const langClass = language === '' ? '' : ` class=\"language-${language}\"`;\n return `<figure class=\"lp-block-code\">${captionHtml(fields)}<pre><code${langClass}>${escapeHtml(code)}</code></pre></figure>`;\n};\n\n/** Fields: `label`/`text` and `href`/`url`; optional `lead`, `secondaryLabel`, `secondaryHref`. */\nconst ctaBlockRenderer: BlockRenderer = (fields) => {\n const label = str(fields, 'label') ?? str(fields, 'text');\n const href = safeUrlOrUndefined(fields, 'href') ?? safeUrlOrUndefined(fields, 'url');\n if (label === undefined || href === undefined) return '';\n const lead = str(fields, 'lead');\n const leadHtml =\n lead !== undefined ? `<p class=\"lp-block-cta__lead\">${escapeHtml(lead)}</p>` : '';\n\n const buttons = [renderCtaAnchor(label, href, true)];\n const secondaryLabel = str(fields, 'secondaryLabel');\n const secondaryHref = safeUrlOrUndefined(fields, 'secondaryHref');\n if (secondaryLabel !== undefined && secondaryHref !== undefined) {\n buttons.push(renderCtaAnchor(secondaryLabel, secondaryHref, false));\n }\n return `<div class=\"lp-block-cta\">${leadHtml}<div class=\"lp-block-cta__actions\">${buttons.join('')}</div></div>`;\n};\n\nfunction renderCtaAnchor(label: string, href: string, primary: boolean): string {\n const targetAttr = isExternalHttpUrl(href) ? ' target=\"_blank\" rel=\"noopener noreferrer\"' : '';\n const variant = primary ? 'primary' : 'secondary';\n return `<a class=\"lp-block-cta__button lp-block-cta__button--${variant}\" href=\"${escapeHtmlAttribute(href)}\"${targetAttr}>${escapeHtml(label)}</a>`;\n}\n\nfunction readLexicalChildren(fields: Record<string, unknown>): readonly LexicalNode[] | undefined {\n const body = fields['body'] ?? fields['content'];\n if (typeof body !== 'object' || body === null) return undefined;\n const root = (body as { root?: { children?: unknown } }).root;\n if (!root || !Array.isArray(root.children)) return undefined;\n return root.children as readonly LexicalNode[];\n}\n\n/** Register every default block renderer under its kebab-, camel- and short slug. Idempotent. */\nexport function registerDefaultBlocks(): void {\n registerBlockRenderer('callout', calloutRenderer);\n registerBlockRenderer('image-block', imageBlockRenderer);\n registerBlockRenderer('imageBlock', imageBlockRenderer);\n registerBlockRenderer('image', imageBlockRenderer);\n registerBlockRenderer('video-block', videoBlockRenderer);\n registerBlockRenderer('videoBlock', videoBlockRenderer);\n registerBlockRenderer('video', videoBlockRenderer);\n registerBlockRenderer('code-block', codeBlockRenderer);\n registerBlockRenderer('codeBlock', codeBlockRenderer);\n registerBlockRenderer('code', codeBlockRenderer);\n registerBlockRenderer('cta-block', ctaBlockRenderer);\n registerBlockRenderer('ctaBlock', ctaBlockRenderer);\n registerBlockRenderer('cta', ctaBlockRenderer);\n}\n\nexport {\n calloutRenderer,\n imageBlockRenderer,\n videoBlockRenderer,\n codeBlockRenderer,\n ctaBlockRenderer,\n};\n"],"mappings":"AAoBO,SAASA,EAASC,EAAaC,GACpC,IAbK,WACL,IACE,MAAMC,EAAOC,WAA0EC,SACnFF,IACJ,YAAY,IAARA,GACuB,eAApBA,EAAc,QACvB,OACE,OAAO,CACT,CACF,CAIOG,GAAwB,OAC7B,MAAMC,EAASH,WACTI,EAAUD,EAAiB,4DAAUE,IAC3C,IAAID,EAAOE,IAAIT,GAAf,CACAO,EAAOG,IAAIV,GACX,IACEW,QAAQC,KAAK,0BAA0BX,IACzC,OAEA,CANqB,CAOvB,CCZO,IAODY,iBAAsBC,OAAOC,IAAI,sCAmDhC,SAASC,EAAYC,GAC1B,MAAMC,EApBR,WACE,MAAMC,EAzBR,WACE,MAAMC,EAAQjB,WACRkB,EAAWD,EAAMP,GACvB,QAAiB,IAAbQ,EAAwB,OAAOA,EACnC,MAAMC,EAA0B,GAEhC,OADAF,EAAMP,GAAQS,EACPA,CACT,CAkBmBC,GACjB,QAA0B,IAAtBJ,EAASK,SAAwB,OAAOL,EAASK,SACrD,QAAsB,IAAlBL,EAASM,KAAoB,OAAON,EAASM,KACjD,MAAMC,EAXR,WACE,MAAMC,EAAaxB,WAA0CyB,aAC7D,GAAyB,iBAAdD,GAAwC,OAAdA,EAErC,MAAyB,mBADTA,EAAyCE,aAClBF,OAAwC,CACjF,CAMcG,GACZ,IACEX,EAASM,UACC,IAARC,EACI,KACAA,EAAIG,aAhD2B,uBAgDa,CAAEE,WAAaC,GAAUA,GAC7E,OAGEb,EAASM,KAAO,IAClB,CACA,OAAON,EAASM,IAClB,CAIiBQ,GACf,OAAe,OAAXf,EAAwBD,EACrBC,EAAOa,WAAWd,EAC3B,CC5EA,IAAMiB,iBAAA,IAA0C1B,IAAI,CAAC,QAAS,SAAU,UAAW,SAE7E2B,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,OAAOR,EAAezB,IAAIuC,EAAOE,SACnC,OACE,OAAOf,EAAcY,KAAKL,KAjB9B,SAA4BS,GAC1B,MAAMC,EAASb,EAAOc,KAAKF,EAAMP,QAAQN,EAAa,OAAO,GAC7D,YAAkB,IAAXc,IAAyBlB,EAAezB,IAAI,GAAG2C,EAAOE,iBAC/D,CAc2CC,CAAmBb,EAC5D,CACF,CAGO,SAASc,kBAAkBf,GAChC,MAAMC,EAAUD,EAAIE,OAAOC,QAAQP,EAAgB,IACnD,MAAO,gBAAgBU,KAAKL,IAAYN,EAAkBW,KAAKL,EACjE,CCzBA,IAAMe,iBAAA,IAAejD,IA4Bd,SAASkD,EAAuBC,GACjCF,EAAShD,IAAIkD,KACjBF,EAAS/C,IAAIiD,GAtCf,SAAc1D,GACZ,IACEU,QAAQC,KAAKX,EACf,OAEA,CACF,CAiCEW,CACE,yDAAyD+C,qFA3B7D,SAAgBA,GACd,OAAIA,EAAKb,WAHY,iBAKjB,0IAIS,OAATa,GAA0B,SAATA,EAEjB,+HAKF,8GAGJ,CAWkEC,CAAOD,OAEzE,CCpDA,IAAME,iBAAA,IAAwCrD,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,QAIIsD,iBAAA,IAAyCtD,IAAI,CACjD,QACA,SACA,WACA,SACA,SACA,UAIIuD,iBAAA,IAA6CvD,IAAI,CACrD,SACA,QACA,SACA,SACA,QACA,OACA,OACA,OACA,OACA,QACA,SACA,SACA,WACA,MACA,OACA,WACA,QACA,WACA,WACA,a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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"lexical.js","names":["warnOnce","key","message","env","globalThis","process","isDevelopmentProcess","holder","warned","Set","has","add","console","warn","SLOT","Symbol","for","trustedHtml","html","policy","policies","realm","existing","created","shared","override","auto","api","candidate","trustedTypes","createPolicy","factory","createHTML","input","resolvePolicy","SAFE_PROTOCOLS","RELATIVE_PATH","PROTOCOL_RELATIVE","PARSER_IGNORED","SCHEME_GAPS","SCHEME","isSafeUrl","url","trimmed","trim","replace","length","startsWith","test","parsed","URL","protocol","value","scheme","exec","toLowerCase","spellsUnsafeScheme","isExternalHttpUrl","reported","reportDroppedAttribute","name","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","setSanitizerDocument","doc","sanitizeHtml","options","document","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","String","sanitizeHtmlWithPolicy","__INLINE_BUILD__","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","hasAttribute","hardenAnchor","HTML_ESCAPES","freeze","HTML_ESCAPE_PATTERN","escapeHtml","text","char","ATTR_ESCAPES","ATTR_ESCAPE_PATTERN","escapeHtmlAttribute","TextFormat","BOLD","ITALIC","STRIKETHROUGH","UNDERLINE","CODE","SUBSCRIPT","SUPERSCRIPT","HIGHLIGHT","ALIGNMENT_NUMERIC","ALIGNMENT_STRING","resolveAlignment","fmt","format","resolveIndent","indent","dirAttribute","direction","layoutClassAttribute","align","classes","push","join","VALID_TAGS","asRecord","readMedia","sanitizeIdent","nonEmpty","PLACEHOLDER","BLANK_TARGET","linkRenderer","ctx","children","renderChildren","fields","relation","patterned","internalLinkPattern","pattern","parts","read","part","encodeURIComponent","matchAll","_match","fillPattern","slug","id","collection","locale","internalUrl","resolveUrl","titleAttr","target","targetAttribute","extractText","out","dimensionAttributes","width","height","typeAttribute","mimeType","registry","registerBlockRenderer","blockType","renderer","lookupBlockRenderer","registeredBlockTypes","keys","renderBlockNode","baseClass","custom","onUnrenderedBlock","spanAttribute","BUILTIN_NODE_RENDERERS","paragraph","heading","tagRaw","list","listType","startAttr","checkAttr","listitem","valueAttr","link","autolink","quote","language","linebreak","horizontalrule","tab","upload","mime","renderVideo","renderAudio","alt","renderImage","label","filename","renderFallbackLink","relationship","relationTo","title","pickRelationLabel","classAttr","block","inlineBlock","tablerow","tablecell","header","register","type","lookup","warnedNoSanitizer","isLexicalContent","root","isArray","lexicalToHtml","context","renderNode","createContext","sanitize","requireDocument","warnNoSanitizerOnce","lexicalToPlainText","extractPlainText","str","safeUrlOrUndefined","raw","captionHtml","caption","calloutRenderer","importance","richText","body","readLexicalChildren","imageBlockRenderer","media","videoBlockRenderer","typeAttr","poster","codeBlockRenderer","langClass","ctaBlockRenderer","lead","leadHtml","buttons","renderCtaAnchor","secondaryLabel","secondaryHref","primary","targetAttr","registerDefaultBlocks"],"sources":["../src/types/dev-warning.ts","../src/security/trusted-types.ts","../src/security/url-validator.ts","../src/security/sanitizer-report.ts","../src/security/sanitizer.ts","../src/security/escape.ts","../src/lexical/types.ts","../src/lexical/utils.ts","../src/lexical/nodes/paragraph.ts","../src/lexical/nodes/heading.ts","../src/lexical/value-shapes.ts","../src/lexical/nodes/link.ts","../src/lexical/nodes/code.ts","../src/lexical/nodes/upload.ts","../src/lexical/nodes/relationship.ts","../src/lexical/blocks/registry.ts","../src/lexical/nodes/block.ts","../src/lexical/nodes/table.ts","../src/lexical/nodes/builtin.ts","../src/lexical/nodes/text.ts","../src/lexical/nodes/list.ts","../src/lexical/nodes/quote.ts","../src/lexical/nodes/linebreak.ts","../src/lexical/registry.ts","../src/lexical/render.ts","../src/lexical/blocks/defaults.ts"],"sourcesContent":["/**\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","/**\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","/** 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 * Lexical document types. Only `type` and `children` are required; every\n * other property is forwarded opaquely to the renderer for that node type.\n */\n\n/** Lexical text-format bitmask; flags combine (`BOLD | ITALIC === 3`). @internal */\nexport const TextFormat = {\n BOLD: 1,\n ITALIC: 2,\n STRIKETHROUGH: 4,\n UNDERLINE: 8,\n CODE: 16,\n SUBSCRIPT: 32,\n SUPERSCRIPT: 64,\n HIGHLIGHT: 128,\n} as const;\n\nexport interface LexicalNode {\n readonly type: string;\n readonly version?: number;\n readonly format?: number | string;\n readonly indent?: number;\n readonly direction?: 'ltr' | 'rtl' | null;\n readonly children?: readonly LexicalNode[];\n readonly text?: string;\n readonly [extra: string]: unknown;\n}\n\nexport interface LexicalRoot {\n readonly root: {\n readonly type?: string;\n readonly children: readonly LexicalNode[];\n readonly direction?: 'ltr' | 'rtl' | null;\n readonly format?: number | string;\n readonly indent?: number;\n readonly [extra: string]: unknown;\n };\n}\n","/** Attribute helpers shared by the block-level node renderers. */\n\nimport type { LexicalNode } from './types';\n\nconst ALIGNMENT_NUMERIC: Readonly<Record<number, string>> = {\n 1: 'left',\n 2: 'center',\n 3: 'right',\n 4: 'justify',\n};\n\nconst ALIGNMENT_STRING = new Set(['left', 'center', 'right', 'justify', 'start', 'end']);\n\n/** Alignment of a block node; Payload emits it as a string or as Lexical's numeric format. */\nexport function resolveAlignment(node: LexicalNode): string | undefined {\n const fmt = node.format;\n if (typeof fmt === 'string' && ALIGNMENT_STRING.has(fmt)) return fmt;\n if (typeof fmt === 'number') return ALIGNMENT_NUMERIC[fmt];\n return undefined;\n}\n\nexport function resolveIndent(node: LexicalNode): number {\n const indent = node.indent;\n return typeof indent === 'number' && indent > 0 ? indent : 0;\n}\n\nexport function dirAttribute(node: LexicalNode): string {\n if (node.direction === 'rtl') return ' dir=\"rtl\"';\n if (node.direction === 'ltr') return ' dir=\"ltr\"';\n return '';\n}\n\n/**\n * Alignment and indent as `lp-align-*` / `lp-indent-N` classes. The sanitizer\n * strips `style` under every policy, so inline CSS would never reach the page.\n */\nexport function layoutClassAttribute(node: LexicalNode): string {\n const align = resolveAlignment(node);\n const indent = resolveIndent(node);\n const classes: string[] = [];\n if (align !== undefined) classes.push(`lp-align-${align}`);\n if (indent > 0) classes.push(`lp-indent-${String(indent)}`);\n return classes.length === 0 ? '' : ` class=\"${classes.join(' ')}\"`;\n}\n","import type { NodeRenderer } from '../registry';\nimport { dirAttribute, layoutClassAttribute } from '../utils';\n\nconst paragraphRenderer: NodeRenderer = (node, ctx): string => {\n const children = ctx.renderChildren(node.children ?? []);\n return `<p${dirAttribute(node)}${layoutClassAttribute(node)}>${children}</p>`;\n};\n\nexport { paragraphRenderer };\n","/** `heading` renderer; an unknown `tag` (transient editor states emit them) falls back to `h2`. */\n\nimport type { NodeRenderer } from '../registry';\nimport { dirAttribute, layoutClassAttribute } from '../utils';\n\nconst VALID_TAGS = new Set(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']);\n\nconst headingRenderer: NodeRenderer = (node, ctx): string => {\n const tagRaw = typeof node['tag'] === 'string' ? node['tag'].toLowerCase() : 'h2';\n const tag = VALID_TAGS.has(tagRaw) ? tagRaw : 'h2';\n const children = ctx.renderChildren(node.children ?? []);\n return `<${tag}${dirAttribute(node)}${layoutClassAttribute(node)}>${children}</${tag}>`;\n};\n\nexport { headingRenderer };\n","/**\n * Value-shape helpers shared by the Lexical node renderers and the field\n * renderers. They live here because `field-types` may import `lexical`, never\n * the reverse (scripts/architecture-policy.ts).\n */\n\nexport interface MediaShape {\n readonly id?: string | number;\n readonly url?: string;\n readonly alt?: string;\n readonly filename?: string;\n readonly mimeType?: string;\n readonly width?: number;\n readonly height?: number;\n readonly sizes?: MediaSizes;\n /** Payload 4's name for `sizes`. */\n readonly variants?: MediaSizes;\n}\n\ntype MediaSizes = Readonly<\n Record<\n string,\n {\n readonly url?: string;\n readonly width?: number;\n readonly height?: number;\n readonly mimeType?: string;\n }\n >\n>;\n\nexport interface RelationShape {\n readonly id?: string | number;\n readonly title?: string;\n readonly name?: string;\n readonly slug?: string;\n readonly url?: string;\n}\n\nexport function asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nexport function readMedia(value: unknown): MediaShape | undefined {\n return asRecord(value);\n}\n\n/**\n * A media document's URL as the preview should load it. Unless its config sets\n * `serverURL`, Payload returns a path on its own origin (`/api/media/file/a.png`);\n * its admin resolves the path against the server, and the preview, framed on\n * another origin, would otherwise ask itself for the file. Only such a path is\n * Payload's to place: any other URL, and every URL when no server is known, is\n * written as delivered. A result on another origin than the server's (`/\\host`\n * is one, to the URL parser) is discarded.\n */\nexport function resolveMediaUrl(url: string, serverURL: string | undefined): string {\n if (serverURL === undefined || serverURL === '' || !url.startsWith('/')) return url;\n try {\n const server = new URL(serverURL);\n const resolved = new URL(url, server);\n return resolved.origin === server.origin ? resolved.href : url;\n } catch {\n return url;\n }\n}\n\n/** Reduce `value` to `[a-z0-9_-]` so it can be a class-name fragment without escaping. */\nexport function sanitizeIdent(value: string): string {\n return value.replace(/[^a-zA-Z0-9_-]/g, '-').toLowerCase();\n}\n\nfunction nonEmpty(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\n/** Label precedence for a populated relation: `title`, `name`, `slug`, then `id`. */\nexport function pickRelationLabel(value: RelationShape): string | undefined {\n const label = nonEmpty(value.title) ?? nonEmpty(value.name) ?? nonEmpty(value.slug);\n if (label !== undefined) return label;\n return value.id === undefined ? undefined : String(value.id);\n}\n","/**\n * `link` / `autolink` renderer. Payload 3.x serialises links as\n * `{ fields: { linkType, url, newTab, doc } }`; vanilla Lexical puts `url`\n * and `target` on the node itself. Both shapes are read, Payload's first.\n *\n * An internal link's address is a route only the site knows. The document's own\n * `url` wins, then `internalLinkPattern`, then `/<slug>`, which is all that can be\n * said without either.\n */\n\nimport { escapeHtml, escapeHtmlAttribute } from '@security/escape';\nimport { isExternalHttpUrl, isSafeUrl } from '@security/url-validator';\nimport type { NodeRenderer, RenderNodeContext } from '../registry';\nimport type { LexicalNode } from '../types';\nimport { asRecord } from '../value-shapes';\n\nconst PLACEHOLDER = /\\{(slug|id|collection|locale)\\}/gu;\nconst BLANK_TARGET = ' target=\"_blank\" rel=\"noopener noreferrer\"';\n\nconst linkRenderer: NodeRenderer = (node, ctx): string => {\n const children = ctx.renderChildren(node.children ?? []);\n const fields = asRecord(node['fields']);\n const url = resolveUrl(node, fields, ctx);\n if (url === undefined || !isSafeUrl(url)) return children;\n const titleAttr =\n typeof node['title'] === 'string' ? ` title=\"${escapeHtml(node['title'])}\"` : '';\n return `<a href=\"${escapeHtmlAttribute(url)}\"${targetAttribute(node, fields, url)}${titleAttr}>${children}</a>`;\n};\n\nfunction resolveUrl(\n node: LexicalNode,\n fields: Record<string, unknown> | undefined,\n ctx: RenderNodeContext,\n): string | undefined {\n if (fields?.['linkType'] === 'internal') return internalUrl(fields['doc'], ctx);\n const url = fields?.['url'] ?? node['url'];\n return typeof url === 'string' && url.length > 0 ? url : undefined;\n}\n\n/** Without a pattern an internal link needs the populated document; a bare id offers no route. */\nfunction internalUrl(doc: unknown, ctx: RenderNodeContext): string | undefined {\n const relation = asRecord(doc);\n const value = asRecord(relation?.['value']);\n const url = value?.['url'];\n if (typeof url === 'string' && url.length > 0) return url;\n const patterned =\n ctx.internalLinkPattern === undefined\n ? undefined\n : fillPattern(ctx.internalLinkPattern, {\n slug: value?.['slug'],\n id: value === undefined ? relation?.['value'] : value['id'],\n collection: relation?.['relationTo'],\n locale: ctx.locale,\n });\n if (patterned !== undefined) return patterned;\n const slug = value?.['slug'];\n return typeof slug === 'string' && slug.length > 0 ? `/${slug}` : undefined;\n}\n\n/** `undefined` when a placeholder the pattern names has no value: half a route is a wrong one. */\nfunction fillPattern(pattern: string, parts: Record<string, unknown>): string | undefined {\n const read = (name: string): string | undefined => {\n const part = parts[name];\n return (typeof part === 'string' && part.length > 0) || typeof part === 'number'\n ? encodeURIComponent(part)\n : undefined;\n };\n for (const [, name = ''] of pattern.matchAll(PLACEHOLDER)) {\n if (read(name) === undefined) return undefined;\n }\n return pattern.replace(PLACEHOLDER, (_match, name: string) => read(name) ?? '');\n}\n\n/** `_blank` always carries `noopener noreferrer`, whichever side asked for it. */\nfunction targetAttribute(\n node: LexicalNode,\n fields: Record<string, unknown> | undefined,\n url: string,\n): string {\n if (fields?.['newTab'] === true || isExternalHttpUrl(url)) return BLANK_TARGET;\n const target = typeof node['target'] === 'string' ? node['target'] : '';\n if (target === '') return '';\n if (target === '_blank') return BLANK_TARGET;\n return ` target=\"${escapeHtml(target)}\"`;\n}\n\nexport { linkRenderer };\n","/** `code` (fenced block) and `code-highlight` (token span) renderers. */\n\nimport { escapeHtml } from '@security/escape';\nimport type { NodeRenderer } from '../registry';\nimport type { LexicalNode } from '../types';\nimport { sanitizeIdent } from '../value-shapes';\n\nconst codeRenderer: NodeRenderer = (node): string => {\n const language = sanitizeIdent(typeof node['language'] === 'string' ? node['language'] : '');\n const langClass = language === '' ? '' : ` class=\"language-${language}\"`;\n return `<pre><code${langClass}>${escapeHtml(extractText(node))}</code></pre>`;\n};\n\nconst codeHighlightRenderer: NodeRenderer = (node): string => {\n const text = typeof node.text === 'string' ? node.text : '';\n const highlight =\n typeof node['highlightType'] === 'string'\n ? ` class=\"token-${sanitizeIdent(node['highlightType'])}\"`\n : '';\n return `<span${highlight}>${escapeHtml(text)}</span>`;\n};\n\nexport { codeRenderer, codeHighlightRenderer };\n\nfunction extractText(node: LexicalNode): string {\n if (typeof node.text === 'string') return node.text;\n if (!node.children) return '';\n let out = '';\n for (const child of node.children) out += extractText(child);\n return out;\n}\n","/**\n * `upload` renderer: `<img>` for image (or unknown) MIME types, `<video>` /\n * `<audio>` for media, a download link otherwise. An unsafe URL renders nothing.\n */\n\nimport { escapeHtml, escapeHtmlAttribute } from '@security/escape';\nimport { isSafeUrl } from '@security/url-validator';\nimport type { NodeRenderer } from '../registry';\nimport { readMedia, type MediaShape } from '../value-shapes';\n\nconst uploadRenderer: NodeRenderer = (node): string => {\n const value = readMedia(node['value']);\n if (value === undefined) return '';\n const url = value.url;\n if (typeof url !== 'string' || !isSafeUrl(url)) return '';\n\n const mime = typeof value.mimeType === 'string' ? value.mimeType : '';\n if (mime.startsWith('video/')) return renderVideo(value, url);\n if (mime.startsWith('audio/')) return renderAudio(value, url);\n if (mime === '' || mime.startsWith('image/')) return renderImage(value, url);\n return renderFallbackLink(value, url);\n};\n\nexport { uploadRenderer };\n\nfunction dimensionAttributes(value: MediaShape): string {\n const width = typeof value.width === 'number' ? ` width=\"${String(value.width)}\"` : '';\n const height = typeof value.height === 'number' ? ` height=\"${String(value.height)}\"` : '';\n return `${width}${height}`;\n}\n\nfunction typeAttribute(value: MediaShape): string {\n return typeof value.mimeType === 'string' ? ` type=\"${escapeHtmlAttribute(value.mimeType)}\"` : '';\n}\n\nfunction renderImage(value: MediaShape, url: string): string {\n const alt = typeof value.alt === 'string' ? escapeHtml(value.alt) : '';\n return `<img src=\"${escapeHtmlAttribute(url)}\" alt=\"${alt}\"${dimensionAttributes(value)} loading=\"lazy\" decoding=\"async\">`;\n}\n\nfunction renderVideo(value: MediaShape, url: string): string {\n return `<video controls${dimensionAttributes(value)}><source src=\"${escapeHtmlAttribute(url)}\"${typeAttribute(value)}></video>`;\n}\n\nfunction renderAudio(value: MediaShape, url: string): string {\n return `<audio controls><source src=\"${escapeHtmlAttribute(url)}\"${typeAttribute(value)}></audio>`;\n}\n\nfunction renderFallbackLink(value: MediaShape, url: string): string {\n const label = typeof value.filename === 'string' ? escapeHtml(value.filename) : escapeHtml(url);\n return `<a href=\"${escapeHtmlAttribute(url)}\" target=\"_blank\" rel=\"noopener noreferrer\">${label}</a>`;\n}\n","/** `relationship` renderer: a link when the populated document carries a URL, a span otherwise. */\n\nimport { escapeHtml, escapeHtmlAttribute } from '@security/escape';\nimport { isSafeUrl } from '@security/url-validator';\nimport type { NodeRenderer } from '../registry';\nimport { asRecord, pickRelationLabel, sanitizeIdent, type RelationShape } from '../value-shapes';\n\nconst relationshipRenderer: NodeRenderer = (node): string => {\n const relationTo = typeof node['relationTo'] === 'string' ? node['relationTo'] : '';\n const value = asRecord(node['value']) as RelationShape | undefined;\n const label = escapeHtml((value && pickRelationLabel(value)) ?? `#${relationTo}`);\n const slug = sanitizeIdent(relationTo);\n const classAttr =\n slug === '' ? ' class=\"lp-relation\"' : ` class=\"lp-relation lp-relation--${slug}\"`;\n const url = value?.url;\n if (typeof url === 'string' && isSafeUrl(url)) {\n return `<a href=\"${escapeHtmlAttribute(url)}\"${classAttr}>${label}</a>`;\n }\n return `<span${classAttr}>${label}</span>`;\n};\n\nexport { relationshipRenderer };\n","/** Block-renderer registry keyed by Payload block slug; consulted by the `block` and `inlineBlock` nodes. */\n\nimport type { LexicalNode } from '../types';\n\nexport interface BlockRenderContext {\n /** Render nested Lexical content through the node registry. */\n readonly renderChildren: (children: readonly LexicalNode[]) => string;\n}\n\n/** Receives the block's `fields` (with `blockType`) and returns HTML. */\nexport type BlockRenderer = (\n fields: Record<string, unknown>,\n context: BlockRenderContext,\n) => string;\n\nconst registry = new Map<string, BlockRenderer>();\n\n/** Register or replace the renderer for `blockType`. */\nexport function registerBlockRenderer(blockType: string, renderer: BlockRenderer): void {\n registry.set(blockType, renderer);\n}\n\n/** @internal */\nexport function lookupBlockRenderer(blockType: string): BlockRenderer | undefined {\n return registry.get(blockType);\n}\n\n/** @internal */\nexport function registeredBlockTypes(): readonly string[] {\n return [...registry.keys()];\n}\n\n/** Test-only: clear the registry. */\nexport function __resetBlockRegistryForTests(): void {\n registry.clear();\n}\n","/**\n * `block` and `inlineBlock` renderers for Payload's BlocksFeature. The block\n * registry is consulted first; without a renderer for the slug the node becomes\n * an empty, class-tagged element a consumer can style or replace.\n *\n * That element is a placeholder, not a rendering. `UNRENDERED_BLOCK_SELECTOR`\n * names it so the write path can keep the markup the server already rendered\n * for the block in its place, instead of replacing a figure and its image with\n * an empty div (LP0410). Whether that succeeds is decided there, not here: this\n * renderer only tells the context which slug it had no renderer for, and the\n * write says what became of it (LP0410 kept, LP0413 lost).\n */\n\nimport { lookupBlockRenderer } from '../blocks/registry';\nimport type { NodeRenderer, RenderNodeContext } from '../registry';\nimport type { LexicalNode } from '../types';\nimport { asRecord, sanitizeIdent } from '../value-shapes';\n\n/**\n * The placeholders above, in the rendered tree. Both are empty by construction,\n * and a registered renderer's output is not: it carries the block's content and\n * its own class (`lp-block-callout`, …).\n */\nexport const UNRENDERED_BLOCK_SELECTOR = '.lp-block:empty,.lp-inline-block:empty';\n\nfunction renderBlockNode(\n node: LexicalNode,\n ctx: RenderNodeContext,\n tag: 'div' | 'span',\n baseClass: string,\n): string {\n const fields = asRecord(node['fields']) ?? {};\n const blockType = typeof fields['blockType'] === 'string' ? fields['blockType'] : '';\n const slug = sanitizeIdent(blockType);\n const classes = slug === '' ? baseClass : `${baseClass} ${baseClass}--${slug}`;\n if (blockType !== '') {\n const custom = lookupBlockRenderer(blockType) ?? lookupBlockRenderer(slug);\n if (custom) return custom(fields, { renderChildren: ctx.renderChildren });\n // The real slug is known only here — the class carries `sanitizeIdent(slug)`,\n // and a hint that names the wrong one to register is no hint.\n ctx.onUnrenderedBlock?.(blockType, classes, fields);\n }\n return `<${tag} class=\"${classes}\"></${tag}>`;\n}\n\nconst blockRenderer: NodeRenderer = (node, ctx) => renderBlockNode(node, ctx, 'div', 'lp-block');\n\nconst inlineBlockRenderer: NodeRenderer = (node, ctx) =>\n renderBlockNode(node, ctx, 'span', 'lp-inline-block');\n\nexport { blockRenderer, inlineBlockRenderer };\n","/** `table` / `tablerow` / `tablecell` renderers for the `@lexical/table` shapes Payload's table feature emits. */\n\nimport type { NodeRenderer } from '../registry';\nimport { dirAttribute } from '../utils';\n\n// `@lexical/table` TableCellHeaderStates: ROW marks a cell in a header row, COLUMN one in a header column.\nconst HEADER_ROW = 1;\nconst HEADER_COLUMN = 2;\n\nconst tableRenderer: NodeRenderer = (node, ctx) =>\n `<table${dirAttribute(node)}><tbody>${ctx.renderChildren(node.children ?? [])}</tbody></table>`;\n\nconst tableRowRenderer: NodeRenderer = (node, ctx) =>\n `<tr>${ctx.renderChildren(node.children ?? [])}</tr>`;\n\nconst tableCellRenderer: NodeRenderer = (node, ctx): string => {\n const header = typeof node['headerState'] === 'number' ? node['headerState'] : 0;\n const tag = (header & (HEADER_ROW | HEADER_COLUMN)) === 0 ? 'td' : 'th';\n const scope =\n header === HEADER_ROW ? ' scope=\"col\"' : header === HEADER_COLUMN ? ' scope=\"row\"' : '';\n return `<${tag}${scope}${spanAttribute('colspan', node['colSpan'])}${spanAttribute('rowspan', node['rowSpan'])}>${ctx.renderChildren(node.children ?? [])}</${tag}>`;\n};\n\nfunction spanAttribute(name: string, value: unknown): string {\n return typeof value === 'number' && value > 1 ? ` ${name}=\"${String(value)}\"` : '';\n}\n\nexport { tableRenderer, tableRowRenderer, tableCellRenderer };\n","import type { NodeRenderer } from '../registry';\nimport { textRenderer } from './text';\nimport { paragraphRenderer } from './paragraph';\nimport { headingRenderer } from './heading';\nimport { listRenderer, listItemRenderer } from './list';\nimport { linkRenderer } from './link';\nimport { quoteRenderer } from './quote';\nimport { codeRenderer, codeHighlightRenderer } from './code';\nimport { linebreakRenderer, horizontalRuleRenderer, tabRenderer } from './linebreak';\nimport { uploadRenderer } from './upload';\nimport { relationshipRenderer } from './relationship';\nimport { blockRenderer, inlineBlockRenderer } from './block';\nimport { tableCellRenderer, tableRenderer, tableRowRenderer } from './table';\n\n// A value table instead of `register()` calls at import time: a consumer that\n// never renders Lexical content must not carry these under `sideEffects: false`.\nexport const BUILTIN_NODE_RENDERERS: Readonly<Record<string, NodeRenderer>> = {\n text: textRenderer,\n paragraph: paragraphRenderer,\n heading: headingRenderer,\n list: listRenderer,\n listitem: listItemRenderer,\n link: linkRenderer,\n autolink: linkRenderer,\n quote: quoteRenderer,\n code: codeRenderer,\n 'code-highlight': codeHighlightRenderer,\n linebreak: linebreakRenderer,\n horizontalrule: horizontalRuleRenderer,\n tab: tabRenderer,\n upload: uploadRenderer,\n relationship: relationshipRenderer,\n block: blockRenderer,\n inlineBlock: inlineBlockRenderer,\n table: tableRenderer,\n tablerow: tableRowRenderer,\n tablecell: tableCellRenderer,\n};\n","/** `text` renderer. Format flags wrap in a fixed order so equal input gives byte-equal output. */\n\nimport { escapeHtml } from '@security/escape';\nimport type { LexicalNode } from '../types';\nimport { TextFormat } from '../types';\nimport type { NodeRenderer } from '../registry';\n\nconst textRenderer: NodeRenderer = (node: LexicalNode): string => {\n const raw = typeof node.text === 'string' ? node.text : '';\n let out = escapeHtml(raw);\n const format = typeof node.format === 'number' ? node.format : 0;\n if ((format & TextFormat.CODE) !== 0) out = `<code>${out}</code>`;\n if ((format & TextFormat.BOLD) !== 0) out = `<strong>${out}</strong>`;\n if ((format & TextFormat.ITALIC) !== 0) out = `<em>${out}</em>`;\n if ((format & TextFormat.UNDERLINE) !== 0) out = `<u>${out}</u>`;\n if ((format & TextFormat.STRIKETHROUGH) !== 0) out = `<s>${out}</s>`;\n if ((format & TextFormat.SUBSCRIPT) !== 0) out = `<sub>${out}</sub>`;\n if ((format & TextFormat.SUPERSCRIPT) !== 0) out = `<sup>${out}</sup>`;\n if ((format & TextFormat.HIGHLIGHT) !== 0) out = `<mark>${out}</mark>`;\n return out;\n};\n\nexport { textRenderer };\n","/** `list` / `listitem` renderers: `bullet` → `<ul>`, `number` → `<ol>`, `check` → `<ul role=\"list\">` with `aria-checked` items. */\n\nimport { escapeHtml } from '@security/escape';\nimport type { NodeRenderer } from '../registry';\nimport { dirAttribute, layoutClassAttribute } from '../utils';\n\nconst listRenderer: NodeRenderer = (node, ctx): string => {\n const listType = typeof node['listType'] === 'string' ? node['listType'] : 'bullet';\n const tag = listType === 'number' ? 'ol' : 'ul';\n const startAttr =\n listType === 'number' && typeof node['start'] === 'number' && node['start'] > 1\n ? ` start=\"${String(node['start'])}\"`\n : '';\n const checkAttr = listType === 'check' ? ' role=\"list\"' : '';\n const children = ctx.renderChildren(node.children ?? []);\n return `<${tag}${dirAttribute(node)}${layoutClassAttribute(node)}${startAttr}${checkAttr}>${children}</${tag}>`;\n};\n\nconst listItemRenderer: NodeRenderer = (node, ctx): string => {\n const children = ctx.renderChildren(node.children ?? []);\n if (typeof node['checked'] === 'boolean') {\n const state = node['checked'] ? 'true' : 'false';\n return `<li role=\"checkbox\" aria-checked=\"${state}\"${layoutClassAttribute(node)}>${children}</li>`;\n }\n const valueAttr =\n typeof node['value'] === 'number' ? ` value=\"${escapeHtml(String(node['value']))}\"` : '';\n return `<li${dirAttribute(node)}${layoutClassAttribute(node)}${valueAttr}>${children}</li>`;\n};\n\nexport { listRenderer, listItemRenderer };\n","import type { NodeRenderer } from '../registry';\nimport { dirAttribute, layoutClassAttribute } from '../utils';\n\nconst quoteRenderer: NodeRenderer = (node, ctx): string => {\n const children = ctx.renderChildren(node.children ?? []);\n return `<blockquote${dirAttribute(node)}${layoutClassAttribute(node)}>${children}</blockquote>`;\n};\n\nexport { quoteRenderer };\n","import type { NodeRenderer } from '../registry';\n\nconst linebreakRenderer: NodeRenderer = () => '<br>';\nconst horizontalRuleRenderer: NodeRenderer = () => '<hr>';\nconst tabRenderer: NodeRenderer = () => '\\t';\n\nexport { linebreakRenderer, horizontalRuleRenderer, tabRenderer };\n","/** Node-renderer registry: consumer registrations layered over the built-in table. */\n\nimport type { LexicalNode } from './types';\nimport { BUILTIN_NODE_RENDERERS } from './nodes/builtin';\n\nexport interface RenderNodeContext {\n /** Render child nodes through the registry. */\n readonly renderChildren: (children: readonly LexicalNode[]) => string;\n /** Text alignment of a block node, or `undefined`. */\n readonly resolveAlignment: (node: LexicalNode) => string | undefined;\n /** Indent level of a node, `0` when none. */\n readonly resolveIndent: (node: LexicalNode) => number;\n /**\n * Where a `block` or `inlineBlock` without a renderer is reported, with the\n * slug Payload sent and the class of the placeholder written in its place.\n * `undefined` on a plain `lexicalToHtml()` call; the `richText` write sets it.\n */\n readonly onUnrenderedBlock?:\n ((blockType: string, placeholderClass: string, fields: unknown) => void) | undefined;\n /** The route of an internal link, with `{slug}`, `{id}`, `{collection}` and `{locale}`. */\n readonly internalLinkPattern?: string | undefined;\n /** What `{locale}` reads. */\n readonly locale?: string | undefined;\n}\n\nexport type NodeRenderer = (node: LexicalNode, context: RenderNodeContext) => string;\n\nconst registry = new Map<string, NodeRenderer>();\n\n/** Register or replace the renderer for `type`. */\nexport function register(type: string, renderer: NodeRenderer): void {\n registry.set(type, renderer);\n}\n\n/** @internal */\nexport function lookup(type: string): NodeRenderer | undefined {\n return registry.get(type) ?? BUILTIN_NODE_RENDERERS[type];\n}\n\nexport function registeredTypes(): readonly string[] {\n return [...new Set([...Object.keys(BUILTIN_NODE_RENDERERS), ...registry.keys()])];\n}\n\n/** Test-only: drop consumer registrations. */\nexport function __resetForTests(): void {\n registry.clear();\n}\n","/**\n * Lexical → HTML. Each node dispatches to the renderer registered for its\n * `type`; an unknown node renders its children so content is never lost.\n */\n\nimport { sanitizeHtml, hasSanitizerDocument, type SanitizerDocument } from '@security/sanitizer';\nimport { lookup, type RenderNodeContext } from './registry';\nimport type { LexicalNode, LexicalRoot } from './types';\nimport { resolveAlignment, resolveIndent } from './utils';\n\nexport interface LexicalRenderOptions {\n /** Pass the result through `sanitizeHtml()` (default `true`). */\n readonly sanitize?: boolean;\n /**\n * The DOM the sanitizer parses with, for this call: an SSR document such as\n * linkedom's or jsdom's. Without it the document `setSanitizerDocument()`\n * supplied is used, then the global one; with neither the result is\n * unsanitised and warns once.\n */\n readonly document?: SanitizerDocument;\n /**\n * Throw instead of returning unsanitised HTML when no document is available:\n * the `SanitizerEnvironmentError` `sanitizeHtml()` throws (ADR 0025). Off in\n * 2.x, where the call warns once and returns the markup; the default in 3.0.\n */\n readonly requireDocument?: boolean;\n /**\n * Called for every `block` or `inlineBlock` the registry has no renderer\n * for, with the block's slug as Payload sent it, the class its empty\n * placeholder carries and the block's fields. The `richText` write listens: it is the one caller\n * that later knows whether the server's markup for the block survived, so\n * the diagnostic is spoken there (LP0410, LP0413), not here.\n */\n readonly onUnrenderedBlock?: (\n blockType: string,\n placeholderClass: string,\n fields: unknown,\n ) => void;\n /**\n * The route of an internal link whose document has no `url` of its own, such\n * as `/{locale}/{slug}/`. `{slug}`, `{id}`, `{collection}` and `{locale}` are\n * filled in and encoded; with one of them empty the link falls back to `/<slug>`.\n */\n readonly internalLinkPattern?: string | undefined;\n /** What `{locale}` reads in `internalLinkPattern`. */\n readonly locale?: string | undefined;\n}\n\nlet warnedNoSanitizer = false;\n\n/** @internal */\nexport function isLexicalContent(value: unknown): value is LexicalRoot {\n if (typeof value !== 'object' || value === null) return false;\n if (!('root' in value)) return false;\n const root = value.root;\n if (typeof root !== 'object' || root === null) return false;\n if (!('children' in root)) return false;\n return Array.isArray(root.children);\n}\n\n/** Render a Lexical document to HTML; without a document the result is unsanitised and warns once, or throws under `requireDocument`. */\nexport function lexicalToHtml(content: LexicalRoot, options: LexicalRenderOptions = {}): string {\n if (!isLexicalContent(content)) return '';\n const html = createContext(options).renderChildren(content.root.children);\n if (options.sanitize === false) return html;\n if (options.document !== undefined) return sanitizeHtml(html, { document: options.document });\n // Without a DOM this throws the sanitizer's own error.\n if (options.requireDocument === true || hasSanitizerDocument()) return sanitizeHtml(html);\n warnNoSanitizerOnce();\n return html;\n}\n\n/** Plain text of a document, paragraphs separated by `\\n`. */\nexport function lexicalToPlainText(content: LexicalRoot): string {\n if (!isLexicalContent(content)) return '';\n return content.root.children.map(extractPlainText).join('\\n').trim();\n}\n\n/** Test-only: allow the SSR warning to fire again. */\nexport function __resetSanitizerWarningForTests(): void {\n warnedNoSanitizer = false;\n}\n\n/**\n * One context per document: `renderChildren` closes over it so a nested\n * block — a `block` inside a list item, an `inlineBlock` inside a paragraph —\n * reports to the same listener as a top-level one.\n */\nfunction createContext(options: LexicalRenderOptions): RenderNodeContext {\n const context: RenderNodeContext = {\n renderChildren: (children) => {\n let out = '';\n for (const child of children) out += renderNode(child, context);\n return out;\n },\n resolveAlignment,\n resolveIndent,\n onUnrenderedBlock: options.onUnrenderedBlock,\n internalLinkPattern: options.internalLinkPattern,\n locale: options.locale,\n };\n return context;\n}\n\nfunction renderNode(node: LexicalNode, context: RenderNodeContext): string {\n const renderer = lookup(node.type);\n if (renderer) return renderer(node, context);\n return node.children !== undefined ? context.renderChildren(node.children) : '';\n}\n\nfunction extractPlainText(node: LexicalNode): string {\n if (typeof node.text === 'string') return node.text;\n if (node.type === 'linebreak') return '\\n';\n if (node.children === undefined) return '';\n let out = '';\n for (const child of node.children) out += extractPlainText(child);\n return out;\n}\n\n// This layer may not import `@core/diagnostics` (architecture policy), so the\n// warn-once lives here; a hostile or absent console must not break rendering.\nfunction warnNoSanitizerOnce(): void {\n if (warnedNoSanitizer) return;\n warnedNoSanitizer = true;\n try {\n console.warn(\n '[live-preview] lexicalToHtml() has no sanitizer document and returned unsanitised HTML; ' +\n 'pass { document } (linkedom, jsdom) per call. 3.0 throws here instead: opt in now with ' +\n '{ requireDocument: true }. See \"HTML sanitization\" in docs/security.md.',\n );\n } catch {\n // Diagnostics never become a second failure.\n }\n}\n","/**\n * Opt-in renderers for the block patterns most Payload sites ship (callout,\n * image, video, code, cta). Each reads well-known field names only, escapes\n * every value, validates URLs and renders nothing when a required field is\n * missing. Register with `registerDefaultBlocks()`.\n */\n\nimport { escapeHtml, escapeHtmlAttribute } from '@security/escape';\nimport { isExternalHttpUrl, isSafeUrl } from '@security/url-validator';\nimport type { LexicalNode } from '../types';\nimport { readMedia, sanitizeIdent, type MediaShape } from '../value-shapes';\nimport { registerBlockRenderer, type BlockRenderer } from './registry';\n\nfunction str(fields: Record<string, unknown>, key: string): string | undefined {\n const value = fields[key];\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction safeUrlOrUndefined(fields: Record<string, unknown>, key: string): string | undefined {\n const raw = str(fields, key);\n return raw !== undefined && isSafeUrl(raw) ? raw : undefined;\n}\n\nfunction captionHtml(fields: Record<string, unknown>): string {\n const caption = str(fields, 'caption');\n return caption !== undefined ? `<figcaption>${escapeHtml(caption)}</figcaption>` : '';\n}\n\nfunction dimensionAttributes(media: MediaShape | undefined): string {\n const width = typeof media?.width === 'number' ? ` width=\"${String(media.width)}\"` : '';\n const height = typeof media?.height === 'number' ? ` height=\"${String(media.height)}\"` : '';\n return `${width}${height}`;\n}\n\n/** Fields: `text` or a Lexical `body`/`content`, optional `title`, `importance` (default `info`). */\nconst calloutRenderer: BlockRenderer = (fields, ctx) => {\n const importance = sanitizeIdent(str(fields, 'importance') ?? 'info');\n const title = str(fields, 'title');\n const text = str(fields, 'text');\n const richText = readLexicalChildren(fields);\n\n const titleHtml = title !== undefined ? `<strong>${escapeHtml(title)}</strong>` : '';\n const bodyHtml =\n richText !== undefined\n ? ctx.renderChildren(richText)\n : text !== undefined\n ? `<p>${escapeHtml(text)}</p>`\n : '';\n return `<aside class=\"lp-block-callout lp-block-callout--${importance}\">${titleHtml}${bodyHtml}</aside>`;\n};\n\n/** Fields: `image` (media) or `imageUrl`, `alt`, optional `caption`. */\nconst imageBlockRenderer: BlockRenderer = (fields) => {\n const media = readMedia(fields['image']);\n const url = media?.url ?? safeUrlOrUndefined(fields, 'imageUrl');\n if (url === undefined || !isSafeUrl(url)) return '';\n const alt = media?.alt ?? str(fields, 'alt') ?? '';\n return `<figure class=\"lp-block-image\"><img src=\"${escapeHtmlAttribute(url)}\" alt=\"${escapeHtmlAttribute(alt)}\"${dimensionAttributes(media)} loading=\"lazy\" decoding=\"async\">${captionHtml(fields)}</figure>`;\n};\n\n/** Fields: `video` (media) or `videoUrl`, `mimeType`, optional `poster` and `caption`. */\nconst videoBlockRenderer: BlockRenderer = (fields) => {\n const media = readMedia(fields['video']);\n const url = media?.url ?? safeUrlOrUndefined(fields, 'videoUrl');\n if (url === undefined || !isSafeUrl(url)) return '';\n const mime = media?.mimeType ?? str(fields, 'mimeType');\n const typeAttr = mime !== undefined ? ` type=\"${escapeHtmlAttribute(mime)}\"` : '';\n const poster = safeUrlOrUndefined(fields, 'poster');\n const posterAttr = poster !== undefined ? ` poster=\"${escapeHtmlAttribute(poster)}\"` : '';\n return `<figure class=\"lp-block-video\"><video controls${posterAttr}><source src=\"${escapeHtmlAttribute(url)}\"${typeAttr}></video>${captionHtml(fields)}</figure>`;\n};\n\n/** Fields: `code` or `content`, optional `language` and `caption`. */\nconst codeBlockRenderer: BlockRenderer = (fields) => {\n const code = str(fields, 'code') ?? str(fields, 'content');\n if (code === undefined) return '';\n const language = sanitizeIdent(str(fields, 'language') ?? '');\n const langClass = language === '' ? '' : ` class=\"language-${language}\"`;\n return `<figure class=\"lp-block-code\">${captionHtml(fields)}<pre><code${langClass}>${escapeHtml(code)}</code></pre></figure>`;\n};\n\n/** Fields: `label`/`text` and `href`/`url`; optional `lead`, `secondaryLabel`, `secondaryHref`. */\nconst ctaBlockRenderer: BlockRenderer = (fields) => {\n const label = str(fields, 'label') ?? str(fields, 'text');\n const href = safeUrlOrUndefined(fields, 'href') ?? safeUrlOrUndefined(fields, 'url');\n if (label === undefined || href === undefined) return '';\n const lead = str(fields, 'lead');\n const leadHtml =\n lead !== undefined ? `<p class=\"lp-block-cta__lead\">${escapeHtml(lead)}</p>` : '';\n\n const buttons = [renderCtaAnchor(label, href, true)];\n const secondaryLabel = str(fields, 'secondaryLabel');\n const secondaryHref = safeUrlOrUndefined(fields, 'secondaryHref');\n if (secondaryLabel !== undefined && secondaryHref !== undefined) {\n buttons.push(renderCtaAnchor(secondaryLabel, secondaryHref, false));\n }\n return `<div class=\"lp-block-cta\">${leadHtml}<div class=\"lp-block-cta__actions\">${buttons.join('')}</div></div>`;\n};\n\nfunction renderCtaAnchor(label: string, href: string, primary: boolean): string {\n const targetAttr = isExternalHttpUrl(href) ? ' target=\"_blank\" rel=\"noopener noreferrer\"' : '';\n const variant = primary ? 'primary' : 'secondary';\n return `<a class=\"lp-block-cta__button lp-block-cta__button--${variant}\" href=\"${escapeHtmlAttribute(href)}\"${targetAttr}>${escapeHtml(label)}</a>`;\n}\n\nfunction readLexicalChildren(fields: Record<string, unknown>): readonly LexicalNode[] | undefined {\n const body = fields['body'] ?? fields['content'];\n if (typeof body !== 'object' || body === null) return undefined;\n const root = (body as { root?: { children?: unknown } }).root;\n if (!root || !Array.isArray(root.children)) return undefined;\n return root.children as readonly LexicalNode[];\n}\n\n/** Register every default block renderer under its kebab-, camel- and short slug. Idempotent. */\nexport function registerDefaultBlocks(): void {\n registerBlockRenderer('callout', calloutRenderer);\n registerBlockRenderer('image-block', imageBlockRenderer);\n registerBlockRenderer('imageBlock', imageBlockRenderer);\n registerBlockRenderer('image', imageBlockRenderer);\n registerBlockRenderer('video-block', videoBlockRenderer);\n registerBlockRenderer('videoBlock', videoBlockRenderer);\n registerBlockRenderer('video', videoBlockRenderer);\n registerBlockRenderer('code-block', codeBlockRenderer);\n registerBlockRenderer('codeBlock', codeBlockRenderer);\n registerBlockRenderer('code', codeBlockRenderer);\n registerBlockRenderer('cta-block', ctaBlockRenderer);\n registerBlockRenderer('ctaBlock', ctaBlockRenderer);\n registerBlockRenderer('cta', ctaBlockRenderer);\n}\n\nexport {\n calloutRenderer,\n imageBlockRenderer,\n videoBlockRenderer,\n codeBlockRenderer,\n ctaBlockRenderer,\n};\n"],"mappings":"AAoBO,SAASA,EAASC,EAAaC,GACpC,IAbK,WACL,IACE,MAAMC,EAAOC,WAA0EC,SACnFF,IACJ,YAAY,IAARA,GACuB,eAApBA,EAAc,QACvB,OACE,OAAO,CACT,CACF,CAIOG,GAAwB,OAC7B,MAAMC,EAASH,WACTI,EAAUD,EAAiB,4DAAUE,IAC3C,IAAID,EAAOE,IAAIT,GAAf,CACAO,EAAOG,IAAIV,GACX,IACEW,QAAQC,KAAK,0BAA0BX,IACzC,OAEA,CANqB,CAOvB,CCZO,IAODY,iBAAsBC,OAAOC,IAAI,sCAmDhC,SAASC,EAAYC,GAC1B,MAAMC,EApBR,WACE,MAAMC,EAzBR,WACE,MAAMC,EAAQjB,WACRkB,EAAWD,EAAMP,GACvB,QAAiB,IAAbQ,EAAwB,OAAOA,EACnC,MAAMC,EAA0B,GAEhC,OADAF,EAAMP,GAAQS,EACPA,CACT,CAkBmBC,GACjB,QAA0B,IAAtBJ,EAASK,SAAwB,OAAOL,EAASK,SACrD,QAAsB,IAAlBL,EAASM,KAAoB,OAAON,EAASM,KACjD,MAAMC,EAXR,WACE,MAAMC,EAAaxB,WAA0CyB,aAC7D,GAAyB,iBAAdD,GAAwC,OAAdA,EAErC,MAAyB,mBADTA,EAAyCE,aAClBF,OAAwC,CACjF,CAMcG,GACZ,IACEX,EAASM,UACC,IAARC,EACI,KACAA,EAAIG,aAhD2B,uBAgDa,CAAEE,WAAaC,GAAUA,GAC7E,OAGEb,EAASM,KAAO,IAClB,CACA,OAAON,EAASM,IAClB,CAIiBQ,GACf,OAAe,OAAXf,EAAwBD,EACrBC,EAAOa,WAAWd,EAC3B,CC5EA,IAAMiB,iBAAA,IAA0C1B,IAAI,CAAC,QAAS,SAAU,UAAW,SAE7E2B,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,OAAOR,EAAezB,IAAIuC,EAAOE,SACnC,OACE,OAAOf,EAAcY,KAAKL,KAjB9B,SAA4BS,GAC1B,MAAMC,EAASb,EAAOc,KAAKF,EAAMP,QAAQN,EAAa,OAAO,GAC7D,YAAkB,IAAXc,IAAyBlB,EAAezB,IAAI,GAAG2C,EAAOE,iBAC/D,CAc2CC,CAAmBb,EAC5D,CACF,CAGO,SAASc,kBAAkBf,GAChC,MAAMC,EAAUD,EAAIE,OAAOC,QAAQP,EAAgB,IACnD,MAAO,gBAAgBU,KAAKL,IAAYN,EAAkBW,KAAKL,EACjE,CCzBA,IAAMe,iBAAA,IAAejD,IA4Bd,SAASkD,EAAuBC,GACjCF,EAAShD,IAAIkD,KACjBF,EAAS/C,IAAIiD,GAtCf,SAAc1D,GACZ,IACEU,QAAQC,KAAKX,EACf,OAEA,CACF,CAiCEW,CACE,yDAAyD+C,qFA3B7D,SAAgBA,GACd,OAAIA,EAAKb,WAHY,iBAKjB,0IAIS,OAATa,GAA0B,SAATA,EAEjB,+HAKF,8GAGJ,CAWkEC,CAAOD,OAEzE,CCpDA,IAAME,iBAAA,IAAwCrD,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,QAIIsD,iBAAA,IAAyCtD,IAAI,CACjD,QACA,SACA,WACA,SACA,SACA,UAIIuD,iBAAA,IAA6CvD,IAAI,CACrD,SACA,QACA,SACA,SACA,QACA,OACA,OACA,OACA,OACA,QACA,SACA,SACA,WACA,MACA,OACA,WACA,QACA,WACA,WACA,a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","ignoreList":[]}
|