opensteer 0.8.9 → 0.8.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-RO6WAWWG.js → chunk-F3X6UOEN.js} +1959 -215
- package/dist/chunk-F3X6UOEN.js.map +1 -0
- package/dist/cli/bin.cjs +2165 -149
- package/dist/cli/bin.cjs.map +1 -1
- package/dist/cli/bin.js +281 -8
- package/dist/cli/bin.js.map +1 -1
- package/dist/index.cjs +170 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -6
- package/dist/index.d.ts +21 -6
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/skills/opensteer/SKILL.md +33 -14
- package/skills/opensteer/references/request-workflow.md +312 -193
- package/skills/opensteer/references/sdk-reference.md +102 -14
- package/skills/recorder/SKILL.md +54 -0
- package/skills/recorder/references/recorder-reference.md +71 -0
- package/dist/chunk-RO6WAWWG.js.map +0 -1
|
@@ -10806,7 +10806,8 @@ async function captureActionBoundarySnapshot(engine, pageRef) {
|
|
|
10806
10806
|
}
|
|
10807
10807
|
return {
|
|
10808
10808
|
pageRef,
|
|
10809
|
-
documentRef: mainFrame.documentRef
|
|
10809
|
+
documentRef: mainFrame.documentRef,
|
|
10810
|
+
url: mainFrame.url
|
|
10810
10811
|
};
|
|
10811
10812
|
}
|
|
10812
10813
|
function createActionBoundaryDiagnostics(input) {
|
|
@@ -14749,7 +14750,7 @@ async function clearPersistedSessionRecord(rootPath, provider) {
|
|
|
14749
14750
|
await rm(resolveLiveSessionRecordPath(rootPath, provider), { force: true });
|
|
14750
14751
|
}
|
|
14751
14752
|
function isPersistedCloudSessionRecord(value) {
|
|
14752
|
-
return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "cloud" && typeof value.sessionId === "string" && value.sessionId.length > 0 && typeof value.
|
|
14753
|
+
return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "cloud" && typeof value.sessionId === "string" && value.sessionId.length > 0 && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt);
|
|
14753
14754
|
}
|
|
14754
14755
|
function isPersistedLocalBrowserSessionRecord(value) {
|
|
14755
14756
|
return value.layout === OPENSTEER_LIVE_SESSION_LAYOUT && value.version === OPENSTEER_LIVE_SESSION_VERSION && value.provider === "local" && (value.engine === "playwright" || value.engine === "abp") && typeof value.pid === "number" && Number.isFinite(value.pid) && typeof value.startedAt === "number" && Number.isFinite(value.startedAt) && typeof value.updatedAt === "number" && Number.isFinite(value.updatedAt) && typeof value.userDataDir === "string" && value.userDataDir.length > 0;
|
|
@@ -22408,11 +22409,11 @@ var SandboxClock = class {
|
|
|
22408
22409
|
performanceNow() {
|
|
22409
22410
|
return this.mode === "manual" ? this.manualNow - this.startedAt : (globalThis.performance?.now() ?? 0) - this.performanceStartedAt;
|
|
22410
22411
|
}
|
|
22411
|
-
setTimeout(callback,
|
|
22412
|
-
return this.registerTimer(false, callback,
|
|
22412
|
+
setTimeout(callback, delay3 = 0, ...args) {
|
|
22413
|
+
return this.registerTimer(false, callback, delay3, args);
|
|
22413
22414
|
}
|
|
22414
|
-
setInterval(callback,
|
|
22415
|
-
return this.registerTimer(true, callback,
|
|
22415
|
+
setInterval(callback, delay3 = 0, ...args) {
|
|
22416
|
+
return this.registerTimer(true, callback, delay3, args);
|
|
22416
22417
|
}
|
|
22417
22418
|
clearTimeout(timerId) {
|
|
22418
22419
|
this.clearTimer(timerId);
|
|
@@ -22433,9 +22434,9 @@ var SandboxClock = class {
|
|
|
22433
22434
|
this.clearTimer(timerId);
|
|
22434
22435
|
}
|
|
22435
22436
|
}
|
|
22436
|
-
registerTimer(repeat, callback,
|
|
22437
|
+
registerTimer(repeat, callback, delay3, args) {
|
|
22437
22438
|
const timerId = this.nextTimerId++;
|
|
22438
|
-
const normalizedDelay = Math.max(0,
|
|
22439
|
+
const normalizedDelay = Math.max(0, delay3);
|
|
22439
22440
|
const record = {
|
|
22440
22441
|
callback,
|
|
22441
22442
|
args,
|
|
@@ -22942,6 +22943,7 @@ function diffInteractionTraces(left, right) {
|
|
|
22942
22943
|
// ../runtime-core/src/sdk/runtime.ts
|
|
22943
22944
|
var requireForAuthRecipeHook = createRequire(import.meta.url);
|
|
22944
22945
|
var MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS = 5e3;
|
|
22946
|
+
var PERSISTED_NETWORK_FLUSH_TIMEOUT_MS = 5e3;
|
|
22945
22947
|
var PENDING_OPERATION_EVENT_CAPTURE_LIMIT = 64;
|
|
22946
22948
|
var PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS = 1e3;
|
|
22947
22949
|
var OpensteerSessionRuntime = class {
|
|
@@ -29081,6 +29083,29 @@ var OpensteerSessionRuntime = class {
|
|
|
29081
29083
|
return snapshot.sessionStorage?.filter((entry) => entry.origin === origin).find((entry) => pageUrl === void 0 || entry.origin === new URL(pageUrl).origin)?.entries.find((entry) => entry.key === key)?.value;
|
|
29082
29084
|
}
|
|
29083
29085
|
async flushPersistedNetworkHistory() {
|
|
29086
|
+
if (this.sessionRef === void 0) {
|
|
29087
|
+
return;
|
|
29088
|
+
}
|
|
29089
|
+
const root = await this.ensureRoot();
|
|
29090
|
+
try {
|
|
29091
|
+
await withDetachedTimeoutSignal(PERSISTED_NETWORK_FLUSH_TIMEOUT_MS, async (signal) => {
|
|
29092
|
+
const browserRecords = await this.readBrowserNetworkRecords(
|
|
29093
|
+
{
|
|
29094
|
+
includeBodies: true,
|
|
29095
|
+
includeCurrentPageOnly: false
|
|
29096
|
+
},
|
|
29097
|
+
signal
|
|
29098
|
+
);
|
|
29099
|
+
await this.networkHistory.persist(browserRecords, root.registry.savedNetwork, {
|
|
29100
|
+
bodyWriteMode: "authoritative",
|
|
29101
|
+
redactSecretHeaders: false
|
|
29102
|
+
});
|
|
29103
|
+
});
|
|
29104
|
+
} catch (error) {
|
|
29105
|
+
if (!isIgnorableRuntimeBindingError(error)) {
|
|
29106
|
+
throw error;
|
|
29107
|
+
}
|
|
29108
|
+
}
|
|
29084
29109
|
}
|
|
29085
29110
|
toDomTargetRef(target) {
|
|
29086
29111
|
if (target.kind === "description") {
|
|
@@ -29237,6 +29262,12 @@ var OpensteerSessionRuntime = class {
|
|
|
29237
29262
|
return "live";
|
|
29238
29263
|
} catch (error) {
|
|
29239
29264
|
if (isIgnorableRuntimeBindingError(error)) {
|
|
29265
|
+
const remainingPages = await engine.listPages({ sessionRef }).catch(() => void 0);
|
|
29266
|
+
const replacementPageRef = remainingPages?.[0]?.pageRef;
|
|
29267
|
+
if (replacementPageRef !== void 0) {
|
|
29268
|
+
this.pageRef = replacementPageRef;
|
|
29269
|
+
return "live";
|
|
29270
|
+
}
|
|
29240
29271
|
return "invalid";
|
|
29241
29272
|
}
|
|
29242
29273
|
throw error;
|
|
@@ -32291,197 +32322,1798 @@ function screenshotMediaType(format2) {
|
|
|
32291
32322
|
}
|
|
32292
32323
|
}
|
|
32293
32324
|
|
|
32294
|
-
// src/
|
|
32295
|
-
var
|
|
32296
|
-
|
|
32297
|
-
|
|
32298
|
-
|
|
32299
|
-
|
|
32300
|
-
|
|
32301
|
-
|
|
32302
|
-
|
|
32303
|
-
|
|
32304
|
-
|
|
32305
|
-
|
|
32306
|
-
|
|
32307
|
-
|
|
32308
|
-
});
|
|
32309
|
-
super(
|
|
32310
|
-
buildSharedRuntimeOptions({
|
|
32311
|
-
name: publicWorkspace ?? "default",
|
|
32312
|
-
rootPath,
|
|
32313
|
-
...publicWorkspace === void 0 ? {} : { workspaceName: publicWorkspace },
|
|
32314
|
-
...options.browser === void 0 ? {} : { browser: options.browser },
|
|
32315
|
-
...options.launch === void 0 ? {} : { launch: options.launch },
|
|
32316
|
-
...options.context === void 0 ? {} : { context: options.context },
|
|
32317
|
-
engineName,
|
|
32318
|
-
...options.engine === void 0 ? {} : { engine: options.engine },
|
|
32319
|
-
...options.engineFactory === void 0 ? {} : { engineFactory: options.engineFactory },
|
|
32320
|
-
...options.policy === void 0 ? {} : { policy: options.policy },
|
|
32321
|
-
...options.descriptorStore === void 0 ? {} : { descriptorStore: options.descriptorStore },
|
|
32322
|
-
...options.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: options.extractionDescriptorStore },
|
|
32323
|
-
...options.registryOverrides === void 0 ? {} : { registryOverrides: options.registryOverrides },
|
|
32324
|
-
cleanupRootOnClose,
|
|
32325
|
-
...options.observability === void 0 ? {} : { observability: options.observability },
|
|
32326
|
-
...options.observationSessionId === void 0 ? {} : { observationSessionId: options.observationSessionId },
|
|
32327
|
-
...options.observationSink === void 0 ? {} : { observationSink: options.observationSink }
|
|
32328
|
-
})
|
|
32329
|
-
);
|
|
32325
|
+
// ../runtime-core/src/recorder/browser-scripts.ts
|
|
32326
|
+
var SINGLE_ATTRIBUTE_PRIORITY = Array.from(
|
|
32327
|
+
/* @__PURE__ */ new Set(["data-testid", "data-test", "data-qa", "data-cy", "id", ...STABLE_PRIMARY_ATTR_KEYS])
|
|
32328
|
+
);
|
|
32329
|
+
var FLOW_RECORDER_INSTALL_SOURCE = String.raw`(() => {
|
|
32330
|
+
const TOP_LEVEL_ONLY = (() => {
|
|
32331
|
+
try {
|
|
32332
|
+
return window.top === window.self;
|
|
32333
|
+
} catch {
|
|
32334
|
+
return false;
|
|
32335
|
+
}
|
|
32336
|
+
})();
|
|
32337
|
+
if (!TOP_LEVEL_ONLY) {
|
|
32338
|
+
return;
|
|
32330
32339
|
}
|
|
32331
|
-
|
|
32332
|
-
|
|
32333
|
-
|
|
32334
|
-
|
|
32335
|
-
|
|
32336
|
-
|
|
32337
|
-
|
|
32338
|
-
|
|
32339
|
-
|
|
32340
|
-
|
|
32341
|
-
|
|
32342
|
-
|
|
32343
|
-
|
|
32344
|
-
|
|
32345
|
-
|
|
32346
|
-
|
|
32347
|
-
|
|
32348
|
-
|
|
32349
|
-
engineName,
|
|
32350
|
-
...options.engine === void 0 ? {} : { engine: options.engine },
|
|
32351
|
-
...options.engineFactory === void 0 ? {} : { engineFactory: options.engineFactory },
|
|
32352
|
-
...options.policy === void 0 ? {} : { policy: options.policy },
|
|
32353
|
-
...options.descriptorStore === void 0 ? {} : { descriptorStore: options.descriptorStore },
|
|
32354
|
-
...options.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: options.extractionDescriptorStore },
|
|
32355
|
-
...options.registryOverrides === void 0 ? {} : { registryOverrides: options.registryOverrides },
|
|
32356
|
-
cleanupRootOnClose,
|
|
32357
|
-
...options.observability === void 0 ? {} : { observability: options.observability },
|
|
32358
|
-
...options.observationSessionId === void 0 ? {} : { observationSessionId: options.observationSessionId },
|
|
32359
|
-
...options.observationSink === void 0 ? {} : { observationSink: options.observationSink }
|
|
32360
|
-
})
|
|
32361
|
-
);
|
|
32340
|
+
|
|
32341
|
+
const globalScope = globalThis;
|
|
32342
|
+
const recorderKey = "__opensteerFlowRecorder";
|
|
32343
|
+
const historyStateKey = "__opensteerFlowRecorderHistory";
|
|
32344
|
+
const recorderUiAttribute = "data-opensteer-recorder-ui";
|
|
32345
|
+
const queueLimit = 1000;
|
|
32346
|
+
const singleAttributePriority = ${JSON.stringify(SINGLE_ATTRIBUTE_PRIORITY)};
|
|
32347
|
+
const stablePrimaryAttrKeys = new Set(${JSON.stringify([...STABLE_PRIMARY_ATTR_KEYS])});
|
|
32348
|
+
const matchAttributePriority = ${JSON.stringify([...MATCH_ATTRIBUTE_PRIORITY])};
|
|
32349
|
+
const attributeDenyKeys = new Set(${JSON.stringify([...ATTRIBUTE_DENY_KEYS])});
|
|
32350
|
+
const lazyLoadingMediaTags = new Set(${JSON.stringify([...LAZY_LOADING_MEDIA_TAGS])});
|
|
32351
|
+
const volatileLazyLoadingAttrs = new Set(${JSON.stringify([...VOLATILE_LAZY_LOADING_ATTRS])});
|
|
32352
|
+
const volatileClassTokens = new Set(${JSON.stringify([...VOLATILE_CLASS_TOKENS])});
|
|
32353
|
+
const volatileLazyClassTokens = new Set(${JSON.stringify([...VOLATILE_LAZY_CLASS_TOKENS])});
|
|
32354
|
+
|
|
32355
|
+
const previous = globalScope[recorderKey];
|
|
32356
|
+
if (previous && typeof previous.dispose === "function") {
|
|
32357
|
+
previous.dispose();
|
|
32362
32358
|
}
|
|
32363
|
-
|
|
32364
|
-
|
|
32365
|
-
const
|
|
32366
|
-
const
|
|
32367
|
-
|
|
32368
|
-
|
|
32369
|
-
|
|
32370
|
-
|
|
32371
|
-
|
|
32372
|
-
|
|
32373
|
-
|
|
32374
|
-
|
|
32375
|
-
|
|
32376
|
-
|
|
32377
|
-
|
|
32378
|
-
|
|
32379
|
-
|
|
32380
|
-
|
|
32381
|
-
|
|
32382
|
-
|
|
32383
|
-
|
|
32384
|
-
|
|
32385
|
-
|
|
32386
|
-
|
|
32387
|
-
|
|
32388
|
-
|
|
32389
|
-
|
|
32390
|
-
|
|
32391
|
-
|
|
32392
|
-
|
|
32393
|
-
},
|
|
32394
|
-
...input.workspaceName === void 0 ? {} : { workspace: input.workspaceName },
|
|
32395
|
-
reconnectable: !input.cleanupRootOnClose
|
|
32359
|
+
|
|
32360
|
+
const queue = [];
|
|
32361
|
+
const cleanup = [];
|
|
32362
|
+
const inputFlushTimers = new Map();
|
|
32363
|
+
const pendingInputs = new Map();
|
|
32364
|
+
let stopRequested = false;
|
|
32365
|
+
let pendingWheel = undefined;
|
|
32366
|
+
|
|
32367
|
+
const actionTargetTags = new Set([
|
|
32368
|
+
"a",
|
|
32369
|
+
"button",
|
|
32370
|
+
"input",
|
|
32371
|
+
"label",
|
|
32372
|
+
"option",
|
|
32373
|
+
"select",
|
|
32374
|
+
"summary",
|
|
32375
|
+
"textarea",
|
|
32376
|
+
]);
|
|
32377
|
+
|
|
32378
|
+
function now() {
|
|
32379
|
+
return Date.now();
|
|
32380
|
+
}
|
|
32381
|
+
|
|
32382
|
+
function enqueue(entry) {
|
|
32383
|
+
queue.push({
|
|
32384
|
+
...entry,
|
|
32385
|
+
timestamp: typeof entry.timestamp === "number" ? entry.timestamp : now(),
|
|
32386
|
+
});
|
|
32387
|
+
if (queue.length > queueLimit) {
|
|
32388
|
+
queue.splice(0, queue.length - queueLimit);
|
|
32396
32389
|
}
|
|
32397
|
-
};
|
|
32398
|
-
}
|
|
32399
|
-
function normalizeWorkspace2(workspace) {
|
|
32400
|
-
if (workspace === void 0) {
|
|
32401
|
-
return void 0;
|
|
32402
32390
|
}
|
|
32403
|
-
const trimmed = workspace.trim();
|
|
32404
|
-
return trimmed.length === 0 ? void 0 : trimmed;
|
|
32405
|
-
}
|
|
32406
|
-
function resolveOwnership(browser) {
|
|
32407
|
-
return typeof browser === "object" && browser.mode === "attach" ? "attached" : "owned";
|
|
32408
|
-
}
|
|
32409
32391
|
|
|
32410
|
-
|
|
32411
|
-
|
|
32412
|
-
|
|
32413
|
-
|
|
32414
|
-
|
|
32392
|
+
function isRecorderUiNode(node) {
|
|
32393
|
+
let current = node instanceof Node ? node : null;
|
|
32394
|
+
while (current) {
|
|
32395
|
+
if (current instanceof Element && current.hasAttribute(recorderUiAttribute)) {
|
|
32396
|
+
return true;
|
|
32397
|
+
}
|
|
32398
|
+
const root = typeof current.getRootNode === "function" ? current.getRootNode() : null;
|
|
32399
|
+
if (root instanceof ShadowRoot) {
|
|
32400
|
+
current = root.host;
|
|
32401
|
+
continue;
|
|
32402
|
+
}
|
|
32403
|
+
current = current instanceof Element ? current.parentElement : null;
|
|
32404
|
+
}
|
|
32405
|
+
return false;
|
|
32415
32406
|
}
|
|
32416
|
-
|
|
32417
|
-
|
|
32418
|
-
|
|
32419
|
-
|
|
32407
|
+
|
|
32408
|
+
function isValidAttributeName(name) {
|
|
32409
|
+
if (typeof name !== "string") {
|
|
32410
|
+
return false;
|
|
32411
|
+
}
|
|
32412
|
+
const normalized = name.trim();
|
|
32413
|
+
if (normalized.length === 0) {
|
|
32414
|
+
return false;
|
|
32415
|
+
}
|
|
32416
|
+
if (/[\s"'<>/]/.test(normalized)) {
|
|
32417
|
+
return false;
|
|
32418
|
+
}
|
|
32419
|
+
return /^[A-Za-z_][A-Za-z0-9_:\-.]*$/.test(normalized);
|
|
32420
32420
|
}
|
|
32421
|
-
|
|
32422
|
-
function
|
|
32423
|
-
|
|
32424
|
-
if (normalized === OPENSTEER_PROVIDER_MODES[0] || normalized === OPENSTEER_PROVIDER_MODES[1]) {
|
|
32425
|
-
return normalized;
|
|
32421
|
+
|
|
32422
|
+
function escapeAttributeValue(value) {
|
|
32423
|
+
return String(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
32426
32424
|
}
|
|
32427
|
-
|
|
32428
|
-
|
|
32429
|
-
|
|
32430
|
-
|
|
32431
|
-
|
|
32432
|
-
|
|
32433
|
-
|
|
32434
|
-
|
|
32435
|
-
|
|
32436
|
-
};
|
|
32425
|
+
|
|
32426
|
+
function escapeIdentifier(value) {
|
|
32427
|
+
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
|
|
32428
|
+
return CSS.escape(value);
|
|
32429
|
+
}
|
|
32430
|
+
return String(value).replace(/[^A-Za-z0-9_-]/g, (character) => {
|
|
32431
|
+
const codePoint = character.codePointAt(0);
|
|
32432
|
+
return "\\" + (codePoint == null ? "" : codePoint.toString(16)) + " ";
|
|
32433
|
+
});
|
|
32437
32434
|
}
|
|
32438
|
-
|
|
32439
|
-
|
|
32440
|
-
|
|
32441
|
-
|
|
32442
|
-
|
|
32435
|
+
|
|
32436
|
+
function normalizeClassValue(element, rawValue) {
|
|
32437
|
+
const tag = element.tagName.toLowerCase();
|
|
32438
|
+
const tokens = String(rawValue)
|
|
32439
|
+
.split(/\s+/u)
|
|
32440
|
+
.map((token) => token.trim())
|
|
32441
|
+
.filter(Boolean)
|
|
32442
|
+
.filter((token) => !volatileClassTokens.has(token))
|
|
32443
|
+
.filter((token) => !(lazyLoadingMediaTags.has(tag) && volatileLazyClassTokens.has(token)));
|
|
32444
|
+
return tokens.join(" ");
|
|
32443
32445
|
}
|
|
32444
|
-
|
|
32445
|
-
|
|
32446
|
-
|
|
32447
|
-
|
|
32448
|
-
|
|
32449
|
-
|
|
32450
|
-
|
|
32451
|
-
|
|
32452
|
-
|
|
32453
|
-
|
|
32454
|
-
|
|
32455
|
-
|
|
32456
|
-
|
|
32457
|
-
|
|
32458
|
-
|
|
32459
|
-
|
|
32460
|
-
|
|
32461
|
-
)
|
|
32446
|
+
|
|
32447
|
+
function shouldKeepAttribute(element, name, value) {
|
|
32448
|
+
const key = String(name || "")
|
|
32449
|
+
.trim()
|
|
32450
|
+
.toLowerCase();
|
|
32451
|
+
if (!key || !String(value || "").trim()) {
|
|
32452
|
+
return false;
|
|
32453
|
+
}
|
|
32454
|
+
if (!isValidAttributeName(key)) {
|
|
32455
|
+
return false;
|
|
32456
|
+
}
|
|
32457
|
+
if (key === "c") {
|
|
32458
|
+
return false;
|
|
32459
|
+
}
|
|
32460
|
+
if (/^on[a-z]/i.test(key)) {
|
|
32461
|
+
return false;
|
|
32462
|
+
}
|
|
32463
|
+
if (attributeDenyKeys.has(key)) {
|
|
32464
|
+
return false;
|
|
32465
|
+
}
|
|
32466
|
+
if (key.startsWith("data-os-") || key.startsWith("data-opensteer-")) {
|
|
32467
|
+
return false;
|
|
32468
|
+
}
|
|
32469
|
+
if (lazyLoadingMediaTags.has(element.tagName.toLowerCase()) && volatileLazyLoadingAttrs.has(key)) {
|
|
32470
|
+
return false;
|
|
32471
|
+
}
|
|
32472
|
+
return true;
|
|
32462
32473
|
}
|
|
32463
|
-
|
|
32464
|
-
|
|
32465
|
-
|
|
32466
|
-
|
|
32467
|
-
|
|
32468
|
-
|
|
32469
|
-
|
|
32470
|
-
|
|
32471
|
-
|
|
32472
|
-
|
|
32473
|
-
|
|
32474
|
-
profileDirectory
|
|
32475
|
-
};
|
|
32476
|
-
} finally {
|
|
32477
|
-
await rm(tempDir, { recursive: true, force: true }).catch(() => void 0);
|
|
32474
|
+
|
|
32475
|
+
function readAttributeValue(element, key) {
|
|
32476
|
+
if (key === "class") {
|
|
32477
|
+
const normalized = normalizeClassValue(element, element.getAttribute("class") || "");
|
|
32478
|
+
return normalized.length === 0 ? undefined : normalized;
|
|
32479
|
+
}
|
|
32480
|
+
const value = element.getAttribute(key);
|
|
32481
|
+
if (!shouldKeepAttribute(element, key, value || "")) {
|
|
32482
|
+
return undefined;
|
|
32483
|
+
}
|
|
32484
|
+
return value || undefined;
|
|
32478
32485
|
}
|
|
32479
|
-
|
|
32480
|
-
function
|
|
32481
|
-
|
|
32482
|
-
|
|
32486
|
+
|
|
32487
|
+
function buildSingleAttributeSelector(element, key, value) {
|
|
32488
|
+
if (!value) {
|
|
32489
|
+
return undefined;
|
|
32490
|
+
}
|
|
32491
|
+
const tag = element.tagName.toLowerCase();
|
|
32492
|
+
if (key === "id") {
|
|
32493
|
+
const idSelector = "#" + escapeIdentifier(value);
|
|
32494
|
+
return element.matches(idSelector)
|
|
32495
|
+
? idSelector
|
|
32496
|
+
: tag + '[id="' + escapeAttributeValue(value) + '"]';
|
|
32497
|
+
}
|
|
32498
|
+
return tag + "[" + key + '="' + escapeAttributeValue(value) + '"]';
|
|
32483
32499
|
}
|
|
32484
|
-
|
|
32500
|
+
|
|
32501
|
+
function isUniqueSelector(selector, element) {
|
|
32502
|
+
if (!selector) {
|
|
32503
|
+
return false;
|
|
32504
|
+
}
|
|
32505
|
+
let matches;
|
|
32506
|
+
try {
|
|
32507
|
+
matches = document.querySelectorAll(selector);
|
|
32508
|
+
} catch {
|
|
32509
|
+
return false;
|
|
32510
|
+
}
|
|
32511
|
+
return matches.length === 1 && matches[0] === element;
|
|
32512
|
+
}
|
|
32513
|
+
|
|
32514
|
+
function nearestRecordTarget(node) {
|
|
32515
|
+
if (isRecorderUiNode(node)) {
|
|
32516
|
+
return null;
|
|
32517
|
+
}
|
|
32518
|
+
let current = node instanceof Element ? node : null;
|
|
32519
|
+
while (current) {
|
|
32520
|
+
if (current.hasAttribute(recorderUiAttribute)) {
|
|
32521
|
+
return null;
|
|
32522
|
+
}
|
|
32523
|
+
const tag = current.tagName.toLowerCase();
|
|
32524
|
+
if (
|
|
32525
|
+
actionTargetTags.has(tag) ||
|
|
32526
|
+
current.hasAttribute("data-testid") ||
|
|
32527
|
+
current.hasAttribute("data-test") ||
|
|
32528
|
+
current.hasAttribute("data-qa") ||
|
|
32529
|
+
current.hasAttribute("data-cy") ||
|
|
32530
|
+
current.hasAttribute("role") ||
|
|
32531
|
+
current.hasAttribute("aria-label") ||
|
|
32532
|
+
current.hasAttribute("name")
|
|
32533
|
+
) {
|
|
32534
|
+
return current;
|
|
32535
|
+
}
|
|
32536
|
+
current = current.parentElement;
|
|
32537
|
+
}
|
|
32538
|
+
return node instanceof Element ? node : null;
|
|
32539
|
+
}
|
|
32540
|
+
|
|
32541
|
+
function buildSegmentSelector(element) {
|
|
32542
|
+
const tag = element.tagName.toLowerCase();
|
|
32543
|
+
for (const key of matchAttributePriority) {
|
|
32544
|
+
const value = readAttributeValue(element, key);
|
|
32545
|
+
if (!value) {
|
|
32546
|
+
continue;
|
|
32547
|
+
}
|
|
32548
|
+
if (key === "class") {
|
|
32549
|
+
const tokens = value
|
|
32550
|
+
.split(/\s+/u)
|
|
32551
|
+
.map((token) => token.trim())
|
|
32552
|
+
.filter(Boolean)
|
|
32553
|
+
.slice(0, 2);
|
|
32554
|
+
if (tokens.length === 0) {
|
|
32555
|
+
continue;
|
|
32556
|
+
}
|
|
32557
|
+
return tag + tokens.map((token) => "." + escapeIdentifier(token)).join("");
|
|
32558
|
+
}
|
|
32559
|
+
return key === "id"
|
|
32560
|
+
? tag + '[id="' + escapeAttributeValue(value) + '"]'
|
|
32561
|
+
: tag + "[" + key + '="' + escapeAttributeValue(value) + '"]';
|
|
32562
|
+
}
|
|
32563
|
+
return tag;
|
|
32564
|
+
}
|
|
32565
|
+
|
|
32566
|
+
function nthOfTypeSegment(element, baseSelector) {
|
|
32567
|
+
const parent = element.parentElement;
|
|
32568
|
+
if (!parent) {
|
|
32569
|
+
return baseSelector;
|
|
32570
|
+
}
|
|
32571
|
+
const sameType = Array.from(parent.children).filter(
|
|
32572
|
+
(child) => child.tagName === element.tagName,
|
|
32573
|
+
);
|
|
32574
|
+
if (sameType.length <= 1) {
|
|
32575
|
+
return baseSelector;
|
|
32576
|
+
}
|
|
32577
|
+
const index = sameType.indexOf(element) + 1;
|
|
32578
|
+
return baseSelector + ":nth-of-type(" + String(index) + ")";
|
|
32579
|
+
}
|
|
32580
|
+
|
|
32581
|
+
function buildSelector(node) {
|
|
32582
|
+
const element = nearestRecordTarget(node);
|
|
32583
|
+
if (!(element instanceof Element)) {
|
|
32584
|
+
return undefined;
|
|
32585
|
+
}
|
|
32586
|
+
|
|
32587
|
+
for (const key of singleAttributePriority) {
|
|
32588
|
+
const value = readAttributeValue(element, key);
|
|
32589
|
+
const selector = buildSingleAttributeSelector(element, key, value);
|
|
32590
|
+
if (selector && isUniqueSelector(selector, element)) {
|
|
32591
|
+
return selector;
|
|
32592
|
+
}
|
|
32593
|
+
if (value && !stablePrimaryAttrKeys.has(key) && key !== "id") {
|
|
32594
|
+
const tagQualified =
|
|
32595
|
+
element.tagName.toLowerCase() + "[" + key + '="' + escapeAttributeValue(value) + '"]';
|
|
32596
|
+
if (isUniqueSelector(tagQualified, element)) {
|
|
32597
|
+
return tagQualified;
|
|
32598
|
+
}
|
|
32599
|
+
}
|
|
32600
|
+
}
|
|
32601
|
+
|
|
32602
|
+
const segments = [];
|
|
32603
|
+
let current = element;
|
|
32604
|
+
let depth = 0;
|
|
32605
|
+
while (current && depth < 6) {
|
|
32606
|
+
const segment = nthOfTypeSegment(current, buildSegmentSelector(current));
|
|
32607
|
+
segments.unshift(segment);
|
|
32608
|
+
const selector = segments.join(" > ");
|
|
32609
|
+
if (isUniqueSelector(selector, element)) {
|
|
32610
|
+
return selector;
|
|
32611
|
+
}
|
|
32612
|
+
current = current.parentElement;
|
|
32613
|
+
depth += 1;
|
|
32614
|
+
}
|
|
32615
|
+
|
|
32616
|
+
const fallback = segments.join(" > ");
|
|
32617
|
+
return fallback.length > 0 ? fallback : element.tagName.toLowerCase();
|
|
32618
|
+
}
|
|
32619
|
+
|
|
32620
|
+
function readTargetValue(target) {
|
|
32621
|
+
if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) {
|
|
32622
|
+
return target.value;
|
|
32623
|
+
}
|
|
32624
|
+
if (target instanceof HTMLSelectElement) {
|
|
32625
|
+
return target.value;
|
|
32626
|
+
}
|
|
32627
|
+
if (target instanceof HTMLElement && target.isContentEditable) {
|
|
32628
|
+
return target.textContent || "";
|
|
32629
|
+
}
|
|
32630
|
+
return undefined;
|
|
32631
|
+
}
|
|
32632
|
+
|
|
32633
|
+
function flushPendingInput(selector) {
|
|
32634
|
+
const pending = pendingInputs.get(selector);
|
|
32635
|
+
if (!pending) {
|
|
32636
|
+
return;
|
|
32637
|
+
}
|
|
32638
|
+
pendingInputs.delete(selector);
|
|
32639
|
+
const timer = inputFlushTimers.get(selector);
|
|
32640
|
+
if (timer !== undefined) {
|
|
32641
|
+
clearTimeout(timer);
|
|
32642
|
+
inputFlushTimers.delete(selector);
|
|
32643
|
+
}
|
|
32644
|
+
enqueue({
|
|
32645
|
+
kind: "type",
|
|
32646
|
+
selector,
|
|
32647
|
+
text: pending.text,
|
|
32648
|
+
timestamp: pending.timestamp,
|
|
32649
|
+
});
|
|
32650
|
+
}
|
|
32651
|
+
|
|
32652
|
+
function flushAllInputs() {
|
|
32653
|
+
for (const selector of Array.from(pendingInputs.keys())) {
|
|
32654
|
+
flushPendingInput(selector);
|
|
32655
|
+
}
|
|
32656
|
+
}
|
|
32657
|
+
|
|
32658
|
+
function flushPendingWheel() {
|
|
32659
|
+
if (!pendingWheel) {
|
|
32660
|
+
return;
|
|
32661
|
+
}
|
|
32662
|
+
clearTimeout(pendingWheel.timerId);
|
|
32663
|
+
enqueue({
|
|
32664
|
+
kind: "scroll",
|
|
32665
|
+
selector: pendingWheel.selector,
|
|
32666
|
+
deltaX: pendingWheel.deltaX,
|
|
32667
|
+
deltaY: pendingWheel.deltaY,
|
|
32668
|
+
timestamp: pendingWheel.timestamp,
|
|
32669
|
+
});
|
|
32670
|
+
pendingWheel = undefined;
|
|
32671
|
+
}
|
|
32672
|
+
|
|
32673
|
+
function scheduleInputFlush(selector) {
|
|
32674
|
+
const existing = inputFlushTimers.get(selector);
|
|
32675
|
+
if (existing !== undefined) {
|
|
32676
|
+
clearTimeout(existing);
|
|
32677
|
+
}
|
|
32678
|
+
const timerId = setTimeout(() => {
|
|
32679
|
+
flushPendingInput(selector);
|
|
32680
|
+
}, 400);
|
|
32681
|
+
inputFlushTimers.set(selector, timerId);
|
|
32682
|
+
}
|
|
32683
|
+
|
|
32684
|
+
function updateHistoryState(mode, nextUrl) {
|
|
32685
|
+
let state;
|
|
32686
|
+
try {
|
|
32687
|
+
const raw = sessionStorage.getItem(historyStateKey);
|
|
32688
|
+
state = raw ? JSON.parse(raw) : undefined;
|
|
32689
|
+
} catch {
|
|
32690
|
+
state = undefined;
|
|
32691
|
+
}
|
|
32692
|
+
if (!state || !Array.isArray(state.entries) || typeof state.index !== "number") {
|
|
32693
|
+
state = { entries: [location.href], index: 0 };
|
|
32694
|
+
}
|
|
32695
|
+
if (mode === "replace") {
|
|
32696
|
+
state.entries[state.index] = nextUrl;
|
|
32697
|
+
} else if (mode === "push") {
|
|
32698
|
+
state.entries = state.entries.slice(0, state.index + 1);
|
|
32699
|
+
state.entries.push(nextUrl);
|
|
32700
|
+
state.index = state.entries.length - 1;
|
|
32701
|
+
} else if (mode === "back") {
|
|
32702
|
+
state.index = Math.max(0, state.index - 1);
|
|
32703
|
+
} else if (mode === "forward") {
|
|
32704
|
+
state.index = Math.min(state.entries.length - 1, state.index + 1);
|
|
32705
|
+
}
|
|
32706
|
+
try {
|
|
32707
|
+
sessionStorage.setItem(historyStateKey, JSON.stringify(state));
|
|
32708
|
+
} catch {}
|
|
32709
|
+
return state;
|
|
32710
|
+
}
|
|
32711
|
+
|
|
32712
|
+
function readHistoryState() {
|
|
32713
|
+
try {
|
|
32714
|
+
const raw = sessionStorage.getItem(historyStateKey);
|
|
32715
|
+
const parsed = raw ? JSON.parse(raw) : undefined;
|
|
32716
|
+
if (
|
|
32717
|
+
parsed &&
|
|
32718
|
+
Array.isArray(parsed.entries) &&
|
|
32719
|
+
parsed.entries.every((entry) => typeof entry === "string") &&
|
|
32720
|
+
typeof parsed.index === "number"
|
|
32721
|
+
) {
|
|
32722
|
+
return parsed;
|
|
32723
|
+
}
|
|
32724
|
+
} catch {}
|
|
32725
|
+
return undefined;
|
|
32726
|
+
}
|
|
32727
|
+
|
|
32728
|
+
function classifyHistoryTraversal(currentUrl) {
|
|
32729
|
+
const state = readHistoryState();
|
|
32730
|
+
if (!state) {
|
|
32731
|
+
return undefined;
|
|
32732
|
+
}
|
|
32733
|
+
if (state.entries[state.index - 1] === currentUrl) {
|
|
32734
|
+
updateHistoryState("back", currentUrl);
|
|
32735
|
+
return "go-back";
|
|
32736
|
+
}
|
|
32737
|
+
if (state.entries[state.index + 1] === currentUrl) {
|
|
32738
|
+
updateHistoryState("forward", currentUrl);
|
|
32739
|
+
return "go-forward";
|
|
32740
|
+
}
|
|
32741
|
+
const existingIndex = state.entries.lastIndexOf(currentUrl);
|
|
32742
|
+
if (existingIndex !== -1) {
|
|
32743
|
+
if (existingIndex < state.index) {
|
|
32744
|
+
while (readHistoryState()?.index > existingIndex) {
|
|
32745
|
+
updateHistoryState("back", currentUrl);
|
|
32746
|
+
}
|
|
32747
|
+
return "go-back";
|
|
32748
|
+
}
|
|
32749
|
+
if (existingIndex > state.index) {
|
|
32750
|
+
while (readHistoryState()?.index < existingIndex) {
|
|
32751
|
+
updateHistoryState("forward", currentUrl);
|
|
32752
|
+
}
|
|
32753
|
+
return "go-forward";
|
|
32754
|
+
}
|
|
32755
|
+
}
|
|
32756
|
+
return undefined;
|
|
32757
|
+
}
|
|
32758
|
+
|
|
32759
|
+
function onInstall() {
|
|
32760
|
+
const currentUrl = location.href;
|
|
32761
|
+
const navigationEntry =
|
|
32762
|
+
typeof performance.getEntriesByType === "function"
|
|
32763
|
+
? performance.getEntriesByType("navigation")[0]
|
|
32764
|
+
: undefined;
|
|
32765
|
+
const navigationType =
|
|
32766
|
+
navigationEntry && typeof navigationEntry.type === "string" ? navigationEntry.type : undefined;
|
|
32767
|
+
const existingState = readHistoryState();
|
|
32768
|
+
|
|
32769
|
+
if (!existingState) {
|
|
32770
|
+
updateHistoryState("replace", currentUrl);
|
|
32771
|
+
return;
|
|
32772
|
+
}
|
|
32773
|
+
|
|
32774
|
+
if (navigationType === "reload") {
|
|
32775
|
+
updateHistoryState("replace", currentUrl);
|
|
32776
|
+
enqueue({
|
|
32777
|
+
kind: "reload",
|
|
32778
|
+
url: currentUrl,
|
|
32779
|
+
});
|
|
32780
|
+
return;
|
|
32781
|
+
}
|
|
32782
|
+
|
|
32783
|
+
if (navigationType === "back_forward") {
|
|
32784
|
+
const traversal = classifyHistoryTraversal(currentUrl);
|
|
32785
|
+
if (traversal === "go-back" || traversal === "go-forward") {
|
|
32786
|
+
enqueue({
|
|
32787
|
+
kind: traversal,
|
|
32788
|
+
url: currentUrl,
|
|
32789
|
+
});
|
|
32790
|
+
return;
|
|
32791
|
+
}
|
|
32792
|
+
}
|
|
32793
|
+
|
|
32794
|
+
if (existingState.entries[existingState.index] !== currentUrl) {
|
|
32795
|
+
updateHistoryState("push", currentUrl);
|
|
32796
|
+
enqueue({
|
|
32797
|
+
kind: "navigate",
|
|
32798
|
+
url: currentUrl,
|
|
32799
|
+
source: "full-navigation",
|
|
32800
|
+
});
|
|
32801
|
+
}
|
|
32802
|
+
}
|
|
32803
|
+
|
|
32804
|
+
function modifierKeys(event) {
|
|
32805
|
+
const modifiers = [];
|
|
32806
|
+
if (event.altKey) {
|
|
32807
|
+
modifiers.push("Alt");
|
|
32808
|
+
}
|
|
32809
|
+
if (event.ctrlKey) {
|
|
32810
|
+
modifiers.push("Control");
|
|
32811
|
+
}
|
|
32812
|
+
if (event.metaKey) {
|
|
32813
|
+
modifiers.push("Meta");
|
|
32814
|
+
}
|
|
32815
|
+
if (event.shiftKey) {
|
|
32816
|
+
modifiers.push("Shift");
|
|
32817
|
+
}
|
|
32818
|
+
return modifiers;
|
|
32819
|
+
}
|
|
32820
|
+
|
|
32821
|
+
function addListener(target, type, listener, options) {
|
|
32822
|
+
target.addEventListener(type, listener, options);
|
|
32823
|
+
cleanup.push(() => {
|
|
32824
|
+
target.removeEventListener(type, listener, options);
|
|
32825
|
+
});
|
|
32826
|
+
}
|
|
32827
|
+
|
|
32828
|
+
function requestStop() {
|
|
32829
|
+
stopRequested = true;
|
|
32830
|
+
}
|
|
32831
|
+
|
|
32832
|
+
function mountStopButton() {
|
|
32833
|
+
if (document.querySelector("[" + recorderUiAttribute + "]")) {
|
|
32834
|
+
return true;
|
|
32835
|
+
}
|
|
32836
|
+
if (stopRequested) {
|
|
32837
|
+
return true;
|
|
32838
|
+
}
|
|
32839
|
+
const parent = document.body || document.documentElement;
|
|
32840
|
+
if (!(parent instanceof HTMLElement)) {
|
|
32841
|
+
return false;
|
|
32842
|
+
}
|
|
32843
|
+
|
|
32844
|
+
const host = document.createElement("div");
|
|
32845
|
+
host.setAttribute(recorderUiAttribute, "true");
|
|
32846
|
+
const shadowRoot = host.attachShadow({ mode: "open" });
|
|
32847
|
+
const style = document.createElement("style");
|
|
32848
|
+
style.textContent = [
|
|
32849
|
+
":host {",
|
|
32850
|
+
" all: initial;",
|
|
32851
|
+
" display: block;",
|
|
32852
|
+
" position: fixed;",
|
|
32853
|
+
" top: 16px;",
|
|
32854
|
+
" right: 16px;",
|
|
32855
|
+
" z-index: 2147483647;",
|
|
32856
|
+
" pointer-events: auto;",
|
|
32857
|
+
" color-scheme: only light;",
|
|
32858
|
+
" contain: content;",
|
|
32859
|
+
" isolation: isolate;",
|
|
32860
|
+
"}",
|
|
32861
|
+
"button {",
|
|
32862
|
+
" appearance: none;",
|
|
32863
|
+
" border: 1px solid rgba(15, 23, 42, 0.2);",
|
|
32864
|
+
" border-radius: 999px;",
|
|
32865
|
+
" background: rgba(15, 23, 42, 0.92);",
|
|
32866
|
+
" color: #ffffff;",
|
|
32867
|
+
" color-scheme: only light;",
|
|
32868
|
+
" cursor: pointer;",
|
|
32869
|
+
" font: 600 12px/1.2 ui-sans-serif, system-ui, sans-serif;",
|
|
32870
|
+
" letter-spacing: 0.01em;",
|
|
32871
|
+
" padding: 10px 14px;",
|
|
32872
|
+
" box-shadow: 0 10px 30px rgba(15, 23, 42, 0.22);",
|
|
32873
|
+
"}",
|
|
32874
|
+
"button:hover {",
|
|
32875
|
+
" background: rgba(15, 23, 42, 0.98);",
|
|
32876
|
+
"}",
|
|
32877
|
+
"button:focus-visible {",
|
|
32878
|
+
" outline: 2px solid #2563eb;",
|
|
32879
|
+
" outline-offset: 2px;",
|
|
32880
|
+
"}",
|
|
32881
|
+
"button:disabled {",
|
|
32882
|
+
" cursor: wait;",
|
|
32883
|
+
" opacity: 0.8;",
|
|
32884
|
+
"}",
|
|
32885
|
+
].join("\n");
|
|
32886
|
+
const button = document.createElement("button");
|
|
32887
|
+
button.type = "button";
|
|
32888
|
+
button.textContent = "Stop recording";
|
|
32889
|
+
button.setAttribute("aria-label", "Stop recording");
|
|
32890
|
+
const consumePointerEvent = (event) => {
|
|
32891
|
+
event.preventDefault();
|
|
32892
|
+
event.stopPropagation();
|
|
32893
|
+
};
|
|
32894
|
+
for (const type of ["pointerdown", "mousedown", "mouseup", "click"]) {
|
|
32895
|
+
button.addEventListener(type, consumePointerEvent);
|
|
32896
|
+
}
|
|
32897
|
+
button.addEventListener("click", () => {
|
|
32898
|
+
requestStop();
|
|
32899
|
+
host.remove();
|
|
32900
|
+
});
|
|
32901
|
+
shadowRoot.append(style, button);
|
|
32902
|
+
parent.appendChild(host);
|
|
32903
|
+
cleanup.push(() => {
|
|
32904
|
+
host.remove();
|
|
32905
|
+
});
|
|
32906
|
+
return true;
|
|
32907
|
+
}
|
|
32908
|
+
|
|
32909
|
+
function ensureStopButtonMounted() {
|
|
32910
|
+
if (mountStopButton()) {
|
|
32911
|
+
return;
|
|
32912
|
+
}
|
|
32913
|
+
|
|
32914
|
+
const observer = new MutationObserver(() => {
|
|
32915
|
+
if (!mountStopButton()) {
|
|
32916
|
+
return;
|
|
32917
|
+
}
|
|
32918
|
+
observer.disconnect();
|
|
32919
|
+
});
|
|
32920
|
+
observer.observe(document, {
|
|
32921
|
+
childList: true,
|
|
32922
|
+
subtree: true,
|
|
32923
|
+
});
|
|
32924
|
+
cleanup.push(() => {
|
|
32925
|
+
observer.disconnect();
|
|
32926
|
+
});
|
|
32927
|
+
}
|
|
32928
|
+
|
|
32929
|
+
addListener(document, "click", (event) => {
|
|
32930
|
+
if (!event.isTrusted) {
|
|
32931
|
+
return;
|
|
32932
|
+
}
|
|
32933
|
+
const target = nearestRecordTarget(event.target);
|
|
32934
|
+
if (!(target instanceof Element)) {
|
|
32935
|
+
return;
|
|
32936
|
+
}
|
|
32937
|
+
const tag = target.tagName.toLowerCase();
|
|
32938
|
+
if (tag === "select" || tag === "option") {
|
|
32939
|
+
return;
|
|
32940
|
+
}
|
|
32941
|
+
const selector = buildSelector(target);
|
|
32942
|
+
if (!selector) {
|
|
32943
|
+
return;
|
|
32944
|
+
}
|
|
32945
|
+
enqueue({
|
|
32946
|
+
kind: "click",
|
|
32947
|
+
selector,
|
|
32948
|
+
button: event.button,
|
|
32949
|
+
modifiers: modifierKeys(event),
|
|
32950
|
+
});
|
|
32951
|
+
}, true);
|
|
32952
|
+
|
|
32953
|
+
addListener(document, "dblclick", (event) => {
|
|
32954
|
+
if (!event.isTrusted) {
|
|
32955
|
+
return;
|
|
32956
|
+
}
|
|
32957
|
+
const target = nearestRecordTarget(event.target);
|
|
32958
|
+
if (!(target instanceof Element)) {
|
|
32959
|
+
return;
|
|
32960
|
+
}
|
|
32961
|
+
const selector = buildSelector(target);
|
|
32962
|
+
if (!selector) {
|
|
32963
|
+
return;
|
|
32964
|
+
}
|
|
32965
|
+
enqueue({
|
|
32966
|
+
kind: "dblclick",
|
|
32967
|
+
selector,
|
|
32968
|
+
});
|
|
32969
|
+
}, true);
|
|
32970
|
+
|
|
32971
|
+
addListener(document, "input", (event) => {
|
|
32972
|
+
if (!event.isTrusted) {
|
|
32973
|
+
return;
|
|
32974
|
+
}
|
|
32975
|
+
const target = nearestRecordTarget(event.target);
|
|
32976
|
+
if (!(target instanceof Element)) {
|
|
32977
|
+
return;
|
|
32978
|
+
}
|
|
32979
|
+
const selector = buildSelector(target);
|
|
32980
|
+
const text = readTargetValue(target);
|
|
32981
|
+
if (!selector || typeof text !== "string") {
|
|
32982
|
+
return;
|
|
32983
|
+
}
|
|
32984
|
+
pendingInputs.set(selector, {
|
|
32985
|
+
selector,
|
|
32986
|
+
text,
|
|
32987
|
+
timestamp: now(),
|
|
32988
|
+
});
|
|
32989
|
+
scheduleInputFlush(selector);
|
|
32990
|
+
}, true);
|
|
32991
|
+
|
|
32992
|
+
addListener(document, "change", (event) => {
|
|
32993
|
+
if (!event.isTrusted) {
|
|
32994
|
+
return;
|
|
32995
|
+
}
|
|
32996
|
+
const target = nearestRecordTarget(event.target);
|
|
32997
|
+
if (!(target instanceof HTMLSelectElement)) {
|
|
32998
|
+
return;
|
|
32999
|
+
}
|
|
33000
|
+
const selector = buildSelector(target);
|
|
33001
|
+
if (!selector) {
|
|
33002
|
+
return;
|
|
33003
|
+
}
|
|
33004
|
+
const selectedOption = target.selectedOptions && target.selectedOptions.length > 0
|
|
33005
|
+
? target.selectedOptions[0]
|
|
33006
|
+
: undefined;
|
|
33007
|
+
enqueue({
|
|
33008
|
+
kind: "select-option",
|
|
33009
|
+
selector,
|
|
33010
|
+
value: target.value,
|
|
33011
|
+
...(selectedOption === undefined ? {} : { label: selectedOption.label || selectedOption.textContent || "" }),
|
|
33012
|
+
});
|
|
33013
|
+
}, true);
|
|
33014
|
+
|
|
33015
|
+
addListener(document, "keydown", (event) => {
|
|
33016
|
+
if (!event.isTrusted) {
|
|
33017
|
+
return;
|
|
33018
|
+
}
|
|
33019
|
+
const allowedKeys = new Set([
|
|
33020
|
+
"ArrowDown",
|
|
33021
|
+
"ArrowLeft",
|
|
33022
|
+
"ArrowRight",
|
|
33023
|
+
"ArrowUp",
|
|
33024
|
+
"Backspace",
|
|
33025
|
+
"Delete",
|
|
33026
|
+
"Enter",
|
|
33027
|
+
"Escape",
|
|
33028
|
+
"Tab",
|
|
33029
|
+
]);
|
|
33030
|
+
if (!allowedKeys.has(event.key)) {
|
|
33031
|
+
return;
|
|
33032
|
+
}
|
|
33033
|
+
const target = nearestRecordTarget(event.target);
|
|
33034
|
+
const selector = target instanceof Element ? buildSelector(target) : undefined;
|
|
33035
|
+
if (event.key === "Enter" && selector) {
|
|
33036
|
+
flushPendingInput(selector);
|
|
33037
|
+
}
|
|
33038
|
+
enqueue({
|
|
33039
|
+
kind: "keypress",
|
|
33040
|
+
key: event.key,
|
|
33041
|
+
modifiers: modifierKeys(event),
|
|
33042
|
+
...(selector === undefined ? {} : { selector }),
|
|
33043
|
+
});
|
|
33044
|
+
}, true);
|
|
33045
|
+
|
|
33046
|
+
addListener(document, "wheel", (event) => {
|
|
33047
|
+
if (!event.isTrusted) {
|
|
33048
|
+
return;
|
|
33049
|
+
}
|
|
33050
|
+
const target = nearestRecordTarget(event.target);
|
|
33051
|
+
const selector = target instanceof Element ? buildSelector(target) : undefined;
|
|
33052
|
+
if (pendingWheel && pendingWheel.selector === selector) {
|
|
33053
|
+
pendingWheel.deltaX += event.deltaX;
|
|
33054
|
+
pendingWheel.deltaY += event.deltaY;
|
|
33055
|
+
clearTimeout(pendingWheel.timerId);
|
|
33056
|
+
pendingWheel.timerId = setTimeout(flushPendingWheel, 250);
|
|
33057
|
+
return;
|
|
33058
|
+
}
|
|
33059
|
+
flushPendingWheel();
|
|
33060
|
+
pendingWheel = {
|
|
33061
|
+
selector,
|
|
33062
|
+
deltaX: event.deltaX,
|
|
33063
|
+
deltaY: event.deltaY,
|
|
33064
|
+
timestamp: now(),
|
|
33065
|
+
timerId: setTimeout(flushPendingWheel, 250),
|
|
33066
|
+
};
|
|
33067
|
+
}, true);
|
|
33068
|
+
|
|
33069
|
+
const originalPushState = history.pushState.bind(history);
|
|
33070
|
+
history.pushState = function pushState(state, unused, url) {
|
|
33071
|
+
const beforeUrl = location.href;
|
|
33072
|
+
const output = originalPushState.apply(this, arguments);
|
|
33073
|
+
const nextUrl = location.href;
|
|
33074
|
+
if (nextUrl !== beforeUrl) {
|
|
33075
|
+
updateHistoryState("push", nextUrl);
|
|
33076
|
+
enqueue({
|
|
33077
|
+
kind: "navigate",
|
|
33078
|
+
url: nextUrl,
|
|
33079
|
+
source: "push-state",
|
|
33080
|
+
});
|
|
33081
|
+
}
|
|
33082
|
+
return output;
|
|
33083
|
+
};
|
|
33084
|
+
cleanup.push(() => {
|
|
33085
|
+
history.pushState = originalPushState;
|
|
33086
|
+
});
|
|
33087
|
+
|
|
33088
|
+
const originalReplaceState = history.replaceState.bind(history);
|
|
33089
|
+
history.replaceState = function replaceState(state, unused, url) {
|
|
33090
|
+
const beforeUrl = location.href;
|
|
33091
|
+
const output = originalReplaceState.apply(this, arguments);
|
|
33092
|
+
const nextUrl = location.href;
|
|
33093
|
+
if (nextUrl !== beforeUrl) {
|
|
33094
|
+
updateHistoryState("replace", nextUrl);
|
|
33095
|
+
enqueue({
|
|
33096
|
+
kind: "navigate",
|
|
33097
|
+
url: nextUrl,
|
|
33098
|
+
source: "replace-state",
|
|
33099
|
+
});
|
|
33100
|
+
}
|
|
33101
|
+
return output;
|
|
33102
|
+
};
|
|
33103
|
+
cleanup.push(() => {
|
|
33104
|
+
history.replaceState = originalReplaceState;
|
|
33105
|
+
});
|
|
33106
|
+
|
|
33107
|
+
addListener(globalScope, "popstate", () => {
|
|
33108
|
+
const currentUrl = location.href;
|
|
33109
|
+
const traversal = classifyHistoryTraversal(currentUrl);
|
|
33110
|
+
if (traversal === "go-back" || traversal === "go-forward") {
|
|
33111
|
+
enqueue({
|
|
33112
|
+
kind: traversal,
|
|
33113
|
+
url: currentUrl,
|
|
33114
|
+
});
|
|
33115
|
+
return;
|
|
33116
|
+
}
|
|
33117
|
+
if (readHistoryState()?.entries[readHistoryState().index] !== currentUrl) {
|
|
33118
|
+
updateHistoryState("push", currentUrl);
|
|
33119
|
+
enqueue({
|
|
33120
|
+
kind: "navigate",
|
|
33121
|
+
url: currentUrl,
|
|
33122
|
+
source: "history-traversal",
|
|
33123
|
+
});
|
|
33124
|
+
}
|
|
33125
|
+
});
|
|
33126
|
+
|
|
33127
|
+
addListener(globalScope, "hashchange", () => {
|
|
33128
|
+
const currentUrl = location.href;
|
|
33129
|
+
updateHistoryState("replace", currentUrl);
|
|
33130
|
+
enqueue({
|
|
33131
|
+
kind: "navigate",
|
|
33132
|
+
url: currentUrl,
|
|
33133
|
+
source: "hashchange",
|
|
33134
|
+
});
|
|
33135
|
+
});
|
|
33136
|
+
|
|
33137
|
+
function drain() {
|
|
33138
|
+
flushAllInputs();
|
|
33139
|
+
flushPendingWheel();
|
|
33140
|
+
return {
|
|
33141
|
+
url: location.href,
|
|
33142
|
+
focused: document.hasFocus(),
|
|
33143
|
+
visibilityState: document.visibilityState,
|
|
33144
|
+
stopRequested,
|
|
33145
|
+
events: queue.splice(0, queue.length),
|
|
33146
|
+
};
|
|
33147
|
+
}
|
|
33148
|
+
|
|
33149
|
+
globalScope[recorderKey] = {
|
|
33150
|
+
installed: true,
|
|
33151
|
+
debugSelector(target) {
|
|
33152
|
+
return buildSelector(target);
|
|
33153
|
+
},
|
|
33154
|
+
drain,
|
|
33155
|
+
requestStop,
|
|
33156
|
+
dispose() {
|
|
33157
|
+
flushAllInputs();
|
|
33158
|
+
flushPendingWheel();
|
|
33159
|
+
for (const dispose of cleanup.splice(0, cleanup.length)) {
|
|
33160
|
+
dispose();
|
|
33161
|
+
}
|
|
33162
|
+
for (const timerId of inputFlushTimers.values()) {
|
|
33163
|
+
clearTimeout(timerId);
|
|
33164
|
+
}
|
|
33165
|
+
inputFlushTimers.clear();
|
|
33166
|
+
pendingInputs.clear();
|
|
33167
|
+
delete globalScope[recorderKey];
|
|
33168
|
+
},
|
|
33169
|
+
};
|
|
33170
|
+
|
|
33171
|
+
ensureStopButtonMounted();
|
|
33172
|
+
onInstall();
|
|
33173
|
+
})();`;
|
|
33174
|
+
var FLOW_RECORDER_INSTALL_SCRIPT = FLOW_RECORDER_INSTALL_SOURCE;
|
|
33175
|
+
var FLOW_RECORDER_DRAIN_SCRIPT = String.raw`(() => {
|
|
33176
|
+
const recorder = globalThis.__opensteerFlowRecorder;
|
|
33177
|
+
if (!recorder || typeof recorder.drain !== "function") {
|
|
33178
|
+
return {
|
|
33179
|
+
url: location.href,
|
|
33180
|
+
focused: document.hasFocus(),
|
|
33181
|
+
visibilityState: document.visibilityState,
|
|
33182
|
+
stopRequested: false,
|
|
33183
|
+
events: [],
|
|
33184
|
+
};
|
|
33185
|
+
}
|
|
33186
|
+
return recorder.drain();
|
|
33187
|
+
})();`;
|
|
33188
|
+
|
|
33189
|
+
// ../runtime-core/src/recorder/event-collector.ts
|
|
33190
|
+
var FlowRecorderCollector = class {
|
|
33191
|
+
runtime;
|
|
33192
|
+
pollIntervalMs;
|
|
33193
|
+
onAction;
|
|
33194
|
+
pages = /* @__PURE__ */ new Map();
|
|
33195
|
+
actions = [];
|
|
33196
|
+
nextPageOrdinal = 0;
|
|
33197
|
+
runningLoop;
|
|
33198
|
+
loopStopRequested = false;
|
|
33199
|
+
stopDetected = false;
|
|
33200
|
+
focusedPageId;
|
|
33201
|
+
stopWaiters = [];
|
|
33202
|
+
constructor(runtime, options = {}) {
|
|
33203
|
+
this.runtime = runtime;
|
|
33204
|
+
this.pollIntervalMs = options.pollIntervalMs ?? 250;
|
|
33205
|
+
this.onAction = options.onAction;
|
|
33206
|
+
}
|
|
33207
|
+
async install() {
|
|
33208
|
+
await this.runtime.addInitScript({
|
|
33209
|
+
script: FLOW_RECORDER_INSTALL_SCRIPT
|
|
33210
|
+
});
|
|
33211
|
+
const { pages } = await this.runtime.listPages();
|
|
33212
|
+
for (const page of pages) {
|
|
33213
|
+
this.ensureKnownPage(page.pageRef, page.url, page.openerPageRef);
|
|
33214
|
+
}
|
|
33215
|
+
await Promise.all(
|
|
33216
|
+
pages.map(
|
|
33217
|
+
(page) => this.runtime.evaluate({
|
|
33218
|
+
script: FLOW_RECORDER_INSTALL_SCRIPT,
|
|
33219
|
+
pageRef: page.pageRef
|
|
33220
|
+
}).catch(() => void 0)
|
|
33221
|
+
)
|
|
33222
|
+
);
|
|
33223
|
+
const evaluatedPages = await this.readEvaluatedPages(pages);
|
|
33224
|
+
for (const page of evaluatedPages) {
|
|
33225
|
+
this.updateKnownPage(page.pageRef, page.currentUrl, page.openerPageRef);
|
|
33226
|
+
}
|
|
33227
|
+
this.focusedPageId = evaluatedPages.find((page) => page.focused)?.pageId ?? this.focusedPageId;
|
|
33228
|
+
}
|
|
33229
|
+
start() {
|
|
33230
|
+
if (this.runningLoop !== void 0) {
|
|
33231
|
+
return;
|
|
33232
|
+
}
|
|
33233
|
+
this.loopStopRequested = false;
|
|
33234
|
+
this.runningLoop = this.runLoop();
|
|
33235
|
+
}
|
|
33236
|
+
async stop() {
|
|
33237
|
+
this.loopStopRequested = true;
|
|
33238
|
+
if (this.runningLoop !== void 0) {
|
|
33239
|
+
await this.runningLoop;
|
|
33240
|
+
this.runningLoop = void 0;
|
|
33241
|
+
}
|
|
33242
|
+
if (!this.stopDetected) {
|
|
33243
|
+
await this.pollOnce().catch(() => void 0);
|
|
33244
|
+
}
|
|
33245
|
+
return this.actions.slice();
|
|
33246
|
+
}
|
|
33247
|
+
getActions() {
|
|
33248
|
+
return this.actions.slice();
|
|
33249
|
+
}
|
|
33250
|
+
async waitForStop() {
|
|
33251
|
+
if (this.stopDetected) {
|
|
33252
|
+
return;
|
|
33253
|
+
}
|
|
33254
|
+
await new Promise((resolve4) => {
|
|
33255
|
+
this.stopWaiters.push(resolve4);
|
|
33256
|
+
});
|
|
33257
|
+
}
|
|
33258
|
+
async pollOnce() {
|
|
33259
|
+
const pollTimestamp = Date.now();
|
|
33260
|
+
const { pages } = await this.runtime.listPages();
|
|
33261
|
+
const previousPageRefs = new Set(this.pages.keys());
|
|
33262
|
+
const evaluatedPages = await this.readEvaluatedPages(pages);
|
|
33263
|
+
if (!this.stopDetected && evaluatedPages.some((page) => page.stopRequested)) {
|
|
33264
|
+
this.stopDetected = true;
|
|
33265
|
+
this.loopStopRequested = true;
|
|
33266
|
+
for (const resolve4 of this.stopWaiters.splice(0, this.stopWaiters.length)) {
|
|
33267
|
+
resolve4();
|
|
33268
|
+
}
|
|
33269
|
+
}
|
|
33270
|
+
const actions = this.normalizePoll({
|
|
33271
|
+
pollTimestamp,
|
|
33272
|
+
previousPageRefs,
|
|
33273
|
+
listedPages: pages,
|
|
33274
|
+
evaluatedPages
|
|
33275
|
+
});
|
|
33276
|
+
if (actions.length === 0) {
|
|
33277
|
+
return [];
|
|
33278
|
+
}
|
|
33279
|
+
actions.sort((left, right) => {
|
|
33280
|
+
const timestampOrder = left.timestamp - right.timestamp;
|
|
33281
|
+
if (timestampOrder !== 0) {
|
|
33282
|
+
return timestampOrder;
|
|
33283
|
+
}
|
|
33284
|
+
return actionSortPriority(left.kind) - actionSortPriority(right.kind);
|
|
33285
|
+
});
|
|
33286
|
+
for (const action of actions) {
|
|
33287
|
+
this.actions.push(action);
|
|
33288
|
+
if (this.onAction !== void 0) {
|
|
33289
|
+
await this.onAction(action);
|
|
33290
|
+
}
|
|
33291
|
+
}
|
|
33292
|
+
return actions;
|
|
33293
|
+
}
|
|
33294
|
+
async runLoop() {
|
|
33295
|
+
while (!this.loopStopRequested) {
|
|
33296
|
+
try {
|
|
33297
|
+
await this.pollOnce();
|
|
33298
|
+
} catch {
|
|
33299
|
+
}
|
|
33300
|
+
if (this.loopStopRequested) {
|
|
33301
|
+
break;
|
|
33302
|
+
}
|
|
33303
|
+
await delay(this.pollIntervalMs);
|
|
33304
|
+
}
|
|
33305
|
+
}
|
|
33306
|
+
async readEvaluatedPages(listedPages) {
|
|
33307
|
+
const pages = await Promise.all(
|
|
33308
|
+
listedPages.map(async (page) => {
|
|
33309
|
+
const knownPage = this.ensureKnownPage(page.pageRef, page.url, page.openerPageRef);
|
|
33310
|
+
const snapshot = await this.readSnapshot(page.pageRef, page.url);
|
|
33311
|
+
this.updateKnownPage(page.pageRef, snapshot.url, page.openerPageRef);
|
|
33312
|
+
return {
|
|
33313
|
+
pageRef: page.pageRef,
|
|
33314
|
+
pageId: knownPage.pageId,
|
|
33315
|
+
previousUrl: knownPage.currentUrl,
|
|
33316
|
+
currentUrl: snapshot.url,
|
|
33317
|
+
focused: snapshot.focused || snapshot.visibilityState === "visible",
|
|
33318
|
+
stopRequested: snapshot.stopRequested,
|
|
33319
|
+
events: snapshot.events,
|
|
33320
|
+
...page.openerPageRef === void 0 ? {} : { openerPageRef: page.openerPageRef },
|
|
33321
|
+
...knownPage.openerPageId === void 0 ? {} : { openerPageId: knownPage.openerPageId }
|
|
33322
|
+
};
|
|
33323
|
+
})
|
|
33324
|
+
);
|
|
33325
|
+
return pages;
|
|
33326
|
+
}
|
|
33327
|
+
async readSnapshot(pageRef, fallbackUrl) {
|
|
33328
|
+
const value = await this.runtime.evaluate({
|
|
33329
|
+
script: FLOW_RECORDER_DRAIN_SCRIPT,
|
|
33330
|
+
pageRef
|
|
33331
|
+
});
|
|
33332
|
+
return normalizeSnapshot(value, fallbackUrl);
|
|
33333
|
+
}
|
|
33334
|
+
normalizePoll(input) {
|
|
33335
|
+
const listedPageRefs = new Set(input.listedPages.map((page) => page.pageRef));
|
|
33336
|
+
const actions = [];
|
|
33337
|
+
const firstEventTimestampByPage = /* @__PURE__ */ new Map();
|
|
33338
|
+
for (const evaluatedPage of input.evaluatedPages) {
|
|
33339
|
+
const firstTimestamp = evaluatedPage.events[0]?.timestamp;
|
|
33340
|
+
if (firstTimestamp !== void 0) {
|
|
33341
|
+
firstEventTimestampByPage.set(evaluatedPage.pageRef, firstTimestamp);
|
|
33342
|
+
}
|
|
33343
|
+
}
|
|
33344
|
+
for (const listedPage of input.listedPages) {
|
|
33345
|
+
if (!input.previousPageRefs.has(listedPage.pageRef)) {
|
|
33346
|
+
const created = this.ensureKnownPage(listedPage.pageRef, listedPage.url, listedPage.openerPageRef);
|
|
33347
|
+
actions.push({
|
|
33348
|
+
kind: "new-tab",
|
|
33349
|
+
timestamp: Math.max(0, (firstEventTimestampByPage.get(listedPage.pageRef) ?? input.pollTimestamp) - 1),
|
|
33350
|
+
pageId: created.pageId,
|
|
33351
|
+
pageUrl: listedPage.url,
|
|
33352
|
+
detail: {
|
|
33353
|
+
kind: "new-tab",
|
|
33354
|
+
...created.openerPageId === void 0 ? {} : { openerPageId: created.openerPageId },
|
|
33355
|
+
initialUrl: listedPage.url
|
|
33356
|
+
}
|
|
33357
|
+
});
|
|
33358
|
+
}
|
|
33359
|
+
}
|
|
33360
|
+
for (const [pageRef, knownPage] of [...this.pages.entries()]) {
|
|
33361
|
+
if (listedPageRefs.has(pageRef)) {
|
|
33362
|
+
continue;
|
|
33363
|
+
}
|
|
33364
|
+
actions.push({
|
|
33365
|
+
kind: "close-tab",
|
|
33366
|
+
timestamp: input.pollTimestamp,
|
|
33367
|
+
pageId: knownPage.pageId,
|
|
33368
|
+
pageUrl: knownPage.currentUrl,
|
|
33369
|
+
detail: {
|
|
33370
|
+
kind: "close-tab"
|
|
33371
|
+
}
|
|
33372
|
+
});
|
|
33373
|
+
this.pages.delete(pageRef);
|
|
33374
|
+
}
|
|
33375
|
+
for (const evaluatedPage of input.evaluatedPages) {
|
|
33376
|
+
const knownPage = this.pages.get(evaluatedPage.pageRef);
|
|
33377
|
+
if (!knownPage) {
|
|
33378
|
+
continue;
|
|
33379
|
+
}
|
|
33380
|
+
if (evaluatedPage.previousUrl !== evaluatedPage.currentUrl && !evaluatedPage.events.some(
|
|
33381
|
+
(event) => event.kind === "navigate" || event.kind === "reload" || event.kind === "go-back" || event.kind === "go-forward"
|
|
33382
|
+
)) {
|
|
33383
|
+
actions.push({
|
|
33384
|
+
kind: "navigate",
|
|
33385
|
+
timestamp: Math.max(
|
|
33386
|
+
0,
|
|
33387
|
+
(firstEventTimestampByPage.get(evaluatedPage.pageRef) ?? input.pollTimestamp) - 1
|
|
33388
|
+
),
|
|
33389
|
+
pageId: knownPage.pageId,
|
|
33390
|
+
pageUrl: evaluatedPage.currentUrl,
|
|
33391
|
+
detail: {
|
|
33392
|
+
kind: "navigate",
|
|
33393
|
+
url: evaluatedPage.currentUrl,
|
|
33394
|
+
source: "poll"
|
|
33395
|
+
}
|
|
33396
|
+
});
|
|
33397
|
+
}
|
|
33398
|
+
actions.push(
|
|
33399
|
+
...evaluatedPage.events.flatMap(
|
|
33400
|
+
(event) => this.normalizeRawEvent(event, knownPage, evaluatedPage.currentUrl)
|
|
33401
|
+
)
|
|
33402
|
+
);
|
|
33403
|
+
this.updateKnownPage(evaluatedPage.pageRef, evaluatedPage.currentUrl, evaluatedPage.openerPageRef);
|
|
33404
|
+
}
|
|
33405
|
+
const focusedPage = input.evaluatedPages.find((page) => page.focused);
|
|
33406
|
+
if (focusedPage !== void 0 && focusedPage.pageId !== this.focusedPageId) {
|
|
33407
|
+
actions.push({
|
|
33408
|
+
kind: "switch-tab",
|
|
33409
|
+
timestamp: Math.max(
|
|
33410
|
+
0,
|
|
33411
|
+
(firstEventTimestampByPage.get(focusedPage.pageRef) ?? input.pollTimestamp) - 1
|
|
33412
|
+
),
|
|
33413
|
+
pageId: focusedPage.pageId,
|
|
33414
|
+
pageUrl: focusedPage.currentUrl,
|
|
33415
|
+
detail: {
|
|
33416
|
+
kind: "switch-tab",
|
|
33417
|
+
...this.focusedPageId === void 0 ? {} : { fromPageId: this.focusedPageId },
|
|
33418
|
+
toPageId: focusedPage.pageId
|
|
33419
|
+
}
|
|
33420
|
+
});
|
|
33421
|
+
this.focusedPageId = focusedPage.pageId;
|
|
33422
|
+
}
|
|
33423
|
+
return dedupeConsecutiveSwitchActions(actions);
|
|
33424
|
+
}
|
|
33425
|
+
normalizeRawEvent(event, page, currentUrl) {
|
|
33426
|
+
switch (event.kind) {
|
|
33427
|
+
case "navigate":
|
|
33428
|
+
return [
|
|
33429
|
+
{
|
|
33430
|
+
kind: "navigate",
|
|
33431
|
+
timestamp: event.timestamp,
|
|
33432
|
+
pageId: page.pageId,
|
|
33433
|
+
pageUrl: event.url,
|
|
33434
|
+
detail: {
|
|
33435
|
+
kind: "navigate",
|
|
33436
|
+
url: event.url,
|
|
33437
|
+
source: event.source
|
|
33438
|
+
}
|
|
33439
|
+
}
|
|
33440
|
+
];
|
|
33441
|
+
case "click":
|
|
33442
|
+
return [
|
|
33443
|
+
{
|
|
33444
|
+
kind: "click",
|
|
33445
|
+
timestamp: event.timestamp,
|
|
33446
|
+
pageId: page.pageId,
|
|
33447
|
+
pageUrl: currentUrl,
|
|
33448
|
+
selector: event.selector,
|
|
33449
|
+
detail: {
|
|
33450
|
+
kind: "click",
|
|
33451
|
+
button: event.button,
|
|
33452
|
+
modifiers: event.modifiers
|
|
33453
|
+
}
|
|
33454
|
+
}
|
|
33455
|
+
];
|
|
33456
|
+
case "dblclick":
|
|
33457
|
+
return [
|
|
33458
|
+
{
|
|
33459
|
+
kind: "dblclick",
|
|
33460
|
+
timestamp: event.timestamp,
|
|
33461
|
+
pageId: page.pageId,
|
|
33462
|
+
pageUrl: currentUrl,
|
|
33463
|
+
selector: event.selector,
|
|
33464
|
+
detail: {
|
|
33465
|
+
kind: "dblclick"
|
|
33466
|
+
}
|
|
33467
|
+
}
|
|
33468
|
+
];
|
|
33469
|
+
case "type":
|
|
33470
|
+
return [
|
|
33471
|
+
{
|
|
33472
|
+
kind: "type",
|
|
33473
|
+
timestamp: event.timestamp,
|
|
33474
|
+
pageId: page.pageId,
|
|
33475
|
+
pageUrl: currentUrl,
|
|
33476
|
+
selector: event.selector,
|
|
33477
|
+
detail: {
|
|
33478
|
+
kind: "type",
|
|
33479
|
+
text: event.text
|
|
33480
|
+
}
|
|
33481
|
+
}
|
|
33482
|
+
];
|
|
33483
|
+
case "keypress":
|
|
33484
|
+
return [
|
|
33485
|
+
{
|
|
33486
|
+
kind: "keypress",
|
|
33487
|
+
timestamp: event.timestamp,
|
|
33488
|
+
pageId: page.pageId,
|
|
33489
|
+
pageUrl: currentUrl,
|
|
33490
|
+
...event.selector === void 0 ? {} : { selector: event.selector },
|
|
33491
|
+
detail: {
|
|
33492
|
+
kind: "keypress",
|
|
33493
|
+
key: event.key,
|
|
33494
|
+
modifiers: event.modifiers
|
|
33495
|
+
}
|
|
33496
|
+
}
|
|
33497
|
+
];
|
|
33498
|
+
case "scroll":
|
|
33499
|
+
return [
|
|
33500
|
+
{
|
|
33501
|
+
kind: "scroll",
|
|
33502
|
+
timestamp: event.timestamp,
|
|
33503
|
+
pageId: page.pageId,
|
|
33504
|
+
pageUrl: currentUrl,
|
|
33505
|
+
...event.selector === void 0 ? {} : { selector: event.selector },
|
|
33506
|
+
detail: {
|
|
33507
|
+
kind: "scroll",
|
|
33508
|
+
deltaX: event.deltaX,
|
|
33509
|
+
deltaY: event.deltaY
|
|
33510
|
+
}
|
|
33511
|
+
}
|
|
33512
|
+
];
|
|
33513
|
+
case "select-option":
|
|
33514
|
+
return [
|
|
33515
|
+
{
|
|
33516
|
+
kind: "select-option",
|
|
33517
|
+
timestamp: event.timestamp,
|
|
33518
|
+
pageId: page.pageId,
|
|
33519
|
+
pageUrl: currentUrl,
|
|
33520
|
+
selector: event.selector,
|
|
33521
|
+
detail: {
|
|
33522
|
+
kind: "select-option",
|
|
33523
|
+
value: event.value,
|
|
33524
|
+
...event.label === void 0 ? {} : { label: event.label }
|
|
33525
|
+
}
|
|
33526
|
+
}
|
|
33527
|
+
];
|
|
33528
|
+
case "reload":
|
|
33529
|
+
return [
|
|
33530
|
+
{
|
|
33531
|
+
kind: "reload",
|
|
33532
|
+
timestamp: event.timestamp,
|
|
33533
|
+
pageId: page.pageId,
|
|
33534
|
+
pageUrl: event.url,
|
|
33535
|
+
detail: {
|
|
33536
|
+
kind: "reload",
|
|
33537
|
+
url: event.url
|
|
33538
|
+
}
|
|
33539
|
+
}
|
|
33540
|
+
];
|
|
33541
|
+
case "go-back":
|
|
33542
|
+
return [
|
|
33543
|
+
{
|
|
33544
|
+
kind: "go-back",
|
|
33545
|
+
timestamp: event.timestamp,
|
|
33546
|
+
pageId: page.pageId,
|
|
33547
|
+
pageUrl: event.url,
|
|
33548
|
+
detail: {
|
|
33549
|
+
kind: "go-back",
|
|
33550
|
+
url: event.url
|
|
33551
|
+
}
|
|
33552
|
+
}
|
|
33553
|
+
];
|
|
33554
|
+
case "go-forward":
|
|
33555
|
+
return [
|
|
33556
|
+
{
|
|
33557
|
+
kind: "go-forward",
|
|
33558
|
+
timestamp: event.timestamp,
|
|
33559
|
+
pageId: page.pageId,
|
|
33560
|
+
pageUrl: event.url,
|
|
33561
|
+
detail: {
|
|
33562
|
+
kind: "go-forward",
|
|
33563
|
+
url: event.url
|
|
33564
|
+
}
|
|
33565
|
+
}
|
|
33566
|
+
];
|
|
33567
|
+
}
|
|
33568
|
+
}
|
|
33569
|
+
ensureKnownPage(pageRef, url, openerPageRef) {
|
|
33570
|
+
const existing = this.pages.get(pageRef);
|
|
33571
|
+
if (existing !== void 0) {
|
|
33572
|
+
return existing;
|
|
33573
|
+
}
|
|
33574
|
+
const openerPageId = openerPageRef === void 0 ? void 0 : this.pages.get(openerPageRef)?.pageId;
|
|
33575
|
+
const page = {
|
|
33576
|
+
pageId: `page${String(this.nextPageOrdinal++)}`,
|
|
33577
|
+
pageRef,
|
|
33578
|
+
currentUrl: url,
|
|
33579
|
+
...openerPageRef === void 0 ? {} : { openerPageRef },
|
|
33580
|
+
...openerPageId === void 0 ? {} : { openerPageId }
|
|
33581
|
+
};
|
|
33582
|
+
this.pages.set(pageRef, page);
|
|
33583
|
+
return page;
|
|
33584
|
+
}
|
|
33585
|
+
updateKnownPage(pageRef, url, openerPageRef) {
|
|
33586
|
+
const current = this.pages.get(pageRef);
|
|
33587
|
+
if (current === void 0) {
|
|
33588
|
+
this.ensureKnownPage(pageRef, url, openerPageRef);
|
|
33589
|
+
return;
|
|
33590
|
+
}
|
|
33591
|
+
const openerPageId = openerPageRef === void 0 ? void 0 : this.pages.get(openerPageRef)?.pageId;
|
|
33592
|
+
this.pages.set(pageRef, {
|
|
33593
|
+
...current,
|
|
33594
|
+
currentUrl: url,
|
|
33595
|
+
...openerPageRef === void 0 ? {} : { openerPageRef },
|
|
33596
|
+
...openerPageId === void 0 ? {} : { openerPageId }
|
|
33597
|
+
});
|
|
33598
|
+
}
|
|
33599
|
+
};
|
|
33600
|
+
function normalizeSnapshot(value, fallbackUrl) {
|
|
33601
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
33602
|
+
return {
|
|
33603
|
+
url: fallbackUrl,
|
|
33604
|
+
focused: false,
|
|
33605
|
+
visibilityState: "hidden",
|
|
33606
|
+
stopRequested: false,
|
|
33607
|
+
events: []
|
|
33608
|
+
};
|
|
33609
|
+
}
|
|
33610
|
+
const snapshot = value;
|
|
33611
|
+
return {
|
|
33612
|
+
url: typeof snapshot.url === "string" ? snapshot.url : fallbackUrl,
|
|
33613
|
+
focused: snapshot.focused === true,
|
|
33614
|
+
visibilityState: snapshot.visibilityState === "visible" || snapshot.visibilityState === "prerender" || snapshot.visibilityState === "hidden" ? snapshot.visibilityState : "hidden",
|
|
33615
|
+
stopRequested: snapshot.stopRequested === true,
|
|
33616
|
+
events: Array.isArray(snapshot.events) ? snapshot.events : []
|
|
33617
|
+
};
|
|
33618
|
+
}
|
|
33619
|
+
function dedupeConsecutiveSwitchActions(actions) {
|
|
33620
|
+
const deduped = [];
|
|
33621
|
+
for (const action of actions) {
|
|
33622
|
+
const previous = deduped[deduped.length - 1];
|
|
33623
|
+
if (previous?.kind === "switch-tab" && action.kind === "switch-tab" && previous.pageId === action.pageId) {
|
|
33624
|
+
continue;
|
|
33625
|
+
}
|
|
33626
|
+
deduped.push(action);
|
|
33627
|
+
}
|
|
33628
|
+
return deduped;
|
|
33629
|
+
}
|
|
33630
|
+
function actionSortPriority(kind) {
|
|
33631
|
+
switch (kind) {
|
|
33632
|
+
case "new-tab":
|
|
33633
|
+
return 0;
|
|
33634
|
+
case "switch-tab":
|
|
33635
|
+
return 1;
|
|
33636
|
+
case "navigate":
|
|
33637
|
+
case "go-back":
|
|
33638
|
+
case "go-forward":
|
|
33639
|
+
case "reload":
|
|
33640
|
+
return 2;
|
|
33641
|
+
case "click":
|
|
33642
|
+
case "dblclick":
|
|
33643
|
+
return 3;
|
|
33644
|
+
case "type":
|
|
33645
|
+
case "keypress":
|
|
33646
|
+
case "select-option":
|
|
33647
|
+
return 4;
|
|
33648
|
+
case "scroll":
|
|
33649
|
+
return 5;
|
|
33650
|
+
case "close-tab":
|
|
33651
|
+
return 6;
|
|
33652
|
+
}
|
|
33653
|
+
}
|
|
33654
|
+
function delay(ms) {
|
|
33655
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
33656
|
+
}
|
|
33657
|
+
|
|
33658
|
+
// ../runtime-core/src/recorder/codegen.ts
|
|
33659
|
+
var DISPATCH_KEY_SCRIPT = String.raw`(key) => {
|
|
33660
|
+
const target = document.activeElement;
|
|
33661
|
+
if (!(target instanceof Element)) {
|
|
33662
|
+
throw new Error("No active element is available for key replay.");
|
|
33663
|
+
}
|
|
33664
|
+
const normalizedKey = String(key);
|
|
33665
|
+
const eventInit = {
|
|
33666
|
+
key: normalizedKey,
|
|
33667
|
+
code: normalizedKey,
|
|
33668
|
+
bubbles: true,
|
|
33669
|
+
cancelable: true,
|
|
33670
|
+
};
|
|
33671
|
+
target.dispatchEvent(new KeyboardEvent("keydown", eventInit));
|
|
33672
|
+
target.dispatchEvent(new KeyboardEvent("keyup", eventInit));
|
|
33673
|
+
}`;
|
|
33674
|
+
var SELECT_OPTION_SCRIPT = String.raw`(selector, value) => {
|
|
33675
|
+
const target = document.querySelector(String(selector));
|
|
33676
|
+
if (!(target instanceof HTMLSelectElement)) {
|
|
33677
|
+
throw new Error("Unable to find a <select> element for option replay.");
|
|
33678
|
+
}
|
|
33679
|
+
target.value = String(value);
|
|
33680
|
+
target.dispatchEvent(new Event("input", { bubbles: true }));
|
|
33681
|
+
target.dispatchEvent(new Event("change", { bubbles: true }));
|
|
33682
|
+
}`;
|
|
33683
|
+
var HISTORY_ACTION_SCRIPT = String.raw`(action) => {
|
|
33684
|
+
const normalized = String(action);
|
|
33685
|
+
if (normalized === "back") {
|
|
33686
|
+
history.back();
|
|
33687
|
+
return;
|
|
33688
|
+
}
|
|
33689
|
+
if (normalized === "forward") {
|
|
33690
|
+
history.forward();
|
|
33691
|
+
return;
|
|
33692
|
+
}
|
|
33693
|
+
if (normalized === "reload") {
|
|
33694
|
+
location.reload();
|
|
33695
|
+
return;
|
|
33696
|
+
}
|
|
33697
|
+
throw new Error("Unsupported history action: " + normalized);
|
|
33698
|
+
}`;
|
|
33699
|
+
var WINDOW_SCROLL_SCRIPT = String.raw`(deltaX, deltaY) => {
|
|
33700
|
+
window.scrollBy(Number(deltaX), Number(deltaY));
|
|
33701
|
+
}`;
|
|
33702
|
+
function generateReplayScript(options) {
|
|
33703
|
+
const pageIds = collectPageIds(options.actions);
|
|
33704
|
+
const initialPageId = pageIds[0] ?? "page0";
|
|
33705
|
+
const lines = [
|
|
33706
|
+
`import { Opensteer } from "opensteer";`,
|
|
33707
|
+
``,
|
|
33708
|
+
`const opensteer = new Opensteer({`,
|
|
33709
|
+
` workspace: ${JSON.stringify(options.workspace)},`,
|
|
33710
|
+
` browser: "persistent",`,
|
|
33711
|
+
`});`,
|
|
33712
|
+
``,
|
|
33713
|
+
`const ${initialPageId} = (await opensteer.open(${JSON.stringify(options.startUrl)})).pageRef;`,
|
|
33714
|
+
`let activePageRef: string | undefined = ${initialPageId};`,
|
|
33715
|
+
``,
|
|
33716
|
+
`async function ensureActive(pageRef: string): Promise<void> {`,
|
|
33717
|
+
` if (activePageRef === pageRef) {`,
|
|
33718
|
+
` return;`,
|
|
33719
|
+
` }`,
|
|
33720
|
+
` await opensteer.activatePage({ pageRef });`,
|
|
33721
|
+
` activePageRef = pageRef;`,
|
|
33722
|
+
`}`,
|
|
33723
|
+
``,
|
|
33724
|
+
`async function dispatchKey(pageRef: string, key: string): Promise<void> {`,
|
|
33725
|
+
` await ensureActive(pageRef);`,
|
|
33726
|
+
` await opensteer.evaluate({`,
|
|
33727
|
+
` pageRef,`,
|
|
33728
|
+
` script: ${JSON.stringify(DISPATCH_KEY_SCRIPT)},`,
|
|
33729
|
+
` args: [key],`,
|
|
33730
|
+
` });`,
|
|
33731
|
+
`}`,
|
|
33732
|
+
``,
|
|
33733
|
+
`async function selectOption(pageRef: string, selector: string, value: string): Promise<void> {`,
|
|
33734
|
+
` await ensureActive(pageRef);`,
|
|
33735
|
+
` await opensteer.evaluate({`,
|
|
33736
|
+
` pageRef,`,
|
|
33737
|
+
` script: ${JSON.stringify(SELECT_OPTION_SCRIPT)},`,
|
|
33738
|
+
` args: [selector, value],`,
|
|
33739
|
+
` });`,
|
|
33740
|
+
`}`,
|
|
33741
|
+
``,
|
|
33742
|
+
`async function runHistoryAction(pageRef: string, action: "back" | "forward" | "reload"): Promise<void> {`,
|
|
33743
|
+
` await ensureActive(pageRef);`,
|
|
33744
|
+
` await opensteer.evaluate({`,
|
|
33745
|
+
` pageRef,`,
|
|
33746
|
+
` script: ${JSON.stringify(HISTORY_ACTION_SCRIPT)},`,
|
|
33747
|
+
` args: [action],`,
|
|
33748
|
+
` });`,
|
|
33749
|
+
`}`,
|
|
33750
|
+
``,
|
|
33751
|
+
`try {`
|
|
33752
|
+
];
|
|
33753
|
+
const declaredPages = /* @__PURE__ */ new Set([initialPageId]);
|
|
33754
|
+
for (let index = 0; index < options.actions.length; index += 1) {
|
|
33755
|
+
const action = options.actions[index];
|
|
33756
|
+
const pageVar = action.pageId;
|
|
33757
|
+
if (action.kind === "type") {
|
|
33758
|
+
const nextAction = options.actions[index + 1];
|
|
33759
|
+
const mergedPressEnter = nextAction?.kind === "keypress" && nextAction.pageId === action.pageId && nextAction.selector === action.selector && nextAction.detail.kind === "keypress" && nextAction.detail.key === "Enter" && nextAction.detail.modifiers.length === 0;
|
|
33760
|
+
lines.push(` await ensureActive(${pageVar});`);
|
|
33761
|
+
lines.push(
|
|
33762
|
+
` await opensteer.input({ selector: ${JSON.stringify(requireSelector(action))}, text: ${JSON.stringify(action.detail.text)}${mergedPressEnter ? `, pressEnter: true` : ``} });`
|
|
33763
|
+
);
|
|
33764
|
+
if (mergedPressEnter) {
|
|
33765
|
+
index += 1;
|
|
33766
|
+
}
|
|
33767
|
+
continue;
|
|
33768
|
+
}
|
|
33769
|
+
if (action.kind === "switch-tab" && options.actions[index - 1]?.kind === "new-tab" && options.actions[index - 1]?.pageId === action.pageId) {
|
|
33770
|
+
continue;
|
|
33771
|
+
}
|
|
33772
|
+
switch (action.kind) {
|
|
33773
|
+
case "navigate":
|
|
33774
|
+
lines.push(` await ensureActive(${pageVar});`);
|
|
33775
|
+
lines.push(` await opensteer.goto(${JSON.stringify(action.detail.url)});`);
|
|
33776
|
+
break;
|
|
33777
|
+
case "click":
|
|
33778
|
+
lines.push(` await ensureActive(${pageVar});`);
|
|
33779
|
+
lines.push(
|
|
33780
|
+
` await opensteer.click({ selector: ${JSON.stringify(requireSelector(action))} });`
|
|
33781
|
+
);
|
|
33782
|
+
break;
|
|
33783
|
+
case "dblclick":
|
|
33784
|
+
lines.push(` await ensureActive(${pageVar});`);
|
|
33785
|
+
lines.push(
|
|
33786
|
+
` await opensteer.click({ selector: ${JSON.stringify(requireSelector(action))} });`
|
|
33787
|
+
);
|
|
33788
|
+
lines.push(
|
|
33789
|
+
` await opensteer.click({ selector: ${JSON.stringify(requireSelector(action))} });`
|
|
33790
|
+
);
|
|
33791
|
+
break;
|
|
33792
|
+
case "keypress":
|
|
33793
|
+
lines.push(
|
|
33794
|
+
` await dispatchKey(${pageVar}, ${JSON.stringify(action.detail.key)});`
|
|
33795
|
+
);
|
|
33796
|
+
break;
|
|
33797
|
+
case "scroll": {
|
|
33798
|
+
const { direction, amount, isWindowScroll } = normalizeScrollAction(action);
|
|
33799
|
+
lines.push(` await ensureActive(${pageVar});`);
|
|
33800
|
+
if (isWindowScroll) {
|
|
33801
|
+
lines.push(` await opensteer.evaluate({`);
|
|
33802
|
+
lines.push(` pageRef: ${pageVar},`);
|
|
33803
|
+
lines.push(` script: ${JSON.stringify(WINDOW_SCROLL_SCRIPT)},`);
|
|
33804
|
+
lines.push(
|
|
33805
|
+
` args: [${String(action.detail.deltaX)}, ${String(action.detail.deltaY)}],`
|
|
33806
|
+
);
|
|
33807
|
+
lines.push(` });`);
|
|
33808
|
+
} else {
|
|
33809
|
+
lines.push(
|
|
33810
|
+
` await opensteer.scroll({ selector: ${JSON.stringify(requireSelector(action))}, direction: ${JSON.stringify(direction)}, amount: ${String(amount)} });`
|
|
33811
|
+
);
|
|
33812
|
+
}
|
|
33813
|
+
break;
|
|
33814
|
+
}
|
|
33815
|
+
case "select-option":
|
|
33816
|
+
lines.push(
|
|
33817
|
+
` await selectOption(${pageVar}, ${JSON.stringify(requireSelector(action))}, ${JSON.stringify(action.detail.value)});`
|
|
33818
|
+
);
|
|
33819
|
+
break;
|
|
33820
|
+
case "new-tab": {
|
|
33821
|
+
const openerPageVar = action.detail.openerPageId;
|
|
33822
|
+
const shouldUseWaitForPage = shouldUsePopupWait(options.actions, index, openerPageVar);
|
|
33823
|
+
const creationLine = shouldUseWaitForPage && openerPageVar !== void 0 ? ` const ${pageVar} = (await opensteer.waitForPage({ openerPageRef: ${openerPageVar}, timeoutMs: 30_000 })).pageRef;` : ` const ${pageVar} = (await opensteer.newPage({${renderNewPageArguments(action.detail.openerPageId, action.detail.initialUrl)}})).pageRef;`;
|
|
33824
|
+
lines.push(creationLine);
|
|
33825
|
+
lines.push(` activePageRef = ${pageVar};`);
|
|
33826
|
+
declaredPages.add(pageVar);
|
|
33827
|
+
break;
|
|
33828
|
+
}
|
|
33829
|
+
case "close-tab":
|
|
33830
|
+
lines.push(` await opensteer.closePage({ pageRef: ${pageVar} });`);
|
|
33831
|
+
lines.push(` if (activePageRef === ${pageVar}) {`);
|
|
33832
|
+
lines.push(` activePageRef = undefined;`);
|
|
33833
|
+
lines.push(` }`);
|
|
33834
|
+
break;
|
|
33835
|
+
case "switch-tab":
|
|
33836
|
+
lines.push(` await ensureActive(${pageVar});`);
|
|
33837
|
+
break;
|
|
33838
|
+
case "go-back":
|
|
33839
|
+
lines.push(` await runHistoryAction(${pageVar}, "back");`);
|
|
33840
|
+
break;
|
|
33841
|
+
case "go-forward":
|
|
33842
|
+
lines.push(` await runHistoryAction(${pageVar}, "forward");`);
|
|
33843
|
+
break;
|
|
33844
|
+
case "reload":
|
|
33845
|
+
lines.push(` await runHistoryAction(${pageVar}, "reload");`);
|
|
33846
|
+
break;
|
|
33847
|
+
}
|
|
33848
|
+
}
|
|
33849
|
+
lines.push(`} finally {`);
|
|
33850
|
+
lines.push(` await opensteer.close();`);
|
|
33851
|
+
lines.push(`}`);
|
|
33852
|
+
lines.push(``);
|
|
33853
|
+
return `${lines.join("\n")}
|
|
33854
|
+
`;
|
|
33855
|
+
}
|
|
33856
|
+
function collectPageIds(actions) {
|
|
33857
|
+
const orderedIds = /* @__PURE__ */ new Set(["page0"]);
|
|
33858
|
+
for (const action of actions) {
|
|
33859
|
+
orderedIds.add(action.pageId);
|
|
33860
|
+
if (action.kind === "new-tab" && action.detail.openerPageId !== void 0) {
|
|
33861
|
+
orderedIds.add(action.detail.openerPageId);
|
|
33862
|
+
}
|
|
33863
|
+
if (action.kind === "switch-tab" && action.detail.fromPageId !== void 0) {
|
|
33864
|
+
orderedIds.add(action.detail.fromPageId);
|
|
33865
|
+
}
|
|
33866
|
+
if (action.kind === "switch-tab") {
|
|
33867
|
+
orderedIds.add(action.detail.toPageId);
|
|
33868
|
+
}
|
|
33869
|
+
}
|
|
33870
|
+
return [...orderedIds].sort(comparePageIds);
|
|
33871
|
+
}
|
|
33872
|
+
function comparePageIds(left, right) {
|
|
33873
|
+
const leftMatch = /^page(\d+)$/u.exec(left);
|
|
33874
|
+
const rightMatch = /^page(\d+)$/u.exec(right);
|
|
33875
|
+
if (leftMatch && rightMatch) {
|
|
33876
|
+
return Number(leftMatch[1]) - Number(rightMatch[1]);
|
|
33877
|
+
}
|
|
33878
|
+
return left.localeCompare(right);
|
|
33879
|
+
}
|
|
33880
|
+
function requireSelector(action) {
|
|
33881
|
+
if (action.selector === void 0) {
|
|
33882
|
+
throw new Error(`Action "${action.kind}" on ${action.pageId} is missing a selector.`);
|
|
33883
|
+
}
|
|
33884
|
+
return action.selector;
|
|
33885
|
+
}
|
|
33886
|
+
function normalizeScrollAction(action) {
|
|
33887
|
+
const horizontal = Math.abs(action.detail.deltaX);
|
|
33888
|
+
const vertical = Math.abs(action.detail.deltaY);
|
|
33889
|
+
if (vertical >= horizontal) {
|
|
33890
|
+
return {
|
|
33891
|
+
direction: action.detail.deltaY < 0 ? "up" : "down",
|
|
33892
|
+
amount: Math.max(1, Math.round(vertical)),
|
|
33893
|
+
isWindowScroll: action.selector === void 0
|
|
33894
|
+
};
|
|
33895
|
+
}
|
|
33896
|
+
return {
|
|
33897
|
+
direction: action.detail.deltaX < 0 ? "left" : "right",
|
|
33898
|
+
amount: Math.max(1, Math.round(horizontal)),
|
|
33899
|
+
isWindowScroll: action.selector === void 0
|
|
33900
|
+
};
|
|
33901
|
+
}
|
|
33902
|
+
function shouldUsePopupWait(actions, newTabIndex, openerPageId) {
|
|
33903
|
+
if (openerPageId === void 0 || newTabIndex === 0) {
|
|
33904
|
+
return false;
|
|
33905
|
+
}
|
|
33906
|
+
const previousAction = actions[newTabIndex - 1];
|
|
33907
|
+
if (previousAction === void 0 || previousAction.pageId !== openerPageId) {
|
|
33908
|
+
return false;
|
|
33909
|
+
}
|
|
33910
|
+
return previousAction.kind === "click" || previousAction.kind === "dblclick" || previousAction.kind === "keypress";
|
|
33911
|
+
}
|
|
33912
|
+
function renderNewPageArguments(openerPageId, initialUrl) {
|
|
33913
|
+
const argumentsList = [];
|
|
33914
|
+
if (openerPageId !== void 0) {
|
|
33915
|
+
argumentsList.push(` openerPageRef: ${openerPageId}`);
|
|
33916
|
+
}
|
|
33917
|
+
if (initialUrl.length > 0 && initialUrl !== "about:blank") {
|
|
33918
|
+
argumentsList.push(` url: ${JSON.stringify(initialUrl)}`);
|
|
33919
|
+
}
|
|
33920
|
+
if (argumentsList.length === 0) {
|
|
33921
|
+
return ``;
|
|
33922
|
+
}
|
|
33923
|
+
return ` ${argumentsList.join(",")}`.trimStart();
|
|
33924
|
+
}
|
|
33925
|
+
|
|
33926
|
+
// src/sdk/runtime.ts
|
|
33927
|
+
var OpensteerRuntime = class extends OpensteerSessionRuntime {
|
|
33928
|
+
constructor(options = {}) {
|
|
33929
|
+
const publicWorkspace = normalizeWorkspace2(options.workspace);
|
|
33930
|
+
const rootPath = options.rootPath ?? (publicWorkspace === void 0 ? path7.resolve(options.rootDir ?? process.cwd(), ".opensteer", "temporary", randomUUID()) : resolveFilesystemWorkspacePath({
|
|
33931
|
+
rootDir: path7.resolve(options.rootDir ?? process.cwd()),
|
|
33932
|
+
workspace: publicWorkspace
|
|
33933
|
+
}));
|
|
33934
|
+
const cleanupRootOnClose = options.cleanupRootOnClose ?? publicWorkspace === void 0;
|
|
33935
|
+
const engineName = options.engineName ?? DEFAULT_OPENSTEER_ENGINE;
|
|
33936
|
+
assertSupportedEngineOptions({
|
|
33937
|
+
engineName,
|
|
33938
|
+
...options.browser === void 0 ? {} : { browser: options.browser },
|
|
33939
|
+
...options.context === void 0 ? {} : { context: options.context }
|
|
33940
|
+
});
|
|
33941
|
+
super(
|
|
33942
|
+
buildSharedRuntimeOptions({
|
|
33943
|
+
name: publicWorkspace ?? "default",
|
|
33944
|
+
rootPath,
|
|
33945
|
+
...publicWorkspace === void 0 ? {} : { workspaceName: publicWorkspace },
|
|
33946
|
+
...options.browser === void 0 ? {} : { browser: options.browser },
|
|
33947
|
+
...options.launch === void 0 ? {} : { launch: options.launch },
|
|
33948
|
+
...options.context === void 0 ? {} : { context: options.context },
|
|
33949
|
+
engineName,
|
|
33950
|
+
...options.engine === void 0 ? {} : { engine: options.engine },
|
|
33951
|
+
...options.engineFactory === void 0 ? {} : { engineFactory: options.engineFactory },
|
|
33952
|
+
...options.policy === void 0 ? {} : { policy: options.policy },
|
|
33953
|
+
...options.descriptorStore === void 0 ? {} : { descriptorStore: options.descriptorStore },
|
|
33954
|
+
...options.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: options.extractionDescriptorStore },
|
|
33955
|
+
...options.registryOverrides === void 0 ? {} : { registryOverrides: options.registryOverrides },
|
|
33956
|
+
cleanupRootOnClose,
|
|
33957
|
+
...options.observability === void 0 ? {} : { observability: options.observability },
|
|
33958
|
+
...options.observationSessionId === void 0 ? {} : { observationSessionId: options.observationSessionId },
|
|
33959
|
+
...options.observationSink === void 0 ? {} : { observationSink: options.observationSink }
|
|
33960
|
+
})
|
|
33961
|
+
);
|
|
33962
|
+
}
|
|
33963
|
+
};
|
|
33964
|
+
var OpensteerSessionRuntime2 = class extends OpensteerSessionRuntime {
|
|
33965
|
+
constructor(options) {
|
|
33966
|
+
const rootPath = options.rootPath ?? path7.resolve(options.rootDir ?? process.cwd());
|
|
33967
|
+
const cleanupRootOnClose = options.cleanupRootOnClose ?? false;
|
|
33968
|
+
const engineName = options.engineName ?? DEFAULT_OPENSTEER_ENGINE;
|
|
33969
|
+
assertSupportedEngineOptions({
|
|
33970
|
+
engineName,
|
|
33971
|
+
...options.browser === void 0 ? {} : { browser: options.browser },
|
|
33972
|
+
...options.context === void 0 ? {} : { context: options.context }
|
|
33973
|
+
});
|
|
33974
|
+
super(
|
|
33975
|
+
buildSharedRuntimeOptions({
|
|
33976
|
+
name: options.name,
|
|
33977
|
+
rootPath,
|
|
33978
|
+
...options.browser === void 0 ? {} : { browser: options.browser },
|
|
33979
|
+
...options.launch === void 0 ? {} : { launch: options.launch },
|
|
33980
|
+
...options.context === void 0 ? {} : { context: options.context },
|
|
33981
|
+
engineName,
|
|
33982
|
+
...options.engine === void 0 ? {} : { engine: options.engine },
|
|
33983
|
+
...options.engineFactory === void 0 ? {} : { engineFactory: options.engineFactory },
|
|
33984
|
+
...options.policy === void 0 ? {} : { policy: options.policy },
|
|
33985
|
+
...options.descriptorStore === void 0 ? {} : { descriptorStore: options.descriptorStore },
|
|
33986
|
+
...options.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: options.extractionDescriptorStore },
|
|
33987
|
+
...options.registryOverrides === void 0 ? {} : { registryOverrides: options.registryOverrides },
|
|
33988
|
+
cleanupRootOnClose,
|
|
33989
|
+
...options.observability === void 0 ? {} : { observability: options.observability },
|
|
33990
|
+
...options.observationSessionId === void 0 ? {} : { observationSessionId: options.observationSessionId },
|
|
33991
|
+
...options.observationSink === void 0 ? {} : { observationSink: options.observationSink }
|
|
33992
|
+
})
|
|
33993
|
+
);
|
|
33994
|
+
}
|
|
33995
|
+
};
|
|
33996
|
+
function buildSharedRuntimeOptions(input) {
|
|
33997
|
+
const ownership = resolveOwnership(input.browser);
|
|
33998
|
+
const engineFactory = input.engineFactory ?? ((factoryOptions) => new OpensteerBrowserManager({
|
|
33999
|
+
rootPath: input.rootPath,
|
|
34000
|
+
...input.workspaceName === void 0 ? {} : { workspace: input.workspaceName },
|
|
34001
|
+
engineName: input.engineName,
|
|
34002
|
+
...(factoryOptions.browser ?? input.browser) === void 0 ? {} : { browser: factoryOptions.browser ?? input.browser },
|
|
34003
|
+
...(factoryOptions.launch ?? input.launch) === void 0 ? {} : { launch: factoryOptions.launch ?? input.launch },
|
|
34004
|
+
...(factoryOptions.context ?? input.context) === void 0 ? {} : { context: factoryOptions.context ?? input.context }
|
|
34005
|
+
}).createEngine());
|
|
34006
|
+
return {
|
|
34007
|
+
name: input.name,
|
|
34008
|
+
...input.workspaceName === void 0 ? {} : { workspaceName: input.workspaceName },
|
|
34009
|
+
rootPath: input.rootPath,
|
|
34010
|
+
...input.engine === void 0 ? {} : { engine: input.engine },
|
|
34011
|
+
...input.engine === void 0 ? { engineFactory } : {},
|
|
34012
|
+
...input.policy === void 0 ? {} : { policy: input.policy },
|
|
34013
|
+
...input.descriptorStore === void 0 ? {} : { descriptorStore: input.descriptorStore },
|
|
34014
|
+
...input.extractionDescriptorStore === void 0 ? {} : { extractionDescriptorStore: input.extractionDescriptorStore },
|
|
34015
|
+
...input.registryOverrides === void 0 ? {} : { registryOverrides: input.registryOverrides },
|
|
34016
|
+
cleanupRootOnClose: input.cleanupRootOnClose,
|
|
34017
|
+
...input.observability === void 0 ? {} : { observability: input.observability },
|
|
34018
|
+
...input.observationSessionId === void 0 ? {} : { observationSessionId: input.observationSessionId },
|
|
34019
|
+
...input.observationSink === void 0 ? {} : { observationSink: input.observationSink },
|
|
34020
|
+
sessionInfo: {
|
|
34021
|
+
provider: {
|
|
34022
|
+
mode: "local",
|
|
34023
|
+
ownership,
|
|
34024
|
+
engine: input.engineName
|
|
34025
|
+
},
|
|
34026
|
+
...input.workspaceName === void 0 ? {} : { workspace: input.workspaceName },
|
|
34027
|
+
reconnectable: !input.cleanupRootOnClose
|
|
34028
|
+
}
|
|
34029
|
+
};
|
|
34030
|
+
}
|
|
34031
|
+
function normalizeWorkspace2(workspace) {
|
|
34032
|
+
if (workspace === void 0) {
|
|
34033
|
+
return void 0;
|
|
34034
|
+
}
|
|
34035
|
+
const trimmed = workspace.trim();
|
|
34036
|
+
return trimmed.length === 0 ? void 0 : trimmed;
|
|
34037
|
+
}
|
|
34038
|
+
function resolveOwnership(browser) {
|
|
34039
|
+
return typeof browser === "object" && browser.mode === "attach" ? "attached" : "owned";
|
|
34040
|
+
}
|
|
34041
|
+
|
|
34042
|
+
// src/provider/config.ts
|
|
34043
|
+
var OPENSTEER_PROVIDER_MODES = ["local", "cloud"];
|
|
34044
|
+
function assertProviderSupportsEngine(provider, engine) {
|
|
34045
|
+
if (engine !== "abp") {
|
|
34046
|
+
return;
|
|
34047
|
+
}
|
|
34048
|
+
if (provider === "cloud") {
|
|
34049
|
+
throw new Error(
|
|
34050
|
+
"ABP is not supported for provider=cloud. Cloud provider currently requires Playwright."
|
|
34051
|
+
);
|
|
34052
|
+
}
|
|
34053
|
+
}
|
|
34054
|
+
function normalizeOpensteerProviderMode(value, source = "OPENSTEER_PROVIDER") {
|
|
34055
|
+
const normalized = value.trim().toLowerCase();
|
|
34056
|
+
if (normalized === OPENSTEER_PROVIDER_MODES[0] || normalized === OPENSTEER_PROVIDER_MODES[1]) {
|
|
34057
|
+
return normalized;
|
|
34058
|
+
}
|
|
34059
|
+
throw new Error(
|
|
34060
|
+
`${source} must be one of ${OPENSTEER_PROVIDER_MODES.join(", ")}; received "${value}".`
|
|
34061
|
+
);
|
|
34062
|
+
}
|
|
34063
|
+
function resolveOpensteerProvider(input = {}) {
|
|
34064
|
+
if (input.provider) {
|
|
34065
|
+
return {
|
|
34066
|
+
mode: input.provider.mode,
|
|
34067
|
+
source: "explicit"
|
|
34068
|
+
};
|
|
34069
|
+
}
|
|
34070
|
+
if (input.environmentProvider !== void 0 && input.environmentProvider.trim().length > 0) {
|
|
34071
|
+
return {
|
|
34072
|
+
mode: normalizeOpensteerProviderMode(input.environmentProvider),
|
|
34073
|
+
source: "env"
|
|
34074
|
+
};
|
|
34075
|
+
}
|
|
34076
|
+
return {
|
|
34077
|
+
mode: "local",
|
|
34078
|
+
source: "default"
|
|
34079
|
+
};
|
|
34080
|
+
}
|
|
34081
|
+
var execFile2 = promisify(execFile);
|
|
34082
|
+
var NODE_SQLITE_SPECIFIER2 = `node:${"sqlite"}`;
|
|
34083
|
+
var CHROME_EPOCH_OFFSET = 11644473600000000n;
|
|
34084
|
+
var CHROME_HMAC_PREFIX_LENGTH = 32;
|
|
34085
|
+
async function readBrowserCookies(input = {}) {
|
|
34086
|
+
const brand2 = resolveRequestedBrand(input);
|
|
34087
|
+
const userDataDir = resolveBrandUserDataDir(brand2, input.userDataDir);
|
|
34088
|
+
const profileDirectory = input.profileDirectory ?? "Default";
|
|
34089
|
+
const cookiesPath = join(userDataDir, profileDirectory, "Cookies");
|
|
34090
|
+
if (!existsSync(cookiesPath)) {
|
|
34091
|
+
throw new Error(
|
|
34092
|
+
`Cookies database not found at "${cookiesPath}". Verify the browser brand, user-data-dir, and profile-directory are correct.`
|
|
34093
|
+
);
|
|
34094
|
+
}
|
|
34095
|
+
const tempDir = await mkdtemp(join(tmpdir(), "opensteer-cookies-"));
|
|
34096
|
+
try {
|
|
34097
|
+
await copyCookiesDatabase(cookiesPath, tempDir);
|
|
34098
|
+
const decryptionKey = await resolveDecryptionKey(brand2.id, userDataDir);
|
|
34099
|
+
const rows = queryAllCookies(join(tempDir, "Cookies"));
|
|
34100
|
+
const cookies = decryptCookieRows(rows, decryptionKey);
|
|
34101
|
+
return {
|
|
34102
|
+
cookies,
|
|
34103
|
+
brandId: brand2.id,
|
|
34104
|
+
brandDisplayName: brand2.displayName,
|
|
34105
|
+
userDataDir,
|
|
34106
|
+
profileDirectory
|
|
34107
|
+
};
|
|
34108
|
+
} finally {
|
|
34109
|
+
await rm(tempDir, { recursive: true, force: true }).catch(() => void 0);
|
|
34110
|
+
}
|
|
34111
|
+
}
|
|
34112
|
+
function resolveRequestedBrand(input) {
|
|
34113
|
+
if (input.brandId !== void 0) {
|
|
34114
|
+
return getBrowserBrand(input.brandId);
|
|
34115
|
+
}
|
|
34116
|
+
const installed = detectInstalledBrowserBrands()[0];
|
|
32485
34117
|
if (!installed) {
|
|
32486
34118
|
throw new Error(
|
|
32487
34119
|
"No Chromium browser found. Install a supported browser or pass brandId explicitly."
|
|
@@ -32515,11 +34147,25 @@ function queryAllCookies(dbPath) {
|
|
|
32515
34147
|
FROM cookies`
|
|
32516
34148
|
);
|
|
32517
34149
|
stmt.setReadBigInts(true);
|
|
32518
|
-
return stmt.all();
|
|
34150
|
+
return stmt.all().map(toRawCookieRow);
|
|
32519
34151
|
} finally {
|
|
32520
34152
|
database.close();
|
|
32521
34153
|
}
|
|
32522
34154
|
}
|
|
34155
|
+
function toRawCookieRow(row) {
|
|
34156
|
+
return {
|
|
34157
|
+
host_key: row.host_key,
|
|
34158
|
+
name: row.name,
|
|
34159
|
+
value: row.value,
|
|
34160
|
+
encrypted_value: row.encrypted_value,
|
|
34161
|
+
path: row.path,
|
|
34162
|
+
expires_utc: row.expires_utc,
|
|
34163
|
+
is_secure: row.is_secure,
|
|
34164
|
+
is_httponly: row.is_httponly,
|
|
34165
|
+
samesite: row.samesite,
|
|
34166
|
+
is_persistent: row.is_persistent
|
|
34167
|
+
};
|
|
34168
|
+
}
|
|
32523
34169
|
async function resolveDecryptionKey(brandId, userDataDir) {
|
|
32524
34170
|
if (process.platform === "darwin") {
|
|
32525
34171
|
const password = await resolveKeychainPassword(brandId);
|
|
@@ -32870,7 +34516,11 @@ var OpensteerCloudClient = class {
|
|
|
32870
34516
|
...input.browser === void 0 ? {} : { browser: input.browser },
|
|
32871
34517
|
...input.context === void 0 ? {} : { context: input.context },
|
|
32872
34518
|
...input.browserProfile === void 0 ? {} : { browserProfile: input.browserProfile },
|
|
32873
|
-
...input.observability === void 0 ? {} : { observability: input.observability }
|
|
34519
|
+
...input.observability === void 0 ? {} : { observability: input.observability },
|
|
34520
|
+
...input.sourceType === void 0 ? {} : { sourceType: input.sourceType },
|
|
34521
|
+
...input.sourceRef === void 0 ? {} : { sourceRef: input.sourceRef },
|
|
34522
|
+
...input.localWorkspaceRootPath === void 0 ? {} : { localWorkspaceRootPath: input.localWorkspaceRootPath },
|
|
34523
|
+
...input.locality === void 0 ? {} : { locality: input.locality }
|
|
32874
34524
|
}
|
|
32875
34525
|
});
|
|
32876
34526
|
return await response.json();
|
|
@@ -33042,12 +34692,12 @@ var OpensteerCloudClient = class {
|
|
|
33042
34692
|
`Unexpected cloud session status "${String(session.status)}" while waiting for close.`
|
|
33043
34693
|
);
|
|
33044
34694
|
}
|
|
33045
|
-
await
|
|
34695
|
+
await delay2(CLOUD_CLOSE_POLL_INTERVAL_MS);
|
|
33046
34696
|
}
|
|
33047
34697
|
throw new Error(`Timed out waiting for cloud session ${sessionId} to close.`);
|
|
33048
34698
|
}
|
|
33049
34699
|
};
|
|
33050
|
-
function
|
|
34700
|
+
function delay2(ms) {
|
|
33051
34701
|
return new Promise((resolve4) => {
|
|
33052
34702
|
setTimeout(resolve4, ms);
|
|
33053
34703
|
});
|
|
@@ -33107,6 +34757,9 @@ var OpensteerSemanticRestClient = class {
|
|
|
33107
34757
|
this.connection = connection;
|
|
33108
34758
|
}
|
|
33109
34759
|
async invoke(operation, input) {
|
|
34760
|
+
return this.invokeInternal(operation, input, false);
|
|
34761
|
+
}
|
|
34762
|
+
async invokeInternal(operation, input, hasRetried) {
|
|
33110
34763
|
const endpoint = opensteerSemanticRestEndpoints.find((entry) => entry.name === operation);
|
|
33111
34764
|
if (!endpoint) {
|
|
33112
34765
|
throw new Error(`unsupported semantic operation ${operation}`);
|
|
@@ -33116,7 +34769,7 @@ var OpensteerSemanticRestClient = class {
|
|
|
33116
34769
|
});
|
|
33117
34770
|
let response;
|
|
33118
34771
|
try {
|
|
33119
|
-
response = await fetch(`${this.connection.
|
|
34772
|
+
response = await fetch(`${await this.connection.getBaseUrl()}${endpoint.path}`, {
|
|
33120
34773
|
method: "POST",
|
|
33121
34774
|
headers: {
|
|
33122
34775
|
authorization: await this.connection.getAuthorizationHeader(),
|
|
@@ -33138,6 +34791,9 @@ var OpensteerSemanticRestClient = class {
|
|
|
33138
34791
|
}
|
|
33139
34792
|
return envelope.data;
|
|
33140
34793
|
} catch (error) {
|
|
34794
|
+
if (!hasRetried && this.connection.handleError && await this.connection.handleError(error, { operation })) {
|
|
34795
|
+
return this.invokeInternal(operation, input, true);
|
|
34796
|
+
}
|
|
33141
34797
|
if (operation === "session.close" && isFetchFailure(error)) {
|
|
33142
34798
|
return { closed: true };
|
|
33143
34799
|
}
|
|
@@ -33271,7 +34927,10 @@ var OpensteerCloudAutomationClient = class {
|
|
|
33271
34927
|
}
|
|
33272
34928
|
async connect() {
|
|
33273
34929
|
const grant = await this.issueGrant("automation");
|
|
33274
|
-
|
|
34930
|
+
if (grant.transport !== "ws") {
|
|
34931
|
+
throw new Error(`cloud issued an invalid ${grant.kind} grant transport`);
|
|
34932
|
+
}
|
|
34933
|
+
const wsUrl = new URL(grant.url);
|
|
33275
34934
|
wsUrl.searchParams.set("token", grant.token);
|
|
33276
34935
|
const socket = new WebSocket2(wsUrl);
|
|
33277
34936
|
this.socket = socket;
|
|
@@ -33636,7 +35295,7 @@ var CloudSessionProxy = class {
|
|
|
33636
35295
|
cloud;
|
|
33637
35296
|
observability;
|
|
33638
35297
|
sessionId;
|
|
33639
|
-
|
|
35298
|
+
semanticGrant;
|
|
33640
35299
|
client;
|
|
33641
35300
|
automation;
|
|
33642
35301
|
workspaceStore;
|
|
@@ -33687,7 +35346,7 @@ var CloudSessionProxy = class {
|
|
|
33687
35346
|
reconnectable: this.workspace !== void 0 || this.sessionId !== void 0 || persisted !== void 0,
|
|
33688
35347
|
capabilities: {
|
|
33689
35348
|
semanticOperations: opensteerSemanticOperationNames,
|
|
33690
|
-
sessionGrants: ["automation", "view", "cdp"],
|
|
35349
|
+
sessionGrants: ["semantic", "automation", "view", "cdp"],
|
|
33691
35350
|
instrumentation: {
|
|
33692
35351
|
route: true,
|
|
33693
35352
|
interceptScript: true,
|
|
@@ -33929,13 +35588,12 @@ var CloudSessionProxy = class {
|
|
|
33929
35588
|
return this.requireClient().invoke("computer.execute", input);
|
|
33930
35589
|
}
|
|
33931
35590
|
async close() {
|
|
33932
|
-
const session = await this.loadPersistedSession() ?? (this.sessionId === void 0
|
|
35591
|
+
const session = await this.loadPersistedSession() ?? (this.sessionId === void 0 ? void 0 : {
|
|
33933
35592
|
layout: "opensteer-session",
|
|
33934
35593
|
version: 1,
|
|
33935
35594
|
provider: "cloud",
|
|
33936
35595
|
...this.workspace === void 0 ? {} : { workspace: this.workspace },
|
|
33937
35596
|
sessionId: this.sessionId,
|
|
33938
|
-
baseUrl: this.sessionBaseUrl,
|
|
33939
35597
|
startedAt: Date.now(),
|
|
33940
35598
|
updatedAt: Date.now()
|
|
33941
35599
|
});
|
|
@@ -33954,7 +35612,7 @@ var CloudSessionProxy = class {
|
|
|
33954
35612
|
this.automation = void 0;
|
|
33955
35613
|
this.client = void 0;
|
|
33956
35614
|
this.sessionId = void 0;
|
|
33957
|
-
this.
|
|
35615
|
+
this.semanticGrant = void 0;
|
|
33958
35616
|
if (this.cleanupRootOnClose) {
|
|
33959
35617
|
await rm(this.rootPath, { recursive: true, force: true }).catch(() => void 0);
|
|
33960
35618
|
}
|
|
@@ -33970,39 +35628,56 @@ var CloudSessionProxy = class {
|
|
|
33970
35628
|
await this.automation?.close().catch(() => void 0);
|
|
33971
35629
|
this.automation = void 0;
|
|
33972
35630
|
this.sessionId = void 0;
|
|
33973
|
-
this.
|
|
35631
|
+
this.semanticGrant = void 0;
|
|
33974
35632
|
}
|
|
33975
35633
|
async ensureSession(input = {}) {
|
|
33976
35634
|
if (this.client) {
|
|
33977
35635
|
return;
|
|
33978
35636
|
}
|
|
33979
35637
|
assertSupportedCloudBrowserMode(input.browser);
|
|
35638
|
+
const localCloud = this.shouldUseLocalCloudTransport();
|
|
35639
|
+
const browserProfile = resolveCloudBrowserProfile(this.cloud, input);
|
|
33980
35640
|
const persisted = await this.loadPersistedSession();
|
|
33981
35641
|
if (persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId)) {
|
|
33982
|
-
|
|
35642
|
+
if (localCloud) {
|
|
35643
|
+
void this.syncRegistryToCloud();
|
|
35644
|
+
} else {
|
|
35645
|
+
await this.syncRegistryToCloud();
|
|
35646
|
+
}
|
|
33983
35647
|
this.bindClient(persisted);
|
|
33984
35648
|
return;
|
|
33985
35649
|
}
|
|
33986
|
-
|
|
33987
|
-
|
|
35650
|
+
if (localCloud) {
|
|
35651
|
+
void this.syncRegistryToCloud();
|
|
35652
|
+
} else {
|
|
35653
|
+
await this.syncRegistryToCloud();
|
|
35654
|
+
}
|
|
35655
|
+
const baseCreateInput = {
|
|
33988
35656
|
...this.workspace === void 0 ? {} : { name: this.workspace },
|
|
33989
35657
|
...input.launch === void 0 ? {} : { browser: input.launch },
|
|
33990
35658
|
...input.context === void 0 ? {} : { context: input.context },
|
|
33991
35659
|
...this.observability === void 0 ? {} : { observability: this.observability },
|
|
33992
|
-
...
|
|
33993
|
-
}
|
|
35660
|
+
...browserProfile === void 0 ? {} : { browserProfile }
|
|
35661
|
+
};
|
|
35662
|
+
const createInput = localCloud && this.workspace !== void 0 ? {
|
|
35663
|
+
...baseCreateInput,
|
|
35664
|
+
sourceType: "local-cloud",
|
|
35665
|
+
sourceRef: this.workspace,
|
|
35666
|
+
localWorkspaceRootPath: this.rootPath,
|
|
35667
|
+
locality: "auto"
|
|
35668
|
+
} : baseCreateInput;
|
|
35669
|
+
const session = await this.cloud.createSession(createInput);
|
|
33994
35670
|
const record = {
|
|
33995
35671
|
layout: "opensteer-session",
|
|
33996
35672
|
version: 1,
|
|
33997
35673
|
provider: "cloud",
|
|
33998
35674
|
...this.workspace === void 0 ? {} : { workspace: this.workspace },
|
|
33999
35675
|
sessionId: session.sessionId,
|
|
34000
|
-
baseUrl: session.baseUrl,
|
|
34001
35676
|
startedAt: Date.now(),
|
|
34002
35677
|
updatedAt: Date.now()
|
|
34003
35678
|
};
|
|
34004
35679
|
await this.writePersistedSession(record);
|
|
34005
|
-
this.bindClient(record);
|
|
35680
|
+
this.bindClient(record, session.initialGrants?.semantic);
|
|
34006
35681
|
}
|
|
34007
35682
|
async syncRegistryToCloud() {
|
|
34008
35683
|
if (this.workspace === void 0) {
|
|
@@ -34011,15 +35686,16 @@ var CloudSessionProxy = class {
|
|
|
34011
35686
|
try {
|
|
34012
35687
|
const workspaceStore = await this.ensureWorkspaceStore();
|
|
34013
35688
|
await syncLocalRegistryToCloud(this.cloud, this.workspace, workspaceStore);
|
|
34014
|
-
} catch
|
|
35689
|
+
} catch {
|
|
34015
35690
|
}
|
|
34016
35691
|
}
|
|
34017
|
-
bindClient(record) {
|
|
35692
|
+
bindClient(record, initialSemanticGrant) {
|
|
34018
35693
|
this.sessionId = record.sessionId;
|
|
34019
|
-
this.
|
|
35694
|
+
this.semanticGrant = initialSemanticGrant?.kind === "semantic" ? initialSemanticGrant : void 0;
|
|
34020
35695
|
this.client = new OpensteerSemanticRestClient({
|
|
34021
|
-
|
|
34022
|
-
getAuthorizationHeader: async () => this.
|
|
35696
|
+
getBaseUrl: async () => (await this.ensureSemanticGrant()).url,
|
|
35697
|
+
getAuthorizationHeader: async () => `Bearer ${(await this.ensureSemanticGrant()).token}`,
|
|
35698
|
+
handleError: (error) => this.handleSemanticClientError(error)
|
|
34023
35699
|
});
|
|
34024
35700
|
this.automation = new OpensteerCloudAutomationClient(this.cloud, record.sessionId);
|
|
34025
35701
|
}
|
|
@@ -34068,6 +35744,43 @@ var CloudSessionProxy = class {
|
|
|
34068
35744
|
}
|
|
34069
35745
|
return this.automation;
|
|
34070
35746
|
}
|
|
35747
|
+
async ensureSemanticGrant(forceRefresh = false) {
|
|
35748
|
+
if (!forceRefresh && this.semanticGrant?.kind === "semantic" && this.semanticGrant.expiresAt > Date.now() + 1e4) {
|
|
35749
|
+
return this.semanticGrant;
|
|
35750
|
+
}
|
|
35751
|
+
if (!this.sessionId) {
|
|
35752
|
+
throw new Error("Cloud session has not been initialized.");
|
|
35753
|
+
}
|
|
35754
|
+
const issued = await this.cloud.issueAccess(this.sessionId, ["semantic"]);
|
|
35755
|
+
const grant = issued.grants.semantic;
|
|
35756
|
+
if (!grant || grant.transport !== "http") {
|
|
35757
|
+
throw new Error("cloud did not issue a valid semantic grant");
|
|
35758
|
+
}
|
|
35759
|
+
this.semanticGrant = grant;
|
|
35760
|
+
return grant;
|
|
35761
|
+
}
|
|
35762
|
+
async handleSemanticClientError(error) {
|
|
35763
|
+
if (!(error instanceof OpensteerSemanticRestError)) {
|
|
35764
|
+
return false;
|
|
35765
|
+
}
|
|
35766
|
+
if (error.statusCode !== 401 && error.statusCode !== 404) {
|
|
35767
|
+
return false;
|
|
35768
|
+
}
|
|
35769
|
+
this.semanticGrant = void 0;
|
|
35770
|
+
try {
|
|
35771
|
+
await this.ensureSemanticGrant(true);
|
|
35772
|
+
return true;
|
|
35773
|
+
} catch {
|
|
35774
|
+
return false;
|
|
35775
|
+
}
|
|
35776
|
+
}
|
|
35777
|
+
shouldUseLocalCloudTransport() {
|
|
35778
|
+
if (this.workspace === void 0) {
|
|
35779
|
+
return false;
|
|
35780
|
+
}
|
|
35781
|
+
const config = this.cloud.getConfig();
|
|
35782
|
+
return isLoopbackBaseUrl(config.baseUrl);
|
|
35783
|
+
}
|
|
34071
35784
|
};
|
|
34072
35785
|
function resolveCloudBrowserProfile(cloud, input) {
|
|
34073
35786
|
return input.browserProfile ?? cloud.getConfig().browserProfile;
|
|
@@ -34083,8 +35796,29 @@ function assertSupportedCloudBrowserMode(browser) {
|
|
|
34083
35796
|
function isMissingCloudSessionError(error) {
|
|
34084
35797
|
return error instanceof Error && /\b404\b/.test(error.message);
|
|
34085
35798
|
}
|
|
35799
|
+
function isLoopbackBaseUrl(baseUrl) {
|
|
35800
|
+
let url;
|
|
35801
|
+
try {
|
|
35802
|
+
url = new URL(baseUrl);
|
|
35803
|
+
} catch {
|
|
35804
|
+
return false;
|
|
35805
|
+
}
|
|
35806
|
+
return url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1" || url.hostname === "[::1]";
|
|
35807
|
+
}
|
|
34086
35808
|
|
|
34087
35809
|
// src/sdk/runtime-resolution.ts
|
|
35810
|
+
var LOCAL_ONLY_RUNTIME_OPTION_KEYS = [
|
|
35811
|
+
"launch",
|
|
35812
|
+
"context",
|
|
35813
|
+
"engine",
|
|
35814
|
+
"engineFactory",
|
|
35815
|
+
"policy",
|
|
35816
|
+
"descriptorStore",
|
|
35817
|
+
"extractionDescriptorStore",
|
|
35818
|
+
"registryOverrides",
|
|
35819
|
+
"observationSessionId",
|
|
35820
|
+
"observationSink"
|
|
35821
|
+
];
|
|
34088
35822
|
function resolveOpensteerRuntimeConfig(input = {}) {
|
|
34089
35823
|
const environment = input.environment ?? process.env;
|
|
34090
35824
|
const provider = resolveOpensteerProvider({
|
|
@@ -34109,8 +35843,15 @@ function createOpensteerSemanticRuntime(input = {}) {
|
|
|
34109
35843
|
...input.provider === void 0 ? {} : { provider: input.provider },
|
|
34110
35844
|
...input.environment === void 0 ? {} : { environment: input.environment }
|
|
34111
35845
|
});
|
|
34112
|
-
|
|
34113
|
-
|
|
35846
|
+
const localOnlyRuntimeOptions = listLocalOnlyRuntimeOptions(runtimeOptions);
|
|
35847
|
+
const providerMode = config.provider.mode === "cloud" && config.provider.source !== "explicit" && localOnlyRuntimeOptions.length > 0 ? "local" : config.provider.mode;
|
|
35848
|
+
if (config.provider.mode === "cloud" && config.provider.source === "explicit" && localOnlyRuntimeOptions.length > 0) {
|
|
35849
|
+
throw new Error(
|
|
35850
|
+
`provider=cloud does not support local runtime options: ${localOnlyRuntimeOptions.join(", ")}.`
|
|
35851
|
+
);
|
|
35852
|
+
}
|
|
35853
|
+
assertProviderSupportsEngine(providerMode, engine);
|
|
35854
|
+
if (providerMode === "cloud") {
|
|
34114
35855
|
return new CloudSessionProxy(new OpensteerCloudClient(config.cloud), {
|
|
34115
35856
|
...runtimeOptions.rootDir === void 0 ? {} : { rootDir: runtimeOptions.rootDir },
|
|
34116
35857
|
...runtimeOptions.rootPath === void 0 ? {} : { rootPath: runtimeOptions.rootPath },
|
|
@@ -34124,6 +35865,9 @@ function createOpensteerSemanticRuntime(input = {}) {
|
|
|
34124
35865
|
engineName: engine
|
|
34125
35866
|
});
|
|
34126
35867
|
}
|
|
35868
|
+
function listLocalOnlyRuntimeOptions(runtimeOptions) {
|
|
35869
|
+
return LOCAL_ONLY_RUNTIME_OPTION_KEYS.filter((key) => runtimeOptions[key] !== void 0);
|
|
35870
|
+
}
|
|
34127
35871
|
var ENV_FILENAMES = [".env", ".env.local"];
|
|
34128
35872
|
var OPENSTEER_ENV_PREFIX = "OPENSTEER_";
|
|
34129
35873
|
var opensteerEnvironmentCache = /* @__PURE__ */ new Map();
|
|
@@ -34227,6 +35971,6 @@ function isOpensteerEnvironmentKey(key) {
|
|
|
34227
35971
|
return key.startsWith(OPENSTEER_ENV_PREFIX);
|
|
34228
35972
|
}
|
|
34229
35973
|
|
|
34230
|
-
export { CloudSessionProxy, DEFAULT_OPENSTEER_ENGINE, DEFERRED_MATCH_ATTR_KEYS, ElementPathError, MATCH_ATTRIBUTE_PRIORITY, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL, OPENSTEER_ENGINE_NAMES, OPENSTEER_FILESYSTEM_WORKSPACE_LAYOUT, OPENSTEER_FILESYSTEM_WORKSPACE_VERSION, OpensteerAttachAmbiguousError, OpensteerBrowserManager, OpensteerCloudClient, OpensteerRuntime, OpensteerSessionRuntime2 as OpensteerSessionRuntime, STABLE_PRIMARY_ATTR_KEYS, assertProviderSupportsEngine, buildArrayFieldPathCandidates, buildDomDescriptorKey, buildDomDescriptorPayload, buildDomDescriptorVersion, buildPathCandidates, buildPathSelectorHint, buildSegmentSelector, clearPersistedSessionRecord, cloneElementPath, cloneReplayElementPath, cloneStructuralElementAnchor, createArtifactStore, createDomRuntime, createFilesystemOpensteerWorkspace, createObservationStore, createOpensteerExtractionDescriptorStore, createOpensteerSemanticRuntime, defaultFallbackPolicy, defaultPolicy, defaultRetryPolicy, defaultSettlePolicy, defaultTimeoutPolicy, delayWithSignal, discoverLocalCdpBrowsers, dispatchSemanticOperation, hashDomDescriptorDescription, inspectCdpEndpoint, isCurrentUrlField, isProcessRunning, isValidCssAttributeKey, listLocalChromeProfiles, loadEnvironment, manifestToExternalBinaryLocation, normalizeExtractedValue, normalizeObservabilityConfig, normalizeOpensteerEngineName, normalizeOpensteerProviderMode, normalizeWorkspaceId, parseDomDescriptorRecord, parseExtractionDescriptorRecord, pathExists, readPersistedCloudSessionRecord, readPersistedLocalBrowserSessionRecord, readPersistedSessionRecord, resolveCloudConfig, resolveCloudSessionRecordPath, resolveDomActionBridge, resolveExtractedValueInContext, resolveFilesystemWorkspacePath, resolveLiveSessionRecordPath, resolveLocalSessionRecordPath, resolveOpensteerEngineName, resolveOpensteerEnvironment, resolveOpensteerProvider, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath, writePersistedSessionRecord };
|
|
34231
|
-
//# sourceMappingURL=chunk-
|
|
34232
|
-
//# sourceMappingURL=chunk-
|
|
35974
|
+
export { CloudSessionProxy, DEFAULT_OPENSTEER_ENGINE, DEFERRED_MATCH_ATTR_KEYS, ElementPathError, FlowRecorderCollector, MATCH_ATTRIBUTE_PRIORITY, OPENSTEER_DOM_ACTION_BRIDGE_SYMBOL, OPENSTEER_ENGINE_NAMES, OPENSTEER_FILESYSTEM_WORKSPACE_LAYOUT, OPENSTEER_FILESYSTEM_WORKSPACE_VERSION, OpensteerAttachAmbiguousError, OpensteerBrowserManager, OpensteerCloudClient, OpensteerRuntime, OpensteerSessionRuntime2 as OpensteerSessionRuntime, STABLE_PRIMARY_ATTR_KEYS, assertProviderSupportsEngine, buildArrayFieldPathCandidates, buildDomDescriptorKey, buildDomDescriptorPayload, buildDomDescriptorVersion, buildPathCandidates, buildPathSelectorHint, buildSegmentSelector, clearPersistedSessionRecord, cloneElementPath, cloneReplayElementPath, cloneStructuralElementAnchor, createArtifactStore, createDomDescriptorStore, createDomRuntime, createFilesystemOpensteerWorkspace, createObservationStore, createOpensteerExtractionDescriptorStore, createOpensteerSemanticRuntime, defaultFallbackPolicy, defaultPolicy, defaultRetryPolicy, defaultSettlePolicy, defaultTimeoutPolicy, delayWithSignal, discoverLocalCdpBrowsers, dispatchSemanticOperation, generateReplayScript, hashDomDescriptorDescription, inspectCdpEndpoint, isCurrentUrlField, isProcessRunning, isValidCssAttributeKey, listLocalChromeProfiles, loadEnvironment, manifestToExternalBinaryLocation, normalizeExtractedValue, normalizeObservabilityConfig, normalizeOpensteerEngineName, normalizeOpensteerProviderMode, normalizeWorkspaceId, parseDomDescriptorRecord, parseExtractionDescriptorRecord, pathExists, readPersistedCloudSessionRecord, readPersistedLocalBrowserSessionRecord, readPersistedSessionRecord, resolveCloudConfig, resolveCloudSessionRecordPath, resolveDomActionBridge, resolveExtractedValueInContext, resolveFilesystemWorkspacePath, resolveLiveSessionRecordPath, resolveLocalSessionRecordPath, resolveOpensteerEngineName, resolveOpensteerEnvironment, resolveOpensteerProvider, resolveOpensteerRuntimeConfig, runWithPolicyTimeout, sanitizeElementPath, sanitizeReplayElementPath, sanitizeStructuralElementAnchor, settleWithPolicy, shouldKeepAttributeForPath, writePersistedSessionRecord };
|
|
35975
|
+
//# sourceMappingURL=chunk-F3X6UOEN.js.map
|
|
35976
|
+
//# sourceMappingURL=chunk-F3X6UOEN.js.map
|