@apifuse/provider-sdk 2.2.0-beta.57 → 2.2.0-beta.59
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 +9 -0
- package/CHANGELOG.md +8 -0
- package/README.md +22 -5
- package/SUBMISSION.md +2 -1
- package/bin/apifuse-pack-types.ts +49 -0
- package/bin/apifuse-record.ts +6 -0
- package/dist/cli/templates/provider/README.md.tpl +19 -5
- package/dist/config/loader.d.ts +7 -0
- package/dist/config/loader.js +30 -6
- package/dist/declaration-validation.d.ts +2 -1
- package/dist/declaration-validation.js +39 -0
- package/dist/define.d.ts +5 -4
- package/dist/define.js +25 -1
- package/dist/engine.d.ts +16 -0
- package/dist/engine.js +58 -1
- package/dist/error-resolution.js +13 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +4 -2
- package/dist/lint.js +3 -3
- package/dist/provider.d.ts +3 -3
- package/dist/provider.js +2 -2
- package/dist/runtime/bounded-expiring-map.d.ts +21 -0
- package/dist/runtime/bounded-expiring-map.js +64 -0
- package/dist/runtime/cache.d.ts +2 -0
- package/dist/runtime/cache.js +8 -1
- package/dist/runtime/egress-lease.d.ts +63 -0
- package/dist/runtime/egress-lease.js +233 -0
- package/dist/runtime/http.js +1 -0
- package/dist/runtime/proxy-retry-policy.d.ts +4 -1
- package/dist/runtime/proxy-retry-policy.js +11 -2
- package/dist/runtime/request-telemetry.d.ts +7 -9
- package/dist/runtime/resolver-config.d.ts +7 -0
- package/dist/runtime/resolver-config.js +8 -0
- package/dist/runtime/resolver-public.d.ts +3 -1
- package/dist/runtime/resolver-public.js +1 -1
- package/dist/runtime/resolver-shared.d.ts +9 -0
- package/dist/runtime/resolver-shared.js +57 -0
- package/dist/runtime/resolver-telemetry.d.ts +134 -0
- package/dist/runtime/resolver-telemetry.js +204 -0
- package/dist/runtime/resolver-usage.d.ts +24 -0
- package/dist/runtime/resolver-usage.js +60 -0
- package/dist/runtime/resolver-vendors/bindings.d.ts +5 -0
- package/dist/runtime/resolver-vendors/bindings.js +5 -0
- package/dist/runtime/resolver-vendors/capsolver.d.ts +2 -2
- package/dist/runtime/resolver-vendors/capsolver.js +21 -9
- package/dist/runtime/resolver-vendors/hypersolutions.d.ts +27 -0
- package/dist/runtime/resolver-vendors/hypersolutions.js +369 -0
- package/dist/runtime/resolver-vendors/twocaptcha.d.ts +2 -2
- package/dist/runtime/resolver-vendors/twocaptcha.js +26 -10
- package/dist/runtime/resolver-vendors/types.d.ts +35 -5
- package/dist/runtime/resolver-vendors/types.js +20 -10
- package/dist/runtime/resolver.d.ts +5 -1
- package/dist/runtime/resolver.js +397 -109
- package/dist/runtime/stealth-akamai-sbsd.d.ts +54 -0
- package/dist/runtime/stealth-akamai-sbsd.js +142 -0
- package/dist/runtime/stealth.js +547 -53
- package/dist/schema.d.ts +9 -0
- package/dist/schema.js +16 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +1 -0
- package/dist/server/self-test.d.ts +4 -0
- package/dist/server/self-test.js +10 -1
- package/dist/server/serve-implementation.d.ts +7 -1
- package/dist/server/serve-implementation.js +181 -53
- package/dist/server/types.d.ts +6 -0
- package/dist/server/types.js +14 -0
- package/dist/stealth/profiles.d.ts +7 -1
- package/dist/stealth/profiles.js +9 -0
- package/dist/testing/run.js +6 -0
- package/dist/types.d.ts +115 -12
- package/package.json +1 -1
- package/src/cli/templates/provider/README.md.tpl +19 -5
- package/src/config/loader.ts +34 -6
- package/src/declaration-validation.ts +47 -1
- package/src/define.ts +46 -3
- package/src/engine.ts +76 -1
- package/src/error-resolution.ts +13 -0
- package/src/index.ts +32 -1
- package/src/lint.ts +3 -3
- package/src/provider.ts +9 -0
- package/src/runtime/bounded-expiring-map.ts +67 -0
- package/src/runtime/cache.ts +10 -1
- package/src/runtime/egress-lease.ts +336 -0
- package/src/runtime/http.ts +1 -0
- package/src/runtime/proxy-retry-policy.ts +12 -1
- package/src/runtime/request-telemetry.ts +5 -7
- package/src/runtime/resolver-config.ts +12 -0
- package/src/runtime/resolver-public.ts +21 -0
- package/src/runtime/resolver-shared.ts +75 -0
- package/src/runtime/resolver-telemetry.ts +362 -0
- package/src/runtime/resolver-usage.ts +96 -0
- package/src/runtime/resolver-vendors/bindings.ts +5 -0
- package/src/runtime/resolver-vendors/capsolver.ts +22 -3
- package/src/runtime/resolver-vendors/hypersolutions.ts +480 -0
- package/src/runtime/resolver-vendors/twocaptcha.ts +26 -12
- package/src/runtime/resolver-vendors/types.ts +60 -12
- package/src/runtime/resolver.ts +465 -124
- package/src/runtime/stealth-akamai-sbsd.ts +207 -0
- package/src/runtime/stealth.ts +727 -95
- package/src/schema.ts +17 -0
- package/src/server/index.ts +21 -0
- package/src/server/self-test.ts +13 -0
- package/src/server/serve-implementation.ts +268 -48
- package/src/server/types.ts +14 -0
- package/src/stealth/profiles.ts +10 -0
- package/src/testing/run.ts +6 -0
- package/src/types.ts +130 -22
package/src/runtime/resolver.ts
CHANGED
|
@@ -17,7 +17,9 @@ import type {
|
|
|
17
17
|
ProviderResolverVendor,
|
|
18
18
|
ResolverContext,
|
|
19
19
|
} from "../types.js";
|
|
20
|
+
import { isProviderCacheBypassed } from "./cache.js";
|
|
20
21
|
import {
|
|
22
|
+
RESOLVER_CHALLENGE_BINDINGS,
|
|
21
23
|
resolverChallengeAllowsDirectCache,
|
|
22
24
|
resolverChallengeIsCacheable,
|
|
23
25
|
resolverChallengeIsIdentityScoped,
|
|
@@ -25,21 +27,26 @@ import {
|
|
|
25
27
|
} from "./resolver-vendors/bindings.js";
|
|
26
28
|
import { createBrowserResolverVendorAdapter } from "./resolver-vendors/browser.js";
|
|
27
29
|
import { createCapsolverResolverVendorAdapter } from "./resolver-vendors/capsolver.js";
|
|
30
|
+
import { createHypersolutionsResolverVendorAdapter } from "./resolver-vendors/hypersolutions.js";
|
|
28
31
|
import { assertResolverHostAllowed } from "./resolver-vendors/hosts.js";
|
|
29
32
|
import { createTwoCaptchaResolverVendorAdapter } from "./resolver-vendors/twocaptcha.js";
|
|
30
33
|
import {
|
|
31
34
|
RESOLVER_VENDOR_CAPABILITIES,
|
|
32
35
|
type ResolverIdentity,
|
|
33
36
|
type ResolverIssuingIdentity,
|
|
37
|
+
type ResolverPaidUsageContext,
|
|
34
38
|
type ResolverVendorAdapter,
|
|
35
39
|
type ResolverVendorTransport,
|
|
36
40
|
ResolverVendorUnavailableError,
|
|
41
|
+
attachResolverVendorPollObserver,
|
|
37
42
|
type ResolverVendorUnavailableReason,
|
|
38
43
|
resolveProviderResolverVendors,
|
|
39
44
|
resolverVendorSupports,
|
|
40
45
|
} from "./resolver-vendors/types.js";
|
|
41
46
|
import {
|
|
42
47
|
createUnsupportedResolverClient,
|
|
48
|
+
inheritResolverTelemetryBinding,
|
|
49
|
+
registerResolverTelemetryBinding,
|
|
43
50
|
RESOLVER_INSTRUMENTATION_METADATA,
|
|
44
51
|
type ResolverSolveWithRecorder,
|
|
45
52
|
} from "./resolver-shared.js";
|
|
@@ -48,11 +55,18 @@ import {
|
|
|
48
55
|
APIFUSE__RESOLVER__2CAPTCHA__API_KEY,
|
|
49
56
|
APIFUSE__RESOLVER__CAPMONSTER__API_KEY,
|
|
50
57
|
APIFUSE__RESOLVER__CAPSOLVER__API_KEY,
|
|
58
|
+
APIFUSE__RESOLVER__HYPERSOLUTIONS__API_KEY,
|
|
51
59
|
APIFUSE__RESOLVER__TIMEOUT_MS,
|
|
52
60
|
DEFAULT_RESOLVER_TIMEOUT_MS,
|
|
61
|
+
kindRequiresClientProfile,
|
|
53
62
|
} from "./resolver-config.js";
|
|
54
63
|
import { DEFAULT_STEALTH_PROFILE } from "./stealth.js";
|
|
55
64
|
import type { TraceRecorder } from "./trace.js";
|
|
65
|
+
import type {
|
|
66
|
+
ResolverTelemetryErrorClass,
|
|
67
|
+
ResolverTelemetryPhase,
|
|
68
|
+
ResolverTelemetrySink,
|
|
69
|
+
} from "./resolver-telemetry.js";
|
|
56
70
|
|
|
57
71
|
export {
|
|
58
72
|
createUnsupportedResolverClient,
|
|
@@ -63,6 +77,7 @@ export {
|
|
|
63
77
|
APIFUSE__RESOLVER__2CAPTCHA__API_KEY,
|
|
64
78
|
APIFUSE__RESOLVER__CAPMONSTER__API_KEY,
|
|
65
79
|
APIFUSE__RESOLVER__CAPSOLVER__API_KEY,
|
|
80
|
+
APIFUSE__RESOLVER__HYPERSOLUTIONS__API_KEY,
|
|
66
81
|
APIFUSE__RESOLVER__TIMEOUT_MS,
|
|
67
82
|
DEFAULT_RESOLVER_TIMEOUT_MS,
|
|
68
83
|
} from "./resolver-config.js";
|
|
@@ -109,6 +124,8 @@ type ResolverChainClient = ResolverContext & {
|
|
|
109
124
|
export interface ResolverRuntimeOptions {
|
|
110
125
|
readonly allowedHosts?: readonly string[];
|
|
111
126
|
readonly cache?: ProviderCache;
|
|
127
|
+
/** Optional request-scoped observability sink; never used as resolver identity. */
|
|
128
|
+
readonly telemetry?: ResolverTelemetrySink;
|
|
112
129
|
/** Inputs for SDK-owned lazy proxy resolution. The SDK never accepts a caller-built identity. */
|
|
113
130
|
readonly proxyIntent?: {
|
|
114
131
|
readonly mode: ProviderProxyMode;
|
|
@@ -231,6 +248,13 @@ const resolverAdapterRegistry: Partial<Record<ProviderResolverVendor, ResolverAd
|
|
|
231
248
|
timeoutMs,
|
|
232
249
|
});
|
|
233
250
|
},
|
|
251
|
+
hypersolutions(configuration, timeoutMs, allowedHosts) {
|
|
252
|
+
return createHypersolutionsResolverVendorAdapter({
|
|
253
|
+
allowedHosts,
|
|
254
|
+
apiKey: configuration,
|
|
255
|
+
timeoutMs,
|
|
256
|
+
});
|
|
257
|
+
},
|
|
234
258
|
};
|
|
235
259
|
|
|
236
260
|
export const RESOLVER_ADAPTER_REGISTRY: Partial<
|
|
@@ -302,6 +326,12 @@ function assertDeclaredKind(
|
|
|
302
326
|
requestedKind: ProviderChallengeKind,
|
|
303
327
|
declaredKinds: readonly ProviderChallengeKind[],
|
|
304
328
|
): void {
|
|
329
|
+
if (!Object.hasOwn(RESOLVER_CHALLENGE_BINDINGS, requestedKind)) {
|
|
330
|
+
throw new ProviderError(`Unknown resolver kind "${requestedKind}"`, {
|
|
331
|
+
code: "RESOLVER_KIND_NOT_DECLARED",
|
|
332
|
+
fix: "Use a challenge kind exported by @apifuse/provider-sdk.",
|
|
333
|
+
});
|
|
334
|
+
}
|
|
305
335
|
if (declaredKinds.includes(requestedKind)) return;
|
|
306
336
|
|
|
307
337
|
const declared = declaredKinds.length > 0 ? declaredKinds.join(", ") : "none";
|
|
@@ -365,23 +395,26 @@ function throwUnsupportedKind(kind: ProviderChallengeKind, usingDefaultVendors:
|
|
|
365
395
|
});
|
|
366
396
|
}
|
|
367
397
|
|
|
368
|
-
function throwExhausted(
|
|
398
|
+
function throwExhausted(
|
|
399
|
+
challengeKind: ProviderChallengeKind,
|
|
400
|
+
attempts: readonly ResolverChainAttempt[],
|
|
401
|
+
executedAttemptCount = attempts.length,
|
|
402
|
+
): never {
|
|
369
403
|
const hasMissingChallengeInput = attempts.some(
|
|
370
404
|
(attempt) => attempt.reason === "missing_challenge_input",
|
|
371
405
|
);
|
|
372
|
-
|
|
373
|
-
.map(({ vendor, reason, missingFields }) =>
|
|
374
|
-
missingFields === undefined || missingFields.length === 0
|
|
375
|
-
? `${vendor}: ${reason}`
|
|
376
|
-
: `${vendor}: ${reason} (missing fields: ${missingFields.join(", ")})`,
|
|
377
|
-
)
|
|
378
|
-
.join(", ");
|
|
379
|
-
throw new ProviderError(`Resolver vendor chain exhausted: ${summary}`, {
|
|
406
|
+
throw new ProviderError("The challenge could not be resolved.", {
|
|
380
407
|
code: "RESOLVER_CHAIN_EXHAUSTED",
|
|
381
408
|
fix: hasMissingChallengeInput
|
|
382
|
-
? "Capture
|
|
383
|
-
: "
|
|
384
|
-
|
|
409
|
+
? "Capture all required challenge fields and retry the request."
|
|
410
|
+
: "Retry the request or contact support if the challenge continues.",
|
|
411
|
+
retryable: false,
|
|
412
|
+
details: {
|
|
413
|
+
challengeKind,
|
|
414
|
+
attempts: executedAttemptCount,
|
|
415
|
+
outcome: "exhausted",
|
|
416
|
+
retryable: false,
|
|
417
|
+
},
|
|
385
418
|
});
|
|
386
419
|
}
|
|
387
420
|
|
|
@@ -451,6 +484,16 @@ function restrictResolverTransport(
|
|
|
451
484
|
allowedHosts: readonly string[],
|
|
452
485
|
): ResolverVendorTransport {
|
|
453
486
|
return {
|
|
487
|
+
sessionHeaders: transport.sessionHeaders,
|
|
488
|
+
...(transport.getCookie
|
|
489
|
+
? {
|
|
490
|
+
getCookie(name, url) {
|
|
491
|
+
// Jar reads are gated like fetches: an adapter may only read state for declared hosts.
|
|
492
|
+
assertResolverHostAllowed(url, allowedHosts);
|
|
493
|
+
return transport.getCookie?.(name, url);
|
|
494
|
+
},
|
|
495
|
+
}
|
|
496
|
+
: {}),
|
|
454
497
|
async fetch(url, init) {
|
|
455
498
|
// Empty declarations remain deny-by-default, matching the adapter-factory/browser path.
|
|
456
499
|
assertResolverHostAllowed(url, allowedHosts);
|
|
@@ -494,10 +537,21 @@ function safePhase(phase: string | undefined): string | undefined {
|
|
|
494
537
|
return phase !== undefined && /^[a-z\d_.:-]{1,64}$/i.test(phase) ? phase : undefined;
|
|
495
538
|
}
|
|
496
539
|
|
|
497
|
-
function unavailableAttempt(
|
|
498
|
-
|
|
540
|
+
function unavailableAttempt(
|
|
541
|
+
error: ResolverVendorUnavailableError,
|
|
542
|
+
forTelemetry = false,
|
|
543
|
+
): ResolverChainAttempt {
|
|
544
|
+
// Preserve the full input for the collector's redact-then-bound path. Span
|
|
545
|
+
// attributes retain their existing sanitization and phase validation.
|
|
546
|
+
const cause =
|
|
547
|
+
forTelemetry && error.cause !== undefined
|
|
548
|
+
? {
|
|
549
|
+
name: error.cause instanceof Error ? error.cause.name : "Error",
|
|
550
|
+
message: error.cause instanceof Error ? error.cause.message : String(error.cause),
|
|
551
|
+
}
|
|
552
|
+
: safeCause(error);
|
|
499
553
|
const upstreamHost = safeUpstreamHost(error.upstreamHost);
|
|
500
|
-
const phase = safePhase(error.phase);
|
|
554
|
+
const phase = forTelemetry ? error.phase : safePhase(error.phase);
|
|
501
555
|
const round =
|
|
502
556
|
Number.isSafeInteger(error.round) && (error.round as number) > 0 ? error.round : undefined;
|
|
503
557
|
return {
|
|
@@ -524,6 +578,125 @@ function unavailableSpanAttributes(error: ResolverVendorUnavailableError): Recor
|
|
|
524
578
|
};
|
|
525
579
|
}
|
|
526
580
|
|
|
581
|
+
type ResolverAttemptDiagnostics = {
|
|
582
|
+
phase?: string;
|
|
583
|
+
pollCount?: number;
|
|
584
|
+
vendorErrorCode?: string;
|
|
585
|
+
vendorErrorDescription?: string;
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
function resolverTelemetryPhase(value: unknown): ResolverTelemetryPhase | undefined {
|
|
589
|
+
return value === "create_task" ||
|
|
590
|
+
value === "poll_result" ||
|
|
591
|
+
value === "cleanup" ||
|
|
592
|
+
value === "measure_ip" ||
|
|
593
|
+
value === "fetch_script" ||
|
|
594
|
+
value === "generate_payload" ||
|
|
595
|
+
value === "post_payload"
|
|
596
|
+
? value
|
|
597
|
+
: undefined;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function diagnosticString(value: unknown): string | undefined {
|
|
601
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function diagnosticCount(value: unknown): number | undefined {
|
|
605
|
+
return typeof value === "number" && Number.isFinite(value)
|
|
606
|
+
? Math.max(0, Math.floor(value))
|
|
607
|
+
: undefined;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function createResolverTelemetryTraceRecorder(input: {
|
|
611
|
+
base?: TraceRecorder;
|
|
612
|
+
vendor: ProviderResolverVendor;
|
|
613
|
+
attemptIndex: number;
|
|
614
|
+
telemetry?: ResolverTelemetrySink;
|
|
615
|
+
diagnostics: ResolverAttemptDiagnostics;
|
|
616
|
+
}): TraceRecorder | undefined {
|
|
617
|
+
if (!input.base && !input.telemetry) return undefined;
|
|
618
|
+
const recorder: TraceRecorder = {
|
|
619
|
+
async runSpan<T>(
|
|
620
|
+
name: string,
|
|
621
|
+
fn: () => Promise<T> | T,
|
|
622
|
+
spanOptions: {
|
|
623
|
+
attributes?: Record<string, unknown>;
|
|
624
|
+
onSuccess?: (value: T) => Record<string, unknown> | undefined;
|
|
625
|
+
onError?: (error: unknown) => Record<string, unknown> | undefined;
|
|
626
|
+
} = {},
|
|
627
|
+
) {
|
|
628
|
+
const adapterPhase = name.startsWith("resolver.vendor.")
|
|
629
|
+
? diagnosticString(name.replace("resolver.vendor.", ""))
|
|
630
|
+
: undefined;
|
|
631
|
+
const phase = resolverTelemetryPhase(adapterPhase);
|
|
632
|
+
const startedAt = Date.now();
|
|
633
|
+
let attributes: Record<string, unknown> | undefined;
|
|
634
|
+
let spanOutcome: "ok" | "error" = "ok";
|
|
635
|
+
const wrappedOptions = {
|
|
636
|
+
...spanOptions,
|
|
637
|
+
onSuccess(value: T) {
|
|
638
|
+
attributes = spanOptions.onSuccess?.(value);
|
|
639
|
+
return attributes;
|
|
640
|
+
},
|
|
641
|
+
onError(error: unknown) {
|
|
642
|
+
attributes = spanOptions.onError?.(error);
|
|
643
|
+
return attributes;
|
|
644
|
+
},
|
|
645
|
+
};
|
|
646
|
+
try {
|
|
647
|
+
if (input.base) return await input.base.runSpan(name, fn, wrappedOptions);
|
|
648
|
+
const value = await fn();
|
|
649
|
+
attributes = spanOptions.onSuccess?.(value);
|
|
650
|
+
return value;
|
|
651
|
+
} catch (error) {
|
|
652
|
+
spanOutcome = "error";
|
|
653
|
+
if (!input.base) attributes = spanOptions.onError?.(error);
|
|
654
|
+
throw error;
|
|
655
|
+
} finally {
|
|
656
|
+
if (adapterPhase && adapterPhase !== "attempt") {
|
|
657
|
+
const reportedPhase = diagnosticString(attributes?.transport_phase) ?? adapterPhase;
|
|
658
|
+
const pollCount = diagnosticCount(attributes?.poll_count);
|
|
659
|
+
const vendorErrorCode = diagnosticString(attributes?.vendor_error_code);
|
|
660
|
+
const vendorErrorDescription = diagnosticString(
|
|
661
|
+
attributes?.vendor_error_description ?? attributes?.error_message,
|
|
662
|
+
);
|
|
663
|
+
if (phase === "cleanup") {
|
|
664
|
+
input.telemetry?.recordVendorAttempt({
|
|
665
|
+
vendor: input.vendor,
|
|
666
|
+
phase: "cleanup",
|
|
667
|
+
diagnostics: { phase: reportedPhase, attemptIndex: input.attemptIndex },
|
|
668
|
+
outcome: spanOutcome,
|
|
669
|
+
ms: Date.now() - startedAt,
|
|
670
|
+
...(pollCount === undefined ? {} : { pollCount }),
|
|
671
|
+
...(vendorErrorCode ? { vendorErrorCode } : {}),
|
|
672
|
+
...(vendorErrorDescription ? { vendorErrorDescription } : {}),
|
|
673
|
+
...(spanOutcome === "error" ? { errorClass: "unexpected" as const } : {}),
|
|
674
|
+
});
|
|
675
|
+
} else {
|
|
676
|
+
input.diagnostics.phase = reportedPhase;
|
|
677
|
+
if (pollCount !== undefined) input.diagnostics.pollCount = pollCount;
|
|
678
|
+
if (vendorErrorCode) input.diagnostics.vendorErrorCode = vendorErrorCode;
|
|
679
|
+
if (vendorErrorDescription) {
|
|
680
|
+
input.diagnostics.vendorErrorDescription = vendorErrorDescription;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
},
|
|
686
|
+
};
|
|
687
|
+
return attachResolverVendorPollObserver(recorder, () => {
|
|
688
|
+
input.diagnostics.pollCount = (input.diagnostics.pollCount ?? 0) + 1;
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
function resolverAttemptErrorClass(
|
|
693
|
+
error: unknown,
|
|
694
|
+
signal: AbortSignal,
|
|
695
|
+
): ResolverTelemetryErrorClass {
|
|
696
|
+
if (signal.aborted) return "aborted";
|
|
697
|
+
return error instanceof ResolverVendorUnavailableError ? error.reason : "unexpected";
|
|
698
|
+
}
|
|
699
|
+
|
|
527
700
|
function challengeOrigin(challenge: ProviderChallenge): string {
|
|
528
701
|
return new URL(challenge.pageUrl).origin;
|
|
529
702
|
}
|
|
@@ -710,13 +883,13 @@ async function cacheResolverSolution(
|
|
|
710
883
|
solution: ChallengeSolution,
|
|
711
884
|
identity: ResolverIssuingIdentity,
|
|
712
885
|
identityScope: string | undefined,
|
|
713
|
-
): Promise<
|
|
714
|
-
if (!resolverChallengeIsCacheable(challenge)) return;
|
|
886
|
+
): Promise<{ written: boolean; reason?: "no_expires" | "not_cacheable" }> {
|
|
887
|
+
if (!resolverChallengeIsCacheable(challenge)) return { written: false, reason: "not_cacheable" };
|
|
715
888
|
const expiresAtMs = solutionExpiryMs(solution);
|
|
716
889
|
const now = Date.now();
|
|
717
|
-
if (expiresAtMs === undefined) return;
|
|
890
|
+
if (expiresAtMs === undefined) return { written: false, reason: "no_expires" };
|
|
718
891
|
const ttlMs = Math.floor(expiresAtMs - now);
|
|
719
|
-
if (ttlMs <= MIN_RESOLVER_CACHE_TTL_MS) return;
|
|
892
|
+
if (ttlMs <= MIN_RESOLVER_CACHE_TTL_MS) return { written: false, reason: "no_expires" };
|
|
720
893
|
|
|
721
894
|
const scopedDigest =
|
|
722
895
|
identityScope !== undefined && resolverChallengeIsIdentityScoped(challenge)
|
|
@@ -727,7 +900,7 @@ async function cacheResolverSolution(
|
|
|
727
900
|
scopedDigest === undefined &&
|
|
728
901
|
identity.proxyUrl === undefined
|
|
729
902
|
) {
|
|
730
|
-
return;
|
|
903
|
+
return { written: false, reason: "not_cacheable" };
|
|
731
904
|
}
|
|
732
905
|
const issuerDigest = scopedDigest ?? resolverIdentityDigest(identity);
|
|
733
906
|
await cache.set(
|
|
@@ -736,7 +909,7 @@ async function cacheResolverSolution(
|
|
|
736
909
|
{ ttlMs },
|
|
737
910
|
);
|
|
738
911
|
rememberSolutionIssuer(solution, issuerDigest);
|
|
739
|
-
if (scopedDigest !== undefined || identity.proxyUrl !== undefined) return;
|
|
912
|
+
if (scopedDigest !== undefined || identity.proxyUrl !== undefined) return { written: true };
|
|
740
913
|
|
|
741
914
|
const indexKey = resolverSolutionIndexCacheKey(cache, challenge);
|
|
742
915
|
const current = await cache.get(indexKey);
|
|
@@ -750,6 +923,7 @@ async function cacheResolverSolution(
|
|
|
750
923
|
],
|
|
751
924
|
now,
|
|
752
925
|
);
|
|
926
|
+
return { written: true };
|
|
753
927
|
}
|
|
754
928
|
|
|
755
929
|
async function invalidateResolverSolutionWithOutcome(
|
|
@@ -866,6 +1040,10 @@ async function resolveResolverIdentity(
|
|
|
866
1040
|
}
|
|
867
1041
|
}
|
|
868
1042
|
|
|
1043
|
+
function createChainTelemetryBinding(options: Parameters<typeof createResolverChainClient>[0]) {
|
|
1044
|
+
return (telemetry: ResolverTelemetrySink) => createResolverChainClient({ ...options, telemetry });
|
|
1045
|
+
}
|
|
1046
|
+
|
|
869
1047
|
function createResolverChainClient(options: {
|
|
870
1048
|
readonly kinds: readonly ProviderChallengeKind[];
|
|
871
1049
|
readonly entries: readonly ResolverChainEntry[];
|
|
@@ -879,128 +1057,284 @@ function createResolverChainClient(options: {
|
|
|
879
1057
|
readonly createTransport?: ResolverRuntimeOptions["createTransport"];
|
|
880
1058
|
readonly clientProfile?: string;
|
|
881
1059
|
readonly allowedHosts?: readonly string[];
|
|
1060
|
+
readonly telemetry?: ResolverTelemetrySink;
|
|
882
1061
|
}): ResolverChainClient {
|
|
883
1062
|
assertClientProfileTransportContract(options.clientProfile, options.transport);
|
|
1063
|
+
const cache =
|
|
1064
|
+
options.cache && !isProviderCacheBypassed(options.cache) ? options.cache : undefined;
|
|
884
1065
|
const client: ResolverChainClient = {
|
|
885
1066
|
async solve(
|
|
886
1067
|
challenge: ProviderChallenge,
|
|
887
1068
|
signal: AbortSignal = new AbortController().signal,
|
|
888
1069
|
traceRecorder?: TraceRecorder,
|
|
889
1070
|
) {
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
1071
|
+
const solveStartedAt = Date.now();
|
|
1072
|
+
let terminalOutcome: "solved" | "cached" | "exhausted" | "aborted" | "error" = "error";
|
|
1073
|
+
try {
|
|
1074
|
+
assertDeclaredKind(challenge.kind, options.kinds);
|
|
1075
|
+
if (options.unavailableReason) {
|
|
1076
|
+
throw new ProviderError(options.unavailableReason, {
|
|
1077
|
+
code: "RESOLVER_UNAVAILABLE",
|
|
1078
|
+
fix: "Configure at least one declared resolver vendor or provide a test ResolverContext override.",
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
897
1081
|
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1082
|
+
const supportingEntries = options.entries.filter((entry) => entry.supports(challenge.kind));
|
|
1083
|
+
if (supportingEntries.length === 0) {
|
|
1084
|
+
throwUnsupportedKind(challenge.kind, options.usingDefaultVendors ?? false);
|
|
1085
|
+
}
|
|
1086
|
+
if (kindRequiresClientProfile(challenge.kind) && !options.clientProfile?.trim()) {
|
|
1087
|
+
options.telemetry?.recordIdentity({ source: "none", failure: "missing_client_profile" });
|
|
1088
|
+
throwExhausted(
|
|
1089
|
+
challenge.kind,
|
|
1090
|
+
supportingEntries.map((entry) => ({
|
|
1091
|
+
vendor: entry.id,
|
|
1092
|
+
reason: "missing_client_profile",
|
|
1093
|
+
})),
|
|
1094
|
+
0,
|
|
1095
|
+
);
|
|
1096
|
+
}
|
|
1097
|
+
signal.throwIfAborted();
|
|
1098
|
+
const identityResolution = options.proxyIntent
|
|
1099
|
+
? await resolveResolverIdentity(options.proxyIntent)
|
|
1100
|
+
: { identity: options.identity };
|
|
1101
|
+
const identity = identityResolution.identity;
|
|
1102
|
+
options.telemetry?.recordIdentity({
|
|
1103
|
+
// For SBSD, "declared" identifies the configured client profile and its bound
|
|
1104
|
+
// session identity; the engine independently supplies solver credentials.
|
|
1105
|
+
source:
|
|
1106
|
+
kindRequiresClientProfile(challenge.kind) && options.clientProfile
|
|
1107
|
+
? "declared"
|
|
1108
|
+
: options.proxyIntent
|
|
1109
|
+
? (identityResolution.userAgentSource ?? "defaulted")
|
|
1110
|
+
: identity
|
|
1111
|
+
? "declared"
|
|
1112
|
+
: "none",
|
|
1113
|
+
...(identityResolution.unavailableReason
|
|
1114
|
+
? { failure: identityResolution.unavailableReason }
|
|
1115
|
+
: {}),
|
|
1116
|
+
});
|
|
1117
|
+
signal.throwIfAborted();
|
|
1118
|
+
const requiredProxyIdentityMissing =
|
|
1119
|
+
options.proxyIntent?.mode === "required" && identity === undefined;
|
|
1120
|
+
if (requiredProxyIdentityMissing) {
|
|
1121
|
+
const reason = identityResolution.unavailableReason ?? "missing_proxy_identity";
|
|
1122
|
+
for (const [entryIndex, entry] of supportingEntries.entries()) {
|
|
1123
|
+
options.telemetry?.recordVendorAttempt({
|
|
1124
|
+
vendor: entry.id,
|
|
1125
|
+
phase: "create_task",
|
|
1126
|
+
diagnostics: { attemptIndex: entryIndex + 1 },
|
|
1127
|
+
outcome: "error",
|
|
1128
|
+
ms: 0,
|
|
1129
|
+
errorClass: reason,
|
|
1130
|
+
});
|
|
1131
|
+
const next = supportingEntries[entryIndex + 1];
|
|
1132
|
+
if (next) {
|
|
1133
|
+
options.telemetry?.recordFailover({ from: entry.id, to: next.id, reason });
|
|
946
1134
|
}
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
options.
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
1135
|
+
}
|
|
1136
|
+
throwExhausted(
|
|
1137
|
+
challenge.kind,
|
|
1138
|
+
supportingEntries.map((entry) => ({
|
|
1139
|
+
vendor: entry.id,
|
|
1140
|
+
reason,
|
|
1141
|
+
})),
|
|
1142
|
+
);
|
|
1143
|
+
}
|
|
1144
|
+
if (!cache) {
|
|
1145
|
+
options.telemetry?.recordCacheRead({ status: "disabled", challengeKind: challenge.kind });
|
|
1146
|
+
} else if (!resolverChallengeIsCacheable(challenge)) {
|
|
1147
|
+
options.telemetry?.recordCacheRead({
|
|
1148
|
+
status: "not_cacheable",
|
|
1149
|
+
challengeKind: challenge.kind,
|
|
1150
|
+
});
|
|
1151
|
+
} else {
|
|
1152
|
+
const cached = await findCachedSolution(
|
|
1153
|
+
cache,
|
|
1154
|
+
challenge,
|
|
1155
|
+
identity,
|
|
1156
|
+
options.identityScope,
|
|
1157
|
+
);
|
|
1158
|
+
options.telemetry?.recordCacheRead({
|
|
1159
|
+
status: cached ? "hit" : "miss",
|
|
1160
|
+
challengeKind: challenge.kind,
|
|
1161
|
+
});
|
|
1162
|
+
if (cached) {
|
|
1163
|
+
terminalOutcome = "cached";
|
|
1164
|
+
return cached;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
const attempts: ResolverChainAttempt[] = [];
|
|
1168
|
+
for (const [entryIndex, entry] of supportingEntries.entries()) {
|
|
1169
|
+
const adapter = entry.createAdapter();
|
|
1170
|
+
const attemptStartedAt = Date.now();
|
|
1171
|
+
const diagnostics: ResolverAttemptDiagnostics = {};
|
|
1172
|
+
const telemetryTraceRecorder = createResolverTelemetryTraceRecorder({
|
|
1173
|
+
base: traceRecorder,
|
|
1174
|
+
vendor: adapter.id,
|
|
1175
|
+
attemptIndex: entryIndex + 1,
|
|
1176
|
+
telemetry: options.telemetry,
|
|
1177
|
+
diagnostics,
|
|
1178
|
+
});
|
|
1179
|
+
let solution: ChallengeSolution;
|
|
1180
|
+
try {
|
|
1181
|
+
const solveAttempt = () => {
|
|
1182
|
+
const requiresTransport = adapterRequiresTransport(adapter, challenge.kind);
|
|
1183
|
+
const unrestrictedTransport =
|
|
1184
|
+
options.transport ??
|
|
1185
|
+
(requiresTransport
|
|
1186
|
+
? options.createTransport?.({
|
|
1187
|
+
clientProfile: options.clientProfile,
|
|
1188
|
+
identityScope: options.identityScope,
|
|
1189
|
+
})
|
|
1190
|
+
: undefined);
|
|
1191
|
+
if (requiresTransport && unrestrictedTransport === undefined) {
|
|
1192
|
+
throw new ResolverVendorUnavailableError(adapter.id, "missing_transport");
|
|
1193
|
+
}
|
|
1194
|
+
const transport = unrestrictedTransport
|
|
1195
|
+
? restrictResolverTransport(unrestrictedTransport, [
|
|
1196
|
+
...(options.allowedHosts ?? []),
|
|
1197
|
+
...(adapter.transportAllowedHosts ?? []),
|
|
1198
|
+
])
|
|
1199
|
+
: undefined;
|
|
1200
|
+
const usage: ResolverPaidUsageContext = {
|
|
1201
|
+
// Position in the declared chain, not in the kind-filtered list.
|
|
1202
|
+
vendorIndex: options.entries.indexOf(entry) + 1,
|
|
1203
|
+
...(options.identityScope
|
|
1204
|
+
? {
|
|
1205
|
+
resolverIdentityScope: createHash("sha256")
|
|
1206
|
+
.update(options.identityScope, "utf8")
|
|
1207
|
+
.digest("hex"),
|
|
1208
|
+
}
|
|
1209
|
+
: {}),
|
|
1210
|
+
};
|
|
1211
|
+
return adapter.solve(
|
|
982
1212
|
challenge,
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
1213
|
+
identity,
|
|
1214
|
+
signal,
|
|
1215
|
+
telemetryTraceRecorder,
|
|
1216
|
+
transport,
|
|
1217
|
+
usage,
|
|
986
1218
|
);
|
|
1219
|
+
};
|
|
1220
|
+
solution = traceRecorder
|
|
1221
|
+
? await traceRecorder.runSpan("resolver.vendor.attempt", solveAttempt, {
|
|
1222
|
+
attributes: {
|
|
1223
|
+
vendor: adapter.id,
|
|
1224
|
+
challenge_kind: challenge.kind,
|
|
1225
|
+
client_profile: options.clientProfile,
|
|
1226
|
+
resolver_identity_source: identityResolution.userAgentSource,
|
|
1227
|
+
},
|
|
1228
|
+
onError(error) {
|
|
1229
|
+
return error instanceof ResolverVendorUnavailableError
|
|
1230
|
+
? unavailableSpanAttributes(error)
|
|
1231
|
+
: undefined;
|
|
1232
|
+
},
|
|
1233
|
+
})
|
|
1234
|
+
: await solveAttempt();
|
|
1235
|
+
} catch (error) {
|
|
1236
|
+
const unavailable = error instanceof ResolverVendorUnavailableError ? error : undefined;
|
|
1237
|
+
const attempt = unavailable ? unavailableAttempt(unavailable, true) : undefined;
|
|
1238
|
+
options.telemetry?.recordVendorAttempt({
|
|
1239
|
+
vendor: adapter.id,
|
|
1240
|
+
phase:
|
|
1241
|
+
resolverTelemetryPhase(unavailable?.phase) ??
|
|
1242
|
+
resolverTelemetryPhase(diagnostics.phase) ??
|
|
1243
|
+
"create_task",
|
|
1244
|
+
diagnostics: {
|
|
1245
|
+
...attempt,
|
|
1246
|
+
attemptIndex: entryIndex + 1,
|
|
1247
|
+
phase: attempt?.phase ?? diagnostics.phase,
|
|
1248
|
+
},
|
|
1249
|
+
outcome: "error",
|
|
1250
|
+
ms: Date.now() - attemptStartedAt,
|
|
1251
|
+
errorClass: resolverAttemptErrorClass(error, signal),
|
|
1252
|
+
...(diagnostics.pollCount === undefined ? {} : { pollCount: diagnostics.pollCount }),
|
|
1253
|
+
...(diagnostics.vendorErrorCode
|
|
1254
|
+
? { vendorErrorCode: diagnostics.vendorErrorCode }
|
|
1255
|
+
: {}),
|
|
1256
|
+
...(diagnostics.vendorErrorDescription
|
|
1257
|
+
? { vendorErrorDescription: diagnostics.vendorErrorDescription }
|
|
1258
|
+
: {}),
|
|
1259
|
+
});
|
|
1260
|
+
signal.throwIfAborted();
|
|
1261
|
+
if (!unavailable || !attempt) throw error;
|
|
1262
|
+
attempts.push(attempt);
|
|
1263
|
+
const next = supportingEntries[entryIndex + 1];
|
|
1264
|
+
if (next) {
|
|
1265
|
+
options.telemetry?.recordFailover({
|
|
1266
|
+
from: adapter.id,
|
|
1267
|
+
to: next.id,
|
|
1268
|
+
reason: unavailable.reason,
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1271
|
+
continue;
|
|
1272
|
+
}
|
|
1273
|
+
options.telemetry?.recordVendorAttempt({
|
|
1274
|
+
vendor: adapter.id,
|
|
1275
|
+
phase: resolverTelemetryPhase(diagnostics.phase) ?? "poll_result",
|
|
1276
|
+
diagnostics: { attemptIndex: entryIndex + 1, phase: diagnostics.phase },
|
|
1277
|
+
outcome: "ok",
|
|
1278
|
+
ms: Date.now() - attemptStartedAt,
|
|
1279
|
+
...(diagnostics.pollCount === undefined ? {} : { pollCount: diagnostics.pollCount }),
|
|
1280
|
+
});
|
|
1281
|
+
if (cache) {
|
|
1282
|
+
try {
|
|
1283
|
+
let cacheWrite: { written: boolean; reason?: "no_expires" | "not_cacheable" };
|
|
1284
|
+
if (!resolverChallengeIsCacheable(challenge)) {
|
|
1285
|
+
cacheWrite = { written: false, reason: "not_cacheable" };
|
|
1286
|
+
} else if (solution.form !== "cookies" || solutionExpiryMs(solution) === undefined) {
|
|
1287
|
+
cacheWrite = { written: false, reason: "no_expires" };
|
|
1288
|
+
} else {
|
|
1289
|
+
const issuingIdentity =
|
|
1290
|
+
adapter.getIssuingIdentity?.(solution, identity, challenge) ??
|
|
1291
|
+
resolverChallengeIssuingIdentity(challenge, {
|
|
1292
|
+
...(identity ? { proxyUrl: identity.proxyUrl } : {}),
|
|
1293
|
+
userAgent: solution.userAgent,
|
|
1294
|
+
});
|
|
1295
|
+
cacheWrite = issuingIdentity
|
|
1296
|
+
? await cacheResolverSolution(
|
|
1297
|
+
cache,
|
|
1298
|
+
challenge,
|
|
1299
|
+
solution,
|
|
1300
|
+
issuingIdentity,
|
|
1301
|
+
options.identityScope,
|
|
1302
|
+
)
|
|
1303
|
+
: { written: false, reason: "not_cacheable" };
|
|
1304
|
+
}
|
|
1305
|
+
options.telemetry?.recordCacheWrite(cacheWrite);
|
|
1306
|
+
} catch (error) {
|
|
1307
|
+
options.telemetry?.recordCacheWrite({ written: false, reason: "error" });
|
|
1308
|
+
throw error;
|
|
987
1309
|
}
|
|
988
1310
|
}
|
|
989
1311
|
rememberSolutionSource(solution, "vendor");
|
|
1312
|
+
// Adapter success includes SBSD payload acceptance with verified:false.
|
|
1313
|
+
// The caller reports protected-refetch verification separately.
|
|
1314
|
+
terminalOutcome = "solved";
|
|
990
1315
|
return solution;
|
|
991
|
-
} catch (error) {
|
|
992
|
-
signal.throwIfAborted();
|
|
993
|
-
if (!(error instanceof ResolverVendorUnavailableError)) throw error;
|
|
994
|
-
// Every vendor-unavailable result, including missing_challenge_input, falls
|
|
995
|
-
// through so another adapter can solve with a different input contract.
|
|
996
|
-
attempts.push(unavailableAttempt(error));
|
|
997
1316
|
}
|
|
998
|
-
}
|
|
999
1317
|
|
|
1000
|
-
|
|
1318
|
+
throwExhausted(challenge.kind, attempts);
|
|
1319
|
+
} catch (error) {
|
|
1320
|
+
terminalOutcome = signal.aborted
|
|
1321
|
+
? "aborted"
|
|
1322
|
+
: error instanceof ProviderError && error.code === "RESOLVER_CHAIN_EXHAUSTED"
|
|
1323
|
+
? "exhausted"
|
|
1324
|
+
: "error";
|
|
1325
|
+
throw error;
|
|
1326
|
+
} finally {
|
|
1327
|
+
options.telemetry?.recordOutcome({
|
|
1328
|
+
outcome: terminalOutcome,
|
|
1329
|
+
solveMs: Date.now() - solveStartedAt,
|
|
1330
|
+
challengeKind: challenge.kind,
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1001
1333
|
},
|
|
1002
1334
|
};
|
|
1003
|
-
resolverCaches.set(client,
|
|
1335
|
+
resolverCaches.set(client, cache ?? null);
|
|
1336
|
+
const reusableOptions = { ...options, telemetry: undefined };
|
|
1337
|
+
registerResolverTelemetryBinding(client, createChainTelemetryBinding(reusableOptions));
|
|
1004
1338
|
return client;
|
|
1005
1339
|
}
|
|
1006
1340
|
|
|
@@ -1015,6 +1349,7 @@ export function createResolverClient(options: {
|
|
|
1015
1349
|
readonly createTransport?: ResolverRuntimeOptions["createTransport"];
|
|
1016
1350
|
readonly clientProfile?: string;
|
|
1017
1351
|
readonly allowedHosts?: readonly string[];
|
|
1352
|
+
readonly telemetry?: ResolverTelemetrySink;
|
|
1018
1353
|
}): ResolverChainClient {
|
|
1019
1354
|
return createResolverChainClient({
|
|
1020
1355
|
kinds: options.kinds,
|
|
@@ -1031,6 +1366,7 @@ export function createResolverClient(options: {
|
|
|
1031
1366
|
createTransport: options.createTransport,
|
|
1032
1367
|
clientProfile: options.clientProfile,
|
|
1033
1368
|
allowedHosts: options.allowedHosts,
|
|
1369
|
+
telemetry: options.telemetry,
|
|
1034
1370
|
});
|
|
1035
1371
|
}
|
|
1036
1372
|
|
|
@@ -1058,7 +1394,9 @@ function resolveVendorAvailability(
|
|
|
1058
1394
|
? APIFUSE__RESOLVER__2CAPTCHA__API_KEY
|
|
1059
1395
|
: vendor === "capsolver"
|
|
1060
1396
|
? APIFUSE__RESOLVER__CAPSOLVER__API_KEY
|
|
1061
|
-
:
|
|
1397
|
+
: vendor === "capmonster"
|
|
1398
|
+
? APIFUSE__RESOLVER__CAPMONSTER__API_KEY
|
|
1399
|
+
: APIFUSE__RESOLVER__HYPERSOLUTIONS__API_KEY;
|
|
1062
1400
|
|
|
1063
1401
|
const configuration = normalizedEnvValue(env, envKey);
|
|
1064
1402
|
return configuration
|
|
@@ -1083,6 +1421,7 @@ export function bindResolverSignal(
|
|
|
1083
1421
|
if (resolverCaches.has(resolver)) {
|
|
1084
1422
|
resolverCaches.set(boundResolver, resolverCaches.get(resolver) ?? null);
|
|
1085
1423
|
}
|
|
1424
|
+
inheritResolverTelemetryBinding(resolver, boundResolver);
|
|
1086
1425
|
return boundResolver;
|
|
1087
1426
|
}
|
|
1088
1427
|
|
|
@@ -1103,6 +1442,7 @@ function createResolverClientFromEnvInternal(
|
|
|
1103
1442
|
kinds: config.kinds,
|
|
1104
1443
|
entries: [],
|
|
1105
1444
|
unavailableReason: "Provider resolver vendor chain is empty",
|
|
1445
|
+
telemetry: options.telemetry,
|
|
1106
1446
|
});
|
|
1107
1447
|
}
|
|
1108
1448
|
|
|
@@ -1135,6 +1475,7 @@ function createResolverClientFromEnvInternal(
|
|
|
1135
1475
|
createTransport: options.createTransport,
|
|
1136
1476
|
clientProfile: config.clientProfile,
|
|
1137
1477
|
allowedHosts,
|
|
1478
|
+
telemetry: options.telemetry,
|
|
1138
1479
|
});
|
|
1139
1480
|
}
|
|
1140
1481
|
|