@rstest/browser 0.11.6 → 0.11.8
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/dist/browser-container/container-static/css/index.43daaf6c93.css +1 -0
- package/dist/browser-container/container-static/js/260.3739fe815c.js +194 -0
- package/dist/browser-container/container-static/js/index.c0f87bfb3e.js +1 -0
- package/dist/browser-container/index.html +1 -1
- package/dist/browserRsbuild.d.ts +24 -4
- package/dist/browserV8Coverage.d.ts +15 -0
- package/dist/client/240~1.js +150 -0
- package/dist/{augmentExpect.d.ts → client/client/augmentExpect.d.ts} +1 -1
- package/dist/client/{browserRpc.d.ts → client/browserRpc.d.ts} +1 -1
- package/dist/client/{dispatchTransport.d.ts → client/dispatchTransport.d.ts} +8 -4
- package/dist/client/client/index.d.ts +2 -0
- package/dist/client/client/runIdentity.d.ts +12 -0
- package/dist/client/client/runner.d.ts +4 -0
- package/dist/client/client/snapshot.d.ts +27 -0
- package/dist/client/client/sourceMapSupport.d.ts +36 -0
- package/dist/{browser.js → client/index.js} +3 -102
- package/dist/client/protocol.d.ts +225 -0
- package/dist/client/rpcProtocol.d.ts +138 -0
- package/dist/client/runner.js +675 -0
- package/dist/client/sourceMap/sourceMapLoader.d.ts +25 -0
- package/dist/containerRpc.d.ts +27 -19
- package/dist/headedRunRegistry.d.ts +51 -0
- package/dist/headedScheduler.d.ts +10 -10
- package/dist/headlessScheduler.d.ts +5 -4
- package/dist/headlessTransport.d.ts +4 -2
- package/dist/hostPayloads.d.ts +1 -7
- package/dist/index.js +840 -330
- package/dist/protocol.d.ts +48 -8
- package/dist/providers/index.d.ts +21 -0
- package/dist/providers/playwright/v8Coverage.d.ts +2 -0
- package/dist/rpcProtocol.d.ts +1 -2
- package/dist/schedulerSeam.d.ts +5 -0
- package/dist/sourceMap/sourceMapLoader.d.ts +12 -1
- package/dist/watchRerunPlanner.d.ts +20 -9
- package/dist/watchSignals.d.ts +8 -1
- package/package.json +11 -11
- package/dist/33.js +0 -7
- package/dist/browser-container/container-static/css/index.0521ff95b1.css +0 -1
- package/dist/browser-container/container-static/js/683.4f821ce8f1.js +0 -194
- package/dist/browser-container/container-static/js/index.568aa7cb35.js +0 -1
- package/dist/browser.d.ts +0 -2
- package/src/augmentExpect.ts +0 -62
- package/src/browser.ts +0 -3
- package/src/browserExecutor.ts +0 -222
- package/src/browserRpcRegistry.ts +0 -57
- package/src/browserRsbuild.ts +0 -1927
- package/src/client/api.ts +0 -213
- package/src/client/browserRpc.ts +0 -86
- package/src/client/dispatchTransport.ts +0 -240
- package/src/client/entry.ts +0 -807
- package/src/client/locator.ts +0 -448
- package/src/client/snapshot.ts +0 -113
- package/src/client/sourceMapSupport.ts +0 -159
- package/src/concurrency.ts +0 -65
- package/src/configValidation.ts +0 -263
- package/src/containerRpc.ts +0 -206
- package/src/dispatchCapabilities.ts +0 -177
- package/src/dispatchRouter.ts +0 -82
- package/src/env.d.ts +0 -54
- package/src/headedScheduler.ts +0 -664
- package/src/headedSerialTaskQueue.ts +0 -23
- package/src/headlessScheduler.ts +0 -566
- package/src/headlessTransport.ts +0 -28
- package/src/hostController.ts +0 -1365
- package/src/hostPayloads.ts +0 -62
- package/src/index.ts +0 -51
- package/src/manifest.d.ts +0 -41
- package/src/protocol.ts +0 -225
- package/src/providers/index.ts +0 -126
- package/src/providers/playwright/compileLocator.ts +0 -130
- package/src/providers/playwright/dispatchBrowserRpc.ts +0 -372
- package/src/providers/playwright/expectUtils.ts +0 -57
- package/src/providers/playwright/implementation.ts +0 -35
- package/src/providers/playwright/index.ts +0 -1
- package/src/providers/playwright/runtime.ts +0 -159
- package/src/providers/playwright/textMatcher.ts +0 -10
- package/src/rpcProtocol.ts +0 -218
- package/src/runSession.ts +0 -110
- package/src/schedulerSeam.ts +0 -49
- package/src/sessionRegistry.ts +0 -89
- package/src/sourceMap/sourceMapLoader.ts +0 -96
- package/src/viewportPresets.ts +0 -69
- package/src/watchRerunPlanner.ts +0 -88
- package/src/watchRuntime.ts +0 -83
- package/src/watchSignals.ts +0 -93
- /package/dist/{rslib-runtime.js → 612~0.js} +0 -0
- /package/dist/client/{api.d.ts → client/api.d.ts} +0 -0
- /package/dist/client/{locator.d.ts → client/locator.d.ts} +0 -0
package/src/client/api.ts
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import type { BrowserElementExpect } from '../augmentExpect';
|
|
2
|
-
import type { BrowserLocatorText, BrowserRpcRequest } from '../rpcProtocol';
|
|
3
|
-
import { callBrowserRpc } from './browserRpc';
|
|
4
|
-
import {
|
|
5
|
-
isLocator,
|
|
6
|
-
Locator,
|
|
7
|
-
page,
|
|
8
|
-
serializeText,
|
|
9
|
-
setTestIdAttribute,
|
|
10
|
-
} from './locator';
|
|
11
|
-
|
|
12
|
-
const serializeMatcherText = (value: string | RegExp): BrowserLocatorText => {
|
|
13
|
-
return serializeText(value);
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const createElementExpect = (
|
|
17
|
-
locator: Locator,
|
|
18
|
-
isNot: boolean,
|
|
19
|
-
): BrowserElementExpect => {
|
|
20
|
-
const callExpect = async (
|
|
21
|
-
method: string,
|
|
22
|
-
args: unknown[],
|
|
23
|
-
timeout?: number,
|
|
24
|
-
): Promise<void> => {
|
|
25
|
-
await callBrowserRpc<void>({
|
|
26
|
-
kind: 'expect',
|
|
27
|
-
locator: locator.ir,
|
|
28
|
-
method,
|
|
29
|
-
args,
|
|
30
|
-
isNot,
|
|
31
|
-
timeout,
|
|
32
|
-
} satisfies Omit<BrowserRpcRequest, 'id' | 'testPath' | 'runId'>);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const api: Omit<BrowserElementExpect, 'not'> = {
|
|
36
|
-
async toBeVisible(options) {
|
|
37
|
-
await callExpect('toBeVisible', [], options?.timeout);
|
|
38
|
-
},
|
|
39
|
-
async toBeHidden(options) {
|
|
40
|
-
await callExpect('toBeHidden', [], options?.timeout);
|
|
41
|
-
},
|
|
42
|
-
async toBeEnabled(options) {
|
|
43
|
-
await callExpect('toBeEnabled', [], options?.timeout);
|
|
44
|
-
},
|
|
45
|
-
async toBeDisabled(options) {
|
|
46
|
-
await callExpect('toBeDisabled', [], options?.timeout);
|
|
47
|
-
},
|
|
48
|
-
async toBeChecked(options) {
|
|
49
|
-
await callExpect('toBeChecked', [], options?.timeout);
|
|
50
|
-
},
|
|
51
|
-
async toBeUnchecked(options) {
|
|
52
|
-
await callExpect('toBeUnchecked', [], options?.timeout);
|
|
53
|
-
},
|
|
54
|
-
async toBeAttached(options) {
|
|
55
|
-
await callExpect('toBeAttached', [], options?.timeout);
|
|
56
|
-
},
|
|
57
|
-
async toBeDetached(options) {
|
|
58
|
-
await callExpect('toBeDetached', [], options?.timeout);
|
|
59
|
-
},
|
|
60
|
-
async toBeEditable(options) {
|
|
61
|
-
await callExpect('toBeEditable', [], options?.timeout);
|
|
62
|
-
},
|
|
63
|
-
async toBeFocused(options) {
|
|
64
|
-
await callExpect('toBeFocused', [], options?.timeout);
|
|
65
|
-
},
|
|
66
|
-
async toBeEmpty(options) {
|
|
67
|
-
await callExpect('toBeEmpty', [], options?.timeout);
|
|
68
|
-
},
|
|
69
|
-
async toBeInViewport(options) {
|
|
70
|
-
const ratio = options?.ratio;
|
|
71
|
-
await callExpect(
|
|
72
|
-
'toBeInViewport',
|
|
73
|
-
ratio === undefined ? [] : [ratio],
|
|
74
|
-
options?.timeout,
|
|
75
|
-
);
|
|
76
|
-
},
|
|
77
|
-
async toHaveText(text, options) {
|
|
78
|
-
await callExpect(
|
|
79
|
-
'toHaveText',
|
|
80
|
-
[serializeMatcherText(text)],
|
|
81
|
-
options?.timeout,
|
|
82
|
-
);
|
|
83
|
-
},
|
|
84
|
-
async toContainText(text, options) {
|
|
85
|
-
await callExpect(
|
|
86
|
-
'toContainText',
|
|
87
|
-
[serializeMatcherText(text)],
|
|
88
|
-
options?.timeout,
|
|
89
|
-
);
|
|
90
|
-
},
|
|
91
|
-
async toHaveValue(value, options) {
|
|
92
|
-
await callExpect(
|
|
93
|
-
'toHaveValue',
|
|
94
|
-
[serializeMatcherText(value)],
|
|
95
|
-
options?.timeout,
|
|
96
|
-
);
|
|
97
|
-
},
|
|
98
|
-
async toHaveId(value, options) {
|
|
99
|
-
await callExpect(
|
|
100
|
-
'toHaveId',
|
|
101
|
-
[serializeMatcherText(value)],
|
|
102
|
-
options?.timeout,
|
|
103
|
-
);
|
|
104
|
-
},
|
|
105
|
-
async toHaveAttribute(name, value, options) {
|
|
106
|
-
const args =
|
|
107
|
-
value === undefined ? [name] : [name, serializeMatcherText(value)];
|
|
108
|
-
await callExpect('toHaveAttribute', args, options?.timeout);
|
|
109
|
-
},
|
|
110
|
-
async toHaveClass(value, options) {
|
|
111
|
-
await callExpect(
|
|
112
|
-
'toHaveClass',
|
|
113
|
-
[serializeMatcherText(value)],
|
|
114
|
-
options?.timeout,
|
|
115
|
-
);
|
|
116
|
-
},
|
|
117
|
-
async toHaveCount(count, options) {
|
|
118
|
-
await callExpect('toHaveCount', [count], options?.timeout);
|
|
119
|
-
},
|
|
120
|
-
async toHaveCSS(name, value, options) {
|
|
121
|
-
if (typeof name !== 'string' || !name) {
|
|
122
|
-
throw new TypeError('toHaveCSS expects a non-empty CSS property name');
|
|
123
|
-
}
|
|
124
|
-
await callExpect(
|
|
125
|
-
'toHaveCSS',
|
|
126
|
-
[name, serializeMatcherText(value)],
|
|
127
|
-
options?.timeout,
|
|
128
|
-
);
|
|
129
|
-
},
|
|
130
|
-
async toHaveJSProperty(name, value, options) {
|
|
131
|
-
if (typeof name !== 'string' || !name) {
|
|
132
|
-
throw new TypeError(
|
|
133
|
-
'toHaveJSProperty expects a non-empty property name',
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
await callExpect('toHaveJSProperty', [name, value], options?.timeout);
|
|
137
|
-
},
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
const withNot = api as BrowserElementExpect;
|
|
141
|
-
Object.defineProperty(withNot, 'not', {
|
|
142
|
-
configurable: false,
|
|
143
|
-
enumerable: false,
|
|
144
|
-
get() {
|
|
145
|
-
return createElementExpect(locator, !isNot);
|
|
146
|
-
},
|
|
147
|
-
});
|
|
148
|
-
return withNot;
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
const element = (locator: unknown): BrowserElementExpect => {
|
|
152
|
-
if (!isLocator(locator)) {
|
|
153
|
-
throw new TypeError(
|
|
154
|
-
'expect.element() expects a Locator returned from @rstest/browser page.getBy* APIs.',
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return createElementExpect(locator, false);
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
const markBrowserElement = (): void => {
|
|
162
|
-
Object.defineProperty(element, '__rstestBrowser', {
|
|
163
|
-
value: true,
|
|
164
|
-
configurable: false,
|
|
165
|
-
enumerable: false,
|
|
166
|
-
writable: false,
|
|
167
|
-
});
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
const installExpectElement = (): void => {
|
|
171
|
-
// In browser runtime, `@rstest/core` exports are proxies that forward property
|
|
172
|
-
// access to `globalThis.RSTEST_API`. Patch the underlying expect implementation.
|
|
173
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
174
|
-
const api = (globalThis as any).RSTEST_API as any;
|
|
175
|
-
const target = api?.expect;
|
|
176
|
-
if (!target) {
|
|
177
|
-
throw new Error(
|
|
178
|
-
'RSTEST_API.expect is not registered yet. This usually indicates @rstest/browser was imported too early.',
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (typeof target.element !== 'function' || !target.element.__rstestBrowser) {
|
|
183
|
-
markBrowserElement();
|
|
184
|
-
target.element = element;
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
installExpectElement();
|
|
189
|
-
|
|
190
|
-
export type {
|
|
191
|
-
BrowserPage,
|
|
192
|
-
BrowserSerializable,
|
|
193
|
-
LocatorBlurOptions,
|
|
194
|
-
LocatorCheckOptions,
|
|
195
|
-
LocatorClickOptions,
|
|
196
|
-
LocatorDblclickOptions,
|
|
197
|
-
LocatorDispatchEventInit,
|
|
198
|
-
LocatorFillOptions,
|
|
199
|
-
LocatorFilterOptions,
|
|
200
|
-
LocatorFocusOptions,
|
|
201
|
-
LocatorGetByRoleOptions,
|
|
202
|
-
LocatorHoverOptions,
|
|
203
|
-
LocatorKeyboardModifier,
|
|
204
|
-
LocatorMouseButton,
|
|
205
|
-
LocatorPosition,
|
|
206
|
-
LocatorPressOptions,
|
|
207
|
-
LocatorScrollIntoViewIfNeededOptions,
|
|
208
|
-
LocatorSelectOptionOptions,
|
|
209
|
-
LocatorSetInputFilesOptions,
|
|
210
|
-
LocatorTextOptions,
|
|
211
|
-
LocatorWaitForOptions,
|
|
212
|
-
} from './locator';
|
|
213
|
-
export { Locator, page, setTestIdAttribute };
|
package/src/client/browserRpc.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import type { BrowserDispatchRequest } from '../protocol';
|
|
2
|
-
import { DISPATCH_METHOD_RPC, DISPATCH_NAMESPACE_BROWSER } from '../protocol';
|
|
3
|
-
import type { BrowserRpcRequest } from '../rpcProtocol';
|
|
4
|
-
import {
|
|
5
|
-
createRequestId,
|
|
6
|
-
dispatchRpc,
|
|
7
|
-
getRpcTimeout,
|
|
8
|
-
} from './dispatchTransport';
|
|
9
|
-
|
|
10
|
-
const getUrlSearchParam = (name: string): string | undefined => {
|
|
11
|
-
try {
|
|
12
|
-
const value = new URL(window.location.href).searchParams.get(name);
|
|
13
|
-
return value ?? undefined;
|
|
14
|
-
} catch {
|
|
15
|
-
return undefined;
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const getCurrentTestPath = (): string => {
|
|
20
|
-
const testPath =
|
|
21
|
-
window.__RSTEST_BROWSER_OPTIONS__?.testFile ??
|
|
22
|
-
getUrlSearchParam('testFile');
|
|
23
|
-
if (!testPath) {
|
|
24
|
-
throw new Error(
|
|
25
|
-
'Browser RPC requires testFile in __RSTEST_BROWSER_OPTIONS__. ' +
|
|
26
|
-
'This usually indicates the runner iframe was not configured by the container or URL.',
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
return testPath;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const getCurrentRunId = (): string => {
|
|
33
|
-
const runId =
|
|
34
|
-
window.__RSTEST_BROWSER_OPTIONS__?.runId ?? getUrlSearchParam('runId');
|
|
35
|
-
if (!runId) {
|
|
36
|
-
throw new Error(
|
|
37
|
-
'Browser RPC requires runId in __RSTEST_BROWSER_OPTIONS__. ' +
|
|
38
|
-
'This usually indicates the runner iframe URL/config is stale or incomplete.',
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
return runId;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const createBrowserDispatchRequest = (
|
|
45
|
-
requestId: string,
|
|
46
|
-
request: BrowserRpcRequest,
|
|
47
|
-
): BrowserDispatchRequest => {
|
|
48
|
-
return {
|
|
49
|
-
requestId,
|
|
50
|
-
namespace: DISPATCH_NAMESPACE_BROWSER,
|
|
51
|
-
method: DISPATCH_METHOD_RPC,
|
|
52
|
-
args: request,
|
|
53
|
-
target: {
|
|
54
|
-
testFile: request.testPath,
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
export const callBrowserRpc = async <T>(
|
|
60
|
-
payload: Omit<BrowserRpcRequest, 'id' | 'testPath' | 'runId'>,
|
|
61
|
-
): Promise<T> => {
|
|
62
|
-
if (
|
|
63
|
-
payload.kind === 'config' &&
|
|
64
|
-
window.__RSTEST_BROWSER_OPTIONS__?.mode === 'collect'
|
|
65
|
-
) {
|
|
66
|
-
return undefined as T;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const id = createRequestId('browser-rpc');
|
|
70
|
-
const rpcTimeout = getRpcTimeout();
|
|
71
|
-
const request: BrowserRpcRequest = {
|
|
72
|
-
id,
|
|
73
|
-
testPath: getCurrentTestPath(),
|
|
74
|
-
runId: getCurrentRunId(),
|
|
75
|
-
...payload,
|
|
76
|
-
};
|
|
77
|
-
const dispatchRequest = createBrowserDispatchRequest(id, request);
|
|
78
|
-
|
|
79
|
-
return dispatchRpc<T>({
|
|
80
|
-
requestId: id,
|
|
81
|
-
request: dispatchRequest,
|
|
82
|
-
timeoutMs: rpcTimeout,
|
|
83
|
-
staleMessage: 'Stale browser RPC request ignored.',
|
|
84
|
-
timeoutMessage: `Browser RPC timeout after ${rpcTimeout / 1000}s: ${request.kind}.${request.method}`,
|
|
85
|
-
});
|
|
86
|
-
};
|
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
BrowserDispatchRequest,
|
|
3
|
-
BrowserDispatchResponse,
|
|
4
|
-
} from '../protocol';
|
|
5
|
-
import {
|
|
6
|
-
DISPATCH_MESSAGE_TYPE,
|
|
7
|
-
DISPATCH_NAMESPACE_RUNNER,
|
|
8
|
-
DISPATCH_RESPONSE_TYPE,
|
|
9
|
-
DISPATCH_RPC_BRIDGE_NAME,
|
|
10
|
-
DISPATCH_RPC_REQUEST_TYPE,
|
|
11
|
-
} from '../protocol';
|
|
12
|
-
|
|
13
|
-
// Coincidentally equal to the host-side RUNNER_FRAMES_READY_TIMEOUT_MS and the
|
|
14
|
-
// runner's CONFIG_WAIT_TIMEOUT_MS (entry.ts), but a semantically distinct
|
|
15
|
-
// default in a different runtime, so deliberately not shared with them.
|
|
16
|
-
const DEFAULT_RPC_TIMEOUT_MS = 30_000;
|
|
17
|
-
|
|
18
|
-
export const getRpcTimeout = (): number => {
|
|
19
|
-
return (
|
|
20
|
-
window.__RSTEST_BROWSER_OPTIONS__?.rpcTimeout ?? DEFAULT_RPC_TIMEOUT_MS
|
|
21
|
-
);
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const pendingRequests = new Map<
|
|
25
|
-
string,
|
|
26
|
-
{
|
|
27
|
-
resolve: (value: unknown) => void;
|
|
28
|
-
reject: (error: Error) => void;
|
|
29
|
-
staleMessage: string;
|
|
30
|
-
}
|
|
31
|
-
>();
|
|
32
|
-
|
|
33
|
-
let requestIdCounter = 0;
|
|
34
|
-
let messageListenerInitialized = false;
|
|
35
|
-
|
|
36
|
-
export const createRequestId = (prefix: string): string => {
|
|
37
|
-
if (typeof globalThis.crypto?.randomUUID === 'function') {
|
|
38
|
-
return globalThis.crypto.randomUUID();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
requestIdCounter += 1;
|
|
42
|
-
return `${prefix}-${Date.now().toString(36)}-${requestIdCounter.toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Build a runner-lifecycle dispatch request.
|
|
47
|
-
*
|
|
48
|
-
* Lifecycle events (`file-ready`, `suite-start`, `suite-result`, `case-start`)
|
|
49
|
-
* share the dispatch-rpc envelope but are delivered fire-and-forget via
|
|
50
|
-
* {@link sendRunnerLifecycle}, so the request id only needs to be unique — it is
|
|
51
|
-
* produced by the shared {@link createRequestId} factory rather than a bespoke
|
|
52
|
-
* per-runner counter.
|
|
53
|
-
*/
|
|
54
|
-
export const createRunnerLifecycleRequest = (
|
|
55
|
-
method: string,
|
|
56
|
-
args: unknown,
|
|
57
|
-
): BrowserDispatchRequest => ({
|
|
58
|
-
requestId: createRequestId('runner-lifecycle'),
|
|
59
|
-
namespace: DISPATCH_NAMESPACE_RUNNER,
|
|
60
|
-
method,
|
|
61
|
-
args,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Deliver a runner-lifecycle request fire-and-forget.
|
|
66
|
-
*
|
|
67
|
-
* Unlike {@link dispatchRpc}, this never awaits, unwraps, id-matches, or times
|
|
68
|
-
* out: the host echoes a response but the runner ignores it. Failures surface
|
|
69
|
-
* only through the optional `onError` hook (debug logging at the call site),
|
|
70
|
-
* keeping the hot test loop non-blocking.
|
|
71
|
-
*/
|
|
72
|
-
export const sendRunnerLifecycle = (
|
|
73
|
-
request: BrowserDispatchRequest,
|
|
74
|
-
onError?: (error: unknown) => void,
|
|
75
|
-
): void => {
|
|
76
|
-
if (window.parent === window) {
|
|
77
|
-
const dispatchBridge = window[DISPATCH_RPC_BRIDGE_NAME];
|
|
78
|
-
if (!dispatchBridge) {
|
|
79
|
-
onError?.(
|
|
80
|
-
new Error('Dispatch RPC bridge is not available in top-level runner.'),
|
|
81
|
-
);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
void Promise.resolve(dispatchBridge(request)).catch((error: unknown) => {
|
|
85
|
-
onError?.(error);
|
|
86
|
-
});
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
window.parent.postMessage(
|
|
91
|
-
{
|
|
92
|
-
type: DISPATCH_MESSAGE_TYPE,
|
|
93
|
-
payload: {
|
|
94
|
-
type: DISPATCH_RPC_REQUEST_TYPE,
|
|
95
|
-
payload: request,
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
'*',
|
|
99
|
-
);
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const isDispatchResponse = (
|
|
103
|
-
value: unknown,
|
|
104
|
-
): value is BrowserDispatchResponse => {
|
|
105
|
-
return (
|
|
106
|
-
typeof value === 'object' &&
|
|
107
|
-
value !== null &&
|
|
108
|
-
'requestId' in value &&
|
|
109
|
-
typeof (value as { requestId: unknown }).requestId === 'string'
|
|
110
|
-
);
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const settlePendingRequest = (response: BrowserDispatchResponse): void => {
|
|
114
|
-
const pending = pendingRequests.get(response.requestId);
|
|
115
|
-
if (!pending) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
pendingRequests.delete(response.requestId);
|
|
120
|
-
if (response.stale) {
|
|
121
|
-
pending.reject(new Error(pending.staleMessage));
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
if (response.error) {
|
|
125
|
-
pending.reject(new Error(response.error));
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
pending.resolve(response.result);
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const initMessageListener = (): void => {
|
|
132
|
-
if (messageListenerInitialized) {
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
messageListenerInitialized = true;
|
|
136
|
-
|
|
137
|
-
window.addEventListener('message', (event: MessageEvent) => {
|
|
138
|
-
if (event.data?.type === DISPATCH_RESPONSE_TYPE) {
|
|
139
|
-
settlePendingRequest(event.data.payload as BrowserDispatchResponse);
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
const unwrapDispatchBridgeResult = <T>(
|
|
145
|
-
requestId: string,
|
|
146
|
-
result: unknown,
|
|
147
|
-
staleMessage: string,
|
|
148
|
-
): T => {
|
|
149
|
-
if (!isDispatchResponse(result)) {
|
|
150
|
-
throw new Error('Invalid dispatch bridge response payload.');
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (result.requestId !== requestId) {
|
|
154
|
-
throw new Error(
|
|
155
|
-
`Mismatched dispatch response id: expected ${requestId}, got ${result.requestId}`,
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
if (result.stale) {
|
|
159
|
-
throw new Error(staleMessage);
|
|
160
|
-
}
|
|
161
|
-
if (result.error) {
|
|
162
|
-
throw new Error(result.error);
|
|
163
|
-
}
|
|
164
|
-
return result.result as T;
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
export const dispatchRpc = <T>({
|
|
168
|
-
requestId,
|
|
169
|
-
request,
|
|
170
|
-
timeoutMs,
|
|
171
|
-
timeoutMessage,
|
|
172
|
-
staleMessage,
|
|
173
|
-
}: {
|
|
174
|
-
requestId: string;
|
|
175
|
-
request: BrowserDispatchRequest;
|
|
176
|
-
timeoutMs: number;
|
|
177
|
-
timeoutMessage: string;
|
|
178
|
-
staleMessage: string;
|
|
179
|
-
}): Promise<T> => {
|
|
180
|
-
if (window.parent === window) {
|
|
181
|
-
const dispatchBridge = window[DISPATCH_RPC_BRIDGE_NAME];
|
|
182
|
-
if (!dispatchBridge) {
|
|
183
|
-
throw new Error(
|
|
184
|
-
'Dispatch RPC bridge is not available in top-level runner.',
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return new Promise<T>((resolve, reject) => {
|
|
189
|
-
const timeoutId = setTimeout(() => {
|
|
190
|
-
reject(new Error(timeoutMessage));
|
|
191
|
-
}, timeoutMs);
|
|
192
|
-
|
|
193
|
-
const call = Promise.resolve(dispatchBridge(request)).then((result) =>
|
|
194
|
-
unwrapDispatchBridgeResult<T>(requestId, result, staleMessage),
|
|
195
|
-
);
|
|
196
|
-
|
|
197
|
-
call
|
|
198
|
-
.then((result) => {
|
|
199
|
-
clearTimeout(timeoutId);
|
|
200
|
-
resolve(result);
|
|
201
|
-
})
|
|
202
|
-
.catch((error) => {
|
|
203
|
-
clearTimeout(timeoutId);
|
|
204
|
-
reject(error instanceof Error ? error : new Error(String(error)));
|
|
205
|
-
});
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
initMessageListener();
|
|
210
|
-
|
|
211
|
-
return new Promise<T>((resolve, reject) => {
|
|
212
|
-
const timeoutId = setTimeout(() => {
|
|
213
|
-
pendingRequests.delete(requestId);
|
|
214
|
-
reject(new Error(timeoutMessage));
|
|
215
|
-
}, timeoutMs);
|
|
216
|
-
|
|
217
|
-
pendingRequests.set(requestId, {
|
|
218
|
-
staleMessage,
|
|
219
|
-
resolve: (value) => {
|
|
220
|
-
clearTimeout(timeoutId);
|
|
221
|
-
resolve(value as T);
|
|
222
|
-
},
|
|
223
|
-
reject: (error) => {
|
|
224
|
-
clearTimeout(timeoutId);
|
|
225
|
-
reject(error);
|
|
226
|
-
},
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
window.parent.postMessage(
|
|
230
|
-
{
|
|
231
|
-
type: DISPATCH_MESSAGE_TYPE,
|
|
232
|
-
payload: {
|
|
233
|
-
type: DISPATCH_RPC_REQUEST_TYPE,
|
|
234
|
-
payload: request,
|
|
235
|
-
},
|
|
236
|
-
},
|
|
237
|
-
'*',
|
|
238
|
-
);
|
|
239
|
-
});
|
|
240
|
-
};
|