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

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 (52) hide show
  1. package/AUTHORING.md +201 -0
  2. package/CHANGELOG.md +10 -0
  3. package/README.md +26 -2
  4. package/bin/apifuse-pack-types.ts +30 -1
  5. package/bin/apifuse-record.ts +622 -57
  6. package/bin/apifuse-submit-check.ts +43 -10
  7. package/dist/define.d.ts +2 -1
  8. package/dist/define.js +61 -3
  9. package/dist/fixture-sanitization.d.ts +26 -0
  10. package/dist/fixture-sanitization.js +216 -0
  11. package/dist/index.d.ts +2 -1
  12. package/dist/index.js +1 -0
  13. package/dist/provider.d.ts +2 -1
  14. package/dist/provider.js +1 -0
  15. package/dist/runtime/http.js +86 -32
  16. package/dist/runtime/instrumentation.js +295 -9
  17. package/dist/runtime/native-network.d.ts +53 -0
  18. package/dist/runtime/native-network.js +477 -0
  19. package/dist/runtime/proxy-nodemaven.d.ts +14 -0
  20. package/dist/runtime/proxy-nodemaven.js +20 -2
  21. package/dist/runtime/request-options.d.ts +68 -1
  22. package/dist/runtime/request-options.js +548 -0
  23. package/dist/runtime/stealth.d.ts +3 -1
  24. package/dist/runtime/stealth.js +239 -39
  25. package/dist/server/index.d.ts +1 -1
  26. package/dist/server/index.js +1 -1
  27. package/dist/server/self-test-input-tokens.d.ts +2 -1
  28. package/dist/server/self-test-input-tokens.js +18 -14
  29. package/dist/stream-evidence.d.ts +74 -0
  30. package/dist/stream-evidence.js +785 -0
  31. package/dist/testing/index.d.ts +1 -1
  32. package/dist/testing/index.js +1 -1
  33. package/dist/testing/run.d.ts +32 -2
  34. package/dist/testing/run.js +451 -19
  35. package/dist/types.d.ts +162 -0
  36. package/package.json +2 -1
  37. package/src/define.ts +81 -3
  38. package/src/fixture-sanitization.ts +247 -0
  39. package/src/index.ts +37 -0
  40. package/src/provider.ts +37 -0
  41. package/src/runtime/http.ts +144 -38
  42. package/src/runtime/instrumentation.ts +424 -8
  43. package/src/runtime/native-network.ts +600 -0
  44. package/src/runtime/proxy-nodemaven.ts +37 -2
  45. package/src/runtime/request-options.ts +680 -1
  46. package/src/runtime/stealth.ts +293 -40
  47. package/src/server/index.ts +4 -1
  48. package/src/server/self-test-input-tokens.ts +29 -14
  49. package/src/stream-evidence.ts +988 -0
  50. package/src/testing/index.ts +9 -1
  51. package/src/testing/run.ts +608 -12
  52. package/src/types.ts +194 -0
