@unotest/web 0.5.0 → 0.6.2

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.
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env bash
2
+ # @unotest/web — agent-action trace hook.
3
+ #
4
+ # Appends each Claude Code hook event (delivered as JSON on stdin) to a
5
+ # JSONL file so a debug session captures EVERYTHING the agent did: every
6
+ # tool call (name + input + output), prompts, session lifecycle, and which
7
+ # files it read (e.g. the write-e2e-test skill). Layer above UNOTEST_DEBUG's
8
+ # app-under-test artifacts — this one sees the agent's host-side actions
9
+ # that the MCP server never observes.
10
+ #
11
+ # Opt-in: no-op unless UNOTEST_DEBUG is set. Never blocks the agent — always
12
+ # exits 0 (only exit 2 would block). Reasoning/thinking is NOT capturable by
13
+ # hooks; this records tool I/O + prompts + lifecycle only.
14
+ #
15
+ # Arg $1: absolute path to the JSONL log file.
16
+
17
+ [ "$UNOTEST_DEBUG" = "1" ] || [ "$UNOTEST_DEBUG" = "full" ] || [ "$UNOTEST_DEBUG" = "summary" ] || exit 0
18
+
19
+ out="$1"
20
+ [ -n "$out" ] || exit 0
21
+ mkdir -p "$(dirname "$out")" 2>/dev/null
22
+ cat >> "$out"
23
+ printf '\n' >> "$out"
24
+ exit 0
@@ -6,8 +6,10 @@ description: Write a new E2E test for the web application under test using @unot
6
6
  # Skill: write-e2e-test
7
7
 
8
8
  You are writing an end-to-end test using `@unotest/web` against a web
9
- app. The test lives in `unotest/e2e/<name>.js` in the project under
10
- test (NOT in the `@unotest/web` package itself).
9
+ app. The test lives in `unotest/e2e/<feature>/<name>.js` in the project
10
+ under test (NOT in the `@unotest/web` package itself). Scenarios are
11
+ grouped by feature/page in subfolders — never placed directly in the
12
+ `unotest/e2e/` root (the linter rejects that with `lint:scenario-in-root`).
11
13
 
12
14
  **Default mode is verify-against-live-app.** A test that hasn't been
13
15
  run is unverified — selectors may be invented, transitions may race,
@@ -53,7 +55,7 @@ Then move to Phase 1. Three files in, exploration done.
53
55
 
54
56
  ```
55
57
  new_context { } → { ok, url: "about:blank" }
56
- explore_start { scenario_name: "<flow-name>" } → { explorationId }
58
+ explore_start { scenario_name: "<feature>/<name>" } → { explorationId }
57
59
  ```
58
60
 
59
61
  Keep the `explorationId` — every recorded step needs it.
@@ -81,30 +83,33 @@ For each page state you traverse:
81
83
  1. **Discover** — `get_page_snapshot { }`. Default outline format:
82
84
 
83
85
  ```
84
- on_screen:
86
+ regions:
85
87
  navigation:
86
88
  - link "Sets 21,414 items" [e3]
87
89
  - link "Themes" [e4]
88
90
  main:
89
91
  - heading "Browse the catalog" [e5]
90
92
  - button "Sign in" [e6]
91
- off_screen:
92
- bottom:
93
+ contentinfo (below):
93
94
  - link "Help" [e21]
94
95
  _meta:
96
+ totalActions: 7 totalRegions: 3
97
+ regions: 2 in, 0 above, 1 below, 0 partial
95
98
  viewport: 1280x720
96
- totalNodes: 142
99
+ mode: outline
97
100
  ```
98
101
 
99
- Line grammar: `- [role] ["name"] [#testId] [eN]`. The `[eN]` ref
100
- is what you'll pass back — refs are **wire-only**, never typed into
101
- the saved test. Sections (navigation / main / dialog / form / cmp /
102
- overlay) group related elements.
102
+ Line grammar: `- {role} {"name"} {#testId} {[clickable]} {[state]} [eN]`.
103
+ The `[eN]` ref is what you'll pass back — refs are **wire-only**, never
104
+ typed into the saved test. Top-level keys are landmark `regions`
105
+ (navigation / main / contentinfo / form / region / …) in reading order.
103
106
 
104
- `off_screen` lists items past the viewport they exist in the DOM
105
- but aren't visible. Scroll first (`explore_step { action:
106
- "scroll_into_view", locator: {kind:"locator", steps:[{kind:"ref", ref:"e21"}]} }`),
107
- then re-snapshot.
107
+ A region wholly or partly past the viewport carries an inline tag after
108
+ its heading `(above)` / `(below)` / `(partial)`; a fully-in-view region
109
+ has none. There is NO separate off-screen block. To reach an action in a
110
+ `(below)` region, scroll first (`explore_step { action: "scroll_into_view",
111
+ locator: {kind:"locator", steps:[{kind:"ref", ref:"e21"}]} }`), then
112
+ re-snapshot.
108
113
 
109
114
  2. **Act + record — pass the `[eN]` ref locator.**
110
115
 
@@ -196,10 +201,12 @@ Read the warnings before saving:
196
201
  Then persist:
197
202
 
198
203
  ```
