@flowriki/agent 0.1.0-beta.24 → 0.1.0-beta.25

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.
@@ -6,7 +6,7 @@ import os from "os";
6
6
  import path from "path";
7
7
  import { createRequire } from "module";
8
8
  import { spawnSync } from "child_process";
9
- var AGENT_VERSION = "0.1.0-beta.24";
9
+ var AGENT_VERSION = "0.1.0-beta.25";
10
10
  async function prepareBrowsers() {
11
11
  if (browsersReady()) {
12
12
  return { ok: true, already: true, message: "chromium is already installed" };
@@ -1053,7 +1053,25 @@ var FlowOccurrenceSchema = z22.object({
1053
1053
  * against the source can line the two up; nothing depends on it to run. */
1054
1054
  stepIds: z22.record(z22.string(), z22.string()).default({}),
1055
1055
  copiedAt: z22.string()
1056
- })
1056
+ }),
1057
+ /** WHICH OF THIS ROW'S STEPS ARE WHAT RUNS BEFORE ITS FLOW — the flow's
1058
+ * sign-in and the prerequisites copied in with it — so the row can be shown
1059
+ * folded, the way the Flows screen shows the same flow.
1060
+ *
1061
+ * A description of steps the row already holds, and nothing more: they are
1062
+ * ordinary copies in `steps` and run in order whatever this says. Kept by
1063
+ * id, so editing or deleting one of them leaves the rest still grouped.
1064
+ * Absent on every row copied before this existed, which simply show flat.
1065
+ * Never sent to a machine — see `withoutFolding`. */
1066
+ before: z22.object({
1067
+ /** The flow's own sign-in, when the copy brought one. */
1068
+ signInStepId: z22.string().optional(),
1069
+ flows: z22.array(z22.object({
1070
+ flowId: z22.string(),
1071
+ name: z22.string(),
1072
+ stepIds: z22.array(z22.string())
1073
+ }).strict()).default([])
1074
+ }).strict().optional()
1057
1075
  }).strict();
1058
1076
  var CASE_SCHEMA_VERSION = 1;
