@apifuse/provider-sdk 2.2.0-beta.57 → 2.2.0-beta.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/AUTHORING.md +9 -0
  2. package/CHANGELOG.md +8 -0
  3. package/README.md +22 -5
  4. package/SUBMISSION.md +2 -1
  5. package/bin/apifuse-pack-types.ts +49 -0
  6. package/bin/apifuse-record.ts +6 -0
  7. package/dist/cli/templates/provider/README.md.tpl +19 -5
  8. package/dist/config/loader.d.ts +7 -0
  9. package/dist/config/loader.js +30 -6
  10. package/dist/declaration-validation.d.ts +2 -1
  11. package/dist/declaration-validation.js +39 -0
  12. package/dist/define.d.ts +5 -4
  13. package/dist/define.js +25 -1
  14. package/dist/engine.d.ts +16 -0
  15. package/dist/engine.js +58 -1
  16. package/dist/error-resolution.js +13 -0
  17. package/dist/index.d.ts +6 -4
  18. package/dist/index.js +4 -2
  19. package/dist/lint.js +3 -3
  20. package/dist/provider.d.ts +3 -3
  21. package/dist/provider.js +2 -2
  22. package/dist/runtime/bounded-expiring-map.d.ts +21 -0
  23. package/dist/runtime/bounded-expiring-map.js +64 -0
  24. package/dist/runtime/cache.d.ts +2 -0
  25. package/dist/runtime/cache.js +8 -1
  26. package/dist/runtime/egress-lease.d.ts +63 -0
  27. package/dist/runtime/egress-lease.js +233 -0
  28. package/dist/runtime/http.js +1 -0
  29. package/dist/runtime/proxy-retry-policy.d.ts +4 -1
  30. package/dist/runtime/proxy-retry-policy.js +11 -2
  31. package/dist/runtime/request-telemetry.d.ts +7 -9
  32. package/dist/runtime/resolver-config.d.ts +7 -0
  33. package/dist/runtime/resolver-config.js +8 -0
  34. package/dist/runtime/resolver-public.d.ts +3 -1
  35. package/dist/runtime/resolver-public.js +1 -1
  36. package/dist/runtime/resolver-shared.d.ts +9 -0
  37. package/dist/runtime/resolver-shared.js +57 -0
  38. package/dist/runtime/resolver-telemetry.d.ts +134 -0
  39. package/dist/runtime/resolver-telemetry.js +204 -0
  40. package/dist/runtime/resolver-usage.d.ts +24 -0
  41. package/dist/runtime/resolver-usage.js +60 -0
  42. package/dist/runtime/resolver-vendors/bindings.d.ts +5 -0
  43. package/dist/runtime/resolver-vendors/bindings.js +5 -0
  44. package/dist/runtime/resolver-vendors/capsolver.d.ts +2 -2
  45. package/dist/runtime/resolver-vendors/capsolver.js +21 -9
  46. package/dist/runtime/resolver-vendors/hypersolutions.d.ts +27 -0
  47. package/dist/runtime/resolver-vendors/hypersolutions.js +369 -0
  48. package/dist/runtime/resolver-vendors/twocaptcha.d.ts +2 -2
  49. package/dist/runtime/resolver-vendors/twocaptcha.js +26 -10
  50. package/dist/runtime/resolver-vendors/types.d.ts +35 -5
  51. package/dist/runtime/resolver-vendors/types.js +20 -10
  52. package/dist/runtime/resolver.d.ts +5 -1
  53. package/dist/runtime/resolver.js +397 -109
  54. package/dist/runtime/stealth-akamai-sbsd.d.ts +54 -0
  55. package/dist/runtime/stealth-akamai-sbsd.js +142 -0
  56. package/dist/runtime/stealth.js +547 -53
  57. package/dist/schema.d.ts +9 -0
  58. package/dist/schema.js +16 -0
  59. package/dist/server/index.d.ts +2 -0
  60. package/dist/server/index.js +1 -0
  61. package/dist/server/self-test.d.ts +4 -0
  62. package/dist/server/self-test.js +10 -1
  63. package/dist/server/serve-implementation.d.ts +7 -1
  64. package/dist/server/serve-implementation.js +181 -53
  65. package/dist/server/types.d.ts +6 -0
  66. package/dist/server/types.js +14 -0
  67. package/dist/stealth/profiles.d.ts +7 -1
  68. package/dist/stealth/profiles.js +9 -0
  69. package/dist/testing/run.js +6 -0
  70. package/dist/types.d.ts +115 -12
  71. package/package.json +1 -1
  72. package/src/cli/templates/provider/README.md.tpl +19 -5
  73. package/src/config/loader.ts +34 -6
  74. package/src/declaration-validation.ts +47 -1
  75. package/src/define.ts +46 -3
  76. package/src/engine.ts +76 -1
  77. package/src/error-resolution.ts +13 -0
  78. package/src/index.ts +32 -1
  79. package/src/lint.ts +3 -3
  80. package/src/provider.ts +9 -0
  81. package/src/runtime/bounded-expiring-map.ts +67 -0
  82. package/src/runtime/cache.ts +10 -1
  83. package/src/runtime/egress-lease.ts +336 -0
  84. package/src/runtime/http.ts +1 -0
  85. package/src/runtime/proxy-retry-policy.ts +12 -1
  86. package/src/runtime/request-telemetry.ts +5 -7
  87. package/src/runtime/resolver-config.ts +12 -0
  88. package/src/runtime/resolver-public.ts +21 -0
  89. package/src/runtime/resolver-shared.ts +75 -0
  90. package/src/runtime/resolver-telemetry.ts +362 -0
  91. package/src/runtime/resolver-usage.ts +96 -0
  92. package/src/runtime/resolver-vendors/bindings.ts +5 -0
  93. package/src/runtime/resolver-vendors/capsolver.ts +22 -3
  94. package/src/runtime/resolver-vendors/hypersolutions.ts +480 -0
  95. package/src/runtime/resolver-vendors/twocaptcha.ts +26 -12
  96. package/src/runtime/resolver-vendors/types.ts +60 -12
  97. package/src/runtime/resolver.ts +465 -124
  98. package/src/runtime/stealth-akamai-sbsd.ts +207 -0
  99. package/src/runtime/stealth.ts +727 -95
  100. package/src/schema.ts +17 -0
  101. package/src/server/index.ts +21 -0
  102. package/src/server/self-test.ts +13 -0
  103. package/src/server/serve-implementation.ts +268 -48
  104. package/src/server/types.ts +14 -0
  105. package/src/stealth/profiles.ts +10 -0
  106. package/src/testing/run.ts +6 -0
  107. package/src/types.ts +130 -22
