@geekmidas/cli 0.3.0 → 0.5.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 (309) hide show
  1. package/README.md +488 -71
  2. package/dist/CronGenerator-BPTqNYOR.d.cts +14 -0
  3. package/dist/{CronGenerator-Bh26MaNA.mjs → CronGenerator-CCRYptuT.mjs} +2 -2
  4. package/dist/{CronGenerator-Bh26MaNA.mjs.map → CronGenerator-CCRYptuT.mjs.map} +1 -1
  5. package/dist/{CronGenerator-C6MF8rlG.cjs → CronGenerator-D4TWXQbh.cjs} +2 -2
  6. package/dist/{CronGenerator-C6MF8rlG.cjs.map → CronGenerator-D4TWXQbh.cjs.map} +1 -1
  7. package/dist/CronGenerator-YAj59JUd.d.mts +14 -0
  8. package/dist/EndpointGenerator-ChAD1INz.d.cts +19 -0
  9. package/dist/EndpointGenerator-Cj3O1U8-.d.mts +19 -0
  10. package/dist/{EndpointGenerator-CWh18d92.mjs → EndpointGenerator-DGivkPLT.mjs} +77 -7
  11. package/dist/EndpointGenerator-DGivkPLT.mjs.map +1 -0
  12. package/dist/{EndpointGenerator-C73wNoih.cjs → EndpointGenerator-npWEDoK2.cjs} +77 -7
  13. package/dist/EndpointGenerator-npWEDoK2.cjs.map +1 -0
  14. package/dist/FunctionGenerator-429-9NER.d.cts +14 -0
  15. package/dist/FunctionGenerator-BQ4ehoID.d.mts +14 -0
  16. package/dist/{FunctionGenerator-BNE_GC7N.mjs → FunctionGenerator-CVk0h8tO.mjs} +2 -2
  17. package/dist/{FunctionGenerator-BNE_GC7N.mjs.map → FunctionGenerator-CVk0h8tO.mjs.map} +1 -1
  18. package/dist/{FunctionGenerator-FgZUTd8L.cjs → FunctionGenerator-DYTnyr4c.cjs} +2 -2
  19. package/dist/{FunctionGenerator-FgZUTd8L.cjs.map → FunctionGenerator-DYTnyr4c.cjs.map} +1 -1
  20. package/dist/Generator-BjHK_qce.d.mts +27 -0
  21. package/dist/{Generator-UanJW0_V.mjs → Generator-CDt4pB3W.mjs} +1 -1
  22. package/dist/{Generator-UanJW0_V.mjs.map → Generator-CDt4pB3W.mjs.map} +1 -1
  23. package/dist/{Generator-CDoEXCDg.cjs → Generator-CLVplqm2.cjs} +1 -1
  24. package/dist/{Generator-CDoEXCDg.cjs.map → Generator-CLVplqm2.cjs.map} +1 -1
  25. package/dist/Generator-DxQMCQp7.d.cts +27 -0
  26. package/dist/OpenApiTsGenerator-Be-sKGTT.cjs +501 -0
  27. package/dist/OpenApiTsGenerator-Be-sKGTT.cjs.map +1 -0
  28. package/dist/OpenApiTsGenerator-C4mHHaku.mjs +495 -0
  29. package/dist/OpenApiTsGenerator-C4mHHaku.mjs.map +1 -0
  30. package/dist/SubscriberGenerator-7uX42xyG.d.mts +15 -0
  31. package/dist/{SubscriberGenerator-Dnlj_1FK.mjs → SubscriberGenerator-DABaJXML.mjs} +2 -2
  32. package/dist/{SubscriberGenerator-Dnlj_1FK.mjs.map → SubscriberGenerator-DABaJXML.mjs.map} +1 -1
  33. package/dist/{SubscriberGenerator-Bd-a7aiw.cjs → SubscriberGenerator-D_zpNGFr.cjs} +2 -2
  34. package/dist/{SubscriberGenerator-Bd-a7aiw.cjs.map → SubscriberGenerator-D_zpNGFr.cjs.map} +1 -1
  35. package/dist/SubscriberGenerator-Dtb3HS4i.d.cts +15 -0
  36. package/dist/api-B3SCEHPf.cjs +190 -0
  37. package/dist/api-B3SCEHPf.cjs.map +1 -0
  38. package/dist/api-BKIN0s0S.mjs +184 -0
  39. package/dist/api-BKIN0s0S.mjs.map +1 -0
  40. package/dist/build/index.cjs +11 -10
  41. package/dist/build/index.d.cts +7 -0
  42. package/dist/build/index.d.mts +7 -0
  43. package/dist/build/index.mjs +11 -10
  44. package/dist/build/manifests.cjs +1 -1
  45. package/dist/build/manifests.d.cts +13 -0
  46. package/dist/build/manifests.d.mts +13 -0
  47. package/dist/build/manifests.mjs +1 -1
  48. package/dist/build/providerResolver.cjs +1 -1
  49. package/dist/build/providerResolver.d.cts +23 -0
  50. package/dist/build/providerResolver.d.mts +23 -0
  51. package/dist/build/providerResolver.mjs +1 -1
  52. package/dist/build/types.d.cts +3 -0
  53. package/dist/build/types.d.mts +3 -0
  54. package/dist/{build-C6uEGRj8.mjs → build-B8C_qHir.mjs} +15 -13
  55. package/dist/build-B8C_qHir.mjs.map +1 -0
  56. package/dist/{build-CBYBPZpC.cjs → build-D0Wr49bf.cjs} +15 -13
  57. package/dist/build-D0Wr49bf.cjs.map +1 -0
  58. package/dist/config-Ba-Gbpbc.d.cts +11 -0
  59. package/dist/config-Bq72aj8e.mjs +75 -0
  60. package/dist/config-Bq72aj8e.mjs.map +1 -0
  61. package/dist/config-CFls09Ey.cjs +93 -0
  62. package/dist/config-CFls09Ey.cjs.map +1 -0
  63. package/dist/config-CLEDqKO3.cjs +157 -0
  64. package/dist/config-CLEDqKO3.cjs.map +1 -0
  65. package/dist/config-DBsmMDhf.d.mts +11 -0
  66. package/dist/config-Dp8RonV_.mjs +151 -0
  67. package/dist/config-Dp8RonV_.mjs.map +1 -0
  68. package/dist/config.cjs +4 -2
  69. package/dist/config.d.cts +48 -0
  70. package/dist/config.d.mts +48 -0
  71. package/dist/config.mjs +2 -2
  72. package/dist/dev/index.cjs +12 -10
  73. package/dist/dev/index.d.cts +36 -0
  74. package/dist/dev/index.d.mts +36 -0
  75. package/dist/dev/index.mjs +10 -10
  76. package/dist/dev-B734w3L1.mjs +343 -0
  77. package/dist/dev-B734w3L1.mjs.map +1 -0
  78. package/dist/{dev-DbtyToc7.cjs → dev-DHqYn8k4.cjs} +161 -47
  79. package/dist/dev-DHqYn8k4.cjs.map +1 -0
  80. package/dist/docker-5d8Yh5_X.cjs +119 -0
  81. package/dist/docker-5d8Yh5_X.cjs.map +1 -0
  82. package/dist/docker-DlUqdFle.mjs +113 -0
  83. package/dist/docker-DlUqdFle.mjs.map +1 -0
  84. package/dist/env-B-OKjgI4.cjs +144 -0
  85. package/dist/env-B-OKjgI4.cjs.map +1 -0
  86. package/dist/env-HfuJRlg5.d.cts +11 -0
  87. package/dist/env-nd-iQPYM.d.mts +11 -0
  88. package/dist/env-tv1HlZlw.mjs +138 -0
  89. package/dist/env-tv1HlZlw.mjs.map +1 -0
  90. package/dist/generators/CronGenerator.cjs +2 -2
  91. package/dist/generators/CronGenerator.d.cts +5 -0
  92. package/dist/generators/CronGenerator.d.mts +5 -0
  93. package/dist/generators/CronGenerator.mjs +2 -2
  94. package/dist/generators/EndpointGenerator.cjs +2 -2
  95. package/dist/generators/EndpointGenerator.d.cts +5 -0
  96. package/dist/generators/EndpointGenerator.d.mts +5 -0
  97. package/dist/generators/EndpointGenerator.mjs +2 -2
  98. package/dist/generators/FunctionGenerator.cjs +2 -2
  99. package/dist/generators/FunctionGenerator.d.cts +5 -0
  100. package/dist/generators/FunctionGenerator.d.mts +5 -0
  101. package/dist/generators/FunctionGenerator.mjs +2 -2
  102. package/dist/generators/Generator.cjs +1 -1
  103. package/dist/generators/Generator.d.cts +4 -0
  104. package/dist/generators/Generator.d.mts +4 -0
  105. package/dist/generators/Generator.mjs +1 -1
  106. package/dist/generators/OpenApiTsGenerator.cjs +3 -0
  107. package/dist/generators/OpenApiTsGenerator.d.cts +44 -0
  108. package/dist/generators/OpenApiTsGenerator.d.mts +44 -0
  109. package/dist/generators/OpenApiTsGenerator.mjs +3 -0
  110. package/dist/generators/SubscriberGenerator.cjs +2 -2
  111. package/dist/generators/SubscriberGenerator.d.cts +5 -0
  112. package/dist/generators/SubscriberGenerator.d.mts +5 -0
  113. package/dist/generators/SubscriberGenerator.mjs +2 -2
  114. package/dist/generators/index.cjs +6 -6
  115. package/dist/generators/index.d.cts +8 -0
  116. package/dist/generators/index.d.mts +8 -0
  117. package/dist/generators/index.mjs +6 -6
  118. package/dist/index-C523No_B.d.mts +64 -0
  119. package/dist/index-DrzN4xkQ.d.cts +64 -0
  120. package/dist/index.cjs +56 -18
  121. package/dist/index.cjs.map +1 -1
  122. package/dist/index.d.cts +1 -0
  123. package/dist/index.d.mts +1 -0
  124. package/dist/index.mjs +56 -18
  125. package/dist/index.mjs.map +1 -1
  126. package/dist/init/generators/config.cjs +3 -0
  127. package/dist/init/generators/config.d.cts +3 -0
  128. package/dist/init/generators/config.d.mts +3 -0
  129. package/dist/init/generators/config.mjs +3 -0
  130. package/dist/init/generators/docker.cjs +3 -0
  131. package/dist/init/generators/docker.d.cts +11 -0
  132. package/dist/init/generators/docker.d.mts +11 -0
  133. package/dist/init/generators/docker.mjs +3 -0
  134. package/dist/init/generators/env.cjs +3 -0
  135. package/dist/init/generators/env.d.cts +3 -0
  136. package/dist/init/generators/env.d.mts +3 -0
  137. package/dist/init/generators/env.mjs +3 -0
  138. package/dist/init/generators/index.cjs +9 -0
  139. package/dist/init/generators/index.d.cts +6 -0
  140. package/dist/init/generators/index.d.mts +6 -0
  141. package/dist/init/generators/index.mjs +6 -0
  142. package/dist/init/generators/models.cjs +3 -0
  143. package/dist/init/generators/models.d.cts +11 -0
  144. package/dist/init/generators/models.d.mts +11 -0
  145. package/dist/init/generators/models.mjs +3 -0
  146. package/dist/init/generators/monorepo.cjs +3 -0
  147. package/dist/init/generators/monorepo.d.cts +11 -0
  148. package/dist/init/generators/monorepo.d.mts +11 -0
  149. package/dist/init/generators/monorepo.mjs +3 -0
  150. package/dist/init/generators/package.cjs +3 -0
  151. package/dist/init/generators/package.d.cts +3 -0
  152. package/dist/init/generators/package.d.mts +3 -0
  153. package/dist/init/generators/package.mjs +3 -0
  154. package/dist/init/generators/source.cjs +3 -0
  155. package/dist/init/generators/source.d.cts +3 -0
  156. package/dist/init/generators/source.d.mts +3 -0
  157. package/dist/init/generators/source.mjs +3 -0
  158. package/dist/init/index.cjs +16 -0
  159. package/dist/init/index.d.cts +17 -0
  160. package/dist/init/index.d.mts +17 -0
  161. package/dist/init/index.mjs +16 -0
  162. package/dist/init/templates/api.cjs +3 -0
  163. package/dist/init/templates/api.d.cts +7 -0
  164. package/dist/init/templates/api.d.mts +7 -0
  165. package/dist/init/templates/api.mjs +3 -0
  166. package/dist/init/templates/index.cjs +10 -0
  167. package/dist/init/templates/index.d.cts +2 -0
  168. package/dist/init/templates/index.d.mts +2 -0
  169. package/dist/init/templates/index.mjs +7 -0
  170. package/dist/init/templates/minimal.cjs +3 -0
  171. package/dist/init/templates/minimal.d.cts +7 -0
  172. package/dist/init/templates/minimal.d.mts +7 -0
  173. package/dist/init/templates/minimal.mjs +3 -0
  174. package/dist/init/templates/serverless.cjs +3 -0
  175. package/dist/init/templates/serverless.d.cts +7 -0
  176. package/dist/init/templates/serverless.d.mts +7 -0
  177. package/dist/init/templates/serverless.mjs +3 -0
  178. package/dist/init/templates/worker.cjs +3 -0
  179. package/dist/init/templates/worker.d.cts +7 -0
  180. package/dist/init/templates/worker.d.mts +7 -0
  181. package/dist/init/templates/worker.mjs +3 -0
  182. package/dist/init/utils.cjs +7 -0
  183. package/dist/init/utils.d.cts +25 -0
  184. package/dist/init/utils.d.mts +25 -0
  185. package/dist/init/utils.mjs +3 -0
  186. package/dist/init-CtOnZn3G.mjs +145 -0
  187. package/dist/init-CtOnZn3G.mjs.map +1 -0
  188. package/dist/init-qLFsWR-R.cjs +151 -0
  189. package/dist/init-qLFsWR-R.cjs.map +1 -0
  190. package/dist/{manifests-C2eMoMUm.mjs → manifests-DIA_2QYd.mjs} +1 -1
  191. package/dist/{manifests-C2eMoMUm.mjs.map → manifests-DIA_2QYd.mjs.map} +1 -1
  192. package/dist/{manifests-CK1VV_pM.cjs → manifests-VJ9-2JpW.cjs} +1 -1
  193. package/dist/{manifests-CK1VV_pM.cjs.map → manifests-VJ9-2JpW.cjs.map} +1 -1
  194. package/dist/minimal-Bdhhpp7v.cjs +93 -0
  195. package/dist/minimal-Bdhhpp7v.cjs.map +1 -0
  196. package/dist/minimal-C4GsE45s.mjs +87 -0
  197. package/dist/minimal-C4GsE45s.mjs.map +1 -0
  198. package/dist/models-DyNwdOcz.cjs +121 -0
  199. package/dist/models-DyNwdOcz.cjs.map +1 -0
  200. package/dist/models-cvNg6Oea.mjs +115 -0
  201. package/dist/models-cvNg6Oea.mjs.map +1 -0
  202. package/dist/monorepo-Cknwzj5C.mjs +184 -0
  203. package/dist/monorepo-Cknwzj5C.mjs.map +1 -0
  204. package/dist/monorepo-sEK8gW59.cjs +190 -0
  205. package/dist/monorepo-sEK8gW59.cjs.map +1 -0
  206. package/dist/openapi-BQWPWyNB.cjs +56 -0
  207. package/dist/openapi-BQWPWyNB.cjs.map +1 -0
  208. package/dist/openapi-DBX8cJJ8.mjs +50 -0
  209. package/dist/openapi-DBX8cJJ8.mjs.map +1 -0
  210. package/dist/{openapi-react-query-D9Z7lh0p.cjs → openapi-react-query-DxHjXQvg.cjs} +1 -1
  211. package/dist/{openapi-react-query-D9Z7lh0p.cjs.map → openapi-react-query-DxHjXQvg.cjs.map} +1 -1
  212. package/dist/{openapi-react-query-MEBlYIM1.mjs → openapi-react-query-o7Mp1Jd5.mjs} +1 -1
  213. package/dist/{openapi-react-query-MEBlYIM1.mjs.map → openapi-react-query-o7Mp1Jd5.mjs.map} +1 -1
  214. package/dist/openapi-react-query.cjs +1 -1
  215. package/dist/openapi-react-query.d.cts +11 -0
  216. package/dist/openapi-react-query.d.mts +11 -0
  217. package/dist/openapi-react-query.mjs +1 -1
  218. package/dist/openapi.cjs +5 -4
  219. package/dist/openapi.d.cts +11 -0
  220. package/dist/openapi.d.mts +11 -0
  221. package/dist/openapi.mjs +5 -4
  222. package/dist/package-C7WhWU8m.d.mts +11 -0
  223. package/dist/package-CIfmeuSW.mjs +51 -0
  224. package/dist/package-CIfmeuSW.mjs.map +1 -0
  225. package/dist/package-DvWEMz6z.d.cts +11 -0
  226. package/dist/package-PP-o1nvq.cjs +57 -0
  227. package/dist/package-PP-o1nvq.cjs.map +1 -0
  228. package/dist/{providerResolver-B_TjNF0_.mjs → providerResolver-DEVKngbC.mjs} +1 -1
  229. package/dist/{providerResolver-B_TjNF0_.mjs.map → providerResolver-DEVKngbC.mjs.map} +1 -1
  230. package/dist/{providerResolver-DgvzNfP4.cjs → providerResolver-DOTbN9jo.cjs} +1 -1
  231. package/dist/{providerResolver-DgvzNfP4.cjs.map → providerResolver-DOTbN9jo.cjs.map} +1 -1
  232. package/dist/serverless-DkHBF2vC.mjs +108 -0
  233. package/dist/serverless-DkHBF2vC.mjs.map +1 -0
  234. package/dist/serverless-Yav3GRVz.cjs +114 -0
  235. package/dist/serverless-Yav3GRVz.cjs.map +1 -0
  236. package/dist/source-D6v2BnKT.d.mts +11 -0
  237. package/dist/source-D8fK9qRo.d.cts +11 -0
  238. package/dist/source-DT5Xhiob.cjs +17 -0
  239. package/dist/source-DT5Xhiob.cjs.map +1 -0
  240. package/dist/source-DnaH_MLA.mjs +11 -0
  241. package/dist/source-DnaH_MLA.mjs.map +1 -0
  242. package/dist/templates-CBFUwpBy.mjs +64 -0
  243. package/dist/templates-CBFUwpBy.mjs.map +1 -0
  244. package/dist/templates-DM_rtYYW.cjs +87 -0
  245. package/dist/templates-DM_rtYYW.cjs.map +1 -0
  246. package/dist/types-C4KITv-y.d.mts +51 -0
  247. package/dist/types-Cxl8-uwV.d.mts +129 -0
  248. package/dist/types-DB99_qIy.d.cts +129 -0
  249. package/dist/types-DLFN49M3.d.cts +51 -0
  250. package/dist/types.d.cts +2 -0
  251. package/dist/types.d.mts +2 -0
  252. package/dist/utils-BX3F4fT8.cjs +99 -0
  253. package/dist/utils-BX3F4fT8.cjs.map +1 -0
  254. package/dist/utils-C31-SWHP.mjs +69 -0
  255. package/dist/utils-C31-SWHP.mjs.map +1 -0
  256. package/dist/worker--8O5a3Hv.cjs +150 -0
  257. package/dist/worker--8O5a3Hv.cjs.map +1 -0
  258. package/dist/worker-Jme7uOOJ.mjs +144 -0
  259. package/dist/worker-Jme7uOOJ.mjs.map +1 -0
  260. package/docs/OPENAPI_TYPESCRIPT_DESIGN.md +408 -0
  261. package/package.json +19 -4
  262. package/src/__tests__/loadEnvFiles.spec.ts +131 -0
  263. package/src/__tests__/openapi.spec.ts +78 -63
  264. package/src/build/index.ts +14 -16
  265. package/src/build/types.ts +18 -2
  266. package/src/config.ts +61 -2
  267. package/src/dev/__tests__/index.spec.ts +98 -1
  268. package/src/dev/index.ts +229 -42
  269. package/src/generators/EndpointGenerator.ts +98 -5
  270. package/src/generators/OpenApiTsGenerator.ts +798 -0
  271. package/src/index.ts +32 -3
  272. package/src/init/__tests__/generators.spec.ts +366 -0
  273. package/src/init/__tests__/init.spec.ts +341 -0
  274. package/src/init/__tests__/utils.spec.ts +104 -0
  275. package/src/init/generators/config.ts +192 -0
  276. package/src/init/generators/docker.ts +134 -0
  277. package/src/init/generators/env.ts +182 -0
  278. package/src/init/generators/index.ts +4 -0
  279. package/src/init/generators/models.ts +129 -0
  280. package/src/init/generators/monorepo.ts +211 -0
  281. package/src/init/generators/package.ts +81 -0
  282. package/src/init/generators/source.ts +15 -0
  283. package/src/init/index.ts +206 -0
  284. package/src/init/templates/api.ts +218 -0
  285. package/src/init/templates/index.ts +108 -0
  286. package/src/init/templates/minimal.ts +102 -0
  287. package/src/init/templates/serverless.ts +129 -0
  288. package/src/init/templates/worker.ts +169 -0
  289. package/src/init/utils.ts +98 -0
  290. package/src/openapi.ts +36 -15
  291. package/src/types.ts +43 -0
  292. package/tsdown.config.ts +1 -1
  293. package/dist/EndpointGenerator-C73wNoih.cjs.map +0 -1
  294. package/dist/EndpointGenerator-CWh18d92.mjs.map +0 -1
  295. package/dist/build-C6uEGRj8.mjs.map +0 -1
  296. package/dist/build-CBYBPZpC.cjs.map +0 -1
  297. package/dist/config-D1EpSGk6.cjs +0 -36
  298. package/dist/config-D1EpSGk6.cjs.map +0 -1
  299. package/dist/config-U-mdW-7Y.mjs +0 -30
  300. package/dist/config-U-mdW-7Y.mjs.map +0 -1
  301. package/dist/dev-DbtyToc7.cjs.map +0 -1
  302. package/dist/dev-DnGYXuMn.mjs +0 -241
  303. package/dist/dev-DnGYXuMn.mjs.map +0 -1
  304. package/dist/openapi-BTHbPrxS.mjs +0 -36
  305. package/dist/openapi-BTHbPrxS.mjs.map +0 -1
  306. package/dist/openapi-CewcfoRH.cjs +0 -42
  307. package/dist/openapi-CewcfoRH.cjs.map +0 -1
  308. /package/dist/{generators-CEKtVh81.cjs → generators-3IemvCLk.cjs} +0 -0
  309. /package/dist/{generators-CsLujGXs.mjs → generators-FNpdfN6J.mjs} +0 -0
