@apifuse/provider-sdk 2.2.0-beta.5 → 2.2.0-beta.50
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 +537 -33
- package/CHANGELOG.md +201 -1
- package/README.md +83 -22
- package/SUBMISSION.md +2 -2
- package/bin/apifuse-check.ts +165 -11
- package/bin/apifuse-dev.ts +57 -13
- package/bin/apifuse-migrate-operation-declaration.ts +55 -0
- package/bin/apifuse-migrate-shape.ts +202 -0
- package/bin/apifuse-pack-check.ts +22 -2
- package/bin/apifuse-pack-smoke.ts +57 -2
- package/bin/apifuse-pack-types.ts +357 -38
- package/bin/apifuse-perf.ts +14 -13
- package/bin/apifuse-record.ts +709 -72
- package/bin/apifuse-submit-check.ts +2344 -324
- package/bin/apifuse-sync-assets.ts +117 -0
- package/bin/submit-check-delimited-text.ts +50 -0
- package/dist/auth-turn/index.d.ts +3 -3
- package/dist/auth-turn/index.js +1 -1
- package/dist/auth.d.ts +14 -0
- package/dist/auth.js +67 -0
- package/dist/ceremonies/index.d.ts +16 -0
- package/dist/ceremonies/index.js +141 -36
- package/dist/cli/commands.d.ts +1 -1
- package/dist/cli/commands.js +27 -0
- package/dist/cli/create.d.ts +4 -1
- package/dist/cli/create.js +42 -37
- package/dist/cli/migrate-operation-declaration.d.ts +59 -0
- package/dist/cli/migrate-operation-declaration.js +1178 -0
- package/dist/cli/migrate-operation-shape.d.ts +44 -0
- package/dist/cli/migrate-operation-shape.js +113 -0
- package/dist/cli/migrate-provider-shape.d.ts +52 -0
- package/dist/cli/migrate-provider-shape.js +578 -0
- 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/Dockerfile.tpl +1 -1
- package/dist/cli/templates/provider/README.md.tpl +5 -5
- package/dist/cli/templates/provider/index.ts.tpl +6 -3
- package/dist/cli/templates/provider/operations/ping.ts.tpl +4 -1
- package/dist/cli/templates/provider/provider.json.tpl +6 -0
- package/dist/config/loader.d.ts +179 -16
- package/dist/config/loader.js +435 -127
- package/dist/contract-serialization.js +4 -8
- package/dist/contract-types.d.ts +12 -5
- package/dist/contract.js +24 -10
- package/dist/declaration-validation.d.ts +33 -0
- package/dist/declaration-validation.js +266 -0
- package/dist/define.d.ts +66 -29
- package/dist/define.js +778 -69
- package/dist/dev.d.ts +3 -0
- package/dist/dev.js +1 -1
- package/dist/engine.d.ts +78 -0
- package/dist/engine.js +133 -0
- package/dist/error-observability.d.ts +7 -0
- package/dist/error-observability.js +61 -0
- package/dist/error-resolution.d.ts +4 -0
- package/dist/error-resolution.js +122 -0
- package/dist/errors.d.ts +33 -0
- package/dist/errors.js +40 -0
- package/dist/fixture-sanitization.d.ts +28 -0
- package/dist/fixture-sanitization.js +227 -0
- package/dist/health-scenario.d.ts +1842 -0
- package/dist/health-scenario.js +624 -0
- package/dist/index.d.ts +21 -10
- package/dist/index.js +12 -7
- package/dist/lint.d.ts +23 -11
- package/dist/lint.js +572 -69
- 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 +10 -2
- package/dist/provider.js +4 -1
- package/dist/runtime/auth-flow.d.ts +5 -1
- package/dist/runtime/auth-flow.js +6 -0
- package/dist/runtime/browser.d.ts +1 -0
- package/dist/runtime/browser.js +492 -49
- package/dist/runtime/cache.d.ts +1 -0
- package/dist/runtime/cache.js +169 -15
- 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 +490 -102
- package/dist/runtime/chrome149-header-order.d.ts +58 -0
- package/dist/runtime/chrome149-header-order.js +289 -0
- package/dist/runtime/env.js +12 -0
- package/dist/runtime/executor.d.ts +3 -2
- package/dist/runtime/executor.js +26 -25
- package/dist/runtime/http.d.ts +1 -0
- package/dist/runtime/http.js +515 -53
- package/dist/runtime/insights.js +2 -2
- package/dist/runtime/instrumentation.d.ts +2 -2
- package/dist/runtime/instrumentation.js +366 -8
- 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 +71 -2
- package/dist/runtime/otlp.js +397 -16
- package/dist/runtime/proxy-errors.js +6 -2
- package/dist/runtime/proxy-nodemaven.d.ts +56 -0
- package/dist/runtime/proxy-nodemaven.js +146 -0
- package/dist/runtime/proxy-telemetry.d.ts +80 -1
- package/dist/runtime/proxy-telemetry.js +154 -47
- 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 +4 -2
- 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 +14 -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 +22 -0
- package/dist/runtime/resolver-vendors/browser.js +377 -0
- package/dist/runtime/resolver-vendors/capsolver.d.ts +22 -0
- package/dist/runtime/resolver-vendors/capsolver.js +531 -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 +408 -0
- package/dist/runtime/resolver-vendors/types.d.ts +94 -0
- package/dist/runtime/resolver-vendors/types.js +96 -0
- package/dist/runtime/resolver.d.ts +72 -0
- package/dist/runtime/resolver.js +771 -0
- package/dist/runtime/secrets.d.ts +27 -0
- package/dist/runtime/secrets.js +51 -0
- package/dist/runtime/state.d.ts +3 -0
- package/dist/runtime/state.js +277 -71
- package/dist/runtime/stealth-cookies.d.ts +20 -0
- package/dist/runtime/stealth-cookies.js +111 -0
- package/dist/runtime/stealth.d.ts +41 -5
- package/dist/runtime/stealth.js +997 -291
- package/dist/runtime/stt.js +1 -12
- package/dist/runtime/timeout.d.ts +5 -0
- package/dist/runtime/timeout.js +12 -0
- package/dist/runtime/trace-config.d.ts +12 -0
- package/dist/runtime/trace-config.js +62 -0
- package/dist/runtime/trace.d.ts +5 -0
- package/dist/runtime/trace.js +43 -10
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +1 -1
- package/dist/server/error-observability.d.ts +1 -0
- package/dist/server/error-observability.js +1 -0
- package/dist/server/index.d.ts +5 -2
- package/dist/server/index.js +2 -2
- 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.d.ts +14 -3
- package/dist/server/self-test.js +129 -58
- package/dist/server/serve-implementation.d.ts +230 -0
- package/dist/server/serve-implementation.js +2265 -0
- package/dist/server/serve.d.ts +1 -70
- package/dist/server/serve.js +1 -1131
- package/dist/server/trace-output.d.ts +6 -0
- package/dist/server/trace-output.js +79 -0
- package/dist/server/types.d.ts +30 -5
- package/dist/server/types.js +13 -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 +9 -4
- package/dist/stealth/profiles.js +160 -211
- package/dist/stream-evidence.d.ts +74 -0
- package/dist/stream-evidence.js +785 -0
- package/dist/stream.js +7 -1
- package/dist/testing/index.d.ts +2 -1
- package/dist/testing/index.js +2 -1
- package/dist/testing/run.d.ts +32 -2
- package/dist/testing/run.js +489 -21
- package/dist/trace-sanitization.d.ts +5 -0
- package/dist/trace-sanitization.js +45 -0
- package/dist/types.d.ts +673 -131
- package/dist/types.js +1 -0
- package/package.json +45 -5
- package/src/auth-turn/index.ts +1 -1
- package/src/auth.ts +118 -0
- package/src/ceremonies/index.ts +189 -46
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/approval-override.ts.txt +6 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/codemod-syntax.ts.txt +3 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/connection-precedence.ts.txt +10 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/docs-conflict.ts.txt +8 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-map.ts.txt +5 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-operation.ts.txt +16 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-map.ts.txt +3 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoist-all.ts.txt +31 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoisted-const.ts.txt +11 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/imported-spread.ts.txt +11 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-map.ts.txt +11 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-cast-tail.ts.txt +21 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-ekitan.ts.txt +11 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-override.ts.txt +14 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/missing-english-locale.ts.txt +7 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/no-safety.ts.txt +6 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/non-literal.ts.txt +7 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/redundant-approval.ts.txt +6 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/safety-conflict.ts.txt +7 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/stream.ts.txt +7 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/tool-router-spread.ts.txt +15 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/unparseable.ts.txt +4 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/verbatim-template.ts.txt +12 -0
- package/src/cli/commands.ts +33 -0
- package/src/cli/create.ts +50 -37
- package/src/cli/migrate-operation-declaration.ts +1654 -0
- package/src/cli/migrate-operation-shape.ts +184 -0
- package/src/cli/migrate-provider-shape.ts +772 -0
- package/src/cli/prompt-assets.ts +865 -0
- package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
- package/src/cli/templates/provider/Dockerfile.tpl +1 -1
- package/src/cli/templates/provider/README.md.tpl +5 -5
- package/src/cli/templates/provider/index.ts.tpl +6 -3
- package/src/cli/templates/provider/operations/ping.ts.tpl +4 -1
- package/src/cli/templates/provider/provider.json.tpl +6 -0
- package/src/config/loader.ts +690 -163
- package/src/contract-serialization.ts +5 -7
- package/src/contract-types.ts +12 -5
- package/src/contract.ts +24 -10
- package/src/declaration-validation.ts +330 -0
- package/src/define.ts +1048 -131
- package/src/dev.ts +4 -1
- package/src/engine.ts +279 -0
- package/src/error-observability.ts +64 -0
- package/src/error-resolution.ts +127 -0
- package/src/errors.ts +68 -0
- package/src/fixture-sanitization.ts +264 -0
- package/src/health-scenario.ts +875 -0
- package/src/index.ts +220 -13
- package/src/lint.ts +698 -90
- 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 +161 -3
- package/src/runtime/auth-flow.ts +12 -0
- package/src/runtime/browser.ts +661 -63
- package/src/runtime/cache.ts +189 -14
- package/src/runtime/choice-wordlist.ts +145 -0
- package/src/runtime/choice.ts +631 -120
- package/src/runtime/chrome149-header-order.ts +330 -0
- package/src/runtime/env.ts +13 -0
- package/src/runtime/executor.ts +43 -31
- package/src/runtime/http.ts +641 -61
- package/src/runtime/insights.ts +2 -2
- package/src/runtime/instrumentation.ts +520 -15
- 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 +467 -21
- package/src/runtime/proxy-errors.ts +12 -4
- package/src/runtime/proxy-nodemaven.ts +221 -0
- package/src/runtime/proxy-telemetry.ts +244 -75
- package/src/runtime/redirects.ts +66 -0
- package/src/runtime/redis.ts +7 -2
- package/src/runtime/request-options.ts +680 -1
- package/src/runtime/resolver-config.ts +6 -0
- package/src/runtime/resolver-public.ts +23 -0
- package/src/runtime/resolver-shared.ts +33 -0
- package/src/runtime/resolver-vendors/bindings.ts +56 -0
- package/src/runtime/resolver-vendors/browser.ts +533 -0
- package/src/runtime/resolver-vendors/capsolver.ts +707 -0
- package/src/runtime/resolver-vendors/hosts.ts +38 -0
- package/src/runtime/resolver-vendors/twocaptcha.ts +540 -0
- package/src/runtime/resolver-vendors/types.ts +212 -0
- package/src/runtime/resolver.ts +1157 -0
- package/src/runtime/secrets.ts +64 -0
- package/src/runtime/state.ts +394 -77
- package/src/runtime/stealth-cookies.ts +132 -0
- package/src/runtime/stealth.ts +1280 -336
- package/src/runtime/stt.ts +1 -19
- package/src/runtime/timeout.ts +18 -0
- package/src/runtime/trace-config.ts +78 -0
- package/src/runtime/trace.ts +57 -17
- package/src/serve.ts +6 -1
- package/src/server/error-observability.ts +1 -0
- package/src/server/index.ts +34 -2
- package/src/server/self-test-input-tokens.ts +29 -14
- package/src/server/self-test.ts +191 -68
- package/src/server/serve-implementation.ts +3378 -0
- package/src/server/serve.ts +1 -1632
- package/src/server/trace-output.ts +129 -0
- package/src/server/types.ts +13 -1
- 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 +202 -231
- package/src/stream-evidence.ts +988 -0
- package/src/stream.ts +8 -1
- package/src/testing/index.ts +10 -1
- package/src/testing/run.ts +658 -15
- package/src/trace-sanitization.ts +63 -0
- package/src/types.ts +788 -174
- 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/dist/runtime/browser.js
CHANGED
|
@@ -15,7 +15,7 @@ function toResourceBody(body) {
|
|
|
15
15
|
return Buffer.from(body);
|
|
16
16
|
}
|
|
17
17
|
function isResourceMethod(method) {
|
|
18
|
-
return method === "GET" || method === "HEAD";
|
|
18
|
+
return method === "GET" || method === "HEAD" || method === "POST";
|
|
19
19
|
}
|
|
20
20
|
async function toResourceRequest(request) {
|
|
21
21
|
const method = request.method().toUpperCase();
|
|
@@ -59,6 +59,68 @@ function getCdpPausedRequestId(params) {
|
|
|
59
59
|
}
|
|
60
60
|
return params.requestId;
|
|
61
61
|
}
|
|
62
|
+
function isCdpResponseStage(params) {
|
|
63
|
+
return (isRecord(params) &&
|
|
64
|
+
(typeof params.responseStatusCode === "number" ||
|
|
65
|
+
typeof params.responseErrorReason === "string"));
|
|
66
|
+
}
|
|
67
|
+
function toCdpPausedDocumentResponse(params) {
|
|
68
|
+
if (!isRecord(params) ||
|
|
69
|
+
typeof params.requestId !== "string" ||
|
|
70
|
+
typeof params.responseStatusCode !== "number" ||
|
|
71
|
+
params.resourceType !== "Document" ||
|
|
72
|
+
!isRecord(params.request)) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const responseCode = params.responseStatusCode;
|
|
76
|
+
const responseHeaders = Array.isArray(params.responseHeaders)
|
|
77
|
+
? params.responseHeaders.flatMap((header) => {
|
|
78
|
+
if (!isRecord(header) ||
|
|
79
|
+
typeof header.name !== "string" ||
|
|
80
|
+
typeof header.value !== "string") {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
return [{ name: header.name, value: header.value }];
|
|
84
|
+
})
|
|
85
|
+
: [];
|
|
86
|
+
return {
|
|
87
|
+
hasBody: String(params.request.method ?? "").toUpperCase() !== "HEAD" &&
|
|
88
|
+
responseCode >= 200 &&
|
|
89
|
+
(responseCode < 300 || responseCode >= 400) &&
|
|
90
|
+
responseCode !== 204 &&
|
|
91
|
+
responseCode !== 205 &&
|
|
92
|
+
responseCode !== 304,
|
|
93
|
+
requestId: params.requestId,
|
|
94
|
+
responseCode,
|
|
95
|
+
responseHeaders,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function toCdpDocumentFulfillParams(response, body, contentSecurityPolicy) {
|
|
99
|
+
const responseBody = typeof body.body === "string" ? body.body : "";
|
|
100
|
+
return {
|
|
101
|
+
body: body.base64Encoded ? responseBody : Buffer.from(responseBody).toString("base64"),
|
|
102
|
+
requestId: response.requestId,
|
|
103
|
+
responseCode: response.responseCode,
|
|
104
|
+
responseHeaders: [
|
|
105
|
+
...response.responseHeaders,
|
|
106
|
+
{ name: "Content-Security-Policy", value: contentSecurityPolicy },
|
|
107
|
+
],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function resourcePolicyFetchPatterns(policy) {
|
|
111
|
+
return [
|
|
112
|
+
{ requestStage: "Request", urlPattern: "*" },
|
|
113
|
+
...(policy.documentContentSecurityPolicy
|
|
114
|
+
? [
|
|
115
|
+
{
|
|
116
|
+
requestStage: "Response",
|
|
117
|
+
resourceType: "Document",
|
|
118
|
+
urlPattern: "*",
|
|
119
|
+
},
|
|
120
|
+
]
|
|
121
|
+
: []),
|
|
122
|
+
];
|
|
123
|
+
}
|
|
62
124
|
function toCdpResourceHeaders(value) {
|
|
63
125
|
if (!isRecord(value)) {
|
|
64
126
|
return {};
|
|
@@ -104,6 +166,128 @@ async function fulfillResourceRoute(route, decision) {
|
|
|
104
166
|
status: decision.status ?? 200,
|
|
105
167
|
});
|
|
106
168
|
}
|
|
169
|
+
async function decideResourceRequest(policy, request) {
|
|
170
|
+
for (const resourceRoute of policy.routes) {
|
|
171
|
+
if (matchesResourceRoute(resourceRoute.match, request)) {
|
|
172
|
+
return await resourceRoute.handle(request);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return { action: "block" };
|
|
176
|
+
}
|
|
177
|
+
async function abortResourceRoute(route) {
|
|
178
|
+
await route.abort("blockedbyclient");
|
|
179
|
+
}
|
|
180
|
+
async function handleCdpResourceRequest(session, policy, allowedMethods, params) {
|
|
181
|
+
const paused = toCdpResourceRequest(params);
|
|
182
|
+
const requestId = paused?.requestId ?? getCdpPausedRequestId(params);
|
|
183
|
+
if (!requestId)
|
|
184
|
+
return;
|
|
185
|
+
try {
|
|
186
|
+
if (!paused || !allowedMethods.has(paused.request.method)) {
|
|
187
|
+
await session.send("Fetch.failRequest", { errorReason: "BlockedByClient", requestId });
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const decision = await decideResourceRequest(policy, paused.request);
|
|
191
|
+
switch (decision.action) {
|
|
192
|
+
case "continue":
|
|
193
|
+
await session.send("Fetch.continueRequest", { requestId });
|
|
194
|
+
return;
|
|
195
|
+
case "fulfill":
|
|
196
|
+
await session.send("Fetch.fulfillRequest", toCdpFulfillParams(requestId, decision));
|
|
197
|
+
return;
|
|
198
|
+
case "block":
|
|
199
|
+
await session.send("Fetch.failRequest", { errorReason: "BlockedByClient", requestId });
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
await session
|
|
205
|
+
.send("Fetch.failRequest", { errorReason: "BlockedByClient", requestId })
|
|
206
|
+
.catch(() => undefined);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async function handleCdpDocumentResponse(session, contentSecurityPolicy, params) {
|
|
210
|
+
const requestId = getCdpPausedRequestId(params);
|
|
211
|
+
if (!requestId)
|
|
212
|
+
return;
|
|
213
|
+
try {
|
|
214
|
+
const response = toCdpPausedDocumentResponse(params);
|
|
215
|
+
if (!response)
|
|
216
|
+
throw new Error("CDP document response metadata is unavailable");
|
|
217
|
+
if (!response.hasBody) {
|
|
218
|
+
await session.send("Fetch.continueResponse", {
|
|
219
|
+
requestId,
|
|
220
|
+
responseCode: response.responseCode,
|
|
221
|
+
responseHeaders: [...response.responseHeaders],
|
|
222
|
+
});
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const body = await session.send("Fetch.getResponseBody", {
|
|
226
|
+
requestId: response.requestId,
|
|
227
|
+
});
|
|
228
|
+
await session.send("Fetch.fulfillRequest", toCdpDocumentFulfillParams(response, body, contentSecurityPolicy));
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
await session
|
|
232
|
+
.send("Fetch.failRequest", { errorReason: "BlockedByClient", requestId })
|
|
233
|
+
.catch(() => undefined);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function getCdpAuthRequiredRequestId(params) {
|
|
237
|
+
if (!isRecord(params) || typeof params.requestId !== "string") {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
return params.requestId;
|
|
241
|
+
}
|
|
242
|
+
function isCdpProxyAuthChallenge(params) {
|
|
243
|
+
return (isRecord(params) &&
|
|
244
|
+
isRecord(params.authChallenge) &&
|
|
245
|
+
params.authChallenge.source === "Proxy");
|
|
246
|
+
}
|
|
247
|
+
async function handleCdpAuthRequired(session, proxy, authAttempts, params) {
|
|
248
|
+
const requestId = getCdpAuthRequiredRequestId(params);
|
|
249
|
+
if (!requestId)
|
|
250
|
+
return;
|
|
251
|
+
// Chromium can emit another challenge after rejected credentials. Cancel a
|
|
252
|
+
// retry so incorrect credentials fail promptly instead of looping forever.
|
|
253
|
+
const response = isCdpProxyAuthChallenge(params) && !authAttempts.has(requestId)
|
|
254
|
+
? (() => {
|
|
255
|
+
authAttempts.add(requestId);
|
|
256
|
+
return {
|
|
257
|
+
authChallengeResponse: {
|
|
258
|
+
password: proxy.password,
|
|
259
|
+
response: "ProvideCredentials",
|
|
260
|
+
username: proxy.username,
|
|
261
|
+
},
|
|
262
|
+
requestId,
|
|
263
|
+
};
|
|
264
|
+
})()
|
|
265
|
+
: {
|
|
266
|
+
authChallengeResponse: { response: "CancelAuth" },
|
|
267
|
+
requestId,
|
|
268
|
+
};
|
|
269
|
+
await session.send("Fetch.continueWithAuth", response).catch(() => undefined);
|
|
270
|
+
}
|
|
271
|
+
function createResourcePolicyHandler(policy, allowedMethods) {
|
|
272
|
+
return async (route) => {
|
|
273
|
+
const request = await toResourceRequest(route.request());
|
|
274
|
+
if (!request || !allowedMethods.has(request.method)) {
|
|
275
|
+
await abortResourceRoute(route);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const decision = await decideResourceRequest(policy, request);
|
|
279
|
+
switch (decision.action) {
|
|
280
|
+
case "continue":
|
|
281
|
+
await route.continue();
|
|
282
|
+
return;
|
|
283
|
+
case "fulfill":
|
|
284
|
+
await fulfillResourceRoute(route, decision);
|
|
285
|
+
return;
|
|
286
|
+
case "block":
|
|
287
|
+
await abortResourceRoute(route);
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
}
|
|
107
291
|
function getDefaultCdpPoolUrl(env = process.env) {
|
|
108
292
|
return env.APIFUSE__CDP_POOL__URL;
|
|
109
293
|
}
|
|
@@ -139,12 +323,60 @@ function formatExpression(fn) {
|
|
|
139
323
|
}
|
|
140
324
|
return `(${fn.toString()})()`;
|
|
141
325
|
}
|
|
142
|
-
function toLaunchOptions(options) {
|
|
326
|
+
function toLaunchOptions(options, proxy) {
|
|
143
327
|
return {
|
|
144
|
-
|
|
328
|
+
// `extraArgs` is optional, but playwright-extra's stealth evasions mutate
|
|
329
|
+
// `options.args` unguarded (navigator.webdriver does
|
|
330
|
+
// `options.args.findIndex(...)` in beforeLaunch). Forwarding `undefined`
|
|
331
|
+
// therefore crashes every stealth launch that omits extraArgs with
|
|
332
|
+
// "TypeError: undefined is not an object (evaluating 'options.args.findIndex')".
|
|
333
|
+
// Always hand the launcher a concrete array.
|
|
334
|
+
args: options.extraArgs ?? [],
|
|
145
335
|
executablePath: options.executablePath,
|
|
146
336
|
headless: options.headless ?? true,
|
|
147
|
-
proxy
|
|
337
|
+
proxy,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
function hasProxyCredentials(proxy) {
|
|
341
|
+
return proxy?.username !== undefined && proxy.password !== undefined;
|
|
342
|
+
}
|
|
343
|
+
function toPlaywrightProxy(proxy) {
|
|
344
|
+
if (!proxy)
|
|
345
|
+
return undefined;
|
|
346
|
+
const schemeEnd = proxy.indexOf("://");
|
|
347
|
+
const authorityStart = schemeEnd >= 0 ? schemeEnd + 3 : 0;
|
|
348
|
+
const remainder = proxy.slice(authorityStart);
|
|
349
|
+
const authorityEnd = remainder.search(/[/?#]/);
|
|
350
|
+
const authority = remainder.slice(0, authorityEnd >= 0 ? authorityEnd : undefined);
|
|
351
|
+
if (!authority.includes("@")) {
|
|
352
|
+
return { server: proxy };
|
|
353
|
+
}
|
|
354
|
+
let parsed;
|
|
355
|
+
try {
|
|
356
|
+
parsed = new URL(proxy);
|
|
357
|
+
}
|
|
358
|
+
catch {
|
|
359
|
+
throw new ProviderError("Browser proxy URL is invalid", {
|
|
360
|
+
code: "BROWSER_PROXY_INVALID",
|
|
361
|
+
fix: "Use a valid proxy URL.",
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
let username;
|
|
365
|
+
let password;
|
|
366
|
+
try {
|
|
367
|
+
username = decodeURIComponent(parsed.username);
|
|
368
|
+
password = decodeURIComponent(parsed.password);
|
|
369
|
+
}
|
|
370
|
+
catch {
|
|
371
|
+
throw new ProviderError("Browser proxy credentials use invalid percent-encoding", {
|
|
372
|
+
code: "BROWSER_PROXY_INVALID",
|
|
373
|
+
fix: "Percent-encode the proxy username and password as URL userinfo.",
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
server: `${parsed.protocol}//${parsed.host}`,
|
|
378
|
+
username,
|
|
379
|
+
password,
|
|
148
380
|
};
|
|
149
381
|
}
|
|
150
382
|
class PlaywrightBrowserLocator {
|
|
@@ -309,13 +541,15 @@ async function loadSeleniumBase() {
|
|
|
309
541
|
}
|
|
310
542
|
class PlaywrightBrowserPage {
|
|
311
543
|
page;
|
|
544
|
+
proxy;
|
|
312
545
|
id = "main";
|
|
313
546
|
pageId;
|
|
314
|
-
constructor(page) {
|
|
547
|
+
constructor(page, proxy = undefined) {
|
|
315
548
|
this.page = page;
|
|
549
|
+
this.proxy = proxy;
|
|
316
550
|
}
|
|
317
|
-
async goto(url) {
|
|
318
|
-
await this.page.goto(url);
|
|
551
|
+
async goto(url, options) {
|
|
552
|
+
await this.page.goto(url, options);
|
|
319
553
|
}
|
|
320
554
|
async evaluate(fn) {
|
|
321
555
|
if (typeof fn === "string") {
|
|
@@ -323,6 +557,9 @@ class PlaywrightBrowserPage {
|
|
|
323
557
|
}
|
|
324
558
|
return await this.page.evaluate(fn);
|
|
325
559
|
}
|
|
560
|
+
async userAgent() {
|
|
561
|
+
return await this.evaluate("navigator.userAgent");
|
|
562
|
+
}
|
|
326
563
|
async waitForSelector(selector, options) {
|
|
327
564
|
await this.page.waitForSelector(selector, options);
|
|
328
565
|
}
|
|
@@ -357,42 +594,107 @@ class PlaywrightBrowserPage {
|
|
|
357
594
|
async close() {
|
|
358
595
|
await this.page.close();
|
|
359
596
|
}
|
|
597
|
+
async cookies() {
|
|
598
|
+
return (await this.page.context().cookies()).map(toBrowserCookie);
|
|
599
|
+
}
|
|
360
600
|
async withResourcePolicy(policy, run) {
|
|
361
601
|
const allowedMethods = new Set(policy.allowedMethods ?? DEFAULT_RESOURCE_METHODS);
|
|
602
|
+
const policyHandler = createResourcePolicyHandler(policy, allowedMethods);
|
|
603
|
+
const context = this.page.context();
|
|
362
604
|
const handler = async (route) => {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
605
|
+
try {
|
|
606
|
+
// Context routing sees a popup's first request before the late `page`
|
|
607
|
+
// event. Fail every secondary page closed so no redirect can escape
|
|
608
|
+
// the per-page CDP interception while that session is being attached.
|
|
609
|
+
if (route.request().frame().page() !== this.page) {
|
|
610
|
+
await abortResourceRoute(route);
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
catch {
|
|
615
|
+
await abortResourceRoute(route);
|
|
366
616
|
return;
|
|
367
617
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
618
|
+
await policyHandler(route);
|
|
619
|
+
};
|
|
620
|
+
const sessions = new Set();
|
|
621
|
+
const pendingAttachments = new Set();
|
|
622
|
+
const proxy = hasProxyCredentials(this.proxy) ? this.proxy : undefined;
|
|
623
|
+
let active = true;
|
|
624
|
+
const attachPage = async (page) => {
|
|
625
|
+
const session = await context.newCDPSession(page);
|
|
626
|
+
if (!active) {
|
|
627
|
+
await session.detach();
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
const onRequestPaused = (params) => {
|
|
631
|
+
if (isCdpResponseStage(params) && policy.documentContentSecurityPolicy) {
|
|
632
|
+
void handleCdpDocumentResponse(session, policy.documentContentSecurityPolicy, params);
|
|
633
|
+
return;
|
|
371
634
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
635
|
+
void handleCdpResourceRequest(session, policy, allowedMethods, params);
|
|
636
|
+
};
|
|
637
|
+
const authAttempts = new Set();
|
|
638
|
+
const onAuthRequired = (params) => {
|
|
639
|
+
if (!proxy)
|
|
640
|
+
return;
|
|
641
|
+
void handleCdpAuthRequired(session, proxy, authAttempts, params);
|
|
642
|
+
};
|
|
643
|
+
session.on("Fetch.requestPaused", onRequestPaused);
|
|
644
|
+
if (proxy) {
|
|
645
|
+
session.on("Fetch.authRequired", onAuthRequired);
|
|
646
|
+
}
|
|
647
|
+
try {
|
|
648
|
+
await session.send("Fetch.enable", {
|
|
649
|
+
...(proxy ? { handleAuthRequests: true } : {}),
|
|
650
|
+
patterns: resourcePolicyFetchPatterns(policy),
|
|
651
|
+
});
|
|
652
|
+
sessions.add(session);
|
|
653
|
+
}
|
|
654
|
+
catch (error) {
|
|
655
|
+
session.off("Fetch.requestPaused", onRequestPaused);
|
|
656
|
+
if (proxy) {
|
|
657
|
+
session.off("Fetch.authRequired", onAuthRequired);
|
|
380
658
|
}
|
|
659
|
+
await session.detach().catch(() => undefined);
|
|
660
|
+
throw error;
|
|
381
661
|
}
|
|
382
|
-
await route.abort("blockedbyclient");
|
|
383
662
|
};
|
|
384
|
-
|
|
663
|
+
const onPage = (page) => {
|
|
664
|
+
const attachment = attachPage(page)
|
|
665
|
+
.catch(async () => {
|
|
666
|
+
await page.close().catch(() => undefined);
|
|
667
|
+
})
|
|
668
|
+
.finally(() => pendingAttachments.delete(attachment));
|
|
669
|
+
pendingAttachments.add(attachment);
|
|
670
|
+
};
|
|
671
|
+
await context.route(RESOURCE_POLICY_ROUTE_PATTERN, handler);
|
|
385
672
|
try {
|
|
386
|
-
|
|
673
|
+
context.on("page", onPage);
|
|
674
|
+
try {
|
|
675
|
+
await attachPage(this.page);
|
|
676
|
+
return await run();
|
|
677
|
+
}
|
|
678
|
+
finally {
|
|
679
|
+
context.off("page", onPage);
|
|
680
|
+
}
|
|
387
681
|
}
|
|
388
682
|
finally {
|
|
389
|
-
|
|
683
|
+
active = false;
|
|
684
|
+
await context.unroute(RESOURCE_POLICY_ROUTE_PATTERN, handler);
|
|
685
|
+
await Promise.allSettled(pendingAttachments);
|
|
686
|
+
await Promise.all([...sessions].map(async (session) => {
|
|
687
|
+
await session.send("Fetch.disable").catch(() => undefined);
|
|
688
|
+
await session.detach().catch(() => undefined);
|
|
689
|
+
}));
|
|
390
690
|
}
|
|
391
691
|
}
|
|
392
692
|
}
|
|
393
693
|
class PlaywrightBrowserClient {
|
|
394
694
|
options;
|
|
395
695
|
browser = null;
|
|
696
|
+
parsedProxy;
|
|
697
|
+
proxyParsed = false;
|
|
396
698
|
engine = "playwright-stealth";
|
|
397
699
|
constructor(options = {}) {
|
|
398
700
|
this.options = options;
|
|
@@ -402,13 +704,17 @@ class PlaywrightBrowserClient {
|
|
|
402
704
|
return this.browser;
|
|
403
705
|
}
|
|
404
706
|
const chromium = await loadChromiumLauncher(this.options);
|
|
405
|
-
|
|
707
|
+
if (!this.proxyParsed) {
|
|
708
|
+
this.parsedProxy = toPlaywrightProxy(this.options.proxy);
|
|
709
|
+
this.proxyParsed = true;
|
|
710
|
+
}
|
|
711
|
+
this.browser = await chromium.launch(toLaunchOptions(this.options, this.parsedProxy));
|
|
406
712
|
return this.browser;
|
|
407
713
|
}
|
|
408
714
|
async newPage() {
|
|
409
715
|
const browser = await this.ensureBrowser();
|
|
410
716
|
const page = await browser.newPage();
|
|
411
|
-
return new PlaywrightBrowserPage(page);
|
|
717
|
+
return new PlaywrightBrowserPage(page, this.parsedProxy);
|
|
412
718
|
}
|
|
413
719
|
async rawPage() {
|
|
414
720
|
throw new ProviderError("ctx.browser.rawPage() requires a CDP pool", {
|
|
@@ -418,9 +724,9 @@ class PlaywrightBrowserClient {
|
|
|
418
724
|
}
|
|
419
725
|
async withIsolatedContext(handler) {
|
|
420
726
|
const browser = await this.ensureBrowser();
|
|
421
|
-
const context = await browser.newContext();
|
|
727
|
+
const context = await browser.newContext({ serviceWorkers: this.options.serviceWorkers });
|
|
422
728
|
const page = await context.newPage();
|
|
423
|
-
const browserPage = new PlaywrightBrowserPage(page);
|
|
729
|
+
const browserPage = new PlaywrightBrowserPage(page, this.parsedProxy);
|
|
424
730
|
try {
|
|
425
731
|
return await handler(browserPage);
|
|
426
732
|
}
|
|
@@ -457,7 +763,7 @@ function normalizeWebSocketEndpoint(endpoint) {
|
|
|
457
763
|
}
|
|
458
764
|
throw new Error(`Unsupported WebSocket endpoint protocol: ${url.protocol}`);
|
|
459
765
|
}
|
|
460
|
-
class
|
|
766
|
+
class WebSocketCommandClient {
|
|
461
767
|
nextId = 1;
|
|
462
768
|
endpoint;
|
|
463
769
|
listeners = new Map();
|
|
@@ -483,12 +789,7 @@ class JsonRpcWebSocketClient {
|
|
|
483
789
|
const id = this.nextId++;
|
|
484
790
|
return await new Promise((resolve, reject) => {
|
|
485
791
|
this.pending.set(id, { resolve, reject });
|
|
486
|
-
socket.send(JSON.stringify(
|
|
487
|
-
id,
|
|
488
|
-
jsonrpc: "2.0",
|
|
489
|
-
method,
|
|
490
|
-
params,
|
|
491
|
-
}));
|
|
792
|
+
socket.send(JSON.stringify(this.createCommandFrame(id, method, params)));
|
|
492
793
|
});
|
|
493
794
|
}
|
|
494
795
|
async close() {
|
|
@@ -526,7 +827,11 @@ class JsonRpcWebSocketClient {
|
|
|
526
827
|
}
|
|
527
828
|
this.pending.delete(payload.id);
|
|
528
829
|
if (payload.error) {
|
|
529
|
-
|
|
830
|
+
const error = new Error(payload.error.message ?? "JSON-RPC command failed");
|
|
831
|
+
if (typeof payload.error.code === "number") {
|
|
832
|
+
Object.assign(error, { code: payload.error.code });
|
|
833
|
+
}
|
|
834
|
+
pending.reject(error);
|
|
530
835
|
return;
|
|
531
836
|
}
|
|
532
837
|
pending.resolve(payload.result ?? {});
|
|
@@ -554,6 +859,26 @@ class JsonRpcWebSocketClient {
|
|
|
554
859
|
return this.socketPromise;
|
|
555
860
|
}
|
|
556
861
|
}
|
|
862
|
+
class CdpWebSocketClient extends WebSocketCommandClient {
|
|
863
|
+
sessionId;
|
|
864
|
+
constructor(endpoint, sessionId) {
|
|
865
|
+
super(endpoint);
|
|
866
|
+
this.sessionId = sessionId;
|
|
867
|
+
}
|
|
868
|
+
createCommandFrame(id, method, params) {
|
|
869
|
+
return {
|
|
870
|
+
id,
|
|
871
|
+
method,
|
|
872
|
+
params,
|
|
873
|
+
...(this.sessionId === undefined ? {} : { sessionId: this.sessionId }),
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
class JsonRpcWebSocketClient extends WebSocketCommandClient {
|
|
878
|
+
createCommandFrame(id, method, params) {
|
|
879
|
+
return { id, jsonrpc: "2.0", method, params };
|
|
880
|
+
}
|
|
881
|
+
}
|
|
557
882
|
function flattenCdpFrameTree(node, out = []) {
|
|
558
883
|
if (!node) {
|
|
559
884
|
return out;
|
|
@@ -567,6 +892,48 @@ function flattenCdpFrameTree(node, out = []) {
|
|
|
567
892
|
function isRecord(value) {
|
|
568
893
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
569
894
|
}
|
|
895
|
+
function isBrowserCookieSameSite(value) {
|
|
896
|
+
return value === "Strict" || value === "Lax" || value === "None";
|
|
897
|
+
}
|
|
898
|
+
function toBrowserCookie(cookie) {
|
|
899
|
+
return {
|
|
900
|
+
name: cookie.name,
|
|
901
|
+
value: cookie.value,
|
|
902
|
+
domain: cookie.domain,
|
|
903
|
+
path: cookie.path,
|
|
904
|
+
...(cookie.expires !== undefined && cookie.expires > 0 ? { expires: cookie.expires } : {}),
|
|
905
|
+
httpOnly: cookie.httpOnly,
|
|
906
|
+
secure: cookie.secure,
|
|
907
|
+
...(isBrowserCookieSameSite(cookie.sameSite) ? { sameSite: cookie.sameSite } : {}),
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
function parseCdpCookies(value) {
|
|
911
|
+
if (!Array.isArray(value)) {
|
|
912
|
+
throw new Error("CDP Network.getCookies returned an invalid cookie list");
|
|
913
|
+
}
|
|
914
|
+
return value.map((cookie) => {
|
|
915
|
+
if (!isRecord(cookie) ||
|
|
916
|
+
typeof cookie.name !== "string" ||
|
|
917
|
+
typeof cookie.value !== "string" ||
|
|
918
|
+
typeof cookie.domain !== "string" ||
|
|
919
|
+
typeof cookie.path !== "string" ||
|
|
920
|
+
typeof cookie.expires !== "number" ||
|
|
921
|
+
typeof cookie.httpOnly !== "boolean" ||
|
|
922
|
+
typeof cookie.secure !== "boolean") {
|
|
923
|
+
throw new Error("CDP Network.getCookies returned an invalid cookie");
|
|
924
|
+
}
|
|
925
|
+
return toBrowserCookie({
|
|
926
|
+
name: cookie.name,
|
|
927
|
+
value: cookie.value,
|
|
928
|
+
domain: cookie.domain,
|
|
929
|
+
path: cookie.path,
|
|
930
|
+
expires: cookie.expires,
|
|
931
|
+
httpOnly: cookie.httpOnly,
|
|
932
|
+
secure: cookie.secure,
|
|
933
|
+
sameSite: cookie.sameSite,
|
|
934
|
+
});
|
|
935
|
+
});
|
|
936
|
+
}
|
|
570
937
|
function parsePoolAcquireResponse(value) {
|
|
571
938
|
if (!isRecord(value) ||
|
|
572
939
|
typeof value.pageId !== "string" ||
|
|
@@ -620,6 +987,17 @@ function getCdpExecutionContext(params) {
|
|
|
620
987
|
id: typeof contextId === "number" ? contextId : undefined,
|
|
621
988
|
};
|
|
622
989
|
}
|
|
990
|
+
function getCdpDestroyedExecutionContextId(params) {
|
|
991
|
+
if (!isRecord(params)) {
|
|
992
|
+
return undefined;
|
|
993
|
+
}
|
|
994
|
+
return typeof params.executionContextId === "number"
|
|
995
|
+
? params.executionContextId
|
|
996
|
+
: undefined;
|
|
997
|
+
}
|
|
998
|
+
function isMissingExecutionContextError(error) {
|
|
999
|
+
return error instanceof Error && /\b(?:cannot|failed to) find context\b/i.test(error.message);
|
|
1000
|
+
}
|
|
623
1001
|
class CdpBrowserLocator {
|
|
624
1002
|
frame;
|
|
625
1003
|
selector;
|
|
@@ -723,16 +1101,22 @@ class CdpPoolBrowserPage {
|
|
|
723
1101
|
get id() {
|
|
724
1102
|
return this.pageId;
|
|
725
1103
|
}
|
|
726
|
-
async goto(url) {
|
|
1104
|
+
async goto(url, options) {
|
|
727
1105
|
await this.initialize();
|
|
728
1106
|
const startedAt = Date.now();
|
|
729
|
-
|
|
730
|
-
const
|
|
731
|
-
|
|
1107
|
+
const timeout = options?.timeout ?? DEFAULT_WAIT_TIMEOUT_MS;
|
|
1108
|
+
const waitUntil = options?.waitUntil ?? "load";
|
|
1109
|
+
let expectedEventSeen = false;
|
|
1110
|
+
const eventName = waitUntil === "domcontentloaded" ? "Page.domContentEventFired" : "Page.loadEventFired";
|
|
1111
|
+
const unsubscribe = this.pageClient.on(eventName, () => {
|
|
1112
|
+
expectedEventSeen = true;
|
|
732
1113
|
});
|
|
733
1114
|
try {
|
|
734
|
-
await this.pageClient.send("Page.navigate", { url });
|
|
735
|
-
|
|
1115
|
+
const navigation = (await this.pageClient.send("Page.navigate", { url }));
|
|
1116
|
+
if (typeof navigation.errorText === "string" && navigation.errorText.length > 0) {
|
|
1117
|
+
throw new Error(`Page.navigate failed: ${navigation.errorText} at ${url}`);
|
|
1118
|
+
}
|
|
1119
|
+
await this.waitForDocumentReady(startedAt + timeout, () => expectedEventSeen, waitUntil);
|
|
736
1120
|
}
|
|
737
1121
|
finally {
|
|
738
1122
|
unsubscribe();
|
|
@@ -742,10 +1126,25 @@ class CdpPoolBrowserPage {
|
|
|
742
1126
|
await this.initialize();
|
|
743
1127
|
return await this.evaluateWithContext(fn);
|
|
744
1128
|
}
|
|
1129
|
+
async userAgent() {
|
|
1130
|
+
return await this.evaluate("navigator.userAgent");
|
|
1131
|
+
}
|
|
745
1132
|
async evaluateInFrame(frameId, fn) {
|
|
746
1133
|
await this.initialize();
|
|
747
1134
|
const contextId = await this.getFrameExecutionContextId(frameId);
|
|
748
|
-
|
|
1135
|
+
try {
|
|
1136
|
+
return await this.evaluateWithContext(fn, contextId);
|
|
1137
|
+
}
|
|
1138
|
+
catch (error) {
|
|
1139
|
+
if (!isMissingExecutionContextError(error)) {
|
|
1140
|
+
throw error;
|
|
1141
|
+
}
|
|
1142
|
+
if (this.frameExecutionContexts.get(frameId) === contextId) {
|
|
1143
|
+
this.frameExecutionContexts.delete(frameId);
|
|
1144
|
+
}
|
|
1145
|
+
const refreshedContextId = await this.getFrameExecutionContextId(frameId);
|
|
1146
|
+
return await this.evaluateWithContext(fn, refreshedContextId);
|
|
1147
|
+
}
|
|
749
1148
|
}
|
|
750
1149
|
async waitForSelectorInFrame(frameId, selector, options) {
|
|
751
1150
|
const timeout = options?.timeout ?? DEFAULT_WAIT_TIMEOUT_MS;
|
|
@@ -845,6 +1244,11 @@ class CdpPoolBrowserPage {
|
|
|
845
1244
|
});
|
|
846
1245
|
return Buffer.from(String(result.data ?? ""), "base64");
|
|
847
1246
|
}
|
|
1247
|
+
async cookies() {
|
|
1248
|
+
await this.initialize();
|
|
1249
|
+
const result = await this.pageClient.send("Network.getCookies");
|
|
1250
|
+
return parseCdpCookies(result.cookies);
|
|
1251
|
+
}
|
|
848
1252
|
async close() {
|
|
849
1253
|
if (this.closed) {
|
|
850
1254
|
return;
|
|
@@ -868,7 +1272,7 @@ class CdpPoolBrowserPage {
|
|
|
868
1272
|
const unsubscribe = this.pageClient.on("Fetch.requestPaused", handlePausedRequest);
|
|
869
1273
|
try {
|
|
870
1274
|
await this.pageClient.send("Fetch.enable", {
|
|
871
|
-
patterns:
|
|
1275
|
+
patterns: resourcePolicyFetchPatterns(policy),
|
|
872
1276
|
});
|
|
873
1277
|
}
|
|
874
1278
|
catch (error) {
|
|
@@ -893,6 +1297,24 @@ class CdpPoolBrowserPage {
|
|
|
893
1297
|
return;
|
|
894
1298
|
}
|
|
895
1299
|
try {
|
|
1300
|
+
if (isCdpResponseStage(params) && policy.documentContentSecurityPolicy) {
|
|
1301
|
+
const response = toCdpPausedDocumentResponse(params);
|
|
1302
|
+
if (!response)
|
|
1303
|
+
throw new Error("CDP document response metadata is unavailable");
|
|
1304
|
+
if (!response.hasBody) {
|
|
1305
|
+
await this.pageClient.send("Fetch.continueResponse", {
|
|
1306
|
+
requestId,
|
|
1307
|
+
responseCode: response.responseCode,
|
|
1308
|
+
responseHeaders: [...response.responseHeaders],
|
|
1309
|
+
});
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
const body = await this.pageClient.send("Fetch.getResponseBody", {
|
|
1313
|
+
requestId: response.requestId,
|
|
1314
|
+
});
|
|
1315
|
+
await this.pageClient.send("Fetch.fulfillRequest", toCdpDocumentFulfillParams(response, body, policy.documentContentSecurityPolicy));
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
896
1318
|
const parsed = toCdpResourceRequest(params);
|
|
897
1319
|
if (!parsed || !allowedMethods.has(parsed.request.method)) {
|
|
898
1320
|
await this.failCdpResourceRequest(requestId);
|
|
@@ -904,6 +1326,11 @@ class CdpPoolBrowserPage {
|
|
|
904
1326
|
}
|
|
905
1327
|
const decision = await resourceRoute.handle(parsed.request);
|
|
906
1328
|
switch (decision.action) {
|
|
1329
|
+
case "continue":
|
|
1330
|
+
await this.pageClient.send("Fetch.continueRequest", {
|
|
1331
|
+
requestId: parsed.requestId,
|
|
1332
|
+
});
|
|
1333
|
+
return;
|
|
907
1334
|
case "fulfill":
|
|
908
1335
|
await this.pageClient.send("Fetch.fulfillRequest", toCdpFulfillParams(parsed.requestId, decision));
|
|
909
1336
|
return;
|
|
@@ -941,6 +1368,20 @@ class CdpPoolBrowserPage {
|
|
|
941
1368
|
this.frameExecutionContexts.set(context.frameId, context.id);
|
|
942
1369
|
}
|
|
943
1370
|
});
|
|
1371
|
+
this.pageClient.on("Runtime.executionContextDestroyed", (params) => {
|
|
1372
|
+
const destroyedContextId = getCdpDestroyedExecutionContextId(params);
|
|
1373
|
+
if (destroyedContextId === undefined) {
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
for (const [frameId, contextId] of this.frameExecutionContexts) {
|
|
1377
|
+
if (contextId === destroyedContextId) {
|
|
1378
|
+
this.frameExecutionContexts.delete(frameId);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
});
|
|
1382
|
+
this.pageClient.on("Runtime.executionContextsCleared", () => {
|
|
1383
|
+
this.frameExecutionContexts.clear();
|
|
1384
|
+
});
|
|
944
1385
|
await this.pageClient.send("Page.enable");
|
|
945
1386
|
await this.pageClient.send("Runtime.enable");
|
|
946
1387
|
this.initialized = true;
|
|
@@ -962,11 +1403,13 @@ class CdpPoolBrowserPage {
|
|
|
962
1403
|
this.frameExecutionContexts.set(frameId, contextId);
|
|
963
1404
|
return contextId;
|
|
964
1405
|
}
|
|
965
|
-
async waitForDocumentReady(deadline,
|
|
1406
|
+
async waitForDocumentReady(deadline, isExpectedEventSeen, waitUntil) {
|
|
966
1407
|
while (Date.now() < deadline) {
|
|
967
1408
|
const readyState = await this.evaluate("document.readyState");
|
|
968
|
-
|
|
969
|
-
|
|
1409
|
+
const documentReady = readyState === "complete" ||
|
|
1410
|
+
(waitUntil === "domcontentloaded" && readyState === "interactive");
|
|
1411
|
+
if (documentReady) {
|
|
1412
|
+
if (isExpectedEventSeen() || readyState === "complete") {
|
|
970
1413
|
return;
|
|
971
1414
|
}
|
|
972
1415
|
}
|
|
@@ -996,7 +1439,7 @@ class CdpPoolBrowserClient {
|
|
|
996
1439
|
...(this.allowedHosts.length > 0 ? { allowedHosts: this.allowedHosts } : {}),
|
|
997
1440
|
...(options?.isolatedContext ? { isolationMode: "browserContext" } : {}),
|
|
998
1441
|
}));
|
|
999
|
-
const pageClient = new
|
|
1442
|
+
const pageClient = new CdpWebSocketClient(acquireResult.wsEndpoint);
|
|
1000
1443
|
const page = new CdpPoolBrowserPage(acquireResult.pageId, acquireResult.browserContextId, pageClient, async (request) => {
|
|
1001
1444
|
await this.poolClient.send("release", request);
|
|
1002
1445
|
});
|