@opetope/react 0.4.0 → 0.5.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,19 @@
1
1
  # @opetope/react
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 410f2f0: Run authentic Calls in headless model tests with `runCall` from `@opetope/react/testing`, preserving their existing policies, cancellation, errors and owner lifetime.
8
+
9
+ Add the recommended `opetope/no-internal-imports` rule for static imports, re-exports, literal dynamic imports, unshadowed `require` and TypeScript import types. `configs.internalImports` uses this rule for JavaScript and TypeScript, including tests, without replacing the host's `no-restricted-imports`. The redundant `internalImportPattern` export is removed; allow trusted implementation files through the config or an explicit rule override.
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies [410f2f0]
14
+ - @opetope/runtime@0.5.0
15
+ - @opetope/core@0.5.0
16
+
3
17
  ## 0.4.0
4
18
 
5
19
  ### Minor Changes
package/README.md CHANGED
@@ -246,21 +246,59 @@ observation disabled. Graph frames remain bounded by the existing ring capacity.
246
246
 
247
247
  ## Testing
248
248
 
249
- `@opetope/react/testing` exports component fixtures and application scenarios:
249
+ `@opetope/react/testing` exports component fixtures, direct Call execution and application scenarios:
250
250
 
251
251
  - `renderSlot(target, { contribution, models, props })` mounts the published target or one fixture contribution;
252
- - `command(run)` produces an authentic `Call` for a model fixture.
252
+ - `command(run)` produces an authentic `Call` for a model fixture;
253
+ - `runCall(target, input?, options?)` executes an existing authentic `Call` without mounting React (D261).
253
254
 
254
255
  The `renderSlot` harness returns `Slot` and `updateProps`: there are no roots and no fixtures for them, UI enters the
255
256
  application as contributions (D85).
256
257
 
258
+ A test that previously mounted `useCommand` only to invoke a Call can use `runCall` directly. The result is the
259
+ Call's output, while a UI test still checks the `CommandOutcome` returned by the mounted consumer:
260
+
261
+ ```ts
262
+ import assert from 'node:assert/strict';
263
+ import { defineModel } from '@opetope/core';
264
+ import type { Call } from '@opetope/core';
265
+ import { defineFeature, openFeature } from '@opetope/runtime';
266
+ import { runCall } from '@opetope/react/testing';
267
+
268
+ const Arithmetic = defineModel<{ readonly double: Call<number, number> }>('example.testing.model');
269
+ const arithmetic = defineFeature({
270
+ id: 'example.testing.arithmetic',
271
+ own: ({ model }) => ({
272
+ arithmetic: model(Arithmetic, context => ({
273
+ double: context.call({ run: (input: number) => input * 2 }),
274
+ })),
275
+ }),
276
+ exports: ({ own }) => ({ double: own.arithmetic.double }),
277
+ });
278
+ const instance = openFeature(arithmetic, { imports: {}, reporter: () => undefined });
279
+ try {
280
+ const ready = await instance.ready;
281
+ const result = await runCall(ready.exports.double, 3);
282
+ assert.equal(result, 6);
283
+ } finally {
284
+ await instance.close();
285
+ }
286
+ ```
287
+
288
+ `runCall` returns `Promise<Output>` and preserves the Call's original errors and cancellation rejections. It adds no
289
+ UI status or outcome wrapper, scheduling policy, model creation or cleanup lifetime. The existing Call retains its
290
+ owner, lane and retirement rules; the test closes the instance it opened, including after failed readiness.
291
+ A void Call can use `runCall(target)`. Options always occupy the third argument:
292
+ `runCall(target, input, { signal })`, or `runCall(voidTarget, undefined, { signal })`.
293
+ `RunCallOptions` contains only the optional `signal`; a pre-aborted signal rejects before the body runs.
294
+
257
295
  ## Word map and entries
258
296
 
259
297
  | Entry | What it holds |
260
298
  | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
261
299
  | `@opetope/react` | `useModel`, `useCommand`, `useCommands`, `useReadable`, `useSelector`, `useResource`, `requiresModels`, `defineSlot`, `defineSwitchSlot`, `Slot`, `ContributionError`; types `SlotTarget`, `SwitchSlotTarget`, `SlotContribution`, `CommandHook`, `CommandOutcome` |
