@abide/abide 0.104.0 → 0.105.1

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 (85) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/bin/abide.ts +80 -173
  3. package/docs/REGISTRY.md +17 -17
  4. package/docs/RULEBOOK.md +50 -12
  5. package/docs/abide.md +9 -9
  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 +45 -86
  10. package/src/compiler/appLayout.ts +447 -0
  11. package/src/compiler/appMap.ts +21 -20
  12. package/src/compiler/checkApp.ts +61 -97
  13. package/src/compiler/checkedShadow.ts +11 -9
  14. package/src/compiler/classifyRead.ts +3 -6
  15. package/src/compiler/compileApp.ts +180 -512
  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/readSources.ts +27 -42
  25. package/src/compiler/readsIn.ts +5 -16
  26. package/src/compiler/resolverIn.ts +77 -0
  27. package/src/server/abideModules.ts +92 -123
  28. package/src/server/answerRequest.ts +18 -4
  29. package/src/server/answerSocket.ts +136 -76
  30. package/src/server/answerValue.ts +28 -15
  31. package/src/server/appTree.ts +49 -71
  32. package/src/server/claudeApi.ts +2 -5
  33. package/src/server/clientBundle.ts +198 -0
  34. package/src/server/coerceArgs.ts +1 -1
  35. package/src/server/commands.ts +93 -57
  36. package/src/server/createApp.ts +90 -16
  37. package/src/server/deferredBuilds.ts +175 -0
  38. package/src/server/devWorker.ts +107 -319
  39. package/src/server/faceOf.ts +120 -0
  40. package/src/server/fenceServer.ts +10 -18
  41. package/src/server/index.ts +3 -0
  42. package/src/server/placement.ts +167 -0
  43. package/src/server/remote.ts +14 -42
  44. package/src/server/render.ts +25 -17
  45. package/src/server/renderComponent.ts +9 -5
  46. package/src/server/requestScope.ts +10 -4
  47. package/src/server/responseRelay.ts +389 -0
  48. package/src/server/runCompiled.ts +9 -12
  49. package/src/server/runDesktop.ts +6 -9
  50. package/src/server/serveBuild.ts +26 -73
  51. package/src/server/serveDev.ts +19 -176
  52. package/src/server/streamEvent.ts +29 -0
  53. package/src/server/withAbideSources.ts +2 -1
  54. package/src/shared/HandlerRoute.ts +68 -0
  55. package/src/shared/REFUSAL_KEY.ts +5 -0
  56. package/src/shared/answerOf.ts +105 -78
  57. package/src/shared/filledPath.ts +1 -9
  58. package/src/shared/framing.ts +127 -128
  59. package/src/shared/queryText.ts +15 -7
  60. package/src/shared/reactive/Cursor.ts +4 -2
  61. package/src/shared/reactive/Reactive.ts +285 -197
  62. package/src/shared/reactive/index.ts +11 -8
  63. package/src/shared/reactive/pairReactive.ts +1 -1
  64. package/src/shared/reactive/scoped.ts +4 -3
  65. package/src/shared/reactive/share.ts +11 -1
  66. package/src/shared/routePattern.ts +0 -123
  67. package/src/shared/rpc.ts +17 -24
  68. package/src/shared/rpcRequest.ts +18 -47
  69. package/src/shared/takeEarlyCall.ts +12 -37
  70. package/src/shared/url.ts +1 -1
  71. package/src/ui/Level.ts +276 -0
  72. package/src/ui/List.ts +17 -17
  73. package/src/ui/Router.ts +20 -87
  74. package/src/ui/applyHead.ts +92 -79
  75. package/src/ui/index.ts +4 -2
  76. package/src/ui/leave.ts +5 -0
  77. package/src/ui/reconcile.ts +23 -104
  78. package/src/ui/regions.ts +27 -2
  79. package/src/ui/rows.ts +123 -2
  80. package/src/ui/wire.ts +12 -27
  81. package/src/compiler/hooksCalled.ts +0 -35
  82. package/src/server/clientBuildOptions.ts +0 -54
  83. package/src/server/refuseAddressCollision.ts +0 -37
  84. package/src/shared/reactive/chunksOfStream.ts +0 -33
  85. package/src/ui/MOUNTING_LEVEL.ts +0 -18
