@geekmidas/cli 0.4.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 (298) hide show
  1. package/dist/CronGenerator-BPTqNYOR.d.cts +14 -0
  2. package/dist/{CronGenerator-Bh26MaNA.mjs → CronGenerator-CCRYptuT.mjs} +2 -2
  3. package/dist/{CronGenerator-Bh26MaNA.mjs.map → CronGenerator-CCRYptuT.mjs.map} +1 -1
  4. package/dist/{CronGenerator-C6MF8rlG.cjs → CronGenerator-D4TWXQbh.cjs} +2 -2
  5. package/dist/{CronGenerator-C6MF8rlG.cjs.map → CronGenerator-D4TWXQbh.cjs.map} +1 -1
  6. package/dist/CronGenerator-YAj59JUd.d.mts +14 -0
  7. package/dist/EndpointGenerator-ChAD1INz.d.cts +19 -0
  8. package/dist/EndpointGenerator-Cj3O1U8-.d.mts +19 -0
  9. package/dist/{EndpointGenerator-CzDhG7Or.mjs → EndpointGenerator-DGivkPLT.mjs} +22 -6
  10. package/dist/EndpointGenerator-DGivkPLT.mjs.map +1 -0
  11. package/dist/{EndpointGenerator-BxNCkus4.cjs → EndpointGenerator-npWEDoK2.cjs} +22 -6
  12. package/dist/EndpointGenerator-npWEDoK2.cjs.map +1 -0
  13. package/dist/FunctionGenerator-429-9NER.d.cts +14 -0
  14. package/dist/FunctionGenerator-BQ4ehoID.d.mts +14 -0
  15. package/dist/{FunctionGenerator-BNE_GC7N.mjs → FunctionGenerator-CVk0h8tO.mjs} +2 -2
  16. package/dist/{FunctionGenerator-BNE_GC7N.mjs.map → FunctionGenerator-CVk0h8tO.mjs.map} +1 -1
  17. package/dist/{FunctionGenerator-FgZUTd8L.cjs → FunctionGenerator-DYTnyr4c.cjs} +2 -2
  18. package/dist/{FunctionGenerator-FgZUTd8L.cjs.map → FunctionGenerator-DYTnyr4c.cjs.map} +1 -1
  19. package/dist/Generator-BjHK_qce.d.mts +27 -0
  20. package/dist/{Generator-UanJW0_V.mjs → Generator-CDt4pB3W.mjs} +1 -1
  21. package/dist/{Generator-UanJW0_V.mjs.map → Generator-CDt4pB3W.mjs.map} +1 -1
  22. package/dist/{Generator-CDoEXCDg.cjs → Generator-CLVplqm2.cjs} +1 -1
  23. package/dist/{Generator-CDoEXCDg.cjs.map → Generator-CLVplqm2.cjs.map} +1 -1
  24. package/dist/Generator-DxQMCQp7.d.cts +27 -0
  25. package/dist/{OpenApiTsGenerator-NBNEoaeO.cjs → OpenApiTsGenerator-Be-sKGTT.cjs} +1 -1
  26. package/dist/{OpenApiTsGenerator-NBNEoaeO.cjs.map → OpenApiTsGenerator-Be-sKGTT.cjs.map} +1 -1
  27. package/dist/{OpenApiTsGenerator-q3aWNkuM.mjs → OpenApiTsGenerator-C4mHHaku.mjs} +1 -1
  28. package/dist/{OpenApiTsGenerator-q3aWNkuM.mjs.map → OpenApiTsGenerator-C4mHHaku.mjs.map} +1 -1
  29. package/dist/SubscriberGenerator-7uX42xyG.d.mts +15 -0
  30. package/dist/{SubscriberGenerator-Dnlj_1FK.mjs → SubscriberGenerator-DABaJXML.mjs} +2 -2
  31. package/dist/{SubscriberGenerator-Dnlj_1FK.mjs.map → SubscriberGenerator-DABaJXML.mjs.map} +1 -1
  32. package/dist/{SubscriberGenerator-Bd-a7aiw.cjs → SubscriberGenerator-D_zpNGFr.cjs} +2 -2
  33. package/dist/{SubscriberGenerator-Bd-a7aiw.cjs.map → SubscriberGenerator-D_zpNGFr.cjs.map} +1 -1
  34. package/dist/SubscriberGenerator-Dtb3HS4i.d.cts +15 -0
  35. package/dist/api-B3SCEHPf.cjs +190 -0
  36. package/dist/api-B3SCEHPf.cjs.map +1 -0
  37. package/dist/api-BKIN0s0S.mjs +184 -0
  38. package/dist/api-BKIN0s0S.mjs.map +1 -0
  39. package/dist/build/index.cjs +11 -10
  40. package/dist/build/index.d.cts +7 -0
  41. package/dist/build/index.d.mts +7 -0
  42. package/dist/build/index.mjs +11 -10
  43. package/dist/build/manifests.cjs +1 -1
  44. package/dist/build/manifests.d.cts +13 -0
  45. package/dist/build/manifests.d.mts +13 -0
  46. package/dist/build/manifests.mjs +1 -1
  47. package/dist/build/providerResolver.cjs +1 -1
  48. package/dist/build/providerResolver.d.cts +23 -0
  49. package/dist/build/providerResolver.d.mts +23 -0
  50. package/dist/build/providerResolver.mjs +1 -1
  51. package/dist/build/types.d.cts +3 -0
  52. package/dist/build/types.d.mts +3 -0
  53. package/dist/{build-DyDgu_D1.mjs → build-B8C_qHir.mjs} +15 -13
  54. package/dist/build-B8C_qHir.mjs.map +1 -0
  55. package/dist/{build-CWtHnJMQ.cjs → build-D0Wr49bf.cjs} +15 -13
  56. package/dist/build-D0Wr49bf.cjs.map +1 -0
  57. package/dist/config-Ba-Gbpbc.d.cts +11 -0
  58. package/dist/config-Bq72aj8e.mjs +75 -0
  59. package/dist/config-Bq72aj8e.mjs.map +1 -0
  60. package/dist/config-CFls09Ey.cjs +93 -0
  61. package/dist/config-CFls09Ey.cjs.map +1 -0
  62. package/dist/config-CLEDqKO3.cjs +157 -0
  63. package/dist/config-CLEDqKO3.cjs.map +1 -0
  64. package/dist/config-DBsmMDhf.d.mts +11 -0
  65. package/dist/config-Dp8RonV_.mjs +151 -0
  66. package/dist/config-Dp8RonV_.mjs.map +1 -0
  67. package/dist/config.cjs +4 -2
  68. package/dist/config.d.cts +48 -0
  69. package/dist/config.d.mts +48 -0
  70. package/dist/config.mjs +2 -2
  71. package/dist/dev/index.cjs +10 -9
  72. package/dist/dev/index.d.cts +36 -0
  73. package/dist/dev/index.d.mts +36 -0
  74. package/dist/dev/index.mjs +10 -10
  75. package/dist/{dev-CpA8AQPX.mjs → dev-B734w3L1.mjs} +93 -49
  76. package/dist/dev-B734w3L1.mjs.map +1 -0
  77. package/dist/{dev-CgDYC4o8.cjs → dev-DHqYn8k4.cjs} +71 -21
  78. package/dist/dev-DHqYn8k4.cjs.map +1 -0
  79. package/dist/docker-5d8Yh5_X.cjs +119 -0
  80. package/dist/docker-5d8Yh5_X.cjs.map +1 -0
  81. package/dist/docker-DlUqdFle.mjs +113 -0
  82. package/dist/docker-DlUqdFle.mjs.map +1 -0
  83. package/dist/env-B-OKjgI4.cjs +144 -0
  84. package/dist/env-B-OKjgI4.cjs.map +1 -0
  85. package/dist/env-HfuJRlg5.d.cts +11 -0
  86. package/dist/env-nd-iQPYM.d.mts +11 -0
  87. package/dist/env-tv1HlZlw.mjs +138 -0
  88. package/dist/env-tv1HlZlw.mjs.map +1 -0
  89. package/dist/generators/CronGenerator.cjs +2 -2
  90. package/dist/generators/CronGenerator.d.cts +5 -0
  91. package/dist/generators/CronGenerator.d.mts +5 -0
  92. package/dist/generators/CronGenerator.mjs +2 -2
  93. package/dist/generators/EndpointGenerator.cjs +2 -2
  94. package/dist/generators/EndpointGenerator.d.cts +5 -0
  95. package/dist/generators/EndpointGenerator.d.mts +5 -0
  96. package/dist/generators/EndpointGenerator.mjs +2 -2
  97. package/dist/generators/FunctionGenerator.cjs +2 -2
  98. package/dist/generators/FunctionGenerator.d.cts +5 -0
  99. package/dist/generators/FunctionGenerator.d.mts +5 -0
  100. package/dist/generators/FunctionGenerator.mjs +2 -2
  101. package/dist/generators/Generator.cjs +1 -1
  102. package/dist/generators/Generator.d.cts +4 -0
  103. package/dist/generators/Generator.d.mts +4 -0
  104. package/dist/generators/Generator.mjs +1 -1
  105. package/dist/generators/OpenApiTsGenerator.cjs +1 -1
  106. package/dist/generators/OpenApiTsGenerator.d.cts +44 -0
  107. package/dist/generators/OpenApiTsGenerator.d.mts +44 -0
  108. package/dist/generators/OpenApiTsGenerator.mjs +1 -1
  109. package/dist/generators/SubscriberGenerator.cjs +2 -2
  110. package/dist/generators/SubscriberGenerator.d.cts +5 -0
  111. package/dist/generators/SubscriberGenerator.d.mts +5 -0
  112. package/dist/generators/SubscriberGenerator.mjs +2 -2
  113. package/dist/generators/index.cjs +6 -6
  114. package/dist/generators/index.d.cts +8 -0
  115. package/dist/generators/index.d.mts +8 -0
  116. package/dist/generators/index.mjs +6 -6
  117. package/dist/index-C523No_B.d.mts +64 -0
  118. package/dist/index-DrzN4xkQ.d.cts +64 -0
  119. package/dist/index.cjs +48 -16
  120. package/dist/index.cjs.map +1 -1
  121. package/dist/index.d.cts +1 -0
  122. package/dist/index.d.mts +1 -0
  123. package/dist/index.mjs +48 -16
  124. package/dist/index.mjs.map +1 -1
  125. package/dist/init/generators/config.cjs +3 -0
  126. package/dist/init/generators/config.d.cts +3 -0
  127. package/dist/init/generators/config.d.mts +3 -0
  128. package/dist/init/generators/config.mjs +3 -0
  129. package/dist/init/generators/docker.cjs +3 -0
  130. package/dist/init/generators/docker.d.cts +11 -0
  131. package/dist/init/generators/docker.d.mts +11 -0
  132. package/dist/init/generators/docker.mjs +3 -0
  133. package/dist/init/generators/env.cjs +3 -0
  134. package/dist/init/generators/env.d.cts +3 -0
  135. package/dist/init/generators/env.d.mts +3 -0
  136. package/dist/init/generators/env.mjs +3 -0
  137. package/dist/init/generators/index.cjs +9 -0
  138. package/dist/init/generators/index.d.cts +6 -0
  139. package/dist/init/generators/index.d.mts +6 -0
  140. package/dist/init/generators/index.mjs +6 -0
  141. package/dist/init/generators/models.cjs +3 -0
  142. package/dist/init/generators/models.d.cts +11 -0
  143. package/dist/init/generators/models.d.mts +11 -0
  144. package/dist/init/generators/models.mjs +3 -0
  145. package/dist/init/generators/monorepo.cjs +3 -0
  146. package/dist/init/generators/monorepo.d.cts +11 -0
  147. package/dist/init/generators/monorepo.d.mts +11 -0
  148. package/dist/init/generators/monorepo.mjs +3 -0
  149. package/dist/init/generators/package.cjs +3 -0
  150. package/dist/init/generators/package.d.cts +3 -0
  151. package/dist/init/generators/package.d.mts +3 -0
  152. package/dist/init/generators/package.mjs +3 -0
  153. package/dist/init/generators/source.cjs +3 -0
  154. package/dist/init/generators/source.d.cts +3 -0
  155. package/dist/init/generators/source.d.mts +3 -0
  156. package/dist/init/generators/source.mjs +3 -0
  157. package/dist/init/index.cjs +16 -0
  158. package/dist/init/index.d.cts +17 -0
  159. package/dist/init/index.d.mts +17 -0
  160. package/dist/init/index.mjs +16 -0
  161. package/dist/init/templates/api.cjs +3 -0
  162. package/dist/init/templates/api.d.cts +7 -0
  163. package/dist/init/templates/api.d.mts +7 -0
  164. package/dist/init/templates/api.mjs +3 -0
  165. package/dist/init/templates/index.cjs +10 -0
  166. package/dist/init/templates/index.d.cts +2 -0
  167. package/dist/init/templates/index.d.mts +2 -0
  168. package/dist/init/templates/index.mjs +7 -0
  169. package/dist/init/templates/minimal.cjs +3 -0
  170. package/dist/init/templates/minimal.d.cts +7 -0
  171. package/dist/init/templates/minimal.d.mts +7 -0
  172. package/dist/init/templates/minimal.mjs +3 -0
  173. package/dist/init/templates/serverless.cjs +3 -0
  174. package/dist/init/templates/serverless.d.cts +7 -0
  175. package/dist/init/templates/serverless.d.mts +7 -0
  176. package/dist/init/templates/serverless.mjs +3 -0
  177. package/dist/init/templates/worker.cjs +3 -0
  178. package/dist/init/templates/worker.d.cts +7 -0
  179. package/dist/init/templates/worker.d.mts +7 -0
  180. package/dist/init/templates/worker.mjs +3 -0
  181. package/dist/init/utils.cjs +7 -0
  182. package/dist/init/utils.d.cts +25 -0
  183. package/dist/init/utils.d.mts +25 -0
  184. package/dist/init/utils.mjs +3 -0
  185. package/dist/init-CtOnZn3G.mjs +145 -0
  186. package/dist/init-CtOnZn3G.mjs.map +1 -0
  187. package/dist/init-qLFsWR-R.cjs +151 -0
  188. package/dist/init-qLFsWR-R.cjs.map +1 -0
  189. package/dist/{manifests-C2eMoMUm.mjs → manifests-DIA_2QYd.mjs} +1 -1
  190. package/dist/{manifests-C2eMoMUm.mjs.map → manifests-DIA_2QYd.mjs.map} +1 -1
  191. package/dist/{manifests-CK1VV_pM.cjs → manifests-VJ9-2JpW.cjs} +1 -1
  192. package/dist/{manifests-CK1VV_pM.cjs.map → manifests-VJ9-2JpW.cjs.map} +1 -1
  193. package/dist/minimal-Bdhhpp7v.cjs +93 -0
  194. package/dist/minimal-Bdhhpp7v.cjs.map +1 -0
  195. package/dist/minimal-C4GsE45s.mjs +87 -0
  196. package/dist/minimal-C4GsE45s.mjs.map +1 -0
  197. package/dist/models-DyNwdOcz.cjs +121 -0
  198. package/dist/models-DyNwdOcz.cjs.map +1 -0
  199. package/dist/models-cvNg6Oea.mjs +115 -0
  200. package/dist/models-cvNg6Oea.mjs.map +1 -0
  201. package/dist/monorepo-Cknwzj5C.mjs +184 -0
  202. package/dist/monorepo-Cknwzj5C.mjs.map +1 -0
  203. package/dist/monorepo-sEK8gW59.cjs +190 -0
  204. package/dist/monorepo-sEK8gW59.cjs.map +1 -0
  205. package/dist/{openapi-DhK4b0lB.cjs → openapi-BQWPWyNB.cjs} +4 -4
  206. package/dist/{openapi-DhK4b0lB.cjs.map → openapi-BQWPWyNB.cjs.map} +1 -1
  207. package/dist/{openapi-DRTRGhTt.mjs → openapi-DBX8cJJ8.mjs} +4 -4
  208. package/dist/{openapi-DRTRGhTt.mjs.map → openapi-DBX8cJJ8.mjs.map} +1 -1
  209. package/dist/{openapi-react-query-D9Z7lh0p.cjs → openapi-react-query-DxHjXQvg.cjs} +1 -1
  210. package/dist/{openapi-react-query-D9Z7lh0p.cjs.map → openapi-react-query-DxHjXQvg.cjs.map} +1 -1
  211. package/dist/{openapi-react-query-MEBlYIM1.mjs → openapi-react-query-o7Mp1Jd5.mjs} +1 -1
  212. package/dist/{openapi-react-query-MEBlYIM1.mjs.map → openapi-react-query-o7Mp1Jd5.mjs.map} +1 -1
  213. package/dist/openapi-react-query.cjs +1 -1
  214. package/dist/openapi-react-query.d.cts +11 -0
  215. package/dist/openapi-react-query.d.mts +11 -0
  216. package/dist/openapi-react-query.mjs +1 -1
  217. package/dist/openapi.cjs +5 -5
  218. package/dist/openapi.d.cts +11 -0
  219. package/dist/openapi.d.mts +11 -0
  220. package/dist/openapi.mjs +5 -5
  221. package/dist/package-C7WhWU8m.d.mts +11 -0
  222. package/dist/package-CIfmeuSW.mjs +51 -0
  223. package/dist/package-CIfmeuSW.mjs.map +1 -0
  224. package/dist/package-DvWEMz6z.d.cts +11 -0
  225. package/dist/package-PP-o1nvq.cjs +57 -0
  226. package/dist/package-PP-o1nvq.cjs.map +1 -0
  227. package/dist/{providerResolver-B_TjNF0_.mjs → providerResolver-DEVKngbC.mjs} +1 -1
  228. package/dist/{providerResolver-B_TjNF0_.mjs.map → providerResolver-DEVKngbC.mjs.map} +1 -1
  229. package/dist/{providerResolver-DgvzNfP4.cjs → providerResolver-DOTbN9jo.cjs} +1 -1
  230. package/dist/{providerResolver-DgvzNfP4.cjs.map → providerResolver-DOTbN9jo.cjs.map} +1 -1
  231. package/dist/serverless-DkHBF2vC.mjs +108 -0
  232. package/dist/serverless-DkHBF2vC.mjs.map +1 -0
  233. package/dist/serverless-Yav3GRVz.cjs +114 -0
  234. package/dist/serverless-Yav3GRVz.cjs.map +1 -0
  235. package/dist/source-D6v2BnKT.d.mts +11 -0
  236. package/dist/source-D8fK9qRo.d.cts +11 -0
  237. package/dist/source-DT5Xhiob.cjs +17 -0
  238. package/dist/source-DT5Xhiob.cjs.map +1 -0
  239. package/dist/source-DnaH_MLA.mjs +11 -0
  240. package/dist/source-DnaH_MLA.mjs.map +1 -0
  241. package/dist/templates-CBFUwpBy.mjs +64 -0
  242. package/dist/templates-CBFUwpBy.mjs.map +1 -0
  243. package/dist/templates-DM_rtYYW.cjs +87 -0
  244. package/dist/templates-DM_rtYYW.cjs.map +1 -0
  245. package/dist/types-C4KITv-y.d.mts +51 -0
  246. package/dist/types-Cxl8-uwV.d.mts +129 -0
  247. package/dist/types-DB99_qIy.d.cts +129 -0
  248. package/dist/types-DLFN49M3.d.cts +51 -0
  249. package/dist/types.d.cts +2 -0
  250. package/dist/types.d.mts +2 -0
  251. package/dist/utils-BX3F4fT8.cjs +99 -0
  252. package/dist/utils-BX3F4fT8.cjs.map +1 -0
  253. package/dist/utils-C31-SWHP.mjs +69 -0
  254. package/dist/utils-C31-SWHP.mjs.map +1 -0
  255. package/dist/worker--8O5a3Hv.cjs +150 -0
  256. package/dist/worker--8O5a3Hv.cjs.map +1 -0
  257. package/dist/worker-Jme7uOOJ.mjs +144 -0
  258. package/dist/worker-Jme7uOOJ.mjs.map +1 -0
  259. package/package.json +11 -3
  260. package/src/__tests__/loadEnvFiles.spec.ts +131 -0
  261. package/src/build/index.ts +14 -16
  262. package/src/build/types.ts +5 -0
  263. package/src/config.ts +57 -0
  264. package/src/dev/index.ts +77 -17
  265. package/src/generators/EndpointGenerator.ts +37 -8
  266. package/src/index.ts +26 -0
  267. package/src/init/__tests__/generators.spec.ts +366 -0
  268. package/src/init/__tests__/init.spec.ts +341 -0
  269. package/src/init/__tests__/utils.spec.ts +104 -0
  270. package/src/init/generators/config.ts +192 -0
  271. package/src/init/generators/docker.ts +134 -0
  272. package/src/init/generators/env.ts +182 -0
  273. package/src/init/generators/index.ts +4 -0
  274. package/src/init/generators/models.ts +129 -0
  275. package/src/init/generators/monorepo.ts +211 -0
  276. package/src/init/generators/package.ts +81 -0
  277. package/src/init/generators/source.ts +15 -0
  278. package/src/init/index.ts +206 -0
  279. package/src/init/templates/api.ts +218 -0
  280. package/src/init/templates/index.ts +108 -0
  281. package/src/init/templates/minimal.ts +102 -0
  282. package/src/init/templates/serverless.ts +129 -0
  283. package/src/init/templates/worker.ts +169 -0
  284. package/src/init/utils.ts +98 -0
  285. package/src/types.ts +22 -2
  286. package/tsdown.config.ts +1 -1
  287. package/dist/EndpointGenerator-BxNCkus4.cjs.map +0 -1
  288. package/dist/EndpointGenerator-CzDhG7Or.mjs.map +0 -1
  289. package/dist/build-CWtHnJMQ.cjs.map +0 -1
  290. package/dist/build-DyDgu_D1.mjs.map +0 -1
  291. package/dist/config-AFmFKmU0.mjs +0 -30
  292. package/dist/config-AFmFKmU0.mjs.map +0 -1
  293. package/dist/config-BVIJpAsa.cjs +0 -36
  294. package/dist/config-BVIJpAsa.cjs.map +0 -1
  295. package/dist/dev-CgDYC4o8.cjs.map +0 -1
  296. package/dist/dev-CpA8AQPX.mjs.map +0 -1
  297. /package/dist/{generators-CEKtVh81.cjs → generators-3IemvCLk.cjs} +0 -0
  298. /package/dist/{generators-CsLujGXs.mjs → generators-FNpdfN6J.mjs} +0 -0
