@tanstack/redact 0.0.10 → 0.0.12

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.
@@ -7,18 +7,18 @@ var GUARD_WINDOW_MS = 3e3;
7
7
  function installHydrationScrollGuard() {
8
8
  if (typeof window === "undefined") return;
9
9
  const w = window;
10
- if (w.__tdomScrollGuardInstalled) return;
11
- w.__tdomScrollGuardInstalled = true;
10
+ if (w.__redactScrollGuardInstalled) return;
11
+ w.__redactScrollGuardInstalled = true;
12
12
  const guardStartedAt = performance.now();
13
13
  let lastUserScrollAt = 0;
14
14
  let programmatic = 0;
15
- w.__tdomScrollLog = [];
15
+ w.__redactScrollLog = [];
16
16
  window.addEventListener(
17
17
  "scroll",
18
18
  () => {
19
19
  if (programmatic === 0) {
20
20
  lastUserScrollAt = performance.now();
21
- w.__tdomScrollLog.push({ t: Math.round(lastUserScrollAt), ev: "user-scroll", y: window.scrollY });
21
+ w.__redactScrollLog.push({ t: Math.round(lastUserScrollAt), ev: "user-scroll", y: window.scrollY });
22
22
  }
23
23
  },
24
24
  { capture: true, passive: true }
@@ -29,7 +29,7 @@ function installHydrationScrollGuard() {
29
29
  const inGuardWindow = now - guardStartedAt < GUARD_WINDOW_MS;
30
30
  const userScrolledRecently = lastUserScrollAt > 0 && now - lastUserScrollAt < 1500;
31
31
  if (inGuardWindow && userScrolledRecently) {
32
- w.__tdomScrollLog.push({
32
+ w.__redactScrollLog.push({
33
33
  t: Math.round(now),
34
34
  ev: "suppressed",
35
35
  args: JSON.stringify(args).slice(0, 80),
@@ -38,7 +38,7 @@ function installHydrationScrollGuard() {
38
38
  });
39
39
  return;
40
40
  }
41
- w.__tdomScrollLog.push({
41
+ w.__redactScrollLog.push({
42
42
  t: Math.round(now),
43
43
  ev: "allowed",
44
44
  args: JSON.stringify(args).slice(0, 80),
@@ -115,6 +115,7 @@ var HEAD_KEY_ATTRS = {
115
115
  style: [],
116
116
  title: []
117
117
  };
118
+ var DOCUMENT_HEAD_TAGS = /* @__PURE__ */ new Set(["base", "link", "meta", "script", "style", "title"]);
118
119
  var CLAIMED = /* @__PURE__ */ new WeakSet();
119
120
  function headAttrsMatch(el, props, keys) {
120
121
  if (CLAIMED.has(el)) return false;
@@ -179,11 +180,17 @@ function adoptHostDom(fiber, parent) {
179
180
  const cursor = hydrationCursors.get(hostParent);
180
181
  if (!cursor) return false;
181
182
  const tag = fiber.type.toLowerCase();
183
+ const documentHeadParent = getDocumentHeadParent(cursor.parent, tag);
182
184
  const parentEl = cursor.parent;
183
185
  const parentTag = parentEl.nodeType === 1 ? parentEl.tagName.toLowerCase() : "";
184
- const isHeadish = parentTag === "head" || parentTag === "html";
186
+ const isHeadish = parentTag === "head" || parentTag === "html" || !!documentHeadParent;
185
187
  let candidate;
186
- if (isHeadish) {
188
+ if (documentHeadParent) {
189
+ candidate = new HydrationCursor(documentHeadParent).takeMatchingHeadElement(
190
+ tag,
191
+ fiber.pendingProps ?? {}
192
+ );
193
+ } else if (isHeadish) {
187
194
  candidate = cursor.takeMatchingHeadElement(tag, fiber.pendingProps ?? {});
188
195
  } else {
189
196
  candidate = cursor.takeHostNode();
@@ -208,6 +215,10 @@ function adoptHostDom(fiber, parent) {
208
215
  hydrationCursors.set(fiber, new HydrationCursor(candidate));
209
216
  return true;
210
217
  }
218
+ function getDocumentHeadParent(parent, tag) {
219
+ if (parent.nodeType !== 9 || !DOCUMENT_HEAD_TAGS.has(tag)) return null;
220
+ return parent.head;
221
+ }
211
222
  function adoptTextDom(fiber, parent, text) {
212
223
  const cursor = hydrationCursors.get(findHostParent(parent));
213
224
  if (!cursor) return false;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/dom/features/hydration/full.ts"],
4
- "sourcesContent": ["import { FiberTag, type Fiber, type FiberRoot } from '../../../core'\nimport { setProp } from '../../dom'\nimport { findRoot } from '../../reconcile'\n\n// Re-export from event-replay so all hydration concerns live behind one\n// feature boundary \u2014 the plugin's stub swap strips drainReplayQueue too.\nexport { drainReplayQueue } from '../../event-replay'\n\nconst GUARD_WINDOW_MS = 3000\n\n/**\n * Preserve the user's scroll position across hydration. If the user scrolled\n * between SSR paint and hydrate (common in dev where JS takes seconds to\n * load), libraries that wire scroll-restoration into a `useLayoutEffect`\n * near the root (e.g. TanStack Router) will run during our synchronous\n * hydrate and call `window.scrollTo(savedFromLastVisit)` \u2014 overwriting the\n * user's fresh scroll. We install a short-lived wrapper around scrollTo that\n * suppresses programmatic calls when a user-initiated scroll happened\n * recently. Only runs in the hydration feature \u2014 the stub skips it.\n */\nexport function installHydrationScrollGuard(): void {\n if (typeof window === 'undefined') return\n const w = window as any\n if (w.__tdomScrollGuardInstalled) return\n w.__tdomScrollGuardInstalled = true\n const guardStartedAt = performance.now()\n let lastUserScrollAt = 0\n let programmatic = 0\n w.__tdomScrollLog = []\n window.addEventListener(\n 'scroll',\n () => {\n if (programmatic === 0) {\n lastUserScrollAt = performance.now()\n w.__tdomScrollLog.push({ t: Math.round(lastUserScrollAt), ev: 'user-scroll', y: window.scrollY })\n }\n },\n { capture: true, passive: true },\n )\n const origScrollTo = window.scrollTo.bind(window)\n window.scrollTo = function (this: any, ...args: any[]) {\n const now = performance.now()\n const inGuardWindow = now - guardStartedAt < GUARD_WINDOW_MS\n const userScrolledRecently = lastUserScrollAt > 0 && now - lastUserScrollAt < 1500\n if (inGuardWindow && userScrolledRecently) {\n w.__tdomScrollLog.push({\n t: Math.round(now),\n ev: 'suppressed',\n args: JSON.stringify(args).slice(0, 80),\n tSinceHydrate: Math.round(now - guardStartedAt),\n tSinceUserScroll: Math.round(now - lastUserScrollAt),\n })\n return\n }\n w.__tdomScrollLog.push({\n t: Math.round(now),\n ev: 'allowed',\n args: JSON.stringify(args).slice(0, 80),\n tSinceHydrate: Math.round(now - guardStartedAt),\n inGuard: inGuardWindow,\n userScrolled: userScrolledRecently,\n })\n programmatic++\n try {\n return (origScrollTo as any).apply(this, args)\n } finally {\n queueMicrotask(() => {\n programmatic = Math.max(0, programmatic - 1)\n })\n }\n }\n}\n\n/**\n * Hydration cursor: walks existing DOM children in document order so we can\n * adopt them during fiber tree construction. One cursor per host parent.\n *\n * `endBefore` scopes the cursor to a subrange \u2014 used by rehydrateBoundary()\n * so we only adopt DOM up to the closing `/$` marker for that boundary.\n */\nexport class HydrationCursor {\n next: ChildNode | null\n parent: Node\n endBefore: ChildNode | null\n constructor(parent: Node, start: ChildNode | null = null, endBefore: ChildNode | null = null) {\n this.parent = parent\n this.next = start ?? parent.firstChild\n this.endBefore = endBefore\n }\n takeHostNode(): ChildNode | null {\n while (this.next && this.next !== this.endBefore) {\n const n = this.next\n // Skip anything that isn't an element (1) or text (3):\n // comments (8), doctype (10), processing instructions (7), cdata (4).\n if (n.nodeType !== 1 && n.nodeType !== 3) {\n this.next = n.nextSibling\n continue\n }\n this.next = n.nextSibling\n return n\n }\n return null\n }\n /**\n * Position-insensitive lookup for head/html adoption. Scans forward past\n * non-matching nodes without removing them, matching by tag AND the key\n * attributes that identify head elements uniquely (rel/href for links,\n * name/property for meta, src for script). Non-matching nodes stay in\n * place so the SSR'd stylesheet/script order is preserved.\n */\n takeMatchingHeadElement(tag: string, props: Record<string, any>): ChildNode | null {\n const target = tag.toLowerCase()\n const keyAttrs = HEAD_KEY_ATTRS[target] ?? []\n let scan = this.parent.firstChild\n while (scan) {\n if (\n scan.nodeType === 1 &&\n (scan as Element).tagName.toLowerCase() === target &&\n headAttrsMatch(scan as Element, props, keyAttrs)\n ) {\n CLAIMED.add(scan)\n return scan\n }\n scan = scan.nextSibling\n }\n return null\n }\n remaining(): ChildNode[] {\n const out: ChildNode[] = []\n let n = this.next\n while (n && n !== this.endBefore) {\n out.push(n)\n n = n.nextSibling\n }\n return out\n }\n}\n\nconst hydrationCursors = new WeakMap<Fiber, HydrationCursor>()\n\n// Head elements that we match against server DOM by attribute signature.\nconst HEAD_KEY_ATTRS: Record<string, ReadonlyArray<string>> = {\n link: ['rel', 'href', 'sizes', 'type'],\n meta: ['name', 'property', 'charset', 'http-equiv'],\n script: ['src', 'type'],\n style: [],\n title: [],\n}\n\n// DOM elements already claimed by some fiber during this hydration pass.\nconst CLAIMED = new WeakSet<Node>()\n\nfunction headAttrsMatch(\n el: Element,\n props: Record<string, any>,\n keys: ReadonlyArray<string>,\n): boolean {\n if (CLAIMED.has(el)) return false\n if (keys.length === 0) return true\n for (const k of keys) {\n const propVal = props[k] ?? (k === 'http-equiv' ? props.httpEquiv : undefined)\n const elVal = el.getAttribute(k)\n // If neither defines it, skip this key; if one defines it, they must match.\n if (propVal == null && elVal == null) continue\n if (propVal == null || elVal == null) continue // tolerate missing on either side\n if (String(propVal) !== elVal) return false\n }\n // At least one matching signal must be present.\n return keys.some((k) => props[k] != null || el.hasAttribute(k))\n}\n\nexport function beginHydration(root: FiberRoot): void {\n root.hydrating = true\n hydrationCursors.set(root.current, new HydrationCursor(root.container))\n}\n\nexport function endHydration(root: FiberRoot): void {\n root.hydrating = false\n hydrationCursors.delete(root.current)\n}\n\n/**\n * Inspect the current cursor position for a streaming-suspense boundary\n * marker emitted by the server. Returns info + advances the cursor past the\n * marker pair (start comment + fallback/real content + end comment).\n */\nexport interface BoundaryInfo {\n kind: 'pending' | 'resolved'\n id: number\n startMark: Comment\n endMark: Comment\n}\n\nexport function tryConsumeBoundary(parent: Fiber): BoundaryInfo | null {\n const cursor = hydrationCursors.get(findHostParent(parent))\n if (!cursor) return null\n const peek = cursor.next\n if (!peek || peek.nodeType !== 8) return null\n const data = (peek as Comment).data\n const m = /^(\\$\\??)(\\d+)$/.exec(data)\n if (!m) return null\n const kind = m[1] === '$?' ? 'pending' : 'resolved'\n const id = Number(m[2])\n const startMark = peek as Comment\n // Advance past the start comment\n cursor.next = startMark.nextSibling\n // Locate end comment: closest <!--/$-->\n let endMark: Comment | null = null\n let scan = startMark.nextSibling\n while (scan) {\n if (scan.nodeType === 8 && (scan as Comment).data === '/$') {\n endMark = scan as Comment\n break\n }\n scan = scan.nextSibling\n }\n if (!endMark) return null\n return { kind, id, startMark, endMark }\n}\n\nexport function advanceCursorPast(parent: Fiber, node: Node): void {\n const cursor = hydrationCursors.get(findHostParent(parent))\n if (!cursor) return\n cursor.next = node.nextSibling\n}\n\nexport function getHydrationCursor(hostFiber: Fiber): HydrationCursor | undefined {\n return hydrationCursors.get(hostFiber)\n}\n\nexport function setHydrationCursor(hostFiber: Fiber, cursor: HydrationCursor): void {\n hydrationCursors.set(hostFiber, cursor)\n}\n\nexport function clearHydrationCursor(hostFiber: Fiber): void {\n hydrationCursors.delete(hostFiber)\n}\n\n/**\n * Try to adopt a DOM node for this host fiber. Returns true if adopted.\n * Attaches existing attrs/children via separate hydrate pass.\n */\nexport function adoptHostDom(fiber: Fiber, parent: Fiber): boolean {\n const hostParent = findHostParent(parent)\n const cursor = hydrationCursors.get(hostParent)\n if (!cursor) return false\n\n const tag = (fiber.type as string).toLowerCase()\n const parentEl = cursor.parent as Element\n const parentTag =\n parentEl.nodeType === 1 ? (parentEl as Element).tagName.toLowerCase() : ''\n const isHeadish = parentTag === 'head' || parentTag === 'html'\n\n let candidate: ChildNode | null\n if (isHeadish) {\n // Head/html children are position-insensitive \u2014 server may emit them in\n // a different order than the React tree (React 19 head hoisting, etc.).\n // Scan forward without removing non-matching nodes; match on attribute\n // signature so we don't adopt the wrong <link> and clobber its props.\n candidate = cursor.takeMatchingHeadElement(tag, fiber.pendingProps ?? {})\n } else {\n candidate = cursor.takeHostNode()\n }\n\n if (!candidate) {\n // Client expected a host here but the cursor is exhausted \u2014 server gave\n // fewer children than the client tree. Report the structural gap (React\n // fires `onRecoverableError` for this exact case) and let the reconciler\n // mount a fresh DOM for this fiber below.\n // Exception: <head> children are position-insensitive; a missing match\n // there means \"server didn't hoist this one yet\", which we silently mount.\n if (!isHeadish) onMismatch(fiber, null)\n return false\n }\n\n if (candidate.nodeType !== 1 || (candidate as Element).tagName.toLowerCase() !== tag) {\n // mismatch \u2014 log and re-render fresh from this point\n onMismatch(fiber, candidate)\n return false\n }\n fiber.dom = candidate\n // Apply props (attach events, sync IDL props). Don't re-set existing attrs.\n const props = fiber.pendingProps ?? {}\n const isSvg =\n tag === 'svg' ||\n ((candidate as Element).namespaceURI === 'http://www.w3.org/2000/svg' &&\n tag !== 'foreignobject')\n for (const k in props) {\n if (k === 'children') continue\n if (k[0] === 'o' && k[1] === 'n' && typeof props[k] === 'function') {\n setProp(candidate as Element, k, props[k], undefined, isSvg)\n }\n // Non-event props: trust the server HTML, skip\n }\n // Set up child cursor for this host's children\n hydrationCursors.set(fiber, new HydrationCursor(candidate))\n return true\n}\n\nexport function adoptTextDom(fiber: Fiber, parent: Fiber, text: string): boolean {\n const cursor = hydrationCursors.get(findHostParent(parent))\n if (!cursor) return false\n const candidate = cursor.takeHostNode()\n if (!candidate) return false\n if (candidate.nodeType === 3) {\n if ((candidate as Text).data !== text) {\n ;(candidate as Text).data = text\n }\n fiber.dom = candidate\n return true\n }\n onMismatch(fiber, candidate)\n return false\n}\n\nexport function findHostParent(fiber: Fiber): Fiber {\n let f: Fiber | null = fiber\n while (f) {\n // A fiber explicitly holding a cursor acts as a boundary for hydration\n // (e.g. Suspense with a scoped cursor during fallback/boundary hydration).\n if (hydrationCursors.has(f)) return f\n if (f.tag === FiberTag.Host || f.tag === FiberTag.Root || f.tag === FiberTag.Portal) {\n return f\n }\n f = f.parent\n }\n throw new Error('No host parent found')\n}\n\nfunction onMismatch(fiber: Fiber, actualNode: ChildNode | null): void {\n // For v1: log and exit hydration for this subtree. The normal reconciler\n // will create a fresh DOM node below.\n const root = findRoot(fiber)\n if (root?.onRecoverableError) {\n root.onRecoverableError(\n new Error(\n `Hydration mismatch: expected <${(fiber.type as string) ?? 'text'}> but found ${\n actualNode ? (actualNode.nodeType === 1 ? (actualNode as Element).tagName : 'text') : 'nothing'\n }.`,\n ),\n )\n }\n // Remove stale DOM if still there\n if (actualNode && actualNode.parentNode) actualNode.parentNode.removeChild(actualNode)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { FiberTag, type Fiber, type FiberRoot } from '../../../core'\nimport { setProp } from '../../dom'\nimport { findRoot } from '../../reconcile'\n\n// Re-export from event-replay so all hydration concerns live behind one\n// feature boundary \u2014 the plugin's stub swap strips drainReplayQueue too.\nexport { drainReplayQueue } from '../../event-replay'\n\nconst GUARD_WINDOW_MS = 3000\n\n/**\n * Preserve the user's scroll position across hydration. If the user scrolled\n * between SSR paint and hydrate (common in dev where JS takes seconds to\n * load), libraries that wire scroll-restoration into a `useLayoutEffect`\n * near the root (e.g. TanStack Router) will run during our synchronous\n * hydrate and call `window.scrollTo(savedFromLastVisit)` \u2014 overwriting the\n * user's fresh scroll. We install a short-lived wrapper around scrollTo that\n * suppresses programmatic calls when a user-initiated scroll happened\n * recently. Only runs in the hydration feature \u2014 the stub skips it.\n */\nexport function installHydrationScrollGuard(): void {\n if (typeof window === 'undefined') return\n const w = window as any\n if (w.__redactScrollGuardInstalled) return\n w.__redactScrollGuardInstalled = true\n const guardStartedAt = performance.now()\n let lastUserScrollAt = 0\n let programmatic = 0\n w.__redactScrollLog = []\n window.addEventListener(\n 'scroll',\n () => {\n if (programmatic === 0) {\n lastUserScrollAt = performance.now()\n w.__redactScrollLog.push({ t: Math.round(lastUserScrollAt), ev: 'user-scroll', y: window.scrollY })\n }\n },\n { capture: true, passive: true },\n )\n const origScrollTo = window.scrollTo.bind(window)\n window.scrollTo = function (this: any, ...args: any[]) {\n const now = performance.now()\n const inGuardWindow = now - guardStartedAt < GUARD_WINDOW_MS\n const userScrolledRecently = lastUserScrollAt > 0 && now - lastUserScrollAt < 1500\n if (inGuardWindow && userScrolledRecently) {\n w.__redactScrollLog.push({\n t: Math.round(now),\n ev: 'suppressed',\n args: JSON.stringify(args).slice(0, 80),\n tSinceHydrate: Math.round(now - guardStartedAt),\n tSinceUserScroll: Math.round(now - lastUserScrollAt),\n })\n return\n }\n w.__redactScrollLog.push({\n t: Math.round(now),\n ev: 'allowed',\n args: JSON.stringify(args).slice(0, 80),\n tSinceHydrate: Math.round(now - guardStartedAt),\n inGuard: inGuardWindow,\n userScrolled: userScrolledRecently,\n })\n programmatic++\n try {\n return (origScrollTo as any).apply(this, args)\n } finally {\n queueMicrotask(() => {\n programmatic = Math.max(0, programmatic - 1)\n })\n }\n }\n}\n\n/**\n * Hydration cursor: walks existing DOM children in document order so we can\n * adopt them during fiber tree construction. One cursor per host parent.\n *\n * `endBefore` scopes the cursor to a subrange \u2014 used by rehydrateBoundary()\n * so we only adopt DOM up to the closing `/$` marker for that boundary.\n */\nexport class HydrationCursor {\n next: ChildNode | null\n parent: Node\n endBefore: ChildNode | null\n constructor(parent: Node, start: ChildNode | null = null, endBefore: ChildNode | null = null) {\n this.parent = parent\n this.next = start ?? parent.firstChild\n this.endBefore = endBefore\n }\n takeHostNode(): ChildNode | null {\n while (this.next && this.next !== this.endBefore) {\n const n = this.next\n // Skip anything that isn't an element (1) or text (3):\n // comments (8), doctype (10), processing instructions (7), cdata (4).\n if (n.nodeType !== 1 && n.nodeType !== 3) {\n this.next = n.nextSibling\n continue\n }\n this.next = n.nextSibling\n return n\n }\n return null\n }\n /**\n * Position-insensitive lookup for head/html adoption. Scans forward past\n * non-matching nodes without removing them, matching by tag AND the key\n * attributes that identify head elements uniquely (rel/href for links,\n * name/property for meta, src for script). Non-matching nodes stay in\n * place so the SSR'd stylesheet/script order is preserved.\n */\n takeMatchingHeadElement(tag: string, props: Record<string, any>): ChildNode | null {\n const target = tag.toLowerCase()\n const keyAttrs = HEAD_KEY_ATTRS[target] ?? []\n let scan = this.parent.firstChild\n while (scan) {\n if (\n scan.nodeType === 1 &&\n (scan as Element).tagName.toLowerCase() === target &&\n headAttrsMatch(scan as Element, props, keyAttrs)\n ) {\n CLAIMED.add(scan)\n return scan\n }\n scan = scan.nextSibling\n }\n return null\n }\n remaining(): ChildNode[] {\n const out: ChildNode[] = []\n let n = this.next\n while (n && n !== this.endBefore) {\n out.push(n)\n n = n.nextSibling\n }\n return out\n }\n}\n\nconst hydrationCursors = new WeakMap<Fiber, HydrationCursor>()\n\n// Head elements that we match against server DOM by attribute signature.\nconst HEAD_KEY_ATTRS: Record<string, ReadonlyArray<string>> = {\n link: ['rel', 'href', 'sizes', 'type'],\n meta: ['name', 'property', 'charset', 'http-equiv'],\n script: ['src', 'type'],\n style: [],\n title: [],\n}\n\nconst DOCUMENT_HEAD_TAGS = new Set(['base', 'link', 'meta', 'script', 'style', 'title'])\n\n// DOM elements already claimed by some fiber during this hydration pass.\nconst CLAIMED = new WeakSet<Node>()\n\nfunction headAttrsMatch(\n el: Element,\n props: Record<string, any>,\n keys: ReadonlyArray<string>,\n): boolean {\n if (CLAIMED.has(el)) return false\n if (keys.length === 0) return true\n for (const k of keys) {\n const propVal = props[k] ?? (k === 'http-equiv' ? props.httpEquiv : undefined)\n const elVal = el.getAttribute(k)\n // If neither defines it, skip this key; if one defines it, they must match.\n if (propVal == null && elVal == null) continue\n if (propVal == null || elVal == null) continue // tolerate missing on either side\n if (String(propVal) !== elVal) return false\n }\n // At least one matching signal must be present.\n return keys.some((k) => props[k] != null || el.hasAttribute(k))\n}\n\nexport function beginHydration(root: FiberRoot): void {\n root.hydrating = true\n hydrationCursors.set(root.current, new HydrationCursor(root.container))\n}\n\nexport function endHydration(root: FiberRoot): void {\n root.hydrating = false\n hydrationCursors.delete(root.current)\n}\n\n/**\n * Inspect the current cursor position for a streaming-suspense boundary\n * marker emitted by the server. Returns info + advances the cursor past the\n * marker pair (start comment + fallback/real content + end comment).\n */\nexport interface BoundaryInfo {\n kind: 'pending' | 'resolved'\n id: number\n startMark: Comment\n endMark: Comment\n}\n\nexport function tryConsumeBoundary(parent: Fiber): BoundaryInfo | null {\n const cursor = hydrationCursors.get(findHostParent(parent))\n if (!cursor) return null\n const peek = cursor.next\n if (!peek || peek.nodeType !== 8) return null\n const data = (peek as Comment).data\n const m = /^(\\$\\??)(\\d+)$/.exec(data)\n if (!m) return null\n const kind = m[1] === '$?' ? 'pending' : 'resolved'\n const id = Number(m[2])\n const startMark = peek as Comment\n // Advance past the start comment\n cursor.next = startMark.nextSibling\n // Locate end comment: closest <!--/$-->\n let endMark: Comment | null = null\n let scan = startMark.nextSibling\n while (scan) {\n if (scan.nodeType === 8 && (scan as Comment).data === '/$') {\n endMark = scan as Comment\n break\n }\n scan = scan.nextSibling\n }\n if (!endMark) return null\n return { kind, id, startMark, endMark }\n}\n\nexport function advanceCursorPast(parent: Fiber, node: Node): void {\n const cursor = hydrationCursors.get(findHostParent(parent))\n if (!cursor) return\n cursor.next = node.nextSibling\n}\n\nexport function getHydrationCursor(hostFiber: Fiber): HydrationCursor | undefined {\n return hydrationCursors.get(hostFiber)\n}\n\nexport function setHydrationCursor(hostFiber: Fiber, cursor: HydrationCursor): void {\n hydrationCursors.set(hostFiber, cursor)\n}\n\nexport function clearHydrationCursor(hostFiber: Fiber): void {\n hydrationCursors.delete(hostFiber)\n}\n\n/**\n * Try to adopt a DOM node for this host fiber. Returns true if adopted.\n * Attaches existing attrs/children via separate hydrate pass.\n */\nexport function adoptHostDom(fiber: Fiber, parent: Fiber): boolean {\n const hostParent = findHostParent(parent)\n const cursor = hydrationCursors.get(hostParent)\n if (!cursor) return false\n\n const tag = (fiber.type as string).toLowerCase()\n const documentHeadParent = getDocumentHeadParent(cursor.parent, tag)\n const parentEl = cursor.parent as Element\n const parentTag =\n parentEl.nodeType === 1 ? (parentEl as Element).tagName.toLowerCase() : ''\n const isHeadish = parentTag === 'head' || parentTag === 'html' || !!documentHeadParent\n\n let candidate: ChildNode | null\n if (documentHeadParent) {\n // React 19 can project <meta>/<title>/<link> from anywhere in the tree into\n // document.head. Redact does not have that projection yet, so when a\n // document-root hydration pass sees a top-level head element, adopt it\n // from <head> rather than trying to append it beside <html>.\n candidate = new HydrationCursor(documentHeadParent).takeMatchingHeadElement(\n tag,\n fiber.pendingProps ?? {},\n )\n } else if (isHeadish) {\n // Head/html children are position-insensitive \u2014 server may emit them in\n // a different order than the React tree (React 19 head hoisting, etc.).\n // Scan forward without removing non-matching nodes; match on attribute\n // signature so we don't adopt the wrong <link> and clobber its props.\n candidate = cursor.takeMatchingHeadElement(tag, fiber.pendingProps ?? {})\n } else {\n candidate = cursor.takeHostNode()\n }\n\n if (!candidate) {\n // Client expected a host here but the cursor is exhausted \u2014 server gave\n // fewer children than the client tree. Report the structural gap (React\n // fires `onRecoverableError` for this exact case) and let the reconciler\n // mount a fresh DOM for this fiber below.\n // Exception: <head> children are position-insensitive; a missing match\n // there means \"server didn't hoist this one yet\", which we silently mount.\n if (!isHeadish) onMismatch(fiber, null)\n return false\n }\n\n if (candidate.nodeType !== 1 || (candidate as Element).tagName.toLowerCase() !== tag) {\n // mismatch \u2014 log and re-render fresh from this point\n onMismatch(fiber, candidate)\n return false\n }\n fiber.dom = candidate\n // Apply props (attach events, sync IDL props). Don't re-set existing attrs.\n const props = fiber.pendingProps ?? {}\n const isSvg =\n tag === 'svg' ||\n ((candidate as Element).namespaceURI === 'http://www.w3.org/2000/svg' &&\n tag !== 'foreignobject')\n for (const k in props) {\n if (k === 'children') continue\n if (k[0] === 'o' && k[1] === 'n' && typeof props[k] === 'function') {\n setProp(candidate as Element, k, props[k], undefined, isSvg)\n }\n // Non-event props: trust the server HTML, skip\n }\n // Set up child cursor for this host's children\n hydrationCursors.set(fiber, new HydrationCursor(candidate))\n return true\n}\n\nfunction getDocumentHeadParent(parent: Node, tag: string): HTMLHeadElement | null {\n if (parent.nodeType !== 9 || !DOCUMENT_HEAD_TAGS.has(tag)) return null\n return (parent as Document).head\n}\n\nexport function adoptTextDom(fiber: Fiber, parent: Fiber, text: string): boolean {\n const cursor = hydrationCursors.get(findHostParent(parent))\n if (!cursor) return false\n const candidate = cursor.takeHostNode()\n if (!candidate) return false\n if (candidate.nodeType === 3) {\n if ((candidate as Text).data !== text) {\n ;(candidate as Text).data = text\n }\n fiber.dom = candidate\n return true\n }\n onMismatch(fiber, candidate)\n return false\n}\n\nexport function findHostParent(fiber: Fiber): Fiber {\n let f: Fiber | null = fiber\n while (f) {\n // A fiber explicitly holding a cursor acts as a boundary for hydration\n // (e.g. Suspense with a scoped cursor during fallback/boundary hydration).\n if (hydrationCursors.has(f)) return f\n if (f.tag === FiberTag.Host || f.tag === FiberTag.Root || f.tag === FiberTag.Portal) {\n return f\n }\n f = f.parent\n }\n throw new Error('No host parent found')\n}\n\nfunction onMismatch(fiber: Fiber, actualNode: ChildNode | null): void {\n // For v1: log and exit hydration for this subtree. The normal reconciler\n // will create a fresh DOM node below.\n const root = findRoot(fiber)\n if (root?.onRecoverableError) {\n root.onRecoverableError(\n new Error(\n `Hydration mismatch: expected <${(fiber.type as string) ?? 'text'}> but found ${\n actualNode ? (actualNode.nodeType === 1 ? (actualNode as Element).tagName : 'text') : 'nothing'\n }.`,\n ),\n )\n }\n // Remove stale DOM if still there\n if (actualNode && actualNode.parentNode) actualNode.parentNode.removeChild(actualNode)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,5 @@
1
1
  // packages/redact/src/dom/features/suspense/full.ts
2
- import { FiberTag } from "../../../core";
2
+ import { FiberTag, createFiber } from "../../../core";
3
3
  import { REACT_SUSPENSE_TYPE } from "../../../react";
4
4
  import {
5
5
  registerRenderer,
@@ -7,8 +7,10 @@ import {
7
7
  installCapability,
8
8
  reconcileChildren,
9
9
  childrenToArray,
10
+ renderFiber,
10
11
  scheduleUpdate,
11
12
  unmountAllChildren,
13
+ unmountFiber,
12
14
  findRoot,
13
15
  runEffects,
14
16
  getCurrentRoot,
@@ -35,7 +37,12 @@ function realHandleSuspended(fiber, thenable) {
35
37
  }
36
38
  function renderSuspense(fiber, domParent, anchor) {
37
39
  const props = fiber.pendingProps ?? {};
38
- const state = fiber.memoizedState ??= { suspended: false, pending: null };
40
+ const state = fiber.memoizedState ??= {
41
+ suspended: false,
42
+ pending: null,
43
+ hiddenDoms: null,
44
+ fallbackFiber: null
45
+ };
39
46
  const root = getCurrentRoot();
40
47
  if (root?.hydrating && fiber.parent && !state.hydrated) {
41
48
  const boundary = tryConsumeBoundary(fiber.parent);
@@ -49,43 +56,92 @@ function renderSuspense(fiber, domParent, anchor) {
49
56
  fiber.memoizedProps = props;
50
57
  return;
51
58
  }
52
- const tryChildren = () => {
53
- reconcileChildren(fiber, childrenToArray(props.children), domParent, anchor);
54
- };
55
- if (state.suspended && state.pending) {
59
+ if (state.suspended && state.pending && state.fallbackFiber) {
60
+ state.fallbackFiber.pendingProps = { children: props.fallback };
61
+ renderFiber(state.fallbackFiber, domParent, anchor);
62
+ fiber.memoizedProps = props;
63
+ return;
64
+ }
65
+ if (state.suspended && state.pending && !state.fallbackFiber) {
56
66
  reconcileChildren(fiber, childrenToArray(props.fallback), domParent, anchor);
57
67
  fiber.memoizedProps = props;
58
68
  return;
59
69
  }
70
+ const hadCommittedPrimary = fiber.memoizedProps !== void 0 && fiber.child !== null;
60
71
  const savedHandler = suspendHandlerStack[suspendHandlerStack.length - 1];
72
+ let suspendedThisRender = false;
73
+ let suspendedThenable = null;
61
74
  suspendHandlerStack.push((thenable) => {
62
- state.suspended = true;
63
- state.pending = thenable;
64
- thenable.then(
65
- () => {
66
- state.suspended = false;
67
- state.pending = null;
68
- scheduleUpdate(fiber);
69
- },
70
- () => {
71
- state.suspended = false;
72
- state.pending = null;
73
- scheduleUpdate(fiber);
74
- }
75
- );
75
+ suspendedThisRender = true;
76
+ suspendedThenable = thenable;
76
77
  });
77
78
  try {
78
- tryChildren();
79
+ reconcileChildren(fiber, childrenToArray(props.children), domParent, anchor);
79
80
  } finally {
80
81
  suspendHandlerStack.pop();
81
82
  void savedHandler;
82
83
  }
83
- if (state.suspended) {
84
- unmountAllChildren(fiber, domParent);
85
- reconcileChildren(fiber, childrenToArray(props.fallback), domParent, anchor);
84
+ if (suspendedThisRender && suspendedThenable) {
85
+ state.suspended = true;
86
+ state.pending = suspendedThenable;
87
+ const onSettle = () => {
88
+ state.suspended = false;
89
+ state.pending = null;
90
+ scheduleUpdate(fiber);
91
+ };
92
+ suspendedThenable.then(onSettle, onSettle);
93
+ if (hadCommittedPrimary && fiber.child) {
94
+ const hostDoms = [];
95
+ let c = fiber.child;
96
+ while (c) {
97
+ collectRootHostDoms(c, hostDoms);
98
+ c = c.sibling;
99
+ }
100
+ const hidden = [];
101
+ for (const d of hostDoms) {
102
+ if (d.nodeType === 1) {
103
+ const el = d;
104
+ hidden.push([el, el.style.display]);
105
+ el.style.display = "none";
106
+ }
107
+ }
108
+ state.hiddenDoms = hidden;
109
+ if (!state.fallbackFiber) {
110
+ state.fallbackFiber = createFiber(FiberTag.Fragment, null, null);
111
+ state.fallbackFiber.parent = fiber;
112
+ }
113
+ state.fallbackFiber.pendingProps = { children: props.fallback };
114
+ renderFiber(state.fallbackFiber, domParent, anchor);
115
+ } else {
116
+ unmountAllChildren(fiber, domParent);
117
+ reconcileChildren(fiber, childrenToArray(props.fallback), domParent, anchor);
118
+ }
119
+ } else {
120
+ if (state.hiddenDoms) {
121
+ for (const [el, origDisplay] of state.hiddenDoms) {
122
+ el.style.display = origDisplay;
123
+ }
124
+ state.hiddenDoms = null;
125
+ }
126
+ if (state.fallbackFiber) {
127
+ unmountFiber(state.fallbackFiber, domParent);
128
+ state.fallbackFiber = null;
129
+ }
86
130
  }
87
131
  fiber.memoizedProps = props;
88
132
  }
133
+ function collectRootHostDoms(fiber, out) {
134
+ if (fiber.tag === FiberTag.Host || fiber.tag === FiberTag.Text) {
135
+ if (fiber.dom) out.push(fiber.dom);
136
+ return;
137
+ }
138
+ if (fiber.tag === FiberTag.Portal) return;
139
+ let c = fiber.child;
140
+ while (c) {
141
+ collectRootHostDoms(c, out);
142
+ c = c.sibling;
143
+ }
144
+ }
89
145
  function hydrateSuspenseBoundary(fiber, props, boundary, domParent, anchor) {
90
146
  const { kind, id, startMark, endMark } = boundary;
91
147
  fiber.memoizedState = {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/dom/features/suspense/full.ts"],
4
- "sourcesContent": ["import { FiberTag, type Fiber } from '../../../core'\nimport { REACT_SUSPENSE_TYPE } from '../../../react'\nimport {\n registerRenderer,\n registerTypeMatcher,\n installCapability,\n reconcileChildren,\n childrenToArray,\n scheduleUpdate,\n unmountAllChildren,\n findRoot,\n runEffects,\n getCurrentRoot,\n withCurrentRoot,\n} from '../../reconcile'\nimport {\n HydrationCursor,\n setHydrationCursor,\n clearHydrationCursor,\n advanceCursorPast,\n tryConsumeBoundary,\n} from '../hydration'\n\nconst suspendHandlerStack: Array<(t: Promise<any>) => void> = []\n\nfunction realHandleSuspended(fiber: Fiber, thenable: Promise<any>): void {\n const handler = suspendHandlerStack[suspendHandlerStack.length - 1]\n if (handler) {\n handler(thenable)\n return\n }\n // Fallback: schedule re-render when promise settles\n thenable.then(\n () => scheduleUpdate(fiber),\n () => scheduleUpdate(fiber),\n )\n}\n\nfunction renderSuspense(fiber: Fiber, domParent: Node, anchor: Node | null): void {\n const props = fiber.pendingProps ?? {}\n const state = (fiber.memoizedState ??= { suspended: false, pending: null as Promise<any> | null })\n\n // Streaming hydration: if the next DOM node is a server-emitted boundary\n // marker, route through the boundary-aware hydration path.\n const root = getCurrentRoot()\n if (root?.hydrating && fiber.parent && !state.hydrated) {\n const boundary = tryConsumeBoundary(fiber.parent)\n if (boundary) {\n hydrateSuspenseBoundary(fiber, props, boundary, domParent, anchor)\n state.hydrated = true\n return\n }\n }\n\n // A descendant Lazy deferred its hydration (see renderLazy's hydrating\n // branch). Its SSR-rendered content is still in the DOM and cursor-bound\n // via the Lazy fiber \u2014 we just haven't swapped it into a fiber subtree\n // yet. Until the Lazy's resume fires, skip our own tryChildren pass so\n // an unrelated re-render can't accidentally flip us into the suspended\n // path and mount a duplicate fallback on top of the SSR content.\n if ((state as any)._awaitingLazyHydration) {\n fiber.memoizedProps = props\n return\n }\n\n const tryChildren = () => {\n reconcileChildren(fiber, childrenToArray(props.children), domParent, anchor)\n }\n\n if (state.suspended && state.pending) {\n // Render fallback while waiting; pending promise will reschedule\n reconcileChildren(fiber, childrenToArray(props.fallback), domParent, anchor)\n fiber.memoizedProps = props\n return\n }\n\n // Attempt children \u2014 suspension is handled by the pushed handler below\n const savedHandler = suspendHandlerStack[suspendHandlerStack.length - 1]\n suspendHandlerStack.push((thenable) => {\n state.suspended = true\n state.pending = thenable\n thenable.then(\n () => {\n state.suspended = false\n state.pending = null\n scheduleUpdate(fiber)\n },\n () => {\n state.suspended = false\n state.pending = null\n scheduleUpdate(fiber)\n },\n )\n })\n try {\n tryChildren()\n } finally {\n suspendHandlerStack.pop()\n void savedHandler\n }\n\n if (state.suspended) {\n // Replace children with fallback\n unmountAllChildren(fiber, domParent)\n reconcileChildren(fiber, childrenToArray(props.fallback), domParent, anchor)\n }\n fiber.memoizedProps = props\n}\n\nfunction hydrateSuspenseBoundary(\n fiber: Fiber,\n props: any,\n boundary: { kind: 'pending' | 'resolved'; id: number; startMark: Comment; endMark: Comment },\n domParent: Node,\n anchor: Node | null,\n): void {\n const { kind, id, startMark, endMark } = boundary\n // Record the boundary shape so we can re-hydrate on reveal.\n fiber.memoizedState = {\n suspended: false,\n pending: null,\n hydrated: true,\n boundaryId: id,\n startMark,\n endMark,\n realChildren: props.children,\n }\n\n if (kind === 'resolved') {\n // Real DOM is inline between startMark and endMark. Hydrate into it.\n const cursor = new HydrationCursor(startMark.parentNode!, startMark.nextSibling, endMark)\n setHydrationCursor(fiber, cursor)\n reconcileChildren(fiber, childrenToArray(props.children), domParent, anchor)\n clearHydrationCursor(fiber)\n advanceCursorPast(fiber.parent!, endMark)\n fiber.memoizedProps = props\n return\n }\n\n // Pending: fallback DOM lives inside <div id=\"B:ID\">. Hydrate the fallback\n // React subtree against that div's children.\n const bDiv = (document as Document).getElementById(`B:${id}`)\n if (bDiv) {\n const cursor = new HydrationCursor(bDiv)\n setHydrationCursor(fiber, cursor)\n reconcileChildren(fiber, childrenToArray(props.fallback), domParent, anchor)\n clearHydrationCursor(fiber)\n } else {\n // Couldn't find fallback container \u2014 render fresh (non-adopting)\n reconcileChildren(fiber, childrenToArray(props.fallback), domParent, anchor)\n }\n advanceCursorPast(fiber.parent!, endMark)\n\n // Register for server-streamed reveal (HTML chunks + $RC calls).\n const win = globalThis as any\n if (typeof win.$RH === 'function') {\n win.$RH(id, () => rehydrateBoundary(fiber))\n }\n // If the inline runtime isn't present, nothing external will mark us dirty.\n\n fiber.memoizedProps = props\n}\n\nfunction rehydrateBoundary(fiber: Fiber): void {\n const state = fiber.memoizedState\n if (!state || !state.startMark || !state.endMark) return\n\n const root = findRoot(fiber)\n if (!root) return\n const parent = state.startMark.parentNode as Node\n if (!parent) return\n\n // Unmount existing fallback subtree. Its DOM has already been removed by $RC\n // (or at least its container); unmounting here cleans up fibers + effects.\n withCurrentRoot(root, () => {\n unmountAllChildren(fiber, parent)\n\n // Re-hydrate with real children against the now-real DOM range.\n root.hydrating = true\n const cursor = new HydrationCursor(parent, state.startMark.nextSibling, state.endMark)\n setHydrationCursor(fiber, cursor)\n reconcileChildren(fiber, childrenToArray(state.realChildren), parent, null)\n clearHydrationCursor(fiber)\n root.hydrating = false\n runEffects(root)\n })\n}\n\nregisterTypeMatcher((type) => (type === REACT_SUSPENSE_TYPE ? FiberTag.Suspense : null))\nregisterRenderer(FiberTag.Suspense, renderSuspense)\ninstallCapability('handleSuspended', realHandleSuspended)\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { FiberTag, createFiber, type Fiber } from '../../../core'\nimport { REACT_SUSPENSE_TYPE } from '../../../react'\nimport {\n registerRenderer,\n registerTypeMatcher,\n installCapability,\n reconcileChildren,\n childrenToArray,\n renderFiber,\n scheduleUpdate,\n unmountAllChildren,\n unmountFiber,\n findRoot,\n runEffects,\n getCurrentRoot,\n withCurrentRoot,\n} from '../../reconcile'\nimport {\n HydrationCursor,\n setHydrationCursor,\n clearHydrationCursor,\n advanceCursorPast,\n tryConsumeBoundary,\n} from '../hydration'\n\nconst suspendHandlerStack: Array<(t: Promise<any>) => void> = []\n\nfunction realHandleSuspended(fiber: Fiber, thenable: Promise<any>): void {\n const handler = suspendHandlerStack[suspendHandlerStack.length - 1]\n if (handler) {\n handler(thenable)\n return\n }\n // Fallback: schedule re-render when promise settles\n thenable.then(\n () => scheduleUpdate(fiber),\n () => scheduleUpdate(fiber),\n )\n}\n\n// React's Suspense semantics: when a re-render of an already-committed\n// boundary suspends, the previously-committed children are kept in the DOM\n// (hidden) so their scroll position, focus, selection, native form state,\n// and component state survive across the suspension. The fallback is mounted\n// alongside the hidden primary until the pending promise resolves.\n//\n// We track the hidden subtree DOM in `state.hiddenDoms` (root host nodes +\n// their original `display` so we can restore it) and the fallback as a\n// detached Fragment fiber in `state.fallbackFiber` (deliberately kept OUT of\n// `fiber.child` so reconciles against `props.children` don't trip on it).\n// First-mount suspensions have no committed DOM worth preserving, so they\n// keep the original unmount-and-render-fallback behavior.\ninterface SuspenseState {\n suspended: boolean\n pending: Promise<any> | null\n hydrated?: boolean\n boundaryId?: number\n startMark?: Comment\n endMark?: Comment\n realChildren?: any\n _awaitingLazyHydration?: boolean\n // Re-suspend preservation:\n hiddenDoms: Array<[HTMLElement, string]> | null\n fallbackFiber: Fiber | null\n}\n\nfunction renderSuspense(fiber: Fiber, domParent: Node, anchor: Node | null): void {\n const props = fiber.pendingProps ?? {}\n const state = (fiber.memoizedState ??= {\n suspended: false,\n pending: null as Promise<any> | null,\n hiddenDoms: null,\n fallbackFiber: null,\n }) as SuspenseState\n\n // Streaming hydration: if the next DOM node is a server-emitted boundary\n // marker, route through the boundary-aware hydration path.\n const root = getCurrentRoot()\n if (root?.hydrating && fiber.parent && !state.hydrated) {\n const boundary = tryConsumeBoundary(fiber.parent)\n if (boundary) {\n hydrateSuspenseBoundary(fiber, props, boundary, domParent, anchor)\n state.hydrated = true\n return\n }\n }\n\n // A descendant Lazy deferred its hydration (see renderLazy's hydrating\n // branch). Its SSR-rendered content is still in the DOM and cursor-bound\n // via the Lazy fiber \u2014 we just haven't swapped it into a fiber subtree\n // yet. Until the Lazy's resume fires, skip our own tryChildren pass so\n // an unrelated re-render can't accidentally flip us into the suspended\n // path and mount a duplicate fallback on top of the SSR content.\n if (state._awaitingLazyHydration) {\n fiber.memoizedProps = props\n return\n }\n\n // We were already in the suspended-with-preserved-primary state. Don't\n // re-attempt primary children (would re-throw and churn the tree). Just\n // refresh the fallback in case its JSX changed, and wait for the pending\n // promise to fire scheduleUpdate.\n if (state.suspended && state.pending && state.fallbackFiber) {\n state.fallbackFiber.pendingProps = { children: props.fallback }\n renderFiber(state.fallbackFiber, domParent, anchor)\n fiber.memoizedProps = props\n return\n }\n\n // Initial-mount suspended path: no committed primary to preserve. Old\n // behavior \u2014 render fallback into fiber.child directly.\n if (state.suspended && state.pending && !state.fallbackFiber) {\n reconcileChildren(fiber, childrenToArray(props.fallback), domParent, anchor)\n fiber.memoizedProps = props\n return\n }\n\n // Snapshot whether we have an existing committed primary tree before\n // attempting the new render. If the new attempt suspends and we did have a\n // committed primary, we keep it (hidden) rather than destroying it.\n const hadCommittedPrimary = fiber.memoizedProps !== undefined && fiber.child !== null\n\n const savedHandler = suspendHandlerStack[suspendHandlerStack.length - 1]\n let suspendedThisRender = false\n let suspendedThenable: Promise<any> | null = null\n suspendHandlerStack.push((thenable) => {\n suspendedThisRender = true\n suspendedThenable = thenable\n })\n try {\n reconcileChildren(fiber, childrenToArray(props.children), domParent, anchor)\n } finally {\n suspendHandlerStack.pop()\n void savedHandler\n }\n\n if (suspendedThisRender && suspendedThenable) {\n state.suspended = true\n state.pending = suspendedThenable\n const onSettle = () => {\n state.suspended = false\n state.pending = null\n scheduleUpdate(fiber)\n }\n suspendedThenable.then(onSettle, onSettle)\n\n if (hadCommittedPrimary && fiber.child) {\n // Hide the primary subtree's root host doms so the fallback is the only\n // thing visible, but the underlying nodes (and their scroll/state/focus)\n // survive. Save original `display` for the resume path.\n const hostDoms: Node[] = []\n let c: Fiber | null = fiber.child\n while (c) {\n collectRootHostDoms(c, hostDoms)\n c = c.sibling\n }\n const hidden: Array<[HTMLElement, string]> = []\n for (const d of hostDoms) {\n if (d.nodeType === 1) {\n const el = d as HTMLElement\n hidden.push([el, el.style.display])\n el.style.display = 'none'\n }\n }\n state.hiddenDoms = hidden\n\n // Mount fallback in a detached Fragment fiber. Kept off `fiber.child`\n // so reconciles of primary don't see it as a stale match candidate.\n if (!state.fallbackFiber) {\n state.fallbackFiber = createFiber(FiberTag.Fragment, null, null)\n state.fallbackFiber.parent = fiber\n }\n state.fallbackFiber.pendingProps = { children: props.fallback }\n renderFiber(state.fallbackFiber, domParent, anchor)\n } else {\n // First-mount suspension \u2014 nothing to preserve.\n unmountAllChildren(fiber, domParent)\n reconcileChildren(fiber, childrenToArray(props.fallback), domParent, anchor)\n }\n } else {\n // Render succeeded. Clean up any preserved-suspend state from a prior\n // suspension cycle: unhide primary, unmount the orphan fallback fiber.\n if (state.hiddenDoms) {\n for (const [el, origDisplay] of state.hiddenDoms) {\n el.style.display = origDisplay\n }\n state.hiddenDoms = null\n }\n if (state.fallbackFiber) {\n unmountFiber(state.fallbackFiber, domParent)\n state.fallbackFiber = null\n }\n }\n fiber.memoizedProps = props\n}\n\n// Walk a fiber subtree collecting host/text DOM nodes that sit at the root\n// of the subtree (do not descend through their children \u2014 display:none on\n// the root hides the whole element). Used by the hide-on-suspend path.\nfunction collectRootHostDoms(fiber: Fiber, out: Node[]): void {\n if (fiber.tag === FiberTag.Host || fiber.tag === FiberTag.Text) {\n if (fiber.dom) out.push(fiber.dom)\n return\n }\n if (fiber.tag === FiberTag.Portal) return\n let c = fiber.child\n while (c) {\n collectRootHostDoms(c, out)\n c = c.sibling\n }\n}\n\nfunction hydrateSuspenseBoundary(\n fiber: Fiber,\n props: any,\n boundary: { kind: 'pending' | 'resolved'; id: number; startMark: Comment; endMark: Comment },\n domParent: Node,\n anchor: Node | null,\n): void {\n const { kind, id, startMark, endMark } = boundary\n // Record the boundary shape so we can re-hydrate on reveal.\n fiber.memoizedState = {\n suspended: false,\n pending: null,\n hydrated: true,\n boundaryId: id,\n startMark,\n endMark,\n realChildren: props.children,\n }\n\n if (kind === 'resolved') {\n // Real DOM is inline between startMark and endMark. Hydrate into it.\n const cursor = new HydrationCursor(startMark.parentNode!, startMark.nextSibling, endMark)\n setHydrationCursor(fiber, cursor)\n reconcileChildren(fiber, childrenToArray(props.children), domParent, anchor)\n clearHydrationCursor(fiber)\n advanceCursorPast(fiber.parent!, endMark)\n fiber.memoizedProps = props\n return\n }\n\n // Pending: fallback DOM lives inside <div id=\"B:ID\">. Hydrate the fallback\n // React subtree against that div's children.\n const bDiv = (document as Document).getElementById(`B:${id}`)\n if (bDiv) {\n const cursor = new HydrationCursor(bDiv)\n setHydrationCursor(fiber, cursor)\n reconcileChildren(fiber, childrenToArray(props.fallback), domParent, anchor)\n clearHydrationCursor(fiber)\n } else {\n // Couldn't find fallback container \u2014 render fresh (non-adopting)\n reconcileChildren(fiber, childrenToArray(props.fallback), domParent, anchor)\n }\n advanceCursorPast(fiber.parent!, endMark)\n\n // Register for server-streamed reveal (HTML chunks + $RC calls).\n const win = globalThis as any\n if (typeof win.$RH === 'function') {\n win.$RH(id, () => rehydrateBoundary(fiber))\n }\n // If the inline runtime isn't present, nothing external will mark us dirty.\n\n fiber.memoizedProps = props\n}\n\nfunction rehydrateBoundary(fiber: Fiber): void {\n const state = fiber.memoizedState\n if (!state || !state.startMark || !state.endMark) return\n\n const root = findRoot(fiber)\n if (!root) return\n const parent = state.startMark.parentNode as Node\n if (!parent) return\n\n // Unmount existing fallback subtree. Its DOM has already been removed by $RC\n // (or at least its container); unmounting here cleans up fibers + effects.\n withCurrentRoot(root, () => {\n unmountAllChildren(fiber, parent)\n\n // Re-hydrate with real children against the now-real DOM range.\n root.hydrating = true\n const cursor = new HydrationCursor(parent, state.startMark.nextSibling, state.endMark)\n setHydrationCursor(fiber, cursor)\n reconcileChildren(fiber, childrenToArray(state.realChildren), parent, null)\n clearHydrationCursor(fiber)\n root.hydrating = false\n runEffects(root)\n })\n}\n\nregisterTypeMatcher((type) => (type === REACT_SUSPENSE_TYPE ? FiberTag.Suspense : null))\nregisterRenderer(FiberTag.Suspense, renderSuspense)\ninstallCapability('handleSuspended', realHandleSuspended)\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -7,6 +7,21 @@ export declare function preload(_href: string, _opts?: any): void;
7
7
  export declare function preinit(_href: string, _opts?: any): void;
8
8
  export declare function preloadModule(_href: string, _opts?: any): void;
9
9
  export declare function preinitModule(_href: string, _opts?: any): void;
10
+ export declare const __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE: {
11
+ d: {
12
+ f(): void;
13
+ r(): void;
14
+ D(): void;
15
+ C(): void;
16
+ L(): void;
17
+ m(): void;
18
+ X(): void;
19
+ S(): void;
20
+ M(): void;
21
+ };
22
+ p: number;
23
+ findDOMNode: any;
24
+ };
10
25
  export declare const version = "19.2.3";
11
26
  import { flushSync, batchedUpdates } from './root';
12
27
  import { createPortal } from './portal';
@@ -20,6 +35,21 @@ declare const _default: {
20
35
  preinit: typeof preinit;
21
36
  preloadModule: typeof preloadModule;
22
37
  preinitModule: typeof preinitModule;
38
+ __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE: {
39
+ d: {
40
+ f(): void;
41
+ r(): void;
42
+ D(): void;
43
+ C(): void;
44
+ L(): void;
45
+ m(): void;
46
+ X(): void;
47
+ S(): void;
48
+ M(): void;
49
+ };
50
+ p: number;
51
+ findDOMNode: any;
52
+ };
23
53
  version: string;
24
54
  };
25
55
  export default _default;
package/dist/dom/index.js CHANGED
@@ -16,6 +16,30 @@ function preloadModule(_href, _opts) {
16
16
  }
17
17
  function preinitModule(_href, _opts) {
18
18
  }
19
+ var __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE = {
20
+ d: {
21
+ f() {
22
+ },
23
+ r() {
24
+ },
25
+ D() {
26
+ },
27
+ C() {
28
+ },
29
+ L() {
30
+ },
31
+ m() {
32
+ },
33
+ X() {
34
+ },
35
+ S() {
36
+ },
37
+ M() {
38
+ }
39
+ },
40
+ p: 0,
41
+ findDOMNode: null
42
+ };
19
43
  var version = "19.2.3";
20
44
  var dom_default = {
21
45
  flushSync: flushSync2,
@@ -27,9 +51,11 @@ var dom_default = {
27
51
  preinit,
28
52
  preloadModule,
29
53
  preinitModule,
54
+ __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,
30
55
  version: "19.2.3"
31
56
  };
32
57
  export {
58
+ __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,
33
59
  createPortal,
34
60
  dom_default as default,
35
61
  flushSync,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/dom/index.ts"],
4
- "sourcesContent": ["// Side-effect import: registers opt-in features (Portal, etc.) with the\n// reconciler. A vite plugin may alias individual feature modules to their\n// stub variants to strip them from the bundle.\nimport './features'\n\nexport { flushSync, batchedUpdates as unstable_batchedUpdates } from './root'\nexport { createPortal } from './portal'\n\n// Resource hints \u2014 stubs\nexport function preconnect(_href: string, _opts?: any): void {}\nexport function prefetchDNS(_href: string): void {}\nexport function preload(_href: string, _opts?: any): void {}\nexport function preinit(_href: string, _opts?: any): void {}\nexport function preloadModule(_href: string, _opts?: any): void {}\nexport function preinitModule(_href: string, _opts?: any): void {}\n\nexport const version = '19.2.3'\n\n// Required by React's default export consumers\nimport { flushSync, batchedUpdates } from './root'\nimport { createPortal } from './portal'\nexport default {\n flushSync,\n unstable_batchedUpdates: batchedUpdates,\n createPortal,\n preconnect,\n prefetchDNS,\n preload,\n preinit,\n preloadModule,\n preinitModule,\n version: '19.2.3',\n}\n"],
5
- "mappings": ";AAGA,OAAO;AAEP,SAAS,WAA6B,sBAA+B;AACrE,SAAS,oBAAoB;AAa7B,SAAS,aAAAA,YAAW,kBAAAC,uBAAsB;AAC1C,SAAS,gBAAAC,qBAAoB;AAXtB,SAAS,WAAW,OAAe,OAAmB;AAAC;AACvD,SAAS,YAAY,OAAqB;AAAC;AAC3C,SAAS,QAAQ,OAAe,OAAmB;AAAC;AACpD,SAAS,QAAQ,OAAe,OAAmB;AAAC;AACpD,SAAS,cAAc,OAAe,OAAmB;AAAC;AAC1D,SAAS,cAAc,OAAe,OAAmB;AAAC;AAE1D,IAAM,UAAU;AAKvB,IAAO,cAAQ;AAAA,EACb,WAAAF;AAAA,EACA,yBAAyBC;AAAA,EACzB,cAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AACX;",
4
+ "sourcesContent": ["// Side-effect import: registers opt-in features (Portal, etc.) with the\n// reconciler. A vite plugin may alias individual feature modules to their\n// stub variants to strip them from the bundle.\nimport './features'\n\nexport { flushSync, batchedUpdates as unstable_batchedUpdates } from './root'\nexport { createPortal } from './portal'\n\n// Resource hints \u2014 stubs\nexport function preconnect(_href: string, _opts?: any): void {}\nexport function prefetchDNS(_href: string): void {}\nexport function preload(_href: string, _opts?: any): void {}\nexport function preinit(_href: string, _opts?: any): void {}\nexport function preloadModule(_href: string, _opts?: any): void {}\nexport function preinitModule(_href: string, _opts?: any): void {}\n\nexport const __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE = {\n d: {\n f() {},\n r() {},\n D() {},\n C() {},\n L() {},\n m() {},\n X() {},\n S() {},\n M() {},\n },\n p: 0,\n findDOMNode: null,\n}\n\nexport const version = '19.2.3'\n\n// Required by React's default export consumers\nimport { flushSync, batchedUpdates } from './root'\nimport { createPortal } from './portal'\nexport default {\n flushSync,\n unstable_batchedUpdates: batchedUpdates,\n createPortal,\n preconnect,\n prefetchDNS,\n preload,\n preinit,\n preloadModule,\n preinitModule,\n __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,\n version: '19.2.3',\n}\n"],
5
+ "mappings": ";AAGA,OAAO;AAEP,SAAS,WAA6B,sBAA+B;AACrE,SAAS,oBAAoB;AA6B7B,SAAS,aAAAA,YAAW,kBAAAC,uBAAsB;AAC1C,SAAS,gBAAAC,qBAAoB;AA3BtB,SAAS,WAAW,OAAe,OAAmB;AAAC;AACvD,SAAS,YAAY,OAAqB;AAAC;AAC3C,SAAS,QAAQ,OAAe,OAAmB;AAAC;AACpD,SAAS,QAAQ,OAAe,OAAmB;AAAC;AACpD,SAAS,cAAc,OAAe,OAAmB;AAAC;AAC1D,SAAS,cAAc,OAAe,OAAmB;AAAC;AAE1D,IAAM,+DAA+D;AAAA,EAC1E,GAAG;AAAA,IACD,IAAI;AAAA,IAAC;AAAA,IACL,IAAI;AAAA,IAAC;AAAA,IACL,IAAI;AAAA,IAAC;AAAA,IACL,IAAI;AAAA,IAAC;AAAA,IACL,IAAI;AAAA,IAAC;AAAA,IACL,IAAI;AAAA,IAAC;AAAA,IACL,IAAI;AAAA,IAAC;AAAA,IACL,IAAI;AAAA,IAAC;AAAA,IACL,IAAI;AAAA,IAAC;AAAA,EACP;AAAA,EACA,GAAG;AAAA,EACH,aAAa;AACf;AAEO,IAAM,UAAU;AAKvB,IAAO,cAAQ;AAAA,EACb,WAAAF;AAAA,EACA,yBAAyBC;AAAA,EACzB,cAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AACX;",
6
6
  "names": ["flushSync", "batchedUpdates", "createPortal"]
7
7
  }
@@ -29,6 +29,7 @@ export declare function installCapability<K extends keyof Capabilities>(name: K,
29
29
  export declare function handleSuspended(fiber: Fiber, thenable: Promise<any>): void;
30
30
  export declare function handleErrorInRender(fiber: Fiber, err: any): void;
31
31
  export declare function isThenable(x: any): x is Promise<any>;
32
+ export declare function unmountFiber(fiber: Fiber, domParent: Node): void;
32
33
  export declare function unmountAllChildren(parent: Fiber, domParent: Node): void;
33
34
  export declare function readContext(fiber: Fiber, ctx: any): any;
34
35
  export declare function enqueueEffect(fiber: Fiber, effect: Effect): void;
@@ -713,12 +713,24 @@ function unmountAllChildren(parent, domParent) {
713
713
  parent.child = null;
714
714
  }
715
715
  function insertInto(parent, node, anchor) {
716
+ const projectedHeadParent = getDocumentHeadInsertionParent(parent, node);
717
+ if (projectedHeadParent) {
718
+ projectedHeadParent.appendChild(node);
719
+ return;
720
+ }
716
721
  if (anchor && anchor.parentNode === parent) {
717
722
  parent.insertBefore(node, anchor);
718
723
  } else {
719
724
  parent.appendChild(node);
720
725
  }
721
726
  }
727
+ var DOCUMENT_HEAD_TAGS = /* @__PURE__ */ new Set(["base", "link", "meta", "script", "style", "title"]);
728
+ function getDocumentHeadInsertionParent(parent, node) {
729
+ if (parent.nodeType !== 9 || node.nodeType !== 1) return null;
730
+ const tag = node.tagName.toLowerCase();
731
+ if (!DOCUMENT_HEAD_TAGS.has(tag)) return null;
732
+ return parent.head;
733
+ }
722
734
  function getHostParent(fiber) {
723
735
  let p = fiber.parent;
724
736
  while (p) {
@@ -860,6 +872,7 @@ export {
860
872
  scheduleLifecycle,
861
873
  scheduleUpdate,
862
874
  unmountAllChildren,
875
+ unmountFiber,
863
876
  withCurrentRoot
864
877
  };
865
878
  //# sourceMappingURL=reconcile.js.map