@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
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// holds one per row — and copying the shared ones onto each instance instead was 87% of
|
|
9
9
|
// what a value cost to construct. See `MEMBER_PROTOTYPE`.
|
|
10
10
|
//
|
|
11
|
-
// Every field `Cell` declares is written in its constructor, in one order, `undefined`
|
|
11
|
+
// Every field `Cell` (`cellShape.ts`) declares is written in its constructor, in one order, `undefined`
|
|
12
12
|
// included — a shape that grows a field on a later path is two hidden classes and every
|
|
13
13
|
// read downstream of it deoptimizes.
|
|
14
14
|
|
|
@@ -19,24 +19,38 @@ 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
|
+
type Adoption,
|
|
24
|
+
type Callable,
|
|
25
|
+
type Cap,
|
|
26
|
+
Cell,
|
|
27
|
+
EMPTY_RING,
|
|
28
|
+
newStream,
|
|
29
|
+
OWES_ABOVE,
|
|
30
|
+
OWES_NOTHING,
|
|
31
|
+
OWES_RELOAD,
|
|
32
|
+
type Persisted,
|
|
33
|
+
simpleShape,
|
|
34
|
+
type Stream,
|
|
35
|
+
} from './cellShape.ts'
|
|
22
36
|
import { CAUGHT_UP, Cursor, hasCursorBehind, keepPlaces } from './Cursor.ts'
|
|
23
|
-
import {
|
|
24
|
-
import { armTimer, type Expiry, expiryFor } from './Expiry.ts'
|
|
37
|
+
import { armTimer, type Expiry } from './Expiry.ts'
|
|
25
38
|
import { isReactive } from './isReactive.ts'
|
|
26
39
|
import { isThenable } from './isThenable.ts'
|
|
27
|
-
import { MUTATION } from './MUTATION.ts'
|
|
28
40
|
import { NO_INITIAL } from './NO_INITIAL.ts'
|
|
29
41
|
import { NotLanded } from './NotLanded.ts'
|
|
30
|
-
import {
|
|
31
|
-
holdLog,
|
|
32
|
-
logOf,
|
|
33
|
-
PatchLog,
|
|
34
|
-
type PatchOp,
|
|
35
|
-
sendInvalidate,
|
|
36
|
-
} from './patchLog.ts'
|
|
42
|
+
import { logOf, PatchLog, type PatchOp, sendInvalidate } from './patchLog.ts'
|
|
37
43
|
import { REACTIVE_BRAND } from './REACTIVE_BRAND.ts'
|
|
38
44
|
import { recordWrites } from './recordWrites.ts'
|
|
39
|
-
import {
|
|
45
|
+
import { runInScope } from './scoping.ts'
|
|
46
|
+
import {
|
|
47
|
+
EVERY_PRODUCER,
|
|
48
|
+
fileProducer,
|
|
49
|
+
inReach,
|
|
50
|
+
markSignals,
|
|
51
|
+
tagCell,
|
|
52
|
+
trackSignal,
|
|
53
|
+
} from './selections.ts'
|
|
40
54
|
import { STREAMING } from './STREAMING.ts'
|
|
41
55
|
import {
|
|
42
56
|
CHECK,
|
|
@@ -45,7 +59,6 @@ import {
|
|
|
45
59
|
DIRTY,
|
|
46
60
|
dropProbeReader,
|
|
47
61
|
newSubscriber,
|
|
48
|
-
newTracked,
|
|
49
62
|
RELEASING,
|
|
50
63
|
revive,
|
|
51
64
|
type Subscriber,
|
|
@@ -276,228 +289,13 @@ export type Reactive<
|
|
|
276
289
|
finally(onSettled: () => void): Promise<Stored>
|
|
277
290
|
}
|
|
278
291
|
|
|
279
|
-
/** The standing adoption: what is adopted, and the way to stop mirroring it. */
|
|
280
|
-
type Adoption = { source: Cell; stop: () => void }
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* The window a `throttle` or a `debounce` holds open, allocated only where one is declared.
|
|
284
|
-
*
|
|
285
|
-
* THE WINDOW A `throttle` OR A `debounce` HOLDS OPEN (RULEBOOK 5.8). One field on the
|
|
286
|
-
* cell rather than one per window field, allocated only where a value declared one — the
|
|
287
|
-
* shape `adoption` already uses, and for the same reason.
|
|
288
|
-
*
|
|
289
|
-
* WHAT A WINDOW CAPS IS THE WAKE, NEVER THE PRODUCTION (5.9). Every write still mints and
|
|
290
|
-
* the ordinal moves. What waits is the value a READER sees and the cascade that tells it so,
|
|
291
|
-
* which is the whole of 5.8's "how often a value changes for a reader".
|
|
292
|
-
*
|
|
293
|
-
* WHAT IT COSTS EVERY VALUE THAT NEVER CAPS: one field load and one branch on the write
|
|
294
|
-
* path, plus `serve` as a frame rather than five stores inline. Measured at 0.99x -> 1.08x
|
|
295
|
-
* against the hand-written arm on the since-removed `publish into a capped ring` case, consistent across three
|
|
296
|
-
* runs over a 0.7% floor — about 1.3 ns on a 14.7 ns op. Moving the comparison target
|
|
297
|
-
* behind the gate did not recover it, so it is the frame and the branch rather than a
|
|
298
|
-
* wasted load. Inlining `serve` into the uncapped arm would buy it back for a second copy
|
|
299
|
-
* of the reader-visible transition, and this file has already paid for one of those: the
|
|
300
|
-
* trade is stated rather than taken, 1.3 ns being nowhere near perception.
|
|
301
|
-
*/
|
|
302
|
-
type Cap = {
|
|
303
|
-
/**
|
|
304
|
-
* True for a throttle, which lets the leading change through; false for a debounce.
|
|
305
|
-
*
|
|
306
|
-
* 5.12 against 5.13, and the only difference between the two mechanisms: a throttle
|
|
307
|
-
* lets the leading change through and holds what follows, a debounce holds everything
|
|
308
|
-
* until the changes stop.
|
|
309
|
-
*/
|
|
310
|
-
leading: boolean
|
|
311
|
-
ms: number
|
|
312
|
-
timer: ReturnType<typeof setTimeout> | null
|
|
313
|
-
/**
|
|
314
|
-
* The production the window is holding back, while `holding` is set.
|
|
315
|
-
*
|
|
316
|
-
* The production the window is holding back. `holding` is a field rather than a test
|
|
317
|
-
* against `held`, because `undefined` is a value an app can write and so cannot carry
|
|
318
|
-
* the absence.
|
|
319
|
-
*/
|
|
320
|
-
held: unknown
|
|
321
|
-
holding: boolean
|
|
322
|
-
/**
|
|
323
|
-
* The last production minted, which the identity gate compares against.
|
|
324
|
-
*
|
|
325
|
-
* WHAT THE IDENTITY GATE COMPARES AGAINST. On every uncapped value that is `cell.value`
|
|
326
|
-
* and the two are one thing; a window moves the last production MINTED apart from the
|
|
327
|
-
* last one RELEASED, and comparing against the released one would collapse a production
|
|
328
|
-
* 5.9 forbids dropping — A, B, A inside one window is three productions, and only the
|
|
329
|
-
* third's own predecessor makes it a repeat.
|
|
330
|
-
*/
|
|
331
|
-
minted: unknown
|
|
332
|
-
/**
|
|
333
|
-
* Whether the window is holding back a body run.
|
|
334
|
-
*
|
|
335
|
-
* A BODY RUN THE WINDOW IS HOLDING BACK (5.11), against `holding`'s production. The two
|
|
336
|
-
* are separate flags because they can stand together: a stream has a body AND tells its
|
|
337
|
-
* chunks through the window (5.17), so one close can owe a run and a telling at once.
|
|
338
|
-
*/
|
|
339
|
-
owed: boolean
|
|
340
|
-
/**
|
|
341
|
-
* Whether the window is holding back a refresh, started when it closes.
|
|
342
|
-
*
|
|
343
|
-
* 5.24 — A RELOAD THE WINDOW IS HOLDING BACK: a refresh made inside it, started when it closes.
|
|
344
|
-
* Started there rather than owed like a run, because 7.11 reloads whether or not anything reads
|
|
345
|
-
* and an owed run waits for a read to pull it.
|
|
346
|
-
*/
|
|
347
|
-
reloading: boolean
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* A value's store and the state of its writes, allocated only where a `store` was declared.
|
|
352
|
-
*
|
|
353
|
-
* A VALUE'S STORE AND WHAT ITS WRITES ARE DOING, allocated only where a `store` was declared.
|
|
354
|
-
*
|
|
355
|
-
* 8.10: ONE WRITE IN FLIGHT AND ONE PENDING. A production arriving while a write is out replaces
|
|
356
|
-
* the pending value rather than queueing behind it: a slow store handed every intermediate is doing
|
|
357
|
-
* work nobody can observe, and a queue is how it grows without bound behind a fast writer.
|
|
358
|
-
*/
|
|
359
|
-
type Persisted = {
|
|
360
|
-
store: Store<unknown>
|
|
361
|
-
/** The value's own `ttl`, taken once, and infinity where it declares none (8.13). */
|
|
362
|
-
ttl: number
|
|
363
|
-
/**
|
|
364
|
-
* Set while a restore mints its production, so what was just read is not written back (8.8).
|
|
365
|
-
*/
|
|
366
|
-
restoring: boolean
|
|
367
|
-
writing: boolean
|
|
368
|
-
pending: boolean
|
|
369
|
-
/**
|
|
370
|
-
* The newest value handed to the store and not yet confirmed.
|
|
371
|
-
*
|
|
372
|
-
* THE NEWEST VALUE THE APP HAS HANDED THE STORE, standing until the store confirms it. It is
|
|
373
|
-
* what `pending` sends next, and what 8.18 serves a re-read in place of calling `get`.
|
|
374
|
-
*/
|
|
375
|
-
unconfirmed: unknown
|
|
376
|
-
/** What stops the store's `watch`, where it has one (8.21). */
|
|
377
|
-
unwatch: (() => void) | null
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* A streaming producer's own state; `null` on every value whose body is not an async generator.
|
|
382
|
-
*
|
|
383
|
-
* A STREAMING PRODUCER'S OWN STATE (9.1, 9.2, 9.6), and `null` on every value whose body is
|
|
384
|
-
* not an async generator — the shape `cap` and `adoption` already use.
|
|
385
|
-
*
|
|
386
|
-
* THE ACCUMULATION IS PUSHED INTO AND MATERIALIZED ON THE READ. A chunk is one push and one
|
|
387
|
-
* cascade; the copy a reader is handed is taken by the read that follows, so a hundred
|
|
388
|
-
* chunks between two reads are one copy rather than a hundred (9.2). The copy is FRESH per
|
|
389
|
-
* chunk count and has to be: a reader holding an earlier snapshot still holds what it read,
|
|
390
|
-
* and a value derived from this one compares by reference — the live buffer handed out would
|
|
391
|
-
* be collapsed at every derived identity gate after the first chunk.
|
|
392
|
-
*
|
|
393
|
-
* WHAT THAT COSTS WHERE A READ FOLLOWS EVERY CHUNK is a copy of the accumulation per read,
|
|
394
|
-
* and a reader woken per chunk made that a copy per chunk: twenty thousand chunks landing in
|
|
395
|
-
* one task took 61 ms against 1.9 ms for the hand-written arm. 9.9 is what bounds it — the
|
|
396
|
-
* wake is held to one per task, which is the unit a browser paints between, so the reads it
|
|
397
|
-
* drops were never visible. A reader that reads per chunk on its own, unwoken, is still a
|
|
398
|
-
* copy per read.
|
|
399
|
-
*/
|
|
400
|
-
type Stream = {
|
|
401
|
-
/**
|
|
402
|
-
* The accumulation of the run being served — and the same array as `ring`, so a cursor
|
|
403
|
-
* walks the chunks this holds rather than a second copy of them.
|
|
404
|
-
*/
|
|
405
|
-
chunks: unknown[]
|
|
406
|
-
/** The accumulation's version: moves on every release, patch and new run. */
|
|
407
|
-
version: number
|
|
408
|
-
/**
|
|
409
|
-
* Which accumulation `value` stands for, as a `version` rather than a chunk count.
|
|
410
|
-
*
|
|
411
|
-
* A version rather than a chunk count because a patch changes the accumulation without
|
|
412
|
-
* changing its length (9.13), and a write stands at the version it was made at until the
|
|
413
|
-
* accumulation moves past it (9.11). -1 where nothing has been materialized.
|
|
414
|
-
*/
|
|
415
|
-
materializedAt: number
|
|
416
|
-
/**
|
|
417
|
-
* Whether `value` is a write rather than a materialization.
|
|
418
|
-
*
|
|
419
|
-
* WHETHER `value` IS A WRITE rather than a materialization, while `materializedAt` stands. A value
|
|
420
|
-
* forwarding this stream serves the write the way it would have served a production (11.59).
|
|
421
|
-
*/
|
|
422
|
-
written: boolean
|
|
423
|
-
/**
|
|
424
|
-
* How many chunks a reader has been told of.
|
|
425
|
-
*
|
|
426
|
-
* HOW MANY CHUNKS A READER HAS BEEN TOLD OF. Every chunk as it lands, without a cap; with one,
|
|
427
|
-
* only what the window has released (5.18) — the rest are in `chunks` for a cursor (5.10) and
|
|
428
|
-
* out of what a read materializes until the release.
|
|
429
|
-
*/
|
|
430
|
-
visible: number
|
|
431
|
-
/** The run in flight, told to return when a newer run supersedes it. */
|
|
432
|
-
iterator: AsyncIterator<unknown> | null
|
|
433
|
-
/**
|
|
434
|
-
* Which run is current; only a new run moves it.
|
|
435
|
-
*
|
|
436
|
-
* WHICH RUN IS CURRENT, and only a new run moves it. Apart from `generation`, which every
|
|
437
|
-
* write moves so that a load it overtook lands nowhere — a write over a stream stands beside
|
|
438
|
-
* the stream (9.11) rather than superseding it, and sharing the counter stopped the stream.
|
|
439
|
-
*/
|
|
440
|
-
run: number
|
|
441
|
-
/**
|
|
442
|
-
* Whether a wake is already held for this task, so the next chunk only pushes.
|
|
443
|
-
*
|
|
444
|
-
* 9.9: chunks have landed that the readers have not been told about, and the value is
|
|
445
|
-
* already in `WAKING` — so the next chunk in this task pushes and does nothing else.
|
|
446
|
-
*/
|
|
447
|
-
waking: boolean
|
|
448
|
-
/**
|
|
449
|
-
* A probe moved under the held wake — the first chunk making the value ready, or one
|
|
450
|
-
* clearing a standing error — and the wake owes it to the guards as well.
|
|
451
|
-
*/
|
|
452
|
-
probesOwed: boolean
|
|
453
|
-
/**
|
|
454
|
-
* The stream this one forwards, and `null` on a value that produces its own.
|
|
455
|
-
*
|
|
456
|
-
* THE STREAM THIS ONE FORWARDS (11.15), and `null` on a value that produces its own. A
|
|
457
|
-
* value adopting a stream IS that stream to a cursor, a guard and an await, so it holds
|
|
458
|
-
* the adopted value's buffer and ring rather than copies — and this is what stops a
|
|
459
|
-
* trigger on it starting its body as though the body were the generator.
|
|
460
|
-
*/
|
|
461
|
-
source: Cell | null
|
|
462
|
-
/**
|
|
463
|
-
* How far the forwarding value's ordinals run ahead of the adopted value's, fixed when
|
|
464
|
-
* the forwarding begins so the adopter's cursor positions go on from its own.
|
|
465
|
-
*/
|
|
466
|
-
ordinalOffset: number
|
|
467
|
-
/**
|
|
468
|
-
* Whether what is served is a run that closed.
|
|
469
|
-
*
|
|
470
|
-
* WHETHER WHAT IS SERVED IS A RUN THAT CLOSED, which is the only accumulation a deadline
|
|
471
|
-
* reaches (6.10). A run in flight has minted nothing, so `expiry.at` says nothing about it —
|
|
472
|
-
* without this the standing arm reads `at` as 0 and drops the value on its first read.
|
|
473
|
-
*/
|
|
474
|
-
closed: boolean
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
function newStream(source: Cell | null): Stream {
|
|
478
|
-
return {
|
|
479
|
-
chunks: EMPTY_RING,
|
|
480
|
-
version: 0,
|
|
481
|
-
visible: 0,
|
|
482
|
-
materializedAt: -1,
|
|
483
|
-
written: false,
|
|
484
|
-
iterator: null,
|
|
485
|
-
run: 0,
|
|
486
|
-
waking: false,
|
|
487
|
-
probesOwed: false,
|
|
488
|
-
source,
|
|
489
|
-
ordinalOffset: 0,
|
|
490
|
-
closed: false,
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
|
|
494
292
|
/**
|
|
495
293
|
* The stream `cell` produces itself, or `null`: it has none, or the one it has is forwarded (11.15).
|
|
496
294
|
*
|
|
497
295
|
* THE ONE DISCRIMINATOR between following and forwarding. An own stream's `adoption` is a write it
|
|
498
296
|
* follows (9.11), and a forwarding value's is the stream it forwards.
|
|
499
297
|
*/
|
|
500
|
-
function ownStream(cell: Cell): Stream | null {
|
|
298
|
+
export function ownStream(cell: Cell): Stream | null {
|
|
501
299
|
const stream = cell.stream
|
|
502
300
|
return stream !== null && stream.source === null ? stream : null
|
|
503
301
|
}
|
|
@@ -556,480 +354,6 @@ export function isStreamBody(body: (args: never) => unknown): boolean {
|
|
|
556
354
|
return Object.getPrototypeOf(body) === ASYNC_GENERATOR_FUNCTION
|
|
557
355
|
}
|
|
558
356
|
|
|
559
|
-
/**
|
|
560
|
-
* A value's internal state record, apart from the callable an app holds.
|
|
561
|
-
*
|
|
562
|
-
* THE INTERNAL FACE, AND A SECOND OBJECT. With the fields on the callable itself, every
|
|
563
|
-
* one was a property added to a FUNCTION at construction, and that was most of what a
|
|
564
|
-
* value cost to make: moving them here took `construct a value` from 123 ns to 87 under
|
|
565
|
-
* JSC and from 101 to 83 under V8, and a derived value's construction, a chain and a
|
|
566
|
-
* layered DAG 7-13% with it. Nothing measured moved the other way. The callable captures
|
|
567
|
-
* its record, so a read is one context load — the hand-written arm's one captured
|
|
568
|
-
* variable — and a member reaches the record through `CELL`, one load ahead of what it
|
|
569
|
-
* did before.
|
|
570
|
-
*
|
|
571
|
-
* THE FIELD COUNT SITS ON A VM BOUNDARY AND IT IS NOT A LEVER. Both engines give an
|
|
572
|
-
* object a fixed inline slot budget and spill the rest to an out-of-line store, and this
|
|
573
|
-
* record is over it — so the count moves numbers that have nothing to do with the work.
|
|
574
|
-
* Two independent measurements say the same thing from opposite ends. Taking `Cell` to
|
|
575
|
-
* 38 is worth ~11% of `construct a value` and ~6-9% of the derived case, and it does not
|
|
576
|
-
* matter HOW the four go: a consolidation into one record and four fields simply deleted
|
|
577
|
-
* buy the same number, so the consolidation is not what pays. Going the other way,
|
|
578
|
-
* 42 -> 46, is FREE — above the budget the out-of-line store already exists. And dropping
|
|
579
|
-
* ONE field moved chain 20 and both layered DAGs +4.2% under V8 while moving nothing
|
|
580
|
-
* under JSC; adding a DEAD boolean back put all three within 0.6%, which is the proof it
|
|
581
|
-
* was layout and not work — that same dead boolean then cost the probe walk 6-7%.
|
|
582
|
-
*
|
|
583
|
-
* Above the budget is not free under JSC, though: three fields more (the dead-reader
|
|
584
|
-
* counts and `probeCursor`) moved `construct a value` and the derived case +6-8% there,
|
|
585
|
-
* and three UNUSED fields on the old record moved them the same — layout, not work.
|
|
586
|
-
*
|
|
587
|
-
* So the boundary is worth KNOWING and not worth engineering around. A field costs a few
|
|
588
|
-
* ns of construction; getting under costs a structural change that the next field added here
|
|
589
|
-
* silently undoes, and it re-rolls in a different direction for every row. If
|
|
590
|
-
* construction ever needs that 11%, shed fields — do not build machinery to shed them.
|
|
591
|
-
* And when a V8-only move of this size shows up on a graph row, the first thing to check
|
|
592
|
-
* is whether the field count changed.
|
|
593
|
-
*/
|
|
594
|
-
class Cell {
|
|
595
|
-
/**
|
|
596
|
-
* Who reads this value, on the value edge.
|
|
597
|
-
*
|
|
598
|
-
* ARRAYS, not sets. The mark walk walks this per write and a set's iterator
|
|
599
|
-
* allocates. A disposed reader is left in place and counted rather than found — see
|
|
600
|
-
* `dropFrom` in `tracking.ts` — so a walk over this skips `!alive`.
|
|
601
|
-
*/
|
|
602
|
-
declare readers: Subscriber[]
|
|
603
|
-
declare deadReaders: number
|
|
604
|
-
/**
|
|
605
|
-
* Who asked about this value's PROBES — a different edge from `readers`, and see
|
|
606
|
-
* `markProbes` for what that buys.
|
|
607
|
-
*/
|
|
608
|
-
declare probeReaders: Subscriber[]
|
|
609
|
-
declare deadProbeReaders: number
|
|
610
|
-
declare sources: Cell[]
|
|
611
|
-
/**
|
|
612
|
-
* The values whose PROBES this body asked about — a `Cell` is a `Subscriber`, so it
|
|
613
|
-
* carries the reader half of the probe edge as well as the source half.
|
|
614
|
-
*/
|
|
615
|
-
declare probeSources: Cell[]
|
|
616
|
-
declare probeCursor: number
|
|
617
|
-
declare cursor: number
|
|
618
|
-
/** See `Subscriber.seen`. */
|
|
619
|
-
declare seen: Set<Cell> | null
|
|
620
|
-
declare seenThrough: number
|
|
621
|
-
declare scanningFrom: number
|
|
622
|
-
declare label: string
|
|
623
|
-
declare body: ((args: unknown) => unknown) | null
|
|
624
|
-
declare args: unknown
|
|
625
|
-
declare value: unknown
|
|
626
|
-
declare landed: boolean
|
|
627
|
-
/**
|
|
628
|
-
* Whether a read may answer with `value`.
|
|
629
|
-
*
|
|
630
|
-
* WHETHER A READ MAY ANSWER WITH `value`. Implies `landed`, and is false over a landed value
|
|
631
|
-
* a trigger, an abort or an adoption has stopped serving — which is the state `landed`
|
|
632
|
-
* alone cannot say.
|
|
633
|
-
*/
|
|
634
|
-
declare servable: boolean
|
|
635
|
-
/**
|
|
636
|
-
* Whether a read can return `value` straight, without going a frame in.
|
|
637
|
-
*
|
|
638
|
-
* `state === CLEAN && servable` — the whole of "this read can return the field",
|
|
639
|
-
* maintained rather than recomputed. A read is the hottest path in the design and it
|
|
640
|
-
* was three loads and three branches to decide.
|
|
641
|
-
*
|
|
642
|
-
* THE INVARIANT IS ONE-DIRECTIONAL and that is what makes it safe to maintain by
|
|
643
|
-
* hand: `true` is written only where all three are known to hold, and every
|
|
644
|
-
* transition that can break one of them writes `false`. So a site that forgets to
|
|
645
|
-
* set it true costs a frame on the reads that follow and cannot serve a wrong value,
|
|
646
|
-
* which is the opposite of how a maintained flag usually fails.
|
|
647
|
-
*/
|
|
648
|
-
declare direct: boolean
|
|
649
|
-
declare loading: boolean
|
|
650
|
-
declare failure: unknown
|
|
651
|
-
/**
|
|
652
|
-
* The compiled `identity` comparator.
|
|
653
|
-
*
|
|
654
|
-
* `null` IS the default — see `comparatorFor`.
|
|
655
|
-
*/
|
|
656
|
-
declare identityFn: ((next: unknown, previous: unknown) => boolean) | null
|
|
657
|
-
/**
|
|
658
|
-
* The comparator this value DECLARED, which `identityFn` returns to when an adoption ends.
|
|
659
|
-
*
|
|
660
|
-
* 11.17 inherits the adopted value's `identity` only where the outer declared none, and only
|
|
661
|
-
* for as long as it adopts: kept on `identityFn` alone, the inherited comparator outlived the
|
|
662
|
-
* adoption and gated the outer's own productions — `flag() ? inner : count()` with `inner`
|
|
663
|
-
* keyed on `.id` read every number as the same `undefined` and never woke again.
|
|
664
|
-
*/
|
|
665
|
-
declare declaredIdentityFn:
|
|
666
|
-
((next: unknown, previous: unknown) => boolean) | null
|
|
667
|
-
declare transform: ((value: unknown) => unknown) | null
|
|
668
|
-
/**
|
|
669
|
-
* `null` on every value that declares no `schema`, which keeps the gate off the write
|
|
670
|
-
* path of everything that did not ask for one.
|
|
671
|
-
*/
|
|
672
|
-
declare schema: Schema<unknown> | null
|
|
673
|
-
/**
|
|
674
|
-
* A streaming producer's chunk buffer, for a cursor to walk (2.6); `EMPTY_RING` on every
|
|
675
|
-
* other value.
|
|
676
|
-
*/
|
|
677
|
-
declare ring: unknown[]
|
|
678
|
-
/**
|
|
679
|
-
* The ordinal before `ring[0]`, so `ring[index]` carries ordinal `ringBase + index + 1` and a
|
|
680
|
-
* cursor finds its place from the ordinal it holds.
|
|
681
|
-
*/
|
|
682
|
-
declare ringBase: number
|
|
683
|
-
/**
|
|
684
|
-
* `null` on every value that declares no `ttl`, and on every value 6.2 makes it inert
|
|
685
|
-
* for. Its presence is also what holds `direct` false — see `serve`.
|
|
686
|
-
*/
|
|
687
|
-
declare expiry: Expiry | null
|
|
688
|
-
declare ordinal: number
|
|
689
|
-
declare generation: number
|
|
690
|
-
declare alive: boolean
|
|
691
|
-
declare isEffect: boolean
|
|
692
|
-
declare isMirror: boolean
|
|
693
|
-
declare adopter: Tracked | null
|
|
694
|
-
/**
|
|
695
|
-
* The mark: `CLEAN`, `CHECK` or `DIRTY`.
|
|
696
|
-
*
|
|
697
|
-
* `CLEAN`, `CHECK` or `DIRTY` — see `tracking.ts`. Stamped on every transitive
|
|
698
|
-
* reader BEFORE anything runs, so a body that reaches a source which has not caught
|
|
699
|
-
* up yet pulls it forward; `running` is what stops a body already on the stack being
|
|
700
|
-
* re-entered.
|
|
701
|
-
*
|
|
702
|
-
* ONE RUN PER NODE PER WRITE is the contract, and the state carries it without a
|
|
703
|
-
* cascade counter: a node that has run is `CLEAN`, and only a source PRODUCING can
|
|
704
|
-
* mark it again — which a source it already read cannot do, because reading it is
|
|
705
|
-
* what resolved it.
|
|
706
|
-
*/
|
|
707
|
-
declare state: number
|
|
708
|
-
declare running: boolean
|
|
709
|
-
/**
|
|
710
|
-
* Whether the last run aborted on a value that had not landed.
|
|
711
|
-
*
|
|
712
|
-
* 2.13: A RUN THAT ABORTED ON A VALUE THAT HAD NOT LANDED, and is owed the rest of itself
|
|
713
|
-
* when that value arrives. Push-pull leaves a marked body for the read that wants it, and
|
|
714
|
-
* that is right for a body which RAN — this one did not, so nothing downstream can pull it:
|
|
715
|
-
* a reader guarding on `ready()` asks the probe edge and never the value, and the guard would
|
|
716
|
-
* answer false for as long as nobody read through it.
|
|
717
|
-
*/
|
|
718
|
-
declare aborted: boolean
|
|
719
|
-
/**
|
|
720
|
-
* The value this one adopted, or `null`.
|
|
721
|
-
*
|
|
722
|
-
* WHAT THIS VALUE'S BODY HANDED BACK, where it handed back another `Reactive`
|
|
723
|
-
* (11.12). `null` on every value that is not adopting, which is nearly all of them —
|
|
724
|
-
* one field rather than two, and the record is allocated only where there is one.
|
|
725
|
-
*/
|
|
726
|
-
declare adoption: Adoption | null
|
|
727
|
-
/**
|
|
728
|
-
* `null` on every value declaring neither `throttle` nor `debounce`, which is nearly
|
|
729
|
-
* all of them.
|
|
730
|
-
*/
|
|
731
|
-
declare cap: Cap | null
|
|
732
|
-
/**
|
|
733
|
-
* `null` on every value declaring no `tags`, which keeps `markProbes` from looking up a
|
|
734
|
-
* selection signal for any of them.
|
|
735
|
-
*/
|
|
736
|
-
declare tags: string[] | null
|
|
737
|
-
/** Whether `declare` built this value for a mutation, which a sweep invalidates (13.16). */
|
|
738
|
-
declare mutation: boolean
|
|
739
|
-
/**
|
|
740
|
-
* Whether what is held was written here since the producer last produced (3.10).
|
|
741
|
-
*
|
|
742
|
-
* Only ever true on a value with a body: a value with no producer has nothing to be out of
|
|
743
|
-
* step with (3.12). Moved by `writtenHere` and `producedAgain` alone, each owning its probe move.
|
|
744
|
-
*/
|
|
745
|
-
declare local: boolean
|
|
746
|
-
/** `null` on every value declaring no `store`, which is what keeps `serve` from persisting. */
|
|
747
|
-
declare store: Persisted | null
|
|
748
|
-
/**
|
|
749
|
-
* The declared `by`.
|
|
750
|
-
*
|
|
751
|
-
* 5.22 — held as declared rather than as a function, so the property name is still there to send.
|
|
752
|
-
*/
|
|
753
|
-
declare by: string | ((element: unknown) => unknown) | null
|
|
754
|
-
/** The probe pass this cell was last visited in. See `walkBusy` and `walkReady`. */
|
|
755
|
-
declare probePass: number
|
|
756
|
-
/**
|
|
757
|
-
* `OWES_NOTHING`, `OWES_ABOVE` or `OWES_RELOAD` — what an `invalidate` left for the next read
|
|
758
|
-
* to do. See `invalidateOne`.
|
|
759
|
-
*/
|
|
760
|
-
declare owes: number
|
|
761
|
-
/** The value this record is the state of — what `NotLanded` names as its source. */
|
|
762
|
-
declare callable: Reactive<unknown> | null
|
|
763
|
-
/** `null` on every value whose body is not an async generator, which is nearly all. */
|
|
764
|
-
declare stream: Stream | null
|
|
765
|
-
/**
|
|
766
|
-
* Whether a read may ever take the field straight, fixed at construction.
|
|
767
|
-
*
|
|
768
|
-
* WHETHER A READ MAY EVER TAKE THE FIELD STRAIGHT, fixed at construction: no expiry to
|
|
769
|
-
* check and no accumulation to materialize. `setDirect` reads this one field where it
|
|
770
|
-
* would otherwise read one per mechanism that needs the read to go a frame in.
|
|
771
|
-
*/
|
|
772
|
-
declare plain: boolean
|
|
773
|
-
/**
|
|
774
|
-
* Whether a settled write goes straight to `produce`, past no gate.
|
|
775
|
-
*
|
|
776
|
-
* NOTHING STANDS BETWEEN A SETTLED WRITE AND `produce`: no `schema`, no `transform`, and no
|
|
777
|
-
* stream whose writes stand apart from its chunks (9.11, 9.12). One field where the write and
|
|
778
|
-
* the recompute each tested two, maintained where a forwarded stream comes and goes.
|
|
779
|
-
*/
|
|
780
|
-
declare bareWrite: boolean
|
|
781
|
-
/**
|
|
782
|
-
* Whether a gated write can take `SIMPLE_PROTOTYPE`'s short path.
|
|
783
|
-
*
|
|
784
|
-
* EVERYTHING A GATED WRITE WOULD OTHERWISE ASK, IN ONE FIELD: no cap holding the
|
|
785
|
-
* production back, no expiry, no store to persist through, no
|
|
786
|
-
* declared `identity`, and `bareWrite`'s own three. A value carrying none of them has a
|
|
787
|
-
* `produce` -> `serve` -> `cascade` that reduces to three stores and a mark — which is
|
|
788
|
-
* what `SIMPLE_PROTOTYPE.set` spells out rather than calls.
|
|
789
|
-
*
|
|
790
|
-
* IT SELECTS A PROTOTYPE AND IS NOT READ ON THE WRITE PATH. See `SIMPLE_PROTOTYPE`.
|
|
791
|
-
*
|
|
792
|
-
* MAINTAINED, NOT FIXED: three of the fields under it move after construction —
|
|
793
|
-
* `bareWrite` and `plain` where a forwarded stream comes and goes, `identityFn` where an
|
|
794
|
-
* adoption takes the inner value's. `simpleShape` is called at each of those.
|
|
795
|
-
*/
|
|
796
|
-
declare simple: boolean
|
|
797
|
-
/**
|
|
798
|
-
* The scope this value was built in, and so the one its body runs under.
|
|
799
|
-
*
|
|
800
|
-
* 11.32 — THE SCOPE THIS VALUE WAS BUILT IN, and so the one its body runs under. `undefined` in a
|
|
801
|
-
* browser, and on a server for a value built outside any request.
|
|
802
|
-
*/
|
|
803
|
-
declare scope: Scope | undefined
|
|
804
|
-
/**
|
|
805
|
-
* Whether this value is one for the whole process, in every scope.
|
|
806
|
-
*
|
|
807
|
-
* 11.34, 13.13 — one value for the process, and so inside every scope a selection is made in. An
|
|
808
|
-
* entry of a `global` keyed value is one too, whichever request first reached it.
|
|
809
|
-
*/
|
|
810
|
-
declare global: boolean
|
|
811
|
-
/**
|
|
812
|
-
* The one weak handle every selection index holds this value by, or `null` where none does.
|
|
813
|
-
*
|
|
814
|
-
* 13.1 AND 13.6: a value filed for the bare sweep or carrying `tags` is findable by one, and
|
|
815
|
-
* every index holds the same handle, so an eviction drops it from all of them at once.
|
|
816
|
-
*/
|
|
817
|
-
declare ref: WeakRef<Cell> | null
|
|
818
|
-
/**
|
|
819
|
-
* Whether this value is filed for the bare selection — once, where it first becomes a load.
|
|
820
|
-
*
|
|
821
|
-
* See `fileProducer`. Not cleared by an eviction: dropping `ref` is what takes an evicted entry
|
|
822
|
-
* out, and this staying set is what keeps its next load from filing it again.
|
|
823
|
-
*/
|
|
824
|
-
declare filed: boolean
|
|
825
|
-
/**
|
|
826
|
-
* Whether what this value was built in has stopped — the setup or the request that owned it.
|
|
827
|
-
*
|
|
828
|
-
* 12.14. Set by `orphan`; the value lets go of its sources once nothing
|
|
829
|
-
* reads it (`releaseOrphan`), which may be at once or when the last reader outside its owner leaves.
|
|
830
|
-
*/
|
|
831
|
-
declare orphaned: boolean
|
|
832
|
-
|
|
833
|
-
constructor(
|
|
834
|
-
label: string,
|
|
835
|
-
body: ((args: unknown) => unknown) | null,
|
|
836
|
-
args: unknown,
|
|
837
|
-
options: ReactiveOptions<unknown, unknown, never> | undefined,
|
|
838
|
-
streaming: boolean,
|
|
839
|
-
) {
|
|
840
|
-
// Every field, in one order, `undefined` included.
|
|
841
|
-
this.readers = []
|
|
842
|
-
this.deadReaders = 0
|
|
843
|
-
this.probeReaders = []
|
|
844
|
-
this.deadProbeReaders = 0
|
|
845
|
-
this.sources = []
|
|
846
|
-
this.probeSources = []
|
|
847
|
-
this.probeCursor = 0
|
|
848
|
-
this.cursor = 0
|
|
849
|
-
this.seen = null
|
|
850
|
-
this.seenThrough = 0
|
|
851
|
-
this.scanningFrom = -1
|
|
852
|
-
this.label = label
|
|
853
|
-
this.body = body
|
|
854
|
-
this.args = args
|
|
855
|
-
this.value = undefined
|
|
856
|
-
this.landed = false
|
|
857
|
-
this.servable = false
|
|
858
|
-
this.direct = false
|
|
859
|
-
this.loading = false
|
|
860
|
-
this.failure = undefined
|
|
861
|
-
this.identityFn = comparatorFor(
|
|
862
|
-
options?.identity as Identity<unknown> | undefined,
|
|
863
|
-
)
|
|
864
|
-
this.declaredIdentityFn = this.identityFn
|
|
865
|
-
this.transform = (options?.transform ?? null) as
|
|
866
|
-
((value: unknown) => unknown) | null
|
|
867
|
-
this.schema = (options?.schema ?? null) as Schema<unknown> | null
|
|
868
|
-
this.ring = EMPTY_RING
|
|
869
|
-
this.ringBase = 0
|
|
870
|
-
// 6.2 DECIDED ONCE, here rather than per production: a body is what makes a value a
|
|
871
|
-
// producer, and a plain state is the one this must not reach. See `expiryFor`.
|
|
872
|
-
this.expiry = expiryFor(
|
|
873
|
-
options?.ttl,
|
|
874
|
-
// A store is a producer too (8.2): there is somewhere to get the value back from, so
|
|
875
|
-
// `ttl` on a value holding one drops rather than being inert.
|
|
876
|
-
body !== null || options?.store !== undefined,
|
|
877
|
-
)
|
|
878
|
-
this.ordinal = 0
|
|
879
|
-
this.generation = 0
|
|
880
|
-
this.alive = true
|
|
881
|
-
this.state = CLEAN
|
|
882
|
-
this.running = false
|
|
883
|
-
this.aborted = false
|
|
884
|
-
this.adoption = null
|
|
885
|
-
this.cap = capFor(label, options?.throttle, options?.debounce)
|
|
886
|
-
const tags = options?.tags
|
|
887
|
-
this.tags =
|
|
888
|
-
tags === undefined
|
|
889
|
-
? null
|
|
890
|
-
: typeof tags === 'function'
|
|
891
|
-
? tags(args as never)
|
|
892
|
-
: tags
|
|
893
|
-
this.mutation =
|
|
894
|
-
(options as { [MUTATION]?: true } | undefined)?.[MUTATION] === true
|
|
895
|
-
this.local = false
|
|
896
|
-
const store = options?.store
|
|
897
|
-
this.store =
|
|
898
|
-
store === undefined
|
|
899
|
-
? null
|
|
900
|
-
: {
|
|
901
|
-
store: (typeof store === 'function'
|
|
902
|
-
? store(args as never)
|
|
903
|
-
: store) as Store<unknown>,
|
|
904
|
-
ttl: options?.ttl ?? Number.POSITIVE_INFINITY,
|
|
905
|
-
restoring: false,
|
|
906
|
-
writing: false,
|
|
907
|
-
pending: false,
|
|
908
|
-
unconfirmed: undefined,
|
|
909
|
-
unwatch: null,
|
|
910
|
-
}
|
|
911
|
-
this.by = (options?.by ?? null) as
|
|
912
|
-
string | ((element: unknown) => unknown) | null
|
|
913
|
-
this.probePass = 0
|
|
914
|
-
this.owes = OWES_NOTHING
|
|
915
|
-
this.isEffect = false
|
|
916
|
-
this.isMirror = false
|
|
917
|
-
this.adopter = null
|
|
918
|
-
this.callable = null
|
|
919
|
-
this.stream = streaming ? newStream(null) : null
|
|
920
|
-
this.plain = this.expiry === null && this.stream === null
|
|
921
|
-
this.bareWrite =
|
|
922
|
-
this.schema === null && this.transform === null && !streaming
|
|
923
|
-
this.simple = false
|
|
924
|
-
this.scope = currentScope()
|
|
925
|
-
this.global = options?.global === true
|
|
926
|
-
this.ref = null
|
|
927
|
-
this.filed = false
|
|
928
|
-
this.orphaned = false
|
|
929
|
-
simpleShape(this)
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
notify(): void {
|
|
933
|
-
run(this)
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
/** What a member's `this` is, and the one way from the value an app holds to its record. */
|
|
938
|
-
type Callable = { [CELL]: Cell }
|
|
939
|
-
|
|
940
|
-
/**
|
|
941
|
-
* Throws where a value declares both `throttle` and `debounce`.
|
|
942
|
-
*
|
|
943
|
-
* 5.14 is a BUILD error and there is no build yet, so it lands at construction — the
|
|
944
|
-
* earliest moment that exists, and the one the app still has a stack for. Naming the value
|
|
945
|
-
* is the clause's own requirement; without it a page declaring both on one of forty values
|
|
946
|
-
* says only that something did. One statement for both producers of a window: a value's
|
|
947
|
-
* (5.8) and a keyed value's, which caps starts rather than wakes (5.19).
|
|
948
|
-
*/
|
|
949
|
-
export function refuseBothCaps(
|
|
950
|
-
label: string,
|
|
951
|
-
throttle: number | undefined,
|
|
952
|
-
debounce: number | undefined,
|
|
953
|
-
): void {
|
|
954
|
-
if (throttle !== undefined && debounce !== undefined)
|
|
955
|
-
throw new Error(
|
|
956
|
-
`${label} declares both throttle and debounce. A value is capped one way or the other.`,
|
|
957
|
-
)
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
function capFor(
|
|
961
|
-
label: string,
|
|
962
|
-
throttle: number | undefined,
|
|
963
|
-
debounce: number | undefined,
|
|
964
|
-
): Cap | null {
|
|
965
|
-
refuseBothCaps(label, throttle, debounce)
|
|
966
|
-
const ms = throttle ?? debounce
|
|
967
|
-
if (ms === undefined) return null
|
|
968
|
-
return {
|
|
969
|
-
leading: throttle !== undefined,
|
|
970
|
-
ms,
|
|
971
|
-
timer: null,
|
|
972
|
-
held: undefined,
|
|
973
|
-
holding: false,
|
|
974
|
-
minted: undefined,
|
|
975
|
-
owed: false,
|
|
976
|
-
reloading: false,
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
/**
|
|
981
|
-
* Compiles `identity` into a comparator, or `null` for the default `Object.is`.
|
|
982
|
-
*
|
|
983
|
-
* THE DEFAULT IS THE REFERENCE, and `structural` is exported for an app to pass.
|
|
984
|
-
*
|
|
985
|
-
* THE CASE FOR A STRUCTURAL DEFAULT IS REAL: a reload that fetched an equal
|
|
986
|
-
* payload builds a FRESH object, so a reference compare never fires and every reader
|
|
987
|
-
* wakes on a value that did not change. What decides it against that is the cost on
|
|
988
|
-
* the shape an app actually holds — a compare walks the whole value on EVERY write,
|
|
989
|
-
* where the wake it avoids is paid only when the payload really is equal. A grid of
|
|
990
|
-
* rows re-assembled per recompute pays the deep walk every time and collapses never.
|
|
991
|
-
*
|
|
992
|
-
* So the framework takes the cheap, predictable default — `===` is what JavaScript
|
|
993
|
-
* means by "the same value" — and the app names `identity: structural` on the values
|
|
994
|
-
* where re-materialization is the common case. 5.5's "not equal wherever it cannot
|
|
995
|
-
* decide" is unchanged; it is just no longer the default reading.
|
|
996
|
-
*
|
|
997
|
-
* `identity` is discriminated by ARITY (5.3), once, at construction — not per compare.
|
|
998
|
-
*
|
|
999
|
-
* NULL IS THE DEFAULT, not a function that implements it. `produce` compares against
|
|
1000
|
-
* `null` and calls `Object.is` directly, which takes an INDIRECT call off the write path:
|
|
1001
|
-
* the site is polymorphic across the default, a projection closure and whatever an app
|
|
1002
|
-
* passed, so it is not one an engine can speculate through. The saving is the indirection
|
|
1003
|
-
* rather than the comparison — `Object.is` is itself the fastest spelling of it.
|
|
1004
|
-
*/
|
|
1005
|
-
function comparatorFor(
|
|
1006
|
-
identity: Identity<unknown> | undefined,
|
|
1007
|
-
): ((next: unknown, previous: unknown) => boolean) | null {
|
|
1008
|
-
if (identity === undefined) return null
|
|
1009
|
-
if (identity.length >= 2)
|
|
1010
|
-
return identity as (next: unknown, previous: unknown) => boolean
|
|
1011
|
-
const project = identity as (value: unknown) => unknown
|
|
1012
|
-
return (next, previous) => project(next) === project(previous)
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
/**
|
|
1016
|
-
* Recomputes `simple` and re-seats the callable on the matching prototype.
|
|
1017
|
-
*
|
|
1018
|
-
* `simple` IN ONE PLACE. A second copy of this conjunction is the drift that would let one of the
|
|
1019
|
-
* sites go on reading `true` after the field under it moved — and what the short write would then
|
|
1020
|
-
* skip is a cap, a ring or a declared `identity`, silently. The prototype that goes with it is
|
|
1021
|
-
* `seat`'s, taken where the transition that moved a field under it ends.
|
|
1022
|
-
*/
|
|
1023
|
-
function simpleShape(cell: Cell): void {
|
|
1024
|
-
cell.simple =
|
|
1025
|
-
cell.bareWrite &&
|
|
1026
|
-
// `plain` carries `expiry === null` and `stream === null` already.
|
|
1027
|
-
cell.plain &&
|
|
1028
|
-
cell.cap === null &&
|
|
1029
|
-
cell.store === null &&
|
|
1030
|
-
cell.identityFn === null
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
357
|
/**
|
|
1034
358
|
* The prototype a value's state calls for: its own stream's writes (9.11), an adoption's (11.59),
|
|
1035
359
|
* the short write of a `simple` value, or the members alone.
|
|
@@ -1345,7 +669,7 @@ function markProbes(cell: Cell, pass: number): void {
|
|
|
1345
669
|
markAsked(cell.probeReaders)
|
|
1346
670
|
// 13.3: a guard over a tag selection asked the SELECTION, so it hears through the selection's
|
|
1347
671
|
// signal rather than through this value's own probe readers.
|
|
1348
|
-
if (cell.tags !== null) markSignals(cell.tags)
|
|
672
|
+
if (cell.tags !== null) markSignals(cell.tags, markAsked)
|
|
1349
673
|
// The bare selection's guard hears every filed value's floor move; one load where nothing asks.
|
|
1350
674
|
if (EVERY_PRODUCER.probeReaders.length !== 0 && cell.filed)
|
|
1351
675
|
markAsked(EVERY_PRODUCER.probeReaders)
|
|
@@ -2462,6 +1786,12 @@ function run(cell: Cell): void {
|
|
|
2462
1786
|
absorb(cell, produced, true)
|
|
2463
1787
|
}
|
|
2464
1788
|
|
|
1789
|
+
// A CELL'S WAKE IS ITS RUN — on the prototype, where the class method stood, so `reader.notify()`
|
|
1790
|
+
// reaches it the way it always did; `cellShape.ts` cannot import `run` and stay off the cycle.
|
|
1791
|
+
Cell.prototype.notify = function notify(this: Cell): void {
|
|
1792
|
+
run(this)
|
|
1793
|
+
}
|
|
1794
|
+
|
|
2465
1795
|
/**
|
|
2466
1796
|
* Leaves a run that read a value which had not landed owing the rest of itself (2.13).
|
|
2467
1797
|
*
|
|
@@ -3106,13 +2436,6 @@ function readIndirect(cell: Cell): unknown {
|
|
|
3106
2436
|
return cell.value
|
|
3107
2437
|
}
|
|
3108
2438
|
|
|
3109
|
-
/**
|
|
3110
|
-
* Every value that is not streaming shares this as its `ring`. The field stays monomorphically an
|
|
3111
|
-
* Array — CLAUDE.md's shape rule — and only a stream's own chunk buffer is ever pushed to. Frozen so
|
|
3112
|
-
* that if anything ever does push here, it says so rather than corrupting every other value at once.
|
|
3113
|
-
*/
|
|
3114
|
-
const EMPTY_RING = Object.freeze([]) as unknown as unknown[]
|
|
3115
|
-
|
|
3116
2439
|
/**
|
|
3117
2440
|
* The current probe pass, stamped on each cell a probe walk visits.
|
|
3118
2441
|
*
|
|
@@ -3415,11 +2738,6 @@ export function refuseInto(value: Reactive<unknown>, failure: unknown): void {
|
|
|
3415
2738
|
refused((value as unknown as Callable)[CELL], failure)
|
|
3416
2739
|
}
|
|
3417
2740
|
|
|
3418
|
-
/** Whether `value` holds what was written here, without subscribing — for a caller that is not a reader. */
|
|
3419
|
-
export function writtenLocally(value: Reactive<unknown>): boolean {
|
|
3420
|
-
return (value as unknown as Callable)[CELL].local
|
|
3421
|
-
}
|
|
3422
|
-
|
|
3423
2741
|
/**
|
|
3424
2742
|
* A promise of the value once it is servable and not busy, or of its failure.
|
|
3425
2743
|
*
|
|
@@ -3600,20 +2918,6 @@ function wakePatched(cell: Cell, head: unknown): void {
|
|
|
3600
2918
|
}
|
|
3601
2919
|
}
|
|
3602
2920
|
|
|
3603
|
-
/**
|
|
3604
|
-
* What an `invalidate` leaves behind: nothing owed, a look above, or a reload.
|
|
3605
|
-
*
|
|
3606
|
-
* WHAT AN INVALIDATE LEAVES BEHIND, and it is a debt rather than a mark. The mark walk's states
|
|
3607
|
-
* are the wrong tool: a node marked `CHECK` or `DIRTY` without its effects queued is one a later
|
|
3608
|
-
* write's walk stops at — `state >= wanted` — and the effects below it are never woken for a
|
|
3609
|
-
* change that really happened. So nothing in the graph's state moves; the invalidated value OWES
|
|
3610
|
-
* A RELOAD, the values built from it OWE A LOOK ABOVE, and none of them is directly servable, so
|
|
3611
|
-
* the read that reaches any of them goes a frame in and settles the debt first (7.12, 7.13).
|
|
3612
|
-
*/
|
|
3613
|
-
const OWES_NOTHING: number = 0
|
|
3614
|
-
const OWES_ABOVE: number = 1
|
|
3615
|
-
const OWES_RELOAD: number = 2
|
|
3616
|
-
|
|
3617
2921
|
/**
|
|
3618
2922
|
* Visits a value and each value down its adoption chain.
|
|
3619
2923
|
*
|
|
@@ -4110,193 +3414,6 @@ function storeWritten(cell: Cell, persisted: Persisted): void {
|
|
|
4110
3414
|
|
|
4111
3415
|
// ── Selections over `tags`, and the bare sweep ──────────────────────────────────────────
|
|
4112
3416
|
|
|
4113
|
-
/**
|
|
4114
|
-
* Each tag to the values carrying it, held weakly.
|
|
4115
|
-
*
|
|
4116
|
-
* THE INDEX HOLDS VALUES WEAKLY. A tag is a way to find a value, never a reason to keep one, and a
|
|
4117
|
-
* strong index would hold every tagged value a page ever made for the life of the process. A
|
|
4118
|
-
* collected value is pruned from its sets by the next sweep that finds it gone.
|
|
4119
|
-
*/
|
|
4120
|
-
const TAGGED = new Map<string, Set<WeakRef<Cell>>>()
|
|
4121
|
-
|
|
4122
|
-
/**
|
|
4123
|
-
* Every value whose producer is a LOAD, held weakly — what a bare selection sweeps.
|
|
4124
|
-
*
|
|
4125
|
-
* 13.6: A BARE SWEEP REACHES PRODUCERS, and of those it files the ones that LOAD: a value holding
|
|
4126
|
-
* a `store`, filed where it is built, and a body the first time it starts a load — a pending
|
|
4127
|
-
* answer or a stream (`beginLoad`). A synchronous body is a derivation: reloading it recomputes
|
|
4128
|
-
* what its sources already say, and whatever load it reads is filed on its own. And filing is
|
|
4129
|
-
* what every filed value pays — a weak handle measured ~230 ns a value under V8 against ~20 under
|
|
4130
|
-
* JSC — so a value with nothing to load never pays it: a derivation per cell per row stays at
|
|
4131
|
-
* what it cost before, and a load pays it beside the promise it already allocates.
|
|
4132
|
-
*
|
|
4133
|
-
* Held weakly for `TAGGED`'s reason. AN ARRAY, COMPACTED, RATHER THAN A `Set`: a `Set` of weak
|
|
4134
|
-
* handles measured ~120 ns a filing under JSC against ~10 for a push. What a `Set` bought was an
|
|
4135
|
-
* O(1) delete on eviction; here an evicted value drops its `ref` instead, and a handle whose value
|
|
4136
|
-
* no longer points back at it is dead, the same as one whose value was collected. Because a bare
|
|
4137
|
-
* sweep may never run, the filing that finds the array past `compactProducersAt` compacts it, and
|
|
4138
|
-
* the bar doubles past what survives, so the walk is amortized over the filings between two.
|
|
4139
|
-
*/
|
|
4140
|
-
let filedProducers: WeakRef<Cell>[] = []
|
|
4141
|
-
let compactProducersAt = 1024
|
|
4142
|
-
|
|
4143
|
-
/**
|
|
4144
|
-
* The probe signal a guard over a selection subscribes to.
|
|
4145
|
-
*
|
|
4146
|
-
* 13.3 AND 13.9: ONE SIGNAL PER SELECTION, keyed by its canonical tags. A guard over a selection
|
|
4147
|
-
* subscribes to this, never to each entry — the probe edge `trackProbe` makes, from a node that
|
|
4148
|
-
* is nothing but that edge.
|
|
4149
|
-
*/
|
|
4150
|
-
const SIGNALS = new Map<string, Tracked>()
|
|
4151
|
-
const SIGNALS_BY_TAG = new Map<string, Tracked[]>()
|
|
4152
|
-
/**
|
|
4153
|
-
* The bare selection's one signal. Not a key in `SIGNALS`: `{ tags: [] }` canonicalizes to the
|
|
4154
|
-
* empty set and matches nothing, where the bare selection matches every filed load in reach.
|
|
4155
|
-
*/
|
|
4156
|
-
const EVERY_PRODUCER = newTracked()
|
|
4157
|
-
|
|
4158
|
-
function refOf(cell: Cell): WeakRef<Cell> {
|
|
4159
|
-
let ref = cell.ref
|
|
4160
|
-
if (ref === null) {
|
|
4161
|
-
ref = new WeakRef(cell)
|
|
4162
|
-
cell.ref = ref
|
|
4163
|
-
}
|
|
4164
|
-
return ref
|
|
4165
|
-
}
|
|
4166
|
-
|
|
4167
|
-
function tagCell(cell: Cell): void {
|
|
4168
|
-
const ref = refOf(cell)
|
|
4169
|
-
for (const tag of cell.tags as string[]) {
|
|
4170
|
-
let carrying = TAGGED.get(tag)
|
|
4171
|
-
if (carrying === undefined) {
|
|
4172
|
-
carrying = new Set()
|
|
4173
|
-
TAGGED.set(tag, carrying)
|
|
4174
|
-
}
|
|
4175
|
-
carrying.add(ref)
|
|
4176
|
-
}
|
|
4177
|
-
}
|
|
4178
|
-
|
|
4179
|
-
function fileProducer(cell: Cell): void {
|
|
4180
|
-
cell.filed = true
|
|
4181
|
-
filedProducers.push(refOf(cell))
|
|
4182
|
-
if (filedProducers.length > compactProducersAt) {
|
|
4183
|
-
filedProducers = liveProducers()
|
|
4184
|
-
compactProducersAt = Math.max(1024, filedProducers.length * 2)
|
|
4185
|
-
}
|
|
4186
|
-
}
|
|
4187
|
-
|
|
4188
|
-
/** The handles whose value is still standing and still filed — neither collected nor evicted. */
|
|
4189
|
-
function liveProducers(): WeakRef<Cell>[] {
|
|
4190
|
-
const live: WeakRef<Cell>[] = []
|
|
4191
|
-
for (let index = 0; index < filedProducers.length; index += 1) {
|
|
4192
|
-
const ref = filedProducers[index] as WeakRef<Cell>
|
|
4193
|
-
if (ref.deref()?.ref === ref) live.push(ref)
|
|
4194
|
-
}
|
|
4195
|
-
return live
|
|
4196
|
-
}
|
|
4197
|
-
|
|
4198
|
-
/**
|
|
4199
|
-
* Removes an evicted value from every selection index.
|
|
4200
|
-
*
|
|
4201
|
-
* What an eviction calls: an entry out of its `Keyed` is out of every selection with it, rather
|
|
4202
|
-
* than reachable by a sweep until a collection happens to run.
|
|
4203
|
-
*/
|
|
4204
|
-
export function leaveSelections(value: unknown): void {
|
|
4205
|
-
const cell = (value as Callable)[CELL]
|
|
4206
|
-
const ref = cell.ref
|
|
4207
|
-
if (ref === null) return
|
|
4208
|
-
// What takes it out of `filedProducers`: a handle its value no longer points back at is dead.
|
|
4209
|
-
cell.ref = null
|
|
4210
|
-
if (cell.tags === null) return
|
|
4211
|
-
for (const tag of cell.tags) {
|
|
4212
|
-
const carrying = TAGGED.get(tag)
|
|
4213
|
-
if (carrying === undefined) continue
|
|
4214
|
-
carrying.delete(ref)
|
|
4215
|
-
if (carrying.size === 0) TAGGED.delete(tag)
|
|
4216
|
-
}
|
|
4217
|
-
}
|
|
4218
|
-
|
|
4219
|
-
/**
|
|
4220
|
-
* Takes every value filed so far out of the bare selection, each filed again by its next load.
|
|
4221
|
-
*
|
|
4222
|
-
* A TEST'S SEAM, with no caller in the framework — `bareSelection.test.ts` calls it before its
|
|
4223
|
-
* first case: a page's process ends with the page, and a test process runs every file before this
|
|
4224
|
-
* one in it. What those files left held — a module's `global`
|
|
4225
|
-
* value, a follow the page keeps for its life — is in reach of a bare sweep (13.13), and a bare
|
|
4226
|
-
* `refresh()` reloads it and reads `busy()` true while it asks.
|
|
4227
|
-
*/
|
|
4228
|
-
export function unfileProducers(): void {
|
|
4229
|
-
for (let index = 0; index < filedProducers.length; index += 1) {
|
|
4230
|
-
const cell = (filedProducers[index] as WeakRef<Cell>).deref()
|
|
4231
|
-
if (cell !== undefined) cell.filed = false
|
|
4232
|
-
}
|
|
4233
|
-
filedProducers = []
|
|
4234
|
-
}
|
|
4235
|
-
|
|
4236
|
-
/**
|
|
4237
|
-
* The values a selection reaches in the current scope, `global` values included: those carrying
|
|
4238
|
-
* any of `tags`, or every filed load where `tags` is `null` — the bare selection.
|
|
4239
|
-
*
|
|
4240
|
-
* 13.8 — A SELECTION IS SCOPE-BOUNDED, and a tag with it, so a selection inside one request never
|
|
4241
|
-
* reaches the values another request filed. 13.13 — a `global` value is in every scope, so every
|
|
4242
|
-
* selection reaches it, and 13.10 is the bare case of that. In a browser every cell's scope is
|
|
4243
|
-
* `undefined`, and so is this.
|
|
4244
|
-
*/
|
|
4245
|
-
function inReach(tags: string[] | null): Cell[] {
|
|
4246
|
-
const scope = currentScope()
|
|
4247
|
-
if (tags === null) {
|
|
4248
|
-
filedProducers = liveProducers()
|
|
4249
|
-
const found: Cell[] = []
|
|
4250
|
-
for (let index = 0; index < filedProducers.length; index += 1) {
|
|
4251
|
-
const cell = (
|
|
4252
|
-
filedProducers[index] as WeakRef<Cell>
|
|
4253
|
-
).deref() as Cell
|
|
4254
|
-
if (cell.scope === scope || cell.global) found.push(cell)
|
|
4255
|
-
}
|
|
4256
|
-
return found
|
|
4257
|
-
}
|
|
4258
|
-
const found = new Set<Cell>()
|
|
4259
|
-
for (const tag of tags) {
|
|
4260
|
-
const refs = TAGGED.get(tag)
|
|
4261
|
-
if (refs === undefined) continue
|
|
4262
|
-
for (const ref of refs) {
|
|
4263
|
-
const cell = ref.deref()
|
|
4264
|
-
if (cell === undefined) refs.delete(ref)
|
|
4265
|
-
else if (cell.scope === scope || cell.global) found.add(cell)
|
|
4266
|
-
}
|
|
4267
|
-
}
|
|
4268
|
-
return [...found]
|
|
4269
|
-
}
|
|
4270
|
-
|
|
4271
|
-
function trackSignal(tags: string[] | null): void {
|
|
4272
|
-
const reader = TRACKING.reader
|
|
4273
|
-
if (reader === null) return
|
|
4274
|
-
if (tags === null) {
|
|
4275
|
-
trackProbe(EVERY_PRODUCER, reader)
|
|
4276
|
-
return
|
|
4277
|
-
}
|
|
4278
|
-
const key = JSON.stringify([...new Set(tags)].sort())
|
|
4279
|
-
let signal = SIGNALS.get(key)
|
|
4280
|
-
if (signal === undefined) {
|
|
4281
|
-
signal = newTracked()
|
|
4282
|
-
SIGNALS.set(key, signal)
|
|
4283
|
-
for (const tag of tags) {
|
|
4284
|
-
const signals = SIGNALS_BY_TAG.get(tag)
|
|
4285
|
-
if (signals === undefined) SIGNALS_BY_TAG.set(tag, [signal])
|
|
4286
|
-
else signals.push(signal)
|
|
4287
|
-
}
|
|
4288
|
-
}
|
|
4289
|
-
trackProbe(signal, reader)
|
|
4290
|
-
}
|
|
4291
|
-
|
|
4292
|
-
function markSignals(tags: string[]): void {
|
|
4293
|
-
for (const tag of tags) {
|
|
4294
|
-
const signals = SIGNALS_BY_TAG.get(tag)
|
|
4295
|
-
if (signals === undefined) continue
|
|
4296
|
-
for (const signal of signals) markAsked(signal.probeReaders)
|
|
4297
|
-
}
|
|
4298
|
-
}
|
|
4299
|
-
|
|
4300
3417
|
/**
|
|
4301
3418
|
* Whether any value in the selection is busy; a reader subscribes to the selection.
|
|
4302
3419
|
*
|
|
@@ -4484,60 +3601,6 @@ const MEMBERS = {
|
|
|
4484
3601
|
},
|
|
4485
3602
|
}
|
|
4486
3603
|
|
|
4487
|
-
/**
|
|
4488
|
-
* The cell `value` serves from, through every adoption: its identity and its ordinal, and nothing
|
|
4489
|
-
* else of it the server can reach.
|
|
4490
|
-
*
|
|
4491
|
-
* 11.12: an adopting value serves the adopted value's production, and an adoption of an adoption
|
|
4492
|
-
* goes on down — so a follow's version (18.7) and its feed name the cell that actually produced.
|
|
4493
|
-
*/
|
|
4494
|
-
export function servedCellOf(value: unknown): { readonly ordinal: number } {
|
|
4495
|
-
return adoptedCellOf(value)
|
|
4496
|
-
}
|
|
4497
|
-
|
|
4498
|
-
/**
|
|
4499
|
-
* Holds a patch log on the cell `value` serves from, with `invalidated` called on its every
|
|
4500
|
-
* invalidate; returns what releases both. 18.11, 7.10.
|
|
4501
|
-
*/
|
|
4502
|
-
export function followServed(
|
|
4503
|
-
value: unknown,
|
|
4504
|
-
invalidated: () => void,
|
|
4505
|
-
): () => void {
|
|
4506
|
-
return holdLog(adoptedCellOf(value), invalidated)
|
|
4507
|
-
}
|
|
4508
|
-
|
|
4509
|
-
/**
|
|
4510
|
-
* When the production `value` serves stops being servable, as a wall-clock time.
|
|
4511
|
-
*
|
|
4512
|
-
* 21.8 — WHEN THE PRODUCTION A VALUE IS SERVING STOPS BEING SERVABLE, as a wall-clock time: its
|
|
4513
|
-
* own `ttl` from when it was minted (6.5), or its entry's from when the entry last produced
|
|
4514
|
-
* (11.45), through whatever it adopts. Infinity where neither bounds it.
|
|
4515
|
-
*/
|
|
4516
|
-
export function deadlineOf(value: unknown): number {
|
|
4517
|
-
const cell = adoptedCellOf(value)
|
|
4518
|
-
if (cell.expiry !== null) return cell.expiry.at + cell.expiry.ms
|
|
4519
|
-
const entry = ENTRY_LIVES.get(cell)
|
|
4520
|
-
return entry === undefined ? Number.POSITIVE_INFINITY : entry.at + entry.ms
|
|
4521
|
-
}
|
|
4522
|
-
|
|
4523
|
-
/**
|
|
4524
|
-
* The generation of the load `value` has in flight, or `undefined`.
|
|
4525
|
-
*
|
|
4526
|
-
* 16.48 — WHICH LOAD A VALUE HAS IN FLIGHT, as the generation that started it, or `undefined` where
|
|
4527
|
-
* none is. Read off the cell rather than through `busy`, which is a probe and would subscribe the
|
|
4528
|
-
* caller that asked (3.3).
|
|
4529
|
-
*
|
|
4530
|
-
* A WINDOW HOLDING A PRODUCTION IS BUSY AND IS NO LOAD (5.15): nothing is in flight for a call to end
|
|
4531
|
-
* or time, and the window's close is its own bound. Told apart by the hold, which only a value with
|
|
4532
|
-
* neither body nor stream makes — so a write's load taken in under a hold is not one a call times.
|
|
4533
|
-
*/
|
|
4534
|
-
export function loadOf(value: unknown): number | undefined {
|
|
4535
|
-
const cell = (value as Callable)[CELL]
|
|
4536
|
-
if (!cell.loading) return undefined
|
|
4537
|
-
if (cell.cap?.holding && cell.stream === null) return undefined
|
|
4538
|
-
return cell.generation
|
|
4539
|
-
}
|
|
4540
|
-
|
|
4541
3604
|
// The cells a mutation's call has been answered from, so the next call knows the answer is spent.
|
|
4542
3605
|
const ANSWERED = new WeakSet<object>()
|
|
4543
3606
|
|
|
@@ -4568,16 +3631,6 @@ export function dropAnswered(value: unknown): void {
|
|
|
4568
3631
|
dropStanding(cell, expiry)
|
|
4569
3632
|
}
|
|
4570
3633
|
|
|
4571
|
-
/**
|
|
4572
|
-
* The generation `value`'s past stands at, or `undefined` while a load is in flight.
|
|
4573
|
-
*
|
|
4574
|
-
* What 21.15's `Cached` compares across a call, read through adoption as `servedCellOf` is.
|
|
4575
|
-
*/
|
|
4576
|
-
export function heldGenerationOf(value: unknown): number | undefined {
|
|
4577
|
-
const cell = adoptedCellOf(value)
|
|
4578
|
-
return cell.loading ? undefined : cell.generation
|
|
4579
|
-
}
|
|
4580
|
-
|
|
4581
3634
|
/**
|
|
4582
3635
|
* Whether `value` stands on a failure the next reach runs again rather than serves.
|
|
4583
3636
|
*
|
|
@@ -4617,26 +3670,6 @@ export function streamingOrSettled(value: unknown): Promise<unknown> {
|
|
|
4617
3670
|
return settled((value as Callable)[CELL], true)
|
|
4618
3671
|
}
|
|
4619
3672
|
|
|
4620
|
-
/**
|
|
4621
|
-
* Whether `value` is a streaming producer's `Reactive`.
|
|
4622
|
-
*
|
|
4623
|
-
* A streaming producer's own `Reactive` — a body declared as an async generator, or one whose run
|
|
4624
|
-
* handed one back (9.6).
|
|
4625
|
-
*/
|
|
4626
|
-
export function isStreaming(value: unknown): boolean {
|
|
4627
|
-
if (!isReactive(value)) return false
|
|
4628
|
-
const stream = (value as Partial<Callable>)[CELL]?.stream
|
|
4629
|
-
return stream !== null && stream !== undefined
|
|
4630
|
-
}
|
|
4631
|
-
|
|
4632
|
-
function adoptedCellOf(value: unknown): Cell {
|
|
4633
|
-
let cell = (value as Callable)[CELL]
|
|
4634
|
-
// A stream's own `adoption` is a write it follows (9.11), and its past is its own.
|
|
4635
|
-
while (cell.adoption !== null && ownStream(cell) === null)
|
|
4636
|
-
cell = cell.adoption.source
|
|
4637
|
-
return cell
|
|
4638
|
-
}
|
|
4639
|
-
|
|
4640
3673
|
/**
|
|
4641
3674
|
* The keys of every `Reactive` member.
|
|
4642
3675
|
*
|