@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
package/src/dev/index.ts CHANGED
@@ -1,21 +1,62 @@
1
1
  import { type ChildProcess, spawn } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
2
3
  import { mkdir } from 'node:fs/promises';
3
4
  import { createServer } from 'node:net';
4
- import { join } from 'node:path';
5
+ import { join, resolve } from 'node:path';
5
6
  import chokidar from 'chokidar';
7
+ import { config as dotenvConfig } from 'dotenv';
8
+ import fg from 'fast-glob';
6
9
  import { resolveProviders } from '../build/providerResolver';
7
- import type { BuildContext } from '../build/types';
8
- import { loadConfig } from '../config';
10
+ import type { BuildContext, NormalizedTelescopeConfig } from '../build/types';
11
+ import { loadConfig, parseModuleConfig } from '../config';
9
12
  import {
10
13
  CronGenerator,
11
14
  EndpointGenerator,
12
15
  FunctionGenerator,
13
16
  SubscriberGenerator,
14
17
  } from '../generators';
15
- import type { LegacyProvider } from '../types';
18
+ import type {
19
+ GkmConfig,
20
+ LegacyProvider,
21
+ Runtime,
22
+ TelescopeConfig,
23
+ } from '../types';
16
24
 
17
25
  const logger = console;
18
26
 
27
+ /**
28
+ * Load environment files
29
+ * @internal Exported for testing
30
+ */
31
+ export function loadEnvFiles(
32
+ envConfig: string | string[] | undefined,
33
+ cwd: string = process.cwd(),
34
+ ): { loaded: string[]; missing: string[] } {
35
+ const loaded: string[] = [];
36
+ const missing: string[] = [];
37
+
38
+ // Normalize to array
39
+ const envFiles = envConfig
40
+ ? Array.isArray(envConfig)
41
+ ? envConfig
42
+ : [envConfig]
43
+ : ['.env'];
44
+
45
+ // Load each env file in order (later files override earlier)
46
+ for (const envFile of envFiles) {
47
+ const envPath = resolve(cwd, envFile);
48
+ if (existsSync(envPath)) {
49
+ dotenvConfig({ path: envPath, override: true, quiet: true });
50
+ loaded.push(envFile);
51
+ } else if (envConfig) {
52
+ // Only report as missing if explicitly configured
53
+ missing.push(envFile);
54
+ }
55
+ }
56
+
57
+ return { loaded, missing };
58
+ }
59
+
19
60
  /**
20
61
  * Check if a port is available
21
62
  * @internal Exported for testing
@@ -62,14 +103,81 @@ export async function findAvailablePort(
62
103
  );
63
104
  }
64
105
 
106
+ /**
107
+ * Normalize telescope configuration
108
+ * @internal Exported for testing
109
+ */
110
+ export function normalizeTelescopeConfig(
111
+ config: GkmConfig['telescope'],
112
+ ): NormalizedTelescopeConfig | undefined {
113
+ if (config === false) {
114
+ return undefined;
115
+ }
116
+
117
+ // Handle string path (e.g., './src/config/telescope')
118
+ if (typeof config === 'string') {
119
+ const { path: telescopePath, importPattern: telescopeImportPattern } =
120
+ parseModuleConfig(config, 'telescope');
121
+
122
+ return {
123
+ enabled: true,
124
+ telescopePath,
125
+ telescopeImportPattern,
126
+ path: '/__telescope',
127
+ ignore: [],
128
+ recordBody: true,
129
+ maxEntries: 1000,
130
+ websocket: true,
131
+ };
132
+ }
133
+
134
+ // Default to enabled in development mode
135
+ const isEnabled =
136
+ config === true || config === undefined || config.enabled !== false;
137
+
138
+ if (!isEnabled) {
139
+ return undefined;
140
+ }
141
+
142
+ const telescopeConfig: TelescopeConfig =
143
+ typeof config === 'object' ? config : {};
144
+
145
+ return {
146
+ enabled: true,
147
+ path: telescopeConfig.path ?? '/__telescope',
148
+ ignore: telescopeConfig.ignore ?? [],
149
+ recordBody: telescopeConfig.recordBody ?? true,
150
+ maxEntries: telescopeConfig.maxEntries ?? 1000,
151
+ websocket: telescopeConfig.websocket ?? true,
152
+ };
153
+ }
154
+
65
155
  export interface DevOptions {
66
156
  port?: number;
67
157
  enableOpenApi?: boolean;
68
158
  }
