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

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 (342) hide show
  1. package/AUTHORING.md +493 -5
  2. package/CHANGELOG.md +131 -1
  3. package/README.md +52 -6
  4. package/SUBMISSION.md +1 -1
  5. package/bin/apifuse-check.ts +106 -62
  6. package/bin/apifuse-create.ts +1 -1
  7. package/bin/apifuse-dev.ts +63 -55
  8. package/bin/apifuse-pack-check.ts +22 -2
  9. package/bin/apifuse-pack-smoke.ts +78 -82
  10. package/bin/apifuse-pack-types.ts +583 -0
  11. package/bin/apifuse-perf.ts +59 -140
  12. package/bin/apifuse-record.ts +698 -113
  13. package/bin/apifuse-submit-check.ts +517 -44
  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.ts +1 -1
  18. package/dist/auth-turn/index.d.ts +4 -4
  19. package/dist/auth-turn/index.js +1 -1
  20. package/dist/auth.d.ts +16 -2
  21. package/dist/auth.js +76 -18
  22. package/dist/ceremonies/index.d.ts +9 -1
  23. package/dist/ceremonies/index.js +117 -29
  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/cli/templates/provider/README.md.tpl +4 -4
  32. package/dist/config/loader.d.ts +176 -17
  33. package/dist/config/loader.js +434 -161
  34. package/dist/contract-serialization.d.ts +2 -2
  35. package/dist/contract-serialization.js +7 -14
  36. package/dist/contract-types.d.ts +3 -2
  37. package/dist/contract.d.ts +3 -3
  38. package/dist/contract.js +6 -6
  39. package/dist/declaration-validation.d.ts +23 -0
  40. package/dist/declaration-validation.js +159 -0
  41. package/dist/define.d.ts +13 -1
  42. package/dist/define.js +391 -122
  43. package/dist/dev.d.ts +1 -1
  44. package/dist/dev.js +1 -1
  45. package/dist/error-resolution.d.ts +4 -0
  46. package/dist/error-resolution.js +123 -0
  47. package/dist/errors.d.ts +19 -1
  48. package/dist/errors.js +41 -3
  49. package/dist/fixture-sanitization.d.ts +26 -0
  50. package/dist/fixture-sanitization.js +216 -0
  51. package/dist/i18n/catalog.d.ts +2 -2
  52. package/dist/i18n/catalog.js +4 -10
  53. package/dist/i18n/index.d.ts +2 -2
  54. package/dist/i18n/index.js +2 -2
  55. package/dist/i18n/keys.d.ts +2 -2
  56. package/dist/index.d.ts +50 -42
  57. package/dist/index.js +41 -37
  58. package/dist/lint.d.ts +6 -1
  59. package/dist/lint.js +370 -18
  60. package/dist/native-address.d.ts +43 -0
  61. package/dist/native-address.js +281 -0
  62. package/dist/native-egress-policy.d.ts +31 -0
  63. package/dist/native-egress-policy.js +288 -0
  64. package/dist/observability.d.ts +5 -2
  65. package/dist/observability.js +48 -1
  66. package/dist/provider.d.ts +13 -11
  67. package/dist/provider.js +10 -9
  68. package/dist/public-schema-field-lint.d.ts +1 -1
  69. package/dist/recipes/gov-api.js +1 -1
  70. package/dist/runtime/auth-flow.d.ts +3 -1
  71. package/dist/runtime/auth-flow.js +8 -3
  72. package/dist/runtime/browser.d.ts +1 -1
  73. package/dist/runtime/browser.js +138 -40
  74. package/dist/runtime/cache.d.ts +2 -1
  75. package/dist/runtime/cache.js +173 -23
  76. package/dist/runtime/choice-wordlist.d.ts +9 -0
  77. package/dist/runtime/choice-wordlist.js +138 -0
  78. package/dist/runtime/choice.d.ts +14 -1
  79. package/dist/runtime/choice.js +566 -101
  80. package/dist/runtime/credential.d.ts +1 -1
  81. package/dist/runtime/credential.js +1 -1
  82. package/dist/runtime/env.d.ts +1 -1
  83. package/dist/runtime/executor.d.ts +1 -1
  84. package/dist/runtime/executor.js +25 -3
  85. package/dist/runtime/http.d.ts +3 -2
  86. package/dist/runtime/http.js +517 -55
  87. package/dist/runtime/insights.d.ts +1 -1
  88. package/dist/runtime/insights.js +6 -13
  89. package/dist/runtime/instrumentation.d.ts +2 -2
  90. package/dist/runtime/instrumentation.js +371 -23
  91. package/dist/runtime/keyring.js +1 -1
  92. package/dist/runtime/namespace.js +1 -1
  93. package/dist/runtime/native-network-errors.d.ts +33 -0
  94. package/dist/runtime/native-network-errors.js +69 -0
  95. package/dist/runtime/native-network.d.ts +96 -0
  96. package/dist/runtime/native-network.js +1232 -0
  97. package/dist/runtime/ocr.d.ts +29 -0
  98. package/dist/runtime/ocr.js +440 -0
  99. package/dist/runtime/otlp.d.ts +1 -1
  100. package/dist/runtime/perf.d.ts +1 -1
  101. package/dist/runtime/provider.d.ts +1 -1
  102. package/dist/runtime/provider.js +1 -2
  103. package/dist/runtime/proxy-errors.d.ts +1 -1
  104. package/dist/runtime/proxy-errors.js +9 -7
  105. package/dist/runtime/proxy-nodemaven.d.ts +56 -0
  106. package/dist/runtime/proxy-nodemaven.js +146 -0
  107. package/dist/runtime/proxy-retry-policy.d.ts +2 -2
  108. package/dist/runtime/proxy-retry-policy.js +2 -2
  109. package/dist/runtime/proxy-telemetry.d.ts +2 -1
  110. package/dist/runtime/proxy-telemetry.js +58 -52
  111. package/dist/runtime/redirects.d.ts +29 -0
  112. package/dist/runtime/redirects.js +36 -0
  113. package/dist/runtime/redis.d.ts +1 -1
  114. package/dist/runtime/redis.js +5 -5
  115. package/dist/runtime/request-options.d.ts +68 -1
  116. package/dist/runtime/request-options.js +548 -0
  117. package/dist/runtime/resolver-config.d.ts +6 -0
  118. package/dist/runtime/resolver-config.js +6 -0
  119. package/dist/runtime/resolver-public.d.ts +1 -0
  120. package/dist/runtime/resolver-public.js +1 -0
  121. package/dist/runtime/resolver-shared.d.ts +3 -0
  122. package/dist/runtime/resolver-shared.js +12 -0
  123. package/dist/runtime/resolver-vendors/bindings.d.ts +48 -0
  124. package/dist/runtime/resolver-vendors/bindings.js +40 -0
  125. package/dist/runtime/resolver-vendors/browser.d.ts +20 -0
  126. package/dist/runtime/resolver-vendors/browser.js +282 -0
  127. package/dist/runtime/resolver-vendors/hosts.d.ts +2 -0
  128. package/dist/runtime/resolver-vendors/hosts.js +33 -0
  129. package/dist/runtime/resolver-vendors/twocaptcha.d.ts +24 -0
  130. package/dist/runtime/resolver-vendors/twocaptcha.js +368 -0
  131. package/dist/runtime/resolver-vendors/types.d.ts +83 -0
  132. package/dist/runtime/resolver-vendors/types.js +69 -0
  133. package/dist/runtime/resolver.d.ts +59 -0
  134. package/dist/runtime/resolver.js +705 -0
  135. package/dist/runtime/secrets.d.ts +27 -0
  136. package/dist/runtime/secrets.js +51 -0
  137. package/dist/runtime/state.d.ts +5 -2
  138. package/dist/runtime/state.js +280 -74
  139. package/dist/runtime/stealth-cookies.d.ts +20 -0
  140. package/dist/runtime/stealth-cookies.js +111 -0
  141. package/dist/runtime/stealth.d.ts +30 -5
  142. package/dist/runtime/stealth.js +523 -259
  143. package/dist/runtime/stt.d.ts +1 -1
  144. package/dist/runtime/stt.js +12 -27
  145. package/dist/runtime/timeout.d.ts +5 -0
  146. package/dist/runtime/timeout.js +12 -0
  147. package/dist/runtime/trace.d.ts +2 -2
  148. package/dist/runtime/trace.js +2 -4
  149. package/dist/runtime/waterfall.d.ts +1 -1
  150. package/dist/schema.d.ts +1 -1
  151. package/dist/schema.js +7 -15
  152. package/dist/serve.d.ts +1 -1
  153. package/dist/serve.js +1 -1
  154. package/dist/server/index.d.ts +7 -7
  155. package/dist/server/index.js +6 -6
  156. package/dist/server/self-test-input-tokens.d.ts +2 -1
  157. package/dist/server/self-test-input-tokens.js +18 -14
  158. package/dist/server/self-test-redaction.d.ts +1 -1
  159. package/dist/server/self-test-redaction.js +1 -1
  160. package/dist/server/self-test.d.ts +117 -3
  161. package/dist/server/self-test.js +787 -151
  162. package/dist/server/serve-implementation.d.ts +210 -0
  163. package/dist/server/serve-implementation.js +2078 -0
  164. package/dist/server/serve.d.ts +1 -70
  165. package/dist/server/serve.js +1 -1143
  166. package/dist/server/types.d.ts +34 -9
  167. package/dist/server/types.js +8 -1
  168. package/dist/stateful/errors.d.ts +19 -0
  169. package/dist/stateful/errors.js +24 -0
  170. package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
  171. package/dist/stateful/http-provider-event-emitter.js +237 -0
  172. package/dist/stateful/http-session-owner-registry.d.ts +44 -0
  173. package/dist/stateful/http-session-owner-registry.js +210 -0
  174. package/dist/stateful/index.d.ts +18 -0
  175. package/dist/stateful/index.js +18 -0
  176. package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
  177. package/dist/stateful/provider-event-delivery-failures.js +43 -0
  178. package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
  179. package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
  180. package/dist/stateful/provider-event-pipeline.d.ts +50 -0
  181. package/dist/stateful/provider-event-pipeline.js +1 -0
  182. package/dist/stateful/provider-events.d.ts +101 -0
  183. package/dist/stateful/provider-events.js +289 -0
  184. package/dist/stateful/session-key.d.ts +15 -0
  185. package/dist/stateful/session-key.js +86 -0
  186. package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
  187. package/dist/stateful/stateful-provider-adapter-context.js +42 -0
  188. package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
  189. package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
  190. package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
  191. package/dist/stateful/stateful-provider-adapter.js +287 -0
  192. package/dist/stateful/stateful-provider-observability.d.ts +62 -0
  193. package/dist/stateful/stateful-provider-observability.js +161 -0
  194. package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
  195. package/dist/stateful/stateful-provider-owner-forwarder.js +215 -0
  196. package/dist/stateful/stateful-provider-runtime-context.d.ts +32 -0
  197. package/dist/stateful/stateful-provider-runtime-context.js +60 -0
  198. package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
  199. package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
  200. package/dist/stateful/stateful-provider-session-routing.d.ts +67 -0
  201. package/dist/stateful/stateful-provider-session-routing.js +345 -0
  202. package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
  203. package/dist/stateful/stateful-provider-session-runtime.js +245 -0
  204. package/dist/stateful-signing.d.ts +18 -0
  205. package/dist/stateful-signing.js +27 -0
  206. package/dist/stealth/profiles.d.ts +1 -1
  207. package/dist/stealth/profiles.js +21 -21
  208. package/dist/stream-evidence.d.ts +74 -0
  209. package/dist/stream-evidence.js +785 -0
  210. package/dist/stream.d.ts +1 -1
  211. package/dist/stream.js +7 -1
  212. package/dist/testing/index.d.ts +3 -2
  213. package/dist/testing/index.js +3 -2
  214. package/dist/testing/run.d.ts +32 -2
  215. package/dist/testing/run.js +488 -28
  216. package/dist/types.d.ts +566 -19
  217. package/dist/types.js +1 -0
  218. package/dist/user-input.d.ts +30 -0
  219. package/dist/user-input.js +66 -0
  220. package/package.json +42 -7
  221. package/src/auth-turn/index.ts +2 -2
  222. package/src/auth.ts +146 -86
  223. package/src/ceremonies/index.ts +167 -92
  224. package/src/cli/commands.ts +10 -0
  225. package/src/cli/create.ts +42 -35
  226. package/src/cli/prompt-assets.ts +865 -0
  227. package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
  228. package/src/cli/templates/provider/README.md.tpl +4 -4
  229. package/src/config/loader.ts +667 -289
  230. package/src/contract-serialization.ts +10 -18
  231. package/src/contract-types.ts +3 -2
  232. package/src/contract.ts +14 -28
  233. package/src/declaration-validation.ts +202 -0
  234. package/src/define.ts +631 -495
  235. package/src/dev.ts +4 -9
  236. package/src/error-resolution.ts +128 -0
  237. package/src/errors.ts +56 -11
  238. package/src/fixture-sanitization.ts +247 -0
  239. package/src/i18n/catalog.ts +10 -32
  240. package/src/i18n/index.ts +2 -2
  241. package/src/i18n/keys.ts +5 -11
  242. package/src/index.ts +158 -44
  243. package/src/lint.ts +488 -154
  244. package/src/native-address.ts +340 -0
  245. package/src/native-egress-policy.ts +358 -0
  246. package/src/observability.ts +51 -1
  247. package/src/provider.ts +66 -11
  248. package/src/public-schema-field-lint.ts +7 -33
  249. package/src/recipes/gov-api.ts +2 -5
  250. package/src/runtime/auth-flow.ts +13 -7
  251. package/src/runtime/browser.ts +252 -207
  252. package/src/runtime/cache.ts +209 -81
  253. package/src/runtime/choice-wordlist.ts +145 -0
  254. package/src/runtime/choice.ts +758 -197
  255. package/src/runtime/credential.ts +2 -2
  256. package/src/runtime/env.ts +1 -1
  257. package/src/runtime/executor.ts +37 -19
  258. package/src/runtime/http.ts +645 -65
  259. package/src/runtime/insights.ts +15 -53
  260. package/src/runtime/instrumentation.ts +530 -67
  261. package/src/runtime/keyring.ts +7 -19
  262. package/src/runtime/namespace.ts +2 -7
  263. package/src/runtime/native-network-errors.ts +99 -0
  264. package/src/runtime/native-network.ts +1605 -0
  265. package/src/runtime/ocr.ts +523 -0
  266. package/src/runtime/otlp.ts +12 -23
  267. package/src/runtime/perf.ts +1 -1
  268. package/src/runtime/provider.ts +4 -9
  269. package/src/runtime/proxy-errors.ts +29 -42
  270. package/src/runtime/proxy-nodemaven.ts +221 -0
  271. package/src/runtime/proxy-retry-policy.ts +3 -3
  272. package/src/runtime/proxy-telemetry.ts +84 -77
  273. package/src/runtime/redirects.ts +66 -0
  274. package/src/runtime/redis.ts +10 -13
  275. package/src/runtime/request-options.ts +679 -9
  276. package/src/runtime/resolver-config.ts +6 -0
  277. package/src/runtime/resolver-public.ts +18 -0
  278. package/src/runtime/resolver-shared.ts +17 -0
  279. package/src/runtime/resolver-vendors/bindings.ts +56 -0
  280. package/src/runtime/resolver-vendors/browser.ts +408 -0
  281. package/src/runtime/resolver-vendors/hosts.ts +38 -0
  282. package/src/runtime/resolver-vendors/twocaptcha.ts +500 -0
  283. package/src/runtime/resolver-vendors/types.ts +173 -0
  284. package/src/runtime/resolver.ts +1060 -0
  285. package/src/runtime/secrets.ts +64 -0
  286. package/src/runtime/state.ts +399 -161
  287. package/src/runtime/stealth-cookies.ts +132 -0
  288. package/src/runtime/stealth.ts +681 -295
  289. package/src/runtime/stt.ts +39 -113
  290. package/src/runtime/timeout.ts +18 -0
  291. package/src/runtime/trace.ts +14 -44
  292. package/src/runtime/waterfall.ts +5 -18
  293. package/src/schema.ts +23 -84
  294. package/src/serve.ts +6 -1
  295. package/src/server/index.ts +30 -7
  296. package/src/server/self-test-input-tokens.ts +29 -14
  297. package/src/server/self-test-redaction.ts +2 -2
  298. package/src/server/self-test.ts +1030 -180
  299. package/src/server/serve-implementation.ts +3062 -0
  300. package/src/server/serve.ts +1 -1781
  301. package/src/server/types.ts +12 -13
  302. package/src/stateful/README.md +146 -0
  303. package/src/stateful/errors.ts +35 -0
  304. package/src/stateful/http-provider-event-emitter.ts +314 -0
  305. package/src/stateful/http-session-owner-registry.ts +306 -0
  306. package/src/stateful/index.ts +18 -0
  307. package/src/stateful/provider-event-delivery-failures.ts +80 -0
  308. package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
  309. package/src/stateful/provider-event-pipeline.ts +61 -0
  310. package/src/stateful/provider-events.ts +462 -0
  311. package/src/stateful/session-key.ts +111 -0
  312. package/src/stateful/stateful-provider-adapter-context.ts +59 -0
  313. package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
  314. package/src/stateful/stateful-provider-adapter.ts +562 -0
  315. package/src/stateful/stateful-provider-observability.ts +261 -0
  316. package/src/stateful/stateful-provider-owner-forwarder.ts +287 -0
  317. package/src/stateful/stateful-provider-runtime-context.ts +92 -0
  318. package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
  319. package/src/stateful/stateful-provider-session-routing.ts +546 -0
  320. package/src/stateful/stateful-provider-session-runtime.ts +403 -0
  321. package/src/stateful-signing.ts +46 -0
  322. package/src/stealth/profiles.ts +27 -33
  323. package/src/stream-evidence.ts +988 -0
  324. package/src/stream.ts +16 -20
  325. package/src/testing/index.ts +11 -2
  326. package/src/testing/run.ts +668 -74
  327. package/src/types.ts +665 -35
  328. package/src/user-input.ts +118 -0
  329. package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
  330. package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
  331. /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  332. /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  333. /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  334. /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  335. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  336. /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
  337. /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
  338. /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
  339. /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
  340. /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
  341. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
  342. /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
