opensteer 0.8.9 → 0.8.11
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-33FDEOQY.js} +1973 -55
- package/dist/chunk-33FDEOQY.js.map +1 -0
- package/dist/cli/bin.cjs +2474 -169
- package/dist/cli/bin.cjs.map +1 -1
- package/dist/cli/bin.js +398 -10
- package/dist/cli/bin.js.map +1 -1
- package/dist/index.cjs +206 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +82 -20
- package/dist/index.d.ts +82 -20
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- 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
|
@@ -1560,6 +1560,9 @@ function isBrowserCoreError(value) {
|
|
|
1560
1560
|
// ../browser-core/src/cdp-visual-stability.ts
|
|
1561
1561
|
var DEFAULT_VISUAL_STABILITY_SETTLE_MS = 750;
|
|
1562
1562
|
|
|
1563
|
+
// ../browser-core/src/post-load-tracker.ts
|
|
1564
|
+
var DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS = 400;
|
|
1565
|
+
|
|
1563
1566
|
// ../protocol/src/identity.ts
|
|
1564
1567
|
var refPrefixes = [
|
|
1565
1568
|
"session",
|
|
@@ -6749,9 +6752,23 @@ var opensteerInspectStorageInputSchema = objectSchema(
|
|
|
6749
6752
|
title: "OpensteerInspectStorageInput"
|
|
6750
6753
|
}
|
|
6751
6754
|
);
|
|
6755
|
+
var opensteerComputerMouseButtonSchema = enumSchema(["left", "middle", "right"], {
|
|
6756
|
+
title: "OpensteerComputerMouseButton"
|
|
6757
|
+
});
|
|
6758
|
+
var opensteerComputerKeyModifierSchema = enumSchema(
|
|
6759
|
+
["Shift", "Control", "Alt", "Meta"],
|
|
6760
|
+
{
|
|
6761
|
+
title: "OpensteerComputerKeyModifier"
|
|
6762
|
+
}
|
|
6763
|
+
);
|
|
6752
6764
|
var opensteerDomClickInputSchema = objectSchema(
|
|
6753
6765
|
{
|
|
6754
6766
|
target: opensteerTargetInputSchema,
|
|
6767
|
+
button: opensteerComputerMouseButtonSchema,
|
|
6768
|
+
clickCount: integerSchema({ minimum: 1 }),
|
|
6769
|
+
modifiers: arraySchema(opensteerComputerKeyModifierSchema, {
|
|
6770
|
+
uniqueItems: true
|
|
6771
|
+
}),
|
|
6755
6772
|
persistAsDescription: stringSchema(),
|
|
6756
6773
|
captureNetwork: stringSchema({ minLength: 1 })
|
|
6757
6774
|
},
|
|
@@ -6851,18 +6868,6 @@ var opensteerSessionCloseOutputSchema = objectSchema(
|
|
|
6851
6868
|
required: ["closed"]
|
|
6852
6869
|
}
|
|
6853
6870
|
);
|
|
6854
|
-
var opensteerComputerMouseButtonSchema = enumSchema(
|
|
6855
|
-
["left", "middle", "right"],
|
|
6856
|
-
{
|
|
6857
|
-
title: "OpensteerComputerMouseButton"
|
|
6858
|
-
}
|
|
6859
|
-
);
|
|
6860
|
-
var opensteerComputerKeyModifierSchema = enumSchema(
|
|
6861
|
-
["Shift", "Control", "Alt", "Meta"],
|
|
6862
|
-
{
|
|
6863
|
-
title: "OpensteerComputerKeyModifier"
|
|
6864
|
-
}
|
|
6865
|
-
);
|
|
6866
6871
|
var opensteerComputerAnnotationSchema = enumSchema(opensteerComputerAnnotationNames, {
|
|
6867
6872
|
title: "OpensteerComputerAnnotation"
|
|
6868
6873
|
});
|
|
@@ -9053,6 +9058,7 @@ var NAVIGATION_VISUAL_STABILITY_PROFILE = {
|
|
|
9053
9058
|
scope: "visible-frames",
|
|
9054
9059
|
timeoutMs: 7e3
|
|
9055
9060
|
};
|
|
9061
|
+
var NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS = 1e3;
|
|
9056
9062
|
var defaultDomActionSettleObserver = {
|
|
9057
9063
|
async settle(input) {
|
|
9058
9064
|
if (input.trigger !== "dom-action") {
|
|
@@ -9088,6 +9094,13 @@ var defaultNavigationSettleObserver = {
|
|
|
9088
9094
|
return false;
|
|
9089
9095
|
}
|
|
9090
9096
|
try {
|
|
9097
|
+
await input.engine.waitForPostLoadQuiet({
|
|
9098
|
+
pageRef: input.pageRef,
|
|
9099
|
+
timeoutMs: effectiveTimeout,
|
|
9100
|
+
quietMs: DEFAULT_POST_LOAD_TRACKER_QUIET_WINDOW_MS,
|
|
9101
|
+
captureWindowMs: Math.min(NAVIGATION_POST_LOAD_CAPTURE_WINDOW_MS, effectiveTimeout),
|
|
9102
|
+
signal: input.signal
|
|
9103
|
+
});
|
|
9091
9104
|
await input.engine.waitForVisualStability({
|
|
9092
9105
|
pageRef: input.pageRef,
|
|
9093
9106
|
timeoutMs: effectiveTimeout,
|
|
@@ -10799,15 +10812,7 @@ var MemoryDomDescriptorStore = class {
|
|
|
10799
10812
|
// ../runtime-core/src/action-boundary.ts
|
|
10800
10813
|
var actionBoundaryDiagnosticsBySignal = /* @__PURE__ */ new WeakMap();
|
|
10801
10814
|
async function captureActionBoundarySnapshot(engine, pageRef) {
|
|
10802
|
-
|
|
10803
|
-
const mainFrame = frames.find((frame) => frame.isMainFrame);
|
|
10804
|
-
if (!mainFrame) {
|
|
10805
|
-
throw new Error(`page ${pageRef} does not expose a main frame`);
|
|
10806
|
-
}
|
|
10807
|
-
return {
|
|
10808
|
-
pageRef,
|
|
10809
|
-
documentRef: mainFrame.documentRef
|
|
10810
|
-
};
|
|
10815
|
+
return engine.getActionBoundarySnapshot({ pageRef });
|
|
10811
10816
|
}
|
|
10812
10817
|
function createActionBoundaryDiagnostics(input) {
|
|
10813
10818
|
return {
|
|
@@ -14749,7 +14754,7 @@ async function clearPersistedSessionRecord(rootPath, provider) {
|
|
|
14749
14754
|
await rm(resolveLiveSessionRecordPath(rootPath, provider), { force: true });
|
|
14750
14755
|
}
|
|
14751
14756
|
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.
|
|
14757
|
+
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
14758
|
}
|
|
14754
14759
|
function isPersistedLocalBrowserSessionRecord(value) {
|
|
14755
14760
|
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 +22413,11 @@ var SandboxClock = class {
|
|
|
22408
22413
|
performanceNow() {
|
|
22409
22414
|
return this.mode === "manual" ? this.manualNow - this.startedAt : (globalThis.performance?.now() ?? 0) - this.performanceStartedAt;
|
|
22410
22415
|
}
|
|
22411
|
-
setTimeout(callback,
|
|
22412
|
-
return this.registerTimer(false, callback,
|
|
22416
|
+
setTimeout(callback, delay3 = 0, ...args) {
|
|
22417
|
+
return this.registerTimer(false, callback, delay3, args);
|
|
22413
22418
|
}
|
|
22414
|
-
setInterval(callback,
|
|
22415
|
-
return this.registerTimer(true, callback,
|
|
22419
|
+
setInterval(callback, delay3 = 0, ...args) {
|
|
22420
|
+
return this.registerTimer(true, callback, delay3, args);
|
|
22416
22421
|
}
|
|
22417
22422
|
clearTimeout(timerId) {
|
|
22418
22423
|
this.clearTimer(timerId);
|
|
@@ -22433,9 +22438,9 @@ var SandboxClock = class {
|
|
|
22433
22438
|
this.clearTimer(timerId);
|
|
22434
22439
|
}
|
|
22435
22440
|
}
|
|
22436
|
-
registerTimer(repeat, callback,
|
|
22441
|
+
registerTimer(repeat, callback, delay3, args) {
|
|
22437
22442
|
const timerId = this.nextTimerId++;
|
|
22438
|
-
const normalizedDelay = Math.max(0,
|
|
22443
|
+
const normalizedDelay = Math.max(0, delay3);
|
|
22439
22444
|
const record = {
|
|
22440
22445
|
callback,
|
|
22441
22446
|
args,
|
|
@@ -22942,6 +22947,7 @@ function diffInteractionTraces(left, right) {
|
|
|
22942
22947
|
// ../runtime-core/src/sdk/runtime.ts
|
|
22943
22948
|
var requireForAuthRecipeHook = createRequire(import.meta.url);
|
|
22944
22949
|
var MUTATION_CAPTURE_FINALIZE_TIMEOUT_MS = 5e3;
|
|
22950
|
+
var PERSISTED_NETWORK_FLUSH_TIMEOUT_MS = 5e3;
|
|
22945
22951
|
var PENDING_OPERATION_EVENT_CAPTURE_LIMIT = 64;
|
|
22946
22952
|
var PENDING_OPERATION_EVENT_CAPTURE_SKEW_MS = 1e3;
|
|
22947
22953
|
var OpensteerSessionRuntime = class {
|
|
@@ -23605,6 +23611,9 @@ var OpensteerSessionRuntime = class {
|
|
|
23605
23611
|
const result = await this.requireDom().click({
|
|
23606
23612
|
pageRef,
|
|
23607
23613
|
target,
|
|
23614
|
+
...input.button === void 0 ? {} : { button: input.button },
|
|
23615
|
+
...input.clickCount === void 0 ? {} : { clickCount: input.clickCount },
|
|
23616
|
+
...input.modifiers === void 0 ? {} : { modifiers: input.modifiers },
|
|
23608
23617
|
timeout
|
|
23609
23618
|
});
|
|
23610
23619
|
return {
|
|
@@ -29081,6 +29090,29 @@ var OpensteerSessionRuntime = class {
|
|
|
29081
29090
|
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
29091
|
}
|
|
29083
29092
|
async flushPersistedNetworkHistory() {
|
|
29093
|
+
if (this.sessionRef === void 0) {
|
|
29094
|
+
return;
|
|
29095
|
+
}
|
|
29096
|
+
const root = await this.ensureRoot();
|
|
29097
|
+
try {
|
|
29098
|
+
await withDetachedTimeoutSignal(PERSISTED_NETWORK_FLUSH_TIMEOUT_MS, async (signal) => {
|
|
29099
|
+
const browserRecords = await this.readBrowserNetworkRecords(
|
|
29100
|
+
{
|
|
29101
|
+
includeBodies: true,
|
|
29102
|
+
includeCurrentPageOnly: false
|
|
29103
|
+
},
|
|
29104
|
+
signal
|
|
29105
|
+
);
|
|
29106
|
+
await this.networkHistory.persist(browserRecords, root.registry.savedNetwork, {
|
|
29107
|
+
bodyWriteMode: "authoritative",
|
|
29108
|
+
redactSecretHeaders: false
|
|
29109
|
+
});
|
|
29110
|
+
});
|
|
29111
|
+
} catch (error) {
|
|
29112
|
+
if (!isIgnorableRuntimeBindingError(error)) {
|
|
29113
|
+
throw error;
|
|
29114
|
+
}
|
|
29115
|
+
}
|
|
29084
29116
|
}
|
|
29085
29117
|
toDomTargetRef(target) {
|
|
29086
29118
|
if (target.kind === "description") {
|
|
@@ -29237,6 +29269,12 @@ var OpensteerSessionRuntime = class {
|
|
|
29237
29269
|
return "live";
|
|
29238
29270
|
} catch (error) {
|
|
29239
29271
|
if (isIgnorableRuntimeBindingError(error)) {
|
|
29272
|
+
const remainingPages = await engine.listPages({ sessionRef }).catch(() => void 0);
|
|
29273
|
+
const replacementPageRef = remainingPages?.[0]?.pageRef;
|
|
29274
|
+
if (replacementPageRef !== void 0) {
|
|
29275
|
+
this.pageRef = replacementPageRef;
|
|
29276
|
+
return "live";
|
|
29277
|
+
}
|
|
29240
29278
|
return "invalid";
|
|
29241
29279
|
}
|
|
29242
29280
|
throw error;
|
|
@@ -32291,6 +32329,1770 @@ function screenshotMediaType(format2) {
|
|
|
32291
32329
|
}
|
|
32292
32330
|
}
|
|
32293
32331
|
|
|
32332
|
+
// ../runtime-core/src/recorder/browser-scripts.ts
|
|
32333
|
+
var SINGLE_ATTRIBUTE_PRIORITY = Array.from(
|
|
32334
|
+
/* @__PURE__ */ new Set(["data-testid", "data-test", "data-qa", "data-cy", "id", ...STABLE_PRIMARY_ATTR_KEYS])
|
|
32335
|
+
);
|
|
32336
|
+
function createFlowRecorderInstallScript(options = {}) {
|
|
32337
|
+
const normalizedOptions = {
|
|
32338
|
+
showStopButton: options.showStopButton ?? true
|
|
32339
|
+
};
|
|
32340
|
+
return String.raw`(() => {
|
|
32341
|
+
const TOP_LEVEL_ONLY = (() => {
|
|
32342
|
+
try {
|
|
32343
|
+
return window.top === window.self;
|
|
32344
|
+
} catch {
|
|
32345
|
+
return false;
|
|
32346
|
+
}
|
|
32347
|
+
})();
|
|
32348
|
+
if (!TOP_LEVEL_ONLY) {
|
|
32349
|
+
return;
|
|
32350
|
+
}
|
|
32351
|
+
|
|
32352
|
+
const globalScope = globalThis;
|
|
32353
|
+
const recorderKey = "__opensteerFlowRecorder";
|
|
32354
|
+
const historyStateKey = "__opensteerFlowRecorderHistory";
|
|
32355
|
+
const recorderUiAttribute = "data-opensteer-recorder-ui";
|
|
32356
|
+
const queueLimit = 1000;
|
|
32357
|
+
const singleAttributePriority = ${JSON.stringify(SINGLE_ATTRIBUTE_PRIORITY)};
|
|
32358
|
+
const stablePrimaryAttrKeys = new Set(${JSON.stringify([...STABLE_PRIMARY_ATTR_KEYS])});
|
|
32359
|
+
const matchAttributePriority = ${JSON.stringify([...MATCH_ATTRIBUTE_PRIORITY])};
|
|
32360
|
+
const attributeDenyKeys = new Set(${JSON.stringify([...ATTRIBUTE_DENY_KEYS])});
|
|
32361
|
+
const lazyLoadingMediaTags = new Set(${JSON.stringify([...LAZY_LOADING_MEDIA_TAGS])});
|
|
32362
|
+
const volatileLazyLoadingAttrs = new Set(${JSON.stringify([...VOLATILE_LAZY_LOADING_ATTRS])});
|
|
32363
|
+
const volatileClassTokens = new Set(${JSON.stringify([...VOLATILE_CLASS_TOKENS])});
|
|
32364
|
+
const volatileLazyClassTokens = new Set(${JSON.stringify([...VOLATILE_LAZY_CLASS_TOKENS])});
|
|
32365
|
+
const options = ${JSON.stringify(normalizedOptions)};
|
|
32366
|
+
|
|
32367
|
+
const previous = globalScope[recorderKey];
|
|
32368
|
+
if (previous && typeof previous.dispose === "function") {
|
|
32369
|
+
previous.dispose();
|
|
32370
|
+
}
|
|
32371
|
+
|
|
32372
|
+
const queue = [];
|
|
32373
|
+
const cleanup = [];
|
|
32374
|
+
const inputFlushTimers = new Map();
|
|
32375
|
+
const pendingInputs = new Map();
|
|
32376
|
+
let historyStateCache = undefined;
|
|
32377
|
+
let stopRequested = false;
|
|
32378
|
+
let pendingWheel = undefined;
|
|
32379
|
+
|
|
32380
|
+
const actionTargetTags = new Set([
|
|
32381
|
+
"a",
|
|
32382
|
+
"button",
|
|
32383
|
+
"input",
|
|
32384
|
+
"label",
|
|
32385
|
+
"option",
|
|
32386
|
+
"select",
|
|
32387
|
+
"summary",
|
|
32388
|
+
"textarea",
|
|
32389
|
+
]);
|
|
32390
|
+
|
|
32391
|
+
function now() {
|
|
32392
|
+
return Date.now();
|
|
32393
|
+
}
|
|
32394
|
+
|
|
32395
|
+
function enqueue(entry) {
|
|
32396
|
+
queue.push({
|
|
32397
|
+
...entry,
|
|
32398
|
+
timestamp: typeof entry.timestamp === "number" ? entry.timestamp : now(),
|
|
32399
|
+
});
|
|
32400
|
+
if (queue.length > queueLimit) {
|
|
32401
|
+
queue.splice(0, queue.length - queueLimit);
|
|
32402
|
+
}
|
|
32403
|
+
}
|
|
32404
|
+
|
|
32405
|
+
function isRecorderUiNode(node) {
|
|
32406
|
+
let current = node instanceof Node ? node : null;
|
|
32407
|
+
while (current) {
|
|
32408
|
+
if (current instanceof Element && current.hasAttribute(recorderUiAttribute)) {
|
|
32409
|
+
return true;
|
|
32410
|
+
}
|
|
32411
|
+
const root = typeof current.getRootNode === "function" ? current.getRootNode() : null;
|
|
32412
|
+
if (root instanceof ShadowRoot) {
|
|
32413
|
+
current = root.host;
|
|
32414
|
+
continue;
|
|
32415
|
+
}
|
|
32416
|
+
current = current instanceof Element ? current.parentElement : null;
|
|
32417
|
+
}
|
|
32418
|
+
return false;
|
|
32419
|
+
}
|
|
32420
|
+
|
|
32421
|
+
function isValidAttributeName(name) {
|
|
32422
|
+
if (typeof name !== "string") {
|
|
32423
|
+
return false;
|
|
32424
|
+
}
|
|
32425
|
+
const normalized = name.trim();
|
|
32426
|
+
if (normalized.length === 0) {
|
|
32427
|
+
return false;
|
|
32428
|
+
}
|
|
32429
|
+
if (/[\s"'<>/]/.test(normalized)) {
|
|
32430
|
+
return false;
|
|
32431
|
+
}
|
|
32432
|
+
return /^[A-Za-z_][A-Za-z0-9_:\-.]*$/.test(normalized);
|
|
32433
|
+
}
|
|
32434
|
+
|
|
32435
|
+
function escapeAttributeValue(value) {
|
|
32436
|
+
return String(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
32437
|
+
}
|
|
32438
|
+
|
|
32439
|
+
function escapeIdentifier(value) {
|
|
32440
|
+
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
|
|
32441
|
+
return CSS.escape(value);
|
|
32442
|
+
}
|
|
32443
|
+
return String(value).replace(/[^A-Za-z0-9_-]/g, (character) => {
|
|
32444
|
+
const codePoint = character.codePointAt(0);
|
|
32445
|
+
return "\\" + (codePoint == null ? "" : codePoint.toString(16)) + " ";
|
|
32446
|
+
});
|
|
32447
|
+
}
|
|
32448
|
+
|
|
32449
|
+
function normalizeClassValue(element, rawValue) {
|
|
32450
|
+
const tag = element.tagName.toLowerCase();
|
|
32451
|
+
const tokens = String(rawValue)
|
|
32452
|
+
.split(/\s+/u)
|
|
32453
|
+
.map((token) => token.trim())
|
|
32454
|
+
.filter(Boolean)
|
|
32455
|
+
.filter((token) => !volatileClassTokens.has(token))
|
|
32456
|
+
.filter((token) => !(lazyLoadingMediaTags.has(tag) && volatileLazyClassTokens.has(token)));
|
|
32457
|
+
return tokens.join(" ");
|
|
32458
|
+
}
|
|
32459
|
+
|
|
32460
|
+
function shouldKeepAttribute(element, name, value) {
|
|
32461
|
+
const key = String(name || "")
|
|
32462
|
+
.trim()
|
|
32463
|
+
.toLowerCase();
|
|
32464
|
+
if (!key || !String(value || "").trim()) {
|
|
32465
|
+
return false;
|
|
32466
|
+
}
|
|
32467
|
+
if (!isValidAttributeName(key)) {
|
|
32468
|
+
return false;
|
|
32469
|
+
}
|
|
32470
|
+
if (key === "c") {
|
|
32471
|
+
return false;
|
|
32472
|
+
}
|
|
32473
|
+
if (/^on[a-z]/i.test(key)) {
|
|
32474
|
+
return false;
|
|
32475
|
+
}
|
|
32476
|
+
if (attributeDenyKeys.has(key)) {
|
|
32477
|
+
return false;
|
|
32478
|
+
}
|
|
32479
|
+
if (key.startsWith("data-os-") || key.startsWith("data-opensteer-")) {
|
|
32480
|
+
return false;
|
|
32481
|
+
}
|
|
32482
|
+
if (lazyLoadingMediaTags.has(element.tagName.toLowerCase()) && volatileLazyLoadingAttrs.has(key)) {
|
|
32483
|
+
return false;
|
|
32484
|
+
}
|
|
32485
|
+
return true;
|
|
32486
|
+
}
|
|
32487
|
+
|
|
32488
|
+
function readAttributeValue(element, key) {
|
|
32489
|
+
if (key === "class") {
|
|
32490
|
+
const normalized = normalizeClassValue(element, element.getAttribute("class") || "");
|
|
32491
|
+
return normalized.length === 0 ? undefined : normalized;
|
|
32492
|
+
}
|
|
32493
|
+
const value = element.getAttribute(key);
|
|
32494
|
+
if (!shouldKeepAttribute(element, key, value || "")) {
|
|
32495
|
+
return undefined;
|
|
32496
|
+
}
|
|
32497
|
+
return value || undefined;
|
|
32498
|
+
}
|
|
32499
|
+
|
|
32500
|
+
function buildSingleAttributeSelector(element, key, value) {
|
|
32501
|
+
if (!value) {
|
|
32502
|
+
return undefined;
|
|
32503
|
+
}
|
|
32504
|
+
const tag = element.tagName.toLowerCase();
|
|
32505
|
+
if (key === "id") {
|
|
32506
|
+
const idSelector = "#" + escapeIdentifier(value);
|
|
32507
|
+
return element.matches(idSelector)
|
|
32508
|
+
? idSelector
|
|
32509
|
+
: tag + '[id="' + escapeAttributeValue(value) + '"]';
|
|
32510
|
+
}
|
|
32511
|
+
return tag + "[" + key + '="' + escapeAttributeValue(value) + '"]';
|
|
32512
|
+
}
|
|
32513
|
+
|
|
32514
|
+
function isUniqueSelector(selector, element) {
|
|
32515
|
+
if (!selector) {
|
|
32516
|
+
return false;
|
|
32517
|
+
}
|
|
32518
|
+
let matches;
|
|
32519
|
+
try {
|
|
32520
|
+
matches = document.querySelectorAll(selector);
|
|
32521
|
+
} catch {
|
|
32522
|
+
return false;
|
|
32523
|
+
}
|
|
32524
|
+
return matches.length === 1 && matches[0] === element;
|
|
32525
|
+
}
|
|
32526
|
+
|
|
32527
|
+
function nearestRecordTarget(node) {
|
|
32528
|
+
if (isRecorderUiNode(node)) {
|
|
32529
|
+
return null;
|
|
32530
|
+
}
|
|
32531
|
+
let current = node instanceof Element ? node : null;
|
|
32532
|
+
while (current) {
|
|
32533
|
+
if (current.hasAttribute(recorderUiAttribute)) {
|
|
32534
|
+
return null;
|
|
32535
|
+
}
|
|
32536
|
+
const tag = current.tagName.toLowerCase();
|
|
32537
|
+
if (
|
|
32538
|
+
actionTargetTags.has(tag) ||
|
|
32539
|
+
current.hasAttribute("data-testid") ||
|
|
32540
|
+
current.hasAttribute("data-test") ||
|
|
32541
|
+
current.hasAttribute("data-qa") ||
|
|
32542
|
+
current.hasAttribute("data-cy") ||
|
|
32543
|
+
current.hasAttribute("role") ||
|
|
32544
|
+
current.hasAttribute("aria-label") ||
|
|
32545
|
+
current.hasAttribute("name")
|
|
32546
|
+
) {
|
|
32547
|
+
return current;
|
|
32548
|
+
}
|
|
32549
|
+
current = current.parentElement;
|
|
32550
|
+
}
|
|
32551
|
+
return node instanceof Element ? node : null;
|
|
32552
|
+
}
|
|
32553
|
+
|
|
32554
|
+
function buildSegmentSelector(element) {
|
|
32555
|
+
const tag = element.tagName.toLowerCase();
|
|
32556
|
+
for (const key of matchAttributePriority) {
|
|
32557
|
+
const value = readAttributeValue(element, key);
|
|
32558
|
+
if (!value) {
|
|
32559
|
+
continue;
|
|
32560
|
+
}
|
|
32561
|
+
if (key === "class") {
|
|
32562
|
+
const tokens = value
|
|
32563
|
+
.split(/\s+/u)
|
|
32564
|
+
.map((token) => token.trim())
|
|
32565
|
+
.filter(Boolean)
|
|
32566
|
+
.slice(0, 2);
|
|
32567
|
+
if (tokens.length === 0) {
|
|
32568
|
+
continue;
|
|
32569
|
+
}
|
|
32570
|
+
return tag + tokens.map((token) => "." + escapeIdentifier(token)).join("");
|
|
32571
|
+
}
|
|
32572
|
+
return key === "id"
|
|
32573
|
+
? tag + '[id="' + escapeAttributeValue(value) + '"]'
|
|
32574
|
+
: tag + "[" + key + '="' + escapeAttributeValue(value) + '"]';
|
|
32575
|
+
}
|
|
32576
|
+
return tag;
|
|
32577
|
+
}
|
|
32578
|
+
|
|
32579
|
+
function nthOfTypeSegment(element, baseSelector) {
|
|
32580
|
+
const parent = element.parentElement;
|
|
32581
|
+
if (!parent) {
|
|
32582
|
+
return baseSelector;
|
|
32583
|
+
}
|
|
32584
|
+
const sameType = Array.from(parent.children).filter(
|
|
32585
|
+
(child) => child.tagName === element.tagName,
|
|
32586
|
+
);
|
|
32587
|
+
if (sameType.length <= 1) {
|
|
32588
|
+
return baseSelector;
|
|
32589
|
+
}
|
|
32590
|
+
const index = sameType.indexOf(element) + 1;
|
|
32591
|
+
return baseSelector + ":nth-of-type(" + String(index) + ")";
|
|
32592
|
+
}
|
|
32593
|
+
|
|
32594
|
+
function buildSelector(node) {
|
|
32595
|
+
const element = nearestRecordTarget(node);
|
|
32596
|
+
if (!(element instanceof Element)) {
|
|
32597
|
+
return undefined;
|
|
32598
|
+
}
|
|
32599
|
+
|
|
32600
|
+
for (const key of singleAttributePriority) {
|
|
32601
|
+
const value = readAttributeValue(element, key);
|
|
32602
|
+
const selector = buildSingleAttributeSelector(element, key, value);
|
|
32603
|
+
if (selector && isUniqueSelector(selector, element)) {
|
|
32604
|
+
return selector;
|
|
32605
|
+
}
|
|
32606
|
+
if (value && !stablePrimaryAttrKeys.has(key) && key !== "id") {
|
|
32607
|
+
const tagQualified =
|
|
32608
|
+
element.tagName.toLowerCase() + "[" + key + '="' + escapeAttributeValue(value) + '"]';
|
|
32609
|
+
if (isUniqueSelector(tagQualified, element)) {
|
|
32610
|
+
return tagQualified;
|
|
32611
|
+
}
|
|
32612
|
+
}
|
|
32613
|
+
}
|
|
32614
|
+
|
|
32615
|
+
const segments = [];
|
|
32616
|
+
let current = element;
|
|
32617
|
+
let depth = 0;
|
|
32618
|
+
while (current && depth < 6) {
|
|
32619
|
+
const segment = nthOfTypeSegment(current, buildSegmentSelector(current));
|
|
32620
|
+
segments.unshift(segment);
|
|
32621
|
+
const selector = segments.join(" > ");
|
|
32622
|
+
if (isUniqueSelector(selector, element)) {
|
|
32623
|
+
return selector;
|
|
32624
|
+
}
|
|
32625
|
+
current = current.parentElement;
|
|
32626
|
+
depth += 1;
|
|
32627
|
+
}
|
|
32628
|
+
|
|
32629
|
+
const fallback = segments.join(" > ");
|
|
32630
|
+
return fallback.length > 0 ? fallback : element.tagName.toLowerCase();
|
|
32631
|
+
}
|
|
32632
|
+
|
|
32633
|
+
function readTargetValue(target) {
|
|
32634
|
+
if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) {
|
|
32635
|
+
return target.value;
|
|
32636
|
+
}
|
|
32637
|
+
if (target instanceof HTMLSelectElement) {
|
|
32638
|
+
return target.value;
|
|
32639
|
+
}
|
|
32640
|
+
if (target instanceof HTMLElement && target.isContentEditable) {
|
|
32641
|
+
return target.textContent || "";
|
|
32642
|
+
}
|
|
32643
|
+
return undefined;
|
|
32644
|
+
}
|
|
32645
|
+
|
|
32646
|
+
function flushPendingInput(selector) {
|
|
32647
|
+
const pending = pendingInputs.get(selector);
|
|
32648
|
+
if (!pending) {
|
|
32649
|
+
return;
|
|
32650
|
+
}
|
|
32651
|
+
pendingInputs.delete(selector);
|
|
32652
|
+
const timer = inputFlushTimers.get(selector);
|
|
32653
|
+
if (timer !== undefined) {
|
|
32654
|
+
clearTimeout(timer);
|
|
32655
|
+
inputFlushTimers.delete(selector);
|
|
32656
|
+
}
|
|
32657
|
+
enqueue({
|
|
32658
|
+
kind: "type",
|
|
32659
|
+
selector,
|
|
32660
|
+
text: pending.text,
|
|
32661
|
+
timestamp: pending.timestamp,
|
|
32662
|
+
});
|
|
32663
|
+
}
|
|
32664
|
+
|
|
32665
|
+
function flushAllInputs() {
|
|
32666
|
+
for (const selector of Array.from(pendingInputs.keys())) {
|
|
32667
|
+
flushPendingInput(selector);
|
|
32668
|
+
}
|
|
32669
|
+
}
|
|
32670
|
+
|
|
32671
|
+
function flushPendingWheel() {
|
|
32672
|
+
if (!pendingWheel) {
|
|
32673
|
+
return;
|
|
32674
|
+
}
|
|
32675
|
+
clearTimeout(pendingWheel.timerId);
|
|
32676
|
+
enqueue({
|
|
32677
|
+
kind: "scroll",
|
|
32678
|
+
selector: pendingWheel.selector,
|
|
32679
|
+
deltaX: pendingWheel.deltaX,
|
|
32680
|
+
deltaY: pendingWheel.deltaY,
|
|
32681
|
+
timestamp: pendingWheel.timestamp,
|
|
32682
|
+
});
|
|
32683
|
+
pendingWheel = undefined;
|
|
32684
|
+
}
|
|
32685
|
+
|
|
32686
|
+
function scheduleInputFlush(selector) {
|
|
32687
|
+
const existing = inputFlushTimers.get(selector);
|
|
32688
|
+
if (existing !== undefined) {
|
|
32689
|
+
clearTimeout(existing);
|
|
32690
|
+
}
|
|
32691
|
+
const timerId = setTimeout(() => {
|
|
32692
|
+
flushPendingInput(selector);
|
|
32693
|
+
}, 400);
|
|
32694
|
+
inputFlushTimers.set(selector, timerId);
|
|
32695
|
+
}
|
|
32696
|
+
|
|
32697
|
+
function createDefaultHistoryState(currentUrl) {
|
|
32698
|
+
return {
|
|
32699
|
+
entries: [currentUrl],
|
|
32700
|
+
index: 0,
|
|
32701
|
+
};
|
|
32702
|
+
}
|
|
32703
|
+
|
|
32704
|
+
function normalizeHistoryState(state) {
|
|
32705
|
+
if (
|
|
32706
|
+
!state ||
|
|
32707
|
+
!Array.isArray(state.entries) ||
|
|
32708
|
+
!state.entries.every((entry) => typeof entry === "string") ||
|
|
32709
|
+
!Number.isInteger(state.index)
|
|
32710
|
+
) {
|
|
32711
|
+
return undefined;
|
|
32712
|
+
}
|
|
32713
|
+
const entries = state.entries.slice();
|
|
32714
|
+
if (entries.length === 0) {
|
|
32715
|
+
return createDefaultHistoryState(location.href);
|
|
32716
|
+
}
|
|
32717
|
+
return {
|
|
32718
|
+
entries,
|
|
32719
|
+
index: Math.min(entries.length - 1, Math.max(0, state.index)),
|
|
32720
|
+
};
|
|
32721
|
+
}
|
|
32722
|
+
|
|
32723
|
+
function writeHistoryState(state) {
|
|
32724
|
+
const normalized = normalizeHistoryState(state) ?? createDefaultHistoryState(location.href);
|
|
32725
|
+
historyStateCache = normalized;
|
|
32726
|
+
try {
|
|
32727
|
+
sessionStorage.setItem(historyStateKey, JSON.stringify(normalized));
|
|
32728
|
+
} catch {}
|
|
32729
|
+
return normalized;
|
|
32730
|
+
}
|
|
32731
|
+
|
|
32732
|
+
function applyHistoryState(state, mode, nextUrl) {
|
|
32733
|
+
const current = normalizeHistoryState(state) ?? createDefaultHistoryState(location.href);
|
|
32734
|
+
const nextState = {
|
|
32735
|
+
entries: current.entries.slice(),
|
|
32736
|
+
index: current.index,
|
|
32737
|
+
};
|
|
32738
|
+
if (mode === "replace") {
|
|
32739
|
+
nextState.entries[nextState.index] = nextUrl;
|
|
32740
|
+
} else if (mode === "push") {
|
|
32741
|
+
nextState.entries = nextState.entries.slice(0, nextState.index + 1);
|
|
32742
|
+
nextState.entries.push(nextUrl);
|
|
32743
|
+
nextState.index = nextState.entries.length - 1;
|
|
32744
|
+
} else if (mode === "back") {
|
|
32745
|
+
nextState.index = Math.max(0, nextState.index - 1);
|
|
32746
|
+
} else if (mode === "forward") {
|
|
32747
|
+
nextState.index = Math.min(nextState.entries.length - 1, nextState.index + 1);
|
|
32748
|
+
}
|
|
32749
|
+
return nextState;
|
|
32750
|
+
}
|
|
32751
|
+
|
|
32752
|
+
function updateHistoryState(mode, nextUrl) {
|
|
32753
|
+
return writeHistoryState(applyHistoryState(readHistoryState(), mode, nextUrl));
|
|
32754
|
+
}
|
|
32755
|
+
|
|
32756
|
+
function readHistoryState() {
|
|
32757
|
+
const cached = normalizeHistoryState(historyStateCache);
|
|
32758
|
+
if (cached) {
|
|
32759
|
+
historyStateCache = cached;
|
|
32760
|
+
return cached;
|
|
32761
|
+
}
|
|
32762
|
+
try {
|
|
32763
|
+
const raw = sessionStorage.getItem(historyStateKey);
|
|
32764
|
+
const parsed = normalizeHistoryState(raw ? JSON.parse(raw) : undefined);
|
|
32765
|
+
if (parsed) {
|
|
32766
|
+
historyStateCache = parsed;
|
|
32767
|
+
return parsed;
|
|
32768
|
+
}
|
|
32769
|
+
} catch {}
|
|
32770
|
+
return undefined;
|
|
32771
|
+
}
|
|
32772
|
+
|
|
32773
|
+
function classifyHistoryTraversal(currentUrl) {
|
|
32774
|
+
const state = readHistoryState();
|
|
32775
|
+
if (!state) {
|
|
32776
|
+
return undefined;
|
|
32777
|
+
}
|
|
32778
|
+
if (state.entries[state.index] === currentUrl) {
|
|
32779
|
+
return undefined;
|
|
32780
|
+
}
|
|
32781
|
+
if (state.entries[state.index - 1] === currentUrl) {
|
|
32782
|
+
writeHistoryState({
|
|
32783
|
+
entries: state.entries,
|
|
32784
|
+
index: state.index - 1,
|
|
32785
|
+
});
|
|
32786
|
+
return "go-back";
|
|
32787
|
+
}
|
|
32788
|
+
if (state.entries[state.index + 1] === currentUrl) {
|
|
32789
|
+
writeHistoryState({
|
|
32790
|
+
entries: state.entries,
|
|
32791
|
+
index: state.index + 1,
|
|
32792
|
+
});
|
|
32793
|
+
return "go-forward";
|
|
32794
|
+
}
|
|
32795
|
+
const existingIndex = state.entries.lastIndexOf(currentUrl);
|
|
32796
|
+
if (existingIndex !== -1 && existingIndex !== state.index) {
|
|
32797
|
+
writeHistoryState({
|
|
32798
|
+
entries: state.entries,
|
|
32799
|
+
index: existingIndex,
|
|
32800
|
+
});
|
|
32801
|
+
return existingIndex < state.index ? "go-back" : "go-forward";
|
|
32802
|
+
}
|
|
32803
|
+
return undefined;
|
|
32804
|
+
}
|
|
32805
|
+
|
|
32806
|
+
function onInstall() {
|
|
32807
|
+
const currentUrl = location.href;
|
|
32808
|
+
const navigationEntry =
|
|
32809
|
+
typeof performance.getEntriesByType === "function"
|
|
32810
|
+
? performance.getEntriesByType("navigation")[0]
|
|
32811
|
+
: undefined;
|
|
32812
|
+
const navigationType =
|
|
32813
|
+
navigationEntry && typeof navigationEntry.type === "string" ? navigationEntry.type : undefined;
|
|
32814
|
+
const existingState = readHistoryState();
|
|
32815
|
+
|
|
32816
|
+
if (!existingState) {
|
|
32817
|
+
updateHistoryState("replace", currentUrl);
|
|
32818
|
+
return;
|
|
32819
|
+
}
|
|
32820
|
+
|
|
32821
|
+
if (navigationType === "reload") {
|
|
32822
|
+
updateHistoryState("replace", currentUrl);
|
|
32823
|
+
enqueue({
|
|
32824
|
+
kind: "reload",
|
|
32825
|
+
url: currentUrl,
|
|
32826
|
+
});
|
|
32827
|
+
return;
|
|
32828
|
+
}
|
|
32829
|
+
|
|
32830
|
+
if (navigationType === "back_forward") {
|
|
32831
|
+
const traversal = classifyHistoryTraversal(currentUrl);
|
|
32832
|
+
if (traversal === "go-back" || traversal === "go-forward") {
|
|
32833
|
+
enqueue({
|
|
32834
|
+
kind: traversal,
|
|
32835
|
+
url: currentUrl,
|
|
32836
|
+
});
|
|
32837
|
+
return;
|
|
32838
|
+
}
|
|
32839
|
+
}
|
|
32840
|
+
|
|
32841
|
+
if (existingState.entries[existingState.index] !== currentUrl) {
|
|
32842
|
+
updateHistoryState("push", currentUrl);
|
|
32843
|
+
enqueue({
|
|
32844
|
+
kind: "navigate",
|
|
32845
|
+
url: currentUrl,
|
|
32846
|
+
source: "full-navigation",
|
|
32847
|
+
});
|
|
32848
|
+
}
|
|
32849
|
+
}
|
|
32850
|
+
|
|
32851
|
+
function modifierKeys(event) {
|
|
32852
|
+
const modifiers = [];
|
|
32853
|
+
if (event.altKey) {
|
|
32854
|
+
modifiers.push("Alt");
|
|
32855
|
+
}
|
|
32856
|
+
if (event.ctrlKey) {
|
|
32857
|
+
modifiers.push("Control");
|
|
32858
|
+
}
|
|
32859
|
+
if (event.metaKey) {
|
|
32860
|
+
modifiers.push("Meta");
|
|
32861
|
+
}
|
|
32862
|
+
if (event.shiftKey) {
|
|
32863
|
+
modifiers.push("Shift");
|
|
32864
|
+
}
|
|
32865
|
+
return modifiers;
|
|
32866
|
+
}
|
|
32867
|
+
|
|
32868
|
+
function addListener(target, type, listener, options) {
|
|
32869
|
+
target.addEventListener(type, listener, options);
|
|
32870
|
+
cleanup.push(() => {
|
|
32871
|
+
target.removeEventListener(type, listener, options);
|
|
32872
|
+
});
|
|
32873
|
+
}
|
|
32874
|
+
|
|
32875
|
+
function requestStop() {
|
|
32876
|
+
stopRequested = true;
|
|
32877
|
+
}
|
|
32878
|
+
|
|
32879
|
+
function mountStopButton() {
|
|
32880
|
+
if (document.querySelector("[" + recorderUiAttribute + "]")) {
|
|
32881
|
+
return true;
|
|
32882
|
+
}
|
|
32883
|
+
if (stopRequested) {
|
|
32884
|
+
return true;
|
|
32885
|
+
}
|
|
32886
|
+
const parent = document.body || document.documentElement;
|
|
32887
|
+
if (!(parent instanceof HTMLElement)) {
|
|
32888
|
+
return false;
|
|
32889
|
+
}
|
|
32890
|
+
|
|
32891
|
+
const host = document.createElement("div");
|
|
32892
|
+
host.setAttribute(recorderUiAttribute, "true");
|
|
32893
|
+
const shadowRoot = host.attachShadow({ mode: "open" });
|
|
32894
|
+
const style = document.createElement("style");
|
|
32895
|
+
style.textContent = [
|
|
32896
|
+
":host {",
|
|
32897
|
+
" all: initial;",
|
|
32898
|
+
" display: block;",
|
|
32899
|
+
" position: fixed;",
|
|
32900
|
+
" top: 16px;",
|
|
32901
|
+
" right: 16px;",
|
|
32902
|
+
" z-index: 2147483647;",
|
|
32903
|
+
" pointer-events: auto;",
|
|
32904
|
+
" color-scheme: only light;",
|
|
32905
|
+
" contain: content;",
|
|
32906
|
+
" isolation: isolate;",
|
|
32907
|
+
"}",
|
|
32908
|
+
"button {",
|
|
32909
|
+
" appearance: none;",
|
|
32910
|
+
" border: 1px solid rgba(15, 23, 42, 0.2);",
|
|
32911
|
+
" border-radius: 999px;",
|
|
32912
|
+
" background: rgba(15, 23, 42, 0.92);",
|
|
32913
|
+
" color: #ffffff;",
|
|
32914
|
+
" color-scheme: only light;",
|
|
32915
|
+
" cursor: pointer;",
|
|
32916
|
+
" font: 600 12px/1.2 ui-sans-serif, system-ui, sans-serif;",
|
|
32917
|
+
" letter-spacing: 0.01em;",
|
|
32918
|
+
" padding: 10px 14px;",
|
|
32919
|
+
" box-shadow: 0 10px 30px rgba(15, 23, 42, 0.22);",
|
|
32920
|
+
"}",
|
|
32921
|
+
"button:hover {",
|
|
32922
|
+
" background: rgba(15, 23, 42, 0.98);",
|
|
32923
|
+
"}",
|
|
32924
|
+
"button:focus-visible {",
|
|
32925
|
+
" outline: 2px solid #2563eb;",
|
|
32926
|
+
" outline-offset: 2px;",
|
|
32927
|
+
"}",
|
|
32928
|
+
].join("\n");
|
|
32929
|
+
const button = document.createElement("button");
|
|
32930
|
+
button.type = "button";
|
|
32931
|
+
button.textContent = "Stop recording";
|
|
32932
|
+
button.setAttribute("aria-label", "Stop recording");
|
|
32933
|
+
const consumePointerEvent = (event) => {
|
|
32934
|
+
event.preventDefault();
|
|
32935
|
+
event.stopPropagation();
|
|
32936
|
+
};
|
|
32937
|
+
for (const type of ["pointerdown", "mousedown", "mouseup", "click"]) {
|
|
32938
|
+
button.addEventListener(type, consumePointerEvent);
|
|
32939
|
+
}
|
|
32940
|
+
button.addEventListener("click", () => {
|
|
32941
|
+
requestStop();
|
|
32942
|
+
host.remove();
|
|
32943
|
+
});
|
|
32944
|
+
shadowRoot.append(style, button);
|
|
32945
|
+
parent.appendChild(host);
|
|
32946
|
+
cleanup.push(() => {
|
|
32947
|
+
host.remove();
|
|
32948
|
+
});
|
|
32949
|
+
return true;
|
|
32950
|
+
}
|
|
32951
|
+
|
|
32952
|
+
function ensureStopButtonMounted() {
|
|
32953
|
+
if (mountStopButton()) {
|
|
32954
|
+
return;
|
|
32955
|
+
}
|
|
32956
|
+
|
|
32957
|
+
const observer = new MutationObserver(() => {
|
|
32958
|
+
if (!mountStopButton()) {
|
|
32959
|
+
return;
|
|
32960
|
+
}
|
|
32961
|
+
observer.disconnect();
|
|
32962
|
+
});
|
|
32963
|
+
observer.observe(document, {
|
|
32964
|
+
childList: true,
|
|
32965
|
+
subtree: true,
|
|
32966
|
+
});
|
|
32967
|
+
cleanup.push(() => {
|
|
32968
|
+
observer.disconnect();
|
|
32969
|
+
});
|
|
32970
|
+
}
|
|
32971
|
+
|
|
32972
|
+
addListener(document, "click", (event) => {
|
|
32973
|
+
if (!event.isTrusted) {
|
|
32974
|
+
return;
|
|
32975
|
+
}
|
|
32976
|
+
const target = nearestRecordTarget(event.target);
|
|
32977
|
+
if (!(target instanceof Element)) {
|
|
32978
|
+
return;
|
|
32979
|
+
}
|
|
32980
|
+
const tag = target.tagName.toLowerCase();
|
|
32981
|
+
if (tag === "select" || tag === "option") {
|
|
32982
|
+
return;
|
|
32983
|
+
}
|
|
32984
|
+
const selector = buildSelector(target);
|
|
32985
|
+
if (!selector) {
|
|
32986
|
+
return;
|
|
32987
|
+
}
|
|
32988
|
+
enqueue({
|
|
32989
|
+
kind: "click",
|
|
32990
|
+
selector,
|
|
32991
|
+
button: event.button,
|
|
32992
|
+
modifiers: modifierKeys(event),
|
|
32993
|
+
});
|
|
32994
|
+
}, true);
|
|
32995
|
+
|
|
32996
|
+
addListener(document, "dblclick", (event) => {
|
|
32997
|
+
if (!event.isTrusted) {
|
|
32998
|
+
return;
|
|
32999
|
+
}
|
|
33000
|
+
const target = nearestRecordTarget(event.target);
|
|
33001
|
+
if (!(target instanceof Element)) {
|
|
33002
|
+
return;
|
|
33003
|
+
}
|
|
33004
|
+
const selector = buildSelector(target);
|
|
33005
|
+
if (!selector) {
|
|
33006
|
+
return;
|
|
33007
|
+
}
|
|
33008
|
+
enqueue({
|
|
33009
|
+
kind: "dblclick",
|
|
33010
|
+
selector,
|
|
33011
|
+
});
|
|
33012
|
+
}, true);
|
|
33013
|
+
|
|
33014
|
+
addListener(document, "input", (event) => {
|
|
33015
|
+
if (!event.isTrusted) {
|
|
33016
|
+
return;
|
|
33017
|
+
}
|
|
33018
|
+
const target = nearestRecordTarget(event.target);
|
|
33019
|
+
if (!(target instanceof Element)) {
|
|
33020
|
+
return;
|
|
33021
|
+
}
|
|
33022
|
+
const selector = buildSelector(target);
|
|
33023
|
+
const text = readTargetValue(target);
|
|
33024
|
+
if (!selector || typeof text !== "string") {
|
|
33025
|
+
return;
|
|
33026
|
+
}
|
|
33027
|
+
pendingInputs.set(selector, {
|
|
33028
|
+
selector,
|
|
33029
|
+
text,
|
|
33030
|
+
timestamp: now(),
|
|
33031
|
+
});
|
|
33032
|
+
scheduleInputFlush(selector);
|
|
33033
|
+
}, true);
|
|
33034
|
+
|
|
33035
|
+
addListener(document, "change", (event) => {
|
|
33036
|
+
if (!event.isTrusted) {
|
|
33037
|
+
return;
|
|
33038
|
+
}
|
|
33039
|
+
const target = nearestRecordTarget(event.target);
|
|
33040
|
+
if (!(target instanceof HTMLSelectElement)) {
|
|
33041
|
+
return;
|
|
33042
|
+
}
|
|
33043
|
+
const selector = buildSelector(target);
|
|
33044
|
+
if (!selector) {
|
|
33045
|
+
return;
|
|
33046
|
+
}
|
|
33047
|
+
const selectedOption = target.selectedOptions && target.selectedOptions.length > 0
|
|
33048
|
+
? target.selectedOptions[0]
|
|
33049
|
+
: undefined;
|
|
33050
|
+
enqueue({
|
|
33051
|
+
kind: "select-option",
|
|
33052
|
+
selector,
|
|
33053
|
+
value: target.value,
|
|
33054
|
+
...(selectedOption === undefined ? {} : { label: selectedOption.label || selectedOption.textContent || "" }),
|
|
33055
|
+
});
|
|
33056
|
+
}, true);
|
|
33057
|
+
|
|
33058
|
+
addListener(document, "keydown", (event) => {
|
|
33059
|
+
if (!event.isTrusted) {
|
|
33060
|
+
return;
|
|
33061
|
+
}
|
|
33062
|
+
const allowedKeys = new Set([
|
|
33063
|
+
"ArrowDown",
|
|
33064
|
+
"ArrowLeft",
|
|
33065
|
+
"ArrowRight",
|
|
33066
|
+
"ArrowUp",
|
|
33067
|
+
"Backspace",
|
|
33068
|
+
"Delete",
|
|
33069
|
+
"Enter",
|
|
33070
|
+
"Escape",
|
|
33071
|
+
"Tab",
|
|
33072
|
+
]);
|
|
33073
|
+
if (!allowedKeys.has(event.key)) {
|
|
33074
|
+
return;
|
|
33075
|
+
}
|
|
33076
|
+
const target = nearestRecordTarget(event.target);
|
|
33077
|
+
const selector = target instanceof Element ? buildSelector(target) : undefined;
|
|
33078
|
+
if (event.key === "Enter" && selector) {
|
|
33079
|
+
flushPendingInput(selector);
|
|
33080
|
+
}
|
|
33081
|
+
enqueue({
|
|
33082
|
+
kind: "keypress",
|
|
33083
|
+
key: event.key,
|
|
33084
|
+
modifiers: modifierKeys(event),
|
|
33085
|
+
...(selector === undefined ? {} : { selector }),
|
|
33086
|
+
});
|
|
33087
|
+
}, true);
|
|
33088
|
+
|
|
33089
|
+
addListener(document, "wheel", (event) => {
|
|
33090
|
+
if (!event.isTrusted) {
|
|
33091
|
+
return;
|
|
33092
|
+
}
|
|
33093
|
+
const target = nearestRecordTarget(event.target);
|
|
33094
|
+
const selector = target instanceof Element ? buildSelector(target) : undefined;
|
|
33095
|
+
if (pendingWheel && pendingWheel.selector === selector) {
|
|
33096
|
+
pendingWheel.deltaX += event.deltaX;
|
|
33097
|
+
pendingWheel.deltaY += event.deltaY;
|
|
33098
|
+
clearTimeout(pendingWheel.timerId);
|
|
33099
|
+
pendingWheel.timerId = setTimeout(flushPendingWheel, 250);
|
|
33100
|
+
return;
|
|
33101
|
+
}
|
|
33102
|
+
flushPendingWheel();
|
|
33103
|
+
pendingWheel = {
|
|
33104
|
+
selector,
|
|
33105
|
+
deltaX: event.deltaX,
|
|
33106
|
+
deltaY: event.deltaY,
|
|
33107
|
+
timestamp: now(),
|
|
33108
|
+
timerId: setTimeout(flushPendingWheel, 250),
|
|
33109
|
+
};
|
|
33110
|
+
}, true);
|
|
33111
|
+
|
|
33112
|
+
const originalPushState = history.pushState.bind(history);
|
|
33113
|
+
history.pushState = function pushState(state, unused, url) {
|
|
33114
|
+
const beforeUrl = location.href;
|
|
33115
|
+
const output = originalPushState.apply(this, arguments);
|
|
33116
|
+
const nextUrl = location.href;
|
|
33117
|
+
if (nextUrl !== beforeUrl) {
|
|
33118
|
+
updateHistoryState("push", nextUrl);
|
|
33119
|
+
enqueue({
|
|
33120
|
+
kind: "navigate",
|
|
33121
|
+
url: nextUrl,
|
|
33122
|
+
source: "push-state",
|
|
33123
|
+
});
|
|
33124
|
+
}
|
|
33125
|
+
return output;
|
|
33126
|
+
};
|
|
33127
|
+
cleanup.push(() => {
|
|
33128
|
+
history.pushState = originalPushState;
|
|
33129
|
+
});
|
|
33130
|
+
|
|
33131
|
+
const originalReplaceState = history.replaceState.bind(history);
|
|
33132
|
+
history.replaceState = function replaceState(state, unused, url) {
|
|
33133
|
+
const beforeUrl = location.href;
|
|
33134
|
+
const output = originalReplaceState.apply(this, arguments);
|
|
33135
|
+
const nextUrl = location.href;
|
|
33136
|
+
if (nextUrl !== beforeUrl) {
|
|
33137
|
+
updateHistoryState("replace", nextUrl);
|
|
33138
|
+
enqueue({
|
|
33139
|
+
kind: "navigate",
|
|
33140
|
+
url: nextUrl,
|
|
33141
|
+
source: "replace-state",
|
|
33142
|
+
});
|
|
33143
|
+
}
|
|
33144
|
+
return output;
|
|
33145
|
+
};
|
|
33146
|
+
cleanup.push(() => {
|
|
33147
|
+
history.replaceState = originalReplaceState;
|
|
33148
|
+
});
|
|
33149
|
+
|
|
33150
|
+
addListener(globalScope, "popstate", () => {
|
|
33151
|
+
const currentUrl = location.href;
|
|
33152
|
+
const traversal = classifyHistoryTraversal(currentUrl);
|
|
33153
|
+
if (traversal === "go-back" || traversal === "go-forward") {
|
|
33154
|
+
enqueue({
|
|
33155
|
+
kind: traversal,
|
|
33156
|
+
url: currentUrl,
|
|
33157
|
+
});
|
|
33158
|
+
return;
|
|
33159
|
+
}
|
|
33160
|
+
const state = readHistoryState();
|
|
33161
|
+
if (state?.entries[state.index] !== currentUrl) {
|
|
33162
|
+
updateHistoryState("push", currentUrl);
|
|
33163
|
+
enqueue({
|
|
33164
|
+
kind: "navigate",
|
|
33165
|
+
url: currentUrl,
|
|
33166
|
+
source: "history-traversal",
|
|
33167
|
+
});
|
|
33168
|
+
}
|
|
33169
|
+
});
|
|
33170
|
+
|
|
33171
|
+
addListener(globalScope, "hashchange", () => {
|
|
33172
|
+
const currentUrl = location.href;
|
|
33173
|
+
updateHistoryState("replace", currentUrl);
|
|
33174
|
+
enqueue({
|
|
33175
|
+
kind: "navigate",
|
|
33176
|
+
url: currentUrl,
|
|
33177
|
+
source: "hashchange",
|
|
33178
|
+
});
|
|
33179
|
+
});
|
|
33180
|
+
|
|
33181
|
+
function drain() {
|
|
33182
|
+
flushAllInputs();
|
|
33183
|
+
flushPendingWheel();
|
|
33184
|
+
return {
|
|
33185
|
+
url: location.href,
|
|
33186
|
+
focused: document.hasFocus(),
|
|
33187
|
+
visibilityState: document.visibilityState,
|
|
33188
|
+
stopRequested,
|
|
33189
|
+
events: queue.splice(0, queue.length),
|
|
33190
|
+
};
|
|
33191
|
+
}
|
|
33192
|
+
|
|
33193
|
+
globalScope[recorderKey] = {
|
|
33194
|
+
installed: true,
|
|
33195
|
+
debugSelector(target) {
|
|
33196
|
+
return buildSelector(target);
|
|
33197
|
+
},
|
|
33198
|
+
drain,
|
|
33199
|
+
requestStop,
|
|
33200
|
+
dispose() {
|
|
33201
|
+
flushAllInputs();
|
|
33202
|
+
flushPendingWheel();
|
|
33203
|
+
for (const dispose of cleanup.splice(0, cleanup.length)) {
|
|
33204
|
+
dispose();
|
|
33205
|
+
}
|
|
33206
|
+
for (const timerId of inputFlushTimers.values()) {
|
|
33207
|
+
clearTimeout(timerId);
|
|
33208
|
+
}
|
|
33209
|
+
inputFlushTimers.clear();
|
|
33210
|
+
pendingInputs.clear();
|
|
33211
|
+
delete globalScope[recorderKey];
|
|
33212
|
+
},
|
|
33213
|
+
};
|
|
33214
|
+
|
|
33215
|
+
if (options.showStopButton) {
|
|
33216
|
+
ensureStopButtonMounted();
|
|
33217
|
+
}
|
|
33218
|
+
onInstall();
|
|
33219
|
+
})();`;
|
|
33220
|
+
}
|
|
33221
|
+
var FLOW_RECORDER_INSTALL_SCRIPT = createFlowRecorderInstallScript();
|
|
33222
|
+
var FLOW_RECORDER_DRAIN_SCRIPT = String.raw`(() => {
|
|
33223
|
+
const recorder = globalThis.__opensteerFlowRecorder;
|
|
33224
|
+
if (!recorder || typeof recorder.drain !== "function") {
|
|
33225
|
+
return {
|
|
33226
|
+
url: location.href,
|
|
33227
|
+
focused: document.hasFocus(),
|
|
33228
|
+
visibilityState: document.visibilityState,
|
|
33229
|
+
stopRequested: false,
|
|
33230
|
+
events: [],
|
|
33231
|
+
};
|
|
33232
|
+
}
|
|
33233
|
+
return recorder.drain();
|
|
33234
|
+
})();`;
|
|
33235
|
+
|
|
33236
|
+
// ../runtime-core/src/recorder/event-collector.ts
|
|
33237
|
+
var FlowRecorderCollector = class {
|
|
33238
|
+
runtime;
|
|
33239
|
+
pollIntervalMs;
|
|
33240
|
+
onAction;
|
|
33241
|
+
installScript;
|
|
33242
|
+
pages = /* @__PURE__ */ new Map();
|
|
33243
|
+
actions = [];
|
|
33244
|
+
nextPageOrdinal = 0;
|
|
33245
|
+
runningLoop;
|
|
33246
|
+
loopStopRequested = false;
|
|
33247
|
+
stopDetected = false;
|
|
33248
|
+
focusedPageId;
|
|
33249
|
+
stopWaiters = [];
|
|
33250
|
+
constructor(runtime, options = {}) {
|
|
33251
|
+
this.runtime = runtime;
|
|
33252
|
+
this.pollIntervalMs = options.pollIntervalMs ?? 250;
|
|
33253
|
+
this.onAction = options.onAction;
|
|
33254
|
+
this.installScript = options.installScript ?? FLOW_RECORDER_INSTALL_SCRIPT;
|
|
33255
|
+
}
|
|
33256
|
+
async install() {
|
|
33257
|
+
await this.runtime.addInitScript({
|
|
33258
|
+
script: this.installScript
|
|
33259
|
+
});
|
|
33260
|
+
const { pages } = await this.runtime.listPages();
|
|
33261
|
+
for (const page of pages) {
|
|
33262
|
+
this.ensureKnownPage(page.pageRef, page.url, page.openerPageRef);
|
|
33263
|
+
}
|
|
33264
|
+
await Promise.all(
|
|
33265
|
+
pages.map(
|
|
33266
|
+
(page) => this.runtime.evaluate({
|
|
33267
|
+
script: this.installScript,
|
|
33268
|
+
pageRef: page.pageRef
|
|
33269
|
+
}).catch(() => void 0)
|
|
33270
|
+
)
|
|
33271
|
+
);
|
|
33272
|
+
const evaluatedPages = await this.readEvaluatedPages(pages);
|
|
33273
|
+
for (const page of evaluatedPages) {
|
|
33274
|
+
this.updateKnownPage(page.pageRef, page.currentUrl, page.openerPageRef);
|
|
33275
|
+
}
|
|
33276
|
+
this.focusedPageId = evaluatedPages.find((page) => page.focused)?.pageId ?? this.focusedPageId;
|
|
33277
|
+
}
|
|
33278
|
+
start() {
|
|
33279
|
+
if (this.runningLoop !== void 0) {
|
|
33280
|
+
return;
|
|
33281
|
+
}
|
|
33282
|
+
this.loopStopRequested = false;
|
|
33283
|
+
this.runningLoop = this.runLoop();
|
|
33284
|
+
}
|
|
33285
|
+
async stop() {
|
|
33286
|
+
this.loopStopRequested = true;
|
|
33287
|
+
if (this.runningLoop !== void 0) {
|
|
33288
|
+
await this.runningLoop;
|
|
33289
|
+
this.runningLoop = void 0;
|
|
33290
|
+
}
|
|
33291
|
+
if (!this.stopDetected) {
|
|
33292
|
+
await this.pollOnce().catch(() => void 0);
|
|
33293
|
+
}
|
|
33294
|
+
return this.actions.slice();
|
|
33295
|
+
}
|
|
33296
|
+
getActions() {
|
|
33297
|
+
return this.actions.slice();
|
|
33298
|
+
}
|
|
33299
|
+
getPages() {
|
|
33300
|
+
return [...this.pages.values()].map((page) => ({
|
|
33301
|
+
pageId: page.pageId,
|
|
33302
|
+
pageRef: page.pageRef,
|
|
33303
|
+
...page.openerPageRef === void 0 ? {} : { openerPageRef: page.openerPageRef },
|
|
33304
|
+
...page.openerPageId === void 0 ? {} : { openerPageId: page.openerPageId },
|
|
33305
|
+
currentUrl: page.currentUrl
|
|
33306
|
+
})).sort((left, right) => comparePageIds(left.pageId, right.pageId));
|
|
33307
|
+
}
|
|
33308
|
+
getFocusedPageId() {
|
|
33309
|
+
return this.focusedPageId;
|
|
33310
|
+
}
|
|
33311
|
+
async waitForStop() {
|
|
33312
|
+
if (this.stopDetected) {
|
|
33313
|
+
return;
|
|
33314
|
+
}
|
|
33315
|
+
await new Promise((resolve4) => {
|
|
33316
|
+
this.stopWaiters.push(resolve4);
|
|
33317
|
+
});
|
|
33318
|
+
}
|
|
33319
|
+
async pollOnce() {
|
|
33320
|
+
const pollTimestamp = Date.now();
|
|
33321
|
+
const { pages } = await this.runtime.listPages();
|
|
33322
|
+
const previousPageRefs = new Set(this.pages.keys());
|
|
33323
|
+
const evaluatedPages = await this.readEvaluatedPages(pages);
|
|
33324
|
+
if (!this.stopDetected && evaluatedPages.some((page) => page.stopRequested)) {
|
|
33325
|
+
this.stopDetected = true;
|
|
33326
|
+
this.loopStopRequested = true;
|
|
33327
|
+
for (const resolve4 of this.stopWaiters.splice(0, this.stopWaiters.length)) {
|
|
33328
|
+
resolve4();
|
|
33329
|
+
}
|
|
33330
|
+
}
|
|
33331
|
+
const actions = this.normalizePoll({
|
|
33332
|
+
pollTimestamp,
|
|
33333
|
+
previousPageRefs,
|
|
33334
|
+
listedPages: pages,
|
|
33335
|
+
evaluatedPages
|
|
33336
|
+
});
|
|
33337
|
+
if (actions.length === 0) {
|
|
33338
|
+
return [];
|
|
33339
|
+
}
|
|
33340
|
+
actions.sort((left, right) => {
|
|
33341
|
+
const timestampOrder = left.timestamp - right.timestamp;
|
|
33342
|
+
if (timestampOrder !== 0) {
|
|
33343
|
+
return timestampOrder;
|
|
33344
|
+
}
|
|
33345
|
+
return actionSortPriority(left.kind) - actionSortPriority(right.kind);
|
|
33346
|
+
});
|
|
33347
|
+
for (const action of actions) {
|
|
33348
|
+
this.actions.push(action);
|
|
33349
|
+
if (this.onAction !== void 0) {
|
|
33350
|
+
await this.onAction(action);
|
|
33351
|
+
}
|
|
33352
|
+
}
|
|
33353
|
+
return actions;
|
|
33354
|
+
}
|
|
33355
|
+
async runLoop() {
|
|
33356
|
+
while (!this.loopStopRequested) {
|
|
33357
|
+
try {
|
|
33358
|
+
await this.pollOnce();
|
|
33359
|
+
} catch {
|
|
33360
|
+
}
|
|
33361
|
+
if (this.loopStopRequested) {
|
|
33362
|
+
break;
|
|
33363
|
+
}
|
|
33364
|
+
await delay(this.pollIntervalMs);
|
|
33365
|
+
}
|
|
33366
|
+
}
|
|
33367
|
+
async readEvaluatedPages(listedPages) {
|
|
33368
|
+
const pages = await Promise.all(
|
|
33369
|
+
listedPages.map(async (page) => {
|
|
33370
|
+
const knownPage = this.ensureKnownPage(page.pageRef, page.url, page.openerPageRef);
|
|
33371
|
+
const snapshot = await this.readSnapshot(page.pageRef, page.url);
|
|
33372
|
+
this.updateKnownPage(page.pageRef, snapshot.url, page.openerPageRef);
|
|
33373
|
+
return {
|
|
33374
|
+
pageRef: page.pageRef,
|
|
33375
|
+
pageId: knownPage.pageId,
|
|
33376
|
+
previousUrl: knownPage.currentUrl,
|
|
33377
|
+
currentUrl: snapshot.url,
|
|
33378
|
+
focused: snapshot.focused || snapshot.visibilityState === "visible",
|
|
33379
|
+
stopRequested: snapshot.stopRequested,
|
|
33380
|
+
events: snapshot.events,
|
|
33381
|
+
...page.openerPageRef === void 0 ? {} : { openerPageRef: page.openerPageRef },
|
|
33382
|
+
...knownPage.openerPageId === void 0 ? {} : { openerPageId: knownPage.openerPageId }
|
|
33383
|
+
};
|
|
33384
|
+
})
|
|
33385
|
+
);
|
|
33386
|
+
return pages;
|
|
33387
|
+
}
|
|
33388
|
+
async readSnapshot(pageRef, fallbackUrl) {
|
|
33389
|
+
const value = await this.runtime.evaluate({
|
|
33390
|
+
script: FLOW_RECORDER_DRAIN_SCRIPT,
|
|
33391
|
+
pageRef
|
|
33392
|
+
});
|
|
33393
|
+
return normalizeSnapshot(value, fallbackUrl);
|
|
33394
|
+
}
|
|
33395
|
+
normalizePoll(input) {
|
|
33396
|
+
const listedPageRefs = new Set(input.listedPages.map((page) => page.pageRef));
|
|
33397
|
+
const actions = [];
|
|
33398
|
+
const firstEventTimestampByPage = /* @__PURE__ */ new Map();
|
|
33399
|
+
for (const evaluatedPage of input.evaluatedPages) {
|
|
33400
|
+
const firstTimestamp = evaluatedPage.events[0]?.timestamp;
|
|
33401
|
+
if (firstTimestamp !== void 0) {
|
|
33402
|
+
firstEventTimestampByPage.set(evaluatedPage.pageRef, firstTimestamp);
|
|
33403
|
+
}
|
|
33404
|
+
}
|
|
33405
|
+
for (const listedPage of input.listedPages) {
|
|
33406
|
+
if (!input.previousPageRefs.has(listedPage.pageRef)) {
|
|
33407
|
+
const created = this.ensureKnownPage(
|
|
33408
|
+
listedPage.pageRef,
|
|
33409
|
+
listedPage.url,
|
|
33410
|
+
listedPage.openerPageRef
|
|
33411
|
+
);
|
|
33412
|
+
actions.push({
|
|
33413
|
+
kind: "new-tab",
|
|
33414
|
+
timestamp: Math.max(
|
|
33415
|
+
0,
|
|
33416
|
+
(firstEventTimestampByPage.get(listedPage.pageRef) ?? input.pollTimestamp) - 1
|
|
33417
|
+
),
|
|
33418
|
+
pageId: created.pageId,
|
|
33419
|
+
pageUrl: listedPage.url,
|
|
33420
|
+
detail: {
|
|
33421
|
+
kind: "new-tab",
|
|
33422
|
+
...created.openerPageId === void 0 ? {} : { openerPageId: created.openerPageId },
|
|
33423
|
+
initialUrl: listedPage.url
|
|
33424
|
+
}
|
|
33425
|
+
});
|
|
33426
|
+
}
|
|
33427
|
+
}
|
|
33428
|
+
for (const [pageRef, knownPage] of [...this.pages.entries()]) {
|
|
33429
|
+
if (listedPageRefs.has(pageRef)) {
|
|
33430
|
+
continue;
|
|
33431
|
+
}
|
|
33432
|
+
actions.push({
|
|
33433
|
+
kind: "close-tab",
|
|
33434
|
+
timestamp: input.pollTimestamp,
|
|
33435
|
+
pageId: knownPage.pageId,
|
|
33436
|
+
pageUrl: knownPage.currentUrl,
|
|
33437
|
+
detail: {
|
|
33438
|
+
kind: "close-tab"
|
|
33439
|
+
}
|
|
33440
|
+
});
|
|
33441
|
+
this.pages.delete(pageRef);
|
|
33442
|
+
}
|
|
33443
|
+
for (const evaluatedPage of input.evaluatedPages) {
|
|
33444
|
+
const knownPage = this.pages.get(evaluatedPage.pageRef);
|
|
33445
|
+
if (!knownPage) {
|
|
33446
|
+
continue;
|
|
33447
|
+
}
|
|
33448
|
+
if (evaluatedPage.previousUrl !== evaluatedPage.currentUrl && !evaluatedPage.events.some(
|
|
33449
|
+
(event) => event.kind === "navigate" || event.kind === "reload" || event.kind === "go-back" || event.kind === "go-forward"
|
|
33450
|
+
)) {
|
|
33451
|
+
actions.push({
|
|
33452
|
+
kind: "navigate",
|
|
33453
|
+
timestamp: Math.max(
|
|
33454
|
+
0,
|
|
33455
|
+
(firstEventTimestampByPage.get(evaluatedPage.pageRef) ?? input.pollTimestamp) - 1
|
|
33456
|
+
),
|
|
33457
|
+
pageId: knownPage.pageId,
|
|
33458
|
+
pageUrl: evaluatedPage.currentUrl,
|
|
33459
|
+
detail: {
|
|
33460
|
+
kind: "navigate",
|
|
33461
|
+
url: evaluatedPage.currentUrl,
|
|
33462
|
+
source: "poll"
|
|
33463
|
+
}
|
|
33464
|
+
});
|
|
33465
|
+
}
|
|
33466
|
+
actions.push(
|
|
33467
|
+
...evaluatedPage.events.flatMap(
|
|
33468
|
+
(event) => this.normalizeRawEvent(event, knownPage, evaluatedPage.currentUrl)
|
|
33469
|
+
)
|
|
33470
|
+
);
|
|
33471
|
+
this.updateKnownPage(
|
|
33472
|
+
evaluatedPage.pageRef,
|
|
33473
|
+
evaluatedPage.currentUrl,
|
|
33474
|
+
evaluatedPage.openerPageRef
|
|
33475
|
+
);
|
|
33476
|
+
}
|
|
33477
|
+
const focusedPage = input.evaluatedPages.find((page) => page.focused);
|
|
33478
|
+
if (focusedPage !== void 0 && focusedPage.pageId !== this.focusedPageId) {
|
|
33479
|
+
actions.push({
|
|
33480
|
+
kind: "switch-tab",
|
|
33481
|
+
timestamp: Math.max(
|
|
33482
|
+
0,
|
|
33483
|
+
(firstEventTimestampByPage.get(focusedPage.pageRef) ?? input.pollTimestamp) - 1
|
|
33484
|
+
),
|
|
33485
|
+
pageId: focusedPage.pageId,
|
|
33486
|
+
pageUrl: focusedPage.currentUrl,
|
|
33487
|
+
detail: {
|
|
33488
|
+
kind: "switch-tab",
|
|
33489
|
+
...this.focusedPageId === void 0 ? {} : { fromPageId: this.focusedPageId },
|
|
33490
|
+
toPageId: focusedPage.pageId
|
|
33491
|
+
}
|
|
33492
|
+
});
|
|
33493
|
+
this.focusedPageId = focusedPage.pageId;
|
|
33494
|
+
}
|
|
33495
|
+
return dedupeConsecutiveSwitchActions(actions);
|
|
33496
|
+
}
|
|
33497
|
+
normalizeRawEvent(event, page, currentUrl) {
|
|
33498
|
+
switch (event.kind) {
|
|
33499
|
+
case "navigate":
|
|
33500
|
+
return [
|
|
33501
|
+
{
|
|
33502
|
+
kind: "navigate",
|
|
33503
|
+
timestamp: event.timestamp,
|
|
33504
|
+
pageId: page.pageId,
|
|
33505
|
+
pageUrl: event.url,
|
|
33506
|
+
detail: {
|
|
33507
|
+
kind: "navigate",
|
|
33508
|
+
url: event.url,
|
|
33509
|
+
source: event.source
|
|
33510
|
+
}
|
|
33511
|
+
}
|
|
33512
|
+
];
|
|
33513
|
+
case "click":
|
|
33514
|
+
return [
|
|
33515
|
+
{
|
|
33516
|
+
kind: "click",
|
|
33517
|
+
timestamp: event.timestamp,
|
|
33518
|
+
pageId: page.pageId,
|
|
33519
|
+
pageUrl: currentUrl,
|
|
33520
|
+
selector: event.selector,
|
|
33521
|
+
detail: {
|
|
33522
|
+
kind: "click",
|
|
33523
|
+
button: event.button,
|
|
33524
|
+
modifiers: event.modifiers
|
|
33525
|
+
}
|
|
33526
|
+
}
|
|
33527
|
+
];
|
|
33528
|
+
case "dblclick":
|
|
33529
|
+
return [
|
|
33530
|
+
{
|
|
33531
|
+
kind: "dblclick",
|
|
33532
|
+
timestamp: event.timestamp,
|
|
33533
|
+
pageId: page.pageId,
|
|
33534
|
+
pageUrl: currentUrl,
|
|
33535
|
+
selector: event.selector,
|
|
33536
|
+
detail: {
|
|
33537
|
+
kind: "dblclick"
|
|
33538
|
+
}
|
|
33539
|
+
}
|
|
33540
|
+
];
|
|
33541
|
+
case "type":
|
|
33542
|
+
return [
|
|
33543
|
+
{
|
|
33544
|
+
kind: "type",
|
|
33545
|
+
timestamp: event.timestamp,
|
|
33546
|
+
pageId: page.pageId,
|
|
33547
|
+
pageUrl: currentUrl,
|
|
33548
|
+
selector: event.selector,
|
|
33549
|
+
detail: {
|
|
33550
|
+
kind: "type",
|
|
33551
|
+
text: event.text
|
|
33552
|
+
}
|
|
33553
|
+
}
|
|
33554
|
+
];
|
|
33555
|
+
case "keypress":
|
|
33556
|
+
return [
|
|
33557
|
+
{
|
|
33558
|
+
kind: "keypress",
|
|
33559
|
+
timestamp: event.timestamp,
|
|
33560
|
+
pageId: page.pageId,
|
|
33561
|
+
pageUrl: currentUrl,
|
|
33562
|
+
...event.selector === void 0 ? {} : { selector: event.selector },
|
|
33563
|
+
detail: {
|
|
33564
|
+
kind: "keypress",
|
|
33565
|
+
key: event.key,
|
|
33566
|
+
modifiers: event.modifiers
|
|
33567
|
+
}
|
|
33568
|
+
}
|
|
33569
|
+
];
|
|
33570
|
+
case "scroll":
|
|
33571
|
+
return [
|
|
33572
|
+
{
|
|
33573
|
+
kind: "scroll",
|
|
33574
|
+
timestamp: event.timestamp,
|
|
33575
|
+
pageId: page.pageId,
|
|
33576
|
+
pageUrl: currentUrl,
|
|
33577
|
+
...event.selector === void 0 ? {} : { selector: event.selector },
|
|
33578
|
+
detail: {
|
|
33579
|
+
kind: "scroll",
|
|
33580
|
+
deltaX: event.deltaX,
|
|
33581
|
+
deltaY: event.deltaY
|
|
33582
|
+
}
|
|
33583
|
+
}
|
|
33584
|
+
];
|
|
33585
|
+
case "select-option":
|
|
33586
|
+
return [
|
|
33587
|
+
{
|
|
33588
|
+
kind: "select-option",
|
|
33589
|
+
timestamp: event.timestamp,
|
|
33590
|
+
pageId: page.pageId,
|
|
33591
|
+
pageUrl: currentUrl,
|
|
33592
|
+
selector: event.selector,
|
|
33593
|
+
detail: {
|
|
33594
|
+
kind: "select-option",
|
|
33595
|
+
value: event.value,
|
|
33596
|
+
...event.label === void 0 ? {} : { label: event.label }
|
|
33597
|
+
}
|
|
33598
|
+
}
|
|
33599
|
+
];
|
|
33600
|
+
case "reload":
|
|
33601
|
+
return [
|
|
33602
|
+
{
|
|
33603
|
+
kind: "reload",
|
|
33604
|
+
timestamp: event.timestamp,
|
|
33605
|
+
pageId: page.pageId,
|
|
33606
|
+
pageUrl: event.url,
|
|
33607
|
+
detail: {
|
|
33608
|
+
kind: "reload",
|
|
33609
|
+
url: event.url
|
|
33610
|
+
}
|
|
33611
|
+
}
|
|
33612
|
+
];
|
|
33613
|
+
case "go-back":
|
|
33614
|
+
return [
|
|
33615
|
+
{
|
|
33616
|
+
kind: "go-back",
|
|
33617
|
+
timestamp: event.timestamp,
|
|
33618
|
+
pageId: page.pageId,
|
|
33619
|
+
pageUrl: event.url,
|
|
33620
|
+
detail: {
|
|
33621
|
+
kind: "go-back",
|
|
33622
|
+
url: event.url
|
|
33623
|
+
}
|
|
33624
|
+
}
|
|
33625
|
+
];
|
|
33626
|
+
case "go-forward":
|
|
33627
|
+
return [
|
|
33628
|
+
{
|
|
33629
|
+
kind: "go-forward",
|
|
33630
|
+
timestamp: event.timestamp,
|
|
33631
|
+
pageId: page.pageId,
|
|
33632
|
+
pageUrl: event.url,
|
|
33633
|
+
detail: {
|
|
33634
|
+
kind: "go-forward",
|
|
33635
|
+
url: event.url
|
|
33636
|
+
}
|
|
33637
|
+
}
|
|
33638
|
+
];
|
|
33639
|
+
}
|
|
33640
|
+
}
|
|
33641
|
+
ensureKnownPage(pageRef, url, openerPageRef) {
|
|
33642
|
+
const existing = this.pages.get(pageRef);
|
|
33643
|
+
if (existing !== void 0) {
|
|
33644
|
+
return existing;
|
|
33645
|
+
}
|
|
33646
|
+
const openerPageId = openerPageRef === void 0 ? void 0 : this.pages.get(openerPageRef)?.pageId;
|
|
33647
|
+
const page = {
|
|
33648
|
+
pageId: `page${String(this.nextPageOrdinal++)}`,
|
|
33649
|
+
pageRef,
|
|
33650
|
+
currentUrl: url,
|
|
33651
|
+
...openerPageRef === void 0 ? {} : { openerPageRef },
|
|
33652
|
+
...openerPageId === void 0 ? {} : { openerPageId }
|
|
33653
|
+
};
|
|
33654
|
+
this.pages.set(pageRef, page);
|
|
33655
|
+
return page;
|
|
33656
|
+
}
|
|
33657
|
+
updateKnownPage(pageRef, url, openerPageRef) {
|
|
33658
|
+
const current = this.pages.get(pageRef);
|
|
33659
|
+
if (current === void 0) {
|
|
33660
|
+
this.ensureKnownPage(pageRef, url, openerPageRef);
|
|
33661
|
+
return;
|
|
33662
|
+
}
|
|
33663
|
+
const openerPageId = openerPageRef === void 0 ? void 0 : this.pages.get(openerPageRef)?.pageId;
|
|
33664
|
+
this.pages.set(pageRef, {
|
|
33665
|
+
...current,
|
|
33666
|
+
currentUrl: url,
|
|
33667
|
+
...openerPageRef === void 0 ? {} : { openerPageRef },
|
|
33668
|
+
...openerPageId === void 0 ? {} : { openerPageId }
|
|
33669
|
+
});
|
|
33670
|
+
}
|
|
33671
|
+
};
|
|
33672
|
+
function normalizeSnapshot(value, fallbackUrl) {
|
|
33673
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
33674
|
+
return {
|
|
33675
|
+
url: fallbackUrl,
|
|
33676
|
+
focused: false,
|
|
33677
|
+
visibilityState: "hidden",
|
|
33678
|
+
stopRequested: false,
|
|
33679
|
+
events: []
|
|
33680
|
+
};
|
|
33681
|
+
}
|
|
33682
|
+
const snapshot = value;
|
|
33683
|
+
return {
|
|
33684
|
+
url: typeof snapshot.url === "string" ? snapshot.url : fallbackUrl,
|
|
33685
|
+
focused: snapshot.focused === true,
|
|
33686
|
+
visibilityState: snapshot.visibilityState === "visible" || snapshot.visibilityState === "prerender" || snapshot.visibilityState === "hidden" ? snapshot.visibilityState : "hidden",
|
|
33687
|
+
stopRequested: snapshot.stopRequested === true,
|
|
33688
|
+
events: Array.isArray(snapshot.events) ? snapshot.events : []
|
|
33689
|
+
};
|
|
33690
|
+
}
|
|
33691
|
+
function dedupeConsecutiveSwitchActions(actions) {
|
|
33692
|
+
const deduped = [];
|
|
33693
|
+
for (const action of actions) {
|
|
33694
|
+
const previous = deduped[deduped.length - 1];
|
|
33695
|
+
if (previous?.kind === "switch-tab" && action.kind === "switch-tab" && previous.pageId === action.pageId) {
|
|
33696
|
+
continue;
|
|
33697
|
+
}
|
|
33698
|
+
deduped.push(action);
|
|
33699
|
+
}
|
|
33700
|
+
return deduped;
|
|
33701
|
+
}
|
|
33702
|
+
function actionSortPriority(kind) {
|
|
33703
|
+
switch (kind) {
|
|
33704
|
+
case "new-tab":
|
|
33705
|
+
return 0;
|
|
33706
|
+
case "switch-tab":
|
|
33707
|
+
return 1;
|
|
33708
|
+
case "navigate":
|
|
33709
|
+
case "go-back":
|
|
33710
|
+
case "go-forward":
|
|
33711
|
+
case "reload":
|
|
33712
|
+
return 2;
|
|
33713
|
+
case "click":
|
|
33714
|
+
case "dblclick":
|
|
33715
|
+
return 3;
|
|
33716
|
+
case "type":
|
|
33717
|
+
case "keypress":
|
|
33718
|
+
case "select-option":
|
|
33719
|
+
return 4;
|
|
33720
|
+
case "scroll":
|
|
33721
|
+
return 5;
|
|
33722
|
+
case "close-tab":
|
|
33723
|
+
return 6;
|
|
33724
|
+
}
|
|
33725
|
+
}
|
|
33726
|
+
function comparePageIds(left, right) {
|
|
33727
|
+
const leftMatch = /^page(\d+)$/u.exec(left);
|
|
33728
|
+
const rightMatch = /^page(\d+)$/u.exec(right);
|
|
33729
|
+
if (leftMatch && rightMatch) {
|
|
33730
|
+
return Number(leftMatch[1]) - Number(rightMatch[1]);
|
|
33731
|
+
}
|
|
33732
|
+
return left.localeCompare(right);
|
|
33733
|
+
}
|
|
33734
|
+
function delay(ms) {
|
|
33735
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
33736
|
+
}
|
|
33737
|
+
|
|
33738
|
+
// ../runtime-core/src/recorder/codegen.ts
|
|
33739
|
+
var DISPATCH_KEY_SCRIPT = String.raw`(key) => {
|
|
33740
|
+
const target = document.activeElement;
|
|
33741
|
+
if (!(target instanceof Element)) {
|
|
33742
|
+
throw new Error("No active element is available for key replay.");
|
|
33743
|
+
}
|
|
33744
|
+
const normalizedKey = String(key);
|
|
33745
|
+
const eventInit = {
|
|
33746
|
+
key: normalizedKey,
|
|
33747
|
+
code: normalizedKey,
|
|
33748
|
+
bubbles: true,
|
|
33749
|
+
cancelable: true,
|
|
33750
|
+
};
|
|
33751
|
+
target.dispatchEvent(new KeyboardEvent("keydown", eventInit));
|
|
33752
|
+
target.dispatchEvent(new KeyboardEvent("keyup", eventInit));
|
|
33753
|
+
}`;
|
|
33754
|
+
var SELECT_OPTION_SCRIPT = String.raw`(selector, value) => {
|
|
33755
|
+
const target = document.querySelector(String(selector));
|
|
33756
|
+
if (!(target instanceof HTMLSelectElement)) {
|
|
33757
|
+
throw new Error("Unable to find a <select> element for option replay.");
|
|
33758
|
+
}
|
|
33759
|
+
target.value = String(value);
|
|
33760
|
+
target.dispatchEvent(new Event("input", { bubbles: true }));
|
|
33761
|
+
target.dispatchEvent(new Event("change", { bubbles: true }));
|
|
33762
|
+
}`;
|
|
33763
|
+
var HISTORY_ACTION_SCRIPT = String.raw`(action) => {
|
|
33764
|
+
const normalized = String(action);
|
|
33765
|
+
if (normalized === "back") {
|
|
33766
|
+
history.back();
|
|
33767
|
+
return;
|
|
33768
|
+
}
|
|
33769
|
+
if (normalized === "forward") {
|
|
33770
|
+
history.forward();
|
|
33771
|
+
return;
|
|
33772
|
+
}
|
|
33773
|
+
if (normalized === "reload") {
|
|
33774
|
+
location.reload();
|
|
33775
|
+
return;
|
|
33776
|
+
}
|
|
33777
|
+
throw new Error("Unsupported history action: " + normalized);
|
|
33778
|
+
}`;
|
|
33779
|
+
var WINDOW_SCROLL_SCRIPT = String.raw`(deltaX, deltaY) => {
|
|
33780
|
+
window.scrollBy(Number(deltaX), Number(deltaY));
|
|
33781
|
+
}`;
|
|
33782
|
+
function generateReplayScript(options) {
|
|
33783
|
+
const replayTarget = resolveReplayTarget(options);
|
|
33784
|
+
const initialPages = orderInitialPages(resolveInitialPages(options));
|
|
33785
|
+
const activeInitialPageId = resolveActiveInitialPageId(options, initialPages);
|
|
33786
|
+
const initialPageId = initialPages[0]?.pageId ?? "page0";
|
|
33787
|
+
const lines = [
|
|
33788
|
+
`import { Opensteer } from "opensteer";`,
|
|
33789
|
+
``,
|
|
33790
|
+
...renderOpensteerBootstrap(replayTarget),
|
|
33791
|
+
``,
|
|
33792
|
+
`const ${initialPageId} = (await opensteer.open(${JSON.stringify(initialPages[0]?.initialUrl ?? "")})).pageRef;`,
|
|
33793
|
+
`let activePageRef: string | undefined = ${initialPageId};`,
|
|
33794
|
+
``,
|
|
33795
|
+
`async function ensureActive(pageRef: string): Promise<void> {`,
|
|
33796
|
+
` if (activePageRef === pageRef) {`,
|
|
33797
|
+
` return;`,
|
|
33798
|
+
` }`,
|
|
33799
|
+
` await opensteer.activatePage({ pageRef });`,
|
|
33800
|
+
` activePageRef = pageRef;`,
|
|
33801
|
+
`}`,
|
|
33802
|
+
``,
|
|
33803
|
+
`async function dispatchKey(pageRef: string, key: string): Promise<void> {`,
|
|
33804
|
+
` await ensureActive(pageRef);`,
|
|
33805
|
+
` await opensteer.evaluate({`,
|
|
33806
|
+
` pageRef,`,
|
|
33807
|
+
` script: ${JSON.stringify(DISPATCH_KEY_SCRIPT)},`,
|
|
33808
|
+
` args: [key],`,
|
|
33809
|
+
` });`,
|
|
33810
|
+
`}`,
|
|
33811
|
+
``,
|
|
33812
|
+
`async function selectOption(pageRef: string, selector: string, value: string): Promise<void> {`,
|
|
33813
|
+
` await ensureActive(pageRef);`,
|
|
33814
|
+
` await opensteer.evaluate({`,
|
|
33815
|
+
` pageRef,`,
|
|
33816
|
+
` script: ${JSON.stringify(SELECT_OPTION_SCRIPT)},`,
|
|
33817
|
+
` args: [selector, value],`,
|
|
33818
|
+
` });`,
|
|
33819
|
+
`}`,
|
|
33820
|
+
``,
|
|
33821
|
+
`async function runHistoryAction(pageRef: string, action: "back" | "forward" | "reload"): Promise<void> {`,
|
|
33822
|
+
` await ensureActive(pageRef);`,
|
|
33823
|
+
` await opensteer.evaluate({`,
|
|
33824
|
+
` pageRef,`,
|
|
33825
|
+
` script: ${JSON.stringify(HISTORY_ACTION_SCRIPT)},`,
|
|
33826
|
+
` args: [action],`,
|
|
33827
|
+
` });`,
|
|
33828
|
+
`}`,
|
|
33829
|
+
``,
|
|
33830
|
+
`try {`
|
|
33831
|
+
];
|
|
33832
|
+
const declaredPages = /* @__PURE__ */ new Set([initialPageId]);
|
|
33833
|
+
for (const page of initialPages.slice(1)) {
|
|
33834
|
+
const openerPageId = page.openerPageId !== void 0 && declaredPages.has(page.openerPageId) ? page.openerPageId : void 0;
|
|
33835
|
+
lines.push(
|
|
33836
|
+
` const ${page.pageId} = (await opensteer.newPage(${renderNewPageInput(openerPageId, page.initialUrl)})).pageRef;`
|
|
33837
|
+
);
|
|
33838
|
+
lines.push(` activePageRef = ${page.pageId};`);
|
|
33839
|
+
declaredPages.add(page.pageId);
|
|
33840
|
+
}
|
|
33841
|
+
if (activeInitialPageId !== void 0 && activeInitialPageId !== initialPageId) {
|
|
33842
|
+
lines.push(` await ensureActive(${activeInitialPageId});`);
|
|
33843
|
+
}
|
|
33844
|
+
for (let index = 0; index < options.actions.length; index += 1) {
|
|
33845
|
+
const action = options.actions[index];
|
|
33846
|
+
const pageVar = action.pageId;
|
|
33847
|
+
if (action.kind === "type") {
|
|
33848
|
+
const nextAction = options.actions[index + 1];
|
|
33849
|
+
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;
|
|
33850
|
+
lines.push(` await ensureActive(${pageVar});`);
|
|
33851
|
+
lines.push(
|
|
33852
|
+
` await opensteer.input({ selector: ${JSON.stringify(requireSelector(action))}, text: ${JSON.stringify(action.detail.text)}${mergedPressEnter ? `, pressEnter: true` : ``} });`
|
|
33853
|
+
);
|
|
33854
|
+
if (mergedPressEnter) {
|
|
33855
|
+
index += 1;
|
|
33856
|
+
}
|
|
33857
|
+
continue;
|
|
33858
|
+
}
|
|
33859
|
+
if (action.kind === "switch-tab" && options.actions[index - 1]?.kind === "new-tab" && options.actions[index - 1]?.pageId === action.pageId) {
|
|
33860
|
+
continue;
|
|
33861
|
+
}
|
|
33862
|
+
switch (action.kind) {
|
|
33863
|
+
case "navigate":
|
|
33864
|
+
lines.push(` await ensureActive(${pageVar});`);
|
|
33865
|
+
lines.push(` await opensteer.goto(${JSON.stringify(action.detail.url)});`);
|
|
33866
|
+
break;
|
|
33867
|
+
case "click":
|
|
33868
|
+
lines.push(` await ensureActive(${pageVar});`);
|
|
33869
|
+
lines.push(
|
|
33870
|
+
` await opensteer.click({ selector: ${JSON.stringify(requireSelector(action))} });`
|
|
33871
|
+
);
|
|
33872
|
+
break;
|
|
33873
|
+
case "dblclick":
|
|
33874
|
+
lines.push(` await ensureActive(${pageVar});`);
|
|
33875
|
+
lines.push(
|
|
33876
|
+
` await opensteer.click({ selector: ${JSON.stringify(requireSelector(action))}, clickCount: 2 });`
|
|
33877
|
+
);
|
|
33878
|
+
break;
|
|
33879
|
+
case "keypress":
|
|
33880
|
+
lines.push(` await dispatchKey(${pageVar}, ${JSON.stringify(action.detail.key)});`);
|
|
33881
|
+
break;
|
|
33882
|
+
case "scroll": {
|
|
33883
|
+
const { direction, amount, isWindowScroll } = normalizeScrollAction(action);
|
|
33884
|
+
lines.push(` await ensureActive(${pageVar});`);
|
|
33885
|
+
if (isWindowScroll) {
|
|
33886
|
+
lines.push(` await opensteer.evaluate({`);
|
|
33887
|
+
lines.push(` pageRef: ${pageVar},`);
|
|
33888
|
+
lines.push(` script: ${JSON.stringify(WINDOW_SCROLL_SCRIPT)},`);
|
|
33889
|
+
lines.push(
|
|
33890
|
+
` args: [${String(action.detail.deltaX)}, ${String(action.detail.deltaY)}],`
|
|
33891
|
+
);
|
|
33892
|
+
lines.push(` });`);
|
|
33893
|
+
} else {
|
|
33894
|
+
lines.push(
|
|
33895
|
+
` await opensteer.scroll({ selector: ${JSON.stringify(requireSelector(action))}, direction: ${JSON.stringify(direction)}, amount: ${String(amount)} });`
|
|
33896
|
+
);
|
|
33897
|
+
}
|
|
33898
|
+
break;
|
|
33899
|
+
}
|
|
33900
|
+
case "select-option":
|
|
33901
|
+
lines.push(
|
|
33902
|
+
` await selectOption(${pageVar}, ${JSON.stringify(requireSelector(action))}, ${JSON.stringify(action.detail.value)});`
|
|
33903
|
+
);
|
|
33904
|
+
break;
|
|
33905
|
+
case "new-tab": {
|
|
33906
|
+
const openerPageVar = action.detail.openerPageId;
|
|
33907
|
+
const shouldUseWaitForPage = shouldUsePopupWait(options.actions, index, openerPageVar);
|
|
33908
|
+
const creationLine = shouldUseWaitForPage && openerPageVar !== void 0 ? ` const ${pageVar} = (await opensteer.waitForPage({ openerPageRef: ${openerPageVar}, timeoutMs: 30_000 })).pageRef;` : ` const ${pageVar} = (await opensteer.newPage(${renderNewPageInput(action.detail.openerPageId, action.detail.initialUrl)})).pageRef;`;
|
|
33909
|
+
lines.push(creationLine);
|
|
33910
|
+
lines.push(` activePageRef = ${pageVar};`);
|
|
33911
|
+
declaredPages.add(pageVar);
|
|
33912
|
+
break;
|
|
33913
|
+
}
|
|
33914
|
+
case "close-tab":
|
|
33915
|
+
lines.push(` await opensteer.closePage({ pageRef: ${pageVar} });`);
|
|
33916
|
+
lines.push(` if (activePageRef === ${pageVar}) {`);
|
|
33917
|
+
lines.push(` activePageRef = undefined;`);
|
|
33918
|
+
lines.push(` }`);
|
|
33919
|
+
break;
|
|
33920
|
+
case "switch-tab":
|
|
33921
|
+
lines.push(` await ensureActive(${pageVar});`);
|
|
33922
|
+
break;
|
|
33923
|
+
case "go-back":
|
|
33924
|
+
lines.push(` await runHistoryAction(${pageVar}, "back");`);
|
|
33925
|
+
break;
|
|
33926
|
+
case "go-forward":
|
|
33927
|
+
lines.push(` await runHistoryAction(${pageVar}, "forward");`);
|
|
33928
|
+
break;
|
|
33929
|
+
case "reload":
|
|
33930
|
+
lines.push(` await runHistoryAction(${pageVar}, "reload");`);
|
|
33931
|
+
break;
|
|
33932
|
+
}
|
|
33933
|
+
}
|
|
33934
|
+
lines.push(`} finally {`);
|
|
33935
|
+
lines.push(` await opensteer.close();`);
|
|
33936
|
+
lines.push(`}`);
|
|
33937
|
+
lines.push(``);
|
|
33938
|
+
return `${lines.join("\n")}
|
|
33939
|
+
`;
|
|
33940
|
+
}
|
|
33941
|
+
function resolveReplayTarget(options) {
|
|
33942
|
+
if (options.replayTarget !== void 0) {
|
|
33943
|
+
return options.replayTarget;
|
|
33944
|
+
}
|
|
33945
|
+
if (options.workspace !== void 0) {
|
|
33946
|
+
return {
|
|
33947
|
+
kind: "local",
|
|
33948
|
+
workspace: options.workspace
|
|
33949
|
+
};
|
|
33950
|
+
}
|
|
33951
|
+
throw new Error("Replay codegen requires either replayTarget or workspace.");
|
|
33952
|
+
}
|
|
33953
|
+
function resolveInitialPages(options) {
|
|
33954
|
+
if (options.initialPages !== void 0 && options.initialPages.length > 0) {
|
|
33955
|
+
const unique = /* @__PURE__ */ new Set();
|
|
33956
|
+
return options.initialPages.map((page) => {
|
|
33957
|
+
if (unique.has(page.pageId)) {
|
|
33958
|
+
throw new Error(`Duplicate initial page id "${page.pageId}" in recording bootstrap.`);
|
|
33959
|
+
}
|
|
33960
|
+
unique.add(page.pageId);
|
|
33961
|
+
return page;
|
|
33962
|
+
});
|
|
33963
|
+
}
|
|
33964
|
+
const startUrl = options.startUrl;
|
|
33965
|
+
if (startUrl === void 0) {
|
|
33966
|
+
throw new Error("Replay codegen requires startUrl when initialPages is not provided.");
|
|
33967
|
+
}
|
|
33968
|
+
return [
|
|
33969
|
+
{
|
|
33970
|
+
pageId: "page0",
|
|
33971
|
+
initialUrl: startUrl
|
|
33972
|
+
}
|
|
33973
|
+
];
|
|
33974
|
+
}
|
|
33975
|
+
function resolveActiveInitialPageId(options, initialPages) {
|
|
33976
|
+
if (options.activePageId !== void 0) {
|
|
33977
|
+
return options.activePageId;
|
|
33978
|
+
}
|
|
33979
|
+
return initialPages[0]?.pageId;
|
|
33980
|
+
}
|
|
33981
|
+
function renderOpensteerBootstrap(replayTarget) {
|
|
33982
|
+
if (replayTarget.kind === "local") {
|
|
33983
|
+
return [
|
|
33984
|
+
`const opensteer = new Opensteer({`,
|
|
33985
|
+
` workspace: ${JSON.stringify(replayTarget.workspace)},`,
|
|
33986
|
+
` browser: "persistent",`,
|
|
33987
|
+
`});`
|
|
33988
|
+
];
|
|
33989
|
+
}
|
|
33990
|
+
return [
|
|
33991
|
+
renderRequireEnvHelper(replayTarget),
|
|
33992
|
+
``,
|
|
33993
|
+
`const opensteer = new Opensteer({`,
|
|
33994
|
+
` provider: {`,
|
|
33995
|
+
` mode: "cloud",`,
|
|
33996
|
+
` baseUrl: requireEnv(${JSON.stringify(replayTarget.baseUrlEnvVar ?? "OPENSTEER_BASE_URL")}),`,
|
|
33997
|
+
` apiKey: requireEnv(${JSON.stringify(replayTarget.apiKeyEnvVar ?? "OPENSTEER_API_KEY")}),`,
|
|
33998
|
+
...renderCloudBrowserProfile(replayTarget),
|
|
33999
|
+
` },`,
|
|
34000
|
+
`});`
|
|
34001
|
+
];
|
|
34002
|
+
}
|
|
34003
|
+
function renderRequireEnvHelper(replayTarget) {
|
|
34004
|
+
const baseUrlEnvVar = replayTarget.baseUrlEnvVar ?? "OPENSTEER_BASE_URL";
|
|
34005
|
+
const apiKeyEnvVar = replayTarget.apiKeyEnvVar ?? "OPENSTEER_API_KEY";
|
|
34006
|
+
return [
|
|
34007
|
+
`function requireEnv(name: string): string {`,
|
|
34008
|
+
` const value = process.env[name];`,
|
|
34009
|
+
` if (typeof value === "string" && value.trim().length > 0) {`,
|
|
34010
|
+
` return value;`,
|
|
34011
|
+
` }`,
|
|
34012
|
+
` throw new Error(\`Missing environment variable \${name}. Set ${baseUrlEnvVar} and ${apiKeyEnvVar} before replaying this recording.\`);`,
|
|
34013
|
+
`}`
|
|
34014
|
+
].join("\n");
|
|
34015
|
+
}
|
|
34016
|
+
function renderCloudBrowserProfile(replayTarget) {
|
|
34017
|
+
if (replayTarget.browserProfileId === void 0) {
|
|
34018
|
+
return [];
|
|
34019
|
+
}
|
|
34020
|
+
return [
|
|
34021
|
+
` browserProfile: {`,
|
|
34022
|
+
` profileId: ${JSON.stringify(replayTarget.browserProfileId)},`,
|
|
34023
|
+
...replayTarget.reuseBrowserProfileIfActive ? [` reuseIfActive: true,`] : [],
|
|
34024
|
+
` },`
|
|
34025
|
+
];
|
|
34026
|
+
}
|
|
34027
|
+
function orderInitialPages(initialPages) {
|
|
34028
|
+
const ordered = [];
|
|
34029
|
+
const declared = /* @__PURE__ */ new Set();
|
|
34030
|
+
const remaining = [...initialPages];
|
|
34031
|
+
while (remaining.length > 0) {
|
|
34032
|
+
let advanced = false;
|
|
34033
|
+
for (let index = 0; index < remaining.length; index += 1) {
|
|
34034
|
+
const page = remaining[index];
|
|
34035
|
+
if (page.openerPageId !== void 0 && !declared.has(page.openerPageId)) {
|
|
34036
|
+
continue;
|
|
34037
|
+
}
|
|
34038
|
+
ordered.push(page);
|
|
34039
|
+
declared.add(page.pageId);
|
|
34040
|
+
remaining.splice(index, 1);
|
|
34041
|
+
advanced = true;
|
|
34042
|
+
break;
|
|
34043
|
+
}
|
|
34044
|
+
if (!advanced) {
|
|
34045
|
+
ordered.push(...remaining.splice(0, remaining.length));
|
|
34046
|
+
}
|
|
34047
|
+
}
|
|
34048
|
+
return ordered;
|
|
34049
|
+
}
|
|
34050
|
+
function requireSelector(action) {
|
|
34051
|
+
if (action.selector === void 0) {
|
|
34052
|
+
throw new Error(`Action "${action.kind}" on ${action.pageId} is missing a selector.`);
|
|
34053
|
+
}
|
|
34054
|
+
return action.selector;
|
|
34055
|
+
}
|
|
34056
|
+
function normalizeScrollAction(action) {
|
|
34057
|
+
const horizontal = Math.abs(action.detail.deltaX);
|
|
34058
|
+
const vertical = Math.abs(action.detail.deltaY);
|
|
34059
|
+
if (vertical >= horizontal) {
|
|
34060
|
+
return {
|
|
34061
|
+
direction: action.detail.deltaY < 0 ? "up" : "down",
|
|
34062
|
+
amount: Math.max(1, Math.round(vertical)),
|
|
34063
|
+
isWindowScroll: action.selector === void 0
|
|
34064
|
+
};
|
|
34065
|
+
}
|
|
34066
|
+
return {
|
|
34067
|
+
direction: action.detail.deltaX < 0 ? "left" : "right",
|
|
34068
|
+
amount: Math.max(1, Math.round(horizontal)),
|
|
34069
|
+
isWindowScroll: action.selector === void 0
|
|
34070
|
+
};
|
|
34071
|
+
}
|
|
34072
|
+
function shouldUsePopupWait(actions, newTabIndex, openerPageId) {
|
|
34073
|
+
if (openerPageId === void 0 || newTabIndex === 0) {
|
|
34074
|
+
return false;
|
|
34075
|
+
}
|
|
34076
|
+
const previousAction = actions[newTabIndex - 1];
|
|
34077
|
+
if (previousAction === void 0 || previousAction.pageId !== openerPageId) {
|
|
34078
|
+
return false;
|
|
34079
|
+
}
|
|
34080
|
+
return previousAction.kind === "click" || previousAction.kind === "dblclick" || previousAction.kind === "keypress";
|
|
34081
|
+
}
|
|
34082
|
+
function renderNewPageInput(openerPageId, initialUrl) {
|
|
34083
|
+
const argumentsList = [];
|
|
34084
|
+
if (openerPageId !== void 0) {
|
|
34085
|
+
argumentsList.push(`openerPageRef: ${openerPageId}`);
|
|
34086
|
+
}
|
|
34087
|
+
if (initialUrl.length > 0 && initialUrl !== "about:blank") {
|
|
34088
|
+
argumentsList.push(`url: ${JSON.stringify(initialUrl)}`);
|
|
34089
|
+
}
|
|
34090
|
+
if (argumentsList.length === 0) {
|
|
34091
|
+
return `{}`;
|
|
34092
|
+
}
|
|
34093
|
+
return `{ ${argumentsList.join(", ")} }`;
|
|
34094
|
+
}
|
|
34095
|
+
|
|
32294
34096
|
// src/sdk/runtime.ts
|
|
32295
34097
|
var OpensteerRuntime = class extends OpensteerSessionRuntime {
|
|
32296
34098
|
constructor(options = {}) {
|
|
@@ -32515,11 +34317,25 @@ function queryAllCookies(dbPath) {
|
|
|
32515
34317
|
FROM cookies`
|
|
32516
34318
|
);
|
|
32517
34319
|
stmt.setReadBigInts(true);
|
|
32518
|
-
return stmt.all();
|
|
34320
|
+
return stmt.all().map(toRawCookieRow);
|
|
32519
34321
|
} finally {
|
|
32520
34322
|
database.close();
|
|
32521
34323
|
}
|
|
32522
34324
|
}
|
|
34325
|
+
function toRawCookieRow(row) {
|
|
34326
|
+
return {
|
|
34327
|
+
host_key: row.host_key,
|
|
34328
|
+
name: row.name,
|
|
34329
|
+
value: row.value,
|
|
34330
|
+
encrypted_value: row.encrypted_value,
|
|
34331
|
+
path: row.path,
|
|
34332
|
+
expires_utc: row.expires_utc,
|
|
34333
|
+
is_secure: row.is_secure,
|
|
34334
|
+
is_httponly: row.is_httponly,
|
|
34335
|
+
samesite: row.samesite,
|
|
34336
|
+
is_persistent: row.is_persistent
|
|
34337
|
+
};
|
|
34338
|
+
}
|
|
32523
34339
|
async function resolveDecryptionKey(brandId, userDataDir) {
|
|
32524
34340
|
if (process.platform === "darwin") {
|
|
32525
34341
|
const password = await resolveKeychainPassword(brandId);
|
|
@@ -32870,7 +34686,11 @@ var OpensteerCloudClient = class {
|
|
|
32870
34686
|
...input.browser === void 0 ? {} : { browser: input.browser },
|
|
32871
34687
|
...input.context === void 0 ? {} : { context: input.context },
|
|
32872
34688
|
...input.browserProfile === void 0 ? {} : { browserProfile: input.browserProfile },
|
|
32873
|
-
...input.observability === void 0 ? {} : { observability: input.observability }
|
|
34689
|
+
...input.observability === void 0 ? {} : { observability: input.observability },
|
|
34690
|
+
...input.sourceType === void 0 ? {} : { sourceType: input.sourceType },
|
|
34691
|
+
...input.sourceRef === void 0 ? {} : { sourceRef: input.sourceRef },
|
|
34692
|
+
...input.localWorkspaceRootPath === void 0 ? {} : { localWorkspaceRootPath: input.localWorkspaceRootPath },
|
|
34693
|
+
...input.locality === void 0 ? {} : { locality: input.locality }
|
|
32874
34694
|
}
|
|
32875
34695
|
});
|
|
32876
34696
|
return await response.json();
|
|
@@ -32896,6 +34716,30 @@ var OpensteerCloudClient = class {
|
|
|
32896
34716
|
});
|
|
32897
34717
|
return await response.json();
|
|
32898
34718
|
}
|
|
34719
|
+
async getSessionRecording(sessionId) {
|
|
34720
|
+
const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}/recording`, {
|
|
34721
|
+
method: "GET"
|
|
34722
|
+
});
|
|
34723
|
+
return await response.json();
|
|
34724
|
+
}
|
|
34725
|
+
async startSessionRecording(sessionId) {
|
|
34726
|
+
const response = await this.request(
|
|
34727
|
+
`/v1/sessions/${encodeURIComponent(sessionId)}/recording/start`,
|
|
34728
|
+
{
|
|
34729
|
+
method: "POST"
|
|
34730
|
+
}
|
|
34731
|
+
);
|
|
34732
|
+
return await response.json();
|
|
34733
|
+
}
|
|
34734
|
+
async stopSessionRecording(sessionId) {
|
|
34735
|
+
const response = await this.request(
|
|
34736
|
+
`/v1/sessions/${encodeURIComponent(sessionId)}/recording/stop`,
|
|
34737
|
+
{
|
|
34738
|
+
method: "POST"
|
|
34739
|
+
}
|
|
34740
|
+
);
|
|
34741
|
+
return await response.json();
|
|
34742
|
+
}
|
|
32899
34743
|
async closeSession(sessionId) {
|
|
32900
34744
|
const response = await this.request(`/v1/sessions/${encodeURIComponent(sessionId)}`, {
|
|
32901
34745
|
method: "DELETE"
|
|
@@ -33042,12 +34886,12 @@ var OpensteerCloudClient = class {
|
|
|
33042
34886
|
`Unexpected cloud session status "${String(session.status)}" while waiting for close.`
|
|
33043
34887
|
);
|
|
33044
34888
|
}
|
|
33045
|
-
await
|
|
34889
|
+
await delay2(CLOUD_CLOSE_POLL_INTERVAL_MS);
|
|
33046
34890
|
}
|
|
33047
34891
|
throw new Error(`Timed out waiting for cloud session ${sessionId} to close.`);
|
|
33048
34892
|
}
|
|
33049
34893
|
};
|
|
33050
|
-
function
|
|
34894
|
+
function delay2(ms) {
|
|
33051
34895
|
return new Promise((resolve4) => {
|
|
33052
34896
|
setTimeout(resolve4, ms);
|
|
33053
34897
|
});
|
|
@@ -33086,9 +34930,11 @@ function resolveCloudConfig(input = {}) {
|
|
|
33086
34930
|
if (!baseUrl || baseUrl.trim().length === 0) {
|
|
33087
34931
|
throw new Error("provider=cloud requires OPENSTEER_BASE_URL or provider.baseUrl.");
|
|
33088
34932
|
}
|
|
34933
|
+
const appBaseUrl = cloudProvider?.appBaseUrl ?? input.environment?.OPENSTEER_CLOUD_APP_BASE_URL;
|
|
33089
34934
|
return {
|
|
33090
34935
|
apiKey: apiKey.trim(),
|
|
33091
34936
|
baseUrl: baseUrl.trim().replace(/\/+$/, ""),
|
|
34937
|
+
...appBaseUrl === void 0 || appBaseUrl.trim().length === 0 ? {} : { appBaseUrl: appBaseUrl.trim().replace(/\/+$/, "") },
|
|
33092
34938
|
...cloudProvider?.browserProfile === void 0 ? {} : { browserProfile: cloudProvider.browserProfile }
|
|
33093
34939
|
};
|
|
33094
34940
|
}
|
|
@@ -33107,6 +34953,9 @@ var OpensteerSemanticRestClient = class {
|
|
|
33107
34953
|
this.connection = connection;
|
|
33108
34954
|
}
|
|
33109
34955
|
async invoke(operation, input) {
|
|
34956
|
+
return this.invokeInternal(operation, input, false);
|
|
34957
|
+
}
|
|
34958
|
+
async invokeInternal(operation, input, hasRetried) {
|
|
33110
34959
|
const endpoint = opensteerSemanticRestEndpoints.find((entry) => entry.name === operation);
|
|
33111
34960
|
if (!endpoint) {
|
|
33112
34961
|
throw new Error(`unsupported semantic operation ${operation}`);
|
|
@@ -33116,7 +34965,7 @@ var OpensteerSemanticRestClient = class {
|
|
|
33116
34965
|
});
|
|
33117
34966
|
let response;
|
|
33118
34967
|
try {
|
|
33119
|
-
response = await fetch(`${this.connection.
|
|
34968
|
+
response = await fetch(`${await this.connection.getBaseUrl()}${endpoint.path}`, {
|
|
33120
34969
|
method: "POST",
|
|
33121
34970
|
headers: {
|
|
33122
34971
|
authorization: await this.connection.getAuthorizationHeader(),
|
|
@@ -33138,6 +34987,9 @@ var OpensteerSemanticRestClient = class {
|
|
|
33138
34987
|
}
|
|
33139
34988
|
return envelope.data;
|
|
33140
34989
|
} catch (error) {
|
|
34990
|
+
if (!hasRetried && this.connection.handleError && await this.connection.handleError(error, { operation })) {
|
|
34991
|
+
return this.invokeInternal(operation, input, true);
|
|
34992
|
+
}
|
|
33141
34993
|
if (operation === "session.close" && isFetchFailure(error)) {
|
|
33142
34994
|
return { closed: true };
|
|
33143
34995
|
}
|
|
@@ -33271,7 +35123,10 @@ var OpensteerCloudAutomationClient = class {
|
|
|
33271
35123
|
}
|
|
33272
35124
|
async connect() {
|
|
33273
35125
|
const grant = await this.issueGrant("automation");
|
|
33274
|
-
|
|
35126
|
+
if (grant.transport !== "ws") {
|
|
35127
|
+
throw new Error(`cloud issued an invalid ${grant.kind} grant transport`);
|
|
35128
|
+
}
|
|
35129
|
+
const wsUrl = new URL(grant.url);
|
|
33275
35130
|
wsUrl.searchParams.set("token", grant.token);
|
|
33276
35131
|
const socket = new WebSocket2(wsUrl);
|
|
33277
35132
|
this.socket = socket;
|
|
@@ -33636,7 +35491,7 @@ var CloudSessionProxy = class {
|
|
|
33636
35491
|
cloud;
|
|
33637
35492
|
observability;
|
|
33638
35493
|
sessionId;
|
|
33639
|
-
|
|
35494
|
+
semanticGrant;
|
|
33640
35495
|
client;
|
|
33641
35496
|
automation;
|
|
33642
35497
|
workspaceStore;
|
|
@@ -33687,7 +35542,7 @@ var CloudSessionProxy = class {
|
|
|
33687
35542
|
reconnectable: this.workspace !== void 0 || this.sessionId !== void 0 || persisted !== void 0,
|
|
33688
35543
|
capabilities: {
|
|
33689
35544
|
semanticOperations: opensteerSemanticOperationNames,
|
|
33690
|
-
sessionGrants: ["automation", "view", "cdp"],
|
|
35545
|
+
sessionGrants: ["semantic", "automation", "view", "cdp"],
|
|
33691
35546
|
instrumentation: {
|
|
33692
35547
|
route: true,
|
|
33693
35548
|
interceptScript: true,
|
|
@@ -33929,13 +35784,12 @@ var CloudSessionProxy = class {
|
|
|
33929
35784
|
return this.requireClient().invoke("computer.execute", input);
|
|
33930
35785
|
}
|
|
33931
35786
|
async close() {
|
|
33932
|
-
const session = await this.loadPersistedSession() ?? (this.sessionId === void 0
|
|
35787
|
+
const session = await this.loadPersistedSession() ?? (this.sessionId === void 0 ? void 0 : {
|
|
33933
35788
|
layout: "opensteer-session",
|
|
33934
35789
|
version: 1,
|
|
33935
35790
|
provider: "cloud",
|
|
33936
35791
|
...this.workspace === void 0 ? {} : { workspace: this.workspace },
|
|
33937
35792
|
sessionId: this.sessionId,
|
|
33938
|
-
baseUrl: this.sessionBaseUrl,
|
|
33939
35793
|
startedAt: Date.now(),
|
|
33940
35794
|
updatedAt: Date.now()
|
|
33941
35795
|
});
|
|
@@ -33954,7 +35808,7 @@ var CloudSessionProxy = class {
|
|
|
33954
35808
|
this.automation = void 0;
|
|
33955
35809
|
this.client = void 0;
|
|
33956
35810
|
this.sessionId = void 0;
|
|
33957
|
-
this.
|
|
35811
|
+
this.semanticGrant = void 0;
|
|
33958
35812
|
if (this.cleanupRootOnClose) {
|
|
33959
35813
|
await rm(this.rootPath, { recursive: true, force: true }).catch(() => void 0);
|
|
33960
35814
|
}
|
|
@@ -33970,39 +35824,56 @@ var CloudSessionProxy = class {
|
|
|
33970
35824
|
await this.automation?.close().catch(() => void 0);
|
|
33971
35825
|
this.automation = void 0;
|
|
33972
35826
|
this.sessionId = void 0;
|
|
33973
|
-
this.
|
|
35827
|
+
this.semanticGrant = void 0;
|
|
33974
35828
|
}
|
|
33975
35829
|
async ensureSession(input = {}) {
|
|
33976
35830
|
if (this.client) {
|
|
33977
35831
|
return;
|
|
33978
35832
|
}
|
|
33979
35833
|
assertSupportedCloudBrowserMode(input.browser);
|
|
35834
|
+
const localCloud = this.shouldUseLocalCloudTransport();
|
|
35835
|
+
const browserProfile = resolveCloudBrowserProfile(this.cloud, input);
|
|
33980
35836
|
const persisted = await this.loadPersistedSession();
|
|
33981
35837
|
if (persisted !== void 0 && await this.isReusableCloudSession(persisted.sessionId)) {
|
|
33982
|
-
|
|
35838
|
+
if (localCloud) {
|
|
35839
|
+
void this.syncRegistryToCloud();
|
|
35840
|
+
} else {
|
|
35841
|
+
await this.syncRegistryToCloud();
|
|
35842
|
+
}
|
|
33983
35843
|
this.bindClient(persisted);
|
|
33984
35844
|
return;
|
|
33985
35845
|
}
|
|
33986
|
-
|
|
33987
|
-
|
|
35846
|
+
if (localCloud) {
|
|
35847
|
+
void this.syncRegistryToCloud();
|
|
35848
|
+
} else {
|
|
35849
|
+
await this.syncRegistryToCloud();
|
|
35850
|
+
}
|
|
35851
|
+
const baseCreateInput = {
|
|
33988
35852
|
...this.workspace === void 0 ? {} : { name: this.workspace },
|
|
33989
35853
|
...input.launch === void 0 ? {} : { browser: input.launch },
|
|
33990
35854
|
...input.context === void 0 ? {} : { context: input.context },
|
|
33991
35855
|
...this.observability === void 0 ? {} : { observability: this.observability },
|
|
33992
|
-
...
|
|
33993
|
-
}
|
|
35856
|
+
...browserProfile === void 0 ? {} : { browserProfile }
|
|
35857
|
+
};
|
|
35858
|
+
const createInput = localCloud && this.workspace !== void 0 ? {
|
|
35859
|
+
...baseCreateInput,
|
|
35860
|
+
sourceType: "local-cloud",
|
|
35861
|
+
sourceRef: this.workspace,
|
|
35862
|
+
localWorkspaceRootPath: this.rootPath,
|
|
35863
|
+
locality: "auto"
|
|
35864
|
+
} : baseCreateInput;
|
|
35865
|
+
const session = await this.cloud.createSession(createInput);
|
|
33994
35866
|
const record = {
|
|
33995
35867
|
layout: "opensteer-session",
|
|
33996
35868
|
version: 1,
|
|
33997
35869
|
provider: "cloud",
|
|
33998
35870
|
...this.workspace === void 0 ? {} : { workspace: this.workspace },
|
|
33999
35871
|
sessionId: session.sessionId,
|
|
34000
|
-
baseUrl: session.baseUrl,
|
|
34001
35872
|
startedAt: Date.now(),
|
|
34002
35873
|
updatedAt: Date.now()
|
|
34003
35874
|
};
|
|
34004
35875
|
await this.writePersistedSession(record);
|
|
34005
|
-
this.bindClient(record);
|
|
35876
|
+
this.bindClient(record, session.initialGrants?.semantic);
|
|
34006
35877
|
}
|
|
34007
35878
|
async syncRegistryToCloud() {
|
|
34008
35879
|
if (this.workspace === void 0) {
|
|
@@ -34011,15 +35882,16 @@ var CloudSessionProxy = class {
|
|
|
34011
35882
|
try {
|
|
34012
35883
|
const workspaceStore = await this.ensureWorkspaceStore();
|
|
34013
35884
|
await syncLocalRegistryToCloud(this.cloud, this.workspace, workspaceStore);
|
|
34014
|
-
} catch
|
|
35885
|
+
} catch {
|
|
34015
35886
|
}
|
|
34016
35887
|
}
|
|
34017
|
-
bindClient(record) {
|
|
35888
|
+
bindClient(record, initialSemanticGrant) {
|
|
34018
35889
|
this.sessionId = record.sessionId;
|
|
34019
|
-
this.
|
|
35890
|
+
this.semanticGrant = initialSemanticGrant?.kind === "semantic" ? initialSemanticGrant : void 0;
|
|
34020
35891
|
this.client = new OpensteerSemanticRestClient({
|
|
34021
|
-
|
|
34022
|
-
getAuthorizationHeader: async () => this.
|
|
35892
|
+
getBaseUrl: async () => (await this.ensureSemanticGrant()).url,
|
|
35893
|
+
getAuthorizationHeader: async () => `Bearer ${(await this.ensureSemanticGrant()).token}`,
|
|
35894
|
+
handleError: (error) => this.handleSemanticClientError(error)
|
|
34023
35895
|
});
|
|
34024
35896
|
this.automation = new OpensteerCloudAutomationClient(this.cloud, record.sessionId);
|
|
34025
35897
|
}
|
|
@@ -34068,6 +35940,43 @@ var CloudSessionProxy = class {
|
|
|
34068
35940
|
}
|
|
34069
35941
|
return this.automation;
|
|
34070
35942
|
}
|
|
35943
|
+
async ensureSemanticGrant(forceRefresh = false) {
|
|
35944
|
+
if (!forceRefresh && this.semanticGrant?.kind === "semantic" && this.semanticGrant.expiresAt > Date.now() + 1e4) {
|
|
35945
|
+
return this.semanticGrant;
|
|
35946
|
+
}
|
|
35947
|
+
if (!this.sessionId) {
|
|
35948
|
+
throw new Error("Cloud session has not been initialized.");
|
|
35949
|
+
}
|
|
35950
|
+
const issued = await this.cloud.issueAccess(this.sessionId, ["semantic"]);
|
|
35951
|
+
const grant = issued.grants.semantic;
|
|
35952
|
+
if (!grant || grant.transport !== "http") {
|
|
35953
|
+
throw new Error("cloud did not issue a valid semantic grant");
|
|
35954
|
+
}
|
|
35955
|
+
this.semanticGrant = grant;
|
|
35956
|
+
return grant;
|
|
35957
|
+
}
|
|
35958
|
+
async handleSemanticClientError(error) {
|
|
35959
|
+
if (!(error instanceof OpensteerSemanticRestError)) {
|
|
35960
|
+
return false;
|
|
35961
|
+
}
|
|
35962
|
+
if (error.statusCode !== 401 && error.statusCode !== 404) {
|
|
35963
|
+
return false;
|
|
35964
|
+
}
|
|
35965
|
+
this.semanticGrant = void 0;
|
|
35966
|
+
try {
|
|
35967
|
+
await this.ensureSemanticGrant(true);
|
|
35968
|
+
return true;
|
|
35969
|
+
} catch {
|
|
35970
|
+
return false;
|
|
35971
|
+
}
|
|
35972
|
+
}
|
|
35973
|
+
shouldUseLocalCloudTransport() {
|
|
35974
|
+
if (this.workspace === void 0) {
|
|
35975
|
+
return false;
|
|
35976
|
+
}
|
|
35977
|
+
const config = this.cloud.getConfig();
|
|
35978
|
+
return isLoopbackBaseUrl(config.baseUrl);
|
|
35979
|
+
}
|
|
34071
35980
|
};
|
|
34072
35981
|
function resolveCloudBrowserProfile(cloud, input) {
|
|
34073
35982
|
return input.browserProfile ?? cloud.getConfig().browserProfile;
|
|
@@ -34083,6 +35992,15 @@ function assertSupportedCloudBrowserMode(browser) {
|
|
|
34083
35992
|
function isMissingCloudSessionError(error) {
|
|
34084
35993
|
return error instanceof Error && /\b404\b/.test(error.message);
|
|
34085
35994
|
}
|
|
35995
|
+
function isLoopbackBaseUrl(baseUrl) {
|
|
35996
|
+
let url;
|
|
35997
|
+
try {
|
|
35998
|
+
url = new URL(baseUrl);
|
|
35999
|
+
} catch {
|
|
36000
|
+
return false;
|
|
36001
|
+
}
|
|
36002
|
+
return url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1" || url.hostname === "[::1]";
|
|
36003
|
+
}
|
|
34086
36004
|
|
|
34087
36005
|
// src/sdk/runtime-resolution.ts
|
|
34088
36006
|
function resolveOpensteerRuntimeConfig(input = {}) {
|
|
@@ -34227,6 +36145,6 @@ function isOpensteerEnvironmentKey(key) {
|
|
|
34227
36145
|
return key.startsWith(OPENSTEER_ENV_PREFIX);
|
|
34228
36146
|
}
|
|
34229
36147
|
|
|
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-
|
|
36148
|
+
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 };
|
|
36149
|
+
//# sourceMappingURL=chunk-33FDEOQY.js.map
|
|
36150
|
+
//# sourceMappingURL=chunk-33FDEOQY.js.map
|