@apifuse/provider-sdk 2.2.0-beta.16 → 2.2.0-beta.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AUTHORING.md CHANGED
@@ -742,6 +742,46 @@ const credentialsAuth = defineCredentialsAuth({
742
742
  `bunx playwright install chromium`, or set
743
743
  `APIFUSE__CDP_POOL__URL` for remote browser debugging.
744
744
 
745
+ ### Native gateway adapter migration
746
+
747
+ Native proxy resolution supports both HTTP CONNECT and SOCKS5 without
748
+ terminating origin TLS. The built-in vendor order follows `proxy.providers`
749
+ exactly: `smartproxy` means the `api.smartproxy.org` allocation vendor (raw
750
+ `ip:port` endpoints), while `nodemaven` is the credentialed gateway. It is not
751
+ the company formerly called Smartproxy; that separate company is represented
752
+ by the deprecated `decodo` name.
753
+
754
+ Custom `gatewaySynthesizers` must now accept the selected `protocol` and the
755
+ injected `credentials` resolver on `NativeGatewayProxySynthesisInput`.
756
+ Synthesizers may be async because allocation vendors perform network I/O. They
757
+ may return a proxy, `undefined` when they do not implement the offered vendor,
758
+ or `{ kind: "skipped", reason }` so an exhausted required chain can explain an
759
+ absent credential, unsupported protocol, or allocation failure. Accordingly,
760
+ `resolveNativeGatewayProxy(...)` must now be awaited.
761
+
762
+ Callers that do not supply custom synthesizers or credentials keep env-backed
763
+ behavior. Hosts that already have an allowlisted `EnvContext` can inject it
764
+ explicitly, and vault-backed or per-tenant hosts can supply their own resolver:
765
+
766
+ ```ts
767
+ import {
768
+ createEnvVendorCredentialResolver,
769
+ createNativeNetworkClient,
770
+ } from "@apifuse/provider-sdk";
771
+
772
+ const network = createNativeNetworkClient({
773
+ proxyPolicy: { mode: "required", providers: ["smartproxy", "nodemaven"] },
774
+ credentials: createEnvVendorCredentialResolver(ctx.env),
775
+ // Optional advanced override; omit for each vendor's default.
776
+ proxyProtocol: "socks5",
777
+ });
778
+ ```
779
+
780
+ Never include credential values in adapter skip messages or thrown errors. The
781
+ SDK redacts built-in proxy URL userinfo, CONNECT authentication, and allocator
782
+ causes, but a custom adapter remains responsible for not publishing secrets in
783
+ its own diagnostics.
784
+
745
785
  ### Limiting stealth response bodies
746
786
 
747
787
  Set `maxBodyBytes` on `ctx.stealth.fetch()` or `session.redirects.run()` when an
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @apifuse/provider-sdk Changelog
2
2
 
3
+ ## 2.2.0-beta.18
4
+
5
+ - Release candidate for main commit cebaf4b994918d2641c0fe6681fbffd3a3284c5d.
6
+
7
+ ## 2.2.0-beta.17
8
+
9
+ - Release candidate for main commit 5a0f9127a5861992d5c144b07ad379a085756544.
10
+
3
11
  ## 2.2.0-beta.16
4
12
 
5
13
  - Release candidate for main commit c5deb2bc31a4a4a236d27a2ce3d214b533ed358f.
@@ -94,6 +102,7 @@
94
102
 
95
103
  ## Unreleased
96
104
 
105
+ - **Breaking for custom native gateway adapters:** `NativeGatewayProxySynthesisInput` now includes an injected `credentials` resolver and selected `protocol`; synthesizers may return promises and structured skip reasons, and `resolveNativeGatewayProxy` is async. Default callers retain env-backed behavior. Native transport now supports both HTTP CONNECT and SOCKS5, defaults per vendor with an explicit runtime override, registers smartproxy allocation ahead of nodemaven when declared in that order, and reports every exhausted vendor reason without exposing proxy credentials.
97
106
  - Honor operation `docs.errorCodes` at runtime: declared provider-owned statuses and retryability now drive the HTTP envelope, observability header, and structured log; invalid statuses fail `defineProvider`, declared codes no longer emit the unregistered-code signal, and `TransportError` status-preservation workarounds are obsolete.
98
107
  - Add an opt-in same-origin redirect hop policy to `ctx.http`, with bounded manual following and typed failures before a refused target is requested.
99
108
  - Enforce provider-declared native TCP/TLS egress before proxy or socket setup, with revocable and expiring dynamic grants plus typed authorization failures; providers without a native egress declaration retain legacy behavior.
@@ -150,12 +150,31 @@ export declare function resolveProxyConfigAsync(options?: ProxyResolutionOptions
150
150
  * Vendor allocation and failover remain owned by the SDK.
151
151
  */
152
152
  export declare function resolveProxy(options?: ProxyResolutionOptions): Promise<ResolvedProxyConfig>;
153
+ /**
154
+ * Each vendor's default egress protocol, chosen from live KR benchmarks. HTTP
155
+ * CONNECT wins for nodemaven (socks5 adds ~500ms through the gateway) and ties
156
+ * for smartproxy, and is the only protocol ctx.http (Bun native fetch) supports.
157
+ * Override per call via ProxyResolutionOptions.protocol (harness/tests).
158
+ */
159
+ export declare const VENDOR_DEFAULT_PROTOCOL: Readonly<Record<ProxyVendorName, ProxyProtocol>>;
153
160
  /**
154
161
  * Guard the No-MITM invariant: a resolved proxy URL must use a tunnelling scheme
155
162
  * (http CONNECT or socks5) so the client TLS handshake reaches the origin
156
163
  * end-to-end. Anything else would intercept TLS and break fingerprinting.
157
164
  */
158
165
  export declare function assertTunnelingScheme(url: string): void;
166
+ export type ProxyVendorResolutionContext = {
167
+ readonly protocol: ProxyProtocol;
168
+ readonly poolIndex: number;
169
+ readonly refreshEpoch: number;
170
+ /** Explicit vendor credentials. Omit only on the legacy ambient-env path. */
171
+ readonly credentials?: Readonly<Record<string, string>>;
172
+ /** Disable non-policy env defaults for deterministic injected adapters. */
173
+ readonly ambientDefaults?: boolean;
174
+ /** Disable env-discovered Redis sharing for deterministic injected adapters. */
175
+ readonly sharedCache?: boolean;
176
+ };
177
+ export declare function resolveWithVendor(vendor: ProxyVendorName, policy: ProviderProxyPolicy, options: ProxyResolutionOptions, context: ProxyVendorResolutionContext): Promise<ResolvedProxyConfig>;
159
178
  /**
160
179
  * Ordered list of SDK-native proxy vendors declared by the policy. `providers`
161
180
  * takes precedence over the legacy singular `provider`; the platform default
@@ -2,7 +2,7 @@ import { createHash, randomUUID } from "node:crypto";
2
2
  import { existsSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { Redis } from "ioredis";
5
- import { NODEMAVEN_DEFAULT_PROTOCOL, NODEMAVEN_MAX_POOL_SIZE, hasNodemavenCredentials, nodemavenPoolSize, synthesizeNodemavenProxy, } from "../runtime/proxy-nodemaven.js";
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
8
8
  // named Smartproxy (smartproxy.com), which rebranded to Decodo in 2025 and is
@@ -415,7 +415,7 @@ export async function resolveProxy(options = {}) {
415
415
  * for smartproxy, and is the only protocol ctx.http (Bun native fetch) supports.
416
416
  * Override per call via ProxyResolutionOptions.protocol (harness/tests).
417
417
  */
418
- const VENDOR_DEFAULT_PROTOCOL = {
418
+ export const VENDOR_DEFAULT_PROTOCOL = {
419
419
  smartproxy: "http",
420
420
  nodemaven: NODEMAVEN_DEFAULT_PROTOCOL,
421
421
  };
@@ -436,16 +436,33 @@ export function assertTunnelingScheme(url) {
436
436
  throw new ProxyResolutionError("PROXY_ALLOCATION_FAILED", `Resolved proxy scheme "${scheme}" is not a tunnelling scheme (expected http or socks5). Refusing to route TLS through a non-tunnelling proxy.`);
437
437
  }
438
438
  }
439
- async function resolveWithVendor(vendor, policy, options, context) {
439
+ export async function resolveWithVendor(vendor, policy, options, context) {
440
440
  if (vendor === "nodemaven") {
441
441
  const startedAt = Date.now();
442
+ const username = (context.credentials === undefined
443
+ ? process.env[NODEMAVEN_USERNAME_ENV]
444
+ : context.credentials[NODEMAVEN_USERNAME_ENV])?.trim();
445
+ const password = (context.credentials === undefined
446
+ ? process.env[NODEMAVEN_PASSWORD_ENV]
447
+ : context.credentials[NODEMAVEN_PASSWORD_ENV])?.trim();
448
+ const filter = context.credentials === undefined
449
+ ? process.env[NODEMAVEN_FILTER_ENV]
450
+ : context.credentials[NODEMAVEN_FILTER_ENV];
451
+ if (!username || !password) {
452
+ throw new ProxyResolutionError("PROXY_ALLOCATION_FAILED", `NodeMaven credentials missing: set ${NODEMAVEN_USERNAME_ENV} and ${NODEMAVEN_PASSWORD_ENV}.`, { vendor: "nodemaven" });
453
+ }
442
454
  const synthesized = synthesizeNodemavenProxy({
443
455
  policy,
456
+ credentials: {
457
+ username,
458
+ password,
459
+ ...(filter ? { filter } : {}),
460
+ },
444
461
  affinityKey: options.affinityKey,
445
462
  protocol: context.protocol,
446
463
  poolIndex: context.poolIndex,
447
464
  refreshEpoch: context.refreshEpoch,
448
- country: resolveSmartproxyCountry(policy),
465
+ country: resolveSmartproxyCountry(policy, context.ambientDefaults !== false),
449
466
  });
450
467
  options.telemetry?.recordProxyResolution({
451
468
  provider: "nodemaven",
@@ -468,13 +485,15 @@ async function resolveWithVendor(vendor, policy, options, context) {
468
485
  };
469
486
  }
470
487
  // smartproxy allocation-style vendor.
471
- const appKey = process.env[SMARTPROXY_APP_KEY_ENV]?.trim();
488
+ const appKey = (context.credentials === undefined
489
+ ? process.env[SMARTPROXY_APP_KEY_ENV]
490
+ : context.credentials[SMARTPROXY_APP_KEY_ENV])?.trim();
472
491
  if (!appKey) {
473
492
  // Guarded by vendorHasCredentials; treated as a vendor-internal failure.
474
493
  throw new ProxyResolutionError("PROXY_ALLOCATION_FAILED", `${SMARTPROXY_APP_KEY_ENV} is not configured.`, { vendor: "smartproxy" });
475
494
  }
476
- const lifetimeMinutes = resolveSmartproxyLifetime(policy);
477
- const allocated = await allocateSmartproxy(policy, appKey, lifetimeMinutes, options.affinityKey, context.protocol);
495
+ const lifetimeMinutes = resolveSmartproxyLifetime(policy, context.ambientDefaults !== false);
496
+ const allocated = await allocateSmartproxy(policy, appKey, lifetimeMinutes, options.affinityKey, context.protocol, context.ambientDefaults !== false, context.sharedCache !== false);
478
497
  options.telemetry?.recordProxyResolution({ ...allocated.telemetry, protocol: context.protocol });
479
498
  const poolIndex = selectProxyPoolIndex(allocated.pool.urls.length, context.poolIndex);
480
499
  const url = allocated.pool.urls[poolIndex];
@@ -665,11 +684,15 @@ export function mapFlatAttempt(flat, sizes) {
665
684
  }
666
685
  return { vendorIndex: 0, poolIndex: 0 };
667
686
  }
668
- function resolveSmartproxyCountry(policy) {
669
- return (policy.geo?.country ?? process.env[DEFAULT_PROXY_COUNTRY_ENV]?.trim().toUpperCase() ?? undefined);
687
+ function resolveSmartproxyCountry(policy, ambientDefaults = true) {
688
+ return (policy.geo?.country ??
689
+ (ambientDefaults
690
+ ? process.env[DEFAULT_PROXY_COUNTRY_ENV]?.trim().toUpperCase() || undefined
691
+ : undefined));
670
692
  }
671
- function resolveSmartproxyLifetime(policy) {
672
- const configuredLifetime = policy.session?.lifetimeMinutes ?? readPositiveNumberEnv(DEFAULT_PROXY_LIFETIME_ENV, 30);
693
+ function resolveSmartproxyLifetime(policy, ambientDefaults = true) {
694
+ const configuredLifetime = policy.session?.lifetimeMinutes ??
695
+ (ambientDefaults ? readPositiveNumberEnv(DEFAULT_PROXY_LIFETIME_ENV, 30) : 30);
673
696
  return Math.min(SMARTPROXY_MAX_LIFETIME_MINUTES, Math.max(1, Math.floor(configuredLifetime)));
674
697
  }
675
698
  function readPositiveNumberEnv(name, fallback) {
@@ -692,20 +715,24 @@ function selectProxyPoolIndex(poolSize, attempt = 0) {
692
715
  const normalizedAttempt = Number.isFinite(attempt) ? Math.max(0, Math.floor(attempt)) : 0;
693
716
  return normalizedAttempt % poolSize;
694
717
  }
695
- function buildSmartproxyCacheKey(policy, affinityKey, lifetimeMinutes, protocol) {
718
+ function buildSmartproxyCacheKey(policy, appKey, affinityKey, lifetimeMinutes, protocol, ambientDefaults = true) {
696
719
  const poolSize = resolveSmartproxyPoolSize(policy);
697
720
  return JSON.stringify({
698
721
  provider: "smartproxy",
722
+ credentialHash: createHash("sha256")
723
+ .update("apifuse-smartproxy-credential:v1\0")
724
+ .update(appKey)
725
+ .digest("hex"),
699
726
  protocol,
700
- country: resolveSmartproxyCountry(policy),
727
+ country: resolveSmartproxyCountry(policy, ambientDefaults),
701
728
  affinity: policy.session?.affinity ?? "request",
702
729
  affinityKey: (policy.session?.affinity ?? "request") === "request" ? undefined : affinityKey,
703
730
  lifetimeMinutes,
704
731
  poolSize,
705
732
  });
706
733
  }
707
- async function allocateSmartproxy(policy, appKey, lifetimeMinutes, affinityKey, protocol) {
708
- const cacheKey = buildSmartproxyCacheKey(policy, affinityKey, lifetimeMinutes, protocol);
734
+ async function allocateSmartproxy(policy, appKey, lifetimeMinutes, affinityKey, protocol, ambientDefaults = true, sharedCache = true) {
735
+ const cacheKey = buildSmartproxyCacheKey(policy, appKey, affinityKey, lifetimeMinutes, protocol, ambientDefaults);
709
736
  const startedAt = Date.now();
710
737
  const now = startedAt;
711
738
  const invalidatedUntil = invalidatedProxyKeys.get(cacheKey) ?? 0;
@@ -713,7 +740,7 @@ async function allocateSmartproxy(policy, appKey, lifetimeMinutes, affinityKey,
713
740
  const cached = proxyCache.get(cacheKey);
714
741
  if (!skipCached && cached && isFresh(cached, now)) {
715
742
  if (shouldSoftRefresh(cached, now)) {
716
- void refreshSmartproxyPool(cacheKey, policy, appKey, lifetimeMinutes, protocol);
743
+ void refreshSmartproxyPool(cacheKey, policy, appKey, lifetimeMinutes, protocol, ambientDefaults, sharedCache);
717
744
  return {
718
745
  pool: cached,
719
746
  telemetry: telemetryForPool(cached, "soft_stale_refresh", startedAt, {
@@ -726,7 +753,7 @@ async function allocateSmartproxy(policy, appKey, lifetimeMinutes, affinityKey,
726
753
  telemetry: telemetryForPool(cached, "memory_hit", startedAt),
727
754
  };
728
755
  }
729
- if (!skipCached) {
756
+ if (!skipCached && sharedCache) {
730
757
  const redisResult = await readSmartproxyRedisPool(cacheKey, startedAt);
731
758
  if (redisResult)
732
759
  return redisResult;
@@ -741,7 +768,7 @@ async function allocateSmartproxy(policy, appKey, lifetimeMinutes, affinityKey,
741
768
  }),
742
769
  };
743
770
  }
744
- const promise = allocateSmartproxyShared(cacheKey, policy, appKey, lifetimeMinutes, startedAt, protocol).finally(() => {
771
+ const promise = allocateSmartproxyShared(cacheKey, policy, appKey, lifetimeMinutes, startedAt, protocol, ambientDefaults, sharedCache).finally(() => {
745
772
  proxyInflight.delete(cacheKey);
746
773
  });
747
774
  proxyInflight.set(cacheKey, promise);
@@ -772,9 +799,9 @@ async function readSmartproxyRedisPool(cacheKey, startedAt) {
772
799
  telemetry: telemetryForPool(pool, "redis_hit", startedAt, { redisReadMs }),
773
800
  };
774
801
  }
775
- async function refreshSmartproxyPool(cacheKey, policy, appKey, lifetimeMinutes, protocol) {
802
+ async function refreshSmartproxyPool(cacheKey, policy, appKey, lifetimeMinutes, protocol, ambientDefaults, sharedCache) {
776
803
  try {
777
- await allocateSmartproxyShared(cacheKey, policy, appKey, lifetimeMinutes, Date.now(), protocol, {
804
+ await allocateSmartproxyShared(cacheKey, policy, appKey, lifetimeMinutes, Date.now(), protocol, ambientDefaults, sharedCache, {
778
805
  background: true,
779
806
  });
780
807
  }
@@ -782,10 +809,10 @@ async function refreshSmartproxyPool(cacheKey, policy, appKey, lifetimeMinutes,
782
809
  // Soft refresh is opportunistic; current fresh pool remains usable.
783
810
  }
784
811
  }
785
- async function allocateSmartproxyShared(cacheKey, policy, appKey, lifetimeMinutes, startedAt, protocol, options = {}) {
786
- const redis = getProxyRedis();
812
+ async function allocateSmartproxyShared(cacheKey, policy, appKey, lifetimeMinutes, startedAt, protocol, ambientDefaults, sharedCache, options = {}) {
813
+ const redis = sharedCache ? getProxyRedis() : undefined;
787
814
  if (!redis || !(await ensureRedisReady(redis))) {
788
- return await allocateAndStoreSmartproxyPool(cacheKey, policy, appKey, lifetimeMinutes, startedAt, { cacheStatus: "allocator", protocol });
815
+ return await allocateAndStoreSmartproxyPool(cacheKey, policy, appKey, lifetimeMinutes, startedAt, { cacheStatus: "allocator", protocol, ambientDefaults });
789
816
  }
790
817
  const poolKey = smartproxyRedisPoolKey(cacheKey);
791
818
  const lockKey = smartproxyRedisLockKey(cacheKey);
@@ -800,6 +827,7 @@ async function allocateSmartproxyShared(cacheKey, policy, appKey, lifetimeMinute
800
827
  redis,
801
828
  poolKey,
802
829
  protocol,
830
+ ambientDefaults,
803
831
  });
804
832
  }
805
833
  finally {
@@ -912,7 +940,7 @@ async function readSmartproxyAllocatorBodyWithDeadline(response, signal) {
912
940
  }
913
941
  async function allocateAndStoreSmartproxyPool(cacheKey, policy, appKey, lifetimeMinutes, startedAt, options) {
914
942
  const poolSize = resolveSmartproxyPoolSize(policy);
915
- const allocatorUrl = buildSmartproxyAllocatorUrl(policy, appKey, lifetimeMinutes, poolSize, options.protocol);
943
+ const allocatorUrl = buildSmartproxyAllocatorUrl(policy, appKey, lifetimeMinutes, poolSize, options.protocol, options.ambientDefaults);
916
944
  const allocatorStartedAt = Date.now();
917
945
  const allocatorDeadlineAt = allocatorStartedAt + smartproxyAllocatorDeadlineMs();
918
946
  let allocation;
@@ -970,7 +998,7 @@ async function allocateAndStoreSmartproxyPool(cacheKey, policy, appKey, lifetime
970
998
  expiresAt: allocatedAt + ttlMs,
971
999
  diagnostics: {
972
1000
  provider: "smartproxy",
973
- country: resolveSmartproxyCountry(policy) ?? "default",
1001
+ country: resolveSmartproxyCountry(policy, options.ambientDefaults) ?? "default",
974
1002
  lifetimeMinutes,
975
1003
  affinity: policy.session?.affinity ?? "request",
976
1004
  rawConnect: true,
@@ -1090,7 +1118,7 @@ const SMARTPROXY_PROTOCOL_PARAM = {
1090
1118
  http: "1",
1091
1119
  socks5: "2",
1092
1120
  };
1093
- function buildSmartproxyAllocatorUrl(policy, appKey, lifetimeMinutes, poolSize, protocol) {
1121
+ function buildSmartproxyAllocatorUrl(policy, appKey, lifetimeMinutes, poolSize, protocol, ambientDefaults = true) {
1094
1122
  const params = new URLSearchParams({
1095
1123
  app_key: appKey,
1096
1124
  pt: "9",
@@ -1100,7 +1128,7 @@ function buildSmartproxyAllocatorUrl(policy, appKey, lifetimeMinutes, poolSize,
1100
1128
  format: "txt",
1101
1129
  lb: "\\n",
1102
1130
  });
1103
- const country = resolveSmartproxyCountry(policy);
1131
+ const country = resolveSmartproxyCountry(policy, ambientDefaults);
1104
1132
  if (country) {
1105
1133
  params.set("cc", country);
1106
1134
  }
@@ -1172,7 +1200,10 @@ function markSmartproxyCacheInvalidated(options = {}) {
1172
1200
  return undefined;
1173
1201
  }
1174
1202
  const lifetimeMinutes = resolveSmartproxyLifetime(policy);
1175
- const cacheKey = buildSmartproxyCacheKey(policy, options.affinityKey, lifetimeMinutes, options.protocol ?? VENDOR_DEFAULT_PROTOCOL.smartproxy);
1203
+ const appKey = process.env[SMARTPROXY_APP_KEY_ENV]?.trim();
1204
+ if (!appKey)
1205
+ return undefined;
1206
+ const cacheKey = buildSmartproxyCacheKey(policy, appKey, options.affinityKey, lifetimeMinutes, options.protocol ?? VENDOR_DEFAULT_PROTOCOL.smartproxy);
1176
1207
  invalidatedProxyKeys.set(cacheKey, Date.now() + SMARTPROXY_INVALIDATION_SKIP_REDIS_MS);
1177
1208
  proxyCache.delete(cacheKey);
1178
1209
  proxyInflight.delete(cacheKey);
package/dist/index.d.ts CHANGED
@@ -22,7 +22,7 @@ export { type CreateCredentialContextOptions, createCredentialContext, } from ".
22
22
  export { createEnvContext } from "./runtime/env.js";
23
23
  export { executeOperation } from "./runtime/executor.js";
24
24
  export { createHttpClient } from "./runtime/http.js";
25
- export { createNativeNetworkClient, deriveNativeCredentialAffinityKey, NativeEgressGrantExpiredError, NativeEgressNotDeclaredError, NativeIdleTimeoutError, NativeNetworkError, NativeProxyExpiredError, resolveNativeGatewayProxy, type NativeGatewayProxy, type NativeGatewayProxyResolutionInput, type NativeGatewayProxySynthesizer, type NativeGatewayProxySynthesisInput, type NativeNetworkClientOptions, type NativeNetworkErrorCode, } from "./runtime/native-network.js";
25
+ 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";
26
26
  export type { Insight, InsightSeverity } from "./runtime/insights.js";
27
27
  export { generateInsights } from "./runtime/insights.js";
28
28
  export { type InstrumentationOptions, type InstrumentedProviderContext, wrapWithInstrumentation, } from "./runtime/instrumentation.js";
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ export { createCredentialContext, } from "./runtime/credential.js";
20
20
  export { createEnvContext } from "./runtime/env.js";
21
21
  export { executeOperation } from "./runtime/executor.js";
22
22
  export { createHttpClient } from "./runtime/http.js";
23
- export { createNativeNetworkClient, deriveNativeCredentialAffinityKey, NativeEgressGrantExpiredError, NativeEgressNotDeclaredError, NativeIdleTimeoutError, NativeNetworkError, NativeProxyExpiredError, resolveNativeGatewayProxy, } from "./runtime/native-network.js";
23
+ export { createNativeNetworkClient, createEnvVendorCredentialResolver, deriveNativeCredentialAffinityKey, NativeEgressGrantExpiredError, NativeEgressNotDeclaredError, NativeIdleTimeoutError, NativeNetworkError, NativeProxyExpiredError, resolveNativeGatewayProxy, } from "./runtime/native-network.js";
24
24
  export { generateInsights } from "./runtime/insights.js";
25
25
  export { wrapWithInstrumentation, } from "./runtime/instrumentation.js";
26
26
  export { prevalidate } from "./runtime/prevalidate.js";
@@ -0,0 +1,43 @@
1
+ export type EgressHostKind = "ipv4" | "ipv6" | "ipv4-mapped-ipv6" | "numeric-ambiguous" | "dns";
2
+ export type EgressHostCanonicalizationFailure = "not-string" | "reserved-delimiter" | "control-character" | "whitespace" | "canonicalization-empty";
3
+ export type EgressHostCanonicalizationResult = {
4
+ readonly ok: true;
5
+ readonly host: string;
6
+ } | {
7
+ readonly ok: false;
8
+ readonly reason: EgressHostCanonicalizationFailure;
9
+ };
10
+ export type Ipv6CidrOverlap = "ipv4-mapped" | "ipv4-compatible";
11
+ export type Ipv6CidrParseResult = {
12
+ readonly ok: true;
13
+ readonly network: Uint8Array;
14
+ readonly prefix: number;
15
+ } | {
16
+ readonly ok: false;
17
+ readonly reason: "malformed";
18
+ readonly overlap?: Ipv6CidrOverlap;
19
+ } | {
20
+ readonly ok: false;
21
+ readonly reason: "non-canonical-network";
22
+ };
23
+ export declare function parseStrictIpv4(value: string): number | undefined;
24
+ /** Parse the RFC 4291 IPv6 text forms accepted at both policy and runtime boundaries. */
25
+ export declare function parseIpv6(value: string): Uint8Array | undefined;
26
+ export declare function parseIpv4Cidr(value: string): {
27
+ readonly ok: true;
28
+ readonly network: number;
29
+ readonly prefix: number;
30
+ } | {
31
+ readonly ok: false;
32
+ readonly reason: "malformed" | "non-canonical-network";
33
+ };
34
+ export declare function parseIpv6Cidr(value: string): Ipv6CidrParseResult;
35
+ export declare function ipv4InCidr(address: number, cidr: string): boolean;
36
+ export declare function ipv6InCidr(address: Uint8Array, cidr: string): boolean;
37
+ export declare function embeddedIpv4FromIpv6(address: Uint8Array): number | undefined;
38
+ export declare function formatIpv6(address: Uint8Array): string;
39
+ /** The single family and ambiguity classifier used by policy and runtime matching. */
40
+ export declare function classifyEgressHost(host: string): EgressHostKind;
41
+ export declare function hasReservedEgressHostDelimiter(value: string): boolean;
42
+ export declare function hasEgressHostControlCharacter(value: string): boolean;
43
+ export declare function canonicalizeEgressHost(value: unknown): EgressHostCanonicalizationResult;