@semiont/core 0.5.23 → 0.5.24

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/dist/testing.d.ts CHANGED
@@ -1,71 +1,5 @@
1
- import { Observable, BehaviorSubject } from 'rxjs';
2
- import { StateUnit, ITransport, BaseUrl, ConnectionState, SemiontError, BusOperationKey, EventMap, ResourceId, EventBus } from '@semiont/core';
3
- import * as fc from 'fast-check';
4
-
5
- /**
6
- * Executable enforcement of the StateUnit pattern — the runtime twin of
7
- * `packages/sdk/docs/STATE-UNITS.md` and the ledger in
8
- * `.plans/STATE-UNIT-AXIOMS.md`. The `StateUnit` interface's own comment notes
9
- * the pattern is convention; this file makes it executable.
10
- *
11
- * `assertStateUnitAxioms(spec)` runs every applicable axiom against a factory in
12
- * one shot, throwing a labeled Error on the first violation (the axiom id is in
13
- * the message). It is framework-agnostic on purpose — only `rxjs` + `fast-check`,
14
- * no `vitest` — so it ships through `@semiont/core/testing` and any package's test
15
- * runner can invoke it from a single `it(...)` per state unit. It lives in core
16
- * (not sdk) so even packages below sdk (e.g. `http-transport`) can use it without
17
- * a dependency cycle.
18
- *
19
- * Axioms (random-input dimension; fast-check):
20
- * A5 dispose() is idempotent and total (n ∈ [1,20] calls never throw)
21
- * A5b post-dispose inertness — every public method is a no-op after dispose
22
- * A6 every pre-dispose subscriber (k ∈ [1,10]) sees `complete` on dispose
23
- * X3-runtime instance isolation — driving one instance never moves another's surfaces
24
- * Structural assertions (single-shot):
25
- * A1 plain-object identity (no class instance)
26
- * X1 no raw Subject on the public surface
27
- * A7-passed disposing the unit must NOT dispose an injected dependency
28
- * A7-owned disposing the unit MUST dispose its internally-constructed children
29
- */
30
-
31
- /**
32
- * A disposable stand-in for an injected dependency. Pass one as a unit's
33
- * constructor arg, then list it in `setup().passedIn` so A7-passed can assert
34
- * the unit never disposed it. Counts calls so A7-passed also holds under the
35
- * repeated-dispose stress of A5.
36
- */
37
- interface DisposeProbe extends StateUnit {
38
- readonly disposeCount: number;
39
- }
40
- declare function disposeProbe(): DisposeProbe;
41
- type SetupResult<T extends StateUnit> = T | {
42
- unit: T;
43
- passedIn?: readonly DisposeProbe[];
44
- teardown?: () => void;
45
- };
46
- interface StateUnitAxiomSpec<T extends StateUnit> {
47
- /**
48
- * Build a FRESH unit. Called many times (fast-check re-runs), so it must
49
- * return an independent instance each call. Return the bare unit, or an object
50
- * carrying the injected `passedIn` probes (A7-passed) and a `teardown` to
51
- * release per-instance resources (e.g. a mock bus).
52
- */
53
- setup: () => SetupResult<T>;
54
- /** Owned public Observables — Subjects the unit completes on dispose (A6, X3, post-dispose inertness). */
55
- surfaces?: (unit: T) => readonly Observable<unknown>[];
56
- /** Public input methods as zero-arg callers (A5b post-dispose, X3 drive). */
57
- invocations?: (unit: T) => readonly (() => unknown)[];
58
- /** Surfaces of internally-constructed children — must complete when the outer disposes (A7-owned). */
59
- ownedChildSurfaces?: (unit: T) => readonly Observable<unknown>[];
60
- /** fast-check run budget per property (default 30). */
61
- numRuns?: number;
62
- }
63
- /**
64
- * Run every applicable axiom against `spec`. Throws a labeled Error on the first
65
- * violation. Axioms whose accessors are omitted are skipped (e.g. A7-passed
66
- * runs only when `setup` returns `passedIn`; A6 only when `surfaces` is given).
67
- */
68
- declare function assertStateUnitAxioms<T extends StateUnit>(spec: StateUnitAxiomSpec<T>): void;
1
+ import { BehaviorSubject, Observable } from 'rxjs';
2
+ import { ITransport, BaseUrl, ConnectionState, SemiontError, BusOperationKey, EventMap, ResourceId, EventBus } from '@semiont/core';
69
3
 
