@grapity/grapity 0.1.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 (642) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +214 -0
  3. package/bin/grapity +2 -0
  4. package/dist/assets/abap-BdImnpbu.js +2 -0
  5. package/dist/assets/abap-BdImnpbu.js.map +1 -0
  6. package/dist/assets/actionscript-3-CoDkCxhg.js +2 -0
  7. package/dist/assets/actionscript-3-CoDkCxhg.js.map +1 -0
  8. package/dist/assets/ada-bCR0ucgS.js +2 -0
  9. package/dist/assets/ada-bCR0ucgS.js.map +1 -0
  10. package/dist/assets/andromeeda-C4gqWexZ.js +2 -0
  11. package/dist/assets/andromeeda-C4gqWexZ.js.map +1 -0
  12. package/dist/assets/angular-html-DA-rfuFy.js +2 -0
  13. package/dist/assets/angular-html-DA-rfuFy.js.map +1 -0
  14. package/dist/assets/angular-ts-BrjP3tb8.js +2 -0
  15. package/dist/assets/angular-ts-BrjP3tb8.js.map +1 -0
  16. package/dist/assets/apache-Pmp26Uib.js +2 -0
  17. package/dist/assets/apache-Pmp26Uib.js.map +1 -0
  18. package/dist/assets/apex-Dqspr-GT.js +2 -0
  19. package/dist/assets/apex-Dqspr-GT.js.map +1 -0
  20. package/dist/assets/apl-CORt7UWP.js +2 -0
  21. package/dist/assets/apl-CORt7UWP.js.map +1 -0
  22. package/dist/assets/applescript-Co6uUVPk.js +2 -0
  23. package/dist/assets/applescript-Co6uUVPk.js.map +1 -0
  24. package/dist/assets/ara-BRHolxvo.js +2 -0
  25. package/dist/assets/ara-BRHolxvo.js.map +1 -0
  26. package/dist/assets/asciidoc-Ve4PFQV2.js +2 -0
  27. package/dist/assets/asciidoc-Ve4PFQV2.js.map +1 -0
  28. package/dist/assets/asm-D_Q5rh1f.js +2 -0
  29. package/dist/assets/asm-D_Q5rh1f.js.map +1 -0
  30. package/dist/assets/astro-HNnZUWAn.js +2 -0
  31. package/dist/assets/astro-HNnZUWAn.js.map +1 -0
  32. package/dist/assets/aurora-x-D-2ljcwZ.js +2 -0
  33. package/dist/assets/aurora-x-D-2ljcwZ.js.map +1 -0
  34. package/dist/assets/awk-DMzUqQB5.js +2 -0
  35. package/dist/assets/awk-DMzUqQB5.js.map +1 -0
  36. package/dist/assets/ayu-dark-DYE7WIF3.js +2 -0
  37. package/dist/assets/ayu-dark-DYE7WIF3.js.map +1 -0
  38. package/dist/assets/ayu-light-BA47KaF1.js +2 -0
  39. package/dist/assets/ayu-light-BA47KaF1.js.map +1 -0
  40. package/dist/assets/ayu-mirage-32ctXXKs.js +2 -0
  41. package/dist/assets/ayu-mirage-32ctXXKs.js.map +1 -0
  42. package/dist/assets/ballerina-BFfxhgS-.js +2 -0
  43. package/dist/assets/ballerina-BFfxhgS-.js.map +1 -0
  44. package/dist/assets/bat-BkioyH1T.js +2 -0
  45. package/dist/assets/bat-BkioyH1T.js.map +1 -0
  46. package/dist/assets/beancount-k_qm7-4y.js +2 -0
  47. package/dist/assets/beancount-k_qm7-4y.js.map +1 -0
  48. package/dist/assets/berry-uYugtg8r.js +2 -0
  49. package/dist/assets/berry-uYugtg8r.js.map +1 -0
  50. package/dist/assets/bibtex-CHM0blh-.js +2 -0
  51. package/dist/assets/bibtex-CHM0blh-.js.map +1 -0
  52. package/dist/assets/bicep-Bmn6On1c.js +2 -0
  53. package/dist/assets/bicep-Bmn6On1c.js.map +1 -0
  54. package/dist/assets/bird2-BIv1doCn.js +2 -0
  55. package/dist/assets/bird2-BIv1doCn.js.map +1 -0
  56. package/dist/assets/blade-2xfisSek.js +2 -0
  57. package/dist/assets/blade-2xfisSek.js.map +1 -0
  58. package/dist/assets/bsl-BO_Y6i37.js +2 -0
  59. package/dist/assets/bsl-BO_Y6i37.js.map +1 -0
  60. package/dist/assets/c-BIGW1oBm.js +2 -0
  61. package/dist/assets/c-BIGW1oBm.js.map +1 -0
  62. package/dist/assets/c3-MRO5bC_T.js +2 -0
  63. package/dist/assets/c3-MRO5bC_T.js.map +1 -0
  64. package/dist/assets/cadence-Bv_4Rxtq.js +2 -0
  65. package/dist/assets/cadence-Bv_4Rxtq.js.map +1 -0
  66. package/dist/assets/cairo-KRGpt6FW.js +2 -0
  67. package/dist/assets/cairo-KRGpt6FW.js.map +1 -0
  68. package/dist/assets/catppuccin-frappe-DFWUc33u.js +2 -0
  69. package/dist/assets/catppuccin-frappe-DFWUc33u.js.map +1 -0
  70. package/dist/assets/catppuccin-latte-C9dUb6Cb.js +2 -0
  71. package/dist/assets/catppuccin-latte-C9dUb6Cb.js.map +1 -0
  72. package/dist/assets/catppuccin-macchiato-DQyhUUbL.js +2 -0
  73. package/dist/assets/catppuccin-macchiato-DQyhUUbL.js.map +1 -0
  74. package/dist/assets/catppuccin-mocha-D87Tk5Gz.js +2 -0
  75. package/dist/assets/catppuccin-mocha-D87Tk5Gz.js.map +1 -0
  76. package/dist/assets/clarity-D53aC0YG.js +2 -0
  77. package/dist/assets/clarity-D53aC0YG.js.map +1 -0
  78. package/dist/assets/clojure-P80f7IUj.js +2 -0
  79. package/dist/assets/clojure-P80f7IUj.js.map +1 -0
  80. package/dist/assets/cmake-D1j8_8rp.js +2 -0
  81. package/dist/assets/cmake-D1j8_8rp.js.map +1 -0
  82. package/dist/assets/cobol-nBiQ_Alo.js +2 -0
  83. package/dist/assets/cobol-nBiQ_Alo.js.map +1 -0
  84. package/dist/assets/codeowners-Bp6g37R7.js +2 -0
  85. package/dist/assets/codeowners-Bp6g37R7.js.map +1 -0
  86. package/dist/assets/codeql-DsOJ9woJ.js +2 -0
  87. package/dist/assets/codeql-DsOJ9woJ.js.map +1 -0
  88. package/dist/assets/coffee-Ch7k5sss.js +2 -0
  89. package/dist/assets/coffee-Ch7k5sss.js.map +1 -0
  90. package/dist/assets/common-lisp-Cg-RD9OK.js +2 -0
  91. package/dist/assets/common-lisp-Cg-RD9OK.js.map +1 -0
  92. package/dist/assets/coq-DkFqJrB1.js +2 -0
  93. package/dist/assets/coq-DkFqJrB1.js.map +1 -0
  94. package/dist/assets/cpp-UfJy6YNI.js +2 -0
  95. package/dist/assets/cpp-UfJy6YNI.js.map +1 -0
  96. package/dist/assets/crystal-DGywbUpC.js +2 -0
  97. package/dist/assets/crystal-DGywbUpC.js.map +1 -0
  98. package/dist/assets/csharp-DSvCPggb.js +2 -0
  99. package/dist/assets/csharp-DSvCPggb.js.map +1 -0
  100. package/dist/assets/css-CLj8gQPS.js +2 -0
  101. package/dist/assets/css-CLj8gQPS.js.map +1 -0
  102. package/dist/assets/csv-fuZLfV_i.js +2 -0
  103. package/dist/assets/csv-fuZLfV_i.js.map +1 -0
  104. package/dist/assets/cue-D82EKSYY.js +2 -0
  105. package/dist/assets/cue-D82EKSYY.js.map +1 -0
  106. package/dist/assets/cypher-COkxafJQ.js +2 -0
  107. package/dist/assets/cypher-COkxafJQ.js.map +1 -0
  108. package/dist/assets/d-85-TOEBH.js +2 -0
  109. package/dist/assets/d-85-TOEBH.js.map +1 -0
  110. package/dist/assets/dark-plus-C3mMm8J8.js +2 -0
  111. package/dist/assets/dark-plus-C3mMm8J8.js.map +1 -0
  112. package/dist/assets/dart-bE4Kk8sk.js +2 -0
  113. package/dist/assets/dart-bE4Kk8sk.js.map +1 -0
  114. package/dist/assets/dax-CEL-wOlO.js +2 -0
  115. package/dist/assets/dax-CEL-wOlO.js.map +1 -0
  116. package/dist/assets/desktop-BmXAJ9_W.js +2 -0
  117. package/dist/assets/desktop-BmXAJ9_W.js.map +1 -0
  118. package/dist/assets/diff-D97Zzqfu.js +2 -0
  119. package/dist/assets/diff-D97Zzqfu.js.map +1 -0
  120. package/dist/assets/docker-BcOcwvcX.js +2 -0
  121. package/dist/assets/docker-BcOcwvcX.js.map +1 -0
  122. package/dist/assets/dotenv-Da5cRb03.js +2 -0
  123. package/dist/assets/dotenv-Da5cRb03.js.map +1 -0
  124. package/dist/assets/dracula-BzJJZx-M.js +2 -0
  125. package/dist/assets/dracula-BzJJZx-M.js.map +1 -0
  126. package/dist/assets/dracula-soft-BXkSAIEj.js +2 -0
  127. package/dist/assets/dracula-soft-BXkSAIEj.js.map +1 -0
  128. package/dist/assets/dream-maker-BtqSS_iP.js +2 -0
  129. package/dist/assets/dream-maker-BtqSS_iP.js.map +1 -0
  130. package/dist/assets/edge-FbVlp4U3.js +2 -0
  131. package/dist/assets/edge-FbVlp4U3.js.map +1 -0
  132. package/dist/assets/elixir-CkH2-t6x.js +2 -0
  133. package/dist/assets/elixir-CkH2-t6x.js.map +1 -0
  134. package/dist/assets/elm-DbKCFpqz.js +2 -0
  135. package/dist/assets/elm-DbKCFpqz.js.map +1 -0
  136. package/dist/assets/emacs-lisp-CXvaQtF9.js +2 -0
  137. package/dist/assets/emacs-lisp-CXvaQtF9.js.map +1 -0
  138. package/dist/assets/erb-Dm6A9KJ5.js +2 -0
  139. package/dist/assets/erb-Dm6A9KJ5.js.map +1 -0
  140. package/dist/assets/erlang-DsQrWhSR.js +2 -0
  141. package/dist/assets/erlang-DsQrWhSR.js.map +1 -0
  142. package/dist/assets/everforest-dark-BgDCqdQA.js +2 -0
  143. package/dist/assets/everforest-dark-BgDCqdQA.js.map +1 -0
  144. package/dist/assets/everforest-light-C8M2exoo.js +2 -0
  145. package/dist/assets/everforest-light-C8M2exoo.js.map +1 -0
  146. package/dist/assets/fennel-BYunw83y.js +2 -0
  147. package/dist/assets/fennel-BYunw83y.js.map +1 -0
  148. package/dist/assets/fish-BvzEVeQv.js +2 -0
  149. package/dist/assets/fish-BvzEVeQv.js.map +1 -0
  150. package/dist/assets/fluent-C4IJs8-o.js +2 -0
  151. package/dist/assets/fluent-C4IJs8-o.js.map +1 -0
  152. package/dist/assets/fortran-fixed-form-CkoXwp7k.js +2 -0
  153. package/dist/assets/fortran-fixed-form-CkoXwp7k.js.map +1 -0
  154. package/dist/assets/fortran-free-form-BxgE0vQu.js +2 -0
  155. package/dist/assets/fortran-free-form-BxgE0vQu.js.map +1 -0
  156. package/dist/assets/fsharp-CXgrBDvD.js +2 -0
  157. package/dist/assets/fsharp-CXgrBDvD.js.map +1 -0
  158. package/dist/assets/gdresource-BOOCDP_w.js +2 -0
  159. package/dist/assets/gdresource-BOOCDP_w.js.map +1 -0
  160. package/dist/assets/gdscript-C5YyOfLZ.js +2 -0
  161. package/dist/assets/gdscript-C5YyOfLZ.js.map +1 -0
  162. package/dist/assets/gdshader-DkwncUOv.js +2 -0
  163. package/dist/assets/gdshader-DkwncUOv.js.map +1 -0
  164. package/dist/assets/genie-D0YGMca9.js +2 -0
  165. package/dist/assets/genie-D0YGMca9.js.map +1 -0
  166. package/dist/assets/gherkin-DyxjwDmM.js +2 -0
  167. package/dist/assets/gherkin-DyxjwDmM.js.map +1 -0
  168. package/dist/assets/git-commit-F4YmCXRG.js +2 -0
  169. package/dist/assets/git-commit-F4YmCXRG.js.map +1 -0
  170. package/dist/assets/git-rebase-r7XF79zn.js +2 -0
  171. package/dist/assets/git-rebase-r7XF79zn.js.map +1 -0
  172. package/dist/assets/github-dark-DHJKELXO.js +2 -0
  173. package/dist/assets/github-dark-DHJKELXO.js.map +1 -0
  174. package/dist/assets/github-dark-default-Cuk6v7N8.js +2 -0
  175. package/dist/assets/github-dark-default-Cuk6v7N8.js.map +1 -0
  176. package/dist/assets/github-dark-dimmed-DH5Ifo-i.js +2 -0
  177. package/dist/assets/github-dark-dimmed-DH5Ifo-i.js.map +1 -0
  178. package/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +2 -0
  179. package/dist/assets/github-dark-high-contrast-E3gJ1_iC.js.map +1 -0
  180. package/dist/assets/github-light-DAi9KRSo.js +2 -0
  181. package/dist/assets/github-light-DAi9KRSo.js.map +1 -0
  182. package/dist/assets/github-light-default-D7oLnXFd.js +2 -0
  183. package/dist/assets/github-light-default-D7oLnXFd.js.map +1 -0
  184. package/dist/assets/github-light-high-contrast-BfjtVDDH.js +2 -0
  185. package/dist/assets/github-light-high-contrast-BfjtVDDH.js.map +1 -0
  186. package/dist/assets/gleam-BspZqrRM.js +2 -0
  187. package/dist/assets/gleam-BspZqrRM.js.map +1 -0
  188. package/dist/assets/glimmer-js-ByusRIyA.js +2 -0
  189. package/dist/assets/glimmer-js-ByusRIyA.js.map +1 -0
  190. package/dist/assets/glimmer-ts-BfAWNZQY.js +2 -0
  191. package/dist/assets/glimmer-ts-BfAWNZQY.js.map +1 -0
  192. package/dist/assets/glsl-DplSGwfg.js +2 -0
  193. package/dist/assets/glsl-DplSGwfg.js.map +1 -0
  194. package/dist/assets/gn-n2N0HUVH.js +2 -0
  195. package/dist/assets/gn-n2N0HUVH.js.map +1 -0
  196. package/dist/assets/gnuplot-DdkO51Og.js +2 -0
  197. package/dist/assets/gnuplot-DdkO51Og.js.map +1 -0
  198. package/dist/assets/go-C27-OAKa.js +2 -0
  199. package/dist/assets/go-C27-OAKa.js.map +1 -0
  200. package/dist/assets/graphql-ChdNCCLP.js +2 -0
  201. package/dist/assets/graphql-ChdNCCLP.js.map +1 -0
  202. package/dist/assets/groovy-gcz8RCvz.js +2 -0
  203. package/dist/assets/groovy-gcz8RCvz.js.map +1 -0
  204. package/dist/assets/gruvbox-dark-hard-CFHQjOhq.js +2 -0
  205. package/dist/assets/gruvbox-dark-hard-CFHQjOhq.js.map +1 -0
  206. package/dist/assets/gruvbox-dark-medium-GsRaNv29.js +2 -0
  207. package/dist/assets/gruvbox-dark-medium-GsRaNv29.js.map +1 -0
  208. package/dist/assets/gruvbox-dark-soft-CVdnzihN.js +2 -0
  209. package/dist/assets/gruvbox-dark-soft-CVdnzihN.js.map +1 -0
  210. package/dist/assets/gruvbox-light-hard-CH1njM8p.js +2 -0
  211. package/dist/assets/gruvbox-light-hard-CH1njM8p.js.map +1 -0
  212. package/dist/assets/gruvbox-light-medium-DRw_LuNl.js +2 -0
  213. package/dist/assets/gruvbox-light-medium-DRw_LuNl.js.map +1 -0
  214. package/dist/assets/gruvbox-light-soft-hJgmCMqR.js +2 -0
  215. package/dist/assets/gruvbox-light-soft-hJgmCMqR.js.map +1 -0
  216. package/dist/assets/hack-DbPARsA_.js +2 -0
  217. package/dist/assets/hack-DbPARsA_.js.map +1 -0
  218. package/dist/assets/haml-D5jkg6IW.js +2 -0
  219. package/dist/assets/haml-D5jkg6IW.js.map +1 -0
  220. package/dist/assets/handlebars-BpdQsYii.js +2 -0
  221. package/dist/assets/handlebars-BpdQsYii.js.map +1 -0
  222. package/dist/assets/haskell-Df6bDoY_.js +2 -0
  223. package/dist/assets/haskell-Df6bDoY_.js.map +1 -0
  224. package/dist/assets/haxe-CzTSHFRz.js +2 -0
  225. package/dist/assets/haxe-CzTSHFRz.js.map +1 -0
  226. package/dist/assets/hcl-BWvSN4gD.js +2 -0
  227. package/dist/assets/hcl-BWvSN4gD.js.map +1 -0
  228. package/dist/assets/hjson-D5-asLiD.js +2 -0
  229. package/dist/assets/hjson-D5-asLiD.js.map +1 -0
  230. package/dist/assets/hlsl-D3lLCCz7.js +2 -0
  231. package/dist/assets/hlsl-D3lLCCz7.js.map +1 -0
  232. package/dist/assets/horizon-BUw7H-hv.js +2 -0
  233. package/dist/assets/horizon-BUw7H-hv.js.map +1 -0
  234. package/dist/assets/horizon-bright-CUuTKBJd.js +2 -0
  235. package/dist/assets/horizon-bright-CUuTKBJd.js.map +1 -0
  236. package/dist/assets/houston-DnULxvSX.js +2 -0
  237. package/dist/assets/houston-DnULxvSX.js.map +1 -0
  238. package/dist/assets/html-derivative-DlHx6ybY.js +2 -0
  239. package/dist/assets/html-derivative-DlHx6ybY.js.map +1 -0
  240. package/dist/assets/html-pp8916En.js +2 -0
  241. package/dist/assets/html-pp8916En.js.map +1 -0
  242. package/dist/assets/http-jrhK8wxY.js +2 -0
  243. package/dist/assets/http-jrhK8wxY.js.map +1 -0
  244. package/dist/assets/hurl-irOxFIW8.js +2 -0
  245. package/dist/assets/hurl-irOxFIW8.js.map +1 -0
  246. package/dist/assets/hxml-Bvhsp5Yf.js +2 -0
  247. package/dist/assets/hxml-Bvhsp5Yf.js.map +1 -0
  248. package/dist/assets/hy-DFXneXwc.js +2 -0
  249. package/dist/assets/hy-DFXneXwc.js.map +1 -0
  250. package/dist/assets/imba-DGztddWO.js +2 -0
  251. package/dist/assets/imba-DGztddWO.js.map +1 -0
  252. package/dist/assets/index-Dq5tdnlb.js +326 -0
  253. package/dist/assets/index-Dq5tdnlb.js.map +1 -0
  254. package/dist/assets/index-NJpHAonA.css +1 -0
  255. package/dist/assets/ini-BEwlwnbL.js +2 -0
  256. package/dist/assets/ini-BEwlwnbL.js.map +1 -0
  257. package/dist/assets/java-CylS5w8V.js +2 -0
  258. package/dist/assets/java-CylS5w8V.js.map +1 -0
  259. package/dist/assets/javascript-wDzz0qaB.js +2 -0
  260. package/dist/assets/javascript-wDzz0qaB.js.map +1 -0
  261. package/dist/assets/jinja-f2NsQr07.js +2 -0
  262. package/dist/assets/jinja-f2NsQr07.js.map +1 -0
  263. package/dist/assets/jison-wvAkD_A8.js +2 -0
  264. package/dist/assets/jison-wvAkD_A8.js.map +1 -0
  265. package/dist/assets/json-Cp-IABpG.js +2 -0
  266. package/dist/assets/json-Cp-IABpG.js.map +1 -0
  267. package/dist/assets/json5-C9tS-k6U.js +2 -0
  268. package/dist/assets/json5-C9tS-k6U.js.map +1 -0
  269. package/dist/assets/jsonc-Des-eS-w.js +2 -0
  270. package/dist/assets/jsonc-Des-eS-w.js.map +1 -0
  271. package/dist/assets/jsonl-DcaNXYhu.js +2 -0
  272. package/dist/assets/jsonl-DcaNXYhu.js.map +1 -0
  273. package/dist/assets/jsonnet-DFQXde-d.js +2 -0
  274. package/dist/assets/jsonnet-DFQXde-d.js.map +1 -0
  275. package/dist/assets/jssm-C2t-YnRu.js +2 -0
  276. package/dist/assets/jssm-C2t-YnRu.js.map +1 -0
  277. package/dist/assets/jsx-g9-lgVsj.js +2 -0
  278. package/dist/assets/jsx-g9-lgVsj.js.map +1 -0
  279. package/dist/assets/julia-D7OTSIA_.js +2 -0
  280. package/dist/assets/julia-D7OTSIA_.js.map +1 -0
  281. package/dist/assets/just-CUsbIsdP.js +2 -0
  282. package/dist/assets/just-CUsbIsdP.js.map +1 -0
  283. package/dist/assets/kanagawa-dragon-CkXjmgJE.js +2 -0
  284. package/dist/assets/kanagawa-dragon-CkXjmgJE.js.map +1 -0
  285. package/dist/assets/kanagawa-lotus-CfQXZHmo.js +2 -0
  286. package/dist/assets/kanagawa-lotus-CfQXZHmo.js.map +1 -0
  287. package/dist/assets/kanagawa-wave-DWedfzmr.js +2 -0
  288. package/dist/assets/kanagawa-wave-DWedfzmr.js.map +1 -0
  289. package/dist/assets/kdl-DV7GczEv.js +2 -0
  290. package/dist/assets/kdl-DV7GczEv.js.map +1 -0
  291. package/dist/assets/kotlin-BdnUsdx6.js +2 -0
  292. package/dist/assets/kotlin-BdnUsdx6.js.map +1 -0
  293. package/dist/assets/kusto-wEQ09or8.js +2 -0
  294. package/dist/assets/kusto-wEQ09or8.js.map +1 -0
  295. package/dist/assets/laserwave-DUszq2jm.js +2 -0
  296. package/dist/assets/laserwave-DUszq2jm.js.map +1 -0
  297. package/dist/assets/latex-CaSxy8MP.js +2 -0
  298. package/dist/assets/latex-CaSxy8MP.js.map +1 -0
  299. package/dist/assets/lean-BZvkOJ9d.js +2 -0
  300. package/dist/assets/lean-BZvkOJ9d.js.map +1 -0
  301. package/dist/assets/less-B1dDrJ26.js +2 -0
  302. package/dist/assets/less-B1dDrJ26.js.map +1 -0
  303. package/dist/assets/light-plus-B7mTdjB0.js +2 -0
  304. package/dist/assets/light-plus-B7mTdjB0.js.map +1 -0
  305. package/dist/assets/liquid-C0sCDyMI.js +2 -0
  306. package/dist/assets/liquid-C0sCDyMI.js.map +1 -0
  307. package/dist/assets/llvm-DjAJT7YJ.js +2 -0
  308. package/dist/assets/llvm-DjAJT7YJ.js.map +1 -0
  309. package/dist/assets/log-2UxHyX5q.js +2 -0
  310. package/dist/assets/log-2UxHyX5q.js.map +1 -0
  311. package/dist/assets/logo-BtOb2qkB.js +2 -0
  312. package/dist/assets/logo-BtOb2qkB.js.map +1 -0
  313. package/dist/assets/lua-BaeVxFsk.js +2 -0
  314. package/dist/assets/lua-BaeVxFsk.js.map +1 -0
  315. package/dist/assets/luau-KW6xsasC.js +2 -0
  316. package/dist/assets/luau-KW6xsasC.js.map +1 -0
  317. package/dist/assets/make-CHLpvVh8.js +2 -0
  318. package/dist/assets/make-CHLpvVh8.js.map +1 -0
  319. package/dist/assets/markdown-Cvjx9yec.js +2 -0
  320. package/dist/assets/markdown-Cvjx9yec.js.map +1 -0
  321. package/dist/assets/marko-DjSrsDqO.js +2 -0
  322. package/dist/assets/marko-DjSrsDqO.js.map +1 -0
  323. package/dist/assets/material-theme-D5KoaKCx.js +2 -0
  324. package/dist/assets/material-theme-D5KoaKCx.js.map +1 -0
  325. package/dist/assets/material-theme-darker-BfHTSMKl.js +2 -0
  326. package/dist/assets/material-theme-darker-BfHTSMKl.js.map +1 -0
  327. package/dist/assets/material-theme-lighter-B0m2ddpp.js +2 -0
  328. package/dist/assets/material-theme-lighter-B0m2ddpp.js.map +1 -0
  329. package/dist/assets/material-theme-ocean-CyktbL80.js +2 -0
  330. package/dist/assets/material-theme-ocean-CyktbL80.js.map +1 -0
  331. package/dist/assets/material-theme-palenight-Csfq5Kiy.js +2 -0
  332. package/dist/assets/material-theme-palenight-Csfq5Kiy.js.map +1 -0
  333. package/dist/assets/matlab-D7o27uSR.js +2 -0
  334. package/dist/assets/matlab-D7o27uSR.js.map +1 -0
  335. package/dist/assets/mdc-DTYItulj.js +2 -0
  336. package/dist/assets/mdc-DTYItulj.js.map +1 -0
  337. package/dist/assets/mdx-Cmh6b_Ma.js +2 -0
  338. package/dist/assets/mdx-Cmh6b_Ma.js.map +1 -0
  339. package/dist/assets/mermaid-mWjccvbQ.js +2 -0
  340. package/dist/assets/mermaid-mWjccvbQ.js.map +1 -0
  341. package/dist/assets/min-dark-CafNBF8u.js +2 -0
  342. package/dist/assets/min-dark-CafNBF8u.js.map +1 -0
  343. package/dist/assets/min-light-CTRr51gU.js +2 -0
  344. package/dist/assets/min-light-CTRr51gU.js.map +1 -0
  345. package/dist/assets/mipsasm-CKIfxQSi.js +2 -0
  346. package/dist/assets/mipsasm-CKIfxQSi.js.map +1 -0
  347. package/dist/assets/mojo-rZm6bMo-.js +2 -0
  348. package/dist/assets/mojo-rZm6bMo-.js.map +1 -0
  349. package/dist/assets/monokai-D4h5O-jR.js +2 -0
  350. package/dist/assets/monokai-D4h5O-jR.js.map +1 -0
  351. package/dist/assets/moonbit-_H4v1dQx.js +2 -0
  352. package/dist/assets/moonbit-_H4v1dQx.js.map +1 -0
  353. package/dist/assets/move-IF9eRakj.js +2 -0
  354. package/dist/assets/move-IF9eRakj.js.map +1 -0
  355. package/dist/assets/narrat-DRg8JJMk.js +2 -0
  356. package/dist/assets/narrat-DRg8JJMk.js.map +1 -0
  357. package/dist/assets/nextflow-C-mBbutL.js +2 -0
  358. package/dist/assets/nextflow-C-mBbutL.js.map +1 -0
  359. package/dist/assets/nextflow-groovy-vE_lwT2v.js +2 -0
  360. package/dist/assets/nextflow-groovy-vE_lwT2v.js.map +1 -0
  361. package/dist/assets/nginx-BpAMiNFr.js +2 -0
  362. package/dist/assets/nginx-BpAMiNFr.js.map +1 -0
  363. package/dist/assets/night-owl-C39BiMTA.js +2 -0
  364. package/dist/assets/night-owl-C39BiMTA.js.map +1 -0
  365. package/dist/assets/night-owl-light-CMTm3GFP.js +2 -0
  366. package/dist/assets/night-owl-light-CMTm3GFP.js.map +1 -0
  367. package/dist/assets/nim-BIad80T-.js +2 -0
  368. package/dist/assets/nim-BIad80T-.js.map +1 -0
  369. package/dist/assets/nix-CwoSXNpI.js +2 -0
  370. package/dist/assets/nix-CwoSXNpI.js.map +1 -0
  371. package/dist/assets/nord-Ddv68eIx.js +2 -0
  372. package/dist/assets/nord-Ddv68eIx.js.map +1 -0
  373. package/dist/assets/nushell-Cz2AlsmD.js +2 -0
  374. package/dist/assets/nushell-Cz2AlsmD.js.map +1 -0
  375. package/dist/assets/objective-c-DXmwc3jG.js +2 -0
  376. package/dist/assets/objective-c-DXmwc3jG.js.map +1 -0
  377. package/dist/assets/objective-cpp-CLxacb5B.js +2 -0
  378. package/dist/assets/objective-cpp-CLxacb5B.js.map +1 -0
  379. package/dist/assets/ocaml-C0hk2d4L.js +2 -0
  380. package/dist/assets/ocaml-C0hk2d4L.js.map +1 -0
  381. package/dist/assets/odin-BBf5iR-q.js +2 -0
  382. package/dist/assets/odin-BBf5iR-q.js.map +1 -0
  383. package/dist/assets/one-dark-pro-DVMEJ2y_.js +2 -0
  384. package/dist/assets/one-dark-pro-DVMEJ2y_.js.map +1 -0
  385. package/dist/assets/one-light-C3Wv6jpd.js +2 -0
  386. package/dist/assets/one-light-C3Wv6jpd.js.map +1 -0
  387. package/dist/assets/openscad-C4EeE6gA.js +2 -0
  388. package/dist/assets/openscad-C4EeE6gA.js.map +1 -0
  389. package/dist/assets/pascal-D93ZcfNL.js +2 -0
  390. package/dist/assets/pascal-D93ZcfNL.js.map +1 -0
  391. package/dist/assets/perl-B9cMNwum.js +2 -0
  392. package/dist/assets/perl-B9cMNwum.js.map +1 -0
  393. package/dist/assets/php-Csjmro_R.js +2 -0
  394. package/dist/assets/php-Csjmro_R.js.map +1 -0
  395. package/dist/assets/pkl-u5AG7uiY.js +2 -0
  396. package/dist/assets/pkl-u5AG7uiY.js.map +1 -0
  397. package/dist/assets/plastic-3e1v2bzS.js +2 -0
  398. package/dist/assets/plastic-3e1v2bzS.js.map +1 -0
  399. package/dist/assets/plsql-ChMvpjG-.js +2 -0
  400. package/dist/assets/plsql-ChMvpjG-.js.map +1 -0
  401. package/dist/assets/po-BTJTHyun.js +2 -0
  402. package/dist/assets/po-BTJTHyun.js.map +1 -0
  403. package/dist/assets/poimandres-CS3Unz2-.js +2 -0
  404. package/dist/assets/poimandres-CS3Unz2-.js.map +1 -0
  405. package/dist/assets/polar-C0HS_06l.js +2 -0
  406. package/dist/assets/polar-C0HS_06l.js.map +1 -0
  407. package/dist/assets/postcss-CXtECtnM.js +2 -0
  408. package/dist/assets/postcss-CXtECtnM.js.map +1 -0
  409. package/dist/assets/powerquery-CEu0bR-o.js +2 -0
  410. package/dist/assets/powerquery-CEu0bR-o.js.map +1 -0
  411. package/dist/assets/powershell-Dpen1YoG.js +2 -0
  412. package/dist/assets/powershell-Dpen1YoG.js.map +1 -0
  413. package/dist/assets/prisma-Dd19v3D-.js +2 -0
  414. package/dist/assets/prisma-Dd19v3D-.js.map +1 -0
  415. package/dist/assets/prolog-CbFg5uaA.js +2 -0
  416. package/dist/assets/prolog-CbFg5uaA.js.map +1 -0
  417. package/dist/assets/proto-C7zT0LnQ.js +2 -0
  418. package/dist/assets/proto-C7zT0LnQ.js.map +1 -0
  419. package/dist/assets/pug-DKIMFp6K.js +2 -0
  420. package/dist/assets/pug-DKIMFp6K.js.map +1 -0
  421. package/dist/assets/puppet-BMWR74SV.js +2 -0
  422. package/dist/assets/puppet-BMWR74SV.js.map +1 -0
  423. package/dist/assets/purescript-CklMAg4u.js +2 -0
  424. package/dist/assets/purescript-CklMAg4u.js.map +1 -0
  425. package/dist/assets/python-B6aJPvgy.js +2 -0
  426. package/dist/assets/python-B6aJPvgy.js.map +1 -0
  427. package/dist/assets/qml-3beO22l8.js +2 -0
  428. package/dist/assets/qml-3beO22l8.js.map +1 -0
  429. package/dist/assets/qmldir-C8lEn-DE.js +2 -0
  430. package/dist/assets/qmldir-C8lEn-DE.js.map +1 -0
  431. package/dist/assets/qss-IeuSbFQv.js +2 -0
  432. package/dist/assets/qss-IeuSbFQv.js.map +1 -0
  433. package/dist/assets/r-Dspwwk_N.js +2 -0
  434. package/dist/assets/r-Dspwwk_N.js.map +1 -0
  435. package/dist/assets/racket-BqYA7rlc.js +2 -0
  436. package/dist/assets/racket-BqYA7rlc.js.map +1 -0
  437. package/dist/assets/raku-DXvB9xmW.js +2 -0
  438. package/dist/assets/raku-DXvB9xmW.js.map +1 -0
  439. package/dist/assets/razor-BjBPvh-w.js +2 -0
  440. package/dist/assets/razor-BjBPvh-w.js.map +1 -0
  441. package/dist/assets/red-bN70gL4F.js +2 -0
  442. package/dist/assets/red-bN70gL4F.js.map +1 -0
  443. package/dist/assets/reg-C-SQnVFl.js +2 -0
  444. package/dist/assets/reg-C-SQnVFl.js.map +1 -0
  445. package/dist/assets/regexp-CDVJQ6XC.js +2 -0
  446. package/dist/assets/regexp-CDVJQ6XC.js.map +1 -0
  447. package/dist/assets/rel-C3B-1QV4.js +2 -0
  448. package/dist/assets/rel-C3B-1QV4.js.map +1 -0
  449. package/dist/assets/riscv-BM1_JUlF.js +2 -0
  450. package/dist/assets/riscv-BM1_JUlF.js.map +1 -0
  451. package/dist/assets/ron-D8l8udqQ.js +2 -0
  452. package/dist/assets/ron-D8l8udqQ.js.map +1 -0
  453. package/dist/assets/rose-pine-dawn-DHQR4-dF.js +2 -0
  454. package/dist/assets/rose-pine-dawn-DHQR4-dF.js.map +1 -0
  455. package/dist/assets/rose-pine-moon-D4_iv3hh.js +2 -0
  456. package/dist/assets/rose-pine-moon-D4_iv3hh.js.map +1 -0
  457. package/dist/assets/rose-pine-qdsjHGoJ.js +2 -0
  458. package/dist/assets/rose-pine-qdsjHGoJ.js.map +1 -0
  459. package/dist/assets/rosmsg-BJDFO7_C.js +2 -0
  460. package/dist/assets/rosmsg-BJDFO7_C.js.map +1 -0
  461. package/dist/assets/rst-CpCqk9r5.js +2 -0
  462. package/dist/assets/rst-CpCqk9r5.js.map +1 -0
  463. package/dist/assets/ruby-DyJCeAvU.js +2 -0
  464. package/dist/assets/ruby-DyJCeAvU.js.map +1 -0
  465. package/dist/assets/rust-B1yitclQ.js +2 -0
  466. package/dist/assets/rust-B1yitclQ.js.map +1 -0
  467. package/dist/assets/sas-DEy46yEz.js +2 -0
  468. package/dist/assets/sas-DEy46yEz.js.map +1 -0
  469. package/dist/assets/sass-Cj5Yp3dK.js +2 -0
  470. package/dist/assets/sass-Cj5Yp3dK.js.map +1 -0
  471. package/dist/assets/scala-C151Ov-r.js +2 -0
  472. package/dist/assets/scala-C151Ov-r.js.map +1 -0
  473. package/dist/assets/scheme-C98Dy4si.js +2 -0
  474. package/dist/assets/scheme-C98Dy4si.js.map +1 -0
  475. package/dist/assets/scss-D5BDwBP9.js +2 -0
  476. package/dist/assets/scss-D5BDwBP9.js.map +1 -0
  477. package/dist/assets/sdbl-DVxCFoDh.js +2 -0
  478. package/dist/assets/sdbl-DVxCFoDh.js.map +1 -0
  479. package/dist/assets/shaderlab-Dg9Lc6iA.js +2 -0
  480. package/dist/assets/shaderlab-Dg9Lc6iA.js.map +1 -0
  481. package/dist/assets/shellscript-Yzrsuije.js +2 -0
  482. package/dist/assets/shellscript-Yzrsuije.js.map +1 -0
  483. package/dist/assets/shellsession-BADoaaVG.js +2 -0
  484. package/dist/assets/shellsession-BADoaaVG.js.map +1 -0
  485. package/dist/assets/slack-dark-BthQWCQV.js +2 -0
  486. package/dist/assets/slack-dark-BthQWCQV.js.map +1 -0
  487. package/dist/assets/slack-ochin-DqwNpetd.js +2 -0
  488. package/dist/assets/slack-ochin-DqwNpetd.js.map +1 -0
  489. package/dist/assets/smalltalk-BERRCDM3.js +2 -0
  490. package/dist/assets/smalltalk-BERRCDM3.js.map +1 -0
  491. package/dist/assets/snazzy-light-Bw305WKR.js +2 -0
  492. package/dist/assets/snazzy-light-Bw305WKR.js.map +1 -0
  493. package/dist/assets/solarized-dark-DXbdFlpD.js +2 -0
  494. package/dist/assets/solarized-dark-DXbdFlpD.js.map +1 -0
  495. package/dist/assets/solarized-light-L9t79GZl.js +2 -0
  496. package/dist/assets/solarized-light-L9t79GZl.js.map +1 -0
  497. package/dist/assets/solidity-rGO070M0.js +2 -0
  498. package/dist/assets/solidity-rGO070M0.js.map +1 -0
  499. package/dist/assets/soy-8wufbnw4.js +2 -0
  500. package/dist/assets/soy-8wufbnw4.js.map +1 -0
  501. package/dist/assets/sparql-rVzFXLq3.js +2 -0
  502. package/dist/assets/sparql-rVzFXLq3.js.map +1 -0
  503. package/dist/assets/splunk-BtCnVYZw.js +2 -0
  504. package/dist/assets/splunk-BtCnVYZw.js.map +1 -0
  505. package/dist/assets/sql-CRqJ_cUM.js +2 -0
  506. package/dist/assets/sql-CRqJ_cUM.js.map +1 -0
  507. package/dist/assets/ssh-config-_ykCGR6B.js +2 -0
  508. package/dist/assets/ssh-config-_ykCGR6B.js.map +1 -0
  509. package/dist/assets/stata-DI20mbqo.js +2 -0
  510. package/dist/assets/stata-DI20mbqo.js.map +1 -0
  511. package/dist/assets/stylus-BEDo0Tqx.js +2 -0
  512. package/dist/assets/stylus-BEDo0Tqx.js.map +1 -0
  513. package/dist/assets/surrealql-Bq5Q-fJD.js +2 -0
  514. package/dist/assets/surrealql-Bq5Q-fJD.js.map +1 -0
  515. package/dist/assets/svelte-Cy7k_4gC.js +2 -0
  516. package/dist/assets/svelte-Cy7k_4gC.js.map +1 -0
  517. package/dist/assets/swift-D82vCrfD.js +2 -0
  518. package/dist/assets/swift-D82vCrfD.js.map +1 -0
  519. package/dist/assets/synthwave-84-CbfX1IO0.js +2 -0
  520. package/dist/assets/synthwave-84-CbfX1IO0.js.map +1 -0
  521. package/dist/assets/system-verilog-CnnmHF94.js +2 -0
  522. package/dist/assets/system-verilog-CnnmHF94.js.map +1 -0
  523. package/dist/assets/systemd-4A_iFExJ.js +2 -0
  524. package/dist/assets/systemd-4A_iFExJ.js.map +1 -0
  525. package/dist/assets/talonscript-CkByrt1z.js +2 -0
  526. package/dist/assets/talonscript-CkByrt1z.js.map +1 -0
  527. package/dist/assets/tasl-QIJgUcNo.js +2 -0
  528. package/dist/assets/tasl-QIJgUcNo.js.map +1 -0
  529. package/dist/assets/tcl-dwOrl1Do.js +2 -0
  530. package/dist/assets/tcl-dwOrl1Do.js.map +1 -0
  531. package/dist/assets/templ-DhtptRzy.js +2 -0
  532. package/dist/assets/templ-DhtptRzy.js.map +1 -0
  533. package/dist/assets/terraform-BETggiCN.js +2 -0
  534. package/dist/assets/terraform-BETggiCN.js.map +1 -0
  535. package/dist/assets/tex-idrVyKtj.js +2 -0
  536. package/dist/assets/tex-idrVyKtj.js.map +1 -0
  537. package/dist/assets/tokyo-night-hegEt444.js +2 -0
  538. package/dist/assets/tokyo-night-hegEt444.js.map +1 -0
  539. package/dist/assets/toml-vGWfd6FD.js +2 -0
  540. package/dist/assets/toml-vGWfd6FD.js.map +1 -0
  541. package/dist/assets/ts-tags-D351s5mN.js +2 -0
  542. package/dist/assets/ts-tags-D351s5mN.js.map +1 -0
  543. package/dist/assets/tsv-B_m7g4N7.js +2 -0
  544. package/dist/assets/tsv-B_m7g4N7.js.map +1 -0
  545. package/dist/assets/tsx-COt5Ahok.js +2 -0
  546. package/dist/assets/tsx-COt5Ahok.js.map +1 -0
  547. package/dist/assets/turtle-BsS91CYL.js +2 -0
  548. package/dist/assets/turtle-BsS91CYL.js.map +1 -0
  549. package/dist/assets/twig-CW1WmMYd.js +2 -0
  550. package/dist/assets/twig-CW1WmMYd.js.map +1 -0
  551. package/dist/assets/typescript-BPQ3VLAy.js +2 -0
  552. package/dist/assets/typescript-BPQ3VLAy.js.map +1 -0
  553. package/dist/assets/typespec-CAFt9gP4.js +2 -0
  554. package/dist/assets/typespec-CAFt9gP4.js.map +1 -0
  555. package/dist/assets/typst-DHCkPAjA.js +2 -0
  556. package/dist/assets/typst-DHCkPAjA.js.map +1 -0
  557. package/dist/assets/v-BcVCzyr7.js +2 -0
  558. package/dist/assets/v-BcVCzyr7.js.map +1 -0
  559. package/dist/assets/vala-CsfeWuGM.js +2 -0
  560. package/dist/assets/vala-CsfeWuGM.js.map +1 -0
  561. package/dist/assets/vb-D17OF-Vu.js +2 -0
  562. package/dist/assets/vb-D17OF-Vu.js.map +1 -0
  563. package/dist/assets/verilog-BQ8w6xss.js +2 -0
  564. package/dist/assets/verilog-BQ8w6xss.js.map +1 -0
  565. package/dist/assets/vesper-DRje8inN.js +2 -0
  566. package/dist/assets/vesper-DRje8inN.js.map +1 -0
  567. package/dist/assets/vhdl-CeAyd5Ju.js +2 -0
  568. package/dist/assets/vhdl-CeAyd5Ju.js.map +1 -0
  569. package/dist/assets/viml-CJc9bBzg.js +2 -0
  570. package/dist/assets/viml-CJc9bBzg.js.map +1 -0
  571. package/dist/assets/vitesse-black-Bkuqu6BP.js +2 -0
  572. package/dist/assets/vitesse-black-Bkuqu6BP.js.map +1 -0
  573. package/dist/assets/vitesse-dark-D0r3Knsf.js +2 -0
  574. package/dist/assets/vitesse-dark-D0r3Knsf.js.map +1 -0
  575. package/dist/assets/vitesse-light-CVO1_9PV.js +2 -0
  576. package/dist/assets/vitesse-light-CVO1_9PV.js.map +1 -0
  577. package/dist/assets/vue-D2xRrEX4.js +2 -0
  578. package/dist/assets/vue-D2xRrEX4.js.map +1 -0
  579. package/dist/assets/vue-html-AaS7Mt5G.js +2 -0
  580. package/dist/assets/vue-html-AaS7Mt5G.js.map +1 -0
  581. package/dist/assets/vue-vine-BoDAl6tE.js +2 -0
  582. package/dist/assets/vue-vine-BoDAl6tE.js.map +1 -0
  583. package/dist/assets/vyper-CDx5xZoG.js +2 -0
  584. package/dist/assets/vyper-CDx5xZoG.js.map +1 -0
  585. package/dist/assets/wasm-CG6Dc4jp.js +2 -0
  586. package/dist/assets/wasm-CG6Dc4jp.js.map +1 -0
  587. package/dist/assets/wasm-MzD3tlZU.js +2 -0
  588. package/dist/assets/wasm-MzD3tlZU.js.map +1 -0
  589. package/dist/assets/wenyan-BV7otONQ.js +2 -0
  590. package/dist/assets/wenyan-BV7otONQ.js.map +1 -0
  591. package/dist/assets/wgsl-Dx-B1_4e.js +2 -0
  592. package/dist/assets/wgsl-Dx-B1_4e.js.map +1 -0
  593. package/dist/assets/wikitext-BhOHFoWU.js +2 -0
  594. package/dist/assets/wikitext-BhOHFoWU.js.map +1 -0
  595. package/dist/assets/wit-5i3qLPDT.js +2 -0
  596. package/dist/assets/wit-5i3qLPDT.js.map +1 -0
  597. package/dist/assets/wolfram-lXgVvXCa.js +2 -0
  598. package/dist/assets/wolfram-lXgVvXCa.js.map +1 -0
  599. package/dist/assets/xml-sdJ4AIDG.js +2 -0
  600. package/dist/assets/xml-sdJ4AIDG.js.map +1 -0
  601. package/dist/assets/xsl-CtQFsRM5.js +2 -0
  602. package/dist/assets/xsl-CtQFsRM5.js.map +1 -0
  603. package/dist/assets/yaml-Buea-lGh.js +2 -0
  604. package/dist/assets/yaml-Buea-lGh.js.map +1 -0
  605. package/dist/assets/zenscript-DVFEvuxE.js +2 -0
  606. package/dist/assets/zenscript-DVFEvuxE.js.map +1 -0
  607. package/dist/assets/zig-VOosw3JB.js +2 -0
  608. package/dist/assets/zig-VOosw3JB.js.map +1 -0
  609. package/dist/cli/index.d.ts +2 -0
  610. package/dist/cli/index.js +5100 -0
  611. package/dist/core/index.d.ts +3640 -0
  612. package/dist/core/index.js +0 -0
  613. package/dist/favicon.svg +11 -0
  614. package/dist/hub/index.d.ts +1 -0
  615. package/dist/hub/index.js +54 -0
  616. package/dist/hub/serve.d.ts +7 -0
  617. package/dist/hub/serve.js +54 -0
  618. package/dist/index.html +18 -0
  619. package/dist/registry/index-Baj_sSgl.d.ts +218 -0
  620. package/dist/registry/index.d.ts +3 -0
  621. package/dist/registry/index.js +2981 -0
  622. package/dist/registry/serve.d.ts +7 -0
  623. package/dist/registry/serve.js +3523 -0
  624. package/drizzle/migrations/pg/0000_brief_night_thrasher.sql +39 -0
  625. package/drizzle/migrations/pg/0001_remarkable_swarm.sql +4 -0
  626. package/drizzle/migrations/pg/0002_wandering_vanisher.sql +38 -0
  627. package/drizzle/migrations/pg/0003_overrated_surge.sql +20 -0
  628. package/drizzle/migrations/pg/meta/0000_snapshot.json +250 -0
  629. package/drizzle/migrations/pg/meta/0001_snapshot.json +318 -0
  630. package/drizzle/migrations/pg/meta/0002_snapshot.json +571 -0
  631. package/drizzle/migrations/pg/meta/0003_snapshot.json +732 -0
  632. package/drizzle/migrations/pg/meta/_journal.json +34 -0
  633. package/drizzle/migrations/sqlite/0000_curly_firebrand.sql +39 -0
  634. package/drizzle/migrations/sqlite/0001_amazing_purple_man.sql +4 -0
  635. package/drizzle/migrations/sqlite/0002_powerful_union_jack.sql +38 -0
  636. package/drizzle/migrations/sqlite/0003_aspiring_edwin_jarvis.sql +20 -0
  637. package/drizzle/migrations/sqlite/meta/0000_snapshot.json +268 -0
  638. package/drizzle/migrations/sqlite/meta/0001_snapshot.json +299 -0
  639. package/drizzle/migrations/sqlite/meta/0002_snapshot.json +543 -0
  640. package/drizzle/migrations/sqlite/meta/0003_snapshot.json +676 -0
  641. package/drizzle/migrations/sqlite/meta/_journal.json +34 -0
  642. package/package.json +116 -0
