@apifuse/provider-sdk 2.2.0-beta.1 → 2.2.0-beta.10

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 (203) hide show
  1. package/AUTHORING.md +106 -0
  2. package/CHANGELOG.md +37 -0
  3. package/README.md +5 -1
  4. package/SUBMISSION.md +1 -1
  5. package/bin/apifuse-check.ts +44 -59
  6. package/bin/apifuse-create.ts +1 -1
  7. package/bin/apifuse-dev.ts +27 -52
  8. package/bin/apifuse-pack-check.ts +14 -0
  9. package/bin/apifuse-pack-smoke.ts +22 -81
  10. package/bin/apifuse-pack-types.ts +266 -0
  11. package/bin/apifuse-perf.ts +45 -127
  12. package/bin/apifuse-record.ts +53 -70
  13. package/bin/apifuse-submit-check.ts +510 -45
  14. package/bin/apifuse-sync-assets.ts +117 -0
  15. package/bin/apifuse.ts +1 -1
  16. package/bin/submit-check-delimited-text.ts +50 -0
  17. package/bin/submit-check-xml-semantics.ts +204 -0
  18. package/bin/submit-check-xml.ts +134 -0
  19. package/dist/auth-turn/index.d.ts +3 -3
  20. package/dist/auth.d.ts +2 -2
  21. package/dist/auth.js +9 -18
  22. package/dist/ceremonies/index.d.ts +1 -1
  23. package/dist/ceremonies/index.js +8 -18
  24. package/dist/cli/commands.d.ts +1 -1
  25. package/dist/cli/commands.js +8 -0
  26. package/dist/cli/create.d.ts +3 -0
  27. package/dist/cli/create.js +34 -35
  28. package/dist/cli/prompt-assets.d.ts +80 -0
  29. package/dist/cli/prompt-assets.js +743 -0
  30. package/dist/cli/templates/provider/AGENTS.md.tpl +17 -8
  31. package/dist/config/loader.d.ts +149 -8
  32. package/dist/config/loader.js +378 -89
  33. package/dist/contract-serialization.d.ts +2 -2
  34. package/dist/contract-serialization.js +3 -6
  35. package/dist/contract-types.d.ts +2 -2
  36. package/dist/contract.d.ts +3 -3
  37. package/dist/contract.js +4 -6
  38. package/dist/define.d.ts +8 -1
  39. package/dist/define.js +119 -122
  40. package/dist/dev.d.ts +1 -1
  41. package/dist/dev.js +1 -1
  42. package/dist/errors.d.ts +4 -1
  43. package/dist/errors.js +48 -0
  44. package/dist/i18n/catalog.d.ts +2 -2
  45. package/dist/i18n/catalog.js +4 -10
  46. package/dist/i18n/index.d.ts +2 -2
  47. package/dist/i18n/index.js +2 -2
  48. package/dist/i18n/keys.d.ts +2 -2
  49. package/dist/index.d.ts +44 -42
  50. package/dist/index.js +39 -37
  51. package/dist/lint.js +8 -15
  52. package/dist/provider.d.ts +11 -11
  53. package/dist/provider.js +9 -9
  54. package/dist/public-schema-field-lint.d.ts +1 -1
  55. package/dist/recipes/gov-api.js +1 -1
  56. package/dist/runtime/auth-flow.d.ts +1 -1
  57. package/dist/runtime/auth-flow.js +3 -3
  58. package/dist/runtime/browser.d.ts +1 -1
  59. package/dist/runtime/browser.js +15 -29
  60. package/dist/runtime/cache.d.ts +1 -1
  61. package/dist/runtime/cache.js +4 -8
  62. package/dist/runtime/choice.d.ts +1 -1
  63. package/dist/runtime/choice.js +31 -35
  64. package/dist/runtime/credential.d.ts +1 -1
  65. package/dist/runtime/credential.js +1 -1
  66. package/dist/runtime/env.d.ts +1 -1
  67. package/dist/runtime/executor.d.ts +1 -1
  68. package/dist/runtime/executor.js +15 -3
  69. package/dist/runtime/http.d.ts +2 -2
  70. package/dist/runtime/http.js +144 -348
  71. package/dist/runtime/insights.d.ts +1 -1
  72. package/dist/runtime/insights.js +6 -13
  73. package/dist/runtime/instrumentation.d.ts +2 -2
  74. package/dist/runtime/instrumentation.js +56 -19
  75. package/dist/runtime/keyring.js +1 -1
  76. package/dist/runtime/namespace.js +1 -1
  77. package/dist/runtime/otlp.d.ts +1 -1
  78. package/dist/runtime/perf.d.ts +1 -1
  79. package/dist/runtime/provider.d.ts +1 -1
  80. package/dist/runtime/provider.js +1 -2
  81. package/dist/runtime/proxy-errors.d.ts +1 -1
  82. package/dist/runtime/proxy-errors.js +9 -7
  83. package/dist/runtime/proxy-nodemaven.d.ts +35 -0
  84. package/dist/runtime/proxy-nodemaven.js +128 -0
  85. package/dist/runtime/proxy-retry-policy.d.ts +40 -0
  86. package/dist/runtime/proxy-retry-policy.js +326 -0
  87. package/dist/runtime/proxy-telemetry.d.ts +2 -1
  88. package/dist/runtime/proxy-telemetry.js +55 -52
  89. package/dist/runtime/redis.d.ts +1 -1
  90. package/dist/runtime/redis.js +2 -4
  91. package/dist/runtime/request-options.d.ts +1 -1
  92. package/dist/runtime/secrets.d.ts +27 -0
  93. package/dist/runtime/secrets.js +51 -0
  94. package/dist/runtime/state.d.ts +2 -2
  95. package/dist/runtime/state.js +15 -4
  96. package/dist/runtime/stealth.d.ts +2 -2
  97. package/dist/runtime/stealth.js +108 -212
  98. package/dist/runtime/stt.d.ts +1 -1
  99. package/dist/runtime/stt.js +11 -15
  100. package/dist/runtime/trace.d.ts +2 -2
  101. package/dist/runtime/trace.js +2 -4
  102. package/dist/runtime/waterfall.d.ts +1 -1
  103. package/dist/schema.d.ts +1 -1
  104. package/dist/schema.js +7 -15
  105. package/dist/serve.d.ts +1 -1
  106. package/dist/serve.js +1 -1
  107. package/dist/server/index.d.ts +7 -7
  108. package/dist/server/index.js +6 -6
  109. package/dist/server/self-test-redaction.d.ts +1 -1
  110. package/dist/server/self-test-redaction.js +1 -1
  111. package/dist/server/self-test.d.ts +104 -3
  112. package/dist/server/self-test.js +673 -115
  113. package/dist/server/serve.d.ts +8 -3
  114. package/dist/server/serve.js +105 -59
  115. package/dist/server/types.d.ts +10 -9
  116. package/dist/server/types.js +3 -7
  117. package/dist/stealth/profiles.d.ts +1 -1
  118. package/dist/stealth/profiles.js +5 -14
  119. package/dist/stream.d.ts +1 -1
  120. package/dist/testing/index.d.ts +2 -2
  121. package/dist/testing/index.js +2 -2
  122. package/dist/testing/run.d.ts +1 -1
  123. package/dist/testing/run.js +7 -14
  124. package/dist/types.d.ts +87 -1
  125. package/dist/user-input.d.ts +30 -0
  126. package/dist/user-input.js +66 -0
  127. package/package.json +9 -5
  128. package/src/auth-turn/index.ts +1 -1
  129. package/src/auth.ts +28 -86
  130. package/src/ceremonies/index.ts +24 -78
  131. package/src/cli/commands.ts +10 -0
  132. package/src/cli/create.ts +42 -35
  133. package/src/cli/prompt-assets.ts +865 -0
  134. package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
  135. package/src/config/loader.ts +543 -208
  136. package/src/contract-serialization.ts +5 -11
  137. package/src/contract-types.ts +2 -2
  138. package/src/contract.ts +12 -28
  139. package/src/define.ts +247 -505
  140. package/src/dev.ts +4 -9
  141. package/src/errors.ts +58 -5
  142. package/src/i18n/catalog.ts +10 -32
  143. package/src/i18n/index.ts +2 -2
  144. package/src/i18n/keys.ts +5 -11
  145. package/src/index.ts +49 -42
  146. package/src/lint.ts +89 -159
  147. package/src/provider.ts +15 -11
  148. package/src/public-schema-field-lint.ts +7 -33
  149. package/src/recipes/gov-api.ts +2 -5
  150. package/src/runtime/auth-flow.ts +5 -7
  151. package/src/runtime/browser.ts +64 -187
  152. package/src/runtime/cache.ts +20 -67
  153. package/src/runtime/choice.ts +79 -132
  154. package/src/runtime/credential.ts +2 -2
  155. package/src/runtime/env.ts +1 -1
  156. package/src/runtime/executor.ts +23 -20
  157. package/src/runtime/http.ts +195 -559
  158. package/src/runtime/insights.ts +15 -53
  159. package/src/runtime/instrumentation.ts +78 -65
  160. package/src/runtime/keyring.ts +7 -19
  161. package/src/runtime/namespace.ts +2 -7
  162. package/src/runtime/otlp.ts +12 -23
  163. package/src/runtime/perf.ts +1 -1
  164. package/src/runtime/provider.ts +4 -9
  165. package/src/runtime/proxy-errors.ts +29 -42
  166. package/src/runtime/proxy-nodemaven.ts +178 -0
  167. package/src/runtime/proxy-retry-policy.ts +469 -0
  168. package/src/runtime/proxy-telemetry.ts +79 -77
  169. package/src/runtime/redis.ts +4 -12
  170. package/src/runtime/request-options.ts +4 -13
  171. package/src/runtime/secrets.ts +64 -0
  172. package/src/runtime/state.ts +41 -110
  173. package/src/runtime/stealth.ts +162 -377
  174. package/src/runtime/stt.ts +38 -94
  175. package/src/runtime/trace.ts +14 -44
  176. package/src/runtime/waterfall.ts +5 -18
  177. package/src/schema.ts +23 -84
  178. package/src/serve.ts +1 -1
  179. package/src/server/index.ts +12 -7
  180. package/src/server/self-test-redaction.ts +2 -2
  181. package/src/server/self-test.ts +857 -132
  182. package/src/server/serve.ts +172 -245
  183. package/src/server/types.ts +7 -19
  184. package/src/stealth/profiles.ts +10 -26
  185. package/src/stream.ts +8 -19
  186. package/src/testing/index.ts +2 -2
  187. package/src/testing/run.ts +19 -67
  188. package/src/types.ts +88 -1
  189. package/src/user-input.ts +118 -0
  190. package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
  191. package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
  192. /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  193. /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  194. /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  195. /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  196. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  197. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
  198. /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  199. /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  200. /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  201. /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  202. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  203. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -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;