262
300
  | `@opetope/react/integration` | `FeatureBoundary`, `ContributionBoundary`, `useFeature`, `useFeatureRetry`, `FeatureBoundaryError` for the integration layer; types `FeatureBoundaryProps`, `ContributionBoundaryProps`, `ContributionErrorContent`, `ContributionFailure`, `FeatureDemandSource`, `FeatureDemandState`, `FeatureDemandResult`, `FeatureDemandLease` |
263
- | `@opetope/react/testing` | `renderSlot`, `command`, `createScenario`, `ScenarioTimeoutError` and fixture/scenario types; tests only |
301
+ | `@opetope/react/testing` | `renderSlot`, `command`, `runCall`, `createScenario`, `ScenarioTimeoutError`, `RunCallOptions` and fixture/scenario types; tests only |
264
302
 
265
303
  `Model` is a typed key for a record of `Readable`, `Call` and readable factories that UI components consume.
266
304
  The constructors of the integration entry are not re-exported from the safe entry and cannot come back into
package/README.ru.md CHANGED
@@ -246,21 +246,59 @@ idle executors могут требовать обхода, чтобы подтв
246
246
 
247
247
  ## Testing
248
248
 
249
- `@opetope/react/testing` экспортирует fixtures компонентов и сценарии приложения:
249
+ `@opetope/react/testing` экспортирует fixtures компонентов, прямой запуск Call и сценарии приложения:
250
250
 
251
251
  - `renderSlot(target, { contribution, models, props })` монтирует опубликованную цель либо один вклад-фикстуру;
252
- - `command(run)` выдаёт подлинный `Call` для фикстуры модели.
252
+ - `command(run)` выдаёт подлинный `Call` для фикстуры модели;
253
+ - `runCall(target, input?, options?)` запускает существующий подлинный `Call` без монтирования React (D261).
253
254
 
254
255
  Харнесс `renderSlot` отдаёт `Slot` и `updateProps`: корней и их фикстур больше нет, UI входит в приложение
255
256
  вкладами (D85).
256
257
 
258
+ Тест, который раньше монтировал `useCommand` только ради вызова Call, может использовать `runCall` напрямую.
259
+ Результат — выход Call; тест UI по-прежнему проверяет `CommandOutcome`, возвращённый смонтированным consumer:
260
+
261
+ ```ts
262
+ import assert from 'node:assert/strict';
263
+ import { defineModel } from '@opetope/core';
264
+ import type { Call } from '@opetope/core';
265
+ import { defineFeature, openFeature } from '@opetope/runtime';
266
+ import { runCall } from '@opetope/react/testing';
267
+
268
+ const Arithmetic = defineModel<{ readonly double: Call<number, number> }>('example.testing.model');
269
+ const arithmetic = defineFeature({
270
+ id: 'example.testing.arithmetic',
271
+ own: ({ model }) => ({
272
+ arithmetic: model(Arithmetic, context => ({
273
+ double: context.call({ run: (input: number) => input * 2 }),
274
+ })),
275
+ }),
276
+ exports: ({ own }) => ({ double: own.arithmetic.double }),
277
+ });
278
+ const instance = openFeature(arithmetic, { imports: {}, reporter: () => undefined });
279
+ try {
280
+ const ready = await instance.ready;
281
+ const result = await runCall(ready.exports.double, 3);
282
+ assert.equal(result, 6);
283
+ } finally {
284
+ await instance.close();
285
+ }
286
+ ```
287
+
288
+ `runCall` возвращает `Promise<Output>` и сохраняет исходные ошибки и причины отклонения при отмене Call. Он не добавляет
289
+ UI-статус, обёртку outcome, политику выполнения, создание модели или отдельное время жизни для cleanup. У Call остаются
290
+ его владелец, lane и правила retirement; тест закрывает открытый экземпляр, в том числе при отказе readiness.
291
+ Для void Call достаточно `runCall(target)`. Options всегда передаются третьим аргументом:
292
+ `runCall(target, input, { signal })` или `runCall(voidTarget, undefined, { signal })`.
293
+ `RunCallOptions` содержит только необязательный `signal`; уже отменённый сигнал отклоняет вызов до входа в его тело.
294
+
257
295
  ## Карта слов и входы
258
296
 
259
297
  | Вход | Что содержит |
