waygraph 0.15.1 → 0.15.3

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.
Files changed (31) hide show
  1. package/dist/cli.js +6 -1
  2. package/dist/engine.d.ts +124 -12
  3. package/dist/engine.js +170 -13
  4. package/dist/highlights.d.ts +8 -0
  5. package/dist/highlights.js +2 -0
  6. package/dist/index.d.ts +2 -2
  7. package/dist/index.js +1 -1
  8. package/dist/types.d.ts +17 -0
  9. package/examples/saucedemo/src/blocks/saucedemo-web/cart/nav-cart.block.ts +4 -4
  10. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +19 -8
  11. package/examples/saucedemo/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts +6 -5
  12. package/examples/saucedemo/src/blocks/saucedemo-web/methods/fill-password.method.block.ts +8 -9
  13. package/examples/saucedemo/src/blocks/saucedemo-web/methods/fill-username.method.block.ts +11 -9
  14. package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-login-for-flow.ts +5 -4
  15. package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +6 -5
  16. package/examples/saucedemo/src/flows/checkout.flow.ts +61 -97
  17. package/examples/saucedemo/src/flows/login.flow.ts +11 -2
  18. package/examples/saucedemo/src/flows/shop.flow.ts +13 -13
  19. package/examples/saucedemo/src/flows/viewer-blocked.flow.ts +9 -2
  20. package/package.json +1 -1
  21. package/templates/quickstart/src/blocks/saucedemo-web/cart/nav-cart.block.ts +4 -4
  22. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +19 -8
  23. package/templates/quickstart/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts +6 -5
  24. package/templates/quickstart/src/blocks/saucedemo-web/methods/fill-password.method.block.ts +8 -9
  25. package/templates/quickstart/src/blocks/saucedemo-web/methods/fill-username.method.block.ts +11 -9
  26. package/templates/quickstart/src/blocks/saucedemo-web/methods/submit-login-for-flow.ts +5 -4
  27. package/templates/quickstart/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +6 -5
  28. package/templates/quickstart/src/flows/checkout.flow.ts +61 -97
  29. package/templates/quickstart/src/flows/login.flow.ts +11 -2
  30. package/templates/quickstart/src/flows/shop.flow.ts +13 -13
  31. package/templates/quickstart/src/flows/viewer-blocked.flow.ts +9 -2
package/dist/cli.js CHANGED
@@ -2381,6 +2381,7 @@ async function presentFailPanel(page, opts) {
2381
2381
  gatesFast,
2382
2382
  demoPace,
2383
2383
  gate,
2384
+ mem,
2384
2385
  } = opts;
2385
2386
  if (hasAuthoredStubOnError(block, fixtures)) {
2386
2387
  await page
@@ -2396,6 +2397,7 @@ async function presentFailPanel(page, opts) {
2396
2397
  fixtures,
2397
2398
  error,
2398
2399
  out: opts.out,
2400
+ mem,
2399
2401
  });
2400
2402
  const errHighlights = errPhase.highlights.map((h) => {
2401
2403
  const styled = applyHighlightStyleDefaults(h, opts.highlightStyle);
@@ -5101,7 +5103,7 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
5101
5103
  !!r.wasFastForward;
5102
5104
  const fixtures = r.highlightFixtures;
5103
5105
  const flowStyle = r.highlightStyle;
5104
- const stubBeforePhase = await runStubPhase(r.block, "stubBefore", { fixtures });
5106
+ const stubBeforePhase = await runStubPhase(r.block, "stubBefore", { fixtures, mem });
5105
5107
  stubBeforeRef.current = stubBeforePhase.highlights.map((h) =>
5106
5108
  applyHighlightStyleDefaults(h, flowStyle),
5107
5109
  );
@@ -5317,6 +5319,7 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
5317
5319
  gatesFast: pacing.gatesFast,
5318
5320
  demoPace: pacing.demoPace,
5319
5321
  gate,
5322
+ mem,
5320
5323
  });
5321
5324
  if (errEdits && errEdits.__wgRetry) {
5322
5325
  await resetPageState(context, page, baseURL);
@@ -5376,6 +5379,7 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
5376
5379
  gatesFast: pacing.gatesFast,
5377
5380
  demoPace: pacing.demoPace,
5378
5381
  gate,
5382
+ mem,
5379
5383
  });
