@abide/abide 0.114.1 → 0.116.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 (45) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/docs/REGISTRY.md +38 -16
  3. package/docs/RULEBOOK.md +173 -12
  4. package/docs/abide.md +266 -24
  5. package/package.json +4 -2
  6. package/src/compiler/REACTIVE_MEMBERS.ts +1 -0
  7. package/src/compiler/classifyTemplate.ts +21 -14
  8. package/src/compiler/emitClient.ts +5 -1
  9. package/src/server/BRIDGE_VERSION.ts +1 -1
  10. package/src/server/IMAGE_TYPES.ts +10 -0
  11. package/src/server/OFFLINE_STALE.ts +10 -0
  12. package/src/server/answerRequest.ts +39 -10
  13. package/src/server/answerRpc.ts +25 -5
  14. package/src/server/answerWorker.ts +173 -28
  15. package/src/server/assistant.ts +81 -12
  16. package/src/server/claudeApi.ts +25 -20
  17. package/src/server/claudeCode.ts +30 -10
  18. package/src/server/createApp.ts +8 -2
  19. package/src/server/generatedSurfaces.ts +48 -14
  20. package/src/server/index.ts +2 -0
  21. package/src/server/render.ts +3 -2
  22. package/src/server/seedBuffer.ts +28 -9
  23. package/src/shared/KEEP_HEADER.ts +7 -0
  24. package/src/shared/KEEP_OPENED.ts +7 -0
  25. package/src/shared/KEPT_HEADER.ts +6 -0
  26. package/src/shared/LOAD_OFFLINE.ts +15 -0
  27. package/src/shared/OUTBOX_SENDING.ts +9 -0
  28. package/src/shared/REACHED_MESSAGE.ts +7 -0
  29. package/src/shared/answerOf.ts +14 -2
  30. package/src/shared/framing.ts +10 -2
  31. package/src/shared/index.ts +2 -0
  32. package/src/shared/outbox.ts +116 -0
  33. package/src/shared/reactive/Keyed.ts +9 -0
  34. package/src/shared/reactive/Reactive.ts +197 -3
  35. package/src/shared/reactive/SELECTION_MEMBERS.ts +1 -0
  36. package/src/shared/reactive/callLoad.ts +8 -0
  37. package/src/shared/reactive/index.ts +23 -1
  38. package/src/shared/rpc.ts +25 -5
  39. package/src/shared/rpcRequest.ts +12 -8
  40. package/src/ui/Router.ts +28 -7
  41. package/src/ui/bindFiles.ts +42 -0
  42. package/src/ui/index.ts +1 -0
  43. package/src/ui/outboxQueue.ts +427 -0
  44. package/src/ui/socketWire.ts +31 -2
  45. package/src/ui/wire.ts +111 -10
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @abide/abide
2
2
 
