@apifuse/provider-sdk 2.2.0-beta.27 → 2.2.0-beta.28

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 (81) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/bin/apifuse-dev.ts +2 -2
  3. package/bin/apifuse-pack-smoke.ts +1 -1
  4. package/bin/apifuse-pack-types.ts +2 -1
  5. package/bin/apifuse-perf.ts +2 -4
  6. package/bin/apifuse-record.ts +1 -1
  7. package/dist/auth-turn/index.d.ts +1 -1
  8. package/dist/auth-turn/index.js +1 -1
  9. package/dist/ceremonies/index.js +52 -11
  10. package/dist/config/loader.d.ts +1 -1
  11. package/dist/config/loader.js +4 -2
  12. package/dist/index.d.ts +7 -6
  13. package/dist/index.js +4 -6
  14. package/dist/provider.d.ts +2 -1
  15. package/dist/provider.js +1 -1
  16. package/dist/runtime/auth-flow.js +1 -1
  17. package/dist/runtime/http.d.ts +1 -0
  18. package/dist/runtime/http.js +135 -12
  19. package/dist/runtime/instrumentation.js +1 -1
  20. package/dist/runtime/native-network-errors.d.ts +33 -0
  21. package/dist/runtime/native-network-errors.js +69 -0
  22. package/dist/runtime/native-network.d.ts +2 -33
  23. package/dist/runtime/native-network.js +2 -68
  24. package/dist/runtime/redis.d.ts +1 -1
  25. package/dist/runtime/redis.js +4 -2
  26. package/dist/runtime/resolver-config.d.ts +6 -0
  27. package/dist/runtime/resolver-config.js +6 -0
  28. package/dist/runtime/resolver-shared.d.ts +3 -0
  29. package/dist/runtime/resolver-shared.js +12 -0
  30. package/dist/runtime/resolver-vendors/twocaptcha.d.ts +2 -1
  31. package/dist/runtime/resolver-vendors/twocaptcha.js +80 -43
  32. package/dist/runtime/resolver-vendors/types.d.ts +1 -1
  33. package/dist/runtime/resolver.d.ts +6 -10
  34. package/dist/runtime/resolver.js +19 -18
  35. package/dist/runtime/state.js +5 -115
  36. package/dist/runtime/stealth-cookies.d.ts +20 -0
  37. package/dist/runtime/stealth-cookies.js +111 -0
  38. package/dist/runtime/stealth.js +7 -130
  39. package/dist/serve.d.ts +1 -1
  40. package/dist/serve.js +1 -1
  41. package/dist/server/index.d.ts +1 -1
  42. package/dist/server/index.js +1 -1
  43. package/dist/server/self-test.d.ts +13 -0
  44. package/dist/server/self-test.js +124 -46
  45. package/dist/server/serve-implementation.d.ts +199 -0
  46. package/dist/server/serve-implementation.js +2054 -0
  47. package/dist/server/serve.d.ts +1 -187
  48. package/dist/server/serve.js +1 -1827
  49. package/dist/stateful/errors.d.ts +5 -0
  50. package/dist/stateful/errors.js +10 -0
  51. package/dist/stateful/stateful-provider-session-routing.d.ts +1 -5
  52. package/dist/stateful/stateful-provider-session-routing.js +2 -10
  53. package/dist/stream.js +7 -1
  54. package/package.json +27 -2
  55. package/src/auth-turn/index.ts +1 -1
  56. package/src/ceremonies/index.ts +68 -18
  57. package/src/config/loader.ts +5 -2
  58. package/src/index.ts +18 -23
  59. package/src/provider.ts +12 -14
  60. package/src/runtime/auth-flow.ts +1 -1
  61. package/src/runtime/http.ts +155 -11
  62. package/src/runtime/instrumentation.ts +1 -1
  63. package/src/runtime/native-network-errors.ts +99 -0
  64. package/src/runtime/native-network.ts +16 -97
  65. package/src/runtime/redis.ts +7 -2
  66. package/src/runtime/resolver-config.ts +6 -0
  67. package/src/runtime/resolver-shared.ts +17 -0
  68. package/src/runtime/resolver-vendors/twocaptcha.ts +100 -49
  69. package/src/runtime/resolver-vendors/types.ts +1 -0
  70. package/src/runtime/resolver.ts +47 -22
  71. package/src/runtime/state.ts +5 -144
  72. package/src/runtime/stealth-cookies.ts +132 -0
  73. package/src/runtime/stealth.ts +14 -157
  74. package/src/serve.ts +6 -1
  75. package/src/server/index.ts +1 -0
  76. package/src/server/self-test.ts +184 -59
  77. package/src/server/serve-implementation.ts +3024 -0
  78. package/src/server/serve.ts +1 -2661
  79. package/src/stateful/errors.ts +12 -0
  80. package/src/stateful/stateful-provider-session-routing.ts +2 -11
  81. package/src/stream.ts +8 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @apifuse/provider-sdk Changelog
2
2
 
3
+ ## 2.2.0-beta.28
4
+
5
+ - Release candidate for main commit 2936f1891d4e2326502f0585081f8a58060d6dd7.
6
+
3
7
  ## 2.2.0-beta.27
4
8
 
5
9
  - Release candidate for main commit 579d9e7fd22d8414b151be2b71a43a0990456911.
@@ -4,7 +4,6 @@ import { existsSync } from "node:fs";
4
4
  import { dirname, relative, resolve } from "node:path";
5
5
  import type { ProviderDefinition } from "../src/index.js";
