@lovrozagar/honey 0.6.8 → 0.7.0

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 (674) hide show
  1. package/README.md +514 -192
  2. package/bin/honey.js +25 -0
  3. package/dist/accepts.d.ts +7 -1
  4. package/dist/accepts.d.ts.map +1 -1
  5. package/dist/accepts.js +126 -0
  6. package/dist/accepts.js.map +1 -0
  7. package/dist/body-kind.d.ts +26 -0
  8. package/dist/body-kind.d.ts.map +1 -0
  9. package/dist/body-kind.js +70 -0
  10. package/dist/body-kind.js.map +1 -0
  11. package/dist/body-limit.d.ts +5 -1
  12. package/dist/body-limit.d.ts.map +1 -1
  13. package/dist/body-limit.js +126 -0
  14. package/dist/body-limit.js.map +1 -0
  15. package/dist/build/index.d.ts +19 -3
  16. package/dist/build/index.d.ts.map +1 -1
  17. package/dist/build/index.js +173 -0
  18. package/dist/build/index.js.map +1 -0
  19. package/dist/cli-args.d.ts +33 -0
  20. package/dist/cli-args.d.ts.map +1 -0
  21. package/dist/cli-args.js +102 -0
  22. package/dist/cli-args.js.map +1 -0
  23. package/dist/cli-go/auth.go +26 -0
  24. package/dist/cli-go/config.go +192 -0
  25. package/dist/cli-go/errors.go +75 -0
  26. package/dist/cli-go/multipart.go +79 -0
  27. package/dist/cli-go/output.go +163 -0
  28. package/dist/cli-go/root.go +30 -0
  29. package/dist/cli-go/stream.go +88 -0
  30. package/dist/cli-go/version.go +5 -0
  31. package/dist/cli.d.ts +1 -1
  32. package/dist/cli.js +209 -0
  33. package/dist/cli.js.map +1 -0
  34. package/dist/client/defaults.js +48 -0
  35. package/dist/client/defaults.js.map +1 -0
  36. package/dist/client/error.d.ts +35 -0
  37. package/dist/client/error.d.ts.map +1 -1
  38. package/dist/client/error.js +71 -0
  39. package/dist/client/error.js.map +1 -0
  40. package/dist/client/http.d.ts +67 -8
  41. package/dist/client/http.d.ts.map +1 -1
  42. package/dist/client/http.js +571 -0
  43. package/dist/client/http.js.map +1 -0
  44. package/dist/client/index.d.ts +3 -2
  45. package/dist/client/index.d.ts.map +1 -1
  46. package/dist/client/index.js +95 -0
  47. package/dist/client/index.js.map +1 -0
  48. package/dist/client/path.d.ts +26 -0
  49. package/dist/client/path.d.ts.map +1 -0
  50. package/dist/client/path.js +163 -0
  51. package/dist/client/path.js.map +1 -0
  52. package/dist/client/redirect-policy.d.ts +12 -0
  53. package/dist/client/redirect-policy.d.ts.map +1 -0
  54. package/dist/client/redirect-policy.js +20 -0
  55. package/dist/client/redirect-policy.js.map +1 -0
  56. package/dist/client/sdk.d.ts +36 -0
  57. package/dist/client/sdk.d.ts.map +1 -1
  58. package/dist/client/sdk.js +292 -0
  59. package/dist/client/sdk.js.map +1 -0
  60. package/dist/client/sse.d.ts +13 -0
  61. package/dist/client/sse.d.ts.map +1 -1
  62. package/dist/client/sse.js +129 -0
  63. package/dist/client/sse.js.map +1 -0
  64. package/dist/client/types.js +2 -0
  65. package/dist/client/types.js.map +1 -0
  66. package/dist/client/ws.d.ts +9 -2
  67. package/dist/client/ws.d.ts.map +1 -1
  68. package/dist/client/ws.js +112 -0
  69. package/dist/client/ws.js.map +1 -0
  70. package/dist/client-go/errors.go +236 -0
  71. package/dist/client-go/export.go +44 -0
  72. package/dist/client-go/invalidation.go +371 -0
  73. package/dist/client-go/nullable.go +62 -0
  74. package/dist/client-go/realtime.go +354 -0
  75. package/dist/client-go/result.go +20 -0
  76. package/dist/client-go/runtime.go +975 -0
  77. package/dist/client-go/sse.go +159 -0
  78. package/dist/client-go/transport.go +495 -0
  79. package/dist/client-go/ws.go +128 -0
  80. package/dist/client-info.d.ts +36 -0
  81. package/dist/client-info.d.ts.map +1 -0
  82. package/dist/client-info.js +53 -0
  83. package/dist/client-info.js.map +1 -0
  84. package/dist/client-mcp/README.md +10 -0
  85. package/dist/client-mcp/server.ts +221 -0
  86. package/dist/client-mcp/types.ts +19 -0
  87. package/dist/client-python/_errors.py +115 -0
  88. package/dist/client-python/_invalidation.py +494 -0
  89. package/dist/client-python/_realtime.py +179 -0
  90. package/dist/client-python/_runtime.py +860 -0
  91. package/dist/client-python/_sse.py +155 -0
  92. package/dist/client-python/_transport.py +366 -0
  93. package/dist/client-python/_ws.py +159 -0
  94. package/dist/client-python/py.typed +0 -0
  95. package/dist/client-rust/errors.rs +220 -0
  96. package/dist/client-rust/invalidation.rs +412 -0
  97. package/dist/client-rust/invalidation_sync.rs +93 -0
  98. package/dist/client-rust/nullable.rs +17 -0
  99. package/dist/client-rust/realtime.rs +702 -0
  100. package/dist/client-rust/result.rs +22 -0
  101. package/dist/client-rust/runtime.rs +980 -0
  102. package/dist/client-rust/runtime_sync.rs +484 -0
  103. package/dist/client-rust/sse.rs +188 -0
  104. package/dist/client-rust/ws.rs +167 -0
  105. package/dist/codegen-go-cli.d.ts.map +1 -1
  106. package/dist/codegen-go-cli.js +964 -0
  107. package/dist/codegen-go-cli.js.map +1 -0
  108. package/dist/codegen-go.d.ts +15 -3
  109. package/dist/codegen-go.d.ts.map +1 -1
  110. package/dist/codegen-go.js +872 -0
  111. package/dist/codegen-go.js.map +1 -0
  112. package/dist/codegen-hash.js +9 -0
  113. package/dist/codegen-hash.js.map +1 -0
  114. package/dist/codegen-ir.d.ts +69 -2
  115. package/dist/codegen-ir.d.ts.map +1 -1
  116. package/dist/codegen-ir.js +661 -0
  117. package/dist/codegen-ir.js.map +1 -0
  118. package/dist/codegen-lang.d.ts +65 -0
  119. package/dist/codegen-lang.d.ts.map +1 -0
  120. package/dist/codegen-lang.js +526 -0
  121. package/dist/codegen-lang.js.map +1 -0
  122. package/dist/codegen-load.d.ts +31 -0
  123. package/dist/codegen-load.d.ts.map +1 -0
  124. package/dist/codegen-load.js +138 -0
  125. package/dist/codegen-load.js.map +1 -0
  126. package/dist/codegen-loaders.js +55 -0
  127. package/dist/codegen-loaders.js.map +1 -0
  128. package/dist/codegen-mcp.d.ts.map +1 -1
  129. package/dist/codegen-mcp.js +368 -0
  130. package/dist/codegen-mcp.js.map +1 -0
  131. package/dist/codegen-python.d.ts +13 -1
  132. package/dist/codegen-python.d.ts.map +1 -1
  133. package/dist/codegen-python.js +828 -0
  134. package/dist/codegen-python.js.map +1 -0
  135. package/dist/codegen-route-tree-intern.d.ts +15 -5
  136. package/dist/codegen-route-tree-intern.d.ts.map +1 -1
  137. package/dist/codegen-route-tree-intern.js +221 -0
  138. package/dist/codegen-route-tree-intern.js.map +1 -0
  139. package/dist/codegen-rust.d.ts.map +1 -1
  140. package/dist/codegen-rust.js +1293 -0
  141. package/dist/codegen-rust.js.map +1 -0
  142. package/dist/codegen-sanitize.d.ts.map +1 -1
  143. package/dist/codegen-sanitize.js +56 -0
  144. package/dist/codegen-sanitize.js.map +1 -0
  145. package/dist/codegen-schema-naming.d.ts +5 -0
  146. package/dist/codegen-schema-naming.d.ts.map +1 -1
  147. package/dist/codegen-schema-naming.js +150 -0
  148. package/dist/codegen-schema-naming.js.map +1 -0
  149. package/dist/codegen-sdk-model.d.ts +71 -0
  150. package/dist/codegen-sdk-model.d.ts.map +1 -0
  151. package/dist/codegen-sdk-model.js +179 -0
  152. package/dist/codegen-sdk-model.js.map +1 -0
  153. package/dist/codegen.d.ts +41 -23
  154. package/dist/codegen.d.ts.map +1 -1
  155. package/dist/codegen.js +4610 -0
  156. package/dist/codegen.js.map +1 -0
  157. package/dist/context.d.ts +36 -6
  158. package/dist/context.d.ts.map +1 -1
  159. package/dist/context.js +242 -0
  160. package/dist/context.js.map +1 -0
  161. package/dist/cookie-sign.d.ts +16 -2
  162. package/dist/cookie-sign.d.ts.map +1 -1
  163. package/dist/cookie-sign.js +104 -0
  164. package/dist/cookie-sign.js.map +1 -0
  165. package/dist/cookie.d.ts +12 -0
  166. package/dist/cookie.d.ts.map +1 -1
  167. package/dist/cookie.js +138 -0
  168. package/dist/cookie.js.map +1 -0
  169. package/dist/cors.d.ts +1 -0
  170. package/dist/cors.d.ts.map +1 -1
  171. package/dist/cors.js +87 -0
  172. package/dist/cors.js.map +1 -0
  173. package/dist/crypto.d.ts +6 -1
  174. package/dist/crypto.d.ts.map +1 -1
  175. package/dist/crypto.js +38 -0
  176. package/dist/crypto.js.map +1 -0
  177. package/dist/csrf.d.ts +19 -0
  178. package/dist/csrf.d.ts.map +1 -1
  179. package/dist/csrf.js +79 -0
  180. package/dist/csrf.js.map +1 -0
  181. package/dist/curl-logger.d.ts +14 -3
  182. package/dist/curl-logger.d.ts.map +1 -1
  183. package/dist/curl-logger.js +199 -0
  184. package/dist/curl-logger.js.map +1 -0
  185. package/dist/detect-runtime.js +13 -0
  186. package/dist/detect-runtime.js.map +1 -0
  187. package/dist/dict.d.ts +11 -0
  188. package/dist/dict.d.ts.map +1 -0
  189. package/dist/dict.js +13 -0
  190. package/dist/dict.js.map +1 -0
  191. package/dist/error.d.ts.map +1 -1
  192. package/dist/error.js +44 -0
  193. package/dist/error.js.map +1 -0
  194. package/dist/errors.js +39 -0
  195. package/dist/errors.js.map +1 -0
  196. package/dist/etag.d.ts +2 -0
  197. package/dist/etag.d.ts.map +1 -1
  198. package/dist/etag.js +82 -0
  199. package/dist/etag.js.map +1 -0
  200. package/dist/feature-detect.d.ts +25 -0
  201. package/dist/feature-detect.d.ts.map +1 -0
  202. package/dist/feature-detect.js +144 -0
  203. package/dist/feature-detect.js.map +1 -0
  204. package/dist/feature-load.js +36 -0
  205. package/dist/feature-load.js.map +1 -0
  206. package/dist/feature-slots.d.ts +44 -0
  207. package/dist/feature-slots.d.ts.map +1 -0
  208. package/dist/feature-slots.js +48 -0
  209. package/dist/feature-slots.js.map +1 -0
  210. package/dist/fetch-request.d.ts +10 -0
  211. package/dist/fetch-request.d.ts.map +1 -0
  212. package/dist/fetch-request.js +13 -0
  213. package/dist/fetch-request.js.map +1 -0
  214. package/dist/gen-process.d.ts +15 -0
  215. package/dist/gen-process.d.ts.map +1 -0
  216. package/dist/gen-process.js +43 -0
  217. package/dist/gen-process.js.map +1 -0
  218. package/dist/gen-write.d.ts +19 -0
  219. package/dist/gen-write.d.ts.map +1 -0
  220. package/dist/gen-write.js +120 -0
  221. package/dist/gen-write.js.map +1 -0
  222. package/dist/glob.d.ts +21 -0
  223. package/dist/glob.d.ts.map +1 -0
  224. package/dist/glob.js +115 -0
  225. package/dist/glob.js.map +1 -0
  226. package/dist/go-type-emitter.d.ts +18 -4
  227. package/dist/go-type-emitter.d.ts.map +1 -1
  228. package/dist/go-type-emitter.js +594 -0
  229. package/dist/go-type-emitter.js.map +1 -0
  230. package/dist/honey-response.d.ts.map +1 -1
  231. package/dist/honey-response.js +338 -0
  232. package/dist/honey-response.js.map +1 -0
  233. package/dist/i18n-slot.d.ts +8 -0
  234. package/dist/i18n-slot.d.ts.map +1 -1
  235. package/dist/i18n-slot.js +14 -0
  236. package/dist/i18n-slot.js.map +1 -0
  237. package/dist/i18n.d.ts +3 -0
  238. package/dist/i18n.d.ts.map +1 -1
  239. package/dist/i18n.js +250 -0
  240. package/dist/i18n.js.map +1 -0
  241. package/dist/index.d.ts +408 -148
  242. package/dist/index.d.ts.map +1 -1
  243. package/dist/index.js +2324 -0
  244. package/dist/index.js.map +1 -0
  245. package/dist/init.d.ts +3 -5
  246. package/dist/init.d.ts.map +1 -1
  247. package/dist/init.js +140 -0
  248. package/dist/init.js.map +1 -0
  249. package/dist/input.js +4 -0
  250. package/dist/input.js.map +1 -0
  251. package/dist/invalidate-check.d.ts +11 -0
  252. package/dist/invalidate-check.d.ts.map +1 -1
  253. package/dist/invalidate-check.js +163 -0
  254. package/dist/invalidate-check.js.map +1 -0
  255. package/dist/invoke-user.d.ts +35 -0
  256. package/dist/invoke-user.d.ts.map +1 -0
  257. package/dist/invoke-user.js +73 -0
  258. package/dist/invoke-user.js.map +1 -0
  259. package/dist/ip-restrict.d.ts +13 -1
  260. package/dist/ip-restrict.d.ts.map +1 -1
  261. package/dist/ip-restrict.js +60 -0
  262. package/dist/ip-restrict.js.map +1 -0
  263. package/dist/ip.d.ts +39 -0
  264. package/dist/ip.d.ts.map +1 -0
  265. package/dist/ip.js +209 -0
  266. package/dist/ip.js.map +1 -0
  267. package/dist/logger.d.ts.map +1 -1
  268. package/dist/logger.js +113 -0
  269. package/dist/logger.js.map +1 -0
  270. package/dist/media-type.d.ts +30 -0
  271. package/dist/media-type.d.ts.map +1 -0
  272. package/dist/media-type.js +97 -0
  273. package/dist/media-type.js.map +1 -0
  274. package/dist/meta-spec-merge.d.ts +32 -0
  275. package/dist/meta-spec-merge.d.ts.map +1 -0
  276. package/dist/meta-spec-merge.js +68 -0
  277. package/dist/meta-spec-merge.js.map +1 -0
  278. package/dist/meta-spec.d.ts +16 -3
  279. package/dist/meta-spec.d.ts.map +1 -1
  280. package/dist/meta-spec.js +535 -0
  281. package/dist/meta-spec.js.map +1 -0
  282. package/dist/middleware.d.ts +23 -6
  283. package/dist/middleware.d.ts.map +1 -1
  284. package/dist/middleware.js +253 -0
  285. package/dist/middleware.js.map +1 -0
  286. package/dist/node-request.d.ts +57 -12
  287. package/dist/node-request.d.ts.map +1 -1
  288. package/dist/node-request.js +407 -0
  289. package/dist/node-request.js.map +1 -0
  290. package/dist/node.d.ts +20 -3
  291. package/dist/node.d.ts.map +1 -1
  292. package/dist/node.js +396 -0
  293. package/dist/node.js.map +1 -0
  294. package/dist/openapi/collect.d.ts +6 -3
  295. package/dist/openapi/collect.d.ts.map +1 -1
  296. package/dist/openapi/collect.js +30 -0
  297. package/dist/openapi/collect.js.map +1 -0
  298. package/dist/openapi/docs-page.d.ts +42 -0
  299. package/dist/openapi/docs-page.d.ts.map +1 -0
  300. package/dist/openapi/docs-page.js +80 -0
  301. package/dist/openapi/docs-page.js.map +1 -0
  302. package/dist/openapi/document.d.ts +4 -0
  303. package/dist/openapi/document.d.ts.map +1 -1
  304. package/dist/openapi/document.js +785 -0
  305. package/dist/openapi/document.js.map +1 -0
  306. package/dist/openapi/json-schema-slot.d.ts +7 -0
  307. package/dist/openapi/json-schema-slot.d.ts.map +1 -1
  308. package/dist/openapi/json-schema-slot.js +38 -0
  309. package/dist/openapi/json-schema-slot.js.map +1 -0
  310. package/dist/openapi/register.d.ts.map +1 -1
  311. package/dist/openapi/register.js +20 -0
  312. package/dist/openapi/register.js.map +1 -0
  313. package/dist/openapi/scalar.d.ts +3 -1
  314. package/dist/openapi/scalar.d.ts.map +1 -1
  315. package/dist/openapi/scalar.js +21 -0
  316. package/dist/openapi/scalar.js.map +1 -0
  317. package/dist/openapi/serve.d.ts +9 -0
  318. package/dist/openapi/serve.d.ts.map +1 -0
  319. package/dist/openapi/serve.js +36 -0
  320. package/dist/openapi/serve.js.map +1 -0
  321. package/dist/openapi/spec-factory.d.ts +30 -1
  322. package/dist/openapi/spec-factory.d.ts.map +1 -1
  323. package/dist/openapi/spec-factory.js +52 -0
  324. package/dist/openapi/spec-factory.js.map +1 -0
  325. package/dist/openapi/spec.d.ts +14 -5
  326. package/dist/openapi/spec.d.ts.map +1 -1
  327. package/dist/openapi/spec.js +50 -0
  328. package/dist/openapi/spec.js.map +1 -0
  329. package/dist/openapi/swagger.d.ts +3 -1
  330. package/dist/openapi/swagger.d.ts.map +1 -1
  331. package/dist/openapi/swagger.js +22 -0
  332. package/dist/openapi/swagger.js.map +1 -0
  333. package/dist/pattern.d.ts +71 -0
  334. package/dist/pattern.d.ts.map +1 -0
  335. package/dist/pattern.js +239 -0
  336. package/dist/pattern.js.map +1 -0
  337. package/dist/peer.d.ts +17 -0
  338. package/dist/peer.d.ts.map +1 -0
  339. package/dist/peer.js +50 -0
  340. package/dist/peer.js.map +1 -0
  341. package/dist/plugin.d.ts +67 -16
  342. package/dist/plugin.d.ts.map +1 -1
  343. package/dist/plugin.js +649 -0
  344. package/dist/plugin.js.map +1 -0
  345. package/dist/powered-by.d.ts.map +1 -1
  346. package/dist/powered-by.js +11 -0
  347. package/dist/powered-by.js.map +1 -0
  348. package/dist/pretty-json.d.ts.map +1 -1
  349. package/dist/pretty-json.js +47 -0
  350. package/dist/pretty-json.js.map +1 -0
  351. package/dist/producer-stream.d.ts +47 -0
  352. package/dist/producer-stream.d.ts.map +1 -0
  353. package/dist/producer-stream.js +267 -0
  354. package/dist/producer-stream.js.map +1 -0
  355. package/dist/proxy.d.ts +47 -8
  356. package/dist/proxy.d.ts.map +1 -1
  357. package/dist/proxy.js +274 -0
  358. package/dist/proxy.js.map +1 -0
  359. package/dist/python-type-emitter.d.ts +14 -1
  360. package/dist/python-type-emitter.d.ts.map +1 -1
  361. package/dist/python-type-emitter.js +157 -0
  362. package/dist/python-type-emitter.js.map +1 -0
  363. package/dist/realtime/bus.d.ts +17 -1
  364. package/dist/realtime/bus.d.ts.map +1 -1
  365. package/dist/realtime/bus.js +112 -0
  366. package/dist/realtime/bus.js.map +1 -0
  367. package/dist/realtime/register.d.ts +3 -0
  368. package/dist/realtime/register.d.ts.map +1 -0
  369. package/dist/realtime/register.js +10 -0
  370. package/dist/realtime/register.js.map +1 -0
  371. package/dist/realtime/route.d.ts +65 -26
  372. package/dist/realtime/route.d.ts.map +1 -1
  373. package/dist/realtime/route.js +72 -0
  374. package/dist/realtime/route.js.map +1 -0
  375. package/dist/realtime/server.d.ts +43 -0
  376. package/dist/realtime/server.d.ts.map +1 -0
  377. package/dist/realtime/server.js +255 -0
  378. package/dist/realtime/server.js.map +1 -0
  379. package/dist/request-id.d.ts +7 -0
  380. package/dist/request-id.d.ts.map +1 -1
  381. package/dist/request-id.js +16 -0
  382. package/dist/request-id.js.map +1 -0
  383. package/dist/request-limits.d.ts +3 -0
  384. package/dist/request-limits.d.ts.map +1 -0
  385. package/dist/request-limits.js +3 -0
  386. package/dist/request-limits.js.map +1 -0
  387. package/dist/request-path.d.ts +33 -0
  388. package/dist/request-path.d.ts.map +1 -0
  389. package/dist/request-path.js +178 -0
  390. package/dist/request-path.js.map +1 -0
  391. package/dist/request-to-curl.d.ts +7 -1
  392. package/dist/request-to-curl.d.ts.map +1 -1
  393. package/dist/request-to-curl.js +48 -0
  394. package/dist/request-to-curl.js.map +1 -0
  395. package/dist/response.d.ts +25 -2
  396. package/dist/response.d.ts.map +1 -1
  397. package/dist/response.js +313 -0
  398. package/dist/response.js.map +1 -0
  399. package/dist/rust-type-emitter.d.ts +14 -2
  400. package/dist/rust-type-emitter.d.ts.map +1 -1
  401. package/dist/rust-type-emitter.js +463 -0
  402. package/dist/rust-type-emitter.js.map +1 -0
  403. package/dist/secure-headers.d.ts.map +1 -1
  404. package/dist/secure-headers.js +53 -0
  405. package/dist/secure-headers.js.map +1 -0
  406. package/dist/serve-register.d.ts +4 -0
  407. package/dist/serve-register.d.ts.map +1 -1
  408. package/dist/serve-register.js +9 -0
  409. package/dist/serve-register.js.map +1 -0
  410. package/dist/serve-slot.d.ts +3 -0
  411. package/dist/serve-slot.d.ts.map +1 -1
  412. package/dist/serve-slot.js +52 -0
  413. package/dist/serve-slot.js.map +1 -0
  414. package/dist/serve.d.ts +18 -1
  415. package/dist/serve.d.ts.map +1 -1
  416. package/dist/serve.js +157 -0
  417. package/dist/serve.js.map +1 -0
  418. package/dist/server-timing.d.ts.map +1 -1
  419. package/dist/server-timing.js +51 -0
  420. package/dist/server-timing.js.map +1 -0
  421. package/dist/static.d.ts +9 -0
  422. package/dist/static.d.ts.map +1 -1
  423. package/dist/static.js +102 -0
  424. package/dist/static.js.map +1 -0
  425. package/dist/telemetry/otel.d.ts +2 -0
  426. package/dist/telemetry/otel.d.ts.map +1 -1
  427. package/dist/telemetry/otel.js +135 -0
  428. package/dist/telemetry/otel.js.map +1 -0
  429. package/dist/testing.d.ts +12 -0
  430. package/dist/testing.d.ts.map +1 -1
  431. package/dist/testing.js +124 -0
  432. package/dist/testing.js.map +1 -0
  433. package/dist/timeout.d.ts +6 -0
  434. package/dist/timeout.d.ts.map +1 -1
  435. package/dist/timeout.js +36 -0
  436. package/dist/timeout.js.map +1 -0
  437. package/dist/tree.d.ts +133 -36
  438. package/dist/tree.d.ts.map +1 -1
  439. package/dist/tree.js +437 -0
  440. package/dist/tree.js.map +1 -0
  441. package/dist/trust.d.ts +21 -0
  442. package/dist/trust.d.ts.map +1 -0
  443. package/dist/trust.js +100 -0
  444. package/dist/trust.js.map +1 -0
  445. package/dist/ts-type-emitter.d.ts.map +1 -1
  446. package/dist/ts-type-emitter.js +150 -0
  447. package/dist/ts-type-emitter.js.map +1 -0
  448. package/dist/type-emitter.d.ts +16 -2
  449. package/dist/type-emitter.d.ts.map +1 -1
  450. package/dist/type-emitter.js +729 -0
  451. package/dist/type-emitter.js.map +1 -0
  452. package/dist/type-extractor.d.ts +8 -4
  453. package/dist/type-extractor.d.ts.map +1 -1
  454. package/dist/type-extractor.js +776 -0
  455. package/dist/type-extractor.js.map +1 -0
  456. package/dist/types.d.ts +7 -0
  457. package/dist/types.d.ts.map +1 -1
  458. package/dist/types.js +96 -0
  459. package/dist/types.js.map +1 -0
  460. package/dist/validation.d.ts +14 -2
  461. package/dist/validation.d.ts.map +1 -1
  462. package/dist/validation.js +335 -0
  463. package/dist/validation.js.map +1 -0
  464. package/dist/with-headers.d.ts +15 -0
  465. package/dist/with-headers.d.ts.map +1 -0
  466. package/dist/with-headers.js +45 -0
  467. package/dist/with-headers.js.map +1 -0
  468. package/dist/ws/bun.d.ts +11 -5
  469. package/dist/ws/bun.d.ts.map +1 -1
  470. package/dist/ws/bun.js +82 -0
  471. package/dist/ws/bun.js.map +1 -0
  472. package/dist/ws/cloudflare.d.ts +25 -3
  473. package/dist/ws/cloudflare.d.ts.map +1 -1
  474. package/dist/ws/cloudflare.js +117 -0
  475. package/dist/ws/cloudflare.js.map +1 -0
  476. package/dist/ws/deno.d.ts +4 -4
  477. package/dist/ws/deno.d.ts.map +1 -1
  478. package/dist/ws/deno.js +133 -0
  479. package/dist/ws/deno.js.map +1 -0
  480. package/dist/ws/node.d.ts +20 -3
  481. package/dist/ws/node.d.ts.map +1 -1
  482. package/dist/ws/node.js +173 -0
  483. package/dist/ws/node.js.map +1 -0
  484. package/dist/ws/session.d.ts +11 -0
  485. package/dist/ws/session.d.ts.map +1 -0
  486. package/dist/ws/session.js +60 -0
  487. package/dist/ws/session.js.map +1 -0
  488. package/dist/ws/shared.d.ts +65 -0
  489. package/dist/ws/shared.d.ts.map +1 -0
  490. package/dist/ws/shared.js +103 -0
  491. package/dist/ws/shared.js.map +1 -0
  492. package/dist/ws-origin.d.ts +20 -0
  493. package/dist/ws-origin.d.ts.map +1 -0
  494. package/dist/ws-origin.js +41 -0
  495. package/dist/ws-origin.js.map +1 -0
  496. package/dist/yaml.d.ts +15 -1
  497. package/dist/yaml.d.ts.map +1 -1
  498. package/dist/yaml.js +101 -0
  499. package/dist/yaml.js.map +1 -0
  500. package/docs/invalidate-spec.md +13 -1
  501. package/docs/meta-spec.md +36 -12
  502. package/docs/packaging.md +43 -24
  503. package/docs/regression-matrix/README.md +32 -0
  504. package/docs/regression-matrix/review.md +44 -0
  505. package/docs/regression-matrix/ws1-3.md +97 -0
  506. package/docs/regression-matrix/ws4-8.md +107 -0
  507. package/docs/regression-matrix/ws9-12.md +266 -0
  508. package/docs/sdk.md +102 -52
  509. package/package.json +183 -52
  510. package/src/accepts.ts +91 -33
  511. package/src/body-kind.ts +67 -0
  512. package/src/body-limit.ts +102 -87
  513. package/src/build/index.ts +104 -54
  514. package/src/cli-args.ts +123 -0
  515. package/src/cli-go/auth.go +12 -1
  516. package/src/cli-go/config.go +29 -2
  517. package/src/cli-go/errors.go +28 -10
  518. package/src/cli-go/multipart.go +51 -27
  519. package/src/cli-go/output.go +58 -4
  520. package/src/cli-go/root.go +1 -1
  521. package/src/cli-go/version.go +2 -2
  522. package/src/cli.ts +154 -148
  523. package/src/client/error.ts +56 -1
  524. package/src/client/http.ts +479 -256
  525. package/src/client/index.ts +55 -17
  526. package/src/client/path.ts +169 -0
  527. package/src/client/redirect-policy.ts +19 -0
  528. package/src/client/sdk.ts +180 -101
  529. package/src/client/sse.ts +92 -66
  530. package/src/client/ws.ts +67 -31
  531. package/src/client-go/errors.go +41 -12
  532. package/src/client-go/export.go +44 -0
  533. package/src/client-go/invalidation.go +52 -54
  534. package/src/client-go/nullable.go +62 -0
  535. package/src/client-go/realtime.go +147 -110
  536. package/src/client-go/runtime.go +646 -88
  537. package/src/client-go/sse.go +104 -94
  538. package/src/client-go/transport.go +67 -27
  539. package/src/client-go/ws.go +49 -25
  540. package/src/client-info.ts +79 -0
  541. package/src/client-mcp/server.ts +133 -3
  542. package/src/client-python/_invalidation.py +207 -291
  543. package/src/client-python/_realtime.py +63 -40
  544. package/src/client-python/_runtime.py +501 -119
  545. package/src/client-python/_sse.py +130 -74
  546. package/src/client-python/_transport.py +37 -9
  547. package/src/client-python/_ws.py +50 -22
  548. package/src/client-rust/errors.rs +21 -1
  549. package/src/client-rust/invalidation.rs +202 -231
  550. package/src/client-rust/invalidation_sync.rs +16 -229
  551. package/src/client-rust/nullable.rs +17 -0
  552. package/src/client-rust/realtime.rs +217 -217
  553. package/src/client-rust/runtime.rs +602 -177
  554. package/src/client-rust/runtime_sync.rs +169 -145
  555. package/src/client-rust/sse.rs +142 -121
  556. package/src/client-rust/ws.rs +61 -37
  557. package/src/codegen-go-cli.ts +607 -728
  558. package/src/codegen-go.ts +710 -770
  559. package/src/codegen-ir.ts +381 -75
  560. package/src/codegen-lang.ts +517 -0
  561. package/src/codegen-load.ts +150 -0
  562. package/src/codegen-mcp.ts +207 -108
  563. package/src/codegen-python.ts +684 -816
  564. package/src/codegen-route-tree-intern.ts +51 -25
  565. package/src/codegen-rust.ts +946 -1240
  566. package/src/codegen-sanitize.ts +0 -4
  567. package/src/codegen-schema-naming.ts +19 -6
  568. package/src/codegen-sdk-model.ts +228 -0
  569. package/src/codegen.ts +1488 -1140
  570. package/src/context.ts +130 -11
  571. package/src/cookie-sign.ts +85 -16
  572. package/src/cookie.ts +66 -11
  573. package/src/cors.ts +55 -54
  574. package/src/crypto.ts +27 -5
  575. package/src/csrf.ts +57 -25
  576. package/src/curl-logger.ts +155 -114
  577. package/src/dict.ts +12 -0
  578. package/src/error.ts +4 -2
  579. package/src/etag.ts +58 -34
  580. package/src/feature-detect.ts +147 -0
  581. package/src/feature-slots.ts +70 -0
  582. package/src/fetch-request.ts +13 -0
  583. package/src/gen-process.ts +42 -0
  584. package/src/gen-write.ts +127 -0
  585. package/src/glob.ts +109 -0
  586. package/src/go-type-emitter.ts +406 -155
  587. package/src/honey-response.ts +36 -9
  588. package/src/i18n-slot.ts +10 -0
  589. package/src/i18n.ts +98 -23
  590. package/src/index.ts +1887 -1756
  591. package/src/init.ts +40 -28
  592. package/src/invalidate-check.ts +42 -4
  593. package/src/invoke-user.ts +95 -0
  594. package/src/ip-restrict.ts +47 -161
  595. package/src/ip.ts +194 -0
  596. package/src/logger.ts +65 -36
  597. package/src/media-type.ts +104 -0
  598. package/src/meta-spec-merge.ts +80 -0
  599. package/src/meta-spec.ts +0 -0
  600. package/src/middleware.ts +112 -12
  601. package/src/node-request.ts +242 -52
  602. package/src/node.ts +318 -75
  603. package/src/openapi/collect.ts +19 -44
  604. package/src/openapi/docs-page.ts +89 -0
  605. package/src/openapi/document.ts +503 -224
  606. package/src/openapi/json-schema-slot.ts +40 -0
  607. package/src/openapi/register.ts +7 -2
  608. package/src/openapi/scalar.ts +11 -10
  609. package/src/openapi/serve.ts +39 -0
  610. package/src/openapi/spec-factory.ts +50 -1
  611. package/src/openapi/spec.ts +53 -30
  612. package/src/openapi/swagger.ts +12 -11
  613. package/src/pattern.ts +245 -0
  614. package/src/peer.ts +53 -0
  615. package/src/plugin.ts +270 -185
  616. package/src/powered-by.ts +2 -2
  617. package/src/pretty-json.ts +25 -9
  618. package/src/producer-stream.ts +296 -0
  619. package/src/proxy.ts +253 -55
  620. package/src/python-type-emitter.ts +68 -56
  621. package/src/realtime/bus.ts +56 -9
  622. package/src/realtime/register.ts +13 -0
  623. package/src/realtime/route.ts +129 -80
  624. package/src/realtime/server.ts +298 -0
  625. package/src/request-id.ts +13 -3
  626. package/src/request-limits.ts +2 -0
  627. package/src/request-path.ts +165 -0
  628. package/src/request-to-curl.ts +28 -7
  629. package/src/response.ts +143 -103
  630. package/src/rust-type-emitter.ts +265 -170
  631. package/src/secure-headers.ts +8 -4
  632. package/src/serve-register.ts +4 -0
  633. package/src/serve-slot.ts +38 -0
  634. package/src/serve.ts +113 -25
  635. package/src/server-timing.ts +11 -7
  636. package/src/static.ts +85 -32
  637. package/src/telemetry/otel.ts +29 -7
  638. package/src/testing.ts +76 -14
  639. package/src/timeout.ts +15 -7
  640. package/src/tree.ts +405 -333
  641. package/src/trust.ts +117 -0
  642. package/src/ts-type-emitter.ts +18 -12
  643. package/src/type-emitter.ts +227 -89
  644. package/src/type-extractor.ts +562 -424
  645. package/src/types.ts +8 -0
  646. package/src/validation.ts +221 -96
  647. package/src/with-headers.ts +45 -0
  648. package/src/ws/bun.ts +53 -15
  649. package/src/ws/cloudflare.ts +81 -19
  650. package/src/ws/deno.ts +124 -36
  651. package/src/ws/node.ts +173 -79
  652. package/src/ws/session.ts +73 -0
  653. package/src/ws/shared.ts +140 -0
  654. package/src/ws-origin.ts +53 -0
  655. package/src/yaml.ts +45 -22
  656. package/dist/client-mcp/server.d.ts +0 -10
  657. package/dist/client-mcp/server.d.ts.map +0 -1
  658. package/dist/client-mcp/types.d.ts +0 -16
  659. package/dist/client-mcp/types.d.ts.map +0 -1
  660. package/dist/cloudflare/realtime.d.ts +0 -30
  661. package/dist/cloudflare/realtime.d.ts.map +0 -1
  662. package/dist/realtime/buffer.d.ts +0 -18
  663. package/dist/realtime/buffer.d.ts.map +0 -1
  664. package/dist/realtime/longpoll.d.ts +0 -23
  665. package/dist/realtime/longpoll.d.ts.map +0 -1
  666. package/dist/realtime/protocol.d.ts +0 -50
  667. package/dist/realtime/protocol.d.ts.map +0 -1
  668. package/dist/realtime/runtime.d.ts +0 -74
  669. package/dist/realtime/runtime.d.ts.map +0 -1
  670. package/src/cloudflare/realtime.ts +0 -234
  671. package/src/realtime/buffer.ts +0 -101
  672. package/src/realtime/longpoll.ts +0 -148
  673. package/src/realtime/protocol.ts +0 -128
  674. package/src/realtime/runtime.ts +0 -427