@@ -19,13 +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 {
23
- CAUGHT_UP,
24
- Cursor,
25
- hasCursorBehind,
26
- keepPlaces,
27
- type Retained,
28
- } from './Cursor.ts'
22
+ import { CAUGHT_UP, Cursor, hasCursorBehind, keepPlaces } from './Cursor.ts'
29
23
  import { ENTRY_LIVES } from './ENTRY_LIVES.ts'
30
24
  import { armTimer, type Expiry, expiryFor } from './Expiry.ts'
31
25
  import { FOLLOWED } from './FOLLOWED.ts'
@@ -488,6 +482,17 @@ function newStream(source: Cell | null): Stream {
488
482
  }
489
483
  }
490
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
+
491
496
  /** Turns `cell` into a streaming producer, forwarding `source`'s stream when it has one. */
492
497
  function becomeStream(cell: Cell, source: Cell | null): Stream {
493
498
  const stream = newStream(source)
@@ -495,6 +500,7 @@ function becomeStream(cell: Cell, source: Cell | null): Stream {
495
500
  cell.plain = false
496
501
  cell.bareWrite = false
497
502
  simpleShape(cell)
503
+ seat(cell)
498
504
  cell.direct = false
499
505
  return stream
500
506
  }
@@ -995,14 +1001,10 @@ function comparatorFor(
995
1001
  /**
996
1002
  * Recomputes `simple` and re-seats the callable on the matching prototype.
997
1003
  *
998
- * `simple` IN ONE PLACE, and the prototype that goes with it. A second copy of this
999
- * conjunction is the drift that would let one of the four sites go on reading `true` after
1000
- * the field under it moved — and what the short write would then skip is a cap, a ring or a
1001
- * declared `identity`, silently.
1002
- *
1003
- * THE CALLABLE IS RE-SEATED ONLY WHERE IT IS ON ONE OF THESE TWO. A value that is adopting or
1004
- * forwarding a stream carries a prototype of its own and keeps it; the field is still
1005
- * 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.
1006
1008
  */
1007
1009
  function simpleShape(cell: Cell): void {
1008
1010
  cell.simple =
@@ -1012,14 +1014,38 @@ function simpleShape(cell: Cell): void {
1012
1014
  cell.cap === null &&
1013
1015
  cell.store === null &&
1014
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 {
1015
1037
  const callable = cell.callable
1016
1038
  if (callable === null) return
1017
- const seat = Object.getPrototypeOf(callable)
1018
- if (seat !== MEMBER_PROTOTYPE && seat !== SIMPLE_PROTOTYPE) return
1019
- Object.setPrototypeOf(
1020
- callable as object,
1021
- 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
1022
1045
  )
1046
+ return
1047
+ const next = seatOf(cell)
1048
+ if (next !== now) Object.setPrototypeOf(callable as object, next)
1023
1049
  }
1024
1050
 
1025
1051
  /**
@@ -1502,19 +1528,8 @@ function produce(cell: Cell, produced: unknown): number {
1502
1528
  // collapses was still accepted — it produced nothing because the value was already
1503
1529
  // the one being written, not because it failed. Leaving the failure standing left
1504
1530
  // `s.error()` reporting a load that a later write superseded.
1505
- const wasServable = cell.servable
1506
1531
  const settling = settleProbes(cell)
1507
- cell.servable = true
1508
- setDirect(cell, cell.state === CLEAN)
1509
- // THE VALUE EDGE MOVED TOO WHERE THERE WAS NOTHING TO SERVE, whatever the gate
1510
- // says about the payload — the same split `refused` makes, and for the same
1511
- // reason. A read taken while this was unservable THREW, so a body that reached
1512
- // it aborted: it is `CLEAN`, holds nothing, and nothing downstream will ever
1513
- // pull it. Left silent, an `invalidate` whose reload landed the value it already
1514
- // held stranded every value built from it at `ready()` false for the life of the
1515
- // page. Over a value still being served nothing a reader can see moved (5.1) and
1516
- // this stays quiet, which is the arm 5.2 is about.
1517
- if (!wasServable) cascade(cell)
1532
+ serveAgain(cell)
1518
1533
  if (settling) probesMoved(cell)
1519
1534
  return cell.ordinal
1520
1535
  }
@@ -1539,8 +1554,7 @@ function produce(cell: Cell, produced: unknown): number {
1539
1554
  if (cap !== null && cell.body === null)
1540
1555
  return capped(cell, cap, produced, moved)
1541
1556
  serve(cell, produced)
1542
- cascade(cell)
1543
- if (moved) probesMoved(cell)
1557
+ land(cell, moved)
1544
1558
  return cell.ordinal
1545
1559
  }
1546
1560
 
@@ -1559,6 +1573,56 @@ function settleProbes(cell: Cell): boolean {
1559
1573
  return moved
1560
1574
  }
1561
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
+
1562
1626
  /**
1563
1627
  * Makes a production what readers see: stores it, arms its deadline, persists it.
1564
1628
  *
@@ -1653,8 +1717,7 @@ function capped(
1653
1717
  // collapses a RUN of changes and the first landing has nothing to collapse with.
1654
1718
  if (wakes(cell, cap, produced)) {
1655
1719
  serve(cell, produced)
1656
- cascade(cell)
1657
- if (moved) probesMoved(cell)
1720
+ land(cell, moved)
1658
1721
  }
1659
1722
  return cell.ordinal
1660
1723
  }
@@ -1686,9 +1749,27 @@ function release(cell: Cell, cap: Cap): void {
1686
1749
  cap.held = undefined
1687
1750
  cell.loading = false
1688
1751
  serve(cell, value)
1689
- cascade(cell)
1690
1752
  // The close is a `busy` edge (5.15) even though the value's readiness never moved.
1691
- 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
1692
1773
  }
1693
1774
 
1694
1775
  function releaseHeld(cell: Cell, cap: Cap): void {
@@ -1861,10 +1942,10 @@ function standWrite(cell: Cell, stream: Stream, value: unknown): number {
1861
1942
  cell.value = value
1862
1943
  stream.materializedAt = stream.version
1863
1944
  stream.written = true
1945
+ // `busy` is the run's, which a write stands beside (3.7), so only `ready` and `error` move.
1864
1946
  const moved = !cell.servable || cell.failure !== undefined
1865
1947
  landChunks(cell)
1866
- cascade(cell)
1867
- if (moved) probesMoved(cell)
1948
+ land(cell, moved)
1868
1949
  return cell.ordinal
1869
1950
  }
1870
1951
 
@@ -1991,7 +2072,7 @@ function adopt(cell: Cell, inner: Cell): Error | undefined {
1991
2072
  // 11.59: an adopting value's writes are the adopted value's, reached through a prototype of its
1992
2073
  // own rather than a branch on every value's write path — see `acceptIntoStream` for what such a
1993
2074
  // branch costs under V8 even where nothing takes it.
1994
- Object.setPrototypeOf(cell.callable as object, ADOPTING_PROTOTYPE)
2075
+ seat(cell)
1995
2076
  if (!cell.loading && busyNow(inner) !== wasBusy) probesMoved(cell)
1996
2077
  return undefined
1997
2078
  }
@@ -2090,6 +2171,7 @@ function mirror(cell: Cell, inner: Cell): void {
2090
2171
  if (cell.stream !== null) stopForwarding(cell)
2091
2172
  if (!inner.servable) {
2092
2173
  // 11.16: the newly adopted has nothing to serve, so neither has this.
2174
+ if (dropHeld(cell)) probesMoved(cell)
2093
2175
  unserve(cell)
2094
2176
  return
2095
2177
  }
@@ -2120,20 +2202,16 @@ function mirror(cell: Cell, inner: Cell): void {
2120
2202
  function dropAdoption(cell: Cell): void {
2121
2203
  if (cell.adoption === null) return
2122
2204
  unfollow(cell)
2123
- const stream = cell.stream
2124
2205
  // 9.11: AN OWN STREAM'S `adoption` IS A WRITE IT FOLLOWS, not a stream it forwards — the
2125
2206
  // discriminator `adoptedCellOf` reads. Letting go of the write is all there is: the stream is
2126
2207
  // its own, so is its prototype, and `follow` never touched `identityFn`. Stopping it as a
2127
2208
  // forward nulled the stream a queued `wakeStreams` was about to read.
2128
- if (stream !== null && stream.source === null) return
2129
- if (stream !== null) stopForwarding(cell)
2209
+ if (ownStream(cell) !== null) return
2210
+ if (cell.stream !== null) stopForwarding(cell)
2130
2211
  // 11.17 inherited the identity for the length of the adoption only.
2131
2212
  cell.identityFn = cell.declaredIdentityFn
2132
2213
  simpleShape(cell)
2133
- Object.setPrototypeOf(
2134
- cell.callable as object,
2135
- cell.simple ? SIMPLE_PROTOTYPE : MEMBER_PROTOTYPE,
2136
- )
2214
+ seat(cell)
2137
2215
  }
2138
2216
 
2139
2217
  /**
@@ -2147,6 +2225,8 @@ function dropAdoption(cell: Cell): void {
2147
2225
  function forwardStream(cell: Cell, inner: Cell, adopted: Stream): void {
2148
2226
  let stream = cell.stream
2149
2227
  if (stream === null) {
2228
+ // The `busy` the hold left is settled below, with the adopter's own load.
2229
+ dropHeld(cell)
2150
2230
  stream = becomeStream(cell, inner)
2151
2231
  stream.ordinalOffset = cell.ordinal - inner.ringBase
2152
2232
  }
@@ -2172,8 +2252,7 @@ function forwardStream(cell: Cell, inner: Cell, adopted: Stream): void {
2172
2252
  // on answering through the adoption (11.19) for as long as the stream is open.
2173
2253
  const moved = settleProbes(cell)
2174
2254
  landChunks(cell)
2175
- cascade(cell)
2176
- if (moved) probesMoved(cell)
2255
+ land(cell, moved)
2177
2256
  }
2178
2257
 
2179
2258
  function stopForwarding(cell: Cell): void {
@@ -2234,17 +2313,7 @@ function run(cell: Cell): void {
2234
2313
  truncateProbes(reader)
2235
2314
  cell.running = false
2236
2315
  if (thrown instanceof NotLanded) {
2237
- cell.loading = true
2238
- // 2.13: the mark a source's production makes is what finishes this run, and
2239
- // `markReaders` queues it rather than leaving it for a read that may never come.
2240
- cell.aborted = true
2241
- // NOT EVERYTHING IT READS IS READY, so neither is this — 11.58, and the same
2242
- // sentence 11.16 makes for a re-adoption. It stops serving rather than handing
2243
- // back a figure it computed from a source that has since gone away, and that
2244
- // is a move its readers have to hear even though the aborted run minted
2245
- // nothing for a cascade to carry.
2246
- if (cell.servable) unserve(cell)
2247
- else probesMoved(cell)
2316
+ abortRun(cell)
2248
2317
  return
2249
2318
  }
2250
2319
  refused(cell, thrown)
@@ -2325,6 +2394,23 @@ function run(cell: Cell): void {
2325
2394
  absorb(cell, produced, true)
2326
2395
  }
2327
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
+
2328
2414
  /**
2329
2415
  * Adopts what a body handed back, refusing the body where that would close a cycle.
2330
2416
  *
@@ -2348,8 +2434,7 @@ function landOpening(cell: Cell): void {
2348
2434
  cell.landed = true
2349
2435
  cell.servable = true
2350
2436
  setDirect(cell, cell.state === CLEAN)
2351
- cascade(cell)
2352
- if (moved) probesMoved(cell)
2437
+ land(cell, moved)
2353
2438
  }
2354
2439
 
2355
2440
  /**
@@ -2361,11 +2446,23 @@ function landOpening(cell: Cell): void {
2361
2446
  * be marked by (14.13).
2362
2447
  */
2363
2448
  function startProducer(cell: Cell): void {
2364
- const stream = cell.stream
2365
- if (stream === null || stream.source !== null) run(cell)
2449
+ const stream = ownStream(cell)
2450
+ if (stream === null) run(cell)
2366
2451
  else startStream(cell, stream)
2367
2452
  }
2368
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
+
2369
2466
  /**
2370
2467
  * Supersedes the run in flight and tells its iterator to return.
2371
2468
  *
@@ -2399,10 +2496,7 @@ function startStream(cell: Cell, stream: Stream): void {
2399
2496
  */
2400
2497
  function promoteToStream(cell: Cell, generator: AsyncIterable<unknown>): void {
2401
2498
  let stream = cell.stream
2402
- if (stream === null) {
2403
- stream = becomeStream(cell, null)
2404
- Object.setPrototypeOf(cell.callable as object, STREAM_PROTOTYPE)
2405
- }
2499
+ if (stream === null) stream = becomeStream(cell, null)
2406
2500
  pumpRun(cell, stream, generator[Symbol.asyncIterator]())
2407
2501
  }
2408
2502
 
@@ -2488,7 +2582,6 @@ async function pump(
2488
2582
  cap.holding = false
2489
2583
  releaseChunks(cell, stream)
2490
2584
  }
2491
- const moved = !begun || cell.loading
2492
2585
  if (!begun) {
2493
2586
  // A stream that closed with nothing in it has finished, and what it accumulated is
2494
2587
  // no chunks — a value, where waiting on would be a load that never ends.
@@ -2496,9 +2589,8 @@ async function pump(
2496
2589
  landChunks(cell)
2497
2590
  cascade(cell)
2498
2591
  }
2499
- cell.loading = false
2500
2592
  // 3.7: `busy` held until the close, and this is the close.
2501
- if (moved) probesMoved(cell)
2593
+ endLoad(cell, !begun)
2502
2594
  // 6.10, AFTER THE CASCADE ABOVE: the entry is what a read would answer with, so it is taken
2503
2595
  // once the last chunk has landed and the value is servable.
2504
2596
  closeRun(cell, stream)
@@ -2635,7 +2727,7 @@ function materialize(cell: Cell, stream: Stream): void {
2635
2727
  // 9.11: the accumulation moved past a write following a `Reactive`, which lets go of it here
2636
2728
  // where no chunk's wake did — a patch, or a run that closed empty. Only an own stream follows;
2637
2729
  // a forwarding one's `adoption` is what it forwards.
2638
- if (cell.adoption !== null && stream.source === null) unfollow(cell)
2730
+ if (cell.adoption !== null && ownStream(cell) !== null) unfollow(cell)
2639
2731
  stream.materializedAt = stream.version
2640
2732
  stream.written = false
2641
2733
  gateInto(cell, stream.chunks.slice(0, stream.visible))
@@ -2685,6 +2777,97 @@ function heldValue(cell: Cell): unknown {
2685
2777
  return cell.value
2686
2778
  }
2687
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
+
2688
2871
  /**
2689
2872
  * Releases the standing production at its deadline, waking nobody.
2690
2873
  *
@@ -2839,8 +3022,7 @@ function expire(cell: Cell): void {
2839
3022
  * which is what lets the flag be conservative.
2840
3023
  */
2841
3024
  function readIndirect(cell: Cell): unknown {
2842
- if (cell.owes !== OWES_NOTHING) settleOwed(cell)
2843
- if (cell.state !== CLEAN) resolve(cell)
3025
+ bringCurrent(cell)
2844
3026
  // 6.5: THE READ THAT FOLLOWS. Every value carrying an expiry arrives here and no
2845
3027
  // other value does, which is the whole of what `setDirect` buys.
2846
3028
  if (cell.expiry !== null) expire(cell)
@@ -2940,53 +3122,6 @@ function failureOf(cell: Cell): unknown {
2940
3122
  return at.failure
2941
3123
  }
2942
3124
 
2943
- /**
2944
- * What one run of `cell` produced, as `for await` walks it.
2945
- *
2946
- * 2.5 AND 2.6 — the settling, yielded once, or a stream's chunks from the first to the close. Which
2947
- * of the two is asked of the settling, since a value may only turn out to stream once its load
2948
- * resolves (9.6).
2949
- *
2950
- * THE CURSOR IS THE WAKE AND THE SETTLING IS THE END (`Cursor.closeOn`).
2951
- */
2952
- async function* producedRun(cell: Cell): AsyncGenerator<unknown> {
2953
- const first = await settled(cell, true)
2954
- if (first !== STREAMING) {
2955
- yield first
2956
- return
2957
- }
2958
- // THE RUN IS ITS RING: a new run's first chunk replaces the array, where a patch mutates it in
2959
- // place (9.13) and only moves the base. A run with no chunk yet holds `EMPTY_RING`.
2960
- let ring = cell.ring
2961
- // 9.15 — a failure already standing is a revalidation's (2.4), and not this run's.
2962
- const standing = failureOf(cell)
2963
- const cursor = new Cursor(cell, cell.ringBase)
2964
- cursor.closeOn(settled(cell))
2965
- try {
2966
- for (;;) {
2967
- if (cell.ring !== ring) {
2968
- // 9.16.
2969
- if (ring !== EMPTY_RING) return
2970
- ring = cell.ring
2971
- }
2972
- const chunk = cursor.take()
2973
- if (chunk !== CAUGHT_UP) {
2974
- yield chunk
2975
- continue
2976
- }
2977
- if (cursor.closed) break
2978
- await cursor.turn()
2979
- }
2980
- } finally {
2981
- cursor.stop()
2982
- }
2983
- if (cursor.failure !== undefined) throw cursor.failure
2984
- // 9.15 AND 16.59 — a run that failed after its first chunk, or one its timeout ended, settles
2985
- // with what it had; the refusal stands beside it.
2986
- const ended = failureOf(cell)
2987
- if (ended !== undefined && ended !== standing) throw ended
2988
- }
2989
-
2990
3125
  /**
2991
3126
  * `busy` without subscribing.
2992
3127
  *
@@ -3042,12 +3177,14 @@ function isReady(cell: Cell): boolean {
3042
3177
  }
3043
3178
 
3044
3179
  /**
3045
- * 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.
3046
3183
  *
3047
3184
  * 3.2 — A PROBE ANSWERS ABOUT THE VALUE A READ WOULD SERVE. Under push-pull a write leaves every
3048
3185
  * derived value it reached unrun, so a probe that walked what the last run left answered about a
3049
3186
  * value nothing would serve: `x.ready()` true over a body whose inputs had moved to a load, and the
3050
- * read the guard admitted threw `NotLanded`. `peek` pulls for the same reason.
3187
+ * read the guard admitted threw `NotLanded`.
3051
3188
  */
3052
3189
  function bringCurrent(cell: Cell): void {
3053
3190
  if (cell.owes !== OWES_NOTHING) settleOwed(cell)
@@ -3087,8 +3224,7 @@ function settled(cell: Cell, untilStreaming = false): Promise<unknown> {
3087
3224
  // ONLY ONCE THE TIMER HAS DROPPED IT. The read's half of 6.5 — a production still served past
3088
3225
  // its deadline — is not taken here, because 16.7's `ttl` of 0 makes every mutation's production
3089
3226
  // past its deadline the moment it lands, and the await that answers the call ran it again.
3090
- if (cell.owes !== OWES_NOTHING) settleOwed(cell)
3091
- if (cell.state !== CLEAN) resolve(cell)
3227
+ bringCurrent(cell)
3092
3228
  if (cell.expiry?.dropped) expire(cell)
3093
3229
  if (untilStreaming && cell.stream !== null)
3094
3230
  return Promise.resolve(STREAMING)
@@ -3339,12 +3475,9 @@ function invalidateOne(cell: Cell): void {
3339
3475
  if (!reloads(cell)) return
3340
3476
  // Not directly servable, so the next read goes a frame in and settles the debt.
3341
3477
  cell.direct = false
3342
- // 7.15: work already in flight was started from what this says is wrong. A load's answer
3343
- // lands nowhere once `generation` has moved; a stream is stopped outright.
3344
- cell.generation += 1
3345
- cell.loading = false
3346
- const stream = cell.stream
3347
- 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)
3348
3481
  cell.owes = OWES_RELOAD
3349
3482
  owedBelow(cell)
3350
3483
  }
@@ -3460,10 +3593,7 @@ export function startDeferred(value: unknown): void {
3460
3593
  * and a reader still holding it hears that.
3461
3594
  */
3462
3595
  export function abandonDeferred(value: unknown): void {
3463
- const cell = (value as Callable)[CELL]
3464
- if (!cell.loading) return
3465
- cell.loading = false
3466
- probesMoved(cell)
3596
+ endLoad((value as Callable)[CELL], false)
3467
3597
  }
3468
3598
 
3469
3599
  // ── `store` ──────────────────────────────────────────────────────────────────────────────
@@ -3516,7 +3646,7 @@ function restoreFirst(
3516
3646
  settleRestore(cell, persisted, answer, initial)
3517
3647
  return
3518
3648
  }
3519
- cell.loading = true
3649
+ beginLoad(cell)
3520
3650
  const generation = cell.generation
3521
3651
  answer.then(
3522
3652
  (value) => {
@@ -3549,10 +3679,7 @@ function settleRestore(
3549
3679
  if (cell.body !== null) startProducer(cell)
3550
3680
  else if (initial === undefined) landOpening(cell)
3551
3681
  else if (initial !== NO_INITIAL) open(cell, initial)
3552
- else if (cell.loading) {
3553
- cell.loading = false
3554
- probesMoved(cell)
3555
- }
3682
+ else endLoad(cell, false)
3556
3683
  }
3557
3684
 
3558
3685
  /**
@@ -3589,8 +3716,7 @@ function restoreStream(cell: Cell, stream: Stream, chunks: unknown[]): void {
3589
3716
  stream.version += 1
3590
3717
  const moved = settleProbes(cell)
3591
3718
  landChunks(cell)
3592
- cascade(cell)
3593
- if (moved) probesMoved(cell)
3719
+ land(cell, moved)
3594
3720
  // 8.7 SAYS THE BODY NEVER STARTS, AND `pump` IS WHAT REACHES `closeRun` — so the
3595
3721
  // restored accumulation is entered as a closed run here or nothing ever does it.
3596
3722
  // Without this `standingLive` answers false for the life of the value, which takes
@@ -3669,13 +3795,7 @@ function settleMiss(cell: Cell): void {
3669
3795
  )
3670
3796
  return
3671
3797
  }
3672
- const wasServable = cell.servable
3673
- const wasLoading = cell.loading
3674
- cell.servable = true
3675
- cell.loading = false
3676
- setDirect(cell, cell.state === CLEAN)
3677
- if (cell.servable !== wasServable) cascade(cell)
3678
- if (wasLoading || cell.servable !== wasServable) probesMoved(cell)
3798
+ endLoad(cell, serveAgain(cell))
3679
3799
  }
3680
3800
 
3681
3801
  /**
@@ -4107,12 +4227,13 @@ const MEMBERS = {
4107
4227
  }
4108
4228
 
4109
4229
  /**
4110
- * 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.
4111
4232
  *
4112
4233
  * 11.12: an adopting value serves the adopted value's production, and an adoption of an adoption
4113
4234
  * goes on down — so a follow's version (18.7) and its feed name the cell that actually produced.
4114
4235
  */
4115
- export function servedCellOf(value: unknown): Retained {
4236
+ export function servedCellOf(value: unknown): { readonly ordinal: number } {
4116
4237
  return adoptedCellOf(value)
4117
4238
  }
4118
4239
 
@@ -4136,10 +4257,16 @@ export function deadlineOf(value: unknown): number {
4136
4257
  * 16.48 — WHICH LOAD A VALUE HAS IN FLIGHT, as the generation that started it, or `undefined` where
4137
4258
  * none is. Read off the cell rather than through `busy`, which is a probe and would subscribe the
4138
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.
4139
4264
  */
4140
4265
  export function loadOf(value: unknown): number | undefined {
4141
4266
  const cell = (value as Callable)[CELL]
4142
- 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
4143
4270
  }
4144
4271
 
4145
4272
  /**
@@ -4176,8 +4303,7 @@ export function failedTransiently(value: unknown): boolean {
4176
4303
  export function abandonLoad(value: unknown, failure: unknown): void {
4177
4304
  const cell = (value as Callable)[CELL]
4178
4305
  if (!cell.loading) return
4179
- cell.generation += 1
4180
- if (cell.stream !== null) stopStream(cell.stream)
4306
+ supersede(cell)
4181
4307
  refused(cell, failure)
4182
4308
  }
4183
4309
 
@@ -4204,42 +4330,10 @@ export function isStreaming(value: unknown): boolean {
4204
4330
  return stream !== null && stream !== undefined
4205
4331
  }
4206
4332
 
4207
- /**
4208
- * A cursor over `value` that reads nothing and only wakes when it changes.
4209
- *
4210
- * PLACED PAST EVERY CHUNK, so it holds no place a patch has to keep (5.7) and is never behind.
4211
- */
4212
- export function wakeOf(value: unknown): Cursor {
4213
- return new Cursor((value as Callable)[CELL], Number.POSITIVE_INFINITY)
4214
- }
4215
-
4216
- /**
4217
- * The chunks a streaming value has told its readers of, from index `from` on.
4218
- *
4219
- * 16.32 — THE CHUNKS A STREAMING VALUE HAS TOLD ITS READERS OF PAST `from`, for a writer sending each
4220
- * once. Ungated they are read out of the accumulation in place, to `visible` rather than its length
4221
- * (5.18): a read materializes a copy of the whole of it (9.1), which for a writer woken per chunk is
4222
- * n²/2 elements copied to send n. A schema or a transform has to see the whole accumulation (9.8),
4223
- * so a gated value answers from what a read would.
4224
- */
4225
- export function* chunksFrom(value: unknown, from: number): Generator<unknown> {
4226
- const cell = (value as Callable)[CELL]
4227
- if (cell.schema === null && cell.transform === null) {
4228
- const stream = cell.stream as Stream
4229
- for (let index = from; index < stream.visible; index += 1)
4230
- yield stream.chunks[index]
4231
- return
4232
- }
4233
- const held = MEMBERS.peek.call(value as Callable)
4234
- if (Array.isArray(held))
4235
- for (let index = from; index < held.length; index += 1)
4236
- yield held[index]
4237
- }
4238
-
4239
4333
  function adoptedCellOf(value: unknown): Cell {
4240
4334
  let cell = (value as Callable)[CELL]
4241
4335
  // A stream's own `adoption` is a write it follows (9.11), and its past is its own.
4242
- while (cell.adoption !== null && cell.stream?.source !== null)
4336
+ while (cell.adoption !== null && ownStream(cell) === null)
4243
4337
  cell = cell.adoption.source
4244
4338
  return cell
4245
4339
  }
@@ -4432,7 +4526,8 @@ function orphan(cell: Cell): void {
4432
4526
  function releaseOrphan(cell: Cell): void {
4433
4527
  cell.orphaned = false
4434
4528
  const stream = cell.stream
4435
- if (stream !== null && stream.source === null) stopStream(stream)
4529
+ const own = ownStream(cell)
4530
+ if (own !== null) stopStream(own)
4436
4531
  dropAdoption(cell)
4437
4532
  cell.aborted = false
4438
4533
  if (cell.body !== null && stream === null) {
@@ -4512,14 +4607,7 @@ export function createReactive<
4512
4607
  // BEFORE THE FIELD, not after: an engine keys its inline caches on the shape a
4513
4608
  // property was added to, and re-pointing the prototype afterwards invalidates the
4514
4609
  // transition the store just built.
4515
- Object.setPrototypeOf(
4516
- self,
4517
- streaming
4518
- ? STREAM_PROTOTYPE
4519
- : cell.simple
4520
- ? SIMPLE_PROTOTYPE
4521
- : MEMBER_PROTOTYPE,
4522
- )
4610
+ Object.setPrototypeOf(self, seatOf(cell))
4523
4611
  self[CELL] = cell
4524
4612
  cell.callable = self as unknown as Reactive<unknown>
4525
4613
  if (cell.tags !== null) tagCell(cell)