199
- save_exploration_as_test { explorationId, scenarioName: "<name>" }
204
+ save_exploration_as_test { explorationId, scenarioName: "<feature>/<name>" }
200
205
  ```
201
206
 
202
- Writes `unotest/e2e/<name>.js`. File exists? Pass `overwrite: true`.
207
+ Writes `unotest/e2e/<feature>/<name>.js`. `scenarioName` MUST include a
208
+ feature subfolder (e.g. `auth/login`) — a bare name is rejected. File
209
+ exists? Pass `overwrite: true`.
203
210
 
204
211
  ### Phase 5 — reset state, then run_test
205
212
 
@@ -513,7 +520,7 @@ npx @unotest/web lint
513
520
  The linter runs two passes:
514
521
 
515
522
  1. **Validator** (`validator:unknown-function`, `validator:dsl`) —
516
- `error` severity, gates `pnpm verify`. Catches unknown function
523
+ `error` severity (fails `npx @unotest/web lint`). Catches unknown function
517
524
  names (`css`, typos), wrong arg kinds, malformed `for`/`if`
518
525
  shapes. **If you see `validator:unknown-function`, you invented
519
526
  a function name** — re-read the DSL cheat-sheet above and use the
package/CHANGELOG.md CHANGED
@@ -5,6 +5,75 @@ All notable changes to `@unotest/web` are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.6.2] - 2026-06-05
9
+
10
+ ### Added
11
+
12
+ - **MCP server `instructions`.** The server now ships start-of-session
13
+ `instructions` (injected by the MCP host into the agent's context the
14
+ moment the server is approved), pointing the agent at the
15
+ `write-e2e-test` skill and the canonical MCP tools before it
16
+ free-researches the repo.
17
+ - **`init` writes/merges Claude Code config.** `init` now copies the
18
+ `write-e2e-test` skill as a real skill **directory**
19
+ (`.claude/skills/write-e2e-test/SKILL.md`, so `/write-e2e-test` and
20
+ auto-invoke work) plus an agent-trace hook (`.claude/hooks/log-agent.sh`,
21
+ a no-op unless `UNOTEST_DEBUG` is set), and merges managed blocks into
22
+ the consumer's `CLAUDE.md` and `.claude/settings.json` (pre-approve MCP
23
+ tools, register the trace hook) without clobbering user content.
24
+
25
+ ### Changed
26
+
27
+ - CLI help uses `examples/01-smoke` as the `e2e` example (runnable example
28
+ scenarios live under `unotest/e2e/examples/`).
29
+
30
+ ### Added
31
+
32
+ - **External variables and secrets.** Scenarios reference external values
33
+ by bare `UPPER_SNAKE` identifiers (`APP_BASE_URL`, `TEST_USER_EMAIL`)
34
+ instead of hard-coding them. Non-secret values live in `unotest/.env`,
35
+ secrets in `unotest/.secrets` (gitignored); both are created by `init`
36
+ and loaded fresh on every run. Secret values are masked as
37
+ `‹secret:NAME›` in logs, debug output, and recorded artifacts, so they
38
+ never reach a saved file in plaintext.
39
+ - **Reusable flows.** Recorded exploration steps can be tagged with a
40
+ `flow` group and extracted on save into a `flow_<name>()` helper under
41
+ `unotest/e2e/_helpers/`; the generated test calls the helper instead of
42
+ inlining the steps. The new `explore_run_flow` MCP tool replays an
43
+ existing `flow_*` helper without re-recording it, and `explore_start`
44
+ now surfaces the available flows and the declared variable names.
45
+ - **Run source snapshot.** Each run writes a `sources.json` (the entry
46
+ scenario plus every loaded helper as they were when the run began), so a
47
+ run always renders against the code that actually executed even if the
48
+ file is edited afterward. (Consumes `@unotest/protocol`'s `RunSources`.)
49
+ - **New scenario linter rules:** `scenario-in-root` (scenarios must live in
50
+ a feature subfolder) and `mustache-in-dsl` (flags `{{NAME}}` template
51
+ syntax inside DSL string literals).
52
+
53
+ ### Changed (BREAKING)
54
+
55
+ - **Scenarios must live in a feature subfolder.** A scenario placed
56
+ directly in `unotest/e2e/` is now rejected by the linter — use
57
+ `unotest/e2e/<feature>/<name>.js` (the `e2e/` root is reserved for
58
+ `_helpers/`). The MCP `scenarioName` argument (`explore_start` /
59
+ `save_exploration_as_test`) likewise requires `<feature>/<name>` form,
60
+ and `init` scaffolds the welcome smoke test at
61
+ `unotest/e2e/welcome/smoke.js`.
62
+ - **`{{NAME}}` inside a DSL string literal is now a lint error.** That form
63
+ is only for MCP recorded values; in scenario code, reference a variable
64
+ as a bare identifier (`fill(loc, PASSWORD)`).
65
+
66
+ ### Fixed
67
+
68
+ - **Windows: path handling.** Run-artifact paths (entry, `sources.json`
69
+ keys, step `file` events) are normalized to project-relative posix, so
70
+ the viewer's asset allowlist and source mapping match on Windows — a
71
+ native `\` previously broke them.
72
+ - **Actionable error when no browser is installed.** Playwright launch now
73
+ preflights the browser binary and fails with a clear message hinting
74
+ `npx @unotest/web install-chromium`, instead of a raw stack trace; launch
75
+ failures are printed to the console instead of exiting silently.
76
+
8
77
  ## [0.5.0] - 2026-05-29
9
78
 
10
79
  ### Added
package/README.md CHANGED
@@ -9,80 +9,80 @@ structured failure bundles.
9
9
 
10
10
  ---
11
11
 
12
- ## 1. Setup (once)
13
-
14
- Works in any project — Node, Django, Rails, Go, anything. No `package.json`
15
- required; everything runs through `npx`.
12
+ ## 1. Setup
16
13
 
17
14
  ```sh
18
- npx @unotest/web@latest init # writes config, starter scenario, and .mcp.json
19
- npx @unotest/web install-chromium # skip if you picked system Chrome during init
15
+ npx @unotest/web@latest init
20
16
  ```
21
17
 
22
- `init` is idempotentit never overwrites your edits.
23
-
24
- ## 2. Connect your agent
18
+ Run once in your project any stack (Node, Django, Rails, Go, …), no
19
+ `package.json` required. `init` writes the config and a starter scenario,
20
+ sets up the browser (bundled Chromium, or your system Chrome if you pick it
21
+ during the prompt), and wires the MCP config so Claude Code / Cursor / Codex
22
+ pick the server up automatically. Re-run anytime to update — it never
23
+ overwrites your edits.
25
24
 
26
- `init` already wrote `.mcp.json` for you, **pinned to the version that ran
27
- it** so the MCP server never drifts. Most clients (Claude Code, Cursor, …)
28
- pick it up automatically. The entry it writes looks like:
25
+ ## 2. Open the viewer
29
26
 
30
- ```json
31
- {
32
- "mcpServers": {
33
- "unotest-web": { "command": "npx", "args": ["-y", "@unotest/web@0.5.0", "mcp"] }
34
- }
35
- }
27
+ ```sh
28
+ npx @unotest/web viewer
36
29
  ```
37
30
 
