@web-auto/camo 0.1.26 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +586 -586
- package/bin/browser-service.mjs +11 -11
- package/bin/camo.mjs +22 -22
- package/package.json +48 -48
- package/scripts/build.mjs +19 -19
- package/scripts/bump-version.mjs +34 -34
- package/scripts/check-file-size.mjs +80 -80
- package/scripts/file-size-policy.json +12 -2
- package/scripts/install.mjs +76 -76
- package/scripts/release.sh +54 -54
- package/src/autoscript/action-providers/index.mjs +6 -6
- package/src/autoscript/impact-engine.mjs +78 -78
- package/src/autoscript/runtime.mjs +1017 -1017
- package/src/autoscript/schema.mjs +376 -376
- package/src/cli.mjs +405 -405
- package/src/commands/attach.mjs +141 -141
- package/src/commands/autoscript.mjs +1011 -1011
- package/src/commands/browser.mjs +1255 -1257
- package/src/commands/container.mjs +401 -401
- package/src/commands/cookies.mjs +69 -69
- package/src/commands/create.mjs +98 -98
- package/src/commands/devtools.mjs +349 -349
- package/src/commands/events.mjs +152 -152
- package/src/commands/highlight-mode.mjs +24 -24
- package/src/commands/init.mjs +68 -68
- package/src/commands/lifecycle.mjs +275 -275
- package/src/commands/mouse.mjs +45 -45
- package/src/commands/profile.mjs +46 -46
- package/src/commands/record.mjs +115 -115
- package/src/commands/system.mjs +14 -14
- package/src/commands/window.mjs +123 -123
- package/src/container/change-notifier.mjs +362 -362
- package/src/container/element-filter.mjs +143 -143
- package/src/container/index.mjs +3 -3
- package/src/container/runtime-core/checkpoint.mjs +209 -209
- package/src/container/runtime-core/index.mjs +21 -21
- package/src/container/runtime-core/operations/index.mjs +774 -774
- package/src/container/runtime-core/operations/selector-scripts.mjs +277 -277
- package/src/container/runtime-core/operations/tab-pool.mjs +746 -746
- package/src/container/runtime-core/operations/viewport.mjs +189 -189
- package/src/container/runtime-core/search.mjs +190 -190
- package/src/container/runtime-core/subscription.mjs +224 -224
- package/src/container/runtime-core/utils.mjs +94 -94
- package/src/container/runtime-core/validation.mjs +127 -184
- package/src/container/runtime-core.mjs +1 -1
- package/src/container/subscription-registry.mjs +459 -459
- package/src/core/actions.mjs +561 -561
- package/src/core/browser.mjs +266 -266
- package/src/core/index.mjs +52 -52
- package/src/core/utils.mjs +91 -91
- package/src/events/daemon-entry.mjs +33 -33
- package/src/events/daemon.mjs +80 -80
- package/src/events/progress-log.mjs +109 -109
- package/src/events/ws-server.mjs +239 -239
- package/src/lib/client.mjs +200 -200
- package/src/lifecycle/cleanup.mjs +83 -83
- package/src/lifecycle/lock.mjs +126 -126
- package/src/lifecycle/session-registry.mjs +279 -279
- package/src/lifecycle/session-view.mjs +76 -76
- package/src/lifecycle/session-watchdog.mjs +281 -281
- package/src/services/browser-service/index.js +671 -674
- package/src/services/browser-service/internal/BrowserSession.input.test.js +389 -389
- package/src/services/browser-service/internal/BrowserSession.js +325 -336
- package/src/services/browser-service/internal/ElementRegistry.js +60 -60
- package/src/services/browser-service/internal/ProfileLock.js +84 -84
- package/src/services/browser-service/internal/SessionManager.js +184 -184
- package/src/services/browser-service/internal/SessionManager.test.js +39 -39
- package/src/services/browser-service/internal/browser-session/cookies.js +144 -144
- package/src/services/browser-service/internal/browser-session/input-ops.js +222 -219
- package/src/services/browser-service/internal/browser-session/input-pipeline.js +144 -144
- package/src/services/browser-service/internal/browser-session/logging.js +46 -46
- package/src/services/browser-service/internal/browser-session/navigation.js +38 -38
- package/src/services/browser-service/internal/browser-session/page-hooks.js +442 -442
- package/src/services/browser-service/internal/browser-session/page-management.js +302 -336
- package/src/services/browser-service/internal/browser-session/page-management.test.js +148 -148
- package/src/services/browser-service/internal/browser-session/recording.js +198 -198
- package/src/services/browser-service/internal/browser-session/runtime-events.js +61 -61
- package/src/services/browser-service/internal/browser-session/session-core.js +84 -84
- package/src/services/browser-service/internal/browser-session/session-state.js +38 -38
- package/src/services/browser-service/internal/browser-session/types.js +14 -14
- package/src/services/browser-service/internal/browser-session/utils.js +95 -95
- package/src/services/browser-service/internal/browser-session/viewport-manager.js +46 -46
- package/src/services/browser-service/internal/browser-session/viewport.js +215 -215
- package/src/services/browser-service/internal/container-matcher.js +851 -851
- package/src/services/browser-service/internal/container-registry.js +182 -182
- package/src/services/browser-service/internal/engine-manager.js +259 -259
- package/src/services/browser-service/internal/fingerprint.js +203 -203
- package/src/services/browser-service/internal/heartbeat.js +137 -137
- package/src/services/browser-service/internal/logging.js +46 -46
- package/src/services/browser-service/internal/page-runtime/runtime.js +1317 -1317
- package/src/services/browser-service/internal/pageRuntime.js +28 -28
- package/src/services/browser-service/internal/runtimeInjector.js +31 -31
- package/src/services/browser-service/internal/service-process-logger.js +140 -140
- package/src/services/browser-service/internal/state-bus.js +45 -45
- package/src/services/browser-service/internal/storage-paths.js +42 -42
- package/src/services/browser-service/internal/ws-server.js +1194 -1194
- package/src/services/browser-service/internal/ws-server.test.js +58 -58
- package/src/services/browser-service/server.mjs +6 -6
- package/src/services/controller/cli-bridge.js +93 -93
- package/src/services/controller/container-index.js +50 -50
- package/src/services/controller/container-storage.js +36 -36
- package/src/services/controller/controller-actions.js +207 -207
- package/src/services/controller/controller.js +1138 -1138
- package/src/services/controller/selectors.js +54 -54
- package/src/services/controller/transport.js +125 -125
- package/src/utils/args.mjs +26 -26
- package/src/utils/browser-service.mjs +544 -544
- package/src/utils/command-log.mjs +64 -64
- package/src/utils/config.mjs +214 -214
- package/src/utils/fingerprint.mjs +181 -181
- package/src/utils/help.mjs +216 -216
- package/src/utils/js-policy.mjs +13 -13
- package/src/utils/ws-client.mjs +30 -30
- package/src/container/runtime-core/operations/tab-pool.mjs.bak +0 -762
- package/src/container/runtime-core/operations/tab-pool.mjs.syntax-error +0 -762
- package/src/services/browser-service/index.js.bak +0 -671
|
@@ -1,85 +1,85 @@
|
|
|
1
|
-
import { ProfileLock } from '../profile-lock';
|
|
2
|
-
import { resolveProfilesRoot } from '../paths';
|
|
3
|
-
export class BrowserSessionCore {
|
|
4
|
-
options;
|
|
5
|
-
browser;
|
|
6
|
-
context;
|
|
7
|
-
page;
|
|
8
|
-
lock;
|
|
9
|
-
profileDir;
|
|
10
|
-
lastKnownUrl = null;
|
|
11
|
-
mode = 'dev';
|
|
12
|
-
recording = {
|
|
13
|
-
active: false,
|
|
14
|
-
enabled: false,
|
|
15
|
-
name: null,
|
|
16
|
-
outputPath: null,
|
|
17
|
-
overlay: false,
|
|
18
|
-
startedAt: null,
|
|
19
|
-
endedAt: null,
|
|
20
|
-
eventCount: 0,
|
|
21
|
-
lastEventAt: null,
|
|
22
|
-
lastError: null,
|
|
23
|
-
};
|
|
24
|
-
exitNotified = false;
|
|
25
|
-
runtimeObservers = new Set();
|
|
26
|
-
onExit;
|
|
27
|
-
constructor(options) {
|
|
28
|
-
this.options = options;
|
|
29
|
-
const profileId = options.profileId || 'default';
|
|
30
|
-
const root = resolveProfilesRoot();
|
|
31
|
-
this.profileDir = `${root}/${profileId}`;
|
|
32
|
-
fs.mkdirSync(this.profileDir, { recursive: true });
|
|
33
|
-
this.lock = new ProfileLock(profileId);
|
|
34
|
-
}
|
|
35
|
-
get id() {
|
|
36
|
-
return this.options.profileId;
|
|
37
|
-
}
|
|
38
|
-
get currentPage() {
|
|
39
|
-
return this.page;
|
|
40
|
-
}
|
|
41
|
-
get modeName() {
|
|
42
|
-
return this.mode;
|
|
43
|
-
}
|
|
44
|
-
setMode(next = 'dev') {
|
|
45
|
-
this.mode = next === 'run' ? 'run' : 'dev';
|
|
46
|
-
}
|
|
47
|
-
getInfo() {
|
|
48
|
-
return {
|
|
49
|
-
session_id: this.options.profileId,
|
|
50
|
-
profileId: this.options.profileId,
|
|
51
|
-
current_url: this.getCurrentUrl(),
|
|
52
|
-
mode: this.mode,
|
|
53
|
-
headless: !!this.options.headless,
|
|
54
|
-
recording: this.getRecordingStatus(),
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
getRecordingStatus() {
|
|
58
|
-
return { ...this.recording };
|
|
59
|
-
}
|
|
60
|
-
async close() {
|
|
61
|
-
try {
|
|
62
|
-
await this.stopRecording({ reason: 'session_close' }).catch(() => { });
|
|
63
|
-
await this.context?.close();
|
|
64
|
-
}
|
|
65
|
-
finally {
|
|
66
|
-
await this.browser?.close();
|
|
67
|
-
this.lock.release();
|
|
68
|
-
this.runtimeObservers.clear();
|
|
69
|
-
this.notifyExit();
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
notifyExit() {
|
|
73
|
-
if (this.exitNotified)
|
|
74
|
-
return;
|
|
75
|
-
this.exitNotified = true;
|
|
76
|
-
this.onExit?.(this.options.profileId);
|
|
77
|
-
}
|
|
78
|
-
addRuntimeEventObserver(observer) {
|
|
79
|
-
this.runtimeObservers.add(observer);
|
|
80
|
-
return () => {
|
|
81
|
-
this.runtimeObservers.delete(observer);
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
}
|
|
1
|
+
import { ProfileLock } from '../profile-lock';
|
|
2
|
+
import { resolveProfilesRoot } from '../paths';
|
|
3
|
+
export class BrowserSessionCore {
|
|
4
|
+
options;
|
|
5
|
+
browser;
|
|
6
|
+
context;
|
|
7
|
+
page;
|
|
8
|
+
lock;
|
|
9
|
+
profileDir;
|
|
10
|
+
lastKnownUrl = null;
|
|
11
|
+
mode = 'dev';
|
|
12
|
+
recording = {
|
|
13
|
+
active: false,
|
|
14
|
+
enabled: false,
|
|
15
|
+
name: null,
|
|
16
|
+
outputPath: null,
|
|
17
|
+
overlay: false,
|
|
18
|
+
startedAt: null,
|
|
19
|
+
endedAt: null,
|
|
20
|
+
eventCount: 0,
|
|
21
|
+
lastEventAt: null,
|
|
22
|
+
lastError: null,
|
|
23
|
+
};
|
|
24
|
+
exitNotified = false;
|
|
25
|
+
runtimeObservers = new Set();
|
|
26
|
+
onExit;
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.options = options;
|
|
29
|
+
const profileId = options.profileId || 'default';
|
|
30
|
+
const root = resolveProfilesRoot();
|
|
31
|
+
this.profileDir = `${root}/${profileId}`;
|
|
32
|
+
fs.mkdirSync(this.profileDir, { recursive: true });
|
|
33
|
+
this.lock = new ProfileLock(profileId);
|
|
34
|
+
}
|
|
35
|
+
get id() {
|
|
36
|
+
return this.options.profileId;
|
|
37
|
+
}
|
|
38
|
+
get currentPage() {
|
|
39
|
+
return this.page;
|
|
40
|
+
}
|
|
41
|
+
get modeName() {
|
|
42
|
+
return this.mode;
|
|
43
|
+
}
|
|
44
|
+
setMode(next = 'dev') {
|
|
45
|
+
this.mode = next === 'run' ? 'run' : 'dev';
|
|
46
|
+
}
|
|
47
|
+
getInfo() {
|
|
48
|
+
return {
|
|
49
|
+
session_id: this.options.profileId,
|
|
50
|
+
profileId: this.options.profileId,
|
|
51
|
+
current_url: this.getCurrentUrl(),
|
|
52
|
+
mode: this.mode,
|
|
53
|
+
headless: !!this.options.headless,
|
|
54
|
+
recording: this.getRecordingStatus(),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
getRecordingStatus() {
|
|
58
|
+
return { ...this.recording };
|
|
59
|
+
}
|
|
60
|
+
async close() {
|
|
61
|
+
try {
|
|
62
|
+
await this.stopRecording({ reason: 'session_close' }).catch(() => { });
|
|
63
|
+
await this.context?.close();
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
await this.browser?.close();
|
|
67
|
+
this.lock.release();
|
|
68
|
+
this.runtimeObservers.clear();
|
|
69
|
+
this.notifyExit();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
notifyExit() {
|
|
73
|
+
if (this.exitNotified)
|
|
74
|
+
return;
|
|
75
|
+
this.exitNotified = true;
|
|
76
|
+
this.onExit?.(this.options.profileId);
|
|
77
|
+
}
|
|
78
|
+
addRuntimeEventObserver(observer) {
|
|
79
|
+
this.runtimeObservers.add(observer);
|
|
80
|
+
return () => {
|
|
81
|
+
this.runtimeObservers.delete(observer);
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
85
|
//# sourceMappingURL=session-core.js.map
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import { createEmptyRecordingState } from './types.js';
|
|
2
|
-
export function createInitialSessionState(options) {
|
|
3
|
-
return {
|
|
4
|
-
options,
|
|
5
|
-
browser: undefined,
|
|
6
|
-
context: undefined,
|
|
7
|
-
page: undefined,
|
|
8
|
-
mode: 'dev',
|
|
9
|
-
lastKnownUrl: null,
|
|
10
|
-
lastCookieSignature: null,
|
|
11
|
-
lastCookieSaveTs: 0,
|
|
12
|
-
lastViewport: {
|
|
13
|
-
lastViewport: null,
|
|
14
|
-
followWindowViewport: !options.headless,
|
|
15
|
-
},
|
|
16
|
-
fingerprint: null,
|
|
17
|
-
recording: createEmptyRecordingState(),
|
|
18
|
-
recordingStream: null,
|
|
19
|
-
exitNotified: false,
|
|
20
|
-
wheelMode: String(process.env.CAMO_SCROLL_INPUT_MODE || '').trim().toLowerCase() === 'keyboard'
|
|
21
|
-
? 'keyboard'
|
|
22
|
-
: 'wheel',
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
export function getActivePage(state) {
|
|
26
|
-
if (state.page && !state.page.isClosed()) {
|
|
27
|
-
return state.page;
|
|
28
|
-
}
|
|
29
|
-
if (!state.context)
|
|
30
|
-
return null;
|
|
31
|
-
const alive = state.context.pages().find((p) => !p.isClosed());
|
|
32
|
-
if (alive) {
|
|
33
|
-
state.page = alive;
|
|
34
|
-
return alive;
|
|
35
|
-
}
|
|
36
|
-
state.page = undefined;
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
1
|
+
import { createEmptyRecordingState } from './types.js';
|
|
2
|
+
export function createInitialSessionState(options) {
|
|
3
|
+
return {
|
|
4
|
+
options,
|
|
5
|
+
browser: undefined,
|
|
6
|
+
context: undefined,
|
|
7
|
+
page: undefined,
|
|
8
|
+
mode: 'dev',
|
|
9
|
+
lastKnownUrl: null,
|
|
10
|
+
lastCookieSignature: null,
|
|
11
|
+
lastCookieSaveTs: 0,
|
|
12
|
+
lastViewport: {
|
|
13
|
+
lastViewport: null,
|
|
14
|
+
followWindowViewport: !options.headless,
|
|
15
|
+
},
|
|
16
|
+
fingerprint: null,
|
|
17
|
+
recording: createEmptyRecordingState(),
|
|
18
|
+
recordingStream: null,
|
|
19
|
+
exitNotified: false,
|
|
20
|
+
wheelMode: String(process.env.CAMO_SCROLL_INPUT_MODE || '').trim().toLowerCase() === 'keyboard'
|
|
21
|
+
? 'keyboard'
|
|
22
|
+
: 'wheel',
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function getActivePage(state) {
|
|
26
|
+
if (state.page && !state.page.isClosed()) {
|
|
27
|
+
return state.page;
|
|
28
|
+
}
|
|
29
|
+
if (!state.context)
|
|
30
|
+
return null;
|
|
31
|
+
const alive = state.context.pages().find((p) => !p.isClosed());
|
|
32
|
+
if (alive) {
|
|
33
|
+
state.page = alive;
|
|
34
|
+
return alive;
|
|
35
|
+
}
|
|
36
|
+
state.page = undefined;
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
39
|
//# sourceMappingURL=session-state.js.map
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
export function createEmptyRecordingState() {
|
|
2
|
-
return {
|
|
3
|
-
active: false,
|
|
4
|
-
enabled: false,
|
|
5
|
-
name: null,
|
|
6
|
-
outputPath: null,
|
|
7
|
-
overlay: false,
|
|
8
|
-
startedAt: null,
|
|
9
|
-
endedAt: null,
|
|
10
|
-
eventCount: 0,
|
|
11
|
-
lastEventAt: null,
|
|
12
|
-
lastError: null,
|
|
13
|
-
};
|
|
14
|
-
}
|
|
1
|
+
export function createEmptyRecordingState() {
|
|
2
|
+
return {
|
|
3
|
+
active: false,
|
|
4
|
+
enabled: false,
|
|
5
|
+
name: null,
|
|
6
|
+
outputPath: null,
|
|
7
|
+
overlay: false,
|
|
8
|
+
startedAt: null,
|
|
9
|
+
endedAt: null,
|
|
10
|
+
eventCount: 0,
|
|
11
|
+
lastEventAt: null,
|
|
12
|
+
lastError: null,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
15
|
//# sourceMappingURL=types.js.map
|
|
@@ -1,95 +1,95 @@
|
|
|
1
|
-
export function resolveInputActionTimeoutMs() {
|
|
2
|
-
const raw = Number(process.env.CAMO_INPUT_ACTION_TIMEOUT_MS ?? process.env.CAMO_API_TIMEOUT_MS ?? 30000);
|
|
3
|
-
return Math.max(1000, Number.isFinite(raw) ? raw : 30000);
|
|
4
|
-
}
|
|
5
|
-
export function resolveNavigationWaitUntil() {
|
|
6
|
-
const raw = String(process.env.CAMO_NAV_WAIT_UNTIL ?? 'commit').trim().toLowerCase();
|
|
7
|
-
if (raw === 'load')
|
|
8
|
-
return 'load';
|
|
9
|
-
if (raw === 'domcontentloaded' || raw === 'dom')
|
|
10
|
-
return 'domcontentloaded';
|
|
11
|
-
if (raw === 'networkidle')
|
|
12
|
-
return 'networkidle';
|
|
13
|
-
return 'commit';
|
|
14
|
-
}
|
|
15
|
-
export function resolveInputActionMaxAttempts() {
|
|
16
|
-
const raw = Number(process.env.CAMO_INPUT_ACTION_MAX_ATTEMPTS ?? 2);
|
|
17
|
-
return Math.max(1, Math.min(3, Number.isFinite(raw) ? Math.floor(raw) : 2));
|
|
18
|
-
}
|
|
19
|
-
export function resolveInputRecoveryDelayMs() {
|
|
20
|
-
const raw = Number(process.env.CAMO_INPUT_RECOVERY_DELAY_MS ?? 120);
|
|
21
|
-
return Math.max(0, Number.isFinite(raw) ? Math.floor(raw) : 120);
|
|
22
|
-
}
|
|
23
|
-
export function resolveInputRecoveryBringToFrontTimeoutMs() {
|
|
24
|
-
const raw = Number(process.env.CAMO_INPUT_RECOVERY_BRING_TO_FRONT_TIMEOUT_MS ?? 800);
|
|
25
|
-
return Math.max(100, Number.isFinite(raw) ? Math.floor(raw) : 800);
|
|
26
|
-
}
|
|
27
|
-
export function resolveInputReadySettleMs() {
|
|
28
|
-
const raw = Number(process.env.CAMO_INPUT_READY_SETTLE_MS ?? 80);
|
|
29
|
-
return Math.max(0, Number.isFinite(raw) ? Math.floor(raw) : 80);
|
|
30
|
-
}
|
|
31
|
-
export function resolveBringToFrontMode() {
|
|
32
|
-
const mode = String(process.env.CAMO_BRING_TO_FRONT_MODE ?? '').trim().toLowerCase();
|
|
33
|
-
if (mode === 'never' || mode === 'off' || mode === 'disabled')
|
|
34
|
-
return 'never';
|
|
35
|
-
if (mode === 'always' || mode === 'on' || mode === 'auto')
|
|
36
|
-
return 'auto';
|
|
37
|
-
const legacy = String(process.env.CAMO_SKIP_BRING_TO_FRONT ?? '').trim().toLowerCase();
|
|
38
|
-
if (legacy === '1' || legacy === 'true' || legacy === 'yes' || legacy === 'on')
|
|
39
|
-
return 'never';
|
|
40
|
-
return 'auto';
|
|
41
|
-
}
|
|
42
|
-
export function shouldSkipBringToFront() {
|
|
43
|
-
return resolveBringToFrontMode() === 'never';
|
|
44
|
-
}
|
|
45
|
-
export function isTimeoutLikeError(error) {
|
|
46
|
-
const message = String(error?.message || error || '').toLowerCase();
|
|
47
|
-
return message.includes('timed out') || message.includes('timeout');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function resolveInputMode() {
|
|
51
|
-
const raw = String(process.env.CAMO_INPUT_MODE ?? '').trim().toLowerCase();
|
|
52
|
-
return raw === 'cdp' ? 'cdp' : 'playwright';
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function normalizeUrl(raw) {
|
|
56
|
-
try {
|
|
57
|
-
const url = new URL(raw);
|
|
58
|
-
return `${url.origin}${url.pathname}`;
|
|
59
|
-
}
|
|
60
|
-
catch {
|
|
61
|
-
return raw;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
export async function ensureInputReadyOnPage(page, headless, bringToFrontTimeoutMs, settleMs) {
|
|
65
|
-
if (headless)
|
|
66
|
-
return;
|
|
67
|
-
if (shouldSkipBringToFront()) {
|
|
68
|
-
if (settleMs > 0) {
|
|
69
|
-
await page.waitForTimeout(settleMs).catch(() => { });
|
|
70
|
-
}
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
let bringToFrontTimer = null;
|
|
74
|
-
try {
|
|
75
|
-
await Promise.race([
|
|
76
|
-
page.bringToFront(),
|
|
77
|
-
new Promise((_resolve, reject) => {
|
|
78
|
-
bringToFrontTimer = setTimeout(() => {
|
|
79
|
-
reject(new Error(`input ready bringToFront timed out after ${bringToFrontTimeoutMs}ms`));
|
|
80
|
-
}, bringToFrontTimeoutMs);
|
|
81
|
-
}),
|
|
82
|
-
]);
|
|
83
|
-
}
|
|
84
|
-
catch {
|
|
85
|
-
// Best-effort only
|
|
86
|
-
}
|
|
87
|
-
finally {
|
|
88
|
-
if (bringToFrontTimer)
|
|
89
|
-
clearTimeout(bringToFrontTimer);
|
|
90
|
-
}
|
|
91
|
-
if (settleMs > 0) {
|
|
92
|
-
await page.waitForTimeout(settleMs).catch(() => { });
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
//# sourceMappingURL=utils.js.map
|
|
1
|
+
export function resolveInputActionTimeoutMs() {
|
|
2
|
+
const raw = Number(process.env.CAMO_INPUT_ACTION_TIMEOUT_MS ?? process.env.CAMO_API_TIMEOUT_MS ?? 30000);
|
|
3
|
+
return Math.max(1000, Number.isFinite(raw) ? raw : 30000);
|
|
4
|
+
}
|
|
5
|
+
export function resolveNavigationWaitUntil() {
|
|
6
|
+
const raw = String(process.env.CAMO_NAV_WAIT_UNTIL ?? 'commit').trim().toLowerCase();
|
|
7
|
+
if (raw === 'load')
|
|
8
|
+
return 'load';
|
|
9
|
+
if (raw === 'domcontentloaded' || raw === 'dom')
|
|
10
|
+
return 'domcontentloaded';
|
|
11
|
+
if (raw === 'networkidle')
|
|
12
|
+
return 'networkidle';
|
|
13
|
+
return 'commit';
|
|
14
|
+
}
|
|
15
|
+
export function resolveInputActionMaxAttempts() {
|
|
16
|
+
const raw = Number(process.env.CAMO_INPUT_ACTION_MAX_ATTEMPTS ?? 2);
|
|
17
|
+
return Math.max(1, Math.min(3, Number.isFinite(raw) ? Math.floor(raw) : 2));
|
|
18
|
+
}
|
|
19
|
+
export function resolveInputRecoveryDelayMs() {
|
|
20
|
+
const raw = Number(process.env.CAMO_INPUT_RECOVERY_DELAY_MS ?? 120);
|
|
21
|
+
return Math.max(0, Number.isFinite(raw) ? Math.floor(raw) : 120);
|
|
22
|
+
}
|
|
23
|
+
export function resolveInputRecoveryBringToFrontTimeoutMs() {
|
|
24
|
+
const raw = Number(process.env.CAMO_INPUT_RECOVERY_BRING_TO_FRONT_TIMEOUT_MS ?? 800);
|
|
25
|
+
return Math.max(100, Number.isFinite(raw) ? Math.floor(raw) : 800);
|
|
26
|
+
}
|
|
27
|
+
export function resolveInputReadySettleMs() {
|
|
28
|
+
const raw = Number(process.env.CAMO_INPUT_READY_SETTLE_MS ?? 80);
|
|
29
|
+
return Math.max(0, Number.isFinite(raw) ? Math.floor(raw) : 80);
|
|
30
|
+
}
|
|
31
|
+
export function resolveBringToFrontMode() {
|
|
32
|
+
const mode = String(process.env.CAMO_BRING_TO_FRONT_MODE ?? '').trim().toLowerCase();
|
|
33
|
+
if (mode === 'never' || mode === 'off' || mode === 'disabled')
|
|
34
|
+
return 'never';
|
|
35
|
+
if (mode === 'always' || mode === 'on' || mode === 'auto')
|
|
36
|
+
return 'auto';
|
|
37
|
+
const legacy = String(process.env.CAMO_SKIP_BRING_TO_FRONT ?? '').trim().toLowerCase();
|
|
38
|
+
if (legacy === '1' || legacy === 'true' || legacy === 'yes' || legacy === 'on')
|
|
39
|
+
return 'never';
|
|
40
|
+
return 'auto';
|
|
41
|
+
}
|
|
42
|
+
export function shouldSkipBringToFront() {
|
|
43
|
+
return resolveBringToFrontMode() === 'never';
|
|
44
|
+
}
|
|
45
|
+
export function isTimeoutLikeError(error) {
|
|
46
|
+
const message = String(error?.message || error || '').toLowerCase();
|
|
47
|
+
return message.includes('timed out') || message.includes('timeout');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function resolveInputMode() {
|
|
51
|
+
const raw = String(process.env.CAMO_INPUT_MODE ?? '').trim().toLowerCase();
|
|
52
|
+
return raw === 'cdp' ? 'cdp' : 'playwright';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function normalizeUrl(raw) {
|
|
56
|
+
try {
|
|
57
|
+
const url = new URL(raw);
|
|
58
|
+
return `${url.origin}${url.pathname}`;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return raw;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export async function ensureInputReadyOnPage(page, headless, bringToFrontTimeoutMs, settleMs) {
|
|
65
|
+
if (headless)
|
|
66
|
+
return;
|
|
67
|
+
if (shouldSkipBringToFront()) {
|
|
68
|
+
if (settleMs > 0) {
|
|
69
|
+
await page.waitForTimeout(settleMs).catch(() => { });
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
let bringToFrontTimer = null;
|
|
74
|
+
try {
|
|
75
|
+
await Promise.race([
|
|
76
|
+
page.bringToFront(),
|
|
77
|
+
new Promise((_resolve, reject) => {
|
|
78
|
+
bringToFrontTimer = setTimeout(() => {
|
|
79
|
+
reject(new Error(`input ready bringToFront timed out after ${bringToFrontTimeoutMs}ms`));
|
|
80
|
+
}, bringToFrontTimeoutMs);
|
|
81
|
+
}),
|
|
82
|
+
]);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// Best-effort only
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
if (bringToFrontTimer)
|
|
89
|
+
clearTimeout(bringToFrontTimer);
|
|
90
|
+
}
|
|
91
|
+
if (settleMs > 0) {
|
|
92
|
+
await page.waitForTimeout(settleMs).catch(() => { });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
import { ensurePageViewport, refreshViewportFromWindow, setViewportSizeOnPage, maybeCenterWindow } from './viewport.js';
|
|
2
|
-
export class BrowserSessionViewportManager {
|
|
3
|
-
profileId;
|
|
4
|
-
getContext;
|
|
5
|
-
getEngine;
|
|
6
|
-
isHeadless;
|
|
7
|
-
state = { lastViewport: null, followWindowViewport: false };
|
|
8
|
-
constructor(profileId, getContext, getEngine, isHeadless) {
|
|
9
|
-
this.profileId = profileId;
|
|
10
|
-
this.getContext = getContext;
|
|
11
|
-
this.getEngine = getEngine;
|
|
12
|
-
this.isHeadless = isHeadless;
|
|
13
|
-
}
|
|
14
|
-
setInitialViewport(viewport, followWindowViewport) {
|
|
15
|
-
this.state.followWindowViewport = followWindowViewport;
|
|
16
|
-
this.state.lastViewport = followWindowViewport
|
|
17
|
-
? null
|
|
18
|
-
: { width: viewport.width, height: viewport.height };
|
|
19
|
-
}
|
|
20
|
-
isFollowingWindow() {
|
|
21
|
-
return this.state.followWindowViewport;
|
|
22
|
-
}
|
|
23
|
-
getLastViewport() {
|
|
24
|
-
return this.state.lastViewport;
|
|
25
|
-
}
|
|
26
|
-
async refreshFromWindow(page) {
|
|
27
|
-
const refreshed = await refreshViewportFromWindow(page).catch(() => null);
|
|
28
|
-
if (refreshed) {
|
|
29
|
-
this.state.lastViewport = refreshed;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
async ensurePageViewport(page) {
|
|
33
|
-
this.state = await ensurePageViewport(page, this.state, this.getContext(), this.getEngine(), this.isHeadless());
|
|
34
|
-
}
|
|
35
|
-
async setViewportSize(page, opts) {
|
|
36
|
-
const next = await setViewportSizeOnPage(page, opts, this.state, this.getContext(), this.getEngine(), this.isHeadless());
|
|
37
|
-
this.state.lastViewport = next;
|
|
38
|
-
return next;
|
|
39
|
-
}
|
|
40
|
-
async maybeCenter(page, fallback) {
|
|
41
|
-
const target = this.state.lastViewport || fallback;
|
|
42
|
-
if (!target)
|
|
43
|
-
return;
|
|
44
|
-
await maybeCenterWindow(page, target, this.isHeadless());
|
|
45
|
-
}
|
|
46
|
-
}
|
|
1
|
+
import { ensurePageViewport, refreshViewportFromWindow, setViewportSizeOnPage, maybeCenterWindow } from './viewport.js';
|
|
2
|
+
export class BrowserSessionViewportManager {
|
|
3
|
+
profileId;
|
|
4
|
+
getContext;
|
|
5
|
+
getEngine;
|
|
6
|
+
isHeadless;
|
|
7
|
+
state = { lastViewport: null, followWindowViewport: false };
|
|
8
|
+
constructor(profileId, getContext, getEngine, isHeadless) {
|
|
9
|
+
this.profileId = profileId;
|
|
10
|
+
this.getContext = getContext;
|
|
11
|
+
this.getEngine = getEngine;
|
|
12
|
+
this.isHeadless = isHeadless;
|
|
13
|
+
}
|
|
14
|
+
setInitialViewport(viewport, followWindowViewport) {
|
|
15
|
+
this.state.followWindowViewport = followWindowViewport;
|
|
16
|
+
this.state.lastViewport = followWindowViewport
|
|
17
|
+
? null
|
|
18
|
+
: { width: viewport.width, height: viewport.height };
|
|
19
|
+
}
|
|
20
|
+
isFollowingWindow() {
|
|
21
|
+
return this.state.followWindowViewport;
|
|
22
|
+
}
|
|
23
|
+
getLastViewport() {
|
|
24
|
+
return this.state.lastViewport;
|
|
25
|
+
}
|
|
26
|
+
async refreshFromWindow(page) {
|
|
27
|
+
const refreshed = await refreshViewportFromWindow(page).catch(() => null);
|
|
28
|
+
if (refreshed) {
|
|
29
|
+
this.state.lastViewport = refreshed;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async ensurePageViewport(page) {
|
|
33
|
+
this.state = await ensurePageViewport(page, this.state, this.getContext(), this.getEngine(), this.isHeadless());
|
|
34
|
+
}
|
|
35
|
+
async setViewportSize(page, opts) {
|
|
36
|
+
const next = await setViewportSizeOnPage(page, opts, this.state, this.getContext(), this.getEngine(), this.isHeadless());
|
|
37
|
+
this.state.lastViewport = next;
|
|
38
|
+
return next;
|
|
39
|
+
}
|
|
40
|
+
async maybeCenter(page, fallback) {
|
|
41
|
+
const target = this.state.lastViewport || fallback;
|
|
42
|
+
if (!target)
|
|
43
|
+
return;
|
|
44
|
+
await maybeCenterWindow(page, target, this.isHeadless());
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
47
|
//# sourceMappingURL=viewport-manager.js.map
|