@remix-run/ui 0.4.0 → 0.6.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.
Files changed (195) hide show
  1. package/README.md +74 -1
  2. package/dist/accordion/index.js +2 -2
  3. package/dist/accordion/primitives.d.ts +1 -1
  4. package/dist/accordion/primitives.js.map +1 -1
  5. package/dist/anchor/index.js.map +1 -1
  6. package/dist/animation/animate-layout-mixin.js +1 -1
  7. package/dist/animation/animate-layout-mixin.js.map +1 -1
  8. package/dist/animation/animate-mixins.js +2 -2
  9. package/dist/animation/animate-mixins.js.map +1 -1
  10. package/dist/animation/demos/color-interpolation.js +1 -1
  11. package/dist/animation/demos/color-interpolation.js.map +1 -1
  12. package/dist/animation/demos/drag-release.d.ts +1 -1
  13. package/dist/animation/demos/hold-to-confirm.d.ts +3 -3
  14. package/dist/animation/demos/material-ripple.js +1 -3
  15. package/dist/animation/demos/material-ripple.js.map +1 -1
  16. package/dist/animation/index.js +4 -4
  17. package/dist/animation/spring.js.map +1 -1
  18. package/dist/breadcrumbs/index.js +2 -2
  19. package/dist/button/index.js +25 -25
  20. package/dist/button/index.js.map +1 -1
  21. package/dist/checkbox/index.js +11 -9
  22. package/dist/checkbox/index.js.map +1 -1
  23. package/dist/combobox/index.js +4 -4
  24. package/dist/combobox/index.js.map +1 -1
  25. package/dist/combobox/primitives.d.ts +1 -1
  26. package/dist/combobox/primitives.js +5 -5
  27. package/dist/combobox/primitives.js.map +1 -1
  28. package/dist/dev/refresh.d.ts +1 -0
  29. package/dist/dev/refresh.js +2 -0
  30. package/dist/dev/refresh.js.map +1 -0
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.js +14 -14
  33. package/dist/input/index.js +9 -9
  34. package/dist/input/index.js.map +1 -1
  35. package/dist/jsx-runtime.js +2 -2
  36. package/dist/listbox/index.js +2 -2
  37. package/dist/menu/hover-aim.js.map +1 -1
  38. package/dist/menu/index.js +3 -3
  39. package/dist/menu/primitives.d.ts +1 -1
  40. package/dist/menu/primitives.js +6 -6
  41. package/dist/menu/primitives.js.map +1 -1
  42. package/dist/popover/index.js +3 -3
  43. package/dist/popover/scroll-lock.js.map +1 -1
  44. package/dist/radio/index.js +7 -7
  45. package/dist/radio/index.js.map +1 -1
  46. package/dist/runtime/client-entry-boundary.d.ts +14 -0
  47. package/dist/runtime/client-entry-boundary.js +29 -0
  48. package/dist/runtime/client-entry-boundary.js.map +1 -0
  49. package/dist/runtime/component.d.ts +19 -4
  50. package/dist/runtime/component.js +39 -9
  51. package/dist/runtime/component.js.map +1 -1
  52. package/dist/runtime/core/attributes.js +36 -3
  53. package/dist/runtime/core/attributes.js.map +1 -1
  54. package/dist/runtime/core/children.d.ts +2 -0
  55. package/dist/runtime/core/children.js +26 -2
  56. package/dist/runtime/core/children.js.map +1 -1
  57. package/dist/runtime/core/props.js +2 -2
  58. package/dist/runtime/core/vnode.d.ts +3 -3
  59. package/dist/runtime/core/vnode.js +22 -1
  60. package/dist/runtime/core/vnode.js.map +1 -1
  61. package/dist/runtime/create-element.d.ts +2 -2
  62. package/dist/runtime/create-element.js +3 -2
  63. package/dist/runtime/create-element.js.map +1 -1
  64. package/dist/runtime/diff-dom.js +307 -220
  65. package/dist/runtime/diff-dom.js.map +1 -1
  66. package/dist/runtime/diff-props.js +3 -3
  67. package/dist/runtime/dom.d.ts +12 -0
  68. package/dist/runtime/element-function.d.ts +3 -0
  69. package/dist/runtime/element-function.js +2 -0
  70. package/dist/runtime/element-function.js.map +1 -0
  71. package/dist/runtime/form-navigation.d.ts +25 -0
  72. package/dist/runtime/form-navigation.js +132 -0
  73. package/dist/runtime/form-navigation.js.map +1 -0
  74. package/dist/runtime/frame-resolution.d.ts +5 -0
  75. package/dist/runtime/frame-resolution.js +10 -0
  76. package/dist/runtime/frame-resolution.js.map +1 -0
  77. package/dist/runtime/frame.d.ts +59 -15
  78. package/dist/runtime/frame.js +383 -114
  79. package/dist/runtime/frame.js.map +1 -1
  80. package/dist/runtime/jsx.d.ts +1 -1
  81. package/dist/runtime/jsx.js +1 -1
  82. package/dist/runtime/jsx.js.map +1 -1
  83. package/dist/runtime/key.d.ts +1 -0
  84. package/dist/runtime/key.js +2 -0
  85. package/dist/runtime/key.js.map +1 -0
  86. package/dist/runtime/mixins/attrs-mixin.js +2 -2
  87. package/dist/runtime/mixins/link-mixin.js +4 -3
  88. package/dist/runtime/mixins/link-mixin.js.map +1 -1
  89. package/dist/runtime/mixins/mixin.d.ts +15 -10
  90. package/dist/runtime/mixins/mixin.js +4 -4
  91. package/dist/runtime/mixins/mixin.js.map +1 -1
  92. package/dist/runtime/mixins/on-mixin.js +1 -1
  93. package/dist/runtime/mixins/ref-mixin.js +1 -1
  94. package/dist/runtime/module-preloader.d.ts +8 -0
  95. package/dist/runtime/module-preloader.js +95 -0
  96. package/dist/runtime/module-preloader.js.map +1 -0
  97. package/dist/runtime/navigation.d.ts +4 -1
  98. package/dist/runtime/navigation.js +157 -32
  99. package/dist/runtime/navigation.js.map +1 -1
  100. package/dist/runtime/reconcile.d.ts +11 -10
  101. package/dist/runtime/reconcile.js +549 -421
  102. package/dist/runtime/reconcile.js.map +1 -1
  103. package/dist/runtime/refresh.d.ts +28 -0
  104. package/dist/runtime/refresh.js +31 -0
  105. package/dist/runtime/refresh.js.map +1 -0
  106. package/dist/runtime/render.js +1 -1
  107. package/dist/runtime/render.js.map +1 -1
  108. package/dist/runtime/run.d.ts +3 -1
  109. package/dist/runtime/run.js +15 -7
  110. package/dist/runtime/run.js.map +1 -1
  111. package/dist/runtime/scheduler.js +8 -16
  112. package/dist/runtime/scheduler.js.map +1 -1
  113. package/dist/runtime/to-vnode.d.ts +2 -2
  114. package/dist/runtime/to-vnode.js +68 -20
  115. package/dist/runtime/to-vnode.js.map +1 -1
  116. package/dist/runtime/vdom.d.ts +4 -2
  117. package/dist/runtime/vdom.js +87 -42
  118. package/dist/runtime/vdom.js.map +1 -1
  119. package/dist/runtime/vnode.d.ts +138 -67
  120. package/dist/runtime/vnode.js +17 -12
  121. package/dist/runtime/vnode.js.map +1 -1
  122. package/dist/select/index.js +11 -11
  123. package/dist/select/index.js.map +1 -1
  124. package/dist/select/primitives.d.ts +1 -1
  125. package/dist/select/primitives.js +6 -6
  126. package/dist/server/stream.d.ts +2 -0
  127. package/dist/server/stream.js +74 -29
  128. package/dist/server/stream.js.map +1 -1
  129. package/dist/shared/focus-styles.d.ts +1 -1
  130. package/dist/shared/focus-styles.js +1 -1
  131. package/dist/shared/focus-styles.js.map +1 -1
  132. package/dist/shared/listbox-popover-styles.js +1 -1
  133. package/dist/shared/style-values.js +26 -26
  134. package/dist/shared/style-values.js.map +1 -1
  135. package/dist/style/css-mixin.js +3 -3
  136. package/dist/style/index.js +2 -2
  137. package/dist/style/style.js +25 -6
  138. package/dist/style/style.js.map +1 -1
  139. package/dist/style/stylesheet.d.ts +0 -1
  140. package/dist/style/stylesheet.js +13 -54
  141. package/dist/style/stylesheet.js.map +1 -1
  142. package/dist/tabs/index.js +11 -11
  143. package/dist/tabs/index.js.map +1 -1
  144. package/dist/tabs/primitives.d.ts +1 -1
  145. package/dist/test.js +1 -1
  146. package/dist/toggle/index.js +9 -9
  147. package/dist/toggle/index.js.map +1 -1
  148. package/dist/toggle/primitives.d.ts +1 -1
  149. package/package.json +10 -7
  150. package/src/animation/animate-mixins.ts +3 -3
  151. package/src/animation/demos/color-interpolation.tsx +1 -1
  152. package/src/button/index.ts +24 -24
  153. package/src/checkbox/index.ts +10 -8
  154. package/src/combobox/index.tsx +1 -1
  155. package/src/dev/refresh.ts +1 -0
  156. package/src/index.ts +1 -1
  157. package/src/input/index.ts +8 -8
  158. package/src/radio/index.ts +6 -5
  159. package/src/runtime/client-entry-boundary.ts +54 -0
  160. package/src/runtime/component.ts +65 -16
  161. package/src/runtime/core/attributes.ts +35 -1
  162. package/src/runtime/core/children.ts +24 -2
  163. package/src/runtime/core/vnode.ts +25 -4
  164. package/src/runtime/create-element.ts +6 -5
  165. package/src/runtime/demos/readme.demo.tsx +4 -4
  166. package/src/runtime/diff-dom.ts +370 -244
  167. package/src/runtime/dom.ts +331 -214
  168. package/src/runtime/element-function.ts +1 -0
  169. package/src/runtime/event-listeners.ts +1 -1
  170. package/src/runtime/form-navigation.ts +184 -0
  171. package/src/runtime/frame-resolution.ts +13 -0
  172. package/src/runtime/frame.ts +500 -124
  173. package/src/runtime/jsx.ts +3 -5
  174. package/src/runtime/key.ts +1 -0
  175. package/src/runtime/mixins/link-mixin.ts +1 -0
  176. package/src/runtime/mixins/mixin.ts +21 -18
  177. package/src/runtime/module-preloader.ts +115 -0
  178. package/src/runtime/navigation.ts +204 -30
  179. package/src/runtime/reconcile.ts +762 -759
  180. package/src/runtime/refresh.ts +46 -0
  181. package/src/runtime/run.ts +12 -2
  182. package/src/runtime/scheduler.ts +9 -29
  183. package/src/runtime/to-vnode.ts +85 -23
  184. package/src/runtime/vdom.ts +89 -70
  185. package/src/runtime/vnode.ts +192 -88
  186. package/src/select/index.tsx +9 -9
  187. package/src/server/README.md +13 -0
  188. package/src/server/stream.ts +93 -30
  189. package/src/shared/focus-styles.ts +1 -1
  190. package/src/shared/style-values.ts +26 -26
  191. package/src/style/style.ts +25 -6
  192. package/src/style/stylesheet.ts +32 -57
  193. package/src/tabs/index.tsx +12 -11
  194. package/src/test/README.md +151 -60
  195. package/src/toggle/index.tsx +9 -7
