@opetope/react 0.10.1 → 0.12.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 +359 -17
- package/README.md +114 -71
- package/README.ru.md +60 -70
- package/dist/command-hook-controller.d.ts +4 -4
- package/dist/command-hook.d.ts +3 -3
- package/dist/command.d.ts +8 -8
- package/dist/contribution-frame-CCKnOxZR.js +2 -0
- package/dist/contribution-frame-CCKnOxZR.js.map +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/model-binding.d.ts +4 -4
- package/dist/model-hook.d.ts +2 -2
- package/dist/model-selection-snapshot.d.ts +20 -3
- package/dist/resource-command.d.ts +11 -4
- package/dist/scenario-slot.d.ts +6 -3
- package/dist/scenario-types.d.ts +2 -2
- package/dist/scenario.d.ts +1 -2
- package/dist/{commands-hook.d.ts → selection-hooks.d.ts} +4 -11
- package/dist/testing.d.ts +3 -3
- package/dist/testing.js +2 -2
- package/dist/testing.js.map +1 -1
- package/package.json +5 -5
- package/dist/contribution-frame-QRJSP2W3.js +0 -2
- package/dist/contribution-frame-QRJSP2W3.js.map +0 -1
package/README.ru.md
CHANGED
|
@@ -19,12 +19,12 @@ API поставляется только в ESM; требуется Node 20.19+
|
|
|
19
19
|
|
|
20
20
|
```tsx
|
|
21
21
|
import { defineModel } from '@opetope/core';
|
|
22
|
-
import type {
|
|
22
|
+
import type { Command, Readable } from '@opetope/core';
|
|
23
23
|
import { defineSlot, requiresModels, Slot, useModel } from '@opetope/react';
|
|
24
24
|
|
|
25
25
|
const CounterModel = defineModel<{
|
|
26
26
|
readonly count: Readable<number>;
|
|
27
|
-
readonly increment:
|
|
27
|
+
readonly increment: Command<void, void>;
|
|
28
28
|
}>('example.counter.model');
|
|
29
29
|
const CounterSlot = defineSlot<{ readonly label: string }>('example.counter.slot');
|
|
30
30
|
|
|
@@ -39,7 +39,7 @@ const CounterButton = requiresModels([CounterModel])(({ label }: { readonly labe
|
|
|
39
39
|
|
|
40
40
|
return (
|
|
41
41
|
<button disabled={inFlight} onClick={() => void run()}>
|
|
42
|
-
{outcome?.
|
|
42
|
+
{outcome?.kind === 'failed' ? 'Retry' : `${label}: ${count}`}
|
|
43
43
|
</button>
|
|
44
44
|
);
|
|
45
45
|
});
|
|
@@ -57,10 +57,10 @@ function CounterArea() {
|
|
|
57
57
|
`outcome` — последний завершившийся не-отменённый исход этого потребителя: `ok` встаёт на место прежнего `failed`,
|
|
58
58
|
`failed` — на место прежнего `ok`, исход `cancelled` не двигает ничего, и старт прогона ничего не чистит, поэтому
|
|
59
59
|
отказ читается, пока идёт повтор. До первого завершения он `undefined`. Читают его по дискриминанту —
|
|
60
|
-
`outcome?.
|
|
60
|
+
`outcome?.kind === 'failed' ? outcome.error : null`.
|
|
61
61
|
|
|
62
62
|
Команда без входа привязывается к событию стрелкой: `onClick={() => void logout.run()}`. Именно стрелка не пускает
|
|
63
|
-
React-событие в
|
|
63
|
+
React-событие в Command и не оставляет промис висеть; `onClick={logout.run}` не компилируется, потому что `MouseEvent`
|
|
64
64
|
не ложится в `void`. Для данных, исхода, callbacks или сигнала отдельного вызова используйте `run(input, options?)`.
|
|
65
65
|
Там, где конфиг линтера запрещает стрелку прямо в пропсе (`react-perf/jsx-no-new-function-as-prop`,
|
|
66
66
|
`react/jsx-no-bind`), её поднимают через `useCallback(() => void logout.run(), [logout.run])`: зависимость — `run`,
|
|
@@ -71,38 +71,24 @@ React-событие в Call и не оставляет промис висет
|
|
|
71
71
|
`opetope/no-command-in-deps` держит это за автора (D290). Сохранённый `run` заменённого или
|
|
72
72
|
размонтированного потребителя закрыт для новых вызовов и не перенаправляет работу на замену.
|
|
73
73
|
|
|
74
|
-
Хук ничего не планирует. Каждый `run` доходит до
|
|
75
|
-
setter абсолютного значения модель объявляет `
|
|
76
|
-
именно там (D185). За потребителем остаётся своё: уже отменённый входной signal
|
|
77
|
-
доходя до
|
|
78
|
-
хоть один начатый им прогон, и у каждого прогона свои callbacks и signal.
|
|
74
|
+
Хук ничего не планирует. Каждый `run` доходит до команды, и решает порядок, с которым команда объявлена (D203).
|
|
75
|
+
Для setter абсолютного значения модель объявляет `concurrency: 'latest'` в `context.command`, и новый вход
|
|
76
|
+
заменяет ожидающий именно там (D185, D387). За потребителем остаётся своё: уже отменённый входной signal
|
|
77
|
+
отвергается как `cancelled`, не доходя до команды, `run` размонтированного потребителя отвергается так же,
|
|
78
|
+
`inFlight` истинен, пока не завершился хоть один начатый им прогон, и у каждого прогона свои callbacks и signal.
|
|
79
79
|
|
|
80
80
|
Чтобы объединить эквивалентную ожидающую или исполняющуюся работу, модель объявляет `dedupe: true` либо
|
|
81
|
-
`dedupe: input => key` у `context.
|
|
82
|
-
обе формы запрещены с `latest` (D277). Это опции
|
|
81
|
+
`dedupe: input => key` у `context.command`. Сохраняются первый вход и независимая отмена каждого вызывающего;
|
|
82
|
+
обе формы запрещены с `latest` (D277). Это опции Command, а не хука.
|
|
83
83
|
|
|
84
|
-
Для нескольких команд
|
|
84
|
+
Для нескольких команд компонент пишет `useCommand` на команду либо одну выборку `useModel`, которая и так
|
|
85
|
+
отдаёт готовые хуки. `useCommands` удалён: запись поверх единственного `CommandHook` была сахаром, экономившим
|
|
86
|
+
вызовы хуков, а не вторым механизмом, и нужный набор — это выбор в модели, где у него есть владелец и время
|
|
87
|
+
жизни, которые даёт `ctx.select` (D388).
|
|
85
88
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
void save.run(input);
|
|
90
|
-
const saving = save.inFlight;
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
Импортируйте `useCommands` из `@opetope/react`. Каждое поле возвращает прежний `CommandHook`, как `useCommand`,
|
|
94
|
-
со своими типами входа/выхода и своими `inFlight` и `outcome`. Ключи — локальные имена UI; команды могут быть
|
|
95
|
-
из разных разрешённых моделей. Второго аргумента нет: политика есть у каждого вызова. Собственный случай у слова
|
|
96
|
-
один — набор ключей, которого компонент не знает заранее: хуки в цикле не вызывают, а фиксированный набор это
|
|
97
|
-
`useCommand` по слову или одна выборка `useModel`. Слово доказано упакованным потребителем `ci:pack` и остаётся
|
|
98
|
-
кандидатом на вывод с этого входа до второго измеренного приложения (D301).
|
|
99
|
-
|
|
100
|
-
Объекту прямо в рендере не нужен `useMemo`. Перестановка ключей и обновление соседнего поля сохраняют объект
|
|
101
|
-
неизменившегося поля и `.run`. Добавление и удаление ключей не меняет число React hooks. Удаление ключа или
|
|
102
|
-
замена его команды начинает новый локальный статус этого поля; отброшенный
|
|
103
|
-
рендер не меняет текущий выбор. Два псевдонима одного Call имеют независимые локальные статусы, как два потребителя
|
|
104
|
-
`useCommand`; они используют одну политику Call и одну command-запись монтирования (D197, D203). Группа не вводит общий busy, очередь или транзакцию; контролам, перезаписывающим одно значение,
|
|
105
|
-
по-прежнему нужна одна смысловая команда. Выбираются только собственные enumerable-поля, включая symbol-ключи.
|
|
89
|
+
Два псевдонима одного Command имеют независимые локальные статусы, как два потребителя `useCommand`; они
|
|
90
|
+
используют один порядок Command и одну command-запись монтирования (D197, D203). Выборка не вводит общий busy,
|
|
91
|
+
очередь или транзакцию; контролам, перезаписывающим одно значение, по-прежнему нужна одна смысловая команда.
|
|
106
92
|
|
|
107
93
|
## Выбор данных и команд
|
|
108
94
|
|
|
@@ -110,8 +96,8 @@ const saving = save.inFlight;
|
|
|
110
96
|
|
|
111
97
|
Результат выбора модели может быть именованным interface без index signature (D217). Результат — плоская запись данных;
|
|
112
98
|
arrays, functions, constructors и встроенные объекты коллекций, дат и promises не являются selection records.
|
|
113
|
-
`read(source)` возвращает снимок; `read(source, project)` — проекцию. Authentic
|
|
114
|
-
превращаются в тот же `CommandHook`, что у `
|
|
99
|
+
`read(source)` возвращает снимок; `read(source, project)` — проекцию. Authentic Commands, выбранные полями record,
|
|
100
|
+
превращаются в тот же `CommandHook`, что у `useCommand`, с независимыми статусами aliases и стабильным `.run`.
|
|
115
101
|
Одну команду можно выбрать без вложенных hooks:
|
|
116
102
|
|
|
117
103
|
```tsx
|
|
@@ -136,7 +122,7 @@ Hook не создаёт модель и не приобретает feature. О
|
|
|
136
122
|
когда один Resource назвали два ключа (D321, D359). Больше ничего:
|
|
137
123
|
`read(resource.state)` не арендует, и однопараметрическая форма тоже. Объединение hooks не обещает меньше подписок или
|
|
138
124
|
более быстрый render; импорт обычного `useModel` теперь также включает реализацию selection. Дополнительного
|
|
139
|
-
scheduler для
|
|
125
|
+
scheduler для Command нет.
|
|
140
126
|
|
|
141
127
|
## Application host
|
|
142
128
|
|
|
@@ -210,27 +196,30 @@ function HomePageEnd() {
|
|
|
210
196
|
React не вводит второй компаратор и второй реестр времени жизни. Упакованный потребитель `ci:pack` проверяет это
|
|
211
197
|
кеширование и проводит через него один вклад (D301).
|
|
212
198
|
|
|
213
|
-
`useResource(resource)` отвечает `{
|
|
199
|
+
`useResource(resource)` отвечает `{ pagination, refresh, reset, retry, state }`: `ResourceState` через
|
|
214
200
|
`useSyncExternalStore`, арендой, которую отдаёт `resource.acquire()`, взятой в commit-эффекте на всё время
|
|
215
201
|
монтирования компонента, и тремя глаголами как обычными потребителями `CommandHook` (D359). Поэтому прерванный
|
|
216
202
|
render не открывает ничего, а при смене ссылки состояние следует новому Resource, и cleanup эффекта освобождает
|
|
217
203
|
предыдущую аренду. Число React-хуков от Resource не зависит: объявление без `pagination` подписывается на
|
|
218
204
|
постоянный источник и держит `loadNext`, который отвечает `skipped`, а объявление, несшее эту capability, отвечает
|
|
219
205
|
`pagination` записью `{ loadNext, state }` (D356). Поле выборки `useModel`, значением которого является Resource,
|
|
220
|
-
отвечает тем же хуком и берёт ту же аренду, поэтому модель не объявляет `
|
|
206
|
+
отвечает тем же хуком и берёт ту же аренду, поэтому модель не объявляет `Command<void, void>` вокруг
|
|
221
207
|
`resource.refresh()` ради `inFlight` в кнопке. `useReadable(resource)` — это ошибка компиляции, потому что Resource
|
|
222
208
|
не `Readable`; `useReadable(resource.state)` и `read(resource.state)` в селекторе остаются пассивными и не арендуют
|
|
223
209
|
ничего: наблюдать и держать — независимые вопросы, поэтому селектор, читающий состояние, и стоящий рядом
|
|
224
210
|
`useResource` подписываются дважды (D349). Обе стороны показывает компилируемый пример спецификации §2.13
|
|
225
211
|
(D301, D321).
|
|
226
212
|
|
|
227
|
-
`refresh.run()`, `retry.run()` и `
|
|
213
|
+
`refresh.run()`, `retry.run()` и `reset.run()` доходят до Resource, которым владеет объявившая его модель:
|
|
228
214
|
`inFlight` истинен, пока не осел исход операции, `outcome` несёт этот `ResourceOperationOutcome` значением `value`
|
|
229
215
|
при статусе `ok`, а `run(undefined, { signal })` отменяет ожидание этого потребителя, а не работу, которой владеет
|
|
230
216
|
модель. Два потребителя одного Resource держат раздельные статусы, а `run` сохраняет ссылку, пока та же
|
|
231
|
-
идентичность Resource. Render, догнавший retirement,
|
|
232
|
-
`
|
|
233
|
-
|
|
217
|
+
идентичность Resource. Render, догнавший retirement, рисует терминальную запись — `idle` с причиной `retired` у Resource, которым
|
|
218
|
+
владеет модель, и `unleased` у фасада фичи, потому что эта ссылка переживает экземпляр, — и размонтируется, отпустив
|
|
219
|
+
аренду (D359, D364). Ничто здесь не отказывает в чтении из-за того, что кончилась чья-то жизнь: источник, у которого
|
|
220
|
+
ушёл владелец, останавливается и продолжает отвечать последним значением, поэтому `useReadable`, `useSelector` с
|
|
221
|
+
inline-селектором и `read` выборки просто читают дальше, и собственная память им не нужна. Оставшийся отказ — это
|
|
222
|
+
упавшая работа: упавшее состояние, сломанный курсор пагинации, — и он по-прежнему доходит до рендера.
|
|
234
223
|
|
|
235
224
|
Прямые пропсы компонента, `props`-адаптер вклада и props-`Readable` его модели используют один снимок
|
|
236
225
|
монтирования. Входящие пропсы слота публикуются в layout до paint, поэтому прямой и адаптированный рендеры
|
|
@@ -272,7 +261,7 @@ inspection session (D206, D215). Передайте обычные `imports`/`co
|
|
|
272
261
|
а targets без props опускают его, как в `Slot` (D217). Fixtures не обходят authority команд.
|
|
273
262
|
|
|
274
263
|
Как и в `openApplication`, `conditions` можно опустить, если включённые фичи не требуют условий от хоста.
|
|
275
|
-
Условия с `
|
|
264
|
+
Условия с `source`/`select` связываются автоматически; внешним условиям по-прежнему нужны источники (D271).
|
|
276
265
|
|
|
277
266
|
Синхронный конструктор возвращает `ready`, поэтому pending lazy body можно исследовать до готовности.
|
|
278
267
|
`waitFor(snapshot => predicate, { label, timeoutMs, pollIntervalMs })` просыпается от inspection и дополнительно
|
|
@@ -292,18 +281,19 @@ Deadline готовности также оставляет приложение
|
|
|
292
281
|
host/UI/GC-утечек. Успешный cleanup очищает imports приложения и внутренние ссылки на renderer. Promise отказавшего
|
|
293
282
|
cleanup может удерживать исходные ошибки и retry capabilities; сохранённый пользователем `mounted.host` удерживает его renderer result.
|
|
294
283
|
|
|
295
|
-
Inspection использует схемы `opetope.devtools-graph/5` и `opetope.devtools-frame/
|
|
284
|
+
Inspection использует схемы `opetope.devtools-graph/5` и `opetope.devtools-frame/5`, а также optional snapshots
|
|
296
285
|
`opetope.runtime-activity/4` (D266, D275, D344, D358). В пределах одной session
|
|
297
286
|
frame без `activity` сохраняет предыдущую activity; полный snapshot/reset без этого поля очищает наблюдение (D216).
|
|
298
287
|
Frame с `activity` заменяет предыдущую activity целиком.
|
|
299
288
|
Используйте согласованные версии runtime/devtools: предыдущие ревизии графа, кадров и activity отвергаются; presence слабого ребра требует graph `/5` и frame `/4`. Activity указывает execution,
|
|
300
|
-
фактическое поколение feature, физические
|
|
289
|
+
фактическое поколение feature, физические вызовы, точных текущих lane blockers, а для каждого зарегистрированного
|
|
301
290
|
Resource — его `kind`, `lifetime`, `epoch`, `state`, `activity` вместе с `operation`, `leases`, `subscribers`,
|
|
302
291
|
`pagination` и `epoch` каждой физической загрузки: ни `attempt`, ни числового generation здесь нет, потому что
|
|
303
292
|
Resource делает одну логическую попытку, а восстановление принадлежит транспорту (D358). Спрос хоста и UI-модели
|
|
304
293
|
неизвестны. `freshness`
|
|
305
294
|
и `truncated` отличают полное live-наблюдение от усечённого или отключённого. Закрытая session имеет stale-снимок;
|
|
306
|
-
`closed: true`
|
|
295
|
+
`closed: true` говорит, что закрытие приложения завершилось, успешным был его физический drain или отказавшим, и
|
|
296
|
+
снимок закрытого приложения не называет ни одной фичи и ни одного Resource (D427). Control authority и продуктовые payload не добавляются.
|
|
307
297
|
Размер activity ограничен capacity записей. Сбор снимка обходит зарегистрированных owners, executors и resources,
|
|
308
298
|
поэтому capacity не ограничивает стоимость обхода. Сбор останавливается после доказанного truncation;
|
|
309
299
|
idle executors могут требовать обхода, чтобы подтвердить полноту данных. Обычный call dispatch не создаёт диагностических записей при
|
|
@@ -316,14 +306,14 @@ idle executors могут требовать обхода, чтобы подтв
|
|
|
316
306
|
называет один вход (D285):
|
|
317
307
|
|
|
318
308
|
- `renderSlot(target, { contribution, models, props })` монтирует опубликованную цель либо один вклад-фикстуру;
|
|
319
|
-
- `command(run)` выдаёт подлинный `
|
|
320
|
-
- `
|
|
309
|
+
- `command(run)` выдаёт подлинный `Command` для фикстуры модели плюс привязку вклада, которую читает монтирование;
|
|
310
|
+
- `runCommand(target, input?, options?)` запускает существующий подлинный `Command` без монтирования React (D261);
|
|
321
311
|
- `testReadable(initial)`, `testResourceEpoch()`, `openModel`, `settled`, `waitFor`, `yieldTurn` и `eventually`
|
|
322
312
|
приходят из входов ниже — для той части теста, в которой UI нет; `testResourceEpoch()` это то, что фикстура
|
|
323
313
|
компонента, собирающая `ResourceState` руками, кладёт в её `epoch` (D362).
|
|
324
314
|
|
|
325
315
|
Тест, который ничего не рендерит, должен импортировать `@opetope/runtime/testing` напрямую: React здесь нужен
|
|
326
|
-
только `renderSlot` и привязочной половине `command`. Сам
|
|
316
|
+
только `renderSlot` и привязочной половине `command`. Сам Command фикстуры чеканит `command` из
|
|
327
317
|
`@opetope/core/testing` (D300), и это единственное имя цепочки реэкспортов, которое насквозь не проходит:
|
|
328
318
|
React-`command` заслоняет его своим, более сильным.
|
|
329
319
|
|
|
@@ -332,21 +322,21 @@ React-`command` заслоняет его своим, более сильным.
|
|
|
332
322
|
компонентного теста нет фичи, которой можно сообщить, — и упакованный потребитель `ci:pack` монтирует его из
|
|
333
323
|
настоящего архива (D301).
|
|
334
324
|
|
|
335
|
-
Тест, который раньше монтировал `useCommand` только ради вызова
|
|
336
|
-
Результат — выход
|
|
325
|
+
Тест, который раньше монтировал `useCommand` только ради вызова Command, может использовать `runCommand` напрямую.
|
|
326
|
+
Результат — выход Command; тест UI по-прежнему проверяет `CommandOutcome`, возвращённый смонтированным consumer:
|
|
337
327
|
|
|
338
328
|
```ts
|
|
339
329
|
import assert from 'node:assert/strict';
|
|
340
330
|
import { defineModel } from '@opetope/core';
|
|
341
|
-
import type {
|
|
331
|
+
import type { Command, ModelCommandContext } from '@opetope/core';
|
|
342
332
|
import { defineFeature, openFeature } from '@opetope/runtime';
|
|
343
|
-
import {
|
|
333
|
+
import { runCommand } from '@opetope/react/testing';
|
|
344
334
|
|
|
345
|
-
const Arithmetic = defineModel<{ readonly double:
|
|
335
|
+
const Arithmetic = defineModel<{ readonly double: Command<number, number> }>('example.testing.model');
|
|
346
336
|
const arithmetic = defineFeature('example.testing.arithmetic', {
|
|
347
337
|
own: ({ model }) => ({
|
|
348
338
|
arithmetic: model(Arithmetic, context => ({
|
|
349
|
-
double: context.
|
|
339
|
+
double: context.command(({ input }: ModelCommandContext<number>) => input * 2),
|
|
350
340
|
})),
|
|
351
341
|
}),
|
|
352
342
|
exports: ({ own }) => ({ double: own.arithmetic.double }),
|
|
@@ -354,35 +344,35 @@ const arithmetic = defineFeature('example.testing.arithmetic', {
|
|
|
354
344
|
const instance = openFeature(arithmetic, { imports: {}, reporter: () => undefined });
|
|
355
345
|
try {
|
|
356
346
|
const ready = await instance.ready;
|
|
357
|
-
const result = await
|
|
347
|
+
const result = await runCommand(ready.exports.double, 3);
|
|
358
348
|
assert.equal(result, 6);
|
|
359
349
|
} finally {
|
|
360
350
|
await instance.close();
|
|
361
351
|
}
|
|
362
352
|
```
|
|
363
353
|
|
|
364
|
-
`
|
|
365
|
-
UI-статус, обёртку outcome, политику выполнения, создание модели или отдельное время жизни для cleanup. У
|
|
354
|
+
`runCommand` возвращает `Promise<Output>` и сохраняет исходные ошибки и причины отклонения при отмене Command. Он не добавляет
|
|
355
|
+
UI-статус, обёртку outcome, политику выполнения, создание модели или отдельное время жизни для cleanup. У Command остаются
|
|
366
356
|
его владелец, lane и правила retirement; тест закрывает открытый экземпляр, в том числе при отказе readiness.
|
|
367
|
-
Для void
|
|
368
|
-
`
|
|
369
|
-
`
|
|
357
|
+
Для void Command достаточно `runCommand(target)`. Options всегда передаются третьим аргументом:
|
|
358
|
+
`runCommand(target, input, { signal })` или `runCommand(voidTarget, undefined, { signal })`.
|
|
359
|
+
`RunCommandOptions` содержит только необязательный `signal`; уже отменённый сигнал отклоняет вызов до входа в его тело.
|
|
370
360
|
|
|
371
|
-
`run`, `
|
|
361
|
+
`run`, `runCommand` и вложенный `invoke` используют одно правило входа (D273): `void` и `undefined` можно опустить;
|
|
372
362
|
остальные входы, включая `T | undefined`, требуют аргумент. `never` нельзя вызвать без входа.
|
|
373
363
|
Обобщённые функции могут передавать цель и её явный вход с сохранением типов.
|
|
374
364
|
|
|
375
365
|
## Карта слов и входы
|
|
376
366
|
|
|
377
|
-
| Вход | Что содержит
|
|
378
|
-
| ---------------------------- |
|
|
379
|
-
| `@opetope/react` | `useModel`, `useCommand`, `
|
|
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`; только для тестов
|
|
367
|
+
| Вход | Что содержит |
|
|
368
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
369
|
+
| `@opetope/react` | `useModel`, `useCommand`, `useReadable`, `useSelector`, `useResource`, `requiresModels`, `defineSlot`, `defineSwitchSlot`, `Slot`, `ContributionError`; типы `SlotTarget`, `SwitchSlotTarget`, `SlotContribution`, `CommandHook`, `CommandOutcome`, `ResourceHook`, `PaginatedResourceHook`, `ResourcePaginationHook` |
|
|
370
|
+
| `@opetope/react/integration` | `FeatureBoundary`, `ContributionBoundary`, `useFeature`, `useFeatureRetry`, `FeatureBoundaryError` для integration-слоя; типы `FeatureBoundaryProps`, `ContributionBoundaryProps`, `ContributionErrorContent`, `ContributionFailure`, `FeatureDemandSource`, `FeatureDemandState`, `FeatureDemandResult`, `FeatureDemandLease` |
|
|
371
|
+
| `@opetope/react/testing` | `renderSlot`, `command`, `createScenario`, `ScenarioTimeoutError` и типы fixtures/scenarios плюс всё, что публикуют `@opetope/runtime/testing` и `@opetope/core/testing`; только для тестов |
|
|
382
372
|
|
|
383
|
-
`
|
|
373
|
+
`Command<Input, Output>` импортируется из `@opetope/core`, а UI-результат `CommandOutcome<Output>` — из `@opetope/react`. Прежний синоним `Command` и переэкспорт `CommandOutcome` из `@opetope/react/integration` удалены (D270).
|
|
384
374
|
|
|
385
|
-
`Model` — типизированный ключ записи из `Readable`, `
|
|
375
|
+
`Model` — типизированный ключ записи из `Readable`, `Command` и фабрик readable, которую используют UI-компоненты.
|
|
386
376
|
Конструкторы integration-входа не переэкспортируются из безопасного входа и не могут вернуться в
|
|
387
377
|
`ui/models/data/contracts` через локальный barrel фичи.
|
|
388
378
|
|
|
@@ -392,7 +382,7 @@ UI-статус, обёртку outcome, политику выполнения,
|
|
|
392
382
|
- один закоммиченный вклад владеет своим кадром моделей до размонтирования или retire;
|
|
393
383
|
- брошенный concurrent-рендер кадр не удерживает;
|
|
394
384
|
- `useCommand` не подписывается на invoker: наблюдаемое состояние вызова лежит в `Readable` модели;
|
|
395
|
-
- исход `cancelled` не двигает `outcome`, и это только коды `
|
|
385
|
+
- исход `cancelled` не двигает `outcome`, и это только коды `CommandError` `cancelled`
|
|
396
386
|
и `closed`; вызов слабого порта без провайдера (`unavailable`) и отклонённая публикация
|
|
397
387
|
(`publication-rejected`) приходят исходом `failed` и оседают в `outcome` (D138, D292);
|
|
398
388
|
- вклад, упавший в рендере или коммите, изолируется своим монтированием и сообщается своей фиче;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CommandInputArgs, CallRunOptions } from '@opetope/core/internal';
|
|
2
2
|
import type { CommandOutcome } from './command.js';
|
|
3
3
|
type CommandRunOptions<Output> = Readonly<{
|
|
4
4
|
onFailure?: (error: unknown) => PromiseLike<void> | void;
|
|
@@ -7,15 +7,15 @@ type CommandRunOptions<Output> = Readonly<{
|
|
|
7
7
|
}>;
|
|
8
8
|
type CommandRun<Input, Output> = {
|
|
9
9
|
(input: Input, options?: CommandRunOptions<Output>): Promise<CommandOutcome<Output>>;
|
|
10
|
-
(...args: [...
|
|
10
|
+
(...args: [...CommandInputArgs<Input>, options?: CommandRunOptions<Output>]): Promise<CommandOutcome<Output>>;
|
|
11
11
|
};
|
|
12
12
|
type CommandInvoker = Readonly<{
|
|
13
13
|
run: (input: never, options?: CallRunOptions<unknown>) => Promise<unknown>;
|
|
14
14
|
}>;
|
|
15
15
|
type CommandNotification<Output> = (inFlight: boolean, outcome?: CommandOutcome<Output>) => void;
|
|
16
16
|
/**
|
|
17
|
-
* D203: the consumer keeps a status, not a schedule. Every `run` reaches the
|
|
18
|
-
*
|
|
17
|
+
* D203: the consumer keeps a status, not a schedule. Every `run` reaches the command, and what the command was
|
|
18
|
+
* declared with — `concurrency`, `memoize`, `dedupe` — decides what happens to it. What stays
|
|
19
19
|
* local is what only this consumer knows: whether it is mounted, its own input signal, and the outcome it shows.
|
|
20
20
|
*/
|
|
21
21
|
declare class CommandHookController<Input, Output> {
|
package/dist/command-hook.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Command } from '@opetope/core';
|
|
2
2
|
import type { CommandOutcome } from './command.js';
|
|
3
3
|
import { CommandHookController } from './command-hook-controller.js';
|
|
4
4
|
import type { CommandRun } from './command-hook-controller.js';
|
|
5
5
|
/** D292: a cancellation is not an answer of the product, so it never settles into the outcome this hook shows. */
|
|
6
6
|
type SettledOutcome<Output> = Exclude<CommandOutcome<Output>, {
|
|
7
|
-
readonly
|
|
7
|
+
readonly kind: 'cancelled';
|
|
8
8
|
}>;
|
|
9
9
|
interface CommandHookStatus<Output> {
|
|
10
10
|
readonly inFlight: boolean;
|
|
@@ -19,6 +19,6 @@ type CommandHook<Input, Output> = CommandHookStatus<Output> & {
|
|
|
19
19
|
* it, and the synchronisation of a slot that was already busy when this mount arrived.
|
|
20
20
|
*/
|
|
21
21
|
declare function useCommandStatus<Input, Output>(currentSlot: CommandHookController<Input, Output>): CommandHook<Input, Output>;
|
|
22
|
-
declare function useCommand<Input, Output>(command:
|
|
22
|
+
declare function useCommand<Input, Output>(command: Command<Input, Output>): CommandHook<Input, Output>;
|
|
23
23
|
export { useCommand, useCommandStatus };
|
|
24
24
|
export type { CommandHook };
|
package/dist/command.d.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
type CommandBinding<Input, Output> =
|
|
1
|
+
import type { Command } from '@opetope/core';
|
|
2
|
+
type CommandBinding<Input, Output> = Command<Input, Output>;
|
|
3
3
|
type CommandOutcome<Output> = {
|
|
4
4
|
readonly error: unknown;
|
|
5
|
-
readonly
|
|
5
|
+
readonly kind: 'failed';
|
|
6
6
|
} | {
|
|
7
7
|
readonly reason: unknown;
|
|
8
|
-
readonly
|
|
8
|
+
readonly kind: 'cancelled';
|
|
9
9
|
} | {
|
|
10
|
-
readonly
|
|
10
|
+
readonly kind: 'ok';
|
|
11
11
|
readonly value: Output;
|
|
12
12
|
};
|
|
13
13
|
type AnyCommandBinding = CommandBinding<never, unknown>;
|
|
14
14
|
type CommandBindingDefinition<Input, Output> = {
|
|
15
|
-
readonly target:
|
|
15
|
+
readonly target: Command<Input, Output>;
|
|
16
16
|
};
|
|
17
|
-
declare function bindCommand<Input, Output>(target:
|
|
18
|
-
declare function getCommandBindingDefinition<Input, Output>(binding:
|
|
17
|
+
declare function bindCommand<Input, Output>(target: Command<Input, Output>): CommandBinding<Input, Output>;
|
|
18
|
+
declare function getCommandBindingDefinition<Input, Output>(binding: Command<Input, Output>): CommandBindingDefinition<Input, Output>;
|
|
19
19
|
declare function isCommandBinding(value: unknown): value is AnyCommandBinding;
|
|
20
20
|
export { bindCommand, getCommandBindingDefinition, isCommandBinding };
|
|
21
21
|
export type { CommandOutcome };
|
|
@@ -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 w,memo as N,useState as W}from"react";import{reportDetachedError as C,compatibleAbortReason as K,isCallTarget as _,isModel as G,isReadable as j,createLifetimeController as H,createCallController as J,getContributionOwner as Q,createState as U,createAggregateError as X}from"@opetope/core/internal";import{isResourceValue as Y,reportRuntimeFailure as Z,requireContributionModelPlans as ee,openContributionModels as te}from"@opetope/runtime/internal";import{C as u,u as ne,b as re}from"./contribution-isolation-Bzfbt4iM.js";import{CommandError as ie}from"@opetope/core";const x=V(void 0);function oe(){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 Y(t)}function se(t,e){return new Promise((n,r)=>{const i=()=>{r(new ie("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 ae(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(C),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:se(r,i)).then(o=>ae(o,n))}}}const ce=Object.freeze({kind:"skipped",reason:"resource-not-ready"});function de(t){const e=t.pagination;if(!(typeof e!="object"||e===null))return e}function E(t){return{loadNext:v(()=>{const e=t.pagination;return e===void 0?Promise.resolve(ce):e.loadNext()}),refresh:v(()=>t.refresh()),reset:v(()=>t.reset()),retry:v(()=>t.retry())}}function ue(t,e){const n=e.get(t);if(n!==void 0)return n;const r=E(t);return e.set(t,r),r}const le=t=>t;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()=>{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,le)}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(!_(t))throw new u("binding-invalid","Command target is not authentic.");return M.set(t,{target:t}),t}function fe(t){const e=M.get(t);if(e===void 0)throw new u("binding-invalid","Command binding is not authentic.");return e}function $(t){return typeof t=="object"&&t!==null&&M.has(t)}const he=Symbol("opetope.model-binding"),me=Symbol("opetope.model-binding-identity"),be=Object.freeze({}),L=new WeakMap;function pe(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 ge(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=pe(t,e,i);if(!(j(o)||y(o)||typeof o=="function")){if(!$(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(!G(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=ge(t,e),r=Object.freeze({[he]:be,[me]:!0});return L.set(r,{commands:n,declaration:t,model:e}),r}function we(t){const e=L.get(t);if(e===void 0)throw new u("binding-invalid","Model binding is not authentic.");return e}let Ce=0;function ve(t){const e=new Map,n=[],r=new Set;for(const i of t){const o=we(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 ye{frame;authority;boundCommands;closeTicket=0;isActive;lifetime;onClose;released=!1;rendererAttached=!1;renderListeners=new Set;state="detached";constructor(e,n){const r=++Ce;this.lifetime=H({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}=ve(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){fe(r);const i=J(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){C(n)}}releaseFrame(){this.released||(this.released=!0,this.frame.commandRecords.clear(),this.frame.models.clear(),this.boundCommands=[],this.renderListeners.clear())}}function Me({children:t,mount:e}){const n=k(e.subscribeRenderable,e.getRenderableSnapshot,e.getRenderableSnapshot);return A(()=>{if(e.attach())return()=>e.detach()},[e]),w(()=>{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 Re(t,e){if(typeof e!="object"||e===null)return q(t,e);const n=i=>j(i)||y(i)||typeof i=="function"||$(i),r=Object.entries(e).map(([i,o])=>[i,n(o)?o:I(o)]);return q(t,Object.fromEntries(r))}function Se(t){return Q(t)}const Oe={models:[]};function P(t,e){try{t()}catch(n){throw X([e,n],"Contribution mount setup and cleanup failed.",{cause:e})}throw e}function ke(t,e,n,r){const i=U(e),{models:o,reporter:c}=r??Oe;try{const s=ee(t.models),a=s.length===0?void 0:te(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 Ae(t,e,n,r){const{bundle:i,models:o,props:c}=ke(t,e,n,r);let s=!1;const a=()=>{if(!s){s=!0;try{i==null||i.close().catch(C)}finally{c.close()}}};try{return{frame:new ye(o.map(([d,h])=>Re(d,h)),{isActive:()=>!s,onClose:a}),isOpen:()=>!s,props:c}}catch(l){return P(a,l)}}function je(t,e){return m(t,{...e})}function xe({bundle:t,contribution:e}){const n=z(t.props),r=p(()=>e.props===void 0?n:e.props(n),[n,e]);return m(Me,{mount:t.frame,children:je(e.Component,r)})}function Ee({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 w(()=>{a.current=r}),A(()=>()=>{i.retired=!0,queueMicrotask(()=>{try{B(i)}catch(l){C(l)}})},[i]),w(()=>{const l=i.bundle??(i.bundle=Ae(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]),w(()=>{(s==null?void 0:s.isOpen())===!0&&s.props.set(r)},[s,r]),s===void 0?null:m(xe,{bundle:s,contribution:e})}function Fe(t){const{authority:e,contribution:n,contributionId:r,fixtures:i,targetId:o}=t,c=ne(),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=>Z(f,d),[d]);return m(re,{content:c,identity:s,report:h,source:l,children:m(Ee,{...t})})}const ze=N(Fe);export{ze as M,z as a,Se as b,E as c,F as d,I as e,y as i,de as p,ue as r,oe as u};
|
|
2
|
+
//# sourceMappingURL=contribution-frame-CCKnOxZR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contribution-frame-CCKnOxZR.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 { CommandError } 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\n/**\n * D439: the widest Resource, and the name says so. `Resource<never, ResourceKey>` said the opposite in both\n * arguments — `never` is the narrowest `Data` a reader can be handed and a named `Key` *requires* `state.key`, so\n * hardly any member of the family fitted it. Nothing was refused, because every value reaches this name through a\n * cast, which is exactly why the name could drift. `ResourceKeyField<never>` requires no field, so\n * `Resource<unknown, never>` is the one form every Resource satisfies — keyed or not, paginated or not.\n */\ntype AnyResource = Resource<unknown, never>;\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 * `{ kind: '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 /** Present when the declaration carried `pagination`; `undefined` for every other Resource. */\n pagination: ResourcePaginationHook<ResourceKey> | undefined;\n refresh: CommandHook<void, ResourceOperationOutcome>;\n reset: 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 loadNext: CommandInvoker;\n refresh: CommandInvoker;\n reset: 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 CommandError('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 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 reset: operationInvoker(() => resource.reset()),\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 type { EqualityOption } from '@opetope/core/internal';\n\nconst identity = <Value>(value: Value): Value => value;\n\n/**\n * D364: what this mount answered last, and nothing about closes. A source whose owner retired stops instead of\n * refusing — it keeps answering its last value — so a render that races a retirement shows its last frame and\n * unmounts (G13) without the reader having to remember anything for it.\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 (): 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 { Command } from '@opetope/core';\nimport { isCallTarget } from '@opetope/core/internal';\n\nimport { ContributionError } from './errors';\n\n/**\n * A bound command is the authentic `Command` 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 = Command<never, unknown>;\ntype CommandBinding<Input, Output> = Command<Input, Output>;\n\ntype CommandOutcome<Output> =\n | { readonly error: unknown; readonly kind: 'failed' }\n | { readonly reason: unknown; readonly kind: 'cancelled' }\n | { readonly kind: 'ok'; readonly value: Output };\n\ntype AnyCommandBinding = CommandBinding<never, unknown>;\n\ntype CommandBindingDefinition<Input, Output> = {\n readonly target: Command<Input, Output>;\n};\n\nconst bindingDefinitions = new WeakMap<CommandBindingIdentity, CommandBindingDefinition<never, unknown>>();\n\nfunction bindCommand<Input, Output>(target: Command<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: Command<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 { Command, ModelOf } from '@opetope/core';\nimport { isModel, isReadable } from '@opetope/core/internal';\nimport type { ExactKeys, ModelIdentity } 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 = Command<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 Keys extends PropertyKey = never>(\n declaration: Declaration,\n model: ExactKeys<Keys, ModelOf<Declaration>> & 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 Command 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 Commands, 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","CommandError","compatibleAbortReason","error","notifySuccess","options","notification","_a","reportDetachedError","operationInvoker","start","_input","unavailablePage","paginationOf","resource","capability","createResourceInvokers","resourceInvokers","cache","existing","invokers","identity","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
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export type { CommandOutcome } from './command.js';
|
|
2
2
|
export { useCommand } from './command-hook.js';
|
|
3
3
|
export type { CommandHook } from './command-hook.js';
|
|
4
|
-
export { useCommands } from './commands-hook.js';
|
|
5
4
|
export { ContributionError } from './errors.js';
|
|
6
5
|
export { useModel } from './model-hook.js';
|
|
7
6
|
export { useReadable, useSelector } from './readable-hooks.js';
|