@unotest/web 0.25.0 → 0.26.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/CHANGELOG.md CHANGED
@@ -1,5 +1,233 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.26.0] - 2026-08-30
4
+
5
+ ### Minor Changes
6
+
7
+ - 3eede7c: The viewer can now be hosted: it shows which test bundle an environment
8
+ is running, and lets you switch it.
9
+
10
+ This is the other half of `bundle push`. Nothing changes for a local
11
+ viewer — everything here appears only when something is hosting it.
12
+
13
+ - **The artifact root and the project root are now genuinely separate.**
14
+ The viewer already took `UNOTEST_ARTIFACTS_ROOT` for the run queue;
15
+ run discovery, the run index, snapshots, log capture and the viewer's
16
+ own lock file now honour it too. Locally the two are the same directory
17
+ and nothing moves. Where they differ — sources in a disposable copy of a
18
+ bundle, history in the environment beside it — the history is no longer
19
+ read out of (or written into) the sources.
20
+ - **Bundle badge in the status bar.** `main@1a2b3c4`, or `wip` for a
21
+ bundle packed from an uncommitted tree, with the author, the pinned
22
+ `@unotest/web` and who switched the environment last. Clicking it lists
23
+ the bundles pushed to this environment and switches to one — that runs
24
+ as a maintenance ticket on the host, so it waits for the environment to
25
+ be free instead of pulling the code out from under a running suite.
26
+ - **Runs remember their bundle.** `manifest.json` and `GET /api/runs`
27
+ carry `bundleId` (from `UNOTEST_BUNDLE_ID`, set by the host); the runs
28
+ list marks the ones that ran on another bundle, so "was this red run
29
+ even the code I am looking at?" has an answer.
30
+ - **The contract is a file, not a mode.** `@unotest/protocol` gains
31
+ `WorkspaceFile` — the host writes `unotest/.workspace.json` beside the
32
+ artifacts and the viewer reads it, the same way the guard's session
33
+ header narrows the UI. No file, no bundle UI; the viewer never learns
34
+ what a box is. Values of environment variables and secrets never appear
35
+ in it — only their names.
36
+ - **Queue tickets can name a bundle.** `QueueTicket.source.bundleId` is
37
+ recorded when a run is queued, so a run executes the code it was
38
+ ordered with even if the environment moved on while it waited.
39
+
40
+ - 6d1c612: `npx @unotest/web bundle push` — send your suite to a box, instead of
41
+ giving a box access to your repository.
42
+
43
+ ```sh
44
+ npx @unotest/web bundle push --box https://tests.example.com
45
+ ```
46
+
47
+ It packs `unotest/`, `unotest.config`, `package.json` and its lockfile
48
+ into one archive and uploads it. That is the whole channel: a box holds
49
+ no deploy key and no token for anybody's code and never pulls anything,
50
+ so nothing is on it that was not pushed to it. (There is no path-scoped
51
+ git credential anywhere — a key that can fetch `unotest/` can fetch the
52
+ product source next to it, which is why this direction is the only one
53
+ we offer.)
54
+
55
+ - **Uncommitted work travels.** The manifest records `dirty: true` and
56
+ the bundle shows up as _wip_, so pushing a fix you have not committed
57
+ yet is a normal loop, not a release step.
58
+ - **What would fail on the box is refused here.** A scenario that reads a
59
+ file outside `unotest/` (`MODULE_NOT_FOUND` on the box, hours later, on
60
+ a run nobody is watching), a missing lockfile (`npm ci` will not run
61
+ without one), no `@unotest/web` in the dependencies, a symlink. Each
62
+ problem names the file, the line and the fix.
63
+ - **Secrets do not travel.** `unotest/.env*` and `.secrets*` are excluded
64
+ even if committed: environment values belong to the environment and are
65
+ injected over the bundle when it runs.
66
+ - **In a git project, git decides what belongs.** The file list comes from
67
+ `git ls-files --cached --others --exclude-standard`, so whatever your
68
+ `.gitignore` keeps out — generated databases, recordings, `.runs` —
69
+ stays out. Content is read from the working tree, not the index.
70
+ - **The id is the content.** `bundleId` is a hash of the packed files, not
71
+ a commit sha (a dirty tree's sha is not unique), so re-pushing an
72
+ unchanged suite is an instant no-op and two people packing the same tree
73
+ get the same bundle.
74
+ - `--dry-run` packs and checks without uploading, `--out <file>` keeps the
75
+ archive (plain `tar.gz` — `tar -tzf` shows what is inside), `--json`
76
+ prints `{ bundleId, status }` for a CI job that runs it afterwards.
77
+ - `UNOTEST_BOX_URL` / `UNOTEST_BOX_TOKEN` supply the address and the
78
+ token, which a CI job usually takes from its secret store. Tokens are
79
+ issued per project on the box.
80
+
81
+ - dbfa36f: Your CI can now ask a box to run the suite it just pushed, and a box can
82
+ report the result back to GitHub.
83
+
84
+ ```sh
85
+ npx @unotest/web bundle push --run --env test --collection smoke
86
+ ```
87
+
88
+ `--run` sends the second half of the CI recipe: upload the bundle, then
89
+ order a run of it. Two requests rather than one flag on the upload,
90
+ because a bundle is _content_ — pushing an identical tree twice is normal
91
+ and answers `exists` — while "run it" is an event. The same split is what
92
+ lets a deploy script run a bundle somebody pushed hours ago.
93
+
94
+ - **The box answers as soon as the runs are queued** (202) with their run
95
+ ids. A suite takes minutes to hours; an HTTP request held open for that
96
+ long is a timeout in somebody's proxy, not a result. The ids are real
97
+ from the moment you get them, so a CI log can print a link into the
98
+ viewer straight away.
99
+ - **`--env <name>` is required with `--run`.** Which environments a box
100
+ has is the operator's decision, and guessing one would be guessing
101
+ which system gets tested.
102
+ - **Without `--collection`, the box runs what that environment normally
103
+ runs** — the collections its `schedules` name. Naming them explicitly is
104
+ the deploy-check case; leaving it out is the "same as every night" case.
105
+ - **`--pr <number>` coalesces.** A newer push of the same pull request
106
+ withdraws its older runs that are still _waiting_; a run that already
107
+ started is never killed. Three pushes in five minutes cost one suite.
108
+ `--json` reports how many were withdrawn, so "my run vanished" has an
109
+ answer in the log of the run that replaced it.
110
+ - **Refusals are typed and say what to do**: no such environment, an
111
+ environment belonging to another project, a bundle the box no longer
112
+ holds, nothing to run.
113
+
114
+ On a hosted viewer, the queue panel now names the change each machine
115
+ ticket belongs to (`PR #412@1a2b3c4`) instead of a row of identical "ci"
116
+ entries, and the history gained a **this bundle only** filter for the
117
+ moment you are judging the bundle in front of you rather than the last
118
+ month of runs.
119
+
120
+ Nothing changes for a local `npx @unotest/web viewer`: there are no
121
+ bundles there, so neither the badge nor the filter appears.
122
+
123
+ - c43aa00: Runs of one project now take turns instead of colliding.
124
+
125
+ Until now nothing coordinated them: the viewer refused a second run with
126
+ HTTP 409, and that was the whole defence — a `npx @unotest/web e2e` in a
127
+ terminal, an agent's `run_test` and a scheduled suite would all start on
128
+ top of each other, driving the same browser and the same seeded database
129
+ at the same time. The 409 protected the UI, not the machine.
130
+
131
+ - **A filesystem queue**, in `unotest/.queue<target>[.<env>]/` beside the
132
+ runs root it belongs to. A run writes a ticket, waits until it is at
133
+ the head, takes a slot, runs, and gives the slot back. There is no
134
+ daemon: whoever holds the slot executes the run itself, so nothing new
135
+ has to be installed, started or kept alive. Only `open("wx")`, rename,
136
+ unlink and mtime — no `flock`, which lies on network and container
137
+ filesystems. A crashed producer is reaped by whoever comes next.
138
+ - **Every producer is in it**: the CLI (`e2e`, `collection`, `author`),
139
+ the viewer's Run button, and the MCP `run_test` (which spawns the CLI).
140
+ A collection is ONE run — its `workers` still run in parallel inside
141
+ it, because a child process carries its parent's lease and skips the
142
+ queue.
143
+ - **The viewer shows the queue**: the Active panel lists what is waiting,
144
+ including tickets a terminal or an agent wrote, with a button to take
145
+ any of them back out. `GET /api/queue`, `DELETE /api/queue/:ticket`,
146
+ and a `queue:changed` websocket message.
147
+ - **Config**: `queue.concurrency` (default 1) and `queue.enabled`
148
+ (default true) in `unotest.config`. Both the CLI and the viewer read
149
+ the same file, so they cannot disagree about how many slots exist.
150
+ - **Escape hatches**: `UNOTEST_NO_QUEUE=1` runs without queueing;
151
+ Ctrl-C while waiting gives up your place in line and runs nothing.
152
+ - **For boxes**: `UNOTEST_QUEUE_GLOBAL_DIR` + `UNOTEST_QUEUE_GLOBAL_SLOTS`
153
+ add a host-wide budget several environments share (a collection weighs
154
+ its worker count), and `UNOTEST_ARTIFACTS_ROOT` puts `.runs` / `.queue`
155
+ somewhere other than the directory the tests live in — so the history
156
+ outlives a throwaway copy of the tests.
157
+
158
+ ### Migration notes
159
+
160
+ **`POST /api/run` no longer answers 409.** Ordering a run while another
161
+ one is active used to fail with `ActiveRunConflictError` (HTTP 409); it
162
+ now succeeds and the run waits. The response changed shape with it:
163
+
164
+ ```diff
165
+ - { runId, kind, ref, pid, startedAt }
166
+ + { runId, kind, ref, status: "queued" | "running", ticket, acceptedAt }
167
+ ```
168
+
169
+ `runId` is still final and immediately usable — open the tab on it as
170
+ before — but the run may not have started yet, and `pid` is not known at
171
+ that point. `POST /api/runs/:runId/abort` covers both states: it
172
+ withdraws a waiting ticket or SIGTERMs a running child.
173
+
174
+ Anything that treated 409 as "busy, try later" should now simply order
175
+ the run. Anything that relied on "only one run can exist" should read
176
+ `GET /api/queue`. To keep the old immediate-start behaviour (without the
177
+ protection), set `queue.enabled: false`.
178
+
179
+ - 8ec4177: Scheduled runs: declare what should run on its own, next to the tests it
180
+ runs.
181
+
182
+ ```js
183
+ // unotest.config.mjs
184
+ export default {
185
+ schedules: [
186
+ { collection: "smoke", cron: "0 * * * *" },
187
+ { collection: "nightly", cron: "0 3 * * *", prepare: "node seed.mjs" },
188
+ ],
189
+ };
190
+ ```
191
+
192
+ - **`schedules`** — a new config leaf: collection + cron, plus an
193
+ optional `env` (which environment the run happens in) and `prepare` (a
194
+ command run before the suite). Versioned with the tests, so "what is
195
+ supposed to run nightly" is in the repo instead of on a machine nobody
196
+ can see.
197
+ - **`npx @unotest/web schedules`** prints what is declared; `--json` is
198
+ the machine shape a scheduler reads back. Each entry also carries the
199
+ collection's `workers` resolved from its yaml manifest, so an executor
200
+ can weigh the run without parsing yaml itself.
201
+ - **`npx @unotest/web collection <name> --scheduled`** runs a collection
202
+ the way its schedule does: in the entry's `env` (unless you set one)
203
+ and with its `prepare` command first, inside the same run slot
204
+ (preparing touches the target, which is exactly what the run queue
205
+ serialises). A failed prepare exits **94**, not 1 — "the environment
206
+ was not prepared, nothing was tested" is a different event from "the
207
+ suite is red", and alerting has to tell them apart. A collection
208
+ listed in `schedules` more than once needs `--scheduled=<index>`
209
+ (zero-based) — guessing which entry's prepare to run is refused.
210
+
211
+ **Nothing in this package ticks.** `schedules` is data: no cron parser,
212
+ no timers, no background runs. Cron is executed by whatever hosts your
213
+ runs — your CI (`on: schedule` + `unotest-web collection`), or a box. A
214
+ clone of a repo that declares schedules therefore never starts running
215
+ suites by itself; `unotest-web schedules` says so out loud instead of
216
+ implying a timer that does not exist.
217
+
218
+ ### Patch Changes
219
+
220
+ - Updated dependencies [3eede7c]
221
+ - Updated dependencies [6d1c612]
222
+ - Updated dependencies [dbfa36f]
223
+ - Updated dependencies [c43aa00]
224
+ - Updated dependencies [8ec4177]
225
+ - @unotest/viewer@0.26.0
226
+ - @unotest/protocol@0.26.0
227
+ - @unotest/core@0.26.0
228
+ - @unotest/dsl@0.26.0
229
+ - @unotest/grounder-client@0.26.0
230
+
3
231
  ## [0.25.0] - 2026-08-29
4
232
 
5
233
  ### Minor Changes
package/README.md CHANGED
@@ -65,6 +65,71 @@ npx @unotest/web e2e <name> # one scenario
65
65
  npx @unotest/web e2e # list / usage
66
66
  ```
67
67
 
68
+ Runs of one project take turns. A second run — from another terminal, the
69
+ viewer's Run button or an agent — joins a queue in `unotest/.queue/` and
70
+ starts when the machine is free, instead of fighting the first one over
71
+ the same browser and the same seeded data. It costs nothing when nothing
72
+ else is running, the viewer shows who is waiting (and can cancel them),
73
+ and Ctrl-C gives up your place in line. Raise `queue.concurrency` in
74
+ `unotest.config` to allow more at once, or set `UNOTEST_NO_QUEUE=1` for a
75
+ single command that must not wait. A collection is one run: its `workers`
76
+ still run in parallel inside it.
77
+
78
+ ### Running on a schedule
79
+
80
+ Declare what should run on its own next to the tests it runs:
81
+
82
+ ```js
83
+ // unotest.config.mjs
84
+ export default {
85
+ schedules: [
86
+ { collection: "smoke", cron: "0 * * * *" },
87
+ { collection: "nightly", cron: "0 3 * * *", prepare: "node seed.mjs" },
88
+ ],
89
+ };
90
+ ```
91
+
92
+ `npx @unotest/web schedules` prints them. Nothing in the CLI ticks: cron
93
+ is executed by whatever hosts your runs — your CI, or a box — so a clone
94
+ of your repo never starts running suites in the background. Run one now
95
+ with `npx @unotest/web collection nightly --scheduled` (that also runs its
96
+ `prepare` command, inside the same run slot).
97
+
98
+ ### Sending the suite to a box
99
+
100
+ If your team runs a box (a machine that keeps the environments, the
101
+ schedules and the history in one place), tests get there by being pushed:
102
+
103
+ ```sh
104
+ npx @unotest/web bundle push --box https://tests.example.com
105
+ ```
106
+
107
+ It packs `unotest/`, your `unotest.config`, `package.json` and its
108
+ lockfile — nothing else — and uploads that. **The box never reads your
109
+ repository**: it holds no key to it, so nothing is there that you did not
110
+ send. Uncommitted work is included and marked *wip*, which is what makes
111
+ this a fast loop rather than a release step. Secrets stay behind:
112
+ `unotest/.env*` and `.secrets*` never travel, because environment values
113
+ belong to the environment and are injected over the bundle when it runs.
114
+
115
+ What would break on the box is refused here instead — a scenario that
116
+ reads a file outside `unotest/`, a missing lockfile, no `@unotest/web`
117
+ pin. The bundle's id is a hash of its content, so pushing an unchanged
118
+ suite is a no-op.
119
+
120
+ From a CI job, push and run in one command:
121
+
122
+ ```sh
123
+ npx @unotest/web bundle push --run --env test --collection smoke --pr 412
124
+ ```
125
+
126
+ The box answers as soon as the runs are queued and gives you their ids —
127
+ a suite takes minutes, and a request held open that long is a timeout,
128
+ not a result. Watch them in the viewer, or let the box report the checks
129
+ back to GitHub. `--pr` makes a newer push withdraw the older runs of the
130
+ same pull request that are still waiting, so three pushes in five minutes
131
+ cost one suite. Recipe: `guides/manuals/ci-setup.md`.
132
+
68
133
  ## 5. Watch it run — the viewer
69
134
 
70
135
  `npx @unotest/web viewer` opens a local UI (no cloud, no account):
@@ -105,6 +105,49 @@ declare const LinterConfigSchema: z.ZodObject<{
105
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" | "lint:goto-concat" | "lint:regex-invalid" | "lint:echo-assert" | "lint:api-call-file-body" | "lint:lint-ok-missing-reason" | "lint:flow-returns-value" | "validator:unknown-function" | "validator:arity" | "validator:arg-kind" | "validator:step-coverage" | "validator:step-shape" | "validator:meta-shape" | "validator:function-shape" | "validator:for-shape" | "validator:if-shape" | "validator:var-shape" | "validator:unsupported-statement" | "validator:unsupported-expression" | "validator:string-concat" | "validator:semantic-loss" | "validator:list-arg" | "validator:wrapped-format", "off" | "warn" | "error">>;
106
106
  }>;
107
107
  type LinterConfig = z.infer<typeof LinterConfigSchema>;
108
+ declare const QueueConfigSchema: z.ZodObject<{
109
+ /** Turn queueing off for this project. `UNOTEST_NO_QUEUE=1` does the
110
+ * same for a single command. */
111
+ enabled: z.ZodDefault<z.ZodBoolean>;
112
+ /** How many runs may be in flight for one environment. 1 (the default)
113
+ * is strict FIFO — the machine runs one thing at a time. */
114
+ concurrency: z.ZodDefault<z.ZodNumber>;
115
+ }, "strip", z.ZodTypeAny, {
116
+ enabled: boolean;
117
+ concurrency: number;
118
+ }, {
119
+ enabled?: boolean | undefined;
120
+ concurrency?: number | undefined;
121
+ }>;
122
+ type QueueConfig = z.infer<typeof QueueConfigSchema>;
123
+ declare const ScheduleSchema: z.ZodObject<{
124
+ /** Collection name — file stem under `_collections/`. */
125
+ collection: z.ZodString;
126
+ /** Standard 5-field cron expression, interpreted in the timezone of
127
+ * whatever executes it (a box container is UTC unless its TZ says
128
+ * otherwise). Validated as a non-empty string and no further:
129
+ * checking the EXPRESSION would mean shipping the cron parser this
130
+ * package deliberately does not have. */
131
+ cron: z.ZodString;
132
+ /** Environment the run happens in (`.env.<name>` overlay + its own
133
+ * `.runs.<name>` root). Absent = base. */
134
+ env: z.ZodOptional<z.ZodString>;
135
+ /** Shell command run before the collection, inside the same queue slot
136
+ * — seeding a database, resetting a fixture. Its failure is reported
137
+ * as "the environment was not prepared", not as a red suite. */
138
+ prepare: z.ZodOptional<z.ZodString>;
139
+ }, "strip", z.ZodTypeAny, {
140
+ collection: string;
141
+ cron: string;
142
+ env?: string | undefined;
143
+ prepare?: string | undefined;
144
+ }, {
145
+ collection: string;
146
+ cron: string;
147
+ env?: string | undefined;
148
+ prepare?: string | undefined;
149
+ }>;
150
+ type ScheduleConfig = z.infer<typeof ScheduleSchema>;
108
151
  declare const McpConfigSchema: z.ZodObject<{
109
152
  transport: z.ZodLiteral<"stdio">;
110
153
  }, "strip", z.ZodTypeAny, {
@@ -172,15 +215,15 @@ declare const WebServerConfigSchema: z.ZodObject<{
172
215
  /** How long to wait for the URL to come up after spawning. */
173
216
  timeoutMs: z.ZodDefault<z.ZodNumber>;
174
217
  }, "strip", z.ZodTypeAny, {
175
- url: string;
176
- timeoutMs: number;
177
218
  command: string;
219
+ url: string;
178
220
  reuseExistingServer: boolean;
221
+ timeoutMs: number;
179
222
  }, {
180
- url: string;
181
223
  command: string;
182
- timeoutMs?: number | undefined;
224
+ url: string;
183
225
  reuseExistingServer?: boolean | undefined;
226
+ timeoutMs?: number | undefined;
184
227
  }>;
185
228
  type WebServerConfig = z.infer<typeof WebServerConfigSchema>;
186
229
  declare const UnotestConfigSchema: z.ZodObject<{
@@ -206,15 +249,15 @@ declare const UnotestConfigSchema: z.ZodObject<{
206
249
  /** How long to wait for the URL to come up after spawning. */
207
250
  timeoutMs: z.ZodDefault<z.ZodNumber>;
208
251
  }, "strip", z.ZodTypeAny, {
209
- url: string;
210
- timeoutMs: number;
211
252
  command: string;
253
+ url: string;
212
254
  reuseExistingServer: boolean;
255
+ timeoutMs: number;
213
256
  }, {
214
- url: string;
215
257
  command: string;
216
- timeoutMs?: number | undefined;
258
+ url: string;
217
259
  reuseExistingServer?: boolean | undefined;
260
+ timeoutMs?: number | undefined;
218
261
  }>>;
219
262
  browsers: z.ZodArray<z.ZodEnum<["chromium", "firefox", "webkit"]>, "many">;
220
263
  channel: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"chrome">, z.ZodLiteral<"msedge">, z.ZodLiteral<"chrome-beta">, z.ZodNull]>>;
@@ -327,6 +370,48 @@ declare const UnotestConfigSchema: z.ZodObject<{
327
370
  }, {
328
371
  transport: "stdio";
329
372
  }>;
373
+ queue: z.ZodObject<{
374
+ /** Turn queueing off for this project. `UNOTEST_NO_QUEUE=1` does the
375
+ * same for a single command. */
376
+ enabled: z.ZodDefault<z.ZodBoolean>;
377
+ /** How many runs may be in flight for one environment. 1 (the default)
378
+ * is strict FIFO — the machine runs one thing at a time. */
379
+ concurrency: z.ZodDefault<z.ZodNumber>;
380
+ }, "strip", z.ZodTypeAny, {
381
+ enabled: boolean;
382
+ concurrency: number;
383
+ }, {
384
+ enabled?: boolean | undefined;
385
+ concurrency?: number | undefined;
386
+ }>;
387
+ /** Scheduled runs (data — nothing here executes them). */
388
+ schedules: z.ZodDefault<z.ZodArray<z.ZodObject<{
389
+ /** Collection name — file stem under `_collections/`. */
390
+ collection: z.ZodString;
391
+ /** Standard 5-field cron expression, interpreted in the timezone of
392
+ * whatever executes it (a box container is UTC unless its TZ says
393
+ * otherwise). Validated as a non-empty string and no further:
394
+ * checking the EXPRESSION would mean shipping the cron parser this
395
+ * package deliberately does not have. */
396
+ cron: z.ZodString;
397
+ /** Environment the run happens in (`.env.<name>` overlay + its own
398
+ * `.runs.<name>` root). Absent = base. */
399
+ env: z.ZodOptional<z.ZodString>;
400
+ /** Shell command run before the collection, inside the same queue slot
401
+ * — seeding a database, resetting a fixture. Its failure is reported
402
+ * as "the environment was not prepared", not as a red suite. */
403
+ prepare: z.ZodOptional<z.ZodString>;
404
+ }, "strip", z.ZodTypeAny, {
405
+ collection: string;
406
+ cron: string;
407
+ env?: string | undefined;
408
+ prepare?: string | undefined;
409
+ }, {
410
+ collection: string;
411
+ cron: string;
412
+ env?: string | undefined;
413
+ prepare?: string | undefined;
414
+ }>, "many">>;
330
415
  sandbox: z.ZodObject<{
331
416
  /** Default cwd for `shell(cmd, ...args)`. Defaults to `process.cwd()`. */
332
417
  shellCwd: z.ZodOptional<z.ZodString>;
@@ -403,6 +488,16 @@ declare const UnotestConfigSchema: z.ZodObject<{
403
488
  mcp: {
404
489
  transport: "stdio";
405
490
  };
491
+ queue: {
492
+ enabled: boolean;
493
+ concurrency: number;
494
+ };
495
+ schedules: {
496
+ collection: string;
497
+ cron: string;
498
+ env?: string | undefined;
499
+ prepare?: string | undefined;
500
+ }[];
406
501
  sandbox: {
407
502
  shellCwd?: string | undefined;
408
503
  shellTimeoutMs?: number | undefined;
@@ -413,10 +508,10 @@ declare const UnotestConfigSchema: z.ZodObject<{
413
508
  };
414
509
  baseUrl?: string | undefined;
415
510
  webServer?: {
416
- url: string;
417
- timeoutMs: number;
418
511
  command: string;
512
+ url: string;
419
513
  reuseExistingServer: boolean;
514
+ timeoutMs: number;
420
515
  } | undefined;
421
516
  channel?: "chrome" | "msedge" | "chrome-beta" | null | undefined;
422
517
  storageState?: string | undefined;
@@ -455,6 +550,10 @@ declare const UnotestConfigSchema: z.ZodObject<{
455
550
  mcp: {
456
551
  transport: "stdio";
457
552
  };
553
+ queue: {
554
+ enabled?: boolean | undefined;
555
+ concurrency?: number | undefined;
556
+ };
458
557
  sandbox: {
459
558
  shellCwd?: string | undefined;
460
559
  shellTimeoutMs?: number | undefined;
@@ -465,16 +564,22 @@ declare const UnotestConfigSchema: z.ZodObject<{
465
564
  };
466
565
  baseUrl?: string | undefined;
467
566
  webServer?: {
468
- url: string;
469
567
  command: string;
470
- timeoutMs?: number | undefined;
568
+ url: string;
471
569
  reuseExistingServer?: boolean | undefined;
570
+ timeoutMs?: number | undefined;
472
571
  } | undefined;
473
572
  channel?: "chrome" | "msedge" | "chrome-beta" | null | undefined;
474
573
  storageState?: string | undefined;
574
+ schedules?: {
575
+ collection: string;
576
+ cron: string;
577
+ env?: string | undefined;
578
+ prepare?: string | undefined;
579
+ }[] | undefined;
475
580
  }>;
476
581
  type UnotestConfig = z.infer<typeof UnotestConfigSchema>;
477
582
  type UnotestConfigInput = z.input<typeof UnotestConfigSchema>;
478
583
  declare const DEFAULT_CONFIG: UnotestConfig;
479
584
 
480
- export { type Browser, type BrowserChannel, BrowserChannelSchema, BrowserSchema, DEFAULT_CONFIG, type DialogPolicy, DialogPolicySchema, type FailureBundleConfig, FailureBundleConfigSchema, type LinterConfig, LinterConfigSchema, type LinterRuleId, LinterRuleIdSchema, type LinterSeverity, LinterSeveritySchema, type McpConfig, McpConfigSchema, type RetryConfig, RetryConfigSchema, type RetryReason, RetryReasonSchema, type SandboxConfig, SandboxConfigSchema, type UnotestConfig, type UnotestConfigInput, UnotestConfigSchema, type Viewport, ViewportSchema, type WebServerConfig, WebServerConfigSchema };
585
+ export { type Browser, type BrowserChannel, BrowserChannelSchema, BrowserSchema, DEFAULT_CONFIG, type DialogPolicy, DialogPolicySchema, type FailureBundleConfig, FailureBundleConfigSchema, type LinterConfig, LinterConfigSchema, type LinterRuleId, LinterRuleIdSchema, type LinterSeverity, LinterSeveritySchema, type McpConfig, McpConfigSchema, type QueueConfig, QueueConfigSchema, type RetryConfig, RetryConfigSchema, type RetryReason, RetryReasonSchema, type SandboxConfig, SandboxConfigSchema, type ScheduleConfig, ScheduleSchema, type UnotestConfig, type UnotestConfigInput, UnotestConfigSchema, type Viewport, ViewportSchema, type WebServerConfig, WebServerConfigSchema };
@@ -1 +1 @@
1
- var _0x17baec=_0x1bc8;(function(_0x350fe5,_0x1dd858){var _0x286110={_0x1a38ff:0x110,_0xd42cbe:0xe6,_0x467426:0x10e,_0x5ec3a4:0x109,_0x3d5261:0xf8,_0x1425ba:0xf2,_0x4bb9c8:0x11d,_0x22ed6a:0xf7},_0xa9b00e=_0x1bc8,_0x1d8761=_0x350fe5();while(!![]){try{var _0x28d445=parseInt(_0xa9b00e(_0x286110._0x1a38ff))/0x1*(-parseInt(_0xa9b00e(_0x286110._0xd42cbe))/0x2)+-parseInt(_0xa9b00e(_0x286110._0x467426))/0x3*(parseInt(_0xa9b00e(0xef))/0x4)+parseInt(_0xa9b00e(_0x286110._0x5ec3a4))/0x5+-parseInt(_0xa9b00e(_0x286110._0x3d5261))/0x6+-parseInt(_0xa9b00e(0xf0))/0x7*(parseInt(_0xa9b00e(_0x286110._0x1425ba))/0x8)+-parseInt(_0xa9b00e(_0x286110._0x4bb9c8))/0x9*(-parseInt(_0xa9b00e(0x11c))/0xa)+parseInt(_0xa9b00e(0xed))/0xb*(parseInt(_0xa9b00e(_0x286110._0x22ed6a))/0xc);if(_0x28d445===_0x1dd858)break;else _0x1d8761['push'](_0x1d8761['shift']());}catch(_0x7489bc){_0x1d8761['push'](_0x1d8761['shift']());}}}(_0x30f9,0x86908));function _0x1bc8(_0x523303,_0x477656){_0x523303=_0x523303-0xe0;var _0x30f964=_0x30f9();var _0x1bc80b=_0x30f964[_0x523303];if(_0x1bc8['fROSgj']===undefined){var _0x2659a1=function(_0x322809){var _0x2079f6='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0x2fd026='',_0x2e085c='';for(var _0x22b087=0x0,_0x3e4599,_0xb6df33,_0x27cc01=0x0;_0xb6df33=_0x322809['charAt'](_0x27cc01++);~_0xb6df33&&(_0x3e4599=_0x22b087%0x4?_0x3e4599*0x40+_0xb6df33:_0xb6df33,_0x22b087++%0x4)?_0x2fd026+=String['fromCharCode'](0xff&_0x3e4599>>(-0x2*_0x22b087&0x6)):0x0){_0xb6df33=_0x2079f6['indexOf'](_0xb6df33);}for(var _0x402e8a=0x0,_0x448e19=_0x2fd026['length'];_0x402e8a<_0x448e19;_0x402e8a++){_0x2e085c+='%'+('00'+_0x2fd026['charCodeAt'](_0x402e8a)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x2e085c);};_0x1bc8['TYZSEc']=_0x2659a1,_0x1bc8['kREMdJ']={},_0x1bc8['fROSgj']=!![];}var _0x56e031=_0x30f964[0x0],_0x3171cb=_0x523303+_0x56e031,_0x2aef2c=_0x1bc8['kREMdJ'][_0x3171cb];return!_0x2aef2c?(_0x1bc80b=_0x1bc8['TYZSEc'](_0x1bc80b),_0x1bc8['kREMdJ'][_0x3171cb]=_0x1bc80b):_0x1bc80b=_0x2aef2c,_0x1bc80b;}import{z}from'zod';var BrowserSchema=z[_0x17baec(0x106)]([_0x17baec(0x11e),_0x17baec(0xe4),_0x17baec(0xfb)]),BrowserChannelSchema=z[_0x17baec(0x122)]([z['literal'](_0x17baec(0x10a)),z['literal'](_0x17baec(0x11f)),z[_0x17baec(0xf5)]('chrome-beta'),z['null']()])[_0x17baec(0xf6)](),ViewportSchema=z[_0x17baec(0x10c)]({'width':z['number']()[_0x17baec(0xe5)]()[_0x17baec(0xfa)](),'height':z['number']()['int']()['positive']()}),RetryReasonSchema=z[_0x17baec(0x106)]([_0x17baec(0x120),'network',_0x17baec(0xeb)]),RetryConfigSchema=z[_0x17baec(0x10c)]({'count':z[_0x17baec(0x112)]()[_0x17baec(0xe5)]()['min'](0x0)[_0x17baec(0xf3)](0xa),'on':z['array'](RetryReasonSchema)}),FailureBundleConfigSchema=z[_0x17baec(0x10c)]({'tier1':z[_0x17baec(0xf5)](!![])[_0x17baec(0x113)](!![]),'tier2':z[_0x17baec(0x102)](),'tier3':z['object']({'network':z[_0x17baec(0x102)](),'video':z[_0x17baec(0x102)]()}),'retention':z[_0x17baec(0x10c)]({'runs':z[_0x17baec(0x112)]()[_0x17baec(0xe5)]()['positive'](),'days':z[_0x17baec(0x112)]()[_0x17baec(0xe5)]()[_0x17baec(0xfa)]()}),'storageDir':z[_0x17baec(0xfd)]()[_0x17baec(0x121)](0x1)}),DialogPolicySchema=z[_0x17baec(0x106)]([_0x17baec(0x101),_0x17baec(0xf4),_0x17baec(0xe9)]),LinterRuleIdSchema=z[_0x17baec(0x106)]([_0x17baec(0x104),'lint:xpath',_0x17baec(0x107),_0x17baec(0xe7),_0x17baec(0x10d),_0x17baec(0xf9),_0x17baec(0x118),_0x17baec(0xe8),'lint:goto-concat',_0x17baec(0xe1),_0x17baec(0xff),_0x17baec(0xfc),'lint:lint-ok-missing-reason',_0x17baec(0x105),_0x17baec(0x119),_0x17baec(0x103),_0x17baec(0x116),_0x17baec(0xfe),_0x17baec(0x10b),'validator:meta-shape',_0x17baec(0x10f),_0x17baec(0x115),'validator:if-shape','validator:var-shape',_0x17baec(0x11b),_0x17baec(0xea),_0x17baec(0x108),_0x17baec(0x114),_0x17baec(0xec),_0x17baec(0x100)]),LinterSeveritySchema=z[_0x17baec(0x106)]([_0x17baec(0x117),'warn','error']),LinterConfigSchema=z[_0x17baec(0x10c)]({'enabled':z[_0x17baec(0x102)](),'rules':z['record'](LinterRuleIdSchema,LinterSeveritySchema)}),McpConfigSchema=z[_0x17baec(0x10c)]({'transport':z[_0x17baec(0xf5)](_0x17baec(0x111))}),SandboxConfigSchema=z[_0x17baec(0x10c)]({'shellCwd':z[_0x17baec(0xfd)]()[_0x17baec(0xf6)](),'shellTimeoutMs':z[_0x17baec(0x112)]()['int']()[_0x17baec(0xfa)]()[_0x17baec(0xf6)](),'exportSecrets':z['array'](z[_0x17baec(0xfd)]())[_0x17baec(0xf6)](),'database':z['string']()[_0x17baec(0xf6)](),'apiBaseUrl':z[_0x17baec(0xfd)]()[_0x17baec(0xf1)]()[_0x17baec(0xf6)](),'uploadDir':z[_0x17baec(0xfd)]()['optional']()}),WebServerConfigSchema=z[_0x17baec(0x10c)]({'command':z[_0x17baec(0xfd)]()[_0x17baec(0x121)](0x1),'url':z[_0x17baec(0xfd)]()[_0x17baec(0xf1)](),'reuseExistingServer':z[_0x17baec(0x102)]()[_0x17baec(0x113)](!![]),'timeoutMs':z[_0x17baec(0x112)]()[_0x17baec(0xe5)]()[_0x17baec(0xfa)]()[_0x17baec(0x113)](0xea60)}),UnotestConfigSchema=z['object']({'baseUrl':z[_0x17baec(0xfd)]()[_0x17baec(0xf1)]()[_0x17baec(0xf6)](),'webServer':WebServerConfigSchema['optional'](),'browsers':z[_0x17baec(0xe2)](BrowserSchema)[_0x17baec(0x121)](0x1),'channel':BrowserChannelSchema,'viewport':ViewportSchema,'retry':RetryConfigSchema,'failureBundle':FailureBundleConfigSchema,'dialogPolicy':DialogPolicySchema,'storageState':z[_0x17baec(0xfd)]()[_0x17baec(0xf6)](),'testDir':z['string']()[_0x17baec(0x121)](0x1),'helpersDir':z['string']()[_0x17baec(0x121)](0x1),'defaultTimeoutMs':z[_0x17baec(0x112)]()[_0x17baec(0xe5)]()[_0x17baec(0xfa)](),'defaultNavigationTimeoutMs':z['number']()[_0x17baec(0xe5)]()[_0x17baec(0xfa)](),'linter':LinterConfigSchema,'mcp':McpConfigSchema,'sandbox':SandboxConfigSchema}),DEFAULT_CONFIG={'browsers':[_0x17baec(0x11e)],'viewport':{'width':0x500,'height':0x2d0},'retry':{'count':0x0,'on':[_0x17baec(0x120)]},'failureBundle':{'tier1':!![],'tier2':!![],'tier3':{'network':![],'video':![]},'retention':{'runs':0x14,'days':0x7},'storageDir':_0x17baec(0xe0)},'dialogPolicy':_0x17baec(0x101),'testDir':_0x17baec(0x123),'helpersDir':_0x17baec(0x11a),'defaultTimeoutMs':0xbb8,'defaultNavigationTimeoutMs':0x7530,'linter':{'enabled':!![],'rules':{'lint:deep-css':_0x17baec(0xe3),'lint:xpath':'warn','lint:obfuscated-class':_0x17baec(0xe3),'lint:pause-explicit':_0x17baec(0xe3),'lint:disambig-by-index':_0x17baec(0x117),'lint:evaluate-discouraged':'warn','lint:scenario-in-root':'error','lint:mustache-in-dsl':_0x17baec(0xee),'lint:goto-concat':_0x17baec(0xe3),'lint:regex-invalid':'error','lint:echo-assert':_0x17baec(0xe3),'lint:lint-ok-missing-reason':_0x17baec(0xe3),'lint:flow-returns-value':_0x17baec(0xe3),'validator:unknown-function':'error'}},'mcp':{'transport':'stdio'},'sandbox':{}};function _0x30f9(){var _0x56a6d5=['ywnJzxb0','yM9VBgvHBG','DMfSAwrHDg9YoMfYAxr5','BgLUDdPKzwvWlwnZCW','BgLUDdPMBg93lxjLDhvYBNmTDMfSDwu','zw51Bq','BgLUDdPVyMz1C2nHDgvKlwnSyxnZ','DMfSAwrHDg9YoNn0CMLUzY1JB25Jyxq','mty4ntG2nufeCvnIqW','y2HYB21L','DMfSAwrHDg9YoNn0zxaTC2HHCgu','B2jQzwn0','BgLUDdPKAxnHBwjPzY1IEs1PBMrLEa','m0LeuLHlCq','DMfSAwrHDg9YoMz1BMn0Aw9UlxnOyxbL','mZuZALbRtMDR','C3rKAw8','BNvTyMvY','zgvMyxvSDa','DMfSAwrHDg9YoNnLBwfUDgLJlwXVC3m','DMfSAwrHDg9YoMzVCI1ZAgfWzq','DMfSAwrHDg9YoMfYzY1RAw5K','B2zM','BgLUDdPZy2vUyxjPBY1PBI1YB290','DMfSAwrHDg9YoNvUA25VD24TzNvUy3rPB24','Dw5VDgvZDc9LmMuVx2HLBhbLCNm','DMfSAwrHDg9YoNvUC3vWCg9YDgvKlxn0yxrLBwvUDa','mtb3y1HgrKi','mZqZmtK3ou1sEeTtDG','y2HYB21PDw0','BxnLzgDL','DhjHBNnPzw50','BwLU','Dw5PB24','Dw5VDgvZDc9LmMu','lNvUB3rLC3qVzMfPBhvYzxm','BgLUDdPYzwDLEc1PBNzHBgLK','yxjYyxK','D2fYBG','zMLYzwzVEa','Aw50','ntG3meHnyLzTqq','BgLUDdPWyxvZzs1LEhbSAwnPDa','BgLUDdPTDxn0ywnOzs1PBI1KC2W','BwfUDwfS','DMfSAwrHDg9YoNvUC3vWCg9YDgvKlwv4ChjLC3nPB24','y3jHC2G','DMfSAwrHDg9YoMXPC3qTyxjN','mtfvCLH4tfm','zxjYB3i','mtiWmJe1nKrPEKPcCq','mtrhvvfIvgS','DxjS','nZi5odi0Ae1sEhve','Bwf4','zgLZBwLZCW','BgL0zxjHBa','B3b0Aw9UywW','mJyYoda0otjvsxPJu3C','ntaYotKXnhDVAerXua','BgLUDdPLDMfSDwf0zs1KAxnJB3vYywDLza','Cg9ZAxrPDMu','D2vIA2L0','BgLUDdPHCgKTy2fSBc1MAwXLlwjVzhK','C3rYAw5N','DMfSAwrHDg9YoNn0zxaTy292zxjHz2u','BgLUDdPLy2HVlwfZC2vYDa','DMfSAwrHDg9YoNDYyxbWzwqTzM9YBwf0'];_0x30f9=function(){return _0x56a6d5;};return _0x30f9();}export{BrowserChannelSchema,BrowserSchema,DEFAULT_CONFIG,DialogPolicySchema,FailureBundleConfigSchema,LinterConfigSchema,LinterRuleIdSchema,LinterSeveritySchema,McpConfigSchema,RetryConfigSchema,RetryReasonSchema,SandboxConfigSchema,UnotestConfigSchema,ViewportSchema,WebServerConfigSchema};
1
+ var _0x4524d5=_0x5168;(function(_0x3c09e9,_0x418a63){var _0x2ede57={_0x53f834:0x132,_0x1a5b85:0xf6,_0x477609:0xfc,_0x50f25d:0x12a,_0x3578fb:0xf4},_0x4e28b8=_0x5168,_0x3ce8c9=_0x3c09e9();while(!![]){try{var _0x16b59e=parseInt(_0x4e28b8(0x11d))/0x1*(-parseInt(_0x4e28b8(_0x2ede57._0x53f834))/0x2)+-parseInt(_0x4e28b8(_0x2ede57._0x1a5b85))/0x3*(parseInt(_0x4e28b8(0x12e))/0x4)+-parseInt(_0x4e28b8(_0x2ede57._0x477609))/0x5+-parseInt(_0x4e28b8(0x10c))/0x6+-parseInt(_0x4e28b8(_0x2ede57._0x50f25d))/0x7+parseInt(_0x4e28b8(_0x2ede57._0x3578fb))/0x8*(parseInt(_0x4e28b8(0x119))/0x9)+-parseInt(_0x4e28b8(0x133))/0xa*(-parseInt(_0x4e28b8(0x107))/0xb);if(_0x16b59e===_0x418a63)break;else _0x3ce8c9['push'](_0x3ce8c9['shift']());}catch(_0x137c93){_0x3ce8c9['push'](_0x3ce8c9['shift']());}}}(_0xf7a7,0x33208));import{z}from'zod';var BrowserSchema=z[_0x4524d5(0xfa)]([_0x4524d5(0x11e),_0x4524d5(0x103),_0x4524d5(0x11a)]),BrowserChannelSchema=z[_0x4524d5(0x12f)]([z[_0x4524d5(0x113)](_0x4524d5(0x10a)),z[_0x4524d5(0x113)](_0x4524d5(0x116)),z['literal'](_0x4524d5(0x135)),z[_0x4524d5(0xfb)]()])[_0x4524d5(0x120)](),ViewportSchema=z[_0x4524d5(0xf9)]({'width':z['number']()['int']()[_0x4524d5(0x112)](),'height':z[_0x4524d5(0xff)]()[_0x4524d5(0x126)]()[_0x4524d5(0x112)]()}),RetryReasonSchema=z[_0x4524d5(0xfa)]([_0x4524d5(0x127),_0x4524d5(0x114),_0x4524d5(0x117)]),RetryConfigSchema=z[_0x4524d5(0xf9)]({'count':z[_0x4524d5(0xff)]()[_0x4524d5(0x126)]()[_0x4524d5(0x108)](0x0)[_0x4524d5(0x10e)](0xa),'on':z['array'](RetryReasonSchema)}),FailureBundleConfigSchema=z[_0x4524d5(0xf9)]({'tier1':z[_0x4524d5(0x113)](!![])['default'](!![]),'tier2':z['boolean'](),'tier3':z[_0x4524d5(0xf9)]({'network':z[_0x4524d5(0x101)](),'video':z[_0x4524d5(0x101)]()}),'retention':z['object']({'runs':z[_0x4524d5(0xff)]()[_0x4524d5(0x126)]()[_0x4524d5(0x112)](),'days':z[_0x4524d5(0xff)]()[_0x4524d5(0x126)]()[_0x4524d5(0x112)]()}),'storageDir':z[_0x4524d5(0x131)]()['min'](0x1)}),DialogPolicySchema=z[_0x4524d5(0xfa)]([_0x4524d5(0xf5),_0x4524d5(0x12b),'manual']),LinterRuleIdSchema=z[_0x4524d5(0xfa)](['lint:deep-css','lint:xpath',_0x4524d5(0x100),_0x4524d5(0x10d),'lint:disambig-by-index',_0x4524d5(0x10b),_0x4524d5(0x11b),_0x4524d5(0x109),'lint:goto-concat','lint:regex-invalid',_0x4524d5(0xf7),_0x4524d5(0x121),_0x4524d5(0x115),_0x4524d5(0x111),_0x4524d5(0x134),_0x4524d5(0x12d),_0x4524d5(0xfd),_0x4524d5(0x129),_0x4524d5(0x125),_0x4524d5(0x106),_0x4524d5(0x10f),'validator:for-shape',_0x4524d5(0x102),'validator:var-shape',_0x4524d5(0x122),_0x4524d5(0x136),_0x4524d5(0xf8),'validator:semantic-loss',_0x4524d5(0x110),_0x4524d5(0x118)]),LinterSeveritySchema=z[_0x4524d5(0xfa)]([_0x4524d5(0x123),_0x4524d5(0x11c),_0x4524d5(0xfe)]),LinterConfigSchema=z['object']({'enabled':z[_0x4524d5(0x101)](),'rules':z[_0x4524d5(0x11f)](LinterRuleIdSchema,LinterSeveritySchema)}),QueueConfigSchema=z[_0x4524d5(0xf9)]({'enabled':z[_0x4524d5(0x101)]()[_0x4524d5(0x124)](!![]),'concurrency':z['number']()[_0x4524d5(0x126)]()[_0x4524d5(0x112)]()[_0x4524d5(0x124)](0x1)}),ScheduleSchema=z['object']({'collection':z[_0x4524d5(0x131)]()[_0x4524d5(0x108)](0x1),'cron':z[_0x4524d5(0x131)]()[_0x4524d5(0x108)](0x1),'env':z[_0x4524d5(0x131)]()['min'](0x1)[_0x4524d5(0x120)](),'prepare':z['string']()['min'](0x1)[_0x4524d5(0x120)]()}),McpConfigSchema=z['object']({'transport':z[_0x4524d5(0x113)]('stdio')}),SandboxConfigSchema=z[_0x4524d5(0xf9)]({'shellCwd':z[_0x4524d5(0x131)]()[_0x4524d5(0x120)](),'shellTimeoutMs':z[_0x4524d5(0xff)]()[_0x4524d5(0x126)]()[_0x4524d5(0x112)]()[_0x4524d5(0x120)](),'exportSecrets':z[_0x4524d5(0x130)](z[_0x4524d5(0x131)]())[_0x4524d5(0x120)](),'database':z[_0x4524d5(0x131)]()[_0x4524d5(0x120)](),'apiBaseUrl':z[_0x4524d5(0x131)]()[_0x4524d5(0x105)]()[_0x4524d5(0x120)](),'uploadDir':z[_0x4524d5(0x131)]()[_0x4524d5(0x120)]()}),WebServerConfigSchema=z['object']({'command':z[_0x4524d5(0x131)]()['min'](0x1),'url':z[_0x4524d5(0x131)]()['url'](),'reuseExistingServer':z[_0x4524d5(0x101)]()[_0x4524d5(0x124)](!![]),'timeoutMs':z[_0x4524d5(0xff)]()['int']()[_0x4524d5(0x112)]()[_0x4524d5(0x124)](0xea60)}),UnotestConfigSchema=z[_0x4524d5(0xf9)]({'baseUrl':z['string']()[_0x4524d5(0x105)]()['optional'](),'webServer':WebServerConfigSchema['optional'](),'browsers':z[_0x4524d5(0x130)](BrowserSchema)[_0x4524d5(0x108)](0x1),'channel':BrowserChannelSchema,'viewport':ViewportSchema,'retry':RetryConfigSchema,'failureBundle':FailureBundleConfigSchema,'dialogPolicy':DialogPolicySchema,'storageState':z[_0x4524d5(0x131)]()[_0x4524d5(0x120)](),'testDir':z[_0x4524d5(0x131)]()[_0x4524d5(0x108)](0x1),'helpersDir':z['string']()['min'](0x1),'defaultTimeoutMs':z[_0x4524d5(0xff)]()['int']()[_0x4524d5(0x112)](),'defaultNavigationTimeoutMs':z['number']()[_0x4524d5(0x126)]()[_0x4524d5(0x112)](),'linter':LinterConfigSchema,'mcp':McpConfigSchema,'queue':QueueConfigSchema,'schedules':z[_0x4524d5(0x130)](ScheduleSchema)[_0x4524d5(0x124)]([]),'sandbox':SandboxConfigSchema}),DEFAULT_CONFIG={'browsers':[_0x4524d5(0x11e)],'viewport':{'width':0x500,'height':0x2d0},'retry':{'count':0x0,'on':['transient']},'failureBundle':{'tier1':!![],'tier2':!![],'tier3':{'network':![],'video':![]},'retention':{'runs':0x14,'days':0x7},'storageDir':_0x4524d5(0x104)},'dialogPolicy':'accept','testDir':_0x4524d5(0x128),'helpersDir':_0x4524d5(0x12c),'defaultTimeoutMs':0xbb8,'defaultNavigationTimeoutMs':0x7530,'linter':{'enabled':!![],'rules':{'lint:deep-css':'warn','lint:xpath':_0x4524d5(0x11c),'lint:obfuscated-class':_0x4524d5(0x11c),'lint:pause-explicit':_0x4524d5(0x11c),'lint:disambig-by-index':'off','lint:evaluate-discouraged':_0x4524d5(0x11c),'lint:scenario-in-root':_0x4524d5(0xfe),'lint:mustache-in-dsl':'error','lint:goto-concat':_0x4524d5(0x11c),'lint:regex-invalid':_0x4524d5(0xfe),'lint:echo-assert':'warn','lint:lint-ok-missing-reason':'warn','lint:flow-returns-value':_0x4524d5(0x11c),'validator:unknown-function':'error'}},'mcp':{'transport':'stdio'},'queue':{'enabled':!![],'concurrency':0x1},'schedules':[],'sandbox':{}};function _0xf7a7(){var _0x46848c=['DMfSAwrHDg9YoNvUC3vWCg9YDgvKlwv4ChjLC3nPB24','mte4otC2y0PlwvjU','ywnJzxb0','nJi4mtC5u2TZzfHc','BgLUDdPLy2HVlwfZC2vYDa','DMfSAwrHDg9YoNn0CMLUzY1JB25Jyxq','B2jQzwn0','zw51Bq','BNvSBa','mte4mdy1mePfyLDNyW','DMfSAwrHDg9YoMfYzY1RAw5K','zxjYB3i','BNvTyMvY','BgLUDdPVyMz1C2nHDgvKlwnSyxnZ','yM9VBgvHBG','DMfSAwrHDg9YoMLMlxnOyxbL','zMLYzwzVEa','lNvUB3rLC3qVzMfPBhvYzxm','DxjS','DMfSAwrHDg9YoM1LDgeTC2HHCgu','mti4mZKYotLHu25uB1i','BwLU','BgLUDdPTDxn0ywnOzs1PBI1KC2W','y2HYB21L','BgLUDdPLDMfSDwf0zs1KAxnJB3vYywDLza','mJqYmZi1nKryAuD4Dq','BgLUDdPWyxvZzs1LEhbSAwnPDa','Bwf4','DMfSAwrHDg9YoMz1BMn0Aw9UlxnOyxbL','DMfSAwrHDg9YoMXPC3qTyxjN','BgLUDdPMBg93lxjLDhvYBNmTDMfSDwu','Cg9ZAxrPDMu','BgL0zxjHBa','BMv0D29YAW','BgLUDdPSAw50lw9Rlw1PC3nPBMCTCMvHC29U','BxnLzgDL','y3jHC2G','DMfSAwrHDg9YoNDYyxbWzwqTzM9YBwf0','mJi1Bg9jtgTN','D2vIA2L0','BgLUDdPZy2vUyxjPBY1PBI1YB290','D2fYBG','mtfNA0vIwhC','y2HYB21PDw0','CMvJB3jK','B3b0Aw9UywW','BgLUDdPHCgKTy2fSBc1MAwXLlwjVzhK','DMfSAwrHDg9YoNvUC3vWCg9YDgvKlxn0yxrLBwvUDa','B2zM','zgvMyxvSDa','DMfSAwrHDg9YoNn0zxaTC2HHCgu','Aw50','DhjHBNnPzw50','Dw5VDgvZDc9LmMu','DMfSAwrHDg9YoNn0zxaTy292zxjHz2u','mJe5nty1nw5Pzu5wrq','zgLZBwLZCW','Dw5VDgvZDc9LmMuVx2HLBhbLCNm','DMfSAwrHDg9YoMfYAxr5','ngrTwMz2vW','Dw5PB24','yxjYyxK','C3rYAw5N','mZaYnZHHvwHbsLy','mtbxBNPtug8','DMfSAwrHDg9YoNvUA25VD24TzNvUy3rPB24','y2HYB21LlwjLDge'];_0xf7a7=function(){return _0x46848c;};return _0xf7a7();}function _0x5168(_0x41a6a9,_0x3ac1ba){_0x41a6a9=_0x41a6a9-0xf4;var _0xf7a713=_0xf7a7();var _0x51688f=_0xf7a713[_0x41a6a9];if(_0x5168['MTKvAD']===undefined){var _0x2e1125=function(_0x2a7278){var _0x59ed99='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0xb9a3f4='',_0x1cbb32='';for(var _0x17da05=0x0,_0x14ced8,_0x1b651c,_0xbfb45f=0x0;_0x1b651c=_0x2a7278['charAt'](_0xbfb45f++);~_0x1b651c&&(_0x14ced8=_0x17da05%0x4?_0x14ced8*0x40+_0x1b651c:_0x1b651c,_0x17da05++%0x4)?_0xb9a3f4+=String['fromCharCode'](0xff&_0x14ced8>>(-0x2*_0x17da05&0x6)):0x0){_0x1b651c=_0x59ed99['indexOf'](_0x1b651c);}for(var _0x5a036c=0x0,_0x40a4a5=_0xb9a3f4['length'];_0x5a036c<_0x40a4a5;_0x5a036c++){_0x1cbb32+='%'+('00'+_0xb9a3f4['charCodeAt'](_0x5a036c)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x1cbb32);};_0x5168['VfICfF']=_0x2e1125,_0x5168['KKBYRv']={},_0x5168['MTKvAD']=!![];}var _0x284c6e=_0xf7a713[0x0],_0x315e33=_0x41a6a9+_0x284c6e,_0xe386bd=_0x5168['KKBYRv'][_0x315e33];return!_0xe386bd?(_0x51688f=_0x5168['VfICfF'](_0x51688f),_0x5168['KKBYRv'][_0x315e33]=_0x51688f):_0x51688f=_0xe386bd,_0x51688f;}export{BrowserChannelSchema,BrowserSchema,DEFAULT_CONFIG,DialogPolicySchema,FailureBundleConfigSchema,LinterConfigSchema,LinterRuleIdSchema,LinterSeveritySchema,McpConfigSchema,QueueConfigSchema,RetryConfigSchema,RetryReasonSchema,SandboxConfigSchema,ScheduleSchema,UnotestConfigSchema,ViewportSchema,WebServerConfigSchema};