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

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 (44) hide show
  1. package/AUTHORING.md +70 -6
  2. package/CHANGELOG.md +12 -0
  3. package/dist/define.js +9 -0
  4. package/dist/errors.d.ts +13 -0
  5. package/dist/errors.js +25 -0
  6. package/dist/index.d.ts +3 -3
  7. package/dist/index.js +2 -2
  8. package/dist/native-egress-policy.d.ts +27 -0
  9. package/dist/native-egress-policy.js +225 -0
  10. package/dist/provider.d.ts +3 -3
  11. package/dist/provider.js +2 -2
  12. package/dist/runtime/executor.js +17 -2
  13. package/dist/runtime/http.js +189 -9
  14. package/dist/runtime/native-network.d.ts +39 -4
  15. package/dist/runtime/native-network.js +365 -20
  16. package/dist/runtime/redirects.d.ts +29 -0
  17. package/dist/runtime/redirects.js +36 -0
  18. package/dist/runtime/stealth.js +16 -44
  19. package/dist/server/index.d.ts +1 -1
  20. package/dist/server/index.js +1 -1
  21. package/dist/server/serve.d.ts +9 -0
  22. package/dist/server/serve.js +190 -51
  23. package/dist/server/types.d.ts +3 -0
  24. package/dist/server/types.js +1 -0
  25. package/dist/stateful/stateful-provider-owner-forwarder.js +9 -1
  26. package/dist/testing/run.js +32 -13
  27. package/dist/types.d.ts +23 -2
  28. package/package.json +1 -1
  29. package/src/define.ts +11 -0
  30. package/src/errors.ts +37 -0
  31. package/src/index.ts +12 -1
  32. package/src/native-egress-policy.ts +285 -0
  33. package/src/provider.ts +7 -0
  34. package/src/runtime/executor.ts +22 -2
  35. package/src/runtime/http.ts +217 -9
  36. package/src/runtime/native-network.ts +474 -22
  37. package/src/runtime/redirects.ts +66 -0
  38. package/src/runtime/stealth.ts +20 -47
  39. package/src/server/index.ts +2 -0
  40. package/src/server/serve.ts +226 -68
  41. package/src/server/types.ts +1 -0
  42. package/src/stateful/stateful-provider-owner-forwarder.ts +9 -1
  43. package/src/testing/run.ts +39 -14
  44. package/src/types.ts +32 -2
@@ -4,6 +4,12 @@ import { createProviderCache } from "../runtime/cache.js";
4
4
  import { createTestProviderChoiceContext } from "../runtime/choice.js";
5
5
  import { createMemoryProviderRuntimeState } from "../runtime/state.js";
6
6
  import { createUnsupportedSttClient } from "../runtime/stt.js";
7
+ import {
8
+ createNativeEgressAuthorization,
9
+ NativeNetworkError,
10
+ snapshotNativeConnectInput,
11
+ snapshotNativeGrantInput,
12
+ } from "../runtime/native-network.js";
7
13
  import { safeParseSchemaSync } from "../schema.js";
8
14
  import { requestPathForFixture } from "../fixture-sanitization.js";
9
15
  import { findStreamCaptureGroup, replayStreamEvidence } from "../stream-evidence.js";
@@ -307,6 +313,17 @@ function createUpstreamContext(
307
313
  };
308
314
  const request = { headers: {} };
309
315
  const state = createMemoryProviderRuntimeState();