@@ -35,6 +35,11 @@ export type ProviderServerLogEvent = (ProviderServerLogEventBase & {
35
35
  message: string;
36
36
  }>;
37
37
  }) | {
38
+ level: "warn";
39
+ event: "provider_secrets_missing";
40
+ providerId: string;
41
+ missingSecrets: string[];
42
+ } | {
38
43
  level: "warn";
39
44
  event: "provider_cleanup_failed";
40
45
  providerId: string;
@@ -2,32 +2,33 @@ 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, ProviderError, SessionExpiredError, TransportError, } 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 { assertRequiredSecretsPresent, listMissingRequiredSecrets, MISSING_SECRET_CODE, } from "../runtime/secrets.js";
22
+ import { createProviderRuntimeStateFromEnv, createUnsupportedProviderRuntimeState, } from "../runtime/state.js";
23
+ import { createStealthClient } from "../runtime/stealth.js";
24
+ import { createSttClientFromEnv } from "../runtime/stt.js";
25
+ import { createTraceContext } from "../runtime/trace.js";
26
+ import { parseSchema } from "../schema.js";
27
+ import { getStealthProfile } from "../stealth/profiles.js";
28
+ import { APIFUSE_STREAM_DONE_EVENT, APIFUSE_STREAM_ERROR_EVENT, encodeSseEvent, error as streamError, } from "../stream.js";
29
+ import { createSelfTestApp, createSelfTestAuthFlowInvoke, createSelfTestInvoke, resolveSelfTestPort, } from "./self-test.js";
30
+ import { resolveSelfTestMasterSecrets } from "./self-test-token.js";
31
+ import { AuthFlowRequestSchema, OperationRequestSchema, } from "./types.js";
31
32
  const DEFAULT_HOST = "0.0.0.0";
