@abide/abide 0.105.1 → 0.107.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 (100) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/bin/abide.ts +4 -1
  3. package/docs/REGISTRY.md +156 -44
  4. package/docs/RULEBOOK.md +392 -88
  5. package/docs/abide.md +7245 -10248
  6. package/package.json +4 -1
  7. package/scaffold/AGENTS.md +2 -0
  8. package/scaffold/src/getGreeting.ts +6 -0
  9. package/scaffold/src/index.abide +3 -1
  10. package/scaffold/src/tests/getGreeting.test.ts +6 -0
  11. package/scaffold/src/tests/index.test.ts +18 -0
  12. package/src/compiler/DOCS_SITE.ts +2 -0
  13. package/src/compiler/Lowering.ts +234 -14
  14. package/src/compiler/PositionTable.ts +14 -7
  15. package/src/compiler/PositionedRefusal.ts +13 -0
  16. package/src/compiler/Refusals.ts +18 -11
  17. package/src/compiler/SHARED_NAMES.ts +2 -0
  18. package/src/compiler/ShadowMap.ts +37 -0
  19. package/src/compiler/TEMPLATE_BLOCKS.ts +12 -0
  20. package/src/compiler/TypeSession.ts +410 -31
  21. package/src/compiler/appLayout.ts +18 -2
  22. package/src/compiler/argsSchemaOf.ts +55 -9
  23. package/src/compiler/checkApp.ts +33 -0
  24. package/src/compiler/checkedShadow.ts +36 -22
  25. package/src/compiler/citedClauses.ts +14 -0
  26. package/src/compiler/classifyRead.ts +6 -0
  27. package/src/compiler/classifyTemplate.ts +7 -4
  28. package/src/compiler/compileApp.ts +2 -2
  29. package/src/compiler/declaredExports.ts +27 -6
  30. package/src/compiler/emitClient.ts +306 -103
  31. package/src/compiler/handlersUnder.ts +4 -0
  32. package/src/compiler/invocationProps.ts +1 -1
  33. package/src/compiler/isIgnored.ts +27 -0
  34. package/src/compiler/parseAbide.ts +176 -76
  35. package/src/compiler/projectShadow.ts +30 -6
  36. package/src/compiler/readSources.ts +1 -1
  37. package/src/compiler/readsIn.ts +100 -1
  38. package/src/compiler/renderMarkup.ts +36 -5
  39. package/src/compiler/scaffold.ts +19 -6
  40. package/src/compiler/serveLanguage.ts +156 -21
  41. package/src/compiler/templateContext.ts +150 -0
  42. package/src/compiler/tokenize.ts +6 -0
  43. package/src/server/BRIDGE_VERSION.ts +1 -1
  44. package/src/server/Inbox.ts +8 -0
  45. package/src/server/MCP_PROTOCOL_VERSION.ts +1 -1
  46. package/src/server/MOUNTABLE.ts +6 -0
  47. package/src/server/abideModules.ts +8 -1
  48. package/src/server/answerRequest.ts +3 -1
  49. package/src/server/answerRpc.ts +26 -12
  50. package/src/server/answerSocket.ts +26 -1
  51. package/src/server/argsFor.ts +4 -3
  52. package/src/server/assistant.ts +21 -14
  53. package/src/server/bridgeRelay.ts +108 -20
  54. package/src/server/claudeApi.ts +19 -11
  55. package/src/server/claudeCode.ts +19 -14
  56. package/src/server/commands.ts +73 -9
  57. package/src/server/createApp.ts +41 -7
  58. package/src/server/devWorker.ts +3 -3
  59. package/src/server/generatedSurfaces.ts +37 -4
  60. package/src/server/muxUpgrade.ts +47 -0
  61. package/src/server/preload.ts +13 -2
  62. package/src/server/render.ts +4 -1
  63. package/src/server/runCompiled.ts +1 -0
  64. package/src/server/seedBuffer.ts +31 -1
  65. package/src/server/serveDev.ts +83 -72
  66. package/src/server/surfaceOf.ts +3 -0
  67. package/src/server/testMount.ts +53 -0
  68. package/src/shared/ADDRESSES.ts +1 -1
  69. package/src/shared/ERROR_META.ts +3 -0
  70. package/src/shared/Schema.ts +1 -1
  71. package/src/shared/WORK.ts +14 -12
  72. package/src/shared/applyPatch.ts +1 -1
  73. package/src/shared/boundValue.ts +81 -0
  74. package/src/shared/index.ts +7 -2
  75. package/src/shared/jsonSchema.ts +25 -2
  76. package/src/shared/log.ts +11 -1
  77. package/src/shared/reactive/Keyed.ts +30 -6
  78. package/src/shared/reactive/PATCHES.ts +1 -1
  79. package/src/shared/reactive/Reactive.ts +118 -60
  80. package/src/shared/reactive/canonicalKey.ts +1 -1
  81. package/src/shared/reactive/index.ts +13 -5
  82. package/src/shared/reactive/recordWrites.ts +1 -1
  83. package/src/shared/reactive/watch.ts +26 -4
  84. package/src/shared/rpc.ts +6 -1
  85. package/src/shared/url.ts +7 -3
  86. package/src/shared/validateJson.ts +1 -1
  87. package/src/ui/ArmRegion.ts +2 -2
  88. package/src/ui/List.ts +9 -9
  89. package/src/ui/RawRun.ts +1 -1
  90. package/src/ui/Router.ts +194 -36
  91. package/src/ui/Scope.ts +29 -0
  92. package/src/ui/adoptWalk.ts +3 -3
  93. package/src/ui/applyHead.ts +25 -3
  94. package/src/ui/bindElement.ts +4 -0
  95. package/src/ui/bindProperty.ts +70 -22
  96. package/src/ui/index.ts +1 -1
  97. package/src/ui/listenForRoutes.ts +14 -3
  98. package/src/ui/reconcile.ts +23 -28
  99. package/src/ui/regions.ts +9 -2
  100. package/src/ui/rows.ts +108 -28
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @abide/abide
2
2
 
