@opetope/react 0.7.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 CHANGED
@@ -1,5 +1,26 @@
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
+
3
24
  ## 0.7.0
4
25
 
5
26
  ### Minor Changes
package/README.md CHANGED
@@ -26,7 +26,7 @@ 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 }>({ id: 'example.counter.slot' });
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 {
@@ -50,7 +50,7 @@ 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, result, lastError }`
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
56
  For `Call<void, Output>` and `Call<undefined, Output>`, the hook also exposes `handler: () => void` (D278).
@@ -59,7 +59,7 @@ Commands with data, including `T | undefined`, `unknown`, `any` and `never`, do
59
59
  Use `run(input, options?)` for data, an outcome, callbacks or a per-run signal. The handler reports failures through
60
60
  `lastError` and preserves the same cancellation behavior; it returns no promise or outcome.
61
61
 
62
- `run` and `handler` are stable while the invoker stays the same; the returned object changes as `inFlight`, `result`
62
+ `run` and `handler` are stable while the invoker stays the same; the returned object changes as `inFlight`, `lastResult`
63
63
  or `lastError` changes. Destructure the function for effect dependencies. A handler captured from a replaced or
64
64
  unmounted consumer is fenced; it does not start work on the replacement.
65
65
 
@@ -176,10 +176,8 @@ root, no `ui` section and no second binding API any more (D85).
176
176
  ```tsx
177
177
  import { defineSlot, defineSwitchSlot, Slot } from '@opetope/react';
178
178
 
179
- const HeaderEnd = defineSlot<{ readonly mode: 'desktop' | 'phone' }>({ id: 'example.headerEnd' });
180
- const PageEnd = defineSwitchSlot<'home' | 'wallet', { readonly compact: boolean }>({
181
- id: 'example.pageEnd',
182
- });
179
+ const HeaderEnd = defineSlot<{ readonly mode: 'desktop' | 'phone' }>('example.headerEnd');
180
+ const PageEnd = defineSwitchSlot<'home' | 'wallet', { readonly compact: boolean }>('example.pageEnd');
183
181
 
