@apifuse/provider-sdk 2.2.0-beta.15 → 2.2.0-beta.17

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.
@@ -3,6 +3,11 @@ export declare const NODEMAVEN_USERNAME_ENV = "APIFUSE__PROXY__NODEMAVEN_USERNAM
3
3
  export declare const NODEMAVEN_PASSWORD_ENV = "APIFUSE__PROXY__NODEMAVEN_PASSWORD";
4
4
  export declare const NODEMAVEN_FILTER_ENV = "APIFUSE__PROXY__NODEMAVEN_FILTER";
5
5
  export declare const NODEMAVEN_GATEWAY_HOST = "gate.nodemaven.com";
6
+ export type NodemavenCredentials = {
7
+ readonly username: string;
8
+ readonly password: string;
9
+ readonly filter?: string;
10
+ };
6
11
  /** Both schemes tunnel bytes end-to-end, preserving the client TLS handshake. */
7
12
  export type ProxyProtocol = "http" | "socks5";
8
13
  /**
@@ -23,6 +28,8 @@ export type NodemavenSessionWindow = {
23
28
  export declare function nodemavenSessionWindow(policy: ProviderProxyPolicy, now?: number): NodemavenSessionWindow;
24
29
  export type NodemavenSynthesisInput = {
25
30
  policy: ProviderProxyPolicy;
31
+ /** Explicit credentials; synthesis never reads process-global state. */
32
+ credentials: NodemavenCredentials;
26
33
  affinityKey: string | undefined;
27
34
  protocol: ProxyProtocol;
28
35
  poolIndex: number;
