@abide/abide 0.101.0 → 0.102.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/CHANGELOG.md +16 -0
- package/bin/abide.ts +223 -164
- package/docs/REGISTRY.md +726 -0
- package/docs/RULEBOOK.md +2753 -0
- package/docs/abide.md +12878 -0
- package/package.json +5 -3
- package/scaffold/AGENTS.md +31 -0
- package/scaffold/CLAUDE.md +1 -0
- package/scaffold/src/layout.abide +18 -0
- package/src/compiler/Lowering.ts +78 -9
- package/src/compiler/ShadowMap.ts +2 -2
- package/src/compiler/TypeSession.ts +262 -67
- package/src/compiler/argsSchemaOf.ts +1 -1
- package/src/compiler/checkApp.ts +43 -15
- package/src/compiler/checkedShadow.ts +39 -5
- package/src/compiler/classifyRead.ts +6 -4
- package/src/compiler/classifyTemplate.ts +1 -1
- package/src/compiler/compileApp.ts +433 -193
- package/src/compiler/compiledEntry.ts +43 -39
- package/src/compiler/componentGraph.ts +58 -71
- package/src/compiler/declaredExports.ts +24 -7
- package/src/compiler/emitClient.ts +141 -71
- package/src/compiler/emitComponent.ts +37 -8
- package/src/compiler/emitSource.ts +106 -13
- package/src/compiler/handlersUnder.ts +106 -59
- package/src/compiler/index.ts +7 -0
- package/src/compiler/mountsOf.ts +138 -0
- package/src/compiler/nearest.ts +14 -0
- package/src/compiler/parseAbide.ts +157 -20
- package/src/compiler/projectShadow.ts +213 -7
- package/src/compiler/reactiveTypes.ts +20 -7
- package/src/compiler/readSources.ts +15 -2
- package/src/compiler/readsIn.ts +61 -7
- package/src/compiler/renderMarkup.ts +3 -3
- package/src/compiler/scaffold.ts +30 -11
- package/src/compiler/serveLanguage.ts +125 -34
- package/src/compiler/sourceMapOf.ts +193 -0
- package/src/compiler/splitImports.ts +205 -83
- package/src/compiler/syncTypes.ts +32 -0
- package/src/compiler/tokenize.ts +14 -0
- package/src/compiler/writeDefaultProject.ts +1 -1
- package/src/compiler/writeTypes.ts +82 -0
- package/src/server/CLIENT_BUILD.ts +11 -13
- package/src/server/DEFAULT_PORT.ts +1 -1
- package/src/server/LAUNCHER_FILES.ts +9 -0
- package/src/server/MODULE_QUERIES.ts +18 -0
- package/src/server/TABLES.ts +7 -0
- package/src/server/VIRTUAL_DIRECTORY.ts +4 -5
- package/src/server/abideModules.ts +391 -0
- package/src/server/answerFailure.ts +16 -6
- package/src/server/answerRequest.ts +235 -106
- package/src/server/answerRpc.ts +102 -30
- package/src/server/answerSocket.ts +65 -20
- package/src/server/answerValue.ts +22 -9
- package/src/server/appDirectories.ts +1 -1
- package/src/server/appPlugins.ts +1 -1
- package/src/server/appTree.ts +179 -0
- package/src/server/argsFor.ts +2 -2
- package/src/server/assistant.ts +2 -2
- package/src/server/bridgeRelay.ts +6 -8
- package/src/server/claudeCode.ts +0 -6
- package/src/server/clientBuildOptions.ts +8 -3
- package/src/server/coerceArgs.ts +1 -1
- package/src/server/commands.ts +37 -37
- package/src/server/componentModules.d.ts +3 -1
- package/src/server/compressAnswer.ts +4 -4
- package/src/server/config.ts +25 -29
- package/src/server/configFields.ts +31 -25
- package/src/server/createApp.ts +59 -73
- package/src/server/devWorker.ts +114 -181
- package/src/server/executableName.ts +4 -0
- package/src/server/fenceServer.ts +37 -35
- package/src/server/generatedSurfaces.ts +99 -66
- package/src/server/hooks.ts +124 -52
- package/src/server/iconAccent.ts +191 -0
- package/src/server/index.ts +11 -7
- package/src/server/installSourceMaps.ts +88 -0
- package/src/server/launcherOptions.ts +34 -31
- package/src/server/launcherPage.ts +107 -64
- package/src/server/manifestOf.ts +16 -13
- package/src/server/namesUnder.ts +2 -2
- package/src/server/nativeMenu.ts +5 -5
- package/src/server/negotiateEncoding.ts +1 -1
- package/src/server/principal.ts +10 -5
- package/src/server/realOrAsGiven.ts +10 -0
- package/src/server/refuseAddressCollision.ts +21 -14
- package/src/server/remote.ts +49 -52
- package/src/server/render.ts +65 -94
- package/src/server/renderComponent.ts +5 -0
- package/src/server/reportHealth.ts +2 -2
- package/src/server/requestScope.ts +12 -1
- package/src/server/runBuild.ts +24 -0
- package/src/server/runCompiled.ts +5 -5
- package/src/server/runDesktop.ts +52 -27
- package/src/server/serveBuild.ts +43 -52
- package/src/server/serveDev.ts +73 -37
- package/src/server/shell.ts +7 -22
- package/src/server/sourceMappings.ts +139 -0
- package/src/server/surfaceOf.ts +18 -3
- package/src/server/testBuild.ts +1 -19
- package/src/server/withAbideSources.ts +117 -0
- package/src/shared/ADDRESSES.ts +7 -18
- package/src/shared/APP_META.ts +7 -0
- package/src/shared/CopyOptions.ts +1 -1
- package/src/shared/DEBUG_META.ts +1 -1
- package/src/shared/RESERVED_DIRECTORIES.ts +15 -0
- package/src/shared/cookies.ts +2 -2
- package/src/shared/csp.ts +1 -1
- package/src/shared/escapeText.ts +1 -1
- package/src/shared/filledPath.ts +85 -0
- package/src/shared/framing.ts +79 -6
- package/src/shared/headKeyOf.ts +2 -0
- package/src/shared/index.ts +1 -1
- package/src/shared/log.ts +27 -13
- package/src/shared/matchRoute.ts +23 -4
- package/src/shared/menuItem.ts +3 -3
- package/src/shared/reactive/Keyed.ts +2 -2
- package/src/shared/reactive/NotLanded.ts +1 -1
- package/src/shared/reactive/Reactive.ts +43 -43
- package/src/shared/reactive/argsContract.ts +4 -4
- package/src/shared/reactive/canonicalKey.ts +4 -4
- package/src/shared/reactive/chunksOfStream.ts +6 -0
- package/src/shared/reactive/index.ts +1 -1
- package/src/shared/reactive/recordWrites.ts +103 -5
- package/src/shared/reactive/structural.ts +1 -1
- package/src/shared/reactive/tracking.ts +1 -1
- package/src/shared/reactive/watch.ts +3 -3
- package/src/shared/refuse.ts +1 -1
- package/src/shared/routePattern.ts +82 -46
- package/src/shared/rpc.ts +37 -8
- package/src/shared/rpcRequest.ts +27 -3
- package/src/shared/url.ts +4 -44
- package/src/shared/writePath.ts +1 -1
- package/src/ui/LAYER_NAMES.ts +1 -1
- package/src/ui/List.ts +3 -3
- package/src/ui/MOUNTING_LEVEL.ts +18 -0
- package/src/ui/Mismatch.ts +1 -1
- package/src/ui/RawRun.ts +1 -1
- package/src/ui/Router.ts +38 -31
- package/src/ui/Scope.ts +1 -1
- package/src/ui/TryRegion.ts +2 -2
- package/src/ui/applyHead.ts +33 -0
- package/src/ui/index.ts +1 -1
- package/src/ui/menuBridge.ts +1 -1
- package/src/ui/moveNode.ts +1 -1
- package/src/ui/reconcile.ts +2 -2
- package/src/ui/reportMenu.ts +2 -2
- package/src/ui/routing.ts +2 -2
- package/src/ui/setAttribute.ts +1 -1
- package/src/ui/setText.ts +1 -1
- package/src/ui/socketWire.ts +9 -7
- package/src/ui/wire.ts +1 -1
- package/scaffold/src/server/app.ts +0 -6
- package/scaffold/src/shared/.gitkeep +0 -0
- package/scaffold/src/ui/app.html +0 -8
- package/src/server/componentImports.ts +0 -56
- /package/scaffold/src/{ui/pages/page.abide → page.abide} +0 -0
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
//
|
|
11
11
|
// Every field `Cell` 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
|
-
// read downstream of it
|
|
13
|
+
// read downstream of it deoptimizes.
|
|
14
14
|
|
|
15
15
|
import { type Failed, isFailed, isFailedNamed } from '../Failed.ts'
|
|
16
16
|
import { namedLogger } from '../log.ts'
|
|
@@ -139,7 +139,7 @@ export type ReactiveOptions<
|
|
|
139
139
|
/**
|
|
140
140
|
* Shapes a settled value on the way in, or refuses it by returning a `Failed`.
|
|
141
141
|
*
|
|
142
|
-
* RULEBOOK 4.6, 4.7: runs untracked, on the settled value, once per
|
|
142
|
+
* RULEBOOK 4.6, 4.7: runs untracked, on the settled value, once per materialization,
|
|
143
143
|
* and refuses by RETURNING a `Failed` rather than by throwing. A throw here would be
|
|
144
144
|
* indistinguishable from the body having failed, and 4.9 has a refused write mint
|
|
145
145
|
* nothing where a failed producer (2.3) may still have something to say.
|
|
@@ -180,7 +180,7 @@ export type ReactiveOptions<
|
|
|
180
180
|
* What tells one array element from another.
|
|
181
181
|
*
|
|
182
182
|
* RULEBOOK 5.22, 32.32: what tells one element from another. The property-name form is the one
|
|
183
|
-
* that crosses a wire (18.4) — a function has no
|
|
183
|
+
* that crosses a wire (18.4) — a function has no serialization.
|
|
184
184
|
*/
|
|
185
185
|
by?:
|
|
186
186
|
| (keyof ElementOf<Stored> & string)
|
|
@@ -374,7 +374,7 @@ type Persisted = {
|
|
|
374
374
|
* A STREAMING PRODUCER'S OWN STATE (9.1, 9.2, 9.6), and `null` on every value whose body is
|
|
375
375
|
* not an async generator — the shape `cap` and `adoption` already use.
|
|
376
376
|
*
|
|
377
|
-
* THE ACCUMULATION IS PUSHED INTO AND
|
|
377
|
+
* THE ACCUMULATION IS PUSHED INTO AND MATERIALIZED ON THE READ. A chunk is one push and one
|
|
378
378
|
* cascade; the copy a reader is handed is taken by the read that follows, so a hundred
|
|
379
379
|
* chunks between two reads are one copy rather than a hundred (9.2). The copy is FRESH per
|
|
380
380
|
* chunk count and has to be: a reader holding an earlier snapshot still holds what it read,
|
|
@@ -401,13 +401,13 @@ type Stream = {
|
|
|
401
401
|
*
|
|
402
402
|
* A version rather than a chunk count because a patch changes the accumulation without
|
|
403
403
|
* changing its length (9.13), and a write stands at the version it was made at until the
|
|
404
|
-
* accumulation moves past it (9.11). -1 where nothing has been
|
|
404
|
+
* accumulation moves past it (9.11). -1 where nothing has been materialized.
|
|
405
405
|
*/
|
|
406
|
-
|
|
406
|
+
materializedAt: number
|
|
407
407
|
/**
|
|
408
|
-
* Whether `value` is a write rather than a
|
|
408
|
+
* Whether `value` is a write rather than a materialization.
|
|
409
409
|
*
|
|
410
|
-
* WHETHER `value` IS A WRITE rather than a
|
|
410
|
+
* WHETHER `value` IS A WRITE rather than a materialization, while `materializedAt` stands. A value
|
|
411
411
|
* forwarding this stream serves the write the way it would have served a production (11.59).
|
|
412
412
|
*/
|
|
413
413
|
written: boolean
|
|
@@ -416,7 +416,7 @@ type Stream = {
|
|
|
416
416
|
*
|
|
417
417
|
* HOW MANY CHUNKS A READER HAS BEEN TOLD OF. Every chunk as it lands, without a cap; with one,
|
|
418
418
|
* only what the window has released (5.18) — the rest are in `chunks` for a cursor (5.10) and
|
|
419
|
-
* out of what a read
|
|
419
|
+
* out of what a read materializes until the release.
|
|
420
420
|
*/
|
|
421
421
|
visible: number
|
|
422
422
|
/** The run in flight, told to return when a newer run supersedes it. */
|
|
@@ -470,7 +470,7 @@ function newStream(source: Cell | null): Stream {
|
|
|
470
470
|
chunks: EMPTY_RING,
|
|
471
471
|
version: 0,
|
|
472
472
|
visible: 0,
|
|
473
|
-
|
|
473
|
+
materializedAt: -1,
|
|
474
474
|
written: false,
|
|
475
475
|
iterator: null,
|
|
476
476
|
run: 0,
|
|
@@ -498,7 +498,7 @@ function becomeStream(cell: Cell, source: Cell | null): Stream {
|
|
|
498
498
|
* every other producer's. A failed or superseded run never reaches here, so it starts no clock.
|
|
499
499
|
*/
|
|
500
500
|
function closeRun(cell: Cell, stream: Stream): void {
|
|
501
|
-
//
|
|
501
|
+
// Materialized here so 9.8's gates run on the accumulation the run closed with.
|
|
502
502
|
if (heldValue(cell) === undefined) return
|
|
503
503
|
stream.closed = true
|
|
504
504
|
const expiry = cell.expiry
|
|
@@ -739,7 +739,7 @@ class Cell {
|
|
|
739
739
|
* Whether a read may ever take the field straight, fixed at construction.
|
|
740
740
|
*
|
|
741
741
|
* WHETHER A READ MAY EVER TAKE THE FIELD STRAIGHT, fixed at construction: no expiry to
|
|
742
|
-
* check and no accumulation to
|
|
742
|
+
* check and no accumulation to materialize. `setDirect` reads this one field where it
|
|
743
743
|
* would otherwise read one per mechanism that needs the read to go a frame in.
|
|
744
744
|
*/
|
|
745
745
|
declare plain: boolean
|
|
@@ -965,7 +965,7 @@ function capFor(
|
|
|
965
965
|
*
|
|
966
966
|
* So the framework takes the cheap, predictable default — `===` is what JavaScript
|
|
967
967
|
* means by "the same value" — and the app names `identity: structural` on the values
|
|
968
|
-
* where re-
|
|
968
|
+
* where re-materialization is the common case. 5.5's "not equal wherever it cannot
|
|
969
969
|
* decide" is unchanged; it is just no longer the default reading.
|
|
970
970
|
*
|
|
971
971
|
* `identity` is discriminated by ARITY (5.3), once, at construction — not per compare.
|
|
@@ -1102,7 +1102,7 @@ export function flushSync(): void {
|
|
|
1102
1102
|
* is never direct, so every read of it goes a frame in to `readIndirect` — which is
|
|
1103
1103
|
* exactly where 6.5 and 6.6 are answered, and is why a value declaring no `ttl`
|
|
1104
1104
|
* never reaches a clock. See `Expiry.ts`. A stream is never direct either, for the same
|
|
1105
|
-
* shape of reason: its read is where the accumulation is
|
|
1105
|
+
* shape of reason: its read is where the accumulation is materialized (9.1).
|
|
1106
1106
|
*/
|
|
1107
1107
|
function setDirect(cell: Cell, when: boolean): void {
|
|
1108
1108
|
cell.direct = when && cell.plain
|
|
@@ -1438,7 +1438,7 @@ function drainPending(): void {
|
|
|
1438
1438
|
// derived CELL on 2.13's aborted-body path, and for one of those `state` is not a
|
|
1439
1439
|
// reminder to run — it IS the pull the next read takes (`state !== CLEAN` is what sends
|
|
1440
1440
|
// a read a frame in). Forced back to `CLEAN` it is not deaf, it is settled: the read
|
|
1441
|
-
// path skips `resolve`, the aborted body is never
|
|
1441
|
+
// path skips `resolve`, the aborted body is never rerun and the `ready()` guard that
|
|
1442
1442
|
// queued it answers false for the life of the page — undoing, on any throw anywhere in
|
|
1443
1443
|
// the drain, exactly what that push was added to repair.
|
|
1444
1444
|
for (let index = pendingAt; index < pendingCount; index += 1) {
|
|
@@ -1747,7 +1747,7 @@ function openWindow(cell: Cell, cap: Cap): void {
|
|
|
1747
1747
|
*
|
|
1748
1748
|
* THE WAY IN, AND THE ORDER IS 4.3 THEN 4.4: the schema gates, and what it RETURNS is
|
|
1749
1749
|
* what `transform` receives — not the original value. A transform reading the raw input
|
|
1750
|
-
* where a schema had
|
|
1750
|
+
* where a schema had normalized it is a second answer to what this value holds.
|
|
1751
1751
|
* What it hands back is the ordinal, or the refusal a gate answered with (4.18) — `unknown`
|
|
1752
1752
|
* because the one caller that cares tells the two apart by type.
|
|
1753
1753
|
*/
|
|
@@ -1769,7 +1769,7 @@ function accept(cell: Cell, value: unknown): unknown {
|
|
|
1769
1769
|
if (transform === null) return produce(cell, checked)
|
|
1770
1770
|
// 4.7 — A `transform` RUNS UNTRACKED: a write can be made from inside a tracked body
|
|
1771
1771
|
// (`watch(() => target.set(…))`, a recompute that writes), and run tracked, the transform's
|
|
1772
|
-
// reads were attributed to that reader — which then
|
|
1772
|
+
// reads were attributed to that reader — which then reran on a value it never read, with
|
|
1773
1773
|
// those reads spliced into its cursor-ordered `sources`.
|
|
1774
1774
|
const shaped = untracked(transform, checked, undefined)
|
|
1775
1775
|
// 4.6 AND 4.9: a `transform` that returned a refusal stops the write here — nothing is
|
|
@@ -1844,7 +1844,7 @@ function writeStream(cell: Cell, value: unknown): unknown {
|
|
|
1844
1844
|
function standWrite(cell: Cell, stream: Stream, value: unknown): number {
|
|
1845
1845
|
// 7.9, as `produce` settles it.
|
|
1846
1846
|
if (cell.owes === OWES_RELOAD) cell.owes = OWES_NOTHING
|
|
1847
|
-
// 5.2 against what is served now, which on a stream may not have been
|
|
1847
|
+
// 5.2 against what is served now, which on a stream may not have been materialized yet.
|
|
1848
1848
|
if (cell.servable) {
|
|
1849
1849
|
const previous = heldValue(cell)
|
|
1850
1850
|
if (
|
|
@@ -1855,7 +1855,7 @@ function standWrite(cell: Cell, stream: Stream, value: unknown): number {
|
|
|
1855
1855
|
return cell.ordinal
|
|
1856
1856
|
}
|
|
1857
1857
|
cell.value = value
|
|
1858
|
-
stream.
|
|
1858
|
+
stream.materializedAt = stream.version
|
|
1859
1859
|
stream.written = true
|
|
1860
1860
|
const moved = !cell.servable || cell.failure !== undefined
|
|
1861
1861
|
landChunks(cell)
|
|
@@ -2030,13 +2030,13 @@ function follow(cell: Cell, stream: Stream, inner: Cell): Error | undefined {
|
|
|
2030
2030
|
if (standing !== null) unfollow(cell)
|
|
2031
2031
|
// What `standWrite`'s identity gate compares against is what is served now.
|
|
2032
2032
|
heldValue(cell)
|
|
2033
|
-
stream.
|
|
2033
|
+
stream.materializedAt = stream.version
|
|
2034
2034
|
stream.written = true
|
|
2035
2035
|
const stop = watchOver([inner], cell, (): undefined => {
|
|
2036
2036
|
// The accumulation moved past the write. The drop is `wakeStreams`'s or the read's, not
|
|
2037
2037
|
// this one's: a mirror runs inside the mark walk over `inner.readers`, and detaching here
|
|
2038
2038
|
// would splice the list that walk is iterating.
|
|
2039
|
-
if (!stream.written || stream.
|
|
2039
|
+
if (!stream.written || stream.materializedAt !== stream.version)
|
|
2040
2040
|
return undefined
|
|
2041
2041
|
if (inner.owes !== OWES_NOTHING) settleOwed(inner)
|
|
2042
2042
|
// 11.16: the followed value has nothing to serve, so neither has this.
|
|
@@ -2075,7 +2075,7 @@ function unserve(cell: Cell): void {
|
|
|
2075
2075
|
}
|
|
2076
2076
|
|
|
2077
2077
|
function mirror(cell: Cell, inner: Cell): void {
|
|
2078
|
-
// 7.13 through the adoption: the re-settle a
|
|
2078
|
+
// 7.13 through the adoption: the re-settle a rerun of the adopter makes reaches a reload the
|
|
2079
2079
|
// adopted value owes, rather than finding it unservable and giving up.
|
|
2080
2080
|
if (inner.owes !== OWES_NOTHING) settleOwed(inner)
|
|
2081
2081
|
const adopted = inner.stream
|
|
@@ -2136,7 +2136,7 @@ function dropAdoption(cell: Cell): void {
|
|
|
2136
2136
|
* Makes an adopter forward an adopted stream's buffer, ring and ordinals.
|
|
2137
2137
|
*
|
|
2138
2138
|
* 11.15 FOR A STREAM: the adopter takes the adopted value's buffer, ring and ordinals by
|
|
2139
|
-
* reference and
|
|
2139
|
+
* reference and materializes from them on its own reads, through its own gates (11.14, 9.8).
|
|
2140
2140
|
* Mirroring the value instead copied the accumulation once per wake whether or not anything
|
|
2141
2141
|
* read it, and handed a cursor over the adopter whole accumulations where it wanted chunks.
|
|
2142
2142
|
*/
|
|
@@ -2151,8 +2151,8 @@ function forwardStream(cell: Cell, inner: Cell, adopted: Stream): void {
|
|
|
2151
2151
|
stream.chunks = adopted.chunks
|
|
2152
2152
|
stream.version = adopted.version
|
|
2153
2153
|
stream.visible = adopted.visible
|
|
2154
|
-
if (adopted.written && adopted.
|
|
2155
|
-
stream.
|
|
2154
|
+
if (adopted.written && adopted.materializedAt === adopted.version) {
|
|
2155
|
+
stream.materializedAt = stream.version
|
|
2156
2156
|
stream.written = true
|
|
2157
2157
|
gateInto(cell, inner.value)
|
|
2158
2158
|
}
|
|
@@ -2188,9 +2188,9 @@ function stopForwarding(cell: Cell): void {
|
|
|
2188
2188
|
* THE ABORT. A body that reads a value which has not landed throws `NotLanded`, and
|
|
2189
2189
|
* this catches it and returns — the body is ABANDONED, not retried in place. The read
|
|
2190
2190
|
* that threw has already registered this cell as a reader of it (tracking happens
|
|
2191
|
-
* before the throw), so the source landing is what
|
|
2191
|
+
* before the throw), so the source landing is what reruns the body.
|
|
2192
2192
|
*
|
|
2193
|
-
* "Throw a sentinel and
|
|
2193
|
+
* "Throw a sentinel and rerun" was refused once on three grounds. Two are answered
|
|
2194
2194
|
* by the reach starting the work (1.7) — the loads are in flight before the body
|
|
2195
2195
|
* runs, so the abort costs a re-entry rather than a round trip. The third, that
|
|
2196
2196
|
* nothing on a server wakes an abandoned body, is answered by the sink rather than
|
|
@@ -2532,7 +2532,7 @@ function acceptChunk(cell: Cell, stream: Stream, chunk: unknown): void {
|
|
|
2532
2532
|
/**
|
|
2533
2533
|
* Tells readers of every chunk landed so far, waking them at most once per task.
|
|
2534
2534
|
*
|
|
2535
|
-
* What a reader is told of: every chunk landed so far becomes what a read
|
|
2535
|
+
* What a reader is told of: every chunk landed so far becomes what a read materializes, and the
|
|
2536
2536
|
* wake goes out at most once per task (9.9).
|
|
2537
2537
|
*/
|
|
2538
2538
|
function releaseChunks(cell: Cell, stream: Stream): void {
|
|
@@ -2546,7 +2546,7 @@ function releaseChunks(cell: Cell, stream: Stream): void {
|
|
|
2546
2546
|
* The streams holding a wake, all drained by one task.
|
|
2547
2547
|
*
|
|
2548
2548
|
* THE STREAMS HOLDING A WAKE, and one task drains all of them — never a task per stream.
|
|
2549
|
-
* The pull is untouched: a read taken before the task runs
|
|
2549
|
+
* The pull is untouched: a read taken before the task runs materializes what has landed
|
|
2550
2550
|
* (12.12), so what waits is only the telling.
|
|
2551
2551
|
*/
|
|
2552
2552
|
const WAKING: Cell[] = []
|
|
@@ -2621,18 +2621,18 @@ function landChunks(cell: Cell): void {
|
|
|
2621
2621
|
}
|
|
2622
2622
|
|
|
2623
2623
|
/**
|
|
2624
|
-
*
|
|
2624
|
+
* Materializes a stream's visible chunks into `value`, through both gates.
|
|
2625
2625
|
*
|
|
2626
2626
|
* 9.1 AND 9.8: the read that asks is where the accumulation becomes a `Stored`, through both
|
|
2627
2627
|
* gates, once per chunk count however many reads ask.
|
|
2628
2628
|
*/
|
|
2629
|
-
function
|
|
2630
|
-
if (stream.
|
|
2629
|
+
function materialize(cell: Cell, stream: Stream): void {
|
|
2630
|
+
if (stream.materializedAt === stream.version || !cell.servable) return
|
|
2631
2631
|
// 9.11: the accumulation moved past a write following a `Reactive`, which lets go of it here
|
|
2632
2632
|
// where no chunk's wake did — a patch, or a run that closed empty. Only an own stream follows;
|
|
2633
2633
|
// a forwarding one's `adoption` is what it forwards.
|
|
2634
2634
|
if (cell.adoption !== null && stream.source === null) unfollow(cell)
|
|
2635
|
-
stream.
|
|
2635
|
+
stream.materializedAt = stream.version
|
|
2636
2636
|
stream.written = false
|
|
2637
2637
|
gateInto(cell, stream.chunks.slice(0, stream.visible))
|
|
2638
2638
|
}
|
|
@@ -2677,7 +2677,7 @@ function gateThrough(cell: Cell, raw: unknown): void {
|
|
|
2677
2677
|
*/
|
|
2678
2678
|
function heldValue(cell: Cell): unknown {
|
|
2679
2679
|
const stream = cell.stream
|
|
2680
|
-
if (stream !== null)
|
|
2680
|
+
if (stream !== null) materialize(cell, stream)
|
|
2681
2681
|
return cell.value
|
|
2682
2682
|
}
|
|
2683
2683
|
|
|
@@ -2840,7 +2840,7 @@ function readIndirect(cell: Cell): unknown {
|
|
|
2840
2840
|
// 6.5: THE READ THAT FOLLOWS. Every value carrying an expiry arrives here and no
|
|
2841
2841
|
// other value does, which is the whole of what `setDirect` buys.
|
|
2842
2842
|
if (cell.expiry !== null) expire(cell)
|
|
2843
|
-
if (cell.stream !== null)
|
|
2843
|
+
if (cell.stream !== null) materialize(cell, cell.stream)
|
|
2844
2844
|
if (!cell.servable) {
|
|
2845
2845
|
// 2.3: a failed producer with nothing landed is the one case holding no value
|
|
2846
2846
|
// and a refusal that explains the absence.
|
|
@@ -2866,7 +2866,7 @@ const EMPTY_RING = Object.freeze([]) as unknown as unknown[]
|
|
|
2866
2866
|
* so a shared subgraph is re-walked once per path that reaches it, and a 26-cell DAG
|
|
2867
2867
|
* with 130 edges cost 19,660 edge visits and 4,010 hash operations per probe read.
|
|
2868
2868
|
*
|
|
2869
|
-
* The stamp
|
|
2869
|
+
* The stamp MEMOIZES, and it is sound for one reason worth stating: within a pass,
|
|
2870
2870
|
* "already visited while the walk is still running" IMPLIES "not busy", because a busy
|
|
2871
2871
|
* node short-circuits the whole walk at its first visit. So a second arrival at a
|
|
2872
2872
|
* stamped node answers false without descending.
|
|
@@ -2883,7 +2883,7 @@ let PROBE_PASS = 0
|
|
|
2883
2883
|
* Propagation, derived on the read that asks (11.22) rather than held as a
|
|
2884
2884
|
* subscription per source. 11.20: a value is busy where a source it read is busy —
|
|
2885
2885
|
* and the premise is what `busy` MEANS, not which trigger produced it, so a refresh
|
|
2886
|
-
* over a landed source reports through here even though the body never
|
|
2886
|
+
* over a landed source reports through here even though the body never reran.
|
|
2887
2887
|
*/
|
|
2888
2888
|
function walkBusy(cell: Cell, pass: number): boolean {
|
|
2889
2889
|
if (cell.loading) return true
|
|
@@ -2967,7 +2967,7 @@ function busyNow(cell: Cell): boolean {
|
|
|
2967
2967
|
* serving the figure it had computed before — right by its own lights, and a lie about the
|
|
2968
2968
|
* data. Nothing in the output says so, which is why this is a walk and not a field.
|
|
2969
2969
|
*
|
|
2970
|
-
* The stamp
|
|
2970
|
+
* The stamp MEMOIZES the same way `walkBusy`'s does and is sound for the mirrored reason:
|
|
2971
2971
|
* this walk short-circuits on NOT ready, so a second arrival at a node the walk is still
|
|
2972
2972
|
* inside is a node that has not refused, and answering `true` there is answering what the
|
|
2973
2973
|
* first visit will.
|
|
@@ -3053,7 +3053,7 @@ function settled(cell: Cell, untilStreaming = false): Promise<unknown> {
|
|
|
3053
3053
|
const waiter: Subscriber = newSubscriber([cell], null)
|
|
3054
3054
|
waiter.notify = (): void => {
|
|
3055
3055
|
// THE WAITER PULLS. A value built from a source that has just landed is marked and
|
|
3056
|
-
// not yet
|
|
3056
|
+
// not yet rerun — nothing else reads it — so asking whether it is busy before
|
|
3057
3057
|
// pulling it answers from the run that aborted, and the await never settles.
|
|
3058
3058
|
if (cell.state !== CLEAN) resolve(cell)
|
|
3059
3059
|
if (untilStreaming && cell.stream !== null) {
|
|
@@ -3093,7 +3093,7 @@ function settled(cell: Cell, untilStreaming = false): Promise<unknown> {
|
|
|
3093
3093
|
* than discovered — and one source given is what `s.watch` means. What was here before was
|
|
3094
3094
|
* a second copy of the subscriber, the disposer, the re-entrancy guard and 12.8, which is
|
|
3095
3095
|
* exactly the divergence 12.9 forbids: the copy had no `catch` at all, so a throwing effect
|
|
3096
|
-
*
|
|
3096
|
+
* reran into the same throw on every write where `watch` stops.
|
|
3097
3097
|
*
|
|
3098
3098
|
* The value is handed over rather than read, so the effect never reaches the tracked path
|
|
3099
3099
|
* and never throws `NotLanded` from this source.
|
|
@@ -3721,7 +3721,7 @@ const TAGGED = new Map<string, Set<WeakRef<Cell>>>()
|
|
|
3721
3721
|
* O(1) delete on eviction; here an evicted value drops its `ref` instead, and a handle whose value
|
|
3722
3722
|
* no longer points back at it is dead, the same as one whose value was collected. Because a bare
|
|
3723
3723
|
* sweep may never run, the filing that finds the array past `compactProducersAt` compacts it, and
|
|
3724
|
-
* the bar doubles past what survives, so the walk is
|
|
3724
|
+
* the bar doubles past what survives, so the walk is amortized over the filings between two.
|
|
3725
3725
|
*/
|
|
3726
3726
|
let filedProducers: WeakRef<Cell>[] = []
|
|
3727
3727
|
let compactProducersAt = 1024
|
|
@@ -3736,7 +3736,7 @@ let compactProducersAt = 1024
|
|
|
3736
3736
|
const SIGNALS = new Map<string, Tracked>()
|
|
3737
3737
|
const SIGNALS_BY_TAG = new Map<string, Tracked[]>()
|
|
3738
3738
|
/**
|
|
3739
|
-
* The bare selection's one signal. Not a key in `SIGNALS`: `{ tags: [] }`
|
|
3739
|
+
* The bare selection's one signal. Not a key in `SIGNALS`: `{ tags: [] }` canonicalizes to the
|
|
3740
3740
|
* empty set and matches nothing, where the bare selection matches every filed load in reach.
|
|
3741
3741
|
*/
|
|
3742
3742
|
const EVERY_PRODUCER = newTracked()
|
|
@@ -4171,7 +4171,7 @@ export function isStreaming(value: unknown): boolean {
|
|
|
4171
4171
|
*
|
|
4172
4172
|
* 16.32 — THE CHUNKS A STREAMING VALUE HAS TOLD ITS READERS OF PAST `from`, for a writer sending each
|
|
4173
4173
|
* once. Ungated they are read out of the accumulation in place, to `visible` rather than its length
|
|
4174
|
-
* (5.18): a read
|
|
4174
|
+
* (5.18): a read materializes a copy of the whole of it (9.1), which for a writer woken per chunk is
|
|
4175
4175
|
* n²/2 elements copied to send n. A schema or a transform has to see the whole accumulation (9.8),
|
|
4176
4176
|
* so a gated value answers from what a read would.
|
|
4177
4177
|
*/
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// through `contractOf` so a transport reads it off whichever face it was handed.
|
|
5
5
|
//
|
|
6
6
|
// DEFAULTS BEFORE THE KEY, IN ONE PLACE. 11.27: a call that left a member to the parameter and one
|
|
7
|
-
// that wrote it are one entry, so every reach, call and lookup takes the key through `
|
|
7
|
+
// that wrote it are one entry, so every reach, call and lookup takes the key through `normalized`
|
|
8
8
|
// — a caller that keyed the args as passed would file the same call twice, right in the value and
|
|
9
9
|
// wrong in the work.
|
|
10
10
|
//
|
|
@@ -54,7 +54,7 @@ export function gateOf(
|
|
|
54
54
|
* `args` itself where the face has no defaults or nothing was missing, so a caller that passed
|
|
55
55
|
* every member allocates nothing and a second application is free.
|
|
56
56
|
*/
|
|
57
|
-
export function
|
|
57
|
+
export function normalized<Args>(
|
|
58
58
|
face: Pick<Gated<unknown>, typeof PARAMETER_DEFAULTS>,
|
|
59
59
|
args: Args,
|
|
60
60
|
): Args {
|
|
@@ -62,12 +62,12 @@ export function normalised<Args>(
|
|
|
62
62
|
return defaults === null ? args : withDefaults(args, defaults)
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
/** The entry key a call with `args` is filed under: `
|
|
65
|
+
/** The entry key a call with `args` is filed under: `normalized`, then 11.24's canonical form. */
|
|
66
66
|
export function keyOf(
|
|
67
67
|
face: Pick<Gated<unknown>, typeof PARAMETER_DEFAULTS>,
|
|
68
68
|
args: unknown,
|
|
69
69
|
): string {
|
|
70
|
-
return canonicalKey(
|
|
70
|
+
return canonicalKey(normalized(face, args))
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
// ABSENT ARGS ARE `{}`: a wire writes the two alike (an empty query), so read apart they are one
|
|
@@ -6,12 +6,12 @@ import { putOwn } from '../putOwn.ts'
|
|
|
6
6
|
* RULEBOOK 11.24: members sorted by name, `undefined` members dropped, `Date` written ISO.
|
|
7
7
|
*
|
|
8
8
|
* 11.26: the key is taken BEFORE any gate runs, because a browser holds no copy of the gate — a
|
|
9
|
-
* key taken after
|
|
10
|
-
* handler with a
|
|
9
|
+
* key taken after normalizing is computed one way on a server and another in a browser, and every
|
|
10
|
+
* handler with a normalizing schema would miss its seed buffer and re-issue the call on hydration.
|
|
11
11
|
*
|
|
12
12
|
* 11.64: WHAT THE WIRE FORM CANNOT WRITE IS REFUSED AT THE REACH. `JSON.stringify` does not fail
|
|
13
13
|
* on any of it — a `Set`, a `Map` and a class instance each have no own enumerable keys and
|
|
14
|
-
*
|
|
14
|
+
* canonicalize to `{}`, and a non-finite number is written `null` — so two different reaches
|
|
15
15
|
* produced one key and 1.8 joined the second to the first's standing work. The value was right
|
|
16
16
|
* for the first caller and wrong for every other one, with nothing to see.
|
|
17
17
|
*/
|
|
@@ -55,7 +55,7 @@ function canonical(value: unknown, path: Steps): unknown {
|
|
|
55
55
|
// AN ORDINARY OBJECT, AND `putOwn` FOR THE ONE KEY THAT NEEDS IT: `__proto__` is a SETTER on
|
|
56
56
|
// `Object.prototype`, so `out[key] = …` for that key creates no own property and
|
|
57
57
|
// `JSON.stringify` never sees it — `{id: 1, __proto__: {role: 'admin'}}` and `{id: 1}` then
|
|
58
|
-
//
|
|
58
|
+
// canonicalize to one string, and 1.8 joins the second reach to the first's standing work.
|
|
59
59
|
// `Object.create(null)` closed that too, and V8 builds it in DICTIONARY mode: the shipped key
|
|
60
60
|
// took 49-56% of the time without it there, and a standing keyed reach 53%, where JSC reads
|
|
61
61
|
// level to 110% (`harness/scripts/sharedCases.ts`, two runs per engine).
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isFailed } from '../Failed.ts'
|
|
1
2
|
import { chunksFrom } from './Reactive.ts'
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -48,4 +49,9 @@ export async function* chunksOfStream(value: unknown): AsyncGenerator<unknown> {
|
|
|
48
49
|
void cursor.return?.(undefined)
|
|
49
50
|
}
|
|
50
51
|
if (failure !== undefined) throw failure
|
|
52
|
+
// 16.59, 16.60 — A RUN ENDED BY ITS TIMEOUT SETTLES with the chunks it had and holds its refusal
|
|
53
|
+
// rather than rejecting, so the refusal is read off the value and thrown after them: a stream
|
|
54
|
+
// answered over http then ends on it (16.68) rather than as though it had finished.
|
|
55
|
+
const held = (value as { error?: () => unknown }).error?.()
|
|
56
|
+
if (isFailed(held)) throw held
|
|
51
57
|
}
|
|
@@ -39,7 +39,7 @@ export { isKeyed, type Keyed } from './Keyed.ts'
|
|
|
39
39
|
export { NotLanded } from './NotLanded.ts'
|
|
40
40
|
// The server reaches the core through here rather than `./Reactive.ts`: `Reactive.ts` →
|
|
41
41
|
// `log.ts` → this barrel → `scoped.ts` → `Reactive.ts` is a cycle, and a server module loaded
|
|
42
|
-
// first that imports the core directly meets `MEMBER_KEYS` before it is
|
|
42
|
+
// first that imports the core directly meets `MEMBER_KEYS` before it is initialized.
|
|
43
43
|
export {
|
|
44
44
|
abandonLoad,
|
|
45
45
|
deadlineOf,
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
//
|
|
6
6
|
// ONLY ARRAYS AND PLAIN OBJECTS ARE WRAPPED. A `Map`, a `Set` or a class instance keeps internal
|
|
7
7
|
// slots a proxy cannot stand in for — `Map.prototype.set` refuses a proxy as its receiver — so one
|
|
8
|
-
// reached is marked written the moment it is read, path and all
|
|
9
|
-
//
|
|
8
|
+
// reached is marked written the moment it is read, path and all, and each object a `Map` or a `Set`
|
|
9
|
+
// hands out is marked as it leaves (`heldView`). That over-reports, and an over-report costs a row
|
|
10
|
+
// remade where an under-report shows a stale one.
|
|
10
11
|
//
|
|
11
12
|
// 18.11 — AND WHERE A SINK IS HANDED IN, each write is kept as data too, as JSON Patch (RFC 6902) at
|
|
12
13
|
// the JSON Pointer it was reached by: a member set is `add`, an index `replace` (or `add` one past
|
|
@@ -49,7 +50,15 @@ export function recordWrites<Value>(
|
|
|
49
50
|
sink?: { ops: PatchOp[]; lost: boolean },
|
|
50
51
|
): void {
|
|
51
52
|
if (!isWrappable(root)) {
|
|
52
|
-
|
|
53
|
+
// A root no proxy can stand in for is written as a whole: what the callback reads out of it
|
|
54
|
+
// reaches the rows that show it with no proxy to say so.
|
|
55
|
+
if (root === null || typeof root !== 'object') {
|
|
56
|
+
mutate(root)
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
PATCHES.epoch += 1
|
|
60
|
+
PATCHES.written.set(root, PATCHES.epoch)
|
|
61
|
+
mutate(heldView(root, PATCHES.epoch) as Value)
|
|
53
62
|
return
|
|
54
63
|
}
|
|
55
64
|
PATCHES.epoch += 1
|
|
@@ -217,9 +226,10 @@ export function recordWrites<Value>(
|
|
|
217
226
|
}
|
|
218
227
|
if (entry.proxy !== null) return entry.proxy
|
|
219
228
|
mark(value)
|
|
229
|
+
entry.view ??= heldView(value, epoch)
|
|
220
230
|
// Reached, and outside what a proxy can watch: whatever it does next is not kept.
|
|
221
231
|
if (sink !== undefined) sink.lost = true
|
|
222
|
-
return
|
|
232
|
+
return entry.view
|
|
223
233
|
},
|
|
224
234
|
set(held, property, value) {
|
|
225
235
|
mark(held)
|
|
@@ -363,7 +373,7 @@ function pointerOf(path: (string | number)[]): string {
|
|
|
363
373
|
/**
|
|
364
374
|
* The JSON Patch an array method makes, read off the array BEFORE it runs.
|
|
365
375
|
*
|
|
366
|
-
* A `splice`'s start and count are
|
|
376
|
+
* A `splice`'s start and count are normalized the way the method does, so the `remove`s and `add`s
|
|
367
377
|
* name the indices it touches.
|
|
368
378
|
*/
|
|
369
379
|
function opsOfCall(
|
|
@@ -421,10 +431,13 @@ class Reached {
|
|
|
421
431
|
* Written in this patch, directly or below — `PATCHES.written` holds this patch's epoch for it.
|
|
422
432
|
*/
|
|
423
433
|
marked: boolean
|
|
434
|
+
/** What one no proxy can stand in for is handed out as: `heldView` of it. */
|
|
435
|
+
view: object | undefined
|
|
424
436
|
constructor(proxy: object | null, parent: object | Parents | null) {
|
|
425
437
|
this.proxy = proxy
|
|
426
438
|
this.parent = parent
|
|
427
439
|
this.marked = false
|
|
440
|
+
this.view = undefined
|
|
428
441
|
}
|
|
429
442
|
}
|
|
430
443
|
|
|
@@ -463,6 +476,91 @@ class Parents {
|
|
|
463
476
|
}
|
|
464
477
|
}
|
|
465
478
|
|
|
479
|
+
/**
|
|
480
|
+
* `container` as the callback is handed it: a `Map` or a `Set` that marks written each object key,
|
|
481
|
+
* value or member it hands out, and anything else as it is.
|
|
482
|
+
*
|
|
483
|
+
* A row over `[...map.values()]` reads one of them, and a write the callback makes to it through
|
|
484
|
+
* `map.get(id)` reaches it with nothing on the way to record it — so each one taken out is taken as
|
|
485
|
+
* written. AS IT LEAVES rather than all of them up front, which would make a patch of one entry cost
|
|
486
|
+
* the whole container. One level down: a row reads its item.
|
|
487
|
+
*/
|
|
488
|
+
function heldView(container: object, epoch: number): object {
|
|
489
|
+
if (!(container instanceof Map) && !(container instanceof Set))
|
|
490
|
+
return container
|
|
491
|
+
return new Proxy(container, new HeldHandler(epoch))
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// `Map`'s and `Set`'s own methods run on the container itself, which is the receiver their internal
|
|
495
|
+
// slots answer to; the ones that hand an object out mark it first.
|
|
496
|
+
class HeldHandler {
|
|
497
|
+
epoch: number
|
|
498
|
+
constructor(epoch: number) {
|
|
499
|
+
this.epoch = epoch
|
|
500
|
+
}
|
|
501
|
+
get(
|
|
502
|
+
held: Map<unknown, unknown> | Set<unknown>,
|
|
503
|
+
property: string | symbol,
|
|
504
|
+
): unknown {
|
|
505
|
+
if (property === TARGET) return held
|
|
506
|
+
const value = Reflect.get(held, property, held)
|
|
507
|
+
if (typeof value !== 'function') return value
|
|
508
|
+
const epoch = this.epoch
|
|
509
|
+
if (property === 'get')
|
|
510
|
+
return (key: unknown) =>
|
|
511
|
+
markedOut((held as Map<unknown, unknown>).get(key), epoch)
|
|
512
|
+
if (property === 'forEach')
|
|
513
|
+
return (
|
|
514
|
+
each: (value: unknown, key: unknown, set: unknown) => void,
|
|
515
|
+
self?: unknown,
|
|
516
|
+
) => {
|
|
517
|
+
for (const [key, member] of held.entries())
|
|
518
|
+
each.call(
|
|
519
|
+
self,
|
|
520
|
+
markedOut(member, epoch),
|
|
521
|
+
markedOut(key, epoch),
|
|
522
|
+
held,
|
|
523
|
+
)
|
|
524
|
+
}
|
|
525
|
+
if (
|
|
526
|
+
property === 'values' ||
|
|
527
|
+
property === 'keys' ||
|
|
528
|
+
property === 'entries' ||
|
|
529
|
+
property === Symbol.iterator
|
|
530
|
+
)
|
|
531
|
+
// A Map's own iterator is its entries; a Set's is its values.
|
|
532
|
+
return () =>
|
|
533
|
+
markedEach(
|
|
534
|
+
(value as () => Iterator<unknown>).call(held),
|
|
535
|
+
property === 'entries' ||
|
|
536
|
+
(property === Symbol.iterator && held instanceof Map),
|
|
537
|
+
epoch,
|
|
538
|
+
)
|
|
539
|
+
return (value as (...args: unknown[]) => unknown).bind(held)
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function* markedEach(
|
|
544
|
+
each: Iterator<unknown>,
|
|
545
|
+
pairs: boolean,
|
|
546
|
+
epoch: number,
|
|
547
|
+
): Generator<unknown> {
|
|
548
|
+
for (let next = each.next(); next.done !== true; next = each.next()) {
|
|
549
|
+
if (pairs) {
|
|
550
|
+
const [key, value] = next.value as [unknown, unknown]
|
|
551
|
+
markedOut(key, epoch)
|
|
552
|
+
markedOut(value, epoch)
|
|
553
|
+
} else markedOut(next.value, epoch)
|
|
554
|
+
yield next.value
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function markedOut<Value>(value: Value, epoch: number): Value {
|
|
559
|
+
if (typeof value === 'object' && value !== null)
|
|
560
|
+
PATCHES.written.set(value, epoch)
|
|
561
|
+
return value
|
|
562
|
+
}
|
|
563
|
+
|
|
466
564
|
/** Whether a proxy can stand in for `value`: an array or a plain object. */
|
|
467
565
|
function isWrappable(value: unknown): value is object {
|
|
468
566
|
if (value === null || typeof value !== 'object') return false
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The by-value comparator an app passes as `identity`: deep equality over arrays and plain objects.
|
|
3
3
|
*
|
|
4
4
|
* Not the default: the default is the reference, and this is what a value whose producer
|
|
5
|
-
* RE-
|
|
5
|
+
* RE-MATERIALIZES declares — `reactive(() => getInvoice({ id }), { identity: structural })`. A
|
|
6
6
|
* fresh-but-equal payload waking every reader is why it exists at all; what it stops being is the
|
|
7
7
|
* price every other value pays.
|
|
8
8
|
*
|
|
@@ -255,7 +255,7 @@ export function dropProbeReader(source: Tracked, reader: Subscriber): void {
|
|
|
255
255
|
* sweep below takes it out.
|
|
256
256
|
*
|
|
257
257
|
* SWEPT ONCE HALF THE LIST IS DEAD, which keeps a walk to at most twice the live readers and the
|
|
258
|
-
* sweep
|
|
258
|
+
* sweep amortized O(1) a drop — and keeps the invariant every `length` test leans on: a list with
|
|
259
259
|
* entries has a LIVE one, since a list that is all dead is at least half dead. HALF, NOT MORE THAN
|
|
260
260
|
* HALF: a keyed replace ends on exactly half, n old rows dead behind n new ones, and a sweep that
|
|
261
261
|
* waited for one more drop kept the old rows — and through each one's `notify`, its scope and its
|
|
@@ -203,7 +203,7 @@ export function watchOver(
|
|
|
203
203
|
// UNTRACKED, AND THE CLEAR IS WHAT MAKES IT SO. The sources are given rather
|
|
204
204
|
// than discovered, but the effect still RUNS wherever it was reached from — a
|
|
205
205
|
// `s.watch` opened inside a `watch` had its reads attributed to the OUTER
|
|
206
|
-
// reader, which then
|
|
206
|
+
// reader, which then reran on a value it never read. A mirror is the same
|
|
207
207
|
// shape one turn up: it runs inside the mark walk, and 4.7 has the `transform`
|
|
208
208
|
// it calls there running untracked.
|
|
209
209
|
const previous = TRACKING.reader
|
|
@@ -219,7 +219,7 @@ export function watchOver(
|
|
|
219
219
|
// subscribed to, so its landing is what runs this again.
|
|
220
220
|
if (thrown instanceof NotLanded) return
|
|
221
221
|
// 12.8: one that throws once usually throws every run, and a watch
|
|
222
|
-
//
|
|
222
|
+
// rerunning into the same throw is a loop with a log line per iteration.
|
|
223
223
|
reader.alive = false
|
|
224
224
|
detach(reader)
|
|
225
225
|
effectThrew(thrown)
|
|
@@ -242,7 +242,7 @@ export function watchOver(
|
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
/**
|
|
245
|
-
* The bare form: runs `effect` as a tracked context,
|
|
245
|
+
* The bare form: runs `effect` as a tracked context, rerunning when anything it read changes.
|
|
246
246
|
*
|
|
247
247
|
* UNOWNED — the caller holds the stop. `watch` is this handed to its owners (`owned`); a template
|
|
248
248
|
* binding's `Scope` and a `{#try}`'s retry hold their own, and call this direct.
|
package/src/shared/refuse.ts
CHANGED
|
@@ -15,7 +15,7 @@ import type { JsonValue } from './JsonValue.ts'
|
|
|
15
15
|
import { namedLogger } from './log.ts'
|
|
16
16
|
import { type Found, runSchema, type Schema } from './Schema.ts'
|
|
17
17
|
|
|
18
|
-
/** The `abide:refuse` logger (28.
|
|
18
|
+
/** The `abide:refuse` logger (28.8) — 15.13 and 15.15's namespace. */
|
|
19
19
|
const REFUSE_LOG = namedLogger('abide:refuse')
|
|
20
20
|
|
|
21
21
|
/**
|