@@ -93,12 +93,26 @@ export const AuthFlowRequestSchema = z.object({
93
93
  headers: z.record(z.string(), z.string()).optional(),
94
94
  input: z.record(z.string(), z.unknown()).optional(),
95
95
  context: z.record(z.string(), z.unknown()).optional(),
96
+ /** Opaque engine state. This is never projected into FlowContext or provider contextPatch. */
97
+ engine: z
98
+ .object({
99
+ egressLease: z.string().min(1).max(16_384).optional(),
100
+ })
101
+ .strict()
102
+ .optional(),
96
103
  connection: OperationConnectionSchema.optional(),
97
104
  });
98
105
 
99
106
  export const AuthFlowSuccessResponseSchema = z.object({
100
107
  data: z.unknown(),
101
108
  contextPatch: z.record(z.string(), z.unknown().nullable()).optional(),
109
+ /** Opaque engine state to carry verbatim to the next auth turn. */
110
+ engine: z
111
+ .object({
112
+ egressLease: z.string().min(1).max(16_384).optional(),
113
+ })
114
+ .strict()
115
+ .optional(),
102
116
  });
103
117
 
104
118
  export const AuthFlowErrorResponseSchema = OperationErrorResponseSchema;
@@ -258,6 +258,16 @@ export function getStealthProfile(selection: StealthProfileSelection = {}): Stea
258
258
  };
