@geekmidas/cli 10.0.0-alpha.2 → 10.0.0-alpha.21

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 (241) hide show
  1. package/README.md +25 -48
  2. package/bin/adjacent-tsconfig.mjs +59 -0
  3. package/bin/gkm.mjs +7 -1
  4. package/dist/{HostingerProvider-Cft7lEYr.mjs → HostingerProvider-BY26bWwB.mjs} +20 -21
  5. package/dist/HostingerProvider-BY26bWwB.mjs.map +1 -0
  6. package/dist/{HostingerProvider-h3EbEp-Z.cjs → HostingerProvider-BZFGYYVA.cjs} +21 -22
  7. package/dist/HostingerProvider-BZFGYYVA.cjs.map +1 -0
  8. package/dist/{LocalStateProvider-Db9SEjyH.cjs → LocalStateProvider-7HR6SS2t.cjs} +11 -6
  9. package/dist/LocalStateProvider-7HR6SS2t.cjs.map +1 -0
  10. package/dist/{LocalStateProvider-Bn6tS-oL.mjs → LocalStateProvider-D7QyJoLM.mjs} +10 -4
  11. package/dist/LocalStateProvider-D7QyJoLM.mjs.map +1 -0
  12. package/dist/{Route53Provider-RdpUDJQk.mjs → Route53Provider-Cvj40A8I.mjs} +12 -12
  13. package/dist/Route53Provider-Cvj40A8I.mjs.map +1 -0
  14. package/dist/{Route53Provider-di2fMM5N.cjs → Route53Provider-DVILBsvV.cjs} +20 -21
  15. package/dist/Route53Provider-DVILBsvV.cjs.map +1 -0
  16. package/dist/{SSMStateProvider-BRt2py-A.mjs → SSMStateProvider-B0Hu1MKZ.mjs} +13 -4
  17. package/dist/SSMStateProvider-B0Hu1MKZ.mjs.map +1 -0
  18. package/dist/{SSMStateProvider-DyX8s9HI.cjs → SSMStateProvider-VrQ-yeqd.cjs} +17 -9
  19. package/dist/SSMStateProvider-VrQ-yeqd.cjs.map +1 -0
  20. package/dist/{backup-provisioner-BrsHwYG0.cjs → backup-provisioner-BmhIO_Yr.cjs} +21 -19
  21. package/dist/backup-provisioner-BmhIO_Yr.cjs.map +1 -0
  22. package/dist/{backup-provisioner-Bgj5Vi_L.mjs → backup-provisioner-DIVCbfee.mjs} +10 -7
  23. package/dist/backup-provisioner-DIVCbfee.mjs.map +1 -0
  24. package/dist/{bundler-BY1OmV1e.cjs → bundler-mjqGAvu1.cjs} +10 -28
  25. package/dist/bundler-mjqGAvu1.cjs.map +1 -0
  26. package/dist/{bundler-Be8Zg4Wd.mjs → bundler-mwH7WOb1.mjs} +9 -26
  27. package/dist/bundler-mwH7WOb1.mjs.map +1 -0
  28. package/dist/{config-BholeD-0.cjs → config-DSXI480r.cjs} +80 -148
  29. package/dist/config-DSXI480r.cjs.map +1 -0
  30. package/dist/{config-CzWnfcXM.mjs → config-DdKkoUE1.mjs} +33 -88
  31. package/dist/config-DdKkoUE1.mjs.map +1 -0
  32. package/dist/config.cjs +4 -5
  33. package/dist/config.d.cts +14 -17
  34. package/dist/config.d.cts.map +1 -1
  35. package/dist/config.d.mts +14 -17
  36. package/dist/config.d.mts.map +1 -1
  37. package/dist/config.mjs +3 -5
  38. package/dist/{credentials-DQmbRnr1.cjs → credentials-BfmmB4WD.cjs} +1508 -1243
  39. package/dist/credentials-BfmmB4WD.cjs.map +1 -0
  40. package/dist/{credentials-BZcp3CGx.mjs → credentials-CD8ftUS6.mjs} +1374 -1114
  41. package/dist/credentials-CD8ftUS6.mjs.map +1 -0
  42. package/dist/deploy/sniffer-envkit-patch.cjs +18 -10
  43. package/dist/deploy/sniffer-envkit-patch.cjs.map +1 -1
  44. package/dist/deploy/sniffer-envkit-patch.d.cts +0 -2
  45. package/dist/deploy/sniffer-envkit-patch.d.cts.map +1 -1
  46. package/dist/deploy/sniffer-envkit-patch.d.mts +0 -2
  47. package/dist/deploy/sniffer-envkit-patch.d.mts.map +1 -1
  48. package/dist/deploy/sniffer-envkit-patch.mjs +10 -2
  49. package/dist/deploy/sniffer-envkit-patch.mjs.map +1 -1
  50. package/dist/deploy/sniffer-hooks.cjs +13 -7
  51. package/dist/deploy/sniffer-hooks.cjs.map +1 -1
  52. package/dist/deploy/sniffer-hooks.d.cts +1 -2
  53. package/dist/deploy/sniffer-hooks.d.cts.map +1 -1
  54. package/dist/deploy/sniffer-hooks.d.mts +1 -2
  55. package/dist/deploy/sniffer-hooks.d.mts.map +1 -1
  56. package/dist/deploy/sniffer-hooks.mjs +9 -2
  57. package/dist/deploy/sniffer-hooks.mjs.map +1 -1
  58. package/dist/deploy/sniffer-loader.cjs +16 -7
  59. package/dist/deploy/sniffer-loader.cjs.map +1 -1
  60. package/dist/deploy/sniffer-loader.d.cts +1 -1
  61. package/dist/deploy/sniffer-loader.d.mts +1 -1
  62. package/dist/deploy/sniffer-loader.mjs +13 -2
  63. package/dist/deploy/sniffer-loader.mjs.map +1 -1
  64. package/dist/deploy/sniffer-routes-worker.cjs +11 -11
  65. package/dist/deploy/sniffer-routes-worker.cjs.map +1 -1
  66. package/dist/deploy/sniffer-routes-worker.d.cts +1 -1
  67. package/dist/deploy/sniffer-routes-worker.d.mts +1 -1
  68. package/dist/deploy/sniffer-routes-worker.mjs +2 -2
  69. package/dist/deploy/sniffer-routes-worker.mjs.map +1 -1
  70. package/dist/deploy/sniffer-worker.cjs +15 -6
  71. package/dist/deploy/sniffer-worker.cjs.map +1 -1
  72. package/dist/deploy/sniffer-worker.d.cts +1 -3
  73. package/dist/deploy/sniffer-worker.d.cts.map +1 -1
  74. package/dist/deploy/sniffer-worker.d.mts +1 -3
  75. package/dist/deploy/sniffer-worker.d.mts.map +1 -1
  76. package/dist/deploy/sniffer-worker.mjs +15 -4
  77. package/dist/deploy/sniffer-worker.mjs.map +1 -1
  78. package/dist/{discover-DTmFeloL.cjs → discover-BERGFg8v.cjs} +81 -64
  79. package/dist/discover-BERGFg8v.cjs.map +1 -0
  80. package/dist/{discover-CAlFgYec.mjs → discover-CK-n3w6b.mjs} +35 -20
  81. package/dist/discover-CK-n3w6b.mjs.map +1 -0
  82. package/dist/{emit-CbcRolsa.cjs → emit-BdoPFjZf.cjs} +53 -36
  83. package/dist/emit-BdoPFjZf.cjs.map +1 -0
  84. package/dist/{emit-CiZS_5Ub.mjs → emit-Bzq7JsMP.mjs} +24 -6
  85. package/dist/emit-Bzq7JsMP.mjs.map +1 -0
  86. package/dist/{generator-VE2JrFlY.mjs → generator-LfZU7NlU.mjs} +8 -14
  87. package/dist/generator-LfZU7NlU.mjs.map +1 -0
  88. package/dist/{generator-CNzBw7g5.cjs → generator-Wdfp0jCS.cjs} +38 -45
  89. package/dist/generator-Wdfp0jCS.cjs.map +1 -0
  90. package/dist/github-Bho-C6Zg.cjs +216 -0
  91. package/dist/github-Bho-C6Zg.cjs.map +1 -0
  92. package/dist/github-De-yMwVt.mjs +216 -0
  93. package/dist/github-De-yMwVt.mjs.map +1 -0
  94. package/dist/{index-CHB4tSzX.d.cts → index-B4H4XF7A.d.mts} +4 -13
  95. package/dist/index-B4H4XF7A.d.mts.map +1 -0
  96. package/dist/{index-BThUzEIp.d.mts → index-RCUizGxL.d.cts} +4 -13
  97. package/dist/index-RCUizGxL.d.cts.map +1 -0
  98. package/dist/index.cjs +5108 -3381
  99. package/dist/index.cjs.map +1 -1
  100. package/dist/index.d.cts +1 -1
  101. package/dist/index.d.mts +1 -1
  102. package/dist/index.mjs +5025 -3315
  103. package/dist/index.mjs.map +1 -1
  104. package/dist/{openapi-c9bpieI5.cjs → openapi-B8bu_OXN.cjs} +228 -188
  105. package/dist/openapi-B8bu_OXN.cjs.map +1 -0
  106. package/dist/{openapi-CY9xFkTH.mjs → openapi-DfjbhrsM.mjs} +172 -143
  107. package/dist/openapi-DfjbhrsM.mjs.map +1 -0
  108. package/dist/openapi.cjs +5 -6
  109. package/dist/openapi.d.cts +47 -14
  110. package/dist/openapi.d.cts.map +1 -1
  111. package/dist/openapi.d.mts +47 -14
  112. package/dist/openapi.d.mts.map +1 -1
  113. package/dist/openapi.mjs +2 -6
  114. package/dist/reconcile/public.cjs +4 -4
  115. package/dist/reconcile/public.d.cts +8 -26
  116. package/dist/reconcile/public.d.cts.map +1 -1
  117. package/dist/reconcile/public.d.mts +8 -26
  118. package/dist/reconcile/public.d.mts.map +1 -1
  119. package/dist/reconcile/public.mjs +3 -4
  120. package/dist/{fullstack-secrets-D1o9-G5l.mjs → reconcile-gb1lz1Dk.mjs} +43 -17
  121. package/dist/reconcile-gb1lz1Dk.mjs.map +1 -0
  122. package/dist/{fullstack-secrets-C-GGohke.cjs → reconcile-hx_vBvux.cjs} +63 -38
  123. package/dist/reconcile-hx_vBvux.cjs.map +1 -0
  124. package/dist/rolldown-runtime-BhDjJH2R.mjs +15 -0
  125. package/dist/{chunk-CUT6urMc.cjs → rolldown-runtime-C0BPl7ul.cjs} +23 -10
  126. package/dist/ssm-BKN1Cg4z.mjs +60 -0
  127. package/dist/ssm-BKN1Cg4z.mjs.map +1 -0
  128. package/dist/ssm-FxeSAt35.cjs +60 -0
  129. package/dist/ssm-FxeSAt35.cjs.map +1 -0
  130. package/dist/{storage-BJmzYxt8.cjs → storage-DgypfSD6.cjs} +65 -77
  131. package/dist/storage-DgypfSD6.cjs.map +1 -0
  132. package/dist/{storage-Bi24WfoW.mjs → storage-TP0x118M.mjs} +22 -15
  133. package/dist/storage-TP0x118M.mjs.map +1 -0
  134. package/dist/transfer-CXoyQZbB.cjs +151 -0
  135. package/dist/transfer-CXoyQZbB.cjs.map +1 -0
  136. package/dist/transfer-Gsxfy6m-.mjs +122 -0
  137. package/dist/transfer-Gsxfy6m-.mjs.map +1 -0
  138. package/dist/types-7R81hu0i.d.mts +6302 -0
  139. package/dist/types-7R81hu0i.d.mts.map +1 -0
  140. package/dist/types-N7c2O2m7.d.cts +6303 -0
  141. package/dist/types-N7c2O2m7.d.cts.map +1 -0
  142. package/dist/workspace/index.cjs +3 -3
  143. package/dist/workspace/index.d.cts +3 -3
  144. package/dist/workspace/index.d.mts +3 -3
  145. package/dist/workspace/index.mjs +2 -3
  146. package/dist/workspace-Bu3xNCej.mjs +6563 -0
  147. package/dist/workspace-Bu3xNCej.mjs.map +1 -0
  148. package/dist/workspace-CRD9YinV.cjs +6741 -0
  149. package/dist/workspace-CRD9YinV.cjs.map +1 -0
  150. package/package.json +33 -43
  151. package/dist/CachedStateProvider-BLo7zv7S.cjs +0 -3
  152. package/dist/CachedStateProvider-BxoAm-B2.mjs +0 -3
  153. package/dist/CachedStateProvider-CqPdW7CB.cjs +0 -60
  154. package/dist/CachedStateProvider-CqPdW7CB.cjs.map +0 -1
  155. package/dist/CachedStateProvider-DTniehMP.mjs +0 -54
  156. package/dist/CachedStateProvider-DTniehMP.mjs.map +0 -1
  157. package/dist/HostingerProvider-Cft7lEYr.mjs.map +0 -1
  158. package/dist/HostingerProvider-h3EbEp-Z.cjs.map +0 -1
  159. package/dist/LocalStateProvider-Bn6tS-oL.mjs.map +0 -1
  160. package/dist/LocalStateProvider-Db9SEjyH.cjs.map +0 -1
  161. package/dist/Route53Provider-RdpUDJQk.mjs.map +0 -1
  162. package/dist/Route53Provider-di2fMM5N.cjs.map +0 -1
  163. package/dist/SSMStateProvider-BRt2py-A.mjs.map +0 -1
  164. package/dist/SSMStateProvider-DyX8s9HI.cjs.map +0 -1
  165. package/dist/backup-provisioner-Bgj5Vi_L.mjs.map +0 -1
  166. package/dist/backup-provisioner-BrsHwYG0.cjs.map +0 -1
  167. package/dist/bundler-BY1OmV1e.cjs.map +0 -1
  168. package/dist/bundler-Be8Zg4Wd.mjs.map +0 -1
  169. package/dist/chunk-Duj1WY3L.mjs +0 -7
  170. package/dist/config-BholeD-0.cjs.map +0 -1
  171. package/dist/config-CzWnfcXM.mjs.map +0 -1
  172. package/dist/credentials-B5zRHdAl.mjs +0 -141
  173. package/dist/credentials-B5zRHdAl.mjs.map +0 -1
  174. package/dist/credentials-BZcp3CGx.mjs.map +0 -1
  175. package/dist/credentials-BapzjyV5.cjs +0 -195
  176. package/dist/credentials-BapzjyV5.cjs.map +0 -1
  177. package/dist/credentials-DQmbRnr1.cjs.map +0 -1
  178. package/dist/discover-CAlFgYec.mjs.map +0 -1
  179. package/dist/discover-DTmFeloL.cjs.map +0 -1
  180. package/dist/dokploy-api-BS2xnJLZ.mjs +0 -3
  181. package/dist/dokploy-api-BsVH0y_s.cjs +0 -718
  182. package/dist/dokploy-api-BsVH0y_s.cjs.map +0 -1
  183. package/dist/dokploy-api-BuxDRo3l.cjs +0 -3
  184. package/dist/dokploy-api-aTFPz7_4.mjs +0 -706
  185. package/dist/dokploy-api-aTFPz7_4.mjs.map +0 -1
  186. package/dist/emit-CbcRolsa.cjs.map +0 -1
  187. package/dist/emit-CiZS_5Ub.mjs.map +0 -1
  188. package/dist/encryption-C6jaU0hE.cjs +0 -54
  189. package/dist/encryption-C6jaU0hE.cjs.map +0 -1
  190. package/dist/encryption-CHzzeR8a.cjs +0 -4
  191. package/dist/encryption-CQUncu7v.mjs +0 -3
  192. package/dist/encryption-D-abyhZx.mjs +0 -42
  193. package/dist/encryption-D-abyhZx.mjs.map +0 -1
  194. package/dist/fullstack-secrets-C-GGohke.cjs.map +0 -1
  195. package/dist/fullstack-secrets-D1o9-G5l.mjs.map +0 -1
  196. package/dist/generator-CNzBw7g5.cjs.map +0 -1
  197. package/dist/generator-VE2JrFlY.mjs.map +0 -1
  198. package/dist/index-BThUzEIp.d.mts.map +0 -1
  199. package/dist/index-CHB4tSzX.d.cts.map +0 -1
  200. package/dist/openapi-CY9xFkTH.mjs.map +0 -1
  201. package/dist/openapi-c9bpieI5.cjs.map +0 -1
  202. package/dist/openapi-react-query-Byltvm_7.mjs +0 -167
  203. package/dist/openapi-react-query-Byltvm_7.mjs.map +0 -1
  204. package/dist/openapi-react-query-CmJ1xDbn.cjs +0 -173
  205. package/dist/openapi-react-query-CmJ1xDbn.cjs.map +0 -1
  206. package/dist/openapi-react-query.cjs +0 -4
  207. package/dist/openapi-react-query.d.cts +0 -11
  208. package/dist/openapi-react-query.d.cts.map +0 -1
  209. package/dist/openapi-react-query.d.mts +0 -11
  210. package/dist/openapi-react-query.d.mts.map +0 -1
  211. package/dist/openapi-react-query.mjs +0 -4
  212. package/dist/reconcile-CFs8C_1a.mjs +0 -37
  213. package/dist/reconcile-CFs8C_1a.mjs.map +0 -1
  214. package/dist/reconcile-D0pimNiy.cjs +0 -37
  215. package/dist/reconcile-D0pimNiy.cjs.map +0 -1
  216. package/dist/setup-Bc73t0ve.mjs +0 -443
  217. package/dist/setup-Bc73t0ve.mjs.map +0 -1
  218. package/dist/setup-C48C9hto.cjs +0 -473
  219. package/dist/setup-C48C9hto.cjs.map +0 -1
  220. package/dist/setup-DCuudnCl.mjs +0 -11
  221. package/dist/setup-fG_ivuxO.cjs +0 -11
  222. package/dist/storage-BJmzYxt8.cjs.map +0 -1
  223. package/dist/storage-BM9hGpgv.cjs +0 -7
  224. package/dist/storage-Bi24WfoW.mjs.map +0 -1
  225. package/dist/storage-CYlGwWTj.mjs +0 -3
  226. package/dist/sync-CmEuNZsB.mjs +0 -76
  227. package/dist/sync-CmEuNZsB.mjs.map +0 -1
  228. package/dist/sync-D306yOWu.cjs +0 -4
  229. package/dist/sync-SizaChmC.cjs +0 -93
  230. package/dist/sync-SizaChmC.cjs.map +0 -1
  231. package/dist/sync-hOqAWHR6.mjs +0 -3
  232. package/dist/types-C-arnQ1r.d.mts +0 -2184
  233. package/dist/types-C-arnQ1r.d.mts.map +0 -1
  234. package/dist/types-abJQuk7m.d.cts +0 -2184
  235. package/dist/types-abJQuk7m.d.cts.map +0 -1
  236. package/dist/workspace-7FqprxuJ.mjs +0 -4323
  237. package/dist/workspace-7FqprxuJ.mjs.map +0 -1
  238. package/dist/workspace-BO_KWohI.cjs +0 -9
  239. package/dist/workspace-DK4WWJIn.mjs +0 -8
  240. package/dist/workspace-XvqO9jIv.cjs +0 -4472
  241. package/dist/workspace-XvqO9jIv.cjs.map +0 -1