@@ -0,0 +1,408 @@
1
+ # OpenAPI TypeScript Generation - Design Document
2
+
3
+ ## Overview
4
+
5
+ This document describes the design for generating TypeScript OpenAPI specifications with integrated authentication support. The feature extends `gkm openapi` with a `--ts` flag that outputs a TypeScript module instead of JSON, enabling:
6
+
7
+ 1. **Type-safe paths** - Full TypeScript interface for API routes
8
+ 2. **Runtime auth map** - Per-endpoint authentication requirements
9
+ 3. **Reusable schemas** - TypeScript interfaces extracted from Zod/Valibot schemas
10
+ 4. **Security scheme definitions** - OpenAPI security schemes as typed constants
11
+
12
+ ## Motivation
13
+
14
+ ### Current Flow (JSON-based)
15
+
16
+ ```
17
+ Endpoints → gkm openapi → openapi.json → openapi-typescript → types.d.ts
18
+
19
+ (no auth info at runtime)
20
+ ```
21
+
22
+ **Problems:**
23
+ - Auth requirements lost at runtime - OpenAPI security info exists but isn't usable by the fetcher
24
+ - Two-step generation - need external tool (`openapi-typescript`) for types
25
+ - No schema reuse - generated types are isolated, can't reference shared interfaces
26
+
27
+ ### Proposed Flow (TypeScript-native)
28
+
29
+ ```
30
+ Endpoints → gkm openapi --ts → openapi.ts
31
+
32
+ Exports:
33
+ ├── paths (type)
34
+ ├── endpointAuth (runtime map)
35
+ ├── securitySchemes (runtime definitions)
36
+ └── schema interfaces (reusable types)
37
+ ```
38
+
39
+ ## Command Interface
40
+
41
+ ```bash
42
+ # Default: generates TypeScript
43
+ gkm openapi --output ./src/api/openapi.ts
44
+
45
+ # Explicit JSON output (legacy)
46
+ gkm openapi --json --output ./openapi.json
47
+ ```
48
+
49
+ ### Options
50
+
51
+ | Flag | Description | Default |
52
+ |------|-------------|---------|
53
+ | `--json` | Generate JSON instead of TypeScript (legacy) | `false` |
54
+ | `--output` | Output file path | `openapi.ts` |
55
+
56
+ ## Generated Output Structure
57
+
58
+ ### File: `openapi.ts`
59
+
60
+ ```typescript
61
+ // Auto-generated by @geekmidas/cli - DO NOT EDIT
62
+ import type { OpenAPIV3_1 } from 'openapi-types';
63
+
64
+ // ============================================================
65
+ // Security Schemes
66
+ // ============================================================
67
+
68
+ /**
69
+ * Available security schemes for this API.
70
+ * Maps authorizer names to OpenAPI security scheme definitions.
71
+ */
72
+ export const securitySchemes = {
73
+ bearer: {
74
+ type: 'http',
75
+ scheme: 'bearer',
76
+ bearerFormat: 'JWT',
77
+ },
78
+ iam: {
79
+ type: 'apiKey',
80
+ in: 'header',
81
+ name: 'Authorization',
82
+ 'x-amazon-apigateway-authtype': 'awsSigv4',
83
+ },
84
+ apiKey: {
85
+ type: 'apiKey',
86
+ in: 'header',
87
+ name: 'X-API-Key',
88
+ },
89
+ } as const satisfies Record<string, OpenAPIV3_1.SecuritySchemeObject>;
90
+
91
+ export type SecuritySchemeId = keyof typeof securitySchemes;
92
+
93
+ // ============================================================
94
+ // Endpoint Authentication Map
95
+ // ============================================================
96
+
97
+ /**
98
+ * Runtime map of endpoints to their required authentication scheme.
99
+ * `null` indicates a public endpoint (no auth required).
100
+ */
101
+ export const endpointAuth = {
102
+ 'POST /tenants': 'iam',
103
+ 'GET /tenants/{id}': 'bearer',
104
+ 'PUT /tenants/{id}': 'bearer',
105
+ 'DELETE /tenants/{id}': 'bearer',
106
+ 'GET /health': null,
107
+ 'GET /docs': null,
108
+ } as const satisfies Record<string, SecuritySchemeId | null>;
109
+
110
+ export type AuthenticatedEndpoint = {
111
+ [K in keyof typeof endpointAuth]: typeof endpointAuth[K] extends null ? never : K;
112
+ }[keyof typeof endpointAuth];
113
+
114
+ export type PublicEndpoint = {
115
+ [K in keyof typeof endpointAuth]: typeof endpointAuth[K] extends null ? K : never;
116
+ }[keyof typeof endpointAuth];
117
+
118
+ // ============================================================
119
+ // Schema Definitions (Reusable Types)
120
+ // ============================================================
121
+
122
+ export interface Tenant {
123
+ id: string;
124
+ name: string;
125
+ createdAt: string;
126
+ updatedAt: string;
127
+ }
128
+
129
+ export interface CreateTenantInput {
130
+ name: string;
131
+ }
132
+
133
+ export interface UpdateTenantInput {
134
+ name?: string;
135
+ }
136
+
137
+ // ============================================================
138
+ // OpenAPI Paths
139
+ // ============================================================
140
+
141
+ export interface paths {
142
+ '/tenants': {
143
+ post: {
144
+ requestBody: {
145
+ content: {
146
+ 'application/json': CreateTenantInput;
147
+ };
148
+ };
149
+ responses: {
150
+ 201: {
151
+ content: {
152
+ 'application/json': Tenant;
153
+ };
154
+ };
155
+ };
156
+ };
157
+ };
158
+ '/tenants/{id}': {
159
+ parameters: {
160
+ path: {
161
+ id: string;
162
+ };
163
+ };
164
+ get: {
165
+ responses: {
166
+ 200: {
167
+ content: {
168
+ 'application/json': Tenant;
169
+ };
170
+ };
171
+ };
172
+ };
173
+ put: {
174
+ requestBody: {
175
+ content: {
176
+ 'application/json': UpdateTenantInput;
177
+ };
178
+ };
179
+ responses: {
180
+ 200: {
181
+ content: {
182
+ 'application/json': Tenant;
183
+ };
184
+ };
185
+ };
186
+ };
187
+ delete: {
188
+ responses: {
189
+ 204: {
190
+ content: never;
191
+ };
192
+ };
193
+ };
194
+ };
195
+ '/health': {
196
+ get: {
197
+ responses: {
198
+ 200: {
199
+ content: {
200
+ 'application/json': { status: string };
201
+ };
202
+ };
203
+ };
204
+ };
205
+ };
206
+ }
207
+
208
+ // ============================================================
209
+ // Full OpenAPI Specification (Optional)
210
+ // ============================================================
211
+
212
+ export const spec = {
213
+ openapi: '3.1.0',
214
+ info: {
215
+ title: 'Tenant API',
216
+ version: '1.0.0',
217
+ },
218
+ paths: { /* ... */ },
219
+ components: {
220
+ securitySchemes,
221
+ schemas: { /* ... */ },
222
+ },
223
+ } as const satisfies OpenAPIV3_1.Document;
224
+ ```
225
+
226
+ ## Implementation Details
227
+
228
+ ### 1. Authorizer to Security Scheme Mapping
229
+
230
+ The `Authorizer.type` field maps to OpenAPI security schemes:
231
+
232
+ | Authorizer Type | OpenAPI Security Scheme |
233
+ |-----------------|------------------------|
234
+ | `jwt`, `bearer` | `{ type: 'http', scheme: 'bearer', bearerFormat: 'JWT' }` |
235
+ | `iam`, `aws-sigv4` | `{ type: 'apiKey', in: 'header', name: 'Authorization', 'x-amazon-apigateway-authtype': 'awsSigv4' }` |
236
+ | `apiKey` | `{ type: 'apiKey', in: metadata.in, name: metadata.name }` |
237
+ | `oauth2` | `{ type: 'oauth2', flows: { ... } }` |
238
+ | `oidc` | `{ type: 'openIdConnect', openIdConnectUrl: metadata.issuer }` |
239
+ | `none` / undefined | `null` (public endpoint) |
240
+
241
+ ### 2. Schema Extraction
242
+
243
+ Schemas are extracted from StandardSchema (Zod/Valibot) definitions:
244
+
245
+ ```typescript
246
+ // From endpoint definition
247
+ const endpoint = e
248
+ .post('/tenants')
249
+ .body(z.object({ name: z.string() }))
250
+ .output(z.object({ id: z.string(), name: z.string() }))
251
+ .handle(async ({ body }) => { ... });
252
+
253
+ // Extracted as
254
+ export interface CreateTenantInput {
255
+ name: string;
256
+ }
257
+
258
+ export interface CreateTenantOutput {
259
+ id: string;
260
+ name: string;
261
+ }
262
+ ```
263
+
264
+ ### 3. Naming Strategy
265
+
266
+ | Schema Location | Generated Name |
267
+ |-----------------|----------------|
268
+ | Body schema | `{OperationId}Input` or `{Method}{Route}Input` |
269
+ | Output schema | `{OperationId}Output` or `{Method}{Route}Output` |
270
+ | Params schema | `{OperationId}Params` |
271
+ | Query schema | `{OperationId}Query` |
272
+
273
+ ### 4. Component Collector Enhancement
274
+
275
+ ```typescript
276
+ // packages/schema/src/openapi.ts
277
+ export interface ComponentCollector {
278
+ schemas: Record<string, OpenAPIV3_1.SchemaObject>;
279
+ securitySchemes: Record<string, OpenAPIV3_1.SecuritySchemeObject>;
280
+ addSchema(id: string, schema: OpenAPIV3_1.SchemaObject): void;
281
+ addSecurityScheme(id: string, scheme: OpenAPIV3_1.SecuritySchemeObject): void;
282
+ getReference(id: string): OpenAPIV3_1.ReferenceObject;
283
+ }
284
+ ```
285
+
286
+ ## Integration with Auth-Aware Fetcher
287
+
288
+ The generated `endpointAuth` map enables automatic auth handling:
289
+
290
+ ```typescript
291
+ // packages/client/src/auth-fetcher.ts
292
+ import { endpointAuth, securitySchemes, type paths } from './openapi';
293
+ import { TokenClient } from '@geekmidas/auth/client';
294
+
295
+ export function createAuthAwareFetcher<Paths>(options: AuthFetcherOptions) {
296
+ const { tokenClient, awsSigner, apiKeyProvider } = options;
297
+
298
+ return async <T extends TypedEndpoint<Paths>>(
299
+ endpoint: T,
300
+ config?: FilteredRequestConfig<Paths, T>,
301
+ ) => {
302
+ const authScheme = endpointAuth[endpoint as keyof typeof endpointAuth];
303
+ let headers: Record<string, string> = {};
304
+
305
+ if (authScheme) {
306
+ const scheme = securitySchemes[authScheme];
307
+
308
+ switch (scheme.type) {
309
+ case 'http':
310
+ if (scheme.scheme === 'bearer') {
311
+ headers = await tokenClient.createValidAuthHeaders();
312
+ }
313
+ break;
314
+ case 'apiKey':
315
+ if (scheme['x-amazon-apigateway-authtype'] === 'awsSigv4') {
316
+ headers = await awsSigner.sign(endpoint, config);
317
+ } else {
318
+ headers[scheme.name] = await apiKeyProvider.getKey();
319
+ }
320
+ break;
321
+ }
322
+ }
323
+
324
+ return baseFetcher.request(endpoint, {
325
+ ...config,
326
+ headers: { ...headers, ...config?.headers },
327
+ });
328
+ };
329
+ }
330
+ ```
331
+
332
+ ## File Structure Changes
333
+
334
+ ```
335
+ packages/
336
+ ├── cli/
337
+ │ └── src/
338
+ │ ├── openapi.ts # Updated: add --ts support
339
+ │ └── generators/
340
+ │ └── OpenApiTsGenerator.ts # New: TypeScript generation
341
+ ├── schema/
342
+ │ └── src/
343
+ │ └── openapi.ts # Updated: security scheme support
344
+ └── client/
345
+ └── src/
346
+ └── auth-fetcher.ts # New: auth-aware fetcher
347
+ ```
348
+
349
+ ## Migration Path
350
+
351
+ ### Breaking Change
352
+
353
+ TypeScript is now the default output format. Existing JSON users must add `--json` flag.
354
+
355
+ ### Existing JSON Users
356
+
357
+ ```bash
358
+ # Before
359
+ gkm openapi --output ./openapi.json
360
+
361
+ # After (explicit JSON)
362
+ gkm openapi --json --output ./openapi.json
363
+ ```
364
+
365
+ ### Adopting TypeScript Output (New Default)
366
+
367
+ 1. Run `gkm openapi --output ./src/api/openapi.ts`
368
+ 2. Update imports from `./openapi-types` to `./openapi`
369
+ 3. Use `createAuthAwareFetcher` instead of `createTypedFetcher`
370
+
371
+ ## Testing Strategy
372
+
373
+ 1. **Unit Tests**
374
+ - Authorizer → security scheme mapping
375
+ - Schema extraction from StandardSchema
376
+ - TypeScript code generation
377
+
378
+ 2. **Integration Tests**
379
+ - Full endpoint → TypeScript output pipeline
380
+ - Generated code compiles without errors
381
+ - Auth map matches endpoint authorizers
382
+
383
+ 3. **E2E Tests**
384
+ - Auth-aware fetcher selects correct auth per endpoint
385
+ - Bearer endpoints get JWT headers
386
+ - IAM endpoints get SigV4 signatures
387
+ - Public endpoints get no auth headers
388
+
389
+ ## Open Questions
390
+
391
+ 1. **Schema naming conflicts** - What if two endpoints have the same operationId?
392
+ - Proposal: Append route hash suffix if conflict detected
393
+
394
+ 2. **Circular schema references** - How to handle `User { friends: User[] }`?
395
+ - Proposal: Use TypeScript `interface` declarations which support self-reference
396
+
397
+ 3. **Optional vs required auth** - Some endpoints allow optional auth (return more data if authenticated)
398
+ - Proposal: Add `optional` auth type: `'bearer' | 'bearer?' | null`
399
+
400
+ ## Timeline
401
+
402
+ | Phase | Description | Estimate |
403
+ |-------|-------------|----------|
404
+ | 1 | Schema package updates (ComponentCollector) | - |
405
+ | 2 | CLI TypeScript generator | - |
406
+ | 3 | Client auth-aware fetcher | - |
407
+ | 4 | Documentation and examples | - |
408
+ | 5 | Testing and refinement | - |
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@geekmidas/cli",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
+ "description": "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs",
4
5
  "private": false,
