@abide/abide 0.110.4 → 0.112.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 (55) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/docs/REGISTRY.md +30 -29
  3. package/docs/RULEBOOK.md +98 -22
  4. package/docs/abide.md +46 -32
  5. package/package.json +1 -1
  6. package/src/compiler/Lowering.ts +5 -0
  7. package/src/compiler/classifyTemplate.ts +129 -34
  8. package/src/compiler/compileApp.ts +2 -1
  9. package/src/compiler/emitClient.ts +128 -14
  10. package/src/compiler/renderMarkup.ts +14 -8
  11. package/src/server/BRIDGE_VERSION.ts +1 -1
  12. package/src/server/HEARTBEAT_MILLISECONDS.ts +2 -0
  13. package/src/server/IDLE_TIMEOUT_SECONDS.ts +6 -0
  14. package/src/server/answerRequest.ts +44 -3
  15. package/src/server/answerRpc.ts +14 -41
  16. package/src/server/answerSocket.ts +2 -2
  17. package/src/server/answerValue.ts +15 -3
  18. package/src/server/answerWorker.ts +195 -0
  19. package/src/server/appManifest.ts +48 -0
  20. package/src/server/assistant.ts +1 -1
  21. package/src/server/bridgeRelay.ts +4 -50
  22. package/src/server/claudeApi.ts +1 -1
  23. package/src/server/claudeCode.ts +3 -3
  24. package/src/server/clientBundle.ts +7 -0
  25. package/src/server/config.ts +14 -0
  26. package/src/server/createApp.ts +28 -4
  27. package/src/server/devWorker.ts +4 -1
  28. package/src/server/render.ts +2 -2
  29. package/src/server/requestScope.ts +31 -11
  30. package/src/server/responseRelay.ts +7 -7
  31. package/src/server/serveBuild.ts +2 -0
  32. package/src/server/serveDev.ts +4 -1
  33. package/src/server/streamEvent.ts +60 -1
  34. package/src/shared/ADDRESSES.ts +11 -0
  35. package/src/shared/ASCII_WHITESPACE.ts +6 -0
  36. package/src/shared/LONGEST_DELAY.ts +2 -0
  37. package/src/shared/OFFLINE_CACHES.ts +12 -0
  38. package/src/shared/OFFLINE_META.ts +7 -0
  39. package/src/shared/answerOf.ts +29 -4
  40. package/src/shared/attributeText.ts +1 -1
  41. package/src/shared/reactive/Expiry.ts +13 -1
  42. package/src/shared/reactive/Reactive.ts +13 -3
  43. package/src/shared/safeUrl.ts +8 -1
  44. package/src/ui/Level.ts +43 -95
  45. package/src/ui/Router.ts +37 -13
  46. package/src/ui/bindGroup.ts +41 -0
  47. package/src/ui/bindProperty.ts +1 -21
  48. package/src/ui/index.ts +5 -3
  49. package/src/ui/reconcileHead.ts +215 -0
  50. package/src/ui/registerWorker.ts +34 -0
  51. package/src/ui/routing.ts +1 -0
  52. package/src/ui/setClassGroup.ts +79 -0
  53. package/src/ui/setStyleGroup.ts +130 -0
  54. package/src/ui/socketWire.ts +33 -2
  55. package/src/ui/applyHead.ts +0 -214
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @abide/abide
2
2
 
