@apifuse/provider-sdk 2.2.0-beta.61 → 2.2.0-beta.63
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 +268 -9
- package/CHANGELOG.md +8 -0
- package/README.md +1 -0
- package/bin/apifuse-check.ts +84 -2
- package/bin/apifuse-dev.ts +11 -31
- package/bin/apifuse-migrate-deployment.ts +200 -0
- package/bin/apifuse-pack-check.ts +11 -4
- package/bin/apifuse-pack-types.ts +191 -4
- package/bin/apifuse-record.ts +44 -32
- package/bin/apifuse-test.ts +3 -1
- package/dist/cli/commands.d.ts +1 -1
- package/dist/cli/commands.js +11 -0
- package/dist/cli/deployment-intent.d.ts +178 -0
- package/dist/cli/deployment-intent.js +277 -0
- package/dist/cli/migrate-deployment-intent.d.ts +132 -0
- package/dist/cli/migrate-deployment-intent.js +1552 -0
- package/dist/cli/migrate-provider-shape.d.ts +5 -1
- package/dist/cli/migrate-provider-shape.js +4 -2
- package/dist/cli/resolver-runtime.d.ts +21 -0
- package/dist/cli/resolver-runtime.js +31 -0
- package/dist/define.d.ts +3 -0
- package/dist/define.js +5 -0
- package/dist/dev.d.ts +3 -1
- package/dist/dev.js +6 -1
- package/dist/engine.d.ts +35 -1
- package/dist/engine.js +21 -2
- package/dist/error-resolution.d.ts +1 -0
- package/dist/error-resolution.js +54 -0
- package/dist/errors.d.ts +39 -1
- package/dist/errors.js +50 -0
- package/dist/handle-meta.d.ts +18 -2
- package/dist/handle-meta.js +30 -2
- package/dist/handle.d.ts +33 -12
- package/dist/handle.js +69 -24
- package/dist/i18n/error-messages.d.ts +86 -0
- package/dist/i18n/error-messages.js +192 -0
- package/dist/i18n/index.d.ts +1 -0
- package/dist/i18n/index.js +1 -0
- package/dist/i18n/keys.d.ts +12 -0
- package/dist/i18n/keys.js +76 -5
- package/dist/index.d.ts +10 -2
- package/dist/index.js +9 -1
- package/dist/lint.d.ts +10 -0
- package/dist/lint.js +440 -57
- package/dist/provider.d.ts +7 -5
- package/dist/provider.js +4 -2
- package/dist/runtime/browser-telemetry-binding.d.ts +5 -0
- package/dist/runtime/browser-telemetry-binding.js +83 -0
- package/dist/runtime/browser-telemetry.d.ts +76 -0
- package/dist/runtime/browser-telemetry.js +165 -0
- package/dist/runtime/browser.d.ts +3 -0
- package/dist/runtime/browser.js +208 -47
- package/dist/runtime/cache-telemetry.d.ts +71 -0
- package/dist/runtime/cache-telemetry.js +177 -0
- package/dist/runtime/cache.js +67 -7
- package/dist/runtime/capability-response.d.ts +3 -0
- package/dist/runtime/capability-response.js +56 -0
- package/dist/runtime/capability-telemetry.d.ts +46 -0
- package/dist/runtime/capability-telemetry.js +68 -0
- package/dist/runtime/diagnostic-env.d.ts +3 -0
- package/dist/runtime/diagnostic-env.js +10 -1
- package/dist/runtime/engine-mode.d.ts +74 -0
- package/dist/runtime/engine-mode.js +152 -0
- package/dist/runtime/handle.js +15 -10
- package/dist/runtime/http-telemetry-binding.d.ts +10 -0
- package/dist/runtime/http-telemetry-binding.js +152 -0
- package/dist/runtime/http-telemetry-guard.d.ts +16 -0
- package/dist/runtime/http-telemetry-guard.js +202 -0
- package/dist/runtime/http-telemetry.d.ts +104 -0
- package/dist/runtime/http-telemetry.js +190 -0
- package/dist/runtime/http.d.ts +31 -0
- package/dist/runtime/http.js +356 -174
- package/dist/runtime/instrumentation.js +92 -13
- package/dist/runtime/ocr-telemetry.d.ts +82 -0
- package/dist/runtime/ocr-telemetry.js +192 -0
- package/dist/runtime/ocr.js +30 -8
- package/dist/runtime/request-telemetry.d.ts +2 -0
- package/dist/runtime/request-telemetry.js +2 -1
- package/dist/runtime/resolver-runtime-options.d.ts +14 -0
- package/dist/runtime/resolver-runtime-options.js +33 -0
- package/dist/runtime/resolver-shared.d.ts +3 -2
- package/dist/runtime/resolver-shared.js +4 -4
- package/dist/runtime/resolver-telemetry.d.ts +1 -0
- package/dist/runtime/resolver-telemetry.js +1 -1
- package/dist/runtime/resolver-vendors/browser.d.ts +2 -0
- package/dist/runtime/resolver-vendors/browser.js +46 -0
- package/dist/runtime/resolver-vendors/hosts.d.ts +21 -0
- package/dist/runtime/resolver-vendors/hosts.js +35 -0
- package/dist/runtime/resolver-vendors/hypersolutions.js +2 -2
- package/dist/runtime/resolver-vendors/types.d.ts +5 -4
- package/dist/runtime/resolver.d.ts +4 -1
- package/dist/runtime/resolver.js +46 -13
- package/dist/runtime/state-telemetry.d.ts +79 -0
- package/dist/runtime/state-telemetry.js +194 -0
- package/dist/runtime/state.d.ts +3 -0
- package/dist/runtime/state.js +26 -7
- package/dist/runtime/stealth-cookies.d.ts +5 -1
- package/dist/runtime/stealth-cookies.js +21 -1
- package/dist/runtime/stealth-owned-headers.d.ts +26 -0
- package/dist/runtime/stealth-owned-headers.js +37 -0
- package/dist/runtime/stealth-telemetry.d.ts +92 -0
- package/dist/runtime/stealth-telemetry.js +179 -0
- package/dist/runtime/stealth.d.ts +4 -1
- package/dist/runtime/stealth.js +228 -85
- package/dist/runtime/stt-telemetry.d.ts +77 -0
- package/dist/runtime/stt-telemetry.js +176 -0
- package/dist/runtime/stt.js +15 -6
- package/dist/server/error-stack-frames.d.ts +35 -0
- package/dist/server/error-stack-frames.js +219 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/index.js +5 -0
- package/dist/server/sdk-version.d.ts +3 -0
- package/dist/server/sdk-version.js +19 -0
- package/dist/server/self-test.js +1 -18
- package/dist/server/sensitive-values.d.ts +1 -1
- package/dist/server/sensitive-values.js +9 -0
- package/dist/server/serve-implementation.d.ts +53 -2
- package/dist/server/serve-implementation.js +369 -123
- package/dist/server/types.d.ts +1 -0
- package/dist/server/types.js +7 -0
- package/dist/stateful/stateful-provider-owner-forwarder.js +22 -0
- package/dist/stateful/stateful-provider-runtime-context.d.ts +3 -0
- package/dist/stateful/stateful-provider-runtime-context.js +1 -0
- package/dist/types.d.ts +94 -1
- package/package.json +6 -1
- package/src/cli/commands.ts +14 -0
- package/src/cli/deployment-intent.ts +364 -0
- package/src/cli/migrate-deployment-intent.ts +1908 -0
- package/src/cli/migrate-provider-shape.ts +10 -4
- package/src/cli/resolver-runtime.ts +72 -0
- package/src/define.ts +13 -0
- package/src/dev.ts +9 -2
- package/src/engine.ts +38 -2
- package/src/error-resolution.ts +58 -0
- package/src/errors.ts +77 -1
- package/src/handle-meta.ts +39 -4
- package/src/handle.ts +121 -38
- package/src/i18n/error-messages.ts +255 -0
- package/src/i18n/index.ts +1 -0
- package/src/i18n/keys.ts +80 -6
- package/src/index.ts +91 -0
- package/src/lint.ts +552 -57
- package/src/provider.ts +20 -0
- package/src/runtime/browser-telemetry-binding.ts +92 -0
- package/src/runtime/browser-telemetry.ts +289 -0
- package/src/runtime/browser.ts +249 -65
- package/src/runtime/cache-telemetry.ts +247 -0
- package/src/runtime/cache.ts +68 -17
- package/src/runtime/capability-response.ts +53 -0
- package/src/runtime/capability-telemetry.ts +101 -0
- package/src/runtime/diagnostic-env.ts +11 -1
- package/src/runtime/engine-mode.ts +233 -0
- package/src/runtime/handle.ts +20 -10
- package/src/runtime/http-telemetry-binding.ts +162 -0
- package/src/runtime/http-telemetry-guard.ts +208 -0
- package/src/runtime/http-telemetry.ts +292 -0
- package/src/runtime/http.ts +457 -204
- package/src/runtime/instrumentation.ts +105 -16
- package/src/runtime/ocr-telemetry.ts +308 -0
- package/src/runtime/ocr.ts +109 -74
- package/src/runtime/request-telemetry.ts +2 -1
- package/src/runtime/resolver-runtime-options.ts +50 -0
- package/src/runtime/resolver-shared.ts +9 -4
- package/src/runtime/resolver-telemetry.ts +1 -1
- package/src/runtime/resolver-vendors/browser.ts +65 -7
- package/src/runtime/resolver-vendors/hosts.ts +43 -0
- package/src/runtime/resolver-vendors/hypersolutions.ts +2 -2
- package/src/runtime/resolver-vendors/types.ts +5 -4
- package/src/runtime/resolver.ts +64 -21
- package/src/runtime/state-telemetry.ts +298 -0
- package/src/runtime/state.ts +46 -45
- package/src/runtime/stealth-cookies.ts +24 -1
- package/src/runtime/stealth-owned-headers.ts +41 -0
- package/src/runtime/stealth-telemetry.ts +304 -0
- package/src/runtime/stealth.ts +355 -150
- package/src/runtime/stt-telemetry.ts +282 -0
- package/src/runtime/stt.ts +93 -71
- package/src/server/error-stack-frames.ts +214 -0
- package/src/server/index.ts +66 -0
- package/src/server/sdk-version.ts +22 -0
- package/src/server/self-test.ts +1 -21
- package/src/server/sensitive-values.ts +14 -0
- package/src/server/serve-implementation.ts +601 -157
- package/src/server/types.ts +7 -0
- package/src/stateful/stateful-provider-owner-forwarder.ts +23 -0
- package/src/stateful/stateful-provider-runtime-context.ts +4 -0
- package/src/types.ts +99 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/approval-override.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/codemod-syntax.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/connection-precedence.ts.txt +0 -10
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-a.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-b.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-barrel.ts.txt +0 -2
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-computed.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-a.ts.txt +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-b.ts.txt +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-barrel.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-catalog.ts.txt +0 -19
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-docs.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-stores.ts.txt +0 -8
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-indirect-config.ts.txt +0 -5
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-nonstatic.ts.txt +0 -4
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-package-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-leaf.ts.txt +0 -10
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-one.ts.txt +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-two.ts.txt +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-one.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-two.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-mixed-index.ts.txt +0 -14
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-registry.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-barrel.ts.txt +0 -2
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-index.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-leaf.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/docs-conflict.ts.txt +0 -8
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-hoisted-array-spread.ts.txt +0 -15
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-imported-array-spread.ts.txt +0 -9
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-kebab-operation-id.ts.txt +0 -17
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-map.ts.txt +0 -5
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-mixed-array-spread.ts.txt +0 -24
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-operation.ts.txt +0 -16
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/examples-snake-operation-id.ts.txt +0 -21
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-duplicate-provider.ts.txt +0 -11
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-id-unresolved.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-map.ts.txt +0 -3
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-many.ts.txt +0 -18
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-one.ts.txt +0 -13
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoist-all.ts.txt +0 -31
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/hoisted-const.ts.txt +0 -11
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/imported-spread.ts.txt +0 -11
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-map.ts.txt +0 -11
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-cast-tail.ts.txt +0 -21
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-ekitan.ts.txt +0 -11
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/inline-spread-override.ts.txt +0 -14
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-en.json +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ja.json +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-canonical-ko.json +0 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/locale-existing-operation-namespace-en.json +0 -8
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/missing-english-locale.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/no-safety.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/non-literal.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/redundant-approval.ts.txt +0 -6
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-binding-ambiguous.ts.txt +0 -12
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported-other.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-imported.ts.txt +0 -5
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-key-ambiguous.ts.txt +0 -19
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-shorthand.ts.txt +0 -9
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-typed.ts.txt +0 -11
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/registry-unrelated.ts.txt +0 -13
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/safety-conflict.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/stream.ts.txt +0 -7
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/tool-router-spread.ts.txt +0 -15
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/unparseable.ts.txt +0 -4
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/verbatim-template.ts.txt +0 -12
package/src/runtime/browser.ts
CHANGED
|
@@ -1,4 +1,33 @@
|
|
|
1
|
-
import { readDiagnosticEnv } from "./diagnostic-env.js";
|
|
1
|
+
import { readDiagnosticEnv, registerDiagnosticValue } from "./diagnostic-env.js";
|
|
2
|
+
import { AsyncResource } from "node:async_hooks";
|
|
3
|
+
import type {
|
|
4
|
+
BrowserTelemetryPoolAcquireOutcome,
|
|
5
|
+
BrowserTelemetrySink,
|
|
6
|
+
} from "./browser-telemetry.js";
|
|
7
|
+
import { observeTelemetryCallback as observeBrowserTelemetry } from "./http-telemetry-guard.js";
|
|
8
|
+
import { cookieValuesFromHeaders } from "./stealth-cookies.js";
|
|
9
|
+
|
|
10
|
+
function browserTelemetryErrorCode(
|
|
11
|
+
error: unknown,
|
|
12
|
+
):
|
|
13
|
+
| "BROWSER_CDP_POOL_REQUIRED"
|
|
14
|
+
| "BROWSER_PROXY_INVALID"
|
|
15
|
+
| "BROWSER_RUNTIME_UNSUPPORTED"
|
|
16
|
+
| "BROWSER_CDP_POOL_ERROR"
|
|
17
|
+
| "other" {
|
|
18
|
+
const code =
|
|
19
|
+
error && typeof error === "object" && "code" in error
|
|
20
|
+
? (error as { code?: unknown }).code
|
|
21
|
+
: undefined;
|
|
22
|
+
if (
|
|
23
|
+
code === "BROWSER_CDP_POOL_REQUIRED" ||
|
|
24
|
+
code === "BROWSER_PROXY_INVALID" ||
|
|
25
|
+
code === "BROWSER_RUNTIME_UNSUPPORTED"
|
|
26
|
+
)
|
|
27
|
+
return code;
|
|
28
|
+
if (typeof code === "number") return "BROWSER_CDP_POOL_ERROR";
|
|
29
|
+
return "other";
|
|
30
|
+
}
|
|
2
31
|
import { createRequire } from "node:module";
|
|
3
32
|
import type { CDPSession, Frame, LaunchOptions, Locator, Page, Request, Route } from "playwright";
|
|
4
33
|
|
|
@@ -416,9 +445,7 @@ function getCdpAuthRequiredRequestId(params: unknown): string | null {
|
|
|
416
445
|
|
|
417
446
|
function isCdpProxyAuthChallenge(params: unknown): boolean {
|
|
418
447
|
return (
|
|
419
|
-
isRecord(params) &&
|
|
420
|
-
isRecord(params.authChallenge) &&
|
|
421
|
-
params.authChallenge.source === "Proxy"
|
|
448
|
+
isRecord(params) && isRecord(params.authChallenge) && params.authChallenge.source === "Proxy"
|
|
422
449
|
);
|
|
423
450
|
}
|
|
424
451
|
|
|
@@ -427,6 +454,7 @@ async function handleCdpAuthRequired(
|
|
|
427
454
|
proxy: PlaywrightProxy & { username: string; password: string },
|
|
428
455
|
authAttempts: Set<string>,
|
|
429
456
|
params: unknown,
|
|
457
|
+
telemetry?: BrowserTelemetrySink,
|
|
430
458
|
): Promise<void> {
|
|
431
459
|
const requestId = getCdpAuthRequiredRequestId(params);
|
|
432
460
|
if (!requestId) return;
|
|
@@ -436,20 +464,21 @@ async function handleCdpAuthRequired(
|
|
|
436
464
|
const response =
|
|
437
465
|
isCdpProxyAuthChallenge(params) && !authAttempts.has(requestId)
|
|
438
466
|
? (() => {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
467
|
+
authAttempts.add(requestId);
|
|
468
|
+
observeBrowserTelemetry(telemetry, () => telemetry?.recordProxyAuthChallenge());
|
|
469
|
+
return {
|
|
470
|
+
authChallengeResponse: {
|
|
471
|
+
password: proxy.password,
|
|
472
|
+
response: "ProvideCredentials" as const,
|
|
473
|
+
username: proxy.username,
|
|
474
|
+
},
|
|
475
|
+
requestId,
|
|
476
|
+
};
|
|
477
|
+
})()
|
|
478
|
+
: {
|
|
479
|
+
authChallengeResponse: { response: "CancelAuth" as const },
|
|
446
480
|
requestId,
|
|
447
481
|
};
|
|
448
|
-
})()
|
|
449
|
-
: {
|
|
450
|
-
authChallengeResponse: { response: "CancelAuth" as const },
|
|
451
|
-
requestId,
|
|
452
|
-
};
|
|
453
482
|
|
|
454
483
|
await session.send("Fetch.continueWithAuth", response).catch(() => undefined);
|
|
455
484
|
}
|
|
@@ -485,6 +514,7 @@ export type BrowserClientOptions = BrowserOptions & {
|
|
|
485
514
|
executablePath?: string;
|
|
486
515
|
extraArgs?: string[];
|
|
487
516
|
serviceWorkers?: "allow" | "block";
|
|
517
|
+
telemetry?: BrowserTelemetrySink;
|
|
488
518
|
};
|
|
489
519
|
|
|
490
520
|
type SupportedBrowserClient = {
|
|
@@ -602,6 +632,8 @@ function toPlaywrightProxy(proxy: string | undefined): LaunchOptions["proxy"] {
|
|
|
602
632
|
fix: "Percent-encode the proxy username and password as URL userinfo.",
|
|
603
633
|
});
|
|
604
634
|
}
|
|
635
|
+
registerDiagnosticValue(username);
|
|
636
|
+
registerDiagnosticValue(password);
|
|
605
637
|
|
|
606
638
|
return {
|
|
607
639
|
server: `${parsed.protocol}//${parsed.host}`,
|
|
@@ -803,13 +835,21 @@ class PlaywrightBrowserPage implements BrowserPageContract {
|
|
|
803
835
|
constructor(
|
|
804
836
|
private readonly page: Page,
|
|
805
837
|
private readonly proxy: LaunchOptions["proxy"] = undefined,
|
|
838
|
+
private readonly telemetry?: BrowserTelemetrySink,
|
|
806
839
|
) {}
|
|
807
840
|
|
|
808
841
|
async goto(
|
|
809
842
|
url: string,
|
|
810
843
|
options?: { readonly timeout?: number; readonly waitUntil?: "load" | "domcontentloaded" },
|
|
811
844
|
): Promise<void> {
|
|
812
|
-
await this.page.goto(url, options);
|
|
845
|
+
const response = await this.page.goto(url, options);
|
|
846
|
+
const headers = await response?.headersArray().catch(() => []);
|
|
847
|
+
for (const header of headers ?? []) {
|
|
848
|
+
if (header.name.toLowerCase() === "set-cookie") {
|
|
849
|
+
registerDiagnosticValue(header.value);
|
|
850
|
+
for (const value of cookieValuesFromHeaders(header.value)) registerDiagnosticValue(value);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
813
853
|
}
|
|
814
854
|
|
|
815
855
|
async evaluate<T>(fn: string | (() => T)): Promise<T> {
|
|
@@ -870,7 +910,9 @@ class PlaywrightBrowserPage implements BrowserPageContract {
|
|
|
870
910
|
}
|
|
871
911
|
|
|
872
912
|
async cookies(): Promise<readonly BrowserCookie[]> {
|
|
873
|
-
|
|
913
|
+
const cookies = (await this.page.context().cookies()).map(toBrowserCookie);
|
|
914
|
+
for (const cookie of cookies) registerDiagnosticValue(cookie.value);
|
|
915
|
+
return cookies;
|
|
874
916
|
}
|
|
875
917
|
|
|
876
918
|
async withResourcePolicy<T>(policy: BrowserResourcePolicy, run: () => Promise<T>): Promise<T> {
|
|
@@ -914,7 +956,7 @@ class PlaywrightBrowserPage implements BrowserPageContract {
|
|
|
914
956
|
const authAttempts = new Set<string>();
|
|
915
957
|
const onAuthRequired = (params: unknown) => {
|
|
916
958
|
if (!proxy) return;
|
|
917
|
-
void handleCdpAuthRequired(session, proxy, authAttempts, params);
|
|
959
|
+
void handleCdpAuthRequired(session, proxy, authAttempts, params, this.telemetry);
|
|
918
960
|
};
|
|
919
961
|
session.on("Fetch.requestPaused", onRequestPaused);
|
|
920
962
|
if (proxy) {
|
|
@@ -990,10 +1032,14 @@ class PlaywrightBrowserClient implements SupportedBrowserClient {
|
|
|
990
1032
|
}
|
|
991
1033
|
|
|
992
1034
|
async newPage(): Promise<BrowserPageContract> {
|
|
1035
|
+
if (!this.options.cdpUrl)
|
|
1036
|
+
observeBrowserTelemetry(this.options.telemetry, () =>
|
|
1037
|
+
this.options.telemetry?.recordPoolAcquire("not_configured"),
|
|
1038
|
+
);
|
|
993
1039
|
const browser = await this.ensureBrowser();
|
|
994
1040
|
const page = await browser.newPage();
|
|
995
1041
|
|
|
996
|
-
return new PlaywrightBrowserPage(page, this.parsedProxy);
|
|
1042
|
+
return new PlaywrightBrowserPage(page, this.parsedProxy, this.options.telemetry);
|
|
997
1043
|
}
|
|
998
1044
|
|
|
999
1045
|
async rawPage(): Promise<BrowserPageContract> {
|
|
@@ -1004,10 +1050,14 @@ class PlaywrightBrowserClient implements SupportedBrowserClient {
|
|
|
1004
1050
|
}
|
|
1005
1051
|
|
|
1006
1052
|
async withIsolatedContext<T>(handler: (page: BrowserPageContract) => Promise<T>): Promise<T> {
|
|
1053
|
+
if (!this.options.cdpUrl)
|
|
1054
|
+
observeBrowserTelemetry(this.options.telemetry, () =>
|
|
1055
|
+
this.options.telemetry?.recordPoolAcquire("not_configured"),
|
|
1056
|
+
);
|
|
1007
1057
|
const browser = await this.ensureBrowser();
|
|
1008
1058
|
const context = await browser.newContext({ serviceWorkers: this.options.serviceWorkers });
|
|
1009
1059
|
const page = await context.newPage();
|
|
1010
|
-
const browserPage = new PlaywrightBrowserPage(page, this.parsedProxy);
|
|
1060
|
+
const browserPage = new PlaywrightBrowserPage(page, this.parsedProxy, this.options.telemetry);
|
|
1011
1061
|
|
|
1012
1062
|
try {
|
|
1013
1063
|
return await handler(browserPage);
|
|
@@ -1061,6 +1111,7 @@ abstract class WebSocketCommandClient {
|
|
|
1061
1111
|
>();
|
|
1062
1112
|
private socket?: WebSocket;
|
|
1063
1113
|
private socketPromise?: Promise<WebSocket>;
|
|
1114
|
+
private closed = false;
|
|
1064
1115
|
|
|
1065
1116
|
constructor(endpoint: string) {
|
|
1066
1117
|
this.endpoint = normalizeWebSocketEndpoint(endpoint);
|
|
@@ -1084,6 +1135,7 @@ abstract class WebSocketCommandClient {
|
|
|
1084
1135
|
params: Record<string, unknown> = {},
|
|
1085
1136
|
): Promise<Record<string, unknown>> {
|
|
1086
1137
|
const socket = await this.getSocket();
|
|
1138
|
+
if (this.closed) throw new Error(`WebSocket closed: ${this.endpoint}`);
|
|
1087
1139
|
const id = this.nextId++;
|
|
1088
1140
|
|
|
1089
1141
|
return await new Promise<Record<string, unknown>>((resolve, reject) => {
|
|
@@ -1094,6 +1146,7 @@ abstract class WebSocketCommandClient {
|
|
|
1094
1146
|
}
|
|
1095
1147
|
|
|
1096
1148
|
async close(): Promise<void> {
|
|
1149
|
+
this.closed = true;
|
|
1097
1150
|
for (const pending of this.pending.values()) {
|
|
1098
1151
|
pending.reject(new Error(`WebSocket closed: ${this.endpoint}`));
|
|
1099
1152
|
}
|
|
@@ -1112,6 +1165,7 @@ abstract class WebSocketCommandClient {
|
|
|
1112
1165
|
): CdpCommandFrame | JsonRpcCommandFrame;
|
|
1113
1166
|
|
|
1114
1167
|
private async getSocket(): Promise<WebSocket> {
|
|
1168
|
+
if (this.closed) throw new Error(`WebSocket closed: ${this.endpoint}`);
|
|
1115
1169
|
if (this.socket?.readyState === WebSocket.OPEN) {
|
|
1116
1170
|
return this.socket;
|
|
1117
1171
|
}
|
|
@@ -1124,6 +1178,11 @@ abstract class WebSocketCommandClient {
|
|
|
1124
1178
|
const socket = new WebSocket(this.endpoint);
|
|
1125
1179
|
|
|
1126
1180
|
socket.addEventListener("open", () => {
|
|
1181
|
+
if (this.closed) {
|
|
1182
|
+
socket.close();
|
|
1183
|
+
reject(new Error(`WebSocket closed: ${this.endpoint}`));
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1127
1186
|
this.socket = socket;
|
|
1128
1187
|
resolve(socket);
|
|
1129
1188
|
});
|
|
@@ -1318,6 +1377,36 @@ function parsePoolAcquireResponse(value: unknown): PoolAcquireResponse {
|
|
|
1318
1377
|
};
|
|
1319
1378
|
}
|
|
1320
1379
|
|
|
1380
|
+
function poolAcquireOutcome(error: unknown): BrowserTelemetryPoolAcquireOutcome {
|
|
1381
|
+
const code =
|
|
1382
|
+
error instanceof Error && typeof (error as Error & { code?: unknown }).code === "number"
|
|
1383
|
+
? (error as Error & { code: number }).code
|
|
1384
|
+
: undefined;
|
|
1385
|
+
switch (code) {
|
|
1386
|
+
case -32001:
|
|
1387
|
+
return "queue_full";
|
|
1388
|
+
case -32002:
|
|
1389
|
+
return "timed_out";
|
|
1390
|
+
case -32003:
|
|
1391
|
+
return "shutting_down";
|
|
1392
|
+
case -32004:
|
|
1393
|
+
return "unknown_lease";
|
|
1394
|
+
case -32005:
|
|
1395
|
+
return "unknown_method";
|
|
1396
|
+
case -32006:
|
|
1397
|
+
return "missing_allowed_hosts";
|
|
1398
|
+
}
|
|
1399
|
+
if (error instanceof Error && /Unable to connect|WebSocket closed/.test(error.message))
|
|
1400
|
+
return "transport_failure";
|
|
1401
|
+
return "other";
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
function poolAcquireCode(error: unknown): number | undefined {
|
|
1405
|
+
return error instanceof Error && typeof (error as Error & { code?: unknown }).code === "number"
|
|
1406
|
+
? (error as Error & { code: number }).code
|
|
1407
|
+
: undefined;
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1321
1410
|
function parseCdpFrameTreeNode(value: unknown): CdpFrameTreeNode | undefined {
|
|
1322
1411
|
if (!isRecord(value) || !isRecord(value.frame)) {
|
|
1323
1412
|
return undefined;
|
|
@@ -1366,9 +1455,7 @@ function getCdpDestroyedExecutionContextId(params: unknown): number | undefined
|
|
|
1366
1455
|
return undefined;
|
|
1367
1456
|
}
|
|
1368
1457
|
|
|
1369
|
-
return typeof params.executionContextId === "number"
|
|
1370
|
-
? params.executionContextId
|
|
1371
|
-
: undefined;
|
|
1458
|
+
return typeof params.executionContextId === "number" ? params.executionContextId : undefined;
|
|
1372
1459
|
}
|
|
1373
1460
|
|
|
1374
1461
|
function isMissingExecutionContextError(error: unknown): boolean {
|
|
@@ -1481,7 +1568,7 @@ class CdpBrowserFrame implements BrowserFrame {
|
|
|
1481
1568
|
}
|
|
1482
1569
|
|
|
1483
1570
|
class CdpPoolBrowserPage implements BrowserPageContract {
|
|
1484
|
-
private
|
|
1571
|
+
private closePromise?: Promise<void>;
|
|
1485
1572
|
private initialized = false;
|
|
1486
1573
|
private readonly frameExecutionContexts = new Map<string, number>();
|
|
1487
1574
|
|
|
@@ -1518,11 +1605,7 @@ class CdpPoolBrowserPage implements BrowserPageContract {
|
|
|
1518
1605
|
if (typeof navigation.errorText === "string" && navigation.errorText.length > 0) {
|
|
1519
1606
|
throw new Error(`Page.navigate failed: ${navigation.errorText} at ${url}`);
|
|
1520
1607
|
}
|
|
1521
|
-
await this.waitForDocumentReady(
|
|
1522
|
-
startedAt + timeout,
|
|
1523
|
-
() => expectedEventSeen,
|
|
1524
|
-
waitUntil,
|
|
1525
|
-
);
|
|
1608
|
+
await this.waitForDocumentReady(startedAt + timeout, () => expectedEventSeen, waitUntil);
|
|
1526
1609
|
} finally {
|
|
1527
1610
|
unsubscribe();
|
|
1528
1611
|
}
|
|
@@ -1699,24 +1782,31 @@ class CdpPoolBrowserPage implements BrowserPageContract {
|
|
|
1699
1782
|
async cookies(): Promise<readonly BrowserCookie[]> {
|
|
1700
1783
|
await this.initialize();
|
|
1701
1784
|
const result = await this.pageClient.send("Network.getCookies");
|
|
1702
|
-
|
|
1785
|
+
const cookies = parseCdpCookies(result.cookies);
|
|
1786
|
+
for (const cookie of cookies) registerDiagnosticValue(cookie.value);
|
|
1787
|
+
return cookies;
|
|
1703
1788
|
}
|
|
1704
1789
|
|
|
1705
1790
|
async close(): Promise<void> {
|
|
1706
|
-
|
|
1791
|
+
// Only the first caller observes the release outcome; repeat calls stay
|
|
1792
|
+
// idempotent (as they were before the promise was memoized) instead of
|
|
1793
|
+
// replaying a cached rejection, and never re-issue a release against a
|
|
1794
|
+
// target the pool has already disposed.
|
|
1795
|
+
if (this.closePromise) {
|
|
1796
|
+
await this.closePromise.catch(() => undefined);
|
|
1707
1797
|
return;
|
|
1708
1798
|
}
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1799
|
+
this.closePromise = (async () => {
|
|
1800
|
+
try {
|
|
1801
|
+
await this.release({
|
|
1802
|
+
...(this.browserContextId ? { browserContextId: this.browserContextId } : {}),
|
|
1803
|
+
pageId: this.pageId,
|
|
1804
|
+
});
|
|
1805
|
+
} finally {
|
|
1806
|
+
await this.pageClient.close();
|
|
1807
|
+
}
|
|
1808
|
+
})();
|
|
1809
|
+
await this.closePromise;
|
|
1720
1810
|
}
|
|
1721
1811
|
|
|
1722
1812
|
async withResourcePolicy<T>(policy: BrowserResourcePolicy, run: () => Promise<T>): Promise<T> {
|
|
@@ -1746,8 +1836,19 @@ class CdpPoolBrowserPage implements BrowserPageContract {
|
|
|
1746
1836
|
try {
|
|
1747
1837
|
return await run();
|
|
1748
1838
|
} finally {
|
|
1749
|
-
|
|
1750
|
-
|
|
1839
|
+
try {
|
|
1840
|
+
if (this.closePromise) {
|
|
1841
|
+
// Keep interception active until release settles. The release failure
|
|
1842
|
+
// stays observable through close() itself; swallowing it here keeps
|
|
1843
|
+
// this finally from replacing run()'s own outcome, and still avoids a
|
|
1844
|
+
// reconnect or a disable against a disposed target.
|
|
1845
|
+
await this.closePromise.catch(() => undefined);
|
|
1846
|
+
} else {
|
|
1847
|
+
await this.pageClient.send("Fetch.disable");
|
|
1848
|
+
}
|
|
1849
|
+
} finally {
|
|
1850
|
+
unsubscribe();
|
|
1851
|
+
}
|
|
1751
1852
|
}
|
|
1752
1853
|
}
|
|
1753
1854
|
|
|
@@ -1859,8 +1960,31 @@ class CdpPoolBrowserPage implements BrowserPageContract {
|
|
|
1859
1960
|
this.pageClient.on("Runtime.executionContextsCleared", () => {
|
|
1860
1961
|
this.frameExecutionContexts.clear();
|
|
1861
1962
|
});
|
|
1963
|
+
const registerResponseCookies = (params: unknown) => {
|
|
1964
|
+
if (!isRecord(params)) return;
|
|
1965
|
+
const headers = Array.isArray(params.responseHeaders)
|
|
1966
|
+
? params.responseHeaders
|
|
1967
|
+
: isRecord(params.headers)
|
|
1968
|
+
? Object.entries(params.headers).map(([name, value]) => ({ name, value }))
|
|
1969
|
+
: [];
|
|
1970
|
+
for (const header of headers) {
|
|
1971
|
+
if (
|
|
1972
|
+
isRecord(header) &&
|
|
1973
|
+
typeof header.name === "string" &&
|
|
1974
|
+
header.name.toLowerCase() === "set-cookie" &&
|
|
1975
|
+
typeof header.value === "string"
|
|
1976
|
+
) {
|
|
1977
|
+
registerDiagnosticValue(header.value);
|
|
1978
|
+
for (const value of cookieValuesFromHeaders(header.value)) registerDiagnosticValue(value);
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
};
|
|
1982
|
+
const scopedCookieRegistration = AsyncResource.bind(registerResponseCookies);
|
|
1983
|
+
this.pageClient.on("Network.responseReceived", scopedCookieRegistration);
|
|
1984
|
+
this.pageClient.on("Network.responseReceivedExtraInfo", scopedCookieRegistration);
|
|
1862
1985
|
await this.pageClient.send("Page.enable");
|
|
1863
1986
|
await this.pageClient.send("Runtime.enable");
|
|
1987
|
+
await this.pageClient.send("Network.enable").catch(() => undefined);
|
|
1864
1988
|
this.initialized = true;
|
|
1865
1989
|
}
|
|
1866
1990
|
|
|
@@ -1910,6 +2034,7 @@ class CdpPoolBrowserPage implements BrowserPageContract {
|
|
|
1910
2034
|
class CdpPoolBrowserClient implements SupportedBrowserClient {
|
|
1911
2035
|
private readonly allowedHosts: string[];
|
|
1912
2036
|
private readonly poolClient: JsonRpcWebSocketClient;
|
|
2037
|
+
private readonly telemetry?: BrowserTelemetrySink;
|
|
1913
2038
|
readonly engine = "playwright-stealth" satisfies BrowserEngine;
|
|
1914
2039
|
|
|
1915
2040
|
constructor(options: BrowserClientOptions) {
|
|
@@ -1920,6 +2045,7 @@ class CdpPoolBrowserClient implements SupportedBrowserClient {
|
|
|
1920
2045
|
}
|
|
1921
2046
|
|
|
1922
2047
|
this.allowedHosts = [...new Set(options.allowedHosts ?? [])];
|
|
2048
|
+
this.telemetry = options.telemetry;
|
|
1923
2049
|
this.poolClient = new JsonRpcWebSocketClient(options.cdpUrl);
|
|
1924
2050
|
}
|
|
1925
2051
|
|
|
@@ -1928,12 +2054,29 @@ class CdpPoolBrowserClient implements SupportedBrowserClient {
|
|
|
1928
2054
|
}
|
|
1929
2055
|
|
|
1930
2056
|
private async acquirePage(options?: { isolatedContext?: boolean }): Promise<BrowserPageContract> {
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
2057
|
+
let acquireResult: PoolAcquireResponse;
|
|
2058
|
+
try {
|
|
2059
|
+
acquireResult = parsePoolAcquireResponse(
|
|
2060
|
+
await this.poolClient.send("acquire", {
|
|
2061
|
+
...(this.allowedHosts.length > 0 ? { allowedHosts: this.allowedHosts } : {}),
|
|
2062
|
+
...(options?.isolatedContext ? { isolationMode: "browserContext" } : {}),
|
|
2063
|
+
}),
|
|
2064
|
+
);
|
|
2065
|
+
observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordPoolAcquire("ok"));
|
|
2066
|
+
} catch (error) {
|
|
2067
|
+
observeBrowserTelemetry(this.telemetry, () =>
|
|
2068
|
+
this.telemetry?.recordPoolAcquire(poolAcquireOutcome(error)),
|
|
2069
|
+
);
|
|
2070
|
+
observeBrowserTelemetry(this.telemetry, () =>
|
|
2071
|
+
this.telemetry?.recordError("BROWSER_CDP_POOL_ERROR"),
|
|
2072
|
+
);
|
|
2073
|
+
const code = poolAcquireCode(error);
|
|
2074
|
+
if (code !== undefined && ![-32001, -32002, -32003, -32004, -32005, -32006].includes(code))
|
|
2075
|
+
observeBrowserTelemetry(this.telemetry, () =>
|
|
2076
|
+
this.telemetry?.recordPoolAcquireUnknownCode?.(code),
|
|
2077
|
+
);
|
|
2078
|
+
throw error;
|
|
2079
|
+
}
|
|
1937
2080
|
const pageClient = new CdpWebSocketClient(acquireResult.wsEndpoint);
|
|
1938
2081
|
const page = new CdpPoolBrowserPage(
|
|
1939
2082
|
acquireResult.pageId,
|
|
@@ -2025,6 +2168,7 @@ export class BrowserClient implements BrowserClientContract {
|
|
|
2025
2168
|
private activePage?: BrowserPageContract;
|
|
2026
2169
|
private readonly activePages = new Set<BrowserPageContract>();
|
|
2027
2170
|
private readonly _engine: BrowserEngine;
|
|
2171
|
+
private readonly telemetry?: BrowserTelemetrySink;
|
|
2028
2172
|
|
|
2029
2173
|
constructor(options: BrowserClientOptions = {}) {
|
|
2030
2174
|
const resolvedOptions = {
|
|
@@ -2033,6 +2177,7 @@ export class BrowserClient implements BrowserClientContract {
|
|
|
2033
2177
|
};
|
|
2034
2178
|
const engine = resolvedOptions.engine ?? "playwright-stealth";
|
|
2035
2179
|
this._engine = engine;
|
|
2180
|
+
this.telemetry = resolvedOptions.telemetry;
|
|
2036
2181
|
this.cdpUrl = resolvedOptions.cdpUrl;
|
|
2037
2182
|
|
|
2038
2183
|
if (resolvedOptions.requireCdpPool && !resolvedOptions.cdpUrl) {
|
|
@@ -2061,23 +2206,46 @@ export class BrowserClient implements BrowserClientContract {
|
|
|
2061
2206
|
}
|
|
2062
2207
|
|
|
2063
2208
|
async newPage(): Promise<BrowserPageContract> {
|
|
2064
|
-
|
|
2065
|
-
|
|
2209
|
+
observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordEngine(this._engine));
|
|
2210
|
+
try {
|
|
2211
|
+
const page = await this.client.newPage();
|
|
2212
|
+
return this.activatePage(page);
|
|
2213
|
+
} catch (error) {
|
|
2214
|
+
observeBrowserTelemetry(this.telemetry, () =>
|
|
2215
|
+
this.telemetry?.recordError(browserTelemetryErrorCode(error)),
|
|
2216
|
+
);
|
|
2217
|
+
throw error;
|
|
2218
|
+
}
|
|
2066
2219
|
}
|
|
2067
2220
|
|
|
2068
2221
|
async rawPage(): Promise<BrowserPageContract> {
|
|
2222
|
+
observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordEngine(this._engine));
|
|
2069
2223
|
if (!this.cdpUrl) {
|
|
2224
|
+
observeBrowserTelemetry(this.telemetry, () =>
|
|
2225
|
+
this.telemetry?.recordPoolAcquire("not_configured"),
|
|
2226
|
+
);
|
|
2227
|
+
observeBrowserTelemetry(this.telemetry, () =>
|
|
2228
|
+
this.telemetry?.recordError("BROWSER_RUNTIME_UNSUPPORTED"),
|
|
2229
|
+
);
|
|
2070
2230
|
throw new ProviderError("ctx.browser.rawPage() requires a CDP pool", {
|
|
2071
2231
|
code: "BROWSER_RUNTIME_UNSUPPORTED",
|
|
2072
2232
|
fix: "Set APIFUSE__CDP_POOL__URL. The SDK escape hatch is CDP pool-backed only and never launches local Chromium.",
|
|
2073
2233
|
});
|
|
2074
2234
|
}
|
|
2075
2235
|
|
|
2076
|
-
|
|
2077
|
-
|
|
2236
|
+
try {
|
|
2237
|
+
const page = await this.client.rawPage();
|
|
2238
|
+
return this.activatePage(page);
|
|
2239
|
+
} catch (error) {
|
|
2240
|
+
observeBrowserTelemetry(this.telemetry, () =>
|
|
2241
|
+
this.telemetry?.recordError(browserTelemetryErrorCode(error)),
|
|
2242
|
+
);
|
|
2243
|
+
throw error;
|
|
2244
|
+
}
|
|
2078
2245
|
}
|
|
2079
2246
|
|
|
2080
2247
|
async withIsolatedContext<T>(handler: (page: BrowserPageContract) => Promise<T>): Promise<T> {
|
|
2248
|
+
observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordEngine(this._engine));
|
|
2081
2249
|
const previousActivePage = this.activePage;
|
|
2082
2250
|
let trackedPage: BrowserPageContract | undefined;
|
|
2083
2251
|
|
|
@@ -2095,22 +2263,34 @@ export class BrowserClient implements BrowserClientContract {
|
|
|
2095
2263
|
}
|
|
2096
2264
|
|
|
2097
2265
|
private activatePage(page: BrowserPageContract): BrowserPageContract {
|
|
2098
|
-
let
|
|
2266
|
+
let closePromise: Promise<void> | undefined;
|
|
2099
2267
|
const originalClose = page.close.bind(page);
|
|
2100
2268
|
const trackedPage = new Proxy(page, {
|
|
2101
2269
|
get: (target, property, receiver) => {
|
|
2102
2270
|
if (property === "close") {
|
|
2103
2271
|
return async () => {
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
this.activePage = undefined;
|
|
2112
|
-
}
|
|
2272
|
+
// Repeat calls resolve on the first outcome instead of replaying a
|
|
2273
|
+
// cached rejection, so a failed release cannot make close() — or
|
|
2274
|
+
// BrowserClient.close(), which closes every tracked page — throw
|
|
2275
|
+
// forever.
|
|
2276
|
+
if (closePromise) {
|
|
2277
|
+
await closePromise.catch(() => undefined);
|
|
2278
|
+
return;
|
|
2113
2279
|
}
|
|
2280
|
+
closePromise = (async () => {
|
|
2281
|
+
// Bookkeeping runs even when the release fails: the page is gone
|
|
2282
|
+
// either way, so leaving it in activePages would leak it and let
|
|
2283
|
+
// solveChallenge reuse a disposed target.
|
|
2284
|
+
try {
|
|
2285
|
+
await originalClose();
|
|
2286
|
+
} finally {
|
|
2287
|
+
this.activePages.delete(trackedPage);
|
|
2288
|
+
if (this.activePage === trackedPage) {
|
|
2289
|
+
this.activePage = undefined;
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
})();
|
|
2293
|
+
await closePromise;
|
|
2114
2294
|
};
|
|
2115
2295
|
}
|
|
2116
2296
|
const value = Reflect.get(target, property, receiver);
|
|
@@ -2123,7 +2303,11 @@ export class BrowserClient implements BrowserClientContract {
|
|
|
2123
2303
|
}
|
|
2124
2304
|
|
|
2125
2305
|
async solveChallenge(request: BrowserChallengeRequest): Promise<BrowserChallengeResult> {
|
|
2306
|
+
observeBrowserTelemetry(this.telemetry, () => this.telemetry?.recordEngine(this._engine));
|
|
2126
2307
|
if (request.type !== "recaptcha") {
|
|
2308
|
+
observeBrowserTelemetry(this.telemetry, () =>
|
|
2309
|
+
this.telemetry?.recordError("BROWSER_RUNTIME_UNSUPPORTED"),
|
|
2310
|
+
);
|
|
2127
2311
|
throw new ProviderError(`Unsupported browser challenge: ${request.type}`, {
|
|
2128
2312
|
code: "BROWSER_RUNTIME_UNSUPPORTED",
|
|
2129
2313
|
});
|