@opetope/react 0.11.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,9 @@
1
- import type { Call, ModelOf } from '@opetope/core';
2
- import type { ModelIdentity, NoExtraKeys } from '@opetope/core/internal';
1
+ import type { Command, ModelOf } from '@opetope/core';
2
+ import type { ExactKeys, ModelIdentity } from '@opetope/core/internal';
3
3
  declare const modelBindingBrand: unique symbol;
4
4
  declare const modelBindingIdentityBrand: unique symbol;
5
5
  type AnyModel = ModelIdentity;
6
- type AnyCommand = Call<never, unknown>;
6
+ type AnyCommand = Command<never, unknown>;
7
7
  interface ModelBindingIdentity {
8
8
  readonly [modelBindingIdentityBrand]: true;
9
9
  }
@@ -15,7 +15,7 @@ type ModelBindingMetadata = {
15
15
  readonly declaration: AnyModel;
16
16
  readonly model: object;
17
17
  };
18
- declare function bindModel<Declaration extends AnyModel, const Actual extends ModelOf<Declaration>>(declaration: Declaration, model: Actual & NoExtraKeys<Actual, ModelOf<Declaration>>): ModelBinding<Declaration>;
18
+ declare function bindModel<Declaration extends AnyModel, const Keys extends PropertyKey = never>(declaration: Declaration, model: ExactKeys<Keys, ModelOf<Declaration>> & ModelOf<Declaration>): ModelBinding<Declaration>;
19
19
  declare function getModelBindingMetadata(binding: ModelBindingIdentity): ModelBindingMetadata;
20
20
  export { bindModel, getModelBindingMetadata };
21
21
  export type { AnyCommand, AnyModel, ModelBindingIdentity };
@@ -1,4 +1,4 @@
1
- import type { Call, ModelOf, PaginatedResource, Resource } from '@opetope/core';
1
+ import type { Command, ModelOf, PaginatedResource, Resource } from '@opetope/core';
2
2
  import type { CommandHook } from './command-hook.js';
3
3
  import type { AnyModel } from './model-binding.js';
4
4
  import type { SelectionContext, SelectionShape } from './model-selection-snapshot.js';
@@ -8,7 +8,7 @@ import type { PaginatedResourceHook, ResourceHook, ResourceKey } from './resourc
8
8
  * `useResource` does. `read(resource.state)` is unchanged — it hands out the state and leases nothing — so naming one
9
9
  * Resource both ways registers it twice: the subscription and the lease are independent.
10
10
  */
11
- type SelectedValue<Value> = Value extends Call<infer Input, infer Output> ? CommandHook<Input, Output> : Value extends PaginatedResource<infer Data, infer Key extends ResourceKey, infer Cursor extends ResourceKey> ? PaginatedResourceHook<Data, Key, Cursor> : Value extends Resource<infer Data, infer Key extends ResourceKey> ? ResourceHook<Data, Key> : Value;
11
+ type SelectedValue<Value> = Value extends Command<infer Input, infer Output> ? CommandHook<Input, Output> : Value extends PaginatedResource<infer Data, infer Key extends ResourceKey, infer Cursor extends ResourceKey> ? PaginatedResourceHook<Data, Key, Cursor> : Value extends Resource<infer Data, infer Key extends ResourceKey> ? ResourceHook<Data, Key> : Value;
12
12
  type ModelSelection<Selected> = {
13
13
  readonly [Key in keyof Selected]: SelectedValue<Selected[Key]>;
14
14
  };