38
- Re-run `npx @unotest/web@latest init` to bump the pin. That's it your
39
- agent now has the tools to explore your app and write tests.
40
-
41
- ## 3. Create a test — just ask
31
+ A local IDE-style UI for your tests no cloud, no account. This is your
32
+ home base: browse scenarios, run them, and watch each step live. (Details
33
+ in §5.)
42
34
 
43
- In your AI editor, point it at a running app and describe the flow in
44
- plain English:
35
+ ## 3. Create a test just ask your agent
45
36
 
46
- > **"Write an e2e test: log in as a demo user, open the first project,
47
- > and check the dashboard shows today's date."**
37
+ In your AI editor (Claude Code, Codex, Cursor, …), open your project and
38
+ describe the flow in plain English:
48
39
 
49
- The agent then, through the MCP server:
40
+ > **"Open https://playground.unotest.com, go to the _Click vs Double-click_
41
+ > section, click the _Click me_ button, and check that a popup appears."**
50
42
 
51
- 1. **Explores your live app** (`explore_step` / `explore_record`) —
52
- clicks, fills, navigates — and watches the real DOM via semantic
53
- snapshots.
54
- 2. **Writes the scenario** to `unotest/e2e/<name>.js`, picking stable
55
- selectors (`getByTestId` `getByRole` never brittle CSS).
56
- 3. **Runs it** (`run_test`) and, if it fails, **debugs itself** — reads
57
- the paused browser state, the failure bundle, and console, then edits
58
- and re-runs.
43
+ Through the MCP server the agent explores your live app clicking,
44
+ filling, reading the real DOM via semantic snapshots — then writes a clean
45
+ scenario to `unotest/e2e/<name>.js` with stable selectors (`getByTestId` →
46
+ `getByRole` never brittle CSS), runs it, and debugs itself if it
47
+ fails. You get a reviewable `.js` test: read it, tweak it, commit it.
59
48
 
60
- You get a clean, reviewable `.js` test. You read it, tweak if you want,
61
- and commit. No hand-written selectors, no flaky waits.
49
+ > For best results, point the agent at its authoring guide, shipped with
50
+ > the package at `node_modules/@unotest/web/guides/agent-integration.md`.
62
51
 
