@geekmidas/cli 0.6.2 → 0.8.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 (274) hide show
  1. package/dist/config.d.cts +1 -1
  2. package/dist/config.d.mts +1 -1
  3. package/dist/index.cjs +2583 -34
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.mjs +2578 -29
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/openapi--vOy9mo4.mjs +978 -0
  8. package/dist/openapi--vOy9mo4.mjs.map +1 -0
  9. package/dist/openapi-CHhTPief.cjs +1014 -0
  10. package/dist/openapi-CHhTPief.cjs.map +1 -0
  11. package/dist/{openapi-react-query-_-B3s8v_.mjs → openapi-react-query-CcciaVu5.mjs} +1 -1
  12. package/dist/{openapi-react-query-_-B3s8v_.mjs.map → openapi-react-query-CcciaVu5.mjs.map} +1 -1
  13. package/dist/{openapi-react-query-Cp-w8_05.cjs → openapi-react-query-o5iMi8tz.cjs} +1 -1
  14. package/dist/{openapi-react-query-Cp-w8_05.cjs.map → openapi-react-query-o5iMi8tz.cjs.map} +1 -1
  15. package/dist/openapi-react-query.cjs +1 -1
  16. package/dist/openapi-react-query.mjs +1 -1
  17. package/dist/openapi.cjs +1 -4
  18. package/dist/openapi.d.cts +1 -1
  19. package/dist/openapi.d.mts +1 -1
  20. package/dist/openapi.mjs +1 -4
  21. package/dist/{types-KmjzMgu8.d.cts → types-DXgiA1sF.d.mts} +58 -53
  22. package/dist/{types-Bi7VzDUZ.d.mts → types-b-vwGpqc.d.cts} +58 -53
  23. package/package.json +6 -6
  24. package/src/__tests__/EndpointGenerator.hooks.spec.ts +204 -0
  25. package/src/__tests__/normalizeHooksConfig.spec.ts +63 -0
  26. package/src/build/index.ts +8 -1
  27. package/src/build/types.ts +19 -0
  28. package/src/dev/index.ts +153 -37
  29. package/src/generators/EndpointGenerator.ts +72 -5
  30. package/src/generators/__tests__/EndpointGenerator.spec.ts +1 -1
  31. package/src/init/generators/config.ts +6 -1
  32. package/src/init/generators/package.ts +5 -1
  33. package/src/init/index.ts +9 -1
  34. package/src/init/templates/api.ts +31 -0
  35. package/src/init/templates/index.ts +1 -0
  36. package/src/init/templates/minimal.ts +83 -0
  37. package/src/types.ts +57 -0
  38. package/tsdown.config.ts +6 -0
  39. package/dist/CronGenerator-CCRYptuT.mjs +0 -55
  40. package/dist/CronGenerator-CCRYptuT.mjs.map +0 -1
  41. package/dist/CronGenerator-D4TWXQbh.cjs +0 -61
  42. package/dist/CronGenerator-D4TWXQbh.cjs.map +0 -1
  43. package/dist/CronGenerator-DWS3CCZt.d.cts +0 -14
  44. package/dist/CronGenerator-DZjdkEjI.d.mts +0 -14
  45. package/dist/EndpointGenerator-DGivkPLT.mjs +0 -335
  46. package/dist/EndpointGenerator-DGivkPLT.mjs.map +0 -1
  47. package/dist/EndpointGenerator-Dh7kMtuL.d.mts +0 -19
  48. package/dist/EndpointGenerator-npWEDoK2.cjs +0 -341
  49. package/dist/EndpointGenerator-npWEDoK2.cjs.map +0 -1
  50. package/dist/EndpointGenerator-zBsie_7s.d.cts +0 -19
  51. package/dist/FunctionGenerator-BmDHo27U.d.mts +0 -14
  52. package/dist/FunctionGenerator-CVk0h8tO.mjs +0 -54
  53. package/dist/FunctionGenerator-CVk0h8tO.mjs.map +0 -1
  54. package/dist/FunctionGenerator-DXjXBxUd.d.cts +0 -14
  55. package/dist/FunctionGenerator-DYTnyr4c.cjs +0 -60
  56. package/dist/FunctionGenerator-DYTnyr4c.cjs.map +0 -1
  57. package/dist/Generator-BGY-2dgI.d.cts +0 -27
  58. package/dist/Generator-CDt4pB3W.mjs +0 -41
  59. package/dist/Generator-CDt4pB3W.mjs.map +0 -1
  60. package/dist/Generator-CLVplqm2.cjs +0 -47
  61. package/dist/Generator-CLVplqm2.cjs.map +0 -1
  62. package/dist/Generator-yi9DH5TN.d.mts +0 -27
  63. package/dist/OpenApiTsGenerator-BVS4pOH7.mjs +0 -495
  64. package/dist/OpenApiTsGenerator-BVS4pOH7.mjs.map +0 -1
  65. package/dist/OpenApiTsGenerator-gPIIyppX.cjs +0 -501
  66. package/dist/OpenApiTsGenerator-gPIIyppX.cjs.map +0 -1
  67. package/dist/SubscriberGenerator-Bb-z3Kvx.d.cts +0 -15
  68. package/dist/SubscriberGenerator-CwsXqCpS.d.mts +0 -15
  69. package/dist/SubscriberGenerator-DABaJXML.mjs +0 -200
  70. package/dist/SubscriberGenerator-DABaJXML.mjs.map +0 -1
  71. package/dist/SubscriberGenerator-D_zpNGFr.cjs +0 -206
  72. package/dist/SubscriberGenerator-D_zpNGFr.cjs.map +0 -1
  73. package/dist/api-Bp5TIl1R.mjs +0 -167
  74. package/dist/api-Bp5TIl1R.mjs.map +0 -1
  75. package/dist/api-D4W9-tdZ.cjs +0 -173
  76. package/dist/api-D4W9-tdZ.cjs.map +0 -1
  77. package/dist/build/index.cjs +0 -15
  78. package/dist/build/index.d.cts +0 -7
  79. package/dist/build/index.d.mts +0 -7
  80. package/dist/build/index.mjs +0 -15
  81. package/dist/build/manifests.cjs +0 -4
  82. package/dist/build/manifests.d.cts +0 -13
  83. package/dist/build/manifests.d.mts +0 -13
  84. package/dist/build/manifests.mjs +0 -3
  85. package/dist/build/providerResolver.cjs +0 -5
  86. package/dist/build/providerResolver.d.cts +0 -23
  87. package/dist/build/providerResolver.d.mts +0 -23
  88. package/dist/build/providerResolver.mjs +0 -3
  89. package/dist/build/types.cjs +0 -0
  90. package/dist/build/types.d.cts +0 -3
  91. package/dist/build/types.d.mts +0 -3
  92. package/dist/build/types.mjs +0 -0
  93. package/dist/build-Cu6Mi0Lf.mjs +0 -87
  94. package/dist/build-Cu6Mi0Lf.mjs.map +0 -1
  95. package/dist/build-wmt8ZcmA.cjs +0 -93
  96. package/dist/build-wmt8ZcmA.cjs.map +0 -1
  97. package/dist/config-BP1IZynR.cjs +0 -168
  98. package/dist/config-BP1IZynR.cjs.map +0 -1
  99. package/dist/config-CIzRhm_D.d.mts +0 -11
  100. package/dist/config-CvehIYsb.d.cts +0 -11
  101. package/dist/config-UCK12Lrr.mjs +0 -162
  102. package/dist/config-UCK12Lrr.mjs.map +0 -1
  103. package/dist/dev/index.cjs +0 -17
  104. package/dist/dev/index.d.cts +0 -36
  105. package/dist/dev/index.d.mts +0 -36
  106. package/dist/dev/index.mjs +0 -13
  107. package/dist/dev-BBPWSllq.mjs +0 -348
  108. package/dist/dev-BBPWSllq.mjs.map +0 -1
  109. package/dist/dev-C2lCgE53.cjs +0 -378
  110. package/dist/dev-C2lCgE53.cjs.map +0 -1
  111. package/dist/docker-2-ipZDOJ.cjs +0 -119
  112. package/dist/docker-2-ipZDOJ.cjs.map +0 -1
  113. package/dist/docker-31GNwU3F.mjs +0 -113
  114. package/dist/docker-31GNwU3F.mjs.map +0 -1
  115. package/dist/env-CQ3hXAAW.d.mts +0 -11
  116. package/dist/env-CS0jvg7k.cjs +0 -144
  117. package/dist/env-CS0jvg7k.cjs.map +0 -1
  118. package/dist/env-D4YFgMqo.d.cts +0 -11
  119. package/dist/env-DEeVOvVu.mjs +0 -138
  120. package/dist/env-DEeVOvVu.mjs.map +0 -1
  121. package/dist/generators/CronGenerator.cjs +0 -4
  122. package/dist/generators/CronGenerator.d.cts +0 -5
  123. package/dist/generators/CronGenerator.d.mts +0 -5
  124. package/dist/generators/CronGenerator.mjs +0 -4
  125. package/dist/generators/EndpointGenerator.cjs +0 -4
  126. package/dist/generators/EndpointGenerator.d.cts +0 -5
  127. package/dist/generators/EndpointGenerator.d.mts +0 -5
  128. package/dist/generators/EndpointGenerator.mjs +0 -4
  129. package/dist/generators/FunctionGenerator.cjs +0 -4
  130. package/dist/generators/FunctionGenerator.d.cts +0 -5
  131. package/dist/generators/FunctionGenerator.d.mts +0 -5
  132. package/dist/generators/FunctionGenerator.mjs +0 -4
  133. package/dist/generators/Generator.cjs +0 -3
  134. package/dist/generators/Generator.d.cts +0 -4
  135. package/dist/generators/Generator.d.mts +0 -4
  136. package/dist/generators/Generator.mjs +0 -3
  137. package/dist/generators/OpenApiTsGenerator.cjs +0 -3
  138. package/dist/generators/OpenApiTsGenerator.d.cts +0 -44
  139. package/dist/generators/OpenApiTsGenerator.d.mts +0 -44
  140. package/dist/generators/OpenApiTsGenerator.mjs +0 -3
  141. package/dist/generators/SubscriberGenerator.cjs +0 -4
  142. package/dist/generators/SubscriberGenerator.d.cts +0 -5
  143. package/dist/generators/SubscriberGenerator.d.mts +0 -5
  144. package/dist/generators/SubscriberGenerator.mjs +0 -4
  145. package/dist/generators/index.cjs +0 -12
  146. package/dist/generators/index.d.cts +0 -8
  147. package/dist/generators/index.d.mts +0 -8
  148. package/dist/generators/index.mjs +0 -8
  149. package/dist/generators-3IemvCLk.cjs +0 -0
  150. package/dist/generators-FNpdfN6J.mjs +0 -0
  151. package/dist/index-DG6xNQMH.d.cts +0 -81
  152. package/dist/index-DZgrOOOW.d.mts +0 -81
  153. package/dist/init/generators/config.cjs +0 -3
  154. package/dist/init/generators/config.d.cts +0 -3
  155. package/dist/init/generators/config.d.mts +0 -3
  156. package/dist/init/generators/config.mjs +0 -3
  157. package/dist/init/generators/docker.cjs +0 -3
  158. package/dist/init/generators/docker.d.cts +0 -11
  159. package/dist/init/generators/docker.d.mts +0 -11
  160. package/dist/init/generators/docker.mjs +0 -3
  161. package/dist/init/generators/env.cjs +0 -3
  162. package/dist/init/generators/env.d.cts +0 -3
  163. package/dist/init/generators/env.d.mts +0 -3
  164. package/dist/init/generators/env.mjs +0 -3
  165. package/dist/init/generators/index.cjs +0 -14
  166. package/dist/init/generators/index.d.cts +0 -6
  167. package/dist/init/generators/index.d.mts +0 -6
  168. package/dist/init/generators/index.mjs +0 -11
  169. package/dist/init/generators/models.cjs +0 -3
  170. package/dist/init/generators/models.d.cts +0 -11
  171. package/dist/init/generators/models.d.mts +0 -11
  172. package/dist/init/generators/models.mjs +0 -3
  173. package/dist/init/generators/monorepo.cjs +0 -3
  174. package/dist/init/generators/monorepo.d.cts +0 -11
  175. package/dist/init/generators/monorepo.d.mts +0 -11
  176. package/dist/init/generators/monorepo.mjs +0 -3
  177. package/dist/init/generators/package.cjs +0 -8
  178. package/dist/init/generators/package.d.cts +0 -3
  179. package/dist/init/generators/package.d.mts +0 -3
  180. package/dist/init/generators/package.mjs +0 -8
  181. package/dist/init/generators/source.cjs +0 -3
  182. package/dist/init/generators/source.d.cts +0 -3
  183. package/dist/init/generators/source.d.mts +0 -3
  184. package/dist/init/generators/source.mjs +0 -3
  185. package/dist/init/index.cjs +0 -16
  186. package/dist/init/index.d.cts +0 -17
  187. package/dist/init/index.d.mts +0 -17
  188. package/dist/init/index.mjs +0 -16
  189. package/dist/init/templates/api.cjs +0 -3
  190. package/dist/init/templates/api.d.cts +0 -7
  191. package/dist/init/templates/api.d.mts +0 -7
  192. package/dist/init/templates/api.mjs +0 -3
  193. package/dist/init/templates/index.cjs +0 -12
  194. package/dist/init/templates/index.d.cts +0 -2
  195. package/dist/init/templates/index.d.mts +0 -2
  196. package/dist/init/templates/index.mjs +0 -7
  197. package/dist/init/templates/minimal.cjs +0 -3
  198. package/dist/init/templates/minimal.d.cts +0 -7
  199. package/dist/init/templates/minimal.d.mts +0 -7
  200. package/dist/init/templates/minimal.mjs +0 -3
  201. package/dist/init/templates/serverless.cjs +0 -3
  202. package/dist/init/templates/serverless.d.cts +0 -7
  203. package/dist/init/templates/serverless.d.mts +0 -7
  204. package/dist/init/templates/serverless.mjs +0 -3
  205. package/dist/init/templates/worker.cjs +0 -3
  206. package/dist/init/templates/worker.d.cts +0 -7
  207. package/dist/init/templates/worker.d.mts +0 -7
  208. package/dist/init/templates/worker.mjs +0 -3
  209. package/dist/init/utils.cjs +0 -7
  210. package/dist/init/utils.d.cts +0 -25
  211. package/dist/init/utils.d.mts +0 -25
  212. package/dist/init/utils.mjs +0 -3
  213. package/dist/init-BMA7xi8r.mjs +0 -161
  214. package/dist/init-BMA7xi8r.mjs.map +0 -1
  215. package/dist/init-D-7WEk-b.cjs +0 -167
  216. package/dist/init-D-7WEk-b.cjs.map +0 -1
  217. package/dist/manifests-BNKG6AXf.mjs +0 -68
  218. package/dist/manifests-BNKG6AXf.mjs.map +0 -1
  219. package/dist/manifests-D13Ej8AE.cjs +0 -80
  220. package/dist/manifests-D13Ej8AE.cjs.map +0 -1
  221. package/dist/minimal-BkyASH_C.mjs +0 -93
  222. package/dist/minimal-BkyASH_C.mjs.map +0 -1
  223. package/dist/minimal-CSFggzdH.cjs +0 -99
  224. package/dist/minimal-CSFggzdH.cjs.map +0 -1
  225. package/dist/models-BWlDfviw.mjs +0 -115
  226. package/dist/models-BWlDfviw.mjs.map +0 -1
  227. package/dist/models-BapGSoHC.cjs +0 -121
  228. package/dist/models-BapGSoHC.cjs.map +0 -1
  229. package/dist/monorepo-BBOWhkcd.mjs +0 -184
  230. package/dist/monorepo-BBOWhkcd.mjs.map +0 -1
  231. package/dist/monorepo-CFtxHeDh.cjs +0 -190
  232. package/dist/monorepo-CFtxHeDh.cjs.map +0 -1
  233. package/dist/openapi-DA9RkPJl.mjs +0 -74
  234. package/dist/openapi-DA9RkPJl.mjs.map +0 -1
  235. package/dist/openapi-DZH6RQHk.cjs +0 -98
  236. package/dist/openapi-DZH6RQHk.cjs.map +0 -1
  237. package/dist/package-6h-7QfJZ.d.cts +0 -11
  238. package/dist/package-BCe_KvGv.d.mts +0 -11
  239. package/dist/package-C3If80n1.mjs +0 -57
  240. package/dist/package-C3If80n1.mjs.map +0 -1
  241. package/dist/package-Dk8IMBOB.cjs +0 -62
  242. package/dist/package-Dk8IMBOB.cjs.map +0 -1
  243. package/dist/providerResolver-DEVKngbC.mjs +0 -96
  244. package/dist/providerResolver-DEVKngbC.mjs.map +0 -1
  245. package/dist/providerResolver-DOTbN9jo.cjs +0 -114
  246. package/dist/providerResolver-DOTbN9jo.cjs.map +0 -1
  247. package/dist/serverless-AGOS-l3G.cjs +0 -119
  248. package/dist/serverless-AGOS-l3G.cjs.map +0 -1
  249. package/dist/serverless-D5HjJByU.mjs +0 -113
  250. package/dist/serverless-D5HjJByU.mjs.map +0 -1
  251. package/dist/source-C1cyfHcF.cjs +0 -17
  252. package/dist/source-C1cyfHcF.cjs.map +0 -1
  253. package/dist/source-C3LiNUV9.d.mts +0 -11
  254. package/dist/source-CkQHBpwu.mjs +0 -11
  255. package/dist/source-CkQHBpwu.mjs.map +0 -1
  256. package/dist/source-Dtcjbokc.d.cts +0 -11
  257. package/dist/templates-C0EMmhwb.mjs +0 -88
  258. package/dist/templates-C0EMmhwb.mjs.map +0 -1
  259. package/dist/templates-CbgQ9dw0.cjs +0 -123
  260. package/dist/templates-CbgQ9dw0.cjs.map +0 -1
  261. package/dist/types-D2xYkOal.d.mts +0 -51
  262. package/dist/types-DA-r8HWZ.d.cts +0 -51
  263. package/dist/types.cjs +0 -0
  264. package/dist/types.d.cts +0 -2
  265. package/dist/types.d.mts +0 -2
  266. package/dist/types.mjs +0 -0
  267. package/dist/utils-CKEzCxc1.mjs +0 -69
  268. package/dist/utils-CKEzCxc1.mjs.map +0 -1
  269. package/dist/utils-DSdN2MTt.cjs +0 -99
  270. package/dist/utils-DSdN2MTt.cjs.map +0 -1
  271. package/dist/worker-CGhlqNH-.cjs +0 -156
  272. package/dist/worker-CGhlqNH-.cjs.map +0 -1
  273. package/dist/worker-CiP420As.mjs +0 -150
  274. package/dist/worker-CiP420As.mjs.map +0 -1