5
6
  "type": "module",
6
7
  "exports": {
@@ -9,6 +10,11 @@
9
10
  "import": "./dist/index.mjs",
10
11
  "require": "./dist/index.cjs"
11
12
  },
13
+ "./config": {
14
+ "types": "./dist/config.d.ts",
15
+ "import": "./dist/config.mjs",
16
+ "require": "./dist/config.cjs"
17
+ },
12
18
  "./openapi": {
13
19
  "types": "./dist/openapi.d.ts",
14
20
  "import": "./dist/openapi.mjs",
@@ -31,24 +37,33 @@
31
37
  "@apidevtools/swagger-parser": "^10.1.0",
32
38
  "chokidar": "~4.0.3",
33
39
  "commander": "^12.1.0",
40
+ "dotenv": "~17.2.3",
34
41
  "fast-glob": "^3.3.2",
35
42
  "lodash.kebabcase": "^4.1.1",
36
- "openapi-typescript": "^7.4.2"
43
+ "openapi-typescript": "^7.4.2",
44
+ "prompts": "~2.4.2"
37
45
  },
38
46
  "devDependencies": {
39
47
  "@types/lodash.kebabcase": "^4.1.9",
40
48
  "@types/node": "~24.9.1",
49
+ "@types/prompts": "~2.4.9",
41
50
  "typescript": "^5.8.2",
42
51
  "vitest": "^3.2.4",
43
52
  "zod": "~4.1.13",
44
53
  "@geekmidas/testkit": "0.0.17"
45
54
  },
