@apifuse/provider-sdk 2.2.0-beta.36 → 2.2.0-beta.37

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 (37) hide show
  1. package/AUTHORING.md +22 -8
  2. package/CHANGELOG.md +4 -0
  3. package/README.md +20 -8
  4. package/bin/apifuse-dev.ts +1 -1
  5. package/bin/apifuse-pack-types.ts +6 -5
  6. package/bin/apifuse-record.ts +1 -1
  7. package/bin/apifuse-submit-check.ts +23 -10
  8. package/dist/cli/templates/provider/index.ts.tpl +6 -3
  9. package/dist/cli/templates/provider/operations/ping.ts.tpl +2 -1
  10. package/dist/define.d.ts +39 -21
  11. package/dist/define.js +28 -9
  12. package/dist/index.d.ts +2 -2
  13. package/dist/provider.d.ts +2 -1
  14. package/dist/runtime/browser.js +19 -11
  15. package/dist/runtime/resolver-public.d.ts +1 -1
  16. package/dist/runtime/resolver-public.js +1 -1
  17. package/dist/runtime/resolver-vendors/browser.js +57 -14
  18. package/dist/runtime/resolver-vendors/types.d.ts +9 -1
  19. package/dist/runtime/resolver-vendors/types.js +15 -0
  20. package/dist/runtime/resolver.d.ts +1 -0
  21. package/dist/runtime/resolver.js +13 -7
  22. package/dist/server/serve-implementation.js +25 -0
  23. package/dist/types.d.ts +25 -17
  24. package/package.json +1 -1
  25. package/src/cli/templates/provider/index.ts.tpl +6 -3
  26. package/src/cli/templates/provider/operations/ping.ts.tpl +2 -1
  27. package/src/define.ts +135 -51
  28. package/src/index.ts +4 -2
  29. package/src/provider.ts +6 -1
  30. package/src/runtime/browser.ts +34 -11
  31. package/src/runtime/resolver-public.ts +2 -0
  32. package/src/runtime/resolver-vendors/browser.ts +69 -11
  33. package/src/runtime/resolver-vendors/types.ts +21 -0
  34. package/src/runtime/resolver.ts +17 -5
  35. package/src/server/serve-implementation.ts +39 -1
  36. package/src/testing/run.ts +3 -3
  37. package/src/types.ts +41 -17
@@ -35,6 +35,7 @@ import {
35
35
  type ResolverVendorTransport,
36
36
  ResolverVendorUnavailableError,
37
37
  type ResolverVendorUnavailableReason,
38
+ resolveProviderResolverVendors,
38
39
  resolverVendorSupports,
39
40
  } from "./resolver-vendors/types.js";
40
41
  import {
@@ -64,6 +65,10 @@ export {
64
65
  APIFUSE__RESOLVER__TIMEOUT_MS,
65
66
  DEFAULT_RESOLVER_TIMEOUT_MS,
66
67
  } from "./resolver-config.js";
68
+ export {
69
+ DEFAULT_RESOLVER_VENDOR_PREFERENCE,
70
+ resolveProviderResolverVendors,
71
+ } from "./resolver-vendors/types.js";
67
72
 
68
73
  type EnvLike = Record<string, string | undefined>;
69
74
 
@@ -340,10 +345,12 @@ function assertKnownResolverVendor(vendor: string): asserts vendor is ProviderRe
340
345
  throw new Error(`Unknown resolver vendor "${vendor}" in resolver configuration`);
341
346
  }
342
347
 
