@geekmidas/cli 0.10.0 → 0.12.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 (145) hide show
  1. package/README.md +525 -0
  2. package/dist/bundler-DRXCw_YR.mjs +70 -0
  3. package/dist/bundler-DRXCw_YR.mjs.map +1 -0
  4. package/dist/bundler-WsEvH_b2.cjs +71 -0
  5. package/dist/bundler-WsEvH_b2.cjs.map +1 -0
  6. package/dist/{config-C9aXOHBe.cjs → config-AmInkU7k.cjs} +8 -8
  7. package/dist/config-AmInkU7k.cjs.map +1 -0
  8. package/dist/{config-BrkUalUh.mjs → config-DYULeEv8.mjs} +3 -3
  9. package/dist/config-DYULeEv8.mjs.map +1 -0
  10. package/dist/config.cjs +1 -1
  11. package/dist/config.d.cts +1 -1
  12. package/dist/config.d.mts +1 -1
  13. package/dist/config.mjs +1 -1
  14. package/dist/encryption-C8H-38Yy.mjs +42 -0
  15. package/dist/encryption-C8H-38Yy.mjs.map +1 -0
  16. package/dist/encryption-Dyf_r1h-.cjs +44 -0
  17. package/dist/encryption-Dyf_r1h-.cjs.map +1 -0
  18. package/dist/index.cjs +2116 -179
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.mjs +2134 -192
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/{openapi-CZLI4QTr.mjs → openapi-BfFlOBCG.mjs} +801 -38
  23. package/dist/openapi-BfFlOBCG.mjs.map +1 -0
  24. package/dist/{openapi-BeHLKcwP.cjs → openapi-Bt_1FDpT.cjs} +794 -31
  25. package/dist/openapi-Bt_1FDpT.cjs.map +1 -0
  26. package/dist/{openapi-react-query-o5iMi8tz.cjs → openapi-react-query-B-sNWHFU.cjs} +5 -5
  27. package/dist/openapi-react-query-B-sNWHFU.cjs.map +1 -0
  28. package/dist/{openapi-react-query-CcciaVu5.mjs → openapi-react-query-B6XTeGqS.mjs} +5 -5
  29. package/dist/openapi-react-query-B6XTeGqS.mjs.map +1 -0
  30. package/dist/openapi-react-query.cjs +1 -1
  31. package/dist/openapi-react-query.d.cts.map +1 -1
  32. package/dist/openapi-react-query.d.mts.map +1 -1
  33. package/dist/openapi-react-query.mjs +1 -1
  34. package/dist/openapi.cjs +2 -2
  35. package/dist/openapi.d.cts +1 -1
  36. package/dist/openapi.d.cts.map +1 -1
  37. package/dist/openapi.d.mts +1 -1
  38. package/dist/openapi.d.mts.map +1 -1
  39. package/dist/openapi.mjs +2 -2
  40. package/dist/storage-BUYQJgz7.cjs +4 -0
  41. package/dist/storage-BXoJvmv2.cjs +149 -0
  42. package/dist/storage-BXoJvmv2.cjs.map +1 -0
  43. package/dist/storage-C9PU_30f.mjs +101 -0
  44. package/dist/storage-C9PU_30f.mjs.map +1 -0
  45. package/dist/storage-DLJAYxzJ.mjs +3 -0
  46. package/dist/{types-b-vwGpqc.d.cts → types-BR0M2v_c.d.mts} +100 -1
  47. package/dist/types-BR0M2v_c.d.mts.map +1 -0
  48. package/dist/{types-DXgiA1sF.d.mts → types-BhkZc-vm.d.cts} +100 -1
  49. package/dist/types-BhkZc-vm.d.cts.map +1 -0
  50. package/examples/cron-example.ts +27 -27
  51. package/examples/env.ts +27 -27
  52. package/examples/function-example.ts +31 -31
  53. package/examples/gkm.config.json +20 -20
  54. package/examples/gkm.config.ts +8 -8
  55. package/examples/gkm.minimal.config.json +5 -5
  56. package/examples/gkm.production.config.json +25 -25
  57. package/examples/logger.ts +2 -2
  58. package/package.json +6 -6
  59. package/src/__tests__/EndpointGenerator.hooks.spec.ts +191 -191
  60. package/src/__tests__/config.spec.ts +55 -55
  61. package/src/__tests__/loadEnvFiles.spec.ts +93 -93
  62. package/src/__tests__/normalizeHooksConfig.spec.ts +58 -58
  63. package/src/__tests__/openapi-react-query.spec.ts +497 -497
  64. package/src/__tests__/openapi.spec.ts +428 -428
  65. package/src/__tests__/test-helpers.ts +76 -76
  66. package/src/auth/__tests__/credentials.spec.ts +204 -0
  67. package/src/auth/__tests__/index.spec.ts +168 -0
  68. package/src/auth/credentials.ts +187 -0
  69. package/src/auth/index.ts +226 -0
  70. package/src/build/__tests__/index-new.spec.ts +474 -474
  71. package/src/build/__tests__/manifests.spec.ts +333 -333
  72. package/src/build/bundler.ts +141 -0
  73. package/src/build/endpoint-analyzer.ts +236 -0
  74. package/src/build/handler-templates.ts +1253 -0
  75. package/src/build/index.ts +250 -179
  76. package/src/build/manifests.ts +52 -52
  77. package/src/build/providerResolver.ts +145 -145
  78. package/src/build/types.ts +64 -43
  79. package/src/config.ts +39 -39
  80. package/src/deploy/__tests__/docker.spec.ts +111 -0
  81. package/src/deploy/__tests__/dokploy.spec.ts +245 -0
  82. package/src/deploy/__tests__/init.spec.ts +662 -0
  83. package/src/deploy/docker.ts +128 -0
  84. package/src/deploy/dokploy.ts +204 -0
  85. package/src/deploy/index.ts +136 -0
  86. package/src/deploy/init.ts +484 -0
  87. package/src/deploy/types.ts +48 -0
  88. package/src/dev/__tests__/index.spec.ts +266 -266
  89. package/src/dev/index.ts +647 -601
  90. package/src/docker/__tests__/compose.spec.ts +531 -0
  91. package/src/docker/__tests__/templates.spec.ts +280 -0
  92. package/src/docker/compose.ts +273 -0
  93. package/src/docker/index.ts +230 -0
  94. package/src/docker/templates.ts +446 -0
  95. package/src/generators/CronGenerator.ts +72 -72
  96. package/src/generators/EndpointGenerator.ts +699 -398
  97. package/src/generators/FunctionGenerator.ts +84 -84
  98. package/src/generators/Generator.ts +72 -72
  99. package/src/generators/OpenApiTsGenerator.ts +577 -577
  100. package/src/generators/SubscriberGenerator.ts +124 -124
  101. package/src/generators/__tests__/CronGenerator.spec.ts +433 -433
  102. package/src/generators/__tests__/EndpointGenerator.spec.ts +532 -382
  103. package/src/generators/__tests__/FunctionGenerator.spec.ts +244 -244
  104. package/src/generators/__tests__/SubscriberGenerator.spec.ts +397 -382
  105. package/src/generators/index.ts +4 -4
  106. package/src/index.ts +623 -201
  107. package/src/init/__tests__/generators.spec.ts +334 -334
  108. package/src/init/__tests__/init.spec.ts +332 -332
  109. package/src/init/__tests__/utils.spec.ts +89 -89
  110. package/src/init/generators/config.ts +175 -175
  111. package/src/init/generators/docker.ts +41 -41
  112. package/src/init/generators/env.ts +72 -72
  113. package/src/init/generators/index.ts +1 -1
  114. package/src/init/generators/models.ts +64 -64
  115. package/src/init/generators/monorepo.ts +161 -161
  116. package/src/init/generators/package.ts +71 -71
  117. package/src/init/generators/source.ts +6 -6
  118. package/src/init/index.ts +203 -208
  119. package/src/init/templates/api.ts +115 -115
  120. package/src/init/templates/index.ts +75 -75
  121. package/src/init/templates/minimal.ts +98 -98
  122. package/src/init/templates/serverless.ts +89 -89
  123. package/src/init/templates/worker.ts +98 -98
  124. package/src/init/utils.ts +54 -56
  125. package/src/openapi-react-query.ts +194 -194
  126. package/src/openapi.ts +63 -63
  127. package/src/secrets/__tests__/encryption.spec.ts +226 -0
  128. package/src/secrets/__tests__/generator.spec.ts +319 -0
  129. package/src/secrets/__tests__/index.spec.ts +91 -0
  130. package/src/secrets/__tests__/storage.spec.ts +403 -0
  131. package/src/secrets/encryption.ts +91 -0
  132. package/src/secrets/generator.ts +164 -0
  133. package/src/secrets/index.ts +383 -0
  134. package/src/secrets/storage.ts +134 -0
  135. package/src/secrets/types.ts +53 -0
  136. package/src/types.ts +295 -176
  137. package/tsdown.config.ts +11 -8
  138. package/dist/config-BrkUalUh.mjs.map +0 -1
  139. package/dist/config-C9aXOHBe.cjs.map +0 -1
  140. package/dist/openapi-BeHLKcwP.cjs.map +0 -1
  141. package/dist/openapi-CZLI4QTr.mjs.map +0 -1
  142. package/dist/openapi-react-query-CcciaVu5.mjs.map +0 -1
  143. package/dist/openapi-react-query-o5iMi8tz.cjs.map +0 -1
  144. package/dist/types-DXgiA1sF.d.mts.map +0 -1
  145. package/dist/types-b-vwGpqc.d.cts.map +0 -1
