@apifuse/provider-sdk 2.1.0-beta.9 → 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 (263) hide show
  1. package/AUTHORING.md +240 -0
  2. package/CHANGELOG.md +93 -0
  3. package/README.md +26 -10
  4. package/SUBMISSION.md +11 -12
  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 +36 -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 +2177 -353
  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/auth-turn.v1.schema.json +89 -0
  20. package/dist/auth-turn/fixtures/invalid/empty-kind.json +4 -0
  21. package/dist/auth-turn/fixtures/invalid/expires-at-not-string.json +5 -0
  22. package/dist/auth-turn/fixtures/invalid/missing-kind.json +3 -0
  23. package/dist/auth-turn/fixtures/invalid/missing-turn-id.json +3 -0
  24. package/dist/auth-turn/fixtures/invalid/timing-unknown-field.json +7 -0
  25. package/dist/auth-turn/fixtures/invalid/turn-id-snake-case.json +4 -0
  26. package/dist/auth-turn/fixtures/invalid/unknown-top-level-field.json +5 -0
  27. package/dist/auth-turn/fixtures/valid/abort.json +8 -0
  28. package/dist/auth-turn/fixtures/valid/challenge.json +17 -0
  29. package/dist/auth-turn/fixtures/valid/complete.json +13 -0
  30. package/dist/auth-turn/fixtures/valid/form.json +14 -0
  31. package/dist/auth-turn/fixtures/valid/message.json +13 -0
  32. package/dist/auth-turn/fixtures/valid/multi_choice.json +15 -0
  33. package/dist/auth-turn/fixtures/valid/pending.json +5 -0
  34. package/dist/auth-turn/fixtures/valid/poll.json +9 -0
  35. package/dist/auth-turn/fixtures/valid/redirect.json +16 -0
  36. package/dist/auth-turn/fixtures/valid/retry.json +8 -0
  37. package/dist/auth-turn/fixtures/valid/unknown-kind.json +7 -0
  38. package/dist/auth-turn/index.d.ts +195 -0
  39. package/dist/auth-turn/index.js +133 -0
  40. package/dist/auth.d.ts +76 -0
  41. package/dist/auth.js +427 -0
  42. package/dist/ceremonies/index.d.ts +1 -1
  43. package/dist/ceremonies/index.js +14 -48
  44. package/dist/cli/commands.d.ts +1 -1
  45. package/dist/cli/commands.js +8 -0
  46. package/dist/cli/create.d.ts +3 -0
  47. package/dist/cli/create.js +47 -33
  48. package/dist/cli/prompt-assets.d.ts +80 -0
  49. package/dist/cli/prompt-assets.js +743 -0
  50. package/dist/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl +58 -0
  51. package/dist/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl +65 -0
  52. package/dist/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl +57 -0
  53. package/dist/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl +52 -0
  54. package/dist/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl +45 -0
  55. package/dist/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl +13 -0
  56. package/dist/cli/templates/provider/.dockerignore.tpl +22 -0
  57. package/dist/cli/templates/provider/.gitignore.tpl +22 -0
  58. package/dist/cli/templates/provider/AGENTS.md.tpl +96 -0
  59. package/dist/cli/templates/provider/Dockerfile.tpl +7 -0
  60. package/dist/cli/templates/provider/README.md.tpl +163 -0
  61. package/dist/cli/templates/provider/dev.ts.tpl +5 -0
  62. package/dist/cli/templates/provider/domain/README.md.tpl +3 -0
  63. package/dist/cli/templates/provider/index.test.ts.tpl +13 -0
  64. package/dist/cli/templates/provider/index.ts.tpl +15 -0
  65. package/dist/cli/templates/provider/mappers/README.md.tpl +3 -0
  66. package/dist/cli/templates/provider/meta.ts.tpl +7 -0
  67. package/dist/cli/templates/provider/operations/index.ts.tpl +5 -0
  68. package/dist/cli/templates/provider/operations/ping.ts.tpl +24 -0
  69. package/dist/cli/templates/provider/schemas/ping.ts.tpl +24 -0
  70. package/dist/cli/templates/provider/start.ts.tpl +5 -0
  71. package/dist/cli/templates/provider/upstream/README.md.tpl +3 -0
  72. package/dist/config/loader.d.ts +149 -8
  73. package/dist/config/loader.js +378 -89
  74. package/dist/contract-serialization.d.ts +2 -2
  75. package/dist/contract-serialization.js +3 -6
  76. package/dist/contract-types.d.ts +2 -2
  77. package/dist/contract.d.ts +3 -3
  78. package/dist/contract.js +5 -6
  79. package/dist/define.d.ts +13 -1
  80. package/dist/define.js +245 -178
  81. package/dist/dev.d.ts +1 -1
  82. package/dist/dev.js +1 -1
  83. package/dist/errors.d.ts +4 -1
  84. package/dist/errors.js +48 -0
  85. package/dist/i18n/catalog.d.ts +2 -2
  86. package/dist/i18n/catalog.js +4 -10
  87. package/dist/i18n/index.d.ts +2 -2
  88. package/dist/i18n/index.js +2 -2
  89. package/dist/i18n/keys.d.ts +2 -2
  90. package/dist/index.d.ts +44 -41
  91. package/dist/index.js +39 -36
  92. package/dist/lint.d.ts +1 -0
  93. package/dist/lint.js +35 -15
  94. package/dist/provider.d.ts +11 -9
  95. package/dist/provider.js +9 -8
  96. package/dist/public-schema-field-lint.d.ts +1 -1
  97. package/dist/recipes/gov-api.js +1 -1
  98. package/dist/runtime/auth-flow.d.ts +1 -1
  99. package/dist/runtime/auth-flow.js +4 -2
  100. package/dist/runtime/browser.d.ts +1 -1
  101. package/dist/runtime/browser.js +214 -25
  102. package/dist/runtime/cache.d.ts +1 -1
  103. package/dist/runtime/cache.js +4 -8
  104. package/dist/runtime/choice.d.ts +1 -1
  105. package/dist/runtime/choice.js +31 -35
  106. package/dist/runtime/credential.d.ts +1 -1
  107. package/dist/runtime/credential.js +1 -1
  108. package/dist/runtime/env.d.ts +1 -1
  109. package/dist/runtime/executor.d.ts +1 -1
  110. package/dist/runtime/executor.js +15 -3
  111. package/dist/runtime/http.d.ts +2 -2
  112. package/dist/runtime/http.js +160 -344
  113. package/dist/runtime/insights.d.ts +1 -1
  114. package/dist/runtime/insights.js +6 -13
  115. package/dist/runtime/instrumentation.d.ts +2 -2
  116. package/dist/runtime/instrumentation.js +56 -19
  117. package/dist/runtime/keyring.js +1 -1
  118. package/dist/runtime/namespace.js +1 -1
  119. package/dist/runtime/otlp.d.ts +1 -1
  120. package/dist/runtime/perf.d.ts +1 -1
  121. package/dist/runtime/provider.d.ts +1 -1
  122. package/dist/runtime/provider.js +1 -2
  123. package/dist/runtime/proxy-errors.d.ts +1 -1
  124. package/dist/runtime/proxy-errors.js +9 -7
  125. package/dist/runtime/proxy-nodemaven.d.ts +35 -0
  126. package/dist/runtime/proxy-nodemaven.js +128 -0
  127. package/dist/runtime/proxy-retry-policy.d.ts +40 -0
  128. package/dist/runtime/proxy-retry-policy.js +326 -0
  129. package/dist/runtime/proxy-telemetry.d.ts +2 -1
  130. package/dist/runtime/proxy-telemetry.js +55 -52
  131. package/dist/runtime/redis.d.ts +1 -1
  132. package/dist/runtime/redis.js +2 -4
  133. package/dist/runtime/request-options.d.ts +1 -1
  134. package/dist/runtime/secrets.d.ts +27 -0
  135. package/dist/runtime/secrets.js +51 -0
  136. package/dist/runtime/state.d.ts +2 -2
  137. package/dist/runtime/state.js +15 -4
  138. package/dist/runtime/stealth.d.ts +7 -4
  139. package/dist/runtime/stealth.js +257 -215
  140. package/dist/runtime/stt.d.ts +1 -1
  141. package/dist/runtime/stt.js +11 -15
  142. package/dist/runtime/trace.d.ts +2 -2
  143. package/dist/runtime/trace.js +2 -4
  144. package/dist/runtime/waterfall.d.ts +1 -1
  145. package/dist/schema.d.ts +1 -1
  146. package/dist/schema.js +7 -15
  147. package/dist/serve.d.ts +1 -1
  148. package/dist/serve.js +1 -1
  149. package/dist/server/index.d.ts +7 -3
  150. package/dist/server/index.js +6 -2
  151. package/dist/server/self-test-input-tokens.d.ts +1 -0
  152. package/dist/server/self-test-input-tokens.js +37 -0
  153. package/dist/server/self-test-redaction.d.ts +20 -0
  154. package/dist/server/self-test-redaction.js +70 -0
  155. package/dist/server/self-test-token.d.ts +30 -0
  156. package/dist/server/self-test-token.js +50 -0
  157. package/dist/server/self-test.d.ts +199 -0
  158. package/dist/server/self-test.js +1113 -0
  159. package/dist/server/serve.d.ts +14 -3
  160. package/dist/server/serve.js +135 -64
  161. package/dist/server/types.d.ts +10 -9
  162. package/dist/server/types.js +3 -7
  163. package/dist/stealth/profiles.d.ts +1 -1
  164. package/dist/stealth/profiles.js +5 -14
  165. package/dist/stream.d.ts +1 -1
  166. package/dist/testing/index.d.ts +2 -2
  167. package/dist/testing/index.js +2 -2
  168. package/dist/testing/run.d.ts +1 -1
  169. package/dist/testing/run.js +12 -15
  170. package/dist/types.d.ts +237 -1
  171. package/dist/user-input.d.ts +30 -0
  172. package/dist/user-input.js +66 -0
  173. package/package.json +16 -5
  174. package/src/auth-turn/auth-turn.v1.schema.json +89 -0
  175. package/src/auth-turn/fixtures/invalid/empty-kind.json +4 -0
  176. package/src/auth-turn/fixtures/invalid/expires-at-not-string.json +5 -0
  177. package/src/auth-turn/fixtures/invalid/missing-kind.json +3 -0
  178. package/src/auth-turn/fixtures/invalid/missing-turn-id.json +3 -0
  179. package/src/auth-turn/fixtures/invalid/timing-unknown-field.json +7 -0
  180. package/src/auth-turn/fixtures/invalid/turn-id-snake-case.json +4 -0
  181. package/src/auth-turn/fixtures/invalid/unknown-top-level-field.json +5 -0
  182. package/src/auth-turn/fixtures/valid/abort.json +8 -0
  183. package/src/auth-turn/fixtures/valid/challenge.json +17 -0
  184. package/src/auth-turn/fixtures/valid/complete.json +13 -0
  185. package/src/auth-turn/fixtures/valid/form.json +14 -0
  186. package/src/auth-turn/fixtures/valid/message.json +13 -0
  187. package/src/auth-turn/fixtures/valid/multi_choice.json +15 -0
  188. package/src/auth-turn/fixtures/valid/pending.json +5 -0
  189. package/src/auth-turn/fixtures/valid/poll.json +9 -0
  190. package/src/auth-turn/fixtures/valid/redirect.json +16 -0
  191. package/src/auth-turn/fixtures/valid/retry.json +8 -0
  192. package/src/auth-turn/fixtures/valid/unknown-kind.json +7 -0
  193. package/src/auth-turn/index.ts +177 -0
  194. package/src/auth.ts +728 -0
  195. package/src/ceremonies/index.ts +33 -121
  196. package/src/cli/commands.ts +10 -0
  197. package/src/cli/create.ts +69 -99
  198. package/src/cli/prompt-assets.ts +865 -0
  199. package/src/cli/templates/provider/.agents/skills/fixtures-and-recording/SKILL.md.tpl +58 -0
  200. package/src/cli/templates/provider/.agents/skills/health-checks-and-fail-closed/SKILL.md.tpl +65 -0
  201. package/src/cli/templates/provider/.agents/skills/normalization-standards/SKILL.md.tpl +57 -0
  202. package/src/cli/templates/provider/.agents/skills/pagination-and-counts/SKILL.md.tpl +52 -0
  203. package/src/cli/templates/provider/.agents/skills/upstream-contract-verification/SKILL.md.tpl +45 -0
  204. package/src/cli/templates/provider/.agents/skills/upstream-notes/README.md.tpl +13 -0
  205. package/src/cli/templates/provider/AGENTS.md.tpl +96 -0
  206. package/src/cli/templates/provider/README.md.tpl +7 -4
  207. package/src/config/loader.ts +543 -208
  208. package/src/contract-serialization.ts +5 -11
  209. package/src/contract-types.ts +2 -2
  210. package/src/contract.ts +13 -28
  211. package/src/define.ts +397 -528
  212. package/src/dev.ts +4 -9
  213. package/src/errors.ts +58 -5
  214. package/src/i18n/catalog.ts +10 -32
  215. package/src/i18n/index.ts +2 -2
  216. package/src/i18n/keys.ts +5 -11
  217. package/src/index.ts +64 -41
  218. package/src/lint.ts +122 -159
  219. package/src/provider.ts +40 -9
  220. package/src/public-schema-field-lint.ts +7 -33
  221. package/src/recipes/gov-api.ts +2 -5
  222. package/src/runtime/auth-flow.ts +6 -6
  223. package/src/runtime/browser.ts +320 -151
  224. package/src/runtime/cache.ts +20 -67
  225. package/src/runtime/choice.ts +79 -132
  226. package/src/runtime/credential.ts +2 -2
  227. package/src/runtime/env.ts +1 -1
  228. package/src/runtime/executor.ts +23 -20
  229. package/src/runtime/http.ts +216 -539
  230. package/src/runtime/insights.ts +15 -53
  231. package/src/runtime/instrumentation.ts +78 -65
  232. package/src/runtime/keyring.ts +7 -19
  233. package/src/runtime/namespace.ts +2 -7
  234. package/src/runtime/otlp.ts +12 -23
  235. package/src/runtime/perf.ts +1 -1
  236. package/src/runtime/provider.ts +4 -9
  237. package/src/runtime/proxy-errors.ts +29 -42
  238. package/src/runtime/proxy-nodemaven.ts +178 -0
  239. package/src/runtime/proxy-retry-policy.ts +469 -0
  240. package/src/runtime/proxy-telemetry.ts +79 -77
  241. package/src/runtime/redis.ts +4 -12
  242. package/src/runtime/request-options.ts +4 -13
  243. package/src/runtime/secrets.ts +64 -0
  244. package/src/runtime/state.ts +41 -110
  245. package/src/runtime/stealth.ts +331 -369
  246. package/src/runtime/stt.ts +38 -94
  247. package/src/runtime/trace.ts +14 -44
  248. package/src/runtime/waterfall.ts +5 -18
  249. package/src/schema.ts +23 -84
  250. package/src/serve.ts +1 -1
  251. package/src/server/index.ts +44 -3
  252. package/src/server/self-test-input-tokens.ts +46 -0
  253. package/src/server/self-test-redaction.ts +97 -0
  254. package/src/server/self-test-token.ts +70 -0
  255. package/src/server/self-test.ts +1450 -0
  256. package/src/server/serve.ts +206 -216
  257. package/src/server/types.ts +7 -19
  258. package/src/stealth/profiles.ts +10 -26
  259. package/src/stream.ts +8 -19
  260. package/src/testing/index.ts +2 -2
  261. package/src/testing/run.ts +24 -64
  262. package/src/types.ts +274 -1
  263. package/src/user-input.ts +118 -0
