@glubean/sdk 0.5.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/contract-artifacts.d.ts +39 -21
- package/dist/contract-artifacts.d.ts.map +1 -1
- package/dist/contract-artifacts.js +80 -24
- package/dist/contract-artifacts.js.map +1 -1
- package/dist/contract-core.d.ts +10 -21
- package/dist/contract-core.d.ts.map +1 -1
- package/dist/contract-core.js +26 -812
- package/dist/contract-core.js.map +1 -1
- package/dist/contract-http/adapter.d.ts +13 -0
- package/dist/contract-http/adapter.d.ts.map +1 -1
- package/dist/contract-http/adapter.js +191 -23
- package/dist/contract-http/adapter.js.map +1 -1
- package/dist/contract-http/factory.d.ts +2 -2
- package/dist/contract-http/factory.d.ts.map +1 -1
- package/dist/contract-http/factory.js +5 -0
- package/dist/contract-http/factory.js.map +1 -1
- package/dist/contract-http/inbound-match.d.ts +47 -0
- package/dist/contract-http/inbound-match.d.ts.map +1 -0
- package/dist/contract-http/inbound-match.js +136 -0
- package/dist/contract-http/inbound-match.js.map +1 -0
- package/dist/contract-http/inbound-verify.d.ts +46 -0
- package/dist/contract-http/inbound-verify.d.ts.map +1 -0
- package/dist/contract-http/inbound-verify.js +101 -0
- package/dist/contract-http/inbound-verify.js.map +1 -0
- package/dist/contract-http/inbound.d.ts +45 -0
- package/dist/contract-http/inbound.d.ts.map +1 -0
- package/dist/contract-http/inbound.js +89 -0
- package/dist/contract-http/inbound.js.map +1 -0
- package/dist/contract-http/index.d.ts +5 -0
- package/dist/contract-http/index.d.ts.map +1 -1
- package/dist/contract-http/index.js +3 -0
- package/dist/contract-http/index.js.map +1 -1
- package/dist/contract-http/openapi.d.ts +0 -7
- package/dist/contract-http/openapi.d.ts.map +1 -1
- package/dist/contract-http/openapi.js +20 -14
- package/dist/contract-http/openapi.js.map +1 -1
- package/dist/contract-http/types.d.ts +106 -2
- package/dist/contract-http/types.d.ts.map +1 -1
- package/dist/contract-http/types.js +33 -0
- package/dist/contract-http/types.js.map +1 -1
- package/dist/contract-types.d.ts +148 -446
- package/dist/contract-types.d.ts.map +1 -1
- package/dist/data-path.d.ts.map +1 -1
- package/dist/data-path.js +10 -2
- package/dist/data-path.js.map +1 -1
- package/dist/index.d.ts +10 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +7 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +14 -0
- package/dist/internal.js.map +1 -1
- package/dist/load/artifact.d.ts +376 -0
- package/dist/load/artifact.d.ts.map +1 -0
- package/dist/load/artifact.js +14 -0
- package/dist/load/artifact.js.map +1 -0
- package/dist/load/builder.d.ts +80 -0
- package/dist/load/builder.d.ts.map +1 -0
- package/dist/load/builder.js +262 -0
- package/dist/load/builder.js.map +1 -0
- package/dist/load/context.d.ts +81 -0
- package/dist/load/context.d.ts.map +1 -0
- package/dist/load/context.js +2 -0
- package/dist/load/context.js.map +1 -0
- package/dist/load/duration.d.ts +9 -0
- package/dist/load/duration.d.ts.map +1 -0
- package/dist/load/duration.js +22 -0
- package/dist/load/duration.js.map +1 -0
- package/dist/load/events.d.ts +132 -0
- package/dist/load/events.d.ts.map +1 -0
- package/dist/load/events.js +2 -0
- package/dist/load/events.js.map +1 -0
- package/dist/load/feeder.d.ts +118 -0
- package/dist/load/feeder.d.ts.map +1 -0
- package/dist/load/feeder.js +170 -0
- package/dist/load/feeder.js.map +1 -0
- package/dist/load/index.d.ts +32 -0
- package/dist/load/index.d.ts.map +1 -0
- package/dist/load/index.js +7 -0
- package/dist/load/index.js.map +1 -0
- package/dist/load/progress.d.ts +56 -0
- package/dist/load/progress.d.ts.map +1 -0
- package/dist/load/progress.js +2 -0
- package/dist/load/progress.js.map +1 -0
- package/dist/load/projection.d.ts +36 -0
- package/dist/load/projection.d.ts.map +1 -0
- package/dist/load/projection.js +47 -0
- package/dist/load/projection.js.map +1 -0
- package/dist/load/runner.d.ts +201 -0
- package/dist/load/runner.d.ts.map +1 -0
- package/dist/load/runner.js +78 -0
- package/dist/load/runner.js.map +1 -0
- package/dist/load/scenario.d.ts +130 -0
- package/dist/load/scenario.d.ts.map +1 -0
- package/dist/load/scenario.js +9 -0
- package/dist/load/scenario.js.map +1 -0
- package/dist/load/step.d.ts +42 -0
- package/dist/load/step.d.ts.map +1 -0
- package/dist/load/step.js +2 -0
- package/dist/load/step.js.map +1 -0
- package/dist/{contract-flow-poll.d.ts → poll-primitives.d.ts} +8 -81
- package/dist/poll-primitives.d.ts.map +1 -0
- package/dist/{contract-flow-poll.js → poll-primitives.js} +10 -64
- package/dist/poll-primitives.js.map +1 -0
- package/dist/{contract-flow-condition.d.ts → predicates.d.ts} +34 -71
- package/dist/predicates.d.ts.map +1 -0
- package/dist/{contract-flow-condition.js → predicates.js} +86 -80
- package/dist/predicates.js.map +1 -0
- package/dist/test/builder.js +2 -2
- package/dist/test/builder.js.map +1 -1
- package/dist/test/utils.d.ts +7 -0
- package/dist/test/utils.d.ts.map +1 -1
- package/dist/test/utils.js +22 -17
- package/dist/test/utils.js.map +1 -1
- package/dist/types.d.ts +42 -14
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/workflow/builder.d.ts +386 -0
- package/dist/workflow/builder.d.ts.map +1 -0
- package/dist/workflow/builder.js +1150 -0
- package/dist/workflow/builder.js.map +1 -0
- package/dist/workflow/execute.d.ts +277 -0
- package/dist/workflow/execute.d.ts.map +1 -0
- package/dist/workflow/execute.js +1489 -0
- package/dist/workflow/execute.js.map +1 -0
- package/dist/workflow/index.d.ts +11 -0
- package/dist/workflow/index.d.ts.map +1 -0
- package/dist/workflow/index.js +15 -0
- package/dist/workflow/index.js.map +1 -0
- package/dist/workflow/project.d.ts +18 -0
- package/dist/workflow/project.d.ts.map +1 -0
- package/dist/workflow/project.js +321 -0
- package/dist/workflow/project.js.map +1 -0
- package/dist/workflow/retry.d.ts +13 -0
- package/dist/workflow/retry.d.ts.map +1 -0
- package/dist/workflow/retry.js +15 -0
- package/dist/workflow/retry.js.map +1 -0
- package/dist/workflow/types.d.ts +512 -0
- package/dist/workflow/types.d.ts.map +1 -0
- package/dist/workflow/types.js +2 -0
- package/dist/workflow/types.js.map +1 -0
- package/package.json +9 -2
- package/dist/contract-flow-condition.d.ts.map +0 -1
- package/dist/contract-flow-condition.js.map +0 -1
- package/dist/contract-flow-poll.d.ts.map +0 -1
- package/dist/contract-flow-poll.js.map +0 -1
- package/dist/contract-http/flow-helpers.d.ts +0 -12
- package/dist/contract-http/flow-helpers.d.ts.map +0 -1
- package/dist/contract-http/flow-helpers.js +0 -34
- package/dist/contract-http/flow-helpers.js.map +0 -1
package/dist/contract-types.d.ts
CHANGED
|
@@ -17,10 +17,8 @@
|
|
|
17
17
|
* See `internal/40-discovery/proposals/contract-generics-complete.md` v5
|
|
18
18
|
* and `internal/40-discovery/proposals/contract-flow.md` v9 for design.
|
|
19
19
|
*/
|
|
20
|
-
import type { SchemaLike, Test, TestContext } from "./types.js";
|
|
20
|
+
import type { SchemaLike, SecretsAccessor, Test, TestContext } from "./types.js";
|
|
21
21
|
import type { KnownArtifacts, KnownArtifactParts, KnownArtifactOptions } from "./index.js";
|
|
22
|
-
import type { RuntimeBranchStep, ExtractedBranchStep, ExtractedPredicate, JsonScalar, BranchPredicate, PredicateScope } from "./contract-flow-condition.js";
|
|
23
|
-
import type { RuntimePollStep, ExtractedPollStep } from "./contract-flow-poll.js";
|
|
24
22
|
/**
|
|
25
23
|
* Case lifecycle.
|
|
26
24
|
*
|
|
@@ -261,6 +259,20 @@ export interface CaseMeta<PayloadSchemas = unknown, Meta = unknown> {
|
|
|
261
259
|
* the `rawBypass` execution path should appear on an overlay.
|
|
262
260
|
*/
|
|
263
261
|
hasNeeds?: boolean;
|
|
262
|
+
/**
|
|
263
|
+
* Direction of the case relative to the system under test. Absent =
|
|
264
|
+
* outbound (we call them — the default since contracts began). "inbound"
|
|
265
|
+
* = the counterparty calls us (inbound-contract-design §9.2); such cases
|
|
266
|
+
* are awaited via a workflow inbound poll, never executed.
|
|
267
|
+
*/
|
|
268
|
+
direction?: "inbound";
|
|
269
|
+
/**
|
|
270
|
+
* Set to `false` by adapters whose case cannot be executed as a Test
|
|
271
|
+
* (inbound-contract-design §9.5). The dispatcher then registers NO Test
|
|
272
|
+
* and NO runnable-inventory entry for it — the case exists only in
|
|
273
|
+
* projection/metadata. Absent = runnable (every pre-existing case).
|
|
274
|
+
*/
|
|
275
|
+
runnable?: boolean;
|
|
264
276
|
}
|
|
265
277
|
/**
|
|
266
278
|
* Runtime contract projection — adapter.project() output.
|
|
@@ -305,6 +317,20 @@ export interface ExtractedContractProjection<SafeSchemas = unknown, SafeMeta = u
|
|
|
305
317
|
cases: Array<ExtractedCaseMeta<SafeSchemas, SafeMeta>>;
|
|
306
318
|
schemas?: SafeSchemas;
|
|
307
319
|
meta?: SafeMeta;
|
|
320
|
+
/**
|
|
321
|
+
* Fully-qualified logical paths of schemas that were DECLARED but could not
|
|
322
|
+
* be projected to JSON Schema (the live `toJSONSchema()` threw, or the value
|
|
323
|
+
* was an unrecognized non-plain object). Distinct from "no schema declared":
|
|
324
|
+
* an absent schema does not appear here. Paths are dotted, e.g.
|
|
325
|
+
* `"request.body"`, `"cases.login.response.body"`, `"cases.login.needsSchema"`.
|
|
326
|
+
*
|
|
327
|
+
* The schema VALUE stays `undefined` in the projection (so OpenAPI / MCP /
|
|
328
|
+
* descriptors degrade exactly as before); this list is the explicit record
|
|
329
|
+
* a snapshot consumer uses to compute `projectionComplete` (false when
|
|
330
|
+
* non-empty) and warn about the specific holes. Undefined/omitted when every
|
|
331
|
+
* declared schema projected cleanly.
|
|
332
|
+
*/
|
|
333
|
+
unprojectableSchemas?: string[];
|
|
308
334
|
}
|
|
309
335
|
/**
|
|
310
336
|
* Protocol adapter interface. Implement to add support for a new protocol
|
|
@@ -423,7 +449,7 @@ export interface ContractProtocolAdapter<Spec = unknown, RuntimeSchemas = unknow
|
|
|
423
449
|
* protocol-specific artifacts (OpenAPI is HTTP-only).
|
|
424
450
|
*/
|
|
425
451
|
artifacts?: {
|
|
426
|
-
[K in keyof KnownArtifacts]?: (projection: ExtractedContractProjection<SafeSchemas, SafeMeta>, options?: KnownArtifactOptions[K]) => KnownArtifactParts[K];
|
|
452
|
+
[K in keyof KnownArtifacts]?: (projection: ExtractedContractProjection<SafeSchemas, SafeMeta>, options?: KnownArtifactOptions[K]) => KnownArtifactParts[K] | null;
|
|
427
453
|
};
|
|
428
454
|
/**
|
|
429
455
|
* Optional: render the `target` string for display. HTTP: "POST /users"
|
|
@@ -443,7 +469,7 @@ export interface ContractProtocolAdapter<Spec = unknown, RuntimeSchemas = unknow
|
|
|
443
469
|
* 1. Computed `resolvedInputs` via `step.bindings.in(state)`
|
|
444
470
|
* 2. Prepared current flow state
|
|
445
471
|
* 3. **v10 (for migrated protocols)**: validated `resolvedInputs` against
|
|
446
|
-
* the case's `needs` schema via `
|
|
472
|
+
* the case's `needs` schema via `validateNeedsOutput`, so
|
|
447
473
|
* the adapter receives already-validated logical input.
|
|
448
474
|
* 4. Passed the live contract instance (access merged scoped-factory state
|
|
449
475
|
* via `contract._spec`)
|
|
@@ -505,6 +531,51 @@ export interface ContractProtocolAdapter<Spec = unknown, RuntimeSchemas = unknow
|
|
|
505
531
|
* Throws on invalid case; returns undefined on success.
|
|
506
532
|
*/
|
|
507
533
|
validateCaseForFlow?: (spec: Spec, caseKey: string, contractId: string) => void;
|
|
534
|
+
/**
|
|
535
|
+
* Optional: classify ONE delivery against an inbound case
|
|
536
|
+
* (inbound-contract-design §9.4/§9.4a). Called by the workflow inbound
|
|
537
|
+
* poll once per unclaimed delivery, in receiver order; the FIRST terminal
|
|
538
|
+
* classification wins (consult #4). Authentication decides failure;
|
|
539
|
+
* content decides attribution: fail-class results come only from the
|
|
540
|
+
* channel (signature / staleness / non-JSON), mismatches are probes.
|
|
541
|
+
*
|
|
542
|
+
* Preflight errors (unknown verifier scheme, missing secret) MUST throw —
|
|
543
|
+
* bad config must never hide as a probe (§9.4 preflight row).
|
|
544
|
+
*
|
|
545
|
+
* The poll resolves the state side itself (it owns the state) and hands
|
|
546
|
+
* the matcher the concrete value + the event's EXTRACTED selector path —
|
|
547
|
+
* a path, not the lens, because the matcher must walk unrelated webhook
|
|
548
|
+
* shapes safely: `e => e.data.id` THROWS on a body without `data`, while
|
|
549
|
+
* a safe path walk yields `undefined` → `type-mismatch` probe (codex I3
|
|
550
|
+
* R1 P2).
|
|
551
|
+
*
|
|
552
|
+
* The staleness clock for timestamped signature schemes is the delivery's
|
|
553
|
+
* own `receivedAt` — verification must reflect RECEIPT time, not whenever
|
|
554
|
+
* the poll happened to scan the inbox, or a pre-existing valid delivery
|
|
555
|
+
* (allowed evidence, §9.4a #3) would misclassify as stale after a slow
|
|
556
|
+
* setup (codex I3 R2 P2).
|
|
557
|
+
*/
|
|
558
|
+
matchInboundCase?: (input: {
|
|
559
|
+
caseSpec: unknown;
|
|
560
|
+
delivery: InboundDelivery;
|
|
561
|
+
secrets: SecretsAccessor;
|
|
562
|
+
correlate?: {
|
|
563
|
+
eventPath: readonly string[];
|
|
564
|
+
stateValue: unknown;
|
|
565
|
+
};
|
|
566
|
+
}) => InboundMatchResult;
|
|
567
|
+
/**
|
|
568
|
+
* Optional: validate inbound-case CONFIG without a delivery — throw on an
|
|
569
|
+
* unknown verifier scheme / missing secret (§9.4 preflight row). The poll
|
|
570
|
+
* calls this every attempt BEFORE reading the receiver: an empty inbox
|
|
571
|
+
* must not let bad config exhaust as a timeout (codex I3 R3 P2 — the
|
|
572
|
+
* matcher's own preflight only runs when a delivery exists). Adapters
|
|
573
|
+
* implementing matchInboundCase should implement this too.
|
|
574
|
+
*/
|
|
575
|
+
preflightInboundCase?: (input: {
|
|
576
|
+
caseSpec: unknown;
|
|
577
|
+
secrets: SecretsAccessor;
|
|
578
|
+
}) => void;
|
|
508
579
|
}
|
|
509
580
|
/**
|
|
510
581
|
* Protocol-agnostic payload summary. Adapter-provided via describePayload.
|
|
@@ -554,14 +625,20 @@ export interface ProtocolContract<Spec = unknown, PayloadSchemas = unknown, Meta
|
|
|
554
625
|
*/
|
|
555
626
|
readonly _spec: Spec;
|
|
556
627
|
/**
|
|
557
|
-
* Return a ContractCaseRef for use in `
|
|
628
|
+
* Return a ContractCaseRef for use in `workflow().call/.poll(...)`.
|
|
558
629
|
*
|
|
559
630
|
* Runtime validation: adapter's `.case(key)` implementation MUST fail-fast
|
|
560
631
|
* if the case contains function-valued input fields (body/params/query/
|
|
561
632
|
* headers as functions). Function fields reference case-local setup state
|
|
562
633
|
* which is not available in flow mode. See contract-flow §5.1.1.
|
|
563
634
|
*/
|
|
564
|
-
case<K extends keyof Cases & string>(key: K): ContractCaseRef<InferCaseInput<Cases[K]>, ApplyCaseOutput<PayloadSchemas, Cases[K]>, InferAcceptKey<PayloadSchemas>, InferRawOutcome<PayloadSchemas, InferOutput<PayloadSchemas
|
|
635
|
+
case<K extends keyof Cases & string>(key: K): ContractCaseRef<InferCaseInput<Cases[K]>, ApplyCaseOutput<PayloadSchemas, Cases[K]>, InferAcceptKey<PayloadSchemas>, InferRawOutcome<PayloadSchemas, InferOutput<PayloadSchemas>>> & (Cases[K] extends {
|
|
636
|
+
direction: "inbound";
|
|
637
|
+
} ? {
|
|
638
|
+
readonly direction: "inbound";
|
|
639
|
+
} : {
|
|
640
|
+
readonly direction?: undefined;
|
|
641
|
+
});
|
|
565
642
|
}
|
|
566
643
|
/**
|
|
567
644
|
* Adapter-defined helper: extract the "case inputs" shape from PayloadSchemas.
|
|
@@ -642,6 +719,66 @@ export type InferAcceptKey<P> = P extends {
|
|
|
642
719
|
export type InferRawOutcome<P, Fallback = unknown> = P extends {
|
|
643
720
|
readonly __rawOutcome?: infer R;
|
|
644
721
|
} ? [unknown] extends [R] ? Fallback : NonNullable<R> : Fallback;
|
|
722
|
+
/**
|
|
723
|
+
* One delivery as received — RAW. `bodyBytes` is the exact byte sequence the
|
|
724
|
+
* counterparty sent: HMAC-style signatures are computed over raw bytes, so
|
|
725
|
+
* any parsing or decoding before verification would destroy the evidence
|
|
726
|
+
* (design §9.1, blind spot 3). Protocol-agnostic: core's inbound-poll
|
|
727
|
+
* machinery and adapter `matchInboundCase` hooks both consume it (the
|
|
728
|
+
* zero-dependency local implementation lives in `contract-http/inbound.ts`).
|
|
729
|
+
*/
|
|
730
|
+
export interface InboundDelivery {
|
|
731
|
+
/** Receiver-assigned, unique per delivery (claim key). */
|
|
732
|
+
id: string;
|
|
733
|
+
/** EXACT body bytes as received — THE signature input. */
|
|
734
|
+
bodyBytes: Uint8Array;
|
|
735
|
+
/**
|
|
736
|
+
* UTF-8 decoded view of `bodyBytes`, for JSON parsing and display. LOSSY
|
|
737
|
+
* for non-UTF-8 payloads — never feed this to a signature verifier.
|
|
738
|
+
*/
|
|
739
|
+
rawBody: string;
|
|
740
|
+
/** Header names lowercased. */
|
|
741
|
+
headers: Record<string, string>;
|
|
742
|
+
method: string;
|
|
743
|
+
path: string;
|
|
744
|
+
/** Epoch ms at receipt — the measured side of `within` evidence. */
|
|
745
|
+
receivedAt: number;
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* The receiver protocol an inbound poll (`.poll(ref, { via })`) consumes
|
|
749
|
+
* (design §9.1). NON-DESTRUCTIVE by construction: matching inspects
|
|
750
|
+
* `deliveries()` snapshots and `claim()`s only the matched delivery — other
|
|
751
|
+
* consumers' events are never swallowed by a failed match.
|
|
752
|
+
*
|
|
753
|
+
* ONE handle corresponds to ONE endpoint/secret domain (design §9.4):
|
|
754
|
+
* the matcher never checks method/path — full grade means "received through
|
|
755
|
+
* THIS handle" (consult #7), so multi-source channels must be split into
|
|
756
|
+
* per-domain handles at the receiver layer.
|
|
757
|
+
*/
|
|
758
|
+
export interface ReceiverHandle {
|
|
759
|
+
/** Snapshot of UNCLAIMED deliveries, oldest first. */
|
|
760
|
+
deliveries(): readonly InboundDelivery[];
|
|
761
|
+
/** Mark one delivery consumed (idempotent; unknown ids are a no-op). */
|
|
762
|
+
claim(id: string): void;
|
|
763
|
+
/** The URL the counterparty should be pointed at. */
|
|
764
|
+
url: string;
|
|
765
|
+
close(): Promise<void>;
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Result of an adapter's `matchInboundCase` over ONE delivery (§9.4/§9.4a).
|
|
769
|
+
* Fail-class results (signature-invalid / stale / unparseable) FAIL the poll
|
|
770
|
+
* node; mismatch results are probes whose labels exist for diagnosis;
|
|
771
|
+
* `matched` claims the delivery and satisfies the poll.
|
|
772
|
+
*/
|
|
773
|
+
export type InboundMatchResult = {
|
|
774
|
+
kind: "matched";
|
|
775
|
+
parsed: unknown;
|
|
776
|
+
} | {
|
|
777
|
+
kind: "type-mismatch" | "correlation-mismatch" | "schema-mismatch";
|
|
778
|
+
} | {
|
|
779
|
+
kind: "signature-invalid" | "stale" | "unparseable";
|
|
780
|
+
detail?: string;
|
|
781
|
+
};
|
|
645
782
|
/**
|
|
646
783
|
* Opaque reference to a single case of a contract. Produced by
|
|
647
784
|
* `ProtocolContract.case(key)`. Used as input to `FlowBuilder.step(...)`.
|
|
@@ -665,28 +802,6 @@ export interface ContractCaseRef<CaseInputs = unknown, CaseOutput = unknown, Acc
|
|
|
665
802
|
/** Phantom — `out`'s `res` type when `accept` is present (adapter raw outcome). */
|
|
666
803
|
readonly __phantom_raw_outcome?: RawOutcome;
|
|
667
804
|
}
|
|
668
|
-
/** Contract-level metadata for a flow (set via `.meta()`). */
|
|
669
|
-
export interface FlowMeta {
|
|
670
|
-
id: string;
|
|
671
|
-
name?: string;
|
|
672
|
-
description?: string;
|
|
673
|
-
tags?: string[];
|
|
674
|
-
extensions?: Extensions;
|
|
675
|
-
/**
|
|
676
|
-
* Mark this flow as skipped at run time. Value is the skip reason
|
|
677
|
-
* displayed in reports. Useful for illustrative examples that should
|
|
678
|
-
* be discoverable (for scanner extraction / docs rendering) but must
|
|
679
|
-
* not attempt live HTTP calls.
|
|
680
|
-
*
|
|
681
|
-
* Mirrors `TestMeta.skip`.
|
|
682
|
-
*/
|
|
683
|
-
skip?: string;
|
|
684
|
-
/**
|
|
685
|
-
* Mark this flow as focused. When any flows/tests in a run are `only`,
|
|
686
|
-
* non-focused ones may be excluded. Mirrors `TestMeta.only`.
|
|
687
|
-
*/
|
|
688
|
-
only?: boolean;
|
|
689
|
-
}
|
|
690
805
|
/**
|
|
691
806
|
* Field dependency / mapping produced by Proxy dry-run of a lens function.
|
|
692
807
|
* Consumed by downstream (MCP/CLI/Cloud) to render flow data-flow diagrams.
|
|
@@ -706,371 +821,14 @@ export interface FieldMapping {
|
|
|
706
821
|
};
|
|
707
822
|
}
|
|
708
823
|
/**
|
|
709
|
-
*
|
|
710
|
-
*
|
|
711
|
-
*
|
|
712
|
-
|
|
713
|
-
export type RuntimeFlowStep = RuntimeContractCallStep | RuntimeComputeStep | RuntimeBranchStep | RuntimePollStep;
|
|
714
|
-
export interface RuntimeContractCallStep {
|
|
715
|
-
kind: "contract-call";
|
|
716
|
-
name?: string;
|
|
717
|
-
ref: ContractCaseRef<any, any, any, any>;
|
|
718
|
-
caseKey: string;
|
|
719
|
-
/** Live contract instance (mirrors ref.contract, kept for direct access). */
|
|
720
|
-
contract: ProtocolContract<any, any, any>;
|
|
721
|
-
bindings?: {
|
|
722
|
-
in?: (state: any) => any;
|
|
723
|
-
out?: (state: any, response: any) => any;
|
|
724
|
-
/**
|
|
725
|
-
* Accepted alternate outcome keys (HTTP: status numbers). When the actual
|
|
726
|
-
* outcome key is in this list AND differs from the case's primary expected
|
|
727
|
-
* outcome, the adapter treats it as a legal outcome: it skips the case's
|
|
728
|
-
* primary result validation (schema / headers / verify) and hands the RAW
|
|
729
|
-
* outcome to `out`, so a condition can branch on it. Protocol-agnostic in
|
|
730
|
-
* core; the adapter interprets the element type.
|
|
731
|
-
*/
|
|
732
|
-
accept?: readonly unknown[];
|
|
733
|
-
};
|
|
734
|
-
}
|
|
735
|
-
export interface RuntimeComputeStep {
|
|
736
|
-
kind: "compute";
|
|
737
|
-
name?: string;
|
|
738
|
-
/**
|
|
739
|
-
* Synchronous pure function. NOT subject to lens Proxy purity — may use
|
|
740
|
-
* template literals / method calls / .map(). MUST be synchronous and
|
|
741
|
-
* MUST NOT return a thenable (enforced at runtime).
|
|
742
|
-
*/
|
|
743
|
-
fn: (state: any) => any;
|
|
744
|
-
}
|
|
745
|
-
/**
|
|
746
|
-
* Runtime flow projection. Carries live callbacks + live contract refs.
|
|
747
|
-
* Never crosses serialization boundaries. `normalizeFlow()` converts to
|
|
748
|
-
* ExtractedFlowProjection for downstream consumers.
|
|
749
|
-
*/
|
|
750
|
-
export interface RuntimeFlowProjection<State = unknown> {
|
|
751
|
-
protocol: "flow";
|
|
752
|
-
description?: string;
|
|
753
|
-
tags?: string[];
|
|
754
|
-
extensions?: Extensions;
|
|
755
|
-
/** Mirrors `FlowMeta.skip` — skip reason for discoverable-but-not-runnable flows. */
|
|
756
|
-
skip?: string;
|
|
757
|
-
/** Mirrors `FlowMeta.only` — focus filter. */
|
|
758
|
-
only?: boolean;
|
|
759
|
-
/** Live flow-level setup callback (only I/O-capable callback in flow). */
|
|
760
|
-
setup?: (ctx: TestContext) => Promise<State>;
|
|
761
|
-
/** Live flow-level teardown callback. Rule 2: outer finally. */
|
|
762
|
-
teardown?: (ctx: TestContext, state: State) => Promise<void>;
|
|
763
|
-
steps: RuntimeFlowStep[];
|
|
764
|
-
}
|
|
765
|
-
/**
|
|
766
|
-
* Extracted flow step — discriminated union, JSON-safe.
|
|
767
|
-
*/
|
|
768
|
-
export type ExtractedFlowStep = ExtractedContractCallStep | ExtractedComputeStep | ExtractedBranchStep | ExtractedPollStep;
|
|
769
|
-
export interface ExtractedContractCallStep {
|
|
770
|
-
kind: "contract-call";
|
|
771
|
-
name?: string;
|
|
772
|
-
contractId: string;
|
|
773
|
-
caseKey: string;
|
|
774
|
-
protocol: string;
|
|
775
|
-
target: string;
|
|
776
|
-
/** Proxy dry-run output — input mappings. */
|
|
777
|
-
inputs?: FieldMapping[];
|
|
778
|
-
/** Proxy dry-run output — state update mappings. */
|
|
779
|
-
outputs?: FieldMapping[];
|
|
780
|
-
/**
|
|
781
|
-
* Accepted alternate outcome keys (HTTP: status numbers) — present when the
|
|
782
|
-
* step declared `accept`. Surfaces the multi-status contract to downstream
|
|
783
|
-
* consumers (scanner / MCP / CLI / Cloud) so a status-gated step is not
|
|
784
|
-
* indistinguishable from a single-primary-status one.
|
|
785
|
-
*/
|
|
786
|
-
accept?: ReadonlyArray<string | number>;
|
|
787
|
-
}
|
|
788
|
-
export interface ExtractedComputeStep {
|
|
789
|
-
kind: "compute";
|
|
790
|
-
name?: string;
|
|
791
|
-
/** Top-level state paths read (from Proxy dry-run). */
|
|
792
|
-
reads: string[];
|
|
793
|
-
/** Top-level state keys written (keys of returned object). */
|
|
794
|
-
writes: string[];
|
|
795
|
-
}
|
|
796
|
-
/**
|
|
797
|
-
* JSON-safe flow projection. Downstream (scanner / MCP / CLI / Cloud) consume
|
|
798
|
-
* this. Produced by `normalizeFlow(runtime)`.
|
|
799
|
-
*/
|
|
800
|
-
export interface ExtractedFlowProjection {
|
|
801
|
-
id: string;
|
|
802
|
-
protocol: "flow";
|
|
803
|
-
description?: string;
|
|
804
|
-
tags?: string[];
|
|
805
|
-
extensions?: Extensions;
|
|
806
|
-
/** Mirrors `FlowMeta.skip`. */
|
|
807
|
-
skip?: string;
|
|
808
|
-
/** Mirrors `FlowMeta.only`. */
|
|
809
|
-
only?: boolean;
|
|
810
|
-
/** Present when flow has a setup callback (state source is dynamic). */
|
|
811
|
-
setupDynamic?: true;
|
|
812
|
-
steps: ExtractedFlowStep[];
|
|
813
|
-
}
|
|
814
|
-
/**
|
|
815
|
-
* Base options shared by `poll` / `pollFn` / `pollAsync`. `Res` switches on
|
|
816
|
-
* `accept` exactly like `step` (no accept → CaseOutput; accept → RawOutcome).
|
|
817
|
-
* `accept?: Accept` is what drives the `Accept` inference. Bound semantics +
|
|
818
|
-
* the build-time rules live in `validatePollBounds`. (Spike P.)
|
|
819
|
-
*/
|
|
820
|
-
export interface PollOptsBase<State, Res, NewState, Accept extends readonly unknown[]> {
|
|
821
|
-
/** The exit (satisfying) response written into state. */
|
|
822
|
-
out?: (state: State, response: Res) => NewState;
|
|
823
|
-
/** Accepted alternate outcome keys (HTTP: status numbers) — drives `Accept` inference. */
|
|
824
|
-
accept?: Accept;
|
|
825
|
-
name?: string;
|
|
826
|
-
/** Total wall-clock bound (ms). */
|
|
827
|
-
timeout?: number;
|
|
828
|
-
/** Max attempts (incl. the first). */
|
|
829
|
-
maxAttempts?: number;
|
|
830
|
-
/** Per-attempt budget (ms) — required when `timeout` is absent. */
|
|
831
|
-
perAttemptTimeout?: number;
|
|
832
|
-
/** Interval between attempts (ms, default 1000). */
|
|
833
|
-
every?: number;
|
|
834
|
-
/** Backoff multiplier applied to `every` after each retry (default 1, capped). */
|
|
835
|
-
backoff?: number;
|
|
836
|
-
}
|
|
837
|
-
/**
|
|
838
|
-
* Poll argument tuple. Mirrors `step`'s conditional tuple: `in` is REQUIRED for
|
|
839
|
-
* a needs-input case and FORBIDDEN for a void-input case. `Exit` is each tier's
|
|
840
|
-
* `{ until, message }` fragment (L2 declarative vs opaque). (Spike P.)
|
|
841
|
-
*/
|
|
842
|
-
export type PollArgs<State, CaseInputs, Res, NewState, Accept extends readonly unknown[], Exit> = [
|
|
843
|
-
CaseInputs
|
|
844
|
-
] extends [void] ? [opts: PollOptsBase<State, Res, NewState, Accept> & Exit] : [opts: PollOptsBase<State, Res, NewState, Accept> & Exit & {
|
|
845
|
-
in: (state: State) => CaseInputs;
|
|
846
|
-
}];
|
|
847
|
-
/**
|
|
848
|
-
* Builder for `contract.flow(id)`. State chain threads through `.step()` /
|
|
849
|
-
* `.compute()` via TypeScript generics.
|
|
850
|
-
*/
|
|
851
|
-
export interface FlowBuilder<State = unknown> {
|
|
852
|
-
readonly __glubean_type: "flow-builder";
|
|
853
|
-
meta(m: Omit<FlowMeta, "id">): FlowBuilder<State>;
|
|
854
|
-
/**
|
|
855
|
-
* Flow-level setup — the ONLY I/O-capable callback in a flow. May be async,
|
|
856
|
-
* may read ctx, may call external services. Returns the initial state.
|
|
857
|
-
*/
|
|
858
|
-
setup<NewState>(fn: (ctx: TestContext) => Promise<NewState>): FlowBuilder<NewState>;
|
|
859
|
-
/**
|
|
860
|
-
* Add a contract-call step. `bindings.in` and `bindings.out` MUST be pure
|
|
861
|
-
* lens functions (select / repack only; no I/O, no method calls, no
|
|
862
|
-
* branching). Lens purity is enforced at Proxy dry-run time during
|
|
863
|
-
* projection extraction.
|
|
864
|
-
*
|
|
865
|
-
* Single signature with rest-parameter conditional tuple (Spike 0 Finding 3).
|
|
866
|
-
* Two-overload form (historically tried) has a subtle hole: overload 1
|
|
867
|
-
* (void-input, no `in`) excess-property-fails when `in` is passed → TS
|
|
868
|
-
* falls through to overload 2, which accepts because `() => X` is
|
|
869
|
-
* bivariant-assignable to `() => void`. Conditional tuple is a single
|
|
870
|
-
* signature so TS doesn't get a second chance:
|
|
871
|
-
* - void-input case → `bindings` optional; `in` not present in the
|
|
872
|
-
* allowed shape
|
|
873
|
-
* - typed-input case → `bindings.in` REQUIRED
|
|
874
|
-
*
|
|
875
|
-
* In v10, `in` returns LOGICAL case input (matches the case's `needs`),
|
|
876
|
-
* NOT an adapter patch. HTTP adapter's `executeCaseInFlow` calls
|
|
877
|
-
* function-valued body/headers/params/query with this logical input
|
|
878
|
-
* (same mechanism as standalone `executeStandaloneCase`).
|
|
879
|
-
*/
|
|
880
|
-
step<CaseInputs, CaseOutput, AcceptKey, RawOutcome, Accept extends readonly AcceptKey[] = never, NewState = State>(ref: ContractCaseRef<CaseInputs, CaseOutput, AcceptKey, RawOutcome>, ...args: [CaseInputs] extends [void] ? [
|
|
881
|
-
bindings?: {
|
|
882
|
-
accept?: Accept;
|
|
883
|
-
out?: (state: State, response: [Accept] extends [never] ? CaseOutput : RawOutcome) => NewState;
|
|
884
|
-
name?: string;
|
|
885
|
-
}
|
|
886
|
-
] : [
|
|
887
|
-
bindings: {
|
|
888
|
-
in: (state: State) => CaseInputs;
|
|
889
|
-
accept?: Accept;
|
|
890
|
-
out?: (state: State, response: [Accept] extends [never] ? CaseOutput : RawOutcome) => NewState;
|
|
891
|
-
name?: string;
|
|
892
|
-
}
|
|
893
|
-
]): FlowBuilder<NewState>;
|
|
894
|
-
/**
|
|
895
|
-
* Add a pure synchronous data-transform step. Accepts any synchronous TS
|
|
896
|
-
* expression (template literals, method calls, .map()). Projection records
|
|
897
|
-
* only read/write dependencies, NOT the formula.
|
|
898
|
-
*
|
|
899
|
-
* Runtime enforcement: throws if `fn` is async or returns a thenable.
|
|
900
|
-
*/
|
|
901
|
-
compute<NewState>(fn: (state: State) => NewState): FlowBuilder<NewState>;
|
|
902
|
-
/**
|
|
903
|
-
* Flow-level teardown — runs in Rule 2 outer-finally, receives last-
|
|
904
|
-
* committed state. If flow.setup threw, teardown does NOT run.
|
|
905
|
-
*/
|
|
906
|
-
teardown(fn: (ctx: TestContext, state: State) => Promise<void>): FlowBuilder<State>;
|
|
907
|
-
/** L2 — declarative predicate (precisely projectable). No else → then keeps State's shape (values may narrow). */
|
|
908
|
-
condition<R extends State = State>(spec: {
|
|
909
|
-
predicate: (w: PredicateScope<State>) => BranchPredicate<State>;
|
|
910
|
-
message?: string;
|
|
911
|
-
}, thenBranch: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<R & NoExtraKeys<R, State>>): FlowBuilder<State>;
|
|
912
|
-
/** L2 with else — `T` is inferred from `then`; `else` is checked against it (`NoInfer`). */
|
|
913
|
-
condition<T>(spec: {
|
|
914
|
-
predicate: (w: PredicateScope<State>) => BranchPredicate<State>;
|
|
915
|
-
message?: string;
|
|
916
|
-
}, thenBranch: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<T>, elseBranch: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<NoInfer<T>>): FlowBuilder<T>;
|
|
917
|
-
/** L1 — opaque sync predicate. `message` is required (projection marks ⚠ opaque-gate). */
|
|
918
|
-
conditionFn<R extends State = State>(spec: {
|
|
919
|
-
predicate: (ctx: TestContext, s: State) => boolean;
|
|
920
|
-
message: string;
|
|
921
|
-
}, thenBranch: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<R & NoExtraKeys<R, State>>): FlowBuilder<State>;
|
|
922
|
-
conditionFn<T>(spec: {
|
|
923
|
-
predicate: (ctx: TestContext, s: State) => boolean;
|
|
924
|
-
message: string;
|
|
925
|
-
}, thenBranch: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<T>, elseBranch: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<NoInfer<T>>): FlowBuilder<T>;
|
|
926
|
-
/** L0 — opaque async predicate (may do out-of-contract I/O). `message` required (⚠ dynamic-gate). */
|
|
927
|
-
conditionAsync<R extends State = State>(spec: {
|
|
928
|
-
predicate: (ctx: TestContext, s: State) => Promise<boolean>;
|
|
929
|
-
message: string;
|
|
930
|
-
}, thenBranch: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<R & NoExtraKeys<R, State>>): FlowBuilder<State>;
|
|
931
|
-
conditionAsync<T>(spec: {
|
|
932
|
-
predicate: (ctx: TestContext, s: State) => Promise<boolean>;
|
|
933
|
-
message: string;
|
|
934
|
-
}, thenBranch: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<T>, elseBranch: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<NoInfer<T>>): FlowBuilder<T>;
|
|
935
|
-
/**
|
|
936
|
-
* value-switch (single lens × N scalar values). Curried so `V` infers from
|
|
937
|
-
* the lens alone (Spike 0): first call fixes `V`, second checks each `value`
|
|
938
|
-
* against it. `default` is required (anchors `T`; the no-match path). switch
|
|
939
|
-
* is always L2 — a clean decision table.
|
|
940
|
-
*/
|
|
941
|
-
switchOn<V>(lens: (s: State) => V): <T>(cases: ReadonlyArray<{
|
|
942
|
-
value: [Exclude<V, undefined>] extends [JsonScalar] ? Exclude<V, undefined> : never;
|
|
943
|
-
then: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<NoInfer<T>>;
|
|
944
|
-
}>, deflt: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<T>) => FlowBuilder<T>;
|
|
945
|
-
/** cond-switch (ordered, possibly-overlapping declarative predicates; first-match). */
|
|
946
|
-
switchCond<T>(cases: ReadonlyArray<{
|
|
947
|
-
when: (w: PredicateScope<State>) => BranchPredicate<State>;
|
|
948
|
-
then: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<NoInfer<T>>;
|
|
949
|
-
}>, deflt: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<T>): FlowBuilder<T>;
|
|
950
|
-
/** L2 — declarative exit predicate over the response (precisely projectable). poll-on-status needs `accept`. */
|
|
951
|
-
poll<CaseInputs, CaseOutput, AcceptKey, RawOutcome, Accept extends readonly AcceptKey[] = never, NewState = State>(ref: ContractCaseRef<CaseInputs, CaseOutput, AcceptKey, RawOutcome>, ...args: PollArgs<State, CaseInputs, [Accept] extends [never] ? CaseOutput : RawOutcome, NewState, Accept, {
|
|
952
|
-
until: (w: PredicateScope<[Accept] extends [never] ? CaseOutput : RawOutcome>) => BranchPredicate<[Accept] extends [never] ? CaseOutput : RawOutcome>;
|
|
953
|
-
message?: string;
|
|
954
|
-
}>): FlowBuilder<NewState>;
|
|
955
|
-
/** L1 — opaque sync exit predicate (gets ctx, res, state). `message` required (marked gate). */
|
|
956
|
-
pollFn<CaseInputs, CaseOutput, AcceptKey, RawOutcome, Accept extends readonly AcceptKey[] = never, NewState = State>(ref: ContractCaseRef<CaseInputs, CaseOutput, AcceptKey, RawOutcome>, ...args: PollArgs<State, CaseInputs, [Accept] extends [never] ? CaseOutput : RawOutcome, NewState, Accept, {
|
|
957
|
-
until: (ctx: TestContext, res: [Accept] extends [never] ? CaseOutput : RawOutcome, state: State) => boolean;
|
|
958
|
-
message: string;
|
|
959
|
-
}>): FlowBuilder<NewState>;
|
|
960
|
-
/** L0 — opaque async exit predicate (may do contract-external I/O). `message` required (marked gate). */
|
|
961
|
-
pollAsync<CaseInputs, CaseOutput, AcceptKey, RawOutcome, Accept extends readonly AcceptKey[] = never, NewState = State>(ref: ContractCaseRef<CaseInputs, CaseOutput, AcceptKey, RawOutcome>, ...args: PollArgs<State, CaseInputs, [Accept] extends [never] ? CaseOutput : RawOutcome, NewState, Accept, {
|
|
962
|
-
until: (ctx: TestContext, res: [Accept] extends [never] ? CaseOutput : RawOutcome, state: State) => Promise<boolean>;
|
|
963
|
-
message: string;
|
|
964
|
-
}>): FlowBuilder<NewState>;
|
|
965
|
-
build(): FlowContract<State>;
|
|
966
|
-
}
|
|
967
|
-
/**
|
|
968
|
-
* Restricted builder for a branch body (condition then/else, switch case/default).
|
|
969
|
-
* Accumulates step / compute / nested branch into the branch node's
|
|
970
|
-
* `cases[].steps` / `default`; it deliberately does NOT expose flow-level
|
|
971
|
-
* setup / teardown / meta / build.
|
|
972
|
-
*
|
|
973
|
-
* INVARIANT BRAND (Spike 0 / §8.1): `State` appears in both covariant (return)
|
|
974
|
-
* and contravariant (parameter) position via the phantom `[FRAGMENT_STATE]`
|
|
975
|
-
* field, making `FlowFragmentBuilder` *invariant* in `State`. Without this,
|
|
976
|
-
* TS structural width-subtyping would let `FlowFragmentBuilder<{...State, x}>`
|
|
977
|
-
* be assignable to `FlowFragmentBuilder<State>`, so a no-else branch could
|
|
978
|
-
* silently add fields (or an else could return a `T` supertype) and bypass
|
|
979
|
-
* convergence. The phantom is type-only — never read at runtime.
|
|
980
|
-
*/
|
|
981
|
-
export interface FlowFragmentBuilder<State = unknown> {
|
|
982
|
-
readonly [FRAGMENT_STATE]: (s: State) => State;
|
|
983
|
-
step<CaseInputs, CaseOutput, AcceptKey, RawOutcome, Accept extends readonly AcceptKey[] = never, NewState = State>(ref: ContractCaseRef<CaseInputs, CaseOutput, AcceptKey, RawOutcome>, ...args: [CaseInputs] extends [void] ? [
|
|
984
|
-
bindings?: {
|
|
985
|
-
accept?: Accept;
|
|
986
|
-
out?: (state: State, response: [Accept] extends [never] ? CaseOutput : RawOutcome) => NewState;
|
|
987
|
-
name?: string;
|
|
988
|
-
}
|
|
989
|
-
] : [
|
|
990
|
-
bindings: {
|
|
991
|
-
in: (state: State) => CaseInputs;
|
|
992
|
-
accept?: Accept;
|
|
993
|
-
out?: (state: State, response: [Accept] extends [never] ? CaseOutput : RawOutcome) => NewState;
|
|
994
|
-
name?: string;
|
|
995
|
-
}
|
|
996
|
-
]): FlowFragmentBuilder<NewState>;
|
|
997
|
-
compute<NewState>(fn: (state: State) => NewState): FlowFragmentBuilder<NewState>;
|
|
998
|
-
condition<R extends State = State>(spec: {
|
|
999
|
-
predicate: (w: PredicateScope<State>) => BranchPredicate<State>;
|
|
1000
|
-
message?: string;
|
|
1001
|
-
}, thenBranch: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<R & NoExtraKeys<R, State>>): FlowFragmentBuilder<State>;
|
|
1002
|
-
condition<T>(spec: {
|
|
1003
|
-
predicate: (w: PredicateScope<State>) => BranchPredicate<State>;
|
|
1004
|
-
message?: string;
|
|
1005
|
-
}, thenBranch: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<T>, elseBranch: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<NoInfer<T>>): FlowFragmentBuilder<T>;
|
|
1006
|
-
conditionFn<R extends State = State>(spec: {
|
|
1007
|
-
predicate: (ctx: TestContext, s: State) => boolean;
|
|
1008
|
-
message: string;
|
|
1009
|
-
}, thenBranch: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<R & NoExtraKeys<R, State>>): FlowFragmentBuilder<State>;
|
|
1010
|
-
conditionFn<T>(spec: {
|
|
1011
|
-
predicate: (ctx: TestContext, s: State) => boolean;
|
|
1012
|
-
message: string;
|
|
1013
|
-
}, thenBranch: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<T>, elseBranch: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<NoInfer<T>>): FlowFragmentBuilder<T>;
|
|
1014
|
-
conditionAsync<R extends State = State>(spec: {
|
|
1015
|
-
predicate: (ctx: TestContext, s: State) => Promise<boolean>;
|
|
1016
|
-
message: string;
|
|
1017
|
-
}, thenBranch: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<R & NoExtraKeys<R, State>>): FlowFragmentBuilder<State>;
|
|
1018
|
-
conditionAsync<T>(spec: {
|
|
1019
|
-
predicate: (ctx: TestContext, s: State) => Promise<boolean>;
|
|
1020
|
-
message: string;
|
|
1021
|
-
}, thenBranch: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<T>, elseBranch: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<NoInfer<T>>): FlowFragmentBuilder<T>;
|
|
1022
|
-
switchOn<V>(lens: (s: State) => V): <T>(cases: ReadonlyArray<{
|
|
1023
|
-
value: [Exclude<V, undefined>] extends [JsonScalar] ? Exclude<V, undefined> : never;
|
|
1024
|
-
then: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<NoInfer<T>>;
|
|
1025
|
-
}>, deflt: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<T>) => FlowFragmentBuilder<T>;
|
|
1026
|
-
switchCond<T>(cases: ReadonlyArray<{
|
|
1027
|
-
when: (w: PredicateScope<State>) => BranchPredicate<State>;
|
|
1028
|
-
then: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<NoInfer<T>>;
|
|
1029
|
-
}>, deflt: (b: FlowFragmentBuilder<State>) => FlowFragmentBuilder<T>): FlowFragmentBuilder<T>;
|
|
1030
|
-
poll<CaseInputs, CaseOutput, AcceptKey, RawOutcome, Accept extends readonly AcceptKey[] = never, NewState = State>(ref: ContractCaseRef<CaseInputs, CaseOutput, AcceptKey, RawOutcome>, ...args: PollArgs<State, CaseInputs, [Accept] extends [never] ? CaseOutput : RawOutcome, NewState, Accept, {
|
|
1031
|
-
until: (w: PredicateScope<[Accept] extends [never] ? CaseOutput : RawOutcome>) => BranchPredicate<[Accept] extends [never] ? CaseOutput : RawOutcome>;
|
|
1032
|
-
message?: string;
|
|
1033
|
-
}>): FlowFragmentBuilder<NewState>;
|
|
1034
|
-
pollFn<CaseInputs, CaseOutput, AcceptKey, RawOutcome, Accept extends readonly AcceptKey[] = never, NewState = State>(ref: ContractCaseRef<CaseInputs, CaseOutput, AcceptKey, RawOutcome>, ...args: PollArgs<State, CaseInputs, [Accept] extends [never] ? CaseOutput : RawOutcome, NewState, Accept, {
|
|
1035
|
-
until: (ctx: TestContext, res: [Accept] extends [never] ? CaseOutput : RawOutcome, state: State) => boolean;
|
|
1036
|
-
message: string;
|
|
1037
|
-
}>): FlowFragmentBuilder<NewState>;
|
|
1038
|
-
pollAsync<CaseInputs, CaseOutput, AcceptKey, RawOutcome, Accept extends readonly AcceptKey[] = never, NewState = State>(ref: ContractCaseRef<CaseInputs, CaseOutput, AcceptKey, RawOutcome>, ...args: PollArgs<State, CaseInputs, [Accept] extends [never] ? CaseOutput : RawOutcome, NewState, Accept, {
|
|
1039
|
-
until: (ctx: TestContext, res: [Accept] extends [never] ? CaseOutput : RawOutcome, state: State) => Promise<boolean>;
|
|
1040
|
-
message: string;
|
|
1041
|
-
}>): FlowFragmentBuilder<NewState>;
|
|
1042
|
-
}
|
|
1043
|
-
/** Module-private phantom brand making FlowFragmentBuilder invariant in State. */
|
|
1044
|
-
declare const FRAGMENT_STATE: unique symbol;
|
|
1045
|
-
/**
|
|
1046
|
-
* No-else branch shape guard. A no-else `then` must NOT add/remove state keys
|
|
1047
|
-
* (when the predicate is false the branch is skipped, so downstream state stays
|
|
1048
|
-
* `State`), but it MAY narrow an existing key's value (e.g. flip `ok: boolean`
|
|
1049
|
-
* to `ok: true`). Encoded as `R extends State` (no removed keys, values only
|
|
1050
|
-
* narrow) intersected with `NoExtraKeys<R, State>` (any extra key collapses to
|
|
1051
|
-
* `never`, so adding a field fails to type-check). This is the ergonomic
|
|
1052
|
-
* counterpart to the invariant brand: it blocks shape drift without rejecting
|
|
1053
|
-
* safe literal narrowing.
|
|
824
|
+
* "No extra keys" structural guard — maps any key of R that is NOT in State to
|
|
825
|
+
* `never`, so a branch/fragment returning extra keys fails to type-check.
|
|
826
|
+
* (Shared type utility; survived the legacy-flow deletion because the vNext
|
|
827
|
+
* builder's strict-S sides use the same trick.)
|
|
1054
828
|
*/
|
|
1055
829
|
export type NoExtraKeys<R, State> = {
|
|
1056
830
|
[K in Exclude<keyof R, keyof State>]: never;
|
|
1057
831
|
};
|
|
1058
|
-
/**
|
|
1059
|
-
* Runtime flow contract. Extends Array<Test> so runner iterates directly.
|
|
1060
|
-
* The single Test inside orchestrates setup → steps → teardown via runFlow.
|
|
1061
|
-
*/
|
|
1062
|
-
export interface FlowContract<State = unknown> extends Array<Test> {
|
|
1063
|
-
readonly _flow: RuntimeFlowProjection<State> & {
|
|
1064
|
-
id: string;
|
|
1065
|
-
};
|
|
1066
|
-
/**
|
|
1067
|
-
* Pre-computed JSON-safe extracted projection. Populated by the flow
|
|
1068
|
-
* builder via `normalizeFlow(_flow)` so downstream consumers (scanner,
|
|
1069
|
-
* CLI, MCP, Cloud) don't need to import the SDK to get field mappings
|
|
1070
|
-
* for `.step()` lenses and reads/writes for `.compute()` nodes.
|
|
1071
|
-
*/
|
|
1072
|
-
readonly _extracted: ExtractedFlowProjection;
|
|
1073
|
-
}
|
|
1074
832
|
/**
|
|
1075
833
|
* Contract registry metadata attached to tests produced by `contract[protocol]()`.
|
|
1076
834
|
* Mirrored by `RegisteredTestMeta.contract` in types.ts.
|
|
@@ -1093,60 +851,4 @@ export interface ContractRegistryMeta {
|
|
|
1093
851
|
/** Plugin-defined free-form meta; core does not inspect. */
|
|
1094
852
|
meta?: unknown;
|
|
1095
853
|
}
|
|
1096
|
-
/**
|
|
1097
|
-
* Flow registry metadata attached to the single Test generated by
|
|
1098
|
-
* `contract.flow()`. Mirrored by `RegisteredTestMeta.flow` in types.ts.
|
|
1099
|
-
*/
|
|
1100
|
-
/** Registry step descriptor — same shape family as ExtractedFlowStep (recursive for branch). */
|
|
1101
|
-
export type FlowRegistryStep = {
|
|
1102
|
-
kind: "contract-call" | "compute";
|
|
1103
|
-
name?: string;
|
|
1104
|
-
contractId?: string;
|
|
1105
|
-
caseKey?: string;
|
|
1106
|
-
protocol?: string;
|
|
1107
|
-
target?: string;
|
|
1108
|
-
inputs?: FieldMapping[];
|
|
1109
|
-
outputs?: FieldMapping[];
|
|
1110
|
-
reads?: string[];
|
|
1111
|
-
writes?: string[];
|
|
1112
|
-
accept?: ReadonlyArray<string | number>;
|
|
1113
|
-
} | {
|
|
1114
|
-
kind: "branch";
|
|
1115
|
-
mode: "value" | "predicate";
|
|
1116
|
-
name?: string;
|
|
1117
|
-
subjectPath?: string[];
|
|
1118
|
-
cases: Array<{
|
|
1119
|
-
value?: JsonScalar;
|
|
1120
|
-
message?: string;
|
|
1121
|
-
predicate?: ExtractedPredicate;
|
|
1122
|
-
steps: FlowRegistryStep[];
|
|
1123
|
-
}>;
|
|
1124
|
-
default: FlowRegistryStep[];
|
|
1125
|
-
} | {
|
|
1126
|
-
kind: "poll";
|
|
1127
|
-
name?: string;
|
|
1128
|
-
contractId?: string;
|
|
1129
|
-
caseKey?: string;
|
|
1130
|
-
protocol?: string;
|
|
1131
|
-
target?: string;
|
|
1132
|
-
inputs?: FieldMapping[];
|
|
1133
|
-
outputs?: FieldMapping[];
|
|
1134
|
-
accept?: ReadonlyArray<string | number>;
|
|
1135
|
-
until?: ExtractedPredicate;
|
|
1136
|
-
message?: string;
|
|
1137
|
-
every?: number;
|
|
1138
|
-
backoff?: number;
|
|
1139
|
-
timeoutMs?: number;
|
|
1140
|
-
perAttemptTimeoutMs?: number;
|
|
1141
|
-
maxAttempts?: number;
|
|
1142
|
-
};
|
|
1143
|
-
export interface FlowRegistryMeta {
|
|
1144
|
-
id: string;
|
|
1145
|
-
description?: string;
|
|
1146
|
-
tags?: string[];
|
|
1147
|
-
/** Step descriptors (same shape family as ExtractedFlowStep; branch nests recursively). */
|
|
1148
|
-
steps: FlowRegistryStep[];
|
|
1149
|
-
setupDynamic?: true;
|
|
1150
|
-
}
|
|
1151
|
-
export {};
|
|
1152
854
|
//# sourceMappingURL=contract-types.d.ts.map
|