@rstest/browser 0.11.5 → 0.11.7

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.
Files changed (86) hide show
  1. package/dist/browser-container/container-static/css/index.43daaf6c93.css +1 -0
  2. package/dist/browser-container/container-static/js/260.3739fe815c.js +194 -0
  3. package/dist/browser-container/container-static/js/index.c0f87bfb3e.js +1 -0
  4. package/dist/browser-container/index.html +1 -1
  5. package/dist/browserExecutor.d.ts +7 -5
  6. package/dist/browserRsbuild.d.ts +139 -0
  7. package/dist/browserV8Coverage.d.ts +15 -0
  8. package/dist/client/240~1.js +150 -0
  9. package/dist/{augmentExpect.d.ts → client/client/augmentExpect.d.ts} +1 -1
  10. package/dist/client/{browserRpc.d.ts → client/browserRpc.d.ts} +1 -1
  11. package/dist/client/{dispatchTransport.d.ts → client/dispatchTransport.d.ts} +8 -4
  12. package/dist/client/client/index.d.ts +2 -0
  13. package/dist/client/client/runIdentity.d.ts +12 -0
  14. package/dist/client/client/runner.d.ts +4 -0
  15. package/dist/client/client/snapshot.d.ts +27 -0
  16. package/dist/client/client/sourceMapSupport.d.ts +36 -0
  17. package/dist/{browser.js → client/index.js} +3 -102
  18. package/dist/client/protocol.d.ts +225 -0
  19. package/dist/client/rpcProtocol.d.ts +138 -0
  20. package/dist/client/runner.js +675 -0
  21. package/dist/client/sourceMap/sourceMapLoader.d.ts +25 -0
  22. package/dist/containerRpc.d.ts +60 -0
  23. package/dist/dispatchCapabilities.d.ts +1 -2
  24. package/dist/headedRunRegistry.d.ts +51 -0
  25. package/dist/headedScheduler.d.ts +58 -0
  26. package/dist/headlessScheduler.d.ts +38 -0
  27. package/dist/headlessTransport.d.ts +4 -2
  28. package/dist/hostController.d.ts +12 -47
  29. package/dist/hostPayloads.d.ts +24 -0
  30. package/dist/index.js +2019 -1516
  31. package/dist/protocol.d.ts +48 -8
  32. package/dist/providers/index.d.ts +21 -0
  33. package/dist/providers/playwright/v8Coverage.d.ts +2 -0
  34. package/dist/rpcProtocol.d.ts +1 -2
  35. package/dist/schedulerSeam.d.ts +42 -0
  36. package/dist/sourceMap/sourceMapLoader.d.ts +12 -1
  37. package/dist/watchRerunPlanner.d.ts +20 -4
  38. package/dist/watchRuntime.d.ts +21 -0
  39. package/dist/watchSignals.d.ts +29 -0
  40. package/package.json +12 -12
  41. package/dist/33.js +0 -7
  42. package/dist/browser-container/container-static/css/index.0521ff95b1.css +0 -1
  43. package/dist/browser-container/container-static/js/506.b2f920fa67.js +0 -194
  44. package/dist/browser-container/container-static/js/index.d27fd63f5e.js +0 -1
  45. package/dist/browser.d.ts +0 -2
  46. package/dist/headlessLatestRerunScheduler.d.ts +0 -18
  47. package/src/augmentExpect.ts +0 -62
  48. package/src/browser.ts +0 -3
  49. package/src/browserExecutor.ts +0 -140
  50. package/src/browserRpcRegistry.ts +0 -57
  51. package/src/client/api.ts +0 -213
  52. package/src/client/browserRpc.ts +0 -86
  53. package/src/client/dispatchTransport.ts +0 -240
  54. package/src/client/entry.ts +0 -807
  55. package/src/client/locator.ts +0 -448
  56. package/src/client/snapshot.ts +0 -113
  57. package/src/client/sourceMapSupport.ts +0 -159
  58. package/src/concurrency.ts +0 -65
  59. package/src/configValidation.ts +0 -263
  60. package/src/dispatchCapabilities.ts +0 -182
  61. package/src/dispatchRouter.ts +0 -82
  62. package/src/env.d.ts +0 -54
  63. package/src/headedSerialTaskQueue.ts +0 -23
  64. package/src/headlessLatestRerunScheduler.ts +0 -76
  65. package/src/headlessTransport.ts +0 -28
  66. package/src/hostController.ts +0 -4633
  67. package/src/index.ts +0 -51
  68. package/src/manifest.d.ts +0 -41
  69. package/src/protocol.ts +0 -225
  70. package/src/providers/index.ts +0 -126
  71. package/src/providers/playwright/compileLocator.ts +0 -130
  72. package/src/providers/playwright/dispatchBrowserRpc.ts +0 -372
  73. package/src/providers/playwright/expectUtils.ts +0 -57
  74. package/src/providers/playwright/implementation.ts +0 -35
  75. package/src/providers/playwright/index.ts +0 -1
  76. package/src/providers/playwright/runtime.ts +0 -159
  77. package/src/providers/playwright/textMatcher.ts +0 -10
  78. package/src/rpcProtocol.ts +0 -218
  79. package/src/runSession.ts +0 -110
  80. package/src/sessionRegistry.ts +0 -89
  81. package/src/sourceMap/sourceMapLoader.ts +0 -96
  82. package/src/viewportPresets.ts +0 -69
  83. package/src/watchRerunPlanner.ts +0 -77
  84. /package/dist/{rslib-runtime.js → 612~0.js} +0 -0
  85. /package/dist/client/{api.d.ts → client/api.d.ts} +0 -0
  86. /package/dist/client/{locator.d.ts → client/locator.d.ts} +0 -0
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Browser snapshot environment that proxies file operations to the host
3
+ * via postMessage RPC through the container.
4
+ */ export declare class BrowserSnapshotEnvironment {
5
+ getVersion(): string;
6
+ getHeader(): string;
7
+ resolveRawPath(_testPath: string, rawPath: string): Promise<string>;
8
+ resolvePath(filepath: string): Promise<string>;
9
+ prepareDirectory(): Promise<void>;
10
+ saveSnapshotFile(filepath: string, snapshot: string): Promise<void>;
11
+ readSnapshotFile(filepath: string): Promise<string | null>;
12
+ removeSnapshotFile(filepath: string): Promise<void>;
13
+ /**
14
+ * Process stack trace for inline snapshots.
15
+ * Maps bundled URLs back to original source file paths.
16
+ */ processStackTrace(stack: {
17
+ file: string;
18
+ line: number;
19
+ column: number;
20
+ method: string;
21
+ }): {
22
+ file: string;
23
+ line: number;
24
+ column: number;
25
+ method: string;
26
+ };
27
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Get all script URLs currently on the page.
3
+ * Used to detect newly loaded chunk scripts.
4
+ */ export declare const getScriptUrls: () => Set<string>;
5
+ /**
6
+ * Find the newly added script URL by comparing script sets.
7
+ * Returns the first new script URL found, or null if none.
8
+ */ export declare const findNewScriptUrl: (beforeUrls: Set<string>, afterUrls: Set<string>) => string | null;
9
+ /**
10
+ * Preload source map for a test file's chunk URL.
11
+ *
12
+ * Always fetches fresh source map to handle file changes during watch mode.
13
+ *
14
+ * @param chunkUrl - The full URL of the chunk JS file
15
+ */ export declare const preloadTestFileSourceMap: (chunkUrl: string) => Promise<void>;
16
+ /**
17
+ * Preload source map for the runner.js file.
18
+ *
19
+ * This is essential for inline snapshot support because the snapshot code
20
+ * runs in runner.js (which contains @rstest/core/internal/browser-runtime).
21
+ * Without this, stack traces from inline snapshots cannot be mapped back
22
+ * to the original source files.
23
+ */ export declare const preloadRunnerSourceMap: () => Promise<void>;
24
+ /**
25
+ * Stack frame interface matching @vitest/snapshot's format
26
+ */ interface StackFrame {
27
+ file: string;
28
+ line: number;
29
+ column: number;
30
+ method?: string;
31
+ }
32
+ /**
33
+ * Map a stack frame from bundled URL to original source file.
34
+ * This is used by BrowserSnapshotEnvironment.processStackTrace
35
+ */ export declare const mapStackFrame: (frame: StackFrame) => StackFrame;
36
+ export { };
@@ -1,81 +1,5 @@
1
- import { DISPATCH_RESPONSE_TYPE, DISPATCH_RPC_BRIDGE_NAME as protocol_DISPATCH_RPC_BRIDGE_NAME, DISPATCH_RPC_REQUEST_TYPE as protocol_DISPATCH_RPC_REQUEST_TYPE, DISPATCH_MESSAGE_TYPE as protocol_DISPATCH_MESSAGE_TYPE, DISPATCH_NAMESPACE_BROWSER } from "./33.js";
2
- const DEFAULT_RPC_TIMEOUT_MS = 30000;
3
- const getRpcTimeout = ()=>window.__RSTEST_BROWSER_OPTIONS__?.rpcTimeout ?? DEFAULT_RPC_TIMEOUT_MS;
4
- const pendingRequests = new Map();
5
- let requestIdCounter = 0;
6
- let messageListenerInitialized = false;
7
- const createRequestId = (prefix)=>{
8
- if ('function' == typeof globalThis.crypto?.randomUUID) return globalThis.crypto.randomUUID();
9
- requestIdCounter += 1;
10
- return `${prefix}-${Date.now().toString(36)}-${requestIdCounter.toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
11
- };
12
- const isDispatchResponse = (value)=>'object' == typeof value && null !== value && 'requestId' in value && 'string' == typeof value.requestId;
13
- const settlePendingRequest = (response)=>{
14
- const pending = pendingRequests.get(response.requestId);
15
- if (!pending) return;
16
- pendingRequests.delete(response.requestId);
17
- if (response.stale) return void pending.reject(new Error(pending.staleMessage));
18
- if (response.error) return void pending.reject(new Error(response.error));
19
- pending.resolve(response.result);
20
- };
21
- const initMessageListener = ()=>{
22
- if (messageListenerInitialized) return;
23
- messageListenerInitialized = true;
24
- window.addEventListener('message', (event)=>{
25
- if (event.data?.type === DISPATCH_RESPONSE_TYPE) settlePendingRequest(event.data.payload);
26
- });
27
- };
28
- const unwrapDispatchBridgeResult = (requestId, result, staleMessage)=>{
29
- if (!isDispatchResponse(result)) throw new Error('Invalid dispatch bridge response payload.');
30
- if (result.requestId !== requestId) throw new Error(`Mismatched dispatch response id: expected ${requestId}, got ${result.requestId}`);
31
- if (result.stale) throw new Error(staleMessage);
32
- if (result.error) throw new Error(result.error);
33
- return result.result;
34
- };
35
- const dispatchRpc = ({ requestId, request, timeoutMs, timeoutMessage, staleMessage })=>{
36
- if (window.parent === window) {
37
- const dispatchBridge = window[protocol_DISPATCH_RPC_BRIDGE_NAME];
38
- if (!dispatchBridge) throw new Error('Dispatch RPC bridge is not available in top-level runner.');
39
- return new Promise((resolve, reject)=>{
40
- const timeoutId = setTimeout(()=>{
41
- reject(new Error(timeoutMessage));
42
- }, timeoutMs);
43
- const call = Promise.resolve(dispatchBridge(request)).then((result)=>unwrapDispatchBridgeResult(requestId, result, staleMessage));
44
- call.then((result)=>{
45
- clearTimeout(timeoutId);
46
- resolve(result);
47
- }).catch((error)=>{
48
- clearTimeout(timeoutId);
49
- reject(error instanceof Error ? error : new Error(String(error)));
50
- });
51
- });
52
- }
53
- initMessageListener();
54
- return new Promise((resolve, reject)=>{
55
- const timeoutId = setTimeout(()=>{
56
- pendingRequests.delete(requestId);
57
- reject(new Error(timeoutMessage));
58
- }, timeoutMs);
59
- pendingRequests.set(requestId, {
60
- staleMessage,
61
- resolve: (value)=>{
62
- clearTimeout(timeoutId);
63
- resolve(value);
64
- },
65
- reject: (error)=>{
66
- clearTimeout(timeoutId);
67
- reject(error);
68
- }
69
- });
70
- window.parent.postMessage({
71
- type: protocol_DISPATCH_MESSAGE_TYPE,
72
- payload: {
73
- type: protocol_DISPATCH_RPC_REQUEST_TYPE,
74
- payload: request
75
- }
76
- }, '*');
77
- });
78
- };
1
+ import { registerElementExpect } from "@rstest/core/internal/browser-runtime";
2
+ import { createRequestId, DISPATCH_NAMESPACE_BROWSER, dispatchRpc, getRpcTimeout } from "./240~1.js";
79
3
  const getUrlSearchParam = (name)=>{
80
4
  try {
81
5
  const value = new URL(window.location.href).searchParams.get(name);
@@ -89,11 +13,6 @@ const getCurrentTestPath = ()=>{
89
13
  if (!testPath) throw new Error("Browser RPC requires testFile in __RSTEST_BROWSER_OPTIONS__. This usually indicates the runner iframe was not configured by the container or URL.");
90
14
  return testPath;
91
15
  };
92
- const getCurrentRunId = ()=>{
93
- const runId = window.__RSTEST_BROWSER_OPTIONS__?.runId ?? getUrlSearchParam('runId');
94
- if (!runId) throw new Error("Browser RPC requires runId in __RSTEST_BROWSER_OPTIONS__. This usually indicates the runner iframe URL/config is stale or incomplete.");
95
- return runId;
96
- };
97
16
  const createBrowserDispatchRequest = (requestId, request)=>({
98
17
  requestId,
99
18
  namespace: DISPATCH_NAMESPACE_BROWSER,
@@ -110,7 +29,6 @@ const callBrowserRpc = async (payload)=>{
110
29
  const request = {
111
30
  id,
112
31
  testPath: getCurrentTestPath(),
113
- runId: getCurrentRunId(),
114
32
  ...payload
115
33
  };
116
34
  const dispatchRequest = createBrowserDispatchRequest(id, request);
@@ -562,22 +480,5 @@ const api_element = (locator)=>{
562
480
  if (!isLocator(locator)) throw new TypeError('expect.element() expects a Locator returned from @rstest/browser page.getBy* APIs.');
563
481
  return createElementExpect(locator, false);
564
482
  };
565
- const markBrowserElement = ()=>{
566
- Object.defineProperty(api_element, '__rstestBrowser', {
567
- value: true,
568
- configurable: false,
569
- enumerable: false,
570
- writable: false
571
- });
572
- };
573
- const installExpectElement = ()=>{
574
- const api = globalThis.RSTEST_API;
575
- const target = api?.expect;
576
- if (!target) throw new Error('RSTEST_API.expect is not registered yet. This usually indicates @rstest/browser was imported too early.');
577
- if ('function' != typeof target.element || !target.element.__rstestBrowser) {
578
- markBrowserElement();
579
- target.element = api_element;
580
- }
581
- };
582
- installExpectElement();
483
+ registerElementExpect(api_element);
583
484
  export { Locator, page, setTestIdAttribute };
@@ -0,0 +1,225 @@
1
+ import type { BrowserViewport } from '@rstest/core/internal/browser';
2
+ import type { BrowserRuntimeConfig, TestFileResult, TestInfo, TestResult } from '@rstest/core/internal/browser-runtime';
3
+ import type { SnapshotUpdateState } from '@vitest/snapshot';
4
+ export type { BrowserLocatorIR, BrowserRpcRequest, SnapshotRpcCall, SnapshotRpcMethod, SnapshotRpcMethodArgs, SnapshotRpcRequest } from './rpcProtocol.js';
5
+ export { validateBrowserRpcRequest } from './rpcProtocol.js';
6
+ export declare const DISPATCH_MESSAGE_TYPE = '__rstest_dispatch__';
7
+ export declare const DISPATCH_RESPONSE_TYPE = '__rstest_dispatch_response__';
8
+ export declare const DISPATCH_RPC_BRIDGE_NAME = '__rstest_dispatch_rpc__';
9
+ export declare const DISPATCH_RPC_REQUEST_TYPE = 'dispatch-rpc-request';
10
+ export declare const RSTEST_CONFIG_MESSAGE_TYPE = 'RSTEST_CONFIG';
11
+ export declare const NO_RPC_TIMEOUT = -1;
12
+ export declare const DISPATCH_NAMESPACE_RUNNER = 'runner';
13
+ export declare const DISPATCH_NAMESPACE_BROWSER = 'browser';
14
+ export declare const DISPATCH_NAMESPACE_FILE_CLEANUP = 'file-cleanup';
15
+ export declare const DISPATCH_NAMESPACE_SNAPSHOT = 'snapshot';
16
+ export declare const DISPATCH_METHOD_RPC = 'rpc';
17
+ export type SerializedRuntimeConfig = BrowserRuntimeConfig;
18
+ // `BrowserViewport` is a core config type (`@rstest/core` owns the canonical
19
+ // definition used by `NormalizedBrowserModeConfig`). Re-export it so the host
20
+ // assigns the SAME type across the seam instead of a hand-copied duplicate.
21
+ export type { BrowserViewport };
22
+ export type BrowserProjectRuntime = {
23
+ name: string;
24
+ environmentName: string;
25
+ projectRoot: string;
26
+ runtimeConfig: SerializedRuntimeConfig;
27
+ viewport?: BrowserViewport;
28
+ };
29
+ /**
30
+ * Test file info with associated project name.
31
+ * Used to track which project a test file belongs to.
32
+ */ export type TestFileInfo = {
33
+ testPath: string;
34
+ projectName: string;
35
+ };
36
+ export type FileCleanupDispatchMethod = 'start' | 'end';
37
+ export type FileCleanupDispatchPayload = {
38
+ projectName: string;
39
+ result?: TestFileResult;
40
+ runId?: string;
41
+ testPath: string;
42
+ };
43
+ /**
44
+ * The committed test-file set plus its monotonic version. The version is what
45
+ * the container acks once every iframe for that set exists in the DOM, so both
46
+ * halves must travel together — a set without its version cannot be acked.
47
+ */ export type VersionedTestFileSet = {
48
+ files: TestFileInfo[];
49
+ version: number;
50
+ };
51
+ /**
52
+ * Execution mode for browser tests.
53
+ * - 'run': Execute tests and report results (default)
54
+ * - 'collect': Only collect test metadata without running
55
+ */ export type BrowserExecutionMode = 'run' | 'collect';
56
+ /**
57
+ * Wire shape of a `log` client message payload. The host receives this and maps
58
+ * it onto core's {@link UserConsoleLog} (notably `level` → `name`); that mapper
59
+ * (`hostController.ts` `handleLog`) annotates its result as `UserConsoleLog`, so
60
+ * the map→core direction is compiler-checked. Owning the wire shape here as one
61
+ * named type keeps the host's input type from drifting away from the producer.
62
+ */ export type BrowserLogPayload = {
63
+ level: 'log' | 'warn' | 'error' | 'info' | 'debug';
64
+ content: string;
65
+ /**
66
+ * Owning project, resolved by the client from its manifest. The host must
67
+ * not re-derive it from `testPath` — concurrent projects can run the same
68
+ * file, so a path-keyed lookup can attribute the log to the wrong project.
69
+ */ projectName: string;
70
+ taskId?: string;
71
+ taskName?: string;
72
+ taskParentNames?: string[];
73
+ taskType?: 'file' | 'suite' | 'case';
74
+ testPath: string;
75
+ type: 'stdout' | 'stderr';
76
+ trace?: string;
77
+ };
78
+ export type BrowserHostConfig = {
79
+ rootPath: string;
80
+ projects: BrowserProjectRuntime[];
81
+ snapshot: {
82
+ updateSnapshot: SnapshotUpdateState;
83
+ };
84
+ testFile?: string;
85
+ /**
86
+ * The run identity this document executes under — the runner's SOLE identity
87
+ * source, adopted once at boot and stamped on every outbound message. Headed:
88
+ * the container confers its frame's current lease over the config handshake
89
+ * (so even an HMR full reload boots with the run the host is awaiting NOW).
90
+ * Headless: injected host-side as `${run.token}:${session.id}`. Never
91
+ * derivable from the URL.
92
+ */ runId?: string;
93
+ /**
94
+ * Base URL for runner (iframe) pages. Container origin; used as a fallback
95
+ * when a project has no entry in `projectRunnerUrls`.
96
+ */ runnerUrl?: string;
97
+ /**
98
+ * Per-project runner origin base URLs, keyed by project name. Each browser
99
+ * project runs on its own dev server, so the container must load each test
100
+ * file's iframe from that project's own origin.
101
+ */ projectRunnerUrls?: Record<string, string>;
102
+ /**
103
+ * WebSocket port for container RPC.
104
+ */ wsPort?: number;
105
+ /**
106
+ * Execution mode. Defaults to 'run'.
107
+ */ mode?: BrowserExecutionMode;
108
+ /**
109
+ * Debug mode. When true, enables verbose logging in browser.
110
+ */ debug?: boolean;
111
+ /** Timeout for RPC operations in milliseconds; negative values disable it. */ rpcTimeout?: number;
112
+ };
113
+ export declare const RSTEST_BROWSER_CACHE_CLEANERS_KEY = '@rstest/browser/cache-cleaners';
114
+ /**
115
+ * The wrapper every runner document posts over `postMessage` / the headless
116
+ * bridge. Identity rides BESIDE the payload, never inside it: `message`
117
+ * payloads reach core reporting (`RunnerEventSink`, `BlobReporter`) as-is, so a
118
+ * transport identity spread into them would leak a nondeterministic UUID into
119
+ * reporter output. `runId` is the identity the document was granted at boot
120
+ * (headed: the container's config handshake; headless: the injected inline
121
+ * options) — never re-derived from the URL or the frame. Absent only for a
122
+ * document that was never granted one; the headed host drops such messages.
123
+ */ export type RunnerEnvelope = {
124
+ runId?: string;
125
+ message: BrowserClientMessage;
126
+ };
127
+ export type BrowserClientMessage = {
128
+ type: 'ready';
129
+ } | {
130
+ type: 'file-start';
131
+ payload: {
132
+ testPath: string;
133
+ projectName: string;
134
+ };
135
+ } | {
136
+ type: 'case-result';
137
+ payload: TestResult;
138
+ } | {
139
+ type: 'file-complete';
140
+ payload: TestFileResult;
141
+ } | {
142
+ type: 'log';
143
+ payload: BrowserLogPayload;
144
+ } | {
145
+ type: 'fatal';
146
+ payload: {
147
+ message: string;
148
+ stack?: string;
149
+ };
150
+ } | {
151
+ type: 'complete';
152
+ } | {
153
+ type: 'collect-result';
154
+ payload: {
155
+ testPath: string;
156
+ project: string;
157
+ tests: TestInfo[];
158
+ };
159
+ } | {
160
+ type: 'collect-complete';
161
+ } | {
162
+ type: typeof DISPATCH_RPC_REQUEST_TYPE;
163
+ payload: BrowserDispatchRequest;
164
+ };
165
+ /**
166
+ * Lifecycle methods the runner emits via `dispatchRunnerLifecycle()` as
167
+ * dispatch-rpc-requests on the `runner` namespace (as opposed to the
168
+ * {@link BrowserClientMessage} types it `send()`s). The runner client imports
169
+ * this instead of redeclaring the list, so the emit site cannot drift from the
170
+ * host router.
171
+ */ export type RunnerLifecycleMethod = 'file-ready' | 'suite-start' | 'suite-result' | 'case-start';
172
+ /**
173
+ * {@link BrowserClientMessage} types that are forwarded to the `runner`
174
+ * namespace (by message `type`) rather than handled at the transport layer.
175
+ * `Extract` keeps this a checked subset of the message union. `ready` and
176
+ * `complete` are deliberately absent: they still arrive, but their only job
177
+ * is done at the dispatch gate and the router ignores them silently.
178
+ */ type RunnerMessageMethod = Extract<BrowserClientMessage['type'], 'file-start' | 'case-result' | 'file-complete' | 'log' | 'fatal'>;
179
+ /**
180
+ * Single source of truth for every method handled by the `runner` dispatch
181
+ * namespace. The host handler table is keyed by this union (a missing key is a
182
+ * compile error), so adding a runner method here forces a matching handler and
183
+ * cannot silently no-op at runtime.
184
+ */ export type RunnerDispatchMethod = RunnerLifecycleMethod | RunnerMessageMethod;
185
+ /**
186
+ * Transport-agnostic envelope used by host routing.
187
+ * `namespace + method + args + target` describes an operation independent of
188
+ * the underlying message channel, and `runToken` provides run-level isolation.
189
+ */ export type BrowserDispatchRequest = {
190
+ requestId: string;
191
+ // Optional so headed/container paths can adopt the same envelope even when
192
+ // run-token isolation is only enforced in headless scheduling today.
193
+ runToken?: number;
194
+ /**
195
+ * Headed run identity, stamped by the runner's dispatch transport from the
196
+ * identity its document adopted at boot. The headed host accepts a request
197
+ * iff this names a live run; headless keeps using the host-injected
198
+ * `runToken` instead (`runToken` is the CYCLE generation, `runId` is the
199
+ * RUN identity — they are not interchangeable).
200
+ */ runId?: string;
201
+ namespace: string;
202
+ method: string;
203
+ args?: unknown;
204
+ // Routing reads `namespace`, `method`, `runToken`, and `target.sessionId`
205
+ // (see dispatchRouter.ts / dispatchBrowserRpcRequest). `target.testFile` and
206
+ // `target.projectName` are carried for diagnostics / forward-compatibility and
207
+ // are NOT consulted for routing today — adding a routing-relevant field here
208
+ // means wiring a reader on the host side, which structural typing won't force.
209
+ target?: {
210
+ testFile?: string;
211
+ sessionId?: string;
212
+ projectName?: string;
213
+ };
214
+ };
215
+ /**
216
+ * Dispatch response envelope.
217
+ * `stale: true` signals a safe drop from an older run generation, not a failure.
218
+ */ export type BrowserDispatchResponse = {
219
+ requestId: string;
220
+ runToken?: number;
221
+ result?: unknown;
222
+ error?: string;
223
+ stale?: boolean;
224
+ };
225
+ export type BrowserDispatchHandler = (request: BrowserDispatchRequest) => Promise<unknown>;
@@ -0,0 +1,138 @@
1
+ export type BrowserLocatorText = {
2
+ type: 'string';
3
+ value: string;
4
+ } | {
5
+ type: 'regexp';
6
+ source: string;
7
+ flags?: string;
8
+ };
9
+ export type BrowserLocatorStep = {
10
+ type: 'getByRole';
11
+ role: string;
12
+ options?: {
13
+ name?: BrowserLocatorText;
14
+ exact?: boolean;
15
+ checked?: boolean;
16
+ disabled?: boolean;
17
+ expanded?: boolean;
18
+ selected?: boolean;
19
+ pressed?: boolean;
20
+ includeHidden?: boolean;
21
+ level?: number;
22
+ };
23
+ } | {
24
+ type: 'locator';
25
+ selector: string;
26
+ } | {
27
+ type: 'getByText';
28
+ text: BrowserLocatorText;
29
+ options?: {
30
+ exact?: boolean;
31
+ };
32
+ } | {
33
+ type: 'getByLabel';
34
+ text: BrowserLocatorText;
35
+ options?: {
36
+ exact?: boolean;
37
+ };
38
+ } | {
39
+ type: 'getByPlaceholder';
40
+ text: BrowserLocatorText;
41
+ options?: {
42
+ exact?: boolean;
43
+ };
44
+ } | {
45
+ type: 'getByAltText';
46
+ text: BrowserLocatorText;
47
+ options?: {
48
+ exact?: boolean;
49
+ };
50
+ } | {
51
+ type: 'getByTitle';
52
+ text: BrowserLocatorText;
53
+ options?: {
54
+ exact?: boolean;
55
+ };
56
+ } | {
57
+ type: 'getByTestId';
58
+ text: BrowserLocatorText;
59
+ } | {
60
+ type: 'filter';
61
+ options: {
62
+ hasText?: BrowserLocatorText;
63
+ hasNotText?: BrowserLocatorText;
64
+ has?: BrowserLocatorIR;
65
+ hasNot?: BrowserLocatorIR;
66
+ };
67
+ } | {
68
+ type: 'and';
69
+ locator: BrowserLocatorIR;
70
+ } | {
71
+ type: 'or';
72
+ locator: BrowserLocatorIR;
73
+ } | {
74
+ type: 'nth';
75
+ index: number;
76
+ } | {
77
+ type: 'first';
78
+ } | {
79
+ type: 'last';
80
+ };
81
+ export type BrowserLocatorIR = {
82
+ steps: BrowserLocatorStep[];
83
+ };
84
+ export type BrowserRpcRequest = {
85
+ id: string;
86
+ /** Absolute test file path for locating runner iframe */ testPath: string;
87
+ kind: 'locator' | 'expect' | 'config';
88
+ locator: BrowserLocatorIR;
89
+ method: string;
90
+ args: unknown[];
91
+ /**
92
+ * Negation for expect matchers (equivalent to Playwright expect(...).not).
93
+ * Only meaningful for kind === 'expect'.
94
+ */ isNot?: boolean;
95
+ /** Optional assertion timeout override (ms). Falls back to the host-provided assertion timeout. */ timeout?: number;
96
+ };
97
+ export declare const validateBrowserRpcRequest: (payload: unknown) => BrowserRpcRequest;
98
+ /**
99
+ * Single source of truth for snapshot RPC methods and their argument shapes.
100
+ * The request union, the client sender, both host-side mappers, and every
101
+ * exhaustiveness check derive from this map — adding or renaming a method here
102
+ * forces a compile error at every site that has not been updated.
103
+ */ export type SnapshotRpcMethodArgs = {
104
+ resolveSnapshotPath: {
105
+ testPath: string;
106
+ };
107
+ readSnapshotFile: {
108
+ filepath: string;
109
+ };
110
+ saveSnapshotFile: {
111
+ filepath: string;
112
+ content: string;
113
+ };
114
+ removeSnapshotFile: {
115
+ filepath: string;
116
+ };
117
+ };
118
+ export type SnapshotRpcMethod = keyof SnapshotRpcMethodArgs;
119
+ /**
120
+ * Snapshot RPC request from runner iframe.
121
+ * The container will forward these to the host via WebSocket RPC.
122
+ */ export type SnapshotRpcRequest = {
123
+ [M in SnapshotRpcMethod]: {
124
+ id: string;
125
+ method: M;
126
+ args: SnapshotRpcMethodArgs[M];
127
+ };
128
+ }[SnapshotRpcMethod];
129
+ /**
130
+ * Client-side snapshot RPC call (request without the transport `id`).
131
+ * Distributive over {@link SnapshotRpcMethod} so `method` and `args` stay paired
132
+ * at the call site — a mismatched pair fails to compile.
133
+ */ export type SnapshotRpcCall = {
134
+ [M in SnapshotRpcMethod]: {
135
+ method: M;
136
+ args: SnapshotRpcMethodArgs[M];
137
+ };
138
+ }[SnapshotRpcMethod];