package/src/lint.ts CHANGED
@@ -1,17 +1,9 @@
1
1
  import type { ZodType } from "zod";
2
2
 
3
- import { lintPublicSchemaFieldNames } from "./public-schema-field-lint";
4
- import {
5
- APIFUSE_DESCRIPTION_KEY_META_KEY,
6
- APIFUSE_SENSITIVE_META_KEY,
7
- } from "./schema";
8
-
9
- type AuthModeLike =
10
- | "none"
11
- | "platform-managed"
12
- | "credentials"
13
- | "oauth2"
14
- | "api-key";
3
+ import { lintPublicSchemaFieldNames } from "./public-schema-field-lint.js";
4
+ import { APIFUSE_DESCRIPTION_KEY_META_KEY, APIFUSE_SENSITIVE_META_KEY } from "./schema.js";
5
+
6
+ type AuthModeLike = "none" | "platform-managed" | "credentials" | "oauth2" | "api-key";
15
7
 
16
8
  type ProviderAuthLike = {
17
9
  mode?: AuthModeLike;
@@ -22,8 +14,12 @@ type ProviderAuthLike = {
22
14
  abort?: unknown;
23
15
  refresh?: unknown;
24
16
  };
17
+ exchange?: unknown;
25
18
  };
26
19
 
20
+ const AUTH_OPERATION_ID_PATTERN =
21
+ /^(?:auth[-_])?(?:login|exchange|continue|refresh|callback)(?:[-_]|$)/i;
22
+
27
23
  type ProviderContractMetaLike = {
28
24
  publicSchemaFieldNames?: "normalized";
29
25
  };