@@ -8,7 +8,14 @@ import type { PaginationOutcome, PaginationState, Resource, ResourceOperationOut
8
8
  import type { ResourceKey } from '@opetope/core/internal';
9
9
  import type { CommandHook } from './command-hook.js';
10
10
  import type { CommandInvoker } from './command-hook-controller.js';
11
- type AnyResource = Resource<never, ResourceKey>;
11
+ /**
12
+ * D439: the widest Resource, and the name says so. `Resource<never, ResourceKey>` said the opposite in both
13
+ * arguments — `never` is the narrowest `Data` a reader can be handed and a named `Key` *requires* `state.key`, so
14
+ * hardly any member of the family fitted it. Nothing was refused, because every value reaches this name through a
15
+ * cast, which is exactly why the name could drift. `ResourceKeyField<never>` requires no field, so
16
+ * `Resource<unknown, never>` is the one form every Resource satisfies — keyed or not, paginated or not.
17
+ */
18
+ type AnyResource = Resource<unknown, never>;
12
19
  /**
13
20
  * D349, D359: a Resource is a structural contract a host may implement with an ordinary object, so the question the
14
21
  * type of a selected field asked is answered by the runtime that owns it — the registry of the materializations it
@@ -25,14 +32,14 @@ type ResourcePaginationHook<Cursor extends ResourceKey> = Readonly<{
25
32
  /**
26
33
  * D359: what `useResource` answers, and what a field of a `useModel` selection whose value is a Resource answers.
27
34
  * The verbs never reject (a Resource answers an outcome), so the ordinary path of `outcome` is
28
- * `{ status: 'ok', value }`; `failed` stays in the type because the controller turns a synchronous throw of an
35
+ * `{ kind: 'ok', value }`; `failed` stays in the type because the controller turns a synchronous throw of an
29
36
  * invoker into it, and the wrapper below carries its own test that it never throws synchronously.
30
37
  */
31
38
  type ResourceHook<Data, Key extends ResourceKey = never> = Readonly<{
32
- invalidate: CommandHook<void, ResourceOperationOutcome>;
33
39
  /** Present when the declaration carried `pagination`; `undefined` for every other Resource. */
34
40
  pagination: ResourcePaginationHook<ResourceKey> | undefined;
35
41
  refresh: CommandHook<void, ResourceOperationOutcome>;
42
+ reset: CommandHook<void, ResourceOperationOutcome>;
36
43
  retry: CommandHook<void, ResourceOperationOutcome>;
37
44
  state: ResourceState<Data, Key>;
38
45
  }>;
@@ -40,9 +47,9 @@ type PaginatedResourceHook<Data, Key extends ResourceKey, Cursor extends Resourc
40
47
  pagination: ResourcePaginationHook<Cursor>;
41
48
  }>;
42
49
  type ResourceInvokers = Readonly<{
43
- invalidate: CommandInvoker;
44
50
  loadNext: CommandInvoker;
45
51
  refresh: CommandInvoker;
52
+ reset: CommandInvoker;
46
53
  retry: CommandInvoker;
47
54
  }>;
48
55
  declare function paginationOf(resource: AnyResource): ResourcePaginationHook<ResourceKey> | undefined;
@@ -1,5 +1,5 @@
1
1
  import type { FC } from 'react';
2
- import type { Call } from '@opetope/core';
2
+ import type { Command } from '@opetope/core';
3
3
  import type { ModelFixture } from './contribution-frame.js';
4
4
  import type { SlotComponentProperties, SlotContribution, SlotProperties, SlotTarget } from './slot.js';
5
5
  /**
@@ -22,10 +22,13 @@ interface SlotTestHarness<Props extends SlotProperties> {
22
22
  }
23
23
  /**
24
24
  * A command a fixture publishes: the test writes the body, the harness gives it the identity a model requires. The
25
- * Call itself is the core fixture (D300); what this word adds, and what keeps it in the React package, is the
25
+ * Command itself is the core fixture (D300); what this word adds, and what keeps it in the React package, is the
26
26
  * contribution binding a mount reads (D285).
27
27
  */