3
+ ## 0.116.0
4
+
5
+ ### Minor Changes
6
+
7
+ - c1c3924: `bind:files` — a file input's files as an array, one or many: a pick writes them, and an array written to it (one dropped, or `[]` to clear) is what the input holds. A `bind:` an element has no property for, such as `bind:file` or `bind:vlaue`, is now a build error where it compiled to an attribute that bound nothing. RULEBOOK 32.73, 32.74
8
+ - 48cfc3e: tools answer images — a handler whose answer is a png, jpeg, gif or webp body reaches a model as the image: `/__abide/mcp` carries it as an `image` content item (so Claude Code, through a bridge, sees it), and a `Toolbox` call answers a `ToolAnswer` of `content` — the same pieces, text or image — which `claudeApi` shows the model as the call's result. Any other image type, an svg, is answered as its text. Breaking for a hand-written `Provider`: a `ToolAnswer` is `{ content, failed }` where it was `{ text, failed }`. RULEBOOK 20.21, 44.35
9
+ - 48cfc3e: multimodal transcripts — an `AssistantMessage`'s `content` is its words or its pieces in order, each `AssistantContent` text or an image as base64 with its type. `assistant` refuses an image a model can't take or one outside a user turn, and hands a `Provider` every turn as its pieces, words alone as one text piece. `claudeApi` shows each image to the model as an image block in its place, and `claudeCode` sends the one message Claude Code is sent as Messages API blocks, each image in its place. RULEBOOK 44.5, 44.36, 44.37, 44.38, 44.39
10
+
11
+ ### Patch Changes
12
+
13
+ - 0a78343: a page reached inside the app reloads offline — the router tells the offline worker each address a navigation reaches, and the worker keeps its page where no saved copy would stand in ([`a3975be`](https://github.com/briancray/abide/commit/a3975be224584df096fe799c6291c0dfcd80cf84))
14
+
15
+ ## 0.115.0
16
+
17
+ ### Minor Changes
18
+
19
+ - bdecaf9: offline-first calls — a kept copy stands in for the network, `synced` says whether what is held is what the producer made, a call's `offline` settles it where no answer came, and `outbox` holds the calls a browser queued until they deliver ([`28322d1`](https://github.com/briancray/abide/commit/28322d10f53305c5a3dd17823a9541f877e131eb))
20
+
3
21
  ## 0.114.1
4
22
 
5
23
  ### Patch Changes
package/docs/REGISTRY.md CHANGED
@@ -128,6 +128,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
128
128
  | `s.busy` | `() => boolean` | Work is in flight for this value. | 3.1, 3.2, 3.3, 3.4, 3.7, 3.9, 5.15, 7.5, 7.8, 11.19, 11.20, 11.21, 11.22, 11.50 |
129
129
  | `s.ready` | `() => boolean` | There is a value to serve. | 3.1, 3.2, 3.3, 3.5, 3.6, 3.8, 7.8, 11.20, 11.54, 11.58 |
130
130
  | `s.error` | `() => Failures \| undefined` | The standing refusal. | 3.1, 3.2, 3.3, 3.5, 4.10, 4.11, 4.12 |
131
+ | `s.synced` | `() => boolean` | What is held is what the producer made. | 3.1, 3.2, 3.3, 3.10, 3.11, 3.12, 3.13 |
131
132
  | `s.isError` | `<Name extends Failures['name']>(error: unknown, name: Name) => error is Extract<Failures, { name: Name }>` | Whether a caught failure is the one named. | 3.1 |
132
133
 
133
134
  ### Triggers
@@ -149,6 +150,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
149
150
  | --- | --- | --- | --- |
150
151
  | `k.busy` | `(pattern?: Partial<Args>) => boolean` | `busy` narrowed to matching entries. | 13.2, 13.3, 13.12 |
151
152
  | `k.ready` | `(pattern?: Partial<Args>) => boolean` | `ready` narrowed to matching entries. | 13.11, 13.12 |
153
+ | `k.synced` | `(pattern?: Partial<Args>) => boolean` | `synced` narrowed to matching entries. | 13.12, 13.18 |
152
154
  | `k.invalidate` | `(pattern?: Partial<Args>) => void` | `invalidate` narrowed to matching entries. | 7.2, 7.3, 13.4, 13.12 |
153
155
  | `k.refresh` | `(pattern?: Partial<Args>) => void` | `refresh` narrowed to matching entries. | 7.4, 7.5, 13.4, 13.12 |
154
156
 
@@ -167,8 +169,9 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
167
169
  | Name | Signature | Meaning | Rules |
168
170
  | --- | --- | --- | --- |
169
171
  | `Selection` | `Reactive<any, any, any, any> \| Keyed<any, any, any, any> \| Rpc<any, any, any, any> \| { tags: string[] }` | What is being asked about. | 13.1, 13.8, 13.9, 13.13, 31.14 |
170
- | `busy` | `(selection?: Selection, pattern?: Partial<Args>) => boolean` | Whether any entry in the selection has work in flight. | 13.2, 13.3, 13.5, 13.12 |
172
+ | `busy` | `(selection?: Selection, pattern?: Partial<Args>) => boolean` | Whether any entry in the selection has work in flight. | 13.2, 13.3, 13.5, 13.12, 46.11 |
171
173
  | `ready` | `(selection?: Selection, pattern?: Partial<Args>) => boolean` | Whether every entry in the selection has a value to serve. | 13.3, 13.5, 13.11, 13.12 |
174
+ | `synced` | `(selection?: Selection, pattern?: Partial<Args>) => boolean` | Whether every entry in the selection holds what its producer made. | 13.12, 13.18 |
172
175
  | `refresh` | `(selection?: Selection, pattern?: Partial<Args>) => void` | Stale-while-revalidate over the selection. | 13.4, 13.7, 13.10, 13.12, 13.14, 13.15, 13.16, 13.17 |
173
176
  | `invalidate` | `(selection?: Selection, pattern?: Partial<Args>) => void` | Marks the selection stale. | 13.4, 13.6, 13.10, 13.12, 13.15, 13.17 |
174
177
 
@@ -209,7 +212,8 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
209
212
 
210
213
  | Name | Signature | Meaning | Rules |
211
214
  | --- | --- | --- | --- |
212
- | `Rpc` | `<Value, Args, Failures, Produced = Value>(args?: Args, options?: { signal?: AbortSignal }) => Reactive<Value, Value, Failures \| Failed<'HttpError', undefined>, Produced>` | A handler reached over http, or in process on a server. | 16.3, 16.4, 16.8, 16.11, 16.25, 16.26, 16.27, 16.28, 16.29, 16.33, 16.43, 16.48, 16.49, 16.62, 41.5, 41.7, 41.12, 41.21, 41.22, 41.25, 41.26, 41.27, 16.79 |
215
+ | `Rpc` | `<Value, Args, Failures, Produced = Value>(args?: Args, options?: { signal?: AbortSignal; offline?: (args: Args) => Value }) => Reactive<Value, Value, Failures \| Failed<'HttpError', undefined>, Produced>` | A handler reached over http, or in process on a server. | 16.3, 16.4, 16.8, 16.11, 16.25, 16.26, 16.27, 16.28, 16.29, 16.33, 16.43, 16.48, 16.49, 16.62, 41.5, 41.7, 41.12, 41.21, 41.22, 41.25, 41.26, 41.27, 16.79, 16.80, 16.85 |
216
+ | `offline` | `(args: Args) => Value` | What a call settles to where a browser could not get its answer. | 16.81, 16.82, 16.83, 16.84 |
213
217
  | `Value` | `unknown` | What the addressed `Reactive` holds. | 16.3, 16.30 |
214
218
  | `Produced` | `unknown` | What it yields, per the unit rule. | 1.2, 16.3 |
215
219
  | `Args` | `Record<string, JsonValue> \| undefined` | The arguments, in the body on a mutation and in the URL on a `GET` or `DELETE`. | 11.24, 16.18, 19.15 |
@@ -218,8 +222,9 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
218
222
  | `GET` | `<Value, Args, Failures, RungFailures, Produced = Value>(handler: (args?: Args) => Value, options?: RpcOptions<Args, RungFailures> & ReactiveOptions<Value, Value, Failures, Args>) => Rpc<Value, Args, Failures \| RungFailures, Produced>` | The plain-function overload, whose options build the value it becomes beside its address. | 16.1, 16.6, 16.7 |
219
223
  | `GET` | `<Value, Failures, RungFailures>(handler: Value \| PromiseLike<Value>, options?: RpcOptions<undefined, RungFailures> & ReactiveOptions<Value, Value, Failures>) => Rpc<Value, undefined, Failures \| RungFailures>` | The value overload, for what the handler owns outright. | 16.1, 16.6, 16.7 |
220
224
  | `POST` / `PUT` / `PATCH` / `DELETE` | same as `GET` | Declares a mutation. | 16.2, 16.7, 16.32, 16.68, 16.76, 16.38, 16.53 |
221
- | `rpc.busy` | `(pattern?: Partial<Args>) => boolean` | `busy` narrowed to the handler's matching entries. | 13.2, 13.3, 13.12 |
225
+ | `rpc.busy` | `(pattern?: Partial<Args>) => boolean` | `busy` narrowed to the handler's matching entries. | 13.2, 13.3, 13.12, 46.11 |
222
226
  | `rpc.ready` | `(pattern?: Partial<Args>) => boolean` | `ready` narrowed to the handler's matching entries. | 13.11, 13.12 |
227
+ | `rpc.synced` | `(pattern?: Partial<Args>) => boolean` | `synced` narrowed to the handler's matching entries. | 13.12, 13.18 |
223
228
  | `rpc.invalidate` | `(pattern?: Partial<Args>) => void` | `invalidate` narrowed to the handler's matching entries. | 7.2, 7.3, 13.4, 13.12 |
224
229
  | `rpc.refresh` | `(pattern?: Partial<Args>) => void` | `refresh` narrowed to the handler's matching entries. | 7.4, 7.5, 13.4, 13.12 |
225
230
  | `rpc.isError` | `<Name extends Failures['name']>(error: unknown, name: Name) => error is Extract<Failures, { name: Name }>` | Whether a caught failure is the one named. | 16.8 |
@@ -286,6 +291,9 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
286
291
  | `cache-control: no-store` | `/__abide/openapi.json`, `/__abide/mcp` | Refuses caching for an answer about the app's surfaces. | 21.4 |
287
292
  | `cache-control: public, max-age=31536000, immutable` | the built bundle | A chunk addressed by its own content hash. | 21.5 |
288
293
  | `cache-control: public, max-age=0, must-revalidate` | a file under a `public` directory | A file at an address the build never chose. | 21.12 |
294
+ | `cache-control: private, stale-while-revalidate=604800, stale-if-error=604800` | a page, a `GET`'s answer, where `ABIDE_OFFLINE` registers its worker | The default that lets a copy kept on the device stand in for the network. | 21.22, 21.11, 21.17 |
295
+ | `cache-control: public, max-age=0, stale-while-revalidate=604800, stale-if-error=604800` | a file under a `public` directory, where `ABIDE_OFFLINE` registers its worker | The same, for a file at an address the build never chose. | 21.23 |
296
+ | `clear-site-data: "cache"` | an answer carrying `abide-principal`, where pages are kept offline | Drops what the browser's own cache kept for the caller signed in before. | 21.24 |
289
297
  | `etag` | a file under a `public` directory | The answered bytes, addressed by their content. | 21.13, 21.14 |
290
298
  | `referrer-policy: strict-origin-when-cross-origin` | a page | The browsers' own default, written down. | 21.6 |
291
299
  | `vary` | wherever an answer depends on a request header | Names the header the answer varied on. | 21.7 |
@@ -313,25 +321,27 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
313
321
 
314
322
  | Name | Signature | Meaning | Rules |
315
323
  | --- | --- | --- | --- |
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, 20.19, 20.20, 36.7, 16.78 |
324
+ | `/__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, 20.19, 20.20, 20.21, 20.22, 36.7, 16.78 |
317
325
 
318
326
  # Assistant
319
327
 
320
328
  | Name | Signature | Meaning | Rules |
321
329
  | --- | --- | --- | --- |
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 |
330
+ | `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, 44.36, 44.37, 44.39 |
331
+ | `AssistantMessage` | `{ role: 'user' \| 'assistant'; content: string \| AssistantContent[] }` | One turn of the transcript a caller sends: its words, or its pieces in order. | 44.3, 44.4 |
332
+ | `AssistantContent` | `{ type: 'text'; text: string } \| { type: 'image'; data: string; mimeType: string }` | A piece of a turn: words, or an image as base64 with its type. | 44.5, 44.35, 44.36, 44.37, 44.38, 44.39 |
324
333
  | `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.28 |
325
334
  | `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.28 |
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 |
335
+ | `Provider` | `{ run(messages: (AssistantMessage & { content: AssistantContent[] })[], tools: Toolbox, limits: { maxTurns: number; signal: AbortSignal }): AsyncGenerator<AssistantEvent> }` | What runs a conversation. | 44.2, 44.28, 44.39 |
336
+ | `Toolbox` | `{ list: ToolDefinition[]; call(name: string, input: unknown): Promise<ToolAnswer> }` | The app's MCP tools, called as the asker. | 44.6, 44.7, 44.35 |
337
+ | `ToolAnswer` | `{ content: AssistantContent[]; failed: boolean }` | What a tool call answered: its pieces, and whether it failed. | 20.21, 20.22, 44.35 |
328
338
  | `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, 20.19, 44.6 |
329
339
 
330
340
  ## `claudeApi`
331
341
 
332
342
  | Name | Signature | Meaning | Rules |
333
343
  | --- | --- | --- | --- |
334
- | `claudeApi` | `(config: ClaudeApiConfig) => Provider` | The Claude Messages API as a provider. | 44.9, 44.10, 44.12, 44.21 |
344
+ | `claudeApi` | `(config: ClaudeApiConfig) => Provider` | The Claude Messages API as a provider. | 44.9, 44.10, 44.12, 44.21, 44.35, 44.38 |
335
345
  | `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
346
 
337
347
  ## `claudeCode`
@@ -381,6 +391,17 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
381
391
  | --- | --- | --- | --- |
382
392
  | `online` | `Reactive<boolean>` | Whether the caller can reach the app. | 23.7, 23.8, 18.33, 18.34 |
383
393
 
394
+ ## `outbox`
395
+
396
+ | Name | Signature | Meaning | Rules |
397
+ | --- | --- | --- | --- |
398
+ | `outbox` | `<Chosen extends Selection>(selection?: Chosen, pattern?: Partial<Args>) => QueuedCall[]` | The calls a browser has queued and not delivered, the refused ones included. | 46.1, 46.2, 46.3, 46.4, 46.5, 46.6, 46.7, 46.8, 46.9, 46.10, 46.14, 46.15, 46.16, 46.17, 46.20, 18.35 |
399
+ | `rpc.outbox` | `(pattern?: Partial<Args>) => QueuedCall[]` | `outbox` narrowed to the handler's calls. | 46.1, 13.12 |
400
+ | `QueuedCall` | `{ id: string; method: 'GET' \| 'POST' \| 'PUT' \| 'PATCH' \| 'DELETE'; url: string; args: Record<string, JsonValue> \| undefined; queuedAt: number; attempts: number; value: Reactive<Value, Value, Failed<string, unknown>>; retry: () => void; discard: () => void }` | One call `outbox` holds. | 46.1, 46.10 |
401
+ | `call.value` | `Reactive<Value, Value, Failed<string, unknown>>` | The value a queued call settled. | 46.5, 46.7, 46.18, 46.19 |
402
+ | `call.retry` | `() => void` | Sends a queued call again. | 46.6, 46.12 |
403
+ | `call.discard` | `() => void` | Drops a queued call. | 46.13 |
404
+
384
405
  ## `reachable`
385
406
 
386
407
  | Name | Signature | Meaning | Rules |
@@ -500,7 +521,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
500
521
 
501
522
  | Name | Signature | Meaning | Rules |
502
523
  | --- | --- | --- | --- |
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, 30.29, 30.30, 30.31, 30.32, 45.12 |
524
+ | `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, 30.32, 45.12, 45.24 |
504
525
 
505
526
  # `.abide` files
506
527
 
@@ -532,11 +553,12 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
532
553
  | `name={expr}` | A reactive attribute or property. | 32.3, 32.20, 32.23, 32.37, 32.70, 32.71 |
533
554
  | `on<event>={fn}` | A native listener on an element, and an ordinary prop on a component. | 32.4 |
534
555
  | `name="…{expr}…"` | An interpolated quoted value. | 32.3, 32.71 |
535
- | `bind:value` | Two-way bind on an element. | 5.16, 32.5, 32.58, 32.59, 32.60, 32.61, 32.62, 32.72 |
556
+ | `bind:value` | Two-way bind on an element. | 5.16, 32.5, 32.58, 32.59, 32.60, 32.61, 32.62, 32.72, 32.74 |
536
557
  | `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
- | `bind:checked` | A boolean bind on an input. | 32.7, 32.72 |
538
- | `bind:open` | The same, on a `<details>`. | 32.7, 32.72 |
539
- | `bind:group` | Radio or checkbox membership. | 32.8, 32.72 |
558
+ | `bind:checked` | A boolean bind on an input. | 32.7, 32.72, 32.74 |
559
+ | `bind:open` | The same, on a `<details>`. | 32.7, 32.72, 32.74 |
560
+ | `bind:group` | Radio or checkbox membership. | 32.8, 32.72, 32.74 |
561
+ | `bind:files` | A file input's files, as a list. | 32.73, 32.72, 32.74 |
540
562
  | `bind:value={{get, set}}` | Two-way bind over an explicit accessor pair, on an element or a component. | 32.5, 32.6 |
541
563
  | `bind:element={Reactive<Element> \| ((element: Element) => void \| Disposer)}` | A node reference. | 32.9, 32.38, 32.66, 32.68, 32.69 |
542
564
  | `class:name={cond}` | Toggles a class. | 32.10, 32.71 |
@@ -606,7 +628,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
606
628
  | Name | Signature | Meaning | Rules |
607
629
  | --- | --- | --- | --- |
608
630
  | `Component` | `interface Component {}` | A component bound to its props. | 32.17, 34.16, 35.4 |
609
- | `render` | `(component: Component) => Promise<Uint8Array>` | What produces a document. | 29.10, 35.4, 35.5, 35.6, 35.15, 35.16, 41.1, 41.2, 41.3, 41.4, 41.9, 41.13, 41.16, 41.17, 41.18, 41.19, 41.20, 41.28, 35.11, 41.29 |
631
+ | `render` | `(component: Component) => Promise<Uint8Array>` | What produces a document. | 29.10, 35.4, 35.5, 35.6, 35.15, 35.16, 41.1, 41.2, 41.3, 41.4, 41.9, 41.13, 41.16, 41.17, 41.18, 41.19, 41.20, 41.28, 35.11, 41.29, 41.30 |
610
632
 
611
633
  ## Sinks
612
634
 
@@ -637,7 +659,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
637
659
  | `ABIDE_MAX_REQUEST_BODY_SIZE` | `number` | The default ceiling on a mutation's body. | 16.13, 16.54, 16.55, 16.78 |
638
660
  | `ABIDE_MAX_SOCKET_MESSAGE_SIZE` | `number` | The ceiling on a frame received over the socket mux, in bytes. | 18.28 |
639
661
  | `ABIDE_MAX_SOCKET_SUBSCRIPTIONS` | `number` | How many values one connection to the socket mux may follow. | 18.31, 18.32 |
640
- | `ABIDE_OFFLINE` | `boolean` | Whether the app keeps its pages and its bundle on the device, for when the network is gone. | 45.1, 45.2, 45.3, 45.4, 45.5, 45.6, 45.7, 45.8, 45.9, 45.10, 45.11, 45.12, 45.13, 45.14, 45.15, 45.16 |
662
+ | `ABIDE_OFFLINE` | `boolean` | Whether the app keeps its pages and its bundle on the device, for when the network is gone. | 45.1, 45.2, 45.3, 45.4, 45.5, 45.6, 45.7, 45.8, 45.9, 45.10, 45.11, 45.12, 45.13, 45.14, 45.15, 45.16, 45.17, 45.18, 45.19, 45.20, 45.21, 45.22, 45.23, 45.24 |
641
663
  | `ABIDE_STOP_TIMEOUT` | `number` | How long `App.stop` drains, in ms. | 42.17, 42.18 |
642
664
  | `ABIDE_MAX_STREAM_BUFFER_SIZE` | `number` | The cap on a stream transcript held in memory. | 36.18 |
643
665
  | `ABIDE_MAX_LOG_BUFFER_COUNT` | `number` | How many lines `log.records` is trimmed back to. | 28.1 |
package/docs/RULEBOOK.md CHANGED
@@ -84,6 +84,7 @@ These are immutable. A change to them is a change to what this document is.
84
84
  - [43. The measurement harness](#43)
85
85
  - [44. The assistant](#44)
86
86
  - [45. Offline](#45)
87
+ - [46. Outbox](#46)
87
88
 
88
89
  ## Terms
89
90
 
@@ -202,6 +203,14 @@ subscribe it to the value.
202
203
 
203
204
  3.9 A value with no producer MUST report `s.busy` false.
204
205
 
206
+ 3.10 A `set` or a `patch` on a value with a body that moves what it holds MUST make `s.synced` false.
207
+
208
+ 3.11 A production the value's body, or the server a copy follows, makes MUST make `s.synced` true.
209
+
210
+ 3.12 A value with no body MUST report `s.synced` true.
211
+
212
+ 3.13 A value MUST report `s.synced` false where a value it read or adopts does.
213
+
205
214
  # 4. Writes and gates
206
215
 
207
216
  4.1 `s.set` MUST accept a settled value or a load, and `reactive` MUST accept the same two.
@@ -739,6 +748,9 @@ every value a mutation's `declare` built.
739
748
  13.17 A bare `invalidate` or `refresh`, or one over a set of `tags`, MUST NOT invalidate a value a
740
749
  mutation's `declare` built while its write is in flight.
741
750
 
751
+ 13.18 `synced` over a selection MUST be true where every entry in it is synced, and a bare `synced`
752
+ where every value it sweeps is.
753
+
742
754
  # 14. Tracking
743
755
 
744
756
  14.1 A `Reactive` MUST be tracked only where something downstream will be rebuilt from it.
@@ -1087,6 +1099,26 @@ sets MUST be refused 413, whether its length was declared or streamed.
1087
1099
  16.79 An `Rpc` call in a browser that gets no answer, and whose `signal` has not aborted, MUST refuse
1088
1100
  with `HttpError` at 503.
1089
1101
 
1102
+ 16.80 A `GET` call in a browser MUST be sent with `cache` `force-cache` where the page has loaded no
1103
+ answer for its args and the worker `ABIDE_OFFLINE` turns on answers the page, and with `no-cache`
1104
+ otherwise.
1105
+
1106
+ 16.81 A call in a browser given `offline` that gets no answer, or an answer at 502, 503 or 504, MUST
1107
+ settle to what `offline` returns for its args, as a write made here (3.10), save a `GET` whose copy
1108
+ already holds an answer for them.
1109
+
1110
+ 16.82 A mutation call that 16.81 settles MUST be queued in `outbox`, and MUST refuse as it was
1111
+ answered where `outbox` cannot keep it.
1112
+
1113
+ 16.83 A mutation call given `offline` MUST be queued in `outbox`, and not sent, while `outbox` holds
1114
+ a call not yet delivered.
1115
+
1116
+ 16.84 `offline` MUST be inert on a server.
1117
+
1118
+ 16.85 A call in a browser answered from a copy the worker `ABIDE_OFFLINE` turns on kept MUST settle
1119
+ as a write made here (3.10), and a `GET`'s copy that 16.81 or this settles MUST be loaded again once
1120
+ `online` is true.
1121
+
1090
1122
  # 17. Response helpers
1091
1123
 
1092
1124
  17.1 Every body helper MUST take `Values<T>`.
@@ -1214,6 +1246,9 @@ again at once where `online` becomes true or the page becomes visible.
1214
1246
 
1215
1247
  18.34 A browser's connection to the socket mux MUST NOT be opened again while `online` is false.
1216
1248
 
1249
+ 18.35 A subscriber's copy of a `GET` written here (3.10) MUST NOT take a value or a patch sent to it
1250
+ while `outbox` holds a call not yet delivered, and MUST read the value again once it holds none.
1251
+
1217
1252
  # 19. Mount paths and schemas
1218
1253
 
1219
1254
  19.1 A route MUST be answered for each method by one file at most, a page answering `GET` and `HEAD`
@@ -1350,13 +1385,21 @@ whatever its handler's args schema declares there.
1350
1385
  20.20 A handler `/__abide/mcp` would list whose args schema can never be an object MUST refuse the
1351
1386
  app at boot, naming the handler.
1352
1387
 
1388
+ 20.21 A `tools/call` that `/__abide/mcp` answers for a call answered with an image body of a type
1389
+ 44.36 names MUST carry that body as one `image` content item, base64 with its type, in place of its
1390
+ text, and MUST carry no `structuredContent`.
1391
+
1392
+ 20.22 A call to a tool declaring an `outputSchema` that is answered with an `image/*` body MUST be
1393
+ refused, its text naming the tool and the image's type, and MUST be logged per 20.18 as a 500.
1394
+
1353
1395
  # 21. Generated headers
1354
1396
 
1355
1397
  21.1 Every response MUST carry `x-content-type-options: nosniff`.
1356
1398
 
1357
1399
  21.2 Every response MUST carry `traceresponse`, a refusal included.
1358
1400
 
1359
- 21.3 A page, an rpc answer and a refusal MUST default to `cache-control: private, no-store`.
1401
+ 21.3 A page, an rpc answer and a refusal MUST default to `cache-control: private, no-store`, save
1402
+ where 21.22 has it.
1360
1403
 
1361
1404
  21.4 `/__abide/openapi.json` and `/__abide/mcp` MUST answer `cache-control: no-store`. `/__abide/health`
1362
1405
  and `/__abide/principal` are answered as handlers (25.3) and carry 21.3's `private, no-store`.
@@ -1374,10 +1417,11 @@ app MUST NOT be required to state the duration a second time.
1374
1417
 
1375
1418
  21.10 A write through `response` MUST override every header and the status the answer carries.
1376
1419
 
1377
- 21.11 The `max-age` 21.8 derives MUST replace 21.3's `no-store`, and 21.3 MUST decide the rest
1378
- of that header.
1420
+ 21.11 The `max-age` 21.8 derives MUST replace 21.3's `no-store`, or lead 21.22's directives, and the
1421
+ rest of that header MUST be the one 21.3 or 21.22 gives.
1379
1422
 
1380
- 21.12 A file under a `public` directory MUST answer `cache-control: public, max-age=0, must-revalidate`.
1423
+ 21.12 A file under a `public` directory MUST answer `cache-control: public, max-age=0, must-revalidate`,
1424
+ save where 21.23 has it.
1381
1425
 
1382
1426
  21.13 A file under a `public` directory MUST answer an `etag` derived from its bytes.
1383
1427
 
@@ -1390,7 +1434,8 @@ held answered it, `Started` where it is a stream timed to its first chunk, and `
1390
1434
 
1391
1435
  21.16 The `max-age` 21.8 derives MUST be what is left of the answered production's `ttl`.
1392
1436
 
1393
- 21.17 A `GET` over a `global` `reactive` whose `ttl` is `Infinity` MUST keep 21.3's `no-store`.
1437
+ 21.17 A `GET` over a `global` `reactive` whose `ttl` is `Infinity` MUST keep the `cache-control` 21.3
1438
+ or 21.22 gives it.
1394
1439
 
1395
1440
  21.18 A page MUST default to `content-security-policy: frame-ancestors 'none'`.
1396
1441
 
@@ -1401,6 +1446,17 @@ time those rungs took apart from what they wrapped.
1401
1446
 
1402
1447
  21.21 The `Direct` duration 21.15 carries MUST NOT include the time 21.20 counts.
1403
1448
 
1449
+ 21.22 Where 45.1 has a page register the worker `ABIDE_OFFLINE` turns on, a page, and the answer of
1450
+ a `GET` of the app's own that is neither a refusal nor a stream 16.32 frames, MUST default to `cache-control: private,
1451
+ stale-while-revalidate=604800, stale-if-error=604800`.
1452
+
1453
+ 21.23 Where 45.1 has a page register the worker `ABIDE_OFFLINE` turns on, a file under a `public`
1454
+ directory MUST answer `cache-control: public, max-age=0, stale-while-revalidate=604800,
1455
+ stale-if-error=604800`.
1456
+
1457
+ 21.24 Where 45.1 has a page register the worker `ABIDE_OFFLINE` turns on, an answer carrying the
1458
+ `abide-principal` header 26.24 has it carry MUST carry `clear-site-data: "cache"`.
1459
+
1404
1460
  # 22. Request-scoped ambients
1405
1461
 
1406
1462
  22.1 `request`, `response` and `server` MUST throw outside a request.
@@ -2023,6 +2079,13 @@ declarations first, then each directive's in the tag's order.
2023
2079
  attribute a `bind:name` names, `checked` beside `bind:group`, an `<option selected>` inside a
2024
2080
  `<select bind:value>`, or text inside a `<textarea bind:value>`.
2025
2081
 
2082
+ 32.73 `bind:files` MUST hold a list of the input's files, with or without `multiple`, MUST write the
2083
+ files picked back on change, and MUST hand the input a list written to it; the server MUST write
2084
+ nothing for it.
2085
+
2086
+ 32.74 A `bind:` on an element naming none of `value`, `checked`, `open`, `group`, `files` and
2087
+ `element` MUST be a build error.
2088
+
2026
2089
  # 33. Props
2027
2090
 
2028
2091
  33.1 `props` MUST be callable from a `<script>` block only, and calling it from a `<script module>`
@@ -2954,7 +3017,10 @@ adopts the page, however early its answer arrived.
2954
3017
  41.28 A `render` MUST let the turn's queued microtasks run between setting up a `<Name/>` whose setup
2955
3018
  made an `Rpc` call and building that component's markup.
2956
3019
 
2957
- 41.29 A `render` answering a request whose `sec-fetch-dest` is `empty` MUST NOT fill the seed buffer.
3020
+ 41.29 A `render` answering a request whose `sec-fetch-dest` is `empty` MUST NOT fill the seed buffer,
3021
+ save one whose `abide-keep` header names `open`.
3022
+
3023
+ 41.30 A `render` answering a request carrying `abide-keep` MUST write 41.20's script.
2958
3024
 
2959
3025
  # 42. The app object
2960
3026
 
@@ -3108,7 +3174,7 @@ of its own.
3108
3174
  each one.
3109
3175
 
3110
3176
  44.5 `claudeCode` MUST send a transcript to Claude Code as one message, its earlier turns folded
3111
- above the last.
3177
+ above the last and each `AssistantContent` image in its place.
3112
3178
 
3113
3179
  44.6 With `mcp`, the `Toolbox` `assistant` hands a `Provider` MUST list the tools `/__abide/mcp`
3114
3180
  lists, and MUST call each as a request through the app answering the request.
@@ -3199,6 +3265,23 @@ that browser carrying any other.
3199
3265
  `principal.clear` made at its browser since it opened signs in, at the caller 26.9 rotates it to,
3200
3266
  and the command it yields MUST pair that one.
3201
3267
 
3268
+ 44.35 A `Toolbox` call MUST hand as its `ToolAnswer`'s `content` the pieces `/__abide/mcp` carries
3269
+ for the same call, `claudeApi` MUST show the model those pieces as the call's result, and the
3270
+ `result` `AssistantEvent` MUST carry their words and none of their images.
3271
+
3272
+ 44.36 `assistant` MUST refuse 400, before any `Provider` is reached, a transcript holding an
3273
+ `AssistantContent` image whose `mimeType` is none of `image/png`, `image/jpeg`, `image/gif` and
3274
+ `image/webp`.
3275
+
3276
+ 44.37 `assistant` MUST refuse 400, before any `Provider` is reached, a transcript holding an
3277
+ `AssistantContent` image in a turn that is not the user's.
3278
+
3279
+ 44.38 `claudeApi` MUST send each `AssistantContent` image to the model as an image block, base64 with
3280
+ its type, in its place in its turn.
3281
+
3282
+ 44.39 `assistant` MUST hand a `Provider` each turn as its `AssistantContent` pieces, a turn's string
3283
+ as one text piece.
3284
+
3202
3285
  # 45. Offline
3203
3286
 
3204
3287
  45.1 Where `ABIDE_OFFLINE` is on, a page `abide build` built MUST register the worker
@@ -3207,12 +3290,11 @@ and the command it yields MUST pair that one.
3207
3290
  45.2 The worker `ABIDE_OFFLINE` turns on MUST be served below `/__abide`, and MUST be allowed the
3208
3291
  scope of the app's mount.
3209
3292
 
3210
- 45.3 The worker `ABIDE_OFFLINE` turns on MUST answer a page, and every other `GET` of the app's own
3211
- origin, with the network's answer, and with the copy it saved of that address where the network
3212
- gives none.
3293
+ 45.3 The worker `ABIDE_OFFLINE` turns on MUST save an answer to a page, or to any other `GET` of the
3294
+ app's own origin, only where its `cache-control` lets a private cache store it.
3213
3295
 
3214
- 45.4 The worker `ABIDE_OFFLINE` turns on MUST save only an answer of 200 that is neither a stream
3215
- 16.32 frames nor a binary 16.34 types.
3296
+ 45.4 The worker `ABIDE_OFFLINE` turns on MUST save only an answer of 200, to the address it asked
3297
+ rather than one a redirect led to, that is neither a stream 16.32 frames nor a binary 16.34 types.
3216
3298
 
3217
3299
  45.5 The worker `ABIDE_OFFLINE` turns on MUST save every file of the build's bundle as it installs.
3218
3300
 
@@ -3248,3 +3330,82 @@ the size it is drawn at.
3248
3330
 
3249
3331
  45.16 The worker `ABIDE_OFFLINE` turns on MUST leave to the network the request 30.15 makes, a
3250
3332
  request carrying `range`, and one accepting `text/event-stream`.
3333
+
3334
+ 45.17 The worker `ABIDE_OFFLINE` turns on MUST answer a request with the copy it saved of its address,
3335
+ and not ask the network, while that copy is fresh by its `cache-control`.
3336
+
3337
+ 45.18 The worker `ABIDE_OFFLINE` turns on MUST answer a request with the copy it saved of its address,
3338
+ and ask the network for the copy it saves next save where the request's `cache` is `force-cache`,
3339
+ while that copy is stale by no more than its `stale-while-revalidate`.
3340
+
3341
+ 45.19 The worker `ABIDE_OFFLINE` turns on MUST answer a request 45.17 and 45.18 leave to the network
3342
+ with the network's answer, and with the copy it saved of that address where the network gives none
3343
+ and that copy is stale by no more than its `stale-if-error`.
3344
+
3345
+ 45.20 The worker `ABIDE_OFFLINE` turns on MUST answer a request that is not a navigation, and whose
3346
+ `cache` is `no-cache`, `reload` or `no-store`, with the network's answer alone.
3347
+
3348
+ 45.21 The worker `ABIDE_OFFLINE` turns on MUST ask the network past the browser's own cache.
3349
+
3350
+ 45.22 The worker `ABIDE_OFFLINE` turns on MUST mark an answer it gives from a copy it saved with an
3351
+ `abide-kept` header naming that copy's `date`.
3352
+
3353
+ 45.23 The worker `ABIDE_OFFLINE` turns on MUST ask for a page it saves with an `abide-keep` header,
3354
+ naming `open` where its answer is the one a navigation opens.
3355
+
3356
+ 45.24 The worker `ABIDE_OFFLINE` turns on MUST save the page of an address a `navigate` reached,
3357
+ where it saved no copy of that address 45.19 would answer with, and MUST NOT save one of another
3358
+ origin.
3359
+
3360
+ # 46. Outbox
3361
+
3362
+ 46.1 `outbox` MUST answer, oldest first, every call queued in it that was neither delivered nor
3363
+ discarded, narrowed to the handler a selection names and the args a pattern matches.
3364
+
3365
+ 46.2 `outbox` MUST keep its calls across a reload, in IndexedDB where the browser has it.
3366
+
3367
+ 46.3 `outbox` MUST send its calls oldest first, one at a time.
3368
+
3369
+ 46.4 A call `outbox` sends that gets no answer, or an answer at 502, 503 or 504, MUST stay in
3370
+ `outbox`, and MUST hold back the calls after it until it is sent again.
3371
+
3372
+ 46.5 A call `outbox` sends that is refused MUST stay in `outbox`, its `value` refused as the server
3373
+ refused it, and MUST NOT hold back the calls after it.
3374
+
3375
+ 46.6 A call `outbox` sends that is refused at 401 MUST hold back every call after it until
3376
+ `principal` changes or a call's `retry` is called.
3377
+
3378
+ 46.7 A call `outbox` delivers MUST leave it, and its `value` MUST take its answer as 3.11 has it.
3379
+
3380
+ 46.8 `outbox` MUST send as a page loads, where `online` becomes true, where the page becomes visible,
3381
+ and after a send that got no answer, waiting from one second to sixty, doubling.
3382
+
3383
+ 46.9 Every tab of an app MUST show one `outbox`, and one tab at a time MUST send from it.
3384
+
3385
+ 46.10 A call queued in `outbox` MUST carry one `idempotency-key` header on every send, its first
3386
+ included.
3387
+
3388
+ 46.11 `busy` over a handler MUST be true while a call `outbox` sends for it is in flight, and a bare
3389
+ `busy` while any call `outbox` sends is.
3390
+
3391
+ 46.12 A queued call's `retry` MUST send it again at once, and MUST clear its `value`'s refusal as a
3392
+ write made here (3.10).
3393
+
3394
+ 46.13 A queued call's `discard` MUST drop it from `outbox`, sent or not.
3395
+
3396
+ 46.14 `outbox` MUST ask the browser to keep the app's storage once it queues a call.
3397
+
3398
+ 46.15 On a server `outbox` MUST answer no calls.
3399
+
3400
+ 46.16 `outbox` over a selection that names no handler MUST answer no calls.
3401
+
3402
+ 46.17 Reading `outbox` MUST subscribe the reader to the calls it answers.
3403
+
3404
+ 46.18 Each call `outbox` answers MUST carry, as `value`, the `Reactive` its call settled, rebuilt from
3405
+ what was kept where the page holds none.
3406
+
3407
+ 46.19 A `value` 46.18 rebuilds MUST hold the stand-in its call settled to, where the browser could
3408
+ keep it, as a write made here (3.10).
3409
+
3410
+ 46.20 A page MUST NOT open the store 46.2 keeps `outbox` in where `outbox` held no call when it last
3411
+ changed.