5380
5384
  if (errEdits && errEdits.__wgRetry) {
5381
5385
  await resetPageState(context, page, baseURL);
@@ -5416,6 +5420,7 @@ async function runStepMode(engine, start, end, context, page, mem, resolved, slo
5416
5420
  const afterPhase = await runStubPhase(r.block, "stubAfter", {
5417
5421
  out: result,
5418
5422
  fixtures: fixturesAfter,
5423
+ mem,
5419
5424
  });
5420
5425
  logStubPhaseFixtures("after", {
5421
5426
  ...afterPhase,
package/dist/engine.d.ts CHANGED
@@ -707,37 +707,58 @@ export declare class MapBuilder<Out extends Checkpoint<string>> {
707
707
  private readonly engine;
708
708
  private readonly steps;
709
709
  private readonly homeOrigin;
710
+ private readonly ff;
710
711
  private constructor();
711
712
  /** @internal - use `engine.map()` or the standalone `map()` export. */
712
713
  static begin(engine: Engine, homeOrigin: string | undefined): MapBuilder<S>;
713
714
  /** Readable chain-opener - mirrors `defineFlow`'s leading `start` sentinel. Returns `this` unchanged; entirely optional. */
714
715
  start(): MapBuilder<Out>;
716
+ /**
717
+ * Open a fast-forward window - steps until {@link ffEnd} collapse into one
718
+ * opaque `fastForwardComposeBlock` (blitz theater). Kind/salt checks still
719
+ * run on each inner step as it is added.
720
+ */
721
+ ffStart(name?: string): MapBuilder<Out>;
722
+ /**
723
+ * Close the current fast-forward window and append one FFCompose step.
724
+ * Throws if empty or if {@link ffStart} was never opened.
725
+ */
726
+ ffEnd(): MapBuilder<Out>;
727
+ private appendStep;
715
728
  /**
716
729
  * Appends an internal navigation/page-arrival step - requires a Block from
717
730
  * `defineNavBlock`/`defineMemNavBlock`/`definePageBlock`. Throws if given
718
731
  * anything else, including a `homeOrigin`-mismatched static `url`.
732
+ * Compile-time: {@link NavBlock} or {@link PageBlock} only (not Assert/Method).
719
733
  */
720
- gotoPage<NextOut extends Checkpoint<string>>(block: Block<Out, NextOut> & {
734
+ gotoPage<NextOut extends Checkpoint<string>>(block: (NavBlock<NextOut> | PageBlock<NextOut>) & {
721
735
  name: string;
722
736
  }): MapBuilder<NextOut>;
723
737
  /**
724
738
  * Appends a genuinely cross-origin navigation step (e.g. `(external)/`
725
739
  * Blocks in the Waygraph Map convention: mailpit, maildrop.cc) - same
726
740
  * kind requirement as {@link gotoPage}, plus the inverse `homeOrigin` check.
741
+ * Compile-time: {@link NavBlock} or {@link PageBlock} only.
727
742
  */
728
- gotoExternal<NextOut extends Checkpoint<string>>(block: Block<Out, NextOut> & {
743
+ gotoExternal<NextOut extends Checkpoint<string>>(block: (NavBlock<NextOut> | PageBlock<NextOut>) & {
729
744
  name: string;
730
745
  }): MapBuilder<NextOut>;
731
- /** Appends a self-loop verification step - requires a Block from `defineAssertBlock`. */
732
- assert<NextOut extends Checkpoint<string>>(block: Block<Out, NextOut> & {
746
+ /**
747
+ * Appends a self-loop verification step - requires a Block from
748
+ * `defineAssertBlock`. Compile-time: {@link AssertBlock} only (not `.method()`).
749
+ */
750
+ assert(block: AssertBlock<Out> & {
733
751
  name: string;
734
- }): MapBuilder<NextOut>;
752
+ }): MapBuilder<Out>;
735
753
  /**
736
754
  * Appends a non-navigating page action (submit, upload, add/remove an
737
755
  * instance) - requires a Block from `defineMethodBlock`/`defineActionBlock`/
738
756
  * `defineEffectBlock`/`defineMemEffectBlock`.
757
+ * Compile-time: {@link MethodBlock} or {@link EffectBlock} only - Assert
758
+ * Blocks must use `.assert()`. Runtime also rejects `__waygraphKind =
759
+ * "assert"` (those still carry method salt from the factory internals).
739
760
  */
740
- method<NextOut extends Checkpoint<string>>(block: Block<Out, NextOut> & {
761
+ method<NextOut extends Checkpoint<string>>(block: (MethodBlock<Out, NextOut> | EffectBlock<Out, NextOut>) & {
741
762
  name: string;
742
763
  }): MapBuilder<NextOut>;
743
764
  /**
@@ -799,8 +820,27 @@ export declare function defineBlock<In extends Checkpoint<string>, Out extends C
799
820
  * `defineFlow([...])`, `connect()`, `composeBlock()` exactly like any other
800
821
  * Block - no special-casing needed anywhere that only expects a `Block`.
801
822
  * See `openspec/specs/nav-block-and-check/spec.md` for why this exists.
823
+ *
824
+ * `__wgFactory` is a TypeScript-only brand so {@link MapBuilder.gotoPage} /
825
+ * {@link MapBuilder.gotoExternal} can reject Assert/Method Blocks at compile
826
+ * time (runtime still checks `__waygraphKind`). Decorate methods are re-stated
827
+ * (via Omit) so `.stubBefore()` / `.withVerify()` keep the brand.
802
828
  */
803
- export type NavBlock<Out extends Checkpoint<string>> = DefinedBlock<Checkpoint<string>, Out>;
829
+ export type NavBlock<Out extends Checkpoint<string>> = Omit<DefinedBlock<Checkpoint<string>, Out>, "withVerify" | "modVerify" | "modVerifyAll" | "stubBefore" | "stubAfter" | "stubOnError"> & {
830
+ readonly __wgFactory?: "nav";
831
+ withVerify(verify: Trait[] | ((out: Out) => Trait[])): NavBlock<Out>;
832
+ modVerify(nameOrIndex: string | number, newCheck: Trait["check"] | Trait): NavBlock<Out>;
833
+ modVerifyAll(patches: Record<string, Trait["check"] | Trait>): NavBlock<Out>;
834
+ stubBefore(stub: import("./highlights.js").HighlightStubPhase): NavBlock<Out>;
835
+ stubBefore(stub: import("./highlights.js").StubLifecycleFn<Out>): NavBlock<Out>;
836
+ stubBefore(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): NavBlock<Out>;
837
+ stubAfter(stub: import("./highlights.js").HighlightStubPhase): NavBlock<Out>;
838
+ stubAfter(stub: import("./highlights.js").StubLifecycleFn<Out>): NavBlock<Out>;
839
+ stubAfter(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): NavBlock<Out>;
840
+ stubOnError(stub: import("./highlights.js").HighlightStubPhase): NavBlock<Out>;
841
+ stubOnError(stub: import("./highlights.js").StubLifecycleFn<Out>): NavBlock<Out>;
842
+ stubOnError(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): NavBlock<Out>;
843
+ };
804
844
  /**
805
845
  * `defineNavBlock`'s own options - exactly one of `url`/`click` required,
806
846
  * enforced at compile time (giving both, or neither, is a type error - see
@@ -897,7 +937,21 @@ export declare function defineNavClickBlock<Out extends Checkpoint<string>>(opti
897
937
  * {@link defineEffectBlock} when the auto menu should spawn one row per live
898
938
  * DOM instance. Prefer hanging methods off a {@link definePageBlock} hub.
899
939
  */
900
- export type MethodBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>> = DefinedBlock<In, Out>;
940
+ export type MethodBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>> = Omit<DefinedBlock<In, Out>, "withVerify" | "modVerify" | "modVerifyAll" | "stubBefore" | "stubAfter" | "stubOnError"> & {
941
+ readonly __wgFactory?: "method";
942
+ withVerify(verify: Trait[] | ((out: Out) => Trait[])): MethodBlock<In, Out>;
943
+ modVerify(nameOrIndex: string | number, newCheck: Trait["check"] | Trait): MethodBlock<In, Out>;
944
+ modVerifyAll(patches: Record<string, Trait["check"] | Trait>): MethodBlock<In, Out>;
945
+ stubBefore(stub: import("./highlights.js").HighlightStubPhase): MethodBlock<In, Out>;
946
+ stubBefore(stub: import("./highlights.js").StubLifecycleFn<Out>): MethodBlock<In, Out>;
947
+ stubBefore(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): MethodBlock<In, Out>;
948
+ stubAfter(stub: import("./highlights.js").HighlightStubPhase): MethodBlock<In, Out>;
949
+ stubAfter(stub: import("./highlights.js").StubLifecycleFn<Out>): MethodBlock<In, Out>;
950
+ stubAfter(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): MethodBlock<In, Out>;
951
+ stubOnError(stub: import("./highlights.js").HighlightStubPhase): MethodBlock<In, Out>;
952
+ stubOnError(stub: import("./highlights.js").StubLifecycleFn<Out>): MethodBlock<In, Out>;
953
+ stubOnError(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): MethodBlock<In, Out>;
954
+ };
901
955
  /** @deprecated Prefer {@link MethodBlock} - same type; Action was the 0.7 name. */
902
956
  export type ActionBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>> = MethodBlock<In, Out>;
903
957
  export declare function defineMethodBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>>(base: Parameters<typeof defineBlock<In, Out>>[0]): MethodBlock<In, Out>;
@@ -941,7 +995,26 @@ export interface AssertBlockOptions<Out extends Checkpoint<string> = Checkpoint<
941
995
  * verify: [Trait.visible(RawMaterialsSel.reorderBadge)],
942
996
  * });
943
997
  */
944
- export declare function defineAssertBlock<Out extends Checkpoint<string> = Checkpoint<string>>(options: AssertBlockOptions<Out>): MethodBlock<Out, Out>;
998
+ export declare function defineAssertBlock<Out extends Checkpoint<string> = Checkpoint<string>>(options: AssertBlockOptions<Out>): AssertBlock<Out>;
999
+ /**
1000
+ * Self-loop verify Block from {@link defineAssertBlock}. Branded for
1001
+ * {@link MapBuilder.assert} (not accepted by `.method()`).
1002
+ */
1003
+ export type AssertBlock<Out extends Checkpoint<string>> = Omit<DefinedBlock<Out, Out>, "withVerify" | "modVerify" | "modVerifyAll" | "stubBefore" | "stubAfter" | "stubOnError"> & {
1004
+ readonly __wgFactory?: "assert";
1005
+ withVerify(verify: Trait[] | ((out: Out) => Trait[])): AssertBlock<Out>;
1006
+ modVerify(nameOrIndex: string | number, newCheck: Trait["check"] | Trait): AssertBlock<Out>;
1007
+ modVerifyAll(patches: Record<string, Trait["check"] | Trait>): AssertBlock<Out>;
1008
+ stubBefore(stub: import("./highlights.js").HighlightStubPhase): AssertBlock<Out>;
1009
+ stubBefore(stub: import("./highlights.js").StubLifecycleFn<Out>): AssertBlock<Out>;
1010
+ stubBefore(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): AssertBlock<Out>;
1011
+ stubAfter(stub: import("./highlights.js").HighlightStubPhase): AssertBlock<Out>;
1012
+ stubAfter(stub: import("./highlights.js").StubLifecycleFn<Out>): AssertBlock<Out>;
1013
+ stubAfter(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): AssertBlock<Out>;
1014
+ stubOnError(stub: import("./highlights.js").HighlightStubPhase): AssertBlock<Out>;
1015
+ stubOnError(stub: import("./highlights.js").StubLifecycleFn<Out>): AssertBlock<Out>;
1016
+ stubOnError(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): AssertBlock<Out>;
1017
+ };
945
1018
  /** One Block (or lazy factory) registered on a {@link PageBlock}. */
946
1019
  export type PageMethodEntry = DefinedBlock<any, any> | (() => DefinedBlock<any, any>);
947
1020
  export type PageBlockOptions<Out extends Checkpoint<string>> = {
@@ -975,10 +1048,25 @@ export type PageBlockOptions<Out extends Checkpoint<string>> = {
975
1048
  * readability / auto grouping. Runtime is still a Block (`__waygraphKind = "page"`).
976
1049
  * `act` is goto/click when `url`/`click` is set, otherwise a no-op (already here).
977
1050
  * Child methods stay ordinary Blocks for graph edges - the page only registers them.
1051
+ * Branded `__wgFactory: "page"` (not `"nav"`) - both are accepted by
1052
+ * {@link MapBuilder.gotoPage} / {@link MapBuilder.gotoExternal}.
978
1053
  */
979
- export type PageBlock<Out extends Checkpoint<string>> = NavBlock<Out> & {
1054
+ export type PageBlock<Out extends Checkpoint<string>> = Omit<DefinedBlock<Checkpoint<string>, Out>, "withVerify" | "modVerify" | "modVerifyAll" | "stubBefore" | "stubAfter" | "stubOnError"> & {
1055
+ readonly __wgFactory?: "page";
980
1056
  /** Resolved method Blocks registered at define time. */
981
1057
  readonly methods: readonly DefinedBlock<any, any>[];
1058
+ withVerify(verify: Trait[] | ((out: Out) => Trait[])): PageBlock<Out>;
1059
+ modVerify(nameOrIndex: string | number, newCheck: Trait["check"] | Trait): PageBlock<Out>;
1060
+ modVerifyAll(patches: Record<string, Trait["check"] | Trait>): PageBlock<Out>;
1061
+ stubBefore(stub: import("./highlights.js").HighlightStubPhase): PageBlock<Out>;
1062
+ stubBefore(stub: import("./highlights.js").StubLifecycleFn<Out>): PageBlock<Out>;
1063
+ stubBefore(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): PageBlock<Out>;
1064
+ stubAfter(stub: import("./highlights.js").HighlightStubPhase): PageBlock<Out>;
1065
+ stubAfter(stub: import("./highlights.js").StubLifecycleFn<Out>): PageBlock<Out>;
1066
+ stubAfter(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): PageBlock<Out>;
1067
+ stubOnError(stub: import("./highlights.js").HighlightStubPhase): PageBlock<Out>;
1068
+ stubOnError(stub: import("./highlights.js").StubLifecycleFn<Out>): PageBlock<Out>;
1069
+ stubOnError(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): PageBlock<Out>;
982
1070
  };
983
1071
  /** @see {@link PageBlockOptions} */
984
1072
  export declare function definePageBlock<Out extends Checkpoint<string>>(options: PageBlockOptions<Out>): PageBlock<Out>;
@@ -988,9 +1076,22 @@ export declare function definePageBlock<Out extends Checkpoint<string>>(options:
988
1076
  * row per live instance (add/remove/toggle). Not a separate engine concept -
989
1077
  * everything is still a Block.
990
1078
  */
991
- export type EffectBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>> = DefinedBlock<In, Out> & {
1079
+ export type EffectBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>> = Omit<DefinedBlock<In, Out>, "withVerify" | "modVerify" | "modVerifyAll" | "stubBefore" | "stubAfter" | "stubOnError"> & {
1080
+ readonly __wgFactory?: "effect";
992
1081
  requires: readonly MemKey<any>[];
993
1082
  instanceOptions: NonNullable<Block<In, Out>["instanceOptions"]>;
1083
+ withVerify(verify: Trait[] | ((out: Out) => Trait[])): EffectBlock<In, Out>;
1084
+ modVerify(nameOrIndex: string | number, newCheck: Trait["check"] | Trait): EffectBlock<In, Out>;
1085
+ modVerifyAll(patches: Record<string, Trait["check"] | Trait>): EffectBlock<In, Out>;
1086
+ stubBefore(stub: import("./highlights.js").HighlightStubPhase): EffectBlock<In, Out>;
1087
+ stubBefore(stub: import("./highlights.js").StubLifecycleFn<Out>): EffectBlock<In, Out>;
1088
+ stubBefore(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): EffectBlock<In, Out>;
1089
+ stubAfter(stub: import("./highlights.js").HighlightStubPhase): EffectBlock<In, Out>;
1090
+ stubAfter(stub: import("./highlights.js").StubLifecycleFn<Out>): EffectBlock<In, Out>;
1091
+ stubAfter(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): EffectBlock<In, Out>;
1092
+ stubOnError(stub: import("./highlights.js").HighlightStubPhase): EffectBlock<In, Out>;
1093
+ stubOnError(stub: import("./highlights.js").StubLifecycleFn<Out>): EffectBlock<In, Out>;
1094
+ stubOnError(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): EffectBlock<In, Out>;
994
1095
  };
995
1096
  /** @deprecated Prefer {@link EffectBlock} - same type; Mem* was an early name. */
996
1097
  export type MemEffectBlock<In extends Checkpoint<string>, Out extends Checkpoint<string>> = EffectBlock<In, Out>;
@@ -1064,7 +1165,18 @@ export declare function locate(page: Page, library: readonly NavBlock<any>[], op
1064
1165
  * argument at all, only whatever `verify` each Block's own `instruction` (or
1065
1166
  * a `withVerify(...)` wrapping it) carries with it.
1066
1167
  */
1067
- export declare function withVerify<In extends Checkpoint<string>, Out extends Checkpoint<string>>(block: Block<In, Out>, verify: Trait[] | ((out: Out) => Trait[])): DefinedBlock<In, Out>;
1168
+ export declare function withVerify<B extends Block<any, any>>(block: B, verify: Trait[] | ((out: any) => Trait[])): B;
1169
+ type StubPhaseFn<Out extends Checkpoint<string>> = import("./highlights.js").HighlightStubPhaseOrFn<Out>;
1170
+ /**
1171
+ * Attach / chain a stubBefore lifecycle on a Block. Prefer
1172
+ * `(ctx) => { ctx.ring(...); const x = ctx.mem?.get(Key); }`.
1173
+ * @example FillUsernameBlock.stubBefore((ctx) => { ctx.ring("username", { ... }) })
1174
+ */
1175
+ export declare function withStubBefore<B extends Block<any, any>>(block: B, stub: StubPhaseFn<any>): B;
1176
+ /** @see {@link withStubBefore} */
1177
+ export declare function withStubAfter<B extends Block<any, any>>(block: B, stub: StubPhaseFn<any>): B;
1178
+ /** @see {@link withStubBefore} */
1179
+ export declare function withStubOnError<B extends Block<any, any>>(block: B, stub: StubPhaseFn<any>): B;
1068
1180
  /**
1069
1181
  * Replaces one Trait inside `block`'s verify list, leaving every other check
1070
1182
  * untouched - the v1/v2/legacy-variant case, where one check needs a different
package/dist/engine.js CHANGED
@@ -811,52 +811,108 @@ export class MapBuilder {
811
811
  engine;
812
812
  steps;
813
813
  homeOrigin;
814
- constructor(engine, steps, homeOrigin) {
814
+ ff;
815
+ constructor(engine, steps, homeOrigin, ff = null) {
815
816
  this.engine = engine;
816
817
  this.steps = steps;
817
818
  this.homeOrigin = homeOrigin;
819
+ this.ff = ff;
818
820
  }
819
821
  /** @internal - use `engine.map()` or the standalone `map()` export. */
820
822
  static begin(engine, homeOrigin) {
821
- return new MapBuilder(engine, [], homeOrigin);
823
+ return new MapBuilder(engine, [], homeOrigin, null);
822
824
  }
823
825
  /** Readable chain-opener - mirrors `defineFlow`'s leading `start` sentinel. Returns `this` unchanged; entirely optional. */
824
826
  start() {
825
827
  return this;
826
828
  }
829
+ /**
830
+ * Open a fast-forward window - steps until {@link ffEnd} collapse into one
831
+ * opaque `fastForwardComposeBlock` (blitz theater). Kind/salt checks still
832
+ * run on each inner step as it is added.
833
+ */
834
+ ffStart(name) {
835
+ if (this.ff) {
836
+ throw new Error(`Waygraph map: .ffStart() while already inside "${this.ff.name}" - call .ffEnd() first`);
837
+ }
838
+ const ffName = typeof name === "string" && name.trim()
839
+ ? name.trim()
840
+ : `ff-${this.steps.length + 1}`;
841
+ return new MapBuilder(this.engine, this.steps, this.homeOrigin, {
842
+ name: ffName,
843
+ buffer: [],
844
+ });
845
+ }
846
+ /**
847
+ * Close the current fast-forward window and append one FFCompose step.
848
+ * Throws if empty or if {@link ffStart} was never opened.
849
+ */
850
+ ffEnd() {
851
+ if (!this.ff) {
852
+ throw new Error("Waygraph map: .ffEnd() with no open .ffStart()");
853
+ }
854
+ if (this.ff.buffer.length === 0) {
855
+ throw new Error(`Waygraph map: .ffEnd() for "${this.ff.name}" has no steps - add .gotoPage/.method/… inside the window`);
856
+ }
857
+ const composed = fastForwardComposeBlock(this.ff.name,
858
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
859
+ this.ff.buffer);
860
+ return new MapBuilder(this.engine, [...this.steps, composed], this.homeOrigin, null);
861
+ }
862
+ appendStep(block) {
863
+ if (this.ff) {
864
+ return new MapBuilder(this.engine, this.steps, this.homeOrigin, {
865
+ name: this.ff.name,
866
+ buffer: [...this.ff.buffer, block],
867
+ });
868
+ }
869
+ return new MapBuilder(this.engine, [...this.steps, block], this.homeOrigin, null);
870
+ }
827
871
  /**
828
872
  * Appends an internal navigation/page-arrival step - requires a Block from
829
873
  * `defineNavBlock`/`defineMemNavBlock`/`definePageBlock`. Throws if given
830
874
  * anything else, including a `homeOrigin`-mismatched static `url`.
875
+ * Compile-time: {@link NavBlock} or {@link PageBlock} only (not Assert/Method).
831
876
  */
832
877
  gotoPage(block) {
833
878
  assertMapKind(block, ["nav", "page"], "gotoPage");
834
879
  assertMapOrigin(block, this.homeOrigin, "internal", "gotoPage");
835
- return new MapBuilder(this.engine, [...this.steps, block], this.homeOrigin);
880
+ return this.appendStep(block);
836
881
  }
837
882
  /**
838
883
  * Appends a genuinely cross-origin navigation step (e.g. `(external)/`
839
884
  * Blocks in the Waygraph Map convention: mailpit, maildrop.cc) - same
840
885
  * kind requirement as {@link gotoPage}, plus the inverse `homeOrigin` check.
886
+ * Compile-time: {@link NavBlock} or {@link PageBlock} only.
841
887
  */
842
888
  gotoExternal(block) {
843
889
  assertMapKind(block, ["nav", "page"], "gotoExternal");
844
890
  assertMapOrigin(block, this.homeOrigin, "external", "gotoExternal");
845
- return new MapBuilder(this.engine, [...this.steps, block], this.homeOrigin);
891
+ return this.appendStep(block);
846
892
  }
847
- /** Appends a self-loop verification step - requires a Block from `defineAssertBlock`. */
893
+ /**
894
+ * Appends a self-loop verification step - requires a Block from
895
+ * `defineAssertBlock`. Compile-time: {@link AssertBlock} only (not `.method()`).
896
+ */
848
897
  assert(block) {
849
898
  assertMapKind(block, ["assert"], "assert");
850
- return new MapBuilder(this.engine, [...this.steps, block], this.homeOrigin);
899
+ return this.appendStep(block);
851
900
  }
852
901
  /**
853
902
  * Appends a non-navigating page action (submit, upload, add/remove an
854
903
  * instance) - requires a Block from `defineMethodBlock`/`defineActionBlock`/
855
904
  * `defineEffectBlock`/`defineMemEffectBlock`.
905
+ * Compile-time: {@link MethodBlock} or {@link EffectBlock} only - Assert
906
+ * Blocks must use `.assert()`. Runtime also rejects `__waygraphKind =
907
+ * "assert"` (those still carry method salt from the factory internals).
856
908
  */
857
909
  method(block) {
910
+ if (mapKindOf(block) === "assert") {
911
+ throw new Error(`Waygraph map: .method("${block.name}") got a defineAssertBlock - use .assert() for ` +
912
+ "self-loop verify steps (Assert Blocks share method salt internally but are not methods).");
913
+ }
858
914
  assertMapSalt(block, ["method", "effect"], "method");
859
- return new MapBuilder(this.engine, [...this.steps, block], this.homeOrigin);
915
+ return this.appendStep(block);
860
916
  }
861
917
  /**
862
918
  * Finalizes this chain into a real, runnable {@link Flow} - same object
@@ -867,6 +923,9 @@ export class MapBuilder {
867
923
  * isn't a flow.
868
924
  */
869
925
  end() {
926
+ if (this.ff) {
927
+ throw new Error(`Waygraph map: .end() while .ffStart("${this.ff.name}") is still open - call .ffEnd() first`);
928
+ }
870
929
  if (this.steps.length === 0) {
871
930
  throw new Error("Waygraph map: .end() called with zero steps - add at least one .gotoPage()/.gotoExternal()/" +
872
931
  ".assert()/.method() before .end()");
@@ -937,8 +996,38 @@ function stripMethods(block) {
937
996
  ...(block.next ? { next: block.next } : {}),
938
997
  ...(block.requires ? { requires: block.requires } : {}),
939
998
  ...(block.routes ? { routes: block.routes } : {}),
999
+ ...(block.instanceOptions ? { instanceOptions: block.instanceOptions } : {}),
940
1000
  };
941
1001
  }
1002
+ /** Copy non-enumerable waygraph markers so decorate/withVerify keep map() kind checks. */
1003
+ function copyWaygraphRuntime(from, to) {
1004
+ for (const key of [
1005
+ "__waygraphKind",
1006
+ "__waygraphSalt",
1007
+ "__waygraphNavUrl",
1008
+ "__waygraphNavClick",
1009
+ ]) {
1010
+ const desc = Object.getOwnPropertyDescriptor(from, key);
1011
+ if (!desc)
1012
+ continue;
1013
+ // Re-define as configurable so later decorate chains can copy again
1014
+ // (factory markers are non-configurable on the original).
1015
+ Object.defineProperty(to, key, {
1016
+ value: desc.value,
1017
+ enumerable: false,
1018
+ writable: false,
1019
+ configurable: true,
1020
+ });
1021
+ }
1022
+ const ff = from.fastForward;
1023
+ if (ff === true) {
1024
+ to.fastForward = true;
1025
+ }
1026
+ const pace = from.demoPace;
1027
+ if (pace !== undefined) {
1028
+ to.demoPace = pace;
1029
+ }
1030
+ }
942
1031
  /**
943
1032
  * Builds a Block with `withVerify`/`modVerify`/`modVerifyAll` attached as
944
1033
  * methods - `LoginBlock.withVerify([...])`, discoverable by typing
@@ -958,12 +1047,19 @@ export function defineBlock(base) {
958
1047
  ...(base.routes ? { routes: base.routes } : {}),
959
1048
  ...(base.instanceOptions ? { instanceOptions: base.instanceOptions } : {}),
960
1049
  };
961
- return {
1050
+ // Methods must close over `defined` (this object), not `plain` - factories
1051
+ // attach __waygraphKind/__waygraphSalt on the returned object after
1052
+ // defineBlock returns; decorate/withVerify need those markers.
1053
+ const defined = {
962
1054
  ...plain,
963
- withVerify: (verify) => withVerify(plain, verify),
964
- modVerify: (nameOrIndex, newCheck) => modVerify(plain, nameOrIndex, newCheck),
965
- modVerifyAll: (patches) => modVerifyAll(plain, patches),
1055
+ withVerify: (verify) => withVerify(defined, verify),
1056
+ modVerify: (nameOrIndex, newCheck) => modVerify(defined, nameOrIndex, newCheck),
1057
+ modVerifyAll: (patches) => modVerifyAll(defined, patches),
1058
+ stubBefore: (stub) => withStubBefore(defined, stub),
1059
+ stubAfter: (stub) => withStubAfter(defined, stub),
1060
+ stubOnError: (stub) => withStubOnError(defined, stub),
966
1061
  };
1062
+ return defined;
967
1063
  }
968
1064
  /**
969
1065
  * Builds a {@link NavBlock}. The generated `act()` is always exactly
@@ -1120,6 +1216,8 @@ export function defineAssertBlock(options) {
1120
1216
  enumerable: false,
1121
1217
  configurable: false,
1122
1218
  });
1219
+ // Brand for map().assert() - distinct from MethodBlock so .method(assert)
1220
+ // is a compile error even though runtime still carries method salt.
1123
1221
  return built;
1124
1222
  }
1125
1223
  function resolvePageMethods(methods) {
@@ -1326,7 +1424,64 @@ export async function locate(page, library, options) {
1326
1424
  */
1327
1425
  export function withVerify(block, verify) {
1328
1426
  const base = stripMethods(block);
1329
- return defineBlock({ ...base, instruction: { ...base.instruction, verify } });
1427
+ const next = defineBlock({ ...base, instruction: { ...base.instruction, verify } });
1428
+ copyWaygraphRuntime(block, next);
1429
+ return next;
1430
+ }
1431
+ function isHighlightStubPhase(v) {
1432
+ return !!v && typeof v === "object" && !Array.isArray(v);
1433
+ }
1434
+ /** Run a prior stub (fn or slot map) into an open ctx so decorate can chain. */
1435
+ async function applyStubIntoCtx(raw, ctx) {
1436
+ if (raw === undefined)
1437
+ return;
1438
+ if (typeof raw === "function") {
1439
+ const ret = await raw(ctx);
1440
+ if (isHighlightStubPhase(ret)) {
1441
+ for (const [id, stub] of Object.entries(ret)) {
1442
+ if (stub)
1443
+ ctx.ring(id, stub);
1444
+ }
1445
+ }
1446
+ return;
1447
+ }
1448
+ if (isHighlightStubPhase(raw)) {
1449
+ for (const [id, stub] of Object.entries(raw)) {
1450
+ if (stub)
1451
+ ctx.ring(id, stub);
1452
+ }
1453
+ }
1454
+ }
1455
+ function withStubPhase(block, phase, stub) {
1456
+ const base = stripMethods(block);
1457
+ const instr = base.instruction;
1458
+ const prev = instr[phase];
1459
+ const chained = async (ctx) => {
1460
+ await applyStubIntoCtx(prev, ctx);
1461
+ await applyStubIntoCtx(stub, ctx);
1462
+ };
1463
+ const next = defineBlock({
1464
+ ...base,
1465
+ instruction: { ...base.instruction, [phase]: chained },
1466
+ });
1467
+ copyWaygraphRuntime(block, next);
1468
+ return next;
1469
+ }
1470
+ /**
1471
+ * Attach / chain a stubBefore lifecycle on a Block. Prefer
1472
+ * `(ctx) => { ctx.ring(...); const x = ctx.mem?.get(Key); }`.
1473
+ * @example FillUsernameBlock.stubBefore((ctx) => { ctx.ring("username", { ... }) })
1474
+ */
1475
+ export function withStubBefore(block, stub) {
1476
+ return withStubPhase(block, "stubBefore", stub);
1477
+ }
1478
+ /** @see {@link withStubBefore} */
1479
+ export function withStubAfter(block, stub) {
1480
+ return withStubPhase(block, "stubAfter", stub);
1481
+ }
1482
+ /** @see {@link withStubBefore} */
1483
+ export function withStubOnError(block, stub) {
1484
+ return withStubPhase(block, "stubOnError", stub);
1330
1485
  }
1331
1486
  /**
1332
1487
  * Replaces one Trait inside `block`'s verify list, leaving every other check
@@ -1371,5 +1526,7 @@ export function modVerify(block, nameOrIndex, newCheck) {
1371
1526
  * same fail-loud-on-unknown-name behavior holds for every entry.
1372
1527
  */
1373
1528
  export function modVerifyAll(block, patches) {
1374
- return Object.entries(patches).reduce((current, [name, newCheck]) => modVerify(current, name, newCheck), defineBlock(stripMethods(block)));
1529
+ const seed = defineBlock(stripMethods(block));
1530
+ copyWaygraphRuntime(block, seed);
1531
+ return Object.entries(patches).reduce((current, [name, newCheck]) => modVerify(current, name, newCheck), seed);
1375
1532
  }
@@ -13,6 +13,7 @@
13
13
  * before advancing is authored the same way on stubs, flow fixtures, and slides.
14
14
  */
15
15
  import type { Block, Checkpoint } from "./types.js";
16
+ import type { MemPage } from "./mem-page.js";
16
17
  /** Shared dwell fields on stubs, fixtures, and yap slides. */
17
18
  export type FixtureDurationFields = {
18
19
  /**
@@ -313,6 +314,12 @@ export type HighlightStubPhase = Record<string, WaygraphHighlightStub>;
313
314
  * }
314
315
  */
315
316
  export type StubCtx<Out extends Checkpoint<string> = Checkpoint<string>> = {
317
+ /**
318
+ * Live Mem for this run - same store `act`/`observe` read. Prefer this over
319
+ * closing over outer scope or inventing parallel fixture bags.
320
+ * Undefined only if the caller ran a stub phase without passing mem (tests).
321
+ */
322
+ readonly mem: MemPage | undefined;
316
323
  /** Present after resolve (stubAfter). Undefined for stubBefore. */
317
324
  readonly out: Out | undefined;
318
325
  /** Present on stubOnError when the step threw. */
@@ -636,6 +643,7 @@ export declare function completeSequentialTodoDock(dock: TodoDockState | undefin
636
643
  export declare function runStubPhase(block: Block<any, any>, phase: HighlightStubPhaseName, opts?: {
637
644
  out?: Checkpoint<string>;
638
645
  error?: unknown;
646
+ mem?: MemPage;
639
647
  fixtures?: HighlightFixtureMap;
640
648
  }): Promise<StubPhaseResult>;
641
649
  /**
@@ -795,6 +795,7 @@ export function completeSequentialTodoDock(dock) {
795
795
  }
796
796
  function createStubCtx(bag, opts) {
797
797
  return {
798
+ mem: opts.mem,
798
799
  out: opts.out,
799
800
  error: opts.error,
800
801
  highlights(slots) {
@@ -1143,6 +1144,7 @@ export async function runStubPhase(block, phase, opts) {
1143
1144
  const ctx = createStubCtx(bag, {
1144
1145
  ...(opts?.out !== undefined ? { out: opts.out } : {}),
1145
1146
  ...(opts?.error !== undefined ? { error: opts.error } : {}),
1147
+ ...(opts?.mem !== undefined ? { mem: opts.mem } : {}),
1146
1148
  });
1147
1149
  const raw = readStubRaw(block, phase);
1148
1150
  if (typeof raw === "function") {
package/dist/index.d.ts CHANGED
@@ -2,10 +2,10 @@ export type { Checkpoint, Instruction, Block, DefinedBlock, Start, WaygraphInsta
2
2
  export { connect, checkpoint } from "./types.js";
3
3
  export { MemKey, MemPage, key, keyGroup } from "./mem-page.js";
4
4
  export { Trait, urlMatches, textEquals, visible, frameVisible, frameTextEquals, frameContainsText } from "./trait.js";
5
- export { runGraph, spawnTab, narrate, Engine, start, end, branch, withVerify, modVerify, modVerifyAll, defineBlock, defineNavBlock, defineNavClickBlock, defineMethodBlock, defineActionBlock, defineAssertBlock, definePageBlock, defineEffectBlock, defineMemEffectBlock, defineMemNavBlock, locate, composeBlock, fastForwardComposeBlock, isFastForwardBlock, preflight, withSessionReset, withTitle, withDemoPace, withBlockPace, withHighlightStyle, withExpectedFailure, withHighlightFixtures, chainFlow, defineLayout, map, MapBuilder, } from "./engine.js";
5
+ export { runGraph, spawnTab, narrate, Engine, start, end, branch, withVerify, modVerify, modVerifyAll, defineBlock, defineNavBlock, defineNavClickBlock, defineMethodBlock, defineActionBlock, defineAssertBlock, definePageBlock, defineEffectBlock, defineMemEffectBlock, defineMemNavBlock, locate, composeBlock, fastForwardComposeBlock, isFastForwardBlock, preflight, withSessionReset, withTitle, withDemoPace, withBlockPace, withHighlightStyle, withExpectedFailure, withHighlightFixtures, withStubBefore, withStubAfter, withStubOnError, chainFlow, defineLayout, map, MapBuilder, } from "./engine.js";
6
6
  export { resolveHighlightSlots, resolveSlides, runStubPhase, resolveFixtureDwellMs, resolveSlideDwellMs, formatHighlightCaption, hasAuthoredStubAfter, hasAuthoredStubOnError, normalizeHighlightTone, normalizeDemoPace, normalizeHighlightSize, normalizeHighlightWeight, applyHighlightStyleDefaults, toneIconPrefix, resolveStepDemoPace, demoPaceScale, demoPaceIsBlitz, demoPaceIsFast, demoPaceIsSlow, demoPaceGateMs, formatDemoPaceBadge, formatDemoPaceLabel, normalizeTodos, normalizeTodoPos, normalizeTodoStyle, buildTodoDock, applyTodoPhase, advanceTodoDock, completeSequentialTodoDock, applyDevicePhase, resolveDeviceState, normalizeDevicePreset, normalizeDeviceOrientation, applyOrientation, orientationFromViewport, DEVICE_PRESETS, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, FIXTURE_DURATION_SLOW_MS_DEFAULT, SLIDE_DURATION_MS_DEFAULT, SLIDE_DURATION_FAST_MS_DEFAULT, WAYGRAPH_RING_CSS, } from "./highlights.js";
7
7
  export type { WaygraphHighlightStub, WaygraphHighlightFixture, WaygraphSlide, WaygraphTodoItem, WaygraphTodoInput, WaygraphTodoGroup, WaygraphTodoGroupInput, WaygraphTodosOpts, TodoDockState, TodoListStyle, FixtureDurationFields, HighlightStubPhase, HighlightStubPhaseName, HighlightFixtureMap, ResolvedHighlight, WaygraphHighlight, HighlightTone, HighlightSize, HighlightWeight, HighlightStyleDefaults, DemoPace, DemoPaceName, StubCtx, StubLifecycleFn, StubPhaseResult, StubPhaseFixtures, DevicePreset, DeviceViewport, DeviceState, DeviceOrientation, } from "./highlights.js";
8
- export type { Flow, EngineConfig, ComposedBlock, FastForwardComposedBlock, RunGraphOptions, BlockInfo, NavBlock, NavBlockOptions, NavClickBlock, NavClickBlockOptions, MethodBlock, ActionBlock, AssertBlockOptions, PageBlock, PageBlockOptions, PageMethodEntry, EffectBlock, MemEffectBlock, MemNavBlock, Layout, LayoutOptions, MapBuilderOptions, } from "./engine.js";
8
+ export type { Flow, EngineConfig, ComposedBlock, FastForwardComposedBlock, RunGraphOptions, BlockInfo, NavBlock, NavBlockOptions, NavClickBlock, NavClickBlockOptions, MethodBlock, ActionBlock, AssertBlock, AssertBlockOptions, PageBlock, PageBlockOptions, PageMethodEntry, EffectBlock, MemEffectBlock, MemNavBlock, Layout, LayoutOptions, MapBuilderOptions, } from "./engine.js";
9
9
  export type { ActionPage } from "./types.js";
10
10
  export { discoverGraph, toMermaid, findOrphanBlocks, findBlockPath } from "./graph.js";
11
11
  export type { WaygraphGraph, WaygraphNode, WaygraphEdge, SkippedBlock, OrphanBlock } from "./graph.js";
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export { connect, checkpoint } from "./types.js";
2
2
  export { MemKey, MemPage, key, keyGroup } from "./mem-page.js";
3
3
  export { Trait, urlMatches, textEquals, visible, frameVisible, frameTextEquals, frameContainsText } from "./trait.js";
4
- export { runGraph, spawnTab, narrate, Engine, start, end, branch, withVerify, modVerify, modVerifyAll, defineBlock, defineNavBlock, defineNavClickBlock, defineMethodBlock, defineActionBlock, defineAssertBlock, definePageBlock, defineEffectBlock, defineMemEffectBlock, defineMemNavBlock, locate, composeBlock, fastForwardComposeBlock, isFastForwardBlock, preflight, withSessionReset, withTitle, withDemoPace, withBlockPace, withHighlightStyle, withExpectedFailure, withHighlightFixtures, chainFlow, defineLayout, map, MapBuilder, } from "./engine.js";
4
+ export { runGraph, spawnTab, narrate, Engine, start, end, branch, withVerify, modVerify, modVerifyAll, defineBlock, defineNavBlock, defineNavClickBlock, defineMethodBlock, defineActionBlock, defineAssertBlock, definePageBlock, defineEffectBlock, defineMemEffectBlock, defineMemNavBlock, locate, composeBlock, fastForwardComposeBlock, isFastForwardBlock, preflight, withSessionReset, withTitle, withDemoPace, withBlockPace, withHighlightStyle, withExpectedFailure, withHighlightFixtures, withStubBefore, withStubAfter, withStubOnError, chainFlow, defineLayout, map, MapBuilder, } from "./engine.js";
5
5
  export { resolveHighlightSlots, resolveSlides, runStubPhase, resolveFixtureDwellMs, resolveSlideDwellMs, formatHighlightCaption, hasAuthoredStubAfter, hasAuthoredStubOnError, normalizeHighlightTone, normalizeDemoPace, normalizeHighlightSize, normalizeHighlightWeight, applyHighlightStyleDefaults, toneIconPrefix, resolveStepDemoPace, demoPaceScale, demoPaceIsBlitz, demoPaceIsFast, demoPaceIsSlow, demoPaceGateMs, formatDemoPaceBadge, formatDemoPaceLabel, normalizeTodos, normalizeTodoPos, normalizeTodoStyle, buildTodoDock, applyTodoPhase, advanceTodoDock, completeSequentialTodoDock, applyDevicePhase, resolveDeviceState, normalizeDevicePreset, normalizeDeviceOrientation, applyOrientation, orientationFromViewport, DEVICE_PRESETS, FIXTURE_DURATION_MS_DEFAULT, FIXTURE_DURATION_FAST_MS_DEFAULT, FIXTURE_DURATION_SLOW_MS_DEFAULT, SLIDE_DURATION_MS_DEFAULT, SLIDE_DURATION_FAST_MS_DEFAULT, WAYGRAPH_RING_CSS, } from "./highlights.js";
6
6
  export { discoverGraph, toMermaid, findOrphanBlocks, findBlockPath } from "./graph.js";
7
7
  export { isFileSelectToken, parseBlocksSelect, matchBlocksSelect, globToRegExp, } from "./blocks-select.js";
package/dist/types.d.ts CHANGED
@@ -276,6 +276,23 @@ export type DefinedBlock<In extends Checkpoint<string>, Out extends Checkpoint<s
276
276
  withVerify(verify: Trait[] | ((out: Out) => Trait[])): DefinedBlock<In, Out>;
277
277
  modVerify(nameOrIndex: string | number, newCheck: Trait["check"] | Trait): DefinedBlock<In, Out>;
278
278
  modVerifyAll(patches: Record<string, Trait["check"] | Trait>): DefinedBlock<In, Out>;
279
+ /**
280
+ * Demo narration before act - prefer `(ctx) => { ctx.ring(...); ctx.mem?.get(...) }`.
281
+ * Chains after any existing stubBefore on this Block (library then call-site).
282
+ * Overloads keep `ctx` typed when the author passes a lifecycle function
283
+ * (a single union parameter would make the callback param `any`).
284
+ */
285
+ stubBefore(stub: import("./highlights.js").HighlightStubPhase): DefinedBlock<In, Out>;
286
+ stubBefore(stub: import("./highlights.js").StubLifecycleFn<Out>): DefinedBlock<In, Out>;
287
+ stubBefore(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): DefinedBlock<In, Out>;
288
+ /** Demo narration after resolve. Prefer ctx callbacks over slot-map objects. */
289
+ stubAfter(stub: import("./highlights.js").HighlightStubPhase): DefinedBlock<In, Out>;
290
+ stubAfter(stub: import("./highlights.js").StubLifecycleFn<Out>): DefinedBlock<In, Out>;
291
+ stubAfter(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): DefinedBlock<In, Out>;
292
+ /** Demo narration when the step fails / expected-failure path. */
293
+ stubOnError(stub: import("./highlights.js").HighlightStubPhase): DefinedBlock<In, Out>;
294
+ stubOnError(stub: import("./highlights.js").StubLifecycleFn<Out>): DefinedBlock<In, Out>;
295
+ stubOnError(stub: (out: Out) => import("./highlights.js").HighlightStubPhase): DefinedBlock<In, Out>;
279
296
  };
280
297
  /**
281
298
  * Structural (not imported) twin of `engine.ts`'s `Layout` - `types.ts` sits