payload-live-preview 2.2.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -18
- package/dist/adapters/astro/PreviewBoundary.astro +17 -4
- package/dist/adapters/astro/RichText.astro +13 -2
- package/dist/adapters/astro/index.d.ts +7 -7
- package/dist/adapters/astro/index.js +1 -1
- package/dist/adapters/astro/index.js.map +1 -1
- package/dist/adapters/astro/middleware-entry.d.ts +3 -3
- package/dist/adapters/astro/middleware-entry.js +1 -1
- package/dist/adapters/astro/middleware-entry.js.map +1 -1
- package/dist/adapters/nextjs/index.d.ts +2 -2
- package/dist/adapters/nextjs/index.js +1 -1
- package/dist/adapters/nextjs/index.js.map +1 -1
- package/dist/adapters/nuxt/index.d.ts +4 -4
- package/dist/adapters/nuxt/index.js +1 -1
- package/dist/adapters/nuxt/index.js.map +1 -1
- package/dist/adapters/nuxt/module.d.ts +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/index.d.ts +5 -5
- 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.d.cts +15 -3
- package/dist/client.d.ts +15 -3
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/codegen-astro.js +1 -1
- package/dist/codegen-astro.js.map +1 -1
- package/dist/codegen-cli.js +1 -1
- package/dist/codegen-cli.js.map +1 -1
- package/dist/codegen.cjs +1 -1
- package/dist/codegen.cjs.map +1 -1
- package/dist/codegen.js +1 -1
- package/dist/codegen.js.map +1 -1
- package/dist/core.cjs +1 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +141 -6
- package/dist/core.d.ts +141 -6
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/doctor-cli.d.ts +1 -1
- package/dist/doctor-cli.js +1 -1
- package/dist/doctor-cli.js.map +1 -1
- package/dist/doctor.d.ts +2 -2
- package/dist/doctor.js +1 -1
- package/dist/doctor.js.map +1 -1
- package/dist/{document-session-Bkd181PV.d.ts → document-session-BuYuO3ae.d.ts} +27 -2
- package/dist/{fragment-endpoint-DGGLIijM.d.ts → fragment-endpoint-CQE_7MKi.d.ts} +5 -3
- package/dist/fragment.cjs +1 -1
- package/dist/fragment.cjs.map +1 -1
- package/dist/fragment.d.cts +6 -2
- package/dist/fragment.d.ts +6 -2
- package/dist/fragment.js +1 -1
- package/dist/fragment.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +79 -16
- package/dist/index.d.ts +79 -16
- 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 +1 -1
- package/dist/lexical.cjs.map +1 -1
- package/dist/lexical.d.cts +16 -4
- package/dist/lexical.d.ts +16 -4
- package/dist/lexical.js +1 -1
- package/dist/lexical.js.map +1 -1
- package/dist/{locals-zz2gi9dj.d.ts → locals-CyzDKfKU.d.ts} +7 -1
- package/dist/{middleware-CI-UTf6R.d.ts → middleware-Dnt6u4nF.d.ts} +2 -2
- package/dist/{options-CnYybG51.d.ts → options-DTzhFVJ4.d.ts} +13 -1
- package/dist/plugin.cjs +1 -1
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +15 -9
- package/dist/plugin.d.ts +15 -9
- package/dist/plugin.js +1 -1
- package/dist/plugin.js.map +1 -1
- package/dist/plugins.cjs +1 -1
- package/dist/plugins.cjs.map +1 -1
- package/dist/plugins.d.cts +8 -2
- package/dist/plugins.d.ts +8 -2
- package/dist/plugins.js +1 -1
- package/dist/plugins.js.map +1 -1
- package/dist/{probe-D-IHg9dF.d.ts → probe-afwdYebU.d.ts} +6 -2
- package/dist/server.cjs +1 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +23 -3
- package/dist/server.d.ts +23 -3
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/structural.cjs +2 -2
- package/dist/structural.cjs.map +1 -1
- package/dist/structural.d.cts +2 -0
- package/dist/structural.d.ts +2 -0
- package/dist/structural.js +1 -1
- package/dist/structural.js.map +1 -1
- package/package.json +7 -4
package/dist/fragment.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fragment.cjs","names":["linkedTimeout","parent","ms","controller","AbortController","timedOut","onAbort","abort","addEventListener","once","timer","setTimeout","signal","dispose","clearTimeout","removeEventListener","errorMessage","error","Error","message","String","ISLAND_ATTRIBUTE","OWNED_ATTRIBUTE","isMorphBoundary","element","tagName","toLowerCase","includes","hasAttribute","isIslandBoundary","editable","getAttribute","isIslandRoot","isInsideIsland","current","parentElement","ISLAND_IDENTITY","renderedSlots","WeakMap","unmountedIslands","WeakSet","isAstroIsland","slotMarkup","island","markup","slot","querySelectorAll","closest","innerHTML","retainIslandBoundary","live","rendered","slots","every","name","get","constructor","ownerDocument","defaultView","customElements","Array","isArray","observedAttributes","observesProps","set","props","setAttribute","removeAttribute","parseDependencyList","value","seen","Set","names","raw","split","trim","length","has","add","push","FRAGMENT_ATTRIBUTE","FRAGMENT_KEY_ATTRIBUTE","describeBoundary","id","key","dependsOn","collectFragmentBoundaries","root","changedFields","boundaries","boundary","some","field","FRAGMENT_VERSION_HEADER","depthOf","depth","maxDepth","deepest","child","Object","values","Math","max","isRecord","parseFragmentResponse","html","revision","metadata","abortReason","reason","DOMException","isAborted","aborted","SUPERSEDED","status","failed","code","createFragmentHandler","options","endpoint","refuse","TypeError","startsWith","location","resolved","URL","href","origin","sameOriginPath","timeoutMs","maxResponseBytes","gate","limit","active","waiting","release","next","shift","resume","async","run","Promise","resolve","reject","waiter","index","indexOf","splice","createGate","maxConcurrent","inFlight","Map","generations","nextGeneration","generationOf","known","request","dedupeKey","JSON","stringify","shared","started","promise","superseded","where","body","source","result","entries","definedOnly","fragment","route","pathname","search","locale","collectionSlug","globalSlug","fields","timeout","failure","response","readingResponse","fetch","method","credentials","headers","accept","ok","type","Number","text","parsed","maxBytes","stream","getReader","TextEncoder","encode","byteLength","reader","cancel","catch","decoder","TextDecoder","total","done","read","decode","releaseLock","readBoundedText","parse","render","forget","delete","then","DIAGNOSTIC_CODES","freeze","NoTrustedOrigin","ReferrerOnlyTrust","PluginIncompatible","ProfileFeatureOmitted","OrphanField","UnattributableUpdate","UnboundField","VisibilityGateDeferred","UnsafeAttributeWrite","TextTargetHasChildren","MissingArrayTemplate","StructuralItemUnkeyed","StructuralDuplicateKey","StructuralUnstableKeys","UnsupportedStrategy","UnknownValueFormat","SanitizerDroppedAttribute","UnrenderedBlockKept","UnfaithfulPatch","ServerFormatReplaced","UnrenderedBlockLost","MessageRejected","TokenRejected","ProtocolShapeUnknown","HandlerThrew","TransformThrew","RendererThrew","StartupFailed","ReadyFailed","HydrationWaitTimedOut","AuditRuntimeMissing","AuditNoFrameAncestors","AuditFrameOptionsBlocks","AuditBindingsExposed","AuditGateThresholdExceeded","AuditUnownedBindings","AuditNoBindings","AuditNotAPage","FragmentRequestFailed","FragmentResponseInvalid","FragmentUnauthorized","FragmentSuperseded","RouteRefreshLoop","FragmentStrategyUnavailable","UnboundChangeRefresh","EscalationUnavailable","IslandStartUnavailable","RouteRefreshUnconfirmed","V2ReadinessGap","RuntimeOnPublicPage","STATE_ATTRIBUTES","keyOf","attribute","keyAttributes","morphElement","sameKind","liveBoundary","renderedBoundary","retainBoundary","focus","activeElement","HTMLElement","contains","selection","HTMLInputElement","HTMLTextAreaElement","selectionStart","selectionEnd","captureFocus","from","attributes","syncAttributes","retainChildrenOf","liveKeyed","keyed","duplicate","children","slice","onDuplicateKey","indexKeyed","keyedElements","liveUnkeyed","childNodes","filter","node","claimed","usedKeys","renderedNodes","cursor","firstChild","unkeyedIndex","i","kept","Element","candidate","reconcile","replaceWith","nextSibling","replaceChild","following","isBlankText","insertBefore","remove","morphChildren","snapshot","isConnected","preventScroll","start","end","setSelectionRange","restoreFocus","nodeValue","nodeType","Node","TEXT_NODE","a","b","namespaceURI","STRATEGY_ATTRIBUTE","FIELD_ATTRIBUTE","ROUTE_REFRESH_HEADER","isRouteBound","explicit","fieldsOf","own","depends","isManagedHeadElement","fragmentStrategyFrom","handler","plan","map","context","elements","report","receivedAt","all","outcome","isCurrent","morph","patch","log","minIntervalMs","lastRefreshAt","saidUnconfirmed","inScope","refresh","now","Date","waitMs","retryAfter","document","view","window","detail","hostRefresh","settled","cache","fresh","DOMParser","parseFromString","x","scrollX","y","scrollY","liveTitle","head","find","freshTitle","title","liveSlots","targets","paired","min","forEach","target","replacement","importNode","anchor","at","addition","append","after","surplus","syncHead","islands","dispatchEvent","CustomEvent","releaseDisconnectedIslands","scrollTo","maxFieldDepth","isInteger","test","slugs","slug","parseFragmentRequestResult","enclosingFragment"],"sources":["../src/fragment/abort.ts","../src/core/islands.ts","../src/core/dependencies.ts","../src/core/strategies.ts","../src/fragment/boundary.ts","../src/types/fragment-protocol.ts","../src/fragment/gate.ts","../src/fragment/handler.ts","../src/types/defined-only.ts","../src/core/diagnostic-codes.ts","../src/core/morph.ts","../src/fragment/route.ts","../src/fragment/index.ts","../src/core/route-refresh.ts","../src/core/structural-applier.ts"],"sourcesContent":["/**\n * One signal for one request: it aborts when the caller's does or when `ms`\n * pass, whichever first. `timedOut()` tells the two apart afterwards; the\n * caller checks its own signal for the other. `dispose()` runs when the\n * request settles — the timer and the parent's listener outlive it otherwise.\n */\n\nexport interface LinkedTimeout {\n readonly signal: AbortSignal;\n readonly timedOut: () => boolean;\n readonly dispose: () => void;\n}\n\nexport function linkedTimeout(parent: AbortSignal, ms: number): LinkedTimeout {\n const controller = new AbortController();\n let timedOut = false;\n const onAbort = (): void => {\n controller.abort();\n };\n parent.addEventListener('abort', onAbort, { once: true });\n // The flag is set before the abort so a listener on `signal` can already read it.\n const timer = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, ms);\n return {\n signal: controller.signal,\n timedOut: () => timedOut,\n dispose: () => {\n clearTimeout(timer);\n parent.removeEventListener('abort', onAbort);\n },\n };\n}\n\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","/**\n * Hydrated islands own their subtree, so the runtime never patches inside one.\n * Instead every flush that carried a change dispatches `payload-live-preview:update`\n * on each island root with the update in `detail`, whether or not a binding\n * outside the islands was written. `data-payload-island=\"patch\"` opts back in.\n * Server HTML reaches an Astro island only through Astro's own props handoff\n * (ADR 0021). @internal\n */\n\nexport const ISLAND_EVENT = 'payload-live-preview:update';\n/** @internal */\nexport const ISLAND_ATTRIBUTE = 'data-payload-island';\nexport const ISLAND_SELECTOR = `astro-island, [${ISLAND_ATTRIBUTE}]`;\n/** The opt-out for a subtree the site scripts itself: the morph never enters it. @internal */\nexport const OWNED_ATTRIBUTE = 'data-payload-owned';\n/**\n * The opt-in for a custom element: present on the live and on the rendered host\n * (its value is ignored), the morph edits the host's attributes and light DOM\n * instead of retaining it whole (ADR 0008 §10). @internal\n */\nexport const MORPH_ATTRIBUTE = 'data-payload-morph';\n\n/**\n * The ownership rule (ADR 0008 §4): a subtree the morph never enters. Custom\n * elements own their subtree unless the template opts in with\n * `data-payload-morph` (§10), islands are a framework's, `contenteditable`\n * is the visitor's, `data-payload-owned` is the site's. The coordinators hand\n * this rule to the engine as `MorphOptions.boundary`; the engine itself only\n * pairs, edits and keeps focus (§9). @beta\n */\nexport function isMorphBoundary(element: Element): boolean {\n const customElement = element.tagName.toLowerCase().includes('-');\n if (customElement && !element.hasAttribute(MORPH_ATTRIBUTE)) return true;\n if (isIslandBoundary(element) || element.hasAttribute(OWNED_ATTRIBUTE)) return true;\n const editable = element.getAttribute('contenteditable');\n return editable !== null && editable !== 'false';\n}\n\nexport interface IslandUpdateDetail {\n readonly fields: Readonly<Record<string, unknown>>;\n readonly revision: number;\n readonly receivedAt: number;\n readonly locale: string | undefined;\n /**\n * Call once the island has rendered this revision, for instance from an\n * effect after its framework committed. Until every island handed the\n * revision has called it, `inspect().revisions.display` says `unconfirmed`\n * (ADR 0023). Repeated, late or superseded calls do nothing.\n */\n readonly displayed: () => void;\n}\n\nfunction isIslandRoot(element: Element): boolean {\n return element.tagName.toLowerCase() === 'astro-island' || element.hasAttribute(ISLAND_ATTRIBUTE);\n}\n\nfunction islandAllowsPatching(island: Element): boolean {\n return island.getAttribute(ISLAND_ATTRIBUTE) === 'patch';\n}\n\n/** Whether `element` itself is an island root that did not opt into patching. */\nexport function isIslandBoundary(element: Element): boolean {\n return isIslandRoot(element) && !islandAllowsPatching(element);\n}\n\n/** Whether `element` (or an ancestor) is an island that did not opt into patching. @internal */\nexport function isInsideIsland(element: Element): boolean {\n let current: Element | null = element;\n while (current !== null) {\n if (isIslandBoundary(current)) return true;\n current = current.parentElement;\n }\n return false;\n}\n\n/** Island roots under `root` that receive update events. */\nexport function collectIslands(root: ParentNode): Element[] {\n const islands: Element[] = [];\n for (const island of root.querySelectorAll(ISLAND_SELECTOR)) {\n if (!islandAllowsPatching(island)) islands.push(island);\n }\n return islands;\n}\n\n/** Astro keeps `ssr` until the framework listener has finished hydrating. @internal */\nexport function isAwaitingIslandHydration(island: Element): boolean {\n return island.tagName.toLowerCase() === 'astro-island' && island.hasAttribute('ssr');\n}\n\nexport function dispatchIslandUpdate(\n islands: readonly Element[],\n detail: IslandUpdateDetail,\n isCurrent: () => boolean,\n): number {\n let dispatched = 0;\n for (const island of islands) {\n if (!isCurrent()) break;\n island.dispatchEvent(\n new CustomEvent<IslandUpdateDetail>(ISLAND_EVENT, { detail, bubbles: false }),\n );\n dispatched += 1;\n if (!isCurrent()) break;\n }\n return dispatched;\n}\n\n/** What decides whether a live `astro-island` still renders the rendered one's component (ADR 0021). */\nconst ISLAND_IDENTITY = [\n 'component-url',\n 'component-export',\n 'renderer-url',\n 'client',\n 'opts',\n 'before-hydration-url',\n] as const;\n/** Slot markup each island was last rendered with; Astro reads slots once, at hydration. */\nconst renderedSlots = new WeakMap<Element, string>();\nconst unmountedIslands = new WeakSet<Element>();\n\nfunction isAstroIsland(element: Element): boolean {\n return element.tagName.toLowerCase() === 'astro-island';\n}\n\nfunction slotMarkup(island: Element): string {\n let markup = '';\n for (const slot of island.querySelectorAll('astro-slot, template[data-astro-template]')) {\n if (slot.parentElement?.closest('astro-island') !== island) continue;\n const name = slot.getAttribute('name') ?? slot.getAttribute('data-astro-template') ?? '';\n markup += `${name}\\u0000${slot.innerHTML}\\u0000`;\n }\n return markup;\n}\n\n/** Only an upgraded island whose class observes `props` re-hydrates in place. */\nfunction observesProps(island: Element): boolean {\n const constructor = island.ownerDocument.defaultView?.customElements.get('astro-island') as\n (CustomElementConstructor & { readonly observedAttributes?: unknown }) | undefined;\n return (\n constructor !== undefined &&\n island instanceof constructor &&\n Array.isArray(constructor.observedAttributes) &&\n constructor.observedAttributes.includes('props')\n );\n}\n\n/**\n * The package's decision for a boundary pair (ADR 0021). A boundary other than\n * an Astro island stays whole (ADR 0008 §4). An island that still renders the\n * same component, with the same slots, keeps its framework state: it takes the\n * rendered props the way Astro's router hands them to a persisted island,\n * `ssr` first, then `props`. Anything else is replaced, and hydrates fresh.\n * @beta\n */\nexport function retainIslandBoundary(live: Element, rendered: Element): boolean {\n if (!isAstroIsland(live) || !isAstroIsland(rendered)) return true;\n const slots = slotMarkup(rendered);\n const retained =\n ISLAND_IDENTITY.every((name) => live.getAttribute(name) === rendered.getAttribute(name)) &&\n (renderedSlots.get(live) ?? slotMarkup(live)) === slots &&\n (live.getAttribute('props') === rendered.getAttribute('props') || observesProps(live));\n if (!retained) {\n renderedSlots.set(rendered, slots);\n return false;\n }\n renderedSlots.set(live, slots);\n const props = rendered.getAttribute('props');\n if (props !== live.getAttribute('props')) {\n live.setAttribute('ssr', '');\n if (props === null) live.removeAttribute('props');\n else live.setAttribute('props', props);\n }\n return true;\n}\n\n/** The Astro islands under `root`, to compare before and after a morph. @internal */\nexport function astroIslandsIn(root: ParentNode): Element[] {\n return Array.from(root.querySelectorAll('astro-island'));\n}\n\n/**\n * Astro's framework clients release their roots on `astro:unmount`, which its\n * router sends after a swap. A morph that disconnects an island sends it once\n * itself, so a removed React root does not outlive its markup. @internal\n */\nexport function releaseDisconnectedIslands(islands: readonly Element[]): void {\n for (const island of islands) {\n if (island.isConnected || unmountedIslands.has(island)) continue;\n unmountedIslands.add(island);\n island.dispatchEvent(new CustomEvent('astro:unmount'));\n }\n}\n\n/**\n * Why an inserted island cannot start: fragment scripts never run, so the page\n * itself must have loaded Astro's island element and this client directive.\n * @internal\n */\nexport function islandStartBlocker(island: Element): 'element' | 'directive' | undefined {\n const view = island.ownerDocument.defaultView;\n if (view?.customElements.get('astro-island') === undefined) return 'element';\n const directive = island.getAttribute('client');\n const astro: unknown = Reflect.get(view, 'Astro');\n if (directive === null || typeof astro !== 'object' || astro === null) return 'directive';\n return Reflect.get(astro, directive) === undefined ? 'directive' : undefined;\n}\n","/**\n * One source → dependents map for `skipUnchanged`. Markup declares it the\n * other way round (`data-payload-depends=\"price\"` on the element bound to\n * `priceLabel`) because an author names what a binding needs; the runtime\n * option is keyed by source because the scheduler asks what changed.\n * @internal\n */\n\nexport type DependencyMap = Readonly<Record<string, readonly string[]>>;\n\n/**\n * Field names from an attribute value. Separators are commas and\n * whitespace; empty entries are dropped; order is kept, duplicates removed.\n * @internal\n */\nexport function parseDependencyList(value: string | null | undefined): readonly string[] {\n if (value === null || value === undefined) return [];\n const seen = new Set<string>();\n const names: string[] = [];\n for (const raw of value.split(/[\\s,]+/)) {\n const name = raw.trim();\n if (name.length === 0 || seen.has(name)) continue;\n seen.add(name);\n names.push(name);\n }\n return names;\n}\n\n/**\n * Merge dependency maps (source → dependents). Later maps add to earlier\n * ones; a dependent is listed once per source.\n * @internal\n */\nexport function mergeDependencyMaps(...maps: readonly DependencyMap[]): DependencyMap {\n const merged = new Map<string, Set<string>>();\n for (const map of maps) {\n for (const [source, dependents] of Object.entries(map)) {\n let bucket = merged.get(source);\n if (bucket === undefined) {\n bucket = new Set();\n merged.set(source, bucket);\n }\n for (const dependent of dependents) bucket.add(dependent);\n }\n }\n const result: Record<string, readonly string[]> = {};\n for (const [source, dependents] of merged) result[source] = [...dependents];\n return result;\n}\n\n/**\n * The map an element's `data-payload-depends` contributes: every declared\n * source points at this binding's field.\n * @internal\n */\nexport function dependencyMapFromBinding(\n fieldName: string,\n dependsOn: readonly string[],\n): DependencyMap {\n const map: Record<string, readonly string[]> = {};\n for (const source of dependsOn) {\n if (source === fieldName) continue;\n map[source] = [fieldName];\n }\n return map;\n}\n","/**\n * How a bound region is brought up to date: the runtime patches it, a server\n * renders a `data-payload-fragment` boundary from the unsaved form state, or\n * the whole route refreshes. Core carries only the seam — which boundaries a\n * strategy owns and what it may do — so a patch-only page pays for nothing\n * more; the orchestration lives in `payload-live-preview/fragment`.\n */\nimport type { DiagnosticCode } from './diagnostic-codes';\nimport type { CachedElement } from './types';\nimport type { OwnerScope } from './unbound-fields';\n\n/** What produced an update: the runtime's DOM patching, a server-rendered fragment, or a route refresh. */\nexport type UpdateSource = 'patch' | 'fragment' | 'route';\n\n/** Marks a server-rendered boundary; the value is the registry id the server may render. @internal */\nexport const FRAGMENT_ATTRIBUTE = 'data-payload-fragment';\n/** Distinguishes several boundaries of one registry id; unique among siblings. @internal */\nexport const FRAGMENT_KEY_ATTRIBUTE = 'data-payload-fragment-key';\n\n/** The nearest fragment boundary enclosing `element` (the element itself included). */\nexport function enclosingFragment(element: Element): Element | null {\n return element.closest(`[${FRAGMENT_ATTRIBUTE}]`);\n}\n\n/**\n * Where a render planned for `planned` lands. A host component that re-renders\n * its region while the render is in flight swaps the element and keeps the\n * identity, so a boundary no longer in the document stands for the one that\n * is: the single connected boundary with its id and key that `inScope` admits.\n * Two candidates name no single region, and none means it is gone (H06).\n */\nexport function liveBoundary(\n root: Document | Element,\n planned: Element,\n inScope: (boundary: Element) => boolean,\n): Element | undefined {\n if (planned.isConnected) return planned;\n const id = planned.getAttribute(FRAGMENT_ATTRIBUTE);\n const key = planned.getAttribute(FRAGMENT_KEY_ATTRIBUTE);\n let found: Element | undefined;\n for (const candidate of root.querySelectorAll(`[${FRAGMENT_ATTRIBUTE}]`)) {\n if (candidate.getAttribute(FRAGMENT_ATTRIBUTE) !== id) continue;\n if (candidate.getAttribute(FRAGMENT_KEY_ATTRIBUTE) !== key || !inScope(candidate)) continue;\n if (found !== undefined) return undefined;\n found = candidate;\n }\n return found;\n}\n\n/** One revision, as a strategy sees it, with the runtime capabilities it may use. */\nexport interface FragmentContext {\n readonly root: ParentNode;\n readonly revision: number;\n readonly receivedAt: number;\n readonly fields: Readonly<Record<string, unknown>>;\n readonly locale: string | undefined;\n readonly collectionSlug: string | undefined;\n readonly globalSlug: string | undefined;\n /** Aborted when a newer revision arrives or the runtime stops. */\n readonly signal: AbortSignal;\n /** Whether this revision is still the current one. */\n readonly isCurrent: () => boolean;\n /** Morph server-rendered HTML into a boundary; no-op after supersession (Trusted Types and the keyed morph apply). */\n readonly morph: (boundary: Element, html: string) => void;\n /** Patch the boundary's owned bindings from this revision; no-op after supersession. */\n readonly patch: (boundary: Element) => void;\n /** Debug log through the runtime's logger, with the diagnostic code. */\n readonly log: (code: DiagnosticCode, detail: string) => void;\n /** Report a boundary rendered. */\n readonly rendered: (boundary: Element, id: string, key: string | undefined) => void;\n /** Report a boundary failed and patched instead; emits the `error` and `fragmentRender` events. */\n readonly failed: (\n boundary: Element,\n id: string,\n key: string | undefined,\n code: DiagnosticCode,\n reason: string,\n ) => void;\n}\n\nexport interface FragmentReport {\n readonly rendered: number;\n readonly failed: number;\n readonly superseded: number;\n}\n\n/** A fragment strategy: which boundaries it owns for a revision, and how it renders them. */\nexport interface FragmentStrategy {\n /** The boundaries this update re-renders; their inner bindings are left to the strategy. */\n readonly plan: (root: ParentNode, changedFields: ReadonlySet<string>) => readonly Element[];\n /** Render the planned boundaries; resolves once every one settled. */\n readonly render: (\n context: FragmentContext,\n boundaries: readonly Element[],\n ) => Promise<FragmentReport>;\n}\n\n/** The boundaries a revision hands the fragment strategy, and the bindings they take from the patch. */\nexport interface FragmentPlan {\n readonly boundaries: readonly Element[];\n readonly strategy: FragmentStrategy;\n readonly ownerKeys: OwnerScope;\n /** Whether a binding sits inside a boundary the strategy renders this revision. */\n readonly covers: (target: CachedElement) => boolean;\n}\n\n/** A plan over boundaries already chosen, whichever question chose them. */\nexport function planBoundaries(\n strategy: FragmentStrategy,\n boundaries: readonly Element[],\n ownerKeys: OwnerScope,\n): FragmentPlan {\n const covered = new Set(boundaries);\n return {\n boundaries,\n strategy,\n ownerKeys,\n covers: (target) =>\n target.fragmentBoundary !== undefined && covered.has(target.fragmentBoundary),\n };\n}\n\n/** One revision, as the route strategy sees it. */\nexport interface RouteContext {\n readonly revision: number;\n readonly receivedAt: number;\n /** Aborted when a newer revision asks for a refresh of its own, a navigation commits, or the runtime stops. */\n readonly signal: AbortSignal;\n /**\n * Whether the refresh may still land. A newer revision that asks for no\n * refresh of its own does not end it: the route is the server's, so the\n * refresh lands for that revision, which the runtime then re-applies.\n */\n readonly isCurrent: () => boolean;\n readonly log: (code: DiagnosticCode, detail: string) => void;\n /**\n * Ask to be run again in `delayMs`, once. A strategy that refuses a refresh\n * because it is rate-limiting itself uses this: the window is a rate limit\n * and not a filter, so what falls inside it still has to reach the preview.\n * The runtime runs at most one such request, and only while the revision that\n * asked is still the current one.\n */\n readonly retryAfter?: (delayMs: number) => void;\n}\n\n/**\n * What one refresh did. `refreshed` asserts that the strategy rendered the\n * current unsaved revision. `partial` completed a render whose data source was\n * not proven current; the runtime still reapplies every binding it can reach.\n * `refused` is the strategy's own brake and not a failure.\n */\nexport type RouteOutcome = 'refreshed' | 'partial' | 'failed' | 'refused' | 'superseded';\n\n/** What the runtime tells a route planner about the update it plans for. */\nexport interface RoutePlanContext {\n /**\n * Whether an element belongs to the update: always `true` without\n * `scopeBindingsByOwner`, otherwise only inside the document the update\n * names. A marker outside it asks for nothing.\n */\n readonly inScope: (element: Element) => boolean;\n}\n\n/**\n * Whether a revision needs the whole route re-rendered, and how. After a\n * refresh the runtime rescans and re-applies the revision, so reachable\n * unsaved bindings land on the fresh markup. The runtime always passes\n * `context`; a planner written before it existed still works, but plans for\n * markers of every document.\n */\nexport interface RouteStrategy {\n readonly plan: (\n root: ParentNode,\n changedFields: ReadonlySet<string>,\n context?: RoutePlanContext,\n ) => boolean;\n readonly refresh: (context: RouteContext) => Promise<RouteOutcome>;\n}\n\nexport interface StrategyHandlers {\n readonly fragment?: FragmentStrategy;\n readonly route?: RouteStrategy;\n}\n\nconst STRATEGY_ATTRIBUTE = 'data-payload-strategy';\n\n/**\n * Explicit `data-payload-strategy` wins; otherwise a binding inside a fragment\n * boundary belongs to the fragment, one in `<head>` to the route, the rest is\n * patched. An unknown explicit value resolves to `undefined` (LP0407).\n * @internal\n */\nexport function resolveStrategy(element: Element): UpdateSource | undefined {\n const explicit = element.getAttribute(STRATEGY_ATTRIBUTE);\n if (explicit !== null && explicit.length > 0) {\n return explicit === 'patch' || explicit === 'fragment' || explicit === 'route'\n ? explicit\n : undefined;\n }\n if (enclosingFragment(element) !== null) return 'fragment';\n if (element.closest('head') !== null) return 'route';\n return 'patch';\n}\n","/** Fragment boundaries: how a `data-payload-fragment` element is read and which ones an update touches. */\n\nimport type { DiagnosticCode } from '@core/diagnostic-codes';\nimport { isInsideIsland } from '@core/islands';\nimport { parseDependencyList } from '@core/dependencies';\nimport { FRAGMENT_ATTRIBUTE, FRAGMENT_KEY_ATTRIBUTE } from '@core/strategies';\n\nexport { FRAGMENT_KEY_ATTRIBUTE };\nconst DEPENDS_ATTRIBUTE = 'data-payload-depends';\n\n/** @internal */\nexport interface FragmentBoundary {\n readonly element: Element;\n /** Registry id — never a path, module or function name. */\n readonly id: string;\n readonly key: string | undefined;\n /** Fields whose change re-renders the boundary; empty means any field. */\n readonly dependsOn: readonly string[];\n}\n\n/** One revision's request to a per-boundary handler. @internal */\nexport interface StrategyRequest {\n readonly revision: number;\n readonly receivedAt: number;\n readonly fields: Readonly<Record<string, unknown>>;\n readonly locale: string | undefined;\n readonly collectionSlug: string | undefined;\n readonly globalSlug: string | undefined;\n /** Aborted when a newer revision arrives or the runtime stops. */\n readonly signal: AbortSignal;\n}\n\n/** @internal */\nexport type FragmentOutcome =\n | {\n readonly status: 'rendered';\n /** The boundary's new inner HTML. */\n readonly html: string;\n readonly metadata?: Readonly<Record<string, unknown>>;\n }\n | { readonly status: 'failed'; readonly code: DiagnosticCode; readonly reason: string }\n | { readonly status: 'superseded' };\n\n/** Renders one boundary for one revision. Must honour `request.signal`. @internal */\nexport type FragmentHandler = (\n request: StrategyRequest,\n boundary: FragmentBoundary,\n) => Promise<FragmentOutcome>;\n\n/** @internal */\nexport function describeBoundary(element: Element): FragmentBoundary | null {\n const id = element.getAttribute(FRAGMENT_ATTRIBUTE);\n if (id === null || id.length === 0) return null;\n const key = element.getAttribute(FRAGMENT_KEY_ATTRIBUTE);\n return {\n element,\n id,\n key: key === null || key.length === 0 ? undefined : key,\n dependsOn: parseDependencyList(element.getAttribute(DEPENDS_ATTRIBUTE)),\n };\n}\n\n/** The boundaries under `root` that `changedFields` touch; one inside an island is the island's business. @internal */\nexport function collectFragmentBoundaries(\n root: ParentNode,\n changedFields: ReadonlySet<string>,\n): readonly FragmentBoundary[] {\n const boundaries: FragmentBoundary[] = [];\n for (const element of root.querySelectorAll(`[${FRAGMENT_ATTRIBUTE}]`)) {\n const boundary = describeBoundary(element);\n if (boundary === null || isInsideIsland(element)) continue;\n if (\n boundary.dependsOn.length > 0 &&\n !boundary.dependsOn.some((field) => changedFields.has(field))\n ) {\n continue;\n }\n boundaries.push(boundary);\n }\n return boundaries;\n}\n","/**\n * The fragment wire protocol: what the browser posts to a fragment endpoint\n * and what it answers. Both sides validate the shape. The request names the\n * page by its own route and query and the boundary by its registry id; nothing\n * on the wire carries code, templates, import paths or file paths. See ADR 0011.\n */\n\n/** Bumped when the request or response shape changes incompatibly. @internal */\nexport const FRAGMENT_PROTOCOL_VERSION = 1;\n/** @internal */\nexport const FRAGMENT_VERSION_HEADER = 'x-payload-fragment-version';\n\n/** @internal */\nexport interface FragmentRequestBody {\n /** Registry id of the boundary (`data-payload-fragment`). */\n readonly fragment: string;\n /** `data-payload-fragment-key`, when several boundaries share one id. */\n readonly key?: string;\n /** The page route the boundary belongs to; authorization is bound to it. */\n readonly route: string;\n /** The page's query string, so a `previewToken` in the URL reaches the endpoint. */\n readonly search: string;\n readonly revision: number;\n readonly locale?: string;\n readonly collectionSlug?: string;\n readonly globalSlug?: string;\n /** The unsaved form state as the admin posted it, after the runtime's merge. */\n readonly fields: Readonly<Record<string, unknown>>;\n}\n\n/** @internal */\nexport interface FragmentResponseBody {\n /** The boundary's new inner HTML. */\n readonly html: string;\n readonly boundary: { readonly id: string; readonly key?: string };\n /** Echoes the request; the client discards a response for another revision. */\n readonly revision: number;\n readonly metadata: {\n readonly renderedAt: string;\n /** What rendered it, e.g. `astro-container`. */\n readonly renderer: string;\n readonly durationMs?: number;\n };\n}\n\n// Bounds the walk over a body anyone may post, not the content: a bulleted list\n// in a column's rich text inside a layout block is 13 levels, a list nested\n// five levels 29 (ADR 0027). The byte cap bounds the rest.\n/** Fixed upper bound for validation before authorization. @internal */\nexport const MAX_FIELD_DEPTH = 64;\n\nfunction depthOf(value: unknown, depth: number, maxDepth: number): number {\n if (depth > maxDepth || typeof value !== 'object' || value === null) return depth;\n let deepest = depth;\n for (const child of Object.values(value)) {\n deepest = Math.max(deepest, depthOf(child, depth + 1, maxDepth));\n if (deepest > maxDepth) break;\n }\n return deepest;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Shape check for a request body; `null` when it is not one. @internal */\nexport function parseFragmentRequest(\n value: unknown,\n maxFieldDepth = MAX_FIELD_DEPTH,\n): FragmentRequestBody | null {\n const result = parseFragmentRequestResult(value, maxFieldDepth);\n return result === 'field-depth' ? null : result;\n}\n\n/** Request validation with a distinct depth refusal for endpoint diagnostics. @internal */\nexport function parseFragmentRequestResult(\n value: unknown,\n maxFieldDepth = MAX_FIELD_DEPTH,\n): FragmentRequestBody | 'field-depth' | null {\n if (!Number.isInteger(maxFieldDepth) || maxFieldDepth < 0 || maxFieldDepth > MAX_FIELD_DEPTH) {\n return null;\n }\n if (!isRecord(value)) return null;\n const fragment = value['fragment'];\n if (typeof fragment !== 'string' || !/^[a-z][a-z0-9-]{0,63}$/iu.test(fragment)) return null;\n const key = value['key'];\n if (key !== undefined && (typeof key !== 'string' || key.length > 128)) return null;\n const route = value['route'];\n if (typeof route !== 'string' || !route.startsWith('/') || route.length > 2048) return null;\n const search = value['search'];\n if (\n typeof search !== 'string' ||\n search.length > 4096 ||\n (search !== '' && !search.startsWith('?'))\n ) {\n return null;\n }\n const revision = value['revision'];\n if (typeof revision !== 'number' || !Number.isInteger(revision) || revision < 0) return null;\n const slugs: (string | undefined)[] = [];\n for (const name of ['locale', 'collectionSlug', 'globalSlug'] as const) {\n const slug = value[name];\n if (slug === undefined) {\n slugs.push(undefined);\n continue;\n }\n if (typeof slug !== 'string' || slug.length > 128) return null;\n slugs.push(slug);\n }\n const [locale, collectionSlug, globalSlug] = slugs;\n const fields = value['fields'];\n if (!isRecord(fields)) return null;\n if (depthOf(fields, 0, maxFieldDepth) > maxFieldDepth) return 'field-depth';\n return {\n fragment,\n ...(typeof key === 'string' ? { key } : {}),\n route,\n search,\n revision,\n ...(locale !== undefined ? { locale } : {}),\n ...(collectionSlug !== undefined ? { collectionSlug } : {}),\n ...(globalSlug !== undefined ? { globalSlug } : {}),\n fields,\n };\n}\n\n/** Shape check for a response body; `null` when it is not one. @internal */\nexport function parseFragmentResponse(value: unknown): FragmentResponseBody | null {\n if (!isRecord(value)) return null;\n const { html, boundary, revision, metadata } = value;\n if (typeof html !== 'string') return null;\n if (!isRecord(boundary) || typeof boundary['id'] !== 'string') return null;\n if (boundary['key'] !== undefined && typeof boundary['key'] !== 'string') return null;\n if (typeof revision !== 'number') return null;\n if (\n !isRecord(metadata) ||\n typeof metadata['renderedAt'] !== 'string' ||\n typeof metadata['renderer'] !== 'string'\n ) {\n return null;\n }\n return value as unknown as FragmentResponseBody;\n}\n","/**\n * A semaphore: at most `limit` callers run at once, the rest queue in order.\n * Cancellation only owns the wait; a running job handles its own signal.\n */\n\nexport type Gate = <T>(run: () => Promise<T>, signal?: AbortSignal) => Promise<T>;\n\ninterface Waiter {\n readonly resume: () => void;\n}\n\nfunction abortReason(signal: AbortSignal): Error {\n return signal.reason instanceof Error\n ? signal.reason\n : new DOMException('The operation was aborted.', 'AbortError');\n}\n\nfunction isAborted(signal: AbortSignal | undefined): boolean {\n return signal?.aborted === true;\n}\n\nexport function createGate(limit: number): Gate {\n let active = 0;\n const waiting: Waiter[] = [];\n // The permit passes straight to the next waiter; decrementing first would let\n // a caller arriving before the waiter wakes run as limit + 1.\n const release = (): void => {\n const next = waiting.shift();\n if (next === undefined) active -= 1;\n else next.resume();\n };\n return async (run, signal) => {\n if (signal !== undefined && isAborted(signal)) throw abortReason(signal);\n if (active < limit) {\n active += 1;\n } else {\n await new Promise<void>((resolve, reject) => {\n let dispose = (): void => undefined;\n const waiter: Waiter = {\n resume: () => {\n dispose();\n resolve();\n },\n };\n waiting.push(waiter);\n if (signal !== undefined) {\n const abort = (): void => {\n const index = waiting.indexOf(waiter);\n if (index === -1) return;\n waiting.splice(index, 1);\n dispose();\n reject(abortReason(signal));\n };\n dispose = (): void => {\n signal.removeEventListener('abort', abort);\n };\n signal.addEventListener('abort', abort, { once: true });\n if (isAborted(signal)) abort();\n }\n });\n // `release()` hands the permit over synchronously, but this continuation\n // runs later. Return it if cancellation won that gap.\n if (signal !== undefined && isAborted(signal)) {\n release();\n throw abortReason(signal);\n }\n }\n try {\n return await run();\n } finally {\n release();\n }\n };\n}\n","/**\n * The HTTP handler behind `createFragmentStrategy()`: one POST per boundary and\n * revision, shared, gated, timed out and size-bounded. Every failure is an\n * `LP08xx` outcome, never an exception. See ADR 0011.\n */\n\nimport type { DiagnosticCode } from '@core/diagnostic-codes';\nimport {\n FRAGMENT_PROTOCOL_VERSION,\n FRAGMENT_VERSION_HEADER,\n parseFragmentResponse,\n type FragmentRequestBody,\n} from '@/types/fragment-protocol';\nimport { definedOnly } from '@/types/defined-only';\nimport { errorMessage, linkedTimeout } from './abort';\nimport type {\n FragmentBoundary,\n FragmentHandler,\n FragmentOutcome,\n StrategyRequest,\n} from './boundary';\nimport { createGate } from './gate';\n\nexport interface FragmentStrategyOptions {\n /** Same-origin path of the fragment endpoint, e.g. `/payload/fragment`. */\n readonly endpoint: string;\n /** `fetch` to use; defaults to the global one. */\n readonly fetch?: (url: string, init?: RequestInit) => Promise<Response>;\n /** Per-request timeout. Default 5000 ms. */\n readonly timeoutMs?: number;\n /** Requests in flight at once; further boundaries wait. Default 4. */\n readonly maxConcurrent?: number;\n /** Largest response body accepted, in bytes. Default 512 KiB. */\n readonly maxResponseBytes?: number;\n /** Extra request headers, e.g. a CSRF token the site already uses. */\n readonly headers?: Readonly<Record<string, string>>;\n /** Where the page lives; defaults to `location`. */\n readonly location?: {\n readonly pathname: string;\n readonly search: string;\n };\n}\n\nconst DEFAULT_TIMEOUT_MS = 5_000;\nconst DEFAULT_MAX_CONCURRENT = 4;\nconst DEFAULT_MAX_RESPONSE_BYTES = 512 * 1024;\nconst SUPERSEDED: FragmentOutcome = { status: 'superseded' };\n\nfunction failed(code: DiagnosticCode, reason: string): FragmentOutcome {\n return { status: 'failed', code, reason };\n}\n\n/**\n * `endpoint` must stay on the page's origin. The URL parser reads `\\` as `/`,\n * so `/\\evil.com/x` leaves it; the resolved origin decides.\n */\nfunction sameOriginPath(endpoint: string): string {\n const refuse = (): never => {\n throw new TypeError(\n 'createFragmentStrategy: endpoint must be a same-origin path starting with \"/\"',\n );\n };\n if (\n typeof endpoint !== 'string' ||\n !endpoint.startsWith('/') ||\n endpoint.startsWith('//') ||\n endpoint.includes('\\\\')\n ) {\n refuse();\n }\n if (typeof location !== 'undefined') {\n let resolved: URL | undefined;\n try {\n resolved = new URL(endpoint, location.href);\n } catch {\n resolved = undefined;\n }\n if (resolved?.origin !== location.origin) refuse();\n }\n return endpoint;\n}\n\nfunction isByteStream(value: unknown): value is ReadableStream<Uint8Array> {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { getReader?: unknown }).getReader === 'function'\n );\n}\n\n/**\n * The body as text, or `null` past `maxBytes` — counted as bytes arrive, so an\n * oversized answer is cut off rather than buffered. An abort cancels the\n * stream, which ends a pending read instead of hanging it.\n */\nasync function readBoundedText(\n response: Response,\n maxBytes: number,\n signal: AbortSignal,\n): Promise<string | null> {\n const stream: unknown = response.body;\n if (!isByteStream(stream)) {\n const text = await response.text();\n return new TextEncoder().encode(text).byteLength > maxBytes ? null : text;\n }\n const reader = stream.getReader();\n const cancel = (): void => {\n reader.cancel().catch(() => undefined);\n };\n signal.addEventListener('abort', cancel, { once: true });\n const decoder = new TextDecoder();\n let text = '';\n let total = 0;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > maxBytes) {\n cancel();\n return null;\n }\n text += decoder.decode(value, { stream: true });\n }\n } finally {\n signal.removeEventListener('abort', cancel);\n reader.releaseLock();\n }\n return text + decoder.decode();\n}\n\n/** @internal */\nexport function createFragmentHandler(options: FragmentStrategyOptions): FragmentHandler {\n const endpoint = sameOriginPath(options.endpoint);\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const maxResponseBytes = options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;\n const gate = createGate(Math.max(1, options.maxConcurrent ?? DEFAULT_MAX_CONCURRENT));\n const inFlight = new Map<string, Promise<FragmentOutcome>>();\n const generations = new WeakMap<AbortSignal, number>();\n let nextGeneration = 0;\n\n const generationOf = (signal: AbortSignal): number => {\n const known = generations.get(signal);\n if (known !== undefined) return known;\n nextGeneration += 1;\n generations.set(signal, nextGeneration);\n return nextGeneration;\n };\n\n async function render(\n request: StrategyRequest,\n boundary: FragmentBoundary,\n ): Promise<FragmentOutcome> {\n // Read through a function: the signal flips during awaits, which narrowing cannot see.\n const superseded = (): boolean => request.signal.aborted;\n if (superseded()) return SUPERSEDED;\n const where = options.location ?? location;\n const body: FragmentRequestBody = definedOnly({\n fragment: boundary.id,\n key: boundary.key,\n route: where.pathname,\n search: where.search,\n revision: request.revision,\n locale: request.locale,\n collectionSlug: request.collectionSlug,\n globalSlug: request.globalSlug,\n fields: request.fields,\n });\n const timeout = linkedTimeout(request.signal, timeoutMs);\n const failure = (error: unknown): FragmentOutcome => {\n if (superseded()) return SUPERSEDED;\n if (timeout.timedOut()) return failed('LP0801', `timeout after ${String(timeoutMs)} ms`);\n return failed('LP0801', errorMessage(error));\n };\n let response: Response | undefined;\n let readingResponse = false;\n try {\n try {\n response = await (options.fetch ?? fetch)(endpoint, {\n method: 'POST',\n credentials: 'same-origin',\n headers: {\n ...options.headers,\n accept: 'application/json',\n 'content-type': 'application/json',\n [FRAGMENT_VERSION_HEADER]: String(FRAGMENT_PROTOCOL_VERSION),\n },\n body: JSON.stringify(body),\n signal: timeout.signal,\n });\n } catch (error) {\n return failure(error);\n }\n if (superseded()) return SUPERSEDED;\n if (response.status === 401 || response.status === 403) {\n return failed('LP0803', `endpoint refused the preview (${String(response.status)})`);\n }\n if (!response.ok) return failed('LP0801', `endpoint answered ${String(response.status)}`);\n const type = response.headers.get('content-type') ?? '';\n if (!type.toLowerCase().startsWith('application/json')) {\n return failed('LP0802', `unexpected content type \"${type}\"`);\n }\n const length = Number(response.headers.get('content-length') ?? '0');\n if (length > maxResponseBytes) return failed('LP0802', 'response exceeds the size limit');\n if (timeout.signal.aborted) return failure(timeout.signal.reason);\n let text: string | null;\n try {\n readingResponse = true;\n text = await readBoundedText(response, maxResponseBytes, timeout.signal);\n } catch (error) {\n return failure(error);\n }\n if (superseded()) return SUPERSEDED;\n if (timeout.timedOut()) return failed('LP0801', `timeout after ${String(timeoutMs)} ms`);\n if (text === null) return failed('LP0802', 'response exceeds the size limit');\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return failed('LP0802', 'response is not JSON');\n }\n const fragment = parseFragmentResponse(parsed);\n if (fragment === null) return failed('LP0802', 'response has the wrong shape');\n if (fragment.boundary.id !== boundary.id || fragment.boundary.key !== boundary.key) {\n return failed('LP0802', 'response is for another boundary');\n }\n if (fragment.revision !== request.revision) return SUPERSEDED;\n return { status: 'rendered', html: fragment.html, metadata: fragment.metadata };\n } finally {\n timeout.dispose();\n if (response !== undefined && !readingResponse) {\n // A refusal stops its producer without holding the gate on cleanup.\n try {\n response.body?.cancel().catch(() => undefined);\n } catch {\n // Cleanup from an injected response cannot replace the original outcome.\n }\n }\n }\n }\n\n return (request, boundary) => {\n // Revision numbers restart when a runtime is recreated. Its abort signal is\n // the internal generation identity, so an old request cannot be shared with\n // a new document/owner session that happens to use the same number.\n const dedupeKey = JSON.stringify([\n generationOf(request.signal),\n boundary.id,\n boundary.key,\n request.revision,\n ]);\n const shared = inFlight.get(dedupeKey);\n if (shared !== undefined) return shared;\n let started = false;\n const promise = gate(() => {\n started = true;\n return render(request, boundary);\n }, request.signal).catch((error: unknown): FragmentOutcome => {\n if (!started && request.signal.aborted) return SUPERSEDED;\n throw error;\n });\n inFlight.set(dedupeKey, promise);\n const forget = (): void => {\n if (inFlight.get(dedupeKey) === promise) inFlight.delete(dedupeKey);\n };\n void promise.then(forget, forget);\n return promise;\n };\n}\n","/** Drops `undefined`-valued keys, so a literal built from optional inputs satisfies `exactOptionalPropertyTypes`. */\n\nexport type DefinedOnly<T> = { readonly [K in keyof T]-?: Exclude<T[K], undefined> };\n\nexport function definedOnly<T extends object>(source: T): DefinedOnly<T> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(source)) {\n if (value !== undefined) result[key] = value;\n }\n return result as DefinedOnly<T>;\n}\n","/**\n * Every diagnostic code the runtime and tools emit, so a consumer can branch\n * on `e.code` instead of matching message text. A code is never reused for a\n * different meaning; a retired one stays reserved.\n *\n * Browser-side reporting sites write the literal (`code: 'LP0603'`) rather than\n * reading it from this record: the frozen table would then ship in the inline\n * runtime for the sake of six characters — it did, 1 287 B in every page, for\n * the one read Z6 left in the strategy runner, and `diagnostic-codes.test.ts`\n * now holds the artifact free of it. Server-side tools may import it.\n */\n\n/**\n * Every diagnostic code the runtime can report, by name.\n *\n * ```ts\n * client.events.on('error', (e) => {\n * if (e.code === DIAGNOSTIC_CODES.TransformThrew) reportToSentry(e.error);\n * });\n * ```\n */\nexport const DIAGNOSTIC_CODES = Object.freeze({\n /** No trusted origin configured in production; nothing will be accepted. */\n NoTrustedOrigin: 'LP0101',\n /** Origin trust rests on `document.referrer`, so any framing site is trusted. */\n ReferrerOnlyTrust: 'LP0102',\n /** A plugin declares a runtime range this runtime does not satisfy; it was refused. */\n PluginIncompatible: 'LP0103',\n /** The page carries the lean runtime and needs a feature that profile leaves out. */\n ProfileFeatureOmitted: 'LP0104',\n\n /** An update named an empty field with no binding anchor on the page; an edit to it has nowhere to land. */\n OrphanField: 'LP0201',\n /** Owner scoping is on and the update names no document it could belong to. */\n UnattributableUpdate: 'LP0202',\n /** An update carried a value for a field the page binds nowhere; editing it changes nothing on this page. */\n UnboundField: 'LP0203',\n\n /** The visibility gate held offscreen writes back until they scroll into view. */\n VisibilityGateDeferred: 'LP0301',\n\n /** A value was refused because the attribute or the value itself is unsafe. */\n UnsafeAttributeWrite: 'LP0401',\n /** A text element has structured children, so replacing its text was skipped. */\n TextTargetHasChildren: 'LP0402',\n /** A structural container has no array template, so the update was skipped. */\n MissingArrayTemplate: 'LP0403',\n /** A structural item has no `id`; it pairs positionally, so an insert re-renders every row after it. */\n StructuralItemUnkeyed: 'LP0404',\n /** Two structural items share a key; later ones pair positionally. */\n StructuralDuplicateKey: 'LP0405',\n /** Every structural key changed while the length did not; the source generates keys per message. */\n StructuralUnstableKeys: 'LP0406',\n /** A binding asks for a delivery strategy this release does not have; it is left unchanged. */\n UnsupportedStrategy: 'LP0407',\n /** `data-payload-format` names a format the vocabulary does not contain; the default formatting was used instead. */\n UnknownValueFormat: 'LP0408',\n /** The strict sanitizer removed an attribute the 1.x `'compat'` policy kept. */\n SanitizerDroppedAttribute: 'LP0409',\n /** A Lexical block has no renderer; the markup the server rendered for it is kept instead. */\n UnrenderedBlockKept: 'LP0410',\n /** A patch cannot reach what the server would have drawn; `onUnfaithfulPatch` decided what happens next. */\n UnfaithfulPatch: 'LP0411',\n /** The first write to a date, number or checkbox binding replaced a different reading of the same value. */\n ServerFormatReplaced: 'LP0412',\n /** A Lexical block has no renderer, and the write could not keep the markup the server rendered for it; it is gone. */\n UnrenderedBlockLost: 'LP0413',\n\n /** A message was rejected before it reached the update pipeline. */\n MessageRejected: 'LP0501',\n /** A preview token was rejected. */\n TokenRejected: 'LP0502',\n /** A trusted origin sent a message this protocol version does not recognise. */\n ProtocolShapeUnknown: 'LP0503',\n\n /** A consumer event handler threw. */\n HandlerThrew: 'LP0601',\n /** A consumer transform threw; the original value was kept. */\n TransformThrew: 'LP0602',\n /** A renderer threw while writing a value. */\n RendererThrew: 'LP0603',\n // LP0604 is reserved and unassigned; codes are never reused or renumbered.\n /** Runtime startup failed. */\n StartupFailed: 'LP0605',\n /** Sending the ready handshake failed. */\n ReadyFailed: 'LP0606',\n /** The page declared hydration, but no React commit, Vue mount or SvelteKit mount came within the cap; the runtime started without waiting. */\n HydrationWaitTimedOut: 'LP0607',\n\n /** The audit found no runtime in a response that carried preview intent. */\n AuditRuntimeMissing: 'LP0701',\n /** The preview response declares no `frame-ancestors`. */\n AuditNoFrameAncestors: 'LP0702',\n /** `X-Frame-Options` forbids framing on a response that declares no `frame-ancestors`; `SAMEORIGIN` forbids it from another origin only. */\n AuditFrameOptionsBlocks: 'LP0703',\n /** Binding attributes are served to anonymous visitors. */\n AuditBindingsExposed: 'LP0704',\n /** More bindings than the visibility gate will write eagerly. */\n AuditGateThresholdExceeded: 'LP0705',\n /** Owner markers exist, but some bindings are outside all of them. */\n AuditUnownedBindings: 'LP0706',\n /** The preview response carries no bindings and no fragment boundary. */\n AuditNoBindings: 'LP0707',\n /** The URL did not return an HTML page, so nothing else can be judged. */\n AuditNotAPage: 'LP0708',\n /** A fragment request failed (network, timeout, server error); the boundary was patched instead. */\n FragmentRequestFailed: 'LP0801',\n /** The fragment response had the wrong content type or shape; the boundary was patched instead. */\n FragmentResponseInvalid: 'LP0802',\n /** The fragment endpoint refused the preview (not authorized); the boundary was patched instead. */\n FragmentUnauthorized: 'LP0803',\n /** A fragment response arrived for a revision that was already superseded and was discarded. */\n FragmentSuperseded: 'LP0804',\n /** A route refresh was refused: the loop guard, or the strategy's minimum interval. */\n RouteRefreshLoop: 'LP0805',\n /** A boundary asks for the fragment strategy but no fragment handler is configured; it is patched. */\n FragmentStrategyUnavailable: 'LP0806',\n /** A revision changed a field with no binding; `onUnfaithfulPatch` refreshed the route. */\n UnboundChangeRefresh: 'LP0807',\n /** `onUnfaithfulPatch: 'escalate'` found no route or fragment strategy, or no boundary, to hand an unfaithful patch to; the page keeps it. */\n EscalationUnavailable: 'LP0808',\n /** A fragment inserted an Astro island the page cannot start: it never loaded the island element or that client directive. */\n IslandStartUnavailable: 'LP0809',\n /** The host's route refresh returned no promise, so the revision was re-applied before the host committed. */\n RouteRefreshUnconfirmed: 'LP0810',\n /** A readiness row is not yet at its 2.0 value; `pll doctor --v2` reports it. */\n V2ReadinessGap: 'LP0709',\n /** The preview runtime is served to anonymous visitors, not only inside the admin frame. */\n RuntimeOnPublicPage: 'LP0710',\n} as const);\n\n/** A diagnostic code, as it appears in log output and on the `error` event. */\nexport type DiagnosticCode = (typeof DIAGNOSTIC_CODES)[keyof typeof DIAGNOSTIC_CODES];\n","/**\n * Keyed DOM morph, the engine: edits a live element toward a freshly rendered\n * one while retaining compatible, paired live nodes. Those nodes keep their\n * live properties; an incompatible or unpaired node is replaced and loses\n * them. It pairs children by key or position, synchronises attributes with the\n * state exception and keeps focus across a retained move; which subtrees it\n * never enters is the coordinator's rule, handed in as\n * `MorphOptions.boundary` and defaulting to the package's (`isMorphBoundary`\n * in islands.ts). See ADR 0008, §9 for the line between the two. A custom\n * element is such a boundary unless both of its sides carry\n * `data-payload-morph` (§10).\n */\n\nimport { isMorphBoundary } from './islands';\n\n/** Attributes the CMS controls only when the template names them (ADR 0008 §3). */\nconst STATE_ATTRIBUTES: ReadonlySet<string> = new Set(['open', 'value', 'checked', 'selected']);\n\n/** @beta */\nexport interface MorphOptions {\n /** Attributes that key child elements for pairing, tried in order; unkeyed children pair by position. */\n readonly keyAttributes: readonly string[];\n /** Called once per parent when two children share a key; the later ones pair by position. */\n readonly onDuplicateKey?: (parent: Element, key: string) => void;\n /** Elements whose attributes are synchronised but whose children are left alone (nested structural slots). */\n readonly retainChildrenOf?: (live: Element, rendered: Element) => boolean;\n /**\n * The ownership rule: a structurally compatible live/rendered pair for\n * which this returns `true` on both sides is retained whole and never\n * entered (ADR 0008 §4). The package's rule — custom elements, islands,\n * `contenteditable`, `data-payload-owned` — is the default. A supplied rule\n * replaces that default; compose it with the exported `isMorphBoundary`\n * predicate to add package-compatible boundaries.\n */\n readonly boundary?: (element: Element) => boolean;\n /**\n * Decides a live/rendered pair that are both boundaries: `true` keeps the\n * live one, after any handoff to its owner; `false` replaces it with the\n * rendered one. Without it every such pair is kept whole (ADR 0008 §4). The\n * package's coordinators hand in `retainIslandBoundary`, under which an\n * Astro island that still renders the same component takes the rendered\n * props through Astro's own handoff (ADR 0021).\n */\n readonly retainBoundary?: (live: Element, rendered: Element) => boolean;\n}\n\n/** Empty attribute values (boolean markers such as `data-payload-island`) are not keys. */\nfunction keyOf(element: Element, options: MorphOptions): string | undefined {\n for (const attribute of options.keyAttributes) {\n const value = element.getAttribute(attribute);\n if (value !== null && value.length > 0) return `${attribute}=${value}`;\n }\n return undefined;\n}\n\n/** Whether `live` can be edited toward `rendered` instead of being replaced by it, under `boundary` (the package's rule by default). @internal */\nexport function isMorphCompatible(\n live: Element,\n rendered: Element,\n boundary: (element: Element) => boolean = isMorphBoundary,\n): boolean {\n return (\n live.tagName === rendered.tagName &&\n live.namespaceURI === rendered.namespaceURI &&\n !boundary(live) &&\n !boundary(rendered)\n );\n}\n\n/**\n * Morph `live` toward `rendered`. Returns `live` when it was retained, or\n * `rendered` when the two are incompatible and the caller must replace.\n * `rendered` is consumed: its children may move into `live`.\n *\n * @beta\n */\nexport function morphElement(live: Element, rendered: Element, options: MorphOptions): Element {\n if (!sameKind(live, rendered)) return rendered;\n const boundary = options.boundary ?? isMorphBoundary;\n const liveBoundary = boundary(live);\n const renderedBoundary = boundary(rendered);\n if (liveBoundary || renderedBoundary) {\n if (!liveBoundary || !renderedBoundary) return rendered;\n return (options.retainBoundary?.(live, rendered) ?? true) ? live : rendered;\n }\n const focus = captureFocus(live);\n syncAttributes(live, rendered);\n if (options.retainChildrenOf?.(live, rendered) !== true) morphChildren(live, rendered, options);\n restoreFocus(focus);\n return live;\n}\n\n/** Match attributes, except that state-bearing ones change only when `rendered` carries them. */\nexport function syncAttributes(live: Element, rendered: Element): void {\n for (const attribute of Array.from(live.attributes)) {\n if (rendered.hasAttribute(attribute.name)) continue;\n if (STATE_ATTRIBUTES.has(attribute.name.toLowerCase())) continue;\n live.removeAttribute(attribute.name);\n }\n for (const attribute of Array.from(rendered.attributes)) {\n if (live.getAttribute(attribute.name) !== attribute.value) {\n live.setAttribute(attribute.name, attribute.value);\n }\n }\n}\n\nfunction morphChildren(live: Element, rendered: Element, options: MorphOptions): void {\n const liveKeyed = indexKeyed(live, options);\n const keyedElements = new Set<Node>(liveKeyed.values());\n const liveUnkeyed = Array.from(live.childNodes).filter((node) => !keyedElements.has(node));\n const claimed = new Set<Node>();\n const usedKeys = new Set<string>();\n const renderedNodes = Array.from(rendered.childNodes);\n let cursor: Node | null = live.firstChild;\n let unkeyedIndex = 0;\n\n for (let i = 0; i < renderedNodes.length; i += 1) {\n const next = renderedNodes[i] as Node;\n let kept: Node | null = null;\n let key = next instanceof Element ? keyOf(next, options) : undefined;\n // A repeated key on the rendered side pairs positionally like a live duplicate.\n if (key !== undefined && usedKeys.has(key)) key = undefined;\n if (key !== undefined) {\n usedKeys.add(key);\n const candidate = liveKeyed.get(key);\n if (candidate !== undefined && !claimed.has(candidate)) {\n kept = reconcile(candidate, next, options) === candidate ? candidate : null;\n if (kept === null) {\n candidate.replaceWith(next);\n claimed.add(next);\n cursor = next.nextSibling;\n continue;\n }\n }\n } else {\n while (unkeyedIndex < liveUnkeyed.length) {\n const candidate = liveUnkeyed[unkeyedIndex] as Node;\n unkeyedIndex += 1;\n if (claimed.has(candidate)) continue;\n if (sameKind(candidate, next)) {\n kept = reconcile(candidate, next, options) === candidate ? candidate : null;\n if (kept === null) {\n live.replaceChild(next, candidate);\n claimed.add(next);\n cursor = next.nextSibling;\n }\n break;\n }\n if (candidate instanceof Element) {\n // `next` is a text/comment, or an element whose kind the following\n // rendered node matches: an insertion, so the live element stays for it.\n const following = renderedNodes[i + 1];\n if (\n !(next instanceof Element) ||\n (following !== undefined && sameKind(candidate, following))\n ) {\n unkeyedIndex -= 1;\n break;\n }\n live.replaceChild(next, candidate);\n claimed.add(next);\n cursor = next.nextSibling;\n break;\n }\n if (!(next instanceof Element)) {\n // Text vs comment: replace rather than rewrite one node type's value into the other.\n live.replaceChild(next, candidate);\n claimed.add(next);\n cursor = next.nextSibling;\n break;\n }\n // A surplus live text/comment before the element `next` needs; removed below.\n }\n if (kept === null && claimed.has(next)) continue;\n }\n\n const node: Node = kept ?? next;\n claimed.add(node);\n // Whitespace-only text the template did not render is surplus. Stepping the\n // cursor over it keeps a retained element where it is: `insertBefore` on a\n // node already in the tree is a remove-and-insert, which blurs a focused input.\n while (cursor !== null && cursor !== node && !claimed.has(cursor) && isBlankText(cursor)) {\n cursor = cursor.nextSibling;\n }\n if (node !== cursor) live.insertBefore(node, cursor);\n cursor = node.nextSibling;\n }\n\n for (const child of Array.from(live.childNodes)) {\n if (!claimed.has(child)) child.remove();\n }\n}\n\n/** Retain `candidate` when it can be edited toward `next`; otherwise report `next`. */\nfunction reconcile(candidate: Node, next: Node, options: MorphOptions): Node {\n if (candidate instanceof Element && next instanceof Element) {\n return morphElement(candidate, next, options);\n }\n if (candidate.nodeValue !== next.nodeValue) candidate.nodeValue = next.nodeValue;\n return candidate;\n}\n\nfunction isBlankText(node: Node): boolean {\n return node.nodeType === Node.TEXT_NODE && (node.nodeValue ?? '').trim().length === 0;\n}\n\nfunction sameKind(a: Node, b: Node): boolean {\n if (a.nodeType !== b.nodeType) return false;\n if (a instanceof Element && b instanceof Element) {\n return a.tagName === b.tagName && a.namespaceURI === b.namespaceURI;\n }\n return true;\n}\n\n/** First element per key; later duplicates are left as they are and pair positionally. */\nfunction indexKeyed(parent: Element, options: MorphOptions): Map<string, Element> {\n const keyed = new Map<string, Element>();\n let duplicate: string | undefined;\n for (const child of Array.from(parent.children)) {\n const key = keyOf(child, options);\n if (key === undefined) continue;\n if (keyed.has(key)) {\n duplicate ??= key.slice(key.indexOf('=') + 1);\n continue;\n }\n keyed.set(key, child);\n }\n if (duplicate !== undefined) options.onDuplicateKey?.(parent, duplicate);\n return keyed;\n}\n\n/** @internal */\nexport interface FocusSnapshot {\n readonly element: HTMLElement;\n readonly selection: readonly [number | null, number | null] | null;\n}\n\n/**\n * A keyed move is a remove-and-insert, which blurs; remember what had focus.\n * The structural applier moves items itself and uses the same pair around\n * its commit, so a move it makes keeps focus the way a move the morph makes\n * does (ADR 0008 §1). @internal\n */\nexport function captureFocus(live: Element): FocusSnapshot | null {\n const active = live.ownerDocument.activeElement;\n if (!(active instanceof HTMLElement) || !live.contains(active)) return null;\n const selection =\n active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement\n ? ([active.selectionStart, active.selectionEnd] as const)\n : null;\n return { element: active, selection };\n}\n\n/** @internal */\nexport function restoreFocus(snapshot: FocusSnapshot | null): void {\n if (snapshot === null) return;\n const { element, selection } = snapshot;\n if (element.ownerDocument.activeElement === element || !element.isConnected) return;\n try {\n element.focus({ preventScroll: true });\n const [start, end] = selection ?? [null, null];\n if (start !== null && end !== null) {\n (element as HTMLInputElement).setSelectionRange(start, end);\n }\n } catch {\n // Not focusable any more; nothing to restore.\n }\n}\n","/**\n * The route strategy: when a revision touches what no smaller strategy can\n * render, ask the host's own router to re-render it, or else fetch the route\n * again and morph it in place. A refresh inside `minIntervalMs` is refused as\n * LP0805 and handed back for a trailing run. See ADR 0011.\n */\n\nimport {\n astroIslandsIn,\n isMorphBoundary,\n OWNED_ATTRIBUTE,\n releaseDisconnectedIslands,\n retainIslandBoundary,\n} from '@core/islands';\nimport { morphElement } from '@core/morph';\nimport { KEY_ATTRIBUTE } from '@core/structural-applier';\nimport { parseDependencyList } from '@core/dependencies';\nimport { readRouteRefresh } from '@core/route-refresh';\nimport { FRAGMENT_ATTRIBUTE, type RouteContext, type RouteStrategy } from '@core/strategies';\nimport { errorMessage, linkedTimeout } from './abort';\nimport { FRAGMENT_KEY_ATTRIBUTE } from './boundary';\n\nconst STRATEGY_ATTRIBUTE = 'data-payload-strategy';\nconst FIELD_ATTRIBUTE = 'data-payload-field';\nconst DEPENDS_ATTRIBUTE = 'data-payload-depends';\n/** Sent with every refresh so a server can tell a preview refresh from navigation. @internal */\nexport const ROUTE_REFRESH_HEADER = 'x-payload-live-preview';\n\nexport interface RouteStrategyOptions {\n /** `fetch` to use; defaults to the global one. */\n readonly fetch?: (url: string, init?: RequestInit) => Promise<Response>;\n /** Per-refresh timeout. Default 8000 ms. */\n readonly timeoutMs?: number;\n /** Shortest gap between two refreshes; one inside it is refused with LP0805 and run when the gap closes. Default 1000 ms. */\n readonly minIntervalMs?: number;\n /** The document to refresh; defaults to `document`. */\n readonly document?: Document;\n /** Where the page lives; defaults to `location`. */\n readonly location?: { readonly href: string };\n /** Scroll access; defaults to `window`. */\n readonly window?: {\n readonly scrollX: number;\n readonly scrollY: number;\n readonly scrollTo: (x: number, y: number) => void;\n };\n}\n\nconst DEFAULT_TIMEOUT_MS = 8_000;\nconst UNCONFIRMED_REFRESH =\n 'route refresh returned no promise; re-applied before the host committed (see LivePreviewRouteRefresh)';\n\nconst DEFAULT_MIN_INTERVAL_MS = 1_000;\n\n/** An explicit `data-payload-strategy=\"route\"`, or a binding in `<head>`. @internal */\nexport function isRouteBound(element: Element): boolean {\n const explicit = element.getAttribute(STRATEGY_ATTRIBUTE);\n if (explicit !== null) return explicit === 'route';\n return element.closest('head') !== null;\n}\n\nfunction fieldsOf(element: Element): readonly string[] {\n const own = element.getAttribute(FIELD_ATTRIBUTE);\n const depends = parseDependencyList(element.getAttribute(DEPENDS_ATTRIBUTE));\n return own === null || own.length === 0 ? depends : [own, ...depends];\n}\n\nfunction isManagedHeadElement(element: Element): boolean {\n if (element.hasAttribute(OWNED_ATTRIBUTE)) return false;\n if (element.tagName === 'META') {\n return element.hasAttribute('name') || element.hasAttribute('property');\n }\n return element.tagName === 'LINK' && element.getAttribute('rel') === 'canonical';\n}\n\n/**\n * Make `<title>` and the ordered direct children this strategy manages match\n * the fresh head, including duplicates and removals. A tag marked\n * `data-payload-owned` belongs to a script and is left alone.\n */\nfunction syncHead(live: Document, fresh: Document): void {\n const liveTitle = Array.from(live.head.children).find((element) => element.tagName === 'TITLE');\n const freshTitle = Array.from(fresh.head.children).find((element) => element.tagName === 'TITLE');\n if (\n !liveTitle?.hasAttribute(OWNED_ATTRIBUTE) &&\n !freshTitle?.hasAttribute(OWNED_ATTRIBUTE) &&\n fresh.title !== live.title\n ) {\n live.title = fresh.title;\n }\n\n const liveSlots = Array.from(live.head.children).filter(isManagedHeadElement);\n const targets = Array.from(fresh.head.children).filter(isManagedHeadElement);\n const paired = Math.min(liveSlots.length, targets.length);\n liveSlots.slice(0, paired).forEach((current, index) => {\n const target = targets[index];\n if (target === undefined) return;\n if (current.tagName === target.tagName) {\n for (const attribute of Array.from(current.attributes)) {\n if (!target.hasAttribute(attribute.name)) current.removeAttribute(attribute.name);\n }\n for (const attribute of Array.from(target.attributes)) {\n if (current.getAttribute(attribute.name) !== attribute.value) {\n current.setAttribute(attribute.name, attribute.value);\n }\n }\n return;\n }\n const replacement = live.importNode(target, true);\n current.replaceWith(replacement);\n liveSlots[index] = replacement;\n });\n\n let anchor = liveSlots.at(-1);\n for (const target of targets.slice(liveSlots.length)) {\n const addition = live.importNode(target, true);\n if (anchor === undefined) live.head.append(addition);\n else anchor.after(addition);\n anchor = addition;\n }\n for (const surplus of liveSlots.slice(targets.length)) {\n surplus.remove();\n }\n}\n\nexport function createRouteStrategy(options: RouteStrategyOptions = {}): RouteStrategy {\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const minIntervalMs = options.minIntervalMs ?? DEFAULT_MIN_INTERVAL_MS;\n let lastRefreshAt = -Infinity;\n let saidUnconfirmed = false;\n\n return {\n plan(root, changedFields, context) {\n for (const element of root.querySelectorAll(\n `[${FIELD_ATTRIBUTE}], [${STRATEGY_ATTRIBUTE}=\"route\"]`,\n )) {\n if (!isRouteBound(element) || context?.inScope(element) === false) continue;\n const fields = fieldsOf(element);\n if (fields.length === 0 || fields.some((field) => changedFields.has(field))) return true;\n }\n return false;\n },\n async refresh(context: RouteContext) {\n const now = Date.now();\n const waitMs = minIntervalMs - (now - lastRefreshAt);\n if (waitMs > 0) {\n // The window paces the server; it does not decide what the editor gets\n // to see. Handing the request back means the change that ended a burst\n // still arrives, instead of waiting for a keystroke that never comes.\n context.retryAfter?.(waitMs);\n context.log(\n 'LP0805',\n `route refresh for revision ${String(context.revision)} refused: the previous one was ${String(now - lastRefreshAt)} ms ago`,\n );\n return 'refused';\n }\n lastRefreshAt = now;\n const live = options.document ?? document;\n const where = options.location ?? location;\n const view = options.window ?? window;\n const timeout = linkedTimeout(context.signal, timeoutMs);\n const failure = (error: unknown): 'failed' | 'superseded' => {\n if (context.signal.aborted) return 'superseded';\n // A timeout surfaces as an AbortError whose text differs per browser.\n const detail = timeout.timedOut()\n ? `route refresh timed out after ${String(timeoutMs)} ms`\n : errorMessage(error);\n context.log('LP0801', detail);\n return 'failed';\n };\n try {\n const hostRefresh = readRouteRefresh();\n if (hostRefresh !== undefined) {\n // The host owns this DOM: it re-renders the route itself, so there is\n // no second HTML request and no morph over a reconciler's own nodes.\n // Awaited, because the runtime re-applies the revision straight after.\n // A host that returns nothing cannot be waited for (ADR 0028).\n try {\n const settled: unknown = hostRefresh();\n if (\n typeof (settled as { then?: unknown } | null | undefined)?.then !== 'function' &&\n !saidUnconfirmed\n ) {\n saidUnconfirmed = true;\n context.log('LP0810', UNCONFIRMED_REFRESH);\n }\n await settled;\n } catch (error) {\n return failure(error);\n }\n return context.isCurrent() ? 'partial' : 'superseded';\n }\n let response: Response;\n try {\n response = await (options.fetch ?? fetch)(where.href, {\n method: 'GET',\n credentials: 'same-origin',\n cache: 'no-store',\n headers: { accept: 'text/html', [ROUTE_REFRESH_HEADER]: 'route' },\n signal: timeout.signal,\n });\n } catch (error) {\n return failure(error);\n }\n if (context.signal.aborted) return 'superseded';\n if (!response.ok || !(response.headers.get('content-type') ?? '').includes('text/html')) {\n context.log(\n 'LP0802',\n `route answered ${String(response.status)} ${response.headers.get('content-type') ?? ''}`,\n );\n return 'failed';\n }\n try {\n const html = await response.text();\n if (!context.isCurrent()) return 'superseded';\n const fresh = new DOMParser().parseFromString(html, 'text/html');\n const x = view.scrollX;\n const y = view.scrollY;\n syncHead(live, fresh);\n // Top-level boundaries pair by identity so a comment or a runtime-added\n // node cannot mispair them; fragment boundaries keep their children —\n // the fragment strategy re-renders those for this revision and must\n // not lose the focused input inside one.\n const islands = astroIslandsIn(live.body);\n morphElement(live.body, fresh.body, {\n boundary: isMorphBoundary,\n retainBoundary: retainIslandBoundary,\n keyAttributes: [KEY_ATTRIBUTE, FRAGMENT_KEY_ATTRIBUTE, FRAGMENT_ATTRIBUTE],\n retainChildrenOf: (element) => element.hasAttribute(FRAGMENT_ATTRIBUTE),\n });\n releaseDisconnectedIslands(islands);\n view.scrollTo(x, y);\n return 'partial';\n } catch (error) {\n return failure(error);\n }\n } finally {\n timeout.dispose();\n }\n },\n };\n}\n","/**\n * `payload-live-preview/fragment` — the fragment and route strategies. The\n * core carries only the seam (`@core/strategies`); this entry owns the\n * orchestration. A request it sends identifies the page by its own URL and a\n * boundary by its registry id; it never carries code, templates, import paths\n * or file paths. See ADR 0011.\n */\n\nimport type { FragmentContext, FragmentReport, FragmentStrategy } from '@core/strategies';\nimport { errorMessage } from './abort';\nimport {\n collectFragmentBoundaries,\n describeBoundary,\n type FragmentHandler,\n type FragmentOutcome,\n type StrategyRequest,\n} from './boundary';\nimport { createFragmentHandler, type FragmentStrategyOptions } from './handler';\n\nexport type {\n FragmentContext,\n FragmentReport,\n FragmentStrategy,\n RouteContext,\n RouteOutcome,\n RoutePlanContext,\n RouteStrategy,\n StrategyHandlers,\n UpdateSource,\n} from '@core/strategies';\nexport { FRAGMENT_ATTRIBUTE, resolveStrategy } from '@core/strategies';\nexport { DIAGNOSTIC_CODES, type DiagnosticCode } from '@core/diagnostic-codes';\nexport {\n collectFragmentBoundaries,\n describeBoundary,\n FRAGMENT_KEY_ATTRIBUTE,\n type FragmentBoundary,\n type FragmentHandler,\n type FragmentOutcome,\n type StrategyRequest,\n} from './boundary';\nexport { createFragmentHandler, type FragmentStrategyOptions } from './handler';\nexport {\n createRouteStrategy,\n isRouteBound,\n ROUTE_REFRESH_HEADER,\n type RouteStrategyOptions,\n} from './route';\nexport {\n FRAGMENT_PROTOCOL_VERSION,\n FRAGMENT_VERSION_HEADER,\n parseFragmentRequest,\n parseFragmentResponse,\n type FragmentRequestBody,\n type FragmentResponseBody,\n} from '@/types/fragment-protocol';\n\n/**\n * The strategy the runtime drives, built on a per-boundary handler: plan the\n * boundaries, render each, morph a success, patch a failure. A late or\n * aborted render is dropped, never applied.\n * @internal\n */\nexport function fragmentStrategyFrom(handler: FragmentHandler): FragmentStrategy {\n return {\n plan: (root, changedFields) =>\n collectFragmentBoundaries(root, changedFields).map((boundary) => boundary.element),\n async render(context: FragmentContext, elements: readonly Element[]): Promise<FragmentReport> {\n const report = { rendered: 0, failed: 0, superseded: 0 };\n const request: StrategyRequest = {\n revision: context.revision,\n receivedAt: context.receivedAt,\n fields: context.fields,\n locale: context.locale,\n collectionSlug: context.collectionSlug,\n globalSlug: context.globalSlug,\n signal: context.signal,\n };\n await Promise.all(\n elements.map(async (element) => {\n const boundary = describeBoundary(element);\n if (boundary === null) return;\n let outcome: FragmentOutcome;\n try {\n outcome = await handler(request, boundary);\n } catch (error) {\n outcome = {\n status: 'failed',\n code: 'LP0801',\n reason: errorMessage(error),\n };\n }\n if (!context.isCurrent() || outcome.status === 'superseded') {\n report.superseded += 1;\n context.log(\n 'LP0804',\n `fragment \"${boundary.id}\" for revision ${String(request.revision)} discarded as superseded`,\n );\n return;\n }\n if (outcome.status === 'rendered') {\n context.morph(element, outcome.html);\n context.rendered(element, boundary.id, boundary.key);\n report.rendered += 1;\n return;\n }\n context.failed(element, boundary.id, boundary.key, outcome.code, outcome.reason);\n if (context.isCurrent()) context.patch(element);\n report.failed += 1;\n }),\n );\n return report;\n },\n };\n}\n\n/** The strategy for a fragment endpoint: hand it to the runtime as `strategies.fragment`. */\nexport function createFragmentStrategy(options: FragmentStrategyOptions): FragmentStrategy {\n return fragmentStrategyFrom(createFragmentHandler(options));\n}\n","/**\n * The seam between a host framework's own router and the route strategy.\n *\n * The route strategy answers a revision by fetching the route and morphing it\n * into the living page. On a page whose DOM belongs to a reconciler that is\n * working against the framework, and it was seen to break once: `removeChild`\n * on `null` inside React's commit phase, over markup this package had patched.\n * A host that re-renders the route itself does the same job without the morph\n * and without the second HTML request, so it is given the chance to.\n *\n * Why a named slot on `window` and not a reference: the runtime is an inline\n * script that builds its strategy before any application code has hydrated, so\n * there is nothing to hand a component; `window.__livePreview`, the only other\n * public surface, is created later and not at all outside a preview; and the\n * two richer extension points — plugins and events — are reachable only through\n * the programmatic client, which a page using an adapter does not have. One\n * slot, written by whoever mounts and read at the moment of a refresh, is the\n * smallest thing that survives both orders of arrival.\n */\n\n/**\n * A host's own route refresh. It must settle once the fresh markup is\n * committed: the runtime re-applies the revision on top of it, and doing that\n * against the markup being replaced would write into nodes about to be dropped.\n * A function that returns nothing is taken as already committed; the route\n * strategy says once that it could not wait (LP0810), and 3.0 requires the\n * promise (ADR 0028).\n */\nexport type RouteRefresh = () => void | Promise<void>;\n\n/** Read late, so it does not matter whether the runtime or the component came first. */\nconst SLOT = '__livePreviewRouteRefresh';\n\ntype WindowSlot = Record<string, RouteRefresh | undefined>;\n\nfunction slot(): WindowSlot | undefined {\n return typeof window === 'undefined' ? undefined : (window as unknown as WindowSlot);\n}\n\n/**\n * Lend the runtime the host's route refresh; the return value undoes it, for a\n * component's cleanup. A later registration wins, and undoing one that has\n * already been replaced does nothing — two components on one page would\n * otherwise take the slot from each other on unmount.\n */\nexport function registerRouteRefresh(refresh: RouteRefresh): () => void {\n const target = slot();\n if (target === undefined) return () => undefined;\n target[SLOT] = refresh;\n return () => {\n // Cleared rather than deleted: the slot is read through `?.` anyway, and\n // removing the key buys nothing a lint rule would not have to be argued out of.\n if (target[SLOT] === refresh) target[SLOT] = undefined;\n };\n}\n\n/** The registered refresh, or `undefined` on a page that registered none. */\nexport function readRouteRefresh(): RouteRefresh | undefined {\n return slot()?.[SLOT];\n}\n","/**\n * Applies an `ArrayPatch[]` diff to a container as keyed DOM operations\n * instead of rebuilding the list, so consumer state on surviving children is\n * kept. Items are paired by `data-payload-key` (the item `id`); items without\n * one pair by position. Nested arrays reconcile recursively through\n * `data-payload-nested-key` slots. See ADR 0008.\n */\n\nimport { sanitizeHtmlWithPolicy, type SanitizerPolicyMode } from '@security/sanitizer';\nimport { trustedHtml } from '@security/trusted-types';\nimport {\n collectTemplateKeys,\n inheritItemAttributes,\n interpolateArrayTemplate,\n sharedItemAttributes,\n type InheritedAttribute,\n} from './array-template';\nimport { safeStringify } from '@field-types/utils';\nimport { diffArray, type ArrayPatch } from '@schema/diff';\nimport { isMorphBoundary, retainIslandBoundary } from './islands';\nimport { captureFocus, restoreFocus, morphElement } from './morph';\nimport { templateSanitizeOptions } from './template-sanitize';\n\n/** @internal */\nexport const KEY_ATTRIBUTE = 'data-payload-key';\nconst NESTED_KEY_ATTRIBUTE = 'data-payload-nested-key';\nconst NESTED_TEMPLATE_ATTRIBUTE = 'data-payload-nested-template';\n\n/** Last value seen per item key, per container; owned by the caller so instances never share it. @internal */\nexport type StructuralStore = WeakMap<Element, Map<string, unknown>>;\n\n/** @internal */\nexport function createStructuralStore(): StructuralStore {\n return new WeakMap<Element, Map<string, unknown>>();\n}\n\n/** @internal */\nexport interface StructuralApplyOptions {\n readonly template: string;\n readonly container: Element;\n readonly patches: readonly ArrayPatch[];\n /** The full new array; inserts and replaces render by index from it. */\n readonly nextItems: readonly unknown[];\n /** Pass the same store across renders so nested arrays diff incrementally. */\n readonly store: StructuralStore;\n /** Re-render existing items even when unchanged. */\n readonly forceRender?: boolean;\n /** The instance's policy for item markup; absent, the process default applies. */\n readonly sanitizerPolicy?: SanitizerPolicyMode | undefined;\n /** Edit a changed item in place instead of replacing it. Default `true`. */\n readonly morph?: boolean;\n /** Reported once per container when two items share a key (`LP0405`). */\n readonly onDuplicateKey?: (container: Element, key: string) => void;\n}\n\n/**\n * Returns `true` for a DOM mutation, `false` for an already-current tree, and\n * `null` when an item's markup has no sanitized element root — preflighted\n * before any mutation, so the previous DOM and memory stay intact.\n * @internal\n */\nexport function applyStructuralPatches(options: StructuralApplyOptions): boolean | null {\n const plan = prepareStructuralPlan(options.container, options.patches, options);\n if (plan === null) return null;\n return commitStructuralPlan(plan, options.store, {\n morph: options.morph ?? true,\n onDuplicateKey: options.onDuplicateKey,\n });\n}\n\n// Plan records are tuples: many are created per recursive render, and named\n// keys would repeat in the inline bundle.\ntype StructuralPlan = readonly [\n container: Element,\n entries: readonly ReconciliationEntry[],\n nextItems: readonly unknown[],\n];\ntype ReconciliationEntry = readonly [\n index: number,\n live: Element | null,\n rendered: Element | undefined,\n nestedSlots: readonly NestedSlotPlan[],\n /** A `replace` patch: a different item, never morphed toward. */\n replace: boolean,\n];\ntype NestedSlotPlan = readonly [\n live: Element | null,\n rendered: Element,\n children: StructuralPlan | undefined,\n];\n\n// One per container, not per item, so — unlike the tuples above — named keys\n// cost the inline bundle nothing per render.\ntype RenderEnv = Pick<\n StructuralApplyOptions,\n 'template' | 'nextItems' | 'forceRender' | 'store' | 'sanitizerPolicy'\n>;\n\ninterface CommitOptions {\n readonly morph: boolean;\n readonly onDuplicateKey: ((container: Element, key: string) => void) | undefined;\n}\n\n/** A rendered slot that still carries a nested template keeps its children for its own plan. */\nfunction isManagedNestedSlot(_live: Element, rendered: Element): boolean {\n return (\n rendered.hasAttribute(NESTED_KEY_ATTRIBUTE) && rendered.hasAttribute(NESTED_TEMPLATE_ATTRIBUTE)\n );\n}\n\n/** Materialise the whole recursive replacement tree before touching live DOM. */\nfunction prepareStructuralPlan(\n container: Element,\n patches: readonly ArrayPatch[],\n env: RenderEnv,\n): StructuralPlan | null {\n const entries = prepareReconciliation(container, createPatchPlan(patches), env);\n return entries === null ? null : [container, entries, env.nextItems];\n}\n\nfunction commitStructuralPlan(\n plan: StructuralPlan,\n store: StructuralStore,\n options: CommitOptions,\n): boolean {\n const [container, entries, nextItems] = plan;\n const claimed = new Set<Element>();\n let mutated = false;\n // Placing a retained item is a remove-and-insert, which blurs whatever the\n // visitor had focused inside it; the morph does the same for a move it\n // makes and restores afterwards, so the commit does too.\n const focus = captureFocus(container);\n for (const [index, live, rendered, nestedSlots, replace] of entries) {\n let node = live;\n if (rendered !== undefined) {\n // Morph a compatible live item in place; otherwise transplant its live\n // nested slots into the rendered item and swap.\n const retained =\n options.morph && !replace && live !== null\n ? morphElement(live, rendered, {\n keyAttributes: [KEY_ATTRIBUTE, NESTED_KEY_ATTRIBUTE],\n boundary: isMorphBoundary,\n retainBoundary: retainIslandBoundary,\n retainChildrenOf: isManagedNestedSlot,\n ...(options.onDuplicateKey !== undefined\n ? { onDuplicateKey: options.onDuplicateKey }\n : {}),\n }) === live\n : false;\n if (!retained) {\n for (const [liveSlot, renderedSlot] of nestedSlots) {\n if (liveSlot === null) continue;\n synchronizeAttributes(liveSlot, renderedSlot);\n renderedSlot.replaceWith(liveSlot);\n }\n }\n for (const [, , children] of nestedSlots) {\n if (children !== undefined) commitStructuralPlan(children, store, options);\n }\n if (retained) {\n mutated = true;\n } else {\n if (live !== null) {\n live.replaceWith(rendered);\n mutated = true;\n }\n node = rendered;\n }\n }\n if (node === null) continue;\n const before = container.children[index] ?? null;\n if (node !== before) {\n container.insertBefore(node, before);\n mutated = true;\n }\n claimed.add(node);\n }\n // `nextItems` is the source of truth; this also drops SSR children the first value did not carry.\n for (const child of Array.from(container.children)) {\n if (claimed.has(child)) continue;\n child.remove();\n mutated = true;\n }\n restoreFocus(focus);\n const memory = getMemory(container, store);\n memory.clear();\n for (const value of nextItems) rememberItem(memory, value);\n return mutated;\n}\n\n/**\n * Pair every final item with a live child (by key, else by position) and\n * render what needs rendering. Patch indices describe `next`, live indices\n * move during reconciliation, so keyed nodes are resolved from the DOM first.\n */\nfunction prepareReconciliation(\n container: Element,\n plan: PatchPlan,\n env: RenderEnv,\n): readonly ReconciliationEntry[] | null {\n const { template, nextItems, forceRender = false, store, sanitizerPolicy } = env;\n const memory = store.get(container);\n // Read before anything is rendered: what the current items share is what the\n // template cannot say, and an inserted item has no predecessor of its own.\n const shared = sharedItemAttributes(container);\n const templateKeys = collectTemplateKeys(nextItems);\n const initialChildren = Array.from(container.children);\n const keyedChildren = indexByAttribute(initialChildren, KEY_ATTRIBUTE);\n const reserved = new Set<Element>();\n const entries: ReconciliationEntry[] = [];\n for (let index = 0; index < nextItems.length; index += 1) {\n const value = nextItems[index];\n const key = readKey(value);\n let live =\n key === undefined\n ? (initialChildren[index] ?? null)\n : (takeIndexedElement(keyedChildren, key) ?? null);\n if (live !== null && reserved.has(live)) live = null;\n if (live !== null) reserved.add(live);\n const replace = plan.replaces.has(index);\n const needsRender = forceRender || plan.renders.has(index) || live === null;\n const rendered = needsRender\n ? renderItem(\n container.ownerDocument,\n template,\n value,\n index,\n sanitizerPolicy,\n shared,\n templateKeys,\n )\n : undefined;\n if (rendered === null) return null;\n const nestedSlots =\n rendered === undefined\n ? []\n : prepareNestedSlots(\n replace ? null : live,\n rendered,\n value,\n key === undefined ? undefined : memory?.get(key),\n env,\n );\n if (nestedSlots === null) return null;\n entries.push([index, live, rendered, nestedSlots, replace]);\n }\n return entries;\n}\n\ninterface PatchPlan {\n readonly renders: ReadonlySet<number>;\n readonly replaces: ReadonlySet<number>;\n}\n\nfunction createPatchPlan(patches: readonly ArrayPatch[]): PatchPlan {\n const renders = new Set<number>();\n const replaces = new Set<number>();\n for (const patch of patches) {\n if (patch.kind === 'insert' || patch.kind === 'update') renders.add(patch.index);\n else if (patch.kind === 'replace') {\n renders.add(patch.index);\n replaces.add(patch.index);\n }\n }\n return { renders, replaces };\n}\n\n/** Plan each direct nested slot; a failure at any depth aborts before commit. */\nfunction prepareNestedSlots(\n oldItem: Element | null,\n newItem: Element,\n nextValue: unknown,\n prevValue: unknown,\n env: RenderEnv,\n): readonly NestedSlotPlan[] | null {\n const oldSlots = oldItem === null ? [] : findDirectNestedSlots(oldItem);\n const oldSlotsByKey = indexByAttribute(oldSlots, NESTED_KEY_ATTRIBUTE);\n const plans: NestedSlotPlan[] = [];\n for (const newSlot of findDirectNestedSlots(newItem)) {\n const key = newSlot.getAttribute(NESTED_KEY_ATTRIBUTE);\n if (key === null) continue;\n const oldSlot = takeIndexedElement(oldSlotsByKey, key) ?? null;\n const nextTemplate = newSlot.getAttribute(NESTED_TEMPLATE_ATTRIBUTE);\n // Without a nested template the slot's rendered static content is authoritative.\n const liveSlot = nextTemplate ? oldSlot : null;\n const previousTemplate = liveSlot?.getAttribute(NESTED_TEMPLATE_ATTRIBUTE) ?? null;\n let children: StructuralPlan | undefined;\n if (nextTemplate) {\n const nextNested = readNestedArray(nextValue, key);\n if (nextNested !== undefined || liveSlot !== null) {\n const resolvedNext = nextNested ?? [];\n const prevNested = liveSlot === null ? [] : (readNestedArray(prevValue, key) ?? []);\n const patches = diffArray(prevNested, resolvedNext);\n const templateChanged = liveSlot !== null && previousTemplate !== nextTemplate;\n if (patches.length > 0 || templateChanged) {\n children =\n prepareStructuralPlan(liveSlot ?? newSlot, patches, {\n template: nextTemplate,\n nextItems: resolvedNext,\n forceRender: templateChanged,\n store: env.store,\n sanitizerPolicy: env.sanitizerPolicy,\n }) ?? undefined;\n if (children === undefined) return null;\n }\n }\n }\n plans.push([liveSlot, newSlot, children]);\n }\n return plans;\n}\n\n/** Only the outermost nested slots: a transplanted slot owns the recursion below it. */\nfunction findDirectNestedSlots(item: Element): readonly Element[] {\n return Array.from(item.querySelectorAll(`[${NESTED_KEY_ATTRIBUTE}]`)).filter((slot) => {\n let ancestor = slot.parentElement;\n while (ancestor !== null && ancestor !== item) {\n if (ancestor.hasAttribute(NESTED_KEY_ATTRIBUTE)) return false;\n ancestor = ancestor.parentElement;\n }\n return ancestor === item;\n });\n}\n\nfunction synchronizeAttributes(target: Element, source: Element): void {\n for (const attribute of Array.from(target.attributes)) {\n if (!source.hasAttribute(attribute.name)) target.removeAttribute(attribute.name);\n }\n for (const attribute of Array.from(source.attributes)) {\n if (target.getAttribute(attribute.name) !== attribute.value) {\n target.setAttribute(attribute.name, attribute.value);\n }\n }\n}\n\nfunction readNestedArray(value: unknown, key: string): readonly unknown[] | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const nested = (value as Record<string, unknown>)[key];\n return Array.isArray(nested) ? (nested as readonly unknown[]) : undefined;\n}\n\nfunction getMemory(container: Element, store: StructuralStore): Map<string, unknown> {\n let map = store.get(container);\n if (!map) {\n map = new Map();\n store.set(container, map);\n }\n return map;\n}\n\nfunction rememberItem(memory: Map<string, unknown>, value: unknown): void {\n const key = readKey(value);\n if (key !== undefined) memory.set(key, value);\n}\n\nfunction readKey(value: unknown): string | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const id = (value as Record<string, unknown>)['id'];\n return typeof id === 'string' || typeof id === 'number' ? String(id) : undefined;\n}\n\nfunction indexByAttribute(elements: readonly Element[], attribute: string): Map<string, Element[]> {\n const indexed = new Map<string, Element[]>();\n // Reverse order so pop() yields duplicates in DOM order.\n for (let index = elements.length - 1; index >= 0; index -= 1) {\n const element = elements[index];\n if (element === undefined) continue;\n const key = element.getAttribute(attribute);\n if (key === null) continue;\n const bucket = indexed.get(key);\n if (bucket === undefined) indexed.set(key, [element]);\n else bucket.push(element);\n }\n return indexed;\n}\n\nfunction takeIndexedElement(indexed: Map<string, Element[]>, key: string): Element | undefined {\n return indexed.get(key)?.pop();\n}\n\n/** Render one item; its `id` is stamped as `data-payload-key` so later diffs find it. */\nfunction renderItem(\n ownerDocument: Document,\n template: string,\n value: unknown,\n index: number,\n policy: SanitizerPolicyMode | undefined,\n shared: readonly InheritedAttribute[],\n knownKeys: ReadonlySet<string>,\n): Element | null {\n const filled = interpolateArrayTemplate(template, value, index, safeStringify, knownKeys);\n const safe = sanitizeHtmlWithPolicy(filled, policy, templateSanitizeOptions(template));\n const host = ownerDocument.createElement('template');\n host.innerHTML = trustedHtml(safe);\n const first = host.content.firstElementChild;\n if (!first) return null;\n // Before the morph, which strips from the live item every attribute the\n // rendered one lacks: the marker has to be on this side to survive.\n inheritItemAttributes(first, shared);\n const key = readKey(value);\n if (key !== undefined) first.setAttribute(KEY_ATTRIBUTE, key);\n return first;\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"fragment.cjs","names":["linkedTimeout","parent","ms","controller","AbortController","timedOut","onAbort","abort","addEventListener","once","timer","setTimeout","signal","dispose","clearTimeout","removeEventListener","errorMessage","error","Error","message","String","ISLAND_ATTRIBUTE","OWNED_ATTRIBUTE","isMorphBoundary","element","tagName","toLowerCase","includes","hasAttribute","isIslandBoundary","editable","getAttribute","isIslandRoot","isInsideIsland","current","parentElement","ISLAND_IDENTITY","renderedSlots","WeakMap","unmountedIslands","WeakSet","isAstroIsland","slotMarkup","island","markup","slot","querySelectorAll","closest","innerHTML","retainIslandBoundary","live","rendered","slots","every","name","get","constructor","ownerDocument","defaultView","customElements","Array","isArray","observedAttributes","observesProps","set","props","setAttribute","removeAttribute","parseDependencyList","value","seen","Set","names","raw","split","trim","length","has","add","push","FRAGMENT_ATTRIBUTE","FRAGMENT_KEY_ATTRIBUTE","describeBoundary","id","key","dependsOn","collectFragmentBoundaries","root","changedFields","boundaries","boundary","some","field","FRAGMENT_VERSION_HEADER","depthOf","depth","maxDepth","deepest","child","Object","values","Math","max","isRecord","parseFragmentResponse","html","revision","metadata","abortReason","reason","DOMException","isAborted","aborted","SUPERSEDED","status","failed","code","createFragmentHandler","options","endpoint","refuse","TypeError","startsWith","location","resolved","URL","href","origin","sameOriginPath","timeoutMs","maxResponseBytes","gate","limit","active","waiting","release","next","shift","resume","async","run","Promise","resolve","reject","waiter","index","indexOf","splice","createGate","maxConcurrent","inFlight","Map","generations","nextGeneration","generationOf","known","request","dedupeKey","JSON","stringify","shared","started","promise","superseded","where","body","source","result","entries","definedOnly","fragment","route","pathname","search","locale","collectionSlug","globalSlug","fields","timeout","failure","response","readingResponse","fetch","method","credentials","headers","accept","ok","type","Number","text","parsed","maxBytes","stream","getReader","TextEncoder","encode","byteLength","reader","cancel","catch","decoder","TextDecoder","total","done","read","decode","releaseLock","readBoundedText","parse","render","forget","delete","then","DIAGNOSTIC_CODES","freeze","NoTrustedOrigin","ReferrerOnlyTrust","PluginIncompatible","ProfileFeatureOmitted","OrphanField","UnattributableUpdate","UnboundField","CoverageDefaultChanges","VisibilityGateDeferred","UnsafeAttributeWrite","TextTargetHasChildren","MissingArrayTemplate","StructuralItemUnkeyed","StructuralDuplicateKey","StructuralUnstableKeys","UnsupportedStrategy","UnknownValueFormat","SanitizerDroppedAttribute","UnrenderedBlockKept","UnfaithfulPatch","ServerFormatReplaced","UnrenderedBlockLost","MessageRejected","TokenRejected","ProtocolShapeUnknown","HandlerThrew","TransformThrew","RendererThrew","StartupFailed","ReadyFailed","HydrationWaitTimedOut","AuditRuntimeMissing","AuditNoFrameAncestors","AuditFrameOptionsBlocks","AuditBindingsExposed","AuditGateThresholdExceeded","AuditUnownedBindings","AuditNoBindings","AuditNotAPage","FragmentRequestFailed","FragmentResponseInvalid","FragmentUnauthorized","FragmentSuperseded","RouteRefreshLoop","FragmentStrategyUnavailable","UnboundChangeRefresh","EscalationUnavailable","IslandStartUnavailable","RouteRefreshUnconfirmed","V2ReadinessGap","RuntimeOnPublicPage","AuditModifierWithoutField","STATE_ATTRIBUTES","keyOf","attribute","keyAttributes","morphElement","sameKind","liveBoundary","renderedBoundary","retainBoundary","focus","activeElement","HTMLElement","contains","selection","HTMLInputElement","HTMLTextAreaElement","selectionStart","selectionEnd","captureFocus","from","attributes","syncAttributes","retainChildrenOf","liveKeyed","keyed","duplicate","children","slice","onDuplicateKey","indexKeyed","keyedElements","liveUnkeyed","childNodes","filter","node","claimed","usedKeys","renderedNodes","cursor","firstChild","unkeyedIndex","i","kept","Element","candidate","reconcile","replaceWith","nextSibling","replaceChild","following","isBlankText","insertBefore","remove","morphChildren","snapshot","isConnected","preventScroll","start","end","setSelectionRange","restoreFocus","nodeValue","nodeType","Node","TEXT_NODE","a","b","namespaceURI","STRATEGY_ATTRIBUTE","FIELD_ATTRIBUTE","ROUTE_REFRESH_HEADER","isRouteBound","explicit","fieldsOf","own","depends","isManagedHeadElement","fragmentStrategyFrom","handler","plan","map","context","elements","report","receivedAt","all","outcome","isCurrent","morph","patch","log","minIntervalMs","lastRefreshAt","saidUnconfirmed","inScope","refresh","now","Date","waitMs","retryAfter","document","view","window","detail","hostRefresh","settled","cache","fresh","DOMParser","parseFromString","x","scrollX","y","scrollY","liveTitle","head","find","freshTitle","title","liveSlots","targets","paired","min","forEach","target","replacement","importNode","anchor","at","addition","append","after","surplus","syncHead","islands","dispatchEvent","CustomEvent","releaseDisconnectedIslands","scrollTo","maxFieldDepth","isInteger","test","slugs","slug","parseFragmentRequestResult","enclosingFragment"],"sources":["../src/fragment/abort.ts","../src/core/islands.ts","../src/core/dependencies.ts","../src/core/strategies.ts","../src/fragment/boundary.ts","../src/types/fragment-protocol.ts","../src/fragment/gate.ts","../src/fragment/handler.ts","../src/types/defined-only.ts","../src/core/diagnostic-codes.ts","../src/core/morph.ts","../src/fragment/route.ts","../src/fragment/index.ts","../src/core/route-refresh.ts","../src/core/structural-applier.ts"],"sourcesContent":["/**\n * One signal for one request: it aborts when the caller's does or when `ms`\n * pass, whichever first. `timedOut()` tells the two apart afterwards; the\n * caller checks its own signal for the other. `dispose()` runs when the\n * request settles — the timer and the parent's listener outlive it otherwise.\n */\n\nexport interface LinkedTimeout {\n readonly signal: AbortSignal;\n readonly timedOut: () => boolean;\n readonly dispose: () => void;\n}\n\nexport function linkedTimeout(parent: AbortSignal, ms: number): LinkedTimeout {\n const controller = new AbortController();\n let timedOut = false;\n const onAbort = (): void => {\n controller.abort();\n };\n parent.addEventListener('abort', onAbort, { once: true });\n // The flag is set before the abort so a listener on `signal` can already read it.\n const timer = setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, ms);\n return {\n signal: controller.signal,\n timedOut: () => timedOut,\n dispose: () => {\n clearTimeout(timer);\n parent.removeEventListener('abort', onAbort);\n },\n };\n}\n\nexport function errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n","/**\n * Hydrated islands own their subtree, so the runtime never patches inside one.\n * Instead every flush that carried a change dispatches `payload-live-preview:update`\n * on each island root with the update in `detail`, whether or not a binding\n * outside the islands was written. `data-payload-island=\"patch\"` opts back in.\n * Server HTML reaches an Astro island only through Astro's own props handoff\n * (ADR 0021). @internal\n */\n\nexport const ISLAND_EVENT = 'payload-live-preview:update';\n/** @internal */\nexport const ISLAND_ATTRIBUTE = 'data-payload-island';\nexport const ISLAND_SELECTOR = `astro-island, [${ISLAND_ATTRIBUTE}]`;\n/** The opt-out for a subtree the site scripts itself: the morph never enters it. @internal */\nexport const OWNED_ATTRIBUTE = 'data-payload-owned';\n/**\n * The opt-in for a custom element: present on the live and on the rendered host\n * (its value is ignored), the morph edits the host's attributes and light DOM\n * instead of retaining it whole (ADR 0008 §10). @internal\n */\nexport const MORPH_ATTRIBUTE = 'data-payload-morph';\n\n/**\n * The ownership rule (ADR 0008 §4): a subtree the morph never enters. Custom\n * elements own their subtree unless the template opts in with\n * `data-payload-morph` (§10), islands are a framework's, `contenteditable`\n * is the visitor's, `data-payload-owned` is the site's. The coordinators hand\n * this rule to the engine as `MorphOptions.boundary`; the engine itself only\n * pairs, edits and keeps focus (§9). @beta\n */\nexport function isMorphBoundary(element: Element): boolean {\n const customElement = element.tagName.toLowerCase().includes('-');\n if (customElement && !element.hasAttribute(MORPH_ATTRIBUTE)) return true;\n if (isIslandBoundary(element) || element.hasAttribute(OWNED_ATTRIBUTE)) return true;\n const editable = element.getAttribute('contenteditable');\n return editable !== null && editable !== 'false';\n}\n\nexport interface IslandUpdateDetail {\n readonly fields: Readonly<Record<string, unknown>>;\n readonly revision: number;\n readonly receivedAt: number;\n readonly locale: string | undefined;\n /**\n * Call once the island has rendered this revision, for instance from an\n * effect after its framework committed. Until every island handed the\n * revision has called it, `inspect().revisions.display` says `unconfirmed`\n * (ADR 0023). Repeated, late or superseded calls do nothing.\n */\n readonly displayed: () => void;\n}\n\nfunction isIslandRoot(element: Element): boolean {\n return element.tagName.toLowerCase() === 'astro-island' || element.hasAttribute(ISLAND_ATTRIBUTE);\n}\n\nfunction islandAllowsPatching(island: Element): boolean {\n return island.getAttribute(ISLAND_ATTRIBUTE) === 'patch';\n}\n\n/** Whether `element` itself is an island root that did not opt into patching. */\nexport function isIslandBoundary(element: Element): boolean {\n return isIslandRoot(element) && !islandAllowsPatching(element);\n}\n\n/** Whether `element` (or an ancestor) is an island that did not opt into patching. @internal */\nexport function isInsideIsland(element: Element): boolean {\n let current: Element | null = element;\n while (current !== null) {\n if (isIslandBoundary(current)) return true;\n current = current.parentElement;\n }\n return false;\n}\n\n/** Island roots under `root` that receive update events. */\nexport function collectIslands(root: ParentNode): Element[] {\n const islands: Element[] = [];\n for (const island of root.querySelectorAll(ISLAND_SELECTOR)) {\n if (!islandAllowsPatching(island)) islands.push(island);\n }\n return islands;\n}\n\n/** Astro keeps `ssr` until the framework listener has finished hydrating. @internal */\nexport function isAwaitingIslandHydration(island: Element): boolean {\n return island.tagName.toLowerCase() === 'astro-island' && island.hasAttribute('ssr');\n}\n\nexport function dispatchIslandUpdate(\n islands: readonly Element[],\n detail: IslandUpdateDetail,\n isCurrent: () => boolean,\n): number {\n let dispatched = 0;\n for (const island of islands) {\n if (!isCurrent()) break;\n island.dispatchEvent(\n new CustomEvent<IslandUpdateDetail>(ISLAND_EVENT, { detail, bubbles: false }),\n );\n dispatched += 1;\n if (!isCurrent()) break;\n }\n return dispatched;\n}\n\n/** What decides whether a live `astro-island` still renders the rendered one's component (ADR 0021). */\nconst ISLAND_IDENTITY = [\n 'component-url',\n 'component-export',\n 'renderer-url',\n 'client',\n 'opts',\n 'before-hydration-url',\n] as const;\n/** Slot markup each island was last rendered with; Astro reads slots once, at hydration. */\nconst renderedSlots = new WeakMap<Element, string>();\nconst unmountedIslands = new WeakSet<Element>();\n\nfunction isAstroIsland(element: Element): boolean {\n return element.tagName.toLowerCase() === 'astro-island';\n}\n\nfunction slotMarkup(island: Element): string {\n let markup = '';\n for (const slot of island.querySelectorAll('astro-slot, template[data-astro-template]')) {\n if (slot.parentElement?.closest('astro-island') !== island) continue;\n const name = slot.getAttribute('name') ?? slot.getAttribute('data-astro-template') ?? '';\n markup += `${name}\\u0000${slot.innerHTML}\\u0000`;\n }\n return markup;\n}\n\n/** Only an upgraded island whose class observes `props` re-hydrates in place. */\nfunction observesProps(island: Element): boolean {\n const constructor = island.ownerDocument.defaultView?.customElements.get('astro-island') as\n (CustomElementConstructor & { readonly observedAttributes?: unknown }) | undefined;\n return (\n constructor !== undefined &&\n island instanceof constructor &&\n Array.isArray(constructor.observedAttributes) &&\n constructor.observedAttributes.includes('props')\n );\n}\n\n/**\n * The package's decision for a boundary pair (ADR 0021). A boundary other than\n * an Astro island stays whole (ADR 0008 §4). An island that still renders the\n * same component, with the same slots, keeps its framework state: it takes the\n * rendered props the way Astro's router hands them to a persisted island,\n * `ssr` first, then `props`. Anything else is replaced, and hydrates fresh.\n * @beta\n */\nexport function retainIslandBoundary(live: Element, rendered: Element): boolean {\n if (!isAstroIsland(live) || !isAstroIsland(rendered)) return true;\n const slots = slotMarkup(rendered);\n const retained =\n ISLAND_IDENTITY.every((name) => live.getAttribute(name) === rendered.getAttribute(name)) &&\n (renderedSlots.get(live) ?? slotMarkup(live)) === slots &&\n (live.getAttribute('props') === rendered.getAttribute('props') || observesProps(live));\n if (!retained) {\n renderedSlots.set(rendered, slots);\n return false;\n }\n renderedSlots.set(live, slots);\n const props = rendered.getAttribute('props');\n if (props !== live.getAttribute('props')) {\n live.setAttribute('ssr', '');\n if (props === null) live.removeAttribute('props');\n else live.setAttribute('props', props);\n }\n return true;\n}\n\n/** The Astro islands under `root`, to compare before and after a morph. @internal */\nexport function astroIslandsIn(root: ParentNode): Element[] {\n return Array.from(root.querySelectorAll('astro-island'));\n}\n\n/**\n * Astro's framework clients release their roots on `astro:unmount`, which its\n * router sends after a swap. A morph that disconnects an island sends it once\n * itself, so a removed React root does not outlive its markup. @internal\n */\nexport function releaseDisconnectedIslands(islands: readonly Element[]): void {\n for (const island of islands) {\n if (island.isConnected || unmountedIslands.has(island)) continue;\n unmountedIslands.add(island);\n island.dispatchEvent(new CustomEvent('astro:unmount'));\n }\n}\n\n/**\n * Why an inserted island cannot start: fragment scripts never run, so the page\n * itself must have loaded Astro's island element and this client directive.\n * @internal\n */\nexport function islandStartBlocker(island: Element): 'element' | 'directive' | undefined {\n const view = island.ownerDocument.defaultView;\n if (view?.customElements.get('astro-island') === undefined) return 'element';\n const directive = island.getAttribute('client');\n const astro: unknown = Reflect.get(view, 'Astro');\n if (directive === null || typeof astro !== 'object' || astro === null) return 'directive';\n return Reflect.get(astro, directive) === undefined ? 'directive' : undefined;\n}\n","/**\n * One source → dependents map for `skipUnchanged`. Markup declares it the\n * other way round (`data-payload-depends=\"price\"` on the element bound to\n * `priceLabel`) because an author names what a binding needs; the runtime\n * option is keyed by source because the scheduler asks what changed.\n * @internal\n */\n\nexport type DependencyMap = Readonly<Record<string, readonly string[]>>;\n\n/**\n * Field names from an attribute value. Separators are commas and\n * whitespace; empty entries are dropped; order is kept, duplicates removed.\n * @internal\n */\nexport function parseDependencyList(value: string | null | undefined): readonly string[] {\n if (value === null || value === undefined) return [];\n const seen = new Set<string>();\n const names: string[] = [];\n for (const raw of value.split(/[\\s,]+/)) {\n const name = raw.trim();\n if (name.length === 0 || seen.has(name)) continue;\n seen.add(name);\n names.push(name);\n }\n return names;\n}\n\n/**\n * Merge dependency maps (source → dependents). Later maps add to earlier\n * ones; a dependent is listed once per source.\n * @internal\n */\nexport function mergeDependencyMaps(...maps: readonly DependencyMap[]): DependencyMap {\n const merged = new Map<string, Set<string>>();\n for (const map of maps) {\n for (const [source, dependents] of Object.entries(map)) {\n let bucket = merged.get(source);\n if (bucket === undefined) {\n bucket = new Set();\n merged.set(source, bucket);\n }\n for (const dependent of dependents) bucket.add(dependent);\n }\n }\n const result: Record<string, readonly string[]> = {};\n for (const [source, dependents] of merged) result[source] = [...dependents];\n return result;\n}\n\n/**\n * The map an element's `data-payload-depends` contributes: every declared\n * source points at this binding's field.\n * @internal\n */\nexport function dependencyMapFromBinding(\n fieldName: string,\n dependsOn: readonly string[],\n): DependencyMap {\n const map: Record<string, readonly string[]> = {};\n for (const source of dependsOn) {\n if (source === fieldName) continue;\n map[source] = [fieldName];\n }\n return map;\n}\n","/**\n * How a bound region is brought up to date: the runtime patches it, a server\n * renders a `data-payload-fragment` boundary from the unsaved form state, or\n * the whole route refreshes. Core carries only the seam — which boundaries a\n * strategy owns and what it may do — so a patch-only page pays for nothing\n * more; the orchestration lives in `payload-live-preview/fragment`.\n */\nimport type { DiagnosticCode } from './diagnostic-codes';\nimport type { CachedElement } from './types';\nimport type { OwnerScope } from './unbound-fields';\n\n/** What produced an update: the runtime's DOM patching, a server-rendered fragment, or a route refresh. */\nexport type UpdateSource = 'patch' | 'fragment' | 'route';\n\n/** Marks a server-rendered boundary; the value is the registry id the server may render. @internal */\nexport const FRAGMENT_ATTRIBUTE = 'data-payload-fragment';\n/** Distinguishes several boundaries of one registry id; unique among siblings. @internal */\nexport const FRAGMENT_KEY_ATTRIBUTE = 'data-payload-fragment-key';\n\n/** The nearest fragment boundary enclosing `element` (the element itself included). */\nexport function enclosingFragment(element: Element): Element | null {\n return element.closest(`[${FRAGMENT_ATTRIBUTE}]`);\n}\n\n/**\n * Where a render planned for `planned` lands. A host component that re-renders\n * its region while the render is in flight swaps the element and keeps the\n * identity, so a boundary no longer in the document stands for the one that\n * is: the single connected boundary with its id and key that `inScope` admits.\n * Two candidates name no single region, and none means it is gone (H06).\n */\nexport function liveBoundary(\n root: Document | Element,\n planned: Element,\n inScope: (boundary: Element) => boolean,\n): Element | undefined {\n if (planned.isConnected) return planned;\n const id = planned.getAttribute(FRAGMENT_ATTRIBUTE);\n const key = planned.getAttribute(FRAGMENT_KEY_ATTRIBUTE);\n let found: Element | undefined;\n for (const candidate of root.querySelectorAll(`[${FRAGMENT_ATTRIBUTE}]`)) {\n if (candidate.getAttribute(FRAGMENT_ATTRIBUTE) !== id) continue;\n if (candidate.getAttribute(FRAGMENT_KEY_ATTRIBUTE) !== key || !inScope(candidate)) continue;\n if (found !== undefined) return undefined;\n found = candidate;\n }\n return found;\n}\n\n/** One revision, as a strategy sees it, with the runtime capabilities it may use. */\nexport interface FragmentContext {\n readonly root: ParentNode;\n readonly revision: number;\n readonly receivedAt: number;\n readonly fields: Readonly<Record<string, unknown>>;\n readonly locale: string | undefined;\n readonly collectionSlug: string | undefined;\n readonly globalSlug: string | undefined;\n /** Aborted when a newer revision arrives or the runtime stops. */\n readonly signal: AbortSignal;\n /** Whether this revision is still the current one. */\n readonly isCurrent: () => boolean;\n /** Morph server-rendered HTML into a boundary; no-op after supersession (Trusted Types and the keyed morph apply). */\n readonly morph: (boundary: Element, html: string) => void;\n /** Patch the boundary's owned bindings from this revision; no-op after supersession. */\n readonly patch: (boundary: Element) => void;\n /** Debug log through the runtime's logger, with the diagnostic code. */\n readonly log: (code: DiagnosticCode, detail: string) => void;\n /** Report a boundary rendered. */\n readonly rendered: (boundary: Element, id: string, key: string | undefined) => void;\n /** Report a boundary failed and patched instead; emits the `error` and `fragmentRender` events. */\n readonly failed: (\n boundary: Element,\n id: string,\n key: string | undefined,\n code: DiagnosticCode,\n reason: string,\n ) => void;\n}\n\nexport interface FragmentReport {\n readonly rendered: number;\n readonly failed: number;\n readonly superseded: number;\n}\n\n/** A fragment strategy: which boundaries it owns for a revision, and how it renders them. */\nexport interface FragmentStrategy {\n /** The boundaries this update re-renders; their inner bindings are left to the strategy. */\n readonly plan: (root: ParentNode, changedFields: ReadonlySet<string>) => readonly Element[];\n /** Render the planned boundaries; resolves once every one settled. */\n readonly render: (\n context: FragmentContext,\n boundaries: readonly Element[],\n ) => Promise<FragmentReport>;\n}\n\n/** The boundaries a revision hands the fragment strategy, and the bindings they take from the patch. */\nexport interface FragmentPlan {\n readonly boundaries: readonly Element[];\n readonly strategy: FragmentStrategy;\n readonly ownerKeys: OwnerScope;\n /** Whether a binding sits inside a boundary the strategy renders this revision. */\n readonly covers: (target: CachedElement) => boolean;\n}\n\n/** A plan over boundaries already chosen, whichever question chose them. */\nexport function planBoundaries(\n strategy: FragmentStrategy,\n boundaries: readonly Element[],\n ownerKeys: OwnerScope,\n): FragmentPlan {\n const covered = new Set(boundaries);\n return {\n boundaries,\n strategy,\n ownerKeys,\n covers: (target) =>\n target.fragmentBoundary !== undefined && covered.has(target.fragmentBoundary),\n };\n}\n\n/** One revision, as the route strategy sees it. */\nexport interface RouteContext {\n readonly revision: number;\n readonly receivedAt: number;\n /** Aborted when a newer revision asks for a refresh of its own, a navigation commits, or the runtime stops. */\n readonly signal: AbortSignal;\n /**\n * Whether the refresh may still land. A newer revision that asks for no\n * refresh of its own does not end it: the route is the server's, so the\n * refresh lands for that revision, which the runtime then re-applies.\n */\n readonly isCurrent: () => boolean;\n readonly log: (code: DiagnosticCode, detail: string) => void;\n /**\n * Ask to be run again in `delayMs`, once. A strategy that refuses a refresh\n * because it is rate-limiting itself uses this: the window is a rate limit\n * and not a filter, so what falls inside it still has to reach the preview.\n * The runtime runs at most one such request, and only while the revision that\n * asked is still the current one.\n */\n readonly retryAfter?: (delayMs: number) => void;\n}\n\n/**\n * What one refresh did. `refreshed` asserts that the strategy rendered the\n * current unsaved revision. `partial` completed a render whose data source was\n * not proven current; the runtime still reapplies every binding it can reach.\n * `refused` is the strategy's own brake and not a failure.\n */\nexport type RouteOutcome = 'refreshed' | 'partial' | 'failed' | 'refused' | 'superseded';\n\n/** What the runtime tells a route planner about the update it plans for. */\nexport interface RoutePlanContext {\n /**\n * Whether an element belongs to the update: always `true` without\n * `scopeBindingsByOwner`, otherwise only inside the document the update\n * names. A marker outside it asks for nothing.\n */\n readonly inScope: (element: Element) => boolean;\n}\n\n/**\n * Whether a revision needs the whole route re-rendered, and how. After a\n * refresh the runtime rescans and re-applies the revision, so reachable\n * unsaved bindings land on the fresh markup. The runtime always passes\n * `context`; a planner written before it existed still works, but plans for\n * markers of every document.\n */\nexport interface RouteStrategy {\n readonly plan: (\n root: ParentNode,\n changedFields: ReadonlySet<string>,\n context?: RoutePlanContext,\n ) => boolean;\n readonly refresh: (context: RouteContext) => Promise<RouteOutcome>;\n}\n\nexport interface StrategyHandlers {\n readonly fragment?: FragmentStrategy;\n readonly route?: RouteStrategy;\n}\n\nconst STRATEGY_ATTRIBUTE = 'data-payload-strategy';\n\n/**\n * Explicit `data-payload-strategy` wins; otherwise a binding inside a fragment\n * boundary belongs to the fragment, one in `<head>` to the route, the rest is\n * patched. An unknown explicit value resolves to `undefined` (LP0407).\n * @internal\n */\nexport function resolveStrategy(element: Element): UpdateSource | undefined {\n const explicit = element.getAttribute(STRATEGY_ATTRIBUTE);\n if (explicit !== null && explicit.length > 0) {\n return explicit === 'patch' || explicit === 'fragment' || explicit === 'route'\n ? explicit\n : undefined;\n }\n if (enclosingFragment(element) !== null) return 'fragment';\n if (element.closest('head') !== null) return 'route';\n return 'patch';\n}\n","/** Fragment boundaries: how a `data-payload-fragment` element is read and which ones an update touches. */\n\nimport type { DiagnosticCode } from '@core/diagnostic-codes';\nimport { isInsideIsland } from '@core/islands';\nimport { parseDependencyList } from '@core/dependencies';\nimport { FRAGMENT_ATTRIBUTE, FRAGMENT_KEY_ATTRIBUTE } from '@core/strategies';\n\nexport { FRAGMENT_KEY_ATTRIBUTE };\nconst DEPENDS_ATTRIBUTE = 'data-payload-depends';\n\n/** @internal */\nexport interface FragmentBoundary {\n readonly element: Element;\n /** Registry id — never a path, module or function name. */\n readonly id: string;\n readonly key: string | undefined;\n /** Fields whose change re-renders the boundary; empty means any field. */\n readonly dependsOn: readonly string[];\n}\n\n/** One revision's request to a per-boundary handler. @internal */\nexport interface StrategyRequest {\n readonly revision: number;\n readonly receivedAt: number;\n readonly fields: Readonly<Record<string, unknown>>;\n readonly locale: string | undefined;\n readonly collectionSlug: string | undefined;\n readonly globalSlug: string | undefined;\n /** Aborted when a newer revision arrives or the runtime stops. */\n readonly signal: AbortSignal;\n}\n\n/** @internal */\nexport type FragmentOutcome =\n | {\n readonly status: 'rendered';\n /** The boundary's new inner HTML. */\n readonly html: string;\n readonly metadata?: Readonly<Record<string, unknown>>;\n }\n | { readonly status: 'failed'; readonly code: DiagnosticCode; readonly reason: string }\n | { readonly status: 'superseded' };\n\n/** Renders one boundary for one revision. Must honour `request.signal`. @internal */\nexport type FragmentHandler = (\n request: StrategyRequest,\n boundary: FragmentBoundary,\n) => Promise<FragmentOutcome>;\n\n/** @internal */\nexport function describeBoundary(element: Element): FragmentBoundary | null {\n const id = element.getAttribute(FRAGMENT_ATTRIBUTE);\n if (id === null || id.length === 0) return null;\n const key = element.getAttribute(FRAGMENT_KEY_ATTRIBUTE);\n return {\n element,\n id,\n key: key === null || key.length === 0 ? undefined : key,\n dependsOn: parseDependencyList(element.getAttribute(DEPENDS_ATTRIBUTE)),\n };\n}\n\n/** The boundaries under `root` that `changedFields` touch; one inside an island is the island's business. @internal */\nexport function collectFragmentBoundaries(\n root: ParentNode,\n changedFields: ReadonlySet<string>,\n): readonly FragmentBoundary[] {\n const boundaries: FragmentBoundary[] = [];\n for (const element of root.querySelectorAll(`[${FRAGMENT_ATTRIBUTE}]`)) {\n const boundary = describeBoundary(element);\n if (boundary === null || isInsideIsland(element)) continue;\n if (\n boundary.dependsOn.length > 0 &&\n !boundary.dependsOn.some((field) => changedFields.has(field))\n ) {\n continue;\n }\n boundaries.push(boundary);\n }\n return boundaries;\n}\n","/**\n * The fragment wire protocol: what the browser posts to a fragment endpoint\n * and what it answers. Both sides validate the shape. The request names the\n * page by its own route and query and the boundary by its registry id; nothing\n * on the wire carries code, templates, import paths or file paths. See ADR 0011.\n */\n\n/** Bumped when the request or response shape changes incompatibly. @internal */\nexport const FRAGMENT_PROTOCOL_VERSION = 1;\n/** @internal */\nexport const FRAGMENT_VERSION_HEADER = 'x-payload-fragment-version';\n\n/** @internal */\nexport interface FragmentRequestBody {\n /** Registry id of the boundary (`data-payload-fragment`). */\n readonly fragment: string;\n /** `data-payload-fragment-key`, when several boundaries share one id. */\n readonly key?: string;\n /** The page route the boundary belongs to; authorization is bound to it. */\n readonly route: string;\n /** The page's query string, so a `previewToken` in the URL reaches the endpoint. */\n readonly search: string;\n readonly revision: number;\n readonly locale?: string;\n readonly collectionSlug?: string;\n readonly globalSlug?: string;\n /** The unsaved form state as the admin posted it, after the runtime's merge. */\n readonly fields: Readonly<Record<string, unknown>>;\n}\n\n/** @internal */\nexport interface FragmentResponseBody {\n /** The boundary's new inner HTML. */\n readonly html: string;\n readonly boundary: { readonly id: string; readonly key?: string };\n /** Echoes the request; the client discards a response for another revision. */\n readonly revision: number;\n readonly metadata: {\n readonly renderedAt: string;\n /** What rendered it, e.g. `astro-container`. */\n readonly renderer: string;\n readonly durationMs?: number;\n };\n}\n\n// Bounds the walk over a body anyone may post, not the content: a bulleted list\n// in a column's rich text inside a layout block is 13 levels, a list nested\n// five levels 29 (ADR 0027). The byte cap bounds the rest.\n/** Fixed upper bound for validation before authorization. @internal */\nexport const MAX_FIELD_DEPTH = 64;\n\nfunction depthOf(value: unknown, depth: number, maxDepth: number): number {\n if (depth > maxDepth || typeof value !== 'object' || value === null) return depth;\n let deepest = depth;\n for (const child of Object.values(value)) {\n deepest = Math.max(deepest, depthOf(child, depth + 1, maxDepth));\n if (deepest > maxDepth) break;\n }\n return deepest;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Shape check for a request body; `null` when it is not one. @internal */\nexport function parseFragmentRequest(\n value: unknown,\n maxFieldDepth = MAX_FIELD_DEPTH,\n): FragmentRequestBody | null {\n const result = parseFragmentRequestResult(value, maxFieldDepth);\n return result === 'field-depth' ? null : result;\n}\n\n/** Request validation with a distinct depth refusal for endpoint diagnostics. @internal */\nexport function parseFragmentRequestResult(\n value: unknown,\n maxFieldDepth = MAX_FIELD_DEPTH,\n): FragmentRequestBody | 'field-depth' | null {\n if (!Number.isInteger(maxFieldDepth) || maxFieldDepth < 0 || maxFieldDepth > MAX_FIELD_DEPTH) {\n return null;\n }\n if (!isRecord(value)) return null;\n const fragment = value['fragment'];\n if (typeof fragment !== 'string' || !/^[a-z][a-z0-9-]{0,63}$/iu.test(fragment)) return null;\n const key = value['key'];\n if (key !== undefined && (typeof key !== 'string' || key.length > 128)) return null;\n const route = value['route'];\n if (typeof route !== 'string' || !route.startsWith('/') || route.length > 2048) return null;\n const search = value['search'];\n if (\n typeof search !== 'string' ||\n search.length > 4096 ||\n (search !== '' && !search.startsWith('?'))\n ) {\n return null;\n }\n const revision = value['revision'];\n if (typeof revision !== 'number' || !Number.isInteger(revision) || revision < 0) return null;\n const slugs: (string | undefined)[] = [];\n for (const name of ['locale', 'collectionSlug', 'globalSlug'] as const) {\n const slug = value[name];\n if (slug === undefined) {\n slugs.push(undefined);\n continue;\n }\n if (typeof slug !== 'string' || slug.length > 128) return null;\n slugs.push(slug);\n }\n const [locale, collectionSlug, globalSlug] = slugs;\n const fields = value['fields'];\n if (!isRecord(fields)) return null;\n if (depthOf(fields, 0, maxFieldDepth) > maxFieldDepth) return 'field-depth';\n return {\n fragment,\n ...(typeof key === 'string' ? { key } : {}),\n route,\n search,\n revision,\n ...(locale !== undefined ? { locale } : {}),\n ...(collectionSlug !== undefined ? { collectionSlug } : {}),\n ...(globalSlug !== undefined ? { globalSlug } : {}),\n fields,\n };\n}\n\n/** Shape check for a response body; `null` when it is not one. @internal */\nexport function parseFragmentResponse(value: unknown): FragmentResponseBody | null {\n if (!isRecord(value)) return null;\n const { html, boundary, revision, metadata } = value;\n if (typeof html !== 'string') return null;\n if (!isRecord(boundary) || typeof boundary['id'] !== 'string') return null;\n if (boundary['key'] !== undefined && typeof boundary['key'] !== 'string') return null;\n if (typeof revision !== 'number') return null;\n if (\n !isRecord(metadata) ||\n typeof metadata['renderedAt'] !== 'string' ||\n typeof metadata['renderer'] !== 'string'\n ) {\n return null;\n }\n return value as unknown as FragmentResponseBody;\n}\n","/**\n * A semaphore: at most `limit` callers run at once, the rest queue in order.\n * Cancellation only owns the wait; a running job handles its own signal.\n */\n\nexport type Gate = <T>(run: () => Promise<T>, signal?: AbortSignal) => Promise<T>;\n\ninterface Waiter {\n readonly resume: () => void;\n}\n\nfunction abortReason(signal: AbortSignal): Error {\n return signal.reason instanceof Error\n ? signal.reason\n : new DOMException('The operation was aborted.', 'AbortError');\n}\n\nfunction isAborted(signal: AbortSignal | undefined): boolean {\n return signal?.aborted === true;\n}\n\nexport function createGate(limit: number): Gate {\n let active = 0;\n const waiting: Waiter[] = [];\n // The permit passes straight to the next waiter; decrementing first would let\n // a caller arriving before the waiter wakes run as limit + 1.\n const release = (): void => {\n const next = waiting.shift();\n if (next === undefined) active -= 1;\n else next.resume();\n };\n return async (run, signal) => {\n if (signal !== undefined && isAborted(signal)) throw abortReason(signal);\n if (active < limit) {\n active += 1;\n } else {\n await new Promise<void>((resolve, reject) => {\n let dispose = (): void => undefined;\n const waiter: Waiter = {\n resume: () => {\n dispose();\n resolve();\n },\n };\n waiting.push(waiter);\n if (signal !== undefined) {\n const abort = (): void => {\n const index = waiting.indexOf(waiter);\n if (index === -1) return;\n waiting.splice(index, 1);\n dispose();\n reject(abortReason(signal));\n };\n dispose = (): void => {\n signal.removeEventListener('abort', abort);\n };\n signal.addEventListener('abort', abort, { once: true });\n if (isAborted(signal)) abort();\n }\n });\n // `release()` hands the permit over synchronously, but this continuation\n // runs later. Return it if cancellation won that gap.\n if (signal !== undefined && isAborted(signal)) {\n release();\n throw abortReason(signal);\n }\n }\n try {\n return await run();\n } finally {\n release();\n }\n };\n}\n","/**\n * The HTTP handler behind `createFragmentStrategy()`: one POST per boundary and\n * revision, shared, gated, timed out and size-bounded. Every failure is an\n * `LP08xx` outcome, never an exception. See ADR 0011.\n */\n\nimport type { DiagnosticCode } from '@core/diagnostic-codes';\nimport {\n FRAGMENT_PROTOCOL_VERSION,\n FRAGMENT_VERSION_HEADER,\n parseFragmentResponse,\n type FragmentRequestBody,\n} from '@/types/fragment-protocol';\nimport { definedOnly } from '@/types/defined-only';\nimport { errorMessage, linkedTimeout } from './abort';\nimport type {\n FragmentBoundary,\n FragmentHandler,\n FragmentOutcome,\n StrategyRequest,\n} from './boundary';\nimport { createGate } from './gate';\n\nexport interface FragmentStrategyOptions {\n /** Same-origin path of the fragment endpoint, e.g. `/payload/fragment`. */\n readonly endpoint: string;\n /** `fetch` to use; defaults to the global one. */\n readonly fetch?: (url: string, init?: RequestInit) => Promise<Response>;\n /** Per-request timeout. Default 5000 ms. */\n readonly timeoutMs?: number;\n /** Requests in flight at once; further boundaries wait. Default 4. */\n readonly maxConcurrent?: number;\n /** Largest response body accepted, in bytes. Default 512 KiB. */\n readonly maxResponseBytes?: number;\n /** Extra request headers, e.g. a CSRF token the site already uses. */\n readonly headers?: Readonly<Record<string, string>>;\n /** Where the page lives; defaults to `location`. */\n readonly location?: {\n readonly pathname: string;\n readonly search: string;\n };\n}\n\nconst DEFAULT_TIMEOUT_MS = 5_000;\nconst DEFAULT_MAX_CONCURRENT = 4;\nconst DEFAULT_MAX_RESPONSE_BYTES = 512 * 1024;\nconst SUPERSEDED: FragmentOutcome = { status: 'superseded' };\n\nfunction failed(code: DiagnosticCode, reason: string): FragmentOutcome {\n return { status: 'failed', code, reason };\n}\n\n/**\n * `endpoint` must stay on the page's origin. The URL parser reads `\\` as `/`,\n * so `/\\evil.com/x` leaves it; the resolved origin decides.\n */\nfunction sameOriginPath(endpoint: string): string {\n const refuse = (): never => {\n throw new TypeError(\n 'createFragmentStrategy: endpoint must be a same-origin path starting with \"/\"',\n );\n };\n if (\n typeof endpoint !== 'string' ||\n !endpoint.startsWith('/') ||\n endpoint.startsWith('//') ||\n endpoint.includes('\\\\')\n ) {\n refuse();\n }\n if (typeof location !== 'undefined') {\n let resolved: URL | undefined;\n try {\n resolved = new URL(endpoint, location.href);\n } catch {\n resolved = undefined;\n }\n if (resolved?.origin !== location.origin) refuse();\n }\n return endpoint;\n}\n\nfunction isByteStream(value: unknown): value is ReadableStream<Uint8Array> {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { getReader?: unknown }).getReader === 'function'\n );\n}\n\n/**\n * The body as text, or `null` past `maxBytes` — counted as bytes arrive, so an\n * oversized answer is cut off rather than buffered. An abort cancels the\n * stream, which ends a pending read instead of hanging it.\n */\nasync function readBoundedText(\n response: Response,\n maxBytes: number,\n signal: AbortSignal,\n): Promise<string | null> {\n const stream: unknown = response.body;\n if (!isByteStream(stream)) {\n const text = await response.text();\n return new TextEncoder().encode(text).byteLength > maxBytes ? null : text;\n }\n const reader = stream.getReader();\n const cancel = (): void => {\n reader.cancel().catch(() => undefined);\n };\n signal.addEventListener('abort', cancel, { once: true });\n const decoder = new TextDecoder();\n let text = '';\n let total = 0;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > maxBytes) {\n cancel();\n return null;\n }\n text += decoder.decode(value, { stream: true });\n }\n } finally {\n signal.removeEventListener('abort', cancel);\n reader.releaseLock();\n }\n return text + decoder.decode();\n}\n\n/** @internal */\nexport function createFragmentHandler(options: FragmentStrategyOptions): FragmentHandler {\n const endpoint = sameOriginPath(options.endpoint);\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const maxResponseBytes = options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;\n const gate = createGate(Math.max(1, options.maxConcurrent ?? DEFAULT_MAX_CONCURRENT));\n const inFlight = new Map<string, Promise<FragmentOutcome>>();\n const generations = new WeakMap<AbortSignal, number>();\n let nextGeneration = 0;\n\n const generationOf = (signal: AbortSignal): number => {\n const known = generations.get(signal);\n if (known !== undefined) return known;\n nextGeneration += 1;\n generations.set(signal, nextGeneration);\n return nextGeneration;\n };\n\n async function render(\n request: StrategyRequest,\n boundary: FragmentBoundary,\n ): Promise<FragmentOutcome> {\n // Read through a function: the signal flips during awaits, which narrowing cannot see.\n const superseded = (): boolean => request.signal.aborted;\n if (superseded()) return SUPERSEDED;\n const where = options.location ?? location;\n const body: FragmentRequestBody = definedOnly({\n fragment: boundary.id,\n key: boundary.key,\n route: where.pathname,\n search: where.search,\n revision: request.revision,\n locale: request.locale,\n collectionSlug: request.collectionSlug,\n globalSlug: request.globalSlug,\n fields: request.fields,\n });\n const timeout = linkedTimeout(request.signal, timeoutMs);\n const failure = (error: unknown): FragmentOutcome => {\n if (superseded()) return SUPERSEDED;\n if (timeout.timedOut()) return failed('LP0801', `timeout after ${String(timeoutMs)} ms`);\n return failed('LP0801', errorMessage(error));\n };\n let response: Response | undefined;\n let readingResponse = false;\n try {\n try {\n response = await (options.fetch ?? fetch)(endpoint, {\n method: 'POST',\n credentials: 'same-origin',\n headers: {\n ...options.headers,\n accept: 'application/json',\n 'content-type': 'application/json',\n [FRAGMENT_VERSION_HEADER]: String(FRAGMENT_PROTOCOL_VERSION),\n },\n body: JSON.stringify(body),\n signal: timeout.signal,\n });\n } catch (error) {\n return failure(error);\n }\n if (superseded()) return SUPERSEDED;\n if (response.status === 401 || response.status === 403) {\n return failed('LP0803', `endpoint refused the preview (${String(response.status)})`);\n }\n if (!response.ok) return failed('LP0801', `endpoint answered ${String(response.status)}`);\n const type = response.headers.get('content-type') ?? '';\n if (!type.toLowerCase().startsWith('application/json')) {\n return failed('LP0802', `unexpected content type \"${type}\"`);\n }\n const length = Number(response.headers.get('content-length') ?? '0');\n if (length > maxResponseBytes) return failed('LP0802', 'response exceeds the size limit');\n if (timeout.signal.aborted) return failure(timeout.signal.reason);\n let text: string | null;\n try {\n readingResponse = true;\n text = await readBoundedText(response, maxResponseBytes, timeout.signal);\n } catch (error) {\n return failure(error);\n }\n if (superseded()) return SUPERSEDED;\n if (timeout.timedOut()) return failed('LP0801', `timeout after ${String(timeoutMs)} ms`);\n if (text === null) return failed('LP0802', 'response exceeds the size limit');\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return failed('LP0802', 'response is not JSON');\n }\n const fragment = parseFragmentResponse(parsed);\n if (fragment === null) return failed('LP0802', 'response has the wrong shape');\n if (fragment.boundary.id !== boundary.id || fragment.boundary.key !== boundary.key) {\n return failed('LP0802', 'response is for another boundary');\n }\n if (fragment.revision !== request.revision) return SUPERSEDED;\n return { status: 'rendered', html: fragment.html, metadata: fragment.metadata };\n } finally {\n timeout.dispose();\n if (response !== undefined && !readingResponse) {\n // A refusal stops its producer without holding the gate on cleanup.\n try {\n response.body?.cancel().catch(() => undefined);\n } catch {\n // Cleanup from an injected response cannot replace the original outcome.\n }\n }\n }\n }\n\n return (request, boundary) => {\n // Revision numbers restart when a runtime is recreated. Its abort signal is\n // the internal generation identity, so an old request cannot be shared with\n // a new document/owner session that happens to use the same number.\n const dedupeKey = JSON.stringify([\n generationOf(request.signal),\n boundary.id,\n boundary.key,\n request.revision,\n ]);\n const shared = inFlight.get(dedupeKey);\n if (shared !== undefined) return shared;\n let started = false;\n const promise = gate(() => {\n started = true;\n return render(request, boundary);\n }, request.signal).catch((error: unknown): FragmentOutcome => {\n if (!started && request.signal.aborted) return SUPERSEDED;\n throw error;\n });\n inFlight.set(dedupeKey, promise);\n const forget = (): void => {\n if (inFlight.get(dedupeKey) === promise) inFlight.delete(dedupeKey);\n };\n void promise.then(forget, forget);\n return promise;\n };\n}\n","/** Drops `undefined`-valued keys, so a literal built from optional inputs satisfies `exactOptionalPropertyTypes`. */\n\nexport type DefinedOnly<T> = { readonly [K in keyof T]-?: Exclude<T[K], undefined> };\n\nexport function definedOnly<T extends object>(source: T): DefinedOnly<T> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(source)) {\n if (value !== undefined) result[key] = value;\n }\n return result as DefinedOnly<T>;\n}\n","/**\n * Every diagnostic code the runtime and tools emit, so a consumer can branch\n * on `e.code` instead of matching message text. A code is never reused for a\n * different meaning; a retired one stays reserved.\n *\n * Browser-side reporting sites write the literal (`code: 'LP0603'`) rather than\n * reading it from this record: the frozen table would then ship in the inline\n * runtime for the sake of six characters — it did, 1 287 B in every page, for\n * the one read Z6 left in the strategy runner, and `diagnostic-codes.test.ts`\n * now holds the artifact free of it. Server-side tools may import it.\n */\n\n/**\n * Every diagnostic code the runtime can report, by name.\n *\n * ```ts\n * client.events.on('error', (e) => {\n * if (e.code === DIAGNOSTIC_CODES.TransformThrew) reportToSentry(e.error);\n * });\n * ```\n */\nexport const DIAGNOSTIC_CODES = Object.freeze({\n /** No trusted origin configured in production; nothing will be accepted. */\n NoTrustedOrigin: 'LP0101',\n /** Origin trust rests on `document.referrer`, so any framing site is trusted. */\n ReferrerOnlyTrust: 'LP0102',\n /** A plugin declares a runtime range this runtime does not satisfy; it was refused. */\n PluginIncompatible: 'LP0103',\n /** The page carries the lean runtime and needs a feature that profile leaves out. */\n ProfileFeatureOmitted: 'LP0104',\n\n /** An update named an empty field with no binding anchor on the page; an edit to it has nowhere to land. */\n OrphanField: 'LP0201',\n /** Owner scoping is on and the update names no document it could belong to. */\n UnattributableUpdate: 'LP0202',\n /** An update carried a value for a field the page binds nowhere; editing it changes nothing on this page. */\n UnboundField: 'LP0203',\n /** In development: a bound path covers its group under the `descendant` default, which 3.0 changes. */\n CoverageDefaultChanges: 'LP0204',\n\n /** The visibility gate held offscreen writes back until they scroll into view. */\n VisibilityGateDeferred: 'LP0301',\n\n /** A value was refused because the attribute or the value itself is unsafe. */\n UnsafeAttributeWrite: 'LP0401',\n /** A text element has structured children, so replacing its text was skipped. */\n TextTargetHasChildren: 'LP0402',\n /** A structural container has no array template, so the update was skipped. */\n MissingArrayTemplate: 'LP0403',\n /** A structural item has no `id`; it pairs positionally, so an insert re-renders every row after it. */\n StructuralItemUnkeyed: 'LP0404',\n /** Two structural items share a key; later ones pair positionally. */\n StructuralDuplicateKey: 'LP0405',\n /** Every structural key changed while the length did not; the source generates keys per message. */\n StructuralUnstableKeys: 'LP0406',\n /** A binding asks for a delivery strategy this release does not have; it is left unchanged. */\n UnsupportedStrategy: 'LP0407',\n /** `data-payload-format` names a format the vocabulary does not contain; the default formatting was used instead. */\n UnknownValueFormat: 'LP0408',\n /** The strict sanitizer removed an attribute the 1.x `'compat'` policy kept. */\n SanitizerDroppedAttribute: 'LP0409',\n /** A Lexical block has no renderer; the markup the server rendered for it is kept instead. */\n UnrenderedBlockKept: 'LP0410',\n /** A patch cannot reach what the server would have drawn; `onUnfaithfulPatch` decided what happens next. */\n UnfaithfulPatch: 'LP0411',\n /** The first write to a date, number, checkbox, select or radio binding replaced a different reading of the same value. */\n ServerFormatReplaced: 'LP0412',\n /** A Lexical block has no renderer, and the write could not keep the markup the server rendered for it; it is gone. */\n UnrenderedBlockLost: 'LP0413',\n\n /** A message was rejected before it reached the update pipeline. */\n MessageRejected: 'LP0501',\n /** A preview token was rejected. */\n TokenRejected: 'LP0502',\n /** A trusted origin sent a message this protocol version does not recognise. */\n ProtocolShapeUnknown: 'LP0503',\n\n /** A consumer event handler threw. */\n HandlerThrew: 'LP0601',\n /** A consumer transform threw; the original value was kept. */\n TransformThrew: 'LP0602',\n /** A renderer threw while writing a value. */\n RendererThrew: 'LP0603',\n // LP0604 is reserved and unassigned; codes are never reused or renumbered.\n /** Runtime startup failed. */\n StartupFailed: 'LP0605',\n /** Sending the ready handshake failed. */\n ReadyFailed: 'LP0606',\n /** The page declared hydration, but no React commit, Vue mount, SvelteKit mount or `markHydrated()` call came within the cap; the runtime started without waiting. */\n HydrationWaitTimedOut: 'LP0607',\n\n /** The audit found no runtime in a response that carried preview intent. */\n AuditRuntimeMissing: 'LP0701',\n /** The preview response declares no `frame-ancestors`. */\n AuditNoFrameAncestors: 'LP0702',\n /** `X-Frame-Options` forbids framing on a response that declares no `frame-ancestors`; `SAMEORIGIN` forbids it from another origin only. */\n AuditFrameOptionsBlocks: 'LP0703',\n /** Binding attributes are served to anonymous visitors. */\n AuditBindingsExposed: 'LP0704',\n /** More bindings than the visibility gate will write eagerly. */\n AuditGateThresholdExceeded: 'LP0705',\n /** Owner markers exist, but some bindings are outside all of them. */\n AuditUnownedBindings: 'LP0706',\n /** The preview response carries no bindings and no fragment boundary. */\n AuditNoBindings: 'LP0707',\n /** The URL did not return an HTML page, so nothing else can be judged. */\n AuditNotAPage: 'LP0708',\n /** A fragment request failed (network, timeout, server error); the boundary was patched instead. */\n FragmentRequestFailed: 'LP0801',\n /** The fragment response had the wrong content type or shape; the boundary was patched instead. */\n FragmentResponseInvalid: 'LP0802',\n /** The fragment endpoint refused the preview (not authorized); the boundary was patched instead. */\n FragmentUnauthorized: 'LP0803',\n /** A fragment response arrived for a revision that was already superseded and was discarded. */\n FragmentSuperseded: 'LP0804',\n /** A route refresh was refused: the loop guard, or the strategy's minimum interval. */\n RouteRefreshLoop: 'LP0805',\n /** A boundary asks for the fragment strategy but no fragment handler is configured; it is patched. */\n FragmentStrategyUnavailable: 'LP0806',\n /** A revision changed a field with no binding; `onUnfaithfulPatch` refreshed the route. */\n UnboundChangeRefresh: 'LP0807',\n /** `onUnfaithfulPatch: 'escalate'` found no route or fragment strategy, or no boundary, to hand an unfaithful patch to; the page keeps it. */\n EscalationUnavailable: 'LP0808',\n /** A fragment inserted an Astro island the page cannot start: it never loaded the island element or that client directive. */\n IslandStartUnavailable: 'LP0809',\n /** The host's route refresh returned no promise, so the revision was re-applied before the host committed. */\n RouteRefreshUnconfirmed: 'LP0810',\n /** A readiness row is not yet at its 2.0 value; `pll doctor --v2` reports it. */\n V2ReadinessGap: 'LP0709',\n /** The preview runtime is served to anonymous visitors, not only inside the admin frame. */\n RuntimeOnPublicPage: 'LP0710',\n /** An element carries attributes that only mean something beside `data-payload-field`, and has none. */\n AuditModifierWithoutField: 'LP0711',\n} as const);\n\n/** A diagnostic code, as it appears in log output and on the `error` event. */\nexport type DiagnosticCode = (typeof DIAGNOSTIC_CODES)[keyof typeof DIAGNOSTIC_CODES];\n","/**\n * Keyed DOM morph, the engine: edits a live element toward a freshly rendered\n * one while retaining compatible, paired live nodes. Those nodes keep their\n * live properties; an incompatible or unpaired node is replaced and loses\n * them. It pairs children by key or position, synchronises attributes with the\n * state exception and keeps focus across a retained move; which subtrees it\n * never enters is the coordinator's rule, handed in as\n * `MorphOptions.boundary` and defaulting to the package's (`isMorphBoundary`\n * in islands.ts). See ADR 0008, §9 for the line between the two. A custom\n * element is such a boundary unless both of its sides carry\n * `data-payload-morph` (§10).\n */\n\nimport { isMorphBoundary } from './islands';\n\n/** Attributes the CMS controls only when the template names them (ADR 0008 §3). */\nconst STATE_ATTRIBUTES: ReadonlySet<string> = new Set(['open', 'value', 'checked', 'selected']);\n\n/** @beta */\nexport interface MorphOptions {\n /** Attributes that key child elements for pairing, tried in order; unkeyed children pair by position. */\n readonly keyAttributes: readonly string[];\n /** Called once per parent when two children share a key; the later ones pair by position. */\n readonly onDuplicateKey?: (parent: Element, key: string) => void;\n /** Elements whose attributes are synchronised but whose children are left alone (nested structural slots). */\n readonly retainChildrenOf?: (live: Element, rendered: Element) => boolean;\n /**\n * The ownership rule: a structurally compatible live/rendered pair for\n * which this returns `true` on both sides is retained whole and never\n * entered (ADR 0008 §4). The package's rule — custom elements, islands,\n * `contenteditable`, `data-payload-owned` — is the default. A supplied rule\n * replaces that default; compose it with the exported `isMorphBoundary`\n * predicate to add package-compatible boundaries.\n */\n readonly boundary?: (element: Element) => boolean;\n /**\n * Decides a live/rendered pair that are both boundaries: `true` keeps the\n * live one, after any handoff to its owner; `false` replaces it with the\n * rendered one. Without it every such pair is kept whole (ADR 0008 §4). The\n * package's coordinators hand in `retainIslandBoundary`, under which an\n * Astro island that still renders the same component takes the rendered\n * props through Astro's own handoff (ADR 0021).\n */\n readonly retainBoundary?: (live: Element, rendered: Element) => boolean;\n}\n\n/** Empty attribute values (boolean markers such as `data-payload-island`) are not keys. */\nfunction keyOf(element: Element, options: MorphOptions): string | undefined {\n for (const attribute of options.keyAttributes) {\n const value = element.getAttribute(attribute);\n if (value !== null && value.length > 0) return `${attribute}=${value}`;\n }\n return undefined;\n}\n\n/** Whether `live` can be edited toward `rendered` instead of being replaced by it, under `boundary` (the package's rule by default). @internal */\nexport function isMorphCompatible(\n live: Element,\n rendered: Element,\n boundary: (element: Element) => boolean = isMorphBoundary,\n): boolean {\n return (\n live.tagName === rendered.tagName &&\n live.namespaceURI === rendered.namespaceURI &&\n !boundary(live) &&\n !boundary(rendered)\n );\n}\n\n/**\n * Morph `live` toward `rendered`. Returns `live` when it was retained, or\n * `rendered` when the two are incompatible and the caller must replace.\n * `rendered` is consumed: its children may move into `live`.\n *\n * @beta\n */\nexport function morphElement(live: Element, rendered: Element, options: MorphOptions): Element {\n if (!sameKind(live, rendered)) return rendered;\n const boundary = options.boundary ?? isMorphBoundary;\n const liveBoundary = boundary(live);\n const renderedBoundary = boundary(rendered);\n if (liveBoundary || renderedBoundary) {\n if (!liveBoundary || !renderedBoundary) return rendered;\n return (options.retainBoundary?.(live, rendered) ?? true) ? live : rendered;\n }\n const focus = captureFocus(live);\n syncAttributes(live, rendered);\n if (options.retainChildrenOf?.(live, rendered) !== true) morphChildren(live, rendered, options);\n restoreFocus(focus);\n return live;\n}\n\n/** Match attributes, except that state-bearing ones change only when `rendered` carries them. */\nexport function syncAttributes(live: Element, rendered: Element): void {\n for (const attribute of Array.from(live.attributes)) {\n if (rendered.hasAttribute(attribute.name)) continue;\n if (STATE_ATTRIBUTES.has(attribute.name.toLowerCase())) continue;\n live.removeAttribute(attribute.name);\n }\n for (const attribute of Array.from(rendered.attributes)) {\n if (live.getAttribute(attribute.name) !== attribute.value) {\n live.setAttribute(attribute.name, attribute.value);\n }\n }\n}\n\nfunction morphChildren(live: Element, rendered: Element, options: MorphOptions): void {\n const liveKeyed = indexKeyed(live, options);\n const keyedElements = new Set<Node>(liveKeyed.values());\n const liveUnkeyed = Array.from(live.childNodes).filter((node) => !keyedElements.has(node));\n const claimed = new Set<Node>();\n const usedKeys = new Set<string>();\n const renderedNodes = Array.from(rendered.childNodes);\n let cursor: Node | null = live.firstChild;\n let unkeyedIndex = 0;\n\n for (let i = 0; i < renderedNodes.length; i += 1) {\n const next = renderedNodes[i] as Node;\n let kept: Node | null = null;\n let key = next instanceof Element ? keyOf(next, options) : undefined;\n // A repeated key on the rendered side pairs positionally like a live duplicate.\n if (key !== undefined && usedKeys.has(key)) key = undefined;\n if (key !== undefined) {\n usedKeys.add(key);\n const candidate = liveKeyed.get(key);\n if (candidate !== undefined && !claimed.has(candidate)) {\n kept = reconcile(candidate, next, options) === candidate ? candidate : null;\n if (kept === null) {\n candidate.replaceWith(next);\n claimed.add(next);\n cursor = next.nextSibling;\n continue;\n }\n }\n } else {\n while (unkeyedIndex < liveUnkeyed.length) {\n const candidate = liveUnkeyed[unkeyedIndex] as Node;\n unkeyedIndex += 1;\n if (claimed.has(candidate)) continue;\n if (sameKind(candidate, next)) {\n kept = reconcile(candidate, next, options) === candidate ? candidate : null;\n if (kept === null) {\n live.replaceChild(next, candidate);\n claimed.add(next);\n cursor = next.nextSibling;\n }\n break;\n }\n if (candidate instanceof Element) {\n // `next` is a text/comment, or an element whose kind the following\n // rendered node matches: an insertion, so the live element stays for it.\n const following = renderedNodes[i + 1];\n if (\n !(next instanceof Element) ||\n (following !== undefined && sameKind(candidate, following))\n ) {\n unkeyedIndex -= 1;\n break;\n }\n live.replaceChild(next, candidate);\n claimed.add(next);\n cursor = next.nextSibling;\n break;\n }\n if (!(next instanceof Element)) {\n // Text vs comment: replace rather than rewrite one node type's value into the other.\n live.replaceChild(next, candidate);\n claimed.add(next);\n cursor = next.nextSibling;\n break;\n }\n // A surplus live text/comment before the element `next` needs; removed below.\n }\n if (kept === null && claimed.has(next)) continue;\n }\n\n const node: Node = kept ?? next;\n claimed.add(node);\n // Whitespace-only text the template did not render is surplus. Stepping the\n // cursor over it keeps a retained element where it is: `insertBefore` on a\n // node already in the tree is a remove-and-insert, which blurs a focused input.\n while (cursor !== null && cursor !== node && !claimed.has(cursor) && isBlankText(cursor)) {\n cursor = cursor.nextSibling;\n }\n if (node !== cursor) live.insertBefore(node, cursor);\n cursor = node.nextSibling;\n }\n\n for (const child of Array.from(live.childNodes)) {\n if (!claimed.has(child)) child.remove();\n }\n}\n\n/** Retain `candidate` when it can be edited toward `next`; otherwise report `next`. */\nfunction reconcile(candidate: Node, next: Node, options: MorphOptions): Node {\n if (candidate instanceof Element && next instanceof Element) {\n return morphElement(candidate, next, options);\n }\n if (candidate.nodeValue !== next.nodeValue) candidate.nodeValue = next.nodeValue;\n return candidate;\n}\n\nfunction isBlankText(node: Node): boolean {\n return node.nodeType === Node.TEXT_NODE && (node.nodeValue ?? '').trim().length === 0;\n}\n\nfunction sameKind(a: Node, b: Node): boolean {\n if (a.nodeType !== b.nodeType) return false;\n if (a instanceof Element && b instanceof Element) {\n return a.tagName === b.tagName && a.namespaceURI === b.namespaceURI;\n }\n return true;\n}\n\n/** First element per key; later duplicates are left as they are and pair positionally. */\nfunction indexKeyed(parent: Element, options: MorphOptions): Map<string, Element> {\n const keyed = new Map<string, Element>();\n let duplicate: string | undefined;\n for (const child of Array.from(parent.children)) {\n const key = keyOf(child, options);\n if (key === undefined) continue;\n if (keyed.has(key)) {\n duplicate ??= key.slice(key.indexOf('=') + 1);\n continue;\n }\n keyed.set(key, child);\n }\n if (duplicate !== undefined) options.onDuplicateKey?.(parent, duplicate);\n return keyed;\n}\n\n/** @internal */\nexport interface FocusSnapshot {\n readonly element: HTMLElement;\n readonly selection: readonly [number | null, number | null] | null;\n}\n\n/**\n * A keyed move is a remove-and-insert, which blurs; remember what had focus.\n * The structural applier moves items itself and uses the same pair around\n * its commit, so a move it makes keeps focus the way a move the morph makes\n * does (ADR 0008 §1). @internal\n */\nexport function captureFocus(live: Element): FocusSnapshot | null {\n const active = live.ownerDocument.activeElement;\n if (!(active instanceof HTMLElement) || !live.contains(active)) return null;\n const selection =\n active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement\n ? ([active.selectionStart, active.selectionEnd] as const)\n : null;\n return { element: active, selection };\n}\n\n/** @internal */\nexport function restoreFocus(snapshot: FocusSnapshot | null): void {\n if (snapshot === null) return;\n const { element, selection } = snapshot;\n if (element.ownerDocument.activeElement === element || !element.isConnected) return;\n try {\n element.focus({ preventScroll: true });\n const [start, end] = selection ?? [null, null];\n if (start !== null && end !== null) {\n (element as HTMLInputElement).setSelectionRange(start, end);\n }\n } catch {\n // Not focusable any more; nothing to restore.\n }\n}\n","/**\n * The route strategy: when a revision touches what no smaller strategy can\n * render, ask the host's own router to re-render it, or else fetch the route\n * again and morph it in place. A refresh inside `minIntervalMs` is refused as\n * LP0805 and handed back for a trailing run. See ADR 0011.\n */\n\nimport {\n astroIslandsIn,\n isMorphBoundary,\n OWNED_ATTRIBUTE,\n releaseDisconnectedIslands,\n retainIslandBoundary,\n} from '@core/islands';\nimport { morphElement } from '@core/morph';\nimport { KEY_ATTRIBUTE } from '@core/structural-applier';\nimport { parseDependencyList } from '@core/dependencies';\nimport { readRouteRefresh } from '@core/route-refresh';\nimport { FRAGMENT_ATTRIBUTE, type RouteContext, type RouteStrategy } from '@core/strategies';\nimport { errorMessage, linkedTimeout } from './abort';\nimport { FRAGMENT_KEY_ATTRIBUTE } from './boundary';\n\nconst STRATEGY_ATTRIBUTE = 'data-payload-strategy';\nconst FIELD_ATTRIBUTE = 'data-payload-field';\nconst DEPENDS_ATTRIBUTE = 'data-payload-depends';\n/** Sent with every refresh so a server can tell a preview refresh from navigation. @internal */\nexport const ROUTE_REFRESH_HEADER = 'x-payload-live-preview';\n\nexport interface RouteStrategyOptions {\n /** `fetch` to use; defaults to the global one. */\n readonly fetch?: (url: string, init?: RequestInit) => Promise<Response>;\n /** Per-refresh timeout. Default 8000 ms. */\n readonly timeoutMs?: number;\n /** Shortest gap between two refreshes; one inside it is refused with LP0805 and run when the gap closes. Default 1000 ms. */\n readonly minIntervalMs?: number;\n /** The document to refresh; defaults to `document`. */\n readonly document?: Document;\n /** Where the page lives; defaults to `location`. */\n readonly location?: { readonly href: string };\n /** Scroll access; defaults to `window`. */\n readonly window?: {\n readonly scrollX: number;\n readonly scrollY: number;\n readonly scrollTo: (x: number, y: number) => void;\n };\n}\n\nconst DEFAULT_TIMEOUT_MS = 8_000;\nconst UNCONFIRMED_REFRESH =\n 'route refresh returned no promise; re-applied before the host committed (see LivePreviewRouteRefresh)';\n\nconst DEFAULT_MIN_INTERVAL_MS = 1_000;\n\n/** An explicit `data-payload-strategy=\"route\"`, or a binding in `<head>`. @internal */\nexport function isRouteBound(element: Element): boolean {\n const explicit = element.getAttribute(STRATEGY_ATTRIBUTE);\n if (explicit !== null) return explicit === 'route';\n return element.closest('head') !== null;\n}\n\nfunction fieldsOf(element: Element): readonly string[] {\n const own = element.getAttribute(FIELD_ATTRIBUTE);\n const depends = parseDependencyList(element.getAttribute(DEPENDS_ATTRIBUTE));\n return own === null || own.length === 0 ? depends : [own, ...depends];\n}\n\nfunction isManagedHeadElement(element: Element): boolean {\n if (element.hasAttribute(OWNED_ATTRIBUTE)) return false;\n if (element.tagName === 'META') {\n return element.hasAttribute('name') || element.hasAttribute('property');\n }\n return element.tagName === 'LINK' && element.getAttribute('rel') === 'canonical';\n}\n\n/**\n * Make `<title>` and the ordered direct children this strategy manages match\n * the fresh head, including duplicates and removals. A tag marked\n * `data-payload-owned` belongs to a script and is left alone.\n */\nfunction syncHead(live: Document, fresh: Document): void {\n const liveTitle = Array.from(live.head.children).find((element) => element.tagName === 'TITLE');\n const freshTitle = Array.from(fresh.head.children).find((element) => element.tagName === 'TITLE');\n if (\n !liveTitle?.hasAttribute(OWNED_ATTRIBUTE) &&\n !freshTitle?.hasAttribute(OWNED_ATTRIBUTE) &&\n fresh.title !== live.title\n ) {\n live.title = fresh.title;\n }\n\n const liveSlots = Array.from(live.head.children).filter(isManagedHeadElement);\n const targets = Array.from(fresh.head.children).filter(isManagedHeadElement);\n const paired = Math.min(liveSlots.length, targets.length);\n liveSlots.slice(0, paired).forEach((current, index) => {\n const target = targets[index];\n if (target === undefined) return;\n if (current.tagName === target.tagName) {\n for (const attribute of Array.from(current.attributes)) {\n if (!target.hasAttribute(attribute.name)) current.removeAttribute(attribute.name);\n }\n for (const attribute of Array.from(target.attributes)) {\n if (current.getAttribute(attribute.name) !== attribute.value) {\n current.setAttribute(attribute.name, attribute.value);\n }\n }\n return;\n }\n const replacement = live.importNode(target, true);\n current.replaceWith(replacement);\n liveSlots[index] = replacement;\n });\n\n let anchor = liveSlots.at(-1);\n for (const target of targets.slice(liveSlots.length)) {\n const addition = live.importNode(target, true);\n if (anchor === undefined) live.head.append(addition);\n else anchor.after(addition);\n anchor = addition;\n }\n for (const surplus of liveSlots.slice(targets.length)) {\n surplus.remove();\n }\n}\n\nexport function createRouteStrategy(options: RouteStrategyOptions = {}): RouteStrategy {\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const minIntervalMs = options.minIntervalMs ?? DEFAULT_MIN_INTERVAL_MS;\n let lastRefreshAt = -Infinity;\n let saidUnconfirmed = false;\n\n return {\n plan(root, changedFields, context) {\n for (const element of root.querySelectorAll(\n `[${FIELD_ATTRIBUTE}], [${STRATEGY_ATTRIBUTE}=\"route\"]`,\n )) {\n if (!isRouteBound(element) || context?.inScope(element) === false) continue;\n const fields = fieldsOf(element);\n if (fields.length === 0 || fields.some((field) => changedFields.has(field))) return true;\n }\n return false;\n },\n async refresh(context: RouteContext) {\n const now = Date.now();\n const waitMs = minIntervalMs - (now - lastRefreshAt);\n if (waitMs > 0) {\n // The window paces the server; it does not decide what the editor gets\n // to see. Handing the request back means the change that ended a burst\n // still arrives, instead of waiting for a keystroke that never comes.\n context.retryAfter?.(waitMs);\n context.log(\n 'LP0805',\n `route refresh for revision ${String(context.revision)} refused: the previous one was ${String(now - lastRefreshAt)} ms ago`,\n );\n return 'refused';\n }\n lastRefreshAt = now;\n const live = options.document ?? document;\n const where = options.location ?? location;\n const view = options.window ?? window;\n const timeout = linkedTimeout(context.signal, timeoutMs);\n const failure = (error: unknown): 'failed' | 'superseded' => {\n if (context.signal.aborted) return 'superseded';\n // A timeout surfaces as an AbortError whose text differs per browser.\n const detail = timeout.timedOut()\n ? `route refresh timed out after ${String(timeoutMs)} ms`\n : errorMessage(error);\n context.log('LP0801', detail);\n return 'failed';\n };\n try {\n const hostRefresh = readRouteRefresh();\n if (hostRefresh !== undefined) {\n // The host owns this DOM: it re-renders the route itself, so there is\n // no second HTML request and no morph over a reconciler's own nodes.\n // Awaited, because the runtime re-applies the revision straight after.\n // A host that returns nothing cannot be waited for (ADR 0028).\n try {\n const settled: unknown = hostRefresh();\n if (\n typeof (settled as { then?: unknown } | null | undefined)?.then !== 'function' &&\n !saidUnconfirmed\n ) {\n saidUnconfirmed = true;\n context.log('LP0810', UNCONFIRMED_REFRESH);\n }\n await settled;\n } catch (error) {\n return failure(error);\n }\n return context.isCurrent() ? 'partial' : 'superseded';\n }\n let response: Response;\n try {\n response = await (options.fetch ?? fetch)(where.href, {\n method: 'GET',\n credentials: 'same-origin',\n cache: 'no-store',\n headers: { accept: 'text/html', [ROUTE_REFRESH_HEADER]: 'route' },\n signal: timeout.signal,\n });\n } catch (error) {\n return failure(error);\n }\n if (context.signal.aborted) return 'superseded';\n if (!response.ok || !(response.headers.get('content-type') ?? '').includes('text/html')) {\n context.log(\n 'LP0802',\n `route answered ${String(response.status)} ${response.headers.get('content-type') ?? ''}`,\n );\n return 'failed';\n }\n try {\n const html = await response.text();\n if (!context.isCurrent()) return 'superseded';\n const fresh = new DOMParser().parseFromString(html, 'text/html');\n const x = view.scrollX;\n const y = view.scrollY;\n syncHead(live, fresh);\n // Top-level boundaries pair by identity so a comment or a runtime-added\n // node cannot mispair them; fragment boundaries keep their children —\n // the fragment strategy re-renders those for this revision and must\n // not lose the focused input inside one.\n const islands = astroIslandsIn(live.body);\n morphElement(live.body, fresh.body, {\n boundary: isMorphBoundary,\n retainBoundary: retainIslandBoundary,\n keyAttributes: [KEY_ATTRIBUTE, FRAGMENT_KEY_ATTRIBUTE, FRAGMENT_ATTRIBUTE],\n retainChildrenOf: (element) => element.hasAttribute(FRAGMENT_ATTRIBUTE),\n });\n releaseDisconnectedIslands(islands);\n view.scrollTo(x, y);\n return 'partial';\n } catch (error) {\n return failure(error);\n }\n } finally {\n timeout.dispose();\n }\n },\n };\n}\n","/**\n * `payload-live-preview/fragment` — the fragment and route strategies. The\n * core carries only the seam (`@core/strategies`); this entry owns the\n * orchestration. A request it sends identifies the page by its own URL and a\n * boundary by its registry id; it never carries code, templates, import paths\n * or file paths. See ADR 0011.\n */\n\nimport type { FragmentContext, FragmentReport, FragmentStrategy } from '@core/strategies';\nimport { errorMessage } from './abort';\nimport {\n collectFragmentBoundaries,\n describeBoundary,\n type FragmentHandler,\n type FragmentOutcome,\n type StrategyRequest,\n} from './boundary';\nimport { createFragmentHandler, type FragmentStrategyOptions } from './handler';\n\nexport type {\n FragmentContext,\n FragmentReport,\n FragmentStrategy,\n RouteContext,\n RouteOutcome,\n RoutePlanContext,\n RouteStrategy,\n StrategyHandlers,\n UpdateSource,\n} from '@core/strategies';\nexport { FRAGMENT_ATTRIBUTE, resolveStrategy } from '@core/strategies';\nexport { DIAGNOSTIC_CODES, type DiagnosticCode } from '@core/diagnostic-codes';\nexport {\n collectFragmentBoundaries,\n describeBoundary,\n FRAGMENT_KEY_ATTRIBUTE,\n type FragmentBoundary,\n type FragmentHandler,\n type FragmentOutcome,\n type StrategyRequest,\n} from './boundary';\nexport { createFragmentHandler, type FragmentStrategyOptions } from './handler';\nexport {\n createRouteStrategy,\n isRouteBound,\n ROUTE_REFRESH_HEADER,\n type RouteStrategyOptions,\n} from './route';\nexport {\n FRAGMENT_PROTOCOL_VERSION,\n FRAGMENT_VERSION_HEADER,\n parseFragmentRequest,\n parseFragmentResponse,\n type FragmentRequestBody,\n type FragmentResponseBody,\n} from '@/types/fragment-protocol';\n\n/**\n * The strategy the runtime drives, built on a per-boundary handler: plan the\n * boundaries, render each, morph a success, patch a failure. A late or\n * aborted render is dropped, never applied.\n * @internal\n */\nexport function fragmentStrategyFrom(handler: FragmentHandler): FragmentStrategy {\n return {\n plan: (root, changedFields) =>\n collectFragmentBoundaries(root, changedFields).map((boundary) => boundary.element),\n async render(context: FragmentContext, elements: readonly Element[]): Promise<FragmentReport> {\n const report = { rendered: 0, failed: 0, superseded: 0 };\n const request: StrategyRequest = {\n revision: context.revision,\n receivedAt: context.receivedAt,\n fields: context.fields,\n locale: context.locale,\n collectionSlug: context.collectionSlug,\n globalSlug: context.globalSlug,\n signal: context.signal,\n };\n await Promise.all(\n elements.map(async (element) => {\n const boundary = describeBoundary(element);\n if (boundary === null) return;\n let outcome: FragmentOutcome;\n try {\n outcome = await handler(request, boundary);\n } catch (error) {\n outcome = {\n status: 'failed',\n code: 'LP0801',\n reason: errorMessage(error),\n };\n }\n if (!context.isCurrent() || outcome.status === 'superseded') {\n report.superseded += 1;\n context.log(\n 'LP0804',\n `fragment \"${boundary.id}\" for revision ${String(request.revision)} discarded as superseded`,\n );\n return;\n }\n if (outcome.status === 'rendered') {\n context.morph(element, outcome.html);\n context.rendered(element, boundary.id, boundary.key);\n report.rendered += 1;\n return;\n }\n context.failed(element, boundary.id, boundary.key, outcome.code, outcome.reason);\n if (context.isCurrent()) context.patch(element);\n report.failed += 1;\n }),\n );\n return report;\n },\n };\n}\n\n/** The strategy for a fragment endpoint: hand it to the runtime as `strategies.fragment`. */\nexport function createFragmentStrategy(options: FragmentStrategyOptions): FragmentStrategy {\n return fragmentStrategyFrom(createFragmentHandler(options));\n}\n","/**\n * The seam between a host framework's own router and the route strategy.\n *\n * The route strategy answers a revision by fetching the route and morphing it\n * into the living page. On a page whose DOM belongs to a reconciler that is\n * working against the framework, and it was seen to break once: `removeChild`\n * on `null` inside React's commit phase, over markup this package had patched.\n * A host that re-renders the route itself does the same job without the morph\n * and without the second HTML request, so it is given the chance to.\n *\n * Why a named slot on `window` and not a reference: the runtime is an inline\n * script that builds its strategy before any application code has hydrated, so\n * there is nothing to hand a component; `window.__livePreview`, the only other\n * public surface, is created later and not at all outside a preview; and the\n * two richer extension points — plugins and events — are reachable only through\n * the programmatic client, which a page using an adapter does not have. One\n * slot, written by whoever mounts and read at the moment of a refresh, is the\n * smallest thing that survives both orders of arrival.\n */\n\n/**\n * A host's own route refresh. It must settle once the fresh markup is\n * committed: the runtime re-applies the revision on top of it, and doing that\n * against the markup being replaced would write into nodes about to be dropped.\n * A function that returns nothing is taken as already committed; the route\n * strategy says once that it could not wait (LP0810), and 3.0 requires the\n * promise (ADR 0028).\n */\nexport type RouteRefresh = () => void | Promise<void>;\n\n/** Read late, so it does not matter whether the runtime or the component came first. */\nconst SLOT = '__livePreviewRouteRefresh';\n\ntype WindowSlot = Record<string, RouteRefresh | undefined>;\n\nfunction slot(): WindowSlot | undefined {\n return typeof window === 'undefined' ? undefined : (window as unknown as WindowSlot);\n}\n\n/**\n * Lend the runtime the host's route refresh; the return value undoes it, for a\n * component's cleanup. A later registration wins, and undoing one that has\n * already been replaced does nothing — two components on one page would\n * otherwise take the slot from each other on unmount.\n */\nexport function registerRouteRefresh(refresh: RouteRefresh): () => void {\n const target = slot();\n if (target === undefined) return () => undefined;\n target[SLOT] = refresh;\n return () => {\n // Cleared rather than deleted: the slot is read through `?.` anyway, and\n // removing the key buys nothing a lint rule would not have to be argued out of.\n if (target[SLOT] === refresh) target[SLOT] = undefined;\n };\n}\n\n/** The registered refresh, or `undefined` on a page that registered none. */\nexport function readRouteRefresh(): RouteRefresh | undefined {\n return slot()?.[SLOT];\n}\n","/**\n * Applies an `ArrayPatch[]` diff to a container as keyed DOM operations\n * instead of rebuilding the list, so consumer state on surviving children is\n * kept. Items are paired by `data-payload-key` (the item `id`); items without\n * one pair by position. Nested arrays reconcile recursively through\n * `data-payload-nested-key` slots. See ADR 0008.\n */\n\nimport { sanitizeHtmlWithPolicy, type SanitizerPolicyMode } from '@security/sanitizer';\nimport { trustedHtml } from '@security/trusted-types';\nimport {\n collectTemplateKeys,\n inheritItemAttributes,\n interpolateArrayTemplate,\n sharedItemAttributes,\n type InheritedAttribute,\n} from './array-template';\nimport { safeStringify } from '@field-types/utils';\nimport { diffArray, type ArrayPatch } from '@schema/diff';\nimport { isMorphBoundary, retainIslandBoundary } from './islands';\nimport { captureFocus, restoreFocus, morphElement } from './morph';\nimport { templateSanitizeOptions } from './template-sanitize';\n\n/** @internal */\nexport const KEY_ATTRIBUTE = 'data-payload-key';\nconst NESTED_KEY_ATTRIBUTE = 'data-payload-nested-key';\nconst NESTED_TEMPLATE_ATTRIBUTE = 'data-payload-nested-template';\n\n/** Last value seen per item key, per container; owned by the caller so instances never share it. @internal */\nexport type StructuralStore = WeakMap<Element, Map<string, unknown>>;\n\n/** @internal */\nexport function createStructuralStore(): StructuralStore {\n return new WeakMap<Element, Map<string, unknown>>();\n}\n\n/** @internal */\nexport interface StructuralApplyOptions {\n readonly template: string;\n readonly container: Element;\n readonly patches: readonly ArrayPatch[];\n /** The full new array; inserts and replaces render by index from it. */\n readonly nextItems: readonly unknown[];\n /** Pass the same store across renders so nested arrays diff incrementally. */\n readonly store: StructuralStore;\n /** Re-render existing items even when unchanged. */\n readonly forceRender?: boolean;\n /** The instance's policy for item markup; absent, the process default applies. */\n readonly sanitizerPolicy?: SanitizerPolicyMode | undefined;\n /** Edit a changed item in place instead of replacing it. Default `true`. */\n readonly morph?: boolean;\n /** Reported once per container when two items share a key (`LP0405`). */\n readonly onDuplicateKey?: (container: Element, key: string) => void;\n}\n\n/**\n * Returns `true` for a DOM mutation, `false` for an already-current tree, and\n * `null` when an item's markup has no sanitized element root — preflighted\n * before any mutation, so the previous DOM and memory stay intact.\n * @internal\n */\nexport function applyStructuralPatches(options: StructuralApplyOptions): boolean | null {\n const plan = prepareStructuralPlan(options.container, options.patches, options);\n if (plan === null) return null;\n return commitStructuralPlan(plan, options.store, {\n morph: options.morph ?? true,\n onDuplicateKey: options.onDuplicateKey,\n });\n}\n\n// Plan records are tuples: many are created per recursive render, and named\n// keys would repeat in the inline bundle.\ntype StructuralPlan = readonly [\n container: Element,\n entries: readonly ReconciliationEntry[],\n nextItems: readonly unknown[],\n];\ntype ReconciliationEntry = readonly [\n index: number,\n live: Element | null,\n rendered: Element | undefined,\n nestedSlots: readonly NestedSlotPlan[],\n /** A `replace` patch: a different item, never morphed toward. */\n replace: boolean,\n];\ntype NestedSlotPlan = readonly [\n live: Element | null,\n rendered: Element,\n children: StructuralPlan | undefined,\n];\n\n// One per container, not per item, so — unlike the tuples above — named keys\n// cost the inline bundle nothing per render.\ntype RenderEnv = Pick<\n StructuralApplyOptions,\n 'template' | 'nextItems' | 'forceRender' | 'store' | 'sanitizerPolicy'\n>;\n\ninterface CommitOptions {\n readonly morph: boolean;\n readonly onDuplicateKey: ((container: Element, key: string) => void) | undefined;\n}\n\n/** A rendered slot that still carries a nested template keeps its children for its own plan. */\nfunction isManagedNestedSlot(_live: Element, rendered: Element): boolean {\n return (\n rendered.hasAttribute(NESTED_KEY_ATTRIBUTE) && rendered.hasAttribute(NESTED_TEMPLATE_ATTRIBUTE)\n );\n}\n\n/** Materialise the whole recursive replacement tree before touching live DOM. */\nfunction prepareStructuralPlan(\n container: Element,\n patches: readonly ArrayPatch[],\n env: RenderEnv,\n): StructuralPlan | null {\n const entries = prepareReconciliation(container, createPatchPlan(patches), env);\n return entries === null ? null : [container, entries, env.nextItems];\n}\n\nfunction commitStructuralPlan(\n plan: StructuralPlan,\n store: StructuralStore,\n options: CommitOptions,\n): boolean {\n const [container, entries, nextItems] = plan;\n const claimed = new Set<Element>();\n let mutated = false;\n // Placing a retained item is a remove-and-insert, which blurs whatever the\n // visitor had focused inside it; the morph does the same for a move it\n // makes and restores afterwards, so the commit does too.\n const focus = captureFocus(container);\n for (const [index, live, rendered, nestedSlots, replace] of entries) {\n let node = live;\n if (rendered !== undefined) {\n // Morph a compatible live item in place; otherwise transplant its live\n // nested slots into the rendered item and swap.\n const retained =\n options.morph && !replace && live !== null\n ? morphElement(live, rendered, {\n keyAttributes: [KEY_ATTRIBUTE, NESTED_KEY_ATTRIBUTE],\n boundary: isMorphBoundary,\n retainBoundary: retainIslandBoundary,\n retainChildrenOf: isManagedNestedSlot,\n ...(options.onDuplicateKey !== undefined\n ? { onDuplicateKey: options.onDuplicateKey }\n : {}),\n }) === live\n : false;\n if (!retained) {\n for (const [liveSlot, renderedSlot] of nestedSlots) {\n if (liveSlot === null) continue;\n synchronizeAttributes(liveSlot, renderedSlot);\n renderedSlot.replaceWith(liveSlot);\n }\n }\n for (const [, , children] of nestedSlots) {\n if (children !== undefined) commitStructuralPlan(children, store, options);\n }\n if (retained) {\n mutated = true;\n } else {\n if (live !== null) {\n live.replaceWith(rendered);\n mutated = true;\n }\n node = rendered;\n }\n }\n if (node === null) continue;\n const before = container.children[index] ?? null;\n if (node !== before) {\n container.insertBefore(node, before);\n mutated = true;\n }\n claimed.add(node);\n }\n // `nextItems` is the source of truth; this also drops SSR children the first value did not carry.\n for (const child of Array.from(container.children)) {\n if (claimed.has(child)) continue;\n child.remove();\n mutated = true;\n }\n restoreFocus(focus);\n const memory = getMemory(container, store);\n memory.clear();\n for (const value of nextItems) rememberItem(memory, value);\n return mutated;\n}\n\n/**\n * Pair every final item with a live child (by key, else by position) and\n * render what needs rendering. Patch indices describe `next`, live indices\n * move during reconciliation, so keyed nodes are resolved from the DOM first.\n */\nfunction prepareReconciliation(\n container: Element,\n plan: PatchPlan,\n env: RenderEnv,\n): readonly ReconciliationEntry[] | null {\n const { template, nextItems, forceRender = false, store, sanitizerPolicy } = env;\n const memory = store.get(container);\n // Read before anything is rendered: what the current items share is what the\n // template cannot say, and an inserted item has no predecessor of its own.\n const shared = sharedItemAttributes(container);\n const templateKeys = collectTemplateKeys(nextItems);\n const initialChildren = Array.from(container.children);\n const keyedChildren = indexByAttribute(initialChildren, KEY_ATTRIBUTE);\n const reserved = new Set<Element>();\n const entries: ReconciliationEntry[] = [];\n for (let index = 0; index < nextItems.length; index += 1) {\n const value = nextItems[index];\n const key = readKey(value);\n let live =\n key === undefined\n ? (initialChildren[index] ?? null)\n : (takeIndexedElement(keyedChildren, key) ?? null);\n if (live !== null && reserved.has(live)) live = null;\n if (live !== null) reserved.add(live);\n const replace = plan.replaces.has(index);\n const needsRender = forceRender || plan.renders.has(index) || live === null;\n const rendered = needsRender\n ? renderItem(\n container.ownerDocument,\n template,\n value,\n index,\n sanitizerPolicy,\n shared,\n templateKeys,\n )\n : undefined;\n if (rendered === null) return null;\n const nestedSlots =\n rendered === undefined\n ? []\n : prepareNestedSlots(\n replace ? null : live,\n rendered,\n value,\n key === undefined ? undefined : memory?.get(key),\n env,\n );\n if (nestedSlots === null) return null;\n entries.push([index, live, rendered, nestedSlots, replace]);\n }\n return entries;\n}\n\ninterface PatchPlan {\n readonly renders: ReadonlySet<number>;\n readonly replaces: ReadonlySet<number>;\n}\n\nfunction createPatchPlan(patches: readonly ArrayPatch[]): PatchPlan {\n const renders = new Set<number>();\n const replaces = new Set<number>();\n for (const patch of patches) {\n if (patch.kind === 'insert' || patch.kind === 'update') renders.add(patch.index);\n else if (patch.kind === 'replace') {\n renders.add(patch.index);\n replaces.add(patch.index);\n }\n }\n return { renders, replaces };\n}\n\n/** Plan each direct nested slot; a failure at any depth aborts before commit. */\nfunction prepareNestedSlots(\n oldItem: Element | null,\n newItem: Element,\n nextValue: unknown,\n prevValue: unknown,\n env: RenderEnv,\n): readonly NestedSlotPlan[] | null {\n const oldSlots = oldItem === null ? [] : findDirectNestedSlots(oldItem);\n const oldSlotsByKey = indexByAttribute(oldSlots, NESTED_KEY_ATTRIBUTE);\n const plans: NestedSlotPlan[] = [];\n for (const newSlot of findDirectNestedSlots(newItem)) {\n const key = newSlot.getAttribute(NESTED_KEY_ATTRIBUTE);\n if (key === null) continue;\n const oldSlot = takeIndexedElement(oldSlotsByKey, key) ?? null;\n const nextTemplate = newSlot.getAttribute(NESTED_TEMPLATE_ATTRIBUTE);\n // Without a nested template the slot's rendered static content is authoritative.\n const liveSlot = nextTemplate ? oldSlot : null;\n const previousTemplate = liveSlot?.getAttribute(NESTED_TEMPLATE_ATTRIBUTE) ?? null;\n let children: StructuralPlan | undefined;\n if (nextTemplate) {\n const nextNested = readNestedArray(nextValue, key);\n if (nextNested !== undefined || liveSlot !== null) {\n const resolvedNext = nextNested ?? [];\n const prevNested = liveSlot === null ? [] : (readNestedArray(prevValue, key) ?? []);\n const patches = diffArray(prevNested, resolvedNext);\n const templateChanged = liveSlot !== null && previousTemplate !== nextTemplate;\n if (patches.length > 0 || templateChanged) {\n children =\n prepareStructuralPlan(liveSlot ?? newSlot, patches, {\n template: nextTemplate,\n nextItems: resolvedNext,\n forceRender: templateChanged,\n store: env.store,\n sanitizerPolicy: env.sanitizerPolicy,\n }) ?? undefined;\n if (children === undefined) return null;\n }\n }\n }\n plans.push([liveSlot, newSlot, children]);\n }\n return plans;\n}\n\n/** Only the outermost nested slots: a transplanted slot owns the recursion below it. */\nfunction findDirectNestedSlots(item: Element): readonly Element[] {\n return Array.from(item.querySelectorAll(`[${NESTED_KEY_ATTRIBUTE}]`)).filter((slot) => {\n let ancestor = slot.parentElement;\n while (ancestor !== null && ancestor !== item) {\n if (ancestor.hasAttribute(NESTED_KEY_ATTRIBUTE)) return false;\n ancestor = ancestor.parentElement;\n }\n return ancestor === item;\n });\n}\n\nfunction synchronizeAttributes(target: Element, source: Element): void {\n for (const attribute of Array.from(target.attributes)) {\n if (!source.hasAttribute(attribute.name)) target.removeAttribute(attribute.name);\n }\n for (const attribute of Array.from(source.attributes)) {\n if (target.getAttribute(attribute.name) !== attribute.value) {\n target.setAttribute(attribute.name, attribute.value);\n }\n }\n}\n\nfunction readNestedArray(value: unknown, key: string): readonly unknown[] | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const nested = (value as Record<string, unknown>)[key];\n return Array.isArray(nested) ? (nested as readonly unknown[]) : undefined;\n}\n\nfunction getMemory(container: Element, store: StructuralStore): Map<string, unknown> {\n let map = store.get(container);\n if (!map) {\n map = new Map();\n store.set(container, map);\n }\n return map;\n}\n\nfunction rememberItem(memory: Map<string, unknown>, value: unknown): void {\n const key = readKey(value);\n if (key !== undefined) memory.set(key, value);\n}\n\nfunction readKey(value: unknown): string | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const id = (value as Record<string, unknown>)['id'];\n return typeof id === 'string' || typeof id === 'number' ? String(id) : undefined;\n}\n\nfunction indexByAttribute(elements: readonly Element[], attribute: string): Map<string, Element[]> {\n const indexed = new Map<string, Element[]>();\n // Reverse order so pop() yields duplicates in DOM order.\n for (let index = elements.length - 1; index >= 0; index -= 1) {\n const element = elements[index];\n if (element === undefined) continue;\n const key = element.getAttribute(attribute);\n if (key === null) continue;\n const bucket = indexed.get(key);\n if (bucket === undefined) indexed.set(key, [element]);\n else bucket.push(element);\n }\n return indexed;\n}\n\nfunction takeIndexedElement(indexed: Map<string, Element[]>, key: string): Element | undefined {\n return indexed.get(key)?.pop();\n}\n\n/** Render one item; its `id` is stamped as `data-payload-key` so later diffs find it. */\nfunction renderItem(\n ownerDocument: Document,\n template: string,\n value: unknown,\n index: number,\n policy: SanitizerPolicyMode | undefined,\n shared: readonly InheritedAttribute[],\n knownKeys: ReadonlySet<string>,\n): Element | null {\n const filled = interpolateArrayTemplate(template, value, index, safeStringify, knownKeys);\n const safe = sanitizeHtmlWithPolicy(filled, policy, templateSanitizeOptions(template));\n const host = ownerDocument.createElement('template');\n host.innerHTML = trustedHtml(safe);\n const first = host.content.firstElementChild;\n if (!first) return null;\n // Before the morph, which strips from the live item every attribute the\n // rendered one lacks: the marker has to be on this side to survive.\n inheritItemAttributes(first, shared);\n const key = readKey(value);\n if (key !== undefined) first.setAttribute(KEY_ATTRIBUTE, key);\n return first;\n}\n"],"mappings":"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","ignoreList":[]}
|