@@ -0,0 +1,1014 @@
1
+ const require_chunk = require('./chunk-CUT6urMc.cjs');
2
+ const require_config = require('./config-CFls09Ey.cjs');
3
+ const path = require_chunk.__toESM(require("path"));
4
+ const node_fs_promises = require_chunk.__toESM(require("node:fs/promises"));
5
+ const node_path = require_chunk.__toESM(require("node:path"));
6
+ const fast_glob = require_chunk.__toESM(require("fast-glob"));
7
+ const lodash_kebabcase = require_chunk.__toESM(require("lodash.kebabcase"));
8
+ const __geekmidas_constructs_endpoints = require_chunk.__toESM(require("@geekmidas/constructs/endpoints"));
9
+ const __geekmidas_schema_conversion = require_chunk.__toESM(require("@geekmidas/schema/conversion"));
10
+
11
+ //#region src/generators/Generator.ts
12
+ var ConstructGenerator = class {
13
+ static async build(context, outputDir, generator, patterns, options) {
14
+ const constructs = await generator.load(patterns);
15
+ return generator.build(context, constructs, outputDir, options);
16
+ }
17
+ async load(patterns, cwd = process.cwd()) {
18
+ const logger = console;
19
+ const globPatterns = Array.isArray(patterns) ? patterns : patterns ? [patterns] : [];
20
+ const files = fast_glob.default.stream(globPatterns, {
21
+ cwd,
22
+ absolute: true
23
+ });
24
+ const constructs = [];
25
+ for await (const f of files) try {
26
+ const file = f.toString();
27
+ const module$1 = await import(file);
28
+ for (const [key, construct] of Object.entries(module$1)) if (this.isConstruct(construct)) constructs.push({
29
+ key,
30
+ name: (0, lodash_kebabcase.default)(key),
31
+ construct,
32
+ path: {
33
+ absolute: file,
34
+ relative: (0, path.relative)(process.cwd(), file)
35
+ }
36
+ });
37
+ } catch (error) {
38
+ logger.warn(`Failed to load ${f}:`, error.message);
39
+ throw new Error("Failed to load constructs. Please check the logs for details.");
40
+ }
41
+ return constructs;
42
+ }
43
+ };
44
+
45
+ //#endregion
46
+ //#region src/generators/EndpointGenerator.ts
47
+ var EndpointGenerator = class extends ConstructGenerator {
48
+ isConstruct(value) {
49
+ return __geekmidas_constructs_endpoints.Endpoint.isEndpoint(value);
50
+ }
51
+ async build(context, constructs, outputDir, options) {
52
+ const provider = options?.provider || "aws-apigatewayv2";
53
+ const enableOpenApi = options?.enableOpenApi || false;
54
+ const logger = console;
55
+ const routes = [];
56
+ if (constructs.length === 0) return routes;
57
+ if (provider === "server") {
58
+ await this.generateEndpointsFile(outputDir, constructs, context);
59
+ const appFile = await this.generateAppFile(outputDir, context);
60
+ routes.push({
61
+ path: "*",
62
+ method: "ALL",
63
+ handler: (0, node_path.relative)(process.cwd(), appFile),
64
+ authorizer: "none"
65
+ });
66
+ logger.log(`Generated server with ${constructs.length} endpoints${enableOpenApi ? " (OpenAPI enabled)" : ""}`);
67
+ } else if (provider === "aws-lambda") {
68
+ const routesDir = (0, node_path.join)(outputDir, "routes");
69
+ await (0, node_fs_promises.mkdir)(routesDir, { recursive: true });
70
+ for (const { key, construct, path: path$1 } of constructs) {
71
+ const handlerFile = await this.generateHandlerFile(routesDir, path$1.relative, key, "aws-apigatewayv2", construct, context);
72
+ const routeInfo = {
73
+ path: construct._path,
74
+ method: construct.method,
75
+ handler: (0, node_path.relative)(process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
76
+ timeout: construct.timeout,
77
+ memorySize: construct.memorySize,
78
+ environment: await construct.getEnvironment(),
79
+ authorizer: construct.authorizer?.name ?? "none"
80
+ };
81
+ routes.push(routeInfo);
82
+ logger.log(`Generated handler for ${routeInfo.method} ${routeInfo.path}`);
83
+ }
84
+ } else for (const { key, construct, path: path$1 } of constructs) {
85
+ const handlerFile = await this.generateHandlerFile(outputDir, path$1.relative, key, provider, construct, context);
86
+ const routeInfo = {
87
+ path: construct._path,
88
+ method: construct.method,
89
+ handler: (0, node_path.relative)(process.cwd(), handlerFile).replace(/\.ts$/, ".handler"),
90
+ timeout: construct.timeout,
91
+ memorySize: construct.memorySize,
92
+ environment: await construct.getEnvironment(),
93
+ authorizer: construct.authorizer?.name ?? "none"
94
+ };
95
+ routes.push(routeInfo);
96
+ logger.log(`Generated handler for ${routeInfo.method} ${routeInfo.path}`);
97
+ }
98
+ return routes;
99
+ }
100
+ async generateHandlerFile(outputDir, sourceFile, exportName, provider, _endpoint, context) {
101
+ const handlerFileName = `${exportName}.ts`;
102
+ const handlerPath = (0, node_path.join)(outputDir, handlerFileName);
103
+ const relativePath = (0, node_path.relative)((0, node_path.dirname)(handlerPath), sourceFile);
104
+ const importPath = relativePath.replace(/\.ts$/, ".js");
105
+ const relativeEnvParserPath = (0, node_path.relative)((0, node_path.dirname)(handlerPath), context.envParserPath);
106
+ let content;
107
+ switch (provider) {
108
+ case "aws-apigatewayv1":
109
+ content = this.generateAWSApiGatewayV1Handler(importPath, exportName, relativeEnvParserPath, context.envParserImportPattern);
110
+ break;
111
+ case "aws-apigatewayv2":
112
+ content = this.generateAWSApiGatewayV2Handler(importPath, exportName, relativeEnvParserPath, context.envParserImportPattern);
113
+ break;
114
+ case "server":
115
+ content = this.generateServerHandler(importPath, exportName);
116
+ break;
117
+ default: throw new Error(`Unsupported provider: ${provider}`);
118
+ }
119
+ await (0, node_fs_promises.writeFile)(handlerPath, content);
120
+ return handlerPath;
121
+ }
122
+ async generateEndpointsFile(outputDir, endpoints, _context) {
123
+ const endpointsFileName = "endpoints.ts";
124
+ const endpointsPath = (0, node_path.join)(outputDir, endpointsFileName);
125
+ const importsByFile = /* @__PURE__ */ new Map();
126
+ for (const { path: path$1, key } of endpoints) {
127
+ const relativePath = (0, node_path.relative)((0, node_path.dirname)(endpointsPath), path$1.relative);
128
+ const importPath = relativePath.replace(/\.ts$/, ".js");
129
+ if (!importsByFile.has(importPath)) importsByFile.set(importPath, []);
130
+ importsByFile.get(importPath).push(key);
131
+ }
132
+ const imports = Array.from(importsByFile.entries()).map(([importPath, exports$1]) => `import { ${exports$1.join(", ")} } from '${importPath}';`).join("\n");
133
+ const allExportNames = endpoints.map(({ key }) => key);
134
+ const content = `import type { EnvironmentParser } from '@geekmidas/envkit';
135
+ import type { Logger } from '@geekmidas/logger';
136
+ import { HonoEndpoint } from '@geekmidas/constructs/hono';
137
+ import { Endpoint } from '@geekmidas/constructs/endpoints';
138
+ import { ServiceDiscovery } from '@geekmidas/services';
139
+ import type { Hono } from 'hono';
140
+ ${imports}
141
+
142
+ const endpoints: Endpoint<any, any, any, any, any, any, any, any, any, any, any, any, any, any>[] = [
143
+ ${allExportNames.join(",\n ")}
144
+ ];
145
+
146
+ export async function setupEndpoints(
147
+ app: Hono,
148
+ envParser: EnvironmentParser<any>,
149
+ logger: Logger,
150
+ enableOpenApi: boolean = true,
151
+ ): Promise<void> {
152
+ const serviceDiscovery = ServiceDiscovery.getInstance(
153
+ logger,
154
+ envParser
155
+ );
156
+
157
+ // Configure OpenAPI options based on enableOpenApi flag
158
+ const openApiOptions: any = enableOpenApi ? {
159
+ docsPath: '/__docs',
160
+ openApiOptions: {
161
+ title: 'API Documentation',
162
+ version: '1.0.0',
163
+ description: 'Generated API documentation'
164
+ }
165
+ } : { docsPath: false };
166
+
167
+ HonoEndpoint.addRoutes(endpoints, serviceDiscovery, app, openApiOptions);
168
+
169
+ // Add Swagger UI if OpenAPI is enabled
170
+ if (enableOpenApi) {
171
+ try {
172
+ const { swaggerUI } = await import('@hono/swagger-ui');
173
+ app.get('/__docs/ui', swaggerUI({ url: '/__docs' }));
174
+ } catch {
175
+ // @hono/swagger-ui not installed, skip Swagger UI
176
+ }
177
+ }
178
+ }
179
+ `;
180
+ await (0, node_fs_promises.writeFile)(endpointsPath, content);
181
+ return endpointsPath;
182
+ }
183
+ async generateAppFile(outputDir, context) {
184
+ const appFileName = "app.ts";
185
+ const appPath = (0, node_path.join)(outputDir, appFileName);
186
+ const relativeLoggerPath = (0, node_path.relative)((0, node_path.dirname)(appPath), context.loggerPath);
187
+ const relativeEnvParserPath = (0, node_path.relative)((0, node_path.dirname)(appPath), context.envParserPath);
188
+ const telescopeEnabled = context.telescope?.enabled;
189
+ const telescopeWebSocketEnabled = context.telescope?.websocket;
190
+ const usesExternalTelescope = !!context.telescope?.telescopePath;
191
+ const studioEnabled = context.studio?.enabled;
192
+ const usesExternalStudio = !!context.studio?.studioPath;
193
+ let telescopeImports = "";
194
+ if (telescopeEnabled) if (usesExternalTelescope) {
195
+ const relativeTelescopePath = (0, node_path.relative)((0, node_path.dirname)(appPath), context.telescope.telescopePath);
196
+ telescopeImports = `import ${context.telescope.telescopeImportPattern} from '${relativeTelescopePath}';
197
+ import { createMiddleware, createUI } from '@geekmidas/telescope/hono';`;
198
+ } else telescopeImports = `import { Telescope, InMemoryStorage } from '@geekmidas/telescope';
199
+ import { createMiddleware, createUI } from '@geekmidas/telescope/hono';`;
200
+ let studioImports = "";
201
+ if (studioEnabled) if (usesExternalStudio) {
202
+ const relativeStudioPath = (0, node_path.relative)((0, node_path.dirname)(appPath), context.studio.studioPath);
203
+ studioImports = `import ${context.studio.studioImportPattern} from '${relativeStudioPath}';
204
+ import { createStudioApp } from '@geekmidas/studio/server/hono';`;
205
+ } else studioImports = `// Studio requires a configured instance - use studio config path
206
+ // import { createStudioApp } from '@geekmidas/studio/server/hono';`;
207
+ let hooksImports = "";
208
+ let beforeSetupCall = "";
209
+ let afterSetupCall = "";
210
+ if (context.hooks?.serverHooksPath) {
211
+ const relativeHooksPath = (0, node_path.relative)((0, node_path.dirname)(appPath), context.hooks.serverHooksPath);
212
+ hooksImports = `import * as serverHooks from '${relativeHooksPath}';`;
213
+ beforeSetupCall = `
214
+ // Call beforeSetup hook if defined
215
+ if (typeof serverHooks.beforeSetup === 'function') {
216
+ await serverHooks.beforeSetup(honoApp, { envParser, logger });
217
+ }
218
+ `;
219
+ afterSetupCall = `
220
+ // Call afterSetup hook if defined
221
+ if (typeof serverHooks.afterSetup === 'function') {
222
+ await serverHooks.afterSetup(honoApp, { envParser, logger });
223
+ }
224
+ `;
225
+ }
226
+ const telescopeWebSocketSetupCode = telescopeWebSocketEnabled ? `
227
+ // Setup WebSocket for real-time telescope updates
228
+ try {
229
+ const { createNodeWebSocket } = await import('@hono/node-ws');
230
+ const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app: honoApp });
231
+ // Add WebSocket route directly to main app (sub-app routes don't support WS upgrade)
232
+ honoApp.get('${context.telescope.path}/ws', upgradeWebSocket(() => ({
233
+ onOpen: (_event: Event, ws: any) => {
234
+ telescope.addWsClient(ws);
235
+ },
236
+ onClose: (_event: Event, ws: any) => {
237
+ telescope.removeWsClient(ws);
238
+ },
239
+ onMessage: (event: MessageEvent, ws: any) => {
240
+ try {
241
+ const data = JSON.parse(event.data);
242
+ if (data.type === 'ping') {
243
+ ws.send(JSON.stringify({ type: 'pong' }));
244
+ }
245
+ } catch {
246
+ // Ignore invalid messages
247
+ }
248
+ },
249
+ })));
250
+ // Store injectWebSocket for server entry to call after serve()
251
+ (honoApp as any).__injectWebSocket = injectWebSocket;
252
+ logger.info('Telescope WebSocket enabled');
253
+ } catch (e) {
254
+ logger.warn({ error: e }, 'WebSocket support not available - install @hono/node-ws for real-time updates');
255
+ }
256
+ ` : "";
257
+ let telescopeSetup = "";
258
+ if (telescopeEnabled) if (usesExternalTelescope) telescopeSetup = `
259
+ ${telescopeWebSocketSetupCode}
260
+ // Add telescope middleware (before endpoints to capture all requests)
261
+ honoApp.use('*', createMiddleware(telescope));
262
+
263
+ // Mount telescope UI
264
+ const telescopeUI = createUI(telescope);
265
+ honoApp.route('${context.telescope.path}', telescopeUI);
266
+ `;
267
+ else telescopeSetup = `
268
+ // Setup Telescope for debugging/monitoring
269
+ const telescopeStorage = new InMemoryStorage({ maxEntries: ${context.telescope.maxEntries} });
270
+ const telescope = new Telescope({
271
+ enabled: true,
272
+ path: '${context.telescope.path}',
273
+ ignorePatterns: ${JSON.stringify(context.telescope.ignore)},
274
+ recordBody: ${context.telescope.recordBody},
275
+ storage: telescopeStorage,
276
+ });
277
+ ${telescopeWebSocketSetupCode}
278
+ // Add telescope middleware (before endpoints to capture all requests)
279
+ honoApp.use('*', createMiddleware(telescope));
280
+
281
+ // Mount telescope UI
282
+ const telescopeUI = createUI(telescope);
283
+ honoApp.route('${context.telescope.path}', telescopeUI);
284
+ `;
285
+ let studioSetup = "";
286
+ if (studioEnabled && usesExternalStudio) studioSetup = `
287
+ // Mount Studio data browser UI
288
+ const studioApp = createStudioApp(studio);
289
+ honoApp.route('${context.studio.path}', studioApp);
290
+ `;
291
+ const content = `/**
292
+ * Generated server application
293
+ *
294
+ * ⚠️ WARNING: This is for LOCAL DEVELOPMENT ONLY
295
+ * The subscriber polling mechanism is not production-ready.
296
+ * For production, use AWS Lambda with SQS/SNS event sources.
297
+ */
298
+ import { Hono } from 'hono';
299
+ import type { Hono as HonoType } from 'hono';
300
+ import { setupEndpoints } from './endpoints.js';
301
+ import { setupSubscribers } from './subscribers.js';
302
+ import ${context.envParserImportPattern} from '${relativeEnvParserPath}';
303
+ import ${context.loggerImportPattern} from '${relativeLoggerPath}';
304
+ ${telescopeImports}
305
+ ${studioImports}
306
+ ${hooksImports}
307
+
308
+ export interface ServerApp {
309
+ app: HonoType;
310
+ start: (options?: {
311
+ port?: number;
312
+ serve: (app: HonoType, port: number) => void | Promise<void>;
313
+ }) => Promise<void>;
314
+ }
315
+
316
+ /**
317
+ * Create and configure the Hono application
318
+ *
319
+ * @param app - Optional Hono app instance to configure (creates new one if not provided)
320
+ * @param enableOpenApi - Enable OpenAPI documentation (default: true)
321
+ * @returns Server app with configured Hono app and start function
322
+ *
323
+ * @example
324
+ * // With Bun
325
+ * import { createApp } from './.gkm/server/app.js';
326
+ *
327
+ * const { app, start } = await createApp();
328
+ *
329
+ * await start({
330
+ * port: 3000,
331
+ * serve: (app, port) => {
332
+ * Bun.serve({ port, fetch: app.fetch });
333
+ * }
334
+ * });
335
+ *
336
+ * @example
337
+ * // With Node.js (using @hono/node-server)
338
+ * import { serve } from '@hono/node-server';
339
+ * import { createApp } from './.gkm/server/app.js';
340
+ *
341
+ * const { app, start } = await createApp();
342
+ *
343
+ * await start({
344
+ * port: 3000,
345
+ * serve: (app, port) => {
346
+ * serve({ fetch: app.fetch, port });
347
+ * }
348
+ * });
349
+ */
350
+ export async function createApp(app?: HonoType, enableOpenApi: boolean = true): Promise<ServerApp> {
351
+ const honoApp = app || new Hono();
352
+ ${telescopeSetup}${beforeSetupCall}${studioSetup}
353
+ // Setup HTTP endpoints
354
+ await setupEndpoints(honoApp, envParser, logger, enableOpenApi);
355
+ ${afterSetupCall}
356
+
357
+ return {
358
+ app: honoApp,
359
+ async start(options) {
360
+ if (!options?.serve) {
361
+ throw new Error(
362
+ 'serve function is required. Pass a serve function for your runtime:\\n' +
363
+ ' - Bun: (app, port) => Bun.serve({ port, fetch: app.fetch })\\n' +
364
+ ' - Node: (app, port) => serve({ fetch: app.fetch, port })'
365
+ );
366
+ }
367
+
368
+ const port = options.port ?? 3000;
369
+
370
+ // Start subscribers in background (non-blocking, local development only)
371
+ await setupSubscribers(envParser, logger).catch((error) => {
372
+ logger.error({ error }, 'Failed to start subscribers');
373
+ });
374
+
375
+ logger.info({ port }, 'Starting server');
376
+
377
+ // Start HTTP server using provided serve function
378
+ await options.serve(honoApp, port);
379
+
380
+ logger.info({ port }, 'Server started');
381
+ }
382
+ };
383
+ }
384
+
385
+ // Default export for convenience
386
+ export default createApp;
387
+ `;
388
+ await (0, node_fs_promises.writeFile)(appPath, content);
389
+ return appPath;
390
+ }
391
+ generateAWSApiGatewayV1Handler(importPath, exportName, envParserPath, envParserImportPattern) {
392
+ return `import { AmazonApiGatewayV1Endpoint } from '@geekmidas/constructs/aws';
393
+ import { ${exportName} } from '${importPath}';
394
+ import ${envParserImportPattern} from '${envParserPath}';
395
+
396
+ const adapter = new AmazonApiGatewayV1Endpoint(envParser, ${exportName});
397
+
398
+ export const handler = adapter.handler;
399
+ `;
400
+ }
401
+ generateAWSApiGatewayV2Handler(importPath, exportName, envParserPath, envParserImportPattern) {
402
+ return `import { AmazonApiGatewayV2Endpoint } from '@geekmidas/constructs/aws';
403
+ import { ${exportName} } from '${importPath}';
404
+ import ${envParserImportPattern} from '${envParserPath}';
405
+
406
+ const adapter = new AmazonApiGatewayV2Endpoint(envParser, ${exportName});
407
+
408
+ export const handler = adapter.handler;
409
+ `;
410
+ }
411
+ generateServerHandler(importPath, exportName) {
412
+ return `import { ${exportName} } from '${importPath}';
413
+
414
+ // Server handler - implement based on your server framework
415
+ export const handler = ${exportName};
416
+ `;
417
+ }
418
+ };
419
+
420
+ //#endregion
421
+ //#region src/generators/OpenApiTsGenerator.ts
422
+ /**
423
+ * Generates TypeScript OpenAPI module from endpoints.
424
+ * Outputs:
425
+ * - securitySchemes: typed security scheme definitions
426
+ * - endpointAuth: runtime map of endpoints to auth requirements
427
+ * - paths: TypeScript interface for type-safe fetcher
428
+ * - schema interfaces: reusable TypeScript types from Zod/Valibot schemas
429
+ */
430
+ var OpenApiTsGenerator = class {
431
+ async generate(endpoints, options = {}) {
432
+ const { title = "API", version = "1.0.0", description } = options;
433
+ const endpointInfos = await this.extractEndpointInfos(endpoints);
434
+ const securitySchemes = this.collectSecuritySchemes(endpointInfos);
435
+ const endpointAuth = this.buildEndpointAuthMap(endpointInfos);
436
+ const schemaInterfaces = await this.generateSchemaInterfaces(endpointInfos);
437
+ const pathsInterface = await this.generatePathsInterface(endpointInfos);
438
+ return this.buildModule({
439
+ title,
440
+ version,
441
+ description,
442
+ securitySchemes,
443
+ endpointAuth,
444
+ schemaInterfaces,
445
+ pathsInterface
446
+ });
447
+ }
448
+ async extractEndpointInfos(endpoints) {
449
+ return endpoints.map((ep) => {
450
+ const route = ep.route.replace(/:(\w+)/g, "{$1}");
451
+ const method = ep.method.toUpperCase();
452
+ const securityScheme = ep.authorizer?.securityScheme;
453
+ return {
454
+ endpoint: `${method} ${route}`,
455
+ route,
456
+ method,
457
+ authorizerName: ep.authorizer?.name ?? null,
458
+ authorizerType: ep.authorizer?.type ?? null,
459
+ securityScheme: securityScheme ?? null,
460
+ input: ep.input,
461
+ output: ep.outputSchema,
462
+ description: ep.description,
463
+ tags: ep.tags,
464
+ operationId: ep.operationId
465
+ };
466
+ });
467
+ }
468
+ collectSecuritySchemes(endpointInfos) {
469
+ const schemes = /* @__PURE__ */ new Map();
470
+ for (const info of endpointInfos) if (info.authorizerName && !schemes.has(info.authorizerName)) {
471
+ const scheme = info.securityScheme ?? (info.authorizerType ? this.mapAuthorizerToSecurityScheme(info.authorizerType, info.authorizerName) : null);
472
+ if (scheme) schemes.set(info.authorizerName, {
473
+ name: info.authorizerName,
474
+ type: scheme.type,
475
+ scheme
476
+ });
477
+ }
478
+ return Array.from(schemes.values());
479
+ }
480
+ mapAuthorizerToSecurityScheme(type, _name) {
481
+ switch (type.toLowerCase()) {
482
+ case "jwt":
483
+ case "bearer": return {
484
+ type: "http",
485
+ scheme: "bearer",
486
+ bearerFormat: "JWT"
487
+ };
488
+ case "iam":
489
+ case "aws-sigv4":
490
+ case "sigv4": return {
491
+ type: "apiKey",
492
+ in: "header",
493
+ name: "Authorization",
494
+ "x-amazon-apigateway-authtype": "awsSigv4"
495
+ };
496
+ case "apikey":
497
+ case "api-key": return {
498
+ type: "apiKey",
499
+ in: "header",
500
+ name: "X-API-Key"
501
+ };
502
+ case "oauth2": return {
503
+ type: "oauth2",
504
+ flows: {}
505
+ };
506
+ case "oidc":
507
+ case "openidconnect": return {
508
+ type: "openIdConnect",
509
+ openIdConnectUrl: ""
510
+ };
511
+ default: return {
512
+ type: "http",
513
+ scheme: "bearer"
514
+ };
515
+ }
516
+ }
517
+ buildEndpointAuthMap(endpointInfos) {
518
+ const authMap = {};
519
+ for (const info of endpointInfos) authMap[info.endpoint] = info.authorizerName;
520
+ return authMap;
521
+ }
522
+ async generateSchemaInterfaces(endpointInfos) {
523
+ const interfaces = [];
524
+ const generatedNames = /* @__PURE__ */ new Set();
525
+ const collectedDefs = /* @__PURE__ */ new Map();
526
+ for (const info of endpointInfos) {
527
+ const baseName = this.getSchemaBaseName(info);
528
+ if (info.input?.body) {
529
+ const name = await this.getSchemaName(info.input.body, `${baseName}Input`);
530
+ if (!generatedNames.has(name)) {
531
+ const schema = await this.schemaToInterfaceWithDefs(info.input.body, name, collectedDefs);
532
+ if (schema) {
533
+ interfaces.push(schema);
534
+ generatedNames.add(name);
535
+ }
536
+ }
537
+ }
538
+ if (info.input?.params) {
539
+ const name = await this.getSchemaName(info.input.params, `${baseName}Params`);
540
+ if (!generatedNames.has(name)) {
541
+ const schema = await this.schemaToInterfaceWithDefs(info.input.params, name, collectedDefs);
542
+ if (schema) {
543
+ interfaces.push(schema);
544
+ generatedNames.add(name);
545
+ }
546
+ }
547
+ }
548
+ if (info.input?.query) {
549
+ const name = await this.getSchemaName(info.input.query, `${baseName}Query`);
550
+ if (!generatedNames.has(name)) {
551
+ const schema = await this.schemaToInterfaceWithDefs(info.input.query, name, collectedDefs);
552
+ if (schema) {
553
+ interfaces.push(schema);
554
+ generatedNames.add(name);
555
+ }
556
+ }
557
+ }
558
+ if (info.output) {
559
+ const name = await this.getSchemaName(info.output, `${baseName}Output`);
560
+ if (!generatedNames.has(name)) {
561
+ const schema = await this.schemaToInterfaceWithDefs(info.output, name, collectedDefs);
562
+ if (schema) {
563
+ interfaces.push(schema);
564
+ generatedNames.add(name);
565
+ }
566
+ }
567
+ }
568
+ }
569
+ for (const [defName, defSchema] of collectedDefs) if (!generatedNames.has(defName)) {
570
+ const interfaceStr = this.jsonSchemaToInterface(defSchema, defName);
571
+ interfaces.push(interfaceStr);
572
+ generatedNames.add(defName);
573
+ }
574
+ return interfaces.join("\n\n");
575
+ }
576
+ /**
577
+ * Get the name for a schema, using metadata `id` if available,
578
+ * otherwise falling back to the provided default name.
579
+ */
580
+ async getSchemaName(schema, defaultName) {
581
+ try {
582
+ const metadata = await (0, __geekmidas_schema_conversion.getSchemaMetadata)(schema);
583
+ if (metadata?.id) return this.pascalCase(metadata.id);
584
+ } catch {}
585
+ return defaultName;
586
+ }
587
+ getSchemaBaseName(info) {
588
+ if (info.operationId) return this.pascalCase(info.operationId);
589
+ const routeParts = info.route.replace(/[{}]/g, "").split("/").filter(Boolean).map((part) => this.pascalCase(part));
590
+ return `${this.pascalCase(info.method.toLowerCase())}${routeParts.join("")}`;
591
+ }
592
+ pascalCase(str) {
593
+ return str.replace(/[-_](.)/g, (_, c) => c.toUpperCase()).replace(/^./, (c) => c.toUpperCase());
594
+ }
595
+ /**
596
+ * Convert schema to interface while collecting $defs for nested schemas
597
+ * with .meta({ id: 'X' }).
598
+ */
599
+ async schemaToInterfaceWithDefs(schema, name, collectedDefs) {
600
+ try {
601
+ const vendor = schema["~standard"]?.vendor;
602
+ if (!vendor || !(vendor in __geekmidas_schema_conversion.StandardSchemaJsonSchema)) return null;
603
+ const toJsonSchema = __geekmidas_schema_conversion.StandardSchemaJsonSchema[vendor];
604
+ const jsonSchema = await toJsonSchema(schema);
605
+ if (!jsonSchema) return null;
606
+ if (jsonSchema.$defs && typeof jsonSchema.$defs === "object") {
607
+ for (const [defName, defSchema] of Object.entries(jsonSchema.$defs)) if (!collectedDefs.has(defName)) {
608
+ const { id,...schemaWithoutId } = defSchema;
609
+ collectedDefs.set(defName, schemaWithoutId);
610
+ }
611
+ }
612
+ const { $defs,...schemaWithoutDefs } = jsonSchema;
613
+ return this.jsonSchemaToInterface(schemaWithoutDefs, name);
614
+ } catch {
615
+ return null;
616
+ }
617
+ }
618
+ jsonSchemaToInterface(schema, name) {
619
+ if (schema.type !== "object" || !schema.properties) {
620
+ const typeStr = this.jsonSchemaTypeToTs(schema);
621
+ return `export type ${name} = ${typeStr};`;
622
+ }
623
+ const props = [];
624
+ const required = new Set(schema.required || []);
625
+ for (const [propName, propSchema] of Object.entries(schema.properties)) {
626
+ const isRequired = required.has(propName);
627
+ const typeStr = this.jsonSchemaTypeToTs(propSchema);
628
+ const optionalMark = isRequired ? "" : "?";
629
+ props.push(` ${propName}${optionalMark}: ${typeStr};`);
630
+ }
631
+ return `export interface ${name} {\n${props.join("\n")}\n}`;
632
+ }
633
+ jsonSchemaTypeToTs(schema) {
634
+ if (!schema) return "unknown";
635
+ if (schema.$ref) {
636
+ const refName = schema.$ref.split("/").pop() || "unknown";
637
+ return refName;
638
+ }
639
+ if (schema.anyOf) return schema.anyOf.map((s) => this.jsonSchemaTypeToTs(s)).join(" | ");
640
+ if (schema.oneOf) return schema.oneOf.map((s) => this.jsonSchemaTypeToTs(s)).join(" | ");
641
+ if (schema.allOf) return schema.allOf.map((s) => this.jsonSchemaTypeToTs(s)).join(" & ");
642
+ switch (schema.type) {
643
+ case "string":
644
+ if (schema.enum) return schema.enum.map((e) => `'${e}'`).join(" | ");
645
+ return "string";
646
+ case "number":
647
+ case "integer": return "number";
648
+ case "boolean": return "boolean";
649
+ case "null": return "null";
650
+ case "array":
651
+ if (schema.items) return `Array<${this.jsonSchemaTypeToTs(schema.items)}>`;
652
+ return "Array<unknown>";
653
+ case "object":
654
+ if (schema.properties) {
655
+ const props = [];
656
+ const required = new Set(schema.required || []);
657
+ for (const [propName, propSchema] of Object.entries(schema.properties)) {
658
+ const isRequired = required.has(propName);
659
+ const typeStr = this.jsonSchemaTypeToTs(propSchema);
660
+ const optionalMark = isRequired ? "" : "?";
661
+ props.push(`${propName}${optionalMark}: ${typeStr}`);
662
+ }
663
+ return `{ ${props.join("; ")} }`;
664
+ }
665
+ if (schema.additionalProperties) {
666
+ const valueType = this.jsonSchemaTypeToTs(schema.additionalProperties);
667
+ return `Record<string, ${valueType}>`;
668
+ }
669
+ return "Record<string, unknown>";
670
+ default: return "unknown";
671
+ }
672
+ }
673
+ async generatePathsInterface(endpointInfos) {
674
+ const pathGroups = /* @__PURE__ */ new Map();
675
+ for (const info of endpointInfos) {
676
+ const existing = pathGroups.get(info.route) || [];
677
+ existing.push(info);
678
+ pathGroups.set(info.route, existing);
679
+ }
680
+ const pathEntries = [];
681
+ for (const [route, infos] of pathGroups) {
682
+ const methodEntries = [];
683
+ for (const info of infos) {
684
+ const methodDef = await this.generateMethodDefinition(info);
685
+ methodEntries.push(` ${info.method.toLowerCase()}: ${methodDef};`);
686
+ }
687
+ const firstWithParams = infos.find((i) => i.input?.params);
688
+ let paramsEntry = "";
689
+ if (firstWithParams?.input?.params) {
690
+ const paramsName = await this.getSchemaName(firstWithParams.input.params, `${this.getSchemaBaseName(firstWithParams)}Params`);
691
+ paramsEntry = `\n parameters: {\n path: ${paramsName};\n };`;
692
+ }
693
+ pathEntries.push(` '${route}': {${paramsEntry}\n${methodEntries.join("\n")}\n };`);
694
+ }
695
+ return `export interface paths {\n${pathEntries.join("\n")}\n}`;
696
+ }
697
+ async generateMethodDefinition(info) {
698
+ const parts = [];
699
+ const baseName = this.getSchemaBaseName(info);
700
+ if (info.input?.body) {
701
+ const bodyName = await this.getSchemaName(info.input.body, `${baseName}Input`);
702
+ parts.push(`requestBody: {
703
+ content: {
704
+ 'application/json': ${bodyName};
705
+ };
706
+ }`);
707
+ }
708
+ if (info.input?.query) {
709
+ const queryName = await this.getSchemaName(info.input.query, `${baseName}Query`);
710
+ parts.push(`parameters: {
711
+ query: ${queryName};
712
+ }`);
713
+ }
714
+ const outputName = info.output ? await this.getSchemaName(info.output, `${baseName}Output`) : "unknown";
715
+ parts.push(`responses: {
716
+ 200: {
717
+ content: {
718
+ 'application/json': ${outputName};
719
+ };
720
+ };
721
+ }`);
722
+ return `{\n ${parts.join(";\n ")};\n }`;
723
+ }
724
+ buildModule(params) {
725
+ const { title, version, description, securitySchemes, endpointAuth, schemaInterfaces, pathsInterface } = params;
726
+ const securitySchemesObj = securitySchemes.reduce((acc, s) => {
727
+ acc[s.name] = s.scheme;
728
+ return acc;
729
+ }, {});
730
+ const schemeNames = securitySchemes.map((s) => `'${s.name}'`).join(" | ");
731
+ const hasSecuritySchemes = schemeNames.length > 0;
732
+ const createApiSection = hasSecuritySchemes ? `
733
+ // ============================================================
734
+ // API Client Factory
735
+ // ============================================================
736
+
737
+ import {
738
+ createAuthAwareFetcher,
739
+ type AuthStrategy,
740
+ } from '@geekmidas/client/auth-fetcher';
741
+ import { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';
742
+ import type { QueryClient } from '@tanstack/react-query';
743
+
744
+ /**
745
+ * Options for creating the API client.
746
+ */
747
+ export interface CreateApiOptions {
748
+ /** Base URL for all API requests (required) */
749
+ baseURL: string;
750
+ /** Auth strategies for each security scheme used in this API */
751
+ authStrategies: Record<SecuritySchemeId, AuthStrategy>;
752
+ /** Optional React Query client instance */
753
+ queryClient?: QueryClient;
754
+ /** Optional request interceptor */
755
+ onRequest?: (config: RequestInit) => RequestInit | Promise<RequestInit>;
756
+ }
757
+
758
+ /**
759
+ * Create a type-safe API client with authentication and React Query hooks.
760
+ *
761
+ * @example
762
+ * \`\`\`typescript
763
+ * const api = createApi({
764
+ * baseURL: 'https://api.example.com',
765
+ * authStrategies: {
766
+ * jwt: { type: 'bearer', tokenProvider },
767
+ * },
768
+ * });
769
+ *
770
+ * // Imperative fetch
771
+ * const user = await api('GET /users/{id}', { params: { id: '123' } });
772
+ *
773
+ * // React Query hooks
774
+ * const { data } = api.useQuery('GET /users/{id}', { params: { id: '123' } });
775
+ * const mutation = api.useMutation('POST /users');
776
+ * \`\`\`
777
+ */
778
+ export function createApi(options: CreateApiOptions) {
779
+ const fetcher = createAuthAwareFetcher<paths, typeof endpointAuth, typeof securitySchemes>({
780
+ baseURL: options.baseURL,
781
+ endpointAuth,
782
+ securitySchemes,
783
+ authStrategies: options.authStrategies,
784
+ onRequest: options.onRequest,
785
+ });
786
+
787
+ const hooks = createEndpointHooks<paths>(fetcher, { queryClient: options.queryClient });
788
+
789
+ return Object.assign(fetcher, hooks);
790
+ }
791
+ ` : `
792
+ // ============================================================
793
+ // API Client Factory
794
+ // ============================================================
795
+
796
+ import { TypedFetcher, type FetcherOptions } from '@geekmidas/client/fetcher';
797
+ import { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';
798
+ import type { QueryClient } from '@tanstack/react-query';
799
+
800
+ /**
801
+ * Options for creating the API client.
802
+ */
803
+ export interface CreateApiOptions extends Omit<FetcherOptions, 'baseURL'> {
804
+ /** Base URL for all API requests (required) */
805
+ baseURL: string;
806
+ /** Optional React Query client instance */
807
+ queryClient?: QueryClient;
808
+ }
809
+
810
+ /**
811
+ * Create a type-safe API client with React Query hooks.
812
+ *
813
+ * @example
814
+ * \`\`\`typescript
815
+ * const api = createApi({
816
+ * baseURL: 'https://api.example.com',
817
+ * });
818
+ *
819
+ * // Imperative fetch
820
+ * const data = await api('GET /health');
821
+ *
822
+ * // React Query hooks
823
+ * const { data } = api.useQuery('GET /health');
824
+ * \`\`\`
825
+ */
826
+ export function createApi(options: CreateApiOptions) {
827
+ const { queryClient, ...fetcherOptions } = options;
828
+ const fetcher = new TypedFetcher<paths>(fetcherOptions);
829
+
830
+ const hooks = createEndpointHooks<paths>(fetcher.request.bind(fetcher), { queryClient });
831
+
832
+ return Object.assign(fetcher.request.bind(fetcher), hooks);
833
+ }
834
+ `;
835
+ return `// Auto-generated by @geekmidas/cli - DO NOT EDIT
836
+ // Generated: ${(/* @__PURE__ */ new Date()).toISOString()}
837
+
838
+ // ============================================================
839
+ // Security Scheme Type
840
+ // ============================================================
841
+
842
+ interface SecuritySchemeObject {
843
+ type: 'apiKey' | 'http' | 'mutualTLS' | 'oauth2' | 'openIdConnect';
844
+ description?: string;
845
+ name?: string;
846
+ in?: 'query' | 'header' | 'cookie';
847
+ scheme?: string;
848
+ bearerFormat?: string;
849
+ flows?: Record<string, unknown>;
850
+ openIdConnectUrl?: string;
851
+ [key: string]: unknown;
852
+ }
853
+
854
+ // ============================================================
855
+ // API Info
856
+ // ============================================================
857
+
858
+ export const apiInfo = {
859
+ title: '${title}',
860
+ version: '${version}',${description ? `\n description: '${description.replace(/'/g, "\\'")}',` : ""}
861
+ } as const;
862
+
863
+ // ============================================================
864
+ // Security Schemes
865
+ // ============================================================
866
+
867
+ /**
868
+ * Available security schemes for this API.
869
+ * Maps authorizer names to OpenAPI security scheme definitions.
870
+ */
871
+ export const securitySchemes = ${JSON.stringify(securitySchemesObj, null, 2).replace(/"([a-zA-Z_$][a-zA-Z0-9_$]*)":/g, "$1:")} as const satisfies Record<string, SecuritySchemeObject>;
872
+
873
+ export type SecuritySchemeId = ${schemeNames || "never"};
874
+
875
+ // ============================================================
876
+ // Endpoint Authentication Map
877
+ // ============================================================
878
+
879
+ /**
880
+ * Runtime map of endpoints to their required authentication scheme.
881
+ * \`null\` indicates a public endpoint (no auth required).
882
+ */
883
+ export const endpointAuth = ${JSON.stringify(endpointAuth, null, 2).replace(/"([^"]+)":/g, "'$1':")} as const satisfies Record<string, SecuritySchemeId | null>;
884
+
885
+ export type EndpointString = keyof typeof endpointAuth;
886
+
887
+ export type AuthenticatedEndpoint = {
888
+ [K in EndpointString]: typeof endpointAuth[K] extends null ? never : K;
889
+ }[EndpointString];
890
+
891
+ export type PublicEndpoint = {
892
+ [K in EndpointString]: typeof endpointAuth[K] extends null ? K : never;
893
+ }[EndpointString];
894
+
895
+ // ============================================================
896
+ // Schema Definitions
897
+ // ============================================================
898
+
899
+ ${schemaInterfaces}
900
+
901
+ // ============================================================
902
+ // OpenAPI Paths
903
+ // ============================================================
904
+
905
+ ${pathsInterface}
906
+ ${createApiSection}
907
+ `;
908
+ }
909
+ };
910
+
911
+ //#endregion
912
+ //#region src/openapi.ts
913
+ /**
914
+ * Fixed output path for generated OpenAPI client (not configurable)
915
+ */
916
+ const OPENAPI_OUTPUT_PATH = "./.gkm/openapi.ts";
917
+ /**
918
+ * Resolve OpenAPI config from GkmConfig
919
+ */
920
+ function resolveOpenApiConfig(config) {
921
+ if (config.openapi === false) return { enabled: false };
922
+ if (config.openapi === true || config.openapi === void 0) return {
923
+ enabled: config.openapi === true,
924
+ title: "API Documentation",
925
+ version: "1.0.0",
926
+ description: "Auto-generated API documentation from endpoints"
927
+ };
928
+ return {
929
+ enabled: config.openapi.enabled !== false,
930
+ title: config.openapi.title || "API Documentation",
931
+ version: config.openapi.version || "1.0.0",
932
+ description: config.openapi.description || "Auto-generated API documentation from endpoints"
933
+ };
934
+ }
935
+ /**
936
+ * Generate OpenAPI spec from endpoints
937
+ * @returns Object with output path and endpoint count, or null if disabled
938
+ */
939
+ async function generateOpenApi(config, options = {}) {
940
+ const logger = options.silent ? { log: () => {} } : console;
941
+ const openApiConfig = resolveOpenApiConfig(config);
942
+ if (!openApiConfig.enabled) return null;
943
+ const endpointGenerator = new EndpointGenerator();
944
+ const loadedEndpoints = await endpointGenerator.load(config.routes);
945
+ if (loadedEndpoints.length === 0) {
946
+ logger.log("No valid endpoints found for OpenAPI generation");
947
+ return null;
948
+ }
949
+ const endpoints = loadedEndpoints.map(({ construct }) => construct);
950
+ const outputPath = (0, node_path.join)(process.cwd(), OPENAPI_OUTPUT_PATH);
951
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(outputPath), { recursive: true });
952
+ const tsGenerator = new OpenApiTsGenerator();
953
+ const tsContent = await tsGenerator.generate(endpoints, {
954
+ title: openApiConfig.title,
955
+ version: openApiConfig.version,
956
+ description: openApiConfig.description
957
+ });
958
+ await (0, node_fs_promises.writeFile)(outputPath, tsContent);
959
+ logger.log(`📄 OpenAPI client generated: ${OPENAPI_OUTPUT_PATH}`);
960
+ return {
961
+ outputPath,
962
+ endpointCount: loadedEndpoints.length
963
+ };
964
+ }
965
+ async function openapiCommand(options = {}) {
966
+ const logger = console;
967
+ try {
968
+ const config = await require_config.loadConfig(options.cwd);
969
+ if (!config.openapi) config.openapi = { enabled: true };
970
+ const result = await generateOpenApi(config);
971
+ if (result) logger.log(`Found ${result.endpointCount} endpoints`);
972
+ } catch (error) {
973
+ throw new Error(`OpenAPI generation failed: ${error.message}`);
974
+ }
975
+ }
976
+
977
+ //#endregion
978
+ Object.defineProperty(exports, 'ConstructGenerator', {
979
+ enumerable: true,
980
+ get: function () {
981
+ return ConstructGenerator;
982
+ }
983
+ });
984
+ Object.defineProperty(exports, 'EndpointGenerator', {
985
+ enumerable: true,
986
+ get: function () {
987
+ return EndpointGenerator;
988
+ }
989
+ });
990
+ Object.defineProperty(exports, 'OPENAPI_OUTPUT_PATH', {
991
+ enumerable: true,
992
+ get: function () {
993
+ return OPENAPI_OUTPUT_PATH;
994
+ }
995
+ });
996
+ Object.defineProperty(exports, 'generateOpenApi', {
997
+ enumerable: true,
998
+ get: function () {
999
+ return generateOpenApi;
1000
+ }
1001
+ });
1002
+ Object.defineProperty(exports, 'openapiCommand', {
1003
+ enumerable: true,
1004
+ get: function () {
1005
+ return openapiCommand;
1006
+ }
1007
+ });
1008
+ Object.defineProperty(exports, 'resolveOpenApiConfig', {
1009
+ enumerable: true,
1010
+ get: function () {
1011
+ return resolveOpenApiConfig;
1012
+ }
1013
+ });
1014
+ //# sourceMappingURL=openapi-CHhTPief.cjs.map