@abide/abide 0.122.0 → 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/CHANGELOG.md +7 -0
- package/docs/REGISTRY.md +10 -10
- package/docs/RULEBOOK.md +23 -13
- package/docs/abide.md +11 -11
- package/package.json +1 -1
- package/src/compiler/Lowering.ts +4 -2
- package/src/compiler/REACTIVE_MEMBERS.ts +2 -3
- package/src/compiler/SHARED_NAMES.ts +2 -0
- package/src/compiler/TypeSession.ts +1 -1
- package/src/compiler/classifyRead.ts +18 -4
- package/src/compiler/projectShadow.ts +1 -0
- package/src/compiler/reactiveTypes.ts +2 -2
- package/src/compiler/readsIn.ts +144 -49
- package/src/server/BRIDGE_VERSION.ts +1 -1
- package/src/server/assistant.ts +25 -4
- package/src/shared/index.ts +2 -1
- package/src/shared/propCell.ts +18 -0
- package/src/shared/writePath.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
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
|
+
|
|
3
10
|
## 0.122.0
|
|
4
11
|
|
|
5
12
|
### Minor 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.
|
|
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 |
|
|
@@ -349,7 +349,7 @@ 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
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 |
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
387
|
-
| `route.navigating` | `Reactive<boolean>` | Whether a navigation is in flight. | 23.1, 31.
|
|
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
|
|
|
@@ -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.
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
1883
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
@@ -3355,8 +3357,8 @@ run no turn for 30 seconds.
|
|
|
3355
3357
|
44.23 A post to `/__abide/bridge` carrying a line that is not JSON MUST be refused 400, and none of
|
|
3356
3358
|
its lines MUST be relayed.
|
|
3357
3359
|
|
|
3358
|
-
44.24 A bridge MUST answer an MCP request its Claude Code made with a failure where
|
|
3359
|
-
|
|
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.
|
|
3360
3362
|
|
|
3361
3363
|
44.25 A bridge whose stream from `/__abide/bridge` ends unasked MUST redial it with its pairing's
|
|
3362
3364
|
token, each wait twice the last, until it is refused or 60 seconds have passed.
|
|
@@ -3379,7 +3381,9 @@ before it exits.
|
|
|
3379
3381
|
|
|
3380
3382
|
44.31 A conversation MUST write one `debug` line on `abide:assistant` when its events end, naming
|
|
3381
3383
|
its stop reason, the transcript's length, the tool calls it made, its usage and how long it took,
|
|
3382
|
-
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.
|
|
3383
3387
|
|
|
3384
3388
|
44.32 A command `claudeCode.bridgeCommand` or `claudeCode.bridge` mints with `tools` MUST grant
|
|
3385
3389
|
those built-in tools after its `--` as `--tools`, and one minted without `tools` MUST carry no `--`.
|
|
@@ -3427,6 +3431,12 @@ install wrote.
|
|
|
3427
3431
|
NAME=VALUE` it is given, and a bridge opening an `abide-bridge` link MUST run Claude Code with those
|
|
3428
3432
|
options ahead of the link's built-in tools and with those variables set, and a link MUST NOT set either.
|
|
3429
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
|
+
|
|
3430
3440
|
# 45. Offline
|
|
3431
3441
|
|
|
3432
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,
|
|
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 |
|
|
@@ -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.
|
|
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
|
|
|
@@ -8418,7 +8418,7 @@ 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) |
|
|
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
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) |
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
8535
|
-
| `route.navigating` | `Reactive<boolean>` | Whether a navigation is in flight. | [23.1](/rulebook#23.1), [31.
|
|
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
|
|
|
@@ -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.
|
|
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.
|
|
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)
|
|
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
|
package/package.json
CHANGED
package/src/compiler/Lowering.ts
CHANGED
|
@@ -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
|
-
*
|
|
12
|
-
*
|
|
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,
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
*/
|
package/src/compiler/readsIn.ts
CHANGED
|
@@ -246,27 +246,22 @@ export async function readsIn(
|
|
|
246
246
|
)
|
|
247
247
|
}),
|
|
248
248
|
)
|
|
249
|
+
const owned = await ownMembers(checker, nodes.flat(), (node) =>
|
|
250
|
+
valueTypeOf(checker, node),
|
|
251
|
+
)
|
|
249
252
|
const holding: Promise<void>[] = []
|
|
250
253
|
const located: { node: Node; read: Read }[] = []
|
|
251
254
|
for (let index = 0; index < nodes.length; index += 1)
|
|
252
255
|
for (const node of nodes[index] as (Node | undefined)[]) {
|
|
253
256
|
if (node === undefined) continue
|
|
254
|
-
const read = classifyRead(node)
|
|
257
|
+
const read = classifyRead(node, owned.has(node))
|
|
255
258
|
if (read === undefined) continue
|
|
256
259
|
read.byValue = byValue[index] as boolean
|
|
257
260
|
read.local = index >= topLevel
|
|
258
261
|
reads.push(read)
|
|
259
262
|
located.push({ node, read })
|
|
260
263
|
if (read.shape === 'value')
|
|
261
|
-
holding.push(
|
|
262
|
-
declaredType(checker, node).then(async (type) => {
|
|
263
|
-
if (
|
|
264
|
-
type !== undefined &&
|
|
265
|
-
(await isReactiveType(checker, abide, type, true))
|
|
266
|
-
)
|
|
267
|
-
read.shape = 'asWritten'
|
|
268
|
-
}),
|
|
269
|
-
)
|
|
264
|
+
holding.push(holdIn(checker, abide, node, read))
|
|
270
265
|
}
|
|
271
266
|
await Promise.all(holding)
|
|
272
267
|
await props
|
|
@@ -392,7 +387,7 @@ async function bindingsOfReads(
|
|
|
392
387
|
* The tracking and type warnings a file's reactive reads earn.
|
|
393
388
|
*
|
|
394
389
|
* The syntax alone decides 14.14 to 14.16 (`trackingWarnings`);
|
|
395
|
-
* 14.17
|
|
390
|
+
* 14.17 asks what a read hands back, which is the return of the `Reactive`'s call.
|
|
396
391
|
*/
|
|
397
392
|
async function warningsFor(
|
|
398
393
|
checker: Checker,
|
|
@@ -424,45 +419,16 @@ async function warningsFor(
|
|
|
424
419
|
isBinaryExpression(parent) &&
|
|
425
420
|
parent.left === node &&
|
|
426
421
|
parent.operatorToken.kind === SyntaxKind.QuestionQuestionToken
|
|
427
|
-
|
|
428
|
-
isPropertyAccessExpression(parent) &&
|
|
429
|
-
parent.expression === node &&
|
|
430
|
-
isIdentifier(parent.name) &&
|
|
431
|
-
REACTIVE_MEMBERS.has(parent.name.text)
|
|
432
|
-
? parent.name.text
|
|
433
|
-
: undefined
|
|
434
|
-
if (!nullish && member === undefined) continue
|
|
422
|
+
if (!nullish) continue
|
|
435
423
|
typed.push(
|
|
436
424
|
valueTypeOf(checker, node).then(async (value) => {
|
|
437
|
-
if (value === undefined) return
|
|
438
425
|
// 14.17 — the fallback of a read that is never nullish is never taken: an unlanded
|
|
439
426
|
// read is a hole the position fills, not an `undefined` (2.2).
|
|
440
|
-
if (
|
|
427
|
+
if (value !== undefined && !(await admitsNullish(value)))
|
|
441
428
|
warnings.push({
|
|
442
429
|
at: read.start,
|
|
443
430
|
message: `abide: \`${name} ?? …\` never takes its fallback — a read of \`${name}\` is not undefined while it loads, and its type says it is not undefined after. Ask \`${name}.ready()\` to tell the two apart. RULEBOOK 14.17.`,
|
|
444
431
|
})
|
|
445
|
-
if (member === undefined) return
|
|
446
|
-
// Per member, because a union's properties are only those every member shares, and
|
|
447
|
-
// `undefined` shares none — so `HTMLVideoElement | undefined` would declare no `error`.
|
|
448
|
-
for (const held of await unionMembers(value)) {
|
|
449
|
-
if ((held.flags & NULLISH) !== 0) continue
|
|
450
|
-
for (const property of await checker.getPropertiesOfType(
|
|
451
|
-
held,
|
|
452
|
-
))
|
|
453
|
-
if (property.name === member) {
|
|
454
|
-
const dot = (
|
|
455
|
-
parent as { questionDotToken?: unknown }
|
|
456
|
-
).questionDotToken
|
|
457
|
-
? '?.'
|
|
458
|
-
: '.'
|
|
459
|
-
warnings.push({
|
|
460
|
-
at: read.start,
|
|
461
|
-
message: `abide: \`${name}${dot}${member}\` reaches the Reactive's own \`${member}\`, and the value it holds has one too. Write \`${name}()${dot}${member}\` for the value's. RULEBOOK 31.13.`,
|
|
462
|
-
})
|
|
463
|
-
return
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
432
|
}),
|
|
467
433
|
)
|
|
468
434
|
}
|
|
@@ -779,7 +745,7 @@ function valueLeaves(node: Node, leaves: Node[]): void {
|
|
|
779
745
|
const ROUTE_MEMBERS = new Set(['url', 'params', 'name', 'navigating'])
|
|
780
746
|
|
|
781
747
|
/**
|
|
782
|
-
* 31.
|
|
748
|
+
* 31.15 — `route`'s four values, each read by name as a name bound to a `Reactive` is.
|
|
783
749
|
*
|
|
784
750
|
* `route.params.id` reads `route.params().id`, and the explicit call keeps compiling (31.7). Told
|
|
785
751
|
* by the symbol, as `props` is, so an app's own `route` is not abide's.
|
|
@@ -1142,13 +1108,18 @@ async function propsReads(
|
|
|
1142
1108
|
),
|
|
1143
1109
|
),
|
|
1144
1110
|
)
|
|
1111
|
+
const owned = await ownMembers(checker, found.flat(), (node) =>
|
|
1112
|
+
checker.getTypeAtLocation(node),
|
|
1113
|
+
)
|
|
1114
|
+
// 33.27 — each prop asked whether its position holds the `Reactive`, all at once.
|
|
1115
|
+
const holding: Promise<void>[] = []
|
|
1145
1116
|
for (let at = 0; at < found.length; at += 1) {
|
|
1146
1117
|
const binding = bindings[at] as (typeof bindings)[number]
|
|
1147
1118
|
if (binding.rest) {
|
|
1148
1119
|
restReads(found[at] as (Node | undefined)[], reads)
|
|
1149
1120
|
continue
|
|
1150
1121
|
}
|
|
1151
|
-
const uses: Read[] = []
|
|
1122
|
+
const uses: { node: Node; read: Read }[] = []
|
|
1152
1123
|
let written = false
|
|
1153
1124
|
for (const node of found[at] as (Node | undefined)[]) {
|
|
1154
1125
|
// The binding itself is the pattern's, and not a use of the name.
|
|
@@ -1157,15 +1128,16 @@ async function propsReads(
|
|
|
1157
1128
|
node.parent?.kind === SyntaxKind.BindingElement
|
|
1158
1129
|
)
|
|
1159
1130
|
continue
|
|
1160
|
-
const read = classifyRead(node)
|
|
1131
|
+
const read = classifyRead(node, owned.has(node))
|
|
1161
1132
|
if (read === undefined) continue
|
|
1162
1133
|
if (
|
|
1163
1134
|
read.shape === 'write' ||
|
|
1164
1135
|
read.shape === 'update' ||
|
|
1165
|
-
isBindOperand(positions, node)
|
|
1136
|
+
isBindOperand(positions, node) ||
|
|
1137
|
+
writesThrough(node, owned)
|
|
1166
1138
|
)
|
|
1167
1139
|
written = true
|
|
1168
|
-
uses.push(read)
|
|
1140
|
+
uses.push({ node, read })
|
|
1169
1141
|
}
|
|
1170
1142
|
if (!written) {
|
|
1171
1143
|
for (const node of found[at] as (Node | undefined)[]) {
|
|
@@ -1173,18 +1145,141 @@ async function propsReads(
|
|
|
1173
1145
|
const read = classifyPropRead(node)
|
|
1174
1146
|
if (read === undefined) continue
|
|
1175
1147
|
read.local = binding.local
|
|
1176
|
-
|
|
1148
|
+
if (read.shape !== 'value') {
|
|
1149
|
+
reads.push(read)
|
|
1150
|
+
continue
|
|
1151
|
+
}
|
|
1152
|
+
holding.push(
|
|
1153
|
+
holdsProp(checker, abide, node, owned.has(node)).then(
|
|
1154
|
+
(held) => {
|
|
1155
|
+
if (!held) return void reads.push(read)
|
|
1156
|
+
// Classified as a page's own name is, reaching a cell over the accessor.
|
|
1157
|
+
const asHeld = classifyRead(node) as Read
|
|
1158
|
+
if (asHeld.shape === 'value')
|
|
1159
|
+
asHeld.shape = 'asWritten'
|
|
1160
|
+
asHeld.held = true
|
|
1161
|
+
asHeld.local = binding.local
|
|
1162
|
+
reads.push(asHeld)
|
|
1163
|
+
},
|
|
1164
|
+
),
|
|
1165
|
+
)
|
|
1177
1166
|
}
|
|
1178
1167
|
continue
|
|
1179
1168
|
}
|
|
1180
1169
|
binding.call.path.push({ name: binding.key, start: 0, end: 0 })
|
|
1181
1170
|
for (const read of binding.defaults) read.through = true
|
|
1182
|
-
|
|
1171
|
+
// A written prop's cell is already a `Reactive`, and a position due one holds it.
|
|
1172
|
+
for (const { node, read } of uses) {
|
|
1183
1173
|
read.through = true
|
|
1184
1174
|
read.local = binding.local
|
|
1185
1175
|
reads.push(read)
|
|
1176
|
+
if (read.shape === 'value')
|
|
1177
|
+
holding.push(holdIn(checker, abide, node, read))
|
|
1186
1178
|
}
|
|
1187
1179
|
}
|
|
1180
|
+
await Promise.all(holding)
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
/**
|
|
1184
|
+
* 31.5 — the `Reactive`'s member a property access reaches by name, or `undefined` where the name
|
|
1185
|
+
* is not one — `n.ready`, `n.set`. Whether it is the `Reactive`'s is the value's type's to say
|
|
1186
|
+
* (`ownMembers`).
|
|
1187
|
+
*/
|
|
1188
|
+
function memberNamed(node: Node): string | undefined {
|
|
1189
|
+
const parent = node.parent
|
|
1190
|
+
return isPropertyAccessExpression(parent) &&
|
|
1191
|
+
parent.expression === node &&
|
|
1192
|
+
isIdentifier(parent.name) &&
|
|
1193
|
+
REACTIVE_MEMBERS.has(parent.name.text)
|
|
1194
|
+
? parent.name.text
|
|
1195
|
+
: undefined
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
/**
|
|
1199
|
+
* 31.5 — the occurrences among `nodes` reaching a member the VALUE's own type declares, where the
|
|
1200
|
+
* access is the value's rather than the `Reactive`'s — a `Map`'s `set` is the map's. Asked before
|
|
1201
|
+
* any is classified, so each is classified once.
|
|
1202
|
+
*
|
|
1203
|
+
* Per union member, because a union's properties are only those every member shares, and
|
|
1204
|
+
* `undefined` shares none — so `HTMLVideoElement | undefined` would declare no `error`. A value
|
|
1205
|
+
* typed `any` or `unknown` declares none: its type says nothing of what it holds.
|
|
1206
|
+
*/
|
|
1207
|
+
async function ownMembers(
|
|
1208
|
+
checker: Checker,
|
|
1209
|
+
nodes: readonly (Node | undefined)[],
|
|
1210
|
+
valueOf: (node: Node) => Promise<Type | undefined>,
|
|
1211
|
+
): Promise<Set<Node>> {
|
|
1212
|
+
const owned = new Set<Node>()
|
|
1213
|
+
const asked: Promise<void>[] = []
|
|
1214
|
+
for (const node of nodes) {
|
|
1215
|
+
if (node === undefined) continue
|
|
1216
|
+
const member = memberNamed(node)
|
|
1217
|
+
if (member === undefined) continue
|
|
1218
|
+
asked.push(
|
|
1219
|
+
valueOf(node).then(async (value) => {
|
|
1220
|
+
if (value === undefined) return
|
|
1221
|
+
for (const held of await unionMembers(value))
|
|
1222
|
+
if (
|
|
1223
|
+
(held.flags & NULLISH) === 0 &&
|
|
1224
|
+
(await checker.getPropertyOfType(held, member)) !==
|
|
1225
|
+
undefined
|
|
1226
|
+
)
|
|
1227
|
+
return void owned.add(node)
|
|
1228
|
+
}),
|
|
1229
|
+
)
|
|
1230
|
+
}
|
|
1231
|
+
await Promise.all(asked)
|
|
1232
|
+
return owned
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
/**
|
|
1236
|
+
* 31.7, 33.6, 33.7 — `n.set(v)` and `n.patch(…)` are writes by name, called or handed on, where the
|
|
1237
|
+
* value's own type declares no member of that name — a `Map`'s `set` writes the map, not the prop.
|
|
1238
|
+
*/
|
|
1239
|
+
function writesThrough(node: Node, owned: ReadonlySet<Node>): boolean {
|
|
1240
|
+
const member = memberNamed(node)
|
|
1241
|
+
return (member === 'set' || member === 'patch') && !owned.has(node)
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
/**
|
|
1245
|
+
* 33.27 — whether a prop the child only reads stands where its `Reactive` is due, as a page's own
|
|
1246
|
+
* name would: an `await` operand (31.3), a `Reactive`'s member its value does not declare (31.5),
|
|
1247
|
+
* or a position typed as a `Reactive` or a `Selection` (31.3, 31.13).
|
|
1248
|
+
*/
|
|
1249
|
+
async function holdsProp(
|
|
1250
|
+
checker: Checker,
|
|
1251
|
+
abide: Abide,
|
|
1252
|
+
node: Node,
|
|
1253
|
+
ownsMember: boolean,
|
|
1254
|
+
): Promise<boolean> {
|
|
1255
|
+
if (node.parent.kind === SyntaxKind.AwaitExpression) return true
|
|
1256
|
+
if (memberNamed(node) !== undefined) return !ownsMember
|
|
1257
|
+
return typedReactive(checker, abide, node)
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
/**
|
|
1261
|
+
* 31.3, 31.13 — `read` holds the `Reactive` where `node` stands in a position whose declared type
|
|
1262
|
+
* is a `Reactive` or a `Selection`.
|
|
1263
|
+
*/
|
|
1264
|
+
async function holdIn(
|
|
1265
|
+
checker: Checker,
|
|
1266
|
+
abide: Abide,
|
|
1267
|
+
node: Node,
|
|
1268
|
+
read: Read,
|
|
1269
|
+
): Promise<void> {
|
|
1270
|
+
if (await typedReactive(checker, abide, node)) read.shape = 'asWritten'
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
/** 31.3, 31.13 — whether `node`'s position is declared a `Reactive` or a `Selection`. */
|
|
1274
|
+
async function typedReactive(
|
|
1275
|
+
checker: Checker,
|
|
1276
|
+
abide: Abide,
|
|
1277
|
+
node: Node,
|
|
1278
|
+
): Promise<boolean> {
|
|
1279
|
+
const type = await declaredType(checker, node)
|
|
1280
|
+
return (
|
|
1281
|
+
type !== undefined && (await isReactiveType(checker, abide, type, true))
|
|
1282
|
+
)
|
|
1188
1283
|
}
|
|
1189
1284
|
|
|
1190
1285
|
/**
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// The `abide-bridge` release a pairing command names, so a visitor's `bunx` runs the relay this
|
|
2
2
|
// server speaks. `packages/abide/tests/claudeCode.test.ts` holds it to the bridge package's own.
|
|
3
|
-
export const BRIDGE_VERSION = '0.5.
|
|
3
|
+
export const BRIDGE_VERSION = '0.5.1'
|
package/src/server/assistant.ts
CHANGED
|
@@ -122,37 +122,58 @@ export async function* assistant(
|
|
|
122
122
|
options.mcp === true ? appSurface() : NO_SURFACE,
|
|
123
123
|
askerOf(request()),
|
|
124
124
|
)
|
|
125
|
+
const signal = request().signal
|
|
125
126
|
const events = provider.run(turns, tools, {
|
|
126
127
|
maxTurns: options.maxTurns ?? MAX_TURNS,
|
|
127
|
-
signal
|
|
128
|
+
signal,
|
|
128
129
|
})
|
|
129
130
|
// 28.7 — a closed namespace runs the provider's stream untouched.
|
|
130
|
-
yield* ASSISTANT_LOG.enabled()
|
|
131
|
+
yield* ASSISTANT_LOG.enabled()
|
|
132
|
+
? traced(events, messages.length, signal)
|
|
133
|
+
: events
|
|
131
134
|
}
|
|
132
135
|
|
|
133
|
-
/**
|
|
136
|
+
/**
|
|
137
|
+
* `events`, and one line on `abide:assistant` once they end, however they end. 44.31.
|
|
138
|
+
*
|
|
139
|
+
* AN UNFINISHED CONVERSATION SAYS WHY, an abort read ahead of a throw: an aborted provider may throw
|
|
140
|
+
* as it unwinds, and the abort is what ended it.
|
|
141
|
+
*/
|
|
134
142
|
async function* traced(
|
|
135
143
|
events: AsyncGenerator<AssistantEvent>,
|
|
136
144
|
messageCount: number,
|
|
145
|
+
signal: AbortSignal,
|
|
137
146
|
): AsyncGenerator<AssistantEvent> {
|
|
138
147
|
const started = performance.now()
|
|
139
148
|
let calls = 0
|
|
140
149
|
let done: Extract<AssistantEvent, { type: 'done' }> | undefined
|
|
150
|
+
let failure: string | undefined
|
|
141
151
|
try {
|
|
142
152
|
for await (const event of events) {
|
|
143
153
|
if (event.type === 'tool') calls += 1
|
|
144
154
|
else if (event.type === 'done') done = event
|
|
145
155
|
yield event
|
|
146
156
|
}
|
|
157
|
+
} catch (thrown) {
|
|
158
|
+
failure = thrown instanceof Error ? thrown.message : String(thrown)
|
|
159
|
+
throw thrown
|
|
147
160
|
} finally {
|
|
161
|
+
const ending =
|
|
162
|
+
done?.stop ??
|
|
163
|
+
(signal.aborted
|
|
164
|
+
? 'unfinished aborted'
|
|
165
|
+
: failure === undefined
|
|
166
|
+
? 'unfinished stopped'
|
|
167
|
+
: 'unfinished failed')
|
|
148
168
|
ASSISTANT_LOG.debug(
|
|
149
|
-
|
|
169
|
+
ending,
|
|
150
170
|
`${messageCount} messages`,
|
|
151
171
|
`${calls} tool calls`,
|
|
152
172
|
done === undefined
|
|
153
173
|
? 'no usage'
|
|
154
174
|
: `${done.usage.input}/${done.usage.output} tokens`,
|
|
155
175
|
`${(performance.now() - started).toFixed(1)}ms`,
|
|
176
|
+
...(ending === 'unfinished failed' ? [failure] : []),
|
|
156
177
|
)
|
|
157
178
|
}
|
|
158
179
|
}
|
package/src/shared/index.ts
CHANGED
|
@@ -54,8 +54,9 @@ export { online } from './online.ts'
|
|
|
54
54
|
export { outbox, type QueuedCall } from './outbox.ts'
|
|
55
55
|
export { onMenu } from './onMenu.ts'
|
|
56
56
|
export { type Principal, principal } from './principal.ts'
|
|
57
|
-
// EMITTED CODE'S: a prop the child writes (33.6, 33.7).
|
|
57
|
+
// EMITTED CODE'S: a prop the child writes (33.6, 33.7), and one it hands where a `Reactive` is due (33.27).
|
|
58
58
|
export {
|
|
59
|
+
heldProp,
|
|
59
60
|
propCell,
|
|
60
61
|
propCells,
|
|
61
62
|
propOr,
|
package/src/shared/propCell.ts
CHANGED
|
@@ -21,6 +21,24 @@ export function propCell(read: Accessor): () => Reactive<unknown> {
|
|
|
21
21
|
return () => cell
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
// Each read prop's `heldProp`, built on the first ask and kept for as long as its accessor is.
|
|
25
|
+
const HELD = new WeakMap<Accessor, Reactive<unknown>>()
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 33.27 — a prop the child only reads, where its position holds the `Reactive` (31.3, 31.13): the
|
|
29
|
+
* one cell `propCell` would have built, over the caller's accessor. So `busy(rows)` asks what the
|
|
30
|
+
* caller's expression read — the caller's own `Reactive` where it bound one — and a plain value's is
|
|
31
|
+
* a cell nothing loads. Built only where one is asked for, and once per accessor.
|
|
32
|
+
*/
|
|
33
|
+
export function heldProp(read: Accessor): Reactive<unknown> {
|
|
34
|
+
let cell = HELD.get(read)
|
|
35
|
+
if (cell === undefined) {
|
|
36
|
+
cell = propCell(read)()
|
|
37
|
+
HELD.set(read, cell)
|
|
38
|
+
}
|
|
39
|
+
return cell
|
|
40
|
+
}
|
|
41
|
+
|
|
24
42
|
/**
|
|
25
43
|
* 33.20 — a defaulted prop's accessor: the caller's, answering the default wherever it reads
|
|
26
44
|
* `undefined`, and the default alone where the caller passed nothing. A bound accessor asked to
|
package/src/shared/writePath.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { putOwn } from './putOwn.ts'
|
|
|
14
14
|
* object where an array was, and `length`, `map` and every later `{#for}` over it go with it. The
|
|
15
15
|
* shape is decided here, where the value is.
|
|
16
16
|
*
|
|
17
|
-
* 31.
|
|
17
|
+
* 31.14 — ONLY PLAIN DATA IS COPIED. An element, a class instance, a `Map`: anything whose prototype
|
|
18
18
|
* is not `Object.prototype`, `null` or `Array.prototype` owns its own state, and its setters say
|
|
19
19
|
* what a write means — `video.currentTime = t` seeks. A spread of one is a plain object holding a
|
|
20
20
|
* few own fields, so the write reached nothing and the `Reactive` came to hold the husk. The write
|