@@ -1,17 +1,21 @@
1
- import { HoneyError } from "../error.ts"
1
+ import { deriveSchemaName, sanitizeComponentName } from "../codegen-schema-naming.ts"
2
+ import type { HoneyError } from "../error.ts"
2
3
  import { ERROR_META } from "../errors.ts"
3
4
  import type { ErrorMetaEntry } from "../errors.ts"
4
5
  import type { InvalidateCheckConfig, InvalidateCheckOperation } from "../invalidate-check.ts"
5
- import { reportMissingInvalidate } from "../invalidate-check.ts"
6
+ import { findInvalidSelectors, invalidateLevel, reportMissingInvalidate } from "../invalidate-check.ts"
6
7
  import {
7
8
  applyMetaSpec,
8
9
  compileMetaSpec,
9
10
  MetaSpecCollector,
10
11
  resolveProfile,
12
+ securitySchemeNames,
11
13
  type SchemaMetaHit,
12
14
  type SchemaMetaLookup,
13
15
  } from "../meta-spec.ts"
14
- import type { RouteHandler, TreeNode } from "../tree.ts"
16
+ import { metaSpecOf } from "../meta-spec-merge.ts"
17
+ import { parsePattern, UNNAMED_WILDCARD } from "../pattern.ts"
18
+ import type { RouteHandler } from "../tree.ts"
15
19
  import type {
16
20
  InputSchemaEntry,
17
21
  InputSchemasDef,
@@ -20,8 +24,8 @@ import type {
20
24
  StandardSchemaLike,
21
25
  } from "../types.ts"
22
26
  import { statusKeyToCode } from "../types.ts"
23
- import { type CollectedWSRoute, extractParams, toOpenApiPath, unwrapEntry, walkTree, walkWSRoutes } from "./collect.ts"
24
- import { getJsonSchemaConverter } from "./json-schema-slot.ts"
27
+ import { collectRoutes, collectWsRoutes, toOpenApiPath, unwrapEntry } from "./collect.ts"
28
+ import { getJsonSchemaConverter, instanceJsonSchema } from "./json-schema-slot.ts"
25
29
 
26
30
  export type OpenApiRouteInfo<TMeta = unknown> = {
27
31
  meta: TMeta
@@ -43,6 +47,7 @@ export type OpenApiSpec = {
43
47
  info: OpenApiInfo
44
48
  openapi: string
45
49
  paths: Record<string, Record<string, Record<string, unknown>>>
50
+ servers?: Array<{ description?: string; url: string }>
46
51
  }
47
52
 
48
53
  export const DEFAULT_ERROR_JSON_SCHEMA: Record<string, unknown> = {
@@ -123,7 +128,7 @@ function getErrorMeta(factory: Record<string, () => HoneyError> | null): Record<
123
128
  }
124
129
 
125
130
  function getMetaSpecConfig(app: unknown): MetaSpecConfig | null {
126
- return (app as { _metaSpec?: MetaSpecConfig | null })._metaSpec ?? null
131
+ return metaSpecOf(app)
127
132
  }
128
133
 
129
134
  function cloneJson<T>(value: T): T {
@@ -180,9 +185,8 @@ function asJsonSchema(
180
185
  io: "input" | "output" = "output",
181
186
  ): Record<string, unknown> {
182
187
  if (entry !== null && entry !== undefined && "~standard" in (entry as object)) {
183
- const convert = getJsonSchemaConverter()
184
- if (convert) return convert(entry as StandardSchemaLike, io)
185
- return {}
188
+ const convert = getJsonSchemaConverter() ?? instanceJsonSchema
189
+ return convert(entry as StandardSchemaLike, io)
186
190
  }
187
191
  return entry as Record<string, unknown>
188
192
  }
@@ -286,6 +290,224 @@ function makeSchemaMetaLookup(handler: RouteHandler): SchemaMetaLookup {
286
290
  }
287
291
  }
288
292
 
293
+ /** Methods an OpenAPI path item can hold. Anything else cannot be described and is skipped. */
294
+ const OPENAPI_METHODS = new Set(["delete", "get", "head", "options", "patch", "post", "put", "trace"])
295
+
296
+ /** What `app.all()` documents: every method a client would send to it, unless the path declares that method itself. */
297
+ const ALL_METHODS = ["get", "post", "put", "patch", "delete"] as const
298
+
299
+ type AppSettingsView = { stripPrefix?: string | null; trailingSlash?: "enforce" | "ignore" | "strip" }
300
+
301
+ function appSettings(app: unknown): AppSettingsView {
302
+ return (app as { _graph?: { settings?: AppSettingsView } })._graph?.settings ?? {}
303
+ }
304
+
305
+ /** The URL path a client sends for a route path: `trailingSlash("enforce")` adds a slash. */
306
+ function publicPath(path: string, settings: AppSettingsView): string {
307
+ if (settings.trailingSlash === "enforce" && path.length > 1 && !path.endsWith("/")) return `${path}/`
308
+ return path
309
+ }
310
+
311
+ function isMetaInternal(meta: Record<string, unknown> | null): boolean {
312
+ return meta?.internal === true
313
+ }
314
+
315
+ /** Path parameters of a concrete route path. Every path parameter is required in OpenAPI. */
316
+ function pathParameters(path: string): Array<Record<string, unknown>> {
317
+ const out: Array<Record<string, unknown>> = []
318
+ for (const seg of parsePattern(path)) {
319
+ if (seg.k === "param") {
320
+ out.push({ in: "path", name: seg.n, required: true, schema: { type: "string" } })
321
+ } else if (seg.k === "wildcard") {
322
+ out.push({
323
+ description: "The rest of the path; may contain `/`.",
324
+ in: "path",
325
+ name: seg.n === UNNAMED_WILDCARD ? "wildcard" : seg.n,
326
+ required: true,
327
+ schema: { type: "string" },
328
+ "x-honey-wildcard": true,
329
+ })
330
+ }
331
+ }
332
+ return out
333
+ }
334
+
335
+ type ContentMap = Record<string, { schema: Record<string, unknown> }>
336
+
337
+ /** Add a response, merging content types and headers into one already declared for the status. */
338
+ function addResponse(
339
+ responses: Record<string, Record<string, unknown>>,
340
+ status: string,
341
+ description: string,
342
+ content: ContentMap | null,
343
+ headers: Record<string, unknown> | null,
344
+ ): void {
345
+ const existing = responses[status]
346
+ if (existing === undefined) {
347
+ const response: Record<string, unknown> = {}
348
+ if (content) response.content = content
349
+ response.description = description
350
+ if (headers) response.headers = headers
351
+ responses[status] = response
352
+ return
353
+ }
354
+ if (headers) existing.headers = { ...(existing.headers as Record<string, unknown> | undefined), ...headers }
355
+ if (content) existing.content = { ...(existing.content as ContentMap | undefined), ...content }
356
+ }
357
+
358
+ /**
359
+ * Turn a converted schema's local references into component references.
360
+ *
361
+ * Converters write `$ref: "#"` for recursion and `#/$defs/<name>` for shared nodes — relative
362
+ * to the schema's own root. In an OpenAPI document `#` is the document, so each is hoisted into
363
+ * `components.schemas` and the reference rewritten to point at it.
364
+ */
365
+ class LocalRefHoister {
366
+ readonly components: Record<string, Record<string, unknown>> = Object.create(null)
367
+
368
+ private readonly taken: (name: string) => boolean
369
+
370
+ constructor(taken: (name: string) => boolean) {
371
+ this.taken = taken
372
+ }
373
+
374
+ private reserve(base: string): string {
375
+ const clean = sanitizeComponentName(base) || "Schema"
376
+ let name = clean
377
+ for (let n = 2; this.taken(name) || Object.hasOwn(this.components, name); n++) name = `${clean}${n}`
378
+ this.components[name] = {}
379
+ return name
380
+ }
381
+
382
+ hoist(schema: Record<string, unknown>, hint: string): Record<string, unknown> {
383
+ const defs = schema.$defs ?? schema.definitions
384
+ const hasDefs = defs !== null && typeof defs === "object" && Object.keys(defs).length > 0
385
+ const selfRef = refersTo(schema, "#")
386
+ if (!hasDefs && !selfRef) return schema
387
+
388
+ const rootName = selfRef ? this.reserve(hint) : null
389
+ const defNames = new Map<string, string>()
390
+ if (hasDefs) {
391
+ for (const key of Object.keys(defs as Record<string, unknown>)) {
392
+ defNames.set(key, this.reserve(key.startsWith("__schema") ? `${hint}${key.slice(8)}` : key))
393
+ }
394
+ }
395
+ const rewrite = (ref: string): string | null => {
396
+ if (ref === "#" && rootName !== null) return `#/components/schemas/${rootName}`
397
+ for (const prefix of ["#/$defs/", "#/definitions/"]) {
398
+ if (!ref.startsWith(prefix)) continue
399
+ const name = defNames.get(decodePointer(ref.slice(prefix.length)))
400
+ if (name !== undefined) return `#/components/schemas/${name}`
401
+ }
402
+ return null
403
+ }
404
+ for (const [key, name] of defNames) {
405
+ this.components[name] = rewriteRefs((defs as Record<string, Record<string, unknown>>)[key], rewrite)
406
+ }
407
+ const { $defs: _defs, definitions: _definitions, ...rest } = schema
408
+ const root = rewriteRefs(rest, rewrite)
409
+ if (rootName === null) return root
410
+ this.components[rootName] = root
411
+ return { $ref: `#/components/schemas/${rootName}` }
412
+ }
413
+ }
414
+
415
+ function decodePointer(token: string): string {
416
+ return token.replace(/~1/g, "/").replace(/~0/g, "~")
417
+ }
418
+
419
+ /** Keys whose values are data, not schemas: a `$ref` inside them is a value, not a reference. */
420
+ const DATA_KEYS = new Set(["const", "default", "enum", "example", "examples"])
421
+
422
+ function refersTo(node: unknown, target: string): boolean {
423
+ if (Array.isArray(node)) return node.some((item) => refersTo(item, target))
424
+ if (node === null || typeof node !== "object") return false
425
+ for (const [key, value] of Object.entries(node)) {
426
+ if (key === "$ref" && value === target) return true
427
+ if (DATA_KEYS.has(key)) continue
428
+ if (refersTo(value, target)) return true
429
+ }
430
+ return false
431
+ }
432
+
433
+ function rewriteRefs(node: unknown, rewrite: (ref: string) => string | null): Record<string, unknown> {
434
+ const walk = (value: unknown): unknown => {
435
+ if (Array.isArray(value)) return value.map(walk)
436
+ if (value === null || typeof value !== "object") return value
437
+ const out: Record<string, unknown> = {}
438
+ for (const [key, child] of Object.entries(value)) {
439
+ if (key === "$ref" && typeof child === "string") {
440
+ out[key] = rewrite(child) ?? child
441
+ } else if (DATA_KEYS.has(key)) {
442
+ out[key] = child
443
+ } else {
444
+ out[key] = walk(child)
445
+ }
446
+ }
447
+ return out
448
+ }
449
+ return walk(node) as Record<string, unknown>
450
+ }
451
+
452
+ function operationIdOf(operation: Record<string, unknown>): string | null {
453
+ return typeof operation.operationId === "string" ? operation.operationId : null
454
+ }
455
+
456
+ type EmittedOperation = {
457
+ handler: object
458
+ /** What one registration shares across its operations: the handler function */
459
+ source: unknown
460
+ method: string
461
+ operation: Record<string, unknown>
462
+ path: string
463
+ /** Path params beyond the route's shortest variant, for optional-param suffixes */
464
+ params: string[]
465
+ }
466
+
467
+ /**
468
+ * Make `operationId`s unique. One route that the document expands into several operations
469
+ * (`.on(["GET","POST"])`, `all()`, an optional param) keeps one meta `operationId`; each
470
+ * operation gets a method or variant suffix. Two different routes declaring the same id is
471
+ * an authoring error.
472
+ */
473
+ function assignOperationIds(emitted: readonly EmittedOperation[], collector: MetaSpecCollector): void {
474
+ const byId = new Map<string, EmittedOperation[]>()
475
+ for (const entry of emitted) {
476
+ const id = operationIdOf(entry.operation)
477
+ if (id === null) continue
478
+ const list = byId.get(id)
479
+ if (list) list.push(entry)
480
+ else byId.set(id, [entry])
481
+ }
482
+ const used = new Set(byId.keys())
483
+ for (const [id, group] of byId) {
484
+ if (group.length < 2) continue
485
+ /* `.on([...])` registers one record per method around one handler function */
486
+ const handlers = new Set(group.map((g) => g.source))
487
+ if (handlers.size > 1) {
488
+ const where = group.map((g) => `${g.method.toUpperCase()} ${g.path}`).join(", ")
489
+ collector.add(
490
+ "DUPLICATE_OPERATION_ID",
491
+ "error",
492
+ `operationId "${id}" is declared by more than one route (${where}); operationIds must be unique`,
493
+ )
494
+ continue
495
+ }
496
+ const methods = new Set(group.map((g) => g.method))
497
+ const minParams = Math.min(...group.map((g) => g.params.length))
498
+ for (const entry of group) {
499
+ let next = id
500
+ if (methods.size > 1) next += `_${entry.method}`
501
+ const extra = entry.params.slice(minParams)
502
+ if (extra.length > 0) next += `_with_${extra.join("_")}`
503
+ if (next === id) continue
504
+ while (used.has(next)) next += "_"
505
+ used.add(next)
506
+ entry.operation.operationId = next
507
+ }
508
+ }
509
+ }
510
+
289
511
  export function generateOpenApiFromTree<TMeta = unknown>(
290
512
  app: unknown,
291
513
  options: {
@@ -301,6 +523,8 @@ export function generateOpenApiFromTree<TMeta = unknown>(
301
523
  const { filter: profileFilter, name: profileName } = resolveProfile(metaSpec, options.profile, collector)
302
524
  const factory = getErrorFactory(app)
303
525
  const errorMeta = getErrorMeta(factory)
526
+ const settings = appSettings(app)
527
+ const hoister = new LocalRefHoister(() => false)
304
528
 
305
529
  const rawErrorSchema = getErrorSchema(app)
306
530
  const baseErrorJsonSchema = rawErrorSchema ? asJsonSchema(rawErrorSchema) : DEFAULT_ERROR_JSON_SCHEMA
@@ -308,107 +532,87 @@ export function generateOpenApiFromTree<TMeta = unknown>(
308
532
  const rawCustomErrorSchema = getCustomErrorSchema(app)
309
533
  const customErrorAddsSchema = rawCustomErrorSchema ? asJsonSchema(rawCustomErrorSchema) : null
310
534
 
311
- const tree = (app as { _tree: TreeNode })._tree
312
- const collected: Array<{ handler: RouteHandler; method: string; path: string }> = []
313
- walkTree(tree, "", collected)
535
+ const collected = collectRoutes(app)
314
536
 
315
537
  const routeFilter = options.filterRoutes
316
- const paths: Record<string, Record<string, Record<string, unknown>>> = {}
538
+ const paths: Record<string, Record<string, Record<string, unknown>>> = Object.create(null)
317
539
  const emitted: InvalidateCheckOperation[] = []
540
+ const operations: EmittedOperation[] = []
318
541
 
319
- for (const { handler, method, path } of collected) {
320
- if (routeFilter) {
321
- const meta = handler.mt as TMeta
322
- if (!routeFilter({ meta, method, path })) continue
323
- }
324
- const oaPath = toOpenApiPath(path)
325
- if (paths[oaPath] === undefined) {
326
- paths[oaPath] = {}
327
- }
328
-
329
- const methodKey = method.toLowerCase()
330
- const operation: Record<string, unknown> = {}
331
- const responses: Record<string, unknown> = {}
332
- const parameters: Array<Record<string, unknown>> = []
333
-
334
- applyMetaSpec({
335
- collector,
336
- filter: profileFilter,
337
- kind: "http",
338
- meta: handler.mt as Record<string, unknown> | null,
339
- method,
340
- operation,
341
- path,
342
- profile: profileName,
343
- schemaMeta: metaSpec.needsSchemas ? makeSchemaMetaLookup(handler) : undefined,
344
- spec: metaSpec,
345
- })
542
+ /* explicit methods per path — `all()` documents only the methods nobody declared */
543
+ const declared = new Set<string>()
544
+ for (const { method, path } of collected) {
545
+ if (method !== "ALL") declared.add(`${method.toLowerCase()} ${path}`)
546
+ }
547
+ const shortestParams = new Map<object, number>()
548
+ for (const { handler, path } of collected) {
549
+ const n = pathParameters(path).length
550
+ const prev = shortestParams.get(handler)
551
+ if (prev === undefined || n < prev) shortestParams.set(handler, n)
552
+ }
346
553
 
347
- const params = extractParams(path)
348
- for (const name of params) {
349
- const isOptional = path.includes(`:${name}?`)
350
- parameters.push({
351
- in: "path",
352
- name,
353
- required: !isOptional,
354
- schema: { type: "string" },
355
- })
356
- }
554
+ for (const { handler, method, path } of collected) {
555
+ const meta = (handler.mt ?? null) as Record<string, unknown> | null
556
+ if (isMetaInternal(meta)) continue
557
+ if (routeFilter && !routeFilter({ meta: (meta ?? {}) as TMeta, method, path })) continue
558
+
559
+ const methods =
560
+ method === "ALL"
561
+ ? ALL_METHODS.filter((m) => !declared.has(`${m} ${path}`))
562
+ : [method.toLowerCase()].filter((m) => {
563
+ if (OPENAPI_METHODS.has(m)) return true
564
+ collector.add(
565
+ "UNSUPPORTED_METHOD",
566
+ "warn",
567
+ `${method} ${path}: OpenAPI cannot describe the ${method} method; the route is left out of the document`,
568
+ )
569
+ return false
570
+ })
571
+ if (methods.length === 0) continue
572
+
573
+ const oaPath = publicPath(toOpenApiPath(path), settings)
574
+ const params = pathParameters(path)
575
+ const schemaHint = (role: "request" | "response", status?: number): string =>
576
+ role === "request"
577
+ ? deriveSchemaName({ method: methods[0], path: oaPath, role })
578
+ : deriveSchemaName({ method: methods[0], path: oaPath, role, status: status ?? 200 })
579
+
580
+ /* the parts that do not depend on the method are built once per route */
581
+ const parameters: Array<Record<string, unknown>> = params.map((p) => ({ ...p }))
582
+ let requestBody: Record<string, unknown> | null = null
583
+ const responses: Record<string, Record<string, unknown>> = {}
357
584
 
358
585
  if (handler.iv) {
586
+ const body: ContentMap = {}
359
587
  for (const [source, entry] of Object.entries(handler.iv)) {
360
588
  if (entry === undefined) continue
361
589
  const unwrapped = unwrapEntry(entry as InputSchemaEntry)
362
590
  const jsonSchema = asJsonSchema(unwrapped, "input")
363
591
 
364
592
  if (source === "json" || source === "form") {
365
- const schema = stripInternalProps(jsonSchema)
366
- const content: Record<string, { schema: Record<string, unknown> }> = {}
593
+ const schema = hoister.hoist(stripInternalProps(jsonSchema), schemaHint("request"))
367
594
  if (source === "form") {
368
- if (formSchemaHasBinaryPart(schema)) {
369
- content["multipart/form-data"] = { schema }
370
- }
371
- content["application/x-www-form-urlencoded"] = { schema }
595
+ if (formSchemaHasBinaryPart(jsonSchema)) body["multipart/form-data"] = { schema }
596
+ body["application/x-www-form-urlencoded"] = { schema }
372
597
  } else {
373
- content["application/json"] = { schema }
374
- }
375
- operation.requestBody = {
376
- content,
377
- required: true,
598
+ body["application/json"] = { schema }
378
599
  }
379
600
  } else if (source === "search" || source === "headers" || source === "cookies") {
380
601
  let location: "cookie" | "header" | "query" = "cookie"
381
602
  if (source === "search") location = "query"
382
603
  else if (source === "headers") location = "header"
383
- const props = jsonSchema.properties as Record<string, unknown> | undefined
384
- const required = (jsonSchema.required as string[]) ?? []
385
- if (props) {
386
- for (const [propName, propSchema] of Object.entries(props)) {
387
- if ((propSchema as Record<string, unknown>)?.["x-internal"] === true) continue
388
- parameters.push({
389
- in: location,
390
- name: propName,
391
- required: required.includes(propName),
392
- schema: propSchema,
393
- })
394
- }
395
- }
604
+ parameters.push(...propertyParameters(jsonSchema, location, hoister, schemaHint("request")))
396
605
  } else if (source === "params") {
397
606
  const props = jsonSchema.properties as Record<string, unknown> | undefined
398
607
  if (props) {
399
608
  for (const param of parameters) {
400
- const propSchema = props[param.name as string]
401
- if (propSchema && param.in === "path") {
402
- param.schema = propSchema
403
- }
609
+ if (param.in !== "path" || !Object.hasOwn(props, param.name as string)) continue
610
+ param.schema = props[param.name as string]
404
611
  }
405
612
  }
406
613
  }
407
614
  }
408
- }
409
-
410
- if (parameters.length > 0) {
411
- operation.parameters = parameters
615
+ if (Object.keys(body).length > 0) requestBody = { content: body, required: true }
412
616
  }
413
617
 
414
618
  if (handler.os) {
@@ -417,138 +621,106 @@ export function generateOpenApiFromTree<TMeta = unknown>(
417
621
  if (contentType === "redirect") {
418
622
  for (const statusKey of Object.keys(schemas)) {
419
623
  const statusCode = statusKeyToCode[statusKey as keyof typeof statusKeyToCode]
420
- if (statusCode) {
421
- responses[String(statusCode)] = {
422
- description: statusKey.replace(/_/g, " "),
423
- headers: {
424
- Location: {
425
- description: "Redirect target URL",
426
- schema: { format: "uri", type: "string" },
427
- },
428
- },
429
- }
430
- }
624
+ if (!statusCode) continue
625
+ addResponse(responses, String(statusCode), statusKey.replace(/_/g, " "), null, {
626
+ Location: {
627
+ description: "Redirect target URL",
628
+ schema: { format: "uri", type: "string" },
629
+ },
630
+ })
431
631
  }
432
632
  continue
433
633
  }
434
634
  for (const [statusKey, schema] of Object.entries(schemas)) {
435
635
  if (schema === undefined) continue
436
636
  const statusCode = statusKeyToCode[statusKey as keyof typeof statusKeyToCode]
437
- if (statusCode) {
438
- responses[String(statusCode)] = {
439
- content: {
440
- [contentType]: {
441
- schema: asJsonSchema(schema as StandardSchemaLike | Record<string, unknown>),
442
- },
443
- },
444
- description: statusKey.replace(/_/g, " "),
445
- }
446
- }
637
+ if (!statusCode) continue
638
+ const converted = asJsonSchema(schema as StandardSchemaLike | Record<string, unknown>)
639
+ addResponse(
640
+ responses,
641
+ String(statusCode),
642
+ statusKey.replace(/_/g, " "),
643
+ { [contentType]: { schema: hoister.hoist(converted, schemaHint("response", statusCode)) } },
644
+ null,
645
+ )
447
646
  }
448
647
  }
449
648
  }
450
649
 
451
650
  if (handler.ek.size > 0) {
452
- type ErrorEntry = { key: string; schema: Record<string, unknown> | null }
453
- const byStatus = new Map<number, ErrorEntry[]>()
454
- for (const ek of handler.ek) {
455
- const info = resolveErrorInfo(ek, factory)
456
- if (info.status > 0) {
457
- let entries = byStatus.get(info.status)
458
- if (!entries) {
459
- entries = []
460
- byStatus.set(info.status, entries)
461
- }
462
- const meta = errorMeta?.[ek]
463
- let customSchema: Record<string, unknown> | null = null
464
- if (meta?.schema) {
465
- const converted = asJsonSchema(meta.schema as StandardSchemaLike)
466
- if (converted && Object.keys(converted).length > 0) {
467
- customSchema = customErrorAddsSchema ? { allOf: [converted, customErrorAddsSchema] } : converted
468
- }
469
- }
470
- entries.push({ key: ek, schema: customSchema })
471
- }
472
- }
473
- for (const [status, entries] of byStatus) {
474
- if (responses[String(status)] === undefined) {
475
- const standardKeys = entries.filter((e) => !e.schema).map((e) => e.key)
476
- const customSchemas = entries.filter((e) => e.schema).map((e) => e.schema as Record<string, unknown>)
477
-
478
- let schema: Record<string, unknown>
479
-
480
- if (customSchemas.length === 0) {
481
- schema = cloneJson(baseErrorJsonSchema)
482
- const props = schema.properties as Record<string, unknown> | undefined
483
- if (props?.error_key) {
484
- props.error_key = { enum: standardKeys.sort(), type: "string" }
485
- }
486
- if (props?.status) {
487
- props.status = { enum: [status], type: "integer" }
488
- }
489
- } else if (standardKeys.length === 0 && customSchemas.length === 1) {
490
- schema = customSchemas[0]
491
- } else {
492
- const schemas: Record<string, unknown>[] = []
493
- if (standardKeys.length > 0) {
494
- const stdSchema = cloneJson(baseErrorJsonSchema)
495
- const props = stdSchema.properties as Record<string, unknown> | undefined
496
- if (props?.error_key) {
497
- props.error_key = { enum: standardKeys.sort(), type: "string" }
498
- }
499
- if (props?.status) {
500
- props.status = { enum: [status], type: "integer" }
501
- }
502
- schemas.push(stdSchema)
503
- }
504
- schemas.push(...customSchemas)
505
- schema = { oneOf: schemas }
506
- }
507
-
508
- responses[String(status)] = {
509
- content: {
510
- "application/json": { schema },
511
- },
512
- description: entries
513
- .map((e) => e.key)
514
- .sort()
515
- .join(", "),
516
- }
517
- }
518
- }
651
+ /* a mounted sub-app's route keeps its own error factory */
652
+ const routeFactory = (handler.fac as Record<string, () => HoneyError> | null | undefined) ?? factory
653
+ addErrorResponses(responses, handler, {
654
+ baseErrorJsonSchema,
655
+ customErrorAddsSchema,
656
+ errorMeta: routeFactory === factory ? errorMeta : getErrorMeta(routeFactory),
657
+ factory: routeFactory,
658
+ })
519
659
  }
520
660
 
521
661
  if (Object.keys(responses).length === 0) {
522
662
  responses["200"] = { description: "Success" }
523
663
  }
524
- operation.responses = responses
525
664
 
526
- paths[oaPath][methodKey] = operation
527
- emitted.push({
528
- meta: handler.mt as Record<string, unknown> | null,
529
- method: methodKey,
530
- operation,
531
- path: oaPath,
532
- })
665
+ for (const methodKey of methods) {
666
+ const operation: Record<string, unknown> = {}
667
+
668
+ applyMetaSpec({
669
+ collector,
670
+ filter: profileFilter,
671
+ kind: "http",
672
+ meta,
673
+ method: methodKey.toUpperCase(),
674
+ operation,
675
+ path,
676
+ profile: profileName,
677
+ schemaMeta: metaSpec.needsSchemas ? makeSchemaMetaLookup(handler) : undefined,
678
+ spec: metaSpec,
679
+ })
680
+
681
+ if (requestBody) operation.requestBody = methods.length > 1 ? cloneJson(requestBody) : requestBody
682
+ if (parameters.length > 0) operation.parameters = methods.length > 1 ? cloneJson(parameters) : parameters
683
+ operation.responses = methods.length > 1 ? cloneJson(responses) : responses
684
+
685
+ const pathItem = paths[oaPath] ?? (paths[oaPath] = {})
686
+ pathItem[methodKey] = operation
687
+ operations.push({
688
+ handler,
689
+ method: methodKey,
690
+ source: handler.fn ?? handler,
691
+ operation,
692
+ params: params.slice(shortestParams.get(handler) ?? 0).map((p) => p.name as string),
693
+ path: oaPath,
694
+ })
695
+ emitted.push({ meta, method: methodKey, operation, path: oaPath })
696
+ }
533
697
  }
534
698
 
535
- const wsRoutes: CollectedWSRoute[] = []
536
- walkWSRoutes(tree, "", wsRoutes)
699
+ const wsRoutes = collectWsRoutes(app)
537
700
 
538
701
  for (const { handler, path } of wsRoutes) {
539
- const oaPath = toOpenApiPath(path)
540
- if (paths[oaPath] === undefined) {
541
- paths[oaPath] = {}
702
+ const meta = handler.mt
703
+ if (isMetaInternal(meta)) continue
704
+ if (routeFilter && !routeFilter({ meta: (meta ?? {}) as TMeta, method: "WS", path })) continue
705
+
706
+ const oaPath = publicPath(toOpenApiPath(path), settings)
707
+ if (paths[oaPath]?.get !== undefined) {
708
+ collector.add(
709
+ "WS_SHADOWED",
710
+ "warn",
711
+ `WS ${path}: GET ${oaPath} is an HTTP operation; the websocket route is left out of the document`,
712
+ )
713
+ continue
542
714
  }
543
715
 
544
716
  const operation: Record<string, unknown> = { "x-websocket": true }
545
- const parameters: Array<Record<string, unknown>> = []
717
+ const parameters: Array<Record<string, unknown>> = pathParameters(path)
546
718
 
547
719
  applyMetaSpec({
548
720
  collector,
549
721
  filter: profileFilter,
550
722
  kind: "ws",
551
- meta: handler.mt,
723
+ meta,
552
724
  method: "WS",
553
725
  operation,
554
726
  path,
@@ -557,58 +729,165 @@ export function generateOpenApiFromTree<TMeta = unknown>(
557
729
  spec: metaSpec,
558
730
  })
559
731
 
560
- const wsParams = extractParams(path)
561
- for (const name of wsParams) {
562
- parameters.push({
563
- in: "path",
564
- name,
565
- required: true,
566
- schema: { type: "string" },
567
- })
568
- }
569
-
570
- if (handler.iv?.search) {
571
- const searchEntry = handler.iv.search
572
- const searchSchema =
573
- "_tag" in searchEntry
574
- ? (searchEntry as { schema: StandardSchemaLike }).schema
575
- : (searchEntry as StandardSchemaLike)
576
- const jsonSchema = asJsonSchema(searchSchema as StandardSchemaLike | Record<string, unknown>, "input")
577
- if (jsonSchema && typeof jsonSchema === "object") {
578
- const props = jsonSchema.properties as Record<string, unknown> | undefined
579
- const required = new Set((jsonSchema.required ?? []) as string[])
580
- if (props) {
581
- for (const [name, schema] of Object.entries(props)) {
582
- parameters.push({
583
- in: "query",
584
- name,
585
- required: required.has(name),
586
- schema,
587
- })
588
- }
589
- }
590
- }
732
+ const searchEntry = handler.iv?.search
733
+ if (searchEntry) {
734
+ const jsonSchema = asJsonSchema(unwrapEntry(searchEntry as InputSchemaEntry), "input")
735
+ const hint = deriveSchemaName({ method: "get", path: oaPath, role: "request" })
736
+ parameters.push(...propertyParameters(jsonSchema, "query", hoister, hint))
591
737
  }
592
738
 
593
739
  if (parameters.length > 0) operation.parameters = parameters
594
740
  operation.responses = { "101": { description: "WebSocket upgrade" } }
595
741
 
596
- paths[oaPath].get = operation
742
+ const pathItem = paths[oaPath] ?? (paths[oaPath] = {})
743
+ pathItem.get = operation
744
+ operations.push({ handler, method: "get", operation, params: [], path: oaPath, source: handler.fn ?? handler })
597
745
  }
598
746
 
747
+ assignOperationIds(operations, collector)
748
+ checkSecurityReferences(operations, options.securitySchemes, collector)
749
+ if (invalidateLevel(options.invalidate) !== "off") {
750
+ const inventory = new Set(collected.map((r) => `${r.method} ${r.path}`))
751
+ for (const bad of findInvalidSelectors(emitted, inventory)) {
752
+ collector.add(
753
+ "INVALID_SELECTOR",
754
+ "error",
755
+ `${bad.method} ${bad.path}: invalidate selector ${JSON.stringify(bad.selector)} names no route ` +
756
+ '— write "<METHOD> <pattern>" for a registered route, e.g. "GET /users/:id"',
757
+ )
758
+ }
759
+ }
599
760
  collector.flush()
600
761
  reportMissingInvalidate(emitted, options.invalidate)
601
762
 
602
763
  const result: OpenApiSpec = {
603
- info: options.info,
764
+ info: cleanInfo(options.info),
604
765
  openapi: "3.1.0",
605
- paths,
766
+ paths: { ...paths },
606
767
  }
607
- if (options.securitySchemes) {
608
- result.components = {
609
- ...result.components,
610
- securitySchemes: options.securitySchemes,
611
- }
768
+ if (settings.stripPrefix) {
769
+ result.servers = [{ url: settings.stripPrefix }]
770
+ }
771
+ const hoisted = Object.keys(hoister.components).length > 0 ? { ...hoister.components } : null
772
+ if (hoisted || options.securitySchemes) {
773
+ result.components = {}
774
+ if (hoisted) result.components.schemas = hoisted
775
+ if (options.securitySchemes) result.components.securitySchemes = options.securitySchemes
612
776
  }
613
777
  return result
614
778
  }
779
+
780
+ /** Only the Info Object's own fields: callers pass their whole options bag. */
781
+ function cleanInfo(info: OpenApiInfo): OpenApiInfo {
782
+ const out: OpenApiInfo = { title: info.title, version: info.version }
783
+ if (typeof info.description === "string") out.description = info.description
784
+ return out
785
+ }
786
+
787
+ /** One parameter per property of an object schema (`search`, `headers`, `cookies`). */
788
+ function propertyParameters(
789
+ jsonSchema: Record<string, unknown>,
790
+ location: "cookie" | "header" | "query",
791
+ hoister: LocalRefHoister,
792
+ hint: string,
793
+ ): Array<Record<string, unknown>> {
794
+ const out: Array<Record<string, unknown>> = []
795
+ const hoisted = hoister.hoist(jsonSchema, hint)
796
+ /* a recursive search schema hoists its root; its properties are still the parameters */
797
+ const root = hoisted.$ref === undefined ? hoisted : jsonSchema
798
+ const props = root.properties as Record<string, unknown> | undefined
799
+ if (!props) return out
800
+ const required = new Set((root.required as string[] | undefined) ?? [])
801
+ for (const name of Object.keys(props)) {
802
+ const propSchema = props[name] as Record<string, unknown> | undefined
803
+ if (propSchema?.["x-internal"] === true) continue
804
+ out.push({ in: location, name, required: required.has(name), schema: propSchema ?? {} })
805
+ }
806
+ return out
807
+ }
808
+
809
+ type ErrorResponseContext = {
810
+ baseErrorJsonSchema: Record<string, unknown>
811
+ customErrorAddsSchema: Record<string, unknown> | null
812
+ errorMeta: Record<string, ErrorMetaEntry> | null
813
+ factory: Record<string, () => HoneyError> | null
814
+ }
815
+
816
+ function addErrorResponses(
817
+ responses: Record<string, Record<string, unknown>>,
818
+ handler: { ek: Set<string> },
819
+ ctx: ErrorResponseContext,
820
+ ): void {
821
+ type ErrorEntry = { key: string; schema: Record<string, unknown> | null }
822
+ const byStatus = new Map<number, ErrorEntry[]>()
823
+ for (const ek of handler.ek) {
824
+ const info = resolveErrorInfo(ek, ctx.factory)
825
+ if (info.status <= 0) continue
826
+ let entries = byStatus.get(info.status)
827
+ if (!entries) {
828
+ entries = []
829
+ byStatus.set(info.status, entries)
830
+ }
831
+ const meta = ctx.errorMeta?.[ek]
832
+ let customSchema: Record<string, unknown> | null = null
833
+ if (meta?.schema) {
834
+ const converted = asJsonSchema(meta.schema as StandardSchemaLike)
835
+ if (converted && Object.keys(converted).length > 0) {
836
+ customSchema = ctx.customErrorAddsSchema ? { allOf: [converted, ctx.customErrorAddsSchema] } : converted
837
+ }
838
+ }
839
+ entries.push({ key: ek, schema: customSchema })
840
+ }
841
+ const standardSchema = (status: number, keys: string[]): Record<string, unknown> => {
842
+ const schema = cloneJson(ctx.baseErrorJsonSchema)
843
+ const props = schema.properties as Record<string, unknown> | undefined
844
+ if (props?.error_key) props.error_key = { enum: [...keys].sort(), type: "string" }
845
+ if (props?.status) props.status = { enum: [status], type: "integer" }
846
+ return schema
847
+ }
848
+ for (const [status, entries] of byStatus) {
849
+ if (responses[String(status)] !== undefined) continue
850
+ const standardKeys = entries.filter((e) => !e.schema).map((e) => e.key)
851
+ const customSchemas = entries.filter((e) => e.schema).map((e) => e.schema as Record<string, unknown>)
852
+
853
+ let schema: Record<string, unknown>
854
+ if (customSchemas.length === 0) {
855
+ schema = standardSchema(status, standardKeys)
856
+ } else if (standardKeys.length === 0 && customSchemas.length === 1) {
857
+ schema = customSchemas[0]
858
+ } else {
859
+ const schemas: Record<string, unknown>[] = []
860
+ if (standardKeys.length > 0) schemas.push(standardSchema(status, standardKeys))
861
+ schemas.push(...customSchemas)
862
+ schema = { oneOf: schemas }
863
+ }
864
+
865
+ responses[String(status)] = {
866
+ content: { "application/json": { schema } },
867
+ description: entries
868
+ .map((e) => e.key)
869
+ .sort()
870
+ .join(", "),
871
+ }
872
+ }
873
+ }
874
+
875
+ /** Every scheme an operation's `security` names must exist in `components.securitySchemes`. */
876
+ function checkSecurityReferences(
877
+ operations: readonly EmittedOperation[],
878
+ schemes: Record<string, unknown> | undefined,
879
+ collector: MetaSpecCollector,
880
+ ): void {
881
+ for (const { method, operation, path } of operations) {
882
+ for (const name of securitySchemeNames(operation.security)) {
883
+ if (schemes && Object.hasOwn(schemes, name)) continue
884
+ collector.add(
885
+ "UNKNOWN_SECURITY_SCHEME",
886
+ schemes ? "error" : "warn",
887
+ schemes
888
+ ? `${method.toUpperCase()} ${path}: security names "${name}", which is not in securitySchemes`
889
+ : `security scheme "${name}" (first used by ${method.toUpperCase()} ${path}) is not declared — pass securitySchemes`,
890
+ )
891
+ }
892
+ }
893
+ }