package/dist/lint.js CHANGED
@@ -1,6 +1,84 @@
1
- import { lintPublicSchemaFieldNames } from "./public-schema-field-lint";
2
- import { APIFUSE_DESCRIPTION_KEY_META_KEY, APIFUSE_SENSITIVE_META_KEY, } from "./schema";
1
+ import { SDK_RUNTIME_OWNED_ERROR_CODES, SDK_STATUS_MAPPED_PROVIDER_ERROR_CODES, } from "./error-resolution.js";
2
+ import { lintPublicSchemaFieldNames } from "./public-schema-field-lint.js";
3
+ import { APIFUSE_DESCRIPTION_KEY_META_KEY, APIFUSE_SENSITIVE_META_KEY } from "./schema.js";
4
+ // Operations that perform an auth-lifecycle action belong on the single
5
+ // `auth.flow` interface, never on a provider operation:
6
+ // - entry (login / signin / authenticate) => auth.flow.start/continue
7
+ // - exit (logout / signout / disconnect) => auth.flow.abort
8
+ //
9
+ // Matching works on `-`/`_` separated segments rather than a raw substring or a
10
+ // leading anchor, so `shop-logout`, `shop_logout` and `user-sign-out-everywhere`
11
+ // are all recognised: a domain prefix does not make the operation any less of an
12
+ // auth-lifecycle action, and operation ids may use either separator.
13
+ //
14
+ // Vocabulary is split into two tiers because auth words collide with ordinary
15
+ // domain verbs. Measured against the live fleet plus synthetic domain ids:
16
+ // - `authorize-payment`, `revoke-invitation`, `unlink-record`,
17
+ // `disconnect-device` are domain actions that never touch the connection
18
+ // credential, so these verbs are NOT matched as segments;
19
+ // - the same verbs as a complete operation id (`authorize`, `revoke`) do
20
+ // refer to the credential itself, so they are matched only in that form.
21
+ // `exchange`, `callback`, `connect`, `session`, `token`, `credential`,
22
+ // `password` and `otp` stay out entirely for the same reason.
23
+ const AUTH_LIFECYCLE_SEGMENT_WORDS = new Set([
24
+ "login",
25
+ "logout",
26
+ "signin",
27
+ "signout",
28
+ "signup",
29
+ "authenticate",
30
+ "reauth",
31
+ "auth",
32
+ ]);
33
+ // Ambiguous as a prefix, unambiguous when they are the whole operation id.
34
+ const AUTH_LIFECYCLE_WHOLE_ID_WORDS = new Set([
35
+ "authorize",
36
+ "revoke",
37
+ "unlink",
38
+ "disconnect",
39
+ ]);
40
+ // A verb stem followed by a direction word across two segments: `sign-out`,
41
+ // `user_sign_up_flow`, and the spelled-out `log-in` / `shop-log-out` forms
42
+ // (their fused equivalents `login`/`logout` live in the segment set above).
43
+ // `sign` pairs match anywhere; `log` pairs match only at the END of the id,
44
+ // because mid-id `log` is the noun in domain phrases measured against real
45
+ // fleets (`audit-log-in-range`, `change-log-out-of-band` are reads of a log,
46
+ // while `shop-log-out` is a logout).
47
+ const AUTH_DIRECTION_PAIRS = new Map([
48
+ ["sign", { directions: new Set(["in", "out", "up"]), endOnly: false }],
49
+ ["log", { directions: new Set(["in", "out"]), endOnly: true }],
50
+ ]);
51
+ // Legacy anchored form kept for token-plumbing words whose bare use is only
52
+ // auth-related when it leads the operation id (`exchange-code`, `refresh`).
3
53
  const AUTH_OPERATION_ID_PATTERN = /^(?:auth[-_])?(?:login|exchange|continue|refresh|callback)(?:[-_]|$)/i;
