@apifuse/provider-sdk 2.2.0-beta.22 → 2.2.0-beta.24

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.
Files changed (64) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/bin/apifuse-dev.ts +4 -0
  3. package/bin/apifuse-pack-types.ts +234 -38
  4. package/bin/apifuse-perf.ts +15 -12
  5. package/bin/apifuse-record.ts +4 -0
  6. package/dist/config/loader.d.ts +8 -19
  7. package/dist/config/loader.js +28 -86
  8. package/dist/contract-types.d.ts +1 -0
  9. package/dist/contract.js +2 -0
  10. package/dist/define.d.ts +5 -1
  11. package/dist/define.js +79 -6
  12. package/dist/error-resolution.js +5 -0
  13. package/dist/index.d.ts +4 -2
  14. package/dist/index.js +2 -0
  15. package/dist/provider.d.ts +1 -1
  16. package/dist/runtime/auth-flow.d.ts +2 -1
  17. package/dist/runtime/auth-flow.js +4 -0
  18. package/dist/runtime/browser.js +78 -9
  19. package/dist/runtime/http.js +0 -1
  20. package/dist/runtime/instrumentation.js +26 -1
  21. package/dist/runtime/ocr.d.ts +29 -0
  22. package/dist/runtime/ocr.js +440 -0
  23. package/dist/runtime/resolver-vendors/bindings.d.ts +8 -0
  24. package/dist/runtime/resolver-vendors/bindings.js +15 -0
  25. package/dist/runtime/resolver-vendors/browser.d.ts +24 -0
  26. package/dist/runtime/resolver-vendors/browser.js +287 -0
  27. package/dist/runtime/resolver-vendors/types.d.ts +42 -0
  28. package/dist/runtime/resolver-vendors/types.js +57 -0
  29. package/dist/runtime/resolver.d.ts +39 -0
  30. package/dist/runtime/resolver.js +414 -0
  31. package/dist/runtime/state.d.ts +3 -0
  32. package/dist/runtime/state.js +245 -141
  33. package/dist/runtime/stealth.js +3 -6
  34. package/dist/runtime/stt.js +1 -12
  35. package/dist/runtime/timeout.d.ts +5 -0
  36. package/dist/runtime/timeout.js +12 -0
  37. package/dist/server/serve.d.ts +6 -1
  38. package/dist/server/serve.js +39 -8
  39. package/dist/testing/run.js +10 -0
  40. package/dist/types.d.ts +163 -4
  41. package/package.json +1 -1
  42. package/src/config/loader.ts +35 -111
  43. package/src/contract-types.ts +1 -0
  44. package/src/contract.ts +2 -0
  45. package/src/define.ts +121 -7
  46. package/src/error-resolution.ts +5 -0
  47. package/src/index.ts +45 -1
  48. package/src/provider.ts +1 -0
  49. package/src/runtime/auth-flow.ts +6 -0
  50. package/src/runtime/browser.ts +139 -19
  51. package/src/runtime/http.ts +0 -1
  52. package/src/runtime/instrumentation.ts +36 -2
  53. package/src/runtime/ocr.ts +523 -0
  54. package/src/runtime/resolver-vendors/bindings.ts +31 -0
  55. package/src/runtime/resolver-vendors/browser.ts +420 -0
  56. package/src/runtime/resolver-vendors/types.ts +113 -0
  57. package/src/runtime/resolver.ts +668 -0
  58. package/src/runtime/state.ts +323 -166
  59. package/src/runtime/stealth.ts +3 -6
  60. package/src/runtime/stt.ts +1 -19
  61. package/src/runtime/timeout.ts +18 -0
  62. package/src/server/serve.ts +80 -5
  63. package/src/testing/run.ts +15 -0
  64. package/src/types.ts +188 -4