@@ -30,8 +30,8 @@ function readNodemavenUsername() {
30
30
  function readNodemavenPassword() {
31
31
  return process.env[NODEMAVEN_PASSWORD_ENV]?.trim() || undefined;
32
32
  }
33
- function resolveNodemavenFilter() {
34
- const raw = process.env[NODEMAVEN_FILTER_ENV]?.trim().toLowerCase();
33
+ function resolveNodemavenFilter(value) {
34
+ const raw = value?.trim().toLowerCase();
35
35
  if (!raw)
36
36
  return DEFAULT_NODEMAVEN_FILTER;
37
37
  if (!NODEMAVEN_FILTERS.has(raw)) {
@@ -97,12 +97,12 @@ function selectPort(protocol, sid, poolIndex) {
97
97
  * There is no allocation API — geo/session are encoded in the username.
98
98
  */
99
99
  export function synthesizeNodemavenProxy(input) {
100
- const username = readNodemavenUsername();
101
- const password = readNodemavenPassword();
100
+ const username = input.credentials.username.trim();
101
+ const password = input.credentials.password.trim();
102
102
  if (!username || !password) {
103
103
  throw new Error(`NodeMaven credentials missing: set ${NODEMAVEN_USERNAME_ENV} and ${NODEMAVEN_PASSWORD_ENV}.`);
104
104
  }
105
- const filter = resolveNodemavenFilter();
105
+ const filter = resolveNodemavenFilter(input.credentials.filter);
106
106
  const sid = deriveSid(input.policy, input.affinityKey, input.poolIndex, input.refreshEpoch);
107
107
  const port = selectPort(input.protocol, sid, input.poolIndex);
108
108
  const lifetimeMinutes = nodemavenLifetimeMinutes(input.policy);
@@ -3,6 +3,7 @@ import { join } from "node:path";
3
3
  import { Hono } from "hono";
4
4
  import { z } from "zod";
5
5
  import { AuthAbortError, createAuthFlowHelpers } from "../auth.js";
6
+ import { SDK_OWNED_PROVIDER_ERROR_CODES, SDK_RUNTIME_OWNED_ERROR_CODES, } from "../error-resolution.js";
6
7
  import { AuthError, isProviderError, isSessionExpiredError, isTransportError, isValidationError, ProviderError, } from "../errors.js";
7
8
  import { loadProviderLocaleCatalogs, localizeAuthTurn, } from "../i18n/catalog.js";
8
9
  import { categoryForStatus, isRetryableCategory, PROVIDER_OBSERVABILITY_TAXONOMY_VERSION, } from "../observability.js";
@@ -15,7 +16,7 @@ import { createEnvContext } from "../runtime/env.js";
15
16
  import { executeOperation } from "../runtime/executor.js";
16
17
  import { createHttpClient } from "../runtime/http.js";
17
18
  import { wrapWithInstrumentation } from "../runtime/instrumentation.js";
18
- import { createNativeNetworkClient } from "../runtime/native-network.js";
19
+ import { createEnvVendorCredentialResolver, createNativeNetworkClient, } from "../runtime/native-network.js";
19
20
  import { getProviderBaseUrl } from "../runtime/provider.js";
20
21
  import { PROXY_AUTH_IP_DENIED_CODE, PROXY_EDGE_AUTH_REJECTED_CODE, PROXY_POOL_EXHAUSTED_CODE, } from "../runtime/proxy-errors.js";
21
22
  import { PROVIDER_TELEMETRY_HEADER, ProxyTelemetryCollector } from "../runtime/proxy-telemetry.js";
@@ -29,6 +30,7 @@ import { STATEFUL_NONCE_HEADER as STATEFUL_FORWARDING_NONCE_HEADER, STATEFUL_SIG
29
30
  import { StatefulRoutingDeadlineError } from "../stateful/stateful-provider-session-routing.js";
30
31
  import { getStealthProfile } from "../stealth/profiles.js";
31
32
  import { APIFUSE_STREAM_DONE_EVENT, APIFUSE_STREAM_ERROR_EVENT, encodeSseEvent, error as streamError, } from "../stream.js";
33
+ import { VALID_OPERATION_ERROR_STATUSES } from "../types.js";
32
34
  import { createSelfTestApp, createSelfTestAuthFlowInvoke, createSelfTestInvoke, resolveSelfTestPort, } from "./self-test.js";
33
35
  import { resolveSelfTestMasterSecrets } from "./self-test-token.js";
34
36
  import { AuthFlowRequestSchema, OperationConnectionSchema, OperationRequestSchema, } from "./types.js";
@@ -223,6 +225,7 @@ function createProviderContext(provider, request, operationId, options = {}, sta
223
225
  egress: provider.native.network,
224
226
  proxyPolicy: resolveNativeProxyPolicy(provider),
225
227
  affinityKey: proxyClientOptions.affinityKey,
228
+ credentials: createEnvVendorCredentialResolver(env),
226
229
  }),
227
230
  },
228
231
  }
@@ -310,6 +313,7 @@ function createAuthFlowContext(provider, request, options = {}, signal) {
310
313
  egress: provider.native.network,
311
314
  proxyPolicy: resolveNativeProxyPolicy(provider),
312
315
  affinityKey: proxyClientOptions.affinityKey,
316
+ credentials: createEnvVendorCredentialResolver(createEnvContext(provider.secrets?.map((secret) => secret.name))),
313
317
  }),
314
318
  },
315
319
  }
@@ -353,8 +357,8 @@ function zodDetails(error) {
353
357
  message: issue.message,
354
358
  }));
355
359
  }
