@unotest/web 0.25.0 → 0.26.1

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,335 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.26.1] - 2026-08-30
4
+
5
+ ### Patch Changes
6
+
7
+ - A push now says which environments could not run the suite
8
+
9
+ The values a suite's externals need live with the environment on a box —
10
+ no bundle carries them, deliberately — so a suite pushed to a box that
11
+ has none of them was accepted happily and went red an hour later,
12
+ complaining about a missing login. The bundle now carries the NAMES its
13
+ `.env` / `.secrets` (or their `.example` twins) declare — names only,
14
+ never values — and the box answers the push with what it cannot supply,
15
+ per environment, plus the command that sets each one.
16
+
17
+ - One command cuts the whole release
18
+
19
+ `publish:ecosystem` now also commits the docs pin and cuts the box
20
+ release (the `guard-v<version>` tag) when something a box runs changed
21
+ since the last one — `--skip-box` refuses, `--box` forces. Two artifacts
22
+ from one commit used to be two commands, and the second one is the one
23
+ people forget: a fleet on yesterday's daemon while npm has today's.
24
+ What is left for a human is one `git push --follow-tags`.
25
+
26
+ - A collection prepares its own environment: `prepare:` in
27
+ `unotest/e2e/_collections/<name>.yaml`
28
+
29
+ `prepare` used to live only in a `schedules` entry, so it ran on a
30
+ scheduled tick and nowhere else. A suite that needs a seeded database was
31
+ therefore red for every other way of starting it — a CI check of a
32
+ deployment, the Run button in a box's viewer — with a message about a
33
+ missing table rather than about an unprepared environment. It belongs to
34
+ the suite, so it now lives with the suite: the collection's own `prepare:`
35
+ runs however the collection was started, inside the same queue slot, and a
36
+ failure still exits 94 rather than pretending the tests ran. A `schedules`
37
+ entry may still name its own (it wins for that tick), and `--no-prepare`
38
+ skips it for a run against an environment somebody else prepared.
39
+
40
+ - Fix: the mirror shipped the monorepo's `.secrets` verbatim
41
+
42
+ `publish-examples` copied `unotest/.secrets` into the public examples
43
+ repository as `.secrets.example`. That was harmless while the file held
44
+ only the playground's public demo login, and stopped being harmless the
45
+ moment a real credential was added to it. Secret KEYS are now spelled out
46
+ in the generator (values only where they are genuinely public), and a
47
+ guard refuses to publish when the two lists disagree — the same discipline
48
+ `.env` has had.
49
+
50
+ - Fix: `bundle push --run --env <name>` refused to run
51
+
52
+ `--env` was consumed globally, where it selects a local `.env.<name>`
53
+ overlay, and never reached `bundle push` — whose `--env` names an
54
+ environment ON A BOX. The command every CI job and the box manual use
55
+ ("--run needs --env") could not work as documented. `bundle` now keeps its
56
+ own flag; every other command is unchanged.
57
+
58
+ - Fix: a viewer started for an environment listed no runs at all
59
+
60
+ A box starts one viewer per environment and names it through
61
+ `UNOTEST_ENV`; the viewer's launcher ignored that variable and came up on
62
+ the base runs directory, while runs were filed under `.runs.<environment>`.
63
+ Effect on a box: an empty run history right after a suite finished.
64
+
65
+ - 9424f6b: Fix: screenshots stayed blank in the viewer when runs write to a separate
66
+ artifact root
67
+
68
+ A run whose artifacts do not live under the working directory — every run on
69
+ a box, where the sources are a disposable copy of a test bundle — emitted
70
+ `screenshot` events whose path was relativised against the working directory.
71
+ That came out as `../../../…`, which the viewer's asset route refuses to
72
+ serve, so every step preview rendered as a broken image. Artifact paths are
73
+ now relative to the artifact root, which is still the project root on every
74
+ local run.
75
+
76
+ The failure bundle had the mirror-image problem: it was written to
77
+ `.unotest/failures/` under the working directory, so on a box it landed
78
+ inside the bundle copy — outside the tree the viewer serves, and discarded on
79
+ the next bundle push. It now hangs off the artifact root too, which leaves
80
+ local runs byte-for-byte unchanged.
81
+
82
+ The inspector's Semantic DOM, Console, Network and Snapshot panes were dark
83
+ for a third reason, and this one bit local runs as well: they composed the
84
+ run's directory in the browser and left out the date shard runs have been
85
+ filed under since M-10, plus the environment suffix on a box. The viewer's
86
+ asset route now takes `?file=<name-relative-to-the-run-dir>` and resolves the
87
+ directory itself — runs root, target suffix, environment suffix and shard are
88
+ all things only the server knows. `?path=` stays for the whole paths the
89
+ server ships on run events.
90
+
91
+ - d380850: `bundle push` strips the project's own install hooks (`postinstall`,
92
+ `prepare`, …) from the packed `package.json`. A box installs the suite's
93
+ dependencies; the project's hooks are not its business, and they point at
94
+ files a bundle deliberately does not carry (build scripts, Makefiles), so
95
+ `npm ci` on the box died with MODULE_NOT_FOUND. Dependency scripts — native
96
+ module builds — are untouched, and the push reports what it removed on a
97
+ `note:` line.
98
+ - Updated dependencies [9424f6b]
99
+ - @unotest/protocol@0.26.1
100
+ - @unotest/viewer@0.26.1
101
+ - @unotest/core@0.26.1
102
+ - @unotest/dsl@0.26.1
103
+ - @unotest/grounder-client@0.26.1
104
+
105
+ ## [0.26.0] - 2026-08-30
106
+
107
+ ### Minor Changes
108
+
109
+ - 3eede7c: The viewer can now be hosted: it shows which test bundle an environment
110
+ is running, and lets you switch it.
111
+
112
+ This is the other half of `bundle push`. Nothing changes for a local
113
+ viewer — everything here appears only when something is hosting it.
114
+
115
+ - **The artifact root and the project root are now genuinely separate.**
116
+ The viewer already took `UNOTEST_ARTIFACTS_ROOT` for the run queue;
117
+ run discovery, the run index, snapshots, log capture and the viewer's
118
+ own lock file now honour it too. Locally the two are the same directory
119
+ and nothing moves. Where they differ — sources in a disposable copy of a
120
+ bundle, history in the environment beside it — the history is no longer
121
+ read out of (or written into) the sources.
122
+ - **Bundle badge in the status bar.** `main@1a2b3c4`, or `wip` for a
123
+ bundle packed from an uncommitted tree, with the author, the pinned
124
+ `@unotest/web` and who switched the environment last. Clicking it lists
125
+ the bundles pushed to this environment and switches to one — that runs
126
+ as a maintenance ticket on the host, so it waits for the environment to
127
+ be free instead of pulling the code out from under a running suite.
128
+ - **Runs remember their bundle.** `manifest.json` and `GET /api/runs`
129
+ carry `bundleId` (from `UNOTEST_BUNDLE_ID`, set by the host); the runs
130
+ list marks the ones that ran on another bundle, so "was this red run
131
+ even the code I am looking at?" has an answer.
132
+ - **The contract is a file, not a mode.** `@unotest/protocol` gains
133
+ `WorkspaceFile` — the host writes `unotest/.workspace.json` beside the
134
+ artifacts and the viewer reads it, the same way the guard's session
135
+ header narrows the UI. No file, no bundle UI; the viewer never learns
136
+ what a box is. Values of environment variables and secrets never appear
137
+ in it — only their names.
138
+ - **Queue tickets can name a bundle.** `QueueTicket.source.bundleId` is
139
+ recorded when a run is queued, so a run executes the code it was
140
+ ordered with even if the environment moved on while it waited.
141
+
142
+ - 6d1c612: `npx @unotest/web bundle push` — send your suite to a box, instead of
143
+ giving a box access to your repository.
144
+
145
+ ```sh
146
+ npx @unotest/web bundle push --box https://tests.example.com
147
+ ```
148
+
149
+ It packs `unotest/`, `unotest.config`, `package.json` and its lockfile
150
+ into one archive and uploads it. That is the whole channel: a box holds
151
+ no deploy key and no token for anybody's code and never pulls anything,
152
+ so nothing is on it that was not pushed to it. (There is no path-scoped
153
+ git credential anywhere — a key that can fetch `unotest/` can fetch the
154
+ product source next to it, which is why this direction is the only one
155
+ we offer.)
156
+
157
+ - **Uncommitted work travels.** The manifest records `dirty: true` and
158
+ the bundle shows up as _wip_, so pushing a fix you have not committed
159
+ yet is a normal loop, not a release step.
160
+ - **What would fail on the box is refused here.** A scenario that reads a
161
+ file outside `unotest/` (`MODULE_NOT_FOUND` on the box, hours later, on
162
+ a run nobody is watching), a missing lockfile (`npm ci` will not run
163
+ without one), no `@unotest/web` in the dependencies, a symlink. Each
164
+ problem names the file, the line and the fix.
165
+ - **Secrets do not travel.** `unotest/.env*` and `.secrets*` are excluded
166
+ even if committed: environment values belong to the environment and are
167
+ injected over the bundle when it runs.
168
+ - **In a git project, git decides what belongs.** The file list comes from
169
+ `git ls-files --cached --others --exclude-standard`, so whatever your
170
+ `.gitignore` keeps out — generated databases, recordings, `.runs` —
171
+ stays out. Content is read from the working tree, not the index.
172
+ - **The id is the content.** `bundleId` is a hash of the packed files, not
173
+ a commit sha (a dirty tree's sha is not unique), so re-pushing an
174
+ unchanged suite is an instant no-op and two people packing the same tree
175
+ get the same bundle.
176
+ - `--dry-run` packs and checks without uploading, `--out <file>` keeps the
177
+ archive (plain `tar.gz` — `tar -tzf` shows what is inside), `--json`
178
+ prints `{ bundleId, status }` for a CI job that runs it afterwards.
179
+ - `UNOTEST_BOX_URL` / `UNOTEST_BOX_TOKEN` supply the address and the
180
+ token, which a CI job usually takes from its secret store. Tokens are
181
+ issued per project on the box.
182
+
183
+ - dbfa36f: Your CI can now ask a box to run the suite it just pushed, and a box can
184
+ report the result back to GitHub.
185
+
186
+ ```sh
187
+ npx @unotest/web bundle push --run --env test --collection smoke
188
+ ```
189
+
190
+ `--run` sends the second half of the CI recipe: upload the bundle, then
191
+ order a run of it. Two requests rather than one flag on the upload,
192
+ because a bundle is _content_ — pushing an identical tree twice is normal
193
+ and answers `exists` — while "run it" is an event. The same split is what
194
+ lets a deploy script run a bundle somebody pushed hours ago.
195
+
196
+ - **The box answers as soon as the runs are queued** (202) with their run
197
+ ids. A suite takes minutes to hours; an HTTP request held open for that
198
+ long is a timeout in somebody's proxy, not a result. The ids are real
199
+ from the moment you get them, so a CI log can print a link into the
200
+ viewer straight away.
201
+ - **`--env <name>` is required with `--run`.** Which environments a box
202
+ has is the operator's decision, and guessing one would be guessing
203
+ which system gets tested.
204
+ - **Without `--collection`, the box runs what that environment normally
205
+ runs** — the collections its `schedules` name. Naming them explicitly is
206
+ the deploy-check case; leaving it out is the "same as every night" case.
207
+ - **`--pr <number>` coalesces.** A newer push of the same pull request
208
+ withdraws its older runs that are still _waiting_; a run that already
209
+ started is never killed. Three pushes in five minutes cost one suite.
210
+ `--json` reports how many were withdrawn, so "my run vanished" has an
211
+ answer in the log of the run that replaced it.
212
+ - **Refusals are typed and say what to do**: no such environment, an
213
+ environment belonging to another project, a bundle the box no longer
214
+ holds, nothing to run.
215
+
216
+ On a hosted viewer, the queue panel now names the change each machine
217
+ ticket belongs to (`PR #412@1a2b3c4`) instead of a row of identical "ci"
218
+ entries, and the history gained a **this bundle only** filter for the
219
+ moment you are judging the bundle in front of you rather than the last
220
+ month of runs.
221
+
222
+ Nothing changes for a local `npx @unotest/web viewer`: there are no
223
+ bundles there, so neither the badge nor the filter appears.
224
+
225
+ - c43aa00: Runs of one project now take turns instead of colliding.
226
+
227
+ Until now nothing coordinated them: the viewer refused a second run with
228
+ HTTP 409, and that was the whole defence — a `npx @unotest/web e2e` in a
229
+ terminal, an agent's `run_test` and a scheduled suite would all start on
230
+ top of each other, driving the same browser and the same seeded database
231
+ at the same time. The 409 protected the UI, not the machine.
232
+
233
+ - **A filesystem queue**, in `unotest/.queue<target>[.<env>]/` beside the
234
+ runs root it belongs to. A run writes a ticket, waits until it is at
235
+ the head, takes a slot, runs, and gives the slot back. There is no
236
+ daemon: whoever holds the slot executes the run itself, so nothing new
237
+ has to be installed, started or kept alive. Only `open("wx")`, rename,
238
+ unlink and mtime — no `flock`, which lies on network and container
239
+ filesystems. A crashed producer is reaped by whoever comes next.
240
+ - **Every producer is in it**: the CLI (`e2e`, `collection`, `author`),
241
+ the viewer's Run button, and the MCP `run_test` (which spawns the CLI).
242
+ A collection is ONE run — its `workers` still run in parallel inside
243
+ it, because a child process carries its parent's lease and skips the
244
+ queue.
245
+ - **The viewer shows the queue**: the Active panel lists what is waiting,
246
+ including tickets a terminal or an agent wrote, with a button to take
247
+ any of them back out. `GET /api/queue`, `DELETE /api/queue/:ticket`,
248
+ and a `queue:changed` websocket message.
249
+ - **Config**: `queue.concurrency` (default 1) and `queue.enabled`
250
+ (default true) in `unotest.config`. Both the CLI and the viewer read
251
+ the same file, so they cannot disagree about how many slots exist.
252
+ - **Escape hatches**: `UNOTEST_NO_QUEUE=1` runs without queueing;
253
+ Ctrl-C while waiting gives up your place in line and runs nothing.
254
+ - **For boxes**: `UNOTEST_QUEUE_GLOBAL_DIR` + `UNOTEST_QUEUE_GLOBAL_SLOTS`
255
+ add a host-wide budget several environments share (a collection weighs
256
+ its worker count), and `UNOTEST_ARTIFACTS_ROOT` puts `.runs` / `.queue`
257
+ somewhere other than the directory the tests live in — so the history
258
+ outlives a throwaway copy of the tests.
259
+
260
+ ### Migration notes
261
+
262
+ **`POST /api/run` no longer answers 409.** Ordering a run while another
263
+ one is active used to fail with `ActiveRunConflictError` (HTTP 409); it
264
+ now succeeds and the run waits. The response changed shape with it:
265
+
266
+ ```diff
267
+ - { runId, kind, ref, pid, startedAt }
268
+ + { runId, kind, ref, status: "queued" | "running", ticket, acceptedAt }
269
+ ```
270
+
271
+ `runId` is still final and immediately usable — open the tab on it as
272
+ before — but the run may not have started yet, and `pid` is not known at
273
+ that point. `POST /api/runs/:runId/abort` covers both states: it
274
+ withdraws a waiting ticket or SIGTERMs a running child.
275
+
276
+ Anything that treated 409 as "busy, try later" should now simply order
277
+ the run. Anything that relied on "only one run can exist" should read
278
+ `GET /api/queue`. To keep the old immediate-start behaviour (without the
279
+ protection), set `queue.enabled: false`.
280
+
281
+ - 8ec4177: Scheduled runs: declare what should run on its own, next to the tests it
282
+ runs.
283
+
284
+ ```js
285
+ // unotest.config.mjs
286
+ export default {
287
+ schedules: [
288
+ { collection: "smoke", cron: "0 * * * *" },
289
+ { collection: "nightly", cron: "0 3 * * *", prepare: "node seed.mjs" },
290
+ ],
291
+ };
292
+ ```
293
+
294
+ - **`schedules`** — a new config leaf: collection + cron, plus an
295
+ optional `env` (which environment the run happens in) and `prepare` (a
296
+ command run before the suite). Versioned with the tests, so "what is
297
+ supposed to run nightly" is in the repo instead of on a machine nobody
298
+ can see.
299
+ - **`npx @unotest/web schedules`** prints what is declared; `--json` is
300
+ the machine shape a scheduler reads back. Each entry also carries the
301
+ collection's `workers` resolved from its yaml manifest, so an executor
302
+ can weigh the run without parsing yaml itself.
303
+ - **`npx @unotest/web collection <name> --scheduled`** runs a collection
304
+ the way its schedule does: in the entry's `env` (unless you set one)
305
+ and with its `prepare` command first, inside the same run slot
306
+ (preparing touches the target, which is exactly what the run queue
307
+ serialises). A failed prepare exits **94**, not 1 — "the environment
308
+ was not prepared, nothing was tested" is a different event from "the
309
+ suite is red", and alerting has to tell them apart. A collection
310
+ listed in `schedules` more than once needs `--scheduled=<index>`
311
+ (zero-based) — guessing which entry's prepare to run is refused.
312
+
313
+ **Nothing in this package ticks.** `schedules` is data: no cron parser,
314
+ no timers, no background runs. Cron is executed by whatever hosts your
315
+ runs — your CI (`on: schedule` + `unotest-web collection`), or a box. A
316
+ clone of a repo that declares schedules therefore never starts running
317
+ suites by itself; `unotest-web schedules` says so out loud instead of
318
+ implying a timer that does not exist.
319
+
320
+ ### Patch Changes
321
+
322
+ - Updated dependencies [3eede7c]
323
+ - Updated dependencies [6d1c612]
324
+ - Updated dependencies [dbfa36f]
325
+ - Updated dependencies [c43aa00]
326
+ - Updated dependencies [8ec4177]
327
+ - @unotest/viewer@0.26.0
328
+ - @unotest/protocol@0.26.0
329
+ - @unotest/core@0.26.0
330
+ - @unotest/dsl@0.26.0
331
+ - @unotest/grounder-client@0.26.0
332
+
3
333
  ## [0.25.0] - 2026-08-29
4
334
 
5
335
  ### Minor Changes
package/README.md CHANGED
@@ -65,6 +65,76 @@ 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
+ Your project's install hooks (`postinstall`, `prepare`, ...) are removed
115
+ from the packed `package.json`: a box installs the suite's dependencies,
116
+ it does not run project hooks — they reference files that do not travel,
117
+ and env setup they usually do is the box's job anyway. Dependency install
118
+ scripts (native modules) run as normal.
119
+
120
+ What would break on the box is refused here instead — a scenario that
121
+ reads a file outside `unotest/`, a missing lockfile, no `@unotest/web`
122
+ pin. The bundle's id is a hash of its content, so pushing an unchanged
123
+ suite is a no-op.
124
+
125
+ From a CI job, push and run in one command:
126
+
127
+ ```sh
128
+ npx @unotest/web bundle push --run --env test --collection smoke --pr 412
129
+ ```
130
+
131
+ The box answers as soon as the runs are queued and gives you their ids —
132
+ a suite takes minutes, and a request held open that long is a timeout,
133
+ not a result. Watch them in the viewer, or let the box report the checks
134
+ back to GitHub. `--pr` makes a newer push withdraw the older runs of the
135
+ same pull request that are still waiting, so three pushes in five minutes
136
+ cost one suite. Recipe: `guides/manuals/ci-setup.md`.
137
+
68
138
  ## 5. Watch it run — the viewer
69
139
 
70
140
  `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, {
@@ -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;
@@ -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;
@@ -472,9 +571,15 @@ declare const UnotestConfigSchema: z.ZodObject<{
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
+ function _0x20b0(){var _0x5a1d62=['DMfSAwrHDg9YoNnLBwfUDgLJlwXVC3m','BMv0D29YAW','DMfSAwrHDg9YoNvUA25VD24TzNvUy3rPB24','B3b0Aw9UywW','BgLUDdPSAw50lw9Rlw1PC3nPBMCTCMvHC29U','BgLUDdPZy2vUyxjPBY1PBI1YB290','nZiZntG4AgjwuNbA','Dw5PB24','ntCXmZe4nvL6BuvAra','BgLUDdPLy2HVlwfZC2vYDa','BgL0zxjHBa','DhjHBNnPzw50','C3rYAw5N','y2HYB21PDw0','C3rKAw8','DMfSAwrHDg9YoMfYAxr5','DMfSAwrHDg9YoNn0zxaTy292zxjHz2u','BxnLzgDL','yM9VBgvHBG','DMfSAwrHDg9YoNn0zxaTC2HHCgu','DMfSAwrHDg9YoMfYzY1RAw5K','BgLUDdPNB3rVlwnVBMnHDa','B2zM','mtqYmZGYoeHkz0zHva','Cg9ZAxrPDMu','BgLUDdPMBg93lxjLDhvYBNmTDMfSDwu','DMfSAwrHDg9YoNDYyxbWzwqTzM9YBwf0','B2jQzwn0','mtm5mdKYmhvjz2PTEq','zMLYzwzVEa','mte3mZe5owPHA3PutG','lNvUB3rLC3qVzMfPBhvYzxm','DMfSAwrHDg9YoMzVCI1ZAgfWzq','ntK4mdKZofDnzLH1Ba','zgLZBwLZCW','DMfSAwrHDg9YoM1LDgeTC2HHCgu','DMfSAwrHDg9YoNn0CMLUzY1JB25Jyxq','Aw50','DMfSAwrHDg9YoNvUC3vWCg9YDgvKlxn0yxrLBwvUDa','BwLU','DxjS','DMfSAwrHDg9YoMXPC3qTyxjN','BNvSBa','ywnJzxb0','Bwf4','zgvMyxvSDa','yxjYyxK','zxjYB3i','Dw5VDgvZDc9LmMu','ndy1otuYqwzWrfvZ','DMfSAwrHDg9YoNzHCI1ZAgfWzq','Dw5VDgvZDc9LmMuVx2HLBhbLCNm','BgLUDdPWyxvZzs1LEhbSAwnPDa','DMfSAwrHDg9YoMLMlxnOyxbL','zw51Bq','D2fYBG','BgLUDdPKAxnHBwjPzY1IEs1PBMrLEa','BNvTyMvY'];_0x20b0=function(){return _0x5a1d62;};return _0x20b0();}var _0x18d8c4=_0x1b61;(function(_0x483e52,_0x421b37){var _0x55fb8a={_0x4e1e74:0x1d9,_0xf371bf:0x1c3},_0x16126e=_0x1b61,_0x49abc8=_0x483e52();while(!![]){try{var _0x29b3c1=parseInt(_0x16126e(_0x55fb8a._0x4e1e74))/0x1+parseInt(_0x16126e(0x1c1))/0x2+-parseInt(_0x16126e(0x1d7))/0x3+-parseInt(_0x16126e(0x1ec))/0x4+-parseInt(_0x16126e(_0x55fb8a._0xf371bf))/0x5+parseInt(_0x16126e(0x1dc))/0x6+-parseInt(_0x16126e(0x1d2))/0x7;if(_0x29b3c1===_0x421b37)break;else _0x49abc8['push'](_0x49abc8['shift']());}catch(_0x479238){_0x49abc8['push'](_0x49abc8['shift']());}}}(_0x20b0,0x93dcf));function _0x1b61(_0xf86022,_0x44c7f0){_0xf86022=_0xf86022-0x1bd;var _0x20b003=_0x20b0();var _0x1b6183=_0x20b003[_0xf86022];if(_0x1b61['HlmCyi']===undefined){var _0x33860d=function(_0xb3b745){var _0x57e223='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0x3595a1='',_0x5dde2f='';for(var _0x3deded=0x0,_0x1d704f,_0x2b1a71,_0x2b6476=0x0;_0x2b1a71=_0xb3b745['charAt'](_0x2b6476++);~_0x2b1a71&&(_0x1d704f=_0x3deded%0x4?_0x1d704f*0x40+_0x2b1a71:_0x2b1a71,_0x3deded++%0x4)?_0x3595a1+=String['fromCharCode'](0xff&_0x1d704f>>(-0x2*_0x3deded&0x6)):0x0){_0x2b1a71=_0x57e223['indexOf'](_0x2b1a71);}for(var _0x56ece8=0x0,_0x14ee7c=_0x3595a1['length'];_0x56ece8<_0x14ee7c;_0x56ece8++){_0x5dde2f+='%'+('00'+_0x3595a1['charCodeAt'](_0x56ece8)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x5dde2f);};_0x1b61['OHdqFs']=_0x33860d,_0x1b61['fPmlGC']={},_0x1b61['HlmCyi']=!![];}var _0x6d98ec=_0x20b003[0x0],_0x57b7ad=_0xf86022+_0x6d98ec,_0x5c5be6=_0x1b61['fPmlGC'][_0x57b7ad];return!_0x5c5be6?(_0x1b6183=_0x1b61['OHdqFs'](_0x1b6183),_0x1b61['fPmlGC'][_0x57b7ad]=_0x1b6183):_0x1b6183=_0x5c5be6,_0x1b6183;}import{z}from'zod';var BrowserSchema=z[_0x18d8c4(0x1f1)]([_0x18d8c4(0x1c8),_0x18d8c4(0x1d8),'webkit']),BrowserChannelSchema=z[_0x18d8c4(0x1c2)]([z[_0x18d8c4(0x1c5)]('chrome'),z['literal'](_0x18d8c4(0x1cc)),z[_0x18d8c4(0x1c5)]('chrome-beta'),z[_0x18d8c4(0x1e5)]()])['optional'](),ViewportSchema=z[_0x18d8c4(0x1d6)]({'width':z['number']()[_0x18d8c4(0x1e0)]()[_0x18d8c4(0x1d3)](),'height':z[_0x18d8c4(0x1f4)]()[_0x18d8c4(0x1e0)]()[_0x18d8c4(0x1d3)]()}),RetryReasonSchema=z[_0x18d8c4(0x1f1)](['transient',_0x18d8c4(0x1f6),'crash']),RetryConfigSchema=z[_0x18d8c4(0x1d6)]({'count':z[_0x18d8c4(0x1f4)]()[_0x18d8c4(0x1e0)]()[_0x18d8c4(0x1e2)](0x0)[_0x18d8c4(0x1e7)](0xa),'on':z[_0x18d8c4(0x1e9)](RetryReasonSchema)}),FailureBundleConfigSchema=z[_0x18d8c4(0x1d6)]({'tier1':z[_0x18d8c4(0x1c5)](!![])[_0x18d8c4(0x1e8)](!![]),'tier2':z[_0x18d8c4(0x1cd)](),'tier3':z['object']({'network':z[_0x18d8c4(0x1cd)](),'video':z[_0x18d8c4(0x1cd)]()}),'retention':z[_0x18d8c4(0x1d6)]({'runs':z[_0x18d8c4(0x1f4)]()['int']()[_0x18d8c4(0x1d3)](),'days':z['number']()[_0x18d8c4(0x1e0)]()[_0x18d8c4(0x1d3)]()}),'storageDir':z['string']()[_0x18d8c4(0x1e2)](0x1)}),DialogPolicySchema=z[_0x18d8c4(0x1f1)]([_0x18d8c4(0x1e6),_0x18d8c4(0x1dd),'manual']),LinterRuleIdSchema=z['enum'](['lint:deep-css','lint:xpath','lint:obfuscated-class',_0x18d8c4(0x1ef),_0x18d8c4(0x1f3),'lint:evaluate-discouraged',_0x18d8c4(0x1c0),'lint:mustache-in-dsl',_0x18d8c4(0x1d0),'lint:regex-invalid',_0x18d8c4(0x1c4),'lint:api-call-file-body',_0x18d8c4(0x1bf),_0x18d8c4(0x1d4),_0x18d8c4(0x1bd),_0x18d8c4(0x1ca),_0x18d8c4(0x1cf),_0x18d8c4(0x1cb),_0x18d8c4(0x1ce),_0x18d8c4(0x1de),'validator:function-shape',_0x18d8c4(0x1db),_0x18d8c4(0x1f0),_0x18d8c4(0x1ed),_0x18d8c4(0x1e1),'validator:unsupported-expression',_0x18d8c4(0x1df),_0x18d8c4(0x1f5),_0x18d8c4(0x1e4),_0x18d8c4(0x1d5)]),LinterSeveritySchema=z[_0x18d8c4(0x1f1)]([_0x18d8c4(0x1d1),'warn',_0x18d8c4(0x1ea)]),LinterConfigSchema=z[_0x18d8c4(0x1d6)]({'enabled':z[_0x18d8c4(0x1cd)](),'rules':z['record'](LinterRuleIdSchema,LinterSeveritySchema)}),QueueConfigSchema=z[_0x18d8c4(0x1d6)]({'enabled':z['boolean']()['default'](!![]),'concurrency':z[_0x18d8c4(0x1f4)]()[_0x18d8c4(0x1e0)]()[_0x18d8c4(0x1d3)]()['default'](0x1)}),ScheduleSchema=z[_0x18d8c4(0x1d6)]({'collection':z[_0x18d8c4(0x1c7)]()[_0x18d8c4(0x1e2)](0x1),'cron':z[_0x18d8c4(0x1c7)]()[_0x18d8c4(0x1e2)](0x1),'env':z[_0x18d8c4(0x1c7)]()[_0x18d8c4(0x1e2)](0x1)[_0x18d8c4(0x1be)](),'prepare':z[_0x18d8c4(0x1c7)]()[_0x18d8c4(0x1e2)](0x1)[_0x18d8c4(0x1be)]()}),McpConfigSchema=z[_0x18d8c4(0x1d6)]({'transport':z[_0x18d8c4(0x1c5)](_0x18d8c4(0x1c9))}),SandboxConfigSchema=z[_0x18d8c4(0x1d6)]({'shellCwd':z['string']()[_0x18d8c4(0x1be)](),'shellTimeoutMs':z[_0x18d8c4(0x1f4)]()[_0x18d8c4(0x1e0)]()['positive']()[_0x18d8c4(0x1be)](),'exportSecrets':z[_0x18d8c4(0x1e9)](z['string']())[_0x18d8c4(0x1be)](),'database':z['string']()['optional'](),'apiBaseUrl':z[_0x18d8c4(0x1c7)]()['url']()['optional'](),'uploadDir':z[_0x18d8c4(0x1c7)]()['optional']()}),WebServerConfigSchema=z['object']({'command':z[_0x18d8c4(0x1c7)]()[_0x18d8c4(0x1e2)](0x1),'url':z['string']()[_0x18d8c4(0x1e3)](),'reuseExistingServer':z['boolean']()[_0x18d8c4(0x1e8)](!![]),'timeoutMs':z['number']()[_0x18d8c4(0x1e0)]()['positive']()['default'](0xea60)}),UnotestConfigSchema=z['object']({'baseUrl':z[_0x18d8c4(0x1c7)]()[_0x18d8c4(0x1e3)]()[_0x18d8c4(0x1be)](),'webServer':WebServerConfigSchema[_0x18d8c4(0x1be)](),'browsers':z[_0x18d8c4(0x1e9)](BrowserSchema)[_0x18d8c4(0x1e2)](0x1),'channel':BrowserChannelSchema,'viewport':ViewportSchema,'retry':RetryConfigSchema,'failureBundle':FailureBundleConfigSchema,'dialogPolicy':DialogPolicySchema,'storageState':z['string']()[_0x18d8c4(0x1be)](),'testDir':z['string']()[_0x18d8c4(0x1e2)](0x1),'helpersDir':z[_0x18d8c4(0x1c7)]()[_0x18d8c4(0x1e2)](0x1),'defaultTimeoutMs':z['number']()['int']()[_0x18d8c4(0x1d3)](),'defaultNavigationTimeoutMs':z[_0x18d8c4(0x1f4)]()[_0x18d8c4(0x1e0)]()[_0x18d8c4(0x1d3)](),'linter':LinterConfigSchema,'mcp':McpConfigSchema,'queue':QueueConfigSchema,'schedules':z[_0x18d8c4(0x1e9)](ScheduleSchema)[_0x18d8c4(0x1e8)]([]),'sandbox':SandboxConfigSchema}),DEFAULT_CONFIG={'browsers':[_0x18d8c4(0x1c8)],'viewport':{'width':0x500,'height':0x2d0},'retry':{'count':0x0,'on':[_0x18d8c4(0x1c6)]},'failureBundle':{'tier1':!![],'tier2':!![],'tier3':{'network':![],'video':![]},'retention':{'runs':0x14,'days':0x7},'storageDir':_0x18d8c4(0x1da)},'dialogPolicy':_0x18d8c4(0x1e6),'testDir':_0x18d8c4(0x1eb),'helpersDir':_0x18d8c4(0x1ee),'defaultTimeoutMs':0xbb8,'defaultNavigationTimeoutMs':0x7530,'linter':{'enabled':!![],'rules':{'lint:deep-css':_0x18d8c4(0x1f2),'lint:xpath':_0x18d8c4(0x1f2),'lint:obfuscated-class':_0x18d8c4(0x1f2),'lint:pause-explicit':_0x18d8c4(0x1f2),'lint:disambig-by-index':_0x18d8c4(0x1d1),'lint:evaluate-discouraged':_0x18d8c4(0x1f2),'lint:scenario-in-root':_0x18d8c4(0x1ea),'lint:mustache-in-dsl':_0x18d8c4(0x1ea),'lint:goto-concat':_0x18d8c4(0x1f2),'lint:regex-invalid':'error','lint:echo-assert':_0x18d8c4(0x1f2),'lint:lint-ok-missing-reason':_0x18d8c4(0x1f2),'lint:flow-returns-value':_0x18d8c4(0x1f2),'validator:unknown-function':_0x18d8c4(0x1ea)}},'mcp':{'transport':_0x18d8c4(0x1c9)},'queue':{'enabled':!![],'concurrency':0x1},'schedules':[],'sandbox':{}};export{BrowserChannelSchema,BrowserSchema,DEFAULT_CONFIG,DialogPolicySchema,FailureBundleConfigSchema,LinterConfigSchema,LinterRuleIdSchema,LinterSeveritySchema,McpConfigSchema,QueueConfigSchema,RetryConfigSchema,RetryReasonSchema,SandboxConfigSchema,ScheduleSchema,UnotestConfigSchema,ViewportSchema,WebServerConfigSchema};