@@ -0,0 +1,15 @@
1
+ export const RESOLVER_CHALLENGE_BINDINGS = {
2
+ aws_waf: "portable",
3
+ cloudflare_interstitial: "identity_scoped",
4
+ };
5
+ export function resolverChallengeIsIdentityScoped(challenge) {
6
+ return (RESOLVER_CHALLENGE_BINDINGS[challenge.kind] ===
7
+ "identity_scoped");
8
+ }
9
+ export function resolverChallengeIssuingIdentity(challenge, identity) {
10
+ const binding = RESOLVER_CHALLENGE_BINDINGS[challenge.kind];
11
+ if (binding === "portable") {
12
+ return { userAgent: identity.userAgent };
13
+ }
14
+ return identity;
15
+ }
@@ -0,0 +1,24 @@
1
+ import type { BrowserClient, ChallengeSolution, ProviderChallenge } from "../../types.js";
2
+ import { type BrowserClientOptions } from "../browser.js";
3
+ import type { TraceRecorder } from "../trace.js";
4
+ import { type ResolverIdentity, type ResolverVendorAdapter } from "./types.js";
5
+ type BrowserClientFactory = (options: BrowserClientOptions) => BrowserClient;
6
+ export interface BrowserResolverVendorOptions {
7
+ readonly cdpUrl?: string;
8
+ readonly timeoutMs: number;
9
+ readonly pollIntervalMs?: number;
10
+ readonly allowedHosts: readonly string[];
11
+ readonly createClient?: BrowserClientFactory;
12
+ }
13
+ export type BrowserResolverSolution = Extract<ChallengeSolution, {
14
+ readonly form: "cookies";
15
+ }> & {
16
+ /** Unix seconds from the cookie that proved the challenge cleared. */
17
+ readonly expires?: number;
18
+ };
19
+ export interface BrowserResolverVendorAdapter extends ResolverVendorAdapter {
20
+ readonly id: "browser";
21
+ solve(challenge: ProviderChallenge, identity: ResolverIdentity | undefined, signal: AbortSignal, traceRecorder?: TraceRecorder): Promise<BrowserResolverSolution>;
22
+ }
23
+ export declare function createBrowserResolverVendorAdapter(options: BrowserResolverVendorOptions): BrowserResolverVendorAdapter;
24
+ export {};
@@ -0,0 +1,287 @@
1
+ import { isProviderError, ProviderError } from "../../errors.js";
2
+ import { createBrowserClient } from "../browser.js";
3
+ import { resolverChallengeIssuingIdentity } from "./bindings.js";
4
+ import { ResolverVendorUnavailableError, } from "./types.js";
5
+ const BROWSER_VENDOR_ID = "browser";
6
+ const DEFAULT_COOKIE_POLL_INTERVAL_MS = 100;
7
+ const SUCCESS_COOKIE_NAMES = {
8
+ aws_waf: "aws-waf-token",
9
+ cloudflare_interstitial: "cf_clearance",
10
+ };
11
+ class BrowserSolveTimeoutError extends Error {
12
+ constructor() {
13
+ super("Browser resolver solve budget elapsed");
14
+ this.name = "BrowserSolveTimeoutError";
15
+ }
16
+ }
17
+ class BrowserCleanupTimeoutError extends Error {
18
+ constructor(timeoutMs) {
19
+ super(`Browser resolver cleanup exceeded ${timeoutMs}ms`);
20
+ this.name = "BrowserCleanupTimeoutError";
21
+ }
22
+ }
23
+ function abortReason(signal) {
24
+ return signal.reason ?? new DOMException("The operation was aborted", "AbortError");
25
+ }
26
+ function raceWithAbort(operation, signal) {
27
+ if (signal.aborted) {
28
+ return Promise.reject(abortReason(signal));
29
+ }
30
+ return new Promise((resolve, reject) => {
31
+ const cleanup = () => signal.removeEventListener("abort", onAbort);
32
+ const onAbort = () => {
33
+ cleanup();
34
+ reject(abortReason(signal));
35
+ };
36
+ signal.addEventListener("abort", onAbort, { once: true });
37
+ operation().then((value) => {
38
+ cleanup();
39
+ resolve(value);
40
+ }, (error) => {
41
+ cleanup();
42
+ reject(error);
43
+ });
44
+ });
45
+ }
46
+ async function runBoundedCleanup(cleanup, timeoutMs, challengeKind, operation, traceRecorder) {
47
+ let timeout;
48
+ const boundedCleanup = async () => {
49
+ try {
50
+ await Promise.race([
51
+ cleanup(),
52
+ new Promise((_resolve, reject) => {
53
+ timeout = setTimeout(() => reject(new BrowserCleanupTimeoutError(timeoutMs)), timeoutMs);
54
+ }),
55
+ ]);
56
+ }
57
+ finally {
58
+ if (timeout !== undefined)
59
+ clearTimeout(timeout);
60
+ }
61
+ };
62
+ if (!traceRecorder) {
63
+ await boundedCleanup().catch(() => undefined);
64
+ return;
65
+ }
66
+ await traceRecorder
67
+ .runSpan("resolver.vendor.cleanup", boundedCleanup, {
68
+ attributes: {
69
+ vendor: BROWSER_VENDOR_ID,
70
+ challenge_kind: challengeKind,
71
+ operation,
72
+ },
73
+ onError(error) {
74
+ return {
75
+ error_message: error instanceof Error ? error.message : String(error),
76
+ ...(error instanceof Error && error.stack ? { error_stack: error.stack } : {}),
77
+ };
78
+ },
79
+ })
80
+ .catch(() => undefined);
81
+ }
82
+ async function abortableDelay(ms, signal) {
83
+ let timer;
84
+ try {
85
+ await raceWithAbort(() => new Promise((resolve) => {
86
+ timer = setTimeout(resolve, ms);
87
+ }), signal);
88
+ }
89
+ finally {
90
+ if (timer !== undefined)
91
+ clearTimeout(timer);
92
+ }
93
+ }
94
+ function isSupportedKind(kind) {
95
+ return Object.hasOwn(SUCCESS_COOKIE_NAMES, kind);
96
+ }
97
+ function normalizedHostname(hostname) {
98
+ return hostname.trim().toLowerCase().replace(/\.$/, "");
99
+ }
100
+ function assertChallengeHostAllowed(pageUrl, allowedHosts) {
101
+ const challengeHost = normalizedHostname(new URL(pageUrl).hostname);
102
+ const isAllowed = allowedHosts.some((host) => {
103
+ const declaredHost = normalizedHostname(host);
104
+ return declaredHost.length > 0 && !declaredHost.includes("*") && declaredHost === challengeHost;
105
+ });
106
+ if (isAllowed)
107
+ return;
108
+ throw new ProviderError(`Resolver challenge host "${challengeHost}" is not declared`, {
109
+ code: "RESOLVER_HOST_NOT_ALLOWED",
110
+ fix: "Add the exact challenge hostname to the provider's allowedHosts declaration.",
111
+ });
112
+ }
113
+ function cookieDomainSpecificity(cookie) {
114
+ return normalizedHostname(cookie.domain.replace(/^\./, "")).length;
115
+ }
116
+ function isHostOnlyCookieFor(cookie, hostname) {
117
+ return (!cookie.domain.startsWith(".") &&
118
+ normalizedHostname(cookie.domain) === normalizedHostname(hostname));
119
+ }
120
+ function cookieAppliesToUrl(cookie, url) {
121
+ const cookieDomain = normalizedHostname(cookie.domain.replace(/^\./, ""));
122
+ const requestHostname = normalizedHostname(url.hostname);
123
+ const domainMatches = cookieDomain.length > 0 &&
124
+ (requestHostname === cookieDomain ||
125
+ (cookie.domain.startsWith(".") && requestHostname.endsWith(`.${cookieDomain}`)));
126
+ if (!domainMatches || (cookie.secure && url.protocol !== "https:"))
127
+ return false;
128
+ const requestPath = url.pathname || "/";
129
+ const cookiePath = cookie.path;
130
+ return (cookiePath.startsWith("/") &&
131
+ (requestPath === cookiePath ||
132
+ (requestPath.startsWith(cookiePath) &&
133
+ (cookiePath.endsWith("/") || requestPath[cookiePath.length] === "/"))));
134
+ }
135
+ function selectSuccessCookie(cookies, successCookieName, pageUrl) {
136
+ const url = new URL(pageUrl);
137
+ return cookies
138
+ .filter((cookie) => cookie.name === successCookieName && cookieAppliesToUrl(cookie, url))
139
+ .sort((left, right) => Number(isHostOnlyCookieFor(right, url.hostname)) -
140
+ Number(isHostOnlyCookieFor(left, url.hostname)) ||
141
+ cookieDomainSpecificity(right) - cookieDomainSpecificity(left) ||
142
+ right.path.length - left.path.length)[0];
143
+ }
144
+ async function solveInPage(page, pageUrl, successCookieName, pollIntervalMs, signal) {
145
+ await raceWithAbort(() => page.goto(pageUrl), signal);
146
+ while (true) {
147
+ const cookies = await raceWithAbort(() => page.cookies(), signal);
148
+ const successCookie = selectSuccessCookie(cookies, successCookieName, pageUrl);
149
+ if (successCookie) {
150
+ const userAgent = await raceWithAbort(() => page.evaluate("navigator.userAgent"), signal);
151
+ return {
152
+ form: "cookies",
153
+ cookies: { [successCookieName]: successCookie.value },
154
+ userAgent,
155
+ ...(successCookie.expires === undefined ? {} : { expires: successCookie.expires }),
156
+ };
157
+ }
158
+ await abortableDelay(pollIntervalMs, signal);
159
+ }
160
+ }
161
+ const POOL_ALLOCATION_EXHAUSTED_CODES = new Set([
162
+ -32_001, // queue full
163
+ -32_002, // acquire timed out
164
+ -32_003, // shutting down
165
+ ]);
166
+ function poolErrorCode(error) {
167
+ const code = error.code;
168
+ return typeof code === "number" ? code : undefined;
169
+ }
170
+ function knownUnavailableReason(error) {
171
+ // Source-grounded mappings:
172
+ // - apps/cdp-pool/src/index.ts: the JSON-RPC codes and messages below.
173
+ // - src/runtime/browser.ts: BROWSER_CDP_POOL_REQUIRED and the two WebSocket messages.
174
+ // The pool's numeric JSON-RPC code is authoritative and is preferred whenever present.
175
+ // The message substrings remain as a fallback for pool builds predating code
176
+ // propagation; they are exact strings verified against the pool source. -32004 (unknown
177
+ // lease) and -32006 (missing allowedHosts) are deliberately unmapped: both are caller
178
+ // bugs, and the next vendor would fail identically, so they propagate unchanged.
179
+ if (isProviderError(error)) {
180
+ return error.code === "BROWSER_CDP_POOL_REQUIRED" ? "missing_credentials" : undefined;
181
+ }
182
+ if (!(error instanceof Error))
183
+ return undefined;
184
+ const code = poolErrorCode(error);
185
+ if (code !== undefined) {
186
+ return POOL_ALLOCATION_EXHAUSTED_CODES.has(code) ? "allocation_exhausted" : undefined;
187
+ }
188
+ if (error.message.includes("CDP pool acquire queue is full") ||
189
+ error.message.includes("CDP pool acquire timed out") ||
190
+ error.message.includes("CDP pool is shutting down")) {
191
+ return "allocation_exhausted";
192
+ }
193
+ if (error.message.includes("Unable to connect to WebSocket endpoint") ||
194
+ error.message.includes("WebSocket closed")) {
195
+ return "transport_failure";
196
+ }
197
+ return undefined;
198
+ }
199
+ async function closeBrowserClient(client, timeoutMs, challengeKind, traceRecorder) {
200
+ const close = client?.close;
201
+ if (!close)
202
+ return;
203
+ await runBoundedCleanup(() => close.call(client), timeoutMs, challengeKind, "client.close", traceRecorder);
204
+ }
205
+ export function createBrowserResolverVendorAdapter(options) {
206
+ const createClient = options.createClient ?? createBrowserClient;
207
+ const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_COOKIE_POLL_INTERVAL_MS;
208
+ return {
209
+ id: BROWSER_VENDOR_ID,
210
+ supports(kind) {
211
+ return isSupportedKind(kind);
212
+ },
213
+ getIssuingIdentity(solution, requestedIdentity, challenge) {
214
+ if (solution.form !== "cookies" || !isSupportedKind(challenge.kind))
215
+ return undefined;
216
+ return resolverChallengeIssuingIdentity(challenge, {
217
+ ...(requestedIdentity ? { proxyUrl: requestedIdentity.proxyUrl } : {}),
218
+ userAgent: solution.userAgent,
219
+ });
220
+ },
221
+ async solve(challenge, identity, callerSignal, traceRecorder) {
222
+ void identity;
223
+ if (!options.cdpUrl?.trim()) {
224
+ throw new ResolverVendorUnavailableError(BROWSER_VENDOR_ID, "missing_credentials");
225
+ }
226
+ if (!isSupportedKind(challenge.kind)) {
227
+ throw new TypeError(`Browser resolver does not support ${challenge.kind}`);
228
+ }
229
+ assertChallengeHostAllowed(challenge.pageUrl, options.allowedHosts);
230
+ const challengeKind = challenge.kind;
231
+ callerSignal.throwIfAborted();
232
+ const solveController = new AbortController();
233
+ const onCallerAbort = () => solveController.abort(abortReason(callerSignal));
234
+ callerSignal.addEventListener("abort", onCallerAbort, { once: true });
235
+ const timeout = setTimeout(() => solveController.abort(new BrowserSolveTimeoutError()), options.timeoutMs);
236
+ let client;
237
+ let handlerEntered = false;
238
+ try {
239
+ client = createClient({
240
+ allowedHosts: [...options.allowedHosts],
241
+ cdpUrl: options.cdpUrl.trim(),
242
+ requireCdpPool: true,
243
+ });
244
+ const contextOperation = client.withIsolatedContext(async (page) => {
245
+ handlerEntered = true;
246
+ return await solveInPage(page, challenge.pageUrl, SUCCESS_COOKIE_NAMES[challengeKind], pollIntervalMs, solveController.signal);
247
+ });
248
+ try {
249
+ return await raceWithAbort(() => contextOperation, solveController.signal);
250
+ }
251
+ catch (error) {
252
+ if (solveController.signal.aborted) {
253
+ if (handlerEntered) {
254
+ await runBoundedCleanup(() => contextOperation, options.timeoutMs, challengeKind, "context.close", traceRecorder);
255
+ throw error;
256
+ }
257
+ await closeBrowserClient(client, options.timeoutMs, challengeKind, traceRecorder);
258
+ void contextOperation.catch(() => undefined);
259
+ }
260
+ throw error;
261
+ }
262
+ }
263
+ catch (error) {
264
+ if (callerSignal.aborted)
265
+ throw abortReason(callerSignal);
266
+ if (error instanceof BrowserSolveTimeoutError) {
267
+ throw new ResolverVendorUnavailableError(BROWSER_VENDOR_ID, "timeout", {
268
+ cause: error,
269
+ });
270
+ }
271
+ if (error instanceof ResolverVendorUnavailableError) {
272
+ throw error;
273
+ }
274
+ const reason = knownUnavailableReason(error);
275
+ if (reason) {
276
+ throw new ResolverVendorUnavailableError(BROWSER_VENDOR_ID, reason, { cause: error });
277
+ }
278
+ throw error;
279
+ }
280
+ finally {
281
+ clearTimeout(timeout);
282
+ callerSignal.removeEventListener("abort", onCallerAbort);
283
+ await closeBrowserClient(client, options.timeoutMs, challengeKind, traceRecorder);
284
+ }
285
+ },
286
+ };
287
+ }
@@ -0,0 +1,42 @@
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"];
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"];
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 ResolverVendorAdapter {
21
+ readonly id: ProviderResolverVendor;
22
+ supports(kind: ProviderChallengeKind): boolean;
23
+ /** Identity the adapter actually used, reported after a successful solve. */
24
+ getIssuingIdentity?(solution: ChallengeSolution, requestedIdentity: ResolverIdentity | undefined, challenge: ProviderChallenge): ResolverIssuingIdentity | undefined;
25
+ solve(challenge: ProviderChallenge, identity: ResolverIdentity | undefined, signal: AbortSignal, traceRecorder?: TraceRecorder): Promise<ChallengeSolution>;
26
+ }
27
+ export type ResolverVendorUnavailableReason = "missing_credentials" | "missing_transport" | "allocation_exhausted" | "transport_failure" | "timeout" | "not_implemented";
28
+ export type ResolverChallengeVerdictReason = "human_puzzle";
29
+ type ResolverErrorOptions = {
30
+ readonly cause?: unknown;
31
+ };
32
+ export declare class ResolverVendorUnavailableError extends Error {
33
+ readonly vendor: ProviderResolverVendor;
34
+ readonly reason: ResolverVendorUnavailableReason;
35
+ constructor(vendor: ProviderResolverVendor, reason: ResolverVendorUnavailableReason, options?: ResolverErrorOptions);
36
+ }
37
+ export declare class ResolverChallengeVerdictError extends Error {
38
+ readonly vendor: ProviderResolverVendor;
39
+ readonly reason: ResolverChallengeVerdictReason;
40
+ constructor(vendor: ProviderResolverVendor, reason: ResolverChallengeVerdictReason, options?: ResolverErrorOptions);
41
+ }
42
+ export {};
@@ -0,0 +1,57 @@
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
+ ],
11
+ capsolver: [
12
+ "turnstile",
13
+ "recaptcha_v2",
14
+ "recaptcha_v3",
15
+ "hcaptcha",
16
+ "cloudflare_interstitial",
17
+ "aws_waf",
18
+ ],
19
+ capmonster: ["turnstile", "recaptcha_v2", "recaptcha_v3", "hcaptcha"],
20
+ custom: [
21
+ "turnstile",
22
+ "recaptcha_v2",
23
+ "recaptcha_v3",
24
+ "hcaptcha",
25
+ "cloudflare_interstitial",
26
+ "aws_waf",
27
+ ],
28
+ };
29
+ export function resolverVendorSupports(vendor, kind) {
30
+ return RESOLVER_VENDOR_CAPABILITIES[vendor].includes(kind);
31
+ }
32
+ export class ResolverVendorUnavailableError extends Error {
33
+ vendor;
34
+ reason;
35
+ constructor(vendor, reason, options = {}) {
36
+ super(`Resolver vendor ${vendor} is unavailable: ${reason}`);
37
+ this.vendor = vendor;
38
+ this.reason = reason;
39
+ this.name = "ResolverVendorUnavailableError";
40
+ if (options.cause !== undefined) {
41
+ this.cause = options.cause;
42
+ }
43
+ }
44
+ }
45
+ export class ResolverChallengeVerdictError extends Error {
46
+ vendor;
47
+ reason;
48
+ constructor(vendor, reason, options = {}) {
49
+ super(`Resolver vendor ${vendor} returned a challenge verdict: ${reason}`);
50
+ this.vendor = vendor;
51
+ this.reason = reason;
52
+ this.name = "ResolverChallengeVerdictError";
53
+ if (options.cause !== undefined) {
54
+ this.cause = options.cause;
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,39 @@
1
+ import type { ChallengeSolution, ProviderCache, ProviderChallenge, ProviderChallengeKind, ProviderResolverConfig, ProviderResolverVendor, ResolverContext } from "../types.js";
2
+ import { type ResolverIdentity, type ResolverVendorAdapter } from "./resolver-vendors/types.js";
3
+ import type { TraceRecorder } from "./trace.js";
4
+ export declare const APIFUSE__RESOLVER__2CAPTCHA__API_KEY = "APIFUSE__RESOLVER__2CAPTCHA__API_KEY";
5
+ export declare const APIFUSE__RESOLVER__CAPSOLVER__API_KEY = "APIFUSE__RESOLVER__CAPSOLVER__API_KEY";
6
+ export declare const APIFUSE__RESOLVER__CAPMONSTER__API_KEY = "APIFUSE__RESOLVER__CAPMONSTER__API_KEY";
7
+ export declare const APIFUSE__RESOLVER__TIMEOUT_MS = "APIFUSE__RESOLVER__TIMEOUT_MS";
8
+ export declare const APIFUSE__CDP_POOL__URL = "APIFUSE__CDP_POOL__URL";
9
+ export declare const DEFAULT_RESOLVER_TIMEOUT_MS = 180000;
10
+ type EnvLike = Record<string, string | undefined>;
11
+ type ResolverChainClient = ResolverContext & {
12
+ solve(challenge: ProviderChallenge, signal?: AbortSignal, traceRecorder?: TraceRecorder): Promise<ChallengeSolution>;
13
+ };
14
+ export interface ResolverRuntimeOptions {
15
+ readonly allowedHosts?: readonly string[];
16
+ readonly cache?: ProviderCache;
17
+ /** Server-owned context/proxy scope used only for identity-bound cache entries. */
18
+ readonly identityScope?: string;
19
+ }
20
+ export declare const RESOLVER_INSTRUMENTATION_METADATA: unique symbol;
21
+ export type ResolverInstrumentationMetadata = {
22
+ readonly target: ResolverContext;
23
+ readonly traceRecorder: TraceRecorder;
24
+ };
25
+ type ResolverAdapterFactory = (configuration: string, timeoutMs: number, allowedHosts: readonly string[]) => ResolverVendorAdapter;
26
+ export declare const RESOLVER_ADAPTER_REGISTRY: Partial<Readonly<Record<ProviderResolverVendor, ResolverAdapterFactory>>>;
27
+ /** Remove the cached entry for the exact solution object returned by this resolver. */
28
+ export declare function invalidateResolverSolution(resolver: ResolverContext, challenge: ProviderChallenge, solution: ChallengeSolution): Promise<void>;
29
+ export declare function createResolverClient(options: {
30
+ readonly kinds: readonly ProviderChallengeKind[];
31
+ readonly adapters: readonly ResolverVendorAdapter[];
32
+ readonly unavailableReason?: string;
33
+ readonly cache?: ProviderCache;
34
+ readonly identity?: ResolverIdentity;
35
+ }): ResolverChainClient;
36
+ export declare function createUnsupportedResolverClient(reason?: string): ResolverContext;
37
+ export declare function bindResolverSignal(resolver: ResolverContext, defaultSignal: AbortSignal | undefined): ResolverContext;
38
+ export declare function createResolverClientFromEnv(config: ProviderResolverConfig | undefined, env?: EnvLike, options?: ResolverRuntimeOptions): ResolverContext;
39
+ export {};