@abide/abide 0.103.0 → 0.105.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 (88) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/bin/abide.ts +80 -173
  3. package/docs/REGISTRY.md +27 -27
  4. package/docs/RULEBOOK.md +68 -24
  5. package/docs/abide.md +31 -46
  6. package/package.json +1 -1
  7. package/src/compiler/Lowering.ts +15 -5
  8. package/src/compiler/Refusals.ts +57 -0
  9. package/src/compiler/TypeSession.ts +46 -89
  10. package/src/compiler/appLayout.ts +447 -0
  11. package/src/compiler/appMap.ts +21 -25
  12. package/src/compiler/checkApp.ts +61 -97
  13. package/src/compiler/checkedShadow.ts +12 -10
  14. package/src/compiler/classifyRead.ts +3 -6
  15. package/src/compiler/compileApp.ts +183 -478
  16. package/src/compiler/componentGraph.ts +2 -71
  17. package/src/compiler/declaredExports.ts +79 -60
  18. package/src/compiler/emitClient.ts +11 -13
  19. package/src/compiler/emitComponent.ts +21 -6
  20. package/src/compiler/emitSource.ts +20 -20
  21. package/src/compiler/handlersUnder.ts +46 -36
  22. package/src/compiler/hookCalls.ts +152 -0
  23. package/src/compiler/mountsOf.ts +31 -54
  24. package/src/compiler/projectShadow.ts +1 -1
  25. package/src/compiler/readSources.ts +27 -42
  26. package/src/compiler/readsIn.ts +5 -16
  27. package/src/compiler/resolverIn.ts +77 -0
  28. package/src/server/abideModules.ts +152 -146
  29. package/src/server/answerRequest.ts +18 -4
  30. package/src/server/answerSocket.ts +136 -76
  31. package/src/server/answerValue.ts +28 -15
  32. package/src/server/appTree.ts +59 -73
  33. package/src/server/claudeApi.ts +2 -5
  34. package/src/server/clientBundle.ts +198 -0
  35. package/src/server/coerceArgs.ts +1 -1
  36. package/src/server/commands.ts +93 -57
  37. package/src/server/createApp.ts +90 -16
  38. package/src/server/deferredBuilds.ts +175 -0
  39. package/src/server/devWorker.ts +107 -319
  40. package/src/server/faceOf.ts +120 -0
  41. package/src/server/fenceServer.ts +10 -18
  42. package/src/server/generatedSurfaces.ts +12 -53
  43. package/src/server/index.ts +3 -0
  44. package/src/server/placement.ts +167 -0
  45. package/src/server/remote.ts +14 -42
  46. package/src/server/render.ts +25 -17
  47. package/src/server/renderComponent.ts +9 -5
  48. package/src/server/requestScope.ts +10 -4
  49. package/src/server/responseRelay.ts +389 -0
  50. package/src/server/runCompiled.ts +9 -12
  51. package/src/server/runDesktop.ts +6 -9
  52. package/src/server/serveBuild.ts +26 -73
  53. package/src/server/serveDev.ts +19 -176
  54. package/src/server/streamEvent.ts +29 -0
  55. package/src/server/versionOf.ts +1 -2
  56. package/src/server/withAbideSources.ts +2 -1
  57. package/src/shared/HandlerRoute.ts +68 -0
  58. package/src/shared/REFUSAL_KEY.ts +5 -0
  59. package/src/shared/RESERVED_DIRECTORIES.ts +6 -0
  60. package/src/shared/answerOf.ts +105 -78
  61. package/src/shared/filledPath.ts +1 -9
  62. package/src/shared/framing.ts +127 -128
  63. package/src/shared/queryText.ts +15 -7
  64. package/src/shared/reactive/Cursor.ts +69 -124
  65. package/src/shared/reactive/Reactive.ts +296 -162
  66. package/src/shared/reactive/callLoad.ts +4 -18
  67. package/src/shared/reactive/index.ts +11 -8
  68. package/src/shared/reactive/pairReactive.ts +1 -1
  69. package/src/shared/reactive/scoped.ts +4 -3
  70. package/src/shared/reactive/share.ts +11 -1
  71. package/src/shared/routePattern.ts +0 -110
  72. package/src/shared/rpc.ts +17 -24
  73. package/src/shared/rpcRequest.ts +18 -47
  74. package/src/shared/takeEarlyCall.ts +12 -37
  75. package/src/shared/url.ts +1 -1
  76. package/src/ui/Level.ts +276 -0
  77. package/src/ui/List.ts +17 -17
  78. package/src/ui/Router.ts +20 -87
  79. package/src/ui/applyHead.ts +92 -79
  80. package/src/ui/index.ts +4 -2
  81. package/src/ui/reconcile.ts +4 -110
  82. package/src/ui/rows.ts +123 -2
  83. package/src/ui/wire.ts +12 -27
  84. package/src/compiler/hooksCalled.ts +0 -35
  85. package/src/server/clientBuildOptions.ts +0 -54
  86. package/src/server/refuseAddressCollision.ts +0 -37
  87. package/src/shared/reactive/chunksOfStream.ts +0 -57
  88. package/src/ui/MOUNTING_LEVEL.ts +0 -18
@@ -19,7 +19,7 @@ import { type Found, runSchema, type Schema } from '../Schema.ts'
19
19
  import { UNEXPECTED } from '../UNEXPECTED.ts'
20
20
  import { WORK } from '../WORK.ts'
21
21
  import { CELL } from './CELL.ts'
22
- import { Cursor, hasCursorBehind, keepPlaces, type Retained } from './Cursor.ts'
22
+ import { CAUGHT_UP, Cursor, hasCursorBehind, keepPlaces } from './Cursor.ts'
23
23
  import { ENTRY_LIVES } from './ENTRY_LIVES.ts'
24
24
  import { armTimer, type Expiry, expiryFor } from './Expiry.ts'
25
25
  import { FOLLOWED } from './FOLLOWED.ts'
@@ -257,7 +257,7 @@ export type Reactive<
257
257
  invalidate(): void
258
258
  // biome-ignore lint/suspicious/noConfusingVoidType: `void` is the RETURN POSITION of an author's effect, and it is load-bearing. `undefined` there refuses `(v) => { read(v) }` — a block body returning nothing infers `void`, which is not assignable to `undefined` — so the rule's fix would reject the ordinary effect.
259
259
  watch(effect: (value: Stored) => void | (() => void)): () => void
