@apifuse/provider-sdk 2.2.0-beta.23 → 2.2.0-beta.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/bin/apifuse-dev.ts +2 -0
- package/bin/apifuse-pack-types.ts +234 -38
- package/bin/apifuse-perf.ts +15 -12
- package/bin/apifuse-record.ts +2 -0
- package/dist/config/loader.d.ts +8 -19
- package/dist/config/loader.js +28 -86
- package/dist/define.d.ts +4 -1
- package/dist/define.js +64 -6
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -0
- package/dist/provider.d.ts +1 -1
- package/dist/runtime/auth-flow.js +2 -0
- package/dist/runtime/browser.js +50 -0
- package/dist/runtime/http.js +0 -1
- package/dist/runtime/instrumentation.js +26 -1
- package/dist/runtime/resolver-vendors/bindings.d.ts +8 -0
- package/dist/runtime/resolver-vendors/bindings.js +15 -0
- package/dist/runtime/resolver-vendors/browser.d.ts +24 -0
- package/dist/runtime/resolver-vendors/browser.js +287 -0
- package/dist/runtime/resolver-vendors/types.d.ts +42 -0
- package/dist/runtime/resolver-vendors/types.js +57 -0
- package/dist/runtime/resolver.d.ts +39 -0
- package/dist/runtime/resolver.js +414 -0
- package/dist/runtime/state.d.ts +3 -0
- package/dist/runtime/state.js +245 -141
- package/dist/runtime/stealth.js +3 -6
- package/dist/server/serve.d.ts +4 -1
- package/dist/server/serve.js +35 -8
- package/dist/testing/run.js +7 -0
- package/dist/types.d.ts +112 -4
- package/package.json +1 -1
- package/src/config/loader.ts +35 -111
- package/src/define.ts +105 -8
- package/src/index.ts +20 -1
- package/src/provider.ts +1 -0
- package/src/runtime/auth-flow.ts +2 -0
- package/src/runtime/browser.ts +69 -0
- package/src/runtime/http.ts +0 -1
- package/src/runtime/instrumentation.ts +36 -2
- package/src/runtime/resolver-vendors/bindings.ts +31 -0
- package/src/runtime/resolver-vendors/browser.ts +420 -0
- package/src/runtime/resolver-vendors/types.ts +113 -0
- package/src/runtime/resolver.ts +668 -0
- package/src/runtime/state.ts +323 -166
- package/src/runtime/stealth.ts +3 -6
- package/src/server/serve.ts +73 -5
- package/src/testing/run.ts +8 -0
- package/src/types.ts +130 -4
package/dist/config/loader.js
CHANGED
|
@@ -221,64 +221,7 @@ function serializeSmartproxyPool(pool) {
|
|
|
221
221
|
}
|
|
222
222
|
function normalizeProxyUrl(url) {
|
|
223
223
|
const normalized = url?.trim();
|
|
224
|
-
return normalized
|
|
225
|
-
}
|
|
226
|
-
function readPositiveIntegerEnv(name) {
|
|
227
|
-
const raw = process.env[name]?.trim();
|
|
228
|
-
if (!raw)
|
|
229
|
-
return undefined;
|
|
230
|
-
if (!/^[1-9]\d*$/.test(raw)) {
|
|
231
|
-
throw new Error(`${name} must be a positive integer`);
|
|
232
|
-
}
|
|
233
|
-
return raw;
|
|
234
|
-
}
|
|
235
|
-
function applyStickyProxySession(proxyUrl) {
|
|
236
|
-
let parsed;
|
|
237
|
-
try {
|
|
238
|
-
parsed = new URL(proxyUrl);
|
|
239
|
-
}
|
|
240
|
-
catch {
|
|
241
|
-
return proxyUrl;
|
|
242
|
-
}
|
|
243
|
-
if (!parsed.hostname || !parsed.username || !parsed.password) {
|
|
244
|
-
return proxyUrl;
|
|
245
|
-
}
|
|
246
|
-
// This rewrites sticky-session usernames for a bring-your-own *gateway* URL
|
|
247
|
-
// (APIFUSE__PROXY__URL). The `smartproxy` host here means a smartproxy.com /
|
|
248
|
-
// Decodo-family gateway that authenticates by username — NOT the
|
|
249
|
-
// api.smartproxy.org allocation vendor, whose endpoints are raw ip:port with
|
|
250
|
-
// no credentials and therefore return early above.
|
|
251
|
-
const host = parsed.hostname.toLowerCase();
|
|
252
|
-
if (!host.includes("smartproxy") && !host.includes("decodo")) {
|
|
253
|
-
return proxyUrl;
|
|
254
|
-
}
|
|
255
|
-
const username = decodeURIComponent(parsed.username);
|
|
256
|
-
const sessionId = process.env.APIFUSE__PROXY__SESSION_ID?.trim() || "apifuse-shared";
|
|
257
|
-
const sessionDuration = readPositiveIntegerEnv("APIFUSE__PROXY__SESSION_DURATION");
|
|
258
|
-
const stickyUsername = host.includes("smartproxy")
|
|
259
|
-
? buildSmartproxyUsername(username, sessionId, sessionDuration)
|
|
260
|
-
: buildDecodoUsername(username, sessionId, sessionDuration ?? "60");
|
|
261
|
-
parsed.username = stickyUsername;
|
|
262
|
-
return parsed.toString();
|
|
263
|
-
}
|
|
264
|
-
function buildSmartproxyUsername(username, sessionId, sessionDuration) {
|
|
265
|
-
const parts = username.split("_");
|
|
266
|
-
const configuredLife = parts.find((part) => part.startsWith("life-"))?.slice("life-".length);
|
|
267
|
-
const baseUsername = parts
|
|
268
|
-
.filter((part) => !part.startsWith("session-") && !part.startsWith("life-"))
|
|
269
|
-
.join("_");
|
|
270
|
-
return `${baseUsername}_session-${sessionId}_life-${sessionDuration ?? configuredLife ?? "60"}`;
|
|
271
|
-
}
|
|
272
|
-
function buildDecodoUsername(username, sessionId, sessionDuration) {
|
|
273
|
-
const withoutSticky = username.replace(/-session-.+-sessionduration-\d+$/, "");
|
|
274
|
-
const baseUsername = withoutSticky.startsWith("user-") ? withoutSticky : `user-${withoutSticky}`;
|
|
275
|
-
return `${baseUsername}-session-${sessionId}-sessionduration-${sessionDuration}`;
|
|
276
|
-
}
|
|
277
|
-
function syncProxyEnv(config) {
|
|
278
|
-
const configProxyUrl = normalizeProxyUrl(config.proxy?.url);
|
|
279
|
-
if (!process.env.APIFUSE__PROXY__URL && configProxyUrl) {
|
|
280
|
-
process.env.APIFUSE__PROXY__URL = configProxyUrl;
|
|
281
|
-
}
|
|
224
|
+
return normalized || undefined;
|
|
282
225
|
}
|
|
283
226
|
export function resolveProxyConfig(options = {}) {
|
|
284
227
|
const explicitProxyUrl = normalizeProxyUrl(options.proxy);
|
|
@@ -293,14 +236,6 @@ export function resolveProxyConfig(options = {}) {
|
|
|
293
236
|
if (!legacyProxyRequested) {
|
|
294
237
|
return { shouldWarn: false };
|
|
295
238
|
}
|
|
296
|
-
const envProxyUrl = normalizeProxyUrl(process.env.APIFUSE__PROXY__URL);
|
|
297
|
-
if (envProxyUrl) {
|
|
298
|
-
return { shouldWarn: false, url: envProxyUrl };
|
|
299
|
-
}
|
|
300
|
-
const configuredProxyUrl = normalizeProxyUrl(options.apifuseConfig?.proxy?.url);
|
|
301
|
-
if (configuredProxyUrl) {
|
|
302
|
-
return { shouldWarn: false, url: configuredProxyUrl };
|
|
303
|
-
}
|
|
304
239
|
return { shouldWarn: true };
|
|
305
240
|
}
|
|
306
241
|
export async function resolveProxyConfigAsync(options = {}) {
|
|
@@ -317,7 +252,17 @@ export async function resolveProxyConfigAsync(options = {}) {
|
|
|
317
252
|
}
|
|
318
253
|
const chain = resolveVendorChain(policy);
|
|
319
254
|
if (chain.length === 0) {
|
|
320
|
-
|
|
255
|
+
const declared = declaredVendorChain(policy);
|
|
256
|
+
const deprecated = declared.filter((vendor) => vendor === "decodo" || vendor === "custom");
|
|
257
|
+
if (policy.mode === "required") {
|
|
258
|
+
const providerIds = declared.length > 0 ? declared.map((vendor) => `"${vendor}"`).join(", ") : "none";
|
|
259
|
+
const deprecatedDetail = deprecated.length > 0
|
|
260
|
+
? ` Deprecated vendor(s): ${deprecated.map((vendor) => `"${vendor}"`).join(", ")}.`
|
|
261
|
+
: "";
|
|
262
|
+
throw new ProxyResolutionError("PROXY_REQUIRED", `Required proxy policy has no SDK-managed adapter for provider id(s): ${providerIds}.${deprecatedDetail} Use "smartproxy" or "nodemaven".`);
|
|
263
|
+
}
|
|
264
|
+
// Deprecated decodo/custom providers have no SDK-managed adapter. Optional
|
|
265
|
+
// policies preserve the warning-only behavior and may continue directly.
|
|
321
266
|
return resolveProxyConfig({
|
|
322
267
|
...options,
|
|
323
268
|
upstream: { proxy: true },
|
|
@@ -524,18 +469,21 @@ function resolvePolicy(options) {
|
|
|
524
469
|
function isRegistryVendor(name) {
|
|
525
470
|
return name === "smartproxy" || name === "nodemaven";
|
|
526
471
|
}
|
|
472
|
+
function declaredVendorChain(policy) {
|
|
473
|
+
const declared = policy.providers?.length
|
|
474
|
+
? policy.providers
|
|
475
|
+
: [policy.provider ?? envDefaultProvider()];
|
|
476
|
+
return declared.filter((vendor) => vendor !== undefined);
|
|
477
|
+
}
|
|
527
478
|
/**
|
|
528
479
|
* Ordered list of SDK-native proxy vendors declared by the policy. `providers`
|
|
529
480
|
* takes precedence over the legacy singular `provider`; the platform default
|
|
530
481
|
* env is the final fallback. Non-registry names (decodo/custom) are dropped so
|
|
531
|
-
* an all-
|
|
482
|
+
* an all-deprecated chain has no managed adapter.
|
|
532
483
|
*/
|
|
533
484
|
export function resolveVendorChain(policy) {
|
|
534
|
-
const declared = policy.providers?.length
|
|
535
|
-
? policy.providers
|
|
536
|
-
: [policy.provider ?? envDefaultProvider()];
|
|
537
485
|
const chain = [];
|
|
538
|
-
for (const name of
|
|
486
|
+
for (const name of declaredVendorChain(policy)) {
|
|
539
487
|
if (isRegistryVendor(name) && !chain.includes(name)) {
|
|
540
488
|
chain.push(name);
|
|
541
489
|
}
|
|
@@ -600,9 +548,8 @@ const UNSAFE_TRANSPORT_RETRY_METHODS = new Set(["POST", "PUT", "PATCH", "DELETE"
|
|
|
600
548
|
* - the method is safe/idempotent — an unsafe request must never be duplicated
|
|
601
549
|
* across the pool even if some framework default would allow it;
|
|
602
550
|
* - the policy resolves a non-empty *registry* vendor chain (smartproxy /
|
|
603
|
-
* nodemaven).
|
|
604
|
-
*
|
|
605
|
-
* with no possible crossover — they keep the retry budget.
|
|
551
|
+
* nodemaven). Deprecated vendors (custom / decodo) resolve no managed pool,
|
|
552
|
+
* so there is no possible endpoint crossover — they keep the retry budget.
|
|
606
553
|
*
|
|
607
554
|
* The widened cap is bounded by the chain's true maximum span (sum of each
|
|
608
555
|
* vendor's max pool size), so a large NodeMaven pool (≤50) stays reachable and
|
|
@@ -621,8 +568,8 @@ const UNSAFE_TRANSPORT_RETRY_METHODS = new Set(["POST", "PUT", "PATCH", "DELETE"
|
|
|
621
568
|
* endpoint each attempt resolves (even a repeated one), so no de-duplication;
|
|
622
569
|
* - the method is safe/idempotent — an unsafe request is never duplicated;
|
|
623
570
|
* - the policy resolves a non-empty registry vendor chain (smartproxy /
|
|
624
|
-
* nodemaven).
|
|
625
|
-
*
|
|
571
|
+
* nodemaven). Deprecated vendors (custom / decodo) resolve no managed
|
|
572
|
+
* endpoint, so there is nothing to rotate or de-duplicate.
|
|
626
573
|
*/
|
|
627
574
|
export function policyRotatesTransportVendorChain(input) {
|
|
628
575
|
if (!input.usesPolicyAllocator || !input.policy || input.explicitRetry) {
|
|
@@ -650,9 +597,8 @@ export function resolvePolicyTransportAttemptCap(input) {
|
|
|
650
597
|
* A registry vendor chain (smartproxy/nodemaven) resolves a potentially
|
|
651
598
|
* *different* endpoint per flat attempt index, so a transport retry should
|
|
652
599
|
* advance across endpoints and de-duplicate once the chain stops yielding new
|
|
653
|
-
* ones.
|
|
654
|
-
*
|
|
655
|
-
* transport loop must not de-duplicate them.
|
|
600
|
+
* ones. Deprecated custom/decodo policies have an empty registry chain and no
|
|
601
|
+
* managed endpoint, so the transport loop has nothing to rotate or de-duplicate.
|
|
656
602
|
*/
|
|
657
603
|
export function policyResolvesRegistryVendorChain(policy) {
|
|
658
604
|
return Boolean(policy) && resolveVendorChain(policy).length > 0;
|
|
@@ -1249,16 +1195,12 @@ export async function loadApiFuseConfig(dir = process.cwd()) {
|
|
|
1249
1195
|
const tsPath = path.resolve(dir, "apifuse.config.ts");
|
|
1250
1196
|
if (existsSync(tsPath)) {
|
|
1251
1197
|
const config = await importConfig(tsPath);
|
|
1252
|
-
|
|
1253
|
-
syncProxyEnv(resolvedConfig);
|
|
1254
|
-
return resolvedConfig;
|
|
1198
|
+
return config ?? {};
|
|
1255
1199
|
}
|
|
1256
1200
|
const jsPath = path.resolve(dir, "apifuse.config.js");
|
|
1257
1201
|
if (existsSync(jsPath)) {
|
|
1258
1202
|
const config = await importConfig(jsPath);
|
|
1259
|
-
|
|
1260
|
-
syncProxyEnv(resolvedConfig);
|
|
1261
|
-
return resolvedConfig;
|
|
1203
|
+
return config ?? {};
|
|
1262
1204
|
}
|
|
1263
1205
|
return {};
|
|
1264
1206
|
}
|
package/dist/define.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthConfig, BrowserEngine, ContextDeclaration, CredentialDeclaration, HealthJourneyDefinition, HealthJourneySchedule, HealthScheduleRandomization, InferSchemaOutput, OperationDefinition, OperationHandlerResult, OperationHttpStreamTransport, OperationSseTransport, OperationWebSocketTransport,
|
|
1
|
+
import type { AuthConfig, BrowserEngine, ContextDeclaration, CredentialDeclaration, HealthJourneyDefinition, HealthJourneySchedule, HealthScheduleRandomization, InferSchemaOutput, NativeProviderConfig, OperationDefinition, OperationHandlerResult, OperationHttpStreamTransport, OperationSseTransport, OperationWebSocketTransport, ProviderAccessConfig, ProviderDefinition, ProviderOcrConfig, ProviderDeploymentOverrides, ProviderHealthMonitorConfig, ProviderProxyConfig, ProviderPublicProfile, ProviderResolverConfig, ProviderReviewed, ProviderSecretDeclaration, ProviderStreamEvent, ProviderSttConfig, SchemaLike, SmsOtpMatcherDefinition, StealthPlatform } from "./types.js";
|
|
2
2
|
type ProviderImplementationSourceAccess = "official_api" | "private_api" | "browser_flow" | "hybrid";
|
|
3
3
|
type ProviderImplementationCredentialStrategy = "apifuse_managed" | "workspace_secret" | "user_oauth" | "user_session" | "none";
|
|
4
4
|
interface ProviderImplementationProfile {
|
|
@@ -8,6 +8,8 @@ interface ProviderImplementationProfile {
|
|
|
8
8
|
operatorNotes?: string;
|
|
9
9
|
visibility: "internal" | "operator";
|
|
10
10
|
}
|
|
11
|
+
export declare const VALID_PROVIDER_RESOLVER_VENDORS: readonly ["browser", "capsolver", "capmonster", "2captcha", "custom"];
|
|
12
|
+
export declare const VALID_PROVIDER_CHALLENGE_KINDS: readonly ["turnstile", "recaptcha_v2", "recaptcha_v3", "hcaptcha", "cloudflare_interstitial", "aws_waf"];
|
|
11
13
|
type ProviderOperation = OperationDefinition<SchemaLike, SchemaLike>;
|
|
12
14
|
type OperationConfig<TInput extends SchemaLike, TOutput extends SchemaLike> = Omit<OperationDefinition<TInput, TOutput>, "handler"> & {
|
|
13
15
|
handler(ctx: Parameters<OperationDefinition<TInput, TOutput>["handler"]>[0], input: InferSchemaOutput<TInput>): OperationHandlerResult<InferSchemaOutput<TOutput>> | Promise<OperationHandlerResult<InferSchemaOutput<TOutput>>>;
|
|
@@ -57,6 +59,7 @@ export interface ProviderConfig<TOperations extends Record<string, ProviderOpera
|
|
|
57
59
|
proxy?: ProviderProxyConfig;
|
|
58
60
|
ocr?: ProviderOcrConfig;
|
|
59
61
|
stt?: ProviderSttConfig;
|
|
62
|
+
resolver?: ProviderResolverConfig;
|
|
60
63
|
browser?: {
|
|
61
64
|
engine: BrowserEngine;
|
|
62
65
|
};
|
package/dist/define.js
CHANGED
|
@@ -49,6 +49,24 @@ const VALID_PROVIDER_PROXY_AFFINITIES = [
|
|
|
49
49
|
];
|
|
50
50
|
const VALID_PROVIDER_OCR_MODES = ["optional", "required"];
|
|
51
51
|
const VALID_PROVIDER_STT_MODES = ["optional", "required"];
|
|
52
|
+
function exhaustiveLiteralArray() {
|
|
53
|
+
return (values, ..._missing) => values;
|
|
54
|
+
}
|
|
55
|
+
export const VALID_PROVIDER_RESOLVER_VENDORS = exhaustiveLiteralArray()([
|
|
56
|
+
"browser",
|
|
57
|
+
"capsolver",
|
|
58
|
+
"capmonster",
|
|
59
|
+
"2captcha",
|
|
60
|
+
"custom",
|
|
61
|
+
]);
|
|
62
|
+
export const VALID_PROVIDER_CHALLENGE_KINDS = exhaustiveLiteralArray()([
|
|
63
|
+
"turnstile",
|
|
64
|
+
"recaptcha_v2",
|
|
65
|
+
"recaptcha_v3",
|
|
66
|
+
"hcaptcha",
|
|
67
|
+
"cloudflare_interstitial",
|
|
68
|
+
"aws_waf",
|
|
69
|
+
]);
|
|
52
70
|
const SMARTPROXY_APP_KEY_SECRET = "APIFUSE__PROXY__SMARTPROXY_APP_KEY";
|
|
53
71
|
const NODEMAVEN_USERNAME_SECRET = "APIFUSE__PROXY__NODEMAVEN_USERNAME";
|
|
54
72
|
const NODEMAVEN_PASSWORD_SECRET = "APIFUSE__PROXY__NODEMAVEN_PASSWORD";
|
|
@@ -57,9 +75,8 @@ const NODEMAVEN_PASSWORD_SECRET = "APIFUSE__PROXY__NODEMAVEN_PASSWORD";
|
|
|
57
75
|
// credential fails at build/validation time rather than during a live outage: a
|
|
58
76
|
// declared-but-uncredentialed fallback leg is a silently dead SPOF, which is
|
|
59
77
|
// exactly the failure class the multi-vendor chain exists to remove. Vendors
|
|
60
|
-
// absent from this map (
|
|
61
|
-
//
|
|
62
|
-
// no declaration requirement.
|
|
78
|
+
// absent from this map (the deprecated `custom`/`decodo` values have no managed
|
|
79
|
+
// adapter) impose no declaration requirement.
|
|
63
80
|
const VENDOR_REQUIRED_SECRETS = {
|
|
64
81
|
smartproxy: [SMARTPROXY_APP_KEY_SECRET],
|
|
65
82
|
nodemaven: [NODEMAVEN_USERNAME_SECRET, NODEMAVEN_PASSWORD_SECRET],
|
|
@@ -362,8 +379,15 @@ function validateProviderProxy(config) {
|
|
|
362
379
|
// `decodo`/`custom` are deprecated vendor values (string-union members, so the
|
|
363
380
|
// @deprecated symbol gate can't catch them — warn at validation time instead).
|
|
364
381
|
const deprecatedVendors = vendorChain.filter((vendor) => vendor === "decodo" || vendor === "custom");
|
|
382
|
+
if (proxy.mode === "required" &&
|
|
383
|
+
vendorChain.length > 0 &&
|
|
384
|
+
deprecatedVendors.length === vendorChain.length) {
|
|
385
|
+
throw new ValidationError(`Provider "${config.id}" requires proxy egress but declares only deprecated proxy vendor(s): ${deprecatedVendors.join(", ")}.`, {
|
|
386
|
+
fix: `Use proxy.provider or proxy.providers with "smartproxy" and/or "nodemaven".`,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
365
389
|
if (deprecatedVendors.length > 0) {
|
|
366
|
-
console.warn(`[provider-sdk] Provider "${config.id}" uses deprecated proxy vendor(s): ${deprecatedVendors.join(", ")}. Use "smartproxy"/"nodemaven"
|
|
390
|
+
console.warn(`[provider-sdk] Provider "${config.id}" uses deprecated proxy vendor(s): ${deprecatedVendors.join(", ")}. Use "smartproxy"/"nodemaven".`);
|
|
367
391
|
}
|
|
368
392
|
}
|
|
369
393
|
function validateProviderStt(config) {
|
|
@@ -390,6 +414,34 @@ function validateProviderOcr(config) {
|
|
|
390
414
|
rejectUnknownFields(ocr, new Set(["mode"]), "ocr");
|
|
391
415
|
assertLiteralField(ocr.mode, "ocr.mode", VALID_PROVIDER_OCR_MODES, config.id);
|
|
392
416
|
}
|
|
417
|
+
function validateProviderResolver(config) {
|
|
418
|
+
const resolver = config.resolver;
|
|
419
|
+
if (resolver === undefined)
|
|
420
|
+
return;
|
|
421
|
+
if (!resolver || typeof resolver !== "object" || Array.isArray(resolver)) {
|
|
422
|
+
throw new ValidationError(`Provider "${config.id}" has invalid resolver: must be an object.`, {
|
|
423
|
+
fix: `Set resolver for provider "${config.id}" to { vendors: ["2captcha"], kinds: ["turnstile"] }.`,
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
rejectUnknownFields(resolver, new Set(["vendors", "kinds"]), "resolver", config.id);
|
|
427
|
+
validateResolverLiteralArray(resolver.vendors, "resolver.vendors", VALID_PROVIDER_RESOLVER_VENDORS, config.id);
|
|
428
|
+
validateResolverLiteralArray(resolver.kinds, "resolver.kinds", VALID_PROVIDER_CHALLENGE_KINDS, config.id);
|
|
429
|
+
}
|
|
430
|
+
function validateResolverLiteralArray(value, field, validValues, providerId) {
|
|
431
|
+
if (!Array.isArray(value)) {
|
|
432
|
+
throw new ValidationError(`Provider "${providerId}" has invalid ${field}: must be an array.`, {
|
|
433
|
+
fix: `Set ${field} for provider "${providerId}" to an array containing only: ${validValues.join(", ")}.`,
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
for (const [index, item] of value.entries()) {
|
|
437
|
+
if (typeof item === "string" && validValues.some((validValue) => validValue === item)) {
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
throw new ValidationError(`Provider "${providerId}" has invalid ${field}[${index}]: ${JSON.stringify(item)}. Expected one of: ${validValues.join(", ")}`, {
|
|
441
|
+
fix: `Set ${field}[${index}] for provider "${providerId}" to one of ${validValues.map((validValue) => `"${validValue}"`).join(", ")}.`,
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
}
|
|
393
445
|
function validateOperationIds(providerId, operations) {
|
|
394
446
|
for (const operationName of Object.keys(operations)) {
|
|
395
447
|
if (!OPERATION_ID_REGEX.test(operationName))
|
|
@@ -764,14 +816,18 @@ function suggestField(unknown, candidates) {
|
|
|
764
816
|
}
|
|
765
817
|
return best;
|
|
766
818
|
}
|
|
767
|
-
function rejectUnknownFields(value, allowed, fieldPath) {
|
|
819
|
+
function rejectUnknownFields(value, allowed, fieldPath, providerId) {
|
|
768
820
|
for (const key of Object.keys(value)) {
|
|
769
821
|
if (allowed.has(key))
|
|
770
822
|
continue;
|
|
771
823
|
const hint = suggestField(key, allowed);
|
|
772
824
|
throw new ValidationError(hint
|
|
773
825
|
? `Unknown field "${key}" on ${fieldPath}. Did you mean "${hint}"?`
|
|
774
|
-
: `Unknown field "${key}" on ${fieldPath}.`, {
|
|
826
|
+
: `Unknown field "${key}" on ${fieldPath}.`, {
|
|
827
|
+
fix: providerId
|
|
828
|
+
? `Remove ${fieldPath}.${key} from provider "${providerId}" or rename it.`
|
|
829
|
+
: `Remove ${fieldPath}.${key} or rename it.`,
|
|
830
|
+
});
|
|
775
831
|
}
|
|
776
832
|
}
|
|
777
833
|
function assertBoundedIntegerMs(value, fieldPath, options) {
|
|
@@ -1553,6 +1609,7 @@ export function defineProvider(config) {
|
|
|
1553
1609
|
validateProviderProxy(config);
|
|
1554
1610
|
validateProviderOcr(config);
|
|
1555
1611
|
validateProviderStt(config);
|
|
1612
|
+
validateProviderResolver(config);
|
|
1556
1613
|
if (config.runtime === "browser" && !config.browser)
|
|
1557
1614
|
throw new ProviderError(`Provider "${config.id}" must define browser.engine when runtime is "browser"`, {
|
|
1558
1615
|
fix: 'Add browser: { engine: "playwright-stealth" } for TypeScript providers, or another supported engine for your runtime',
|
|
@@ -1572,6 +1629,7 @@ export function defineProvider(config) {
|
|
|
1572
1629
|
proxy: config.proxy,
|
|
1573
1630
|
ocr: config.ocr,
|
|
1574
1631
|
stt: config.stt,
|
|
1632
|
+
resolver: config.resolver,
|
|
1575
1633
|
browser: config.browser,
|
|
1576
1634
|
auth: config.auth,
|
|
1577
1635
|
reviewed: config.reviewed,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from "./auth.js";
|
|
2
2
|
export * from "./ceremonies/index.js";
|
|
3
3
|
export * from "./choice-token.js";
|
|
4
|
-
export type { ApiFuseConfig, BrowserConfig,
|
|
4
|
+
export type { ApiFuseConfig, BrowserConfig, ProxyProtocol, ProxyResolutionOptions, ProxyResolutionSource, ProxyVendorName, ResolvedProxyConfig, SessionConfig, } from "./config/loader.js";
|
|
5
5
|
export { defineConfig, loadApiFuseConfig, resolveProxy } from "./config/loader.js";
|
|
6
6
|
export { canonicalJson, digestProviderContract, extractProviderContract, type JsonPrimitive, type JsonValue, PROVIDER_CONTRACT_SCHEMA_VERSION, type ProviderContractOperation, type ProviderContractSnapshot, } from "./contract.js";
|
|
7
7
|
export { centered, delayed, defineHealthJourney, defineOperation, defineProvider, defineSmsOtpMatcher, defineStreamOperation, every, type ProviderConfig, } from "./define.js";
|
|
@@ -29,6 +29,7 @@ export { generateInsights } from "./runtime/insights.js";
|
|
|
29
29
|
export { type InstrumentationOptions, type InstrumentedProviderContext, wrapWithInstrumentation, } from "./runtime/instrumentation.js";
|
|
30
30
|
export { type PrevalidateResult, prevalidate } from "./runtime/prevalidate.js";
|
|
31
31
|
export { getProviderBaseUrl } from "./runtime/provider.js";
|
|
32
|
+
export { APIFUSE__CDP_POOL__URL, APIFUSE__RESOLVER__2CAPTCHA__API_KEY, APIFUSE__RESOLVER__CAPMONSTER__API_KEY, APIFUSE__RESOLVER__CAPSOLVER__API_KEY, APIFUSE__RESOLVER__TIMEOUT_MS, createResolverClientFromEnv, createUnsupportedResolverClient, DEFAULT_RESOLVER_TIMEOUT_MS, invalidateResolverSolution, type ResolverRuntimeOptions, } from "./runtime/resolver.js";
|
|
32
33
|
export { assertRequiredSecretsPresent, listMissingRequiredSecrets, MISSING_SECRET_CODE, } from "./runtime/secrets.js";
|
|
33
34
|
export { createUnsupportedProviderRuntimeState, UnsupportedProviderStateError, } from "./runtime/state.js";
|
|
34
35
|
export { createStealthClient } from "./runtime/stealth.js";
|
|
@@ -39,7 +40,7 @@ export { APIFUSE_DESCRIPTION_KEY_META_KEY, APIFUSE_REDACTION_MARKER, APIFUSE_SEN
|
|
|
39
40
|
export { createServerApp, ERROR_OBSERVABILITY_HEADER, type ServeOptions, serve, } from "./server/index.js";
|
|
40
41
|
export { getStealthProfile, listStealthProfiles } from "./stealth/profiles.js";
|
|
41
42
|
export * from "./stream.js";
|
|
42
|
-
export type { ApiFuseResponse, AuthConfig, AuthContext, AuthFlowDefinition, AuthFlowInputHandler, AuthFlowStartHandler, AuthMode, AuthTurn, Bcp47Locale, BrowserEngine, BrowserOptions, BrowserResourceBody, BrowserResourceDecision, BrowserResourceMethod, BrowserResourcePolicy, BrowserResourceRequest, BrowserResourceRoute, ConnectionMode, ContextDeclaration, CookieJar, ContextScratchpad, CredentialContext, CredentialDeclaration, E164PhoneNumber, EnvContext, FlowContext, FlowContextStore, HealthCheckAssertionContext, HealthCheckCase, HealthCheckCaseResult, HealthCheckSuite, HealthCheckUnsupported, HealthJourneyDefinition, HealthJourneyEventContext, HealthJourneyGatewayContext, HealthJourneyJournalContext, HealthJourneyManualTriggerPolicy, HealthJourneyRunContext, HealthJourneyRunResult, HealthJourneySchedule, HealthScheduleRandomization, HealthJourneySmsContext, HealthJourneyStep, HttpClient, HttpMethod, HttpRedirectFailureReason, HttpRedirectPolicy, HttpRedirectPolicyMode, HttpResponse, HttpRetryOptions, HttpRetrySummary, HttpStreamResponse, IanaTimeZone, InferSchemaOutput, Iso3166Alpha2CountryCode, Iso4217CurrencyCode, Iso8601Duration, NativeContext, NativeNetworkClient, NativeNetworkCloseReason, NativeNetworkConnection, NativeNetworkConnectInput, NativeNetworkConnectOptions, NativeNetworkDynamicGrantOptions, NativeNetworkEgressGrant, NativeProviderConfig, NativeProviderContext, NativeProxyDrainHandler, NativeProxyEgressInfo, NativeProxyExpiringEvent, NativeProxyExpiringReason, NativeTcpDynamicEgressRule, NativeTcpEgressGrant, NativeTcpEgressRule, NativeTcpPortRange, NativeTcpTlsMode, NativeTlsConnectOptions, OcrCaptchaCandidate, OcrCaptchaOptions, OcrCaptchaResult, OcrContext, OcrImageInput, OcrRecognizeRequest, OcrResult, OcrWarning, OperationAnnotations, OperationApprovalPolicy, OperationContractMetadata, OperationDefinition, OperationDeprecationMetadata, OperationDocMeta, OperationErrorCode, ProviderErrorStatus, OperationHandlerResult, OperationInputExample, OperationLifecycle, OperationObservabilityConfig, OperationObservabilitySensitiveConfig, OperationRelationships, OperationRiskClass, OperationSensitivePath, OperationToolRouterMetadata, OperationTransport, OperationTransportKind, ProbeInterval, ProviderAccessConfig, ProviderAccessVisibility, ProviderCache, ProviderCacheGetOrSetOptions, ProviderCacheKeyOptions, ProviderCacheLookupMeta, ProviderCacheResponseMeta, ProviderCacheResult, ProviderChoiceBindingOptions, ProviderChoiceContext, ProviderChoiceIssueOptions, ProviderChoiceParseOptions, ProviderContext, ProviderDefinition, ProviderDeploymentOverrides, ProviderFileRef, ProviderFilesContext, ProviderHealthMonitorConfig, ProviderHealthProbeConfig, ProviderLocale, ProviderLocaleKey, ProviderLocaleKeyInput, ProviderLogoProfile, ProviderLogoSource, ProviderMeta, ProviderOcrConfig, ProviderProxyConfig, ProviderProxyMode, ProviderProxyPolicy, ProxiedOAuthConfig, ProviderProxyProvider, ProviderProxySessionAffinity, ProviderPublicConnectionMode, ProviderPublicProfile, ProviderReviewed, ProviderResolvedFile, ProviderRuntimeState, ProviderSecretDeclaration, ProviderStateDurationString, ProviderStateNamespace, ProviderStreamEvent, ProviderSttConfig, ProviderSttMode, ProviderSupportLevel, RequestOptions, RedirectRunReason, Rfc3339Instant, SchemaLike, SmsOrigin, SmsOtpExtractionPattern, SmsOtpMatcherDefinition, SseMessage, StandardSchemaV1, StateCasResult, StateNamespaceOptions, StateValue, StateWriteOptions, StealthClient, StealthCookieStore, StealthCookieStoreV1, StealthFetchOptions, StealthPlatform, StealthProfile, StealthRedirectHop, StealthRedirectRunOptions, StealthRedirectRunResult, StealthResponse, StealthSession, StealthSessionCookies, SttAudioInput, SttContext, SttPromptPolicy, SttSegment, SttTranscribeMode, SttTranscribeRequest, SttTranscript, SttUnsupportedOptionPolicy, SttUsage, SttVerificationCodeOptions, SttWarning, TraceConfig, TraceSpan, VerificationCodeCandidate, VerificationCodeCandidateSource, VerificationCodeExtractionResult, } from "./types.js";
|
|
43
|
+
export type { ApiFuseResponse, AuthConfig, AuthContext, AuthFlowDefinition, AuthFlowInputHandler, AuthFlowStartHandler, AuthMode, AuthTurn, Bcp47Locale, BrowserCookie, BrowserEngine, BrowserOptions, BrowserResourceBody, BrowserResourceDecision, BrowserResourceMethod, BrowserResourcePolicy, BrowserResourceRequest, BrowserResourceRoute, ChallengeSolution, ConnectionMode, ContextDeclaration, CookieJar, ContextScratchpad, CredentialContext, CredentialDeclaration, E164PhoneNumber, EnvContext, FlowContext, FlowContextStore, HealthCheckAssertionContext, HealthCheckCase, HealthCheckCaseResult, HealthCheckSuite, HealthCheckUnsupported, HealthJourneyDefinition, HealthJourneyEventContext, HealthJourneyGatewayContext, HealthJourneyJournalContext, HealthJourneyManualTriggerPolicy, HealthJourneyRunContext, HealthJourneyRunResult, HealthJourneySchedule, HealthScheduleRandomization, HealthJourneySmsContext, HealthJourneyStep, HttpClient, HttpMethod, HttpRedirectFailureReason, HttpRedirectPolicy, HttpRedirectPolicyMode, HttpResponse, HttpRetryOptions, HttpRetrySummary, HttpStreamResponse, IanaTimeZone, InferSchemaOutput, Iso3166Alpha2CountryCode, Iso4217CurrencyCode, Iso8601Duration, NativeContext, NativeNetworkClient, NativeNetworkCloseReason, NativeNetworkConnection, NativeNetworkConnectInput, NativeNetworkConnectOptions, NativeNetworkDynamicGrantOptions, NativeNetworkEgressGrant, NativeProviderConfig, NativeProviderContext, NativeProxyDrainHandler, NativeProxyEgressInfo, NativeProxyExpiringEvent, NativeProxyExpiringReason, NativeTcpDynamicEgressRule, NativeTcpEgressGrant, NativeTcpEgressRule, NativeTcpPortRange, NativeTcpTlsMode, NativeTlsConnectOptions, OcrCaptchaCandidate, OcrCaptchaOptions, OcrCaptchaResult, OcrContext, OcrImageInput, OcrRecognizeRequest, OcrResult, OcrWarning, OperationAnnotations, OperationApprovalPolicy, OperationContractMetadata, OperationDefinition, OperationDeprecationMetadata, OperationDocMeta, OperationErrorCode, ProviderErrorStatus, OperationHandlerResult, OperationInputExample, OperationLifecycle, OperationObservabilityConfig, OperationObservabilitySensitiveConfig, OperationRelationships, OperationRiskClass, OperationSensitivePath, OperationToolRouterMetadata, OperationTransport, OperationTransportKind, ProbeInterval, ProviderAccessConfig, ProviderAccessVisibility, ProviderCache, ProviderCacheGetOrSetOptions, ProviderCacheKeyOptions, ProviderCacheLookupMeta, ProviderCacheResponseMeta, ProviderCacheResult, ProviderChoiceBindingOptions, ProviderChoiceContext, ProviderChoiceIssueOptions, ProviderChoiceParseOptions, ProviderChallenge, ProviderChallengeKind, ProviderContext, ProviderDefinition, ProviderDeploymentOverrides, ProviderFileRef, ProviderFilesContext, ProviderHealthMonitorConfig, ProviderHealthProbeConfig, ProviderLocale, ProviderLocaleKey, ProviderLocaleKeyInput, ProviderLogoProfile, ProviderLogoSource, ProviderMeta, ProviderOcrConfig, ProviderProxyConfig, ProviderProxyMode, ProviderProxyPolicy, ProxiedOAuthConfig, ProviderProxyProvider, ProviderProxySessionAffinity, ProviderPublicConnectionMode, ProviderPublicProfile, ProviderReviewed, ProviderResolvedFile, ProviderResolverConfig, ProviderResolverVendor, ProviderRuntimeState, ProviderSecretDeclaration, ProviderStateDurationString, ProviderStateNamespace, ProviderStreamEvent, ProviderSttConfig, ProviderSttMode, ProviderSupportLevel, RequestOptions, RedirectRunReason, ResolverContext, Rfc3339Instant, SchemaLike, SmsOrigin, SmsOtpExtractionPattern, SmsOtpMatcherDefinition, SseMessage, StandardSchemaV1, StateCasResult, StateNamespaceOptions, StateNamespaceScope, StateValue, StateWriteOptions, StealthClient, StealthCookieStore, StealthCookieStoreV1, StealthFetchOptions, StealthPlatform, StealthProfile, StealthRedirectHop, StealthRedirectRunOptions, StealthRedirectRunResult, StealthResponse, StealthSession, StealthSessionCookies, SttAudioInput, SttContext, SttPromptPolicy, SttSegment, SttTranscribeMode, SttTranscribeRequest, SttTranscript, SttUnsupportedOptionPolicy, SttUsage, SttVerificationCodeOptions, SttWarning, TraceConfig, TraceSpan, VerificationCodeCandidate, VerificationCodeCandidateSource, VerificationCodeExtractionResult, } from "./types.js";
|
|
43
44
|
export { DEFAULT_OPERATION_TRANSPORT, HttpRetryAfterPolicy, HttpRetryDelayStrategy, HttpRetryJitter, HttpRetryPreset, HttpRetryUnsafeMethodPolicy, PROBE_INTERVALS, STREAM_CHUNK_BYTES_MAX, STREAM_CHUNK_BYTES_MIN, STREAM_HEARTBEAT_MS_MAX, STREAM_HEARTBEAT_MS_MIN, STREAM_IDLE_TIMEOUT_MS_MAX, STREAM_IDLE_TIMEOUT_MS_MIN, STREAM_MAX_DURATION_MS_MAX, STREAM_MAX_DURATION_MS_MIN, } from "./types.js";
|
|
44
45
|
export * from "./utils/date.js";
|
|
45
46
|
export * from "./utils/parse.js";
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,7 @@ export { generateInsights } from "./runtime/insights.js";
|
|
|
26
26
|
export { wrapWithInstrumentation, } from "./runtime/instrumentation.js";
|
|
27
27
|
export { prevalidate } from "./runtime/prevalidate.js";
|
|
28
28
|
export { getProviderBaseUrl } from "./runtime/provider.js";
|
|
29
|
+
export { APIFUSE__CDP_POOL__URL, APIFUSE__RESOLVER__2CAPTCHA__API_KEY, APIFUSE__RESOLVER__CAPMONSTER__API_KEY, APIFUSE__RESOLVER__CAPSOLVER__API_KEY, APIFUSE__RESOLVER__TIMEOUT_MS, createResolverClientFromEnv, createUnsupportedResolverClient, DEFAULT_RESOLVER_TIMEOUT_MS, invalidateResolverSolution, } from "./runtime/resolver.js";
|
|
29
30
|
export { assertRequiredSecretsPresent, listMissingRequiredSecrets, MISSING_SECRET_CODE, } from "./runtime/secrets.js";
|
|
30
31
|
export { createUnsupportedProviderRuntimeState, UnsupportedProviderStateError, } from "./runtime/state.js";
|
|
31
32
|
export { createStealthClient } from "./runtime/stealth.js";
|
package/dist/provider.d.ts
CHANGED
|
@@ -7,6 +7,6 @@ export { AuthError, HttpRedirectError, isProviderError, isSessionExpiredError, i
|
|
|
7
7
|
export { getProviderLocalePath, providerLocaleKey, qualifyProviderLocaleKey, } from "./i18n/index.js";
|
|
8
8
|
export { type CreateProviderChoiceContextOptions, createProviderChoiceContext, createTestProviderChoiceContext, PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV, } from "./runtime/choice.js";
|
|
9
9
|
export { APIFUSE_DESCRIPTION_KEY_META_KEY, APIFUSE_REDACTION_MARKER, APIFUSE_SENSITIVE_KIND_META_KEY, APIFUSE_SENSITIVE_META_KEY, collectSensitivePaths, describeKey, field, fields, isSensitiveSchema, redactPayload, type SensitiveFieldKind, type SensitiveFieldOptions, type SensitivePath, sensitive, z, } from "./schema.js";
|
|
10
|
-
export type { AuthAbortData, AuthAbortRetry, AuthFlowTerminalContext, AuthMode, AuthSafeData, AuthSafeJson, FlowContext, HealthCheckAssertionContext, HealthCheckCase, HealthCheckSuite, HealthCheckUnsupported, HealthJourneyDefinition, HealthJourneyEventContext, HealthJourneyManualTriggerPolicy, HealthJourneyRunContext, HealthJourneyRunResult, HealthScheduleRandomization, HttpRedirectFailureReason, HttpRedirectPolicy, HttpRedirectPolicyMode, HttpRetryOptions, HttpRetrySummary, InferSchemaOutput, NativeContext, NativeNetworkClient, NativeNetworkCloseReason, NativeNetworkConnection, NativeNetworkConnectInput, NativeNetworkConnectOptions, NativeNetworkDynamicGrantOptions, NativeNetworkEgressGrant, NativeProviderConfig, NativeProviderContext, NativeProxyDrainHandler, NativeProxyEgressInfo, NativeProxyExpiringEvent, NativeProxyExpiringReason, NativeTcpDynamicEgressRule, NativeTcpEgressGrant, NativeTcpEgressRule, NativeTcpPortRange, NativeTcpTlsMode, NativeTlsConnectOptions, OperationApprovalPolicy, OperationContractMetadata, OperationDefinition, OperationDocMeta, OperationErrorCode, ProviderErrorStatus, OperationInputExample, OperationLifecycle, OperationObservabilityConfig, OperationObservabilitySensitiveConfig, OperationRelationships, OperationRiskClass, OperationSensitivePath, OperationToolRouterMetadata, OperationTransport, ProviderAccessVisibility, ProviderChoiceBindingOptions, ProviderChoiceContext, ProviderChoiceIssueOptions, ProviderChoiceParseOptions, ProviderContext, ProviderDefinition, ProviderDeploymentOverrides, ProviderFileRef, ProviderFilesContext, ProviderLocale, ProviderLocaleKey, ProviderLocaleKeyInput, ProviderLogoProfile, ProviderProxyPolicy, ProxiedOAuthConfig, ProviderPublicConnectionMode, ProviderPublicProfile, ProviderResolvedFile, ProviderRuntimeState, ProviderStateDurationString, ProviderStateNamespace, ProviderSupportLevel, RedirectRunReason, SchemaLike, SmsOtpMatcherDefinition, StandardSchemaV1, StateCasResult, StateNamespaceOptions, StateValue, StateWriteOptions, } from "./types.js";
|
|
10
|
+
export type { AuthAbortData, AuthAbortRetry, AuthFlowTerminalContext, AuthMode, AuthSafeData, AuthSafeJson, FlowContext, HealthCheckAssertionContext, HealthCheckCase, HealthCheckSuite, HealthCheckUnsupported, HealthJourneyDefinition, HealthJourneyEventContext, HealthJourneyManualTriggerPolicy, HealthJourneyRunContext, HealthJourneyRunResult, HealthScheduleRandomization, HttpRedirectFailureReason, HttpRedirectPolicy, HttpRedirectPolicyMode, HttpRetryOptions, HttpRetrySummary, InferSchemaOutput, NativeContext, NativeNetworkClient, NativeNetworkCloseReason, NativeNetworkConnection, NativeNetworkConnectInput, NativeNetworkConnectOptions, NativeNetworkDynamicGrantOptions, NativeNetworkEgressGrant, NativeProviderConfig, NativeProviderContext, NativeProxyDrainHandler, NativeProxyEgressInfo, NativeProxyExpiringEvent, NativeProxyExpiringReason, NativeTcpDynamicEgressRule, NativeTcpEgressGrant, NativeTcpEgressRule, NativeTcpPortRange, NativeTcpTlsMode, NativeTlsConnectOptions, OperationApprovalPolicy, OperationContractMetadata, OperationDefinition, OperationDocMeta, OperationErrorCode, ProviderErrorStatus, OperationInputExample, OperationLifecycle, OperationObservabilityConfig, OperationObservabilitySensitiveConfig, OperationRelationships, OperationRiskClass, OperationSensitivePath, OperationToolRouterMetadata, OperationTransport, ProviderAccessVisibility, ProviderChoiceBindingOptions, ProviderChoiceContext, ProviderChoiceIssueOptions, ProviderChoiceParseOptions, ProviderContext, ProviderDefinition, ProviderDeploymentOverrides, ProviderFileRef, ProviderFilesContext, ProviderLocale, ProviderLocaleKey, ProviderLocaleKeyInput, ProviderLogoProfile, ProviderProxyPolicy, ProxiedOAuthConfig, ProviderPublicConnectionMode, ProviderPublicProfile, ProviderResolvedFile, ProviderRuntimeState, ProviderStateDurationString, ProviderStateNamespace, ProviderSupportLevel, RedirectRunReason, SchemaLike, SmsOtpMatcherDefinition, StandardSchemaV1, StateCasResult, StateNamespaceOptions, StateNamespaceScope, StateValue, StateWriteOptions, } from "./types.js";
|
|
11
11
|
export { createNativeNetworkClient, createEnvVendorCredentialResolver, deriveNativeCredentialAffinityKey, NativeEgressGrantExpiredError, NativeEgressNotDeclaredError, NativeIdleTimeoutError, NativeNetworkError, NativeProxyExpiredError, resolveNativeGatewayProxy, type NativeGatewayProxy, type NativeGatewayProxyResolutionInput, type NativeGatewayProxySkipReason, type NativeGatewayProxySynthesizer, type NativeGatewayProxySynthesisResult, type NativeGatewayProxySynthesisInput, type NativeNetworkClientOptions, type NativeNetworkErrorCode, type VendorCredentialLookup, type VendorCredentialResolver, } from "./runtime/native-network.js";
|
|
12
12
|
export { HttpRetryAfterPolicy, HttpRetryDelayStrategy, HttpRetryJitter, HttpRetryPreset, HttpRetryUnsafeMethodPolicy, } from "./types.js";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ContextAccessError } from "../errors.js";
|
|
2
2
|
import { createAuthFlowHelpers } from "../auth.js";
|
|
3
3
|
import { createUnsupportedOcrClient } from "./ocr.js";
|
|
4
|
+
import { createUnsupportedResolverClient } from "./resolver.js";
|
|
4
5
|
import { createUnsupportedSttClient } from "./stt.js";
|
|
5
6
|
function normalizeAllowedKeys(allowedKeys) {
|
|
6
7
|
return new Set(allowedKeys.filter((key) => key.trim().length > 0));
|
|
@@ -44,6 +45,7 @@ export function createFlowContext(options) {
|
|
|
44
45
|
context: createScratchpad(options.allowedKeys, options.initialContext),
|
|
45
46
|
ocr: options.ocr ?? createUnsupportedOcrClient(),
|
|
46
47
|
stt: options.stt ?? createUnsupportedSttClient(),
|
|
48
|
+
resolver: createUnsupportedResolverClient("Resolver is not available in auth flow context"),
|
|
47
49
|
auth: createAuthFlowHelpers(),
|
|
48
50
|
};
|
|
49
51
|
}
|
package/dist/runtime/browser.js
CHANGED
|
@@ -357,6 +357,9 @@ class PlaywrightBrowserPage {
|
|
|
357
357
|
async close() {
|
|
358
358
|
await this.page.close();
|
|
359
359
|
}
|
|
360
|
+
async cookies() {
|
|
361
|
+
return (await this.page.context().cookies()).map(toBrowserCookie);
|
|
362
|
+
}
|
|
360
363
|
async withResourcePolicy(policy, run) {
|
|
361
364
|
const allowedMethods = new Set(policy.allowedMethods ?? DEFAULT_RESOURCE_METHODS);
|
|
362
365
|
const handler = async (route) => {
|
|
@@ -586,6 +589,48 @@ function flattenCdpFrameTree(node, out = []) {
|
|
|
586
589
|
function isRecord(value) {
|
|
587
590
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
588
591
|
}
|
|
592
|
+
function isBrowserCookieSameSite(value) {
|
|
593
|
+
return value === "Strict" || value === "Lax" || value === "None";
|
|
594
|
+
}
|
|
595
|
+
function toBrowserCookie(cookie) {
|
|
596
|
+
return {
|
|
597
|
+
name: cookie.name,
|
|
598
|
+
value: cookie.value,
|
|
599
|
+
domain: cookie.domain,
|
|
600
|
+
path: cookie.path,
|
|
601
|
+
...(cookie.expires !== undefined && cookie.expires > 0 ? { expires: cookie.expires } : {}),
|
|
602
|
+
httpOnly: cookie.httpOnly,
|
|
603
|
+
secure: cookie.secure,
|
|
604
|
+
...(isBrowserCookieSameSite(cookie.sameSite) ? { sameSite: cookie.sameSite } : {}),
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
function parseCdpCookies(value) {
|
|
608
|
+
if (!Array.isArray(value)) {
|
|
609
|
+
throw new Error("CDP Network.getCookies returned an invalid cookie list");
|
|
610
|
+
}
|
|
611
|
+
return value.map((cookie) => {
|
|
612
|
+
if (!isRecord(cookie) ||
|
|
613
|
+
typeof cookie.name !== "string" ||
|
|
614
|
+
typeof cookie.value !== "string" ||
|
|
615
|
+
typeof cookie.domain !== "string" ||
|
|
616
|
+
typeof cookie.path !== "string" ||
|
|
617
|
+
typeof cookie.expires !== "number" ||
|
|
618
|
+
typeof cookie.httpOnly !== "boolean" ||
|
|
619
|
+
typeof cookie.secure !== "boolean") {
|
|
620
|
+
throw new Error("CDP Network.getCookies returned an invalid cookie");
|
|
621
|
+
}
|
|
622
|
+
return toBrowserCookie({
|
|
623
|
+
name: cookie.name,
|
|
624
|
+
value: cookie.value,
|
|
625
|
+
domain: cookie.domain,
|
|
626
|
+
path: cookie.path,
|
|
627
|
+
expires: cookie.expires,
|
|
628
|
+
httpOnly: cookie.httpOnly,
|
|
629
|
+
secure: cookie.secure,
|
|
630
|
+
sameSite: cookie.sameSite,
|
|
631
|
+
});
|
|
632
|
+
});
|
|
633
|
+
}
|
|
589
634
|
function parsePoolAcquireResponse(value) {
|
|
590
635
|
if (!isRecord(value) ||
|
|
591
636
|
typeof value.pageId !== "string" ||
|
|
@@ -864,6 +909,11 @@ class CdpPoolBrowserPage {
|
|
|
864
909
|
});
|
|
865
910
|
return Buffer.from(String(result.data ?? ""), "base64");
|
|
866
911
|
}
|
|
912
|
+
async cookies() {
|
|
913
|
+
await this.initialize();
|
|
914
|
+
const result = await this.pageClient.send("Network.getCookies");
|
|
915
|
+
return parseCdpCookies(result.cookies);
|
|
916
|
+
}
|
|
867
917
|
async close() {
|
|
868
918
|
if (this.closed) {
|
|
869
919
|
return;
|
package/dist/runtime/http.js
CHANGED
|
@@ -376,7 +376,6 @@ async function resolveNativeProxy(options, clientOptions, warn, proxyAttemptOffs
|
|
|
376
376
|
const resolvedProxy = await resolveProxyConfigAsync({
|
|
377
377
|
proxy: options.proxy ?? clientOptions.proxy,
|
|
378
378
|
upstream: clientOptions.upstream,
|
|
379
|
-
apifuseConfig: clientOptions.apifuseConfig,
|
|
380
379
|
proxyPolicy: clientOptions.proxyPolicy,
|
|
381
380
|
affinityKey: clientOptions.affinityKey,
|
|
382
381
|
proxyAttempt: computeProxyAttemptIndex({
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readableBytes, readableLines, readableTextChunks } from "../stream.js";
|
|
2
2
|
import { parseHttpRequestInvocation, isSensitiveKey, redactSensitiveError, redactSensitiveText, redactUrlQueryParams, requestOptionsFromHttpInvocation, serializeRequestUrl, } from "./request-options.js";
|
|
3
3
|
import { createTraceContext, getTraceRecorder, } from "./trace.js";
|
|
4
|
+
import { RESOLVER_INSTRUMENTATION_METADATA } from "./resolver.js";
|
|
4
5
|
const BROWSER_PAGE_METHODS = new Set(["goto", "fill", "click", "type", "waitForSelector"]);
|
|
5
6
|
const DIAGNOSTIC_BASE_URL = "http://apifuse-instrumentation.invalid";
|
|
6
7
|
function isThenable(value) {
|
|
@@ -420,12 +421,35 @@ function wrapNamespace(namespace, target, trace, shouldInstrument) {
|
|
|
420
421
|
return target;
|
|
421
422
|
}
|
|
422
423
|
const wrappedMethods = new Map();
|
|
424
|
+
const resolverMetadata = namespace === "resolver" ? { target, traceRecorder: recorder } : undefined;
|
|
423
425
|
return new Proxy(target, {
|
|
424
426
|
get(namespaceTarget, property, receiver) {
|
|
427
|
+
if (property === RESOLVER_INSTRUMENTATION_METADATA && resolverMetadata) {
|
|
428
|
+
return resolverMetadata;
|
|
429
|
+
}
|
|
425
430
|
const value = Reflect.get(namespaceTarget, property, receiver);
|
|
426
431
|
if (typeof value !== "function" || property === "constructor") {
|
|
427
432
|
return value;
|
|
428
433
|
}
|
|
434
|
+
if (namespace === "resolver" && property === "solve") {
|
|
435
|
+
if (wrappedMethods.has(property)) {
|
|
436
|
+
return wrappedMethods.get(property);
|
|
437
|
+
}
|
|
438
|
+
const wrapped = (...args) => {
|
|
439
|
+
const challenge = args[0];
|
|
440
|
+
const challengeKind = typeof challenge === "object" &&
|
|
441
|
+
challenge !== null &&
|
|
442
|
+
"kind" in challenge &&
|
|
443
|
+
typeof challenge.kind === "string"
|
|
444
|
+
? challenge.kind
|
|
445
|
+
: undefined;
|
|
446
|
+
return recorder.runSpan("resolver.solve", () => Reflect.apply(value, namespaceTarget, [args[0], args[1], recorder]), {
|
|
447
|
+
attributes: challengeKind ? { challenge_kind: challengeKind } : undefined,
|
|
448
|
+
});
|
|
449
|
+
};
|
|
450
|
+
wrappedMethods.set(property, wrapped);
|
|
451
|
+
return wrapped;
|
|
452
|
+
}
|
|
429
453
|
if (namespace === "browser" && property === "newPage") {
|
|
430
454
|
if (wrappedMethods.has(property)) {
|
|
431
455
|
return wrappedMethods.get(property);
|
|
@@ -573,7 +597,8 @@ export function wrapWithInstrumentation(ctx, options = {}) {
|
|
|
573
597
|
property === "stealth" ||
|
|
574
598
|
property === "browser" ||
|
|
575
599
|
property === "session" ||
|
|
576
|
-
property === "state"
|
|
600
|
+
property === "state" ||
|
|
601
|
+
property === "resolver") {
|
|
577
602
|
const namespace = property;
|
|
578
603
|
if (wrappedTargets.has(namespace)) {
|
|
579
604
|
return wrappedTargets.get(namespace);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ProviderChallenge } from "../../types.js";
|
|
2
|
+
import type { ResolverIssuingIdentity } from "./types.js";
|
|
3
|
+
export declare const RESOLVER_CHALLENGE_BINDINGS: {
|
|
4
|
+
readonly aws_waf: "portable";
|
|
5
|
+
readonly cloudflare_interstitial: "identity_scoped";
|
|
6
|
+
};
|
|
7
|
+
export declare function resolverChallengeIsIdentityScoped(challenge: ProviderChallenge): boolean;
|
|
8
|
+
export declare function resolverChallengeIssuingIdentity(challenge: ProviderChallenge, identity: ResolverIssuingIdentity): ResolverIssuingIdentity;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const RESOLVER_CHALLENGE_BINDINGS = {
|
|
2
|
+
aws_waf: "portable",
|
|
3
|
+
cloudflare_interstitial: "identity_scoped",
|
|
4
|
+
};
|
|
5
|
+
export function resolverChallengeIsIdentityScoped(challenge) {
|
|
6
|
+
return (RESOLVER_CHALLENGE_BINDINGS[challenge.kind] ===
|
|
7
|
+
"identity_scoped");
|
|
8
|
+
}
|
|
9
|
+
export function resolverChallengeIssuingIdentity(challenge, identity) {
|
|
10
|
+
const binding = RESOLVER_CHALLENGE_BINDINGS[challenge.kind];
|
|
11
|
+
if (binding === "portable") {
|
|
12
|
+
return { userAgent: identity.userAgent };
|
|
13
|
+
}
|
|
14
|
+
return identity;
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { BrowserClient, ChallengeSolution, ProviderChallenge } from "../../types.js";
|
|
2
|
+
import { type BrowserClientOptions } from "../browser.js";
|
|
3
|
+
import type { TraceRecorder } from "../trace.js";
|
|
4
|
+
import { type ResolverIdentity, type ResolverVendorAdapter } from "./types.js";
|
|
5
|
+
type BrowserClientFactory = (options: BrowserClientOptions) => BrowserClient;
|
|
6
|
+
export interface BrowserResolverVendorOptions {
|
|
7
|
+
readonly cdpUrl?: string;
|
|
8
|
+
readonly timeoutMs: number;
|
|
9
|
+
readonly pollIntervalMs?: number;
|
|
10
|
+
readonly allowedHosts: readonly string[];
|
|
11
|
+
readonly createClient?: BrowserClientFactory;
|
|
12
|
+
}
|
|
13
|
+
export type BrowserResolverSolution = Extract<ChallengeSolution, {
|
|
14
|
+
readonly form: "cookies";
|
|
15
|
+
}> & {
|
|
16
|
+
/** Unix seconds from the cookie that proved the challenge cleared. */
|
|
17
|
+
readonly expires?: number;
|
|
18
|
+
};
|
|
19
|
+
export interface BrowserResolverVendorAdapter extends ResolverVendorAdapter {
|
|
20
|
+
readonly id: "browser";
|
|
21
|
+
solve(challenge: ProviderChallenge, identity: ResolverIdentity | undefined, signal: AbortSignal, traceRecorder?: TraceRecorder): Promise<BrowserResolverSolution>;
|
|
22
|
+
}
|
|
23
|
+
export declare function createBrowserResolverVendorAdapter(options: BrowserResolverVendorOptions): BrowserResolverVendorAdapter;
|
|
24
|
+
export {};
|