codex-chatgpt-control 0.5.1-alpha.2 → 0.5.1-alpha.3
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 +15 -0
- package/README.md +27 -0
- package/contracts/v1/fixtures/backend-capabilities.json +40 -1
- package/contracts/v1/fixtures/backend-compatibility.json +21 -0
- package/contracts/v1/fixtures/backend-version.json +6 -1
- package/contracts/v1/fixtures/operation-action-prepared-event.json +42 -0
- package/contracts/v1/fixtures/operation-action.json +13 -0
- package/contracts/v1/fixtures/operation-artifact-receipt.json +14 -0
- package/contracts/v1/fixtures/operation-artifact-transfer-intent-event.json +24 -0
- package/contracts/v1/fixtures/operation-artifact-transfer-receipt-event.json +26 -0
- package/contracts/v1/fixtures/operation-artifact-transfer-state.json +178 -0
- package/contracts/v1/fixtures/operation-blocker.json +10 -0
- package/contracts/v1/fixtures/operation-collect-request.json +17 -0
- package/contracts/v1/fixtures/operation-collect-result.json +42 -0
- package/contracts/v1/fixtures/operation-control-receipt.json +13 -0
- package/contracts/v1/fixtures/operation-control-request.json +18 -0
- package/contracts/v1/fixtures/operation-control-result.json +31 -0
- package/contracts/v1/fixtures/operation-event.json +18 -0
- package/contracts/v1/fixtures/operation-handle.json +10 -0
- package/contracts/v1/fixtures/operation-inspect-request.json +13 -0
- package/contracts/v1/fixtures/operation-inspect-result.json +205 -0
- package/contracts/v1/fixtures/operation-ownership-baseline-event.json +34 -0
- package/contracts/v1/fixtures/operation-receipt.json +32 -0
- package/contracts/v1/fixtures/operation-recovery-decision.json +7 -0
- package/contracts/v1/fixtures/operation-recovery-observation.json +12 -0
- package/contracts/v1/fixtures/operation-request.json +33 -0
- package/contracts/v1/fixtures/operation-state.json +144 -0
- package/contracts/v1/fixtures/operation-submission-witness-event.json +20 -0
- package/contracts/v1/fixtures/operation-submission-witness.json +11 -0
- package/contracts/v1/fixtures/operation-submit-result.json +16 -0
- package/contracts/v1/fixtures/operation-target-established-event.json +21 -0
- package/contracts/v1/manifest.json +155 -1
- package/contracts/v1/parity-suite.json +96 -1
- package/contracts/v1/schemas/backend-compatibility.schema.json +65 -0
- package/contracts/v1/schemas/backend-request.schema.json +8 -2
- package/contracts/v1/schemas/capabilities.schema.json +81 -1
- package/contracts/v1/schemas/manifest.schema.json +57 -0
- package/contracts/v1/schemas/operation-action.schema.json +155 -0
- package/contracts/v1/schemas/operation-artifact-receipt.schema.json +89 -0
- package/contracts/v1/schemas/operation-blocker.schema.json +70 -0
- package/contracts/v1/schemas/operation-collect-request.schema.json +46 -0
- package/contracts/v1/schemas/operation-collect-result.schema.json +74 -0
- package/contracts/v1/schemas/operation-control-receipt.schema.json +108 -0
- package/contracts/v1/schemas/operation-control-request.schema.json +111 -0
- package/contracts/v1/schemas/operation-control-result.schema.json +112 -0
- package/contracts/v1/schemas/operation-event.schema.json +756 -0
- package/contracts/v1/schemas/operation-handle.schema.json +55 -0
- package/contracts/v1/schemas/operation-inspect-request.schema.json +42 -0
- package/contracts/v1/schemas/operation-inspect-result.schema.json +1662 -0
- package/contracts/v1/schemas/operation-receipt.schema.json +153 -0
- package/contracts/v1/schemas/operation-recovery.schema.json +314 -0
- package/contracts/v1/schemas/operation-request.schema.json +158 -0
- package/contracts/v1/schemas/operation-state.schema.json +691 -0
- package/contracts/v1/schemas/operation-submission-witness.schema.json +48 -0
- package/contracts/v1/schemas/operation-submit-result.schema.json +94 -0
- package/contracts/v1/surface-drift-policy.json +50 -0
- package/contracts/v1/vectors/operation-request-digest-v1.json +43 -0
- package/dist/codex-chatgpt-control-backend.mjs +43897 -9094
- package/dist/codex-chatgpt-control.bundle.mjs +46360 -10262
- package/dist/src/backend/client.d.ts +103 -2
- package/dist/src/backend/client.js +1436 -97
- package/dist/src/backend/compatibility.d.ts +12 -0
- package/dist/src/backend/compatibility.js +208 -0
- package/dist/src/backend/protocol.d.ts +84 -1
- package/dist/src/backend/protocol.js +31 -5
- package/dist/src/backend/runtime-identity.d.ts +10 -0
- package/dist/src/backend/runtime-identity.js +141 -0
- package/dist/src/backend/session.d.ts +10 -2
- package/dist/src/backend/session.js +478 -7
- package/dist/src/backend/stdio-server.d.ts +4 -1
- package/dist/src/backend/stdio-server.js +240 -28
- package/dist/src/browser/active-composer-file-input.d.ts +9 -0
- package/dist/src/browser/active-composer-file-input.js +33 -0
- package/dist/src/browser/attach.d.ts +4 -1
- package/dist/src/browser/attach.js +136 -28
- package/dist/src/browser/downloads.d.ts +2 -0
- package/dist/src/client.d.ts +38 -0
- package/dist/src/client.js +1222 -59
- package/dist/src/commands/configuration.js +13 -7
- package/dist/src/commands/doctor.d.ts +1 -1
- package/dist/src/commands/doctor.js +26 -3
- package/dist/src/commands/experience.js +35 -0
- package/dist/src/commands/files.js +4 -27
- package/dist/src/commands/modes.js +49 -17
- package/dist/src/commands/power-discovery.d.ts +130 -0
- package/dist/src/commands/power-discovery.js +627 -0
- package/dist/src/commands/project-sources.js +325 -31
- package/dist/src/commands/sequence.js +59 -32
- package/dist/src/commands/session.js +7 -2
- package/dist/src/commands/work.js +31 -0
- package/dist/src/errors.d.ts +9 -0
- package/dist/src/errors.js +22 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +9 -0
- package/dist/src/operations/artifact-output.d.ts +69 -0
- package/dist/src/operations/artifact-output.js +1748 -0
- package/dist/src/operations/artifact-stream.d.ts +19 -0
- package/dist/src/operations/artifact-stream.js +35 -0
- package/dist/src/operations/artifact-transfer.d.ts +149 -0
- package/dist/src/operations/artifact-transfer.js +1312 -0
- package/dist/src/operations/browser-adapter.d.ts +161 -0
- package/dist/src/operations/browser-adapter.js +2023 -0
- package/dist/src/operations/browser-observation.d.ts +110 -0
- package/dist/src/operations/browser-observation.js +1208 -0
- package/dist/src/operations/browser-target.d.ts +103 -0
- package/dist/src/operations/browser-target.js +489 -0
- package/dist/src/operations/canonical.d.ts +4 -0
- package/dist/src/operations/canonical.js +412 -0
- package/dist/src/operations/chatgpt-runtime.d.ts +72 -0
- package/dist/src/operations/chatgpt-runtime.js +1150 -0
- package/dist/src/operations/client.d.ts +138 -0
- package/dist/src/operations/client.js +1143 -0
- package/dist/src/operations/collector.d.ts +195 -0
- package/dist/src/operations/collector.js +1039 -0
- package/dist/src/operations/control.d.ts +372 -0
- package/dist/src/operations/control.js +1498 -0
- package/dist/src/operations/file-identity.d.ts +33 -0
- package/dist/src/operations/file-identity.js +153 -0
- package/dist/src/operations/handle.d.ts +19 -0
- package/dist/src/operations/handle.js +727 -0
- package/dist/src/operations/index.d.ts +22 -0
- package/dist/src/operations/index.js +22 -0
- package/dist/src/operations/journal.d.ts +142 -0
- package/dist/src/operations/journal.js +1931 -0
- package/dist/src/operations/production-attachments.d.ts +116 -0
- package/dist/src/operations/production-attachments.js +1018 -0
- package/dist/src/operations/production-chatgpt-artifacts.d.ts +74 -0
- package/dist/src/operations/production-chatgpt-artifacts.js +1146 -0
- package/dist/src/operations/production-chatgpt-attachments.d.ts +71 -0
- package/dist/src/operations/production-chatgpt-attachments.js +1563 -0
- package/dist/src/operations/production-configuration.d.ts +42 -0
- package/dist/src/operations/production-configuration.js +1363 -0
- package/dist/src/operations/production-primitives.d.ts +53 -0
- package/dist/src/operations/production-primitives.js +1073 -0
- package/dist/src/operations/production-work-steer.d.ts +203 -0
- package/dist/src/operations/production-work-steer.js +1218 -0
- package/dist/src/operations/recovery.d.ts +80 -0
- package/dist/src/operations/recovery.js +174 -0
- package/dist/src/operations/runtime-adapter.d.ts +118 -0
- package/dist/src/operations/runtime-adapter.js +786 -0
- package/dist/src/operations/send-once.d.ts +224 -0
- package/dist/src/operations/send-once.js +1081 -0
- package/dist/src/operations/service.d.ts +281 -0
- package/dist/src/operations/service.js +2810 -0
- package/dist/src/operations/staging.d.ts +136 -0
- package/dist/src/operations/staging.js +630 -0
- package/dist/src/operations/state-machine.d.ts +29 -0
- package/dist/src/operations/state-machine.js +2047 -0
- package/dist/src/operations/submission.d.ts +423 -0
- package/dist/src/operations/submission.js +1676 -0
- package/dist/src/operations/turn-ownership.d.ts +202 -0
- package/dist/src/operations/turn-ownership.js +700 -0
- package/dist/src/operations/types.d.ts +454 -0
- package/dist/src/operations/types.js +20 -0
- package/dist/src/operations/wire-requests.d.ts +21 -0
- package/dist/src/operations/wire-requests.js +396 -0
- package/dist/src/operations/wire-results.d.ts +97 -0
- package/dist/src/operations/wire-results.js +818 -0
- package/dist/src/runner/responses.d.ts +3 -1
- package/dist/src/runner/responses.js +19 -1
- package/dist/src/runner/result.js +227 -55
- package/dist/src/runner/types.d.ts +12 -0
- package/dist/src/runtime/command-routing.d.ts +149 -0
- package/dist/src/runtime/command-routing.js +431 -0
- package/dist/src/runtime/coordinated-browser.d.ts +24 -0
- package/dist/src/runtime/coordinated-browser.js +315 -0
- package/dist/src/runtime/coordinated-page.d.ts +39 -0
- package/dist/src/runtime/coordinated-page.js +634 -0
- package/dist/src/runtime/operation-context.d.ts +142 -0
- package/dist/src/runtime/operation-context.js +410 -0
- package/dist/src/runtime/runtime-session.d.ts +95 -0
- package/dist/src/runtime/runtime-session.js +314 -0
- package/dist/src/runtime/tab-coordinator.d.ts +200 -0
- package/dist/src/runtime/tab-coordinator.js +1200 -0
- package/dist/src/runtime/value-boundaries.d.ts +18 -0
- package/dist/src/runtime/value-boundaries.js +49 -0
- package/dist/src/safety/untrusted-output.js +2 -1
- package/dist/src/scripts/backend-server.js +4 -1
- package/dist/src/scripts/live-smoke/harness.js +79 -7
- package/dist/src/scripts/live-smoke/scenarios.d.ts +28 -0
- package/dist/src/scripts/live-smoke/scenarios.js +110 -18
- package/dist/src/scripts/live-smoke/types.d.ts +3 -0
- package/dist/src/scripts/release-canary-module.js +17 -4
- package/dist/src/types.d.ts +38 -1
- package/package.json +1 -1
- package/references/2026-08-16-transactional-operations.md +459 -0
- package/references/agents-runner.md +34 -0
- package/references/backend-protocol.md +73 -0
- package/references/python-parity.md +72 -0
- package/references/responses-adapter.md +19 -0
- package/references/streaming.md +6 -0
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The priority mapping is deliberately public. It makes a call site review
|
|
3
|
+
*able without having to infer scheduler intent from a method name.
|
|
4
|
+
*/
|
|
5
|
+
export const COORDINATED_PAGE_PRIORITIES = Object.freeze({
|
|
6
|
+
read: "read",
|
|
7
|
+
mutation: "mutation",
|
|
8
|
+
control: "control"
|
|
9
|
+
});
|
|
10
|
+
const MAX_PROTO_DEPTH = 12;
|
|
11
|
+
const MAX_CAPABILITY_DEPTH = 8;
|
|
12
|
+
const MAX_ARGUMENTS = 16;
|
|
13
|
+
const MAX_CACHED_PAGE_AFFINITIES = 256;
|
|
14
|
+
export class CoordinatedPageError extends Error {
|
|
15
|
+
code = "coordinated_page_invalid";
|
|
16
|
+
constructor(message, options) {
|
|
17
|
+
super(message, options);
|
|
18
|
+
this.name = "CoordinatedPageError";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const pageWrappers = new WeakMap();
|
|
22
|
+
const rawValues = new WeakMap();
|
|
23
|
+
// waitForEvent has a short actor-held registration lifetime and a potentially
|
|
24
|
+
// long event lifetime outside the actor. Mutation flows need an explicit
|
|
25
|
+
// fence for the former before they click.
|
|
26
|
+
const eventRegistrationBarriers = new WeakMap();
|
|
27
|
+
/** Return the registration fence for a coordinated waitForEvent promise. */
|
|
28
|
+
export function coordinatedEventRegistrationBarrier(value) {
|
|
29
|
+
return isObjectLike(value) ? eventRegistrationBarriers.get(value) : undefined;
|
|
30
|
+
}
|
|
31
|
+
function isObjectLike(value) {
|
|
32
|
+
return (typeof value === "object" && value !== null) || typeof value === "function";
|
|
33
|
+
}
|
|
34
|
+
function labelForKey(key) {
|
|
35
|
+
try {
|
|
36
|
+
return typeof key === "symbol" ? key.toString() : String(key);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return "unknown";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function invalid(message, cause) {
|
|
43
|
+
throw new CoordinatedPageError(message, cause === undefined ? undefined : { cause });
|
|
44
|
+
}
|
|
45
|
+
function readDataMember(value, key, label) {
|
|
46
|
+
let current = value;
|
|
47
|
+
for (let depth = 0; current !== null && depth < MAX_PROTO_DEPTH; depth += 1) {
|
|
48
|
+
let descriptor;
|
|
49
|
+
try {
|
|
50
|
+
descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
return invalid(`Cannot inspect ${label}: the provider object rejected a bounded descriptor read`, error);
|
|
54
|
+
}
|
|
55
|
+
if (descriptor !== undefined) {
|
|
56
|
+
if (!("value" in descriptor)) {
|
|
57
|
+
return invalid(`Cannot use ${label}: accessor-backed provider members are not supported`);
|
|
58
|
+
}
|
|
59
|
+
if (current !== value && typeof descriptor.value === "function") {
|
|
60
|
+
// The Codex browser bridge exposes class instances through proxies.
|
|
61
|
+
// Its normal property read returns a receiver-safe bound callable;
|
|
62
|
+
// applying the raw prototype method to the proxy fails private-field
|
|
63
|
+
// brand checks. Only consult the provider binding after proving the
|
|
64
|
+
// member is an inherited data method, never for an accessor.
|
|
65
|
+
try {
|
|
66
|
+
const receiverSafe = Reflect.get(value, key, value);
|
|
67
|
+
if (typeof receiverSafe === "function")
|
|
68
|
+
return receiverSafe;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
return invalid(`Cannot use ${label}: provider method binding failed`, error);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return descriptor.value;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const prototype = Object.getPrototypeOf(current);
|
|
78
|
+
current = isObjectLike(prototype) ? prototype : null;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
return invalid(`Cannot inspect ${label}: the provider prototype chain is not readable`, error);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (current !== null)
|
|
85
|
+
return invalid(`Cannot inspect ${label}: provider prototype depth exceeded`);
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
function requiredCallable(value, key, label) {
|
|
89
|
+
const member = readDataMember(value, key, label);
|
|
90
|
+
if (typeof member !== "function")
|
|
91
|
+
return invalid(`${label} is not available as a callable provider method`);
|
|
92
|
+
return member;
|
|
93
|
+
}
|
|
94
|
+
function optionalCallable(value, key, label) {
|
|
95
|
+
const member = readDataMember(value, key, label);
|
|
96
|
+
if (member === undefined)
|
|
97
|
+
return undefined;
|
|
98
|
+
if (typeof member !== "function")
|
|
99
|
+
return invalid(`${label} is not callable`);
|
|
100
|
+
return member;
|
|
101
|
+
}
|
|
102
|
+
function requiredRecord(value, label) {
|
|
103
|
+
if (!isObjectLike(value))
|
|
104
|
+
return invalid(`${label} must be an object`);
|
|
105
|
+
return value;
|
|
106
|
+
}
|
|
107
|
+
function validateStableOwnerId(value, label) {
|
|
108
|
+
if (typeof value !== "string")
|
|
109
|
+
return invalid(`${label} must be a stable string`);
|
|
110
|
+
const normalized = value.trim();
|
|
111
|
+
if (normalized.length === 0 || normalized.length > 512 || /[\u0000-\u001f\u007f]/u.test(normalized)) {
|
|
112
|
+
return invalid(`${label} must be a non-empty stable string`);
|
|
113
|
+
}
|
|
114
|
+
return normalized;
|
|
115
|
+
}
|
|
116
|
+
function normalizeOwner(value) {
|
|
117
|
+
const owner = requiredRecord(value, "owner");
|
|
118
|
+
const backendSessionId = validateStableOwnerId(readDataMember(owner, "backendSessionId", "owner.backendSessionId"), "owner.backendSessionId");
|
|
119
|
+
const ownerIdValue = readDataMember(owner, "ownerId", "owner.ownerId");
|
|
120
|
+
const operationIdValue = readDataMember(owner, "operationId", "owner.operationId");
|
|
121
|
+
const ownerId = ownerIdValue === undefined ? undefined : validateStableOwnerId(ownerIdValue, "owner.ownerId");
|
|
122
|
+
const operationId = operationIdValue === undefined
|
|
123
|
+
? undefined
|
|
124
|
+
: validateStableOwnerId(operationIdValue, "owner.operationId");
|
|
125
|
+
return Object.freeze({
|
|
126
|
+
backendSessionId,
|
|
127
|
+
...(ownerId === undefined ? {} : { ownerId }),
|
|
128
|
+
...(operationId === undefined ? {} : { operationId })
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
function normalizeResource(value) {
|
|
132
|
+
const resource = requiredRecord(value, "resource");
|
|
133
|
+
const kind = readDataMember(resource, "kind", "resource.kind");
|
|
134
|
+
const key = readDataMember(resource, "key", "resource.key");
|
|
135
|
+
if (kind !== "tab" && kind !== "browser")
|
|
136
|
+
return invalid("resource.kind must be tab or browser");
|
|
137
|
+
if (typeof key !== "string" || key.length === 0)
|
|
138
|
+
return invalid("resource.key must be a canonical coordinator key");
|
|
139
|
+
const parts = key.split(":");
|
|
140
|
+
const expectedParts = kind === "tab" ? 4 : 3;
|
|
141
|
+
if (parts.length !== expectedParts || parts[0] !== kind)
|
|
142
|
+
return invalid("resource.key must be a canonical coordinator key");
|
|
143
|
+
for (let index = 1; index < parts.length; index += 1) {
|
|
144
|
+
const encoded = parts[index];
|
|
145
|
+
if (encoded === undefined || encoded.length === 0)
|
|
146
|
+
return invalid("resource.key must be a canonical coordinator key");
|
|
147
|
+
let decoded;
|
|
148
|
+
try {
|
|
149
|
+
decoded = decodeURIComponent(encoded);
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
return invalid("resource.key must be a canonical coordinator key", error);
|
|
153
|
+
}
|
|
154
|
+
if (encodeURIComponent(decoded) !== encoded)
|
|
155
|
+
return invalid("resource.key must be a canonical coordinator key");
|
|
156
|
+
validateStableOwnerId(decoded, `resource.key part ${index}`);
|
|
157
|
+
if (["unknown", "undefined", "null", "n/a", "na"].includes(decoded.toLowerCase())) {
|
|
158
|
+
return invalid("resource.key must be a canonical coordinator key");
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return Object.freeze({ kind, key });
|
|
162
|
+
}
|
|
163
|
+
function normalizeOptions(value) {
|
|
164
|
+
const options = requiredRecord(value, "coordinated page options");
|
|
165
|
+
const coordinator = readDataMember(options, "coordinator", "options.coordinator");
|
|
166
|
+
if (!isObjectLike(coordinator))
|
|
167
|
+
return invalid("options.coordinator must be a ProcessTabCoordinator");
|
|
168
|
+
if (typeof readDataMember(coordinator, "withTabTransaction", "coordinator.withTabTransaction") !== "function"
|
|
169
|
+
|| typeof readDataMember(coordinator, "withBrowserAcquisition", "coordinator.withBrowserAcquisition") !== "function") {
|
|
170
|
+
return invalid("options.coordinator must expose both coordinator transaction methods");
|
|
171
|
+
}
|
|
172
|
+
const resource = normalizeResource(readDataMember(options, "resource", "options.resource"));
|
|
173
|
+
const owner = normalizeOwner(readDataMember(options, "owner", "options.owner"));
|
|
174
|
+
const timeoutValue = readDataMember(options, "defaultTimeoutMs", "options.defaultTimeoutMs");
|
|
175
|
+
if (timeoutValue !== undefined && (!Number.isSafeInteger(timeoutValue) || timeoutValue < 0)) {
|
|
176
|
+
return invalid("options.defaultTimeoutMs must be a non-negative safe integer");
|
|
177
|
+
}
|
|
178
|
+
const defaultTimeoutMs = timeoutValue === undefined ? undefined : timeoutValue;
|
|
179
|
+
return Object.freeze({
|
|
180
|
+
coordinator: coordinator,
|
|
181
|
+
resource,
|
|
182
|
+
owner,
|
|
183
|
+
...(defaultTimeoutMs === undefined ? {} : { defaultTimeoutMs })
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
function safeInvocation(fn, receiver, args) {
|
|
187
|
+
if (args.length > MAX_ARGUMENTS)
|
|
188
|
+
return invalid("Provider invocation argument count exceeded the bounded facade limit");
|
|
189
|
+
try {
|
|
190
|
+
return Reflect.apply(fn, receiver, args);
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function absorbRejection(promise) {
|
|
197
|
+
// Keep the original promise for caller-visible error identity while
|
|
198
|
+
// attaching a rejection handler immediately if a caller abandons it.
|
|
199
|
+
void promise.catch(() => undefined);
|
|
200
|
+
return promise;
|
|
201
|
+
}
|
|
202
|
+
function timeoutFromArg(value, label) {
|
|
203
|
+
if (!isObjectLike(value))
|
|
204
|
+
return undefined;
|
|
205
|
+
const timeout = readDataMember(value, "timeoutMs", `${label}.timeoutMs`);
|
|
206
|
+
if (timeout === undefined)
|
|
207
|
+
return undefined;
|
|
208
|
+
if (typeof timeout !== "number" || !Number.isFinite(timeout) || timeout < 0) {
|
|
209
|
+
return invalid(`${label}.timeoutMs must be a non-negative finite number`);
|
|
210
|
+
}
|
|
211
|
+
return timeout;
|
|
212
|
+
}
|
|
213
|
+
function timeoutFromEventOptions(value) {
|
|
214
|
+
// Event settlement is intentionally not governed by this deadline: only
|
|
215
|
+
// provider registration runs under the actor. Reading timeoutMs here is
|
|
216
|
+
// still useful to reject malformed accessor-backed options early without
|
|
217
|
+
// ever holding the actor while the event waits.
|
|
218
|
+
if (!isObjectLike(value))
|
|
219
|
+
return undefined;
|
|
220
|
+
const timeoutMs = readDataMember(value, "timeoutMs", "waitForEvent.options.timeoutMs");
|
|
221
|
+
if (timeoutMs !== undefined && (typeof timeoutMs !== "number" || !Number.isFinite(timeoutMs) || timeoutMs < 0)) {
|
|
222
|
+
return invalid("waitForEvent.options.timeoutMs must be a non-negative finite number");
|
|
223
|
+
}
|
|
224
|
+
const timeout = readDataMember(value, "timeout", "waitForEvent.options.timeout");
|
|
225
|
+
if (timeout !== undefined && (typeof timeout !== "number" || !Number.isFinite(timeout) || timeout < 0)) {
|
|
226
|
+
return invalid("waitForEvent.options.timeout must be a non-negative finite number");
|
|
227
|
+
}
|
|
228
|
+
if (timeoutMs === undefined)
|
|
229
|
+
return timeout;
|
|
230
|
+
if (timeout === undefined)
|
|
231
|
+
return timeoutMs;
|
|
232
|
+
return Math.min(timeoutMs, timeout);
|
|
233
|
+
}
|
|
234
|
+
function isPromiseLike(value) {
|
|
235
|
+
return isObjectLike(value) && typeof readDataMember(value, "then", "provider promise.then") === "function";
|
|
236
|
+
}
|
|
237
|
+
function makeCacheKey(options) {
|
|
238
|
+
const owner = options.owner;
|
|
239
|
+
return [
|
|
240
|
+
options.resource.kind,
|
|
241
|
+
options.resource.key,
|
|
242
|
+
owner.backendSessionId,
|
|
243
|
+
owner.ownerId ?? "",
|
|
244
|
+
owner.operationId ?? "",
|
|
245
|
+
options.defaultTimeoutMs === undefined ? "" : String(options.defaultTimeoutMs)
|
|
246
|
+
].map(part => encodeURIComponent(part)).join(":");
|
|
247
|
+
}
|
|
248
|
+
function getPageCache(rawPage, coordinator) {
|
|
249
|
+
let byCoordinator = pageWrappers.get(rawPage);
|
|
250
|
+
if (byCoordinator === undefined) {
|
|
251
|
+
byCoordinator = new WeakMap();
|
|
252
|
+
pageWrappers.set(rawPage, byCoordinator);
|
|
253
|
+
}
|
|
254
|
+
let byAffinity = byCoordinator.get(coordinator);
|
|
255
|
+
if (byAffinity === undefined) {
|
|
256
|
+
byAffinity = new Map();
|
|
257
|
+
byCoordinator.set(coordinator, byAffinity);
|
|
258
|
+
}
|
|
259
|
+
return byAffinity;
|
|
260
|
+
}
|
|
261
|
+
function cachePage(cache, affinity, page) {
|
|
262
|
+
cache.delete(affinity);
|
|
263
|
+
cache.set(affinity, new WeakRef(page));
|
|
264
|
+
for (const [key, reference] of cache) {
|
|
265
|
+
if (reference.deref() === undefined)
|
|
266
|
+
cache.delete(key);
|
|
267
|
+
}
|
|
268
|
+
while (cache.size > MAX_CACHED_PAGE_AFFINITIES) {
|
|
269
|
+
const oldest = cache.keys().next().value;
|
|
270
|
+
if (oldest === undefined)
|
|
271
|
+
break;
|
|
272
|
+
cache.delete(oldest);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function wrapResult(value, state, label, depth = 0) {
|
|
276
|
+
if (depth > MAX_CAPABILITY_DEPTH || !isObjectLike(value))
|
|
277
|
+
return value;
|
|
278
|
+
if (isFileChooserCandidate(value))
|
|
279
|
+
return wrapFileChooser(value, state, label);
|
|
280
|
+
// Capability methods generally return ordinary data (inventories, assets,
|
|
281
|
+
// and receipts), not another provider object. Do not proxy those values:
|
|
282
|
+
// array/string helpers must remain local synchronous operations.
|
|
283
|
+
return value;
|
|
284
|
+
}
|
|
285
|
+
function isFileChooserCandidate(value) {
|
|
286
|
+
return typeof readDataMember(value, "setFiles", "file chooser.setFiles") === "function";
|
|
287
|
+
}
|
|
288
|
+
function assertProxyInterceptable(target, key, label) {
|
|
289
|
+
let descriptor;
|
|
290
|
+
try {
|
|
291
|
+
descriptor = Object.getOwnPropertyDescriptor(target, key);
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
invalid(`Cannot wrap ${label}: provider object rejected a descriptor read`, error);
|
|
295
|
+
}
|
|
296
|
+
if (descriptor !== undefined && descriptor.configurable === false && "value" in descriptor && descriptor.writable === false) {
|
|
297
|
+
return invalid(`Cannot wrap ${label}: provider method is a non-configurable immutable property`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function wrapFileChooser(rawChooser, state, label) {
|
|
301
|
+
const existing = state.fileChooserWrappers.get(rawChooser);
|
|
302
|
+
if (existing !== undefined)
|
|
303
|
+
return existing;
|
|
304
|
+
assertProxyInterceptable(rawChooser, "setFiles", `${label}.setFiles`);
|
|
305
|
+
const wrapper = new Proxy(rawChooser, {
|
|
306
|
+
get(target, property) {
|
|
307
|
+
const propertyLabel = `${label}.${labelForKey(property)}`;
|
|
308
|
+
if (property === "setFiles") {
|
|
309
|
+
const setFiles = requiredCallable(target, property, propertyLabel);
|
|
310
|
+
return (paths, options) => {
|
|
311
|
+
return routeTransaction(state, "mutation", propertyLabel, timeoutFromArg(options, propertyLabel), () => safeInvocation(setFiles, target, [paths, options]));
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
if (property === "element") {
|
|
315
|
+
assertProxyInterceptable(target, property, propertyLabel);
|
|
316
|
+
const element = optionalCallable(target, property, propertyLabel);
|
|
317
|
+
if (element === undefined)
|
|
318
|
+
return undefined;
|
|
319
|
+
return () => routeTransaction(state, "read", propertyLabel, undefined, async () => {
|
|
320
|
+
const result = await safeInvocation(element, target, []);
|
|
321
|
+
return wrapLocator(await result, state, propertyLabel);
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
if (property === "isMultiple") {
|
|
325
|
+
assertProxyInterceptable(target, property, propertyLabel);
|
|
326
|
+
const isMultiple = optionalCallable(target, property, propertyLabel);
|
|
327
|
+
if (isMultiple === undefined)
|
|
328
|
+
return undefined;
|
|
329
|
+
return () => routeTransaction(state, "read", propertyLabel, undefined, () => safeInvocation(isMultiple, target, []));
|
|
330
|
+
}
|
|
331
|
+
const member = readDataMember(target, property, propertyLabel);
|
|
332
|
+
if (typeof member === "function")
|
|
333
|
+
return member.bind(target);
|
|
334
|
+
return member;
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
state.fileChooserWrappers.set(rawChooser, wrapper);
|
|
338
|
+
rawValues.set(wrapper, rawChooser);
|
|
339
|
+
return wrapper;
|
|
340
|
+
}
|
|
341
|
+
function capabilityPriority(name) {
|
|
342
|
+
const lower = name.toLowerCase();
|
|
343
|
+
if (/^(get|list|read|inspect|status|count|query|fetch|metadata|inventory)/u.test(lower))
|
|
344
|
+
return "read";
|
|
345
|
+
if (/^(stop|cancel)/u.test(lower))
|
|
346
|
+
return "control";
|
|
347
|
+
return "mutation";
|
|
348
|
+
}
|
|
349
|
+
function wrapCapability(value, state, label, depth) {
|
|
350
|
+
const existing = state.capabilityWrappers.get(value);
|
|
351
|
+
if (existing !== undefined)
|
|
352
|
+
return existing;
|
|
353
|
+
const wrapper = new Proxy(value, {
|
|
354
|
+
get(target, property) {
|
|
355
|
+
if (property === "then")
|
|
356
|
+
return undefined;
|
|
357
|
+
const propertyLabel = `${label}.${labelForKey(property)}`;
|
|
358
|
+
const member = readDataMember(target, property, propertyLabel);
|
|
359
|
+
if (typeof member !== "function")
|
|
360
|
+
return member;
|
|
361
|
+
if (typeof property === "symbol")
|
|
362
|
+
return member;
|
|
363
|
+
assertProxyInterceptable(target, property, propertyLabel);
|
|
364
|
+
return (...args) => routeTransaction(state, capabilityPriority(labelForKey(property)), propertyLabel, timeoutFromArg(args.at(-1), propertyLabel), async () => wrapResult(await safeInvocation(member, target, args), state, propertyLabel, depth + 1));
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
state.capabilityWrappers.set(value, wrapper);
|
|
368
|
+
rawValues.set(wrapper, value);
|
|
369
|
+
return wrapper;
|
|
370
|
+
}
|
|
371
|
+
function wrapLocator(rawLocator, state, label) {
|
|
372
|
+
if (!isObjectLike(rawLocator))
|
|
373
|
+
return invalid(`${label} did not return a locator object`);
|
|
374
|
+
const existing = state.locatorWrappers.get(rawLocator);
|
|
375
|
+
if (existing !== undefined)
|
|
376
|
+
return existing;
|
|
377
|
+
const wrapper = {};
|
|
378
|
+
const locator = rawLocator;
|
|
379
|
+
state.locatorWrappers.set(locator, wrapper);
|
|
380
|
+
rawValues.set(wrapper, locator);
|
|
381
|
+
const sync = (name) => {
|
|
382
|
+
const member = optionalCallable(locator, name, `${label}.${name}`);
|
|
383
|
+
if (member === undefined)
|
|
384
|
+
return;
|
|
385
|
+
wrapper[name] = (...args) => {
|
|
386
|
+
const child = safeInvocation(member, locator, args);
|
|
387
|
+
return wrapLocator(child, state, `${label}.${name}`);
|
|
388
|
+
};
|
|
389
|
+
};
|
|
390
|
+
sync("nth");
|
|
391
|
+
sync("first");
|
|
392
|
+
sync("last");
|
|
393
|
+
sync("locator");
|
|
394
|
+
sync("filter");
|
|
395
|
+
sync("getByRole");
|
|
396
|
+
sync("getByText");
|
|
397
|
+
const transaction = (name, priority, argumentTimeoutIndex) => {
|
|
398
|
+
const member = optionalCallable(locator, name, `${label}.${name}`);
|
|
399
|
+
if (member === undefined)
|
|
400
|
+
return;
|
|
401
|
+
wrapper[name] = (...args) => routeTransaction(state, priority, `${label}.${name}`, argumentTimeoutIndex === undefined ? state.options.defaultTimeoutMs : timeoutFromArg(args[argumentTimeoutIndex], `${label}.${name}`) ?? state.options.defaultTimeoutMs, () => safeInvocation(member, locator, args));
|
|
402
|
+
};
|
|
403
|
+
transaction("click", "mutation", 0);
|
|
404
|
+
transaction("press", "mutation", 1);
|
|
405
|
+
transaction("fill", "mutation", 1);
|
|
406
|
+
transaction("textContent", "read", 0);
|
|
407
|
+
transaction("innerText", "read", 0);
|
|
408
|
+
transaction("innerHTML", "read", 0);
|
|
409
|
+
transaction("count", "read", undefined);
|
|
410
|
+
transaction("allTextContents", "read", 0);
|
|
411
|
+
transaction("isVisible", "read", 0);
|
|
412
|
+
transaction("evaluate", "mutation", 2);
|
|
413
|
+
transaction("setInputFiles", "mutation", 1);
|
|
414
|
+
return wrapper;
|
|
415
|
+
}
|
|
416
|
+
function wrapPlaywright(value, state, label) {
|
|
417
|
+
const existing = state.playwrightWrappers.get(value);
|
|
418
|
+
if (existing !== undefined)
|
|
419
|
+
return existing;
|
|
420
|
+
const wrapper = new Proxy(value, {
|
|
421
|
+
get(target, property) {
|
|
422
|
+
const propertyLabel = `${label}.${labelForKey(property)}`;
|
|
423
|
+
const member = readDataMember(target, property, propertyLabel);
|
|
424
|
+
if (typeof member !== "function")
|
|
425
|
+
return member;
|
|
426
|
+
if (typeof property === "symbol")
|
|
427
|
+
return member;
|
|
428
|
+
if (property === "waitForTimeout") {
|
|
429
|
+
assertProxyInterceptable(target, property, propertyLabel);
|
|
430
|
+
return (milliseconds) => {
|
|
431
|
+
const result = safeInvocation(member, target, [milliseconds]);
|
|
432
|
+
return absorbRejection(Promise.resolve(result));
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
assertProxyInterceptable(target, property, propertyLabel);
|
|
436
|
+
return (...args) => routeTransaction(state, "read", propertyLabel, timeoutFromArg(args.at(-1), propertyLabel) ?? state.options.defaultTimeoutMs, async () => wrapResult(await safeInvocation(member, target, args), state, propertyLabel));
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
state.playwrightWrappers.set(value, wrapper);
|
|
440
|
+
rawValues.set(wrapper, value);
|
|
441
|
+
return wrapper;
|
|
442
|
+
}
|
|
443
|
+
function routeTransaction(state, priority, label, timeoutMs, callback) {
|
|
444
|
+
const requestOptions = {
|
|
445
|
+
owner: state.options.owner,
|
|
446
|
+
priority,
|
|
447
|
+
label,
|
|
448
|
+
...(timeoutMs === undefined ? {} : { timeoutMs })
|
|
449
|
+
};
|
|
450
|
+
const { coordinator, resource } = state.options;
|
|
451
|
+
const pending = resource.kind === "tab"
|
|
452
|
+
? coordinator.withTabTransaction(resource.key, requestOptions, () => callback())
|
|
453
|
+
: coordinator.withBrowserAcquisition(resource.key, requestOptions, () => callback());
|
|
454
|
+
return absorbRejection(pending);
|
|
455
|
+
}
|
|
456
|
+
function waitForEvent(state, rawPage, event, optionsOrCallback) {
|
|
457
|
+
const wait = requiredCallable(rawPage, "waitForEvent", "page.waitForEvent");
|
|
458
|
+
const eventTimeoutMs = timeoutFromEventOptions(optionsOrCallback);
|
|
459
|
+
const registrationTimeoutMs = eventTimeoutMs === undefined
|
|
460
|
+
? state.options.defaultTimeoutMs
|
|
461
|
+
: state.options.defaultTimeoutMs === undefined
|
|
462
|
+
? eventTimeoutMs
|
|
463
|
+
: Math.min(eventTimeoutMs, state.options.defaultTimeoutMs);
|
|
464
|
+
const registration = routeTransaction(state, "read", "page.waitForEvent.register", registrationTimeoutMs, () => {
|
|
465
|
+
let candidate;
|
|
466
|
+
try {
|
|
467
|
+
candidate = safeInvocation(wait, rawPage, [event, optionsOrCallback]);
|
|
468
|
+
}
|
|
469
|
+
catch (error) {
|
|
470
|
+
throw error;
|
|
471
|
+
}
|
|
472
|
+
const providerPromise = Promise.resolve(candidate);
|
|
473
|
+
const settled = providerPromise.then(value => value, error => { throw error; });
|
|
474
|
+
void settled.catch(() => undefined);
|
|
475
|
+
return { promise: settled };
|
|
476
|
+
});
|
|
477
|
+
const result = registration.then(async ({ promise }) => wrapResult(await promise, state, "page.waitForEvent.result"));
|
|
478
|
+
const handled = absorbRejection(result);
|
|
479
|
+
// Readiness is total and path/data free. Provider success or failure remains
|
|
480
|
+
// observable only through the original event promise.
|
|
481
|
+
const barrier = registration.then(() => undefined, () => undefined);
|
|
482
|
+
void barrier.catch(() => undefined);
|
|
483
|
+
eventRegistrationBarriers.set(handled, barrier);
|
|
484
|
+
return handled;
|
|
485
|
+
}
|
|
486
|
+
function makeKeyboard(rawKeyboard, state) {
|
|
487
|
+
const press = optionalCallable(rawKeyboard, "press", "page.keyboard.press");
|
|
488
|
+
if (press === undefined)
|
|
489
|
+
return {};
|
|
490
|
+
return {
|
|
491
|
+
press: (key) => routeTransaction(state, "mutation", "page.keyboard.press", state.options.defaultTimeoutMs, () => safeInvocation(press, rawKeyboard, [key]))
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
function makeMouse(rawMouse, state) {
|
|
495
|
+
const move = optionalCallable(rawMouse, "move", "page.mouse.move");
|
|
496
|
+
const click = optionalCallable(rawMouse, "click", "page.mouse.click");
|
|
497
|
+
return {
|
|
498
|
+
...(move === undefined ? {} : {
|
|
499
|
+
move: (x, y) => routeTransaction(state, "mutation", "page.mouse.move", state.options.defaultTimeoutMs, () => safeInvocation(move, rawMouse, [x, y]))
|
|
500
|
+
}),
|
|
501
|
+
...(click === undefined ? {} : {
|
|
502
|
+
click: (x, y) => routeTransaction(state, "mutation", "page.mouse.click", state.options.defaultTimeoutMs, () => safeInvocation(click, rawMouse, [x, y]))
|
|
503
|
+
})
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
function makeCua(rawCua, state) {
|
|
507
|
+
const move = optionalCallable(rawCua, "move", "page.cua.move");
|
|
508
|
+
const click = optionalCallable(rawCua, "click", "page.cua.click");
|
|
509
|
+
const keypress = optionalCallable(rawCua, "keypress", "page.cua.keypress");
|
|
510
|
+
return {
|
|
511
|
+
...(move === undefined ? {} : {
|
|
512
|
+
move: (options) => routeTransaction(state, "mutation", "page.cua.move", state.options.defaultTimeoutMs, () => safeInvocation(move, rawCua, [options]))
|
|
513
|
+
}),
|
|
514
|
+
...(click === undefined ? {} : {
|
|
515
|
+
click: (options) => routeTransaction(state, "mutation", "page.cua.click", state.options.defaultTimeoutMs, () => safeInvocation(click, rawCua, [options]))
|
|
516
|
+
}),
|
|
517
|
+
...(keypress === undefined ? {} : {
|
|
518
|
+
keypress: (options) => routeTransaction(state, "mutation", "page.cua.keypress", state.options.defaultTimeoutMs, () => safeInvocation(keypress, rawCua, [options]))
|
|
519
|
+
})
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
function makeCapabilities(rawCapabilities, state) {
|
|
523
|
+
const get = optionalCallable(rawCapabilities, "get", "page.capabilities.get");
|
|
524
|
+
if (get === undefined)
|
|
525
|
+
return {};
|
|
526
|
+
return {
|
|
527
|
+
get: (id) => routeTransaction(state, "read", "page.capabilities.get", state.options.defaultTimeoutMs, async () => {
|
|
528
|
+
const value = await safeInvocation(get, rawCapabilities, [id]);
|
|
529
|
+
return isObjectLike(value) ? wrapCapability(value, state, "page.capabilities.result", 0) : value;
|
|
530
|
+
})
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
function buildPage(state) {
|
|
534
|
+
const rawPage = state.rawPage;
|
|
535
|
+
const wrapper = {};
|
|
536
|
+
rawValues.set(wrapper, rawPage);
|
|
537
|
+
for (const property of ["id", "tabId"]) {
|
|
538
|
+
const value = readDataMember(rawPage, property, `page.${property}`);
|
|
539
|
+
if (value !== undefined)
|
|
540
|
+
wrapper[property] = value;
|
|
541
|
+
}
|
|
542
|
+
const locator = optionalCallable(rawPage, "locator", "page.locator");
|
|
543
|
+
if (locator !== undefined)
|
|
544
|
+
wrapper.locator = (selector) => wrapLocator(safeInvocation(locator, rawPage, [selector]), state, "page.locator");
|
|
545
|
+
const getByRole = optionalCallable(rawPage, "getByRole", "page.getByRole");
|
|
546
|
+
if (getByRole !== undefined)
|
|
547
|
+
wrapper.getByRole = (role, options) => wrapLocator(safeInvocation(getByRole, rawPage, [role, options]), state, "page.getByRole");
|
|
548
|
+
const getByPlaceholder = optionalCallable(rawPage, "getByPlaceholder", "page.getByPlaceholder");
|
|
549
|
+
if (getByPlaceholder !== undefined)
|
|
550
|
+
wrapper.getByPlaceholder = (text, options) => wrapLocator(safeInvocation(getByPlaceholder, rawPage, [text, options]), state, "page.getByPlaceholder");
|
|
551
|
+
const getByText = optionalCallable(rawPage, "getByText", "page.getByText");
|
|
552
|
+
if (getByText !== undefined)
|
|
553
|
+
wrapper.getByText = (text, options) => wrapLocator(safeInvocation(getByText, rawPage, [text, options]), state, "page.getByText");
|
|
554
|
+
const url = optionalCallable(rawPage, "url", "page.url");
|
|
555
|
+
if (url !== undefined)
|
|
556
|
+
wrapper.url = () => routeTransaction(state, "read", "page.url", state.options.defaultTimeoutMs, () => safeInvocation(url, rawPage, []));
|
|
557
|
+
const title = optionalCallable(rawPage, "title", "page.title");
|
|
558
|
+
if (title !== undefined)
|
|
559
|
+
wrapper.title = () => routeTransaction(state, "read", "page.title", state.options.defaultTimeoutMs, () => safeInvocation(title, rawPage, []));
|
|
560
|
+
const goto = optionalCallable(rawPage, "goto", "page.goto");
|
|
561
|
+
if (goto !== undefined)
|
|
562
|
+
wrapper.goto = (urlValue, options) => routeTransaction(state, "mutation", "page.goto", timeoutFromArg(options, "page.goto") ?? state.options.defaultTimeoutMs, () => safeInvocation(goto, rawPage, [urlValue, options]));
|
|
563
|
+
const evaluate = optionalCallable(rawPage, "evaluate", "page.evaluate");
|
|
564
|
+
if (evaluate !== undefined)
|
|
565
|
+
wrapper.evaluate = (fn, arg, options) => routeTransaction(state, "mutation", "page.evaluate", timeoutFromArg(options, "page.evaluate") ?? state.options.defaultTimeoutMs, () => safeInvocation(evaluate, rawPage, [fn, arg, options]));
|
|
566
|
+
const content = optionalCallable(rawPage, "content", "page.content");
|
|
567
|
+
if (content !== undefined)
|
|
568
|
+
wrapper.content = (options) => routeTransaction(state, "read", "page.content", timeoutFromArg(options, "page.content") ?? state.options.defaultTimeoutMs, () => safeInvocation(content, rawPage, [options]));
|
|
569
|
+
const close = optionalCallable(rawPage, "close", "page.close");
|
|
570
|
+
if (close !== undefined)
|
|
571
|
+
wrapper.close = () => routeTransaction(state, "mutation", "page.close", state.options.defaultTimeoutMs, () => safeInvocation(close, rawPage, []));
|
|
572
|
+
const waitForTimeout = optionalCallable(rawPage, "waitForTimeout", "page.waitForTimeout");
|
|
573
|
+
if (waitForTimeout !== undefined)
|
|
574
|
+
wrapper.waitForTimeout = (milliseconds) => {
|
|
575
|
+
const result = safeInvocation(waitForTimeout, rawPage, [milliseconds]);
|
|
576
|
+
return absorbRejection(Promise.resolve(result));
|
|
577
|
+
};
|
|
578
|
+
const waitForEventMethod = optionalCallable(rawPage, "waitForEvent", "page.waitForEvent");
|
|
579
|
+
if (waitForEventMethod !== undefined)
|
|
580
|
+
wrapper.waitForEvent = (event, optionsOrCallback) => waitForEvent(state, rawPage, event, optionsOrCallback);
|
|
581
|
+
const keyboard = readDataMember(rawPage, "keyboard", "page.keyboard");
|
|
582
|
+
if (keyboard !== undefined)
|
|
583
|
+
wrapper.keyboard = makeKeyboard(requiredRecord(keyboard, "page.keyboard"), state);
|
|
584
|
+
const mouse = readDataMember(rawPage, "mouse", "page.mouse");
|
|
585
|
+
if (mouse !== undefined)
|
|
586
|
+
wrapper.mouse = makeMouse(requiredRecord(mouse, "page.mouse"), state);
|
|
587
|
+
const cua = readDataMember(rawPage, "cua", "page.cua");
|
|
588
|
+
if (cua !== undefined)
|
|
589
|
+
wrapper.cua = makeCua(requiredRecord(cua, "page.cua"), state);
|
|
590
|
+
const capabilities = readDataMember(rawPage, "capabilities", "page.capabilities");
|
|
591
|
+
if (capabilities !== undefined)
|
|
592
|
+
wrapper.capabilities = makeCapabilities(requiredRecord(capabilities, "page.capabilities"), state);
|
|
593
|
+
const playwright = readDataMember(rawPage, "playwright", "page.playwright");
|
|
594
|
+
if (playwright !== undefined)
|
|
595
|
+
wrapper.playwright = wrapPlaywright(requiredRecord(playwright, "page.playwright"), state, "page.playwright");
|
|
596
|
+
return wrapper;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Wrap one provider page. The raw page is never enumerated or copied; only
|
|
600
|
+
* the documented PageLike members are read through bounded data descriptors.
|
|
601
|
+
* Locator construction remains synchronous, while each locator action is a
|
|
602
|
+
* separate short coordinator transaction.
|
|
603
|
+
*/
|
|
604
|
+
export function createCoordinatedPage(page, options) {
|
|
605
|
+
if (!isObjectLike(page))
|
|
606
|
+
return invalid("page must be a provider PageLike object");
|
|
607
|
+
const normalized = normalizeOptions(options);
|
|
608
|
+
const cache = getPageCache(page, normalized.coordinator);
|
|
609
|
+
const affinity = makeCacheKey(normalized);
|
|
610
|
+
const existing = cache.get(affinity)?.deref();
|
|
611
|
+
if (existing !== undefined) {
|
|
612
|
+
cachePage(cache, affinity, existing);
|
|
613
|
+
return existing;
|
|
614
|
+
}
|
|
615
|
+
cache.delete(affinity);
|
|
616
|
+
const state = {
|
|
617
|
+
rawPage: page,
|
|
618
|
+
options: normalized,
|
|
619
|
+
locatorWrappers: new WeakMap(),
|
|
620
|
+
capabilityWrappers: new WeakMap(),
|
|
621
|
+
fileChooserWrappers: new WeakMap(),
|
|
622
|
+
playwrightWrappers: new WeakMap()
|
|
623
|
+
};
|
|
624
|
+
const wrapper = buildPage(state);
|
|
625
|
+
cachePage(cache, affinity, wrapper);
|
|
626
|
+
rawValues.set(wrapper, page);
|
|
627
|
+
return wrapper;
|
|
628
|
+
}
|
|
629
|
+
/** Explicit identity seam for integrations that need the underlying provider page. */
|
|
630
|
+
export function unwrapCoordinatedPage(page) {
|
|
631
|
+
if (!isObjectLike(page))
|
|
632
|
+
return page;
|
|
633
|
+
return rawValues.get(page) ?? page;
|
|
634
|
+
}
|