260
- /** The production in flight, and then what follows it (2.5). */
260
+ /** What one run produced: the settled value once, or a stream's chunks to its close (2.5, 2.6). */
261
261
  [Symbol.asyncIterator](): AsyncIterator<Produced>
262
262
  then<Result>(
263
263
  onSettled?: (value: Stored) => Result | PromiseLike<Result>,
@@ -459,7 +459,7 @@ type Stream = {
459
459
  * Whether what is served is a run that closed.
460
460
  *
461
461
  * WHETHER WHAT IS SERVED IS A RUN THAT CLOSED, which is the only accumulation a deadline
462
- * reaches (6.11). A run in flight has minted nothing, so `expiry.at` says nothing about it —
462
+ * reaches (6.10). A run in flight has minted nothing, so `expiry.at` says nothing about it —
463
463
  * without this the standing arm reads `at` as 0 and drops the value on its first read.
464
464
  */
465
465
  closed: boolean
@@ -482,6 +482,17 @@ function newStream(source: Cell | null): Stream {
482
482
  }
483
483
  }
484
484
 
485
+ /**
486
+ * The stream `cell` produces itself, or `null`: it has none, or the one it has is forwarded (11.15).
487
+ *
488
+ * THE ONE DISCRIMINATOR between following and forwarding. An own stream's `adoption` is a write it
489
+ * follows (9.11), and a forwarding value's is the stream it forwards.
490
+ */
491
+ function ownStream(cell: Cell): Stream | null {
492
+ const stream = cell.stream
493
+ return stream !== null && stream.source === null ? stream : null
494
+ }
495
+
485
496
  /** Turns `cell` into a streaming producer, forwarding `source`'s stream when it has one. */
486
497
  function becomeStream(cell: Cell, source: Cell | null): Stream {
487
498
  const stream = newStream(source)
@@ -489,12 +500,13 @@ function becomeStream(cell: Cell, source: Cell | null): Stream {
489
500
  cell.plain = false
490
501
  cell.bareWrite = false
491
502
  simpleShape(cell)
503
+ seat(cell)
492
504
  cell.direct = false
493
505
  return stream
494
506
  }
495
507
 
496
508
  /**
497
- * Starts a closed run's clock: the close is a streaming producer's mint (6.11), as `produce` is
509
+ * Starts a closed run's clock: the close is a streaming producer's mint (6.10), as `produce` is
498
510
  * every other producer's. A failed or superseded run never reaches here, so it starts no clock.
499
511
  */
500
512
  function closeRun(cell: Cell, stream: Stream): void {
@@ -651,8 +663,8 @@ class Cell {
651
663
  */
652
664
  declare schema: Schema<unknown> | null
653
665
  /**
654
- * A streaming producer's chunk buffer, for a cursor to walk (2.5); `EMPTY_RING` on every
655
- * other value, whose cursor answers the standing production from `value`.
666
+ * A streaming producer's chunk buffer, for a cursor to walk (2.6); `EMPTY_RING` on every
667
+ * other value.
656
668
  */
657
669
  declare ring: unknown[]
658
670
  /**
@@ -989,14 +1001,10 @@ function comparatorFor(
989
1001
  /**
990
1002
  * Recomputes `simple` and re-seats the callable on the matching prototype.
991
1003
  *
992
- * `simple` IN ONE PLACE, and the prototype that goes with it. A second copy of this
993
- * conjunction is the drift that would let one of the four sites go on reading `true` after
994
- * the field under it moved — and what the short write would then skip is a cap, a ring or a
995
- * declared `identity`, silently.
996
- *
997
- * THE CALLABLE IS RE-SEATED ONLY WHERE IT IS ON ONE OF THESE TWO. A value that is adopting or
998
- * forwarding a stream carries a prototype of its own and keeps it; the field is still
999
- * maintained, so the seat it lands on when the adoption drops is the right one.
1004
+ * `simple` IN ONE PLACE. A second copy of this conjunction is the drift that would let one of the
1005
+ * sites go on reading `true` after the field under it moved — and what the short write would then
1006
+ * skip is a cap, a ring or a declared `identity`, silently. The prototype that goes with it is
1007
+ * `seat`'s, taken where the transition that moved a field under it ends.
1000
1008
  */
1001
1009
  function simpleShape(cell: Cell): void {
1002
1010
  cell.simple =
@@ -1006,14 +1014,38 @@ function simpleShape(cell: Cell): void {
1006
1014
  cell.cap === null &&
1007
1015
  cell.store === null &&
1008
1016
  cell.identityFn === null
1017
+ }
1018
+
1019
+ /**
1020
+ * The prototype a value's state calls for: its own stream's writes (9.11), an adoption's (11.59),
1021
+ * the short write of a `simple` value, or the members alone.
1022
+ */
1023
+ function seatOf(cell: Cell): object {
1024
+ if (ownStream(cell) !== null) return STREAM_PROTOTYPE
1025
+ if (cell.adoption !== null) return ADOPTING_PROTOTYPE
1026
+ return cell.simple ? SIMPLE_PROTOTYPE : MEMBER_PROTOTYPE
1027
+ }
1028
+
1029
+ /**
1030
+ * Re-seats the callable on the prototype its state calls for, where that moved.
1031
+ *
1032
+ * THE ONE WRITER OF A VALUE'S PROTOTYPE after construction, so the seat is derived rather than
1033
+ * remembered by each transition that moves it. A seat this file did not make — `pairReactive`'s —
1034
+ * is its owner's, and left alone.
1035
+ */
1036
+ function seat(cell: Cell): void {
1009
1037
  const callable = cell.callable
1010
1038
  if (callable === null) return
1011
- const seat = Object.getPrototypeOf(callable)
1012
- if (seat !== MEMBER_PROTOTYPE && seat !== SIMPLE_PROTOTYPE) return
1013
- Object.setPrototypeOf(
1014
- callable as object,
1015
- cell.simple ? SIMPLE_PROTOTYPE : MEMBER_PROTOTYPE,
1039
+ const now = Object.getPrototypeOf(callable)
1040
+ if (
1041
+ now !== MEMBER_PROTOTYPE &&
1042
+ now !== SIMPLE_PROTOTYPE &&
1043
+ now !== ADOPTING_PROTOTYPE &&
1044
+ now !== STREAM_PROTOTYPE
1016
1045
  )
1046
+ return
1047
+ const next = seatOf(cell)
1048
+ if (next !== now) Object.setPrototypeOf(callable as object, next)
1017
1049
  }
1018
1050
 
1019
1051
  /**
@@ -1496,19 +1528,8 @@ function produce(cell: Cell, produced: unknown): number {
1496
1528
  // collapses was still accepted — it produced nothing because the value was already
1497
1529
  // the one being written, not because it failed. Leaving the failure standing left
1498
1530
  // `s.error()` reporting a load that a later write superseded.
1499
- const wasServable = cell.servable
1500
1531
  const settling = settleProbes(cell)
1501
- cell.servable = true
1502
- setDirect(cell, cell.state === CLEAN)
1503
- // THE VALUE EDGE MOVED TOO WHERE THERE WAS NOTHING TO SERVE, whatever the gate
1504
- // says about the payload — the same split `refused` makes, and for the same
1505
- // reason. A read taken while this was unservable THREW, so a body that reached
1506
- // it aborted: it is `CLEAN`, holds nothing, and nothing downstream will ever
1507
- // pull it. Left silent, an `invalidate` whose reload landed the value it already
1508
- // held stranded every value built from it at `ready()` false for the life of the
1509
- // page. Over a value still being served nothing a reader can see moved (5.1) and
1510
- // this stays quiet, which is the arm 5.2 is about.
1511
- if (!wasServable) cascade(cell)
1532
+ serveAgain(cell)
1512
1533
  if (settling) probesMoved(cell)
1513
1534
  return cell.ordinal
1514
1535
  }
@@ -1519,9 +1540,7 @@ function produce(cell: Cell, produced: unknown): number {
1519
1540
  // `ttl` must not pay.
1520
1541
  const expiry = cell.expiry
1521
1542
  const mintedAt = expiry === null ? 0 : Date.now()
1522
- // 6.10: AN `undefined` IS SERVED AND MOVES NO ORDINAL, so no cursor is handed one — it is not a
1523
- // message, and not a JSON value either.
1524
- if (produced !== undefined) cell.ordinal += 1
1543
+ cell.ordinal += 1
1525
1544
  if (expiry !== null) {
1526
1545
  expiry.at = mintedAt
1527
1546
  // A production is what 6.7 released being replaced, so the value is no longer
@@ -1535,8 +1554,7 @@ function produce(cell: Cell, produced: unknown): number {
1535
1554
  if (cap !== null && cell.body === null)
1536
1555
  return capped(cell, cap, produced, moved)
1537
1556
  serve(cell, produced)
1538
- cascade(cell)
1539
- if (moved) probesMoved(cell)
1557
+ land(cell, moved)
1540
1558
  return cell.ordinal
1541
1559
  }
1542
1560
 
@@ -1555,6 +1573,56 @@ function settleProbes(cell: Cell): boolean {
1555
1573
  return moved
1556
1574
  }
1557
1575
 
1576
+ // THE LOAD-STATE TRANSITIONS. `loading`, `servable` and `failure` are written by these and by the
1577
+ // value's construction, and nowhere else: `beginLoad`, `settleProbes` with `serve` or `landChunks`
1578
+ // and then `land`, `serveAgain`, `endLoad`, `refused`, `unserve`, `hold`/`release`/`dropHeld`,
1579
+ // `abortRun`, `supersede`, `landOpening` and `dropStanding`.
1580
+ // Each owns the probe move it makes, so a site that changes what a probe answers cannot forget to
1581
+ // say so. `loading` carries five meanings and they are named here rather than told apart: a load in
1582
+ // flight, an open stream run, a window holding a production (5.15), a body aborted on a value that
1583
+ // had not landed (2.13), and a keyed entry a window has not started (5.19).
1584
+
1585
+ /**
1586
+ * Tells both edges a production landed: the readers through `cascade`, the guards where
1587
+ * `settleProbes` said a probe moved.
1588
+ *
1589
+ * THE OTHER HALF OF `settleProbes`, which has to be read before the landing it describes.
1590
+ */
1591
+ function land(cell: Cell, moved: boolean): void {
1592
+ cascade(cell)
1593
+ if (moved) probesMoved(cell)
1594
+ }
1595
+
1596
+ /**
1597
+ * Serves what is held again, with no production; returns whether there had been nothing to serve.
1598
+ *
1599
+ * THE VALUE EDGE MOVES WHERE THERE WAS NOTHING TO SERVE, whatever the identity gate says about the
1600
+ * payload — the same split `refused` makes, and for the same reason. A read taken while this was
1601
+ * unservable THREW, so a body that reached it aborted: it is `CLEAN`, holds nothing, and nothing
1602
+ * downstream will ever pull it. Left silent, an `invalidate` whose reload landed the value it
1603
+ * already held stranded every value built from it at `ready()` false for the life of the page. Over
1604
+ * a value still being served nothing a reader can see moved (5.1) and this stays quiet, which is the
1605
+ * arm 5.2 is about.
1606
+ */
1607
+ function serveAgain(cell: Cell): boolean {
1608
+ const was = cell.servable
1609
+ cell.servable = true
1610
+ setDirect(cell, cell.state === CLEAN)
1611
+ if (!was) cascade(cell)
1612
+ return !was
1613
+ }
1614
+
1615
+ /**
1616
+ * Ends a load that landed nothing: a stream's close, a deferred entry a window dropped, a store's
1617
+ * miss with nothing to fall back on. `busy` moves where it was loading, or where `moved` says
1618
+ * another probe did.
1619
+ */
1620
+ function endLoad(cell: Cell, moved: boolean): void {
1621
+ const was = cell.loading
1622
+ cell.loading = false
1623
+ if (was || moved) probesMoved(cell)
1624
+ }
1625
+
1558
1626
  /**
1559
1627
  * Makes a production what readers see: stores it, arms its deadline, persists it.
1560
1628
  *
@@ -1649,8 +1717,7 @@ function capped(
1649
1717
  // collapses a RUN of changes and the first landing has nothing to collapse with.
1650
1718
  if (wakes(cell, cap, produced)) {
1651
1719
  serve(cell, produced)
1652
- cascade(cell)
1653
- if (moved) probesMoved(cell)
1720
+ land(cell, moved)
1654
1721
  }
1655
1722
  return cell.ordinal
1656
1723
  }
@@ -1682,9 +1749,27 @@ function release(cell: Cell, cap: Cap): void {
1682
1749
  cap.held = undefined
1683
1750
  cell.loading = false
1684
1751
  serve(cell, value)
1685
- cascade(cell)
1686
1752
  // The close is a `busy` edge (5.15) even though the value's readiness never moved.
1687
- probesMoved(cell)
1753
+ land(cell, true)
1754
+ }
1755
+
1756
+ /**
1757
+ * Discards the production a window is holding, where an adoption superseded it; returns whether
1758
+ * `busy` was the hold's.
1759
+ *
1760
+ * An adoption is a later write (11.61), and a later write replaces what a window holds — save that
1761
+ * these two do not reach the window: a stream is forwarded rather than produced (11.15), and a value
1762
+ * with nothing to serve leaves this one nothing (11.16). What the window minted last is then what is
1763
+ * served, which the identity gate compares against (5.9).
1764
+ */
1765
+ function dropHeld(cell: Cell): boolean {
1766
+ const cap = cell.cap
1767
+ if (cap === null || !cap.holding || cell.stream !== null) return false
1768
+ cap.holding = false
1769
+ cap.held = undefined
1770
+ cap.minted = cell.value
1771
+ cell.loading = false
1772
+ return true
1688
1773
  }
1689
1774
 
1690
1775
  function releaseHeld(cell: Cell, cap: Cap): void {
@@ -1857,10 +1942,10 @@ function standWrite(cell: Cell, stream: Stream, value: unknown): number {
1857
1942
  cell.value = value
1858
1943
  stream.materializedAt = stream.version
1859
1944
  stream.written = true
1945
+ // `busy` is the run's, which a write stands beside (3.7), so only `ready` and `error` move.
1860
1946
  const moved = !cell.servable || cell.failure !== undefined
1861
1947
  landChunks(cell)
1862
- cascade(cell)
1863
- if (moved) probesMoved(cell)
1948
+ land(cell, moved)
1864
1949
  return cell.ordinal
1865
1950
  }
1866
1951
 
@@ -1987,7 +2072,7 @@ function adopt(cell: Cell, inner: Cell): Error | undefined {
1987
2072
  // 11.59: an adopting value's writes are the adopted value's, reached through a prototype of its
1988
2073
  // own rather than a branch on every value's write path — see `acceptIntoStream` for what such a
1989
2074
  // branch costs under V8 even where nothing takes it.
1990
- Object.setPrototypeOf(cell.callable as object, ADOPTING_PROTOTYPE)
2075
+ seat(cell)
1991
2076
  if (!cell.loading && busyNow(inner) !== wasBusy) probesMoved(cell)
1992
2077
  return undefined
1993
2078
  }
@@ -2086,6 +2171,7 @@ function mirror(cell: Cell, inner: Cell): void {
2086
2171
  if (cell.stream !== null) stopForwarding(cell)
2087
2172
  if (!inner.servable) {
2088
2173
  // 11.16: the newly adopted has nothing to serve, so neither has this.
2174
+ if (dropHeld(cell)) probesMoved(cell)
2089
2175
  unserve(cell)
2090
2176
  return
2091
2177
  }
@@ -2116,20 +2202,16 @@ function mirror(cell: Cell, inner: Cell): void {
2116
2202
  function dropAdoption(cell: Cell): void {
2117
2203
  if (cell.adoption === null) return
2118
2204
  unfollow(cell)
2119
- const stream = cell.stream
2120
2205
  // 9.11: AN OWN STREAM'S `adoption` IS A WRITE IT FOLLOWS, not a stream it forwards — the
2121
2206
  // discriminator `adoptedCellOf` reads. Letting go of the write is all there is: the stream is
2122
2207
  // its own, so is its prototype, and `follow` never touched `identityFn`. Stopping it as a
2123
2208
  // forward nulled the stream a queued `wakeStreams` was about to read.
2124
- if (stream !== null && stream.source === null) return
2125
- if (stream !== null) stopForwarding(cell)
2209
+ if (ownStream(cell) !== null) return
2210
+ if (cell.stream !== null) stopForwarding(cell)
2126
2211
  // 11.17 inherited the identity for the length of the adoption only.
2127
2212
  cell.identityFn = cell.declaredIdentityFn
2128
2213
  simpleShape(cell)
2129
- Object.setPrototypeOf(
2130
- cell.callable as object,
2131
- cell.simple ? SIMPLE_PROTOTYPE : MEMBER_PROTOTYPE,
2132
- )
2214
+ seat(cell)
2133
2215
  }
2134
2216
 
2135
2217
  /**
@@ -2143,6 +2225,8 @@ function dropAdoption(cell: Cell): void {
2143
2225
  function forwardStream(cell: Cell, inner: Cell, adopted: Stream): void {
2144
2226
  let stream = cell.stream
2145
2227
  if (stream === null) {
2228
+ // The `busy` the hold left is settled below, with the adopter's own load.
2229
+ dropHeld(cell)
2146
2230
  stream = becomeStream(cell, inner)
2147
2231
  stream.ordinalOffset = cell.ordinal - inner.ringBase
2148
2232
  }
@@ -2168,8 +2252,7 @@ function forwardStream(cell: Cell, inner: Cell, adopted: Stream): void {
2168
2252
  // on answering through the adoption (11.19) for as long as the stream is open.
2169
2253
  const moved = settleProbes(cell)
2170
2254
  landChunks(cell)
2171
- cascade(cell)
2172
- if (moved) probesMoved(cell)
2255
+ land(cell, moved)
2173
2256
  }
2174
2257
 
2175
2258
  function stopForwarding(cell: Cell): void {
@@ -2230,17 +2313,7 @@ function run(cell: Cell): void {
2230
2313
  truncateProbes(reader)
2231
2314
  cell.running = false
2232
2315
  if (thrown instanceof NotLanded) {
2233
- cell.loading = true
2234
- // 2.13: the mark a source's production makes is what finishes this run, and
2235
- // `markReaders` queues it rather than leaving it for a read that may never come.
2236
- cell.aborted = true
2237
- // NOT EVERYTHING IT READS IS READY, so neither is this — 11.58, and the same
2238
- // sentence 11.16 makes for a re-adoption. It stops serving rather than handing
2239
- // back a figure it computed from a source that has since gone away, and that
2240
- // is a move its readers have to hear even though the aborted run minted
2241
- // nothing for a cascade to carry.
2242
- if (cell.servable) unserve(cell)
2243
- else probesMoved(cell)
2316
+ abortRun(cell)
2244
2317
  return
2245
2318
  }
2246
2319
  refused(cell, thrown)
@@ -2321,6 +2394,23 @@ function run(cell: Cell): void {
2321
2394
  absorb(cell, produced, true)
2322
2395
  }
2323
2396
 
2397
+ /**
2398
+ * Leaves a run that read a value which had not landed owing the rest of itself (2.13).
2399
+ *
2400
+ * BUSY, because what it waits on is loading. The mark a source's production makes is what finishes
2401
+ * the run, and `markReaders` queues it rather than leaving it for a read that may never come. And
2402
+ * NOT EVERYTHING IT READS IS READY, so neither is this — 11.58, and the same sentence 11.16 makes
2403
+ * for a re-adoption. It stops serving rather than handing back a figure it computed from a source
2404
+ * that has since gone away, and that is a move its readers have to hear even though the aborted run
2405
+ * minted nothing for a cascade to carry.
2406
+ */
2407
+ function abortRun(cell: Cell): void {
2408
+ cell.loading = true
2409
+ cell.aborted = true
2410
+ if (cell.servable) unserve(cell)
2411
+ else probesMoved(cell)
2412
+ }
2413
+
2324
2414
  /**
2325
2415
  * Adopts what a body handed back, refusing the body where that would close a cycle.
2326
2416
  *
@@ -2336,7 +2426,7 @@ function adoptAnswer(cell: Cell, produced: unknown): void {
2336
2426
  *
2337
2427
  * 4.16: AN OPENING `undefined` IS LOADED AND IS NOT A PRODUCTION THROUGH THE PIPELINE. No `schema`
2338
2428
  * judges it, no `transform` shapes it, no window holds it and no `store` writes it — it is the value
2339
- * being empty, and the first real production is what those are for. It mints no ordinal (6.10).
2429
+ * being empty, and the first real production is what those are for. It mints no ordinal.
2340
2430
  */
2341
2431
  function landOpening(cell: Cell): void {
2342
2432
  const moved = settleProbes(cell)
@@ -2344,8 +2434,7 @@ function landOpening(cell: Cell): void {
2344
2434
  cell.landed = true
2345
2435
  cell.servable = true
2346
2436
  setDirect(cell, cell.state === CLEAN)
2347
- cascade(cell)
2348
- if (moved) probesMoved(cell)
2437
+ land(cell, moved)
2349
2438
  }
2350
2439
 
2351
2440
  /**
@@ -2357,11 +2446,23 @@ function landOpening(cell: Cell): void {
2357
2446
  * be marked by (14.13).
2358
2447
  */
2359
2448
  function startProducer(cell: Cell): void {
2360
- const stream = cell.stream
2361
- if (stream === null || stream.source !== null) run(cell)
2449
+ const stream = ownStream(cell)
2450
+ if (stream === null) run(cell)
2362
2451
  else startStream(cell, stream)
2363
2452
  }
2364
2453
 
2454
+ /**
2455
+ * Supersedes the work in flight: a load's answer lands nowhere once `generation` has moved, and a
2456
+ * stream's own run is stopped outright. `busy` moves silently, and telling the guards is the
2457
+ * caller's — 7.3 has an invalidate tell nobody.
2458
+ */
2459
+ function supersede(cell: Cell): void {
2460
+ cell.generation += 1
2461
+ cell.loading = false
2462
+ const stream = ownStream(cell)
2463
+ if (stream !== null) stopStream(stream)
2464
+ }
2465
+
2365
2466
  /**
2366
2467
  * Supersedes the run in flight and tells its iterator to return.
2367
2468
  *
@@ -2395,10 +2496,7 @@ function startStream(cell: Cell, stream: Stream): void {
2395
2496
  */
2396
2497
  function promoteToStream(cell: Cell, generator: AsyncIterable<unknown>): void {
2397
2498
  let stream = cell.stream
2398
- if (stream === null) {
2399
- stream = becomeStream(cell, null)
2400
- Object.setPrototypeOf(cell.callable as object, STREAM_PROTOTYPE)
2401
- }
2499
+ if (stream === null) stream = becomeStream(cell, null)
2402
2500
  pumpRun(cell, stream, generator[Symbol.asyncIterator]())
2403
2501
  }
2404
2502
 
@@ -2484,7 +2582,6 @@ async function pump(
2484
2582
  cap.holding = false
2485
2583
  releaseChunks(cell, stream)
2486
2584
  }
2487
- const moved = !begun || cell.loading
2488
2585
  if (!begun) {
2489
2586
  // A stream that closed with nothing in it has finished, and what it accumulated is
2490
2587
  // no chunks — a value, where waiting on would be a load that never ends.
@@ -2492,10 +2589,9 @@ async function pump(
2492
2589
  landChunks(cell)
2493
2590
  cascade(cell)
2494
2591
  }
2495
- cell.loading = false
2496
2592
  // 3.7: `busy` held until the close, and this is the close.
2497
- if (moved) probesMoved(cell)
2498
- // 6.11, AFTER THE CASCADE ABOVE: the entry is what a read would answer with, so it is taken
2593
+ endLoad(cell, !begun)
2594
+ // 6.10, AFTER THE CASCADE ABOVE: the entry is what a read would answer with, so it is taken
2499
2595
  // once the last chunk has landed and the value is servable.
2500
2596
  closeRun(cell, stream)
2501
2597
  // 8.9: a stream is written once, at the close, with the accumulation and never a partial.
@@ -2631,7 +2727,7 @@ function materialize(cell: Cell, stream: Stream): void {
2631
2727
  // 9.11: the accumulation moved past a write following a `Reactive`, which lets go of it here
2632
2728
  // where no chunk's wake did — a patch, or a run that closed empty. Only an own stream follows;
2633
2729
  // a forwarding one's `adoption` is what it forwards.
2634
- if (cell.adoption !== null && stream.source === null) unfollow(cell)
2730
+ if (cell.adoption !== null && ownStream(cell) !== null) unfollow(cell)
2635
2731
  stream.materializedAt = stream.version
2636
2732
  stream.written = false
2637
2733
  gateInto(cell, stream.chunks.slice(0, stream.visible))
@@ -2681,6 +2777,97 @@ function heldValue(cell: Cell): unknown {
2681
2777
  return cell.value
2682
2778
  }
2683
2779
 
2780
+ /**
2781
+ * The chunks of the run `cell` is serving, from its first to its close (2.6).
2782
+ *
2783
+ * ONE RUN, AND THE READER OWNS ITS BOUNDARY. The run is its ring: a new run's first chunk replaces
2784
+ * the array, where a patch mutates it in place (9.13) and only moves the base — so a ring that moved
2785
+ * ends the walk (9.16), save the `EMPTY_RING` a run with no chunk yet holds. And what the run failed
2786
+ * on is thrown after its chunks (9.15), unless it was standing before the walk began — a
2787
+ * revalidation's (2.4), not this run's. The cursor is the wake and the settling is the end
2788
+ * (`Cursor.closeOn`).
2789
+ *
2790
+ * `gated` IS THE WIRE'S, and the two never share a take. Ungated is the ring by ordinal, every chunk
2791
+ * a window collapsed included (5.10) — `for await`. Gated is what a read would serve, chunk by chunk
2792
+ * (16.32): the accumulation to what the window released (5.18), or the gated accumulation (9.8).
2793
+ */
2794
+ async function* runChunks(cell: Cell, gated: boolean): AsyncGenerator<unknown> {
2795
+ let ring = cell.ring
2796
+ const standing = failureOf(cell)
2797
+ // Gated reads by index and holds no place, so it sits past every chunk where no patch keeps it.
2798
+ const cursor = new Cursor(
2799
+ cell,
2800
+ gated ? Number.POSITIVE_INFINITY : cell.ringBase,
2801
+ )
2802
+ cursor.closeOn(settled(cell))
2803
+ let sent = 0
2804
+ try {
2805
+ for (;;) {
2806
+ if (cell.ring !== ring) {
2807
+ if (ring !== EMPTY_RING) return
2808
+ ring = cell.ring
2809
+ }
2810
+ const chunk = gated ? toldAt(cell, sent) : cursor.take()
2811
+ if (chunk !== CAUGHT_UP) {
2812
+ sent += 1
2813
+ yield chunk
2814
+ continue
2815
+ }
2816
+ if (cursor.closed) break
2817
+ await cursor.turn()
2818
+ }
2819
+ } finally {
2820
+ cursor.stop()
2821
+ }
2822
+ if (cursor.failure !== undefined) throw cursor.failure
2823
+ // 9.15 AND 16.59 — a run that failed after its first chunk, or one its timeout ended, settles
2824
+ // with what it had; the refusal stands beside it.
2825
+ const ended = failureOf(cell)
2826
+ if (ended !== undefined && ended !== standing) throw ended
2827
+ }
2828
+
2829
+ /**
2830
+ * The chunk at `index` of what a read of `cell` would serve, or `CAUGHT_UP`.
2831
+ *
2832
+ * Ungated it is read out of the accumulation in place, to `visible`: a read materializes a copy of
2833
+ * the whole of it (9.1), which for a writer woken per chunk is n²/2 elements copied to send n. A
2834
+ * schema or a transform has to see the whole accumulation (9.8), so a gated value answers from what
2835
+ * a read would.
2836
+ */
2837
+ function toldAt(cell: Cell, index: number): unknown {
2838
+ const stream = cell.stream
2839
+ // A forward that ended (11.16) is no stream to send any more of.
2840
+ if (stream === null) return CAUGHT_UP
2841
+ if (cell.schema === null && cell.transform === null)
2842
+ return index < stream.visible ? stream.chunks[index] : CAUGHT_UP
2843
+ const held = MEMBERS.peek.call(cell.callable as unknown as Callable)
2844
+ return Array.isArray(held) && index < held.length ? held[index] : CAUGHT_UP
2845
+ }
2846
+
2847
+ /**
2848
+ * What one run of `cell` produced, as `for await` walks it.
2849
+ *
2850
+ * 2.5 AND 2.6 — the settling, yielded once, or a stream's chunks from the first to the close. Which
2851
+ * of the two is asked of the settling, since a value may only turn out to stream once its load
2852
+ * resolves (9.6).
2853
+ */
2854
+ async function* producedRun(cell: Cell): AsyncGenerator<unknown> {
2855
+ const first = await settled(cell, true)
2856
+ if (first !== STREAMING) {
2857
+ yield first
2858
+ return
2859
+ }
2860
+ yield* runChunks(cell, false)
2861
+ }
2862
+
2863
+ /**
2864
+ * The chunks of the run `value` is serving, as a streamed answer sends them (16.32), ending with
2865
+ * that run (9.16) and throwing what it failed on (9.15).
2866
+ */
2867
+ export function chunksOfStream(value: unknown): AsyncGenerator<unknown> {
2868
+ return runChunks((value as Callable)[CELL], true)
2869
+ }
2870
+
2684
2871
  /**
2685
2872
  * Releases the standing production at its deadline, waking nobody.
2686
2873
  *
@@ -2773,7 +2960,7 @@ function beginLoad(cell: Cell): void {
2773
2960
  *
2774
2961
  * AND WHAT IS SERVED WAS MINTED, which every producer but a streaming one answers yes to the moment
2775
2962
  * it is servable. A stream serves the run it is building (9.1), and that run has minted nothing
2776
- * until it closes (6.11) — so a deadline has nothing to measure from and must not drop it.
2963
+ * until it closes (6.10) — so a deadline has nothing to measure from and must not drop it.
2777
2964
  */
2778
2965
  function standingLive(cell: Cell, expiry: Expiry): boolean {
2779
2966
  if (!reloads(cell) || expiry.dropped || !cell.servable) return false
@@ -2835,8 +3022,7 @@ function expire(cell: Cell): void {
2835
3022
  * which is what lets the flag be conservative.
2836
3023
  */
2837
3024
  function readIndirect(cell: Cell): unknown {
2838
- if (cell.owes !== OWES_NOTHING) settleOwed(cell)
2839
- if (cell.state !== CLEAN) resolve(cell)
3025
+ bringCurrent(cell)
2840
3026
  // 6.5: THE READ THAT FOLLOWS. Every value carrying an expiry arrives here and no
2841
3027
  // other value does, which is the whole of what `setDirect` buys.
2842
3028
  if (cell.expiry !== null) expire(cell)
@@ -2991,12 +3177,14 @@ function isReady(cell: Cell): boolean {
2991
3177
  }
2992
3178
 
2993
3179
  /**
2994
- * The body run where it is owed, before a probe answers about it.
3180
+ * Settles what an invalidate left owing and runs the body where it is marked: what a read, a probe,
3181
+ * a `peek` and an await each pull before they answer. Where they part is the deadline — a read and
3182
+ * a `peek` take 6.5's half, an await only a drop the timer made (16.7) — and each says so after.
2995
3183
  *
2996
3184
  * 3.2 — A PROBE ANSWERS ABOUT THE VALUE A READ WOULD SERVE. Under push-pull a write leaves every
2997
3185
  * derived value it reached unrun, so a probe that walked what the last run left answered about a
2998
3186
  * value nothing would serve: `x.ready()` true over a body whose inputs had moved to a load, and the
2999
- * read the guard admitted threw `NotLanded`. `peek` pulls for the same reason.
3187
+ * read the guard admitted threw `NotLanded`.
3000
3188
  */
3001
3189
  function bringCurrent(cell: Cell): void {
3002
3190
  if (cell.owes !== OWES_NOTHING) settleOwed(cell)
@@ -3036,8 +3224,7 @@ function settled(cell: Cell, untilStreaming = false): Promise<unknown> {
3036
3224
  // ONLY ONCE THE TIMER HAS DROPPED IT. The read's half of 6.5 — a production still served past
3037
3225
  // its deadline — is not taken here, because 16.7's `ttl` of 0 makes every mutation's production
3038
3226
  // past its deadline the moment it lands, and the await that answers the call ran it again.
3039
- if (cell.owes !== OWES_NOTHING) settleOwed(cell)
3040
- if (cell.state !== CLEAN) resolve(cell)
3227
+ bringCurrent(cell)
3041
3228
  if (cell.expiry?.dropped) expire(cell)
3042
3229
  if (untilStreaming && cell.stream !== null)
3043
3230
  return Promise.resolve(STREAMING)
@@ -3288,12 +3475,9 @@ function invalidateOne(cell: Cell): void {
3288
3475
  if (!reloads(cell)) return
3289
3476
  // Not directly servable, so the next read goes a frame in and settles the debt.
3290
3477
  cell.direct = false
3291
- // 7.15: work already in flight was started from what this says is wrong. A load's answer
3292
- // lands nowhere once `generation` has moved; a stream is stopped outright.
3293
- cell.generation += 1
3294
- cell.loading = false
3295
- const stream = cell.stream
3296
- if (stream !== null && stream.source === null) stopStream(stream)
3478
+ // 7.15: work already in flight was started from what this says is wrong — and 7.3 moves no
3479
+ // probe for it.
3480
+ supersede(cell)
3297
3481
  cell.owes = OWES_RELOAD
3298
3482
  owedBelow(cell)
3299
3483
  }
@@ -3409,10 +3593,7 @@ export function startDeferred(value: unknown): void {
3409
3593
  * and a reader still holding it hears that.
3410
3594
  */
3411
3595
  export function abandonDeferred(value: unknown): void {
3412
- const cell = (value as Callable)[CELL]
3413
- if (!cell.loading) return
3414
- cell.loading = false
3415
- probesMoved(cell)
3596
+ endLoad((value as Callable)[CELL], false)
3416
3597
  }
3417
3598
 
3418
3599
  // ── `store` ──────────────────────────────────────────────────────────────────────────────
@@ -3465,7 +3646,7 @@ function restoreFirst(
3465
3646
  settleRestore(cell, persisted, answer, initial)
3466
3647
  return
3467
3648
  }
3468
- cell.loading = true
3649
+ beginLoad(cell)
3469
3650
  const generation = cell.generation
3470
3651
  answer.then(
3471
3652
  (value) => {
@@ -3498,10 +3679,7 @@ function settleRestore(
3498
3679
  if (cell.body !== null) startProducer(cell)
3499
3680
  else if (initial === undefined) landOpening(cell)
3500
3681
  else if (initial !== NO_INITIAL) open(cell, initial)
3501
- else if (cell.loading) {
3502
- cell.loading = false
3503
- probesMoved(cell)
3504
- }
3682
+ else endLoad(cell, false)
3505
3683
  }
3506
3684
 
3507
3685
  /**
@@ -3538,8 +3716,7 @@ function restoreStream(cell: Cell, stream: Stream, chunks: unknown[]): void {
3538
3716
  stream.version += 1
3539
3717
  const moved = settleProbes(cell)
3540
3718
  landChunks(cell)
3541
- cascade(cell)
3542
- if (moved) probesMoved(cell)
3719
+ land(cell, moved)
3543
3720
  // 8.7 SAYS THE BODY NEVER STARTS, AND `pump` IS WHAT REACHES `closeRun` — so the
3544
3721
  // restored accumulation is entered as a closed run here or nothing ever does it.
3545
3722
  // Without this `standingLive` answers false for the life of the value, which takes
@@ -3618,13 +3795,7 @@ function settleMiss(cell: Cell): void {
3618
3795
  )
3619
3796
  return
3620
3797
  }
3621
- const wasServable = cell.servable
3622
- const wasLoading = cell.loading
3623
- cell.servable = true
3624
- cell.loading = false
3625
- setDirect(cell, cell.state === CLEAN)
3626
- if (cell.servable !== wasServable) cascade(cell)
3627
- if (wasLoading || cell.servable !== wasServable) probesMoved(cell)
3798
+ endLoad(cell, serveAgain(cell))
3628
3799
  }
3629
3800
 
3630
3801
  /**
@@ -4034,20 +4205,7 @@ const MEMBERS = {
4034
4205
  return watchCell(this[CELL], effect)
4035
4206
  },
4036
4207
  [Symbol.asyncIterator](this: Callable): AsyncIterator<unknown> {
4037
- const cell = this[CELL]
4038
- // 2.5: "the production in flight, from its start". Starting one ordinal back is
4039
- // what includes it rather than skipping to whatever comes next — and clamped at
4040
- // zero, where a value nothing has produced into must WAIT rather than hand over
4041
- // the `undefined` it is holding.
4042
- // A STREAM'S PRODUCTION IN FLIGHT IS THE STREAM, so its start is the run's first
4043
- // chunk rather than the latest one.
4044
- const from =
4045
- cell.stream !== null
4046
- ? cell.ringBase
4047
- : cell.ordinal > 0
4048
- ? cell.ordinal - 1
4049
- : 0
4050
- return new Cursor(cell, from)
4208
+ return producedRun(this[CELL])
4051
4209
  },
4052
4210
  // biome-ignore lint/suspicious/noThenProperty: RULEBOOK 1.4 — a `Reactive` MUST be thenable, which is what this rule warns about. Banning it buys a discrimination rather than a semantics, and 16.4's in-process `Rpc` arm is what that discrimination would have cost. 1.6 is the guard that makes thenability safe — the brand is read before `then`.
4053
4211
  then(
@@ -4069,12 +4227,13 @@ const MEMBERS = {
4069
4227
  }
4070
4228
 
4071
4229
  /**
4072
- * The cell `value` serves from, through every adoption.
4230
+ * The cell `value` serves from, through every adoption: its identity and its ordinal, and nothing
4231
+ * else of it the server can reach.
4073
4232
  *
4074
4233
  * 11.12: an adopting value serves the adopted value's production, and an adoption of an adoption
4075
4234
  * goes on down — so a follow's version (18.7) and its feed name the cell that actually produced.
4076
4235
  */
4077
- export function servedCellOf(value: unknown): Retained {
4236
+ export function servedCellOf(value: unknown): { readonly ordinal: number } {
4078
4237
  return adoptedCellOf(value)
4079
4238
  }
4080
4239
 
@@ -4098,10 +4257,16 @@ export function deadlineOf(value: unknown): number {
4098
4257
  * 16.48 — WHICH LOAD A VALUE HAS IN FLIGHT, as the generation that started it, or `undefined` where
4099
4258
  * none is. Read off the cell rather than through `busy`, which is a probe and would subscribe the
4100
4259
  * caller that asked (3.3).
4260
+ *
4261
+ * A WINDOW HOLDING A PRODUCTION IS BUSY AND IS NO LOAD (5.15): nothing is in flight for a call to end
4262
+ * or time, and the window's close is its own bound. Told apart by the hold, which only a value with
4263
+ * neither body nor stream makes — so a write's load taken in under a hold is not one a call times.
4101
4264
  */
4102
4265
  export function loadOf(value: unknown): number | undefined {
4103
4266
  const cell = (value as Callable)[CELL]
4104
- return cell.loading ? cell.generation : undefined
4267
+ if (!cell.loading) return undefined
4268
+ if (cell.cap?.holding && cell.stream === null) return undefined
4269
+ return cell.generation
4105
4270
  }
4106
4271
 
4107
4272
  /**
@@ -4138,8 +4303,7 @@ export function failedTransiently(value: unknown): boolean {
4138
4303
  export function abandonLoad(value: unknown, failure: unknown): void {
4139
4304
  const cell = (value as Callable)[CELL]
4140
4305
  if (!cell.loading) return
4141
- cell.generation += 1
4142
- if (cell.stream !== null) stopStream(cell.stream)
4306
+ supersede(cell)
4143
4307
  refused(cell, failure)
4144
4308
  }
4145
4309
 
@@ -4166,33 +4330,10 @@ export function isStreaming(value: unknown): boolean {
4166
4330
  return stream !== null && stream !== undefined
4167
4331
  }
4168
4332
 
4169
- /**
4170
- * The chunks a streaming value has told its readers of, from index `from` on.
4171
- *
4172
- * 16.32 — THE CHUNKS A STREAMING VALUE HAS TOLD ITS READERS OF PAST `from`, for a writer sending each
4173
- * once. Ungated they are read out of the accumulation in place, to `visible` rather than its length
4174
- * (5.18): a read materializes a copy of the whole of it (9.1), which for a writer woken per chunk is
4175
- * n²/2 elements copied to send n. A schema or a transform has to see the whole accumulation (9.8),
4176
- * so a gated value answers from what a read would.
4177
- */
4178
- export function* chunksFrom(value: unknown, from: number): Generator<unknown> {
4179
- const cell = (value as Callable)[CELL]
4180
- if (cell.schema === null && cell.transform === null) {
4181
- const stream = cell.stream as Stream
4182
- for (let index = from; index < stream.visible; index += 1)
4183
- yield stream.chunks[index]
4184
- return
4185
- }
4186
- const held = MEMBERS.peek.call(value as Callable)
4187
- if (Array.isArray(held))
4188
- for (let index = from; index < held.length; index += 1)
4189
- yield held[index]
4190
- }
4191
-
4192
4333
  function adoptedCellOf(value: unknown): Cell {
4193
4334
  let cell = (value as Callable)[CELL]
4194
4335
  // A stream's own `adoption` is a write it follows (9.11), and its past is its own.
4195
- while (cell.adoption !== null && cell.stream?.source !== null)
4336
+ while (cell.adoption !== null && ownStream(cell) === null)
4196
4337
  cell = cell.adoption.source
4197
4338
  return cell
4198
4339
  }
@@ -4347,8 +4488,7 @@ const SIMPLE_PROTOTYPE = brandedPrototype({
4347
4488
  return undefined
4348
4489
  }
4349
4490
  cell.value = value
4350
- // 6.10: an `undefined` is served and moves no ordinal.
4351
- if (value !== undefined) cell.ordinal += 1
4491
+ cell.ordinal += 1
4352
4492
  // `setDirect`, with `plain` known true by `simple`.
4353
4493
  cell.direct = cell.state === CLEAN
4354
4494
  if (cell.readers.length !== 0) markReaders(cell, DIRTY)
@@ -4386,7 +4526,8 @@ function orphan(cell: Cell): void {
4386
4526
  function releaseOrphan(cell: Cell): void {
4387
4527
  cell.orphaned = false
4388
4528
  const stream = cell.stream
4389
- if (stream !== null && stream.source === null) stopStream(stream)
4529
+ const own = ownStream(cell)
4530
+ if (own !== null) stopStream(own)
4390
4531
  dropAdoption(cell)
4391
4532
  cell.aborted = false
4392
4533
  if (cell.body !== null && stream === null) {
@@ -4466,14 +4607,7 @@ export function createReactive<
4466
4607
  // BEFORE THE FIELD, not after: an engine keys its inline caches on the shape a
4467
4608
  // property was added to, and re-pointing the prototype afterwards invalidates the
4468
4609
  // transition the store just built.
4469
- Object.setPrototypeOf(
4470
- self,
4471
- streaming
4472
- ? STREAM_PROTOTYPE
4473
- : cell.simple
4474
- ? SIMPLE_PROTOTYPE
4475
- : MEMBER_PROTOTYPE,
4476
- )
4610
+ Object.setPrototypeOf(self, seatOf(cell))
4477
4611
  self[CELL] = cell
4478
4612
  cell.callable = self as unknown as Reactive<unknown>
4479
4613
  if (cell.tags !== null) tagCell(cell)