54
+ function isAuthLifecycleOperationId(operationId, authMode) {
55
+ const segments = operationId.toLowerCase().split(/[-_]+/).filter(Boolean);
56
+ if (segments.some((segment) => AUTH_LIFECYCLE_SEGMENT_WORDS.has(segment)))
57
+ return true;
58
+ // A verb stem + direction spread across two segments (`sign-out`,
59
+ // `sign_up`, `shop-log-out`); see AUTH_DIRECTION_PAIRS for positioning.
60
+ if (segments.some((segment, index) => {
61
+ const pair = AUTH_DIRECTION_PAIRS.get(segment);
62
+ if (pair === undefined || index + 1 >= segments.length)
63
+ return false;
64
+ if (!pair.directions.has(segments[index + 1]))
65
+ return false;
66
+ return pair.endOnly ? index + 2 === segments.length : true;
67
+ })) {
68
+ return true;
69
+ }
70
+ if (segments.length === 1 && AUTH_LIFECYCLE_WHOLE_ID_WORDS.has(segments[0])) {
71
+ return true;
72
+ }
73
+ // The legacy anchored pattern keeps its original scope. It matches ordinary
74
+ // domain ids such as `exchange-rates` and `refresh-catalog`, so extending it
75
+ // to `oauth2_proxied` would spread that behavior to providers it never
76
+ // applied to; proxied providers are covered by the segment tiers above.
77
+ if (authMode === "credentials" || authMode === "oauth2") {
78
+ return AUTH_OPERATION_ID_PATTERN.test(operationId);
79
+ }
80
+ return false;
81
+ }
4
82
  function lintAllowedHosts(providerId, allowedHosts) {
5
83
  const prefix = providerId ? `Provider "${providerId}"` : "Provider";
6
84
  if (!allowedHosts) {
@@ -120,8 +198,7 @@ function lintAuthModel(provider) {
120
198
  });
121
199
  }