@@ -1,14 +1,37 @@
1
- import { jsx } from "./jsx.js";
2
- import { Frame, createFrameHandle } from "./component.js";
3
- import { createComponentErrorEvent, getComponentError } from "./error-event.js";
4
- import { invariant } from "./invariant.js";
5
- import { createRangeRoot, createRoot } from "./vdom.js";
6
- import { diffNodes } from "./diff-dom.js";
7
- import { createStyleManager } from "../style/index.js";
8
- import { findFlushMarker } from "./stream-protocol.js";
1
+ import { jsx } from './jsx.js';
2
+ import { Frame, createFrameHandle } from './component.js';
3
+ import { createComponentErrorEvent, getComponentError } from './error-event.js';
4
+ import { invariant } from './invariant.js';
5
+ import { createRangeRoot, createRoot } from './vdom.js';
6
+ import { diffNodes } from './diff-dom.js';
7
+ import { createStyleManager } from '../style/index.js';
8
+ import { findFlushMarker } from './stream-protocol.js';
9
+ import { getDocumentModulePreloader } from './module-preloader.js';
10
+ import { unwrapFrameResolution } from './frame-resolution.js';
11
+ import { disposeClientEntryBoundary, getClientEntryBoundaryOwner, setClientEntryBoundaryOwner, } from './client-entry-boundary.js';
9
12
  const bufferedFrameTemplates = new Map();
10
13
  const frameTemplateListeners = new Map();
11
14
  const DOCTYPE_PATTERN = /<!doctype(?:\s[^>]*)?>/gi;