@@ -1,12 +1,12 @@
1
- import { ConstructGenerator, isPartitionedRoutes } from "./discover-CAlFgYec.mjs";
2
- import { loadWorkspaceConfig } from "./config-CzWnfcXM.mjs";
3
- import { dirname, join, relative } from "node:path";
1
+ import { r as ConstructGenerator } from "./discover-CK-n3w6b.mjs";
2
+ import { o as loadWorkspaceConfig } from "./config-DdKkoUE1.mjs";
4
3
  import { mkdir, writeFile } from "node:fs/promises";
4
+ import { dirname, join, relative, resolve } from "node:path";
5
5
  import { spawn } from "node:child_process";
6
+ import { kebabCase } from "@geekmidas/manifest";
6
7
  import { Endpoint } from "@geekmidas/constructs/endpoints";
7
8
  import { fileURLToPath } from "node:url";
8
9
  import { StandardSchemaJsonSchema, getRegisteredZodJsonSchemas, getSchemaMetadata } from "@geekmidas/schema/conversion";
9
-
10
10
  //#region src/build/endpoint-analyzer.ts
11
11
  /**
12
12
  * Analyze an endpoint to extract its features
@@ -83,7 +83,6 @@ function summarizeAnalysis(analyses) {
83
83
  byFeature
84
84
  };
85
85
  }
86
-
87
86
  //#endregion
88
87
  //#region src/build/handler-templates.ts
89
88
  /**
@@ -105,8 +104,7 @@ function generateMinimalHandler(analysis) {
105
104
  const { exportName, features } = analysis;
106
105
  const method = analysis.method.toLowerCase();
107
106
  const validators = generateValidators(exportName, features);
108
- const hasValidators = validators.length > 0;
109
- if (!hasValidators && !features.hasOutputValidation) return `
107
+ if (!(validators.length > 0) && !features.hasOutputValidation) return `
110
108
  // Minimal handler: ${analysis.route} (${analysis.method})
111
109
  app.${method}('${analysis.route}', async (c) => {
112
110
  const result = await ${exportName}.handler(
@@ -304,8 +302,7 @@ ${factories.join("\n")}
304
302
  */