@@ -1,8 +1,7 @@
1
1
  const require_chunk = require('./chunk-CUT6urMc.cjs');
2
- const require_config = require('./config-C9aXOHBe.cjs');
3
- const path = require_chunk.__toESM(require("path"));
4
- const node_fs_promises = require_chunk.__toESM(require("node:fs/promises"));
2
+ const require_config = require('./config-AmInkU7k.cjs');
5
3
  const node_path = require_chunk.__toESM(require("node:path"));
4
+ const node_fs_promises = require_chunk.__toESM(require("node:fs/promises"));
6
5
  const fast_glob = require_chunk.__toESM(require("fast-glob"));
7
6
  const lodash_kebabcase = require_chunk.__toESM(require("lodash.kebabcase"));
8
7
  const __geekmidas_constructs_endpoints = require_chunk.__toESM(require("@geekmidas/constructs/endpoints"));
@@ -31,7 +30,7 @@ var ConstructGenerator = class {
31
30
  construct,
32
31
  path: {
33
32
  absolute: file,
34
- relative: (0, path.relative)(process.cwd(), file)
33
+ relative: (0, node_path.relative)(process.cwd(), file)
35
34
  }
36
35
  });
37
36
  } catch (error) {
@@ -42,6 +41,586 @@ var ConstructGenerator = class {
42
41
  }
43
42
  };
44
43
 