122
200
  if (hasReusableSecretKeys(credentialKeys) &&
123
- (!provider.credential?.storesReusableSecret ||
124
- !provider.credential.justification)) {
201
+ (!provider.credential?.storesReusableSecret || !provider.credential.justification)) {
125
202
  diagnostics.push({
126
203
  rule: "credential-reusable-secret",
127
204
  level: "error",
@@ -131,8 +208,7 @@ function lintAuthModel(provider) {
131
208
  }
132
209
  if (typeof provider.auth?.flow?.refresh === "function" &&
133
210
  hasReusableReloginSecretKeys(credentialKeys) &&
134
- (!provider.credential?.storesReusableSecret ||
135
- !provider.credential.justification)) {
211
+ (!provider.credential?.storesReusableSecret || !provider.credential.justification)) {
136
212
  diagnostics.push({
137
213
  rule: "auth-refresh-reusable-secret",
138
214
  level: "error",
@@ -149,8 +225,7 @@ function lintAuthModel(provider) {
149
225
  });
150
226
  }
151
227
  const authFlowSource = getAuthFlowSource(provider);
152
- if (authFlowSource.includes("ctx.context") &&
153
- (provider.context?.keys?.length ?? 0) === 0) {
228
+ if (authFlowSource.includes("ctx.context") && (provider.context?.keys?.length ?? 0) === 0) {
154
229
  diagnostics.push({
155
230
  rule: "context-keys-required",
156
231
  level: "warn",
@@ -435,8 +510,7 @@ function isComplexSchema(schema, seen = new Set()) {
435
510
  const childChildren = getChildSchemas(child);
436
511
  return childChildren.length > 0;
437
512
  });
438
- return (hasNestedComposite ||
439
- children.some(({ schema: child }) => isComplexSchema(child, seen)));
513
+ return hasNestedComposite || children.some(({ schema: child }) => isComplexSchema(child, seen));
440
514
  }
441
515
  function hasBidirectionalFixtures(fixtures) {
442
516
  if (!fixtures || typeof fixtures !== "object") {
@@ -448,9 +522,7 @@ function getOperationSource(operation) {
448
522
  if (operation.source) {
449
523
  return operation.source;
450
524
  }
451
- return typeof operation.handler === "function"
452
- ? operation.handler.toString()
453
- : "";
525
+ return typeof operation.handler === "function" ? operation.handler.toString() : "";
454
526
  }
455
527
  function lintStealthTransportUsage(provider) {
456
528
  if (provider.stealth || !provider.operations) {
@@ -593,6 +665,281 @@ function lintSelfHostedBrowserPatterns(provider, options) {
593
665
  }
594
666
  return diagnostics;
595
667
  }
668
+ const THROWN_ERROR_CONSTRUCTION_PATTERN = /new\s+(?:ProviderError|ValidationError)\s*\(/g;
669
+ const TEST_SOURCE_FILE_PATTERN = /(?:^|\/)(?:__tests__|__mocks__)\/|\.(?:test|spec)\.[cm]?[jt]sx?$/;
670
+ /**
671
+ * Skips a string literal starting at `startIndex` (which must point at the
672
+ * opening quote). Returns the index of the closing quote, or -1 when the
673
+ * literal is unterminated. Template literals handle nested `${...}`
674
+ * expressions, including strings inside them.
675
+ */
676
+ function skipStringLiteral(source, startIndex) {
677
+ const quote = source[startIndex];
678
+ for (let index = startIndex + 1; index < source.length; index++) {
679
+ const char = source[index];
680
+ if (char === "\\") {
681
+ index++;
682
+ continue;
683
+ }
684
+ if (quote === "`" && char === "$" && source[index + 1] === "{") {
685
+ index = skipTemplateExpression(source, index + 2);
686
+ if (index < 0) {
687
+ return -1;
688
+ }
689
+ continue;
690
+ }
691
+ if (char === quote) {
692
+ return index;
693
+ }
694
+ if (quote !== "`" && char === "\n") {
695
+ return -1;
696
+ }
697
+ }
698
+ return -1;
699
+ }
700
+ function skipTemplateExpression(source, startIndex) {
701
+ let depth = 1;
702
+ for (let index = startIndex; index < source.length; index++) {
703
+ const char = source[index];
704
+ if (char === '"' || char === "'" || char === "`") {
705
+ index = skipStringLiteral(source, index);
706
+ if (index < 0) {
707
+ return -1;
708
+ }
709
+ continue;
710
+ }
711
+ if (char === "{") {
712
+ depth++;
713
+ }
714
+ else if (char === "}") {
715
+ depth--;
716
+ if (depth === 0) {
717
+ return index;
718
+ }
719
+ }
720
+ }
721
+ return -1;
722
+ }
723
+ /**
724
+ * Extracts the argument text of a call whose opening paren has already been
725
+ * consumed (`startIndex` points just past it). Returns undefined when the
726
+ * call never closes in this source, which the caller treats as "skip
727
+ * silently" — this scanner is conservative by design.
728
+ */
729
+ function extractBalancedCallArguments(source, startIndex) {
730
+ let depth = 1;
731
+ for (let index = startIndex; index < source.length; index++) {
732
+ const char = source[index];
733
+ if (char === '"' || char === "'" || char === "`") {
734
+ index = skipStringLiteral(source, index);
735
+ if (index < 0) {
736
+ return undefined;
737
+ }
738
+ continue;
739
+ }
740
+ if (char === "/" && source[index + 1] === "/") {
741
+ const newline = source.indexOf("\n", index);
742
+ if (newline === -1) {
743
+ return undefined;
744
+ }
745
+ index = newline;
746
+ continue;
747
+ }
748
+ if (char === "/" && source[index + 1] === "*") {
749
+ const end = source.indexOf("*/", index + 2);
750
+ if (end === -1) {
751
+ return undefined;
752
+ }
753
+ index = end + 1;
754
+ continue;
755
+ }
756
+ if (char === "(") {
757
+ depth++;
758
+ }
759
+ else if (char === ")") {
760
+ depth--;
761
+ if (depth === 0) {
762
+ return source.slice(startIndex, index);
763
+ }
764
+ }
765
+ }
766
+ return undefined;
767
+ }
768
+ /**
769
+ * Collects literal string values of top-level `code:` properties inside a
770
+ * ProviderError/ValidationError options object. Only plain `"..."` / `'...'`
771
+ * literals at options-object depth count; computed codes (identifiers,
772
+ * ternaries, template substitutions, concatenations, escapes) are skipped
773
+ * silently so the rule never guesses.
774
+ */
775
+ function collectLiteralErrorCodeValues(args) {
776
+ const codes = [];
777
+ let braceDepth = 0;
778
+ let parenDepth = 0;
779
+ let bracketDepth = 0;
780
+ let previousSignificantChar = "";
781
+ for (let index = 0; index < args.length; index++) {
782
+ const char = args[index] ?? "";
783
+ if (char === '"' || char === "'" || char === "`") {
784
+ const end = skipStringLiteral(args, index);
785
+ if (end < 0) {
786
+ return codes;
787
+ }
788
+ index = end;
789
+ previousSignificantChar = char;
790
+ continue;
791
+ }
792
+ if (char === "/" && args[index + 1] === "/") {
793
+ const newline = args.indexOf("\n", index);
794
+ if (newline === -1) {
795
+ return codes;
796
+ }
797
+ index = newline;
798
+ continue;
799
+ }
800
+ if (char === "/" && args[index + 1] === "*") {
801
+ const end = args.indexOf("*/", index + 2);
802
+ if (end === -1) {
803
+ return codes;
804
+ }
805
+ index = end + 1;
806
+ continue;
807
+ }
808
+ if (/\s/.test(char)) {
809
+ continue;
810
+ }
811
+ if (char === "{") {
812
+ braceDepth++;
813
+ }
814
+ else if (char === "}") {
815
+ braceDepth--;
816
+ }
817
+ else if (char === "(") {
818
+ parenDepth++;
819
+ }
820
+ else if (char === ")") {
821
+ parenDepth--;
822
+ }
823
+ else if (char === "[") {
824
+ bracketDepth++;
825
+ }
826
+ else if (char === "]") {
827
+ bracketDepth--;
828
+ }
829
+ else if (braceDepth === 1 &&
830
+ parenDepth === 0 &&
831
+ bracketDepth === 0 &&
832
+ (previousSignificantChar === "{" || previousSignificantChar === ",") &&
833
+ args.startsWith("code", index)) {
834
+ let cursor = index + "code".length;
835
+ while (cursor < args.length && /\s/.test(args[cursor] ?? "")) {
836
+ cursor++;
837
+ }
838
+ if (args[cursor] === ":") {
839
+ cursor++;
840
+ while (cursor < args.length && /\s/.test(args[cursor] ?? "")) {
841
+ cursor++;
842
+ }
843
+ const quote = args[cursor];
844
+ if (quote === '"' || quote === "'") {
845
+ const end = skipStringLiteral(args, cursor);
846
+ if (end > cursor) {
847
+ const value = args.slice(cursor + 1, end);
848
+ let after = end + 1;
849
+ while (after < args.length && /\s/.test(args[after] ?? "")) {
850
+ after++;
851
+ }
852
+ const nextChar = after < args.length ? (args[after] ?? "") : "";
853
+ if (!value.includes("\\") && (nextChar === "," || nextChar === "}" || nextChar === "")) {
854
+ codes.push(value);
855
+ }
856
+ index = end;
857
+ previousSignificantChar = quote;
858
+ continue;
859
+ }
860
+ return codes;
861
+ }
862
+ }
863
+ }
864
+ previousSignificantChar = char;
865
+ }
866
+ return codes;
867
+ }
868
+ function collectLiteralThrownErrorCodes(source) {
869
+ const codes = [];
870
+ THROWN_ERROR_CONSTRUCTION_PATTERN.lastIndex = 0;
871
+ for (let match = THROWN_ERROR_CONSTRUCTION_PATTERN.exec(source); match; match = THROWN_ERROR_CONSTRUCTION_PATTERN.exec(source)) {
872
+ const argsStart = match.index + match[0].length;
873
+ const args = extractBalancedCallArguments(source, argsStart);
874
+ if (args !== undefined) {
875
+ codes.push(...collectLiteralErrorCodeValues(args));
876
+ }
877
+ THROWN_ERROR_CONSTRUCTION_PATTERN.lastIndex = argsStart;
878
+ }
879
+ return codes;
880
+ }
881
+ /**
882
+ * Static counterpart of the runtime `unregistered_provider_error_code`
883
+ * signal (honest-provider-error-contract Phase 3.5.5): flags
884
+ * `new ProviderError(...)` / `new ValidationError(...)` constructions whose
885
+ * literal `code` is neither SDK-registered (SDK_RUNTIME_OWNED_ERROR_CODES
886
+ * plus the canonical status-mapped codes shared with serve.ts toStatusCode)
887
+ * nor declared in any operation's docs.errorCodes. At runtime such a code
888
+ * serves HTTP 500 and emits the signal; this rule surfaces it at check time.
889
+ *
890
+ * A throw site cannot be attributed to a specific operation statically —
891
+ * providers routinely throw from helpers shared across operations — so this
892
+ * rule matches against the provider-level union of declared codes. That is
893
+ * the honest scope: it will not catch a code declared only on the "wrong"
894
+ * operation, and it never claims per-operation attribution it cannot prove.
895
+ * Only literal string codes are checked; computed/dynamic codes and test
896
+ * sources are skipped silently. Warning level: the long tail of existing
897
+ * providers converges gradually, so this must not fail `apifuse check`.
898
+ */
899
+ function lintUndeclaredThrownErrorCodes(provider) {
900
+ const knownCodes = new Set([
901
+ ...SDK_RUNTIME_OWNED_ERROR_CODES,
902
+ ...SDK_STATUS_MAPPED_PROVIDER_ERROR_CODES.keys(),
903
+ ]);
904
+ for (const operation of Object.values(provider.operations ?? {})) {
905
+ for (const entry of operation.docs?.errorCodes ?? []) {
906
+ if (typeof entry?.code === "string") {
907
+ knownCodes.add(entry.code);
908
+ }
909
+ }
910
+ }
911
+ const sources = [];
912
+ const sourceFiles = Object.entries(provider.providerSourceFiles ?? {}).filter(([filePath]) => !TEST_SOURCE_FILE_PATTERN.test(filePath));
913
+ if (sourceFiles.length > 0) {
914
+ for (const [filePath, source] of sourceFiles) {
915
+ sources.push({ field: `sourceFiles.${filePath}`, source });
916
+ }
917
+ }
918
+ else {
919
+ if (provider.authFlowSource) {
920
+ sources.push({ field: "auth.flow", source: provider.authFlowSource });
921
+ }
922
+ for (const [operationKey, operation] of Object.entries(provider.operations ?? {})) {
923
+ const source = getOperationSource(operation);
924
+ if (source) {
925
+ sources.push({ field: `operations.${operationKey}.handler`, source });
926
+ }
927
+ }
928
+ }
929
+ const diagnostics = [];
930
+ for (const { field, source } of sources) {
931
+ const undeclaredCodes = new Set(collectLiteralThrownErrorCodes(source).filter((code) => !knownCodes.has(code)));
932
+ for (const code of undeclaredCodes) {
933
+ diagnostics.push({
934
+ rule: "thrown-error-code-undeclared",
935
+ level: "warn",
936
+ field,
937
+ message: `Thrown error code "${code}" (${field}) is neither SDK-registered nor declared in any operation's docs.errorCodes; at runtime it serves HTTP 500 and emits the unregistered_provider_error_code signal. Declare it in the owning operation's docs.errorCodes with status and retryable.`,
938
+ });
939
+ }
940
+ }
941
+ return diagnostics;
942
+ }
596
943
  export function lintOperation(op) {
597
944
  const diagnostics = [];
598
945
  const description = op.description ?? "";
@@ -621,8 +968,7 @@ export function lintOperation(op) {
621
968
  message: "Operation whenToUse must use whenToUseKeys instead of raw static prose.",
622
969
  });
623
970
  }
624
- if ((op.whenNotToUse?.length ?? 0) > 0 &&
625
- !(op.whenNotToUseKeys?.length ?? 0)) {
971
+ if ((op.whenNotToUse?.length ?? 0) > 0 && !(op.whenNotToUseKeys?.length ?? 0)) {
626
972
  diagnostics.push({
627
973
  rule: "operation-when-not-to-use-raw-prose",
628
974
  level: "error",
@@ -684,17 +1030,23 @@ export function lintProvider(provider, options = {}) {
684
1030
  ...lintCredentialWriteUsage(provider),
685
1031
  ...lintPlaywrightDirectImports(provider),
686
1032
  ...lintSelfHostedBrowserPatterns(provider, options),
1033
+ ...lintUndeclaredThrownErrorCodes(provider),
687
1034
  ];
688
1035
  if (provider.operations) {
689
1036
  const authMode = provider.auth?.mode;
690
- if (authMode === "credentials" || authMode === "oauth2") {
1037
+ // Every authenticated mode owns an auth.flow; `oauth2_proxied` was
1038
+ // previously exempt, which let auth-lifecycle operations ship on
1039
+ // proxied providers unchecked.
1040
+ if (authMode === "credentials" ||
1041
+ authMode === "oauth2" ||
1042
+ authMode === "oauth2_proxied") {
691
1043
  for (const operationKey of Object.keys(provider.operations)) {
692
- if (AUTH_OPERATION_ID_PATTERN.test(operationKey)) {
1044
+ if (isAuthLifecycleOperationId(operationKey, authMode)) {
693
1045
  diagnostics.push({
694
1046
  rule: "auth-operation-unsupported",
695
1047
  level: "error",
696
1048
  field: `operations.${operationKey}`,
697
- 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.`,
1049
+ message: `Provider "${provider.id ?? "unknown"}" operation "${operationKey}" performs an auth-lifecycle action (login, logout, token exchange or similar). Authenticated providers must expose the whole credential lifecycle through the single auth.flow interface because Gateway persists only auth.flow complete turn data.credential as the connection credential, and an operation that mutates the session outside that interface leaves the stored connection stale. Move sign-in logic into auth.flow.start/continue and sign-out/disconnect logic into auth.flow.abort (served by POST /auth/disconnect) instead of a provider operation.`,
698
1050
  });
699
1051
  }
700
1052
  }
@@ -0,0 +1,43 @@
1
+ export type EgressHostKind = "ipv4" | "ipv6" | "ipv4-mapped-ipv6" | "numeric-ambiguous" | "dns";
2
+ export type EgressHostCanonicalizationFailure = "not-string" | "reserved-delimiter" | "control-character" | "whitespace" | "canonicalization-empty";
3
+ export type EgressHostCanonicalizationResult = {
4
+ readonly ok: true;
5
+ readonly host: string;
6
+ } | {
7
+ readonly ok: false;
8
+ readonly reason: EgressHostCanonicalizationFailure;
9
+ };
10
+ export type Ipv6CidrOverlap = "ipv4-mapped" | "ipv4-compatible";
11
+ export type Ipv6CidrParseResult = {
12
+ readonly ok: true;
13
+ readonly network: Uint8Array;
14
+ readonly prefix: number;
15
+ } | {
16
+ readonly ok: false;
17
+ readonly reason: "malformed";
18
+ readonly overlap?: Ipv6CidrOverlap;
19
+ } | {
20
+ readonly ok: false;
21
+ readonly reason: "non-canonical-network";
22
+ };
23
+ export declare function parseStrictIpv4(value: string): number | undefined;
24
+ /** Parse the RFC 4291 IPv6 text forms accepted at both policy and runtime boundaries. */
25
+ export declare function parseIpv6(value: string): Uint8Array | undefined;
26
+ export declare function parseIpv4Cidr(value: string): {
27
+ readonly ok: true;
28
+ readonly network: number;
29
+ readonly prefix: number;
30
+ } | {
31
+ readonly ok: false;
32
+ readonly reason: "malformed" | "non-canonical-network";
33
+ };
34
+ export declare function parseIpv6Cidr(value: string): Ipv6CidrParseResult;
35
+ export declare function ipv4InCidr(address: number, cidr: string): boolean;
36
+ export declare function ipv6InCidr(address: Uint8Array, cidr: string): boolean;
37
+ export declare function embeddedIpv4FromIpv6(address: Uint8Array): number | undefined;
38
+ export declare function formatIpv6(address: Uint8Array): string;
39
+ /** The single family and ambiguity classifier used by policy and runtime matching. */
40
+ export declare function classifyEgressHost(host: string): EgressHostKind;
41
+ export declare function hasReservedEgressHostDelimiter(value: string): boolean;
42
+ export declare function hasEgressHostControlCharacter(value: string): boolean;
43
+ export declare function canonicalizeEgressHost(value: unknown): EgressHostCanonicalizationResult;