react-server-dom-webpack 19.0.0-canary-95e6f032c-20240401 → 19.0.0-canary-7a2609eed-20240403

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 (37) hide show
  1. package/cjs/react-server-dom-webpack-client.browser.development.js +20 -6
  2. package/cjs/react-server-dom-webpack-client.browser.production.js +7 -1
  3. package/cjs/react-server-dom-webpack-client.browser.production.min.js +14 -14
  4. package/cjs/react-server-dom-webpack-client.browser.production.min.js.map +1 -1
  5. package/cjs/react-server-dom-webpack-client.edge.development.js +20 -6
  6. package/cjs/react-server-dom-webpack-client.edge.production.js +7 -1
  7. package/cjs/react-server-dom-webpack-client.edge.production.min.js +1 -1
  8. package/cjs/react-server-dom-webpack-client.edge.production.min.js.map +1 -1
  9. package/cjs/react-server-dom-webpack-client.node.development.js +20 -6
  10. package/cjs/react-server-dom-webpack-client.node.production.js +7 -1
  11. package/cjs/react-server-dom-webpack-client.node.production.min.js +1 -1
  12. package/cjs/react-server-dom-webpack-client.node.production.min.js.map +1 -1
  13. package/cjs/react-server-dom-webpack-client.node.unbundled.development.js +20 -6
  14. package/cjs/react-server-dom-webpack-client.node.unbundled.production.js +7 -1
  15. package/cjs/react-server-dom-webpack-client.node.unbundled.production.min.js +1 -1
  16. package/cjs/react-server-dom-webpack-client.node.unbundled.production.min.js.map +1 -1
  17. package/cjs/react-server-dom-webpack-server.browser.development.js +90 -37
  18. package/cjs/react-server-dom-webpack-server.browser.production.js +88 -38
  19. package/cjs/react-server-dom-webpack-server.browser.production.min.js +46 -45
  20. package/cjs/react-server-dom-webpack-server.browser.production.min.js.map +1 -1
  21. package/cjs/react-server-dom-webpack-server.edge.development.js +90 -37
  22. package/cjs/react-server-dom-webpack-server.edge.production.js +88 -38
  23. package/cjs/react-server-dom-webpack-server.edge.production.min.js +32 -31
  24. package/cjs/react-server-dom-webpack-server.edge.production.min.js.map +1 -1
  25. package/cjs/react-server-dom-webpack-server.node.development.js +90 -37
  26. package/cjs/react-server-dom-webpack-server.node.production.js +88 -38
  27. package/cjs/react-server-dom-webpack-server.node.production.min.js +33 -31
  28. package/cjs/react-server-dom-webpack-server.node.production.min.js.map +1 -1
  29. package/cjs/react-server-dom-webpack-server.node.unbundled.development.js +90 -37
  30. package/cjs/react-server-dom-webpack-server.node.unbundled.production.js +88 -38
  31. package/cjs/react-server-dom-webpack-server.node.unbundled.production.min.js +33 -31
  32. package/cjs/react-server-dom-webpack-server.node.unbundled.production.min.js.map +1 -1
  33. package/package.json +3 -3
  34. package/umd/react-server-dom-webpack-client.browser.development.js +20 -6
  35. package/umd/react-server-dom-webpack-client.browser.production.min.js +15 -15
  36. package/umd/react-server-dom-webpack-server.browser.development.js +90 -37
  37. package/umd/react-server-dom-webpack-server.browser.production.min.js +52 -51
@@ -1034,10 +1034,6 @@ function use(usable) {
1034
1034
  }
1035
1035
  }
1036
1036
 