63
- > Give the agent its authoring guide for best results — see
64
- > [agent-integration.md](https://github.com/unotest/web/blob/main/docs/manuals/agent-integration.md).
52
+ ## 4. Run a test
65
53
 
66
- ## 4. Run your tests
54
+ Click any scenario in the viewer, or run from the CLI:
67
55
 
68
56
  ```sh
69
- npx @unotest/web e2e <name> # one scenario
70
- npx @unotest/web e2e # see usage / list
57
+ npx @unotest/web e2e <name> # one scenario
58
+ npx @unotest/web e2e # list / usage
71
59
  ```
72
60
 
73
- Steps are grouped under `//@collapse("...")` blocks. The title says **what**
74
- the group does in plain English; the steps inside are **how**. This isn't
75
- decoration it's the intent your agent reads back to repair a step when a
76
- selector drifts, and it's how the viewer and block-editor fold a scenario
77
- into readable sections. Every step lives inside a block:
61
+ ## 5. Watch it run the viewer
62
+
63
+ `npx @unotest/web viewer` opens a local UI (no cloud, no account):
64
+
65
+ - **Scenario tree** scenarios, collections, helpers, and run history.
66
+ - **Run from the UI** — results stream live over WebSocket as steps execute.
67
+ - **Block view** — each `//@collapse` group folds into a section with
68
+ per-step status; a failure pins an error card to the offending line.
69
+ - **Step debugger** — set gutter breakpoints, run paused, inspect the live
70
+ DOM at each stop.
71
+ - **Inspector** — screenshots and failure artifacts per run.
72
+ - **Integrated terminal** — a docked xterm (toggle `` ⌃` ``) to drive the
73
+ CLI without leaving the window.
74
+
75
+ ### Writing by hand (optional)
76
+
77
+ Scenarios are plain `.js`. Every executable step sits inside a
78
+ `//@collapse("what this does")` block — plain-English intent the agent reads
79
+ back to repair a step when a selector drifts:
78
80
 
79
81
  ```js
80
82
  function test_login() {
81
- //@collapse("Log in as demo user")
83
+ //@collapse("Log in as the demo user")
82
84
  goto('https://app.example.com');
83
- click(getByRole('button', { name: /sign in/i }));
84
85
  fill(getByLabel('Email'), 'demo@example.com');
85
- fill(getByLabel('Password'), 'demo');
86
86
  click(getByRole('button', { name: 'Continue' }));
87
87
  //@endcollapse
88
88
 
@@ -92,32 +92,8 @@ function test_login() {
92
92
  }
93
93
  ```
94
94
 
95
- ## 5. Watch it run the viewer
96
-
97
- ```sh
98
- npx @unotest/web viewer
99
- ```
100
-
101
- A local IDE-style UI for your test runs (no cloud, no account):
102
-
103
- - **Scenario tree** — browse scenarios, collections, helpers, and run history.
104
- - **Run from the UI** — launch any scenario or collection; results stream
105
- live over WebSocket as steps execute.
106
- - **Block view** — each `//@collapse` group rendered as a foldable section
107
- with per-step status; failures pin an error card to the offending line.
108
- - **Step debugger** — set breakpoints in the gutter, run paused, and
109
- inspect the live DOM at each stop.
110
- - **Inspector** — screenshots and failure artifacts per run.
111
- - **Integrated terminal** — a docked xterm (toggle with `` ⌃` ``) to drive
112
- the CLI without leaving the window.
113
- - **Source ↔ block** — switch between the raw `.js` and the block view.
114
-
115
- ---
116
-
117
- ## Writing by hand (optional)
118
-
119
- Scenarios are plain `.js`. The DSL uses the familiar modern
120
- browser-automation vocabulary, so it reads the way you already expect:
95
+ The DSL uses the familiar modern browser-automation vocabulary, so it reads
96
+ the way you already expect:
121
97
 
122
98
  - **Navigation** — `goto`, `reload`, `waitForUrl`
123
99
  - **Locators** — `getByRole`, `getByTestId`, `getByLabel`, `getByText`, `getByPlaceholder`, `locator`
@@ -125,21 +101,20 @@ browser-automation vocabulary, so it reads the way you already expect:
125
101
  - **Assertions** — `assertText`, `assertUrl`, `assertVisible`, `assertChecked`, `expect`
126
102
  - **Setup** — `dbQuery`, `dbExec`, `apiCall`, `shell`, `evaluate`
127
103
 
128
- Full reference: [`dsl-reference.md`](https://github.com/unotest/web/blob/main/docs/dsl-reference.md).
129
-
130
- ## Browser support
104
+ Full reference, shipped with the package:
105
+ `node_modules/@unotest/web/guides/dsl-reference.md` (plus operational guides
106
+ alongside it in `guides/`).
131
107
 
132
- Chromium — use your system Chrome, or a bundled build via
133
- `npx @unotest/web install-chromium`.
108
+ ---
134
109
 
135
110
  ## Ecosystem
136
111
 
137
112
  | Package | Role |
138
113
  |---|---|
139
114
  | `@unotest/web` | this package — CLI / MCP server / runner |
140
- | [`@unotest/viewer`](https://github.com/unotest/viewer) | local results browser |
141
- | [`@unotest/protocol`](https://github.com/unotest/protocol) | shared types |
142
- | [`@unotest/dsl`](https://github.com/unotest/dsl) | scenario parser + validator engine |
115
+ | [`@unotest/viewer`](https://www.npmjs.com/package/@unotest/viewer) | local results browser |
116
+ | [`@unotest/protocol`](https://www.npmjs.com/package/@unotest/protocol) | shared types |
117
+ | [`@unotest/dsl`](https://www.npmjs.com/package/@unotest/dsl) | scenario parser + validator engine |
143
118
 
144
119
  ## License
145
120
 
@@ -17,43 +17,21 @@
17
17
 
18
18
  import { spawn } from "node:child_process";
19
19
  import { fileURLToPath, pathToFileURL } from "node:url";
20
- import { dirname, resolve, join } from "node:path";
20
+ import { dirname, resolve } from "node:path";
21
21
  import { createRequire } from "node:module";
22
- import { existsSync, readFileSync } from "node:fs";
22
+ import { existsSync } from "node:fs";
23
23
 
24
24
  const here = dirname(fileURLToPath(import.meta.url));
25
25
  const root = resolve(here, "..");
26
26
 
27
- // Load <cwd>/unotest/.env into process.env before anything else (MCP server
28
- // and CLI both reach buildLaunchOpts / config / db drivers via process.env).
29
- // Existing process.env wins values from `.mcp.json` env or the user's
30
- // shell override the file. Format: KEY=VALUE per line, `#` comments,
31
- // surrounding single/double quotes stripped if balanced.
32
- function loadUnotestEnv() {
33
- const path = join(process.cwd(), "unotest", ".env");
34
- if (!existsSync(path)) return;
35
- let raw;
36
- try { raw = readFileSync(path, "utf8"); } catch { return; }
37
- for (const line of raw.split(/\r?\n/)) {
38
- const trimmed = line.trim();
39
- if (trimmed === "" || trimmed.startsWith("#")) continue;
40
- const eq = trimmed.indexOf("=");
41
- if (eq <= 0) continue;
42
- const key = trimmed.slice(0, eq).trim();
43
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) continue;
44
- if (process.env[key] !== undefined) continue;
45
- let value = trimmed.slice(eq + 1).trim();
46
- if (value.length >= 2) {
47
- const first = value[0];
48
- const last = value[value.length - 1];
49
- if ((first === '"' || first === "'") && first === last) {
50
- value = value.slice(1, -1);
51
- }
52
- }
53
- process.env[key] = value;
54
- }
55
- }
56
- loadUnotestEnv();
27
+ // NOTE: `unotest/.env` is intentionally NOT loaded here. The dispatcher runs
28
+ // in every process including the long-lived `viewer` launcher and
29
+ // flattening `.env` into a launcher's process.env freezes a boot snapshot
30
+ // that spawned runs would inherit, so UI edits to `.env` wouldn't apply
31
+ // until the launcher restarted. Instead, `loadConfig()` flattens `.env` per
32
+ // run-process via `applyEnvFile`; the viewer never loads config, so each
33
+ // scenario run it spawns is a fresh process that reads the current file.
34
+ // See src/config/env-file.ts.
57
35
 
58
36
  // Resolve tsx CLI binary from @unotest/web's own node_modules. tsx CLI
59
37
  // (`tsx/cli` export → dist/cli.mjs) handles both legacy and modern Node
@@ -90,19 +90,19 @@ declare const FailureBundleConfigSchema: z.ZodObject<{
90
90
  type FailureBundleConfig = z.infer<typeof FailureBundleConfigSchema>;
91
91
  declare const DialogPolicySchema: z.ZodEnum<["accept", "dismiss", "manual"]>;
92
92
  type DialogPolicy = z.infer<typeof DialogPolicySchema>;
93
- declare const LinterRuleIdSchema: z.ZodEnum<["lint:deep-css", "lint:xpath", "lint:obfuscated-class", "lint:pause-explicit", "lint:disambig-by-index", "lint:evaluate-discouraged", "validator:unknown-function", "validator:dsl"]>;
93
+ declare const LinterRuleIdSchema: z.ZodEnum<["lint:deep-css", "lint:xpath", "lint:obfuscated-class", "lint:pause-explicit", "lint:disambig-by-index", "lint:evaluate-discouraged", "lint:scenario-in-root", "lint:mustache-in-dsl", "validator:unknown-function", "validator:dsl"]>;
94
94
  type LinterRuleId = z.infer<typeof LinterRuleIdSchema>;
95
95
  declare const LinterSeveritySchema: z.ZodEnum<["off", "warn", "error"]>;
96
96
  type LinterSeverity = z.infer<typeof LinterSeveritySchema>;
97
97
  declare const LinterConfigSchema: z.ZodObject<{
98
98
  enabled: z.ZodBoolean;
99
- rules: z.ZodRecord<z.ZodEnum<["lint:deep-css", "lint:xpath", "lint:obfuscated-class", "lint:pause-explicit", "lint:disambig-by-index", "lint:evaluate-discouraged", "validator:unknown-function", "validator:dsl"]>, z.ZodEnum<["off", "warn", "error"]>>;
99
+ rules: z.ZodRecord<z.ZodEnum<["lint:deep-css", "lint:xpath", "lint:obfuscated-class", "lint:pause-explicit", "lint:disambig-by-index", "lint:evaluate-discouraged", "lint:scenario-in-root", "lint:mustache-in-dsl", "validator:unknown-function", "validator:dsl"]>, z.ZodEnum<["off", "warn", "error"]>>;
100
100
  }, "strip", z.ZodTypeAny, {
101
101
  enabled: boolean;
102
- rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "validator:unknown-function" | "validator:dsl", "off" | "warn" | "error">>;
102
+ rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "validator:unknown-function" | "validator:dsl", "off" | "warn" | "error">>;
103
103
  }, {
104
104
  enabled: boolean;
105
- rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "validator:unknown-function" | "validator:dsl", "off" | "warn" | "error">>;
105
+ rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "validator:unknown-function" | "validator:dsl", "off" | "warn" | "error">>;
106
106
  }>;
107
107
  type LinterConfig = z.infer<typeof LinterConfigSchema>;
108
108
  declare const McpConfigSchema: z.ZodObject<{
@@ -228,13 +228,13 @@ declare const UnotestConfigSchema: z.ZodObject<{
228
228
  defaultNavigationTimeoutMs: z.ZodNumber;
229
229
  linter: z.ZodObject<{
230
230
  enabled: z.ZodBoolean;
231
- rules: z.ZodRecord<z.ZodEnum<["lint:deep-css", "lint:xpath", "lint:obfuscated-class", "lint:pause-explicit", "lint:disambig-by-index", "lint:evaluate-discouraged", "validator:unknown-function", "validator:dsl"]>, z.ZodEnum<["off", "warn", "error"]>>;
231
+ rules: z.ZodRecord<z.ZodEnum<["lint:deep-css", "lint:xpath", "lint:obfuscated-class", "lint:pause-explicit", "lint:disambig-by-index", "lint:evaluate-discouraged", "lint:scenario-in-root", "lint:mustache-in-dsl", "validator:unknown-function", "validator:dsl"]>, z.ZodEnum<["off", "warn", "error"]>>;
232
232
  }, "strip", z.ZodTypeAny, {
233
233
  enabled: boolean;
234
- rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "validator:unknown-function" | "validator:dsl", "off" | "warn" | "error">>;
234
+ rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "validator:unknown-function" | "validator:dsl", "off" | "warn" | "error">>;
235
235
  }, {
236
236
  enabled: boolean;
237
- rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "validator:unknown-function" | "validator:dsl", "off" | "warn" | "error">>;
237
+ rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "validator:unknown-function" | "validator:dsl", "off" | "warn" | "error">>;
238
238
  }>;
239
239
  mcp: z.ZodObject<{
240
240
  transport: z.ZodLiteral<"stdio">;
@@ -292,7 +292,7 @@ declare const UnotestConfigSchema: z.ZodObject<{
292
292
  defaultNavigationTimeoutMs: number;
293
293
  linter: {
294
294
  enabled: boolean;
295
- rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "validator:unknown-function" | "validator:dsl", "off" | "warn" | "error">>;
295
+ rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "validator:unknown-function" | "validator:dsl", "off" | "warn" | "error">>;
296
296
  };
297
297
  mcp: {
298
298
  transport: "stdio";
@@ -335,7 +335,7 @@ declare const UnotestConfigSchema: z.ZodObject<{
335
335
  defaultNavigationTimeoutMs: number;
336
336
  linter: {
337
337
  enabled: boolean;
338
- rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "validator:unknown-function" | "validator:dsl", "off" | "warn" | "error">>;
338
+ rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "validator:unknown-function" | "validator:dsl", "off" | "warn" | "error">>;
339
339
  };
340
340
  mcp: {
341
341
  transport: "stdio";
@@ -1 +1 @@
1
- var _0x13b842=_0x5c89;function _0xdd09(){var _0x5e35ee=['mte0mdu3m1D1qvHbvG','C3rYAw5N','Dw5VDgvZDc9LmMu','DxjS','mtiWnte3mgXgtwTPsq','CMvJB3jK','B2jQzwn0','og51D0rnAq','y3jHC2G','zxjYB3i','zgLZBwLZCW','Bwf4','D2vIA2L0','yM9VBgvHBG','DMfSAwrHDg9YoMrZBa','BNvTyMvY','BgLUDdP4Cgf0Aa','C3rKAw8','nda2ota3mwPkrfvnza','D2fYBG','y2HYB21LlwjLDge','y2HYB21L','B3b0Aw9UywW','BwLU','yxjYyxK','mtGZndnut05hBwm','BNvSBa','mti4mtu3r2L4B1rK','BgLUDdPKAxnHBwjPzY1IEs1PBMrLEa','Aw50','BxnLzgDL','Dw5VDgvZDc9LmMuVx2HLBhbLCNm','odCYmty5mhn6tKX6qW','DhjHBNnPzw50','BgL0zxjHBa','Cg9ZAxrPDMu','Dw5PB24','mti2odaZmtbvvxP6Exe','zw51Bq','y2HYB21PDw0','BgLUDdPVyMz1C2nHDgvKlwnSyxnZ','ywnJzxb0','zMLYzwzVEa','B2zM','zgvMyxvSDa','mJj1CwzxvLG','lNvUB3rLC3qVzMfPBhvYzxm','BMv0D29YAW','mtqWv2jAt3zn'];_0xdd09=function(){return _0x5e35ee;};return _0xdd09();}(function(_0x21391e,_0x290be0){var _0x3fe06b={_0x580772:0x1ee},_0x2523e1=_0x5c89,_0x24948d=_0x21391e();while(!![]){try{var _0x13f503=-parseInt(_0x2523e1(0x1f5))/0x1*(-parseInt(_0x2523e1(0x209))/0x2)+-parseInt(_0x2523e1(0x1f7))/0x3*(parseInt(_0x2523e1(0x20c))/0x4)+-parseInt(_0x2523e1(0x211))/0x5+parseInt(_0x2523e1(0x1fc))/0x6+parseInt(_0x2523e1(0x20d))/0x7+parseInt(_0x2523e1(0x214))/0x8*(-parseInt(_0x2523e1(_0x3fe06b._0x580772))/0x9)+parseInt(_0x2523e1(0x201))/0xa;if(_0x13f503===_0x290be0)break;else _0x24948d['push'](_0x24948d['shift']());}catch(_0x579fa0){_0x24948d['push'](_0x24948d['shift']());}}}(_0xdd09,0xdb3f8));import{z}from'zod';function _0x5c89(_0x13dc6a,_0x2e4bdd){_0x13dc6a=_0x13dc6a-0x1ea;var _0xdd09ee=_0xdd09();var _0x5c8924=_0xdd09ee[_0x13dc6a];if(_0x5c89['gBJOUl']===undefined){var _0x154e3c=function(_0x59b266){var _0x217219='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0x54907f='',_0x350b77='';for(var _0x7ff67b=0x0,_0x294a3c,_0x5646bb,_0x34038e=0x0;_0x5646bb=_0x59b266['charAt'](_0x34038e++);~_0x5646bb&&(_0x294a3c=_0x7ff67b%0x4?_0x294a3c*0x40+_0x5646bb:_0x5646bb,_0x7ff67b++%0x4)?_0x54907f+=String['fromCharCode'](0xff&_0x294a3c>>(-0x2*_0x7ff67b&0x6)):0x0){_0x5646bb=_0x217219['indexOf'](_0x5646bb);}for(var _0x23afbd=0x0,_0x3f5513=_0x54907f['length'];_0x23afbd<_0x3f5513;_0x23afbd++){_0x350b77+='%'+('00'+_0x54907f['charCodeAt'](_0x23afbd)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x350b77);};_0x5c89['yWsZuA']=_0x154e3c,_0x5c89['dCxQZg']={},_0x5c89['gBJOUl']=!![];}var _0x3c3e52=_0xdd09ee[0x0],_0x1f40d4=_0x13dc6a+_0x3c3e52,_0x504fd8=_0x5c89['dCxQZg'][_0x1f40d4];return!_0x504fd8?(_0x5c8924=_0x5c89['yWsZuA'](_0x5c8924),_0x5c89['dCxQZg'][_0x1f40d4]=_0x5c8924):_0x5c8924=_0x504fd8,_0x5c8924;}var BrowserSchema=z[_0x13b842(0x202)]([_0x13b842(0x203),_0x13b842(0x206),_0x13b842(0x219)]),BrowserChannelSchema=z[_0x13b842(0x200)]([z[_0x13b842(0x1fe)](_0x13b842(0x1f1)),z[_0x13b842(0x1fe)](_0x13b842(0x1fa)),z[_0x13b842(0x1fe)](_0x13b842(0x1f0)),z[_0x13b842(0x1f6)]()])['optional'](),ViewportSchema=z[_0x13b842(0x213)]({'width':z['number']()[_0x13b842(0x1f9)]()[_0x13b842(0x1ff)](),'height':z['number']()[_0x13b842(0x1f9)]()[_0x13b842(0x1ff)]()}),RetryReasonSchema=z['enum']([_0x13b842(0x1fd),_0x13b842(0x20b),_0x13b842(0x215)]),RetryConfigSchema=z['object']({'count':z[_0x13b842(0x1eb)]()[_0x13b842(0x1f9)]()[_0x13b842(0x1f3)](0x0)[_0x13b842(0x218)](0xa),'on':z[_0x13b842(0x1f4)](RetryReasonSchema)}),FailureBundleConfigSchema=z[_0x13b842(0x213)]({'tier1':z[_0x13b842(0x1fe)](!![])[_0x13b842(0x208)](!![]),'tier2':z[_0x13b842(0x21a)](),'tier3':z['object']({'network':z[_0x13b842(0x21a)](),'video':z[_0x13b842(0x21a)]()}),'retention':z['object']({'runs':z[_0x13b842(0x1eb)]()[_0x13b842(0x1f9)]()[_0x13b842(0x1ff)](),'days':z[_0x13b842(0x1eb)]()[_0x13b842(0x1f9)]()[_0x13b842(0x1ff)]()}),'storageDir':z[_0x13b842(0x20e)]()['min'](0x1)}),DialogPolicySchema=z['enum']([_0x13b842(0x205),_0x13b842(0x217),'manual']),LinterRuleIdSchema=z['enum'](['lint:deep-css',_0x13b842(0x1ec),_0x13b842(0x204),'lint:pause-explicit',_0x13b842(0x1f8),'lint:evaluate-discouraged','validator:unknown-function',_0x13b842(0x1ea)]),LinterSeveritySchema=z[_0x13b842(0x202)]([_0x13b842(0x207),'warn',_0x13b842(0x216)]),LinterConfigSchema=z[_0x13b842(0x213)]({'enabled':z[_0x13b842(0x21a)](),'rules':z[_0x13b842(0x212)](LinterRuleIdSchema,LinterSeveritySchema)}),McpConfigSchema=z[_0x13b842(0x213)]({'transport':z[_0x13b842(0x1fe)](_0x13b842(0x1ed))}),SandboxConfigSchema=z[_0x13b842(0x213)]({'shellCwd':z[_0x13b842(0x20e)]()['optional'](),'database':z[_0x13b842(0x20e)]()[_0x13b842(0x1f2)](),'apiBaseUrl':z[_0x13b842(0x20e)]()[_0x13b842(0x210)]()[_0x13b842(0x1f2)]()}),UnotestConfigSchema=z[_0x13b842(0x213)]({'baseUrl':z[_0x13b842(0x20e)]()[_0x13b842(0x210)]()['optional'](),'browsers':z[_0x13b842(0x1f4)](BrowserSchema)['min'](0x1),'channel':BrowserChannelSchema,'viewport':ViewportSchema,'retry':RetryConfigSchema,'failureBundle':FailureBundleConfigSchema,'dialogPolicy':DialogPolicySchema,'storageState':z['string']()[_0x13b842(0x1f2)](),'testDir':z['string']()['min'](0x1),'helpersDir':z[_0x13b842(0x20e)]()[_0x13b842(0x1f3)](0x1),'defaultTimeoutMs':z[_0x13b842(0x1eb)]()['int']()[_0x13b842(0x1ff)](),'defaultNavigationTimeoutMs':z['number']()['int']()[_0x13b842(0x1ff)](),'linter':LinterConfigSchema,'mcp':McpConfigSchema,'sandbox':SandboxConfigSchema}),DEFAULT_CONFIG={'browsers':[_0x13b842(0x203)],'viewport':{'width':0x500,'height':0x2d0},'retry':{'count':0x0,'on':[_0x13b842(0x1fd)]},'failureBundle':{'tier1':!![],'tier2':!![],'tier3':{'network':![],'video':![]},'retention':{'runs':0x14,'days':0x7},'storageDir':_0x13b842(0x20a)},'dialogPolicy':_0x13b842(0x205),'testDir':_0x13b842(0x20f),'helpersDir':_0x13b842(0x1fb),'defaultTimeoutMs':0xbb8,'defaultNavigationTimeoutMs':0x3a98,'linter':{'enabled':!![],'rules':{'lint:deep-css':'warn','lint:xpath':'warn','lint:obfuscated-class':_0x13b842(0x1ef),'lint:pause-explicit':_0x13b842(0x1ef),'lint:disambig-by-index':_0x13b842(0x207),'lint:evaluate-discouraged':_0x13b842(0x1ef),'validator:unknown-function':_0x13b842(0x216),'validator:dsl':_0x13b842(0x216)}},'mcp':{'transport':'stdio'},'sandbox':{}};export{BrowserChannelSchema,BrowserSchema,DEFAULT_CONFIG,DialogPolicySchema,FailureBundleConfigSchema,LinterConfigSchema,LinterRuleIdSchema,LinterSeveritySchema,McpConfigSchema,RetryConfigSchema,RetryReasonSchema,SandboxConfigSchema,UnotestConfigSchema,ViewportSchema};
1
+ function _0x16c7(){var _0xb973b1=['Cg9ZAxrPDMu','CMvJB3jK','zw51Bq','C3rYAw5N','zxjYB3i','mtjMqLfpzNa','Aw50','BgL0zxjHBa','mZu4nK5xwLbkzq','mtuZmdm1yvLirMvm','mtuZA1vPqM9R','y2HYB21LlwjLDge','Dw5VDgvZDc9LmMuVx2HLBhbLCNm','B2zM','y2HYB21PDw0','zgvMyxvSDa','y3jHC2G','BNvSBa','y2HYB21L','BxnLzgDL','nZe5otCWwuL5r3HT','Dw5PB24','BNvTyMvY','D2vIA2L0','BwLU','B3b0Aw9UywW','DxjS','DMfSAwrHDg9YoNvUA25VD24TzNvUy3rPB24','mJu0nJbzBuHRqNe','D2fYBG','B2jQzwn0','zgLZBwLZCW','BgLUDdPZy2vUyxjPBY1PBI1YB290','BMv0D29YAW','mZaXC29WD2Tr','yM9VBgvHBG','BgLUDdPLDMfSDwf0zs1KAxnJB3vYywDLza','yxjYyxK','ywnJzxb0','DhjHBNnPzw50','Dw5VDgvZDc9LmMu','BwfUDwfS','mZCWodbWt3vmB1y','otC5u1f3zMH4','mJm1odGXmeXcDvPQDa','DMfSAwrHDg9YoMrZBa','BgLUDdPTDxn0ywnOzs1PBI1KC2W','otu2nhDrvKrmzW','zMLYzwzVEa'];_0x16c7=function(){return _0xb973b1;};return _0x16c7();}var _0x3893a2=_0x1f93;(function(_0x224983,_0x4ea4ba){var _0x4a0693={_0xb7d0c5:0xab,_0x5053dd:0xa8,_0x4ef3b5:0xc5,_0x33befc:0xcd},_0x80a53d=_0x1f93,_0x2a1e1b=_0x224983();while(!![]){try{var _0x5ceb66=parseInt(_0x80a53d(0xac))/0x1+-parseInt(_0x80a53d(_0x4a0693._0xb7d0c5))/0x2+parseInt(_0x80a53d(0xad))/0x3*(-parseInt(_0x80a53d(0xa1))/0x4)+parseInt(_0x80a53d(0xb7))/0x5*(parseInt(_0x80a53d(_0x4a0693._0x5053dd))/0x6)+-parseInt(_0x80a53d(_0x4a0693._0x4ef3b5))/0x7*(parseInt(_0x80a53d(_0x4a0693._0x33befc))/0x8)+parseInt(_0x80a53d(0xcf))/0x9+-parseInt(_0x80a53d(0xbf))/0xa*(parseInt(_0x80a53d(0xce))/0xb);if(_0x5ceb66===_0x4ea4ba)break;else _0x2a1e1b['push'](_0x2a1e1b['shift']());}catch(_0x5b69b7){_0x2a1e1b['push'](_0x2a1e1b['shift']());}}}(_0x16c7,0x25788));import{z}from'zod';function _0x1f93(_0x1ec1b0,_0x2c48d8){_0x1ec1b0=_0x1ec1b0-0x9f;var _0x16c79d=_0x16c7();var _0x1f9358=_0x16c79d[_0x1ec1b0];if(_0x1f93['iUYfIn']===undefined){var _0x1e2f01=function(_0x5ba337){var _0x379035='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0x5ad0e0='',_0x477573='';for(var _0x2f68f7=0x0,_0x1cbf77,_0x5c6ba6,_0x10bb37=0x0;_0x5c6ba6=_0x5ba337['charAt'](_0x10bb37++);~_0x5c6ba6&&(_0x1cbf77=_0x2f68f7%0x4?_0x1cbf77*0x40+_0x5c6ba6:_0x5c6ba6,_0x2f68f7++%0x4)?_0x5ad0e0+=String['fromCharCode'](0xff&_0x1cbf77>>(-0x2*_0x2f68f7&0x6)):0x0){_0x5c6ba6=_0x379035['indexOf'](_0x5c6ba6);}for(var _0x5865de=0x0,_0x5b87a9=_0x5ad0e0['length'];_0x5865de<_0x5b87a9;_0x5865de++){_0x477573+='%'+('00'+_0x5ad0e0['charCodeAt'](_0x5865de)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x477573);};_0x1f93['UPRwyN']=_0x1e2f01,_0x1f93['fNATEj']={},_0x1f93['iUYfIn']=!![];}var _0xad7c19=_0x16c79d[0x0],_0x3792ff=_0x1ec1b0+_0xad7c19,_0x5080ef=_0x1f93['fNATEj'][_0x3792ff];return!_0x5080ef?(_0x1f9358=_0x1f93['UPRwyN'](_0x1f9358),_0x1f93['fNATEj'][_0x3792ff]=_0x1f9358):_0x1f9358=_0x5080ef,_0x1f9358;}var BrowserSchema=z[_0x3893a2(0xa5)](['chromium',_0x3893a2(0xa2),_0x3893a2(0xba)]),BrowserChannelSchema=z[_0x3893a2(0xb8)]([z['literal'](_0x3893a2(0xb5)),z[_0x3893a2(0xaa)](_0x3893a2(0xb6)),z[_0x3893a2(0xaa)](_0x3893a2(0xae)),z[_0x3893a2(0xb4)]()])[_0x3893a2(0xbc)](),ViewportSchema=z[_0x3893a2(0xc1)]({'width':z[_0x3893a2(0xb9)]()[_0x3893a2(0xa9)]()[_0x3893a2(0xa3)](),'height':z[_0x3893a2(0xb9)]()[_0x3893a2(0xa9)]()[_0x3893a2(0xa3)]()}),RetryReasonSchema=z[_0x3893a2(0xa5)]([_0x3893a2(0xca),_0x3893a2(0xc4),_0x3893a2(0xb3)]),RetryConfigSchema=z[_0x3893a2(0xc1)]({'count':z[_0x3893a2(0xb9)]()[_0x3893a2(0xa9)]()[_0x3893a2(0xbb)](0x0)['max'](0xa),'on':z['array'](RetryReasonSchema)}),FailureBundleConfigSchema=z[_0x3893a2(0xc1)]({'tier1':z['literal'](!![])[_0x3893a2(0xb2)](!![]),'tier2':z[_0x3893a2(0xc6)](),'tier3':z[_0x3893a2(0xc1)]({'network':z[_0x3893a2(0xc6)](),'video':z[_0x3893a2(0xc6)]()}),'retention':z[_0x3893a2(0xc1)]({'runs':z[_0x3893a2(0xb9)]()[_0x3893a2(0xa9)]()[_0x3893a2(0xa3)](),'days':z[_0x3893a2(0xb9)]()[_0x3893a2(0xa9)]()[_0x3893a2(0xa3)]()}),'storageDir':z[_0x3893a2(0xa6)]()['min'](0x1)}),DialogPolicySchema=z[_0x3893a2(0xa5)]([_0x3893a2(0xc9),_0x3893a2(0xc2),_0x3893a2(0xcc)]),LinterRuleIdSchema=z[_0x3893a2(0xa5)](['lint:deep-css','lint:xpath','lint:obfuscated-class','lint:pause-explicit','lint:disambig-by-index',_0x3893a2(0xc7),_0x3893a2(0xc3),_0x3893a2(0xa0),_0x3893a2(0xbe),_0x3893a2(0x9f)]),LinterSeveritySchema=z[_0x3893a2(0xa5)](['off',_0x3893a2(0xc0),'error']),LinterConfigSchema=z[_0x3893a2(0xc1)]({'enabled':z[_0x3893a2(0xc6)](),'rules':z[_0x3893a2(0xa4)](LinterRuleIdSchema,LinterSeveritySchema)}),McpConfigSchema=z['object']({'transport':z['literal']('stdio')}),SandboxConfigSchema=z[_0x3893a2(0xc1)]({'shellCwd':z[_0x3893a2(0xa6)]()['optional'](),'database':z[_0x3893a2(0xa6)]()['optional'](),'apiBaseUrl':z['string']()[_0x3893a2(0xbd)]()['optional']()}),UnotestConfigSchema=z['object']({'baseUrl':z[_0x3893a2(0xa6)]()[_0x3893a2(0xbd)]()[_0x3893a2(0xbc)](),'browsers':z[_0x3893a2(0xc8)](BrowserSchema)['min'](0x1),'channel':BrowserChannelSchema,'viewport':ViewportSchema,'retry':RetryConfigSchema,'failureBundle':FailureBundleConfigSchema,'dialogPolicy':DialogPolicySchema,'storageState':z[_0x3893a2(0xa6)]()[_0x3893a2(0xbc)](),'testDir':z[_0x3893a2(0xa6)]()['min'](0x1),'helpersDir':z[_0x3893a2(0xa6)]()[_0x3893a2(0xbb)](0x1),'defaultTimeoutMs':z[_0x3893a2(0xb9)]()[_0x3893a2(0xa9)]()[_0x3893a2(0xa3)](),'defaultNavigationTimeoutMs':z[_0x3893a2(0xb9)]()[_0x3893a2(0xa9)]()[_0x3893a2(0xa3)](),'linter':LinterConfigSchema,'mcp':McpConfigSchema,'sandbox':SandboxConfigSchema}),DEFAULT_CONFIG={'browsers':[_0x3893a2(0xb1)],'viewport':{'width':0x500,'height':0x2d0},'retry':{'count':0x0,'on':['transient']},'failureBundle':{'tier1':!![],'tier2':!![],'tier3':{'network':![],'video':![]},'retention':{'runs':0x14,'days':0x7},'storageDir':'.unotest/failures'},'dialogPolicy':_0x3893a2(0xc9),'testDir':_0x3893a2(0xcb),'helpersDir':_0x3893a2(0xaf),'defaultTimeoutMs':0xbb8,'defaultNavigationTimeoutMs':0x3a98,'linter':{'enabled':!![],'rules':{'lint:deep-css':'warn','lint:xpath':_0x3893a2(0xc0),'lint:obfuscated-class':_0x3893a2(0xc0),'lint:pause-explicit':_0x3893a2(0xc0),'lint:disambig-by-index':_0x3893a2(0xb0),'lint:evaluate-discouraged':_0x3893a2(0xc0),'lint:scenario-in-root':_0x3893a2(0xa7),'lint:mustache-in-dsl':'error','validator:unknown-function':_0x3893a2(0xa7),'validator:dsl':_0x3893a2(0xa7)}},'mcp':{'transport':'stdio'},'sandbox':{}};export{BrowserChannelSchema,BrowserSchema,DEFAULT_CONFIG,DialogPolicySchema,FailureBundleConfigSchema,LinterConfigSchema,LinterRuleIdSchema,LinterSeveritySchema,McpConfigSchema,RetryConfigSchema,RetryReasonSchema,SandboxConfigSchema,UnotestConfigSchema,ViewportSchema};