package/src/dev/index.ts CHANGED
@@ -1,12 +1,14 @@
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';
6
8
  import fg from 'fast-glob';
7
9
  import { resolveProviders } from '../build/providerResolver';
8
10
  import type { BuildContext, NormalizedTelescopeConfig } from '../build/types';
9
- import { loadConfig } from '../config';
11
+ import { loadConfig, parseModuleConfig } from '../config';
10
12
  import {
11
13
  CronGenerator,
12
14
  EndpointGenerator,
@@ -22,6 +24,39 @@ import type {
22
24
 
23
25
  const logger = console;
24
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
+
25
60
  /**
26
61
  * Check if a port is available
27
62
  * @internal Exported for testing
@@ -79,6 +114,23 @@ export function normalizeTelescopeConfig(
79
114
  return undefined;
80
115
  }
81
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
+
82
134
  // Default to enabled in development mode
83
135
  const isEnabled =
84
136
  config === true || config === undefined || config.enabled !== false;
@@ -106,8 +158,26 @@ export interface DevOptions {
106
158
  }
107
159
 
108
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
+
109
168
  const config = await loadConfig();
110
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
+
111
181
  // Force server provider for dev mode
112
182
  const resolved = resolveProviders(config, { provider: 'server' });
113
183
 
@@ -124,21 +194,11 @@ export async function devCommand(options: DevOptions): Promise<void> {
124
194
  }
125
195
  logger.log(`Using envParser: ${config.envParser}`);
126
196
 
127
- // Parse envParser configuration
128
- const [envParserPath, envParserName] = config.envParser.split('#');
129
- const envParserImportPattern = !envParserName
130
- ? 'envParser'
131
- : envParserName === 'envParser'
132
- ? '{ envParser }'
133
- : `{ ${envParserName} as envParser }`;
134
-
135
- // Parse logger configuration
136
- const [loggerPath, loggerName] = config.logger.split('#');
137
- const loggerImportPattern = !loggerName
138
- ? 'logger'
139
- : loggerName === 'logger'
140
- ? '{ logger }'
141
- : `{ ${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');
142
202
 
143
203
  // Normalize telescope configuration
144
204
  const telescope = normalizeTelescopeConfig(config.telescope);
@@ -336,10 +336,23 @@ export function setupEndpoints(
336
336
  // Generate telescope imports and setup if enabled
337
337
  const telescopeEnabled = context.telescope?.enabled;
338
338
  const telescopeWebSocketEnabled = context.telescope?.websocket;
339
- const telescopeImports = telescopeEnabled
340
- ? `import { Telescope, InMemoryStorage } from '@geekmidas/telescope';
341
- import { createMiddleware, createUI } from '@geekmidas/telescope/hono';`
342
- : '';
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
+ }
343
356
 
344
357
  const telescopeWebSocketSetupCode = telescopeWebSocketEnabled
345
358
  ? `
@@ -375,8 +388,23 @@ import { createMiddleware, createUI } from '@geekmidas/telescope/hono';`
375
388
  `
376
389
  : '';
377
390
 
378
- const telescopeSetup = telescopeEnabled
379
- ? `
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 = `
380
408
  // Setup Telescope for debugging/monitoring
381
409
  const telescopeStorage = new InMemoryStorage({ maxEntries: ${context.telescope!.maxEntries} });
382
410
  const telescope = new Telescope({
@@ -393,8 +421,9 @@ ${telescopeWebSocketSetupCode}
393
421
  // Mount telescope UI
394
422
  const telescopeUI = createUI(telescope);
395
423
  honoApp.route('${context.telescope!.path}', telescopeUI);
396
- `
397
- : '';
424
+ `;
425
+ }
426
+ }
398
427
 
399
428
  const content = `/**
400
429
  * Generated server application
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ import { Command } from 'commander';
4
4
  import pkg from '../package.json' assert { type: 'json' };
5
5
  import { buildCommand } from './build/index.ts';
6
6
  import { devCommand } from './dev/index.ts';
7
+ import { type InitOptions, initCommand } from './init/index.ts';
7
8
  import { generateReactQueryCommand } from './openapi-react-query.ts';
8
9
  import { openapiCommand } from './openapi.ts';
9
10
  import type { LegacyProvider, MainProvider } from './types.ts';
@@ -16,6 +17,31 @@ program
16
17
  .version(pkg.version)
17
18
  .option('--cwd <path>', 'Change working directory');
18
19
 
20
+ program
21
+ .command('init')
22
+ .description('Scaffold a new project')
23
+ .argument('[name]', 'Project name')
24
+ .option(
25
+ '--template <template>',
26
+ 'Project template (minimal, api, serverless, worker)',
27
+ )
28
+ .option('--skip-install', 'Skip dependency installation', false)
29
+ .option('-y, --yes', 'Skip prompts, use defaults', false)
30
+ .option('--monorepo', 'Setup as monorepo with packages/models', false)
31
+ .option('--api-path <path>', 'API app path in monorepo (default: apps/api)')
32
+ .action(async (name: string | undefined, options: InitOptions) => {
33
+ try {
34
+ const globalOptions = program.opts();
35
+ if (globalOptions.cwd) {
36
+ process.chdir(globalOptions.cwd);
37
+ }
38
+ await initCommand(name, options);
39
+ } catch (error) {
40
+ console.error('Init failed:', (error as Error).message);
41
+ process.exit(1);
42
+ }
43
+ });
44
+
19
45
  program
20
46
  .command('build')
21
47
  .description('Build handlers from endpoints, functions, and crons')
@@ -0,0 +1,366 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { generateConfigFiles } from '../generators/config.js';
3
+ import { generateDockerFiles } from '../generators/docker.js';
4
+ import { generateEnvFiles } from '../generators/env.js';
5
+ import { generateModelsPackage } from '../generators/models.js';
6
+ import { generateMonorepoFiles } from '../generators/monorepo.js';
7
+ import { generatePackageJson } from '../generators/package.js';
8
+ import type { TemplateOptions } from '../templates/index.js';
9
+ import { minimalTemplate } from '../templates/minimal.js';
10
+ import { serverlessTemplate } from '../templates/serverless.js';
11
+ import { workerTemplate } from '../templates/worker.js';
12
+
13
+ const baseOptions: TemplateOptions = {
14
+ name: 'test-project',
15
+ template: 'minimal',
16
+ telescope: true,
17
+ database: true,
18
+ routeStyle: 'file-based',
19
+ monorepo: false,
20
+ apiPath: '',
21
+ };
22
+
23
+ describe('generatePackageJson', () => {
24
+ it('should generate package.json with correct name', () => {
25
+ const files = generatePackageJson(baseOptions, minimalTemplate);
26
+ expect(files).toHaveLength(1);
27
+ expect(files[0].path).toBe('package.json');
28
+
29
+ const pkg = JSON.parse(files[0].content);
30
+ expect(pkg.name).toBe('test-project');
31
+ expect(pkg.type).toBe('module');
32
+ expect(pkg.private).toBe(true);
33
+ });
34
+
35
+ it('should include telescope when enabled', () => {
36
+ const files = generatePackageJson(baseOptions, minimalTemplate);
37
+ const pkg = JSON.parse(files[0].content);
38
+ expect(pkg.dependencies['@geekmidas/telescope']).toBe('workspace:*');
39
+ });
40
+
41
+ it('should include database dependencies when enabled', () => {
42
+ const files = generatePackageJson(baseOptions, minimalTemplate);
43
+ const pkg = JSON.parse(files[0].content);
44
+ expect(pkg.dependencies['@geekmidas/db']).toBe('workspace:*');
45
+ expect(pkg.dependencies['kysely']).toBeDefined();
46
+ expect(pkg.dependencies['pg']).toBeDefined();
47
+ });
48
+
49
+ it('should exclude telescope when disabled', () => {
50
+ const options = { ...baseOptions, telescope: false };
51
+ const files = generatePackageJson(options, minimalTemplate);
52
+ const pkg = JSON.parse(files[0].content);
53
+ expect(pkg.dependencies['@geekmidas/telescope']).toBeUndefined();
54
+ });
55
+
56
+ it('should use workspace:* for @geekmidas packages', () => {
57
+ const files = generatePackageJson(baseOptions, minimalTemplate);
58
+ const pkg = JSON.parse(files[0].content);
59
+ expect(pkg.dependencies['@geekmidas/constructs']).toBe('workspace:*');
60
+ expect(pkg.dependencies['@geekmidas/envkit']).toBe('workspace:*');
61
+ expect(pkg.dependencies['@geekmidas/logger']).toBe('workspace:*');
62
+ });
63
+
64
+ it('should use tilde versions for external packages', () => {
65
+ const files = generatePackageJson(baseOptions, minimalTemplate);
66
+ const pkg = JSON.parse(files[0].content);
67
+ expect(pkg.dependencies['hono']).toMatch(/^~/);
68
+ expect(pkg.dependencies['pino']).toMatch(/^~/);
69
+ expect(pkg.devDependencies['typescript']).toMatch(/^~/);
70
+ });
71
+
72
+ it('should include biome and turbo for non-monorepo', () => {
73
+ const files = generatePackageJson(baseOptions, minimalTemplate);
74
+ const pkg = JSON.parse(files[0].content);
75
+ expect(pkg.devDependencies['@biomejs/biome']).toBeDefined();
76
+ expect(pkg.devDependencies['turbo']).toBeDefined();
77
+ expect(pkg.scripts['lint']).toBeDefined();
78
+ expect(pkg.scripts['fmt']).toBeDefined();
79
+ });
80
+
81
+ it('should exclude biome and turbo for monorepo apps', () => {
82
+ const options: TemplateOptions = {
83
+ ...baseOptions,
84
+ monorepo: true,
85
+ apiPath: 'apps/api',
86
+ };
87
+ const files = generatePackageJson(options, minimalTemplate);
88
+ const pkg = JSON.parse(files[0].content);
89
+ expect(pkg.devDependencies['@biomejs/biome']).toBeUndefined();
90
+ expect(pkg.devDependencies['turbo']).toBeUndefined();
91
+ expect(pkg.scripts['lint']).toBeUndefined();
92
+ expect(pkg.scripts['fmt']).toBeUndefined();
93
+ });
94
+
95
+ it('should include models package for monorepo apps', () => {
96
+ const options: TemplateOptions = {
97
+ ...baseOptions,
98
+ monorepo: true,
99
+ apiPath: 'apps/api',
100
+ };
101
+ const files = generatePackageJson(options, minimalTemplate);
102
+ const pkg = JSON.parse(files[0].content);
103
+ expect(pkg.dependencies['@test-project/models']).toBe('workspace:*');
104
+ expect(pkg.dependencies['zod']).toBeUndefined(); // zod is in models
105
+ });
106
+
107
+ it('should use scoped package name for monorepo apps', () => {
108
+ const options: TemplateOptions = {
109
+ ...baseOptions,
110
+ monorepo: true,
111
+ apiPath: 'apps/api',
112
+ };
113
+ const files = generatePackageJson(options, minimalTemplate);
114
+ const pkg = JSON.parse(files[0].content);
115
+ expect(pkg.name).toBe('@test-project/api');
116
+ });
117
+ });
118
+
119
+ describe('generateConfigFiles', () => {
120
+ it('should generate gkm.config.ts and tsconfig.json for non-monorepo', () => {
121
+ const files = generateConfigFiles(baseOptions, minimalTemplate);
122
+ const paths = files.map((f) => f.path);
123
+ expect(paths).toContain('gkm.config.ts');
124
+ expect(paths).toContain('tsconfig.json');
125
+ expect(paths).toContain('biome.json');
126
+ expect(paths).toContain('turbo.json');
127
+ });
128
+
129
+ it('should only generate gkm.config.ts and tsconfig.json for monorepo', () => {
130
+ const options: TemplateOptions = {
131
+ ...baseOptions,
132
+ monorepo: true,
133
+ apiPath: 'apps/api',
134
+ };
135
+ const files = generateConfigFiles(options, minimalTemplate);
136
+ const paths = files.map((f) => f.path);
137
+ expect(paths).toContain('gkm.config.ts');
138
+ expect(paths).toContain('tsconfig.json');
139
+ expect(paths).not.toContain('biome.json');
140
+ expect(paths).not.toContain('turbo.json');
141
+ });
142
+
143
+ it('should include telescope config when enabled', () => {
144
+ const files = generateConfigFiles(baseOptions, minimalTemplate);
145
+ const gkmConfig = files.find((f) => f.path === 'gkm.config.ts');
146
+ expect(gkmConfig?.content).toContain('telescope');
147
+ });
148
+
149
+ it('should include functions config for serverless template', () => {
150
+ const options = { ...baseOptions, template: 'serverless' as const };
151
+ const files = generateConfigFiles(options, serverlessTemplate);
152
+ const gkmConfig = files.find((f) => f.path === 'gkm.config.ts');
153
+ expect(gkmConfig?.content).toContain('functions');
154
+ });
155
+
156
+ it('should include paths config for monorepo apps', () => {
157
+ const options: TemplateOptions = {
158
+ ...baseOptions,
159
+ monorepo: true,
160
+ apiPath: 'apps/api',
161
+ };
162
+ const files = generateConfigFiles(options, minimalTemplate);
163
+ const tsConfig = files.find((f) => f.path === 'tsconfig.json');
164
+ const config = JSON.parse(tsConfig!.content);
165
+ expect(config.extends).toBe('../../tsconfig.json');
166
+ expect(config.compilerOptions.paths).toBeDefined();
167
+ expect(config.compilerOptions.paths['@test-project/*']).toBeDefined();
168
+ });
169
+ });
170
+
171
+ describe('generateEnvFiles', () => {
172
+ it('should generate all env files for non-monorepo', () => {
173
+ const files = generateEnvFiles(baseOptions, minimalTemplate);
174
+ const paths = files.map((f) => f.path);
175
+ expect(paths).toContain('.env');
176
+ expect(paths).toContain('.env.example');
177
+ expect(paths).toContain('.env.development');
178
+ expect(paths).toContain('.env.test');
179
+ expect(paths).toContain('.gitignore');
180
+ });
181
+
182
+ it('should not generate .gitignore for monorepo', () => {
183
+ const options: TemplateOptions = {
184
+ ...baseOptions,
185
+ monorepo: true,
186
+ apiPath: 'apps/api',
187
+ };
188
+ const files = generateEnvFiles(options, minimalTemplate);
189
+ const paths = files.map((f) => f.path);
190
+ expect(paths).not.toContain('.gitignore');
191
+ });
192
+
193
+ it('should include DATABASE_URL when database is enabled', () => {
194
+ const files = generateEnvFiles(baseOptions, minimalTemplate);
195
+ const envFile = files.find((f) => f.path === '.env');
196
+ expect(envFile?.content).toContain('DATABASE_URL');
197
+ });
198
+
199
+ it('should include RABBITMQ_URL for worker template', () => {
200
+ const options = { ...baseOptions, template: 'worker' as const };
201
+ const files = generateEnvFiles(options, workerTemplate);
202
+ const envFile = files.find((f) => f.path === '.env');
203
+ expect(envFile?.content).toContain('RABBITMQ_URL');
204
+ });
205
+ });
206
+
207
+ describe('generateDockerFiles', () => {
208
+ it('should generate docker-compose.yml', () => {
209
+ const files = generateDockerFiles(baseOptions, minimalTemplate);
210
+ expect(files).toHaveLength(1);
211
+ expect(files[0].path).toBe('docker-compose.yml');
212
+ });
213
+
214
+ it('should include postgres when database is enabled', () => {
215
+ const files = generateDockerFiles(baseOptions, minimalTemplate);
216
+ expect(files[0].content).toContain('postgres');
217
+ expect(files[0].content).toContain('5432');
218
+ });
219
+
220
+ it('should include redis', () => {
221
+ const files = generateDockerFiles(baseOptions, minimalTemplate);
222
+ expect(files[0].content).toContain('redis');
223
+ expect(files[0].content).toContain('6379');
224
+ });
225
+
226
+ it('should include serverless-redis-http for serverless template', () => {
227
+ const options = { ...baseOptions, template: 'serverless' as const };
228
+ const files = generateDockerFiles(options, serverlessTemplate);
229
+ expect(files[0].content).toContain('hiett/serverless-redis-http');
230
+ expect(files[0].content).toContain('8079');
231
+ });
232
+
233
+ it('should include rabbitmq for worker template', () => {
234
+ const options = { ...baseOptions, template: 'worker' as const };
235
+ const files = generateDockerFiles(options, workerTemplate);
236
+ expect(files[0].content).toContain('rabbitmq');
237
+ expect(files[0].content).toContain('5672');
238
+ expect(files[0].content).toContain('15672');
239
+ });
240
+ });
241
+
242
+ describe('generateMonorepoFiles', () => {
243
+ it('should return empty array for non-monorepo', () => {
244
+ const files = generateMonorepoFiles(baseOptions, minimalTemplate);
245
+ expect(files).toHaveLength(0);
246
+ });
247
+
248
+ it('should generate root files for monorepo', () => {
249
+ const options: TemplateOptions = {
250
+ ...baseOptions,
251
+ monorepo: true,
252
+ apiPath: 'apps/api',
253
+ };
254
+ const files = generateMonorepoFiles(options, minimalTemplate);
255
+ const paths = files.map((f) => f.path);
256
+ expect(paths).toContain('package.json');
257
+ expect(paths).toContain('pnpm-workspace.yaml');
258
+ expect(paths).toContain('tsconfig.json');
259
+ expect(paths).toContain('biome.json');
260
+ expect(paths).toContain('turbo.json');
261
+ expect(paths).toContain('.gitignore');
262
+ });
263
+
264
+ it('should include correct workspace paths', () => {
265
+ const options: TemplateOptions = {
266
+ ...baseOptions,
267
+ monorepo: true,
268
+ apiPath: 'apps/api',
269
+ };
270
+ const files = generateMonorepoFiles(options, minimalTemplate);
271
+ const workspace = files.find((f) => f.path === 'pnpm-workspace.yaml');
272
+ expect(workspace?.content).toContain("'apps/*'");
273
+ expect(workspace?.content).toContain("'packages/*'");
274
+ });
275
+
276
+ it('should include root scripts', () => {
277
+ const options: TemplateOptions = {
278
+ ...baseOptions,
279
+ monorepo: true,
280
+ apiPath: 'apps/api',
281
+ };
282
+ const files = generateMonorepoFiles(options, minimalTemplate);
283
+ const pkgJson = files.find((f) => f.path === 'package.json');
284
+ const pkg = JSON.parse(pkgJson!.content);
285
+ expect(pkg.scripts.dev).toBe('turbo dev');
286
+ expect(pkg.scripts.build).toBe('turbo build');
287
+ expect(pkg.scripts.lint).toBe('biome lint .');
288
+ });
289
+ });
290
+
291
+ describe('generateModelsPackage', () => {
292
+ it('should return empty array for non-monorepo', () => {
293
+ const files = generateModelsPackage(baseOptions);
294
+ expect(files).toHaveLength(0);
295
+ });
296
+
297
+ it('should generate models package for monorepo', () => {
298
+ const options: TemplateOptions = {
299
+ ...baseOptions,
300
+ monorepo: true,
301
+ apiPath: 'apps/api',
302
+ };
303
+ const files = generateModelsPackage(options);
304
+ const paths = files.map((f) => f.path);
305
+ expect(paths).toContain('packages/models/package.json');
306
+ expect(paths).toContain('packages/models/tsconfig.json');
307
+ expect(paths).toContain('packages/models/src/index.ts');
308
+ });
309
+
310
+ it('should use correct package name', () => {
311
+ const options: TemplateOptions = {
312
+ ...baseOptions,
313
+ monorepo: true,
314
+ apiPath: 'apps/api',
315
+ };
316
+ const files = generateModelsPackage(options);
317
+ const pkgJson = files.find(
318
+ (f) => f.path === 'packages/models/package.json',
319
+ );
320
+ const pkg = JSON.parse(pkgJson!.content);
321
+ expect(pkg.name).toBe('@test-project/models');
322
+ });
323
+
324
+ it('should include zod as dependency', () => {
325
+ const options: TemplateOptions = {
326
+ ...baseOptions,
327
+ monorepo: true,
328
+ apiPath: 'apps/api',
329
+ };
330
+ const files = generateModelsPackage(options);
331
+ const pkgJson = files.find(
332
+ (f) => f.path === 'packages/models/package.json',
333
+ );
334
+ const pkg = JSON.parse(pkgJson!.content);
335
+ expect(pkg.dependencies.zod).toBeDefined();
336
+ });
337
+
338
+ it('should include example schemas', () => {
339
+ const options: TemplateOptions = {
340
+ ...baseOptions,
341
+ monorepo: true,
342
+ apiPath: 'apps/api',
343
+ };
344
+ const files = generateModelsPackage(options);
345
+ const indexTs = files.find(
346
+ (f) => f.path === 'packages/models/src/index.ts',
347
+ );
348
+ expect(indexTs?.content).toContain('userSchema');
349
+ expect(indexTs?.content).toContain('paginationSchema');
350
+ expect(indexTs?.content).toContain("import { z } from 'zod'");
351
+ });
352
+
353
+ it('should extend root tsconfig', () => {
354
+ const options: TemplateOptions = {
355
+ ...baseOptions,
356
+ monorepo: true,
357
+ apiPath: 'apps/api',
358
+ };
359
+ const files = generateModelsPackage(options);
360
+ const tsConfig = files.find(
361
+ (f) => f.path === 'packages/models/tsconfig.json',
362
+ );
363
+ const config = JSON.parse(tsConfig!.content);
364
+ expect(config.extends).toBe('../../tsconfig.json');
365
+ });
366
+ });