32
33
  const DEFAULT_PORT = 3000;
33
34
  const AUTH_FLOW_LOCALES = ["en", "ko", "ja"];
@@ -93,9 +94,7 @@ function getProviderStealthBaseUrl(provider) {
93
94
  return firstHost ? `https://${firstHost}` : undefined;
94
95
  }
95
96
  function getProviderStealthProfile(provider) {
96
- return provider.stealth?.profile
97
- ? getStealthProfile(provider.stealth.profile)
98
- : undefined;
97
+ return provider.stealth?.profile ? getStealthProfile(provider.stealth.profile) : undefined;
99
98
  }
100
99
  function isProductionProviderBrowserMode(provider, env = process.env) {
101
100
  if (provider.runtime !== "browser") {
@@ -104,18 +103,19 @@ function isProductionProviderBrowserMode(provider, env = process.env) {
104
103
  if (env.APIFUSE__PROVIDER__RUNTIME === "browser") {
105
104
  return true;
106
105
  }
107
- return (env.NODE_ENV === "production" && env.APIFUSE__PROVIDER__ID === provider.id);
106
+ return env.NODE_ENV === "production" && env.APIFUSE__PROVIDER__ID === provider.id;
108
107
  }
109
108
  export function resolveProviderProxyAffinityKey(provider, request, operationId) {
110
- const connectionKey = request.connection?.id ?? request.connection?.externalRef;
111
- const affinity = typeof provider.proxy === "object"
112
- ? provider.proxy.session?.affinity
113
- : undefined;
109
+ const connectionKey = resolveOperationConnectionId(request) ?? request.connection?.externalRef;
110
+ const affinity = typeof provider.proxy === "object" ? provider.proxy.session?.affinity : undefined;
114
111
  if (affinity === "operation") {
115
112
  return `${provider.id}/${operationId}`;
116
113
  }
117
114
  return connectionKey ?? provider.id;
118
115
  }
116
+ function resolveOperationConnectionId(request) {
117
+ return request.connection?.id ?? request.connectionId;
118
+ }
119
119
  function createProviderContext(provider, request, operationId, options = {}, state = createUnsupportedProviderRuntimeState(), proxyTelemetry) {
120
120
  const baseUrl = getProviderBaseUrl(provider);
121
121
  const stealthBaseUrl = getProviderStealthBaseUrl(provider);
@@ -142,7 +142,7 @@ function createProviderContext(provider, request, operationId, options = {}, sta
142
142
  values: request.connection?.secrets,
143
143
  });
144
144
  const requestContext = {
145
- connectionId: request.connection?.id,
145
+ connectionId: resolveOperationConnectionId(request),
146
146
  headers: request.headers ?? {},
147
147
  };
148
148
  const context = wrapWithInstrumentation({
@@ -290,7 +290,7 @@ function zodDetails(error) {
290
290
  }));
291
291
  }
292
292
  function toErrorResponse(error, requestId) {
293
- if (error instanceof ProviderError) {
293
+ if (isProviderError(error)) {
294
294
  const details = publicProviderErrorDetails(error);
295
295
  return {
296
296
  error: {
@@ -312,11 +312,22 @@ function toErrorResponse(error, requestId) {
312
312
  },
313
313
  };
314
314
  }
315
+ // A masked internal error MUST NOT be advertised as retryable: without an
316
+ // explicit retryable:false the hub (bori provider-backed engine) defaults 5xx
317
+ // to retryable:true, which turns a deterministic pre-upstream crash into an
318
+ // infinite START->CONTINUE->restart loop (2026-07-22 catchtable reserve RCA).
319
+ // We still refuse to leak message/stack — only the error class name (or the
320
+ // primitive type for non-Error throwables) is surfaced for ops triage.
315
321
  return {
316
322
  error: {
317
323
  code: "internal_error",
318
324
  message: "Internal error",
319
325
  ...(requestId ? { requestId } : {}),
326
+ details: {
327
+ retryable: false,
328
+ category: "internal_error",
329
+ errorClass: error instanceof Error ? error.name : typeof error,
330
+ },
320
331
  },
321
332
  };
322
333
  }
@@ -340,20 +351,37 @@ function publicProviderErrorDetails(error) {
340
351
  function isPlainRecord(value) {
341
352
  return value !== null && typeof value === "object" && !Array.isArray(value);
342
353
  }
354
+ // Accepts `unknown` so the branded guards narrow cleanly from the top: the
355
+ // subtype error classes are structurally compatible with ProviderError, so
356
+ // narrowing from a ProviderError-typed value would collapse the negative branch
357
+ // to `never`. Narrowing from unknown avoids that while still recognizing errors
358
+ // from a duplicate SDK module instance.
343
359
  function providerObservabilityDetails(error) {
344
360
  // Session-expiry surfaces the credential_expired category + the opt-in
345
361
  // retryable signal so Gateway/Credential Service can refresh and re-drive the
346
362
  // operation (see design.md §4.3 D3). Without this branch the auth error would
347
363
  // serialize as a bare 401 with no retryable/category, losing the refresh
348
364
  // signal for exactly the retryOnAuthRefresh operations it is meant to enable.
349
- if (error instanceof SessionExpiredError) {
365
+ if (isSessionExpiredError(error)) {
350
366
  return {
351
367
  category: error.options?.category ?? "credential_expired",
352
368
  taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
353
369
  retryable: error.options?.retryable ?? false,
354
370
  };
355
371
  }
356
- if (!(error instanceof TransportError)) {
372
+ // Missing-secret errors carry the canonical credential_unavailable category
373
+ // so Gateway/observability can attribute the failure to provisioning, not
374
+ // the upstream. Matched by code (not constructor) so both the SDK-owned
375
+ // runtime gate and any not-yet-migrated provider-thrown MISSING_SECRET
376
+ // serialize identically, including across duplicate SDK module instances.
377
+ if (isProviderError(error) && error.code === MISSING_SECRET_CODE) {
378
+ return {
379
+ category: error.options?.category ?? "credential_unavailable",
380
+ taxonomyVersion: PROVIDER_OBSERVABILITY_TAXONOMY_VERSION,
381
+ retryable: error.options?.retryable ?? false,
382
+ };
383
+ }
384
+ if (!isTransportError(error)) {
357
385
  return undefined;
358
386
  }
359
387
  const isProxyPoolCode = error.code === PROXY_POOL_EXHAUSTED_CODE ||
@@ -382,7 +410,7 @@ function providerObservabilityDetails(error) {
382
410
  };
383
411
  }
384
412
  function publicProviderErrorMessage(error) {
385
- if (error instanceof TransportError) {
413
+ if (isTransportError(error)) {
386
414
  if (error.code === PROXY_AUTH_IP_DENIED_CODE) {
387
415
  return error.message;
388
416
  }
@@ -410,14 +438,18 @@ function toStatusCode(error) {
410
438
  if (error instanceof z.ZodError) {
411
439
  return 400;
412
440
  }
413
- if (error instanceof TransportError) {
441
+ if (isTransportError(error)) {
414
442
  return error.code === "transport_timeout" ? 504 : 502;
415
443
  }
416
- if (error instanceof ProviderError) {
444
+ if (isProviderError(error)) {
417
445
  switch (error.code) {
418
446
  case "AUTH_REQUIRED":
419
447
  case "reauth_required":
420
448
  return 401;
449
+ // Unprovisioned declared secret: a deployment/config defect, never an
450
+ // upstream failure — explicit 400 (was only reached via fallthrough).
451
+ case MISSING_SECRET_CODE:
452
+ return 400;
421
453
  case "NOT_FOUND":
422
454
  case "not_found":
423
455
  case "NO_DATA":
@@ -445,16 +477,14 @@ function extractRequestId(raw) {
445
477
  return typeof value === "string" ? value : undefined;
446
478
  }
447
479
  function logProviderError(logger, provider, kind, route, requestId, error, status, cost) {
448
- const code = error instanceof ProviderError
480
+ const code = isProviderError(error)
449
481
  ? (error.code ?? "provider_error")
450
482
  : error instanceof z.ZodError
451
483
  ? "invalid_request"
452
484
  : "internal_error";
453
485
  const errorClass = error instanceof Error ? error.name : typeof error;
454
486
  const message = error instanceof Error ? error.message : String(error);
455
- const details = error instanceof ProviderError
456
- ? providerObservabilityDetails(error)
457
- : undefined;
487
+ const details = isProviderError(error) ? providerObservabilityDetails(error) : undefined;
458
488
  const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
459
489
  emit({
460
490
  level: status >= 500 ? "error" : "warn",
@@ -468,7 +498,7 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
468
498
  code,
469
499
  errorClass,
470
500
  message,
471
- ...(error instanceof TransportError && error.upstreamStatus
501
+ ...(isTransportError(error) && error.upstreamStatus
472
502
  ? { upstreamStatus: error.upstreamStatus }
473
503
  : {}),
474
504
  ...(details
@@ -591,8 +621,7 @@ async function validateSseEvent(operation, event) {
591
621
  const transport = getSseTransport(operation);
592
622
  const schema = transport?.events?.[event.event];
593
623
  if (!schema) {
594
- if (event.event === APIFUSE_STREAM_ERROR_EVENT ||
595
- event.event === APIFUSE_STREAM_DONE_EVENT) {
624
+ if (event.event === APIFUSE_STREAM_ERROR_EVENT || event.event === APIFUSE_STREAM_DONE_EVENT) {
596
625
  return event;
597
626
  }
598
627
  throw new ProviderError(`SSE event "${event.event}" is not declared in operation transport.events.`, {
@@ -711,8 +740,7 @@ function enforceStreamChunkLimit(body, maxChunkBytes) {
711
740
  }
712
741
  function toStreamingResponse(operation, result, cleanup, requestId) {
713
742
  const transport = operation.transport?.kind ?? "json";
714
- if (transport === "sse" &&
715
- (result instanceof Response || result instanceof ReadableStream)) {
743
+ if (transport === "sse" && (result instanceof Response || result instanceof ReadableStream)) {
716
744
  void cleanup();
717
745
  throw new ProviderError("SSE operations must return an AsyncIterable of typed stream.event(...) values.", {
718
746
  code: "SSE_RESULT_UNSUPPORTED",
@@ -723,9 +751,7 @@ function toStreamingResponse(operation, result, cleanup, requestId) {
723
751
  }
724
752
  if (result instanceof Response) {
725
753
  const httpTransport = getHttpStreamTransport(operation);
726
- if (httpTransport &&
727
- result.body &&
728
- httpTransport?.maxChunkBytes !== undefined) {
754
+ if (httpTransport && result.body && httpTransport?.maxChunkBytes !== undefined) {
729
755
  return responseWithCleanup(new Response(enforceStreamChunkLimit(result.body, httpTransport.maxChunkBytes), {
730
756
  headers: result.headers,
731
757
  status: result.status,
@@ -744,8 +770,7 @@ function toStreamingResponse(operation, result, cleanup, requestId) {
744
770
  ? { "Content-Type": "text/event-stream; charset=utf-8" }
745
771
  : {
746
772
  "Content-Type": operation.transport?.kind === "http-stream"
747
- ? (operation.transport.contentType ??
748
- "application/octet-stream")
773
+ ? (operation.transport.contentType ?? "application/octet-stream")
749
774
  : "application/octet-stream",
750
775
  },
751
776
  }), cleanup);
@@ -763,9 +788,7 @@ function getSseTransport(operation) {
763
788
  return operation.transport?.kind === "sse" ? operation.transport : undefined;
764
789
  }
765
790
  function getHttpStreamTransport(operation) {
766
- return operation.transport?.kind === "http-stream"
767
- ? operation.transport
768
- : undefined;
791
+ return operation.transport?.kind === "http-stream" ? operation.transport : undefined;
769
792
  }
770
793
  function toAuthFlowResponse(result, contextPatch) {
771
794
  if (result instanceof Response) {
@@ -793,7 +816,7 @@ function isAuthFlowLocale(value) {
793
816
  return value === "en" || value === "ko" || value === "ja";
794
817
  }
795
818
  function isAuthTurn(value) {
796
- return (!!value && typeof value === "object" && "kind" in value && "turnId" in value);
819
+ return !!value && typeof value === "object" && "kind" in value && "turnId" in value;
797
820
  }
798
821
  function loadAuthFlowLocaleCatalogs(provider) {
799
822
  for (const providerDir of [
@@ -890,8 +913,16 @@ async function handleAuthFlow(provider, request, route, options = {}, signal) {
890
913
  code: "AUTH_FLOW_NOT_CONFIGURED",
891
914
  });
892
915
  }
916
+ // Same SDK-owned gate as executeOperation: OAuth/credentials ceremonies
917
+ // depend on declared secrets (client ids/secrets), so fail structured before
918
+ // any flow code runs instead of at whatever point the ceremony first reads
919
+ // the env. `abort` stays exempt: a user must always be able to cancel a
920
+ // stranded flow even when provisioning is broken.
893
921
  const { context, getPatch } = createAuthFlowContext(provider, request, options, signal);
894
922
  try {
923
+ if (route !== "abort") {
924
+ assertRequiredSecretsPresent(provider, context.env);
925
+ }
895
926
  const result = route === "start"
896
927
  ? await flow.start(context)
897
928
  : route === "continue"
@@ -938,6 +969,20 @@ export function createServerApp(provider, options = {}) {
938
969
  providerId: provider.id,
939
970
  allowMemoryFallback: options.allowMemoryStateFallback === true,
940
971
  });
972
+ // Boot-time visibility for unprovisioned declared secrets: emit a structured
973
+ // warn so deploy tooling/alerting sees the gap the moment the pod boots,
974
+ // instead of discovering it request-by-request. Deliberately log-only — a
975
+ // boot crash would trade a structured MISSING_SECRET signal for
976
+ // CrashLoopBackOff. Requests still fail closed via the executeOperation gate.
977
+ const missingSecretsAtBoot = listMissingRequiredSecrets(provider, createEnvContext(provider.secrets?.map((secret) => secret.name)));
978
+ if (missingSecretsAtBoot.length > 0) {
979
+ logger({
980
+ level: "warn",
981
+ event: "provider_secrets_missing",
982
+ providerId: provider.id,
983
+ missingSecrets: missingSecretsAtBoot,
984
+ });
985
+ }
941
986
  app.notFound((c) => c.json({
942
987
  error: {
943
988
  code: "not_found",
@@ -1124,6 +1169,7 @@ export async function serve(provider, options = {}) {
1124
1169
  const selfTestApp = createSelfTestApp(provider, {
1125
1170
  secrets: selfTestSecrets,
1126
1171
  invoke: createSelfTestInvoke(app),
1172
+ authFlow: createSelfTestAuthFlowInvoke(app),
1127
1173
  });
1128
1174
  bunRuntime.serve({
1129
1175
  port: options.selfTestPort ?? resolveSelfTestPort(),
@@ -1,15 +1,15 @@
1
1
  import { z } from "zod";
2
2
  export declare const ConnectionModeSchema: z.ZodEnum<{
3
- credentials: "credentials";
4
3
  none: "none";
4
+ credentials: "credentials";
5
5
  oauth2: "oauth2";
6
6
  "platform-managed": "platform-managed";
7
7
  }>;
8
8
  export declare const OperationConnectionSchema: z.ZodObject<{
9
9
  id: z.ZodString;
10
10
  mode: z.ZodEnum<{
11
- credentials: "credentials";
12
11
  none: "none";
12
+ credentials: "credentials";
13
13
  oauth2: "oauth2";
14
14
  "platform-managed": "platform-managed";
15
15
  }>;
@@ -21,11 +21,12 @@ export declare const OperationConnectionSchema: z.ZodObject<{
21
21
  export declare const OperationRequestSchema: z.ZodObject<{
22
22
  requestId: z.ZodString;
23
23
  input: z.ZodRecord<z.ZodString, z.ZodUnknown>;
24
+ connectionId: z.ZodOptional<z.ZodString>;
24
25
  connection: z.ZodOptional<z.ZodObject<{
25
26
  id: z.ZodString;
26
27
  mode: z.ZodEnum<{
27
- credentials: "credentials";
28
28
  none: "none";
29
+ credentials: "credentials";
29
30
  oauth2: "oauth2";
30
31
  "platform-managed": "platform-managed";
31
32
  }>;
@@ -54,21 +55,21 @@ export declare const OperationSuccessResponseSchema: z.ZodObject<{
54
55
  stale: z.ZodBoolean;
55
56
  keys: z.ZodArray<z.ZodString>;
56
57
  source: z.ZodOptional<z.ZodEnum<{
57
- loader: "loader";
58
- memory: "memory";
59
58
  mixed: "mixed";
60
59
  redis: "redis";
60
+ memory: "memory";
61
+ loader: "loader";
61
62
  }>>;
62
63
  }, z.core.$strip>>;
63
64
  retry: z.ZodOptional<z.ZodObject<{
64
65
  attempts: z.ZodNumber;
65
66
  retries: z.ZodNumber;
66
67
  preset: z.ZodOptional<z.ZodEnum<{
67
- aggressive_read: "aggressive_read";
68
68
  off: "off";
69
- rate_limit_aware: "rate_limit_aware";
70
- safe_read: "safe_read";
71
69
  transport_transient: "transport_transient";
70
+ safe_read: "safe_read";
71
+ aggressive_read: "aggressive_read";
72
+ rate_limit_aware: "rate_limit_aware";
72
73
  }>>;
73
74
  transport: z.ZodEnum<{
74
75
  native: "native";
@@ -100,8 +101,8 @@ export declare const AuthFlowRequestSchema: z.ZodObject<{
100
101
  connection: z.ZodOptional<z.ZodObject<{
101
102
  id: z.ZodString;
102
103
  mode: z.ZodEnum<{
103
- credentials: "credentials";
104
104
  none: "none";
105
+ credentials: "credentials";
105
106
  oauth2: "oauth2";
106
107
  "platform-managed": "platform-managed";
107
108
  }>;
@@ -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,
@@ -17,6 +12,7 @@ export const OperationConnectionSchema = z.object({
17
12
  export const OperationRequestSchema = z.object({
18
13
  requestId: z.string(),
19
14
  input: z.record(z.string(), z.unknown()),
15
+ connectionId: z.string().optional(),
20
16
  connection: OperationConnectionSchema.optional(),
21
17
  headers: z.record(z.string(), z.string()).optional(),
22
18
  trace: z.record(z.string(), z.string()).optional(),
@@ -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__`;