356
- function toErrorResponse(error, requestId) {
357
- const observability = errorObservabilityDetails(error);
360
+ function toErrorResponse(error, requestId, declaredErrorCode) {
361
+ const observability = errorObservabilityDetails(error, declaredErrorCode);
358
362
  if (error instanceof StatefulRoutingDeadlineError) {
359
363
  return {
360
364
  error: {
@@ -414,7 +418,10 @@ function toErrorResponse(error, requestId) {
414
418
  // narrowing from a ProviderError-typed value would collapse the negative branch
415
419
  // to `never`. Narrowing from unknown avoids that while still recognizing errors
416
420
  // from a duplicate SDK module instance.
417
- function providerObservabilityDetails(error) {
421
+ function providerObservabilityDetails(error, declaredErrorCode) {
422
+ const declaredRetryable = sdkOwnsErrorResolution(error)
423
+ ? undefined
424
+ : declaredErrorCode?.retryable;
418
425
  // Session-expiry surfaces the credential_expired category + the opt-in
419
426
  // retryable signal so Gateway/Credential Service can refresh and re-drive the
420
427
  // operation (see design.md §4.3 D3). Without this branch the auth error would
@@ -424,7 +431,7 @@ function providerObservabilityDetails(error) {
424
431
  return {
425
432
  category: error.options?.category ?? "credential_expired",
426
433
  taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
427
- retryable: error.options?.retryable ?? false,
434
+ retryable: error.options?.retryable ?? declaredRetryable ?? false,
428
435
  };
429
436
  }
430
437
  // Missing-secret errors carry the canonical credential_unavailable category
@@ -436,7 +443,7 @@ function providerObservabilityDetails(error) {
436
443
  return {
437
444
  category: error.options?.category ?? "credential_unavailable",
438
445
  taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
439
- retryable: error.options?.retryable ?? false,
446
+ retryable: error.options?.retryable ?? declaredRetryable ?? false,
440
447
  };
441
448
  }
442
449
  if (!isTransportError(error)) {
@@ -467,17 +474,23 @@ function providerObservabilityDetails(error) {
467
474
  ...(error.upstreamStatus ? { upstreamStatus: error.upstreamStatus } : {}),
468
475
  };
469
476
  }
470
- function errorObservabilityDetails(error) {
471
- const providerDetails = providerObservabilityDetails(error);
477
+ function errorObservabilityDetails(error, declaredErrorCode) {
478
+ const effectiveDeclaration = sdkOwnsErrorResolution(error) ? undefined : declaredErrorCode;
479
+ const providerDetails = providerObservabilityDetails(error, effectiveDeclaration);
472
480
  if (providerDetails)
473
481
  return providerDetails;
474
482
  if (error instanceof z.ZodError || isValidationError(error)) {
483
+ const declaredStatus = effectiveDeclaration?.status;
475
484
  return {
476
485
  category: isProviderError(error) && error.options?.category
477
486
  ? error.options.category
478
- : "input_validation",
487
+ : isEmittableErrorStatus(declaredStatus) && declaredStatus >= 500
488
+ ? "provider_error"
489
+ : "input_validation",
479
490
  taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
480
- retryable: isProviderError(error) ? (error.options?.retryable ?? false) : false,
491
+ retryable: isProviderError(error)
492
+ ? (error.options?.retryable ?? effectiveDeclaration?.retryable ?? false)
493
+ : false,
481
494
  };
482
495
  }
483
496
  if (error instanceof StatefulRoutingDeadlineError) {
@@ -491,7 +504,7 @@ function errorObservabilityDetails(error) {
491
504
  return {
492
505
  category: error.options?.category ?? "provider_error",
493
506
  taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
494
- retryable: error.options?.retryable ?? false,
507
+ retryable: error.options?.retryable ?? effectiveDeclaration?.retryable ?? false,
495
508
  };
496
509
  }
497
510
  return {
@@ -500,9 +513,9 @@ function errorObservabilityDetails(error) {
500
513
  retryable: false,
501
514
  };
502
515
  }
503
- function responseWithErrorObservability(response, error) {
516
+ function responseWithErrorObservability(response, error, declaredErrorCode) {
504
517
  const headers = new Headers(response.headers);
505
- headers.set(ERROR_OBSERVABILITY_HEADER, JSON.stringify(errorObservabilityDetails(error)));
518
+ headers.set(ERROR_OBSERVABILITY_HEADER, JSON.stringify(errorObservabilityDetails(error, declaredErrorCode)));
506
519
  return new Response(response.body, {
507
520
  status: response.status,
508
521
  statusText: response.statusText,
@@ -534,7 +547,11 @@ function publicProviderErrorMessage(error) {
534
547
  }
535
548
  return error.message;
536
549
  }
537
- function toStatusCode(error) {
550
+ function isEmittableErrorStatus(value) {
551
+ return (typeof value === "number" &&
552
+ VALID_OPERATION_ERROR_STATUSES.some((status) => status === value));
553
+ }
554
+ function toStatusCode(error, declaredErrorCode) {
538
555
  if (error instanceof z.ZodError) {
539
556
  return 400;
540
557
  }
@@ -542,6 +559,10 @@ function toStatusCode(error) {
542
559
  return 504;
543
560
  }
544
561
  if (isProviderError(error)) {
562
+ if (!sdkOwnsErrorResolution(error) &&
563
+ isEmittableErrorStatus(declaredErrorCode?.status)) {
564
+ return declaredErrorCode.status;
565
+ }
545
566
  switch (error.code) {
546
567
  case "AUTH_REQUIRED":
547
568
  case "reauth_required":
@@ -576,82 +597,32 @@ function toStatusCode(error) {
576
597
  }
577
598
  return 500;
578
599
  }
579
- // Codes emitted by SDK-owned paths must never be attributed to provider
580
- // authors by the unregistered-code signal, even when their intentional status
581
- // is 500. Provider-authored codes not in this registry retain the signal.
582
- const SDK_OWNED_PROVIDER_ERROR_CODES = new Set([
583
- "AUTH_PROMPT_UNAVAILABLE",
584
- "BROWSER_CDP_POOL_REQUIRED",
585
- "BROWSER_RUNTIME_UNSUPPORTED",
586
- "STEALTH_RUNTIME_UNSUPPORTED",
587
- "SSE_EVENT_UNDECLARED",
588
- "STREAM_EVENT_TOO_LARGE",
589
- "STREAM_CHUNK_TOO_LARGE",
590
- "SSE_RESULT_UNSUPPORTED",
591
- "STREAM_RESULT_UNSUPPORTED",
592
- "AUTH_FLOW_NOT_CONFIGURED",
593
- "refresh_not_supported",
594
- "RUNTIME_UNSUPPORTED",
595
- "PROVIDER_STATE_UNSUPPORTED",
596
- "CHOICE_TOKEN_MASTER_SECRET_NOT_CONFIGURED",
597
- "CHOICE_STATE_PAYLOAD_TOO_LARGE",
598
- "CHOICE_STATE_UNAVAILABLE",
599
- "CHOICE_CONTEXT_REQUIRED",
600
- "unsupported_stealth_cookie_store_version",
601
- "provider_secret_error",
602
- "credential_key_error",
603
- "credential_mode_error",
604
- "flow_expired",
605
- "turn_validation_error",
606
- "context_access_error",
607
- "UNSUPPORTED_STT_OPTION",
608
- "INVALID_STT_AUDIO",
609
- "STT_AUDIO_TOO_LARGE",
610
- "STT_UPSTREAM_FAILED",
611
- "INVALID_STT_VERIFICATION_CODE_OPTIONS",
612
- "NO_CODE_FOUND",
613
- "AMBIGUOUS_CODE",
614
- "retry_invalid_policy",
615
- "retry_unsafe_method",
616
- "stealth_cookie_store_serialize_failed",
617
- "response_too_large",
618
- "transport_stream_unavailable",
619
- "transport_invalid_method",
620
- "http_transport_override_unsupported",
621
- "http_redirect_policy_invalid",
622
- "http_redirect_stopped",
623
- "http_redirect_max_hops",
624
- "http_redirect_missing_location",
625
- "http_redirect_loop",
626
- "transport_invalid_url",
627
- "retry_exhausted",
628
- "auth_abort_unsafe_data",
629
- "credentials_auth_missing_credential_keys",
630
- "credentials_auth_missing_credential",
631
- "credentials_auth_invalid_login_result",
632
- "credentials_auth_unknown_challenge",
633
- "credentials_auth_unknown_pending_challenge",
634
- "STATEFUL_FORWARDING_NOT_CONFIGURED",
635
- "STATEFUL_FORWARDING_SIGNATURE_MISSING",
636
- "STATEFUL_FORWARDING_NONCE_INVALID",
637
- "STATEFUL_FORWARDING_TIMESTAMP_INVALID",
638
- "STATEFUL_FORWARDING_SIGNATURE_INVALID",
639
- "STATEFUL_FORWARDING_REPLAY_DETECTED",
640
- "STATEFUL_FORWARDING_REPLAY_CACHE_FULL",
641
- "STATEFUL_FORWARDING_ENVELOPE_INVALID",
642
- "STATEFUL_FORWARDING_PROVIDER_MISMATCH",
643
- "STATEFUL_FORWARDING_SOURCE_POD_MISMATCH",
644
- "STATEFUL_FORWARDING_OWNER_FENCE_INVALID",
645
- "STATEFUL_FORWARDING_REQUEST_FAILED",
646
- "STATEFUL_FORWARDING_CONTEXT_MISSING",
647
- "STATEFUL_FORWARDING_BAD_RESPONSE",
648
- "STATEFUL_INTERNAL_EXECUTOR_NOT_CONFIGURED",
649
- "STATEFUL_FILE_FORWARDING_UNSUPPORTED",
650
- "STATEFUL_CONTROL_PLANE_OPERATION_AMBIGUOUS",
651
- "STATEFUL_CONTROL_PLANE_REQUEST_FAILED",
652
- "STATEFUL_CONTROL_PLANE_HTTP_ERROR",
653
- "STATEFUL_CONTROL_PLANE_INVALID_RESPONSE",
654
- ]);
600
+ function sdkOwnsErrorResolution(error) {
601
+ if (isSessionExpiredError(error))
602
+ return true;
603
+ if (isTransportError(error))
604
+ return true;
605
+ if (error instanceof z.ZodError)
606
+ return true;
607
+ if (error instanceof StatefulRoutingDeadlineError)
608
+ return true;
609
+ return (isProviderError(error) &&
610
+ typeof error.code === "string" &&
611
+ SDK_RUNTIME_OWNED_ERROR_CODES.has(error.code));
612
+ }
613
+ function buildOperationErrorCodeLookup(provider) {
614
+ return new Map(Object.entries(provider.operations).flatMap(([operationId, operation]) => {
615
+ const errorCodes = operation.docs?.errorCodes;
616
+ return errorCodes?.length
617
+ ? [[operationId, new Map(errorCodes.map((entry) => [entry.code, entry]))]]
618
+ : [];
619
+ }));
620
+ }
621
+ function declaredErrorCodeFor(error, operationId, lookup) {
622
+ if (!operationId || !isProviderError(error) || typeof error.code !== "string")
623
+ return undefined;
624
+ return lookup.get(operationId)?.get(error.code);
625
+ }
655
626
  function extractRequestId(raw) {
656
627
  if (!raw || typeof raw !== "object") {
657
628
  return undefined;
@@ -659,7 +630,7 @@ function extractRequestId(raw) {
659
630
  const value = Object.getOwnPropertyDescriptor(raw, "requestId")?.value;
660
631
  return typeof value === "string" ? value : undefined;
661
632
  }
662
- function logProviderError(logger, provider, kind, route, requestId, error, status, cost) {
633
+ function logProviderError(logger, provider, kind, route, requestId, error, status, cost, declaredErrorCode) {
663
634
  const code = isProviderError(error)
664
635
  ? (error.code ?? "provider_error")
665
636
  : error instanceof z.ZodError
@@ -669,12 +640,13 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
669
640
  : "internal_error";
670
641
  const errorClass = error instanceof Error ? error.name : typeof error;
671
642
  const message = error instanceof Error ? error.message : String(error);
672
- const details = errorObservabilityDetails(error);
643
+ const details = errorObservabilityDetails(error, declaredErrorCode);
673
644
  const isUnregisteredProviderErrorCode = status === 500 &&
674
645
  isProviderError(error) &&
675
646
  !isValidationError(error) &&
676
647
  typeof error.code === "string" &&
677
- !SDK_OWNED_PROVIDER_ERROR_CODES.has(error.code);
648
+ !SDK_OWNED_PROVIDER_ERROR_CODES.has(error.code) &&
649
+ declaredErrorCode === undefined;
678
650
  const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
679
651
  emit({
680
652
  level: status >= 500 ? "error" : "warn",
@@ -1270,6 +1242,7 @@ export function createServerApp(provider, options = {}) {
1270
1242
  validateStatefulServerConfig(options);
1271
1243
  const app = new Hono();
1272
1244
  const logger = options.logger ?? defaultProviderServerLogger;
1245
+ const operationErrorCodes = buildOperationErrorCodeLookup(provider);
1273
1246
  const statefulForwardingReplayCache = new StatefulForwardingReplayCache(options.statefulForwarding?.replayCacheMaxEntries ??
1274
1247
  DEFAULT_STATEFUL_FORWARDING_REPLAY_CACHE_MAX_ENTRIES);
1275
1248
  const state = options.state ??
@@ -1303,6 +1276,7 @@ export function createServerApp(provider, options = {}) {
1303
1276
  app.post(STATEFUL_INTERNAL_OPERATIONS_ROUTE, async (c) => {
1304
1277
  let rawBodyText = "";
1305
1278
  let rawBody;
1279
+ let operationId;
1306
1280
  const operation = "stateful-internal";
1307
1281
  const requestCost = startRequestCost();
1308
1282
  try {
@@ -1388,7 +1362,7 @@ export function createServerApp(provider, options = {}) {
1388
1362
  });
1389
1363
  }
1390
1364
  const request = operationRequestFromForwardingEnvelope(envelope);
1391
- const operationId = envelope.operationId;
1365
+ operationId = envelope.operationId;
1392
1366
  const ctx = createProviderContext(provider, request, operationId, options, state);
1393
1367
  if (deadlineAtMs !== undefined && deadlineAtMs <= Date.now()) {
1394
1368
  throw new StatefulRoutingDeadlineError(envelope.requestId, envelope.deadlineAt);
@@ -1405,13 +1379,14 @@ export function createServerApp(provider, options = {}) {
1405
1379
  return c.json({ data: output });
1406
1380
  }
1407
1381
  catch (error) {
1408
- const status = toStatusCode(error);
1382
+ const declaredErrorCode = declaredErrorCodeFor(error, operationId, operationErrorCodes);
1383
+ const status = toStatusCode(error, declaredErrorCode);
1409
1384
  if (isProviderError(error) && error.code === "STATEFUL_FORWARDING_REPLAY_CACHE_FULL") {
1410
1385
  c.header("Retry-After", String(STATEFUL_FORWARDING_REPLAY_RETRY_AFTER_SECONDS));
1411
1386
  }
1412
1387
  const requestId = extractRequestId(rawBody);
1413
- logProviderError(logger, provider, "operation", operation, requestId, error, status, finishRequestCost(requestCost));
1414
- return responseWithErrorObservability(c.json(toErrorResponse(error, requestId), status), error);
1388
+ logProviderError(logger, provider, "operation", operationId || operation, requestId, error, status, finishRequestCost(requestCost), declaredErrorCode);
1389
+ return responseWithErrorObservability(c.json(toErrorResponse(error, requestId, declaredErrorCode), status), error, declaredErrorCode);
1415
1390
  }
1416
1391
  });
1417
1392
  app.post("/v1/:operation", async (c) => {
@@ -1439,13 +1414,14 @@ export function createServerApp(provider, options = {}) {
1439
1414
  return c.json(response);
1440
1415
  }
1441
1416
  catch (error) {
1442
- const status = toStatusCode(error);
1417
+ const declaredErrorCode = declaredErrorCodeFor(error, operation, operationErrorCodes);
1418
+ const status = toStatusCode(error, declaredErrorCode);
1443
1419
  const requestId = extractRequestId(rawBody);
1444
- logProviderError(logger, provider, "operation", operation, requestId, error, status, finishRequestCost(requestCost));
1420
+ logProviderError(logger, provider, "operation", operation, requestId, error, status, finishRequestCost(requestCost), declaredErrorCode);
1445
1421
  const telemetryHeader = proxyTelemetry.toHeaderValue();
1446
1422
  if (telemetryHeader)
1447
1423
  c.header(PROVIDER_TELEMETRY_HEADER, telemetryHeader);
1448
- return responseWithErrorObservability(c.json(toErrorResponse(error, requestId), status), error);
1424
+ return responseWithErrorObservability(c.json(toErrorResponse(error, requestId, declaredErrorCode), status), error, declaredErrorCode);
1449
1425
  }
1450
1426
  });
1451
1427
  app.post("/auth/start", async (c) => {
package/dist/types.d.ts CHANGED
@@ -610,9 +610,11 @@ export interface HealthMonitorProbeOverride {
610
610
  /** Optional degraded threshold override for generated registry probes. */
611
611
  degradedThresholdMs?: number;
612
612
  }
613
+ export declare const VALID_OPERATION_ERROR_STATUSES: readonly [400, 401, 404, 429, 500, 502, 503, 504];
614
+ export type ProviderErrorStatus = (typeof VALID_OPERATION_ERROR_STATUSES)[number];
613
615
  export interface OperationErrorCode {
614
616
  code: string;
615
- status?: number;
617
+ status?: ProviderErrorStatus;
616
618
  description: string;
617
619
  retryable?: boolean;
618
620
  }
@@ -1087,6 +1089,10 @@ export interface NativeTcpEgressRule {
1087
1089
  /**
1088
1090
  * Bounded native TCP egress discovered through a declared bootstrap endpoint.
1089
1091
  * Host suffixes are exact DNS suffixes, not wildcard patterns.
1092
+ * Dynamic rules must declare at least one target host selector through
1093
+ * targetHostSuffixes and/or targetIpv4Cidrs. IPv4-literal grant targets match
1094
+ * only targetIpv4Cidrs, while DNS-name targets match only targetHostSuffixes.
1095
+ * CIDRs are exact IPv4 networks in a.b.c.d/nn form.
1090
1096
  *
1091
1097
  * Dynamic rules are ordered. The first rule whose source, target, port, and TLS
1092
1098
  * selectors match exclusively owns the grant; its ttlMs and maxGrants bounds
@@ -1099,7 +1105,8 @@ export interface NativeTcpDynamicEgressRule {
1099
1105
  readonly sourceHostSuffixes?: readonly string[];
1100
1106
  readonly sourcePorts?: readonly number[];
1101
1107
  readonly sourcePortRanges?: readonly NativeTcpPortRange[];
1102
- readonly targetHostSuffixes: readonly string[];
1108
+ readonly targetHostSuffixes?: readonly string[];
1109
+ readonly targetIpv4Cidrs?: readonly string[];
1103
1110
  readonly targetPorts?: readonly number[];
1104
1111
  readonly targetPortRanges?: readonly NativeTcpPortRange[];
1105
1112
  readonly tls: NativeTcpTlsMode;
package/dist/types.js CHANGED
@@ -32,6 +32,7 @@ export const HEALTH_CHECK_TIMEOUT_MS_MIN = 1;
32
32
  export const HEALTH_CHECK_TIMEOUT_MS_MAX = 60_000;
33
33
  export const HEALTH_CHECK_DEGRADED_THRESHOLD_MS_MIN = 1;
34
34
  export const HEALTH_CHECK_DEGRADED_THRESHOLD_MS_MAX = 60_000;
35
+ export const VALID_OPERATION_ERROR_STATUSES = [400, 401, 404, 429, 500, 502, 503, 504];
35
36
  export const HttpRetryPreset = {
36
37
  Off: "off",
37
38
  TransportTransient: "transport_transient",
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.2.0-beta.15",
2
+ "version": "2.2.0-beta.17",
3
3
  "name": "@apifuse/provider-sdk",
4
4
  "private": false,
5
5
  "type": "module",