28
- declare function command<Input, Output>(run: (input: Input) => Output | PromiseLike<Output>): Call<Input, Output>;
28
+ declare function command<Input = void, Output = void>(run: (context: {
29
+ readonly input: Input;
30
+ readonly signal: AbortSignal;
31
+ }) => Output | PromiseLike<Output>): Command<Input, Output>;
29
32
  /**
30
33
  * Mounts the contributions of one slot the way a host does, with optional model fixtures for a component test. D19 and D85: the same
31
34
  * ContributionMount is shared by component tests and the application scenario harness (D206).
@@ -1,6 +1,6 @@
1
1
  import type { FC } from 'react';
2
- import type { Application } from '@opetope/runtime';
3
- import type { ApplicationConditionBinding, OpenApplicationOptions, RuntimeGraphSnapshot } from '@opetope/runtime/internal';
2
+ import type { Application, ApplicationConditionBinding } from '@opetope/runtime';
3
+ import type { 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 {
@@ -1,5 +1,4 @@
1
- import type { Application } from '@opetope/runtime';
2
- import type { ApplicationConditionBinding } from '@opetope/runtime/internal';
1
+ import type { Application, ApplicationConditionBinding } from '@opetope/runtime';
3
2
  import type { Scenario, ScenarioFeatures, ScenarioHostMount, ScenarioOptions } from './scenario-types.js';
4
3
  /** D206: one real application, its existing inspection session and renderer-owned Slot ingress. */
5
4
  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>;
@@ -1,28 +1,21 @@
1
- import type { Call } from '@opetope/core';
1
+ import type { Command } from '@opetope/core';
2
2
  import type { CommandHook } from './command-hook.js';
3
3
  import type { SelectedResource } from './model-selection-snapshot.js';
4
- type CommandSelection = Readonly<Record<string, Call<never, unknown>>>;
4
+ type CommandSelection = Readonly<Record<string, Command<never, unknown>>>;
5
5
  type ResourceSelection = ReadonlyMap<PropertyKey, SelectedResource>;
6
- type HookOf<Command> = Command extends Call<infer Input, infer Output> ? CommandHook<Input, Output> : never;
7
- type CommandsHook<Commands> = {
8
- readonly [Key in keyof Commands]: HookOf<Commands[Key]>;
9
- };
10
6
  /** D359: what one selected Resource answers — its three verbs, its state, and the pagination capability if it has one. */
11
7
  type SelectedResourceHook = Readonly<{
12
- invalidate: CommandHook<never, unknown>;
13
8
  pagination: Readonly<{
14
9
  loadNext: CommandHook<never, unknown>;
15
10
  state: unknown;
16
11
  }> | undefined;
17
12
  refresh: CommandHook<never, unknown>;
13
+ reset: CommandHook<never, unknown>;
18
14
  retry: CommandHook<never, unknown>;
19
15
  state: unknown;
20
16
  }>;
21
17
  type SelectionHooks = Readonly<Record<PropertyKey, CommandHook<never, unknown> | SelectedResourceHook>>;
22
18
  /** The consumers of one selection: a command per key, and the two verbs of a resource per key (D321). */
23
19
  declare function useSelectionHooks(commands: CommandSelection, resources: ResourceSelection): SelectionHooks;
24
- declare function useCommands<Commands extends {
25
- readonly [Key in keyof Commands]: Call<never, unknown>;
26
- }>(commands: Commands): CommandsHook<Commands>;
27
- export { useCommands, useSelectionHooks };
20
+ export { useSelectionHooks };
28
21
  export type { SelectionHooks };
package/dist/testing.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * Every word of the entry below, except one: `command` is shadowed here by the fixture that also registers the
3
3
  * contribution binding a mount reads, so the React name keeps meaning the stronger thing it has always meant. The
4
- * core fixture is the same Call without that binding, and a test that wants it names `@opetope/core/testing` (D300).
4
+ * core fixture is the same Command without that binding, and a test that wants it names `@opetope/core/testing` (D300).
5
5
  */
