@geekmidas/cli 10.0.0-alpha.6 → 10.0.0-alpha.60

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 (303) hide show
  1. package/README.md +225 -268
  2. package/bin/adjacent-tsconfig.mjs +97 -0
  3. package/bin/gkm.mjs +16 -2
  4. package/bin/owning-tsconfig-jsx-register.mjs +18 -0
  5. package/bin/owning-tsconfig-jsx.mjs +108 -0
  6. package/dist/{HostingerProvider-h3EbEp-Z.cjs → HostingerProvider-BTZd2u0f.cjs} +21 -22
  7. package/dist/HostingerProvider-BTZd2u0f.cjs.map +1 -0
  8. package/dist/{HostingerProvider-Cft7lEYr.mjs → HostingerProvider-DgzKa_Tj.mjs} +20 -21
  9. package/dist/HostingerProvider-DgzKa_Tj.mjs.map +1 -0
  10. package/dist/LegacyStateStore-CToa2hga.mjs +89 -0
  11. package/dist/LegacyStateStore-CToa2hga.mjs.map +1 -0
  12. package/dist/LegacyStateStore-CidlPT0N.cjs +89 -0
  13. package/dist/LegacyStateStore-CidlPT0N.cjs.map +1 -0
  14. package/dist/LocalStateStore-BFLiOocU.mjs +165 -0
  15. package/dist/LocalStateStore-BFLiOocU.mjs.map +1 -0
  16. package/dist/LocalStateStore-Dpuqka1N.cjs +176 -0
  17. package/dist/LocalStateStore-Dpuqka1N.cjs.map +1 -0
  18. package/dist/{openapi-D-dx9Akj.cjs → OpenApiTsGenerator-BTuUumsV.cjs} +330 -469
  19. package/dist/OpenApiTsGenerator-BTuUumsV.cjs.map +1 -0
  20. package/dist/{openapi-Bw8DWFWz.mjs → OpenApiTsGenerator-CvVr1Ilz.mjs} +295 -403
  21. package/dist/OpenApiTsGenerator-CvVr1Ilz.mjs.map +1 -0
  22. package/dist/{Route53Provider-RdpUDJQk.mjs → Route53Provider-Cvj40A8I.mjs} +12 -12
  23. package/dist/Route53Provider-Cvj40A8I.mjs.map +1 -0
  24. package/dist/{Route53Provider-di2fMM5N.cjs → Route53Provider-DVILBsvV.cjs} +20 -21
  25. package/dist/Route53Provider-DVILBsvV.cjs.map +1 -0
  26. package/dist/S3StateStore-aaGkjcxO.mjs +161 -0
  27. package/dist/S3StateStore-aaGkjcxO.mjs.map +1 -0
  28. package/dist/S3StateStore-cnm813Xn.cjs +160 -0
  29. package/dist/S3StateStore-cnm813Xn.cjs.map +1 -0
  30. package/dist/SSMStateStore-BllaJKGo.mjs +154 -0
  31. package/dist/SSMStateStore-BllaJKGo.mjs.map +1 -0
  32. package/dist/SSMStateStore-DwaGprVX.cjs +153 -0
  33. package/dist/SSMStateStore-DwaGprVX.cjs.map +1 -0
  34. package/dist/appPorts-C9z9Pzn9.mjs +223 -0
  35. package/dist/appPorts-C9z9Pzn9.mjs.map +1 -0
  36. package/dist/appPorts-Cu79hJF6.cjs +264 -0
  37. package/dist/appPorts-Cu79hJF6.cjs.map +1 -0
  38. package/dist/aws-DkHdTV-p.cjs +61 -0
  39. package/dist/aws-DkHdTV-p.cjs.map +1 -0
  40. package/dist/aws-DojzQnLd.mjs +61 -0
  41. package/dist/aws-DojzQnLd.mjs.map +1 -0
  42. package/dist/{backup-provisioner-BrsHwYG0.cjs → backup-provisioner-BmhIO_Yr.cjs} +21 -19
  43. package/dist/backup-provisioner-BmhIO_Yr.cjs.map +1 -0
  44. package/dist/{backup-provisioner-Bgj5Vi_L.mjs → backup-provisioner-DIVCbfee.mjs} +10 -7
  45. package/dist/backup-provisioner-DIVCbfee.mjs.map +1 -0
  46. package/dist/{bundler-BY1OmV1e.cjs → bundler-CzXZPSBJ.cjs} +20 -32
  47. package/dist/bundler-CzXZPSBJ.cjs.map +1 -0
  48. package/dist/{bundler-Be8Zg4Wd.mjs → bundler-DJVR4QlW.mjs} +18 -29
  49. package/dist/bundler-DJVR4QlW.mjs.map +1 -0
  50. package/dist/cli-D_qTl20Z.cjs +69 -0
  51. package/dist/cli-D_qTl20Z.cjs.map +1 -0
  52. package/dist/cli-DzR1hpum.mjs +69 -0
  53. package/dist/cli-DzR1hpum.mjs.map +1 -0
  54. package/dist/{config-CxP7P-qG.mjs → config-C5-EEdmr.mjs} +144 -127
  55. package/dist/config-C5-EEdmr.mjs.map +1 -0
  56. package/dist/{config-CNz5KaSR.cjs → config-DXnkCEq0.cjs} +212 -184
  57. package/dist/config-DXnkCEq0.cjs.map +1 -0
  58. package/dist/config.cjs +9 -6
  59. package/dist/config.d.cts +124 -33
  60. package/dist/config.d.cts.map +1 -1
  61. package/dist/config.d.mts +124 -33
  62. package/dist/config.d.mts.map +1 -1
  63. package/dist/config.mjs +3 -5
  64. package/dist/deploy/public.cjs +22 -0
  65. package/dist/deploy/public.d.cts +459 -0
  66. package/dist/deploy/public.d.cts.map +1 -0
  67. package/dist/deploy/public.d.mts +459 -0
  68. package/dist/deploy/public.d.mts.map +1 -0
  69. package/dist/deploy/public.mjs +5 -0
  70. package/dist/deploy/sniffer-envkit-patch.cjs +18 -10
  71. package/dist/deploy/sniffer-envkit-patch.cjs.map +1 -1
  72. package/dist/deploy/sniffer-envkit-patch.d.cts +0 -2
  73. package/dist/deploy/sniffer-envkit-patch.d.cts.map +1 -1
  74. package/dist/deploy/sniffer-envkit-patch.d.mts +0 -2
  75. package/dist/deploy/sniffer-envkit-patch.d.mts.map +1 -1
  76. package/dist/deploy/sniffer-envkit-patch.mjs +10 -2
  77. package/dist/deploy/sniffer-envkit-patch.mjs.map +1 -1
  78. package/dist/deploy/sniffer-hooks.cjs +13 -7
  79. package/dist/deploy/sniffer-hooks.cjs.map +1 -1
  80. package/dist/deploy/sniffer-hooks.d.cts +1 -2
  81. package/dist/deploy/sniffer-hooks.d.cts.map +1 -1
  82. package/dist/deploy/sniffer-hooks.d.mts +1 -2
  83. package/dist/deploy/sniffer-hooks.d.mts.map +1 -1
  84. package/dist/deploy/sniffer-hooks.mjs +10 -3
  85. package/dist/deploy/sniffer-hooks.mjs.map +1 -1
  86. package/dist/deploy/sniffer-loader.cjs +16 -7
  87. package/dist/deploy/sniffer-loader.cjs.map +1 -1
  88. package/dist/deploy/sniffer-loader.d.cts +1 -1
  89. package/dist/deploy/sniffer-loader.d.mts +1 -1
  90. package/dist/deploy/sniffer-loader.mjs +14 -3
  91. package/dist/deploy/sniffer-loader.mjs.map +1 -1
  92. package/dist/deploy/sniffer-routes-worker.cjs +11 -11
  93. package/dist/deploy/sniffer-routes-worker.cjs.map +1 -1
  94. package/dist/deploy/sniffer-routes-worker.d.cts +1 -1
  95. package/dist/deploy/sniffer-routes-worker.d.mts +1 -1
  96. package/dist/deploy/sniffer-routes-worker.mjs +3 -3
  97. package/dist/deploy/sniffer-routes-worker.mjs.map +1 -1
  98. package/dist/deploy/sniffer-worker.cjs +15 -6
  99. package/dist/deploy/sniffer-worker.cjs.map +1 -1
  100. package/dist/deploy/sniffer-worker.d.cts +1 -3
  101. package/dist/deploy/sniffer-worker.d.cts.map +1 -1
  102. package/dist/deploy/sniffer-worker.d.mts +1 -3
  103. package/dist/deploy/sniffer-worker.d.mts.map +1 -1
  104. package/dist/deploy/sniffer-worker.mjs +15 -4
  105. package/dist/deploy/sniffer-worker.mjs.map +1 -1
  106. package/dist/deploy-0fBVeWO-.cjs +3518 -0
  107. package/dist/deploy-0fBVeWO-.cjs.map +1 -0
  108. package/dist/deploy-D6Q09OkA.mjs +3429 -0
  109. package/dist/deploy-D6Q09OkA.mjs.map +1 -0
  110. package/dist/{discover-DTmFeloL.cjs → discover-Bc-4eA_p.cjs} +131 -103
  111. package/dist/discover-Bc-4eA_p.cjs.map +1 -0
  112. package/dist/{discover-CAlFgYec.mjs → discover-FrYvzkeC.mjs} +91 -58
  113. package/dist/discover-FrYvzkeC.mjs.map +1 -0
  114. package/dist/{emit-CiZS_5Ub.mjs → emit-C1e8qD08.mjs} +13 -26
  115. package/dist/emit-C1e8qD08.mjs.map +1 -0
  116. package/dist/{emit-CbcRolsa.cjs → emit-Dmc1nvdS.cjs} +31 -57
  117. package/dist/emit-Dmc1nvdS.cjs.map +1 -0
  118. package/dist/encryption-CML4UaPi.cjs +91 -0
  119. package/dist/encryption-CML4UaPi.cjs.map +1 -0
  120. package/dist/encryption-aDA2AzVx.mjs +90 -0
  121. package/dist/encryption-aDA2AzVx.mjs.map +1 -0
  122. package/dist/fakes-C9RiT6XG.mjs +125 -0
  123. package/dist/fakes-C9RiT6XG.mjs.map +1 -0
  124. package/dist/fakes-zW-jNAbQ.cjs +148 -0
  125. package/dist/fakes-zW-jNAbQ.cjs.map +1 -0
  126. package/dist/github-Cq57poPj.cjs +213 -0
  127. package/dist/github-Cq57poPj.cjs.map +1 -0
  128. package/dist/github-DmCj2u95.mjs +213 -0
  129. package/dist/github-DmCj2u95.mjs.map +1 -0
  130. package/dist/home-DOzBaWaL.cjs +33 -0
  131. package/dist/home-DOzBaWaL.cjs.map +1 -0
  132. package/dist/home-GI7pJl1M.mjs +22 -0
  133. package/dist/home-GI7pJl1M.mjs.map +1 -0
  134. package/dist/{index-DEbaleny.d.mts → index-Da1-oa9W.d.mts} +4 -23
  135. package/dist/index-Da1-oa9W.d.mts.map +1 -0
  136. package/dist/{index-BGGdjueF.d.cts → index-UiWCaGzU.d.cts} +4 -23
  137. package/dist/index-UiWCaGzU.d.cts.map +1 -0
  138. package/dist/index.cjs +9096 -12213
  139. package/dist/index.cjs.map +1 -1
  140. package/dist/index.d.cts +1 -1
  141. package/dist/index.d.mts +1 -1
  142. package/dist/index.mjs +9067 -12192
  143. package/dist/index.mjs.map +1 -1
  144. package/dist/migrate-BTqIflwc.cjs +743 -0
  145. package/dist/migrate-BTqIflwc.cjs.map +1 -0
  146. package/dist/migrate-B_6KofUo.mjs +736 -0
  147. package/dist/migrate-B_6KofUo.mjs.map +1 -0
  148. package/dist/openapi.cjs +198 -10
  149. package/dist/openapi.cjs.map +1 -0
  150. package/dist/openapi.d.cts +21 -19
  151. package/dist/openapi.d.cts.map +1 -1
  152. package/dist/openapi.d.mts +21 -19
  153. package/dist/openapi.d.mts.map +1 -1
  154. package/dist/openapi.mjs +193 -5
  155. package/dist/openapi.mjs.map +1 -0
  156. package/dist/ready-CCLIAkwR.cjs +807 -0
  157. package/dist/ready-CCLIAkwR.cjs.map +1 -0
  158. package/dist/ready-CIHpnUc5.mjs +760 -0
  159. package/dist/ready-CIHpnUc5.mjs.map +1 -0
  160. package/dist/reconcile/public.cjs +3 -5
  161. package/dist/reconcile/public.d.cts +30 -33
  162. package/dist/reconcile/public.d.cts.map +1 -1
  163. package/dist/reconcile/public.d.mts +30 -33
  164. package/dist/reconcile/public.d.mts.map +1 -1
  165. package/dist/reconcile/public.mjs +3 -4
  166. package/dist/{reconcile-CFs8C_1a.mjs → reconcile--I7V4Klj.mjs} +4 -7
  167. package/dist/reconcile--I7V4Klj.mjs.map +1 -0
  168. package/dist/{reconcile-D0pimNiy.cjs → reconcile-CuCQBa5-.cjs} +5 -8
  169. package/dist/reconcile-CuCQBa5-.cjs.map +1 -0
  170. package/dist/rolldown-runtime-BhDjJH2R.mjs +15 -0
  171. package/dist/{chunk-CUT6urMc.cjs → rolldown-runtime-C0BPl7ul.cjs} +23 -10
  172. package/dist/sniffer-BM8fpCas.mjs +6321 -0
  173. package/dist/sniffer-BM8fpCas.mjs.map +1 -0
  174. package/dist/sniffer-DIv12ncU.cjs +6707 -0
  175. package/dist/sniffer-DIv12ncU.cjs.map +1 -0
  176. package/dist/storage-BzaUxmqe.mjs +124 -0
  177. package/dist/storage-BzaUxmqe.mjs.map +1 -0
  178. package/dist/storage-CSGoC3bK.cjs +152 -0
  179. package/dist/storage-CSGoC3bK.cjs.map +1 -0
  180. package/dist/store-BS8A7NcA.mjs +415 -0
  181. package/dist/store-BS8A7NcA.mjs.map +1 -0
  182. package/dist/store-KvNQwYaB.cjs +510 -0
  183. package/dist/store-KvNQwYaB.cjs.map +1 -0
  184. package/dist/types-Bp1mVxG0.d.mts +6410 -0
  185. package/dist/types-Bp1mVxG0.d.mts.map +1 -0
  186. package/dist/types-efNLu9xu.d.cts +6411 -0
  187. package/dist/types-efNLu9xu.d.cts.map +1 -0
  188. package/dist/vitest.cjs +65 -0
  189. package/dist/vitest.cjs.map +1 -0
  190. package/dist/vitest.d.cts +29 -0
  191. package/dist/vitest.d.cts.map +1 -0
  192. package/dist/vitest.d.mts +29 -0
  193. package/dist/vitest.d.mts.map +1 -0
  194. package/dist/vitest.mjs +65 -0
  195. package/dist/vitest.mjs.map +1 -0
  196. package/dist/workspace/index.cjs +3 -4
  197. package/dist/workspace/index.d.cts +3 -3
  198. package/dist/workspace/index.d.mts +3 -3
  199. package/dist/workspace/index.mjs +2 -3
  200. package/dist/{workspace-LKiNyaDy.cjs → workspace-B64Xk1Mb.cjs} +1070 -1118
  201. package/dist/workspace-B64Xk1Mb.cjs.map +1 -0
  202. package/dist/{workspace-DIuZx-cc.mjs → workspace-Bp1xVaOz.mjs} +901 -972
  203. package/dist/workspace-Bp1xVaOz.mjs.map +1 -0
  204. package/package.json +38 -20
  205. package/dist/CachedStateProvider-BLo7zv7S.cjs +0 -3
  206. package/dist/CachedStateProvider-BxoAm-B2.mjs +0 -3
  207. package/dist/CachedStateProvider-CqPdW7CB.cjs +0 -60
  208. package/dist/CachedStateProvider-CqPdW7CB.cjs.map +0 -1
  209. package/dist/CachedStateProvider-DTniehMP.mjs +0 -54
  210. package/dist/CachedStateProvider-DTniehMP.mjs.map +0 -1
  211. package/dist/HostingerProvider-Cft7lEYr.mjs.map +0 -1
  212. package/dist/HostingerProvider-h3EbEp-Z.cjs.map +0 -1
  213. package/dist/LocalStateProvider-Bn6tS-oL.mjs +0 -42
  214. package/dist/LocalStateProvider-Bn6tS-oL.mjs.map +0 -1
  215. package/dist/LocalStateProvider-Db9SEjyH.cjs +0 -43
  216. package/dist/LocalStateProvider-Db9SEjyH.cjs.map +0 -1
  217. package/dist/Route53Provider-RdpUDJQk.mjs.map +0 -1
  218. package/dist/Route53Provider-di2fMM5N.cjs.map +0 -1
  219. package/dist/SSMStateProvider-BRt2py-A.mjs +0 -66
  220. package/dist/SSMStateProvider-BRt2py-A.mjs.map +0 -1
  221. package/dist/SSMStateProvider-DyX8s9HI.cjs +0 -66
  222. package/dist/SSMStateProvider-DyX8s9HI.cjs.map +0 -1
  223. package/dist/backup-provisioner-Bgj5Vi_L.mjs.map +0 -1
  224. package/dist/backup-provisioner-BrsHwYG0.cjs.map +0 -1
  225. package/dist/bundler-BY1OmV1e.cjs.map +0 -1
  226. package/dist/bundler-Be8Zg4Wd.mjs.map +0 -1
  227. package/dist/chunk-Duj1WY3L.mjs +0 -7
  228. package/dist/config-CNz5KaSR.cjs.map +0 -1
  229. package/dist/config-CxP7P-qG.mjs.map +0 -1
  230. package/dist/credentials-B5zRHdAl.mjs +0 -141
  231. package/dist/credentials-B5zRHdAl.mjs.map +0 -1
  232. package/dist/credentials-BK3Z93MX.mjs +0 -2548
  233. package/dist/credentials-BK3Z93MX.mjs.map +0 -1
  234. package/dist/credentials-BapzjyV5.cjs +0 -195
  235. package/dist/credentials-BapzjyV5.cjs.map +0 -1
  236. package/dist/credentials-DzsXZzkx.cjs +0 -2680
  237. package/dist/credentials-DzsXZzkx.cjs.map +0 -1
  238. package/dist/discover-CAlFgYec.mjs.map +0 -1
  239. package/dist/discover-DTmFeloL.cjs.map +0 -1
  240. package/dist/dokploy-api-BS2xnJLZ.mjs +0 -3
  241. package/dist/dokploy-api-BsVH0y_s.cjs +0 -718
  242. package/dist/dokploy-api-BsVH0y_s.cjs.map +0 -1
  243. package/dist/dokploy-api-BuxDRo3l.cjs +0 -3
  244. package/dist/dokploy-api-aTFPz7_4.mjs +0 -706
  245. package/dist/dokploy-api-aTFPz7_4.mjs.map +0 -1
  246. package/dist/emit-CbcRolsa.cjs.map +0 -1
  247. package/dist/emit-CiZS_5Ub.mjs.map +0 -1
  248. package/dist/encryption-C6jaU0hE.cjs +0 -54
  249. package/dist/encryption-C6jaU0hE.cjs.map +0 -1
  250. package/dist/encryption-CHzzeR8a.cjs +0 -4
  251. package/dist/encryption-CQUncu7v.mjs +0 -3
  252. package/dist/encryption-D-abyhZx.mjs +0 -42
  253. package/dist/encryption-D-abyhZx.mjs.map +0 -1
  254. package/dist/fullstack-secrets-C-GGohke.cjs +0 -111
  255. package/dist/fullstack-secrets-C-GGohke.cjs.map +0 -1
  256. package/dist/fullstack-secrets-D1o9-G5l.mjs +0 -87
  257. package/dist/fullstack-secrets-D1o9-G5l.mjs.map +0 -1
  258. package/dist/generator-CNzBw7g5.cjs +0 -289
  259. package/dist/generator-CNzBw7g5.cjs.map +0 -1
  260. package/dist/generator-VE2JrFlY.mjs +0 -253
  261. package/dist/generator-VE2JrFlY.mjs.map +0 -1
  262. package/dist/index-BGGdjueF.d.cts.map +0 -1
  263. package/dist/index-DEbaleny.d.mts.map +0 -1
  264. package/dist/openapi-Bw8DWFWz.mjs.map +0 -1
  265. package/dist/openapi-D-dx9Akj.cjs.map +0 -1
  266. package/dist/openapi-react-query-Byltvm_7.mjs +0 -167
  267. package/dist/openapi-react-query-Byltvm_7.mjs.map +0 -1
  268. package/dist/openapi-react-query-CmJ1xDbn.cjs +0 -173
  269. package/dist/openapi-react-query-CmJ1xDbn.cjs.map +0 -1
  270. package/dist/openapi-react-query.cjs +0 -4
  271. package/dist/openapi-react-query.d.cts +0 -11
  272. package/dist/openapi-react-query.d.cts.map +0 -1
  273. package/dist/openapi-react-query.d.mts +0 -11
  274. package/dist/openapi-react-query.d.mts.map +0 -1
  275. package/dist/openapi-react-query.mjs +0 -4
  276. package/dist/reconcile-CFs8C_1a.mjs.map +0 -1
  277. package/dist/reconcile-D0pimNiy.cjs.map +0 -1
  278. package/dist/setup-8IWPsRIN.cjs +0 -11
  279. package/dist/setup-BWMhbW-p.mjs +0 -443
  280. package/dist/setup-BWMhbW-p.mjs.map +0 -1
  281. package/dist/setup-D7L4BgBT.cjs +0 -473
  282. package/dist/setup-D7L4BgBT.cjs.map +0 -1
  283. package/dist/setup-DGIPd2fh.mjs +0 -11
  284. package/dist/storage-BJmzYxt8.cjs +0 -363
  285. package/dist/storage-BJmzYxt8.cjs.map +0 -1
  286. package/dist/storage-BM9hGpgv.cjs +0 -7
  287. package/dist/storage-Bi24WfoW.mjs +0 -297
  288. package/dist/storage-Bi24WfoW.mjs.map +0 -1
  289. package/dist/storage-CYlGwWTj.mjs +0 -3
  290. package/dist/sync-CmEuNZsB.mjs +0 -76
  291. package/dist/sync-CmEuNZsB.mjs.map +0 -1
  292. package/dist/sync-D306yOWu.cjs +0 -4
  293. package/dist/sync-SizaChmC.cjs +0 -93
  294. package/dist/sync-SizaChmC.cjs.map +0 -1
  295. package/dist/sync-hOqAWHR6.mjs +0 -3
  296. package/dist/types-BLSNdrk0.d.cts +0 -2176
  297. package/dist/types-BLSNdrk0.d.cts.map +0 -1
  298. package/dist/types-C3POXhw3.d.mts +0 -2176
  299. package/dist/types-C3POXhw3.d.mts.map +0 -1
  300. package/dist/workspace-CGaUAw5A.cjs +0 -9
  301. package/dist/workspace-CnTqIlB3.mjs +0 -8
  302. package/dist/workspace-DIuZx-cc.mjs.map +0 -1
  303. package/dist/workspace-LKiNyaDy.cjs.map +0 -1
