@deepseek-ai/dsh-experimental-inspector 0.1.6-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +156 -0
- package/README.zh.md +156 -0
- package/lib/client.js +3424 -0
- package/lib/index.js +2406 -0
- package/lib/types/client/bridge/controller.d.ts +10 -0
- package/lib/types/client/bridge/dispatcher.d.ts +26 -0
- package/lib/types/client/bridge/lifecycle.d.ts +20 -0
- package/lib/types/client/bridge/publisher.d.ts +42 -0
- package/lib/types/client/bridge/rpc.d.ts +13 -0
- package/lib/types/client/bridge/transport.d.ts +34 -0
- package/lib/types/client/cdp/console.d.ts +45 -0
- package/lib/types/client/cdp/debugger.d.ts +8 -0
- package/lib/types/client/cdp/errors.d.ts +8 -0
- package/lib/types/client/cdp/heap-profiler.d.ts +8 -0
- package/lib/types/client/cdp/index.d.ts +10 -0
- package/lib/types/client/cdp/objects.d.ts +69 -0
- package/lib/types/client/cdp/profiler.d.ts +8 -0
- package/lib/types/client/cdp/properties.d.ts +16 -0
- package/lib/types/client/cdp/runtime.d.ts +79 -0
- package/lib/types/client/cdp/sources.d.ts +52 -0
- package/lib/types/client/cdp/stack.d.ts +27 -0
- package/lib/types/client/index.d.ts +3 -0
- package/lib/types/client/inspection/cordis.d.ts +3 -0
- package/lib/types/client/inspection/network.d.ts +4 -0
- package/lib/types/client/inspection/realm.d.ts +27 -0
- package/lib/types/client/plugin.d.ts +28 -0
- package/lib/types/host/bridge/controller.d.ts +108 -0
- package/lib/types/host/bridge/dispatcher.d.ts +16 -0
- package/lib/types/host/bridge/lifecycle.d.ts +42 -0
- package/lib/types/host/bridge/publisher.d.ts +31 -0
- package/lib/types/host/bridge/rpc.d.ts +15 -0
- package/lib/types/host/bridge/transport.d.ts +28 -0
- package/lib/types/host/cdp/console.d.ts +14 -0
- package/lib/types/host/cdp/debugger.d.ts +8 -0
- package/lib/types/host/cdp/errors.d.ts +6 -0
- package/lib/types/host/cdp/heap-profiler.d.ts +8 -0
- package/lib/types/host/cdp/index.d.ts +10 -0
- package/lib/types/host/cdp/objects.d.ts +8 -0
- package/lib/types/host/cdp/profiler.d.ts +8 -0
- package/lib/types/host/cdp/properties.d.ts +7 -0
- package/lib/types/host/cdp/runtime.d.ts +16 -0
- package/lib/types/host/cdp/sources.d.ts +14 -0
- package/lib/types/host/cdp/stack.d.ts +4 -0
- package/lib/types/host/index.d.ts +3 -0
- package/lib/types/host/inspection/cordis.d.ts +3 -0
- package/lib/types/host/inspection/network.d.ts +23 -0
- package/lib/types/host/inspection/realm.d.ts +9 -0
- package/lib/types/host/plugin.d.ts +19 -0
- package/lib/types/index.d.ts +50 -0
- package/lib/types/shared/bridge/buffer.d.ts +62 -0
- package/lib/types/shared/bridge/codec.d.ts +4 -0
- package/lib/types/shared/bridge/control-codec.d.ts +27 -0
- package/lib/types/shared/bridge/ids.d.ts +19 -0
- package/lib/types/shared/bridge/messages/control.d.ts +64 -0
- package/lib/types/shared/bridge/messages/cordis.d.ts +4 -0
- package/lib/types/shared/bridge/messages/network.d.ts +4 -0
- package/lib/types/shared/bridge/messages/observation.d.ts +110 -0
- package/lib/types/shared/bridge/messages/query/codec.d.ts +40 -0
- package/lib/types/shared/bridge/messages/query/commands.d.ts +35 -0
- package/lib/types/shared/bridge/messages/query/frames.d.ts +31 -0
- package/lib/types/shared/bridge/messages/query/index.d.ts +5 -0
- package/lib/types/shared/bridge/messages/runtime/command-codec.d.ts +9 -0
- package/lib/types/shared/bridge/messages/runtime/commands.d.ts +76 -0
- package/lib/types/shared/bridge/messages/runtime/console-frames.d.ts +52 -0
- package/lib/types/shared/bridge/messages/runtime/frames.d.ts +98 -0
- package/lib/types/shared/bridge/messages/runtime/index.d.ts +5 -0
- package/lib/types/shared/bridge/messages/runtime/value-codec.d.ts +28 -0
- package/lib/types/shared/bridge/messages/sources/codec.d.ts +15 -0
- package/lib/types/shared/bridge/messages/sources/commands.d.ts +42 -0
- package/lib/types/shared/bridge/messages/sources/frames.d.ts +67 -0
- package/lib/types/shared/bridge/messages/sources/index.d.ts +5 -0
- package/lib/types/shared/bridge/publisher.d.ts +33 -0
- package/lib/types/shared/bridge/query-reader.d.ts +10 -0
- package/lib/types/shared/bridge/rpc.d.ts +62 -0
- package/lib/types/shared/bridge/validation.d.ts +3 -0
- package/lib/types/shared/bridge/version.d.ts +3 -0
- package/lib/types/shared/cdp/capabilities.d.ts +17 -0
- package/lib/types/shared/cdp/console.d.ts +28 -0
- package/lib/types/shared/cdp/debugger.d.ts +69 -0
- package/lib/types/shared/cdp/errors.d.ts +27 -0
- package/lib/types/shared/cdp/ids.d.ts +9 -0
- package/lib/types/shared/cdp/index.d.ts +11 -0
- package/lib/types/shared/cdp/operations.d.ts +86 -0
- package/lib/types/shared/cdp/property.d.ts +28 -0
- package/lib/types/shared/cdp/realm.d.ts +137 -0
- package/lib/types/shared/cdp/remote-object.d.ts +44 -0
- package/lib/types/shared/cdp/sources.d.ts +18 -0
- package/lib/types/shared/cordis/collector.d.ts +26 -0
- package/lib/types/shared/cordis/ids.d.ts +7 -0
- package/lib/types/shared/cordis/model.d.ts +61 -0
- package/lib/types/shared/cordis/object-reference.d.ts +14 -0
- package/lib/types/shared/cordis/object-registry.d.ts +80 -0
- package/lib/types/shared/cordis/observer.d.ts +15 -0
- package/lib/types/shared/cordis/projector.d.ts +34 -0
- package/lib/types/shared/cordis/publisher.d.ts +13 -0
- package/lib/types/shared/cordis/reader.d.ts +18 -0
- package/lib/types/shared/cordis/snapshot.d.ts +39 -0
- package/lib/types/shared/identity.d.ts +12 -0
- package/lib/types/shared/index.d.ts +18 -0
- package/lib/types/shared/json.d.ts +35 -0
- package/lib/types/shared/network/event-source.d.ts +20 -0
- package/lib/types/shared/network/observation.d.ts +52 -0
- package/lib/types/shared/service.d.ts +23 -0
- package/lib/types/shared/validation.d.ts +52 -0
- package/lib/types/worker/bridge/endpoint.d.ts +49 -0
- package/lib/types/worker/bridge/hub.d.ts +108 -0
- package/lib/types/worker/bridge/runtime-rpc.d.ts +90 -0
- package/lib/types/worker/bridge/session.d.ts +15 -0
- package/lib/types/worker/bridge/source-rpc.d.ts +42 -0
- package/lib/types/worker/cdp/domains/debugger/cdp-params.d.ts +15 -0
- package/lib/types/worker/cdp/domains/debugger/index.d.ts +6 -0
- package/lib/types/worker/cdp/domains/debugger/projector.d.ts +22 -0
- package/lib/types/worker/cdp/domains/debugger/script-registry.d.ts +70 -0
- package/lib/types/worker/cdp/domains/debugger/session.d.ts +47 -0
- package/lib/types/worker/cdp/domains/dom/index.d.ts +4 -0
- package/lib/types/worker/cdp/domains/dom/model.d.ts +108 -0
- package/lib/types/worker/cdp/domains/dom/session.d.ts +74 -0
- package/lib/types/worker/cdp/domains/native.d.ts +25 -0
- package/lib/types/worker/cdp/domains/network/session.d.ts +46 -0
- package/lib/types/worker/cdp/domains/runtime/cdp-params.d.ts +82 -0
- package/lib/types/worker/cdp/domains/runtime/index.d.ts +3 -0
- package/lib/types/worker/cdp/domains/runtime/object-table.d.ts +117 -0
- package/lib/types/worker/cdp/domains/runtime/session.d.ts +97 -0
- package/lib/types/worker/cdp/ids.d.ts +37 -0
- package/lib/types/worker/cdp/protocol.d.ts +46 -0
- package/lib/types/worker/cdp/realm-sessions.d.ts +64 -0
- package/lib/types/worker/cdp/session.d.ts +34 -0
- package/lib/types/worker/cdp/target.d.ts +17 -0
- package/lib/types/worker/entry.d.ts +3 -0
- package/lib/types/worker/inspection/cordis-query.d.ts +11 -0
- package/lib/types/worker/inspection/cordis-store.d.ts +94 -0
- package/lib/types/worker/inspection/network-store.d.ts +110 -0
- package/lib/types/worker/inspection/query-router.d.ts +78 -0
- package/lib/types/worker/inspection/realm-store.d.ts +58 -0
- package/lib/types/worker/inspection/realm.d.ts +43 -0
- package/lib/types/worker/realms/client/bridge.d.ts +18 -0
- package/lib/types/worker/realms/client/console.d.ts +21 -0
- package/lib/types/worker/realms/client/debugger.d.ts +8 -0
- package/lib/types/worker/realms/client/index.d.ts +18 -0
- package/lib/types/worker/realms/client/runtime.d.ts +28 -0
- package/lib/types/worker/realms/client/scripts.d.ts +15 -0
- package/lib/types/worker/realms/client/sources.d.ts +30 -0
- package/lib/types/worker/realms/client/values.d.ts +55 -0
- package/lib/types/worker/realms/host/bridge.d.ts +49 -0
- package/lib/types/worker/realms/host/console.d.ts +25 -0
- package/lib/types/worker/realms/host/debugger.d.ts +25 -0
- package/lib/types/worker/realms/host/index.d.ts +13 -0
- package/lib/types/worker/realms/host/runtime.d.ts +54 -0
- package/lib/types/worker/realms/host/scripts.d.ts +9 -0
- package/lib/types/worker/realms/host/sources.d.ts +26 -0
- package/lib/types/worker/realms/host/values.d.ts +22 -0
- package/lib/types/worker/server.d.ts +16 -0
- package/lib/worker.js +7184 -0
- package/package.json +64 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,3424 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@deepseek-ai/dsh-experimental-inspector",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let _deepseek_ai_cordis = require("@deepseek-ai/cordis");
|
|
8
|
+
//#region lib/types/shared/json.js
|
|
9
|
+
/** JSON values admitted by every Inspector cross-realm message. */
|
|
10
|
+
/**
|
|
11
|
+
* Test that a value can cross both MessagePort and JSON WebSocket carriers without coercion.
|
|
12
|
+
* @param value - Candidate wire value.
|
|
13
|
+
* @returns Whether the value is lossless JSON data.
|
|
14
|
+
*/
|
|
15
|
+
function isJsonValue(value) {
|
|
16
|
+
return visitJson(value, /* @__PURE__ */ new Set());
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Compute the UTF-8 byte length of a JSON wire value.
|
|
20
|
+
* @param value - Validated JSON value.
|
|
21
|
+
* @returns Its encoded byte length.
|
|
22
|
+
*/
|
|
23
|
+
function jsonByteLength(value) {
|
|
24
|
+
return new TextEncoder().encode(JSON.stringify(value)).byteLength;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Test whether a value is a plain object with string own keys.
|
|
28
|
+
* @param value - Candidate object.
|
|
29
|
+
* @returns Whether the value has `Object.prototype` or a null prototype.
|
|
30
|
+
*/
|
|
31
|
+
function isPlainObject(value) {
|
|
32
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
33
|
+
const prototype = Reflect.getPrototypeOf(value);
|
|
34
|
+
return prototype === Object.prototype || prototype === null;
|
|
35
|
+
}
|
|
36
|
+
function visitJson(value, ancestors) {
|
|
37
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
38
|
+
if (typeof value === "number") return Number.isFinite(value) && !Object.is(value, -0);
|
|
39
|
+
if (typeof value !== "object" || ancestors.has(value)) return false;
|
|
40
|
+
ancestors.add(value);
|
|
41
|
+
try {
|
|
42
|
+
if (Array.isArray(value)) {
|
|
43
|
+
if (Object.getPrototypeOf(value) !== Array.prototype || Reflect.ownKeys(value).length !== value.length + 1) return false;
|
|
44
|
+
return value.every((item) => visitJson(item, ancestors));
|
|
45
|
+
}
|
|
46
|
+
if (!isPlainObject(value)) return false;
|
|
47
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
48
|
+
if (typeof key !== "string") return false;
|
|
49
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
50
|
+
if (descriptor?.enumerable !== true || !("value" in descriptor) || !visitJson(descriptor.value, ancestors)) return false;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
} finally {
|
|
54
|
+
ancestors.delete(value);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region lib/types/shared/identity.js
|
|
59
|
+
/** Shared branded-identifier construction without assigning protocol ownership. */
|
|
60
|
+
/**
|
|
61
|
+
* Validate and brand a non-empty identifier received from or sent across a runtime boundary.
|
|
62
|
+
* @param value - Untrusted identifier text.
|
|
63
|
+
* @param label - Field name used in validation errors.
|
|
64
|
+
* @returns The role-branded identifier.
|
|
65
|
+
*/
|
|
66
|
+
function inspectorId(value, label) {
|
|
67
|
+
if (value.length === 0 || value.length > 256) throw new Error(`inspector protocol: ${label} must contain 1 to 256 characters`);
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region lib/types/shared/validation.js
|
|
72
|
+
/** Shared exact-object readers for versioned Inspector wire protocols. */
|
|
73
|
+
/**
|
|
74
|
+
* Require a plain object containing only the listed fields.
|
|
75
|
+
* @param value - Candidate object.
|
|
76
|
+
* @param keys - Complete field allowlist.
|
|
77
|
+
* @param label - Object name used in validation errors.
|
|
78
|
+
* @returns The validated plain object.
|
|
79
|
+
*/
|
|
80
|
+
function exactObject(value, keys, label) {
|
|
81
|
+
if (!isPlainObject(value)) throw new Error(`inspector protocol: ${label} must be an object`);
|
|
82
|
+
exactKeys(value, keys, label);
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Reject fields outside one versioned object's declared field set.
|
|
87
|
+
* @param value - Plain object being validated.
|
|
88
|
+
* @param keys - Complete field allowlist.
|
|
89
|
+
* @param label - Object name used in validation errors.
|
|
90
|
+
*/
|
|
91
|
+
function exactKeys(value, keys, label) {
|
|
92
|
+
const allowed = new Set(keys);
|
|
93
|
+
for (const key of Reflect.ownKeys(value)) if (typeof key !== "string" || !allowed.has(key)) throw new Error(`inspector protocol: ${label} has unknown field ${JSON.stringify(String(key))}`);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Read one non-empty opaque identifier.
|
|
97
|
+
* @param value - Candidate identifier.
|
|
98
|
+
* @param label - Field name used in validation errors.
|
|
99
|
+
* @returns The role-branded identifier.
|
|
100
|
+
*/
|
|
101
|
+
function wireId(value, label) {
|
|
102
|
+
if (typeof value !== "string") throw new Error(`inspector protocol: ${label} must be a string`);
|
|
103
|
+
return inspectorId(value, label);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Read one optional string field.
|
|
107
|
+
* @param value - Object containing the field.
|
|
108
|
+
* @param key - Field name.
|
|
109
|
+
* @returns An empty object or the validated field.
|
|
110
|
+
*/
|
|
111
|
+
function optionalString(value, key) {
|
|
112
|
+
const item = value[key];
|
|
113
|
+
if (item === void 0) return {};
|
|
114
|
+
if (typeof item !== "string") throw new Error(`inspector protocol: ${key} must be a string`);
|
|
115
|
+
return { [key]: item };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Read one optional boolean field.
|
|
119
|
+
* @param value - Object containing the field.
|
|
120
|
+
* @param key - Field name.
|
|
121
|
+
* @returns An empty object or the validated field.
|
|
122
|
+
*/
|
|
123
|
+
function optionalBoolean(value, key) {
|
|
124
|
+
const item = value[key];
|
|
125
|
+
if (item === void 0) return {};
|
|
126
|
+
if (typeof item !== "boolean") throw new Error(`inspector protocol: ${key} must be a boolean`);
|
|
127
|
+
return { [key]: item };
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Read one optional non-negative finite number field.
|
|
131
|
+
* @param value - Object containing the field.
|
|
132
|
+
* @param key - Field name.
|
|
133
|
+
* @returns An empty object or the validated field.
|
|
134
|
+
*/
|
|
135
|
+
function optionalNonNegativeNumber(value, key) {
|
|
136
|
+
const item = value[key];
|
|
137
|
+
if (item === void 0) return {};
|
|
138
|
+
if (typeof item !== "number" || !Number.isFinite(item) || item < 0) throw new Error(`inspector protocol: ${key} must be a non-negative finite number`);
|
|
139
|
+
return { [key]: item };
|
|
140
|
+
}
|
|
141
|
+
//#endregion
|
|
142
|
+
//#region lib/types/shared/bridge/control-codec.js
|
|
143
|
+
/**
|
|
144
|
+
* Decode bootstrap data injected into the browser global.
|
|
145
|
+
* @param value - Untrusted injected value.
|
|
146
|
+
* @returns The validated Client bootstrap.
|
|
147
|
+
*/
|
|
148
|
+
function parseInspectorClientBootstrap(value) {
|
|
149
|
+
const record = exactObject(value, [
|
|
150
|
+
"endpoint",
|
|
151
|
+
"protocol",
|
|
152
|
+
"maxQueuedRecords",
|
|
153
|
+
"maxQueuedBytes",
|
|
154
|
+
"maxRecordsPerFrame",
|
|
155
|
+
"maxFrameBytes",
|
|
156
|
+
"reconnectBaseMs",
|
|
157
|
+
"reconnectMaxMs",
|
|
158
|
+
"queryTimeoutMs",
|
|
159
|
+
"maxRuntimeObjectsPerSession",
|
|
160
|
+
"maxRuntimePropertiesPerResult",
|
|
161
|
+
"maxCordisNodes",
|
|
162
|
+
"maxClientSourceBytes"
|
|
163
|
+
], "Client bootstrap");
|
|
164
|
+
if (typeof record.endpoint !== "string" || typeof record.protocol !== "string") throw new Error("inspector protocol: Client bootstrap endpoint and protocol must be strings");
|
|
165
|
+
let endpoint;
|
|
166
|
+
try {
|
|
167
|
+
endpoint = new URL(record.endpoint);
|
|
168
|
+
} catch {
|
|
169
|
+
throw new Error("inspector protocol: Client bootstrap endpoint must be an absolute URL");
|
|
170
|
+
}
|
|
171
|
+
if (endpoint.protocol !== "ws:" || endpoint.hostname !== "127.0.0.1") throw new Error("inspector protocol: Client bootstrap endpoint must use ws on 127.0.0.1");
|
|
172
|
+
if (record.protocol.length === 0 || record.protocol.length > 256) throw new Error("inspector protocol: Client bootstrap protocol must contain 1 to 256 characters");
|
|
173
|
+
const bootstrap = {
|
|
174
|
+
endpoint: record.endpoint,
|
|
175
|
+
protocol: record.protocol,
|
|
176
|
+
maxQueuedRecords: natural$2(record.maxQueuedRecords, "maxQueuedRecords"),
|
|
177
|
+
maxQueuedBytes: natural$2(record.maxQueuedBytes, "maxQueuedBytes"),
|
|
178
|
+
maxRecordsPerFrame: natural$2(record.maxRecordsPerFrame, "maxRecordsPerFrame"),
|
|
179
|
+
maxFrameBytes: natural$2(record.maxFrameBytes, "maxFrameBytes"),
|
|
180
|
+
reconnectBaseMs: natural$2(record.reconnectBaseMs, "reconnectBaseMs"),
|
|
181
|
+
reconnectMaxMs: natural$2(record.reconnectMaxMs, "reconnectMaxMs"),
|
|
182
|
+
queryTimeoutMs: natural$2(record.queryTimeoutMs, "queryTimeoutMs"),
|
|
183
|
+
maxRuntimeObjectsPerSession: natural$2(record.maxRuntimeObjectsPerSession, "maxRuntimeObjectsPerSession"),
|
|
184
|
+
maxRuntimePropertiesPerResult: natural$2(record.maxRuntimePropertiesPerResult, "maxRuntimePropertiesPerResult"),
|
|
185
|
+
maxClientSourceBytes: natural$2(record.maxClientSourceBytes, "maxClientSourceBytes"),
|
|
186
|
+
maxCordisNodes: natural$2(record.maxCordisNodes, "maxCordisNodes")
|
|
187
|
+
};
|
|
188
|
+
if (bootstrap.reconnectMaxMs < bootstrap.reconnectBaseMs) throw new Error("inspector protocol: reconnectMaxMs must be at least reconnectBaseMs");
|
|
189
|
+
return bootstrap;
|
|
190
|
+
}
|
|
191
|
+
function natural$2(value, label, zero = false) {
|
|
192
|
+
if (!Number.isSafeInteger(value) || value < (zero ? 0 : 1)) throw new Error(`inspector protocol: ${label} must be ${zero ? "a non-negative" : "a positive"} safe integer`);
|
|
193
|
+
return value;
|
|
194
|
+
}
|
|
195
|
+
//#endregion
|
|
196
|
+
//#region lib/types/shared/bridge/query-reader.js
|
|
197
|
+
/** Query-backed adapter for the transport-independent Cordis tree reader. */
|
|
198
|
+
/**
|
|
199
|
+
* Create a reader that obtains the tree through the typed Inspector query protocol.
|
|
200
|
+
* @param requester - Active Host or Client query connection.
|
|
201
|
+
* @returns A non-CDP Cordis tree reader.
|
|
202
|
+
*/
|
|
203
|
+
function createQueryCordisRuntimeTreeReader(requester) {
|
|
204
|
+
return { async getTree() {
|
|
205
|
+
return (await requester.request({ op: "cordis-tree/get" })).tree;
|
|
206
|
+
} };
|
|
207
|
+
}
|
|
208
|
+
//#endregion
|
|
209
|
+
//#region lib/types/shared/service.js
|
|
210
|
+
/** Cordis service API shared by the Host and Client plugin faces. */
|
|
211
|
+
/**
|
|
212
|
+
* Create the shared service façade without exposing the carrier implementation.
|
|
213
|
+
* @param connection - Realm-local observation and query transport.
|
|
214
|
+
* @returns The Cordis service value.
|
|
215
|
+
*/
|
|
216
|
+
function createInspectorService(connection) {
|
|
217
|
+
return {
|
|
218
|
+
publish: (topic, payload, monotonicMs) => {
|
|
219
|
+
connection.publish(topic, payload, monotonicMs);
|
|
220
|
+
},
|
|
221
|
+
cordis: createQueryCordisRuntimeTreeReader(connection)
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
//#endregion
|
|
225
|
+
//#region lib/types/shared/bridge/messages/cordis.js
|
|
226
|
+
/** Bridge message metadata for Cordis runtime-tree snapshots. */
|
|
227
|
+
/** Observation topic carrying the latest complete Cordis tree. */
|
|
228
|
+
const CORDIS_TREE_TOPIC = "cordis/tree";
|
|
229
|
+
//#endregion
|
|
230
|
+
//#region ../../util/crypto/lib/index.js
|
|
231
|
+
/**
|
|
232
|
+
* UUID minting that works in every JavaScript context this repository ships
|
|
233
|
+
* to. `crypto.randomUUID` is a secure-context Web API — a page or worker
|
|
234
|
+
* served over plain HTTP on a LAN address has no such method — while
|
|
235
|
+
* `crypto.getRandomValues` is unrestricted everywhere (browsers, workers,
|
|
236
|
+
* Node ≥ 19). One implementation here replaces per-caller polyfills; the
|
|
237
|
+
* `no-restricted-properties` lint rule points `crypto.randomUUID` callers at
|
|
238
|
+
* this module.
|
|
239
|
+
* @module @deepseek-ai/dsh-util-crypto
|
|
240
|
+
*/
|
|
241
|
+
/**
|
|
242
|
+
* Encode bytes as canonical base64 without overflowing function argument limits.
|
|
243
|
+
* @param data - Bytes to encode.
|
|
244
|
+
* @returns base64 text.
|
|
245
|
+
*/
|
|
246
|
+
function bytesToBase64(data) {
|
|
247
|
+
let binary = "";
|
|
248
|
+
const chunk = 32768;
|
|
249
|
+
for (let offset = 0; offset < data.length; offset += chunk) binary += String.fromCharCode(...data.subarray(offset, offset + chunk));
|
|
250
|
+
return btoa(binary);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Random v4 UUID, minted from `crypto.getRandomValues`.
|
|
254
|
+
* @returns the UUID string.
|
|
255
|
+
*/
|
|
256
|
+
function randomUUID() {
|
|
257
|
+
const bytes = globalThis.crypto.getRandomValues(new Uint8Array(16));
|
|
258
|
+
const hex = Array.from(bytes, (byte, index) => {
|
|
259
|
+
return (index === 6 ? byte & 15 | 64 : index === 8 ? byte & 63 | 128 : byte).toString(16).padStart(2, "0");
|
|
260
|
+
}).join("");
|
|
261
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
262
|
+
}
|
|
263
|
+
//#endregion
|
|
264
|
+
//#region lib/types/shared/cordis/object-registry.js
|
|
265
|
+
/** Realm-local retention and identity for live objects referenced by Inspector snapshots. */
|
|
266
|
+
const REGISTRIES_SYMBOL = "dsh.inspector.realm-object-registries";
|
|
267
|
+
const MAX_FIBER_WRAPPER_DEPTH = 8;
|
|
268
|
+
`${JSON.stringify(REGISTRIES_SYMBOL)}`;
|
|
269
|
+
/** One realm's bounded table of objects retained by its latest semantic snapshot. */
|
|
270
|
+
var RealmObjectRegistry = class {
|
|
271
|
+
/** Realm-unique id carried by every reference from this registry. */
|
|
272
|
+
id = inspectorId(randomUUID(), "registryId");
|
|
273
|
+
known = /* @__PURE__ */ new WeakMap();
|
|
274
|
+
retained = /* @__PURE__ */ new Map();
|
|
275
|
+
nextHandle = 1;
|
|
276
|
+
disposed = false;
|
|
277
|
+
constructor() {
|
|
278
|
+
registries().set(this.id, this);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Start one replacement generation.
|
|
282
|
+
* @returns A collector that atomically installs exactly the retained objects on commit.
|
|
283
|
+
*/
|
|
284
|
+
begin() {
|
|
285
|
+
if (this.disposed) throw new Error("inspector: realm object registry is disposed");
|
|
286
|
+
return new RealmObjectGeneration(this);
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Resolve one current opaque handle.
|
|
290
|
+
* @param handle - Handle from the latest committed snapshot.
|
|
291
|
+
* @returns The live object, when it remains retained.
|
|
292
|
+
*/
|
|
293
|
+
resolve(handle) {
|
|
294
|
+
return this.retained.get(handle);
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Identify one object retained by the latest snapshot. Cordis plugin calls may return nested thenable facades;
|
|
298
|
+
* only objects whose prototype path consists exclusively of those `then` wrappers resolve to the retained Fiber.
|
|
299
|
+
* @param value - Candidate live value.
|
|
300
|
+
* @returns Its wire reference, when present in this registry.
|
|
301
|
+
*/
|
|
302
|
+
identify(value) {
|
|
303
|
+
if ((typeof value !== "object" || value === null) && typeof value !== "function") return void 0;
|
|
304
|
+
let candidate = value;
|
|
305
|
+
for (let depth = 0; candidate !== null && depth <= MAX_FIBER_WRAPPER_DEPTH; depth++) {
|
|
306
|
+
const handle = this.known.get(candidate);
|
|
307
|
+
if (handle !== void 0 && this.retained.get(handle) === candidate) return {
|
|
308
|
+
registryId: this.id,
|
|
309
|
+
handle
|
|
310
|
+
};
|
|
311
|
+
try {
|
|
312
|
+
const keys = Reflect.ownKeys(candidate);
|
|
313
|
+
if (keys.length !== 1 || keys[0] !== "then") return void 0;
|
|
314
|
+
candidate = Object.getPrototypeOf(candidate);
|
|
315
|
+
} catch {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/** Remove this registry from the realm and release all strong references. */
|
|
321
|
+
close() {
|
|
322
|
+
if (this.disposed) return;
|
|
323
|
+
this.disposed = true;
|
|
324
|
+
registries().delete(this.id);
|
|
325
|
+
this.retained.clear();
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Assign a stable handle and retain a value in one pending generation.
|
|
329
|
+
* @param value - Object represented by the pending snapshot.
|
|
330
|
+
* @param next - Pending generation's strong-reference table.
|
|
331
|
+
* @returns The registry id and stable object handle.
|
|
332
|
+
*/
|
|
333
|
+
retain(value, next) {
|
|
334
|
+
let handle = this.known.get(value);
|
|
335
|
+
if (handle === void 0) {
|
|
336
|
+
handle = inspectorId(`object-${String(this.nextHandle++)}`, "objectHandle");
|
|
337
|
+
this.known.set(value, handle);
|
|
338
|
+
}
|
|
339
|
+
next.set(handle, value);
|
|
340
|
+
return {
|
|
341
|
+
registryId: this.id,
|
|
342
|
+
handle
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Replace the current strong-reference set with one completed generation.
|
|
347
|
+
* @param next - Complete object table for the committed snapshot.
|
|
348
|
+
*/
|
|
349
|
+
commit(next) {
|
|
350
|
+
this.retained = next;
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
/** Mutable object set assembled before one snapshot becomes visible. */
|
|
354
|
+
var RealmObjectGeneration = class {
|
|
355
|
+
owner;
|
|
356
|
+
retained = /* @__PURE__ */ new Map();
|
|
357
|
+
committed = false;
|
|
358
|
+
constructor(owner) {
|
|
359
|
+
this.owner = owner;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Retain one object and obtain its stable opaque reference.
|
|
363
|
+
* @param value - Context or Fiber represented in the snapshot.
|
|
364
|
+
* @returns Source-local wire reference.
|
|
365
|
+
*/
|
|
366
|
+
retain(value) {
|
|
367
|
+
if (this.committed) throw new Error("inspector: realm object generation is already committed");
|
|
368
|
+
return this.owner.retain(value, this.retained);
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Stop retaining an object omitted while bounding the pending snapshot.
|
|
372
|
+
* @param handle - Opaque handle removed from this pending generation.
|
|
373
|
+
*/
|
|
374
|
+
release(handle) {
|
|
375
|
+
if (this.committed) throw new Error("inspector: realm object generation is already committed");
|
|
376
|
+
this.retained.delete(handle);
|
|
377
|
+
}
|
|
378
|
+
/** Atomically replace the registry's retained set. */
|
|
379
|
+
commit() {
|
|
380
|
+
if (this.committed) return;
|
|
381
|
+
this.committed = true;
|
|
382
|
+
this.owner.commit(this.retained);
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
/**
|
|
386
|
+
* Identify a retained object across all Inspector collectors in this realm.
|
|
387
|
+
* @param value - Runtime value returned to a debugger.
|
|
388
|
+
* @returns Its source-local reference, when the value is a visible entity.
|
|
389
|
+
*/
|
|
390
|
+
function identifyRealmObject(value) {
|
|
391
|
+
for (const registry of registries().values()) {
|
|
392
|
+
const reference = registry.identify(value);
|
|
393
|
+
if (reference !== void 0) return reference;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function registries() {
|
|
397
|
+
const key = Symbol.for(REGISTRIES_SYMBOL);
|
|
398
|
+
const existing = Reflect.get(globalThis, key);
|
|
399
|
+
if (existing instanceof Map) return existing;
|
|
400
|
+
const value = /* @__PURE__ */ new Map();
|
|
401
|
+
Reflect.set(globalThis, key, value);
|
|
402
|
+
return value;
|
|
403
|
+
}
|
|
404
|
+
//#endregion
|
|
405
|
+
//#region lib/types/shared/cordis/collector.js
|
|
406
|
+
/** Shared Host/Client projection from live Cordis objects to a bounded semantic tree. */
|
|
407
|
+
const SHADOW = Symbol.for("cordis.shadow");
|
|
408
|
+
/** Realm-local collector with a current live-object table. */
|
|
409
|
+
var CordisTreeCollector = class {
|
|
410
|
+
root;
|
|
411
|
+
limits;
|
|
412
|
+
/** Live-object table replaced atomically with each emitted snapshot. */
|
|
413
|
+
objects = new RealmObjectRegistry();
|
|
414
|
+
revision = 0;
|
|
415
|
+
constructor(root, limits) {
|
|
416
|
+
this.root = root;
|
|
417
|
+
this.limits = limits;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Capture the current reachable Context/Fiber tree.
|
|
421
|
+
* @returns A detached JSON snapshot whose retained objects replace the prior generation atomically.
|
|
422
|
+
*/
|
|
423
|
+
snapshot() {
|
|
424
|
+
const collected = collectContexts(this.root);
|
|
425
|
+
const tree = collected.root;
|
|
426
|
+
const objects = this.objects.begin();
|
|
427
|
+
let nodeCount = 0;
|
|
428
|
+
let truncated = collected.truncated;
|
|
429
|
+
const contextNode = (info) => {
|
|
430
|
+
if (nodeCount >= this.limits.maxNodes) {
|
|
431
|
+
truncated = true;
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
nodeCount++;
|
|
435
|
+
const node = {
|
|
436
|
+
kind: "context",
|
|
437
|
+
objectHandle: objects.retain(info.value).handle,
|
|
438
|
+
children: []
|
|
439
|
+
};
|
|
440
|
+
for (const child of info.children) if (child.fiber !== void 0 && child.fiber.ctx === child.value) {
|
|
441
|
+
const projected = fiberNode(child.fiber, child);
|
|
442
|
+
if (projected !== void 0) node.children.push(projected);
|
|
443
|
+
} else {
|
|
444
|
+
const projected = contextNode(child);
|
|
445
|
+
if (projected !== void 0) node.children.push(projected);
|
|
446
|
+
}
|
|
447
|
+
return node;
|
|
448
|
+
};
|
|
449
|
+
const fiberNode = (fiber, owned) => {
|
|
450
|
+
if (fiber.uid === null) return void 0;
|
|
451
|
+
if (nodeCount + 2 > this.limits.maxNodes) {
|
|
452
|
+
truncated = true;
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
nodeCount++;
|
|
456
|
+
const context = contextNode(owned);
|
|
457
|
+
return {
|
|
458
|
+
kind: "fiber",
|
|
459
|
+
objectHandle: objects.retain(fiber).handle,
|
|
460
|
+
uid: fiber.uid,
|
|
461
|
+
children: [context]
|
|
462
|
+
};
|
|
463
|
+
};
|
|
464
|
+
const root = contextNode(tree);
|
|
465
|
+
if (root === void 0) throw new Error("inspector: maxNodes cannot retain the root Context");
|
|
466
|
+
let snapshot = {
|
|
467
|
+
schemaVersion: 0,
|
|
468
|
+
revision: ++this.revision,
|
|
469
|
+
objectRegistryId: this.objects.id,
|
|
470
|
+
root,
|
|
471
|
+
truncated
|
|
472
|
+
};
|
|
473
|
+
while (jsonByteLength(snapshot) > this.limits.maxBytes) {
|
|
474
|
+
const removed = pruneLast(root);
|
|
475
|
+
if (removed.length === 0) break;
|
|
476
|
+
for (const handle of removed) objects.release(handle);
|
|
477
|
+
snapshot = {
|
|
478
|
+
...snapshot,
|
|
479
|
+
truncated: true
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
if (jsonByteLength(snapshot) > this.limits.maxBytes) throw new Error("inspector: Cordis root exceeds the source-frame byte limit");
|
|
483
|
+
objects.commit();
|
|
484
|
+
return snapshot;
|
|
485
|
+
}
|
|
486
|
+
/** Release the realm-global resolver and every retained object. */
|
|
487
|
+
close() {
|
|
488
|
+
this.objects.close();
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
function collectContexts(root) {
|
|
492
|
+
const contexts = /* @__PURE__ */ new Map();
|
|
493
|
+
let truncated = false;
|
|
494
|
+
const ensure = (candidate, depth = 0) => {
|
|
495
|
+
if (depth > 100) {
|
|
496
|
+
truncated = true;
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
const value = unwrapContext(candidate);
|
|
500
|
+
if (!_deepseek_ai_cordis.Context.is(value)) return void 0;
|
|
501
|
+
const existing = contexts.get(value);
|
|
502
|
+
if (existing !== void 0) return existing;
|
|
503
|
+
if (value === root) {
|
|
504
|
+
const info = describeContext(value);
|
|
505
|
+
contexts.set(value, info);
|
|
506
|
+
return info;
|
|
507
|
+
}
|
|
508
|
+
const parent = ensure(unwrapContext(Object.getPrototypeOf(value)), depth + 1);
|
|
509
|
+
if (parent === void 0) return void 0;
|
|
510
|
+
const info = describeContext(value);
|
|
511
|
+
contexts.set(value, info);
|
|
512
|
+
parent.children.push(info);
|
|
513
|
+
return info;
|
|
514
|
+
};
|
|
515
|
+
const rootInfo = ensure(root);
|
|
516
|
+
for (const runtime of root.registry.values()) for (const fiber of runtime.fibers) {
|
|
517
|
+
if (fiber.uid === null) continue;
|
|
518
|
+
ensure(fiber.parent);
|
|
519
|
+
ensure(fiber.ctx);
|
|
520
|
+
}
|
|
521
|
+
for (const key of Reflect.ownKeys(root.events._hooks)) for (const hook of root.events._hooks[key] ?? []) ensure(hook.ctx);
|
|
522
|
+
const order = (info) => info.fiber?.uid ?? Number.MAX_SAFE_INTEGER;
|
|
523
|
+
for (const info of contexts.values()) info.children.sort((left, right) => order(left) - order(right));
|
|
524
|
+
return {
|
|
525
|
+
root: rootInfo,
|
|
526
|
+
truncated
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
function describeContext(value) {
|
|
530
|
+
return {
|
|
531
|
+
value,
|
|
532
|
+
children: [],
|
|
533
|
+
fiber: ownValue(value, "fiber")
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
function ownValue(value, key) {
|
|
537
|
+
return Reflect.getOwnPropertyDescriptor(value, key)?.value;
|
|
538
|
+
}
|
|
539
|
+
function unwrapContext(value) {
|
|
540
|
+
let current = value;
|
|
541
|
+
while (typeof current === "object" && current !== null && Object.hasOwn(current, SHADOW)) current = Object.getPrototypeOf(current);
|
|
542
|
+
return current;
|
|
543
|
+
}
|
|
544
|
+
function pruneLast(context) {
|
|
545
|
+
const child = context.children.at(-1);
|
|
546
|
+
if (child === void 0) return [];
|
|
547
|
+
if (child.kind === "context") {
|
|
548
|
+
const nested = pruneLast(child);
|
|
549
|
+
if (nested.length > 0) return nested;
|
|
550
|
+
context.children.pop();
|
|
551
|
+
return [child.objectHandle];
|
|
552
|
+
}
|
|
553
|
+
const owned = child.children[0];
|
|
554
|
+
const nested = pruneLast(owned);
|
|
555
|
+
if (nested.length > 0) return nested;
|
|
556
|
+
context.children.pop();
|
|
557
|
+
return [child.objectHandle, owned.objectHandle];
|
|
558
|
+
}
|
|
559
|
+
//#endregion
|
|
560
|
+
//#region lib/types/shared/cordis/observer.js
|
|
561
|
+
/** Lifecycle-driven Cordis tree publication shared by Host and Client plugin faces. */
|
|
562
|
+
/**
|
|
563
|
+
* Observe one Cordis realm and publish immutable tree replacements.
|
|
564
|
+
* @param ctx - Plugin context whose root is inspected and whose effects own listeners.
|
|
565
|
+
* @param listener - Consumer of complete snapshots in the inspected realm.
|
|
566
|
+
* @param limits - Snapshot node and encoded-byte limits.
|
|
567
|
+
* @returns A disposer that unregisters listeners and releases retained objects.
|
|
568
|
+
*/
|
|
569
|
+
function observeCordisTree(ctx, listener, limits) {
|
|
570
|
+
const collector = new CordisTreeCollector(ctx.root, limits);
|
|
571
|
+
let scheduled = false;
|
|
572
|
+
let closed = false;
|
|
573
|
+
const publish = () => {
|
|
574
|
+
scheduled = false;
|
|
575
|
+
if (closed) return;
|
|
576
|
+
listener(collector.snapshot());
|
|
577
|
+
};
|
|
578
|
+
const schedule = () => {
|
|
579
|
+
if (scheduled || closed) return;
|
|
580
|
+
scheduled = true;
|
|
581
|
+
queueMicrotask(publish);
|
|
582
|
+
};
|
|
583
|
+
const disposers = [ctx.on("internal/plugin", schedule, { global: true }), ctx.on("internal/status", schedule, { global: true })];
|
|
584
|
+
publish();
|
|
585
|
+
return () => {
|
|
586
|
+
if (closed) return;
|
|
587
|
+
closed = true;
|
|
588
|
+
for (const dispose of disposers) dispose();
|
|
589
|
+
collector.close();
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
//#endregion
|
|
593
|
+
//#region lib/types/shared/cordis/publisher.js
|
|
594
|
+
/** Shared Host/Client publication of browser-safe Cordis snapshots. */
|
|
595
|
+
/**
|
|
596
|
+
* Observe one Cordis runtime and retain its latest source snapshot.
|
|
597
|
+
* @param ctx - Plugin context whose root is inspected.
|
|
598
|
+
* @param publisher - Active Host or Client source publisher.
|
|
599
|
+
* @param limits - Snapshot node and encoded-byte limits.
|
|
600
|
+
* @returns A disposer that stops observation and releases retained objects.
|
|
601
|
+
*/
|
|
602
|
+
function publishCordisTree(ctx, publisher, limits) {
|
|
603
|
+
return observeCordisTree(ctx, (snapshot) => {
|
|
604
|
+
publisher.setState(CORDIS_TREE_TOPIC, snapshot);
|
|
605
|
+
}, limits);
|
|
606
|
+
}
|
|
607
|
+
//#endregion
|
|
608
|
+
//#region lib/types/shared/bridge/messages/runtime/console-frames.js
|
|
609
|
+
/**
|
|
610
|
+
* Parse a Worker-to-Client Console lifecycle frame.
|
|
611
|
+
* @param value - Untrusted decoded frame.
|
|
612
|
+
* @returns A validated enable or disable frame.
|
|
613
|
+
*/
|
|
614
|
+
function parseClientConsoleControlFrame(value) {
|
|
615
|
+
exactKeys(value, [
|
|
616
|
+
"v",
|
|
617
|
+
"t",
|
|
618
|
+
"sourceId",
|
|
619
|
+
"generation",
|
|
620
|
+
"sessionId"
|
|
621
|
+
], "Client Console control frame");
|
|
622
|
+
if (value.v !== 0 || value.t !== "client-console/enable" && value.t !== "client-console/disable") throw new Error("inspector protocol: invalid Client Console control frame");
|
|
623
|
+
return {
|
|
624
|
+
v: 0,
|
|
625
|
+
t: value.t,
|
|
626
|
+
sourceId: wireId(value.sourceId, "sourceId"),
|
|
627
|
+
generation: wireId(value.generation, "generation"),
|
|
628
|
+
sessionId: wireId(value.sessionId, "sessionId")
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
//#endregion
|
|
632
|
+
//#region lib/types/shared/bridge/messages/runtime/command-codec.js
|
|
633
|
+
/** Exact wire decoder for Client Runtime commands. */
|
|
634
|
+
/**
|
|
635
|
+
* Parse and rebuild one Runtime command before it enters the Client realm.
|
|
636
|
+
* @param value - Untrusted command value.
|
|
637
|
+
* @returns The validated command union member.
|
|
638
|
+
*/
|
|
639
|
+
function parseClientRuntimeCommand(value) {
|
|
640
|
+
if (!isPlainObject(value) || typeof value.op !== "string") throw new Error("inspector protocol: Client Runtime command must have an op");
|
|
641
|
+
switch (value.op) {
|
|
642
|
+
case "evaluate":
|
|
643
|
+
exactKeys(value, [
|
|
644
|
+
"op",
|
|
645
|
+
"expression",
|
|
646
|
+
"objectGroup",
|
|
647
|
+
"includeCommandLineAPI",
|
|
648
|
+
"silent",
|
|
649
|
+
"returnByValue",
|
|
650
|
+
"generatePreview",
|
|
651
|
+
"userGesture",
|
|
652
|
+
"awaitPromise",
|
|
653
|
+
"disableBreaks",
|
|
654
|
+
"replMode",
|
|
655
|
+
"allowUnsafeEvalBlockedByCSP",
|
|
656
|
+
"timeoutMs"
|
|
657
|
+
], "evaluate command");
|
|
658
|
+
if (typeof value.expression !== "string") throw new Error("inspector protocol: evaluate expression must be a string");
|
|
659
|
+
return {
|
|
660
|
+
op: "evaluate",
|
|
661
|
+
expression: value.expression,
|
|
662
|
+
...optionalString(value, "objectGroup"),
|
|
663
|
+
...optionalBoolean(value, "includeCommandLineAPI"),
|
|
664
|
+
...optionalBoolean(value, "silent"),
|
|
665
|
+
...optionalBoolean(value, "returnByValue"),
|
|
666
|
+
...optionalBoolean(value, "generatePreview"),
|
|
667
|
+
...optionalBoolean(value, "userGesture"),
|
|
668
|
+
...optionalBoolean(value, "awaitPromise"),
|
|
669
|
+
...optionalBoolean(value, "disableBreaks"),
|
|
670
|
+
...optionalBoolean(value, "replMode"),
|
|
671
|
+
...optionalBoolean(value, "allowUnsafeEvalBlockedByCSP"),
|
|
672
|
+
...optionalNonNegativeNumber(value, "timeoutMs")
|
|
673
|
+
};
|
|
674
|
+
case "get-properties":
|
|
675
|
+
exactKeys(value, [
|
|
676
|
+
"op",
|
|
677
|
+
"handle",
|
|
678
|
+
"ownProperties",
|
|
679
|
+
"accessorPropertiesOnly",
|
|
680
|
+
"generatePreview",
|
|
681
|
+
"nonIndexedPropertiesOnly"
|
|
682
|
+
], "get-properties command");
|
|
683
|
+
return {
|
|
684
|
+
op: "get-properties",
|
|
685
|
+
handle: wireId(value.handle, "handle"),
|
|
686
|
+
...optionalBoolean(value, "ownProperties"),
|
|
687
|
+
...optionalBoolean(value, "accessorPropertiesOnly"),
|
|
688
|
+
...optionalBoolean(value, "generatePreview"),
|
|
689
|
+
...optionalBoolean(value, "nonIndexedPropertiesOnly")
|
|
690
|
+
};
|
|
691
|
+
case "call-function": return parseCallFunction(value);
|
|
692
|
+
case "await-promise":
|
|
693
|
+
exactKeys(value, [
|
|
694
|
+
"op",
|
|
695
|
+
"promise",
|
|
696
|
+
"returnByValue",
|
|
697
|
+
"generatePreview"
|
|
698
|
+
], "await-promise command");
|
|
699
|
+
return {
|
|
700
|
+
op: "await-promise",
|
|
701
|
+
promise: wireId(value.promise, "promise"),
|
|
702
|
+
...optionalBoolean(value, "returnByValue"),
|
|
703
|
+
...optionalBoolean(value, "generatePreview")
|
|
704
|
+
};
|
|
705
|
+
case "release-object":
|
|
706
|
+
exactKeys(value, ["op", "handle"], "release-object command");
|
|
707
|
+
return {
|
|
708
|
+
op: "release-object",
|
|
709
|
+
handle: wireId(value.handle, "handle")
|
|
710
|
+
};
|
|
711
|
+
case "release-object-group":
|
|
712
|
+
exactKeys(value, ["op", "objectGroup"], "release-object-group command");
|
|
713
|
+
if (typeof value.objectGroup !== "string") throw new Error("inspector protocol: objectGroup must be a string");
|
|
714
|
+
return {
|
|
715
|
+
op: "release-object-group",
|
|
716
|
+
objectGroup: value.objectGroup
|
|
717
|
+
};
|
|
718
|
+
case "global-lexical-scope-names":
|
|
719
|
+
exactKeys(value, ["op"], "global-lexical-scope-names command");
|
|
720
|
+
return { op: "global-lexical-scope-names" };
|
|
721
|
+
default: throw new Error(`inspector protocol: unknown Client Runtime command ${JSON.stringify(value.op)}`);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
function parseCallFunction(value) {
|
|
725
|
+
exactKeys(value, [
|
|
726
|
+
"op",
|
|
727
|
+
"functionDeclaration",
|
|
728
|
+
"receiver",
|
|
729
|
+
"arguments",
|
|
730
|
+
"objectGroup",
|
|
731
|
+
"silent",
|
|
732
|
+
"returnByValue",
|
|
733
|
+
"generatePreview",
|
|
734
|
+
"userGesture",
|
|
735
|
+
"awaitPromise"
|
|
736
|
+
], "call-function command");
|
|
737
|
+
if (typeof value.functionDeclaration !== "string") throw new Error("inspector protocol: functionDeclaration must be a string");
|
|
738
|
+
let args;
|
|
739
|
+
if (value.arguments !== void 0) {
|
|
740
|
+
if (!Array.isArray(value.arguments)) throw new Error("inspector protocol: call arguments must be an array");
|
|
741
|
+
args = value.arguments.map(parseCallArgument);
|
|
742
|
+
}
|
|
743
|
+
return {
|
|
744
|
+
op: "call-function",
|
|
745
|
+
functionDeclaration: value.functionDeclaration,
|
|
746
|
+
...value.receiver === void 0 ? {} : { receiver: wireId(value.receiver, "receiver") },
|
|
747
|
+
...args === void 0 ? {} : { arguments: args },
|
|
748
|
+
...optionalString(value, "objectGroup"),
|
|
749
|
+
...optionalBoolean(value, "silent"),
|
|
750
|
+
...optionalBoolean(value, "returnByValue"),
|
|
751
|
+
...optionalBoolean(value, "generatePreview"),
|
|
752
|
+
...optionalBoolean(value, "userGesture"),
|
|
753
|
+
...optionalBoolean(value, "awaitPromise")
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
function parseCallArgument(value) {
|
|
757
|
+
if (!isPlainObject(value) || typeof value.kind !== "string") throw new Error("inspector protocol: invalid Client Runtime call argument");
|
|
758
|
+
switch (value.kind) {
|
|
759
|
+
case "value":
|
|
760
|
+
exactKeys(value, ["kind", "value"], "value call argument");
|
|
761
|
+
if (!isJsonValue(value.value)) throw new Error("inspector protocol: call argument value must be JSON");
|
|
762
|
+
return {
|
|
763
|
+
kind: "value",
|
|
764
|
+
value: value.value
|
|
765
|
+
};
|
|
766
|
+
case "unserializable":
|
|
767
|
+
exactKeys(value, ["kind", "value"], "unserializable call argument");
|
|
768
|
+
if (typeof value.value !== "string") throw new Error("inspector protocol: unserializable argument must be a string");
|
|
769
|
+
return {
|
|
770
|
+
kind: "unserializable",
|
|
771
|
+
value: value.value
|
|
772
|
+
};
|
|
773
|
+
case "object":
|
|
774
|
+
exactKeys(value, ["kind", "handle"], "object call argument");
|
|
775
|
+
return {
|
|
776
|
+
kind: "object",
|
|
777
|
+
handle: wireId(value.handle, "handle")
|
|
778
|
+
};
|
|
779
|
+
case "undefined":
|
|
780
|
+
exactKeys(value, ["kind"], "undefined call argument");
|
|
781
|
+
return { kind: "undefined" };
|
|
782
|
+
default: throw new Error(`inspector protocol: unknown call argument ${JSON.stringify(value.kind)}`);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
//#endregion
|
|
786
|
+
//#region lib/types/shared/bridge/messages/runtime/frames.js
|
|
787
|
+
/**
|
|
788
|
+
* Parse and rebuild one Worker-to-Client Runtime request.
|
|
789
|
+
* @param value - Untrusted request frame.
|
|
790
|
+
* @returns The validated request frame.
|
|
791
|
+
*/
|
|
792
|
+
function parseClientRuntimeRequestFrame(value) {
|
|
793
|
+
exactKeys(value, [
|
|
794
|
+
"v",
|
|
795
|
+
"t",
|
|
796
|
+
"sourceId",
|
|
797
|
+
"generation",
|
|
798
|
+
"sessionId",
|
|
799
|
+
"requestId",
|
|
800
|
+
"command"
|
|
801
|
+
], "Client Runtime request");
|
|
802
|
+
if (value.v !== 0 || value.t !== "client-runtime/request") throw new Error("inspector protocol: invalid Client Runtime request envelope");
|
|
803
|
+
return {
|
|
804
|
+
v: 0,
|
|
805
|
+
t: "client-runtime/request",
|
|
806
|
+
sourceId: wireId(value.sourceId, "sourceId"),
|
|
807
|
+
generation: wireId(value.generation, "generation"),
|
|
808
|
+
sessionId: wireId(value.sessionId, "sessionId"),
|
|
809
|
+
requestId: wireId(value.requestId, "requestId"),
|
|
810
|
+
command: parseClientRuntimeCommand(value.command)
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Parse and rebuild one Worker-to-Client Runtime cancellation.
|
|
815
|
+
* @param value - Untrusted cancellation frame.
|
|
816
|
+
* @returns The validated cancellation frame.
|
|
817
|
+
*/
|
|
818
|
+
function parseClientRuntimeCancelFrame(value) {
|
|
819
|
+
exactKeys(value, [
|
|
820
|
+
"v",
|
|
821
|
+
"t",
|
|
822
|
+
"sourceId",
|
|
823
|
+
"generation",
|
|
824
|
+
"sessionId",
|
|
825
|
+
"requestId"
|
|
826
|
+
], "Client Runtime cancellation");
|
|
827
|
+
if (value.v !== 0 || value.t !== "client-runtime/cancel") throw new Error("inspector protocol: invalid Client Runtime cancellation envelope");
|
|
828
|
+
return {
|
|
829
|
+
v: 0,
|
|
830
|
+
t: "client-runtime/cancel",
|
|
831
|
+
sourceId: wireId(value.sourceId, "sourceId"),
|
|
832
|
+
generation: wireId(value.generation, "generation"),
|
|
833
|
+
sessionId: wireId(value.sessionId, "sessionId"),
|
|
834
|
+
requestId: wireId(value.requestId, "requestId")
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Parse and rebuild one Worker acknowledgement for a Client Runtime response.
|
|
839
|
+
* @param value - Untrusted acknowledgement frame.
|
|
840
|
+
* @returns The validated acknowledgement frame.
|
|
841
|
+
*/
|
|
842
|
+
function parseClientRuntimeResponseAcknowledgedFrame(value) {
|
|
843
|
+
exactKeys(value, [
|
|
844
|
+
"v",
|
|
845
|
+
"t",
|
|
846
|
+
"sourceId",
|
|
847
|
+
"generation",
|
|
848
|
+
"sessionId",
|
|
849
|
+
"requestId"
|
|
850
|
+
], "Client Runtime response acknowledgement");
|
|
851
|
+
if (value.v !== 0 || value.t !== "client-runtime/response-acknowledged") throw new Error("inspector protocol: invalid Client Runtime response acknowledgement envelope");
|
|
852
|
+
return {
|
|
853
|
+
v: 0,
|
|
854
|
+
t: "client-runtime/response-acknowledged",
|
|
855
|
+
sourceId: wireId(value.sourceId, "sourceId"),
|
|
856
|
+
generation: wireId(value.generation, "generation"),
|
|
857
|
+
sessionId: wireId(value.sessionId, "sessionId"),
|
|
858
|
+
requestId: wireId(value.requestId, "requestId")
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Parse and rebuild one Runtime-session cleanup notification.
|
|
863
|
+
* @param value - Untrusted cleanup frame.
|
|
864
|
+
* @returns The validated cleanup frame.
|
|
865
|
+
*/
|
|
866
|
+
function parseClientRuntimeSessionClosedFrame(value) {
|
|
867
|
+
exactKeys(value, [
|
|
868
|
+
"v",
|
|
869
|
+
"t",
|
|
870
|
+
"sourceId",
|
|
871
|
+
"generation",
|
|
872
|
+
"sessionId"
|
|
873
|
+
], "Client Runtime session close");
|
|
874
|
+
if (value.v !== 0 || value.t !== "client-runtime/session-closed") throw new Error("inspector protocol: invalid Client Runtime session close envelope");
|
|
875
|
+
return {
|
|
876
|
+
v: 0,
|
|
877
|
+
t: "client-runtime/session-closed",
|
|
878
|
+
sourceId: wireId(value.sourceId, "sourceId"),
|
|
879
|
+
generation: wireId(value.generation, "generation"),
|
|
880
|
+
sessionId: wireId(value.sessionId, "sessionId")
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
//#endregion
|
|
884
|
+
//#region lib/types/shared/bridge/messages/sources/codec.js
|
|
885
|
+
/** Exact decoders for Client source catalog operations and values. */
|
|
886
|
+
/**
|
|
887
|
+
* Parse one Worker-to-Client source command.
|
|
888
|
+
* @param value - Untrusted decoded command.
|
|
889
|
+
* @returns The validated command.
|
|
890
|
+
*/
|
|
891
|
+
function parseClientSourceCommand(value) {
|
|
892
|
+
if (!isPlainObject(value) || typeof value.op !== "string") throw new Error("inspector protocol: Client source command must have an op");
|
|
893
|
+
if (value.op === "list-scripts") {
|
|
894
|
+
exactKeys(value, ["op"], "Client source list command");
|
|
895
|
+
return { op: "list-scripts" };
|
|
896
|
+
}
|
|
897
|
+
if (value.op !== "get-content-chunk") throw new Error(`inspector protocol: unknown Client source command ${JSON.stringify(value.op)}`);
|
|
898
|
+
exactKeys(value, [
|
|
899
|
+
"op",
|
|
900
|
+
"scriptKey",
|
|
901
|
+
"content",
|
|
902
|
+
"offset",
|
|
903
|
+
"maxBytes"
|
|
904
|
+
], "Client source chunk command");
|
|
905
|
+
return {
|
|
906
|
+
op: "get-content-chunk",
|
|
907
|
+
scriptKey: wireId(value.scriptKey, "scriptKey"),
|
|
908
|
+
content: contentKind(value.content),
|
|
909
|
+
offset: natural$1(value.offset, "offset", true),
|
|
910
|
+
maxBytes: natural$1(value.maxBytes, "maxBytes", false)
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
function contentKind(value) {
|
|
914
|
+
if (value !== "source" && value !== "source-map") throw new Error("inspector protocol: invalid Client source content kind");
|
|
915
|
+
return value;
|
|
916
|
+
}
|
|
917
|
+
function natural$1(value, label, zero) {
|
|
918
|
+
if (!Number.isSafeInteger(value) || value < (zero ? 0 : 1)) throw new Error(`inspector protocol: ${label} must be ${zero ? "a non-negative" : "a positive"} integer`);
|
|
919
|
+
return value;
|
|
920
|
+
}
|
|
921
|
+
//#endregion
|
|
922
|
+
//#region lib/types/shared/bridge/messages/sources/frames.js
|
|
923
|
+
/**
|
|
924
|
+
* Parse one Worker-to-Client source request.
|
|
925
|
+
* @param value - Untrusted decoded request.
|
|
926
|
+
* @returns The validated request frame.
|
|
927
|
+
*/
|
|
928
|
+
function parseClientSourceRequestFrame(value) {
|
|
929
|
+
exactKeys(value, [
|
|
930
|
+
"v",
|
|
931
|
+
"t",
|
|
932
|
+
"sourceId",
|
|
933
|
+
"generation",
|
|
934
|
+
"sessionId",
|
|
935
|
+
"requestId",
|
|
936
|
+
"command"
|
|
937
|
+
], "Client source request");
|
|
938
|
+
if (value.v !== 0 || value.t !== "client-sources/request") throw new Error("inspector protocol: invalid Client source request envelope");
|
|
939
|
+
return {
|
|
940
|
+
v: 0,
|
|
941
|
+
t: "client-sources/request",
|
|
942
|
+
sourceId: wireId(value.sourceId, "sourceId"),
|
|
943
|
+
generation: wireId(value.generation, "generation"),
|
|
944
|
+
sessionId: wireId(value.sessionId, "sessionId"),
|
|
945
|
+
requestId: wireId(value.requestId, "requestId"),
|
|
946
|
+
command: parseClientSourceCommand(value.command)
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Parse one Client source-session cleanup notification.
|
|
951
|
+
* @param value - Untrusted decoded notification.
|
|
952
|
+
* @returns The validated cleanup frame.
|
|
953
|
+
*/
|
|
954
|
+
function parseClientSourceSessionClosedFrame(value) {
|
|
955
|
+
exactKeys(value, [
|
|
956
|
+
"v",
|
|
957
|
+
"t",
|
|
958
|
+
"sourceId",
|
|
959
|
+
"generation",
|
|
960
|
+
"sessionId"
|
|
961
|
+
], "Client source session close");
|
|
962
|
+
if (value.v !== 0 || value.t !== "client-sources/session-closed") throw new Error("inspector protocol: invalid Client source session close envelope");
|
|
963
|
+
return {
|
|
964
|
+
v: 0,
|
|
965
|
+
t: "client-sources/session-closed",
|
|
966
|
+
sourceId: wireId(value.sourceId, "sourceId"),
|
|
967
|
+
generation: wireId(value.generation, "generation"),
|
|
968
|
+
sessionId: wireId(value.sessionId, "sessionId")
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
//#endregion
|
|
972
|
+
//#region lib/types/shared/bridge/messages/observation.js
|
|
973
|
+
/** Versioned source lifecycle, observation, and extension frames shared by both carriers. */
|
|
974
|
+
/**
|
|
975
|
+
* Parse and rebuild one Worker control frame received by a source.
|
|
976
|
+
* @param value - Untrusted decoded wire value.
|
|
977
|
+
* @returns The validated Worker-to-source frame.
|
|
978
|
+
*/
|
|
979
|
+
function parseWorkerSourceFrame(value) {
|
|
980
|
+
if (!isJsonValue(value) || !isPlainObject(value) || value.v !== 0 || typeof value.t !== "string") throw new Error("inspector protocol: invalid Worker source frame");
|
|
981
|
+
if (value.t === "source/rejected") {
|
|
982
|
+
exactKeys(value, [
|
|
983
|
+
"v",
|
|
984
|
+
"t",
|
|
985
|
+
"code",
|
|
986
|
+
"message"
|
|
987
|
+
], "source/rejected frame");
|
|
988
|
+
if (value.code !== "invalid-frame" && value.code !== "version-mismatch" && value.code !== "unauthorized" || typeof value.message !== "string") throw new Error("inspector protocol: invalid source/rejected frame");
|
|
989
|
+
return {
|
|
990
|
+
v: 0,
|
|
991
|
+
t: "source/rejected",
|
|
992
|
+
code: value.code,
|
|
993
|
+
message: value.message
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
if (value.t === "client-runtime/request") return parseClientRuntimeRequestFrame(value);
|
|
997
|
+
if (value.t === "client-runtime/cancel") return parseClientRuntimeCancelFrame(value);
|
|
998
|
+
if (value.t === "client-runtime/response-acknowledged") return parseClientRuntimeResponseAcknowledgedFrame(value);
|
|
999
|
+
if (value.t === "client-runtime/session-closed") return parseClientRuntimeSessionClosedFrame(value);
|
|
1000
|
+
if (value.t === "client-sources/request") return parseClientSourceRequestFrame(value);
|
|
1001
|
+
if (value.t === "client-sources/session-closed") return parseClientSourceSessionClosedFrame(value);
|
|
1002
|
+
if (value.t === "client-console/enable" || value.t === "client-console/disable") return parseClientConsoleControlFrame(value);
|
|
1003
|
+
const common = {
|
|
1004
|
+
v: 0,
|
|
1005
|
+
sourceId: sourceId(value.sourceId),
|
|
1006
|
+
generation: generation(value.generation)
|
|
1007
|
+
};
|
|
1008
|
+
if (value.t === "source/accepted") {
|
|
1009
|
+
exactKeys(value, [
|
|
1010
|
+
"v",
|
|
1011
|
+
"t",
|
|
1012
|
+
"sourceId",
|
|
1013
|
+
"generation"
|
|
1014
|
+
], "source/accepted frame");
|
|
1015
|
+
return {
|
|
1016
|
+
...common,
|
|
1017
|
+
t: "source/accepted"
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
if (value.t === "source/append-acknowledged") {
|
|
1021
|
+
exactKeys(value, [
|
|
1022
|
+
"v",
|
|
1023
|
+
"t",
|
|
1024
|
+
"sourceId",
|
|
1025
|
+
"generation",
|
|
1026
|
+
"nextSequence"
|
|
1027
|
+
], "source append acknowledgement");
|
|
1028
|
+
return {
|
|
1029
|
+
...common,
|
|
1030
|
+
t: "source/append-acknowledged",
|
|
1031
|
+
nextSequence: natural(value.nextSequence, "nextSequence")
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
if (value.t === "source/resnapshot" && typeof value.reason === "string") {
|
|
1035
|
+
exactKeys(value, [
|
|
1036
|
+
"v",
|
|
1037
|
+
"t",
|
|
1038
|
+
"sourceId",
|
|
1039
|
+
"generation",
|
|
1040
|
+
"expectedSequence",
|
|
1041
|
+
"reason"
|
|
1042
|
+
], "source/resnapshot frame");
|
|
1043
|
+
return {
|
|
1044
|
+
...common,
|
|
1045
|
+
t: "source/resnapshot",
|
|
1046
|
+
expectedSequence: natural(value.expectedSequence, "expectedSequence"),
|
|
1047
|
+
reason: value.reason
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
throw new Error(`inspector protocol: unknown Worker source frame ${JSON.stringify(value.t)}`);
|
|
1051
|
+
}
|
|
1052
|
+
function sourceId(value) {
|
|
1053
|
+
if (typeof value !== "string") throw new Error("inspector protocol: sourceId must be a string");
|
|
1054
|
+
return inspectorId(value, "sourceId");
|
|
1055
|
+
}
|
|
1056
|
+
function generation(value) {
|
|
1057
|
+
if (typeof value !== "string") throw new Error("inspector protocol: generation must be a string");
|
|
1058
|
+
return inspectorId(value, "generation");
|
|
1059
|
+
}
|
|
1060
|
+
function natural(value, label) {
|
|
1061
|
+
if (!Number.isSafeInteger(value) || value < 0) throw new Error(`inspector protocol: ${label} must be a non-negative safe integer`);
|
|
1062
|
+
return value;
|
|
1063
|
+
}
|
|
1064
|
+
//#endregion
|
|
1065
|
+
//#region lib/types/shared/bridge/publisher.js
|
|
1066
|
+
/** Source-side interfaces shared by MessagePort and WebSocket bridge implementations. */
|
|
1067
|
+
/** Shared observation and query delegation inherited by both source transports. */
|
|
1068
|
+
var InspectorSourceConnection = class {
|
|
1069
|
+
/** Publish one JSON observation without waiting on its carrier. */
|
|
1070
|
+
publish(topic, payload, monotonicMs = performance.now()) {
|
|
1071
|
+
this.publisher.publish(topic, payload, monotonicMs);
|
|
1072
|
+
}
|
|
1073
|
+
/** Retain and publish one state value for reconnect or replacement recovery. */
|
|
1074
|
+
setState(topic, payload, monotonicMs = performance.now()) {
|
|
1075
|
+
this.publisher.setState(topic, payload, monotonicMs);
|
|
1076
|
+
}
|
|
1077
|
+
/** Execute one non-CDP query through the active source generation. */
|
|
1078
|
+
request(query) {
|
|
1079
|
+
return this.queries.request(query);
|
|
1080
|
+
}
|
|
1081
|
+
};
|
|
1082
|
+
//#endregion
|
|
1083
|
+
//#region lib/types/client/cdp/stack.js
|
|
1084
|
+
/** Browser stack parsing for realm-neutral Runtime and Console events. */
|
|
1085
|
+
/**
|
|
1086
|
+
* Capture the caller stack of a wrapped Client Console method.
|
|
1087
|
+
* @param resolveScript - Resolver for Client catalog script keys.
|
|
1088
|
+
* @returns Parsed call frames when the browser supplies a stack.
|
|
1089
|
+
*/
|
|
1090
|
+
function captureClientConsoleStack(resolveScript) {
|
|
1091
|
+
return parseClientStack((/* @__PURE__ */ new Error()).stack, resolveScript, 3);
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* Parse the stack attached to an uncaught Client value when available.
|
|
1095
|
+
* @param value - Thrown or rejected value.
|
|
1096
|
+
* @param resolveScript - Resolver for Client catalog script keys.
|
|
1097
|
+
* @returns Parsed call frames when the value has a recognized stack string.
|
|
1098
|
+
*/
|
|
1099
|
+
function clientErrorStack(value, resolveScript = () => void 0) {
|
|
1100
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
1101
|
+
let stack;
|
|
1102
|
+
try {
|
|
1103
|
+
stack = Reflect.get(value, "stack");
|
|
1104
|
+
} catch {
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
return typeof stack === "string" ? parseClientStack(stack, resolveScript, 0) : void 0;
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Parse V8- and Firefox-style textual frames into the common stack model.
|
|
1111
|
+
* @param stack - Browser stack text.
|
|
1112
|
+
* @param resolveScript - Resolver for Client catalog script keys.
|
|
1113
|
+
* @param skipFrames - Parsed observer frames omitted from the result.
|
|
1114
|
+
* @returns Parsed call frames, or `undefined` when none remain.
|
|
1115
|
+
*/
|
|
1116
|
+
function parseClientStack(stack, resolveScript, skipFrames) {
|
|
1117
|
+
if (stack === void 0) return void 0;
|
|
1118
|
+
const frames = [];
|
|
1119
|
+
for (const line of stack.split("\n")) {
|
|
1120
|
+
const frame = parseFrame(line, resolveScript);
|
|
1121
|
+
if (frame !== void 0) frames.push(frame);
|
|
1122
|
+
}
|
|
1123
|
+
const callFrames = frames.slice(skipFrames);
|
|
1124
|
+
return callFrames.length === 0 ? void 0 : { callFrames };
|
|
1125
|
+
}
|
|
1126
|
+
function parseFrame(line, resolveScript) {
|
|
1127
|
+
const chrome = /^\s*at\s+(?:(.*?)\s+\()?(.+):(\d+):(\d+)\)?$/u.exec(line);
|
|
1128
|
+
const firefox = chrome === null ? /^(.*?)@(.+):(\d+):(\d+)$/u.exec(line) : null;
|
|
1129
|
+
const match = chrome ?? firefox;
|
|
1130
|
+
if (match === null) return void 0;
|
|
1131
|
+
const url = match[2];
|
|
1132
|
+
const lineNumber = Number(match[3]) - 1;
|
|
1133
|
+
const columnNumber = Number(match[4]) - 1;
|
|
1134
|
+
if (url === void 0 || !Number.isSafeInteger(lineNumber) || !Number.isSafeInteger(columnNumber)) return void 0;
|
|
1135
|
+
const scriptKey = resolveScript(url);
|
|
1136
|
+
return {
|
|
1137
|
+
functionName: match[1] ?? "",
|
|
1138
|
+
...scriptKey === void 0 ? {} : { scriptKey },
|
|
1139
|
+
url,
|
|
1140
|
+
lineNumber,
|
|
1141
|
+
columnNumber
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
//#endregion
|
|
1145
|
+
//#region lib/types/client/cdp/console.js
|
|
1146
|
+
/** Client Console observation shared by every active DevTools Runtime session. */
|
|
1147
|
+
/**
|
|
1148
|
+
* Describe browser-side Console observation.
|
|
1149
|
+
* @returns The Console capability advertised by a browser Client source.
|
|
1150
|
+
*/
|
|
1151
|
+
function consoleBridgeCapability() {
|
|
1152
|
+
return { type: "client-console" };
|
|
1153
|
+
}
|
|
1154
|
+
const METHODS = [
|
|
1155
|
+
["log", "log"],
|
|
1156
|
+
["debug", "debug"],
|
|
1157
|
+
["info", "info"],
|
|
1158
|
+
["error", "error"],
|
|
1159
|
+
["warn", "warning"],
|
|
1160
|
+
["dir", "dir"],
|
|
1161
|
+
["dirxml", "dirxml"],
|
|
1162
|
+
["table", "table"],
|
|
1163
|
+
["trace", "trace"],
|
|
1164
|
+
["clear", "clear"],
|
|
1165
|
+
["group", "startGroup"],
|
|
1166
|
+
["groupCollapsed", "startGroupCollapsed"],
|
|
1167
|
+
["groupEnd", "endGroup"],
|
|
1168
|
+
["assert", "assert"],
|
|
1169
|
+
["profile", "profile"],
|
|
1170
|
+
["profileEnd", "profileEnd"],
|
|
1171
|
+
["count", "count"],
|
|
1172
|
+
["timeEnd", "timeEnd"]
|
|
1173
|
+
];
|
|
1174
|
+
/** Installs one transparent console/error observer and fans out session-local values. */
|
|
1175
|
+
var ClientConsoleObserver = class {
|
|
1176
|
+
runtime;
|
|
1177
|
+
sink;
|
|
1178
|
+
resolveScript;
|
|
1179
|
+
sessions = /* @__PURE__ */ new Set();
|
|
1180
|
+
installed = [];
|
|
1181
|
+
active = false;
|
|
1182
|
+
closed = false;
|
|
1183
|
+
constructor(runtime, sink, resolveScript = () => void 0) {
|
|
1184
|
+
this.runtime = runtime;
|
|
1185
|
+
this.sink = sink;
|
|
1186
|
+
this.resolveScript = resolveScript;
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Start producing events for one DevTools Runtime session.
|
|
1190
|
+
* @param sessionId - Session whose object table retains event arguments.
|
|
1191
|
+
*/
|
|
1192
|
+
enable(sessionId) {
|
|
1193
|
+
if (this.closed) return;
|
|
1194
|
+
this.sessions.add(sessionId);
|
|
1195
|
+
if (!this.active) this.install();
|
|
1196
|
+
}
|
|
1197
|
+
/**
|
|
1198
|
+
* Stop producing events and release Console objects for one session.
|
|
1199
|
+
* @param sessionId - Session being disabled or closed.
|
|
1200
|
+
*/
|
|
1201
|
+
disable(sessionId) {
|
|
1202
|
+
this.sessions.delete(sessionId);
|
|
1203
|
+
this.runtime.releaseObjectGroup(sessionId, "console");
|
|
1204
|
+
if (this.sessions.size === 0) this.uninstall();
|
|
1205
|
+
}
|
|
1206
|
+
/** Restore original browser hooks and clear every active session. */
|
|
1207
|
+
close() {
|
|
1208
|
+
if (this.closed) return;
|
|
1209
|
+
this.closed = true;
|
|
1210
|
+
this.reset();
|
|
1211
|
+
}
|
|
1212
|
+
/** Stop observing the current source generation while allowing a later reconnect. */
|
|
1213
|
+
reset() {
|
|
1214
|
+
this.sessions.clear();
|
|
1215
|
+
this.uninstall();
|
|
1216
|
+
}
|
|
1217
|
+
install() {
|
|
1218
|
+
this.active = true;
|
|
1219
|
+
for (const [name, type] of METHODS) {
|
|
1220
|
+
const candidate = Reflect.get(console, name);
|
|
1221
|
+
if (typeof candidate !== "function") continue;
|
|
1222
|
+
const original = candidate;
|
|
1223
|
+
const capture = (values) => {
|
|
1224
|
+
this.captureConsole(type, values);
|
|
1225
|
+
};
|
|
1226
|
+
const replacement = function(...args) {
|
|
1227
|
+
const result = Reflect.apply(original, this, args);
|
|
1228
|
+
const values = name === "assert" ? args.slice(1) : args;
|
|
1229
|
+
if (name !== "assert" || !args[0]) capture(values);
|
|
1230
|
+
return result;
|
|
1231
|
+
};
|
|
1232
|
+
if (Reflect.set(console, name, replacement)) this.installed.push({
|
|
1233
|
+
name,
|
|
1234
|
+
original,
|
|
1235
|
+
replacement
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
addGlobalListener("error", this.onError);
|
|
1239
|
+
addGlobalListener("unhandledrejection", this.onUnhandledRejection);
|
|
1240
|
+
}
|
|
1241
|
+
uninstall() {
|
|
1242
|
+
if (!this.active) return;
|
|
1243
|
+
this.active = false;
|
|
1244
|
+
removeGlobalListener("error", this.onError);
|
|
1245
|
+
removeGlobalListener("unhandledrejection", this.onUnhandledRejection);
|
|
1246
|
+
for (const method of this.installed.splice(0).reverse()) if (Reflect.get(console, method.name) === method.replacement) Reflect.set(console, method.name, method.original);
|
|
1247
|
+
}
|
|
1248
|
+
onError = (event) => {
|
|
1249
|
+
const error = Reflect.get(event, "error");
|
|
1250
|
+
const message = Reflect.get(event, "message");
|
|
1251
|
+
this.captureException(error ?? new Error(typeof message === "string" ? message : "Client error"));
|
|
1252
|
+
};
|
|
1253
|
+
onUnhandledRejection = (event) => {
|
|
1254
|
+
this.captureException(Reflect.get(event, "reason"));
|
|
1255
|
+
};
|
|
1256
|
+
captureConsole(type, values) {
|
|
1257
|
+
const timestamp = Date.now();
|
|
1258
|
+
const stackTrace = captureClientConsoleStack(this.resolveScript);
|
|
1259
|
+
queueMicrotask(() => {
|
|
1260
|
+
for (const sessionId of [...this.sessions]) try {
|
|
1261
|
+
const event = this.runtime.consoleEvent(sessionId, type, values, timestamp, stackTrace);
|
|
1262
|
+
if (event !== void 0) this.sink(sessionId, event);
|
|
1263
|
+
} catch {}
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
captureException(error) {
|
|
1267
|
+
const timestamp = Date.now();
|
|
1268
|
+
const stackTrace = clientErrorStack(error, this.resolveScript);
|
|
1269
|
+
queueMicrotask(() => {
|
|
1270
|
+
for (const sessionId of [...this.sessions]) try {
|
|
1271
|
+
const event = this.runtime.exceptionEvent(sessionId, error, timestamp, stackTrace);
|
|
1272
|
+
if (event !== void 0) this.sink(sessionId, event);
|
|
1273
|
+
} catch {}
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1277
|
+
function addGlobalListener(type, listener) {
|
|
1278
|
+
const add = Reflect.get(globalThis, "addEventListener");
|
|
1279
|
+
if (typeof add === "function") Reflect.apply(add, globalThis, [type, listener]);
|
|
1280
|
+
}
|
|
1281
|
+
function removeGlobalListener(type, listener) {
|
|
1282
|
+
const remove = Reflect.get(globalThis, "removeEventListener");
|
|
1283
|
+
if (typeof remove === "function") Reflect.apply(remove, globalThis, [type, listener]);
|
|
1284
|
+
}
|
|
1285
|
+
//#endregion
|
|
1286
|
+
//#region lib/types/client/cdp/errors.js
|
|
1287
|
+
/** Client Runtime failures that belong to the transport rather than evaluated JavaScript. */
|
|
1288
|
+
/** Failure returned through the typed Client Runtime error outcome. */
|
|
1289
|
+
var ClientRuntimeExecutionError = class extends Error {
|
|
1290
|
+
code;
|
|
1291
|
+
constructor(code, message) {
|
|
1292
|
+
super(message);
|
|
1293
|
+
this.code = code;
|
|
1294
|
+
}
|
|
1295
|
+
};
|
|
1296
|
+
//#endregion
|
|
1297
|
+
//#region lib/types/client/cdp/objects.js
|
|
1298
|
+
/** Client-local object handles and CDP-compatible RemoteObject serialization. */
|
|
1299
|
+
const MAX_CLASS_PROTOTYPE_DEPTH = 32;
|
|
1300
|
+
/** Per-DevTools-session owner of all live Client object references. */
|
|
1301
|
+
var ClientObjectStore = class {
|
|
1302
|
+
maxObjects;
|
|
1303
|
+
objects = /* @__PURE__ */ new Map();
|
|
1304
|
+
groups = /* @__PURE__ */ new Map();
|
|
1305
|
+
allocations = /* @__PURE__ */ new Map();
|
|
1306
|
+
nextOrdinal = 1;
|
|
1307
|
+
constructor(maxObjects) {
|
|
1308
|
+
this.maxObjects = maxObjects;
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Start tracking handles allocated by one independently settling operation.
|
|
1312
|
+
* @returns An opaque allocation identity.
|
|
1313
|
+
*/
|
|
1314
|
+
beginAllocation() {
|
|
1315
|
+
const allocation = Symbol("Client Runtime object allocation");
|
|
1316
|
+
this.allocations.set(allocation, /* @__PURE__ */ new Set());
|
|
1317
|
+
return allocation;
|
|
1318
|
+
}
|
|
1319
|
+
/**
|
|
1320
|
+
* Keep an operation's handles and release its allocation bookkeeping.
|
|
1321
|
+
* @param allocation - Allocation returned by {@link beginAllocation}.
|
|
1322
|
+
*/
|
|
1323
|
+
commitAllocation(allocation) {
|
|
1324
|
+
this.allocations.delete(allocation);
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Resolve one handle or fail without exposing another session's objects.
|
|
1328
|
+
* @param handle - Client-local object handle.
|
|
1329
|
+
* @returns The retained JavaScript value.
|
|
1330
|
+
*/
|
|
1331
|
+
get(handle) {
|
|
1332
|
+
const object = this.objects.get(handle);
|
|
1333
|
+
if (object === void 0) throw new ClientRuntimeExecutionError("object-not-found", "Client RemoteObject was released");
|
|
1334
|
+
return object.value;
|
|
1335
|
+
}
|
|
1336
|
+
/**
|
|
1337
|
+
* Read the object group inherited by values reached through one handle.
|
|
1338
|
+
* @param handle - Client-local object handle.
|
|
1339
|
+
* @returns Its object group, or `undefined` when it is ungrouped.
|
|
1340
|
+
*/
|
|
1341
|
+
group(handle) {
|
|
1342
|
+
const object = this.objects.get(handle);
|
|
1343
|
+
if (object === void 0) throw new ClientRuntimeExecutionError("object-not-found", "Client RemoteObject was released");
|
|
1344
|
+
return object.group;
|
|
1345
|
+
}
|
|
1346
|
+
/**
|
|
1347
|
+
* Convert a live value to the JSON-safe RemoteObject protocol.
|
|
1348
|
+
* @param value - Value owned by this Client realm.
|
|
1349
|
+
* @param options - Object group and serialization options.
|
|
1350
|
+
* @param allocation - Optional operation that owns any newly retained handle.
|
|
1351
|
+
* @returns A primitive value or opaque Client handle with display metadata.
|
|
1352
|
+
*/
|
|
1353
|
+
serialize(value, options = {}, allocation) {
|
|
1354
|
+
const primitive = serializePrimitive(value);
|
|
1355
|
+
if (primitive !== void 0) return primitive;
|
|
1356
|
+
if (options.returnByValue === true) return { descriptor: {
|
|
1357
|
+
type: typeof value === "function" ? "function" : "object",
|
|
1358
|
+
value: serializeByValue(value),
|
|
1359
|
+
description: describe(value)
|
|
1360
|
+
} };
|
|
1361
|
+
const type = typeof value === "function" ? "function" : typeof value === "symbol" ? "symbol" : "object";
|
|
1362
|
+
const subtype = type === "object" ? subtypeOf(value) : void 0;
|
|
1363
|
+
const objectReference = identifyRealmObject(value);
|
|
1364
|
+
return {
|
|
1365
|
+
descriptor: {
|
|
1366
|
+
type,
|
|
1367
|
+
...subtype === void 0 ? {} : { subtype },
|
|
1368
|
+
className: className(value),
|
|
1369
|
+
description: describe(value),
|
|
1370
|
+
...options.generatePreview === true && type === "object" ? { preview: preview(value, type, subtype) } : {}
|
|
1371
|
+
},
|
|
1372
|
+
object: { handle: this.register(value, options.group, allocation) },
|
|
1373
|
+
...objectReference === void 0 ? {} : { semanticReference: objectReference }
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1377
|
+
* Release exactly one handle. Releasing an unknown handle is idempotent.
|
|
1378
|
+
* @param handle - Client-local object handle.
|
|
1379
|
+
*/
|
|
1380
|
+
release(handle) {
|
|
1381
|
+
const object = this.objects.get(handle);
|
|
1382
|
+
if (object === void 0) return;
|
|
1383
|
+
this.objects.delete(handle);
|
|
1384
|
+
if (object.group === void 0) return;
|
|
1385
|
+
const members = this.groups.get(object.group);
|
|
1386
|
+
members?.delete(handle);
|
|
1387
|
+
if (members?.size === 0) this.groups.delete(object.group);
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* Release every handle in one DevTools object group.
|
|
1391
|
+
* @param group - DevTools object-group name.
|
|
1392
|
+
*/
|
|
1393
|
+
releaseGroup(group) {
|
|
1394
|
+
const members = this.groups.get(group);
|
|
1395
|
+
if (members === void 0) return;
|
|
1396
|
+
for (const handle of members) this.objects.delete(handle);
|
|
1397
|
+
this.groups.delete(group);
|
|
1398
|
+
}
|
|
1399
|
+
/**
|
|
1400
|
+
* Discard exactly the handles allocated by one failed operation.
|
|
1401
|
+
* @param allocation - Allocation returned by {@link beginAllocation}.
|
|
1402
|
+
*/
|
|
1403
|
+
rollback(allocation) {
|
|
1404
|
+
const handles = this.allocations.get(allocation);
|
|
1405
|
+
if (handles === void 0) return;
|
|
1406
|
+
this.allocations.delete(allocation);
|
|
1407
|
+
for (const handle of handles) this.release(handle);
|
|
1408
|
+
}
|
|
1409
|
+
/** Release the whole DevTools session. */
|
|
1410
|
+
clear() {
|
|
1411
|
+
this.objects.clear();
|
|
1412
|
+
this.groups.clear();
|
|
1413
|
+
this.allocations.clear();
|
|
1414
|
+
}
|
|
1415
|
+
register(value, group, allocation) {
|
|
1416
|
+
if (this.objects.size >= this.maxObjects) throw new ClientRuntimeExecutionError("result-too-large", `Client Runtime retained-object limit ${String(this.maxObjects)} reached`);
|
|
1417
|
+
const ordinal = this.nextOrdinal++;
|
|
1418
|
+
const handle = inspectorId(`object-${String(ordinal)}`, "handle");
|
|
1419
|
+
this.objects.set(handle, {
|
|
1420
|
+
value,
|
|
1421
|
+
group
|
|
1422
|
+
});
|
|
1423
|
+
if (allocation !== void 0) this.allocations.get(allocation)?.add(handle);
|
|
1424
|
+
if (group !== void 0) {
|
|
1425
|
+
let members = this.groups.get(group);
|
|
1426
|
+
if (members === void 0) {
|
|
1427
|
+
members = /* @__PURE__ */ new Set();
|
|
1428
|
+
this.groups.set(group, members);
|
|
1429
|
+
}
|
|
1430
|
+
members.add(handle);
|
|
1431
|
+
}
|
|
1432
|
+
return handle;
|
|
1433
|
+
}
|
|
1434
|
+
};
|
|
1435
|
+
function serializePrimitive(value) {
|
|
1436
|
+
if (value === void 0) return { descriptor: { type: "undefined" } };
|
|
1437
|
+
if (value === null) return { descriptor: {
|
|
1438
|
+
type: "object",
|
|
1439
|
+
subtype: "null",
|
|
1440
|
+
value: null
|
|
1441
|
+
} };
|
|
1442
|
+
if (typeof value === "string") return { descriptor: {
|
|
1443
|
+
type: "string",
|
|
1444
|
+
value
|
|
1445
|
+
} };
|
|
1446
|
+
if (typeof value === "boolean") return { descriptor: {
|
|
1447
|
+
type: "boolean",
|
|
1448
|
+
value
|
|
1449
|
+
} };
|
|
1450
|
+
if (typeof value === "bigint") {
|
|
1451
|
+
const text = `${String(value)}n`;
|
|
1452
|
+
return { descriptor: {
|
|
1453
|
+
type: "bigint",
|
|
1454
|
+
unserializableValue: text,
|
|
1455
|
+
description: text
|
|
1456
|
+
} };
|
|
1457
|
+
}
|
|
1458
|
+
if (typeof value !== "number") return void 0;
|
|
1459
|
+
if (Number.isFinite(value) && !Object.is(value, -0)) return { descriptor: {
|
|
1460
|
+
type: "number",
|
|
1461
|
+
value,
|
|
1462
|
+
description: String(value)
|
|
1463
|
+
} };
|
|
1464
|
+
const text = Object.is(value, -0) ? "-0" : String(value);
|
|
1465
|
+
return { descriptor: {
|
|
1466
|
+
type: "number",
|
|
1467
|
+
unserializableValue: text,
|
|
1468
|
+
description: text
|
|
1469
|
+
} };
|
|
1470
|
+
}
|
|
1471
|
+
function serializeByValue(value) {
|
|
1472
|
+
let serialized;
|
|
1473
|
+
try {
|
|
1474
|
+
serialized = JSON.stringify(value);
|
|
1475
|
+
} catch (error) {
|
|
1476
|
+
throw new ClientRuntimeExecutionError("unsupported", `Value cannot be returned by value: ${renderError$4(error)}`);
|
|
1477
|
+
}
|
|
1478
|
+
if (typeof serialized !== "string") throw new ClientRuntimeExecutionError("unsupported", "Value cannot be returned by value");
|
|
1479
|
+
const result = JSON.parse(serialized);
|
|
1480
|
+
if (!isJsonValue(result)) throw new ClientRuntimeExecutionError("unsupported", "Value is outside the JSON value set");
|
|
1481
|
+
return result;
|
|
1482
|
+
}
|
|
1483
|
+
function preview(value, type, subtype) {
|
|
1484
|
+
const properties = [];
|
|
1485
|
+
let overflow = false;
|
|
1486
|
+
if (typeof value === "object" && value !== null || typeof value === "function") {
|
|
1487
|
+
let keys = [];
|
|
1488
|
+
try {
|
|
1489
|
+
keys = Reflect.ownKeys(value);
|
|
1490
|
+
} catch {
|
|
1491
|
+
overflow = true;
|
|
1492
|
+
}
|
|
1493
|
+
for (const key of keys) {
|
|
1494
|
+
if (properties.length === 5) {
|
|
1495
|
+
overflow = true;
|
|
1496
|
+
break;
|
|
1497
|
+
}
|
|
1498
|
+
let descriptor;
|
|
1499
|
+
try {
|
|
1500
|
+
descriptor = Reflect.getOwnPropertyDescriptor(value, key);
|
|
1501
|
+
} catch {
|
|
1502
|
+
continue;
|
|
1503
|
+
}
|
|
1504
|
+
if (descriptor === void 0) continue;
|
|
1505
|
+
if (!("value" in descriptor)) {
|
|
1506
|
+
properties.push({
|
|
1507
|
+
name: String(key),
|
|
1508
|
+
type: "accessor"
|
|
1509
|
+
});
|
|
1510
|
+
continue;
|
|
1511
|
+
}
|
|
1512
|
+
const propertyType = remoteType(descriptor.value);
|
|
1513
|
+
const propertySubtype = propertyType === "object" ? subtypeOf(descriptor.value) : void 0;
|
|
1514
|
+
properties.push({
|
|
1515
|
+
name: String(key),
|
|
1516
|
+
type: propertyType,
|
|
1517
|
+
value: previewText(descriptor.value),
|
|
1518
|
+
...propertySubtype === void 0 ? {} : { subtype: propertySubtype }
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
return {
|
|
1523
|
+
type,
|
|
1524
|
+
...subtype === void 0 ? {} : { subtype },
|
|
1525
|
+
description: describe(value),
|
|
1526
|
+
overflow,
|
|
1527
|
+
properties
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
function remoteType(value) {
|
|
1531
|
+
if (value === null) return "object";
|
|
1532
|
+
return typeof value;
|
|
1533
|
+
}
|
|
1534
|
+
function subtypeOf(value) {
|
|
1535
|
+
if (value === null) return "null";
|
|
1536
|
+
if (Array.isArray(value)) return "array";
|
|
1537
|
+
if (ArrayBuffer.isView(value)) return value instanceof DataView ? "dataview" : "typedarray";
|
|
1538
|
+
if (typeof value !== "object") return void 0;
|
|
1539
|
+
for (const [prototype, subtype] of SUBTYPES_BY_PROTOTYPE) if (inheritsFrom(value, prototype)) return subtype;
|
|
1540
|
+
}
|
|
1541
|
+
function className(value) {
|
|
1542
|
+
if (typeof value === "function") return functionName(value);
|
|
1543
|
+
if (typeof value === "symbol") return "Symbol";
|
|
1544
|
+
if (typeof value !== "object" || value === null) return "Object";
|
|
1545
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1546
|
+
let prototype = prototypeOf(value);
|
|
1547
|
+
while (prototype !== null && visited.size < MAX_CLASS_PROTOTYPE_DEPTH && !visited.has(prototype)) {
|
|
1548
|
+
visited.add(prototype);
|
|
1549
|
+
const constructor = Reflect.getOwnPropertyDescriptor(prototype, "constructor");
|
|
1550
|
+
const candidate = constructor !== void 0 && "value" in constructor ? constructor.value : void 0;
|
|
1551
|
+
if (typeof candidate === "function") return functionName(candidate);
|
|
1552
|
+
prototype = prototypeOf(prototype);
|
|
1553
|
+
}
|
|
1554
|
+
return "Object";
|
|
1555
|
+
}
|
|
1556
|
+
function describe(value) {
|
|
1557
|
+
if (typeof value === "function") try {
|
|
1558
|
+
return Function.prototype.toString.call(value);
|
|
1559
|
+
} catch {
|
|
1560
|
+
return functionName(value);
|
|
1561
|
+
}
|
|
1562
|
+
const subtype = subtypeOf(value);
|
|
1563
|
+
if (subtype === "array") {
|
|
1564
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(value, "length");
|
|
1565
|
+
const length = descriptor !== void 0 && "value" in descriptor ? descriptor.value : void 0;
|
|
1566
|
+
return `Array(${typeof length === "number" ? String(length) : "?"})`;
|
|
1567
|
+
}
|
|
1568
|
+
if (subtype === "error") {
|
|
1569
|
+
const stack = ownString(value, "stack");
|
|
1570
|
+
if (stack !== void 0) return stack;
|
|
1571
|
+
const name = ownString(value, "name") ?? className(value);
|
|
1572
|
+
const message = ownString(value, "message");
|
|
1573
|
+
return message === void 0 || message.length === 0 ? name : `${name}: ${message}`;
|
|
1574
|
+
}
|
|
1575
|
+
if (subtype === "date") try {
|
|
1576
|
+
return Date.prototype.toString.call(value);
|
|
1577
|
+
} catch {
|
|
1578
|
+
return "Date";
|
|
1579
|
+
}
|
|
1580
|
+
if (subtype === "regexp") try {
|
|
1581
|
+
return RegExp.prototype.toString.call(value);
|
|
1582
|
+
} catch {
|
|
1583
|
+
return "RegExp";
|
|
1584
|
+
}
|
|
1585
|
+
return className(value);
|
|
1586
|
+
}
|
|
1587
|
+
function previewText(value) {
|
|
1588
|
+
if (typeof value === "string") return value.slice(0, 100);
|
|
1589
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "symbol") return String(value);
|
|
1590
|
+
if (value === null) return "null";
|
|
1591
|
+
if (value === void 0) return "undefined";
|
|
1592
|
+
return describe(value).slice(0, 100);
|
|
1593
|
+
}
|
|
1594
|
+
function functionName(value) {
|
|
1595
|
+
try {
|
|
1596
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(value, "name");
|
|
1597
|
+
const name = descriptor !== void 0 && "value" in descriptor ? descriptor.value : void 0;
|
|
1598
|
+
return typeof name === "string" && name.length > 0 ? name : "Function";
|
|
1599
|
+
} catch {
|
|
1600
|
+
return "Function";
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
function prototypeOf(value) {
|
|
1604
|
+
try {
|
|
1605
|
+
return Reflect.getPrototypeOf(value);
|
|
1606
|
+
} catch {
|
|
1607
|
+
return null;
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
function inheritsFrom(value, expected) {
|
|
1611
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1612
|
+
let current = prototypeOf(value);
|
|
1613
|
+
while (current !== null && visited.size < MAX_CLASS_PROTOTYPE_DEPTH && !visited.has(current)) {
|
|
1614
|
+
if (current === expected) return true;
|
|
1615
|
+
visited.add(current);
|
|
1616
|
+
current = prototypeOf(current);
|
|
1617
|
+
}
|
|
1618
|
+
return false;
|
|
1619
|
+
}
|
|
1620
|
+
function ownString(value, key) {
|
|
1621
|
+
try {
|
|
1622
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(value, key);
|
|
1623
|
+
return descriptor !== void 0 && "value" in descriptor && typeof descriptor.value === "string" ? descriptor.value : void 0;
|
|
1624
|
+
} catch {
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
function renderError$4(error) {
|
|
1629
|
+
return error instanceof Error ? error.message : String(error);
|
|
1630
|
+
}
|
|
1631
|
+
const SUBTYPES_BY_PROTOTYPE = [
|
|
1632
|
+
[RegExp.prototype, "regexp"],
|
|
1633
|
+
[Date.prototype, "date"],
|
|
1634
|
+
[Map.prototype, "map"],
|
|
1635
|
+
[Set.prototype, "set"],
|
|
1636
|
+
[WeakMap.prototype, "weakmap"],
|
|
1637
|
+
[WeakSet.prototype, "weakset"],
|
|
1638
|
+
[Error.prototype, "error"],
|
|
1639
|
+
[Promise.prototype, "promise"],
|
|
1640
|
+
[ArrayBuffer.prototype, "arraybuffer"],
|
|
1641
|
+
[DataView.prototype, "dataview"]
|
|
1642
|
+
];
|
|
1643
|
+
//#endregion
|
|
1644
|
+
//#region lib/types/client/cdp/properties.js
|
|
1645
|
+
/** Lazy Client property enumeration for `Runtime.getProperties`. */
|
|
1646
|
+
/**
|
|
1647
|
+
* Read property descriptors without invoking getters.
|
|
1648
|
+
* @param objects - Object table that owns the requested handle.
|
|
1649
|
+
* @param command - Validated property request.
|
|
1650
|
+
* @param maxProperties - Maximum descriptors returned by this operation.
|
|
1651
|
+
* @param allocation - Current operation's object-allocation identity.
|
|
1652
|
+
* @returns Own or inherited descriptors and the immediate prototype.
|
|
1653
|
+
*/
|
|
1654
|
+
function getClientProperties(objects, command, maxProperties, allocation) {
|
|
1655
|
+
const raw = objects.get(command.handle);
|
|
1656
|
+
if (!isObjectLike(raw)) return { properties: [] };
|
|
1657
|
+
const value = typeof raw === "symbol" ? Symbol.prototype : raw;
|
|
1658
|
+
const group = objects.group(command.handle);
|
|
1659
|
+
const properties = [];
|
|
1660
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1661
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1662
|
+
let owner = value;
|
|
1663
|
+
let own = true;
|
|
1664
|
+
while (owner !== null) {
|
|
1665
|
+
if (visited.has(owner) || visited.size >= maxProperties) throw new ClientRuntimeExecutionError("result-too-large", "Client prototype traversal exceeded its configured limit");
|
|
1666
|
+
visited.add(owner);
|
|
1667
|
+
const keys = readKeys(owner);
|
|
1668
|
+
for (const key of keys) {
|
|
1669
|
+
if (seen.has(key)) continue;
|
|
1670
|
+
seen.add(key);
|
|
1671
|
+
if (command.nonIndexedPropertiesOnly === true && typeof key === "string" && isArrayIndex(key)) continue;
|
|
1672
|
+
const descriptor = readDescriptor(owner, key);
|
|
1673
|
+
if (descriptor === void 0) continue;
|
|
1674
|
+
if (command.accessorPropertiesOnly === true && "value" in descriptor) continue;
|
|
1675
|
+
if (properties.length >= maxProperties) throw new ClientRuntimeExecutionError("result-too-large", `Client property result exceeds the configured ${String(maxProperties)}-property limit`);
|
|
1676
|
+
properties.push(toRemoteDescriptor(objects, key, descriptor, group, own, command.generatePreview === true, allocation));
|
|
1677
|
+
}
|
|
1678
|
+
if (command.ownProperties === true) break;
|
|
1679
|
+
owner = readPrototype(owner);
|
|
1680
|
+
own = false;
|
|
1681
|
+
}
|
|
1682
|
+
if (command.accessorPropertiesOnly === true) return { properties };
|
|
1683
|
+
const prototype = readPrototype(value);
|
|
1684
|
+
return {
|
|
1685
|
+
properties,
|
|
1686
|
+
internalProperties: prototype === null ? [] : [{
|
|
1687
|
+
name: "[[Prototype]]",
|
|
1688
|
+
value: objects.serialize(prototype, remoteOptions(group, command.generatePreview), allocation)
|
|
1689
|
+
}]
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
function toRemoteDescriptor(objects, key, descriptor, group, own, generatePreview, allocation) {
|
|
1693
|
+
const common = {
|
|
1694
|
+
name: typeof key === "symbol" ? key.description ?? String(key) : String(key),
|
|
1695
|
+
configurable: descriptor.configurable ?? false,
|
|
1696
|
+
enumerable: descriptor.enumerable ?? false,
|
|
1697
|
+
isOwn: own,
|
|
1698
|
+
...typeof key === "symbol" ? { symbol: objects.serialize(key, remoteOptions(group), allocation) } : {}
|
|
1699
|
+
};
|
|
1700
|
+
if ("value" in descriptor) return {
|
|
1701
|
+
...common,
|
|
1702
|
+
value: objects.serialize(descriptor.value, remoteOptions(group, generatePreview), allocation),
|
|
1703
|
+
writable: descriptor.writable ?? false
|
|
1704
|
+
};
|
|
1705
|
+
const getter = Reflect.get(descriptor, "get");
|
|
1706
|
+
const setter = Reflect.get(descriptor, "set");
|
|
1707
|
+
return {
|
|
1708
|
+
...common,
|
|
1709
|
+
...getter === void 0 ? {} : { get: objects.serialize(getter, remoteOptions(group), allocation) },
|
|
1710
|
+
...setter === void 0 ? {} : { set: objects.serialize(setter, remoteOptions(group), allocation) }
|
|
1711
|
+
};
|
|
1712
|
+
}
|
|
1713
|
+
function readKeys(value) {
|
|
1714
|
+
try {
|
|
1715
|
+
return Reflect.ownKeys(value);
|
|
1716
|
+
} catch (error) {
|
|
1717
|
+
throw new ClientRuntimeExecutionError("internal-error", `Cannot enumerate Client object: ${renderError$3(error)}`);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
function readDescriptor(value, key) {
|
|
1721
|
+
try {
|
|
1722
|
+
return Reflect.getOwnPropertyDescriptor(value, key);
|
|
1723
|
+
} catch (error) {
|
|
1724
|
+
throw new ClientRuntimeExecutionError("internal-error", `Cannot read Client property ${String(key)}: ${renderError$3(error)}`);
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
function readPrototype(value) {
|
|
1728
|
+
try {
|
|
1729
|
+
return Object.getPrototypeOf(value);
|
|
1730
|
+
} catch (error) {
|
|
1731
|
+
throw new ClientRuntimeExecutionError("internal-error", `Cannot read Client object prototype: ${renderError$3(error)}`);
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
function isObjectLike(value) {
|
|
1735
|
+
return typeof value === "object" && value !== null || typeof value === "function" || typeof value === "symbol";
|
|
1736
|
+
}
|
|
1737
|
+
function isArrayIndex(value) {
|
|
1738
|
+
const number = Number(value);
|
|
1739
|
+
return Number.isInteger(number) && number >= 0 && number < 4294967295 && String(number) === value;
|
|
1740
|
+
}
|
|
1741
|
+
function renderError$3(error) {
|
|
1742
|
+
return error instanceof Error ? error.message : String(error);
|
|
1743
|
+
}
|
|
1744
|
+
function remoteOptions(group, generatePreview) {
|
|
1745
|
+
return {
|
|
1746
|
+
...group === void 0 ? {} : { group },
|
|
1747
|
+
...generatePreview === void 0 ? {} : { generatePreview }
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1750
|
+
//#endregion
|
|
1751
|
+
//#region lib/types/client/cdp/runtime.js
|
|
1752
|
+
/** Client-realm executor for the typed Runtime command protocol. */
|
|
1753
|
+
const MAX_RUNTIME_ERROR_MESSAGE_LENGTH = 2048;
|
|
1754
|
+
/**
|
|
1755
|
+
* Describe browser-side Runtime execution.
|
|
1756
|
+
* @param origin - Origin assigned to the synthetic execution context.
|
|
1757
|
+
* @returns The Runtime capability advertised by a browser Client source.
|
|
1758
|
+
*/
|
|
1759
|
+
function runtimeBridgeCapability(origin) {
|
|
1760
|
+
return {
|
|
1761
|
+
type: "client-runtime",
|
|
1762
|
+
origin
|
|
1763
|
+
};
|
|
1764
|
+
}
|
|
1765
|
+
/** Executes Runtime requests while isolating object handles by DevTools session. */
|
|
1766
|
+
var ClientRuntimeExecutor = class {
|
|
1767
|
+
limits;
|
|
1768
|
+
resolveScript;
|
|
1769
|
+
sessions = /* @__PURE__ */ new Map();
|
|
1770
|
+
responseAllocations = /* @__PURE__ */ new Map();
|
|
1771
|
+
constructor(limits, resolveScript = () => void 0) {
|
|
1772
|
+
this.limits = limits;
|
|
1773
|
+
this.resolveScript = resolveScript;
|
|
1774
|
+
}
|
|
1775
|
+
/**
|
|
1776
|
+
* Execute one request and preserve its source, generation, session, and request identities.
|
|
1777
|
+
* @param frame - Validated command envelope from the Worker.
|
|
1778
|
+
* @param signal - Optional cancellation for an operation awaiting user code.
|
|
1779
|
+
* @param deferObjectCommit - Keep new object handles provisional until {@link acknowledge}.
|
|
1780
|
+
* @returns A success or transport-error response for the same request.
|
|
1781
|
+
*/
|
|
1782
|
+
async execute(frame, signal, deferObjectCommit = false) {
|
|
1783
|
+
const session = this.session(frame.sessionId);
|
|
1784
|
+
const allocation = session.beginAllocation();
|
|
1785
|
+
try {
|
|
1786
|
+
const result = await session.execute(frame.command, allocation, signal);
|
|
1787
|
+
if (signal?.aborted === true) throw new ClientRuntimeExecutionError("timeout", "Client Runtime request was canceled");
|
|
1788
|
+
const response = responseFrame(frame, {
|
|
1789
|
+
ok: true,
|
|
1790
|
+
result
|
|
1791
|
+
});
|
|
1792
|
+
if (!isJsonValue(response) || jsonByteLength(response) > this.limits.maxResponseBytes) {
|
|
1793
|
+
session.rollback(allocation);
|
|
1794
|
+
return responseFrame(frame, {
|
|
1795
|
+
ok: false,
|
|
1796
|
+
error: {
|
|
1797
|
+
code: "result-too-large",
|
|
1798
|
+
message: "Client Runtime result exceeds the source-frame byte limit"
|
|
1799
|
+
}
|
|
1800
|
+
});
|
|
1801
|
+
}
|
|
1802
|
+
if (deferObjectCommit) {
|
|
1803
|
+
if (this.responseAllocations.has(frame.requestId)) {
|
|
1804
|
+
session.rollback(allocation);
|
|
1805
|
+
return responseFrame(frame, {
|
|
1806
|
+
ok: false,
|
|
1807
|
+
error: {
|
|
1808
|
+
code: "invalid-request",
|
|
1809
|
+
message: "Client Runtime request id is already pending"
|
|
1810
|
+
}
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1813
|
+
this.responseAllocations.set(frame.requestId, {
|
|
1814
|
+
sessionId: frame.sessionId,
|
|
1815
|
+
session,
|
|
1816
|
+
allocation
|
|
1817
|
+
});
|
|
1818
|
+
} else session.commitAllocation(allocation);
|
|
1819
|
+
return response;
|
|
1820
|
+
} catch (error) {
|
|
1821
|
+
session.rollback(allocation);
|
|
1822
|
+
return responseFrame(frame, {
|
|
1823
|
+
ok: false,
|
|
1824
|
+
error: runtimeError(error)
|
|
1825
|
+
});
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
/**
|
|
1829
|
+
* Commit handles after the Worker accepts one Runtime response.
|
|
1830
|
+
* @param sessionId - Session that owns the response.
|
|
1831
|
+
* @param requestId - Correlation id acknowledged by the Worker.
|
|
1832
|
+
*/
|
|
1833
|
+
acknowledge(sessionId, requestId) {
|
|
1834
|
+
const pending = this.responseAllocations.get(requestId);
|
|
1835
|
+
if (pending === void 0 || pending.sessionId !== sessionId) return;
|
|
1836
|
+
this.responseAllocations.delete(requestId);
|
|
1837
|
+
pending.session.commitAllocation(pending.allocation);
|
|
1838
|
+
}
|
|
1839
|
+
/**
|
|
1840
|
+
* Roll back handles from a canceled or otherwise unaccepted Runtime response.
|
|
1841
|
+
* @param sessionId - Session that owns the response.
|
|
1842
|
+
* @param requestId - Correlation id rejected by the Worker.
|
|
1843
|
+
*/
|
|
1844
|
+
cancel(sessionId, requestId) {
|
|
1845
|
+
const pending = this.responseAllocations.get(requestId);
|
|
1846
|
+
if (pending === void 0 || pending.sessionId !== sessionId) return;
|
|
1847
|
+
this.responseAllocations.delete(requestId);
|
|
1848
|
+
pending.session.rollback(pending.allocation);
|
|
1849
|
+
}
|
|
1850
|
+
/**
|
|
1851
|
+
* Release all values retained for one closed DevTools connection.
|
|
1852
|
+
* @param sessionId - Runtime session owned by that DevTools connection.
|
|
1853
|
+
*/
|
|
1854
|
+
closeSession(sessionId) {
|
|
1855
|
+
for (const [requestId, pending] of this.responseAllocations) if (pending.sessionId === sessionId) this.responseAllocations.delete(requestId);
|
|
1856
|
+
this.sessions.get(sessionId)?.close();
|
|
1857
|
+
this.sessions.delete(sessionId);
|
|
1858
|
+
}
|
|
1859
|
+
/**
|
|
1860
|
+
* Release one object group without closing the surrounding Runtime session.
|
|
1861
|
+
* @param sessionId - Session that owns the retained objects.
|
|
1862
|
+
* @param group - Object-group name to release.
|
|
1863
|
+
*/
|
|
1864
|
+
releaseObjectGroup(sessionId, group) {
|
|
1865
|
+
this.sessions.get(sessionId)?.releaseObjectGroup(group);
|
|
1866
|
+
}
|
|
1867
|
+
/**
|
|
1868
|
+
* Serialize one Console call for a specific DevTools Runtime session.
|
|
1869
|
+
* @param sessionId - Session receiving the Console event.
|
|
1870
|
+
* @param type - Console API operation.
|
|
1871
|
+
* @param values - Original arguments from the page call.
|
|
1872
|
+
* @param timestamp - Epoch timestamp in milliseconds.
|
|
1873
|
+
* @param stackTrace - Browser call frames captured before deferred delivery.
|
|
1874
|
+
* @returns A wire-safe event whose object handles belong only to this session.
|
|
1875
|
+
*/
|
|
1876
|
+
consoleEvent(sessionId, type, values, timestamp, stackTrace) {
|
|
1877
|
+
const session = this.session(sessionId);
|
|
1878
|
+
const allocation = session.beginAllocation();
|
|
1879
|
+
try {
|
|
1880
|
+
const event = {
|
|
1881
|
+
type: "console-api",
|
|
1882
|
+
event: {
|
|
1883
|
+
type,
|
|
1884
|
+
arguments: session.serializeAll(values, "console", allocation),
|
|
1885
|
+
timestamp,
|
|
1886
|
+
...stackTrace === void 0 ? {} : { stackTrace }
|
|
1887
|
+
}
|
|
1888
|
+
};
|
|
1889
|
+
if (!isJsonValue(event) || jsonByteLength(event) + 4096 > this.limits.maxResponseBytes) {
|
|
1890
|
+
session.rollback(allocation);
|
|
1891
|
+
return;
|
|
1892
|
+
}
|
|
1893
|
+
session.commitAllocation(allocation);
|
|
1894
|
+
return event;
|
|
1895
|
+
} catch (error) {
|
|
1896
|
+
session.rollback(allocation);
|
|
1897
|
+
throw error;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
/**
|
|
1901
|
+
* Serialize one uncaught Client exception for a DevTools Runtime session.
|
|
1902
|
+
* @param sessionId - Session receiving the exception event.
|
|
1903
|
+
* @param error - Thrown or rejected value.
|
|
1904
|
+
* @param timestamp - Epoch timestamp in milliseconds.
|
|
1905
|
+
* @param stackTrace - Browser call frames attached to the failure.
|
|
1906
|
+
* @returns A wire-safe exception event.
|
|
1907
|
+
*/
|
|
1908
|
+
exceptionEvent(sessionId, error, timestamp, stackTrace) {
|
|
1909
|
+
const session = this.session(sessionId);
|
|
1910
|
+
const allocation = session.beginAllocation();
|
|
1911
|
+
try {
|
|
1912
|
+
const event = {
|
|
1913
|
+
type: "exception",
|
|
1914
|
+
event: {
|
|
1915
|
+
timestamp,
|
|
1916
|
+
details: session.describeException(error, "console", stackTrace, allocation)
|
|
1917
|
+
}
|
|
1918
|
+
};
|
|
1919
|
+
if (!isJsonValue(event) || jsonByteLength(event) + 4096 > this.limits.maxResponseBytes) {
|
|
1920
|
+
session.rollback(allocation);
|
|
1921
|
+
return;
|
|
1922
|
+
}
|
|
1923
|
+
session.commitAllocation(allocation);
|
|
1924
|
+
return event;
|
|
1925
|
+
} catch (serializationError) {
|
|
1926
|
+
session.rollback(allocation);
|
|
1927
|
+
throw serializationError;
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
/** Release all sessions when a source generation ends or reconnects. */
|
|
1931
|
+
reset() {
|
|
1932
|
+
this.responseAllocations.clear();
|
|
1933
|
+
for (const session of this.sessions.values()) session.close();
|
|
1934
|
+
this.sessions.clear();
|
|
1935
|
+
}
|
|
1936
|
+
session(sessionId) {
|
|
1937
|
+
let session = this.sessions.get(sessionId);
|
|
1938
|
+
if (session === void 0) {
|
|
1939
|
+
session = new ClientRuntimeSession(this.limits.maxObjectsPerSession, this.limits.maxPropertiesPerResult, this.resolveScript);
|
|
1940
|
+
this.sessions.set(sessionId, session);
|
|
1941
|
+
}
|
|
1942
|
+
return session;
|
|
1943
|
+
}
|
|
1944
|
+
};
|
|
1945
|
+
var ClientRuntimeSession = class {
|
|
1946
|
+
maxProperties;
|
|
1947
|
+
resolveScript;
|
|
1948
|
+
objects;
|
|
1949
|
+
constructor(maxObjects, maxProperties, resolveScript) {
|
|
1950
|
+
this.maxProperties = maxProperties;
|
|
1951
|
+
this.resolveScript = resolveScript;
|
|
1952
|
+
this.objects = new ClientObjectStore(maxObjects);
|
|
1953
|
+
}
|
|
1954
|
+
beginAllocation() {
|
|
1955
|
+
return this.objects.beginAllocation();
|
|
1956
|
+
}
|
|
1957
|
+
commitAllocation(allocation) {
|
|
1958
|
+
this.objects.commitAllocation(allocation);
|
|
1959
|
+
}
|
|
1960
|
+
rollback(allocation) {
|
|
1961
|
+
this.objects.rollback(allocation);
|
|
1962
|
+
}
|
|
1963
|
+
async execute(command, allocation, signal) {
|
|
1964
|
+
switch (command.op) {
|
|
1965
|
+
case "evaluate": return {
|
|
1966
|
+
op: command.op,
|
|
1967
|
+
completion: await this.evaluate(command, allocation, signal)
|
|
1968
|
+
};
|
|
1969
|
+
case "get-properties": {
|
|
1970
|
+
const result = getClientProperties(this.objects, command, this.maxProperties, allocation);
|
|
1971
|
+
return {
|
|
1972
|
+
op: command.op,
|
|
1973
|
+
...result
|
|
1974
|
+
};
|
|
1975
|
+
}
|
|
1976
|
+
case "call-function": return {
|
|
1977
|
+
op: command.op,
|
|
1978
|
+
completion: await this.callFunction(command, allocation, signal)
|
|
1979
|
+
};
|
|
1980
|
+
case "await-promise": return {
|
|
1981
|
+
op: command.op,
|
|
1982
|
+
completion: await this.awaitPromise(command, allocation, signal)
|
|
1983
|
+
};
|
|
1984
|
+
case "release-object":
|
|
1985
|
+
this.objects.release(command.handle);
|
|
1986
|
+
return { op: command.op };
|
|
1987
|
+
case "release-object-group":
|
|
1988
|
+
this.releaseObjectGroup(command.objectGroup);
|
|
1989
|
+
return { op: command.op };
|
|
1990
|
+
case "global-lexical-scope-names": return {
|
|
1991
|
+
op: command.op,
|
|
1992
|
+
names: []
|
|
1993
|
+
};
|
|
1994
|
+
default: return assertNever$1(command);
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
close() {
|
|
1998
|
+
this.objects.clear();
|
|
1999
|
+
}
|
|
2000
|
+
releaseObjectGroup(group) {
|
|
2001
|
+
this.objects.releaseGroup(group);
|
|
2002
|
+
}
|
|
2003
|
+
serializeAll(values, group, allocation) {
|
|
2004
|
+
return values.map((value) => this.objects.serialize(value, {
|
|
2005
|
+
group,
|
|
2006
|
+
generatePreview: true
|
|
2007
|
+
}, allocation));
|
|
2008
|
+
}
|
|
2009
|
+
describeException(error, group, stackTrace, allocation) {
|
|
2010
|
+
const options = { ...group === void 0 ? {} : { group } };
|
|
2011
|
+
const resolvedStackTrace = stackTrace ?? clientErrorStack(error, this.resolveScript);
|
|
2012
|
+
const firstFrame = resolvedStackTrace?.callFrames[0];
|
|
2013
|
+
return {
|
|
2014
|
+
text: "Uncaught",
|
|
2015
|
+
lineNumber: firstFrame?.lineNumber ?? 0,
|
|
2016
|
+
columnNumber: firstFrame?.columnNumber ?? 0,
|
|
2017
|
+
...firstFrame === void 0 ? clientUrl() : { url: firstFrame.url },
|
|
2018
|
+
...resolvedStackTrace === void 0 ? {} : { stackTrace: resolvedStackTrace },
|
|
2019
|
+
exception: this.objects.serialize(error, options, allocation)
|
|
2020
|
+
};
|
|
2021
|
+
}
|
|
2022
|
+
async evaluate(command, allocation, signal) {
|
|
2023
|
+
let value;
|
|
2024
|
+
try {
|
|
2025
|
+
value = globalThis.eval(command.expression);
|
|
2026
|
+
if (command.awaitPromise === true) value = await awaitWithCancellation(value, signal, command.timeoutMs);
|
|
2027
|
+
} catch (error) {
|
|
2028
|
+
if (error instanceof ClientRuntimeExecutionError) throw error;
|
|
2029
|
+
return this.exception(error, command.objectGroup, allocation);
|
|
2030
|
+
}
|
|
2031
|
+
return this.completion(value, allocation, command.objectGroup, command.generatePreview, command.returnByValue);
|
|
2032
|
+
}
|
|
2033
|
+
async callFunction(command, allocation, signal) {
|
|
2034
|
+
const receiver = command.receiver === void 0 ? globalThis : this.objects.get(command.receiver);
|
|
2035
|
+
const inheritedGroup = command.receiver === void 0 ? void 0 : this.objects.group(command.receiver);
|
|
2036
|
+
const group = command.objectGroup ?? inheritedGroup;
|
|
2037
|
+
const args = (command.arguments ?? []).map((argument) => this.resolveArgument(argument));
|
|
2038
|
+
let value;
|
|
2039
|
+
try {
|
|
2040
|
+
const fn = globalThis.eval(`(${command.functionDeclaration}\n)`);
|
|
2041
|
+
if (typeof fn !== "function") throw new TypeError("functionDeclaration did not evaluate to a function");
|
|
2042
|
+
value = Reflect.apply(fn, receiver, args);
|
|
2043
|
+
if (command.awaitPromise === true) value = await awaitWithCancellation(value, signal);
|
|
2044
|
+
} catch (error) {
|
|
2045
|
+
if (error instanceof ClientRuntimeExecutionError) throw error;
|
|
2046
|
+
return this.exception(error, group, allocation);
|
|
2047
|
+
}
|
|
2048
|
+
return this.completion(value, allocation, group, command.generatePreview, command.returnByValue);
|
|
2049
|
+
}
|
|
2050
|
+
async awaitPromise(command, allocation, signal) {
|
|
2051
|
+
const group = this.objects.group(command.promise);
|
|
2052
|
+
let value;
|
|
2053
|
+
try {
|
|
2054
|
+
value = await awaitWithCancellation(this.objects.get(command.promise), signal);
|
|
2055
|
+
} catch (error) {
|
|
2056
|
+
if (error instanceof ClientRuntimeExecutionError) throw error;
|
|
2057
|
+
return this.exception(error, group, allocation);
|
|
2058
|
+
}
|
|
2059
|
+
return this.completion(value, allocation, group, command.generatePreview, command.returnByValue);
|
|
2060
|
+
}
|
|
2061
|
+
resolveArgument(argument) {
|
|
2062
|
+
switch (argument.kind) {
|
|
2063
|
+
case "value": return argument.value;
|
|
2064
|
+
case "object": return this.objects.get(argument.handle);
|
|
2065
|
+
case "undefined": return;
|
|
2066
|
+
case "unserializable": return parseUnserializable(argument.value);
|
|
2067
|
+
default: return assertNever$1(argument);
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
exception(error, group, allocation) {
|
|
2071
|
+
const options = { ...group === void 0 ? {} : { group } };
|
|
2072
|
+
const details = this.describeException(error, group, void 0, allocation);
|
|
2073
|
+
return {
|
|
2074
|
+
result: this.objects.serialize(error, options, allocation),
|
|
2075
|
+
exceptionDetails: details
|
|
2076
|
+
};
|
|
2077
|
+
}
|
|
2078
|
+
completion(value, allocation, group, generatePreview, returnByValue) {
|
|
2079
|
+
return { result: this.objects.serialize(value, {
|
|
2080
|
+
...group === void 0 ? {} : { group },
|
|
2081
|
+
...generatePreview === void 0 ? {} : { generatePreview },
|
|
2082
|
+
...returnByValue === void 0 ? {} : { returnByValue }
|
|
2083
|
+
}, allocation) };
|
|
2084
|
+
}
|
|
2085
|
+
};
|
|
2086
|
+
function responseFrame(request, outcome) {
|
|
2087
|
+
return {
|
|
2088
|
+
v: 0,
|
|
2089
|
+
t: "client-runtime/response",
|
|
2090
|
+
sourceId: request.sourceId,
|
|
2091
|
+
generation: request.generation,
|
|
2092
|
+
sessionId: request.sessionId,
|
|
2093
|
+
requestId: request.requestId,
|
|
2094
|
+
outcome
|
|
2095
|
+
};
|
|
2096
|
+
}
|
|
2097
|
+
function runtimeError(error) {
|
|
2098
|
+
return {
|
|
2099
|
+
code: error instanceof ClientRuntimeExecutionError ? error.code : "internal-error",
|
|
2100
|
+
message: (error instanceof Error ? error.message : String(error)).slice(0, MAX_RUNTIME_ERROR_MESSAGE_LENGTH)
|
|
2101
|
+
};
|
|
2102
|
+
}
|
|
2103
|
+
function parseUnserializable(value) {
|
|
2104
|
+
if (value === "NaN") return NaN;
|
|
2105
|
+
if (value === "Infinity") return Number.POSITIVE_INFINITY;
|
|
2106
|
+
if (value === "-Infinity") return Number.NEGATIVE_INFINITY;
|
|
2107
|
+
if (value === "-0") return -0;
|
|
2108
|
+
if (/^-?(?:0|[1-9]\d*)n$/u.test(value)) return BigInt(value.slice(0, -1));
|
|
2109
|
+
throw new ClientRuntimeExecutionError("invalid-request", `Unsupported unserializable value ${JSON.stringify(value)}`);
|
|
2110
|
+
}
|
|
2111
|
+
function clientUrl() {
|
|
2112
|
+
const location = Reflect.get(globalThis, "location");
|
|
2113
|
+
if (typeof location !== "object" || location === null) return {};
|
|
2114
|
+
const href = Reflect.get(location, "href");
|
|
2115
|
+
return typeof href === "string" ? { url: href } : {};
|
|
2116
|
+
}
|
|
2117
|
+
async function awaitWithCancellation(value, signal, timeoutMs) {
|
|
2118
|
+
if (signal?.aborted === true) throw new ClientRuntimeExecutionError("timeout", "Client Runtime request was canceled");
|
|
2119
|
+
let timer;
|
|
2120
|
+
let onAbort;
|
|
2121
|
+
try {
|
|
2122
|
+
const limits = [];
|
|
2123
|
+
if (timeoutMs !== void 0) limits.push(new Promise((_resolve, reject) => {
|
|
2124
|
+
timer = setTimeout(() => {
|
|
2125
|
+
reject(new ClientRuntimeExecutionError("timeout", `Client evaluation exceeded ${String(timeoutMs)}ms`));
|
|
2126
|
+
}, timeoutMs);
|
|
2127
|
+
}));
|
|
2128
|
+
if (signal !== void 0) limits.push(new Promise((_resolve, reject) => {
|
|
2129
|
+
onAbort = () => {
|
|
2130
|
+
reject(new ClientRuntimeExecutionError("timeout", "Client Runtime request was canceled"));
|
|
2131
|
+
};
|
|
2132
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2133
|
+
}));
|
|
2134
|
+
return await Promise.race([Promise.resolve(value), ...limits]);
|
|
2135
|
+
} finally {
|
|
2136
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
2137
|
+
if (onAbort !== void 0) signal?.removeEventListener("abort", onAbort);
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
function assertNever$1(value) {
|
|
2141
|
+
throw new Error(`Unexpected Client Runtime variant: ${JSON.stringify(value)}`);
|
|
2142
|
+
}
|
|
2143
|
+
//#endregion
|
|
2144
|
+
//#region lib/types/client/cdp/sources.js
|
|
2145
|
+
/** Browser-side catalog for the Inspector Client bundle and its source map. */
|
|
2146
|
+
const PACKAGE_ID = "@deepseek-ai/dsh-experimental-inspector";
|
|
2147
|
+
const CLIENT_SCRIPT_KEY = inspectorId("client-bundle", "scriptKey");
|
|
2148
|
+
/**
|
|
2149
|
+
* Describe browser-side source access.
|
|
2150
|
+
* @param available - Whether the Client bundle was discovered.
|
|
2151
|
+
* @returns The Sources capability when this Client discovered its bundle.
|
|
2152
|
+
*/
|
|
2153
|
+
function sourcesBridgeCapability(available) {
|
|
2154
|
+
return available ? { type: "client-sources" } : void 0;
|
|
2155
|
+
}
|
|
2156
|
+
/** Deliberate error serialized by the Client source transport. */
|
|
2157
|
+
var ClientSourceCatalogError = class extends Error {
|
|
2158
|
+
code;
|
|
2159
|
+
constructor(code, message) {
|
|
2160
|
+
super(message);
|
|
2161
|
+
this.code = code;
|
|
2162
|
+
}
|
|
2163
|
+
};
|
|
2164
|
+
/** Executes bounded, read-only operations over Client script assets. */
|
|
2165
|
+
var ClientSourceCatalog = class {
|
|
2166
|
+
assets = /* @__PURE__ */ new Map();
|
|
2167
|
+
constructor(assets) {
|
|
2168
|
+
for (const asset of assets) {
|
|
2169
|
+
if (this.assets.has(asset.scriptKey)) throw new Error(`inspector: duplicate Client script key ${asset.scriptKey}`);
|
|
2170
|
+
this.assets.set(asset.scriptKey, { asset });
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
/**
|
|
2174
|
+
* Resolve a stack-frame URL to this catalog's local script key.
|
|
2175
|
+
* @param url - Absolute or page-relative stack-frame URL.
|
|
2176
|
+
* @returns The matching script key when the URL belongs to this catalog.
|
|
2177
|
+
*/
|
|
2178
|
+
scriptKeyForUrl(url) {
|
|
2179
|
+
const normalized = normalizedUrl(url);
|
|
2180
|
+
for (const entry of this.assets.values()) if (normalizedUrl(entry.asset.url) === normalized) return entry.asset.scriptKey;
|
|
2181
|
+
}
|
|
2182
|
+
/**
|
|
2183
|
+
* Execute one validated source operation.
|
|
2184
|
+
* @param command - Read-only catalog command.
|
|
2185
|
+
* @param maxContentBytes - Maximum encoded bytes admitted for one asset.
|
|
2186
|
+
* @returns Script metadata or one bounded content chunk.
|
|
2187
|
+
*/
|
|
2188
|
+
async execute(command, maxContentBytes) {
|
|
2189
|
+
if (command.op === "list-scripts") return {
|
|
2190
|
+
op: command.op,
|
|
2191
|
+
scripts: await Promise.all([...this.assets.values()].map(async (entry) => this.describe(entry, maxContentBytes)))
|
|
2192
|
+
};
|
|
2193
|
+
const entry = this.assets.get(command.scriptKey);
|
|
2194
|
+
if (entry === void 0) throw new ClientSourceCatalogError("script-not-found", "Client script is not available");
|
|
2195
|
+
const bytes = command.content === "source" ? await this.sourceBytes(entry, maxContentBytes) : await this.sourceMapBytes(entry, maxContentBytes);
|
|
2196
|
+
if (bytes === void 0) return {
|
|
2197
|
+
op: command.op,
|
|
2198
|
+
scriptKey: command.scriptKey,
|
|
2199
|
+
content: command.content,
|
|
2200
|
+
available: false
|
|
2201
|
+
};
|
|
2202
|
+
if (command.offset > bytes.byteLength) throw new ClientSourceCatalogError("invalid-request", "Client source chunk offset exceeds content length");
|
|
2203
|
+
const nextOffset = Math.min(bytes.byteLength, command.offset + command.maxBytes);
|
|
2204
|
+
return {
|
|
2205
|
+
op: command.op,
|
|
2206
|
+
scriptKey: command.scriptKey,
|
|
2207
|
+
content: command.content,
|
|
2208
|
+
available: true,
|
|
2209
|
+
offset: command.offset,
|
|
2210
|
+
nextOffset,
|
|
2211
|
+
data: bytesToBase64(bytes.subarray(command.offset, nextOffset)),
|
|
2212
|
+
eof: nextOffset === bytes.byteLength
|
|
2213
|
+
};
|
|
2214
|
+
}
|
|
2215
|
+
async describe(entry, maxContentBytes) {
|
|
2216
|
+
const source = await this.source(entry, maxContentBytes);
|
|
2217
|
+
const newline = source.lastIndexOf("\n");
|
|
2218
|
+
return {
|
|
2219
|
+
scriptKey: entry.asset.scriptKey,
|
|
2220
|
+
url: entry.asset.url,
|
|
2221
|
+
hash: entry.asset.hash,
|
|
2222
|
+
buildId: "",
|
|
2223
|
+
...entry.asset.sourceMapUrl === void 0 ? {} : { sourceMapUrl: entry.asset.sourceMapUrl },
|
|
2224
|
+
startLine: 0,
|
|
2225
|
+
startColumn: 0,
|
|
2226
|
+
endLine: countNewlines(source),
|
|
2227
|
+
endColumn: newline === -1 ? source.length : source.length - newline - 1,
|
|
2228
|
+
...entry.asset.isModule === void 0 ? {} : { isModule: entry.asset.isModule },
|
|
2229
|
+
length: source.length
|
|
2230
|
+
};
|
|
2231
|
+
}
|
|
2232
|
+
source(entry, maxContentBytes) {
|
|
2233
|
+
entry.source ??= entry.asset.loadSource().catch((error) => {
|
|
2234
|
+
throw new ClientSourceCatalogError("load-failed", `Cannot load Client script: ${renderError$2(error)}`);
|
|
2235
|
+
});
|
|
2236
|
+
return entry.source.then((source) => {
|
|
2237
|
+
if (new TextEncoder().encode(source).byteLength > maxContentBytes) throw new ClientSourceCatalogError("result-too-large", "Client script exceeds the configured content limit");
|
|
2238
|
+
return source;
|
|
2239
|
+
});
|
|
2240
|
+
}
|
|
2241
|
+
sourceBytes(entry, maxContentBytes) {
|
|
2242
|
+
entry.sourceBytes ??= this.source(entry, maxContentBytes).then((source) => new TextEncoder().encode(source));
|
|
2243
|
+
return entry.sourceBytes;
|
|
2244
|
+
}
|
|
2245
|
+
sourceMapBytes(entry, maxContentBytes) {
|
|
2246
|
+
if (entry.asset.loadSourceMap === void 0) return Promise.resolve(void 0);
|
|
2247
|
+
entry.sourceMapBytes ??= entry.asset.loadSourceMap().then((value) => value === void 0 ? void 0 : new TextEncoder().encode(value)).catch((error) => {
|
|
2248
|
+
throw new ClientSourceCatalogError("load-failed", `Cannot load Client source map: ${renderError$2(error)}`);
|
|
2249
|
+
});
|
|
2250
|
+
return entry.sourceMapBytes.then((bytes) => {
|
|
2251
|
+
if (bytes !== void 0 && bytes.byteLength > maxContentBytes) throw new ClientSourceCatalogError("result-too-large", "Client source map exceeds the configured content limit");
|
|
2252
|
+
return bytes;
|
|
2253
|
+
});
|
|
2254
|
+
}
|
|
2255
|
+
};
|
|
2256
|
+
/**
|
|
2257
|
+
* Discover this package's bundle URL from the Host-injected web boot graph.
|
|
2258
|
+
* @returns A lazy catalog, or `undefined` outside the assembled web application.
|
|
2259
|
+
*/
|
|
2260
|
+
function discoverInspectorClientSourceCatalog() {
|
|
2261
|
+
const graph = Reflect.get(globalThis, "__DSH_BOOT__");
|
|
2262
|
+
if (typeof graph !== "object" || graph === null) return void 0;
|
|
2263
|
+
const entries = Reflect.get(graph, "entries");
|
|
2264
|
+
if (!Array.isArray(entries)) return void 0;
|
|
2265
|
+
const row = entries.find((value) => {
|
|
2266
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2267
|
+
return Reflect.get(value, "id") === PACKAGE_ID;
|
|
2268
|
+
});
|
|
2269
|
+
if (row === void 0 || typeof row.url !== "string" || typeof row.rev !== "string") return void 0;
|
|
2270
|
+
const base = browserLocation();
|
|
2271
|
+
if (base === void 0) return void 0;
|
|
2272
|
+
const sourceUrl = new URL(row.url, base);
|
|
2273
|
+
const sourceMapUrl = new URL(sourceUrl.href);
|
|
2274
|
+
sourceMapUrl.pathname = `${sourceMapUrl.pathname}.map`;
|
|
2275
|
+
return new ClientSourceCatalog([{
|
|
2276
|
+
scriptKey: CLIENT_SCRIPT_KEY,
|
|
2277
|
+
url: sourceUrl.href,
|
|
2278
|
+
hash: row.rev,
|
|
2279
|
+
sourceMapUrl: sourceMapUrl.href,
|
|
2280
|
+
isModule: false,
|
|
2281
|
+
loadSource: async () => fetchText(sourceUrl.href),
|
|
2282
|
+
loadSourceMap: async () => fetchText(sourceMapUrl.href)
|
|
2283
|
+
}]);
|
|
2284
|
+
}
|
|
2285
|
+
async function fetchText(url) {
|
|
2286
|
+
const response = await fetch(url);
|
|
2287
|
+
if (!response.ok) throw new Error(`${String(response.status)} ${response.statusText}`);
|
|
2288
|
+
return response.text();
|
|
2289
|
+
}
|
|
2290
|
+
function browserLocation() {
|
|
2291
|
+
const location = Reflect.get(globalThis, "location");
|
|
2292
|
+
if (typeof location !== "object" || location === null) return void 0;
|
|
2293
|
+
const href = Reflect.get(location, "href");
|
|
2294
|
+
return typeof href === "string" ? href : void 0;
|
|
2295
|
+
}
|
|
2296
|
+
function countNewlines(value) {
|
|
2297
|
+
let count = 0;
|
|
2298
|
+
for (let index = 0; index < value.length; index++) if (value.charCodeAt(index) === 10) count++;
|
|
2299
|
+
return count;
|
|
2300
|
+
}
|
|
2301
|
+
function renderError$2(error) {
|
|
2302
|
+
return error instanceof Error ? error.message : String(error);
|
|
2303
|
+
}
|
|
2304
|
+
function normalizedUrl(value) {
|
|
2305
|
+
try {
|
|
2306
|
+
const url = new URL(value, browserLocation());
|
|
2307
|
+
url.hash = "";
|
|
2308
|
+
return url.href;
|
|
2309
|
+
} catch {
|
|
2310
|
+
return value;
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
//#endregion
|
|
2314
|
+
//#region lib/types/client/cdp/index.js
|
|
2315
|
+
/** Source-side CDP capability declarations for the browser Client realm. */
|
|
2316
|
+
/**
|
|
2317
|
+
* Describe Client operations that require Worker-to-page bridge messages.
|
|
2318
|
+
* @param origin - Origin assigned to the synthetic execution context.
|
|
2319
|
+
* @param hasSources - Whether the Client bundle source was discovered.
|
|
2320
|
+
* @returns Capabilities included in the Client source handshake.
|
|
2321
|
+
*/
|
|
2322
|
+
function bridgeCapabilities(origin, hasSources) {
|
|
2323
|
+
return [
|
|
2324
|
+
runtimeBridgeCapability(origin),
|
|
2325
|
+
consoleBridgeCapability(),
|
|
2326
|
+
sourcesBridgeCapability(hasSources),
|
|
2327
|
+
void 0,
|
|
2328
|
+
void 0,
|
|
2329
|
+
void 0
|
|
2330
|
+
].filter((capability) => capability !== void 0);
|
|
2331
|
+
}
|
|
2332
|
+
//#endregion
|
|
2333
|
+
//#region lib/types/client/inspection/realm.js
|
|
2334
|
+
/** Stable Client source identity with a fresh descriptor for each WebSocket generation. */
|
|
2335
|
+
const CLIENT_SOURCE_STORAGE_KEY = "dsh.experimental-inspector.client-source-id.v0";
|
|
2336
|
+
const CLIENT_SOURCE_LOCK_PREFIX = "dsh.experimental-inspector.client-source:";
|
|
2337
|
+
/** Owns one browser realm's stable source id across transport reconnects. */
|
|
2338
|
+
var ClientRealmSource = class ClientRealmSource {
|
|
2339
|
+
label;
|
|
2340
|
+
releaseClaim;
|
|
2341
|
+
/** Logical source id retained across reconnecting transport generations. */
|
|
2342
|
+
sourceId;
|
|
2343
|
+
constructor(label, sourceId = sessionClientSourceId(), releaseClaim) {
|
|
2344
|
+
this.label = label;
|
|
2345
|
+
this.releaseClaim = releaseClaim;
|
|
2346
|
+
this.sourceId = sourceId;
|
|
2347
|
+
}
|
|
2348
|
+
/**
|
|
2349
|
+
* Claim the tab identity before opening its source transport. Browsers with
|
|
2350
|
+
* Web Locks reject a copied `sessionStorage` identity while its original tab
|
|
2351
|
+
* remains live; a fresh id is persisted and claimed instead.
|
|
2352
|
+
* @param label - Human-readable Client label reported to the Worker.
|
|
2353
|
+
* @returns The claimed realm source.
|
|
2354
|
+
*/
|
|
2355
|
+
static async claim(label) {
|
|
2356
|
+
let sourceId = sessionClientSourceId();
|
|
2357
|
+
const locks = browserLockManager();
|
|
2358
|
+
if (locks === void 0) return new ClientRealmSource(label, sourceId);
|
|
2359
|
+
while (true) {
|
|
2360
|
+
const release = await tryClaimSourceId(locks, sourceId);
|
|
2361
|
+
if (release !== void 0) {
|
|
2362
|
+
persistClientSourceId(sourceId);
|
|
2363
|
+
return new ClientRealmSource(label, sourceId, release);
|
|
2364
|
+
}
|
|
2365
|
+
sourceId = generatedClientSourceId();
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
/**
|
|
2369
|
+
* Create the descriptor for one newly admitted transport generation.
|
|
2370
|
+
* @param hasSources - Whether the built Client bundle is available for source reads.
|
|
2371
|
+
* @returns A source descriptor with a fresh generation.
|
|
2372
|
+
*/
|
|
2373
|
+
connect(hasSources) {
|
|
2374
|
+
return {
|
|
2375
|
+
sourceId: this.sourceId,
|
|
2376
|
+
generation: inspectorId(randomUUID(), "generation"),
|
|
2377
|
+
kind: "client",
|
|
2378
|
+
label: this.label,
|
|
2379
|
+
timeOriginMs: performance.timeOrigin,
|
|
2380
|
+
capabilities: bridgeCapabilities(clientOrigin(), hasSources)
|
|
2381
|
+
};
|
|
2382
|
+
}
|
|
2383
|
+
/** Release this page's identity claim. */
|
|
2384
|
+
close() {
|
|
2385
|
+
this.releaseClaim?.();
|
|
2386
|
+
this.releaseClaim = void 0;
|
|
2387
|
+
}
|
|
2388
|
+
};
|
|
2389
|
+
function sessionClientSourceId() {
|
|
2390
|
+
const generated = generatedClientSourceId();
|
|
2391
|
+
try {
|
|
2392
|
+
const stored = sessionStorage.getItem(CLIENT_SOURCE_STORAGE_KEY);
|
|
2393
|
+
if (stored !== null) try {
|
|
2394
|
+
return inspectorId(stored, "sourceId");
|
|
2395
|
+
} catch {}
|
|
2396
|
+
sessionStorage.setItem(CLIENT_SOURCE_STORAGE_KEY, generated);
|
|
2397
|
+
} catch {}
|
|
2398
|
+
return generated;
|
|
2399
|
+
}
|
|
2400
|
+
function generatedClientSourceId() {
|
|
2401
|
+
return inspectorId(`client-${randomUUID()}`, "sourceId");
|
|
2402
|
+
}
|
|
2403
|
+
function persistClientSourceId(sourceId) {
|
|
2404
|
+
try {
|
|
2405
|
+
sessionStorage.setItem(CLIENT_SOURCE_STORAGE_KEY, sourceId);
|
|
2406
|
+
} catch {}
|
|
2407
|
+
}
|
|
2408
|
+
function browserLockManager() {
|
|
2409
|
+
if (typeof navigator === "undefined") return void 0;
|
|
2410
|
+
return navigator.locks;
|
|
2411
|
+
}
|
|
2412
|
+
function tryClaimSourceId(locks, sourceId) {
|
|
2413
|
+
return new Promise((resolve, reject) => {
|
|
2414
|
+
let release;
|
|
2415
|
+
const held = new Promise((released) => {
|
|
2416
|
+
release = released;
|
|
2417
|
+
});
|
|
2418
|
+
locks.request(`${CLIENT_SOURCE_LOCK_PREFIX}${sourceId}`, { ifAvailable: true }, async (lock) => {
|
|
2419
|
+
if (lock === null) {
|
|
2420
|
+
resolve(void 0);
|
|
2421
|
+
return;
|
|
2422
|
+
}
|
|
2423
|
+
resolve(release);
|
|
2424
|
+
await held;
|
|
2425
|
+
}).catch(reject);
|
|
2426
|
+
});
|
|
2427
|
+
}
|
|
2428
|
+
function clientOrigin() {
|
|
2429
|
+
const location = Reflect.get(globalThis, "location");
|
|
2430
|
+
if (typeof location !== "object" || location === null) return "";
|
|
2431
|
+
const origin = Reflect.get(location, "origin");
|
|
2432
|
+
return typeof origin === "string" ? origin : "";
|
|
2433
|
+
}
|
|
2434
|
+
//#endregion
|
|
2435
|
+
//#region lib/types/client/inspection/network.js
|
|
2436
|
+
/** Client network observation is not enabled in the current source producer. */
|
|
2437
|
+
/** Observation topics published by the Client network adapter. */
|
|
2438
|
+
const NETWORK_TOPICS = [];
|
|
2439
|
+
//#endregion
|
|
2440
|
+
//#region lib/types/client/bridge/lifecycle.js
|
|
2441
|
+
/** Reconnection lifecycle for the browser Client bridge. */
|
|
2442
|
+
/** Owns one bounded-backoff timer and prevents reconnection after disposal. */
|
|
2443
|
+
var ClientBridgeLifecycle = class {
|
|
2444
|
+
baseDelayMs;
|
|
2445
|
+
maxDelayMs;
|
|
2446
|
+
reconnectAttempt = 0;
|
|
2447
|
+
reconnectTimer;
|
|
2448
|
+
closed = false;
|
|
2449
|
+
constructor(baseDelayMs, maxDelayMs) {
|
|
2450
|
+
this.baseDelayMs = baseDelayMs;
|
|
2451
|
+
this.maxDelayMs = maxDelayMs;
|
|
2452
|
+
}
|
|
2453
|
+
/** Reset backoff after the Worker accepts a source generation. */
|
|
2454
|
+
connected() {
|
|
2455
|
+
this.reconnectAttempt = 0;
|
|
2456
|
+
}
|
|
2457
|
+
/**
|
|
2458
|
+
* Schedule the next reconnect attempt unless one is already pending.
|
|
2459
|
+
* @param connect - Operation that opens the next transport generation.
|
|
2460
|
+
*/
|
|
2461
|
+
reconnect(connect) {
|
|
2462
|
+
if (this.reconnectTimer !== void 0 || this.closed) return;
|
|
2463
|
+
const cap = Math.min(this.maxDelayMs, this.baseDelayMs * 2 ** this.reconnectAttempt);
|
|
2464
|
+
this.reconnectAttempt++;
|
|
2465
|
+
this.reconnectTimer = setTimeout(() => {
|
|
2466
|
+
this.reconnectTimer = void 0;
|
|
2467
|
+
connect();
|
|
2468
|
+
}, cap / 2 + Math.random() * cap / 2);
|
|
2469
|
+
}
|
|
2470
|
+
/** Stop pending and future reconnect attempts. */
|
|
2471
|
+
close() {
|
|
2472
|
+
if (this.closed) return;
|
|
2473
|
+
this.closed = true;
|
|
2474
|
+
if (this.reconnectTimer !== void 0) clearTimeout(this.reconnectTimer);
|
|
2475
|
+
this.reconnectTimer = void 0;
|
|
2476
|
+
}
|
|
2477
|
+
};
|
|
2478
|
+
//#endregion
|
|
2479
|
+
//#region lib/types/shared/bridge/buffer.js
|
|
2480
|
+
/** Realm-neutral bounded buffering for Host and Client observation sources. */
|
|
2481
|
+
const SOURCE_FRAME_OVERHEAD_BYTES = 4096;
|
|
2482
|
+
/**
|
|
2483
|
+
* Owns retained state, queued events, and source-local sequencing independently
|
|
2484
|
+
* of whether frames travel over MessagePort or WebSocket.
|
|
2485
|
+
*/
|
|
2486
|
+
var InspectorSourceBuffer = class {
|
|
2487
|
+
options;
|
|
2488
|
+
queue = [];
|
|
2489
|
+
state = /* @__PURE__ */ new Map();
|
|
2490
|
+
queuedBytes = 0;
|
|
2491
|
+
nextSequence = 1;
|
|
2492
|
+
expectedSequence = 1;
|
|
2493
|
+
constructor(options) {
|
|
2494
|
+
this.options = options;
|
|
2495
|
+
}
|
|
2496
|
+
/** Whether at least one observation is waiting for transport. */
|
|
2497
|
+
get hasPending() {
|
|
2498
|
+
return this.queue.length > 0;
|
|
2499
|
+
}
|
|
2500
|
+
/**
|
|
2501
|
+
* Validate and enqueue one observation, dropping the oldest prefix as needed.
|
|
2502
|
+
* A record larger than one transport frame is dropped after consuming its sequence number.
|
|
2503
|
+
* @param topic - Declared domain topic.
|
|
2504
|
+
* @param payload - Lossless JSON payload.
|
|
2505
|
+
* @param monotonicMs - Finite source-clock timestamp.
|
|
2506
|
+
*/
|
|
2507
|
+
publish(topic, payload, monotonicMs) {
|
|
2508
|
+
this.enqueue(this.record(topic, payload, monotonicMs));
|
|
2509
|
+
}
|
|
2510
|
+
/**
|
|
2511
|
+
* Replace one retained topic and enqueue the same observation for live delivery.
|
|
2512
|
+
* @param topic - Declared state topic.
|
|
2513
|
+
* @param payload - Lossless JSON payload retained for replacement frames.
|
|
2514
|
+
* @param monotonicMs - Finite source-clock timestamp.
|
|
2515
|
+
*/
|
|
2516
|
+
setState(topic, payload, monotonicMs) {
|
|
2517
|
+
const record = this.record(topic, payload, monotonicMs);
|
|
2518
|
+
const previous = this.state.get(topic);
|
|
2519
|
+
this.state.set(topic, record);
|
|
2520
|
+
if (!this.stateFits()) {
|
|
2521
|
+
if (previous === void 0) this.state.delete(topic);
|
|
2522
|
+
else this.state.set(topic, previous);
|
|
2523
|
+
throw new Error("inspector: source state exceeds the source-frame byte limit");
|
|
2524
|
+
}
|
|
2525
|
+
this.enqueue(record);
|
|
2526
|
+
}
|
|
2527
|
+
/**
|
|
2528
|
+
* Build a complete state replacement and absorb every preceding queue drop.
|
|
2529
|
+
* @param sourceId - Logical source identity.
|
|
2530
|
+
* @param generation - Current transport generation.
|
|
2531
|
+
* @returns A replacement frame whose sequence is the next append position.
|
|
2532
|
+
*/
|
|
2533
|
+
replacement(sourceId, generation) {
|
|
2534
|
+
const nextSequence = this.queue[0]?.sequence ?? this.nextSequence;
|
|
2535
|
+
this.expectedSequence = nextSequence;
|
|
2536
|
+
return {
|
|
2537
|
+
v: 0,
|
|
2538
|
+
t: "source/replace",
|
|
2539
|
+
sourceId,
|
|
2540
|
+
generation,
|
|
2541
|
+
nextSequence,
|
|
2542
|
+
records: [...this.state.values()]
|
|
2543
|
+
};
|
|
2544
|
+
}
|
|
2545
|
+
/**
|
|
2546
|
+
* Remove and sequence the next transport-sized observation batch.
|
|
2547
|
+
* @param sourceId - Logical source identity.
|
|
2548
|
+
* @param generation - Current transport generation.
|
|
2549
|
+
* @returns The next append frame, or `undefined` when the queue is empty.
|
|
2550
|
+
*/
|
|
2551
|
+
takeBatch(sourceId, generation) {
|
|
2552
|
+
if (this.queue.length === 0) return void 0;
|
|
2553
|
+
const batch = [];
|
|
2554
|
+
let batchBytes = SOURCE_FRAME_OVERHEAD_BYTES;
|
|
2555
|
+
const first = this.queue[0];
|
|
2556
|
+
while (batch.length < this.options.maxRecordsPerFrame && this.queue.length > 0) {
|
|
2557
|
+
const candidate = this.queue[0];
|
|
2558
|
+
if (candidate.sequence !== first.sequence + batch.length) break;
|
|
2559
|
+
if (batch.length > 0 && batchBytes + candidate.bytes > this.options.maxFrameBytes) break;
|
|
2560
|
+
this.queue.shift();
|
|
2561
|
+
batch.push(candidate);
|
|
2562
|
+
batchBytes += candidate.bytes;
|
|
2563
|
+
}
|
|
2564
|
+
this.queuedBytes -= batch.reduce((sum, item) => sum + item.bytes, 0);
|
|
2565
|
+
const firstSequence = first.sequence;
|
|
2566
|
+
const frame = {
|
|
2567
|
+
v: 0,
|
|
2568
|
+
t: "source/append",
|
|
2569
|
+
sourceId,
|
|
2570
|
+
generation,
|
|
2571
|
+
firstSequence,
|
|
2572
|
+
droppedBefore: firstSequence - this.expectedSequence,
|
|
2573
|
+
records: batch.map((item) => item.record)
|
|
2574
|
+
};
|
|
2575
|
+
this.expectedSequence = firstSequence + frame.records.length;
|
|
2576
|
+
return frame;
|
|
2577
|
+
}
|
|
2578
|
+
/** Discard observations that have not entered a transport frame. */
|
|
2579
|
+
discardPending() {
|
|
2580
|
+
this.queue.length = 0;
|
|
2581
|
+
this.queuedBytes = 0;
|
|
2582
|
+
}
|
|
2583
|
+
record(topic, payload, monotonicMs) {
|
|
2584
|
+
if (topic.length === 0 || topic.length > 128) throw new Error("inspector: topic must contain 1 to 128 characters");
|
|
2585
|
+
if (!this.options.topics.includes("*") && !this.options.topics.includes(topic)) throw new Error(`inspector: source does not declare topic ${JSON.stringify(topic)}`);
|
|
2586
|
+
if (!isJsonValue(payload)) throw new Error("inspector: source payload must be lossless JSON data");
|
|
2587
|
+
if (!Number.isFinite(monotonicMs)) throw new Error("inspector: monotonicMs must be finite");
|
|
2588
|
+
return {
|
|
2589
|
+
monotonicMs,
|
|
2590
|
+
topic,
|
|
2591
|
+
payload
|
|
2592
|
+
};
|
|
2593
|
+
}
|
|
2594
|
+
enqueue(record) {
|
|
2595
|
+
const bytes = jsonByteLength(record);
|
|
2596
|
+
const sequence = this.nextSequence++;
|
|
2597
|
+
if (bytes + SOURCE_FRAME_OVERHEAD_BYTES > this.options.maxFrameBytes) return;
|
|
2598
|
+
this.queue.push({
|
|
2599
|
+
sequence,
|
|
2600
|
+
bytes,
|
|
2601
|
+
record
|
|
2602
|
+
});
|
|
2603
|
+
this.queuedBytes += bytes;
|
|
2604
|
+
while (this.queue.length > this.options.maxQueuedRecords || this.queuedBytes > this.options.maxQueuedBytes) {
|
|
2605
|
+
const dropped = this.queue.shift();
|
|
2606
|
+
this.queuedBytes -= dropped.bytes;
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
stateFits() {
|
|
2610
|
+
return jsonByteLength([...this.state.values()]) + SOURCE_FRAME_OVERHEAD_BYTES <= this.options.maxFrameBytes;
|
|
2611
|
+
}
|
|
2612
|
+
};
|
|
2613
|
+
//#endregion
|
|
2614
|
+
//#region lib/types/client/bridge/publisher.js
|
|
2615
|
+
/** Buffered Client observation publication across reconnecting WebSockets. */
|
|
2616
|
+
/** Non-blocking Client publisher whose bounded state survives transport reconnects. */
|
|
2617
|
+
var ClientBridgePublisher = class {
|
|
2618
|
+
maxBufferedBytes;
|
|
2619
|
+
records;
|
|
2620
|
+
active;
|
|
2621
|
+
flushTimer;
|
|
2622
|
+
closed = false;
|
|
2623
|
+
constructor(options, maxBufferedBytes) {
|
|
2624
|
+
this.maxBufferedBytes = maxBufferedBytes;
|
|
2625
|
+
this.records = new InspectorSourceBuffer(options);
|
|
2626
|
+
}
|
|
2627
|
+
publish(topic, payload, monotonicMs = performance.now()) {
|
|
2628
|
+
if (this.closed) return;
|
|
2629
|
+
this.records.publish(topic, payload, monotonicMs);
|
|
2630
|
+
this.flush();
|
|
2631
|
+
}
|
|
2632
|
+
setState(topic, payload, monotonicMs = performance.now()) {
|
|
2633
|
+
if (this.closed) throw new Error("inspector: Client source is closed");
|
|
2634
|
+
this.records.setState(topic, payload, monotonicMs);
|
|
2635
|
+
this.flush();
|
|
2636
|
+
}
|
|
2637
|
+
/**
|
|
2638
|
+
* Install one unopened transport generation.
|
|
2639
|
+
* @param socket - WebSocket carrying the generation.
|
|
2640
|
+
* @param source - Source identity and generation sent by the socket.
|
|
2641
|
+
*/
|
|
2642
|
+
connect(socket, source) {
|
|
2643
|
+
this.active = {
|
|
2644
|
+
socket,
|
|
2645
|
+
source,
|
|
2646
|
+
accepted: false
|
|
2647
|
+
};
|
|
2648
|
+
}
|
|
2649
|
+
/**
|
|
2650
|
+
* Send retained state and queued observations after Worker acceptance.
|
|
2651
|
+
* @param socket - Accepted active WebSocket.
|
|
2652
|
+
*/
|
|
2653
|
+
accept(socket) {
|
|
2654
|
+
const active = this.active;
|
|
2655
|
+
if (active?.socket !== socket) return;
|
|
2656
|
+
active.accepted = true;
|
|
2657
|
+
this.replace(socket);
|
|
2658
|
+
this.flush();
|
|
2659
|
+
}
|
|
2660
|
+
/**
|
|
2661
|
+
* Resend retained state for the active generation.
|
|
2662
|
+
* @param socket - WebSocket that received the resnapshot request.
|
|
2663
|
+
*/
|
|
2664
|
+
replace(socket) {
|
|
2665
|
+
const active = this.active;
|
|
2666
|
+
if (active?.socket !== socket || socket.readyState !== WebSocket.OPEN) return;
|
|
2667
|
+
socket.send(JSON.stringify(this.records.replacement(active.source.sourceId, active.source.generation)));
|
|
2668
|
+
}
|
|
2669
|
+
/**
|
|
2670
|
+
* Forget one closed transport while retaining buffered state for reconnect.
|
|
2671
|
+
* @param socket - WebSocket whose close event fired.
|
|
2672
|
+
*/
|
|
2673
|
+
disconnect(socket) {
|
|
2674
|
+
if (this.active?.socket === socket) this.active = void 0;
|
|
2675
|
+
}
|
|
2676
|
+
/** Stop delayed writes and reject later publication. */
|
|
2677
|
+
close() {
|
|
2678
|
+
if (this.closed) return;
|
|
2679
|
+
this.closed = true;
|
|
2680
|
+
this.active = void 0;
|
|
2681
|
+
if (this.flushTimer !== void 0) clearTimeout(this.flushTimer);
|
|
2682
|
+
this.flushTimer = void 0;
|
|
2683
|
+
}
|
|
2684
|
+
flush() {
|
|
2685
|
+
const active = this.active;
|
|
2686
|
+
if (!active?.accepted || active.socket.readyState !== WebSocket.OPEN) return;
|
|
2687
|
+
if (active.socket.bufferedAmount > this.maxBufferedBytes) {
|
|
2688
|
+
this.scheduleFlush();
|
|
2689
|
+
return;
|
|
2690
|
+
}
|
|
2691
|
+
while (this.records.hasPending && active.socket.bufferedAmount <= this.maxBufferedBytes) {
|
|
2692
|
+
const frame = this.records.takeBatch(active.source.sourceId, active.source.generation);
|
|
2693
|
+
if (frame === void 0) break;
|
|
2694
|
+
active.socket.send(JSON.stringify(frame));
|
|
2695
|
+
}
|
|
2696
|
+
if (this.records.hasPending) this.scheduleFlush();
|
|
2697
|
+
}
|
|
2698
|
+
scheduleFlush() {
|
|
2699
|
+
if (this.flushTimer !== void 0 || this.closed) return;
|
|
2700
|
+
this.flushTimer = setTimeout(() => {
|
|
2701
|
+
this.flushTimer = void 0;
|
|
2702
|
+
this.flush();
|
|
2703
|
+
}, 25);
|
|
2704
|
+
}
|
|
2705
|
+
};
|
|
2706
|
+
/**
|
|
2707
|
+
* Decode a consumer-neutral tree received across an Inspector transport.
|
|
2708
|
+
* @param value - Untrusted query result value.
|
|
2709
|
+
* @returns A detached tree containing only public semantic fields.
|
|
2710
|
+
*/
|
|
2711
|
+
function parseCordisRuntimeTree(value) {
|
|
2712
|
+
const record = exactObject(value, [
|
|
2713
|
+
"schemaVersion",
|
|
2714
|
+
"host",
|
|
2715
|
+
"clients"
|
|
2716
|
+
], "Cordis runtime tree");
|
|
2717
|
+
if (record.schemaVersion !== 0 || !Array.isArray(record.clients)) throw new Error("inspector protocol: invalid Cordis runtime tree");
|
|
2718
|
+
const host = record.host === null ? null : parseRealm(record.host, "host");
|
|
2719
|
+
const clients = record.clients.map((client) => parseRealm(client, "client"));
|
|
2720
|
+
const sourceIds = /* @__PURE__ */ new Set();
|
|
2721
|
+
for (const realm of host === null ? clients : [host, ...clients]) {
|
|
2722
|
+
if (sourceIds.has(realm.source.sourceId)) throw new Error("inspector protocol: Cordis runtime tree repeats a sourceId");
|
|
2723
|
+
sourceIds.add(realm.source.sourceId);
|
|
2724
|
+
}
|
|
2725
|
+
return {
|
|
2726
|
+
schemaVersion: 0,
|
|
2727
|
+
host,
|
|
2728
|
+
clients
|
|
2729
|
+
};
|
|
2730
|
+
}
|
|
2731
|
+
function parseRealm(value, kind) {
|
|
2732
|
+
const record = exactObject(value, [
|
|
2733
|
+
"source",
|
|
2734
|
+
"connection",
|
|
2735
|
+
"revision",
|
|
2736
|
+
"truncated",
|
|
2737
|
+
"root"
|
|
2738
|
+
], "Cordis runtime realm");
|
|
2739
|
+
const source = exactObject(record.source, [
|
|
2740
|
+
"sourceId",
|
|
2741
|
+
"kind",
|
|
2742
|
+
"label"
|
|
2743
|
+
], "Cordis runtime source");
|
|
2744
|
+
if (source.kind !== kind || typeof source.label !== "string" || source.label.length === 0 || source.label.length > 256) throw new Error(`inspector protocol: invalid ${kind} Cordis runtime source`);
|
|
2745
|
+
if (!Number.isSafeInteger(record.revision) || record.revision < 1 || typeof record.truncated !== "boolean") throw new Error("inspector protocol: invalid Cordis runtime realm header");
|
|
2746
|
+
const root = parseNode(record.root, { fiberUids: /* @__PURE__ */ new Set() }, 0);
|
|
2747
|
+
if (root.kind !== "context") throw new Error("inspector protocol: Cordis runtime root must be a Context");
|
|
2748
|
+
return {
|
|
2749
|
+
source: {
|
|
2750
|
+
sourceId: wireId(source.sourceId, "sourceId"),
|
|
2751
|
+
kind,
|
|
2752
|
+
label: source.label
|
|
2753
|
+
},
|
|
2754
|
+
connection: parseConnection(record.connection),
|
|
2755
|
+
revision: record.revision,
|
|
2756
|
+
truncated: record.truncated,
|
|
2757
|
+
root
|
|
2758
|
+
};
|
|
2759
|
+
}
|
|
2760
|
+
function parseConnection(value) {
|
|
2761
|
+
if (!isPlainObject(value)) throw new Error("inspector protocol: Cordis runtime connection must be an object");
|
|
2762
|
+
if (value.state === "connected") {
|
|
2763
|
+
exactKeys(value, ["state"], "connected Cordis runtime connection");
|
|
2764
|
+
return { state: "connected" };
|
|
2765
|
+
}
|
|
2766
|
+
if (value.state === "disconnected" && typeof value.reason === "string") {
|
|
2767
|
+
exactKeys(value, ["state", "reason"], "disconnected Cordis runtime connection");
|
|
2768
|
+
return {
|
|
2769
|
+
state: "disconnected",
|
|
2770
|
+
reason: value.reason
|
|
2771
|
+
};
|
|
2772
|
+
}
|
|
2773
|
+
throw new Error("inspector protocol: invalid Cordis runtime connection");
|
|
2774
|
+
}
|
|
2775
|
+
function parseNode(value, state, depth) {
|
|
2776
|
+
if (depth > 256) throw new Error("inspector protocol: Cordis runtime tree exceeds the depth limit");
|
|
2777
|
+
if (!isPlainObject(value) || value.kind !== "context" && value.kind !== "fiber") throw new Error("inspector protocol: Cordis runtime node must have a known kind");
|
|
2778
|
+
const record = exactObject(value, value.kind === "fiber" ? [
|
|
2779
|
+
"kind",
|
|
2780
|
+
"uid",
|
|
2781
|
+
"children"
|
|
2782
|
+
] : ["kind", "children"], "Cordis runtime node");
|
|
2783
|
+
if (!Array.isArray(record.children)) throw new Error("inspector protocol: Cordis runtime node children must be an array");
|
|
2784
|
+
if (record.kind === "context") return {
|
|
2785
|
+
kind: "context",
|
|
2786
|
+
children: record.children.map((child) => parseNode(child, state, depth + 1))
|
|
2787
|
+
};
|
|
2788
|
+
if (!Number.isSafeInteger(record.uid) || record.uid < 1 || record.children.length !== 1) throw new Error("inspector protocol: invalid Cordis runtime Fiber");
|
|
2789
|
+
const uid = record.uid;
|
|
2790
|
+
if (state.fiberUids.has(uid)) throw new Error("inspector protocol: Cordis runtime tree repeats a Fiber uid");
|
|
2791
|
+
state.fiberUids.add(uid);
|
|
2792
|
+
const context = parseNode(record.children[0], state, depth + 1);
|
|
2793
|
+
if (context.kind !== "context") throw new Error("inspector protocol: Cordis runtime Fiber child must be a Context");
|
|
2794
|
+
return {
|
|
2795
|
+
kind: "fiber",
|
|
2796
|
+
uid,
|
|
2797
|
+
children: [context]
|
|
2798
|
+
};
|
|
2799
|
+
}
|
|
2800
|
+
//#endregion
|
|
2801
|
+
//#region lib/types/shared/bridge/messages/query/codec.js
|
|
2802
|
+
/** Exact decoders for non-CDP Inspector query frames. */
|
|
2803
|
+
/**
|
|
2804
|
+
* Test whether a decoded carrier value belongs to the query response protocol.
|
|
2805
|
+
* @param value - Decoded carrier value.
|
|
2806
|
+
* @returns Whether the query response decoder owns the value.
|
|
2807
|
+
*/
|
|
2808
|
+
function isInspectorQueryResponseEnvelope(value) {
|
|
2809
|
+
return isPlainObject(value) && value.t === "query/response";
|
|
2810
|
+
}
|
|
2811
|
+
/**
|
|
2812
|
+
* Decode one Worker-to-source query response.
|
|
2813
|
+
* @param value - Untrusted decoded carrier value.
|
|
2814
|
+
* @returns The detached, validated response frame.
|
|
2815
|
+
*/
|
|
2816
|
+
function parseInspectorQueryResponseFrame(value) {
|
|
2817
|
+
const record = exactObject(value, [
|
|
2818
|
+
"v",
|
|
2819
|
+
"t",
|
|
2820
|
+
"sourceId",
|
|
2821
|
+
"generation",
|
|
2822
|
+
"requestId",
|
|
2823
|
+
"outcome"
|
|
2824
|
+
], "query response");
|
|
2825
|
+
if (record.v !== 0 || record.t !== "query/response") throw new Error("inspector protocol: invalid query response envelope");
|
|
2826
|
+
return {
|
|
2827
|
+
v: 0,
|
|
2828
|
+
t: "query/response",
|
|
2829
|
+
sourceId: wireId(record.sourceId, "sourceId"),
|
|
2830
|
+
generation: wireId(record.generation, "generation"),
|
|
2831
|
+
requestId: wireId(record.requestId, "requestId"),
|
|
2832
|
+
outcome: parseOutcome(record.outcome)
|
|
2833
|
+
};
|
|
2834
|
+
}
|
|
2835
|
+
function parseResult(value) {
|
|
2836
|
+
if (!isPlainObject(value) || typeof value.op !== "string") throw new Error("inspector protocol: query result must have an op");
|
|
2837
|
+
switch (value.op) {
|
|
2838
|
+
case "cordis-tree/get":
|
|
2839
|
+
exactKeys(value, ["op", "tree"], "Cordis tree query result");
|
|
2840
|
+
return {
|
|
2841
|
+
op: "cordis-tree/get",
|
|
2842
|
+
tree: parseCordisRuntimeTree(value.tree)
|
|
2843
|
+
};
|
|
2844
|
+
default: throw new Error(`inspector protocol: unknown query result ${JSON.stringify(value.op)}`);
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
function parseOutcome(value) {
|
|
2848
|
+
if (!isPlainObject(value) || typeof value.ok !== "boolean") throw new Error("inspector protocol: invalid query outcome");
|
|
2849
|
+
if (value.ok) {
|
|
2850
|
+
exactKeys(value, ["ok", "result"], "successful query outcome");
|
|
2851
|
+
return {
|
|
2852
|
+
ok: true,
|
|
2853
|
+
result: parseResult(value.result)
|
|
2854
|
+
};
|
|
2855
|
+
}
|
|
2856
|
+
exactKeys(value, ["ok", "error"], "failed query outcome");
|
|
2857
|
+
const error = exactObject(value.error, ["code", "message"], "query error");
|
|
2858
|
+
if (!QUERY_ERROR_CODES.has(error.code) || typeof error.message !== "string") throw new Error("inspector protocol: invalid query error");
|
|
2859
|
+
return {
|
|
2860
|
+
ok: false,
|
|
2861
|
+
error: {
|
|
2862
|
+
code: error.code,
|
|
2863
|
+
message: error.message
|
|
2864
|
+
}
|
|
2865
|
+
};
|
|
2866
|
+
}
|
|
2867
|
+
const QUERY_ERROR_CODES = new Set([
|
|
2868
|
+
"invalid-request",
|
|
2869
|
+
"stale-source",
|
|
2870
|
+
"result-too-large",
|
|
2871
|
+
"internal-error"
|
|
2872
|
+
]);
|
|
2873
|
+
//#endregion
|
|
2874
|
+
//#region lib/types/shared/bridge/rpc.js
|
|
2875
|
+
/** Shared Host/Client owner of correlated non-CDP query requests. */
|
|
2876
|
+
/** Failure deliberately returned by the Worker query handler. */
|
|
2877
|
+
var InspectorQueryRemoteError = class extends Error {
|
|
2878
|
+
code;
|
|
2879
|
+
constructor(code, message) {
|
|
2880
|
+
super(message);
|
|
2881
|
+
this.code = code;
|
|
2882
|
+
}
|
|
2883
|
+
};
|
|
2884
|
+
/** Correlates requests for one reconnecting Host or Client source. */
|
|
2885
|
+
var InspectorQueryConnection = class {
|
|
2886
|
+
options;
|
|
2887
|
+
pending = /* @__PURE__ */ new Map();
|
|
2888
|
+
active;
|
|
2889
|
+
nextRequestId = 0;
|
|
2890
|
+
closed = false;
|
|
2891
|
+
constructor(options) {
|
|
2892
|
+
this.options = options;
|
|
2893
|
+
}
|
|
2894
|
+
/**
|
|
2895
|
+
* Admit the source generation acknowledged by the Worker.
|
|
2896
|
+
* @param sourceId - Stable source identity.
|
|
2897
|
+
* @param generation - Newly accepted transport generation.
|
|
2898
|
+
* @param sender - Carrier writer valid for that generation.
|
|
2899
|
+
*/
|
|
2900
|
+
connect(sourceId, generation, sender) {
|
|
2901
|
+
if (this.closed) throw new Error("inspector query connection is closed");
|
|
2902
|
+
this.disconnect("Inspector source generation replaced");
|
|
2903
|
+
this.active = {
|
|
2904
|
+
sourceId,
|
|
2905
|
+
generation,
|
|
2906
|
+
sender
|
|
2907
|
+
};
|
|
2908
|
+
}
|
|
2909
|
+
/**
|
|
2910
|
+
* Execute a query against the currently accepted source generation.
|
|
2911
|
+
* @param query - Closed typed query command.
|
|
2912
|
+
* @returns The result with the same operation discriminant.
|
|
2913
|
+
*/
|
|
2914
|
+
request(query) {
|
|
2915
|
+
const active = this.active;
|
|
2916
|
+
if (this.closed || active === void 0) return Promise.reject(/* @__PURE__ */ new Error("Inspector query transport is not connected"));
|
|
2917
|
+
const requestId = inspectorId(`query-${String(++this.nextRequestId)}`, "requestId");
|
|
2918
|
+
const frame = {
|
|
2919
|
+
v: 0,
|
|
2920
|
+
t: "query/request",
|
|
2921
|
+
sourceId: active.sourceId,
|
|
2922
|
+
generation: active.generation,
|
|
2923
|
+
requestId,
|
|
2924
|
+
query
|
|
2925
|
+
};
|
|
2926
|
+
if (jsonByteLength(frame) > this.options.maxFrameBytes) return Promise.reject(/* @__PURE__ */ new Error(`Inspector query request exceeds ${String(this.options.maxFrameBytes)} bytes`));
|
|
2927
|
+
return new Promise((resolve, reject) => {
|
|
2928
|
+
const timer = setTimeout(() => {
|
|
2929
|
+
this.pending.delete(requestId);
|
|
2930
|
+
reject(/* @__PURE__ */ new Error(`Inspector query ${query.op} timed out after ${String(this.options.timeoutMs)}ms`));
|
|
2931
|
+
}, this.options.timeoutMs);
|
|
2932
|
+
this.pending.set(requestId, {
|
|
2933
|
+
op: query.op,
|
|
2934
|
+
resolve,
|
|
2935
|
+
reject,
|
|
2936
|
+
timer
|
|
2937
|
+
});
|
|
2938
|
+
try {
|
|
2939
|
+
active.sender.send(frame);
|
|
2940
|
+
} catch (error) {
|
|
2941
|
+
this.rejectPending(requestId, renderError$1(error));
|
|
2942
|
+
}
|
|
2943
|
+
});
|
|
2944
|
+
}
|
|
2945
|
+
/**
|
|
2946
|
+
* Consume a decoded carrier value when it is a query response.
|
|
2947
|
+
* @param value - Untrusted Worker-to-source value.
|
|
2948
|
+
* @returns Whether the value belonged to the query protocol.
|
|
2949
|
+
*/
|
|
2950
|
+
receive(value) {
|
|
2951
|
+
if (!isInspectorQueryResponseEnvelope(value)) return false;
|
|
2952
|
+
let frame;
|
|
2953
|
+
try {
|
|
2954
|
+
frame = parseInspectorQueryResponseFrame(value);
|
|
2955
|
+
if (jsonByteLength(frame) > this.options.maxFrameBytes) throw new Error(`inspector protocol: query response exceeds ${String(this.options.maxFrameBytes)} bytes`);
|
|
2956
|
+
} catch (error) {
|
|
2957
|
+
this.disconnect(`Invalid Inspector query response: ${renderError$1(error).message}`);
|
|
2958
|
+
throw error;
|
|
2959
|
+
}
|
|
2960
|
+
const pending = this.pending.get(frame.requestId);
|
|
2961
|
+
if (pending === void 0) return true;
|
|
2962
|
+
const active = this.active;
|
|
2963
|
+
if (active === void 0 || frame.sourceId !== active.sourceId || frame.generation !== active.generation) {
|
|
2964
|
+
this.rejectPending(frame.requestId, /* @__PURE__ */ new Error("Inspector query response source generation does not match"));
|
|
2965
|
+
return true;
|
|
2966
|
+
}
|
|
2967
|
+
if (!frame.outcome.ok) {
|
|
2968
|
+
this.rejectPending(frame.requestId, new InspectorQueryRemoteError(frame.outcome.error.code, frame.outcome.error.message));
|
|
2969
|
+
return true;
|
|
2970
|
+
}
|
|
2971
|
+
if (frame.outcome.result.op !== pending.op) {
|
|
2972
|
+
this.rejectPending(frame.requestId, /* @__PURE__ */ new Error(`Inspector query response op ${frame.outcome.result.op} does not match ${pending.op}`));
|
|
2973
|
+
return true;
|
|
2974
|
+
}
|
|
2975
|
+
clearTimeout(pending.timer);
|
|
2976
|
+
this.pending.delete(frame.requestId);
|
|
2977
|
+
pending.resolve(frame.outcome.result);
|
|
2978
|
+
return true;
|
|
2979
|
+
}
|
|
2980
|
+
/**
|
|
2981
|
+
* Reject active requests while permitting a later source generation.
|
|
2982
|
+
* @param reason - Failure reported to every pending caller.
|
|
2983
|
+
*/
|
|
2984
|
+
disconnect(reason) {
|
|
2985
|
+
this.active = void 0;
|
|
2986
|
+
for (const requestId of [...this.pending.keys()]) this.rejectPending(requestId, new Error(reason));
|
|
2987
|
+
}
|
|
2988
|
+
/**
|
|
2989
|
+
* Permanently reject requests and prevent later reconnection.
|
|
2990
|
+
* @param reason - Failure reported to every pending caller.
|
|
2991
|
+
*/
|
|
2992
|
+
close(reason = "Inspector query connection closed") {
|
|
2993
|
+
if (this.closed) return;
|
|
2994
|
+
this.closed = true;
|
|
2995
|
+
this.disconnect(reason);
|
|
2996
|
+
}
|
|
2997
|
+
rejectPending(requestId, error) {
|
|
2998
|
+
const pending = this.pending.get(requestId);
|
|
2999
|
+
if (pending === void 0) return;
|
|
3000
|
+
clearTimeout(pending.timer);
|
|
3001
|
+
this.pending.delete(requestId);
|
|
3002
|
+
pending.reject(error);
|
|
3003
|
+
}
|
|
3004
|
+
};
|
|
3005
|
+
function renderError$1(error) {
|
|
3006
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
3007
|
+
}
|
|
3008
|
+
//#endregion
|
|
3009
|
+
//#region lib/types/client/bridge/rpc.js
|
|
3010
|
+
/** Client-side non-CDP query bridge over the active Worker WebSocket. */
|
|
3011
|
+
/** Owns query correlation across reconnecting Client source generations. */
|
|
3012
|
+
var ClientBridgeRpc = class extends InspectorQueryConnection {
|
|
3013
|
+
/**
|
|
3014
|
+
* Connect query writes to one accepted Client WebSocket generation.
|
|
3015
|
+
* @param source - Accepted source descriptor.
|
|
3016
|
+
* @param socket - Active source WebSocket.
|
|
3017
|
+
*/
|
|
3018
|
+
connectSocket(source, socket) {
|
|
3019
|
+
this.connect(source.sourceId, source.generation, { send: (frame) => {
|
|
3020
|
+
if (socket.readyState !== WebSocket.OPEN) throw new Error("Inspector Client query socket is not connected");
|
|
3021
|
+
socket.send(JSON.stringify(frame));
|
|
3022
|
+
} });
|
|
3023
|
+
}
|
|
3024
|
+
};
|
|
3025
|
+
//#endregion
|
|
3026
|
+
//#region lib/types/client/bridge/dispatcher.js
|
|
3027
|
+
/** Dispatch of validated Worker frames to browser-realm capability handlers. */
|
|
3028
|
+
/**
|
|
3029
|
+
* Dispatch one validated Worker frame without exposing transport details to domain adapters.
|
|
3030
|
+
* @param frame - Decoded Worker-to-source frame.
|
|
3031
|
+
* @param handlers - Browser-realm operations for each frame family.
|
|
3032
|
+
*/
|
|
3033
|
+
function dispatchBridgeFrame(frame, handlers) {
|
|
3034
|
+
switch (frame.t) {
|
|
3035
|
+
case "source/accepted":
|
|
3036
|
+
handlers.accepted(frame);
|
|
3037
|
+
return;
|
|
3038
|
+
case "source/append-acknowledged":
|
|
3039
|
+
handlers.acknowledged(frame);
|
|
3040
|
+
return;
|
|
3041
|
+
case "source/resnapshot":
|
|
3042
|
+
handlers.resnapshot(frame);
|
|
3043
|
+
return;
|
|
3044
|
+
case "source/rejected":
|
|
3045
|
+
handlers.rejected(frame);
|
|
3046
|
+
return;
|
|
3047
|
+
case "client-runtime/request":
|
|
3048
|
+
handlers.runtime(frame);
|
|
3049
|
+
return;
|
|
3050
|
+
case "client-runtime/cancel":
|
|
3051
|
+
handlers.runtimeCanceled(frame);
|
|
3052
|
+
return;
|
|
3053
|
+
case "client-runtime/response-acknowledged":
|
|
3054
|
+
handlers.runtimeAcknowledged(frame);
|
|
3055
|
+
return;
|
|
3056
|
+
case "client-runtime/session-closed":
|
|
3057
|
+
handlers.runtimeClosed(frame);
|
|
3058
|
+
return;
|
|
3059
|
+
case "client-console/enable":
|
|
3060
|
+
handlers.consoleEnabled(frame);
|
|
3061
|
+
return;
|
|
3062
|
+
case "client-console/disable":
|
|
3063
|
+
handlers.consoleDisabled(frame);
|
|
3064
|
+
return;
|
|
3065
|
+
case "client-sources/request":
|
|
3066
|
+
handlers.sources(frame);
|
|
3067
|
+
return;
|
|
3068
|
+
case "client-sources/session-closed":
|
|
3069
|
+
handlers.sourcesClosed(frame);
|
|
3070
|
+
return;
|
|
3071
|
+
default: return assertNever(frame);
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
function assertNever(value) {
|
|
3075
|
+
throw new Error(`Unexpected Worker source frame: ${JSON.stringify(value)}`);
|
|
3076
|
+
}
|
|
3077
|
+
//#endregion
|
|
3078
|
+
//#region lib/types/client/bridge/transport.js
|
|
3079
|
+
/** Client observation and Runtime endpoint over the Inspector Worker's ingest WebSocket. */
|
|
3080
|
+
/** Reconnecting Client source whose bounded queue never blocks page work. */
|
|
3081
|
+
var ClientInspectorSource = class extends InspectorSourceConnection {
|
|
3082
|
+
bootstrap;
|
|
3083
|
+
sourceCatalog;
|
|
3084
|
+
realmSource;
|
|
3085
|
+
publisher;
|
|
3086
|
+
socket;
|
|
3087
|
+
generation;
|
|
3088
|
+
accepted = false;
|
|
3089
|
+
closed = false;
|
|
3090
|
+
runtime;
|
|
3091
|
+
runtimeRequests = /* @__PURE__ */ new Map();
|
|
3092
|
+
console;
|
|
3093
|
+
queries;
|
|
3094
|
+
lifecycle;
|
|
3095
|
+
constructor(bootstrap, label = document.title || "Client", sourceCatalog = discoverInspectorClientSourceCatalog(), realmSource = new ClientRealmSource(label)) {
|
|
3096
|
+
super();
|
|
3097
|
+
this.bootstrap = bootstrap;
|
|
3098
|
+
this.sourceCatalog = sourceCatalog;
|
|
3099
|
+
this.realmSource = realmSource;
|
|
3100
|
+
this.lifecycle = new ClientBridgeLifecycle(bootstrap.reconnectBaseMs, bootstrap.reconnectMaxMs);
|
|
3101
|
+
this.publisher = new ClientBridgePublisher({
|
|
3102
|
+
topics: ["*"],
|
|
3103
|
+
maxQueuedRecords: bootstrap.maxQueuedRecords,
|
|
3104
|
+
maxQueuedBytes: bootstrap.maxQueuedBytes,
|
|
3105
|
+
maxRecordsPerFrame: bootstrap.maxRecordsPerFrame,
|
|
3106
|
+
maxFrameBytes: bootstrap.maxFrameBytes
|
|
3107
|
+
}, bootstrap.maxQueuedBytes);
|
|
3108
|
+
this.runtime = new ClientRuntimeExecutor({
|
|
3109
|
+
maxObjectsPerSession: bootstrap.maxRuntimeObjectsPerSession,
|
|
3110
|
+
maxPropertiesPerResult: bootstrap.maxRuntimePropertiesPerResult,
|
|
3111
|
+
maxResponseBytes: bootstrap.maxFrameBytes
|
|
3112
|
+
}, (url) => this.sourceCatalog?.scriptKeyForUrl(url));
|
|
3113
|
+
this.console = new ClientConsoleObserver(this.runtime, (sessionId, event) => {
|
|
3114
|
+
const socket = this.socket;
|
|
3115
|
+
const generation = this.generation;
|
|
3116
|
+
if (this.closed || !this.accepted || socket?.readyState !== WebSocket.OPEN || generation === void 0) return;
|
|
3117
|
+
const frame = {
|
|
3118
|
+
v: 0,
|
|
3119
|
+
t: "client-console/event",
|
|
3120
|
+
sourceId: this.realmSource.sourceId,
|
|
3121
|
+
generation,
|
|
3122
|
+
sessionId,
|
|
3123
|
+
event
|
|
3124
|
+
};
|
|
3125
|
+
if (!isJsonValue(frame) || jsonByteLength(frame) > this.bootstrap.maxFrameBytes) return;
|
|
3126
|
+
try {
|
|
3127
|
+
socket.send(JSON.stringify(frame));
|
|
3128
|
+
} catch {}
|
|
3129
|
+
}, (url) => this.sourceCatalog?.scriptKeyForUrl(url));
|
|
3130
|
+
this.queries = new ClientBridgeRpc({
|
|
3131
|
+
timeoutMs: bootstrap.queryTimeoutMs,
|
|
3132
|
+
maxFrameBytes: bootstrap.maxFrameBytes
|
|
3133
|
+
});
|
|
3134
|
+
this.connect();
|
|
3135
|
+
}
|
|
3136
|
+
/** Permanently stop reconnecting and close the active source generation. */
|
|
3137
|
+
close() {
|
|
3138
|
+
if (this.closed) return;
|
|
3139
|
+
this.closed = true;
|
|
3140
|
+
this.console.close();
|
|
3141
|
+
this.cancelRuntimeRequests();
|
|
3142
|
+
this.runtime.reset();
|
|
3143
|
+
this.queries.close("Inspector Client source closed");
|
|
3144
|
+
this.lifecycle.close();
|
|
3145
|
+
this.publisher.close();
|
|
3146
|
+
const socket = this.socket;
|
|
3147
|
+
const generation = this.generation;
|
|
3148
|
+
try {
|
|
3149
|
+
if (socket?.readyState === WebSocket.OPEN && generation !== void 0) {
|
|
3150
|
+
const frame = {
|
|
3151
|
+
v: 0,
|
|
3152
|
+
t: "source/close",
|
|
3153
|
+
sourceId: this.realmSource.sourceId,
|
|
3154
|
+
generation
|
|
3155
|
+
};
|
|
3156
|
+
socket.send(JSON.stringify(frame));
|
|
3157
|
+
socket.close(1e3, "Client source closed");
|
|
3158
|
+
} else socket?.close();
|
|
3159
|
+
} finally {
|
|
3160
|
+
this.socket = void 0;
|
|
3161
|
+
this.realmSource.close();
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
connect() {
|
|
3165
|
+
if (this.closed) return;
|
|
3166
|
+
this.console.reset();
|
|
3167
|
+
this.cancelRuntimeRequests();
|
|
3168
|
+
this.runtime.reset();
|
|
3169
|
+
this.queries.disconnect("Inspector Client source reconnecting");
|
|
3170
|
+
const source = this.realmSource.connect(this.sourceCatalog !== void 0);
|
|
3171
|
+
const generation = source.generation;
|
|
3172
|
+
const socket = new WebSocket(this.bootstrap.endpoint, this.bootstrap.protocol);
|
|
3173
|
+
this.socket = socket;
|
|
3174
|
+
this.generation = generation;
|
|
3175
|
+
this.accepted = false;
|
|
3176
|
+
this.publisher.connect(socket, source);
|
|
3177
|
+
socket.addEventListener("open", () => {
|
|
3178
|
+
if (this.socket !== socket || this.closed) return;
|
|
3179
|
+
const frame = {
|
|
3180
|
+
v: 0,
|
|
3181
|
+
t: "source/open",
|
|
3182
|
+
source,
|
|
3183
|
+
topics: ["*", ...NETWORK_TOPICS]
|
|
3184
|
+
};
|
|
3185
|
+
socket.send(JSON.stringify(frame));
|
|
3186
|
+
});
|
|
3187
|
+
socket.addEventListener("message", (event) => {
|
|
3188
|
+
if (this.socket !== socket || typeof event.data !== "string") return;
|
|
3189
|
+
try {
|
|
3190
|
+
if (new TextEncoder().encode(event.data).byteLength > this.bootstrap.maxFrameBytes) throw new Error(`inspector protocol: Worker frame exceeds ${String(this.bootstrap.maxFrameBytes)} bytes`);
|
|
3191
|
+
const value = JSON.parse(event.data);
|
|
3192
|
+
if (this.queries.receive(value)) return;
|
|
3193
|
+
const frame = parseWorkerSourceFrame(value);
|
|
3194
|
+
if (frame.t !== "source/rejected" && (frame.sourceId !== this.realmSource.sourceId || frame.generation !== generation)) return;
|
|
3195
|
+
dispatchBridgeFrame(frame, {
|
|
3196
|
+
accepted: () => {
|
|
3197
|
+
this.accepted = true;
|
|
3198
|
+
this.lifecycle.connected();
|
|
3199
|
+
this.queries.connectSocket(source, socket);
|
|
3200
|
+
this.publisher.accept(socket);
|
|
3201
|
+
},
|
|
3202
|
+
acknowledged: () => {},
|
|
3203
|
+
resnapshot: () => {
|
|
3204
|
+
this.publisher.replace(socket);
|
|
3205
|
+
},
|
|
3206
|
+
rejected: (rejected) => {
|
|
3207
|
+
console.error(`[inspector] Client source rejected: ${rejected.message}`);
|
|
3208
|
+
socket.close(1008, "source rejected");
|
|
3209
|
+
},
|
|
3210
|
+
runtime: (request) => {
|
|
3211
|
+
this.executeRuntime(socket, generation, request).catch((error) => {
|
|
3212
|
+
console.error("[inspector] Client Runtime transport failed:", error);
|
|
3213
|
+
socket.close(1011, "Client Runtime transport failed");
|
|
3214
|
+
});
|
|
3215
|
+
},
|
|
3216
|
+
runtimeCanceled: (canceled) => {
|
|
3217
|
+
this.cancelRuntime(canceled.sessionId, canceled.requestId);
|
|
3218
|
+
},
|
|
3219
|
+
runtimeAcknowledged: (acknowledged) => {
|
|
3220
|
+
this.acknowledgeRuntime(acknowledged.sessionId, acknowledged.requestId);
|
|
3221
|
+
},
|
|
3222
|
+
runtimeClosed: (closed) => {
|
|
3223
|
+
this.cancelRuntimeSession(closed.sessionId);
|
|
3224
|
+
this.console.disable(closed.sessionId);
|
|
3225
|
+
this.runtime.closeSession(closed.sessionId);
|
|
3226
|
+
},
|
|
3227
|
+
consoleEnabled: (enabled) => {
|
|
3228
|
+
this.console.enable(enabled.sessionId);
|
|
3229
|
+
},
|
|
3230
|
+
consoleDisabled: (disabled) => {
|
|
3231
|
+
this.console.disable(disabled.sessionId);
|
|
3232
|
+
},
|
|
3233
|
+
sources: (request) => {
|
|
3234
|
+
this.executeSourceRequest(socket, generation, request).catch((error) => {
|
|
3235
|
+
console.error("[inspector] Client Sources transport failed:", error);
|
|
3236
|
+
socket.close(1011, "Client Sources transport failed");
|
|
3237
|
+
});
|
|
3238
|
+
},
|
|
3239
|
+
sourcesClosed: () => {}
|
|
3240
|
+
});
|
|
3241
|
+
} catch (error) {
|
|
3242
|
+
console.error("[inspector] invalid Worker control frame:", error);
|
|
3243
|
+
socket.close(1008, "invalid Worker control frame");
|
|
3244
|
+
}
|
|
3245
|
+
});
|
|
3246
|
+
socket.addEventListener("close", () => {
|
|
3247
|
+
if (this.socket !== socket || this.closed) return;
|
|
3248
|
+
this.socket = void 0;
|
|
3249
|
+
this.accepted = false;
|
|
3250
|
+
this.publisher.disconnect(socket);
|
|
3251
|
+
this.console.reset();
|
|
3252
|
+
this.cancelRuntimeRequests();
|
|
3253
|
+
this.runtime.reset();
|
|
3254
|
+
this.queries.disconnect("Inspector Client source disconnected");
|
|
3255
|
+
this.lifecycle.reconnect(() => {
|
|
3256
|
+
this.connect();
|
|
3257
|
+
});
|
|
3258
|
+
});
|
|
3259
|
+
socket.addEventListener("error", () => {});
|
|
3260
|
+
}
|
|
3261
|
+
async executeRuntime(socket, generation, frame) {
|
|
3262
|
+
const controller = new AbortController();
|
|
3263
|
+
const operation = {
|
|
3264
|
+
controller,
|
|
3265
|
+
sessionId: frame.sessionId
|
|
3266
|
+
};
|
|
3267
|
+
this.runtimeRequests.set(frame.requestId, operation);
|
|
3268
|
+
const response = await this.runtime.execute(frame, controller.signal, true);
|
|
3269
|
+
if (this.runtimeRequests.get(frame.requestId) !== operation) return;
|
|
3270
|
+
if (this.closed || this.socket !== socket || this.generation !== generation || socket.readyState !== WebSocket.OPEN) {
|
|
3271
|
+
this.cancelRuntime(frame.sessionId, frame.requestId);
|
|
3272
|
+
return;
|
|
3273
|
+
}
|
|
3274
|
+
socket.send(JSON.stringify(response));
|
|
3275
|
+
}
|
|
3276
|
+
acknowledgeRuntime(sessionId, requestId) {
|
|
3277
|
+
const operation = this.runtimeRequests.get(requestId);
|
|
3278
|
+
if (operation === void 0 || operation.sessionId !== sessionId) return;
|
|
3279
|
+
this.runtimeRequests.delete(requestId);
|
|
3280
|
+
this.runtime.acknowledge(sessionId, requestId);
|
|
3281
|
+
}
|
|
3282
|
+
cancelRuntime(sessionId, requestId) {
|
|
3283
|
+
const operation = this.runtimeRequests.get(requestId);
|
|
3284
|
+
if (operation === void 0 || operation.sessionId !== sessionId) return;
|
|
3285
|
+
this.runtimeRequests.delete(requestId);
|
|
3286
|
+
operation.controller.abort();
|
|
3287
|
+
this.runtime.cancel(sessionId, requestId);
|
|
3288
|
+
}
|
|
3289
|
+
cancelRuntimeSession(sessionId) {
|
|
3290
|
+
for (const [requestId, operation] of this.runtimeRequests) {
|
|
3291
|
+
if (operation.sessionId !== sessionId) continue;
|
|
3292
|
+
operation.controller.abort();
|
|
3293
|
+
this.runtime.cancel(sessionId, requestId);
|
|
3294
|
+
this.runtimeRequests.delete(requestId);
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
cancelRuntimeRequests() {
|
|
3298
|
+
for (const [requestId, operation] of this.runtimeRequests) {
|
|
3299
|
+
operation.controller.abort();
|
|
3300
|
+
this.runtime.cancel(operation.sessionId, requestId);
|
|
3301
|
+
}
|
|
3302
|
+
this.runtimeRequests.clear();
|
|
3303
|
+
}
|
|
3304
|
+
async executeSourceRequest(socket, generation, frame) {
|
|
3305
|
+
let outcome;
|
|
3306
|
+
try {
|
|
3307
|
+
if (this.sourceCatalog === void 0) throw new ClientSourceCatalogError("invalid-request", "Client source catalog is unavailable");
|
|
3308
|
+
outcome = {
|
|
3309
|
+
ok: true,
|
|
3310
|
+
result: await this.sourceCatalog.execute(frame.command, this.bootstrap.maxClientSourceBytes)
|
|
3311
|
+
};
|
|
3312
|
+
} catch (error) {
|
|
3313
|
+
outcome = {
|
|
3314
|
+
ok: false,
|
|
3315
|
+
error: {
|
|
3316
|
+
code: error instanceof ClientSourceCatalogError ? error.code : "internal-error",
|
|
3317
|
+
message: renderError(error).slice(0, 2048)
|
|
3318
|
+
}
|
|
3319
|
+
};
|
|
3320
|
+
}
|
|
3321
|
+
let response = {
|
|
3322
|
+
v: 0,
|
|
3323
|
+
t: "client-sources/response",
|
|
3324
|
+
sourceId: this.realmSource.sourceId,
|
|
3325
|
+
generation,
|
|
3326
|
+
sessionId: frame.sessionId,
|
|
3327
|
+
requestId: frame.requestId,
|
|
3328
|
+
outcome
|
|
3329
|
+
};
|
|
3330
|
+
if (!isJsonValue(response) || jsonByteLength(response) > this.bootstrap.maxFrameBytes) response = {
|
|
3331
|
+
...response,
|
|
3332
|
+
outcome: {
|
|
3333
|
+
ok: false,
|
|
3334
|
+
error: {
|
|
3335
|
+
code: "result-too-large",
|
|
3336
|
+
message: "Client source result exceeds the source-frame byte limit"
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
};
|
|
3340
|
+
if (this.closed || this.socket !== socket || this.generation !== generation || socket.readyState !== WebSocket.OPEN) return;
|
|
3341
|
+
socket.send(JSON.stringify(response));
|
|
3342
|
+
}
|
|
3343
|
+
};
|
|
3344
|
+
function renderError(error) {
|
|
3345
|
+
return error instanceof Error ? error.message : String(error);
|
|
3346
|
+
}
|
|
3347
|
+
//#endregion
|
|
3348
|
+
//#region lib/types/client/bridge/controller.js
|
|
3349
|
+
/** Browser Client bridge construction for the Cordis plugin entry. */
|
|
3350
|
+
/**
|
|
3351
|
+
* Start the browser source transport for one validated Host bootstrap.
|
|
3352
|
+
* @param bootstrap - Host-injected endpoint and resource limits.
|
|
3353
|
+
* @returns The active reconnecting Client source after its tab identity is claimed.
|
|
3354
|
+
*/
|
|
3355
|
+
async function startInspectorClient(bootstrap) {
|
|
3356
|
+
const label = document.title || "Client";
|
|
3357
|
+
const realmSource = await ClientRealmSource.claim(label);
|
|
3358
|
+
try {
|
|
3359
|
+
return new ClientInspectorSource(bootstrap, label, void 0, realmSource);
|
|
3360
|
+
} catch (error) {
|
|
3361
|
+
realmSource.close();
|
|
3362
|
+
throw error;
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
//#endregion
|
|
3366
|
+
//#region lib/types/client/plugin.js
|
|
3367
|
+
/** Client Cordis plugin that publishes browser observations directly to the Inspector Worker. */
|
|
3368
|
+
/** Cordis plugin name shared with the Host face. */
|
|
3369
|
+
const name = "experimental-inspector";
|
|
3370
|
+
/** This transport root has no Client service dependencies. */
|
|
3371
|
+
const inject = [];
|
|
3372
|
+
/**
|
|
3373
|
+
* Mount the Client source and shared `ctx.inspector` publishing API.
|
|
3374
|
+
* @param ctx - Client Cordis context whose page identity and lifecycle own the source.
|
|
3375
|
+
*/
|
|
3376
|
+
async function apply(ctx) {
|
|
3377
|
+
const injected = globalThis.__DSH_INSPECTOR__;
|
|
3378
|
+
if (injected === void 0) throw new Error("experimental inspector: Host bootstrap is missing");
|
|
3379
|
+
const bootstrap = parseInspectorClientBootstrap(injected);
|
|
3380
|
+
await ctx.effect(async () => {
|
|
3381
|
+
const source = await startInspectorClient(bootstrap);
|
|
3382
|
+
const disposers = [];
|
|
3383
|
+
try {
|
|
3384
|
+
disposers.push(publishCordisTree(ctx, source, {
|
|
3385
|
+
maxNodes: bootstrap.maxCordisNodes,
|
|
3386
|
+
maxBytes: bootstrap.maxFrameBytes - 4096
|
|
3387
|
+
}));
|
|
3388
|
+
disposers.push(ctx.provide("inspector", createInspectorService(source)));
|
|
3389
|
+
} catch (error) {
|
|
3390
|
+
try {
|
|
3391
|
+
disposeInspectorClient(source, disposers);
|
|
3392
|
+
} catch (cleanupError) {
|
|
3393
|
+
ctx.logger.error("experimental-inspector: Client initialization rollback failed", cleanupError);
|
|
3394
|
+
}
|
|
3395
|
+
throw error;
|
|
3396
|
+
}
|
|
3397
|
+
return () => {
|
|
3398
|
+
disposeInspectorClient(source, disposers);
|
|
3399
|
+
};
|
|
3400
|
+
}, "experimental-inspector: Client source");
|
|
3401
|
+
}
|
|
3402
|
+
function disposeInspectorClient(source, disposers) {
|
|
3403
|
+
const failures = [];
|
|
3404
|
+
for (const dispose of [...disposers].reverse()) try {
|
|
3405
|
+
dispose();
|
|
3406
|
+
} catch (error) {
|
|
3407
|
+
failures.push(error);
|
|
3408
|
+
}
|
|
3409
|
+
try {
|
|
3410
|
+
source.close();
|
|
3411
|
+
} catch (error) {
|
|
3412
|
+
failures.push(error);
|
|
3413
|
+
}
|
|
3414
|
+
if (failures.length > 0) throw new AggregateError(failures, "experimental-inspector: Client disposal failed");
|
|
3415
|
+
}
|
|
3416
|
+
//#endregion
|
|
3417
|
+
exports.apply = apply;
|
|
3418
|
+
exports.inject = inject;
|
|
3419
|
+
exports.name = name;
|
|
3420
|
+
return module.exports;
|
|
3421
|
+
}
|
|
3422
|
+
});
|
|
3423
|
+
|
|
3424
|
+
//# sourceMappingURL=client.js.map
|