@abide/abide 0.123.3 → 0.124.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.
- package/ABIDE.md +8 -5
- package/CHANGELOG.md +31 -0
- package/docs/REGISTRY.md +45 -12
- package/docs/RULEBOOK.md +24 -14
- package/docs/abide.md +999 -459
- package/package.json +1 -1
- package/src/compiler/ARRAY_MUTATORS.ts +20 -0
- package/src/compiler/LoweredFile.ts +609 -0
- package/src/compiler/Lowering.ts +17 -0
- package/src/compiler/SHARED_NAMES.ts +2 -0
- package/src/compiler/ShadowHost.ts +670 -0
- package/src/compiler/ShadowMap.ts +1 -1
- package/src/compiler/TypeSession.ts +71 -1715
- package/src/compiler/appLints.ts +109 -0
- package/src/compiler/checkedShadow.ts +1 -1
- package/src/compiler/classifyRead.ts +16 -1
- package/src/compiler/compileApp.ts +2 -2
- package/src/compiler/declaredExports.ts +44 -17
- package/src/compiler/diagnoseSources.ts +232 -0
- package/src/compiler/formatAbide.ts +5 -6
- package/src/compiler/handlersUnder.ts +16 -35
- package/src/compiler/reactiveReads.ts +235 -0
- package/src/compiler/reactiveTypes.ts +13 -0
- package/src/compiler/readsIn.ts +180 -81
- package/src/compiler/routesDeclaration.ts +46 -0
- package/src/compiler/scaffold.ts +7 -2
- package/src/compiler/serveLanguage.ts +26 -20
- package/src/compiler/splitImports.ts +2 -2
- package/src/server/DesktopSession.ts +213 -0
- package/src/server/Followers.ts +399 -0
- package/src/server/MODULE_QUERIES.ts +6 -2
- package/src/server/PageRender.ts +201 -0
- package/src/server/abideModules.ts +7 -7
- package/src/server/answerFailure.ts +3 -2
- package/src/server/answerRequest.ts +6 -70
- package/src/server/answerRpc.ts +48 -260
- package/src/server/answerSocket.ts +41 -463
- package/src/server/appKit.ts +1 -1
- package/src/server/argsContract.ts +1 -1
- package/src/server/createApp.ts +8 -2
- package/src/server/csp.ts +1 -1
- package/src/server/deferredBuilds.ts +3 -16
- package/src/server/faceOf.ts +1 -1
- package/src/server/generatedSurfaces.ts +2 -1
- package/src/server/handlerFileMount.ts +1 -1
- package/src/server/index.ts +1 -1
- package/src/server/installServer.ts +5 -9
- package/src/server/invokeHandler.ts +230 -0
- package/src/server/mountHandlers.ts +184 -0
- package/src/server/openDesktopWindow.ts +308 -0
- package/src/server/preload.ts +18 -0
- package/src/server/principal.ts +1 -1
- package/src/server/remote.ts +1 -1
- package/src/server/render.ts +31 -59
- package/src/server/requestScope.ts +5 -36
- package/src/server/runDesktop.ts +19 -447
- package/src/server/seedBuffer.ts +11 -11
- package/src/server/servedApp.ts +1 -1
- package/src/server/shell.ts +1 -1
- package/src/server/surfaceOf.ts +1 -1
- package/src/server/versionOf.ts +1 -1
- package/src/shared/RPC_SERVER.ts +27 -31
- package/src/shared/Schema.ts +1 -2
- package/src/shared/index.ts +1 -1
- package/src/shared/{note.ts → issueReport.ts} +22 -0
- package/src/shared/loadCall.ts +66 -0
- package/src/shared/reactive/Keyed.ts +23 -15
- package/src/shared/reactive/Reactive.ts +34 -1001
- package/src/shared/reactive/byOf.ts +2 -5
- package/src/shared/reactive/callLoad.ts +8 -14
- package/src/shared/reactive/cellShape.ts +734 -0
- package/src/shared/reactive/index.ts +12 -24
- package/src/shared/reactive/pairReactive.ts +5 -3
- package/src/shared/reactive/producer.ts +121 -0
- package/src/shared/reactive/scoped.ts +3 -2
- package/src/shared/reactive/selections.ts +209 -0
- package/src/shared/reactive/watch.ts +3 -4
- package/src/shared/rpc.ts +8 -5
- package/src/shared/validateJson.ts +1 -2
- package/src/shared/writePath.ts +29 -0
- package/src/ui/Router.ts +53 -69
- package/src/ui/outboxQueue.ts +2 -8
- package/src/ui/outcomeOf.ts +120 -0
- package/src/ui/socketWire.ts +3 -3
- package/src/ui/wire.ts +9 -10
- package/src/shared/LOAD_OFFLINE.ts +0 -15
- package/src/shared/LOAD_SIGNAL.ts +0 -10
- package/src/shared/issuesOf.ts +0 -18
- package/src/shared/reactive/ENTRY_LIVES.ts +0 -7
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
// A VALUE'S RECORD, AND THE ONE PLACE ITS SHAPE IS DECLARED. `Cell` and the sub-records it holds
|
|
2
|
+
// (`Cap`, `Persisted`, `Stream`, `Adoption`) live here rather than in `Reactive.ts` so a sibling
|
|
3
|
+
// that reaches a value's record — `Keyed`, `watch`, `byOf`, the selection index, the producer port —
|
|
4
|
+
// names the real type instead of re-declaring the few fields it reads.
|
|
5
|
+
//
|
|
6
|
+
// NO RUNTIME EDGE BACK TO THE CORE. `Reactive.ts` imports this, and a module reaching this ahead
|
|
7
|
+
// of the core would otherwise enter the cycle `index.ts` names. That is why `notify` — a cell's
|
|
8
|
+
// wake, which is a run of the graph — is put on the prototype by `Reactive.ts` (see `run` there)
|
|
9
|
+
// rather than declared as a method here: the instance shape is the constructor's, and a prototype
|
|
10
|
+
// slot is not part of it.
|
|
11
|
+
|
|
12
|
+
import type { Schema } from '../Schema.ts'
|
|
13
|
+
import { CELL } from './CELL.ts'
|
|
14
|
+
import { type Expiry, expiryFor } from './Expiry.ts'
|
|
15
|
+
import { MUTATION } from './MUTATION.ts'
|
|
16
|
+
import type { Identity, Reactive, ReactiveOptions, Store } from './Reactive.ts'
|
|
17
|
+
import { currentScope, type Scope } from './scoping.ts'
|
|
18
|
+
import { CLEAN, type Subscriber, type Tracked } from './tracking.ts'
|
|
19
|
+
|
|
20
|
+
/** The standing adoption: what is adopted, and the way to stop mirroring it. */
|
|
21
|
+
export type Adoption = { source: Cell; stop: () => void }
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The window a `throttle` or a `debounce` holds open, allocated only where one is declared.
|
|
25
|
+
*
|
|
26
|
+
* THE WINDOW A `throttle` OR A `debounce` HOLDS OPEN (RULEBOOK 5.8). One field on the
|
|
27
|
+
* cell rather than one per window field, allocated only where a value declared one — the
|
|
28
|
+
* shape `adoption` already uses, and for the same reason.
|
|
29
|
+
*
|
|
30
|
+
* WHAT A WINDOW CAPS IS THE WAKE, NEVER THE PRODUCTION (5.9). Every write still mints and
|
|
31
|
+
* the ordinal moves. What waits is the value a READER sees and the cascade that tells it so,
|
|
32
|
+
* which is the whole of 5.8's "how often a value changes for a reader".
|
|
33
|
+
*
|
|
34
|
+
* WHAT IT COSTS EVERY VALUE THAT NEVER CAPS: one field load and one branch on the write
|
|
35
|
+
* path, plus `serve` as a frame rather than five stores inline. Measured at 0.99x -> 1.08x
|
|
36
|
+
* against the hand-written arm on the since-removed `publish into a capped ring` case, consistent across three
|
|
37
|
+
* runs over a 0.7% floor — about 1.3 ns on a 14.7 ns op. Moving the comparison target
|
|
38
|
+
* behind the gate did not recover it, so it is the frame and the branch rather than a
|
|
39
|
+
* wasted load. Inlining `serve` into the uncapped arm would buy it back for a second copy
|
|
40
|
+
* of the reader-visible transition, and this file has already paid for one of those: the
|
|
41
|
+
* trade is stated rather than taken, 1.3 ns being nowhere near perception.
|
|
42
|
+
*/
|
|
43
|
+
export type Cap = {
|
|
44
|
+
/**
|
|
45
|
+
* True for a throttle, which lets the leading change through; false for a debounce.
|
|
46
|
+
*
|
|
47
|
+
* 5.12 against 5.13, and the only difference between the two mechanisms: a throttle
|
|
48
|
+
* lets the leading change through and holds what follows, a debounce holds everything
|
|
49
|
+
* until the changes stop.
|
|
50
|
+
*/
|
|
51
|
+
leading: boolean
|
|
52
|
+
ms: number
|
|
53
|
+
timer: ReturnType<typeof setTimeout> | null
|
|
54
|
+
/**
|
|
55
|
+
* The production the window is holding back, while `holding` is set.
|
|
56
|
+
*
|
|
57
|
+
* The production the window is holding back. `holding` is a field rather than a test
|
|
58
|
+
* against `held`, because `undefined` is a value an app can write and so cannot carry
|
|
59
|
+
* the absence.
|
|
60
|
+
*/
|
|
61
|
+
held: unknown
|
|
62
|
+
holding: boolean
|
|
63
|
+
/**
|
|
64
|
+
* The last production minted, which the identity gate compares against.
|
|
65
|
+
*
|
|
66
|
+
* WHAT THE IDENTITY GATE COMPARES AGAINST. On every uncapped value that is `cell.value`
|
|
67
|
+
* and the two are one thing; a window moves the last production MINTED apart from the
|
|
68
|
+
* last one RELEASED, and comparing against the released one would collapse a production
|
|
69
|
+
* 5.9 forbids dropping — A, B, A inside one window is three productions, and only the
|
|
70
|
+
* third's own predecessor makes it a repeat.
|
|
71
|
+
*/
|
|
72
|
+
minted: unknown
|
|
73
|
+
/**
|
|
74
|
+
* Whether the window is holding back a body run.
|
|
75
|
+
*
|
|
76
|
+
* A BODY RUN THE WINDOW IS HOLDING BACK (5.11), against `holding`'s production. The two
|
|
77
|
+
* are separate flags because they can stand together: a stream has a body AND tells its
|
|
78
|
+
* chunks through the window (5.17), so one close can owe a run and a telling at once.
|
|
79
|
+
*/
|
|
80
|
+
owed: boolean
|
|
81
|
+
/**
|
|
82
|
+
* Whether the window is holding back a refresh, started when it closes.
|
|
83
|
+
*
|
|
84
|
+
* 5.24 — A RELOAD THE WINDOW IS HOLDING BACK: a refresh made inside it, started when it closes.
|
|
85
|
+
* Started there rather than owed like a run, because 7.11 reloads whether or not anything reads
|
|
86
|
+
* and an owed run waits for a read to pull it.
|
|
87
|
+
*/
|
|
88
|
+
reloading: boolean
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* A value's store and the state of its writes, allocated only where a `store` was declared.
|
|
93
|
+
*
|
|
94
|
+
* A VALUE'S STORE AND WHAT ITS WRITES ARE DOING, allocated only where a `store` was declared.
|
|
95
|
+
*
|
|
96
|
+
* 8.10: ONE WRITE IN FLIGHT AND ONE PENDING. A production arriving while a write is out replaces
|
|
97
|
+
* the pending value rather than queueing behind it: a slow store handed every intermediate is doing
|
|
98
|
+
* work nobody can observe, and a queue is how it grows without bound behind a fast writer.
|
|
99
|
+
*/
|
|
100
|
+
export type Persisted = {
|
|
101
|
+
store: Store<unknown>
|
|
102
|
+
/** The value's own `ttl`, taken once, and infinity where it declares none (8.13). */
|
|
103
|
+
ttl: number
|
|
104
|
+
/**
|
|
105
|
+
* Set while a restore mints its production, so what was just read is not written back (8.8).
|
|
106
|
+
*/
|
|
107
|
+
restoring: boolean
|
|
108
|
+
writing: boolean
|
|
109
|
+
pending: boolean
|
|
110
|
+
/**
|
|
111
|
+
* The newest value handed to the store and not yet confirmed.
|
|
112
|
+
*
|
|
113
|
+
* THE NEWEST VALUE THE APP HAS HANDED THE STORE, standing until the store confirms it. It is
|
|
114
|
+
* what `pending` sends next, and what 8.18 serves a re-read in place of calling `get`.
|
|
115
|
+
*/
|
|
116
|
+
unconfirmed: unknown
|
|
117
|
+
/** What stops the store's `watch`, where it has one (8.21). */
|
|
118
|
+
unwatch: (() => void) | null
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* A streaming producer's own state; `null` on every value whose body is not an async generator.
|
|
123
|
+
*
|
|
124
|
+
* A STREAMING PRODUCER'S OWN STATE (9.1, 9.2, 9.6), and `null` on every value whose body is
|
|
125
|
+
* not an async generator — the shape `cap` and `adoption` already use.
|
|
126
|
+
*
|
|
127
|
+
* THE ACCUMULATION IS PUSHED INTO AND MATERIALIZED ON THE READ. A chunk is one push and one
|
|
128
|
+
* cascade; the copy a reader is handed is taken by the read that follows, so a hundred
|
|
129
|
+
* chunks between two reads are one copy rather than a hundred (9.2). The copy is FRESH per
|
|
130
|
+
* chunk count and has to be: a reader holding an earlier snapshot still holds what it read,
|
|
131
|
+
* and a value derived from this one compares by reference — the live buffer handed out would
|
|
132
|
+
* be collapsed at every derived identity gate after the first chunk.
|
|
133
|
+
*
|
|
134
|
+
* WHAT THAT COSTS WHERE A READ FOLLOWS EVERY CHUNK is a copy of the accumulation per read,
|
|
135
|
+
* and a reader woken per chunk made that a copy per chunk: twenty thousand chunks landing in
|
|
136
|
+
* one task took 61 ms against 1.9 ms for the hand-written arm. 9.9 is what bounds it — the
|
|
137
|
+
* wake is held to one per task, which is the unit a browser paints between, so the reads it
|
|
138
|
+
* drops were never visible. A reader that reads per chunk on its own, unwoken, is still a
|
|
139
|
+
* copy per read.
|
|
140
|
+
*/
|
|
141
|
+
export type Stream = {
|
|
142
|
+
/**
|
|
143
|
+
* The accumulation of the run being served — and the same array as `ring`, so a cursor
|
|
144
|
+
* walks the chunks this holds rather than a second copy of them.
|
|
145
|
+
*/
|
|
146
|
+
chunks: unknown[]
|
|
147
|
+
/** The accumulation's version: moves on every release, patch and new run. */
|
|
148
|
+
version: number
|
|
149
|
+
/**
|
|
150
|
+
* Which accumulation `value` stands for, as a `version` rather than a chunk count.
|
|
151
|
+
*
|
|
152
|
+
* A version rather than a chunk count because a patch changes the accumulation without
|
|
153
|
+
* changing its length (9.13), and a write stands at the version it was made at until the
|
|
154
|
+
* accumulation moves past it (9.11). -1 where nothing has been materialized.
|
|
155
|
+
*/
|
|
156
|
+
materializedAt: number
|
|
157
|
+
/**
|
|
158
|
+
* Whether `value` is a write rather than a materialization.
|
|
159
|
+
*
|
|
160
|
+
* WHETHER `value` IS A WRITE rather than a materialization, while `materializedAt` stands. A value
|
|
161
|
+
* forwarding this stream serves the write the way it would have served a production (11.59).
|
|
162
|
+
*/
|
|
163
|
+
written: boolean
|
|
164
|
+
/**
|
|
165
|
+
* How many chunks a reader has been told of.
|
|
166
|
+
*
|
|
167
|
+
* HOW MANY CHUNKS A READER HAS BEEN TOLD OF. Every chunk as it lands, without a cap; with one,
|
|
168
|
+
* only what the window has released (5.18) — the rest are in `chunks` for a cursor (5.10) and
|
|
169
|
+
* out of what a read materializes until the release.
|
|
170
|
+
*/
|
|
171
|
+
visible: number
|
|
172
|
+
/** The run in flight, told to return when a newer run supersedes it. */
|
|
173
|
+
iterator: AsyncIterator<unknown> | null
|
|
174
|
+
/**
|
|
175
|
+
* Which run is current; only a new run moves it.
|
|
176
|
+
*
|
|
177
|
+
* WHICH RUN IS CURRENT, and only a new run moves it. Apart from `generation`, which every
|
|
178
|
+
* write moves so that a load it overtook lands nowhere — a write over a stream stands beside
|
|
179
|
+
* the stream (9.11) rather than superseding it, and sharing the counter stopped the stream.
|
|
180
|
+
*/
|
|
181
|
+
run: number
|
|
182
|
+
/**
|
|
183
|
+
* Whether a wake is already held for this task, so the next chunk only pushes.
|
|
184
|
+
*
|
|
185
|
+
* 9.9: chunks have landed that the readers have not been told about, and the value is
|
|
186
|
+
* already in `WAKING` — so the next chunk in this task pushes and does nothing else.
|
|
187
|
+
*/
|
|
188
|
+
waking: boolean
|
|
189
|
+
/**
|
|
190
|
+
* A probe moved under the held wake — the first chunk making the value ready, or one
|
|
191
|
+
* clearing a standing error — and the wake owes it to the guards as well.
|
|
192
|
+
*/
|
|
193
|
+
probesOwed: boolean
|
|
194
|
+
/**
|
|
195
|
+
* The stream this one forwards, and `null` on a value that produces its own.
|
|
196
|
+
*
|
|
197
|
+
* THE STREAM THIS ONE FORWARDS (11.15), and `null` on a value that produces its own. A
|
|
198
|
+
* value adopting a stream IS that stream to a cursor, a guard and an await, so it holds
|
|
199
|
+
* the adopted value's buffer and ring rather than copies — and this is what stops a
|
|
200
|
+
* trigger on it starting its body as though the body were the generator.
|
|
201
|
+
*/
|
|
202
|
+
source: Cell | null
|
|
203
|
+
/**
|
|
204
|
+
* How far the forwarding value's ordinals run ahead of the adopted value's, fixed when
|
|
205
|
+
* the forwarding begins so the adopter's cursor positions go on from its own.
|
|
206
|
+
*/
|
|
207
|
+
ordinalOffset: number
|
|
208
|
+
/**
|
|
209
|
+
* Whether what is served is a run that closed.
|
|
210
|
+
*
|
|
211
|
+
* WHETHER WHAT IS SERVED IS A RUN THAT CLOSED, which is the only accumulation a deadline
|
|
212
|
+
* reaches (6.10). A run in flight has minted nothing, so `expiry.at` says nothing about it —
|
|
213
|
+
* without this the standing arm reads `at` as 0 and drops the value on its first read.
|
|
214
|
+
*/
|
|
215
|
+
closed: boolean
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function newStream(source: Cell | null): Stream {
|
|
219
|
+
return {
|
|
220
|
+
chunks: EMPTY_RING,
|
|
221
|
+
version: 0,
|
|
222
|
+
visible: 0,
|
|
223
|
+
materializedAt: -1,
|
|
224
|
+
written: false,
|
|
225
|
+
iterator: null,
|
|
226
|
+
run: 0,
|
|
227
|
+
waking: false,
|
|
228
|
+
probesOwed: false,
|
|
229
|
+
source,
|
|
230
|
+
ordinalOffset: 0,
|
|
231
|
+
closed: false,
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Every value that is not streaming shares this as its `ring`. The field stays monomorphically an
|
|
237
|
+
* Array — CLAUDE.md's shape rule — and only a stream's own chunk buffer is ever pushed to. Frozen so
|
|
238
|
+
* that if anything ever does push here, it says so rather than corrupting every other value at once.
|
|
239
|
+
*/
|
|
240
|
+
export const EMPTY_RING = Object.freeze([]) as unknown as unknown[]
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* What an `invalidate` leaves behind: nothing owed, a look above, or a reload.
|
|
244
|
+
*
|
|
245
|
+
* WHAT AN INVALIDATE LEAVES BEHIND, and it is a debt rather than a mark. The mark walk's states
|
|
246
|
+
* are the wrong tool: a node marked `CHECK` or `DIRTY` without its effects queued is one a later
|
|
247
|
+
* write's walk stops at — `state >= wanted` — and the effects below it are never woken for a
|
|
248
|
+
* change that really happened. So nothing in the graph's state moves; the invalidated value OWES
|
|
249
|
+
* A RELOAD, the values built from it OWE A LOOK ABOVE, and none of them is directly servable, so
|
|
250
|
+
* the read that reaches any of them goes a frame in and settles the debt first (7.12, 7.13).
|
|
251
|
+
*/
|
|
252
|
+
export const OWES_NOTHING: number = 0
|
|
253
|
+
export const OWES_ABOVE: number = 1
|
|
254
|
+
export const OWES_RELOAD: number = 2
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* A value's internal state record, apart from the callable an app holds.
|
|
258
|
+
*
|
|
259
|
+
* THE INTERNAL FACE, AND A SECOND OBJECT. With the fields on the callable itself, every
|
|
260
|
+
* one was a property added to a FUNCTION at construction, and that was most of what a
|
|
261
|
+
* value cost to make: moving them here took `construct a value` from 123 ns to 87 under
|
|
262
|
+
* JSC and from 101 to 83 under V8, and a derived value's construction, a chain and a
|
|
263
|
+
* layered DAG 7-13% with it. Nothing measured moved the other way. The callable captures
|
|
264
|
+
* its record, so a read is one context load — the hand-written arm's one captured
|
|
265
|
+
* variable — and a member reaches the record through `CELL`, one load ahead of what it
|
|
266
|
+
* did before.
|
|
267
|
+
*
|
|
268
|
+
* THE FIELD COUNT SITS ON A VM BOUNDARY AND IT IS NOT A LEVER. Both engines give an
|
|
269
|
+
* object a fixed inline slot budget and spill the rest to an out-of-line store, and this
|
|
270
|
+
* record is over it — so the count moves numbers that have nothing to do with the work.
|
|
271
|
+
* Two independent measurements say the same thing from opposite ends. Taking `Cell` to
|
|
272
|
+
* 38 is worth ~11% of `construct a value` and ~6-9% of the derived case, and it does not
|
|
273
|
+
* matter HOW the four go: a consolidation into one record and four fields simply deleted
|
|
274
|
+
* buy the same number, so the consolidation is not what pays. Going the other way,
|
|
275
|
+
* 42 -> 46, is FREE — above the budget the out-of-line store already exists. And dropping
|
|
276
|
+
* ONE field moved chain 20 and both layered DAGs +4.2% under V8 while moving nothing
|
|
277
|
+
* under JSC; adding a DEAD boolean back put all three within 0.6%, which is the proof it
|
|
278
|
+
* was layout and not work — that same dead boolean then cost the probe walk 6-7%.
|
|
279
|
+
*
|
|
280
|
+
* Above the budget is not free under JSC, though: three fields more (the dead-reader
|
|
281
|
+
* counts and `probeCursor`) moved `construct a value` and the derived case +6-8% there,
|
|
282
|
+
* and three UNUSED fields on the old record moved them the same — layout, not work.
|
|
283
|
+
*
|
|
284
|
+
* So the boundary is worth KNOWING and not worth engineering around. A field costs a few
|
|
285
|
+
* ns of construction; getting under costs a structural change that the next field added here
|
|
286
|
+
* silently undoes, and it re-rolls in a different direction for every row. If
|
|
287
|
+
* construction ever needs that 11%, shed fields — do not build machinery to shed them.
|
|
288
|
+
* And when a V8-only move of this size shows up on a graph row, the first thing to check
|
|
289
|
+
* is whether the field count changed.
|
|
290
|
+
*/
|
|
291
|
+
export class Cell {
|
|
292
|
+
/**
|
|
293
|
+
* Who reads this value, on the value edge.
|
|
294
|
+
*
|
|
295
|
+
* ARRAYS, not sets. The mark walk walks this per write and a set's iterator
|
|
296
|
+
* allocates. A disposed reader is left in place and counted rather than found — see
|
|
297
|
+
* `dropFrom` in `tracking.ts` — so a walk over this skips `!alive`.
|
|
298
|
+
*/
|
|
299
|
+
declare readers: Subscriber[]
|
|
300
|
+
declare deadReaders: number
|
|
301
|
+
/**
|
|
302
|
+
* Who asked about this value's PROBES — a different edge from `readers`, and see
|
|
303
|
+
* `markProbes` for what that buys.
|
|
304
|
+
*/
|
|
305
|
+
declare probeReaders: Subscriber[]
|
|
306
|
+
declare deadProbeReaders: number
|
|
307
|
+
declare sources: Cell[]
|
|
308
|
+
/**
|
|
309
|
+
* The values whose PROBES this body asked about — a `Cell` is a `Subscriber`, so it
|
|
310
|
+
* carries the reader half of the probe edge as well as the source half.
|
|
311
|
+
*/
|
|
312
|
+
declare probeSources: Cell[]
|
|
313
|
+
declare probeCursor: number
|
|
314
|
+
declare cursor: number
|
|
315
|
+
/** See `Subscriber.seen`. */
|
|
316
|
+
declare seen: Set<Cell> | null
|
|
317
|
+
declare seenThrough: number
|
|
318
|
+
declare scanningFrom: number
|
|
319
|
+
declare label: string
|
|
320
|
+
declare body: ((args: unknown) => unknown) | null
|
|
321
|
+
declare args: unknown
|
|
322
|
+
declare value: unknown
|
|
323
|
+
declare landed: boolean
|
|
324
|
+
/**
|
|
325
|
+
* Whether a read may answer with `value`.
|
|
326
|
+
*
|
|
327
|
+
* WHETHER A READ MAY ANSWER WITH `value`. Implies `landed`, and is false over a landed value
|
|
328
|
+
* a trigger, an abort or an adoption has stopped serving — which is the state `landed`
|
|
329
|
+
* alone cannot say.
|
|
330
|
+
*/
|
|
331
|
+
declare servable: boolean
|
|
332
|
+
/**
|
|
333
|
+
* Whether a read can return `value` straight, without going a frame in.
|
|
334
|
+
*
|
|
335
|
+
* `state === CLEAN && servable` — the whole of "this read can return the field",
|
|
336
|
+
* maintained rather than recomputed. A read is the hottest path in the design and it
|
|
337
|
+
* was three loads and three branches to decide.
|
|
338
|
+
*
|
|
339
|
+
* THE INVARIANT IS ONE-DIRECTIONAL and that is what makes it safe to maintain by
|
|
340
|
+
* hand: `true` is written only where all three are known to hold, and every
|
|
341
|
+
* transition that can break one of them writes `false`. So a site that forgets to
|
|
342
|
+
* set it true costs a frame on the reads that follow and cannot serve a wrong value,
|
|
343
|
+
* which is the opposite of how a maintained flag usually fails.
|
|
344
|
+
*/
|
|
345
|
+
declare direct: boolean
|
|
346
|
+
declare loading: boolean
|
|
347
|
+
declare failure: unknown
|
|
348
|
+
/**
|
|
349
|
+
* The compiled `identity` comparator.
|
|
350
|
+
*
|
|
351
|
+
* `null` IS the default — see `comparatorFor`.
|
|
352
|
+
*/
|
|
353
|
+
declare identityFn: ((next: unknown, previous: unknown) => boolean) | null
|
|
354
|
+
/**
|
|
355
|
+
* The comparator this value DECLARED, which `identityFn` returns to when an adoption ends.
|
|
356
|
+
*
|
|
357
|
+
* 11.17 inherits the adopted value's `identity` only where the outer declared none, and only
|
|
358
|
+
* for as long as it adopts: kept on `identityFn` alone, the inherited comparator outlived the
|
|
359
|
+
* adoption and gated the outer's own productions — `flag() ? inner : count()` with `inner`
|
|
360
|
+
* keyed on `.id` read every number as the same `undefined` and never woke again.
|
|
361
|
+
*/
|
|
362
|
+
declare declaredIdentityFn:
|
|
363
|
+
((next: unknown, previous: unknown) => boolean) | null
|
|
364
|
+
declare transform: ((value: unknown) => unknown) | null
|
|
365
|
+
/**
|
|
366
|
+
* `null` on every value that declares no `schema`, which keeps the gate off the write
|
|
367
|
+
* path of everything that did not ask for one.
|
|
368
|
+
*/
|
|
369
|
+
declare schema: Schema<unknown> | null
|
|
370
|
+
/**
|
|
371
|
+
* A streaming producer's chunk buffer, for a cursor to walk (2.6); `EMPTY_RING` on every
|
|
372
|
+
* other value.
|
|
373
|
+
*/
|
|
374
|
+
declare ring: unknown[]
|
|
375
|
+
/**
|
|
376
|
+
* The ordinal before `ring[0]`, so `ring[index]` carries ordinal `ringBase + index + 1` and a
|
|
377
|
+
* cursor finds its place from the ordinal it holds.
|
|
378
|
+
*/
|
|
379
|
+
declare ringBase: number
|
|
380
|
+
/**
|
|
381
|
+
* `null` on every value that declares no `ttl`, and on every value 6.2 makes it inert
|
|
382
|
+
* for. Its presence is also what holds `direct` false — see `serve`.
|
|
383
|
+
*/
|
|
384
|
+
declare expiry: Expiry | null
|
|
385
|
+
declare ordinal: number
|
|
386
|
+
declare generation: number
|
|
387
|
+
declare alive: boolean
|
|
388
|
+
declare isEffect: boolean
|
|
389
|
+
declare isMirror: boolean
|
|
390
|
+
declare adopter: Tracked | null
|
|
391
|
+
/**
|
|
392
|
+
* The mark: `CLEAN`, `CHECK` or `DIRTY`.
|
|
393
|
+
*
|
|
394
|
+
* `CLEAN`, `CHECK` or `DIRTY` — see `tracking.ts`. Stamped on every transitive
|
|
395
|
+
* reader BEFORE anything runs, so a body that reaches a source which has not caught
|
|
396
|
+
* up yet pulls it forward; `running` is what stops a body already on the stack being
|
|
397
|
+
* re-entered.
|
|
398
|
+
*
|
|
399
|
+
* ONE RUN PER NODE PER WRITE is the contract, and the state carries it without a
|
|
400
|
+
* cascade counter: a node that has run is `CLEAN`, and only a source PRODUCING can
|
|
401
|
+
* mark it again — which a source it already read cannot do, because reading it is
|
|
402
|
+
* what resolved it.
|
|
403
|
+
*/
|
|
404
|
+
declare state: number
|
|
405
|
+
declare running: boolean
|
|
406
|
+
/**
|
|
407
|
+
* Whether the last run aborted on a value that had not landed.
|
|
408
|
+
*
|
|
409
|
+
* 2.13: A RUN THAT ABORTED ON A VALUE THAT HAD NOT LANDED, and is owed the rest of itself
|
|
410
|
+
* when that value arrives. Push-pull leaves a marked body for the read that wants it, and
|
|
411
|
+
* that is right for a body which RAN — this one did not, so nothing downstream can pull it:
|
|
412
|
+
* a reader guarding on `ready()` asks the probe edge and never the value, and the guard would
|
|
413
|
+
* answer false for as long as nobody read through it.
|
|
414
|
+
*/
|
|
415
|
+
declare aborted: boolean
|
|
416
|
+
/**
|
|
417
|
+
* The value this one adopted, or `null`.
|
|
418
|
+
*
|
|
419
|
+
* WHAT THIS VALUE'S BODY HANDED BACK, where it handed back another `Reactive`
|
|
420
|
+
* (11.12). `null` on every value that is not adopting, which is nearly all of them —
|
|
421
|
+
* one field rather than two, and the record is allocated only where there is one.
|
|
422
|
+
*/
|
|
423
|
+
declare adoption: Adoption | null
|
|
424
|
+
/**
|
|
425
|
+
* `null` on every value declaring neither `throttle` nor `debounce`, which is nearly
|
|
426
|
+
* all of them.
|
|
427
|
+
*/
|
|
428
|
+
declare cap: Cap | null
|
|
429
|
+
/**
|
|
430
|
+
* `null` on every value declaring no `tags`, which keeps `markProbes` from looking up a
|
|
431
|
+
* selection signal for any of them.
|
|
432
|
+
*/
|
|
433
|
+
declare tags: string[] | null
|
|
434
|
+
/** Whether `declare` built this value for a mutation, which a sweep invalidates (13.16). */
|
|
435
|
+
declare mutation: boolean
|
|
436
|
+
/**
|
|
437
|
+
* Whether what is held was written here since the producer last produced (3.10).
|
|
438
|
+
*
|
|
439
|
+
* Only ever true on a value with a body: a value with no producer has nothing to be out of
|
|
440
|
+
* step with (3.12). Moved by `writtenHere` and `producedAgain` alone, each owning its probe move.
|
|
441
|
+
*/
|
|
442
|
+
declare local: boolean
|
|
443
|
+
/** `null` on every value declaring no `store`, which is what keeps `serve` from persisting. */
|
|
444
|
+
declare store: Persisted | null
|
|
445
|
+
/**
|
|
446
|
+
* The declared `by`.
|
|
447
|
+
*
|
|
448
|
+
* 5.22 — held as declared rather than as a function, so the property name is still there to send.
|
|
449
|
+
*/
|
|
450
|
+
declare by: string | ((element: unknown) => unknown) | null
|
|
451
|
+
/** The probe pass this cell was last visited in. See `walkBusy` and `walkReady`. */
|
|
452
|
+
declare probePass: number
|
|
453
|
+
/**
|
|
454
|
+
* `OWES_NOTHING`, `OWES_ABOVE` or `OWES_RELOAD` — what an `invalidate` left for the next read
|
|
455
|
+
* to do. See `invalidateOne`.
|
|
456
|
+
*/
|
|
457
|
+
declare owes: number
|
|
458
|
+
/** The value this record is the state of — what `NotLanded` names as its source. */
|
|
459
|
+
declare callable: Reactive<unknown> | null
|
|
460
|
+
/** `null` on every value whose body is not an async generator, which is nearly all. */
|
|
461
|
+
declare stream: Stream | null
|
|
462
|
+
/**
|
|
463
|
+
* Whether a read may ever take the field straight, fixed at construction.
|
|
464
|
+
*
|
|
465
|
+
* WHETHER A READ MAY EVER TAKE THE FIELD STRAIGHT, fixed at construction: no expiry to
|
|
466
|
+
* check and no accumulation to materialize. `setDirect` reads this one field where it
|
|
467
|
+
* would otherwise read one per mechanism that needs the read to go a frame in.
|
|
468
|
+
*/
|
|
469
|
+
declare plain: boolean
|
|
470
|
+
/**
|
|
471
|
+
* Whether a settled write goes straight to `produce`, past no gate.
|
|
472
|
+
*
|
|
473
|
+
* NOTHING STANDS BETWEEN A SETTLED WRITE AND `produce`: no `schema`, no `transform`, and no
|
|
474
|
+
* stream whose writes stand apart from its chunks (9.11, 9.12). One field where the write and
|
|
475
|
+
* the recompute each tested two, maintained where a forwarded stream comes and goes.
|
|
476
|
+
*/
|
|
477
|
+
declare bareWrite: boolean
|
|
478
|
+
/**
|
|
479
|
+
* Whether a gated write can take `SIMPLE_PROTOTYPE`'s short path.
|
|
480
|
+
*
|
|
481
|
+
* EVERYTHING A GATED WRITE WOULD OTHERWISE ASK, IN ONE FIELD: no cap holding the
|
|
482
|
+
* production back, no expiry, no store to persist through, no
|
|
483
|
+
* declared `identity`, and `bareWrite`'s own three. A value carrying none of them has a
|
|
484
|
+
* `produce` -> `serve` -> `cascade` that reduces to three stores and a mark — which is
|
|
485
|
+
* what `SIMPLE_PROTOTYPE.set` spells out rather than calls.
|
|
486
|
+
*
|
|
487
|
+
* IT SELECTS A PROTOTYPE AND IS NOT READ ON THE WRITE PATH. See `SIMPLE_PROTOTYPE`.
|
|
488
|
+
*
|
|
489
|
+
* MAINTAINED, NOT FIXED: three of the fields under it move after construction —
|
|
490
|
+
* `bareWrite` and `plain` where a forwarded stream comes and goes, `identityFn` where an
|
|
491
|
+
* adoption takes the inner value's. `simpleShape` is called at each of those.
|
|
492
|
+
*/
|
|
493
|
+
declare simple: boolean
|
|
494
|
+
/**
|
|
495
|
+
* The scope this value was built in, and so the one its body runs under.
|
|
496
|
+
*
|
|
497
|
+
* 11.32 — THE SCOPE THIS VALUE WAS BUILT IN, and so the one its body runs under. `undefined` in a
|
|
498
|
+
* browser, and on a server for a value built outside any request.
|
|
499
|
+
*/
|
|
500
|
+
declare scope: Scope | undefined
|
|
501
|
+
/**
|
|
502
|
+
* Whether this value is one for the whole process, in every scope.
|
|
503
|
+
*
|
|
504
|
+
* 11.34, 13.13 — one value for the process, and so inside every scope a selection is made in. An
|
|
505
|
+
* entry of a `global` keyed value is one too, whichever request first reached it.
|
|
506
|
+
*/
|
|
507
|
+
declare global: boolean
|
|
508
|
+
/**
|
|
509
|
+
* The one weak handle every selection index holds this value by, or `null` where none does.
|
|
510
|
+
*
|
|
511
|
+
* 13.1 AND 13.6: a value filed for the bare sweep or carrying `tags` is findable by one, and
|
|
512
|
+
* every index holds the same handle, so an eviction drops it from all of them at once.
|
|
513
|
+
*/
|
|
514
|
+
declare ref: WeakRef<Cell> | null
|
|
515
|
+
/**
|
|
516
|
+
* Whether this value is filed for the bare selection — once, where it first becomes a load.
|
|
517
|
+
*
|
|
518
|
+
* See `fileProducer`. Not cleared by an eviction: dropping `ref` is what takes an evicted entry
|
|
519
|
+
* out, and this staying set is what keeps its next load from filing it again.
|
|
520
|
+
*/
|
|
521
|
+
declare filed: boolean
|
|
522
|
+
/**
|
|
523
|
+
* Whether what this value was built in has stopped — the setup or the request that owned it.
|
|
524
|
+
*
|
|
525
|
+
* 12.14. Set by `orphan`; the value lets go of its sources once nothing
|
|
526
|
+
* reads it (`releaseOrphan`), which may be at once or when the last reader outside its owner leaves.
|
|
527
|
+
*/
|
|
528
|
+
declare orphaned: boolean
|
|
529
|
+
/** A cell's wake, which is a run of the graph: on the prototype, from `Reactive.ts` — see above. */
|
|
530
|
+
declare notify: () => void
|
|
531
|
+
|
|
532
|
+
constructor(
|
|
533
|
+
label: string,
|
|
534
|
+
body: ((args: unknown) => unknown) | null,
|
|
535
|
+
args: unknown,
|
|
536
|
+
options: ReactiveOptions<unknown, unknown, never> | undefined,
|
|
537
|
+
streaming: boolean,
|
|
538
|
+
) {
|
|
539
|
+
// Every field, in one order, `undefined` included.
|
|
540
|
+
this.readers = []
|
|
541
|
+
this.deadReaders = 0
|
|
542
|
+
this.probeReaders = []
|
|
543
|
+
this.deadProbeReaders = 0
|
|
544
|
+
this.sources = []
|
|
545
|
+
this.probeSources = []
|
|
546
|
+
this.probeCursor = 0
|
|
547
|
+
this.cursor = 0
|
|
548
|
+
this.seen = null
|
|
549
|
+
this.seenThrough = 0
|
|
550
|
+
this.scanningFrom = -1
|
|
551
|
+
this.label = label
|
|
552
|
+
this.body = body
|
|
553
|
+
this.args = args
|
|
554
|
+
this.value = undefined
|
|
555
|
+
this.landed = false
|
|
556
|
+
this.servable = false
|
|
557
|
+
this.direct = false
|
|
558
|
+
this.loading = false
|
|
559
|
+
this.failure = undefined
|
|
560
|
+
this.identityFn = comparatorFor(
|
|
561
|
+
options?.identity as Identity<unknown> | undefined,
|
|
562
|
+
)
|
|
563
|
+
this.declaredIdentityFn = this.identityFn
|
|
564
|
+
this.transform = (options?.transform ?? null) as
|
|
565
|
+
((value: unknown) => unknown) | null
|
|
566
|
+
this.schema = (options?.schema ?? null) as Schema<unknown> | null
|
|
567
|
+
this.ring = EMPTY_RING
|
|
568
|
+
this.ringBase = 0
|
|
569
|
+
// 6.2 DECIDED ONCE, here rather than per production: a body is what makes a value a
|
|
570
|
+
// producer, and a plain state is the one this must not reach. See `expiryFor`.
|
|
571
|
+
this.expiry = expiryFor(
|
|
572
|
+
options?.ttl,
|
|
573
|
+
// A store is a producer too (8.2): there is somewhere to get the value back from, so
|
|
574
|
+
// `ttl` on a value holding one drops rather than being inert.
|
|
575
|
+
body !== null || options?.store !== undefined,
|
|
576
|
+
)
|
|
577
|
+
this.ordinal = 0
|
|
578
|
+
this.generation = 0
|
|
579
|
+
this.alive = true
|
|
580
|
+
this.state = CLEAN
|
|
581
|
+
this.running = false
|
|
582
|
+
this.aborted = false
|
|
583
|
+
this.adoption = null
|
|
584
|
+
this.cap = capFor(label, options?.throttle, options?.debounce)
|
|
585
|
+
const tags = options?.tags
|
|
586
|
+
this.tags =
|
|
587
|
+
tags === undefined
|
|
588
|
+
? null
|
|
589
|
+
: typeof tags === 'function'
|
|
590
|
+
? tags(args as never)
|
|
591
|
+
: tags
|
|
592
|
+
this.mutation =
|
|
593
|
+
(options as { [MUTATION]?: true } | undefined)?.[MUTATION] === true
|
|
594
|
+
this.local = false
|
|
595
|
+
const store = options?.store
|
|
596
|
+
this.store =
|
|
597
|
+
store === undefined
|
|
598
|
+
? null
|
|
599
|
+
: {
|
|
600
|
+
store: (typeof store === 'function'
|
|
601
|
+
? store(args as never)
|
|
602
|
+
: store) as Store<unknown>,
|
|
603
|
+
ttl: options?.ttl ?? Number.POSITIVE_INFINITY,
|
|
604
|
+
restoring: false,
|
|
605
|
+
writing: false,
|
|
606
|
+
pending: false,
|
|
607
|
+
unconfirmed: undefined,
|
|
608
|
+
unwatch: null,
|
|
609
|
+
}
|
|
610
|
+
this.by = (options?.by ?? null) as
|
|
611
|
+
string | ((element: unknown) => unknown) | null
|
|
612
|
+
this.probePass = 0
|
|
613
|
+
this.owes = OWES_NOTHING
|
|
614
|
+
this.isEffect = false
|
|
615
|
+
this.isMirror = false
|
|
616
|
+
this.adopter = null
|
|
617
|
+
this.callable = null
|
|
618
|
+
this.stream = streaming ? newStream(null) : null
|
|
619
|
+
this.plain = this.expiry === null && this.stream === null
|
|
620
|
+
this.bareWrite =
|
|
621
|
+
this.schema === null && this.transform === null && !streaming
|
|
622
|
+
this.simple = false
|
|
623
|
+
this.scope = currentScope()
|
|
624
|
+
this.global = options?.global === true
|
|
625
|
+
this.ref = null
|
|
626
|
+
this.filed = false
|
|
627
|
+
this.orphaned = false
|
|
628
|
+
simpleShape(this)
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/** What a member's `this` is, and the one way from the value an app holds to its record. */
|
|
633
|
+
export type Callable = { [CELL]: Cell }
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Throws where a value declares both `throttle` and `debounce`.
|
|
637
|
+
*
|
|
638
|
+
* 5.14 is a BUILD error and there is no build yet, so it lands at construction — the
|
|
639
|
+
* earliest moment that exists, and the one the app still has a stack for. Naming the value
|
|
640
|
+
* is the clause's own requirement; without it a page declaring both on one of forty values
|
|
641
|
+
* says only that something did. One statement for both producers of a window: a value's
|
|
642
|
+
* (5.8) and a keyed value's, which caps starts rather than wakes (5.19).
|
|
643
|
+
*/
|
|
644
|
+
export function refuseBothCaps(
|
|
645
|
+
label: string,
|
|
646
|
+
throttle: number | undefined,
|
|
647
|
+
debounce: number | undefined,
|
|
648
|
+
): void {
|
|
649
|
+
if (throttle !== undefined && debounce !== undefined)
|
|
650
|
+
throw new Error(
|
|
651
|
+
`${label} declares both throttle and debounce. A value is capped one way or the other.`,
|
|
652
|
+
)
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
export function capFor(
|
|
656
|
+
label: string,
|
|
657
|
+
throttle: number | undefined,
|
|
658
|
+
debounce: number | undefined,
|
|
659
|
+
): Cap | null {
|
|
660
|
+
refuseBothCaps(label, throttle, debounce)
|
|
661
|
+
const ms = throttle ?? debounce
|
|
662
|
+
if (ms === undefined) return null
|
|
663
|
+
return {
|
|
664
|
+
leading: throttle !== undefined,
|
|
665
|
+
ms,
|
|
666
|
+
timer: null,
|
|
667
|
+
held: undefined,
|
|
668
|
+
holding: false,
|
|
669
|
+
minted: undefined,
|
|
670
|
+
owed: false,
|
|
671
|
+
reloading: false,
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Compiles `identity` into a comparator, or `null` for the default `Object.is`.
|
|
677
|
+
*
|
|
678
|
+
* THE DEFAULT IS THE REFERENCE, and `structural` is exported for an app to pass.
|
|
679
|
+
*
|
|
680
|
+
* THE CASE FOR A STRUCTURAL DEFAULT IS REAL: a reload that fetched an equal
|
|
681
|
+
* payload builds a FRESH object, so a reference compare never fires and every reader
|
|
682
|
+
* wakes on a value that did not change. What decides it against that is the cost on
|
|
683
|
+
* the shape an app actually holds — a compare walks the whole value on EVERY write,
|
|
684
|
+
* where the wake it avoids is paid only when the payload really is equal. A grid of
|
|
685
|
+
* rows re-assembled per recompute pays the deep walk every time and collapses never.
|
|
686
|
+
*
|
|
687
|
+
* So the framework takes the cheap, predictable default — `===` is what JavaScript
|
|
688
|
+
* means by "the same value" — and the app names `identity: structural` on the values
|
|
689
|
+
* where re-materialization is the common case. 5.5's "not equal wherever it cannot
|
|
690
|
+
* decide" is unchanged; it is just no longer the default reading.
|
|
691
|
+
*
|
|
692
|
+
* `identity` is discriminated by ARITY (5.3), once, at construction — not per compare.
|
|
693
|
+
*
|
|
694
|
+
* NULL IS THE DEFAULT, not a function that implements it. `produce` compares against
|
|
695
|
+
* `null` and calls `Object.is` directly, which takes an INDIRECT call off the write path:
|
|
696
|
+
* the site is polymorphic across the default, a projection closure and whatever an app
|
|
697
|
+
* passed, so it is not one an engine can speculate through. The saving is the indirection
|
|
698
|
+
* rather than the comparison — `Object.is` is itself the fastest spelling of it.
|
|
699
|
+
*/
|
|
700
|
+
export function comparatorFor(
|
|
701
|
+
identity: Identity<unknown> | undefined,
|
|
702
|
+
): ((next: unknown, previous: unknown) => boolean) | null {
|
|
703
|
+
if (identity === undefined) return null
|
|
704
|
+
if (identity.length >= 2)
|
|
705
|
+
return identity as (next: unknown, previous: unknown) => boolean
|
|
706
|
+
const project = identity as (value: unknown) => unknown
|
|
707
|
+
return (next, previous) => project(next) === project(previous)
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Recomputes `simple` and re-seats the callable on the matching prototype.
|
|
712
|
+
*
|
|
713
|
+
* `simple` IN ONE PLACE. A second copy of this conjunction is the drift that would let one of the
|
|
714
|
+
* sites go on reading `true` after the field under it moved — and what the short write would then
|
|
715
|
+
* skip is a cap, a ring or a declared `identity`, silently. The prototype that goes with it is
|
|
716
|
+
* `seat`'s, taken where the transition that moved a field under it ends.
|
|
717
|
+
*/
|
|
718
|
+
export function simpleShape(cell: Cell): void {
|
|
719
|
+
cell.simple =
|
|
720
|
+
cell.bareWrite &&
|
|
721
|
+
// `plain` carries `expiry === null` and `stream === null` already.
|
|
722
|
+
cell.plain &&
|
|
723
|
+
cell.cap === null &&
|
|
724
|
+
cell.store === null &&
|
|
725
|
+
cell.identityFn === null
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* The record `value` keeps its state on, for a reader outside the core: `value` is a `Reactive`, and
|
|
730
|
+
* this is the one cast from what an app holds to the record, rather than one per reader.
|
|
731
|
+
*/
|
|
732
|
+
export function cellOf(value: unknown): Cell {
|
|
733
|
+
return (value as Callable)[CELL]
|
|
734
|
+
}
|