3
+ ## 0.112.0
4
+
5
+ ### Minor Changes
6
+
7
+ - cabe8d1: a class or style attribute beside class: and style: directives keeps moving and writes only its own classes and declarations, and an element where a bind: and something else both write its value is a build error ([`bbbfd81`](https://github.com/briancray/abide/commit/bbbfd810d817235ddb51b8c605756187e473719e))
8
+
9
+ ### Patch Changes
10
+
11
+ - cabe8d1: one writer owns document.head — a keyless head node stands in its contributors' order, an equal one another contributor made stays when the first leaves, and a page's head leaves with its layout's slot ([`f21f687`](https://github.com/briancray/abide/commit/f21f68742efb463f7873a6aafc4482fd572363e7))
12
+
13
+ ## 0.111.0
14
+
15
+ ### Minor Changes
16
+
17
+ - f9b3789: ABIDE_OFFLINE — a built app keeps its pages and bundle in a service worker and installs as a web app, a call that reaches no server refuses 503 and a stale value goes on serving, and the socket mux redials when the browser comes back online ([`ee0428a`](https://github.com/briancray/abide/commit/ee0428a958fc70bf13eb10f2d8df2d7b8f3232df))
18
+
19
+ ### Patch Changes
20
+
21
+ - f9b3789: a request is never reaped by the idle timeout while it is being answered, an idle connection is reaped after 10 seconds, and every event stream beats every 15 seconds ([`14eea6e`](https://github.com/briancray/abide/commit/14eea6e7ae0b72eb194c16e17ca4e7c4498baf46))
22
+ - f9b3789: the client bundle is built in the NODE_ENV of its mode, not of the process that started the build ([`c94a975`](https://github.com/briancray/abide/commit/c94a975f38da935b0f62e52789cefe07d5e887ba))
23
+ - 0838a38: fix: a `blob:` or `data:` URL passes the URL-attribute scheme check (RULEBOOK 32.20), so `<image href={objectUrl}>` or `<img src={dataUrl}>` shows its image instead of being written empty
24
+ - 110aa66: a `ttl` of `Infinity`, or past the 2^31 - 1 ms `setTimeout` holds, no longer re-arms its timer every millisecond for the life of the value: `Infinity` arms none, and a longer `ttl` arms at the cap and re-arms from there
25
+
3
26
  ## 0.110.4
4
27
 
5
28
  ### Patch Changes
package/docs/REGISTRY.md CHANGED
@@ -134,7 +134,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
134
134
 
135
135
  | Name | Signature | Meaning | Rules |
136
136
  | --- | --- | --- | --- |
137
- | `s.invalidate` | `() => void` | Drop the cache behind the value. | 7.1, 7.2, 7.3, 7.6, 7.7, 7.9, 7.10, 7.12, 7.13, 7.14, 7.15, 7.16, 7.17, 11.51 |
137
+ | `s.invalidate` | `() => void` | Drop the cache behind the value. | 7.1, 7.2, 7.3, 7.6, 7.7, 7.9, 7.10, 7.12, 7.13, 7.14, 7.15, 7.16, 7.17, 7.19, 11.51 |
138
138
  | `s.refresh` | `() => void` | Reload while continuing to serve what is held. | 4.12, 7.1, 7.4, 7.5, 7.6, 7.11, 7.18, 11.51, 5.24 |
139
139
 
140
140
  ### Effects
@@ -209,7 +209,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
209
209
 
210
210
  | Name | Signature | Meaning | Rules |
211
211
  | --- | --- | --- | --- |
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 |
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 |
213
213
  | `Value` | `unknown` | What the addressed `Reactive` holds. | 16.3, 16.30 |
214
214
  | `Produced` | `unknown` | What it yields, per the unit rule. | 1.2, 16.3 |
215
215
  | `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 |
@@ -235,7 +235,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
235
235
  | --- | --- | --- | --- |
236
236
  | `description` | `string` | The human description carried onto every generated surface. | 16.28 |
237
237
  | `middleware` | `Middleware<{ request: Request; args: () => Args }, Value \| Failures>[]` | The rungs run per call. | 16.11, 16.12, 16.16, 16.17, 16.18, 16.19, 16.20 |
238
- | `timeout` | `number` | How long the call may stay open, and the floor under the request's idle timeout. | 16.22, 16.23, 16.56, 16.57, 16.58, 16.59, 16.60, 16.61, 16.77 |
238
+ | `timeout` | `number` | How long the call may go without progress. | 16.22, 16.58, 16.59, 16.60, 16.61, 16.77 |
239
239
  | `clients` | `Clients` | Which surfaces carry this handler. | 16.37, 20.3 |
240
240
  | `crossOrigin` | `boolean \| string[]` | Whether other origins may call it, or follow it over the mux. | 16.24, 18.2, 21.19 |
241
241
  | `maxBodySize` | `number` | The largest request body a mutation will accept, in bytes. | 16.13, 16.54, 16.55 |
@@ -249,7 +249,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
249
249
  | `json` | `<T>(value: T) => T` | A value as JSON, fixed, with `undefined` sent as `null`. | 17.7, 17.9, 17.10 |
250
250
  | `redirect` | `(to: string, status?: RedirectStatus) => undefined` | A navigation. | 16.52, 17.3 |
251
251
  | `jsonl` | `<T>(values: Values<T>) => Values<T>` | One JSON value per line, fixed. | 16.44, 16.75, 17.6, 17.7 |
252
- | `sse` | `<T>(values: Values<T>) => Values<T>` | The same machine framed as server-sent events, fixed. | 16.45, 16.75, 17.6, 17.7 |
252
+ | `sse` | `<T>(values: Values<T>) => Values<T>` | The same machine framed as server-sent events, fixed. | 16.45, 16.57, 16.75, 17.6, 17.7 |
253
253
  | `bytes` | `(values: Values<Uint8Array>) => Values<Uint8Array>` | A stream of binary chunks, fixed. | 16.34, 16.75, 17.6, 17.7 |
254
254
  | `RedirectStatus` | `301 \| 302 \| 303 \| 307 \| 308` | The statuses a `redirect` may carry. | 17.3 |
255
255
  | `refuse` | `(status: number, message?: string) => Failed<'HttpError', undefined>` | An undeclared refusal at a status. | 15.5, 15.9, 15.10, 15.16, 15.19, 15.20, 16.40, 17.4, 17.5, 17.8 |
@@ -293,7 +293,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
293
293
  | `access-control-allow-origin` | an rpc answer to another origin | The origin a handler admitted. | 21.19, 16.24 |
294
294
  | `allow: GET, HEAD` | a page, a public file or the bundle, asked by another method | The methods the address answers. | 15.17 |
295
295
  | `server-timing` | an rpc answer; `Middleware` and a `timing` span on any answer | The handler's duration, named `Direct`, `Seeded` from the render's run, `Cached` from a held `global` production, or `Started` to a stream's first chunk; the rungs' time; and each `timing` span. | 21.15, 21.20, 21.21, 24.6 |
296
- | `etag` | a `GET` answered from a `global` value | The version of the production answered. | 18.5, 18.6, 18.7, 18.9, 18.19, 18.20, 18.22, 18.23 |
296
+ | `etag` | a `GET` answered from a `global` value | The version of the production answered. | 18.5, 18.6, 18.7, 18.9, 18.19, 18.20, 18.22, 18.23, 16.57 |
297
297
 
298
298
  # Generated surfaces
299
299
 
@@ -321,9 +321,9 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
321
321
  | --- | --- | --- | --- |
322
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
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 |
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.28 |
325
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 |
326
+ | `Provider` | `{ run(messages: AssistantMessage[], tools: Toolbox, limits: { maxTurns: number; signal: AbortSignal }): AsyncGenerator<AssistantEvent> }` | What runs a conversation. | 44.2, 44.28 |
327
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
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, 20.19, 44.6 |
329
329
 
@@ -338,12 +338,12 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
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. | 44.5, 44.13, 44.17, 44.18, 44.19, 44.24, 44.25, 44.28, 44.31 |
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` | `(options?: { tools?: string[] }) => { command: string; expires: number }` | The command that pairs a bridge with this browser, granting the built-in tools it is given. | 44.14, 44.33 |
344
- | `claudeCode.bridge` | `(options?: { tools?: string[] }) => AsyncGenerator<BridgeStatus>` | This browser's pairing, as it changes. | 44.20, 44.22, 44.33 |
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.27, 44.30 |
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.29 |
343
+ | `claudeCode.bridgeCommand` | `(options?: { tools?: string[] }) => { command: string; expires: number }` | The command that pairs a bridge with this browser, granting the built-in tools it is given. | 44.14, 44.32 |
344
+ | `claudeCode.bridge` | `(options?: { tools?: string[] }) => AsyncGenerator<BridgeStatus>` | This browser's pairing, as it changes. | 44.20, 44.22, 44.32 |
345
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 |
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, 16.57, 16.78 |
347
347
 
348
348
  # Ambient values
349
349
 
@@ -379,7 +379,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
379
379
 
380
380
  | Name | Signature | Meaning | Rules |
381
381
  | --- | --- | --- | --- |
382
- | `online` | `Reactive<boolean>` | Whether the caller can reach the app. | 23.7, 23.8 |
382
+ | `online` | `Reactive<boolean>` | Whether the caller can reach the app. | 23.7, 23.8, 18.33, 18.34 |
383
383
 
384
384
  ## `reachable`
385
385
 
@@ -465,13 +465,13 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
465
465
  | `abide:principal` | `debug` `warning` | A principal set or cleared. | 28.8 |
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, a tool call answered, or a tool published with no description. | 20.3, 20.9, 20.18, 28.8 |
468
- | `abide:assistant` | `debug` | One line per conversation, once it ends. | 44.32 |
468
+ | `abide:assistant` | `debug` | One line per conversation, once it ends. | 44.31 |
469
469
  | `abide:openapi` | `warning` | A handler whose schema had no JSON Schema export. | 20.11, 28.8 |
470
470
  | `abide:render` | `warning` | An `error.abide` that itself failed. | 28.8 |
471
471
  | `abide:refuse` | `warning` | A message formatter that threw. | 15.13, 28.8 |
472
472
  | `abide:reactive` | `warning` | A failed revalidation over a value still being served. | 4.12, 28.8 |
473
473
  | `abide:watch` | `warning` | A `watch` effect or `Disposer` that threw. | 12.6, 28.8 |
474
- | `abide:hydrate` / `abide:navigate` | `warning` | The browser lane. | 28.8, 41.10, 41.15, 41.24 |
474
+ | `abide:hydrate` / `abide:navigate` | `warning` | The browser lane. | 28.8, 41.10, 41.15, 41.24, 45.10 |
475
475
 
476
476
  ## `csp`
477
477
 
@@ -500,7 +500,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
500
500
 
501
501
  | Name | Signature | Meaning | Rules |
502
502
  | --- | --- | --- | --- |
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 |
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 |
504
504
 
505
505
  # `.abide` files
506
506
 
@@ -529,18 +529,18 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
529
529
  | `{expr}` | Reactive text, escaped. | 32.1, 32.23 |
530
530
  | formatting whitespace | What a run of whitespace renders as. | 33.17, 33.18 |
531
531
  | `{raw(...)}` | Raw HTML. | 32.2, 32.25, 32.26, 32.30, 32.31, 32.70 |
532
- | `name={expr}` | A reactive attribute or property. | 32.3, 32.20, 32.23, 32.37 |
532
+ | `name={expr}` | A reactive attribute or property. | 32.3, 32.20, 32.23, 32.37, 32.71 |
533
533
  | `on<event>={fn}` | A native listener on an element, and an ordinary prop on a component. | 32.4 |
534
- | `name="…{expr}…"` | An interpolated quoted value. | 32.3 |
535
- | `bind:value` | Two-way bind on an element. | 5.16, 32.5, 32.58, 32.59, 32.60, 32.61, 32.62 |
534
+ | `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 |
536
536
  | `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 |
538
- | `bind:open` | The same, on a `<details>`. | 32.7 |
539
- | `bind:group` | Radio or checkbox membership. | 32.8 |
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 |
540
540
  | `bind:value={{get, set}}` | Two-way bind over an explicit accessor pair, on an element or a component. | 32.5, 32.6 |
541
541
  | `bind:element={Reactive<Element> \| ((element: Element) => void \| Disposer)}` | A node reference. | 32.9, 32.38, 32.66, 32.68, 32.69 |
542
- | `class:name={cond}` | Toggles a class. | 32.10 |
543
- | `style:prop={value}` | Sets one style property. | 32.10, 32.19 |
542
+ | `class:name={cond}` | Toggles a class. | 32.10, 32.71 |
543
+ | `style:prop={value}` | Sets one style property. | 32.10, 32.19, 32.71 |
544
544
  | `{...expr}` | Spreads props or attributes. | 32.11, 32.37, 33.12, 33.13 |
545
545
  | static template text | What an author writes outside a slot, in body text and in an attribute value. | 32.27, 32.28 |
546
546
 
@@ -606,7 +606,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
606
606
  | Name | Signature | Meaning | Rules |
607
607
  | --- | --- | --- | --- |
608
608
  | `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 |
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 |
610
610
 
611
611
  ## Sinks
612
612
 
@@ -633,10 +633,11 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
633
633
  | `ABIDE_PRINCIPAL_TTL` | `number` | The principal cookie's life, in ms. | 26.15, 26.16 |
634
634
  | `ABIDE_APP_TOKEN` | `string \| null` | The bearer the remote CLI sends. | 36.6 |
635
635
  | `ABIDE_APP_URL` | `string \| null` | Names an app somewhere else, for the remote CLI. | 36.6 |
636
- | `ABIDE_RPC_TIMEOUT` | `number` | The default ms a call may go without progress. | 16.22, 16.56, 16.58 |
636
+ | `ABIDE_RPC_TIMEOUT` | `number` | The default ms a call may go without progress. | 16.22, 16.58 |
637
637
  | `ABIDE_MAX_REQUEST_BODY_SIZE` | `number` | The default ceiling on a mutation's body. | 16.13, 16.54, 16.55, 16.78 |
638
638
  | `ABIDE_MAX_SOCKET_MESSAGE_SIZE` | `number` | The ceiling on a frame received over the socket mux, in bytes. | 18.28 |
639
639
  | `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 |
640
641
  | `ABIDE_STOP_TIMEOUT` | `number` | How long `App.stop` drains, in ms. | 42.17, 42.18 |
641
642
  | `ABIDE_MAX_STREAM_BUFFER_SIZE` | `number` | The cap on a stream transcript held in memory. | 36.18 |
642
643
  | `ABIDE_MAX_LOG_BUFFER_COUNT` | `number` | How many lines `log.records` is trimmed back to. | 28.1 |
@@ -657,7 +658,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
657
658
  | `**/public/**` | The files answered as authored, at their path below that directory. | 23.15, 23.16, 23.18, 36.11, 36.12, 36.13, 36.14, 36.15, 36.17, 21.12, 21.13, 21.14, 38.18, 38.19 |
658
659
  | `src/public/favicon.ico` | The file answered at `/favicon.ico`. | 36.11 |
659
660
  | `src/public/robots.txt` | The file answered at `/robots.txt`. | 36.11 |
660
- | `src/public/icon.png` · `src/public/icon.icns` | The files answered at `/icon.png` and `/icon.icns`, and the desktop app's icon. | 36.11, 38.79 |
661
+ | `src/public/icon.png` · `src/public/icon.icns` | The files answered at `/icon.png` and `/icon.icns`, and the desktop app's icon. | 36.11, 38.79, 45.15 |
661
662
  | `src/**/` | A directory of the app's, a segment of the route of every file directly in it. | 23.14, 23.17, 23.22 |
662
663
  | `**/index.abide` | The page at its directory's route. | 19.1, 23.3, 23.4, 23.14, 23.18 |
663
664
  | `**/<name>.abide` | The page at its directory's route and then `<name>`. | 19.1, 23.3, 23.4, 23.14, 23.21 |
@@ -706,7 +707,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
706
707
  | `App` | `interface App` | What a host, a test or a script holds the app as. | 42.12, 42.13 |
707
708
  | `App.fetch` | `(request: Request) => Promise<Response>` | The whole request path, entered in process. | 42.5, 42.6 |
708
709
  | `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 |
709
- | `App.listen` | `(options?: { port?: number; hostname?: string; unix?: string }) => Promise<void>` | The socket, bound. | 42.9, 18.28, 18.29, 18.30 |
710
+ | `App.listen` | `(options?: { port?: number; hostname?: string; unix?: string }) => Promise<void>` | The socket, bound. | 42.9, 16.23, 16.56, 18.28, 18.29, 18.30 |
710
711
  | `App.stop` | `() => Promise<void>` | The drain and the teardown. | 42.10, 42.11, 42.15, 42.17, 37.4 |
711
712
  | `App[Symbol.asyncDispose]` | `() => Promise<void>` | `App.stop`, under the name `await using` calls. | 42.14 |
712
713
 
@@ -829,7 +830,7 @@ rows a runner prints, and an adapter for an abide app.
829
830
  | `abide run <file> [args…]` | Runs a script under the abide runtime. | 38.2, 38.57 |
830
831
  | `abide check [dir…]` | The type-checker over a `.abide` tree. | 38.3, 38.31, 38.32, 38.33, 38.40, 38.105 |
831
832
  | `abide routes [--json]` | Every address the app answers, the file answering it, and where each hook is filed. | 38.108 |
832
- | `abide dev [--port <n>]` | Watches the project and keeps the app up. | 37.8, 37.9, 38.4, 38.14, 38.15, 38.16, 38.17, 38.19, 38.24, 38.25, 38.26, 38.28, 38.30, 38.35, 38.36, 38.37, 38.38, 38.41, 38.43, 38.58, 38.63, 38.64, 38.65, 38.94, 38.96, 38.97 |
833
+ | `abide dev [--port <n>]` | Watches the project and keeps the app up. | 16.23, 16.56, 37.8, 37.9, 38.4, 38.14, 38.15, 38.16, 38.17, 38.19, 38.24, 38.25, 38.26, 38.28, 38.30, 38.35, 38.36, 38.37, 38.38, 38.41, 38.43, 38.58, 38.63, 38.64, 38.65, 38.94, 38.96, 38.97 |
833
834
  | `abide build` | Builds the client into content-hashed chunks and a manifest. | 38.43, 38.5, 38.18, 38.25, 38.26, 38.27, 38.30, 38.66, 38.94 |
834
835
  | `abide start [--port <n>]` | Boots the built app. | 37.8, 37.10, 37.11, 37.12, 38.6, 38.100, 38.20, 38.21, 38.22, 38.23, 38.25, 38.26, 38.27, 38.28, 38.29, 38.58, 38.67, 38.68, 38.95, 38.97, 38.110 |
835
836
  | `abide [--url <origin>] [--port <n>]` | The interactive shell — on the built app it boots, or on the running app `--url` names. | 37.8, 37.10, 37.11, 37.12, 38.6, 38.7, 38.53, 38.54, 38.55, 38.58, 38.59, 38.60, 38.62 |
package/docs/RULEBOOK.md CHANGED
@@ -83,6 +83,7 @@ These are immutable. A change to them is a change to what this document is.
83
83
  - [42. The app object](#42)
84
84
  - [43. The measurement harness](#43)
85
85
  - [44. The assistant](#44)
86
+ - [45. Offline](#45)
86
87
 
87
88
  ## Terms
88
89
 
@@ -307,8 +308,8 @@ window held MUST start one reload when the window closes.
307
308
 
308
309
  6.2 `ttl` on a value with no producer MUST be inert.
309
310
 
310
- 6.3 An implementation MUST hold one expiry timer per value declaring `ttl`, armed at the deadline of
311
- the production it serves.
311
+ 6.3 An implementation MUST hold at most one expiry timer per value declaring `ttl`, armed no later
312
+ than the deadline of the production it serves, and none where that deadline is never reached.
312
313
 
313
314
  6.4 A `ttl` expiry timer MUST NOT hold the process open.
314
315
 
@@ -366,6 +367,9 @@ producer.
366
367
 
367
368
  7.18 `s.refresh` on a wired `Reactive` MUST refresh every subscriber's copy.
368
369
 
370
+ 7.19 A load over a value `s.invalidate` marked that is refused with `HttpError` at 503 MUST leave the
371
+ value it held being served.
372
+
369
373
  # 8. Stores
370
374
 
371
375
  8.1 A `Store` MUST provide both `get` and `set`.
@@ -897,9 +901,10 @@ declared.
897
901
 
898
902
  16.21 A throw from a `Middleware` rung MUST escape the whole onion rather than unwind through it.
899
903
 
900
- 16.22 `timeout` MUST raise the request's idle timeout, and MUST only raise it.
904
+ 16.22 `timeout` and `ABIDE_RPC_TIMEOUT` MUST default to no bound.
901
905
 
902
- 16.23 A request's `timeout` floor MUST be the maximum over every handler that ran in it.
906
+ 16.23 A request MUST NOT be ended by the idle timeout of the server `App.listen` or `abide dev` binds
907
+ from when it has been received whole until its answer, a stream's last chunk included, has been sent.
903
908
 
904
909
  16.24 `crossOrigin` MUST default to false, and where it is declared abide MUST answer the `OPTIONS`
905
910
  preflight itself. An `OPTIONS` naming no `access-control-request-method` is no preflight, and MUST be
@@ -997,10 +1002,11 @@ MUST refuse with `aborted` at status 499, and MUST abort the request carrying it
997
1002
  16.55 A mutation's body over the ceiling `maxBodySize` or `ABIDE_MAX_REQUEST_BODY_SIZE` sets MUST
998
1003
  be refused 413, whether its length was declared or streamed.
999
1004
 
1000
- 16.56 Where neither `timeout` nor `ABIDE_RPC_TIMEOUT` is set, a call MUST NOT raise the request's
1001
- idle timeout.
1005
+ 16.56 A connection to the server `App.listen` or `abide dev` binds MUST be ended once it goes 10
1006
+ seconds without a byte while no request over it is being answered.
1002
1007
 
1003
- 16.57 A `timeout` of `Infinity` MUST leave the request's idle timeout off.
1008
+ 16.57 An answer typed `text/event-stream` MUST carry a comment every 15 seconds for as long as it is
1009
+ open.
1004
1010
 
1005
1011
  16.58 Where a handler names no `timeout`, `ABIDE_RPC_TIMEOUT` MUST be its `timeout`.
1006
1012
 
@@ -1069,6 +1075,9 @@ call MUST wait on that window as 5.15 has it.
1069
1075
  16.78 A body `/__abide/mcp` or `/__abide/bridge` reads over the ceiling `ABIDE_MAX_REQUEST_BODY_SIZE`
1070
1076
  sets MUST be refused 413, whether its length was declared or streamed.
1071
1077
 
1078
+ 16.79 An `Rpc` call in a browser that gets no answer, and whose `signal` has not aborted, MUST refuse
1079
+ with `HttpError` at 503.
1080
+
1072
1081
  # 17. Response helpers
1073
1082
 
1074
1083
  17.1 Every body helper MUST take `Values<T>`.
@@ -1191,6 +1200,11 @@ unsent, and MUST NOT be sent the frames after it.
1191
1200
  18.32 A subscription to a `GET` naming an id its connection already follows MUST replace that one, and
1192
1201
  MUST NOT count toward 18.31's bound.
1193
1202
 
1203
+ 18.33 A browser's connection to the socket mux that closed while a `GET` is followed MUST be opened
1204
+ again at once where `online` becomes true or the page becomes visible.
1205
+
1206
+ 18.34 A browser's connection to the socket mux MUST NOT be opened again while `online` is false.
1207
+
1194
1208
  # 19. Mount paths and schemas
1195
1209
 
1196
1210
  19.1 A route MUST be answered for each method by one file at most, a page answering `GET` and `HEAD`
@@ -1715,7 +1729,7 @@ fragment it carries names.
1715
1729
  and no public file of the app is served at.
1716
1730
 
1717
1731
  30.27 A `navigate` whose request fails, or whose route's chunks fail to load, MUST leave the address
1718
- to the document.
1732
+ to the document, save where 30.32 holds.
1719
1733
 
1720
1734
  30.28 A `Query` record and the args an `Rpc` carries in its URL MUST be written in one spelling: a
1721
1735
  member left `undefined` omitted, a list its member repeated, an empty list its name with no `=`, and
@@ -1730,6 +1744,9 @@ the document, MUST land at that scroll.
1730
1744
  30.31 A `navigate` that a later `navigate` overtakes before it mounts MUST abort the request it made
1731
1745
  to `middleware`.
1732
1746
 
1747
+ 30.32 A `navigate` that is not a history step, whose request gets no answer while `online` is false,
1748
+ MUST leave the page and the address where they stand, save where 45.12 holds.
1749
+
1733
1750
  # 31. Reading and writing by name
1734
1751
 
1735
1752
  31.1 Inside a `.abide` file a name bound to a `Reactive` MUST be read wherever 31.3 does not hold it.
@@ -1826,7 +1843,7 @@ body.
1826
1843
  MUST NOT be built as a string.
1827
1844
 
1828
1845
  32.20 An interpolation in a URL-typed attribute MUST be scheme-checked, and a scheme outside
1829
- `http:`, `https:`, `mailto:`, `tel:` or a relative reference MUST be dropped and warned on
1846
+ `http:`, `https:`, `mailto:`, `tel:`, `blob:`, `data:` or a relative reference MUST be dropped and warned on
1830
1847
  `abide:render`, naming the attribute and not the value.
1831
1848
 
1832
1849
  32.21 A control block MUST bind each reactive read and probe call in its subject once for the body.
@@ -1986,6 +2003,15 @@ reading layout, querying its descendants or calling `focus()` acts on a connecte
1986
2003
  32.70 Markup the client parses for a slot holding a value branded by `raw`, a `{#if}` arm or a
1987
2004
  `{#for}` row MUST be parsed as the document's parser parses it inside the element it goes into.
1988
2005
 
2006
+ 32.71 A `class` or `style` attribute beside a `class:name` or `style:prop` MUST keep moving (32.3)
2007
+ and MUST write only its own classes or declarations; a class either of them holds MUST stay on the
2008
+ element, and a style property MUST resolve as the server's merged attribute does — the attribute's
2009
+ declarations first, then each directive's in the tag's order.
2010
+
2011
+ 32.72 An element MUST be a build error where a `bind:` and something else both write its value: the
2012
+ attribute a `bind:name` names, `checked` beside `bind:group`, an `<option selected>` inside a
2013
+ `<select bind:value>`, or text inside a `<textarea bind:value>`.
2014
+
1989
2015
  # 33. Props
1990
2016
 
1991
2017
  33.1 `props` MUST be callable from a `<script>` block only, and calling it from a `<script module>`
@@ -2657,8 +2683,9 @@ hydration woke before it settles; it MUST throw where the component was not impo
2657
2683
  39.4 `<head>` contributions MUST be merged by key, and the deepest contributor MUST win. The shell's
2658
2684
  `<head>` MUST be the outermost contributor.
2659
2685
 
2660
- 39.5 A `<head>` contribution with no merge key MUST append — in tree order among the contributions
2661
- its level mounts with, and when it mounts for a component mounted after its level.
2686
+ 39.5 A `<head>` contribution with no merge key MUST append, and MUST stand in its contributors'
2687
+ order — each level's own, then its components' in the order they mounted, outermost level first — a
2688
+ component mounted after its level included.
2662
2689
 
2663
2690
  39.6 On a client navigation the incoming page's keyed `<head>` contributions MUST replace the
2664
2691
  outgoing page's by key, and a layout's MUST NOT move.
@@ -2692,7 +2719,7 @@ contributor has that key.
2692
2719
  MUST replace, in place, the one that same contributor wrote before.
2693
2720
 
2694
2721
  39.16 A `<head>` contribution with no merge key MUST be removed when the page, layout or component
2695
- that made it leaves.
2722
+ that made it leaves, and an equal one another contributor made MUST stay.
2696
2723
 
2697
2724
  39.17 A `<script>` a `<head>` contribution writes in a browser MUST run when it is written, as the
2698
2725
  one a server paints does, and MUST NOT run again while the same contributor writes it unchanged.
@@ -2912,6 +2939,8 @@ adopts the page, however early its answer arrived.
2912
2939
  41.28 A `render` MUST let the turn's queued microtasks run between setting up a `<Name/>` whose setup
2913
2940
  made an `Rpc` call and building that component's markup.
2914
2941
 
2942
+ 41.29 A `render` answering a request whose `sec-fetch-dest` is `empty` MUST NOT fill the seed buffer.
2943
+
2915
2944
  # 42. The app object
2916
2945
 
2917
2946
  42.1 `createApp` MUST build the route table and compose every `Middleware` rung, per 16.14.
@@ -3091,7 +3120,7 @@ it, once, and MUST NOT pair once it has expired.
3091
3120
  removed when the run ends, and with no built-in tool and no setting source the visitor did not pass
3092
3121
  it after `--`.
3093
3122
 
3094
- 44.16 A bridge MUST lay each option a site sets but `tools` (44.30) on as Claude Code's flag of that
3123
+ 44.16 A bridge MUST lay each option a site sets but `tools` (44.29) on as Claude Code's flag of that
3095
3124
  name, MUST fail a turn whose config sets any option `ClaudeCodeConfig` does not name, and MUST lay
3096
3125
  an option the visitor passed after `--` over the site's.
3097
3126
 
@@ -3123,25 +3152,72 @@ token, each wait twice the last, until it is refused or 60 seconds have passed.
3123
3152
  44.26 `/__abide/bridge` MUST hold a pairing whose stream dropped for 60 seconds, and a `GET` bearing
3124
3153
  its token within them MUST resume the stream with every frame the dropped one had not sent.
3125
3154
 
3126
- 44.27 `/__abide/bridge` MUST send a comment on a pairing's stream every 15 seconds for as long as
3127
- the stream is open.
3128
-
3129
- 44.28 A bridge whose stream from `/__abide/bridge` carries no line for 45 seconds MUST end it and
3155
+ 44.27 A bridge whose stream from `/__abide/bridge` carries no line for 45 seconds MUST end it and
3130
3156
  redial it as 44.25 says.
3131
3157
 
3132
- 44.29 A `Provider` MUST yield a tool call's `tool` event when the model starts writing the call,
3158
+ 44.28 A `Provider` MUST yield a tool call's `tool` event when the model starts writing the call,
3133
3159
  and its `input` event, with the same `id`, once the call is written.
3134
3160
 
3135
- 44.30 A built-in tool MUST be available to a turn only where the site's `tools` names it and the
3161
+ 44.29 A built-in tool MUST be available to a turn only where the site's `tools` names it and the
3136
3162
  visitor's `--tools` does, MUST be approved without asking unless the visitor passed `--allowedTools`,
3137
3163
  and the bridge MUST say once each tool the site asked for that the visitor did not grant.
3138
3164
 
3139
- 44.31 A bridge stopped by `SIGTERM`, `SIGINT` or `SIGHUP` MUST end every Claude Code run it started
3165
+ 44.30 A bridge stopped by `SIGTERM`, `SIGINT` or `SIGHUP` MUST end every Claude Code run it started
3140
3166
  before it exits.
3141
3167
 
3142
- 44.32 A conversation MUST write one `debug` line on `abide:assistant` when its events end, naming
3168
+ 44.31 A conversation MUST write one `debug` line on `abide:assistant` when its events end, naming
3143
3169
  its stop reason, the transcript's length, the tool calls it made, its usage and how long it took,
3144
3170
  and naming it `unfinished` where it ended without a `done`.
3145
3171
 
3146
- 44.33 A command `claudeCode.bridgeCommand` or `claudeCode.bridge` mints with `tools` MUST grant
3172
+ 44.32 A command `claudeCode.bridgeCommand` or `claudeCode.bridge` mints with `tools` MUST grant
3147
3173
  those built-in tools after its `--` as `--tools`, and one minted without `tools` MUST carry no `--`.
3174
+
3175
+ # 45. Offline
3176
+
3177
+ 45.1 Where `ABIDE_OFFLINE` is on, a page `abide build` built MUST register the worker
3178
+ `ABIDE_OFFLINE` turns on, and a page `abide dev` serves MUST NOT.
3179
+
3180
+ 45.2 The worker `ABIDE_OFFLINE` turns on MUST be served below `/__abide`, and MUST be allowed the
3181
+ scope of the app's mount.
3182
+
3183
+ 45.3 The worker `ABIDE_OFFLINE` turns on MUST answer a page, and every other `GET` of the app's own
3184
+ origin, with the network's answer, and with the copy it saved of that address where the network
3185
+ gives none.
3186
+
3187
+ 45.4 The worker `ABIDE_OFFLINE` turns on MUST save only an answer of 200 that is neither a stream
3188
+ 16.32 frames nor a binary 16.34 types.
3189
+
3190
+ 45.5 The worker `ABIDE_OFFLINE` turns on MUST save every file of the build's bundle as it installs.
3191
+
3192
+ 45.6 The worker `ABIDE_OFFLINE` turns on MUST answer a file of the build's bundle from what it
3193
+ saved.
3194
+
3195
+ 45.7 The worker `ABIDE_OFFLINE` turns on MUST drop everything it saved where an answer it is handed
3196
+ carries `abide-principal`, where `principal` changes in any tab, and where a form is submitted.
3197
+
3198
+ 45.8 The worker `ABIDE_OFFLINE` turns on, of a new build, MUST drop everything the one before it
3199
+ saved, and every file of the bundle the new build does not name.
3200
+
3201
+ 45.9 The address of the worker `ABIDE_OFFLINE` turns on MUST answer a worker that drops what was
3202
+ saved and unregisters itself, under `abide dev` and where `ABIDE_OFFLINE` is off.
3203
+
3204
+ 45.10 The worker `ABIDE_OFFLINE` turns on that fails to register MUST warn on `abide:hydrate`.
3205
+
3206
+ 45.11 The worker `ABIDE_OFFLINE` turns on MUST answer a page the network gives no answer for, and
3207
+ that it saved no copy of, 503 with a page saying so.
3208
+
3209
+ 45.12 A `navigate` 30.32 would leave standing MUST be left to the document where the worker
3210
+ `ABIDE_OFFLINE` turns on saved its address's page.
3211
+
3212
+ 45.13 The worker `ABIDE_OFFLINE` turns on MUST save, as it activates, the page of every window it
3213
+ then controls.
3214
+
3215
+ 45.14 `ABIDE_OFFLINE` MUST be off where `config` holds it as `false`, or as text reading `false` or
3216
+ `0` in any case, and on otherwise.
3217
+
3218
+ 45.15 Where `ABIDE_OFFLINE` is on and the app's public `/icon.png` is a PNG, every page MUST link a
3219
+ web app manifest naming the app, with the app's mount as its scope and its start, and that icon at
3220
+ the size it is drawn at.
3221
+
3222
+ 45.16 The worker `ABIDE_OFFLINE` turns on MUST leave to the network the request 30.15 makes, a
3223
+ request carrying `range`, and one accepting `text/event-stream`.