260
298
  | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
261
299
  | `@opetope/react` | `useModel`, `useCommand`, `useCommands`, `useReadable`, `useSelector`, `useResource`, `requiresModels`, `defineSlot`, `defineSwitchSlot`, `Slot`, `ContributionError`; типы `SlotTarget`, `SwitchSlotTarget`, `SlotContribution`, `CommandHook`, `CommandOutcome` |
262
300
  | `@opetope/react/integration` | `FeatureBoundary`, `ContributionBoundary`, `useFeature`, `useFeatureRetry`, `FeatureBoundaryError` для integration-слоя; типы `FeatureBoundaryProps`, `ContributionBoundaryProps`, `ContributionErrorContent`, `ContributionFailure`, `FeatureDemandSource`, `FeatureDemandState`, `FeatureDemandResult`, `FeatureDemandLease` |
263
- | `@opetope/react/testing` | `renderSlot`, `command`, `createScenario`, `ScenarioTimeoutError` и типы fixtures/scenarios; только для тестов |
301
+ | `@opetope/react/testing` | `renderSlot`, `command`, `runCall`, `createScenario`, `ScenarioTimeoutError`, `RunCallOptions` и типы fixtures/scenarios; только для тестов |
264
302
 
265
303
  `Model` — типизированный ключ записи из `Readable`, `Call` и фабрик readable, которую используют UI-компоненты.
266
304
  Конструкторы integration-входа не переэкспортируются из безопасного входа и не могут вернуться в
@@ -0,0 +1,8 @@
1
+ import type { Call } from '@opetope/core';
2
+ interface RunCallOptions {
3
+ readonly signal?: AbortSignal;
4
+ }
5
+ /** Runs an authentic Call in a test; its existing owner still controls scheduling, retirement and cleanup. */
6
+ declare function runCall<Input, Output>(target: Call<Input, Output>, ...args: [Input] extends [void] ? [input?: NoInfer<Input>, options?: RunCallOptions] : [input: NoInfer<Input>, options?: RunCallOptions]): Promise<Output>;
7
+ export { runCall };
8
+ export type { RunCallOptions };
package/dist/testing.d.ts CHANGED
@@ -2,4 +2,6 @@ export { createScenario } from './scenario.js';
2
2
  export { ScenarioTimeoutError } from './scenario-diagnostics.js';
3
3
  export { command, renderSlot } from './scenario-slot.js';
4
4
  export type { SlotTestFixture, SlotTestHarness } from './scenario-slot.js';
5
+ export { runCall } from './testing-call.js';
6
+ export type { RunCallOptions } from './testing-call.js';
5
7
  export type { Scenario, ScenarioHost, ScenarioHostMount, ScenarioMount, ScenarioOptions, ScenarioOwnership, ScenarioWaitOptions, } from './scenario-types.js';