6
- export { CallTimeoutError, eventually, openModel, runCall, settled, testReadable, testResourceEpoch, waitFor, WaitTimeoutError, yieldTurn, } from '@opetope/runtime/testing';
7
- export type { CallInputArgs, EventuallyOptions, ModelInstance, OpenModelOptions, RunCallOptions, SettleTarget, WaitOptions, } from '@opetope/runtime/testing';
6
+ export { CommandTimeoutError, eventually, openModel, runCommand, settled, testReadable, testResourceEpoch, waitFor, WaitTimeoutError, yieldTurn, } from '@opetope/runtime/testing';
7
+ export type { CommandInputArgs, EventuallyOptions, ModelInstance, OpenModelOptions, RunCommandOptions, SettleTarget, WaitOptions, } from '@opetope/runtime/testing';
8
8
  export { createScenario } from './scenario.js';
9
9
  export { ScenarioTimeoutError } from './scenario-diagnostics.js';
10
10
  export { command, renderSlot } from './scenario-slot.js';
package/dist/testing.js CHANGED
@@ -1,4 +1,4 @@
1
- import{CallTimeoutError as ye,WaitTimeoutError as Se,eventually as ge,openModel as ve,runCall as $e,settled as ke,testReadable as Me,testResourceEpoch as Ce,waitFor as je,yieldTurn as Ee}from"@opetope/runtime/testing";import{hasOwn as F,createAggregateError as x}from"@opetope/core/internal";import{openApplication as _}from"@opetope/runtime";import{createInspectionSession as N}from"@opetope/runtime/internal";import{createElement as B}from"react";import{declarationId as H}from"@opetope/core";import{command as W}from"@opetope/core/testing";import{e as q,a as P,b as D,M as L}from"./contribution-frame-DkNmjChZ.js";function U(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}; leases ${String(n.leases)}.`)]}function K(e){const t=[...e.runtime.conditions.flatMap(n=>n.state.kind==="true"?[]:[`Condition ${n.label}: ${n.state.kind}.`]),...e.runtime.instances.flatMap(U),...J(e.activity)];return t.length===0?"No registered runtime blocker observed; external UI and host work are unknown.":t.join(`
1
+ import{CommandTimeoutError as ye,WaitTimeoutError as ge,eventually as Se,openModel as ve,runCommand as $e,settled as ke,testReadable as Me,testResourceEpoch as Ce,waitFor as je,yieldTurn as Ee}from"@opetope/runtime/testing";import{hasOwn as E,createAggregateError as x}from"@opetope/core/internal";import{openApplication as _}from"@opetope/runtime";import{createInspectionSession as N}from"@opetope/runtime/internal";import{createElement as B}from"react";import{declarationId as H}from"@opetope/core";import{command as W}from"@opetope/core/testing";import{e as q,a as P,b as D,M as L}from"./contribution-frame-CCKnOxZR.js";function U(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`Command ${e.declaration}: ${e.state}; lane parallel; blockers none (parallel).`;const t=e.lane.blockedBy==="unknown"?"unknown":e.lane.blockedBy.join(", ");return`Command ${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}; leases ${String(n.leases)}.`)]}function K(e){const t=[...e.runtime.conditions.flatMap(n=>n.state.kind==="true"?[]:[`Condition ${n.label}: ${n.state.kind}.`]),...e.runtime.instances.flatMap(U),...J(e.activity)];return t.length===0?"No registered runtime blocker observed; external UI and host work are unknown.":t.join(`
2
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
- ${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");function te(e){return q(W(e))}function O(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=P(e.entries),d=P(w);return(c===void 0?i.map(p=>[p.id,p.value,D(p)??l]):[[ee,c,l]]).map(([p,f,k])=>B(L,{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=O(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 y=(f,k=f!==void 0)=>{s||(s=!0,l(),k?c(f):w())},i=()=>{if(!s)try{t(this.source.getSnapshot())&&y()}catch(f){y(f,!0)}},d=f=>y(f),v=setTimeout(()=>y(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 R(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){return{...e.cleanupFailure===void 0?{}:{cleanupFailure:e.cleanupFailure},...F(e,"conditions")?{conditions:e.conditions}:{},...F(e,"imports")?{imports:e.imports}:{},reporter:e.reporter}}function ie(e,t){const n=C(t.timeoutMs??1e3,"Scenario timeoutMs"),r=R(t.historyCapacity??64,"Scenario historyCapacity"),o=R(t.activityCapacity??256,"Scenario activityCapacity");let w=t.host;oe(w);let c=_(e,se(t)),s;try{s=N(c,{activityCapacity:o,ringCapacity:r})}catch(u){throw c.ready.catch(()=>{}),c.close().catch(()=>{}),u}let l="open",y=!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()}},I=s.subscribe(j);j();const E=$.deadline(c.ready,"application ready");E.catch(()=>{});let M,S;const A=()=>{if(M!==void 0)return M;let u,m;M=new Promise((a,h)=>{u=a,m=h}),l="closing",$.cancel();const g=[];try{g.push(c.close())}catch(a){g.push(Promise.reject(a))}return g.push(...[...v].map(a=>a.close())),Promise.allSettled(g).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{I(),s.close(),v.clear(),c=void 0,w=void 0}y=h.length===0,h.length===0?u():m(x(h,"Scenario cleanup failed."))}),M};return Object.freeze({close:()=>{if(S!==void 0)return S;const u=A();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 g=O(u,m),a=new Z((b,z)=>{v.delete(a),g.close(),b&&p.push(z)});v.add(a);let h;try{if(h=w.mount(g.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.");g.updateProps(b),$.notify()}})},notify:()=>{l==="open"&&$.notify()},ownership:()=>Y(f(),y),ready:E,waitFor:(u,m={})=>l!=="open"?Promise.reject(new Error("Scenario is closing.")):$.waitFor(u,m)})}export{ye as CallTimeoutError,T as ScenarioTimeoutError,Se as WaitTimeoutError,te as command,ie as createScenario,ge as eventually,ve as openModel,ne as renderSlot,$e as runCall,ke as settled,Me as testReadable,Ce as testResourceEpoch,je as waitFor,Ee as yieldTurn};
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");function te(e){return q(W(e))}function O(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=P(e.entries),d=P(w);return(c===void 0?i.map(p=>[p.id,p.value,D(p)??l]):[[ee,c,l]]).map(([p,f,k])=>B(L,{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=O(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 y=(f,k=f!==void 0)=>{s||(s=!0,l(),k?c(f):w())},i=()=>{if(!s)try{t(this.source.getSnapshot())&&y()}catch(f){y(f,!0)}},d=f=>y(f),v=setTimeout(()=>y(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 R(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){return{...e.cleanupFailure===void 0?{}:{cleanupFailure:e.cleanupFailure},...E(e,"conditions")?{conditions:e.conditions}:{},...E(e,"grace")?{grace:e.grace}:{},...E(e,"imports")?{imports:e.imports}:{},reporter:e.reporter}}function ie(e,t){const n=C(t.timeoutMs??1e3,"Scenario timeoutMs"),r=R(t.historyCapacity??64,"Scenario historyCapacity"),o=R(t.activityCapacity??256,"Scenario activityCapacity");let w=t.host;oe(w);let c=_(e,se(t)),s;try{s=N(c,{activityCapacity:o,ringCapacity:r})}catch(u){throw c.ready.catch(()=>{}),c.close().catch(()=>{}),u}let l="open",y=!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()}},I=s.subscribe(j);j();const F=$.deadline(c.ready,"application ready");F.catch(()=>{});let M,g;const A=()=>{if(M!==void 0)return M;let u,m;M=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{I(),s.close(),v.clear(),c=void 0,w=void 0}y=h.length===0,h.length===0?u():m(x(h,"Scenario cleanup failed."))}),M};return Object.freeze({close:()=>{if(g!==void 0)return g;const u=A();return g!==void 0||(g=$.deadline(u,"scenario close"),g.catch(()=>{l==="closing"&&(g=void 0)})),g},getSnapshot:f,history:k,mount:(u,m={})=>{if(l!=="open")throw new Error("Scenario is closing.");const S=O(u,m),a=new Z((b,z)=>{v.delete(a),S.close(),b&&p.push(z)});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:()=>Y(f(),y),ready:F,waitFor:(u,m={})=>l!=="open"?Promise.reject(new Error("Scenario is closing.")):$.waitFor(u,m)})}export{ye as CommandTimeoutError,T as ScenarioTimeoutError,ge as WaitTimeoutError,te as command,ie as createScenario,Se as eventually,ve as openModel,ne as renderSlot,$e as runCommand,ke as settled,Me as testReadable,Ce as testResourceEpoch,je as waitFor,Ee as yieldTurn};
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}; leases ${String(resource.leases)}.`,\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 { command as callFixture } from '@opetope/core/testing';\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\n/**\n * A command a fixture publishes: the test writes the body, the harness gives it the identity a model requires. The\n * Call itself is the core fixture (D300); what this word adds, and what keeps it in the React package, is the\n * contribution binding a mount reads (D285).\n */\nfunction command<Input, Output>(run: (input: Input) => Output | PromiseLike<Output>): Call<Input, Output> {\n return bindCommand(callFixture<Input, Output>(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"],"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","command","run","bindCommand","callFixture","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"],"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"],"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 `Command ${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 `Command ${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}; leases ${String(resource.leases)}.`,\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 { Command } from '@opetope/core';\nimport { command as callFixture } from '@opetope/core/testing';\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\n/**\n * A command a fixture publishes: the test writes the body, the harness gives it the identity a model requires. The\n * Command itself is the core fixture (D300); what this word adds, and what keeps it in the React package, is the\n * contribution binding a mount reads (D285).\n */\nfunction command<Input = void, Output = void>(\n run: (context: { readonly input: Input; readonly signal: AbortSignal }) => Output | PromiseLike<Output>,\n): Command<Input, Output> {\n return bindCommand(callFixture<Input, Output>(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, ApplicationConditionBinding, 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/**\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 /*\n * D366: a scenario opens a real application, so the window of a lease is the host's word here too. D407: the\n * word is the key, as it already is for `conditions` and `imports` beside it — a `grace` written as `undefined`\n * travels to `openApplication` and is refused there, instead of being dropped here into the `0` of a host that\n * wrote no window at all.\n */\n ...(hasOwn(options, 'grace') ? { grace: options.grace! } : {}),\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"],"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","command","run","bindCommand","callFixture","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"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opetope/react",
3
- "version": "0.11.0",
3
+ "version": "0.12.1",
4
4
  "engines": {
5
5
  "node": ">=20.19.0"
6
6
  },
@@ -15,7 +15,6 @@
15
15
  "files": [
16
16
  "dist",
17
17
  "README.md",
18
- "README.ru.md",
19
18
  "LICENSE",
20
19
  "CHANGELOG.md"
21
20
  ],
@@ -50,16 +49,16 @@
50
49
  }
51
50
  ],
52
51
  "devDependencies": {
53
- "@opetope/core": "0.11.0",
54
- "@opetope/runtime": "0.11.0",
52
+ "@opetope/core": "0.12.1",
53
+ "@opetope/runtime": "0.12.1",
55
54
  "@testing-library/react": "16.3.3",
56
55
  "@types/react": "19.2.18",
57
56
  "react": "19.2.8",
58
57
  "react-dom": "19.2.8"
59
58
  },
60
59
  "peerDependencies": {
61
- "@opetope/core": "0.11.0",
62
- "@opetope/runtime": "0.11.0",
60
+ "@opetope/core": "0.12.1",
61
+ "@opetope/runtime": "0.12.1",
63
62
  "react": ">=19.0.0 <20"
64
63
  },
65
64
  "sideEffects": false,