playthrough 0.5.1 → 0.6.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/src/test.js CHANGED
@@ -1,5 +1,5 @@
1
- import { _ as seconds, a as GLIMPSES, c as PERSON, d as RECORDING, f as WROTE, l as PICTURE, n as ASSETS, o as HANDS, p as firstLine, r as DONE_ON, s as HOLD, t as ACTIONS, u as READINGS, v as keepAssets } from "./reporter-S9fOk0za.js";
2
- import { A as repo, C as changes, D as personIn, E as make, O as setupIn, n as REACHES_OUTSIDE, r as WALKED, t as ALONGSIDE, w as firstOf } from "./flows-vi0amwSo.js";
1
+ import { _ as seconds, a as GLIMPSES, c as PERSON, d as RECORDING, f as WROTE, l as PICTURE, n as ASSETS, o as HANDS, p as firstLine, r as DONE_ON, s as HOLD, t as ACTIONS, u as READINGS, v as keepAssets } from "./reporter-DlS7qehW.js";
2
+ import { A as setupIn, E as firstOf, M as repo, O as make, T as changes, b as sameWords, k as personIn, n as FORK_RAN, r as REACHES_OUTSIDE, t as ALONGSIDE } from "./flows-CbrwgoHB.js";
3
3
  import { n as readPage, r as unhovered } from "./read-Bg9XVTtn.js";
4
4
  import { readFileSync, writeFileSync } from "node:fs";
5
5
  import { join } from "node:path";