1037
- function createSignal() {
1038
- return new AbortController().signal;
1039
- }
1040
-
1041
1037
  function resolveCache() {
1042
1038
  var request = resolveRequest();
1043
1039
 
@@ -1049,17 +1045,6 @@ function resolveCache() {
1049
1045
  }
1050
1046
 
1051
1047
  var DefaultCacheDispatcher = {
1052
- getCacheSignal: function () {
1053
- var cache = resolveCache();
1054
- var entry = cache.get(createSignal);
1055
-
1056
- if (entry === undefined) {
1057
- entry = createSignal();
1058
- cache.set(createSignal, entry);
1059
- }
1060
-
1061
- return entry;
1062
- },
1063
1048
  getCacheForType: function (resourceType) {
1064
1049
  var cache = resolveCache();
1065
1050
  var entry = cache.get(resourceType);
@@ -1422,11 +1407,15 @@ if (!ReactSharedServerInternals) {
1422
1407
  var ObjectPrototype = Object.prototype;
1423
1408
  var stringify = JSON.stringify; // Serializable values
1424
1409
  // Thenable<ReactClientValue>
1410
+ // task status
1425
1411
 
1426
1412
  var PENDING$1 = 0;
1427
1413
  var COMPLETED = 1;
1428
1414
  var ABORTED = 3;
1429
- var ERRORED$1 = 4;
1415
+ var ERRORED$1 = 4; // object reference status
1416
+
1417
+ var SEEN_BUT_NOT_YET_OUTLINED = -1;
1418
+ var NEVER_OUTLINED = -2;
1430
1419
  var ReactCurrentCache = ReactSharedServerInternals.ReactCurrentCache;
1431
1420
  var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
1432
1421
 
@@ -1732,15 +1721,68 @@ function renderFragment(request, task, children) {
1732
1721
  }
1733
1722
  }
1734
1723
 
1735
- {
1736
- return children;
1737
- }
1724
+ if (task.keyPath !== null) {
1725
+ // We have a Server Component that specifies a key but we're now splitting
1726
+ // the tree using a fragment.
1727
+ var fragment = [REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE, task.keyPath, {
1728
+ children: children
1729
+ }];
1730
+
1731
+ if (!task.implicitSlot) {
1732
+ // If this was keyed inside a set. I.e. the outer Server Component was keyed
1733
+ // then we need to handle reorders of the whole set. To do this we need to wrap
1734
+ // this array in a keyed Fragment.
1735
+ return fragment;
1736
+ } // If the outer Server Component was implicit but then an inner one had a key
1737
+ // we don't actually need to be able to move the whole set around. It'll always be
1738
+ // in an implicit slot. The key only exists to be able to reset the state of the
1739
+ // children. We could achieve the same effect by passing on the keyPath to the next
1740
+ // set of components inside the fragment. This would also allow a keyless fragment
1741
+ // reconcile against a single child.
1742
+ // Unfortunately because of JSON.stringify, we can't call the recursive loop for
1743
+ // each child within this context because we can't return a set with already resolved
1744
+ // values. E.g. a string would get double encoded. Returning would pop the context.
1745
+ // So instead, we wrap it with an unkeyed fragment and inner keyed fragment.
1746
+
1747
+
1748
+ return [fragment];
1749
+ } // Since we're yielding here, that implicitly resets the keyPath context on the
1750
+ // way up. Which is what we want since we've consumed it. If this changes to
1751
+ // be recursive serialization, we need to reset the keyPath and implicitSlot,
1752
+ // before recursing here.
1753
+
1754
+
1755
+ return children;
1738
1756
  }
1739
1757
 
1740
1758
  function renderClientElement(task, type, key, props) {
1741
- {
1742
- return [REACT_ELEMENT_TYPE, type, key, props];
1743
- } // We prepend the terminal client element that actually gets serialized with
1759
+ // the keys of any Server Components which are not serialized.
1760
+
1761
+
1762
+ var keyPath = task.keyPath;
1763
+
1764
+ if (key === null) {
1765
+ key = keyPath;
1766
+ } else if (keyPath !== null) {
1767
+ key = keyPath + ',' + key;
1768
+ }
1769
+
1770
+ var element = [REACT_ELEMENT_TYPE, type, key, props];
1771
+
1772
+ if (task.implicitSlot && key !== null) {
1773
+ // The root Server Component had no key so it was in an implicit slot.
1774
+ // If we had a key lower, it would end up in that slot with an explicit key.
1775
+ // We wrap the element in a fragment to give it an implicit key slot with
1776
+ // an inner explicit key.
1777
+ return [element];
1778
+ } // Since we're yielding here, that implicitly resets the keyPath context on the
1779
+ // way up. Which is what we want since we've consumed it. If this changes to
1780
+ // be recursive serialization, we need to reset the keyPath and implicitSlot,
1781
+ // before recursing here. We also need to reset it once we render into an array
1782
+ // or anything else too which we also get implicitly.
1783
+
1784
+
1785
+ return element;
1744
1786
  } // The chunk ID we're currently rendering that we can assign debug data to.
1745
1787
 
1746
1788
 
@@ -1859,7 +1901,7 @@ function createTask(request, model, keyPath, implicitSlot, abortSet) {
1859
1901
  if (typeof model === 'object' && model !== null) {
1860
1902
  // If we're about to write this into a new task we can assign it an ID early so that
1861
1903
  // any other references can refer to the value we're about to write.
1862
- {
1904
+ if ((keyPath !== null || implicitSlot)) ; else {
1863
1905
  request.writtenObjects.set(model, id);
1864
1906
  }
1865
1907
  }
@@ -2066,8 +2108,7 @@ function serializeMap(request, map) {
2066
2108
  var existingId = writtenObjects.get(key);
2067
2109
 
2068
2110
  if (existingId === undefined) {
2069
- // Mark all object keys as seen so that they're always outlined.
2070
- writtenObjects.set(key, -1);
2111
+ writtenObjects.set(key, SEEN_BUT_NOT_YET_OUTLINED);
2071
2112
  }
2072
2113
  }
2073
2114
  }
@@ -2087,8 +2128,7 @@ function serializeSet(request, set) {
2087
2128
  var existingId = writtenObjects.get(key);
2088
2129
 
2089
2130
  if (existingId === undefined) {
2090
- // Mark all object keys as seen so that they're always outlined.
2091
- writtenObjects.set(key, -1);
2131
+ writtenObjects.set(key, SEEN_BUT_NOT_YET_OUTLINED);
2092
2132
  }
2093
2133
  }
2094
2134
  }
@@ -2192,12 +2232,11 @@ function renderModelDestructive(request, task, parent, parentPropertyName, value
2192
2232
  var _existingId = _writtenObjects.get(value);
2193
2233
 
2194
2234
  if (_existingId !== undefined) {
2195
- if (modelRoot === value) {
2235
+ if ((task.keyPath !== null || task.implicitSlot)) ; else if (modelRoot === value) {
2196
2236
  // This is the ID we're currently emitting so we need to write it
2197
2237
  // once but if we discover it again, we refer to it by id.
2198
2238
  modelRoot = null;
2199
- } else if (_existingId === -1) {
2200
- // Seen but not yet outlined.
2239
+ } else if (_existingId === SEEN_BUT_NOT_YET_OUTLINED) {
2201
2240
  // TODO: If we throw here we can treat this as suspending which causes an outline
2202
2241
  // but that is able to reuse the same task if we're already in one but then that
2203
2242
  // will be a lazy future value rather than guaranteed to exist but maybe that's good.
@@ -2216,7 +2255,11 @@ function renderModelDestructive(request, task, parent, parentPropertyName, value
2216
2255
  } else {
2217
2256
  // This is the first time we've seen this object. We may never see it again
2218
2257
  // so we'll inline it. Mark it as seen. If we see it again, we'll outline.
2219
- _writtenObjects.set(value, -1);
2258
+ _writtenObjects.set(value, SEEN_BUT_NOT_YET_OUTLINED); // The element's props are marked as "never outlined" so that they are inlined into
2259
+ // the same row as the element itself.
2260
+
2261
+
2262
+ _writtenObjects.set(value.props, NEVER_OUTLINED);
2220
2263
  }
2221
2264
 
2222
2265
  var element = value;
@@ -2241,7 +2284,11 @@ function renderModelDestructive(request, task, parent, parentPropertyName, value
2241
2284
  var ref;
2242
2285
 
2243
2286
  {
2244
- ref = element.ref;
2287
+ // TODO: This is a temporary, intermediate step. Once the feature
2288
+ // flag is removed, we should get the ref off the props object right
2289
+ // before using it.
2290
+ var refProp = props.ref;
2291
+ ref = refProp !== undefined ? refProp : null;
2245
2292
  } // Attempt to render the Server Component.
2246
2293
 
2247
2294
 
@@ -2290,7 +2337,14 @@ function renderModelDestructive(request, task, parent, parentPropertyName, value
2290
2337
 
2291
2338
  if (typeof value.then === 'function') {
2292
2339
  if (existingId !== undefined) {
2293
- if (modelRoot === value) {
2340
+ if ((task.keyPath !== null || task.implicitSlot)) {
2341
+ // If we're in some kind of context we can't reuse the result of this render or
2342
+ // previous renders of this element. We only reuse Promises if they're not wrapped
2343
+ // by another Server Component.
2344
+ var _promiseId = serializeThenable(request, task, value);
2345
+
2346
+ return serializePromiseID(_promiseId);
2347
+ } else if (modelRoot === value) {
2294
2348
  // This is the ID we're currently emitting so we need to write it
2295
2349
  // once but if we discover it again, we refer to it by id.
2296
2350
  modelRoot = null;
@@ -2312,12 +2366,11 @@ function renderModelDestructive(request, task, parent, parentPropertyName, value
2312
2366
  // This is the ID we're currently emitting so we need to write it
2313
2367
  // once but if we discover it again, we refer to it by id.
2314
2368
  modelRoot = null;
2315
- } else if (existingId === -1) {
2316
- // Seen but not yet outlined.
2369
+ } else if (existingId === SEEN_BUT_NOT_YET_OUTLINED) {
2317
2370
  var _newId = outlineModel(request, value);
2318
2371
 
2319
2372
  return serializeByValueID(_newId);
2320
- } else {
2373
+ } else if (existingId !== NEVER_OUTLINED) {
2321
2374
  // We've already emitted this as an outlined object, so we can
2322
2375
  // just refer to that by its existing ID.
2323
2376
  return serializeByValueID(existingId);
@@ -2325,7 +2378,7 @@ function renderModelDestructive(request, task, parent, parentPropertyName, value
2325
2378
  } else {
2326
2379
  // This is the first time we've seen this object. We may never see it again
2327
2380
  // so we'll inline it. Mark it as seen. If we see it again, we'll outline.
2328
- writtenObjects.set(value, -1);
2381
+ writtenObjects.set(value, SEEN_BUT_NOT_YET_OUTLINED);
2329
2382
  }
2330
2383
 
2331
2384
  if (isArray(value)) {
@@ -992,10 +992,6 @@ function use(usable) {
992
992
  }
993
993
  }
994
994
 
995
- function createSignal() {
996
- return new AbortController().signal;
997
- }
998
-
999
995
  function resolveCache() {
1000
996
  const request = resolveRequest();
1001
997
 
@@ -1007,18 +1003,6 @@ function resolveCache() {
1007
1003
  }
1008
1004
 
1009
1005
  const DefaultCacheDispatcher = {
1010
- getCacheSignal() {
1011
- const cache = resolveCache();
1012
- let entry = cache.get(createSignal);
1013
-
1014
- if (entry === undefined) {
1015
- entry = createSignal();
1016
- cache.set(createSignal, entry);
1017
- }
1018
-
1019
- return entry;
1020
- },
1021
-
1022
1006
  getCacheForType(resourceType) {
1023
1007
  const cache = resolveCache();
1024
1008
  let entry = cache.get(resourceType);
@@ -1251,11 +1235,15 @@ if (!ReactSharedServerInternals) {
1251
1235
  const ObjectPrototype = Object.prototype;
1252
1236
  const stringify = JSON.stringify; // Serializable values
1253
1237
  // Thenable<ReactClientValue>
1238
+ // task status
1254
1239
 
1255
1240
  const PENDING$1 = 0;
1256
1241
  const COMPLETED = 1;
1257
1242
  const ABORTED = 3;
1258
- const ERRORED$1 = 4;
1243
+ const ERRORED$1 = 4; // object reference status
1244
+
1245
+ const SEEN_BUT_NOT_YET_OUTLINED = -1;
1246
+ const NEVER_OUTLINED = -2;
1259
1247
  const ReactCurrentCache = ReactSharedServerInternals.ReactCurrentCache;
1260
1248
  const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
1261
1249
 
@@ -1508,15 +1496,68 @@ function renderFunctionComponent(request, task, key, Component, props) {
1508
1496
 
1509
1497
  function renderFragment(request, task, children) {
1510
1498
 
1511
- {
1512
- return children;
1513
- }
1499
+ if (task.keyPath !== null) {
1500
+ // We have a Server Component that specifies a key but we're now splitting
1501
+ // the tree using a fragment.
1502
+ const fragment = [REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE, task.keyPath, {
1503
+ children
1504
+ }];
1505
+
1506
+ if (!task.implicitSlot) {
1507
+ // If this was keyed inside a set. I.e. the outer Server Component was keyed
1508
+ // then we need to handle reorders of the whole set. To do this we need to wrap
1509
+ // this array in a keyed Fragment.
1510
+ return fragment;
1511
+ } // If the outer Server Component was implicit but then an inner one had a key
1512
+ // we don't actually need to be able to move the whole set around. It'll always be
1513
+ // in an implicit slot. The key only exists to be able to reset the state of the
1514
+ // children. We could achieve the same effect by passing on the keyPath to the next
1515
+ // set of components inside the fragment. This would also allow a keyless fragment
1516
+ // reconcile against a single child.
1517
+ // Unfortunately because of JSON.stringify, we can't call the recursive loop for
1518
+ // each child within this context because we can't return a set with already resolved
1519
+ // values. E.g. a string would get double encoded. Returning would pop the context.
1520
+ // So instead, we wrap it with an unkeyed fragment and inner keyed fragment.
1521
+
1522
+
1523
+ return [fragment];
1524
+ } // Since we're yielding here, that implicitly resets the keyPath context on the
1525
+ // way up. Which is what we want since we've consumed it. If this changes to
1526
+ // be recursive serialization, we need to reset the keyPath and implicitSlot,
1527
+ // before recursing here.
1528
+
1529
+
1530
+ return children;
1514
1531
  }
1515
1532
 
1516
1533
  function renderClientElement(task, type, key, props) {
1517
- {
1518
- return [REACT_ELEMENT_TYPE, type, key, props];
1519
- } // We prepend the terminal client element that actually gets serialized with
1534
+ // the keys of any Server Components which are not serialized.
1535
+
1536
+
1537
+ const keyPath = task.keyPath;
1538
+
1539
+ if (key === null) {
1540
+ key = keyPath;
1541
+ } else if (keyPath !== null) {
1542
+ key = keyPath + ',' + key;
1543
+ }
1544
+
1545
+ const element = [REACT_ELEMENT_TYPE, type, key, props];
1546
+
1547
+ if (task.implicitSlot && key !== null) {
1548
+ // The root Server Component had no key so it was in an implicit slot.
1549
+ // If we had a key lower, it would end up in that slot with an explicit key.
1550
+ // We wrap the element in a fragment to give it an implicit key slot with
1551
+ // an inner explicit key.
1552
+ return [element];
1553
+ } // Since we're yielding here, that implicitly resets the keyPath context on the
1554
+ // way up. Which is what we want since we've consumed it. If this changes to
1555
+ // be recursive serialization, we need to reset the keyPath and implicitSlot,
1556
+ // before recursing here. We also need to reset it once we render into an array
1557
+ // or anything else too which we also get implicitly.
1558
+
1559
+
1560
+ return element;
1520
1561
  } // The chunk ID we're currently rendering that we can assign debug data to.
1521
1562
 
1522
1563
 
@@ -1608,7 +1649,7 @@ function createTask(request, model, keyPath, implicitSlot, abortSet) {
1608
1649
  if (typeof model === 'object' && model !== null) {
1609
1650
  // If we're about to write this into a new task we can assign it an ID early so that
1610
1651
  // any other references can refer to the value we're about to write.
1611
- {
1652
+ if ((keyPath !== null || implicitSlot)) ; else {
1612
1653
  request.writtenObjects.set(model, id);
1613
1654
  }
1614
1655
  }
@@ -1794,8 +1835,7 @@ function serializeMap(request, map) {
1794
1835
  const existingId = writtenObjects.get(key);
1795
1836
 
1796
1837
  if (existingId === undefined) {
1797
- // Mark all object keys as seen so that they're always outlined.
1798
- writtenObjects.set(key, -1);
1838
+ writtenObjects.set(key, SEEN_BUT_NOT_YET_OUTLINED);
1799
1839
  }
1800
1840
  }
1801
1841
  }
@@ -1815,8 +1855,7 @@ function serializeSet(request, set) {
1815
1855
  const existingId = writtenObjects.get(key);
1816
1856
 
1817
1857
  if (existingId === undefined) {
1818
- // Mark all object keys as seen so that they're always outlined.
1819
- writtenObjects.set(key, -1);
1858
+ writtenObjects.set(key, SEEN_BUT_NOT_YET_OUTLINED);
1820
1859
  }
1821
1860
  }
1822
1861
  }
@@ -1919,12 +1958,11 @@ function renderModelDestructive(request, task, parent, parentPropertyName, value
1919
1958
  const existingId = writtenObjects.get(value);
1920
1959
 
1921
1960
  if (existingId !== undefined) {
1922
- if (modelRoot === value) {
1961
+ if ((task.keyPath !== null || task.implicitSlot)) ; else if (modelRoot === value) {
1923
1962
  // This is the ID we're currently emitting so we need to write it
1924
1963
  // once but if we discover it again, we refer to it by id.
1925
1964
  modelRoot = null;
1926
- } else if (existingId === -1) {
1927
- // Seen but not yet outlined.
1965
+ } else if (existingId === SEEN_BUT_NOT_YET_OUTLINED) {
1928
1966
  // TODO: If we throw here we can treat this as suspending which causes an outline
1929
1967
  // but that is able to reuse the same task if we're already in one but then that
1930
1968
  // will be a lazy future value rather than guaranteed to exist but maybe that's good.
@@ -1943,7 +1981,10 @@ function renderModelDestructive(request, task, parent, parentPropertyName, value
1943
1981
  } else {
1944
1982
  // This is the first time we've seen this object. We may never see it again
1945
1983
  // so we'll inline it. Mark it as seen. If we see it again, we'll outline.
1946
- writtenObjects.set(value, -1);
1984
+ writtenObjects.set(value, SEEN_BUT_NOT_YET_OUTLINED); // The element's props are marked as "never outlined" so that they are inlined into
1985
+ // the same row as the element itself.
1986
+
1987
+ writtenObjects.set(value.props, NEVER_OUTLINED);
1947
1988
  }
1948
1989
 
1949
1990
  const element = value;
@@ -1952,7 +1993,11 @@ function renderModelDestructive(request, task, parent, parentPropertyName, value
1952
1993
  let ref;
1953
1994
 
1954
1995
  {
1955
- ref = element.ref;
1996
+ // TODO: This is a temporary, intermediate step. Once the feature
1997
+ // flag is removed, we should get the ref off the props object right
1998
+ // before using it.
1999
+ const refProp = props.ref;
2000
+ ref = refProp !== undefined ? refProp : null;
1956
2001
  } // Attempt to render the Server Component.
1957
2002
 
1958
2003
 
@@ -1983,7 +2028,13 @@ function renderModelDestructive(request, task, parent, parentPropertyName, value
1983
2028
 
1984
2029
  if (typeof value.then === 'function') {
1985
2030
  if (existingId !== undefined) {
1986
- if (modelRoot === value) {
2031
+ if ((task.keyPath !== null || task.implicitSlot)) {
2032
+ // If we're in some kind of context we can't reuse the result of this render or
2033
+ // previous renders of this element. We only reuse Promises if they're not wrapped
2034
+ // by another Server Component.
2035
+ const promiseId = serializeThenable(request, task, value);
2036
+ return serializePromiseID(promiseId);
2037
+ } else if (modelRoot === value) {
1987
2038
  // This is the ID we're currently emitting so we need to write it
1988
2039
  // once but if we discover it again, we refer to it by id.
1989
2040
  modelRoot = null;
@@ -2005,11 +2056,10 @@ function renderModelDestructive(request, task, parent, parentPropertyName, value
2005
2056
  // This is the ID we're currently emitting so we need to write it
2006
2057
  // once but if we discover it again, we refer to it by id.
2007
2058
  modelRoot = null;
2008
- } else if (existingId === -1) {
2009
- // Seen but not yet outlined.
2059
+ } else if (existingId === SEEN_BUT_NOT_YET_OUTLINED) {
2010
2060
  const newId = outlineModel(request, value);
2011
2061
  return serializeByValueID(newId);
2012
- } else {
2062
+ } else if (existingId !== NEVER_OUTLINED) {
2013
2063
  // We've already emitted this as an outlined object, so we can
2014
2064
  // just refer to that by its existing ID.
2015
2065
  return serializeByValueID(existingId);
@@ -2017,7 +2067,7 @@ function renderModelDestructive(request, task, parent, parentPropertyName, value
2017
2067
  } else {
2018
2068
  // This is the first time we've seen this object. We may never see it again
2019
2069
  // so we'll inline it. Mark it as seen. If we see it again, we'll outline.
2020
- writtenObjects.set(value, -1);
2070
+ writtenObjects.set(value, SEEN_BUT_NOT_YET_OUTLINED);
2021
2071
  }
2022
2072
 
2023
2073
  if (isArray(value)) {