6
6
  import {
7
- createBrowserClient,
8
7
  createCredentialContext,
9
8
  createEnvContext,
10
9
  createHttpClient,
@@ -12,12 +11,13 @@ import {
12
11
  createProviderCache,
13
12
  createProviderChoiceContext,
14
13
  createUnsupportedResolverClient,
15
- createStealthClient,
16
14
  createSttClientFromEnv,
17
15
  PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
18
16
  ProviderError,
19
17
  } from "../src/index.js";
18
+ import { createBrowserClient } from "../src/runtime/browser.js";
20
19
  import { createMemoryProviderRuntimeState } from "../src/runtime/state.js";
20
+ import { createStealthClient } from "../src/runtime/stealth.js";
21
21
  import { createTraceContext } from "../src/runtime/trace.js";
22
22
  import type { BrowserClient, ProviderContext } from "../src/types.js";
23
23
 
@@ -224,7 +224,7 @@ function smokePackedStealthNative(consumerDir: string): void {
224
224
  "--eval",
225
225
  [
226
226
  'import { createServer } from "node:http";',
227
- 'import { createStealthClient } from "@apifuse/provider-sdk";',
227
+ 'import { createStealthClient } from "@apifuse/provider-sdk/runtime/stealth";',
228
228
  "const server = createServer((_request, response) => {",
229
229
  ' response.setHeader("set-cookie", "pack_native_cookie=landed; Path=/");',
230
230
  ' response.end("packed native stealth ok");',
@@ -357,7 +357,8 @@ function setUpFixtureConsumer(consumerDir: string, tarballPath: string): void {
357
357
  writeFileSync(
358
358
  join(consumerDir, "consumer.ts"),
359
359
  [
360
- 'import { defineProvider, invalidateResolverSolution, ProviderError, resolveProxy, SessionExpiredError, z } from "@apifuse/provider-sdk";',
360
+ 'import { defineProvider, ProviderError, resolveProxy, SessionExpiredError, z } from "@apifuse/provider-sdk";',
361
+ 'import { invalidateResolverSolution } from "@apifuse/provider-sdk/runtime/resolver";',
361
362
  'import type { BrowserCookie, ChallengeSolution, NativeNetworkClient, NativeNetworkConnection, NativeProviderConfig, NativeProviderContext, NativeTcpEgressGrant, ProviderChallenge, ProviderContext, ProviderFileRef, ProviderFilesContext, ProviderResolvedFile, ProviderResolverConfig, ResolverContext, ResolverRuntimeOptions } from "@apifuse/provider-sdk";',
362
363
  'import type { ProxyProtocol, ProxyResolutionOptions, ProxyResolutionSource, ProxyVendorName, RequestOptions, ResolvedProxyConfig } from "@apifuse/provider-sdk";',
363
364
  'import { defineCredentialsAuth } from "@apifuse/provider-sdk/provider";',
@@ -11,7 +11,6 @@ import {
11
11
  createBypassProviderCache,
12
12
  createHttpClient,
13
13
  createProviderChoiceContext,
14
- createStealthClient,
15
14
  createSttClientFromEnv,
16
15
  executeOperation,
17
16
  getProviderBaseUrl,
@@ -28,6 +27,7 @@ import {
28
27
  } from "../src/index.js";
29
28
  import { computeStats, groupSpansByName, type PerfStats } from "../src/runtime/perf.js";
30
29
  import { createMemoryProviderRuntimeState } from "../src/runtime/state.js";
30
+ import { createStealthClient } from "../src/runtime/stealth.js";
31
31
  import { createTraceContext, resolveTraceContextOptions } from "../src/runtime/trace.js";
32
32
  import { renderWaterfall } from "../src/runtime/waterfall.js";
33
33
  import type { BrowserClient } from "../src/types.js";
@@ -458,9 +458,7 @@ async function loadFixtureReplay(providerDirectory: string): Promise<FixtureRepl
458
458
  async function assertProxyConfigured(provider: ProviderDefinition): Promise<void> {
459
459
  const policy = provider.proxy;
460
460
  if (!policy || typeof policy !== "object" || policy.mode === "disabled") {
461
- throw new Error(
462
- "--compare-proxy requires an enabled ProviderProxyPolicy on the provider.",
463
- );
461
+ throw new Error("--compare-proxy requires an enabled ProviderProxyPolicy on the provider.");
464
462
  }
465
463
 
466
464
  const resolved = await resolveProxy({ proxyPolicy: policy });
@@ -10,7 +10,6 @@ import {
10
10
  createHttpClient,
11
11
  createOcrClientFromEnv,
12
12
  createProviderChoiceContext,
13
- createStealthClient,
14
13
  createSttClientFromEnv,
15
14
  createUnsupportedResolverClient,
16
15
  executeOperation,
@@ -33,6 +32,7 @@ import {
33
32
  sanitizeFixtureString,
34
33
  } from "../src/fixture-sanitization.js";
35
34
  import { createMemoryProviderRuntimeState } from "../src/runtime/state.js";
35
+ import { createStealthClient } from "../src/runtime/stealth.js";
36
36
  import {
37
37
  REDACTED_QUERY_VALUE,
38
38
  isSensitiveKey,
@@ -20,7 +20,7 @@ export declare const AUTH_TURN_SCHEMA_ARTIFACT_PATH = "dist/auth-turn/auth-turn.
20
20
  *
21
21
  * This is the exact codification of the runtime validation the SDK applies to
22
22
  * ceremony outputs (see `validateCeremonyOutput` in `src/ceremonies`), which
23
- * compiles this same document. `kind` is an OPEN string on the wire: the known
23
+ * evaluates this same document. `kind` is an OPEN string on the wire: the known
24
24
  * kinds in {@link TURN_KINDS} are tooling metadata, never a wire constraint.
25
25
  *
26
26
  * The committed artifact at `src/auth-turn/auth-turn.v1.schema.json` (shipped
@@ -18,7 +18,7 @@ export const AUTH_TURN_SCHEMA_ARTIFACT_PATH = "dist/auth-turn/auth-turn.v1.schem
18
18
  *
19
19
  * This is the exact codification of the runtime validation the SDK applies to
20
20
  * ceremony outputs (see `validateCeremonyOutput` in `src/ceremonies`), which
21
- * compiles this same document. `kind` is an OPEN string on the wire: the known
21
+ * evaluates this same document. `kind` is an OPEN string on the wire: the known
22
22
  * kinds in {@link TURN_KINDS} are tooling metadata, never a wire constraint.
23
23
  *
24
24
  * The committed artifact at `src/auth-turn/auth-turn.v1.schema.json` (shipped
@@ -1,12 +1,6 @@
1
1
  import { createHash, randomBytes, randomUUID } from "node:crypto";
2
- import Ajv2020 from "ajv/dist/2020.js";
3
2
  import { AUTH_TURN_SCHEMA } from "../auth-turn/index.js";
4
3
  import { FlowExpiredError, ProviderSecretError, TurnValidationError, ValidationError, } from "../errors.js";
5
- const ajv = new Ajv2020({ allErrors: true, strict: true, strictSchema: true });
6
- // Runtime ceremony-output validation derives from the exported versioned
7
- // contract: it compiles the exact AUTH_TURN_SCHEMA document shipped at
8
- // dist/auth-turn/auth-turn.v1.schema.json, so the two cannot drift.
9
- const validateAuthTurn = ajv.compile(AUTH_TURN_SCHEMA);
10
4
  const OAUTH2_STATE_KEY = "__oauth2_state";
11
5
  const OAUTH2_PKCE_VERIFIER_KEY = "__oauth2_pkce_verifier";
12
6
  export const OAUTH2_PROXIED_AUTH_PROXY_ORIGIN_ENV_KEY = "APIFUSE__AUTH_PROXY__URL";
@@ -26,6 +20,50 @@ const FORM_FIELD_ORDER_EXTENSION = "x-apifuse-field-order";
26
20
  function isRecord(value) {
27
21
  return !!value && typeof value === "object" && !Array.isArray(value);
28
22
  }
23
+ function validateSchemaNode(value, schema, path, errors) {
24
+ if (schema.type === "object") {
25
+ if (!isRecord(value)) {
26
+ errors.push(`${path} must be object`);
27
+ return;
28
+ }
29
+ const properties = schema.properties ?? {};
30
+ for (const required of schema.required ?? []) {
31
+ if (!Object.hasOwn(value, required) || value[required] === undefined) {
32
+ errors.push(`${path} must have required property '${required}'`);
33
+ }
34
+ }
35
+ if (schema.additionalProperties === false) {
36
+ for (const key of Object.keys(value)) {
37
+ if (!Object.hasOwn(properties, key)) {
38
+ errors.push(`${path} must NOT have additional property '${key}'`);
39
+ }
40
+ }
41
+ }
42
+ for (const [key, childSchema] of Object.entries(properties)) {
43
+ if (Object.hasOwn(value, key) && value[key] !== undefined) {
44
+ validateSchemaNode(value[key], childSchema, `${path}/${key}`, errors);
45
+ }
46
+ }
47
+ return;
48
+ }
49
+ if (schema.type === "string") {
50
+ if (typeof value !== "string") {
51
+ errors.push(`${path} must be string`);
52
+ }
53
+ else if (schema.minLength !== undefined && value.length < schema.minLength) {
54
+ errors.push(`${path} must NOT have fewer than ${schema.minLength} characters`);
55
+ }
56
+ return;
57
+ }
58
+ if (schema.type === "number") {
59
+ if (typeof value !== "number" || !Number.isFinite(value)) {
60
+ errors.push(`${path} must be number`);
61
+ }
62
+ else if (schema.minimum !== undefined && value < schema.minimum) {
63
+ errors.push(`${path} must be >= ${schema.minimum}`);
64
+ }
65
+ }
66
+ }
29
67
  function ensureRecord(value) {
30
68
  return isRecord(value) ? value : {};
31
69
  }
@@ -114,11 +152,14 @@ function withDeclaredFormFieldOrder(expectedInput) {
114
152
  };
115
153
  }
116
154
  export function validateCeremonyOutput(turn) {
117
- if (!validateAuthTurn(turn)) {
118
- const detail = validateAuthTurn.errors
119
- ?.map((error) => `${error.instancePath || "$"} ${error.message ?? "invalid"}`)
120
- .join("; ");
121
- throw new TurnValidationError(detail || "Invalid AuthTurn output");
155
+ // Evaluate the exact exported versioned schema without eagerly initializing
156
+ // a general-purpose JSON Schema compiler in every provider process. Contract
157
+ // parity tests compare this focused evaluator with AJV over all fixtures and
158
+ // edge probes, so the runtime and shipped document remain locked together.
159
+ const errors = [];
160
+ validateSchemaNode(turn, AUTH_TURN_SCHEMA, "", errors);
161
+ if (errors.length > 0) {
162
+ throw new TurnValidationError(errors.join("; "));
122
163
  }
123
164
  return turn;
124
165
  }
@@ -1,4 +1,4 @@
1
- import { Redis } from "ioredis";
1
+ import type { Redis } from "ioredis";
2
2
  import type { ProviderProxyPolicy, TraceConfig } from "../types.js";
3
3
  import { type ProxyProtocol } from "../runtime/proxy-nodemaven.js";
4
4
  export type { ProxyProtocol } from "../runtime/proxy-nodemaven.js";
@@ -1,7 +1,7 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import { existsSync } from "node:fs";
3
+ import { createRequire } from "node:module";
3
4
  import path from "node:path";
4
- import { Redis } from "ioredis";
5
5
  import { NODEMAVEN_DEFAULT_PROTOCOL, NODEMAVEN_FILTER_ENV, NODEMAVEN_MAX_POOL_SIZE, NODEMAVEN_PASSWORD_ENV, NODEMAVEN_USERNAME_ENV, hasNodemavenCredentials, nodemavenPoolSize, synthesizeNodemavenProxy, } from "../runtime/proxy-nodemaven.js";
6
6
  // "smartproxy" here is api.smartproxy.org — a residential proxy with an IP
7
7
  // extraction API (app_key → raw ip:port pool). It is NOT the company formerly
@@ -34,6 +34,7 @@ export class ProxyResolutionError extends Error {
34
34
  }
35
35
  }
36
36
  const proxyCache = new Map();
37
+ const require = createRequire(import.meta.url);
37
38
  const proxyInflight = new Map();
38
39
  const invalidatedProxyKeys = new Map();
39
40
  const redisClients = new Map();
@@ -84,7 +85,8 @@ function getProxyRedis() {
84
85
  const existing = redisClients.get(redisUrl);
85
86
  if (existing)
86
87
  return existing;
87
- const redis = new Redis(redisUrl, {
88
+ const { Redis: RedisClient } = require("ioredis");
89
+ const redis = new RedisClient(redisUrl, {
88
90
  connectTimeout: REDIS_TIMEOUT_MS,
89
91
  enableOfflineQueue: false,
90
92
  lazyConnect: true,
package/dist/index.d.ts CHANGED
@@ -16,29 +16,30 @@ export * from "./recipes/gov-api.js";
16
16
  export * from "./recipes/rest-api.js";
17
17
  export { createFlowContext, createScratchpad } from "./runtime/auth-flow.js";
18
18
  export type { BrowserClientOptions } from "./runtime/browser.js";
19
- export { BrowserClient, createBrowserClient } from "./runtime/browser.js";
20
19
  export { APIFUSE__CACHE__KEY_PEPPER_ENV, createBypassProviderCache, createProviderCache, type ProviderCacheOptions, resetProviderCacheForTests, } from "./runtime/cache.js";
21
20
  export { type CreateProviderChoiceContextOptions, createProviderChoiceContext, createTestProviderChoiceContext, PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV, } from "./runtime/choice.js";
22
21
  export { type CreateCredentialContextOptions, createCredentialContext, } from "./runtime/credential.js";
23
22
  export { createEnvContext } from "./runtime/env.js";
24
23
  export { executeOperation } from "./runtime/executor.js";
25
24
  export { createHttpClient } from "./runtime/http.js";
26
- 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";
25
+ export { NativeEgressGrantExpiredError, NativeEgressNotDeclaredError, NativeIdleTimeoutError, NativeNetworkError, NativeProxyExpiredError, } from "./runtime/native-network-errors.js";
26
+ export type { NativeGatewayProxy, NativeGatewayProxyResolutionInput, NativeGatewayProxySkipReason, NativeGatewayProxySynthesizer, NativeGatewayProxySynthesisResult, NativeGatewayProxySynthesisInput, NativeNetworkClientOptions, NativeNetworkErrorCode, VendorCredentialLookup, VendorCredentialResolver, } from "./runtime/native-network.js";
27
27
  export type { Insight, InsightSeverity } from "./runtime/insights.js";
28
28
  export { generateInsights } from "./runtime/insights.js";
29
29
  export { type InstrumentationOptions, type InstrumentedProviderContext, wrapWithInstrumentation, } from "./runtime/instrumentation.js";
30
- export { type PrevalidateResult, prevalidate } from "./runtime/prevalidate.js";
30
+ export type { PrevalidateResult } 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
+ export { APIFUSE__CDP_POOL__URL, APIFUSE__RESOLVER__2CAPTCHA__API_KEY, APIFUSE__RESOLVER__CAPMONSTER__API_KEY, APIFUSE__RESOLVER__CAPSOLVER__API_KEY, APIFUSE__RESOLVER__TIMEOUT_MS, DEFAULT_RESOLVER_TIMEOUT_MS, } from "./runtime/resolver-config.js";
33
+ export { createUnsupportedResolverClient } from "./runtime/resolver-shared.js";
34
+ export type { ResolverRuntimeOptions } from "./runtime/resolver.js";
33
35
  export type { ResolverVendorTransport } from "./runtime/resolver-vendors/types.js";
34
36
  export { assertRequiredSecretsPresent, listMissingRequiredSecrets, MISSING_SECRET_CODE, } from "./runtime/secrets.js";
35
37
  export { createUnsupportedProviderRuntimeState, UnsupportedProviderStateError, } from "./runtime/state.js";
36
- export { createStealthClient } from "./runtime/stealth.js";
37
38
  export { APIFUSE__OCR__API_KEY_ENV, APIFUSE__OCR__BACKEND_ENV, APIFUSE__OCR__BASE_URL_ENV, APIFUSE__OCR__CLOUDFLARE_API_TOKEN_ENV, APIFUSE__OCR__MODEL_ENV, CLOUDFLARE_ACCOUNT_ID_ENV, CLOUDFLARE_WORKERS_AI_OCR_BACKEND, createCloudflareWorkersAiOcrClient, createOcrClientFromEnv, createOpenAiCompatibleOcrClient, createUnsupportedOcrClient, DEFAULT_CLOUDFLARE_WORKERS_AI_OCR_MODEL, extractCaptchaCandidates, OPENAI_COMPATIBLE_OCR_BACKEND, } from "./runtime/ocr.js";
38
39
  export { APIFUSE__STT__BACKEND_ENV, APIFUSE__STT__CLOUDFLARE_API_TOKEN_ENV, APIFUSE__STT__MODEL_ENV, createSttClientFromEnv, createUnsupportedSttClient, extractVerificationCode, resolveSttPrompt, } from "./runtime/stt.js";
39
40
  export { type CreateTraceContextOptions, createTraceContext, type Span, type TraceContext, } from "./runtime/trace.js";
40
41
  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";
41
- export { createServerApp, ERROR_OBSERVABILITY_HEADER, type ServeOptions, serve, } from "./server/index.js";
42
+ export { createServerApp, createServerAppAsync, ERROR_OBSERVABILITY_HEADER, type ServeOptions, serve, } from "./server/serve.js";
42
43
  export { getStealthProfile, listStealthProfiles } from "./stealth/profiles.js";
43
44
  export * from "./stream.js";
44
45
  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";
package/dist/index.js CHANGED
@@ -14,27 +14,25 @@ export { lintOperation, lintProvider, } from "./lint.js";
14
14
  export * from "./recipes/gov-api.js";
15
15
  export * from "./recipes/rest-api.js";
16
16
  export { createFlowContext, createScratchpad } from "./runtime/auth-flow.js";
17
- export { BrowserClient, createBrowserClient } from "./runtime/browser.js";
18
17
  export { APIFUSE__CACHE__KEY_PEPPER_ENV, createBypassProviderCache, createProviderCache, resetProviderCacheForTests, } from "./runtime/cache.js";
19
18
  export { createProviderChoiceContext, createTestProviderChoiceContext, PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV, } from "./runtime/choice.js";
20
19
  export { createCredentialContext, } from "./runtime/credential.js";
21
20
  export { createEnvContext } from "./runtime/env.js";
22
21
  export { executeOperation } from "./runtime/executor.js";
23
22
  export { createHttpClient } from "./runtime/http.js";
24
- export { createNativeNetworkClient, createEnvVendorCredentialResolver, deriveNativeCredentialAffinityKey, NativeEgressGrantExpiredError, NativeEgressNotDeclaredError, NativeIdleTimeoutError, NativeNetworkError, NativeProxyExpiredError, resolveNativeGatewayProxy, } from "./runtime/native-network.js";
23
+ export { NativeEgressGrantExpiredError, NativeEgressNotDeclaredError, NativeIdleTimeoutError, NativeNetworkError, NativeProxyExpiredError, } from "./runtime/native-network-errors.js";
25
24
  export { generateInsights } from "./runtime/insights.js";
26
25
  export { wrapWithInstrumentation, } from "./runtime/instrumentation.js";
27
- export { prevalidate } from "./runtime/prevalidate.js";
28
26
  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";
27
+ export { APIFUSE__CDP_POOL__URL, APIFUSE__RESOLVER__2CAPTCHA__API_KEY, APIFUSE__RESOLVER__CAPMONSTER__API_KEY, APIFUSE__RESOLVER__CAPSOLVER__API_KEY, APIFUSE__RESOLVER__TIMEOUT_MS, DEFAULT_RESOLVER_TIMEOUT_MS, } from "./runtime/resolver-config.js";
28
+ export { createUnsupportedResolverClient } from "./runtime/resolver-shared.js";
30
29
  export { assertRequiredSecretsPresent, listMissingRequiredSecrets, MISSING_SECRET_CODE, } from "./runtime/secrets.js";
31
30
  export { createUnsupportedProviderRuntimeState, UnsupportedProviderStateError, } from "./runtime/state.js";
32
- export { createStealthClient } from "./runtime/stealth.js";
33
31
  export { APIFUSE__OCR__API_KEY_ENV, APIFUSE__OCR__BACKEND_ENV, APIFUSE__OCR__BASE_URL_ENV, APIFUSE__OCR__CLOUDFLARE_API_TOKEN_ENV, APIFUSE__OCR__MODEL_ENV, CLOUDFLARE_ACCOUNT_ID_ENV, CLOUDFLARE_WORKERS_AI_OCR_BACKEND, createCloudflareWorkersAiOcrClient, createOcrClientFromEnv, createOpenAiCompatibleOcrClient, createUnsupportedOcrClient, DEFAULT_CLOUDFLARE_WORKERS_AI_OCR_MODEL, extractCaptchaCandidates, OPENAI_COMPATIBLE_OCR_BACKEND, } from "./runtime/ocr.js";
34
32
  export { APIFUSE__STT__BACKEND_ENV, APIFUSE__STT__CLOUDFLARE_API_TOKEN_ENV, APIFUSE__STT__MODEL_ENV, createSttClientFromEnv, createUnsupportedSttClient, extractVerificationCode, resolveSttPrompt, } from "./runtime/stt.js";
35
33
  export { createTraceContext, } from "./runtime/trace.js";
36
34
  export { APIFUSE_DESCRIPTION_KEY_META_KEY, APIFUSE_REDACTION_MARKER, APIFUSE_SENSITIVE_KIND_META_KEY, APIFUSE_SENSITIVE_META_KEY, collectSensitivePaths, describeKey, field, fields, isSensitiveSchema, redactPayload, sensitive, z, } from "./schema.js";
37
- export { createServerApp, ERROR_OBSERVABILITY_HEADER, serve, } from "./server/index.js";
35
+ export { createServerApp, createServerAppAsync, ERROR_OBSERVABILITY_HEADER, serve, } from "./server/serve.js";
38
36
  export { getStealthProfile, listStealthProfiles } from "./stealth/profiles.js";
39
37
  export * from "./stream.js";
40
38
  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";
@@ -8,5 +8,6 @@ export { getProviderLocalePath, providerLocaleKey, qualifyProviderLocaleKey, } f
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
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
- 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";
11
+ export { NativeEgressGrantExpiredError, NativeEgressNotDeclaredError, NativeIdleTimeoutError, NativeNetworkError, NativeProxyExpiredError, } from "./runtime/native-network-errors.js";
12
+ export type { NativeGatewayProxy, NativeGatewayProxyResolutionInput, NativeGatewayProxySkipReason, NativeGatewayProxySynthesizer, NativeGatewayProxySynthesisResult, NativeGatewayProxySynthesisInput, NativeNetworkClientOptions, NativeNetworkErrorCode, VendorCredentialLookup, VendorCredentialResolver, } from "./runtime/native-network.js";
12
13
  export { HttpRetryAfterPolicy, HttpRetryDelayStrategy, HttpRetryJitter, HttpRetryPreset, HttpRetryUnsafeMethodPolicy, } from "./types.js";
package/dist/provider.js CHANGED
@@ -6,5 +6,5 @@ export { AuthError, HttpRedirectError, isProviderError, isSessionExpiredError, i
6
6
  export { getProviderLocalePath, providerLocaleKey, qualifyProviderLocaleKey, } from "./i18n/index.js";
7
7
  export { createProviderChoiceContext, createTestProviderChoiceContext, PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV, } from "./runtime/choice.js";
8
8
  export { APIFUSE_DESCRIPTION_KEY_META_KEY, APIFUSE_REDACTION_MARKER, APIFUSE_SENSITIVE_KIND_META_KEY, APIFUSE_SENSITIVE_META_KEY, collectSensitivePaths, describeKey, field, fields, isSensitiveSchema, redactPayload, sensitive, z, } from "./schema.js";
9
- export { createNativeNetworkClient, createEnvVendorCredentialResolver, deriveNativeCredentialAffinityKey, NativeEgressGrantExpiredError, NativeEgressNotDeclaredError, NativeIdleTimeoutError, NativeNetworkError, NativeProxyExpiredError, resolveNativeGatewayProxy, } from "./runtime/native-network.js";
9
+ export { NativeEgressGrantExpiredError, NativeEgressNotDeclaredError, NativeIdleTimeoutError, NativeNetworkError, NativeProxyExpiredError, } from "./runtime/native-network-errors.js";
10
10
  export { HttpRetryAfterPolicy, HttpRetryDelayStrategy, HttpRetryJitter, HttpRetryPreset, HttpRetryUnsafeMethodPolicy, } from "./types.js";
@@ -1,7 +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
+ import { createUnsupportedResolverClient } from "./resolver-shared.js";
5
5
  import { createUnsupportedSttClient } from "./stt.js";
6
6
  function normalizeAllowedKeys(allowedKeys) {
7
7
  return new Set(allowedKeys.filter((key) => key.trim().length > 0));
@@ -4,5 +4,6 @@ export type HttpClientOptions = ProxyResolutionOptions & {
4
4
  warn?: (message: string) => void;
5
5
  userAgent?: string;
6
6
  onRetrySummary?: (summary: HttpRetrySummary) => void;
7
+ signal?: AbortSignal;
7
8
  };
8
9
  export declare function createHttpClient(baseUrl?: string, clientOptions?: HttpClientOptions): HttpClient;
@@ -11,10 +11,42 @@ function isHttpStatusOutcome(outcome) {
11
11
  function isDedupeSkipOutcome(outcome) {
12
12
  return "kind" in outcome && outcome.kind === "dedupe-skip";
13
13
  }
14
- async function sleep(ms) {
14
+ function toAmbientCancellationError(signal, error = signal.reason) {
15
+ if (error instanceof TransportError && error.code === "transport_cancelled") {
16
+ return error;
17
+ }
18
+ return new TransportError("Request cancelled", {
19
+ code: "transport_cancelled",
20
+ status: 0,
21
+ retryable: false,
22
+ ...(error !== undefined
23
+ ? { cause: error instanceof Error ? error : new Error(String(error)) }
24
+ : {}),
25
+ });
26
+ }
27
+ function throwIfAmbientAborted(signal) {
28
+ if (signal?.aborted)
29
+ throw toAmbientCancellationError(signal);
30
+ }
31
+ async function sleep(ms, signal) {
32
+ throwIfAmbientAborted(signal);
15
33
  if (ms <= 0)
16
34
  return;
17
- await new Promise((resolve) => setTimeout(resolve, ms));
35
+ if (!signal) {
36
+ await new Promise((resolve) => setTimeout(resolve, ms));
37
+ return;
38
+ }
39
+ await new Promise((resolve, reject) => {
40
+ const onAbort = () => {
41
+ clearTimeout(timer);
42
+ reject(toAmbientCancellationError(signal));
43
+ };
44
+ const timer = setTimeout(() => {
45
+ signal.removeEventListener("abort", onAbort);
46
+ resolve();
47
+ }, ms);
48
+ signal.addEventListener("abort", onAbort, { once: true });
49
+ });
18
50
  }
19
51
  function toUpstreamHttpError(status) {
20
52
  return new TransportError(`Upstream request failed with status ${status}`, {
@@ -53,7 +85,17 @@ function parseJson(body) {
53
85
  function isTimeoutMessage(message) {
54
86
  return /\b(timed out|timeout|deadline exceeded)\b/i.test(message);
55
87
  }
56
- function toHttpTransportError(error) {
88
+ function toHttpTransportError(error, ambientSignal, timeoutSignal) {
89
+ if (ambientSignal?.aborted) {
90
+ return toAmbientCancellationError(ambientSignal, error);
91
+ }
92
+ if (timeoutSignal?.aborted) {
93
+ return new TransportError("Request timed out", {
94
+ code: "transport_timeout",
95
+ status: 0,
96
+ ...(error instanceof Error ? { cause: error } : {}),
97
+ });
98
+ }
57
99
  if (error instanceof TransportError) {
58
100
  if (error.code) {
59
101
  return error;
@@ -125,6 +167,76 @@ function requireNativeResponseBody(response) {
125
167
  }
126
168
  return response.body;
127
169
  }
170
+ function mergeAbortSignals(...signals) {
171
+ const activeSignals = signals.filter((signal) => signal != null);
172
+ if (activeSignals.length === 0)
173
+ return undefined;
174
+ if (activeSignals.length === 1)
175
+ return activeSignals[0];
176
+ return AbortSignal.any(activeSignals);
177
+ }
178
+ function cancelStreamOnAbort(body, signal) {
179
+ if (!signal)
180
+ return body;
181
+ let reader;
182
+ let finished = false;
183
+ let streamController;
184
+ const cleanup = () => signal.removeEventListener("abort", onAbort);
185
+ const onAbort = () => {
186
+ if (finished)
187
+ return;
188
+ finished = true;
189
+ cleanup();
190
+ const reason = toAmbientCancellationError(signal);
191
+ streamController?.error(reason);
192
+ const cancellation = reader ? reader.cancel(reason) : body.cancel(reason);
193
+ void cancellation.catch(() => undefined).finally(() => reader?.releaseLock());
194
+ };
195
+ return new ReadableStream({
196
+ start(controller) {
197
+ streamController = controller;
198
+ signal.addEventListener("abort", onAbort, { once: true });
199
+ if (signal.aborted)
200
+ onAbort();
201
+ },
202
+ async pull(controller) {
203
+ try {
204
+ reader ??= body.getReader();
205
+ const chunk = await reader.read();
206
+ if (finished)
207
+ return;
208
+ if (chunk.done) {
209
+ finished = true;
210
+ cleanup();
211
+ controller.close();
212
+ reader.releaseLock();
213
+ return;
214
+ }
215
+ controller.enqueue(chunk.value);
216
+ }
217
+ catch (error) {
218
+ if (finished)
219
+ return;
220
+ finished = true;
221
+ cleanup();
222
+ controller.error(error);
223
+ reader?.releaseLock();
224
+ }
225
+ },
226
+ async cancel(reason) {
227
+ if (finished)
228
+ return;
229
+ finished = true;
230
+ cleanup();
231
+ try {
232
+ await (reader ? reader.cancel(reason) : body.cancel(reason));
233
+ }
234
+ finally {
235
+ reader?.releaseLock();
236
+ }
237
+ },
238
+ }, { highWaterMark: 0 });
239
+ }
128
240
  function sanitizeStreamErrors(body, serializedUrl) {
129
241
  if (serializedUrl.sensitiveValues.length === 0)
130
242
  return body;
@@ -154,9 +266,9 @@ function sanitizeStreamErrors(body, serializedUrl) {
154
266
  },
155
267
  }, { highWaterMark: 0 });
156
268
  }
157
- function toNativeHttpStreamResponse(response, serializedUrl) {
269
+ function toNativeHttpStreamResponse(response, serializedUrl, signal) {
158
270
  const headers = Object.fromEntries(response.headers.entries());
159
- const body = sanitizeStreamErrors(requireNativeResponseBody(response), serializedUrl);
271
+ const body = sanitizeStreamErrors(cancelStreamOnAbort(requireNativeResponseBody(response), signal), serializedUrl);
160
272
  return {
161
273
  body,
162
274
  headers,
@@ -418,15 +530,18 @@ async function fetchNativeHttp(baseUrl, url, method, options, clientOptions, war
418
530
  const serializedUrl = serializeHttpRequestUrl(baseUrl, url, options);
419
531
  const { requestUrl } = serializedUrl;
420
532
  const controller = options.timeout ? new AbortController() : undefined;
533
+ const signal = mergeAbortSignals(clientOptions.signal, controller?.signal);
421
534
  const timeoutHandle = options.timeout
422
535
  ? setTimeout(() => controller?.abort(), options.timeout)
423
536
  : undefined;
424
537
  let proxy;
425
538
  try {
539
+ throwIfAmbientAborted(clientOptions.signal);
426
540
  // Resolve inside the try (and after the timeout is armed) so allocator
427
541
  // failures are branded as TransportErrors and count against the request
428
542
  // deadline, exactly as an inline resolve would.
429
543
  proxy = await resolveNativeProxy(options, clientOptions, warn, proxyAttemptOffset);
544
+ throwIfAmbientAborted(clientOptions.signal);
430
545
  // For a registry allocator chain, skip an endpoint a prior attempt already
431
546
  // tried rather than re-issuing the same request. Returning the sentinel
432
547
  // (instead of breaking) lets the loop keep advancing the flat offset until
@@ -441,7 +556,7 @@ async function fetchNativeHttp(baseUrl, url, method, options, clientOptions, war
441
556
  headers: options.headers,
442
557
  method,
443
558
  ...(proxy ? { proxy } : {}),
444
- signal: controller?.signal,
559
+ ...(signal ? { signal } : {}),
445
560
  };
446
561
  if (options.body !== undefined) {
447
562
  requestInit.body = normalizeNativeFetchBody(options.body);
@@ -471,7 +586,7 @@ async function fetchNativeHttp(baseUrl, url, method, options, clientOptions, war
471
586
  if (error instanceof SyntaxError) {
472
587
  throw redactSensitiveError(error, serializedUrl.sensitiveValues, serializedUrl.requestUrl, serializedUrl.redactedUrl);
473
588
  }
474
- const transportError = redactSensitiveError(toHttpTransportError(error), serializedUrl.sensitiveValues, serializedUrl.requestUrl, serializedUrl.redactedUrl);
589
+ const transportError = redactSensitiveError(toHttpTransportError(error, clientOptions.signal, controller?.signal), serializedUrl.sensitiveValues, serializedUrl.requestUrl, serializedUrl.redactedUrl);
475
590
  transportError.proxyUsed = Boolean(proxy);
476
591
  throw transportError;
477
592
  }
@@ -484,16 +599,19 @@ async function fetchNativeHttpStream(baseUrl, url, method, options, clientOption
484
599
  const serializedUrl = serializeHttpRequestUrl(baseUrl, url, options);
485
600
  const { requestUrl } = serializedUrl;
486
601
  const controller = options.timeout ? new AbortController() : undefined;
602
+ const signal = mergeAbortSignals(clientOptions.signal, controller?.signal);
487
603
  const timeoutHandle = options.timeout
488
604
  ? setTimeout(() => controller?.abort(), options.timeout)
489
605
  : undefined;
490
606
  try {
607
+ throwIfAmbientAborted(clientOptions.signal);
491
608
  const proxy = await resolveNativeProxy(options, clientOptions, warn);
609
+ throwIfAmbientAborted(clientOptions.signal);
492
610
  const requestInit = {
493
611
  headers: options.headers,
494
612
  method,
495
613
  ...(proxy ? { proxy } : {}),
496
- signal: controller?.signal,
614
+ ...(signal ? { signal } : {}),
497
615
  };
498
616
  if (options.body !== undefined) {
499
617
  requestInit.body = normalizeNativeFetchBody(options.body);
@@ -506,13 +624,15 @@ async function fetchNativeHttpStream(baseUrl, url, method, options, clientOption
506
624
  status: response.status,
507
625
  });
508
626
  }
509
- return toNativeHttpStreamResponse(response, serializedUrl);
627
+ // Per-call timeout remains header-scoped, while the ambient request signal
628
+ // stays attached to the response body for its full consumption lifetime.
629
+ return toNativeHttpStreamResponse(response, serializedUrl, clientOptions.signal);
510
630
  }
511
631
  catch (error) {
512
632
  if (error instanceof SyntaxError) {
513
633
  throw redactSensitiveError(error, serializedUrl.sensitiveValues, serializedUrl.requestUrl, serializedUrl.redactedUrl);
514
634
  }
515
- throw redactSensitiveError(toHttpTransportError(error), serializedUrl.sensitiveValues, serializedUrl.requestUrl, serializedUrl.redactedUrl);
635
+ throw redactSensitiveError(toHttpTransportError(error, clientOptions.signal, controller?.signal), serializedUrl.sensitiveValues, serializedUrl.requestUrl, serializedUrl.redactedUrl);
516
636
  }
517
637
  finally {
518
638
  if (timeoutHandle)
@@ -616,6 +736,7 @@ export function createHttpClient(baseUrl, clientOptions = {}) {
616
736
  const dedupeContext = dedupeAllocatorEndpoints ? { attempted: new Set() } : undefined;
617
737
  const executeOnce = (proxyAttemptOffset = 0) => fetchNativeHttp(baseUrl, url, methodName, attemptOptions, clientOptions, warnOnce, statusRetryEnabled ? retryOptions?.statusCodes : undefined, proxyAttemptOffset, dedupeContext);
618
738
  if (!retryEnabled || !retryOptions) {
739
+ throwIfAmbientAborted(clientOptions.signal);
619
740
  const outcome = await executeOnce();
620
741
  if (isDedupeSkipOutcome(outcome)) {
621
742
  // Single-shot path never de-duplicates (dedupeContext is undefined),
@@ -639,6 +760,7 @@ export function createHttpClient(baseUrl, clientOptions = {}) {
639
760
  // allocations skip offsets.
640
761
  let issued = 0;
641
762
  for (let attempt = 1; attempt <= transportAttemptCap; attempt += 1) {
763
+ throwIfAmbientAborted(clientOptions.signal);
642
764
  // Whether this offset actually issued a request (vs. a skipped duplicate),
643
765
  // so the catch counts a thrown *transport* failure once without
644
766
  // double-counting a status outcome that already incremented before it
@@ -657,7 +779,7 @@ export function createHttpClient(baseUrl, clientOptions = {}) {
657
779
  if (isHttpStatusOutcome(outcome)) {
658
780
  lastStatus = outcome.status;
659
781
  if (outcome.retryable && issued < retryOptions.attempts) {
660
- await sleep(computeProxyTransportRetryDelayMs(retryOptions, attempt, outcome.headers));
782
+ await sleep(computeProxyTransportRetryDelayMs(retryOptions, attempt, outcome.headers), clientOptions.signal);
661
783
  continue;
662
784
  }
663
785
  throw toUpstreamHttpError(outcome.status);
@@ -680,6 +802,7 @@ export function createHttpClient(baseUrl, clientOptions = {}) {
680
802
  return response;
681
803
  }
682
804
  catch (error) {
805
+ throwIfAmbientAborted(clientOptions.signal);
683
806
  if (!issuedThisAttempt)
684
807
  issued += 1;
685
808
  lastError = error;
@@ -694,7 +817,7 @@ export function createHttpClient(baseUrl, clientOptions = {}) {
694
817
  options: retryOptions,
695
818
  proxyUsed,
696
819
  })) {
697
- await sleep(computeProxyTransportRetryDelayMs(retryOptions, attempt));
820
+ await sleep(computeProxyTransportRetryDelayMs(retryOptions, attempt), clientOptions.signal);
698
821
  continue;
699
822
  }
700
823
  throw error;