@@ -138,7 +138,7 @@ const recorded = test$1.extend({
138
138
  rest: async ({ page }, use) => use(run ? await watchRest(page) : async () => {}),
139
139
  setup: async ({ page, request, baseURL, person, rest: _ }, use, testInfo) => {
140
140
  if (run?.only && !run.only.includes(testInfo.title)) testInfo.skip();
141
- if (run?.walk && testInfo.title !== run.walk.flow) testInfo.skip();
141
+ if (run?.fork && testInfo.title !== run.fork.flow) testInfo.skip();
142
142
  const app = await setupOf();
143
143
  if (person) personIn(app, person);
144
144
  if (run) await readied(page, run);
@@ -176,7 +176,7 @@ const recorded = test$1.extend({
176
176
  });
177
177
  for (const context of opened) await context.close();
178
178
  },
179
- recorder: [async ({ page, setup, rest, person, handTo: _ }, use, testInfo) => {
179
+ recorder: [async ({ page, request, baseURL, setup, rest, person, handTo }, use, testInfo) => {
180
180
  if (!run) return use();
181
181
  testInfo.setTimeout(testInfo.timeout * 3);
182
182
  const flow = {
@@ -191,7 +191,15 @@ const recorded = test$1.extend({
191
191
  inStep: 0,
192
192
  hands: [],
193
193
  assets: [],
194
- doneOn: {}
194
+ doneOn: {},
195
+ given: {
196
+ page,
197
+ context: page.context(),
198
+ request,
199
+ baseURL,
200
+ setup,
201
+ handTo
202
+ }
195
203
  };
196
204
  await page.exposeFunction("__playthrough", (event) => void (flow.page === page && flow.events.push(event)));
197
205
  flow.assets.push(keepAssets(page, run.folder));
@@ -214,15 +222,20 @@ const recorded = test$1.extend({
214
222
  }
215
223
  const last = flow.page;
216
224
  const assets = async () => Object.assign({}, ...await Promise.all(flow.assets.map((kept) => kept())));
217
- if (run.walk) {
218
- if (!flow.walked) return;
225
+ if (run.fork) {
226
+ if (!flow.forked) return;
219
227
  await last.waitForTimeout(HOLD);
220
228
  await last.evaluate(() => window.rrwebRecord.record.addCustomEvent("end", {}));
221
229
  await last.waitForTimeout(100);
222
- return writeFileSync(join(run.folder, WALKED), JSON.stringify({
223
- ...flow.walked,
230
+ writeFileSync(join(run.folder, "end.png"), await last.screenshot({
231
+ animations: "disabled",
232
+ caret: "hide"
233
+ }));
234
+ return writeFileSync(join(run.folder, FORK_RAN), JSON.stringify({
235
+ ...flow.forked,
224
236
  events,
225
237
  readings,
238
+ glimpses,
226
239
  assets: await assets()
227
240
  }));
228
241
  }
@@ -387,12 +400,17 @@ function answers(dialog) {
387
400
  }
388
401
  }
389
402
  let collecting;
403
+ let collectingForks = [];
390
404
  let around = [];
391
405
  let loading = Promise.resolve();
392
406
  function flowsIn(file, written) {
393
407
  const flows = /* @__PURE__ */ new Map();
394
408
  const loaded = loading.then(async () => {
395
- [collecting, around] = [flows, [{ use: {} }]];
409
+ [collecting, around, collectingForks] = [
410
+ flows,
411
+ [{ use: {} }],
412
+ []
413
+ ];
396
414
  try {
397
415
  await import(`${pathToFileURL(file).href}?${written}`);
398
416
  } finally {
@@ -406,7 +424,7 @@ function flowsIn(file, written) {
406
424
  const taking = new AsyncLocalStorage();
407
425
  const holds = (flow) => !flow.wrote && flow.page.url() === flow.address;
408
426
  var Stopped = class extends Error {};
409
- async function runAlongside(page, { request, baseURL, started, setup: appSetup, borrowed, arrived, person }, body, orders) {
427
+ async function runAlongside(page, { request, baseURL, started, setup: appSetup, borrowed, arrived, person }, body, orders, forks = []) {
410
428
  const readied = async (page) => {
411
429
  const rest = await watchRest(page);
412
430
  await page.clock.setSystemTime(onReviewDay(started));
@@ -420,6 +438,7 @@ async function runAlongside(page, { request, baseURL, started, setup: appSetup,
420
438
  };
421
439
  const flow = {
422
440
  page,
441
+ forks,
423
442
  depth: 0,
424
443
  rest: async () => {},
425
444
  actions: 0,
@@ -468,14 +487,15 @@ async function runAlongside(page, { request, baseURL, started, setup: appSetup,
468
487
  person: who,
469
488
  setup: made.setup
470
489
  });
471
- await body({
490
+ flow.given = {
472
491
  page,
473
492
  context: page.context(),
474
493
  request,
475
494
  baseURL,
476
495
  setup: made.setup,
477
496
  handTo
478
- });
497
+ };
498
+ await body(flow.given);
479
499
  await reached(flow);
480
500
  return {
481
501
  handed: flow.handed,
@@ -505,7 +525,7 @@ async function reached(flow, step, actions) {
505
525
  if (stopped) throw new Stopped();
506
526
  const where = against(flow.passed, step, actions, to);
507
527
  if (where === "on") return;
508
- if (where === "here" && to.rests) await handOver(flow, to.did);
528
+ if (where === "here" && to.rests) await handOver(flow, to);
509
529
  if (where === "past" || to.rests) {
510
530
  if (step) throw new Stopped();
511
531
  return;
@@ -547,9 +567,13 @@ async function kept(page, rest) {
547
567
  clock: kept.clock - Date.now()
548
568
  };
549
569
  }
550
- async function handOver(flow, did) {
551
- const { page, rest, from, again } = flow;
552
- if (did?.length) {
570
+ async function handOver(flow, { notes, did }) {
571
+ const fork = notes && flow.forks.find((fork) => fork.notes.some((note) => notes.includes(note)));
572
+ if (fork) {
573
+ flow.forking = true;
574
+ await fork.body(flow.given);
575
+ } else if (did?.length) {
576
+ const { page, rest } = flow;
553
577
  await taking.exit(async () => {
554
578
  for (const one of did) {
555
579
  if (!await doAgain(page, one).then(() => true, () => false)) break;
@@ -559,6 +583,7 @@ async function handOver(flow, did) {
559
583
  flow.handed = await kept(page, rest);
560
584
  return;
561
585
  }
586
+ const { page, rest, from, again } = flow;
562
587
  flow.handed = from && again.length && holds(flow) ? {
563
588
  ...from,
564
589
  again
@@ -576,6 +601,7 @@ const pathTo = (target) => target.evaluate((element) => {
576
601
  const playwrightStep = test$1.step;
577
602
  const step = Object.assign(async (title, body, options) => {
578
603
  const taken = taking.getStore();
604
+ if (taken?.forking) return body(void 0);
579
605
  if (taken) {
580
606
  const own = !taken.depth;
581
607
  if (own) Object.assign(taken, {
@@ -602,6 +628,10 @@ const step = Object.assign(async (title, body, options) => {
602
628
  step: title,
603
629
  inStep: 0
604
630
  });
631
+ flow.forked?.steps.push({
632
+ title,
633
+ at: seconds(Date.now() - (flow.events[0]?.timestamp ?? Date.now()))
634
+ });
605
635
  flow.depth++;
606
636
  let done;
607
637
  try {
@@ -625,7 +655,7 @@ const step = Object.assign(async (title, body, options) => {
625
655
  caret: "hide"
626
656
  }));
627
657
  });
628
- if (run?.walk?.step === title && run.walk.actions === void 0 && !flow.walked) await walkOn(flow, run.walk);
658
+ if (run?.fork?.step === title && run.fork.actions === void 0 && !flow.forked) await forkOn(flow, run.fork);
629
659
  return done;
630
660
  }, options);
631
661
  }, playwrightStep);
@@ -642,8 +672,10 @@ const outsideIn = (details) => {
642
672
  };
643
673
  const keeping = Object.assign((title, ...rest) => {
644
674
  const scopes = [...around];
675
+ const forks = collectingForks;
645
676
  collecting?.set(title, {
646
677
  body: rest.at(-1),
678
+ forks,
647
679
  outside: (rest.length > 1 ? outsideIn(rest[0]) : void 0) ?? scopes.findLast((scope) => scope.outside)?.outside,
648
680
  get use() {
649
681
  return Object.assign({}, ...scopes.map((scope) => scope.use));
@@ -665,7 +697,17 @@ const keeping = Object.assign((title, ...rest) => {
665
697
  },
666
698
  extend: () => keeping
667
699
  });
668
- const test = globalThis[ALONGSIDE] ? keeping : withSteps(recorded);
700
+ const forks = [];
701
+ const fork = (name, { notes }, body) => void forks.push({
702
+ name,
703
+ notes: notes.map(String),
704
+ body
705
+ });
706
+ const test = globalThis[ALONGSIDE] ? Object.assign(keeping, { fork: (name, { notes }, body) => void collectingForks.push({
707
+ name,
708
+ notes: notes.map(String),
709
+ body
710
+ }) }) : Object.assign(withSteps(recorded), { fork });
669
711
  const actions = [
670
712
  "click",
671
713
  "dblclick",
@@ -740,7 +782,7 @@ function pace(page) {
740
782
  }
741
783
  async function acted(does, target, act, given) {
742
784
  const taken = taking.getStore();
743
- const keeping = taken?.depth ? taken : void 0;
785
+ const keeping = taken?.depth || taken?.forking ? taken : void 0;
744
786
  let on, address;
745
787
  if (keeping) {
746
788
  if (!holds(keeping)) Object.assign(keeping, {
@@ -758,10 +800,10 @@ async function acted(does, target, act, given) {
758
800
  await dialog;
759
801
  });
760
802
  underway?.actions.push(Date.now());
761
- const walk = run?.walk;
762
- if (walk && underway?.depth && !underway.walked && walk.step === underway.step && walk.actions === ++underway.inStep) {
803
+ const fork = run?.fork;
804
+ if (fork && underway?.depth && !underway.forked && fork.step === underway.step && fork.actions === ++underway.inStep) {
763
805
  await underway.page.waitForTimeout(650);
764
- await walkOn(underway, walk);
806
+ await forkOn(underway, fork);
765
807
  }
766
808
  if (keeping) {
767
809
  await keeping.page.waitForTimeout(SAVING);
@@ -776,7 +818,7 @@ async function acted(does, target, act, given) {
776
818
  again: []
777
819
  });
778
820
  }
779
- if (keeping) await reached(keeping, keeping.step, ++keeping.actions);
821
+ if (keeping && !keeping.forking) await reached(keeping, keeping.step, ++keeping.actions);
780
822
  return result;
781
823
  }
782
824
  async function follow(flow, target) {
@@ -794,56 +836,32 @@ const doneOn = (target) => target.ariaSnapshot({ timeout: 1e3 }).then((snapshot)
794
836
  } : void 0;
795
837
  }, () => void 0);
796
838
  function alike(one, other) {
797
- if (one.role !== other.role) return false;
798
- const words = (name) => name.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean);
799
- const [few, many] = [words(one.name), words(other.name)].sort((a, b) => a.length - b.length);
800
- return !few.length || few.filter((word) => many.includes(word)).length * 2 >= few.length;
839
+ return one.role === other.role && sameWords(one.name, other.name);
801
840
  }
802
- async function walkOn(flow, walk) {
841
+ async function forkOn(flow, run) {
803
842
  await flow.rest();
804
843
  const since = () => seconds(Date.now() - (flow.events[0]?.timestamp ?? Date.now()));
805
- const walked = flow.walked = {
844
+ const written = forks.find((fork) => fork.notes.some((note) => run.notes.includes(note)));
845
+ const ran = flow.forked = {
806
846
  from: since(),
807
- done: [],
808
- found: []
809
- };
810
- const look = async () => {
811
- for (const [i, { after, css }] of walk.look.entries()) {
812
- if (after !== walked.done.length) continue;
813
- walked.found[i] = css ? await flow.page.evaluate((css) => {
814
- const on = document.querySelector(css);
815
- const node = on ? window.rrwebRecord.record.mirror.getId(on) : -1;
816
- return node > 0 ? node : null;
817
- }, css).catch(() => null) : null;
818
- writeFileSync(join(run.folder, `look-${i}.png`), await flow.page.screenshot({
819
- animations: "disabled",
820
- caret: "hide"
821
- }));
822
- }
847
+ steps: [],
848
+ ...written && { fork: written.name }
823
849
  };
824
- await look();
825
- for (const did of walk.did) {
850
+ if (written) {
851
+ const depth = flow.depth;
852
+ flow.depth = 0;
826
853
  try {
827
- await doAgain(flow.page, did);
828
- } catch {
829
- walked.stopped = did.what || did.on;
830
- break;
854
+ await written.body(flow.given);
855
+ } catch (error) {
856
+ ran.failed = {
857
+ ...ran.steps.length && { at: ran.steps.at(-1).title },
858
+ why: firstLine(String(error?.message ?? error))
859
+ };
860
+ } finally {
861
+ flow.depth = depth;
831
862
  }
832
- await flow.rest();
833
- walked.done.push(since());
834
- await unhovered(flow.page, async () => {
835
- const { screen, things, items } = await readPage(flow.page);
836
- flow.readings.push({
837
- step: "In the app",
838
- at: since(),
839
- screen,
840
- things,
841
- items
842
- });
843
- });
844
- await look();
845
863
  }
846
- test$1.info().skip(true, "Walked as far as the person's steps go");
864
+ test$1.info().skip(true, "Ran as far as the note's fork");
847
865
  }
848
866
  async function doAgain(page, { does, on, with: given }) {
849
867
  const target = page.locator(on).first();
@@ -862,7 +880,7 @@ async function doAgain(page, { does, on, with: given }) {
862
880
  }
863
881
  async function glimpse() {
864
882
  const flow = underway;
865
- if (!pointer || !flow || run?.walk) return;
883
+ if (!pointer || !flow || run?.fork && !flow.forked) return;
866
884
  const at = seconds(Date.now() - (flow.events[0]?.timestamp ?? Date.now()));
867
885
  const read = await readPage(flow.page).catch(() => void 0);
868
886
  if (read) flow.glimpses.push({
@@ -1,3 +1,4 @@
1
+ import type { Glimpse, Reading } from "../../page/src/index.js";
1
2
  import { type Did, type Flow } from "../../review/src/index.js";
2
3
  import { type Failed, type Written } from "./reporter.js";
3
4
  import { type App, type Repo } from "./repo.js";
@@ -14,7 +15,7 @@ export type Run = {
14
15
  before: boolean;
15
16
  taken: string[];
16
17
  started: string;
17
- walk?: Walk;
18
+ fork?: ForkRun;
18
19
  doneInRound?: Record<string, DoneOn>;
19
20
  };
20
21
  export type On = {
@@ -22,29 +23,32 @@ export type On = {
22
23
  name: string;
23
24
  };
24
25
  export type DoneOn = Record<string, (On | null)[]>;
25
- export type Walk = {
26
+ export type ForkRun = {
26
27
  flow: string;
27
28
  step: string;
28
29
  actions?: number;
29
- did: Did[];
30
- look: {
31
- after: number;
32
- css?: string;
33
- }[];
30
+ notes: string[];
34
31
  };
35
- export type Walked = {
32
+ export type ForkRan = {
36
33
  from: number;
37
- done: number[];
38
- found: (number | null)[];
39
- stopped?: string;
34
+ fork?: string;
35
+ steps: {
36
+ title: string;
37
+ at: number;
38
+ }[];
39
+ failed?: {
40
+ at?: string;
41
+ why: string;
42
+ };
40
43
  };
41
- export declare const WALKED = "walked.json";
44
+ export declare const FORK_RAN = "fork.json";
42
45
  export type To = {
43
46
  flow: string;
44
47
  step?: string;
45
48
  actions?: number;
46
49
  rests?: boolean;
47
50
  did?: Did[];
51
+ notes?: string[];
48
52
  };
49
53
  export type Handed = {
50
54
  url: string;
@@ -110,17 +114,18 @@ export type Ran = {
110
114
  failed: Failed[];
111
115
  written: Record<string, Written>;
112
116
  };
113
- export type WalkedRun = Walked & {
117
+ export type ForkRecorded = ForkRan & {
114
118
  events: {
115
119
  timestamp: number;
116
120
  }[];
117
- readings: unknown[];
121
+ readings: Reading[];
122
+ glimpses: Glimpse[];
118
123
  assets: Record<string, string>;
119
124
  files: {
120
125
  name: string;
121
126
  bytes: Buffer;
122
127
  }[];
123
- pictures: (Buffer | undefined)[];
128
+ picture?: Buffer;
124
129
  };
125
- export declare function walkFork(repo: Repo, id: string, walk: Walk): Promise<WalkedRun | undefined>;
130
+ export declare function runFork(repo: Repo, id: string, fork: ForkRun): Promise<ForkRecorded | undefined>;
126
131
  export declare const failedAt: ({ flow, at, why }: Failed) => string;
@@ -8,6 +8,13 @@ export declare const tablet: Screen;
8
8
  export declare const expect: import("@playwright/test").Expect<{}>;
9
9
  export declare const onReviewDay: (started: string, now?: number) => Date;
10
10
  export type HandTo = (person: string, screen?: Screen) => Promise<Page>;
11
+ declare const recorded: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & {
12
+ person: string | undefined;
13
+ rest: () => Promise<void>;
14
+ setup: Record<string, any>;
15
+ handTo: HandTo;
16
+ recorder: void;
17
+ }, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
11
18
  export declare function pageFor(browser: Browser, baseURL: string, screen: Screen, use?: BrowserContextOptions): Promise<{
12
19
  context: BrowserContext;
13
20
  page: Page;
@@ -31,6 +38,7 @@ export type Kept = {
31
38
  use: BrowserContextOptions & {
32
39
  person?: string;
33
40
  };
41
+ forks: Forked[];
34
42
  };
35
43
  export declare function flowsIn(file: string, written: number): Promise<Map<string, Kept>>;
36
44
  export type Arrived = (step: string | undefined, actions: number | undefined, worked: number) => void;
@@ -42,15 +50,20 @@ export declare function runAlongside(page: Page, { request, baseURL, started, se
42
50
  borrowed: string;
43
51
  arrived?: Arrived;
44
52
  person?: string;
45
- }, body: Body, orders: Orders): Promise<{
53
+ }, body: Body, orders: Orders, forks?: Forked[]): Promise<{
46
54
  handed?: Handed;
47
55
  stopped?: Pick<Failed, "at" | "why">;
48
56
  made?: Made;
49
57
  }>;
50
- export declare const test: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & {
51
- person: string | undefined;
52
- rest: () => Promise<void>;
53
- setup: Record<string, any>;
54
- handTo: HandTo;
55
- recorder: void;
56
- }, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
58
+ type Forked = {
59
+ name: string;
60
+ notes: string[];
61
+ body: Body;
62
+ };
63
+ declare const fork: (name: string, { notes }: {
64
+ notes: string[];
65
+ }, body: Body) => undefined;
66
+ export declare const test: typeof recorded & {
67
+ fork: typeof fork;
68
+ };
69
+ export {};
@@ -13,3 +13,8 @@ export declare const HOLDING: RegExp;
13
13
  export declare const nearTo: (read: Glimpse[], at: number, point: Point) => Thing | undefined;
14
14
  export declare function shownAt(readings: Reading[], element: Element, at: number): Shown | undefined;
15
15
  export declare const elementOf: (thing: Thing, name: string) => Element;
16
+ export declare function sameWords(one: string, other: string): boolean;
17
+ export declare function firstShown(element: Element, seen: Glimpse[]): {
18
+ at: number;
19
+ element: Element;
20
+ } | undefined;
@@ -61,6 +61,10 @@ export declare const Place: z.ZodObject<{
61
61
  }, z.core.$strict>>;
62
62
  recording: z.ZodString;
63
63
  length: z.ZodNumber;
64
+ steps: z.ZodOptional<z.ZodArray<z.ZodObject<{
65
+ title: z.ZodString;
66
+ at: z.ZodNumber;
67
+ }, z.core.$strict>>>;
64
68
  }, z.core.$strict>>;
65
69
  }, z.core.$strict>;
66
70
  export declare const Step: z.ZodObject<{
@@ -128,6 +132,10 @@ export declare const Change: z.ZodObject<{
128
132
  }, z.core.$strict>>;
129
133
  recording: z.ZodString;
130
134
  length: z.ZodNumber;
135
+ steps: z.ZodOptional<z.ZodArray<z.ZodObject<{
136
+ title: z.ZodString;
137
+ at: z.ZodNumber;
138
+ }, z.core.$strict>>>;
131
139
  }, z.core.$strict>>;
132
140
  }, z.core.$strict>>;
133
141
  pictureOnly: z.ZodOptional<z.ZodLiteral<true>>;
@@ -179,6 +187,10 @@ export declare const Comparison: z.ZodObject<{
179
187
  }, z.core.$strict>>;
180
188
  recording: z.ZodString;
181
189
  length: z.ZodNumber;
190
+ steps: z.ZodOptional<z.ZodArray<z.ZodObject<{
191
+ title: z.ZodString;
192
+ at: z.ZodNumber;
193
+ }, z.core.$strict>>>;
182
194
  }, z.core.$strict>>;
183
195
  }, z.core.$strict>>;
184
196
  pictureOnly: z.ZodOptional<z.ZodLiteral<true>>;
@@ -233,6 +245,10 @@ export declare const Reply: z.ZodObject<{
233
245
  }, z.core.$strict>>;
234
246
  recording: z.ZodString;
235
247
  length: z.ZodNumber;
248
+ steps: z.ZodOptional<z.ZodArray<z.ZodObject<{
249
+ title: z.ZodString;
250
+ at: z.ZodNumber;
251
+ }, z.core.$strict>>>;
236
252
  }, z.core.$strict>>;
237
253
  }, z.core.$strict>>;
238
254
  watch: z.ZodOptional<z.ZodString>;
@@ -278,6 +294,10 @@ export declare const Note: z.ZodObject<{
278
294
  }, z.core.$strict>>;
279
295
  recording: z.ZodString;
280
296
  length: z.ZodNumber;
297
+ steps: z.ZodOptional<z.ZodArray<z.ZodObject<{
298
+ title: z.ZodString;
299
+ at: z.ZodNumber;
300
+ }, z.core.$strict>>>;
281
301
  }, z.core.$strict>>;
282
302
  }, z.core.$strict>>;
283
303
  words: z.ZodString;
@@ -342,6 +362,10 @@ export declare const Round: z.ZodObject<{
342
362
  }, z.core.$strict>>;
343
363
  recording: z.ZodString;
344
364
  length: z.ZodNumber;
365
+ steps: z.ZodOptional<z.ZodArray<z.ZodObject<{
366
+ title: z.ZodString;
367
+ at: z.ZodNumber;
368
+ }, z.core.$strict>>>;
345
369
  }, z.core.$strict>>;
346
370
  }, z.core.$strict>>;
347
371
  watch: z.ZodOptional<z.ZodString>;
@@ -411,6 +435,10 @@ export declare const Send: z.ZodObject<{
411
435
  }, z.core.$strict>>;
412
436
  recording: z.ZodString;
413
437
  length: z.ZodNumber;
438
+ steps: z.ZodOptional<z.ZodArray<z.ZodObject<{
439
+ title: z.ZodString;
440
+ at: z.ZodNumber;
441
+ }, z.core.$strict>>>;
414
442
  }, z.core.$strict>>;
415
443
  }, z.core.$strict>>;
416
444
  words: z.ZodString;
@@ -500,6 +528,10 @@ export declare const Review: z.ZodObject<{
500
528
  }, z.core.$strict>>;
501
529
  recording: z.ZodString;
502
530
  length: z.ZodNumber;
531
+ steps: z.ZodOptional<z.ZodArray<z.ZodObject<{
532
+ title: z.ZodString;
533
+ at: z.ZodNumber;
534
+ }, z.core.$strict>>>;
503
535
  }, z.core.$strict>>;
504
536
  }, z.core.$strict>>;
505
537
  watch: z.ZodOptional<z.ZodString>;
@@ -562,6 +594,10 @@ export declare const Review: z.ZodObject<{
562
594
  }, z.core.$strict>>;
563
595
  recording: z.ZodString;
564
596
  length: z.ZodNumber;
597
+ steps: z.ZodOptional<z.ZodArray<z.ZodObject<{
598
+ title: z.ZodString;
599
+ at: z.ZodNumber;
600
+ }, z.core.$strict>>>;
565
601
  }, z.core.$strict>>;
566
602
  }, z.core.$strict>>;
567
603
  pictureOnly: z.ZodOptional<z.ZodLiteral<true>>;
@@ -613,6 +649,10 @@ export declare const Review: z.ZodObject<{
613
649
  }, z.core.$strict>>;
614
650
  recording: z.ZodString;
615
651
  length: z.ZodNumber;
652
+ steps: z.ZodOptional<z.ZodArray<z.ZodObject<{
653
+ title: z.ZodString;
654
+ at: z.ZodNumber;
655
+ }, z.core.$strict>>>;
616
656
  }, z.core.$strict>>;
617
657
  }, z.core.$strict>>;
618
658
  words: z.ZodString;
@@ -72,6 +72,10 @@ export declare const toAccountFor: (comparison?: Comparison) => {
72
72
  }[];
73
73
  recording: string;
74
74
  length: number;
75
+ steps?: {
76
+ title: string;
77
+ at: number;
78
+ }[] | undefined;
75
79
  } | undefined;
76
80
  }[];
77
81
  pictureOnly?: true | undefined;