316
+ const nativeEgress = provider.native
317
+ ? createNativeEgressAuthorization({ egress: provider.native.network })
318
+ : undefined;
319
+ const requireNativeEgress = () => {
320
+ if (!nativeEgress)
321
+ throw new NativeNetworkError(
322
+ "Native egress authorization is unavailable",
323
+ "native_egress_policy_invalid",
324
+ );
325
+ return nativeEgress;
326
+ };
310
327
  const dispatch = async (
311
328
  call: Omit<StandardTestsUpstreamCall, "operationName">,
312
329
  ): Promise<NormalizedUpstreamResponse> => {
@@ -498,30 +515,38 @@ function createUpstreamContext(
498
515
  ...(provider.native
499
516
  ? {
500
517
  native: {
501
- network: {
502
- connectTcp: async (options) =>
503
- createNativeConnection(
518
+ network: {
519
+ connectTcp: async (options) => {
520
+ const request = snapshotNativeConnectInput(options);
521
+ requireNativeEgress().assertConnect(request, "disabled");
522
+ return createNativeConnection(
504
523
  await dispatch({
505
524
  transport: "native",
506
525
  method: "connectTcp",
507
- url: `tcp://${options.host}:${options.port}`,
508
- options,
526
+ url: `tcp://${request.host}:${request.port}`,
527
+ options: request,
509
528
  }),
510
529
  dispatch,
511
- `tcp://${options.host}:${options.port}`,
512
- ),
513
- connectTls: async (options) =>
514
- createNativeConnection(
530
+ `tcp://${request.host}:${request.port}`,
531
+ );
532
+ },
533
+ connectTls: async (options) => {
534
+ const request = snapshotNativeConnectInput(options);
535
+ requireNativeEgress().assertConnect(request, "required");
536
+ return createNativeConnection(
515
537
  await dispatch({
516
538
  transport: "native",
517
539
  method: "connectTls",
518
- url: `tls://${options.host}:${options.port}`,
519
- options,
540
+ url: `tls://${request.host}:${request.port}`,
541
+ options: request,
520
542
  }),
521
543
  dispatch,
522
- `tls://${options.host}:${options.port}`,
523
- ),
524
- grantTcpEgress: () => ({ revoke: () => {} }),
544
+ `tls://${request.host}:${request.port}`,
545
+ );
546
+ },
547
+ grantTcpEgress: (input) => {
548
+ return requireNativeEgress().grant(snapshotNativeGrantInput(input));
549
+ },
525
550
  },
526
551
  },
527
552
  }
package/src/types.ts CHANGED
@@ -1019,8 +1019,32 @@ export interface RequestOptions {
1019
1019
  */
1020
1020
  throwOnHttpError?: boolean;
1021
1021
  retry?: boolean | HttpRetryPreset | HttpRetryOptions;
1022
+ /**
1023
+ * Opt-in redirect-hop enforcement for ctx.http. When present, redirects are
1024
+ * evaluated before the next request is issued. Existing callers that omit
1025
+ * this policy retain the native fetch redirect behavior.
1026
+ */
1027
+ redirectPolicy?: HttpRedirectPolicy;
1028
+ }
1029
+
1030
+ export type RedirectRunReason =
1031
+ | "completed"
1032
+ | "stopped"
1033
+ | "max_hops"
1034
+ | "missing_location"
1035
+ | "loop";
1036
+
1037
+ export type HttpRedirectPolicyMode = "same-origin";
1038
+
1039
+ export interface HttpRedirectPolicy {
1040
+ /** Only follow redirects whose canonical scheme, host, and port match the initial URL. */
1041
+ mode: HttpRedirectPolicyMode;
1042
+ /** Maximum number of redirect hops that may be followed. Must be an integer from 0 to 20. */
1043
+ maxHops: number;
1022
1044
  }
1023
1045
 
1046
+ export type HttpRedirectFailureReason = Exclude<RedirectRunReason, "completed">;
1047
+
1024
1048
  export type HttpMethod =
1025
1049
  | "HEAD"
1026
1050
  | "head"
@@ -1039,7 +1063,7 @@ export type HttpMethod =
1039
1063
  | "PATCH"
1040
1064
  | "patch";
1041
1065
 
1042
- export interface StealthFetchOptions extends RequestOptions {
1066
+ export interface StealthFetchOptions extends Omit<RequestOptions, "redirectPolicy"> {
1043
1067
  method?: HttpMethod;
1044
1068
  body?: string | Buffer;
1045
1069
  redirect?: "follow" | "manual" | "error";
@@ -1152,7 +1176,7 @@ export interface StealthRedirectRunOptions
1152
1176
  export interface StealthRedirectRunResult {
1153
1177
  final: StealthResponse;
1154
1178
  hops: StealthRedirectHop[];
1155
- reason: "completed" | "stopped" | "max_hops" | "missing_location" | "loop";
1179
+ reason: RedirectRunReason;
1156
1180
  /**
1157
1181
  * Complete flat view across all redirect hosts. Attributes and duplicate names are lost.
1158
1182
  * @deprecated Use cookieStore for lossless persistence.
@@ -1290,6 +1314,12 @@ export interface NativeTcpEgressRule {
1290
1314
  /**
1291
1315
  * Bounded native TCP egress discovered through a declared bootstrap endpoint.
1292
1316
  * Host suffixes are exact DNS suffixes, not wildcard patterns.
1317
+ *
1318
+ * Dynamic rules are ordered. The first rule whose source, target, port, and TLS
1319
+ * selectors match exclusively owns the grant; its ttlMs and maxGrants bounds
1320
+ * apply, and an exhausted/shorter rule never falls through to a later overlap.
1321
+ * Every rule must declare a source host selector, source port list/range, and
1322
+ * target port list/range; omitted ttlMs/maxGrants remain unbounded.
1293
1323
  */
1294
1324
  export interface NativeTcpDynamicEgressRule {
1295
1325
  readonly sourceHost?: string;