@apifuse/provider-sdk 2.2.0-beta.3 → 2.2.0-beta.30
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/AUTHORING.md +493 -5
- package/CHANGELOG.md +127 -1
- package/README.md +52 -6
- package/SUBMISSION.md +1 -1
- package/bin/apifuse-check.ts +106 -62
- package/bin/apifuse-create.ts +1 -1
- package/bin/apifuse-dev.ts +63 -55
- package/bin/apifuse-pack-check.ts +22 -2
- package/bin/apifuse-pack-smoke.ts +78 -82
- package/bin/apifuse-pack-types.ts +583 -0
- package/bin/apifuse-perf.ts +59 -140
- package/bin/apifuse-record.ts +698 -113
- package/bin/apifuse-submit-check.ts +517 -44
- package/bin/apifuse-sync-assets.ts +117 -0
- package/bin/apifuse.ts +1 -1
- package/bin/submit-check-delimited-text.ts +50 -0
- package/bin/submit-check-xml.ts +1 -1
- package/dist/auth-turn/index.d.ts +4 -4
- package/dist/auth-turn/index.js +1 -1
- package/dist/auth.d.ts +16 -2
- package/dist/auth.js +76 -18
- package/dist/ceremonies/index.d.ts +9 -1
- package/dist/ceremonies/index.js +117 -29
- package/dist/cli/commands.d.ts +1 -1
- package/dist/cli/commands.js +8 -0
- package/dist/cli/create.d.ts +3 -0
- package/dist/cli/create.js +34 -35
- package/dist/cli/prompt-assets.d.ts +80 -0
- package/dist/cli/prompt-assets.js +743 -0
- package/dist/cli/templates/provider/AGENTS.md.tpl +17 -8
- package/dist/cli/templates/provider/README.md.tpl +4 -4
- package/dist/config/loader.d.ts +176 -17
- package/dist/config/loader.js +434 -161
- package/dist/contract-serialization.d.ts +2 -2
- package/dist/contract-serialization.js +7 -14
- package/dist/contract-types.d.ts +3 -2
- package/dist/contract.d.ts +3 -3
- package/dist/contract.js +6 -6
- package/dist/declaration-validation.d.ts +23 -0
- package/dist/declaration-validation.js +159 -0
- package/dist/define.d.ts +13 -1
- package/dist/define.js +391 -122
- package/dist/dev.d.ts +1 -1
- package/dist/dev.js +1 -1
- package/dist/error-resolution.d.ts +4 -0
- package/dist/error-resolution.js +122 -0
- package/dist/errors.d.ts +19 -1
- package/dist/errors.js +41 -3
- package/dist/fixture-sanitization.d.ts +26 -0
- package/dist/fixture-sanitization.js +216 -0
- package/dist/i18n/catalog.d.ts +2 -2
- package/dist/i18n/catalog.js +4 -10
- package/dist/i18n/index.d.ts +2 -2
- package/dist/i18n/index.js +2 -2
- package/dist/i18n/keys.d.ts +2 -2
- package/dist/index.d.ts +50 -42
- package/dist/index.js +41 -37
- package/dist/lint.d.ts +6 -1
- package/dist/lint.js +370 -18
- package/dist/native-address.d.ts +43 -0
- package/dist/native-address.js +281 -0
- package/dist/native-egress-policy.d.ts +31 -0
- package/dist/native-egress-policy.js +288 -0
- package/dist/observability.d.ts +5 -2
- package/dist/observability.js +48 -1
- package/dist/provider.d.ts +13 -11
- package/dist/provider.js +10 -9
- package/dist/public-schema-field-lint.d.ts +1 -1
- package/dist/recipes/gov-api.js +1 -1
- package/dist/runtime/auth-flow.d.ts +3 -1
- package/dist/runtime/auth-flow.js +8 -3
- package/dist/runtime/browser.d.ts +1 -1
- package/dist/runtime/browser.js +138 -40
- package/dist/runtime/cache.d.ts +2 -1
- package/dist/runtime/cache.js +173 -23
- package/dist/runtime/choice-wordlist.d.ts +9 -0
- package/dist/runtime/choice-wordlist.js +138 -0
- package/dist/runtime/choice.d.ts +13 -1
- package/dist/runtime/choice.js +514 -124
- package/dist/runtime/credential.d.ts +1 -1
- package/dist/runtime/credential.js +1 -1
- package/dist/runtime/env.d.ts +1 -1
- package/dist/runtime/executor.d.ts +1 -1
- package/dist/runtime/executor.js +25 -3
- package/dist/runtime/http.d.ts +3 -2
- package/dist/runtime/http.js +517 -55
- package/dist/runtime/insights.d.ts +1 -1
- package/dist/runtime/insights.js +6 -13
- package/dist/runtime/instrumentation.d.ts +2 -2
- package/dist/runtime/instrumentation.js +371 -23
- package/dist/runtime/keyring.js +1 -1
- package/dist/runtime/namespace.js +1 -1
- package/dist/runtime/native-network-errors.d.ts +33 -0
- package/dist/runtime/native-network-errors.js +69 -0
- package/dist/runtime/native-network.d.ts +96 -0
- package/dist/runtime/native-network.js +1232 -0
- package/dist/runtime/ocr.d.ts +29 -0
- package/dist/runtime/ocr.js +440 -0
- package/dist/runtime/otlp.d.ts +1 -1
- package/dist/runtime/perf.d.ts +1 -1
- package/dist/runtime/provider.d.ts +1 -1
- package/dist/runtime/provider.js +1 -2
- package/dist/runtime/proxy-errors.d.ts +1 -1
- package/dist/runtime/proxy-errors.js +9 -7
- package/dist/runtime/proxy-nodemaven.d.ts +56 -0
- package/dist/runtime/proxy-nodemaven.js +146 -0
- package/dist/runtime/proxy-retry-policy.d.ts +2 -2
- package/dist/runtime/proxy-retry-policy.js +2 -2
- package/dist/runtime/proxy-telemetry.d.ts +2 -1
- package/dist/runtime/proxy-telemetry.js +58 -52
- package/dist/runtime/redirects.d.ts +29 -0
- package/dist/runtime/redirects.js +36 -0
- package/dist/runtime/redis.d.ts +1 -1
- package/dist/runtime/redis.js +5 -5
- package/dist/runtime/request-options.d.ts +68 -1
- package/dist/runtime/request-options.js +548 -0
- package/dist/runtime/resolver-config.d.ts +6 -0
- package/dist/runtime/resolver-config.js +6 -0
- package/dist/runtime/resolver-public.d.ts +1 -0
- package/dist/runtime/resolver-public.js +1 -0
- package/dist/runtime/resolver-shared.d.ts +3 -0
- package/dist/runtime/resolver-shared.js +12 -0
- package/dist/runtime/resolver-vendors/bindings.d.ts +48 -0
- package/dist/runtime/resolver-vendors/bindings.js +40 -0
- package/dist/runtime/resolver-vendors/browser.d.ts +20 -0
- package/dist/runtime/resolver-vendors/browser.js +282 -0
- package/dist/runtime/resolver-vendors/hosts.d.ts +2 -0
- package/dist/runtime/resolver-vendors/hosts.js +33 -0
- package/dist/runtime/resolver-vendors/twocaptcha.d.ts +24 -0
- package/dist/runtime/resolver-vendors/twocaptcha.js +368 -0
- package/dist/runtime/resolver-vendors/types.d.ts +83 -0
- package/dist/runtime/resolver-vendors/types.js +69 -0
- package/dist/runtime/resolver.d.ts +59 -0
- package/dist/runtime/resolver.js +705 -0
- package/dist/runtime/secrets.d.ts +27 -0
- package/dist/runtime/secrets.js +51 -0
- package/dist/runtime/state.d.ts +5 -2
- package/dist/runtime/state.js +280 -74
- package/dist/runtime/stealth-cookies.d.ts +20 -0
- package/dist/runtime/stealth-cookies.js +111 -0
- package/dist/runtime/stealth.d.ts +30 -5
- package/dist/runtime/stealth.js +523 -259
- package/dist/runtime/stt.d.ts +1 -1
- package/dist/runtime/stt.js +12 -27
- package/dist/runtime/timeout.d.ts +5 -0
- package/dist/runtime/timeout.js +12 -0
- package/dist/runtime/trace.d.ts +2 -2
- package/dist/runtime/trace.js +2 -4
- package/dist/runtime/waterfall.d.ts +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +7 -15
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +1 -1
- package/dist/server/index.d.ts +7 -7
- package/dist/server/index.js +6 -6
- package/dist/server/self-test-input-tokens.d.ts +2 -1
- package/dist/server/self-test-input-tokens.js +18 -14
- package/dist/server/self-test-redaction.d.ts +1 -1
- package/dist/server/self-test-redaction.js +1 -1
- package/dist/server/self-test.d.ts +117 -3
- package/dist/server/self-test.js +787 -151
- package/dist/server/serve-implementation.d.ts +210 -0
- package/dist/server/serve-implementation.js +2077 -0
- package/dist/server/serve.d.ts +1 -70
- package/dist/server/serve.js +1 -1143
- package/dist/server/types.d.ts +34 -9
- package/dist/server/types.js +8 -1
- package/dist/stateful/errors.d.ts +19 -0
- package/dist/stateful/errors.js +24 -0
- package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
- package/dist/stateful/http-provider-event-emitter.js +237 -0
- package/dist/stateful/http-session-owner-registry.d.ts +44 -0
- package/dist/stateful/http-session-owner-registry.js +210 -0
- package/dist/stateful/index.d.ts +18 -0
- package/dist/stateful/index.js +18 -0
- package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
- package/dist/stateful/provider-event-delivery-failures.js +43 -0
- package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
- package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
- package/dist/stateful/provider-event-pipeline.d.ts +50 -0
- package/dist/stateful/provider-event-pipeline.js +1 -0
- package/dist/stateful/provider-events.d.ts +101 -0
- package/dist/stateful/provider-events.js +289 -0
- package/dist/stateful/session-key.d.ts +15 -0
- package/dist/stateful/session-key.js +86 -0
- package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
- package/dist/stateful/stateful-provider-adapter-context.js +42 -0
- package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
- package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
- package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
- package/dist/stateful/stateful-provider-adapter.js +287 -0
- package/dist/stateful/stateful-provider-observability.d.ts +62 -0
- package/dist/stateful/stateful-provider-observability.js +161 -0
- package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
- package/dist/stateful/stateful-provider-owner-forwarder.js +215 -0
- package/dist/stateful/stateful-provider-runtime-context.d.ts +32 -0
- package/dist/stateful/stateful-provider-runtime-context.js +60 -0
- package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
- package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
- package/dist/stateful/stateful-provider-session-routing.d.ts +67 -0
- package/dist/stateful/stateful-provider-session-routing.js +345 -0
- package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
- package/dist/stateful/stateful-provider-session-runtime.js +245 -0
- package/dist/stateful-signing.d.ts +18 -0
- package/dist/stateful-signing.js +27 -0
- package/dist/stealth/profiles.d.ts +1 -1
- package/dist/stealth/profiles.js +21 -21
- package/dist/stream-evidence.d.ts +74 -0
- package/dist/stream-evidence.js +785 -0
- package/dist/stream.d.ts +1 -1
- package/dist/stream.js +7 -1
- package/dist/testing/index.d.ts +3 -2
- package/dist/testing/index.js +3 -2
- package/dist/testing/run.d.ts +32 -2
- package/dist/testing/run.js +488 -28
- package/dist/types.d.ts +566 -19
- package/dist/types.js +1 -0
- package/dist/user-input.d.ts +30 -0
- package/dist/user-input.js +66 -0
- package/package.json +42 -7
- package/src/auth-turn/index.ts +2 -2
- package/src/auth.ts +146 -86
- package/src/ceremonies/index.ts +167 -92
- package/src/cli/commands.ts +10 -0
- package/src/cli/create.ts +42 -35
- package/src/cli/prompt-assets.ts +865 -0
- package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
- package/src/cli/templates/provider/README.md.tpl +4 -4
- package/src/config/loader.ts +667 -289
- package/src/contract-serialization.ts +10 -18
- package/src/contract-types.ts +3 -2
- package/src/contract.ts +14 -28
- package/src/declaration-validation.ts +202 -0
- package/src/define.ts +631 -495
- package/src/dev.ts +4 -9
- package/src/error-resolution.ts +127 -0
- package/src/errors.ts +56 -11
- package/src/fixture-sanitization.ts +247 -0
- package/src/i18n/catalog.ts +10 -32
- package/src/i18n/index.ts +2 -2
- package/src/i18n/keys.ts +5 -11
- package/src/index.ts +157 -44
- package/src/lint.ts +488 -154
- package/src/native-address.ts +340 -0
- package/src/native-egress-policy.ts +358 -0
- package/src/observability.ts +51 -1
- package/src/provider.ts +65 -11
- package/src/public-schema-field-lint.ts +7 -33
- package/src/recipes/gov-api.ts +2 -5
- package/src/runtime/auth-flow.ts +13 -7
- package/src/runtime/browser.ts +252 -207
- package/src/runtime/cache.ts +209 -81
- package/src/runtime/choice-wordlist.ts +145 -0
- package/src/runtime/choice.ts +685 -216
- package/src/runtime/credential.ts +2 -2
- package/src/runtime/env.ts +1 -1
- package/src/runtime/executor.ts +37 -19
- package/src/runtime/http.ts +645 -65
- package/src/runtime/insights.ts +15 -53
- package/src/runtime/instrumentation.ts +530 -67
- package/src/runtime/keyring.ts +7 -19
- package/src/runtime/namespace.ts +2 -7
- package/src/runtime/native-network-errors.ts +99 -0
- package/src/runtime/native-network.ts +1605 -0
- package/src/runtime/ocr.ts +523 -0
- package/src/runtime/otlp.ts +12 -23
- package/src/runtime/perf.ts +1 -1
- package/src/runtime/provider.ts +4 -9
- package/src/runtime/proxy-errors.ts +29 -42
- package/src/runtime/proxy-nodemaven.ts +221 -0
- package/src/runtime/proxy-retry-policy.ts +3 -3
- package/src/runtime/proxy-telemetry.ts +84 -77
- package/src/runtime/redirects.ts +66 -0
- package/src/runtime/redis.ts +10 -13
- package/src/runtime/request-options.ts +679 -9
- package/src/runtime/resolver-config.ts +6 -0
- package/src/runtime/resolver-public.ts +18 -0
- package/src/runtime/resolver-shared.ts +17 -0
- package/src/runtime/resolver-vendors/bindings.ts +56 -0
- package/src/runtime/resolver-vendors/browser.ts +408 -0
- package/src/runtime/resolver-vendors/hosts.ts +38 -0
- package/src/runtime/resolver-vendors/twocaptcha.ts +500 -0
- package/src/runtime/resolver-vendors/types.ts +173 -0
- package/src/runtime/resolver.ts +1060 -0
- package/src/runtime/secrets.ts +64 -0
- package/src/runtime/state.ts +399 -161
- package/src/runtime/stealth-cookies.ts +132 -0
- package/src/runtime/stealth.ts +681 -295
- package/src/runtime/stt.ts +39 -113
- package/src/runtime/timeout.ts +18 -0
- package/src/runtime/trace.ts +14 -44
- package/src/runtime/waterfall.ts +5 -18
- package/src/schema.ts +23 -84
- package/src/serve.ts +6 -1
- package/src/server/index.ts +30 -7
- package/src/server/self-test-input-tokens.ts +29 -14
- package/src/server/self-test-redaction.ts +2 -2
- package/src/server/self-test.ts +1030 -180
- package/src/server/serve-implementation.ts +3060 -0
- package/src/server/serve.ts +1 -1781
- package/src/server/types.ts +12 -13
- package/src/stateful/README.md +146 -0
- package/src/stateful/errors.ts +35 -0
- package/src/stateful/http-provider-event-emitter.ts +314 -0
- package/src/stateful/http-session-owner-registry.ts +306 -0
- package/src/stateful/index.ts +18 -0
- package/src/stateful/provider-event-delivery-failures.ts +80 -0
- package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
- package/src/stateful/provider-event-pipeline.ts +61 -0
- package/src/stateful/provider-events.ts +462 -0
- package/src/stateful/session-key.ts +111 -0
- package/src/stateful/stateful-provider-adapter-context.ts +59 -0
- package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
- package/src/stateful/stateful-provider-adapter.ts +562 -0
- package/src/stateful/stateful-provider-observability.ts +261 -0
- package/src/stateful/stateful-provider-owner-forwarder.ts +287 -0
- package/src/stateful/stateful-provider-runtime-context.ts +92 -0
- package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
- package/src/stateful/stateful-provider-session-routing.ts +546 -0
- package/src/stateful/stateful-provider-session-runtime.ts +403 -0
- package/src/stateful-signing.ts +46 -0
- package/src/stealth/profiles.ts +27 -33
- package/src/stream-evidence.ts +988 -0
- package/src/stream.ts +16 -20
- package/src/testing/index.ts +11 -2
- package/src/testing/run.ts +668 -74
- package/src/types.ts +665 -35
- package/src/user-input.ts +118 -0
- package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
- package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
- /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
package/src/runtime/browser.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import type { Frame, LaunchOptions, Locator, Page, Request, Route } from "playwright";
|
|
3
3
|
|
|
4
|
-
import { ProviderError } from "../errors";
|
|
4
|
+
import { ProviderError } from "../errors.js";
|
|
5
5
|
import type {
|
|
6
6
|
BrowserChallengeRequest,
|
|
7
7
|
BrowserChallengeResult,
|
|
8
8
|
BrowserClient as BrowserClientContract,
|
|
9
|
+
BrowserCookie,
|
|
9
10
|
BrowserEngine,
|
|
10
11
|
BrowserFrame,
|
|
11
12
|
BrowserLocator,
|
|
@@ -16,7 +17,7 @@ import type {
|
|
|
16
17
|
BrowserResourceMethod,
|
|
17
18
|
BrowserResourcePolicy,
|
|
18
19
|
BrowserResourceRequest,
|
|
19
|
-
} from "../types";
|
|
20
|
+
} from "../types.js";
|
|
20
21
|
|
|
21
22
|
const require = createRequire(import.meta.url);
|
|
22
23
|
const DEFAULT_WAIT_TIMEOUT_MS = 30_000;
|
|
@@ -29,9 +30,7 @@ type PlaywrightExtraModule = {
|
|
|
29
30
|
chromium: PlaywrightModule["chromium"] & { use(plugin: unknown): unknown };
|
|
30
31
|
};
|
|
31
32
|
|
|
32
|
-
type StealthPluginFactory = (options?: {
|
|
33
|
-
enabledEvasions?: Set<string>;
|
|
34
|
-
}) => unknown;
|
|
33
|
+
type StealthPluginFactory = (options?: { enabledEvasions?: Set<string> }) => unknown;
|
|
35
34
|
|
|
36
35
|
type PoolAcquireResponse = {
|
|
37
36
|
browserContextId?: string;
|
|
@@ -44,19 +43,35 @@ type PoolReleaseRequest = {
|
|
|
44
43
|
pageId: string;
|
|
45
44
|
};
|
|
46
45
|
|
|
47
|
-
type
|
|
46
|
+
type WebSocketCommandId = number;
|
|
48
47
|
|
|
49
|
-
type
|
|
48
|
+
type WebSocketCommandError = {
|
|
50
49
|
code?: number;
|
|
51
50
|
message?: string;
|
|
52
51
|
};
|
|
53
52
|
|
|
54
|
-
type
|
|
55
|
-
error?:
|
|
56
|
-
id?:
|
|
53
|
+
type WebSocketCommandMessage = {
|
|
54
|
+
error?: WebSocketCommandError;
|
|
55
|
+
id?: WebSocketCommandId;
|
|
56
|
+
jsonrpc?: "2.0";
|
|
57
57
|
method?: string;
|
|
58
58
|
params?: Record<string, unknown>;
|
|
59
59
|
result?: Record<string, unknown>;
|
|
60
|
+
sessionId?: string;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
type CdpCommandFrame = {
|
|
64
|
+
id: WebSocketCommandId;
|
|
65
|
+
method: string;
|
|
66
|
+
params: Record<string, unknown>;
|
|
67
|
+
sessionId?: string;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
type JsonRpcCommandFrame = {
|
|
71
|
+
id: WebSocketCommandId;
|
|
72
|
+
jsonrpc: "2.0";
|
|
73
|
+
method: string;
|
|
74
|
+
params: Record<string, unknown>;
|
|
60
75
|
};
|
|
61
76
|
|
|
62
77
|
type CdpFrameTreeNode = {
|
|
@@ -81,9 +96,7 @@ type CdpFetchFulfillParams = {
|
|
|
81
96
|
|
|
82
97
|
type BrowserPageContract = BrowserPage;
|
|
83
98
|
|
|
84
|
-
function toResourceBody(
|
|
85
|
-
body: BrowserResourceBody | undefined,
|
|
86
|
-
): Buffer | string | undefined {
|
|
99
|
+
function toResourceBody(body: BrowserResourceBody | undefined): Buffer | string | undefined {
|
|
87
100
|
if (body === undefined || typeof body === "string" || Buffer.isBuffer(body)) {
|
|
88
101
|
return body;
|
|
89
102
|
}
|
|
@@ -99,9 +112,7 @@ function isResourceMethod(method: string): method is BrowserResourceMethod {
|
|
|
99
112
|
return method === "GET" || method === "HEAD";
|
|
100
113
|
}
|
|
101
114
|
|
|
102
|
-
async function toResourceRequest(
|
|
103
|
-
request: Request,
|
|
104
|
-
): Promise<BrowserResourceRequest | null> {
|
|
115
|
+
async function toResourceRequest(request: Request): Promise<BrowserResourceRequest | null> {
|
|
105
116
|
const method = request.method().toUpperCase();
|
|
106
117
|
if (!isResourceMethod(method)) {
|
|
107
118
|
return null;
|
|
@@ -139,8 +150,7 @@ function toCdpResourceRequest(
|
|
|
139
150
|
request: {
|
|
140
151
|
headers: toCdpResourceHeaders(rawRequest.headers),
|
|
141
152
|
method,
|
|
142
|
-
resourceType:
|
|
143
|
-
typeof params.resourceType === "string" ? params.resourceType : undefined,
|
|
153
|
+
resourceType: typeof params.resourceType === "string" ? params.resourceType : undefined,
|
|
144
154
|
url,
|
|
145
155
|
},
|
|
146
156
|
};
|
|
@@ -189,15 +199,14 @@ function toCdpFulfillParams(
|
|
|
189
199
|
): CdpFetchFulfillParams {
|
|
190
200
|
const body = toResourceBody(decision.body);
|
|
191
201
|
return {
|
|
192
|
-
...(body === undefined
|
|
193
|
-
? {}
|
|
194
|
-
: { body: Buffer.from(body).toString("base64") }),
|
|
202
|
+
...(body === undefined ? {} : { body: Buffer.from(body).toString("base64") }),
|
|
195
203
|
...(decision.headers === undefined
|
|
196
204
|
? {}
|
|
197
205
|
: {
|
|
198
|
-
responseHeaders: Object.entries(decision.headers).map(
|
|
199
|
-
|
|
200
|
-
|
|
206
|
+
responseHeaders: Object.entries(decision.headers).map(([name, value]) => ({
|
|
207
|
+
name,
|
|
208
|
+
value,
|
|
209
|
+
})),
|
|
201
210
|
}),
|
|
202
211
|
requestId,
|
|
203
212
|
responseCode: decision.status ?? 200,
|
|
@@ -228,18 +237,14 @@ type SupportedBrowserClient = {
|
|
|
228
237
|
close(): Promise<void>;
|
|
229
238
|
newPage(): Promise<BrowserPageContract>;
|
|
230
239
|
rawPage(): Promise<BrowserPageContract>;
|
|
231
|
-
withIsolatedContext<T>(
|
|
232
|
-
handler: (page: BrowserPageContract) => Promise<T>,
|
|
233
|
-
): Promise<T>;
|
|
240
|
+
withIsolatedContext<T>(handler: (page: BrowserPageContract) => Promise<T>): Promise<T>;
|
|
234
241
|
};
|
|
235
242
|
|
|
236
243
|
function getDefaultCdpPoolUrl(env = process.env): string | undefined {
|
|
237
244
|
return env.APIFUSE__CDP_POOL__URL;
|
|
238
245
|
}
|
|
239
246
|
|
|
240
|
-
async function importOptionalModule<T extends object>(
|
|
241
|
-
moduleName: string,
|
|
242
|
-
): Promise<T> {
|
|
247
|
+
async function importOptionalModule<T extends object>(moduleName: string): Promise<T> {
|
|
243
248
|
return (await import(moduleName)) as T;
|
|
244
249
|
}
|
|
245
250
|
|
|
@@ -285,7 +290,13 @@ function formatExpression<T>(fn: string | (() => T)): string {
|
|
|
285
290
|
|
|
286
291
|
function toLaunchOptions(options: BrowserClientOptions): LaunchOptions {
|
|
287
292
|
return {
|
|
288
|
-
|
|
293
|
+
// `extraArgs` is optional, but playwright-extra's stealth evasions mutate
|
|
294
|
+
// `options.args` unguarded (navigator.webdriver does
|
|
295
|
+
// `options.args.findIndex(...)` in beforeLaunch). Forwarding `undefined`
|
|
296
|
+
// therefore crashes every stealth launch that omits extraArgs with
|
|
297
|
+
// "TypeError: undefined is not an object (evaluating 'options.args.findIndex')".
|
|
298
|
+
// Always hand the launcher a concrete array.
|
|
299
|
+
args: options.extraArgs ?? [],
|
|
289
300
|
executablePath: options.executablePath,
|
|
290
301
|
headless: options.headless ?? true,
|
|
291
302
|
proxy: options.proxy ? { server: options.proxy } : undefined,
|
|
@@ -369,9 +380,7 @@ async function loadPlaywright(): Promise<PlaywrightModule> {
|
|
|
369
380
|
}
|
|
370
381
|
|
|
371
382
|
try {
|
|
372
|
-
return unwrapModuleDefault(
|
|
373
|
-
await importOptionalModule<PlaywrightModule>("playwright"),
|
|
374
|
-
);
|
|
383
|
+
return unwrapModuleDefault(await importOptionalModule<PlaywrightModule>("playwright"));
|
|
375
384
|
} catch (error) {
|
|
376
385
|
if (isModuleNotFoundError(error)) {
|
|
377
386
|
throw new ProviderError("Playwright is not installed", {
|
|
@@ -419,19 +428,14 @@ async function loadPlaywrightExtra(): Promise<PlaywrightExtraModule> {
|
|
|
419
428
|
async function loadStealthPluginFactory(): Promise<StealthPluginFactory> {
|
|
420
429
|
try {
|
|
421
430
|
return unwrapModuleDefault(
|
|
422
|
-
await importOptionalModule<StealthPluginFactory>(
|
|
423
|
-
"puppeteer-extra-plugin-stealth",
|
|
424
|
-
),
|
|
431
|
+
await importOptionalModule<StealthPluginFactory>("puppeteer-extra-plugin-stealth"),
|
|
425
432
|
);
|
|
426
433
|
} catch (error) {
|
|
427
434
|
if (isModuleNotFoundError(error)) {
|
|
428
|
-
throw new ProviderError(
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
fix: "Run: bun add playwright-extra puppeteer-extra-plugin-stealth",
|
|
433
|
-
},
|
|
434
|
-
);
|
|
435
|
+
throw new ProviderError("puppeteer-extra-plugin-stealth is not installed", {
|
|
436
|
+
cause: error instanceof Error ? error : undefined,
|
|
437
|
+
fix: "Run: bun add playwright-extra puppeteer-extra-plugin-stealth",
|
|
438
|
+
});
|
|
435
439
|
}
|
|
436
440
|
|
|
437
441
|
throw error;
|
|
@@ -503,10 +507,7 @@ class PlaywrightBrowserPage implements BrowserPageContract {
|
|
|
503
507
|
return await this.page.evaluate(fn);
|
|
504
508
|
}
|
|
505
509
|
|
|
506
|
-
async waitForSelector(
|
|
507
|
-
selector: string,
|
|
508
|
-
options?: { timeout?: number },
|
|
509
|
-
): Promise<void> {
|
|
510
|
+
async waitForSelector(selector: string, options?: { timeout?: number }): Promise<void> {
|
|
510
511
|
await this.page.waitForSelector(selector, options);
|
|
511
512
|
}
|
|
512
513
|
|
|
@@ -551,13 +552,12 @@ class PlaywrightBrowserPage implements BrowserPageContract {
|
|
|
551
552
|
await this.page.close();
|
|
552
553
|
}
|
|
553
554
|
|
|
554
|
-
async
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
);
|
|
555
|
+
async cookies(): Promise<readonly BrowserCookie[]> {
|
|
556
|
+
return (await this.page.context().cookies()).map(toBrowserCookie);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
async withResourcePolicy<T>(policy: BrowserResourcePolicy, run: () => Promise<T>): Promise<T> {
|
|
560
|
+
const allowedMethods = new Set(policy.allowedMethods ?? DEFAULT_RESOURCE_METHODS);
|
|
561
561
|
const handler = async (route: Route): Promise<void> => {
|
|
562
562
|
const request = await toResourceRequest(route.request());
|
|
563
563
|
if (!request || !allowedMethods.has(request.method)) {
|
|
@@ -623,9 +623,7 @@ class PlaywrightBrowserClient implements SupportedBrowserClient {
|
|
|
623
623
|
});
|
|
624
624
|
}
|
|
625
625
|
|
|
626
|
-
async withIsolatedContext<T>(
|
|
627
|
-
handler: (page: BrowserPageContract) => Promise<T>,
|
|
628
|
-
): Promise<T> {
|
|
626
|
+
async withIsolatedContext<T>(handler: (page: BrowserPageContract) => Promise<T>): Promise<T> {
|
|
629
627
|
const browser = await this.ensureBrowser();
|
|
630
628
|
const context = await browser.newContext();
|
|
631
629
|
const page = await context.newPage();
|
|
@@ -670,15 +668,12 @@ function normalizeWebSocketEndpoint(endpoint: string): string {
|
|
|
670
668
|
throw new Error(`Unsupported WebSocket endpoint protocol: ${url.protocol}`);
|
|
671
669
|
}
|
|
672
670
|
|
|
673
|
-
class
|
|
671
|
+
abstract class WebSocketCommandClient {
|
|
674
672
|
private nextId = 1;
|
|
675
673
|
private readonly endpoint: string;
|
|
676
|
-
private readonly listeners = new Map<
|
|
677
|
-
string,
|
|
678
|
-
Set<(params: unknown) => void>
|
|
679
|
-
>();
|
|
674
|
+
private readonly listeners = new Map<string, Set<(params: unknown) => void>>();
|
|
680
675
|
private readonly pending = new Map<
|
|
681
|
-
|
|
676
|
+
WebSocketCommandId,
|
|
682
677
|
{
|
|
683
678
|
reject: (reason?: unknown) => void;
|
|
684
679
|
resolve: (value: Record<string, unknown>) => void;
|
|
@@ -714,14 +709,7 @@ class JsonRpcWebSocketClient {
|
|
|
714
709
|
return await new Promise<Record<string, unknown>>((resolve, reject) => {
|
|
715
710
|
this.pending.set(id, { resolve, reject });
|
|
716
711
|
|
|
717
|
-
socket.send(
|
|
718
|
-
JSON.stringify({
|
|
719
|
-
id,
|
|
720
|
-
jsonrpc: "2.0",
|
|
721
|
-
method,
|
|
722
|
-
params,
|
|
723
|
-
}),
|
|
724
|
-
);
|
|
712
|
+
socket.send(JSON.stringify(this.createCommandFrame(id, method, params)));
|
|
725
713
|
});
|
|
726
714
|
}
|
|
727
715
|
|
|
@@ -737,6 +725,12 @@ class JsonRpcWebSocketClient {
|
|
|
737
725
|
this.socketPromise = undefined;
|
|
738
726
|
}
|
|
739
727
|
|
|
728
|
+
protected abstract createCommandFrame(
|
|
729
|
+
id: WebSocketCommandId,
|
|
730
|
+
method: string,
|
|
731
|
+
params: Record<string, unknown>,
|
|
732
|
+
): CdpCommandFrame | JsonRpcCommandFrame;
|
|
733
|
+
|
|
740
734
|
private async getSocket(): Promise<WebSocket> {
|
|
741
735
|
if (this.socket?.readyState === WebSocket.OPEN) {
|
|
742
736
|
return this.socket;
|
|
@@ -759,7 +753,7 @@ class JsonRpcWebSocketClient {
|
|
|
759
753
|
typeof event.data === "string"
|
|
760
754
|
? event.data
|
|
761
755
|
: Buffer.from(event.data as ArrayBufferLike).toString("utf8");
|
|
762
|
-
const payload = JSON.parse(rawData) as
|
|
756
|
+
const payload = JSON.parse(rawData) as WebSocketCommandMessage;
|
|
763
757
|
|
|
764
758
|
if (typeof payload.id === "number") {
|
|
765
759
|
const pending = this.pending.get(payload.id);
|
|
@@ -770,9 +764,11 @@ class JsonRpcWebSocketClient {
|
|
|
770
764
|
this.pending.delete(payload.id);
|
|
771
765
|
|
|
772
766
|
if (payload.error) {
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
767
|
+
const error = new Error(payload.error.message ?? "JSON-RPC command failed");
|
|
768
|
+
if (typeof payload.error.code === "number") {
|
|
769
|
+
Object.assign(error, { code: payload.error.code });
|
|
770
|
+
}
|
|
771
|
+
pending.reject(error);
|
|
776
772
|
return;
|
|
777
773
|
}
|
|
778
774
|
|
|
@@ -800,11 +796,7 @@ class JsonRpcWebSocketClient {
|
|
|
800
796
|
});
|
|
801
797
|
|
|
802
798
|
socket.addEventListener("error", () => {
|
|
803
|
-
reject(
|
|
804
|
-
new Error(
|
|
805
|
-
`Unable to connect to WebSocket endpoint: ${this.endpoint}`,
|
|
806
|
-
),
|
|
807
|
-
);
|
|
799
|
+
reject(new Error(`Unable to connect to WebSocket endpoint: ${this.endpoint}`));
|
|
808
800
|
});
|
|
809
801
|
});
|
|
810
802
|
|
|
@@ -812,6 +804,38 @@ class JsonRpcWebSocketClient {
|
|
|
812
804
|
}
|
|
813
805
|
}
|
|
814
806
|
|
|
807
|
+
class CdpWebSocketClient extends WebSocketCommandClient {
|
|
808
|
+
constructor(
|
|
809
|
+
endpoint: string,
|
|
810
|
+
private readonly sessionId?: string,
|
|
811
|
+
) {
|
|
812
|
+
super(endpoint);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
protected createCommandFrame(
|
|
816
|
+
id: WebSocketCommandId,
|
|
817
|
+
method: string,
|
|
818
|
+
params: Record<string, unknown>,
|
|
819
|
+
): CdpCommandFrame {
|
|
820
|
+
return {
|
|
821
|
+
id,
|
|
822
|
+
method,
|
|
823
|
+
params,
|
|
824
|
+
...(this.sessionId === undefined ? {} : { sessionId: this.sessionId }),
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
class JsonRpcWebSocketClient extends WebSocketCommandClient {
|
|
830
|
+
protected createCommandFrame(
|
|
831
|
+
id: WebSocketCommandId,
|
|
832
|
+
method: string,
|
|
833
|
+
params: Record<string, unknown>,
|
|
834
|
+
): JsonRpcCommandFrame {
|
|
835
|
+
return { id, jsonrpc: "2.0", method, params };
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
815
839
|
function flattenCdpFrameTree(
|
|
816
840
|
node: CdpFrameTreeNode | undefined,
|
|
817
841
|
out: CdpFrameTreeNode["frame"][] = [],
|
|
@@ -832,6 +856,64 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
832
856
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
833
857
|
}
|
|
834
858
|
|
|
859
|
+
function isBrowserCookieSameSite(value: unknown): value is BrowserCookie["sameSite"] {
|
|
860
|
+
return value === "Strict" || value === "Lax" || value === "None";
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
function toBrowserCookie(cookie: {
|
|
864
|
+
readonly name: string;
|
|
865
|
+
readonly value: string;
|
|
866
|
+
readonly domain: string;
|
|
867
|
+
readonly path: string;
|
|
868
|
+
readonly expires?: number;
|
|
869
|
+
readonly httpOnly: boolean;
|
|
870
|
+
readonly secure: boolean;
|
|
871
|
+
readonly sameSite?: unknown;
|
|
872
|
+
}): BrowserCookie {
|
|
873
|
+
return {
|
|
874
|
+
name: cookie.name,
|
|
875
|
+
value: cookie.value,
|
|
876
|
+
domain: cookie.domain,
|
|
877
|
+
path: cookie.path,
|
|
878
|
+
...(cookie.expires !== undefined && cookie.expires > 0 ? { expires: cookie.expires } : {}),
|
|
879
|
+
httpOnly: cookie.httpOnly,
|
|
880
|
+
secure: cookie.secure,
|
|
881
|
+
...(isBrowserCookieSameSite(cookie.sameSite) ? { sameSite: cookie.sameSite } : {}),
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
function parseCdpCookies(value: unknown): readonly BrowserCookie[] {
|
|
886
|
+
if (!Array.isArray(value)) {
|
|
887
|
+
throw new Error("CDP Network.getCookies returned an invalid cookie list");
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
return value.map((cookie) => {
|
|
891
|
+
if (
|
|
892
|
+
!isRecord(cookie) ||
|
|
893
|
+
typeof cookie.name !== "string" ||
|
|
894
|
+
typeof cookie.value !== "string" ||
|
|
895
|
+
typeof cookie.domain !== "string" ||
|
|
896
|
+
typeof cookie.path !== "string" ||
|
|
897
|
+
typeof cookie.expires !== "number" ||
|
|
898
|
+
typeof cookie.httpOnly !== "boolean" ||
|
|
899
|
+
typeof cookie.secure !== "boolean"
|
|
900
|
+
) {
|
|
901
|
+
throw new Error("CDP Network.getCookies returned an invalid cookie");
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
return toBrowserCookie({
|
|
905
|
+
name: cookie.name,
|
|
906
|
+
value: cookie.value,
|
|
907
|
+
domain: cookie.domain,
|
|
908
|
+
path: cookie.path,
|
|
909
|
+
expires: cookie.expires,
|
|
910
|
+
httpOnly: cookie.httpOnly,
|
|
911
|
+
secure: cookie.secure,
|
|
912
|
+
sameSite: cookie.sameSite,
|
|
913
|
+
});
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
|
|
835
917
|
function parsePoolAcquireResponse(value: unknown): PoolAcquireResponse {
|
|
836
918
|
if (
|
|
837
919
|
!isRecord(value) ||
|
|
@@ -843,19 +925,14 @@ function parsePoolAcquireResponse(value: unknown): PoolAcquireResponse {
|
|
|
843
925
|
});
|
|
844
926
|
}
|
|
845
927
|
|
|
846
|
-
if (
|
|
847
|
-
value.browserContextId !== undefined &&
|
|
848
|
-
typeof value.browserContextId !== "string"
|
|
849
|
-
) {
|
|
928
|
+
if (value.browserContextId !== undefined && typeof value.browserContextId !== "string") {
|
|
850
929
|
throw new ProviderError("CDP Pool returned an invalid acquire response", {
|
|
851
930
|
code: "BROWSER_RUNTIME_UNSUPPORTED",
|
|
852
931
|
});
|
|
853
932
|
}
|
|
854
933
|
|
|
855
934
|
return {
|
|
856
|
-
...(value.browserContextId
|
|
857
|
-
? { browserContextId: value.browserContextId }
|
|
858
|
-
: {}),
|
|
935
|
+
...(value.browserContextId ? { browserContextId: value.browserContextId } : {}),
|
|
859
936
|
pageId: value.pageId,
|
|
860
937
|
wsEndpoint: value.wsEndpoint,
|
|
861
938
|
};
|
|
@@ -881,10 +958,7 @@ function parseCdpFrameTreeNode(value: unknown): CdpFrameTreeNode | undefined {
|
|
|
881
958
|
frame: {
|
|
882
959
|
id: frameId,
|
|
883
960
|
name: typeof value.frame.name === "string" ? value.frame.name : undefined,
|
|
884
|
-
parentId:
|
|
885
|
-
typeof value.frame.parentId === "string"
|
|
886
|
-
? value.frame.parentId
|
|
887
|
-
: undefined,
|
|
961
|
+
parentId: typeof value.frame.parentId === "string" ? value.frame.parentId : undefined,
|
|
888
962
|
url: typeof value.frame.url === "string" ? value.frame.url : undefined,
|
|
889
963
|
},
|
|
890
964
|
...(childFrames ? { childFrames } : {}),
|
|
@@ -902,22 +976,30 @@ function getCdpExecutionContext(params: unknown): {
|
|
|
902
976
|
const contextId = params.context.id;
|
|
903
977
|
const auxData = params.context.auxData;
|
|
904
978
|
return {
|
|
905
|
-
frameId:
|
|
906
|
-
isRecord(auxData) && typeof auxData.frameId === "string"
|
|
907
|
-
? auxData.frameId
|
|
908
|
-
: undefined,
|
|
979
|
+
frameId: isRecord(auxData) && typeof auxData.frameId === "string" ? auxData.frameId : undefined,
|
|
909
980
|
id: typeof contextId === "number" ? contextId : undefined,
|
|
910
981
|
};
|
|
911
982
|
}
|
|
912
983
|
|
|
984
|
+
function getCdpDestroyedExecutionContextId(params: unknown): number | undefined {
|
|
985
|
+
if (!isRecord(params)) {
|
|
986
|
+
return undefined;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
return typeof params.executionContextId === "number"
|
|
990
|
+
? params.executionContextId
|
|
991
|
+
: undefined;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
function isMissingExecutionContextError(error: unknown): boolean {
|
|
995
|
+
return error instanceof Error && /\b(?:cannot|failed to) find context\b/i.test(error.message);
|
|
996
|
+
}
|
|
997
|
+
|
|
913
998
|
class CdpBrowserLocator implements BrowserLocator {
|
|
914
999
|
constructor(
|
|
915
1000
|
private readonly frame: {
|
|
916
1001
|
evaluate<T>(fn: string | (() => T)): Promise<T>;
|
|
917
|
-
waitForSelector?(
|
|
918
|
-
selector: string,
|
|
919
|
-
options?: { timeout?: number },
|
|
920
|
-
): Promise<void>;
|
|
1002
|
+
waitForSelector?(selector: string, options?: { timeout?: number }): Promise<void>;
|
|
921
1003
|
},
|
|
922
1004
|
private readonly selector: string,
|
|
923
1005
|
) {}
|
|
@@ -989,9 +1071,7 @@ class CdpBrowserFrame implements BrowserFrame {
|
|
|
989
1071
|
) {}
|
|
990
1072
|
|
|
991
1073
|
async url(): Promise<string> {
|
|
992
|
-
const evaluatedUrl = await this.evaluate<string | undefined>(
|
|
993
|
-
"window.location.href",
|
|
994
|
-
);
|
|
1074
|
+
const evaluatedUrl = await this.evaluate<string | undefined>("window.location.href");
|
|
995
1075
|
return evaluatedUrl || this.initialUrl;
|
|
996
1076
|
}
|
|
997
1077
|
|
|
@@ -1011,10 +1091,7 @@ class CdpBrowserFrame implements BrowserFrame {
|
|
|
1011
1091
|
return new CdpBrowserLocator(this, selector);
|
|
1012
1092
|
}
|
|
1013
1093
|
|
|
1014
|
-
async waitForSelector(
|
|
1015
|
-
selector: string,
|
|
1016
|
-
options?: { timeout?: number },
|
|
1017
|
-
): Promise<void> {
|
|
1094
|
+
async waitForSelector(selector: string, options?: { timeout?: number }): Promise<void> {
|
|
1018
1095
|
await this.page.waitForSelectorInFrame(this.id, selector, options);
|
|
1019
1096
|
}
|
|
1020
1097
|
|
|
@@ -1031,7 +1108,7 @@ class CdpPoolBrowserPage implements BrowserPageContract {
|
|
|
1031
1108
|
constructor(
|
|
1032
1109
|
readonly pageId: string,
|
|
1033
1110
|
private readonly browserContextId: string | undefined,
|
|
1034
|
-
private readonly pageClient:
|
|
1111
|
+
private readonly pageClient: CdpWebSocketClient,
|
|
1035
1112
|
private readonly release: (request: PoolReleaseRequest) => Promise<void>,
|
|
1036
1113
|
) {}
|
|
1037
1114
|
|
|
@@ -1049,10 +1126,7 @@ class CdpPoolBrowserPage implements BrowserPageContract {
|
|
|
1049
1126
|
|
|
1050
1127
|
try {
|
|
1051
1128
|
await this.pageClient.send("Page.navigate", { url });
|
|
1052
|
-
await this.waitForDocumentReady(
|
|
1053
|
-
startedAt + DEFAULT_WAIT_TIMEOUT_MS,
|
|
1054
|
-
() => loadEventSeen,
|
|
1055
|
-
);
|
|
1129
|
+
await this.waitForDocumentReady(startedAt + DEFAULT_WAIT_TIMEOUT_MS, () => loadEventSeen);
|
|
1056
1130
|
} finally {
|
|
1057
1131
|
unsubscribe();
|
|
1058
1132
|
}
|
|
@@ -1063,13 +1137,23 @@ class CdpPoolBrowserPage implements BrowserPageContract {
|
|
|
1063
1137
|
return await this.evaluateWithContext<T>(fn);
|
|
1064
1138
|
}
|
|
1065
1139
|
|
|
1066
|
-
async evaluateInFrame<T>(
|
|
1067
|
-
frameId: string,
|
|
1068
|
-
fn: string | (() => T),
|
|
1069
|
-
): Promise<T> {
|
|
1140
|
+
async evaluateInFrame<T>(frameId: string, fn: string | (() => T)): Promise<T> {
|
|
1070
1141
|
await this.initialize();
|
|
1071
1142
|
const contextId = await this.getFrameExecutionContextId(frameId);
|
|
1072
|
-
|
|
1143
|
+
try {
|
|
1144
|
+
return await this.evaluateWithContext<T>(fn, contextId);
|
|
1145
|
+
} catch (error) {
|
|
1146
|
+
if (!isMissingExecutionContextError(error)) {
|
|
1147
|
+
throw error;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
if (this.frameExecutionContexts.get(frameId) === contextId) {
|
|
1151
|
+
this.frameExecutionContexts.delete(frameId);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
const refreshedContextId = await this.getFrameExecutionContextId(frameId);
|
|
1155
|
+
return await this.evaluateWithContext<T>(fn, refreshedContextId);
|
|
1156
|
+
}
|
|
1073
1157
|
}
|
|
1074
1158
|
|
|
1075
1159
|
async waitForSelectorInFrame(
|
|
@@ -1096,10 +1180,7 @@ class CdpPoolBrowserPage implements BrowserPageContract {
|
|
|
1096
1180
|
throw new Error(`Timed out waiting for selector: ${selector}`);
|
|
1097
1181
|
}
|
|
1098
1182
|
|
|
1099
|
-
private async evaluateWithContext<T>(
|
|
1100
|
-
fn: string | (() => T),
|
|
1101
|
-
contextId?: number,
|
|
1102
|
-
): Promise<T> {
|
|
1183
|
+
private async evaluateWithContext<T>(fn: string | (() => T), contextId?: number): Promise<T> {
|
|
1103
1184
|
const result = await this.pageClient.send("Runtime.evaluate", {
|
|
1104
1185
|
awaitPromise: true,
|
|
1105
1186
|
...(contextId === undefined ? {} : { contextId }),
|
|
@@ -1109,20 +1190,14 @@ class CdpPoolBrowserPage implements BrowserPageContract {
|
|
|
1109
1190
|
|
|
1110
1191
|
if (result.exceptionDetails) {
|
|
1111
1192
|
throw new Error(
|
|
1112
|
-
String(
|
|
1113
|
-
(result.exceptionDetails as { text?: string }).text ??
|
|
1114
|
-
"Browser evaluation failed",
|
|
1115
|
-
),
|
|
1193
|
+
String((result.exceptionDetails as { text?: string }).text ?? "Browser evaluation failed"),
|
|
1116
1194
|
);
|
|
1117
1195
|
}
|
|
1118
1196
|
|
|
1119
1197
|
return (result.result as { value?: T } | undefined)?.value as T;
|
|
1120
1198
|
}
|
|
1121
1199
|
|
|
1122
|
-
async waitForSelector(
|
|
1123
|
-
selector: string,
|
|
1124
|
-
options?: { timeout?: number },
|
|
1125
|
-
): Promise<void> {
|
|
1200
|
+
async waitForSelector(selector: string, options?: { timeout?: number }): Promise<void> {
|
|
1126
1201
|
const timeout = options?.timeout ?? DEFAULT_WAIT_TIMEOUT_MS;
|
|
1127
1202
|
const deadline = Date.now() + timeout;
|
|
1128
1203
|
|
|
@@ -1188,14 +1263,7 @@ class CdpPoolBrowserPage implements BrowserPageContract {
|
|
|
1188
1263
|
const result = await this.pageClient.send("Page.getFrameTree");
|
|
1189
1264
|
const frames = flattenCdpFrameTree(parseCdpFrameTreeNode(result.frameTree));
|
|
1190
1265
|
return frames.map(
|
|
1191
|
-
(frame) =>
|
|
1192
|
-
new CdpBrowserFrame(
|
|
1193
|
-
frame.id,
|
|
1194
|
-
this,
|
|
1195
|
-
frame.url ?? "",
|
|
1196
|
-
frame.name,
|
|
1197
|
-
frame.parentId,
|
|
1198
|
-
),
|
|
1266
|
+
(frame) => new CdpBrowserFrame(frame.id, this, frame.url ?? "", frame.name, frame.parentId),
|
|
1199
1267
|
);
|
|
1200
1268
|
}
|
|
1201
1269
|
|
|
@@ -1209,8 +1277,7 @@ class CdpPoolBrowserPage implements BrowserPageContract {
|
|
|
1209
1277
|
|
|
1210
1278
|
async url(): Promise<string> {
|
|
1211
1279
|
const [mainFrame] = await this.frames();
|
|
1212
|
-
const frameUrl =
|
|
1213
|
-
mainFrame instanceof CdpBrowserFrame ? mainFrame.fallbackUrl() : "";
|
|
1280
|
+
const frameUrl = mainFrame instanceof CdpBrowserFrame ? mainFrame.fallbackUrl() : "";
|
|
1214
1281
|
return frameUrl || (await this.evaluate<string>("window.location.href"));
|
|
1215
1282
|
}
|
|
1216
1283
|
|
|
@@ -1229,6 +1296,12 @@ class CdpPoolBrowserPage implements BrowserPageContract {
|
|
|
1229
1296
|
return Buffer.from(String(result.data ?? ""), "base64");
|
|
1230
1297
|
}
|
|
1231
1298
|
|
|
1299
|
+
async cookies(): Promise<readonly BrowserCookie[]> {
|
|
1300
|
+
await this.initialize();
|
|
1301
|
+
const result = await this.pageClient.send("Network.getCookies");
|
|
1302
|
+
return parseCdpCookies(result.cookies);
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1232
1305
|
async close(): Promise<void> {
|
|
1233
1306
|
if (this.closed) {
|
|
1234
1307
|
return;
|
|
@@ -1238,9 +1311,7 @@ class CdpPoolBrowserPage implements BrowserPageContract {
|
|
|
1238
1311
|
|
|
1239
1312
|
try {
|
|
1240
1313
|
await this.release({
|
|
1241
|
-
...(this.browserContextId
|
|
1242
|
-
? { browserContextId: this.browserContextId }
|
|
1243
|
-
: {}),
|
|
1314
|
+
...(this.browserContextId ? { browserContextId: this.browserContextId } : {}),
|
|
1244
1315
|
pageId: this.pageId,
|
|
1245
1316
|
});
|
|
1246
1317
|
} finally {
|
|
@@ -1248,25 +1319,13 @@ class CdpPoolBrowserPage implements BrowserPageContract {
|
|
|
1248
1319
|
}
|
|
1249
1320
|
}
|
|
1250
1321
|
|
|
1251
|
-
async withResourcePolicy<T>(
|
|
1252
|
-
policy
|
|
1253
|
-
run: () => Promise<T>,
|
|
1254
|
-
): Promise<T> {
|
|
1255
|
-
const allowedMethods = new Set(
|
|
1256
|
-
policy.allowedMethods ?? DEFAULT_RESOURCE_METHODS,
|
|
1257
|
-
);
|
|
1322
|
+
async withResourcePolicy<T>(policy: BrowserResourcePolicy, run: () => Promise<T>): Promise<T> {
|
|
1323
|
+
const allowedMethods = new Set(policy.allowedMethods ?? DEFAULT_RESOURCE_METHODS);
|
|
1258
1324
|
const handlePausedRequest = (params: unknown): void => {
|
|
1259
|
-
void this.handleResourcePolicyPausedRequest(
|
|
1260
|
-
params,
|
|
1261
|
-
policy,
|
|
1262
|
-
allowedMethods,
|
|
1263
|
-
);
|
|
1325
|
+
void this.handleResourcePolicyPausedRequest(params, policy, allowedMethods);
|
|
1264
1326
|
};
|
|
1265
1327
|
|
|
1266
|
-
const unsubscribe = this.pageClient.on(
|
|
1267
|
-
"Fetch.requestPaused",
|
|
1268
|
-
handlePausedRequest,
|
|
1269
|
-
);
|
|
1328
|
+
const unsubscribe = this.pageClient.on("Fetch.requestPaused", handlePausedRequest);
|
|
1270
1329
|
|
|
1271
1330
|
try {
|
|
1272
1331
|
await this.pageClient.send("Fetch.enable", {
|
|
@@ -1358,6 +1417,21 @@ class CdpPoolBrowserPage implements BrowserPageContract {
|
|
|
1358
1417
|
this.frameExecutionContexts.set(context.frameId, context.id);
|
|
1359
1418
|
}
|
|
1360
1419
|
});
|
|
1420
|
+
this.pageClient.on("Runtime.executionContextDestroyed", (params) => {
|
|
1421
|
+
const destroyedContextId = getCdpDestroyedExecutionContextId(params);
|
|
1422
|
+
if (destroyedContextId === undefined) {
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
for (const [frameId, contextId] of this.frameExecutionContexts) {
|
|
1427
|
+
if (contextId === destroyedContextId) {
|
|
1428
|
+
this.frameExecutionContexts.delete(frameId);
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
this.pageClient.on("Runtime.executionContextsCleared", () => {
|
|
1433
|
+
this.frameExecutionContexts.clear();
|
|
1434
|
+
});
|
|
1361
1435
|
await this.pageClient.send("Page.enable");
|
|
1362
1436
|
await this.pageClient.send("Runtime.enable");
|
|
1363
1437
|
this.initialized = true;
|
|
@@ -1376,9 +1450,7 @@ class CdpPoolBrowserPage implements BrowserPageContract {
|
|
|
1376
1450
|
});
|
|
1377
1451
|
const contextId = result.executionContextId;
|
|
1378
1452
|
if (typeof contextId !== "number") {
|
|
1379
|
-
throw new Error(
|
|
1380
|
-
`Unable to resolve execution context for frame: ${frameId}`,
|
|
1381
|
-
);
|
|
1453
|
+
throw new Error(`Unable to resolve execution context for frame: ${frameId}`);
|
|
1382
1454
|
}
|
|
1383
1455
|
|
|
1384
1456
|
this.frameExecutionContexts.set(frameId, contextId);
|
|
@@ -1424,20 +1496,14 @@ class CdpPoolBrowserClient implements SupportedBrowserClient {
|
|
|
1424
1496
|
return await this.acquirePage({ isolatedContext: true });
|
|
1425
1497
|
}
|
|
1426
1498
|
|
|
1427
|
-
private async acquirePage(options?: {
|
|
1428
|
-
isolatedContext?: boolean;
|
|
1429
|
-
}): Promise<BrowserPageContract> {
|
|
1499
|
+
private async acquirePage(options?: { isolatedContext?: boolean }): Promise<BrowserPageContract> {
|
|
1430
1500
|
const acquireResult = parsePoolAcquireResponse(
|
|
1431
1501
|
await this.poolClient.send("acquire", {
|
|
1432
|
-
...(this.allowedHosts.length > 0
|
|
1433
|
-
|
|
1434
|
-
: {}),
|
|
1435
|
-
...(options?.isolatedContext
|
|
1436
|
-
? { isolationMode: "browserContext" }
|
|
1437
|
-
: {}),
|
|
1502
|
+
...(this.allowedHosts.length > 0 ? { allowedHosts: this.allowedHosts } : {}),
|
|
1503
|
+
...(options?.isolatedContext ? { isolationMode: "browserContext" } : {}),
|
|
1438
1504
|
}),
|
|
1439
1505
|
);
|
|
1440
|
-
const pageClient = new
|
|
1506
|
+
const pageClient = new CdpWebSocketClient(acquireResult.wsEndpoint);
|
|
1441
1507
|
const page = new CdpPoolBrowserPage(
|
|
1442
1508
|
acquireResult.pageId,
|
|
1443
1509
|
acquireResult.browserContextId,
|
|
@@ -1463,9 +1529,7 @@ class CdpPoolBrowserClient implements SupportedBrowserClient {
|
|
|
1463
1529
|
return await this.newPage();
|
|
1464
1530
|
}
|
|
1465
1531
|
|
|
1466
|
-
async withIsolatedContext<T>(
|
|
1467
|
-
handler: (page: BrowserPageContract) => Promise<T>,
|
|
1468
|
-
): Promise<T> {
|
|
1532
|
+
async withIsolatedContext<T>(handler: (page: BrowserPageContract) => Promise<T>): Promise<T> {
|
|
1469
1533
|
const page = await this.acquirePage({ isolatedContext: true });
|
|
1470
1534
|
|
|
1471
1535
|
try {
|
|
@@ -1481,9 +1545,7 @@ class CdpPoolBrowserClient implements SupportedBrowserClient {
|
|
|
1481
1545
|
}
|
|
1482
1546
|
|
|
1483
1547
|
class UnsupportedBrowserEngineClient implements SupportedBrowserClient {
|
|
1484
|
-
constructor(
|
|
1485
|
-
readonly engine: Extract<BrowserEngine, "nodriver" | "selenium-uc">,
|
|
1486
|
-
) {}
|
|
1548
|
+
constructor(readonly engine: Extract<BrowserEngine, "nodriver" | "selenium-uc">) {}
|
|
1487
1549
|
|
|
1488
1550
|
async newPage(): Promise<never> {
|
|
1489
1551
|
if (this.engine === "nodriver") {
|
|
@@ -1510,15 +1572,11 @@ class UnsupportedBrowserEngineClient implements SupportedBrowserClient {
|
|
|
1510
1572
|
async close(): Promise<void> {}
|
|
1511
1573
|
}
|
|
1512
1574
|
|
|
1513
|
-
function createPlaywrightStealthClient(
|
|
1514
|
-
options: BrowserClientOptions = {},
|
|
1515
|
-
): SupportedBrowserClient {
|
|
1575
|
+
function createPlaywrightStealthClient(options: BrowserClientOptions = {}): SupportedBrowserClient {
|
|
1516
1576
|
return new PlaywrightBrowserClient(options);
|
|
1517
1577
|
}
|
|
1518
1578
|
|
|
1519
|
-
function createCdpPoolBrowserClient(
|
|
1520
|
-
options: BrowserClientOptions,
|
|
1521
|
-
): SupportedBrowserClient {
|
|
1579
|
+
function createCdpPoolBrowserClient(options: BrowserClientOptions): SupportedBrowserClient {
|
|
1522
1580
|
return new CdpPoolBrowserClient(options);
|
|
1523
1581
|
}
|
|
1524
1582
|
|
|
@@ -1547,13 +1605,10 @@ export class BrowserClient implements BrowserClientContract {
|
|
|
1547
1605
|
this.cdpUrl = resolvedOptions.cdpUrl;
|
|
1548
1606
|
|
|
1549
1607
|
if (resolvedOptions.requireCdpPool && !resolvedOptions.cdpUrl) {
|
|
1550
|
-
throw new ProviderError(
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
fix: "Set APIFUSE__CDP_POOL__URL for deployed browser providers. Local standalone development may omit it.",
|
|
1555
|
-
},
|
|
1556
|
-
);
|
|
1608
|
+
throw new ProviderError("Managed CDP Pool is required for browser providers in production", {
|
|
1609
|
+
code: "BROWSER_CDP_POOL_REQUIRED",
|
|
1610
|
+
fix: "Set APIFUSE__CDP_POOL__URL for deployed browser providers. Local standalone development may omit it.",
|
|
1611
|
+
});
|
|
1557
1612
|
}
|
|
1558
1613
|
|
|
1559
1614
|
switch (engine) {
|
|
@@ -1591,9 +1646,7 @@ export class BrowserClient implements BrowserClientContract {
|
|
|
1591
1646
|
return this.activatePage(page);
|
|
1592
1647
|
}
|
|
1593
1648
|
|
|
1594
|
-
async withIsolatedContext<T>(
|
|
1595
|
-
handler: (page: BrowserPageContract) => Promise<T>,
|
|
1596
|
-
): Promise<T> {
|
|
1649
|
+
async withIsolatedContext<T>(handler: (page: BrowserPageContract) => Promise<T>): Promise<T> {
|
|
1597
1650
|
const previousActivePage = this.activePage;
|
|
1598
1651
|
let trackedPage: BrowserPageContract | undefined;
|
|
1599
1652
|
|
|
@@ -1638,16 +1691,11 @@ export class BrowserClient implements BrowserClientContract {
|
|
|
1638
1691
|
return trackedPage;
|
|
1639
1692
|
}
|
|
1640
1693
|
|
|
1641
|
-
async solveChallenge(
|
|
1642
|
-
request: BrowserChallengeRequest,
|
|
1643
|
-
): Promise<BrowserChallengeResult> {
|
|
1694
|
+
async solveChallenge(request: BrowserChallengeRequest): Promise<BrowserChallengeResult> {
|
|
1644
1695
|
if (request.type !== "recaptcha") {
|
|
1645
|
-
throw new ProviderError(
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
code: "BROWSER_RUNTIME_UNSUPPORTED",
|
|
1649
|
-
},
|
|
1650
|
-
);
|
|
1696
|
+
throw new ProviderError(`Unsupported browser challenge: ${request.type}`, {
|
|
1697
|
+
code: "BROWSER_RUNTIME_UNSUPPORTED",
|
|
1698
|
+
});
|
|
1651
1699
|
}
|
|
1652
1700
|
|
|
1653
1701
|
if (this.activePage) {
|
|
@@ -1709,8 +1757,7 @@ async function findRecaptchaFrame(
|
|
|
1709
1757
|
for (const frame of frames) {
|
|
1710
1758
|
const url = await frame.url();
|
|
1711
1759
|
const matchesRecaptcha =
|
|
1712
|
-
url.includes("google.com/recaptcha") ||
|
|
1713
|
-
url.includes("recaptcha.net/recaptcha");
|
|
1760
|
+
url.includes("google.com/recaptcha") || url.includes("recaptcha.net/recaptcha");
|
|
1714
1761
|
const matchesSiteKey = !siteKey || url.includes(siteKey);
|
|
1715
1762
|
if (matchesRecaptcha && matchesSiteKey) {
|
|
1716
1763
|
return frame;
|
|
@@ -1720,8 +1767,6 @@ async function findRecaptchaFrame(
|
|
|
1720
1767
|
return undefined;
|
|
1721
1768
|
}
|
|
1722
1769
|
|
|
1723
|
-
export function createBrowserClient(
|
|
1724
|
-
options: BrowserClientOptions = {},
|
|
1725
|
-
): BrowserClient {
|
|
1770
|
+
export function createBrowserClient(options: BrowserClientOptions = {}): BrowserClient {
|
|
1726
1771
|
return new BrowserClient(options);
|
|
1727
1772
|
}
|