@@ -0,0 +1,3523 @@
1
+ // src/registry/serve.ts
2
+ import path from "path";
3
+ import fs from "fs";
4
+ import { serve } from "@hono/node-server";
5
+
6
+ // src/registry/server.ts
7
+ import { Hono as Hono22 } from "hono";
8
+ import { logger } from "hono/logger";
9
+ import { cors } from "hono/cors";
10
+ import { prettyJSON } from "hono/pretty-json";
11
+
12
+ // src/registry/routes/push.ts
13
+ import { Hono } from "hono";
14
+
15
+ // src/registry/services/registry.ts
16
+ import { v4 as uuid2 } from "uuid";
17
+
18
+ // src/registry/utils.ts
19
+ import crypto from "crypto";
20
+ function computeChecksum(content) {
21
+ return crypto.createHash("sha256").update(content).digest("hex");
22
+ }
23
+
24
+ // src/registry/utils/normalize-spec.ts
25
+ import yaml from "js-yaml";
26
+ var HTTP_METHODS = ["get", "post", "put", "delete", "patch", "head", "options", "trace"];
27
+ var ROOT_ORDER = ["openapi", "info", "servers", "tags", "paths", "webhooks", "components", "security", "externalDocs"];
28
+ var OPERATION_ORDER = ["tags", "summary", "description", "operationId", "parameters", "requestBody", "responses", "security", "deprecated"];
29
+ var COMPONENTS_ORDER = ["schemas", "responses", "parameters", "requestBodies", "headers", "securitySchemes", "links", "callbacks"];
30
+ function normalizeSpec(content) {
31
+ const trimmed = content.trimStart();
32
+ const isJson = trimmed.startsWith("{") || trimmed.startsWith("[");
33
+ const parsed = yaml.load(content);
34
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
35
+ throw new Error("Invalid spec content: not a valid YAML/JSON object");
36
+ }
37
+ const canonical = sortObjectKeys(parsed, []);
38
+ if (isJson) {
39
+ return JSON.stringify(canonical, null, 2) + "\n";
40
+ }
41
+ return yaml.dump(canonical, {
42
+ noRefs: true,
43
+ lineWidth: -1
44
+ });
45
+ }
46
+ function sortObjectKeys(value, path2) {
47
+ if (Array.isArray(value)) {
48
+ return value.map((item) => sortObjectKeys(item, path2));
49
+ }
50
+ if (value && typeof value === "object") {
51
+ const obj = value;
52
+ const keys = Object.keys(obj);
53
+ const sortedKeys = getSortedKeys(keys, path2);
54
+ const sorted = {};
55
+ for (const key of sortedKeys) {
56
+ sorted[key] = sortObjectKeys(obj[key], [...path2, key]);
57
+ }
58
+ return sorted;
59
+ }
60
+ return value;
61
+ }
62
+ function getSortedKeys(keys, path2) {
63
+ if (path2.length === 0) {
64
+ return sortKeysByOrder(keys, ROOT_ORDER);
65
+ }
66
+ if (path2.length === 2 && (path2[0] === "paths" || path2[0] === "webhooks")) {
67
+ return sortPathItemKeys(keys);
68
+ }
69
+ if (path2.length === 3 && (path2[0] === "paths" || path2[0] === "webhooks") && HTTP_METHODS.includes(path2[2])) {
70
+ return sortKeysByOrder(keys, OPERATION_ORDER);
71
+ }
72
+ if (path2.length === 1 && path2[0] === "components") {
73
+ return sortKeysByOrder(keys, COMPONENTS_ORDER);
74
+ }
75
+ return keys.sort();
76
+ }
77
+ function sortKeysByOrder(keys, order) {
78
+ const known = new Set(order);
79
+ const knownKeys = [];
80
+ const unknownKeys = [];
81
+ for (const key of keys) {
82
+ if (known.has(key)) {
83
+ knownKeys.push(key);
84
+ } else {
85
+ unknownKeys.push(key);
86
+ }
87
+ }
88
+ knownKeys.sort((a, b) => order.indexOf(a) - order.indexOf(b));
89
+ unknownKeys.sort();
90
+ return [...knownKeys, ...unknownKeys];
91
+ }
92
+ function sortPathItemKeys(keys) {
93
+ const methodSet = new Set(HTTP_METHODS);
94
+ const methods = [];
95
+ const others = [];
96
+ for (const key of keys) {
97
+ if (methodSet.has(key)) {
98
+ methods.push(key);
99
+ } else {
100
+ others.push(key);
101
+ }
102
+ }
103
+ methods.sort((a, b) => HTTP_METHODS.indexOf(a) - HTTP_METHODS.indexOf(b));
104
+ others.sort();
105
+ return [...methods, ...others];
106
+ }
107
+
108
+ // src/registry/parser/openapi/parse.ts
109
+ import yaml2 from "js-yaml";
110
+ function parseOpenApiSpec(content) {
111
+ const obj = yaml2.load(content);
112
+ if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
113
+ throw new Error("Invalid spec content: not a valid YAML/JSON object");
114
+ }
115
+ return obj;
116
+ }
117
+
118
+ // src/registry/compat-engine/differ.ts
119
+ var HTTP_METHODS2 = ["get", "post", "put", "delete", "patch", "head", "options", "trace"];
120
+ function createRefResolver(spec) {
121
+ return (ref) => {
122
+ if (!ref.startsWith("#/")) return void 0;
123
+ const path2 = ref.replace("#/", "").split("/");
124
+ let current = spec;
125
+ for (const key of path2) {
126
+ if (current && typeof current === "object") {
127
+ current = current[key];
128
+ } else {
129
+ return void 0;
130
+ }
131
+ }
132
+ return current;
133
+ };
134
+ }
135
+ function resolveSchemaRef(resolver, schema) {
136
+ if (!schema || typeof schema !== "object") return schema;
137
+ if (schema.$ref) {
138
+ const resolved = resolver(schema.$ref);
139
+ if (resolved && typeof resolved === "object") {
140
+ return resolved;
141
+ }
142
+ }
143
+ return schema;
144
+ }
145
+ function resolveParamRef(resolver, param) {
146
+ if (!param || typeof param !== "object") return param;
147
+ if (param.$ref) {
148
+ const resolved = resolver(param.$ref);
149
+ if (resolved && typeof resolved === "object") {
150
+ return resolved;
151
+ }
152
+ }
153
+ return param;
154
+ }
155
+ function diffSpecs(oldSpec, newSpec) {
156
+ const changes = [];
157
+ const oldResolver = createRefResolver(oldSpec);
158
+ const newResolver = createRefResolver(newSpec);
159
+ changes.push(...diffGlobalMetadata(oldSpec, newSpec));
160
+ const oldPaths = oldSpec.paths ?? {};
161
+ const newPaths = newSpec.paths ?? {};
162
+ for (const path2 of Object.keys(oldPaths)) {
163
+ const oldPathItem = oldPaths[path2] ?? {};
164
+ const newPathItem = newPaths[path2] ?? {};
165
+ for (const method of HTTP_METHODS2) {
166
+ const oldOp = oldPathItem[method];
167
+ const newOp = newPathItem[method];
168
+ if (oldOp && !newOp) {
169
+ changes.push({
170
+ type: "endpoint-removed",
171
+ path: path2,
172
+ method,
173
+ wasDeprecated: !!oldOp.deprecated,
174
+ xSunset: typeof oldOp["x-sunset"] === "string" ? oldOp["x-sunset"] : void 0,
175
+ wasDraft: !!oldOp["x-draft"]
176
+ });
177
+ } else if (oldOp && newOp) {
178
+ if (!oldOp["x-draft"] && newOp["x-draft"]) {
179
+ changes.push({ type: "stable-endpoint-marked-draft", path: path2, method });
180
+ }
181
+ if (oldOp["x-draft"] && !newOp["x-draft"]) {
182
+ changes.push({ type: "draft-endpoint-marked-stable", path: path2, method });
183
+ }
184
+ if (!oldOp.deprecated && newOp.deprecated) {
185
+ changes.push({
186
+ type: "endpoint-deprecated",
187
+ path: path2,
188
+ method,
189
+ xSunset: typeof newOp["x-sunset"] === "string" ? newOp["x-sunset"] : void 0
190
+ });
191
+ }
192
+ if (oldOp.deprecated && !newOp.deprecated) {
193
+ changes.push({ type: "endpoint-un-deprecated", path: path2, method });
194
+ }
195
+ changes.push(...diffOperationMetadata(path2, method, oldOp, newOp));
196
+ changes.push(...diffParameters(path2, method, oldOp, newOp, oldResolver, newResolver));
197
+ changes.push(...diffRequestBody(path2, method, oldOp, newOp, oldResolver, newResolver));
198
+ changes.push(...diffResponses(path2, method, oldOp, newOp, oldResolver, newResolver));
199
+ }
200
+ }
201
+ }
202
+ for (const path2 of Object.keys(newPaths)) {
203
+ const oldPathItem = oldPaths[path2] ?? {};
204
+ const newPathItem = newPaths[path2] ?? {};
205
+ for (const method of HTTP_METHODS2) {
206
+ if (!oldPathItem[method] && newPathItem[method]) {
207
+ changes.push({ type: "endpoint-added", path: path2, method });
208
+ }
209
+ }
210
+ }
211
+ return changes;
212
+ }
213
+ function diffGlobalMetadata(oldSpec, newSpec) {
214
+ const changes = [];
215
+ const oldInfo = oldSpec.info ?? {};
216
+ const newInfo = newSpec.info ?? {};
217
+ if (oldInfo.title !== newInfo.title && (oldInfo.title !== void 0 || newInfo.title !== void 0)) {
218
+ changes.push({ type: "documentation-updated", kind: "info-title-changed", path: "/info/title", detail: `${oldInfo.title} \u2192 ${newInfo.title}` });
219
+ }
220
+ if (oldInfo.version !== newInfo.version && (oldInfo.version !== void 0 || newInfo.version !== void 0)) {
221
+ changes.push({ type: "documentation-updated", kind: "info-version-changed", path: "/info/version", detail: `${oldInfo.version} \u2192 ${newInfo.version}` });
222
+ }
223
+ if (oldInfo.description !== newInfo.description && (oldInfo.description !== void 0 || newInfo.description !== void 0)) {
224
+ changes.push({ type: "documentation-updated", kind: "info-description-changed", path: "/info/description" });
225
+ }
226
+ const oldServers = (oldSpec.servers ?? []).map((s) => typeof s === "string" ? s : s?.url).filter(Boolean);
227
+ const newServers = (newSpec.servers ?? []).map((s) => typeof s === "string" ? s : s?.url).filter(Boolean);
228
+ if (!arraysEqual(oldServers, newServers)) {
229
+ changes.push({ type: "documentation-updated", kind: "servers-changed", path: "/servers" });
230
+ }
231
+ const oldSecurity = oldSpec.security ?? [];
232
+ const newSecurity = newSpec.security ?? [];
233
+ if (oldSecurity.length === 0 && newSecurity.length > 0) {
234
+ changes.push({ type: "global-security-added" });
235
+ } else if (oldSecurity.length > 0 && newSecurity.length === 0) {
236
+ changes.push({ type: "global-security-removed" });
237
+ } else if (!deepEqual(oldSecurity, newSecurity)) {
238
+ changes.push({ type: "documentation-updated", kind: "security-scheme-changed", path: "/security" });
239
+ }
240
+ const oldWebhooks = Object.keys(oldSpec.webhooks ?? {});
241
+ const newWebhooks = Object.keys(newSpec.webhooks ?? {});
242
+ const addedWebhooks = newWebhooks.filter((w) => !oldWebhooks.includes(w));
243
+ const removedWebhooks = oldWebhooks.filter((w) => !newWebhooks.includes(w));
244
+ for (const w of addedWebhooks) {
245
+ changes.push({ type: "documentation-updated", kind: "webhook-added", path: `/webhooks/${w}` });
246
+ }
247
+ for (const w of removedWebhooks) {
248
+ changes.push({ type: "documentation-updated", kind: "webhook-removed", path: `/webhooks/${w}` });
249
+ }
250
+ if (!deepEqual(oldSpec.externalDocs, newSpec.externalDocs)) {
251
+ changes.push({ type: "documentation-updated", kind: "external-docs-changed", path: "/externalDocs" });
252
+ }
253
+ return changes;
254
+ }
255
+ function diffOperationMetadata(path2, method, oldOp, newOp) {
256
+ const changes = [];
257
+ if (oldOp.operationId !== newOp.operationId && (oldOp.operationId !== void 0 || newOp.operationId !== void 0)) {
258
+ changes.push({ type: "documentation-updated", kind: "operation-id-changed", path: path2, method });
259
+ }
260
+ if (oldOp.summary !== newOp.summary && (oldOp.summary !== void 0 || newOp.summary !== void 0)) {
261
+ changes.push({ type: "documentation-updated", kind: "summary-changed", path: path2, method });
262
+ }
263
+ if (oldOp.description !== newOp.description && (oldOp.description !== void 0 || newOp.description !== void 0)) {
264
+ changes.push({ type: "documentation-updated", kind: "description-changed", path: path2, method });
265
+ }
266
+ if (!arraysEqual(oldOp.tags ?? [], newOp.tags ?? [])) {
267
+ changes.push({ type: "documentation-updated", kind: "tags-changed", path: path2, method });
268
+ }
269
+ return changes;
270
+ }
271
+ function diffParameters(path2, method, oldOp, newOp, oldResolver, newResolver) {
272
+ const changes = [];
273
+ const wasDraft = !!oldOp["x-draft"];
274
+ const oldParamMap = /* @__PURE__ */ new Map();
275
+ for (const p of oldOp.parameters ?? []) {
276
+ const resolved = resolveParamRef(oldResolver, p);
277
+ if (resolved) {
278
+ oldParamMap.set(`${resolved.name}:${resolved.in}`, resolved);
279
+ }
280
+ }
281
+ const newParamMap = /* @__PURE__ */ new Map();
282
+ for (const p of newOp.parameters ?? []) {
283
+ const resolved = resolveParamRef(newResolver, p);
284
+ if (resolved) {
285
+ newParamMap.set(`${resolved.name}:${resolved.in}`, resolved);
286
+ }
287
+ }
288
+ for (const [key, newParam] of newParamMap) {
289
+ if (!oldParamMap.has(key)) {
290
+ if (newParam.required) {
291
+ changes.push({ type: "required-param-added", path: path2, method, paramName: newParam.name, paramIn: newParam.in, wasDraft });
292
+ } else {
293
+ changes.push({ type: "optional-param-added", path: path2, method, paramName: newParam.name, paramIn: newParam.in });
294
+ }
295
+ }
296
+ }
297
+ for (const [key, oldParam] of oldParamMap) {
298
+ const newParam = newParamMap.get(key);
299
+ if (!newParam) {
300
+ changes.push({
301
+ type: "param-removed",
302
+ path: path2,
303
+ method,
304
+ paramName: oldParam.name,
305
+ paramIn: oldParam.in,
306
+ wasDeprecated: !!oldParam.deprecated,
307
+ xSunset: typeof oldParam["x-sunset"] === "string" ? oldParam["x-sunset"] : void 0,
308
+ wasDraft
309
+ });
310
+ continue;
311
+ }
312
+ const oldType = oldParam.schema?.type ?? oldParam.type ?? "any";
313
+ const newType = newParam.schema?.type ?? newParam.type ?? "any";
314
+ if (oldType !== newType) {
315
+ changes.push({ type: "param-type-changed", path: path2, method, paramName: oldParam.name, paramIn: oldParam.in, oldType, newType, wasDraft });
316
+ }
317
+ const oldFormat = oldParam.schema?.format ?? oldParam.format ?? "";
318
+ const newFormat = newParam.schema?.format ?? newParam.format ?? "";
319
+ if (oldFormat !== newFormat) {
320
+ changes.push({ type: "param-format-changed", path: path2, method, paramName: oldParam.name, paramIn: oldParam.in, oldFormat, newFormat });
321
+ }
322
+ if (!oldParam.required && newParam.required) {
323
+ changes.push({ type: "param-now-required", path: path2, method, paramName: oldParam.name, paramIn: oldParam.in, wasDraft });
324
+ }
325
+ if (oldParam.required && !newParam.required) {
326
+ changes.push({ type: "param-now-optional", path: path2, method, paramName: oldParam.name, paramIn: oldParam.in });
327
+ }
328
+ if (oldParam.description !== newParam.description && (oldParam.description !== void 0 || newParam.description !== void 0)) {
329
+ changes.push({ type: "param-description-changed", path: path2, method, paramName: oldParam.name, paramIn: oldParam.in });
330
+ }
331
+ if (!deepEqual(oldParam.default, newParam.default)) {
332
+ changes.push({ type: "param-default-changed", path: path2, method, paramName: oldParam.name, paramIn: oldParam.in });
333
+ }
334
+ if (!oldParam.deprecated && newParam.deprecated) {
335
+ changes.push({
336
+ type: "param-deprecated",
337
+ path: path2,
338
+ method,
339
+ paramName: oldParam.name,
340
+ paramIn: oldParam.in,
341
+ xSunset: typeof newParam["x-sunset"] === "string" ? newParam["x-sunset"] : void 0
342
+ });
343
+ }
344
+ if (oldParam.deprecated && !newParam.deprecated) {
345
+ changes.push({ type: "param-un-deprecated", path: path2, method, paramName: oldParam.name, paramIn: oldParam.in });
346
+ }
347
+ const oldEnum = new Set(Array.isArray(oldParam.schema?.enum) ? oldParam.schema.enum : []);
348
+ const newEnum = new Set(Array.isArray(newParam.schema?.enum) ? newParam.schema.enum : []);
349
+ for (const v of newEnum) {
350
+ if (!oldEnum.has(v)) {
351
+ changes.push({ type: "param-enum-value-added", path: path2, method, paramName: oldParam.name, paramIn: oldParam.in, value: String(v) });
352
+ }
353
+ }
354
+ for (const v of oldEnum) {
355
+ if (!newEnum.has(v)) {
356
+ changes.push({ type: "param-enum-value-removed", path: path2, method, paramName: oldParam.name, paramIn: oldParam.in, value: String(v), wasDraft });
357
+ }
358
+ }
359
+ }
360
+ return changes;
361
+ }
362
+ function diffRequestBody(path2, method, oldOp, newOp, oldResolver, newResolver) {
363
+ const changes = [];
364
+ const wasDraft = !!oldOp["x-draft"];
365
+ const oldBody = oldOp.requestBody;
366
+ const newBody = newOp.requestBody;
367
+ if (!oldBody && newBody) {
368
+ changes.push({ type: "request-body-added", path: path2, method });
369
+ return changes;
370
+ }
371
+ if (oldBody && !newBody) {
372
+ changes.push({ type: "request-body-removed", path: path2, method, wasDraft });
373
+ return changes;
374
+ }
375
+ if (!oldBody && !newBody) {
376
+ return changes;
377
+ }
378
+ const oldMediaTypes = Object.keys(oldBody.content ?? {});
379
+ const newMediaTypes = Object.keys(newBody.content ?? {});
380
+ const sharedMediaTypes = oldMediaTypes.filter((m) => newMediaTypes.includes(m));
381
+ if (oldMediaTypes.length > 0 && newMediaTypes.length > 0 && sharedMediaTypes.length === 0) {
382
+ changes.push({
383
+ type: "request-body-media-type-changed",
384
+ path: path2,
385
+ method,
386
+ oldMediaType: oldMediaTypes[0],
387
+ newMediaType: newMediaTypes[0]
388
+ });
389
+ }
390
+ if (oldBody.description !== newBody.description && (oldBody.description !== void 0 || newBody.description !== void 0)) {
391
+ changes.push({ type: "request-body-description-changed", path: path2, method });
392
+ }
393
+ for (const mediaType of sharedMediaTypes) {
394
+ const oldSchema = oldBody.content[mediaType]?.schema;
395
+ const newSchema = newBody.content[mediaType]?.schema;
396
+ if (oldSchema && newSchema) {
397
+ changes.push(...diffSchemaProperties(path2, method, oldSchema, newSchema, "request-body", wasDraft, oldResolver, newResolver));
398
+ }
399
+ }
400
+ return changes;
401
+ }
402
+ function diffResponses(path2, method, oldOp, newOp, oldResolver, newResolver) {
403
+ const changes = [];
404
+ const wasDraft = !!oldOp["x-draft"];
405
+ const oldResponses = oldOp.responses ?? {};
406
+ const newResponses = newOp.responses ?? {};
407
+ for (const status of Object.keys(oldResponses)) {
408
+ if (!newResponses[status]) {
409
+ changes.push({ type: "response-status-removed", path: path2, method, statusCode: status, wasDraft });
410
+ } else {
411
+ changes.push(...diffResponse(path2, method, status, oldResponses[status], newResponses[status], wasDraft, oldResolver, newResolver));
412
+ }
413
+ }
414
+ for (const status of Object.keys(newResponses)) {
415
+ if (!oldResponses[status]) {
416
+ changes.push({ type: "response-status-added", path: path2, method, statusCode: status });
417
+ }
418
+ }
419
+ return changes;
420
+ }
421
+ function diffResponse(path2, method, statusCode, oldResp, newResp, wasDraft, oldResolver, newResolver) {
422
+ const changes = [];
423
+ if (oldResp.description !== newResp.description && (oldResp.description !== void 0 || newResp.description !== void 0)) {
424
+ changes.push({ type: "response-description-changed", path: path2, method, statusCode });
425
+ }
426
+ const oldMediaTypes = Object.keys(oldResp.content ?? {});
427
+ const newMediaTypes = Object.keys(newResp.content ?? {});
428
+ const sharedMediaTypes = oldMediaTypes.filter((m) => newMediaTypes.includes(m));
429
+ if (oldMediaTypes.length > 0 && newMediaTypes.length > 0 && sharedMediaTypes.length === 0) {
430
+ changes.push({
431
+ type: "response-media-type-changed",
432
+ path: path2,
433
+ method,
434
+ statusCode,
435
+ oldMediaType: oldMediaTypes[0],
436
+ newMediaType: newMediaTypes[0]
437
+ });
438
+ }
439
+ for (const mediaType of sharedMediaTypes) {
440
+ const oldSchema = oldResp.content[mediaType]?.schema;
441
+ const newSchema = newResp.content[mediaType]?.schema;
442
+ if (oldSchema && newSchema) {
443
+ changes.push(...diffSchemaProperties(path2, method, oldSchema, newSchema, `response/${statusCode}`, wasDraft, oldResolver, newResolver));
444
+ }
445
+ }
446
+ return changes;
447
+ }
448
+ function diffSchemaProperties(path2, method, oldSchema, newSchema, prefix, wasDraft, oldResolver, newResolver) {
449
+ const changes = [];
450
+ const oldProps = flattenSchemaProperties(oldSchema, oldResolver);
451
+ const newProps = flattenSchemaProperties(newSchema, newResolver);
452
+ for (const [propPath, newProp] of newProps) {
453
+ const fullPath = prefix ? `${prefix}.${propPath}` : propPath;
454
+ if (!oldProps.has(propPath)) {
455
+ if (prefix.startsWith("request-body")) {
456
+ changes.push({
457
+ type: "request-body-property-added",
458
+ path: path2,
459
+ method,
460
+ propertyPath: propPath,
461
+ required: newProp.required,
462
+ wasDraft
463
+ });
464
+ } else if (prefix.startsWith("response/")) {
465
+ const statusCode = prefix.split("/")[1];
466
+ changes.push({
467
+ type: "response-property-added",
468
+ path: path2,
469
+ method,
470
+ statusCode,
471
+ propertyPath: propPath
472
+ });
473
+ }
474
+ }
475
+ }
476
+ for (const [propPath, oldProp] of oldProps) {
477
+ const fullPath = prefix ? `${prefix}.${propPath}` : propPath;
478
+ const newProp = newProps.get(propPath);
479
+ if (!newProp) {
480
+ const wasDeprecated = !!oldProp.schema?.deprecated;
481
+ const xSunset = typeof oldProp.schema?.["x-sunset"] === "string" ? oldProp.schema["x-sunset"] : void 0;
482
+ if (prefix.startsWith("request-body")) {
483
+ changes.push({
484
+ type: "request-body-property-removed",
485
+ path: path2,
486
+ method,
487
+ propertyPath: propPath,
488
+ wasDeprecated,
489
+ xSunset,
490
+ wasDraft
491
+ });
492
+ } else if (prefix.startsWith("response/")) {
493
+ const statusCode = prefix.split("/")[1];
494
+ changes.push({
495
+ type: "response-property-removed",
496
+ path: path2,
497
+ method,
498
+ statusCode,
499
+ propertyPath: propPath,
500
+ originalValue: safeStringify(oldProp.schema),
501
+ wasDeprecated,
502
+ xSunset,
503
+ wasDraft
504
+ });
505
+ }
506
+ continue;
507
+ }
508
+ if (!oldProp.schema?.deprecated && newProp.schema?.deprecated) {
509
+ const xSunset = typeof newProp.schema?.["x-sunset"] === "string" ? newProp.schema["x-sunset"] : void 0;
510
+ if (prefix.startsWith("request-body")) {
511
+ changes.push({
512
+ type: "request-body-property-deprecated",
513
+ path: path2,
514
+ method,
515
+ propertyPath: propPath,
516
+ xSunset
517
+ });
518
+ } else if (prefix.startsWith("response/")) {
519
+ const statusCode = prefix.split("/")[1];
520
+ changes.push({
521
+ type: "response-property-deprecated",
522
+ path: path2,
523
+ method,
524
+ statusCode,
525
+ propertyPath: propPath,
526
+ xSunset
527
+ });
528
+ }
529
+ }
530
+ if (oldProp.schema?.deprecated && !newProp.schema?.deprecated) {
531
+ if (prefix.startsWith("request-body")) {
532
+ changes.push({
533
+ type: "request-body-property-un-deprecated",
534
+ path: path2,
535
+ method,
536
+ propertyPath: propPath
537
+ });
538
+ } else if (prefix.startsWith("response/")) {
539
+ const statusCode = prefix.split("/")[1];
540
+ changes.push({
541
+ type: "response-property-un-deprecated",
542
+ path: path2,
543
+ method,
544
+ statusCode,
545
+ propertyPath: propPath
546
+ });
547
+ }
548
+ }
549
+ const oldType = oldProp.schema?.type ?? "any";
550
+ const newType = newProp.schema?.type ?? "any";
551
+ if (oldType !== newType) {
552
+ if (prefix.startsWith("request-body")) {
553
+ changes.push({
554
+ type: "request-body-property-type-changed",
555
+ path: path2,
556
+ method,
557
+ propertyPath: propPath,
558
+ oldType,
559
+ newType,
560
+ wasDraft
561
+ });
562
+ } else if (prefix.startsWith("response/")) {
563
+ const statusCode = prefix.split("/")[1];
564
+ changes.push({
565
+ type: "response-property-type-changed",
566
+ path: path2,
567
+ method,
568
+ statusCode,
569
+ propertyPath: propPath,
570
+ oldType,
571
+ newType,
572
+ wasDraft
573
+ });
574
+ }
575
+ }
576
+ if (!oldProp.required && newProp.required) {
577
+ if (prefix.startsWith("request-body")) {
578
+ changes.push({
579
+ type: "request-body-property-now-required",
580
+ path: path2,
581
+ method,
582
+ propertyPath: propPath,
583
+ wasDraft
584
+ });
585
+ } else if (prefix.startsWith("response/")) {
586
+ const statusCode = prefix.split("/")[1];
587
+ changes.push({
588
+ type: "response-property-now-required",
589
+ path: path2,
590
+ method,
591
+ statusCode,
592
+ propertyPath: propPath,
593
+ wasDraft
594
+ });
595
+ }
596
+ }
597
+ if (oldProp.required && !newProp.required) {
598
+ if (prefix.startsWith("request-body")) {
599
+ changes.push({
600
+ type: "request-body-property-now-optional",
601
+ path: path2,
602
+ method,
603
+ propertyPath: propPath
604
+ });
605
+ } else if (prefix.startsWith("response/")) {
606
+ const statusCode = prefix.split("/")[1];
607
+ changes.push({
608
+ type: "response-property-now-optional",
609
+ path: path2,
610
+ method,
611
+ statusCode,
612
+ propertyPath: propPath
613
+ });
614
+ }
615
+ }
616
+ }
617
+ return changes;
618
+ }
619
+ function flattenSchemaProperties(schema, resolver, prefix = "", parentRequired = false) {
620
+ const result = /* @__PURE__ */ new Map();
621
+ if (!schema || typeof schema !== "object") return result;
622
+ const resolved = resolveSchemaRef(resolver, schema);
623
+ if (!resolved || typeof resolved !== "object") return result;
624
+ const requiredSet = new Set(Array.isArray(resolved.required) ? resolved.required : []);
625
+ if (resolved.properties) {
626
+ for (const [key, value] of Object.entries(resolved.properties)) {
627
+ const propPath = prefix ? `${prefix}.${key}` : key;
628
+ const isRequired = requiredSet.has(key);
629
+ result.set(propPath, { schema: value, required: isRequired });
630
+ if (value?.properties || value?.allOf || value?.anyOf || value?.oneOf || value?.$ref || value?.additionalProperties && typeof value.additionalProperties === "object") {
631
+ for (const [subKey, subVal] of flattenSchemaProperties(value, resolver, propPath, isRequired)) {
632
+ result.set(subKey, subVal);
633
+ }
634
+ }
635
+ }
636
+ }
637
+ if (resolved.additionalProperties && typeof resolved.additionalProperties === "object") {
638
+ const ap = resolved.additionalProperties;
639
+ if (ap.type || ap.properties || ap.allOf || ap.anyOf || ap.oneOf || ap.$ref) {
640
+ for (const [subKey, subVal] of flattenSchemaProperties(ap, resolver, prefix, parentRequired)) {
641
+ if (!result.has(subKey)) {
642
+ result.set(subKey, subVal);
643
+ }
644
+ }
645
+ }
646
+ }
647
+ const composed = resolved.allOf ?? resolved.anyOf ?? resolved.oneOf;
648
+ if (composed) {
649
+ for (const sub of composed) {
650
+ for (const [subKey, subVal] of flattenSchemaProperties(sub, resolver, prefix, parentRequired)) {
651
+ if (!result.has(subKey)) {
652
+ result.set(subKey, subVal);
653
+ }
654
+ }
655
+ }
656
+ }
657
+ return result;
658
+ }
659
+ function arraysEqual(a, b) {
660
+ if (a.length !== b.length) return false;
661
+ for (let i = 0; i < a.length; i++) {
662
+ if (!deepEqual(a[i], b[i])) return false;
663
+ }
664
+ return true;
665
+ }
666
+ function deepEqual(a, b) {
667
+ if (a === b) return true;
668
+ if (a == null || b == null) return false;
669
+ if (typeof a !== typeof b) return false;
670
+ if (typeof a !== "object") return false;
671
+ const aKeys = Object.keys(a);
672
+ const bKeys = Object.keys(b);
673
+ if (aKeys.length !== bKeys.length) return false;
674
+ for (const key of aKeys) {
675
+ if (!bKeys.includes(key)) return false;
676
+ if (!deepEqual(a[key], b[key])) return false;
677
+ }
678
+ return true;
679
+ }
680
+ function safeStringify(value) {
681
+ try {
682
+ return JSON.stringify(value);
683
+ } catch {
684
+ return String(value);
685
+ }
686
+ }
687
+
688
+ // src/registry/compat-engine/grace-period.ts
689
+ function checkGracePeriod(changes) {
690
+ const violations = [];
691
+ const now = /* @__PURE__ */ new Date();
692
+ for (const change of changes) {
693
+ if (change.type === "endpoint-removed" && change.wasDraft) continue;
694
+ if (change.type === "param-removed" && change.wasDraft) continue;
695
+ if (change.type === "request-body-property-removed" && change.wasDraft) continue;
696
+ if (change.type === "response-property-removed" && change.wasDraft) continue;
697
+ if (change.type === "endpoint-removed") {
698
+ if (!change.wasDeprecated) {
699
+ violations.push({ path: change.path, method: change.method, rule: "endpoint-removed-without-deprecation" });
700
+ continue;
701
+ }
702
+ if (!change.xSunset) {
703
+ violations.push({ path: change.path, method: change.method, rule: "endpoint-removed-missing-sunset" });
704
+ continue;
705
+ }
706
+ const sunsetDate = new Date(change.xSunset);
707
+ if (isNaN(sunsetDate.getTime()) || sunsetDate >= now) {
708
+ violations.push({
709
+ path: change.path,
710
+ method: change.method,
711
+ rule: "endpoint-removed-before-sunset",
712
+ xSunset: change.xSunset
713
+ });
714
+ }
715
+ continue;
716
+ }
717
+ if (change.type === "param-removed") {
718
+ if (!change.wasDeprecated) {
719
+ violations.push({
720
+ path: change.path,
721
+ method: change.method,
722
+ rule: "param-removed-without-deprecation",
723
+ paramName: change.paramName,
724
+ paramIn: change.paramIn
725
+ });
726
+ continue;
727
+ }
728
+ if (!change.xSunset) {
729
+ violations.push({
730
+ path: change.path,
731
+ method: change.method,
732
+ rule: "param-removed-missing-sunset",
733
+ paramName: change.paramName,
734
+ paramIn: change.paramIn
735
+ });
736
+ continue;
737
+ }
738
+ const sunsetDate = new Date(change.xSunset);
739
+ if (isNaN(sunsetDate.getTime()) || sunsetDate >= now) {
740
+ violations.push({
741
+ path: change.path,
742
+ method: change.method,
743
+ rule: "param-removed-before-sunset",
744
+ xSunset: change.xSunset,
745
+ paramName: change.paramName,
746
+ paramIn: change.paramIn
747
+ });
748
+ }
749
+ continue;
750
+ }
751
+ if (change.type === "request-body-property-removed") {
752
+ if (!change.wasDeprecated) {
753
+ violations.push({
754
+ path: change.path,
755
+ method: change.method,
756
+ rule: "request-body-property-removed-without-deprecation",
757
+ propertyPath: change.propertyPath
758
+ });
759
+ continue;
760
+ }
761
+ if (!change.xSunset) {
762
+ violations.push({
763
+ path: change.path,
764
+ method: change.method,
765
+ rule: "request-body-property-removed-missing-sunset",
766
+ propertyPath: change.propertyPath
767
+ });
768
+ continue;
769
+ }
770
+ const sunsetDate = new Date(change.xSunset);
771
+ if (isNaN(sunsetDate.getTime()) || sunsetDate >= now) {
772
+ violations.push({
773
+ path: change.path,
774
+ method: change.method,
775
+ rule: "request-body-property-removed-before-sunset",
776
+ xSunset: change.xSunset,
777
+ propertyPath: change.propertyPath
778
+ });
779
+ }
780
+ continue;
781
+ }
782
+ if (change.type === "response-property-removed") {
783
+ if (!change.wasDeprecated) {
784
+ violations.push({
785
+ path: change.path,
786
+ method: change.method,
787
+ rule: "response-property-removed-without-deprecation",
788
+ statusCode: change.statusCode,
789
+ propertyPath: change.propertyPath
790
+ });
791
+ continue;
792
+ }
793
+ if (!change.xSunset) {
794
+ violations.push({
795
+ path: change.path,
796
+ method: change.method,
797
+ rule: "response-property-removed-missing-sunset",
798
+ statusCode: change.statusCode,
799
+ propertyPath: change.propertyPath
800
+ });
801
+ continue;
802
+ }
803
+ const sunsetDate = new Date(change.xSunset);
804
+ if (isNaN(sunsetDate.getTime()) || sunsetDate >= now) {
805
+ violations.push({
806
+ path: change.path,
807
+ method: change.method,
808
+ rule: "response-property-removed-before-sunset",
809
+ xSunset: change.xSunset,
810
+ statusCode: change.statusCode,
811
+ propertyPath: change.propertyPath
812
+ });
813
+ }
814
+ continue;
815
+ }
816
+ }
817
+ return violations;
818
+ }
819
+
820
+ // src/registry/compat-engine/classify.ts
821
+ import { v4 as uuid } from "uuid";
822
+ function classifyChanges(changes, graceViolations, previousVersion) {
823
+ const breakingChanges = [];
824
+ const safeChanges = [];
825
+ let hasGraceViolations = false;
826
+ const violationByKey = /* @__PURE__ */ new Map();
827
+ for (const v of graceViolations) {
828
+ let key = `${v.path}:${v.method}`;
829
+ if (v.paramName && v.paramIn) {
830
+ key = `${key}:param:${v.paramIn}:${v.paramName}`;
831
+ } else if (v.statusCode && v.propertyPath) {
832
+ key = `${key}:response:${v.statusCode}:${v.propertyPath}`;
833
+ } else if (v.propertyPath) {
834
+ key = `${key}:request-body:${v.propertyPath}`;
835
+ }
836
+ violationByKey.set(key, v);
837
+ }
838
+ for (const change of changes) {
839
+ switch (change.type) {
840
+ case "stable-endpoint-marked-draft":
841
+ breakingChanges.push({
842
+ id: uuid(),
843
+ rule: "stable-endpoint-marked-draft",
844
+ description: `${change.method.toUpperCase()} ${change.path} cannot be downgraded from stable to draft`,
845
+ path: `${change.path}/${change.method.toUpperCase()}`,
846
+ category: "structural"
847
+ });
848
+ break;
849
+ case "endpoint-removed": {
850
+ if (change.wasDraft) {
851
+ safeChanges.push({
852
+ id: uuid(),
853
+ rule: "draft-endpoint-changed",
854
+ description: `${change.method.toUpperCase()} ${change.path} was removed (endpoint was marked x-draft)`,
855
+ path: `${change.path}/${change.method.toUpperCase()}`,
856
+ category: "documentation"
857
+ });
858
+ break;
859
+ }
860
+ const violation = violationByKey.get(`${change.path}:${change.method}`);
861
+ if (violation) {
862
+ hasGraceViolations = true;
863
+ breakingChanges.push({
864
+ id: uuid(),
865
+ rule: violation.rule,
866
+ description: describeGraceViolation(change.path, change.method, violation),
867
+ path: `${change.path}/${change.method.toUpperCase()}`,
868
+ category: "structural",
869
+ originalValue: "endpoint existed",
870
+ newValue: "endpoint removed"
871
+ });
872
+ } else {
873
+ breakingChanges.push({
874
+ id: uuid(),
875
+ rule: "endpoint-removed",
876
+ description: `${change.method.toUpperCase()} ${change.path} was removed after sunset on ${change.xSunset ?? "unknown"}`,
877
+ path: `${change.path}/${change.method.toUpperCase()}`,
878
+ category: "structural",
879
+ originalValue: "endpoint existed",
880
+ newValue: "endpoint removed"
881
+ });
882
+ }
883
+ break;
884
+ }
885
+ case "endpoint-added":
886
+ safeChanges.push({
887
+ id: uuid(),
888
+ rule: "endpoint-added",
889
+ description: `${change.method.toUpperCase()} ${change.path} was added`,
890
+ path: `${change.path}/${change.method.toUpperCase()}`,
891
+ category: "structural"
892
+ });
893
+ break;
894
+ case "endpoint-deprecated":
895
+ safeChanges.push({
896
+ id: uuid(),
897
+ rule: "endpoint-deprecated",
898
+ description: `${change.method.toUpperCase()} ${change.path} was marked deprecated${change.xSunset ? ` (sunset: ${change.xSunset})` : ""}`,
899
+ path: `${change.path}/${change.method.toUpperCase()}`,
900
+ category: "structural"
901
+ });
902
+ break;
903
+ case "endpoint-un-deprecated":
904
+ safeChanges.push({
905
+ id: uuid(),
906
+ rule: "endpoint-un-deprecated",
907
+ description: `${change.method.toUpperCase()} ${change.path} was un-deprecated`,
908
+ path: `${change.path}/${change.method.toUpperCase()}`,
909
+ category: "documentation"
910
+ });
911
+ break;
912
+ case "draft-endpoint-marked-stable":
913
+ safeChanges.push({
914
+ id: uuid(),
915
+ rule: "draft-endpoint-marked-stable",
916
+ description: `${change.method.toUpperCase()} ${change.path} was promoted from draft to stable`,
917
+ path: `${change.path}/${change.method.toUpperCase()}`,
918
+ category: "documentation"
919
+ });
920
+ break;
921
+ case "required-param-added":
922
+ if (change.wasDraft) {
923
+ safeChanges.push({
924
+ id: uuid(),
925
+ rule: "draft-endpoint-changed",
926
+ description: `Required ${change.paramIn} parameter '${change.paramName}' was added to draft endpoint ${change.method.toUpperCase()} ${change.path}`,
927
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
928
+ category: "documentation"
929
+ });
930
+ break;
931
+ }
932
+ breakingChanges.push({
933
+ id: uuid(),
934
+ rule: "required-request-param-added",
935
+ description: `Required ${change.paramIn} parameter '${change.paramName}' was added to ${change.method.toUpperCase()} ${change.path}`,
936
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
937
+ category: "structural"
938
+ });
939
+ break;
940
+ case "optional-param-added":
941
+ safeChanges.push({
942
+ id: uuid(),
943
+ rule: "optional-request-param-added",
944
+ description: `Optional ${change.paramIn} parameter '${change.paramName}' was added to ${change.method.toUpperCase()} ${change.path}`,
945
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
946
+ category: "structural"
947
+ });
948
+ break;
949
+ case "param-removed": {
950
+ if (change.wasDraft) {
951
+ safeChanges.push({
952
+ id: uuid(),
953
+ rule: "draft-endpoint-changed",
954
+ description: `${change.method.toUpperCase()} ${change.path}: ${change.paramIn} parameter '${change.paramName}' was removed from draft endpoint`,
955
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
956
+ category: "documentation"
957
+ });
958
+ break;
959
+ }
960
+ const violation = violationByKey.get(`${change.path}:${change.method}:param:${change.paramIn}:${change.paramName}`);
961
+ if (violation) {
962
+ hasGraceViolations = true;
963
+ breakingChanges.push({
964
+ id: uuid(),
965
+ rule: violation.rule,
966
+ description: describeGraceViolation(change.path, change.method, violation, change.paramName, change.paramIn),
967
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
968
+ category: "structural"
969
+ });
970
+ } else {
971
+ breakingChanges.push({
972
+ id: uuid(),
973
+ rule: "param-removed",
974
+ description: `${change.method.toUpperCase()} ${change.path}: ${change.paramIn} parameter '${change.paramName}' was removed after sunset on ${change.xSunset ?? "unknown"}`,
975
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
976
+ category: "structural"
977
+ });
978
+ }
979
+ break;
980
+ }
981
+ case "param-type-changed": {
982
+ if (change.wasDraft) {
983
+ safeChanges.push({
984
+ id: uuid(),
985
+ rule: "draft-endpoint-changed",
986
+ description: `${change.paramIn} parameter '${change.paramName}' type changed from ${change.oldType} to ${change.newType} on draft endpoint ${change.method.toUpperCase()} ${change.path}`,
987
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
988
+ category: "documentation"
989
+ });
990
+ break;
991
+ }
992
+ breakingChanges.push({
993
+ id: uuid(),
994
+ rule: "param-type-changed",
995
+ description: `${change.paramIn} parameter '${change.paramName}' type changed from ${change.oldType} to ${change.newType} on ${change.method.toUpperCase()} ${change.path}`,
996
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
997
+ category: "structural"
998
+ });
999
+ break;
1000
+ }
1001
+ case "param-format-changed":
1002
+ safeChanges.push({
1003
+ id: uuid(),
1004
+ rule: "param-format-changed",
1005
+ description: `${change.paramIn} parameter '${change.paramName}' format changed from ${change.oldFormat} to ${change.newFormat} on ${change.method.toUpperCase()} ${change.path}`,
1006
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
1007
+ category: "documentation"
1008
+ });
1009
+ break;
1010
+ case "param-now-required": {
1011
+ if (change.wasDraft) {
1012
+ safeChanges.push({
1013
+ id: uuid(),
1014
+ rule: "draft-endpoint-changed",
1015
+ description: `${change.paramIn} parameter '${change.paramName}' became required on draft endpoint ${change.method.toUpperCase()} ${change.path}`,
1016
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
1017
+ category: "documentation"
1018
+ });
1019
+ break;
1020
+ }
1021
+ breakingChanges.push({
1022
+ id: uuid(),
1023
+ rule: "param-now-required",
1024
+ description: `${change.paramIn} parameter '${change.paramName}' became required on ${change.method.toUpperCase()} ${change.path}`,
1025
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
1026
+ category: "structural"
1027
+ });
1028
+ break;
1029
+ }
1030
+ case "param-now-optional":
1031
+ safeChanges.push({
1032
+ id: uuid(),
1033
+ rule: "param-now-optional",
1034
+ description: `${change.paramIn} parameter '${change.paramName}' became optional on ${change.method.toUpperCase()} ${change.path}`,
1035
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
1036
+ category: "documentation"
1037
+ });
1038
+ break;
1039
+ case "param-description-changed":
1040
+ safeChanges.push({
1041
+ id: uuid(),
1042
+ rule: "param-description-changed",
1043
+ description: `Description of ${change.paramIn} parameter '${change.paramName}' changed on ${change.method.toUpperCase()} ${change.path}`,
1044
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
1045
+ category: "documentation"
1046
+ });
1047
+ break;
1048
+ case "param-default-changed":
1049
+ safeChanges.push({
1050
+ id: uuid(),
1051
+ rule: "param-default-changed",
1052
+ description: `Default value of ${change.paramIn} parameter '${change.paramName}' changed on ${change.method.toUpperCase()} ${change.path}`,
1053
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
1054
+ category: "documentation"
1055
+ });
1056
+ break;
1057
+ case "param-deprecated":
1058
+ safeChanges.push({
1059
+ id: uuid(),
1060
+ rule: "param-deprecated",
1061
+ description: `${change.paramIn} parameter '${change.paramName}' was marked deprecated${change.xSunset ? ` (sunset: ${change.xSunset})` : ""} on ${change.method.toUpperCase()} ${change.path}`,
1062
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
1063
+ category: "structural"
1064
+ });
1065
+ break;
1066
+ case "param-un-deprecated":
1067
+ safeChanges.push({
1068
+ id: uuid(),
1069
+ rule: "param-un-deprecated",
1070
+ description: `${change.paramIn} parameter '${change.paramName}' was un-deprecated on ${change.method.toUpperCase()} ${change.path}`,
1071
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
1072
+ category: "documentation"
1073
+ });
1074
+ break;
1075
+ case "param-enum-value-added":
1076
+ safeChanges.push({
1077
+ id: uuid(),
1078
+ rule: "param-enum-value-added",
1079
+ description: `Enum value '${change.value}' added to ${change.paramIn} parameter '${change.paramName}' on ${change.method.toUpperCase()} ${change.path}`,
1080
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
1081
+ category: "structural"
1082
+ });
1083
+ break;
1084
+ case "param-enum-value-removed": {
1085
+ if (change.wasDraft) {
1086
+ safeChanges.push({
1087
+ id: uuid(),
1088
+ rule: "draft-endpoint-changed",
1089
+ description: `Enum value '${change.value}' removed from ${change.paramIn} parameter '${change.paramName}' on draft endpoint ${change.method.toUpperCase()} ${change.path}`,
1090
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
1091
+ category: "documentation"
1092
+ });
1093
+ break;
1094
+ }
1095
+ breakingChanges.push({
1096
+ id: uuid(),
1097
+ rule: "param-enum-value-removed",
1098
+ description: `Enum value '${change.value}' removed from ${change.paramIn} parameter '${change.paramName}' on ${change.method.toUpperCase()} ${change.path}`,
1099
+ path: `${change.path}/${change.method.toUpperCase()}/parameters/${change.paramName}`,
1100
+ category: "structural"
1101
+ });
1102
+ break;
1103
+ }
1104
+ case "request-body-added":
1105
+ breakingChanges.push({
1106
+ id: uuid(),
1107
+ rule: "request-body-added",
1108
+ description: `Request body added to ${change.method.toUpperCase()} ${change.path}`,
1109
+ path: `${change.path}/${change.method.toUpperCase()}/requestBody`,
1110
+ category: "structural"
1111
+ });
1112
+ break;
1113
+ case "request-body-removed": {
1114
+ if (change.wasDraft) {
1115
+ safeChanges.push({
1116
+ id: uuid(),
1117
+ rule: "draft-endpoint-changed",
1118
+ description: `Request body removed from draft endpoint ${change.method.toUpperCase()} ${change.path}`,
1119
+ path: `${change.path}/${change.method.toUpperCase()}/requestBody`,
1120
+ category: "documentation"
1121
+ });
1122
+ break;
1123
+ }
1124
+ safeChanges.push({
1125
+ id: uuid(),
1126
+ rule: "request-body-removed",
1127
+ description: `Request body removed from ${change.method.toUpperCase()} ${change.path}`,
1128
+ path: `${change.path}/${change.method.toUpperCase()}/requestBody`,
1129
+ category: "documentation"
1130
+ });
1131
+ break;
1132
+ }
1133
+ case "request-body-property-added":
1134
+ if (change.required) {
1135
+ if (change.wasDraft) {
1136
+ safeChanges.push({
1137
+ id: uuid(),
1138
+ rule: "draft-endpoint-changed",
1139
+ description: `Required request body property '${change.propertyPath}' added to draft endpoint ${change.method.toUpperCase()} ${change.path}`,
1140
+ path: `${change.path}/${change.method.toUpperCase()}/requestBody/${change.propertyPath}`,
1141
+ category: "documentation"
1142
+ });
1143
+ } else {
1144
+ breakingChanges.push({
1145
+ id: uuid(),
1146
+ rule: "request-body-property-added",
1147
+ description: `Required request body property '${change.propertyPath}' added to ${change.method.toUpperCase()} ${change.path}`,
1148
+ path: `${change.path}/${change.method.toUpperCase()}/requestBody/${change.propertyPath}`,
1149
+ category: "structural"
1150
+ });
1151
+ }
1152
+ } else {
1153
+ safeChanges.push({
1154
+ id: uuid(),
1155
+ rule: "request-body-property-added",
1156
+ description: `Optional request body property '${change.propertyPath}' added to ${change.method.toUpperCase()} ${change.path}`,
1157
+ path: `${change.path}/${change.method.toUpperCase()}/requestBody/${change.propertyPath}`,
1158
+ category: "documentation"
1159
+ });
1160
+ }
1161
+ break;
1162
+ case "request-body-property-removed": {
1163
+ if (change.wasDraft) {
1164
+ safeChanges.push({
1165
+ id: uuid(),
1166
+ rule: "draft-endpoint-changed",
1167
+ description: `${change.method.toUpperCase()} ${change.path}: request body property '${change.propertyPath}' was removed from draft endpoint`,
1168
+ path: `${change.path}/${change.method.toUpperCase()}/requestBody/${change.propertyPath}`,
1169
+ category: "documentation"
1170
+ });
1171
+ break;
1172
+ }
1173
+ const violation = violationByKey.get(`${change.path}:${change.method}:request-body:${change.propertyPath}`);
1174
+ if (violation) {
1175
+ hasGraceViolations = true;
1176
+ breakingChanges.push({
1177
+ id: uuid(),
1178
+ rule: violation.rule,
1179
+ description: describeGraceViolation(change.path, change.method, violation, void 0, void 0, change.propertyPath),
1180
+ path: `${change.path}/${change.method.toUpperCase()}/requestBody/${change.propertyPath}`,
1181
+ category: "structural",
1182
+ originalValue: "property existed",
1183
+ newValue: "property removed"
1184
+ });
1185
+ } else {
1186
+ breakingChanges.push({
1187
+ id: uuid(),
1188
+ rule: "request-body-property-removed",
1189
+ description: `${change.method.toUpperCase()} ${change.path}: request body property '${change.propertyPath}' was removed after sunset on ${change.xSunset ?? "unknown"}`,
1190
+ path: `${change.path}/${change.method.toUpperCase()}/requestBody/${change.propertyPath}`,
1191
+ category: "structural"
1192
+ });
1193
+ }
1194
+ break;
1195
+ }
1196
+ case "request-body-property-type-changed": {
1197
+ if (change.wasDraft) {
1198
+ safeChanges.push({
1199
+ id: uuid(),
1200
+ rule: "draft-endpoint-changed",
1201
+ description: `Request body property '${change.propertyPath}' type changed from ${change.oldType} to ${change.newType} on draft endpoint ${change.method.toUpperCase()} ${change.path}`,
1202
+ path: `${change.path}/${change.method.toUpperCase()}/requestBody/${change.propertyPath}`,
1203
+ category: "documentation"
1204
+ });
1205
+ break;
1206
+ }
1207
+ breakingChanges.push({
1208
+ id: uuid(),
1209
+ rule: "request-body-property-type-changed",
1210
+ description: `Request body property '${change.propertyPath}' type changed from ${change.oldType} to ${change.newType} on ${change.method.toUpperCase()} ${change.path}`,
1211
+ path: `${change.path}/${change.method.toUpperCase()}/requestBody/${change.propertyPath}`,
1212
+ category: "structural"
1213
+ });
1214
+ break;
1215
+ }
1216
+ case "request-body-property-now-required": {
1217
+ if (change.wasDraft) {
1218
+ safeChanges.push({
1219
+ id: uuid(),
1220
+ rule: "draft-endpoint-changed",
1221
+ description: `Request body property '${change.propertyPath}' became required on draft endpoint ${change.method.toUpperCase()} ${change.path}`,
1222
+ path: `${change.path}/${change.method.toUpperCase()}/requestBody/${change.propertyPath}`,
1223
+ category: "documentation"
1224
+ });
1225
+ break;
1226
+ }
1227
+ breakingChanges.push({
1228
+ id: uuid(),
1229
+ rule: "request-body-property-now-required",
1230
+ description: `Request body property '${change.propertyPath}' became required on ${change.method.toUpperCase()} ${change.path}`,
1231
+ path: `${change.path}/${change.method.toUpperCase()}/requestBody/${change.propertyPath}`,
1232
+ category: "structural"
1233
+ });
1234
+ break;
1235
+ }
1236
+ case "request-body-property-now-optional":
1237
+ safeChanges.push({
1238
+ id: uuid(),
1239
+ rule: "request-body-property-now-optional",
1240
+ description: `Request body property '${change.propertyPath}' became optional on ${change.method.toUpperCase()} ${change.path}`,
1241
+ path: `${change.path}/${change.method.toUpperCase()}/requestBody/${change.propertyPath}`,
1242
+ category: "documentation"
1243
+ });
1244
+ break;
1245
+ case "request-body-property-deprecated":
1246
+ safeChanges.push({
1247
+ id: uuid(),
1248
+ rule: "request-body-property-deprecated",
1249
+ description: `Request body property '${change.propertyPath}' was marked deprecated${change.xSunset ? ` (sunset: ${change.xSunset})` : ""} on ${change.method.toUpperCase()} ${change.path}`,
1250
+ path: `${change.path}/${change.method.toUpperCase()}/requestBody/${change.propertyPath}`,
1251
+ category: "structural"
1252
+ });
1253
+ break;
1254
+ case "request-body-property-un-deprecated":
1255
+ safeChanges.push({
1256
+ id: uuid(),
1257
+ rule: "request-body-property-un-deprecated",
1258
+ description: `Request body property '${change.propertyPath}' was un-deprecated on ${change.method.toUpperCase()} ${change.path}`,
1259
+ path: `${change.path}/${change.method.toUpperCase()}/requestBody/${change.propertyPath}`,
1260
+ category: "documentation"
1261
+ });
1262
+ break;
1263
+ case "request-body-media-type-changed":
1264
+ breakingChanges.push({
1265
+ id: uuid(),
1266
+ rule: "request-body-media-type-changed",
1267
+ description: `Request body media type changed from ${change.oldMediaType} to ${change.newMediaType} on ${change.method.toUpperCase()} ${change.path}`,
1268
+ path: `${change.path}/${change.method.toUpperCase()}/requestBody`,
1269
+ category: "structural"
1270
+ });
1271
+ break;
1272
+ case "request-body-description-changed":
1273
+ safeChanges.push({
1274
+ id: uuid(),
1275
+ rule: "request-body-description-changed",
1276
+ description: `Request body description changed on ${change.method.toUpperCase()} ${change.path}`,
1277
+ path: `${change.path}/${change.method.toUpperCase()}/requestBody`,
1278
+ category: "documentation"
1279
+ });
1280
+ break;
1281
+ case "response-property-removed": {
1282
+ if (change.wasDraft) {
1283
+ safeChanges.push({
1284
+ id: uuid(),
1285
+ rule: "draft-endpoint-changed",
1286
+ description: `${change.method.toUpperCase()} ${change.path}: response property '${change.propertyPath}' was removed from ${change.statusCode} response in draft endpoint`,
1287
+ path: `${change.path}/${change.method.toUpperCase()}/response/${change.statusCode}/${change.propertyPath}`,
1288
+ category: "documentation"
1289
+ });
1290
+ break;
1291
+ }
1292
+ const violation = violationByKey.get(`${change.path}:${change.method}:response:${change.statusCode}:${change.propertyPath}`);
1293
+ if (violation) {
1294
+ hasGraceViolations = true;
1295
+ breakingChanges.push({
1296
+ id: uuid(),
1297
+ rule: violation.rule,
1298
+ description: describeGraceViolation(change.path, change.method, violation, void 0, void 0, change.propertyPath, change.statusCode),
1299
+ path: `${change.path}/${change.method.toUpperCase()}/response/${change.statusCode}/${change.propertyPath}`,
1300
+ category: "structural",
1301
+ originalValue: change.originalValue,
1302
+ newValue: void 0
1303
+ });
1304
+ } else {
1305
+ breakingChanges.push({
1306
+ id: uuid(),
1307
+ rule: "response-property-removed",
1308
+ description: `${change.method.toUpperCase()} ${change.path}: response property '${change.propertyPath}' was removed from ${change.statusCode} response after sunset on ${change.xSunset ?? "unknown"}`,
1309
+ path: `${change.path}/${change.method.toUpperCase()}/response/${change.statusCode}/${change.propertyPath}`,
1310
+ category: "structural",
1311
+ originalValue: change.originalValue,
1312
+ newValue: void 0
1313
+ });
1314
+ }
1315
+ break;
1316
+ }
1317
+ case "response-property-added":
1318
+ safeChanges.push({
1319
+ id: uuid(),
1320
+ rule: "response-property-added",
1321
+ description: `Response property '${change.propertyPath}' added to ${change.method.toUpperCase()} ${change.path} (${change.statusCode})`,
1322
+ path: `${change.path}/${change.method.toUpperCase()}/response/${change.statusCode}/${change.propertyPath}`,
1323
+ category: "documentation"
1324
+ });
1325
+ break;
1326
+ case "response-property-type-changed": {
1327
+ if (change.wasDraft) {
1328
+ safeChanges.push({
1329
+ id: uuid(),
1330
+ rule: "draft-endpoint-changed",
1331
+ description: `Response property '${change.propertyPath}' type changed from ${change.oldType} to ${change.newType} on draft endpoint ${change.method.toUpperCase()} ${change.path} (${change.statusCode})`,
1332
+ path: `${change.path}/${change.method.toUpperCase()}/response/${change.statusCode}/${change.propertyPath}`,
1333
+ category: "documentation"
1334
+ });
1335
+ break;
1336
+ }
1337
+ breakingChanges.push({
1338
+ id: uuid(),
1339
+ rule: "response-property-type-changed",
1340
+ description: `Response property '${change.propertyPath}' type changed from ${change.oldType} to ${change.newType} on ${change.method.toUpperCase()} ${change.path} (${change.statusCode})`,
1341
+ path: `${change.path}/${change.method.toUpperCase()}/response/${change.statusCode}/${change.propertyPath}`,
1342
+ category: "structural"
1343
+ });
1344
+ break;
1345
+ }
1346
+ case "response-property-now-required": {
1347
+ if (change.wasDraft) {
1348
+ safeChanges.push({
1349
+ id: uuid(),
1350
+ rule: "draft-endpoint-changed",
1351
+ description: `Response property '${change.propertyPath}' became required on draft endpoint ${change.method.toUpperCase()} ${change.path} (${change.statusCode})`,
1352
+ path: `${change.path}/${change.method.toUpperCase()}/response/${change.statusCode}/${change.propertyPath}`,
1353
+ category: "documentation"
1354
+ });
1355
+ break;
1356
+ }
1357
+ breakingChanges.push({
1358
+ id: uuid(),
1359
+ rule: "response-property-now-required",
1360
+ description: `Response property '${change.propertyPath}' became required on ${change.method.toUpperCase()} ${change.path} (${change.statusCode})`,
1361
+ path: `${change.path}/${change.method.toUpperCase()}/response/${change.statusCode}/${change.propertyPath}`,
1362
+ category: "structural"
1363
+ });
1364
+ break;
1365
+ }
1366
+ case "response-property-now-optional":
1367
+ safeChanges.push({
1368
+ id: uuid(),
1369
+ rule: "response-property-now-optional",
1370
+ description: `Response property '${change.propertyPath}' became optional on ${change.method.toUpperCase()} ${change.path} (${change.statusCode})`,
1371
+ path: `${change.path}/${change.method.toUpperCase()}/response/${change.statusCode}/${change.propertyPath}`,
1372
+ category: "documentation"
1373
+ });
1374
+ break;
1375
+ case "response-property-deprecated":
1376
+ safeChanges.push({
1377
+ id: uuid(),
1378
+ rule: "response-property-deprecated",
1379
+ description: `Response property '${change.propertyPath}' was marked deprecated${change.xSunset ? ` (sunset: ${change.xSunset})` : ""} on ${change.method.toUpperCase()} ${change.path} (${change.statusCode})`,
1380
+ path: `${change.path}/${change.method.toUpperCase()}/response/${change.statusCode}/${change.propertyPath}`,
1381
+ category: "structural"
1382
+ });
1383
+ break;
1384
+ case "response-property-un-deprecated":
1385
+ safeChanges.push({
1386
+ id: uuid(),
1387
+ rule: "response-property-un-deprecated",
1388
+ description: `Response property '${change.propertyPath}' was un-deprecated on ${change.method.toUpperCase()} ${change.path} (${change.statusCode})`,
1389
+ path: `${change.path}/${change.method.toUpperCase()}/response/${change.statusCode}/${change.propertyPath}`,
1390
+ category: "documentation"
1391
+ });
1392
+ break;
1393
+ case "response-status-removed": {
1394
+ if (change.wasDraft) {
1395
+ safeChanges.push({
1396
+ id: uuid(),
1397
+ rule: "draft-endpoint-changed",
1398
+ description: `Response status ${change.statusCode} was removed from draft endpoint ${change.method.toUpperCase()} ${change.path}`,
1399
+ path: `${change.path}/${change.method.toUpperCase()}/responses/${change.statusCode}`,
1400
+ category: "documentation"
1401
+ });
1402
+ break;
1403
+ }
1404
+ breakingChanges.push({
1405
+ id: uuid(),
1406
+ rule: "response-status-removed",
1407
+ description: `Response status ${change.statusCode} was removed from ${change.method.toUpperCase()} ${change.path}`,
1408
+ path: `${change.path}/${change.method.toUpperCase()}/responses/${change.statusCode}`,
1409
+ category: "structural"
1410
+ });
1411
+ break;
1412
+ }
1413
+ case "response-status-added":
1414
+ safeChanges.push({
1415
+ id: uuid(),
1416
+ rule: "response-status-added",
1417
+ description: `Response status ${change.statusCode} was added to ${change.method.toUpperCase()} ${change.path}`,
1418
+ path: `${change.path}/${change.method.toUpperCase()}/responses/${change.statusCode}`,
1419
+ category: "structural"
1420
+ });
1421
+ break;
1422
+ case "response-media-type-changed":
1423
+ breakingChanges.push({
1424
+ id: uuid(),
1425
+ rule: "response-media-type-changed",
1426
+ description: `Response media type changed from ${change.oldMediaType} to ${change.newMediaType} on ${change.method.toUpperCase()} ${change.path} (${change.statusCode})`,
1427
+ path: `${change.path}/${change.method.toUpperCase()}/responses/${change.statusCode}`,
1428
+ category: "structural"
1429
+ });
1430
+ break;
1431
+ case "response-description-changed":
1432
+ safeChanges.push({
1433
+ id: uuid(),
1434
+ rule: "response-description-changed",
1435
+ description: `Response description changed for ${change.statusCode} on ${change.method.toUpperCase()} ${change.path}`,
1436
+ path: `${change.path}/${change.method.toUpperCase()}/responses/${change.statusCode}`,
1437
+ category: "documentation"
1438
+ });
1439
+ break;
1440
+ case "global-security-added":
1441
+ breakingChanges.push({
1442
+ id: uuid(),
1443
+ rule: "global-security-added",
1444
+ description: "Global security requirement was added",
1445
+ path: "/security",
1446
+ category: "structural"
1447
+ });
1448
+ break;
1449
+ case "global-security-removed":
1450
+ safeChanges.push({
1451
+ id: uuid(),
1452
+ rule: "global-security-removed",
1453
+ description: "Global security requirement was removed",
1454
+ path: "/security",
1455
+ category: "documentation"
1456
+ });
1457
+ break;
1458
+ case "documentation-updated":
1459
+ safeChanges.push({
1460
+ id: uuid(),
1461
+ rule: change.kind,
1462
+ description: `${change.kind.replace(/-/g, " ")}${change.detail ? ` (${change.detail})` : ""}`,
1463
+ path: change.method ? `${change.path}/${change.method.toUpperCase()}` : change.path,
1464
+ category: "documentation"
1465
+ });
1466
+ break;
1467
+ }
1468
+ }
1469
+ const hasStructuralSafe = safeChanges.some((c) => c.category === "structural");
1470
+ const classification = breakingChanges.length > 0 ? "major" : hasStructuralSafe ? "minor" : safeChanges.length > 0 ? "patch" : "patch";
1471
+ return {
1472
+ compatReport: {
1473
+ previousVersion,
1474
+ classification,
1475
+ breakingChanges,
1476
+ safeChanges
1477
+ },
1478
+ hasGraceViolations
1479
+ };
1480
+ }
1481
+ function describeGraceViolation(path2, method, violation, paramName, paramIn, propertyPath, statusCode) {
1482
+ const endpoint = `${method.toUpperCase()} ${path2}`;
1483
+ if (paramName && paramIn) {
1484
+ switch (violation.rule) {
1485
+ case "param-removed-without-deprecation":
1486
+ return `${endpoint}: ${paramIn} parameter '${paramName}' was removed without first being marked as deprecated`;
1487
+ case "param-removed-missing-sunset":
1488
+ return `${endpoint}: ${paramIn} parameter '${paramName}' is deprecated but has no x-sunset date set`;
1489
+ case "param-removed-before-sunset":
1490
+ return `${endpoint}: ${paramIn} parameter '${paramName}' was removed before its sunset date (${violation.xSunset ?? "unknown"})`;
1491
+ }
1492
+ }
1493
+ if (propertyPath && statusCode) {
1494
+ switch (violation.rule) {
1495
+ case "response-property-removed-without-deprecation":
1496
+ return `${endpoint}: response property '${propertyPath}' was removed from ${statusCode} response without first being marked as deprecated`;
1497
+ case "response-property-removed-missing-sunset":
1498
+ return `${endpoint}: response property '${propertyPath}' in ${statusCode} response is deprecated but has no x-sunset date set`;
1499
+ case "response-property-removed-before-sunset":
1500
+ return `${endpoint}: response property '${propertyPath}' was removed from ${statusCode} response before its sunset date (${violation.xSunset ?? "unknown"})`;
1501
+ }
1502
+ }
1503
+ if (propertyPath) {
1504
+ switch (violation.rule) {
1505
+ case "request-body-property-removed-without-deprecation":
1506
+ return `${endpoint}: request body property '${propertyPath}' was removed without first being marked as deprecated`;
1507
+ case "request-body-property-removed-missing-sunset":
1508
+ return `${endpoint}: request body property '${propertyPath}' is deprecated but has no x-sunset date set`;
1509
+ case "request-body-property-removed-before-sunset":
1510
+ return `${endpoint}: request body property '${propertyPath}' was removed before its sunset date (${violation.xSunset ?? "unknown"})`;
1511
+ }
1512
+ }
1513
+ switch (violation.rule) {
1514
+ case "endpoint-removed-without-deprecation":
1515
+ return `${endpoint} was removed without first being marked as deprecated`;
1516
+ case "endpoint-removed-missing-sunset":
1517
+ return `${endpoint} is deprecated but has no x-sunset date set`;
1518
+ case "endpoint-removed-before-sunset":
1519
+ return `${endpoint} was removed before its sunset date (${violation.xSunset ?? "unknown"})`;
1520
+ default:
1521
+ return `${endpoint} grace period violation: ${violation.rule}`;
1522
+ }
1523
+ }
1524
+
1525
+ // src/registry/services/registry.ts
1526
+ var BreakingChangeError = class extends Error {
1527
+ constructor(compatReport) {
1528
+ super("Breaking changes detected");
1529
+ this.compatReport = compatReport;
1530
+ this.name = "BreakingChangeError";
1531
+ }
1532
+ compatReport;
1533
+ };
1534
+ var PrereleaseConstraintError = class extends Error {
1535
+ constructor(message) {
1536
+ super(message);
1537
+ this.name = "PrereleaseConstraintError";
1538
+ }
1539
+ };
1540
+ var RegistryService = class {
1541
+ constructor(store) {
1542
+ this.store = store;
1543
+ }
1544
+ store;
1545
+ async pushSpec(content, name, options) {
1546
+ const prerelease = options?.prerelease ?? false;
1547
+ const existingSpec = await this.store.getSpec(name);
1548
+ const isNewSpec = !existingSpec;
1549
+ const isOpenApi = (options?.type ?? "openapi") === "openapi";
1550
+ const normalized = isOpenApi ? normalizeSpec(content) : content;
1551
+ const checksum = computeChecksum(normalized);
1552
+ let spec;
1553
+ let compatReport;
1554
+ let semver;
1555
+ let previousVersion;
1556
+ if (isNewSpec) {
1557
+ spec = {
1558
+ id: uuid2(),
1559
+ name,
1560
+ type: options?.type ?? "openapi",
1561
+ description: options?.description,
1562
+ owner: options?.owner,
1563
+ sourceRepo: options?.sourceRepo,
1564
+ tags: options?.tags ?? [],
1565
+ createdAt: /* @__PURE__ */ new Date(),
1566
+ updatedAt: /* @__PURE__ */ new Date()
1567
+ };
1568
+ semver = prerelease ? "0.1.0" : "1.0.0";
1569
+ } else {
1570
+ spec = existingSpec;
1571
+ const latestVersion = await this.store.getLatestVersion(name);
1572
+ if (prerelease && latestVersion && !latestVersion.isPrerelease) {
1573
+ throw new PrereleaseConstraintError(
1574
+ `Cannot push pre-release version. ${name}@${latestVersion.semver} is already a release version.`
1575
+ );
1576
+ }
1577
+ if (!latestVersion) {
1578
+ semver = prerelease ? "0.1.0" : "1.0.0";
1579
+ } else {
1580
+ previousVersion = latestVersion.semver;
1581
+ const oldSpec = parseOpenApiSpec(latestVersion.content);
1582
+ const newSpec = parseOpenApiSpec(normalized);
1583
+ const changes = diffSpecs(oldSpec, newSpec);
1584
+ const graceViolations = checkGracePeriod(changes);
1585
+ const { compatReport: report } = classifyChanges(
1586
+ changes,
1587
+ graceViolations,
1588
+ previousVersion
1589
+ );
1590
+ const allowedRules = /* @__PURE__ */ new Set([
1591
+ "endpoint-removed",
1592
+ "param-removed",
1593
+ "request-body-property-removed",
1594
+ "response-property-removed"
1595
+ ]);
1596
+ const blockedBreakingChanges = report.breakingChanges.filter(
1597
+ (b) => !allowedRules.has(b.rule)
1598
+ );
1599
+ if (!options?.force && blockedBreakingChanges.length > 0) {
1600
+ throw new BreakingChangeError(report);
1601
+ }
1602
+ if (prerelease) {
1603
+ const level = report.classification === "patch" ? "patch" : "minor";
1604
+ semver = this.bumpPreRelease(latestVersion.semver, level);
1605
+ } else if (latestVersion.isPrerelease) {
1606
+ semver = "1.0.0";
1607
+ } else {
1608
+ semver = this.bumpRelease(latestVersion.semver, report.classification);
1609
+ }
1610
+ compatReport = { ...report, suggestedVersion: semver };
1611
+ }
1612
+ }
1613
+ const version = {
1614
+ id: uuid2(),
1615
+ specId: spec.id,
1616
+ semver,
1617
+ content: normalized,
1618
+ checksum,
1619
+ gitRef: options?.gitRef,
1620
+ pushedBy: options?.pushedBy,
1621
+ compatibility: compatReport ?? (isNewSpec ? { previousVersion: "0.0.0", classification: "initial", breakingChanges: [], safeChanges: [] } : void 0),
1622
+ previousVersion,
1623
+ forceReason: options?.force ? options.reason : void 0,
1624
+ isPrerelease: prerelease,
1625
+ createdAt: /* @__PURE__ */ new Date()
1626
+ };
1627
+ await this.store.pushSpecVersion(spec, version);
1628
+ const auditAction = options?.force ? "spec.push.force" : "spec.push";
1629
+ await this.store.logAudit(auditAction, options?.pushedBy ?? "unknown", name, semver, {
1630
+ breakingChanges: compatReport?.breakingChanges.length ?? 0,
1631
+ safeChanges: compatReport?.safeChanges.length ?? 0,
1632
+ forced: options?.force ?? false,
1633
+ reason: options?.reason
1634
+ });
1635
+ return { spec, version, compatReport, isNewSpec };
1636
+ }
1637
+ async listSpecs(filters) {
1638
+ const specs2 = await this.store.listSpecs(filters);
1639
+ return Promise.all(
1640
+ specs2.map(async (spec) => {
1641
+ const latestVersion = await this.store.getLatestVersion(spec.name);
1642
+ return { ...spec, latestVersion: latestVersion ?? void 0 };
1643
+ })
1644
+ );
1645
+ }
1646
+ async getSpec(name) {
1647
+ const spec = await this.store.getSpec(name);
1648
+ if (!spec) return null;
1649
+ const latestVersion = await this.store.getLatestVersion(name);
1650
+ return { spec, latestVersion: latestVersion ?? void 0 };
1651
+ }
1652
+ async listVersions(name, options) {
1653
+ return this.store.listVersions(name, options);
1654
+ }
1655
+ async getVersion(name, semver) {
1656
+ return this.store.getSpecVersion(name, semver);
1657
+ }
1658
+ async getCompatReport(name, semver) {
1659
+ return this.store.getCompatReport(name, semver);
1660
+ }
1661
+ async compareVersions(name, from, to) {
1662
+ const spec = await this.store.getSpec(name);
1663
+ if (!spec) return null;
1664
+ const all = await this.store.listVersions(name, { limit: 1e4, offset: 0 });
1665
+ const fromVersion = all.versions.find((v) => v.semver === from);
1666
+ const toVersion = all.versions.find((v) => v.semver === to);
1667
+ if (!fromVersion || !toVersion) return null;
1668
+ const sorted = [...all.versions].sort((a, b) => {
1669
+ const aParts = a.semver.split(".").map(Number);
1670
+ const bParts = b.semver.split(".").map(Number);
1671
+ for (let i = 0; i < 3; i++) {
1672
+ if (aParts[i] !== bParts[i]) return aParts[i] - bParts[i];
1673
+ }
1674
+ return 0;
1675
+ });
1676
+ const fromIndex = sorted.findIndex((v) => v.semver === from);
1677
+ const toIndex = sorted.findIndex((v) => v.semver === to);
1678
+ const start = Math.min(fromIndex, toIndex);
1679
+ const end = Math.max(fromIndex, toIndex);
1680
+ const steps = sorted.slice(start + 1, end + 1).map((v) => {
1681
+ const report = v.compatibility;
1682
+ if (!report) {
1683
+ return {
1684
+ version: v.semver,
1685
+ previousVersion: v.previousVersion ?? "0.0.0",
1686
+ classification: "initial",
1687
+ breakingChanges: [],
1688
+ safeChanges: []
1689
+ };
1690
+ }
1691
+ return {
1692
+ version: v.semver,
1693
+ previousVersion: report.previousVersion,
1694
+ classification: report.classification,
1695
+ breakingChanges: report.breakingChanges,
1696
+ safeChanges: report.safeChanges
1697
+ };
1698
+ });
1699
+ return { from: sorted[start].semver, to: sorted[end].semver, steps };
1700
+ }
1701
+ async deleteSpec(name, actor) {
1702
+ const deleted = await this.store.deleteSpec(name);
1703
+ if (deleted) {
1704
+ await this.store.logAudit("spec.delete", actor ?? "unknown", name, void 0, {});
1705
+ }
1706
+ return deleted;
1707
+ }
1708
+ bumpRelease(current, classification) {
1709
+ const parts = current.split(".").map(Number);
1710
+ if (parts.length !== 3) return "1.0.0";
1711
+ const [major, minor, patch] = parts;
1712
+ switch (classification) {
1713
+ case "major":
1714
+ return `${major + 1}.0.0`;
1715
+ case "minor":
1716
+ return `${major}.${minor + 1}.0`;
1717
+ case "patch":
1718
+ return `${major}.${minor}.${patch + 1}`;
1719
+ default:
1720
+ return `${major}.${minor + 1}.0`;
1721
+ }
1722
+ }
1723
+ bumpPreRelease(current, level) {
1724
+ const parts = current.split(".").map(Number);
1725
+ if (parts.length !== 3) return "0.1.0";
1726
+ const [major, minor, patch] = parts;
1727
+ switch (level) {
1728
+ case "minor":
1729
+ return `${major}.${minor + 1}.0`;
1730
+ case "patch":
1731
+ return `${major}.${minor}.${patch + 1}`;
1732
+ }
1733
+ }
1734
+ };
1735
+
1736
+ // src/registry/routes/push.ts
1737
+ var pushRoute = new Hono().post("/", async (c) => {
1738
+ let body;
1739
+ try {
1740
+ body = await c.req.json();
1741
+ } catch {
1742
+ return c.json({ error: "bad_request", message: "Request body must be valid JSON", statusCode: 400 }, 400);
1743
+ }
1744
+ if (!body.content || typeof body.content !== "string") {
1745
+ return c.json({ error: "bad_request", message: "Missing required field: content", statusCode: 400 }, 400);
1746
+ }
1747
+ if (!body.name || typeof body.name !== "string") {
1748
+ return c.json({ error: "bad_request", message: "Missing required field: name", statusCode: 400 }, 400);
1749
+ }
1750
+ if (body.force && !body.reason) {
1751
+ return c.json({
1752
+ error: "bad_request",
1753
+ message: "reason is required when force is true",
1754
+ statusCode: 400
1755
+ }, 400);
1756
+ }
1757
+ const store = c.get("store");
1758
+ const service = new RegistryService(store);
1759
+ try {
1760
+ const result = await service.pushSpec(body.content, body.name, {
1761
+ type: body.type,
1762
+ description: body.description,
1763
+ owner: body.owner,
1764
+ sourceRepo: body.sourceRepo,
1765
+ tags: Array.isArray(body.tags) ? body.tags : void 0,
1766
+ gitRef: body.gitRef,
1767
+ pushedBy: body.pushedBy,
1768
+ prerelease: body.prerelease,
1769
+ force: body.force,
1770
+ reason: body.reason
1771
+ });
1772
+ return c.json({ data: result }, 201);
1773
+ } catch (err) {
1774
+ if (err instanceof BreakingChangeError) {
1775
+ return c.json({
1776
+ error: "breaking_change",
1777
+ message: "Breaking changes detected. Use force: true with a reason to override.",
1778
+ statusCode: 409,
1779
+ compatReport: err.compatReport
1780
+ }, 409);
1781
+ }
1782
+ if (err instanceof PrereleaseConstraintError) {
1783
+ return c.json({
1784
+ error: "prerelease_constraint",
1785
+ message: err.message,
1786
+ statusCode: 422
1787
+ }, 422);
1788
+ }
1789
+ console.error("Push spec error:", err);
1790
+ return c.json({
1791
+ error: "internal_error",
1792
+ message: err instanceof Error ? err.message : "An unexpected error occurred",
1793
+ statusCode: 500
1794
+ }, 500);
1795
+ }
1796
+ });
1797
+
1798
+ // src/registry/routes/validate.ts
1799
+ import { Hono as Hono2 } from "hono";
1800
+
1801
+ // src/registry/parser/openapi/validate.ts
1802
+ import yaml3 from "js-yaml";
1803
+ import SwaggerParser from "@apidevtools/swagger-parser";
1804
+ async function validateOpenApiSpec(content) {
1805
+ let obj;
1806
+ try {
1807
+ obj = yaml3.load(content);
1808
+ } catch (err) {
1809
+ return {
1810
+ valid: false,
1811
+ errors: [`YAML/JSON parse error: ${err instanceof Error ? err.message : String(err)}`],
1812
+ warnings: []
1813
+ };
1814
+ }
1815
+ if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
1816
+ return { valid: false, errors: ["Spec content is not a valid YAML/JSON object"], warnings: [] };
1817
+ }
1818
+ try {
1819
+ await SwaggerParser.validate(obj);
1820
+ return { valid: true, errors: [], warnings: [] };
1821
+ } catch (err) {
1822
+ return {
1823
+ valid: false,
1824
+ errors: [err instanceof Error ? err.message : String(err)],
1825
+ warnings: []
1826
+ };
1827
+ }
1828
+ }
1829
+
1830
+ // src/registry/routes/validate.ts
1831
+ var validateRoute = new Hono2().post("/:name/validate", async (c) => {
1832
+ const name = c.req.param("name");
1833
+ let body;
1834
+ try {
1835
+ body = await c.req.json();
1836
+ } catch {
1837
+ return c.json({ error: "bad_request", message: "Request body must be valid JSON", statusCode: 400 }, 400);
1838
+ }
1839
+ if (!body.content || typeof body.content !== "string") {
1840
+ return c.json({ error: "bad_request", message: "Missing required field: content", statusCode: 400 }, 400);
1841
+ }
1842
+ const { valid, errors, warnings } = await validateOpenApiSpec(body.content);
1843
+ if (!valid) {
1844
+ return c.json({ data: { valid, errors, warnings } });
1845
+ }
1846
+ const store = c.get("store");
1847
+ const service = new RegistryService(store);
1848
+ const result = await service.getSpec(name);
1849
+ if (!result) {
1850
+ return c.json({ error: "not_found", message: `Spec "${name}" not found`, statusCode: 404 }, 404);
1851
+ }
1852
+ if (!result.latestVersion) {
1853
+ return c.json({ data: { valid: true, errors: [], warnings: [] } });
1854
+ }
1855
+ try {
1856
+ const oldSpec = parseOpenApiSpec(result.latestVersion.content);
1857
+ const newSpec = parseOpenApiSpec(body.content);
1858
+ const changes = diffSpecs(oldSpec, newSpec);
1859
+ const graceViolations = checkGracePeriod(changes);
1860
+ const { compatReport } = classifyChanges(changes, graceViolations, result.latestVersion.semver);
1861
+ const allowedRules = /* @__PURE__ */ new Set([
1862
+ "endpoint-removed",
1863
+ "param-removed",
1864
+ "request-body-property-removed",
1865
+ "response-property-removed"
1866
+ ]);
1867
+ const blockedChanges = compatReport.breakingChanges.filter(
1868
+ (b) => !allowedRules.has(b.rule)
1869
+ );
1870
+ const hasBlocked = blockedChanges.length > 0;
1871
+ return c.json({
1872
+ data: {
1873
+ compatReport,
1874
+ errors: hasBlocked ? blockedChanges.map((b) => b.description) : [],
1875
+ valid: !hasBlocked,
1876
+ warnings
1877
+ }
1878
+ });
1879
+ } catch (err) {
1880
+ return c.json({
1881
+ data: {
1882
+ errors: [err instanceof Error ? err.message : "Compat analysis failed"],
1883
+ valid: false,
1884
+ warnings
1885
+ }
1886
+ });
1887
+ }
1888
+ });
1889
+
1890
+ // src/registry/routes/list.ts
1891
+ import { Hono as Hono3 } from "hono";
1892
+ var listRoute = new Hono3().get("/", async (c) => {
1893
+ const store = c.get("store");
1894
+ const service = new RegistryService(store);
1895
+ const type = c.req.query("type");
1896
+ const owner = c.req.query("owner");
1897
+ const tags = c.req.query("tags")?.split(",");
1898
+ const specs2 = await service.listSpecs({ type, owner, tags });
1899
+ return c.json({ data: specs2 });
1900
+ });
1901
+
1902
+ // src/registry/routes/get-spec.ts
1903
+ import { Hono as Hono4 } from "hono";
1904
+ function withoutContent({ content: _, ...rest }) {
1905
+ return rest;
1906
+ }
1907
+ var getSpecRoute = new Hono4().get("/:name", async (c) => {
1908
+ const name = c.req.param("name");
1909
+ const store = c.get("store");
1910
+ const service = new RegistryService(store);
1911
+ const result = await service.getSpec(name);
1912
+ if (!result) {
1913
+ return c.json({ error: "not_found", message: `Spec "${name}" not found`, statusCode: 404 }, 404);
1914
+ }
1915
+ const { spec, latestVersion } = result;
1916
+ return c.json({
1917
+ data: {
1918
+ latestVersion: latestVersion ? withoutContent(latestVersion) : void 0,
1919
+ spec
1920
+ }
1921
+ });
1922
+ });
1923
+
1924
+ // src/registry/routes/delete-spec.ts
1925
+ import { Hono as Hono5 } from "hono";
1926
+ var deleteSpecRoute = new Hono5().delete(
1927
+ "/:name",
1928
+ async (c) => {
1929
+ const name = c.req.param("name");
1930
+ const store = c.get("store");
1931
+ const service = new RegistryService(store);
1932
+ const deleted = await service.deleteSpec(name);
1933
+ if (!deleted) {
1934
+ return c.json({ error: "not_found", message: `Spec "${name}" not found`, statusCode: 404 }, 404);
1935
+ }
1936
+ return new Response(null, { status: 204 });
1937
+ }
1938
+ );
1939
+
1940
+ // src/registry/routes/versions.ts
1941
+ import { Hono as Hono6 } from "hono";
1942
+ function withoutContent2({ content: _, ...rest }) {
1943
+ return rest;
1944
+ }
1945
+ var versionsRoute = new Hono6().get(
1946
+ "/:name/versions",
1947
+ async (c) => {
1948
+ const name = c.req.param("name");
1949
+ const store = c.get("store");
1950
+ const service = new RegistryService(store);
1951
+ const spec = await service.getSpec(name);
1952
+ if (!spec) {
1953
+ return c.json({ error: "not_found", message: `Spec "${name}" not found`, statusCode: 404 }, 404);
1954
+ }
1955
+ const rawLimit = parseInt(c.req.query("limit") ?? "10", 10);
1956
+ const rawOffset = parseInt(c.req.query("offset") ?? "0", 10);
1957
+ const limit = Math.min(Math.max(isNaN(rawLimit) ? 10 : rawLimit, 1), 25);
1958
+ const offset = Math.max(isNaN(rawOffset) ? 0 : rawOffset, 0);
1959
+ const { versions, total } = await service.listVersions(name, { limit, offset });
1960
+ return c.json({
1961
+ data: versions.map(withoutContent2),
1962
+ pagination: {
1963
+ hasMore: offset + limit < total,
1964
+ limit,
1965
+ offset,
1966
+ total
1967
+ }
1968
+ });
1969
+ }
1970
+ );
1971
+
1972
+ // src/registry/routes/get-version.ts
1973
+ import { Hono as Hono7 } from "hono";
1974
+ function withoutContent3({ content: _, ...rest }) {
1975
+ return rest;
1976
+ }
1977
+ var getVersionRoute = new Hono7().get(
1978
+ "/:name/versions/:semver",
1979
+ async (c) => {
1980
+ const name = c.req.param("name");
1981
+ const semver = c.req.param("semver");
1982
+ const store = c.get("store");
1983
+ const service = new RegistryService(store);
1984
+ const version = await service.getVersion(name, semver);
1985
+ if (!version) {
1986
+ return c.json({ error: "not_found", message: `Version ${semver} not found for spec "${name}"`, statusCode: 404 }, 404);
1987
+ }
1988
+ return c.json({ data: withoutContent3(version) });
1989
+ }
1990
+ );
1991
+
1992
+ // src/registry/routes/compat-report.ts
1993
+ import { Hono as Hono8 } from "hono";
1994
+ var compatReportRoute = new Hono8().get(
1995
+ "/:name/compat/:semver",
1996
+ async (c) => {
1997
+ const name = c.req.param("name");
1998
+ const semver = c.req.param("semver");
1999
+ const store = c.get("store");
2000
+ const service = new RegistryService(store);
2001
+ const report = await service.getCompatReport(name, semver);
2002
+ if (!report) {
2003
+ return c.json({ error: "not_found", message: `Compat report not found for ${name}@${semver}`, statusCode: 404 }, 404);
2004
+ }
2005
+ return c.json({ data: report });
2006
+ }
2007
+ );
2008
+
2009
+ // src/registry/routes/compare-versions.ts
2010
+ import { Hono as Hono9 } from "hono";
2011
+ var compareVersionsRoute = new Hono9().get(
2012
+ "/:name/compare",
2013
+ async (c) => {
2014
+ const name = c.req.param("name");
2015
+ const from = c.req.query("from");
2016
+ const to = c.req.query("to");
2017
+ const store = c.get("store");
2018
+ const service = new RegistryService(store);
2019
+ if (!from || !to) {
2020
+ return c.json(
2021
+ {
2022
+ error: "bad_request",
2023
+ message: "Query parameters 'from' and 'to' are required",
2024
+ statusCode: 400
2025
+ },
2026
+ 400
2027
+ );
2028
+ }
2029
+ const result = await service.compareVersions(name, from, to);
2030
+ if (!result) {
2031
+ return c.json(
2032
+ {
2033
+ error: "not_found",
2034
+ message: `Spec or version not found for ${name}`,
2035
+ statusCode: 404
2036
+ },
2037
+ 404
2038
+ );
2039
+ }
2040
+ return c.json({ data: result });
2041
+ }
2042
+ );
2043
+
2044
+ // src/registry/routes/serve-spec.ts
2045
+ import { Hono as Hono10 } from "hono";
2046
+ import yaml4 from "js-yaml";
2047
+ var CONTENT_TYPES = {
2048
+ openapi: {
2049
+ json: "application/vnd.oai.openapi+json",
2050
+ yaml: "application/vnd.oai.openapi+yaml"
2051
+ },
2052
+ asyncapi: {
2053
+ json: "application/vnd.aai.asyncapi+json",
2054
+ yaml: "application/vnd.aai.asyncapi+yaml"
2055
+ }
2056
+ };
2057
+ async function serveSpec(c, name, semver, format) {
2058
+ const store = c.get("store");
2059
+ const service = new RegistryService(store);
2060
+ let content;
2061
+ let specType;
2062
+ let resolvedSemver;
2063
+ if (semver) {
2064
+ const version = await service.getVersion(name, semver);
2065
+ if (!version) {
2066
+ return c.json({ error: "not_found", message: `Version ${semver} not found for spec "${name}"`, statusCode: 404 }, 404);
2067
+ }
2068
+ content = version.content;
2069
+ resolvedSemver = semver;
2070
+ const result = await service.getSpec(name);
2071
+ specType = result?.spec.type ?? "openapi";
2072
+ } else {
2073
+ const result = await service.getSpec(name);
2074
+ if (!result || !result.latestVersion) {
2075
+ return c.json({ error: "not_found", message: `Spec "${name}" not found`, statusCode: 404 }, 404);
2076
+ }
2077
+ content = result.latestVersion.content;
2078
+ resolvedSemver = result.latestVersion.semver;
2079
+ specType = result.spec.type;
2080
+ }
2081
+ const parsed = yaml4.load(content);
2082
+ const contentType = CONTENT_TYPES[specType]?.[format] ?? CONTENT_TYPES.openapi[format];
2083
+ const headers = {
2084
+ "Content-Type": contentType
2085
+ };
2086
+ if (resolvedSemver) {
2087
+ headers["Grapity-Resolved-Version"] = resolvedSemver;
2088
+ }
2089
+ if (format === "yaml") {
2090
+ return new Response(yaml4.dump(parsed), { headers });
2091
+ }
2092
+ return new Response(JSON.stringify(parsed), { headers });
2093
+ }
2094
+ var serveSpecRoute = new Hono10().get(
2095
+ "/:name/spec.json",
2096
+ (c) => serveSpec(c, c.req.param("name"), void 0, "json")
2097
+ ).get(
2098
+ "/:name/spec.yaml",
2099
+ (c) => serveSpec(c, c.req.param("name"), void 0, "yaml")
2100
+ ).get(
2101
+ "/:name/versions/:semver/spec.json",
2102
+ (c) => serveSpec(c, c.req.param("name"), c.req.param("semver"), "json")
2103
+ ).get(
2104
+ "/:name/versions/:semver/spec.yaml",
2105
+ (c) => serveSpec(c, c.req.param("name"), c.req.param("semver"), "yaml")
2106
+ );
2107
+
2108
+ // src/registry/routes/health.ts
2109
+ import { Hono as Hono11 } from "hono";
2110
+ var healthRoute = new Hono11().get("/", async (c) => {
2111
+ return c.json({
2112
+ status: "ok",
2113
+ version: "0.0.1",
2114
+ uptime: process.uptime()
2115
+ });
2116
+ });
2117
+
2118
+ // src/registry/routes/welcome.ts
2119
+ import { Hono as Hono12 } from "hono";
2120
+ function buildPage(port, mode) {
2121
+ return `<!DOCTYPE html>
2122
+ <html lang="en">
2123
+ <head>
2124
+ <meta charset="UTF-8">
2125
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
2126
+ <title>Registry | grapity</title>
2127
+ <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0%25' y1='0%25' x2='100%25' y2='100%25'%3E%3Cstop offset='0%25' stop-color='%236366f1'/%3E%3Cstop offset='100%25' stop-color='%2306b6d4'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='32' height='32' rx='6' fill='%230a0a0f'/%3E%3Cpath d='M7 24 L16 6 L25 24 L21 24 L16 14 L11 24 Z' fill='url(%23g)'/%3E%3Ccircle cx='16' cy='24' r='2.5' fill='url(%23g)'/%3E%3C/svg%3E">
2128
+ <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=IBM+Plex+Mono:wght@400;500&family=IBM+Plex+Sans:wght@400;500;600&display=swap" rel="stylesheet">
2129
+ <style>
2130
+ html { overflow-y: scroll; }
2131
+ :root {
2132
+ --bg: #07070e;
2133
+ --surface: #0c0c15;
2134
+ --card: #0e0e18;
2135
+ --border: #181828;
2136
+ --indigo: #6366f1;
2137
+ --cyan: #06b6d4;
2138
+ --green: #22d3a5;
2139
+ --red: #f43f5e;
2140
+ --text: #ddddf5;
2141
+ --muted: #44445e;
2142
+ --mono: 'IBM Plex Mono', monospace;
2143
+ --sans: 'IBM Plex Sans', sans-serif;
2144
+ }
2145
+ * { box-sizing: border-box; margin: 0; padding: 0; }
2146
+ body {
2147
+ background: var(--bg);
2148
+ color: var(--text);
2149
+ font-family: var(--sans);
2150
+ min-height: 100vh;
2151
+ display: flex;
2152
+ flex-direction: column;
2153
+ align-items: center;
2154
+ padding: 48px 24px;
2155
+ }
2156
+
2157
+ /* \u2500\u2500 RGB CHROMATIC GLITCH \u2500\u2500 */
2158
+ @keyframes rgb-split {
2159
+ 0%,100% { transform: translate(0,0); opacity:1; }
2160
+ 8% { transform: translate(-3px, 0); opacity:0.85; }
2161
+ 9% { transform: translate(0,0); opacity:1; }
2162
+ 22% { transform: translate(2px,-1px); opacity:0.9; }
2163
+ 23% { transform: translate(0,0); }
2164
+ 60% { transform: translate(-2px,1px); }
2165
+ 61% { transform: translate(3px,0); opacity:0.8; }
2166
+ 62% { transform: translate(0,0); opacity:1; }
2167
+ 85% { transform: translate(1px,-1px); }
2168
+ 86% { transform: translate(0,0); }
2169
+ }
2170
+ .layer-r { fill: #f43f5e; mix-blend-mode: screen; animation: rgb-split 2.5s ease-in-out infinite; }
2171
+ .layer-g { fill: url(#lgr); mix-blend-mode: normal; }
2172
+ .layer-b { fill: #06b6d4; mix-blend-mode: screen; animation: rgb-split 2.5s ease-in-out infinite reverse; animation-delay: 0.05s; }
2173
+
2174
+ /* \u2500\u2500 LAYOUT \u2500\u2500 */
2175
+ .hero {
2176
+ display: flex;
2177
+ flex-direction: column;
2178
+ align-items: center;
2179
+ gap: 28px;
2180
+ text-align: center;
2181
+ max-width: 720px;
2182
+ width: 100%;
2183
+ }
2184
+ .hero > svg {
2185
+ margin-bottom: -24px;
2186
+ }
2187
+
2188
+ .wordmark {
2189
+ display: flex;
2190
+ flex-direction: column;
2191
+ align-items: center;
2192
+ gap: 10px;
2193
+ }
2194
+
2195
+ .wordmark h1 {
2196
+ font-family: 'Space Grotesk', sans-serif;
2197
+ font-size: 32px;
2198
+ font-weight: 600;
2199
+ letter-spacing: 0.02em;
2200
+ }
2201
+ .wordmark h1 .brand {
2202
+ color: var(--text);
2203
+ }
2204
+ .wordmark h1 .product {
2205
+ color: var(--muted);
2206
+ }
2207
+
2208
+ .wordmark .subtitle {
2209
+ font-family: var(--mono);
2210
+ font-size: 12px;
2211
+ color: var(--muted);
2212
+ letter-spacing: 0.14em;
2213
+ text-transform: uppercase;
2214
+ }
2215
+
2216
+ /* \u2500\u2500 STATUS BADGE \u2500\u2500 */
2217
+ .status-row {
2218
+ display: flex;
2219
+ align-items: center;
2220
+ gap: 10px;
2221
+ background: var(--card);
2222
+ border: 1px solid var(--border);
2223
+ border-radius: 8px;
2224
+ padding: 8px 16px;
2225
+ font-family: var(--mono);
2226
+ font-size: 12px;
2227
+ color: var(--muted);
2228
+ }
2229
+ @keyframes pulse-dot {
2230
+ 0%,100% { opacity:1; }
2231
+ 50% { opacity:0.3; }
2232
+ }
2233
+ .status-dot {
2234
+ width: 6px; height: 6px;
2235
+ border-radius: 50%;
2236
+ background: var(--green);
2237
+ animation: pulse-dot 2s ease-in-out infinite;
2238
+ flex-shrink: 0;
2239
+ }
2240
+ .status-row span { color: var(--text); }
2241
+ .status-row .sep { color: var(--border); }
2242
+
2243
+ /* \u2500\u2500 TABS \u2500\u2500 */
2244
+ .tabs {
2245
+ display: flex;
2246
+ gap: 4px;
2247
+ background: var(--card);
2248
+ border: 1px solid var(--border);
2249
+ border-radius: 8px;
2250
+ padding: 4px;
2251
+ width: 100%;
2252
+ max-width: 520px;
2253
+ }
2254
+ .tab {
2255
+ flex: 1;
2256
+ padding: 8px 12px;
2257
+ border: none;
2258
+ background: transparent;
2259
+ color: var(--muted);
2260
+ font-family: var(--mono);
2261
+ font-size: 13px;
2262
+ cursor: pointer;
2263
+ border-radius: 6px;
2264
+ transition: color 0.15s, background 0.15s;
2265
+ }
2266
+ .tab:hover { color: var(--text); }
2267
+ .tab.active {
2268
+ background: #6366f120;
2269
+ color: var(--text);
2270
+ }
2271
+
2272
+ /* \u2500\u2500 PANELS \u2500\u2500 */
2273
+ .panel {
2274
+ width: 100%;
2275
+ display: flex;
2276
+ flex-direction: column;
2277
+ gap: 4px;
2278
+ }
2279
+ .panel.hidden { display: none; }
2280
+
2281
+ .endpoints {
2282
+ width: 100%;
2283
+ display: flex;
2284
+ flex-direction: column;
2285
+ gap: 4px;
2286
+ }
2287
+
2288
+ .endpoint {
2289
+ display: flex;
2290
+ align-items: center;
2291
+ gap: 10px;
2292
+ padding: 10px 16px;
2293
+ background: var(--card);
2294
+ border: 1px solid var(--border);
2295
+ border-radius: 7px;
2296
+ font-family: var(--mono);
2297
+ font-size: 13px;
2298
+ text-decoration: none;
2299
+ color: var(--text);
2300
+ transition: border-color 0.15s, background 0.15s;
2301
+ }
2302
+ .endpoint:hover { border-color: #6366f133; background: #0f0f1e; }
2303
+
2304
+ .method {
2305
+ font-size: 12px;
2306
+ font-weight: 600;
2307
+ letter-spacing: 0.05em;
2308
+ padding: 2px 8px;
2309
+ border-radius: 4px;
2310
+ min-width: 42px;
2311
+ text-align: center;
2312
+ border: 1px solid;
2313
+ }
2314
+ .GET { background: #3b82f620; color: #3b82f6; border-color: #3b82f630; }
2315
+ .POST { background: #22d3a520; color: #22d3a5; border-color: #22d3a530; }
2316
+ .PUT { background: #f59e0b20; color: #f59e0b; border-color: #f59e0b30; }
2317
+ .DELETE { background: #f43f5e20; color: #f43f5e; border-color: #f43f5e30; }
2318
+ .PATCH { background: #a855f720; color: #a855f7; border-color: #a855f730; }
2319
+
2320
+ .path { flex: 1; color: var(--text); }
2321
+ .desc { color: var(--muted); font-size: 12px; }
2322
+
2323
+ /* \u2500\u2500 LINKS \u2500\u2500 */
2324
+ .links {
2325
+ display: flex;
2326
+ align-items: center;
2327
+ gap: 20px;
2328
+ }
2329
+ .link {
2330
+ font-family: var(--mono);
2331
+ font-size: 12px;
2332
+ color: var(--muted);
2333
+ text-decoration: none;
2334
+ display: flex;
2335
+ align-items: center;
2336
+ gap: 6px;
2337
+ transition: color 0.15s;
2338
+ }
2339
+ .link:hover { color: var(--text); }
2340
+ .link-dot { width: 3px; height: 3px; border-radius: 50%; background: var(--border); }
2341
+ </style>
2342
+ </head>
2343
+ <body>
2344
+ <div class="hero">
2345
+
2346
+ <!-- LOGO -->
2347
+ <svg viewBox="0 0 32 32" width="80" height="80" xmlns="http://www.w3.org/2000/svg">
2348
+ <defs>
2349
+ <linearGradient id="lgr" x1="0%" y1="0%" x2="100%" y2="100%">
2350
+ <stop offset="0%" stop-color="#6366f1"/>
2351
+ <stop offset="100%" stop-color="#06b6d4"/>
2352
+ </linearGradient>
2353
+ </defs>
2354
+ <rect width="32" height="32" rx="6" fill="#07070e"/>
2355
+ <path class="layer-r" d="M7 24 L16 6 L25 24 L21 24 L16 14 L11 24 Z"/>
2356
+ <circle class="layer-r" cx="16" cy="24" r="2.5"/>
2357
+ <path class="layer-g" d="M7 24 L16 6 L25 24 L21 24 L16 14 L11 24 Z"/>
2358
+ <circle class="layer-g" cx="16" cy="24" r="2.5"/>
2359
+ <path class="layer-b" d="M7 24 L16 6 L25 24 L21 24 L16 14 L11 24 Z"/>
2360
+ <circle class="layer-b" cx="16" cy="24" r="2.5"/>
2361
+ </svg>
2362
+
2363
+ <!-- WORDMARK -->
2364
+ <div class="wordmark">
2365
+ <h1><span class="brand">grapity</span> <span class="product">Registry</span></h1>
2366
+ <span class="subtitle">API contract and gateway config server</span>
2367
+ </div>
2368
+
2369
+ <!-- STATUS -->
2370
+ <div class="status-row">
2371
+ <span class="status-dot"></span>
2372
+ running on <span>localhost:${port}</span>
2373
+ <span class="sep">|</span>
2374
+ mode: <span>${mode}</span>
2375
+ <span class="sep">|</span>
2376
+ <a href="/v1/health" style="color:var(--cyan);text-decoration:none;">health \u2197</a>
2377
+ </div>
2378
+
2379
+ <!-- TABS -->
2380
+ <div class="tabs">
2381
+ <button class="tab active" onclick="switchTab('specs')">specs</button>
2382
+ <button class="tab" onclick="switchTab('gateway')">gateway configs</button>
2383
+ <button class="tab" onclick="switchTab('logs')">gateway logs</button>
2384
+ </div>
2385
+
2386
+ <!-- SPECS PANEL -->
2387
+ <div id="panel-specs" class="panel">
2388
+ <div class="endpoints">
2389
+ <div class="endpoint">
2390
+ <span class="method POST">POST</span>
2391
+ <span class="path">/v1/specs</span>
2392
+ <span class="desc">push a spec version</span>
2393
+ </div>
2394
+ <div class="endpoint">
2395
+ <span class="method GET">GET</span>
2396
+ <span class="path">/v1/specs</span>
2397
+ <span class="desc">list all specs</span>
2398
+ </div>
2399
+ <div class="endpoint">
2400
+ <span class="method GET">GET</span>
2401
+ <span class="path">/v1/specs/:name</span>
2402
+ <span class="desc">get spec metadata</span>
2403
+ </div>
2404
+ <div class="endpoint">
2405
+ <span class="method DELETE">DELETE</span>
2406
+ <span class="path">/v1/specs/:name</span>
2407
+ <span class="desc">delete spec and all versions</span>
2408
+ </div>
2409
+ <div class="endpoint">
2410
+ <span class="method POST">POST</span>
2411
+ <span class="path">/v1/specs/:name/validate</span>
2412
+ <span class="desc">validate against latest</span>
2413
+ </div>
2414
+ <div class="endpoint">
2415
+ <span class="method GET">GET</span>
2416
+ <span class="path">/v1/specs/:name/versions</span>
2417
+ <span class="desc">list versions</span>
2418
+ </div>
2419
+ <div class="endpoint">
2420
+ <span class="method GET">GET</span>
2421
+ <span class="path">/v1/specs/:name/versions/:semver</span>
2422
+ <span class="desc">get specific version</span>
2423
+ </div>
2424
+ <div class="endpoint">
2425
+ <span class="method GET">GET</span>
2426
+ <span class="path">/v1/specs/:name/spec.{yaml|json}</span>
2427
+ <span class="desc">fetch raw spec</span>
2428
+ </div>
2429
+ <div class="endpoint">
2430
+ <span class="method GET">GET</span>
2431
+ <span class="path">/v1/specs/:name/versions/:semver/spec.{yaml|json}</span>
2432
+ <span class="desc">version-specific spec</span>
2433
+ </div>
2434
+ <div class="endpoint">
2435
+ <span class="method GET">GET</span>
2436
+ <span class="path">/v1/specs/:name/compat/:semver</span>
2437
+ <span class="desc">compatibility report</span>
2438
+ </div>
2439
+ </div>
2440
+ </div>
2441
+
2442
+ <!-- GATEWAY CONFIGS PANEL -->
2443
+ <div id="panel-gateway" class="panel hidden">
2444
+ <div class="endpoints">
2445
+ <div class="endpoint">
2446
+ <span class="method POST">POST</span>
2447
+ <span class="path">/v1/gateway-configs</span>
2448
+ <span class="desc">push a gateway config</span>
2449
+ </div>
2450
+ <div class="endpoint">
2451
+ <span class="method GET">GET</span>
2452
+ <span class="path">/v1/gateway-configs</span>
2453
+ <span class="desc">list all gateway configs</span>
2454
+ </div>
2455
+ <div class="endpoint">
2456
+ <span class="method GET">GET</span>
2457
+ <span class="path">/v1/gateway-configs/:name</span>
2458
+ <span class="desc">get gateway config metadata</span>
2459
+ </div>
2460
+ <div class="endpoint">
2461
+ <span class="method GET">GET</span>
2462
+ <span class="path">/v1/gateway-configs/:name/versions</span>
2463
+ <span class="desc">list versions</span>
2464
+ </div>
2465
+ <div class="endpoint">
2466
+ <span class="method GET">GET</span>
2467
+ <span class="path">/v1/gateway-configs/:name/versions/:version</span>
2468
+ <span class="desc">get specific version</span>
2469
+ </div>
2470
+ </div>
2471
+ </div>
2472
+
2473
+ <!-- GATEWAY LOGS PANEL -->
2474
+ <div id="panel-logs" class="panel hidden">
2475
+ <div class="endpoints">
2476
+ <div class="endpoint">
2477
+ <span class="method POST">POST</span>
2478
+ <span class="path">/v1/gateway-logs/ingest/:provider/:environment</span>
2479
+ <span class="desc">ingest a gateway log</span>
2480
+ </div>
2481
+ <div class="endpoint">
2482
+ <span class="method GET">GET</span>
2483
+ <span class="path">/v1/gateway-logs</span>
2484
+ <span class="desc">query logs</span>
2485
+ </div>
2486
+ <div class="endpoint">
2487
+ <span class="method GET">GET</span>
2488
+ <span class="path">/v1/gateway-logs/stats</span>
2489
+ <span class="desc">endpoint usage stats</span>
2490
+ </div>
2491
+ <div class="endpoint">
2492
+ <span class="method GET">GET</span>
2493
+ <span class="path">/v1/gateway-logs/:id</span>
2494
+ <span class="desc">get a single log entry</span>
2495
+ </div>
2496
+ </div>
2497
+ </div>
2498
+
2499
+ <!-- LINKS -->
2500
+ <div class="links">
2501
+ <a class="link" href="https://github.com/grapitydev" target="_blank" rel="noopener">
2502
+ <svg viewBox="0 0 16 16" width="13" height="13" fill="currentColor"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>
2503
+ grapitydev
2504
+ </a>
2505
+ <span class="link-dot"></span>
2506
+ <a class="link" href="https://grapity.dev" target="_blank" rel="noopener">
2507
+ <svg viewBox="0 0 16 16" width="13" height="13" fill="currentColor"><path d="M8 0a8 8 0 100 16A8 8 0 008 0zm5.68 5.33h-2.3c-.25-1.1-.63-2.06-1.1-2.8a6.4 6.4 0 013.4 2.8zM8 1.37c.64.8 1.14 1.85 1.44 3.03h-2.88C6.86 3.22 7.36 2.17 8 1.37zm-3.28.16c-.47.74-.85 1.7-1.1 2.8h-2.3a6.4 6.4 0 013.4-2.8zM1.34 6.67h2.65c-.07.43-.1.87-.1 1.33 0 .46.03.9.1 1.33H1.34A6.43 6.43 0 011 8c0-.46.12-.91.34-1.33zm.98 4h2.3c.25 1.1.63 2.06 1.1 2.8a6.4 6.4 0 01-3.4-2.8zM8 14.63c-.64-.8-1.14-1.85-1.44-3.03h2.88c-.3 1.18-.8 2.23-1.44 3.03zm3.28-.16c.47-.74.85-1.7 1.1-2.8h2.3a6.4 6.4 0 01-3.4 2.8zm1.39-4.14c.07-.43.1-.87.1-1.33 0-.46-.03-.9-.1-1.33h2.65c.22.42.34.87.34 1.33a6.43 6.43 0 01-.34 1.33h-2.65zm-.66-4h-2.32A9.84 9.84 0 008 1.6c0-.09.01-.17.01-.26.87.38 1.65.95 2.28 1.67.34.42.64.9.88 1.46zm-3.65 0H6.04c.24-.56.54-1.04.88-1.46A6.38 6.38 0 019.15 9 9.84 9.84 0 009.08 6.33zM6.67 9H9.33A8.4 8.4 0 019.44 10.4H6.56C6.58 9.94 6.62 9.47 6.67 9z"/></svg>
2508
+ grapity.dev
2509
+ </a>
2510
+ <span class="link-dot"></span>
2511
+ <a class="link" href="https://grapity.dev/docs/platform/registry/overview" target="_blank" rel="noopener">
2512
+ docs \u2197
2513
+ </a>
2514
+ </div>
2515
+
2516
+ </div>
2517
+
2518
+ <script>
2519
+ function switchTab(tab) {
2520
+ document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
2521
+ document.querySelectorAll('.panel').forEach(p => p.classList.add('hidden'));
2522
+ const tabIndex = tab === 'specs' ? 1 : tab === 'gateway' ? 2 : 3;
2523
+ document.querySelector('.tab:nth-child(' + tabIndex + ')').classList.add('active');
2524
+ document.getElementById('panel-' + tab).classList.remove('hidden');
2525
+ }
2526
+ </script>
2527
+
2528
+ </body>
2529
+ </html>`;
2530
+ }
2531
+ var welcomeRoute = new Hono12().get("/", (c) => {
2532
+ const config = c.get("config");
2533
+ const mode = config.database === "sqlite" ? "local" : "remote";
2534
+ return c.html(buildPage(config.port, mode));
2535
+ });
2536
+
2537
+ // src/registry/routes/push-gateway-config.ts
2538
+ import { Hono as Hono13 } from "hono";
2539
+
2540
+ // src/registry/services/gateway.ts
2541
+ import { v4 as uuid3 } from "uuid";
2542
+ import yaml5 from "js-yaml";
2543
+ var UnsupportedProviderError = class extends Error {
2544
+ constructor(provider) {
2545
+ super(`Unsupported provider: "${provider}". Only "kong" is supported.`);
2546
+ this.name = "UnsupportedProviderError";
2547
+ }
2548
+ };
2549
+ var SpecNotFoundError = class extends Error {
2550
+ constructor(name, semver) {
2551
+ super(`Spec "${name}@${semver}" not found`);
2552
+ this.name = "SpecNotFoundError";
2553
+ }
2554
+ };
2555
+ var RouteNotFoundError = class extends Error {
2556
+ constructor(path2, methods) {
2557
+ super(`Route ${methods.join(",")} ${path2} not found in spec`);
2558
+ this.name = "RouteNotFoundError";
2559
+ }
2560
+ };
2561
+ var NoEnvironmentsError = class extends Error {
2562
+ constructor() {
2563
+ super("At least one environment must be defined");
2564
+ this.name = "NoEnvironmentsError";
2565
+ }
2566
+ };
2567
+ var NameExistsError = class extends Error {
2568
+ constructor(name) {
2569
+ super(`Gateway config "${name}" already exists`);
2570
+ this.name = "NameExistsError";
2571
+ }
2572
+ };
2573
+ var SUPPORTED_PROVIDERS = ["kong"];
2574
+ var GatewayService = class {
2575
+ constructor(gatewayStore, specStore) {
2576
+ this.gatewayStore = gatewayStore;
2577
+ this.specStore = specStore;
2578
+ }
2579
+ gatewayStore;
2580
+ specStore;
2581
+ async pushGatewayConfig(data) {
2582
+ if (!SUPPORTED_PROVIDERS.includes(data.provider)) {
2583
+ throw new UnsupportedProviderError(data.provider);
2584
+ }
2585
+ const specVersion = await this.specStore.getSpecVersion(data.specName, data.specSemver);
2586
+ if (!specVersion) {
2587
+ throw new SpecNotFoundError(data.specName, data.specSemver);
2588
+ }
2589
+ const envKeys = Object.keys(data.environments);
2590
+ if (envKeys.length === 0) {
2591
+ throw new NoEnvironmentsError();
2592
+ }
2593
+ const parsedSpec = yaml5.load(specVersion.content);
2594
+ const specPaths = parsedSpec?.paths ?? {};
2595
+ for (const route of data.routes) {
2596
+ const specPathItem = specPaths[route.path];
2597
+ if (!specPathItem) {
2598
+ throw new RouteNotFoundError(route.path, route.methods);
2599
+ }
2600
+ for (const method of route.methods) {
2601
+ const lowerMethod = method.toLowerCase();
2602
+ if (!specPathItem[lowerMethod]) {
2603
+ throw new RouteNotFoundError(route.path, [method]);
2604
+ }
2605
+ }
2606
+ }
2607
+ const existingConfig = await this.gatewayStore.getGatewayConfig(data.name);
2608
+ const config = {
2609
+ id: existingConfig?.id ?? uuid3(),
2610
+ name: data.name,
2611
+ provider: data.provider,
2612
+ specName: data.specName,
2613
+ specSemver: data.specSemver,
2614
+ createdAt: existingConfig?.createdAt ?? /* @__PURE__ */ new Date(),
2615
+ updatedAt: /* @__PURE__ */ new Date()
2616
+ };
2617
+ const environments = envKeys.map((name) => ({
2618
+ name,
2619
+ ...data.environments[name]
2620
+ }));
2621
+ const version = {
2622
+ id: uuid3(),
2623
+ gatewayConfigId: config.id,
2624
+ routes: data.routes,
2625
+ environments,
2626
+ callerIdentification: data.callerIdentification,
2627
+ content: data.content,
2628
+ checksum: computeChecksum(data.content),
2629
+ pushedBy: data.pushedBy,
2630
+ createdAt: /* @__PURE__ */ new Date()
2631
+ };
2632
+ await this.gatewayStore.pushGatewayConfigVersion(config, version);
2633
+ return { config, version };
2634
+ }
2635
+ async listGatewayConfigs() {
2636
+ return this.gatewayStore.listGatewayConfigs();
2637
+ }
2638
+ async getGatewayConfig(name) {
2639
+ return this.gatewayStore.getGatewayConfig(name);
2640
+ }
2641
+ async listGatewayConfigVersions(name) {
2642
+ return this.gatewayStore.listGatewayConfigVersions(name);
2643
+ }
2644
+ async getGatewayConfigVersion(name, versionId) {
2645
+ return this.gatewayStore.getGatewayConfigVersion(name, versionId);
2646
+ }
2647
+ };
2648
+
2649
+ // src/registry/routes/push-gateway-config.ts
2650
+ var pushGatewayConfigRoute = new Hono13().post("/", async (c) => {
2651
+ let body;
2652
+ try {
2653
+ body = await c.req.json();
2654
+ } catch {
2655
+ return c.json({ error: "bad_request", message: "Request body must be valid JSON", statusCode: 400 }, 400);
2656
+ }
2657
+ if (!body.name || typeof body.name !== "string") {
2658
+ return c.json({ error: "bad_request", message: "Missing required field: name", statusCode: 400 }, 400);
2659
+ }
2660
+ if (!body.provider || typeof body.provider !== "string") {
2661
+ return c.json({ error: "bad_request", message: "Missing required field: provider", statusCode: 400 }, 400);
2662
+ }
2663
+ if (!body.specName || typeof body.specName !== "string") {
2664
+ return c.json({ error: "bad_request", message: "Missing required field: specName", statusCode: 400 }, 400);
2665
+ }
2666
+ if (!body.specSemver || typeof body.specSemver !== "string") {
2667
+ return c.json({ error: "bad_request", message: "Missing required field: specSemver", statusCode: 400 }, 400);
2668
+ }
2669
+ if (!body.content || typeof body.content !== "string") {
2670
+ return c.json({ error: "bad_request", message: "Missing required field: content", statusCode: 400 }, 400);
2671
+ }
2672
+ const store = c.get("store");
2673
+ const service = new GatewayService(store, store);
2674
+ try {
2675
+ const result = await service.pushGatewayConfig({
2676
+ name: body.name,
2677
+ provider: body.provider,
2678
+ specName: body.specName,
2679
+ specSemver: body.specSemver,
2680
+ routes: Array.isArray(body.routes) ? body.routes : [],
2681
+ environments: body.environments ?? {},
2682
+ callerIdentification: body.callerIdentification,
2683
+ content: body.content,
2684
+ pushedBy: body.pushedBy
2685
+ });
2686
+ return c.json({ data: result }, 201);
2687
+ } catch (err) {
2688
+ if (err instanceof UnsupportedProviderError) {
2689
+ return c.json({ error: "unsupported_provider", message: err.message, statusCode: 422 }, 422);
2690
+ }
2691
+ if (err instanceof SpecNotFoundError) {
2692
+ return c.json({ error: "spec_not_found", message: err.message, statusCode: 422 }, 422);
2693
+ }
2694
+ if (err instanceof RouteNotFoundError) {
2695
+ return c.json({ error: "route_not_found", message: err.message, statusCode: 422 }, 422);
2696
+ }
2697
+ if (err instanceof NoEnvironmentsError) {
2698
+ return c.json({ error: "no_environments", message: err.message, statusCode: 422 }, 422);
2699
+ }
2700
+ if (err instanceof NameExistsError) {
2701
+ return c.json({ error: "name_exists", message: err.message, statusCode: 409 }, 409);
2702
+ }
2703
+ console.error("Push gateway config error:", err);
2704
+ return c.json({
2705
+ error: "internal_error",
2706
+ message: err instanceof Error ? err.message : "An unexpected error occurred",
2707
+ statusCode: 500
2708
+ }, 500);
2709
+ }
2710
+ });
2711
+
2712
+ // src/registry/routes/list-gateway-configs.ts
2713
+ import { Hono as Hono14 } from "hono";
2714
+ var listGatewayConfigsRoute = new Hono14().get("/", async (c) => {
2715
+ const store = c.get("store");
2716
+ const service = new GatewayService(store, store);
2717
+ const configs = await service.listGatewayConfigs();
2718
+ return c.json({ data: configs });
2719
+ });
2720
+
2721
+ // src/registry/routes/get-gateway-config.ts
2722
+ import { Hono as Hono15 } from "hono";
2723
+ var getGatewayConfigRoute = new Hono15().get("/:name", async (c) => {
2724
+ const name = c.req.param("name");
2725
+ const store = c.get("store");
2726
+ const service = new GatewayService(store, store);
2727
+ const config = await service.getGatewayConfig(name);
2728
+ if (!config) {
2729
+ return c.json({ error: "not_found", message: `Gateway config "${name}" not found`, statusCode: 404 }, 404);
2730
+ }
2731
+ return c.json({ data: config });
2732
+ });
2733
+
2734
+ // src/registry/routes/gateway-config-versions.ts
2735
+ import { Hono as Hono16 } from "hono";
2736
+ function withoutContent4({ content: _, ...rest }) {
2737
+ return rest;
2738
+ }
2739
+ var gatewayConfigVersionsRoute = new Hono16().get(
2740
+ "/:name/versions",
2741
+ async (c) => {
2742
+ const name = c.req.param("name");
2743
+ const store = c.get("store");
2744
+ const service = new GatewayService(store, store);
2745
+ const versions = await service.listGatewayConfigVersions(name);
2746
+ return c.json({ data: versions.map(withoutContent4) });
2747
+ }
2748
+ );
2749
+
2750
+ // src/registry/routes/get-gateway-config-version.ts
2751
+ import { Hono as Hono17 } from "hono";
2752
+ var getGatewayConfigVersionRoute = new Hono17().get(
2753
+ "/:name/versions/:version",
2754
+ async (c) => {
2755
+ const name = c.req.param("name");
2756
+ const versionId = c.req.param("version");
2757
+ const store = c.get("store");
2758
+ const service = new GatewayService(store, store);
2759
+ const version = await service.getGatewayConfigVersion(name, versionId);
2760
+ if (!version) {
2761
+ return c.json({ error: "not_found", message: `Version ${versionId} not found for gateway config "${name}"`, statusCode: 404 }, 404);
2762
+ }
2763
+ return c.json({ data: version });
2764
+ }
2765
+ );
2766
+
2767
+ // src/registry/routes/ingest-gateway-log.ts
2768
+ import { Hono as Hono18 } from "hono";
2769
+
2770
+ // src/registry/services/gateway-log.ts
2771
+ import { v4 as uuid4 } from "uuid";
2772
+ var DEFAULT_CALLER_RULES = [
2773
+ { source: "kong.consumer.id", confidence: "high" },
2774
+ { source: "header.x-consumer-id", confidence: "medium" },
2775
+ { source: "header.x-client-id", confidence: "medium" },
2776
+ { source: "ip+ua", confidence: "low" }
2777
+ ];
2778
+ function extractCallerIdentity(payload, rules) {
2779
+ for (const rule of rules) {
2780
+ const id = resolveCallerId(payload, rule.source);
2781
+ if (id) {
2782
+ return { callerId: id, callerSource: rule.source, callerConfidence: rule.confidence };
2783
+ }
2784
+ }
2785
+ return { callerConfidence: "anonymous" };
2786
+ }
2787
+ function resolveCallerId(payload, source) {
2788
+ if (source === "kong.consumer.id") {
2789
+ return payload.consumer?.id;
2790
+ }
2791
+ if (source.startsWith("header.")) {
2792
+ const headerName = source.slice("header.".length);
2793
+ const headers = payload.request?.headers ?? {};
2794
+ const value = headers[headerName] ?? headers[headerName.toLowerCase()];
2795
+ if (value) {
2796
+ return Array.isArray(value) ? value[0] : value;
2797
+ }
2798
+ }
2799
+ if (source === "ip+ua") {
2800
+ const ip = payload.client_ip ?? payload.tries?.[0]?.ip;
2801
+ const ua = payload.request?.headers?.["user-agent"] ?? payload.request?.headers?.["User-Agent"];
2802
+ if (ip && ua) {
2803
+ return `${ip}::${Array.isArray(ua) ? ua[0] : ua}`;
2804
+ }
2805
+ if (ip) {
2806
+ return ip;
2807
+ }
2808
+ }
2809
+ if (source === "ip") {
2810
+ return payload.client_ip ?? payload.tries?.[0]?.ip;
2811
+ }
2812
+ return void 0;
2813
+ }
2814
+ function extractRoutePath(payload) {
2815
+ const routePaths = payload.route?.paths;
2816
+ if (routePaths && routePaths.length > 0) {
2817
+ return routePaths[0];
2818
+ }
2819
+ return void 0;
2820
+ }
2821
+ var GatewayLogService = class {
2822
+ constructor(store) {
2823
+ this.store = store;
2824
+ }
2825
+ store;
2826
+ async ingestLog(provider, environment, payload) {
2827
+ const kongPayload = payload;
2828
+ const gatewayConfigName = kongPayload.service?.name;
2829
+ if (!gatewayConfigName) {
2830
+ throw new Error("Missing service.name in gateway log payload");
2831
+ }
2832
+ const configVersion = await this.store.getLatestGatewayConfigVersion(gatewayConfigName);
2833
+ const rules = configVersion?.callerIdentification?.rules ?? DEFAULT_CALLER_RULES;
2834
+ const caller = extractCallerIdentity(kongPayload, rules);
2835
+ const routePath = extractRoutePath(kongPayload);
2836
+ const log = {
2837
+ id: uuid4(),
2838
+ provider,
2839
+ gatewayConfigName,
2840
+ environment,
2841
+ method: kongPayload.request?.method?.toUpperCase() ?? "UNKNOWN",
2842
+ path: kongPayload.request?.uri ?? "/",
2843
+ routePath,
2844
+ status: kongPayload.response?.status ?? 0,
2845
+ callerId: caller.callerId,
2846
+ callerSource: caller.callerSource,
2847
+ callerConfidence: caller.callerConfidence,
2848
+ occurredAt: kongPayload.started_at ? new Date(kongPayload.started_at) : /* @__PURE__ */ new Date(),
2849
+ createdAt: /* @__PURE__ */ new Date()
2850
+ };
2851
+ await this.store.recordGatewayLog(log);
2852
+ }
2853
+ async listLogs(filters) {
2854
+ return this.store.listGatewayLogs(filters);
2855
+ }
2856
+ async getLog(id) {
2857
+ return this.store.getGatewayLog(id);
2858
+ }
2859
+ async getStats(filters) {
2860
+ return this.store.getGatewayLogStats(filters);
2861
+ }
2862
+ };
2863
+
2864
+ // src/registry/routes/ingest-gateway-log.ts
2865
+ var ingestGatewayLogRoute = new Hono18().post("/ingest/:provider/:environment", async (c) => {
2866
+ const store = c.get("store");
2867
+ const service = new GatewayLogService(store);
2868
+ const provider = c.req.param("provider");
2869
+ const environment = c.req.param("environment");
2870
+ try {
2871
+ const payload = await c.req.json();
2872
+ await service.ingestLog(provider, environment, payload);
2873
+ return c.json({ status: "ok" }, 201);
2874
+ } catch (err) {
2875
+ console.error("Gateway log ingest error:", err);
2876
+ return c.json({
2877
+ error: "bad_request",
2878
+ message: err instanceof Error ? err.message : "Invalid log payload",
2879
+ statusCode: 400
2880
+ }, 400);
2881
+ }
2882
+ });
2883
+
2884
+ // src/registry/routes/list-gateway-logs.ts
2885
+ import { Hono as Hono19 } from "hono";
2886
+ var listGatewayLogsRoute = new Hono19().get("/", async (c) => {
2887
+ const store = c.get("store");
2888
+ const service = new GatewayLogService(store);
2889
+ const filters = {
2890
+ gatewayConfigName: c.req.query("gatewayConfig") ?? void 0,
2891
+ environment: c.req.query("environment") ?? void 0,
2892
+ path: c.req.query("path") ?? void 0,
2893
+ method: c.req.query("method") ?? void 0,
2894
+ status: c.req.query("status") ? parseInt(c.req.query("status"), 10) : void 0,
2895
+ from: c.req.query("from") ? new Date(c.req.query("from")) : void 0,
2896
+ to: c.req.query("to") ? new Date(c.req.query("to")) : void 0,
2897
+ limit: c.req.query("limit") ? parseInt(c.req.query("limit"), 10) : void 0,
2898
+ offset: c.req.query("offset") ? parseInt(c.req.query("offset"), 10) : void 0
2899
+ };
2900
+ const result = await service.listLogs(filters);
2901
+ return c.json({
2902
+ data: result.logs,
2903
+ pagination: {
2904
+ total: result.total,
2905
+ limit: filters.limit ?? 50,
2906
+ offset: filters.offset ?? 0,
2907
+ hasMore: (filters.offset ?? 0) + (filters.limit ?? 50) < result.total
2908
+ }
2909
+ });
2910
+ });
2911
+
2912
+ // src/registry/routes/get-gateway-log.ts
2913
+ import { Hono as Hono20 } from "hono";
2914
+ var getGatewayLogRoute = new Hono20().get("/:id", async (c) => {
2915
+ const store = c.get("store");
2916
+ const service = new GatewayLogService(store);
2917
+ const id = c.req.param("id");
2918
+ const log = await service.getLog(id);
2919
+ if (!log) {
2920
+ return c.json({ error: "not_found", message: "Log not found", statusCode: 404 }, 404);
2921
+ }
2922
+ return c.json({ data: log });
2923
+ });
2924
+
2925
+ // src/registry/routes/gateway-log-stats.ts
2926
+ import { Hono as Hono21 } from "hono";
2927
+ var gatewayLogStatsRoute = new Hono21().get("/stats", async (c) => {
2928
+ const store = c.get("store");
2929
+ const service = new GatewayLogService(store);
2930
+ const filters = {
2931
+ gatewayConfigName: c.req.query("gatewayConfig") ?? void 0,
2932
+ environment: c.req.query("environment") ?? void 0,
2933
+ from: c.req.query("from") ? new Date(c.req.query("from")) : void 0,
2934
+ to: c.req.query("to") ? new Date(c.req.query("to")) : void 0
2935
+ };
2936
+ const stats = await service.getStats(filters);
2937
+ return c.json({
2938
+ data: stats.map((s) => ({
2939
+ gatewayConfigName: s.gatewayConfigName,
2940
+ environment: s.environment,
2941
+ method: s.method,
2942
+ routePath: s.routePath,
2943
+ lastSeenAt: s.lastSeenAt.toISOString(),
2944
+ totalCalls: s.totalCalls,
2945
+ uniqueCallerIds: s.uniqueCallerIds
2946
+ }))
2947
+ });
2948
+ });
2949
+
2950
+ // src/registry/server.ts
2951
+ function createApp(config, store) {
2952
+ const app = new Hono22();
2953
+ app.use("*", logger());
2954
+ app.use("*", cors());
2955
+ app.use("*", prettyJSON());
2956
+ app.use("*", async (c, next) => {
2957
+ c.set("store", store);
2958
+ c.set("config", config);
2959
+ await next();
2960
+ });
2961
+ app.route("/v1/specs", pushRoute);
2962
+ app.route("/v1/specs", validateRoute);
2963
+ app.route("/v1/specs", listRoute);
2964
+ app.route("/v1/specs", getSpecRoute);
2965
+ app.route("/v1/specs", deleteSpecRoute);
2966
+ app.route("/v1/specs", versionsRoute);
2967
+ app.route("/v1/specs", getVersionRoute);
2968
+ app.route("/v1/specs", serveSpecRoute);
2969
+ app.route("/v1/specs", compatReportRoute);
2970
+ app.route("/v1/specs", compareVersionsRoute);
2971
+ app.route("/v1/gateway-configs", pushGatewayConfigRoute);
2972
+ app.route("/v1/gateway-configs", listGatewayConfigsRoute);
2973
+ app.route("/v1/gateway-configs", getGatewayConfigRoute);
2974
+ app.route("/v1/gateway-configs", gatewayConfigVersionsRoute);
2975
+ app.route("/v1/gateway-configs", getGatewayConfigVersionRoute);
2976
+ app.route("/v1/gateway-logs", ingestGatewayLogRoute);
2977
+ app.route("/v1/gateway-logs", listGatewayLogsRoute);
2978
+ app.route("/v1/gateway-logs", gatewayLogStatsRoute);
2979
+ app.route("/v1/gateway-logs", getGatewayLogRoute);
2980
+ app.route("/v1/health", healthRoute);
2981
+ app.route("/", welcomeRoute);
2982
+ return app;
2983
+ }
2984
+
2985
+ // src/registry/config.ts
2986
+ var defaultConfig = {
2987
+ port: 3750,
2988
+ database: "sqlite",
2989
+ sqlitePath: void 0,
2990
+ gracePeriodDays: 30
2991
+ };
2992
+
2993
+ // src/registry/storage/sqlite.ts
2994
+ import Database from "better-sqlite3";
2995
+ import { drizzle } from "drizzle-orm/better-sqlite3";
2996
+ import { migrate } from "drizzle-orm/better-sqlite3/migrator";
2997
+ import { sql, eq, and, desc } from "drizzle-orm";
2998
+
2999
+ // src/registry/storage/schema.ts
3000
+ import { sqliteTable, text, integer, index } from "drizzle-orm/sqlite-core";
3001
+ var specs = sqliteTable("specs", {
3002
+ id: text("id").primaryKey(),
3003
+ name: text("name").notNull().unique(),
3004
+ type: text("type", { enum: ["openapi", "asyncapi"] }).notNull(),
3005
+ description: text("description"),
3006
+ owner: text("owner"),
3007
+ sourceRepo: text("source_repo"),
3008
+ tags: text("tags", { mode: "json" }).$type().default([]),
3009
+ createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
3010
+ updatedAt: integer("updated_at", { mode: "timestamp" }).notNull()
3011
+ });
3012
+ var specVersions = sqliteTable("spec_versions", {
3013
+ id: text("id").primaryKey(),
3014
+ specId: text("spec_id").notNull().references(() => specs.id),
3015
+ semver: text("semver").notNull(),
3016
+ content: text("content").notNull(),
3017
+ checksum: text("checksum").notNull(),
3018
+ gitRef: text("git_ref"),
3019
+ pushedBy: text("pushed_by"),
3020
+ compatibility: text("compatibility", { mode: "json" }).$type(),
3021
+ previousVersion: text("previous_version"),
3022
+ forceReason: text("force_reason"),
3023
+ isPrerelease: integer("is_prerelease", { mode: "boolean" }).notNull().default(false),
3024
+ createdAt: integer("created_at", { mode: "timestamp" }).notNull()
3025
+ }, (table) => [
3026
+ index("idx_spec_versions_spec_id").on(table.specId),
3027
+ index("idx_spec_versions_semver").on(table.specId, table.semver)
3028
+ ]);
3029
+ var auditLog = sqliteTable("audit_log", {
3030
+ id: text("id").primaryKey(),
3031
+ action: text("action", { enum: ["spec.push", "spec.push.force", "spec.delete"] }).notNull(),
3032
+ actor: text("actor").notNull(),
3033
+ specName: text("spec_name").notNull(),
3034
+ version: text("version"),
3035
+ details: text("details", { mode: "json" }).$type(),
3036
+ createdAt: integer("created_at", { mode: "timestamp" }).notNull()
3037
+ }, (table) => [
3038
+ index("idx_audit_log_spec_name").on(table.specName),
3039
+ index("idx_audit_log_created_at").on(table.createdAt)
3040
+ ]);
3041
+ var gatewayConfigs = sqliteTable("gateway_configs", {
3042
+ id: text("id").primaryKey(),
3043
+ name: text("name").notNull().unique(),
3044
+ provider: text("provider", { enum: ["kong"] }).notNull(),
3045
+ specName: text("spec_name").notNull(),
3046
+ specSemver: text("spec_semver").notNull(),
3047
+ createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
3048
+ updatedAt: integer("updated_at", { mode: "timestamp" }).notNull()
3049
+ });
3050
+ var gatewayConfigVersions = sqliteTable("gateway_config_versions", {
3051
+ id: text("id").primaryKey(),
3052
+ gatewayConfigId: text("gateway_config_id").notNull().references(() => gatewayConfigs.id),
3053
+ routes: text("routes", { mode: "json" }).$type().notNull(),
3054
+ environments: text("environments", { mode: "json" }).$type().notNull(),
3055
+ callerIdentification: text("caller_identification", { mode: "json" }).$type(),
3056
+ content: text("content").notNull(),
3057
+ checksum: text("checksum").notNull(),
3058
+ pushedBy: text("pushed_by"),
3059
+ createdAt: integer("created_at", { mode: "timestamp" }).notNull()
3060
+ }, (table) => [
3061
+ index("idx_gateway_config_versions_config_id").on(table.gatewayConfigId)
3062
+ ]);
3063
+ var httpLogs = sqliteTable("http_logs", {
3064
+ id: text("id").primaryKey(),
3065
+ provider: text("provider").notNull(),
3066
+ gatewayConfigName: text("gateway_config_name").notNull(),
3067
+ environment: text("environment").notNull(),
3068
+ method: text("method").notNull(),
3069
+ path: text("path").notNull(),
3070
+ routePath: text("route_path"),
3071
+ status: integer("status").notNull(),
3072
+ callerId: text("caller_id"),
3073
+ callerSource: text("caller_source"),
3074
+ callerConfidence: text("caller_confidence").notNull(),
3075
+ occurredAt: integer("occurred_at", { mode: "timestamp" }).notNull(),
3076
+ createdAt: integer("created_at", { mode: "timestamp" }).notNull()
3077
+ }, (table) => [
3078
+ index("idx_http_logs_config_env").on(table.gatewayConfigName, table.environment),
3079
+ index("idx_http_logs_occurred_at").on(table.occurredAt),
3080
+ index("idx_http_logs_caller").on(table.gatewayConfigName, table.environment, table.callerId)
3081
+ ]);
3082
+ var provisions = sqliteTable("provisions", {
3083
+ id: text("id").primaryKey(),
3084
+ gatewayConfigName: text("gateway_config_name").notNull(),
3085
+ gatewayConfigVersion: text("gateway_config_version").notNull(),
3086
+ environment: text("environment").notNull(),
3087
+ provider: text("provider", { enum: ["kong"] }).notNull(),
3088
+ synced: integer("synced", { mode: "boolean" }).notNull().default(false),
3089
+ actor: text("actor").notNull(),
3090
+ details: text("details", { mode: "json" }).$type(),
3091
+ createdAt: integer("created_at", { mode: "timestamp" }).notNull()
3092
+ }, (table) => [
3093
+ index("idx_provisions_config_name").on(table.gatewayConfigName),
3094
+ index("idx_provisions_created_at").on(table.createdAt)
3095
+ ]);
3096
+
3097
+ // src/registry/storage/sqlite.ts
3098
+ import { v4 as uuid5 } from "uuid";
3099
+
3100
+ // src/registry/paths.ts
3101
+ var SQLITE_MIGRATIONS_FOLDER = new URL(
3102
+ "../../drizzle/migrations/sqlite",
3103
+ import.meta.url
3104
+ ).pathname;
3105
+ var PG_MIGRATIONS_FOLDER = new URL(
3106
+ "../../drizzle/migrations/pg",
3107
+ import.meta.url
3108
+ ).pathname;
3109
+
3110
+ // src/registry/storage/sqlite.ts
3111
+ var MIGRATIONS_FOLDER = SQLITE_MIGRATIONS_FOLDER;
3112
+ var SQLiteSpecStore = class {
3113
+ db;
3114
+ constructor(dbPath) {
3115
+ const sqlite = new Database(dbPath);
3116
+ this.db = drizzle(sqlite);
3117
+ }
3118
+ async migrate() {
3119
+ await migrate(this.db, {
3120
+ migrationsFolder: MIGRATIONS_FOLDER
3121
+ });
3122
+ }
3123
+ async getSpec(name) {
3124
+ const rows = await this.db.select().from(specs).where(eq(specs.name, name)).limit(1);
3125
+ if (rows.length === 0) return null;
3126
+ return this.mapSpecRow(rows[0]);
3127
+ }
3128
+ async getSpecVersion(name, semver) {
3129
+ const spec = await this.getSpec(name);
3130
+ if (!spec) return null;
3131
+ const rows = await this.db.select().from(specVersions).where(and(eq(specVersions.specId, spec.id), eq(specVersions.semver, semver))).limit(1);
3132
+ if (rows.length === 0) return null;
3133
+ return this.mapVersionRow(rows[0]);
3134
+ }
3135
+ async getLatestVersion(name) {
3136
+ const spec = await this.getSpec(name);
3137
+ if (!spec) return null;
3138
+ const rows = await this.db.select().from(specVersions).where(eq(specVersions.specId, spec.id)).orderBy(desc(specVersions.createdAt), desc(sql`rowid`)).limit(1);
3139
+ if (rows.length === 0) return null;
3140
+ return this.mapVersionRow(rows[0]);
3141
+ }
3142
+ async listSpecs(filters) {
3143
+ const conditions = [];
3144
+ if (filters?.type) conditions.push(eq(specs.type, filters.type));
3145
+ if (filters?.owner) conditions.push(eq(specs.owner, filters.owner));
3146
+ let rows = conditions.length > 0 ? await this.db.select().from(specs).where(and(...conditions)) : await this.db.select().from(specs);
3147
+ if (filters?.tags && filters.tags.length > 0) {
3148
+ rows = rows.filter((row) => {
3149
+ const rowTags = row.tags ?? [];
3150
+ return filters.tags.every((tag) => rowTags.includes(tag));
3151
+ });
3152
+ }
3153
+ return rows.map((r) => this.mapSpecRow(r));
3154
+ }
3155
+ async listVersions(name, options) {
3156
+ const spec = await this.getSpec(name);
3157
+ if (!spec) return { versions: [], total: 0 };
3158
+ const limit = options?.limit ?? 10;
3159
+ const offset = options?.offset ?? 0;
3160
+ const [countRow] = await this.db.select({ count: sql`count(*)` }).from(specVersions).where(eq(specVersions.specId, spec.id));
3161
+ const total = Number(countRow?.count ?? 0);
3162
+ const rows = await this.db.select().from(specVersions).where(eq(specVersions.specId, spec.id)).orderBy(desc(specVersions.createdAt), desc(sql`rowid`)).limit(limit).offset(offset);
3163
+ return { versions: rows.map((r) => this.mapVersionRow(r)), total };
3164
+ }
3165
+ async pushSpecVersion(spec, version) {
3166
+ const existingSpec = await this.getSpec(spec.name);
3167
+ if (!existingSpec) {
3168
+ await this.db.insert(specs).values({
3169
+ id: spec.id,
3170
+ name: spec.name,
3171
+ type: spec.type,
3172
+ description: spec.description ?? null,
3173
+ owner: spec.owner ?? null,
3174
+ sourceRepo: spec.sourceRepo ?? null,
3175
+ tags: spec.tags ?? [],
3176
+ createdAt: spec.createdAt,
3177
+ updatedAt: spec.updatedAt
3178
+ });
3179
+ } else {
3180
+ await this.db.update(specs).set({ updatedAt: /* @__PURE__ */ new Date() }).where(eq(specs.id, existingSpec.id));
3181
+ }
3182
+ const specId = existingSpec?.id ?? spec.id;
3183
+ await this.db.insert(specVersions).values({
3184
+ id: version.id,
3185
+ specId,
3186
+ semver: version.semver,
3187
+ content: version.content,
3188
+ checksum: version.checksum,
3189
+ gitRef: version.gitRef ?? null,
3190
+ pushedBy: version.pushedBy ?? null,
3191
+ compatibility: version.compatibility ?? null,
3192
+ previousVersion: version.previousVersion ?? null,
3193
+ forceReason: version.forceReason ?? null,
3194
+ isPrerelease: version.isPrerelease,
3195
+ createdAt: version.createdAt
3196
+ });
3197
+ return version;
3198
+ }
3199
+ async deleteSpec(name) {
3200
+ const existingSpec = await this.getSpec(name);
3201
+ if (!existingSpec) return false;
3202
+ await this.db.delete(specVersions).where(eq(specVersions.specId, existingSpec.id));
3203
+ await this.db.delete(specs).where(eq(specs.id, existingSpec.id));
3204
+ return true;
3205
+ }
3206
+ async getCompatReport(name, semver) {
3207
+ const version = await this.getSpecVersion(name, semver);
3208
+ return version?.compatibility ?? null;
3209
+ }
3210
+ async logAudit(action, actor, specName, version, details) {
3211
+ await this.db.insert(auditLog).values({
3212
+ id: uuid5(),
3213
+ action,
3214
+ actor,
3215
+ specName,
3216
+ version: version ?? null,
3217
+ details: details ?? null,
3218
+ createdAt: /* @__PURE__ */ new Date()
3219
+ });
3220
+ }
3221
+ mapSpecRow(row) {
3222
+ return {
3223
+ id: row.id,
3224
+ name: row.name,
3225
+ type: row.type,
3226
+ description: row.description ?? void 0,
3227
+ owner: row.owner ?? void 0,
3228
+ sourceRepo: row.sourceRepo ?? void 0,
3229
+ tags: row.tags ?? [],
3230
+ createdAt: row.createdAt,
3231
+ updatedAt: row.updatedAt
3232
+ };
3233
+ }
3234
+ mapVersionRow(row) {
3235
+ return {
3236
+ id: row.id,
3237
+ specId: row.specId,
3238
+ semver: row.semver,
3239
+ content: row.content,
3240
+ checksum: row.checksum,
3241
+ gitRef: row.gitRef ?? void 0,
3242
+ pushedBy: row.pushedBy ?? void 0,
3243
+ compatibility: row.compatibility ?? void 0,
3244
+ previousVersion: row.previousVersion ?? void 0,
3245
+ forceReason: row.forceReason ?? void 0,
3246
+ isPrerelease: row.isPrerelease,
3247
+ createdAt: row.createdAt
3248
+ };
3249
+ }
3250
+ // GatewayConfigStore implementation
3251
+ async getGatewayConfig(name) {
3252
+ const rows = await this.db.select().from(gatewayConfigs).where(eq(gatewayConfigs.name, name)).limit(1);
3253
+ if (rows.length === 0) return null;
3254
+ return this.mapGatewayConfigRow(rows[0]);
3255
+ }
3256
+ async listGatewayConfigs() {
3257
+ const rows = await this.db.select().from(gatewayConfigs);
3258
+ return rows.map((r) => this.mapGatewayConfigRow(r));
3259
+ }
3260
+ async getGatewayConfigVersion(name, versionId) {
3261
+ const config = await this.getGatewayConfig(name);
3262
+ if (!config) return null;
3263
+ const rows = await this.db.select().from(gatewayConfigVersions).where(and(eq(gatewayConfigVersions.gatewayConfigId, config.id), eq(gatewayConfigVersions.id, versionId))).limit(1);
3264
+ if (rows.length === 0) return null;
3265
+ return this.mapGatewayConfigVersionRow(rows[0]);
3266
+ }
3267
+ async getLatestGatewayConfigVersion(name) {
3268
+ const config = await this.getGatewayConfig(name);
3269
+ if (!config) return null;
3270
+ const rows = await this.db.select().from(gatewayConfigVersions).where(eq(gatewayConfigVersions.gatewayConfigId, config.id)).orderBy(desc(gatewayConfigVersions.createdAt), desc(sql`rowid`)).limit(1);
3271
+ if (rows.length === 0) return null;
3272
+ return this.mapGatewayConfigVersionRow(rows[0]);
3273
+ }
3274
+ async listGatewayConfigVersions(name) {
3275
+ const config = await this.getGatewayConfig(name);
3276
+ if (!config) return [];
3277
+ const rows = await this.db.select().from(gatewayConfigVersions).where(eq(gatewayConfigVersions.gatewayConfigId, config.id)).orderBy(desc(gatewayConfigVersions.createdAt), desc(sql`rowid`)).limit(5);
3278
+ return rows.map((r) => this.mapGatewayConfigVersionRow(r));
3279
+ }
3280
+ async pushGatewayConfigVersion(config, version) {
3281
+ const existingConfig = await this.getGatewayConfig(config.name);
3282
+ if (!existingConfig) {
3283
+ await this.db.insert(gatewayConfigs).values({
3284
+ id: config.id,
3285
+ name: config.name,
3286
+ provider: config.provider,
3287
+ specName: config.specName,
3288
+ specSemver: config.specSemver,
3289
+ createdAt: config.createdAt,
3290
+ updatedAt: config.updatedAt
3291
+ });
3292
+ } else {
3293
+ await this.db.update(gatewayConfigs).set({ updatedAt: /* @__PURE__ */ new Date(), specSemver: config.specSemver }).where(eq(gatewayConfigs.id, existingConfig.id));
3294
+ }
3295
+ const configId = existingConfig?.id ?? config.id;
3296
+ await this.db.insert(gatewayConfigVersions).values({
3297
+ id: version.id,
3298
+ gatewayConfigId: configId,
3299
+ routes: version.routes,
3300
+ environments: version.environments,
3301
+ callerIdentification: version.callerIdentification ?? null,
3302
+ content: version.content,
3303
+ checksum: version.checksum,
3304
+ pushedBy: version.pushedBy ?? null,
3305
+ createdAt: version.createdAt
3306
+ });
3307
+ const versions = await this.db.select().from(gatewayConfigVersions).where(eq(gatewayConfigVersions.gatewayConfigId, configId)).orderBy(desc(gatewayConfigVersions.createdAt), desc(sql`rowid`));
3308
+ if (versions.length > 5) {
3309
+ const toDelete = versions.slice(5);
3310
+ for (const v of toDelete) {
3311
+ await this.db.delete(gatewayConfigVersions).where(eq(gatewayConfigVersions.id, v.id));
3312
+ }
3313
+ }
3314
+ return version;
3315
+ }
3316
+ async recordProvision(provision) {
3317
+ await this.db.insert(provisions).values({
3318
+ id: provision.id,
3319
+ gatewayConfigName: provision.gatewayConfigName,
3320
+ gatewayConfigVersion: provision.gatewayConfigVersion,
3321
+ environment: provision.environment,
3322
+ provider: provision.provider,
3323
+ synced: provision.synced,
3324
+ actor: provision.actor,
3325
+ details: provision.details ?? null,
3326
+ createdAt: provision.createdAt
3327
+ });
3328
+ }
3329
+ async listProvisions(gatewayConfigName) {
3330
+ const rows = gatewayConfigName ? await this.db.select().from(provisions).where(eq(provisions.gatewayConfigName, gatewayConfigName)).orderBy(desc(provisions.createdAt)) : await this.db.select().from(provisions).orderBy(desc(provisions.createdAt));
3331
+ return rows.map((r) => ({
3332
+ id: r.id,
3333
+ gatewayConfigName: r.gatewayConfigName,
3334
+ gatewayConfigVersion: r.gatewayConfigVersion,
3335
+ environment: r.environment,
3336
+ provider: r.provider,
3337
+ synced: r.synced,
3338
+ actor: r.actor,
3339
+ details: r.details ?? void 0,
3340
+ createdAt: r.createdAt
3341
+ }));
3342
+ }
3343
+ mapGatewayConfigRow(row) {
3344
+ return {
3345
+ id: row.id,
3346
+ name: row.name,
3347
+ provider: row.provider,
3348
+ specName: row.specName,
3349
+ specSemver: row.specSemver,
3350
+ createdAt: row.createdAt,
3351
+ updatedAt: row.updatedAt
3352
+ };
3353
+ }
3354
+ mapGatewayConfigVersionRow(row) {
3355
+ return {
3356
+ id: row.id,
3357
+ gatewayConfigId: row.gatewayConfigId,
3358
+ routes: row.routes,
3359
+ environments: row.environments,
3360
+ callerIdentification: row.callerIdentification ?? void 0,
3361
+ content: row.content,
3362
+ checksum: row.checksum,
3363
+ pushedBy: row.pushedBy ?? void 0,
3364
+ createdAt: row.createdAt
3365
+ };
3366
+ }
3367
+ async recordGatewayLog(log) {
3368
+ await this.db.insert(httpLogs).values({
3369
+ id: log.id,
3370
+ provider: log.provider,
3371
+ gatewayConfigName: log.gatewayConfigName,
3372
+ environment: log.environment,
3373
+ method: log.method,
3374
+ path: log.path,
3375
+ routePath: log.routePath ?? null,
3376
+ status: log.status,
3377
+ callerId: log.callerId ?? null,
3378
+ callerSource: log.callerSource ?? null,
3379
+ callerConfidence: log.callerConfidence,
3380
+ occurredAt: log.occurredAt,
3381
+ createdAt: log.createdAt
3382
+ });
3383
+ }
3384
+ async listGatewayLogs(filters) {
3385
+ const limit = filters.limit ?? 50;
3386
+ const offset = filters.offset ?? 0;
3387
+ let query = this.db.select().from(httpLogs);
3388
+ const conditions = [];
3389
+ if (filters.gatewayConfigName) {
3390
+ conditions.push(eq(httpLogs.gatewayConfigName, filters.gatewayConfigName));
3391
+ }
3392
+ if (filters.environment) {
3393
+ conditions.push(eq(httpLogs.environment, filters.environment));
3394
+ }
3395
+ if (filters.path) {
3396
+ conditions.push(eq(httpLogs.path, filters.path));
3397
+ }
3398
+ if (filters.method) {
3399
+ conditions.push(eq(httpLogs.method, filters.method));
3400
+ }
3401
+ if (filters.status !== void 0) {
3402
+ conditions.push(eq(httpLogs.status, filters.status));
3403
+ }
3404
+ if (filters.from) {
3405
+ conditions.push(sql`${httpLogs.occurredAt} >= ${filters.from.getTime()}`);
3406
+ }
3407
+ if (filters.to) {
3408
+ conditions.push(sql`${httpLogs.occurredAt} <= ${filters.to.getTime()}`);
3409
+ }
3410
+ if (conditions.length > 0) {
3411
+ query = query.where(and(...conditions));
3412
+ }
3413
+ const countResult = await this.db.select({ count: sql`count(*)` }).from(httpLogs).where(conditions.length > 0 ? and(...conditions) : void 0);
3414
+ const total = countResult[0]?.count ?? 0;
3415
+ const rows = await query.orderBy(desc(httpLogs.occurredAt)).limit(limit).offset(offset);
3416
+ return {
3417
+ logs: rows.map((r) => ({
3418
+ id: r.id,
3419
+ provider: r.provider,
3420
+ gatewayConfigName: r.gatewayConfigName,
3421
+ environment: r.environment,
3422
+ method: r.method,
3423
+ path: r.path,
3424
+ routePath: r.routePath ?? void 0,
3425
+ status: r.status,
3426
+ callerId: r.callerId ?? void 0,
3427
+ callerSource: r.callerSource ?? void 0,
3428
+ callerConfidence: r.callerConfidence,
3429
+ occurredAt: r.occurredAt,
3430
+ createdAt: r.createdAt
3431
+ })),
3432
+ total
3433
+ };
3434
+ }
3435
+ async getGatewayLog(id) {
3436
+ const rows = await this.db.select().from(httpLogs).where(eq(httpLogs.id, id)).limit(1);
3437
+ if (rows.length === 0) return null;
3438
+ const r = rows[0];
3439
+ return {
3440
+ id: r.id,
3441
+ provider: r.provider,
3442
+ gatewayConfigName: r.gatewayConfigName,
3443
+ environment: r.environment,
3444
+ method: r.method,
3445
+ path: r.path,
3446
+ routePath: r.routePath ?? void 0,
3447
+ status: r.status,
3448
+ callerId: r.callerId ?? void 0,
3449
+ callerSource: r.callerSource ?? void 0,
3450
+ callerConfidence: r.callerConfidence,
3451
+ occurredAt: r.occurredAt,
3452
+ createdAt: r.createdAt
3453
+ };
3454
+ }
3455
+ async getGatewayLogStats(_filters) {
3456
+ const conditions = [];
3457
+ if (_filters.gatewayConfigName) {
3458
+ conditions.push(eq(httpLogs.gatewayConfigName, _filters.gatewayConfigName));
3459
+ }
3460
+ if (_filters.environment) {
3461
+ conditions.push(eq(httpLogs.environment, _filters.environment));
3462
+ }
3463
+ if (_filters.from) {
3464
+ conditions.push(sql`${httpLogs.occurredAt} >= ${_filters.from.getTime()}`);
3465
+ }
3466
+ if (_filters.to) {
3467
+ conditions.push(sql`${httpLogs.occurredAt} <= ${_filters.to.getTime()}`);
3468
+ }
3469
+ const whereClause = conditions.length > 0 ? and(...conditions) : void 0;
3470
+ const rows = await this.db.select({
3471
+ gatewayConfigName: httpLogs.gatewayConfigName,
3472
+ environment: httpLogs.environment,
3473
+ method: httpLogs.method,
3474
+ routePath: httpLogs.routePath,
3475
+ lastSeenAt: sql`max(${httpLogs.occurredAt})`,
3476
+ totalCalls: sql`count(*)`,
3477
+ uniqueCallerIds: sql`count(distinct ${httpLogs.callerId})`
3478
+ }).from(httpLogs).where(whereClause).groupBy(httpLogs.gatewayConfigName, httpLogs.environment, httpLogs.method, httpLogs.routePath);
3479
+ return rows.map((r) => ({
3480
+ gatewayConfigName: r.gatewayConfigName,
3481
+ environment: r.environment,
3482
+ method: r.method,
3483
+ routePath: r.routePath ?? "/",
3484
+ lastSeenAt: new Date(r.lastSeenAt),
3485
+ totalCalls: r.totalCalls,
3486
+ uniqueCallerIds: r.uniqueCallerIds
3487
+ }));
3488
+ }
3489
+ async deleteGatewayLogsOlderThan(days) {
3490
+ const cutoff = /* @__PURE__ */ new Date();
3491
+ cutoff.setDate(cutoff.getDate() - days);
3492
+ await this.db.delete(httpLogs).where(sql`${httpLogs.occurredAt} < ${cutoff.getTime()}`);
3493
+ }
3494
+ };
3495
+
3496
+ // src/registry/serve.ts
3497
+ async function startServer(userConfig) {
3498
+ const config = { ...defaultConfig, ...userConfig };
3499
+ if (!config.sqlitePath && config.database === "sqlite") {
3500
+ const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
3501
+ config.sqlitePath = path.join(homeDir, ".grapity", "registry.db");
3502
+ }
3503
+ if (config.database === "sqlite" && config.sqlitePath) {
3504
+ const dir = path.dirname(config.sqlitePath);
3505
+ if (!fs.existsSync(dir)) {
3506
+ fs.mkdirSync(dir, { recursive: true });
3507
+ }
3508
+ }
3509
+ const store = new SQLiteSpecStore(config.sqlitePath);
3510
+ await store.migrate();
3511
+ const app = createApp(config, store);
3512
+ serve({
3513
+ fetch: app.fetch,
3514
+ port: config.port
3515
+ });
3516
+ return app;
3517
+ }
3518
+ if (process.argv[1] === new URL(import.meta.url).pathname) {
3519
+ startServer();
3520
+ }
3521
+ export {
3522
+ startServer
3523
+ };