@@ -127,34 +123,29 @@ function lintReviewed(
127
123
  ];
128
124
  }
129
125
 
126
+ function isProviderAuthLike(value: unknown): value is ProviderAuthLike {
127
+ return !!value && typeof value === "object" && !Array.isArray(value);
128
+ }
129
+
130
130
  function hasReusableSecretKeys(keys: readonly string[] | undefined): boolean {
131
131
  if (!keys) {
132
132
  return false;
133
133
  }
134
134
 
135
135
  return keys.some((key) =>
136
- /(access_token|refresh_token|password|secret|cookie|session|token|api[_-]?key)/i.test(
137
- key,
138
- ),
136
+ /(access_token|refresh_token|password|secret|cookie|session|token|api[_-]?key)/i.test(key),
139
137
  );
140
138
  }
141
139
 
142
- function hasReusableReloginSecretKeys(
143
- keys: readonly string[] | undefined,
144
- ): boolean {
140
+ function hasReusableReloginSecretKeys(keys: readonly string[] | undefined): boolean {
145
141
  if (!keys) {
146
142
  return false;
147
143
  }
148
144
 
149
- return keys.some((key) =>
150
- /(password|passcode|secret|cookie|session)/i.test(key),
151
- );
145
+ return keys.some((key) => /(password|passcode|secret|cookie|session)/i.test(key));
152
146
  }
153
147
 
154
- function getAuthFlowSource(provider: {
155
- auth?: ProviderAuthLike;
156
- authFlowSource?: string;
157
- }): string {
148
+ function getAuthFlowSource(provider: { auth?: ProviderAuthLike; authFlowSource?: string }): string {
158
149
  if (provider.authFlowSource) {
159
150
  return provider.authFlowSource;
160
151
  }
@@ -168,10 +159,7 @@ function getAuthFlowSource(provider: {
168
159
  ];
169
160
 
170
161
  return parts
171
- .filter(
172
- (part): part is (...args: unknown[]) => unknown =>
173
- typeof part === "function",
174
- )
162
+ .filter((part): part is (...args: unknown[]) => unknown => typeof part === "function")
175
163
  .map((part) => part.toString())
176
164
  .join("\n");
177
165
  }
@@ -215,6 +203,15 @@ function lintAuthModel(provider: {
215
203
  });
216
204
  }
217
205
 
206
+ if (isProviderAuthLike(provider.auth) && "exchange" in provider.auth) {
207
+ diagnostics.push({
208
+ rule: "auth-exchange-unsupported",
209
+ level: "error",
210
+ field: "auth.exchange",
211
+ message: `${providerLabel} must not define auth.exchange. The Provider SDK has one auth interface: auth.flow. Gateway only calls auth.flow.start/continue/poll/abort/refresh and persists complete turn data.credential as-is; put login/token/session exchange inside auth.flow.continue.`,
212
+ });
213
+ }
214
+
218
215
  if (authMode === "credentials" && credentialKeys.length === 0) {
219
216
  diagnostics.push({
220
217
  rule: "credential-keys-required-when-credentials-mode",
@@ -226,8 +223,7 @@ function lintAuthModel(provider: {
226
223
 
227
224
  if (
228
225
  hasReusableSecretKeys(credentialKeys) &&
229
- (!provider.credential?.storesReusableSecret ||
230
- !provider.credential.justification)
226
+ (!provider.credential?.storesReusableSecret || !provider.credential.justification)
231
227
  ) {
232
228
  diagnostics.push({
233
229
  rule: "credential-reusable-secret",
@@ -240,8 +236,7 @@ function lintAuthModel(provider: {
240
236
  if (
241
237
  typeof provider.auth?.flow?.refresh === "function" &&
242
238
  hasReusableReloginSecretKeys(credentialKeys) &&
243
- (!provider.credential?.storesReusableSecret ||
244
- !provider.credential.justification)
239
+ (!provider.credential?.storesReusableSecret || !provider.credential.justification)
245
240
  ) {
246
241
  diagnostics.push({
247
242
  rule: "auth-refresh-reusable-secret",
@@ -261,10 +256,7 @@ function lintAuthModel(provider: {
261
256
  }
262
257
 
263
258
  const authFlowSource = getAuthFlowSource(provider);
264
- if (
265
- authFlowSource.includes("ctx.context") &&
266
- (provider.context?.keys?.length ?? 0) === 0
267
- ) {
259
+ if (authFlowSource.includes("ctx.context") && (provider.context?.keys?.length ?? 0) === 0) {
268
260
  diagnostics.push({
269
261
  rule: "context-keys-required",
270
262
  level: "warn",
@@ -306,8 +298,7 @@ function isSchemaRecord(value: unknown): value is Record<string, SchemaLike> {
306
298
  }
307
299
 
308
300
  function getObjectShape(schema: SchemaLike): Record<string, SchemaLike> {
309
- const rawShape =
310
- typeof schema.shape === "function" ? schema.shape() : schema.shape;
301
+ const rawShape = typeof schema.shape === "function" ? schema.shape() : schema.shape;
311
302
  if (isSchemaRecord(rawShape)) {
312
303
  return rawShape;
313
304
  }
@@ -327,9 +318,7 @@ function getObjectShape(schema: SchemaLike): Record<string, SchemaLike> {
327
318
  return {};
328
319
  }
329
320
 
330
- function getChildSchemas(
331
- schema: SchemaLike,
332
- ): Array<{ key: string; schema: SchemaLike }> {
321
+ function getChildSchemas(schema: SchemaLike): Array<{ key: string; schema: SchemaLike }> {
333
322
  const seen = new Map<string, SchemaLike>();
334
323
  const def = getSchemaDef(schema);
335
324
 
@@ -433,10 +422,7 @@ function getSchemaMetadata(schema: SchemaLike): Record<string, unknown> {
433
422
  }
434
423
 
435
424
  function getSchemaDescriptionKey(schema: SchemaLike): string | undefined {
436
- const value = Reflect.get(
437
- getSchemaMetadata(schema),
438
- APIFUSE_DESCRIPTION_KEY_META_KEY,
439
- );
425
+ const value = Reflect.get(getSchemaMetadata(schema), APIFUSE_DESCRIPTION_KEY_META_KEY);
440
426
  return typeof value === "string" && value.length > 0 ? value : undefined;
441
427
  }
442
428
 
@@ -580,22 +566,14 @@ function collectSchemaDescriptionKeyDiagnostics(
580
566
  ? `${currentPath}[]`
581
567
  : `${currentPath}.${child.key}`;
582
568
  diagnostics.push(
583
- ...collectSchemaDescriptionKeyDiagnostics(
584
- child.schema,
585
- childPath,
586
- seen,
587
- !isStructuralNode,
588
- ),
569
+ ...collectSchemaDescriptionKeyDiagnostics(child.schema, childPath, seen, !isStructuralNode),
589
570
  );
590
571
  }
591
572
 
592
573
  return diagnostics;
593
574
  }
594
575
 
595
- function isComplexSchema(
596
- schema: unknown,
597
- seen = new Set<SchemaLike>(),
598
- ): boolean {
576
+ function isComplexSchema(schema: unknown, seen = new Set<SchemaLike>()): boolean {
599
577
  if (!isSchema(schema) || seen.has(schema)) {
600
578
  return false;
601
579
  }
@@ -607,10 +585,7 @@ function isComplexSchema(
607
585
  return childChildren.length > 0;
608
586
  });
609
587
 
610
- return (
611
- hasNestedComposite ||
612
- children.some(({ schema: child }) => isComplexSchema(child, seen))
613
- );
588
+ return hasNestedComposite || children.some(({ schema: child }) => isComplexSchema(child, seen));
614
589
  }
615
590
 
616
591
  function hasBidirectionalFixtures(fixtures: unknown): boolean {
@@ -621,16 +596,11 @@ function hasBidirectionalFixtures(fixtures: unknown): boolean {
621
596
  return "request" in fixtures && "response" in fixtures;
622
597
  }
623
598
 
624
- function getOperationSource(operation: {
625
- handler?: unknown;
626
- source?: string;
627
- }): string {
599
+ function getOperationSource(operation: { handler?: unknown; source?: string }): string {
628
600
  if (operation.source) {
629
601
  return operation.source;
630
602
  }
631
- return typeof operation.handler === "function"
632
- ? operation.handler.toString()
633
- : "";
603
+ return typeof operation.handler === "function" ? operation.handler.toString() : "";
634
604
  }
635
605
 
636
606
  function lintStealthTransportUsage(provider: {
@@ -643,22 +613,20 @@ function lintStealthTransportUsage(provider: {
643
613
  }
644
614
 
645
615
  const providerLabel = provider.id ? `Provider "${provider.id}"` : "Provider";
646
- return Object.entries(provider.operations).flatMap(
647
- ([operationKey, operation]) => {
648
- const source = getOperationSource(operation);
649
- if (!/\bctx\.stealth\b/.test(source)) {
650
- return [];
651
- }
652
- return [
653
- {
654
- rule: "stealth-config-required",
655
- level: "error" as const,
656
- field: `operations.${operationKey}`,
657
- message: `${providerLabel} operation "${operationKey}" uses ctx.stealth but provider.stealth is not declared.`,
658
- },
659
- ];
660
- },
661
- );
616
+ return Object.entries(provider.operations).flatMap(([operationKey, operation]) => {
617
+ const source = getOperationSource(operation);
618
+ if (!/\bctx\.stealth\b/.test(source)) {
619
+ return [];
620
+ }
621
+ return [
622
+ {
623
+ rule: "stealth-config-required",
624
+ level: "error" as const,
625
+ field: `operations.${operationKey}`,
626
+ message: `${providerLabel} operation "${operationKey}" uses ctx.stealth but provider.stealth is not declared.`,
627
+ },
628
+ ];
629
+ });
662
630
  }
663
631
 
664
632
  function lintCredentialWriteUsage(provider: {
@@ -668,24 +636,22 @@ function lintCredentialWriteUsage(provider: {
668
636
  return [];
669
637
  }
670
638
 
671
- return Object.entries(provider.operations).flatMap(
672
- ([operationKey, operation]) => {
673
- const source = getOperationSource(operation);
674
- if (!/\bctx\.credential\.(?:set|setMany)\s*\(/.test(source)) {
675
- return [];
676
- }
639
+ return Object.entries(provider.operations).flatMap(([operationKey, operation]) => {
640
+ const source = getOperationSource(operation);
641
+ if (!/\bctx\.credential\.(?:set|setMany)\s*\(/.test(source)) {
642
+ return [];
643
+ }
677
644
 
678
- return [
679
- {
680
- rule: "ctx-credential-write-forbidden-in-handler",
681
- level: "error" as const,
682
- field: `operations.${operationKey}.handler`,
683
- message:
684
- "Operation handlers must not mutate credentials; return refreshed credentials from auth.flow.refresh instead.",
685
- },
686
- ];
687
- },
688
- );
645
+ return [
646
+ {
647
+ rule: "ctx-credential-write-forbidden-in-handler",
648
+ level: "error" as const,
649
+ field: `operations.${operationKey}.handler`,
650
+ message:
651
+ "Operation handlers must not mutate credentials; return refreshed credentials from auth.flow.refresh instead.",
652
+ },
653
+ ];
654
+ });
689
655
  }
690
656
 
691
657
  function lintPlaywrightDirectImports(provider: {
@@ -707,9 +673,7 @@ function lintPlaywrightDirectImports(provider: {
707
673
  });
708
674
  }
709
675
 
710
- for (const [filePath, source] of Object.entries(
711
- provider.providerSourceFiles ?? {},
712
- )) {
676
+ for (const [filePath, source] of Object.entries(provider.providerSourceFiles ?? {})) {
713
677
  if (!importPattern.test(source)) {
714
678
  continue;
715
679
  }
@@ -797,15 +761,11 @@ function lintSelfHostedBrowserPatterns(
797
761
  sources.push({ field: "auth.flow", source: provider.authFlowSource });
798
762
  }
799
763
 
800
- for (const [filePath, source] of Object.entries(
801
- provider.providerSourceFiles ?? {},
802
- )) {
764
+ for (const [filePath, source] of Object.entries(provider.providerSourceFiles ?? {})) {
803
765
  sources.push({ field: `sourceFiles.${filePath}`, source });
804
766
  }
805
767
 
806
- for (const [operationKey, operation] of Object.entries(
807
- provider.operations ?? {},
808
- )) {
768
+ for (const [operationKey, operation] of Object.entries(provider.operations ?? {})) {
809
769
  const source = getOperationSource(operation);
810
770
  if (source) {
811
771
  sources.push({
@@ -848,16 +808,14 @@ export function lintOperation(op: {
848
808
  }): LintDiagnostic[] {
849
809
  const diagnostics: LintDiagnostic[] = [];
850
810
  const description = op.description ?? "";
851
- const hasDescriptionKey =
852
- typeof op.descriptionKey === "string" && op.descriptionKey.length > 0;
811
+ const hasDescriptionKey = typeof op.descriptionKey === "string" && op.descriptionKey.length > 0;
853
812
 
854
813
  if (description.trim().length > 0 && !hasDescriptionKey) {
855
814
  diagnostics.push({
856
815
  rule: "operation-description-raw-prose",
857
816
  level: "error",
858
817
  field: "description",
859
- message:
860
- "Operation description must use descriptionKey instead of raw static prose.",
818
+ message: "Operation description must use descriptionKey instead of raw static prose.",
861
819
  });
862
820
  }
863
821
 
@@ -875,21 +833,16 @@ export function lintOperation(op: {
875
833
  rule: "operation-when-to-use-raw-prose",
876
834
  level: "error",
877
835
  field: "whenToUse",
878
- message:
879
- "Operation whenToUse must use whenToUseKeys instead of raw static prose.",
836
+ message: "Operation whenToUse must use whenToUseKeys instead of raw static prose.",
880
837
  });
881
838
  }
882
839
 
883
- if (
884
- (op.whenNotToUse?.length ?? 0) > 0 &&
885
- !(op.whenNotToUseKeys?.length ?? 0)
886
- ) {
840
+ if ((op.whenNotToUse?.length ?? 0) > 0 && !(op.whenNotToUseKeys?.length ?? 0)) {
887
841
  diagnostics.push({
888
842
  rule: "operation-when-not-to-use-raw-prose",
889
843
  level: "error",
890
844
  field: "whenNotToUse",
891
- message:
892
- "Operation whenNotToUse must use whenNotToUseKeys instead of raw static prose.",
845
+ message: "Operation whenNotToUse must use whenNotToUseKeys instead of raw static prose.",
893
846
  });
894
847
  }
895
848
 
@@ -925,14 +878,11 @@ export function lintOperation(op: {
925
878
  rule: "complex-input-has-examples",
926
879
  level: "warn",
927
880
  field: "inputExamples",
928
- message:
929
- "Complex input schemas should provide at least 2 input examples.",
881
+ message: "Complex input schemas should provide at least 2 input examples.",
930
882
  });
931
883
  }
932
884
 
933
- for (const field of uniqueFields(
934
- collectUnmarkedSensitiveFields(op.input, "input"),
935
- )) {
885
+ for (const field of uniqueFields(collectUnmarkedSensitiveFields(op.input, "input"))) {
936
886
  diagnostics.push({
937
887
  rule: "sensitive-field-unmarked",
938
888
  level: "warn",
@@ -941,9 +891,7 @@ export function lintOperation(op: {
941
891
  });
942
892
  }
943
893
 
944
- for (const field of uniqueFields(
945
- collectUnmarkedSensitiveFields(op.output, "output"),
946
- )) {
894
+ for (const field of uniqueFields(collectUnmarkedSensitiveFields(op.output, "output"))) {
947
895
  diagnostics.push({
948
896
  rule: "sensitive-field-unmarked",
949
897
  level: "warn",
@@ -1006,41 +954,56 @@ export function lintProvider(
1006
954
  ...lintSelfHostedBrowserPatterns(provider, options),
1007
955
  ];
1008
956
 
957
+ if (provider.operations) {
958
+ const authMode = provider.auth?.mode;
959
+ if (authMode === "credentials" || authMode === "oauth2") {
960
+ for (const operationKey of Object.keys(provider.operations)) {
961
+ if (AUTH_OPERATION_ID_PATTERN.test(operationKey)) {
962
+ diagnostics.push({
963
+ rule: "auth-operation-unsupported",
964
+ level: "error",
965
+ field: `operations.${operationKey}`,
966
+ message: `Provider "${provider.id ?? "unknown"}" operation "${operationKey}" looks like a login/token/session exchange endpoint. Authenticated providers must expose login through the single auth.flow interface because Gateway persists only auth.flow complete turn data.credential as the connection credential. Move this logic into auth.flow.continue instead of a provider operation.`,
967
+ });
968
+ }
969
+ }
970
+ }
971
+ }
972
+
1009
973
  if (!provider.operations) {
1010
974
  return diagnostics;
1011
975
  }
1012
976
 
1013
977
  diagnostics.push(
1014
- ...Object.entries(provider.operations).flatMap(
1015
- ([operationKey, operation]) =>
1016
- [
1017
- ...lintOperation({
1018
- description: operation.description ?? "",
1019
- descriptionKey: operation.descriptionKey,
1020
- whenToUse: operation.whenToUse,
1021
- whenToUseKeys: operation.whenToUseKeys,
1022
- whenNotToUse: operation.whenNotToUse,
1023
- whenNotToUseKeys: operation.whenNotToUseKeys,
1024
- input: operation.input,
1025
- output: operation.output,
1026
- fixtures: operation.fixtures,
1027
- inputExamples: operation.inputExamples,
1028
- derivations: operation.derivations,
1029
- }),
1030
- ...lintPublicSchemaFieldNames(
1031
- provider.id,
1032
- operationKey,
1033
- operation.input,
1034
- operation.output,
1035
- provider.meta?.contract?.publicSchemaFieldNames === "normalized",
1036
- ),
1037
- ].map((diagnostic) => ({
1038
- ...diagnostic,
1039
- field: diagnostic.field
1040
- ? `operations.${operationKey}.${diagnostic.field}`
1041
- : `operations.${operationKey}`,
1042
- message: `[${operationKey}] ${diagnostic.message}`,
1043
- })),
978
+ ...Object.entries(provider.operations).flatMap(([operationKey, operation]) =>
979
+ [
980
+ ...lintOperation({
981
+ description: operation.description ?? "",
982
+ descriptionKey: operation.descriptionKey,
983
+ whenToUse: operation.whenToUse,
984
+ whenToUseKeys: operation.whenToUseKeys,
985
+ whenNotToUse: operation.whenNotToUse,
986
+ whenNotToUseKeys: operation.whenNotToUseKeys,
987
+ input: operation.input,
988
+ output: operation.output,
989
+ fixtures: operation.fixtures,
990
+ inputExamples: operation.inputExamples,
991
+ derivations: operation.derivations,
992
+ }),
993
+ ...lintPublicSchemaFieldNames(
994
+ provider.id,
995
+ operationKey,
996
+ operation.input,
997
+ operation.output,
998
+ provider.meta?.contract?.publicSchemaFieldNames === "normalized",
999
+ ),
1000
+ ].map((diagnostic) => ({
1001
+ ...diagnostic,
1002
+ field: diagnostic.field
1003
+ ? `operations.${operationKey}.${diagnostic.field}`
1004
+ : `operations.${operationKey}`,
1005
+ message: `[${operationKey}] ${diagnostic.message}`,
1006
+ })),
1044
1007
  ),
1045
1008
  );
1046
1009
 
package/src/provider.ts CHANGED
@@ -1,4 +1,23 @@
1
- export { createFormCeremony } from "./ceremonies";
1
+ export {
2
+ AuthAbortError,
3
+ credentialsAuthChallenge,
4
+ createAuthFlowHelpers,
5
+ defineCredentialsAuth,
6
+ } from "./auth.js";
7
+ export type {
8
+ CredentialsAuthChallengeDefinition,
9
+ CredentialsAuthChallengeRequest,
10
+ CredentialsAuthCompleteResult,
11
+ CredentialsAuthCredential,
12
+ CredentialsAuthField,
13
+ CredentialsAuthFields,
14
+ CredentialsAuthFieldType,
15
+ CredentialsAuthInput,
16
+ CredentialsAuthLoginResult,
17
+ DefineCredentialsAuthOptions,
18
+ DefinedCredentialsAuth,
19
+ } from "./auth.js";
20
+ export { createFormCeremony } from "./ceremonies/index.js";
2
21
  export {
3
22
  assertFreshProviderChoiceIssuedAt,
4
23
  createProviderChoiceToken,
@@ -6,32 +25,37 @@ export {
6
25
  type ProviderChoiceTokenErrorReason,
7
26
  type ProviderChoiceTokenPayload,
8
27
  parseProviderChoiceToken,
9
- } from "./choice-token";
28
+ } from "./choice-token.js";
10
29
  export {
30
+ centered,
31
+ delayed,
11
32
  defineHealthJourney,
12
33
  defineOperation,
13
34
  defineProvider,
14
35
  defineSmsOtpMatcher,
15
36
  every,
16
- } from "./define";
37
+ } from "./define.js";
17
38
  export {
18
39
  AuthError,
40
+ isProviderError,
41
+ isSessionExpiredError,
42
+ isTransportError,
19
43
  ProviderError,
20
44
  SessionExpiredError,
21
45
  TransportError,
22
46
  ValidationError,
23
- } from "./errors";
47
+ } from "./errors.js";
24
48
  export {
25
49
  getProviderLocalePath,
26
50
  providerLocaleKey,
27
51
  qualifyProviderLocaleKey,
28
- } from "./i18n";
52
+ } from "./i18n/index.js";
29
53
  export {
30
54
  type CreateProviderChoiceContextOptions,
31
55
  createProviderChoiceContext,
32
56
  createTestProviderChoiceContext,
33
57
  PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV,
34
- } from "./runtime/choice";
58
+ } from "./runtime/choice.js";
35
59
  export {
36
60
  APIFUSE_DESCRIPTION_KEY_META_KEY,
37
61
  APIFUSE_REDACTION_MARKER,
@@ -48,9 +72,14 @@ export {
48
72
  type SensitivePath,
49
73
  sensitive,
50
74
  z,
51
- } from "./schema";
75
+ } from "./schema.js";
52
76
  export type {
77
+ AuthAbortData,
78
+ AuthAbortRetry,
79
+ AuthFlowTerminalContext,
53
80
  AuthMode,
81
+ AuthSafeData,
82
+ AuthSafeJson,
54
83
  FlowContext,
55
84
  HealthCheckAssertionContext,
56
85
  HealthCheckCase,
@@ -61,6 +90,7 @@ export type {
61
90
  HealthJourneyManualTriggerPolicy,
62
91
  HealthJourneyRunContext,
63
92
  HealthJourneyRunResult,
93
+ HealthScheduleRandomization,
64
94
  HttpRetryOptions,
65
95
  HttpRetrySummary,
66
96
  InferSchemaOutput,
@@ -85,6 +115,7 @@ export type {
85
115
  ProviderChoiceParseOptions,
86
116
  ProviderContext,
87
117
  ProviderDefinition,
118
+ ProviderDeploymentOverrides,
88
119
  ProviderLocale,
89
120
  ProviderLocaleKey,
90
121
  ProviderLocaleKeyInput,
@@ -103,11 +134,11 @@ export type {
103
134
  StateNamespaceOptions,
104
135
  StateValue,
105
136
  StateWriteOptions,
106
- } from "./types";
137
+ } from "./types.js";
107
138
  export {
108
139
  HttpRetryAfterPolicy,
109
140
  HttpRetryDelayStrategy,
110
141
  HttpRetryJitter,
111
142
  HttpRetryPreset,
112
143
  HttpRetryUnsafeMethodPolicy,
113
- } from "./types";
144
+ } from "./types.js";
@@ -1,6 +1,6 @@
1
1
  import type { ZodType } from "zod";
2
2
 
3
- import type { LintDiagnostic } from "./lint";
3
+ import type { LintDiagnostic } from "./lint.js";
4
4
 
5
5
  type SchemaLike = ZodType & {
6
6
  def?: Record<string, unknown>;
@@ -55,8 +55,7 @@ function isSchemaRecord(value: unknown): value is Record<string, SchemaLike> {
55
55
  }
56
56
 
57
57
  function getObjectShape(schema: SchemaLike): Record<string, SchemaLike> {
58
- const rawShape =
59
- typeof schema.shape === "function" ? schema.shape() : schema.shape;
58
+ const rawShape = typeof schema.shape === "function" ? schema.shape() : schema.shape;
60
59
  if (isSchemaRecord(rawShape)) {
61
60
  return rawShape;
62
61
  }
@@ -69,10 +68,7 @@ function getObjectShape(schema: SchemaLike): Record<string, SchemaLike> {
69
68
  return isSchemaRecord(defShape) ? defShape : {};
70
69
  }
71
70
 
72
- function appendSchemaChildren(
73
- children: SchemaLike[],
74
- value: unknown,
75
- ): SchemaLike[] {
71
+ function appendSchemaChildren(children: SchemaLike[], value: unknown): SchemaLike[] {
76
72
  if (isSchema(value)) {
77
73
  children.push(value);
78
74
  return children;
@@ -183,26 +179,14 @@ function collectPublicSchemaFieldDiagnostics(
183
179
  });
184
180
  }
185
181
  diagnostics.push(
186
- ...collectPublicSchemaFieldDiagnostics(
187
- providerId,
188
- operationId,
189
- child,
190
- fieldPath,
191
- seen,
192
- ),
182
+ ...collectPublicSchemaFieldDiagnostics(providerId, operationId, child, fieldPath, seen),
193
183
  );
194
184
  }
195
185
 
196
186
  for (const child of getTransparentChildSchemas(schema)) {
197
187
  const childPath = schema.element === child ? `${basePath}[]` : basePath;
198
188
  diagnostics.push(
199
- ...collectPublicSchemaFieldDiagnostics(
200
- providerId,
201
- operationId,
202
- child,
203
- childPath,
204
- seen,
205
- ),
189
+ ...collectPublicSchemaFieldDiagnostics(providerId, operationId, child, childPath, seen),
206
190
  );
207
191
  }
208
192
 
@@ -221,17 +205,7 @@ export function lintPublicSchemaFieldNames(
221
205
  }
222
206
 
223
207
  return [
224
- ...collectPublicSchemaFieldDiagnostics(
225
- providerId,
226
- operationId,
227
- input,
228
- "input",
229
- ),
230
- ...collectPublicSchemaFieldDiagnostics(
231
- providerId,
232
- operationId,
233
- output,
234
- "output",
235
- ),
208
+ ...collectPublicSchemaFieldDiagnostics(providerId, operationId, input, "input"),
209
+ ...collectPublicSchemaFieldDiagnostics(providerId, operationId, output, "output"),
236
210
  ];
237
211
  }
@@ -1,4 +1,4 @@
1
- import { unwrapEnvelope } from "../utils/parse";
1
+ import { unwrapEnvelope } from "../utils/parse.js";
2
2
 
3
3
  function getResultCode(raw: unknown): string | undefined {
4
4
  if (!raw || typeof raw !== "object") {
@@ -33,10 +33,7 @@ export function checkResultCode(
33
33
  * Replace placeholder values with null
34
34
  * e.g., nullIfPlaceholder('해당없음', ['해당없음', '-', '']) → null
35
35
  */
36
- export function nullIfPlaceholder(
37
- v: unknown,
38
- patterns: string[],
39
- ): unknown | null {
36
+ export function nullIfPlaceholder(v: unknown, patterns: string[]): unknown | null {
40
37
  if (v === null || v === undefined) {
41
38
  return null;
42
39
  }