@apifuse/provider-sdk 2.2.0-beta.3 → 2.2.0-beta.5

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 (165) hide show
  1. package/AUTHORING.md +53 -0
  2. package/CHANGELOG.md +8 -0
  3. package/bin/apifuse-check.ts +18 -58
  4. package/bin/apifuse-create.ts +1 -1
  5. package/bin/apifuse-dev.ts +27 -52
  6. package/bin/apifuse-pack-smoke.ts +22 -81
  7. package/bin/apifuse-pack-types.ts +266 -0
  8. package/bin/apifuse-perf.ts +45 -127
  9. package/bin/apifuse-record.ts +23 -61
  10. package/bin/apifuse-submit-check.ts +266 -34
  11. package/bin/apifuse.ts +1 -1
  12. package/bin/submit-check-xml.ts +1 -1
  13. package/dist/auth-turn/index.d.ts +1 -1
  14. package/dist/auth.d.ts +2 -2
  15. package/dist/auth.js +9 -18
  16. package/dist/ceremonies/index.d.ts +1 -1
  17. package/dist/ceremonies/index.js +8 -18
  18. package/dist/config/loader.d.ts +2 -2
  19. package/dist/config/loader.js +19 -42
  20. package/dist/contract-serialization.d.ts +2 -2
  21. package/dist/contract-serialization.js +3 -6
  22. package/dist/contract-types.d.ts +2 -2
  23. package/dist/contract.d.ts +3 -3
  24. package/dist/contract.js +4 -6
  25. package/dist/define.d.ts +8 -1
  26. package/dist/define.js +57 -115
  27. package/dist/dev.d.ts +1 -1
  28. package/dist/dev.js +1 -1
  29. package/dist/errors.d.ts +1 -1
  30. package/dist/errors.js +1 -3
  31. package/dist/i18n/catalog.d.ts +2 -2
  32. package/dist/i18n/catalog.js +4 -10
  33. package/dist/i18n/index.d.ts +2 -2
  34. package/dist/i18n/index.js +2 -2
  35. package/dist/i18n/keys.d.ts +2 -2
  36. package/dist/index.d.ts +43 -42
  37. package/dist/index.js +38 -37
  38. package/dist/lint.js +8 -15
  39. package/dist/provider.d.ts +11 -11
  40. package/dist/provider.js +9 -9
  41. package/dist/public-schema-field-lint.d.ts +1 -1
  42. package/dist/recipes/gov-api.js +1 -1
  43. package/dist/runtime/auth-flow.d.ts +1 -1
  44. package/dist/runtime/auth-flow.js +3 -3
  45. package/dist/runtime/browser.d.ts +1 -1
  46. package/dist/runtime/browser.js +15 -29
  47. package/dist/runtime/cache.d.ts +1 -1
  48. package/dist/runtime/cache.js +4 -8
  49. package/dist/runtime/choice.d.ts +1 -1
  50. package/dist/runtime/choice.js +12 -34
  51. package/dist/runtime/credential.d.ts +1 -1
  52. package/dist/runtime/credential.js +1 -1
  53. package/dist/runtime/env.d.ts +1 -1
  54. package/dist/runtime/executor.d.ts +1 -1
  55. package/dist/runtime/executor.js +2 -2
  56. package/dist/runtime/http.d.ts +2 -2
  57. package/dist/runtime/http.js +5 -5
  58. package/dist/runtime/insights.d.ts +1 -1
  59. package/dist/runtime/insights.js +6 -13
  60. package/dist/runtime/instrumentation.d.ts +2 -2
  61. package/dist/runtime/instrumentation.js +5 -15
  62. package/dist/runtime/keyring.js +1 -1
  63. package/dist/runtime/namespace.js +1 -1
  64. package/dist/runtime/otlp.d.ts +1 -1
  65. package/dist/runtime/perf.d.ts +1 -1
  66. package/dist/runtime/provider.d.ts +1 -1
  67. package/dist/runtime/provider.js +1 -2
  68. package/dist/runtime/proxy-errors.d.ts +1 -1
  69. package/dist/runtime/proxy-errors.js +3 -5
  70. package/dist/runtime/proxy-retry-policy.d.ts +2 -2
  71. package/dist/runtime/proxy-retry-policy.js +2 -2
  72. package/dist/runtime/proxy-telemetry.d.ts +1 -1
  73. package/dist/runtime/proxy-telemetry.js +16 -48
  74. package/dist/runtime/redis.d.ts +1 -1
  75. package/dist/runtime/redis.js +2 -4
  76. package/dist/runtime/request-options.d.ts +1 -1
  77. package/dist/runtime/state.d.ts +2 -2
  78. package/dist/runtime/state.js +3 -3
  79. package/dist/runtime/stealth.d.ts +2 -2
  80. package/dist/runtime/stealth.js +6 -6
  81. package/dist/runtime/stt.d.ts +1 -1
  82. package/dist/runtime/stt.js +11 -15
  83. package/dist/runtime/trace.d.ts +2 -2
  84. package/dist/runtime/trace.js +2 -4
  85. package/dist/runtime/waterfall.d.ts +1 -1
  86. package/dist/schema.d.ts +1 -1
  87. package/dist/schema.js +7 -15
  88. package/dist/serve.d.ts +1 -1
  89. package/dist/serve.js +1 -1
  90. package/dist/server/index.d.ts +7 -7
  91. package/dist/server/index.js +6 -6
  92. package/dist/server/self-test-redaction.d.ts +1 -1
  93. package/dist/server/self-test-redaction.js +1 -1
  94. package/dist/server/self-test.d.ts +104 -3
  95. package/dist/server/self-test.js +673 -115
  96. package/dist/server/serve.d.ts +3 -3
  97. package/dist/server/serve.js +37 -49
  98. package/dist/server/types.js +2 -7
  99. package/dist/stealth/profiles.d.ts +1 -1
  100. package/dist/stealth/profiles.js +5 -14
  101. package/dist/stream.d.ts +1 -1
  102. package/dist/testing/index.d.ts +2 -2
  103. package/dist/testing/index.js +2 -2
  104. package/dist/testing/run.d.ts +1 -1
  105. package/dist/testing/run.js +7 -14
  106. package/dist/types.d.ts +57 -0
  107. package/dist/user-input.d.ts +30 -0
  108. package/dist/user-input.js +66 -0
  109. package/package.json +6 -4
  110. package/src/auth-turn/index.ts +1 -1
  111. package/src/auth.ts +28 -86
  112. package/src/ceremonies/index.ts +24 -78
  113. package/src/config/loader.ts +58 -176
  114. package/src/contract-serialization.ts +5 -11
  115. package/src/contract-types.ts +2 -2
  116. package/src/contract.ts +12 -28
  117. package/src/define.ts +174 -495
  118. package/src/dev.ts +4 -9
  119. package/src/errors.ts +4 -11
  120. package/src/i18n/catalog.ts +10 -32
  121. package/src/i18n/index.ts +2 -2
  122. package/src/i18n/keys.ts +5 -11
  123. package/src/index.ts +44 -42
  124. package/src/lint.ts +89 -159
  125. package/src/provider.ts +12 -11
  126. package/src/public-schema-field-lint.ts +7 -33
  127. package/src/recipes/gov-api.ts +2 -5
  128. package/src/runtime/auth-flow.ts +5 -7
  129. package/src/runtime/browser.ts +64 -187
  130. package/src/runtime/cache.ts +20 -67
  131. package/src/runtime/choice.ts +55 -129
  132. package/src/runtime/credential.ts +2 -2
  133. package/src/runtime/env.ts +1 -1
  134. package/src/runtime/executor.ts +9 -19
  135. package/src/runtime/http.ts +7 -7
  136. package/src/runtime/insights.ts +15 -53
  137. package/src/runtime/instrumentation.ts +19 -58
  138. package/src/runtime/keyring.ts +7 -19
  139. package/src/runtime/namespace.ts +2 -7
  140. package/src/runtime/otlp.ts +12 -23
  141. package/src/runtime/perf.ts +1 -1
  142. package/src/runtime/provider.ts +4 -9
  143. package/src/runtime/proxy-errors.ts +17 -38
  144. package/src/runtime/proxy-retry-policy.ts +3 -3
  145. package/src/runtime/proxy-telemetry.ts +23 -72
  146. package/src/runtime/redis.ts +4 -12
  147. package/src/runtime/request-options.ts +4 -13
  148. package/src/runtime/state.ts +30 -109
  149. package/src/runtime/stealth.ts +8 -8
  150. package/src/runtime/stt.ts +38 -94
  151. package/src/runtime/trace.ts +14 -44
  152. package/src/runtime/waterfall.ts +5 -18
  153. package/src/schema.ts +23 -84
  154. package/src/serve.ts +1 -1
  155. package/src/server/index.ts +12 -7
  156. package/src/server/self-test-redaction.ts +2 -2
  157. package/src/server/self-test.ts +857 -132
  158. package/src/server/serve.ts +89 -238
  159. package/src/server/types.ts +6 -19
  160. package/src/stealth/profiles.ts +10 -26
  161. package/src/stream.ts +8 -19
  162. package/src/testing/index.ts +2 -2
  163. package/src/testing/run.ts +19 -67
  164. package/src/types.ts +58 -0
  165. package/src/user-input.ts +118 -0
