@opetope/react 0.6.0 → 0.8.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 +52 -0
- package/README.md +37 -17
- package/README.ru.md +37 -17
- package/dist/command-hook-controller.d.ts +2 -1
- package/dist/command-hook.d.ts +6 -2
- package/dist/contribution-frame-DXl7K0-B.js +2 -0
- package/dist/contribution-frame-DXl7K0-B.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/readable-hooks.d.ts +4 -1
- package/dist/scenario-types.d.ts +2 -2
- package/dist/scenario.d.ts +2 -1
- package/dist/slot.d.ts +3 -6
- package/dist/testing.js +3 -3
- package/dist/testing.js.map +1 -1
- package/package.json +5 -5
- package/dist/contribution-frame-B1PnpOaP.js +0 -2
- package/dist/contribution-frame-B1PnpOaP.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,57 @@
|
|
|
1
1
|
# @opetope/react
|
|
2
2
|
|
|
3
|
+
## 0.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- dee6f21: Let a feature answer a condition. `defineCondition(id)` is now only a name; the fact behind it comes either from a feature of the world, through the fifth word of `provides` — `condition(target, ({ own }) => readable)` — or from the host, and never from both. The answer reads `own`, so a fact no longer has to widen `exports` to gate anything, and the name can be declared in a shared module that imports no feature.
|
|
8
|
+
|
|
9
|
+
One id keeps at most one answer: two answering features, a feature waiting for the condition it answers, an answer shorter-lived than what it gates, and an answer to a condition that already names its own source are refused when the application compiles. A lazy header declares the condition exactly as it declares a port, and its body is held to that declaration.
|
|
10
|
+
|
|
11
|
+
`openApplication` and `createScenario` take `conditions` as an array of `bind(condition, readable)` — the same word `imports` uses. Identity comes from the token instead of a repeated id string, and the type asks for exactly the host conditions the world does not answer itself, naming a missing or unexpected binding by its id. `defineCondition(id, { from, select })` stays for the other relationship: a consumer reading one feature's exports, declared where that reading belongs.
|
|
12
|
+
|
|
13
|
+
- aff7b5d: Give every authoring verb one shape: `verb(subjects…, behaviours…, options?)` (D279). What a step works on and the work itself are arguments; only modifiers stay in the trailing record. This covers `call`, `effect`, `event`, `resource`, `stream`, the `scope` openers, `derive`, `collection`, `lane` and `useSelector`; `key` became a modifier, and a target that already is a request key names itself. `attach` and `pipe` keep their contract-declaring records (D223).
|
|
14
|
+
|
|
15
|
+
Name every definition the same way: `defineX(id, options?)` (D280), including `defineFeature` and `defineApplication`, whose `id` left the sections record. Move `reporter` from `defineApplication` to `openApplication` and `createScenario`, and make an empty `imports` omissible on both `open*` verbs (D281). Add the updater form `ctx.update(state, previous => next)` (D282) and rename `CommandHook.result` to `lastResult` (D283). No compatibility aliases: packages, examples, guides and the ESLint rules move together.
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- Updated dependencies [dee6f21]
|
|
20
|
+
- Updated dependencies [aff7b5d]
|
|
21
|
+
- @opetope/core@0.8.0
|
|
22
|
+
- @opetope/runtime@0.8.0
|
|
23
|
+
|
|
24
|
+
## 0.7.0
|
|
25
|
+
|
|
26
|
+
### Minor Changes
|
|
27
|
+
|
|
28
|
+
- 538cdfe: Replace Lookup/fromOptional with Maybe/fromMaybe and some/none branches across collections, registries and optional imports. Add pure maybe operations and reactive projections that preserve absence unless a fallback is supplied.
|
|
29
|
+
|
|
30
|
+
Select a single dependency method with call(source, key), or several with calls(source, keys). Support optional host and feature providers, including inside models, and an explicit whenMissing: 'none' outcome while preserving provider failures, cancellation and physical ownership. Existing custom call({ run }) remains available. This alpha release removes the former names.
|
|
31
|
+
|
|
32
|
+
Devtools weak edges now carry presence: some/none. Graph schema 5 and frame schema 4 reject the previous lookup field and revisions.
|
|
33
|
+
|
|
34
|
+
- 538cdfe: Add a stable `.handler` to inputless CommandHook values returned by useCommand, useCommands and useModel selections. Use onClick={logout.handler} to ignore the React event and invoke the command with undefined. Retain .run for explicit inputs, callbacks, signals and CommandOutcome. Preserve immutable status snapshots, errors, cancellation and binding lifetime.
|
|
35
|
+
|
|
36
|
+
### Patch Changes
|
|
37
|
+
|
|
38
|
+
- 538cdfe: Make computed accept its calculation first: computed(get => value, options?). The injected get tracks each Readable used by the calculation; the optional second argument supplies result equality. Remove the former object argument without a compatibility overload.
|
|
39
|
+
|
|
40
|
+
Keep derive({ from, select }) for explicit sources and preserve the existing reactive scheduling and lifetime behavior. Migrate framework consumers and add a compile-checked model example in the English and Russian guides.
|
|
41
|
+
|
|
42
|
+
- 538cdfe: Replace singleFlight with dedupe on custom Calls. Use dedupe: true to share one pending or running execution per Call and generation, or a key function to share equivalent inputs. Retain the first input and independent caller cancellation. Remove the former name without a compatibility alias.
|
|
43
|
+
|
|
44
|
+
Reject both forms of dedupe with policy: latest in types and at runtime. Different keys keep one FIFO under queue and run independently under parallel. Preserve once caching before deduplication and keep scheduling options on Call declarations rather than React hooks. Add a compile-checked model example and update the English and Russian guides.
|
|
45
|
+
|
|
46
|
+
- Updated dependencies [538cdfe]
|
|
47
|
+
- Updated dependencies [538cdfe]
|
|
48
|
+
- Updated dependencies [538cdfe]
|
|
49
|
+
- Updated dependencies [538cdfe]
|
|
50
|
+
- Updated dependencies [538cdfe]
|
|
51
|
+
- Updated dependencies [538cdfe]
|
|
52
|
+
- @opetope/core@0.7.0
|
|
53
|
+
- @opetope/runtime@0.7.0
|
|
54
|
+
|
|
3
55
|
## 0.6.0
|
|
4
56
|
|
|
5
57
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -26,19 +26,19 @@ const CounterModel = defineModel<{
|
|
|
26
26
|
readonly count: Readable<number>;
|
|
27
27
|
readonly increment: Call<void, void>;
|
|
28
28
|
}>('example.counter.model');
|
|
29
|
-
const CounterSlot = defineSlot<{ readonly label: string }>(
|
|
29
|
+
const CounterSlot = defineSlot<{ readonly label: string }>('example.counter.slot');
|
|
30
30
|
|
|
31
31
|
const CounterButton = requiresModels([CounterModel])(({ label }: { readonly label: string }) => {
|
|
32
32
|
const {
|
|
33
33
|
count,
|
|
34
|
-
increment: { inFlight, lastError
|
|
34
|
+
increment: { handler, inFlight, lastError },
|
|
35
35
|
} = useModel(CounterModel, (model, { read }) => ({
|
|
36
36
|
count: read(model.count),
|
|
37
37
|
increment: model.increment,
|
|
38
38
|
}));
|
|
39
39
|
|
|
40
40
|
return (
|
|
41
|
-
<button disabled={inFlight} onClick={
|
|
41
|
+
<button disabled={inFlight} onClick={handler}>
|
|
42
42
|
{lastError ? 'Retry' : `${label}: ${count}`}
|
|
43
43
|
</button>
|
|
44
44
|
);
|
|
@@ -50,11 +50,18 @@ function CounterArea() {
|
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
A component gets neither a service, nor a class, nor a runtime ref. The contribution mount grants its feature's `own` models implicitly; the component or hook that reads a per-mount
|
|
53
|
-
model declares it with `requiresModels`. `useModel` reads exactly that authority frame. `useCommand` returns `{ run, inFlight,
|
|
53
|
+
model declares it with `requiresModels`. `useModel` reads exactly that authority frame. `useCommand` returns `{ run, inFlight, lastResult, lastError }`
|
|
54
54
|
and always resolves the outcome, so a normal cancellation never becomes an unhandled rejection (D116).
|
|
55
55
|
|
|
56
|
-
`
|
|
57
|
-
`
|
|
56
|
+
For `Call<void, Output>` and `Call<undefined, Output>`, the hook also exposes `handler: () => void` (D278).
|
|
57
|
+
Use `onClick={logout.handler}`: it ignores the React event and invokes the Call with `undefined`.
|
|
58
|
+
Commands with data, including `T | undefined`, `unknown`, `any` and `never`, do not expose a handler in their type.
|
|
59
|
+
Use `run(input, options?)` for data, an outcome, callbacks or a per-run signal. The handler reports failures through
|
|
60
|
+
`lastError` and preserves the same cancellation behavior; it returns no promise or outcome.
|
|
61
|
+
|
|
62
|
+
`run` and `handler` are stable while the invoker stays the same; the returned object changes as `inFlight`, `lastResult`
|
|
63
|
+
or `lastError` changes. Destructure the function for effect dependencies. A handler captured from a replaced or
|
|
64
|
+
unmounted consumer is fenced; it does not start work on the replacement.
|
|
58
65
|
|
|
59
66
|
The hook schedules nothing. Every `run` reaches the call, and the policy the call was created with decides what
|
|
60
67
|
happens to it (D203). For an absolute value setter the model declares `policy: 'latest'` in `context.call`, and the
|
|
@@ -63,6 +70,10 @@ input signal is refused as `cancelled` without reaching the call, a run of an un
|
|
|
63
70
|
way, `inFlight` is true while any run this consumer started is unsettled, and every run carries its own callbacks
|
|
64
71
|
and signal.
|
|
65
72
|
|
|
73
|
+
To share equivalent pending or running work, the model declares `dedupe: true` or `dedupe: input => key`
|
|
74
|
+
on `context.call`. Sharing keeps the first input and each caller's independent cancellation; neither form may
|
|
75
|
+
be combined with `latest` (D277). These are Call options, not hook options.
|
|
76
|
+
|
|
66
77
|
For several commands use an object selection (D174):
|
|
67
78
|
|
|
68
79
|
```tsx
|
|
@@ -91,6 +102,18 @@ A model selection can use a named interface without an index signature (D217). I
|
|
|
91
102
|
arrays, functions, constructors and built-in collection/date/promise objects are not selection records.
|
|
92
103
|
`read(source)` returns its snapshot; `read(source, project)` returns a projection. Authentic Calls selected as record
|
|
93
104
|
fields become the same `CommandHook` as `useCommands`, including independent alias statuses and stable `.run`.
|
|
105
|
+
The same inputless `.handler` is available in `useCommands` and model selections. A single selected command needs
|
|
106
|
+
no nested hooks:
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
const { logout } = useModel(AuthModel, auth => ({ logout: auth.logout }));
|
|
110
|
+
return (
|
|
111
|
+
<button disabled={logout.inFlight} onClick={logout.handler}>
|
|
112
|
+
Log out
|
|
113
|
+
</button>
|
|
114
|
+
);
|
|
115
|
+
```
|
|
116
|
+
|
|
94
117
|
The one-argument form still returns the granted model; individual hooks remain available.
|
|
95
118
|
|
|
96
119
|
Only explicitly read sources are subscribed, once per distinct Readable in the selection. Returned data fields are
|
|
@@ -153,10 +176,8 @@ root, no `ui` section and no second binding API any more (D85).
|
|
|
153
176
|
```tsx
|
|
154
177
|
import { defineSlot, defineSwitchSlot, Slot } from '@opetope/react';
|
|
155
178
|
|
|
156
|
-
const HeaderEnd = defineSlot<{ readonly mode: 'desktop' | 'phone' }>(
|
|
157
|
-
const PageEnd = defineSwitchSlot<'home' | 'wallet', { readonly compact: boolean }>(
|
|
158
|
-
id: 'example.pageEnd',
|
|
159
|
-
});
|
|
179
|
+
const HeaderEnd = defineSlot<{ readonly mode: 'desktop' | 'phone' }>('example.headerEnd');
|
|
180
|
+
const PageEnd = defineSwitchSlot<'home' | 'wallet', { readonly compact: boolean }>('example.pageEnd');
|
|
160
181
|
|
|
161
182
|
function Header({ mode }: { readonly mode: 'desktop' | 'phone' }) {
|
|
162
183
|
return <Slot props={{ mode }} target={HeaderEnd} />;
|
|
@@ -234,11 +255,11 @@ a scoped zero-count assertion, without proving absence of arbitrary host, UI or
|
|
|
234
255
|
application imports and internal renderer references. A failed cleanup promise can retain original errors and retry
|
|
235
256
|
capabilities; a caller that keeps `mounted.host` also keeps its own renderer result.
|
|
236
257
|
|
|
237
|
-
The inspection schemas are `opetope.devtools-graph/
|
|
238
|
-
`opetope.runtime-activity/1` snapshots (D266). Within one session,
|
|
258
|
+
The inspection schemas are `opetope.devtools-graph/5` and `opetope.devtools-frame/4`, with optional
|
|
259
|
+
`opetope.runtime-activity/1` snapshots (D266, D275). Within one session,
|
|
239
260
|
a frame without `activity` preserves the previous activity; a full snapshot/reset without it clears that observation
|
|
240
261
|
(D216). Activity-bearing frames replace the previous activity in full.
|
|
241
|
-
Use matching runtime/devtools versions: graph
|
|
262
|
+
Use matching runtime/devtools versions: previous graph/frame revisions are rejected; weak-edge presence requires graph `/5` and frame `/4`. Activity identifies the execution,
|
|
242
263
|
actual feature generation, physical Calls, exact current lane blockers, registered resource leases and load attempts.
|
|
243
264
|
Host demand and UI models are unknown; stream observation covers state, not physical load identities. `freshness`
|
|
244
265
|
and `truncated` distinguish a complete live view from a partial or detached one. A closed session is stale;
|
|
@@ -270,11 +291,10 @@ import { defineFeature, openFeature } from '@opetope/runtime';
|
|
|
270
291
|
import { runCall } from '@opetope/react/testing';
|
|
271
292
|
|
|
272
293
|
const Arithmetic = defineModel<{ readonly double: Call<number, number> }>('example.testing.model');
|
|
273
|
-
const arithmetic = defineFeature({
|
|
274
|
-
id: 'example.testing.arithmetic',
|
|
294
|
+
const arithmetic = defineFeature('example.testing.arithmetic', {
|
|
275
295
|
own: ({ model }) => ({
|
|
276
296
|
arithmetic: model(Arithmetic, context => ({
|
|
277
|
-
double: context.call(
|
|
297
|
+
double: context.call((input: number) => input * 2),
|
|
278
298
|
})),
|
|
279
299
|
}),
|
|
280
300
|
exports: ({ own }) => ({ double: own.arithmetic.double }),
|
|
@@ -320,7 +340,7 @@ The constructors of the integration entry are not re-exported from the safe entr
|
|
|
320
340
|
- one committed contribution owns its model frame until unmount or retire;
|
|
321
341
|
- an abandoned concurrent render holds no frame;
|
|
322
342
|
- `useCommand` does not subscribe to the invoker: the observable state of a call lies in a `Readable` of the model;
|
|
323
|
-
- the `cancelled` outcome moves neither `
|
|
343
|
+
- the `cancelled` outcome moves neither `lastResult` nor `lastError`, and it is only the `CallError` codes `cancelled`
|
|
324
344
|
and `closed`; a call to a weak port with no provider (`unavailable`) and a rejected publication
|
|
325
345
|
(`publication-rejected`) arrive as the `failed` outcome and settle in `lastError` (D138);
|
|
326
346
|
- a contribution that fails to render or to commit is contained by its own mount and reported to its feature;
|
package/README.ru.md
CHANGED
|
@@ -26,19 +26,19 @@ const CounterModel = defineModel<{
|
|
|
26
26
|
readonly count: Readable<number>;
|
|
27
27
|
readonly increment: Call<void, void>;
|
|
28
28
|
}>('example.counter.model');
|
|
29
|
-
const CounterSlot = defineSlot<{ readonly label: string }>(
|
|
29
|
+
const CounterSlot = defineSlot<{ readonly label: string }>('example.counter.slot');
|
|
30
30
|
|
|
31
31
|
const CounterButton = requiresModels([CounterModel])(({ label }: { readonly label: string }) => {
|
|
32
32
|
const {
|
|
33
33
|
count,
|
|
34
|
-
increment: { inFlight, lastError
|
|
34
|
+
increment: { handler, inFlight, lastError },
|
|
35
35
|
} = useModel(CounterModel, (model, { read }) => ({
|
|
36
36
|
count: read(model.count),
|
|
37
37
|
increment: model.increment,
|
|
38
38
|
}));
|
|
39
39
|
|
|
40
40
|
return (
|
|
41
|
-
<button disabled={inFlight} onClick={
|
|
41
|
+
<button disabled={inFlight} onClick={handler}>
|
|
42
42
|
{lastError ? 'Retry' : `${label}: ${count}`}
|
|
43
43
|
</button>
|
|
44
44
|
);
|
|
@@ -51,11 +51,18 @@ function CounterArea() {
|
|
|
51
51
|
|
|
52
52
|
Компонент не получает ни сервис, ни класс, ни ref рантайма. Монтирование вклада неявно выдаёт модели `own` его фичи;
|
|
53
53
|
компонент или хук, читающий per-mount модель, объявляет её через `requiresModels`. `useModel` читает ровно этот кадр
|
|
54
|
-
авторитета. `useCommand` отдаёт `{ run, inFlight,
|
|
54
|
+
авторитета. `useCommand` отдаёт `{ run, inFlight, lastResult, lastError }`
|
|
55
55
|
и всегда resolve-ит исход, поэтому штатная отмена не становится unhandled rejection (D116).
|
|
56
56
|
|
|
57
|
-
`
|
|
58
|
-
|
|
57
|
+
Для `Call<void, Output>` и `Call<undefined, Output>` хук также предоставляет `handler: () => void` (D278).
|
|
58
|
+
Пишите `onClick={logout.handler}`: обработчик игнорирует React event и вызывает Call с `undefined`.
|
|
59
|
+
У команд с данными, включая `T | undefined`, `unknown`, `any` и `never`, handler недоступен в типе.
|
|
60
|
+
Для данных, исхода, callbacks или сигнала отдельного вызова используйте `run(input, options?)`. Handler показывает
|
|
61
|
+
ошибки через `lastError` и сохраняет обработку отмены; promise или исход он не возвращает.
|
|
62
|
+
|
|
63
|
+
`run` и `handler` стабильны, пока invoker тот же; возвращённый объект меняется вместе с `inFlight`, `lastResult` или
|
|
64
|
+
`lastError`. Для зависимостей эффекта берите функцию деструктуризацией. Сохранённый handler заменённого или
|
|
65
|
+
размонтированного потребителя закрыт для новых вызовов и не перенаправляет работу на замену.
|
|
59
66
|
|
|
60
67
|
Хук ничего не планирует. Каждый `run` доходит до вызова, и решает политика, с которой вызов создан (D203). Для
|
|
61
68
|
setter абсолютного значения модель объявляет `policy: 'latest'` в `context.call`, и новый вход заменяет ожидающий
|
|
@@ -63,6 +70,10 @@ setter абсолютного значения модель объявляет `
|
|
|
63
70
|
доходя до вызова, `run` размонтированного потребителя отвергается так же, `inFlight` истинен, пока не завершился
|
|
64
71
|
хоть один начатый им прогон, и у каждого прогона свои callbacks и signal.
|
|
65
72
|
|
|
73
|
+
Чтобы объединить эквивалентную ожидающую или исполняющуюся работу, модель объявляет `dedupe: true` либо
|
|
74
|
+
`dedupe: input => key` у `context.call`. Сохраняются первый вход и независимая отмена каждого вызывающего;
|
|
75
|
+
обе формы запрещены с `latest` (D277). Это опции Call, а не хука.
|
|
76
|
+
|
|
66
77
|
Для нескольких команд используйте объектный выбор (D174):
|
|
67
78
|
|
|
68
79
|
```tsx
|
|
@@ -91,6 +102,18 @@ const saving = save.inFlight;
|
|
|
91
102
|
arrays, functions, constructors и встроенные объекты коллекций, дат и promises не являются selection records.
|
|
92
103
|
`read(source)` возвращает снимок; `read(source, project)` — проекцию. Authentic Calls, выбранные полями record,
|
|
93
104
|
превращаются в тот же `CommandHook`, что у `useCommands`, с независимыми статусами aliases и стабильным `.run`.
|
|
105
|
+
Тот же `.handler` для команды без входа доступен в `useCommands` и выборе модели. Одну команду можно выбрать без
|
|
106
|
+
вложенных hooks:
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
const { logout } = useModel(AuthModel, auth => ({ logout: auth.logout }));
|
|
110
|
+
return (
|
|
111
|
+
<button disabled={logout.inFlight} onClick={logout.handler}>
|
|
112
|
+
Log out
|
|
113
|
+
</button>
|
|
114
|
+
);
|
|
115
|
+
```
|
|
116
|
+
|
|
94
117
|
Форма с одним аргументом по-прежнему возвращает выданную модель; отдельные hooks сохраняются.
|
|
95
118
|
|
|
96
119
|
Подписка создаётся только на явно прочитанные источники, одна на каждый различный Readable внутри selection.
|
|
@@ -152,10 +175,8 @@ render-колбэком: колбэк `children` получает готовый
|
|
|
152
175
|
```tsx
|
|
153
176
|
import { defineSlot, defineSwitchSlot, Slot } from '@opetope/react';
|
|
154
177
|
|
|
155
|
-
const HeaderEnd = defineSlot<{ readonly mode: 'desktop' | 'phone' }>(
|
|
156
|
-
const PageEnd = defineSwitchSlot<'home' | 'wallet', { readonly compact: boolean }>(
|
|
157
|
-
id: 'example.pageEnd',
|
|
158
|
-
});
|
|
178
|
+
const HeaderEnd = defineSlot<{ readonly mode: 'desktop' | 'phone' }>('example.headerEnd');
|
|
179
|
+
const PageEnd = defineSwitchSlot<'home' | 'wallet', { readonly compact: boolean }>('example.pageEnd');
|
|
159
180
|
|
|
160
181
|
function Header({ mode }: { readonly mode: 'desktop' | 'phone' }) {
|
|
161
182
|
return <Slot props={{ mode }} target={HeaderEnd} />;
|
|
@@ -234,11 +255,11 @@ Deadline готовности также оставляет приложение
|
|
|
234
255
|
host/UI/GC-утечек. Успешный cleanup очищает imports приложения и внутренние ссылки на renderer. Promise отказавшего
|
|
235
256
|
cleanup может удерживать исходные ошибки и retry capabilities; сохранённый пользователем `mounted.host` удерживает его renderer result.
|
|
236
257
|
|
|
237
|
-
Inspection использует схемы `opetope.devtools-graph/
|
|
238
|
-
`opetope.runtime-activity/1` (D266). В пределах одной session
|
|
258
|
+
Inspection использует схемы `opetope.devtools-graph/5` и `opetope.devtools-frame/4`, а также optional snapshots
|
|
259
|
+
`opetope.runtime-activity/1` (D266, D275). В пределах одной session
|
|
239
260
|
frame без `activity` сохраняет предыдущую activity; полный snapshot/reset без этого поля очищает наблюдение (D216).
|
|
240
261
|
Frame с `activity` заменяет предыдущую activity целиком.
|
|
241
|
-
Используйте согласованные версии runtime/devtools:
|
|
262
|
+
Используйте согласованные версии runtime/devtools: предыдущие ревизии графа и кадров отвергаются; presence слабого ребра требует graph `/5` и frame `/4`. Activity указывает execution,
|
|
242
263
|
фактическое поколение feature, физические Calls, точных текущих lane blockers, зарегистрированные leases и попытки загрузок.
|
|
243
264
|
Спрос хоста и UI-модели неизвестны; у stream наблюдается state, но не физические identities загрузок. `freshness`
|
|
244
265
|
и `truncated` отличают полное live-наблюдение от усечённого или отключённого. Закрытая session имеет stale-снимок;
|
|
@@ -270,11 +291,10 @@ import { defineFeature, openFeature } from '@opetope/runtime';
|
|
|
270
291
|
import { runCall } from '@opetope/react/testing';
|
|
271
292
|
|
|
272
293
|
const Arithmetic = defineModel<{ readonly double: Call<number, number> }>('example.testing.model');
|
|
273
|
-
const arithmetic = defineFeature({
|
|
274
|
-
id: 'example.testing.arithmetic',
|
|
294
|
+
const arithmetic = defineFeature('example.testing.arithmetic', {
|
|
275
295
|
own: ({ model }) => ({
|
|
276
296
|
arithmetic: model(Arithmetic, context => ({
|
|
277
|
-
double: context.call(
|
|
297
|
+
double: context.call((input: number) => input * 2),
|
|
278
298
|
})),
|
|
279
299
|
}),
|
|
280
300
|
exports: ({ own }) => ({ double: own.arithmetic.double }),
|
|
@@ -320,7 +340,7 @@ UI-статус, обёртку outcome, политику выполнения,
|
|
|
320
340
|
- один закоммиченный вклад владеет своим кадром моделей до размонтирования или retire;
|
|
321
341
|
- брошенный concurrent-рендер кадр не удерживает;
|
|
322
342
|
- `useCommand` не подписывается на invoker: наблюдаемое состояние вызова лежит в `Readable` модели;
|
|
323
|
-
- исход `cancelled` не двигает ни `
|
|
343
|
+
- исход `cancelled` не двигает ни `lastResult`, ни `lastError`, и это только коды `CallError` `cancelled`
|
|
324
344
|
и `closed`; вызов слабого порта без провайдера (`unavailable`) и отклонённая публикация
|
|
325
345
|
(`publication-rejected`) приходят исходом `failed` и оседают в `lastError` (D138);
|
|
326
346
|
- вклад, упавший в рендере или коммите, изолируется своим монтированием и сообщается своей фиче;
|
|
@@ -15,11 +15,12 @@ type CommandInvoker = Readonly<{
|
|
|
15
15
|
type CommandNotification<Output> = (inFlight: boolean, outcome?: CommandOutcome<Output>) => void;
|
|
16
16
|
/**
|
|
17
17
|
* D203: the consumer keeps a status, not a schedule. Every `run` reaches the call, and the policy the call was
|
|
18
|
-
* created with — `queue`, `latest`, `parallel`, `once`, `
|
|
18
|
+
* created with — `queue`, `latest`, `parallel`, `once`, `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> {
|
|
22
22
|
private readonly invoker;
|
|
23
|
+
readonly handler: () => void;
|
|
23
24
|
readonly run: CommandRun<Input, Output>;
|
|
24
25
|
private inFlight;
|
|
25
26
|
private notify;
|
package/dist/command-hook.d.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import type { Call } from '@opetope/core';
|
|
2
|
+
import type { IsAny } from '@opetope/core/internal';
|
|
2
3
|
import type { CommandRun } from './command-hook-controller.js';
|
|
3
4
|
interface CommandHookStatus<Output> {
|
|
4
5
|
readonly inFlight: boolean;
|
|
5
6
|
readonly lastError: unknown | null;
|
|
6
|
-
readonly
|
|
7
|
+
readonly lastResult: Output | undefined;
|
|
7
8
|
}
|
|
9
|
+
type CommandHandler<Input> = IsAny<Input> extends true ? object : [Input] extends [never] ? object : [Input] extends [void] ? {
|
|
10
|
+
readonly handler: () => void;
|
|
11
|
+
} : object;
|
|
8
12
|
type CommandHook<Input, Output> = CommandHookStatus<Output> & {
|
|
9
13
|
readonly run: CommandRun<Input, Output>;
|
|
10
|
-
}
|
|
14
|
+
} & CommandHandler<Input>;
|
|
11
15
|
declare function useCommand<Input, Output>(command: Call<Input, Output>): CommandHook<Input, Output>;
|
|
12
16
|
export { useCommand };
|
|
13
17
|
export type { CommandHook };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{jsx as m}from"react/jsx-runtime";import{useContext as B,createContext as L,useCallback as y,useRef as R,useMemo as p,useSyncExternalStore as S,useEffect as T,useInsertionEffect as O,useLayoutEffect as C,memo as D,useState as V}from"react";import{isCallTarget as P,isModel as W,isReadable as A,createLifetimeController as K,createCallController as G,reportDetachedError as w,getContributionOwner as H,createState as J,createAggregateError as N}from"@opetope/core/internal";import{reportRuntimeFailure as Q,requireContributionModelPlans as U,openContributionModels as X}from"@opetope/runtime/internal";import{C as u,u as Y,b as Z}from"./contribution-isolation-Bzfbt4iM.js";const j=L(void 0);function _(){const t=B(j);if(t===void 0)throw new u("missing","Opetope hook was called outside a contribution mount.");return t}const ee=t=>t;function F(t,e,n){const r=e,i=(n==null?void 0:n.equals)??Object.is,s=y(d=>t.subscribe(d),[t]),c=R(void 0);let o=c.current;o===void 0&&(o={hasValue:!1,value:void 0},c.current=o);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 M=g;return o.hasValue&&i(o.value,g)&&(M=o.value),d=!0,h=b,f=M,f}return i(f,g)?(h=b,f):(h=b,f=g,f)}},[o,i,t,r]),l=S(s,a,a);return T(()=>{o.hasValue=!0,o.value=l},[o,l]),l}function k(t){return F(t,ee)}const v=new WeakMap;function x(t){if(arguments.length>1)throw new u("binding-invalid","Command follows the target it binds; binding options are forbidden.");if(!P(t))throw new u("binding-invalid","Command target is not authentic.");return v.set(t,{target:t}),t}function te(t){const e=v.get(t);if(e===void 0)throw new u("binding-invalid","Command binding is not authentic.");return e}function z(t){return typeof t=="object"&&t!==null&&v.has(t)}const ne=Symbol("opetope.model-binding"),ie=Symbol("opetope.model-binding-identity"),re=Object.freeze({}),E=new WeakMap;function oe(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 se(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 s=oe(t,e,i);if(!(A(s)||typeof s=="function")){if(!z(s))throw new u("binding-invalid",`Model ${t.id}.${String(i)} requires a Readable or a bound command.`);n.push(s)}}return Object.freeze(n)}function $(t,e){if(!W(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=se(t,e),r=Object.freeze({[ne]:re,[ie]:!0});return E.set(r,{commands:n,declaration:t,model:e}),r}function ae(t){const e=E.get(t);if(e===void 0)throw new u("binding-invalid","Model binding is not authentic.");return e}let ce=0;function de(t){const e=new Map,n=[],r=new Set;for(const i of t){const s=ae(i);if(e.has(s.declaration))throw new u("duplicate",`Model ${s.declaration.id} is bound twice in one mount.`);e.set(s.declaration,s.model);for(const c of s.commands)r.has(c)||(r.add(c),n.push(c))}return{commands:Object.freeze(n),frame:{commandRecords:new Map,models:e}}}class ue{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=K({id:`contribution.${String(r)}`}),this.isActive=n==null?void 0:n.isActive,this.onClose=n==null?void 0:n.onClose;const{commands:i,frame:s}=de(e);this.frame=s,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){te(r);const i=G(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 le({children:t,mount:e}){const n=S(e.subscribeRenderable,e.getRenderableSnapshot,e.getRenderableSnapshot);return O(()=>{if(e.attach())return()=>e.detach()},[e]),C(()=>{n||e.releaseClosedFrame()},[e,n]),n?m(j,{value:e.frame,children:t}):null}function I(t){const e=t.bundle;t.bundle=void 0,e==null||e.frame.close()}function fe(t,e){if(typeof e!="object"||e===null)return $(t,e);const n=Object.entries(e).map(([r,i])=>[r,A(i)||typeof i=="function"||z(i)?i:x(i)]);return $(t,Object.fromEntries(n))}function he(t){return H(t)}const me={models:[]};function q(t,e){try{t()}catch(n){throw N([e,n],"Contribution mount setup and cleanup failed.",{cause:e})}throw e}function be(t,e,n,r){const i=J(e),{models:s,reporter:c}=r??me;try{const o=U(t.models),a=o.length===0?void 0:X(o,i,c);return{bundle:a,models:[...s,...(a==null?void 0:a.models)??[],...n],props:i}}catch(o){return q(i.close,o)}}function pe(t,e,n,r){const{bundle:i,models:s,props:c}=be(t,e,n,r);let o=!1;const a=()=>{if(!o){o=!0;try{c.close()}finally{i==null||i.close().catch(w)}}};try{return{frame:new ue(s.map(([d,h])=>fe(d,h)),{isActive:()=>!o,onClose:a}),isOpen:()=>!o,props:c}}catch(l){return q(a,l)}}function ge(t,e){return m(t,{...e})}function Ce({bundle:t,contribution:e}){const n=k(t.props),r=p(()=>e.props===void 0?n:e.props(n),[n,e]);return m(le,{mount:t.frame,children:ge(e.Component,r)})}function we({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]),[s,c]=V(void 0),o=(s==null?void 0:s.owner)===i?s.bundle:void 0,a=R(r);return C(()=>{a.current=r}),O(()=>()=>{i.retired=!0,queueMicrotask(()=>{try{I(i)}catch(l){w(l)}})},[i]),C(()=>{const l=i.bundle??(i.bundle=pe(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&&I(i)}},[i]),C(()=>{(o==null?void 0:o.isOpen())===!0&&o.props.set(r)},[o,r]),o===void 0?null:m(Ce,{bundle:o,contribution:e})}function ve(t){const{authority:e,contribution:n,contributionId:r,fixtures:i,targetId:s}=t,c=Y(),o=p(()=>({authority:e,contribution:n,fixtures:i}),[e,n,i]),a=e==null?void 0:e.feature,l=p(()=>({contribution:r,feature:a,target:s}),[r,a,s]),d=e==null?void 0:e.reporter,h=y(f=>Q(f,d),[d]);return m(Z,{content:c,identity:o,report:h,source:l,children:m(we,{...t})})}const Me=D(ve);export{Me as M,k as a,F as b,he as c,x as d,_ as u};
|
|
2
|
+
//# sourceMappingURL=contribution-frame-DXl7K0-B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contribution-frame-DXl7K0-B.js","sources":["../src/mount-context.tsx","../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","import { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\n\nimport type { Readable } from '@opetope/core';\n\nconst identity = <Value>(value: Value): Value => value;\n\ninterface SelectionInstance<Selected> {\n hasValue: boolean;\n value: Selected | undefined;\n}\n\n/** D279: the source and the selection are positional; `equals` is the one modifier, spelled as in Core. */\nexport function useSelector<Value, Selected>(\n readable: Readable<Value>,\n select: (value: NoInfer<Value>) => Selected,\n options?: { readonly equals?: (left: NoInfer<Selected>, right: NoInfer<Selected>) => boolean },\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 { 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';\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 if (isReadable(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 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 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/** Model fields arrive as authentic kernel calls; binding them registers the command records the frame needs. */\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 fields = Object.entries(model).map(([key, value]): readonly [string, unknown] => [\n key,\n isReadable(value) || typeof value === 'function' || isCommandBinding(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 props.close();\n } finally {\n void bundle?.close().catch(reportDetachedError);\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","identity","value","useSelector","readable","select","options","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","error","onClose","ticket","calls","registration","subscribed","commandIndex","controller","createCallController","lease","reportDetachedError","MountProvider","children","mount","renderable","useInsertionEffect","useLayoutEffect","_jsx","closeOwnedMount","owner","bundle","bindFrameModel","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.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{useState as S,useInsertionEffect as h,useLayoutEffect as f,useMemo as g,useSyncExternalStore as b,useRef as m,useCallback as w,useEffect as
|
|
1
|
+
import{useState as S,useInsertionEffect as h,useLayoutEffect as f,useMemo as g,useSyncExternalStore as b,useRef as m,useCallback as w,useEffect as K,createElement as E,Fragment as x}from"react";import{CallError as $,declarationId as H}from"@opetope/core";import{compatibleAbortReason as T,isCancellation as G,reportDetachedError as v,isCallTarget as U,isReadable as D,createAggregateError as L,createContributionTarget as W}from"@opetope/core/internal";import{C as d}from"./contribution-isolation-Bzfbt4iM.js";import{u as p,a as R,M as B,c as J}from"./contribution-frame-DXl7K0-B.js";import{b as Fe}from"./contribution-frame-DXl7K0-B.js";function O(n,e){var t;if(G(e))return{reason:e,status:"cancelled"};try{const s=(t=n==null?void 0:n.onFailure)==null?void 0:t.call(n,e);s!==void 0&&Promise.resolve(s).catch(v)}catch(s){v(s)}return{error:e,status:"failed"}}function N(n){const e=n==null?void 0:n.onSuccess,t=n==null?void 0:n.signal;return e===void 0?t===void 0?void 0:{signal:t}:t===void 0?{onSuccess:e}:{onSuccess:e,signal:t}}function Q(n,e){let t;const s=new Promise(r=>{t=r});return{input:n,options:e===void 0?void 0:{...e},promise:s,resolve:t}}function V(n,e){const t=n.resolve;n.input=void 0,n.options=void 0,n.resolve=void 0,t==null||t(e)}class C{invoker;handler=()=>{this.run(void 0)};run=((e,t)=>{if(this.notify===void 0)return Promise.resolve({reason:new d("inactive","Command consumer is not mounted."),status:"cancelled"});const s=this.cancelledInput(t);if(s!==void 0)return Promise.resolve(s);const r=Q(e,t);return this.start(r),r.promise});inFlight=0;notify;constructor(e){this.invoker=e}attach(e){return this.notify=e,()=>{this.notify===e&&(this.notify=void 0)}}synchronize(){var e;(e=this.notify)==null||e.call(this,this.inFlight>0)}aborted(e){return{reason:new $("cancelled","Command input was cancelled.",T(e)),status:"cancelled"}}cancelledInput(e){var t;return((t=e==null?void 0:e.signal)==null?void 0:t.aborted)===!0?this.aborted(e.signal):void 0}finish(e,t){var s;e.resolve!==void 0&&(this.inFlight-=1,V(e,t),(s=this.notify)==null||s.call(this,this.inFlight>0,t))}invoke(e){let t;try{t=this.invoker.run(e.input,N(e.options)),e.input=void 0}catch(s){this.finish(e,O(e.options,s));return}t.then(s=>this.finish(e,{status:"ok",value:s}),s=>this.finish(e,O(e.options,s)))}start(e){var t;this.inFlight+=1,(t=this.notify)==null||t.call(this,!0),this.invoke(e)}}const X={inFlight:!1,lastError:null,lastResult:void 0};function Y(n,e,t,s){return s===void 0&&n.slot===e&&n.inFlight===t}function Z(n,e,t,s){if(Y(n,e,t,s))return n;const{lastError:r,lastResult:i}=n.slot===e?n:{lastError:null,lastResult:void 0};return(s==null?void 0:s.status)==="ok"?{inFlight:t,lastError:null,lastResult:s.value,slot:e}:(s==null?void 0:s.status)==="failed"?{inFlight:t,lastError:s.error,lastResult:i,slot:e}:{inFlight:t,lastError:r,lastResult:i,slot:e}}function _(n){const[e]=S(()=>new WeakMap);let t=e.get(n);return t===void 0&&(t=new C(n),e.set(n,t)),t}function q(n){const t=p().commandRecords.get(n);if(t===void 0)throw new d("missing",`Command ${n.id} is not bound in this contribution.`);const s=_(t.invoker),[r,i]=S(()=>({inFlight:!1,lastError:null,lastResult:void 0,slot:s}));h(()=>s.attach((A,I)=>i(P=>Z(P,s,A,I))),[s]),f(()=>s.synchronize(),[s]);const{inFlight:o,lastError:a,lastResult:l}=r.slot===s?r:X,c=s.run,u=s.handler;return g(()=>({handler:u,inFlight:o,lastError:a,lastResult:l,run:c}),[u,o,a,l,c])}const ee=()=>{},j=()=>ee,te=()=>0,ne=Object.freeze({});function se(n){const e=new C(n);return{detach:void 0,hook:{handler:e.handler,inFlight:!1,lastError:null,lastResult:void 0,run:e.run},invoker:n,slot:e}}function re(n,e){return n.inFlight===e.inFlight&&Object.is(n.lastResult,e.lastResult)&&Object.is(n.lastError,e.lastError)}function ie(n,e){return(e==null?void 0:e.status)==="ok"?{lastError:null,lastResult:e.value}:(e==null?void 0:e.status)==="failed"?{lastError:e.error,lastResult:n.lastResult}:n}function oe(n,e,t){const s=n.hook;if(t===void 0&&s.inFlight===e)return!1;const{lastError:r,lastResult:i}=ie(s,t),o={handler:n.slot.handler,inFlight:e,lastError:r,lastResult:i,run:s.run};return re(s,o)?!1:(n.hook=o,!0)}function le(n,e){return(n==null?void 0:n.invoker)===e?n:se(e)}class ae{entries=new Map;listeners=new Set;revision=0;close(){var e;for(const t of this.entries.values())(e=t.detach)==null||e.call(t),t.detach=void 0;this.entries.clear()}commit(e){var t;for(const[s,r]of this.entries)e.get(s)!==r&&((t=r.detach)==null||t.call(r),r.detach=void 0);this.entries=e;for(const s of e.values())s.detach??(s.detach=s.slot.attach((r,i)=>{if(oe(s,r,i)){this.revision+=1;for(const o of this.listeners)o()}}))}getSnapshot=()=>this.revision;prepare(e,t){const s=new Map;for(const r of Reflect.ownKeys(e)){if(!Object.prototype.propertyIsEnumerable.call(e,r))continue;const i=e[r],o=i===void 0?void 0:t.commandRecords.get(i);if(o===void 0)throw new d("missing",`Command at ${String(r)} is not bound in this contribution.`);s.set(r,le(this.entries.get(r),o.invoker))}return s}subscribe=e=>(this.listeners.add(e),()=>{this.listeners.delete(e)})}function ce(n){const e=m(void 0);return Reflect.ownKeys(n).some(s=>Object.prototype.propertyIsEnumerable.call(n,s))?e.current??(e.current=new ae):void 0}function M(n){const e=p(),t=ce(n),s=(t==null?void 0:t.getSnapshot)??te;b((t==null?void 0:t.subscribe)??j,s,s);const r=t==null?void 0:t.prepare(n,e);return h(()=>()=>t==null?void 0:t.close(),[t]),h(()=>{r!==void 0&&(t==null||t.commit(r))}),f(()=>{if(r!==void 0)for(const i of r.values())i.slot.synchronize()}),r===void 0?ne:Object.fromEntries([...r].map(([i,o])=>[i,o.hook]))}function ue(n,e){if(!D(n))throw new d("binding-invalid","Model selection read requires a Readable.");if(e.has(n))return e.get(n);const t=n.getSnapshot();return e.set(n,t),t}function de(n,e){const s=n({read:(r,i)=>{const o=ue(r,e);return i===void 0?o:i(o)}});if(typeof s!="object"||s===null||Array.isArray(s))throw new d("binding-invalid","Model selection requires a record.");return Object.fromEntries(Reflect.ownKeys(s).filter(r=>Object.prototype.propertyIsEnumerable.call(s,r)).map(r=>[r,s[r]]))}function fe(n,e,t){const s=Reflect.ownKeys(e),r=Reflect.ownKeys(n.values);if(s.length!==r.length||t.size!==n.sources.size)return!1;for(const i of t)if(!n.sources.has(i))return!1;return s.every((i,o)=>r[o]===i&&Object.is(n.values[i],e[i]))}const y={commands:{},sources:new Set,values:{}};function he(n){return Object.fromEntries(Reflect.ownKeys(n).filter(e=>U(n[e])).map(e=>[e,n[e]]))}function be(n,e){let t,s,r=y;return()=>{const i=e.current;if(i===void 0)return y;if(t!==void 0&&s===i&&[...t].every(([c,u])=>Object.is(c.getSnapshot(),u)))return r;const o=new Map,a=de(c=>i(n,c),o),l=new Set(o.keys());return(t===void 0||!fe(r,a,l))&&(r={commands:he(a),sources:l,values:a}),t=o,s=i,r}}function k(n,e=[]){for(const t of n){const s=t.dispose;t.dispose=void 0;try{s==null||s()}catch(r){e.push(r)}}if(e.length===1)throw e[0];if(e.length>1)throw L(e,"Model selection sources failed to release.")}class me{enabled=!1;listeners=new Set;sources=new Set;subscriptions=new Map;close(){this.enabled=!1,this.sources=new Set,this.releaseAll()}commit(e){this.sources=e,this.enabled=!0;try{this.synchronize()}catch(t){this.releaseAll([t])}}subscribe=e=>{const t={notify:e};this.listeners.add(t);try{this.synchronize()}catch(s){this.listeners.delete(t),this.releaseAll([s])}return()=>{this.listeners.delete(t),this.listeners.size===0&&this.releaseAll()}};add(e){const t={dispose:void 0};this.subscriptions.set(e,t);const s=e.subscribe(this.notify);if(typeof s!="function")throw new TypeError("Model selection source must return a disposer.");this.subscriptions.get(e)===t?t.dispose=s:s()}notify=()=>{for(const e of[...this.listeners])this.listeners.has(e)&&e.notify()};releaseAll(e){const t=[...this.subscriptions.values()];this.subscriptions.clear(),k(t,e)}removeUnselected(){const e=[];for(const[t,s]of this.subscriptions)this.sources.has(t)||(this.subscriptions.delete(t),e.push(s));k(e)}synchronize(){this.removeUnselected();for(const e of this.sources){if(!this.enabled||this.listeners.size===0)return;this.sources.has(e)&&!this.subscriptions.has(e)&&this.add(e)}}}const pe=()=>y;function ye(n){const e=m(void 0);return n?e.current??(e.current=new me):void 0}function Se(n,e){const t=p();if(!t.models.has(n))throw new d("missing",`Model ${n.id} is not granted to this contribution.`);const s=t.models.get(n),r=ye(e!==void 0),i=m(void 0);i.current=e;const o=e!==void 0,a=g(()=>o?be(s,i):pe,[s,o]),l=b((r==null?void 0:r.subscribe)??j,a,a),c=M(l.commands);return f(()=>()=>r==null?void 0:r.close(),[r]),f(()=>r==null?void 0:r.commit(l.sources),[l,r]),e===void 0?s:Object.fromEntries(Reflect.ownKeys(l.values).map(u=>[u,Object.prototype.hasOwnProperty.call(c,u)?c[u]:l.values[u]]))}function ge(n){const e=Object.freeze([...n]);return t=>{if(t.requires!==void 0)throw new d("duplicate","Component already declares the models it requires.");return Object.defineProperty(t,"requires",{enumerable:!1,value:e}),t}}function we(n){const e=w(r=>n.subscribe(r),[n]),t=w(()=>n.getSnapshot(),[n]),s=b(e,t,t);return K(()=>n.retain(),[n]),s}const z=Object.freeze({});function F(n){return W(n,"slot")}function Ee(n){const e=H(n),t=new Map,s=r=>{const i=t.get(r);if(i!==void 0)return i;const o=F(`${e}/${r}`);return t.set(r,o),o};return Object.defineProperty(s,"id",{enumerable:!0,value:e}),Object.freeze(s)}function ve(n){const e=R(n.target.entries),t="props"in n?n.props??z:z;return e.length===0?null:e.map(s=>E(x,{key:s.id},E(B,{authority:J(s),contribution:s.value,contributionId:s.id,slotProps:t,targetId:n.target.id})))}export{d as ContributionError,ve as Slot,F as defineSlot,Ee as defineSwitchSlot,ge as requiresModels,q as useCommand,M as useCommands,Se as useModel,R as useReadable,we as useResource,Fe as useSelector};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|