343
- function throwUnsupportedKind(kind: ProviderChallengeKind): never {
348
+ function throwUnsupportedKind(kind: ProviderChallengeKind, usingDefaultVendors: boolean): never {
344
349
  throw new ProviderError(`Resolver vendor chain does not support kind "${kind}"`, {
345
350
  code: "RESOLVER_KIND_UNSUPPORTED_BY_CHAIN",
346
- fix: `Add a resolver vendor that supports "${kind}" to the provider's resolver.vendors declaration.`,
351
+ fix: usingDefaultVendors
352
+ ? `No SDK default resolver vendor supports "${kind}". Declare an explicit resolver.vendors override with a supporting vendor.`
353
+ : `Add a resolver vendor that supports "${kind}" to the provider's resolver.vendors declaration.`,
347
354
  });
348
355
  }
349
356
 
@@ -814,6 +821,7 @@ function createResolverChainClient(options: {
814
821
  readonly kinds: readonly ProviderChallengeKind[];
815
822
  readonly entries: readonly ResolverChainEntry[];
816
823
  readonly unavailableReason?: string;
824
+ readonly usingDefaultVendors?: boolean;
817
825
  readonly cache?: ProviderCache;
818
826
  readonly identity?: ResolverIdentity;
819
827
  readonly proxyIntent?: ResolverRuntimeOptions["proxyIntent"];
@@ -839,7 +847,9 @@ function createResolverChainClient(options: {
839
847
  }
840
848
 
841
849
  const supportingEntries = options.entries.filter((entry) => entry.supports(challenge.kind));
842
- if (supportingEntries.length === 0) throwUnsupportedKind(challenge.kind);
850
+ if (supportingEntries.length === 0) {
851
+ throwUnsupportedKind(challenge.kind, options.usingDefaultVendors ?? false);
852
+ }
843
853
  signal.throwIfAborted();
844
854
  const identityResolution = options.proxyIntent
845
855
  ? await resolveResolverIdentity(options.proxyIntent)
@@ -1033,7 +1043,8 @@ function createResolverClientFromEnvInternal(
1033
1043
  }
1034
1044
  assertClientProfileTransportContract(config.clientProfile, options.transport);
1035
1045
 
1036
- if (config.vendors.length === 0) {
1046
+ const vendors = resolveProviderResolverVendors(config);
1047
+ if (config.vendors !== undefined && config.vendors.length === 0) {
1037
1048
  return createResolverChainClient({
1038
1049
  kinds: config.kinds,
1039
1050
  entries: [],
@@ -1047,7 +1058,7 @@ function createResolverClientFromEnvInternal(
1047
1058
 
1048
1059
  return createResolverChainClient({
1049
1060
  kinds: config.kinds,
1050
- entries: config.vendors.map((configuredVendor) => {
1061
+ entries: vendors.map((configuredVendor) => {
1051
1062
  assertKnownResolverVendor(configuredVendor);
1052
1063
  const vendor = configuredVendor;
1053
1064
  return {
@@ -1062,6 +1073,7 @@ function createResolverClientFromEnvInternal(
1062
1073
  ),
1063
1074
  };
1064
1075
  }),
1076
+ usingDefaultVendors: config.vendors === undefined,
1065
1077
  cache: options.cache,
1066
1078
  proxyIntent: options.proxyIntent,
1067
1079
  identityScope: options.identityScope,
@@ -1,3 +1,4 @@
1
+ import { createHash } from "node:crypto";
1
2
  import { existsSync } from "node:fs";
2
3
  import { createRequire } from "node:module";
3
4
  import { join } from "node:path";
@@ -745,7 +746,7 @@ function createProviderContext(
745
746
  ...event,
746
747
  }),
747
748
  }),
748
- });
749
+ } as ProviderContext);
749
750
  wrappedContext = context;
750
751
  return context;
751
752
  }
@@ -1441,6 +1442,41 @@ function extractRequestId(raw: unknown): string | undefined {
1441
1442
  return typeof value === "string" ? value : undefined;
1442
1443
  }
1443
1444
 
1445
+ type ProviderErrorCauseFrame = {
1446
+ errorClass: string;
1447
+ code?: string;
1448
+ messageLength: number;
1449
+ messageFingerprint: string;
1450
+ };
1451
+
1452
+ const MAX_PROVIDER_ERROR_CAUSE_FRAMES = 5;
1453
+
1454
+ function providerErrorCauseChain(error: unknown): ProviderErrorCauseFrame[] | undefined {
1455
+ if (!(error instanceof Error) && !isProviderError(error)) return undefined;
1456
+
1457
+ const seen = new Set<object>([error]);
1458
+ const frames: ProviderErrorCauseFrame[] = [];
1459
+ let cause = error.cause;
1460
+
1461
+ while (
1462
+ frames.length < MAX_PROVIDER_ERROR_CAUSE_FRAMES &&
1463
+ (cause instanceof Error || isProviderError(cause)) &&
1464
+ !seen.has(cause)
1465
+ ) {
1466
+ seen.add(cause);
1467
+ const message = cause.message;
1468
+ frames.push({
1469
+ errorClass: cause.name,
1470
+ ...(isProviderError(cause) && typeof cause.code === "string" ? { code: cause.code } : {}),
1471
+ messageLength: message.length,
1472
+ messageFingerprint: createHash("sha256").update(message).digest("hex").slice(0, 12),
1473
+ });
1474
+ cause = cause.cause;
1475
+ }
1476
+
1477
+ return frames.length > 0 ? frames : undefined;
1478
+ }
1479
+
1444
1480
  function logProviderError(
1445
1481
  logger: ProviderServerLogger | unknown,
1446
1482
  provider: ProviderDefinition,
@@ -1461,6 +1497,7 @@ function logProviderError(
1461
1497
  : "internal_error";
1462
1498
  const errorClass = error instanceof Error ? error.name : typeof error;
1463
1499
  const message = error instanceof Error ? error.message : String(error);
1500
+ const causeChain = providerErrorCauseChain(error);
1464
1501
  const details = errorObservabilityDetails(error, declaredErrorCode);
1465
1502
  const isUnregisteredProviderErrorCode =
1466
1503
  status === 500 &&
@@ -1482,6 +1519,7 @@ function logProviderError(
1482
1519
  code,
1483
1520
  errorClass,
1484
1521
  message,
1522
+ ...(causeChain ? { causeChain } : {}),
1485
1523
  ...(details.upstreamStatus ? { upstreamStatus: details.upstreamStatus } : {}),
1486
1524
  errorCategory: details.category,
1487
1525
  taxonomyVersion: details.taxonomyVersion,
@@ -525,7 +525,7 @@ function createUpstreamContext(
525
525
  ...(provider.native
526
526
  ? {
527
527
  native: {
528
- network: {
528
+ network: {
529
529
  connectTcp: async (options) => {
530
530
  const request = snapshotNativeConnectInput(options);
531
531
  requireNativeEgress().assertConnect(request, "disabled");
@@ -578,7 +578,7 @@ function createUpstreamContext(
578
578
  credential,
579
579
  state,
580
580
  }),
581
- };
581
+ } as ProviderContext;
582
582
  }
583
583
 
584
584
  function createNativeConnection(
@@ -709,7 +709,7 @@ export function createSnapshotContext(rawFixture: unknown): ProviderContext {
709
709
  credential,
710
710
  state,
711
711
  }),
712
- };
712
+ } satisfies Omit<ProviderContext, "native"> as unknown as ProviderContext;
713
713
  snapshotCaptureStates.set(context, {
714
714
  assertConsumed() {
715
715
  if (streamCaptureGroup && nextCaptureItem !== streamCaptureGroup.items.length) {
package/src/types.ts CHANGED
@@ -308,16 +308,16 @@ export interface ProviderSttConfig {
308
308
  }
309
309
 
310
310
  /**
311
- * `browser` is the in-house CDP pool (`apps/cdp-pool`, reached through
312
- * `createBrowserClient`) and is a first-class vendor rather than an escape hatch:
313
- * for fingerprint-family kinds, it was measured faster than a paid vendor
314
- * (4.5 s vs 17.5 s) at zero marginal cost.
311
+ * Union order is documentation only.
315
312
  *
316
- * `2captcha` is the vendor already carrying production traffic in
317
- * `apifuse-provider-tabelog`.
313
+ * The SDK owns the default hosted-vendor fallback policy and derives the chain
314
+ * from each provider's declared challenge kinds. Hosted solvers are preferred
315
+ * with `capsolver` ahead of `2captcha` in that policy.
318
316
  *
319
- * Union order is documentation only; the effective fallback order is whatever
320
- * `ProviderResolverConfig.vendors` declares.
317
+ * `browser` is the in-house CDP pool and remains opt-in; it is excluded from the
318
+ * default chain. `custom` is likewise reserved for provider-specific configuration.
319
+ *
320
+ * `ProviderResolverConfig.vendors` overrides the SDK policy when declared.
321
321
  */
322
322
  export type ProviderResolverVendor =
323
323
  | "browser"
@@ -411,8 +411,8 @@ export type ChallengeSolution =
411
411
  };
412
412
 
413
413
  export interface ProviderResolverConfig {
414
- /** Ordered vendor fallback chain, tried first to last. */
415
- readonly vendors: readonly ProviderResolverVendor[];
414
+ /** Optional ordered override for the SDK-owned vendor fallback chain. */
415
+ readonly vendors?: readonly ProviderResolverVendor[];
416
416
  /** Challenge kinds this provider is permitted to request. */
417
417
  readonly kinds: readonly ProviderChallengeKind[];
418
418
  /**
@@ -1636,9 +1636,6 @@ export interface NativeContext {
1636
1636
  readonly network: NativeNetworkClient;
1637
1637
  }
1638
1638
 
1639
- /** Consumer-facing alias for the native capability on provider contexts. */
1640
- export type NativeProviderContext = NativeContext;
1641
-
1642
1639
  export interface NativeProviderConfig {
1643
1640
  readonly network?: {
1644
1641
  readonly tcp?: readonly NativeTcpEgressRule[];
@@ -1816,6 +1813,10 @@ export interface BrowserPage extends BrowserFrame {
1816
1813
  cookies(): Promise<readonly BrowserCookie[]>;
1817
1814
  fill(selector: string, text: string): Promise<void>;
1818
1815
  goto(url: string): Promise<void>;
1816
+ goto(
1817
+ url: string,
1818
+ options?: { readonly timeout?: number; readonly waitUntil?: "load" | "domcontentloaded" },
1819
+ ): Promise<void>;
1819
1820
  pageId?: string;
1820
1821
  screenshot(options?: { fullPage?: boolean }): Promise<Buffer>;
1821
1822
  click(selector: string): Promise<void>;
@@ -2139,7 +2140,7 @@ export interface FlowContext {
2139
2140
  * un-keyed entries would be shared across all connectionless ceremonies. */
2140
2141
  readonly state?: ProviderRuntimeState;
2141
2142
  /** Present when the selected runtime supplies native network capabilities. */
2142
- readonly native?: NativeProviderContext;
2143
+ readonly native?: NativeContext;
2143
2144
  stealth: StealthClient;
2144
2145
  env: EnvContext;
2145
2146
  credential?: CredentialContext;
@@ -2275,8 +2276,8 @@ export interface ProviderContext {
2275
2276
  http: HttpClient;
2276
2277
  /** Present for requests carrying runtime-resolvable file references. */
2277
2278
  readonly files?: ProviderFilesContext;
2278
- /** Present when the selected runtime supplies native network capabilities. */
2279
- readonly native?: NativeProviderContext;
2279
+ /** Native network capability selected by declaration-derived contexts. */
2280
+ readonly native: NativeContext;
2280
2281
  cache: ProviderCache;
2281
2282
  state: ProviderRuntimeState;
2282
2283
  stealth: StealthClient;
@@ -2289,6 +2290,28 @@ export interface ProviderContext {
2289
2290
  choice: ProviderChoiceContext;
2290
2291
  }
2291
2292
 
2293
+ /**
2294
+ * The operation context exposed for one provider declaration. Capability
2295
+ * bindings are present only when their corresponding declaration is present;
2296
+ * trace and request remain ambient runtime bindings.
2297
+ */
2298
+ export type ProviderContextFor<TConfig> =
2299
+ Pick<ProviderContext, "trace" | "request">
2300
+ & ("env" extends keyof TConfig ? Pick<ProviderContext, "env"> : Record<never, never>)
2301
+ & ("credential" extends keyof TConfig ? Pick<ProviderContext, "credential"> : Record<never, never>)
2302
+ & ("http" extends keyof TConfig ? Pick<ProviderContext, "http"> : Record<never, never>)
2303
+ & ("files" extends keyof TConfig ? Pick<ProviderContext, "files"> : Record<never, never>)
2304
+ & ("native" extends keyof TConfig ? Pick<ProviderContext, "native"> : Record<never, never>)
2305
+ & ("cache" extends keyof TConfig ? Pick<ProviderContext, "cache"> : Record<never, never>)
2306
+ & ("state" extends keyof TConfig ? Pick<ProviderContext, "state"> : Record<never, never>)
2307
+ & ("stealth" extends keyof TConfig ? Pick<ProviderContext, "stealth"> : Record<never, never>)
2308
+ & ("browser" extends keyof TConfig ? Pick<ProviderContext, "browser"> : Record<never, never>)
2309
+ & ("auth" extends keyof TConfig ? Pick<ProviderContext, "auth"> : Record<never, never>)
2310
+ & ("ocr" extends keyof TConfig ? Pick<ProviderContext, "ocr"> : Record<never, never>)
2311
+ & ("stt" extends keyof TConfig ? Pick<ProviderContext, "stt"> : Record<never, never>)
2312
+ & ("resolver" extends keyof TConfig ? Pick<ProviderContext, "resolver"> : Record<never, never>)
2313
+ & ("choice" extends keyof TConfig ? Pick<ProviderContext, "choice"> : Record<never, never>);
2314
+
2292
2315
  export interface ProxiedOAuthConfig {
2293
2316
  authorizeUrl: string;
2294
2317
  tokenUrl: string;
@@ -2345,6 +2368,7 @@ export interface OperationContractMetadata {
2345
2368
  export interface OperationDefinition<
2346
2369
  TInput extends SchemaLike = SchemaLike,
2347
2370
  TOutput extends SchemaLike = SchemaLike,
2371
+ TContext = ProviderContext,
2348
2372
  > {
2349
2373
  /**
2350
2374
  * Short English display title for the operation. The SDK passes it through
@@ -2376,7 +2400,7 @@ export interface OperationDefinition<
2376
2400
  input: TInput;
2377
2401
  output: TOutput;
2378
2402
  handler(
2379
- ctx: ProviderContext,
2403
+ ctx: TContext,
2380
2404
  input: InferSchemaOutput<TInput>,
2381
2405
  ):
2382
2406
  | OperationHandlerResult<InferSchemaOutput<TOutput>>