@reckona/mreact-server 0.0.181 → 0.0.183

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/reorder.js CHANGED
@@ -1,17 +1,31 @@
1
1
  /** Moves out-of-order fragments into their matching placeholders within a DOM root. */
2
2
  export function applyOutOfOrderFragments(root = document) {
3
3
  const fragments = Array.from(root.querySelectorAll("template[data-mreact-oob-fragment]"));
4
+ const completionMarkers = new Map();
5
+ for (const marker of root.querySelectorAll("[data-mreact-oob-complete]")) {
6
+ const id = marker.getAttribute("data-mreact-oob-complete");
7
+ if (!completionMarkers.has(id)) {
8
+ completionMarkers.set(id, marker);
9
+ }
10
+ }
11
+ const placeholders = new Map();
12
+ for (const placeholder of root.querySelectorAll("[data-mreact-oob-placeholder]")) {
13
+ const id = placeholder.getAttribute("data-mreact-oob-placeholder");
14
+ if (!placeholders.has(id)) {
15
+ placeholders.set(id, placeholder);
16
+ }
17
+ }
4
18
  for (const fragment of fragments) {
5
19
  const id = fragment.dataset.mreactOobFragment;
6
20
  if (id === undefined) {
7
21
  continue;
8
22
  }
9
- const completionMarker = Array.from(root.querySelectorAll("[data-mreact-oob-complete]")).find((candidate) => candidate.getAttribute("data-mreact-oob-complete") === id);
23
+ const completionMarker = completionMarkers.get(id);
10
24
  if (completionMarker === undefined) {
11
25
  continue;
12
26
  }
13
- const placeholder = root.querySelector(`[data-mreact-oob-placeholder="${cssEscape(id)}"]`);
14
- if (placeholder === null) {
27
+ const placeholder = placeholders.get(id);
28
+ if (placeholder === undefined) {
15
29
  continue;
16
30
  }
17
31
  placeholder.replaceWith(fragment.content.cloneNode(true));
@@ -19,7 +33,4 @@ export function applyOutOfOrderFragments(root = document) {
19
33
  completionMarker.remove();
20
34
  }
21
35
  }
22
- function cssEscape(value) {
23
- return value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"");
24
- }
25
36
  //# sourceMappingURL=reorder.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"reorder.js","sourceRoot":"","sources":["../src/reorder.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,MAAM,UAAU,wBAAwB,CAAC,OAAmB,QAAQ;IAClE,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAC1B,IAAI,CAAC,gBAAgB,CACnB,oCAAoC,CACrC,CACF,CAAC;IAEF,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,iBAAiB,CAAC;QAE9C,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;YACrB,SAAS;QACX,CAAC;QAED,MAAM,gBAAgB,GAAG,KAAK,CAAC,IAAI,CACjC,IAAI,CAAC,gBAAgB,CAAU,4BAA4B,CAAC,CAC7D,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,YAAY,CAAC,0BAA0B,CAAC,KAAK,EAAE,CAAC,CAAC;QAEjF,IAAI,gBAAgB,KAAK,SAAS,EAAE,CAAC;YACnC,SAAS;QACX,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,CACpC,iCAAiC,SAAS,CAAC,EAAE,CAAC,IAAI,CACnD,CAAC;QAEF,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,SAAS;QACX,CAAC;QAED,WAAW,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1D,QAAQ,CAAC,MAAM,EAAE,CAAC;QAClB,gBAAgB,CAAC,MAAM,EAAE,CAAC;IAC5B,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACjE,CAAC","sourcesContent":["/** Moves out-of-order fragments into their matching placeholders within a DOM root. */\nexport function applyOutOfOrderFragments(root: ParentNode = document): void {\n const fragments = Array.from(\n root.querySelectorAll<HTMLTemplateElement>(\n \"template[data-mreact-oob-fragment]\",\n ),\n );\n\n for (const fragment of fragments) {\n const id = fragment.dataset.mreactOobFragment;\n\n if (id === undefined) {\n continue;\n }\n\n const completionMarker = Array.from(\n root.querySelectorAll<Element>(\"[data-mreact-oob-complete]\"),\n ).find((candidate) => candidate.getAttribute(\"data-mreact-oob-complete\") === id);\n\n if (completionMarker === undefined) {\n continue;\n }\n\n const placeholder = root.querySelector<Element>(\n `[data-mreact-oob-placeholder=\"${cssEscape(id)}\"]`,\n );\n\n if (placeholder === null) {\n continue;\n }\n\n placeholder.replaceWith(fragment.content.cloneNode(true));\n fragment.remove();\n completionMarker.remove();\n }\n}\n\nfunction cssEscape(value: string): string {\n return value.replaceAll(\"\\\\\", \"\\\\\\\\\").replaceAll(\"\\\"\", \"\\\\\\\"\");\n}\n"]}
1
+ {"version":3,"file":"reorder.js","sourceRoot":"","sources":["../src/reorder.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,MAAM,UAAU,wBAAwB,CAAC,OAAmB,QAAQ;IAClE,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAC1B,IAAI,CAAC,gBAAgB,CACnB,oCAAoC,CACrC,CACF,CAAC;IACF,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAA0B,CAAC;IAC5D,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,gBAAgB,CAAU,4BAA4B,CAAC,EAAE,CAAC;QAClF,MAAM,EAAE,GAAG,MAAM,CAAC,YAAY,CAAC,0BAA0B,CAAC,CAAC;QAC3D,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YAC/B,iBAAiB,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IACD,MAAM,YAAY,GAAG,IAAI,GAAG,EAA0B,CAAC;IACvD,KAAK,MAAM,WAAW,IAAI,IAAI,CAAC,gBAAgB,CAAU,+BAA+B,CAAC,EAAE,CAAC;QAC1F,MAAM,EAAE,GAAG,WAAW,CAAC,YAAY,CAAC,6BAA6B,CAAC,CAAC;QACnE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YAC1B,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,iBAAiB,CAAC;QAE9C,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;YACrB,SAAS;QACX,CAAC;QAED,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnD,IAAI,gBAAgB,KAAK,SAAS,EAAE,CAAC;YACnC,SAAS;QACX,CAAC;QAED,MAAM,WAAW,GAAG,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzC,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC9B,SAAS;QACX,CAAC;QAED,WAAW,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1D,QAAQ,CAAC,MAAM,EAAE,CAAC;QAClB,gBAAgB,CAAC,MAAM,EAAE,CAAC;IAC5B,CAAC;AACH,CAAC","sourcesContent":["/** Moves out-of-order fragments into their matching placeholders within a DOM root. */\nexport function applyOutOfOrderFragments(root: ParentNode = document): void {\n const fragments = Array.from(\n root.querySelectorAll<HTMLTemplateElement>(\n \"template[data-mreact-oob-fragment]\",\n ),\n );\n const completionMarkers = new Map<string | null, Element>();\n for (const marker of root.querySelectorAll<Element>(\"[data-mreact-oob-complete]\")) {\n const id = marker.getAttribute(\"data-mreact-oob-complete\");\n if (!completionMarkers.has(id)) {\n completionMarkers.set(id, marker);\n }\n }\n const placeholders = new Map<string | null, Element>();\n for (const placeholder of root.querySelectorAll<Element>(\"[data-mreact-oob-placeholder]\")) {\n const id = placeholder.getAttribute(\"data-mreact-oob-placeholder\");\n if (!placeholders.has(id)) {\n placeholders.set(id, placeholder);\n }\n }\n\n for (const fragment of fragments) {\n const id = fragment.dataset.mreactOobFragment;\n\n if (id === undefined) {\n continue;\n }\n\n const completionMarker = completionMarkers.get(id);\n if (completionMarker === undefined) {\n continue;\n }\n\n const placeholder = placeholders.get(id);\n if (placeholder === undefined) {\n continue;\n }\n\n placeholder.replaceWith(fragment.content.cloneNode(true));\n fragment.remove();\n completionMarker.remove();\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reckona/mreact-server",
3
- "version": "0.0.181",
3
+ "version": "0.0.183",
4
4
  "description": "Server rendering, streaming, hydration, and Flight primitives for mreact.",
5
5
  "keywords": [
6
6
  "flight",
@@ -61,7 +61,7 @@
61
61
  "access": "public"
62
62
  },
63
63
  "dependencies": {
64
- "@reckona/mreact-shared": "0.0.181",
65
- "@reckona/mreact-compat": "0.0.181"
64
+ "@reckona/mreact-compat": "0.0.183",
65
+ "@reckona/mreact-shared": "0.0.183"
66
66
  }
67
67
  }
package/src/boundary.ts CHANGED
@@ -472,7 +472,7 @@ function isPromiseLike(value: unknown): value is PromiseLike<void> {
472
472
  return isPromiseLikeUnknown(value);
473
473
  }
474
474
 
475
- const outOfOrderReorderScript = `(()=>{function apply(root){const fragments=Array.from(root.querySelectorAll("template[data-mreact-oob-fragment]"));for(const fragment of fragments){const id=fragment.getAttribute("data-mreact-oob-fragment");if(id===null)continue;const markers=Array.from(root.querySelectorAll("[data-mreact-oob-complete]"));const marker=markers.find((candidate)=>candidate.getAttribute("data-mreact-oob-complete")===id);if(marker===undefined)continue;const placeholders=Array.from(root.querySelectorAll("[data-mreact-oob-placeholder]"));const placeholder=placeholders.find((candidate)=>candidate.getAttribute("data-mreact-oob-placeholder")===id);if(placeholder===undefined)continue;placeholder.replaceWith(fragment.content.cloneNode(true));fragment.remove();marker.remove();}}apply(document);new MutationObserver(()=>apply(document)).observe(document.documentElement,{childList:true,subtree:true});})();`;
475
+ const outOfOrderReorderScript = `(()=>{function apply(root){const fragments=Array.from(root.querySelectorAll("template[data-mreact-oob-fragment]"));const markers=new Map();for(const marker of root.querySelectorAll("[data-mreact-oob-complete]")){const id=marker.getAttribute("data-mreact-oob-complete");if(!markers.has(id))markers.set(id,marker);}const placeholders=new Map();for(const placeholder of root.querySelectorAll("[data-mreact-oob-placeholder]")){const id=placeholder.getAttribute("data-mreact-oob-placeholder");if(!placeholders.has(id))placeholders.set(id,placeholder);}for(const fragment of fragments){const id=fragment.getAttribute("data-mreact-oob-fragment");if(id===null)continue;const marker=markers.get(id);if(marker===undefined)continue;const placeholder=placeholders.get(id);if(placeholder===undefined)continue;placeholder.replaceWith(fragment.content.cloneNode(true));fragment.remove();marker.remove();}}apply(document);new MutationObserver(()=>apply(document)).observe(document.documentElement,{childList:true,subtree:true});})();`;
476
476
 
477
477
  const reactSuspenseRevealScriptBody = `(self.$RC=self.$RC||function(bid,sid){var b=document.getElementById(bid);var s=document.getElementById(sid);if(!b||!s)return;var p=b.parentNode;var e=b.nextSibling;var d=0;var r=[];for(var n=e;n;n=n.nextSibling){if(n.nodeType===8){if(n.data==="$"||n.data==="$?"||n.data==="$!")d++;else if(n.data==="/$"){if(d===0){e=n;break;}d--;}}r.push(n);}for(var i=0;r[i];i++)p.removeChild(r[i]);while(s.firstChild)p.insertBefore(s.firstChild,e);s.remove();b.data="$";})`;
478
478
 
package/src/flight.ts CHANGED
@@ -1704,202 +1704,228 @@ function parseReactFlightId(value: string): number {
1704
1704
  return Number.parseInt(value, 16);
1705
1705
  }
1706
1706
 
1707
- async function serializeFlightValue(
1707
+ type FlightSerializationResult<T extends FlightModel = FlightModel> = T | Promise<T>;
1708
+
1709
+ function serializeFlightValue(
1708
1710
  value: unknown,
1709
1711
  state: FlightSerializationState,
1710
1712
  depth: number,
1711
- ): Promise<FlightModel> {
1713
+ ): FlightSerializationResult {
1712
1714
  if (depth > MAX_FLIGHT_DECODE_DEPTH) flightTooDeep();
1713
1715
 
1714
- const awaited = await value;
1716
+ if (isThenable(value)) {
1717
+ return Promise.resolve(value).then((awaited) => serializeFlightValue(awaited, state, depth));
1718
+ }
1715
1719
 
1716
- if (awaited === null) {
1720
+ if (value === null) {
1717
1721
  return null;
1718
1722
  }
1719
1723
 
1720
- if (awaited === undefined) {
1724
+ if (value === undefined) {
1721
1725
  return { kind: "undefined" };
1722
1726
  }
1723
1727
 
1724
- if (typeof awaited === "string" || typeof awaited === "boolean") {
1725
- return awaited;
1728
+ if (typeof value === "string" || typeof value === "boolean") {
1729
+ return value;
1726
1730
  }
1727
1731
 
1728
- if (typeof awaited === "number") {
1729
- if (Number.isNaN(awaited)) {
1732
+ if (typeof value === "number") {
1733
+ if (Number.isNaN(value)) {
1730
1734
  return { kind: "number", value: "NaN" };
1731
1735
  }
1732
1736
 
1733
- if (awaited === Infinity) {
1737
+ if (value === Infinity) {
1734
1738
  return { kind: "number", value: "Infinity" };
1735
1739
  }
1736
1740
 
1737
- if (awaited === -Infinity) {
1741
+ if (value === -Infinity) {
1738
1742
  return { kind: "number", value: "-Infinity" };
1739
1743
  }
1740
1744
 
1741
- if (Object.is(awaited, -0)) {
1745
+ if (Object.is(value, -0)) {
1742
1746
  return { kind: "number", value: "-0" };
1743
1747
  }
1744
1748
 
1745
- return awaited;
1749
+ return value;
1746
1750
  }
1747
1751
 
1748
- if (typeof awaited === "bigint") {
1749
- return { kind: "bigint", value: awaited.toString() };
1752
+ if (typeof value === "bigint") {
1753
+ return { kind: "bigint", value: value.toString() };
1750
1754
  }
1751
1755
 
1752
- if (typeof awaited === "symbol") {
1753
- return { kind: "symbol", name: Symbol.keyFor(awaited) ?? awaited.description ?? "" };
1756
+ if (typeof value === "symbol") {
1757
+ return { kind: "symbol", name: Symbol.keyFor(value) ?? value.description ?? "" };
1754
1758
  }
1755
1759
 
1756
- if (Array.isArray(awaited)) {
1757
- return await Promise.all(awaited.map((item) => serializeFlightValue(item, state, depth + 1)));
1760
+ if (Array.isArray(value)) {
1761
+ return resolveFlightArray(value.map((item) => serializeFlightValue(item, state, depth + 1)));
1758
1762
  }
1759
1763
 
1760
- if (isServerReference(awaited)) {
1761
- return {
1764
+ if (isServerReference(value)) {
1765
+ const id = getServerReferenceId(value, state);
1766
+ return resolveFlightResult(id, (resolvedId) => ({
1762
1767
  kind: "server-reference",
1763
- id: await getServerReferenceId(awaited, state),
1764
- };
1768
+ id: resolvedId,
1769
+ }));
1765
1770
  }
1766
1771
 
1767
- if (isReactCompatElement(awaited)) {
1768
- return await serializeElement(awaited, state, depth + 1);
1772
+ if (isReactCompatElement(value)) {
1773
+ return serializeElement(value, state, depth + 1);
1769
1774
  }
1770
1775
 
1771
- if (awaited instanceof Date) {
1772
- return { kind: "date", value: awaited.toJSON() };
1776
+ if (value instanceof Date) {
1777
+ return { kind: "date", value: value.toJSON() };
1773
1778
  }
1774
1779
 
1775
- if (awaited instanceof Map) {
1776
- return {
1777
- kind: "map",
1778
- entries: await Promise.all(
1779
- Array.from(awaited.entries()).map(
1780
- async ([key, value]) =>
1781
- [
1782
- await serializeFlightValue(key, state, depth + 1),
1783
- await serializeFlightValue(value, state, depth + 1),
1784
- ] as [FlightModel, FlightModel],
1780
+ if (value instanceof Map) {
1781
+ const entries = resolveFlightArray(
1782
+ Array.from(value.entries()).map(([key, entryValue]) =>
1783
+ resolveFlightTuple(
1784
+ serializeFlightValue(key, state, depth + 1),
1785
+ serializeFlightValue(entryValue, state, depth + 1),
1785
1786
  ),
1786
1787
  ),
1787
- };
1788
+ );
1789
+ return resolveFlightResult(entries, (resolvedEntries) => ({
1790
+ kind: "map",
1791
+ entries: resolvedEntries,
1792
+ }));
1788
1793
  }
1789
1794
 
1790
- if (awaited instanceof Set) {
1791
- return {
1792
- kind: "set",
1793
- values: await Promise.all(
1794
- Array.from(awaited.values()).map((value) => serializeFlightValue(value, state, depth + 1)),
1795
+ if (value instanceof Set) {
1796
+ const values = resolveFlightArray(
1797
+ Array.from(value.values()).map((entryValue) =>
1798
+ serializeFlightValue(entryValue, state, depth + 1),
1795
1799
  ),
1796
- };
1800
+ );
1801
+ return resolveFlightResult(values, (resolvedValues) => ({
1802
+ kind: "set",
1803
+ values: resolvedValues,
1804
+ }));
1797
1805
  }
1798
1806
 
1799
- if (isFormDataLike(awaited)) {
1800
- return {
1801
- kind: "form-data",
1802
- entries: await Promise.all(
1803
- Array.from(awaited.entries()).map(
1804
- async ([key, value]) =>
1805
- [key, await serializeFlightValue(value, state, depth + 1)] as [string, FlightModel],
1806
- ),
1807
+ if (isFormDataLike(value)) {
1808
+ const entries = resolveFlightArray(
1809
+ Array.from(value.entries()).map(([key, entryValue]) =>
1810
+ resolveFlightResult(serializeFlightValue(entryValue, state, depth + 1), (resolvedValue) => [
1811
+ key,
1812
+ resolvedValue,
1813
+ ] as [string, FlightModel]),
1807
1814
  ),
1808
- };
1815
+ );
1816
+ return resolveFlightResult(entries, (resolvedEntries) => ({
1817
+ kind: "form-data",
1818
+ entries: resolvedEntries,
1819
+ }));
1809
1820
  }
1810
1821
 
1811
- if (isIterableObject(awaited)) {
1812
- return {
1822
+ if (isIterableObject(value)) {
1823
+ const values = resolveFlightArray(
1824
+ Array.from(value).map((entryValue) => serializeFlightValue(entryValue, state, depth + 1)),
1825
+ );
1826
+ return resolveFlightResult(values, (resolvedValues) => ({
1813
1827
  kind: "iterable",
1814
- values: await Promise.all(
1815
- Array.from(awaited).map((value) => serializeFlightValue(value, state, depth + 1)),
1816
- ),
1817
- };
1828
+ values: resolvedValues,
1829
+ }));
1818
1830
  }
1819
1831
 
1820
- if (awaited instanceof Error) {
1832
+ if (value instanceof Error) {
1821
1833
  return {
1822
1834
  kind: "error",
1823
- name: awaited.name,
1824
- message: awaited.message,
1835
+ name: value.name,
1836
+ message: value.message,
1825
1837
  };
1826
1838
  }
1827
1839
 
1828
- if (typeof awaited === "object") {
1829
- return await serializeObject(awaited as Record<string, unknown>, state, depth + 1);
1840
+ if (typeof value === "object") {
1841
+ return serializeObject(value as Record<string, unknown>, state, depth + 1);
1830
1842
  }
1831
1843
 
1832
- throw new TypeError(`Unsupported Flight value: ${typeof awaited}`);
1844
+ throw new TypeError(`Unsupported Flight value: ${typeof value}`);
1833
1845
  }
1834
1846
 
1835
- async function serializeElement(
1847
+ function serializeElement(
1836
1848
  element: ReactCompatElementLike,
1837
1849
  state: FlightSerializationState,
1838
1850
  depth: number,
1839
- ): Promise<FlightElementModel | FlightModel> {
1851
+ ): FlightSerializationResult<FlightElementModel | FlightModel> {
1840
1852
  if (typeof element.type === "function") {
1841
- return await serializeFlightValue(element.type(element.props), state, depth + 1);
1853
+ return serializeFlightValue(element.type(element.props), state, depth + 1);
1842
1854
  }
1843
1855
 
1844
1856
  if (isClientReference(element.type)) {
1845
- return {
1857
+ const elementType = element.type;
1858
+ const props = serializeProps(element.props, state, depth + 1);
1859
+ return resolveFlightResult(props, (resolvedProps) => ({
1846
1860
  kind: "element",
1847
1861
  type: {
1848
1862
  kind: "client-reference",
1849
- id: getClientReferenceId(element.type, state),
1863
+ id: getClientReferenceId(elementType, state),
1850
1864
  },
1851
1865
  key: element.key,
1852
- props: await serializeProps(element.props, state, depth + 1),
1853
- };
1866
+ props: resolvedProps,
1867
+ }));
1854
1868
  }
1855
1869
 
1856
1870
  if (typeof element.type === "string") {
1857
- return {
1871
+ const elementType = element.type;
1872
+ const props = serializeProps(element.props, state, depth + 1);
1873
+ return resolveFlightResult(props, (resolvedProps) => ({
1858
1874
  kind: "element",
1859
- type: element.type,
1875
+ type: elementType,
1860
1876
  key: element.key,
1861
- props: await serializeProps(element.props, state, depth + 1),
1862
- };
1877
+ props: resolvedProps,
1878
+ }));
1863
1879
  }
1864
1880
 
1865
1881
  if (element.type === REACT_COMPAT_FRAGMENT_TYPE) {
1866
- return {
1882
+ const props = serializeProps(element.props, state, depth + 1);
1883
+ return resolveFlightResult(props, (resolvedProps) => ({
1867
1884
  kind: "element",
1868
1885
  type: { kind: "fragment" },
1869
1886
  key: element.key,
1870
- props: await serializeProps(element.props, state, depth + 1),
1871
- };
1887
+ props: resolvedProps,
1888
+ }));
1872
1889
  }
1873
1890
 
1874
1891
  throw new TypeError("Unsupported Flight element type.");
1875
1892
  }
1876
1893
 
1877
- async function serializeProps(
1894
+ function serializeProps(
1878
1895
  props: Record<string, unknown>,
1879
1896
  state: FlightSerializationState,
1880
1897
  depth: number,
1881
- ): Promise<Record<string, FlightModel>> {
1882
- const entries = await Promise.all(
1883
- Object.entries(props).map(
1884
- async ([key, value]) => [key, await serializeFlightValue(value, state, depth + 1)] as const,
1898
+ ): FlightSerializationResult<Record<string, FlightModel>> {
1899
+ const entries = resolveFlightArray(
1900
+ Object.entries(props).map(([key, value]) =>
1901
+ resolveFlightResult(serializeFlightValue(value, state, depth + 1), (resolvedValue) => [
1902
+ key,
1903
+ resolvedValue,
1904
+ ]),
1885
1905
  ),
1886
1906
  );
1887
1907
 
1888
- return Object.fromEntries(entries);
1908
+ return resolveFlightResult(entries, (resolvedEntries) => Object.fromEntries(resolvedEntries));
1889
1909
  }
1890
1910
 
1891
- async function serializeObject(
1911
+ function serializeObject(
1892
1912
  object: Record<string, unknown>,
1893
1913
  state: FlightSerializationState,
1894
1914
  depth: number,
1895
- ): Promise<FlightObjectModel> {
1896
- return Object.fromEntries(
1897
- await Promise.all(
1898
- Object.entries(object).map(
1899
- async ([key, value]) => [key, await serializeFlightValue(value, state, depth + 1)] as const,
1915
+ ): FlightSerializationResult<FlightObjectModel> {
1916
+ const entries = resolveFlightArray(
1917
+ Object.entries(object).map(([key, value]) =>
1918
+ resolveFlightResult(
1919
+ serializeFlightValue(value, state, depth + 1),
1920
+ (resolvedValue) => [key, resolvedValue] as const,
1900
1921
  ),
1901
1922
  ),
1902
- ) as FlightObjectModel;
1923
+ );
1924
+
1925
+ return resolveFlightResult(
1926
+ entries,
1927
+ (resolvedEntries) => Object.fromEntries(resolvedEntries) as FlightObjectModel,
1928
+ );
1903
1929
  }
1904
1930
 
1905
1931
  function getClientReferenceId(reference: ClientReference, state: FlightSerializationState): number {
@@ -1921,14 +1947,24 @@ function getClientReferenceId(reference: ClientReference, state: FlightSerializa
1921
1947
  return id;
1922
1948
  }
1923
1949
 
1924
- async function getServerReferenceId(
1950
+ function getServerReferenceId(
1925
1951
  reference: ServerReference,
1926
1952
  state: FlightSerializationState,
1927
- ): Promise<number> {
1953
+ ): number | Promise<number> {
1928
1954
  const serializedBound =
1929
1955
  reference.bound === undefined
1930
1956
  ? undefined
1931
- : await Promise.all(reference.bound.map((value) => serializeFlightValue(value, state, 0)));
1957
+ : resolveFlightArray(reference.bound.map((value) => serializeFlightValue(value, state, 0)));
1958
+ return resolveFlightResult(serializedBound, (resolvedBound) =>
1959
+ getServerReferenceIdForBound(reference, state, resolvedBound),
1960
+ );
1961
+ }
1962
+
1963
+ function getServerReferenceIdForBound(
1964
+ reference: ServerReference,
1965
+ state: FlightSerializationState,
1966
+ serializedBound: FlightModel[] | undefined,
1967
+ ): number {
1932
1968
  const key = `${reference.moduleId}:${reference.exportName}:${JSON.stringify(serializedBound ?? null)}`;
1933
1969
  const existing = state.serverReferenceIndexes.get(key);
1934
1970
 
@@ -1947,6 +1983,24 @@ async function getServerReferenceId(
1947
1983
  return id;
1948
1984
  }
1949
1985
 
1986
+ function resolveFlightArray<T>(values: Array<T | Promise<T>>): T[] | Promise<T[]> {
1987
+ return values.some(isThenable) ? Promise.all(values) : (values as T[]);
1988
+ }
1989
+
1990
+ function resolveFlightTuple<TLeft, TRight>(
1991
+ left: TLeft | Promise<TLeft>,
1992
+ right: TRight | Promise<TRight>,
1993
+ ): [TLeft, TRight] | Promise<[TLeft, TRight]> {
1994
+ return isThenable(left) || isThenable(right) ? Promise.all([left, right]) : [left, right];
1995
+ }
1996
+
1997
+ function resolveFlightResult<T, TResult>(
1998
+ value: T | Promise<T>,
1999
+ map: (value: T) => TResult,
2000
+ ): TResult | Promise<TResult> {
2001
+ return isThenable(value) ? value.then(map) : map(value);
2002
+ }
2003
+
1950
2004
  function isReactCompatElement(value: unknown): value is ReactCompatElementLike {
1951
2005
  return (
1952
2006
  typeof value === "object" &&
@@ -1955,6 +2009,14 @@ function isReactCompatElement(value: unknown): value is ReactCompatElementLike {
1955
2009
  );
1956
2010
  }
1957
2011
 
2012
+ function isThenable(value: unknown): value is PromiseLike<unknown> {
2013
+ return (
2014
+ value !== null &&
2015
+ (typeof value === "object" || typeof value === "function") &&
2016
+ typeof (value as { then?: unknown }).then === "function"
2017
+ );
2018
+ }
2019
+
1958
2020
  function isFormDataLike(value: unknown): value is FormData {
1959
2021
  return typeof FormData !== "undefined" && value instanceof FormData;
1960
2022
  }
package/src/reorder.ts CHANGED
@@ -5,6 +5,20 @@ export function applyOutOfOrderFragments(root: ParentNode = document): void {
5
5
  "template[data-mreact-oob-fragment]",
6
6
  ),
7
7
  );
8
+ const completionMarkers = new Map<string | null, Element>();
9
+ for (const marker of root.querySelectorAll<Element>("[data-mreact-oob-complete]")) {
10
+ const id = marker.getAttribute("data-mreact-oob-complete");
11
+ if (!completionMarkers.has(id)) {
12
+ completionMarkers.set(id, marker);
13
+ }
14
+ }
15
+ const placeholders = new Map<string | null, Element>();
16
+ for (const placeholder of root.querySelectorAll<Element>("[data-mreact-oob-placeholder]")) {
17
+ const id = placeholder.getAttribute("data-mreact-oob-placeholder");
18
+ if (!placeholders.has(id)) {
19
+ placeholders.set(id, placeholder);
20
+ }
21
+ }
8
22
 
9
23
  for (const fragment of fragments) {
10
24
  const id = fragment.dataset.mreactOobFragment;
@@ -13,19 +27,13 @@ export function applyOutOfOrderFragments(root: ParentNode = document): void {
13
27
  continue;
14
28
  }
15
29
 
16
- const completionMarker = Array.from(
17
- root.querySelectorAll<Element>("[data-mreact-oob-complete]"),
18
- ).find((candidate) => candidate.getAttribute("data-mreact-oob-complete") === id);
19
-
30
+ const completionMarker = completionMarkers.get(id);
20
31
  if (completionMarker === undefined) {
21
32
  continue;
22
33
  }
23
34
 
24
- const placeholder = root.querySelector<Element>(
25
- `[data-mreact-oob-placeholder="${cssEscape(id)}"]`,
26
- );
27
-
28
- if (placeholder === null) {
35
+ const placeholder = placeholders.get(id);
36
+ if (placeholder === undefined) {
29
37
  continue;
30
38
  }
31
39
 
@@ -34,7 +42,3 @@ export function applyOutOfOrderFragments(root: ParentNode = document): void {
34
42
  completionMarker.remove();
35
43
  }
36
44
  }
37
-
38
- function cssEscape(value: string): string {
39
- return value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"");
40
- }