69
159
 
70
160
  export async function devCommand(options: DevOptions): Promise<void> {
161
+ // Load default .env file BEFORE loading config
162
+ // This ensures env vars are available when config and its dependencies are loaded
163
+ const defaultEnv = loadEnvFiles('.env');
164
+ if (defaultEnv.loaded.length > 0) {
165
+ logger.log(`📦 Loaded env: ${defaultEnv.loaded.join(', ')}`);
166
+ }
167
+
71
168
  const config = await loadConfig();
72
169
 
170
+ // Load any additional env files specified in config
171
+ if (config.env) {
172
+ const { loaded, missing } = loadEnvFiles(config.env);
173
+ if (loaded.length > 0) {
174
+ logger.log(`📦 Loaded env: ${loaded.join(', ')}`);
175
+ }
176
+ if (missing.length > 0) {
177
+ logger.warn(`⚠️ Missing env files: ${missing.join(', ')}`);
178
+ }
179
+ }
180
+
73
181
  // Force server provider for dev mode
74
182
  const resolved = resolveProviders(config, { provider: 'server' });
75
183
 
@@ -86,27 +194,24 @@ export async function devCommand(options: DevOptions): Promise<void> {
86
194
  }
87
195
  logger.log(`Using envParser: ${config.envParser}`);
88
196
 
89
- // Parse envParser configuration
90
- const [envParserPath, envParserName] = config.envParser.split('#');
91
- const envParserImportPattern = !envParserName
92
- ? 'envParser'
93
- : envParserName === 'envParser'
94
- ? '{ envParser }'
95
- : `{ ${envParserName} as envParser }`;
96
-
97
- // Parse logger configuration
98
- const [loggerPath, loggerName] = config.logger.split('#');
99
- const loggerImportPattern = !loggerName
100
- ? 'logger'
101
- : loggerName === 'logger'
102
- ? '{ logger }'
103
- : `{ ${loggerName} as logger }`;
197
+ // Parse envParser and logger configuration
198
+ const { path: envParserPath, importPattern: envParserImportPattern } =
199
+ parseModuleConfig(config.envParser, 'envParser');
200
+ const { path: loggerPath, importPattern: loggerImportPattern } =
201
+ parseModuleConfig(config.logger, 'logger');
202
+
203
+ // Normalize telescope configuration
204
+ const telescope = normalizeTelescopeConfig(config.telescope);
205
+ if (telescope) {
206
+ logger.log(`🔭 Telescope enabled at ${telescope.path}`);
207
+ }
104
208
 
105
209
  const buildContext: BuildContext = {
106
210
  envParserPath,
107
211
  envParserImportPattern,
108
212
  loggerPath,
109
213
  loggerImportPattern,
214
+ telescope,
110
215
  };
111
216
 
112
217
  // Build initial version
@@ -117,31 +222,70 @@ export async function devCommand(options: DevOptions): Promise<void> {
117
222
  resolved.enableOpenApi,
118
223
  );
119
224
 
225
+ // Determine runtime (default to node)
226
+ const runtime: Runtime = config.runtime ?? 'node';
227
+
120
228
  // Start the dev server
121
229
  const devServer = new DevServer(
122
230
  resolved.providers[0] as LegacyProvider,
123
231
  options.port || 3000,
124
232
  resolved.enableOpenApi,
233
+ telescope,
234
+ runtime,
125
235
  );
126
236
 
127
237
  await devServer.start();
128
238
 
129
239
  // Watch for file changes
240
+ const envParserFile = config.envParser.split('#')[0];
241
+ const loggerFile = config.logger.split('#')[0];
242
+
130
243
  const watchPatterns = [
131
244
  config.routes,
132
245
  ...(config.functions ? [config.functions] : []),
133
246
  ...(config.crons ? [config.crons] : []),
134
247
  ...(config.subscribers ? [config.subscribers] : []),
135
- config.envParser.split('#')[0],
136
- config.logger.split('#')[0],
248
+ // Add .ts extension if not present for config files
249
+ envParserFile.endsWith('.ts') ? envParserFile : `${envParserFile}.ts`,
250
+ loggerFile.endsWith('.ts') ? loggerFile : `${loggerFile}.ts`,
137
251
  ].flat();
138
252
 
139
- logger.log(`👀 Watching for changes in: ${watchPatterns.join(', ')}`);
253
+ // Normalize patterns - remove leading ./ when using cwd option
254
+ const normalizedPatterns = watchPatterns.map((p) =>
255
+ p.startsWith('./') ? p.slice(2) : p,
256
+ );
257
+
258
+ logger.log(`👀 Watching for changes in: ${normalizedPatterns.join(', ')}`);
259
+
260
+ // Resolve glob patterns to actual files (chokidar 4.x doesn't support globs)
261
+ const resolvedFiles = await fg(normalizedPatterns, {
262
+ cwd: process.cwd(),
263
+ absolute: false,
264
+ onlyFiles: true,
265
+ });
266
+
267
+ // Also watch the directories for new files
268
+ const dirsToWatch = [
269
+ ...new Set(resolvedFiles.map((f) => f.split('/').slice(0, -1).join('/'))),
270
+ ];
271
+
272
+ logger.log(
273
+ `📁 Found ${resolvedFiles.length} files in ${dirsToWatch.length} directories`,
274
+ );
140
275
 
141
- const watcher = chokidar.watch(watchPatterns, {
276
+ const watcher = chokidar.watch([...resolvedFiles, ...dirsToWatch], {
142
277
  ignored: /(^|[\/\\])\../, // ignore dotfiles
143
278
  persistent: true,
144
279
  ignoreInitial: true,
280
+ cwd: process.cwd(),
281
+ });
282
+
283
+ watcher.on('ready', () => {
284
+ logger.log('🔍 File watcher ready');
285
+ });
286
+
287
+ watcher.on('error', (error) => {
288
+ logger.error('❌ Watcher error:', error);
145
289
  });
146
290
 
147
291
  let rebuildTimeout: NodeJS.Timeout | null = null;
@@ -229,6 +373,8 @@ class DevServer {
229
373
  private provider: LegacyProvider,
230
374
  private requestedPort: number,
231
375
  private enableOpenApi: boolean,
376
+ private telescope?: NormalizedTelescopeConfig,
377
+ private runtime: Runtime = 'node',
232
378
  ) {
233
379
  this.actualPort = requestedPort;
234
380
  }
@@ -260,12 +406,14 @@ class DevServer {
260
406
  logger.log(`\n✨ Starting server on port ${this.actualPort}...`);
261
407
 
262
408
  // Start the server using tsx (TypeScript execution)
409
+ // Use detached: true so we can kill the entire process tree
263
410
  this.serverProcess = spawn(
264
411
  'npx',
265
412
  ['tsx', serverEntryPath, '--port', this.actualPort.toString()],
266
413
  {
267
414
  stdio: 'inherit',
268
415
  env: { ...process.env, NODE_ENV: 'development' },
416
+ detached: true,
269
417
  },
270
418
  );
271
419
 
@@ -292,19 +440,39 @@ class DevServer {
292
440
  `📚 API Docs available at http://localhost:${this.actualPort}/docs`,
293
441
  );
294
442
  }
443
+ if (this.telescope) {
444
+ logger.log(
445
+ `🔭 Telescope available at http://localhost:${this.actualPort}${this.telescope.path}`,
446
+ );
447
+ }
295
448
  }
296
449
  }
297
450
 
298
451
  async stop(): Promise<void> {
299
452
  if (this.serverProcess && this.isRunning) {
300
- this.serverProcess.kill('SIGTERM');
453
+ const pid = this.serverProcess.pid;
454
+
455
+ // Kill the entire process group (negative PID kills the group)
456
+ if (pid) {
457
+ try {
458
+ process.kill(-pid, 'SIGTERM');
459
+ } catch {
460
+ // Process might already be dead
461
+ }
462
+ }
301
463
 
302
464
  // Wait for process to exit
303
465
  await new Promise<void>((resolve) => {
304
466
  const timeout = setTimeout(() => {
305
- this.serverProcess?.kill('SIGKILL');
467
+ if (pid) {
468
+ try {
469
+ process.kill(-pid, 'SIGKILL');
470
+ } catch {
471
+ // Process might already be dead
472
+ }
473
+ }
306
474
  resolve();
307
- }, 5000);
475
+ }, 3000);
308
476
 
309
477
  this.serverProcess?.on('exit', () => {
310
478
  clearTimeout(timeout);
@@ -318,7 +486,21 @@ class DevServer {
318
486
  }
319
487
 
320
488
  async restart(): Promise<void> {
489
+ const portToReuse = this.actualPort;
321
490
  await this.stop();
491
+
492
+ // Wait for port to be released (up to 3 seconds)
493
+ let attempts = 0;
494
+ while (attempts < 30) {
495
+ if (await isPortAvailable(portToReuse)) {
496
+ break;
497
+ }
498
+ await new Promise((resolve) => setTimeout(resolve, 100));
499
+ attempts++;
500
+ }
501
+
502
+ // Force reuse the same port
503
+ this.requestedPort = portToReuse;
322
504
  await this.start();
323
505
  }
324
506
 
@@ -333,6 +515,24 @@ class DevServer {
333
515
  join(dirname(serverPath), 'app.js'),
334
516
  );
335
517
 
518
+ const serveCode =
519
+ this.runtime === 'bun'
520
+ ? `Bun.serve({
521
+ port,
522
+ fetch: app.fetch,
523
+ });`
524
+ : `const { serve } = await import('@hono/node-server');
525
+ const server = serve({
526
+ fetch: app.fetch,
527
+ port,
528
+ });
529
+ // Inject WebSocket support if available
530
+ const injectWs = (app as any).__injectWebSocket;
531
+ if (injectWs) {
532
+ injectWs(server);
533
+ console.log('🔌 Telescope real-time updates enabled');
534
+ }`;
535
+
336
536
  const content = `#!/usr/bin/env node
337
537
  /**
338
538
  * Development server entry point
@@ -344,27 +544,14 @@ const port = process.argv.includes('--port')
344
544
  ? Number.parseInt(process.argv[process.argv.indexOf('--port') + 1])
345
545
  : 3000;
346
546
 
347
- const { app, start } = createApp(undefined, ${this.enableOpenApi});
547
+ // createApp is async to support optional WebSocket setup
548
+ const { app, start } = await createApp(undefined, ${this.enableOpenApi});
348
549
 
349
550
  // Start the server
350
551
  start({
351
552
  port,
352
553
  serve: async (app, port) => {
353
- // Detect runtime and use appropriate server
354
- if (typeof Bun !== 'undefined') {
355
- // Bun runtime
356
- Bun.serve({
357
- port,
358
- fetch: app.fetch,
359
- });
360
- } else {
361
- // Node.js runtime with @hono/node-server
362
- const { serve } = await import('@hono/node-server');
363
- serve({
364
- fetch: app.fetch,
365
- port,
366
- });
367
- }
554
+ ${serveCode}
368
555
  },
369
556
  }).catch((error) => {
370
557
  console.error('Failed to start server:', error);
@@ -249,7 +249,7 @@ export class EndpointGenerator extends ConstructGenerator<
249
249
  any
250
250
  >
251
251
  >[],
252
- context: BuildContext,
252
+ _context: BuildContext,
253
253
  ): Promise<string> {
254
254
  const endpointsFileName = 'endpoints.ts';
255
255
  const endpointsPath = join(outputDir, endpointsFileName);
@@ -333,6 +333,98 @@ export function setupEndpoints(
333
333
  context.envParserPath,
334
334
  );
335
335
 
336
+ // Generate telescope imports and setup if enabled
337
+ const telescopeEnabled = context.telescope?.enabled;
338
+ const telescopeWebSocketEnabled = context.telescope?.websocket;
339
+ const usesExternalTelescope = !!context.telescope?.telescopePath;
340
+
341
+ // Generate imports based on whether telescope is external or inline
342
+ let telescopeImports = '';
343
+ if (telescopeEnabled) {
344
+ if (usesExternalTelescope) {
345
+ const relativeTelescopePath = relative(
346
+ dirname(appPath),
347
+ context.telescope!.telescopePath!,
348
+ );
349
+ telescopeImports = `import ${context.telescope!.telescopeImportPattern} from '${relativeTelescopePath}';
350
+ import { createMiddleware, createUI } from '@geekmidas/telescope/hono';`;
351
+ } else {
352
+ telescopeImports = `import { Telescope, InMemoryStorage } from '@geekmidas/telescope';
353
+ import { createMiddleware, createUI } from '@geekmidas/telescope/hono';`;
354
+ }
355
+ }
356
+
357
+ const telescopeWebSocketSetupCode = telescopeWebSocketEnabled
358
+ ? `
359
+ // Setup WebSocket for real-time telescope updates
360
+ try {
361
+ const { createNodeWebSocket } = await import('@hono/node-ws');
362
+ const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app: honoApp });
363
+ // Add WebSocket route directly to main app (sub-app routes don't support WS upgrade)
364
+ honoApp.get('${context.telescope!.path}/ws', upgradeWebSocket(() => ({
365
+ onOpen: (_event: Event, ws: any) => {
366
+ telescope.addWsClient(ws);
367
+ },
368
+ onClose: (_event: Event, ws: any) => {
369
+ telescope.removeWsClient(ws);
370
+ },
371
+ onMessage: (event: MessageEvent, ws: any) => {
372
+ try {
373
+ const data = JSON.parse(event.data);
374
+ if (data.type === 'ping') {
375
+ ws.send(JSON.stringify({ type: 'pong' }));
376
+ }
377
+ } catch {
378
+ // Ignore invalid messages
379
+ }
380
+ },
381
+ })));
382
+ // Store injectWebSocket for server entry to call after serve()
383
+ (honoApp as any).__injectWebSocket = injectWebSocket;
384
+ logger.info('Telescope WebSocket enabled');
385
+ } catch (e) {
386
+ logger.warn({ error: e }, 'WebSocket support not available - install @hono/node-ws for real-time updates');
387
+ }
388
+ `
389
+ : '';
390
+
391
+ // Generate telescope setup - either use external instance or create inline
392
+ let telescopeSetup = '';
393
+ if (telescopeEnabled) {
394
+ if (usesExternalTelescope) {
395
+ // Use external telescope instance - no need to create one
396
+ telescopeSetup = `
397
+ ${telescopeWebSocketSetupCode}
398
+ // Add telescope middleware (before endpoints to capture all requests)
399
+ honoApp.use('*', createMiddleware(telescope));
400
+
401
+ // Mount telescope UI
402
+ const telescopeUI = createUI(telescope);
403
+ honoApp.route('${context.telescope!.path}', telescopeUI);
404
+ `;
405
+ } else {
406
+ // Create inline telescope instance
407
+ telescopeSetup = `
408
+ // Setup Telescope for debugging/monitoring
409
+ const telescopeStorage = new InMemoryStorage({ maxEntries: ${context.telescope!.maxEntries} });
410
+ const telescope = new Telescope({
411
+ enabled: true,
412
+ path: '${context.telescope!.path}',
413
+ ignorePatterns: ${JSON.stringify(context.telescope!.ignore)},
414
+ recordBody: ${context.telescope!.recordBody},
415
+ storage: telescopeStorage,
416
+ });
417
+ ${telescopeWebSocketSetupCode}
418
+ // Add telescope middleware (before endpoints to capture all requests)
419
+ honoApp.use('*', createMiddleware(telescope));
420
+
421
+ // Mount telescope UI
422
+ const telescopeUI = createUI(telescope);
423
+ honoApp.route('${context.telescope!.path}', telescopeUI);
424
+ `;
425
+ }
426
+ }
427
+
336
428
  const content = `/**
337
429
  * Generated server application
338
430
  *
@@ -346,6 +438,7 @@ import { setupEndpoints } from './endpoints.js';
346
438
  import { setupSubscribers } from './subscribers.js';
347
439
  import ${context.envParserImportPattern} from '${relativeEnvParserPath}';
348
440
  import ${context.loggerImportPattern} from '${relativeLoggerPath}';
441
+ ${telescopeImports}
349
442
 
350
443
  export interface ServerApp {
351
444
  app: HonoType;
@@ -366,7 +459,7 @@ export interface ServerApp {
366
459
  * // With Bun
367
460
  * import { createApp } from './.gkm/server/app.js';
368
461
  *
369
- * const { app, start } = createApp();
462
+ * const { app, start } = await createApp();
370
463
  *
371
464
  * await start({
372
465
  * port: 3000,
@@ -380,7 +473,7 @@ export interface ServerApp {
380
473
  * import { serve } from '@hono/node-server';
381
474
  * import { createApp } from './.gkm/server/app.js';
382
475
  *
383
- * const { app, start } = createApp();
476
+ * const { app, start } = await createApp();
384
477
  *
385
478
  * await start({
386
479
  * port: 3000,
@@ -389,9 +482,9 @@ export interface ServerApp {
389
482
  * }
390
483
  * });
391
484
  */
392
- export function createApp(app?: HonoType, enableOpenApi: boolean = true): ServerApp {
485
+ export async function createApp(app?: HonoType, enableOpenApi: boolean = true): Promise<ServerApp> {
393
486
  const honoApp = app || new Hono();
394
-
487
+ ${telescopeSetup}
395
488
  // Setup HTTP endpoints
396
489
  setupEndpoints(honoApp, envParser, logger, enableOpenApi);
397
490