15
+ function createLinkedAbortController(first, second) {
16
+ let controller = new AbortController();
17
+ let signals = second ? [first, second] : [first];
18
+ let abort = () => controller.abort();
19
+ for (let signal of signals) {
20
+ if (signal.aborted) {
21
+ controller.abort();
22
+ break;
23
+ }
24
+ signal.addEventListener('abort', abort, { once: true });
25
+ }
26
+ return {
27
+ controller,
28
+ disconnect() {
29
+ for (let signal of signals) {
30
+ signal.removeEventListener('abort', abort);
31
+ }
32
+ },
33
+ };
34
+ }
12
35
  function stripDoctypeMarkup(html) {
13
36
  return html.replace(DOCTYPE_PATTERN, '');
14
37
  }
@@ -24,11 +47,35 @@ function syncElementAttributes(target, source) {
24
47
  }
25
48
  }
26
49
  }
50
+ const FRAME_RUNTIME = Symbol('FrameRuntime');
51
+ export function isFrameRuntime(value) {
52
+ return isRecord(value) && Reflect.get(value, FRAME_RUNTIME) === true;
53
+ }
54
+ /**
55
+ * Reloads a frame and returns response metadata used by the navigation runtime.
56
+ *
57
+ * @param frame Frame handle to reload.
58
+ * @param options Form submission metadata and cancellation signal.
59
+ * @returns The reload signal and final response URL, when redirected.
60
+ */
61
+ export function reloadFrameForNavigation(frame, options) {
62
+ let runtime = frame.$runtime;
63
+ invariant(isFrameRuntime(runtime), 'Expected a frame runtime');
64
+ let reload = runtime.reloadForNavigation;
65
+ invariant(reload, 'Expected frame runtime to support navigation reloads');
66
+ return reload(options);
67
+ }
27
68
  export function createFrame(root, init) {
28
69
  let container = createContainer(root);
29
70
  let contentRoot;
30
71
  let reloadController;
72
+ // The style registry is document-level and shared by every frame; only the
73
+ // frame that created it (the runtime root) disposes it.
74
+ let ownsStyleManager = !init.styleManager;
75
+ let reloadAbortUnsubscribe;
76
+ let reloadKind;
31
77
  let styleManager = init.styleManager ?? createStyleManager();
78
+ let modulePreloader = getDocumentModulePreloader(container.doc);
32
79
  let currentMarker = init.marker;
33
80
  let displayedContentStatus = init.marker?.status ?? 'resolved';
34
81
  let pendingTemplateMarkerId;
@@ -36,37 +83,21 @@ export function createFrame(root, init) {
36
83
  let pendingTemplateUnsubscribe;
37
84
  let inheritedReloadPending = false;
38
85
  let inheritedReloadAbortUnsubscribe;
86
+ let disposed = false;
87
+ let lifecycleController = new AbortController();
88
+ if (isDocumentNode(container.root)) {
89
+ modulePreloader.adoptInitialPreloadLinks(container.root);
90
+ }
91
+ else {
92
+ modulePreloader.consumePreloadLinks(container.root);
93
+ }
39
94
  // Merge any rmx-data found in the current document once at startup.
40
95
  mergeRmxDataFromDocument(init.data, container.doc);
41
- let runtime = createFrameRuntime({ ...init, styleManager });
96
+ let runtime = createFrameRuntime({ ...init, styleManager, reloadForNavigation: reload });
42
97
  let frame = createFrameHandle({
43
98
  src: init.src,
44
99
  $runtime: runtime,
45
- reload: async () => {
46
- reloadController?.abort();
47
- let controller = new AbortController();
48
- reloadController = controller;
49
- frame.dispatchEvent(new Event('reloadStart'));
50
- startSubFrameInheritedReloads(getContentNodes(), controller.signal);
51
- try {
52
- let content = await init.resolveFrame(frame.src, controller.signal, frameName);
53
- if (reloadController !== controller || controller.signal.aborted)
54
- return controller.signal;
55
- await render(content, { signal: controller.signal });
56
- return controller.signal;
57
- }
58
- catch (error) {
59
- if (reloadController !== controller || controller.signal.aborted)
60
- return controller.signal;
61
- init.errorTarget.dispatchEvent(createComponentErrorEvent(error));
62
- throw error;
63
- }
64
- finally {
65
- if (reloadController === controller) {
66
- frame.dispatchEvent(new Event('reloadComplete'));
67
- }
68
- }
69
- },
100
+ reload: async () => (await reload()).signal,
70
101
  replace: async (content) => {
71
102
  await render(content);
72
103
  },
@@ -90,18 +121,41 @@ export function createFrame(root, init) {
90
121
  moduleLoads: init.moduleLoads,
91
122
  frameInstances: init.frameInstances,
92
123
  namedFrames: init.namedFrames,
124
+ lifecycleSignal: lifecycleController.signal,
93
125
  regionTailRef: container.regionTailRef,
94
126
  regionParent: container.regionParent,
95
127
  };
96
128
  async function render(content, options) {
97
- if (options?.signal?.aborted)
129
+ if (disposed || lifecycleController.signal.aborted || options?.signal?.aborted)
130
+ return;
131
+ let ownsData = options?.data === undefined;
132
+ let renderOptions = {
133
+ ...options,
134
+ data: options?.data ?? {},
135
+ };
136
+ try {
137
+ await renderContent(content, renderOptions);
138
+ }
139
+ finally {
140
+ if (ownsData)
141
+ clearRmxData(renderOptions.data);
142
+ }
143
+ }
144
+ async function renderContent(content, options) {
145
+ if (isRenderAborted(options.signal))
98
146
  return;
99
147
  if (content instanceof ReadableStream) {
100
- await renderFrameStream(content, container.doc, async (html, flushKind) => {
101
- if (options?.signal?.aborted)
102
- return;
103
- await render(html, { ...options, flushKind });
104
- });
148
+ let linkedAbort = createLinkedAbortController(lifecycleController.signal, options.signal);
149
+ try {
150
+ await renderFrameStream(content, container.doc, async (html, flushKind) => {
151
+ if (isRenderAborted(options.signal))
152
+ return;
153
+ await render(html, { ...options, flushKind });
154
+ }, linkedAbort.controller.signal);
155
+ }
156
+ finally {
157
+ linkedAbort.disconnect();
158
+ }
105
159
  return;
106
160
  }
107
161
  if (isRemixNodeFrameContent(content)) {
@@ -112,10 +166,10 @@ export function createFrame(root, init) {
112
166
  clearFrameContent();
113
167
  contentRoot = createFrameContentRoot();
114
168
  }
115
- if (options?.signal?.aborted)
169
+ if (isRenderAborted(options.signal))
116
170
  return;
117
171
  contentRoot.render(content);
118
- displayedContentStatus = options?.contentStatus ?? 'resolved';
172
+ displayedContentStatus = options.contentStatus ?? 'resolved';
119
173
  return;
120
174
  }
121
175
  if (contentRoot) {
@@ -136,50 +190,66 @@ export function createFrame(root, init) {
136
190
  let htmlContent = typeof content === 'string' ? stripDoctypeMarkup(content) : undefined;
137
191
  let isFullDocumentReload = container.root instanceof Document &&
138
192
  htmlContent !== undefined &&
139
- options?.flushKind === 'document';
193
+ options.flushKind === 'document';
140
194
  if (isFullDocumentReload && htmlContent !== undefined) {
141
195
  let parsed = new DOMParser().parseFromString(htmlContent, 'text/html');
142
- mergeRmxDataFromDocument(context.data, parsed);
143
- context.styleManager.replaceServerStyles(collectFrameServerStyleTags(createElementContainer(parsed)));
196
+ modulePreloader.consumePreloadLinks(parsed);
197
+ let responseData = options.data;
198
+ mergeRmxDataFromDocument(responseData, parsed);
199
+ let responseContext = { ...context, data: responseData };
200
+ context.styleManager.adoptServerStyles(collectFrameServerStyleTags(createElementContainer(parsed)));
144
201
  syncElementAttributes(container.doc.documentElement, parsed.documentElement);
145
202
  diffNodes([container.doc.head], [parsed.head], {
146
- ...context,
203
+ ...responseContext,
147
204
  regionParent: container.doc.documentElement,
148
205
  regionTailRef: null,
149
- signal: options?.signal,
206
+ signal: options.signal,
207
+ isActiveModulePreload: modulePreloader.hasActivePreloads()
208
+ ? modulePreloader.isActivePreload
209
+ : undefined,
150
210
  });
151
211
  diffNodes([container.doc.body], [parsed.body], {
152
- ...context,
212
+ ...responseContext,
153
213
  regionParent: container.doc.documentElement,
154
214
  regionTailRef: null,
155
- signal: options?.signal,
215
+ signal: options.signal,
156
216
  });
157
217
  let bodyContainer = createElementContainer(container.doc.body);
158
- if (options?.signal?.aborted)
218
+ if (isRenderAborted(options.signal))
159
219
  return;
160
- scheduleHydrationInContainer(bodyContainer, context, options?.initialHydrationTracker);
161
- await createSubFrames(bodyContainer.childNodes, context, options);
162
- displayedContentStatus = options?.contentStatus ?? 'resolved';
220
+ scheduleHydrationInContainer(bodyContainer, responseContext, options.initialHydrationTracker, options.signal);
221
+ await createSubFrames(bodyContainer.childNodes, responseContext, options);
222
+ if (isRenderAborted(options.signal))
223
+ return;
224
+ displayedContentStatus = options.contentStatus ?? 'resolved';
163
225
  return;
164
226
  }
165
227
  let fragment = htmlContent !== undefined ? createFragmentFromString(container.doc, htmlContent) : content;
166
- context.styleManager.replaceServerStyles(collectFrameServerStyleTags(createElementContainer(fragment)));
228
+ modulePreloader.consumePreloadLinks(fragment);
229
+ context.styleManager.adoptServerStyles(collectFrameServerStyleTags(createElementContainer(fragment)));
167
230
  removeEmptyHeads(fragment);
168
- mergeRmxDataFromFragment(context.data, fragment);
231
+ let responseData = options.data;
232
+ mergeRmxDataFromFragment(responseData, fragment);
233
+ let responseContext = { ...context, data: responseData };
169
234
  let nextContainer = createContainer(fragment);
170
- if (options?.signal?.aborted)
235
+ if (isRenderAborted(options.signal))
171
236
  return;
172
237
  diffNodes(container.childNodes, Array.from(nextContainer.childNodes), {
173
- ...context,
238
+ ...responseContext,
174
239
  regionTailRef: container.regionTailRef,
175
240
  regionParent: container.regionParent,
176
- signal: options?.signal,
241
+ signal: options.signal,
177
242
  });
178
- if (options?.signal?.aborted)
243
+ if (isRenderAborted(options.signal))
244
+ return;
245
+ scheduleHydrationInContainer(container, responseContext, options.initialHydrationTracker, options.signal);
246
+ await createSubFrames(container.childNodes, responseContext, options);
247
+ if (isRenderAborted(options.signal))
179
248
  return;
180
- scheduleHydrationInContainer(container, context, options?.initialHydrationTracker);
181
- await createSubFrames(container.childNodes, context, options);
182
- displayedContentStatus = options?.contentStatus ?? 'resolved';
249
+ displayedContentStatus = options.contentStatus ?? 'resolved';
250
+ }
251
+ function isRenderAborted(signal) {
252
+ return disposed || lifecycleController.signal.aborted || signal?.aborted === true;
183
253
  }
184
254
  function createFrameContentRoot() {
185
255
  let virtualRoot;
@@ -217,18 +287,34 @@ export function createFrame(root, init) {
217
287
  }
218
288
  async function hydrateInitial() {
219
289
  let initialHydrationTracker = createInitialHydrationTracker();
220
- context.styleManager.replaceServerStyles(collectFrameServerStyleTags(container));
221
- await createSubFrames(container.childNodes, context);
290
+ context.styleManager.adoptServerStyles(collectFrameServerStyleTags(container));
291
+ let subFramesReady = createSubFrames(container.childNodes, context);
222
292
  scheduleHydrationInContainer(container, context, initialHydrationTracker);
223
- if (currentMarker?.status === 'pending') {
224
- await watchPendingFrameTemplate(currentMarker, initialHydrationTracker);
293
+ try {
294
+ await subFramesReady;
295
+ if (disposed || context.lifecycleSignal.aborted)
296
+ return;
297
+ if (currentMarker?.status === 'pending') {
298
+ await watchPendingFrameTemplate(currentMarker, initialHydrationTracker);
299
+ }
300
+ initialHydrationTracker.finalize();
301
+ await initialHydrationTracker.ready();
302
+ }
303
+ finally {
304
+ clearRmxData(context.data);
225
305
  }
226
- initialHydrationTracker.finalize();
227
- await initialHydrationTracker.ready();
228
306
  }
229
307
  function dispose() {
308
+ if (disposed)
309
+ return;
310
+ disposed = true;
311
+ lifecycleController.abort();
312
+ clearRmxData(context.data);
230
313
  reloadController?.abort();
231
314
  reloadController = undefined;
315
+ reloadAbortUnsubscribe?.();
316
+ reloadAbortUnsubscribe = undefined;
317
+ reloadKind = undefined;
232
318
  contentRoot?.dispose();
233
319
  contentRoot = undefined;
234
320
  clearPendingFrameTemplateWatch();
@@ -236,7 +322,9 @@ export function createFrame(root, init) {
236
322
  removeVirtualRoots(container.childNodes);
237
323
  // Dispose sub-frames recursively.
238
324
  disposeSubFrames(container.childNodes, context);
239
- context.styleManager.dispose();
325
+ if (ownsStyleManager) {
326
+ context.styleManager.dispose();
327
+ }
240
328
  if (frameName) {
241
329
  if (init.namedFrames.get(frameName) === frame) {
242
330
  init.namedFrames.delete(frameName);
@@ -250,14 +338,17 @@ export function createFrame(root, init) {
250
338
  flush: () => context.scheduler.dequeue(),
251
339
  clearPendingTemplateWatch: clearPendingFrameTemplateWatch,
252
340
  isDisplayingResolvedContent: () => displayedContentStatus === 'resolved',
341
+ beginClientFrameReloadForAncestorReload,
342
+ cancelReload,
253
343
  startInheritedReload,
254
344
  updateMarker,
255
345
  renderMarkerContent,
346
+ matchesIdentity: (src, name) => !disposed && frame.src === src && frameName === name,
256
347
  dispose,
257
348
  handle: frame,
258
349
  };
259
350
  async function updateMarker(marker, options) {
260
- if (options?.signal?.aborted)
351
+ if (disposed || context.lifecycleSignal.aborted || options?.signal?.aborted)
261
352
  return;
262
353
  let previousMarker = currentMarker;
263
354
  let isInheritedReload = previousMarker !== undefined && previousMarker.id !== marker.id;
@@ -280,7 +371,7 @@ export function createFrame(root, init) {
280
371
  }
281
372
  }
282
373
  async function renderMarkerContent(marker, content, options) {
283
- if (options?.signal?.aborted)
374
+ if (disposed || context.lifecycleSignal.aborted || options?.signal?.aborted)
284
375
  return;
285
376
  let previousMarker = currentMarker;
286
377
  let isInheritedReload = previousMarker !== undefined && previousMarker.id !== marker.id;
@@ -290,10 +381,102 @@ export function createFrame(root, init) {
290
381
  }
291
382
  clearPendingFrameTemplateWatch();
292
383
  await render(content, { ...options, contentStatus: 'resolved' });
293
- if (isInheritedReload && !options?.signal?.aborted) {
384
+ if (isInheritedReload &&
385
+ !disposed &&
386
+ !context.lifecycleSignal.aborted &&
387
+ !options?.signal?.aborted) {
294
388
  completeInheritedReload();
295
389
  }
296
390
  }
391
+ async function reload(options) {
392
+ let controller = startReload(options?.signal);
393
+ return await resolveAndRenderReload(controller, options);
394
+ }
395
+ function startReload(signal) {
396
+ let controller = replaceReloadController(signal);
397
+ reloadKind = 'direct';
398
+ frame.dispatchEvent(new Event('reloadStart'));
399
+ startSubFrameInheritedReloads(getContentNodes(), controller.signal);
400
+ return controller;
401
+ }
402
+ function beginClientFrameReloadForAncestorReload(signal) {
403
+ let inheritedReloadStarted = reuseInheritedReloadStart();
404
+ let continuingAncestorReload = reloadKind === 'ancestor';
405
+ let controller = replaceReloadController(signal);
406
+ reloadKind = 'ancestor';
407
+ if (!inheritedReloadStarted && !continuingAncestorReload) {
408
+ frame.dispatchEvent(new Event('reloadStart'));
409
+ startSubFrameInheritedReloads(getContentNodes(), controller.signal);
410
+ }
411
+ return {
412
+ controller,
413
+ complete: () => completeReload(controller),
414
+ };
415
+ }
416
+ function cancelReload() {
417
+ let controller = reloadController;
418
+ if (!controller)
419
+ return;
420
+ controller.abort();
421
+ completeReload(controller);
422
+ }
423
+ function replaceReloadController(signal) {
424
+ reloadController?.abort();
425
+ reloadAbortUnsubscribe?.();
426
+ reloadAbortUnsubscribe = undefined;
427
+ let controller = new AbortController();
428
+ reloadController = controller;
429
+ if (signal) {
430
+ if (signal.aborted) {
431
+ controller.abort();
432
+ }
433
+ else {
434
+ let abort = () => controller.abort();
435
+ signal.addEventListener('abort', abort, { once: true });
436
+ reloadAbortUnsubscribe = () => signal.removeEventListener('abort', abort);
437
+ }
438
+ }
439
+ return controller;
440
+ }
441
+ async function resolveAndRenderReload(controller, options) {
442
+ try {
443
+ let resolution = await init.resolveFrame(frame.src, {
444
+ ...options,
445
+ signal: controller.signal,
446
+ target: frameName,
447
+ });
448
+ if (reloadController !== controller || controller.signal.aborted) {
449
+ return { signal: controller.signal };
450
+ }
451
+ let { content, redirectedTo } = await unwrapFrameResolution(resolution);
452
+ if (reloadController !== controller || controller.signal.aborted) {
453
+ return { signal: controller.signal };
454
+ }
455
+ await render(content, { signal: controller.signal });
456
+ return {
457
+ signal: controller.signal,
458
+ redirectedTo: reloadController === controller && !controller.signal.aborted ? redirectedTo : undefined,
459
+ };
460
+ }
461
+ catch (error) {
462
+ if (reloadController !== controller || controller.signal.aborted) {
463
+ return { signal: controller.signal };
464
+ }
465
+ init.errorTarget.dispatchEvent(createComponentErrorEvent(error));
466
+ throw error;
467
+ }
468
+ finally {
469
+ completeReload(controller);
470
+ }
471
+ }
472
+ function completeReload(controller) {
473
+ if (reloadController !== controller || reloadKind === undefined)
474
+ return;
475
+ reloadAbortUnsubscribe?.();
476
+ reloadAbortUnsubscribe = undefined;
477
+ reloadKind = undefined;
478
+ frame.dispatchEvent(new Event('reloadComplete'));
479
+ }
297
480
  function startInheritedReload(signal) {
298
481
  if (signal?.aborted)
299
482
  return;
@@ -312,6 +495,14 @@ export function createFrame(root, init) {
312
495
  };
313
496
  }
314
497
  }
498
+ function reuseInheritedReloadStart() {
499
+ if (!inheritedReloadPending)
500
+ return false;
501
+ inheritedReloadPending = false;
502
+ inheritedReloadAbortUnsubscribe?.();
503
+ inheritedReloadAbortUnsubscribe = undefined;
504
+ return true;
505
+ }
315
506
  function completeInheritedReload() {
316
507
  if (!inheritedReloadPending)
317
508
  return;
@@ -328,7 +519,7 @@ export function createFrame(root, init) {
328
519
  if (isFrameStart(node)) {
329
520
  let end = findEndMarker(node, isFrameStart, isFrameEnd);
330
521
  context.frameInstances.get(node)?.startInheritedReload(signal);
331
- i = nodes.indexOf(end);
522
+ i = findMarkerRangeEndIndex(nodes, end, i);
332
523
  continue;
333
524
  }
334
525
  if (node.childNodes && node.childNodes.length > 0) {
@@ -344,7 +535,7 @@ export function createFrame(root, init) {
344
535
  pendingTemplateMarkerId = undefined;
345
536
  }
346
537
  async function watchPendingFrameTemplate(marker, initialHydrationTracker, signal, onResolved) {
347
- if (signal?.aborted)
538
+ if (disposed || context.lifecycleSignal.aborted || signal?.aborted)
348
539
  return;
349
540
  if (pendingTemplateMarkerId === marker.id)
350
541
  return;
@@ -354,24 +545,24 @@ export function createFrame(root, init) {
354
545
  if (early) {
355
546
  clearPendingFrameTemplateWatch();
356
547
  await render(early, { initialHydrationTracker, signal, contentStatus: 'resolved' });
357
- if (!signal?.aborted)
548
+ if (!disposed && !context.lifecycleSignal.aborted && !signal?.aborted)
358
549
  onResolved?.();
359
550
  return;
360
551
  }
361
- if (signal?.aborted) {
552
+ if (disposed || context.lifecycleSignal.aborted || signal?.aborted) {
362
553
  clearPendingFrameTemplateWatch();
363
554
  return;
364
555
  }
365
556
  let observer = setupTemplateObserver();
366
557
  pendingTemplateObserver = observer;
367
558
  let unsubscribe = subscribeFrameTemplate(marker.id, async (fragment) => {
368
- if (signal?.aborted)
559
+ if (disposed || context.lifecycleSignal.aborted || signal?.aborted)
369
560
  return;
370
561
  if (pendingTemplateMarkerId !== marker.id)
371
562
  return;
372
563
  clearPendingFrameTemplateWatch();
373
564
  await render(fragment, { signal, contentStatus: 'resolved' });
374
- if (!signal?.aborted)
565
+ if (!disposed && !context.lifecycleSignal.aborted && !signal?.aborted)
375
566
  onResolved?.();
376
567
  });
377
568
  pendingTemplateUnsubscribe = unsubscribe;
@@ -384,13 +575,14 @@ export function createFrame(root, init) {
384
575
  if (buffered) {
385
576
  clearPendingFrameTemplateWatch();
386
577
  await render(buffered, { initialHydrationTracker, signal, contentStatus: 'resolved' });
387
- if (!signal?.aborted)
578
+ if (!disposed && !context.lifecycleSignal.aborted && !signal?.aborted)
388
579
  onResolved?.();
389
580
  }
390
581
  }
391
582
  }
392
583
  export function createFrameRuntime(init) {
393
584
  return {
585
+ [FRAME_RUNTIME]: true,
394
586
  topFrame: init.topFrame,
395
587
  errorTarget: init.errorTarget,
396
588
  loadModule: init.loadModule,
@@ -398,11 +590,12 @@ export function createFrameRuntime(init) {
398
590
  pendingClientEntries: init.pendingClientEntries,
399
591
  scheduler: init.scheduler,
400
592
  styleManager: init.styleManager,
401
- data: init.data,
402
593
  moduleCache: init.moduleCache,
403
594
  moduleLoads: init.moduleLoads,
404
595
  frameInstances: init.frameInstances,
405
596
  namedFrames: init.namedFrames,
597
+ serverFrameReload: undefined,
598
+ reloadForNavigation: init.reloadForNavigation,
406
599
  };
407
600
  }
408
601
  function createInitialHydrationTracker() {
@@ -457,6 +650,10 @@ function mergeRmxDataFromFragment(into, fragment) {
457
650
  script.remove();
458
651
  }
459
652
  }
653
+ function clearRmxData(data) {
654
+ delete data.h;
655
+ delete data.f;
656
+ }
460
657
  function removeEmptyHeads(fragment) {
461
658
  let heads = Array.from(fragment.querySelectorAll('head'));
462
659
  for (let head of heads) {
@@ -478,14 +675,14 @@ function collectOwnedServerStyleTags(nodes, styles) {
478
675
  let node = nodes[i];
479
676
  if (isFrameStart(node)) {
480
677
  let end = findEndMarker(node, isFrameStart, isFrameEnd);
481
- i = nodes.indexOf(end);
678
+ i = findMarkerRangeEndIndex(nodes, end, i);
482
679
  continue;
483
680
  }
484
681
  if (node instanceof HTMLStyleElement && node.matches('style[data-rmx]')) {
485
682
  styles.push(node);
486
683
  continue;
487
684
  }
488
- if (node instanceof Element || node instanceof Document || node instanceof DocumentFragment) {
685
+ if (node.childNodes.length > 0) {
489
686
  collectOwnedServerStyleTags(Array.from(node.childNodes), styles);
490
687
  }
491
688
  }
@@ -520,7 +717,7 @@ function copyOwnRmxEntries(target, source) {
520
717
  target[key] = source[key];
521
718
  }
522
719
  }
523
- function scheduleHydrationInContainer(container, context, initialHydrationTracker) {
720
+ function scheduleHydrationInContainer(container, context, initialHydrationTracker, signal) {
524
721
  let hydrationMarkers = findHydrationMarkers(container);
525
722
  if (hydrationMarkers.length === 0)
526
723
  return;
@@ -531,44 +728,67 @@ function scheduleHydrationInContainer(container, context, initialHydrationTracke
531
728
  let entry = hydrationData[marker.id];
532
729
  if (!entry)
533
730
  continue;
534
- scheduleHydrationMarker(marker, entry, context, initialHydrationTracker);
731
+ scheduleHydrationMarker(marker, entry, context, initialHydrationTracker, signal);
535
732
  }
536
733
  }
537
- function scheduleHydrationMarker(marker, entry, context, initialHydrationTracker) {
734
+ function scheduleHydrationMarker(marker, entry, context, initialHydrationTracker, signal) {
735
+ if (signal?.aborted || context.lifecycleSignal.aborted)
736
+ return;
538
737
  let done = initialHydrationTracker?.track();
539
738
  let key = `${entry.moduleUrl}#${entry.exportName}`;
739
+ let identity = {
740
+ moduleUrl: entry.moduleUrl,
741
+ exportName: entry.exportName,
742
+ };
743
+ let props = entry.props;
744
+ let completed = false;
745
+ let complete = () => {
746
+ if (completed)
747
+ return;
748
+ completed = true;
749
+ props = undefined;
750
+ signal?.removeEventListener('abort', complete);
751
+ context.lifecycleSignal.removeEventListener('abort', complete);
752
+ done?.();
753
+ };
754
+ signal?.addEventListener('abort', complete, { once: true });
755
+ context.lifecycleSignal.addEventListener('abort', complete, { once: true });
540
756
  let hydrateWithComponent = (component) => {
757
+ if (signal?.aborted || context.lifecycleSignal.aborted)
758
+ return;
541
759
  if (!isHydrationMarkerLive(marker, context))
542
760
  return;
543
- let vElement = createElement(component, entry.props);
761
+ if (!props)
762
+ return;
763
+ let vElement = createElement(component, props);
544
764
  context.pendingClientEntries.set(marker.start, [marker.end, vElement]);
545
- hydrateRegion(vElement, marker.start, marker.end, context);
765
+ hydrateRegion(vElement, marker.start, marker.end, identity, context, signal);
546
766
  };
547
767
  let cached = context.moduleCache.get(key);
548
768
  if (cached) {
549
769
  hydrateWithComponent(cached);
550
- done?.();
770
+ complete();
551
771
  return;
552
772
  }
553
- getOrStartModuleLoad(key, entry, marker.id, context)
773
+ getOrStartModuleLoad(key, identity, marker.id, context)
554
774
  .then((component) => {
555
775
  if (component) {
556
776
  hydrateWithComponent(component);
557
777
  }
558
778
  })
559
779
  .finally(() => {
560
- done?.();
780
+ complete();
561
781
  });
562
782
  }
563
- function getOrStartModuleLoad(key, entry, markerId, context) {
783
+ function getOrStartModuleLoad(key, identity, markerId, context) {
564
784
  let inFlight = context.moduleLoads.get(key);
565
785
  if (inFlight)
566
786
  return inFlight;
567
787
  let loadPromise = (async () => {
568
788
  try {
569
- let mod = await context.loadModule(entry.moduleUrl, entry.exportName);
570
- if (typeof mod !== 'function') {
571
- throw new Error(`Export "${entry.exportName}" from "${entry.moduleUrl}" is not a function`);
789
+ let mod = await context.loadModule(identity.moduleUrl, identity.exportName);
790
+ if (!isElementFunction(mod)) {
791
+ throw new Error(`Export "${identity.exportName}" from "${identity.moduleUrl}" is not a function`);
572
792
  }
573
793
  context.moduleCache.set(key, mod);
574
794
  return mod;
@@ -586,8 +806,12 @@ function getOrStartModuleLoad(key, entry, markerId, context) {
586
806
  }
587
807
  function createElement(component, props) {
588
808
  let revivedProps = reviveSerializedValue(props);
809
+ invariant(isRecord(revivedProps), 'Expected revived component props to be an object');
589
810
  return jsx(component, revivedProps);
590
811
  }
812
+ function isElementFunction(value) {
813
+ return typeof value === 'function';
814
+ }
591
815
  function reviveSerializedValue(value) {
592
816
  if (value === null || value === undefined)
593
817
  return value;
@@ -596,6 +820,8 @@ function reviveSerializedValue(value) {
596
820
  if (Array.isArray(value)) {
597
821
  return value.map((item) => reviveSerializedValue(item));
598
822
  }
823
+ if (!isRecord(value))
824
+ return value;
599
825
  let record = value;
600
826
  if (record.$rmxFrame === true) {
601
827
  let props = reviveSerializedObject(record.props);
@@ -619,15 +845,34 @@ function reviveSerializedObject(value) {
619
845
  let revived = reviveSerializedValue(value);
620
846
  if (!revived || typeof revived !== 'object' || Array.isArray(revived))
621
847
  return {};
622
- return revived;
848
+ return isRecord(revived) ? revived : {};
849
+ }
850
+ function isRecord(value) {
851
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
623
852
  }
624
- function hydrateRegion(vElement, start, end, context) {
853
+ function hydrateRegion(vElement, start, end, identity, context, signal) {
854
+ if (signal?.aborted)
855
+ return;
625
856
  context.pendingClientEntries.delete(start);
626
857
  // The same marker can be discovered by overlapping hydration passes
627
858
  // (for example, document root + nested frame root). Reuse the existing
628
859
  // virtual root instead of redefining the marker property.
629
- if (isHydratedVirtualRootMarker(start)) {
630
- start.$rmx.render(vElement);
860
+ let owner = getClientEntryBoundaryOwner(start);
861
+ if (owner) {
862
+ if (!signal) {
863
+ owner.root.render(vElement);
864
+ return;
865
+ }
866
+ let frameRuntime = context.frame.$runtime;
867
+ invariant(isFrameRuntime(frameRuntime), 'Expected frame runtime while rendering a preserved client entry');
868
+ let previousServerFrameReload = frameRuntime.serverFrameReload;
869
+ frameRuntime.serverFrameReload = { signal };
870
+ try {
871
+ owner.root.render(vElement);
872
+ }
873
+ finally {
874
+ frameRuntime.serverFrameReload = previousServerFrameReload;
875
+ }
631
876
  return;
632
877
  }
633
878
  let root = createRangeRoot([start, end], {
@@ -640,7 +885,7 @@ function hydrateRegion(vElement, start, end, context) {
640
885
  return;
641
886
  context.errorTarget.dispatchEvent(createComponentErrorEvent(getComponentError(event)));
642
887
  });
643
- Object.defineProperty(start, '$rmx', { value: root, enumerable: false });
888
+ setClientEntryBoundaryOwner(start, identity, root);
644
889
  root.render(vElement);
645
890
  }
646
891
  async function createSubFrames(nodes, context, options) {
@@ -675,6 +920,7 @@ async function createSubFrames(nodes, context, options) {
675
920
  resolveFrame: context.resolveFrame,
676
921
  pendingClientEntries: context.pendingClientEntries,
677
922
  scheduler: context.scheduler,
923
+ styleManager: context.styleManager,
678
924
  data: context.data,
679
925
  moduleCache: context.moduleCache,
680
926
  moduleLoads: context.moduleLoads,
@@ -684,7 +930,7 @@ async function createSubFrames(nodes, context, options) {
684
930
  context.frameInstances.set(node, subFrame);
685
931
  }
686
932
  }
687
- i = nodes.indexOf(end);
933
+ i = findMarkerRangeEndIndex(nodes, end, i);
688
934
  continue;
689
935
  }
690
936
  if (node.childNodes && node.childNodes.length > 0) {
@@ -719,10 +965,9 @@ function isHydrationMarkerLive(marker, context) {
719
965
  function removeVirtualRoots(nodes) {
720
966
  for (let i = 0; i < nodes.length; i++) {
721
967
  let node = nodes[i];
722
- if (isHydratedVirtualRootMarker(node)) {
723
- node.$rmx.dispose();
968
+ if (isCommentNode(node) && isHydrationStart(node) && disposeClientEntryBoundary(node)) {
724
969
  let end = findEndMarker(node, isHydrationStart, isHydrationEnd);
725
- i = nodes.indexOf(end);
970
+ i = findMarkerRangeEndIndex(nodes, end, i);
726
971
  continue;
727
972
  }
728
973
  if (node.childNodes && node.childNodes.length > 0) {
@@ -740,7 +985,7 @@ function disposeSubFrames(nodes, context) {
740
985
  subFrame.dispose();
741
986
  context.frameInstances.delete(node);
742
987
  }
743
- i = nodes.indexOf(end);
988
+ i = findMarkerRangeEndIndex(nodes, end, i);
744
989
  continue;
745
990
  }
746
991
  if (node.childNodes && node.childNodes.length > 0) {
@@ -801,7 +1046,9 @@ export function publishFrameTemplate(id, fragment) {
801
1046
  return;
802
1047
  }
803
1048
  for (let listener of listeners) {
804
- listener(fragment.cloneNode(true));
1049
+ let clone = fragment.cloneNode(true);
1050
+ invariant(isDocumentFragmentNode(clone), 'Expected cloned frame template fragment');
1051
+ listener(clone);
805
1052
  }
806
1053
  }
807
1054
  export function consumeFrameTemplate(id) {
@@ -873,12 +1120,21 @@ function extractTemplatesFromBuffer(doc, buffer, onTemplate) {
873
1120
  remainder: tail.slice(tailStart),
874
1121
  };
875
1122
  }
876
- async function renderFrameStream(stream, doc, applyHtml) {
1123
+ async function renderFrameStream(stream, doc, applyHtml, signal) {
877
1124
  let reader = stream.getReader();
878
1125
  let decoder = new TextDecoder();
879
1126
  let buffer = '';
880
1127
  let html = '';
881
1128
  let appliedOnce = false;
1129
+ let abort = () => {
1130
+ void reader.cancel().catch(() => { });
1131
+ };
1132
+ if (signal?.aborted) {
1133
+ await reader.cancel();
1134
+ reader.releaseLock();
1135
+ return;
1136
+ }
1137
+ signal?.addEventListener('abort', abort, { once: true });
882
1138
  try {
883
1139
  while (true) {
884
1140
  let { done, value } = await reader.read();
@@ -913,6 +1169,7 @@ async function renderFrameStream(stream, doc, applyHtml) {
913
1169
  }
914
1170
  }
915
1171
  finally {
1172
+ signal?.removeEventListener('abort', abort);
916
1173
  reader.releaseLock();
917
1174
  }
918
1175
  }
@@ -973,7 +1230,7 @@ function createFragmentFromString(doc, content) {
973
1230
  }
974
1231
  function isRemixNodeFrameContent(content) {
975
1232
  return !(content instanceof ReadableStream ||
976
- content instanceof DocumentFragment ||
1233
+ isDocumentFragmentNode(content) ||
977
1234
  typeof content === 'string');
978
1235
  }
979
1236
  function findHydrationMarkers(container) {
@@ -998,10 +1255,10 @@ function walkCommentsInNodes(nodes, cb) {
998
1255
  // are discovered and hydrated by the nested frame instance only.
999
1256
  if (isFrameStart(node)) {
1000
1257
  let end = findEndMarker(node, isFrameStart, isFrameEnd);
1001
- i = nodes.indexOf(end);
1258
+ i = findMarkerRangeEndIndex(nodes, end, i);
1002
1259
  continue;
1003
1260
  }
1004
- if (node.nodeType === Node.COMMENT_NODE)
1261
+ if (isCommentNode(node))
1005
1262
  cb(node);
1006
1263
  if (node.childNodes && node.childNodes.length > 0) {
1007
1264
  walkCommentsInNodes(Array.from(node.childNodes), cb);
@@ -1014,11 +1271,8 @@ function isHydrationStart(node) {
1014
1271
  function isHydrationEnd(node) {
1015
1272
  return node.data.trim() === '/rmx:h';
1016
1273
  }
1017
- function isHydratedVirtualRootMarker(node) {
1018
- return node instanceof Comment && '$rmx' in node;
1019
- }
1020
1274
  function isFrameStart(node) {
1021
- return node instanceof Comment && node.data.trim().startsWith('rmx:f:');
1275
+ return isCommentNode(node) && node.data.trim().startsWith('rmx:f:');
1022
1276
  }
1023
1277
  function isFrameEnd(node) {
1024
1278
  return node.data.trim() === '/rmx:f';
@@ -1028,11 +1282,15 @@ function getFrameId(start) {
1028
1282
  invariant(trimmed.startsWith('rmx:f:'), 'Invalid frame start marker');
1029
1283
  return trimmed.slice('rmx:f:'.length);
1030
1284
  }
1285
+ function findMarkerRangeEndIndex(nodes, end, startIndex) {
1286
+ // The snapshot may not contain an end marker moved by a DOM update.
1287
+ return Math.max(startIndex, nodes.indexOf(end));
1288
+ }
1031
1289
  function findEndMarker(start, isStart, isEnd) {
1032
1290
  let node = start.nextSibling;
1033
1291
  let depth = 1;
1034
1292
  while (node) {
1035
- if (node.nodeType === Node.COMMENT_NODE) {
1293
+ if (isCommentNode(node)) {
1036
1294
  let comment = node;
1037
1295
  if (isStart(comment))
1038
1296
  depth++;
@@ -1046,4 +1304,15 @@ function findEndMarker(start, isStart, isEnd) {
1046
1304
  }
1047
1305
  throw new Error('End marker not found');
1048
1306
  }
1307
+ function isCommentNode(node) {
1308
+ return node?.nodeType === Node.COMMENT_NODE;
1309
+ }
1310
+ function isDocumentNode(node) {
1311
+ return node.nodeType === Node.DOCUMENT_NODE;
1312
+ }
1313
+ function isDocumentFragmentNode(value) {
1314
+ return (typeof value === 'object' &&
1315
+ value !== null &&
1316
+ Reflect.get(value, 'nodeType') === Node.DOCUMENT_FRAGMENT_NODE);
1317
+ }
1049
1318
  //# sourceMappingURL=frame.js.map