package/dist/testing.js CHANGED
@@ -1,4 +1,4 @@
1
- import{defineCallTarget as x,createAggregateError as _}from"@opetope/core/internal";import{openApplication as N}from"@opetope/runtime";import{createInspectionSession as q}from"@opetope/runtime/internal";import{createElement as B}from"react";import{declarationId as H}from"@opetope/core";import{d as D,a as F,c as L,M as U}from"./contribution-frame-B1PnpOaP.js";function W(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 G(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 J(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(G),...e.resources.map(n=>`Resource ${n.id}: ${n.state}; retainers ${String(n.retainers)}.`)]}function K(e){const t=[...e.runtime.conditions.flatMap(n=>n.state.kind==="true"?[]:[`Condition ${n.label}: ${n.state.kind}.`]),...e.runtime.instances.flatMap(W),...J(e.activity)];return t.length===0?"No registered runtime blocker observed; external UI and host work are unknown.":t.join(`
1
+ import{defineCallTarget as x,createAggregateError as _,invokeCallTarget as N}from"@opetope/core/internal";import{openApplication as q}from"@opetope/runtime";import{createInspectionSession as B}from"@opetope/runtime/internal";import{createElement as H}from"react";import{declarationId as D}from"@opetope/core";import{d as L,a as F,c as U,M as W}from"./contribution-frame-B1PnpOaP.js";function G(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 J(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 K(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(J),...e.resources.map(n=>`Resource ${n.id}: ${n.state}; retainers ${String(n.retainers)}.`)]}function Q(e){const t=[...e.runtime.conditions.flatMap(n=>n.state.kind==="true"?[]:[`Condition ${n.label}: ${n.state.kind}.`]),...e.runtime.instances.flatMap(G),...K(e.activity)];return t.length===0?"No registered runtime blocker observed; external UI and host work are unknown.":t.join(`
2
2
  `)}class E extends Error{label;timeoutMs;snapshot;history;code="timeout";constructor(t,n,r,o){super(`Scenario timed out after ${String(n)}ms: ${t}.
3
- ${K(r)}`),this.label=t,this.timeoutMs=n,this.snapshot=r,this.history=o,this.name="ScenarioTimeoutError"}}function Q(e,t){return!t||e===void 0?!1:e.closed&&!e.truncated}function V(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 X(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 Y(e,t){const n=e.activity,r=Q(n,t);return Object.freeze({...X(n,r),closed:t,reasons:V(n,r,t),scope:"registered-runtime",status:r?"complete":"unknown"})}class Z{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 ee=H("testing.contribution");let T=0;function te(e){return T+=1,D(x({id:`testing.command.${String(T)}`,run:e}))}function I(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=F(e.entries),d=F(w);return(c===void 0?i.map(p=>[p.id,p.value,L(p)??l]):[[ee,c,l]]).map(([p,f,k])=>B(U,{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 ne(e,t={}){const n=I(e,t);return Object.freeze({Slot:n.Slot,updateProps:n.updateProps})}function C(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 re{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 C(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=C(n.timeoutMs??this.source.timeoutMs,"Scenario timeoutMs"),o=C(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 E(t,n,this.source.getSnapshot(),this.source.history())}}function A(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 oe(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 se(e,t){const n=C(t.timeoutMs??1e3,"Scenario timeoutMs"),r=A(t.historyCapacity??64,"Scenario historyCapacity"),o=A(t.activityCapacity??256,"Scenario activityCapacity");let w=t.host;oe(w);let c=N(e,{...t.cleanupFailure===void 0?{}:{cleanupFailure:t.cleanupFailure},conditions:t.conditions,imports:t.imports}),s;try{s=q(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 re({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 M,S;const z=()=>{if(M!==void 0)return M;let u,m;M=new Promise((a,h)=>{u=a,m=h}),l="closing",$.cancel();const y=[];try{y.push(c.close())}catch(a){y.push(Promise.reject(a))}return y.push(...[...v].map(a=>a.close())),Promise.allSettled(y).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(_(h,"Scenario cleanup failed."))}),M};return Object.freeze({close:()=>{if(S!==void 0)return S;const u=z();return S!==void 0||(S=$.deadline(u,"scenario close"),S.catch(()=>{l==="closing"&&(S=void 0)})),S},getSnapshot:f,history:k,mount:(u,m={})=>{if(l!=="open")throw new Error("Scenario is closing.");const y=I(u,m),a=new Z((b,O)=>{v.delete(a),y.close(),b&&p.push(O)});v.add(a);let h;try{if(h=w.mount(y.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.");y.updateProps(b),$.notify()}})},notify:()=>{l==="open"&&$.notify()},ownership:()=>Y(f(),g),ready:P,waitFor:(u,m={})=>l!=="open"?Promise.reject(new Error("Scenario is closing.")):$.waitFor(u,m)})}export{E as ScenarioTimeoutError,te as command,se as createScenario,ne as renderSlot};
3
+ ${Q(r)}`),this.label=t,this.timeoutMs=n,this.snapshot=r,this.history=o,this.name="ScenarioTimeoutError"}}function V(e,t){return!t||e===void 0?!1:e.closed&&!e.truncated}function X(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 Y(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 Z(e,t){const n=e.activity,r=V(n,t);return Object.freeze({...Y(n,r),closed:t,reasons:X(n,r,t),scope:"registered-runtime",status:r?"complete":"unknown"})}class ee{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 te=D("testing.contribution");let T=0;function ne(e){return T+=1,L(x({id:`testing.command.${String(T)}`,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,u=s===void 0?void 0:{models:[],reporter:s};return Object.freeze({close:()=>{r={},o.clear()},Slot:()=>{const i=F(e.entries),d=F(w);return(c===void 0?i.map(p=>[p.id,p.value,U(p)??u]):[[te,c,u]]).map(([p,f,k])=>H(W,{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 re(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 oe{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,u=()=>{};const g=(f,k=f!==void 0)=>{s||(s=!0,u(),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);u=()=>{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 E(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 se(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 ie(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;se(w);let c=q(e,{...t.cleanupFailure===void 0?{}:{cleanupFailure:t.cleanupFailure},conditions:t.conditions,imports:t.imports}),s;try{s=B(c,{activityCapacity:o,ringCapacity:r})}catch(l){throw c.ready.catch(()=>{}),c.close().catch(()=>{}),l}let u="open",g=!1,i=!1;const d=[],v=new Set,p=[],f=()=>s.getSnapshot(),k=()=>Object.freeze([...d]),$=new oe({getSnapshot:f,history:k,timeoutMs:n}),j=()=>{if(!i){i=!0;try{const l=f(),m=d[d.length-1];((m==null?void 0:m.stateRevision)!==l.stateRevision||m.activity!==l.activity)&&(d.push(l),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 l,m;C=new Promise((a,h)=>{l=a,m=h}),u="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,u="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?l():m(_(h,"Scenario cleanup failed."))}),C};return Object.freeze({close:()=>{if(y!==void 0)return y;const l=z();return y!==void 0||(y=$.deadline(l,"scenario close"),y.catch(()=>{u==="closing"&&(y=void 0)})),y},getSnapshot:f,history:k,mount:(l,m={})=>{if(u!=="open")throw new Error("Scenario is closing.");const S=A(l,m),a=new ee((b,O)=>{v.delete(a),S.close(),b&&p.push(O)});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(u!=="open"||a.closed)throw new Error("Scenario mount is closed.");S.updateProps(b),$.notify()}})},notify:()=>{u==="open"&&$.notify()},ownership:()=>Z(f(),g),ready:P,waitFor:(l,m={})=>u!=="open"?Promise.reject(new Error("Scenario is closing.")):$.waitFor(l,m)})}async function ae(e,...t){var n;return N(e,t[0],{signal:((n=t[1])==null?void 0:n.signal)??new AbortController().signal})}export{E as ScenarioTimeoutError,ne as command,ie as createScenario,re as renderSlot,ae 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"],"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 } from '@opetope/core/internal';\nimport { openApplication } from '@opetope/runtime';\nimport type { Application, ApplicationExecution } from '@opetope/runtime';\nimport { createInspectionSession } from '@opetope/runtime/internal';\nimport type { 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<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 let execution: ApplicationExecution | undefined = openApplication(application, {\n ...(options.cleanupFailure === undefined ? {} : { cleanupFailure: options.cleanupFailure }),\n conditions: options.conditions,\n imports: options.imports,\n });\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"],"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","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"],"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 } from '@opetope/core/internal';\nimport { openApplication } from '@opetope/runtime';\nimport type { Application, ApplicationExecution } from '@opetope/runtime';\nimport { createInspectionSession } from '@opetope/runtime/internal';\nimport type { 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<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 let execution: ApplicationExecution | undefined = openApplication(application, {\n ...(options.cleanupFailure === undefined ? {} : { cleanupFailure: options.cleanupFailure }),\n conditions: options.conditions,\n imports: options.imports,\n });\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 { 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. */\nasync function runCall<Input, Output>(\n target: Call<Input, Output>,\n ...args: [Input] extends [void]\n ? [input?: NoInfer<Input>, options?: RunCallOptions]\n : [input: NoInfer<Input>, options?: RunCallOptions]\n): Promise<Output> {\n return invokeCallTarget(target, args[0] as Input, {\n signal: args[1]?.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","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","args","invokeCallTarget","_a"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opetope/react",
3
- "version": "0.4.0",
3
+ "version": "0.5.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.4.0",
54
- "@opetope/runtime": "0.4.0",
53
+ "@opetope/core": "0.5.0",
54
+ "@opetope/runtime": "0.5.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.4.0",
62
- "@opetope/runtime": "0.4.0",
61
+ "@opetope/core": "0.5.0",
62
+ "@opetope/runtime": "0.5.0",
63
63
  "react": ">=19.0.0 <20"
64
64
  },
65
65
  "sideEffects": false,