70
4
  /**
71
5
  * FaultyTransport — a seeded, scriptable `ITransport` simulator for the
@@ -111,6 +45,18 @@ interface RequestLogEntry {
111
45
  * the same key are the same logical request re-issued.
112
46
  */
113
47
  retryKey: string;
48
+ /**
49
+ * The payload as emitted — envelope, options, params, `correlationId` and
50
+ * all. This is the surface for "assert what my orchestrator actually SENT"
51
+ * (SDK-TESTING-DOUBLE gap 6): without it every consumer harness re-invented
52
+ * a per-channel `transport.on(...)` wire recorder alongside this log.
53
+ *
54
+ * SHALLOW snapshot: the top level is copied at emit time, so a caller that
55
+ * mutates its own payload object afterwards cannot rewrite history. Nested
56
+ * objects are shared by reference — deep-freeze is not worth the cost in a
57
+ * double, and no in-repo caller mutates nested request payloads.
58
+ */
59
+ payload: Record<string, unknown>;
114
60
  }
115
61
  interface FaultyTransportConfig {
116
62
  /**
@@ -169,113 +115,5 @@ declare class FaultyTransport implements ITransport {
169
115
  dispose(): void;
170
116
  }
171
117
 
172
- /**
173
- * Executable enforcement of the liveness axioms — the runtime twin of
174
- * `.plans/LIVENESS-AXIOMS.md`, and the composition-level sibling of
175
- * `assertStateUnitAxioms` (state-unit-axioms.ts). Where the StateUnit axioms
176
- * make *per-unit* wrongness mechanically detectable, these make *silence*
177
- * detectable: every existing enforcement tier is safety (nothing wrong is
178
- * delivered); these assert liveness (something is eventually delivered).
179
- *
180
- * Axioms (fault-schedule dimension; fast-check):
181
- * L1 Subscriber liveness — every output emits next|error within the bound,
182
- * under any fault schedule. Error is a permitted outcome; the forbidden
183
- * fourth state is pending-forever.
184
- * L2 Request settlement — every awaited path settles within the bound;
185
- * re-issues per logical request stay within the retry budget (B14: one);
186
- * a faulted request must be re-issued or surfaced, never swallowed.
187
- * L3 Delivery across lifecycle transitions — every event written to a live
188
- * connection reaches the output exactly once, wherever a client-initiated
189
- * transition (handover / reconnect / scope change) lands. Retirement is
190
- * by drain, never by abort (TRANSPORT-HTTP.md, Abort discipline).
191
- *
192
- * Framework-agnostic on purpose — only `rxjs` + `fast-check`, no `vitest` — so
193
- * it ships through `@semiont/core/testing` and any package's test runner can
194
- * invoke it. Deterministic virtual time: properties pass a small explicit
195
- * `timeoutMs` to `busRequest`; no `Date.now`, no 30 s real waits.
196
- */
197
-
198
- /** What one fresh run of the composition exposes to the axioms. */
199
- interface LivenessScenario {
200
- /**
201
- * Live-query-shaped outputs. The harness subscribes each one; every
202
- * subscription must see `next` or `error` within the bound (L1).
203
- */
204
- outputs: readonly Observable<unknown>[];
205
- /**
206
- * Awaited paths. Each promise must settle — resolve or reject — within the
207
- * bound (L2). Rejections are fine; pending-forever is the violation.
208
- */
209
- settlements?: readonly Promise<unknown>[];
210
- teardown?: () => void;
211
- }
212
- interface LivenessAxiomSpec {
213
- /** Build a FRESH composition wired to the given transport. Called per run. */
214
- setup: (transport: FaultyTransport) => LivenessScenario | Promise<LivenessScenario>;
215
- /**
216
- * The timeoutMs the scenario passes to `busRequest` — the bound is derived
217
- * from it: (timeoutMs × (1 + retryBudget) + Σdelays) × slackFactor.
218
- */
219
- timeoutMs: number;
220
- /** Max sanctioned re-issues per logical request (B14 budget). Default 1. */
221
- retryBudget?: number;
222
- /** Override the generated fault schedules (teeth tests pin one). */
223
- scheduleArb?: fc.Arbitrary<readonly FaultAction[]>;
224
- /** Passed through to FaultyTransport (reply synthesis). */
225
- makeResponse?: (operation: string, payload: Record<string, unknown>) => unknown;
226
- /** fast-check run budget (default 25 — CI-fast; crank locally). */
227
- numRuns?: number;
228
- /** Real-scheduler jitter headroom on the bound (default 4×). */
229
- slackFactor?: number;
230
- }
231
- /** The five wire behaviors, uniformly weighted; delays stay small (≤5 ms). */
232
- declare function arbFaultAction(): fc.Arbitrary<FaultAction>;
233
- declare function arbFaultSchedule(maxLength?: number): fc.Arbitrary<readonly FaultAction[]>;
234
- /**
235
- * Run L1 + L2 against `spec` across generated fault schedules. Throws a
236
- * labeled Error (`L1: …` / `L2: …`) on the first violation.
237
- */
238
- declare function assertLivenessAxioms(spec: LivenessAxiomSpec): Promise<void>;
239
- /**
240
- * A connection-stream-shaped subject: something that accepts writes to the
241
- * live connection, can be told to transition (handover / reconnect / scope
242
- * change), and exposes the subscriber-facing output. P3 adapts the real
243
- * actor's mock-connection harness to this shape; the teeth tests drive
244
- * reconstructed pre-fix doubles.
245
- */
246
- interface DeliverySubject {
247
- /** Write the event with this id to the currently-live connection. */
248
- write: (eventId: string) => void;
249
- /** Client-initiated lifecycle transition. */
250
- transition: () => void | Promise<void>;
251
- /** Subscriber-facing output; each emission is a delivered event id. */
252
- output$: Observable<string>;
253
- /**
254
- * Drain pending asynchronous delivery at end of sequence (a live connection
255
- * eventually flushes). Default: one macrotask tick.
256
- */
257
- settle?: () => Promise<void>;
258
- teardown?: () => void;
259
- }
260
- type DeliveryOp = 'write' | 'transition';
261
- interface DeliveryAxiomSpec {
262
- /** Build a FRESH subject. Called per run. */
263
- setup: () => DeliverySubject;
264
- /** Override the generated op sequences (teeth tests pin one). */
265
- opsArb?: fc.Arbitrary<readonly DeliveryOp[]>;
266
- /** Max generated sequence length (default 12). */
267
- maxOps?: number;
268
- /** fast-check run budget (default 50 — these runs are cheap). */
269
- numRuns?: number;
270
- }
271
- declare function arbDeliveryOps(maxOps?: number): fc.Arbitrary<readonly DeliveryOp[]>;
272
- /**
273
- * Run L3 against `spec` across generated write/transition interleavings.
274
- * Throws a labeled Error (`L3: …`) on the first violation: an event written
275
- * to a live connection delivered zero times (lost — retired by abort instead
276
- * of drain) or more than once (duplicate).
277
- */
278
- declare function assertExactlyOnceDelivery(spec: DeliveryAxiomSpec): Promise<void>;
279
-
280
- export { FaultyTransport, arbDeliveryOps, arbFaultAction, arbFaultSchedule, assertExactlyOnceDelivery, assertLivenessAxioms, assertStateUnitAxioms, disposeProbe, retryKeyOf };
281
- export type { DeliveryAxiomSpec, DeliveryOp, DeliverySubject, DisposeProbe, FaultAction, FaultyTransportConfig, LivenessAxiomSpec, LivenessScenario, RequestLogEntry, StateUnitAxiomSpec };
118
+ export { FaultyTransport, retryKeyOf };
119
+ export type { FaultAction, FaultyTransportConfig, RequestLogEntry };