@apifuse/provider-sdk 2.2.0-beta.3 → 2.2.0-beta.31
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 +131 -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 +123 -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 +14 -1
- package/dist/runtime/choice.js +566 -101
- 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 +2078 -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 +128 -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 +158 -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 +66 -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 +758 -197
- 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 +3062 -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
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { assertResolverHostAllowed } from "./hosts.js";
|
|
2
|
+
import { ResolverChallengeVerdictError, ResolverVendorUnavailableError, resolverVendorSupports, } from "./types.js";
|
|
3
|
+
const TWOCAPTCHA_VENDOR_ID = "2captcha";
|
|
4
|
+
const DEFAULT_TWOCAPTCHA_BASE_URL = "https://api.2captcha.com";
|
|
5
|
+
const DEFAULT_POLL_INTERVAL_MS = 3_000;
|
|
6
|
+
const DEFAULT_TIMEOUT_MS = 180_000;
|
|
7
|
+
class TwoCaptchaSolveTimeoutError extends Error {
|
|
8
|
+
constructor() {
|
|
9
|
+
super("2captcha resolver solve budget elapsed");
|
|
10
|
+
this.name = "TwoCaptchaSolveTimeoutError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function isJsonRecord(value) {
|
|
14
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
15
|
+
}
|
|
16
|
+
function abortReason(signal) {
|
|
17
|
+
return signal.reason ?? new DOMException("The operation was aborted", "AbortError");
|
|
18
|
+
}
|
|
19
|
+
function containsSensitiveValue(value, sensitiveValues) {
|
|
20
|
+
const secrets = sensitiveValues.filter((secret) => secret.length > 0);
|
|
21
|
+
if (secrets.length === 0)
|
|
22
|
+
return false;
|
|
23
|
+
const seen = new Set();
|
|
24
|
+
const inspect = (candidate) => {
|
|
25
|
+
if (typeof candidate === "string") {
|
|
26
|
+
return secrets.some((secret) => candidate.includes(secret));
|
|
27
|
+
}
|
|
28
|
+
if (candidate === null ||
|
|
29
|
+
(typeof candidate !== "object" && typeof candidate !== "function")) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
if (seen.has(candidate))
|
|
33
|
+
return false;
|
|
34
|
+
seen.add(candidate);
|
|
35
|
+
try {
|
|
36
|
+
for (const property of Reflect.ownKeys(candidate)) {
|
|
37
|
+
if (typeof property === "string" && inspect(property))
|
|
38
|
+
return true;
|
|
39
|
+
const descriptor = Object.getOwnPropertyDescriptor(candidate, property);
|
|
40
|
+
if (!descriptor)
|
|
41
|
+
return true;
|
|
42
|
+
if ("value" in descriptor && inspect(descriptor.value))
|
|
43
|
+
return true;
|
|
44
|
+
if (descriptor.get !== undefined || descriptor.set !== undefined)
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
};
|
|
53
|
+
return inspect(value);
|
|
54
|
+
}
|
|
55
|
+
function safeCauseOptions(error, sensitiveValues) {
|
|
56
|
+
return containsSensitiveValue(error, sensitiveValues) ? {} : { cause: error };
|
|
57
|
+
}
|
|
58
|
+
function raceWithAbort(operation, signal, phase, sensitiveValues = []) {
|
|
59
|
+
if (signal.aborted)
|
|
60
|
+
return Promise.reject(abortReason(signal));
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
const cleanup = () => signal.removeEventListener("abort", onAbort);
|
|
63
|
+
const onAbort = () => {
|
|
64
|
+
cleanup();
|
|
65
|
+
reject(abortReason(signal));
|
|
66
|
+
};
|
|
67
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
68
|
+
operation().then((value) => {
|
|
69
|
+
cleanup();
|
|
70
|
+
resolve(value);
|
|
71
|
+
}, (error) => {
|
|
72
|
+
cleanup();
|
|
73
|
+
if (phase === undefined) {
|
|
74
|
+
reject(error);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
reject(new ResolverVendorUnavailableError(TWOCAPTCHA_VENDOR_ID, "transport_failure", {
|
|
78
|
+
...safeCauseOptions(error, sensitiveValues),
|
|
79
|
+
phase,
|
|
80
|
+
}));
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
async function abortableDelay(ms, signal) {
|
|
85
|
+
let timer;
|
|
86
|
+
try {
|
|
87
|
+
await raceWithAbort(() => new Promise((resolve) => {
|
|
88
|
+
timer = setTimeout(resolve, ms);
|
|
89
|
+
}), signal);
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
if (timer !== undefined)
|
|
93
|
+
clearTimeout(timer);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function parseProxyConfiguration(proxyUrl) {
|
|
97
|
+
try {
|
|
98
|
+
const url = new URL(proxyUrl);
|
|
99
|
+
const protocol = url.protocol.slice(0, -1).toLowerCase();
|
|
100
|
+
const proxyType = protocol === "socks4" || protocol === "socks5"
|
|
101
|
+
? protocol
|
|
102
|
+
: protocol === "http" || protocol === "https"
|
|
103
|
+
? "http"
|
|
104
|
+
: undefined;
|
|
105
|
+
const defaultPort = proxyType === "http" ? 80 : 1080;
|
|
106
|
+
const proxyPort = Number(url.port || defaultPort);
|
|
107
|
+
if (!proxyType || !url.hostname || !Number.isInteger(proxyPort) || proxyPort <= 0) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
const proxyLogin = url.username ? decodeURIComponent(url.username) : undefined;
|
|
111
|
+
const proxyPassword = url.password ? decodeURIComponent(url.password) : undefined;
|
|
112
|
+
return {
|
|
113
|
+
proxyType,
|
|
114
|
+
proxyAddress: url.hostname,
|
|
115
|
+
proxyPort,
|
|
116
|
+
...(proxyLogin ? { proxyLogin } : {}),
|
|
117
|
+
...(proxyPassword ? { proxyPassword } : {}),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function errorText(payload, key) {
|
|
125
|
+
const value = payload[key];
|
|
126
|
+
return typeof value === "string" ? value : "";
|
|
127
|
+
}
|
|
128
|
+
function isAllocationExhausted(payload) {
|
|
129
|
+
const code = errorText(payload, "errorCode").toLowerCase();
|
|
130
|
+
const description = errorText(payload, "errorDescription").toLowerCase();
|
|
131
|
+
return (code === "error_zero_balance" ||
|
|
132
|
+
/(?:insufficient|zero|no|not enough)\s+(?:balance|funds|credit)/u.test(`${code} ${description}`));
|
|
133
|
+
}
|
|
134
|
+
function isNegativeVerdict(payload) {
|
|
135
|
+
return errorText(payload, "errorCode").toLowerCase() === "error_captcha_unsolvable";
|
|
136
|
+
}
|
|
137
|
+
function unavailableForPayload(payload, phase) {
|
|
138
|
+
if (isNegativeVerdict(payload)) {
|
|
139
|
+
return new ResolverChallengeVerdictError(TWOCAPTCHA_VENDOR_ID, "solve_failed");
|
|
140
|
+
}
|
|
141
|
+
return new ResolverVendorUnavailableError(TWOCAPTCHA_VENDOR_ID, isAllocationExhausted(payload) ? "allocation_exhausted" : "transport_failure", { phase });
|
|
142
|
+
}
|
|
143
|
+
async function postJson(fetchImpl, url, body, signal, phase, sensitiveValues) {
|
|
144
|
+
const response = await raceWithAbort(() => fetchImpl(url, {
|
|
145
|
+
method: "POST",
|
|
146
|
+
headers: { "content-type": "application/json" },
|
|
147
|
+
body: JSON.stringify(body),
|
|
148
|
+
signal,
|
|
149
|
+
redirect: "error",
|
|
150
|
+
}), signal, phase, sensitiveValues);
|
|
151
|
+
let responseText;
|
|
152
|
+
try {
|
|
153
|
+
responseText = await raceWithAbort(() => response.text(), signal, phase);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
if (error instanceof ResolverVendorUnavailableError)
|
|
157
|
+
throw error;
|
|
158
|
+
throw new ResolverVendorUnavailableError(TWOCAPTCHA_VENDOR_ID, "transport_failure", {
|
|
159
|
+
cause: error,
|
|
160
|
+
phase,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
let payload;
|
|
164
|
+
try {
|
|
165
|
+
payload = JSON.parse(responseText);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// JSON parse errors may contain response-body excerpts, so do not retain them as causes.
|
|
169
|
+
throw new ResolverVendorUnavailableError(TWOCAPTCHA_VENDOR_ID, "transport_failure", {
|
|
170
|
+
phase,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
if (!isJsonRecord(payload)) {
|
|
174
|
+
throw new ResolverVendorUnavailableError(TWOCAPTCHA_VENDOR_ID, "transport_failure", {
|
|
175
|
+
phase,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return { ok: response.ok, payload };
|
|
179
|
+
}
|
|
180
|
+
function taskIdFrom(payload) {
|
|
181
|
+
const taskId = payload.taskId;
|
|
182
|
+
return typeof taskId === "string" || typeof taskId === "number" ? taskId : undefined;
|
|
183
|
+
}
|
|
184
|
+
function tokenFrom(payload, challenge) {
|
|
185
|
+
const solution = payload.solution;
|
|
186
|
+
if (!isJsonRecord(solution))
|
|
187
|
+
return undefined;
|
|
188
|
+
if (challenge.kind === "aws_waf") {
|
|
189
|
+
return typeof solution.existing_token === "string" ? solution.existing_token : undefined;
|
|
190
|
+
}
|
|
191
|
+
if (typeof solution.gRecaptchaResponse === "string")
|
|
192
|
+
return solution.gRecaptchaResponse;
|
|
193
|
+
return typeof solution.token === "string" ? solution.token : undefined;
|
|
194
|
+
}
|
|
195
|
+
function endpoint(baseUrl, path) {
|
|
196
|
+
return `${baseUrl.replace(/\/+$/u, "")}/${path}`;
|
|
197
|
+
}
|
|
198
|
+
function spanErrorAttributes(error, phase) {
|
|
199
|
+
if (error instanceof ResolverVendorUnavailableError) {
|
|
200
|
+
return {
|
|
201
|
+
unavailability_reason: error.reason,
|
|
202
|
+
transport_phase: error.phase,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
// The solve budget raises TwoCaptchaSolveTimeoutError inside the spanned closure and
|
|
206
|
+
// the outer catch converts it only after the span has been finalized, so the span has
|
|
207
|
+
// to recognize it here or slow solves lose exactly the attribution these spans add.
|
|
208
|
+
if (error instanceof TwoCaptchaSolveTimeoutError) {
|
|
209
|
+
return {
|
|
210
|
+
unavailability_reason: "timeout",
|
|
211
|
+
transport_phase: phase,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
export function createTwoCaptchaResolverVendorAdapter(options) {
|
|
217
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
218
|
+
const baseUrl = options.baseUrl ?? DEFAULT_TWOCAPTCHA_BASE_URL;
|
|
219
|
+
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
220
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
221
|
+
const now = options.now ?? Date.now;
|
|
222
|
+
const delay = options.delay ?? abortableDelay;
|
|
223
|
+
return {
|
|
224
|
+
id: TWOCAPTCHA_VENDOR_ID,
|
|
225
|
+
supports(kind) {
|
|
226
|
+
return resolverVendorSupports(TWOCAPTCHA_VENDOR_ID, kind);
|
|
227
|
+
},
|
|
228
|
+
async solve(challenge, identity, callerSignal, traceRecorder) {
|
|
229
|
+
const apiKey = options.apiKey?.trim();
|
|
230
|
+
if (!apiKey) {
|
|
231
|
+
throw new ResolverVendorUnavailableError(TWOCAPTCHA_VENDOR_ID, "missing_credentials", {
|
|
232
|
+
phase: "create_task",
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
if (!resolverVendorSupports(TWOCAPTCHA_VENDOR_ID, challenge.kind)) {
|
|
236
|
+
throw new TypeError(`2captcha resolver does not support ${challenge.kind}`);
|
|
237
|
+
}
|
|
238
|
+
if (challenge.kind !== "recaptcha_v2" && challenge.kind !== "aws_waf") {
|
|
239
|
+
throw new ResolverVendorUnavailableError(TWOCAPTCHA_VENDOR_ID, "not_implemented", {
|
|
240
|
+
phase: "create_task",
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
if (challenge.kind === "aws_waf" &&
|
|
244
|
+
(!challenge.siteKey?.trim() ||
|
|
245
|
+
!challenge.captchaScript?.trim() ||
|
|
246
|
+
!challenge.context?.trim() ||
|
|
247
|
+
!challenge.iv?.trim())) {
|
|
248
|
+
throw new ResolverVendorUnavailableError(TWOCAPTCHA_VENDOR_ID, "not_implemented", {
|
|
249
|
+
phase: "create_task",
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
assertResolverHostAllowed(challenge.pageUrl, options.allowedHosts);
|
|
253
|
+
callerSignal.throwIfAborted();
|
|
254
|
+
const proxy = identity ? parseProxyConfiguration(identity.proxyUrl) : undefined;
|
|
255
|
+
if (identity && !proxy) {
|
|
256
|
+
throw new ResolverVendorUnavailableError(TWOCAPTCHA_VENDOR_ID, "transport_failure", {
|
|
257
|
+
phase: "create_task",
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
const solveController = new AbortController();
|
|
261
|
+
const onCallerAbort = () => solveController.abort(abortReason(callerSignal));
|
|
262
|
+
callerSignal.addEventListener("abort", onCallerAbort, { once: true });
|
|
263
|
+
const timeout = options.now
|
|
264
|
+
? undefined
|
|
265
|
+
: setTimeout(() => solveController.abort(new TwoCaptchaSolveTimeoutError()), timeoutMs);
|
|
266
|
+
const startedAt = now();
|
|
267
|
+
let phase = "create_task";
|
|
268
|
+
try {
|
|
269
|
+
const createTask = async () => {
|
|
270
|
+
const task = challenge.kind === "aws_waf"
|
|
271
|
+
? {
|
|
272
|
+
type: proxy ? "AmazonTask" : "AmazonTaskProxyless",
|
|
273
|
+
websiteURL: challenge.pageUrl,
|
|
274
|
+
websiteKey: challenge.siteKey,
|
|
275
|
+
captchaScript: challenge.captchaScript,
|
|
276
|
+
context: challenge.context,
|
|
277
|
+
iv: challenge.iv,
|
|
278
|
+
...(identity ? { userAgent: identity.userAgent } : {}),
|
|
279
|
+
...(proxy ?? {}),
|
|
280
|
+
}
|
|
281
|
+
: {
|
|
282
|
+
type: proxy ? "RecaptchaV2Task" : "RecaptchaV2TaskProxyless",
|
|
283
|
+
websiteURL: challenge.pageUrl,
|
|
284
|
+
websiteKey: challenge.siteKey,
|
|
285
|
+
isInvisible: false,
|
|
286
|
+
...(identity ? { userAgent: identity.userAgent } : {}),
|
|
287
|
+
...(proxy ?? {}),
|
|
288
|
+
};
|
|
289
|
+
const createResult = await postJson(fetchImpl, endpoint(baseUrl, "createTask"), { clientKey: apiKey, task }, solveController.signal, phase, [apiKey]);
|
|
290
|
+
const taskId = taskIdFrom(createResult.payload);
|
|
291
|
+
if (!createResult.ok || createResult.payload.errorId !== 0 || taskId === undefined) {
|
|
292
|
+
throw unavailableForPayload(createResult.payload, phase);
|
|
293
|
+
}
|
|
294
|
+
return taskId;
|
|
295
|
+
};
|
|
296
|
+
const taskId = traceRecorder
|
|
297
|
+
? await traceRecorder.runSpan("resolver.vendor.create_task", createTask, {
|
|
298
|
+
attributes: {
|
|
299
|
+
vendor: TWOCAPTCHA_VENDOR_ID,
|
|
300
|
+
challenge_kind: challenge.kind,
|
|
301
|
+
},
|
|
302
|
+
onError: (error) => spanErrorAttributes(error, "create_task"),
|
|
303
|
+
})
|
|
304
|
+
: await createTask();
|
|
305
|
+
phase = "poll_result";
|
|
306
|
+
const pollResult = async () => {
|
|
307
|
+
while (true) {
|
|
308
|
+
callerSignal.throwIfAborted();
|
|
309
|
+
const remainingMs = timeoutMs - (now() - startedAt);
|
|
310
|
+
if (remainingMs <= 0)
|
|
311
|
+
throw new TwoCaptchaSolveTimeoutError();
|
|
312
|
+
await delay(Math.min(pollIntervalMs, remainingMs), solveController.signal);
|
|
313
|
+
callerSignal.throwIfAborted();
|
|
314
|
+
if (now() - startedAt >= timeoutMs)
|
|
315
|
+
throw new TwoCaptchaSolveTimeoutError();
|
|
316
|
+
const pollResult = await postJson(fetchImpl, endpoint(baseUrl, "getTaskResult"), { clientKey: apiKey, taskId }, solveController.signal, phase, [apiKey]);
|
|
317
|
+
if (!pollResult.ok || pollResult.payload.errorId !== 0) {
|
|
318
|
+
throw unavailableForPayload(pollResult.payload, phase);
|
|
319
|
+
}
|
|
320
|
+
if (pollResult.payload.status === "processing")
|
|
321
|
+
continue;
|
|
322
|
+
if (pollResult.payload.status !== "ready") {
|
|
323
|
+
throw new ResolverVendorUnavailableError(TWOCAPTCHA_VENDOR_ID, "transport_failure", { phase });
|
|
324
|
+
}
|
|
325
|
+
const token = tokenFrom(pollResult.payload, challenge);
|
|
326
|
+
if (!token?.trim()) {
|
|
327
|
+
throw new ResolverVendorUnavailableError(TWOCAPTCHA_VENDOR_ID, "transport_failure", { phase });
|
|
328
|
+
}
|
|
329
|
+
return { form: "token", token };
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
return traceRecorder
|
|
333
|
+
? await traceRecorder.runSpan("resolver.vendor.poll_result", pollResult, {
|
|
334
|
+
attributes: {
|
|
335
|
+
vendor: TWOCAPTCHA_VENDOR_ID,
|
|
336
|
+
challenge_kind: challenge.kind,
|
|
337
|
+
},
|
|
338
|
+
onError: (error) => spanErrorAttributes(error, "poll_result"),
|
|
339
|
+
})
|
|
340
|
+
: await pollResult();
|
|
341
|
+
}
|
|
342
|
+
catch (error) {
|
|
343
|
+
if (callerSignal.aborted)
|
|
344
|
+
throw abortReason(callerSignal);
|
|
345
|
+
if (error instanceof TwoCaptchaSolveTimeoutError ||
|
|
346
|
+
solveController.signal.reason instanceof TwoCaptchaSolveTimeoutError) {
|
|
347
|
+
throw new ResolverVendorUnavailableError(TWOCAPTCHA_VENDOR_ID, "timeout", {
|
|
348
|
+
cause: error,
|
|
349
|
+
phase,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
if (error instanceof ResolverVendorUnavailableError ||
|
|
353
|
+
error instanceof ResolverChallengeVerdictError) {
|
|
354
|
+
throw error;
|
|
355
|
+
}
|
|
356
|
+
throw new ResolverVendorUnavailableError(TWOCAPTCHA_VENDOR_ID, "transport_failure", {
|
|
357
|
+
...safeCauseOptions(error, [apiKey]),
|
|
358
|
+
phase,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
finally {
|
|
362
|
+
if (timeout !== undefined)
|
|
363
|
+
clearTimeout(timeout);
|
|
364
|
+
callerSignal.removeEventListener("abort", onCallerAbort);
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
};
|
|
368
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { ChallengeSolution, ProviderChallenge, ProviderChallengeKind, ProviderResolverVendor } from "../../types.js";
|
|
2
|
+
import type { TraceRecorder } from "../trace.js";
|
|
3
|
+
export declare const RESOLVER_VENDOR_CAPABILITIES: {
|
|
4
|
+
readonly browser: readonly ["aws_waf", "cloudflare_interstitial"];
|
|
5
|
+
readonly "2captcha": readonly ["turnstile", "recaptcha_v2", "recaptcha_v3", "hcaptcha", "cloudflare_interstitial", "aws_waf", "akamai_sec_cpt", "akamai_sensor"];
|
|
6
|
+
readonly capsolver: readonly ["turnstile", "recaptcha_v2", "recaptcha_v3", "hcaptcha", "cloudflare_interstitial", "aws_waf"];
|
|
7
|
+
readonly capmonster: readonly ["turnstile", "recaptcha_v2", "recaptcha_v3", "hcaptcha"];
|
|
8
|
+
readonly custom: readonly ["turnstile", "recaptcha_v2", "recaptcha_v3", "hcaptcha", "cloudflare_interstitial", "aws_waf", "akamai_sec_cpt", "akamai_sensor"];
|
|
9
|
+
};
|
|
10
|
+
export declare function resolverVendorSupports(vendor: ProviderResolverVendor, kind: ProviderChallengeKind): boolean;
|
|
11
|
+
export interface ResolverIdentity {
|
|
12
|
+
readonly proxyUrl: string;
|
|
13
|
+
readonly userAgent: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ResolverIssuingIdentity {
|
|
16
|
+
/** Absent when the adapter genuinely solved without a proxy. */
|
|
17
|
+
readonly proxyUrl?: string;
|
|
18
|
+
readonly userAgent: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ResolverVendorTransport {
|
|
21
|
+
/**
|
|
22
|
+
* Bound to the resolved proxy lease and client profile.
|
|
23
|
+
* Implementations MUST NOT follow redirects and MUST return the initial redirect response.
|
|
24
|
+
*/
|
|
25
|
+
fetch(url: string, init: {
|
|
26
|
+
method: "GET" | "POST";
|
|
27
|
+
headers?: Readonly<Record<string, string>>;
|
|
28
|
+
body?: string;
|
|
29
|
+
signal: AbortSignal;
|
|
30
|
+
/** Implementations MUST honor manual redirect handling when the SDK guard sets it. */
|
|
31
|
+
redirect?: "manual";
|
|
32
|
+
}): Promise<{
|
|
33
|
+
readonly status: number;
|
|
34
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
35
|
+
readonly body: string;
|
|
36
|
+
/** Cookies observed on the response, including cache-relevant attributes. */
|
|
37
|
+
readonly cookies: readonly {
|
|
38
|
+
readonly name: string;
|
|
39
|
+
readonly value: string;
|
|
40
|
+
/** Epoch seconds. A session cookie must be undefined, never CDP's -1 sentinel. */
|
|
41
|
+
readonly expires?: number;
|
|
42
|
+
readonly httpOnly: boolean;
|
|
43
|
+
readonly secure: boolean;
|
|
44
|
+
readonly domain?: string;
|
|
45
|
+
readonly path?: string;
|
|
46
|
+
readonly sameSite?: string;
|
|
47
|
+
}[];
|
|
48
|
+
}>;
|
|
49
|
+
}
|
|
50
|
+
export interface ResolverVendorAdapter {
|
|
51
|
+
readonly id: ProviderResolverVendor;
|
|
52
|
+
readonly requiresTransport?: boolean | ((kind: ProviderChallengeKind) => boolean);
|
|
53
|
+
supports(kind: ProviderChallengeKind): boolean;
|
|
54
|
+
/** Identity the adapter actually used, reported after a successful solve. */
|
|
55
|
+
getIssuingIdentity?(solution: ChallengeSolution, requestedIdentity: ResolverIdentity | undefined, challenge: ProviderChallenge): ResolverIssuingIdentity | undefined;
|
|
56
|
+
solve(challenge: ProviderChallenge, identity: ResolverIdentity | undefined, signal: AbortSignal, traceRecorder?: TraceRecorder, transport?: ResolverVendorTransport): Promise<ChallengeSolution>;
|
|
57
|
+
}
|
|
58
|
+
export type ResolverVendorUnavailableReason = "missing_credentials" | "missing_proxy_identity" | "missing_client_profile" | "missing_transport" | "allocation_exhausted" | "transport_failure" | "timeout" | "not_implemented";
|
|
59
|
+
export type ResolverChallengeVerdictReason = "human_puzzle" | "solve_failed";
|
|
60
|
+
type ResolverErrorOptions = {
|
|
61
|
+
/** Raw cause; adapters must not place bodies, cookies, headers, credentials, or proxy URLs here. */
|
|
62
|
+
readonly cause?: unknown;
|
|
63
|
+
/** Upstream hostname only; never a URL. */
|
|
64
|
+
readonly upstreamHost?: string;
|
|
65
|
+
/** Adapter-defined sensor-loop phase, such as fetch_script or post_sensor. */
|
|
66
|
+
readonly phase?: string;
|
|
67
|
+
/** One-based sensor-loop round when known. */
|
|
68
|
+
readonly round?: number;
|
|
69
|
+
};
|
|
70
|
+
export declare class ResolverVendorUnavailableError extends Error {
|
|
71
|
+
readonly vendor: ProviderResolverVendor;
|
|
72
|
+
readonly reason: ResolverVendorUnavailableReason;
|
|
73
|
+
readonly upstreamHost?: string;
|
|
74
|
+
readonly phase?: string;
|
|
75
|
+
readonly round?: number;
|
|
76
|
+
constructor(vendor: ProviderResolverVendor, reason: ResolverVendorUnavailableReason, options?: ResolverErrorOptions);
|
|
77
|
+
}
|
|
78
|
+
export declare class ResolverChallengeVerdictError extends Error {
|
|
79
|
+
readonly vendor: ProviderResolverVendor;
|
|
80
|
+
readonly reason: ResolverChallengeVerdictReason;
|
|
81
|
+
constructor(vendor: ProviderResolverVendor, reason: ResolverChallengeVerdictReason, options?: ResolverErrorOptions);
|
|
82
|
+
}
|
|
83
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export const RESOLVER_VENDOR_CAPABILITIES = {
|
|
2
|
+
browser: ["aws_waf", "cloudflare_interstitial"],
|
|
3
|
+
"2captcha": [
|
|
4
|
+
"turnstile",
|
|
5
|
+
"recaptcha_v2",
|
|
6
|
+
"recaptcha_v3",
|
|
7
|
+
"hcaptcha",
|
|
8
|
+
"cloudflare_interstitial",
|
|
9
|
+
"aws_waf",
|
|
10
|
+
"akamai_sec_cpt",
|
|
11
|
+
"akamai_sensor",
|
|
12
|
+
],
|
|
13
|
+
capsolver: [
|
|
14
|
+
"turnstile",
|
|
15
|
+
"recaptcha_v2",
|
|
16
|
+
"recaptcha_v3",
|
|
17
|
+
"hcaptcha",
|
|
18
|
+
"cloudflare_interstitial",
|
|
19
|
+
"aws_waf",
|
|
20
|
+
],
|
|
21
|
+
capmonster: ["turnstile", "recaptcha_v2", "recaptcha_v3", "hcaptcha"],
|
|
22
|
+
custom: [
|
|
23
|
+
"turnstile",
|
|
24
|
+
"recaptcha_v2",
|
|
25
|
+
"recaptcha_v3",
|
|
26
|
+
"hcaptcha",
|
|
27
|
+
"cloudflare_interstitial",
|
|
28
|
+
"aws_waf",
|
|
29
|
+
"akamai_sec_cpt",
|
|
30
|
+
"akamai_sensor",
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
export function resolverVendorSupports(vendor, kind) {
|
|
34
|
+
return RESOLVER_VENDOR_CAPABILITIES[vendor].includes(kind);
|
|
35
|
+
}
|
|
36
|
+
export class ResolverVendorUnavailableError extends Error {
|
|
37
|
+
vendor;
|
|
38
|
+
reason;
|
|
39
|
+
upstreamHost;
|
|
40
|
+
phase;
|
|
41
|
+
round;
|
|
42
|
+
constructor(vendor, reason, options = {}) {
|
|
43
|
+
super(`Resolver vendor ${vendor} is unavailable: ${reason}`);
|
|
44
|
+
this.vendor = vendor;
|
|
45
|
+
this.reason = reason;
|
|
46
|
+
this.name = "ResolverVendorUnavailableError";
|
|
47
|
+
if (options.cause !== undefined) {
|
|
48
|
+
this.cause = options.cause;
|
|
49
|
+
}
|
|
50
|
+
this.upstreamHost = options.upstreamHost;
|
|
51
|
+
this.phase = options.phase;
|
|
52
|
+
this.round = options.round;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export class ResolverChallengeVerdictError extends Error {
|
|
56
|
+
vendor;
|
|
57
|
+
reason;
|
|
58
|
+
constructor(vendor, reason, options = {}) {
|
|
59
|
+
super(reason === "solve_failed"
|
|
60
|
+
? `Resolver vendor ${vendor} attempted the challenge but did not solve it`
|
|
61
|
+
: `Resolver vendor ${vendor} returned a challenge verdict: ${reason}`);
|
|
62
|
+
this.vendor = vendor;
|
|
63
|
+
this.reason = reason;
|
|
64
|
+
this.name = "ResolverChallengeVerdictError";
|
|
65
|
+
if (options.cause !== undefined) {
|
|
66
|
+
this.cause = options.cause;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type ProxyResolutionOptions } from "../config/loader.js";
|
|
2
|
+
import type { ChallengeSolution, ProviderCache, ProviderChallenge, ProviderChallengeKind, ProviderProxyMode, ProviderResolverConfig, ProviderResolverVendor, ResolverContext } from "../types.js";
|
|
3
|
+
import { type ResolverIdentity, type ResolverVendorAdapter, type ResolverVendorTransport } from "./resolver-vendors/types.js";
|
|
4
|
+
import type { TraceRecorder } from "./trace.js";
|
|
5
|
+
export { createUnsupportedResolverClient, RESOLVER_INSTRUMENTATION_METADATA, } from "./resolver-shared.js";
|
|
6
|
+
export { APIFUSE__CDP_POOL__URL, APIFUSE__RESOLVER__2CAPTCHA__API_KEY, APIFUSE__RESOLVER__CAPMONSTER__API_KEY, APIFUSE__RESOLVER__CAPSOLVER__API_KEY, APIFUSE__RESOLVER__TIMEOUT_MS, DEFAULT_RESOLVER_TIMEOUT_MS, } from "./resolver-config.js";
|
|
7
|
+
type EnvLike = Record<string, string | undefined>;
|
|
8
|
+
type ResolverChainClient = ResolverContext & {
|
|
9
|
+
solve(challenge: ProviderChallenge, signal?: AbortSignal, traceRecorder?: TraceRecorder): Promise<ChallengeSolution>;
|
|
10
|
+
};
|
|
11
|
+
export interface ResolverRuntimeOptions {
|
|
12
|
+
readonly allowedHosts?: readonly string[];
|
|
13
|
+
readonly cache?: ProviderCache;
|
|
14
|
+
/** Inputs for SDK-owned lazy proxy resolution. The SDK never accepts a caller-built identity. */
|
|
15
|
+
readonly proxyIntent?: {
|
|
16
|
+
readonly mode: ProviderProxyMode;
|
|
17
|
+
readonly upstream: NonNullable<ProxyResolutionOptions["upstream"]>;
|
|
18
|
+
readonly affinityKey?: ProxyResolutionOptions["affinityKey"];
|
|
19
|
+
readonly telemetry?: ProxyResolutionOptions["telemetry"];
|
|
20
|
+
readonly userAgent?: string;
|
|
21
|
+
};
|
|
22
|
+
/** Server-owned context/proxy scope used only for identity-bound cache entries. */
|
|
23
|
+
readonly identityScope?: string;
|
|
24
|
+
/** SDK-owned transport already bound to the resolved proxy lease and client profile. */
|
|
25
|
+
readonly transport?: ResolverVendorTransport;
|
|
26
|
+
/** Creates an SDK-owned transport bound to the declared profile and server-owned scope. */
|
|
27
|
+
readonly createTransport?: (input: {
|
|
28
|
+
readonly clientProfile?: string;
|
|
29
|
+
/** Server-owned proxy/context scope; the SDK never accepts a caller-built identity. */
|
|
30
|
+
readonly identityScope?: string;
|
|
31
|
+
}) => ResolverVendorTransport;
|
|
32
|
+
}
|
|
33
|
+
export type ResolverInstrumentationMetadata = {
|
|
34
|
+
readonly target: ResolverContext;
|
|
35
|
+
readonly traceRecorder: TraceRecorder;
|
|
36
|
+
};
|
|
37
|
+
export type ResolverAdapterFactory = (configuration: string, timeoutMs: number, allowedHosts: readonly string[]) => ResolverVendorAdapter;
|
|
38
|
+
export declare const RESOLVER_ADAPTER_REGISTRY: Partial<Readonly<Record<ProviderResolverVendor, ResolverAdapterFactory>>>;
|
|
39
|
+
export declare function swapResolverAdapterFactoryForTests(vendor: ProviderResolverVendor, factory: ResolverAdapterFactory | undefined): () => void;
|
|
40
|
+
/** Internal test seam; deliberately not re-exported from the package root. */
|
|
41
|
+
export declare function swapResolverDefaultUserAgentForTests(resolver: (() => string | undefined) | undefined): () => void;
|
|
42
|
+
/** Remove the cached entry for the exact solution object returned by this resolver. */
|
|
43
|
+
export declare function invalidateResolverSolution(resolver: ResolverContext, challenge: ProviderChallenge, solution: ChallengeSolution): Promise<void>;
|
|
44
|
+
export declare function createResolverClient(options: {
|
|
45
|
+
readonly kinds: readonly ProviderChallengeKind[];
|
|
46
|
+
readonly adapters: readonly ResolverVendorAdapter[];
|
|
47
|
+
readonly unavailableReason?: string;
|
|
48
|
+
readonly cache?: ProviderCache;
|
|
49
|
+
readonly identity?: ResolverIdentity;
|
|
50
|
+
readonly proxyIntent?: ResolverRuntimeOptions["proxyIntent"];
|
|
51
|
+
readonly transport?: ResolverVendorTransport;
|
|
52
|
+
readonly createTransport?: ResolverRuntimeOptions["createTransport"];
|
|
53
|
+
readonly clientProfile?: string;
|
|
54
|
+
readonly allowedHosts?: readonly string[];
|
|
55
|
+
}): ResolverChainClient;
|
|
56
|
+
export declare function bindResolverSignal(resolver: ResolverContext, defaultSignal: AbortSignal | undefined): ResolverContext;
|
|
57
|
+
export declare function createResolverClientFromEnv(config: ProviderResolverConfig | undefined, env?: EnvLike, options?: ResolverRuntimeOptions): ResolverContext;
|
|
58
|
+
/** Internal test seam; deliberately not re-exported from the package root. */
|
|
59
|
+
export declare function createResolverClientFromEnvForTests(config: ProviderResolverConfig | undefined, env: EnvLike, options: ResolverRuntimeOptions, adapterFactories: Partial<Readonly<Record<ProviderResolverVendor, ResolverAdapterFactory>>>): ResolverContext;
|