package/README.md CHANGED
@@ -7,7 +7,7 @@ A powerful CLI tool for building and managing TypeScript-based backend APIs with
7
7
  - **Project Scaffolding**: Interactive `init` command to bootstrap new projects with templates
8
8
  - **Multi-Provider Support**: Generate handlers for AWS Lambda (API Gateway v1/v2) and server applications
9
9
  - **Development Server**: Hot-reload development server with file watching
10
- - **Telescope Integration**: Laravel-style debugging dashboard for inspecting requests, logs, and exceptions
10
+ - **Telescope Integration**: Laravel-style recording of requests, logs and exceptions, served as JSON
11
11
  - **OpenAPI Generation**: Auto-generate OpenAPI 3.0 specifications from your endpoints
12
12
  - **Docker Support**: Generate optimized Dockerfiles with multi-stage builds, turbo prune for monorepos
13
13
  - **Secrets Management**: Secure credential generation, encryption, and stage-based secrets storage
@@ -54,6 +54,7 @@ Create a `gkm.config.ts` file in your project root:
54
54
  import { defineConfig } from '@geekmidas/cli/config';
55
55
 
56
56
  export default defineConfig({
57
+ stages: { local: 'dev', deployed: ['prod'] },
57
58
  // Glob pattern to find endpoint files
58
59
  routes: 'src/routes/**/*.ts',
59
60
 
@@ -72,7 +73,7 @@ export default defineConfig({
72
73
  // Logger configuration
73
74
  logger: './src/logger.ts#logger',
74
75
 
75
- // Optional: Telescope debugging dashboard (enabled by default in dev)
76
+ // Optional: Telescope request recording (enabled by default in dev)
76
77
  telescope: {
77
78
  enabled: true,
78
79
  path: '/__telescope',
@@ -142,36 +143,32 @@ export const createUser = api
142
143
 
143
144
  ### 5. Create Subscribers (Optional)
144
145
 
145
- Create event subscribers in `src/subscribers/`:
146
+ Declare the topic once, and a worker to run what has no port; a subscriber is
147
+ built from the worker and binds to the topic:
146
148
 
147
149
  ```typescript
150
+ // src/constructs/topics.ts
151
+ import { Topic } from '@geekmidas/constructs/topic';
152
+ import { z } from 'zod';
153
+
154
+ export const users = new Topic('Users', {
155
+ events: {
156
+ 'user.created': z.object({ userId: z.string(), email: z.email() }),
157
+ 'user.updated': z.object({ userId: z.string() }),
158
+ },
159
+ });
160
+
161
+ // src/constructs/worker.ts
162
+ import { Worker } from '@geekmidas/constructs/worker';
163
+
164
+ export const worker = new Worker('Jobs', { logger });
165
+
148
166
  // src/subscribers/userSubscriber.ts
149
- import { SubscriberBuilder } from '@geekmidas/constructs/subscribers';
150
- import type { Service } from '@geekmidas/services';
151
- import type { EventPublisher, PublishableMessage } from '@geekmidas/events';
152
- import type { EnvironmentParser } from '@geekmidas/envkit';
153
-
154
- // Define event types
155
- type UserEvents =
156
- | PublishableMessage<'user.created', { userId: string; email: string }>
157
- | PublishableMessage<'user.updated', { userId: string }>;
158
-
159
- // Create event publisher service
160
- const userEventPublisher = {
161
- serviceName: 'userEventPublisher' as const,
162
- async register(envParser: EnvironmentParser<{}>) {
163
- const config = envParser.create((get) => ({
164
- publisherUrl: get('EVENT_PUBLISHER_URL').string()
165
- })).parse();
166
-
167
- const { Publisher } = await import('@geekmidas/events');
168
- return Publisher.fromConnectionString<UserEvents>(config.publisherUrl);
169
- }
170
- } satisfies Service<'userEventPublisher', EventPublisher<UserEvents>>;
167
+ import { users } from '../constructs/topics';
168
+ import { worker } from '../constructs/worker';
171
169
 
172
- // Create subscriber
173
- export const userCreatedSubscriber = new SubscriberBuilder()
174
- .publisher(userEventPublisher)
170
+ export const userCreatedSubscriber = worker
171
+ .topic(users)
175
172
  .subscribe(['user.created'])
176
173
  .handle(async ({ events, logger }) => {
177
174
  for (const event of events) {
@@ -181,11 +178,16 @@ export const userCreatedSubscriber = new SubscriberBuilder()
181
178
  });
182
179
  ```
183
180
 
181
+ An endpoint publishes to the topic with `.event(users, { type: 'user.created',
182
+ payload: (user) => ({ userId: user.id, email: user.email }) })`. Declaring the
183
+ topic is what puts a broker in the local plan and resolves
184
+ `USERS_PUBLISHER_CONNECTION_STRING` for whatever publishes to it.
185
+
184
186
  ### 6. Build Handlers
185
187
 
186
188
  ```bash
187
- # Generate AWS Lambda handlers
188
- npx gkm build --provider aws-apigatewayv1
189
+ # Build for where gkm.config.ts deploys (AWS for `deploy: { default: 'sst' }`)
190
+ npx gkm build
189
191
 
190
192
  # Generate server application
191
193
  npx gkm build --provider server
@@ -224,7 +226,7 @@ When run without `--yes`, the command will ask:
224
226
  - `api` - Full API with auth, database, services
225
227
  - `serverless` - AWS Lambda handlers
226
228
  - `worker` - Background job processing
227
- 3. **Telescope** - Include debugging dashboard (default: yes)
229
+ 3. **Telescope** - Include request, exception and log recording (default: yes)
228
230
  4. **Database** - Include Kysely database support (default: yes)
229
231
  5. **Logger** - Choose logger implementation:
230
232
  - `pino` - Fast JSON logger for production (recommended)
@@ -309,17 +311,20 @@ Generate handlers from your endpoints.
309
311
  gkm build [options]
310
312
  ```
311
313
 
314
+ By default it builds for where the project deploys: `deploy: { default: 'sst' }`
315
+ builds for AWS, one Lambda per construct; `dokploy` (the default when nothing
316
+ is declared) builds a server.
317
+
312
318
  **Options:**
313
- - `--provider <provider>`: Target provider (default: `aws-apigatewayv1`)
314
- - `aws-apigatewayv1`: AWS API Gateway v1 Lambda handlers
315
- - `aws-apigatewayv2`: AWS API Gateway v2 Lambda handlers
319
+ - `--provider <provider>`: Override the deploy target
320
+ - `aws`: One Lambda handler per construct (API Gateway v2 for endpoints)
316
321
  - `server`: Server application with Hono
317
322
  - `--production`: Generate production-optimized bundle (server provider only)
318
323
 
319
324
  **Example:**
320
325
  ```bash
321
- # Generate AWS Lambda handlers
322
- gkm build --provider aws-apigatewayv1
326
+ # Build for where gkm.config.ts deploys
327
+ gkm build
323
328
 
324
329
  # Generate server application
325
330
  gkm build --provider server
@@ -443,7 +448,7 @@ gkm docker [options]
443
448
 
444
449
  **Generated Files:**
445
450
  - `.gkm/docker/Dockerfile` - Multi-stage or slim Dockerfile
446
- - `.gkm/docker/docker-compose.yml` - Docker Compose configuration
451
+ - `docker-compose.constructs.yml` - the containers the constructs imply, and the app (project root)
447
452
  - `.gkm/docker/docker-entrypoint.sh` - Entrypoint script
448
453
  - `.dockerignore` - Docker ignore file (project root)
449
454
 
@@ -490,6 +495,7 @@ Configure Docker settings in `gkm.config.ts`:
490
495
  import { defineConfig } from '@geekmidas/cli/config';
491
496
 
492
497
  export default defineConfig({
498
+ stages: { local: 'dev', deployed: ['prod'] },
493
499
  routes: 'src/routes/**/*.ts',
494
500
  envParser: './src/env.ts',
495
501
  logger: './src/logger.ts',
@@ -503,38 +509,20 @@ export default defineConfig({
503
509
  baseImage: 'node:22-alpine',
504
510
  // Port to expose (default: 3000)
505
511
  port: 3000,
506
- // Docker Compose services
507
- compose: {
508
- services: {
509
- postgres: { image: 'postgis/postgis:16-3.4-alpine' }, // Custom image
510
- redis: true, // Default version
511
- rabbitmq: { version: '3.12-management-alpine' }, // Custom version
512
- },
513
- },
514
512
  },
515
513
  });
516
514
  ```
517
515
 
518
- **Docker Compose Services:**
519
-
520
- Services can be configured with custom versions, custom images, or use defaults:
521
-
522
- ```typescript
523
- // Object format (recommended)
524
- services: {
525
- postgres: { version: '15-alpine' }, // Custom version
526
- redis: true, // Default: redis:7-alpine
527
- rabbitmq: { version: '3.12-management-alpine' },
528
- }
529
-
530
- // Custom images (e.g., PostGIS, Redis Stack)
531
- services: {
532
- postgres: { image: 'postgis/postgis:16-3.4-alpine' }, // Full image reference
533
- redis: { image: 'redis/redis-stack:latest' },
534
- }
516
+ **Compose:** there is no `compose` option. `gkm docker` writes
517
+ `docker-compose.constructs.yml` at the project root from the declared
518
+ constructs; an image pin or an extra service goes in your own
519
+ `docker-compose.yml`, which is merged over it:
535
520
 
536
- // Legacy array format - uses default versions
537
- services: ['postgres', 'redis', 'rabbitmq']
521
+ ```yaml
522
+ # docker-compose.yml
523
+ services:
524
+ postgres:
525
+ image: postgis/postgis:16-3.4-alpine
538
526
  ```
539
527
 
540
528
  **Service Configuration Options:**
@@ -557,7 +545,7 @@ services: ['postgres', 'redis', 'rabbitmq']
557
545
 
558
546
  ### `gkm dev`
559
547
 
560
- Start a development server with hot-reload and optional Telescope debugging dashboard.
548
+ Start a development server with hot-reload and optional Telescope recording.
561
549
 
562
550
  ```bash
563
551
  gkm dev [options]
@@ -569,7 +557,8 @@ gkm dev [options]
569
557
  **Features:**
570
558
  - Hot-reload on file changes (endpoints, functions, crons, subscribers)
571
559
  - Automatic port switching if requested port is in use
572
- - Telescope debugging dashboard (enabled by default)
560
+ - Telescope JSON API at `/__telescope/api` (enabled by default)
561
+ - The declared database, read-only, as JSON at `/__gkm/db`
573
562
  - Real-time WebSocket updates in Telescope
574
563
 
575
564
  **Example:**
@@ -682,8 +671,8 @@ gkm secrets:init --stage production --force
682
671
  ```
683
672
 
684
673
  **Generated:**
685
- - Secure passwords for postgres, redis, rabbitmq (based on `docker.compose.services` config)
686
- - Connection URLs (`DATABASE_URL`, `REDIS_URL`, `RABBITMQ_URL`)
674
+ - Custom secrets for the stage; the containers' credentials are provisioned by
675
+ reconcile from the declared constructs, not stored here
687
676
  - Stored in `.gkm/secrets/<stage>.json` (gitignored)
688
677
 
689
678
  ### `gkm secrets:set`
@@ -797,7 +786,7 @@ Store credentials locally to avoid setting environment variables for every comma
797
786
 
798
787
  ### `gkm login`
799
788
 
800
- Authenticate with a deployment service. Credentials are stored in `~/.gkm/credentials.json`.
789
+ Authenticate with a deployment service. Credentials are stored in `~/.gkm/credentials.json` (`$GKM_HOME/credentials.json` when `GKM_HOME` is set).
801
790
 
802
791
  ```bash
803
792
  gkm login [options]
@@ -883,6 +872,20 @@ gkm deploy --provider <provider> --stage <stage> [options]
883
872
  - `--tag <tag>`: Image tag (default: `stage-timestamp`)
884
873
  - `--skip-push`: Skip pushing image to registry
885
874
  - `--skip-build`: Skip build step (use existing build)
875
+ - `--json`: Write the deploy's events as JSON lines on stdout instead of progress; never prompts
876
+ - `--dry-run`: Show what would be created or reused, and change, build and push nothing
877
+
878
+ At a terminal a missing Dokploy or registry login is asked for (and the Dokploy one stored). With `--json` or without a terminal nothing is asked: the deploy stops with `MissingCredential` and exits 1.
879
+
880
+ **From a program:** the same deploy is `deploy()` from `@geekmidas/cli/deploy` — explicit `cwd`, an injected `CredentialProvider`, a logger and an `AbortSignal`; it returns a run you iterate for JSON events, plus a `result` promise. It never prompts, prints or exits.
881
+
882
+ ```typescript
883
+ import { deploy } from '@geekmidas/cli/deploy';
884
+
885
+ const run = deploy({ cwd: '/srv/shop', stage: 'production', signal });
886
+ for await (const event of run) console.log(event.type);
887
+ const result = await run.result;
888
+ ```
886
889
 
887
890
  **Examples:**
888
891
  ```bash
@@ -929,6 +932,7 @@ This allows the CLI to validate that all required secrets are configured before
929
932
  ```typescript
930
933
  // gkm.config.ts
931
934
  export default defineConfig({
935
+ stages: { local: 'dev', deployed: ['prod'] },
932
936
  routes: 'src/endpoints/**/*.ts',
933
937
  envParser: './src/env.ts',
934
938
  logger: './src/logger.ts',
@@ -937,25 +941,19 @@ export default defineConfig({
937
941
  registry: 'ghcr.io/myorg',
938
942
  imageName: 'my-api',
939
943
  },
940
-
941
- // For Dokploy deployments
942
- providers: {
943
- dokploy: {
944
- endpoint: 'https://dokploy.example.com',
945
- projectId: 'proj_xxx',
946
- applicationId: 'app_xxx',
947
- },
948
- },
949
944
  });
950
945
  ```
951
946
 
952
947
  **Environment Variables:**
953
948
  - `DOKPLOY_API_TOKEN`: API token for Dokploy (not needed if logged in via `gkm login`)
954
- - `GKM_MASTER_KEY`: Automatically set by Dokploy, or manually for Docker deployments
949
+ - `DOKPLOY_ENDPOINT`: Dokploy URL, if neither the stored login nor `deploy.dokploy.endpoint` gives one
950
+ - `DOCKER_REGISTRY_USERNAME` / `DOCKER_REGISTRY_PASSWORD`: a registry login, used only when Dokploy has no registry for `deploy.dokploy.registry` and one has to be created
951
+ - `GKM_HOME`: where stage keys and stored logins live (default `~/.gkm`)
952
+ - `GKM_MASTER_KEY`: Automatically set by Dokploy, or manually for Docker deployments — from `.gkm/server/master.key`, which `gkm build --stage` writes (the key is never printed; output shows its fingerprint)
955
953
 
956
954
  ### `gkm deploy:init`
957
955
 
958
- Initialize a new Dokploy deployment by creating a project and application via the Dokploy API. Automatically updates `gkm.config.ts` with the configuration.
956
+ Initialize a new Dokploy deployment by creating a project and application via the Dokploy API. Nothing is written to `gkm.config.ts`: the ids are rediscovered by name on each run and kept in the deploy state file.
959
957
 
960
958
  ```bash
961
959
  gkm deploy:init --project <name> --app <name> [options]
@@ -990,8 +988,7 @@ gkm deploy:init \
990
988
  1. Searches for existing project by name, or creates a new one
991
989
  2. Creates a new application in the project
992
990
  3. Configures registry if `--registry-id` is provided
993
- 4. Updates `gkm.config.ts` with the Dokploy configuration
994
- 5. Shows next steps for secrets and deployment
991
+ 4. Shows next steps for secrets and deployment
995
992
 
996
993
  ### `gkm deploy:list`
997
994
 
@@ -1040,7 +1037,7 @@ export const envParser = new EnvironmentParser({...process.env, ...Credentials})
1040
1037
 
1041
1038
  **How it works:**
1042
1039
  - At build time, secrets are encrypted with AES-256-GCM and embedded in the bundle
1043
- - An ephemeral master key is generated per build
1040
+ - An ephemeral master key is generated per build and written to `.gkm/server/master.key` (mode `0600`); the build prints its path and fingerprint, never the key
1044
1041
  - At runtime, `Credentials` decrypts using `GKM_MASTER_KEY` environment variable
1045
1042
  - In development (no embedded secrets), `Credentials` returns `{}`
1046
1043
 
@@ -1059,28 +1056,23 @@ The following commands are planned for future releases:
1059
1056
  The `gkm.config.ts` file defines how the CLI discovers and processes your endpoints:
1060
1057
 
1061
1058
  ```typescript
1062
- // Construct types accept a string, string[], or partitioned config
1063
- type Routes = string | string[] | PartitionedRoutes;
1064
-
1065
- interface PartitionedRoutes {
1066
- paths: string | string[];
1067
- partition: (filepath: string) => string;
1068
- }
1059
+ // Construct types accept a glob string or an array of them
1060
+ type Routes = string | string[];
1069
1061
 
1070
1062
  interface GkmConfig {
1071
- routes: Routes; // Glob patterns or partitioned config
1063
+ routes: Routes; // Glob patterns
1072
1064
  envParser: string; // Path to environment parser
1073
1065
  logger: string; // Path to logger configuration
1074
- functions?: Routes; // Glob patterns or partitioned config
1075
- crons?: Routes; // Glob patterns or partitioned config
1076
- subscribers?: Routes; // Glob patterns or partitioned config
1066
+ functions?: Routes; // Glob patterns
1067
+ crons?: Routes; // Glob patterns
1068
+ subscribers?: Routes; // Glob patterns
1077
1069
  runtime?: 'node' | 'bun'; // Runtime environment (default: 'node')
1078
1070
  telescope?: boolean | TelescopeConfig; // Telescope debugging config
1079
1071
  }
1080
1072
 
1081
1073
  interface TelescopeConfig {
1082
1074
  enabled?: boolean; // Enable/disable (default: true in dev)
1083
- path?: string; // Dashboard path (default: '/__telescope')
1075
+ path?: string; // Where the JSON API is mounted (default: '/__telescope')
1084
1076
  ignore?: string[]; // URL patterns to ignore
1085
1077
  recordBody?: boolean; // Record request/response bodies (default: true)
1086
1078
  maxEntries?: number; // Max entries to keep (default: 1000)
@@ -1092,7 +1084,7 @@ interface TelescopeConfig {
1092
1084
 
1093
1085
  #### `routes`
1094
1086
 
1095
- Glob pattern(s) to discover endpoint files. Can be a single pattern, array of patterns, or a partitioned config:
1087
+ Glob pattern(s) to discover endpoint files. Can be a single pattern or an array of patterns:
1096
1088
 
1097
1089
  ```typescript
1098
1090
  // Single pattern
@@ -1104,19 +1096,8 @@ routes: [
1104
1096
  'src/api/**/*.ts',
1105
1097
  'src/handlers/**/*.ts'
1106
1098
  ]
1107
-
1108
- // Partitioned — groups constructs in the generated manifest
1109
- routes: {
1110
- paths: './src/endpoints/**/*.ts',
1111
- partition: (filepath) => {
1112
- const match = filepath.match(/endpoints\/([^/]+)\//);
1113
- return match?.[1] ?? 'default';
1114
- },
1115
- }
1116
1099
  ```
1117
1100
 
1118
- The same partitioned format works for `functions`, `crons`, and `subscribers`. When a `partition` callback is provided, the generated manifest groups constructs by partition name (e.g., `manifest.routes.admin`). Without it, manifest fields remain flat arrays.
1119
-
1120
1101
  #### `envParser`
1121
1102
 
1122
1103
  Path to your environment parser configuration. Supports both default and named exports.
@@ -1149,7 +1130,7 @@ logger: './src/utils.ts#appLogger'
1149
1130
 
1150
1131
  #### `telescope`
1151
1132
 
1152
- Configuration for the Telescope debugging dashboard. Telescope is enabled by default when using `gkm dev`.
1133
+ Configuration for Telescope, which records requests, logs and exceptions and serves them as JSON. Telescope is enabled by default when using `gkm dev`.
1153
1134
 
1154
1135
  ```typescript
1155
1136
  // Disable telescope
@@ -1174,7 +1155,7 @@ telescope: {
1174
1155
  | Option | Type | Default | Description |
1175
1156
  |--------|------|---------|-------------|
1176
1157
  | `enabled` | `boolean` | `true` | Enable/disable Telescope |
1177
- | `path` | `string` | `/__telescope` | Dashboard URL path |
1158
+ | `path` | `string` | `/__telescope` | Where the JSON API is mounted |
1178
1159
  | `ignore` | `string[]` | `[]` | URL patterns to exclude from recording |
1179
1160
  | `recordBody` | `boolean` | `true` | Record request/response bodies |
1180
1161
  | `maxEntries` | `number` | `1000` | Maximum entries per type to keep |
@@ -1216,6 +1197,7 @@ import { defineWorkspace } from '@geekmidas/cli/config';
1216
1197
 
1217
1198
  export default defineWorkspace({
1218
1199
  name: 'my-project',
1200
+ stages: { local: 'dev', deployed: ['prod'] },
1219
1201
  constructs: './constructs/**/*.ts',
1220
1202
  shared: {
1221
1203
  packages: ['packages/*'],
@@ -1239,7 +1221,7 @@ them out of the manifest; none of them is listed in config.
1239
1221
 
1240
1222
  ```typescript
1241
1223
  // constructs/api.ts
1242
- export const api = new RestApi('Api', { defaultAuthorizer: 'none', logger });
1224
+ export const api = new RestApi('Api', { path: 'apps/api', defaultAuthorizer: 'none', logger });
1243
1225
  ```
1244
1226
 
1245
1227
  `gkm build --provider server` generates a Hono server from the endpoints built
@@ -1253,6 +1235,7 @@ An auth server mounts a wildcard, so there is nothing for a glob to find:
1253
1235
  ```typescript
1254
1236
  // constructs/auth.ts
1255
1237
  export const auth = new BetterAuth('Auth', {
1238
+ path: 'apps/auth',
1256
1239
  database: authDb,
1257
1240
  basePath: '/api/auth',
1258
1241
  });
@@ -1271,8 +1254,8 @@ from being read by it.
1271
1254
 
1272
1255
  ```typescript
1273
1256
  // constructs/site.ts
1274
- export const web = new StaticSite('Web').dependsOn([api, auth]);
1275
- export const admin = new StaticSite('Admin', { variant: 'next' }).dependsOn([api]);
1257
+ export const web = new StaticSite('Web', { path: 'apps/web' }).dependsOn([api, auth]);
1258
+ export const admin = new StaticSite('Admin', { path: 'apps/admin', variant: 'next' }).dependsOn([api]);
1276
1259
  ```
1277
1260
 
1278
1261
  `.dependsOn()` is the single fact behind four things that are hand-maintained
@@ -1291,7 +1274,7 @@ gkm docker
1291
1274
  - `.gkm/docker/Dockerfile.api` - Routes-based backend (uses `gkm build`)
1292
1275
  - `.gkm/docker/Dockerfile.auth` - Entry-based backend (uses esbuild bundling)
1293
1276
  - `.gkm/docker/Dockerfile.web` - Next.js standalone output
1294
- - `.gkm/docker/docker-compose.yml` - Full stack with all apps and services
1277
+ - `docker-compose.constructs.yml` - every container the constructs imply, and each app behind the `apps` profile (project root)
1295
1278
  - `.dockerignore` - Optimized ignore patterns
1296
1279
 
1297
1280
  **Dockerfile types by app:**
@@ -1358,12 +1341,10 @@ interface WorkspaceConfig {
1358
1341
  What an app is comes from the declaration, not from here:
1359
1342
 
1360
1343
  ```typescript
1361
- /** Overrides, all optional. A surface normally has none. */
1344
+ /** How a site is built and run. A surface declares only its `path`. */
1362
1345
  interface AppSpec {
1363
- /** Default: `apps/<kebab-id>` if present, else the project root. */
1364
- path?: string;
1365
- /** Default: the conventional directories under `path`. */
1366
- code?: string;
1346
+ /** Required: where the app lives, relative to the workspace root. */
1347
+ path: string;
1367
1348
  /** Default: assigned in a stable order, so adding an app renumbers nothing. */
1368
1349
  port?: number;
1369
1350
  entry?: string;
@@ -1371,42 +1352,24 @@ interface AppSpec {
1371
1352
  }
1372
1353
  ```
1373
1354
 
1374
- ## Providers
1375
-
1376
- ### AWS API Gateway v1
1377
-
1378
- Generates Lambda handlers compatible with AWS API Gateway v1 (REST API).
1379
-
1380
- ```bash
1381
- gkm build --provider aws-apigatewayv1
1382
- ```
1383
-
1384
- **Generated Handler:**
1385
- ```typescript
1386
- import { AmazonApiGatewayV1Endpoint } from '@geekmidas/constructs/aws';
1387
- import { myEndpoint } from '../src/routes/example.js';
1388
- import { envParser } from '../src/env.js';
1355
+ ## Build Targets
1389
1356
 
1390
- const adapter = new AmazonApiGatewayV1Endpoint(envParser, myEndpoint);
1391
-
1392
- export const handler = adapter.handler;
1393
- ```
1357
+ ### AWS
1394
1358
 
1395
- ### AWS API Gateway v2
1396
-
1397
- Generates Lambda handlers compatible with AWS API Gateway v2 (HTTP API).
1359
+ Generates one Lambda handler per construct. Endpoints get an API Gateway v2
1360
+ (HTTP API) adapter.
1398
1361
 
1399
1362
  ```bash
1400
- gkm build --provider aws-apigatewayv2
1363
+ gkm build # with deploy: { default: 'sst' }
1364
+ gkm build --provider aws # whatever the deploy target
1401
1365
  ```
1402
1366
 
1403
1367
  **Generated Handler:**
1404
1368
  ```typescript
1405
1369
  import { AmazonApiGatewayV2Endpoint } from '@geekmidas/constructs/aws';
1406
- import { myEndpoint } from '../src/routes/example.js';
1407
- import { envParser } from '../src/env.js';
1370
+ import { myEndpoint } from '../../../src/routes/example.js';
1408
1371
 
1409
- const adapter = new AmazonApiGatewayV2Endpoint(envParser, myEndpoint);
1372
+ const adapter = new AmazonApiGatewayV2Endpoint(myEndpoint);
1410
1373
 
1411
1374
  export const handler = adapter.handler;
1412
1375
  ```
@@ -1448,139 +1411,141 @@ export default createApp;
1448
1411
 
1449
1412
  ## Output Structure
1450
1413
 
1451
- The CLI generates files in the `.gkm/<provider>` directory:
1414
+ Handlers are generated beside the app, in `<app>/.gkm/<target>`; the manifest
1415
+ at the workspace root, where `sst.config.ts` runs:
1452
1416
 
1453
1417
  ```
1454
- .gkm/
1455
- ├── aws-apigatewayv1/
1456
- │ ├── getUsers.ts # Individual Lambda handler
1457
- │ ├── createUser.ts # Individual Lambda handler
1458
- ├── server/
1459
- │ ├── app.ts # Server application
1460
- │ ├── endpoints.ts # Endpoint exports
1461
- ├── manifest/
1462
- │ ├── aws.ts # AWS manifest with types
1463
- │ └── server.ts # Server manifest with types
1464
- └── openapi.json # OpenAPI specification
1418
+ <root>/
1419
+ ├── .gkm/manifest/
1420
+ │ ├── aws.ts # AWS manifest with types
1421
+ │ └── server.ts # Server manifest with types
1422
+ └── apps/api/.gkm/
1423
+ ├── aws/
1424
+ │ ├── routes/getUsers.ts # One Lambda handler per endpoint
1425
+ │ ├── functions/
1426
+ │ ├── crons/
1427
+ │ ├── queues/
1428
+ │ └── subscribers/
1429
+ └── server/
1430
+ ├── app.ts # Server application
1431
+ └── endpoints.ts # Endpoint exports
1465
1432
  ```
1466
1433
 
1467
1434
  ### Build Manifest
1468
1435
 
1469
- The CLI generates TypeScript manifests with full type information in the `.gkm/manifest/` directory. These manifests export both the data and derived types for type-safe usage.
1436
+ The manifest is the application's, not an app's: the root `gkm build` builds
1437
+ every backend and writes it once, to `.gkm/manifest/aws.ts` or
1438
+ `.gkm/manifest/server.ts` at the workspace root. An app's own build writes no
1439
+ manifest. Every handler path in it is relative to the workspace root. `gkm init`
1440
+ adds a root tsconfig alias for the manifest, `@<project>/manifest`.
1470
1441
 
1471
- #### AWS Manifest (`.gkm/manifest/aws.ts`)
1442
+ It exports two values — every declared construct keyed by id, and the backends
1443
+ the build resolved — and the types derived from them:
1472
1444
 
1473
1445
  ```typescript
1474
- export const manifest = {
1475
- routes: [
1476
- {
1477
- path: '/users',
1478
- method: 'GET',
1479
- handler: '.gkm/aws-apigatewayv1/getUsers.handler',
1480
- authorizer: 'jwt',
1481
- },
1482
- {
1483
- path: '/users',
1484
- method: 'POST',
1485
- handler: '.gkm/aws-apigatewayv1/createUser.handler',
1486
- authorizer: 'jwt',
1487
- },
1488
- ],
1489
- functions: [
1490
- {
1491
- name: 'processData',
1492
- handler: '.gkm/aws-lambda/functions/processData.handler',
1493
- timeout: 60,
1494
- memorySize: 256,
1495
- },
1496
- ],
1497
- crons: [
1498
- {
1499
- name: 'dailyCleanup',
1500
- handler: '.gkm/aws-lambda/crons/dailyCleanup.handler',
1501
- schedule: 'rate(1 day)',
1502
- timeout: 300,
1503
- memorySize: 512,
1446
+ export const constructs = {
1447
+ Database: { id: 'Database', kind: 'database' },
1448
+ Api: {
1449
+ id: 'Api',
1450
+ kind: 'rest-api',
1451
+ path: 'apps/api',
1452
+ endpoints: [
1453
+ {
1454
+ id: 'ApiGET/users',
1455
+ method: 'GET',
1456
+ path: '/users',
1457
+ handler: 'apps/api/.gkm/aws/routes/getUsers.handler',
1458
+ dependencies: [{ target: 'Database', kind: 'database' }],
1459
+ authorizer: 'iam',
1460
+ },
1461
+ ],
1462
+ },
1463
+ ProcessData: {
1464
+ id: 'ProcessData',
1465
+ kind: 'function',
1466
+ handler: 'apps/api/.gkm/aws/functions/processData.handler',
1467
+ dependencies: [],
1468
+ },
1469
+ DailyCleanup: {
1470
+ id: 'DailyCleanup',
1471
+ kind: 'cron',
1472
+ handler: 'apps/api/.gkm/aws/crons/dailyCleanup.handler',
1473
+ schedule: 'rate(1 day)',
1474
+ dependencies: [{ target: 'Database', kind: 'database' }],
1475
+ },
1476
+ Emails: {
1477
+ id: 'Emails',
1478
+ kind: 'queue',
1479
+ // The queue's consumer is nested in it
1480
+ worker: {
1481
+ id: 'emails',
1482
+ handler: 'apps/api/.gkm/aws/queues/emails.handler',
1483
+ dependencies: [],
1504
1484
  },
1505
- ],
1506
- subscribers: [],
1507
- } as const;
1485
+ },
1486
+ Users: {
1487
+ id: 'Users',
1488
+ kind: 'topic',
1489
+ events: ['user.created'],
1490
+ // As are a topic's subscribers
1491
+ subscribers: [
1492
+ {
1493
+ id: 'onUser',
1494
+ handler: 'apps/api/.gkm/aws/subscribers/onUser.handler',
1495
+ events: ['user.created'],
1496
+ dependencies: [],
1497
+ },
1498
+ ],
1499
+ },
1500
+ } as const satisfies ConstructManifest;
1501
+
1502
+ export const backends = { cache: 'upstash', email: 'ses' } as const;
1508
1503
 
1509
1504
  // Derived types
1510
- export type Route = (typeof manifest.routes)[number];
1511
- export type Function = (typeof manifest.functions)[number];
1512
- export type Cron = (typeof manifest.crons)[number];
1513
- export type Subscriber = (typeof manifest.subscribers)[number];
1505
+ export type Ids = IdsOf<typeof constructs>;
1506
+ export type Construct<Id extends Ids> = DeclarationOf<typeof constructs, Id>;
1507
+ export type Kind = Construct<Ids>['kind'];
1514
1508
 
1515
1509
  // Useful union types
1516
- export type Authorizer = Route['authorizer'];
1517
- export type HttpMethod = Route['method'];
1518
- export type RoutePath = Route['path'];
1510
+ export type ProvidedKeys = AllProvidedKeys<typeof constructs>;
1511
+ export type Surfaces = IdsOfKind<typeof constructs, 'rest-api'>;
1512
+ export type CacheBackend = (typeof backends)['cache'];
1513
+ export type EmailBackend = (typeof backends)['email'];
1519
1514
  ```
1520
1515
 
1521
- When routes are partitioned, the manifest groups them by partition name and generates partition-aware types:
1516
+ A `RestApi` carries its endpoints, each with its method, path, handler, the
1517
+ dependencies it declared and its authorizer. On AWS the handler is the
1518
+ endpoint's own Lambda (`apps/api/.gkm/aws/routes/<export>.handler`); in the
1519
+ server manifest every endpoint's handler is the app entry
1520
+ (`apps/api/.gkm/server/app.ts`). Functions and crons are top-level
1521
+ declarations; a queue's worker and a topic's subscribers are nested in the
1522
+ queue or topic.
1522
1523
 
1523
- ```typescript
1524
- export const manifest = {
1525
- routes: {
1526
- "admin": [{ path: '/admin/users', method: 'GET', handler: '...', authorizer: 'jwt' }],
1527
- "default": [{ path: '/users', method: 'GET', handler: '...', authorizer: 'jwt' }],
1528
- },
1529
- functions: [...], // flat if not partitioned
1530
- } as const;
1524
+ #### Using the Manifest
1531
1525
 
1532
- // Partition-aware derived types
1533
- export type RoutePartition = keyof typeof manifest.routes;
1534
- export type Route<P extends RoutePartition = RoutePartition> =
1535
- (typeof manifest.routes)[P][number];
1536
- ```
1537
-
1538
- #### Server Manifest (`.gkm/manifest/server.ts`)
1526
+ On AWS, `fromManifest` from `@geekmidas/cloud/sst` provisions all of it —
1527
+ resources, each API's endpoints, queue consumers, functions and crons:
1539
1528
 
1540
1529
  ```typescript
1541
- export const manifest = {
1542
- app: {
1543
- handler: '.gkm/server/app.ts',
1544
- endpoints: '.gkm/server/endpoints.ts',
1545
- },
1546
- routes: [
1547
- { path: '/users', method: 'GET', authorizer: 'jwt' },
1548
- { path: '/users', method: 'POST', authorizer: 'jwt' },
1549
- ],
1550
- subscribers: [
1551
- { name: 'orderHandler', subscribedEvents: ['order.created'] },
1552
- ],
1553
- } as const;
1530
+ // sst.config.ts
1531
+ const { App, fromManifest, Stack } = await import('@geekmidas/cloud/sst');
1532
+ const { backends, constructs } = await import('./.gkm/manifest/aws.js');
1554
1533
 
1555
- // Derived types
1556
- export type Route = (typeof manifest.routes)[number];
1557
- export type Subscriber = (typeof manifest.subscribers)[number];
1558
-
1559
- // Useful union types
1560
- export type Authorizer = Route['authorizer'];
1561
- export type HttpMethod = Route['method'];
1562
- export type RoutePath = Route['path'];
1534
+ fromManifest(new Stack(app, 'Shop'), constructs, { Database: { vpc } }, backends);
1563
1535
  ```
1564
1536
 
1565
- #### Using Manifest Types
1566
-
1567
- Import the manifest types for type-safe infrastructure configuration:
1537
+ The types narrow to what was declared:
1568
1538
 
1569
1539
  ```typescript
1570
- import { manifest, type Route, type Authorizer } from './.gkm/manifest/aws';
1540
+ import { constructs, type Construct, type Surfaces } from './.gkm/manifest/aws';
1571
1541
 
1572
- // Type-safe route iteration
1573
- for (const route of manifest.routes) {
1574
- console.log(`${route.method} ${route.path} -> ${route.handler}`);
1575
- }
1542
+ const api: Construct<'Api'> = constructs.Api;
1576
1543
 
1577
- // Use union types for validation
1578
- function isValidMethod(method: string): method is HttpMethod {
1579
- return manifest.routes.some((r) => r.method === method);
1544
+ for (const endpoint of api.endpoints) {
1545
+ console.log(`${endpoint.method} ${endpoint.path} -> ${endpoint.handler}`);
1580
1546
  }
1581
1547
 
1582
- // Access authorizer names
1583
- const authorizers = new Set(manifest.routes.map((r) => r.authorizer));
1548
+ const surface: Surfaces = 'Api'; // any other id is a type error
1584
1549
  ```
1585
1550
 
1586
1551
  ## OpenAPI Generation
@@ -1675,17 +1640,17 @@ provider:
1675
1640
 
1676
1641
  functions:
1677
1642
  getUsers:
1678
- handler: .gkm/aws-apigatewayv1/getUsers.handler
1643
+ handler: .gkm/aws/routes/getUsers.handler
1679
1644
  events:
1680
- - http:
1681
- path: users
1645
+ - httpApi:
1646
+ path: /users
1682
1647
  method: get
1683
-
1648
+
1684
1649
  createUser:
1685
- handler: .gkm/aws-apigatewayv1/createUser.handler
1650
+ handler: .gkm/aws/routes/createUser.handler
1686
1651
  events:
1687
- - http:
1688
- path: users
1652
+ - httpApi:
1653
+ path: /users
1689
1654
  method: post
1690
1655
  ```
1691
1656
 
@@ -1883,6 +1848,7 @@ Configure multiple patterns for complex project structures:
1883
1848
  import { defineConfig } from '@geekmidas/cli/config';
1884
1849
 
1885
1850
  export default defineConfig({
1851
+ stages: { local: 'dev', deployed: ['prod'] },
1886
1852
  routes: [
1887
1853
  'src/routes/**/*.ts',
1888
1854
  'src/api/v1/**/*.ts',
@@ -1935,7 +1901,7 @@ Error: OpenAPI generation failed: Invalid endpoint schema
1935
1901
  "dev": "gkm dev",
1936
1902
  "dev:port": "gkm dev --port 8080",
1937
1903
  "build": "gkm build",
1938
- "build:lambda": "gkm build --provider aws-apigatewayv1",
1904
+ "build:lambda": "gkm build --provider aws",
1939
1905
  "build:server": "gkm build --provider server",
1940
1906
  "docs": "gkm openapi --output src/api.ts"
1941
1907
  }
@@ -2029,8 +1995,8 @@ DEBUG=gkm:* npx gkm build
2029
1995
  ### Types
2030
1996
 
2031
1997
  ```typescript
2032
- // Provider options
2033
- type Provider = 'server' | 'aws-apigatewayv1' | 'aws-apigatewayv2';
1998
+ // Build targets
1999
+ type Provider = 'aws' | 'server';
2034
2000
 
2035
2001
  // Runtime options
2036
2002
  type Runtime = 'node' | 'bun';
@@ -2054,17 +2020,8 @@ interface DockerConfig {
2054
2020
  imageName?: string; // Image name (defaults to package.json name)
2055
2021
  baseImage?: string; // Base image (default: node:22-alpine)
2056
2022
  port?: number; // Port to expose (default: 3000)
2057
- compose?: {
2058
- services?: ComposeServicesConfig | ComposeServiceName[];
2059
- };
2060
2023
  }
2061
2024
 
2062
- // Service configuration for docker-compose
2063
- type ComposeServiceName = 'postgres' | 'redis' | 'rabbitmq';
2064
- type ComposeServicesConfig = {
2065
- [K in ComposeServiceName]?: boolean | { version?: string };
2066
- };
2067
-
2068
2025
  // Telescope configuration
2069
2026
  interface TelescopeConfig {
2070
2027
  enabled?: boolean;
@@ -2077,7 +2034,7 @@ interface TelescopeConfig {
2077
2034
 
2078
2035
  // Build options
2079
2036
  interface BuildOptions {
2080
- provider: Provider;
2037
+ provider?: Provider; // default: where gkm.config.ts deploys
2081
2038
  }
2082
2039
 
2083
2040
  // Dev options