3
+ ## 0.107.0
4
+
5
+ ### Minor Changes
6
+
7
+ - bd2a74f: Breaking: an assistant announces a tool call as the model starts writing it, and a site may ask the visitor's Claude Code for built-in tools ([`c959804`](https://github.com/briancray/abide/commit/c959804fd19c3e99fe439f3f1120f1ef13f42ff8))
8
+
9
+ ### Patch Changes
10
+
11
+ - bd2a74f: a bind:element callback is handed a connected node whose blocks and components have built, and bind:element on a component is a build error ([`e15fbee`](https://github.com/briancray/abide/commit/e15fbee6f7c780dab2105ca245dd79dbb4a2e317))
12
+
13
+ ## 0.106.0
14
+
15
+ ### Minor Changes
16
+
17
+ - 9aa03a5: socket mux bounds, request ceilings, stop timeout, scroll restore, numeric and composed bind:value, and keyed rows kept across patches (16.78, 18.28-18.32, 30.29-30.31, 32.58-32.62, 32.66, 32.67, 42.17) ([`301bf47`](https://github.com/briancray/abide/commit/301bf47adf5059271b439fe3c129963861dd1c72))
18
+ - 9aa03a5: block tags and template context, cited clauses on diagnostics, lsp references and completions, derived answer schemas, and a scaffold with a handler and its test (32.63-32.65, 38.112-38.118, 19.21-19.24) ([`4f86359`](https://github.com/briancray/abide/commit/4f86359c7f995e2ec8c7b71d1bce5bae4cccc465))
19
+ - 9aa03a5: publish @abide/harness, with abide's bench and e2e moved to abide and the arms to the docs app (43.1, 43.21, 43.23, 38.119) ([`8521bd7`](https://github.com/briancray/abide/commit/8521bd7b364d998a1a59f764b51b34cf273f7a84))
20
+
21
+ ### Patch Changes
22
+
23
+ - 9aa03a5: a render's scope is held while the values it seeded are in flight, so a joined stream closes (41.13) ([`09d7aa0`](https://github.com/briancray/abide/commit/09d7aa040d20205dddd5c8de19a425523f5124a4))
24
+ - 9aa03a5: an error page's document is marked, so the browser hydrates no page over it and leaving it is a document load (35.18) ([`28daeed`](https://github.com/briancray/abide/commit/28daeed1468c8f466c722d177a5abbf5afaf333a))
25
+ - 9aa03a5: url() takes a union of route literals with optional params ([`3cd7683`](https://github.com/briancray/abide/commit/3cd76831f9dc0867b9a2f8576803ca9ddcac845e))
26
+ - 9aa03a5: a mutation called twice in one turn is two writes (16.7) ([`60c5915`](https://github.com/briancray/abide/commit/60c5915be24e843eee0671fa0db444aeb8a69525))
27
+ - d42dfad: @abide/abide/test preloads no build from a workspace's root ([`c76a54e`](https://github.com/briancray/abide/commit/c76a54e3ddaf03d42c5534dee97c124856d063fb))
28
+ - 9aa03a5: a request with no args is held to a derived args schema, so a missing required member is refused (19.9) ([`e8e65f9`](https://github.com/briancray/abide/commit/e8e65f9ac7de561deb649b19c792ca31dd816008))
29
+ - 9aa03a5: a checker releases the snapshots it replaced, and abide lsp closes an app's checker once none of its pages is open ([`fc19c96`](https://github.com/briancray/abide/commit/fc19c965fb2ef5abdc873279a1091806f2745ac9))
30
+
3
31
  ## 0.105.1
4
32
 
5
33
  ### Patch Changes
package/bin/abide.ts CHANGED
@@ -45,6 +45,7 @@ import { pathsUnder, serveBuild } from '#server/serveBuild.ts'
45
45
  import { serveDev } from '#server/serveDev.ts'
46
46
  import { escapeText } from '#shared/escapeText.ts'
47
47
  import { ICON_ADDRESSES, LAUNCHER_PAGE } from '#shared/LAUNCHER_FILES.ts'
48
+ import abidePackage from '../package.json' with { type: 'json' }
48
49
 
49
50
  /** Where the app a command builds or serves is, and where its build goes — both absolute. */
50
51
  type AppDirectories = { root: string; out: string }
@@ -55,6 +56,7 @@ type AppDirectories = { root: string; out: string }
55
56
  */
56
57
  const DISK: Host = {
57
58
  name: 'abide',
59
+ version: abidePackage.version,
58
60
  // 38.100 — built in this process rather than a child, where nobody built it, and said on
59
61
  // stderr: the command's stdout is its own — `openapi`'s document, `mcp`'s messages.
60
62
  app: ({ values }) =>
@@ -688,7 +690,8 @@ async function scaffoldCommand({
688
690
  for (const path of written) console.log(` wrote ${path}`)
689
691
  for (const path of skipped) console.log(` kept ${path}`)
690
692
  console.log(
691
- `\nabide scaffold: ${into}\n\n cd ${name}\n bun install\n abide dev`,
693
+ // The starter's own script: `abide` alone is on no PATH until it is installed globally.
694
+ `\nabide scaffold: ${into}\n\n cd ${name}\n bun install\n bun run dev`,
692
695
  )
693
696
  return 0
694
697
  }
package/docs/REGISTRY.md CHANGED
@@ -58,12 +58,12 @@ Accepted, Failures>` is the inner value's own `Accepted`. Then `Failures`, then
58
58
  | `Shaped` | `unknown` | What a `transform` hands back: the value to store, or a refusal. | 4.6, 4.9 |
59
59
  | `Produced` | `unknown` | What a production carries, where that differs from what is stored. | 1.2, 9.3 |
60
60
  | `Args` | `Record<string, JsonValue> \| undefined` | The key of a keyed value. | 9.4, 11.24, 11.25, 11.26, 11.27, 11.29, 11.31, 11.64, 19.15 |
61
- | `reactive` | `<Computed, Shaped = Awaited<Computed>, Gate = Schema<Awaited<Computed>>>(body: () => Computed, options?: Omit<ReactiveOptions<Awaited<Computed>, Kept<Shaped>, never>, 'schema'> & { transform?: (value: Awaited<Computed>) => Shaped; schema?: Gate & Schema<Awaited<Computed>> }) => Reactive<Kept<Shaped>, Awaited<Computed>, AdoptedFailures<Computed> \| Refused<Shaped> \| Gated<Gate, Awaited<Computed>>, AdoptedProduced<Computed, Kept<Shaped>>>` | The unkeyed body overload. | 3.9, 11.1, 11.3, 11.5, 11.7, 11.8, 11.9, 11.10, 11.11, 11.12, 11.13, 11.14, 11.15, 11.16, 11.17, 11.18, 11.19, 11.20, 11.21, 11.22, 11.32, 11.33, 11.50, 11.51, 11.52, 11.53, 11.54, 11.57, 11.58, 41.6, 11.62, 11.63, 11.65, 11.66, 12.14 |
61
+ | `reactive` | `<Computed, Shaped = Awaited<Computed>, Gate = Schema<Awaited<Computed>>>(body: () => Computed, options?: Omit<ReactiveOptions<Awaited<NoInfer<Computed>>, Kept<Shaped>, never>, 'schema'> & { transform?: (value: Awaited<NoInfer<Computed>>) => Shaped; schema?: Gate & Schema<Awaited<NoInfer<Computed>>> }) => Reactive<Kept<Shaped>, Awaited<Computed>, AdoptedFailures<Computed> \| Refused<Shaped> \| Gated<Gate, Awaited<Computed>>, AdoptedProduced<Computed, Kept<Shaped>>>` | The unkeyed body overload. | 3.9, 11.1, 11.3, 11.5, 11.7, 11.8, 11.9, 11.10, 11.11, 11.12, 11.13, 11.14, 11.15, 11.16, 11.17, 11.18, 11.19, 11.20, 11.21, 11.22, 11.32, 11.33, 11.50, 11.51, 11.52, 11.53, 11.54, 11.57, 11.58, 41.6, 11.62, 11.63, 11.65, 11.66, 12.14, 19.24 |
62
62
  | `reactive` | `<Chunk, Shaped = Chunk[], Gate = Schema<Chunk[]>>(body: () => AsyncGenerator<Chunk> \| Promise<AsyncGenerator<Chunk>>, options?: Omit<ReactiveOptions<Chunk[], Kept<Shaped>, never>, 'schema'> & { transform?: (value: Chunk[]) => Shaped; schema?: Gate & Schema<Chunk[]> }) => Reactive<Kept<Shaped>, Chunk[], Refused<Shaped> \| Gated<Gate, Chunk[]>, Chunk>` | The streaming body overload. | 3.7, 3.8, 4.8, 9.1, 9.2, 9.6, 9.7, 9.8, 9.9 |
63
63
  | `reactive` | `<Chunk, Args, Shaped = Chunk[], Gate = Schema<Chunk[]>, ArgsGate = Schema<Args>>(body: (args: Args) => AsyncGenerator<Chunk> \| Promise<AsyncGenerator<Chunk>>, options?: Omit<ReactiveOptions<Chunk[], Kept<Shaped>, never, Args>, 'schema' \| 'args'> & { transform?: (value: Chunk[]) => Shaped; schema?: Gate & Schema<Chunk[]>; args?: ArgsGate & Schema<Args> }) => Keyed<Kept<Shaped>, Args, Chunk[], Refused<Shaped> \| Gated<Gate, Chunk[]> \| Gated<ArgsGate, Args>, Chunk>` | The keyed streaming body overload. | 9.6, 9.9 |
64
64
  | `reactive` | `<Chunk, Args, Shaped = Chunk[], Gate = Schema<Chunk[]>, ArgsGate = Schema<Args \| undefined>>(body: (args?: Args) => AsyncGenerator<Chunk> \| Promise<AsyncGenerator<Chunk>>, options?: Omit<ReactiveOptions<Chunk[], Kept<Shaped>, never, Args \| undefined>, 'schema' \| 'args'> & { transform?: (value: Chunk[]) => Shaped; schema?: Gate & Schema<Chunk[]>; args?: ArgsGate & Schema<Args \| undefined> }) => Keyed<Kept<Shaped>, Args \| undefined, Chunk[], Refused<Shaped> \| Gated<Gate, Chunk[]> \| Gated<ArgsGate, Args \| undefined>, Chunk>` | The keyed streaming overload for a body whose parameter is optional. | 11.2 |
65
- | `reactive` | `<Computed, Args, Shaped = Awaited<Computed>, Gate = Schema<Awaited<Computed>>, ArgsGate = Schema<Args>>(body: (args: Args) => Computed, options?: Omit<ReactiveOptions<Awaited<Computed>, Kept<Shaped>, never, Args>, 'schema' \| 'args'> & { transform?: (value: Awaited<Computed>) => Shaped; schema?: Gate & Schema<Awaited<Computed>>; args?: ArgsGate & Schema<Args> }) => Keyed<Kept<Shaped>, Args, Awaited<Computed>, AdoptedFailures<Computed> \| Refused<Shaped> \| Gated<Gate, Awaited<Computed>> \| Gated<ArgsGate, Args>, AdoptedProduced<Computed, Kept<Shaped>>>` | The keyed body overload. | 11.2, 11.3, 11.4, 11.6, 11.7, 11.23, 11.55, 11.62 |
66
- | `reactive` | `<Computed, Args, Shaped = Awaited<Computed>, Gate = Schema<Awaited<Computed>>, ArgsGate = Schema<Args \| undefined>>(body: (args?: Args) => Computed, options?: Omit<ReactiveOptions<Awaited<Computed>, Kept<Shaped>, never, Args \| undefined>, 'schema' \| 'args'> & { transform?: (value: Awaited<Computed>) => Shaped; schema?: Gate & Schema<Awaited<Computed>>; args?: ArgsGate & Schema<Args \| undefined> }) => Keyed<Kept<Shaped>, Args \| undefined, Awaited<Computed>, AdoptedFailures<Computed> \| Refused<Shaped> \| Gated<Gate, Awaited<Computed>> \| Gated<ArgsGate, Args \| undefined>, AdoptedProduced<Computed, Kept<Shaped>>>` | The keyed overload for a body whose parameter is optional. | 11.2 |
65
+ | `reactive` | `<Computed, Args, Shaped = Awaited<Computed>, Gate = Schema<Awaited<Computed>>, ArgsGate = Schema<Args>>(body: (args: Args) => Computed, options?: Omit<ReactiveOptions<Awaited<NoInfer<Computed>>, Kept<Shaped>, never, Args>, 'schema' \| 'args'> & { transform?: (value: Awaited<NoInfer<Computed>>) => Shaped; schema?: Gate & Schema<Awaited<NoInfer<Computed>>>; args?: ArgsGate & Schema<Args> }) => Keyed<Kept<Shaped>, Args, Awaited<Computed>, AdoptedFailures<Computed> \| Refused<Shaped> \| Gated<Gate, Awaited<Computed>> \| Gated<ArgsGate, Args>, AdoptedProduced<Computed, Kept<Shaped>>>` | The keyed body overload. | 11.2, 11.3, 11.4, 11.6, 11.7, 11.23, 11.55, 11.62, 19.24 |
66
+ | `reactive` | `<Computed, Args, Shaped = Awaited<Computed>, Gate = Schema<Awaited<Computed>>, ArgsGate = Schema<Args \| undefined>>(body: (args?: Args) => Computed, options?: Omit<ReactiveOptions<Awaited<Computed>, Kept<Shaped>, never, Args \| undefined>, 'schema' \| 'args'> & { transform?: (value: Awaited<Computed>) => Shaped; schema?: Gate & Schema<Awaited<Computed>>; args?: ArgsGate & Schema<Args \| undefined> }) => Keyed<Kept<Shaped>, Args \| undefined, Awaited<Computed>, AdoptedFailures<Computed> \| Refused<Shaped> \| Gated<Gate, Awaited<Computed>> \| Gated<ArgsGate, Args \| undefined>, AdoptedProduced<Computed, Kept<Shaped>>>` | The keyed overload for a body whose parameter is optional. | 11.2, 19.24 |
67
67
  | `reactive` | `<Accepted, Shaped = Accepted, Gate = Schema<Accepted>>(initial: Accepted \| Promise<Accepted>, options?: Omit<ReactiveOptions<Accepted, Kept<Shaped>, never>, 'schema'> & { transform?: (value: Accepted) => Shaped; schema?: Gate & Schema<Accepted> }) => Reactive<Kept<Shaped>, Accepted, Refused<Shaped> \| Gated<Gate, Accepted>>` | The value overload, for what the scope owns outright. | 4.1, 4.2, 4.14, 4.15, 4.16 |
68
68
  | `reactive` | `<Accepted = undefined, Shaped = Accepted, Gate = Schema<Accepted>>(initial: undefined, options?: Omit<ReactiveOptions<Accepted, Kept<Shaped>, never>, 'schema'> & { transform?: (value: Accepted) => Shaped; schema?: Gate & Schema<Accepted> }) => Reactive<Kept<Shaped>, Accepted, Refused<Shaped> \| Gated<Gate, Accepted>>` | The undefined overload, for a value opening at `undefined`. | 4.15, 4.16 |
69
69
  | `reactive` | `<Accepted>() => Reactive<Kept<Accepted>, Accepted, Refused<Accepted>>` | The empty overload, for a value nothing has written yet. | 4.14 |
@@ -181,21 +181,21 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
181
181
  | `refuse.named` | `<Name extends string, Data extends JsonValue>(name: Name, status: number \| undefined, message: string \| ((data: Data) => string) \| undefined, options: { schema: Schema<Data> }) => ((data?: Data) => Failed<Name, Data>) & { is: (error: unknown) => error is Failed<Name, Data> }` | The same, carrying data its schema declares. | 15.3, 15.11, 15.12, 15.13, 15.14, 15.15, 15.18 |
182
182
  | `Failed<Name, Data>` | `Error & { name: Name; status: number; message: string; data: Data }` | The one refusal type, in-process and over a wire alike. | 15.1, 15.2, 15.6, 16.40, 16.41, 15.21 |
183
183
  | `return myError(data)` | `Failed<Name, Data>` | The one spelling for every refusal. | 15.4, 15.5 |
184
- | `notFound` | `(data?: { path: string; method: string }) => Failed<'NotFound', { path: string; method: string }>` | Abide's own refusal for an address nothing answers. | 15.7, 15.9, 15.22 |
185
- | `validationError` | `<T>(data?: Issues<T>) => Failed<'ValidationError', Issues<T>>` | Abide's own refusal for a schema or an `args` gate that refused, or a call's route parameter no segment can spell. | 15.8, 15.9 |
186
- | `aborted` | `(data?: undefined) => Failed<'Aborted', undefined>` | Abide's own refusal for a load every caller aborted, and for a client that left. | 15.9, 16.49, 16.63 |
184
+ | `notFound` | `(data?: { path: string; method: string }) => Failed<'NotFound', { path: string; method: string }>` | abide's own refusal for an address nothing answers. | 15.7, 15.9, 15.22 |
185
+ | `validationError` | `<T>(data?: Issues<T>) => Failed<'ValidationError', Issues<T>>` | abide's own refusal for a schema or an `args` gate that refused, or a call's route parameter no segment can spell. | 15.8, 15.9 |
186
+ | `aborted` | `(data?: undefined) => Failed<'Aborted', undefined>` | abide's own refusal for a load every caller aborted, and for a client that left. | 15.9, 16.49, 16.63 |
187
187
  | `myError(data)` DISCARDED | compile error | A `Failed` built and thrown away. | 15.10 |
188
188
 
189
189
  ## Tracking
190
190
 
191
191
  | Context | Meaning | Rules |
192
192
  | --- | --- | --- |
193
- | template expression | The flow itself. | 14.1, 14.2, 14.11, 14.17, 32.25, 32.57 |
193
+ | template expression | The flow itself. | 14.1, 14.2, 14.11, 14.17, 14.19, 32.25, 32.57 |
194
194
  | branch-local `<script>` | An item's own setup scope. | 14.8, 34.7, 34.8, 34.9, 34.10 |
195
- | `reactive` body, unkeyed | The unkeyed body's own scope. | 14.3, 14.11, 14.15, 14.16, 14.17 |
195
+ | `reactive` body, unkeyed | The unkeyed body's own scope. | 14.3, 14.11, 14.15, 14.16, 14.17, 14.19 |
196
196
  | `reactive` body, keyed | The keyed body's own scope. | 14.4 |
197
197
  | block body binding | A live read, like a prop. | 14.5 |
198
- | `watch` effect, bare | The effect's own scope. | 14.6, 14.14, 14.16 |
198
+ | `watch` effect, bare | The effect's own scope. | 14.6, 14.14, 14.16, 14.19 |
199
199
  | `watch` effect, over sources | The scope its sources narrow. | 12.3, 14.6 |
200
200
  | component `<script>` setup | The component's own setup scope. | 14.7, 14.12, 14.13 |
201
201
  | event handler | Not the flow. | 14.9 |
@@ -272,7 +272,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
272
272
  | `Issues<T>` | `T extends object ? Partial<Record<Paths<T> \| '', string[]>> : string[]` | What was wrong, keyed by where. | 19.4, 19.19 |
273
273
  | `Paths<T>` | `string` | The dot-joined leaf paths of a type. | 19.5 |
274
274
  | `JsonSchema` | `JsonValue` | A JSON Schema document, the native form. | 19.7, 19.14 |
275
- | `jsonSchema` | `<T>(document: JsonSchema) => StandardSchemaV1<T>` | A JSON Schema document as a `Schema`, carrying its validator. | 19.13, 19.14, 20.11 |
275
+ | `jsonSchema` | `<T>(document: JsonSchema) => StandardSchemaV1<T>` | A JSON Schema document as a `Schema`, carrying its validator. | 19.13, 19.14, 19.23, 20.11 |
276
276
  | `validateJson` | `<T>(schema: JsonSchema, value: unknown) => Issues<T> \| null` | The native validator. | 19.13 |
277
277
 
278
278
  ## Headers abide generates
@@ -306,44 +306,44 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
306
306
 
307
307
  | Name | Signature | Meaning | Rules |
308
308
  | --- | --- | --- | --- |
309
- | `/__abide/openapi.json` | `GET` | The generated OpenAPI document. | 20.5, 20.6, 20.7, 20.8, 20.11, 20.13, 36.7 |
309
+ | `/__abide/openapi.json` | `GET` | The generated OpenAPI document. | 19.21, 19.22, 20.5, 20.6, 20.7, 20.8, 20.11, 20.13, 20.15, 36.7 |
310
310
  | `OpenApiDocument` | `JsonValue` | OpenAPI 3.1, whose schema dialect is `JsonSchema`. | 20.6 |
311
311
 
312
312
  ## MCP
313
313
 
314
314
  | Name | Signature | Meaning | Rules |
315
315
  | --- | --- | --- | --- |
316
- | `/__abide/mcp` | `POST` | The streamable-http MCP endpoint. | 20.5, 20.7, 20.8, 20.9, 20.10, 20.12, 20.13, 36.7 |
316
+ | `/__abide/mcp` | `POST` | The streamable-http MCP endpoint. | 19.21, 19.22, 20.5, 20.7, 20.8, 20.9, 20.10, 20.12, 20.13, 20.14, 20.16, 20.17, 36.7, 16.78 |
317
317
 
318
318
  # Assistant
319
319
 
320
320
  | Name | Signature | Meaning | Rules |
321
321
  | --- | --- | --- | --- |
322
- | `assistant` | `(messages: AssistantMessage[], provider: Provider, options?: AssistantOptions) => AsyncGenerator<AssistantEvent>` | A conversation with a model, as the events it produces. | 45.1, 45.2, 45.3, 45.4, 45.21 |
323
- | `AssistantMessage` | `{ role: 'user' \| 'assistant'; content: string }` | One turn of the transcript a caller sends. | 45.3, 45.4 |
324
- | `AssistantEvent` | `{ type: 'text'; text: string } \| { type: 'tool'; id: string; name: string; input: unknown } \| { type: 'result'; id: string; text: string; failed: boolean } \| { type: 'done'; stop: string; usage: { input: number; output: number } }` | What a conversation produces: text, a tool call, its result, and the end. | 45.2, 45.11 |
325
- | `AssistantOptions` | `{ mcp?: boolean; maxTurns?: number }` | What `assistant` takes beside a provider. | 45.6, 45.8, 45.11 |
326
- | `Provider` | `{ run(messages: AssistantMessage[], tools: Toolbox, limits: { maxTurns: number; signal: AbortSignal }): AsyncGenerator<AssistantEvent> }` | What runs a conversation. | 45.2 |
327
- | `Toolbox` | `{ list: ToolDefinition[]; call(name: string, input: unknown): Promise<{ text: string; failed: boolean }> }` | The app's MCP tools, called as the asker. | 45.6, 45.7 |
328
- | `ToolDefinition` | `{ name: string; description: string; inputSchema: unknown }` | A tool as the app's MCP surface lists it. | 45.6 |
322
+ | `assistant` | `(messages: AssistantMessage[], provider: Provider, options?: AssistantOptions) => AsyncGenerator<AssistantEvent>` | A conversation with a model, as the events it produces. | 44.1, 44.2, 44.3, 44.4, 44.21 |
323
+ | `AssistantMessage` | `{ role: 'user' \| 'assistant'; content: string }` | One turn of the transcript a caller sends. | 44.3, 44.4 |
324
+ | `AssistantEvent` | `{ type: 'text'; text: string } \| { type: 'tool'; id: string; name: string } \| { type: 'input'; id: string; input: unknown } \| { type: 'result'; id: string; text: string; failed: boolean } \| { type: 'done'; stop: string; usage: { input: number; output: number } }` | What a conversation produces: text, a tool call as it starts, its input once written, its result, and the end. | 44.2, 44.11, 44.29 |
325
+ | `AssistantOptions` | `{ mcp?: boolean; maxTurns?: number }` | What `assistant` takes beside a provider. | 44.6, 44.8, 44.11 |
326
+ | `Provider` | `{ run(messages: AssistantMessage[], tools: Toolbox, limits: { maxTurns: number; signal: AbortSignal }): AsyncGenerator<AssistantEvent> }` | What runs a conversation. | 44.2, 44.29 |
327
+ | `Toolbox` | `{ list: ToolDefinition[]; call(name: string, input: unknown): Promise<{ text: string; failed: boolean }> }` | The app's MCP tools, called as the asker. | 44.6, 44.7 |
328
+ | `ToolDefinition` | `{ name: string; description: string; inputSchema: unknown; annotations: { readOnlyHint: boolean; destructiveHint: boolean; idempotentHint: boolean }; outputSchema: unknown }` | A tool as the app's MCP surface lists it. | 20.14, 20.16, 44.6 |
329
329
 
330
330
  ## `claudeApi`
331
331
 
332
332
  | Name | Signature | Meaning | Rules |
333
333
  | --- | --- | --- | --- |
334
- | `claudeApi` | `(config: ClaudeApiConfig) => Provider` | The Claude Messages API as a provider. | 45.9, 45.10, 45.12, 45.21 |
335
- | `ClaudeApiConfig` | `{ apiKey: string; baseURL?: string; model: string; max_tokens: number; system?: string \| unknown[]; tools?: unknown[]; [field: string]: unknown }` | A Messages API request body, less `messages` and `stream`, with the client's key and origin. | 45.9, 45.10 |
334
+ | `claudeApi` | `(config: ClaudeApiConfig) => Provider` | The Claude Messages API as a provider. | 44.9, 44.10, 44.12, 44.21 |
335
+ | `ClaudeApiConfig` | `{ apiKey: string; baseURL?: string; model: string; max_tokens: number; system?: string \| unknown[]; tools?: unknown[]; [field: string]: unknown }` | A Messages API request body, less `messages` and `stream`, with the client's key and origin. | 44.9, 44.10 |
336
336
 
337
337
  ## `claudeCode`
338
338
 
339
339
  | Name | Signature | Meaning | Rules |
340
340
  | --- | --- | --- | --- |
341
- | `claudeCode` | `(config?: ClaudeCodeConfig) => Provider` | The visitor's own Claude Code, reached through the bridge paired with their browser, as a provider. | 45.5, 45.13, 45.17, 45.18, 45.19 |
342
- | `ClaudeCodeConfig` | `{ model?: string; fallbackModel?: string; systemPrompt?: string; appendSystemPrompt?: string; effort?: 'low' \| 'medium' \| 'high' \| 'xhigh' \| 'max' }` | The Claude Code options a site may set for a turn. | 45.16 |
343
- | `claudeCode.bridgeCommand` | `() => { command: string; expires: number }` | The command that pairs a bridge with this browser. | 45.14 |
344
- | `claudeCode.bridge` | `() => AsyncGenerator<BridgeStatus>` | This browser's pairing, as it changes. | 45.20, 45.22 |
345
- | `BridgeStatus` | `{ paired: boolean; command: string \| undefined }` | Whether a bridge is paired, and the command that pairs one where none is. | 45.20 |
346
- | `/__abide/bridge` | `GET`, `POST` | Where a bridge pairs, and is relayed its turns. | 36.7, 45.14, 45.15, 45.19 |
341
+ | `claudeCode` | `(config?: ClaudeCodeConfig) => Provider` | The visitor's own Claude Code, reached through the bridge paired with their browser, as a provider. | 44.5, 44.13, 44.17, 44.18, 44.19, 44.24, 44.25, 44.28 |
342
+ | `ClaudeCodeConfig` | `{ model?: string; fallbackModel?: string; systemPrompt?: string; appendSystemPrompt?: string; effort?: 'low' \| 'medium' \| 'high' \| 'xhigh' \| 'max'; maxBudgetUsd?: number; betas?: string[]; disallowedTools?: string[]; tools?: string[] }` | The Claude Code options a site may set for a turn, and the built-in tools it asks the visitor for. | 44.16, 44.30 |
343
+ | `claudeCode.bridgeCommand` | `() => { command: string; expires: number }` | The command that pairs a bridge with this browser. | 44.14 |
344
+ | `claudeCode.bridge` | `() => AsyncGenerator<BridgeStatus>` | This browser's pairing, as it changes. | 44.20, 44.22 |
345
+ | `BridgeStatus` | `{ paired: boolean; command: string \| undefined }` | Whether a bridge is paired, and the command that pairs one where none is. | 44.20 |
346
+ | `/__abide/bridge` | `GET`, `POST` | Where a bridge pairs, and is relayed its turns. | 36.7, 44.14, 44.15, 44.19, 44.23, 44.26, 44.27, 16.78 |
347
347
 
348
348
  # Ambient values
349
349
 
@@ -466,7 +466,6 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
466
466
  | `abide:health` | `warning` | An `onHealth` that threw or answered a non-object. | 25.2, 28.8 |
467
467
  | `abide:mcp` | `debug` `warning` | A handler withheld from MCP, or a tool published with no description. | 20.3, 20.9, 28.8 |
468
468
  | `abide:openapi` | `warning` | A handler whose schema had no JSON Schema export. | 20.11, 28.8 |
469
- | `abide:config` | `warning` | A second `onConfig` replacing the first. | 27.6, 28.8 |
470
469
  | `abide:render` | `warning` | An `error.abide` that itself failed. | 28.8 |
471
470
  | `abide:refuse` | `warning` | A message formatter that threw. | 15.13, 28.8 |
472
471
  | `abide:reactive` | `warning` | A failed revalidation over a value still being served. | 4.12, 28.8 |
@@ -500,7 +499,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
500
499
 
501
500
  | Name | Signature | Meaning | Rules |
502
501
  | --- | --- | --- | --- |
503
- | `navigate` | `(url?: URL \| string, options?: { replace?: boolean, keepScroll?: boolean }) => Promise<void>` | Navigates. | 30.7, 30.9, 30.10, 30.11, 30.12, 30.13, 30.14, 30.15, 30.16, 30.17, 30.18, 30.20, 30.21, 30.22, 30.23, 30.24, 30.25, 30.27 |
502
+ | `navigate` | `(url?: URL \| string, options?: { replace?: boolean, keepScroll?: boolean }) => Promise<void>` | Navigates. | 30.7, 30.9, 30.10, 30.11, 30.12, 30.13, 30.14, 30.15, 30.16, 30.17, 30.18, 30.20, 30.21, 30.22, 30.23, 30.24, 30.25, 30.27, 30.29, 30.30, 30.31 |
504
503
 
505
504
  # `.abide` files
506
505
 
@@ -532,13 +531,13 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
532
531
  | `name={expr}` | A reactive attribute or property. | 32.3, 32.20, 32.23, 32.37 |
533
532
  | `on<event>={fn}` | A native listener on an element, and an ordinary prop on a component. | 32.4 |
534
533
  | `name="…{expr}…"` | An interpolated quoted value. | 32.3 |
535
- | `bind:value` | Two-way bind on an element. | 5.16, 32.5 |
534
+ | `bind:value` | Two-way bind on an element. | 5.16, 32.5, 32.58, 32.59, 32.60, 32.61, 32.62 |
536
535
  | `bind:prop={state}` | Hands a component prop the caller's `Reactive`, or a `{get, set}` pair, to write through. | 32.6, 33.6, 33.8, 14.18 |
537
536
  | `bind:checked` | A boolean bind on an input. | 32.7 |
538
537
  | `bind:open` | The same, on a `<details>`. | 32.7 |
539
538
  | `bind:group` | Radio or checkbox membership. | 32.8 |
540
539
  | `bind:value={{get, set}}` | Two-way bind over an explicit accessor pair, on an element or a component. | 32.5, 32.6 |
541
- | `bind:element={Reactive<Element> \| ((element: Element) => void \| Disposer)}` | A node reference. | 32.9, 32.38 |
540
+ | `bind:element={Reactive<Element> \| ((element: Element) => void \| Disposer)}` | A node reference. | 32.9, 32.38, 32.66, 32.68, 32.69 |
542
541
  | `class:name={cond}` | Toggles a class. | 32.10 |
543
542
  | `style:prop={value}` | Sets one style property. | 32.10, 32.19 |
544
543
  | `{...expr}` | Spreads props or attributes. | 32.11, 32.37, 33.12, 33.13 |
@@ -555,15 +554,15 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
555
554
 
556
555
  | Spelling | Branches | Meaning | Rules |
557
556
  | --- | --- | --- | --- |
558
- | `{#if cond}` | `{:else if cond}`, `{:else}` | Conditional markup. | 32.12, 32.21, 41.24 |
559
- | `{#for item, index of list by key}` | — | Repeats markup over a list, or over a cursor as it produces. | 32.13, 32.14, 32.15, 32.22, 32.29, 32.32, 32.33, 41.8, 41.14, 41.15 |
560
- | `{#try}` | `{:catch e}`, `{:finally}` | A render-time boundary and a region. | 32.16, 32.56 |
557
+ | `{#if cond}` | `{:else if cond}`, `{:else}` | Conditional markup. | 32.12, 32.21, 32.63, 32.64, 32.65, 41.24 |
558
+ | `{#for item, index of list by key}` | — | Repeats markup over a list, or over a cursor as it produces. | 32.13, 32.14, 32.15, 32.22, 32.29, 32.32, 32.33, 32.63, 32.64, 32.67, 41.8, 41.14, 41.15 |
559
+ | `{#try}` | `{:catch e}`, `{:finally}` | A render-time boundary and a region. | 32.16, 32.56, 32.63, 32.64 |
561
560
 
562
561
  ### Components
563
562
 
564
563
  | Spelling | Meaning | Rules |
565
564
  | --- | --- | --- |
566
- | `{#component Name(pattern)}` | An inline component. | 32.39, 32.40, 32.41, 32.42, 32.43, 32.44, 32.45, 32.46, 32.47, 32.48, 32.49, 32.50, 32.51, 32.52, 32.53, 32.54, 32.55 |
565
+ | `{#component Name(pattern)}` | An inline component. | 32.39, 32.40, 32.41, 32.42, 32.43, 32.44, 32.45, 32.46, 32.47, 32.48, 32.49, 32.50, 32.51, 32.52, 32.53, 32.54, 32.55, 32.63, 32.64 |
567
566
  | `<Name/>` | A component invocation. | 32.17, 32.34, 32.35, 32.36, 33.19, 33.21, 41.24, 41.28 |
568
567
  | `<slot/>` | The position children render into. | 32.18 |
569
568
  | `<slot>fallback</slot>` | Renders children, or a fallback. | 32.18 |
@@ -594,8 +593,8 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
594
593
 
595
594
  | Path | Meaning | Rules |
596
595
  | --- | --- | --- |
597
- | `<head>` | A page's, layout's or component's contribution to the document head. | 39.1, 39.2, 39.3, 39.4, 39.5, 39.6, 39.11, 39.12, 39.13, 39.14, 39.15, 39.16 |
598
- | `view-transition-name` | What turns view transitions on, in the app's own stylesheet. | 39.7, 39.8, 39.9, 39.10 |
596
+ | `<head>` | A page's, layout's or component's contribution to the document head. | 39.1, 39.2, 39.3, 39.4, 39.5, 39.6, 39.11, 39.12, 39.13, 39.14, 39.15, 39.16, 39.17 |
597
+ | `view-transition-name` | What turns view transitions on, in the app's own stylesheet. | 39.7, 39.8, 39.9, 39.10, 39.18, 39.19 |
599
598
  | `[name]` | A required dynamic segment. | 16.65, 23.5, 30.3 |
600
599
  | `[[name]]` | An optional segment. | 23.5, 30.4 |
601
600
  | `[...name]` | A rest segment, terminal. | 23.5, 30.5 |
@@ -634,7 +633,10 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
634
633
  | `ABIDE_APP_TOKEN` | `string \| null` | The bearer the remote CLI sends. | 36.6 |
635
634
  | `ABIDE_APP_URL` | `string \| null` | Names an app somewhere else, for the remote CLI. | 36.6 |
636
635
  | `ABIDE_RPC_TIMEOUT` | `number` | The default ms a call may go without progress. | 16.22, 16.56, 16.58 |
637
- | `ABIDE_MAX_REQUEST_BODY_SIZE` | `number` | The default ceiling on a mutation's body. | 16.13, 16.54, 16.55 |
636
+ | `ABIDE_MAX_REQUEST_BODY_SIZE` | `number` | The default ceiling on a mutation's body. | 16.13, 16.54, 16.55, 16.78 |
637
+ | `ABIDE_MAX_SOCKET_MESSAGE_SIZE` | `number` | The ceiling on a frame received over the socket mux, in bytes. | 18.28 |
638
+ | `ABIDE_MAX_SOCKET_SUBSCRIPTIONS` | `number` | How many values one connection to the socket mux may follow. | 18.31, 18.32 |
639
+ | `ABIDE_STOP_TIMEOUT` | `number` | How long `App.stop` drains, in ms. | 42.17, 42.18 |
638
640
  | `ABIDE_MAX_STREAM_BUFFER_SIZE` | `number` | The cap on a stream transcript held in memory. | 36.18 |
639
641
  | `ABIDE_MAX_LOG_BUFFER_COUNT` | `number` | How many lines `log.records` is trimmed back to. | 28.1 |
640
642
  | `ABIDE_LOG_FORMAT` | `'tsv' \| 'json' \| null` | The machine log format. | 36.8 |
@@ -661,7 +663,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
661
663
  | `**/layout.abide` | A layout, rendering every page below its route through a slot, and the hooks over that route. | 35.1, 35.12, 35.13, 37.2, 41.11, 41.19, 41.23 |
662
664
  | `src/launcher.abide` | The desktop launcher, beside the root layout: the `onLauncher` it calls, and its page in place of the launcher's own where it has markup. | 38.80, 38.82, 38.84, 38.85, 38.107 |
663
665
  | `src/layout.abide` | The root layout, whose `<script server>` is the app's own, and whose `<html>` is the document its pages are served in. | 35.15, 35.16, 35.17, 37.1, 37.3, 39.4 |
664
- | `**/error.abide` | The page a refusal renders in. | 35.2, 35.3, 35.14 |
666
+ | `**/error.abide` | The page a refusal renders in. | 35.2, 35.3, 35.14, 35.18 |
665
667
  | `**/node_modules/**/*.abide` | A component a dependency ships as source, compiled with the app that imports it. | 34.23, 34.24, 34.25, 34.26, 34.27, 38.99 |
666
668
  | `import '…' with { type: 'routes' }` | A package's directory mounted at the importing file's directory, its pages, layouts, error pages and handler files answering below it. | 23.19, 23.20 |
667
669
  | `bunfig.toml` | The file whose `[serve.static] plugins` the client is built with. | 38.43 |
@@ -702,17 +704,126 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
702
704
  | `App` | `interface App` | What a host, a test or a script holds the app as. | 42.12, 42.13 |
703
705
  | `App.fetch` | `(request: Request) => Promise<Response>` | The whole request path, entered in process. | 42.5, 42.6 |
704
706
  | `App.run` | `<Result>(fn: () => Result \| Promise<Result>, options?: { request?: Request }) => Promise<Result>` | One scope around a function, and nothing else. | 42.7, 42.8, 11.38 |
705
- | `App.listen` | `(options?: { port?: number; hostname?: string; unix?: string }) => Promise<void>` | The socket, bound. | 42.9 |
706
- | `App.stop` | `() => Promise<void>` | The drain and the teardown. | 42.10, 42.11, 42.15, 37.4 |
707
+ | `App.listen` | `(options?: { port?: number; hostname?: string; unix?: string }) => Promise<void>` | The socket, bound. | 42.9, 18.28, 18.29, 18.30 |
708
+ | `App.stop` | `() => Promise<void>` | The drain and the teardown. | 42.10, 42.11, 42.15, 42.17, 37.4 |
707
709
  | `App[Symbol.asyncDispose]` | `() => Promise<void>` | `App.stop`, under the name `await using` calls. | 42.14 |
708
710
 
711
+ # Tests
712
+
713
+ | Name | Signature | Meaning | Rules |
714
+ | --- | --- | --- | --- |
715
+ | `mount` | `(component: Component) => Promise<Mounted>` | A bound component, rendered into the document's body and hydrated there. | 38.119 |
716
+ | `Mounted` | `{ stop(): void; [Symbol.dispose](): void }` | A component `mount` hydrated, until it is stopped. | 38.119 |
717
+
718
+ # The measurement harness
719
+
720
+ `@abide/harness`, a package of its own: three lanes, the report and gate vocabulary they share, the
721
+ rows a runner prints, and an adapter for an abide app.
722
+
723
+ ## `@abide/harness/measure` — DOM calls, counted from inside the page
724
+
725
+ | Name | Signature | Meaning | Rules |
726
+ | --- | --- | --- | --- |
727
+ | `measure` | `(body: () => void) => Work` | The work `body` did to the DOM, and what every published source counted while it ran. | 43.1, 43.3, 43.4, 43.6, 43.15, 43.19 |
728
+ | `Work` | `{ elementsMoved: number; markersMoved: number; textMoved: number; nodesCreated: number; attributesSet: number; classWrites: number; styleWrites: number; dataWrites: number; redundantDataWrites: number; listenersBound: number; published: PublishedWork }` | What an op did, one count per row. | 43.2, 43.6, 43.19 |
729
+ | `publishedBy` | `<Field extends string>(work: Work, source: string, fields: readonly Field[]) => Record<Field, number> \| null` | One source's counters off a `Work`. | 43.19, 43.20 |
730
+ | `PublishedWork` | `Record<string, Record<string, number>>` | The records sources publish, by source name. | 43.21 |
731
+ | `PUBLISHED_WORK_KEY` | `'__HARNESS_WORK__'` | The global a source publishes its record under. | 43.21 |
732
+ | `time` | `(spec: Omit<BatchSpec, 'emulated'>) => Batched` | `batch`, with whether the DOM is emulated filled in. | 43.6, 43.22 |
733
+ | `patchSet` | `() => string[]` | Every DOM member the lane counts, as `Interface.member`. | 43.5 |
734
+
735
+ ## `@abide/harness/preload` — a DOM for `bun test`
736
+
737
+ | Name | Meaning | Rules |
738
+ | --- | --- | --- |
739
+ | `preload = ["@abide/harness/preload"]` | The `[test]` preload that registers a DOM with the measure lane's patches installed. | 43.4, 43.28 |
740
+
741
+ ## `@abide/harness/engine` — what the browser engine did
742
+
743
+ | Name | Signature | Meaning | Rules |
744
+ | --- | --- | --- | --- |
745
+ | `traceOp` | `(page: Page, op: string, body: () => Promise<void>) => Promise<Trace>` | One op in a Chromium page, traced. | 43.1 |
746
+ | `Trace` | `{ op: string; measures: { name: string; startNanoseconds: number; endNanoseconds: number }[]; counters: { recalcStyle: number; layout: number; paint: number; forcedLayout: number }; layers: { style: number; layout: number; script: number } }` | What the engine did over one traced op, and the user timing emitted inside it. | 43.13 |
747
+ | `shares` | `(trace: Trace, spec: SharesSpec) => Shares` | The fraction of an op one layer took. | 43.13, 43.14 |
748
+ | `SharesSpec` | `{ layer: string; of: string; threshold: 'frame' \| 'interaction' }` | The layer, the op it is a share of, and the threshold it is judged against. | 43.13 |
749
+ | `Shares` | `{ op: string; layer: string; layerNanoseconds: number; opNanoseconds: number; ceilingCpu: number; ceilingObserved: number; threshold: 'frame' \| 'interaction'; underOneFrame: boolean; overTheThreshold: boolean }` | What `shares` hands back. | 43.14 |
750
+ | `measuresNamed` | `(trace: Trace, name: string) => { startNanoseconds: number; endNanoseconds: number }[]` | Every user-timing measure in a trace with one name. | — |
751
+ | `durationOf` | `(trace: Trace, name: string) => number` | The summed duration of the measures in a trace with one name. | — |
752
+
753
+ ## `@abide/harness/server` — bytes, turns and allocations
754
+
755
+ | Name | Signature | Meaning | Rules |
756
+ | --- | --- | --- | --- |
757
+ | `bytes` | `(of: unknown) => Promise<number>` | A payload's byte length, or the engine's shallow estimate of any other value. | 43.1 |
758
+ | `ticks` | `(body: () => unknown) => Promise<number>` | The microtask turns `body`'s result took to settle. | 43.1 |
759
+ | `retained` | `(body: () => unknown) => Record<string, number>` | The live objects `body` left behind, by type. | 43.16 |
760
+ | `allocated` | `(body: () => unknown, reps?: number) => { tagged: 'batch mean'; reps: number; perRep: Record<string, number>; growth: Record<string, number> }` | The objects `body` allocates per run, as a batch mean. | 43.17 |
761
+ | `closures` | `(body: () => unknown) => { 'Function': number; 'JSLexicalEnvironment': number }` | The functions and scopes `body` left behind. | 43.16 |
762
+
763
+ ## `@abide/harness/report` — samples, ratios and the clock
764
+
765
+ | Name | Signature | Meaning | Rules |
766
+ | --- | --- | --- | --- |
767
+ | `batch` | `(spec: BatchSpec) => Batched` | Each arm timed in interleaved batches, with an A/A floor. | 43.7, 43.8, 43.11, 43.12, 43.18, 43.22 |
768
+ | `BatchSpec` | `{ case: string; arms: Record<string, () => void>; emulated: 'dom' \| null; work?: Readonly<Record<string, Readonly<Record<string, number>>>>; n?: number; reps?: number; warmup?: number; budgetNanoseconds?: number }` | The arms `batch` interleaves, and the options that pin or bound the run. | 43.8, 43.22 |
769
+ | `Batched` | `{ case: string; n: number; reps: number; warmup: number; substrate: 'jsc' \| 'v8' \| 'spidermonkey' \| 'unknown'; floor: number; samples: Record<string, Sample> }` | What `batch` hands back. | 43.7 |
770
+ | `Sample` | `{ arm: string; case: string; n: number; reps: number; warmup: number; substrate: 'jsc' \| 'v8' \| 'spidermonkey' \| 'unknown'; emulated: 'dom' \| null; nanoseconds: number; p95: number; underOneFrame: boolean; work: Readonly<Record<string, number>> \| null }` | One arm's timing. | 43.11, 43.12 |
771
+ | `ratio` | `(numerator: Sample, denominator: Sample, spec: RatioSpec) => Ratio` | One arm's time over another's, as the floor allows it to be said. | 43.7, 43.10 |
772
+ | `RatioSpec` | `{ floor: number; workFactor?: number }` | The floor and the work tolerance a `ratio` is judged against. | 43.7 |
773
+ | `Ratio` | `{ kind: 'ratio'; case: string; numerator: string; denominator: string; value: number; workDisagreement: string[] } \| { kind: 'underOneFrame'; case: string; numerator: string; denominator: string; workDisagreement: string[] } \| { kind: 'underTheFloor'; case: string; numerator: string; denominator: string; floor: number; workDisagreement: string[] }` | What `ratio` hands back. | 43.7 |
774
+ | `claimOf` | `(result: Ratio) => string` | A `Ratio` as the one line a report prints. | 43.7 |
775
+ | `emulatedDom` | `() => 'dom' \| null` | Whether the DOM in this process is emulated. | 43.22 |
776
+ | `THRESHOLDS` | `{ readonly frame: 16700000; readonly interaction: 100000000 }` | Where a cost crosses perception, in nanoseconds. | 43.14 |
777
+
778
+ ## `@abide/harness/gate` — tests whose revert can be run
779
+
780
+ | Name | Signature | Meaning | Rules |
781
+ | --- | --- | --- | --- |
782
+ | `gate` | `(name: string, spec: GateSpec, body: () => void \| Promise<void>) => void` | A test with a runnable revert. | 43.9 |
783
+ | `GateSpec` | `{ revert: () => unknown; worth: Record<string, number \| string> }` | A gate's revert and what the broken arm reports. | 43.9 |
784
+ | `spawned` | `(command: string[], options?: { cwd?: string; env?: Record<string, string \| undefined>; deadline?: number }) => Promise<{ exitCode: number; stdout: string; stderr: string }>` | A child process run to its end, awaited. | 43.24 |
785
+ | `inAFreshProcess` | `(source: string, workerId?: string, timeoutMilliseconds?: number) => Promise<{ ok: boolean; output: string }>` | Source run in a child bun with no DOM preloaded. | 43.25 |
786
+
787
+ ## `@abide/harness/rows` — what a runner prints
788
+
789
+ | Name | Signature | Meaning | Rules |
790
+ | --- | --- | --- | --- |
791
+ | `RowItem` | `{ id: string; section: string; name: string }` | One entry a runner offers to run. | 43.26 |
792
+ | `RowState` | `'queued' \| 'running' \| 'done' \| 'broken'` | Where a row is in its run. | 43.27 |
793
+ | `BenchRow` | `{ id: string; state: RowState; unit: string; subject: number \| null; handWritten: number \| null; claim: string; n: number \| null; floor: number \| null; note: string }` | One bench's result against its hand-written arm. | 43.26 |
794
+ | `TestRow` | `{ id: string; state: RowState; tests: number; failures: number; skipped: number; milliseconds: number; cases: TestCase[]; note: string }` | One test file's result. | 43.27 |
795
+ | `TestCase` | `{ name: string; milliseconds: number; skipped: boolean; failure: string \| null }` | One test in a file, as its report gave it. | 43.27 |
796
+ | `RunnerFailure` | `{ failure: string }` | The line written after a runner died part way. | — |
797
+ | `isRunnerFailure` | `(line: object) => line is RunnerFailure` | Whether a line is a `RunnerFailure`. | — |
798
+ | `inScope` | `(item: RowItem, scope: string) => boolean` | Whether an item falls in a `--scope`. | 43.26 |
799
+ | `matchesFilter` | `(item: RowItem, filter: string) => boolean` | Whether an item matches a `--filter`. | 43.26 |
800
+
801
+ ## `@abide/harness/runner` — runners that print rows
802
+
803
+ | Name | Signature | Meaning | Rules |
804
+ | --- | --- | --- | --- |
805
+ | `rowsCommand` | `<Item extends RowItem, Row>(items: Item[], run: (selected: Item[], emit: (row: Row) => void) => Promise<void>) => Promise<void>` | A runner's command line: its catalog under `--list`, else the rows of what it selects. | 43.26 |
806
+ | `testRows` | `(suites: TestSuite[], cwd: string) => Promise<void>` | `rowsCommand` over the test files of `suites`, one `TestRow` per file. | 43.27 |
807
+ | `TestSuite` | `{ section: string; root: string; runner: 'bun'; pattern?: string } \| { section: string; root: string; runner: 'playwright'; projects: string[]; pattern?: string }` | One directory of test files, listed under one section. | 43.27 |
808
+
809
+ ## `@abide/harness/abide` — the adapter for an abide app
810
+
811
+ | Name | Signature | Meaning | Rules |
812
+ | --- | --- | --- | --- |
813
+ | `measure` | `(body: () => void) => AbideWork` | The work an op did to the DOM, with abide's own counters beside it. | 43.1, 43.19, 43.20, 43.23 |
814
+ | `AbideWork` | `Work & { wakes: number \| null; bindingRuns: number \| null; descents: number \| null }` | What `measure` hands back. | 43.23 |
815
+
816
+ | Name | Meaning | Rules |
817
+ | --- | --- | --- |
818
+ | `preload = ["@abide/harness/abide/preload"]` | The `[test]` preload that registers a DOM a test mounts into and measures, then abide's own. | 38.109, 38.119 |
819
+
709
820
  # CLI
710
821
 
711
822
  ## Commands
712
823
 
713
824
  | Command | Meaning | Rules |
714
825
  | --- | --- | --- |
715
- | `abide scaffold <name>` | Writes a starter project. | 38.1, 38.42, 38.103, 38.104, 38.106 |
826
+ | `abide scaffold <name>` | Writes a starter project. | 38.1, 38.42, 38.103, 38.104, 38.106, 38.112 |
716
827
  | `abide run <file> [args…]` | Runs a script under the abide runtime. | 38.2, 38.57 |
717
828
  | `abide check [dir…]` | The type-checker over a `.abide` tree. | 38.3, 38.31, 38.32, 38.33, 38.40, 38.105 |
718
829
  | `abide routes [--json]` | Every address the app answers, the file answering it, and where each hook is filed. | 38.108 |
@@ -726,5 +837,6 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
726
837
  | `abide watch <address> [args]` | A live subscription to one of the app's followed `GET`s. | 38.69 |
727
838
  | `abide compile [--target] [--out] [--platforms]` | One standalone executable. | 37.8, 37.10, 37.11, 37.12, 38.10, 38.25, 38.26, 38.44, 38.45, 38.46, 38.47, 38.48, 38.49, 38.50, 38.51, 38.52, 38.56, 38.93, 38.97 |
728
839
  | `abide bundle [--out]` | A desktop app for the host platform, with a window on the app. | 38.10, 38.25, 38.26, 38.70, 38.71, 38.72, 38.73, 38.74, 38.75, 38.76, 38.77, 38.78, 38.79, 38.80, 38.81, 38.82, 38.83, 38.84, 38.85, 38.86, 38.87, 38.88, 38.89, 38.90, 38.91, 38.92, 38.101, 38.102 |
729
- | `abide lsp` | The `.abide` language server, over stdio. | 38.11, 38.34, 38.39, 38.98 |
730
- | `--help` · `-h` · `abide help` | Usage, generated from the command list. | 38.12 |
840
+ | `abide lsp` | The `.abide` language server, over stdio. | 38.11, 38.34, 38.39, 38.98, 38.116, 38.117, 38.118 |
841
+ | `--help` · `-h` · `abide help` | Usage, generated from the command list. | 38.12, 38.113, 38.114 |
842
+ | `--version` · `-v` | The version of abide, or of the app a compiled executable holds. | 38.115 |