259
259
  }
260
260
 
261
+ /**
262
+ * The transport owns the browser version (Chromium follows the pinned wreq-js build), so a
263
+ * resolver `clientProfile` such as `safari17_0` or `chrome_149` only has to name the same
264
+ * browser family as the stealth session it runs on.
265
+ */
266
+ export function resolverClientProfileFamily(clientProfile: string): StealthBrowser | undefined {
267
+ const family = /^[a-z]+/u.exec(clientProfile.trim().toLowerCase())?.[0];
268
+ return family === "chrome" || family === "firefox" || family === "safari" ? family : undefined;
269
+ }
270
+
261
271
  export function listStealthProfiles(): StealthProfileDescriptor[] {
262
272
  return SUPPORTED_STEALTH_PROFILES.map((profile) => ({ ...profile }));
263
273
  }
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, it } from "bun:test";
2
2
 
3
+ import { ProviderError } from "../errors.js";
3
4
  import { createProviderCache } from "../runtime/cache.js";
4
5
  import { createTestProviderChoiceContext } from "../runtime/choice.js";
5
6
  import { createMemoryProviderRuntimeState } from "../runtime/state.js";
@@ -425,6 +426,11 @@ function createUpstreamContext(
425
426
 
426
427
  const createStealthSession = (): ReturnType<ProviderContext["stealth"]["createSession"]> => ({
427
428
  fetch: stealthCall,
429
+ async replayChallenged() {
430
+ throw new ProviderError("Recorded transports cannot replay an engine-owned challenge", {
431
+ code: "REPLAY_SESSION_MISMATCH",
432
+ });
433
+ },
428
434
  cookies: {
429
435
  ...emptyCookieJar,
430
436
  has: () => false,
package/src/types.ts CHANGED
@@ -278,6 +278,7 @@ export type ProviderResolverVendor =
278
278
  | "capsolver"
279
279
  | "capmonster"
280
280
  | "2captcha"
281
+ | "hypersolutions"
281
282
  | "custom";
282
283
 
283
284
  /**
@@ -343,6 +344,23 @@ export type ProviderChallenge =
343
344
  readonly bmsz?: string;
344
345
  /** Bot Manager major version when known ("3" measured on zozo.jp). */
345
346
  readonly version?: string;
347
+ }
348
+ | {
349
+ readonly kind: "akamai_sbsd";
350
+ readonly pageUrl: string;
351
+ /**
352
+ * Discovered SBSD script URL. `?v=&t=` is a hard challenge and `?v=`
353
+ * is passive or a remembered script. A later challenge token never
354
+ * changes this URL.
355
+ */
356
+ readonly scriptUrl: string;
357
+ readonly stateCookieName: "sbsd_o" | "bm_so";
358
+ /**
359
+ * Token returned by a later `cpr_chlge` response. When present with a
360
+ * remembered v-only script, it selects index 0 and is appended only to
361
+ * the payload POST URL.
362
+ */
363
+ readonly challengeToken?: string;
346
364
  };
347
365
 
348
366
  export type ProviderChallengeKind = ProviderChallenge["kind"];
@@ -352,7 +370,9 @@ export type ProviderChallengeKind = ProviderChallenge["kind"];
352
370
  * per challenge kind: `aws_waf` was measured portable across residential leases
353
371
  * on buyee, while `cf_clearance` is unmeasured here and treated as scoped to the
354
372
  * identity that produced it. The provider attaches the returned cookies to its
355
- * own requests.
373
+ * own requests. `cookie_state` solutions carry no values: SBSD state lives only
374
+ * in the engine-owned bound jar, and a 2xx payload POST is not proof of a solve;
375
+ * only the next protected GET can verify it (the safe refetch is Phase 2).
356
376
  */
357
377
  export type ChallengeSolution =
358
378
  | { readonly form: "token"; readonly token: string }
@@ -367,21 +387,44 @@ export type ChallengeSolution =
367
387
  * the upstream cookie's expiry. `expires` takes precedence when both are present.
368
388
  */
369
389
  readonly sdkEstimatedExpires?: number;
390
+ }
391
+ | {
392
+ readonly form: "cookie_state";
393
+ readonly kind: "akamai_sbsd";
394
+ readonly outcome: "payload_accepted";
395
+ /** Always false until a later protected GET verifies the bound jar (Phase 2). */
396
+ readonly verified: false;
397
+ /** Name only; the identity-bound cookie value remains in the engine-owned jar. */
398
+ readonly stateCookieName: "sbsd_o" | "bm_so";
399
+ /** Epoch seconds observed on the updated state cookie, when supplied upstream. */
400
+ readonly expires?: number;
370
401
  };
371
402
 
372
- export interface ProviderResolverConfig {
373
- /** Optional ordered override for the SDK-owned vendor fallback chain. */
374
- readonly vendors?: readonly ProviderResolverVendor[];
375
- /** Challenge kinds this provider is permitted to request. */
376
- readonly kinds: readonly ProviderChallengeKind[];
377
- /**
378
- * Client fingerprint profile the SDK must use when reaching this upstream.
379
- * Measured on zozo.jp: Chrome/Firefox profiles are refused 403 before any
380
- * challenge is served, while a Safari profile is admitted. Provider-declared
381
- * because only the provider knows its upstream's admission rule.
382
- */
383
- readonly clientProfile?: string;
384
- }
403
+ /**
404
+ * `akamai_sbsd` declarations require a transport-owned client profile because the
405
+ * solve runs on the provider's admitted session. Other kinds may omit it.
406
+ */
407
+ export type ProviderResolverConfig =
408
+ | {
409
+ /** Optional ordered override for the SDK-owned vendor fallback chain. */
410
+ readonly vendors?: readonly ProviderResolverVendor[];
411
+ /** Challenge kinds this provider is permitted to request. */
412
+ readonly kinds: readonly Exclude<ProviderChallengeKind, "akamai_sbsd">[];
413
+ readonly clientProfile?: string;
414
+ }
415
+ | {
416
+ /** Optional ordered override for the SDK-owned vendor fallback chain. */
417
+ readonly vendors?: readonly ProviderResolverVendor[];
418
+ /** Challenge kinds this provider is permitted to request. */
419
+ readonly kinds: readonly ProviderChallengeKind[];
420
+ /**
421
+ * Client fingerprint profile the SDK must use when reaching this upstream.
422
+ * Measured on zozo.jp: Chrome/Firefox profiles are refused 403 before any
423
+ * challenge is served, while a Safari profile is admitted. Provider-declared
424
+ * because only the provider knows its upstream's admission rule.
425
+ */
426
+ readonly clientProfile: string;
427
+ };
385
428
 
386
429
  export type SttAudioInput = {
387
430
  kind: "base64";
@@ -905,6 +948,14 @@ export type StealthProfileSelection =
905
948
  | { browser: "firefox"; os?: "windows" | "macos" | "linux" }
906
949
  | { browser: "safari"; os?: "macos" | "ios" };
907
950
 
951
+ /** Provider-owned opt-ins layered onto the transport-owned browser/OS profile. */
952
+ export type ProviderStealthConfig = StealthProfileSelection & {
953
+ readonly challengeDetection?: {
954
+ /** Classify SBSD responses even when no resolver is declared (detect/report mode). */
955
+ readonly akamaiSbsd?: boolean;
956
+ };
957
+ };
958
+
908
959
  export type BrowserEngine = "playwright-stealth" | "nodriver" | "selenium-uc";
909
960
  export interface BrowserOptions {
910
961
  headless?: boolean;
@@ -1238,9 +1289,25 @@ export type HttpMethod =
1238
1289
  | "patch";
1239
1290
 
1240
1291
  export interface StealthFetchOptions extends Omit<RequestOptions, "redirectPolicy" | "headers"> {
1292
+ /**
1293
+ * Defaults to true. Set to false when callers need to inspect upstream
1294
+ * non-2xx bodies themselves instead of converting them to TransportError.
1295
+ *
1296
+ * Documented exception: a non-2xx response that an opted-in SDK challenge
1297
+ * detector classified (`response.challenge` is set) is returned, not thrown,
1298
+ * whatever this flag says, so the classification stays observable. Callers
1299
+ * that need the failure semantics check `response.challenge` first.
1300
+ */
1301
+ throwOnHttpError?: boolean;
1241
1302
  /**
1242
1303
  * Request headers. Array values and case-insensitive duplicate names are
1243
1304
  * combined in caller order using `", "`, matching Chrome's Fetch behavior.
1305
+ *
1306
+ * The stealth transport owns `host`, `connection`, `user-agent`, `sec-ch-ua`,
1307
+ * `sec-ch-ua-mobile`, `sec-ch-ua-platform`, `accept-encoding`, and every
1308
+ * `sec-fetch-*` header. Supplying any of them throws
1309
+ * `STEALTH_HEADER_OVERRIDE_UNSUPPORTED` at request time; declare the request
1310
+ * shape with `stealth.requestClass` instead.
1244
1311
  */
1245
1312
  headers?: Record<string, string | string[]>;
1246
1313
  method?: HttpMethod;
@@ -1260,13 +1327,26 @@ export interface StealthFetchOptions extends Omit<RequestOptions, "redirectPolic
1260
1327
  */
1261
1328
  proxyAttemptOffset?: number;
1262
1329
  /**
1263
- * SDK-specific stealth controls. Standard HTTP metadata such as language,
1264
- * referrer, and content type belongs in `headers`.
1330
+ * SDK-specific stealth controls, including a per-request browser and/or OS
1331
+ * override. Standard HTTP metadata such as language, referrer, and content
1332
+ * type belongs in `headers`.
1265
1333
  */
1266
1334
  stealth?: StealthProfileSelection & {
1267
- /** Override the configured browser and/or OS for this request. */
1268
- /** Declare the Chrome request class when it cannot be inferred from the method. */
1335
+ /**
1336
+ * Declare the Chrome request class when it cannot be inferred from the
1337
+ * method (`POST` infers `post`, everything else `navigation`). This is the
1338
+ * replacement for hand-set `Sec-Fetch-*` headers, which the transport
1339
+ * rejects; it drives `Sec-Fetch-Mode`/`Dest`/`User`, `Accept`, `Priority`,
1340
+ * and `Upgrade-Insecure-Requests`.
1341
+ */
1269
1342
  requestClass?: "navigation" | "xhr" | "post";
1343
+ /**
1344
+ * Whether a `navigation` request carries a user gesture and therefore
1345
+ * `Sec-Fetch-User: ?1` (default `true`). Set `false` for script-driven
1346
+ * navigations such as `location.replace`, which Chrome sends without it.
1347
+ * Ignored for the `xhr` and `post` request classes.
1348
+ */
1349
+ userActivation?: boolean;
1270
1350
  /**
1271
1351
  * Use only for proxy products that terminate CONNECT with a private CA
1272
1352
  * instead of tunneling the origin certificate chain.
@@ -1326,6 +1406,8 @@ export interface DeclarativeStealthResponse {
1326
1406
  headers: Record<string, string>;
1327
1407
  rawHeaders: [string, string][];
1328
1408
  body: string;
1409
+ /** Present only when an opted-in SDK detector returned an unresolved challenge response. */
1410
+ challenge?: StealthChallengeClassification;
1329
1411
  httpVersion?: string;
1330
1412
  tlsInfo?: { protocol?: string; cipher?: string; [key: string]: unknown };
1331
1413
  cookies: CookieJar;
@@ -1336,6 +1418,23 @@ export interface DeclarativeStealthResponse {
1336
1418
 
1337
1419
  export type StealthResponse = DeclarativeStealthResponse;
1338
1420
 
1421
+ /** Why a detected challenge response was returned to the caller instead of being solved and refetched. */
1422
+ export type StealthChallengeClassification = {
1423
+ readonly challenge: Extract<ProviderChallenge, { readonly kind: "akamai_sbsd" }>;
1424
+ /**
1425
+ * `resolver_unavailable`: detect-only provider or no solver configured.
1426
+ * `replay_required`: the request is not a plain GET, so it is never solved or replayed
1427
+ * automatically. `solve_failed`: a concurrent request on the same session owned the
1428
+ * solve and it rejected (that owner receives the error). `challenge_persisted`: the
1429
+ * single refetch after a solve was challenged again.
1430
+ */
1431
+ readonly outcome:
1432
+ | "resolver_unavailable"
1433
+ | "replay_required"
1434
+ | "solve_failed"
1435
+ | "challenge_persisted";
1436
+ };
1437
+
1339
1438
  export type RequestWithMethodOptions = RequestOptions & {
1340
1439
  method?: string;
1341
1440
  body?: unknown;
@@ -1371,6 +1470,12 @@ export interface StealthRedirectRunResult {
1371
1470
 
1372
1471
  export interface StealthSession {
1373
1472
  fetch(url: string, options?: StealthFetchOptions): Promise<StealthResponse>;
1473
+ /**
1474
+ * Explicitly solve and replay one unsafe request previously classified as
1475
+ * `replay_required`. The original method, URL, headers, and body bytes are
1476
+ * retained by this session and may be replayed exactly once.
1477
+ */
1478
+ replayChallenged(response: StealthResponse): Promise<StealthResponse>;
1374
1479
  cookies: StealthSessionCookies;
1375
1480
  redirects: {
1376
1481
  run(options: StealthRedirectRunOptions): Promise<StealthRedirectRunResult>;
@@ -2266,8 +2371,9 @@ export interface ProviderRuntimeState {
2266
2371
  /**
2267
2372
  * The operation context exposed for one provider declaration. Capability
2268
2373
  * bindings are present only when their corresponding declaration is present;
2269
- * trace and request remain ambient runtime bindings. Omitting the type
2270
- * parameter preserves the legacy full context shape for existing annotations.
2374
+ * platform-managed auth itself declares the credential binding. Trace and
2375
+ * request remain ambient runtime bindings. Omitting the type parameter
2376
+ * preserves the legacy full context shape for existing annotations.
2271
2377
  */
2272
2378
  export type ProviderContext<TConfig = Record<string, unknown>> = {
2273
2379
  request?: ProviderRequestContext;
@@ -2276,7 +2382,9 @@ export type ProviderContext<TConfig = Record<string, unknown>> = {
2276
2382
  & ("env" extends keyof TConfig ? { env: EnvContext } : Record<never, never>)
2277
2383
  & ("credential" extends keyof TConfig
2278
2384
  ? { credential: CredentialContext }
2279
- : Record<never, never>)
2385
+ : TConfig extends { auth: { mode: "platform-managed" } }
2386
+ ? { credential: CredentialContext }
2387
+ : Record<never, never>)
2280
2388
  & ("http" extends keyof TConfig ? { http: HttpClient } : Record<never, never>)
2281
2389
  & ("files" extends keyof TConfig
2282
2390
  ? string extends keyof TConfig
@@ -2471,7 +2579,7 @@ export interface ProviderDefinition<TContext = ProviderContext> {
2471
2579
  http?: Record<string, never> | true;
2472
2580
  allowedHosts?: string[];
2473
2581
  native?: NativeProviderConfig;
2474
- stealth?: StealthProfileSelection;
2582
+ stealth?: ProviderStealthConfig;
2475
2583
  proxy?: ProviderProxyConfig;
2476
2584
  ocr?: ProviderOcrConfig;
2477
2585
  stt?: ProviderSttConfig;