@@ -1,7 +1,7 @@
1
1
  import { Hono } from "hono";
2
- import { type ProviderErrorCategory } from "../observability";
3
- import type { ProviderDefinition, ProviderRuntimeState, SttContext } from "../types";
4
- import { type OperationRequest } from "./types";
2
+ import { type ProviderErrorCategory } from "../observability.js";
3
+ import type { ProviderDefinition, ProviderRuntimeState, SttContext } from "../types.js";
4
+ import { type OperationRequest } from "./types.js";
5
5
  export declare function resolveProviderProxyAffinityKey(provider: ProviderDefinition, request: OperationRequest, operationId: string): string;
6
6
  type ProviderRequestCost = {
7
7
  durationMs: number;
@@ -2,32 +2,32 @@ import { existsSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { Hono } from "hono";
4
4
  import { z } from "zod";
5
- import { AuthAbortError, createAuthFlowHelpers } from "../auth";
6
- import { AuthError, isProviderError, isSessionExpiredError, isTransportError, ProviderError, } from "../errors";
7
- import { loadProviderLocaleCatalogs, localizeAuthTurn, } from "../i18n/catalog";
8
- import { categoryForStatus, isRetryableCategory, PROVIDER_OBSERVABILITY_TAXONOMY_VERSION, } from "../observability";
9
- import { createScratchpad } from "../runtime/auth-flow";
10
- import { createBrowserClient } from "../runtime/browser";
11
- import { createProviderCache } from "../runtime/cache";
12
- import { createProviderChoiceContext, PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV, } from "../runtime/choice";
13
- import { createCredentialContext } from "../runtime/credential";
14
- import { createEnvContext } from "../runtime/env";
15
- import { executeOperation } from "../runtime/executor";
16
- import { createHttpClient } from "../runtime/http";
17
- import { wrapWithInstrumentation } from "../runtime/instrumentation";
18
- import { getProviderBaseUrl } from "../runtime/provider";
19
- import { PROXY_AUTH_IP_DENIED_CODE, PROXY_EDGE_AUTH_REJECTED_CODE, PROXY_POOL_EXHAUSTED_CODE, } from "../runtime/proxy-errors";
20
- import { PROVIDER_TELEMETRY_HEADER, ProxyTelemetryCollector, } from "../runtime/proxy-telemetry";
21
- import { createProviderRuntimeStateFromEnv, createUnsupportedProviderRuntimeState, } from "../runtime/state";
22
- import { createStealthClient } from "../runtime/stealth";
23
- import { createSttClientFromEnv } from "../runtime/stt";
24
- import { createTraceContext } from "../runtime/trace";
25
- import { parseSchema } from "../schema";
26
- import { getStealthProfile } from "../stealth/profiles";
27
- import { APIFUSE_STREAM_DONE_EVENT, APIFUSE_STREAM_ERROR_EVENT, encodeSseEvent, error as streamError, } from "../stream";
28
- import { createSelfTestApp, createSelfTestInvoke, resolveSelfTestPort, } from "./self-test";
29
- import { resolveSelfTestMasterSecrets } from "./self-test-token";
30
- import { AuthFlowRequestSchema, OperationRequestSchema, } from "./types";
5
+ import { AuthAbortError, createAuthFlowHelpers } from "../auth.js";
6
+ import { AuthError, isProviderError, isSessionExpiredError, isTransportError, ProviderError, } from "../errors.js";
7
+ import { loadProviderLocaleCatalogs, localizeAuthTurn, } from "../i18n/catalog.js";
8
+ import { categoryForStatus, isRetryableCategory, PROVIDER_OBSERVABILITY_TAXONOMY_VERSION, } from "../observability.js";
9
+ import { createScratchpad } from "../runtime/auth-flow.js";
10
+ import { createBrowserClient } from "../runtime/browser.js";
11
+ import { createProviderCache } from "../runtime/cache.js";
12
+ import { createProviderChoiceContext, PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV, } from "../runtime/choice.js";
13
+ import { createCredentialContext } from "../runtime/credential.js";
14
+ import { createEnvContext } from "../runtime/env.js";
15
+ import { executeOperation } from "../runtime/executor.js";
16
+ import { createHttpClient } from "../runtime/http.js";
17
+ import { wrapWithInstrumentation } from "../runtime/instrumentation.js";
18
+ import { getProviderBaseUrl } from "../runtime/provider.js";
19
+ import { PROXY_AUTH_IP_DENIED_CODE, PROXY_EDGE_AUTH_REJECTED_CODE, PROXY_POOL_EXHAUSTED_CODE, } from "../runtime/proxy-errors.js";
20
+ import { PROVIDER_TELEMETRY_HEADER, ProxyTelemetryCollector } from "../runtime/proxy-telemetry.js";
21
+ import { createProviderRuntimeStateFromEnv, createUnsupportedProviderRuntimeState, } from "../runtime/state.js";
22
+ import { createStealthClient } from "../runtime/stealth.js";
23
+ import { createSttClientFromEnv } from "../runtime/stt.js";
24
+ import { createTraceContext } from "../runtime/trace.js";
25
+ import { parseSchema } from "../schema.js";
26
+ import { getStealthProfile } from "../stealth/profiles.js";
27
+ import { APIFUSE_STREAM_DONE_EVENT, APIFUSE_STREAM_ERROR_EVENT, encodeSseEvent, error as streamError, } from "../stream.js";
28
+ import { createSelfTestApp, createSelfTestAuthFlowInvoke, createSelfTestInvoke, resolveSelfTestPort, } from "./self-test.js";
29
+ import { resolveSelfTestMasterSecrets } from "./self-test-token.js";
30
+ import { AuthFlowRequestSchema, OperationRequestSchema, } from "./types.js";
31
31
  const DEFAULT_HOST = "0.0.0.0";
32
32
  const DEFAULT_PORT = 3000;
33
33
  const AUTH_FLOW_LOCALES = ["en", "ko", "ja"];
@@ -93,9 +93,7 @@ function getProviderStealthBaseUrl(provider) {
93
93
  return firstHost ? `https://${firstHost}` : undefined;
94
94
  }
95
95
  function getProviderStealthProfile(provider) {
96
- return provider.stealth?.profile
97
- ? getStealthProfile(provider.stealth.profile)
98
- : undefined;
96
+ return provider.stealth?.profile ? getStealthProfile(provider.stealth.profile) : undefined;
99
97
  }
100
98
  function isProductionProviderBrowserMode(provider, env = process.env) {
101
99
  if (provider.runtime !== "browser") {
@@ -104,13 +102,11 @@ function isProductionProviderBrowserMode(provider, env = process.env) {
104
102
  if (env.APIFUSE__PROVIDER__RUNTIME === "browser") {
105
103
  return true;
106
104
  }
107
- return (env.NODE_ENV === "production" && env.APIFUSE__PROVIDER__ID === provider.id);
105
+ return env.NODE_ENV === "production" && env.APIFUSE__PROVIDER__ID === provider.id;
108
106
  }
109
107
  export function resolveProviderProxyAffinityKey(provider, request, operationId) {
110
108
  const connectionKey = resolveOperationConnectionId(request) ?? request.connection?.externalRef;
111
- const affinity = typeof provider.proxy === "object"
112
- ? provider.proxy.session?.affinity
113
- : undefined;
109
+ const affinity = typeof provider.proxy === "object" ? provider.proxy.session?.affinity : undefined;
114
110
  if (affinity === "operation") {
115
111
  return `${provider.id}/${operationId}`;
116
112
  }
@@ -460,9 +456,7 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
460
456
  : "internal_error";
461
457
  const errorClass = error instanceof Error ? error.name : typeof error;
462
458
  const message = error instanceof Error ? error.message : String(error);
463
- const details = isProviderError(error)
464
- ? providerObservabilityDetails(error)
465
- : undefined;
459
+ const details = isProviderError(error) ? providerObservabilityDetails(error) : undefined;
466
460
  const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
467
461
  emit({
468
462
  level: status >= 500 ? "error" : "warn",
@@ -599,8 +593,7 @@ async function validateSseEvent(operation, event) {
599
593
  const transport = getSseTransport(operation);
600
594
  const schema = transport?.events?.[event.event];
601
595
  if (!schema) {
602
- if (event.event === APIFUSE_STREAM_ERROR_EVENT ||
603
- event.event === APIFUSE_STREAM_DONE_EVENT) {
596
+ if (event.event === APIFUSE_STREAM_ERROR_EVENT || event.event === APIFUSE_STREAM_DONE_EVENT) {
604
597
  return event;
605
598
  }
606
599
  throw new ProviderError(`SSE event "${event.event}" is not declared in operation transport.events.`, {
@@ -719,8 +712,7 @@ function enforceStreamChunkLimit(body, maxChunkBytes) {
719
712
  }
720
713
  function toStreamingResponse(operation, result, cleanup, requestId) {
721
714
  const transport = operation.transport?.kind ?? "json";
722
- if (transport === "sse" &&
723
- (result instanceof Response || result instanceof ReadableStream)) {
715
+ if (transport === "sse" && (result instanceof Response || result instanceof ReadableStream)) {
724
716
  void cleanup();
725
717
  throw new ProviderError("SSE operations must return an AsyncIterable of typed stream.event(...) values.", {
726
718
  code: "SSE_RESULT_UNSUPPORTED",
@@ -731,9 +723,7 @@ function toStreamingResponse(operation, result, cleanup, requestId) {
731
723
  }
732
724
  if (result instanceof Response) {
733
725
  const httpTransport = getHttpStreamTransport(operation);
734
- if (httpTransport &&
735
- result.body &&
736
- httpTransport?.maxChunkBytes !== undefined) {
726
+ if (httpTransport && result.body && httpTransport?.maxChunkBytes !== undefined) {
737
727
  return responseWithCleanup(new Response(enforceStreamChunkLimit(result.body, httpTransport.maxChunkBytes), {
738
728
  headers: result.headers,
739
729
  status: result.status,
@@ -752,8 +742,7 @@ function toStreamingResponse(operation, result, cleanup, requestId) {
752
742
  ? { "Content-Type": "text/event-stream; charset=utf-8" }
753
743
  : {
754
744
  "Content-Type": operation.transport?.kind === "http-stream"
755
- ? (operation.transport.contentType ??
756
- "application/octet-stream")
745
+ ? (operation.transport.contentType ?? "application/octet-stream")
757
746
  : "application/octet-stream",
758
747
  },
759
748
  }), cleanup);
@@ -771,9 +760,7 @@ function getSseTransport(operation) {
771
760
  return operation.transport?.kind === "sse" ? operation.transport : undefined;
772
761
  }
773
762
  function getHttpStreamTransport(operation) {
774
- return operation.transport?.kind === "http-stream"
775
- ? operation.transport
776
- : undefined;
763
+ return operation.transport?.kind === "http-stream" ? operation.transport : undefined;
777
764
  }
778
765
  function toAuthFlowResponse(result, contextPatch) {
779
766
  if (result instanceof Response) {
@@ -801,7 +788,7 @@ function isAuthFlowLocale(value) {
801
788
  return value === "en" || value === "ko" || value === "ja";
802
789
  }
803
790
  function isAuthTurn(value) {
804
- return (!!value && typeof value === "object" && "kind" in value && "turnId" in value);
791
+ return !!value && typeof value === "object" && "kind" in value && "turnId" in value;
805
792
  }
806
793
  function loadAuthFlowLocaleCatalogs(provider) {
807
794
  for (const providerDir of [
@@ -1132,6 +1119,7 @@ export async function serve(provider, options = {}) {
1132
1119
  const selfTestApp = createSelfTestApp(provider, {
1133
1120
  secrets: selfTestSecrets,
1134
1121
  invoke: createSelfTestInvoke(app),
1122
+ authFlow: createSelfTestAuthFlowInvoke(app),
1135
1123
  });
1136
1124
  bunRuntime.serve({
1137
1125
  port: options.selfTestPort ?? resolveSelfTestPort(),
@@ -1,11 +1,6 @@
1
1
  import { z } from "zod";
2
- import { HttpRetryPreset } from "../types";
3
- export const ConnectionModeSchema = z.enum([
4
- "oauth2",
5
- "credentials",
6
- "platform-managed",
7
- "none",
8
- ]);
2
+ import { HttpRetryPreset } from "../types.js";
3
+ export const ConnectionModeSchema = z.enum(["oauth2", "credentials", "platform-managed", "none"]);
9
4
  export const OperationConnectionSchema = z.object({
10
5
  id: z.string(),
11
6
  mode: ConnectionModeSchema,
@@ -1,4 +1,4 @@
1
- import type { StealthProfile } from "../types";
1
+ import type { StealthProfile } from "../types.js";
2
2
  export declare function generateLayer2Headers(profile: StealthProfile): Record<string, string>;
3
3
  export declare function getStealthProfile(name: string): StealthProfile;
4
4
  export declare function listStealthProfiles(): string[];
@@ -1,4 +1,4 @@
1
- import { SDKError } from "../errors";
1
+ import { SDKError } from "../errors.js";
2
2
  const CHROMIUM_HEADER_ORDER = [
3
3
  ":method",
4
4
  ":authority",
@@ -74,12 +74,8 @@ function createProfile(name, definition) {
74
74
  tlsClientIdentifier: definition.tlsClientIdentifier,
75
75
  ja3: definition.ja3,
76
76
  ja4: definition.ja4,
77
- h2Settings: definition.h2Settings
78
- ? { ...definition.h2Settings }
79
- : undefined,
80
- headerOrder: definition.headerOrder
81
- ? [...definition.headerOrder]
82
- : undefined,
77
+ h2Settings: definition.h2Settings ? { ...definition.h2Settings } : undefined,
78
+ headerOrder: definition.headerOrder ? [...definition.headerOrder] : undefined,
83
79
  };
84
80
  }
85
81
  function extractBrowserMajorVersion(profile) {
@@ -120,9 +116,7 @@ export function generateLayer2Headers(profile) {
120
116
  : `"Chromium";v="${majorVersion}", "Google Chrome";v="${majorVersion}", "Not)A;Brand";v="99"`;
121
117
  headers["Sec-Ch-Ua-Platform"] = toPlatformHeaderValue(profile.platform);
122
118
  headers["Sec-Ch-Ua-Mobile"] =
123
- profile.platform === "android" || profile.platform === "ios"
124
- ? "?1"
125
- : "?0";
119
+ profile.platform === "android" || profile.platform === "ios" ? "?1" : "?0";
126
120
  }
127
121
  return headers;
128
122
  }
@@ -252,8 +246,5 @@ export function getStealthProfile(name) {
252
246
  };
253
247
  }
254
248
  export function listStealthProfiles() {
255
- return [
256
- ...Object.keys(STEALTH_PROFILES),
257
- ...Object.keys(STEALTH_PROFILE_ALIASES),
258
- ];
249
+ return [...Object.keys(STEALTH_PROFILES), ...Object.keys(STEALTH_PROFILE_ALIASES)];
259
250
  }
package/dist/stream.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { SseMessage } from "./types";
1
+ import type { SseMessage } from "./types.js";
2
2
  export interface SseEvent<TData = unknown> {
3
3
  event: string;
4
4
  data: TData;
@@ -1,2 +1,2 @@
1
- export { describeTransform, snapshotTransform, toMatchShape } from "./helpers";
2
- export { runStandardTests } from "./run";
1
+ export { describeTransform, snapshotTransform, toMatchShape } from "./helpers.js";
2
+ export { runStandardTests } from "./run.js";
@@ -1,2 +1,2 @@
1
- export { describeTransform, snapshotTransform, toMatchShape } from "./helpers";
2
- export { runStandardTests } from "./run";
1
+ export { describeTransform, snapshotTransform, toMatchShape } from "./helpers.js";
2
+ export { runStandardTests } from "./run.js";
@@ -1,4 +1,4 @@
1
- import type { AuthMode, ProviderDefinition } from "../types";
1
+ import type { AuthMode, ProviderDefinition } from "../types.js";
2
2
  export interface StandardTestsManifest {
3
3
  id?: string;
4
4
  displayName?: string;
@@ -1,19 +1,14 @@
1
1
  import { describe, expect, it } from "bun:test";
2
- import { createProviderCache } from "../runtime/cache";
3
- import { createTestProviderChoiceContext } from "../runtime/choice";
4
- import { createMemoryProviderRuntimeState } from "../runtime/state";
5
- import { createUnsupportedSttClient } from "../runtime/stt";
6
- import { safeParseSchemaSync } from "../schema";
2
+ import { createProviderCache } from "../runtime/cache.js";
3
+ import { createTestProviderChoiceContext } from "../runtime/choice.js";
4
+ import { createMemoryProviderRuntimeState } from "../runtime/state.js";
5
+ import { createUnsupportedSttClient } from "../runtime/stt.js";
6
+ import { safeParseSchemaSync } from "../schema.js";
7
7
  // Mirrors CONNECTOR_ID_REGEX in ../define.ts, which defineProvider() enforces.
8
8
  // A single lowercase segment (no hyphen) is a valid id, so the trailing group
9
9
  // is optional (`*`), matching providers like `kakaomap`, `kstartup`, `triple`.
10
10
  const CONNECTOR_ID_REGEX = /^[a-z][a-z0-9]*(-[a-z][a-z0-9]*)*$/;
11
- const VALID_AUTH_MODES = [
12
- "none",
13
- "platform-managed",
14
- "credentials",
15
- "oauth2",
16
- ];
11
+ const VALID_AUTH_MODES = ["none", "platform-managed", "credentials", "oauth2"];
17
12
  const UPDATE_SNAPSHOT_ARGS = new Set(["-u", "--update-snapshots"]);
18
13
  function isFixtureEnvelope(value) {
19
14
  return (value !== null &&
@@ -60,9 +55,7 @@ function inferFixtureDir(providerId) {
60
55
  .map((line) => line.match(/\(?((?:file:\/\/)?[^():]+\.test\.ts)/)?.[1])
61
56
  .find((file) => file !== undefined);
62
57
  if (testFile) {
63
- const pathname = testFile.startsWith("file://")
64
- ? new URL(testFile).pathname
65
- : testFile;
58
+ const pathname = testFile.startsWith("file://") ? new URL(testFile).pathname : testFile;
66
59
  return `${pathname.replace(/\/[^/]+$/, "")}/../__fixtures__`;
67
60
  }
68
61
  return `providers/${providerId}/__fixtures__`;
package/dist/types.d.ts CHANGED
@@ -1418,6 +1418,19 @@ export interface OperationContractMetadata {
1418
1418
  deprecation?: OperationDeprecationMetadata;
1419
1419
  }
1420
1420
  export interface OperationDefinition<TInput extends SchemaLike = SchemaLike, TOutput extends SchemaLike = SchemaLike> {
1421
+ /**
1422
+ * Short English display title for the operation. The SDK passes it through
1423
+ * verbatim; the APIFuse registry derives the operation's en locale title
1424
+ * baseline from it (`docs.titleKey` remains available as an explicit key
1425
+ * override).
1426
+ */
1427
+ title?: string;
1428
+ /**
1429
+ * Raw English operation description. The SDK passes it through verbatim;
1430
+ * keyed descriptions via `descriptionKey` remain the primary flow, and
1431
+ * provider lint rules for raw prose descriptions still apply.
1432
+ */
1433
+ description?: string;
1421
1434
  descriptionKey?: ProviderLocaleKeyInput;
1422
1435
  docs?: OperationDocMeta;
1423
1436
  whenToUseKeys?: readonly ProviderLocaleKeyInput[];
@@ -1447,10 +1460,54 @@ export interface OperationDefinition<TInput extends SchemaLike = SchemaLike, TOu
1447
1460
  healthCheck?: HealthCheckSuite<InferSchemaOutput<TInput>, InferSchemaOutput<TOutput>>;
1448
1461
  healthCheckUnsupported?: HealthCheckUnsupported;
1449
1462
  }
1463
+ /**
1464
+ * Author-declared deployment overrides accepted as the optional top-level
1465
+ * `deployment` key on `defineProvider()`.
1466
+ *
1467
+ * The SDK passes this object through VERBATIM onto the returned provider
1468
+ * definition — it is typed here but deliberately not deep-validated: the
1469
+ * APIFuse registry builder owns deployment validation and resolves omitted
1470
+ * fields against the runtime deployment profiles. The shape mirrors the
1471
+ * registry's `ProviderDeploymentOverrides` contract; every field is
1472
+ * optional. Note that the deployment `runtime` axis
1473
+ * (`shared`/`dedicated`/`browser`) is distinct from the provider execution
1474
+ * `runtime` (`standard`/`shared`/`browser`).
1475
+ */
1476
+ export interface ProviderDeploymentOverrides {
1477
+ runtime?: "shared" | "dedicated" | "browser";
1478
+ language?: "typescript" | "python";
1479
+ replicas?: number;
1480
+ hpa?: {
1481
+ enabled: boolean;
1482
+ minReplicas?: number;
1483
+ maxReplicas?: number;
1484
+ targetCPUUtilizationPercentage?: number;
1485
+ };
1486
+ resources?: {
1487
+ cpu: string;
1488
+ memory: string;
1489
+ };
1490
+ cache?: {
1491
+ redis?: {
1492
+ enabled: boolean;
1493
+ url?: string;
1494
+ };
1495
+ };
1496
+ network?: {
1497
+ additionalTcpPorts?: number[];
1498
+ };
1499
+ buildContext?: string;
1500
+ }
1450
1501
  export interface ProviderDefinition {
1451
1502
  id: string;
1452
1503
  version: string;
1453
1504
  runtime: "standard" | "shared" | "browser";
1505
+ /**
1506
+ * Optional deployment overrides, passed through verbatim from
1507
+ * `defineProvider({ deployment })`. Validation and profile resolution are
1508
+ * owned by the APIFuse registry builder, not the SDK.
1509
+ */
1510
+ deployment?: ProviderDeploymentOverrides;
1454
1511
  allowedHosts?: string[];
1455
1512
  stealth?: {
1456
1513
  profile: string;
@@ -0,0 +1,30 @@
1
+ export declare const NEEDS_INPUT_STATUS: "needs_input";
2
+ export interface ProviderSelectionOption {
3
+ readonly selection_value: string;
4
+ readonly label: string;
5
+ readonly [extra: string]: unknown;
6
+ }
7
+ export interface ProviderRequiredSelection {
8
+ readonly selection_key: string;
9
+ readonly label: string;
10
+ readonly required: boolean;
11
+ readonly selection_type?: string;
12
+ readonly valid_options: readonly ProviderSelectionOption[];
13
+ readonly [extra: string]: unknown;
14
+ }
15
+ export interface ProviderSelectedOption {
16
+ readonly selection_key: string;
17
+ readonly selection_value: string;
18
+ /** Free-text answer for text-kind selections. */
19
+ readonly text?: string;
20
+ /** Quantity for quantity-kind selections. */
21
+ readonly quantity?: number;
22
+ }
23
+ export interface ProviderNeedsInputPayload {
24
+ readonly status: typeof NEEDS_INPUT_STATUS;
25
+ readonly required_selections: readonly ProviderRequiredSelection[];
26
+ readonly selected_options?: readonly ProviderSelectedOption[];
27
+ /** Provider-specific fresh state token(s), e.g. `reservation_state`. */
28
+ readonly [extra: string]: unknown;
29
+ }
30
+ export declare function isProviderNeedsInputPayload(value: unknown): value is ProviderNeedsInputPayload;
@@ -0,0 +1,66 @@
1
+ // Official cross-provider contract for user-input round-trips.
2
+ //
3
+ // Doctrine (born from the 2026-07-20 CatchTable reserve incident): an
4
+ // operation MUST NOT dead-end on a problem the end user can resolve by
5
+ // choosing among live options. Instead of throwing, the provider returns a
6
+ // SUCCESSFUL payload with `status: "needs_input"` that carries everything a
7
+ // consumer-side agent needs to relay the choice verbatim and retry:
8
+ //
9
+ // - `required_selections`: only the still-pending questions, each with
10
+ // human-readable `label`s and `valid_options` the agent shows the user
11
+ // as-is. The agent never chooses on the user's behalf.
12
+ // - `selected_options`: selections already settled, echoed so the follow-up
13
+ // call keeps them without the agent reconstructing anything.
14
+ // - a fresh provider-specific state token (e.g. `reservation_state`) minted
15
+ // at response time, so the retry never races an expired token.
16
+ //
17
+ // Keeping this success-shaped (instead of error `details`) is deliberate:
18
+ // consumer error-shaping layers routinely strip error metadata, and a model
19
+ // that only sees "error" narrates failure to the user. Complex recovery
20
+ // logic belongs to the system, not the model.
21
+ //
22
+ // Deliberately ABSENT: retry templates, next-action routing, or any other
23
+ // agent choreography. Provider payloads carry upstream-backed data only; how
24
+ // to phrase the ask and shape the retry call is the consumer's contract
25
+ // (apifuse#1030). The retry is fully determined by data: copy the echoed
26
+ // selected_options, add the user's new answers, resend with the fresh state.
27
+ export const NEEDS_INPUT_STATUS = "needs_input";
28
+ function isRecord(value) {
29
+ return typeof value === "object" && value !== null && !Array.isArray(value);
30
+ }
31
+ function isSelectionOption(value) {
32
+ return (isRecord(value) &&
33
+ typeof value.selection_value === "string" &&
34
+ typeof value.label === "string");
35
+ }
36
+ function isRequiredSelection(value) {
37
+ return (isRecord(value) &&
38
+ typeof value.selection_key === "string" &&
39
+ typeof value.label === "string" &&
40
+ typeof value.required === "boolean" &&
41
+ Array.isArray(value.valid_options) &&
42
+ value.valid_options.every(isSelectionOption));
43
+ }
44
+ function isSelectedOption(value) {
45
+ return (isRecord(value) &&
46
+ typeof value.selection_key === "string" &&
47
+ typeof value.selection_value === "string");
48
+ }
49
+ export function isProviderNeedsInputPayload(value) {
50
+ if (!isRecord(value)) {
51
+ return false;
52
+ }
53
+ if (value.status !== NEEDS_INPUT_STATUS ||
54
+ !Array.isArray(value.required_selections) ||
55
+ !value.required_selections.every(isRequiredSelection)) {
56
+ return false;
57
+ }
58
+ // A needs_input with nothing to ask AND nothing settled to echo is a
59
+ // no-op dead end — reject it so providers cannot ship it accidentally.
60
+ if (value.required_selections.length > 0) {
61
+ return true;
62
+ }
63
+ return (Array.isArray(value.selected_options) &&
64
+ value.selected_options.length > 0 &&
65
+ value.selected_options.every(isSelectedOption));
66
+ }
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.2.0-beta.3",
2
+ "version": "2.2.0-beta.5",
3
3
  "name": "@apifuse/provider-sdk",
4
4
  "private": false,
5
5
  "type": "module",
@@ -76,20 +76,22 @@
76
76
  "lint": "biome lint .",
77
77
  "lint:fix": "biome lint --write",
78
78
  "format": "biome format --write",
79
- "type-check": "tsgo --noEmit",
79
+ "type-check": "tsc --noEmit",
80
80
  "test": "bun test",
81
81
  "check": "bun run lint && bun run type-check && bun run build",
82
82
  "pack:check": "bun run build && bun bin/apifuse-pack-check.ts",
83
83
  "pack:smoke": "bun run build && bun bin/apifuse-pack-smoke.ts",
84
+ "pack:types": "bun run build && bun bin/apifuse-pack-types.ts",
84
85
  "release:guard": "bun scripts/guard-release-pr.ts",
85
86
  "format:check": "biome format .",
86
- "build": "tsgo -p tsconfig.build.json && rm -rf dist/cli/templates && cp -R src/cli/templates dist/cli/templates && rm -rf dist/auth-turn/fixtures && cp -R src/auth-turn/fixtures dist/auth-turn/fixtures && cp src/auth-turn/auth-turn.v1.schema.json dist/auth-turn/auth-turn.v1.schema.json"
87
+ "build": "tsc -p tsconfig.build.json && rm -rf dist/cli/templates && cp -R src/cli/templates dist/cli/templates && rm -rf dist/auth-turn/fixtures && cp -R src/auth-turn/fixtures dist/auth-turn/fixtures && cp src/auth-turn/auth-turn.v1.schema.json dist/auth-turn/auth-turn.v1.schema.json"
87
88
  },
88
89
  "devDependencies": {
90
+ "@arethetypeswrong/cli": "^0.18.5",
89
91
  "@biomejs/biome": "^2.5.0",
90
92
  "@types/bun": "latest",
91
93
  "@types/node": "^25.9.3",
92
- "@typescript/native-preview": "7.0.0-dev.20260419.1"
94
+ "typescript": "7.0.2"
93
95
  },
94
96
  "dependencies": {
95
97
  "@clack/prompts": "^1.5.1",
@@ -1,4 +1,4 @@
1
- import type { AuthTurn } from "../types";
1
+ import type { AuthTurn } from "../types.js";
2
2
 
3
3
  export type { AuthTurn };
4
4