@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.
Files changed (150) hide show
  1. package/ABIDE.md +8 -5
  2. package/CHANGELOG.md +58 -0
  3. package/bin/abide.ts +36 -65
  4. package/docs/REGISTRY.md +45 -12
  5. package/docs/RULEBOOK.md +24 -14
  6. package/docs/abide.md +999 -459
  7. package/package.json +1 -1
  8. package/src/compiler/ARRAY_MUTATORS.ts +20 -0
  9. package/src/compiler/Diagnostics.ts +198 -0
  10. package/src/compiler/KeptChecker.ts +97 -0
  11. package/src/compiler/LoweredFile.ts +609 -0
  12. package/src/compiler/Lowering.ts +18 -1
  13. package/src/compiler/PositionedRefusal.ts +1 -1
  14. package/src/compiler/SHARED_NAMES.ts +2 -0
  15. package/src/compiler/ShadowHost.ts +670 -0
  16. package/src/compiler/ShadowMap.ts +1 -1
  17. package/src/compiler/TypeSession.ts +76 -1743
  18. package/src/compiler/appLayout.ts +28 -20
  19. package/src/compiler/appLints.ts +109 -0
  20. package/src/compiler/argsDefaultsIn.ts +1 -1
  21. package/src/compiler/checkApp.ts +64 -68
  22. package/src/compiler/checkedShadow.ts +2 -2
  23. package/src/compiler/classifyRead.ts +16 -1
  24. package/src/compiler/compileApp.ts +102 -71
  25. package/src/compiler/declaredExports.ts +46 -21
  26. package/src/compiler/diagnoseSources.ts +232 -0
  27. package/src/compiler/emitClient.ts +1 -1
  28. package/src/compiler/formatAbide.ts +7 -8
  29. package/src/compiler/handlersUnder.ts +32 -63
  30. package/src/compiler/lineOf.ts +0 -19
  31. package/src/compiler/matchersOf.ts +3 -3
  32. package/src/compiler/mountsOf.ts +3 -3
  33. package/src/compiler/reactiveReads.ts +235 -0
  34. package/src/compiler/reactiveTypes.ts +13 -0
  35. package/src/compiler/readSources.ts +16 -18
  36. package/src/compiler/readsIn.ts +181 -82
  37. package/src/compiler/routesDeclaration.ts +46 -0
  38. package/src/compiler/scaffold.ts +7 -2
  39. package/src/compiler/serveLanguage.ts +46 -74
  40. package/src/compiler/sharedModuleWrites.ts +1 -1
  41. package/src/compiler/splitImports.ts +2 -2
  42. package/src/compiler/templateWarnings.ts +1 -2
  43. package/src/compiler/trackingWarnings.ts +1 -1
  44. package/src/server/CLIENT_BUILD.ts +4 -32
  45. package/src/server/DesktopSession.ts +213 -0
  46. package/src/server/Followers.ts +399 -0
  47. package/src/server/MODULE_QUERIES.ts +6 -2
  48. package/src/server/PageRender.ts +201 -0
  49. package/src/server/RESUME_MILLISECONDS.ts +2 -0
  50. package/src/server/abideModules.ts +7 -7
  51. package/src/server/answerFailure.ts +3 -2
  52. package/src/server/answerRequest.ts +16 -369
  53. package/src/server/answerRpc.ts +48 -302
  54. package/src/server/answerSocket.ts +41 -490
  55. package/src/server/appBuild.ts +377 -0
  56. package/src/server/appKit.ts +1 -1
  57. package/src/server/appPlugins.ts +15 -17
  58. package/src/server/appTree.ts +6 -5
  59. package/src/server/argsContract.ts +209 -0
  60. package/src/server/bridgeRelay.ts +153 -65
  61. package/src/server/claudeCode.ts +30 -127
  62. package/src/server/config.ts +6 -7
  63. package/src/server/createApp.ts +38 -66
  64. package/src/server/csp.ts +1 -1
  65. package/src/server/deferredBuilds.ts +3 -16
  66. package/src/server/devWorker.ts +11 -15
  67. package/src/server/faceOf.ts +1 -1
  68. package/src/server/generatedSurfaces.ts +2 -1
  69. package/src/server/handlerFileMount.ts +40 -0
  70. package/src/server/index.ts +3 -3
  71. package/src/server/installServer.ts +5 -9
  72. package/src/server/invokeHandler.ts +230 -0
  73. package/src/server/mountHandlers.ts +184 -0
  74. package/src/server/openDesktopWindow.ts +308 -0
  75. package/src/server/packageDirectory.ts +14 -0
  76. package/src/server/preload.ts +21 -2
  77. package/src/server/principal.ts +5 -4
  78. package/src/server/remote.ts +2 -2
  79. package/src/server/render.ts +31 -59
  80. package/src/server/requestScope.ts +5 -36
  81. package/src/server/runCompiled.ts +1 -1
  82. package/src/server/runDesktop.ts +20 -448
  83. package/src/server/seedBuffer.ts +12 -12
  84. package/src/server/serveDev.ts +24 -58
  85. package/src/server/servedApp.ts +390 -0
  86. package/src/server/shell.ts +1 -1
  87. package/src/server/signInChanged.ts +20 -0
  88. package/src/server/surfaceOf.ts +3 -2
  89. package/src/server/versionOf.ts +7 -16
  90. package/src/shared/RPC_SERVER.ts +27 -31
  91. package/src/shared/Schema.ts +1 -2
  92. package/src/shared/applyPatch.ts +1 -1
  93. package/src/shared/index.ts +1 -1
  94. package/src/shared/{note.ts → issueReport.ts} +22 -0
  95. package/src/shared/loadCall.ts +66 -0
  96. package/src/shared/muxFrames.ts +1 -1
  97. package/src/shared/reactive/Keyed.ts +24 -16
  98. package/src/shared/reactive/Reactive.ts +37 -991
  99. package/src/shared/reactive/argsContract.ts +131 -4
  100. package/src/shared/reactive/byOf.ts +2 -5
  101. package/src/shared/reactive/callLoad.ts +8 -14
  102. package/src/shared/reactive/cellShape.ts +734 -0
  103. package/src/shared/reactive/index.ts +12 -23
  104. package/src/shared/reactive/pairReactive.ts +5 -3
  105. package/src/shared/reactive/patchLog.ts +79 -0
  106. package/src/shared/reactive/patchMarks.ts +34 -0
  107. package/src/shared/reactive/producer.ts +121 -0
  108. package/src/shared/reactive/recordWrites.ts +11 -14
  109. package/src/shared/reactive/scoped.ts +3 -2
  110. package/src/shared/reactive/selections.ts +209 -0
  111. package/src/shared/reactive/share.ts +4 -3
  112. package/src/shared/reactive/watch.ts +3 -4
  113. package/src/shared/rpc.ts +20 -24
  114. package/src/shared/validateJson.ts +1 -2
  115. package/src/shared/writePath.ts +29 -0
  116. package/src/ui/ArmRegion.ts +10 -4
  117. package/src/ui/Level.ts +11 -20
  118. package/src/ui/LevelChain.ts +272 -0
  119. package/src/ui/Router.ts +106 -313
  120. package/src/ui/Scope.ts +17 -3
  121. package/src/ui/TryRegion.ts +22 -23
  122. package/src/ui/bindSpread.ts +2 -4
  123. package/src/ui/listenForRoutes.ts +3 -10
  124. package/src/ui/outboxQueue.ts +23 -11
  125. package/src/ui/outcomeOf.ts +120 -0
  126. package/src/ui/reconcile.ts +4 -6
  127. package/src/ui/routing.ts +2 -1
  128. package/src/ui/rows.ts +2 -2
  129. package/src/ui/setAttribute.ts +12 -3
  130. package/src/ui/socketWire.ts +28 -13
  131. package/src/ui/wire.ts +9 -10
  132. package/src/compiler/Finding.ts +0 -2
  133. package/src/compiler/Refusals.ts +0 -65
  134. package/src/compiler/formatDiagnosis.ts +0 -20
  135. package/src/server/appDirectories.ts +0 -9
  136. package/src/server/argsFor.ts +0 -39
  137. package/src/server/buildDirectory.ts +0 -10
  138. package/src/server/coerceArgs.ts +0 -109
  139. package/src/server/derivedArgs.ts +0 -26
  140. package/src/server/runBuild.ts +0 -24
  141. package/src/server/serveBuild.ts +0 -144
  142. package/src/server/testBuild.ts +0 -220
  143. package/src/shared/LOAD_OFFLINE.ts +0 -15
  144. package/src/shared/LOAD_SIGNAL.ts +0 -10
  145. package/src/shared/issuesOf.ts +0 -18
  146. package/src/shared/reactive/ENTRY_LIVES.ts +0 -7
  147. package/src/shared/reactive/FOLLOWED.ts +0 -8
  148. package/src/shared/reactive/PATCHES.ts +0 -11
  149. package/src/shared/reactive/PATCH_SINKS.ts +0 -26
  150. 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) and the defaults its body's parameter writes (11.29). A `Keyed` is where the
3
- // contract starts; a server's forwarder and an `Rpc` are faces built over one, and each carries it
4
- // through `contractOf` so a transport reads it off whichever face it was handed.
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 { LOAD_OFFLINE } from '../LOAD_OFFLINE.ts'
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 './index.ts'
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 (`LOAD_SIGNAL`, read by the body as it starts) — the caller's is only one vote.
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
- LOAD_SIGNAL.current = ending?.signal
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
- const standing = LOAD_OFFLINE.standing
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
- LOAD_SIGNAL.current = undefined
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)