305
303
  function generateMinimalEndpointFile(analysis, endpointImport) {
306
304
  const { exportName, features } = analysis;
307
- const needsValidators = features.hasBodyValidation || features.hasQueryValidation || features.hasParamValidation;
308
- const validatorImport = needsValidators ? generateValidatorImportsForEndpoint(analysis) : "";
305
+ const validatorImport = features.hasBodyValidation || features.hasQueryValidation || features.hasParamValidation ? generateValidatorImportsForEndpoint(analysis) : "";
309
306
  const handler = generateMinimalHandler(analysis);
310
307
  return `/**
311
308
  * Minimal endpoint: ${analysis.route} (${analysis.method})
@@ -330,8 +327,7 @@ ${handler}
330
327
  */
331
328
  function generateStandardEndpointFile(analysis, endpointImport) {
332
329
  const { exportName, features } = analysis;
333
- const needsValidators = features.hasBodyValidation || features.hasQueryValidation || features.hasParamValidation;
334
- const validatorImport = needsValidators ? generateValidatorImportsForEndpoint(analysis) : "";
330
+ const validatorImport = features.hasBodyValidation || features.hasQueryValidation || features.hasParamValidation ? generateValidatorImportsForEndpoint(analysis) : "";
335
331
  const eventsImport = features.hasEvents ? `import { publishConstructEvents } from '@geekmidas/constructs/endpoints';` : "";
336
332
  const handler = generateStandardHandler(analysis);
337
333
  return `/**
@@ -423,7 +419,7 @@ export function setupFullEndpoints(
423
419
  }
424
420
  const imports = tierEndpoints.map((a) => `import { setup${capitalize(a.exportName)} } from './${a.exportName}.js';`).join("\n");
425
421
  if (tier === "minimal") {
426
- const calls$1 = tierEndpoints.map((a) => ` setup${capitalize(a.exportName)}(app, logger);`).join("\n");
422
+ const calls = tierEndpoints.map((a) => ` setup${capitalize(a.exportName)}(app, logger);`).join("\n");
427
423
  return `/**
428
424
  * Minimal-tier endpoint index (${tierEndpoints.length} endpoints)
429
425
  * Near-raw-Hono performance
@@ -436,12 +432,12 @@ export function setupMinimalEndpoints(
436
432
  app: Hono,
437
433
  logger: Logger,
438
434
  ): void {
439
- ${calls$1}
435
+ ${calls}
440
436
  }
441
437
  `;
442
438
  }
443
439
  if (tier === "standard") {
444
- const calls$1 = tierEndpoints.map((a) => ` setup${capitalize(a.exportName)}(app, serviceDiscovery, logger);`).join("\n");
440
+ const calls = tierEndpoints.map((a) => ` setup${capitalize(a.exportName)}(app, serviceDiscovery, logger);`).join("\n");
445
441
  return `/**
446
442
  * Standard-tier endpoint index (${tierEndpoints.length} endpoints)
447
443
  * Auth and/or services enabled
@@ -456,7 +452,7 @@ export function setupStandardEndpoints(
456
452
  serviceDiscovery: ServiceDiscovery<any, any>,
457
453
  logger: Logger,
458
454
  ): void {
459
- ${calls$1}
455
+ ${calls}
460
456
  }
461
457
  `;
462
458
  }
@@ -526,9 +522,7 @@ function generateEndpointFilesNested(analyses, endpointImports) {
526
522
  case "standard":
527
523
  files[fileName] = generateStandardEndpointFile(analysis, endpointImport);
528
524
  break;
529
- case "full":
530
- files[fileName] = generateFullEndpointFile(analysis, endpointImport);
531
- break;
525
+ case "full": files[fileName] = generateFullEndpointFile(analysis, endpointImport);
532
526
  }
533
527
  }
534
528
  return files;
@@ -585,7 +579,6 @@ export async function setupEndpoints(
585
579
  }
586
580
  `;
587
581
  }
588
-
589
582
  //#endregion
590
583
  //#region src/generators/EndpointGenerator.ts
591
584
  /**
@@ -602,24 +595,21 @@ export async function setupEndpoints(
602
595
  *
603
596
  * Falls back to the configured paths for an app that declares no surface.
604
597
  */
605
- function runtimeFor(context, fromDir) {
606
- const module = context.surface?.module;
598
+ function runtimeFor(context, fromDir, owner) {
599
+ const module = owner ? context.owners?.[owner] ?? context.surface?.module : context.surface?.module;
607
600
  if (module) {
608
601
  const specifier = importSpecifier(fromDir, module.specifier);
609
602
  return {
610
603
  imports: `import { ${module.exportName} as __surface } from '${specifier}';`,
611
604
  bindings: [
612
- "// What this surface was declared with. Objects, not module paths:",
613
- "// the entry imports the surface, so there is nothing to print.",
605
+ "// What this construct was declared with. Objects, not module paths:",
606
+ "// the entry imports the owner, so there is nothing to print.",
614
607
  "const envParser = __surface.envParser;",
615
608
  "const logger = __surface.logger;"
616
609
  ].join("\n")
617
610
  };
618
611
  }
619
- return {
620
- imports: [`import ${context.envParserImportPattern} from '${importSpecifier(fromDir, context.envParserPath)}';`, `import ${context.loggerImportPattern} from '${importSpecifier(fromDir, context.loggerPath)}';`].join("\n"),
621
- bindings: ""
622
- };
612
+ throw new Error(`No owner to take a logger and an environment parser from${owner ? ` for "${owner}"` : ""}. Build it from a \`RestApi\` or a \`Worker\`.`);
623
613
  }
624
614
  /** A relative specifier ESM will accept: always prefixed, never bare. */
625
615
  function importSpecifier(fromDir, target) {
@@ -734,8 +724,7 @@ var EndpointGenerator = class extends ConstructGenerator {
734
724
  async generateHandlerFile(outputDir, sourceFile, exportName, provider, _endpoint, context) {
735
725
  const handlerFileName = `${exportName}.ts`;
736
726
  const handlerPath = join(outputDir, handlerFileName);
737
- const relativePath = relative(dirname(handlerPath), sourceFile);
738
- const importPath = relativePath.replace(/\.ts$/, ".js");
727
+ const importPath = relative(dirname(handlerPath), sourceFile).replace(/\.ts$/, ".js");
739
728
  let content;
740
729
  switch (provider) {
741
730
  case "aws-apigatewayv1":
@@ -753,12 +742,10 @@ var EndpointGenerator = class extends ConstructGenerator {
753
742
  return handlerPath;
754
743
  }
755
744
  async generateEndpointsFile(outputDir, endpoints, context) {
756
- const endpointsFileName = "endpoints.ts";
757
- const endpointsPath = join(outputDir, endpointsFileName);
745
+ const endpointsPath = join(outputDir, "endpoints.ts");
758
746
  const importsByFile = /* @__PURE__ */ new Map();
759
747
  for (const { path, key } of endpoints) {
760
- const relativePath = relative(dirname(endpointsPath), path.relative);
761
- const importPath = relativePath.replace(/\.ts$/, ".js");
748
+ const importPath = relative(dirname(endpointsPath), path.relative).replace(/\.ts$/, ".js");
762
749
  if (!importsByFile.has(importPath)) importsByFile.set(importPath, []);
763
750
  importsByFile.get(importPath)?.push(key);
764
751
  }
@@ -824,11 +811,9 @@ export async function setupEndpoints(
824
811
  const analyses = endpoints.map(({ key, construct }) => analyzeEndpoint(construct, key));
825
812
  const endpointImports = endpoints.map(({ key, path }) => {
826
813
  const tierDir = join(endpointsDir, "standard");
827
- const relativePath = relative(tierDir, path.relative);
828
- const importPath = relativePath.replace(/\.ts$/, ".js");
829
814
  return {
830
815
  exportName: key,
831
- importPath
816
+ importPath: relative(tierDir, path.relative).replace(/\.ts$/, ".js")
832
817
  };
833
818
  });
834
819
  const summary = summarizeAnalysis(analyses);
@@ -850,8 +835,7 @@ export async function setupEndpoints(
850
835
  }
851
836
  async generateAppFile(outputDir, context) {
852
837
  if (context.production?.enabled) return this.generateProductionAppFile(outputDir, context);
853
- const appFileName = "app.ts";
854
- const appPath = join(outputDir, appFileName);
838
+ const appPath = join(outputDir, "app.ts");
855
839
  const runtime = runtimeFor(context, dirname(appPath));
856
840
  const telescopeEnabled = context.telescope?.enabled;
857
841
  const telescopeWebSocketEnabled = context.telescope?.websocket;
@@ -861,32 +845,35 @@ export async function setupEndpoints(
861
845
  const studioFromDatabase = !usesExternalStudio && !!context.studio?.database;
862
846
  const telescopeFromSurface = !!context.surface?.module && !usesExternalTelescope;
863
847
  let telescopeImports = "";
864
- if (telescopeEnabled) if (telescopeFromSurface) telescopeImports = "import { createMiddleware, createUI } from '@geekmidas/telescope/hono';";
865
- else if (usesExternalTelescope) {
866
- const relativeTelescopePath = relative(dirname(appPath), context.telescope?.telescopePath);
867
- telescopeImports = `import ${context.telescope?.telescopeImportPattern} from '${relativeTelescopePath}';
848
+ if (telescopeEnabled) {
849
+ if (telescopeFromSurface) telescopeImports = "import { createMiddleware, createUI } from '@geekmidas/telescope/hono';";
850
+ else if (usesExternalTelescope) {
851
+ const relativeTelescopePath = relative(dirname(appPath), context.telescope?.telescopePath);
852
+ telescopeImports = `import ${context.telescope?.telescopeImportPattern} from '${relativeTelescopePath}';
868
853
  import { createMiddleware, createUI } from '@geekmidas/telescope/hono';`;
869
- } else telescopeImports = `import { Telescope, InMemoryStorage } from '@geekmidas/telescope';
854
+ } else telescopeImports = `import { Telescope, InMemoryStorage } from '@geekmidas/telescope';
870
855
  import { createMiddleware, createUI } from '@geekmidas/telescope/hono';`;
856
+ }
871
857
  let studioImports = "";
872
- if (studioEnabled) if (usesExternalStudio) {
873
- const relativeStudioPath = relative(dirname(appPath), context.studio?.studioPath);
874
- studioImports = `import ${context.studio?.studioImportPattern} from '${relativeStudioPath}';
858
+ if (studioEnabled) {
859
+ if (usesExternalStudio) {
860
+ const relativeStudioPath = relative(dirname(appPath), context.studio?.studioPath);
861
+ studioImports = `import ${context.studio?.studioImportPattern} from '${relativeStudioPath}';
875
862
  import { createStudioApp } from '@geekmidas/studio/server/hono';`;
876
- } else if (studioFromDatabase) {
877
- const dbSpecifier = importSpecifier(dirname(appPath), context.studio.database.specifier);
878
- studioImports = `import { snifferContext } from '@geekmidas/constructs';
863
+ } else if (studioFromDatabase) {
864
+ const dbSpecifier = importSpecifier(dirname(appPath), context.studio.database.specifier);
865
+ studioImports = `import { snifferContext } from '@geekmidas/constructs';
879
866
  import { Direction, InMemoryMonitoringStorage, Studio } from '@geekmidas/studio';
880
867
  import { ${context.studio.database.exportName} as __studioDb } from '${dbSpecifier}';
881
868
  import { createStudioApp } from '@geekmidas/studio/server/hono';`;
882
- } else studioImports = "";
869
+ } else studioImports = "";
870
+ }
883
871
  const cors = corsFor(context.surface);
884
872
  let hooksImports = "";
885
873
  let beforeSetupCall = "";
886
874
  let afterSetupCall = "";
887
875
  if (context.hooks?.serverHooksPath) {
888
- const relativeHooksPath = relative(dirname(appPath), context.hooks.serverHooksPath);
889
- hooksImports = `import * as serverHooks from '${relativeHooksPath}';`;
876
+ hooksImports = `import * as serverHooks from '${relative(dirname(appPath), context.hooks.serverHooksPath)}';`;
890
877
  beforeSetupCall = `
891
878
  // Call beforeSetup hook if defined
892
879
  if (typeof serverHooks.beforeSetup === 'function') {
@@ -932,7 +919,8 @@ import { createStudioApp } from '@geekmidas/studio/server/hono';`;
932
919
  }
933
920
  ` : "";
934
921
  let telescopeSetup = "";
935
- if (telescopeEnabled) if (telescopeFromSurface || usesExternalTelescope) telescopeSetup = `
922
+ if (telescopeEnabled) {
923
+ if (telescopeFromSurface || usesExternalTelescope) telescopeSetup = `
936
924
  ${telescopeWebSocketSetupCode}
937
925
  // Add telescope middleware (before endpoints to capture all requests)
938
926
  honoApp.use('*', createMiddleware(telescope));
@@ -941,7 +929,7 @@ ${telescopeWebSocketSetupCode}
941
929
  const telescopeUI = createUI(telescope);
942
930
  honoApp.route('${context.telescope?.path}', telescopeUI);
943
931
  `;
944
- else telescopeSetup = `
932
+ else telescopeSetup = `
945
933
  // Setup Telescope for debugging/monitoring
946
934
  const telescopeStorage = new InMemoryStorage({ maxEntries: ${context.telescope?.maxEntries} });
947
935
  const telescope = new Telescope({
@@ -959,6 +947,7 @@ ${telescopeWebSocketSetupCode}
959
947
  const telescopeUI = createUI(telescope);
960
948
  honoApp.route('${context.telescope?.path}', telescopeUI);
961
949
  `;
950
+ }
962
951
  let studioSetup = "";
963
952
  if (studioEnabled && studioFromDatabase) studioSetup = `
964
953
  // Studio, built from the declared database. The client comes from the
@@ -992,6 +981,7 @@ import type { Hono as HonoType } from 'hono';
992
981
  import { setupEndpoints } from './endpoints.js';
993
982
  import { setupSubscribers } from './subscribers.js';
994
983
  import { setupQueues } from './queues.js';
984
+ import { setupCrons } from './crons.js';
995
985
  ${runtime.imports}
996
986
  ${telescopeImports}
997
987
  ${studioImports}
@@ -1079,6 +1069,12 @@ ${afterSetupCall}
1079
1069
  logger.error({ error }, 'Failed to start queue workers');
1080
1070
  });
1081
1071
 
1072
+ // Schedule this app's crons. Caught like the others: a scheduler that
1073
+ // cannot start is not a reason for the HTTP server not to.
1074
+ await setupCrons(envParser, logger).catch((error) => {
1075
+ logger.error({ error }, 'Failed to schedule crons');
1076
+ });
1077
+
1082
1078
  logger.info({ port }, 'Starting server');
1083
1079
 
1084
1080
  // Start HTTP server using provided serve function
@@ -1147,10 +1143,8 @@ export const handler = ${exportName};
1147
1143
  * No dev tools (Telescope, Studio, WebSocket), includes health checks and graceful shutdown
1148
1144
  */
1149
1145
  async generateProductionAppFile(outputDir, context) {
1150
- const appFileName = "app.ts";
1151
- const appPath = join(outputDir, appFileName);
1152
- const relativeLoggerPath = relative(dirname(appPath), context.loggerPath);
1153
- const relativeEnvParserPath = relative(dirname(appPath), context.envParserPath);
1146
+ const appPath = join(outputDir, "app.ts");
1147
+ const runtime = runtimeFor(context, dirname(appPath));
1154
1148
  const production = context.production;
1155
1149
  const healthCheckPath = production.healthCheck;
1156
1150
  const enableGracefulShutdown = production.gracefulShutdown;
@@ -1161,8 +1155,7 @@ export const handler = ${exportName};
1161
1155
  let beforeSetupCall = "";
1162
1156
  let afterSetupCall = "";
1163
1157
  if (context.hooks?.serverHooksPath) {
1164
- const relativeHooksPath = relative(dirname(appPath), context.hooks.serverHooksPath);
1165
- hooksImports = `import * as serverHooks from '${relativeHooksPath}';`;
1158
+ hooksImports = `import * as serverHooks from '${relative(dirname(appPath), context.hooks.serverHooksPath)}';`;
1166
1159
  beforeSetupCall = `
1167
1160
  // Call beforeSetup hook if defined
1168
1161
  if (typeof serverHooks.beforeSetup === 'function') {
@@ -1186,9 +1179,16 @@ export const handler = ${exportName};
1186
1179
  await setupQueues(envParser, logger).catch((error) => {
1187
1180
  logger.error({ error }, 'Failed to start queue workers');
1188
1181
  });
1182
+
1183
+ // Schedule this app's crons. Caught like the others: a scheduler that
1184
+ // cannot start is not a reason for the HTTP server not to.
1185
+ await setupCrons(envParser, logger).catch((error) => {
1186
+ logger.error({ error }, 'Failed to schedule crons');
1187
+ });
1189
1188
  ` : "";
1190
1189
  const subscriberImport = includeSubscribers ? `import { setupSubscribers } from './subscribers.js';
1191
- import { setupQueues } from './queues.js';` : "";
1190
+ import { setupQueues } from './queues.js';
1191
+ import { setupCrons } from './crons.js';` : "";
1192
1192
  const gracefulShutdownCode = enableGracefulShutdown ? `
1193
1193
  // Graceful shutdown handling
1194
1194
  let isShuttingDown = false;
@@ -1204,7 +1204,6 @@ import { setupQueues } from './queues.js';` : "";
1204
1204
  process.on('SIGTERM', shutdown);
1205
1205
  process.on('SIGINT', shutdown);
1206
1206
  ` : "";
1207
- const endpointsImportPath = production.optimizedHandlers ? "./endpoints/index.js" : "./endpoints.js";
1208
1207
  const content = `/**
1209
1208
  * Generated production server application
1210
1209
  *
@@ -1216,10 +1215,10 @@ import { setupQueues } from './queues.js';` : "";
1216
1215
  */
1217
1216
  import { Hono } from 'hono';
1218
1217
  import type { Hono as HonoType } from 'hono';
1219
- import { setupEndpoints } from '${endpointsImportPath}';
1218
+ import { setupEndpoints } from '${production.optimizedHandlers ? "./endpoints/index.js" : "./endpoints.js"}';
1220
1219
  ${subscriberImport}
1221
- import ${context.envParserImportPattern} from '${relativeEnvParserPath}';
1222
- import ${context.loggerImportPattern} from '${relativeLoggerPath}';
1220
+ ${runtime.imports}
1221
+ ${runtime.bindings}
1223
1222
  ${hooksImports}
1224
1223
  ${cors.imports}
1225
1224
  ${context.storageDrivers?.imports ?? ""}
@@ -1283,7 +1282,7 @@ export default createApp;
1283
1282
  */
1284
1283
  async generateProductionServerEntry(outputDir) {
1285
1284
  const serverPath = join(outputDir, "server.ts");
1286
- const content = `#!/usr/bin/env node
1285
+ await writeFile(serverPath, `#!/usr/bin/env node
1287
1286
  /**
1288
1287
  * Production server entry point
1289
1288
  * Generated by 'gkm build --provider server --production'
@@ -1299,11 +1298,9 @@ await start({
1299
1298
  port,
1300
1299
  serve: (app, port) => { serve({ fetch: app.fetch, port }); },
1301
1300
  });
1302
- `;
1303
- await writeFile(serverPath, content);
1301
+ `);
1304
1302
  }
1305
1303
  };
1306
-
1307
1304
  //#endregion
1308
1305
  //#region src/generators/OpenApiTsGenerator.ts
1309
1306
  /**
@@ -1458,7 +1455,7 @@ var OpenApiTsGenerator = class {
1458
1455
  for (const [rawName, rawSchema] of Object.entries(registrySchemas)) {
1459
1456
  const defName = this.pascalCase(rawName);
1460
1457
  if (collectedDefs.has(defName) || generatedNames.has(defName)) continue;
1461
- const { id: _id, $defs: _defs,...rest } = rawSchema;
1458
+ const { id: _id, $defs: _defs, ...rest } = rawSchema;
1462
1459
  collectedDefs.set(defName, rest);
1463
1460
  }
1464
1461
  for (const [defName, defSchema] of collectedDefs) if (!generatedNames.has(defName)) {
@@ -1494,27 +1491,29 @@ var OpenApiTsGenerator = class {
1494
1491
  async schemaToInterfaceWithDefs(schema, name, collectedDefs) {
1495
1492
  try {
1496
1493
  const vendor = schema["~standard"]?.vendor;
1497
- if (!vendor || !(vendor in StandardSchemaJsonSchema)) return null;
1494
+ if (!vendor || !(vendor in StandardSchemaJsonSchema)) return unknownType(name);
1498
1495
  const toJsonSchema = StandardSchemaJsonSchema[vendor];
1499
1496
  const jsonSchema = await toJsonSchema(schema);
1500
- if (!jsonSchema) return null;
1497
+ if (!jsonSchema) return unknownType(name);
1501
1498
  if (jsonSchema.$defs && typeof jsonSchema.$defs === "object") {
1502
1499
  for (const [defName, defSchema] of Object.entries(jsonSchema.$defs)) if (!collectedDefs.has(defName)) {
1503
- const { id,...schemaWithoutId } = defSchema;
1500
+ const { id, ...schemaWithoutId } = defSchema;
1504
1501
  collectedDefs.set(defName, schemaWithoutId);
1505
1502
  }
1506
1503
  }
1507
- const { $defs,...schemaWithoutDefs } = jsonSchema;
1504
+ const { $defs, ...schemaWithoutDefs } = jsonSchema;
1505
+ const ref = schemaWithoutDefs.$ref;
1506
+ if (ref && Object.keys(schemaWithoutDefs).every((k) => k === "$ref" || k === "$schema")) {
1507
+ const target = ref.split("/").pop();
1508
+ return target === name ? null : `export type ${name} = ${target};`;
1509
+ }
1508
1510
  return this.jsonSchemaToInterface(schemaWithoutDefs, name);
1509
1511
  } catch {
1510
- return null;
1512
+ return unknownType(name);
1511
1513
  }
1512
1514
  }
1513
1515
  jsonSchemaToInterface(schema, name) {
1514
- if (schema.type !== "object" || !schema.properties) {
1515
- const typeStr = this.jsonSchemaTypeToTs(schema);
1516
- return `export type ${name} = ${typeStr};`;
1517
- }
1516
+ if (schema.type !== "object" || !schema.properties) return `export type ${name} = ${this.jsonSchemaTypeToTs(schema)};`;
1518
1517
  const props = [];
1519
1518
  const required = new Set(schema.required || []);
1520
1519
  for (const [propName, propSchema] of Object.entries(schema.properties)) {
@@ -1527,13 +1526,15 @@ var OpenApiTsGenerator = class {
1527
1526
  }
1528
1527
  jsonSchemaTypeToTs(schema) {
1529
1528
  if (!schema) return "unknown";
1530
- if (schema.$ref) {
1531
- const refName = schema.$ref.split("/").pop() || "unknown";
1532
- return refName;
1533
- }
1529
+ if (schema.$ref) return schema.$ref.split("/").pop() || "unknown";
1530
+ if (schema.const !== void 0) return typeof schema.const === "string" ? `'${schema.const}'` : String(schema.const);
1534
1531
  if (schema.anyOf) return schema.anyOf.map((s) => this.jsonSchemaTypeToTs(s)).join(" | ");
1535
1532
  if (schema.oneOf) return schema.oneOf.map((s) => this.jsonSchemaTypeToTs(s)).join(" | ");
1536
1533
  if (schema.allOf) return schema.allOf.map((s) => this.jsonSchemaTypeToTs(s)).join(" & ");
1534
+ if (Array.isArray(schema.type)) return schema.type.map((type) => this.jsonSchemaTypeToTs({
1535
+ ...schema,
1536
+ type
1537
+ })).join(" | ");
1537
1538
  switch (schema.type) {
1538
1539
  case "string":
1539
1540
  if (schema.enum) return schema.enum.map((e) => `'${e}'`).join(" | ");
@@ -1557,10 +1558,7 @@ var OpenApiTsGenerator = class {
1557
1558
  }
1558
1559
  return `{ ${props.join("; ")} }`;
1559
1560
  }
1560
- if (schema.additionalProperties) {
1561
- const valueType = this.jsonSchemaTypeToTs(schema.additionalProperties);
1562
- return `Record<string, ${valueType}>`;
1563
- }
1561
+ if (schema.additionalProperties) return `Record<string, ${this.jsonSchemaTypeToTs(schema.additionalProperties)}>`;
1564
1562
  return "Record<string, unknown>";
1565
1563
  default: return "unknown";
1566
1564
  }
@@ -1581,10 +1579,7 @@ var OpenApiTsGenerator = class {
1581
1579
  }
1582
1580
  const firstWithParams = infos.find((i) => i.input?.params);
1583
1581
  let paramsEntry = "";
1584
- if (firstWithParams?.input?.params) {
1585
- const paramsName = await this.getSchemaName(firstWithParams.input.params, `${this.getSchemaBaseName(firstWithParams)}Params`);
1586
- paramsEntry = `\n parameters: {\n path: ${paramsName};\n };`;
1587
- }
1582
+ if (firstWithParams?.input?.params) paramsEntry = `\n parameters: {\n path: ${await this.getSchemaName(firstWithParams.input.params, `${this.getSchemaBaseName(firstWithParams)}Params`)};\n };`;
1588
1583
  pathEntries.push(` '${route}': {${paramsEntry}\n${methodEntries.join("\n")}\n };`);
1589
1584
  }
1590
1585
  return `export interface paths {\n${pathEntries.join("\n")}\n}`;
@@ -1623,8 +1618,7 @@ var OpenApiTsGenerator = class {
1623
1618
  return acc;
1624
1619
  }, {});
1625
1620
  const schemeNames = securitySchemes.map((s) => `'${s.name}'`).join(" | ");
1626
- const hasSecuritySchemes = schemeNames.length > 0;
1627
- const createApiSection = hasSecuritySchemes ? `
1621
+ const createApiSection = schemeNames.length > 0 ? `
1628
1622
  // ============================================================
1629
1623
  // API Client Factory
1630
1624
  // ============================================================
@@ -1801,20 +1795,14 @@ ${createApiSection}
1801
1795
  `;
1802
1796
  }
1803
1797
  };
1804
-
1805
- //#endregion
1806
- //#region src/workspace/client-generator.ts
1807
1798
  /**
1808
- * Normalize routes to an array of patterns.
1809
- * Handles string, string[], and PartitionedRoutes (extracts paths).
1810
- * @internal Exported for use in dev command
1799
+ * The declaration for a schema that cannot be converted — a vendor with no
1800
+ * converter, or one that fails. The paths interface names it either way, so
1801
+ * leaving it undeclared made the generated client fail to compile.
1811
1802
  */
1812
- function normalizeRoutes(routes) {
1813
- if (!routes) return [];
1814
- if (isPartitionedRoutes(routes)) return Array.isArray(routes.paths) ? routes.paths : [routes.paths];
1815
- return Array.isArray(routes) ? routes : [routes];
1803
+ function unknownType(name) {
1804
+ return `export type ${name} = unknown;`;
1816
1805
  }
1817
-
1818
1806
  //#endregion
1819
1807
  //#region src/openapi.ts
1820
1808
  /**
@@ -1822,6 +1810,16 @@ function normalizeRoutes(routes) {
1822
1810
  */
1823
1811
  const OPENAPI_OUTPUT_PATH = "./.gkm/openapi.ts";
1824
1812
  /**
1813
+ * Where a surface's generated client lands.
1814
+ *
1815
+ * Named from the surface, so `new RestApi('Webhooks')` writes
1816
+ * `.gkm/openapi/webhooks.ts` — the same kebab form that gives it its container
1817
+ * name and its `WEBHOOKS_URL`.
1818
+ */
1819
+ function openApiPathFor(surfaceId) {
1820
+ return `./.gkm/openapi/${kebabCase(surfaceId)}.ts`;
1821
+ }
1822
+ /**
1825
1823
  * Resolve OpenAPI config from GkmConfig
1826
1824
  */
1827
1825
  function resolveOpenApiConfig(config) {
@@ -1840,33 +1838,65 @@ function resolveOpenApiConfig(config) {
1840
1838
  };
1841
1839
  }
1842
1840
  /**
1843
- * Generate OpenAPI spec from endpoints
1844
- * @returns Object with output path and endpoint count, or null if disabled
1841
+ * Load endpoints from a glob, then generate.
1842
+ *
1843
+ * For the callers that have no endpoints in hand — `gkm openapi`, and `gkm dev`
1844
+ * regenerating on reload. The glob is the app's derived one — the conventional
1845
+ * directories under it — not a `routes` field anybody wrote.
1845
1846
  */
1846
- async function generateOpenApi(config, options = {}) {
1847
+ async function generateOpenApiFrom(routes, options = {}) {
1848
+ return generateOpenApi((await new EndpointGenerator().load(routes, void 0, options.bustCache)).map(({ construct }) => construct), options);
1849
+ }
1850
+ /**
1851
+ * Generate an OpenAPI spec for each surface these endpoints name.
1852
+ *
1853
+ * Takes the endpoints, not a glob to find them with. The build has already
1854
+ * loaded every one of them to generate handlers, so globbing again was a second
1855
+ * discovery pass over the same files — and the reason this function needed a
1856
+ * `routes` to be told where to look, which is how a retired config field stayed
1857
+ * load-bearing.
1858
+ *
1859
+ * @returns What was written, or null if disabled or there were none
1860
+ */
1861
+ async function generateOpenApi(endpoints, options = {}) {
1847
1862
  const logger = options.silent ? { log: () => {} } : console;
1848
- const openApiConfig = resolveOpenApiConfig(config);
1863
+ const openApiConfig = resolveOpenApiConfig({ openapi: options.openapi });
1849
1864
  if (!openApiConfig.enabled) return null;
1850
- const endpointGenerator = new EndpointGenerator();
1851
- const loadedEndpoints = await endpointGenerator.load(config.routes, void 0, options.bustCache);
1852
- if (loadedEndpoints.length === 0) {
1865
+ if (endpoints.length === 0) {
1853
1866
  logger.log("No valid endpoints found for OpenAPI generation");
1854
1867
  return null;
1855
1868
  }
1856
- const endpoints = loadedEndpoints.map(({ construct }) => construct);
1857
- const outputPath = join(process.cwd(), OPENAPI_OUTPUT_PATH);
1858
- await mkdir(dirname(outputPath), { recursive: true });
1869
+ const bySurface = /* @__PURE__ */ new Map();
1870
+ for (const endpoint of endpoints) {
1871
+ const owner = endpoint.owner ?? endpoint.surface?.id;
1872
+ if (!owner) continue;
1873
+ const existing = bySurface.get(owner);
1874
+ if (existing) existing.push(endpoint);
1875
+ else bySurface.set(owner, [endpoint]);
1876
+ }
1877
+ if (bySurface.size === 0) {
1878
+ logger.log("No endpoints named a surface, so no OpenAPI was generated");
1879
+ return null;
1880
+ }
1859
1881
  const tsGenerator = new OpenApiTsGenerator();
1860
- const tsContent = await tsGenerator.generate(endpoints, {
1861
- title: openApiConfig.title,
1862
- version: openApiConfig.version,
1863
- description: openApiConfig.description
1864
- });
1865
- await writeFile(outputPath, tsContent);
1866
- logger.log(`📄 OpenAPI client generated: ${OPENAPI_OUTPUT_PATH}`);
1882
+ const written = [];
1883
+ for (const [surfaceId, surfaceEndpoints] of bySurface) {
1884
+ const relative = openApiPathFor(surfaceId);
1885
+ const outputPath = join(process.cwd(), relative);
1886
+ await mkdir(dirname(outputPath), { recursive: true });
1887
+ const tsContent = await tsGenerator.generate(surfaceEndpoints, {
1888
+ title: openApiConfig.title ?? surfaceId,
1889
+ version: openApiConfig.version,
1890
+ description: openApiConfig.description
1891
+ });
1892
+ await writeFile(outputPath, tsContent);
1893
+ written.push(relative);
1894
+ logger.log(`📄 OpenAPI client generated: ${relative} (${surfaceEndpoints.length} endpoints)`);
1895
+ }
1867
1896
  return {
1868
- outputPath,
1869
- endpointCount: loadedEndpoints.length
1897
+ outputPath: join(process.cwd(), written[0]),
1898
+ outputPaths: written.map((r) => join(process.cwd(), r)),
1899
+ endpointCount: endpoints.length
1870
1900
  };
1871
1901
  }
1872
1902
  async function openapiCommand(options = {}) {
@@ -1876,12 +1906,12 @@ async function openapiCommand(options = {}) {
1876
1906
  if (loadedConfig.type === "single") {
1877
1907
  const config = loadedConfig.raw;
1878
1908
  if (!config.openapi) config.openapi = { enabled: true };
1879
- const result = await generateOpenApi(config);
1909
+ const result = await generateOpenApiFrom(config.constructs, { openapi: config.openapi });
1880
1910
  if (result) logger.log(`Found ${result.endpointCount} endpoints`);
1881
1911
  } else {
1882
1912
  const { workspace } = loadedConfig;
1883
1913
  const workspaceRoot = workspace.root;
1884
- const backendApps = Object.entries(workspace.apps).filter(([_, app]) => app.type === "backend" && app.routes !== void 0 && app.openapi !== false && (typeof app.openapi !== "object" || app.openapi.enabled !== false));
1914
+ const backendApps = Object.entries(workspace.apps).filter(([_, app]) => app.type === "backend" && app.openapi !== false && (typeof app.openapi !== "object" || app.openapi.enabled !== false));
1885
1915
  if (backendApps.length === 0) {
1886
1916
  logger.log("No backend apps with OpenAPI enabled found");
1887
1917
  return;
@@ -1895,8 +1925,12 @@ async function openapiCommand(options = {}) {
1895
1925
  return;
1896
1926
  }
1897
1927
  for (const [appName, app] of backendApps) {
1898
- if (app.type !== "backend" || !app.routes) continue;
1899
1928
  const appPath = join(workspaceRoot, app.path);
1929
+ if (resolve(appPath) === resolve(workspaceRoot)) {
1930
+ const result = await generateOpenApiForApp(workspaceRoot, appName, app, false);
1931
+ if (result) logger.log(`Found ${result.endpointCount} endpoints`);
1932
+ continue;
1933
+ }
1900
1934
  await runOpenApiInSubprocess(appPath, appName);
1901
1935
  }
1902
1936
  }
@@ -1909,18 +1943,13 @@ async function openapiCommand(options = {}) {
1909
1943
  * Runs in-process. The caller is responsible for ensuring `process.cwd()`
1910
1944
  * is the app's directory so tsx loads the app's tsconfig path aliases.
1911
1945
  */
1912
- async function generateOpenApiForApp(workspaceRoot, _appName, app) {
1913
- if (app.type !== "backend" || !app.routes) return null;
1946
+ async function generateOpenApiForApp(workspaceRoot, _appName, app, silent = true) {
1947
+ if (app.type !== "backend") return null;
1914
1948
  const appPath = join(workspaceRoot, app.path);
1915
- const routes = normalizeRoutes(app.routes);
1916
- const routesGlob = routes.map((r) => join(appPath, r));
1917
- const gkmConfig = {
1918
- routes: routesGlob,
1919
- envParser: app.envParser || "",
1920
- logger: app.logger || "",
1921
- openapi: app.openapi
1922
- };
1923
- return generateOpenApi(gkmConfig, { silent: true });
1949
+ return generateOpenApiFrom([join(appPath, "**/*.ts")], {
1950
+ openapi: app.openapi ?? { enabled: true },
1951
+ silent
1952
+ });
1924
1953
  }
1925
1954
  /**
1926
1955
  * Resolve the gkm bin path. Tests can override via `GKM_BIN_PATH` env var
@@ -1938,7 +1967,7 @@ function resolveGkmBinPath() {
1938
1967
  */
1939
1968
  async function runOpenApiInSubprocess(appCwd, appName) {
1940
1969
  const binPath = resolveGkmBinPath();
1941
- await new Promise((resolve$1, reject) => {
1970
+ await new Promise((resolve, reject) => {
1942
1971
  const child = spawn(process.execPath, [
1943
1972
  binPath,
1944
1973
  "openapi",
@@ -1951,12 +1980,12 @@ async function runOpenApiInSubprocess(appCwd, appName) {
1951
1980
  });
1952
1981
  child.on("error", reject);
1953
1982
  child.on("close", (code) => {
1954
- if (code === 0) resolve$1();
1955
- else reject(new Error(`OpenAPI generation for app "${appName}" exited with code ${code}`));
1983
+ if (code === 0) resolve();
1984
+ else reject(/* @__PURE__ */ new Error(`OpenAPI generation for app "${appName}" exited with code ${code}`));
1956
1985
  });
1957
1986
  });
1958
1987
  }
1959
-
1960
1988
  //#endregion
1961
- export { EndpointGenerator, OPENAPI_OUTPUT_PATH, generateOpenApi, normalizeRoutes, openapiCommand, resolveOpenApiConfig };
1962
- //# sourceMappingURL=openapi-CY9xFkTH.mjs.map
1989
+ export { openapiCommand as a, runtimeFor as c, openApiPathFor as i, generateOpenApi as n, resolveOpenApiConfig as o, generateOpenApiFrom as r, EndpointGenerator as s, OPENAPI_OUTPUT_PATH as t };
1990
+
1991
+ //# sourceMappingURL=openapi-DfjbhrsM.mjs.map