184
182
  function Header({ mode }: { readonly mode: 'desktop' | 'phone' }) {
185
183
  return <Slot props={{ mode }} target={HeaderEnd} />;
@@ -293,11 +291,10 @@ import { defineFeature, openFeature } from '@opetope/runtime';
293
291
  import { runCall } from '@opetope/react/testing';
294
292
 
295
293
  const Arithmetic = defineModel<{ readonly double: Call<number, number> }>('example.testing.model');
296
- const arithmetic = defineFeature({
297
- id: 'example.testing.arithmetic',
294
+ const arithmetic = defineFeature('example.testing.arithmetic', {
298
295
  own: ({ model }) => ({
299
296
  arithmetic: model(Arithmetic, context => ({
300
- double: context.call({ run: (input: number) => input * 2 }),
297
+ double: context.call((input: number) => input * 2),
301
298
  })),
302
299
  }),
303
300
  exports: ({ own }) => ({ double: own.arithmetic.double }),
@@ -343,7 +340,7 @@ The constructors of the integration entry are not re-exported from the safe entr
343
340
  - one committed contribution owns its model frame until unmount or retire;
344
341
  - an abandoned concurrent render holds no frame;
345
342
  - `useCommand` does not subscribe to the invoker: the observable state of a call lies in a `Readable` of the model;
346
- - the `cancelled` outcome moves neither `result` nor `lastError`, and it is only the `CallError` codes `cancelled`
343
+ - the `cancelled` outcome moves neither `lastResult` nor `lastError`, and it is only the `CallError` codes `cancelled`
347
344
  and `closed`; a call to a weak port with no provider (`unavailable`) and a rejected publication
348
345
  (`publication-rejected`) arrive as the `failed` outcome and settle in `lastError` (D138);
349
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,7 +26,7 @@ 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 }>({ id: 'example.counter.slot' });
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 {
@@ -51,7 +51,7 @@ function CounterArea() {
51
51
 
52
52
  Компонент не получает ни сервис, ни класс, ни ref рантайма. Монтирование вклада неявно выдаёт модели `own` его фичи;
53
53
  компонент или хук, читающий per-mount модель, объявляет её через `requiresModels`. `useModel` читает ровно этот кадр
54
- авторитета. `useCommand` отдаёт `{ run, inFlight, result, lastError }`
54
+ авторитета. `useCommand` отдаёт `{ run, inFlight, lastResult, lastError }`
55
55
  и всегда resolve-ит исход, поэтому штатная отмена не становится unhandled rejection (D116).
56
56
 
57
57
  Для `Call<void, Output>` и `Call<undefined, Output>` хук также предоставляет `handler: () => void` (D278).
@@ -60,7 +60,7 @@ function CounterArea() {
60
60
  Для данных, исхода, callbacks или сигнала отдельного вызова используйте `run(input, options?)`. Handler показывает
61
61
  ошибки через `lastError` и сохраняет обработку отмены; promise или исход он не возвращает.
62
62
 
63
- `run` и `handler` стабильны, пока invoker тот же; возвращённый объект меняется вместе с `inFlight`, `result` или
63
+ `run` и `handler` стабильны, пока invoker тот же; возвращённый объект меняется вместе с `inFlight`, `lastResult` или
64
64
  `lastError`. Для зависимостей эффекта берите функцию деструктуризацией. Сохранённый handler заменённого или
65
65
  размонтированного потребителя закрыт для новых вызовов и не перенаправляет работу на замену.
66
66
 
@@ -175,10 +175,8 @@ render-колбэком: колбэк `children` получает готовый
175
175
  ```tsx
176
176
  import { defineSlot, defineSwitchSlot, Slot } from '@opetope/react';
177
177
 
178
- const HeaderEnd = defineSlot<{ readonly mode: 'desktop' | 'phone' }>({ id: 'example.headerEnd' });
179
- const PageEnd = defineSwitchSlot<'home' | 'wallet', { readonly compact: boolean }>({
180
- id: 'example.pageEnd',
181
- });
178
+ const HeaderEnd = defineSlot<{ readonly mode: 'desktop' | 'phone' }>('example.headerEnd');
179
+ const PageEnd = defineSwitchSlot<'home' | 'wallet', { readonly compact: boolean }>('example.pageEnd');
182
180
 
183
181
  function Header({ mode }: { readonly mode: 'desktop' | 'phone' }) {
184
182
  return <Slot props={{ mode }} target={HeaderEnd} />;
@@ -293,11 +291,10 @@ import { defineFeature, openFeature } from '@opetope/runtime';
293
291
  import { runCall } from '@opetope/react/testing';
294
292
 
295
293
  const Arithmetic = defineModel<{ readonly double: Call<number, number> }>('example.testing.model');
296
- const arithmetic = defineFeature({
297
- id: 'example.testing.arithmetic',
294
+ const arithmetic = defineFeature('example.testing.arithmetic', {
298
295
  own: ({ model }) => ({
299
296
  arithmetic: model(Arithmetic, context => ({
300
- double: context.call({ run: (input: number) => input * 2 }),
297
+ double: context.call((input: number) => input * 2),
301
298
  })),
302
299
  }),
303
300
  exports: ({ own }) => ({ double: own.arithmetic.double }),
@@ -343,7 +340,7 @@ UI-статус, обёртку outcome, политику выполнения,
343
340
  - один закоммиченный вклад владеет своим кадром моделей до размонтирования или retire;
344
341
  - брошенный concurrent-рендер кадр не удерживает;
345
342
  - `useCommand` не подписывается на invoker: наблюдаемое состояние вызова лежит в `Readable` модели;
346
- - исход `cancelled` не двигает ни `result`, ни `lastError`, и это только коды `CallError` `cancelled`
343
+ - исход `cancelled` не двигает ни `lastResult`, ни `lastError`, и это только коды `CallError` `cancelled`
347
344
  и `closed`; вызов слабого порта без провайдера (`unavailable`) и отклонённая публикация
348
345
  (`publication-rejected`) приходят исходом `failed` и оседают в `lastError` (D138);
349
346
  - вклад, упавший в рендере или коммите, изолируется своим монтированием и сообщается своей фиче;
@@ -4,7 +4,7 @@ import type { CommandRun } from './command-hook-controller.js';
4
4
  interface CommandHookStatus<Output> {
5
5
  readonly inFlight: boolean;
6
6
  readonly lastError: unknown | null;
7
- readonly result: Output | undefined;
7
+ readonly lastResult: Output | undefined;
8
8
  }
9
9
  type CommandHandler<Input> = IsAny<Input> extends true ? object : [Input] extends [never] ? object : [Input] extends [void] ? {
10
10
  readonly handler: () => void;
@@ -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 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 O,M as B,c as J}from"./contribution-frame-B1PnpOaP.js";import{b as Fe}from"./contribution-frame-B1PnpOaP.js";function C(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 j{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,C(e.options,s));return}t.then(s=>this.finish(e,{status:"ok",value:s}),s=>this.finish(e,C(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,result: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,result:i}=n.slot===e?n:{lastError:null,result:void 0};return(s==null?void 0:s.status)==="ok"?{inFlight:t,lastError:null,result:s.value,slot:e}:(s==null?void 0:s.status)==="failed"?{inFlight:t,lastError:s.error,result:i,slot:e}:{inFlight:t,lastError:r,result:i,slot:e}}function _(n){const[e]=S(()=>new WeakMap);let t=e.get(n);return t===void 0&&(t=new j(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,result: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:c,result:l}=r.slot===s?r:X,a=s.run,u=s.handler;return g(()=>({handler:u,inFlight:o,lastError:c,result:l,run:a}),[u,o,c,l,a])}const ee=()=>{},M=()=>ee,te=()=>0,ne=Object.freeze({});function se(n){const e=new j(n);return{detach:void 0,hook:{handler:e.handler,inFlight:!1,lastError:null,result:void 0,run:e.run},invoker:n,slot:e}}function re(n,e){return n.inFlight===e.inFlight&&Object.is(n.result,e.result)&&Object.is(n.lastError,e.lastError)}function ie(n,e){return(e==null?void 0:e.status)==="ok"?{lastError:null,result:e.value}:(e==null?void 0:e.status)==="failed"?{lastError:e.error,result:n.result}:n}function oe(n,e,t){const s=n.hook;if(t===void 0&&s.inFlight===e)return!1;const{lastError:r,result:i}=ie(s,t),o={handler:n.slot.handler,inFlight:e,lastError:r,result: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 ce{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 ae(n){const e=m(void 0);return Reflect.ownKeys(n).some(s=>Object.prototype.propertyIsEnumerable.call(n,s))?e.current??(e.current=new ce):void 0}function k(n){const e=p(),t=ae(n),s=(t==null?void 0:t.getSnapshot)??te;b((t==null?void 0:t.subscribe)??M,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(([a,u])=>Object.is(a.getSnapshot(),u)))return r;const o=new Map,c=de(a=>i(n,a),o),l=new Set(o.keys());return(t===void 0||!fe(r,c,l))&&(r={commands:he(c),sources:l,values:c}),t=o,s=i,r}}function R(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(),R(t,e)}removeUnselected(){const e=[];for(const[t,s]of this.subscriptions)this.sources.has(t)||(this.subscriptions.delete(t),e.push(s));R(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,c=g(()=>o?be(s,i):pe,[s,o]),l=b((r==null?void 0:r.subscribe)??M,c,c),a=k(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(a,u)?a[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.id),t=new Map,s=r=>{const i=t.get(r);if(i!==void 0)return i;const o=F({id:`${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=O(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,k as useCommands,Se as useModel,O as useReadable,we as useResource,Fe as useSelector};
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
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/command-hook-controller.ts","../src/command-hook.ts","../src/idle-subscription.ts","../src/commands-hook.ts","../src/model-selection-snapshot.ts","../src/model-selection-store.ts","../src/model-hook.ts","../src/requires-models.ts","../src/resource-hook.ts","../src/slot.tsx"],"sourcesContent":["import { CallError } from '@opetope/core';\nimport { compatibleAbortReason, isCancellation, reportDetachedError } from '@opetope/core/internal';\nimport type { CallInputArgs, CallRunOptions } from '@opetope/core/internal';\n\nimport type { CommandOutcome } from './command';\nimport { ContributionError } from './errors';\n\ntype CommandRunOptions<Output> = Readonly<{\n onFailure?: (error: unknown) => PromiseLike<void> | void;\n onSuccess?: (value: Output) => PromiseLike<void> | void;\n signal?: AbortSignal;\n}>;\n\ntype CommandRun<Input, Output> = {\n (input: Input, options?: CommandRunOptions<Output>): Promise<CommandOutcome<Output>>;\n (...args: [...CallInputArgs<Input>, options?: CommandRunOptions<Output>]): Promise<CommandOutcome<Output>>;\n};\n\ntype CommandInvoker = Readonly<{\n run: (input: never, options?: CallRunOptions<unknown>) => Promise<unknown>;\n}>;\n\ntype CommandNotification<Output> = (inFlight: boolean, outcome?: CommandOutcome<Output>) => void;\n\ntype CommandRequest<Input, Output> = {\n input: Input | undefined;\n options: CommandRunOptions<Output> | undefined;\n readonly promise: Promise<CommandOutcome<Output>>;\n resolve: ((outcome: CommandOutcome<Output>) => void) | undefined;\n};\n\nfunction failureOutcome<Output>(options: CommandRunOptions<Output> | undefined, error: unknown): CommandOutcome<never> {\n if (isCancellation(error)) return { reason: error, status: 'cancelled' };\n\n try {\n const notification = options?.onFailure?.(error);\n\n if (notification !== undefined) void Promise.resolve(notification).catch(reportDetachedError);\n } catch (callbackError) {\n reportDetachedError(callbackError);\n }\n\n return { error, status: 'failed' };\n}\n\nfunction runOptions<Output>(options: CommandRunOptions<Output> | undefined): CallRunOptions<Output> | undefined {\n const onSuccess = options?.onSuccess;\n const signal = options?.signal;\n\n if (onSuccess === undefined) return signal === undefined ? undefined : { signal };\n\n return signal === undefined ? { onSuccess } : { onSuccess, signal };\n}\n\nfunction requestOf<Input, Output>(\n input: Input,\n options: CommandRunOptions<Output> | undefined,\n): CommandRequest<Input, Output> {\n let resolve!: (outcome: CommandOutcome<Output>) => void;\n const promise = new Promise<CommandOutcome<Output>>(settle => {\n resolve = settle;\n });\n\n return { input, options: options === undefined ? undefined : { ...options }, promise, resolve };\n}\n\nfunction settle<Input, Output>(request: CommandRequest<Input, Output>, outcome: CommandOutcome<Output>): void {\n const resolve = request.resolve;\n request.input = undefined;\n request.options = undefined;\n request.resolve = undefined;\n resolve?.(outcome);\n}\n\n/**\n * D203: the consumer keeps a status, not a schedule. Every `run` reaches the call, and the policy the call was\n * created with — `queue`, `latest`, `parallel`, `once`, `dedupe` — decides what happens to it. What stays\n * local is what only this consumer knows: whether it is mounted, its own input signal, and the outcome it shows.\n */\nclass CommandHookController<Input, Output> {\n // React may supply an event; inputless handlers intentionally never forward it as Call data.\n readonly handler = (): void => {\n void this.run(undefined as Input);\n };\n readonly run: CommandRun<Input, Output> = ((input: Input, options?: CommandRunOptions<Output>) => {\n if (this.notify === undefined) {\n return Promise.resolve({\n reason: new ContributionError('inactive', 'Command consumer is not mounted.'),\n status: 'cancelled' as const,\n });\n }\n\n const aborted = this.cancelledInput(options);\n\n if (aborted !== undefined) return Promise.resolve(aborted);\n\n const request = requestOf(input, options);\n this.start(request);\n\n return request.promise;\n }) as CommandRun<Input, Output>;\n private inFlight = 0;\n private notify: CommandNotification<Output> | undefined;\n\n constructor(private readonly invoker: CommandInvoker) {}\n\n attach(notify: CommandNotification<Output>): () => void {\n this.notify = notify;\n\n return () => {\n if (this.notify !== notify) return;\n\n this.notify = undefined;\n };\n }\n\n synchronize(): void {\n this.notify?.(this.inFlight > 0);\n }\n\n private aborted(signal: AbortSignal): CommandOutcome<never> {\n return {\n reason: new CallError('cancelled', 'Command input was cancelled.', compatibleAbortReason(signal)),\n status: 'cancelled',\n };\n }\n\n private cancelledInput(options: CommandRunOptions<Output> | undefined): CommandOutcome<never> | undefined {\n return options?.signal?.aborted === true ? this.aborted(options.signal) : undefined;\n }\n\n private finish(request: CommandRequest<Input, Output>, outcome: CommandOutcome<Output>): void {\n if (request.resolve === undefined) return;\n\n this.inFlight -= 1;\n settle(request, outcome);\n this.notify?.(this.inFlight > 0, outcome);\n }\n\n private invoke(request: CommandRequest<Input, Output>): void {\n let invocation: Promise<Output>;\n\n try {\n invocation = this.invoker.run(request.input as never, runOptions(request.options) as never) as Promise<Output>;\n request.input = undefined;\n } catch (error) {\n this.finish(request, failureOutcome(request.options, error));\n\n return;\n }\n\n void invocation.then(\n value => this.finish(request, { status: 'ok', value }),\n (error: unknown) => this.finish(request, failureOutcome(request.options, error)),\n );\n }\n\n private start(request: CommandRequest<Input, Output>): void {\n this.inFlight += 1;\n // Reserve the flight before publishing: a reentrant run must already see this consumer as busy.\n this.notify?.(true);\n this.invoke(request);\n }\n}\n\nexport { CommandHookController };\nexport type { CommandInvoker, CommandRun };\n","import { useInsertionEffect, useLayoutEffect, useMemo, useState } from 'react';\n\nimport type { Call } from '@opetope/core';\nimport type { IsAny } from '@opetope/core/internal';\n\nimport type { CommandOutcome } from './command';\nimport { CommandHookController } from './command-hook-controller';\nimport type { CommandInvoker, CommandRun } from './command-hook-controller';\nimport { ContributionError } from './errors';\nimport { useFrame } from './mount-context';\n\ninterface CommandHookStatus<Output> {\n readonly inFlight: boolean;\n readonly lastError: unknown | null;\n readonly result: Output | undefined;\n}\n\nconst emptyStatus: CommandHookStatus<never> = { inFlight: false, lastError: null, result: undefined };\n\ntype CommandHandler<Input> =\n IsAny<Input> extends true\n ? object\n : [Input] extends [never]\n ? object\n : [Input] extends [void]\n ? { readonly handler: () => void }\n : object;\n\ntype CommandHook<Input, Output> = CommandHookStatus<Output> & {\n readonly run: CommandRun<Input, Output>;\n} & CommandHandler<Input>;\n\ntype CommandHookState<Input, Output> = CommandHookStatus<Output> & {\n readonly slot: CommandHookController<Input, Output>;\n};\n\nfunction unchangedState<Input, Output>(\n current: CommandHookState<Input, Output>,\n slot: CommandHookController<Input, Output>,\n inFlight: boolean,\n outcome: CommandOutcome<Output> | undefined,\n): boolean {\n return outcome === undefined && current.slot === slot && current.inFlight === inFlight;\n}\n\nfunction nextState<Input, Output>(\n current: CommandHookState<Input, Output>,\n slot: CommandHookController<Input, Output>,\n inFlight: boolean,\n outcome?: CommandOutcome<Output>,\n): CommandHookState<Input, Output> {\n if (unchangedState(current, slot, inFlight, outcome)) return current;\n\n const { lastError, result } = current.slot === slot ? current : { lastError: null, result: undefined };\n\n if (outcome?.status === 'ok') return { inFlight, lastError: null, result: outcome.value, slot };\n\n if (outcome?.status === 'failed') return { inFlight, lastError: outcome.error, result, slot };\n\n return { inFlight, lastError, result, slot };\n}\n\nfunction useCommandSlot<Input, Output>(invoker: CommandInvoker): CommandHookController<Input, Output> {\n // Slots are inert until commit. An interrupted render cannot fence the committed source or admit its own work.\n const [slots] = useState(() => new WeakMap<object, CommandHookController<Input, Output>>());\n let slot = slots.get(invoker);\n\n if (slot === undefined) {\n slot = new CommandHookController(invoker);\n slots.set(invoker, slot);\n }\n\n return slot;\n}\n\nfunction useCommand<Input, Output>(command: Call<Input, Output>): CommandHook<Input, Output> {\n const frame = useFrame();\n const record = frame.commandRecords.get(command);\n\n if (record === undefined) {\n throw new ContributionError('missing', `Command ${command.id} is not bound in this contribution.`);\n }\n\n const currentSlot = useCommandSlot<Input, Output>(record.invoker);\n const [hookState, setHookState] = useState<CommandHookState<Input, Output>>(() => ({\n inFlight: false,\n lastError: null,\n result: undefined,\n slot: currentSlot,\n }));\n useInsertionEffect(\n () =>\n currentSlot.attach((inFlight, outcome) =>\n setHookState(current => nextState(current, currentSlot, inFlight, outcome)),\n ),\n [currentSlot],\n );\n useLayoutEffect(() => currentSlot.synchronize(), [currentSlot]);\n const { inFlight, lastError, result } = hookState.slot === currentSlot ? hookState : emptyStatus;\n const run = currentSlot.run;\n const handler = currentSlot.handler;\n\n return useMemo(() => ({ handler, inFlight, lastError, result, run }), [handler, inFlight, lastError, result, run]);\n}\n\nexport { useCommand };\nexport type { CommandHook };\n","const idleRelease = (): void => undefined;\nconst subscribeIdle = (): (() => void) => idleRelease;\nconst idleRevision = (): number => 0;\n\nexport { idleRevision, subscribeIdle };\n","import { useInsertionEffect, useLayoutEffect, useRef, useSyncExternalStore } from 'react';\n\nimport type { Call } from '@opetope/core';\n\nimport type { CommandOutcome } from './command';\nimport type { CommandHook } from './command-hook';\nimport { CommandHookController } from './command-hook-controller';\nimport type { CommandInvoker } from './command-hook-controller';\nimport { ContributionError } from './errors';\nimport { idleRevision, subscribeIdle } from './idle-subscription';\nimport { useFrame } from './mount-context';\nimport type { Frame } from './mount-frame';\n\ntype CommandSelection = Readonly<Record<string, Call<never, unknown>>>;\ntype HookOf<Command> = Command extends Call<infer Input, infer Output> ? CommandHook<Input, Output> : never;\ntype CommandsHook<Commands> = {\n readonly [Key in keyof Commands]: HookOf<Commands[Key]>;\n};\nconst emptyHooks = Object.freeze({});\n\ntype CommandEntry = {\n detach: (() => void) | undefined;\n hook: CommandHook<never, unknown> & { readonly handler: () => void };\n readonly invoker: CommandInvoker;\n readonly slot: CommandHookController<never, unknown>;\n};\n\nfunction createEntry(invoker: CommandInvoker): CommandEntry {\n const slot = new CommandHookController<never, unknown>(invoker);\n\n return {\n detach: undefined,\n hook: { handler: slot.handler, inFlight: false, lastError: null, result: undefined, run: slot.run },\n invoker,\n slot,\n };\n}\n\nfunction unchangedHook(previous: CommandHook<never, unknown>, next: CommandHook<never, unknown>): boolean {\n return (\n previous.inFlight === next.inFlight &&\n Object.is(previous.result, next.result) &&\n Object.is(previous.lastError, next.lastError)\n );\n}\n\nfunction outcomeStatus(previous: CommandHook<never, unknown>, outcome: CommandOutcome<unknown> | undefined) {\n if (outcome?.status === 'ok') return { lastError: null, result: outcome.value };\n\n if (outcome?.status === 'failed') return { lastError: outcome.error, result: previous.result };\n\n return previous;\n}\n\nfunction updateEntry(entry: CommandEntry, inFlight: boolean, outcome?: CommandOutcome<unknown>): boolean {\n const previous = entry.hook;\n\n if (outcome === undefined && previous.inFlight === inFlight) return false;\n\n const { lastError, result } = outcomeStatus(previous, outcome);\n const next = { handler: entry.slot.handler, inFlight, lastError, result, run: previous.run };\n\n if (unchangedHook(previous, next)) return false;\n\n entry.hook = next;\n\n return true;\n}\n\nfunction selectEntry(previous: CommandEntry | undefined, invoker: CommandInvoker): CommandEntry {\n return previous?.invoker === invoker ? previous : createEntry(invoker);\n}\n\n/** Selection is inert during render; only a committed selection can admit work or discard an old queue. */\nclass CommandGroup {\n private entries = new Map<PropertyKey, CommandEntry>();\n private readonly listeners = new Set<() => void>();\n private revision = 0;\n\n close(): void {\n for (const entry of this.entries.values()) {\n entry.detach?.();\n entry.detach = undefined;\n }\n\n this.entries.clear();\n }\n\n commit(selected: Map<PropertyKey, CommandEntry>): void {\n for (const [key, entry] of this.entries) {\n if (selected.get(key) === entry) continue;\n\n entry.detach?.();\n entry.detach = undefined;\n }\n\n this.entries = selected;\n\n for (const entry of selected.values()) {\n entry.detach ??= entry.slot.attach((inFlight, outcome) => {\n if (!updateEntry(entry, inFlight, outcome)) return;\n\n this.revision += 1;\n\n for (const listener of this.listeners) listener();\n });\n }\n }\n\n getSnapshot = (): number => this.revision;\n\n prepare(commands: CommandSelection, frame: Frame): Map<PropertyKey, CommandEntry> {\n const selected = new Map<PropertyKey, CommandEntry>();\n\n for (const key of Reflect.ownKeys(commands)) {\n if (!Object.prototype.propertyIsEnumerable.call(commands, key)) continue;\n\n const command = (commands as Readonly<Record<PropertyKey, Call<never, unknown>>>)[key];\n const record = command === undefined ? undefined : frame.commandRecords.get(command);\n\n if (record === undefined) {\n throw new ContributionError('missing', `Command at ${String(key)} is not bound in this contribution.`);\n }\n\n selected.set(key, selectEntry(this.entries.get(key), record.invoker));\n }\n\n return selected;\n }\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n\n return () => {\n this.listeners.delete(listener);\n };\n };\n}\n\nfunction useCommandGroup(commands: CommandSelection): CommandGroup | undefined {\n const groupRef = useRef<CommandGroup | undefined>(undefined);\n const populated = Reflect.ownKeys(commands).some(key => Object.prototype.propertyIsEnumerable.call(commands, key));\n\n return populated ? (groupRef.current ??= new CommandGroup()) : undefined;\n}\n\nfunction useCommands<Commands extends { readonly [Key in keyof Commands]: Call<never, unknown> }>(\n commands: Commands,\n): CommandsHook<Commands> {\n const frame = useFrame();\n const group = useCommandGroup(commands);\n const getSnapshot = group?.getSnapshot ?? idleRevision;\n useSyncExternalStore(group?.subscribe ?? subscribeIdle, getSnapshot, getSnapshot);\n const selected = group?.prepare(commands, frame);\n useInsertionEffect(() => () => group?.close(), [group]);\n useInsertionEffect(() => {\n if (selected !== undefined) group?.commit(selected);\n });\n useLayoutEffect(() => {\n if (selected === undefined) return;\n\n for (const entry of selected.values()) entry.slot.synchronize();\n });\n\n return (\n selected === undefined ? emptyHooks : Object.fromEntries([...selected].map(([key, entry]) => [key, entry.hook]))\n ) as CommandsHook<Commands>;\n}\n\nexport { useCommands };\n","import type { Readable } from '@opetope/core';\nimport { isCallTarget, isReadable } from '@opetope/core/internal';\n\nimport { ContributionError } from './errors';\nimport type { AnyCommand } from './model-binding';\n\ninterface SelectionRead {\n <Value>(source: Readable<Value>): Value;\n <Value, Selected>(source: Readable<Value>, select: (value: Value) => Selected): Selected;\n}\n\ntype SelectionContext = Readonly<{ read: SelectionRead }>;\ntype NonSelectionRecord =\n | readonly unknown[]\n | ((...arguments_: never[]) => unknown)\n | (abstract new (...arguments_: never[]) => unknown)\n | Date\n | PromiseLike<unknown>\n | ReadonlyMap<unknown, unknown>\n | ReadonlySet<unknown>\n | RegExp\n | WeakMap<object, unknown>\n | WeakSet<object>;\n/** A named interface needs no open index signature; container and callable results are not field selections. */\ntype SelectionShape<Selected extends object> = Selected extends NonSelectionRecord ? never : Selected;\ntype SelectionRecord = Readonly<Record<PropertyKey, unknown>>;\ntype SelectionSnapshot = Readonly<{\n commands: Readonly<Record<PropertyKey, AnyCommand>>;\n sources: ReadonlySet<Readable<unknown>>;\n values: SelectionRecord;\n}>;\n\nfunction readSource<Value>(source: Readable<Value>, sources: Map<Readable<unknown>, unknown>): Value {\n if (!isReadable(source)) {\n throw new ContributionError('binding-invalid', 'Model selection read requires a Readable.');\n }\n\n if (sources.has(source)) return sources.get(source) as Value;\n\n const value = source.getSnapshot();\n sources.set(source, value);\n\n return value;\n}\n\nfunction collectSelection(\n select: (context: SelectionContext) => object,\n sources: Map<Readable<unknown>, unknown>,\n): SelectionRecord {\n const read = <Value, Selected>(source: Readable<Value>, project?: (value: Value) => Selected) => {\n const value = readSource(source, sources);\n\n return project === undefined ? value : project(value);\n };\n const selected = select({ read });\n\n if (typeof selected !== 'object' || selected === null || Array.isArray(selected)) {\n throw new ContributionError('binding-invalid', 'Model selection requires a record.');\n }\n\n return Object.fromEntries(\n Reflect.ownKeys(selected)\n .filter(key => Object.prototype.propertyIsEnumerable.call(selected, key))\n .map(key => [key, (selected as SelectionRecord)[key]]),\n );\n}\n\nfunction equalSelection(previous: SelectionSnapshot, values: SelectionRecord, sources: ReadonlySet<Readable<unknown>>) {\n const keys = Reflect.ownKeys(values);\n const previousKeys = Reflect.ownKeys(previous.values);\n\n if (keys.length !== previousKeys.length || sources.size !== previous.sources.size) return false;\n\n for (const source of sources) if (!previous.sources.has(source)) return false;\n\n return keys.every((key, index) => previousKeys[index] === key && Object.is(previous.values[key], values[key]));\n}\n\ntype ModelSelector<Model> = (model: Model, context: SelectionContext) => object;\ntype SelectionReaderRef<Model> = { readonly current: ModelSelector<Model> | undefined };\n\nconst emptySelectionSnapshot: SelectionSnapshot = { commands: {}, sources: new Set(), values: {} };\n\nfunction selectionCommands(values: SelectionRecord): Readonly<Record<PropertyKey, AnyCommand>> {\n return Object.fromEntries(\n Reflect.ownKeys(values)\n .filter(key => isCallTarget(values[key]))\n .map(key => [key, values[key]]),\n ) as Readonly<Record<PropertyKey, AnyCommand>>;\n}\n\n/**\n * D214: one reader per model, not per render. The author's selector arrives through a ref, so an inline arrow does not\n * rebuild the reader; the fast path — nothing observed has moved — belongs to the selector that computed the snapshot,\n * so a new selector is always called again, its result compared field by field, and an equal selection keeps the\n * previous snapshot. A rejected or abandoned render still changes nothing: only a committed selection subscribes.\n */\nfunction createSelectionSnapshot<Model>(model: Model, selectRef: SelectionReaderRef<Model>): () => SelectionSnapshot {\n let observed: Map<Readable<unknown>, unknown> | undefined;\n let observedSelect: ModelSelector<Model> | undefined;\n let snapshot: SelectionSnapshot = emptySelectionSnapshot;\n\n return () => {\n const select = selectRef.current;\n\n if (select === undefined) return emptySelectionSnapshot;\n\n if (\n observed !== undefined &&\n observedSelect === select &&\n [...observed].every(([source, value]) => Object.is(source.getSnapshot(), value))\n ) {\n return snapshot;\n }\n\n const nextObserved = new Map<Readable<unknown>, unknown>();\n const values = collectSelection(context => select(model, context), nextObserved);\n const sources = new Set(nextObserved.keys());\n\n if (observed === undefined || !equalSelection(snapshot, values, sources)) {\n snapshot = { commands: selectionCommands(values), sources, values };\n }\n\n observed = nextObserved;\n observedSelect = select;\n\n return snapshot;\n };\n}\n\nexport { createSelectionSnapshot, emptySelectionSnapshot };\nexport type { ModelSelector, SelectionContext, SelectionShape };\n","import type { Readable } from '@opetope/core';\nimport { createAggregateError } from '@opetope/core/internal';\n\ntype Subscription = { dispose: (() => void) | undefined };\ntype Listener = Readonly<{ notify: () => void }>;\n\nfunction releaseSubscriptions(subscriptions: Iterable<Subscription>, failures: unknown[] = []): void {\n for (const subscription of subscriptions) {\n const dispose = subscription.dispose;\n subscription.dispose = undefined;\n\n try {\n dispose?.();\n } catch (error) {\n failures.push(error);\n }\n }\n\n if (failures.length === 1) throw failures[0];\n\n if (failures.length > 1) throw createAggregateError(failures, 'Model selection sources failed to release.');\n}\n\n/** A subscription set, not a scheduler: source notifications use React's snapshot consistency check. */\nclass ModelSelectionStore {\n private enabled = false;\n private readonly listeners = new Set<Listener>();\n private sources: ReadonlySet<Readable<unknown>> = new Set();\n private readonly subscriptions = new Map<Readable<unknown>, Subscription>();\n\n close(): void {\n this.enabled = false;\n this.sources = new Set();\n this.releaseAll();\n }\n\n commit(sources: ReadonlySet<Readable<unknown>>): void {\n this.sources = sources;\n this.enabled = true;\n\n try {\n this.synchronize();\n } catch (error) {\n this.releaseAll([error]);\n }\n }\n\n subscribe = (notify: () => void): (() => void) => {\n const listener = { notify };\n this.listeners.add(listener);\n\n try {\n this.synchronize();\n } catch (error) {\n this.listeners.delete(listener);\n this.releaseAll([error]);\n }\n\n return () => {\n this.listeners.delete(listener);\n\n if (this.listeners.size === 0) this.releaseAll();\n };\n };\n\n private add(source: Readable<unknown>): void {\n const subscription: Subscription = { dispose: undefined };\n // Reserve before foreign code: a synchronous notification may replace or close this selection.\n this.subscriptions.set(source, subscription);\n const dispose = source.subscribe(this.notify);\n\n if (typeof dispose !== 'function') throw new TypeError('Model selection source must return a disposer.');\n\n if (this.subscriptions.get(source) === subscription) subscription.dispose = dispose;\n else dispose();\n }\n\n private notify = (): void => {\n for (const listener of [...this.listeners]) {\n if (this.listeners.has(listener)) listener.notify();\n }\n };\n\n private releaseAll(failures?: unknown[]): void {\n const subscriptions = [...this.subscriptions.values()];\n this.subscriptions.clear();\n releaseSubscriptions(subscriptions, failures);\n }\n\n private removeUnselected(): void {\n const removed: Subscription[] = [];\n\n for (const [source, subscription] of this.subscriptions) {\n if (this.sources.has(source)) continue;\n\n this.subscriptions.delete(source);\n removed.push(subscription);\n }\n\n releaseSubscriptions(removed);\n }\n\n private synchronize(): void {\n this.removeUnselected();\n\n for (const source of this.sources) {\n if (!this.enabled || this.listeners.size === 0) return;\n\n if (this.sources.has(source) && !this.subscriptions.has(source)) this.add(source);\n }\n }\n}\n\nexport { ModelSelectionStore };\n","import { useLayoutEffect, useMemo, useRef, useSyncExternalStore } from 'react';\n\nimport type { Call, ModelOf } from '@opetope/core';\n\nimport type { CommandHook } from './command-hook';\nimport { useCommands } from './commands-hook';\nimport { ContributionError } from './errors';\nimport { subscribeIdle } from './idle-subscription';\nimport type { AnyModel } from './model-binding';\nimport { createSelectionSnapshot, emptySelectionSnapshot } from './model-selection-snapshot';\nimport type { ModelSelector, SelectionContext, SelectionShape } from './model-selection-snapshot';\nimport { ModelSelectionStore } from './model-selection-store';\nimport { useFrame } from './mount-context';\n\ntype SelectedValue<Value> = Value extends Call<infer Input, infer Output> ? CommandHook<Input, Output> : Value;\ntype ModelSelection<Selected> = { readonly [Key in keyof Selected]: SelectedValue<Selected[Key]> };\n\nconst readEmptySnapshot = () => emptySelectionSnapshot;\n\nfunction useSelectionStore(selected: boolean): ModelSelectionStore | undefined {\n const storeRef = useRef<ModelSelectionStore | undefined>(undefined);\n\n return selected ? (storeRef.current ??= new ModelSelectionStore()) : undefined;\n}\n\n/** D205: resolve only a granted model, explicitly read data, and reuse the ordinary per-key command consumers. */\nfunction useModel<Declaration extends AnyModel>(declaration: Declaration): ModelOf<Declaration>;\nfunction useModel<Declaration extends AnyModel, const Selected extends object>(\n declaration: Declaration,\n select: (model: ModelOf<Declaration>, context: SelectionContext) => Selected & SelectionShape<Selected>,\n): ModelSelection<Selected>;\nfunction useModel<Declaration extends AnyModel>(\n declaration: Declaration,\n select?: (model: ModelOf<Declaration>, context: SelectionContext) => object,\n): unknown {\n const frame = useFrame();\n\n if (!frame.models.has(declaration)) {\n throw new ContributionError('missing', `Model ${declaration.id} is not granted to this contribution.`);\n }\n\n const model = frame.models.get(declaration) as ModelOf<Declaration>;\n const store = useSelectionStore(select !== undefined);\n // D214: the selector of this render, read by a reader that belongs to the model. Writing the latest callback into\n // a ref is what `useSyncExternalStoreWithSelector` does: the reader only reads it, and never writes to it.\n const selectRef = useRef<ModelSelector<ModelOf<Declaration>> | undefined>(undefined);\n selectRef.current = select;\n const selected = select !== undefined;\n const getSnapshot = useMemo(\n () => (selected ? createSelectionSnapshot(model, selectRef) : readEmptySnapshot),\n [model, selected],\n );\n const snapshot = useSyncExternalStore(store?.subscribe ?? subscribeIdle, getSnapshot, getSnapshot);\n const commands = useCommands(snapshot.commands);\n useLayoutEffect(() => () => store?.close(), [store]);\n useLayoutEffect(() => store?.commit(snapshot.sources), [snapshot, store]);\n\n if (select === undefined) return model;\n\n return Object.fromEntries(\n Reflect.ownKeys(snapshot.values).map(key => [\n key,\n Object.prototype.hasOwnProperty.call(commands, key) ? commands[key] : snapshot.values[key],\n ]),\n );\n}\n\nexport { useModel };\n","import type { FunctionComponent } from 'react';\n\nimport type { ModelIdentity } from '@opetope/core/internal';\n\nimport { ContributionError } from './errors';\n\ntype AnyModel = ModelIdentity;\n\n/**\n * The marker a component uses to state which UI models of its contribution it reads. `slot` checks by type that the\n * contribution grants every model of this list — it may grant more, because the mount serves its whole instance.\n *\n * The runtime authority is the mount frame, not this marker: `useModel` resolves against what the mount was given\n * and refuses anything else. The lint rule `opetope/require-declared-models` checks visible contribution sites and\n * each reader of a per-mount model within the same module. Imported implementations and dynamic declaration lists\n * remain unknown to this syntactic check (D85, D158, D250).\n */\ntype ComponentRequiringModels<Props, Models extends readonly AnyModel[]> = FunctionComponent<Props> & {\n readonly requires: Models;\n};\n\nfunction requiresModels<const Models extends readonly AnyModel[]>(\n models: Models,\n): <Props>(component: FunctionComponent<Props>) => ComponentRequiringModels<Props, Models> {\n const declared = Object.freeze([...models]) as unknown as Models;\n\n return <Props>(component: FunctionComponent<Props>): ComponentRequiringModels<Props, Models> => {\n const existing = (component as { readonly requires?: readonly AnyModel[] }).requires;\n\n if (existing !== undefined) {\n throw new ContributionError('duplicate', 'Component already declares the models it requires.');\n }\n\n Object.defineProperty(component, 'requires', { enumerable: false, value: declared });\n\n return component as ComponentRequiringModels<Props, Models>;\n };\n}\n\nexport { requiresModels };\n","import { useCallback, useEffect, useSyncExternalStore } from 'react';\n\nimport type { Resource, ResourceSnapshot } from '@opetope/runtime';\nimport type { ResourceRequestKey } from '@opetope/runtime/internal';\n\nfunction useResource<Data, Key extends ResourceRequestKey>(resource: Resource<Data, Key>): ResourceSnapshot<Data, Key> {\n const subscribe = useCallback((listener: () => void) => resource.subscribe(listener), [resource]);\n // D199: a resource is a structural contract, so it may be an object with methods. Reading through it keeps the\n // receiver the adapter expects; handing `resource.getSnapshot` to React would call it with none.\n const getSnapshot = useCallback(() => resource.getSnapshot(), [resource]);\n const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n useEffect(() => resource.retain(), [resource]);\n\n return snapshot;\n}\n\nexport { useResource };\n","import type { ComponentType, ReactNode } from 'react';\nimport { createElement, Fragment } from 'react';\n\nimport type { DeclarationId } from '@opetope/core';\nimport { declarationId } from '@opetope/core';\nimport type { ContributionTarget } from '@opetope/core/internal';\nimport { createContributionTarget } from '@opetope/core/internal';\nimport type { ContributionModel } from '@opetope/runtime/internal';\n\nimport { contributionAuthority, ContributionMount } from './contribution-frame';\nimport { useReadable } from './readable-hooks';\n\ntype SlotProperties = object | undefined;\ntype EmptySlotProperties = Readonly<Record<string, never>>;\ntype SlotComponentProperties<Props extends SlotProperties> = [Props] extends [undefined]\n ? EmptySlotProperties\n : Extract<Props, object>;\n\n/**\n * What a contribution publishes: the component, the optional props adapter and the optional UI models the mount\n * creates. Without an adapter the component takes the slot props; with one it takes exactly the adapter result (D85).\n */\ntype SlotContribution<Props extends SlotProperties = undefined> = {\n readonly Component: ComponentType<never>;\n readonly models?: readonly ContributionModel[];\n readonly props?: (slotProps: SlotComponentProperties<Props>) => object;\n};\n\ntype SlotTarget<Props extends SlotProperties = undefined> = ContributionTarget<SlotContribution<Props>>;\n\ntype SwitchSlotTarget<Route extends string, Props extends SlotProperties = undefined> = ((\n route: Route,\n) => SlotTarget<Props>) & {\n readonly id: DeclarationId;\n};\n\ntype SlotRenderProps<Props extends SlotProperties> = [Props] extends [undefined]\n ? {\n readonly props?: never;\n readonly target: SlotTarget<Props>;\n }\n : {\n readonly props: SlotComponentProperties<Props>;\n readonly target: SlotTarget<Props>;\n };\n\nconst emptySlotProperties: EmptySlotProperties = Object.freeze({});\n\nfunction defineSlot<Props extends SlotProperties = undefined>(options: { readonly id: string }): SlotTarget<Props> {\n return createContributionTarget<SlotContribution<Props>>(options, 'slot');\n}\n\nfunction defineSwitchSlot<Route extends string, Props extends SlotProperties = undefined>(options: {\n readonly id: string;\n}): SwitchSlotTarget<Route, Props> {\n const id = declarationId(options.id);\n const targets = new Map<Route, SlotTarget<Props>>();\n\n const switchSlot = (route: Route): SlotTarget<Props> => {\n const existing = targets.get(route);\n\n if (existing !== undefined) return existing;\n\n const target = defineSlot<Props>({ id: `${id}/${route}` });\n targets.set(route, target);\n\n return target;\n };\n\n Object.defineProperty(switchSlot, 'id', { enumerable: true, value: id });\n\n return Object.freeze(switchSlot) as SwitchSlotTarget<Route, Props>;\n}\n\n/**\n * Every contribution renders inside its own mount: the models its generation owns plus the UI models it declared,\n * created on mount and closed on unmount (D70, D85).\n */\nfunction Slot<Props extends SlotProperties>(options: SlotRenderProps<Props>): ReactNode {\n const entries = useReadable(options.target.entries);\n const slotProps: object = 'props' in options ? (options.props ?? emptySlotProperties) : emptySlotProperties;\n\n if (entries.length === 0) return null;\n\n return entries.map(entry =>\n createElement(\n Fragment,\n { key: entry.id },\n createElement(ContributionMount, {\n authority: contributionAuthority(entry),\n contribution: entry.value,\n contributionId: entry.id,\n slotProps,\n targetId: options.target.id,\n }),\n ),\n );\n}\n\nexport { defineSlot, defineSwitchSlot, Slot };\nexport type { SlotComponentProperties, SlotContribution, SlotProperties, SlotTarget, SwitchSlotTarget };\n"],"names":["failureOutcome","options","error","isCancellation","notification","_a","reportDetachedError","callbackError","runOptions","onSuccess","signal","requestOf","input","resolve","promise","settle","request","outcome","CommandHookController","ContributionError","aborted","invoker","notify","CallError","compatibleAbortReason","invocation","value","emptyStatus","unchangedState","current","slot","inFlight","nextState","lastError","result","useCommandSlot","slots","useState","useCommand","command","record","useFrame","currentSlot","hookState","setHookState","useInsertionEffect","useLayoutEffect","run","handler","useMemo","idleRelease","subscribeIdle","idleRevision","emptyHooks","createEntry","unchangedHook","previous","next","outcomeStatus","updateEntry","entry","selectEntry","CommandGroup","selected","key","listener","commands","frame","useCommandGroup","groupRef","useRef","useCommands","group","getSnapshot","useSyncExternalStore","readSource","source","sources","isReadable","collectSelection","select","project","equalSelection","values","keys","previousKeys","index","emptySelectionSnapshot","selectionCommands","isCallTarget","createSelectionSnapshot","model","selectRef","observed","observedSelect","snapshot","nextObserved","context","releaseSubscriptions","subscriptions","failures","subscription","dispose","createAggregateError","ModelSelectionStore","removed","readEmptySnapshot","useSelectionStore","storeRef","useModel","declaration","store","requiresModels","models","declared","component","useResource","resource","subscribe","useCallback","useEffect","emptySlotProperties","defineSlot","createContributionTarget","defineSwitchSlot","id","declarationId","targets","switchSlot","route","existing","target","Slot","entries","useReadable","slotProps","createElement","Fragment","ContributionMount","contributionAuthority"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/command-hook-controller.ts","../src/command-hook.ts","../src/idle-subscription.ts","../src/commands-hook.ts","../src/model-selection-snapshot.ts","../src/model-selection-store.ts","../src/model-hook.ts","../src/requires-models.ts","../src/resource-hook.ts","../src/slot.tsx"],"sourcesContent":["import { CallError } from '@opetope/core';\nimport { compatibleAbortReason, isCancellation, reportDetachedError } from '@opetope/core/internal';\nimport type { CallInputArgs, CallRunOptions } from '@opetope/core/internal';\n\nimport type { CommandOutcome } from './command';\nimport { ContributionError } from './errors';\n\ntype CommandRunOptions<Output> = Readonly<{\n onFailure?: (error: unknown) => PromiseLike<void> | void;\n onSuccess?: (value: Output) => PromiseLike<void> | void;\n signal?: AbortSignal;\n}>;\n\ntype CommandRun<Input, Output> = {\n (input: Input, options?: CommandRunOptions<Output>): Promise<CommandOutcome<Output>>;\n (...args: [...CallInputArgs<Input>, options?: CommandRunOptions<Output>]): Promise<CommandOutcome<Output>>;\n};\n\ntype CommandInvoker = Readonly<{\n run: (input: never, options?: CallRunOptions<unknown>) => Promise<unknown>;\n}>;\n\ntype CommandNotification<Output> = (inFlight: boolean, outcome?: CommandOutcome<Output>) => void;\n\ntype CommandRequest<Input, Output> = {\n input: Input | undefined;\n options: CommandRunOptions<Output> | undefined;\n readonly promise: Promise<CommandOutcome<Output>>;\n resolve: ((outcome: CommandOutcome<Output>) => void) | undefined;\n};\n\nfunction failureOutcome<Output>(options: CommandRunOptions<Output> | undefined, error: unknown): CommandOutcome<never> {\n if (isCancellation(error)) return { reason: error, status: 'cancelled' };\n\n try {\n const notification = options?.onFailure?.(error);\n\n if (notification !== undefined) void Promise.resolve(notification).catch(reportDetachedError);\n } catch (callbackError) {\n reportDetachedError(callbackError);\n }\n\n return { error, status: 'failed' };\n}\n\nfunction runOptions<Output>(options: CommandRunOptions<Output> | undefined): CallRunOptions<Output> | undefined {\n const onSuccess = options?.onSuccess;\n const signal = options?.signal;\n\n if (onSuccess === undefined) return signal === undefined ? undefined : { signal };\n\n return signal === undefined ? { onSuccess } : { onSuccess, signal };\n}\n\nfunction requestOf<Input, Output>(\n input: Input,\n options: CommandRunOptions<Output> | undefined,\n): CommandRequest<Input, Output> {\n let resolve!: (outcome: CommandOutcome<Output>) => void;\n const promise = new Promise<CommandOutcome<Output>>(settle => {\n resolve = settle;\n });\n\n return { input, options: options === undefined ? undefined : { ...options }, promise, resolve };\n}\n\nfunction settle<Input, Output>(request: CommandRequest<Input, Output>, outcome: CommandOutcome<Output>): void {\n const resolve = request.resolve;\n request.input = undefined;\n request.options = undefined;\n request.resolve = undefined;\n resolve?.(outcome);\n}\n\n/**\n * D203: the consumer keeps a status, not a schedule. Every `run` reaches the call, and the policy the call was\n * created with — `queue`, `latest`, `parallel`, `once`, `dedupe` — decides what happens to it. What stays\n * local is what only this consumer knows: whether it is mounted, its own input signal, and the outcome it shows.\n */\nclass CommandHookController<Input, Output> {\n // React may supply an event; inputless handlers intentionally never forward it as Call data.\n readonly handler = (): void => {\n void this.run(undefined as Input);\n };\n readonly run: CommandRun<Input, Output> = ((input: Input, options?: CommandRunOptions<Output>) => {\n if (this.notify === undefined) {\n return Promise.resolve({\n reason: new ContributionError('inactive', 'Command consumer is not mounted.'),\n status: 'cancelled' as const,\n });\n }\n\n const aborted = this.cancelledInput(options);\n\n if (aborted !== undefined) return Promise.resolve(aborted);\n\n const request = requestOf(input, options);\n this.start(request);\n\n return request.promise;\n }) as CommandRun<Input, Output>;\n private inFlight = 0;\n private notify: CommandNotification<Output> | undefined;\n\n constructor(private readonly invoker: CommandInvoker) {}\n\n attach(notify: CommandNotification<Output>): () => void {\n this.notify = notify;\n\n return () => {\n if (this.notify !== notify) return;\n\n this.notify = undefined;\n };\n }\n\n synchronize(): void {\n this.notify?.(this.inFlight > 0);\n }\n\n private aborted(signal: AbortSignal): CommandOutcome<never> {\n return {\n reason: new CallError('cancelled', 'Command input was cancelled.', compatibleAbortReason(signal)),\n status: 'cancelled',\n };\n }\n\n private cancelledInput(options: CommandRunOptions<Output> | undefined): CommandOutcome<never> | undefined {\n return options?.signal?.aborted === true ? this.aborted(options.signal) : undefined;\n }\n\n private finish(request: CommandRequest<Input, Output>, outcome: CommandOutcome<Output>): void {\n if (request.resolve === undefined) return;\n\n this.inFlight -= 1;\n settle(request, outcome);\n this.notify?.(this.inFlight > 0, outcome);\n }\n\n private invoke(request: CommandRequest<Input, Output>): void {\n let invocation: Promise<Output>;\n\n try {\n invocation = this.invoker.run(request.input as never, runOptions(request.options) as never) as Promise<Output>;\n request.input = undefined;\n } catch (error) {\n this.finish(request, failureOutcome(request.options, error));\n\n return;\n }\n\n void invocation.then(\n value => this.finish(request, { status: 'ok', value }),\n (error: unknown) => this.finish(request, failureOutcome(request.options, error)),\n );\n }\n\n private start(request: CommandRequest<Input, Output>): void {\n this.inFlight += 1;\n // Reserve the flight before publishing: a reentrant run must already see this consumer as busy.\n this.notify?.(true);\n this.invoke(request);\n }\n}\n\nexport { CommandHookController };\nexport type { CommandInvoker, CommandRun };\n","import { useInsertionEffect, useLayoutEffect, useMemo, useState } from 'react';\n\nimport type { Call } from '@opetope/core';\nimport type { IsAny } from '@opetope/core/internal';\n\nimport type { CommandOutcome } from './command';\nimport { CommandHookController } from './command-hook-controller';\nimport type { CommandInvoker, CommandRun } from './command-hook-controller';\nimport { ContributionError } from './errors';\nimport { useFrame } from './mount-context';\n\ninterface CommandHookStatus<Output> {\n readonly inFlight: boolean;\n readonly lastError: unknown | null;\n readonly lastResult: Output | undefined;\n}\n\nconst emptyStatus: CommandHookStatus<never> = { inFlight: false, lastError: null, lastResult: undefined };\n\ntype CommandHandler<Input> =\n IsAny<Input> extends true\n ? object\n : [Input] extends [never]\n ? object\n : [Input] extends [void]\n ? { readonly handler: () => void }\n : object;\n\ntype CommandHook<Input, Output> = CommandHookStatus<Output> & {\n readonly run: CommandRun<Input, Output>;\n} & CommandHandler<Input>;\n\ntype CommandHookState<Input, Output> = CommandHookStatus<Output> & {\n readonly slot: CommandHookController<Input, Output>;\n};\n\nfunction unchangedState<Input, Output>(\n current: CommandHookState<Input, Output>,\n slot: CommandHookController<Input, Output>,\n inFlight: boolean,\n outcome: CommandOutcome<Output> | undefined,\n): boolean {\n return outcome === undefined && current.slot === slot && current.inFlight === inFlight;\n}\n\nfunction nextState<Input, Output>(\n current: CommandHookState<Input, Output>,\n slot: CommandHookController<Input, Output>,\n inFlight: boolean,\n outcome?: CommandOutcome<Output>,\n): CommandHookState<Input, Output> {\n if (unchangedState(current, slot, inFlight, outcome)) return current;\n\n const { lastError, lastResult } = current.slot === slot ? current : { lastError: null, lastResult: undefined };\n\n if (outcome?.status === 'ok') return { inFlight, lastError: null, lastResult: outcome.value, slot };\n\n if (outcome?.status === 'failed') return { inFlight, lastError: outcome.error, lastResult, slot };\n\n return { inFlight, lastError, lastResult, slot };\n}\n\nfunction useCommandSlot<Input, Output>(invoker: CommandInvoker): CommandHookController<Input, Output> {\n // Slots are inert until commit. An interrupted render cannot fence the committed source or admit its own work.\n const [slots] = useState(() => new WeakMap<object, CommandHookController<Input, Output>>());\n let slot = slots.get(invoker);\n\n if (slot === undefined) {\n slot = new CommandHookController(invoker);\n slots.set(invoker, slot);\n }\n\n return slot;\n}\n\nfunction useCommand<Input, Output>(command: Call<Input, Output>): CommandHook<Input, Output> {\n const frame = useFrame();\n const record = frame.commandRecords.get(command);\n\n if (record === undefined) {\n throw new ContributionError('missing', `Command ${command.id} is not bound in this contribution.`);\n }\n\n const currentSlot = useCommandSlot<Input, Output>(record.invoker);\n const [hookState, setHookState] = useState<CommandHookState<Input, Output>>(() => ({\n inFlight: false,\n lastError: null,\n lastResult: undefined,\n slot: currentSlot,\n }));\n useInsertionEffect(\n () =>\n currentSlot.attach((inFlight, outcome) =>\n setHookState(current => nextState(current, currentSlot, inFlight, outcome)),\n ),\n [currentSlot],\n );\n useLayoutEffect(() => currentSlot.synchronize(), [currentSlot]);\n const { inFlight, lastError, lastResult } = hookState.slot === currentSlot ? hookState : emptyStatus;\n const run = currentSlot.run;\n const handler = currentSlot.handler;\n\n return useMemo(\n () => ({ handler, inFlight, lastError, lastResult, run }),\n [handler, inFlight, lastError, lastResult, run],\n );\n}\n\nexport { useCommand };\nexport type { CommandHook };\n","const idleRelease = (): void => undefined;\nconst subscribeIdle = (): (() => void) => idleRelease;\nconst idleRevision = (): number => 0;\n\nexport { idleRevision, subscribeIdle };\n","import { useInsertionEffect, useLayoutEffect, useRef, useSyncExternalStore } from 'react';\n\nimport type { Call } from '@opetope/core';\n\nimport type { CommandOutcome } from './command';\nimport type { CommandHook } from './command-hook';\nimport { CommandHookController } from './command-hook-controller';\nimport type { CommandInvoker } from './command-hook-controller';\nimport { ContributionError } from './errors';\nimport { idleRevision, subscribeIdle } from './idle-subscription';\nimport { useFrame } from './mount-context';\nimport type { Frame } from './mount-frame';\n\ntype CommandSelection = Readonly<Record<string, Call<never, unknown>>>;\ntype HookOf<Command> = Command extends Call<infer Input, infer Output> ? CommandHook<Input, Output> : never;\ntype CommandsHook<Commands> = {\n readonly [Key in keyof Commands]: HookOf<Commands[Key]>;\n};\nconst emptyHooks = Object.freeze({});\n\ntype CommandEntry = {\n detach: (() => void) | undefined;\n hook: CommandHook<never, unknown> & { readonly handler: () => void };\n readonly invoker: CommandInvoker;\n readonly slot: CommandHookController<never, unknown>;\n};\n\nfunction createEntry(invoker: CommandInvoker): CommandEntry {\n const slot = new CommandHookController<never, unknown>(invoker);\n\n return {\n detach: undefined,\n hook: { handler: slot.handler, inFlight: false, lastError: null, lastResult: undefined, run: slot.run },\n invoker,\n slot,\n };\n}\n\nfunction unchangedHook(previous: CommandHook<never, unknown>, next: CommandHook<never, unknown>): boolean {\n return (\n previous.inFlight === next.inFlight &&\n Object.is(previous.lastResult, next.lastResult) &&\n Object.is(previous.lastError, next.lastError)\n );\n}\n\nfunction outcomeStatus(previous: CommandHook<never, unknown>, outcome: CommandOutcome<unknown> | undefined) {\n if (outcome?.status === 'ok') return { lastError: null, lastResult: outcome.value };\n\n if (outcome?.status === 'failed') return { lastError: outcome.error, lastResult: previous.lastResult };\n\n return previous;\n}\n\nfunction updateEntry(entry: CommandEntry, inFlight: boolean, outcome?: CommandOutcome<unknown>): boolean {\n const previous = entry.hook;\n\n if (outcome === undefined && previous.inFlight === inFlight) return false;\n\n const { lastError, lastResult } = outcomeStatus(previous, outcome);\n const next = { handler: entry.slot.handler, inFlight, lastError, lastResult, run: previous.run };\n\n if (unchangedHook(previous, next)) return false;\n\n entry.hook = next;\n\n return true;\n}\n\nfunction selectEntry(previous: CommandEntry | undefined, invoker: CommandInvoker): CommandEntry {\n return previous?.invoker === invoker ? previous : createEntry(invoker);\n}\n\n/** Selection is inert during render; only a committed selection can admit work or discard an old queue. */\nclass CommandGroup {\n private entries = new Map<PropertyKey, CommandEntry>();\n private readonly listeners = new Set<() => void>();\n private revision = 0;\n\n close(): void {\n for (const entry of this.entries.values()) {\n entry.detach?.();\n entry.detach = undefined;\n }\n\n this.entries.clear();\n }\n\n commit(selected: Map<PropertyKey, CommandEntry>): void {\n for (const [key, entry] of this.entries) {\n if (selected.get(key) === entry) continue;\n\n entry.detach?.();\n entry.detach = undefined;\n }\n\n this.entries = selected;\n\n for (const entry of selected.values()) {\n entry.detach ??= entry.slot.attach((inFlight, outcome) => {\n if (!updateEntry(entry, inFlight, outcome)) return;\n\n this.revision += 1;\n\n for (const listener of this.listeners) listener();\n });\n }\n }\n\n getSnapshot = (): number => this.revision;\n\n prepare(commands: CommandSelection, frame: Frame): Map<PropertyKey, CommandEntry> {\n const selected = new Map<PropertyKey, CommandEntry>();\n\n for (const key of Reflect.ownKeys(commands)) {\n if (!Object.prototype.propertyIsEnumerable.call(commands, key)) continue;\n\n const command = (commands as Readonly<Record<PropertyKey, Call<never, unknown>>>)[key];\n const record = command === undefined ? undefined : frame.commandRecords.get(command);\n\n if (record === undefined) {\n throw new ContributionError('missing', `Command at ${String(key)} is not bound in this contribution.`);\n }\n\n selected.set(key, selectEntry(this.entries.get(key), record.invoker));\n }\n\n return selected;\n }\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n\n return () => {\n this.listeners.delete(listener);\n };\n };\n}\n\nfunction useCommandGroup(commands: CommandSelection): CommandGroup | undefined {\n const groupRef = useRef<CommandGroup | undefined>(undefined);\n const populated = Reflect.ownKeys(commands).some(key => Object.prototype.propertyIsEnumerable.call(commands, key));\n\n return populated ? (groupRef.current ??= new CommandGroup()) : undefined;\n}\n\nfunction useCommands<Commands extends { readonly [Key in keyof Commands]: Call<never, unknown> }>(\n commands: Commands,\n): CommandsHook<Commands> {\n const frame = useFrame();\n const group = useCommandGroup(commands);\n const getSnapshot = group?.getSnapshot ?? idleRevision;\n useSyncExternalStore(group?.subscribe ?? subscribeIdle, getSnapshot, getSnapshot);\n const selected = group?.prepare(commands, frame);\n useInsertionEffect(() => () => group?.close(), [group]);\n useInsertionEffect(() => {\n if (selected !== undefined) group?.commit(selected);\n });\n useLayoutEffect(() => {\n if (selected === undefined) return;\n\n for (const entry of selected.values()) entry.slot.synchronize();\n });\n\n return (\n selected === undefined ? emptyHooks : Object.fromEntries([...selected].map(([key, entry]) => [key, entry.hook]))\n ) as CommandsHook<Commands>;\n}\n\nexport { useCommands };\n","import type { Readable } from '@opetope/core';\nimport { isCallTarget, isReadable } from '@opetope/core/internal';\n\nimport { ContributionError } from './errors';\nimport type { AnyCommand } from './model-binding';\n\ninterface SelectionRead {\n <Value>(source: Readable<Value>): Value;\n <Value, Selected>(source: Readable<Value>, select: (value: Value) => Selected): Selected;\n}\n\ntype SelectionContext = Readonly<{ read: SelectionRead }>;\ntype NonSelectionRecord =\n | readonly unknown[]\n | ((...arguments_: never[]) => unknown)\n | (abstract new (...arguments_: never[]) => unknown)\n | Date\n | PromiseLike<unknown>\n | ReadonlyMap<unknown, unknown>\n | ReadonlySet<unknown>\n | RegExp\n | WeakMap<object, unknown>\n | WeakSet<object>;\n/** A named interface needs no open index signature; container and callable results are not field selections. */\ntype SelectionShape<Selected extends object> = Selected extends NonSelectionRecord ? never : Selected;\ntype SelectionRecord = Readonly<Record<PropertyKey, unknown>>;\ntype SelectionSnapshot = Readonly<{\n commands: Readonly<Record<PropertyKey, AnyCommand>>;\n sources: ReadonlySet<Readable<unknown>>;\n values: SelectionRecord;\n}>;\n\nfunction readSource<Value>(source: Readable<Value>, sources: Map<Readable<unknown>, unknown>): Value {\n if (!isReadable(source)) {\n throw new ContributionError('binding-invalid', 'Model selection read requires a Readable.');\n }\n\n if (sources.has(source)) return sources.get(source) as Value;\n\n const value = source.getSnapshot();\n sources.set(source, value);\n\n return value;\n}\n\nfunction collectSelection(\n select: (context: SelectionContext) => object,\n sources: Map<Readable<unknown>, unknown>,\n): SelectionRecord {\n const read = <Value, Selected>(source: Readable<Value>, project?: (value: Value) => Selected) => {\n const value = readSource(source, sources);\n\n return project === undefined ? value : project(value);\n };\n const selected = select({ read });\n\n if (typeof selected !== 'object' || selected === null || Array.isArray(selected)) {\n throw new ContributionError('binding-invalid', 'Model selection requires a record.');\n }\n\n return Object.fromEntries(\n Reflect.ownKeys(selected)\n .filter(key => Object.prototype.propertyIsEnumerable.call(selected, key))\n .map(key => [key, (selected as SelectionRecord)[key]]),\n );\n}\n\nfunction equalSelection(previous: SelectionSnapshot, values: SelectionRecord, sources: ReadonlySet<Readable<unknown>>) {\n const keys = Reflect.ownKeys(values);\n const previousKeys = Reflect.ownKeys(previous.values);\n\n if (keys.length !== previousKeys.length || sources.size !== previous.sources.size) return false;\n\n for (const source of sources) if (!previous.sources.has(source)) return false;\n\n return keys.every((key, index) => previousKeys[index] === key && Object.is(previous.values[key], values[key]));\n}\n\ntype ModelSelector<Model> = (model: Model, context: SelectionContext) => object;\ntype SelectionReaderRef<Model> = { readonly current: ModelSelector<Model> | undefined };\n\nconst emptySelectionSnapshot: SelectionSnapshot = { commands: {}, sources: new Set(), values: {} };\n\nfunction selectionCommands(values: SelectionRecord): Readonly<Record<PropertyKey, AnyCommand>> {\n return Object.fromEntries(\n Reflect.ownKeys(values)\n .filter(key => isCallTarget(values[key]))\n .map(key => [key, values[key]]),\n ) as Readonly<Record<PropertyKey, AnyCommand>>;\n}\n\n/**\n * D214: one reader per model, not per render. The author's selector arrives through a ref, so an inline arrow does not\n * rebuild the reader; the fast path — nothing observed has moved — belongs to the selector that computed the snapshot,\n * so a new selector is always called again, its result compared field by field, and an equal selection keeps the\n * previous snapshot. A rejected or abandoned render still changes nothing: only a committed selection subscribes.\n */\nfunction createSelectionSnapshot<Model>(model: Model, selectRef: SelectionReaderRef<Model>): () => SelectionSnapshot {\n let observed: Map<Readable<unknown>, unknown> | undefined;\n let observedSelect: ModelSelector<Model> | undefined;\n let snapshot: SelectionSnapshot = emptySelectionSnapshot;\n\n return () => {\n const select = selectRef.current;\n\n if (select === undefined) return emptySelectionSnapshot;\n\n if (\n observed !== undefined &&\n observedSelect === select &&\n [...observed].every(([source, value]) => Object.is(source.getSnapshot(), value))\n ) {\n return snapshot;\n }\n\n const nextObserved = new Map<Readable<unknown>, unknown>();\n const values = collectSelection(context => select(model, context), nextObserved);\n const sources = new Set(nextObserved.keys());\n\n if (observed === undefined || !equalSelection(snapshot, values, sources)) {\n snapshot = { commands: selectionCommands(values), sources, values };\n }\n\n observed = nextObserved;\n observedSelect = select;\n\n return snapshot;\n };\n}\n\nexport { createSelectionSnapshot, emptySelectionSnapshot };\nexport type { ModelSelector, SelectionContext, SelectionShape };\n","import type { Readable } from '@opetope/core';\nimport { createAggregateError } from '@opetope/core/internal';\n\ntype Subscription = { dispose: (() => void) | undefined };\ntype Listener = Readonly<{ notify: () => void }>;\n\nfunction releaseSubscriptions(subscriptions: Iterable<Subscription>, failures: unknown[] = []): void {\n for (const subscription of subscriptions) {\n const dispose = subscription.dispose;\n subscription.dispose = undefined;\n\n try {\n dispose?.();\n } catch (error) {\n failures.push(error);\n }\n }\n\n if (failures.length === 1) throw failures[0];\n\n if (failures.length > 1) throw createAggregateError(failures, 'Model selection sources failed to release.');\n}\n\n/** A subscription set, not a scheduler: source notifications use React's snapshot consistency check. */\nclass ModelSelectionStore {\n private enabled = false;\n private readonly listeners = new Set<Listener>();\n private sources: ReadonlySet<Readable<unknown>> = new Set();\n private readonly subscriptions = new Map<Readable<unknown>, Subscription>();\n\n close(): void {\n this.enabled = false;\n this.sources = new Set();\n this.releaseAll();\n }\n\n commit(sources: ReadonlySet<Readable<unknown>>): void {\n this.sources = sources;\n this.enabled = true;\n\n try {\n this.synchronize();\n } catch (error) {\n this.releaseAll([error]);\n }\n }\n\n subscribe = (notify: () => void): (() => void) => {\n const listener = { notify };\n this.listeners.add(listener);\n\n try {\n this.synchronize();\n } catch (error) {\n this.listeners.delete(listener);\n this.releaseAll([error]);\n }\n\n return () => {\n this.listeners.delete(listener);\n\n if (this.listeners.size === 0) this.releaseAll();\n };\n };\n\n private add(source: Readable<unknown>): void {\n const subscription: Subscription = { dispose: undefined };\n // Reserve before foreign code: a synchronous notification may replace or close this selection.\n this.subscriptions.set(source, subscription);\n const dispose = source.subscribe(this.notify);\n\n if (typeof dispose !== 'function') throw new TypeError('Model selection source must return a disposer.');\n\n if (this.subscriptions.get(source) === subscription) subscription.dispose = dispose;\n else dispose();\n }\n\n private notify = (): void => {\n for (const listener of [...this.listeners]) {\n if (this.listeners.has(listener)) listener.notify();\n }\n };\n\n private releaseAll(failures?: unknown[]): void {\n const subscriptions = [...this.subscriptions.values()];\n this.subscriptions.clear();\n releaseSubscriptions(subscriptions, failures);\n }\n\n private removeUnselected(): void {\n const removed: Subscription[] = [];\n\n for (const [source, subscription] of this.subscriptions) {\n if (this.sources.has(source)) continue;\n\n this.subscriptions.delete(source);\n removed.push(subscription);\n }\n\n releaseSubscriptions(removed);\n }\n\n private synchronize(): void {\n this.removeUnselected();\n\n for (const source of this.sources) {\n if (!this.enabled || this.listeners.size === 0) return;\n\n if (this.sources.has(source) && !this.subscriptions.has(source)) this.add(source);\n }\n }\n}\n\nexport { ModelSelectionStore };\n","import { useLayoutEffect, useMemo, useRef, useSyncExternalStore } from 'react';\n\nimport type { Call, ModelOf } from '@opetope/core';\n\nimport type { CommandHook } from './command-hook';\nimport { useCommands } from './commands-hook';\nimport { ContributionError } from './errors';\nimport { subscribeIdle } from './idle-subscription';\nimport type { AnyModel } from './model-binding';\nimport { createSelectionSnapshot, emptySelectionSnapshot } from './model-selection-snapshot';\nimport type { ModelSelector, SelectionContext, SelectionShape } from './model-selection-snapshot';\nimport { ModelSelectionStore } from './model-selection-store';\nimport { useFrame } from './mount-context';\n\ntype SelectedValue<Value> = Value extends Call<infer Input, infer Output> ? CommandHook<Input, Output> : Value;\ntype ModelSelection<Selected> = { readonly [Key in keyof Selected]: SelectedValue<Selected[Key]> };\n\nconst readEmptySnapshot = () => emptySelectionSnapshot;\n\nfunction useSelectionStore(selected: boolean): ModelSelectionStore | undefined {\n const storeRef = useRef<ModelSelectionStore | undefined>(undefined);\n\n return selected ? (storeRef.current ??= new ModelSelectionStore()) : undefined;\n}\n\n/** D205: resolve only a granted model, explicitly read data, and reuse the ordinary per-key command consumers. */\nfunction useModel<Declaration extends AnyModel>(declaration: Declaration): ModelOf<Declaration>;\nfunction useModel<Declaration extends AnyModel, const Selected extends object>(\n declaration: Declaration,\n select: (model: ModelOf<Declaration>, context: SelectionContext) => Selected & SelectionShape<Selected>,\n): ModelSelection<Selected>;\nfunction useModel<Declaration extends AnyModel>(\n declaration: Declaration,\n select?: (model: ModelOf<Declaration>, context: SelectionContext) => object,\n): unknown {\n const frame = useFrame();\n\n if (!frame.models.has(declaration)) {\n throw new ContributionError('missing', `Model ${declaration.id} is not granted to this contribution.`);\n }\n\n const model = frame.models.get(declaration) as ModelOf<Declaration>;\n const store = useSelectionStore(select !== undefined);\n // D214: the selector of this render, read by a reader that belongs to the model. Writing the latest callback into\n // a ref is what `useSyncExternalStoreWithSelector` does: the reader only reads it, and never writes to it.\n const selectRef = useRef<ModelSelector<ModelOf<Declaration>> | undefined>(undefined);\n selectRef.current = select;\n const selected = select !== undefined;\n const getSnapshot = useMemo(\n () => (selected ? createSelectionSnapshot(model, selectRef) : readEmptySnapshot),\n [model, selected],\n );\n const snapshot = useSyncExternalStore(store?.subscribe ?? subscribeIdle, getSnapshot, getSnapshot);\n const commands = useCommands(snapshot.commands);\n useLayoutEffect(() => () => store?.close(), [store]);\n useLayoutEffect(() => store?.commit(snapshot.sources), [snapshot, store]);\n\n if (select === undefined) return model;\n\n return Object.fromEntries(\n Reflect.ownKeys(snapshot.values).map(key => [\n key,\n Object.prototype.hasOwnProperty.call(commands, key) ? commands[key] : snapshot.values[key],\n ]),\n );\n}\n\nexport { useModel };\n","import type { FunctionComponent } from 'react';\n\nimport type { ModelIdentity } from '@opetope/core/internal';\n\nimport { ContributionError } from './errors';\n\ntype AnyModel = ModelIdentity;\n\n/**\n * The marker a component uses to state which UI models of its contribution it reads. `slot` checks by type that the\n * contribution grants every model of this list — it may grant more, because the mount serves its whole instance.\n *\n * The runtime authority is the mount frame, not this marker: `useModel` resolves against what the mount was given\n * and refuses anything else. The lint rule `opetope/require-declared-models` checks visible contribution sites and\n * each reader of a per-mount model within the same module. Imported implementations and dynamic declaration lists\n * remain unknown to this syntactic check (D85, D158, D250).\n */\ntype ComponentRequiringModels<Props, Models extends readonly AnyModel[]> = FunctionComponent<Props> & {\n readonly requires: Models;\n};\n\nfunction requiresModels<const Models extends readonly AnyModel[]>(\n models: Models,\n): <Props>(component: FunctionComponent<Props>) => ComponentRequiringModels<Props, Models> {\n const declared = Object.freeze([...models]) as unknown as Models;\n\n return <Props>(component: FunctionComponent<Props>): ComponentRequiringModels<Props, Models> => {\n const existing = (component as { readonly requires?: readonly AnyModel[] }).requires;\n\n if (existing !== undefined) {\n throw new ContributionError('duplicate', 'Component already declares the models it requires.');\n }\n\n Object.defineProperty(component, 'requires', { enumerable: false, value: declared });\n\n return component as ComponentRequiringModels<Props, Models>;\n };\n}\n\nexport { requiresModels };\n","import { useCallback, useEffect, useSyncExternalStore } from 'react';\n\nimport type { Resource, ResourceSnapshot } from '@opetope/runtime';\nimport type { ResourceRequestKey } from '@opetope/runtime/internal';\n\nfunction useResource<Data, Key extends ResourceRequestKey>(resource: Resource<Data, Key>): ResourceSnapshot<Data, Key> {\n const subscribe = useCallback((listener: () => void) => resource.subscribe(listener), [resource]);\n // D199: a resource is a structural contract, so it may be an object with methods. Reading through it keeps the\n // receiver the adapter expects; handing `resource.getSnapshot` to React would call it with none.\n const getSnapshot = useCallback(() => resource.getSnapshot(), [resource]);\n const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n useEffect(() => resource.retain(), [resource]);\n\n return snapshot;\n}\n\nexport { useResource };\n","import type { ComponentType, ReactNode } from 'react';\nimport { createElement, Fragment } from 'react';\n\nimport type { DeclarationId } from '@opetope/core';\nimport { declarationId } from '@opetope/core';\nimport type { ContributionTarget } from '@opetope/core/internal';\nimport { createContributionTarget } from '@opetope/core/internal';\nimport type { ContributionModel } from '@opetope/runtime/internal';\n\nimport { contributionAuthority, ContributionMount } from './contribution-frame';\nimport { useReadable } from './readable-hooks';\n\ntype SlotProperties = object | undefined;\ntype EmptySlotProperties = Readonly<Record<string, never>>;\ntype SlotComponentProperties<Props extends SlotProperties> = [Props] extends [undefined]\n ? EmptySlotProperties\n : Extract<Props, object>;\n\n/**\n * What a contribution publishes: the component, the optional props adapter and the optional UI models the mount\n * creates. Without an adapter the component takes the slot props; with one it takes exactly the adapter result (D85).\n */\ntype SlotContribution<Props extends SlotProperties = undefined> = {\n readonly Component: ComponentType<never>;\n readonly models?: readonly ContributionModel[];\n readonly props?: (slotProps: SlotComponentProperties<Props>) => object;\n};\n\ntype SlotTarget<Props extends SlotProperties = undefined> = ContributionTarget<SlotContribution<Props>>;\n\ntype SwitchSlotTarget<Route extends string, Props extends SlotProperties = undefined> = ((\n route: Route,\n) => SlotTarget<Props>) & {\n readonly id: DeclarationId;\n};\n\ntype SlotRenderProps<Props extends SlotProperties> = [Props] extends [undefined]\n ? {\n readonly props?: never;\n readonly target: SlotTarget<Props>;\n }\n : {\n readonly props: SlotComponentProperties<Props>;\n readonly target: SlotTarget<Props>;\n };\n\nconst emptySlotProperties: EmptySlotProperties = Object.freeze({});\n\n/** D280: a slot is a name, so it takes its id as the first argument. */\nfunction defineSlot<Props extends SlotProperties = undefined>(id: string): SlotTarget<Props> {\n return createContributionTarget<SlotContribution<Props>>(id, 'slot');\n}\n\nfunction defineSwitchSlot<Route extends string, Props extends SlotProperties = undefined>(\n id: string,\n): SwitchSlotTarget<Route, Props> {\n const declared = declarationId(id);\n const targets = new Map<Route, SlotTarget<Props>>();\n\n const switchSlot = (route: Route): SlotTarget<Props> => {\n const existing = targets.get(route);\n\n if (existing !== undefined) return existing;\n\n const target = defineSlot<Props>(`${declared}/${route}`);\n targets.set(route, target);\n\n return target;\n };\n\n Object.defineProperty(switchSlot, 'id', { enumerable: true, value: declared });\n\n return Object.freeze(switchSlot) as SwitchSlotTarget<Route, Props>;\n}\n\n/**\n * Every contribution renders inside its own mount: the models its generation owns plus the UI models it declared,\n * created on mount and closed on unmount (D70, D85).\n */\nfunction Slot<Props extends SlotProperties>(options: SlotRenderProps<Props>): ReactNode {\n const entries = useReadable(options.target.entries);\n const slotProps: object = 'props' in options ? (options.props ?? emptySlotProperties) : emptySlotProperties;\n\n if (entries.length === 0) return null;\n\n return entries.map(entry =>\n createElement(\n Fragment,\n { key: entry.id },\n createElement(ContributionMount, {\n authority: contributionAuthority(entry),\n contribution: entry.value,\n contributionId: entry.id,\n slotProps,\n targetId: options.target.id,\n }),\n ),\n );\n}\n\nexport { defineSlot, defineSwitchSlot, Slot };\nexport type { SlotComponentProperties, SlotContribution, SlotProperties, SlotTarget, SwitchSlotTarget };\n"],"names":["failureOutcome","options","error","isCancellation","notification","_a","reportDetachedError","callbackError","runOptions","onSuccess","signal","requestOf","input","resolve","promise","settle","request","outcome","CommandHookController","ContributionError","aborted","invoker","notify","CallError","compatibleAbortReason","invocation","value","emptyStatus","unchangedState","current","slot","inFlight","nextState","lastError","lastResult","useCommandSlot","slots","useState","useCommand","command","record","useFrame","currentSlot","hookState","setHookState","useInsertionEffect","useLayoutEffect","run","handler","useMemo","idleRelease","subscribeIdle","idleRevision","emptyHooks","createEntry","unchangedHook","previous","next","outcomeStatus","updateEntry","entry","selectEntry","CommandGroup","selected","key","listener","commands","frame","useCommandGroup","groupRef","useRef","useCommands","group","getSnapshot","useSyncExternalStore","readSource","source","sources","isReadable","collectSelection","select","project","equalSelection","values","keys","previousKeys","index","emptySelectionSnapshot","selectionCommands","isCallTarget","createSelectionSnapshot","model","selectRef","observed","observedSelect","snapshot","nextObserved","context","releaseSubscriptions","subscriptions","failures","subscription","dispose","createAggregateError","ModelSelectionStore","removed","readEmptySnapshot","useSelectionStore","storeRef","useModel","declaration","store","requiresModels","models","declared","component","useResource","resource","subscribe","useCallback","useEffect","emptySlotProperties","defineSlot","id","createContributionTarget","defineSwitchSlot","declarationId","targets","switchSlot","route","existing","target","Slot","entries","useReadable","slotProps","createElement","Fragment","ContributionMount","contributionAuthority"],"mappings":"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"}
@@ -1,3 +1,6 @@
1
1
  import type { Readable } from '@opetope/core';
2
- export declare function useSelector<Value, Selected>(readable: Readable<Value>, selector: (value: Value) => Selected, isEqual?: (left: Selected, right: Selected) => boolean): Selected;
2
+ /** D279: the source and the selection are positional; `equals` is the one modifier, spelled as in Core. */
3
+ export declare function useSelector<Value, Selected>(readable: Readable<Value>, select: (value: NoInfer<Value>) => Selected, options?: {
4
+ readonly equals?: (left: NoInfer<Selected>, right: NoInfer<Selected>) => boolean;
5
+ }): Selected;
3
6
  export declare function useReadable<Value>(readable: Readable<Value>): Value;
@@ -1,6 +1,6 @@
1
1
  import type { FC } from 'react';
2
2
  import type { Application } from '@opetope/runtime';
3
- import type { OpenApplicationOptions, RuntimeGraphSnapshot } from '@opetope/runtime/internal';
3
+ import type { ApplicationConditionBinding, OpenApplicationOptions, RuntimeGraphSnapshot } from '@opetope/runtime/internal';
4
4
  import type { SlotComponentProperties, SlotProperties, SlotTarget } from './slot.js';
5
5
  type ScenarioFeatures = Application extends Application<infer Features> ? Features : never;
6
6
  interface ScenarioHostMount {
@@ -10,7 +10,7 @@ interface ScenarioHostMount {
10
10
  interface ScenarioHost<Mounted extends ScenarioHostMount> {
11
11
  mount(Component: FC): Mounted;
12
12
  }
13
- type ScenarioOptions<Features extends ScenarioFeatures, Mounted extends ScenarioHostMount> = OpenApplicationOptions<Features> & {
13
+ type ScenarioOptions<Features extends ScenarioFeatures, Mounted extends ScenarioHostMount, Bindings extends readonly ApplicationConditionBinding[] = readonly ApplicationConditionBinding[]> = OpenApplicationOptions<Features, Bindings> & {
14
14
  readonly activityCapacity?: number;
15
15
  readonly historyCapacity?: number;
16
16
  readonly host: ScenarioHost<Mounted>;
@@ -1,5 +1,6 @@
1
1
  import type { Application } from '@opetope/runtime';
2
+ import type { ApplicationConditionBinding } from '@opetope/runtime/internal';
2
3
  import type { Scenario, ScenarioFeatures, ScenarioHostMount, ScenarioOptions } from './scenario-types.js';
3
4
  /** D206: one real application, its existing inspection session and renderer-owned Slot ingress. */
4
- declare function createScenario<const Features extends ScenarioFeatures, Mounted extends ScenarioHostMount>(application: Application<Features>, options: ScenarioOptions<NoInfer<Features>, Mounted>): Scenario<Mounted>;
5
+ declare function createScenario<const Features extends ScenarioFeatures, Mounted extends ScenarioHostMount, const Bindings extends readonly ApplicationConditionBinding[] = readonly []>(application: Application<Features>, options: ScenarioOptions<NoInfer<Features>, Mounted, Bindings>): Scenario<Mounted>;
5
6
  export { createScenario };
package/dist/slot.d.ts CHANGED
@@ -25,12 +25,9 @@ type SlotRenderProps<Props extends SlotProperties> = [Props] extends [undefined]
25
25
  readonly props: SlotComponentProperties<Props>;
26
26
  readonly target: SlotTarget<Props>;
27
27
  };
28
- declare function defineSlot<Props extends SlotProperties = undefined>(options: {
29
- readonly id: string;
30
- }): SlotTarget<Props>;
31
- declare function defineSwitchSlot<Route extends string, Props extends SlotProperties = undefined>(options: {
32
- readonly id: string;
33
- }): SwitchSlotTarget<Route, Props>;
28
+ /** D280: a slot is a name, so it takes its id as the first argument. */
29
+ declare function defineSlot<Props extends SlotProperties = undefined>(id: string): SlotTarget<Props>;
30
+ declare function defineSwitchSlot<Route extends string, Props extends SlotProperties = undefined>(id: string): SwitchSlotTarget<Route, Props>;
34
31
  /**
35
32
  * Every contribution renders inside its own mount: the models its generation owns plus the UI models it declared,
36
33
  * created on mount and closed on unmount (D70, D85).
package/dist/testing.js CHANGED
@@ -1,4 +1,4 @@
1
- import{defineCallTarget as _,hasOwn as N,createAggregateError as q,invokeCallTarget as B}from"@opetope/core/internal";import{openApplication as H}from"@opetope/runtime";import{createInspectionSession as D}from"@opetope/runtime/internal";import{createElement as L}from"react";import{declarationId as U}from"@opetope/core";import{d as W,a as E,c as G,M as J}from"./contribution-frame-B1PnpOaP.js";function K(e){if(e.state.kind==="ready")return[];const t=e.state.kind==="waiting"?` (${e.state.reason})`:"";return[`Feature ${e.label}: ${e.state.kind}${t}.`]}function Q(e){if(e.lane===null)return`Call ${e.declaration}: ${e.state}; lane parallel; blockers none (parallel).`;const t=e.lane.blockedBy==="unknown"?"unknown":e.lane.blockedBy.join(", ");return`Call ${e.declaration}: ${e.state}; lane ${e.lane.declaration}; blockers ${t}.`}function V(e){if(e===void 0)return["Runtime activity: unknown."];const t=[];return(e.freshness==="stale"||e.truncated)&&t.push(`Activity: ${e.freshness}${e.truncated?", truncated":""}; completeness unknown.`),[...t,...e.features.map(n=>`Feature ${n.feature}: ${n.phase}; body ${n.body}; host demand unknown.`),...e.calls.map(Q),...e.resources.map(n=>`Resource ${n.id}: ${n.state}; retainers ${String(n.retainers)}.`)]}function X(e){const t=[...e.runtime.conditions.flatMap(n=>n.state.kind==="true"?[]:[`Condition ${n.label}: ${n.state.kind}.`]),...e.runtime.instances.flatMap(K),...V(e.activity)];return t.length===0?"No registered runtime blocker observed; external UI and host work are unknown.":t.join(`
2
- `)}class T extends Error{label;timeoutMs;snapshot;history;code="timeout";constructor(t,n,r,s){super(`Scenario timed out after ${String(n)}ms: ${t}.
3
- ${X(r)}`),this.label=t,this.timeoutMs=n,this.snapshot=r,this.history=s,this.name="ScenarioTimeoutError"}}function Y(e,t){return!t||e===void 0?!1:e.closed&&!e.truncated}function Z(e,t,n){if(e===void 0)return["Runtime activity is unavailable."];const r=[];return e.truncated&&r.push("The activity snapshot is truncated."),e.freshness==="stale"&&!t&&r.push("The activity snapshot is stale."),n||r.push("Successful application and mount cleanup has not been observed."),Object.freeze(r)}function ee(e,t){return e===void 0||e.truncated||e.freshness==="stale"&&!t?{calls:"unknown",features:"unknown",resources:"unknown"}:{calls:e.calls.length,features:e.features.length,resources:e.resources.length}}function te(e,t){const n=e.activity,r=Y(n,t);return Object.freeze({...ee(n,r),closed:t,reasons:Z(n,r,t),scope:"registered-runtime",status:r?"complete":"unknown"})}class ne{released;get closed(){return this.completion!==void 0}available=!1;completion;handle;reject;resolve;constructor(t){this.released=t}close=()=>this.completion!==void 0?this.completion:(this.completion=new Promise((t,n)=>{this.resolve=t,this.reject=n}),this.available&&this.releaseAvailable(),this.completion);provide(t){this.available=!0,this.handle=t,this.closed&&this.releaseAvailable()}finish(t,n){const r=this.resolve,s=this.reject;this.resolve=void 0,this.reject=void 0,this.released(t,n),t?s==null||s(n):r==null||r()}releaseAvailable(){const t=this.handle;this.handle=void 0,this.available=!1;try{Promise.resolve(t==null?void 0:t.unmount()).then(()=>this.finish(!1),n=>this.finish(!0,n))}catch(n){this.finish(!0,n)}}}const re=U("testing.contribution");let A=0;function oe(e){return A+=1,W(_({id:`testing.command.${String(A)}`,run:e}))}function I(e,t={}){const n=t.models??[];let r=t.props??{};const s=new Set,p={getSnapshot:()=>r,subscribe:a=>(s.add(a),()=>s.delete(a))},g=t.contribution,o=t.reporter,m=o===void 0?void 0:{models:[],reporter:o};return Object.freeze({close:()=>{r={},s.clear()},Slot:()=>{const a=E(e.entries),w=E(p);return(g===void 0?a.map(u=>[u.id,u.value,G(u)??m]):[[re,g,m]]).map(([u,f,S])=>L(J,{authority:S,contribution:f,contributionId:u,fixtures:n,key:u,slotProps:w,targetId:e.id}))},updateProps:a=>{r=a;for(const w of[...s])w()}})}function se(e,t={}){const n=I(e,t);return Object.freeze({Slot:n.Slot,updateProps:n.updateProps})}function M(e,t){if(!Number.isFinite(e)||e<=0||e>2147483647)throw new RangeError(`${t} must be a finite duration from 0 (exclusive) to 2147483647ms.`);return e}class ie{source;listeners=new Set;pending=new Set;constructor(t){this.source=t}cancel(){for(const t of[...this.pending])t(new Error("Scenario is closing."))}deadline(t,n,r=this.source.timeoutMs){return M(r,"Scenario timeoutMs"),new Promise((s,p)=>{const g=setTimeout(()=>p(this.timeout(n,r)),r);Promise.resolve(t).then(o=>{clearTimeout(g),s(o)},o=>{clearTimeout(g),p(o)})})}notify=()=>{for(const t of[...this.listeners])t()};waitFor(t,n){const r=M(n.timeoutMs??this.source.timeoutMs,"Scenario timeoutMs"),s=M(n.pollIntervalMs??10,"Scenario pollIntervalMs");return new Promise((p,g)=>{let o=!1,m=()=>{};const l=(f,S=f!==void 0)=>{o||(o=!0,m(),S?g(f):p())},a=()=>{if(!o)try{t(this.source.getSnapshot())&&l()}catch(f){l(f,!0)}},w=f=>l(f),y=setTimeout(()=>l(this.timeout(n.label??"waitFor",r)),r),u=setInterval(a,s);m=()=>{clearTimeout(y),clearInterval(u),this.listeners.delete(a),this.pending.delete(w)},this.listeners.add(a),this.pending.add(w),a()})}timeout(t,n){return new T(t,n,this.source.getSnapshot(),this.source.history())}}function O(e,t){if(!Number.isSafeInteger(e)||e<1||e>1e4)throw new RangeError(`${t} must be an integer from 1 to 10000.`);return e}function ae(e){if(e===null||typeof e!="object"||!("mount"in e)||typeof e.mount!="function")throw new TypeError("Scenario requires a host.mount(Component) adapter.")}function ce(e,t){const n=M(t.timeoutMs??1e3,"Scenario timeoutMs"),r=O(t.historyCapacity??64,"Scenario historyCapacity"),s=O(t.activityCapacity??256,"Scenario activityCapacity");let p=t.host;ae(p);const g={...t.cleanupFailure===void 0?{}:{cleanupFailure:t.cleanupFailure},...N(t,"conditions")?{conditions:t.conditions}:{},imports:t.imports};let o=H(e,g),m;try{m=D(o,{activityCapacity:s,ringCapacity:r})}catch(c){throw o.ready.catch(()=>{}),o.close().catch(()=>{}),c}let l="open",a=!1,w=!1;const y=[],u=new Set,f=[],S=()=>m.getSnapshot(),P=()=>Object.freeze([...y]),k=new ie({getSnapshot:S,history:P,timeoutMs:n}),j=()=>{if(!w){w=!0;try{const c=S(),h=y[y.length-1];((h==null?void 0:h.stateRevision)!==c.stateRevision||h.activity!==c.activity)&&(y.push(c),y.length>r&&y.shift())}finally{w=!1}k.notify()}},R=m.subscribe(j);j();const F=k.deadline(o.ready,"application ready");F.catch(()=>{});let C,v;const z=()=>{if(C!==void 0)return C;let c,h;C=new Promise((i,d)=>{c=i,h=d}),l="closing",k.cancel();const $=[];try{$.push(o.close())}catch(i){$.push(Promise.reject(i))}return $.push(...[...u].map(i=>i.close())),Promise.allSettled($).then(i=>{const d=[...new Set([...f,...i.flatMap(b=>b.status==="rejected"?[b.reason]:[])])];f.length=0,l="closed";try{j()}catch(b){d.push(b)}finally{R(),m.close(),u.clear(),o=void 0,p=void 0}a=d.length===0,d.length===0?c():h(q(d,"Scenario cleanup failed."))}),C};return Object.freeze({close:()=>{if(v!==void 0)return v;const c=z();return v!==void 0||(v=k.deadline(c,"scenario close"),v.catch(()=>{l==="closing"&&(v=void 0)})),v},getSnapshot:S,history:P,mount:(c,h={})=>{if(l!=="open")throw new Error("Scenario is closing.");const $=I(c,h),i=new ne((b,x)=>{u.delete(i),$.close(),b&&f.push(x)});u.add(i);let d;try{if(d=p.mount($.Slot),d===null||typeof d!="object"||typeof d.unmount!="function")throw new TypeError("Scenario host.mount must return an unmount() handle.");i.provide(d)}catch(b){throw i.provide(void 0),i.close().catch(()=>{}),b}return Object.freeze({host:d,unmount:i.close,updateProps:b=>{if(l!=="open"||i.closed)throw new Error("Scenario mount is closed.");$.updateProps(b),k.notify()}})},notify:()=>{l==="open"&&k.notify()},ownership:()=>te(S(),a),ready:F,waitFor:(c,h={})=>l!=="open"?Promise.reject(new Error("Scenario is closing.")):k.waitFor(c,h)})}async function le(e,t,n){return B(e,t,{signal:(n==null?void 0:n.signal)??new AbortController().signal})}export{T as ScenarioTimeoutError,oe as command,ce as createScenario,se as renderSlot,le as runCall};
1
+ import{defineCallTarget as _,hasOwn as F,createAggregateError as N,invokeCallTarget as q}from"@opetope/core/internal";import{openApplication as B}from"@opetope/runtime";import{createInspectionSession as H}from"@opetope/runtime/internal";import{createElement as D}from"react";import{declarationId as L}from"@opetope/core";import{d as U,a as E,c as W,M as G}from"./contribution-frame-DXl7K0-B.js";function J(e){if(e.state.kind==="ready")return[];const t=e.state.kind==="waiting"?` (${e.state.reason})`:"";return[`Feature ${e.label}: ${e.state.kind}${t}.`]}function K(e){if(e.lane===null)return`Call ${e.declaration}: ${e.state}; lane parallel; blockers none (parallel).`;const t=e.lane.blockedBy==="unknown"?"unknown":e.lane.blockedBy.join(", ");return`Call ${e.declaration}: ${e.state}; lane ${e.lane.declaration}; blockers ${t}.`}function Q(e){if(e===void 0)return["Runtime activity: unknown."];const t=[];return(e.freshness==="stale"||e.truncated)&&t.push(`Activity: ${e.freshness}${e.truncated?", truncated":""}; completeness unknown.`),[...t,...e.features.map(n=>`Feature ${n.feature}: ${n.phase}; body ${n.body}; host demand unknown.`),...e.calls.map(K),...e.resources.map(n=>`Resource ${n.id}: ${n.state}; retainers ${String(n.retainers)}.`)]}function V(e){const t=[...e.runtime.conditions.flatMap(n=>n.state.kind==="true"?[]:[`Condition ${n.label}: ${n.state.kind}.`]),...e.runtime.instances.flatMap(J),...Q(e.activity)];return t.length===0?"No registered runtime blocker observed; external UI and host work are unknown.":t.join(`
2
+ `)}class T extends Error{label;timeoutMs;snapshot;history;code="timeout";constructor(t,n,r,o){super(`Scenario timed out after ${String(n)}ms: ${t}.
3
+ ${V(r)}`),this.label=t,this.timeoutMs=n,this.snapshot=r,this.history=o,this.name="ScenarioTimeoutError"}}function X(e,t){return!t||e===void 0?!1:e.closed&&!e.truncated}function Y(e,t,n){if(e===void 0)return["Runtime activity is unavailable."];const r=[];return e.truncated&&r.push("The activity snapshot is truncated."),e.freshness==="stale"&&!t&&r.push("The activity snapshot is stale."),n||r.push("Successful application and mount cleanup has not been observed."),Object.freeze(r)}function Z(e,t){return e===void 0||e.truncated||e.freshness==="stale"&&!t?{calls:"unknown",features:"unknown",resources:"unknown"}:{calls:e.calls.length,features:e.features.length,resources:e.resources.length}}function ee(e,t){const n=e.activity,r=X(n,t);return Object.freeze({...Z(n,r),closed:t,reasons:Y(n,r,t),scope:"registered-runtime",status:r?"complete":"unknown"})}class te{released;get closed(){return this.completion!==void 0}available=!1;completion;handle;reject;resolve;constructor(t){this.released=t}close=()=>this.completion!==void 0?this.completion:(this.completion=new Promise((t,n)=>{this.resolve=t,this.reject=n}),this.available&&this.releaseAvailable(),this.completion);provide(t){this.available=!0,this.handle=t,this.closed&&this.releaseAvailable()}finish(t,n){const r=this.resolve,o=this.reject;this.resolve=void 0,this.reject=void 0,this.released(t,n),t?o==null||o(n):r==null||r()}releaseAvailable(){const t=this.handle;this.handle=void 0,this.available=!1;try{Promise.resolve(t==null?void 0:t.unmount()).then(()=>this.finish(!1),n=>this.finish(!0,n))}catch(n){this.finish(!0,n)}}}const ne=L("testing.contribution");let O=0;function re(e){return O+=1,U(_({id:`testing.command.${String(O)}`,run:e}))}function A(e,t={}){const n=t.models??[];let r=t.props??{};const o=new Set,w={getSnapshot:()=>r,subscribe:i=>(o.add(i),()=>o.delete(i))},c=t.contribution,s=t.reporter,l=s===void 0?void 0:{models:[],reporter:s};return Object.freeze({close:()=>{r={},o.clear()},Slot:()=>{const i=E(e.entries),d=E(w);return(c===void 0?i.map(p=>[p.id,p.value,W(p)??l]):[[ne,c,l]]).map(([p,f,k])=>D(G,{authority:k,contribution:f,contributionId:p,fixtures:n,key:p,slotProps:d,targetId:e.id}))},updateProps:i=>{r=i;for(const d of[...o])d()}})}function oe(e,t={}){const n=A(e,t);return Object.freeze({Slot:n.Slot,updateProps:n.updateProps})}function M(e,t){if(!Number.isFinite(e)||e<=0||e>2147483647)throw new RangeError(`${t} must be a finite duration from 0 (exclusive) to 2147483647ms.`);return e}class se{source;listeners=new Set;pending=new Set;constructor(t){this.source=t}cancel(){for(const t of[...this.pending])t(new Error("Scenario is closing."))}deadline(t,n,r=this.source.timeoutMs){return M(r,"Scenario timeoutMs"),new Promise((o,w)=>{const c=setTimeout(()=>w(this.timeout(n,r)),r);Promise.resolve(t).then(s=>{clearTimeout(c),o(s)},s=>{clearTimeout(c),w(s)})})}notify=()=>{for(const t of[...this.listeners])t()};waitFor(t,n){const r=M(n.timeoutMs??this.source.timeoutMs,"Scenario timeoutMs"),o=M(n.pollIntervalMs??10,"Scenario pollIntervalMs");return new Promise((w,c)=>{let s=!1,l=()=>{};const g=(f,k=f!==void 0)=>{s||(s=!0,l(),k?c(f):w())},i=()=>{if(!s)try{t(this.source.getSnapshot())&&g()}catch(f){g(f,!0)}},d=f=>g(f),v=setTimeout(()=>g(this.timeout(n.label??"waitFor",r)),r),p=setInterval(i,o);l=()=>{clearTimeout(v),clearInterval(p),this.listeners.delete(i),this.pending.delete(d)},this.listeners.add(i),this.pending.add(d),i()})}timeout(t,n){return new T(t,n,this.source.getSnapshot(),this.source.history())}}function I(e,t){if(!Number.isSafeInteger(e)||e<1||e>1e4)throw new RangeError(`${t} must be an integer from 1 to 10000.`);return e}function ie(e){if(e===null||typeof e!="object"||!("mount"in e)||typeof e.mount!="function")throw new TypeError("Scenario requires a host.mount(Component) adapter.")}function ae(e){return{...e.cleanupFailure===void 0?{}:{cleanupFailure:e.cleanupFailure},...F(e,"conditions")?{conditions:e.conditions}:{},...F(e,"imports")?{imports:e.imports}:{},reporter:e.reporter}}function ce(e,t){const n=M(t.timeoutMs??1e3,"Scenario timeoutMs"),r=I(t.historyCapacity??64,"Scenario historyCapacity"),o=I(t.activityCapacity??256,"Scenario activityCapacity");let w=t.host;ie(w);let c=B(e,ae(t)),s;try{s=H(c,{activityCapacity:o,ringCapacity:r})}catch(u){throw c.ready.catch(()=>{}),c.close().catch(()=>{}),u}let l="open",g=!1,i=!1;const d=[],v=new Set,p=[],f=()=>s.getSnapshot(),k=()=>Object.freeze([...d]),$=new se({getSnapshot:f,history:k,timeoutMs:n}),j=()=>{if(!i){i=!0;try{const u=f(),m=d[d.length-1];((m==null?void 0:m.stateRevision)!==u.stateRevision||m.activity!==u.activity)&&(d.push(u),d.length>r&&d.shift())}finally{i=!1}$.notify()}},R=s.subscribe(j);j();const P=$.deadline(c.ready,"application ready");P.catch(()=>{});let C,y;const z=()=>{if(C!==void 0)return C;let u,m;C=new Promise((a,h)=>{u=a,m=h}),l="closing",$.cancel();const S=[];try{S.push(c.close())}catch(a){S.push(Promise.reject(a))}return S.push(...[...v].map(a=>a.close())),Promise.allSettled(S).then(a=>{const h=[...new Set([...p,...a.flatMap(b=>b.status==="rejected"?[b.reason]:[])])];p.length=0,l="closed";try{j()}catch(b){h.push(b)}finally{R(),s.close(),v.clear(),c=void 0,w=void 0}g=h.length===0,h.length===0?u():m(N(h,"Scenario cleanup failed."))}),C};return Object.freeze({close:()=>{if(y!==void 0)return y;const u=z();return y!==void 0||(y=$.deadline(u,"scenario close"),y.catch(()=>{l==="closing"&&(y=void 0)})),y},getSnapshot:f,history:k,mount:(u,m={})=>{if(l!=="open")throw new Error("Scenario is closing.");const S=A(u,m),a=new te((b,x)=>{v.delete(a),S.close(),b&&p.push(x)});v.add(a);let h;try{if(h=w.mount(S.Slot),h===null||typeof h!="object"||typeof h.unmount!="function")throw new TypeError("Scenario host.mount must return an unmount() handle.");a.provide(h)}catch(b){throw a.provide(void 0),a.close().catch(()=>{}),b}return Object.freeze({host:h,unmount:a.close,updateProps:b=>{if(l!=="open"||a.closed)throw new Error("Scenario mount is closed.");S.updateProps(b),$.notify()}})},notify:()=>{l==="open"&&$.notify()},ownership:()=>ee(f(),g),ready:P,waitFor:(u,m={})=>l!=="open"?Promise.reject(new Error("Scenario is closing.")):$.waitFor(u,m)})}async function ue(e,t,n){return q(e,t,{signal:(n==null?void 0:n.signal)??new AbortController().signal})}export{T as ScenarioTimeoutError,re as command,ce as createScenario,oe as renderSlot,ue as runCall};
4
4
  //# sourceMappingURL=testing.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"testing.js","sources":["../src/scenario-diagnostics.ts","../src/scenario-mount.ts","../src/scenario-slot.tsx","../src/scenario-wait.ts","../src/scenario.ts","../src/testing-call.ts"],"sourcesContent":["import type { RuntimeActivitySnapshot, RuntimeGraphSnapshot } from '@opetope/runtime/internal';\n\nimport type { ScenarioOwnership } from './scenario-types';\n\nfunction instanceFacts(instance: RuntimeGraphSnapshot['runtime']['instances'][number]): readonly string[] {\n if (instance.state.kind === 'ready') return [];\n\n const waiting = instance.state.kind === 'waiting' ? ` (${instance.state.reason})` : '';\n\n return [`Feature ${instance.label}: ${instance.state.kind}${waiting}.`];\n}\n\nfunction callFact(call: RuntimeActivitySnapshot['calls'][number]): string {\n if (call.lane === null) return `Call ${call.declaration}: ${call.state}; lane parallel; blockers none (parallel).`;\n\n const blockers = call.lane.blockedBy === 'unknown' ? 'unknown' : call.lane.blockedBy.join(', ');\n\n return `Call ${call.declaration}: ${call.state}; lane ${call.lane.declaration}; blockers ${blockers}.`;\n}\n\nfunction activityFacts(activity: RuntimeActivitySnapshot | undefined): readonly string[] {\n if (activity === undefined) return ['Runtime activity: unknown.'];\n\n const facts: string[] = [];\n\n if (activity.freshness === 'stale' || activity.truncated) {\n facts.push(`Activity: ${activity.freshness}${activity.truncated ? ', truncated' : ''}; completeness unknown.`);\n }\n\n return [\n ...facts,\n ...activity.features.map(\n feature => `Feature ${feature.feature}: ${feature.phase}; body ${feature.body}; host demand unknown.`,\n ),\n ...activity.calls.map(callFact),\n ...activity.resources.map(\n resource => `Resource ${resource.id}: ${resource.state}; retainers ${String(resource.retainers)}.`,\n ),\n ];\n}\n\nfunction explainSnapshot(snapshot: RuntimeGraphSnapshot): string {\n const facts = [\n ...snapshot.runtime.conditions.flatMap(condition =>\n condition.state.kind === 'true' ? [] : [`Condition ${condition.label}: ${condition.state.kind}.`],\n ),\n ...snapshot.runtime.instances.flatMap(instanceFacts),\n ...activityFacts(snapshot.activity),\n ];\n\n return facts.length === 0\n ? 'No registered runtime blocker observed; external UI and host work are unknown.'\n : facts.join('\\n');\n}\n\n/** D206: the failure carries the producer's same data-only graph/activity facts, without product payloads. */\nclass ScenarioTimeoutError extends Error {\n readonly code = 'timeout';\n\n constructor(\n readonly label: string,\n readonly timeoutMs: number,\n readonly snapshot: RuntimeGraphSnapshot,\n readonly history: readonly RuntimeGraphSnapshot[],\n ) {\n super(`Scenario timed out after ${String(timeoutMs)}ms: ${label}.\\n${explainSnapshot(snapshot)}`);\n this.name = 'ScenarioTimeoutError';\n }\n}\n\nfunction ownershipComplete(activity: RuntimeActivitySnapshot | undefined, successfullyClosed: boolean): boolean {\n if (!successfullyClosed || activity === undefined) return false;\n\n return activity.closed && !activity.truncated;\n}\n\nfunction ownershipReasons(\n activity: RuntimeActivitySnapshot | undefined,\n complete: boolean,\n successfullyClosed: boolean,\n): readonly string[] {\n if (activity === undefined) return ['Runtime activity is unavailable.'];\n\n const reasons: string[] = [];\n\n if (activity.truncated) reasons.push('The activity snapshot is truncated.');\n\n if (activity.freshness === 'stale' && !complete) reasons.push('The activity snapshot is stale.');\n\n if (!successfullyClosed) reasons.push('Successful application and mount cleanup has not been observed.');\n\n return Object.freeze(reasons);\n}\n\nfunction ownershipCounts(\n activity: RuntimeActivitySnapshot | undefined,\n complete: boolean,\n): Pick<ScenarioOwnership, 'calls' | 'features' | 'resources'> {\n if (activity === undefined || activity.truncated || (activity.freshness === 'stale' && !complete)) {\n return { calls: 'unknown', features: 'unknown', resources: 'unknown' };\n }\n\n return { calls: activity.calls.length, features: activity.features.length, resources: activity.resources.length };\n}\n\nfunction scenarioOwnership(snapshot: RuntimeGraphSnapshot, successfullyClosed: boolean): ScenarioOwnership {\n const activity = snapshot.activity;\n const complete = ownershipComplete(activity, successfullyClosed);\n\n return Object.freeze({\n ...ownershipCounts(activity, complete),\n closed: successfullyClosed,\n reasons: ownershipReasons(activity, complete, successfullyClosed),\n scope: 'registered-runtime',\n status: complete ? 'complete' : 'unknown',\n });\n}\n\nexport { scenarioOwnership, ScenarioTimeoutError };\n","import type { ScenarioHostMount } from './scenario-types';\n\n/** A reservation exists before the host callback, so reentrant close also owns a mount returned later. */\nclass ScenarioMountLease<Mounted extends ScenarioHostMount> {\n get closed(): boolean {\n return this.completion !== undefined;\n }\n private available = false;\n private completion: Promise<void> | undefined;\n private handle: Mounted | undefined;\n private reject: ((error: unknown) => void) | undefined;\n\n private resolve: (() => void) | undefined;\n\n constructor(private readonly released: (failed: boolean, error?: unknown) => void) {}\n\n readonly close = (): Promise<void> => {\n if (this.completion !== undefined) return this.completion;\n\n this.completion = new Promise<void>((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n\n if (this.available) this.releaseAvailable();\n\n return this.completion;\n };\n\n provide(handle: Mounted | undefined): void {\n this.available = true;\n this.handle = handle;\n\n if (this.closed) this.releaseAvailable();\n }\n\n private finish(failed: boolean, error?: unknown): void {\n const resolve = this.resolve;\n const reject = this.reject;\n this.resolve = undefined;\n this.reject = undefined;\n this.released(failed, error);\n\n if (!failed) resolve?.();\n else reject?.(error);\n }\n\n private releaseAvailable(): void {\n const handle = this.handle;\n this.handle = undefined;\n this.available = false;\n try {\n Promise.resolve(handle?.unmount()).then(\n () => this.finish(false),\n (error: unknown) => this.finish(true, error),\n );\n } catch (error) {\n this.finish(true, error);\n }\n }\n}\n\nexport { ScenarioMountLease };\n","import { createElement } from 'react';\nimport type { FC, ReactNode } from 'react';\n\nimport { declarationId } from '@opetope/core';\nimport type { Call } from '@opetope/core';\nimport { defineCallTarget } from '@opetope/core/internal';\n\nimport { bindCommand } from './command';\nimport { contributionAuthority, ContributionMount } from './contribution-frame';\nimport type { ContributionValue, ModelFixture } from './contribution-frame';\nimport { useReadable } from './readable-hooks';\nimport type { SlotComponentProperties, SlotContribution, SlotProperties, SlotTarget } from './slot';\n\n/**\n * Two forms, as `renderRoot` had (D19): without `contribution` the harness mounts what the target published, with it\n * the harness mounts one fixture contribution so a component test needs no generation.\n */\ntype SlotTestFixture<Props extends SlotProperties> = {\n readonly contribution?: SlotContribution<Props>;\n readonly models?: readonly ModelFixture[];\n readonly props?: SlotComponentProperties<Props>;\n /**\n * Where a contained mount failure is reported (D256). A component test has no feature to report to, so without it\n * the failure is detached and the assertion has nothing to read.\n */\n readonly reporter?: (error: unknown) => void;\n};\n\ninterface SlotTestHarness<Props extends SlotProperties> {\n readonly Slot: FC;\n readonly updateProps: (props: SlotComponentProperties<Props>) => void;\n}\n\nconst testingContributionId = declarationId('testing.contribution');\n\nlet fixtureSequence = 0;\n\n/** A command a fixture publishes: the test writes the body, the harness gives it the identity a model requires. */\nfunction command<Input, Output>(run: (input: Input) => Output | PromiseLike<Output>): Call<Input, Output> {\n fixtureSequence += 1;\n\n return bindCommand(defineCallTarget<Input, Output>({ id: `testing.command.${String(fixtureSequence)}`, run }));\n}\n\n/**\n * Mounts the contributions of one slot the way a host does, with optional model fixtures for a component test. D19 and D85: the same\n * ContributionMount is shared by component tests and the application scenario harness (D206).\n */\nfunction createSlotHarness<Props extends SlotProperties>(\n target: SlotTarget<Props>,\n fixture: SlotTestFixture<Props> = {},\n): SlotTestHarness<Props> & { readonly close: () => void } {\n const fixtures = fixture.models ?? [];\n let current: object = fixture.props ?? {};\n const listeners = new Set<() => void>();\n const props = {\n getSnapshot: (): object => current,\n subscribe: (listener: () => void): (() => void) => {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n };\n\n const supplied = fixture.contribution;\n const reported = fixture.reporter;\n const fixtureAuthority = reported === undefined ? undefined : { models: [], reporter: reported };\n const Mounted: FC = (): ReactNode => {\n const published = useReadable(target.entries);\n const slotProps = useReadable(props);\n const entries =\n supplied === undefined\n ? published.map(\n entry =>\n [entry.id, entry.value as ContributionValue, contributionAuthority(entry) ?? fixtureAuthority] as const,\n )\n : [[testingContributionId, supplied as unknown as ContributionValue, fixtureAuthority] as const];\n\n return entries.map(([contributionId, contribution, authority]) =>\n createElement(ContributionMount, {\n authority,\n contribution,\n contributionId,\n fixtures,\n key: contributionId,\n slotProps,\n targetId: target.id,\n }),\n );\n };\n\n return Object.freeze({\n close: (): void => {\n current = {};\n listeners.clear();\n },\n Slot: Mounted,\n updateProps: (next: SlotComponentProperties<Props>): void => {\n current = next;\n\n for (const listener of [...listeners]) listener();\n },\n });\n}\n\nfunction renderSlot<Props extends SlotProperties>(\n target: SlotTarget<Props>,\n fixture: SlotTestFixture<Props> = {},\n): SlotTestHarness<Props> {\n const harness = createSlotHarness(target, fixture);\n\n return Object.freeze({ Slot: harness.Slot, updateProps: harness.updateProps });\n}\n\nexport { command, createSlotHarness, renderSlot };\nexport type { SlotTestFixture, SlotTestHarness };\n","import type { RuntimeGraphSnapshot } from '@opetope/runtime/internal';\n\nimport { ScenarioTimeoutError } from './scenario-diagnostics';\nimport type { ScenarioWaitOptions } from './scenario-types';\n\nfunction positiveDuration(value: number, label: string): number {\n if (!Number.isFinite(value) || value <= 0 || value > 2_147_483_647) {\n throw new RangeError(`${label} must be a finite duration from 0 (exclusive) to 2147483647ms.`);\n }\n\n return value;\n}\n\ninterface ScenarioWaitSource {\n readonly getSnapshot: () => RuntimeGraphSnapshot;\n readonly history: () => readonly RuntimeGraphSnapshot[];\n readonly timeoutMs: number;\n}\n\nclass ScenarioWaiters {\n private readonly listeners = new Set<() => void>();\n private readonly pending = new Set<(error: Error) => void>();\n\n constructor(private readonly source: ScenarioWaitSource) {}\n\n cancel(): void {\n for (const reject of [...this.pending]) reject(new Error('Scenario is closing.'));\n }\n\n deadline<Value>(result: PromiseLike<Value>, label: string, timeoutMs = this.source.timeoutMs): Promise<Value> {\n positiveDuration(timeoutMs, 'Scenario timeoutMs');\n\n return new Promise<Value>((resolve, reject) => {\n const timer = setTimeout(() => reject(this.timeout(label, timeoutMs)), timeoutMs);\n Promise.resolve(result).then(\n value => {\n clearTimeout(timer);\n resolve(value);\n },\n (error: unknown) => {\n clearTimeout(timer);\n reject(error);\n },\n );\n });\n }\n\n readonly notify = (): void => {\n for (const listener of [...this.listeners]) listener();\n };\n\n waitFor(predicate: (snapshot: RuntimeGraphSnapshot) => boolean, options: ScenarioWaitOptions): Promise<void> {\n const timeoutMs = positiveDuration(options.timeoutMs ?? this.source.timeoutMs, 'Scenario timeoutMs');\n const pollIntervalMs = positiveDuration(options.pollIntervalMs ?? 10, 'Scenario pollIntervalMs');\n\n return new Promise<void>((resolve, reject) => {\n let done = false;\n let dispose = (): void => undefined;\n const finish = (error?: unknown, failed = error !== undefined): void => {\n if (done) return;\n\n done = true;\n dispose();\n\n if (!failed) resolve();\n else reject(error);\n };\n const check = (): void => {\n if (done) return;\n\n try {\n if (predicate(this.source.getSnapshot())) finish();\n } catch (error) {\n finish(error, true);\n }\n };\n const cancel = (error: Error): void => finish(error);\n const deadline = setTimeout(() => finish(this.timeout(options.label ?? 'waitFor', timeoutMs)), timeoutMs);\n const poll = setInterval(check, pollIntervalMs);\n dispose = (): void => {\n clearTimeout(deadline);\n clearInterval(poll);\n this.listeners.delete(check);\n this.pending.delete(cancel);\n };\n this.listeners.add(check);\n this.pending.add(cancel);\n check();\n });\n }\n\n private timeout(label: string, timeoutMs: number): ScenarioTimeoutError {\n return new ScenarioTimeoutError(label, timeoutMs, this.source.getSnapshot(), this.source.history());\n }\n}\n\nexport { positiveDuration, ScenarioWaiters };\n","import { createAggregateError, hasOwn } from '@opetope/core/internal';\nimport { openApplication } from '@opetope/runtime';\nimport type { Application, ApplicationExecution } from '@opetope/runtime';\nimport { createInspectionSession } from '@opetope/runtime/internal';\nimport type { OpenApplicationOptions, RuntimeGraphSnapshot, RuntimeInspectionSession } from '@opetope/runtime/internal';\n\nimport { scenarioOwnership } from './scenario-diagnostics';\nimport { ScenarioMountLease } from './scenario-mount';\nimport { createSlotHarness } from './scenario-slot';\nimport type {\n Scenario,\n ScenarioFeatures,\n ScenarioHost,\n ScenarioHostMount,\n ScenarioMount,\n ScenarioOptions,\n ScenarioWaitOptions,\n} from './scenario-types';\nimport { positiveDuration, ScenarioWaiters } from './scenario-wait';\nimport type { SlotComponentProperties, SlotProperties, SlotTarget } from './slot';\n\nfunction boundedCapacity(value: number, label: string): number {\n if (!Number.isSafeInteger(value) || value < 1 || value > 10_000) {\n throw new RangeError(`${label} must be an integer from 1 to 10000.`);\n }\n\n return value;\n}\n\nfunction validateHost(host: unknown): void {\n if (host === null || typeof host !== 'object' || !('mount' in host) || typeof host.mount !== 'function') {\n throw new TypeError('Scenario requires a host.mount(Component) adapter.');\n }\n}\n\n/** D206: one real application, its existing inspection session and renderer-owned Slot ingress. */\nfunction createScenario<const Features extends ScenarioFeatures, Mounted extends ScenarioHostMount>(\n application: Application<Features>,\n options: ScenarioOptions<NoInfer<Features>, Mounted>,\n): Scenario<Mounted> {\n const timeoutMs = positiveDuration(options.timeoutMs ?? 1_000, 'Scenario timeoutMs');\n const historyCapacity = boundedCapacity(options.historyCapacity ?? 64, 'Scenario historyCapacity');\n const activityCapacity = boundedCapacity(options.activityCapacity ?? 256, 'Scenario activityCapacity');\n let host: ScenarioHost<Mounted> | undefined = options.host;\n\n validateHost(host);\n\n // Preserve absence separately from an explicitly invalid value while projecting the condition-dependent type.\n const applicationOptions = {\n ...(options.cleanupFailure === undefined ? {} : { cleanupFailure: options.cleanupFailure }),\n ...(hasOwn(options, 'conditions') ? { conditions: options.conditions } : {}),\n imports: options.imports,\n } as OpenApplicationOptions<Features>;\n let execution: ApplicationExecution | undefined = openApplication(application, applicationOptions);\n let session: RuntimeInspectionSession;\n try {\n session = createInspectionSession(execution, { activityCapacity, ringCapacity: historyCapacity });\n } catch (error) {\n void execution.ready.catch(() => undefined);\n void execution.close().catch(() => undefined);\n throw error;\n }\n\n let phase: 'closed' | 'closing' | 'open' = 'open';\n let successfullyClosed = false;\n let capturing = false;\n const records: RuntimeGraphSnapshot[] = [];\n const mounts = new Set<ScenarioMountLease<Mounted>>();\n const mountFailures: unknown[] = [];\n const getSnapshot = (): RuntimeGraphSnapshot => session.getSnapshot();\n const history = (): readonly RuntimeGraphSnapshot[] => Object.freeze([...records]);\n const waiters = new ScenarioWaiters({ getSnapshot, history, timeoutMs });\n const capture = (): void => {\n if (capturing) return;\n\n capturing = true;\n try {\n const snapshot = getSnapshot();\n const previous = records[records.length - 1];\n\n if (previous?.stateRevision !== snapshot.stateRevision || previous.activity !== snapshot.activity) {\n records.push(snapshot);\n\n if (records.length > historyCapacity) records.shift();\n }\n } finally {\n capturing = false;\n }\n waiters.notify();\n };\n const release = session.subscribe(capture);\n capture();\n const ready = waiters.deadline(execution.ready, 'application ready');\n // A test may deliberately inspect loading without awaiting ready; rejection remains observable to that caller.\n void ready.catch(() => undefined);\n\n let physicalClose: Promise<void> | undefined;\n let closeAttempt: Promise<void> | undefined;\n const startClose = (): Promise<void> => {\n if (physicalClose !== undefined) return physicalClose;\n\n let resolve!: () => void;\n let reject!: (error: unknown) => void;\n physicalClose = new Promise<void>((accept, refuse) => {\n resolve = accept;\n reject = refuse;\n });\n phase = 'closing';\n waiters.cancel();\n const drains: Promise<void>[] = [];\n // Fence before any foreign unmount callback can reenter UI ingress; join all physical drains afterwards.\n try {\n drains.push(execution!.close());\n } catch (error) {\n drains.push(Promise.reject(error));\n }\n drains.push(...[...mounts].map(mount => mount.close()));\n void Promise.allSettled(drains).then(results => {\n const failures = [\n ...new Set([\n ...mountFailures,\n ...results.flatMap(result => (result.status === 'rejected' ? [result.reason as unknown] : [])),\n ]),\n ];\n mountFailures.length = 0;\n phase = 'closed';\n try {\n capture();\n } catch (error) {\n failures.push(error);\n } finally {\n release();\n session.close();\n mounts.clear();\n execution = undefined;\n host = undefined;\n }\n successfullyClosed = failures.length === 0;\n\n if (failures.length === 0) resolve();\n else reject(createAggregateError(failures, 'Scenario cleanup failed.'));\n });\n\n return physicalClose;\n };\n\n const close = (): Promise<void> => {\n if (closeAttempt !== undefined) return closeAttempt;\n\n const drain = startClose();\n\n // A nested close during unmount may have installed the same attempt already.\n if (closeAttempt !== undefined) return closeAttempt;\n\n closeAttempt = waiters.deadline(drain, 'scenario close');\n void closeAttempt.catch(() => {\n // A deadline cannot cancel physical work. A later close can await that same drain with a new deadline.\n if (phase === 'closing') closeAttempt = undefined;\n });\n\n return closeAttempt;\n };\n\n const mount: Scenario<Mounted>['mount'] = <Props extends SlotProperties>(\n target: SlotTarget<Props>,\n fixture: { readonly props?: SlotComponentProperties<Props> } = {},\n ): ScenarioMount<Props, Mounted> => {\n if (phase !== 'open') throw new Error('Scenario is closing.');\n\n const slot = createSlotHarness(target, fixture);\n const lease = new ScenarioMountLease<Mounted>((failed, error) => {\n mounts.delete(lease);\n slot.close();\n\n if (failed) mountFailures.push(error);\n });\n mounts.add(lease);\n let mounted: Mounted;\n try {\n mounted = host!.mount(slot.Slot);\n\n if (mounted === null || typeof mounted !== 'object' || typeof mounted.unmount !== 'function') {\n throw new TypeError('Scenario host.mount must return an unmount() handle.');\n }\n\n lease.provide(mounted);\n } catch (error) {\n lease.provide(undefined);\n void lease.close().catch(() => undefined);\n throw error;\n }\n\n return Object.freeze({\n host: mounted,\n unmount: lease.close,\n updateProps: (props: SlotComponentProperties<Props>): void => {\n if (phase !== 'open' || lease.closed) throw new Error('Scenario mount is closed.');\n\n slot.updateProps(props);\n waiters.notify();\n },\n });\n };\n\n return Object.freeze({\n close,\n getSnapshot,\n history,\n mount,\n notify: (): void => {\n if (phase === 'open') waiters.notify();\n },\n ownership: () => scenarioOwnership(getSnapshot(), successfullyClosed),\n ready,\n waitFor: (predicate: (snapshot: RuntimeGraphSnapshot) => boolean, waitOptions: ScenarioWaitOptions = {}) => {\n if (phase !== 'open') return Promise.reject(new Error('Scenario is closing.'));\n\n return waiters.waitFor(predicate, waitOptions);\n },\n });\n}\n\nexport { createScenario };\n","import type { Call } from '@opetope/core';\nimport type { CallInputArgs } from '@opetope/core/internal';\nimport { invokeCallTarget } from '@opetope/core/internal';\n\ninterface RunCallOptions {\n readonly signal?: AbortSignal;\n}\n\n/** Runs an authentic Call in a test; its existing owner still controls scheduling, retirement and cleanup. */\nfunction runCall<Input, Output>(\n target: Call<Input, Output>,\n input: NoInfer<Input>,\n options?: RunCallOptions,\n): Promise<Output>;\nfunction runCall<Input, Output>(\n target: Call<Input, Output>,\n ...args: [...CallInputArgs<Input>, options?: RunCallOptions]\n): Promise<Output>;\nasync function runCall<Input, Output>(\n target: Call<Input, Output>,\n input?: Input,\n options?: RunCallOptions,\n): Promise<Output> {\n return invokeCallTarget(target, input as Input, {\n signal: options?.signal ?? new AbortController().signal,\n });\n}\n\nexport { runCall };\nexport type { RunCallOptions };\n"],"names":["instanceFacts","instance","waiting","callFact","call","blockers","activityFacts","activity","facts","feature","resource","explainSnapshot","snapshot","condition","ScenarioTimeoutError","label","timeoutMs","history","ownershipComplete","successfullyClosed","ownershipReasons","complete","reasons","ownershipCounts","scenarioOwnership","ScenarioMountLease","released","resolve","reject","handle","failed","error","testingContributionId","declarationId","fixtureSequence","command","run","bindCommand","defineCallTarget","createSlotHarness","target","fixture","fixtures","current","listeners","props","listener","supplied","reported","fixtureAuthority","published","useReadable","slotProps","entry","contributionAuthority","contributionId","contribution","authority","createElement","ContributionMount","next","renderSlot","harness","positiveDuration","value","ScenarioWaiters","source","result","timer","predicate","options","pollIntervalMs","done","dispose","finish","check","cancel","deadline","poll","boundedCapacity","validateHost","host","createScenario","application","historyCapacity","activityCapacity","applicationOptions","hasOwn","execution","openApplication","session","createInspectionSession","phase","capturing","records","mounts","mountFailures","getSnapshot","waiters","capture","previous","release","ready","physicalClose","closeAttempt","startClose","accept","refuse","drains","mount","results","failures","createAggregateError","drain","slot","lease","mounted","waitOptions","runCall","input","invokeCallTarget"],"mappings":"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"}
1
+ {"version":3,"file":"testing.js","sources":["../src/scenario-diagnostics.ts","../src/scenario-mount.ts","../src/scenario-slot.tsx","../src/scenario-wait.ts","../src/scenario.ts","../src/testing-call.ts"],"sourcesContent":["import type { RuntimeActivitySnapshot, RuntimeGraphSnapshot } from '@opetope/runtime/internal';\n\nimport type { ScenarioOwnership } from './scenario-types';\n\nfunction instanceFacts(instance: RuntimeGraphSnapshot['runtime']['instances'][number]): readonly string[] {\n if (instance.state.kind === 'ready') return [];\n\n const waiting = instance.state.kind === 'waiting' ? ` (${instance.state.reason})` : '';\n\n return [`Feature ${instance.label}: ${instance.state.kind}${waiting}.`];\n}\n\nfunction callFact(call: RuntimeActivitySnapshot['calls'][number]): string {\n if (call.lane === null) return `Call ${call.declaration}: ${call.state}; lane parallel; blockers none (parallel).`;\n\n const blockers = call.lane.blockedBy === 'unknown' ? 'unknown' : call.lane.blockedBy.join(', ');\n\n return `Call ${call.declaration}: ${call.state}; lane ${call.lane.declaration}; blockers ${blockers}.`;\n}\n\nfunction activityFacts(activity: RuntimeActivitySnapshot | undefined): readonly string[] {\n if (activity === undefined) return ['Runtime activity: unknown.'];\n\n const facts: string[] = [];\n\n if (activity.freshness === 'stale' || activity.truncated) {\n facts.push(`Activity: ${activity.freshness}${activity.truncated ? ', truncated' : ''}; completeness unknown.`);\n }\n\n return [\n ...facts,\n ...activity.features.map(\n feature => `Feature ${feature.feature}: ${feature.phase}; body ${feature.body}; host demand unknown.`,\n ),\n ...activity.calls.map(callFact),\n ...activity.resources.map(\n resource => `Resource ${resource.id}: ${resource.state}; retainers ${String(resource.retainers)}.`,\n ),\n ];\n}\n\nfunction explainSnapshot(snapshot: RuntimeGraphSnapshot): string {\n const facts = [\n ...snapshot.runtime.conditions.flatMap(condition =>\n condition.state.kind === 'true' ? [] : [`Condition ${condition.label}: ${condition.state.kind}.`],\n ),\n ...snapshot.runtime.instances.flatMap(instanceFacts),\n ...activityFacts(snapshot.activity),\n ];\n\n return facts.length === 0\n ? 'No registered runtime blocker observed; external UI and host work are unknown.'\n : facts.join('\\n');\n}\n\n/** D206: the failure carries the producer's same data-only graph/activity facts, without product payloads. */\nclass ScenarioTimeoutError extends Error {\n readonly code = 'timeout';\n\n constructor(\n readonly label: string,\n readonly timeoutMs: number,\n readonly snapshot: RuntimeGraphSnapshot,\n readonly history: readonly RuntimeGraphSnapshot[],\n ) {\n super(`Scenario timed out after ${String(timeoutMs)}ms: ${label}.\\n${explainSnapshot(snapshot)}`);\n this.name = 'ScenarioTimeoutError';\n }\n}\n\nfunction ownershipComplete(activity: RuntimeActivitySnapshot | undefined, successfullyClosed: boolean): boolean {\n if (!successfullyClosed || activity === undefined) return false;\n\n return activity.closed && !activity.truncated;\n}\n\nfunction ownershipReasons(\n activity: RuntimeActivitySnapshot | undefined,\n complete: boolean,\n successfullyClosed: boolean,\n): readonly string[] {\n if (activity === undefined) return ['Runtime activity is unavailable.'];\n\n const reasons: string[] = [];\n\n if (activity.truncated) reasons.push('The activity snapshot is truncated.');\n\n if (activity.freshness === 'stale' && !complete) reasons.push('The activity snapshot is stale.');\n\n if (!successfullyClosed) reasons.push('Successful application and mount cleanup has not been observed.');\n\n return Object.freeze(reasons);\n}\n\nfunction ownershipCounts(\n activity: RuntimeActivitySnapshot | undefined,\n complete: boolean,\n): Pick<ScenarioOwnership, 'calls' | 'features' | 'resources'> {\n if (activity === undefined || activity.truncated || (activity.freshness === 'stale' && !complete)) {\n return { calls: 'unknown', features: 'unknown', resources: 'unknown' };\n }\n\n return { calls: activity.calls.length, features: activity.features.length, resources: activity.resources.length };\n}\n\nfunction scenarioOwnership(snapshot: RuntimeGraphSnapshot, successfullyClosed: boolean): ScenarioOwnership {\n const activity = snapshot.activity;\n const complete = ownershipComplete(activity, successfullyClosed);\n\n return Object.freeze({\n ...ownershipCounts(activity, complete),\n closed: successfullyClosed,\n reasons: ownershipReasons(activity, complete, successfullyClosed),\n scope: 'registered-runtime',\n status: complete ? 'complete' : 'unknown',\n });\n}\n\nexport { scenarioOwnership, ScenarioTimeoutError };\n","import type { ScenarioHostMount } from './scenario-types';\n\n/** A reservation exists before the host callback, so reentrant close also owns a mount returned later. */\nclass ScenarioMountLease<Mounted extends ScenarioHostMount> {\n get closed(): boolean {\n return this.completion !== undefined;\n }\n private available = false;\n private completion: Promise<void> | undefined;\n private handle: Mounted | undefined;\n private reject: ((error: unknown) => void) | undefined;\n\n private resolve: (() => void) | undefined;\n\n constructor(private readonly released: (failed: boolean, error?: unknown) => void) {}\n\n readonly close = (): Promise<void> => {\n if (this.completion !== undefined) return this.completion;\n\n this.completion = new Promise<void>((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n\n if (this.available) this.releaseAvailable();\n\n return this.completion;\n };\n\n provide(handle: Mounted | undefined): void {\n this.available = true;\n this.handle = handle;\n\n if (this.closed) this.releaseAvailable();\n }\n\n private finish(failed: boolean, error?: unknown): void {\n const resolve = this.resolve;\n const reject = this.reject;\n this.resolve = undefined;\n this.reject = undefined;\n this.released(failed, error);\n\n if (!failed) resolve?.();\n else reject?.(error);\n }\n\n private releaseAvailable(): void {\n const handle = this.handle;\n this.handle = undefined;\n this.available = false;\n try {\n Promise.resolve(handle?.unmount()).then(\n () => this.finish(false),\n (error: unknown) => this.finish(true, error),\n );\n } catch (error) {\n this.finish(true, error);\n }\n }\n}\n\nexport { ScenarioMountLease };\n","import { createElement } from 'react';\nimport type { FC, ReactNode } from 'react';\n\nimport { declarationId } from '@opetope/core';\nimport type { Call } from '@opetope/core';\nimport { defineCallTarget } from '@opetope/core/internal';\n\nimport { bindCommand } from './command';\nimport { contributionAuthority, ContributionMount } from './contribution-frame';\nimport type { ContributionValue, ModelFixture } from './contribution-frame';\nimport { useReadable } from './readable-hooks';\nimport type { SlotComponentProperties, SlotContribution, SlotProperties, SlotTarget } from './slot';\n\n/**\n * Two forms, as `renderRoot` had (D19): without `contribution` the harness mounts what the target published, with it\n * the harness mounts one fixture contribution so a component test needs no generation.\n */\ntype SlotTestFixture<Props extends SlotProperties> = {\n readonly contribution?: SlotContribution<Props>;\n readonly models?: readonly ModelFixture[];\n readonly props?: SlotComponentProperties<Props>;\n /**\n * Where a contained mount failure is reported (D256). A component test has no feature to report to, so without it\n * the failure is detached and the assertion has nothing to read.\n */\n readonly reporter?: (error: unknown) => void;\n};\n\ninterface SlotTestHarness<Props extends SlotProperties> {\n readonly Slot: FC;\n readonly updateProps: (props: SlotComponentProperties<Props>) => void;\n}\n\nconst testingContributionId = declarationId('testing.contribution');\n\nlet fixtureSequence = 0;\n\n/** A command a fixture publishes: the test writes the body, the harness gives it the identity a model requires. */\nfunction command<Input, Output>(run: (input: Input) => Output | PromiseLike<Output>): Call<Input, Output> {\n fixtureSequence += 1;\n\n return bindCommand(defineCallTarget<Input, Output>({ id: `testing.command.${String(fixtureSequence)}`, run }));\n}\n\n/**\n * Mounts the contributions of one slot the way a host does, with optional model fixtures for a component test. D19 and D85: the same\n * ContributionMount is shared by component tests and the application scenario harness (D206).\n */\nfunction createSlotHarness<Props extends SlotProperties>(\n target: SlotTarget<Props>,\n fixture: SlotTestFixture<Props> = {},\n): SlotTestHarness<Props> & { readonly close: () => void } {\n const fixtures = fixture.models ?? [];\n let current: object = fixture.props ?? {};\n const listeners = new Set<() => void>();\n const props = {\n getSnapshot: (): object => current,\n subscribe: (listener: () => void): (() => void) => {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n },\n };\n\n const supplied = fixture.contribution;\n const reported = fixture.reporter;\n const fixtureAuthority = reported === undefined ? undefined : { models: [], reporter: reported };\n const Mounted: FC = (): ReactNode => {\n const published = useReadable(target.entries);\n const slotProps = useReadable(props);\n const entries =\n supplied === undefined\n ? published.map(\n entry =>\n [entry.id, entry.value as ContributionValue, contributionAuthority(entry) ?? fixtureAuthority] as const,\n )\n : [[testingContributionId, supplied as unknown as ContributionValue, fixtureAuthority] as const];\n\n return entries.map(([contributionId, contribution, authority]) =>\n createElement(ContributionMount, {\n authority,\n contribution,\n contributionId,\n fixtures,\n key: contributionId,\n slotProps,\n targetId: target.id,\n }),\n );\n };\n\n return Object.freeze({\n close: (): void => {\n current = {};\n listeners.clear();\n },\n Slot: Mounted,\n updateProps: (next: SlotComponentProperties<Props>): void => {\n current = next;\n\n for (const listener of [...listeners]) listener();\n },\n });\n}\n\nfunction renderSlot<Props extends SlotProperties>(\n target: SlotTarget<Props>,\n fixture: SlotTestFixture<Props> = {},\n): SlotTestHarness<Props> {\n const harness = createSlotHarness(target, fixture);\n\n return Object.freeze({ Slot: harness.Slot, updateProps: harness.updateProps });\n}\n\nexport { command, createSlotHarness, renderSlot };\nexport type { SlotTestFixture, SlotTestHarness };\n","import type { RuntimeGraphSnapshot } from '@opetope/runtime/internal';\n\nimport { ScenarioTimeoutError } from './scenario-diagnostics';\nimport type { ScenarioWaitOptions } from './scenario-types';\n\nfunction positiveDuration(value: number, label: string): number {\n if (!Number.isFinite(value) || value <= 0 || value > 2_147_483_647) {\n throw new RangeError(`${label} must be a finite duration from 0 (exclusive) to 2147483647ms.`);\n }\n\n return value;\n}\n\ninterface ScenarioWaitSource {\n readonly getSnapshot: () => RuntimeGraphSnapshot;\n readonly history: () => readonly RuntimeGraphSnapshot[];\n readonly timeoutMs: number;\n}\n\nclass ScenarioWaiters {\n private readonly listeners = new Set<() => void>();\n private readonly pending = new Set<(error: Error) => void>();\n\n constructor(private readonly source: ScenarioWaitSource) {}\n\n cancel(): void {\n for (const reject of [...this.pending]) reject(new Error('Scenario is closing.'));\n }\n\n deadline<Value>(result: PromiseLike<Value>, label: string, timeoutMs = this.source.timeoutMs): Promise<Value> {\n positiveDuration(timeoutMs, 'Scenario timeoutMs');\n\n return new Promise<Value>((resolve, reject) => {\n const timer = setTimeout(() => reject(this.timeout(label, timeoutMs)), timeoutMs);\n Promise.resolve(result).then(\n value => {\n clearTimeout(timer);\n resolve(value);\n },\n (error: unknown) => {\n clearTimeout(timer);\n reject(error);\n },\n );\n });\n }\n\n readonly notify = (): void => {\n for (const listener of [...this.listeners]) listener();\n };\n\n waitFor(predicate: (snapshot: RuntimeGraphSnapshot) => boolean, options: ScenarioWaitOptions): Promise<void> {\n const timeoutMs = positiveDuration(options.timeoutMs ?? this.source.timeoutMs, 'Scenario timeoutMs');\n const pollIntervalMs = positiveDuration(options.pollIntervalMs ?? 10, 'Scenario pollIntervalMs');\n\n return new Promise<void>((resolve, reject) => {\n let done = false;\n let dispose = (): void => undefined;\n const finish = (error?: unknown, failed = error !== undefined): void => {\n if (done) return;\n\n done = true;\n dispose();\n\n if (!failed) resolve();\n else reject(error);\n };\n const check = (): void => {\n if (done) return;\n\n try {\n if (predicate(this.source.getSnapshot())) finish();\n } catch (error) {\n finish(error, true);\n }\n };\n const cancel = (error: Error): void => finish(error);\n const deadline = setTimeout(() => finish(this.timeout(options.label ?? 'waitFor', timeoutMs)), timeoutMs);\n const poll = setInterval(check, pollIntervalMs);\n dispose = (): void => {\n clearTimeout(deadline);\n clearInterval(poll);\n this.listeners.delete(check);\n this.pending.delete(cancel);\n };\n this.listeners.add(check);\n this.pending.add(cancel);\n check();\n });\n }\n\n private timeout(label: string, timeoutMs: number): ScenarioTimeoutError {\n return new ScenarioTimeoutError(label, timeoutMs, this.source.getSnapshot(), this.source.history());\n }\n}\n\nexport { positiveDuration, ScenarioWaiters };\n","import { createAggregateError, hasOwn } from '@opetope/core/internal';\nimport { openApplication } from '@opetope/runtime';\nimport type { Application, ApplicationExecution } from '@opetope/runtime';\nimport { createInspectionSession } from '@opetope/runtime/internal';\nimport type {\n ApplicationConditionBinding,\n OpenApplicationOptions,\n RuntimeGraphSnapshot,\n RuntimeInspectionSession,\n} from '@opetope/runtime/internal';\n\nimport { scenarioOwnership } from './scenario-diagnostics';\nimport { ScenarioMountLease } from './scenario-mount';\nimport { createSlotHarness } from './scenario-slot';\nimport type {\n Scenario,\n ScenarioFeatures,\n ScenarioHost,\n ScenarioHostMount,\n ScenarioMount,\n ScenarioOptions,\n ScenarioWaitOptions,\n} from './scenario-types';\nimport { positiveDuration, ScenarioWaiters } from './scenario-wait';\nimport type { SlotComponentProperties, SlotProperties, SlotTarget } from './slot';\n\nfunction boundedCapacity(value: number, label: string): number {\n if (!Number.isSafeInteger(value) || value < 1 || value > 10_000) {\n throw new RangeError(`${label} must be an integer from 1 to 10000.`);\n }\n\n return value;\n}\n\nfunction validateHost(host: unknown): void {\n if (host === null || typeof host !== 'object' || !('mount' in host) || typeof host.mount !== 'function') {\n throw new TypeError('Scenario requires a host.mount(Component) adapter.');\n }\n}\n\n/**\n * What the scenario hands to `openApplication`: absence is preserved separately from an explicitly invalid value,\n * so the condition-dependent type and the D281 omissions behave exactly as they do for a host.\n */\nfunction applicationOpenOptions<const Features extends ScenarioFeatures>(\n options: ScenarioOptions<Features, ScenarioHostMount>,\n): OpenApplicationOptions<Features> {\n return {\n ...(options.cleanupFailure === undefined ? {} : { cleanupFailure: options.cleanupFailure }),\n ...(hasOwn(options, 'conditions') ? { conditions: options.conditions } : {}),\n ...(hasOwn(options, 'imports') ? { imports: options.imports } : {}),\n reporter: options.reporter,\n };\n}\n\n/** D206: one real application, its existing inspection session and renderer-owned Slot ingress. */\nfunction createScenario<\n const Features extends ScenarioFeatures,\n Mounted extends ScenarioHostMount,\n const Bindings extends readonly ApplicationConditionBinding[] = readonly [],\n>(\n application: Application<Features>,\n options: ScenarioOptions<NoInfer<Features>, Mounted, Bindings>,\n): Scenario<Mounted> {\n const timeoutMs = positiveDuration(options.timeoutMs ?? 1_000, 'Scenario timeoutMs');\n const historyCapacity = boundedCapacity(options.historyCapacity ?? 64, 'Scenario historyCapacity');\n const activityCapacity = boundedCapacity(options.activityCapacity ?? 256, 'Scenario activityCapacity');\n let host: ScenarioHost<Mounted> | undefined = options.host;\n\n validateHost(host);\n\n let execution: ApplicationExecution | undefined = openApplication(application, applicationOpenOptions(options));\n let session: RuntimeInspectionSession;\n try {\n session = createInspectionSession(execution, { activityCapacity, ringCapacity: historyCapacity });\n } catch (error) {\n void execution.ready.catch(() => undefined);\n void execution.close().catch(() => undefined);\n throw error;\n }\n\n let phase: 'closed' | 'closing' | 'open' = 'open';\n let successfullyClosed = false;\n let capturing = false;\n const records: RuntimeGraphSnapshot[] = [];\n const mounts = new Set<ScenarioMountLease<Mounted>>();\n const mountFailures: unknown[] = [];\n const getSnapshot = (): RuntimeGraphSnapshot => session.getSnapshot();\n const history = (): readonly RuntimeGraphSnapshot[] => Object.freeze([...records]);\n const waiters = new ScenarioWaiters({ getSnapshot, history, timeoutMs });\n const capture = (): void => {\n if (capturing) return;\n\n capturing = true;\n try {\n const snapshot = getSnapshot();\n const previous = records[records.length - 1];\n\n if (previous?.stateRevision !== snapshot.stateRevision || previous.activity !== snapshot.activity) {\n records.push(snapshot);\n\n if (records.length > historyCapacity) records.shift();\n }\n } finally {\n capturing = false;\n }\n waiters.notify();\n };\n const release = session.subscribe(capture);\n capture();\n const ready = waiters.deadline(execution.ready, 'application ready');\n // A test may deliberately inspect loading without awaiting ready; rejection remains observable to that caller.\n void ready.catch(() => undefined);\n\n let physicalClose: Promise<void> | undefined;\n let closeAttempt: Promise<void> | undefined;\n const startClose = (): Promise<void> => {\n if (physicalClose !== undefined) return physicalClose;\n\n let resolve!: () => void;\n let reject!: (error: unknown) => void;\n physicalClose = new Promise<void>((accept, refuse) => {\n resolve = accept;\n reject = refuse;\n });\n phase = 'closing';\n waiters.cancel();\n const drains: Promise<void>[] = [];\n // Fence before any foreign unmount callback can reenter UI ingress; join all physical drains afterwards.\n try {\n drains.push(execution!.close());\n } catch (error) {\n drains.push(Promise.reject(error));\n }\n drains.push(...[...mounts].map(mount => mount.close()));\n void Promise.allSettled(drains).then(results => {\n const failures = [\n ...new Set([\n ...mountFailures,\n ...results.flatMap(result => (result.status === 'rejected' ? [result.reason as unknown] : [])),\n ]),\n ];\n mountFailures.length = 0;\n phase = 'closed';\n try {\n capture();\n } catch (error) {\n failures.push(error);\n } finally {\n release();\n session.close();\n mounts.clear();\n execution = undefined;\n host = undefined;\n }\n successfullyClosed = failures.length === 0;\n\n if (failures.length === 0) resolve();\n else reject(createAggregateError(failures, 'Scenario cleanup failed.'));\n });\n\n return physicalClose;\n };\n\n const close = (): Promise<void> => {\n if (closeAttempt !== undefined) return closeAttempt;\n\n const drain = startClose();\n\n // A nested close during unmount may have installed the same attempt already.\n if (closeAttempt !== undefined) return closeAttempt;\n\n closeAttempt = waiters.deadline(drain, 'scenario close');\n void closeAttempt.catch(() => {\n // A deadline cannot cancel physical work. A later close can await that same drain with a new deadline.\n if (phase === 'closing') closeAttempt = undefined;\n });\n\n return closeAttempt;\n };\n\n const mount: Scenario<Mounted>['mount'] = <Props extends SlotProperties>(\n target: SlotTarget<Props>,\n fixture: { readonly props?: SlotComponentProperties<Props> } = {},\n ): ScenarioMount<Props, Mounted> => {\n if (phase !== 'open') throw new Error('Scenario is closing.');\n\n const slot = createSlotHarness(target, fixture);\n const lease = new ScenarioMountLease<Mounted>((failed, error) => {\n mounts.delete(lease);\n slot.close();\n\n if (failed) mountFailures.push(error);\n });\n mounts.add(lease);\n let mounted: Mounted;\n try {\n mounted = host!.mount(slot.Slot);\n\n if (mounted === null || typeof mounted !== 'object' || typeof mounted.unmount !== 'function') {\n throw new TypeError('Scenario host.mount must return an unmount() handle.');\n }\n\n lease.provide(mounted);\n } catch (error) {\n lease.provide(undefined);\n void lease.close().catch(() => undefined);\n throw error;\n }\n\n return Object.freeze({\n host: mounted,\n unmount: lease.close,\n updateProps: (props: SlotComponentProperties<Props>): void => {\n if (phase !== 'open' || lease.closed) throw new Error('Scenario mount is closed.');\n\n slot.updateProps(props);\n waiters.notify();\n },\n });\n };\n\n return Object.freeze({\n close,\n getSnapshot,\n history,\n mount,\n notify: (): void => {\n if (phase === 'open') waiters.notify();\n },\n ownership: () => scenarioOwnership(getSnapshot(), successfullyClosed),\n ready,\n waitFor: (predicate: (snapshot: RuntimeGraphSnapshot) => boolean, waitOptions: ScenarioWaitOptions = {}) => {\n if (phase !== 'open') return Promise.reject(new Error('Scenario is closing.'));\n\n return waiters.waitFor(predicate, waitOptions);\n },\n });\n}\n\nexport { createScenario };\n","import type { Call } from '@opetope/core';\nimport type { CallInputArgs } from '@opetope/core/internal';\nimport { invokeCallTarget } from '@opetope/core/internal';\n\ninterface RunCallOptions {\n readonly signal?: AbortSignal;\n}\n\n/** Runs an authentic Call in a test; its existing owner still controls scheduling, retirement and cleanup. */\nfunction runCall<Input, Output>(\n target: Call<Input, Output>,\n input: NoInfer<Input>,\n options?: RunCallOptions,\n): Promise<Output>;\nfunction runCall<Input, Output>(\n target: Call<Input, Output>,\n ...args: [...CallInputArgs<Input>, options?: RunCallOptions]\n): Promise<Output>;\nasync function runCall<Input, Output>(\n target: Call<Input, Output>,\n input?: Input,\n options?: RunCallOptions,\n): Promise<Output> {\n return invokeCallTarget(target, input as Input, {\n signal: options?.signal ?? new AbortController().signal,\n });\n}\n\nexport { runCall };\nexport type { RunCallOptions };\n"],"names":["instanceFacts","instance","waiting","callFact","call","blockers","activityFacts","activity","facts","feature","resource","explainSnapshot","snapshot","condition","ScenarioTimeoutError","label","timeoutMs","history","ownershipComplete","successfullyClosed","ownershipReasons","complete","reasons","ownershipCounts","scenarioOwnership","ScenarioMountLease","released","resolve","reject","handle","failed","error","testingContributionId","declarationId","fixtureSequence","command","run","bindCommand","defineCallTarget","createSlotHarness","target","fixture","fixtures","current","listeners","props","listener","supplied","reported","fixtureAuthority","published","useReadable","slotProps","entry","contributionAuthority","contributionId","contribution","authority","createElement","ContributionMount","next","renderSlot","harness","positiveDuration","value","ScenarioWaiters","source","result","timer","predicate","options","pollIntervalMs","done","dispose","finish","check","cancel","deadline","poll","boundedCapacity","validateHost","host","applicationOpenOptions","hasOwn","createScenario","application","historyCapacity","activityCapacity","execution","openApplication","session","createInspectionSession","phase","capturing","records","mounts","mountFailures","getSnapshot","waiters","capture","previous","release","ready","physicalClose","closeAttempt","startClose","accept","refuse","drains","mount","results","failures","createAggregateError","drain","slot","lease","mounted","waitOptions","runCall","input","invokeCallTarget"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opetope/react",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "engines": {
5
5
  "node": ">=20.19.0"
6
6
  },
@@ -50,16 +50,16 @@
50
50
  }
51
51
  ],
52
52
  "devDependencies": {
53
- "@opetope/core": "0.7.0",
54
- "@opetope/runtime": "0.7.0",
53
+ "@opetope/core": "0.8.0",
54
+ "@opetope/runtime": "0.8.0",
55
55
  "@testing-library/react": "16.3.3",
56
56
  "@types/react": "19.2.18",
57
57
  "react": "19.2.8",
58
58
  "react-dom": "19.2.8"
59
59
  },
60
60
  "peerDependencies": {
61
- "@opetope/core": "0.7.0",
62
- "@opetope/runtime": "0.7.0",
61
+ "@opetope/core": "0.8.0",
62
+ "@opetope/runtime": "0.8.0",
63
63
  "react": ">=19.0.0 <20"
64
64
  },
65
65
  "sideEffects": false,
@@ -1,2 +0,0 @@
1
- import{jsx as m}from"react/jsx-runtime";import{useContext as B,createContext as E,useCallback as v,useRef as M,useMemo as b,useSyncExternalStore as y,useEffect as L,useInsertionEffect as R,useLayoutEffect as p,memo as T,useState as q}from"react";import{isCallTarget as D,isModel as V,isReadable as S,createLifetimeController as P,createCallController as W,reportDetachedError as g,getContributionOwner as K,createState as G,createAggregateError as H}from"@opetope/core/internal";import{reportRuntimeFailure as J,requireContributionModelPlans as N,openContributionModels as Q}from"@opetope/runtime/internal";import{C as u,u as U,b as X}from"./contribution-isolation-Bzfbt4iM.js";const O=E(void 0);function Y(){const t=B(O);if(t===void 0)throw new u("missing","Opetope hook was called outside a contribution mount.");return t}const Z=t=>t;function A(t,e,n=Object.is){const r=v(a=>t.subscribe(a),[t]),i=M(void 0);let o=i.current;o===void 0&&(o={hasValue:!1,value:void 0},i.current=o);const d=b(()=>{let a=!1,l,c;return()=>{const f=t.getSnapshot();if(a&&Object.is(l,f))return c;const h=e(f);if(!a){let w=h;return o.hasValue&&n(o.value,h)&&(w=o.value),a=!0,l=f,c=w,c}return n(c,h)?(l=f,c):(l=f,c=h,c)}},[o,n,t,e]),s=y(r,d,d);return L(()=>{o.hasValue=!0,o.value=s},[o,s]),s}function j(t){return A(t,Z)}const C=new WeakMap;function F(t){if(arguments.length>1)throw new u("binding-invalid","Command follows the target it binds; binding options are forbidden.");if(!D(t))throw new u("binding-invalid","Command target is not authentic.");return C.set(t,{target:t}),t}function _(t){const e=C.get(t);if(e===void 0)throw new u("binding-invalid","Command binding is not authentic.");return e}function k(t){return typeof t=="object"&&t!==null&&C.has(t)}const ee=Symbol("opetope.model-binding"),te=Symbol("opetope.model-binding-identity"),ne=Object.freeze({}),x=new WeakMap;function ie(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 re(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=ie(t,e,i);if(!(S(o)||typeof o=="function")){if(!k(o))throw new u("binding-invalid",`Model ${t.id}.${String(i)} requires a Readable or a bound command.`);n.push(o)}}return Object.freeze(n)}function z(t,e){if(!V(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=re(t,e),r=Object.freeze({[ee]:ne,[te]:!0});return x.set(r,{commands:n,declaration:t,model:e}),r}function oe(t){const e=x.get(t);if(e===void 0)throw new u("binding-invalid","Model binding is not authentic.");return e}let se=0;function ae(t){const e=new Map,n=[],r=new Set;for(const i of t){const o=oe(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 d of o.commands)r.has(d)||(r.add(d),n.push(d))}return{commands:Object.freeze(n),frame:{commandRecords:new Map,models:e}}}class ce{frame;authority;boundCommands;closeTicket=0;isActive;lifetime;onClose;released=!1;rendererAttached=!1;renderListeners=new Set;state="detached";constructor(e,n){const r=++se;this.lifetime=P({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}=ae(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){_(r);const i=W(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){g(n)}}releaseFrame(){this.released||(this.released=!0,this.frame.commandRecords.clear(),this.frame.models.clear(),this.boundCommands=[],this.renderListeners.clear())}}function de({children:t,mount:e}){const n=y(e.subscribeRenderable,e.getRenderableSnapshot,e.getRenderableSnapshot);return R(()=>{if(e.attach())return()=>e.detach()},[e]),p(()=>{n||e.releaseClosedFrame()},[e,n]),n?m(O,{value:e.frame,children:t}):null}function $(t){const e=t.bundle;t.bundle=void 0,e==null||e.frame.close()}function ue(t,e){if(typeof e!="object"||e===null)return z(t,e);const n=Object.entries(e).map(([r,i])=>[r,S(i)||typeof i=="function"||k(i)?i:F(i)]);return z(t,Object.fromEntries(n))}function le(t){return K(t)}const fe={models:[]};function I(t,e){try{t()}catch(n){throw H([e,n],"Contribution mount setup and cleanup failed.",{cause:e})}throw e}function he(t,e,n,r){const i=G(e),{models:o,reporter:d}=r??fe;try{const s=N(t.models),a=s.length===0?void 0:Q(s,i,d);return{bundle:a,models:[...o,...(a==null?void 0:a.models)??[],...n],props:i}}catch(s){return I(i.close,s)}}function me(t,e,n,r){const{bundle:i,models:o,props:d}=he(t,e,n,r);let s=!1;const a=()=>{if(!s){s=!0;try{d.close()}finally{i==null||i.close().catch(g)}}};try{return{frame:new ce(o.map(([c,f])=>ue(c,f)),{isActive:()=>!s,onClose:a}),isOpen:()=>!s,props:d}}catch(l){return I(a,l)}}function be(t,e){return m(t,{...e})}function pe({bundle:t,contribution:e}){const n=j(t.props),r=b(()=>e.props===void 0?n:e.props(n),[n,e]);return m(de,{mount:t.frame,children:be(e.Component,r)})}function ge({authority:t,contribution:e,fixtures:n,slotProps:r}){const i=b(()=>({authority:t,bundle:void 0,contribution:e,fixtures:n,retired:!1}),[t,e,n]),[o,d]=q(void 0),s=(o==null?void 0:o.owner)===i?o.bundle:void 0,a=M(r);return p(()=>{a.current=r}),R(()=>()=>{i.retired=!0,queueMicrotask(()=>{try{$(i)}catch(l){g(l)}})},[i]),p(()=>{const l=i.bundle??(i.bundle=me(i.contribution,a.current,i.fixtures??[],i.authority));return d(c=>(c==null?void 0:c.owner)===i?c:{bundle:l,owner:i}),()=>{i.retired&&$(i)}},[i]),p(()=>{(s==null?void 0:s.isOpen())===!0&&s.props.set(r)},[s,r]),s===void 0?null:m(pe,{bundle:s,contribution:e})}function Ce(t){const{authority:e,contribution:n,contributionId:r,fixtures:i,targetId:o}=t,d=U(),s=b(()=>({authority:e,contribution:n,fixtures:i}),[e,n,i]),a=e==null?void 0:e.feature,l=b(()=>({contribution:r,feature:a,target:o}),[r,a,o]),c=e==null?void 0:e.reporter,f=v(h=>J(h,c),[c]);return m(X,{content:d,identity:s,report:f,source:l,children:m(ge,{...t})})}const we=T(Ce);export{we as M,j as a,A as b,le as c,F as d,Y as u};
2
- //# sourceMappingURL=contribution-frame-B1PnpOaP.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"contribution-frame-B1PnpOaP.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\nexport function useSelector<Value, Selected>(\n readable: Readable<Value>,\n selector: (value: Value) => Selected,\n isEqual: (left: Selected, right: Selected) => boolean = Object.is,\n): Selected {\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 as Selected, 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","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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"}