@apifuse/provider-sdk 2.2.0-beta.58 → 2.2.0-beta.60
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 +25 -0
- package/CHANGELOG.md +8 -0
- package/README.md +18 -2
- package/bin/apifuse-dev.ts +2 -1
- package/bin/apifuse-pack-types.ts +49 -0
- package/bin/apifuse-perf.ts +41 -4
- package/bin/apifuse-record.ts +9 -2
- package/dist/cli/templates/provider/README.md.tpl +15 -3
- package/dist/config/loader.d.ts +7 -0
- package/dist/config/loader.js +43 -18
- package/dist/declaration-validation.d.ts +2 -1
- package/dist/declaration-validation.js +39 -0
- package/dist/define.d.ts +4 -4
- package/dist/define.js +25 -1
- package/dist/engine.d.ts +16 -0
- package/dist/engine.js +59 -3
- package/dist/error-resolution.js +13 -0
- package/dist/index.d.ts +7 -4
- package/dist/index.js +4 -2
- package/dist/lint.js +38 -17
- 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/browser.js +2 -1
- package/dist/runtime/cache.d.ts +2 -0
- package/dist/runtime/cache.js +11 -3
- package/dist/runtime/diagnostic-env-values.d.ts +5 -0
- package/dist/runtime/diagnostic-env-values.js +57 -0
- package/dist/runtime/diagnostic-env.d.ts +11 -0
- package/dist/runtime/diagnostic-env.js +35 -0
- package/dist/runtime/diagnostic-redactor.d.ts +49 -0
- package/dist/runtime/diagnostic-redactor.js +569 -0
- package/dist/runtime/egress-lease.d.ts +65 -0
- package/dist/runtime/egress-lease.js +236 -0
- package/dist/runtime/env.js +2 -1
- package/dist/runtime/http.js +1 -0
- package/dist/runtime/instrumentation.js +3 -0
- package/dist/runtime/ocr.js +2 -1
- package/dist/runtime/otlp.js +5 -4
- package/dist/runtime/proxy-nodemaven.js +3 -2
- package/dist/runtime/proxy-retry-policy.d.ts +4 -1
- package/dist/runtime/proxy-retry-policy.js +11 -2
- package/dist/runtime/request-options.d.ts +7 -0
- package/dist/runtime/request-options.js +62 -22
- 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/browser.js +0 -1
- 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 +400 -111
- 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/runtime/stt.js +3 -2
- package/dist/runtime/trace.d.ts +5 -0
- package/dist/runtime/trace.js +115 -20
- package/dist/schema.d.ts +20 -0
- package/dist/schema.js +72 -2
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +1 -0
- package/dist/server/self-test-redaction.js +8 -33
- package/dist/server/self-test.d.ts +4 -0
- package/dist/server/self-test.js +10 -1
- package/dist/server/sensitive-values.d.ts +26 -0
- package/dist/server/sensitive-values.js +131 -0
- package/dist/server/serve-implementation.d.ts +7 -1
- package/dist/server/serve-implementation.js +394 -125
- package/dist/server/trace-output.d.ts +2 -1
- package/dist/server/trace-output.js +46 -14
- 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/trace-sanitization.d.ts +4 -3
- package/dist/trace-sanitization.js +45 -22
- package/dist/types.d.ts +106 -10
- package/package.json +2 -1
- package/src/cli/templates/provider/README.md.tpl +15 -3
- package/src/config/loader.ts +47 -18
- package/src/declaration-validation.ts +47 -1
- package/src/define.ts +45 -3
- package/src/engine.ts +78 -3
- package/src/error-resolution.ts +13 -0
- package/src/index.ts +34 -1
- package/src/lint.ts +44 -18
- package/src/provider.ts +10 -0
- package/src/runtime/bounded-expiring-map.ts +67 -0
- package/src/runtime/browser.ts +2 -1
- package/src/runtime/cache.ts +13 -3
- package/src/runtime/diagnostic-env-values.ts +56 -0
- package/src/runtime/diagnostic-env.ts +55 -0
- package/src/runtime/diagnostic-redactor.ts +662 -0
- package/src/runtime/egress-lease.ts +341 -0
- package/src/runtime/env.ts +2 -1
- package/src/runtime/http.ts +1 -0
- package/src/runtime/instrumentation.ts +3 -0
- package/src/runtime/ocr.ts +2 -1
- package/src/runtime/otlp.ts +5 -4
- package/src/runtime/proxy-nodemaven.ts +3 -2
- package/src/runtime/proxy-retry-policy.ts +12 -1
- package/src/runtime/request-options.ts +76 -28
- 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/browser.ts +0 -1
- 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 +468 -126
- package/src/runtime/stealth-akamai-sbsd.ts +207 -0
- package/src/runtime/stealth.ts +727 -95
- package/src/runtime/stt.ts +3 -2
- package/src/runtime/trace.ts +168 -26
- package/src/schema.ts +80 -2
- package/src/server/index.ts +21 -0
- package/src/server/self-test-redaction.ts +10 -40
- package/src/server/self-test.ts +13 -0
- package/src/server/sensitive-values.ts +200 -0
- package/src/server/serve-implementation.ts +561 -136
- package/src/server/trace-output.ts +77 -17
- package/src/server/types.ts +14 -0
- package/src/stealth/profiles.ts +10 -0
- package/src/testing/run.ts +6 -0
- package/src/trace-sanitization.ts +70 -23
- package/src/types.ts +124 -19
package/dist/runtime/resolver.js
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
|
+
import { readDiagnosticEnv } from "./diagnostic-env.js";
|
|
1
2
|
import { createHash } from "node:crypto";
|
|
2
3
|
import { resolveProxyConfigAsync, } from "../config/loader.js";
|
|
3
4
|
import { ProviderError } from "../errors.js";
|
|
4
5
|
import { getStealthProfile } from "../stealth/profiles.js";
|
|
5
|
-
import {
|
|
6
|
+
import { isProviderCacheBypassed } from "./cache.js";
|
|
7
|
+
import { RESOLVER_CHALLENGE_BINDINGS, resolverChallengeAllowsDirectCache, resolverChallengeIsCacheable, resolverChallengeIsIdentityScoped, resolverChallengeIssuingIdentity, } from "./resolver-vendors/bindings.js";
|
|
6
8
|
import { createBrowserResolverVendorAdapter } from "./resolver-vendors/browser.js";
|
|
7
9
|
import { createCapsolverResolverVendorAdapter } from "./resolver-vendors/capsolver.js";
|
|
10
|
+
import { createHypersolutionsResolverVendorAdapter } from "./resolver-vendors/hypersolutions.js";
|
|
8
11
|
import { assertResolverHostAllowed } from "./resolver-vendors/hosts.js";
|
|
9
12
|
import { createTwoCaptchaResolverVendorAdapter } from "./resolver-vendors/twocaptcha.js";
|
|
10
|
-
import { RESOLVER_VENDOR_CAPABILITIES, ResolverVendorUnavailableError, resolveProviderResolverVendors, resolverVendorSupports, } from "./resolver-vendors/types.js";
|
|
11
|
-
import { createUnsupportedResolverClient, RESOLVER_INSTRUMENTATION_METADATA, } from "./resolver-shared.js";
|
|
12
|
-
import { 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";
|
|
13
|
+
import { RESOLVER_VENDOR_CAPABILITIES, ResolverVendorUnavailableError, attachResolverVendorPollObserver, resolveProviderResolverVendors, resolverVendorSupports, } from "./resolver-vendors/types.js";
|
|
14
|
+
import { createUnsupportedResolverClient, inheritResolverTelemetryBinding, registerResolverTelemetryBinding, RESOLVER_INSTRUMENTATION_METADATA, } from "./resolver-shared.js";
|
|
15
|
+
import { APIFUSE__CDP_POOL__URL, APIFUSE__RESOLVER__2CAPTCHA__API_KEY, APIFUSE__RESOLVER__CAPMONSTER__API_KEY, APIFUSE__RESOLVER__CAPSOLVER__API_KEY, APIFUSE__RESOLVER__HYPERSOLUTIONS__API_KEY, APIFUSE__RESOLVER__TIMEOUT_MS, DEFAULT_RESOLVER_TIMEOUT_MS, kindRequiresClientProfile, } from "./resolver-config.js";
|
|
13
16
|
import { DEFAULT_STEALTH_PROFILE } from "./stealth.js";
|
|
14
17
|
export { createUnsupportedResolverClient, RESOLVER_INSTRUMENTATION_METADATA, } from "./resolver-shared.js";
|
|
15
|
-
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";
|
|
18
|
+
export { APIFUSE__CDP_POOL__URL, APIFUSE__RESOLVER__2CAPTCHA__API_KEY, APIFUSE__RESOLVER__CAPMONSTER__API_KEY, APIFUSE__RESOLVER__CAPSOLVER__API_KEY, APIFUSE__RESOLVER__HYPERSOLUTIONS__API_KEY, APIFUSE__RESOLVER__TIMEOUT_MS, DEFAULT_RESOLVER_TIMEOUT_MS, } from "./resolver-config.js";
|
|
16
19
|
export { DEFAULT_RESOLVER_VENDOR_PREFERENCE, resolveProviderResolverVendors, } from "./resolver-vendors/types.js";
|
|
17
20
|
const RESOLVER_SOLUTION_CACHE_NAMESPACE = "resolver-solution";
|
|
18
21
|
const RESOLVER_SOLUTION_INDEX_CACHE_NAMESPACE = "resolver-solution-index";
|
|
@@ -81,6 +84,13 @@ const resolverAdapterRegistry = {
|
|
|
81
84
|
timeoutMs,
|
|
82
85
|
});
|
|
83
86
|
},
|
|
87
|
+
hypersolutions(configuration, timeoutMs, allowedHosts) {
|
|
88
|
+
return createHypersolutionsResolverVendorAdapter({
|
|
89
|
+
allowedHosts,
|
|
90
|
+
apiKey: configuration,
|
|
91
|
+
timeoutMs,
|
|
92
|
+
});
|
|
93
|
+
},
|
|
84
94
|
};
|
|
85
95
|
export const RESOLVER_ADAPTER_REGISTRY = resolverAdapterRegistry;
|
|
86
96
|
export function swapResolverAdapterFactoryForTests(vendor, factory) {
|
|
@@ -121,11 +131,11 @@ const KNOWN_UNIMPLEMENTED_RESOLVER_VENDORS = new Set([
|
|
|
121
131
|
"custom",
|
|
122
132
|
]);
|
|
123
133
|
function normalizedEnvValue(env, key) {
|
|
124
|
-
const value = env
|
|
134
|
+
const value = readDiagnosticEnv(key, env)?.trim();
|
|
125
135
|
return value ? value : undefined;
|
|
126
136
|
}
|
|
127
137
|
function readPositiveIntegerEnv(env, name) {
|
|
128
|
-
const raw = env
|
|
138
|
+
const raw = readDiagnosticEnv(name, env)?.trim();
|
|
129
139
|
if (!raw)
|
|
130
140
|
return undefined;
|
|
131
141
|
if (!/^[1-9]\d*$/.test(raw)) {
|
|
@@ -134,6 +144,12 @@ function readPositiveIntegerEnv(env, name) {
|
|
|
134
144
|
return raw;
|
|
135
145
|
}
|
|
136
146
|
function assertDeclaredKind(requestedKind, declaredKinds) {
|
|
147
|
+
if (!Object.hasOwn(RESOLVER_CHALLENGE_BINDINGS, requestedKind)) {
|
|
148
|
+
throw new ProviderError(`Unknown resolver kind "${requestedKind}"`, {
|
|
149
|
+
code: "RESOLVER_KIND_NOT_DECLARED",
|
|
150
|
+
fix: "Use a challenge kind exported by @apifuse/provider-sdk.",
|
|
151
|
+
});
|
|
152
|
+
}
|
|
137
153
|
if (declaredKinds.includes(requestedKind))
|
|
138
154
|
return;
|
|
139
155
|
const declared = declaredKinds.length > 0 ? declaredKinds.join(", ") : "none";
|
|
@@ -179,19 +195,20 @@ function throwUnsupportedKind(kind, usingDefaultVendors) {
|
|
|
179
195
|
: `Add a resolver vendor that supports "${kind}" to the provider's resolver.vendors declaration.`,
|
|
180
196
|
});
|
|
181
197
|
}
|
|
182
|
-
function throwExhausted(attempts) {
|
|
198
|
+
function throwExhausted(challengeKind, attempts, executedAttemptCount = attempts.length) {
|
|
183
199
|
const hasMissingChallengeInput = attempts.some((attempt) => attempt.reason === "missing_challenge_input");
|
|
184
|
-
|
|
185
|
-
.map(({ vendor, reason, missingFields }) => missingFields === undefined || missingFields.length === 0
|
|
186
|
-
? `${vendor}: ${reason}`
|
|
187
|
-
: `${vendor}: ${reason} (missing fields: ${missingFields.join(", ")})`)
|
|
188
|
-
.join(", ");
|
|
189
|
-
throw new ProviderError(`Resolver vendor chain exhausted: ${summary}`, {
|
|
200
|
+
throw new ProviderError("The challenge could not be resolved.", {
|
|
190
201
|
code: "RESOLVER_CHAIN_EXHAUSTED",
|
|
191
202
|
fix: hasMissingChallengeInput
|
|
192
|
-
? "Capture
|
|
193
|
-
: "
|
|
194
|
-
|
|
203
|
+
? "Capture all required challenge fields and retry the request."
|
|
204
|
+
: "Retry the request or contact support if the challenge continues.",
|
|
205
|
+
retryable: false,
|
|
206
|
+
details: {
|
|
207
|
+
challengeKind,
|
|
208
|
+
attempts: executedAttemptCount,
|
|
209
|
+
outcome: "exhausted",
|
|
210
|
+
retryable: false,
|
|
211
|
+
},
|
|
195
212
|
});
|
|
196
213
|
}
|
|
197
214
|
function assertClientProfileTransportContract(clientProfile, transport) {
|
|
@@ -245,6 +262,16 @@ function sanitizeCauseMessage(message) {
|
|
|
245
262
|
}
|
|
246
263
|
function restrictResolverTransport(transport, allowedHosts) {
|
|
247
264
|
return {
|
|
265
|
+
sessionHeaders: transport.sessionHeaders,
|
|
266
|
+
...(transport.getCookie
|
|
267
|
+
? {
|
|
268
|
+
getCookie(name, url) {
|
|
269
|
+
// Jar reads are gated like fetches: an adapter may only read state for declared hosts.
|
|
270
|
+
assertResolverHostAllowed(url, allowedHosts);
|
|
271
|
+
return transport.getCookie?.(name, url);
|
|
272
|
+
},
|
|
273
|
+
}
|
|
274
|
+
: {}),
|
|
248
275
|
async fetch(url, init) {
|
|
249
276
|
// Empty declarations remain deny-by-default, matching the adapter-factory/browser path.
|
|
250
277
|
assertResolverHostAllowed(url, allowedHosts);
|
|
@@ -286,10 +313,17 @@ function safeUpstreamHost(host) {
|
|
|
286
313
|
function safePhase(phase) {
|
|
287
314
|
return phase !== undefined && /^[a-z\d_.:-]{1,64}$/i.test(phase) ? phase : undefined;
|
|
288
315
|
}
|
|
289
|
-
function unavailableAttempt(error) {
|
|
290
|
-
|
|
316
|
+
function unavailableAttempt(error, forTelemetry = false) {
|
|
317
|
+
// Preserve the full input for the collector's redact-then-bound path. Span
|
|
318
|
+
// attributes retain their existing sanitization and phase validation.
|
|
319
|
+
const cause = forTelemetry && error.cause !== undefined
|
|
320
|
+
? {
|
|
321
|
+
name: error.cause instanceof Error ? error.cause.name : "Error",
|
|
322
|
+
message: error.cause instanceof Error ? error.cause.message : String(error.cause),
|
|
323
|
+
}
|
|
324
|
+
: safeCause(error);
|
|
291
325
|
const upstreamHost = safeUpstreamHost(error.upstreamHost);
|
|
292
|
-
const phase = safePhase(error.phase);
|
|
326
|
+
const phase = forTelemetry ? error.phase : safePhase(error.phase);
|
|
293
327
|
const round = Number.isSafeInteger(error.round) && error.round > 0 ? error.round : undefined;
|
|
294
328
|
return {
|
|
295
329
|
vendor: error.vendor,
|
|
@@ -313,6 +347,103 @@ function unavailableSpanAttributes(error) {
|
|
|
313
347
|
transport_round: attempt.round,
|
|
314
348
|
};
|
|
315
349
|
}
|
|
350
|
+
function resolverTelemetryPhase(value) {
|
|
351
|
+
return value === "create_task" ||
|
|
352
|
+
value === "poll_result" ||
|
|
353
|
+
value === "cleanup" ||
|
|
354
|
+
value === "measure_ip" ||
|
|
355
|
+
value === "fetch_script" ||
|
|
356
|
+
value === "generate_payload" ||
|
|
357
|
+
value === "post_payload"
|
|
358
|
+
? value
|
|
359
|
+
: undefined;
|
|
360
|
+
}
|
|
361
|
+
function diagnosticString(value) {
|
|
362
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
363
|
+
}
|
|
364
|
+
function diagnosticCount(value) {
|
|
365
|
+
return typeof value === "number" && Number.isFinite(value)
|
|
366
|
+
? Math.max(0, Math.floor(value))
|
|
367
|
+
: undefined;
|
|
368
|
+
}
|
|
369
|
+
function createResolverTelemetryTraceRecorder(input) {
|
|
370
|
+
if (!input.base && !input.telemetry)
|
|
371
|
+
return undefined;
|
|
372
|
+
const recorder = {
|
|
373
|
+
async runSpan(name, fn, spanOptions = {}) {
|
|
374
|
+
const adapterPhase = name.startsWith("resolver.vendor.")
|
|
375
|
+
? diagnosticString(name.replace("resolver.vendor.", ""))
|
|
376
|
+
: undefined;
|
|
377
|
+
const phase = resolverTelemetryPhase(adapterPhase);
|
|
378
|
+
const startedAt = Date.now();
|
|
379
|
+
let attributes;
|
|
380
|
+
let spanOutcome = "ok";
|
|
381
|
+
const wrappedOptions = {
|
|
382
|
+
...spanOptions,
|
|
383
|
+
onSuccess(value) {
|
|
384
|
+
attributes = spanOptions.onSuccess?.(value);
|
|
385
|
+
return attributes;
|
|
386
|
+
},
|
|
387
|
+
onError(error) {
|
|
388
|
+
attributes = spanOptions.onError?.(error);
|
|
389
|
+
return attributes;
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
try {
|
|
393
|
+
if (input.base)
|
|
394
|
+
return await input.base.runSpan(name, fn, wrappedOptions);
|
|
395
|
+
const value = await fn();
|
|
396
|
+
attributes = spanOptions.onSuccess?.(value);
|
|
397
|
+
return value;
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
spanOutcome = "error";
|
|
401
|
+
if (!input.base)
|
|
402
|
+
attributes = spanOptions.onError?.(error);
|
|
403
|
+
throw error;
|
|
404
|
+
}
|
|
405
|
+
finally {
|
|
406
|
+
if (adapterPhase && adapterPhase !== "attempt") {
|
|
407
|
+
const reportedPhase = diagnosticString(attributes?.transport_phase) ?? adapterPhase;
|
|
408
|
+
const pollCount = diagnosticCount(attributes?.poll_count);
|
|
409
|
+
const vendorErrorCode = diagnosticString(attributes?.vendor_error_code);
|
|
410
|
+
const vendorErrorDescription = diagnosticString(attributes?.vendor_error_description ?? attributes?.error_message);
|
|
411
|
+
if (phase === "cleanup") {
|
|
412
|
+
input.telemetry?.recordVendorAttempt({
|
|
413
|
+
vendor: input.vendor,
|
|
414
|
+
phase: "cleanup",
|
|
415
|
+
diagnostics: { phase: reportedPhase, attemptIndex: input.attemptIndex },
|
|
416
|
+
outcome: spanOutcome,
|
|
417
|
+
ms: Date.now() - startedAt,
|
|
418
|
+
...(pollCount === undefined ? {} : { pollCount }),
|
|
419
|
+
...(vendorErrorCode ? { vendorErrorCode } : {}),
|
|
420
|
+
...(vendorErrorDescription ? { vendorErrorDescription } : {}),
|
|
421
|
+
...(spanOutcome === "error" ? { errorClass: "unexpected" } : {}),
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
input.diagnostics.phase = reportedPhase;
|
|
426
|
+
if (pollCount !== undefined)
|
|
427
|
+
input.diagnostics.pollCount = pollCount;
|
|
428
|
+
if (vendorErrorCode)
|
|
429
|
+
input.diagnostics.vendorErrorCode = vendorErrorCode;
|
|
430
|
+
if (vendorErrorDescription) {
|
|
431
|
+
input.diagnostics.vendorErrorDescription = vendorErrorDescription;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
};
|
|
438
|
+
return attachResolverVendorPollObserver(recorder, () => {
|
|
439
|
+
input.diagnostics.pollCount = (input.diagnostics.pollCount ?? 0) + 1;
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
function resolverAttemptErrorClass(error, signal) {
|
|
443
|
+
if (signal.aborted)
|
|
444
|
+
return "aborted";
|
|
445
|
+
return error instanceof ResolverVendorUnavailableError ? error.reason : "unexpected";
|
|
446
|
+
}
|
|
316
447
|
function challengeOrigin(challenge) {
|
|
317
448
|
return new URL(challenge.pageUrl).origin;
|
|
318
449
|
}
|
|
@@ -448,27 +579,27 @@ async function writeResolverCacheIndex(cache, challenge, entries, now) {
|
|
|
448
579
|
}
|
|
449
580
|
async function cacheResolverSolution(cache, challenge, solution, identity, identityScope) {
|
|
450
581
|
if (!resolverChallengeIsCacheable(challenge))
|
|
451
|
-
return;
|
|
582
|
+
return { written: false, reason: "not_cacheable" };
|
|
452
583
|
const expiresAtMs = solutionExpiryMs(solution);
|
|
453
584
|
const now = Date.now();
|
|
454
585
|
if (expiresAtMs === undefined)
|
|
455
|
-
return;
|
|
586
|
+
return { written: false, reason: "no_expires" };
|
|
456
587
|
const ttlMs = Math.floor(expiresAtMs - now);
|
|
457
588
|
if (ttlMs <= MIN_RESOLVER_CACHE_TTL_MS)
|
|
458
|
-
return;
|
|
589
|
+
return { written: false, reason: "no_expires" };
|
|
459
590
|
const scopedDigest = identityScope !== undefined && resolverChallengeIsIdentityScoped(challenge)
|
|
460
591
|
? resolverIdentityScopeDigest(identityScope)
|
|
461
592
|
: undefined;
|
|
462
593
|
if (!resolverChallengeAllowsDirectCache(challenge) &&
|
|
463
594
|
scopedDigest === undefined &&
|
|
464
595
|
identity.proxyUrl === undefined) {
|
|
465
|
-
return;
|
|
596
|
+
return { written: false, reason: "not_cacheable" };
|
|
466
597
|
}
|
|
467
598
|
const issuerDigest = scopedDigest ?? resolverIdentityDigest(identity);
|
|
468
599
|
await cache.set(resolverSolutionCacheKey(cache, challenge, issuerDigest), { expiresAtMs, issuerDigest, solution }, { ttlMs });
|
|
469
600
|
rememberSolutionIssuer(solution, issuerDigest);
|
|
470
601
|
if (scopedDigest !== undefined || identity.proxyUrl !== undefined)
|
|
471
|
-
return;
|
|
602
|
+
return { written: true };
|
|
472
603
|
const indexKey = resolverSolutionIndexCacheKey(cache, challenge);
|
|
473
604
|
const current = await cache.get(indexKey);
|
|
474
605
|
const currentEntries = isResolverCacheIndex(current?.value) ? current.value.entries : [];
|
|
@@ -476,6 +607,7 @@ async function cacheResolverSolution(cache, challenge, solution, identity, ident
|
|
|
476
607
|
...currentEntries.filter((entry) => entry.issuerDigest !== issuerDigest),
|
|
477
608
|
{ direct: true, expiresAtMs, issuerDigest },
|
|
478
609
|
], now);
|
|
610
|
+
return { written: true };
|
|
479
611
|
}
|
|
480
612
|
async function invalidateResolverSolutionWithOutcome(resolver, challenge, solution) {
|
|
481
613
|
const metadata = resolver[RESOLVER_INSTRUMENTATION_METADATA];
|
|
@@ -566,107 +698,258 @@ async function resolveResolverIdentity(proxyIntent) {
|
|
|
566
698
|
return { unavailableReason: "missing_client_profile", userAgentSource };
|
|
567
699
|
}
|
|
568
700
|
}
|
|
701
|
+
function createChainTelemetryBinding(options) {
|
|
702
|
+
return (telemetry) => createResolverChainClient({ ...options, telemetry });
|
|
703
|
+
}
|
|
569
704
|
function createResolverChainClient(options) {
|
|
570
705
|
assertClientProfileTransportContract(options.clientProfile, options.transport);
|
|
706
|
+
const cache = options.cache && !isProviderCacheBypassed(options.cache) ? options.cache : undefined;
|
|
571
707
|
const client = {
|
|
572
708
|
async solve(challenge, signal = new AbortController().signal, traceRecorder) {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
709
|
+
const solveStartedAt = Date.now();
|
|
710
|
+
let terminalOutcome = "error";
|
|
711
|
+
try {
|
|
712
|
+
assertDeclaredKind(challenge.kind, options.kinds);
|
|
713
|
+
if (options.unavailableReason) {
|
|
714
|
+
throw new ProviderError(options.unavailableReason, {
|
|
715
|
+
code: "RESOLVER_UNAVAILABLE",
|
|
716
|
+
fix: "Configure at least one declared resolver vendor or provide a test ResolverContext override.",
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
const supportingEntries = options.entries.filter((entry) => entry.supports(challenge.kind));
|
|
720
|
+
if (supportingEntries.length === 0) {
|
|
721
|
+
throwUnsupportedKind(challenge.kind, options.usingDefaultVendors ?? false);
|
|
722
|
+
}
|
|
723
|
+
if (kindRequiresClientProfile(challenge.kind) && !options.clientProfile?.trim()) {
|
|
724
|
+
options.telemetry?.recordIdentity({ source: "none", failure: "missing_client_profile" });
|
|
725
|
+
throwExhausted(challenge.kind, supportingEntries.map((entry) => ({
|
|
726
|
+
vendor: entry.id,
|
|
727
|
+
reason: "missing_client_profile",
|
|
728
|
+
})), 0);
|
|
729
|
+
}
|
|
730
|
+
signal.throwIfAborted();
|
|
731
|
+
const identityResolution = options.proxyIntent
|
|
732
|
+
? await resolveResolverIdentity(options.proxyIntent)
|
|
733
|
+
: { identity: options.identity };
|
|
734
|
+
const identity = identityResolution.identity;
|
|
735
|
+
options.telemetry?.recordIdentity({
|
|
736
|
+
// For SBSD, "declared" identifies the configured client profile and its bound
|
|
737
|
+
// session identity; the engine independently supplies solver credentials.
|
|
738
|
+
source: kindRequiresClientProfile(challenge.kind) && options.clientProfile
|
|
739
|
+
? "declared"
|
|
740
|
+
: options.proxyIntent
|
|
741
|
+
? (identityResolution.userAgentSource ?? "defaulted")
|
|
742
|
+
: identity
|
|
743
|
+
? "declared"
|
|
744
|
+
: "none",
|
|
745
|
+
...(identityResolution.unavailableReason
|
|
746
|
+
? { failure: identityResolution.unavailableReason }
|
|
747
|
+
: {}),
|
|
578
748
|
});
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
throwExhausted(supportingEntries.map((entry) => ({
|
|
596
|
-
vendor: entry.id,
|
|
597
|
-
reason: identityResolution.unavailableReason ?? "missing_proxy_identity",
|
|
598
|
-
})));
|
|
599
|
-
}
|
|
600
|
-
if (options.cache && resolverChallengeIsCacheable(challenge)) {
|
|
601
|
-
const cached = await findCachedSolution(options.cache, challenge, identity, options.identityScope);
|
|
602
|
-
if (cached)
|
|
603
|
-
return cached;
|
|
604
|
-
}
|
|
605
|
-
const attempts = [];
|
|
606
|
-
for (const entry of supportingEntries) {
|
|
607
|
-
const adapter = entry.createAdapter();
|
|
608
|
-
try {
|
|
609
|
-
const solveAttempt = () => {
|
|
610
|
-
const requiresTransport = adapterRequiresTransport(adapter, challenge.kind);
|
|
611
|
-
const unrestrictedTransport = options.transport ??
|
|
612
|
-
(requiresTransport
|
|
613
|
-
? options.createTransport?.({
|
|
614
|
-
clientProfile: options.clientProfile,
|
|
615
|
-
identityScope: options.identityScope,
|
|
616
|
-
})
|
|
617
|
-
: undefined);
|
|
618
|
-
if (requiresTransport && unrestrictedTransport === undefined) {
|
|
619
|
-
throw new ResolverVendorUnavailableError(adapter.id, "missing_transport");
|
|
749
|
+
signal.throwIfAborted();
|
|
750
|
+
const requiredProxyIdentityMissing = options.proxyIntent?.mode === "required" && identity === undefined;
|
|
751
|
+
if (requiredProxyIdentityMissing) {
|
|
752
|
+
const reason = identityResolution.unavailableReason ?? "missing_proxy_identity";
|
|
753
|
+
for (const [entryIndex, entry] of supportingEntries.entries()) {
|
|
754
|
+
options.telemetry?.recordVendorAttempt({
|
|
755
|
+
vendor: entry.id,
|
|
756
|
+
phase: "create_task",
|
|
757
|
+
diagnostics: { attemptIndex: entryIndex + 1 },
|
|
758
|
+
outcome: "error",
|
|
759
|
+
ms: 0,
|
|
760
|
+
errorClass: reason,
|
|
761
|
+
});
|
|
762
|
+
const next = supportingEntries[entryIndex + 1];
|
|
763
|
+
if (next) {
|
|
764
|
+
options.telemetry?.recordFailover({ from: entry.id, to: next.id, reason });
|
|
620
765
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
};
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
766
|
+
}
|
|
767
|
+
throwExhausted(challenge.kind, supportingEntries.map((entry) => ({
|
|
768
|
+
vendor: entry.id,
|
|
769
|
+
reason,
|
|
770
|
+
})));
|
|
771
|
+
}
|
|
772
|
+
if (!cache) {
|
|
773
|
+
options.telemetry?.recordCacheRead({ status: "disabled", challengeKind: challenge.kind });
|
|
774
|
+
}
|
|
775
|
+
else if (!resolverChallengeIsCacheable(challenge)) {
|
|
776
|
+
options.telemetry?.recordCacheRead({
|
|
777
|
+
status: "not_cacheable",
|
|
778
|
+
challengeKind: challenge.kind,
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
else {
|
|
782
|
+
const cached = await findCachedSolution(cache, challenge, identity, options.identityScope);
|
|
783
|
+
options.telemetry?.recordCacheRead({
|
|
784
|
+
status: cached ? "hit" : "miss",
|
|
785
|
+
challengeKind: challenge.kind,
|
|
786
|
+
});
|
|
787
|
+
if (cached) {
|
|
788
|
+
terminalOutcome = "cached";
|
|
789
|
+
return cached;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
const attempts = [];
|
|
793
|
+
for (const [entryIndex, entry] of supportingEntries.entries()) {
|
|
794
|
+
const adapter = entry.createAdapter();
|
|
795
|
+
const attemptStartedAt = Date.now();
|
|
796
|
+
const diagnostics = {};
|
|
797
|
+
const telemetryTraceRecorder = createResolverTelemetryTraceRecorder({
|
|
798
|
+
base: traceRecorder,
|
|
799
|
+
vendor: adapter.id,
|
|
800
|
+
attemptIndex: entryIndex + 1,
|
|
801
|
+
telemetry: options.telemetry,
|
|
802
|
+
diagnostics,
|
|
803
|
+
});
|
|
804
|
+
let solution;
|
|
805
|
+
try {
|
|
806
|
+
const solveAttempt = () => {
|
|
807
|
+
const requiresTransport = adapterRequiresTransport(adapter, challenge.kind);
|
|
808
|
+
const unrestrictedTransport = options.transport ??
|
|
809
|
+
(requiresTransport
|
|
810
|
+
? options.createTransport?.({
|
|
811
|
+
clientProfile: options.clientProfile,
|
|
812
|
+
identityScope: options.identityScope,
|
|
813
|
+
})
|
|
814
|
+
: undefined);
|
|
815
|
+
if (requiresTransport && unrestrictedTransport === undefined) {
|
|
816
|
+
throw new ResolverVendorUnavailableError(adapter.id, "missing_transport");
|
|
817
|
+
}
|
|
818
|
+
const transport = unrestrictedTransport
|
|
819
|
+
? restrictResolverTransport(unrestrictedTransport, [
|
|
820
|
+
...(options.allowedHosts ?? []),
|
|
821
|
+
...(adapter.transportAllowedHosts ?? []),
|
|
822
|
+
])
|
|
823
|
+
: undefined;
|
|
824
|
+
const usage = {
|
|
825
|
+
// Position in the declared chain, not in the kind-filtered list.
|
|
826
|
+
vendorIndex: options.entries.indexOf(entry) + 1,
|
|
827
|
+
...(options.identityScope
|
|
828
|
+
? {
|
|
829
|
+
resolverIdentityScope: createHash("sha256")
|
|
830
|
+
.update(options.identityScope, "utf8")
|
|
831
|
+
.digest("hex"),
|
|
832
|
+
}
|
|
833
|
+
: {}),
|
|
834
|
+
};
|
|
835
|
+
return adapter.solve(challenge, identity, signal, telemetryTraceRecorder, transport, usage);
|
|
836
|
+
};
|
|
837
|
+
solution = traceRecorder
|
|
838
|
+
? await traceRecorder.runSpan("resolver.vendor.attempt", solveAttempt, {
|
|
839
|
+
attributes: {
|
|
840
|
+
vendor: adapter.id,
|
|
841
|
+
challenge_kind: challenge.kind,
|
|
842
|
+
client_profile: options.clientProfile,
|
|
843
|
+
resolver_identity_source: identityResolution.userAgentSource,
|
|
844
|
+
},
|
|
845
|
+
onError(error) {
|
|
846
|
+
return error instanceof ResolverVendorUnavailableError
|
|
847
|
+
? unavailableSpanAttributes(error)
|
|
848
|
+
: undefined;
|
|
849
|
+
},
|
|
850
|
+
})
|
|
851
|
+
: await solveAttempt();
|
|
852
|
+
}
|
|
853
|
+
catch (error) {
|
|
854
|
+
const unavailable = error instanceof ResolverVendorUnavailableError ? error : undefined;
|
|
855
|
+
const attempt = unavailable ? unavailableAttempt(unavailable, true) : undefined;
|
|
856
|
+
options.telemetry?.recordVendorAttempt({
|
|
857
|
+
vendor: adapter.id,
|
|
858
|
+
phase: resolverTelemetryPhase(unavailable?.phase) ??
|
|
859
|
+
resolverTelemetryPhase(diagnostics.phase) ??
|
|
860
|
+
"create_task",
|
|
861
|
+
diagnostics: {
|
|
862
|
+
...attempt,
|
|
863
|
+
attemptIndex: entryIndex + 1,
|
|
864
|
+
phase: attempt?.phase ?? diagnostics.phase,
|
|
638
865
|
},
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
866
|
+
outcome: "error",
|
|
867
|
+
ms: Date.now() - attemptStartedAt,
|
|
868
|
+
errorClass: resolverAttemptErrorClass(error, signal),
|
|
869
|
+
...(diagnostics.pollCount === undefined ? {} : { pollCount: diagnostics.pollCount }),
|
|
870
|
+
...(diagnostics.vendorErrorCode
|
|
871
|
+
? { vendorErrorCode: diagnostics.vendorErrorCode }
|
|
872
|
+
: {}),
|
|
873
|
+
...(diagnostics.vendorErrorDescription
|
|
874
|
+
? { vendorErrorDescription: diagnostics.vendorErrorDescription }
|
|
875
|
+
: {}),
|
|
876
|
+
});
|
|
877
|
+
signal.throwIfAborted();
|
|
878
|
+
if (!unavailable || !attempt)
|
|
879
|
+
throw error;
|
|
880
|
+
attempts.push(attempt);
|
|
881
|
+
const next = supportingEntries[entryIndex + 1];
|
|
882
|
+
if (next) {
|
|
883
|
+
options.telemetry?.recordFailover({
|
|
884
|
+
from: adapter.id,
|
|
885
|
+
to: next.id,
|
|
886
|
+
reason: unavailable.reason,
|
|
649
887
|
});
|
|
650
|
-
|
|
651
|
-
|
|
888
|
+
}
|
|
889
|
+
continue;
|
|
890
|
+
}
|
|
891
|
+
options.telemetry?.recordVendorAttempt({
|
|
892
|
+
vendor: adapter.id,
|
|
893
|
+
phase: resolverTelemetryPhase(diagnostics.phase) ?? "poll_result",
|
|
894
|
+
diagnostics: { attemptIndex: entryIndex + 1, phase: diagnostics.phase },
|
|
895
|
+
outcome: "ok",
|
|
896
|
+
ms: Date.now() - attemptStartedAt,
|
|
897
|
+
...(diagnostics.pollCount === undefined ? {} : { pollCount: diagnostics.pollCount }),
|
|
898
|
+
});
|
|
899
|
+
if (cache) {
|
|
900
|
+
try {
|
|
901
|
+
let cacheWrite;
|
|
902
|
+
if (!resolverChallengeIsCacheable(challenge)) {
|
|
903
|
+
cacheWrite = { written: false, reason: "not_cacheable" };
|
|
904
|
+
}
|
|
905
|
+
else if (solution.form !== "cookies" || solutionExpiryMs(solution) === undefined) {
|
|
906
|
+
cacheWrite = { written: false, reason: "no_expires" };
|
|
907
|
+
}
|
|
908
|
+
else {
|
|
909
|
+
const issuingIdentity = adapter.getIssuingIdentity?.(solution, identity, challenge) ??
|
|
910
|
+
resolverChallengeIssuingIdentity(challenge, {
|
|
911
|
+
...(identity ? { proxyUrl: identity.proxyUrl } : {}),
|
|
912
|
+
userAgent: solution.userAgent,
|
|
913
|
+
});
|
|
914
|
+
cacheWrite = issuingIdentity
|
|
915
|
+
? await cacheResolverSolution(cache, challenge, solution, issuingIdentity, options.identityScope)
|
|
916
|
+
: { written: false, reason: "not_cacheable" };
|
|
917
|
+
}
|
|
918
|
+
options.telemetry?.recordCacheWrite(cacheWrite);
|
|
919
|
+
}
|
|
920
|
+
catch (error) {
|
|
921
|
+
options.telemetry?.recordCacheWrite({ written: false, reason: "error" });
|
|
922
|
+
throw error;
|
|
652
923
|
}
|
|
653
924
|
}
|
|
654
925
|
rememberSolutionSource(solution, "vendor");
|
|
926
|
+
// Adapter success includes SBSD payload acceptance with verified:false.
|
|
927
|
+
// The caller reports protected-refetch verification separately.
|
|
928
|
+
terminalOutcome = "solved";
|
|
655
929
|
return solution;
|
|
656
930
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
931
|
+
throwExhausted(challenge.kind, attempts);
|
|
932
|
+
}
|
|
933
|
+
catch (error) {
|
|
934
|
+
terminalOutcome = signal.aborted
|
|
935
|
+
? "aborted"
|
|
936
|
+
: error instanceof ProviderError && error.code === "RESOLVER_CHAIN_EXHAUSTED"
|
|
937
|
+
? "exhausted"
|
|
938
|
+
: "error";
|
|
939
|
+
throw error;
|
|
940
|
+
}
|
|
941
|
+
finally {
|
|
942
|
+
options.telemetry?.recordOutcome({
|
|
943
|
+
outcome: terminalOutcome,
|
|
944
|
+
solveMs: Date.now() - solveStartedAt,
|
|
945
|
+
challengeKind: challenge.kind,
|
|
946
|
+
});
|
|
665
947
|
}
|
|
666
|
-
throwExhausted(attempts);
|
|
667
948
|
},
|
|
668
949
|
};
|
|
669
|
-
resolverCaches.set(client,
|
|
950
|
+
resolverCaches.set(client, cache ?? null);
|
|
951
|
+
const reusableOptions = { ...options, telemetry: undefined };
|
|
952
|
+
registerResolverTelemetryBinding(client, createChainTelemetryBinding(reusableOptions));
|
|
670
953
|
return client;
|
|
671
954
|
}
|
|
672
955
|
export function createResolverClient(options) {
|
|
@@ -685,6 +968,7 @@ export function createResolverClient(options) {
|
|
|
685
968
|
createTransport: options.createTransport,
|
|
686
969
|
clientProfile: options.clientProfile,
|
|
687
970
|
allowedHosts: options.allowedHosts,
|
|
971
|
+
telemetry: options.telemetry,
|
|
688
972
|
});
|
|
689
973
|
}
|
|
690
974
|
function resolveVendorAvailability(vendor, env) {
|
|
@@ -706,7 +990,9 @@ function resolveVendorAvailability(vendor, env) {
|
|
|
706
990
|
? APIFUSE__RESOLVER__2CAPTCHA__API_KEY
|
|
707
991
|
: vendor === "capsolver"
|
|
708
992
|
? APIFUSE__RESOLVER__CAPSOLVER__API_KEY
|
|
709
|
-
:
|
|
993
|
+
: vendor === "capmonster"
|
|
994
|
+
? APIFUSE__RESOLVER__CAPMONSTER__API_KEY
|
|
995
|
+
: APIFUSE__RESOLVER__HYPERSOLUTIONS__API_KEY;
|
|
710
996
|
const configuration = normalizedEnvValue(env, envKey);
|
|
711
997
|
return configuration
|
|
712
998
|
? { vendor, available: true, configuration }
|
|
@@ -723,6 +1009,7 @@ export function bindResolverSignal(resolver, defaultSignal) {
|
|
|
723
1009
|
if (resolverCaches.has(resolver)) {
|
|
724
1010
|
resolverCaches.set(boundResolver, resolverCaches.get(resolver) ?? null);
|
|
725
1011
|
}
|
|
1012
|
+
inheritResolverTelemetryBinding(resolver, boundResolver);
|
|
726
1013
|
return boundResolver;
|
|
727
1014
|
}
|
|
728
1015
|
function createResolverClientFromEnvInternal(config, env, options, adapterFactories) {
|
|
@@ -736,6 +1023,7 @@ function createResolverClientFromEnvInternal(config, env, options, adapterFactor
|
|
|
736
1023
|
kinds: config.kinds,
|
|
737
1024
|
entries: [],
|
|
738
1025
|
unavailableReason: "Provider resolver vendor chain is empty",
|
|
1026
|
+
telemetry: options.telemetry,
|
|
739
1027
|
});
|
|
740
1028
|
}
|
|
741
1029
|
const timeoutValue = readPositiveIntegerEnv(env, APIFUSE__RESOLVER__TIMEOUT_MS);
|
|
@@ -760,6 +1048,7 @@ function createResolverClientFromEnvInternal(config, env, options, adapterFactor
|
|
|
760
1048
|
createTransport: options.createTransport,
|
|
761
1049
|
clientProfile: config.clientProfile,
|
|
762
1050
|
allowedHosts,
|
|
1051
|
+
telemetry: options.telemetry,
|
|
763
1052
|
});
|
|
764
1053
|
}
|
|
765
1054
|
export function createResolverClientFromEnv(config, env = process.env, options = {}) {
|