@unotest/protocol 0.5.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 ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@unotest/protocol` are documented in this file.
4
+
5
+ ## [0.5.0] - 2026-05-29
6
+
7
+ Initial release. Shared protocol types and contracts for the `@unotest`
8
+ ecosystem: `IRunnerAdapter`, `RunArtifact`, `WsMessage`, path constants,
9
+ and `LockFile` types used by `@unotest/web` and `@unotest/viewer`.
10
+ Includes `EnvFile` / `EnvVar` / `EnvUpdate` (viewer env-variable editor)
11
+ and the `ENV_FILE` path constant.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ivan Volkov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # @unotest/protocol
2
+
3
+ Shared protocol types and contracts for the `@unotest` ecosystem.
4
+
5
+ Consumed by:
6
+
7
+ - `@unotest/web` — web (Playwright) test runner
8
+ - `@unotest/mobile` — mobile test runner
9
+ - `@unotest/viewer` — localhost viewer (HTTP + WS)
10
+
11
+ This package contains **only**:
12
+
13
+ - TypeScript types (stripped at compile time)
14
+ - String constants (filesystem paths, kind literals)
15
+ - Pure functions (validators, ID builders) that are browser-safe
16
+
17
+ **No runtime dependencies.** No `node:fs`, no Playwright, no Zod. Anything
18
+ that needs an environment lives in the consumer package — not here.
19
+
20
+ ## Key contracts
21
+
22
+ - `IRunnerAdapter` — the surface `@unotest/viewer` consumes to start a
23
+ run without knowing which runner package it's driving. Implementations
24
+ live in `@unotest/web` and `@unotest/mobile`.
25
+ - `RunRequest` — discriminated union of the run shapes the viewer can
26
+ ask an adapter to execute.
27
+ - `RunArtifact` / `RunMeta` / `RunManifest` — the on-disk format
28
+ written by runners under `unotest/.runs/<runId>/`, read by the viewer.
29
+ - `WsMessage` — the typed websocket event protocol from viewer-server
30
+ to viewer-web.
@@ -0,0 +1,666 @@
1
+ /** User-facing project directory containing scenarios + helpers + collections. */
2
+ declare const UNOTEST_DIR = "unotest";
3
+ /** Subdir under `unotest/` for `.test.js` scenarios. */
4
+ declare const E2E_SUBDIR = "e2e";
5
+ /** Subdir under `unotest/e2e/` for reusable `snake_case` helper files. */
6
+ declare const HELPERS_SUBDIR = "_helpers";
7
+ /** Subdir under `unotest/e2e/` for `.yaml` collection manifests. */
8
+ declare const COLLECTIONS_SUBDIR = "_collections";
9
+ /** Subdir under `unotest/` for runtime run-artifact dirs (`<runId>/`).
10
+ * Dot-prefix so a single `.gitignore` line covers all runtime debris
11
+ * without touching user code in `unotest/e2e/`. */
12
+ declare const RUNS_SUBDIR = ".runs";
13
+ /** Lockfile path *relative to* `unotest/`. Project-local (multiple viewers
14
+ * for different projects coexist). */
15
+ declare const LOCK_FILE = ".viewer.lock";
16
+ /** Server log destination when viewer is spawned detached (no terminal to
17
+ * inherit stdout). Relative to `unotest/`. */
18
+ declare const LOG_FILE = ".viewer.log";
19
+ /** Append-only JSONL with `RunArtifact` events; lives at
20
+ * `unotest/.runs/<runId>/steps.jsonl`. */
21
+ declare const STEPS_FILE = "steps.jsonl";
22
+ /** mtime-updated periodically by the runner; viewer treats run as
23
+ * `interrupted` after `> 30s` without mtime change (per design-v0). */
24
+ declare const HEARTBEAT_FILE = "heartbeat";
25
+ /** Captured child-process stdout when run is spawned by viewer. */
26
+ declare const STDOUT_FILE = "stdout.log";
27
+ /** Captured child-process stderr when run is spawned by viewer. */
28
+ declare const STDERR_FILE = "stderr.log";
29
+ /** Append-only JSONL of `DbgCommand` lines written by viewer/MCP/CLI;
30
+ * read by the debug-commands-watcher inside the runner-child. Lives at
31
+ * `unotest/.runs/<runId>/commands.jsonl`. */
32
+ declare const COMMANDS_FILE = "commands.jsonl";
33
+ /** Atomically rewritten JSON with the current runtime snapshot (vars,
34
+ * callStack, state, lastFailure). Producers: runner-child writes after
35
+ * every step / on pause / on resume. Readers: viewer's
36
+ * `GET /api/runs/:runId/runtime` and MCP `inspect_runtime`. Distinct
37
+ * from the page-snapshot (semantic-DOM YAML) which lives in
38
+ * failure-bundles. */
39
+ declare const RUNTIME_FILE = "runtime.json";
40
+ /** Per-project shared breakpoint store. Path is relative to project root
41
+ * (NOT under `unotest/.runs/`). Schema: `BreakpointsFile` in
42
+ * `protocol/breakpoints-file.ts`. */
43
+ declare const DEBUGGER_FILE = "unotest/.debugger.json";
44
+ /** Project env file edited by the viewer's env-variable editor. Relative
45
+ * to project root. Surfaced as `EnvFile` (`protocol/env-file.ts`). */
46
+ declare const ENV_FILE = "unotest/.env";
47
+
48
+ interface RegexLiteral {
49
+ readonly kind: "regex";
50
+ readonly source: string;
51
+ /** ES5 subset — substring of `gim`, no duplicates. */
52
+ readonly flags: string;
53
+ }
54
+ /** A field whose runtime accepts either a literal string match or a
55
+ * regex matcher (Playwright API convention). */
56
+ type Matcher = string | RegexLiteral;
57
+ declare function isRegexLiteral(v: unknown): v is RegexLiteral;
58
+ /** Build a RegexLiteral from primitive parts. Convenience for tests
59
+ * and codegen — the canonical constructor path is `@unotest/dsl`'s
60
+ * `RegExpValue` (parsed from `/pattern/flags` syntax). Flag
61
+ * validation is intentionally NOT duplicated here: by the time a
62
+ * RegexLiteral exists, its flags have already been validated at the
63
+ * DSL parse boundary, and re-validation would create two sources of
64
+ * truth for the same rule. */
65
+ declare function regexLiteral(source: string, flags?: string): RegexLiteral;
66
+
67
+ declare function makeRunId(name: string): string;
68
+ /** Strip everything that isn't safe as a directory name segment. Folder
69
+ * separators in `name` collapse to `-` so scenario `auth/signin` becomes
70
+ * `auth-signin` in the runId. Consecutive dashes collapse to one, and
71
+ * leading/trailing dashes are trimmed so a name like `🔥 smoke 🔥` ends
72
+ * up as `smoke` rather than `--smoke--`. When the result is empty
73
+ * (an all-unicode-emoji name yields no safe characters), we fall back
74
+ * to a literal `run` segment — uniqueness still comes from the ts +
75
+ * random suffix appended by `makeRunId`. */
76
+ declare function sanitizeRunIdSegment(name: string): string;
77
+
78
+ /** Filesystem-unsafe characters in a collection name. Block-list approach
79
+ * (any Unicode letter is fine — collections can be named in any
80
+ * language) bounded by what `posix.join("…", `${name}.yaml`)` must keep
81
+ * safe: no path separators, no Windows-reserved punctuation, no NUL /
82
+ * control chars. */
83
+ declare const COLLECTION_NAME_FORBIDDEN_RE: RegExp;
84
+ /** Validate a collection name. Returns `null` on success, or a
85
+ * human-readable reason string on failure. Shared by the viewer-server
86
+ * writer (authoritative) and any client form (pre-flight) so both sides
87
+ * agree on what's acceptable. Pure function — safe to bundle in the
88
+ * browser. */
89
+ declare function validateCollectionName(name: string): string | null;
90
+ /** Parsed YAML collection manifest from `unotest/e2e/_collections/*.yaml`.
91
+ * Per design-v0/decisions.md, Phase 1 keeps the manifest minimal — only
92
+ * `description` and `scenarios`. No `parallel`/`env`/`retry`/`timeout`
93
+ * fields until a real use-case appears. */
94
+ interface CollectionMeta {
95
+ /** Filename stem (single source of truth — no `name:` field inside YAML). */
96
+ name: string;
97
+ /** Project-relative path to the `.yaml` file. */
98
+ path: string;
99
+ /** Optional one-line description from the YAML's top-level `description:`. */
100
+ description: string | null;
101
+ /** Scenario names referenced in the YAML's `scenarios:` array. Names are
102
+ * scenario-meta `name` values (file stem relative to `e2e/`). Orphan
103
+ * references (scenario file missing) are NOT filtered out — the viewer
104
+ * surfaces them as red entries with «file not found» tooltip; running
105
+ * a collection skips orphans with a warning, not blocks. */
106
+ scenarios: string[];
107
+ }
108
+
109
+ /** Run a single scenario by its `name` (file stem under `e2e/`). */
110
+ interface ScenarioRunRequest {
111
+ kind: "scenario";
112
+ /** Scenario `name` — file stem relative to `unotest/e2e/`. */
113
+ scenario: string;
114
+ /** Run with browser visible (web) / device window visible (mobile).
115
+ * Adapter maps this to the runner's preferred env / arg. */
116
+ headed?: boolean;
117
+ /** Spawn with debugger enabled (breakpoints from
118
+ * `unotest/.debugger.json` + inline override). */
119
+ debug?: boolean;
120
+ /** Inline breakpoint override as `"line:col"` strings. Only honored
121
+ * when `debug=true`. */
122
+ breakpoints?: string[];
123
+ }
124
+ /** Run a collection — the runner's CLI orchestrates scenarios inside.
125
+ * Viewer doesn't see the per-scenario subprocesses; it sees one direct
126
+ * child plus the artifacts each scenario writes to its own `.runs/`
127
+ * dir, linked to this parent via `parentRunId` in their manifests. */
128
+ interface CollectionRunRequest {
129
+ kind: "collection";
130
+ /** Collection `name` — filename stem under `_collections/`. */
131
+ collection: string;
132
+ /** Forward to each child scenario. */
133
+ headed?: boolean;
134
+ /** Worker count for parallel scenario execution. Default `1`
135
+ * (serial). The adapter passes it to the CLI; the runner enforces. */
136
+ workers?: number;
137
+ /** Stop on first failure. Default `false` (continue-on-fail). */
138
+ bail?: boolean;
139
+ }
140
+ /** Discriminated union of every run-shape the viewer can request. */
141
+ type RunRequest = ScenarioRunRequest | CollectionRunRequest;
142
+ /** All `kind` literals — useful for switch-completeness checks and for
143
+ * adapter `supportedKinds` lists. */
144
+ type RunKind = RunRequest["kind"];
145
+
146
+ declare const RUN_MANIFEST_FILE = "manifest.json";
147
+ interface RunManifest {
148
+ /** Format-version of this manifest. Bump only on breaking schema
149
+ * changes — additive optional fields don't require it. Starts at 1. */
150
+ schemaVersion: 1;
151
+ /** Identifies which RunRequest variant produced this run. */
152
+ kind: RunKind;
153
+ /** Original (un-sanitized) `ref` from the request — scenario `name`
154
+ * for `kind:"scenario"`, collection `name` for `kind:"collection"`.
155
+ * The `runId` directory uses a sanitized form; this carries the
156
+ * human-readable original. */
157
+ ref: string;
158
+ /** Run-id of the parent collection-run when this is a child scenario
159
+ * spawned inside a collection. Absent for top-level scenario-runs
160
+ * and for collection-runs themselves. */
161
+ parentRunId?: string;
162
+ /** Adapter `name` that produced this run (`"@unotest/web"`,
163
+ * `"@unotest/mobile"`). Lets the viewer pick rendering details for
164
+ * runner-specific bundle contents without runner branches in core
165
+ * logic. */
166
+ adapter: string;
167
+ /** Wallclock at runner start. Distinct from the first event's `t`
168
+ * (which may have leading delay for adapter setup). */
169
+ startedAt: number;
170
+ }
171
+
172
+ /** The argv/env builder for a specific runner CLI. Pure functions —
173
+ * no spawning, no fs access. The viewer owns the actual subprocess
174
+ * lifecycle; the adapter merely tells it WHAT to spawn. */
175
+ interface IRunnerAdapter {
176
+ /** Human-readable identity for logs and error messages
177
+ * (e.g. `"@unotest/web"`). NOT used for control flow — viewer
178
+ * must not branch on this. */
179
+ readonly name: string;
180
+ /** Which `RunRequest.kind` values this adapter can handle. Viewer
181
+ * rejects unsupported kinds with `UnsupportedRunKindError` before
182
+ * building argv. */
183
+ readonly supportedKinds: ReadonlyArray<RunKind>;
184
+ /** Absolute path to the bin entry that should be spawned with the
185
+ * current Node binary: `spawn(process.execPath, [resolveBin(), ...buildArgs(req)])`.
186
+ * Throws if the runner package isn't installed reachably from the
187
+ * viewer's project root. */
188
+ resolveBin(): string;
189
+ /** Argv to pass after the bin path. Pure — depends only on `req`.
190
+ * Example for web: `["e2e", "smoke", "--debug", "--break", "10:4"]`. */
191
+ buildArgs(req: RunRequest): string[];
192
+ /** Environment overrides merged on top of `process.env` by the
193
+ * caller. Pure — no `process.env` reads here. Example for web:
194
+ * `{ UNOTEST_HEADED: "1" }`. Worker-isolation env
195
+ * (`UNOTEST_WORKER_INDEX/COUNT`) is NOT the adapter's
196
+ * responsibility — see `D-C4` in the collections plan. */
197
+ buildEnv(req: RunRequest): NodeJS.ProcessEnv;
198
+ }
199
+ /** Thrown by the viewer when a `RunRequest.kind` isn't in the
200
+ * adapter's `supportedKinds`. The exception filter maps to HTTP 400
201
+ * with the kind name + adapter name in the message. */
202
+ declare class UnsupportedRunKindError extends Error {
203
+ constructor(kind: RunKind, adapterName: string);
204
+ }
205
+ /** Thrown by the viewer when the configured runner package can't be
206
+ * located on disk. Message names the package + how to install. */
207
+ declare class RunnerAdapterNotFoundError extends Error {
208
+ constructor(packageName: string, hint: string);
209
+ }
210
+ /** Thrown by the viewer when `unotest.config.*` doesn't declare a
211
+ * `runner:` field. Surfaces what packages exist so the user can
212
+ * pick. */
213
+ declare class RunnerNotConfiguredError extends Error {
214
+ constructor(message: string);
215
+ }
216
+
217
+ /** Session profile — the kind of process a tab spawns. Mirrored by the
218
+ * viewer's TerminalStore.TermKind. */
219
+ type TerminalKind = "local" | "claude" | "codex";
220
+ interface TerminalSpawnMessage {
221
+ type: "spawn";
222
+ id: string;
223
+ kind: TerminalKind;
224
+ cols: number;
225
+ rows: number;
226
+ }
227
+ interface TerminalDataMessage {
228
+ type: "data";
229
+ id: string;
230
+ /** Raw bytes the user typed, forwarded to the PTY stdin. */
231
+ data: string;
232
+ }
233
+ interface TerminalResizeMessage {
234
+ type: "resize";
235
+ id: string;
236
+ cols: number;
237
+ rows: number;
238
+ }
239
+ interface TerminalCloseMessage {
240
+ type: "close";
241
+ id: string;
242
+ }
243
+ type TerminalClientMessage = TerminalSpawnMessage | TerminalDataMessage | TerminalResizeMessage | TerminalCloseMessage;
244
+ interface TerminalOutputMessage {
245
+ type: "output";
246
+ id: string;
247
+ /** Raw PTY stdout/stderr bytes to write to the xterm instance. */
248
+ data: string;
249
+ }
250
+ interface TerminalExitMessage {
251
+ type: "exit";
252
+ id: string;
253
+ exitCode: number | null;
254
+ }
255
+ /** Spawn failure (e.g. `claude` not on PATH) reported to the tab so it can
256
+ * print a friendly line instead of silently dying. */
257
+ interface TerminalErrorMessage {
258
+ type: "error";
259
+ id: string;
260
+ message: string;
261
+ }
262
+ type TerminalServerMessage = TerminalOutputMessage | TerminalExitMessage | TerminalErrorMessage;
263
+ /** The `ws` adapter routes inbound messages by an `event` envelope; both
264
+ * directions use this single event name on `/ws/terminal`. */
265
+ declare const TERMINAL_WS_EVENT = "terminal";
266
+
267
+ /** Contents of `unotest/.viewer.lock`. Written by viewer-server on
268
+ * startup, removed on graceful shutdown. Stale detection: `process.kill(
269
+ * pid, 0)` throws ESRCH if PID is dead → lockfile is stale, safe to
270
+ * overwrite. */
271
+ interface LockFile {
272
+ /** PID of the viewer-server process. */
273
+ pid: number;
274
+ /** Full URL (with port) to reach the viewer. e.g. `http://localhost:5173`. */
275
+ url: string;
276
+ /** ISO 8601 timestamp of `pid` start. Used for tie-breaking and stale-age
277
+ * heuristics if `kill(pid, 0)` is inconclusive. */
278
+ startedAt: string;
279
+ }
280
+
281
+ /** Parsed scenario metadata from the mandatory header lines
282
+ * (per design-v0/decisions.md, "Test source format"):
283
+ *
284
+ * // id-<kebab-slug>
285
+ * // <human title>
286
+ * // #<6-hex-color>
287
+ * function test_<...>() { ... }
288
+ *
289
+ * All three lines are required; a file without a complete header is
290
+ * rejected by the discovery service as parse-error (no legacy fallback). */
291
+ interface ScenarioMeta {
292
+ /** Slug from `// id-<slug>`. Stable test identity across rename. */
293
+ id: string;
294
+ /** Human-readable title from `// <text>`. */
295
+ title: string;
296
+ /** Color identifier from `// #RRGGBB`. Used only in tab headers as a
297
+ * thin colored strip — NOT a status signal. */
298
+ color: string;
299
+ /** File stem relative to `unotest/e2e/`, dirs as slashes.
300
+ * Examples: `smoke-welcome`, `auth/signin-flow`.
301
+ * Source-of-truth identity for CLI invocations and tab keys. */
302
+ name: string;
303
+ /** Project-relative path to the source file. */
304
+ path: string;
305
+ }
306
+
307
+ /** Helper file metadata. Helpers live in `unotest/e2e/_helpers/**` as
308
+ * `snake_case` functions (`signin_as`, `seed_user`, …). The runner
309
+ * auto-discovers them; no import system in scenarios. */
310
+ interface HelperMeta {
311
+ /** File stem relative to `_helpers/`, dirs as slashes. */
312
+ name: string;
313
+ /** Project-relative path to the source file. */
314
+ path: string;
315
+ }
316
+
317
+ /** Terminal outcome reported by the runner via `run:finished`. */
318
+ type RunOutcome = "completed" | "failed" | "aborted" | "interrupted";
319
+ /** Scenario-level run-id. Format opaque to consumers; treat as string. */
320
+ type RunId = string;
321
+ /** Common fields on every event. */
322
+ interface RunArtifactBase {
323
+ /** Wallclock timestamp (Date.now()) at emit. */
324
+ t: number;
325
+ }
326
+ interface RunStartedEvent extends RunArtifactBase {
327
+ kind: "run:started";
328
+ /** Same as the `<runId>` directory name; included for self-describing
329
+ * events when reassembled out-of-band. */
330
+ runId: RunId;
331
+ /** Scenario `name` (file stem relative to `e2e/`). */
332
+ scenario: string;
333
+ }
334
+ interface StepStartedEvent extends RunArtifactBase {
335
+ kind: "step:started";
336
+ /** Line number of the DSL call in the source file (1-indexed). */
337
+ line: number;
338
+ /** Column number (0-indexed). */
339
+ col: number;
340
+ /** Source text of the statement (e.g. `goto("https://…")`). */
341
+ text: string;
342
+ }
343
+ interface StepOkEvent extends RunArtifactBase {
344
+ kind: "step:ok";
345
+ /** Wall-clock duration of the step that just finished. */
346
+ durationMs: number;
347
+ }
348
+ interface StepFailEvent extends RunArtifactBase {
349
+ kind: "step:fail";
350
+ /** Human-readable error message (single line preferred). */
351
+ error: string;
352
+ /** Error class name (e.g. `"AssertionError"`, `"LocatorError"`,
353
+ * `"TimeoutError"`). Lets the viewer's `ErrorCard` show a typed
354
+ * pill instead of a generic "Error" label. Optional because some
355
+ * thrown errors lack a meaningful class name. */
356
+ errorName?: string;
357
+ /** Project-relative path to a Playwright trace zip, if captured. */
358
+ trace?: string;
359
+ }
360
+ interface ScreenshotEvent extends RunArtifactBase {
361
+ kind: "screenshot";
362
+ /** Project-relative path to the PNG (under `unotest/.runs/<runId>/screenshot/`). */
363
+ path: string;
364
+ }
365
+ /** Emitted ONCE after a failing run when the runner has finished writing
366
+ * the D-16 failure bundle to disk. Viewer's inspector reads
367
+ * `bundlePath` to fetch artifacts (screenshot, semantic DOM, network,
368
+ * trace) via `/api/runs/<runId>/asset?path=...`. The bundle directory
369
+ * is keyed by its own id (`<scenario>__<isoDate>__<stepIdx>`) under
370
+ * `.unotest/failures/`, not under `unotest/.runs/`, because the
371
+ * bundle layout pre-dates per-run dirs and is still consumed by the
372
+ * agent-fix MCP tools. */
373
+ interface FailureBundleWrittenEvent extends RunArtifactBase {
374
+ kind: "failure-bundle:written";
375
+ /** Project-relative path to the bundle directory. */
376
+ bundlePath: string;
377
+ /** Project-relative path to the screenshot PNG inside the bundle,
378
+ * when tier-2 capture succeeded. */
379
+ screenshotPath?: string;
380
+ }
381
+ interface RunFinishedEvent extends RunArtifactBase {
382
+ kind: "run:finished";
383
+ outcome: RunOutcome;
384
+ }
385
+ /** Debugger pause. Reason discriminates:
386
+ * - `breakpoint` — executor halted before a statement whose `line:col` is
387
+ * in `RunOptions.breakpoints`.
388
+ * - `step` — `pauseRequested` flag was set by a `pause` debug command,
389
+ * or the run was started in step-mode and just completed a statement.
390
+ * - `failure` — executor caught an error and `pauseOnFailure` is true.
391
+ *
392
+ * When the runner is hosting a debug session it also writes a parallel
393
+ * `runtime.json` file with the same snapshot. The snapshot is included
394
+ * inline here for replay / single-frame consumers that don't want to do a
395
+ * separate fs read. */
396
+ interface PausedEvent extends RunArtifactBase {
397
+ kind: "paused";
398
+ reason: "breakpoint" | "step" | "failure";
399
+ line: number;
400
+ col: number;
401
+ /** Inline snapshot of the runtime state at the pause point. `vars` is
402
+ * per-value JSON-stringified (LocatorValue → "<Locator role=... name=...>"
403
+ * human-readable string) and per-var truncated to 4KB to keep the JSONL
404
+ * line manageable. `callStack` is outermost→innermost user-function frames. */
405
+ snapshot?: {
406
+ vars: Record<string, string>;
407
+ callStack: {
408
+ fn: string;
409
+ file: string;
410
+ line: number;
411
+ }[];
412
+ };
413
+ }
414
+ /** Emitted when execution resumes after a `paused` event. The `by` field
415
+ * records which debug command unblocked it — purely informational; the
416
+ * next `step:started` event carries the actual line/col. */
417
+ interface ResumedEvent extends RunArtifactBase {
418
+ kind: "resumed";
419
+ by: "step" | "continue";
420
+ }
421
+ /** Discriminated union of every JSONL event the scenario runner emits. */
422
+ type RunArtifact = RunStartedEvent | StepStartedEvent | StepOkEvent | StepFailEvent | ScreenshotEvent | FailureBundleWrittenEvent | PausedEvent | ResumedEvent | RunFinishedEvent;
423
+ interface CollectionRunStartedEvent extends RunArtifactBase {
424
+ kind: "collection-run:started";
425
+ runId: RunId;
426
+ /** Collection `name` (filename stem). */
427
+ collection: string;
428
+ /** Scenario names that will be executed, in order. */
429
+ scenarios: string[];
430
+ }
431
+ interface CollectionScenarioStartedEvent extends RunArtifactBase {
432
+ kind: "collection-run:scenario-started";
433
+ /** The scenario-run-id created when this scenario started executing. */
434
+ scenarioRunId: RunId;
435
+ scenario: string;
436
+ }
437
+ interface CollectionScenarioFinishedEvent extends RunArtifactBase {
438
+ kind: "collection-run:scenario-finished";
439
+ scenarioRunId: RunId;
440
+ scenario: string;
441
+ outcome: RunOutcome;
442
+ }
443
+ interface CollectionRunFinishedEvent extends RunArtifactBase {
444
+ kind: "collection-run:finished";
445
+ outcome: RunOutcome;
446
+ }
447
+ type CollectionRunArtifact = CollectionRunStartedEvent | CollectionScenarioStartedEvent | CollectionScenarioFinishedEvent | CollectionRunFinishedEvent;
448
+
449
+ /** Lifecycle stage as inferred by the server when listing runs. */
450
+ type RunStatus = "running" | "passed" | "failed" | "aborted" | "interrupted";
451
+ interface RunMeta {
452
+ runId: RunId;
453
+ kind: RunKind;
454
+ /** Scenario `name` (file stem under `e2e/`) for `kind:"scenario"`,
455
+ * collection `name` (yaml stem) for `kind:"collection"`. */
456
+ ref: string;
457
+ /** Wallclock from the `run:started` event. */
458
+ startedAt: number;
459
+ /** Wallclock from the `run:finished` event, or null if still active. */
460
+ finishedAt: number | null;
461
+ /** Total wall-clock duration, or null if still running. */
462
+ durationMs: number | null;
463
+ /** Status derived from outcome / liveness — `running` while the run's
464
+ * heartbeat is fresh and no `run:finished` has landed. */
465
+ status: RunStatus;
466
+ /** Terminal outcome from `run:finished`, or null if still running. */
467
+ outcome: RunOutcome | null;
468
+ /** Run-id of the parent collection-run when this is a child scenario
469
+ * spawned inside one. Absent for top-level scenarios and for
470
+ * collection-runs themselves. Populated by the writer from the
471
+ * child's `manifest.json`. */
472
+ parentRunId?: string;
473
+ }
474
+
475
+ interface FsScenariosChangedMessage {
476
+ kind: "fs:scenarios-changed";
477
+ }
478
+ interface FsHelpersChangedMessage {
479
+ kind: "fs:helpers-changed";
480
+ }
481
+ interface FsCollectionsChangedMessage {
482
+ kind: "fs:collections-changed";
483
+ }
484
+ /** Emitted when `unotest/.debugger.json` is written by anyone (viewer
485
+ * via PUT /api/breakpoints, MCP, CLI). The client's BreakpointsStore
486
+ * refetches via GET /api/breakpoints so all open tabs converge on the
487
+ * same BP set. */
488
+ interface FsBreakpointsChangedMessage {
489
+ kind: "fs:bp-changed";
490
+ }
491
+ /** Emitted when a new `unotest/.runs/<runId>/` directory appears. The
492
+ * browser fetches `/api/runs/<runId>` for the full snapshot. */
493
+ interface FsRunStartedMessage {
494
+ kind: "fs:run-started";
495
+ runId: RunId;
496
+ scenario: string;
497
+ }
498
+ /** Emitted on every appended line of `unotest/.runs/<runId>/steps.jsonl`.
499
+ * `event` is the parsed line. */
500
+ interface FsRunStepMessage {
501
+ kind: "fs:run-step";
502
+ runId: RunId;
503
+ event: RunArtifact;
504
+ }
505
+ /** Same as `fs:run-step` but for collection-run-level events. */
506
+ interface FsCollectionRunStepMessage {
507
+ kind: "fs:collection-run-step";
508
+ runId: RunId;
509
+ event: CollectionRunArtifact;
510
+ }
511
+ /** Emitted when the run's `steps.jsonl` is closed (last event was
512
+ * `run:finished`) OR the heartbeat goes stale (>30s without mtime
513
+ * change — viewer infers `interrupted`). */
514
+ interface FsRunFinishedMessage {
515
+ kind: "fs:run-finished";
516
+ runId: RunId;
517
+ outcome: RunOutcome;
518
+ }
519
+ /** A single line of stdout or stderr captured from a runner child the
520
+ * viewer-server spawned. Logs always carry a `runId` so the browser
521
+ * attaches them to the right run-pane. */
522
+ interface LogLineMessage {
523
+ kind: "log:line";
524
+ runId: RunId;
525
+ stream: "stdout" | "stderr";
526
+ line: string;
527
+ }
528
+ /** Non-fatal server error surfaced to the browser (e.g. unexpected file
529
+ * format, watcher restart, child-spawn failure). Fatal server crashes
530
+ * drop the WS — the browser reconnects and resyncs via REST. */
531
+ interface ErrorMessage {
532
+ kind: "error";
533
+ message: string;
534
+ }
535
+ /** Discriminated union of every WS message the server emits to clients. */
536
+ type WsMessage = FsScenariosChangedMessage | FsHelpersChangedMessage | FsCollectionsChangedMessage | FsBreakpointsChangedMessage | FsRunStartedMessage | FsRunStepMessage | FsCollectionRunStepMessage | FsRunFinishedMessage | LogLineMessage | ErrorMessage;
537
+ /** All WS message `kind` literals as a union type — useful for handler
538
+ * registries (`Record<WsMessageKind, Handler>`). */
539
+ type WsMessageKind = WsMessage["kind"];
540
+
541
+ /** Open scenario source (read-only view). Keyed by project-relative path. */
542
+ interface ScenarioTab {
543
+ kind: "scenario";
544
+ path: string;
545
+ }
546
+ /** Open helper source. */
547
+ interface HelperTab {
548
+ kind: "helper";
549
+ path: string;
550
+ }
551
+ /** Open collection manifest (with scenarios list + Run button). */
552
+ interface CollectionTab {
553
+ kind: "collection";
554
+ path: string;
555
+ }
556
+ /** Open scenario-run (live or historical). */
557
+ interface ScenarioRunTab {
558
+ kind: "scenario-run";
559
+ runId: RunId;
560
+ }
561
+ /** Open collection-run (wraps N scenario-runs). */
562
+ interface CollectionRunTab {
563
+ kind: "collection-run";
564
+ runId: RunId;
565
+ }
566
+ /** Discriminated union of every open-tab shape the frontend supports. */
567
+ type TabKind = ScenarioTab | HelperTab | CollectionTab | ScenarioRunTab | CollectionRunTab;
568
+ /** All tab `kind` literals — useful for switch-completeness checks. */
569
+ type TabKindName = TabKind["kind"];
570
+
571
+ interface DbgCommandStep {
572
+ id: string;
573
+ cmd: "step";
574
+ }
575
+ interface DbgCommandContinue {
576
+ id: string;
577
+ cmd: "continue";
578
+ }
579
+ interface DbgCommandPause {
580
+ id: string;
581
+ cmd: "pause";
582
+ }
583
+ interface DbgCommandSetBp {
584
+ id: string;
585
+ cmd: "set-bp";
586
+ line: number;
587
+ col: number;
588
+ }
589
+ interface DbgCommandClearBp {
590
+ id: string;
591
+ cmd: "clear-bp";
592
+ line: number;
593
+ col: number;
594
+ }
595
+ interface DbgCommandAbort {
596
+ id: string;
597
+ cmd: "abort";
598
+ }
599
+ type DbgCommand = DbgCommandStep | DbgCommandContinue | DbgCommandPause | DbgCommandSetBp | DbgCommandClearBp | DbgCommandAbort;
600
+ type DbgCommandKind = DbgCommand["cmd"];
601
+
602
+ interface BreakpointEntry {
603
+ line: number;
604
+ col: number;
605
+ }
606
+ interface BreakpointsFile {
607
+ version: 1;
608
+ scenarios: Record<string, BreakpointEntry[]>;
609
+ }
610
+
611
+ interface EnvVar {
612
+ key: string;
613
+ value: string;
614
+ /** Mask in the UI (key looks like a credential). Value is still sent. */
615
+ secret: boolean;
616
+ /** Render an on/off toggle — value is exactly `"0"` or `"1"`. */
617
+ bool: boolean;
618
+ /** The line is commented out (`# KEY=VALUE`) — inactive. Shown dimmed and
619
+ * read-only; never written back (stays a comment). */
620
+ commented: boolean;
621
+ }
622
+ interface EnvFile {
623
+ version: 1;
624
+ vars: EnvVar[];
625
+ }
626
+ /** PUT body — only values change; keys are fixed by the `.env` file. */
627
+ interface EnvUpdate {
628
+ version: 1;
629
+ values: Record<string, string>;
630
+ }
631
+
632
+ type RuntimeState = "starting" | "running" | "paused-step" | "paused-failure" | "paused-breakpoint" | "completed" | "failed" | "aborted";
633
+ interface RuntimeStateFile {
634
+ runId: string;
635
+ scenario: string;
636
+ testFn: string;
637
+ state: RuntimeState;
638
+ /** Current AST node location while running or paused; null in terminal
639
+ * states (completed/failed/aborted). */
640
+ current: {
641
+ line: number;
642
+ col: number;
643
+ } | null;
644
+ /** Per-value JSON-stringified vars. Locator values render as
645
+ * human-readable strings. Per-var truncated to 4KB. */
646
+ vars: Record<string, string>;
647
+ /** User-function frames outermost→innermost. Empty when at top level. */
648
+ callStack: {
649
+ fn: string;
650
+ file: string;
651
+ line: number;
652
+ }[];
653
+ lastFailure: {
654
+ line: number;
655
+ col: number;
656
+ error: {
657
+ message: string;
658
+ name?: string;
659
+ };
660
+ } | null;
661
+ /** Wallclock timestamp of the last write — readers use this to detect
662
+ * staleness (e.g. ≥30s without update ⇒ runner died). */
663
+ updatedAt: number;
664
+ }
665
+
666
+ export { type BreakpointEntry, type BreakpointsFile, COLLECTIONS_SUBDIR, COLLECTION_NAME_FORBIDDEN_RE, COMMANDS_FILE, type CollectionMeta, type CollectionRunArtifact, type CollectionRunFinishedEvent, type CollectionRunRequest, type CollectionRunStartedEvent, type CollectionRunTab, type CollectionScenarioFinishedEvent, type CollectionScenarioStartedEvent, type CollectionTab, DEBUGGER_FILE, type DbgCommand, type DbgCommandAbort, type DbgCommandClearBp, type DbgCommandContinue, type DbgCommandKind, type DbgCommandPause, type DbgCommandSetBp, type DbgCommandStep, E2E_SUBDIR, ENV_FILE, type EnvFile, type EnvUpdate, type EnvVar, type ErrorMessage, type FailureBundleWrittenEvent, type FsBreakpointsChangedMessage, type FsCollectionRunStepMessage, type FsCollectionsChangedMessage, type FsHelpersChangedMessage, type FsRunFinishedMessage, type FsRunStartedMessage, type FsRunStepMessage, type FsScenariosChangedMessage, HEARTBEAT_FILE, HELPERS_SUBDIR, type HelperMeta, type HelperTab, type IRunnerAdapter, LOCK_FILE, LOG_FILE, type LockFile, type LogLineMessage, type Matcher, type PausedEvent, RUNS_SUBDIR, RUNTIME_FILE, RUN_MANIFEST_FILE, type RegexLiteral, type ResumedEvent, type RunArtifact, type RunFinishedEvent, type RunId, type RunKind, type RunManifest, type RunMeta, type RunOutcome, type RunRequest, type RunStartedEvent, type RunStatus, RunnerAdapterNotFoundError, RunnerNotConfiguredError, type RuntimeState, type RuntimeStateFile, STDERR_FILE, STDOUT_FILE, STEPS_FILE, type ScenarioMeta, type ScenarioRunRequest, type ScenarioRunTab, type ScenarioTab, type ScreenshotEvent, type StepFailEvent, type StepOkEvent, type StepStartedEvent, TERMINAL_WS_EVENT, type TabKind, type TabKindName, type TerminalClientMessage, type TerminalCloseMessage, type TerminalDataMessage, type TerminalErrorMessage, type TerminalExitMessage, type TerminalKind, type TerminalOutputMessage, type TerminalResizeMessage, type TerminalServerMessage, type TerminalSpawnMessage, UNOTEST_DIR, UnsupportedRunKindError, type WsMessage, type WsMessageKind, isRegexLiteral, makeRunId, regexLiteral, sanitizeRunIdSegment, validateCollectionName };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ var _0x4525cf=_0x4e04;(function(_0x5d6d49,_0x1dfb67){var _0x2c662e={_0x34fbd2:0x174},_0x10ba75=_0x4e04,_0x32a0df=_0x5d6d49();while(!![]){try{var _0x2391dd=-parseInt(_0x10ba75(0x178))/0x1*(parseInt(_0x10ba75(0x192))/0x2)+-parseInt(_0x10ba75(0x183))/0x3+parseInt(_0x10ba75(0x176))/0x4*(parseInt(_0x10ba75(0x193))/0x5)+parseInt(_0x10ba75(0x17a))/0x6+-parseInt(_0x10ba75(0x18e))/0x7+parseInt(_0x10ba75(0x180))/0x8+parseInt(_0x10ba75(_0x2c662e._0x34fbd2))/0x9;if(_0x2391dd===_0x1dfb67)break;else _0x32a0df['push'](_0x32a0df['shift']());}catch(_0x347c47){_0x32a0df['push'](_0x32a0df['shift']());}}}(_0x1828,0xcefba));var __defProp=Object[_0x4525cf(0x17e)],__name=(_0x1dec4e,_0x3c2462)=>__defProp(_0x1dec4e,_0x4525cf(0x189),{'value':_0x3c2462,'configurable':!![]}),UNOTEST_DIR=_0x4525cf(0x17f),E2E_SUBDIR=_0x4525cf(0x19d),HELPERS_SUBDIR=_0x4525cf(0x19a),COLLECTIONS_SUBDIR='_collections',RUNS_SUBDIR=_0x4525cf(0x195),LOCK_FILE=_0x4525cf(0x186),LOG_FILE=_0x4525cf(0x17b),STEPS_FILE=_0x4525cf(0x191),HEARTBEAT_FILE='heartbeat',STDOUT_FILE=_0x4525cf(0x19c),STDERR_FILE=_0x4525cf(0x175),COMMANDS_FILE='commands.jsonl',RUNTIME_FILE='runtime.json',DEBUGGER_FILE=_0x4525cf(0x1a1),ENV_FILE=_0x4525cf(0x199);function isRegexLiteral(_0xa7c839){var _0x20ac3a={_0x44f337:0x18d,_0x2eaa9d:0x19f,_0x3e8409:0x196},_0x59e1c8=_0x4525cf;return typeof _0xa7c839==='object'&&_0xa7c839!==null&&_0xa7c839[_0x59e1c8(_0x20ac3a._0x44f337)]===_0x59e1c8(0x194)&&typeof _0xa7c839[_0x59e1c8(_0x20ac3a._0x2eaa9d)]===_0x59e1c8(0x187)&&typeof _0xa7c839[_0x59e1c8(_0x20ac3a._0x3e8409)]===_0x59e1c8(0x187);}__name(isRegexLiteral,'isRegexLiteral');function regexLiteral(_0x1f5378,_0x274fdd=''){return{'kind':'regex','source':_0x1f5378,'flags':_0x274fdd};}function _0x1828(){var _0x10562d=['y2fUBM90igXVywqGCNvUBMvYigfKyxb0zxiGiG','BgvUz3rO','zgvMAw5LuhjVCgvYDhK','Dw5VDgvZDa','oteXnZeZnKjNAfrpEa','DgvYBwLUywW','uNvUBMvYtM90q29UzMLNDxjLzevYCM9Y','ndm3mdqZzfvyuhj4','DhjPBq','BxvZDcbUB3qGyMuGiI4Iig9YiciUlIi','lNzPzxDLCI5SB2nR','C3rYAw5N','BxvZDcbUB3qGyMuGzw1WDhK','BMfTzq','BwfUAwzLC3qUANnVBG','iI4G','BwfRzvj1BKLK','A2LUza','mte3mJGZmZjSrwrjz0i','CNvU','CMvWBgfJzq','C3rLChmUANnVBMW','mZuZmJzfrwD4D3O','mtC4ndbgBMPrzfq','CMvNzxG','lNj1BNm','zMXHz3m','BxvZDcbIzsbHihn0CMLUzW','Dg9tDhjPBMC','Dw5VDgvZDc8Uzw52','x2HLBhbLCNm','BxvZDcbUB3qGy29UDgfPBIbCic8GoIaQid8GiIa8id4GFcbVCIbJB250CM9SignOyxjHy3rLCNm','C3rKB3v0lMXVzW','ztjL','DMfSAwrHDgvdB2XSzwn0Aw9UtMfTzq','C291CMnL','iIbKB2vZig5VDcbZDxbWB3j0ihj1BIbRAw5Kici','Dw5VDgvZDc8UzgvIDwDNzxiUANnVBG','uNvUBMvYqwrHChrLCK5VDezVDw5KrxjYB3i','mtiWndaWntz6AhfqqvK','C3rKzxjYlMXVzW','mtq2ogLcCgDyta','vw5ZDxbWB3j0zwrsDw5lAw5KrxjYB3i','nZHzDMnAuKO','CNvUBMvYici','mtu1odC0nKHNsNzftG','lNzPzxDLCI5SB2C'];_0x1828=function(){return _0x10562d;};return _0x1828();}__name(regexLiteral,'regexLiteral');var RAND_BYTES=0x3;function makeRunId(_0x3bad09){var _0x5b7e24={_0x5ed313:0x17d},_0x41b7df=_0x4525cf;const _0x22e4c8=sanitizeRunIdSegment(_0x3bad09),_0x329de1=Date['now']()[_0x41b7df(0x198)](0x24),_0x35ac39=new Uint8Array(RAND_BYTES);globalThis['crypto']['getRandomValues'](_0x35ac39);let _0x1296e0='';for(let _0x5f1022=0x0;_0x5f1022<_0x35ac39[_0x41b7df(_0x5b7e24._0x5ed313)];_0x5f1022++){_0x1296e0+=_0x35ac39[_0x5f1022][_0x41b7df(0x198)](0x10)['padStart'](0x2,'0');}return _0x22e4c8+'-'+_0x329de1+'-'+_0x1296e0;}__name(makeRunId,_0x4525cf(0x18c));function sanitizeRunIdSegment(_0x485dc8){var _0x129427={_0x29d8c3:0x190},_0x3fe6f1=_0x4525cf;const _0x205fe5=_0x485dc8[_0x3fe6f1(_0x129427._0x29d8c3)](/[^a-zA-Z0-9_-]+/g,'-')[_0x3fe6f1(0x190)](/-+/g,'-')[_0x3fe6f1(0x190)](/^-|-$/g,'');return _0x205fe5[_0x3fe6f1(0x17d)]>0x0?_0x205fe5:_0x3fe6f1(0x18f);}__name(sanitizeRunIdSegment,'sanitizeRunIdSegment');function _0x4e04(_0x1da389,_0x465bf7){_0x1da389=_0x1da389-0x174;var _0x182894=_0x1828();var _0x4e04bf=_0x182894[_0x1da389];if(_0x4e04['NSkCTR']===undefined){var _0x3d6318=function(_0x4cae78){var _0x4e6c75='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0x1dec4e='',_0x3c2462='';for(var _0xa7c839=0x0,_0x1f5378,_0x274fdd,_0x3bad09=0x0;_0x274fdd=_0x4cae78['charAt'](_0x3bad09++);~_0x274fdd&&(_0x1f5378=_0xa7c839%0x4?_0x1f5378*0x40+_0x274fdd:_0x274fdd,_0xa7c839++%0x4)?_0x1dec4e+=String['fromCharCode'](0xff&_0x1f5378>>(-0x2*_0xa7c839&0x6)):0x0){_0x274fdd=_0x4e6c75['indexOf'](_0x274fdd);}for(var _0x22e4c8=0x0,_0x329de1=_0x1dec4e['length'];_0x22e4c8<_0x329de1;_0x22e4c8++){_0x3c2462+='%'+('00'+_0x1dec4e['charCodeAt'](_0x22e4c8)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x3c2462);};_0x4e04['zOLaWv']=_0x3d6318,_0x4e04['FehDCm']={},_0x4e04['NSkCTR']=!![];}var _0x5da670=_0x182894[0x0],_0x16228f=_0x1da389+_0x5da670,_0x3b083e=_0x4e04['FehDCm'][_0x16228f];return!_0x3b083e?(_0x4e04bf=_0x4e04['zOLaWv'](_0x4e04bf),_0x4e04['FehDCm'][_0x16228f]=_0x4e04bf):_0x4e04bf=_0x3b083e,_0x4e04bf;}var COLLECTION_NAME_FORBIDDEN_RE=/[\\/:*?"<>|\x00-\x1f]/;function validateCollectionName(_0x391586){var _0x535afd={_0x13bc86:0x197,_0x4eb493:0x185,_0x19d9cc:0x19b},_0x46cbdf=_0x4525cf;if(typeof _0x391586!==_0x46cbdf(0x187))return _0x46cbdf(_0x535afd._0x13bc86);const _0x25e716=_0x391586[_0x46cbdf(0x184)]();if(_0x25e716[_0x46cbdf(0x17d)]===0x0)return _0x46cbdf(0x188);if(_0x25e716==='.'||_0x25e716==='..')return _0x46cbdf(_0x535afd._0x4eb493);if(_0x25e716['startsWith']('.'))return'must\x20not\x20start\x20with\x20\x27.\x27';if(COLLECTION_NAME_FORBIDDEN_RE['test'](_0x25e716))return _0x46cbdf(_0x535afd._0x19d9cc);return null;}__name(validateCollectionName,_0x4525cf(0x19e));var RUN_MANIFEST_FILE=_0x4525cf(0x18a),UnsupportedRunKindError=class extends Error{static{__name(this,_0x4525cf(0x177));}constructor(_0x435e97,_0x6b153a){var _0x5b3768={_0x68d67e:0x177},_0x58838f=_0x4525cf;super(_0x58838f(0x179)+_0x6b153a+_0x58838f(0x1a0)+_0x435e97+'\x22'),this['name']=_0x58838f(_0x5b3768._0x68d67e);}},RunnerAdapterNotFoundError=class extends Error{static{__name(this,_0x4525cf(0x1a2));}constructor(_0x16bd7d,_0x3710c9){var _0x5c8352={_0x3ceb84:0x17c},_0x251254=_0x4525cf;super(_0x251254(_0x5c8352._0x3ceb84)+_0x16bd7d+_0x251254(0x18b)+_0x3710c9),this[_0x251254(0x189)]='RunnerAdapterNotFoundError';}},RunnerNotConfiguredError=class extends Error{static{__name(this,_0x4525cf(0x182));}constructor(_0x24c564){var _0x33cbe1={_0x5a8eb9:0x189},_0x17c861=_0x4525cf;super(_0x24c564),this[_0x17c861(_0x33cbe1._0x5a8eb9)]=_0x17c861(0x182);}},TERMINAL_WS_EVENT=_0x4525cf(0x181);export{COLLECTIONS_SUBDIR,COLLECTION_NAME_FORBIDDEN_RE,COMMANDS_FILE,DEBUGGER_FILE,E2E_SUBDIR,ENV_FILE,HEARTBEAT_FILE,HELPERS_SUBDIR,LOCK_FILE,LOG_FILE,RUNS_SUBDIR,RUNTIME_FILE,RUN_MANIFEST_FILE,RunnerAdapterNotFoundError,RunnerNotConfiguredError,STDERR_FILE,STDOUT_FILE,STEPS_FILE,TERMINAL_WS_EVENT,UNOTEST_DIR,UnsupportedRunKindError,isRegexLiteral,makeRunId,regexLiteral,sanitizeRunIdSegment,validateCollectionName};
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@unotest/protocol",
3
+ "version": "0.5.0",
4
+ "description": "Cross-runner protocol for the @unotest ecosystem: shared TypeScript types, path constants, the IRunnerAdapter contract that lets @unotest/viewer drive @unotest/web, @unotest/mobile (and future runners) through the same surface. Pure types + pure functions only — zero runtime deps, browser-safe.",
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "author": "Ivan Volkov <ivan@volkov.io>",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/unotest/protocol.git"
13
+ },
14
+ "type": "module",
15
+ "packageManager": "pnpm@10.12.4",
16
+ "engines": {
17
+ "node": ">=20"
18
+ },
19
+ "main": "./dist/index.js",
20
+ "types": "./dist/index.d.ts",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "CHANGELOG.md",
30
+ "LICENSE",
31
+ "README.md"
32
+ ],
33
+ "scripts": {
34
+ "typecheck": "tsc --noEmit",
35
+ "test": "node --import tsx --test --test-reporter=spec 'src/**/*.test.ts'",
36
+ "compile": "tsup",
37
+ "verify": "pnpm typecheck && pnpm test",
38
+ "obfuscate": "node scripts/obfuscate.mjs",
39
+ "build": "pnpm verify && pnpm compile && pnpm obfuscate && pnpm check:tarball",
40
+ "prepublishOnly": "pnpm build",
41
+ "check:no-cyrillic": "node scripts/check-no-cyrillic.mjs",
42
+ "check:no-internal-refs": "node scripts/check-no-internal-refs.mjs",
43
+ "check:surface": "pnpm check:no-cyrillic && pnpm check:no-internal-refs",
44
+ "check:tarball": "node scripts/check-tarball.mjs",
45
+ "prepublish:check": "node scripts/prepublish-check.mjs"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^22.10.0",
49
+ "javascript-obfuscator": "^4.1.1",
50
+ "tsup": "^8.5.1",
51
+ "tsx": "^4.19.2",
52
+ "typescript": "^5.7.2"
53
+ }
54
+ }