44
+ //#endregion
45
+ //#region src/build/endpoint-analyzer.ts
46
+ /**
47
+ * Analyze an endpoint to extract its features
48
+ */
49
+ function analyzeEndpointFeatures(endpoint) {
50
+ return {
51
+ hasAuth: !!endpoint.authorizer,
52
+ hasServices: endpoint.services.length > 0,
53
+ hasDatabase: !!endpoint.databaseService,
54
+ hasBodyValidation: !!endpoint.input?.body,
55
+ hasQueryValidation: !!endpoint.input?.query,
56
+ hasParamValidation: !!endpoint.input?.params,
57
+ hasAudits: (endpoint.audits?.length ?? 0) > 0,
58
+ hasEvents: (endpoint.events?.length ?? 0) > 0,
59
+ hasRateLimit: !!endpoint.rateLimit,
60
+ hasRls: !!endpoint.rlsConfig && !endpoint.rlsBypass,
61
+ hasOutputValidation: !!endpoint.outputSchema
62
+ };
63
+ }
64
+ /**
65
+ * Determine the optimization tier for an endpoint based on its features
66
+ */
67
+ function determineEndpointTier(features) {
68
+ const { hasAuth, hasServices, hasDatabase, hasAudits, hasEvents, hasRateLimit, hasRls } = features;
69
+ if (!hasAuth && !hasServices && !hasDatabase && !hasAudits && !hasEvents && !hasRateLimit && !hasRls) return "minimal";
70
+ if (hasAudits || hasRls || hasRateLimit) return "full";
71
+ return "standard";
72
+ }
73
+ /**
74
+ * Perform complete analysis of an endpoint
75
+ */
76
+ function analyzeEndpoint(endpoint, exportName) {
77
+ const features = analyzeEndpointFeatures(endpoint);
78
+ const tier = determineEndpointTier(features);
79
+ return {
80
+ route: endpoint.route,
81
+ method: endpoint.method,
82
+ exportName,
83
+ features,
84
+ tier,
85
+ serviceNames: endpoint.services.map((s) => s.serviceName),
86
+ databaseServiceName: endpoint.databaseService?.serviceName
87
+ };
88
+ }
89
+ /**
90
+ * Generate a summary of endpoint analysis for logging
91
+ */
92
+ function summarizeAnalysis(analyses) {
93
+ const byTier = {
94
+ minimal: 0,
95
+ standard: 0,
96
+ full: 0
97
+ };
98
+ const byFeature = {
99
+ hasAuth: 0,
100
+ hasServices: 0,
101
+ hasDatabase: 0,
102
+ hasBodyValidation: 0,
103
+ hasQueryValidation: 0,
104
+ hasParamValidation: 0,
105
+ hasAudits: 0,
106
+ hasEvents: 0,
107
+ hasRateLimit: 0,
108
+ hasRls: 0,
109
+ hasOutputValidation: 0
110
+ };
111
+ for (const analysis of analyses) {
112
+ byTier[analysis.tier]++;
113
+ for (const [feature, enabled] of Object.entries(analysis.features)) if (enabled) byFeature[feature]++;
114
+ }
115
+ return {
116
+ total: analyses.length,
117
+ byTier,
118
+ byFeature
119
+ };
120
+ }
121
+
122
+ //#endregion
123
+ //#region src/build/handler-templates.ts
124
+ /**
125
+ * Generate validator references for an endpoint
126
+ */
127
+ function generateValidators(exportName, features) {
128
+ const validators = [];
129
+ if (features.hasBodyValidation) validators.push(`validateBody(${exportName})`);
130
+ if (features.hasQueryValidation) validators.push(`validateQuery(${exportName})`);
131
+ if (features.hasParamValidation) validators.push(`validateParams(${exportName})`);
132
+ return validators.length > 0 ? `\n ${validators.join(",\n ")},` : "";
133
+ }
134
+ /**
135
+ * Generate a minimal handler (near-raw-Hono performance)
136
+ *
137
+ * Used for: Health checks, public endpoints with no services
138
+ */
139
+ function generateMinimalHandler(analysis) {
140
+ const { exportName, features } = analysis;
141
+ const method = analysis.method.toLowerCase();
142
+ const validators = generateValidators(exportName, features);
143
+ const hasValidators = validators.length > 0;
144
+ if (!hasValidators && !features.hasOutputValidation) return `
145
+ // Minimal handler: ${analysis.route} (${analysis.method})
146
+ app.${method}('${analysis.route}', async (c) => {
147
+ const result = await ${exportName}.handler(
148
+ {
149
+ services: {},
150
+ logger,
151
+ body: undefined,
152
+ query: undefined,
153
+ params: undefined,
154
+ session: undefined,
155
+ header: Endpoint.createHeaders(c.req.header()),
156
+ cookie: Endpoint.createCookies(c.req.header().cookie),
157
+ auditor: undefined,
158
+ db: undefined,
159
+ } as any,
160
+ { getMetadata: () => ({}) } as any,
161
+ );
162
+ return c.json(result, ${exportName}.status as any);
163
+ });`;
164
+ return `
165
+ // Minimal handler with validation: ${analysis.route} (${analysis.method})
166
+ app.${method}('${analysis.route}',${validators}
167
+ async (c) => {
168
+ const result = await ${exportName}.handler(
169
+ {
170
+ services: {},
171
+ logger,
172
+ body: ${features.hasBodyValidation ? "(c.req.valid as any)('json')" : "undefined"},
173
+ query: ${features.hasQueryValidation ? "(c.req.valid as any)('query')" : "undefined"},
174
+ params: ${features.hasParamValidation ? "(c.req.valid as any)('param')" : "undefined"},
175
+ session: undefined,
176
+ header: Endpoint.createHeaders(c.req.header()),
177
+ cookie: Endpoint.createCookies(c.req.header().cookie),
178
+ auditor: undefined,
179
+ db: undefined,
180
+ } as any,
181
+ { getMetadata: () => ({}) } as any,
182
+ );
183
+ ${features.hasOutputValidation ? `const output = await ${exportName}.parseOutput(result);
184
+ return c.json(output, ${exportName}.status as any);` : `return c.json(result, ${exportName}.status as any);`}
185
+ }
186
+ );`;
187
+ }
188
+ /**
189
+ * Generate a standard handler (auth and/or services)
190
+ *
191
+ * Used for: Authenticated endpoints, endpoints with services
192
+ */
193
+ function generateStandardHandler(analysis) {
194
+ const { exportName, features } = analysis;
195
+ const method = analysis.method.toLowerCase();
196
+ const validators = generateValidators(exportName, features);
197
+ let serviceResolution = "";
198
+ if (features.hasServices || features.hasDatabase) serviceResolution = `
199
+ const services = await serviceDiscovery.register(${exportName}.services);
200
+ ${features.hasDatabase ? `const db = ${exportName}.databaseService
201
+ ? (await serviceDiscovery.register([${exportName}.databaseService]) as any)[${exportName}.databaseService.serviceName]
202
+ : undefined;` : "const db = undefined;"}`;
203
+ else serviceResolution = `
204
+ const services = {};
205
+ const db = undefined;`;
206
+ let authCode = "";
207
+ if (features.hasAuth) authCode = `
208
+ // Authentication
209
+ const session = await ${exportName}.getSession({
210
+ services,
211
+ logger,
212
+ header,
213
+ cookie,
214
+ ...(db !== undefined && { db }),
215
+ } as any);
216
+
217
+ const isAuthorized = await ${exportName}.authorize({
218
+ header,
219
+ cookie,
220
+ services,
221
+ logger,
222
+ session,
223
+ } as any);
224
+
225
+ if (!isAuthorized) {
226
+ return c.json({ error: 'Unauthorized' }, 401);
227
+ }`;
228
+ let eventCode = "";
229
+ if (features.hasEvents) eventCode = `
230
+ // Publish events on success
231
+ if (Endpoint.isSuccessStatus(${exportName}.status)) {
232
+ await (publishConstructEvents as any)(
233
+ ${exportName},
234
+ result,
235
+ serviceDiscovery,
236
+ logger,
237
+ );
238
+ }`;
239
+ return `
240
+ // Standard handler: ${analysis.route} (${analysis.method})
241
+ app.${method}('${analysis.route}',${validators}
242
+ async (c) => {
243
+ const headerValues = c.req.header();
244
+ const header = Endpoint.createHeaders(headerValues);
245
+ const cookie = Endpoint.createCookies(headerValues.cookie);
246
+ ${serviceResolution}
247
+ ${authCode}
248
+
249
+ const responseBuilder = new ResponseBuilder();
250
+ const result = await ${exportName}.handler(
251
+ {
252
+ services,
253
+ logger,
254
+ body: ${features.hasBodyValidation ? "(c.req.valid as any)('json')" : "undefined"},
255
+ query: ${features.hasQueryValidation ? "(c.req.valid as any)('query')" : "undefined"},
256
+ params: ${features.hasParamValidation ? "(c.req.valid as any)('param')" : "undefined"},
257
+ session: ${features.hasAuth ? "session" : "undefined"},
258
+ header,
259
+ cookie,
260
+ auditor: undefined,
261
+ db,
262
+ } as any,
263
+ responseBuilder,
264
+ );
265
+
266
+ let data = result;
267
+ let metadata = responseBuilder.getMetadata();
268
+
269
+ if (Endpoint.hasMetadata(result)) {
270
+ data = result.data;
271
+ metadata = result.metadata;
272
+ }
273
+
274
+ ${features.hasOutputValidation ? `const output = ${exportName}.outputSchema
275
+ ? await ${exportName}.parseOutput(data)
276
+ : data;` : "const output = data;"}
277
+ ${eventCode}
278
+
279
+ const status = (metadata.status ?? ${exportName}.status) as any;
280
+ return c.json(output, status);
281
+ }
282
+ );`;
283
+ }
284
+ /**
285
+ * Generate validators.ts - Shared validator middleware factories
286
+ */
287
+ function generateValidatorsFile(analyses) {
288
+ const needsBody = analyses.some((a) => a.features.hasBodyValidation);
289
+ const needsQuery = analyses.some((a) => a.features.hasQueryValidation);
290
+ const needsParams = analyses.some((a) => a.features.hasParamValidation);
291
+ if (!needsBody && !needsQuery && !needsParams) return `// No validators needed for this build\nexport {};\n`;
292
+ const exports$1 = [];
293
+ const factories = [];
294
+ if (needsBody) {
295
+ exports$1.push("validateBody");
296
+ factories.push(`
297
+ export const validateBody = (endpoint: any) =>
298
+ validator('json', async (value, c) => {
299
+ if (!endpoint.input?.body) return undefined;
300
+ const parsed = await Endpoint.validate(endpoint.input.body, value);
301
+ if (parsed.issues) return c.json(parsed.issues, 422);
302
+ return parsed.value;
303
+ });`);
304
+ }
305
+ if (needsQuery) {
306
+ exports$1.push("validateQuery");
307
+ factories.push(`
308
+ export const validateQuery = (endpoint: any) =>
309
+ validator('query', async (_, c) => {
310
+ if (!endpoint.input?.query) return undefined;
311
+ const rawQuery = Object.fromEntries(new URL(c.req.url).searchParams);
312
+ const parsed = await Endpoint.validate(endpoint.input.query, rawQuery);
313
+ if (parsed.issues) return c.json(parsed.issues, 422);
314
+ return parsed.value;
315
+ });`);
316
+ }
317
+ if (needsParams) {
318
+ exports$1.push("validateParams");
319
+ factories.push(`
320
+ export const validateParams = (endpoint: any) =>
321
+ validator('param', async (params, c) => {
322
+ if (!endpoint.input?.params) return undefined;
323
+ const parsed = await Endpoint.validate(endpoint.input.params, params);
324
+ if (parsed.issues) return c.json(parsed.issues, 422);
325
+ return parsed.value;
326
+ });`);
327
+ }
328
+ return `/**
329
+ * Generated validator middleware factories
330
+ * Shared across all endpoint tiers that need validation
331
+ */
332
+ import { validator } from 'hono/validator';
333
+ import { Endpoint } from '@geekmidas/constructs/endpoints';
334
+ ${factories.join("\n")}
335
+ `;
336
+ }
337
+ /**
338
+ * Generate a standalone minimal endpoint file
339
+ */
340
+ function generateMinimalEndpointFile(analysis, endpointImport) {
341
+ const { exportName, features } = analysis;
342
+ const needsValidators = features.hasBodyValidation || features.hasQueryValidation || features.hasParamValidation;
343
+ const validatorImport = needsValidators ? generateValidatorImportsForEndpoint(analysis) : "";
344
+ const handler = generateMinimalHandler(analysis);
345
+ return `/**
346
+ * Minimal endpoint: ${analysis.route} (${analysis.method})
347
+ * Near-raw-Hono performance
348
+ */
349
+ import type { Hono } from 'hono';
350
+ import type { Logger } from '@geekmidas/logger';
351
+ import { Endpoint } from '@geekmidas/constructs/endpoints';
352
+ ${validatorImport}
353
+ import { ${exportName} } from '${endpointImport.importPath}';
354
+
355
+ export function setup${capitalize(exportName)}(
356
+ app: Hono,
357
+ logger: Logger,
358
+ ): void {
359
+ ${handler}
360
+ }
361
+ `;
362
+ }
363
+ /**
364
+ * Generate a standalone standard endpoint file
365
+ */
366
+ function generateStandardEndpointFile(analysis, endpointImport) {
367
+ const { exportName, features } = analysis;
368
+ const needsValidators = features.hasBodyValidation || features.hasQueryValidation || features.hasParamValidation;
369
+ const validatorImport = needsValidators ? generateValidatorImportsForEndpoint(analysis) : "";
370
+ const eventsImport = features.hasEvents ? `import { publishConstructEvents } from '@geekmidas/constructs/endpoints';` : "";
371
+ const handler = generateStandardHandler(analysis);
372
+ return `/**
373
+ * Standard endpoint: ${analysis.route} (${analysis.method})
374
+ * Auth and/or services enabled
375
+ */
376
+ import type { Hono } from 'hono';
377
+ import type { Logger } from '@geekmidas/logger';
378
+ import type { ServiceDiscovery } from '@geekmidas/services';
379
+ import { Endpoint, ResponseBuilder } from '@geekmidas/constructs/endpoints';
380
+ ${eventsImport}
381
+ ${validatorImport}
382
+ import { ${exportName} } from '${endpointImport.importPath}';
383
+
384
+ export function setup${capitalize(exportName)}(
385
+ app: Hono,
386
+ serviceDiscovery: ServiceDiscovery<any, any>,
387
+ logger: Logger,
388
+ ): void {
389
+ ${handler}
390
+ }
391
+ `;
392
+ }
393
+ /**
394
+ * Generate a standalone full endpoint file
395
+ */
396
+ function generateFullEndpointFile(analysis, endpointImport) {
397
+ const { exportName } = analysis;
398
+ return `/**
399
+ * Full endpoint: ${analysis.route} (${analysis.method})
400
+ * Complex features: audits, RLS, rate limiting
401
+ */
402
+ import type { Hono } from 'hono';
403
+ import type { ServiceDiscovery } from '@geekmidas/services';
404
+ import { HonoEndpoint } from '@geekmidas/constructs/hono';
405
+ import { ${exportName} } from '${endpointImport.importPath}';
406
+
407
+ export function setup${capitalize(exportName)}(
408
+ app: Hono,
409
+ serviceDiscovery: ServiceDiscovery<any, any>,
410
+ openApiOptions: any,
411
+ ): void {
412
+ HonoEndpoint.addRoutes([${exportName}] as any, serviceDiscovery, app, openApiOptions);
413
+ }
414
+ `;
415
+ }
416
+ /**
417
+ * Generate tier index file that imports and calls all endpoint setup functions
418
+ */
419
+ function generateTierIndexFile(tier, analyses) {
420
+ const tierEndpoints = analyses.filter((a) => a.tier === tier);
421
+ if (tierEndpoints.length === 0) {
422
+ if (tier === "minimal") return `// No minimal-tier endpoints in this build
423
+ import type { Hono } from 'hono';
424
+ import type { Logger } from '@geekmidas/logger';
425
+
426
+ export function setupMinimalEndpoints(
427
+ _app: Hono,
428
+ _logger: Logger,
429
+ ): void {
430
+ // No minimal endpoints
431
+ }
432
+ `;
433
+ if (tier === "standard") return `// No standard-tier endpoints in this build
434
+ import type { Hono } from 'hono';
435
+ import type { Logger } from '@geekmidas/logger';
436
+ import type { ServiceDiscovery } from '@geekmidas/services';
437
+
438
+ export function setupStandardEndpoints(
439
+ _app: Hono,
440
+ _serviceDiscovery: ServiceDiscovery<any, any>,
441
+ _logger: Logger,
442
+ ): void {
443
+ // No standard endpoints
444
+ }
445
+ `;
446
+ return `// No full-tier endpoints in this build
447
+ import type { Hono } from 'hono';
448
+ import type { ServiceDiscovery } from '@geekmidas/services';
449
+
450
+ export function setupFullEndpoints(
451
+ _app: Hono,
452
+ _serviceDiscovery: ServiceDiscovery<any, any>,
453
+ _enableOpenApi: boolean,
454
+ ): void {
455
+ // No full endpoints
456
+ }
457
+ `;
458
+ }
459
+ const imports = tierEndpoints.map((a) => `import { setup${capitalize(a.exportName)} } from './${a.exportName}.js';`).join("\n");
460
+ if (tier === "minimal") {
461
+ const calls$1 = tierEndpoints.map((a) => ` setup${capitalize(a.exportName)}(app, logger);`).join("\n");
462
+ return `/**
463
+ * Minimal-tier endpoint index (${tierEndpoints.length} endpoints)
464
+ * Near-raw-Hono performance
465
+ */
466
+ import type { Hono } from 'hono';
467
+ import type { Logger } from '@geekmidas/logger';
468
+ ${imports}
469
+
470
+ export function setupMinimalEndpoints(
471
+ app: Hono,
472
+ logger: Logger,
473
+ ): void {
474
+ ${calls$1}
475
+ }
476
+ `;
477
+ }
478
+ if (tier === "standard") {
479
+ const calls$1 = tierEndpoints.map((a) => ` setup${capitalize(a.exportName)}(app, serviceDiscovery, logger);`).join("\n");
480
+ return `/**
481
+ * Standard-tier endpoint index (${tierEndpoints.length} endpoints)
482
+ * Auth and/or services enabled
483
+ */
484
+ import type { Hono } from 'hono';
485
+ import type { Logger } from '@geekmidas/logger';
486
+ import type { ServiceDiscovery } from '@geekmidas/services';
487
+ ${imports}
488
+
489
+ export function setupStandardEndpoints(
490
+ app: Hono,
491
+ serviceDiscovery: ServiceDiscovery<any, any>,
492
+ logger: Logger,
493
+ ): void {
494
+ ${calls$1}
495
+ }
496
+ `;
497
+ }
498
+ const calls = tierEndpoints.map((a) => ` setup${capitalize(a.exportName)}(app, serviceDiscovery, openApiOptions);`).join("\n");
499
+ return `/**
500
+ * Full-tier endpoint index (${tierEndpoints.length} endpoints)
501
+ * Complex features: audits, RLS, rate limiting
502
+ */
503
+ import type { Hono } from 'hono';
504
+ import type { ServiceDiscovery } from '@geekmidas/services';
505
+ ${imports}
506
+
507
+ export function setupFullEndpoints(
508
+ app: Hono,
509
+ serviceDiscovery: ServiceDiscovery<any, any>,
510
+ enableOpenApi: boolean,
511
+ ): void {
512
+ const openApiOptions: any = enableOpenApi ? {
513
+ docsPath: '/__docs',
514
+ openApiOptions: {
515
+ title: 'API Documentation',
516
+ version: '1.0.0',
517
+ description: 'Generated API documentation'
518
+ }
519
+ } : { docsPath: false };
520
+
521
+ ${calls}
522
+ }
523
+ `;
524
+ }
525
+ /**
526
+ * Generate validator imports for a single endpoint
527
+ */
528
+ function generateValidatorImportsForEndpoint(analysis) {
529
+ const imports = [];
530
+ if (analysis.features.hasBodyValidation) imports.push("validateBody");
531
+ if (analysis.features.hasQueryValidation) imports.push("validateQuery");
532
+ if (analysis.features.hasParamValidation) imports.push("validateParams");
533
+ if (imports.length === 0) return "";
534
+ return `import { ${imports.join(", ")} } from '../validators.js';`;
535
+ }
536
+ /**
537
+ * Capitalize first letter
538
+ */
539
+ function capitalize(str) {
540
+ return str.charAt(0).toUpperCase() + str.slice(1);
541
+ }
542
+ /**
543
+ * Generate all endpoint files with nested folder structure (per-endpoint files)
544
+ */
545
+ function generateEndpointFilesNested(analyses, endpointImports) {
546
+ const files = {
547
+ "validators.ts": generateValidatorsFile(analyses),
548
+ "minimal/index.ts": generateTierIndexFile("minimal", analyses),
549
+ "standard/index.ts": generateTierIndexFile("standard", analyses),
550
+ "full/index.ts": generateTierIndexFile("full", analyses),
551
+ "index.ts": generateNestedIndexFile(analyses)
552
+ };
553
+ for (const analysis of analyses) {
554
+ const endpointImport = endpointImports.find((i) => i.exportName === analysis.exportName);
555
+ if (!endpointImport) continue;
556
+ const fileName = `${analysis.tier}/${analysis.exportName}.ts`;
557
+ switch (analysis.tier) {
558
+ case "minimal":
559
+ files[fileName] = generateMinimalEndpointFile(analysis, endpointImport);
560
+ break;
561
+ case "standard":
562
+ files[fileName] = generateStandardEndpointFile(analysis, endpointImport);
563
+ break;
564
+ case "full":
565
+ files[fileName] = generateFullEndpointFile(analysis, endpointImport);
566
+ break;
567
+ }
568
+ }
569
+ return files;
570
+ }
571
+ /**
572
+ * Generate index.ts for nested structure
573
+ */
574
+ function generateNestedIndexFile(analyses) {
575
+ const minimalCount = analyses.filter((a) => a.tier === "minimal").length;
576
+ const standardCount = analyses.filter((a) => a.tier === "standard").length;
577
+ const fullCount = analyses.filter((a) => a.tier === "full").length;
578
+ return `/**
579
+ * Generated optimized endpoints
580
+ *
581
+ * Build-time optimization tiers:
582
+ * - minimal: ${minimalCount} endpoints (near-raw-Hono)
583
+ * - standard: ${standardCount} endpoints (auth/services)
584
+ * - full: ${fullCount} endpoints (audits/rls/rate-limit)
585
+ */
586
+ import type { EnvironmentParser } from '@geekmidas/envkit';
587
+ import type { Logger } from '@geekmidas/logger';
588
+ import type { Hono } from 'hono';
589
+ import { ServiceDiscovery } from '@geekmidas/services';
590
+ import { setupMinimalEndpoints } from './minimal/index.js';
591
+ import { setupStandardEndpoints } from './standard/index.js';
592
+ import { setupFullEndpoints } from './full/index.js';
593
+
594
+ export async function setupEndpoints(
595
+ app: Hono,
596
+ envParser: EnvironmentParser<any>,
597
+ logger: Logger,
598
+ enableOpenApi: boolean = false,
599
+ ): Promise<void> {
600
+ const serviceDiscovery = ServiceDiscovery.getInstance(envParser);
601
+
602
+ // Minimal handlers (${minimalCount} endpoints) - near-raw-Hono performance
603
+ setupMinimalEndpoints(app, logger);
604
+
605
+ // Standard handlers (${standardCount} endpoints) - auth/services
606
+ setupStandardEndpoints(app, serviceDiscovery, logger);
607
+
608
+ // Full handlers (${fullCount} endpoints) - audits/rls/rate-limit
609
+ setupFullEndpoints(app, serviceDiscovery, enableOpenApi);
610
+
611
+ // Add Swagger UI if OpenAPI is enabled
612
+ if (enableOpenApi) {
613
+ try {
614
+ const { swaggerUI } = await import('@hono/swagger-ui');
615
+ app.get('/__docs/ui', swaggerUI({ url: '/__docs' }));
616
+ } catch {
617
+ // @hono/swagger-ui not installed, skip Swagger UI
618
+ }
619
+ }
620
+ }
621
+ `;
622
+ }
623
+
45
624
  //#endregion
