@abide/abide 0.123.2 → 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 +58 -0
- package/bin/abide.ts +36 -65
- 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/Diagnostics.ts +198 -0
- package/src/compiler/KeptChecker.ts +97 -0
- package/src/compiler/LoweredFile.ts +609 -0
- package/src/compiler/Lowering.ts +18 -1
- package/src/compiler/PositionedRefusal.ts +1 -1
- 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 +76 -1743
- package/src/compiler/appLayout.ts +28 -20
- package/src/compiler/appLints.ts +109 -0
- package/src/compiler/argsDefaultsIn.ts +1 -1
- package/src/compiler/checkApp.ts +64 -68
- package/src/compiler/checkedShadow.ts +2 -2
- package/src/compiler/classifyRead.ts +16 -1
- package/src/compiler/compileApp.ts +102 -71
- package/src/compiler/declaredExports.ts +46 -21
- package/src/compiler/diagnoseSources.ts +232 -0
- package/src/compiler/emitClient.ts +1 -1
- package/src/compiler/formatAbide.ts +7 -8
- package/src/compiler/handlersUnder.ts +32 -63
- package/src/compiler/lineOf.ts +0 -19
- package/src/compiler/matchersOf.ts +3 -3
- package/src/compiler/mountsOf.ts +3 -3
- package/src/compiler/reactiveReads.ts +235 -0
- package/src/compiler/reactiveTypes.ts +13 -0
- package/src/compiler/readSources.ts +16 -18
- package/src/compiler/readsIn.ts +181 -82
- package/src/compiler/routesDeclaration.ts +46 -0
- package/src/compiler/scaffold.ts +7 -2
- package/src/compiler/serveLanguage.ts +46 -74
- package/src/compiler/sharedModuleWrites.ts +1 -1
- package/src/compiler/splitImports.ts +2 -2
- package/src/compiler/templateWarnings.ts +1 -2
- package/src/compiler/trackingWarnings.ts +1 -1
- package/src/server/CLIENT_BUILD.ts +4 -32
- 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/RESUME_MILLISECONDS.ts +2 -0
- package/src/server/abideModules.ts +7 -7
- package/src/server/answerFailure.ts +3 -2
- package/src/server/answerRequest.ts +16 -369
- package/src/server/answerRpc.ts +48 -302
- package/src/server/answerSocket.ts +41 -490
- package/src/server/appBuild.ts +377 -0
- package/src/server/appKit.ts +1 -1
- package/src/server/appPlugins.ts +15 -17
- package/src/server/appTree.ts +6 -5
- package/src/server/argsContract.ts +209 -0
- package/src/server/bridgeRelay.ts +153 -65
- package/src/server/claudeCode.ts +30 -127
- package/src/server/config.ts +6 -7
- package/src/server/createApp.ts +38 -66
- package/src/server/csp.ts +1 -1
- package/src/server/deferredBuilds.ts +3 -16
- package/src/server/devWorker.ts +11 -15
- package/src/server/faceOf.ts +1 -1
- package/src/server/generatedSurfaces.ts +2 -1
- package/src/server/handlerFileMount.ts +40 -0
- package/src/server/index.ts +3 -3
- 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/packageDirectory.ts +14 -0
- package/src/server/preload.ts +21 -2
- package/src/server/principal.ts +5 -4
- package/src/server/remote.ts +2 -2
- package/src/server/render.ts +31 -59
- package/src/server/requestScope.ts +5 -36
- package/src/server/runCompiled.ts +1 -1
- package/src/server/runDesktop.ts +20 -448
- package/src/server/seedBuffer.ts +12 -12
- package/src/server/serveDev.ts +24 -58
- package/src/server/servedApp.ts +390 -0
- package/src/server/shell.ts +1 -1
- package/src/server/signInChanged.ts +20 -0
- package/src/server/surfaceOf.ts +3 -2
- package/src/server/versionOf.ts +7 -16
- package/src/shared/RPC_SERVER.ts +27 -31
- package/src/shared/Schema.ts +1 -2
- package/src/shared/applyPatch.ts +1 -1
- 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/muxFrames.ts +1 -1
- package/src/shared/reactive/Keyed.ts +24 -16
- package/src/shared/reactive/Reactive.ts +37 -991
- package/src/shared/reactive/argsContract.ts +131 -4
- 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 -23
- package/src/shared/reactive/pairReactive.ts +5 -3
- package/src/shared/reactive/patchLog.ts +79 -0
- package/src/shared/reactive/patchMarks.ts +34 -0
- package/src/shared/reactive/producer.ts +121 -0
- package/src/shared/reactive/recordWrites.ts +11 -14
- package/src/shared/reactive/scoped.ts +3 -2
- package/src/shared/reactive/selections.ts +209 -0
- package/src/shared/reactive/share.ts +4 -3
- package/src/shared/reactive/watch.ts +3 -4
- package/src/shared/rpc.ts +20 -24
- package/src/shared/validateJson.ts +1 -2
- package/src/shared/writePath.ts +29 -0
- package/src/ui/ArmRegion.ts +10 -4
- package/src/ui/Level.ts +11 -20
- package/src/ui/LevelChain.ts +272 -0
- package/src/ui/Router.ts +106 -313
- package/src/ui/Scope.ts +17 -3
- package/src/ui/TryRegion.ts +22 -23
- package/src/ui/bindSpread.ts +2 -4
- package/src/ui/listenForRoutes.ts +3 -10
- package/src/ui/outboxQueue.ts +23 -11
- package/src/ui/outcomeOf.ts +120 -0
- package/src/ui/reconcile.ts +4 -6
- package/src/ui/routing.ts +2 -1
- package/src/ui/rows.ts +2 -2
- package/src/ui/setAttribute.ts +12 -3
- package/src/ui/socketWire.ts +28 -13
- package/src/ui/wire.ts +9 -10
- package/src/compiler/Finding.ts +0 -2
- package/src/compiler/Refusals.ts +0 -65
- package/src/compiler/formatDiagnosis.ts +0 -20
- package/src/server/appDirectories.ts +0 -9
- package/src/server/argsFor.ts +0 -39
- package/src/server/buildDirectory.ts +0 -10
- package/src/server/coerceArgs.ts +0 -109
- package/src/server/derivedArgs.ts +0 -26
- package/src/server/runBuild.ts +0 -24
- package/src/server/serveBuild.ts +0 -144
- package/src/server/testBuild.ts +0 -220
- 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
- package/src/shared/reactive/FOLLOWED.ts +0 -8
- package/src/shared/reactive/PATCHES.ts +0 -11
- package/src/shared/reactive/PATCH_SINKS.ts +0 -26
- package/src/shared/reactive/canonicalKey.ts +0 -95
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// WHAT A KEYED VALUE'S ARGS ARE HELD TO, past the face an app is handed: the `args` schema it was
|
|
2
|
-
// declared with (19.7)
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// declared with (19.7), the defaults its body's parameter writes (11.29), and — on a server's `Rpc`
|
|
3
|
+
// — the schema its annotation derives (19.9). A `Keyed` is where the contract starts; a server's
|
|
4
|
+
// forwarder and an `Rpc` are faces built over one, and each carries it through `contractOf` so a
|
|
5
|
+
// transport reads it off whichever face it was handed. What a server adds before a call off the wire
|
|
6
|
+
// reaches here — its text coerced, its path's parameters laid in — is `#server/argsContract.ts`.
|
|
5
7
|
//
|
|
6
8
|
// DEFAULTS BEFORE THE KEY, IN ONE PLACE. 11.27: a call that left a member to the parameter and one
|
|
7
9
|
// that wrote it are one entry, so every reach, call and lookup takes the key through `normalized`
|
|
@@ -10,9 +12,11 @@
|
|
|
10
12
|
//
|
|
11
13
|
// Imports no part of the core, so `scoped.ts` can carry the contract without reaching `Keyed.ts`.
|
|
12
14
|
|
|
15
|
+
import { DERIVED_ARGS } from '../DERIVED_ARGS.ts'
|
|
16
|
+
import type { Issues } from '../Issues.ts'
|
|
13
17
|
import { putOwn } from '../putOwn.ts'
|
|
18
|
+
import { validationError } from '../refuse.ts'
|
|
14
19
|
import type { Schema } from '../Schema.ts'
|
|
15
|
-
import { canonicalKey } from './canonicalKey.ts'
|
|
16
20
|
import type { Defaults } from './parameterDefaults.ts'
|
|
17
21
|
|
|
18
22
|
export const ARGS_GATE: unique symbol = Symbol.for('abide.argsGate')
|
|
@@ -62,6 +66,35 @@ export function normalized<Args>(
|
|
|
62
66
|
return defaults === null ? args : withDefaults(args, defaults)
|
|
63
67
|
}
|
|
64
68
|
|
|
69
|
+
/**
|
|
70
|
+
* 19.9 — the issues an `Rpc`'s args leave against the schema its annotation derives, `null` where
|
|
71
|
+
* they hold: `undefined` until the module its handler is loaded through stamps it (`derivedArgs`),
|
|
72
|
+
* and so in a browser always. A function rather than the schema, so this seam never carries the
|
|
73
|
+
* validator (19.13).
|
|
74
|
+
*/
|
|
75
|
+
export type HeldToAnnotation = {
|
|
76
|
+
[DERIVED_ARGS]: ((args: unknown) => Issues<unknown> | null) | undefined
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The args a call of `face` is made with: `normalized`, then held to the schema its annotation
|
|
81
|
+
* derives. Throws the `ValidationError` where they do not hold.
|
|
82
|
+
*
|
|
83
|
+
* 15.8, 19.9 — ONE STEP FOR EVERY WAY A HANDLER IS CALLED, in process and over the wire alike: a
|
|
84
|
+
* type an in-process caller wrote holds nothing at run time (`as never`, a value off JSON), so it is
|
|
85
|
+
* refused as the wire refuses it rather than handed to the body. 11.27 — the defaults first, so the
|
|
86
|
+
* check reads what the handler will.
|
|
87
|
+
*/
|
|
88
|
+
export function acceptedArgs(
|
|
89
|
+
face: Pick<Gated<unknown>, typeof PARAMETER_DEFAULTS> & HeldToAnnotation,
|
|
90
|
+
given: unknown,
|
|
91
|
+
): unknown {
|
|
92
|
+
const args = normalized(face, given)
|
|
93
|
+
const issues = face[DERIVED_ARGS]?.(args) ?? null
|
|
94
|
+
if (issues !== null) throw validationError(issues)
|
|
95
|
+
return args
|
|
96
|
+
}
|
|
97
|
+
|
|
65
98
|
/** The entry key a call with `args` is filed under: `normalized`, then 11.24's canonical form. */
|
|
66
99
|
export function keyOf(
|
|
67
100
|
face: Pick<Gated<unknown>, typeof PARAMETER_DEFAULTS>,
|
|
@@ -95,3 +128,97 @@ function withDefaults<Args>(args: Args, defaults: Defaults): Args {
|
|
|
95
128
|
}
|
|
96
129
|
return (filled ?? given) as Args
|
|
97
130
|
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* The entry key for an `Args` value: its canonical wire form, as a string.
|
|
134
|
+
*
|
|
135
|
+
* RULEBOOK 11.24: members sorted by name, `undefined` members dropped, `Date` written ISO.
|
|
136
|
+
*
|
|
137
|
+
* 11.26: the key is taken BEFORE any gate runs, because a browser holds no copy of the gate — a
|
|
138
|
+
* key taken after normalizing is computed one way on a server and another in a browser, and every
|
|
139
|
+
* handler with a normalizing schema would miss its seed buffer and re-issue the call on hydration.
|
|
140
|
+
*
|
|
141
|
+
* 11.64: WHAT THE WIRE FORM CANNOT WRITE IS REFUSED AT THE REACH. `JSON.stringify` does not fail
|
|
142
|
+
* on any of it — a `Set`, a `Map` and a class instance each have no own enumerable keys and
|
|
143
|
+
* canonicalize to `{}`, and a non-finite number is written `null` — so two different reaches
|
|
144
|
+
* produced one key and 1.8 joined the second to the first's standing work. The value was right
|
|
145
|
+
* for the first caller and wrong for every other one, with nothing to see.
|
|
146
|
+
*/
|
|
147
|
+
export function canonicalKey(args: unknown): string {
|
|
148
|
+
if (args === undefined) return ''
|
|
149
|
+
return JSON.stringify(canonical(args, []))
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// THE PATH IS A STACK OF STEPS, joined only when a refusal names it. Built as a string per member
|
|
153
|
+
// it was a concatenation for every element and every nested key on every reach, for a message the
|
|
154
|
+
// success path never reads.
|
|
155
|
+
type Steps = (string | number)[]
|
|
156
|
+
|
|
157
|
+
function canonical(value: unknown, path: Steps): unknown {
|
|
158
|
+
if (value === null) return value
|
|
159
|
+
const kind = typeof value
|
|
160
|
+
if (kind === 'number') {
|
|
161
|
+
if (!Number.isFinite(value)) throw outside(String(value), path)
|
|
162
|
+
return value
|
|
163
|
+
}
|
|
164
|
+
if (kind !== 'object') {
|
|
165
|
+
if (kind === 'boolean' || kind === 'string') return value
|
|
166
|
+
// `undefined` reaches here from an ARRAY element alone — a member is dropped above.
|
|
167
|
+
throw outside(kind === 'undefined' ? 'undefined' : `a ${kind}`, path)
|
|
168
|
+
}
|
|
169
|
+
if (value instanceof Date) return value.toISOString()
|
|
170
|
+
if (Array.isArray(value)) {
|
|
171
|
+
const out: unknown[] = []
|
|
172
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
173
|
+
path.push(index)
|
|
174
|
+
out.push(canonical(value[index], path))
|
|
175
|
+
path.pop()
|
|
176
|
+
}
|
|
177
|
+
return out
|
|
178
|
+
}
|
|
179
|
+
const prototype = Object.getPrototypeOf(value)
|
|
180
|
+
if (prototype !== Object.prototype && prototype !== null)
|
|
181
|
+
throw outside(named(value as object), path)
|
|
182
|
+
const source = value as Record<string, unknown>
|
|
183
|
+
const keys = Object.keys(source).sort()
|
|
184
|
+
// AN ORDINARY OBJECT, AND `putOwn` FOR THE ONE KEY THAT NEEDS IT: `__proto__` is a SETTER on
|
|
185
|
+
// `Object.prototype`, so `out[key] = …` for that key creates no own property and
|
|
186
|
+
// `JSON.stringify` never sees it — `{id: 1, __proto__: {role: 'admin'}}` and `{id: 1}` then
|
|
187
|
+
// canonicalize to one string, and 1.8 joins the second reach to the first's standing work.
|
|
188
|
+
// `Object.create(null)` closed that too, and V8 builds it in DICTIONARY mode: the shipped key
|
|
189
|
+
// took 49-56% of the time without it there, and a standing keyed reach 53%, where JSC reads
|
|
190
|
+
// level to 110% (`abide/bench/sharedCases.ts`, two runs per engine).
|
|
191
|
+
const out: Record<string, unknown> = {}
|
|
192
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
193
|
+
const key = keys[index] as string
|
|
194
|
+
const member = source[key]
|
|
195
|
+
if (member === undefined) continue
|
|
196
|
+
path.push(key)
|
|
197
|
+
putOwn(out, key, canonical(member, path))
|
|
198
|
+
path.pop()
|
|
199
|
+
}
|
|
200
|
+
return out
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function named(value: object): string {
|
|
204
|
+
const from = value.constructor as { name?: unknown } | undefined
|
|
205
|
+
return typeof from?.name === 'string' && from.name !== ''
|
|
206
|
+
? `a ${from.name}`
|
|
207
|
+
: 'a class instance'
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function outside(what: string, steps: Steps): Error {
|
|
211
|
+
let path = ''
|
|
212
|
+
for (const step of steps)
|
|
213
|
+
path +=
|
|
214
|
+
typeof step === 'number'
|
|
215
|
+
? `[${step}]`
|
|
216
|
+
: path === ''
|
|
217
|
+
? step
|
|
218
|
+
: `.${step}`
|
|
219
|
+
return new Error(
|
|
220
|
+
`An Args key cannot carry ${what} at ${
|
|
221
|
+
path === '' ? 'the arguments themselves' : `"${path}"`
|
|
222
|
+
}: the canonical wire form writes it as another entry's key (RULEBOOK 11.64).`,
|
|
223
|
+
)
|
|
224
|
+
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// the chunk every page loads. The cell is read here, in the core's folder, so its layout stays the
|
|
4
4
|
// core's.
|
|
5
5
|
import { CELL } from './CELL.ts'
|
|
6
|
+
import type { Callable } from './cellShape.ts'
|
|
6
7
|
import { isReactive } from './isReactive.ts'
|
|
7
8
|
|
|
8
9
|
/** The `by` a `Reactive` declared (32.32), or `null` where it declared none or `value` is not one. */
|
|
@@ -10,9 +11,5 @@ export function byOf(
|
|
|
10
11
|
value: unknown,
|
|
11
12
|
): string | ((element: unknown) => unknown) | null {
|
|
12
13
|
if (!isReactive(value)) return null
|
|
13
|
-
return (
|
|
14
|
-
value as unknown as {
|
|
15
|
-
[CELL]: { by: string | ((element: unknown) => unknown) | null }
|
|
16
|
-
}
|
|
17
|
-
)[CELL].by
|
|
14
|
+
return (value as unknown as Callable)[CELL].by
|
|
18
15
|
}
|
|
@@ -7,18 +7,16 @@
|
|
|
7
7
|
// ONE RECORD PER VALUE, IN A SIDE TABLE, not a field on the cell. Every value would carry the field
|
|
8
8
|
// and only a called one ever fills it, so the table is what keeps a plain `reactive` paying nothing.
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import { LOAD_SIGNAL } from '../LOAD_SIGNAL.ts'
|
|
10
|
+
import { beginLoadCall, endLoadCall, reachedInLoadCall } from '../loadCall.ts'
|
|
12
11
|
import { aborted, refuse } from '../refuse.ts'
|
|
13
|
-
// Through the barrel, for the cycle it names: rpc reaches this module ahead of the barrel.
|
|
12
|
+
// Through the barrel first, for the cycle it names: rpc reaches this module ahead of the barrel.
|
|
13
|
+
import { isReactive, STREAMING } from './index.ts'
|
|
14
14
|
import {
|
|
15
15
|
abandonLoad,
|
|
16
16
|
failedTransiently,
|
|
17
|
-
isReactive,
|
|
18
17
|
loadOf,
|
|
19
|
-
STREAMING,
|
|
20
18
|
streamingOrSettled,
|
|
21
|
-
} from './
|
|
19
|
+
} from './producer.ts'
|
|
22
20
|
|
|
23
21
|
/**
|
|
24
22
|
* Who is waiting on one value's load, and what the last one came to.
|
|
@@ -75,7 +73,7 @@ const ATTENDED = new WeakMap<object, Attended>()
|
|
|
75
73
|
* Reaches `args` through `reach` as a call does, under that call's `signal` and `timeout`.
|
|
76
74
|
*
|
|
77
75
|
* 16.49 — a load this call starts may be ended by it, so its transport is handed a signal of the
|
|
78
|
-
* load's own (`
|
|
76
|
+
* load's own (`startingLoadCall`, read by the body as it starts) — the caller's is only one vote.
|
|
79
77
|
*/
|
|
80
78
|
export function callLoad(
|
|
81
79
|
reach: (args: unknown) => unknown,
|
|
@@ -86,14 +84,12 @@ export function callLoad(
|
|
|
86
84
|
offline?: (args: unknown) => unknown,
|
|
87
85
|
): unknown {
|
|
88
86
|
const ending = signal === undefined ? undefined : new AbortController()
|
|
89
|
-
|
|
90
|
-
LOAD_OFFLINE.current = offline
|
|
87
|
+
beginLoadCall(ending?.signal, offline)
|
|
91
88
|
let reached: unknown
|
|
92
89
|
let attended: Attended | undefined
|
|
93
90
|
try {
|
|
94
91
|
reached = reach(args)
|
|
95
|
-
|
|
96
|
-
if (standing !== undefined) standing.value = reached
|
|
92
|
+
reachedInLoadCall(reached)
|
|
97
93
|
attended = ATTENDED.get(reached as object)
|
|
98
94
|
// 16.62 — the callers of an aborted load stopped waiting, and this one has not.
|
|
99
95
|
// 15.19 — and of one whose last answer failed transiently, which runs again here where an
|
|
@@ -106,9 +102,7 @@ export function callLoad(
|
|
|
106
102
|
if (again || failedTransiently(reached))
|
|
107
103
|
(reached as { refresh(): void }).refresh()
|
|
108
104
|
} finally {
|
|
109
|
-
|
|
110
|
-
LOAD_OFFLINE.current = undefined
|
|
111
|
-
LOAD_OFFLINE.standing = undefined
|
|
105
|
+
endLoadCall()
|
|
112
106
|
}
|
|
113
107
|
if (!isReactive(reached)) return reached
|
|
114
108
|
const load = loadOf(reached)
|