package/src/provider.ts CHANGED
@@ -94,6 +94,26 @@ export type {
94
94
  HttpRetryOptions,
95
95
  HttpRetrySummary,
96
96
  InferSchemaOutput,
97
+ NativeContext,
98
+ NativeNetworkClient,
99
+ NativeNetworkCloseReason,
100
+ NativeNetworkConnection,
101
+ NativeNetworkConnectInput,
102
+ NativeNetworkConnectOptions,
103
+ NativeNetworkDynamicGrantOptions,
104
+ NativeNetworkEgressGrant,
105
+ NativeProviderConfig,
106
+ NativeProviderContext,
107
+ NativeProxyDrainHandler,
108
+ NativeProxyEgressInfo,
109
+ NativeProxyExpiringEvent,
110
+ NativeProxyExpiringReason,
111
+ NativeTcpDynamicEgressRule,
112
+ NativeTcpEgressGrant,
113
+ NativeTcpEgressRule,
114
+ NativeTcpPortRange,
115
+ NativeTcpTlsMode,
116
+ NativeTlsConnectOptions,
97
117
  OperationApprovalPolicy,
98
118
  OperationContractMetadata,
99
119
  OperationDefinition,
@@ -116,6 +136,8 @@ export type {
116
136
  ProviderContext,
117
137
  ProviderDefinition,
118
138
  ProviderDeploymentOverrides,
139
+ ProviderFileRef,
140
+ ProviderFilesContext,
119
141
  ProviderLocale,
120
142
  ProviderLocaleKey,
121
143
  ProviderLocaleKeyInput,
@@ -123,6 +145,7 @@ export type {
123
145
  ProviderProxyPolicy,
124
146
  ProviderPublicConnectionMode,
125
147
  ProviderPublicProfile,
148
+ ProviderResolvedFile,
126
149
  ProviderRuntimeState,
127
150
  ProviderStateDurationString,
128
151
  ProviderStateNamespace,
@@ -135,6 +158,20 @@ export type {
135
158
  StateValue,
136
159
  StateWriteOptions,
137
160
  } from "./types.js";
161
+ export {
162
+ createNativeNetworkClient,
163
+ deriveNativeCredentialAffinityKey,
164
+ NativeIdleTimeoutError,
165
+ NativeNetworkError,
166
+ NativeProxyExpiredError,
167
+ resolveNativeGatewayProxy,
168
+ type NativeGatewayProxy,
169
+ type NativeGatewayProxyResolutionInput,
170
+ type NativeGatewayProxySynthesizer,
171
+ type NativeGatewayProxySynthesisInput,
172
+ type NativeNetworkClientOptions,
173
+ type NativeNetworkErrorCode,
174
+ } from "./runtime/native-network.js";
138
175
  export {
139
176
  HttpRetryAfterPolicy,
140
177
  HttpRetryDelayStrategy,
@@ -28,7 +28,13 @@ import {
28
28
  shouldRetryProxyTransportAttempt,
29
29
  validateUnsafeProxyTransportRetryMethods,
30
30
  } from "./proxy-retry-policy.js";
31
- import { appendQueryParams, normalizeHttpRequestBody } from "./request-options.js";
31
+ import {
32
+ normalizeHttpRequestBody,
33
+ redactSensitiveError,
34
+ redactSensitiveRequestError,
35
+ type SerializedRequestUrl,
36
+ serializeRequestUrl,
37
+ } from "./request-options.js";
32
38
 
33
39
  const DEFAULT_HTTP_BASE_URL = "http://localhost";
34
40
 
@@ -208,9 +214,58 @@ function requireNativeResponseBody(response: Response): ReadableStream<Uint8Arra
208
214
  return response.body;
209
215
  }
210
216
 
211
- function toNativeHttpStreamResponse(response: Response): HttpStreamResponse {
217
+ function sanitizeStreamErrors(
218
+ body: ReadableStream<Uint8Array>,
219
+ serializedUrl: SerializedRequestUrl,
220
+ ): ReadableStream<Uint8Array> {
221
+ if (serializedUrl.sensitiveValues.length === 0) return body;
222
+
223
+ let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;
224
+ return new ReadableStream<Uint8Array>(
225
+ {
226
+ async pull(controller) {
227
+ try {
228
+ reader ??= body.getReader();
229
+ const chunk = await reader.read();
230
+ if (chunk.done) {
231
+ controller.close();
232
+ return;
233
+ }
234
+ controller.enqueue(chunk.value);
235
+ } catch (error) {
236
+ controller.error(
237
+ redactSensitiveError(
238
+ error,
239
+ serializedUrl.sensitiveValues,
240
+ serializedUrl.requestUrl,
241
+ serializedUrl.redactedUrl,
242
+ ),
243
+ );
244
+ }
245
+ },
246
+ async cancel(reason) {
247
+ try {
248
+ await (reader ? reader.cancel(reason) : body.cancel(reason));
249
+ } catch (error) {
250
+ throw redactSensitiveError(
251
+ error,
252
+ serializedUrl.sensitiveValues,
253
+ serializedUrl.requestUrl,
254
+ serializedUrl.redactedUrl,
255
+ );
256
+ }
257
+ },
258
+ },
259
+ { highWaterMark: 0 },
260
+ );
261
+ }
262
+
263
+ function toNativeHttpStreamResponse(
264
+ response: Response,
265
+ serializedUrl: SerializedRequestUrl,
266
+ ): HttpStreamResponse {
212
267
  const headers = Object.fromEntries(response.headers.entries());
213
- const body = requireNativeResponseBody(response);
268
+ const body = sanitizeStreamErrors(requireNativeResponseBody(response), serializedUrl);
214
269
  return {
215
270
  body,
216
271
  headers,
@@ -305,6 +360,22 @@ function normalizeNativeFetchBody(body: unknown): string | ArrayBuffer | undefin
305
360
  return copied.buffer;
306
361
  }
307
362
 
363
+ function serializeHttpRequestUrl(
364
+ baseUrl: string | undefined,
365
+ url: string,
366
+ options: RequestOptions,
367
+ ): SerializedRequestUrl {
368
+ try {
369
+ return serializeRequestUrl(
370
+ resolveHttpUrl(baseUrl, url),
371
+ options.params,
372
+ options.sensitiveParams,
373
+ );
374
+ } catch (error) {
375
+ throw redactSensitiveRequestError(error, url, options.sensitiveParams);
376
+ }
377
+ }
378
+
308
379
  async function fetchNativeHttp(
309
380
  baseUrl: string | undefined,
310
381
  url: string,
@@ -316,7 +387,8 @@ async function fetchNativeHttp(
316
387
  proxyAttemptOffset = 0,
317
388
  dedupe?: { attempted: Set<string> },
318
389
  ): Promise<NativeHttpAttemptOutcome> {
319
- const requestUrl = appendQueryParams(resolveHttpUrl(baseUrl, url), options.params);
390
+ const serializedUrl = serializeHttpRequestUrl(baseUrl, url, options);
391
+ const { requestUrl } = serializedUrl;
320
392
  const controller = options.timeout ? new AbortController() : undefined;
321
393
  const timeoutHandle = options.timeout
322
394
  ? setTimeout(() => controller?.abort(), options.timeout)
@@ -374,9 +446,19 @@ async function fetchNativeHttp(
374
446
  return toNativeHttpResponse(response);
375
447
  } catch (error) {
376
448
  if (error instanceof SyntaxError) {
377
- throw error;
449
+ throw redactSensitiveError(
450
+ error,
451
+ serializedUrl.sensitiveValues,
452
+ serializedUrl.requestUrl,
453
+ serializedUrl.redactedUrl,
454
+ );
378
455
  }
379
- const transportError = toHttpTransportError(error) as NativeHttpAttemptError;
456
+ const transportError: NativeHttpAttemptError = redactSensitiveError(
457
+ toHttpTransportError(error),
458
+ serializedUrl.sensitiveValues,
459
+ serializedUrl.requestUrl,
460
+ serializedUrl.redactedUrl,
461
+ );
380
462
  transportError.proxyUsed = Boolean(proxy);
381
463
  throw transportError;
382
464
  } finally {
@@ -392,7 +474,8 @@ async function fetchNativeHttpStream(
392
474
  clientOptions: HttpClientOptions,
393
475
  warn: (message: string) => void,
394
476
  ): Promise<HttpStreamResponse> {
395
- const requestUrl = appendQueryParams(resolveHttpUrl(baseUrl, url), options.params);
477
+ const serializedUrl = serializeHttpRequestUrl(baseUrl, url, options);
478
+ const { requestUrl } = serializedUrl;
396
479
  const controller = options.timeout ? new AbortController() : undefined;
397
480
  const timeoutHandle = options.timeout
398
481
  ? setTimeout(() => controller?.abort(), options.timeout)
@@ -421,12 +504,22 @@ async function fetchNativeHttpStream(
421
504
  });
422
505
  }
423
506
 
424
- return toNativeHttpStreamResponse(response);
507
+ return toNativeHttpStreamResponse(response, serializedUrl);
425
508
  } catch (error) {
426
509
  if (error instanceof SyntaxError) {
427
- throw error;
510
+ throw redactSensitiveError(
511
+ error,
512
+ serializedUrl.sensitiveValues,
513
+ serializedUrl.requestUrl,
514
+ serializedUrl.redactedUrl,
515
+ );
428
516
  }
429
- throw toHttpTransportError(error);
517
+ throw redactSensitiveError(
518
+ toHttpTransportError(error),
519
+ serializedUrl.sensitiveValues,
520
+ serializedUrl.requestUrl,
521
+ serializedUrl.redactedUrl,
522
+ );
430
523
  } finally {
431
524
  if (timeoutHandle) clearTimeout(timeoutHandle);
432
525
  }
@@ -451,22 +544,29 @@ export function createHttpClient(
451
544
  method: string,
452
545
  options: RequestOptions & { body?: unknown } = {},
453
546
  ): Promise<HttpResponse> {
454
- if (!baseUrl && !isAbsoluteUrl(url)) {
455
- throw new TransportError(
456
- "ctx.http requires an absolute URL when provider.upstream.baseUrl is not declared",
457
- { code: "transport_invalid_url" },
458
- );
459
- }
460
- assertNoHttpTransportOverrides(options);
461
- const headersOptions = withClientHeaders(options, clientOptions, options.body);
462
- const methodName = normalizeHttpMethod(method);
463
- const explicitRetry = headersOptions.retry !== undefined;
464
- const retryOptions =
465
- normalizeProxyTransportRetryOptions(headersOptions.retry, {
466
- label: "HTTP",
467
- }) ??
468
- (explicitRetry ? undefined : createDefaultProxyTransportRetryOptions({ label: "HTTP" }));
469
- if (retryOptions) validateUnsafeProxyTransportRetryMethods(retryOptions, "HTTP");
547
+ const { explicitRetry, headersOptions, methodName, retryOptions } = (() => {
548
+ try {
549
+ if (!baseUrl && !isAbsoluteUrl(url)) {
550
+ throw new TransportError(
551
+ "ctx.http requires an absolute URL when provider.upstream.baseUrl is not declared",
552
+ { code: "transport_invalid_url" },
553
+ );
554
+ }
555
+ assertNoHttpTransportOverrides(options);
556
+ const headersOptions = withClientHeaders(options, clientOptions, options.body);
557
+ const methodName = normalizeHttpMethod(method);
558
+ const explicitRetry = headersOptions.retry !== undefined;
559
+ const retryOptions =
560
+ normalizeProxyTransportRetryOptions(headersOptions.retry, {
561
+ label: "HTTP",
562
+ }) ??
563
+ (explicitRetry ? undefined : createDefaultProxyTransportRetryOptions({ label: "HTTP" }));
564
+ if (retryOptions) validateUnsafeProxyTransportRetryMethods(retryOptions, "HTTP");
565
+ return { explicitRetry, headersOptions, methodName, retryOptions };
566
+ } catch (error) {
567
+ throw redactSensitiveRequestError(error, url, options.sensitiveParams);
568
+ }
569
+ })();
470
570
  const retryEnabled = Boolean(
471
571
  retryOptions &&
472
572
  retryOptions.attempts > 1 &&
@@ -531,9 +631,7 @@ export function createHttpClient(
531
631
  explicitRetry,
532
632
  method: methodName,
533
633
  });
534
- const dedupeContext = dedupeAllocatorEndpoints
535
- ? { attempted: new Set<string>() }
536
- : undefined;
634
+ const dedupeContext = dedupeAllocatorEndpoints ? { attempted: new Set<string>() } : undefined;
537
635
 
538
636
  const executeOnce = (proxyAttemptOffset = 0): Promise<NativeHttpAttemptOutcome> =>
539
637
  fetchNativeHttp(
@@ -654,15 +752,23 @@ export function createHttpClient(
654
752
  method: string,
655
753
  options: RequestOptions & { body?: unknown } = {},
656
754
  ): Promise<HttpStreamResponse> {
657
- if (!baseUrl && !isAbsoluteUrl(url)) {
658
- throw new TransportError(
659
- "ctx.http requires an absolute URL when provider.upstream.baseUrl is not declared",
660
- { code: "transport_invalid_url" },
661
- );
662
- }
663
- assertNoHttpTransportOverrides(options);
664
- const headersOptions = withClientHeaders(options, clientOptions, options.body);
665
- const methodName = normalizeHttpMethod(method);
755
+ const { headersOptions, methodName } = (() => {
756
+ try {
757
+ if (!baseUrl && !isAbsoluteUrl(url)) {
758
+ throw new TransportError(
759
+ "ctx.http requires an absolute URL when provider.upstream.baseUrl is not declared",
760
+ { code: "transport_invalid_url" },
761
+ );
762
+ }
763
+ assertNoHttpTransportOverrides(options);
764
+ return {
765
+ headersOptions: withClientHeaders(options, clientOptions, options.body),
766
+ methodName: normalizeHttpMethod(method),
767
+ };
768
+ } catch (error) {
769
+ throw redactSensitiveRequestError(error, url, options.sensitiveParams);
770
+ }
771
+ })();
666
772
  return fetchNativeHttpStream(baseUrl, url, methodName, headersOptions, clientOptions, warnOnce);
667
773
  }
668
774