@abide/abide 0.121.1 → 0.123.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ABIDE.md CHANGED
@@ -57,6 +57,10 @@ affected code saying what it did, and tell the user.
57
57
  scripts, the CLI and MCP alike.
58
58
  - A `.abide` cannot export. A `<script server>` in one holds that file's private handlers
59
59
  (`const save = POST(…)`), callable by that file alone, and its hooks (`onStart`, `middleware`, …).
60
+ - A rung shared between handlers or layouts is typed `Middleware` and nothing else:
61
+ `export const signedIn: Middleware = async (next) => (await principal.authenticated) ? next() : refuse(401)`
62
+ fits a handler's `middleware: […]` and a layout's `middleware(…)` alike (RULEBOOK 37.14). No
63
+ `<Ctx, Result>` of its own, and no `as` on a `refuse`.
60
64
  - A directory holds addresses, tests, benches, `.d.ts` declarations and two fenced directories
61
65
  alone: `lib/` for components and modules, and `public/` for files served as they are. Anything
62
66
  else beside the pages is a build error.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @abide/abide
2
2
 
3
+ ## 0.123.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 0e5f83e: an unfinished conversation's log line says why — aborted, failed with the throw, or stopped (RULEBOOK 44.31) ([`042aea3`](https://github.com/briancray/abide/commit/042aea3150ae0fd29959bbe1346d1595b86617bc))
8
+ - 0e5f83e: Breaking: a member the value's type declares is the value's, and a read prop holds a Reactive where one is due (RULEBOOK 31.5, 31.6, 33.27) ([`50b1151`](https://github.com/briancray/abide/commit/50b1151d46389185df0b61d0dd82241566446275))
9
+
10
+ ## 0.122.0
11
+
12
+ ### Minor Changes
13
+
14
+ - a2144de: `Middleware` is one type for a rung shared between handlers and layouts: `const signedIn: Middleware = async (next) => …` fits a handler's `middleware: […]` and a layout's `middleware(…)` alike, with no `<Ctx, Result>` of its own. `Middleware<Response>` is a rung that answers with a `Response` of its own, and `Middleware<never, { args: () => unknown }>` one that reads a handler's args. A `refuse(…)` a layout's rung returns is now answered in its lane — the nearest error page over a page, Problem Details over a handler, and by what the request accepts before routing — and the rung outside it is handed that `Response` (RULEBOOK 37.14, 37.15). BREAKING: `Middleware<Ctx, Result>` written with a context type first no longer reads that way; annotate `next` instead, or use the forms above.
15
+
16
+ ### Patch Changes
17
+
18
+ - 44e9f1d: ABIDE.md names the one-word Middleware form for a shared rung (RULEBOOK 37.14) ([`0699004`](https://github.com/briancray/abide/commit/0699004266bf8174263d1820cee2e9946eeee8a3))
19
+
3
20
  ## 0.121.1
4
21
 
5
22
  ### Patch Changes
package/docs/REGISTRY.md CHANGED
@@ -168,7 +168,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
168
168
 
169
169
  | Name | Signature | Meaning | Rules |
170
170
  | --- | --- | --- | --- |
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 |
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.13 |
172
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, 41.28, 41.31, 46.11 |
173
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, 41.28, 41.31 |
174
174
  | `synced` | `(selection?: Selection, pattern?: Partial<Args>) => boolean` | Whether every entry in the selection holds what its producer made. | 13.12, 13.18 |
@@ -218,7 +218,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
218
218
  | `Value` | `unknown` | What the addressed `Reactive` holds. | 16.3, 16.30 |
219
219
  | `Produced` | `unknown` | What it yields, per the unit rule. | 1.2, 16.3 |
220
220
  | `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 |
221
- | `Middleware` | `<Ctx, Result>(next: (ctx?: Ctx) => Promise<Result>, ctx: Ctx) => Result \| Promise<Result>` | One rung of `middleware`. | 16.14, 16.15, 16.16, 16.21 |
221
+ | `Middleware` | `Middleware<Answer = never, Extra extends object = object> = <Given extends { request: Request } & Extra, Result>(next: (ctx?: Given) => Promise<Result>, ctx: Given) => Result \| Answer \| Failed<string, unknown> \| Promise<Result \| Answer \| Failed<string, unknown>>` | One rung, written once for every lane whose context carries `Extra`. | 16.14, 16.15, 16.16, 16.21, 37.14, 37.15 |
222
222
  | `GET` | `<Value, Args, Failures, RungFailures, Produced = Value>(handler: Reactive<Value, Value, Failures, Produced> \| Keyed<Value, Args, Value, Failures, Produced>, options?: RpcOptions<Args, RungFailures>) => Rpc<Value, Args, Failures \| RungFailures, Produced>` | Declares a read any surface may call. | 16.1, 16.2, 16.5, 16.6, 16.30, 16.31, 16.32, 16.68, 16.76, 16.34, 16.35, 16.36, 16.37, 16.39, 16.44, 16.45, 16.46, 16.47, 16.75, 16.50, 16.51, 16.52, 16.64, 18.1, 18.3, 18.6, 18.8, 18.10, 18.12, 18.21, 18.24, 18.27 |
223
223
  | `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 `ReactiveOptions` (`ttl`, `global`, `debounce` and the rest) build the value it becomes beside its address. | 16.1, 16.6, 16.7 |
224
224
  | `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, whose `ReactiveOptions` build the value it becomes. | 16.1, 16.6, 16.7 |
@@ -240,7 +240,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
240
240
  | Name | Signature | Meaning | Rules |
241
241
  | --- | --- | --- | --- |
242
242
  | `description` | `string` | The human description carried onto every generated surface. | 16.28 |
243
- | `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 |
243
+ | `middleware` | `((next: (ctx?: { request: Request; args: () => Args }) => Promise<unknown>, ctx: { request: Request; args: () => Args }) => unknown)[]` | The rungs run per call. | 16.11, 16.12, 16.16, 16.17, 16.18, 16.19, 16.20 |
244
244
  | `timeout` | `number` | How long the call may go without progress. | 16.22, 16.58, 16.59, 16.60, 16.61, 16.77 |
245
245
  | `clients` | `Clients` | Which surfaces carry this handler. | 16.37, 20.3 |
246
246
  | `crossOrigin` | `boolean \| string[]` | Whether other origins may call it, or follow it over the mux. | 16.24, 18.2, 21.19 |
@@ -349,9 +349,9 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
349
349
 
350
350
  | Name | Signature | Meaning | Rules |
351
351
  | --- | --- | --- | --- |
352
- | `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, 44.33 |
352
+ | `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, 44.33, 44.45, 44.46 |
353
353
  | `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 |
354
- | `claudeCode.bridgeCommand` | `(options?: { tools?: string[] }) => { command: string; link: string; expires: number }` | The command and the link that pair a bridge with this browser, granting the built-in tools it is given. | 44.14, 44.32, 44.33, 44.40, 44.41, 44.42, 44.43 |
354
+ | `claudeCode.bridgeCommand` | `(options?: { tools?: string[] }) => { command: string; link: string; expires: number }` | The command and the link that pair a bridge with this browser, granting the built-in tools it is given. | 44.14, 44.32, 44.33, 44.40, 44.41, 44.42, 44.43, 44.44 |
355
355
  | `claudeCode.bridge` | `(options?: { tools?: string[] }) => AsyncGenerator<BridgeStatus>` | This browser's pairing, as it changes. | 44.20, 44.22, 44.32, 44.33, 44.34, 44.40 |
356
356
  | `BridgeStatus` | `{ paired: boolean; command: string \| undefined; link: string \| undefined }` | Whether a bridge is paired, and the command and the link that pair one where none is. | 44.20, 44.40 |
357
357
  | `/__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 |
@@ -380,11 +380,11 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
380
380
 
381
381
  | Name | Signature | Meaning | Rules |
382
382
  | --- | --- | --- | --- |
383
- | `route.url` | `Reactive<URL>` | Where we are, fragment included. | 23.1, 23.2, 23.3, 23.4, 30.20, 30.21, 30.23, 30.33, 19.16, 31.16 |
384
- | `route.params` | `Reactive<Params>` | The matched route's segments — typed by its own route in a page or a layout, and `Params` in any other file. | 23.1, 23.5, 23.9, 30.23, 31.16 |
383
+ | `route.url` | `Reactive<URL>` | Where we are, fragment included. | 23.1, 23.2, 23.3, 23.4, 30.20, 30.21, 30.23, 30.33, 19.16, 31.15 |
384
+ | `route.params` | `Reactive<Params>` | The matched route's segments — typed by its own route in a page or a layout, and `Params` in any other file. | 23.1, 23.5, 23.9, 30.23, 31.15 |
385
385
  | `Params` | `Record<string, string \| string[]>` | What a matched route's segments are. | 23.5 |
386
- | `route.name` | `Reactive<string>` | The resolution path, the same under any mount path. | 23.1, 23.6, 23.14, 31.16 |
387
- | `route.navigating` | `Reactive<boolean>` | Whether a navigation is in flight. | 23.1, 31.16 |
386
+ | `route.name` | `Reactive<string>` | The resolution path, the same under any mount path. | 23.1, 23.6, 23.14, 31.15 |
387
+ | `route.navigating` | `Reactive<boolean>` | Whether a navigation is in flight. | 23.1, 31.15 |
388
388
 
389
389
  ## `online`
390
390
 
@@ -499,7 +499,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
499
499
 
500
500
  | Name | Signature | Meaning | Rules |
501
501
  | --- | --- | --- | --- |
502
- | `csp` | `(sources?: Record<string, string[]>) => Middleware` | The rung that sets `content-security-policy`. | 29.1, 29.2, 29.5, 29.6, 29.7, 29.8 |
502
+ | `csp` | `(sources?: Record<string, string[]>) => (next: (ctx?: { request: Request }) => Promise<Response>, ctx: { request: Request }) => Promise<Response>` | The rung that sets `content-security-policy`. | 29.1, 29.2, 29.5, 29.6, 29.7, 29.8 |
503
503
  | `csp.nonce` | `() => string` | This response's nonce, for the app's own inline script. | 29.3, 29.4, 29.9, 29.11 |
504
504
 
505
505
  ## `url`
@@ -533,12 +533,12 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
533
533
  | `foo` wherever the declared type of its position is not a `Reactive` | a live read | 31.1, 31.2, 31.10 |
534
534
  | `const x: Reactive<…> = foo` / a `Reactive<…>`-typed parameter, return or `bind:` operand | the `Reactive` | 31.3 |
535
535
  | `await foo` | the settling of the `Reactive`, which is its value | 1.4, 2.7, 31.3 |
536
- | `refresh(foo)` / a `Selection`-typed parameter | the `Reactive` | 31.14 |
536
+ | `refresh(foo)` / a `Selection`-typed parameter | the `Reactive` | 31.13 |
537
537
  | `foo = bar` | a write | 31.4, 11.61 |
538
- | `foo.bar = v` | a write through a path | 31.8, 31.15 |
538
+ | `foo.bar = v` | a write through a path | 31.8, 31.14 |
539
539
  | `foo.push(v)` | the same | 31.8, 31.9 |
540
540
  | `const [a, b] = foo` / `...foo` | one read of the value | 31.12 |
541
- | `foo.bar` | the value's `bar`, or the `Reactive`'s own `bar` where it declares one | 31.5, 31.13 |
541
+ | `foo.bar` | the value's `bar`, or the `Reactive`'s own `bar` where it declares one the value does not | 31.5 |
542
542
  | `foo.bar(…)` | the `Reactive` API where `bar` is a member | 31.6 |
543
543
  | `foo()` / `foo.set(v)` | the explicit forms | 31.7, 31.11 |
544
544
 
@@ -596,7 +596,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
596
596
 
597
597
  | Name | Signature | Meaning | Rules |
598
598
  | --- | --- | --- | --- |
599
- | `props` | `<Props>() => { [K in keyof Props]: Props[K] }` | One live accessor per prop the component declares. | 33.1, 33.2, 33.3, 33.4, 33.5, 33.6, 33.7, 33.8, 33.9, 33.10, 33.11, 33.14, 33.15, 33.20, 33.22, 33.25 |
599
+ | `props` | `<Props>() => { [K in keyof Props]: Props[K] }` | One live accessor per prop the component declares. | 33.1, 33.2, 33.3, 33.4, 33.5, 33.6, 33.7, 33.8, 33.9, 33.10, 33.11, 33.14, 33.15, 33.20, 33.22, 33.25, 33.27 |
600
600
  | `Attributes<Tag>` | `Attributes<Tag extends keyof HTMLElementTagNameMap \| keyof SVGElementTagNameMap>` | The attributes an HTML or SVG element takes, typed as a component's props. | 33.23, 33.24, 33.26 |
601
601
  | `children` | always accepted | What a caller passes between the tags. | 33.16, 32.18 |
602
602
 
@@ -702,7 +702,7 @@ type Gated<Gate, Accepted> = [Schema<Accepted>] extends [Gate] ? never : Failed<
702
702
 
703
703
  | Name | Signature | Meaning | Rules |
704
704
  | --- | --- | --- | --- |
705
- | `middleware` | `(...rungs: Middleware<{ request: Request }, Response>[]) => () => void` | The per-request lane at the calling file's route. | 18.26, 37.2, 37.3, 37.13 |
705
+ | `middleware` | `(...rungs: ((next: (ctx?: { request: Request }) => Promise<Response>, ctx: { request: Request }) => Response \| Failed<string, unknown> \| Promise<Response \| Failed<string, unknown>>)[]) => () => void` | The per-request lane at the calling file's route. | 18.26, 37.2, 37.3, 37.13, 37.14, 37.15 |
706
706
  | `onStart` | `(fn: (start: () => Promise<void>) => void \| Promise<void>) => () => void` | Wraps the boot. | 37.4, 37.6, 37.13 |
707
707
  | `onStop` | `(fn: (stop: () => Promise<void>) => void \| Promise<void>) => () => void` | Mirrors it for teardown. | 37.4, 37.6, 37.13 |
708
708
  | `onError` | `(fn: (error: unknown) => unknown) => () => void` | Runs on an unexpected error at the calling file's route. | 37.5, 37.8, 37.13 |
package/docs/RULEBOOK.md CHANGED
@@ -1871,16 +1871,17 @@ the routes 30.9 ranks after that route.
1871
1871
 
1872
1872
  31.3 A name MUST hold the `Reactive` only where the declared type of its position is a `Reactive`: an
1873
1873
  annotated binding, an annotated return, a `Reactive`-typed parameter, a `bind:` operand, an `await`
1874
- operand — or where 31.14 holds it.
1874
+ operand — or where 31.13 holds it.
1875
1875
 
1876
1876
  31.4 Assignment to such a name MUST be a write, and the assigned value MAY be settled, a load, or a
1877
1877
  `Reactive`.
1878
1878
 
1879
1879
  31.5 A property access on such a name MUST reach the value, except a member declared on the
1880
- `Reactive` type, which MUST reach that member bound to the `Reactive`.
1880
+ `Reactive` type that the value's type does not declare, which MUST reach that member bound to the
1881
+ `Reactive`.
1881
1882
 
1882
- 31.6 A call on such a name MUST reach the `Reactive` API where the member is declared on the
1883
- `Reactive` type, and the value's method otherwise.
1883
+ 31.6 A call on such a name MUST reach the `Reactive` API where 31.5 reaches the `Reactive`'s
1884
+ member, and the value's method otherwise.
1884
1885
 
1885
1886
  31.7 `s` and `s.set` MUST keep compiling inside a `.abide` file.
1886
1887
 
@@ -1896,17 +1897,14 @@ the member name.
1896
1897
 
1897
1898
  31.12 A destructuring pattern or a spread over such a name MUST read it once.
1898
1899
 
1899
- 31.13 The compiler MUST warn where 31.5 reaches a `Reactive` member the value's type also declares,
1900
- naming the explicit read.
1901
-
1902
- 31.14 A name MUST hold the `Reactive` where the declared type of its position is a `Selection`, or a
1900
+ 31.13 A name MUST hold the `Reactive` where the declared type of its position is a `Selection`, or a
1903
1901
  type parameter constrained by one.
1904
1902
 
1905
- 31.15 A write through a member path that reaches a value which is not plain data — an array, or an
1903
+ 31.14 A write through a member path that reaches a value which is not plain data — an array, or an
1906
1904
  object whose prototype is `Object.prototype` or `null` — MUST assign on that value itself, MUST NOT
1907
1905
  copy it or any level above it, and MUST NOT wake a reader.
1908
1906
 
1909
- 31.16 `route.url`, `route.params`, `route.name` and `route.navigating` MUST each be read and written
1907
+ 31.15 `route.url`, `route.params`, `route.name` and `route.navigating` MUST each be read and written
1910
1908
  as a name bound to a `Reactive` is, per 31.1 to 31.7.
1911
1909
 
1912
1910
  # 32. Templating
@@ -2219,6 +2217,10 @@ that element as its `currentTarget`.
2219
2217
  included, under the names SVG spells them, and a tag both HTML and SVG name MUST be typed as the HTML
2220
2218
  element.
2221
2219
 
2220
+ 33.27 A name `props` binds MUST hold a `Reactive` wherever 31.3 or 31.13 has a name hold its
2221
+ `Reactive` or 31.5 has it reach its `Reactive`'s member, and that `Reactive`'s probes MUST answer for
2222
+ every `Reactive` the caller's expression read.
2223
+
2222
2224
  # 34. Script and style blocks
2223
2225
 
2224
2226
  34.1 A `<script>` block MUST be per-instance component setup.
@@ -2454,6 +2456,13 @@ through `App.stop`, and the process MUST then exit with 0.
2454
2456
  37.13 A lifecycle hook called in a module of the app's own that is neither a `.abide` nor a handler
2455
2457
  file MUST be a build error naming the module and the hook.
2456
2458
 
2459
+ 37.14 A `Failed` a `middleware` rung returns MUST be answered in the lane the rung is filed over: by
2460
+ the nearest `error.abide` per 35.2 over a page, as 15.21 states over a handler, and as 15.22 paints
2461
+ a 405 for a rung filed at the root route.
2462
+
2463
+ 37.15 A `middleware` rung outside one that returned a `Failed` MUST be handed 37.14's answer from
2464
+ `next`, and MUST NOT be handed the `Failed`.
2465
+
2457
2466
  # 38. Commands
2458
2467
 
2459
2468
  ## The commands
@@ -3348,8 +3357,8 @@ run no turn for 30 seconds.
3348
3357
  44.23 A post to `/__abide/bridge` carrying a line that is not JSON MUST be refused 400, and none of
3349
3358
  its lines MUST be relayed.
3350
3359
 
3351
- 44.24 A bridge MUST answer an MCP request its Claude Code made with a failure where the report
3352
- carrying it was not taken by `/__abide/bridge`, or where no reply arrived within 120 seconds.
3360
+ 44.24 A bridge MUST answer an MCP request its Claude Code made with a failure where
3361
+ `/__abide/bridge` sent no reply within 120 seconds.
3353
3362
 
3354
3363
  44.25 A bridge whose stream from `/__abide/bridge` ends unasked MUST redial it with its pairing's
3355
3364
  token, each wait twice the last, until it is refused or 60 seconds have passed.
@@ -3372,7 +3381,9 @@ before it exits.
3372
3381
 
3373
3382
  44.31 A conversation MUST write one `debug` line on `abide:assistant` when its events end, naming
3374
3383
  its stop reason, the transcript's length, the tool calls it made, its usage and how long it took,
3375
- and naming it `unfinished` where it ended without a `done`.
3384
+ and naming it `unfinished` where it ended without a `done`, followed by why — `aborted` where the
3385
+ request's signal aborted, else `failed` where its provider threw, with the throw's message ending the
3386
+ line, else `stopped` where its caller stopped reading.
3376
3387
 
3377
3388
  44.32 A command `claudeCode.bridgeCommand` or `claudeCode.bridge` mints with `tools` MUST grant
3378
3389
  those built-in tools after its `--` as `--tools`, and one minted without `tools` MUST carry no `--`.
@@ -3409,12 +3420,23 @@ a link in the `abide-bridge` scheme naming the same site, code, bridge release a
3409
3420
  whose release is a version alone, and MUST run the release it names.
3410
3421
 
3411
3422
  44.42 A bridge opening an `abide-bridge` link MUST ask the visitor before it pairs, every time, naming
3412
- the site, the built-in tools and the bridge release the link asks for.
3423
+ the site, the built-in tools and the bridge release the link asks for, and the options and the names
3424
+ of the variables `abide-bridge install` kept (44.44).
3413
3425
 
3414
3426
  44.43 `abide-bridge install` MUST write the app that opens links under the visitor's own Application
3415
3427
  Support and register it there, and `abide-bridge uninstall` MUST unregister it and remove all
3416
3428
  install wrote.
3417
3429
 
3430
+ 44.44 `abide-bridge install` MUST keep the Claude Code options after its `--` and each `--env
3431
+ NAME=VALUE` it is given, and a bridge opening an `abide-bridge` link MUST run Claude Code with those
3432
+ options ahead of the link's built-in tools and with those variables set, and a link MUST NOT set either.
3433
+
3434
+ 44.45 A bridge MUST say that `/__abide/bridge` did not take a report once, and not again until a
3435
+ report is taken, and MUST NOT say it while it redials per 44.25.
3436
+
3437
+ 44.46 A bridge MUST end a turn once `/__abide/bridge` did not take a report carrying it, and MUST
3438
+ report that turn failed.
3439
+
3418
3440
  # 45. Offline
3419
3441
 
3420
3442
  45.1 Where `ABIDE_OFFLINE` is on, a page `abide build` built MUST register the worker
package/docs/abide.md CHANGED
@@ -625,7 +625,7 @@ test('typing writes the value, and the value fills the field', async () => {
625
625
  | `const [a, b] = foo` / `...foo` | one read, taken apart |
626
626
  | `foo = bar` | a write |
627
627
  | `foo.bar` | the value's `bar` |
628
- | `foo.bar(…)` | the value's method, or a member of the `Reactive` such as `busy` |
628
+ | `foo.bar(…)` | the value's method where it has one, and otherwise a member of the `Reactive` such as `busy` |
629
629
  | `foo()` / `foo.set(v)` | the explicit read and write |
630
630
  | `await foo` | the value, once it has settled |
631
631
  | `refresh(foo)` / a `Selection`-typed parameter | the `Reactive` itself |
@@ -4227,24 +4227,28 @@ async function exportAll() {
4227
4227
  ```
4228
4228
 
4229
4229
  ```ts src/account/exportContacts.handler.ts
4230
- import { principal, refuse } from '@abide/abide'
4231
4230
  import { POST } from '@abide/abide/server'
4232
4231
 
4233
4232
  import { CONTACTS } from '../contacts/lib/CONTACTS.server.ts'
4233
+ import { adminsOnly } from './lib/adminsOnly.ts'
4234
4234
 
4235
4235
  export default POST(async () => CONTACTS, {
4236
4236
  description: 'Every contact, for an admin to export.',
4237
- middleware: [
4238
- // Runs before the handler, on every call, in process or not.
4239
- async (next, ctx) => {
4240
- const { role } = await principal.resolved
4241
- if (role !== 'admin') return refuse(403, 'Admins only.')
4242
- return next(ctx)
4243
- },
4244
- ],
4237
+ // Runs before the handler, on every call, in process or not.
4238
+ middleware: [adminsOnly],
4245
4239
  })
4246
4240
  ```
4247
4241
 
4242
+ ```ts src/account/lib/adminsOnly.ts
4243
+ import { type Middleware, principal, refuse } from '@abide/abide'
4244
+
4245
+ // One rung for a handler's `middleware` and a layout's alike.
4246
+ export const adminsOnly: Middleware = async (next) => {
4247
+ const { role } = await principal.resolved
4248
+ return role === 'admin' ? next() : refuse(403, 'Admins only.')
4249
+ }
4250
+ ```
4251
+
4248
4252
  ```ts src/account/lib/ExportContacts.test.ts
4249
4253
  import { expect, test } from 'bun:test'
4250
4254
 
@@ -4278,22 +4282,18 @@ test('sales and a signed-out caller are refused 403', async () => {
4278
4282
  })
4279
4283
  ```
4280
4284
 
4281
- A rung is a `Middleware`: it is handed `next` and the call's context, and either returns `next(ctx)` or answers in the handler's place. Returning `refuse(403, …)` refuses, and neither the handler nor any rung after it runs. A throw escapes every rung rather than unwinding through them.
4285
+ A rung is a `Middleware`: it is handed `next` and the call's context, and either returns `next()` or answers in the handler's place. Returning `refuse(403, …)` refuses, and neither the handler nor any rung after it runs. A throw escapes every rung rather than unwinding through them.
4282
4286
 
4283
4287
  The rung sits on the handler, so it runs on every call: from a browser, from another handler, and from a page rendering on the server. It reads arguments through `ctx.args()`, which parses the body on its first call. So a rung that never asks refuses a signed-out caller before their body is parsed or checked.
4284
4288
 
4285
- To guard a whole section, call `middleware(…)` in its `layout.abide`, such as `src/account/admin/layout.abide`. That rung runs over every page and handler below it, and it has the request and nothing else:
4289
+ To guard a whole section, call `middleware(…)` in its `layout.abide`, such as `src/account/admin/layout.abide`. That rung runs over every page and handler below it, and it has the request and nothing else. The same `adminsOnly` fits there, and its refusal is painted by the section's `error.abide` over a page and answered as Problem Details over a handler:
4286
4290
 
4287
4291
  ```abide abide
4288
4292
  <script server>
4289
- import { principal } from '@abide/abide'
4290
4293
  import { middleware } from '@abide/abide/server'
4294
+ import { adminsOnly } from '#src/account/lib/adminsOnly.ts'
4291
4295
 
4292
- middleware(async (next) => {
4293
- const { role } = await principal.resolved
4294
- if (role !== 'admin') return new Response('Admins only.', { status: 403 })
4295
- return next()
4296
- })
4296
+ middleware(adminsOnly)
4297
4297
  </script>
4298
4298
 
4299
4299
  <slot/>
@@ -7482,7 +7482,7 @@ Arrive here knowing which name you want. Each page is read off the registry: the
7482
7482
 
7483
7483
  | Name | Signature | Meaning | Rules |
7484
7484
  | --- | --- | --- | --- |
7485
- | `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](/rulebook#13.1), [13.8](/rulebook#13.8), [13.9](/rulebook#13.9), [13.13](/rulebook#13.13), [31.14](/rulebook#31.14) |
7485
+ | `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](/rulebook#13.1), [13.8](/rulebook#13.8), [13.9](/rulebook#13.9), [13.13](/rulebook#13.13), [31.13](/rulebook#31.13) |
7486
7486
 
7487
7487
  ## Taught in
7488
7488
 
@@ -7776,7 +7776,7 @@ Reactive primitives: what is written in place rather than imported.
7776
7776
 
7777
7777
  | Name | Signature | Meaning | Rules |
7778
7778
  | --- | --- | --- | --- |
7779
- | `Middleware` | `<Ctx, Result>(next: (ctx?: Ctx) => Promise<Result>, ctx: Ctx) => Result \| Promise<Result>` | One rung of `middleware`. | [16.14](/rulebook#16.14), [16.15](/rulebook#16.15), [16.16](/rulebook#16.16), [16.21](/rulebook#16.21) |
7779
+ | `Middleware` | `Middleware<Answer = never, Extra extends object = object> = <Given extends { request: Request } & Extra, Result>(next: (ctx?: Given) => Promise<Result>, ctx: Given) => Result \| Answer \| Failed<string, unknown> \| Promise<Result \| Answer \| Failed<string, unknown>>` | One rung, written once for every lane whose context carries `Extra`. | [16.14](/rulebook#16.14), [16.15](/rulebook#16.15), [16.16](/rulebook#16.16), [16.21](/rulebook#16.21), [37.14](/rulebook#37.14), [37.15](/rulebook#37.15) |
7780
7780
 
7781
7781
  ## Taught in
7782
7782
 
@@ -7851,7 +7851,7 @@ Reactive primitives: what is written in place rather than imported.
7851
7851
  | Name | Signature | Meaning | Rules |
7852
7852
  | --- | --- | --- | --- |
7853
7853
  | `description` | `string` | The human description carried onto every generated surface. | [16.28](/rulebook#16.28) |
7854
- | `middleware` | `Middleware<{ request: Request; args: () => Args }, Value \| Failures>[]` | The rungs run per call. | [16.11](/rulebook#16.11), [16.12](/rulebook#16.12), [16.16](/rulebook#16.16), [16.17](/rulebook#16.17), [16.18](/rulebook#16.18), [16.19](/rulebook#16.19), [16.20](/rulebook#16.20) |
7854
+ | `middleware` | `((next: (ctx?: { request: Request; args: () => Args }) => Promise<unknown>, ctx: { request: Request; args: () => Args }) => unknown)[]` | The rungs run per call. | [16.11](/rulebook#16.11), [16.12](/rulebook#16.12), [16.16](/rulebook#16.16), [16.17](/rulebook#16.17), [16.18](/rulebook#16.18), [16.19](/rulebook#16.19), [16.20](/rulebook#16.20) |
7855
7855
  | `timeout` | `number` | How long the call may go without progress. | [16.22](/rulebook#16.22), [16.58](/rulebook#16.58), [16.59](/rulebook#16.59), [16.60](/rulebook#16.60), [16.61](/rulebook#16.61), [16.77](/rulebook#16.77) |
7856
7856
  | `clients` | `Clients` | Which surfaces carry this handler. | [16.37](/rulebook#16.37), [20.3](/rulebook#20.3) |
7857
7857
  | `crossOrigin` | `boolean \| string[]` | Whether other origins may call it, or follow it over the mux. | [16.24](/rulebook#16.24), [18.2](/rulebook#18.2), [21.19](/rulebook#21.19) |
@@ -8418,8 +8418,8 @@ Generated surfaces: what is written in place rather than imported.
8418
8418
 
8419
8419
  | Name | Signature | Meaning | Rules |
8420
8420
  | --- | --- | --- | --- |
8421
- | `claudeCode` | `(config?: ClaudeCodeConfig) => Provider` | The visitor's own Claude Code, reached through the bridge paired with their browser, as a provider. | [44.5](/rulebook#44.5), [44.13](/rulebook#44.13), [44.17](/rulebook#44.17), [44.18](/rulebook#44.18), [44.19](/rulebook#44.19), [44.24](/rulebook#44.24), [44.25](/rulebook#44.25), [44.27](/rulebook#44.27), [44.30](/rulebook#44.30), [44.33](/rulebook#44.33) |
8422
- | `claudeCode.bridgeCommand` | `(options?: { tools?: string[] }) => { command: string; link: string; expires: number }` | The command and the link that pair a bridge with this browser, granting the built-in tools it is given. | [44.14](/rulebook#44.14), [44.32](/rulebook#44.32), [44.33](/rulebook#44.33), [44.40](/rulebook#44.40), [44.41](/rulebook#44.41), [44.42](/rulebook#44.42), [44.43](/rulebook#44.43) |
8421
+ | `claudeCode` | `(config?: ClaudeCodeConfig) => Provider` | The visitor's own Claude Code, reached through the bridge paired with their browser, as a provider. | [44.5](/rulebook#44.5), [44.13](/rulebook#44.13), [44.17](/rulebook#44.17), [44.18](/rulebook#44.18), [44.19](/rulebook#44.19), [44.24](/rulebook#44.24), [44.25](/rulebook#44.25), [44.27](/rulebook#44.27), [44.30](/rulebook#44.30), [44.33](/rulebook#44.33), [44.45](/rulebook#44.45), [44.46](/rulebook#44.46) |
8422
+ | `claudeCode.bridgeCommand` | `(options?: { tools?: string[] }) => { command: string; link: string; expires: number }` | The command and the link that pair a bridge with this browser, granting the built-in tools it is given. | [44.14](/rulebook#44.14), [44.32](/rulebook#44.32), [44.33](/rulebook#44.33), [44.40](/rulebook#44.40), [44.41](/rulebook#44.41), [44.42](/rulebook#44.42), [44.43](/rulebook#44.43), [44.44](/rulebook#44.44) |
8423
8423
  | `claudeCode.bridge` | `(options?: { tools?: string[] }) => AsyncGenerator<BridgeStatus>` | This browser's pairing, as it changes. | [44.20](/rulebook#44.20), [44.22](/rulebook#44.22), [44.32](/rulebook#44.32), [44.33](/rulebook#44.33), [44.34](/rulebook#44.34), [44.40](/rulebook#44.40) |
8424
8424
  | `/__abide/bridge` | `GET`, `POST` | Where a bridge pairs, and is relayed its turns. | [36.7](/rulebook#36.7), [44.14](/rulebook#44.14), [44.15](/rulebook#44.15), [44.19](/rulebook#44.19), [44.23](/rulebook#44.23), [44.26](/rulebook#44.26), [16.57](/rulebook#16.57), [16.78](/rulebook#16.78) |
8425
8425
 
@@ -8529,10 +8529,10 @@ Generated surfaces: what is written in place rather than imported.
8529
8529
 
8530
8530
  | Name | Signature | Meaning | Rules |
8531
8531
  | --- | --- | --- | --- |
8532
- | `route.url` | `Reactive<URL>` | Where we are, fragment included. | [23.1](/rulebook#23.1), [23.2](/rulebook#23.2), [23.3](/rulebook#23.3), [23.4](/rulebook#23.4), [30.20](/rulebook#30.20), [30.21](/rulebook#30.21), [30.23](/rulebook#30.23), [30.33](/rulebook#30.33), [19.16](/rulebook#19.16), [31.16](/rulebook#31.16) |
8533
- | `route.params` | `Reactive<Params>` | The matched route's segments — typed by its own route in a page or a layout, and `Params` in any other file. | [23.1](/rulebook#23.1), [23.5](/rulebook#23.5), [23.9](/rulebook#23.9), [30.23](/rulebook#30.23), [31.16](/rulebook#31.16) |
8534
- | `route.name` | `Reactive<string>` | The resolution path, the same under any mount path. | [23.1](/rulebook#23.1), [23.6](/rulebook#23.6), [23.14](/rulebook#23.14), [31.16](/rulebook#31.16) |
8535
- | `route.navigating` | `Reactive<boolean>` | Whether a navigation is in flight. | [23.1](/rulebook#23.1), [31.16](/rulebook#31.16) |
8532
+ | `route.url` | `Reactive<URL>` | Where we are, fragment included. | [23.1](/rulebook#23.1), [23.2](/rulebook#23.2), [23.3](/rulebook#23.3), [23.4](/rulebook#23.4), [30.20](/rulebook#30.20), [30.21](/rulebook#30.21), [30.23](/rulebook#30.23), [30.33](/rulebook#30.33), [19.16](/rulebook#19.16), [31.15](/rulebook#31.15) |
8533
+ | `route.params` | `Reactive<Params>` | The matched route's segments — typed by its own route in a page or a layout, and `Params` in any other file. | [23.1](/rulebook#23.1), [23.5](/rulebook#23.5), [23.9](/rulebook#23.9), [30.23](/rulebook#30.23), [31.15](/rulebook#31.15) |
8534
+ | `route.name` | `Reactive<string>` | The resolution path, the same under any mount path. | [23.1](/rulebook#23.1), [23.6](/rulebook#23.6), [23.14](/rulebook#23.14), [31.15](/rulebook#31.15) |
8535
+ | `route.navigating` | `Reactive<boolean>` | Whether a navigation is in flight. | [23.1](/rulebook#23.1), [31.15](/rulebook#31.15) |
8536
8536
 
8537
8537
  ## Taught in
8538
8538
 
@@ -8977,7 +8977,7 @@ Generated surfaces: what is written in place rather than imported.
8977
8977
 
8978
8978
  | Name | Signature | Meaning | Rules |
8979
8979
  | --- | --- | --- | --- |
8980
- | `csp` | `(sources?: Record<string, string[]>) => Middleware` | The rung that sets `content-security-policy`. | [29.1](/rulebook#29.1), [29.2](/rulebook#29.2), [29.5](/rulebook#29.5), [29.6](/rulebook#29.6), [29.7](/rulebook#29.7), [29.8](/rulebook#29.8) |
8980
+ | `csp` | `(sources?: Record<string, string[]>) => (next: (ctx?: { request: Request }) => Promise<Response>, ctx: { request: Request }) => Promise<Response>` | The rung that sets `content-security-policy`. | [29.1](/rulebook#29.1), [29.2](/rulebook#29.2), [29.5](/rulebook#29.5), [29.6](/rulebook#29.6), [29.7](/rulebook#29.7), [29.8](/rulebook#29.8) |
8981
8981
  | `csp.nonce` | `() => string` | This response's nonce, for the app's own inline script. | [29.3](/rulebook#29.3), [29.4](/rulebook#29.4), [29.9](/rulebook#29.9), [29.11](/rulebook#29.11) |
8982
8982
 
8983
8983
  ## Taught in
@@ -9215,12 +9215,12 @@ Generated surfaces: what is written in place rather than imported.
9215
9215
  | `foo` wherever the declared type of its position is not a `Reactive` | a live read | [31.1](/rulebook#31.1), [31.2](/rulebook#31.2), [31.10](/rulebook#31.10) |
9216
9216
  | `const x: Reactive<…> = foo` / a `Reactive<…>`-typed parameter, return or `bind:` operand | the `Reactive` | [31.3](/rulebook#31.3) |
9217
9217
  | `await foo` | the settling of the `Reactive`, which is its value | [1.4](/rulebook#1.4), [2.7](/rulebook#2.7), [31.3](/rulebook#31.3) |
9218
- | `refresh(foo)` / a `Selection`-typed parameter | the `Reactive` | [31.14](/rulebook#31.14) |
9218
+ | `refresh(foo)` / a `Selection`-typed parameter | the `Reactive` | [31.13](/rulebook#31.13) |
9219
9219
  | `foo = bar` | a write | [31.4](/rulebook#31.4), [11.61](/rulebook#11.61) |
9220
- | `foo.bar = v` | a write through a path | [31.8](/rulebook#31.8), [31.15](/rulebook#31.15) |
9220
+ | `foo.bar = v` | a write through a path | [31.8](/rulebook#31.8), [31.14](/rulebook#31.14) |
9221
9221
  | `foo.push(v)` | the same | [31.8](/rulebook#31.8), [31.9](/rulebook#31.9) |
9222
9222
  | `const [a, b] = foo` / `...foo` | one read of the value | [31.12](/rulebook#31.12) |
9223
- | `foo.bar` | the value's `bar`, or the `Reactive`'s own `bar` where it declares one | [31.5](/rulebook#31.5), [31.13](/rulebook#31.13) |
9223
+ | `foo.bar` | the value's `bar`, or the `Reactive`'s own `bar` where it declares one the value does not | [31.5](/rulebook#31.5) |
9224
9224
  | `foo.bar(…)` | the `Reactive` API where `bar` is a member | [31.6](/rulebook#31.6) |
9225
9225
  | `foo()` / `foo.set(v)` | the explicit forms | [31.7](/rulebook#31.7), [31.11](/rulebook#31.11) |
9226
9226
 
@@ -9330,7 +9330,7 @@ Generated surfaces: what is written in place rather than imported.
9330
9330
 
9331
9331
  | Name | Signature | Meaning | Rules |
9332
9332
  | --- | --- | --- | --- |
9333
- | `props` | `<Props>() => { [K in keyof Props]: Props[K] }` | One live accessor per prop the component declares. | [33.1](/rulebook#33.1), [33.2](/rulebook#33.2), [33.3](/rulebook#33.3), [33.4](/rulebook#33.4), [33.5](/rulebook#33.5), [33.6](/rulebook#33.6), [33.7](/rulebook#33.7), [33.8](/rulebook#33.8), [33.9](/rulebook#33.9), [33.10](/rulebook#33.10), [33.11](/rulebook#33.11), [33.14](/rulebook#33.14), [33.15](/rulebook#33.15), [33.20](/rulebook#33.20), [33.22](/rulebook#33.22), [33.25](/rulebook#33.25) |
9333
+ | `props` | `<Props>() => { [K in keyof Props]: Props[K] }` | One live accessor per prop the component declares. | [33.1](/rulebook#33.1), [33.2](/rulebook#33.2), [33.3](/rulebook#33.3), [33.4](/rulebook#33.4), [33.5](/rulebook#33.5), [33.6](/rulebook#33.6), [33.7](/rulebook#33.7), [33.8](/rulebook#33.8), [33.9](/rulebook#33.9), [33.10](/rulebook#33.10), [33.11](/rulebook#33.11), [33.14](/rulebook#33.14), [33.15](/rulebook#33.15), [33.20](/rulebook#33.20), [33.22](/rulebook#33.22), [33.25](/rulebook#33.25), [33.27](/rulebook#33.27) |
9334
9334
  | `children` | always accepted | What a caller passes between the tags. | [33.16](/rulebook#33.16), [32.18](/rulebook#32.18) |
9335
9335
 
9336
9336
  ## Taught in
@@ -9511,7 +9511,7 @@ Configuration: what is written in place rather than imported.
9511
9511
 
9512
9512
  | Name | Signature | Meaning | Rules |
9513
9513
  | --- | --- | --- | --- |
9514
- | `middleware` | `(...rungs: Middleware<{ request: Request }, Response>[]) => () => void` | The per-request lane at the calling file's route. | [18.26](/rulebook#18.26), [37.2](/rulebook#37.2), [37.3](/rulebook#37.3), [37.13](/rulebook#37.13) |
9514
+ | `middleware` | `(...rungs: ((next: (ctx?: { request: Request }) => Promise<Response>, ctx: { request: Request }) => Response \| Failed<string, unknown> \| Promise<Response \| Failed<string, unknown>>)[]) => () => void` | The per-request lane at the calling file's route. | [18.26](/rulebook#18.26), [37.2](/rulebook#37.2), [37.3](/rulebook#37.3), [37.13](/rulebook#37.13), [37.14](/rulebook#37.14), [37.15](/rulebook#37.15) |
9515
9515
 
9516
9516
  ## Taught in
9517
9517
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abide/abide",
3
- "version": "0.121.1",
3
+ "version": "0.123.0",
4
4
  "type": "module",
5
5
  "description": "Isomorphic type-safe framework for reactive async interfaces for humans and machines",
6
6
  "license": "MIT",
@@ -686,7 +686,8 @@ export class Lowering {
686
686
  /**
687
687
  * The read's name as emitted: `name()` for a written prop, whose cell its accessor hands back.
688
688
  *
689
- * 33.6 — a written prop's cell is reached through its accessor.
689
+ * 33.6 — a written prop's cell is reached through its accessor. 33.27 — a read prop where a
690
+ * `Reactive` is due, a cell over its accessor.
690
691
  */
691
692
  private nameOf(read: Read): string {
692
693
  const spelled = this.projection.text.slice(read.start, read.end)
@@ -694,6 +695,7 @@ export class Lowering {
694
695
  // reads the one `route` every page shares.
695
696
  if (this.routed !== undefined && read.routeParams)
696
697
  return `(${spelled} as unknown as () => __RouteParams<${JSON.stringify(this.routed.route)}, ${JSON.stringify(this.routed.kind)}>)`
698
+ if (read.held) return `${this.use('heldProp')}(${spelled})`
697
699
  return read.through ? `${spelled}()` : spelled
698
700
  }
699
701
 
@@ -734,7 +736,7 @@ export class Lowering {
734
736
  const name = this.nameOf(read)
735
737
  switch (read.shape) {
736
738
  case 'asWritten':
737
- return read.through ? name : undefined
739
+ return read.through || read.held ? name : undefined
738
740
  // 31.5 — the member BOUND to the `Reactive`, arguments and all.
739
741
  case 'member':
740
742
  return this.checking
@@ -8,9 +8,8 @@
8
8
  * `Reactive` itself, which reaches that. Without the list every `n.foo` reads as the API and the
9
9
  * value's own members are unreachable, which is what `regions.join is not a function` was.
10
10
  *
11
- * 31.9 wants the same question answered from the TYPE rather than from the name, which is a
12
- * checker query this does not make yet. A value whose own type declares `set` or `peek` therefore
13
- * resolves to the `Reactive`'s, and 31.13 asks for a warning naming exactly that case.
11
+ * The list names the candidates; the value's TYPE decides (`declaresMember` in `readsIn`): a value
12
+ * whose own type declares `set` or `error` keeps its own, so a `Map`'s `set` is the map's.
14
13
  *
15
14
  * WHICH IS WHY A NAME THE `Reactive` DOES NOT DECLARE MUST NOT BE HERE: it takes a member the
16
15
  * value really has and resolves it to nothing. This list is exactly `MEMBERS` in
@@ -29,6 +29,8 @@ export const SHARED_NAMES: ReadonlySet<string> = new Set([
29
29
  // 32.6, 33.6, 33.7 — a component's `bind:`, and the cell a child writing that prop builds.
30
30
  'boundProp',
31
31
  'propCell',
32
+ // 33.27 — a read prop where its position holds the `Reactive`.
33
+ 'heldProp',
32
34
  'propCells',
33
35
  // 33.20 — a default answering where the caller's accessor reads `undefined`.
34
36
  'propOr',
@@ -625,7 +625,7 @@ export class TypeSession {
625
625
  * shadow is asked is `readsIn`'s.
626
626
  *
627
627
  * WHAT IT HAS TO SAY THAT IS NOT A TYPE ERROR comes back beside the reads: the compiler's own
628
- * warnings (14.14 to 14.17, 31.13, 32.14), each on the `.abide` line where the source's text
628
+ * warnings (14.14 to 14.17, 32.14), each on the `.abide` line where the source's text
629
629
  * was handed in.
630
630
  */
631
631
  async readsFor(
@@ -52,8 +52,7 @@ export type Read = {
52
52
  * that brackets a binding element's initializer, which together make a default an accessor;
53
53
  * `absent`, which gives a binding with no default an accessor of `undefined` (33.4); and
54
54
  * `restMember`, a member of the rest element, which calls that key's accessor (33.15), beside
55
- * `restWhole`, the rest element handed on as one value (33.22). The
56
- * pair and `absent` are INSERTIONS — zero-width, so what stands between the pair goes on being
55
+ * `restWhole`, the rest element handed on as one value (33.22). The pair and `absent` are INSERTIONS — zero-width, so what stands between the pair goes on being
57
56
  * lowered by the same walk, and a default reading a `Reactive` still reads it.
58
57
  */
59
58
  shape:
@@ -96,6 +95,12 @@ export type Read = {
96
95
  * pair, that the default is one too.
97
96
  */
98
97
  through: boolean
98
+ /**
99
+ * 33.27 — the name is a prop the child only reads, standing where a `Reactive` is due.
100
+ *
101
+ * So the `Reactive` every rewrite reaches is a cell over its accessor, `heldProp(name)`.
102
+ */
103
+ held: boolean
99
104
  /** 32.32 — the name is bound to a `reactive(…)` whose options declare `by`. */
100
105
  byValue: boolean
101
106
  /** 14.8 — the name is declared by a branch-local `<script>`, inside the body it scopes. */
@@ -136,6 +141,7 @@ export function newRead(
136
141
  path: [],
137
142
  value: undefined,
138
143
  through: false,
144
+ held: false,
139
145
  byValue: false,
140
146
  local: false,
141
147
  keys: [],
@@ -235,7 +241,10 @@ export function classifyPropRead(node: Node): Read | undefined {
235
241
  * `undefined` for an occurrence that is not a read at all: the declaration itself, an import or
236
242
  * export specifier, and a `typeof n` in a type.
237
243
  */
238
- export function classifyRead(node: Node): Read | undefined {
244
+ export function classifyRead(
245
+ node: Node,
246
+ valueMember = false,
247
+ ): Read | undefined {
239
248
  const parent = node.parent
240
249
  if (isNotARead(parent)) return undefined
241
250
  if (
@@ -278,7 +287,12 @@ export function classifyRead(node: Node): Read | undefined {
278
287
  if (isPropertyAccessExpression(up) && up.expression === top) {
279
288
  // 31.5 — the `Reactive`'s own member is the API, and no path. Called, it stays as
280
289
  // written; REFERENCED, it is handed over bound, so `promise.then(n.set)` writes `n`.
281
- if (top === node && REACTIVE_MEMBERS.has(up.name.text)) {
290
+ // `valueMember` where the value's own type declares one of that name (31.5).
291
+ if (
292
+ top === node &&
293
+ !valueMember &&
294
+ REACTIVE_MEMBERS.has(up.name.text)
295
+ ) {
282
296
  const outer = up.parent
283
297
  if (
284
298
  (isCallExpression(outer) && outer.expression === up) ||
@@ -697,6 +697,7 @@ declare function ${BOUND}<Value>(value: ${REACTIVE}<Value, any, any, any>): Valu
697
697
  declare function ${BOUND}<Value>(value: { get(): Value; set(value: Value): unknown }): Value
698
698
  declare function __restValues<Accessors>(record: Accessors): { [Key in keyof Accessors]: NonNullable<Accessors[Key]> extends () => infer Value ? Value : never }
699
699
  declare function __propCell<Value>(read: () => Value): () => ${REACTIVE}<Value>
700
+ declare function __heldProp<Value>(read: () => Value): ${REACTIVE}<Value, Value, any, any>
700
701
  declare function __raw(value: unknown): unknown
701
702
  declare function __writePath<Root>(root: Root, path: readonly PropertyKey[], value: unknown): Root
702
703
  declare type __RouteParams<Pattern extends string, Kind extends string> = Kind extends 'layout' ? ${ROUTE_PARAMS}<Pattern> & { [name: string]: string | string[] | undefined } : ${ROUTE_PARAMS}<Pattern>
@@ -111,7 +111,7 @@ export type Abide = {
111
111
  route: string | undefined
112
112
  reactive: CheckedSymbol | undefined
113
113
  /**
114
- * 31.14 — abide's `Selection` alias, which a position may be typed as rather than as a `Reactive`.
114
+ * 31.13 — abide's `Selection` alias, which a position may be typed as rather than as a `Reactive`.
115
115
  */
116
116
  selection: CheckedSymbol | undefined
117
117
  props: string | undefined
@@ -185,7 +185,7 @@ export async function isReactiveType(
185
185
  /**
186
186
  * Whether a type is abide's `Selection`, or a type parameter constrained by one.
187
187
  *
188
- * 31.14 — A POSITION TYPED AS A `Selection`, or as a type parameter constrained by one (the free
188
+ * 31.13 — A POSITION TYPED AS A `Selection`, or as a type parameter constrained by one (the free
189
189
  * `refresh`, `invalidate`, `busy` and `ready` take `Chosen extends Selection`). A value is never a
190
190
  * selection of itself, so a read there hands `refresh` the invoice rather than the thing to reload.
191
191
  */