@abide/abide 0.101.0 → 0.103.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 +26 -0
- package/bin/abide.ts +226 -164
- package/docs/REGISTRY.md +730 -0
- package/docs/RULEBOOK.md +2768 -0
- package/docs/abide.md +13127 -0
- package/package.json +6 -3
- package/scaffold/AGENTS.md +38 -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 +341 -71
- package/src/compiler/appMap.ts +96 -0
- package/src/compiler/argsSchemaOf.ts +1 -1
- package/src/compiler/checkApp.ts +53 -8
- package/src/compiler/checkedShadow.ts +73 -6
- package/src/compiler/classifyRead.ts +6 -4
- package/src/compiler/classifyTemplate.ts +1 -1
- package/src/compiler/compileApp.ts +598 -200
- package/src/compiler/compiledEntry.ts +48 -40
- package/src/compiler/componentGraph.ts +57 -71
- package/src/compiler/declaredExports.ts +26 -9
- package/src/compiler/emitClient.ts +142 -72
- package/src/compiler/emitComponent.ts +41 -12
- package/src/compiler/emitSource.ts +102 -13
- package/src/compiler/handlersUnder.ts +164 -76
- package/src/compiler/hooksCalled.ts +35 -0
- package/src/compiler/index.ts +7 -0
- package/src/compiler/mountsOf.ts +150 -0
- package/src/compiler/nearest.ts +14 -0
- package/src/compiler/parseAbide.ts +157 -20
- package/src/compiler/projectShadow.ts +250 -19
- package/src/compiler/reactiveTypes.ts +20 -7
- package/src/compiler/readSources.ts +15 -2
- package/src/compiler/readsIn.ts +62 -8
- package/src/compiler/renderMarkup.ts +3 -3
- package/src/compiler/scaffold.ts +37 -15
- package/src/compiler/serveLanguage.ts +101 -34
- package/src/compiler/sharedModuleWrites.ts +2 -2
- package/src/compiler/sourceMapOf.ts +193 -0
- package/src/compiler/splitImports.ts +205 -83
- package/src/compiler/tokenize.ts +14 -0
- package/src/compiler/writeDefaultProject.ts +29 -2
- package/src/server/CLIENT_BUILD.ts +11 -13
- package/src/server/DEFAULT_PORT.ts +1 -1
- package/src/server/MODULE_QUERIES.ts +27 -0
- package/src/server/TABLES.ts +7 -0
- package/src/server/VIRTUAL_DIRECTORY.ts +4 -5
- package/src/server/abideModules.ts +457 -0
- package/src/server/answerFailure.ts +16 -6
- package/src/server/answerRequest.ts +241 -107
- package/src/server/answerRpc.ts +107 -33
- 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 +238 -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 +38 -37
- package/src/server/componentModules.d.ts +2 -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 +77 -79
- package/src/server/devWorker.ts +118 -181
- package/src/server/executableName.ts +4 -0
- package/src/server/fenceServer.ts +57 -32
- package/src/server/generatedSurfaces.ts +99 -66
- package/src/server/hooks.ts +137 -52
- package/src/server/iconAccent.ts +191 -0
- package/src/server/index.ts +12 -7
- package/src/server/installSourceMaps.ts +93 -0
- package/src/server/launcherOptions.ts +33 -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 +6 -6
- package/src/server/negotiateEncoding.ts +1 -1
- package/src/server/preload.ts +13 -0
- 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 +48 -28
- package/src/server/serveBuild.ts +43 -52
- package/src/server/serveDev.ts +79 -37
- package/src/server/shell.ts +7 -22
- package/src/server/sourceMappings.ts +139 -0
- package/src/server/surfaceOf.ts +20 -3
- package/src/server/testBuild.ts +1 -19
- package/src/server/withAbideSources.ts +117 -0
- package/src/shared/ADDRESSES.ts +10 -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/LAUNCHER_FILES.ts +9 -0
- package/src/shared/RESERVED_DIRECTORIES.ts +24 -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 +2 -1
- package/src/shared/log.ts +27 -13
- package/src/shared/matchRoute.ts +23 -4
- package/src/{ui → shared}/menuBridge.ts +2 -2
- package/src/shared/menuItem.ts +4 -4
- package/src/shared/onMenu.ts +58 -0
- 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/{ui → shared}/reportMenu.ts +8 -10
- package/src/shared/routePattern.ts +122 -48
- 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 +37 -36
- 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/moveNode.ts +1 -1
- package/src/ui/reconcile.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 → index.abide} +0 -0
- /package/src/{compiler → shared}/SPECIFIERS.ts +0 -0
package/src/shared/matchRoute.ts
CHANGED
|
@@ -55,8 +55,18 @@ function walk(
|
|
|
55
55
|
if (index === segments.length)
|
|
56
56
|
return prefix || at === parts.length ? {} : null
|
|
57
57
|
const segment = segments[index] as Segment
|
|
58
|
-
// 30.5 — A REST TAKES
|
|
59
|
-
|
|
58
|
+
// 30.5 — A REST TAKES THE MOST THE SEGMENTS AFTER IT LEAVE, nothing included, so a handler's
|
|
59
|
+
// name after one (`/docs/[...path]/save`) is reached rather than spent on the rest.
|
|
60
|
+
if (segment.kind === 'rest') {
|
|
61
|
+
for (let end = parts.length; end >= at; end -= 1) {
|
|
62
|
+
const params = walk(segments, index + 1, parts, end, prefix)
|
|
63
|
+
if (params !== null) {
|
|
64
|
+
params[segment.name] = parts.slice(at, end)
|
|
65
|
+
return params
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return null
|
|
69
|
+
}
|
|
60
70
|
const part = parts[at]
|
|
61
71
|
if (segment.kind === 'optional') {
|
|
62
72
|
if (part !== undefined) {
|
|
@@ -80,8 +90,9 @@ function walk(
|
|
|
80
90
|
* The first route whose pattern matches `parts`, with its params, or `undefined`.
|
|
81
91
|
*
|
|
82
92
|
* 30.9 — THE FIRST PATTERN THAT MATCHES WINS, over a table already ranked most-specific-first.
|
|
83
|
-
*
|
|
84
|
-
*
|
|
93
|
+
* The client router resolves through this, and the server walks the same ranked table with
|
|
94
|
+
* `matchOf`, passing a route with no answer for the method asked. The two resolving one address
|
|
95
|
+
* alike is the isomorphism 30.8 rests on.
|
|
85
96
|
*
|
|
86
97
|
* Walked rather than compiled to a regex: a route table is matched once per navigation, so the
|
|
87
98
|
* walk is never the cost, and a regex would have to encode three segment kinds in a grammar nobody
|
|
@@ -97,3 +108,11 @@ export function firstMatch<T extends { segments: Segment[] }>(
|
|
|
97
108
|
}
|
|
98
109
|
return undefined
|
|
99
110
|
}
|
|
111
|
+
|
|
112
|
+
/** The params `segments` match the whole of `parts` with, or `undefined` where they do not. */
|
|
113
|
+
export function matchOf(
|
|
114
|
+
segments: Segment[],
|
|
115
|
+
parts: string[],
|
|
116
|
+
): Params | undefined {
|
|
117
|
+
return walk(segments, 0, parts, 0, false) ?? undefined
|
|
118
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The channel a desktop app's window opens to the page for its menu, or `undefined` in a browser.
|
|
3
3
|
*
|
|
4
|
-
* ITS OWN MODULE, so
|
|
5
|
-
* page inside a desktop app loads. 38.
|
|
4
|
+
* ITS OWN MODULE, so `onMenu` asks without importing the reporter: `reportMenu` is a chunk only a
|
|
5
|
+
* page inside a desktop app loads. 38.88.
|
|
6
6
|
*/
|
|
7
7
|
export function menuBridge():
|
|
8
8
|
| { postMessage(message: string): void }
|
package/src/shared/menuItem.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// 38.
|
|
2
|
-
// `<script
|
|
1
|
+
// 38.88 — THE DESKTOP APP'S MENU BAR IS THE ROUTE'S: a layout or a page calls `onMenu` in its
|
|
2
|
+
// `<script>`, and the window shows the menus filed while it stands. An item made by
|
|
3
3
|
// `menuItem` is one a page answers; the rest are an address or a role, which need no page.
|
|
4
4
|
|
|
5
|
-
/** What a menu item does natively, where an address or a page's own answer could not. 38.
|
|
5
|
+
/** What a menu item does natively, where an address or a page's own answer could not. 38.89. */
|
|
6
6
|
export type MenuRole =
|
|
7
7
|
| 'about'
|
|
8
8
|
| 'hide'
|
|
@@ -37,7 +37,7 @@ export type MenuItem =
|
|
|
37
37
|
items?: MenuItem[]
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
/** An item a page answers: each function `on` filed runs when it is picked. 38.
|
|
40
|
+
/** An item a page answers: each function `on` filed runs when it is picked. 38.91. */
|
|
41
41
|
export class MenuEntry {
|
|
42
42
|
label: string
|
|
43
43
|
key: string | undefined
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// 38.88, 38.92 — THE MENUS A LAYOUT OR A PAGE ADDS TO A DESKTOP APP'S MENU BAR, filed while it
|
|
2
|
+
// stands. Called in its `<script>`, so the menus are a function of its state: what `read` reads moving
|
|
3
|
+
// tells the window again, and the component going takes them with it. Outside a desktop app's window
|
|
4
|
+
// there is no menu bar, and nothing is filed.
|
|
5
|
+
|
|
6
|
+
import { menuBridge } from './menuBridge.ts'
|
|
7
|
+
import type { MenuItem } from './menuItem.ts'
|
|
8
|
+
import { watch } from './reactive/index.ts'
|
|
9
|
+
|
|
10
|
+
/** One `onMenu`'s menus, as it last answered, and whether its component has gone. */
|
|
11
|
+
type Filed = { menus: MenuItem[]; gone: boolean }
|
|
12
|
+
|
|
13
|
+
// IN THE ORDER FILED, which is outermost first: a layout's setup runs before the page inside it.
|
|
14
|
+
let filed: Filed[] = []
|
|
15
|
+
let telling = false
|
|
16
|
+
|
|
17
|
+
/** Files the menus `read` answers for as long as the calling component stands; returns what unfiles them. */
|
|
18
|
+
export function onMenu(read: () => readonly MenuItem[]): () => void {
|
|
19
|
+
if (menuBridge() === undefined) return () => {}
|
|
20
|
+
const entry: Filed = { menus: [], gone: false }
|
|
21
|
+
filed.push(entry)
|
|
22
|
+
const stop = watch(() => {
|
|
23
|
+
entry.gone = false
|
|
24
|
+
entry.menus = [...read()]
|
|
25
|
+
tell()
|
|
26
|
+
// A rerun files again at once; a component that went stays gone.
|
|
27
|
+
return () => {
|
|
28
|
+
entry.gone = true
|
|
29
|
+
tell()
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
// 12.8 — a `read` that threw stopped the watch with no cleanup to run, so the unfiling is
|
|
33
|
+
// done here too.
|
|
34
|
+
return () => {
|
|
35
|
+
stop()
|
|
36
|
+
entry.gone = true
|
|
37
|
+
tell()
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ONCE PER TURN, after every rerun and every component a navigation took has settled.
|
|
42
|
+
function tell(): void {
|
|
43
|
+
if (telling) return
|
|
44
|
+
telling = true
|
|
45
|
+
queueMicrotask(async () => {
|
|
46
|
+
telling = false
|
|
47
|
+
const standing: Filed[] = []
|
|
48
|
+
const menus: MenuItem[] = []
|
|
49
|
+
for (const entry of filed) {
|
|
50
|
+
if (entry.gone) continue
|
|
51
|
+
standing.push(entry)
|
|
52
|
+
menus.push(...entry.menus)
|
|
53
|
+
}
|
|
54
|
+
filed = standing
|
|
55
|
+
const { reportMenu } = await import('./reportMenu.ts')
|
|
56
|
+
reportMenu(menus)
|
|
57
|
+
})
|
|
58
|
+
}
|
|
@@ -12,7 +12,7 @@ import type { Schema } from '../Schema.ts'
|
|
|
12
12
|
import {
|
|
13
13
|
ARGS_GATE,
|
|
14
14
|
type Gated,
|
|
15
|
-
|
|
15
|
+
normalized,
|
|
16
16
|
PARAMETER_DEFAULTS,
|
|
17
17
|
} from './argsContract.ts'
|
|
18
18
|
import { CELL } from './CELL.ts'
|
|
@@ -352,7 +352,7 @@ export function createKeyed<
|
|
|
352
352
|
}
|
|
353
353
|
|
|
354
354
|
function reach(given: Args): Reactive<Stored, Accepted, Failures> {
|
|
355
|
-
const args =
|
|
355
|
+
const args = normalized(self, given)
|
|
356
356
|
const key = canonicalKey(args)
|
|
357
357
|
const standing = entries.get(key)
|
|
358
358
|
// 1.8: reaching an instance that exists joins the standing work — 15.19, unless that work
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* What a read throws where nothing has landed.
|
|
3
3
|
*
|
|
4
4
|
* RULEBOOK 2.2. It carries the value it was thrown FOR, and that is the whole mechanism behind the
|
|
5
|
-
* abort: a tracked body that meets one subscribes to `source` and
|
|
5
|
+
* abort: a tracked body that meets one subscribes to `source` and reruns when it lands, so the
|
|
6
6
|
* body never computes against an absent value and never takes the wrong branch. The
|
|
7
7
|
* privileged-call hazard is what that buys — a body run to completion against an unlanded read
|
|
8
8
|
* issues the else arm's call for a caller nothing has authenticated.
|
|
@@ -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,
|