@opetope/react 0.9.7 → 0.10.1
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 +73 -0
- package/README.md +40 -29
- package/README.ru.md +41 -29
- package/dist/commands-hook.d.ts +7 -2
- package/dist/contribution-frame-QRJSP2W3.js +2 -0
- package/dist/contribution-frame-QRJSP2W3.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/model-hook.d.ts +6 -6
- package/dist/model-selection-snapshot.d.ts +6 -2
- package/dist/model-selection-store.d.ts +1 -1
- package/dist/resource-command.d.ts +38 -27
- package/dist/resource-hook.d.ts +8 -6
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +2 -2
- package/dist/testing.js.map +1 -1
- package/package.json +5 -5
- package/dist/contribution-frame-qP1uPkaO.js +0 -2
- package/dist/contribution-frame-qP1uPkaO.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,78 @@
|
|
|
1
1
|
# @opetope/react
|
|
2
2
|
|
|
3
|
+
## 0.10.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 5c7abd5: **A closed source says nothing new (D362).** A regression of 0.10.0: any `context.effect` whose source observed
|
|
8
|
+
`resource.state`, directly or through a `derive`, put one `ReadableError: Cannot read a closed readable state.` into
|
|
9
|
+
the model's reporter when the model was fenced. `resource.state` closes inside its controller's fence, while the
|
|
10
|
+
nodes fenced after it still follow it (D338), and a close is a change dependents hear once (D146) — so the `derive`
|
|
11
|
+
recomputed against a closed source and the effect reported that refusal as a failure. The same reached
|
|
12
|
+
`scope.while/switch/keyed` over such a source and the `request`, `enabled` or scope input of another Resource.
|
|
13
|
+
|
|
14
|
+
- **The law.** A runtime node that follows a `Readable` reads a `ReadableError` of code `closed` as a fact about the
|
|
15
|
+
source's lifetime: it keeps what it last observed, does nothing and leaves no reporter record. A `scope` keeps its
|
|
16
|
+
current child, released by its own drain; a Resource keeps the materialization it selected. In React the close
|
|
17
|
+
arrives from outside the model, so `read` in a `useModel` selection, `useSelector` and `useReadable` answer the last
|
|
18
|
+
value they read and a render that raced a retirement shows its last frame instead of failing mid-render, which is
|
|
19
|
+
what `useResource` already did. Unchanged: `resource.state` still closes at retirement, an explicit read by author
|
|
20
|
+
code still fails, and every refusal that is not a close is still reported once.
|
|
21
|
+
- **`Resource.acquire` is a readonly function-valued field**, like `ResourceCommit.update`: the runtime hands out a
|
|
22
|
+
plain function, and it is the one word a consumer holds rather than calls on the spot, so a `useModel` selection may
|
|
23
|
+
name `model.resource.acquire` without tripping `@typescript-eslint/unbound-method` and without a fresh arrow per
|
|
24
|
+
render that would make the selection unequal to itself. The verbs stay methods.
|
|
25
|
+
- **`testResourceEpoch()` on `@opetope/core/testing`** (re-exported by `@opetope/runtime/testing` and
|
|
26
|
+
`@opetope/react/testing`) mints one opaque `ResourceEpoch`, so a test that writes a `ResourceState` by hand no
|
|
27
|
+
longer casts `{} as ResourceEpoch` against the brand.
|
|
28
|
+
|
|
29
|
+
- Updated dependencies [5c7abd5]
|
|
30
|
+
- @opetope/core@0.10.1
|
|
31
|
+
- @opetope/runtime@0.10.1
|
|
32
|
+
|
|
33
|
+
## 0.10.0
|
|
34
|
+
|
|
35
|
+
### Minor Changes
|
|
36
|
+
|
|
37
|
+
- 12abb62: **One Resource family: `state` observes, `acquire` holds (D349-D361).** `ctx.resource(from, target, load, options)`
|
|
38
|
+
and `ctx.stream(...)` are replaced by `ctx.resource.load({ ... })` and `ctx.resource.live({ ... })`, and `own.resource`
|
|
39
|
+
carries the same two forms with an import in `source`. The answer is a `Resource<Data, Key>` that is not a `Readable`:
|
|
40
|
+
`state` is a passive `Readable<ResourceState<Data, Key>>`, `acquire()` takes a lease and returns its release, and
|
|
41
|
+
`refresh()`, `retry()` and `invalidate()` are the verbs. `ResourceSnapshot`, `retain()`, `retention`,
|
|
42
|
+
`StreamBackpressure` (now `EventBackpressure`) and Kit's `projectResourceState` are removed; `derive(resource, ...)`
|
|
43
|
+
no longer compiles, because reading data and asking for it are two rights and one of them is `acquire`.
|
|
44
|
+
|
|
45
|
+
- **The declaration is a record.** `lifetime: 'lease' | 'owner'` is required and there is no default; `identity` names
|
|
46
|
+
`key` and `scope`, and a request that is not an atom must write `key`; `enabled` and `cache` are data on the same
|
|
47
|
+
record. A live connection writes `connect`, positional `apply` reducers and `delivery`, and a one-shot writes
|
|
48
|
+
`load`. `pagination` is a capability of the declaration, so a paginated Resource answers `pagination` with its own
|
|
49
|
+
`state` and `loadNext`.
|
|
50
|
+
- **No automatic recovery (D351).** `retry`/`reconnect` budgets are gone: a rejection is the terminal failure of that
|
|
51
|
+
logical attempt, and a transient one belongs to the source adapter. `delivery` conflates instead of holding a
|
|
52
|
+
backlog, and `ResourceState` carries one `activity` over both channels, so a failed channel behind a live one is
|
|
53
|
+
visible instead of hidden.
|
|
54
|
+
- **React (D359).** `useResource(resource)` answers `{ state, refresh, retry, invalidate, pagination }` and takes the
|
|
55
|
+
lease in a commit effect; a Resource field in a `useModel` selection takes the same lease. `useReadable(resource)`
|
|
56
|
+
is a compile error; `useReadable(resource.state)` stays passive.
|
|
57
|
+
- **Devtools (D358).** The inspection protocol moves to `opetope.runtime-activity/4` with no compatibility branch:
|
|
58
|
+
recreate exports from a matching runtime.
|
|
59
|
+
- **Budgets.** Two ceilings ratchet, both recorded in D361. `public feature consumer` measures 38.63 kB brotli and
|
|
60
|
+
moves 36.5 kB to 39 kB: the pagination capability is 0.82 kB of it, measured by building the same fixture with the
|
|
61
|
+
capability unreachable. Devtools' `react panel consumer` measures 27.29 kB and moves 27.2 kB to 27.3 kB for the
|
|
62
|
+
seven Resource facts of protocol `/4`. Everything else holds: `application graph consumer` 47.34 of 48 kB,
|
|
63
|
+
`@opetope/react` 12.08 of 18 kB, `react coverage help surface` 1.49 of 1.5 kB. Public types ratchet 44 to 55
|
|
64
|
+
against the target of 60, values stay 39, and the Kit bucket ratchets 4/4 to 3/3. No allocation or timing row
|
|
65
|
+
keeps its name and gets a larger number.
|
|
66
|
+
|
|
67
|
+
Migration is written down step by step in `docs/resource-migration.md`; `docs/spec.md` section 2.3 is the normative
|
|
68
|
+
declaration.
|
|
69
|
+
|
|
70
|
+
### Patch Changes
|
|
71
|
+
|
|
72
|
+
- Updated dependencies [12abb62]
|
|
73
|
+
- @opetope/core@0.10.0
|
|
74
|
+
- @opetope/runtime@0.10.0
|
|
75
|
+
|
|
3
76
|
## 0.9.7
|
|
4
77
|
|
|
5
78
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -131,9 +131,10 @@ references. A newly allocated nested object is a changed field. The callback is
|
|
|
131
131
|
A read or selector error reaches the nearest React error boundary. Changing the selected sources or command keys
|
|
132
132
|
changes their subscriptions/consumers at commit; an abandoned render cannot replace committed authority.
|
|
133
133
|
|
|
134
|
-
The hook neither creates a model nor acquires a feature. It
|
|
135
|
-
values — that field answers a `ResourceHook
|
|
136
|
-
|
|
134
|
+
The hook neither creates a model nor acquires a feature. It leases exactly the resources its selection names as
|
|
135
|
+
values — that field answers a `ResourceHook`, and one lease per Resource identity covers the mount even when two
|
|
136
|
+
keys name one Resource (D321, D359) — and nothing else: a
|
|
137
|
+
`read(resource.state)` leases nothing, and neither does the one-argument form. Combining hooks does not promise fewer
|
|
137
138
|
source subscriptions or faster renders; a raw `useModel` import now also includes the selection implementation.
|
|
138
139
|
There is no additional Call scheduler.
|
|
139
140
|
|
|
@@ -210,19 +211,27 @@ frame of the instance that gave the contribution. The order and the stable key c
|
|
|
210
211
|
React introduces neither a second comparator nor a second lifetime registry. The packaged consumer of `ci:pack`
|
|
211
212
|
exercises that caching and routes one contribution through it (D301).
|
|
212
213
|
|
|
213
|
-
`useResource(resource)` answers `{
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
`refresh
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
214
|
+
`useResource(resource)` answers `{ invalidate, pagination, refresh, retry, state }`: the `ResourceState` through
|
|
215
|
+
`useSyncExternalStore`, the lease `resource.acquire()` answers taken in a commit effect for as long as the component
|
|
216
|
+
is mounted, and the three verbs as ordinary `CommandHook` consumers (D359). An interrupted render therefore opens
|
|
217
|
+
nothing, and when the reference changes the state follows the new Resource while the effect cleanup releases the
|
|
218
|
+
previous lease. The number of React hooks does not depend on the Resource: a declaration without `pagination`
|
|
219
|
+
subscribes to a constant source and keeps a `loadNext` that answers `skipped`, while a declaration that carried the
|
|
220
|
+
capability answers `pagination` as `{ loadNext, state }` (D356). A field of a `useModel` selection whose value is a
|
|
221
|
+
Resource answers the same hook and takes the same lease, so a model declares no `Call<void, void>` around
|
|
222
|
+
`resource.refresh()` to give a button its `inFlight`. `useReadable(resource)` is a compile error, because a Resource
|
|
223
|
+
is not a `Readable`; `useReadable(resource.state)` and a `read(resource.state)` in a selector stay passive and lease
|
|
224
|
+
nothing — observing and holding are independent questions, so a selector that reads the state and a `useResource`
|
|
225
|
+
beside it subscribe twice (D349). The compile-checked example of specification §2.13 shows both sides of that
|
|
226
|
+
(D301, D321).
|
|
227
|
+
|
|
228
|
+
`refresh.run()`, `retry.run()` and `invalidate.run()` reach the Resource, which is owned by the model that declared
|
|
229
|
+
it: `inFlight` is true until the outcome of the operation settles, `outcome` carries that `ResourceOperationOutcome`
|
|
230
|
+
as the `value` of an `ok` status, and `run(undefined, { signal })` cancels the wait of this consumer and not the work
|
|
231
|
+
the model owns. Two consumers of one Resource keep separate statuses, and `run` keeps its identity while the Resource
|
|
232
|
+
does. A render that caught up with retirement answers the last state it saw — what `useReadable` does with a source
|
|
233
|
+
its owner closed — and a selection answers a closed record instead of throwing mid-render, so the tree unmounts and
|
|
234
|
+
releases its lease rather than breaking the render (D359).
|
|
226
235
|
|
|
227
236
|
Direct component props, a contribution's `props` adapter and its model's props `Readable` share the same
|
|
228
237
|
mount-owned snapshot. Incoming slot props publish in layout before paint, so direct and adapted renders cannot
|
|
@@ -270,7 +279,7 @@ The synchronous constructor exposes `ready`, so a test can inspect a pending laz
|
|
|
270
279
|
`waitFor(snapshot => predicate, { label, timeoutMs, pollIntervalMs })` wakes on inspection changes and also polls
|
|
271
280
|
external UI predicates; `notify()` wakes it after a controlled fixture update. The default deadline is 1000ms,
|
|
272
281
|
with a 10ms predicate poll. A `ScenarioTimeoutError` carries the data-only snapshot, bounded history and observed
|
|
273
|
-
conditions, feature phases, body loads, lane blockers and resource
|
|
282
|
+
conditions, feature phases, body loads, lane blockers and resource lease facts. It does not infer repository
|
|
274
283
|
or network causes. `getSnapshot()` and `history()` use that same observation model; history defaults to 64 snapshots,
|
|
275
284
|
activity to 256 records. Capacities accept integers from 1 to 10000. Do not replace predicates with a fixed number of ticks.
|
|
276
285
|
|
|
@@ -284,13 +293,14 @@ application imports and internal renderer references. A failed cleanup promise c
|
|
|
284
293
|
capabilities; a caller that keeps `mounted.host` also keeps its own renderer result.
|
|
285
294
|
|
|
286
295
|
The inspection schemas are `opetope.devtools-graph/5` and `opetope.devtools-frame/4`, with optional
|
|
287
|
-
`opetope.runtime-activity/
|
|
296
|
+
`opetope.runtime-activity/4` snapshots (D266, D275, D344, D358). Within one session,
|
|
288
297
|
a frame without `activity` preserves the previous activity; a full snapshot/reset without it clears that observation
|
|
289
298
|
(D216). Activity-bearing frames replace the previous activity in full.
|
|
290
|
-
Use matching runtime/devtools versions: previous graph/frame revisions are rejected; weak-edge presence requires graph `/5` and frame `/4`. Activity identifies the execution,
|
|
291
|
-
actual feature generation, physical Calls, exact current lane blockers,
|
|
292
|
-
|
|
293
|
-
|
|
299
|
+
Use matching runtime/devtools versions: previous graph/frame and activity revisions are rejected; weak-edge presence requires graph `/5` and frame `/4`. Activity identifies the execution,
|
|
300
|
+
actual feature generation, physical Calls, exact current lane blockers, and for every registered Resource its
|
|
301
|
+
`kind`, `lifetime`, `epoch`, `state`, `activity` with its `operation`, `leases`, `subscribers`, `pagination` and the
|
|
302
|
+
`epoch` of each physical load — there is no `attempt` and no numeric generation, because a Resource runs one logical
|
|
303
|
+
attempt and recovery belongs to the transport (D358). Host demand and UI models are unknown. `freshness`
|
|
294
304
|
and `truncated` distinguish a complete live view from a partial or detached one. A closed session is stale;
|
|
295
305
|
`closed: true` requires successful physical application drain. No control authority or product payload is added.
|
|
296
306
|
Activity output is bounded by record capacity. Snapshot collection still visits registered owners, executors and
|
|
@@ -307,8 +317,9 @@ observation disabled. Graph frames remain bounded by the existing ring capacity.
|
|
|
307
317
|
- `renderSlot(target, { contribution, models, props })` mounts the published target or one fixture contribution;
|
|
308
318
|
- `command(run)` produces an authentic `Call` for a model fixture, plus the contribution binding a mount reads;
|
|
309
319
|
- `runCall(target, input?, options?)` executes an existing authentic `Call` without mounting React (D261);
|
|
310
|
-
- `testReadable(initial)`, `openModel`, `settled`, `waitFor`, `yieldTurn` and `eventually`
|
|
311
|
-
below, for the part of a test that has no UI in it
|
|
320
|
+
- `testReadable(initial)`, `testResourceEpoch()`, `openModel`, `settled`, `waitFor`, `yieldTurn` and `eventually`
|
|
321
|
+
come from the entries below, for the part of a test that has no UI in it — `testResourceEpoch()` is what a
|
|
322
|
+
component fixture writing a `ResourceState` by hand puts in its `epoch` (D362).
|
|
312
323
|
|
|
313
324
|
A test that renders nothing should import `@opetope/runtime/testing` directly: `renderSlot` and the binding half of
|
|
314
325
|
`command` are all that needs React here. The Call a fixture mints is the `command` of `@opetope/core/testing`
|
|
@@ -362,11 +373,11 @@ Generic helpers may forward the target and its explicit input without losing the
|
|
|
362
373
|
|
|
363
374
|
## Word map and entries
|
|
364
375
|
|
|
365
|
-
| Entry | What it holds
|
|
366
|
-
| ---------------------------- |
|
|
367
|
-
| `@opetope/react` | `useModel`, `useCommand`, `useCommands`, `useReadable`, `useSelector`, `useResource`, `requiresModels`, `defineSlot`, `defineSwitchSlot`, `Slot`, `ContributionError`; types `SlotTarget`, `SwitchSlotTarget`, `SlotContribution`, `CommandHook`, `CommandOutcome`, `ResourceHook`
|
|
368
|
-
| `@opetope/react/integration` | `FeatureBoundary`, `ContributionBoundary`, `useFeature`, `useFeatureRetry`, `FeatureBoundaryError` for the integration layer; types `FeatureBoundaryProps`, `ContributionBoundaryProps`, `ContributionErrorContent`, `ContributionFailure`, `FeatureDemandSource`, `FeatureDemandState`, `FeatureDemandResult`, `FeatureDemandLease`
|
|
369
|
-
| `@opetope/react/testing` | `renderSlot`, `command`, `createScenario`, `ScenarioTimeoutError` and fixture/scenario types, plus everything `@opetope/runtime/testing` and `@opetope/core/testing` publish; tests only
|
|
376
|
+
| Entry | What it holds |
|
|
377
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
378
|
+
| `@opetope/react` | `useModel`, `useCommand`, `useCommands`, `useReadable`, `useSelector`, `useResource`, `requiresModels`, `defineSlot`, `defineSwitchSlot`, `Slot`, `ContributionError`; types `SlotTarget`, `SwitchSlotTarget`, `SlotContribution`, `CommandHook`, `CommandOutcome`, `ResourceHook`, `PaginatedResourceHook`, `ResourcePaginationHook` |
|
|
379
|
+
| `@opetope/react/integration` | `FeatureBoundary`, `ContributionBoundary`, `useFeature`, `useFeatureRetry`, `FeatureBoundaryError` for the integration layer; types `FeatureBoundaryProps`, `ContributionBoundaryProps`, `ContributionErrorContent`, `ContributionFailure`, `FeatureDemandSource`, `FeatureDemandState`, `FeatureDemandResult`, `FeatureDemandLease` |
|
|
380
|
+
| `@opetope/react/testing` | `renderSlot`, `command`, `createScenario`, `ScenarioTimeoutError` and fixture/scenario types, plus everything `@opetope/runtime/testing` and `@opetope/core/testing` publish; tests only |
|
|
370
381
|
|
|
371
382
|
`Call<Input, Output>` is imported from `@opetope/core`; UI `CommandOutcome<Output>` is imported from `@opetope/react`. The former `Command` synonym and the `CommandOutcome` re-export from `@opetope/react/integration` are removed (D270).
|
|
372
383
|
|
package/README.ru.md
CHANGED
|
@@ -131,9 +131,10 @@ return (
|
|
|
131
131
|
без hooks, команд и side effects. Ошибка read/selector попадает в ближайший React error boundary.
|
|
132
132
|
Изменение источников и ключей команд применяется в commit; abandoned render не меняет действующие полномочия.
|
|
133
133
|
|
|
134
|
-
Hook не создаёт модель и не приобретает feature. Он
|
|
135
|
-
значениями: такое поле отвечает `ResourceHook
|
|
136
|
-
|
|
134
|
+
Hook не создаёт модель и не приобретает feature. Он арендует ровно те ресурсы, которые выборка назвала
|
|
135
|
+
значениями: такое поле отвечает `ResourceHook`, и одна аренда на identity Resource покрывает всё монтирование, даже
|
|
136
|
+
когда один Resource назвали два ключа (D321, D359). Больше ничего:
|
|
137
|
+
`read(resource.state)` не арендует, и однопараметрическая форма тоже. Объединение hooks не обещает меньше подписок или
|
|
137
138
|
более быстрый render; импорт обычного `useModel` теперь также включает реализацию selection. Дополнительного
|
|
138
139
|
scheduler для Call нет.
|
|
139
140
|
|
|
@@ -209,19 +210,27 @@ function HomePageEnd() {
|
|
|
209
210
|
React не вводит второй компаратор и второй реестр времени жизни. Упакованный потребитель `ci:pack` проверяет это
|
|
210
211
|
кеширование и проводит через него один вклад (D301).
|
|
211
212
|
|
|
212
|
-
`useResource(resource)` отвечает `{
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
`refresh
|
|
221
|
-
не
|
|
222
|
-
|
|
223
|
-
`
|
|
224
|
-
|
|
213
|
+
`useResource(resource)` отвечает `{ invalidate, pagination, refresh, retry, state }`: `ResourceState` через
|
|
214
|
+
`useSyncExternalStore`, арендой, которую отдаёт `resource.acquire()`, взятой в commit-эффекте на всё время
|
|
215
|
+
монтирования компонента, и тремя глаголами как обычными потребителями `CommandHook` (D359). Поэтому прерванный
|
|
216
|
+
render не открывает ничего, а при смене ссылки состояние следует новому Resource, и cleanup эффекта освобождает
|
|
217
|
+
предыдущую аренду. Число React-хуков от Resource не зависит: объявление без `pagination` подписывается на
|
|
218
|
+
постоянный источник и держит `loadNext`, который отвечает `skipped`, а объявление, несшее эту capability, отвечает
|
|
219
|
+
`pagination` записью `{ loadNext, state }` (D356). Поле выборки `useModel`, значением которого является Resource,
|
|
220
|
+
отвечает тем же хуком и берёт ту же аренду, поэтому модель не объявляет `Call<void, void>` вокруг
|
|
221
|
+
`resource.refresh()` ради `inFlight` в кнопке. `useReadable(resource)` — это ошибка компиляции, потому что Resource
|
|
222
|
+
не `Readable`; `useReadable(resource.state)` и `read(resource.state)` в селекторе остаются пассивными и не арендуют
|
|
223
|
+
ничего: наблюдать и держать — независимые вопросы, поэтому селектор, читающий состояние, и стоящий рядом
|
|
224
|
+
`useResource` подписываются дважды (D349). Обе стороны показывает компилируемый пример спецификации §2.13
|
|
225
|
+
(D301, D321).
|
|
226
|
+
|
|
227
|
+
`refresh.run()`, `retry.run()` и `invalidate.run()` доходят до Resource, которым владеет объявившая его модель:
|
|
228
|
+
`inFlight` истинен, пока не осел исход операции, `outcome` несёт этот `ResourceOperationOutcome` значением `value`
|
|
229
|
+
при статусе `ok`, а `run(undefined, { signal })` отменяет ожидание этого потребителя, а не работу, которой владеет
|
|
230
|
+
модель. Два потребителя одного Resource держат раздельные статусы, а `run` сохраняет ссылку, пока та же
|
|
231
|
+
идентичность Resource. Render, догнавший retirement, отвечает последним виденным состоянием — тем же, что делает
|
|
232
|
+
`useReadable` с источником, закрытым его владельцем, — а выборка отвечает закрытой записью вместо падения посреди
|
|
233
|
+
render-а, поэтому дерево размонтируется и отпустит аренду, а не сломает рендер (D359).
|
|
225
234
|
|
|
226
235
|
Прямые пропсы компонента, `props`-адаптер вклада и props-`Readable` его модели используют один снимок
|
|
227
236
|
монтирования. Входящие пропсы слота публикуются в layout до paint, поэтому прямой и адаптированный рендеры
|
|
@@ -269,7 +278,7 @@ inspection session (D206, D215). Передайте обычные `imports`/`co
|
|
|
269
278
|
`waitFor(snapshot => predicate, { label, timeoutMs, pollIntervalMs })` просыпается от inspection и дополнительно
|
|
270
279
|
опрашивает predicates внешнего UI; `notify()` будит его после изменения управляемой fixture. По умолчанию deadline
|
|
271
280
|
равен 1000ms, polling predicate — 10ms. `ScenarioTimeoutError` содержит data-only snapshot, ограниченную историю и
|
|
272
|
-
наблюдаемые conditions, фазы feature, body load, lane blockers и
|
|
281
|
+
наблюдаемые conditions, фазы feature, body load, lane blockers и аренды ресурсов. Причины внутри repository
|
|
273
282
|
или сети не выводятся из догадок. `getSnapshot()` и `history()` используют ту же модель наблюдения; по умолчанию
|
|
274
283
|
хранятся 64 снимка, activity ограничена 256 записями. Capacities — целые от 1 до 10000.
|
|
275
284
|
Не заменяйте predicates фиксированным числом ticks.
|
|
@@ -284,13 +293,15 @@ host/UI/GC-утечек. Успешный cleanup очищает imports при
|
|
|
284
293
|
cleanup может удерживать исходные ошибки и retry capabilities; сохранённый пользователем `mounted.host` удерживает его renderer result.
|
|
285
294
|
|
|
286
295
|
Inspection использует схемы `opetope.devtools-graph/5` и `opetope.devtools-frame/4`, а также optional snapshots
|
|
287
|
-
`opetope.runtime-activity/
|
|
296
|
+
`opetope.runtime-activity/4` (D266, D275, D344, D358). В пределах одной session
|
|
288
297
|
frame без `activity` сохраняет предыдущую activity; полный snapshot/reset без этого поля очищает наблюдение (D216).
|
|
289
298
|
Frame с `activity` заменяет предыдущую activity целиком.
|
|
290
|
-
Используйте согласованные версии runtime/devtools: предыдущие ревизии
|
|
291
|
-
фактическое поколение feature, физические Calls, точных текущих lane blockers,
|
|
292
|
-
|
|
293
|
-
|
|
299
|
+
Используйте согласованные версии runtime/devtools: предыдущие ревизии графа, кадров и activity отвергаются; presence слабого ребра требует graph `/5` и frame `/4`. Activity указывает execution,
|
|
300
|
+
фактическое поколение feature, физические Calls, точных текущих lane blockers, а для каждого зарегистрированного
|
|
301
|
+
Resource — его `kind`, `lifetime`, `epoch`, `state`, `activity` вместе с `operation`, `leases`, `subscribers`,
|
|
302
|
+
`pagination` и `epoch` каждой физической загрузки: ни `attempt`, ни числового generation здесь нет, потому что
|
|
303
|
+
Resource делает одну логическую попытку, а восстановление принадлежит транспорту (D358). Спрос хоста и UI-модели
|
|
304
|
+
неизвестны. `freshness`
|
|
294
305
|
и `truncated` отличают полное live-наблюдение от усечённого или отключённого. Закрытая session имеет stale-снимок;
|
|
295
306
|
`closed: true` требует успешного физического drain приложения. Control authority и продуктовые payload не добавляются.
|
|
296
307
|
Размер activity ограничен capacity записей. Сбор снимка обходит зарегистрированных owners, executors и resources,
|
|
@@ -307,8 +318,9 @@ idle executors могут требовать обхода, чтобы подтв
|
|
|
307
318
|
- `renderSlot(target, { contribution, models, props })` монтирует опубликованную цель либо один вклад-фикстуру;
|
|
308
319
|
- `command(run)` выдаёт подлинный `Call` для фикстуры модели плюс привязку вклада, которую читает монтирование;
|
|
309
320
|
- `runCall(target, input?, options?)` запускает существующий подлинный `Call` без монтирования React (D261);
|
|
310
|
-
- `testReadable(initial)`, `openModel`, `settled`, `waitFor`, `yieldTurn` и `eventually`
|
|
311
|
-
для той части теста, в которой UI
|
|
321
|
+
- `testReadable(initial)`, `testResourceEpoch()`, `openModel`, `settled`, `waitFor`, `yieldTurn` и `eventually`
|
|
322
|
+
приходят из входов ниже — для той части теста, в которой UI нет; `testResourceEpoch()` это то, что фикстура
|
|
323
|
+
компонента, собирающая `ResourceState` руками, кладёт в её `epoch` (D362).
|
|
312
324
|
|
|
313
325
|
Тест, который ничего не рендерит, должен импортировать `@opetope/runtime/testing` напрямую: React здесь нужен
|
|
314
326
|
только `renderSlot` и привязочной половине `command`. Сам Call фикстуры чеканит `command` из
|
|
@@ -362,11 +374,11 @@ UI-статус, обёртку outcome, политику выполнения,
|
|
|
362
374
|
|
|
363
375
|
## Карта слов и входы
|
|
364
376
|
|
|
365
|
-
| Вход | Что содержит
|
|
366
|
-
| ---------------------------- |
|
|
367
|
-
| `@opetope/react` | `useModel`, `useCommand`, `useCommands`, `useReadable`, `useSelector`, `useResource`, `requiresModels`, `defineSlot`, `defineSwitchSlot`, `Slot`, `ContributionError`; типы `SlotTarget`, `SwitchSlotTarget`, `SlotContribution`, `CommandHook`, `CommandOutcome`, `ResourceHook`
|
|
368
|
-
| `@opetope/react/integration` | `FeatureBoundary`, `ContributionBoundary`, `useFeature`, `useFeatureRetry`, `FeatureBoundaryError` для integration-слоя; типы `FeatureBoundaryProps`, `ContributionBoundaryProps`, `ContributionErrorContent`, `ContributionFailure`, `FeatureDemandSource`, `FeatureDemandState`, `FeatureDemandResult`, `FeatureDemandLease`
|
|
369
|
-
| `@opetope/react/testing` | `renderSlot`, `command`, `createScenario`, `ScenarioTimeoutError` и типы fixtures/scenarios плюс всё, что публикуют `@opetope/runtime/testing` и `@opetope/core/testing`; только для тестов
|
|
377
|
+
| Вход | Что содержит |
|
|
378
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
379
|
+
| `@opetope/react` | `useModel`, `useCommand`, `useCommands`, `useReadable`, `useSelector`, `useResource`, `requiresModels`, `defineSlot`, `defineSwitchSlot`, `Slot`, `ContributionError`; типы `SlotTarget`, `SwitchSlotTarget`, `SlotContribution`, `CommandHook`, `CommandOutcome`, `ResourceHook`, `PaginatedResourceHook`, `ResourcePaginationHook` |
|
|
380
|
+
| `@opetope/react/integration` | `FeatureBoundary`, `ContributionBoundary`, `useFeature`, `useFeatureRetry`, `FeatureBoundaryError` для integration-слоя; типы `FeatureBoundaryProps`, `ContributionBoundaryProps`, `ContributionErrorContent`, `ContributionFailure`, `FeatureDemandSource`, `FeatureDemandState`, `FeatureDemandResult`, `FeatureDemandLease` |
|
|
381
|
+
| `@opetope/react/testing` | `renderSlot`, `command`, `createScenario`, `ScenarioTimeoutError` и типы fixtures/scenarios плюс всё, что публикуют `@opetope/runtime/testing` и `@opetope/core/testing`; только для тестов |
|
|
370
382
|
|
|
371
383
|
`Call<Input, Output>` импортируется из `@opetope/core`, а UI-результат `CommandOutcome<Output>` — из `@opetope/react`. Прежний синоним `Command` и переэкспорт `CommandOutcome` из `@opetope/react/integration` удалены (D270).
|
|
372
384
|
|
package/dist/commands-hook.d.ts
CHANGED
|
@@ -7,11 +7,16 @@ type HookOf<Command> = Command extends Call<infer Input, infer Output> ? Command
|
|
|
7
7
|
type CommandsHook<Commands> = {
|
|
8
8
|
readonly [Key in keyof Commands]: HookOf<Commands[Key]>;
|
|
9
9
|
};
|
|
10
|
-
/**
|
|
10
|
+
/** D359: what one selected Resource answers — its three verbs, its state, and the pagination capability if it has one. */
|
|
11
11
|
type SelectedResourceHook = Readonly<{
|
|
12
|
+
invalidate: CommandHook<never, unknown>;
|
|
13
|
+
pagination: Readonly<{
|
|
14
|
+
loadNext: CommandHook<never, unknown>;
|
|
15
|
+
state: unknown;
|
|
16
|
+
}> | undefined;
|
|
12
17
|
refresh: CommandHook<never, unknown>;
|
|
13
18
|
retry: CommandHook<never, unknown>;
|
|
14
|
-
|
|
19
|
+
state: unknown;
|
|
15
20
|
}>;
|
|
16
21
|
type SelectionHooks = Readonly<Record<PropertyKey, CommandHook<never, unknown> | SelectedResourceHook>>;
|
|
17
22
|
/** The consumers of one selection: a command per key, and the two verbs of a resource per key (D321). */
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{jsx as m}from"react/jsx-runtime";import{useContext as T,createContext as V,useCallback as S,useRef as O,useMemo as p,useSyncExternalStore as k,useEffect as D,useInsertionEffect as A,useLayoutEffect as C,memo as N,useState as W}from"react";import{reportDetachedError as w,compatibleAbortReason as K,isClosedReadable as _,isCallTarget as G,isModel as H,isReadable as j,createLifetimeController as J,createCallController as Q,getContributionOwner as U,createState as X,createAggregateError as Y}from"@opetope/core/internal";import{isResourceValue as Z,reportRuntimeFailure as ee,requireContributionModelPlans as te,openContributionModels as ne}from"@opetope/runtime/internal";import{C as u,u as re,b as ie}from"./contribution-isolation-Bzfbt4iM.js";import{CallError as oe}from"@opetope/core";const x=V(void 0);function se(){const t=T(x);if(t===void 0)throw new u("missing","Opetope hook was called outside a contribution mount.");return t}function y(t){return Z(t)}function ae(t,e){return new Promise((n,r)=>{const i=()=>{r(new oe("cancelled","Resource operation wait was cancelled.",K(e)))};if(e.aborted){i();return}e.addEventListener("abort",i,{once:!0}),t.then(o=>{e.removeEventListener("abort",i),n(o)},o=>{e.removeEventListener("abort",i),r(o)})})}function ce(t,e){var r;const n=(r=e==null?void 0:e.onSuccess)==null?void 0:r.call(e,t);return n!==void 0&&Promise.resolve(n).catch(w),t}function v(t){return{run:(e,n)=>{let r;try{r=Promise.resolve(t())}catch(o){return Promise.reject(o)}const i=n==null?void 0:n.signal;return(i===void 0?r:ae(r,i)).then(o=>ce(o,n))}}}const de=Object.freeze({kind:"skipped",reason:"resource-not-ready"});function ue(t){const e=t.pagination;if(!(typeof e!="object"||e===null))return e}function E(t){return{invalidate:v(()=>t.invalidate()),loadNext:v(()=>{const e=t.pagination;return e===void 0?Promise.resolve(de):e.loadNext()}),refresh:v(()=>t.refresh()),retry:v(()=>t.retry())}}function le(t,e){const n=e.get(t);if(n!==void 0)return n;const r=E(t);return e.set(t,r),r}const fe=t=>t;function he(t){let e,n=!1;return()=>{try{return e=t(),n=!0,e}catch(r){if(!n||!_(r))throw r;return e}}}function F(t,e,n){const r=e,i=(n==null?void 0:n.equals)??Object.is,o=S(d=>t.subscribe(d),[t]),c=O(void 0);let s=c.current;s===void 0&&(s={hasValue:!1,value:void 0},c.current=s);const a=p(()=>{let d=!1,h,f;return he(()=>{const b=t.getSnapshot();if(d&&Object.is(h,b))return f;const g=r(b);if(!d){let R=g;return s.hasValue&&i(s.value,g)&&(R=s.value),d=!0,h=b,f=R,f}return i(f,g)?(h=b,f):(h=b,f=g,f)})},[s,i,t,r]),l=k(o,a,a);return D(()=>{s.hasValue=!0,s.value=l},[s,l]),l}function z(t){return F(t,fe)}const M=new WeakMap;function I(t){if(arguments.length>1)throw new u("binding-invalid","Command follows the target it binds; binding options are forbidden.");if(!G(t))throw new u("binding-invalid","Command target is not authentic.");return M.set(t,{target:t}),t}function me(t){const e=M.get(t);if(e===void 0)throw new u("binding-invalid","Command binding is not authentic.");return e}function L(t){return typeof t=="object"&&t!==null&&M.has(t)}const be=Symbol("opetope.model-binding"),pe=Symbol("opetope.model-binding-identity"),ge=Object.freeze({}),$=new WeakMap;function Ce(t,e,n){if(typeof n!="string")throw new u("binding-invalid",`Model ${t.id} accepts only string fields.`);const r=Object.getOwnPropertyDescriptor(e,n);if((r==null?void 0:r.enumerable)!==!0||!("value"in r))throw new u("binding-invalid",`Model ${t.id}.${n} requires an enumerable data binding.`);return r.value}function we(t,e){const n=[],r=Reflect.ownKeys(e);if(r.length===0)throw new u("binding-invalid",`Model ${t.id} requires at least one field.`);for(const i of r){const o=Ce(t,e,i);if(!(j(o)||y(o)||typeof o=="function")){if(!L(o))throw new u("binding-invalid",`Model ${t.id}.${String(i)} requires a Readable, a Resource or a bound command.`);n.push(o)}}return Object.freeze(n)}function q(t,e){if(!H(t))throw new u("binding-invalid","Model declaration is not authentic.");if(typeof e!="object"||e===null)throw new u("binding-invalid",`Model ${t.id} requires a record binding.`);const n=we(t,e),r=Object.freeze({[be]:ge,[pe]:!0});return $.set(r,{commands:n,declaration:t,model:e}),r}function ve(t){const e=$.get(t);if(e===void 0)throw new u("binding-invalid","Model binding is not authentic.");return e}let ye=0;function Me(t){const e=new Map,n=[],r=new Set;for(const i of t){const o=ve(i);if(e.has(o.declaration))throw new u("duplicate",`Model ${o.declaration.id} is bound twice in one mount.`);e.set(o.declaration,o.model);for(const c of o.commands)r.has(c)||(r.add(c),n.push(c))}return{commands:Object.freeze(n),frame:{commandRecords:new Map,models:e}}}class Re{frame;authority;boundCommands;closeTicket=0;isActive;lifetime;onClose;released=!1;rendererAttached=!1;renderListeners=new Set;state="detached";constructor(e,n){const r=++ye;this.lifetime=J({id:`contribution.${String(r)}`}),this.isActive=n==null?void 0:n.isActive,this.onClose=n==null?void 0:n.onClose;const{commands:i,frame:o}=Me(e);this.frame=o,this.boundCommands=i,this.buildCommandRecords(r)}attach(){if(this.state==="closing")return this.closeTicket+=1,this.state="active",this.rendererAttached=!0,!0;if(this.state==="closed")return!1;if(this.state==="active")throw new u("duplicate","One contribution mount cannot mount twice.");const e=this.createAuthority(this.lifetime.open());try{for(const n of this.frame.commandRecords.values())n.controller.activate()}catch(n){throw this.authority=e,this.state="active",this.close(),n}return this.authority=e,this.rendererAttached=!0,this.state="active",!0}close(){if(this.state==="closed")return;this.state="closed",this.closeTicket+=1,this.lifetime.close(),this.authority=void 0;for(const n of this.frame.commandRecords.values())n.controller.close();const e=this.onClose;this.onClose=void 0,e==null||e(),this.publishRenderable(),this.rendererAttached||this.releaseFrame()}detach(){if(!this.rendererAttached)return;if(this.rendererAttached=!1,this.state==="closed"){this.releaseFrame();return}if(this.state!=="active")return;this.state="closing";const e=++this.closeTicket;queueMicrotask(()=>{this.state==="closing"&&e===this.closeTicket&&this.close()})}diagnostics(){const e=[...this.frame.commandRecords.values()].map(n=>n.controller.diagnostics());return Object.freeze({active:this.state==="active",calls:Object.freeze(e),commandRecords:this.frame.commandRecords.size,models:this.frame.models.size})}getRenderableSnapshot=()=>this.state!=="closed"&&(this.isActive===void 0||this.isActive());releaseClosedFrame(){this.state==="closed"&&this.releaseFrame()}subscribeRenderable=e=>{if(this.state==="closed")return()=>{};const n={listener:e};this.renderListeners.add(n);let r=!0;return()=>{r&&(r=!1,this.renderListeners.delete(n))}};buildCommandRecords(e){let n=0;for(const r of this.boundCommands){me(r);const i=Q(r,{captureAuthority:this.captureAuthority,id:`contributionCommand.${String(e)}.${String(++n)}`,scheduling:{policy:"parallel"}});this.frame.commandRecords.set(r,{controller:i,invoker:Object.freeze({run:i.run})})}}captureAuthority=()=>{const e=this.authority;if(this.state!=="active"||e===void 0)throw new u("inactive","Contribution mount is not active.");return e.assertCurrent(),e};createAuthority(e){return Object.freeze({assertCurrent:()=>{if(this.state!=="active")throw new u("inactive","Contribution mount is not active.");e.assertCurrent()},isCurrent:()=>this.state==="active"&&e.isCurrent()})}publishRenderable(){for(const{listener:e}of[...this.renderListeners])try{e()}catch(n){w(n)}}releaseFrame(){this.released||(this.released=!0,this.frame.commandRecords.clear(),this.frame.models.clear(),this.boundCommands=[],this.renderListeners.clear())}}function Se({children:t,mount:e}){const n=k(e.subscribeRenderable,e.getRenderableSnapshot,e.getRenderableSnapshot);return A(()=>{if(e.attach())return()=>e.detach()},[e]),C(()=>{n||e.releaseClosedFrame()},[e,n]),n?m(x,{value:e.frame,children:t}):null}function B(t){const e=t.bundle;t.bundle=void 0,e==null||e.frame.close()}function Oe(t,e){if(typeof e!="object"||e===null)return q(t,e);const n=i=>j(i)||y(i)||typeof i=="function"||L(i),r=Object.entries(e).map(([i,o])=>[i,n(o)?o:I(o)]);return q(t,Object.fromEntries(r))}function ke(t){return U(t)}const Ae={models:[]};function P(t,e){try{t()}catch(n){throw Y([e,n],"Contribution mount setup and cleanup failed.",{cause:e})}throw e}function je(t,e,n,r){const i=X(e),{models:o,reporter:c}=r??Ae;try{const s=te(t.models),a=s.length===0?void 0:ne(s,i,c);return{bundle:a,models:[...o,...(a==null?void 0:a.models)??[],...n],props:i}}catch(s){return P(i.close,s)}}function xe(t,e,n,r){const{bundle:i,models:o,props:c}=je(t,e,n,r);let s=!1;const a=()=>{if(!s){s=!0;try{i==null||i.close().catch(w)}finally{c.close()}}};try{return{frame:new Re(o.map(([d,h])=>Oe(d,h)),{isActive:()=>!s,onClose:a}),isOpen:()=>!s,props:c}}catch(l){return P(a,l)}}function Ee(t,e){return m(t,{...e})}function Fe({bundle:t,contribution:e}){const n=z(t.props),r=p(()=>e.props===void 0?n:e.props(n),[n,e]);return m(Se,{mount:t.frame,children:Ee(e.Component,r)})}function ze({authority:t,contribution:e,fixtures:n,slotProps:r}){const i=p(()=>({authority:t,bundle:void 0,contribution:e,fixtures:n,retired:!1}),[t,e,n]),[o,c]=W(void 0),s=(o==null?void 0:o.owner)===i?o.bundle:void 0,a=O(r);return C(()=>{a.current=r}),A(()=>()=>{i.retired=!0,queueMicrotask(()=>{try{B(i)}catch(l){w(l)}})},[i]),C(()=>{const l=i.bundle??(i.bundle=xe(i.contribution,a.current,i.fixtures??[],i.authority));return c(d=>(d==null?void 0:d.owner)===i?d:{bundle:l,owner:i}),()=>{i.retired&&B(i)}},[i]),C(()=>{(s==null?void 0:s.isOpen())===!0&&s.props.set(r)},[s,r]),s===void 0?null:m(Fe,{bundle:s,contribution:e})}function Ie(t){const{authority:e,contribution:n,contributionId:r,fixtures:i,targetId:o}=t,c=re(),s=p(()=>({authority:e,contribution:n,fixtures:i}),[e,n,i]),a=e==null?void 0:e.feature,l=p(()=>({contribution:r,feature:a,target:o}),[r,a,o]),d=e==null?void 0:e.reporter,h=S(f=>ee(f,d),[d]);return m(ie,{content:c,identity:s,report:h,source:l,children:m(ze,{...t})})}const Le=N(Ie);export{Le as M,z as a,ke as b,E as c,F as d,I as e,y as i,ue as p,le as r,se as u};
|
|
2
|
+
//# sourceMappingURL=contribution-frame-QRJSP2W3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contribution-frame-QRJSP2W3.js","sources":["../src/mount-context.tsx","../src/resource-command.ts","../src/readable-hooks.ts","../src/command.ts","../src/model-binding.ts","../src/mount-frame.ts","../src/mount-provider.tsx","../src/contribution-frame.tsx"],"sourcesContent":["import { createContext, useContext } from 'react';\n\nimport { ContributionError } from './errors';\nimport type { Frame } from './mount-frame';\n\nconst FrameContext = createContext<Frame | undefined>(undefined);\n\nfunction useFrame(): Frame {\n const frame = useContext(FrameContext);\n\n if (frame === undefined) {\n throw new ContributionError('missing', 'Opetope hook was called outside a contribution mount.');\n }\n\n return frame;\n}\n\nexport { FrameContext, useFrame };\n","/**\n * D359: the verbs of a Resource, dressed as the commands a button already knows, and the recognition that answers\n * «is this selected field a Resource». A consumer keeps a status — `inFlight` until the operation settles, the\n * settled `outcome` — while the Resource stays owned by the model that declared it: cancelling this wait cancels the\n * wait and not the load.\n */\n\nimport { CallError } from '@opetope/core';\nimport type {\n PaginationOutcome,\n PaginationState,\n Resource,\n ResourceOperationOutcome,\n ResourceState,\n} from '@opetope/core';\nimport { compatibleAbortReason, reportDetachedError } from '@opetope/core/internal';\nimport type { CallRunOptions, ResourceKey } from '@opetope/core/internal';\nimport { isResourceValue } from '@opetope/runtime/internal';\n\nimport type { CommandHook } from './command-hook';\nimport type { CommandInvoker } from './command-hook-controller';\n\ntype AnyResource = Resource<never, ResourceKey>;\n\n/**\n * D349, D359: a Resource is a structural contract a host may implement with an ordinary object, so the question the\n * type of a selected field asked is answered by the runtime that owns it — the registry of the materializations it\n * built, and the structural contract for everything else. Asking it here a second time would let the two answers\n * drift, and a host adapter typed as a hook reaches the render where `state` is `undefined` and `refresh.run()`\n * throws.\n */\nfunction isResource(value: unknown): boolean {\n return isResourceValue(value);\n}\n\n/** D356: the nested capability, with `loadNext` as a command and the projected pagination state beside it. */\ntype ResourcePaginationHook<Cursor extends ResourceKey> = Readonly<{\n loadNext: CommandHook<void, PaginationOutcome<Cursor>>;\n state: PaginationState<Cursor>;\n}>;\n\n/**\n * D359: what `useResource` answers, and what a field of a `useModel` selection whose value is a Resource answers.\n * The verbs never reject (a Resource answers an outcome), so the ordinary path of `outcome` is\n * `{ status: 'ok', value }`; `failed` stays in the type because the controller turns a synchronous throw of an\n * invoker into it, and the wrapper below carries its own test that it never throws synchronously.\n */\ntype ResourceHook<Data, Key extends ResourceKey = never> = Readonly<{\n invalidate: CommandHook<void, ResourceOperationOutcome>;\n /** Present when the declaration carried `pagination`; `undefined` for every other Resource. */\n pagination: ResourcePaginationHook<ResourceKey> | undefined;\n refresh: CommandHook<void, ResourceOperationOutcome>;\n retry: CommandHook<void, ResourceOperationOutcome>;\n state: ResourceState<Data, Key>;\n}>;\n\ntype PaginatedResourceHook<Data, Key extends ResourceKey, Cursor extends ResourceKey> = Omit<\n ResourceHook<Data, Key>,\n 'pagination'\n> &\n Readonly<{ pagination: ResourcePaginationHook<Cursor> }>;\n\ntype ResourceInvokers = Readonly<{\n invalidate: CommandInvoker;\n loadNext: CommandInvoker;\n refresh: CommandInvoker;\n retry: CommandInvoker;\n}>;\n\n/** The consumer's own cancellation: it ends this wait, and the work the model owns keeps running. */\nfunction awaitCancellable(operation: Promise<unknown>, signal: AbortSignal): Promise<unknown> {\n return new Promise<unknown>((resolve, reject) => {\n const cancel = (): void => {\n reject(new CallError('cancelled', 'Resource operation wait was cancelled.', compatibleAbortReason(signal)));\n };\n\n if (signal.aborted) {\n cancel();\n\n return;\n }\n\n signal.addEventListener('abort', cancel, { once: true });\n operation.then(\n value => {\n signal.removeEventListener('abort', cancel);\n resolve(value);\n },\n (error: unknown) => {\n signal.removeEventListener('abort', cancel);\n reject(error);\n },\n );\n });\n}\n\nfunction notifySuccess(value: unknown, options: CallRunOptions<unknown> | undefined): unknown {\n const notification = options?.onSuccess?.(value);\n\n // The failure of a consumer's own callback is detached from the operation it watched, as it is for a command.\n if (notification !== undefined) void Promise.resolve(notification).catch(reportDetachedError);\n\n return value;\n}\n\n/**\n * The invariant of this wrapper: it never throws synchronously, so the `failed` outcome a consumer can see is the one\n * the verb itself produced and never one this layer invented in the middle of a render commit.\n */\nfunction operationInvoker(start: () => Promise<unknown>): CommandInvoker {\n return {\n run: (_input: never, options?: CallRunOptions<unknown>): Promise<unknown> => {\n let operation: Promise<unknown>;\n\n try {\n operation = Promise.resolve(start());\n } catch (error) {\n return Promise.reject(error);\n }\n\n const signal = options?.signal;\n\n return (signal === undefined ? operation : awaitCancellable(operation, signal)).then(value =>\n notifySuccess(value, options),\n );\n },\n };\n}\n\n/** A Resource without the capability still answers a `loadNext` invoker, so the hook count never moves. */\nconst unavailablePage: PaginationOutcome<ResourceKey> = Object.freeze({\n kind: 'skipped',\n reason: 'resource-not-ready',\n});\n\nfunction paginationOf(resource: AnyResource): ResourcePaginationHook<ResourceKey> | undefined {\n const capability = (resource as { readonly pagination?: unknown }).pagination;\n\n if (typeof capability !== 'object' || capability === null) return undefined;\n\n return capability as unknown as ResourcePaginationHook<ResourceKey>;\n}\n\n/**\n * One set per Resource identity. `selectEntry` and `useCommandSlot` both pick their controller by the identity of the\n * invoker, so a set rebuilt on every render would reset `inFlight`, drop `outcome` and change `run` — which is why it\n * is memoised against the Resource itself and never assembled during a render.\n */\nfunction createResourceInvokers(resource: AnyResource): ResourceInvokers {\n // D349: a Resource is a structural contract, so every verb is called through it and never as a detached function.\n return {\n invalidate: operationInvoker(() => resource.invalidate()),\n loadNext: operationInvoker(() => {\n const capability = (resource as { readonly pagination?: { loadNext(): Promise<unknown> } }).pagination;\n\n return capability === undefined ? Promise.resolve(unavailablePage) : capability.loadNext();\n }),\n refresh: operationInvoker(() => resource.refresh()),\n retry: operationInvoker(() => resource.retry()),\n };\n}\n\nfunction resourceInvokers(resource: AnyResource, cache: WeakMap<object, ResourceInvokers>): ResourceInvokers {\n const existing = cache.get(resource);\n\n if (existing !== undefined) return existing;\n\n const invokers = createResourceInvokers(resource);\n cache.set(resource, invokers);\n\n return invokers;\n}\n\n/**\n * The one module of this package that talks to the runtime about a materialization, so the rest of React reads the\n * recognition and the key type from here instead of routing to `@opetope/runtime/internal` of its own.\n */\nexport { createResourceInvokers, isResource, paginationOf, resourceInvokers };\nexport type { AnyResource, PaginatedResourceHook, ResourceHook, ResourceInvokers, ResourceKey, ResourcePaginationHook };\n","import { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\n\nimport type { Readable } from '@opetope/core';\nimport { isClosedReadable } from '@opetope/core/internal';\nimport type { EqualityOption } from '@opetope/core/internal';\n\nconst identity = <Value>(value: Value): Value => value;\n\n/**\n * G13, D362: the owner closed this source — a retirement this render raced, and the consumer could not have\n * prevented it. The reader answers the last value it read, so the tree shows its last frame and unmounts instead of\n * failing in the middle of a render. A source never read has nothing to answer with, and every other refusal of the\n * source is the consumer's to see.\n */\nfunction keepLastOnClose<Selected>(read: () => Selected): () => Selected {\n let last: Selected | undefined;\n let hasLast = false;\n\n return (): Selected => {\n try {\n last = read();\n hasLast = true;\n\n return last;\n } catch (error) {\n if (!hasLast || !isClosedReadable(error)) throw error;\n\n return last as Selected;\n }\n };\n}\n\ninterface SelectionInstance<Selected> {\n hasValue: boolean;\n value: Selected | undefined;\n}\n\n/**\n * D279: the source and the selection are positional; `equals` is the one modifier, spelled as in Core — including\n * D348's rule for where the comparator is read, so a named non-generic one compiles here as it does in `derive`.\n */\nexport function useSelector<Value, Selected>(\n readable: Readable<Value>,\n select: (value: NoInfer<Value>) => Selected,\n options?: { readonly equals?: EqualityOption<NoInfer<Selected>> },\n): Selected {\n const selector = select;\n const isEqual = options?.equals ?? Object.is;\n const subscribe = useCallback((listener: () => void) => readable.subscribe(listener), [readable]);\n const instanceRef = useRef<SelectionInstance<Selected> | undefined>(undefined);\n let instance = instanceRef.current;\n\n if (instance === undefined) {\n instance = { hasValue: false, value: undefined };\n instanceRef.current = instance;\n }\n\n const getSelectedSnapshot = useMemo(() => {\n let initialized = false;\n let sourceSnapshot: Value;\n let selectedSnapshot: Selected;\n\n return keepLastOnClose((): Selected => {\n const nextSource = readable.getSnapshot();\n\n if (initialized && Object.is(sourceSnapshot, nextSource)) return selectedSnapshot;\n\n const nextSelected = selector(nextSource);\n\n if (!initialized) {\n let nextSelectedSnapshot = nextSelected;\n\n if (instance.hasValue && isEqual(instance.value, nextSelected)) {\n nextSelectedSnapshot = instance.value as Selected;\n }\n\n initialized = true;\n sourceSnapshot = nextSource;\n selectedSnapshot = nextSelectedSnapshot;\n\n return selectedSnapshot;\n }\n\n if (isEqual(selectedSnapshot, nextSelected)) {\n sourceSnapshot = nextSource;\n\n return selectedSnapshot;\n }\n\n sourceSnapshot = nextSource;\n selectedSnapshot = nextSelected;\n\n return selectedSnapshot;\n });\n }, [instance, isEqual, readable, selector]);\n\n const selected = useSyncExternalStore(subscribe, getSelectedSnapshot, getSelectedSnapshot);\n\n useEffect(() => {\n instance.hasValue = true;\n instance.value = selected;\n }, [instance, selected]);\n\n return selected;\n}\n\nexport function useReadable<Value>(readable: Readable<Value>): Value {\n return useSelector(readable, identity);\n}\n","import type { Call } from '@opetope/core';\nimport { isCallTarget } from '@opetope/core/internal';\n\nimport { ContributionError } from './errors';\n\n/**\n * A bound command is the authentic `Call` the model field declares, so the common path adds no wrapper. Observable\n * call state belongs to the model as a `Readable` field (D71), so a binding carries no mode.\n */\ntype CommandBindingIdentity = Call<never, unknown>;\ntype CommandBinding<Input, Output> = Call<Input, Output>;\n\ntype CommandOutcome<Output> =\n | { readonly error: unknown; readonly status: 'failed' }\n | { readonly reason: unknown; readonly status: 'cancelled' }\n | { readonly status: 'ok'; readonly value: Output };\n\ntype AnyCommandBinding = CommandBinding<never, unknown>;\n\ntype CommandBindingDefinition<Input, Output> = {\n readonly target: Call<Input, Output>;\n};\n\nconst bindingDefinitions = new WeakMap<CommandBindingIdentity, CommandBindingDefinition<never, unknown>>();\n\nfunction bindCommand<Input, Output>(target: Call<Input, Output>): CommandBinding<Input, Output> {\n if (arguments.length > 1) {\n throw new ContributionError(\n 'binding-invalid',\n 'Command follows the target it binds; binding options are forbidden.',\n );\n }\n\n if (!isCallTarget(target)) throw new ContributionError('binding-invalid', 'Command target is not authentic.');\n\n bindingDefinitions.set(target, { target });\n\n return target;\n}\n\nfunction getCommandBindingDefinition<Input, Output>(\n binding: Call<Input, Output>,\n): CommandBindingDefinition<Input, Output> {\n const definition = bindingDefinitions.get(binding);\n\n if (definition === undefined) throw new ContributionError('binding-invalid', 'Command binding is not authentic.');\n\n return definition as CommandBindingDefinition<Input, Output>;\n}\n\nfunction isCommandBinding(value: unknown): value is AnyCommandBinding {\n return typeof value === 'object' && value !== null && bindingDefinitions.has(value as CommandBindingIdentity);\n}\n\nexport { bindCommand, getCommandBindingDefinition, isCommandBinding };\nexport type { CommandOutcome };\n","import type { Call, ModelOf } from '@opetope/core';\nimport { isModel, isReadable } from '@opetope/core/internal';\nimport type { ModelIdentity, NoExtraKeys } from '@opetope/core/internal';\n\nimport { isCommandBinding } from './command';\nimport { ContributionError } from './errors';\nimport { isResource } from './resource-command';\n\nconst modelBindingBrand = Symbol('opetope.model-binding');\nconst modelBindingIdentityBrand = Symbol('opetope.model-binding-identity');\nconst modelBindingMarker = Object.freeze({});\n\ntype AnyModel = ModelIdentity;\ntype AnyCommand = Call<never, unknown>;\n\ninterface ModelBindingIdentity {\n readonly [modelBindingIdentityBrand]: true;\n}\n\ninterface ModelBinding<Declaration extends AnyModel> extends ModelBindingIdentity {\n readonly [modelBindingBrand]: (declaration: Declaration) => Declaration;\n}\n\ntype ModelBindingMetadata = {\n readonly commands: readonly AnyCommand[];\n readonly declaration: AnyModel;\n readonly model: object;\n};\n\nconst registry = new WeakMap<ModelBindingIdentity, ModelBindingMetadata>();\n\n/**\n * A model field is either a stable Readable or a bound command. The declaration carries the shape in its type, so\n * binding authority is the runtime check that every published field is one of those two authentic kinds.\n */\nfunction modelFieldValue(declaration: AnyModel, model: object, field: PropertyKey): unknown {\n if (typeof field !== 'string') {\n throw new ContributionError('binding-invalid', `Model ${declaration.id} accepts only string fields.`);\n }\n\n const descriptor = Object.getOwnPropertyDescriptor(model, field);\n\n if (descriptor?.enumerable !== true || !('value' in descriptor)) {\n throw new ContributionError(\n 'binding-invalid',\n `Model ${declaration.id}.${field} requires an enumerable data binding.`,\n );\n }\n\n return descriptor.value;\n}\n\nfunction snapshotModelFields(declaration: AnyModel, model: object): readonly AnyCommand[] {\n const commands: AnyCommand[] = [];\n const fields = Reflect.ownKeys(model);\n\n if (fields.length === 0)\n throw new ContributionError('binding-invalid', `Model ${declaration.id} requires at least one field.`);\n\n for (const field of fields) {\n const value = modelFieldValue(declaration, model, field);\n\n // D349: a Resource is a field a model publishes, and it is not a `Readable`, so it is recognised by name.\n if (isReadable(value) || isResource(value) || typeof value === 'function') continue;\n\n if (!isCommandBinding(value)) {\n throw new ContributionError(\n 'binding-invalid',\n `Model ${declaration.id}.${String(field)} requires a Readable, a Resource or a bound command.`,\n );\n }\n\n commands.push(value);\n }\n\n return Object.freeze(commands);\n}\n\nfunction bindModel<Declaration extends AnyModel, const Actual extends ModelOf<Declaration>>(\n declaration: Declaration,\n model: Actual & NoExtraKeys<Actual, ModelOf<Declaration>>,\n): ModelBinding<Declaration> {\n if (!isModel(declaration)) throw new ContributionError('binding-invalid', 'Model declaration is not authentic.');\n\n if (typeof model !== 'object' || model === null) {\n throw new ContributionError('binding-invalid', `Model ${declaration.id} requires a record binding.`);\n }\n\n const commands = snapshotModelFields(declaration, model);\n const binding = Object.freeze({\n [modelBindingBrand]: modelBindingMarker as unknown as (value: Declaration) => Declaration,\n [modelBindingIdentityBrand]: true as const,\n });\n registry.set(binding, { commands, declaration, model });\n\n return binding;\n}\n\nfunction getModelBindingMetadata(binding: ModelBindingIdentity): ModelBindingMetadata {\n const metadata = registry.get(binding);\n\n if (metadata === undefined) throw new ContributionError('binding-invalid', 'Model binding is not authentic.');\n\n return metadata;\n}\n\nexport { bindModel, getModelBindingMetadata };\nexport type { AnyCommand, AnyModel, ModelBindingIdentity };\n","import type {\n CallAuthority,\n CallController,\n CallDiagnostics,\n CallRunOptions,\n LifetimeLease,\n} from '@opetope/core/internal';\nimport { createCallController, createLifetimeController, reportDetachedError } from '@opetope/core/internal';\n\nimport { getCommandBindingDefinition } from './command';\nimport { ContributionError } from './errors';\nimport { getModelBindingMetadata } from './model-binding';\nimport type { AnyCommand, AnyModel, ModelBindingIdentity } from './model-binding';\n\ntype RuntimeCommandInvoker = {\n readonly run: (input: never, options?: CallRunOptions<unknown>) => Promise<unknown>;\n};\n\ntype CommandRecord = {\n readonly controller: CallController<never, unknown>;\n readonly invoker: RuntimeCommandInvoker;\n};\n\n/** What a mounted contribution grants its components: the models it may read and the commands it may invoke. */\ntype Frame = {\n readonly commandRecords: Map<AnyCommand, CommandRecord>;\n readonly models: Map<AnyModel, unknown>;\n};\n\ntype MountFrameLifecycle = {\n readonly isActive: () => boolean;\n readonly onClose: () => void;\n};\n\ntype MountFrameDiagnostics = {\n readonly active: boolean;\n readonly calls: readonly CallDiagnostics[];\n readonly commandRecords: number;\n readonly models: number;\n};\n\nlet nextMountId = 0;\n\nfunction buildFrame(supplied: readonly ModelBindingIdentity[]): {\n readonly commands: readonly AnyCommand[];\n readonly frame: Frame;\n} {\n const models = new Map<AnyModel, unknown>();\n const commands: AnyCommand[] = [];\n const bound = new Set<AnyCommand>();\n\n for (const candidate of supplied) {\n const binding = getModelBindingMetadata(candidate);\n\n if (models.has(binding.declaration)) {\n throw new ContributionError('duplicate', `Model ${binding.declaration.id} is bound twice in one mount.`);\n }\n\n models.set(binding.declaration, binding.model);\n\n for (const command of binding.commands) {\n // D197: one Call is one command of the mount, however many fields of the models name it. A second name is an\n // alias, not a second command, so it shares the record, the activation and the fence.\n if (bound.has(command)) continue;\n\n bound.add(command);\n commands.push(command);\n }\n }\n\n return { commands: Object.freeze(commands), frame: { commandRecords: new Map(), models } };\n}\n\n/**\n * The lifetime of one mounted contribution. `attach` claims it for the renderer, `detach` schedules the close on the\n * next microtask so a StrictMode remount reuses the same mount, and `close` fences every command it granted (D85).\n */\nclass MountFrameImpl {\n readonly frame: Frame;\n private authority: CallAuthority | undefined;\n private boundCommands: readonly AnyCommand[];\n private closeTicket = 0;\n private readonly isActive: (() => boolean) | undefined;\n private readonly lifetime: ReturnType<typeof createLifetimeController>;\n private onClose: (() => void) | undefined;\n private released = false;\n private rendererAttached = false;\n private readonly renderListeners = new Set<{ readonly listener: () => void }>();\n private state: 'active' | 'closed' | 'closing' | 'detached' = 'detached';\n\n constructor(models: readonly ModelBindingIdentity[], lifecycle?: MountFrameLifecycle) {\n const mountId = ++nextMountId;\n this.lifetime = createLifetimeController({ id: `contribution.${String(mountId)}` });\n this.isActive = lifecycle?.isActive;\n this.onClose = lifecycle?.onClose;\n const { commands, frame } = buildFrame(models);\n this.frame = frame;\n this.boundCommands = commands;\n this.buildCommandRecords(mountId);\n }\n\n attach(): boolean {\n if (this.state === 'closing') {\n this.closeTicket += 1;\n this.state = 'active';\n this.rendererAttached = true;\n\n return true;\n }\n\n if (this.state === 'closed') return false;\n\n if (this.state === 'active') throw new ContributionError('duplicate', 'One contribution mount cannot mount twice.');\n\n const authority = this.createAuthority(this.lifetime.open());\n\n try {\n for (const record of this.frame.commandRecords.values()) record.controller.activate();\n } catch (error) {\n this.authority = authority;\n this.state = 'active';\n this.close();\n\n throw error;\n }\n\n this.authority = authority;\n this.rendererAttached = true;\n this.state = 'active';\n\n return true;\n }\n\n close(): void {\n if (this.state === 'closed') return;\n\n this.state = 'closed';\n this.closeTicket += 1;\n this.lifetime.close();\n this.authority = undefined;\n\n for (const record of this.frame.commandRecords.values()) record.controller.close();\n\n const onClose = this.onClose;\n this.onClose = undefined;\n onClose?.();\n this.publishRenderable();\n\n if (!this.rendererAttached) this.releaseFrame();\n }\n\n detach(): void {\n if (!this.rendererAttached) return;\n\n this.rendererAttached = false;\n\n if (this.state === 'closed') {\n this.releaseFrame();\n\n return;\n }\n\n if (this.state !== 'active') return;\n\n this.state = 'closing';\n const ticket = ++this.closeTicket;\n\n queueMicrotask(() => {\n if (this.state === 'closing' && ticket === this.closeTicket) this.close();\n });\n }\n\n diagnostics(): MountFrameDiagnostics {\n const calls = [...this.frame.commandRecords.values()].map(record => record.controller.diagnostics());\n\n return Object.freeze({\n active: this.state === 'active',\n calls: Object.freeze(calls),\n commandRecords: this.frame.commandRecords.size,\n models: this.frame.models.size,\n });\n }\n\n getRenderableSnapshot = (): boolean => this.state !== 'closed' && (this.isActive === undefined || this.isActive());\n\n releaseClosedFrame(): void {\n if (this.state === 'closed') this.releaseFrame();\n }\n\n subscribeRenderable = (listener: () => void): (() => void) => {\n if (this.state === 'closed') return (): void => undefined;\n\n const registration = { listener };\n this.renderListeners.add(registration);\n let subscribed = true;\n\n return (): void => {\n if (!subscribed) return;\n\n subscribed = false;\n this.renderListeners.delete(registration);\n };\n };\n\n private buildCommandRecords(mountId: number): void {\n let commandIndex = 0;\n\n // `buildFrame` already reduced the models to their distinct Calls, so every command here gets its own record.\n for (const command of this.boundCommands) {\n // Authenticity check only: the binding carries no mode since observe was removed (D71).\n getCommandBindingDefinition(command);\n const controller = createCallController(command, {\n captureAuthority: this.captureAuthority,\n id: `contributionCommand.${String(mountId)}.${String(++commandIndex)}`,\n scheduling: { policy: 'parallel' },\n });\n this.frame.commandRecords.set(command, { controller, invoker: Object.freeze({ run: controller.run }) });\n }\n }\n\n private captureAuthority = (): CallAuthority => {\n const authority = this.authority;\n\n if (this.state !== 'active' || authority === undefined) {\n throw new ContributionError('inactive', 'Contribution mount is not active.');\n }\n\n authority.assertCurrent();\n\n return authority;\n };\n\n private createAuthority(lease: LifetimeLease): CallAuthority {\n return Object.freeze({\n assertCurrent: (): void => {\n if (this.state !== 'active') throw new ContributionError('inactive', 'Contribution mount is not active.');\n\n lease.assertCurrent();\n },\n isCurrent: (): boolean => this.state === 'active' && lease.isCurrent(),\n });\n }\n\n private publishRenderable(): void {\n for (const { listener } of [...this.renderListeners]) {\n try {\n listener();\n } catch (error) {\n reportDetachedError(error);\n }\n }\n }\n\n private releaseFrame(): void {\n if (this.released) return;\n\n this.released = true;\n this.frame.commandRecords.clear();\n this.frame.models.clear();\n this.boundCommands = [];\n this.renderListeners.clear();\n }\n}\n\nexport { MountFrameImpl };\nexport type { Frame };\n","import { useInsertionEffect, useLayoutEffect, useSyncExternalStore } from 'react';\nimport type { PropsWithChildren, ReactElement } from 'react';\n\nimport { FrameContext } from './mount-context';\nimport type { MountFrameImpl } from './mount-frame';\n\nfunction MountProvider({\n children,\n mount,\n}: PropsWithChildren<{ readonly mount: MountFrameImpl }>): ReactElement | null {\n const renderable = useSyncExternalStore(\n mount.subscribeRenderable,\n mount.getRenderableSnapshot,\n mount.getRenderableSnapshot,\n );\n\n useInsertionEffect(() => {\n const attached = mount.attach();\n\n if (!attached) return undefined;\n\n return () => mount.detach();\n }, [mount]);\n\n useLayoutEffect(() => {\n if (!renderable) mount.releaseClosedFrame();\n }, [mount, renderable]);\n\n return renderable ? <FrameContext value={mount.frame}>{children}</FrameContext> : null;\n}\n\nexport { MountProvider };\n","import { memo, useCallback, useInsertionEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';\nimport type { ComponentType, ReactElement } from 'react';\n\nimport type { DeclarationId, State } from '@opetope/core';\nimport {\n createAggregateError,\n createState,\n getContributionOwner,\n isReadable,\n reportDetachedError,\n} from '@opetope/core/internal';\nimport type { ModelIdentity } from '@opetope/core/internal';\nimport { openContributionModels, reportRuntimeFailure, requireContributionModelPlans } from '@opetope/runtime/internal';\nimport type { ContributionModelBundle, RuntimeErrorReporter } from '@opetope/runtime/internal';\n\nimport { bindCommand, isCommandBinding } from './command';\nimport { ContributionIsolation, useContributionErrorContent } from './contribution-isolation';\nimport { bindModel } from './model-binding';\nimport { isResource } from './resource-command';\nimport type { ModelBindingIdentity } from './model-binding';\nimport { MountFrameImpl } from './mount-frame';\nimport { MountProvider } from './mount-provider';\nimport { useReadable } from './readable-hooks';\n\ntype AnyModel = ModelIdentity;\ntype ContributionAuthority = {\n /** The feature whose generation published this contribution (D257). */\n readonly feature?: DeclarationId;\n readonly models: readonly (readonly [AnyModel, unknown])[];\n /** The reporter of the feature that published this contribution: a contained failure is its diagnostic, not ours. */\n readonly reporter?: RuntimeErrorReporter;\n};\ntype ModelFixture = readonly [declaration: AnyModel, model: unknown];\n\ntype ContributionValue = {\n readonly Component: ComponentType<never>;\n readonly models?: readonly object[] | undefined;\n readonly props?: ((slotProps: never) => object) | undefined;\n};\n\ntype ContributionMountProps = {\n readonly authority?: ContributionAuthority | undefined;\n readonly contribution: ContributionValue;\n /** The published entry this mount renders, and the slot it renders in: the identity of a contained failure (D257). */\n readonly contributionId: DeclarationId;\n readonly fixtures?: readonly ModelFixture[];\n readonly slotProps: object;\n readonly targetId: DeclarationId;\n};\n\ntype MountBundle = {\n readonly frame: MountFrameImpl;\n readonly isOpen: () => boolean;\n readonly props: State<object>;\n};\n\ntype MountOwner = {\n readonly authority: ContributionAuthority | undefined;\n bundle: MountBundle | undefined;\n readonly contribution: ContributionValue;\n readonly fixtures: readonly ModelFixture[] | undefined;\n retired: boolean;\n};\n\ntype CommittedMount = {\n readonly bundle: MountBundle;\n readonly owner: MountOwner;\n};\n\nfunction closeOwnedMount(owner: MountOwner): void {\n const bundle = owner.bundle;\n owner.bundle = undefined;\n bundle?.frame.close();\n}\n\n/**\n * Model fields arrive as authentic kernel calls; binding them registers the command records the frame needs. D349: a\n * Resource is none of those and needs none of it — it is a field the selection reads on its own terms, so it travels\n * to the frame unchanged instead of being offered to `bindCommand`, which would refuse it as inauthentic.\n */\nfunction bindFrameModel(declaration: AnyModel, model: unknown): ModelBindingIdentity {\n if (typeof model !== 'object' || model === null) return bindModel(declaration as never, model as never);\n\n const passesThrough = (value: unknown): boolean =>\n isReadable(value) || isResource(value) || typeof value === 'function' || isCommandBinding(value);\n const fields = Object.entries(model).map(([key, value]): readonly [string, unknown] => [\n key,\n passesThrough(value) ? value : bindCommand(value as never),\n ]);\n\n return bindModel(declaration as never, Object.fromEntries(fields) as never);\n}\n\n/**\n * D70: the authority of a contribution belongs to the entry a feature published, not to the value it wrote. One\n * static contribution object may be published by two features, and each mount must read the models of its own.\n */\nfunction contributionAuthority(entry: unknown): ContributionAuthority | undefined {\n return getContributionOwner(entry) as ContributionAuthority | undefined;\n}\n\n/** A mount with no publishing generation behind it: a fixture of a component test grants nothing and reports nowhere. */\nconst unownedContribution: ContributionAuthority = { models: [] };\n\nfunction cleanupFailedMount(close: () => void, cause: unknown): never {\n try {\n close();\n } catch (error) {\n throw createAggregateError([cause, error], 'Contribution mount setup and cleanup failed.', { cause });\n }\n\n throw cause;\n}\n\nfunction contributionModels(\n contribution: ContributionValue,\n slotProps: object,\n fixtures: readonly ModelFixture[],\n owner: ContributionAuthority | undefined,\n): {\n readonly bundle: ContributionModelBundle | undefined;\n readonly models: readonly ModelFixture[];\n readonly props: State<object>;\n} {\n const props = createState(slotProps);\n const { models: granted, reporter } = owner ?? unownedContribution;\n\n try {\n const plans = requireContributionModelPlans(contribution.models);\n const bundle = plans.length === 0 ? undefined : openContributionModels(plans, props, reporter);\n\n return {\n bundle,\n models: [...granted, ...(bundle?.models ?? []), ...fixtures],\n props,\n };\n } catch (error) {\n return cleanupFailedMount(props.close, error);\n }\n}\n\nfunction createMountBundle(\n contribution: ContributionValue,\n slotProps: object,\n fixtures: readonly ModelFixture[],\n authority: ContributionAuthority | undefined,\n): MountBundle {\n const { bundle, models, props } = contributionModels(contribution, slotProps, fixtures, authority);\n let closed = false;\n const close = (): void => {\n if (closed) return;\n\n closed = true;\n\n try {\n void bundle?.close().catch(reportDetachedError);\n } finally {\n props.close();\n }\n };\n try {\n const frame = new MountFrameImpl(\n models.map(([declaration, model]) => bindFrameModel(declaration, model)),\n { isActive: () => !closed, onClose: close },\n );\n\n return { frame, isOpen: () => !closed, props };\n } catch (error) {\n return cleanupFailedMount(close, error);\n }\n}\n\nfunction createComponentElement(Component: ComponentType<never>, props: object): ReactElement {\n const Rendered = Component as ComponentType<object>;\n\n return <Rendered {...props} />;\n}\n\ntype MountedContributionProps = {\n readonly bundle: MountBundle;\n readonly contribution: ContributionValue;\n};\n\n/** Reads the committed props of one live mount: every hook here sees a bundle that already exists. */\nfunction MountedContribution({ bundle, contribution }: MountedContributionProps): ReactElement {\n const committedProps = useReadable(bundle.props);\n const componentProps = useMemo(\n () => (contribution.props === undefined ? committedProps : contribution.props(committedProps as never)),\n [committedProps, contribution],\n );\n\n return (\n <MountProvider mount={bundle.frame}>{createComponentElement(contribution.Component, componentProps)}</MountProvider>\n );\n}\n\n/**\n * D188: a mount is created in commit, never in render. The first pass renders nothing, the layout effect builds the\n * bundle from the props that committed and publishes it, and React flushes that update before paint. A render that\n * is thrown away — StrictMode's double invoke, an abandoned concurrent pass, a suspended sibling — therefore\n * creates no models at all, so there is nothing to sweep, adopt or replace.\n */\nfunction ContributionMount({\n authority,\n contribution,\n fixtures,\n slotProps,\n}: ContributionMountProps): ReactElement | null {\n const owner = useMemo<MountOwner>(\n () => ({ authority, bundle: undefined, contribution, fixtures, retired: false }),\n [authority, contribution, fixtures],\n );\n const [committed, setCommitted] = useState<CommittedMount | undefined>(undefined);\n const bundle = committed?.owner === owner ? committed.bundle : undefined;\n const committedSlotProps = useRef(slotProps);\n\n useLayoutEffect(() => {\n committedSlotProps.current = slotProps;\n });\n\n // Suspense disconnects layout effects while hiding, but insertion cleanup only retires an actual identity.\n // Do not close Readables in insertion: their listeners may update React. A hidden tree has no layout cleanup\n // left to run on unmount, so the microtask also releases a mount that never becomes visible again.\n useInsertionEffect(\n () => () => {\n owner.retired = true;\n queueMicrotask(() => {\n try {\n closeOwnedMount(owner);\n } catch (error) {\n reportDetachedError(error);\n }\n });\n },\n [owner],\n );\n\n // The identity of the contribution owns the mount; a reveal and StrictMode reuse what its first commit created.\n useLayoutEffect(() => {\n const created = (owner.bundle ??= createMountBundle(\n owner.contribution,\n committedSlotProps.current,\n owner.fixtures ?? [],\n owner.authority,\n ));\n setCommitted(current => (current?.owner === owner ? current : { bundle: created, owner }));\n\n return () => {\n if (owner.retired) closeOwnedMount(owner);\n };\n }, [owner]);\n\n // Both the component and its models read the same committed snapshot, published before paint.\n useLayoutEffect(() => {\n if (bundle?.isOpen() === true) bundle.props.set(slotProps);\n }, [bundle, slotProps]);\n\n if (bundle === undefined) return null;\n\n return <MountedContribution bundle={bundle} contribution={contribution} />;\n}\n\n/**\n * D256: every mount is a boundary. The isolation sits above `ContributionMount`, so it contains the render of the\n * component and the commit that builds the mount alike, and a retry rebuilds the mount instead of reusing the bundle\n * that failed. Containment is unconditional; the content a failure shows is the host policy of `ContributionBoundary`.\n */\nfunction IsolatedContributionMount(props: ContributionMountProps): ReactElement {\n const { authority, contribution, contributionId, fixtures, targetId } = props;\n const content = useContributionErrorContent();\n const identity = useMemo(() => ({ authority, contribution, fixtures }), [authority, contribution, fixtures]);\n const feature = authority?.feature;\n const source = useMemo(\n () => ({ contribution: contributionId, feature, target: targetId }),\n [contributionId, feature, targetId],\n );\n const reporter = authority?.reporter;\n const report = useCallback((error: unknown) => reportRuntimeFailure(error, reporter), [reporter]);\n\n return (\n <ContributionIsolation content={content} identity={identity} report={report} source={source}>\n <ContributionMount {...props} />\n </ContributionIsolation>\n );\n}\n\n/**\n * One mount, one memo: `Slot` recreates its children whenever `entries` changes, so publishing or withholding one\n * contribution used to re-render every other mount of that target. The props are the contribution, the slot props\n * and the fixtures, all of which are stable while nothing about that mount changed.\n */\nconst MemoizedContributionMount = memo(IsolatedContributionMount);\n\nexport { contributionAuthority, MemoizedContributionMount as ContributionMount };\nexport type { ContributionValue, ModelFixture };\n"],"names":["FrameContext","createContext","useFrame","frame","useContext","ContributionError","isResource","value","isResourceValue","awaitCancellable","operation","signal","resolve","reject","cancel","CallError","compatibleAbortReason","error","notifySuccess","options","notification","_a","reportDetachedError","operationInvoker","start","_input","unavailablePage","paginationOf","resource","capability","createResourceInvokers","resourceInvokers","cache","existing","invokers","identity","keepLastOnClose","read","last","hasLast","isClosedReadable","useSelector","readable","select","selector","isEqual","subscribe","useCallback","listener","instanceRef","useRef","instance","getSelectedSnapshot","useMemo","initialized","sourceSnapshot","selectedSnapshot","nextSource","nextSelected","nextSelectedSnapshot","selected","useSyncExternalStore","useEffect","useReadable","bindingDefinitions","bindCommand","target","isCallTarget","getCommandBindingDefinition","binding","definition","isCommandBinding","modelBindingBrand","modelBindingIdentityBrand","modelBindingMarker","registry","modelFieldValue","declaration","model","field","descriptor","snapshotModelFields","commands","fields","isReadable","bindModel","isModel","getModelBindingMetadata","metadata","nextMountId","buildFrame","supplied","models","bound","candidate","command","MountFrameImpl","lifecycle","mountId","createLifetimeController","authority","record","onClose","ticket","calls","registration","subscribed","commandIndex","controller","createCallController","lease","MountProvider","children","mount","renderable","useInsertionEffect","useLayoutEffect","_jsx","closeOwnedMount","owner","bundle","bindFrameModel","passesThrough","key","contributionAuthority","entry","getContributionOwner","unownedContribution","cleanupFailedMount","close","cause","createAggregateError","contributionModels","contribution","slotProps","fixtures","props","createState","granted","reporter","plans","requireContributionModelPlans","openContributionModels","createMountBundle","closed","createComponentElement","Component","MountedContribution","committedProps","componentProps","ContributionMount","committed","setCommitted","useState","committedSlotProps","created","current","IsolatedContributionMount","contributionId","targetId","content","useContributionErrorContent","feature","source","report","reportRuntimeFailure","ContributionIsolation","MemoizedContributionMount","memo"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,6 @@ export { useModel } from './model-hook.js';
|
|
|
7
7
|
export { useReadable, useSelector } from './readable-hooks.js';
|
|
8
8
|
export { requiresModels } from './requires-models.js';
|
|
9
9
|
export { useResource } from './resource-hook.js';
|
|
10
|
-
export type { ResourceHook } from './resource-command.js';
|
|
10
|
+
export type { PaginatedResourceHook, ResourceHook, ResourcePaginationHook } from './resource-command.js';
|
|
11
11
|
export { defineSlot, defineSwitchSlot, Slot } from './slot.js';
|
|
12
12
|
export type { SlotContribution, SlotTarget, SwitchSlotTarget } from './slot.js';
|