46
55
  "peerDependencies": {
47
- "@geekmidas/constructs": "~0.0.22",
48
- "@geekmidas/envkit": "~0.0.8",
56
+ "@geekmidas/constructs": "~0.2.0",
57
+ "@geekmidas/envkit": "~0.1.0",
49
58
  "@geekmidas/logger": "~0.0.1",
59
+ "@geekmidas/telescope": "~0.0.1",
50
60
  "@geekmidas/schema": "~0.0.2"
51
61
  },
62
+ "peerDependenciesMeta": {
63
+ "@geekmidas/telescope": {
64
+ "optional": true
65
+ }
66
+ },
52
67
  "scripts": {
53
68
  "ts": "tsc --noEmit --skipLibCheck src/**/*.ts",
54
69
  "test": "vitest",
@@ -0,0 +1,131 @@
1
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
5
+ import { loadEnvFiles } from '../dev';
6
+
7
+ describe('loadEnvFiles', () => {
8
+ let testDir: string;
9
+ const originalEnv = { ...process.env };
10
+
11
+ beforeEach(() => {
12
+ // Create a temporary test directory
13
+ testDir = join(tmpdir(), `gkm-test-${Date.now()}`);
14
+ mkdirSync(testDir, { recursive: true });
15
+
16
+ // Reset process.env to original state
17
+ process.env = { ...originalEnv };
18
+ });
19
+
20
+ afterEach(() => {
21
+ // Clean up test directory
22
+ if (existsSync(testDir)) {
23
+ rmSync(testDir, { recursive: true, force: true });
24
+ }
25
+
26
+ // Reset process.env
27
+ process.env = originalEnv;
28
+ });
29
+
30
+ it('should load a single .env file', () => {
31
+ writeFileSync(join(testDir, '.env'), 'TEST_VAR=hello\nANOTHER_VAR=world');
32
+
33
+ const { loaded, missing } = loadEnvFiles('.env', testDir);
34
+
35
+ expect(loaded).toEqual(['.env']);
36
+ expect(missing).toEqual([]);
37
+ expect(process.env.TEST_VAR).toBe('hello');
38
+ expect(process.env.ANOTHER_VAR).toBe('world');
39
+ });
40
+
41
+ it('should load multiple env files in order', () => {
42
+ writeFileSync(join(testDir, '.env'), 'VAR1=base\nVAR2=base');
43
+ writeFileSync(join(testDir, '.env.local'), 'VAR2=local\nVAR3=local');
44
+
45
+ const { loaded, missing } = loadEnvFiles(['.env', '.env.local'], testDir);
46
+
47
+ expect(loaded).toEqual(['.env', '.env.local']);
48
+ expect(missing).toEqual([]);
49
+ expect(process.env.VAR1).toBe('base');
50
+ expect(process.env.VAR2).toBe('local'); // Overridden by .env.local
51
+ expect(process.env.VAR3).toBe('local');
52
+ });
53
+
54
+ it('should report missing files when explicitly configured', () => {
55
+ writeFileSync(join(testDir, '.env'), 'VAR=value');
56
+
57
+ const { loaded, missing } = loadEnvFiles(
58
+ ['.env', '.env.local', '.env.missing'],
59
+ testDir,
60
+ );
61
+
62
+ expect(loaded).toEqual(['.env']);
63
+ expect(missing).toEqual(['.env.local', '.env.missing']);
64
+ expect(process.env.VAR).toBe('value');
65
+ });
66
+
67
+ it('should not report missing .env when using default', () => {
68
+ // No .env file exists
69
+ const { loaded, missing } = loadEnvFiles(undefined, testDir);
70
+
71
+ expect(loaded).toEqual([]);
72
+ expect(missing).toEqual([]);
73
+ });
74
+
75
+ it('should handle undefined config with default .env', () => {
76
+ writeFileSync(join(testDir, '.env'), 'DEFAULT_VAR=default');
77
+
78
+ const { loaded, missing } = loadEnvFiles(undefined, testDir);
79
+
80
+ expect(loaded).toEqual(['.env']);
81
+ expect(missing).toEqual([]);
82
+ expect(process.env.DEFAULT_VAR).toBe('default');
83
+ });
84
+
85
+ it('should handle string config as single file', () => {
86
+ writeFileSync(join(testDir, '.env.production'), 'PROD_VAR=production');
87
+
88
+ const { loaded, missing } = loadEnvFiles('.env.production', testDir);
89
+
90
+ expect(loaded).toEqual(['.env.production']);
91
+ expect(missing).toEqual([]);
92
+ expect(process.env.PROD_VAR).toBe('production');
93
+ });
94
+
95
+ it('should override earlier files with later files', () => {
96
+ writeFileSync(join(testDir, '.env'), 'SHARED=first\nONLY_FIRST=yes');
97
+ writeFileSync(join(testDir, '.env.local'), 'SHARED=second');
98
+ writeFileSync(join(testDir, '.env.dev'), 'SHARED=third\nONLY_DEV=yes');
99
+
100
+ const { loaded } = loadEnvFiles(
101
+ ['.env', '.env.local', '.env.dev'],
102
+ testDir,
103
+ );
104
+
105
+ expect(loaded).toEqual(['.env', '.env.local', '.env.dev']);
106
+ expect(process.env.SHARED).toBe('third');
107
+ expect(process.env.ONLY_FIRST).toBe('yes');
108
+ expect(process.env.ONLY_DEV).toBe('yes');
109
+ });
110
+
111
+ it('should handle empty env files', () => {
112
+ writeFileSync(join(testDir, '.env'), '');
113
+
114
+ const { loaded, missing } = loadEnvFiles('.env', testDir);
115
+
116
+ expect(loaded).toEqual(['.env']);
117
+ expect(missing).toEqual([]);
118
+ });
119
+
120
+ it('should handle env files with comments', () => {
121
+ writeFileSync(
122
+ join(testDir, '.env'),
123
+ '# This is a comment\nVAR=value\n# Another comment',
124
+ );
125
+
126
+ const { loaded } = loadEnvFiles('.env', testDir);
127
+
128
+ expect(loaded).toEqual(['.env']);
129
+ expect(process.env.VAR).toBe('value');
130
+ });
131
+ });