@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
|
@@ -1,4 +1,61 @@
|
|
|
1
1
|
import { ProviderError } from "../errors.js";
|
|
2
|
+
const telemetryBindings = new WeakMap();
|
|
3
|
+
/** Internal contract: reconstruct a chain with a new request sink, without mutating the host chain. */
|
|
4
|
+
export function registerResolverTelemetryBinding(resolver, bind) {
|
|
5
|
+
telemetryBindings.set(resolver, bind);
|
|
6
|
+
}
|
|
7
|
+
/** Preserve reconstruction across SDK wrappers without capturing their current request sink. */
|
|
8
|
+
export function inheritResolverTelemetryBinding(resolver, wrapper) {
|
|
9
|
+
const bind = telemetryBindings.get(resolver);
|
|
10
|
+
if (bind)
|
|
11
|
+
telemetryBindings.set(wrapper, bind);
|
|
12
|
+
}
|
|
13
|
+
/** SDK chains expose each vendor invocation; opaque host resolvers expose one custom invocation. */
|
|
14
|
+
export function bindResolverTelemetry(resolver, sink) {
|
|
15
|
+
if (!sink)
|
|
16
|
+
return resolver;
|
|
17
|
+
const metadata = resolver[RESOLVER_INSTRUMENTATION_METADATA];
|
|
18
|
+
if (metadata)
|
|
19
|
+
return bindResolverTelemetry(metadata.target, sink);
|
|
20
|
+
const bind = telemetryBindings.get(resolver);
|
|
21
|
+
if (bind)
|
|
22
|
+
return bind(sink);
|
|
23
|
+
const wrapper = {
|
|
24
|
+
async solve(challenge, signal, recorder) {
|
|
25
|
+
const startedAt = Date.now();
|
|
26
|
+
let outcome = "error";
|
|
27
|
+
try {
|
|
28
|
+
const solution = await resolver.solve(challenge, signal, recorder);
|
|
29
|
+
outcome = "solved";
|
|
30
|
+
return solution;
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
outcome = signal?.aborted
|
|
34
|
+
? "aborted"
|
|
35
|
+
: error instanceof ProviderError && error.code === "RESOLVER_CHAIN_EXHAUSTED"
|
|
36
|
+
? "exhausted"
|
|
37
|
+
: "error";
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
const ms = Date.now() - startedAt;
|
|
42
|
+
sink.recordVendorAttempt({
|
|
43
|
+
vendor: "custom",
|
|
44
|
+
phase: outcome === "solved" ? "poll_result" : "create_task",
|
|
45
|
+
outcome: outcome === "solved" ? "ok" : "error",
|
|
46
|
+
ms,
|
|
47
|
+
});
|
|
48
|
+
sink.recordOutcome({ outcome, solveMs: ms, challengeKind: challenge.kind });
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
// Rebinding this wrapper returns to the original host, never to its previous sink.
|
|
53
|
+
registerResolverTelemetryBinding(wrapper, createOpaqueResolverTelemetryBinding(resolver));
|
|
54
|
+
return wrapper;
|
|
55
|
+
}
|
|
56
|
+
function createOpaqueResolverTelemetryBinding(resolver) {
|
|
57
|
+
return (sink) => bindResolverTelemetry(resolver, sink);
|
|
58
|
+
}
|
|
2
59
|
export const RESOLVER_INSTRUMENTATION_METADATA = Symbol.for("@apifuse/provider-sdk/runtime/resolver-instrumentation-metadata");
|
|
3
60
|
export function createUnsupportedResolverClient(reason) {
|
|
4
61
|
return {
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { ProviderChallengeKind, ProviderResolverVendor } from "../types.js";
|
|
2
|
+
import { type ClosedEnum, type TelemetryContributor } from "./request-telemetry.js";
|
|
3
|
+
import type { ResolverVendorUnavailableReason } from "./resolver-vendors/types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Result of the resolver call. `solved` means the adapter returned a solution;
|
|
6
|
+
* for SBSD this means payload acceptance, even when `verified` is false.
|
|
7
|
+
* It does not assert that a later protected request succeeded. Stealth reports
|
|
8
|
+
* that verification separately through the refetch status and challenge outcome.
|
|
9
|
+
*/
|
|
10
|
+
export type ResolverTelemetryOutcome = "solved" | "cached" | "exhausted" | "aborted" | "error";
|
|
11
|
+
export type ResolverTelemetryCacheStatus = "hit" | "miss" | "disabled" | "not_cacheable";
|
|
12
|
+
export type ResolverTelemetryIdentitySource = "declared" | "defaulted" | "none";
|
|
13
|
+
export type ResolverTelemetryPhase = "create_task" | "poll_result" | "cleanup" | "measure_ip" | "fetch_script" | "generate_payload" | "post_payload";
|
|
14
|
+
export type ResolverTelemetryAttemptOutcome = "ok" | "error";
|
|
15
|
+
export type ResolverTelemetryErrorClass = ResolverVendorUnavailableReason | "aborted" | "unexpected";
|
|
16
|
+
export type ResolverTelemetryCacheWriteReason = "no_expires" | "not_cacheable" | "error";
|
|
17
|
+
export type ResolverIdentityTelemetryEvent = {
|
|
18
|
+
readonly source: ResolverTelemetryIdentitySource;
|
|
19
|
+
readonly failure?: ResolverVendorUnavailableReason;
|
|
20
|
+
};
|
|
21
|
+
export type ResolverCacheReadTelemetryEvent = {
|
|
22
|
+
readonly status: ResolverTelemetryCacheStatus;
|
|
23
|
+
readonly challengeKind: ProviderChallengeKind;
|
|
24
|
+
};
|
|
25
|
+
export type ResolverCacheWriteTelemetryEvent = {
|
|
26
|
+
readonly written: boolean;
|
|
27
|
+
readonly reason?: ResolverTelemetryCacheWriteReason;
|
|
28
|
+
};
|
|
29
|
+
export type ResolverVendorAttemptTelemetryEvent = {
|
|
30
|
+
readonly vendor: ProviderResolverVendor;
|
|
31
|
+
readonly phase: ResolverTelemetryPhase;
|
|
32
|
+
readonly outcome: ResolverTelemetryAttemptOutcome;
|
|
33
|
+
readonly ms: number;
|
|
34
|
+
readonly pollCount?: number;
|
|
35
|
+
readonly errorClass?: ResolverTelemetryErrorClass;
|
|
36
|
+
readonly vendorErrorCode?: string;
|
|
37
|
+
readonly vendorErrorDescription?: string;
|
|
38
|
+
/** Operator-only diagnostics; free text is redacted, detached and bounded by the collector. */
|
|
39
|
+
readonly diagnostics?: {
|
|
40
|
+
readonly cause?: {
|
|
41
|
+
readonly name: string;
|
|
42
|
+
readonly message: string;
|
|
43
|
+
};
|
|
44
|
+
readonly upstreamHost?: string;
|
|
45
|
+
readonly missingFields?: readonly string[];
|
|
46
|
+
readonly round?: number;
|
|
47
|
+
/** One-based position in the supporting vendor chain. */
|
|
48
|
+
readonly attemptIndex?: number;
|
|
49
|
+
/** Actual adapter phase, including phases outside the header's closed enum. */
|
|
50
|
+
readonly phase?: string;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
export type ResolverFailoverTelemetryEvent = {
|
|
54
|
+
readonly from: ProviderResolverVendor;
|
|
55
|
+
readonly to: ProviderResolverVendor;
|
|
56
|
+
readonly reason: ResolverVendorUnavailableReason;
|
|
57
|
+
};
|
|
58
|
+
export type ResolverOutcomeTelemetryEvent = {
|
|
59
|
+
readonly outcome: ResolverTelemetryOutcome;
|
|
60
|
+
readonly solveMs: number;
|
|
61
|
+
readonly challengeKind: ProviderChallengeKind;
|
|
62
|
+
};
|
|
63
|
+
export interface ResolverTelemetrySink {
|
|
64
|
+
recordIdentity(event: ResolverIdentityTelemetryEvent): void;
|
|
65
|
+
recordCacheRead(event: ResolverCacheReadTelemetryEvent): void;
|
|
66
|
+
recordCacheWrite(event: ResolverCacheWriteTelemetryEvent): void;
|
|
67
|
+
recordVendorAttempt(event: ResolverVendorAttemptTelemetryEvent): void;
|
|
68
|
+
recordFailover(event: ResolverFailoverTelemetryEvent): void;
|
|
69
|
+
recordOutcome(event: ResolverOutcomeTelemetryEvent): void;
|
|
70
|
+
}
|
|
71
|
+
export type ResolverAttemptSample = {
|
|
72
|
+
v: ProviderResolverVendor;
|
|
73
|
+
p: ResolverTelemetryPhase;
|
|
74
|
+
o: ResolverTelemetryAttemptOutcome;
|
|
75
|
+
ms: number;
|
|
76
|
+
c?: string;
|
|
77
|
+
e?: ResolverTelemetryErrorClass;
|
|
78
|
+
diagnostics?: ResolverVendorAttemptTelemetryEvent["diagnostics"];
|
|
79
|
+
};
|
|
80
|
+
export type ResolverTelemetryLogPayload = {
|
|
81
|
+
/** Adapter result; `solved` includes unverified payload acceptance. Read the protected refetch status/challenge outcome for verification. */
|
|
82
|
+
outcome?: ResolverTelemetryOutcome;
|
|
83
|
+
challengeKind?: ProviderChallengeKind;
|
|
84
|
+
cacheStatus?: ResolverTelemetryCacheStatus;
|
|
85
|
+
cacheWrite?: {
|
|
86
|
+
written: boolean;
|
|
87
|
+
reason?: ResolverTelemetryCacheWriteReason;
|
|
88
|
+
};
|
|
89
|
+
identitySource?: ResolverTelemetryIdentitySource;
|
|
90
|
+
identityFailure?: ResolverVendorUnavailableReason;
|
|
91
|
+
solveMs?: number;
|
|
92
|
+
attempts: number;
|
|
93
|
+
failovers: number;
|
|
94
|
+
vendorChain: ProviderResolverVendor[];
|
|
95
|
+
vendorUsed?: ProviderResolverVendor;
|
|
96
|
+
pollCount: number;
|
|
97
|
+
attemptSamples?: ResolverAttemptSample[];
|
|
98
|
+
attemptSamplesDropped?: number;
|
|
99
|
+
lastVendorErrorDescription?: string;
|
|
100
|
+
};
|
|
101
|
+
export type ResolverTelemetryHeaderPayload = {
|
|
102
|
+
/** Same adapter-result semantics as the log outcome; `solved` does not imply protected-refetch success. */
|
|
103
|
+
outcome?: ClosedEnum<ResolverTelemetryOutcome>;
|
|
104
|
+
cacheStatus?: ClosedEnum<ResolverTelemetryCacheStatus>;
|
|
105
|
+
solveMs?: number;
|
|
106
|
+
attempts: number;
|
|
107
|
+
failovers: number;
|
|
108
|
+
vendorUsed?: ClosedEnum<ProviderResolverVendor>;
|
|
109
|
+
vendorChain: ClosedEnum<ProviderResolverVendor>[];
|
|
110
|
+
pollCount: number;
|
|
111
|
+
identitySource?: ClosedEnum<ResolverTelemetryIdentitySource>;
|
|
112
|
+
attemptSamples?: {
|
|
113
|
+
v: ClosedEnum<ProviderResolverVendor>;
|
|
114
|
+
p: ClosedEnum<ResolverTelemetryPhase>;
|
|
115
|
+
o: ClosedEnum<ResolverTelemetryAttemptOutcome>;
|
|
116
|
+
ms: number;
|
|
117
|
+
c?: ClosedEnum<string>;
|
|
118
|
+
}[];
|
|
119
|
+
};
|
|
120
|
+
export declare class ResolverTelemetryCollector implements ResolverTelemetrySink, TelemetryContributor<ResolverTelemetryLogPayload, ResolverTelemetryHeaderPayload> {
|
|
121
|
+
#private;
|
|
122
|
+
readonly key: "resolver";
|
|
123
|
+
constructor(options?: {
|
|
124
|
+
redact?: (text: string) => string;
|
|
125
|
+
});
|
|
126
|
+
recordIdentity(event: ResolverIdentityTelemetryEvent): void;
|
|
127
|
+
recordCacheRead(event: ResolverCacheReadTelemetryEvent): void;
|
|
128
|
+
recordCacheWrite(event: ResolverCacheWriteTelemetryEvent): void;
|
|
129
|
+
recordVendorAttempt(event: ResolverVendorAttemptTelemetryEvent): void;
|
|
130
|
+
recordFailover(_event: ResolverFailoverTelemetryEvent): void;
|
|
131
|
+
recordOutcome(event: ResolverOutcomeTelemetryEvent): void;
|
|
132
|
+
toLogPayload(): ResolverTelemetryLogPayload | undefined;
|
|
133
|
+
toHeaderPayload(log: ResolverTelemetryLogPayload): ResolverTelemetryHeaderPayload;
|
|
134
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
import { closedEnum } from "./request-telemetry.js";
|
|
3
|
+
const MAX_RESOLVER_ATTEMPT_SAMPLES = 24;
|
|
4
|
+
const MAX_RESOLVER_VENDOR_CHAIN = 64;
|
|
5
|
+
const MAX_DIAGNOSTIC_TEXT = 300;
|
|
6
|
+
const MAX_MISSING_FIELDS = 24;
|
|
7
|
+
const HEADER_TOKEN = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,63}$/;
|
|
8
|
+
function boundedInteger(value) {
|
|
9
|
+
return Math.max(0, Math.floor(Number.isFinite(value) ? value : 0));
|
|
10
|
+
}
|
|
11
|
+
function boundedText(value, redact) {
|
|
12
|
+
let result = value;
|
|
13
|
+
try {
|
|
14
|
+
result = redact?.(value) ?? value;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return "[REDACTION_FAILED]";
|
|
18
|
+
}
|
|
19
|
+
// Copy code units through an independent UTF-16 buffer. A sliced string can
|
|
20
|
+
// otherwise keep a multi-megabyte vendor response alive in both JSC and V8.
|
|
21
|
+
// Preserve lone surrogates deliberately, including a pair split at the limit.
|
|
22
|
+
const length = Math.min(result.length, MAX_DIAGNOSTIC_TEXT);
|
|
23
|
+
const units = Buffer.alloc(length * 2);
|
|
24
|
+
for (let index = 0; index < length; index += 1) {
|
|
25
|
+
units.writeUInt16LE(result.charCodeAt(index), index * 2);
|
|
26
|
+
}
|
|
27
|
+
return units.toString("utf16le");
|
|
28
|
+
}
|
|
29
|
+
function boundedDiagnostics(diagnostics, redact) {
|
|
30
|
+
return {
|
|
31
|
+
...(diagnostics.cause
|
|
32
|
+
? {
|
|
33
|
+
cause: {
|
|
34
|
+
name: boundedText(diagnostics.cause.name, redact),
|
|
35
|
+
message: boundedText(diagnostics.cause.message, redact),
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
: {}),
|
|
39
|
+
...(diagnostics.upstreamHost
|
|
40
|
+
? { upstreamHost: boundedText(diagnostics.upstreamHost, redact) }
|
|
41
|
+
: {}),
|
|
42
|
+
...(diagnostics.missingFields
|
|
43
|
+
? {
|
|
44
|
+
missingFields: diagnostics.missingFields
|
|
45
|
+
.slice(0, MAX_MISSING_FIELDS)
|
|
46
|
+
.map((field) => boundedText(field, redact)),
|
|
47
|
+
}
|
|
48
|
+
: {}),
|
|
49
|
+
...(diagnostics.round === undefined ? {} : { round: boundedInteger(diagnostics.round) }),
|
|
50
|
+
...(diagnostics.attemptIndex === undefined
|
|
51
|
+
? {}
|
|
52
|
+
: { attemptIndex: boundedInteger(diagnostics.attemptIndex) }),
|
|
53
|
+
...(diagnostics.phase ? { phase: boundedText(diagnostics.phase, redact) } : {}),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export class ResolverTelemetryCollector {
|
|
57
|
+
key = "resolver";
|
|
58
|
+
#redact;
|
|
59
|
+
#identity;
|
|
60
|
+
#cacheRead;
|
|
61
|
+
#cacheWrite;
|
|
62
|
+
#outcome;
|
|
63
|
+
#attempts = 0;
|
|
64
|
+
#failovers = 0;
|
|
65
|
+
#pollCount = 0;
|
|
66
|
+
#vendorChain = [];
|
|
67
|
+
#vendorUsed;
|
|
68
|
+
#attemptSamples = [];
|
|
69
|
+
#attemptSamplesDropped = 0;
|
|
70
|
+
#lastVendorErrorDescription;
|
|
71
|
+
constructor(options = {}) {
|
|
72
|
+
this.#redact = options.redact;
|
|
73
|
+
}
|
|
74
|
+
recordIdentity(event) {
|
|
75
|
+
this.#identity = { ...event };
|
|
76
|
+
}
|
|
77
|
+
recordCacheRead(event) {
|
|
78
|
+
this.#cacheRead = { ...event };
|
|
79
|
+
}
|
|
80
|
+
recordCacheWrite(event) {
|
|
81
|
+
this.#cacheWrite = { ...event };
|
|
82
|
+
}
|
|
83
|
+
recordVendorAttempt(event) {
|
|
84
|
+
const ms = boundedInteger(event.ms);
|
|
85
|
+
const pollCount = event.pollCount === undefined ? 0 : boundedInteger(event.pollCount);
|
|
86
|
+
this.#pollCount += pollCount;
|
|
87
|
+
if (event.phase !== "cleanup") {
|
|
88
|
+
this.#attempts += 1;
|
|
89
|
+
if (this.#vendorChain.length < MAX_RESOLVER_VENDOR_CHAIN &&
|
|
90
|
+
this.#vendorChain.at(-1) !== event.vendor) {
|
|
91
|
+
this.#vendorChain.push(event.vendor);
|
|
92
|
+
}
|
|
93
|
+
if (event.outcome === "ok")
|
|
94
|
+
this.#vendorUsed = event.vendor;
|
|
95
|
+
}
|
|
96
|
+
if (this.#attemptSamples.length < MAX_RESOLVER_ATTEMPT_SAMPLES) {
|
|
97
|
+
this.#attemptSamples.push({
|
|
98
|
+
v: event.vendor,
|
|
99
|
+
p: event.phase,
|
|
100
|
+
o: event.outcome,
|
|
101
|
+
ms,
|
|
102
|
+
...(event.vendorErrorCode ? { c: boundedText(event.vendorErrorCode, this.#redact) } : {}),
|
|
103
|
+
...(event.errorClass ? { e: event.errorClass } : {}),
|
|
104
|
+
...(event.diagnostics
|
|
105
|
+
? { diagnostics: boundedDiagnostics(event.diagnostics, this.#redact) }
|
|
106
|
+
: {}),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
this.#attemptSamplesDropped += 1;
|
|
111
|
+
}
|
|
112
|
+
if (event.vendorErrorDescription) {
|
|
113
|
+
this.#lastVendorErrorDescription = boundedText(event.vendorErrorDescription, this.#redact);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
recordFailover(_event) {
|
|
117
|
+
this.#failovers += 1;
|
|
118
|
+
}
|
|
119
|
+
recordOutcome(event) {
|
|
120
|
+
this.#outcome = { ...event, solveMs: boundedInteger(event.solveMs) };
|
|
121
|
+
}
|
|
122
|
+
toLogPayload() {
|
|
123
|
+
if (!this.#identity &&
|
|
124
|
+
!this.#cacheRead &&
|
|
125
|
+
!this.#cacheWrite &&
|
|
126
|
+
!this.#outcome &&
|
|
127
|
+
this.#attempts === 0 &&
|
|
128
|
+
this.#attemptSamples.length === 0 &&
|
|
129
|
+
this.#failovers === 0) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
...(this.#outcome
|
|
134
|
+
? {
|
|
135
|
+
outcome: this.#outcome.outcome,
|
|
136
|
+
challengeKind: this.#outcome.challengeKind,
|
|
137
|
+
solveMs: this.#outcome.solveMs,
|
|
138
|
+
}
|
|
139
|
+
: this.#cacheRead
|
|
140
|
+
? { challengeKind: this.#cacheRead.challengeKind }
|
|
141
|
+
: {}),
|
|
142
|
+
...(this.#cacheRead ? { cacheStatus: this.#cacheRead.status } : {}),
|
|
143
|
+
...(this.#cacheWrite ? { cacheWrite: { ...this.#cacheWrite } } : {}),
|
|
144
|
+
...(this.#identity
|
|
145
|
+
? {
|
|
146
|
+
identitySource: this.#identity.source,
|
|
147
|
+
...(this.#identity.failure ? { identityFailure: this.#identity.failure } : {}),
|
|
148
|
+
}
|
|
149
|
+
: {}),
|
|
150
|
+
attempts: this.#attempts,
|
|
151
|
+
failovers: this.#failovers,
|
|
152
|
+
vendorChain: [...this.#vendorChain],
|
|
153
|
+
...(this.#vendorUsed ? { vendorUsed: this.#vendorUsed } : {}),
|
|
154
|
+
pollCount: this.#pollCount,
|
|
155
|
+
...(this.#attemptSamples.length > 0
|
|
156
|
+
? {
|
|
157
|
+
attemptSamples: this.#attemptSamples.map((sample) => ({
|
|
158
|
+
...sample,
|
|
159
|
+
...(sample.diagnostics
|
|
160
|
+
? {
|
|
161
|
+
diagnostics: {
|
|
162
|
+
...sample.diagnostics,
|
|
163
|
+
...(sample.diagnostics.cause
|
|
164
|
+
? { cause: { ...sample.diagnostics.cause } }
|
|
165
|
+
: {}),
|
|
166
|
+
...(sample.diagnostics.missingFields
|
|
167
|
+
? { missingFields: [...sample.diagnostics.missingFields] }
|
|
168
|
+
: {}),
|
|
169
|
+
},
|
|
170
|
+
}
|
|
171
|
+
: {}),
|
|
172
|
+
})),
|
|
173
|
+
}
|
|
174
|
+
: {}),
|
|
175
|
+
...(this.#attemptSamplesDropped > 0
|
|
176
|
+
? { attemptSamplesDropped: this.#attemptSamplesDropped }
|
|
177
|
+
: {}),
|
|
178
|
+
...(this.#lastVendorErrorDescription
|
|
179
|
+
? { lastVendorErrorDescription: this.#lastVendorErrorDescription }
|
|
180
|
+
: {}),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
toHeaderPayload(log) {
|
|
184
|
+
const attemptSamples = log.attemptSamples?.map((sample) => ({
|
|
185
|
+
v: closedEnum(sample.v),
|
|
186
|
+
p: closedEnum(sample.p),
|
|
187
|
+
o: closedEnum(sample.o),
|
|
188
|
+
ms: sample.ms,
|
|
189
|
+
...(sample.c && HEADER_TOKEN.test(sample.c) ? { c: closedEnum(sample.c) } : {}),
|
|
190
|
+
}));
|
|
191
|
+
return {
|
|
192
|
+
...(log.outcome ? { outcome: closedEnum(log.outcome) } : {}),
|
|
193
|
+
...(log.cacheStatus ? { cacheStatus: closedEnum(log.cacheStatus) } : {}),
|
|
194
|
+
...(log.solveMs === undefined ? {} : { solveMs: log.solveMs }),
|
|
195
|
+
attempts: log.attempts,
|
|
196
|
+
failovers: log.failovers,
|
|
197
|
+
...(log.vendorUsed ? { vendorUsed: closedEnum(log.vendorUsed) } : {}),
|
|
198
|
+
vendorChain: log.vendorChain.map((vendor) => closedEnum(vendor)),
|
|
199
|
+
pollCount: log.pollCount,
|
|
200
|
+
...(log.identitySource ? { identitySource: closedEnum(log.identitySource) } : {}),
|
|
201
|
+
...(attemptSamples && attemptSamples.length > 0 ? { attemptSamples } : {}),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ProviderChallengeKind, ProviderResolverVendor } from "../types.js";
|
|
2
|
+
import type { ResolverPaidUsageContext } from "./resolver-vendors/types.js";
|
|
3
|
+
import type { TraceRecorder } from "./trace.js";
|
|
4
|
+
export declare const RESOLVER_PAID_USAGE_SPAN = "resolver.usage";
|
|
5
|
+
export type ResolverPaidUsageOutcome = "success" | "vendor_error" | "timeout" | "abandoned";
|
|
6
|
+
export type ResolverPaidUsageEndpoint = "capsolver:create_task" | "twocaptcha:create_task" | "hyper:ip" | "hyper:sbsd_create";
|
|
7
|
+
/**
|
|
8
|
+
* Records one `resolver.usage` span around a paid vendor call. Callers validate credentials
|
|
9
|
+
* and transport first, so a span means a potentially billable request was attempted (a
|
|
10
|
+
* failed create the vendor may still bill is a span too). Without a trace recorder the call
|
|
11
|
+
* still runs; the lost billing record is warned once per warn sink.
|
|
12
|
+
*/
|
|
13
|
+
export declare function recordPaidResolverCreate<T>(options: {
|
|
14
|
+
readonly traceRecorder?: TraceRecorder;
|
|
15
|
+
readonly vendor: ProviderResolverVendor;
|
|
16
|
+
readonly kind: ProviderChallengeKind;
|
|
17
|
+
readonly endpoint: ResolverPaidUsageEndpoint;
|
|
18
|
+
/** 1-based round for endpoints called more than once per solve (Hyper payload rounds). */
|
|
19
|
+
readonly round?: number;
|
|
20
|
+
readonly signal: AbortSignal;
|
|
21
|
+
readonly usage?: ResolverPaidUsageContext;
|
|
22
|
+
readonly warn?: (message: string) => void;
|
|
23
|
+
readonly create: () => Promise<T>;
|
|
24
|
+
}): Promise<T>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export const RESOLVER_PAID_USAGE_SPAN = "resolver.usage";
|
|
2
|
+
/**
|
|
3
|
+
* How one call to the endpoint is counted. `metered` endpoints are the vendors' task or
|
|
4
|
+
* payload creation calls: the SDK counts one unit per call (the vendor ledger stays
|
|
5
|
+
* authoritative for refunds of failed tasks). Hyper's `/ip` reflector is authenticated
|
|
6
|
+
* but has no published price, so it is recorded with 0 units and `billing: "unconfirmed"`
|
|
7
|
+
* rather than invented as a paid unit; the span still proves the call was made.
|
|
8
|
+
*/
|
|
9
|
+
const ENDPOINT_BILLING = {
|
|
10
|
+
"capsolver:create_task": { billable_units: 1, billing: "metered" },
|
|
11
|
+
"twocaptcha:create_task": { billable_units: 1, billing: "metered" },
|
|
12
|
+
"hyper:ip": { billable_units: 0, billing: "unconfirmed" },
|
|
13
|
+
"hyper:sbsd_create": { billable_units: 1, billing: "metered" },
|
|
14
|
+
};
|
|
15
|
+
function errorOutcome(error, signal) {
|
|
16
|
+
const signalReason = signal.reason;
|
|
17
|
+
const errorName = error instanceof Error ? error.name : "";
|
|
18
|
+
const reasonName = signalReason instanceof Error ? signalReason.name : "";
|
|
19
|
+
const unavailableReason = error?.reason;
|
|
20
|
+
if (unavailableReason === "timeout" ||
|
|
21
|
+
/timeout/iu.test(errorName) ||
|
|
22
|
+
/timeout/iu.test(reasonName)) {
|
|
23
|
+
return "timeout";
|
|
24
|
+
}
|
|
25
|
+
if (signal.aborted)
|
|
26
|
+
return "abandoned";
|
|
27
|
+
return "vendor_error";
|
|
28
|
+
}
|
|
29
|
+
const warnedSinks = new WeakSet();
|
|
30
|
+
/**
|
|
31
|
+
* Records one `resolver.usage` span around a paid vendor call. Callers validate credentials
|
|
32
|
+
* and transport first, so a span means a potentially billable request was attempted (a
|
|
33
|
+
* failed create the vendor may still bill is a span too). Without a trace recorder the call
|
|
34
|
+
* still runs; the lost billing record is warned once per warn sink.
|
|
35
|
+
*/
|
|
36
|
+
export async function recordPaidResolverCreate(options) {
|
|
37
|
+
if (!options.traceRecorder) {
|
|
38
|
+
const warn = options.warn ?? console.warn;
|
|
39
|
+
if (!warnedSinks.has(warn)) {
|
|
40
|
+
warnedSinks.add(warn);
|
|
41
|
+
warn(`[provider-sdk] Paid resolver call ${options.endpoint} ran without a trace recorder; its resolver.usage record is lost (reported once).`);
|
|
42
|
+
}
|
|
43
|
+
return options.create();
|
|
44
|
+
}
|
|
45
|
+
const baseAttributes = {
|
|
46
|
+
vendor: options.vendor,
|
|
47
|
+
challenge_kind: options.kind,
|
|
48
|
+
endpoint: options.endpoint,
|
|
49
|
+
...ENDPOINT_BILLING[options.endpoint],
|
|
50
|
+
// Only the resolver chain knows the vendor's position; a direct adapter call has none.
|
|
51
|
+
...(options.usage ? { vendor_index: options.usage.vendorIndex } : {}),
|
|
52
|
+
...(options.round === undefined ? {} : { round: options.round }),
|
|
53
|
+
resolver_identity_scope: options.usage?.resolverIdentityScope,
|
|
54
|
+
};
|
|
55
|
+
return options.traceRecorder.runSpan(RESOLVER_PAID_USAGE_SPAN, options.create, {
|
|
56
|
+
attributes: baseAttributes,
|
|
57
|
+
onSuccess: () => ({ outcome: "success" }),
|
|
58
|
+
onError: (error) => ({ outcome: errorOutcome(error, options.signal) }),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
@@ -41,6 +41,11 @@ export declare const RESOLVER_CHALLENGE_BINDINGS: {
|
|
|
41
41
|
readonly identityBinding: "identity_scoped";
|
|
42
42
|
readonly directCacheable: false;
|
|
43
43
|
};
|
|
44
|
+
readonly akamai_sbsd: {
|
|
45
|
+
readonly cacheable: false;
|
|
46
|
+
readonly identityBinding: "identity_scoped";
|
|
47
|
+
readonly directCacheable: false;
|
|
48
|
+
};
|
|
44
49
|
};
|
|
45
50
|
export declare function resolverChallengeIsCacheable(challenge: ProviderChallenge): boolean;
|
|
46
51
|
export declare function resolverChallengeAllowsDirectCache(challenge: ProviderChallenge): boolean;
|
|
@@ -22,6 +22,11 @@ export const RESOLVER_CHALLENGE_BINDINGS = {
|
|
|
22
22
|
identityBinding: "identity_scoped",
|
|
23
23
|
directCacheable: false,
|
|
24
24
|
},
|
|
25
|
+
akamai_sbsd: {
|
|
26
|
+
cacheable: false,
|
|
27
|
+
identityBinding: "identity_scoped",
|
|
28
|
+
directCacheable: false,
|
|
29
|
+
},
|
|
25
30
|
};
|
|
26
31
|
export function resolverChallengeIsCacheable(challenge) {
|
|
27
32
|
return RESOLVER_CHALLENGE_BINDINGS[challenge.kind].cacheable;
|
|
@@ -111,7 +111,6 @@ async function runBoundedCleanup(cleanup, timeoutMs, challengeKind, operation, t
|
|
|
111
111
|
onError(error) {
|
|
112
112
|
return {
|
|
113
113
|
error_message: error instanceof Error ? error.message : String(error),
|
|
114
|
-
...(error instanceof Error && error.stack ? { error_stack: error.stack } : {}),
|
|
115
114
|
};
|
|
116
115
|
},
|
|
117
116
|
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ChallengeSolution, ProviderChallenge } from "../../types.js";
|
|
2
2
|
import type { TraceRecorder } from "../trace.js";
|
|
3
|
-
import { type ResolverIdentity, type ResolverVendorAdapter } from "./types.js";
|
|
3
|
+
import { type ResolverIdentity, type ResolverPaidUsageContext, type ResolverVendorAdapter, type ResolverVendorTransport } from "./types.js";
|
|
4
4
|
type Delay = (ms: number, signal: AbortSignal) => Promise<void>;
|
|
5
5
|
export interface CapsolverResolverVendorOptions {
|
|
6
6
|
readonly apiKey?: string;
|
|
@@ -16,7 +16,7 @@ export interface CapsolverResolverVendorOptions {
|
|
|
16
16
|
}
|
|
17
17
|
export interface CapsolverResolverVendorAdapter extends ResolverVendorAdapter {
|
|
18
18
|
readonly id: "capsolver";
|
|
19
|
-
solve(challenge: ProviderChallenge, identity: ResolverIdentity | undefined, signal: AbortSignal, traceRecorder?: TraceRecorder): Promise<ChallengeSolution>;
|
|
19
|
+
solve(challenge: ProviderChallenge, identity: ResolverIdentity | undefined, signal: AbortSignal, traceRecorder?: TraceRecorder, transport?: ResolverVendorTransport, usage?: ResolverPaidUsageContext): Promise<ChallengeSolution>;
|
|
20
20
|
}
|
|
21
21
|
export declare function createCapsolverResolverVendorAdapter(options: CapsolverResolverVendorOptions): CapsolverResolverVendorAdapter;
|
|
22
22
|
export {};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getStealthProfile } from "../../stealth/profiles.js";
|
|
2
2
|
import { redactSensitiveText } from "../request-options.js";
|
|
3
3
|
import { DEFAULT_STEALTH_PROFILE } from "../stealth.js";
|
|
4
|
+
import { recordPaidResolverCreate } from "../resolver-usage.js";
|
|
4
5
|
import { assertResolverHostAllowed } from "./hosts.js";
|
|
5
|
-
import { ResolverChallengeVerdictError, ResolverVendorUnavailableError, resolverVendorSupports, } from "./types.js";
|
|
6
|
+
import { ResolverChallengeVerdictError, ResolverVendorUnavailableError, recordResolverVendorPoll, resolverVendorSupports, } from "./types.js";
|
|
6
7
|
const CAPSOLVER_VENDOR_ID = "capsolver";
|
|
7
8
|
const DEFAULT_CAPSOLVER_BASE_URL = "https://api.capsolver.com";
|
|
8
9
|
const DEFAULT_POLL_INTERVAL_MS = 2_000;
|
|
@@ -304,7 +305,7 @@ export function createCapsolverResolverVendorAdapter(options) {
|
|
|
304
305
|
supports(kind) {
|
|
305
306
|
return resolverVendorSupports(CAPSOLVER_VENDOR_ID, kind);
|
|
306
307
|
},
|
|
307
|
-
async solve(challenge, identity, callerSignal, traceRecorder) {
|
|
308
|
+
async solve(challenge, identity, callerSignal, traceRecorder, _transport, usage) {
|
|
308
309
|
const apiKey = options.apiKey?.trim();
|
|
309
310
|
if (!apiKey) {
|
|
310
311
|
throw new ResolverVendorUnavailableError(CAPSOLVER_VENDOR_ID, "missing_credentials", {
|
|
@@ -416,12 +417,22 @@ export function createCapsolverResolverVendorAdapter(options) {
|
|
|
416
417
|
: (() => {
|
|
417
418
|
throw new TypeError(`Capsolver resolver does not support ${challenge.kind}`);
|
|
418
419
|
})();
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
420
|
+
return await recordPaidResolverCreate({
|
|
421
|
+
traceRecorder,
|
|
422
|
+
vendor: CAPSOLVER_VENDOR_ID,
|
|
423
|
+
kind: challenge.kind,
|
|
424
|
+
endpoint: "capsolver:create_task",
|
|
425
|
+
signal: solveController.signal,
|
|
426
|
+
usage,
|
|
427
|
+
create: async () => {
|
|
428
|
+
const createResult = await postJson(fetchImpl, endpoint(baseUrl, "createTask"), { clientKey: apiKey, task }, solveController.signal, phase, sensitiveValues, parseCreateTaskResponse);
|
|
429
|
+
const taskId = createResult.payload.taskId;
|
|
430
|
+
if (!createResult.ok || createResult.payload.errorId !== 0 || taskId === undefined) {
|
|
431
|
+
throw unavailableForPayload(createResult.payload, phase, sensitiveValues);
|
|
432
|
+
}
|
|
433
|
+
return taskId;
|
|
434
|
+
},
|
|
435
|
+
});
|
|
425
436
|
};
|
|
426
437
|
const taskId = traceRecorder
|
|
427
438
|
? await traceRecorder.runSpan("resolver.vendor.create_task", createTask, {
|
|
@@ -443,6 +454,7 @@ export function createCapsolverResolverVendorAdapter(options) {
|
|
|
443
454
|
callerSignal.throwIfAborted();
|
|
444
455
|
if (now() - startedAt >= timeoutMs)
|
|
445
456
|
throw new CapsolverSolveTimeoutError();
|
|
457
|
+
recordResolverVendorPoll(traceRecorder);
|
|
446
458
|
const result = await postJson(fetchImpl, endpoint(baseUrl, "getTaskResult"), { clientKey: apiKey, taskId }, solveController.signal, phase, sensitiveValues, parsePollResultResponse);
|
|
447
459
|
if (!result.ok || result.payload.errorId !== 0) {
|
|
448
460
|
throw unavailableForPayload(result.payload, phase, sensitiveValues);
|
|
@@ -460,7 +472,7 @@ export function createCapsolverResolverVendorAdapter(options) {
|
|
|
460
472
|
}
|
|
461
473
|
const solutionValue = challenge.kind === "aws_waf"
|
|
462
474
|
? result.payload.solution?.cookie
|
|
463
|
-
: result.payload.solution?.token ?? result.payload.solution?.gRecaptchaResponse;
|
|
475
|
+
: (result.payload.solution?.token ?? result.payload.solution?.gRecaptchaResponse);
|
|
464
476
|
if (challenge.kind === "cloudflare_interstitial") {
|
|
465
477
|
const cookies = result.payload.solution?.cookies;
|
|
466
478
|
const clearance = cookies?.cf_clearance ?? solutionValue;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ChallengeSolution, ProviderChallenge } from "../../types.js";
|
|
2
|
+
import type { TraceRecorder } from "../trace.js";
|
|
3
|
+
import { type ResolverIdentity, type ResolverPaidUsageContext, type ResolverVendorAdapter, type ResolverVendorTransport } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Only the observed-IP reflector rides the bound transport: Hyper must see the
|
|
6
|
+
* egress address the upstream will see. The payload-generation POST carries no
|
|
7
|
+
* identity and goes direct, as in the measured zozotown source.
|
|
8
|
+
*/
|
|
9
|
+
declare const HYPER_TRANSPORT_HOSTS: readonly ["ip.hypersolutions.co"];
|
|
10
|
+
export interface HypersolutionsResolverVendorOptions {
|
|
11
|
+
readonly apiKey?: string;
|
|
12
|
+
readonly timeoutMs?: number;
|
|
13
|
+
readonly allowedHosts: readonly string[];
|
|
14
|
+
/** Direct egress used only for the Hyper payload-generation POST. */
|
|
15
|
+
readonly fetchImpl?: typeof fetch;
|
|
16
|
+
}
|
|
17
|
+
export type AkamaiSbsdChallengeSolution = Extract<ChallengeSolution, {
|
|
18
|
+
readonly form: "cookie_state";
|
|
19
|
+
}>;
|
|
20
|
+
export interface HypersolutionsResolverVendorAdapter extends ResolverVendorAdapter {
|
|
21
|
+
readonly id: "hypersolutions";
|
|
22
|
+
readonly requiresTransport: true;
|
|
23
|
+
readonly transportAllowedHosts: typeof HYPER_TRANSPORT_HOSTS;
|
|
24
|
+
solve(challenge: ProviderChallenge, identity: ResolverIdentity | undefined, signal: AbortSignal, traceRecorder?: TraceRecorder, transport?: ResolverVendorTransport, usage?: ResolverPaidUsageContext): Promise<AkamaiSbsdChallengeSolution>;
|
|
25
|
+
}
|
|
26
|
+
export declare function createHypersolutionsResolverVendorAdapter(options: HypersolutionsResolverVendorOptions): HypersolutionsResolverVendorAdapter;
|
|
27
|
+
export {};
|