46
625
  //#region src/generators/EndpointGenerator.ts
47
626
  var EndpointGenerator = class extends ConstructGenerator {
@@ -67,8 +646,8 @@ var EndpointGenerator = class extends ConstructGenerator {
67
646
  } else if (provider === "aws-lambda") {
68
647
  const routesDir = (0, node_path.join)(outputDir, "routes");
69
648
  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);
649
+ for (const { key, construct, path } of constructs) {
650
+ const handlerFile = await this.generateHandlerFile(routesDir, path.relative, key, "aws-apigatewayv2", construct, context);
72
651
  const routeInfo = {
73
652
  path: construct._path,
74
653
  method: construct.method,
@@ -81,8 +660,8 @@ var EndpointGenerator = class extends ConstructGenerator {
81
660
  routes.push(routeInfo);
82
661
  logger.log(`Generated handler for ${routeInfo.method} ${routeInfo.path}`);
83
662
  }
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);
663
+ } else for (const { key, construct, path } of constructs) {
664
+ const handlerFile = await this.generateHandlerFile(outputDir, path.relative, key, provider, construct, context);
86
665
  const routeInfo = {
87
666
  path: construct._path,
88
667
  method: construct.method,
@@ -119,25 +698,26 @@ var EndpointGenerator = class extends ConstructGenerator {
119
698
  await (0, node_fs_promises.writeFile)(handlerPath, content);
120
699
  return handlerPath;
121
700
  }
122
- async generateEndpointsFile(outputDir, endpoints, _context) {
701
+ async generateEndpointsFile(outputDir, endpoints, context) {
123
702
  const endpointsFileName = "endpoints.ts";
124
703
  const endpointsPath = (0, node_path.join)(outputDir, endpointsFileName);
125
704
  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);
705
+ for (const { path, key } of endpoints) {
706
+ const relativePath = (0, node_path.relative)((0, node_path.dirname)(endpointsPath), path.relative);
128
707
  const importPath = relativePath.replace(/\.ts$/, ".js");
129
708
  if (!importsByFile.has(importPath)) importsByFile.set(importPath, []);
130
- importsByFile.get(importPath).push(key);
709
+ importsByFile.get(importPath)?.push(key);
131
710
  }
132
- const imports = Array.from(importsByFile.entries()).map(([importPath, exports$1]) => `import { ${exports$1.join(", ")} } from '${importPath}';`).join("\n");
711
+ const endpointImports = Array.from(importsByFile.entries()).map(([importPath, exports$1]) => `import { ${exports$1.join(", ")} } from '${importPath}';`).join("\n");
133
712
  const allExportNames = endpoints.map(({ key }) => key);
713
+ if (context.production?.enabled && context.production.optimizedHandlers) return this.generateOptimizedEndpointsFile(endpointsPath, endpoints, endpointImports, allExportNames);
134
714
  const content = `import type { EnvironmentParser } from '@geekmidas/envkit';
135
715
  import type { Logger } from '@geekmidas/logger';
136
716
  import { HonoEndpoint } from '@geekmidas/constructs/hono';
137
717
  import { Endpoint } from '@geekmidas/constructs/endpoints';
138
718
  import { ServiceDiscovery } from '@geekmidas/services';
139
719
  import type { Hono } from 'hono';
140
- ${imports}
720
+ ${endpointImports}
141
721
 
142
722
  const endpoints: Endpoint<any, any, any, any, any, any, any, any, any, any, any, any, any, any>[] = [
143
723
  ${allExportNames.join(",\n ")}
@@ -149,10 +729,7 @@ export async function setupEndpoints(
149
729
  logger: Logger,
150
730
  enableOpenApi: boolean = true,
151
731
  ): Promise<void> {
152
- const serviceDiscovery = ServiceDiscovery.getInstance(
153
- logger,
154
- envParser
155
- );
732
+ const serviceDiscovery = ServiceDiscovery.getInstance(envParser);
156
733
 
157
734
  // Configure OpenAPI options based on enableOpenApi flag
158
735
  const openApiOptions: any = enableOpenApi ? {
@@ -180,7 +757,45 @@ export async function setupEndpoints(
180
757
  await (0, node_fs_promises.writeFile)(endpointsPath, content);
181
758
  return endpointsPath;
182
759
  }
760
+ /**
761
+ * Generate optimized endpoints files with nested folder structure (per-endpoint files)
762
+ */
763
+ async generateOptimizedEndpointsFile(endpointsPath, endpoints, _endpointImports, _allExportNames) {
764
+ const logger = console;
765
+ const outputDir = (0, node_path.dirname)(endpointsPath);
766
+ const endpointsDir = (0, node_path.join)(outputDir, "endpoints");
767
+ await (0, node_fs_promises.mkdir)((0, node_path.join)(endpointsDir, "minimal"), { recursive: true });
768
+ await (0, node_fs_promises.mkdir)((0, node_path.join)(endpointsDir, "standard"), { recursive: true });
769
+ await (0, node_fs_promises.mkdir)((0, node_path.join)(endpointsDir, "full"), { recursive: true });
770
+ const analyses = endpoints.map(({ key, construct }) => analyzeEndpoint(construct, key));
771
+ const endpointImports = endpoints.map(({ key, path }) => {
772
+ const tierDir = (0, node_path.join)(endpointsDir, "standard");
773
+ const relativePath = (0, node_path.relative)(tierDir, path.relative);
774
+ const importPath = relativePath.replace(/\.ts$/, ".js");
775
+ return {
776
+ exportName: key,
777
+ importPath
778
+ };
779
+ });
780
+ const summary = summarizeAnalysis(analyses);
781
+ logger.log(`\n📊 Endpoint Analysis:`);
782
+ logger.log(` Total: ${summary.total} endpoints`);
783
+ logger.log(` - Minimal (near-raw-Hono): ${summary.byTier.minimal} endpoints`);
784
+ logger.log(` - Standard (auth/services): ${summary.byTier.standard} endpoints`);
785
+ logger.log(` - Full (audits/rls/rate-limit): ${summary.byTier.full} endpoints`);
786
+ const files = generateEndpointFilesNested(analyses, endpointImports);
787
+ for (const [filename, content] of Object.entries(files)) {
788
+ const filePath = (0, node_path.join)(endpointsDir, filename);
789
+ await (0, node_fs_promises.mkdir)((0, node_path.dirname)(filePath), { recursive: true });
790
+ await (0, node_fs_promises.writeFile)(filePath, content);
791
+ }
792
+ const endpointFiles = Object.keys(files).filter((f) => !f.endsWith("index.ts") && !f.endsWith("validators.ts")).length;
793
+ const indexFiles = Object.keys(files).filter((f) => f.endsWith("index.ts")).length;
794
+ logger.log(` Generated ${endpointFiles} endpoint files + ${indexFiles} index files + validators.ts`);
795
+ return (0, node_path.join)(endpointsDir, "index.ts");
796
+ }
183
797
  async generateAppFile(outputDir, context) {
798
+ if (context.production?.enabled) return this.generateProductionAppFile(outputDir, context);
184
799
  const appFileName = "app.ts";
185
800
  const appPath = (0, node_path.join)(outputDir, appFileName);
186
801
  const relativeLoggerPath = (0, node_path.relative)((0, node_path.dirname)(appPath), context.loggerPath);
@@ -192,15 +807,15 @@ export async function setupEndpoints(
192
807
  const usesExternalStudio = !!context.studio?.studioPath;
193
808
  let telescopeImports = "";
194
809
  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}';
810
+ const relativeTelescopePath = (0, node_path.relative)((0, node_path.dirname)(appPath), context.telescope?.telescopePath);
811
+ telescopeImports = `import ${context.telescope?.telescopeImportPattern} from '${relativeTelescopePath}';
197
812
  import { createMiddleware, createUI } from '@geekmidas/telescope/hono';`;
198
813
  } else telescopeImports = `import { Telescope, InMemoryStorage } from '@geekmidas/telescope';
199
814
  import { createMiddleware, createUI } from '@geekmidas/telescope/hono';`;
200
815
  let studioImports = "";
201
816
  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}';
817
+ const relativeStudioPath = (0, node_path.relative)((0, node_path.dirname)(appPath), context.studio?.studioPath);
818
+ studioImports = `import ${context.studio?.studioImportPattern} from '${relativeStudioPath}';
204
819
  import { createStudioApp } from '@geekmidas/studio/server/hono';`;
205
820
  } else studioImports = `// Studio requires a configured instance - use studio config path
206
821
  // import { createStudioApp } from '@geekmidas/studio/server/hono';`;
@@ -229,7 +844,7 @@ import { createStudioApp } from '@geekmidas/studio/server/hono';`;
229
844
  const { createNodeWebSocket } = await import('@hono/node-ws');
230
845
  const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app: honoApp });
231
846
  // Add WebSocket route directly to main app (sub-app routes don't support WS upgrade)
232
- honoApp.get('${context.telescope.path}/ws', upgradeWebSocket(() => ({
847
+ honoApp.get('${context.telescope?.path}/ws', upgradeWebSocket(() => ({
233
848
  onOpen: (_event: Event, ws: any) => {
234
849
  telescope.addWsClient(ws);
235
850
  },
@@ -262,16 +877,16 @@ ${telescopeWebSocketSetupCode}
262
877
 
263
878
  // Mount telescope UI
264
879
  const telescopeUI = createUI(telescope);
265
- honoApp.route('${context.telescope.path}', telescopeUI);
880
+ honoApp.route('${context.telescope?.path}', telescopeUI);
266
881
  `;
267
882
  else telescopeSetup = `
268
883
  // Setup Telescope for debugging/monitoring
269
- const telescopeStorage = new InMemoryStorage({ maxEntries: ${context.telescope.maxEntries} });
884
+ const telescopeStorage = new InMemoryStorage({ maxEntries: ${context.telescope?.maxEntries} });
270
885
  const telescope = new Telescope({
271
886
  enabled: true,
272
- path: '${context.telescope.path}',
273
- ignorePatterns: ${JSON.stringify(context.telescope.ignore)},
274
- recordBody: ${context.telescope.recordBody},
887
+ path: '${context.telescope?.path}',
888
+ ignorePatterns: ${JSON.stringify(context.telescope?.ignore)},
889
+ recordBody: ${context.telescope?.recordBody},
275
890
  storage: telescopeStorage,
276
891
  });
277
892
  ${telescopeWebSocketSetupCode}
@@ -280,13 +895,13 @@ ${telescopeWebSocketSetupCode}
280
895
 
281
896
  // Mount telescope UI
282
897
  const telescopeUI = createUI(telescope);
283
- honoApp.route('${context.telescope.path}', telescopeUI);
898
+ honoApp.route('${context.telescope?.path}', telescopeUI);
284
899
  `;
285
900
  let studioSetup = "";
286
901
  if (studioEnabled && usesExternalStudio) studioSetup = `
287
902
  // Mount Studio data browser UI
288
903
  const studioApp = createStudioApp(studio);
289
- honoApp.route('${context.studio.path}', studioApp);
904
+ honoApp.route('${context.studio?.path}', studioApp);
290
905
  `;
291
906
  const content = `/**
292
907
  * Generated server application
@@ -415,6 +1030,154 @@ export const handler = adapter.handler;
415
1030
  export const handler = ${exportName};
416
1031
  `;
417
1032
  }
1033
+ /**
1034
+ * Generate a production-optimized app.ts file
1035
+ * No dev tools (Telescope, Studio, WebSocket), includes health checks and graceful shutdown
1036
+ */
1037
+ async generateProductionAppFile(outputDir, context) {
1038
+ const appFileName = "app.ts";
1039
+ const appPath = (0, node_path.join)(outputDir, appFileName);
1040
+ const relativeLoggerPath = (0, node_path.relative)((0, node_path.dirname)(appPath), context.loggerPath);
1041
+ const relativeEnvParserPath = (0, node_path.relative)((0, node_path.dirname)(appPath), context.envParserPath);
1042
+ const production = context.production;
1043
+ const healthCheckPath = production.healthCheck;
1044
+ const enableGracefulShutdown = production.gracefulShutdown;
1045
+ const enableOpenApi = production.openapi;
1046
+ const includeSubscribers = production.subscribers === "include";
1047
+ let hooksImports = "";
1048
+ let beforeSetupCall = "";
1049
+ let afterSetupCall = "";
1050
+ if (context.hooks?.serverHooksPath) {
1051
+ const relativeHooksPath = (0, node_path.relative)((0, node_path.dirname)(appPath), context.hooks.serverHooksPath);
1052
+ hooksImports = `import * as serverHooks from '${relativeHooksPath}';`;
1053
+ beforeSetupCall = `
1054
+ // Call beforeSetup hook if defined
1055
+ if (typeof serverHooks.beforeSetup === 'function') {
1056
+ await serverHooks.beforeSetup(honoApp, { envParser, logger });
1057
+ }
1058
+ `;
1059
+ afterSetupCall = `
1060
+ // Call afterSetup hook if defined
1061
+ if (typeof serverHooks.afterSetup === 'function') {
1062
+ await serverHooks.afterSetup(honoApp, { envParser, logger });
1063
+ }
1064
+ `;
1065
+ }
1066
+ const subscriberSetup = includeSubscribers ? `
1067
+ // Start subscribers in background
1068
+ await setupSubscribers(envParser, logger).catch((error) => {
1069
+ logger.error({ error }, 'Failed to start subscribers');
1070
+ });
1071
+ ` : "";
1072
+ const subscriberImport = includeSubscribers ? `import { setupSubscribers } from './subscribers.js';` : "";
1073
+ const gracefulShutdownCode = enableGracefulShutdown ? `
1074
+ // Graceful shutdown handling
1075
+ let isShuttingDown = false;
1076
+
1077
+ const shutdown = async () => {
1078
+ if (isShuttingDown) return;
1079
+ isShuttingDown = true;
1080
+ logger.info('Graceful shutdown initiated');
1081
+ // Allow in-flight requests to complete (30s timeout)
1082
+ setTimeout(() => process.exit(0), 30000);
1083
+ };
1084
+
1085
+ process.on('SIGTERM', shutdown);
1086
+ process.on('SIGINT', shutdown);
1087
+ ` : "";
1088
+ const endpointsImportPath = production.optimizedHandlers ? "./endpoints/index.js" : "./endpoints.js";
1089
+ const content = `/**
1090
+ * Generated production server application
1091
+ *
1092
+ * This is a production-optimized build without dev tools.
1093
+ * - No Telescope debugging dashboard
1094
+ * - No Studio database browser
1095
+ * - No WebSocket updates
1096
+ * - Includes health checks and graceful shutdown
1097
+ */
1098
+ import { Hono } from 'hono';
1099
+ import type { Hono as HonoType } from 'hono';
1100
+ import { setupEndpoints } from '${endpointsImportPath}';
1101
+ ${subscriberImport}
1102
+ import ${context.envParserImportPattern} from '${relativeEnvParserPath}';
1103
+ import ${context.loggerImportPattern} from '${relativeLoggerPath}';
1104
+ ${hooksImports}
1105
+
1106
+ export interface ServerApp {
1107
+ app: HonoType;
1108
+ start: (options?: {
1109
+ port?: number;
1110
+ serve: (app: HonoType, port: number) => void | Promise<void>;
1111
+ }) => Promise<void>;
1112
+ }
1113
+
1114
+ /**
1115
+ * Create and configure the production Hono application
1116
+ */
1117
+ export async function createApp(app?: HonoType): Promise<ServerApp> {
1118
+ const honoApp = app || new Hono();
1119
+
1120
+ // Health check endpoint (always first)
1121
+ honoApp.get('${healthCheckPath}', (c) => c.json({ status: 'ok', timestamp: Date.now() }));
1122
+ honoApp.get('/ready', (c) => c.json({ ready: true }));
1123
+ ${beforeSetupCall}
1124
+ // Setup HTTP endpoints (OpenAPI: ${enableOpenApi})
1125
+ await setupEndpoints(honoApp, envParser, logger, ${enableOpenApi});
1126
+ ${afterSetupCall}
1127
+ return {
1128
+ app: honoApp,
1129
+ async start(options) {
1130
+ if (!options?.serve) {
1131
+ throw new Error(
1132
+ 'serve function is required. Pass a serve function for your runtime:\\n' +
1133
+ ' - Bun: (app, port) => Bun.serve({ port, fetch: app.fetch })\\n' +
1134
+ ' - Node: (app, port) => serve({ fetch: app.fetch, port })'
1135
+ );
1136
+ }
1137
+
1138
+ const port = options.port ?? Number(process.env.PORT) ?? 3000;
1139
+ ${gracefulShutdownCode}${subscriberSetup}
1140
+ logger.info({ port }, 'Starting production server');
1141
+
1142
+ // Start HTTP server using provided serve function
1143
+ await options.serve(honoApp, port);
1144
+
1145
+ logger.info({ port }, 'Production server started');
1146
+ }
1147
+ };
1148
+ }
1149
+
1150
+ // Default export for convenience
1151
+ export default createApp;
1152
+ `;
1153
+ await (0, node_fs_promises.writeFile)(appPath, content);
1154
+ await this.generateProductionServerEntry(outputDir);
1155
+ return appPath;
1156
+ }
1157
+ /**
1158
+ * Generate production server.ts entry point
1159
+ */
1160
+ async generateProductionServerEntry(outputDir) {
1161
+ const serverPath = (0, node_path.join)(outputDir, "server.ts");
1162
+ const content = `#!/usr/bin/env node
1163
+ /**
1164
+ * Production server entry point
1165
+ * Generated by 'gkm build --provider server --production'
1166
+ */
1167
+ import { serve } from '@hono/node-server';
1168
+ import { createApp } from './app.js';
1169
+
1170
+ const port = Number(process.env.PORT) || 3000;
1171
+
1172
+ const { app, start } = await createApp();
1173
+
1174
+ await start({
1175
+ port,
1176
+ serve: (app, port) => serve({ fetch: app.fetch, port }),
1177
+ });
1178
+ `;
1179
+ await (0, node_fs_promises.writeFile)(serverPath, content);
1180
+ }
418
1181
  };
419
1182
 
420
1183
  //#endregion
@@ -1011,4 +1774,4 @@ Object.defineProperty(exports, 'resolveOpenApiConfig', {
1011
1774
  return resolveOpenApiConfig;
1012
1775
  }
1013
1776
  });
1014
- //# sourceMappingURL=openapi-BeHLKcwP.cjs.map
1777
+ //# sourceMappingURL=openapi-Bt_1FDpT.cjs.map