1059
1077
  var TestCaseSchema = z22.object({
@@ -1575,7 +1593,7 @@ function expandForRun(flow, lookup) {
1575
1593
 
1576
1594
  // ../engine-playwright/dist/index.js
1577
1595
  import { createHmac, randomBytes } from "crypto";
1578
- import { errors } from "playwright";
1596
+ import { errors as errors2 } from "playwright";
1579
1597
 
1580
1598
  // ../core/dist/index.js
1581
1599
  function proposeAssertions(before, after) {
@@ -1896,7 +1914,7 @@ var STEP_PRESENTATION = {
1896
1914
  // ../engine-playwright/dist/index.js
1897
1915
  import fs4 from "fs";
1898
1916
  import path22 from "path";
1899
- import { chromium, firefox, webkit } from "playwright";
1917
+ import { chromium, errors, firefox, webkit } from "playwright";
1900
1918
 
1901
1919
  // ../recorder/dist/index.js
1902
1920
  function openShadowRoots() {
@@ -3923,38 +3941,63 @@ function resolveCapture(options = {}) {
3923
3941
  };
3924
3942
  }
3925
3943
  var forcedHeadless = () => process.env["FLOWRIKI_FORCE_HEADLESS"] === "1";
3944
+ var LAUNCH_ATTEMPT_MS = () => Number(process.env["FLOWRIKI_LAUNCH_TIMEOUT_MS"] ?? 45e3);
3945
+ var isStartupFailure = (error) => error instanceof errors.TimeoutError || /Target page, context or browser has been closed|Browser has been closed|browser has disconnected/i.test(String(error?.message ?? error));
3946
+ async function launchWithRetry(launch, setUp, { attempts = 2, pauseMs = 1e3 } = {}) {
3947
+ let last;
3948
+ for (let attempt = 1; attempt <= attempts; attempt++) {
3949
+ let browser = null;
3950
+ try {
3951
+ browser = await launch(LAUNCH_ATTEMPT_MS());
3952
+ return { browser, value: await setUp(browser) };
3953
+ } catch (error) {
3954
+ last = error;
3955
+ await browser?.close().catch(() => {
3956
+ });
3957
+ if (!isStartupFailure(error)) throw error;
3958
+ if (attempt < attempts) await new Promise((r) => setTimeout(r, pauseMs));
3959
+ }
3960
+ }
3961
+ const seconds = Math.round(LAUNCH_ATTEMPT_MS() / 1e3);
3962
+ const why = String(last?.message ?? last).split("\n")[0];
3963
+ throw new Error(
3964
+ `the browser did not start after ${attempts} attempts of ${seconds}s each (${why}) \u2014 on a Mac this is usually a visible browser that could not open a window; run it again, or run it headless`
3965
+ );
3966
+ }
3926
3967
  async function launchSession(environment = {}, headlessRequested = true, slowMo = 0, capture = {}, existingBrowser, storageState) {
3927
3968
  const headless = headlessRequested || forcedHeadless();
3928
3969
  const config = { ...DEFAULT_ENVIRONMENT, ...ExecutionEnvironmentSchema.parse(environment) };
3929
3970
  const fullWindow = config.fullWindow === true && !headless;
3930
3971
  const engine = { chromium, firefox, webkit }[config.browser];
3931
- const browser = existingBrowser ?? await engine.launch({
3972
+ const launch = (timeout) => engine.launch({
3932
3973
  headless,
3933
3974
  slowMo,
3975
+ timeout,
3934
3976
  // Chromium's flag. Firefox and WebKit have no equivalent, and `viewport:
3935
3977
  // null` still gives them a page that fills their default window — bigger
3936
3978
  // than the fixed box, if not maximised.
3937
3979
  ...fullWindow && config.browser === "chromium" ? { args: ["--start-maximized"] } : {}
3938
3980
  });
3981
+ const { viewport, locale, timezoneId, colorScheme } = config;
3982
+ const artifacts = resolveCapture(capture);
3983
+ const makeContext = (browser2) => browser2.newContext({
3984
+ viewport: fullWindow ? null : viewport,
3985
+ locale,
3986
+ timezoneId,
3987
+ colorScheme,
3988
+ ...storageState ? { storageState } : {},
3989
+ // Playwright can only be told to record at context creation, so a video
3990
+ // wanted "on failure" is recorded for every run and thrown away when the
3991
+ // run passes. There is no way to start one after the fact.
3992
+ //
3993
+ // The video still needs a fixed size even when the page has none —
3994
+ // `size` is what the file is encoded at, and Playwright scales the frame
3995
+ // to fit it. The configured viewport is the closest thing to an intended
3996
+ // aspect ratio we have.
3997
+ ...artifacts.video !== "off" && capture.dir ? { recordVideo: { dir: capture.dir, size: viewport } } : {}
3998
+ });
3999
+ const { browser, value: context } = existingBrowser ? { browser: existingBrowser, value: await makeContext(existingBrowser) } : await launchWithRetry(launch, makeContext);
3939
4000
  try {
3940
- const { viewport, locale, timezoneId, colorScheme } = config;
3941
- const artifacts = resolveCapture(capture);
3942
- const context = await browser.newContext({
3943
- viewport: fullWindow ? null : viewport,
3944
- locale,
3945
- timezoneId,
3946
- colorScheme,
3947
- ...storageState ? { storageState } : {},
3948
- // Playwright can only be told to record at context creation, so a video
3949
- // wanted "on failure" is recorded for every run and thrown away when the
3950
- // run passes. There is no way to start one after the fact.
3951
- //
3952
- // The video still needs a fixed size even when the page has none —
3953
- // `size` is what the file is encoded at, and Playwright scales the frame
3954
- // to fit it. The configured viewport is the closest thing to an intended
3955
- // aspect ratio we have.
3956
- ...artifacts.video !== "off" && capture.dir ? { recordVideo: { dir: capture.dir, size: viewport } } : {}
3957
- });
3958
4001
  if (artifacts.trace !== "off" && capture.dir) {
3959
4002
  await context.tracing.start({ screenshots: true, snapshots: true, sources: false });
3960
4003
  }
@@ -4839,7 +4882,7 @@ async function runStepsIn(session, steps, opts) {
4839
4882
  }
4840
4883
  } catch (err) {
4841
4884
  result.status = "failed";
4842
- result.failureKind = err.kind ?? (/strict mode violation/.test(String(err)) ? "ambiguous" : err instanceof errors.TimeoutError ? "timeout" : "unknown");
4885
+ result.failureKind = err.kind ?? (/strict mode violation/.test(String(err)) ? "ambiguous" : err instanceof errors2.TimeoutError ? "timeout" : "unknown");
4843
4886
  result.detail = firstLine2(err);
4844
4887
  }
4845
4888
  const wantsShot = artifacts.screenshot === "always" || artifacts.screenshot === "on-failure" && result.status === "failed";
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  resolveCheckouts,
9
9
  start,
10
10
  status
11
- } from "./chunk-EBT7VSPY.js";
11
+ } from "./chunk-WJ643WHC.js";
12
12
 
13
13
  // src/cli.ts
14
14
  import { parseArgs } from "util";
package/dist/engine.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  resolveCheckouts,
10
10
  start,
11
11
  status
12
- } from "./chunk-EBT7VSPY.js";
12
+ } from "./chunk-WJ643WHC.js";
13
13
  export {
14
14
  AGENT_VERSION,
15
15
  browsersReady,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowriki/agent",
3
- "version": "0.1.0-beta.24",
3
+ "version": "0.1.0-beta.25",
4
4
  "description": "Run Flowriki UI tests on your own computer. Connects this machine to your Flowriki account.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",