@geekmidas/cli 10.0.0-alpha.37 → 10.0.0-alpha.39

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 (114) hide show
  1. package/README.md +27 -26
  2. package/dist/{HostingerProvider-GF6NAZAK.mjs → HostingerProvider-Bh6K7QYz.mjs} +2 -2
  3. package/dist/{HostingerProvider-GF6NAZAK.mjs.map → HostingerProvider-Bh6K7QYz.mjs.map} +1 -1
  4. package/dist/{OpenApiTsGenerator-D1nhN98m.cjs → OpenApiTsGenerator-CeEI4ll9.cjs} +3 -3
  5. package/dist/OpenApiTsGenerator-CeEI4ll9.cjs.map +1 -0
  6. package/dist/{OpenApiTsGenerator-hDINScnz.mjs → OpenApiTsGenerator-DQqOuSW8.mjs} +3 -3
  7. package/dist/OpenApiTsGenerator-DQqOuSW8.mjs.map +1 -0
  8. package/dist/{ssm-FxeSAt35.cjs → aws-DkHdTV-p.cjs} +7 -6
  9. package/dist/aws-DkHdTV-p.cjs.map +1 -0
  10. package/dist/{ssm-BKN1Cg4z.mjs → aws-DojzQnLd.mjs} +7 -6
  11. package/dist/aws-DojzQnLd.mjs.map +1 -0
  12. package/dist/{bundler-D9IPNvDt.mjs → bundler--fyybf1N.mjs} +13 -7
  13. package/dist/bundler--fyybf1N.mjs.map +1 -0
  14. package/dist/{bundler-CYF5mWi9.cjs → bundler-By6_sXiL.cjs} +12 -6
  15. package/dist/bundler-By6_sXiL.cjs.map +1 -0
  16. package/dist/{config-Dss1WwtH.mjs → config-5watB1D0.mjs} +95 -71
  17. package/dist/config-5watB1D0.mjs.map +1 -0
  18. package/dist/{config-DgAHy8nY.cjs → config-CJ-PAb7M.cjs} +116 -74
  19. package/dist/config-CJ-PAb7M.cjs.map +1 -0
  20. package/dist/config.cjs +6 -3
  21. package/dist/config.d.cts +40 -19
  22. package/dist/config.d.cts.map +1 -1
  23. package/dist/config.d.mts +40 -19
  24. package/dist/config.d.mts.map +1 -1
  25. package/dist/config.mjs +3 -3
  26. package/dist/{discover-CroTIm0K.cjs → discover-B0f_kuNN.cjs} +2 -1
  27. package/dist/{discover-CroTIm0K.cjs.map → discover-B0f_kuNN.cjs.map} +1 -1
  28. package/dist/{discover-CMxesskG.mjs → discover-BLhf7V_J.mjs} +2 -1
  29. package/dist/{discover-CMxesskG.mjs.map → discover-BLhf7V_J.mjs.map} +1 -1
  30. package/dist/{github-tZmMZ7td.cjs → github-BAr8WGkR.cjs} +10 -13
  31. package/dist/github-BAr8WGkR.cjs.map +1 -0
  32. package/dist/{github-C99BMxf0.mjs → github-D5MthUlR.mjs} +8 -11
  33. package/dist/github-D5MthUlR.mjs.map +1 -0
  34. package/dist/{index-CAp22ul5.d.cts → index-CLH1DgsO.d.cts} +2 -2
  35. package/dist/{index-CAp22ul5.d.cts.map → index-CLH1DgsO.d.cts.map} +1 -1
  36. package/dist/{index-DxXMtQNT.d.mts → index-CST2jOPH.d.mts} +2 -2
  37. package/dist/{index-DxXMtQNT.d.mts.map → index-CST2jOPH.d.mts.map} +1 -1
  38. package/dist/index.cjs +1015 -353
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.mjs +1003 -341
  41. package/dist/index.mjs.map +1 -1
  42. package/dist/{migrate-_2J3lFT5.mjs → migrate-BJR912RE.mjs} +9 -7
  43. package/dist/migrate-BJR912RE.mjs.map +1 -0
  44. package/dist/{migrate-Cgga-OGi.cjs → migrate-Bu8POqsR.cjs} +8 -6
  45. package/dist/migrate-Bu8POqsR.cjs.map +1 -0
  46. package/dist/openapi.cjs +2 -2
  47. package/dist/openapi.d.cts +1 -1
  48. package/dist/openapi.d.mts +1 -1
  49. package/dist/openapi.mjs +2 -2
  50. package/dist/{ready-CXhoutix.cjs → ready-B0WVtkjR.cjs} +96 -6
  51. package/dist/ready-B0WVtkjR.cjs.map +1 -0
  52. package/dist/{ready-D1BCiaYU.mjs → ready-BXmLS4VB.mjs} +92 -8
  53. package/dist/ready-BXmLS4VB.mjs.map +1 -0
  54. package/dist/reconcile/public.cjs +1 -1
  55. package/dist/reconcile/public.d.cts +1 -1
  56. package/dist/reconcile/public.d.mts +1 -1
  57. package/dist/reconcile/public.mjs +1 -1
  58. package/dist/reconcile--I7V4Klj.mjs +34 -0
  59. package/dist/reconcile--I7V4Klj.mjs.map +1 -0
  60. package/dist/reconcile-CuCQBa5-.cjs +34 -0
  61. package/dist/reconcile-CuCQBa5-.cjs.map +1 -0
  62. package/dist/storage-BzaUxmqe.mjs +124 -0
  63. package/dist/storage-BzaUxmqe.mjs.map +1 -0
  64. package/dist/storage-CSGoC3bK.cjs +152 -0
  65. package/dist/storage-CSGoC3bK.cjs.map +1 -0
  66. package/dist/store-DQgxT_WR.cjs +211 -0
  67. package/dist/store-DQgxT_WR.cjs.map +1 -0
  68. package/dist/store-DeGATCS-.mjs +182 -0
  69. package/dist/store-DeGATCS-.mjs.map +1 -0
  70. package/dist/{types-BTiu5kwo.d.mts → types-B0EVAF6U.d.mts} +24 -10
  71. package/dist/types-B0EVAF6U.d.mts.map +1 -0
  72. package/dist/{types-Bj_FpeTc.d.cts → types-CHKCUKM0.d.cts} +24 -10
  73. package/dist/types-CHKCUKM0.d.cts.map +1 -0
  74. package/dist/vitest.cjs +1 -1
  75. package/dist/vitest.mjs +1 -1
  76. package/dist/workspace/index.cjs +1 -1
  77. package/dist/workspace/index.d.cts +2 -2
  78. package/dist/workspace/index.d.mts +2 -2
  79. package/dist/workspace/index.mjs +1 -1
  80. package/dist/{workspace-BtXCL1C8.mjs → workspace-CrFbItPz.mjs} +111 -39
  81. package/dist/workspace-CrFbItPz.mjs.map +1 -0
  82. package/dist/{workspace-CsL76tpc.mjs → workspace-DOmIhiau.mjs} +2 -2
  83. package/dist/{workspace-CsL76tpc.mjs.map → workspace-DOmIhiau.mjs.map} +1 -1
  84. package/dist/{workspace-DUhLejv4.cjs → workspace-DZ1G4zow.cjs} +121 -37
  85. package/dist/workspace-DZ1G4zow.cjs.map +1 -0
  86. package/dist/{workspace-CAJk6UmC.cjs → workspace-RYSYtHP7.cjs} +2 -2
  87. package/dist/{workspace-CAJk6UmC.cjs.map → workspace-RYSYtHP7.cjs.map} +1 -1
  88. package/package.json +14 -14
  89. package/dist/OpenApiTsGenerator-D1nhN98m.cjs.map +0 -1
  90. package/dist/OpenApiTsGenerator-hDINScnz.mjs.map +0 -1
  91. package/dist/bundler-CYF5mWi9.cjs.map +0 -1
  92. package/dist/bundler-D9IPNvDt.mjs.map +0 -1
  93. package/dist/config-DgAHy8nY.cjs.map +0 -1
  94. package/dist/config-Dss1WwtH.mjs.map +0 -1
  95. package/dist/github-C99BMxf0.mjs.map +0 -1
  96. package/dist/github-tZmMZ7td.cjs.map +0 -1
  97. package/dist/migrate-Cgga-OGi.cjs.map +0 -1
  98. package/dist/migrate-_2J3lFT5.mjs.map +0 -1
  99. package/dist/ready-CXhoutix.cjs.map +0 -1
  100. package/dist/ready-D1BCiaYU.mjs.map +0 -1
  101. package/dist/reconcile-BxZTV2vl.cjs +0 -332
  102. package/dist/reconcile-BxZTV2vl.cjs.map +0 -1
  103. package/dist/reconcile-yNq6Shau.mjs +0 -279
  104. package/dist/reconcile-yNq6Shau.mjs.map +0 -1
  105. package/dist/ssm-BKN1Cg4z.mjs.map +0 -1
  106. package/dist/ssm-FxeSAt35.cjs.map +0 -1
  107. package/dist/storage-BjTLjDDp.mjs +0 -297
  108. package/dist/storage-BjTLjDDp.mjs.map +0 -1
  109. package/dist/storage-C2JC31V9.cjs +0 -344
  110. package/dist/storage-C2JC31V9.cjs.map +0 -1
  111. package/dist/types-BTiu5kwo.d.mts.map +0 -1
  112. package/dist/types-Bj_FpeTc.d.cts.map +0 -1
  113. package/dist/workspace-BtXCL1C8.mjs.map +0 -1
  114. package/dist/workspace-DUhLejv4.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpenApiTsGenerator-DQqOuSW8.mjs","names":[],"sources":["../src/build/endpoint-analyzer.ts","../src/build/handler-templates.ts","../src/generators/EndpointGenerator.ts","../src/generators/OpenApiTsGenerator.ts"],"sourcesContent":["/**\n * Endpoint Analyzer for Build-Time Feature Detection\n *\n * Analyzes endpoints at build time to determine their features and assign\n * optimization tiers. This enables generating specialized handler code\n * for maximum performance.\n */\nimport type { Endpoint } from '@geekmidas/constructs/endpoints';\n\n/**\n * Features detected from an endpoint configuration\n */\nexport interface EndpointFeatures {\n\thasAuth: boolean;\n\thasServices: boolean;\n\thasDatabase: boolean;\n\thasBodyValidation: boolean;\n\thasQueryValidation: boolean;\n\thasParamValidation: boolean;\n\thasAudits: boolean;\n\thasEvents: boolean;\n\thasRateLimit: boolean;\n\thasRls: boolean;\n\thasOutputValidation: boolean;\n}\n\n/**\n * Optimization tiers based on endpoint complexity\n *\n * - minimal: No auth, no services, no audits, no events - near-raw-Hono performance\n * - standard: Some features enabled - uses middleware composition\n * - full: Complex endpoints with many features - full handler chain\n */\nexport type EndpointTier = 'minimal' | 'standard' | 'full';\n\n/**\n * Complete analysis of an endpoint for build-time optimization\n */\nexport interface EndpointAnalysis {\n\troute: string;\n\tmethod: string;\n\texportName: string;\n\tfeatures: EndpointFeatures;\n\ttier: EndpointTier;\n\tserviceNames: string[];\n\tdatabaseServiceName?: string;\n}\n\n/**\n * Analyze an endpoint to extract its features\n */\nexport function analyzeEndpointFeatures(\n\tendpoint: Endpoint<\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany\n\t>,\n): EndpointFeatures {\n\treturn {\n\t\thasAuth: !!endpoint.authorizer,\n\t\thasServices: endpoint.services.length > 0,\n\t\thasDatabase: !!endpoint.databaseService,\n\t\thasBodyValidation: !!endpoint.input?.body,\n\t\thasQueryValidation: !!endpoint.input?.query,\n\t\thasParamValidation: !!endpoint.input?.params,\n\t\t// Only declarative audits (.audit([...])) require full tier with transaction wrapping\n\t\t// Having auditorStorageService just makes auditor available to handler (like other services)\n\t\thasAudits: (endpoint.audits?.length ?? 0) > 0,\n\t\thasEvents: (endpoint.events?.length ?? 0) > 0,\n\t\thasRateLimit: !!endpoint.rateLimit,\n\t\thasRls: !!endpoint.rlsConfig && !endpoint.rlsBypass,\n\t\thasOutputValidation: !!endpoint.outputSchema,\n\t};\n}\n\n/**\n * Determine the optimization tier for an endpoint based on its features\n */\nexport function determineEndpointTier(\n\tfeatures: EndpointFeatures,\n): EndpointTier {\n\tconst {\n\t\thasAuth,\n\t\thasServices,\n\t\thasDatabase,\n\t\thasAudits,\n\t\thasEvents,\n\t\thasRateLimit,\n\t\thasRls,\n\t} = features;\n\n\t// Minimal tier: No complex features\n\t// These endpoints can use near-raw-Hono handlers\n\tif (\n\t\t!hasAuth &&\n\t\t!hasServices &&\n\t\t!hasDatabase &&\n\t\t!hasAudits &&\n\t\t!hasEvents &&\n\t\t!hasRateLimit &&\n\t\t!hasRls\n\t) {\n\t\treturn 'minimal';\n\t}\n\n\t// Full tier: Has audits, RLS, or rate limiting (complex state management)\n\tif (hasAudits || hasRls || hasRateLimit) {\n\t\treturn 'full';\n\t}\n\n\t// Standard tier: Auth and/or services, but no complex state\n\treturn 'standard';\n}\n\n/**\n * Perform complete analysis of an endpoint\n */\nexport function analyzeEndpoint(\n\tendpoint: Endpoint<\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany\n\t>,\n\texportName: string,\n): EndpointAnalysis {\n\tconst features = analyzeEndpointFeatures(endpoint);\n\tconst tier = determineEndpointTier(features);\n\n\treturn {\n\t\troute: endpoint.route,\n\t\tmethod: endpoint.method,\n\t\texportName,\n\t\tfeatures,\n\t\ttier,\n\t\tserviceNames: endpoint.services.map(\n\t\t\t(s: { serviceName: string }) => s.serviceName,\n\t\t),\n\t\tdatabaseServiceName: endpoint.databaseService?.serviceName,\n\t};\n}\n\n/**\n * Analyze multiple endpoints and return analysis results\n */\nexport function analyzeEndpoints(\n\tendpoints: Array<{\n\t\tendpoint: Endpoint<\n\t\t\tany,\n\t\t\tany,\n\t\t\tany,\n\t\t\tany,\n\t\t\tany,\n\t\t\tany,\n\t\t\tany,\n\t\t\tany,\n\t\t\tany,\n\t\t\tany,\n\t\t\tany,\n\t\t\tany\n\t\t>;\n\t\texportName: string;\n\t}>,\n): EndpointAnalysis[] {\n\treturn endpoints.map(({ endpoint, exportName }) =>\n\t\tanalyzeEndpoint(endpoint, exportName),\n\t);\n}\n\n/**\n * Generate a summary of endpoint analysis for logging\n */\nexport function summarizeAnalysis(analyses: EndpointAnalysis[]): {\n\ttotal: number;\n\tbyTier: Record<EndpointTier, number>;\n\tbyFeature: Record<keyof EndpointFeatures, number>;\n} {\n\tconst byTier: Record<EndpointTier, number> = {\n\t\tminimal: 0,\n\t\tstandard: 0,\n\t\tfull: 0,\n\t};\n\n\tconst byFeature: Record<keyof EndpointFeatures, number> = {\n\t\thasAuth: 0,\n\t\thasServices: 0,\n\t\thasDatabase: 0,\n\t\thasBodyValidation: 0,\n\t\thasQueryValidation: 0,\n\t\thasParamValidation: 0,\n\t\thasAudits: 0,\n\t\thasEvents: 0,\n\t\thasRateLimit: 0,\n\t\thasRls: 0,\n\t\thasOutputValidation: 0,\n\t};\n\n\tfor (const analysis of analyses) {\n\t\tbyTier[analysis.tier]++;\n\n\t\tfor (const [feature, enabled] of Object.entries(analysis.features)) {\n\t\t\tif (enabled) {\n\t\t\t\tbyFeature[feature as keyof EndpointFeatures]++;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\ttotal: analyses.length,\n\t\tbyTier,\n\t\tbyFeature,\n\t};\n}\n","/**\n * Handler Templates for Build-Time Code Generation\n *\n * Generates optimized handler code based on endpoint tier:\n * - minimal: Near-raw-Hono performance for simple endpoints\n * - standard: Middleware composition for auth/services\n * - full: Complete handler chain for complex endpoints\n *\n * Output structure (split by tier):\n * - endpoints/validators.ts - Shared validator factories\n * - endpoints/minimal.ts - Minimal tier handlers\n * - endpoints/standard.ts - Standard tier handlers\n * - endpoints/full.ts - Full tier handlers\n * - endpoints/index.ts - Main entry point\n */\nimport type { EndpointAnalysis, EndpointFeatures } from './endpoint-analyzer';\n\n/**\n * Multi-file output structure (flat by tier)\n */\nexport interface GeneratedEndpointFiles {\n\t'validators.ts': string;\n\t'minimal.ts': string;\n\t'standard.ts': string;\n\t'full.ts': string;\n\t'index.ts': string;\n}\n\n/**\n * Nested folder structure with per-endpoint files\n *\n * Structure:\n * - endpoints/validators.ts\n * - endpoints/minimal/index.ts\n * - endpoints/minimal/[endpointName].ts\n * - endpoints/standard/index.ts\n * - endpoints/standard/[endpointName].ts\n * - endpoints/full/index.ts\n * - endpoints/full/[endpointName].ts\n * - endpoints/index.ts\n */\nexport interface GeneratedEndpointFilesNested {\n\t'validators.ts': string;\n\t'minimal/index.ts': string;\n\t'standard/index.ts': string;\n\t'full/index.ts': string;\n\t'index.ts': string;\n\t[path: string]: string; // e.g., 'minimal/healthEndpoint.ts'\n}\n\n/**\n * Endpoint import info for generating import statements\n */\nexport interface EndpointImportInfo {\n\texportName: string;\n\timportPath: string;\n}\n\n/**\n * Generate imports needed for optimized endpoints file\n */\nexport function generateOptimizedImports(analyses: EndpointAnalysis[]): string {\n\tconst needsValidator = analyses.some(\n\t\t(a) =>\n\t\t\ta.features.hasBodyValidation ||\n\t\t\ta.features.hasQueryValidation ||\n\t\t\ta.features.hasParamValidation,\n\t);\n\n\tconst needsResponseBuilder = analyses.some(\n\t\t(a) => a.tier === 'standard' || a.tier === 'full',\n\t);\n\n\tconst needsServiceDiscovery = analyses.some(\n\t\t(a) => a.features.hasServices || a.features.hasDatabase,\n\t);\n\n\tconst needsEvents = analyses.some((a) => a.features.hasEvents);\n\n\tconst needsAudits = analyses.some((a) => a.features.hasAudits);\n\n\tconst needsRateLimit = analyses.some((a) => a.features.hasRateLimit);\n\n\tconst needsRls = analyses.some((a) => a.features.hasRls);\n\n\tconst imports: string[] = [\n\t\t`import type { EnvironmentParser } from '@geekmidas/envkit';`,\n\t\t`import type { Logger } from '@geekmidas/logger';`,\n\t\t`import type { Hono } from 'hono';`,\n\t];\n\n\tif (needsValidator) {\n\t\timports.push(`import { validator } from 'hono/validator';`);\n\t}\n\n\timports.push(`import { Endpoint } from '@geekmidas/constructs/endpoints';`);\n\n\tif (needsResponseBuilder) {\n\t\timports.push(\n\t\t\t`import { ResponseBuilder } from '@geekmidas/constructs/endpoints';`,\n\t\t);\n\t}\n\n\tif (needsServiceDiscovery) {\n\t\timports.push(`import { ServiceDiscovery } from '@geekmidas/services';`);\n\t}\n\n\tif (needsEvents) {\n\t\timports.push(\n\t\t\t`import { publishConstructEvents } from '@geekmidas/constructs/endpoints';`,\n\t\t);\n\t}\n\n\tif (needsAudits) {\n\t\timports.push(\n\t\t\t`import { createAuditContext, withAuditableEndpointTransaction } from '@geekmidas/constructs/endpoints';`,\n\t\t);\n\t}\n\n\tif (needsRateLimit) {\n\t\timports.push(`import { createError } from '@geekmidas/errors';`);\n\t}\n\n\tif (needsRls) {\n\t\timports.push(\n\t\t\t`import { withRlsContext, extractRlsContext } from '@geekmidas/constructs/endpoints';`,\n\t\t);\n\t}\n\n\treturn imports.join('\\n');\n}\n\n/**\n * Generate reusable validator middleware factories\n */\nexport function generateValidatorFactories(\n\tanalyses: EndpointAnalysis[],\n): string {\n\tconst needsBody = analyses.some((a) => a.features.hasBodyValidation);\n\tconst needsQuery = analyses.some((a) => a.features.hasQueryValidation);\n\tconst needsParams = analyses.some((a) => a.features.hasParamValidation);\n\n\tif (!needsBody && !needsQuery && !needsParams) {\n\t\treturn '';\n\t}\n\n\tconst factories: string[] = [];\n\n\tif (needsBody) {\n\t\tfactories.push(`\nconst validateBody = (endpoint: any) =>\n validator('json', async (value, c) => {\n if (!endpoint.input?.body) return undefined;\n const parsed = await Endpoint.validate(endpoint.input.body, value);\n if (parsed.issues) return c.json(parsed.issues, 422);\n return parsed.value;\n });`);\n\t}\n\n\tif (needsQuery) {\n\t\tfactories.push(`\nconst validateQuery = (endpoint: any) =>\n validator('query', async (_, c) => {\n if (!endpoint.input?.query) return undefined;\n const rawQuery = Object.fromEntries(new URL(c.req.url).searchParams);\n const parsed = await Endpoint.validate(endpoint.input.query, rawQuery);\n if (parsed.issues) return c.json(parsed.issues, 422);\n return parsed.value;\n });`);\n\t}\n\n\tif (needsParams) {\n\t\tfactories.push(`\nconst validateParams = (endpoint: any) =>\n validator('param', async (params, c) => {\n if (!endpoint.input?.params) return undefined;\n const parsed = await Endpoint.validate(endpoint.input.params, params);\n if (parsed.issues) return c.json(parsed.issues, 422);\n return parsed.value;\n });`);\n\t}\n\n\treturn factories.join('\\n');\n}\n\n/**\n * Generate validator references for an endpoint\n */\nfunction generateValidators(\n\texportName: string,\n\tfeatures: EndpointFeatures,\n): string {\n\tconst validators: string[] = [];\n\n\tif (features.hasBodyValidation) {\n\t\tvalidators.push(`validateBody(${exportName})`);\n\t}\n\n\tif (features.hasQueryValidation) {\n\t\tvalidators.push(`validateQuery(${exportName})`);\n\t}\n\n\tif (features.hasParamValidation) {\n\t\tvalidators.push(`validateParams(${exportName})`);\n\t}\n\n\t// Add trailing comma if there are validators (needed before the handler function)\n\treturn validators.length > 0 ? `\\n ${validators.join(',\\n ')},` : '';\n}\n\n/**\n * Generate a minimal handler (near-raw-Hono performance)\n *\n * Used for: Health checks, public endpoints with no services\n */\nexport function generateMinimalHandler(analysis: EndpointAnalysis): string {\n\tconst { exportName, features } = analysis;\n\tconst method = analysis.method.toLowerCase();\n\n\tconst validators = generateValidators(exportName, features);\n\tconst hasValidators = validators.length > 0;\n\n\t// For truly minimal endpoints (no validation), generate inline handler\n\tif (!hasValidators && !features.hasOutputValidation) {\n\t\treturn `\n // Minimal handler: ${analysis.route} (${analysis.method})\n app.${method}('${analysis.route}', async (c) => {\n const result = await ${exportName}.handler(\n {\n services: {},\n logger,\n body: undefined,\n query: undefined,\n params: undefined,\n session: undefined,\n header: Endpoint.createHeaders(c.req.header()),\n cookie: Endpoint.createCookies(c.req.header().cookie),\n auditor: undefined,\n db: undefined,\n } as any,\n { getMetadata: () => ({}) } as any,\n );\n return c.json(result, ${exportName}.status as any);\n });`;\n\t}\n\n\t// With validation but still minimal\n\treturn `\n // Minimal handler with validation: ${analysis.route} (${analysis.method})\n app.${method}('${analysis.route}',${validators}\n async (c) => {\n const result = await ${exportName}.handler(\n {\n services: {},\n logger,\n body: ${features.hasBodyValidation ? \"(c.req.valid as any)('json')\" : 'undefined'},\n query: ${features.hasQueryValidation ? \"(c.req.valid as any)('query')\" : 'undefined'},\n params: ${features.hasParamValidation ? \"(c.req.valid as any)('param')\" : 'undefined'},\n session: undefined,\n header: Endpoint.createHeaders(c.req.header()),\n cookie: Endpoint.createCookies(c.req.header().cookie),\n auditor: undefined,\n db: undefined,\n } as any,\n { getMetadata: () => ({}) } as any,\n );\n ${\n\t\t\t\tfeatures.hasOutputValidation\n\t\t\t\t\t? `const output = await ${exportName}.parseOutput(result);\n return c.json(output, ${exportName}.status as any);`\n\t\t\t\t\t: `return c.json(result, ${exportName}.status as any);`\n\t\t\t}\n }\n );`;\n}\n\n/**\n * Generate a standard handler (auth and/or services)\n *\n * Used for: Authenticated endpoints, endpoints with services\n */\nexport function generateStandardHandler(analysis: EndpointAnalysis): string {\n\tconst { exportName, features } = analysis;\n\tconst method = analysis.method.toLowerCase();\n\n\tconst validators = generateValidators(exportName, features);\n\n\t// Build service resolution code\n\tlet serviceResolution = '';\n\tif (features.hasServices || features.hasDatabase) {\n\t\tserviceResolution = `\n const services = await serviceDiscovery.register(${exportName}.services);\n ${\n\t\t\t\tfeatures.hasDatabase\n\t\t\t\t\t? `const db = ${exportName}.databaseService\n ? (await serviceDiscovery.register([${exportName}.databaseService]) as any)[${exportName}.databaseService.serviceName]\n : undefined;`\n\t\t\t\t\t: 'const db = undefined;'\n\t\t\t}`;\n\t} else {\n\t\tserviceResolution = `\n const services = {};\n const db = undefined;`;\n\t}\n\n\t// Build auth code\n\tlet authCode = '';\n\tif (features.hasAuth) {\n\t\tauthCode = `\n // Authentication\n const session = await ${exportName}.getSession({\n services,\n logger,\n header,\n cookie,\n ...(db !== undefined && { db }),\n } as any);\n\n const isAuthorized = await ${exportName}.authorize({\n header,\n cookie,\n services,\n logger,\n session,\n } as any);\n\n if (!isAuthorized) {\n return c.json({ error: 'Unauthorized' }, 401);\n }`;\n\t}\n\n\t// Build event publishing code\n\tlet eventCode = '';\n\tif (features.hasEvents) {\n\t\teventCode = `\n // Publish events on success\n if (Endpoint.isSuccessStatus(${exportName}.status)) {\n await (publishConstructEvents as any)(\n ${exportName},\n result,\n serviceDiscovery,\n logger,\n );\n }`;\n\t}\n\n\treturn `\n // Standard handler: ${analysis.route} (${analysis.method})\n app.${method}('${analysis.route}',${validators}\n async (c) => {\n const headerValues = c.req.header();\n const header = Endpoint.createHeaders(headerValues);\n const cookie = Endpoint.createCookies(headerValues.cookie);\n${serviceResolution}\n${authCode}\n\n const responseBuilder = new ResponseBuilder();\n const result = await ${exportName}.handler(\n {\n services,\n logger,\n body: ${features.hasBodyValidation ? \"(c.req.valid as any)('json')\" : 'undefined'},\n query: ${features.hasQueryValidation ? \"(c.req.valid as any)('query')\" : 'undefined'},\n params: ${features.hasParamValidation ? \"(c.req.valid as any)('param')\" : 'undefined'},\n session: ${features.hasAuth ? 'session' : 'undefined'},\n header,\n cookie,\n auditor: undefined,\n db,\n } as any,\n responseBuilder,\n );\n\n let data = result;\n let metadata = responseBuilder.getMetadata();\n\n if (Endpoint.hasMetadata(result)) {\n data = result.data;\n metadata = result.metadata;\n }\n\n ${\n\t\t\t\tfeatures.hasOutputValidation\n\t\t\t\t\t? `const output = ${exportName}.outputSchema\n ? await ${exportName}.parseOutput(data)\n : data;`\n\t\t\t\t\t: 'const output = data;'\n\t\t\t}\n${eventCode}\n\n const status = (metadata.status ?? ${exportName}.status) as any;\n return c.json(output, status);\n }\n );`;\n}\n\n/**\n * Generate setup function that uses HonoEndpoint.addRoutes for full-featured endpoints\n * but generates optimized inline handlers for minimal/standard endpoints\n */\nexport function generateOptimizedSetupFunction(\n\tanalyses: EndpointAnalysis[],\n\t_allExportNames: string[],\n): string {\n\tconst minimalEndpoints = analyses.filter((a) => a.tier === 'minimal');\n\tconst standardEndpoints = analyses.filter((a) => a.tier === 'standard');\n\tconst fullEndpoints = analyses.filter((a) => a.tier === 'full');\n\n\t// Generate inline handlers for minimal and standard endpoints\n\tconst minimalHandlers = minimalEndpoints\n\t\t.map((a) => generateMinimalHandler(a))\n\t\t.join('\\n');\n\n\tconst standardHandlers = standardEndpoints\n\t\t.map((a) => generateStandardHandler(a))\n\t\t.join('\\n');\n\n\t// Full endpoints use HonoEndpoint.addRoutes\n\tconst fullEndpointNames = fullEndpoints.map((a) => a.exportName);\n\n\tconst fullEndpointsSetup =\n\t\tfullEndpointNames.length > 0\n\t\t\t? `\n // Full-featured endpoints use HonoEndpoint.addRoutes\n const fullEndpoints = [${fullEndpointNames.join(', ')}];\n HonoEndpoint.addRoutes(fullEndpoints, serviceDiscovery, app, openApiOptions);`\n\t\t\t: '';\n\n\t// Add HonoEndpoint import only if needed\n\tconst honoEndpointImport =\n\t\tfullEndpointNames.length > 0\n\t\t\t? `import { HonoEndpoint } from '@geekmidas/constructs/hono';`\n\t\t\t: '';\n\n\t// Only generate openApiOptions if we have full endpoints that need it\n\tconst openApiOptionsDecl =\n\t\tfullEndpointNames.length > 0\n\t\t\t? `\n const openApiOptions: any = enableOpenApi ? {\n docsPath: '/__docs',\n openApiOptions: {\n title: 'API Documentation',\n version: '1.0.0',\n description: 'Generated API documentation'\n }\n } : { docsPath: false };`\n\t\t\t: '';\n\n\treturn `${honoEndpointImport}\n\nexport async function setupEndpoints(\n app: Hono,\n envParser: EnvironmentParser<any>,\n logger: Logger,\n enableOpenApi: boolean = false,\n): Promise<void> {\n const serviceDiscovery = ServiceDiscovery.getInstance(envParser);\n${openApiOptionsDecl}\n\n // ============================================\n // Minimal handlers (${minimalEndpoints.length} endpoints)\n // Near-raw-Hono performance\n // ============================================\n${minimalHandlers}\n\n // ============================================\n // Standard handlers (${standardEndpoints.length} endpoints)\n // Auth and/or services\n // ============================================\n${standardHandlers}\n${fullEndpointsSetup}\n\n // Add Swagger UI if OpenAPI is enabled\n if (enableOpenApi) {\n try {\n const { swaggerUI } = await import('@hono/swagger-ui');\n app.get('/__docs/ui', swaggerUI({ url: '/__docs' }));\n } catch {\n // @hono/swagger-ui not installed, skip Swagger UI\n }\n }\n}`;\n}\n\n/**\n * Generate complete optimized endpoints file\n */\nexport function generateOptimizedEndpointsFile(\n\tanalyses: EndpointAnalysis[],\n\tendpointImports: string,\n\tallExportNames: string[],\n): string {\n\tconst imports = generateOptimizedImports(analyses);\n\tconst validatorFactories = generateValidatorFactories(analyses);\n\tconst setupFunction = generateOptimizedSetupFunction(\n\t\tanalyses,\n\t\tallExportNames,\n\t);\n\n\treturn `/**\n * Generated optimized endpoints file\n *\n * Build-time optimization tiers:\n * - minimal: ${analyses.filter((a) => a.tier === 'minimal').length} endpoints (near-raw-Hono)\n * - standard: ${analyses.filter((a) => a.tier === 'standard').length} endpoints (auth/services)\n * - full: ${analyses.filter((a) => a.tier === 'full').length} endpoints (audits/rls/rate-limit)\n */\n${imports}\n${endpointImports}\n${validatorFactories}\n\n${setupFunction}\n`;\n}\n\n// ============================================================================\n// Multi-File Generation (Split by Tier)\n// ============================================================================\n\n/**\n * Generate validators.ts - Shared validator middleware factories\n */\nfunction generateValidatorsFile(analyses: EndpointAnalysis[]): string {\n\tconst needsBody = analyses.some((a) => a.features.hasBodyValidation);\n\tconst needsQuery = analyses.some((a) => a.features.hasQueryValidation);\n\tconst needsParams = analyses.some((a) => a.features.hasParamValidation);\n\n\tif (!needsBody && !needsQuery && !needsParams) {\n\t\treturn `// No validators needed for this build\\nexport {};\\n`;\n\t}\n\n\tconst exports: string[] = [];\n\tconst factories: string[] = [];\n\n\tif (needsBody) {\n\t\texports.push('validateBody');\n\t\tfactories.push(`\nexport const validateBody = (endpoint: any) =>\n validator('json', async (value, c) => {\n if (!endpoint.input?.body) return undefined;\n const parsed = await Endpoint.validate(endpoint.input.body, value);\n if (parsed.issues) return c.json(parsed.issues, 422);\n return parsed.value;\n });`);\n\t}\n\n\tif (needsQuery) {\n\t\texports.push('validateQuery');\n\t\tfactories.push(`\nexport const validateQuery = (endpoint: any) =>\n validator('query', async (_, c) => {\n if (!endpoint.input?.query) return undefined;\n const rawQuery = Object.fromEntries(new URL(c.req.url).searchParams);\n const parsed = await Endpoint.validate(endpoint.input.query, rawQuery);\n if (parsed.issues) return c.json(parsed.issues, 422);\n return parsed.value;\n });`);\n\t}\n\n\tif (needsParams) {\n\t\texports.push('validateParams');\n\t\tfactories.push(`\nexport const validateParams = (endpoint: any) =>\n validator('param', async (params, c) => {\n if (!endpoint.input?.params) return undefined;\n const parsed = await Endpoint.validate(endpoint.input.params, params);\n if (parsed.issues) return c.json(parsed.issues, 422);\n return parsed.value;\n });`);\n\t}\n\n\treturn `/**\n * Generated validator middleware factories\n * Shared across all endpoint tiers that need validation\n */\nimport { validator } from 'hono/validator';\nimport { Endpoint } from '@geekmidas/constructs/endpoints';\n${factories.join('\\n')}\n`;\n}\n\n/**\n * Generate minimal.ts - Minimal tier handlers\n */\nfunction generateMinimalFile(\n\tanalyses: EndpointAnalysis[],\n\tendpointImports: EndpointImportInfo[],\n): string {\n\tconst minimalEndpoints = analyses.filter((a) => a.tier === 'minimal');\n\n\tif (minimalEndpoints.length === 0) {\n\t\treturn `// No minimal-tier endpoints in this build\nimport type { Hono } from 'hono';\nimport type { Logger } from '@geekmidas/logger';\n\nexport function setupMinimalEndpoints(\n _app: Hono,\n _logger: Logger,\n): void {\n // No minimal endpoints\n}\n`;\n\t}\n\n\tconst minimalExportNames = minimalEndpoints.map((a) => a.exportName);\n\tconst relevantImports = endpointImports.filter((i) =>\n\t\tminimalExportNames.includes(i.exportName),\n\t);\n\n\tconst importStatements = relevantImports\n\t\t.map((i) => `import { ${i.exportName} } from '${i.importPath}';`)\n\t\t.join('\\n');\n\n\tconst needsValidators = minimalEndpoints.some(\n\t\t(a) =>\n\t\t\ta.features.hasBodyValidation ||\n\t\t\ta.features.hasQueryValidation ||\n\t\t\ta.features.hasParamValidation,\n\t);\n\n\tconst validatorImport = needsValidators\n\t\t? generateValidatorImports(minimalEndpoints)\n\t\t: '';\n\n\tconst handlers = minimalEndpoints\n\t\t.map((a) => generateMinimalHandler(a))\n\t\t.join('\\n');\n\n\treturn `/**\n * Minimal-tier endpoint handlers (${minimalEndpoints.length} endpoints)\n * Near-raw-Hono performance - no auth, no services, no complex features\n */\nimport type { Hono } from 'hono';\nimport type { Logger } from '@geekmidas/logger';\nimport { Endpoint } from '@geekmidas/constructs/endpoints';\n${validatorImport}\n${importStatements}\n\nexport function setupMinimalEndpoints(\n app: Hono,\n logger: Logger,\n): void {\n${handlers}\n}\n`;\n}\n\n/**\n * Generate standard.ts - Standard tier handlers\n */\nfunction generateStandardFile(\n\tanalyses: EndpointAnalysis[],\n\tendpointImports: EndpointImportInfo[],\n): string {\n\tconst standardEndpoints = analyses.filter((a) => a.tier === 'standard');\n\n\tif (standardEndpoints.length === 0) {\n\t\treturn `// No standard-tier endpoints in this build\nimport type { Hono } from 'hono';\nimport type { Logger } from '@geekmidas/logger';\nimport type { ServiceDiscovery } from '@geekmidas/services';\n\nexport function setupStandardEndpoints(\n _app: Hono,\n _serviceDiscovery: ServiceDiscovery<any, any>,\n _logger: Logger,\n): void {\n // No standard endpoints\n}\n`;\n\t}\n\n\tconst standardExportNames = standardEndpoints.map((a) => a.exportName);\n\tconst relevantImports = endpointImports.filter((i) =>\n\t\tstandardExportNames.includes(i.exportName),\n\t);\n\n\tconst importStatements = relevantImports\n\t\t.map((i) => `import { ${i.exportName} } from '${i.importPath}';`)\n\t\t.join('\\n');\n\n\tconst needsValidators = standardEndpoints.some(\n\t\t(a) =>\n\t\t\ta.features.hasBodyValidation ||\n\t\t\ta.features.hasQueryValidation ||\n\t\t\ta.features.hasParamValidation,\n\t);\n\n\tconst validatorImport = needsValidators\n\t\t? generateValidatorImports(standardEndpoints)\n\t\t: '';\n\n\tconst needsEvents = standardEndpoints.some((a) => a.features.hasEvents);\n\tconst eventsImport = needsEvents\n\t\t? `import { publishConstructEvents } from '@geekmidas/constructs/endpoints';`\n\t\t: '';\n\n\tconst handlers = standardEndpoints\n\t\t.map((a) => generateStandardHandler(a))\n\t\t.join('\\n');\n\n\treturn `/**\n * Standard-tier endpoint handlers (${standardEndpoints.length} endpoints)\n * Auth and/or services enabled, but no complex features like audits/RLS\n */\nimport type { Hono } from 'hono';\nimport type { Logger } from '@geekmidas/logger';\nimport type { ServiceDiscovery } from '@geekmidas/services';\nimport { Endpoint, ResponseBuilder } from '@geekmidas/constructs/endpoints';\n${eventsImport}\n${validatorImport}\n${importStatements}\n\nexport function setupStandardEndpoints(\n app: Hono,\n serviceDiscovery: ServiceDiscovery<any, any>,\n logger: Logger,\n): void {\n${handlers}\n}\n`;\n}\n\n/**\n * Generate full.ts - Full tier handlers (uses HonoEndpoint.addRoutes)\n */\nfunction generateFullFile(\n\tanalyses: EndpointAnalysis[],\n\tendpointImports: EndpointImportInfo[],\n): string {\n\tconst fullEndpoints = analyses.filter((a) => a.tier === 'full');\n\n\tif (fullEndpoints.length === 0) {\n\t\treturn `// No full-tier endpoints in this build\nimport type { Hono } from 'hono';\nimport type { ServiceDiscovery } from '@geekmidas/services';\n\nexport function setupFullEndpoints(\n _app: Hono,\n _serviceDiscovery: ServiceDiscovery<any, any>,\n _enableOpenApi: boolean,\n): void {\n // No full endpoints\n}\n`;\n\t}\n\n\tconst fullExportNames = fullEndpoints.map((a) => a.exportName);\n\tconst relevantImports = endpointImports.filter((i) =>\n\t\tfullExportNames.includes(i.exportName),\n\t);\n\n\tconst importStatements = relevantImports\n\t\t.map((i) => `import { ${i.exportName} } from '${i.importPath}';`)\n\t\t.join('\\n');\n\n\treturn `/**\n * Full-tier endpoint handlers (${fullEndpoints.length} endpoints)\n * Complex features: audits, RLS, rate limiting\n * Uses HonoEndpoint.addRoutes for full feature support\n */\nimport type { Hono } from 'hono';\nimport type { ServiceDiscovery } from '@geekmidas/services';\nimport { HonoEndpoint } from '@geekmidas/constructs/hono';\n${importStatements}\n\nconst fullEndpoints = [${fullExportNames.join(', ')}];\n\nexport function setupFullEndpoints(\n app: Hono,\n serviceDiscovery: ServiceDiscovery<any, any>,\n enableOpenApi: boolean,\n): void {\n const openApiOptions: any = enableOpenApi ? {\n docsPath: '/__docs',\n openApiOptions: {\n title: 'API Documentation',\n version: '1.0.0',\n description: 'Generated API documentation'\n }\n } : { docsPath: false };\n\n HonoEndpoint.addRoutes(fullEndpoints as any, serviceDiscovery, app, openApiOptions);\n}\n`;\n}\n\n/**\n * Generate index.ts - Main entry point\n */\nfunction generateIndexFile(analyses: EndpointAnalysis[]): string {\n\tconst minimalCount = analyses.filter((a) => a.tier === 'minimal').length;\n\tconst standardCount = analyses.filter((a) => a.tier === 'standard').length;\n\tconst fullCount = analyses.filter((a) => a.tier === 'full').length;\n\n\treturn `/**\n * Generated optimized endpoints\n *\n * Build-time optimization tiers:\n * - minimal: ${minimalCount} endpoints (near-raw-Hono)\n * - standard: ${standardCount} endpoints (auth/services)\n * - full: ${fullCount} endpoints (audits/rls/rate-limit)\n */\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Logger } from '@geekmidas/logger';\nimport type { Hono } from 'hono';\nimport { ServiceDiscovery } from '@geekmidas/services';\nimport { setupMinimalEndpoints } from './minimal.js';\nimport { setupStandardEndpoints } from './standard.js';\nimport { setupFullEndpoints } from './full.js';\n\nexport async function setupEndpoints(\n app: Hono,\n envParser: EnvironmentParser<any>,\n logger: Logger,\n enableOpenApi: boolean = false,\n): Promise<void> {\n const serviceDiscovery = ServiceDiscovery.getInstance(envParser);\n\n // Minimal handlers (${minimalCount} endpoints) - near-raw-Hono performance\n setupMinimalEndpoints(app, logger);\n\n // Standard handlers (${standardCount} endpoints) - auth/services\n setupStandardEndpoints(app, serviceDiscovery, logger);\n\n // Full handlers (${fullCount} endpoints) - audits/rls/rate-limit\n setupFullEndpoints(app, serviceDiscovery, enableOpenApi);\n\n // Add Swagger UI if OpenAPI is enabled\n if (enableOpenApi) {\n try {\n const { swaggerUI } = await import('@hono/swagger-ui');\n app.get('/__docs/ui', swaggerUI({ url: '/__docs' }));\n } catch {\n // @hono/swagger-ui not installed, skip Swagger UI\n }\n }\n}\n`;\n}\n\n/**\n * Generate validator imports based on what's needed\n */\nfunction generateValidatorImports(analyses: EndpointAnalysis[]): string {\n\tconst needsBody = analyses.some((a) => a.features.hasBodyValidation);\n\tconst needsQuery = analyses.some((a) => a.features.hasQueryValidation);\n\tconst needsParams = analyses.some((a) => a.features.hasParamValidation);\n\n\tconst imports: string[] = [];\n\tif (needsBody) imports.push('validateBody');\n\tif (needsQuery) imports.push('validateQuery');\n\tif (needsParams) imports.push('validateParams');\n\n\tif (imports.length === 0) return '';\n\treturn `import { ${imports.join(', ')} } from './validators.js';`;\n}\n\n/**\n * Generate all endpoint files split by tier (flat structure)\n */\nexport function generateEndpointFilesByTier(\n\tanalyses: EndpointAnalysis[],\n\tendpointImports: EndpointImportInfo[],\n): GeneratedEndpointFiles {\n\treturn {\n\t\t'validators.ts': generateValidatorsFile(analyses),\n\t\t'minimal.ts': generateMinimalFile(analyses, endpointImports),\n\t\t'standard.ts': generateStandardFile(analyses, endpointImports),\n\t\t'full.ts': generateFullFile(analyses, endpointImports),\n\t\t'index.ts': generateIndexFile(analyses),\n\t};\n}\n\n// ============================================================================\n// Per-Endpoint File Generation (Nested Folder Structure)\n// ============================================================================\n\n/**\n * Generate a standalone minimal endpoint file\n */\nfunction generateMinimalEndpointFile(\n\tanalysis: EndpointAnalysis,\n\tendpointImport: EndpointImportInfo,\n): string {\n\tconst { exportName, features } = analysis;\n\n\tconst needsValidators =\n\t\tfeatures.hasBodyValidation ||\n\t\tfeatures.hasQueryValidation ||\n\t\tfeatures.hasParamValidation;\n\n\tconst validatorImport = needsValidators\n\t\t? generateValidatorImportsForEndpoint(analysis)\n\t\t: '';\n\n\tconst handler = generateMinimalHandler(analysis);\n\n\treturn `/**\n * Minimal endpoint: ${analysis.route} (${analysis.method})\n * Near-raw-Hono performance\n */\nimport type { Hono } from 'hono';\nimport type { Logger } from '@geekmidas/logger';\nimport { Endpoint } from '@geekmidas/constructs/endpoints';\n${validatorImport}\nimport { ${exportName} } from '${endpointImport.importPath}';\n\nexport function setup${capitalize(exportName)}(\n app: Hono,\n logger: Logger,\n): void {\n${handler}\n}\n`;\n}\n\n/**\n * Generate a standalone standard endpoint file\n */\nfunction generateStandardEndpointFile(\n\tanalysis: EndpointAnalysis,\n\tendpointImport: EndpointImportInfo,\n): string {\n\tconst { exportName, features } = analysis;\n\n\tconst needsValidators =\n\t\tfeatures.hasBodyValidation ||\n\t\tfeatures.hasQueryValidation ||\n\t\tfeatures.hasParamValidation;\n\n\tconst validatorImport = needsValidators\n\t\t? generateValidatorImportsForEndpoint(analysis)\n\t\t: '';\n\n\tconst eventsImport = features.hasEvents\n\t\t? `import { publishConstructEvents } from '@geekmidas/constructs/endpoints';`\n\t\t: '';\n\n\tconst handler = generateStandardHandler(analysis);\n\n\treturn `/**\n * Standard endpoint: ${analysis.route} (${analysis.method})\n * Auth and/or services enabled\n */\nimport type { Hono } from 'hono';\nimport type { Logger } from '@geekmidas/logger';\nimport type { ServiceDiscovery } from '@geekmidas/services';\nimport { Endpoint, ResponseBuilder } from '@geekmidas/constructs/endpoints';\n${eventsImport}\n${validatorImport}\nimport { ${exportName} } from '${endpointImport.importPath}';\n\nexport function setup${capitalize(exportName)}(\n app: Hono,\n serviceDiscovery: ServiceDiscovery<any, any>,\n logger: Logger,\n): void {\n${handler}\n}\n`;\n}\n\n/**\n * Generate a standalone full endpoint file\n */\nfunction generateFullEndpointFile(\n\tanalysis: EndpointAnalysis,\n\tendpointImport: EndpointImportInfo,\n): string {\n\tconst { exportName } = analysis;\n\n\treturn `/**\n * Full endpoint: ${analysis.route} (${analysis.method})\n * Complex features: audits, RLS, rate limiting\n */\nimport type { Hono } from 'hono';\nimport type { ServiceDiscovery } from '@geekmidas/services';\nimport { HonoEndpoint } from '@geekmidas/constructs/hono';\nimport { ${exportName} } from '${endpointImport.importPath}';\n\nexport function setup${capitalize(exportName)}(\n app: Hono,\n serviceDiscovery: ServiceDiscovery<any, any>,\n openApiOptions: any,\n): void {\n HonoEndpoint.addRoutes([${exportName}] as any, serviceDiscovery, app, openApiOptions);\n}\n`;\n}\n\n/**\n * Generate tier index file that imports and calls all endpoint setup functions\n */\nfunction generateTierIndexFile(\n\ttier: 'minimal' | 'standard' | 'full',\n\tanalyses: EndpointAnalysis[],\n): string {\n\tconst tierEndpoints = analyses.filter((a) => a.tier === tier);\n\n\tif (tierEndpoints.length === 0) {\n\t\tif (tier === 'minimal') {\n\t\t\treturn `// No minimal-tier endpoints in this build\nimport type { Hono } from 'hono';\nimport type { Logger } from '@geekmidas/logger';\n\nexport function setupMinimalEndpoints(\n _app: Hono,\n _logger: Logger,\n): void {\n // No minimal endpoints\n}\n`;\n\t\t}\n\t\tif (tier === 'standard') {\n\t\t\treturn `// No standard-tier endpoints in this build\nimport type { Hono } from 'hono';\nimport type { Logger } from '@geekmidas/logger';\nimport type { ServiceDiscovery } from '@geekmidas/services';\n\nexport function setupStandardEndpoints(\n _app: Hono,\n _serviceDiscovery: ServiceDiscovery<any, any>,\n _logger: Logger,\n): void {\n // No standard endpoints\n}\n`;\n\t\t}\n\t\t// full\n\t\treturn `// No full-tier endpoints in this build\nimport type { Hono } from 'hono';\nimport type { ServiceDiscovery } from '@geekmidas/services';\n\nexport function setupFullEndpoints(\n _app: Hono,\n _serviceDiscovery: ServiceDiscovery<any, any>,\n _enableOpenApi: boolean,\n): void {\n // No full endpoints\n}\n`;\n\t}\n\n\tconst imports = tierEndpoints\n\t\t.map(\n\t\t\t(a) =>\n\t\t\t\t`import { setup${capitalize(a.exportName)} } from './${a.exportName}.js';`,\n\t\t)\n\t\t.join('\\n');\n\n\tif (tier === 'minimal') {\n\t\tconst calls = tierEndpoints\n\t\t\t.map((a) => ` setup${capitalize(a.exportName)}(app, logger);`)\n\t\t\t.join('\\n');\n\n\t\treturn `/**\n * Minimal-tier endpoint index (${tierEndpoints.length} endpoints)\n * Near-raw-Hono performance\n */\nimport type { Hono } from 'hono';\nimport type { Logger } from '@geekmidas/logger';\n${imports}\n\nexport function setupMinimalEndpoints(\n app: Hono,\n logger: Logger,\n): void {\n${calls}\n}\n`;\n\t}\n\n\tif (tier === 'standard') {\n\t\tconst calls = tierEndpoints\n\t\t\t.map(\n\t\t\t\t(a) =>\n\t\t\t\t\t` setup${capitalize(a.exportName)}(app, serviceDiscovery, logger);`,\n\t\t\t)\n\t\t\t.join('\\n');\n\n\t\treturn `/**\n * Standard-tier endpoint index (${tierEndpoints.length} endpoints)\n * Auth and/or services enabled\n */\nimport type { Hono } from 'hono';\nimport type { Logger } from '@geekmidas/logger';\nimport type { ServiceDiscovery } from '@geekmidas/services';\n${imports}\n\nexport function setupStandardEndpoints(\n app: Hono,\n serviceDiscovery: ServiceDiscovery<any, any>,\n logger: Logger,\n): void {\n${calls}\n}\n`;\n\t}\n\n\t// full\n\tconst calls = tierEndpoints\n\t\t.map(\n\t\t\t(a) =>\n\t\t\t\t` setup${capitalize(a.exportName)}(app, serviceDiscovery, openApiOptions);`,\n\t\t)\n\t\t.join('\\n');\n\n\treturn `/**\n * Full-tier endpoint index (${tierEndpoints.length} endpoints)\n * Complex features: audits, RLS, rate limiting\n */\nimport type { Hono } from 'hono';\nimport type { ServiceDiscovery } from '@geekmidas/services';\n${imports}\n\nexport function setupFullEndpoints(\n app: Hono,\n serviceDiscovery: ServiceDiscovery<any, any>,\n enableOpenApi: boolean,\n): void {\n const openApiOptions: any = enableOpenApi ? {\n docsPath: '/__docs',\n openApiOptions: {\n title: 'API Documentation',\n version: '1.0.0',\n description: 'Generated API documentation'\n }\n } : { docsPath: false };\n\n${calls}\n}\n`;\n}\n\n/**\n * Generate validator imports for a single endpoint\n */\nfunction generateValidatorImportsForEndpoint(\n\tanalysis: EndpointAnalysis,\n): string {\n\tconst imports: string[] = [];\n\tif (analysis.features.hasBodyValidation) imports.push('validateBody');\n\tif (analysis.features.hasQueryValidation) imports.push('validateQuery');\n\tif (analysis.features.hasParamValidation) imports.push('validateParams');\n\n\tif (imports.length === 0) return '';\n\treturn `import { ${imports.join(', ')} } from '../validators.js';`;\n}\n\n/**\n * Capitalize first letter\n */\nfunction capitalize(str: string): string {\n\treturn str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n/**\n * Generate all endpoint files with nested folder structure (per-endpoint files)\n */\nexport function generateEndpointFilesNested(\n\tanalyses: EndpointAnalysis[],\n\tendpointImports: EndpointImportInfo[],\n): GeneratedEndpointFilesNested {\n\tconst files: GeneratedEndpointFilesNested = {\n\t\t'validators.ts': generateValidatorsFile(analyses),\n\t\t'minimal/index.ts': generateTierIndexFile('minimal', analyses),\n\t\t'standard/index.ts': generateTierIndexFile('standard', analyses),\n\t\t'full/index.ts': generateTierIndexFile('full', analyses),\n\t\t'index.ts': generateNestedIndexFile(analyses),\n\t};\n\n\t// Generate individual endpoint files\n\tfor (const analysis of analyses) {\n\t\tconst endpointImport = endpointImports.find(\n\t\t\t(i) => i.exportName === analysis.exportName,\n\t\t);\n\t\tif (!endpointImport) continue;\n\n\t\tconst fileName = `${analysis.tier}/${analysis.exportName}.ts`;\n\n\t\tswitch (analysis.tier) {\n\t\t\tcase 'minimal':\n\t\t\t\tfiles[fileName] = generateMinimalEndpointFile(analysis, endpointImport);\n\t\t\t\tbreak;\n\t\t\tcase 'standard':\n\t\t\t\tfiles[fileName] = generateStandardEndpointFile(\n\t\t\t\t\tanalysis,\n\t\t\t\t\tendpointImport,\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase 'full':\n\t\t\t\tfiles[fileName] = generateFullEndpointFile(analysis, endpointImport);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn files;\n}\n\n/**\n * Generate index.ts for nested structure\n */\nfunction generateNestedIndexFile(analyses: EndpointAnalysis[]): string {\n\tconst minimalCount = analyses.filter((a) => a.tier === 'minimal').length;\n\tconst standardCount = analyses.filter((a) => a.tier === 'standard').length;\n\tconst fullCount = analyses.filter((a) => a.tier === 'full').length;\n\n\treturn `/**\n * Generated optimized endpoints\n *\n * Build-time optimization tiers:\n * - minimal: ${minimalCount} endpoints (near-raw-Hono)\n * - standard: ${standardCount} endpoints (auth/services)\n * - full: ${fullCount} endpoints (audits/rls/rate-limit)\n */\nimport type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Logger } from '@geekmidas/logger';\nimport type { Hono } from 'hono';\nimport { ServiceDiscovery } from '@geekmidas/services';\nimport { setupMinimalEndpoints } from './minimal/index.js';\nimport { setupStandardEndpoints } from './standard/index.js';\nimport { setupFullEndpoints } from './full/index.js';\n\nexport async function setupEndpoints(\n app: Hono,\n envParser: EnvironmentParser<any>,\n logger: Logger,\n enableOpenApi: boolean = false,\n): Promise<void> {\n const serviceDiscovery = ServiceDiscovery.getInstance(envParser);\n\n // Minimal handlers (${minimalCount} endpoints) - near-raw-Hono performance\n setupMinimalEndpoints(app, logger);\n\n // Standard handlers (${standardCount} endpoints) - auth/services\n setupStandardEndpoints(app, serviceDiscovery, logger);\n\n // Full handlers (${fullCount} endpoints) - audits/rls/rate-limit\n setupFullEndpoints(app, serviceDiscovery, enableOpenApi);\n\n // Add Swagger UI if OpenAPI is enabled\n if (enableOpenApi) {\n try {\n const { swaggerUI } = await import('@hono/swagger-ui');\n app.get('/__docs/ui', swaggerUI({ url: '/__docs' }));\n } catch {\n // @hono/swagger-ui not installed, skip Swagger UI\n }\n }\n}\n`;\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative } from 'node:path';\nimport { Endpoint } from '@geekmidas/constructs/endpoints';\nimport {\n\tanalyzeEndpoint,\n\ttype EndpointAnalysis,\n\tsummarizeAnalysis,\n} from '../build/endpoint-analyzer';\nimport {\n\ttype EndpointImportInfo,\n\tgenerateEndpointFilesNested,\n} from '../build/handler-templates';\nimport type { BuildContext } from '../build/types';\nimport type { LegacyProvider, RouteInfo } from '../types';\nimport type { StorageDrivers } from './drivers';\nimport {\n\tConstructGenerator,\n\ttype GeneratedConstruct,\n\ttype GeneratorOptions,\n} from './Generator';\n\n/**\n * How a generated entry gets its logger and environment parser.\n *\n * From the surface it serves, when there is one: the surface holds both as\n * *objects*, and discovery already recorded which module declares it, so the\n * entry imports that module and reads them off it.\n *\n * The alternative — the shape this replaces — was naming both in config as\n * module paths, `envParser: './config/env#envParser'`, and printing an import\n * for each. Two strings per app, checked by nothing, describing files the\n * surface was already holding the contents of.\n *\n * Falls back to the configured paths for an app that declares no surface.\n */\nexport function runtimeFor(\n\tcontext: BuildContext,\n\tfromDir: string,\n\t/**\n\t * The construct that owns the thing being generated.\n\t *\n\t * An endpoint's surface, or the worker that handed out the cron factory.\n\t * Omitted for the app entry itself, which belongs to the primary surface.\n\t */\n\towner?: string,\n): { imports: string; bindings: string } {\n\tconst module = owner\n\t\t? (context.owners?.[owner] ?? context.surface?.module)\n\t\t: context.surface?.module;\n\n\tif (module) {\n\t\tconst specifier = importSpecifier(fromDir, module.specifier);\n\n\t\t// Bound to the same names the rest of the entry already uses, so what\n\t\t// changes is where they come from and nothing else.\n\t\treturn {\n\t\t\timports: `import { ${module.exportName} as __surface } from '${specifier}';`,\n\t\t\tbindings: [\n\t\t\t\t'// What this construct was declared with. Objects, not module paths:',\n\t\t\t\t'// the entry imports the owner, so there is nothing to print.',\n\t\t\t\t'const envParser = __surface.envParser;',\n\t\t\t\t'const logger = __surface.logger;',\n\t\t\t].join('\\n'),\n\t\t};\n\t}\n\n\t// No fallback. Every runnable comes from a factory a `RestApi` or a `Worker`\n\t// handed out, and that construct holds the objects — so there is no longer a\n\t// case where the only thing we know about a logger is a module path somebody\n\t// typed into config.\n\tthrow new Error(\n\t\t`No owner to take a logger and an environment parser from${\n\t\t\towner ? ` for \"${owner}\"` : ''\n\t\t}. Build it from a \\`RestApi\\` or a \\`Worker\\`.`,\n\t);\n}\n\n/** A relative specifier ESM will accept: always prefixed, never bare. */\nfunction importSpecifier(fromDir: string, target: string): string {\n\tconst rel = relative(fromDir, target).replace(/\\.ts$/, '.js');\n\n\treturn rel.startsWith('.') ? rel : `./${rel}`;\n}\n\n/**\n * CORS for a surface, derived rather than hand-written.\n *\n * Who may call a surface is already in the graph — every construct that\n * declared an edge to it — and reaches the process as one comma-separated\n * value. An application that maintained this by hand maintained a list that was\n * wrong in two directions at once: it trusted origins nothing was serving, and\n * it could not name hosts a deploy had not chosen yet.\n *\n * Only the parts a graph cannot answer are configurable, and every one has a\n * default: a surface that says nothing about CORS still gets it.\n */\nexport function corsFor(surface: BuildContext['surface']): {\n\timports: string;\n\tsetup: string;\n} {\n\tif (!surface) return { imports: '', setup: '' };\n\n\tconst cors = surface.cors ?? {};\n\tconst allowHeaders = [\n\t\t'content-type',\n\t\t'authorization',\n\t\t...(cors.allowHeaders ?? []),\n\t];\n\n\treturn {\n\t\timports: `import { cors } from 'hono/cors';`,\n\t\tsetup: `\n // Who may call ${surface.id}, read off the graph rather than listed here.\n {\n const { origins } = envParser\n .create((get) => ({\n origins: get('${surface.trustedOriginsKey}')\n .string()\n .default('')\n .transform((value: string) =>\n value.split(',').map((origin) => origin.trim()).filter(Boolean),\n ),\n }))\n .parse();\n\n honoApp.use('*', cors({\n origin: origins,\n allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],\n allowHeaders: ${JSON.stringify(allowHeaders)},${\n\t\t\t\tcors.exposeHeaders\n\t\t\t\t\t? `\\n exposeHeaders: ${JSON.stringify(cors.exposeHeaders)},`\n\t\t\t\t\t: ''\n\t\t\t}\n credentials: ${cors.credentials ?? true},\n maxAge: ${cors.maxAge ?? 86400},\n }));\n }\n`,\n\t};\n}\n\nexport class EndpointGenerator extends ConstructGenerator<\n\tEndpoint<any, any, any, any, any, any, any, any, any, any, any, any>,\n\tRouteInfo[]\n> {\n\tisConstruct(\n\t\tvalue: any,\n\t): value is Endpoint<\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany,\n\t\tany\n\t> {\n\t\treturn Endpoint.isEndpoint(value);\n\t}\n\n\tasync build(\n\t\tcontext: BuildContext,\n\t\tconstructs: GeneratedConstruct<\n\t\t\tEndpoint<any, any, any, any, any, any, any, any, any, any, any, any>\n\t\t>[],\n\t\toutputDir: string,\n\t\toptions?: GeneratorOptions,\n\t): Promise<RouteInfo[]> {\n\t\tconst provider = options?.provider || 'aws-apigatewayv2';\n\t\tconst enableOpenApi = options?.enableOpenApi || false;\n\t\tconst logger = console;\n\t\tconst routes: RouteInfo[] = [];\n\n\t\tif (constructs.length === 0) {\n\t\t\treturn routes;\n\t\t}\n\n\t\tif (provider === 'server') {\n\t\t\t// Generate endpoints.ts and app.ts\n\t\t\tawait this.generateEndpointsFile(outputDir, constructs, context);\n\t\t\tconst appFile = await this.generateAppFile(outputDir, context);\n\n\t\t\troutes.push({\n\t\t\t\tpath: '*',\n\t\t\t\tmethod: 'ALL',\n\t\t\t\thandler: relative(process.cwd(), appFile),\n\t\t\t\tauthorizer: 'none',\n\t\t\t});\n\n\t\t\tlogger.log(\n\t\t\t\t`Generated server with ${constructs.length} endpoints${enableOpenApi ? ' (OpenAPI enabled)' : ''}`,\n\t\t\t);\n\t\t} else if (provider === 'aws-lambda') {\n\t\t\t// For aws-lambda, create routes subdirectory\n\t\t\tconst routesDir = join(outputDir, 'routes');\n\t\t\tawait mkdir(routesDir, { recursive: true });\n\n\t\t\t// Generate individual handlers for API Gateway routes\n\t\t\tfor (const { key, construct, path } of constructs) {\n\t\t\t\tconst handlerFile = await this.generateHandlerFile(\n\t\t\t\t\troutesDir,\n\t\t\t\t\tpath.relative,\n\t\t\t\t\tkey,\n\t\t\t\t\t'aws-apigatewayv2',\n\t\t\t\t\tconstruct,\n\t\t\t\t\tcontext,\n\t\t\t\t);\n\n\t\t\t\tconst routeInfo: RouteInfo = {\n\t\t\t\t\tpath: construct._path,\n\t\t\t\t\tmethod: construct.method,\n\t\t\t\t\thandler: relative(process.cwd(), handlerFile).replace(\n\t\t\t\t\t\t/\\.ts$/,\n\t\t\t\t\t\t'.handler',\n\t\t\t\t\t),\n\t\t\t\t\ttimeout: construct.timeout,\n\t\t\t\t\tmemorySize: construct.memorySize,\n\t\t\t\t\tenvironment: await construct.getEnvironment({\n\t\t\t\t\t\tmarkOptional: context.markOptional,\n\t\t\t\t\t}),\n\t\t\t\t\tdependencies: construct.constructs,\n\t\t\t\t\tauthorizer: construct.authorizer?.name ?? 'none',\n\t\t\t\t};\n\n\t\t\t\troutes.push(routeInfo);\n\t\t\t\tlogger.log(\n\t\t\t\t\t`Generated handler for ${routeInfo.method} ${routeInfo.path}`,\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\t// Generate individual handler files for AWS API Gateway providers\n\t\t\tfor (const { key, construct, path } of constructs) {\n\t\t\t\tconst handlerFile = await this.generateHandlerFile(\n\t\t\t\t\toutputDir,\n\t\t\t\t\tpath.relative,\n\t\t\t\t\tkey,\n\t\t\t\t\tprovider,\n\t\t\t\t\tconstruct,\n\t\t\t\t\tcontext,\n\t\t\t\t);\n\n\t\t\t\tconst routeInfo: RouteInfo = {\n\t\t\t\t\tpath: construct._path,\n\t\t\t\t\tmethod: construct.method,\n\t\t\t\t\thandler: relative(process.cwd(), handlerFile).replace(\n\t\t\t\t\t\t/\\.ts$/,\n\t\t\t\t\t\t'.handler',\n\t\t\t\t\t),\n\t\t\t\t\ttimeout: construct.timeout,\n\t\t\t\t\tmemorySize: construct.memorySize,\n\t\t\t\t\tenvironment: await construct.getEnvironment({\n\t\t\t\t\t\tmarkOptional: context.markOptional,\n\t\t\t\t\t}),\n\t\t\t\t\tdependencies: construct.constructs,\n\t\t\t\t\tauthorizer: construct.authorizer?.name ?? 'none',\n\t\t\t\t};\n\n\t\t\t\troutes.push(routeInfo);\n\t\t\t\tlogger.log(\n\t\t\t\t\t`Generated handler for ${routeInfo.method} ${routeInfo.path}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn routes;\n\t}\n\n\tprivate async generateHandlerFile(\n\t\toutputDir: string,\n\t\tsourceFile: string,\n\t\texportName: string,\n\t\tprovider: LegacyProvider,\n\t\t_endpoint: Endpoint<\n\t\t\tany,\n\t\t\tany,\n\t\t\tany,\n\t\t\tany,\n\t\t\tany,\n\t\t\tany,\n\t\t\tany,\n\t\t\tany,\n\t\t\tany,\n\t\t\tany,\n\t\t\tany,\n\t\t\tany\n\t\t>,\n\t\tcontext: BuildContext,\n\t): Promise<string> {\n\t\tconst handlerFileName = `${exportName}.ts`;\n\t\tconst handlerPath = join(outputDir, handlerFileName);\n\n\t\tconst relativePath = relative(dirname(handlerPath), sourceFile);\n\t\tconst importPath = relativePath.replace(/\\.ts$/, '.js');\n\n\t\tlet content: string;\n\n\t\tswitch (provider) {\n\t\t\tcase 'aws-apigatewayv1':\n\t\t\t\tcontent = this.generateAWSApiGatewayV1Handler(\n\t\t\t\t\timportPath,\n\t\t\t\t\texportName,\n\t\t\t\t\tcontext.storageDrivers,\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase 'aws-apigatewayv2':\n\t\t\t\tcontent = this.generateAWSApiGatewayV2Handler(\n\t\t\t\t\timportPath,\n\t\t\t\t\texportName,\n\t\t\t\t\tcontext.storageDrivers,\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\tcase 'server':\n\t\t\t\tcontent = this.generateServerHandler(importPath, exportName);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unsupported provider: ${provider}`);\n\t\t}\n\n\t\tawait writeFile(handlerPath, content);\n\t\treturn handlerPath;\n\t}\n\n\tprivate async generateEndpointsFile(\n\t\toutputDir: string,\n\t\tendpoints: GeneratedConstruct<\n\t\t\tEndpoint<any, any, any, any, any, any, any, any, any, any, any, any>\n\t\t>[],\n\t\tcontext: BuildContext,\n\t): Promise<string> {\n\t\tconst endpointsFileName = 'endpoints.ts';\n\t\tconst endpointsPath = join(outputDir, endpointsFileName);\n\n\t\t// Group imports by file\n\t\tconst importsByFile = new Map<string, string[]>();\n\n\t\tfor (const { path, key } of endpoints) {\n\t\t\tconst relativePath = relative(dirname(endpointsPath), path.relative);\n\t\t\tconst importPath = relativePath.replace(/\\.ts$/, '.js');\n\n\t\t\tif (!importsByFile.has(importPath)) {\n\t\t\t\timportsByFile.set(importPath, []);\n\t\t\t}\n\t\t\timportsByFile.get(importPath)?.push(key);\n\t\t}\n\n\t\t// Generate import statements for endpoints\n\t\tconst endpointImports = Array.from(importsByFile.entries())\n\t\t\t.map(\n\t\t\t\t([importPath, exports]) =>\n\t\t\t\t\t`import { ${exports.join(', ')} } from '${importPath}';`,\n\t\t\t)\n\t\t\t.join('\\n');\n\n\t\tconst allExportNames = endpoints.map(({ key }) => key);\n\n\t\t// Check if we should use optimized handler generation\n\t\tif (context.production?.enabled && context.production.optimizedHandlers) {\n\t\t\treturn this.generateOptimizedEndpointsFile(\n\t\t\t\tendpointsPath,\n\t\t\t\tendpoints,\n\t\t\t\tendpointImports,\n\t\t\t\tallExportNames,\n\t\t\t);\n\t\t}\n\n\t\t// Standard generation (development or optimizedHandlers: false)\n\t\tconst content = `import type { EnvironmentParser } from '@geekmidas/envkit';\nimport type { Logger } from '@geekmidas/logger';\nimport { HonoEndpoint } from '@geekmidas/constructs/hono';\nimport { Endpoint } from '@geekmidas/constructs/endpoints';\nimport { ServiceDiscovery } from '@geekmidas/services';\nimport type { Hono } from 'hono';\n${endpointImports}\n\nexport const endpoints: Endpoint<any, any, any, any, any, any, any, any, any, any, any, any>[] = [\n ${allExportNames.join(',\\n ')}\n];\n\nexport async function setupEndpoints(\n app: Hono,\n envParser: EnvironmentParser<any>,\n logger: Logger,\n enableOpenApi: boolean = true,\n): Promise<void> {\n const serviceDiscovery = ServiceDiscovery.getInstance(envParser);\n\n // Configure OpenAPI options based on enableOpenApi flag\n const openApiOptions: any = enableOpenApi ? {\n docsPath: '/__docs',\n openApiOptions: {\n title: 'API Documentation',\n version: '1.0.0',\n description: 'Generated API documentation'\n }\n } : { docsPath: false };\n\n HonoEndpoint.addRoutes(endpoints, serviceDiscovery, app, openApiOptions);\n\n // Add Swagger UI if OpenAPI is enabled\n if (enableOpenApi) {\n try {\n const { swaggerUI } = await import('@hono/swagger-ui');\n app.get('/__docs/ui', swaggerUI({ url: '/__docs' }));\n } catch {\n // @hono/swagger-ui not installed, skip Swagger UI\n }\n }\n}\n`;\n\n\t\tawait writeFile(endpointsPath, content);\n\n\t\treturn endpointsPath;\n\t}\n\n\t/**\n\t * Generate optimized endpoints files with nested folder structure (per-endpoint files)\n\t */\n\tprivate async generateOptimizedEndpointsFile(\n\t\tendpointsPath: string,\n\t\tendpoints: GeneratedConstruct<\n\t\t\tEndpoint<any, any, any, any, any, any, any, any, any, any, any, any>\n\t\t>[],\n\t\t_endpointImports: string,\n\t\t_allExportNames: string[],\n\t): Promise<string> {\n\t\tconst logger = console;\n\t\tconst outputDir = dirname(endpointsPath);\n\n\t\t// Create endpoints subdirectory with tier folders\n\t\tconst endpointsDir = join(outputDir, 'endpoints');\n\t\tawait mkdir(join(endpointsDir, 'minimal'), { recursive: true });\n\t\tawait mkdir(join(endpointsDir, 'standard'), { recursive: true });\n\t\tawait mkdir(join(endpointsDir, 'full'), { recursive: true });\n\n\t\t// Analyze each endpoint\n\t\tconst analyses: EndpointAnalysis[] = endpoints.map(({ key, construct }) =>\n\t\t\tanalyzeEndpoint(construct, key),\n\t\t);\n\n\t\t// Build endpoint import info with correct relative paths from each tier folder\n\t\t// Use paths relative to the tier folder (e.g., endpoints/standard/)\n\t\tconst endpointImports: EndpointImportInfo[] = endpoints.map(\n\t\t\t({ key, path }) => {\n\t\t\t\t// Calculate relative path from tier folder (one level deeper than endpointsDir)\n\t\t\t\tconst tierDir = join(endpointsDir, 'standard'); // Use any tier as reference - same depth\n\t\t\t\tconst relativePath = relative(tierDir, path.relative);\n\t\t\t\tconst importPath = relativePath.replace(/\\.ts$/, '.js');\n\t\t\t\treturn { exportName: key, importPath };\n\t\t\t},\n\t\t);\n\n\t\t// Log analysis summary\n\t\tconst summary = summarizeAnalysis(analyses);\n\t\tlogger.log(`\\n📊 Endpoint Analysis:`);\n\t\tlogger.log(` Total: ${summary.total} endpoints`);\n\t\tlogger.log(\n\t\t\t` - Minimal (near-raw-Hono): ${summary.byTier.minimal} endpoints`,\n\t\t);\n\t\tlogger.log(\n\t\t\t` - Standard (auth/services): ${summary.byTier.standard} endpoints`,\n\t\t);\n\t\tlogger.log(\n\t\t\t` - Full (audits/rls/rate-limit): ${summary.byTier.full} endpoints`,\n\t\t);\n\n\t\t// Generate files with nested structure (per-endpoint files)\n\t\tconst files = generateEndpointFilesNested(analyses, endpointImports);\n\n\t\t// Write each file, creating directories as needed\n\t\tfor (const [filename, content] of Object.entries(files)) {\n\t\t\tconst filePath = join(endpointsDir, filename);\n\t\t\tawait mkdir(dirname(filePath), { recursive: true });\n\t\t\tawait writeFile(filePath, content);\n\t\t}\n\n\t\t// Count files by type\n\t\tconst endpointFiles = Object.keys(files).filter(\n\t\t\t(f) => !f.endsWith('index.ts') && !f.endsWith('validators.ts'),\n\t\t).length;\n\t\tconst indexFiles = Object.keys(files).filter((f) =>\n\t\t\tf.endsWith('index.ts'),\n\t\t).length;\n\n\t\tlogger.log(\n\t\t\t` Generated ${endpointFiles} endpoint files + ${indexFiles} index files + validators.ts`,\n\t\t);\n\n\t\t// Return path to index file\n\t\treturn join(endpointsDir, 'index.ts');\n\t}\n\n\tprivate async generateAppFile(\n\t\toutputDir: string,\n\t\tcontext: BuildContext,\n\t): Promise<string> {\n\t\t// Use production generator if in production mode\n\t\tif (context.production?.enabled) {\n\t\t\treturn this.generateProductionAppFile(outputDir, context);\n\t\t}\n\n\t\tconst appFileName = 'app.ts';\n\t\tconst appPath = join(outputDir, appFileName);\n\n\t\t// The logger and the env parser, from the surface that already holds them.\n\t\tconst runtime = runtimeFor(context, dirname(appPath));\n\n\t\t// Generate telescope imports and setup if enabled\n\t\tconst telescopeEnabled = context.telescope?.enabled;\n\t\tconst telescopeWebSocketEnabled = context.telescope?.websocket;\n\t\tconst usesExternalTelescope = !!context.telescope?.telescopePath;\n\n\t\t// Generate studio imports and setup if enabled\n\t\tconst studioEnabled = context.studio?.enabled;\n\t\tconst usesExternalStudio = !!context.studio?.studioPath;\n\t\t// Studio from the declared database rather than from a module someone\n\t\t// wrote to resolve one. Every input it takes is derivable: the client\n\t\t// from the construct, the rest defaults.\n\t\tconst studioFromDatabase =\n\t\t\t!usesExternalStudio && !!context.studio?.database;\n\n\t\t// Generate imports based on whether telescope is external or inline\n\t\tconst telescopeFromSurface =\n\t\t\t!!context.surface?.module && !usesExternalTelescope;\n\n\t\tlet telescopeImports = '';\n\t\tif (telescopeEnabled) {\n\t\t\tif (telescopeFromSurface) {\n\t\t\t\t// The entry already imports the surface, which may or may not have\n\t\t\t\t// been given a Telescope — an object, so only the running entry can\n\t\t\t\t// tell. The inline one stands in when it was not.\n\t\t\t\ttelescopeImports = `import { Telescope, InMemoryStorage } from '@geekmidas/telescope';\nimport { createMiddleware, createUI } from '@geekmidas/telescope/hono';`;\n\t\t\t} else if (usesExternalTelescope) {\n\t\t\t\tconst relativeTelescopePath = relative(\n\t\t\t\t\tdirname(appPath),\n\t\t\t\t\tcontext.telescope?.telescopePath!,\n\t\t\t\t);\n\t\t\t\ttelescopeImports = `import ${context.telescope?.telescopeImportPattern} from '${relativeTelescopePath}';\nimport { createMiddleware, createUI } from '@geekmidas/telescope/hono';`;\n\t\t\t} else {\n\t\t\t\ttelescopeImports = `import { Telescope, InMemoryStorage } from '@geekmidas/telescope';\nimport { createMiddleware, createUI } from '@geekmidas/telescope/hono';`;\n\t\t\t}\n\t\t}\n\n\t\t// Generate imports for studio\n\t\tlet studioImports = '';\n\t\tif (studioEnabled) {\n\t\t\tif (usesExternalStudio) {\n\t\t\t\tconst relativeStudioPath = relative(\n\t\t\t\t\tdirname(appPath),\n\t\t\t\t\tcontext.studio?.studioPath!,\n\t\t\t\t);\n\t\t\t\tstudioImports = `import ${context.studio?.studioImportPattern} from '${relativeStudioPath}';\nimport { createStudioApp } from '@geekmidas/studio/server/hono';`;\n\t\t\t} else if (studioFromDatabase) {\n\t\t\t\tconst dbSpecifier = importSpecifier(\n\t\t\t\t\tdirname(appPath),\n\t\t\t\t\tcontext.studio!.database!.specifier,\n\t\t\t\t);\n\t\t\t\tstudioImports = `import { snifferContext } from '@geekmidas/constructs';\nimport { Direction, InMemoryMonitoringStorage, Studio } from '@geekmidas/studio';\nimport { ${context.studio!.database!.exportName} as __studioDb } from '${dbSpecifier}';\nimport { createStudioApp } from '@geekmidas/studio/server/hono';`;\n\t\t\t} else {\n\t\t\t\tstudioImports = '';\n\t\t\t}\n\t\t}\n\n\t\t// Generate imports for server hooks\n\t\tconst cors = corsFor(context.surface);\n\t\tlet hooksImports = '';\n\t\tlet beforeSetupCall = '';\n\t\tlet afterSetupCall = '';\n\t\tif (context.hooks?.serverHooksPath) {\n\t\t\tconst relativeHooksPath = relative(\n\t\t\t\tdirname(appPath),\n\t\t\t\tcontext.hooks.serverHooksPath,\n\t\t\t);\n\t\t\thooksImports = `import * as serverHooks from '${relativeHooksPath}';`;\n\t\t\tbeforeSetupCall = `\n // Call beforeSetup hook if defined\n if (typeof serverHooks.beforeSetup === 'function') {\n await serverHooks.beforeSetup(honoApp, { envParser, logger });\n }\n`;\n\t\t\tafterSetupCall = `\n // Call afterSetup hook if defined\n if (typeof serverHooks.afterSetup === 'function') {\n await serverHooks.afterSetup(honoApp, { envParser, logger });\n }\n`;\n\t\t}\n\n\t\tconst telescopeWebSocketSetupCode = telescopeWebSocketEnabled\n\t\t\t? `\n // Setup WebSocket for real-time telescope updates\n try {\n const { createNodeWebSocket } = await import('@hono/node-ws');\n const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app: honoApp });\n // Add WebSocket route directly to main app (sub-app routes don't support WS upgrade)\n honoApp.get('${context.telescope?.path}/ws', upgradeWebSocket(() => ({\n onOpen: (_event: Event, ws: any) => {\n telescope.addWsClient(ws);\n },\n onClose: (_event: Event, ws: any) => {\n telescope.removeWsClient(ws);\n },\n onMessage: (event: MessageEvent, ws: any) => {\n try {\n const data = JSON.parse(event.data);\n if (data.type === 'ping') {\n ws.send(JSON.stringify({ type: 'pong' }));\n }\n } catch {\n // Ignore invalid messages\n }\n },\n })));\n // Store injectWebSocket for server entry to call after serve()\n (honoApp as any).__injectWebSocket = injectWebSocket;\n logger.info('Telescope WebSocket enabled');\n } catch (e) {\n logger.warn({ error: e }, 'WebSocket support not available - install @hono/node-ws for real-time updates');\n }\n`\n\t\t\t: '';\n\n\t\t// Generate telescope setup - either use external instance or create inline\n\t\tlet telescopeSetup = '';\n\t\tif (telescopeEnabled) {\n\t\t\tif (telescopeFromSurface || usesExternalTelescope) {\n\t\t\t\t// Use external telescope instance - no need to create one\n\t\t\t\ttelescopeSetup = `\n${telescopeWebSocketSetupCode}\n // Add telescope middleware (before endpoints to capture all requests)\n honoApp.use('*', createMiddleware(telescope));\n\n // Mount telescope UI\n const telescopeUI = createUI(telescope);\n honoApp.route('${context.telescope?.path}', telescopeUI);\n`;\n\t\t\t} else {\n\t\t\t\t// Create inline telescope instance\n\t\t\t\ttelescopeSetup = `\n // Setup Telescope for debugging/monitoring\n const telescopeStorage = new InMemoryStorage({ maxEntries: ${context.telescope?.maxEntries} });\n const telescope = new Telescope({\n enabled: true,\n path: '${context.telescope?.path}',\n ignorePatterns: ${JSON.stringify(context.telescope?.ignore)},\n recordBody: ${context.telescope?.recordBody},\n storage: telescopeStorage,\n });\n${telescopeWebSocketSetupCode}\n // Add telescope middleware (before endpoints to capture all requests)\n honoApp.use('*', createMiddleware(telescope));\n\n // Mount telescope UI\n const telescopeUI = createUI(telescope);\n honoApp.route('${context.telescope?.path}', telescopeUI);\n`;\n\t\t\t}\n\t\t}\n\n\t\t// Generate studio setup - requires external instance\n\t\tlet studioSetup = '';\n\t\tif (studioEnabled && studioFromDatabase) {\n\t\t\tstudioSetup = `\n // Studio, built from the declared database. The client comes from the\n // construct, so what you inspect is by definition what the handlers write to.\n const studio = new Studio({\n monitoring: { storage: new InMemoryMonitoringStorage({ maxEntries: 100 }) },\n data: {\n db: await __studioDb.service.register({ envParser, context: snifferContext }),\n cursor: { field: 'id', direction: Direction.Desc },\n },\n enabled: true,\n });\n\n const studioApp = createStudioApp(studio);\n honoApp.route('${context.studio?.path}', studioApp);\n`;\n\t\t} else if (studioEnabled && usesExternalStudio) {\n\t\t\tstudioSetup = `\n // Mount Studio data browser UI\n const studioApp = createStudioApp(studio);\n honoApp.route('${context.studio?.path}', studioApp);\n`;\n\t\t}\n\n\t\tconst content = `/**\n * Generated server application\n *\n * ⚠️ WARNING: This is for LOCAL DEVELOPMENT ONLY\n * The subscriber polling mechanism is not production-ready.\n * For production, use AWS Lambda with SQS/SNS event sources.\n */\nimport { Hono } from 'hono';\nimport type { Hono as HonoType } from 'hono';\nimport { setupEndpoints } from './endpoints.js';\nimport { setupSubscribers } from './subscribers.js';\nimport { setupQueues } from './queues.js';\nimport { setupCrons } from './crons.js';\n${runtime.imports}\n${telescopeImports}\n${studioImports}\n${hooksImports}\n${cors.imports}\n${context.storageDrivers?.imports ?? ''}\n\n${runtime.bindings}\n${\n\ttelescopeEnabled && telescopeFromSurface\n\t\t? `// The surface's own Telescope, or the one \\`telescope\\` config describes.\nconst telescope =\n __surface.telescope ??\n new Telescope({\n enabled: true,\n path: '${context.telescope?.path}',\n ignorePatterns: ${JSON.stringify(context.telescope?.ignore)},\n recordBody: ${context.telescope?.recordBody},\n storage: new InMemoryStorage({ maxEntries: ${context.telescope?.maxEntries} }),\n });`\n\t\t: ''\n}\n\n${\n\tcontext.storageDrivers?.setup\n\t\t? `// Which storage drivers exist is the entry point's decision: the scheme in\n// each injected URL picks one, so no construct and no application module names\n// a provider.\n${context.storageDrivers.setup}\n`\n\t\t: ''\n}\nexport interface ServerApp {\n app: HonoType;\n start: (options?: {\n port?: number;\n serve: (app: HonoType, port: number) => void | Promise<void>;\n }) => Promise<void>;\n}\n\n/**\n * Create and configure the Hono application\n *\n * @param app - Optional Hono app instance to configure (creates new one if not provided)\n * @param enableOpenApi - Enable OpenAPI documentation (default: true)\n * @returns Server app with configured Hono app and start function\n *\n * @example\n * // With Bun\n * import { createApp } from './.gkm/server/app.js';\n *\n * const { app, start } = await createApp();\n *\n * await start({\n * port: 3000,\n * serve: (app, port) => {\n * Bun.serve({ port, fetch: app.fetch });\n * }\n * });\n *\n * @example\n * // With Node.js (using @hono/node-server)\n * import { serve } from '@hono/node-server';\n * import { createApp } from './.gkm/server/app.js';\n *\n * const { app, start } = await createApp();\n *\n * await start({\n * port: 3000,\n * serve: (app, port) => {\n * serve({ fetch: app.fetch, port });\n * }\n * });\n */\nexport async function createApp(app?: HonoType, enableOpenApi: boolean = true): Promise<ServerApp> {\n const honoApp = app || new Hono();\n${telescopeSetup}${cors.setup}${beforeSetupCall}${studioSetup}\n // Setup HTTP endpoints\n await setupEndpoints(honoApp, envParser, logger, enableOpenApi);\n${afterSetupCall}\n\n return {\n app: honoApp,\n async start(options) {\n if (!options?.serve) {\n throw new Error(\n 'serve function is required. Pass a serve function for your runtime:\\\\n' +\n ' - Bun: (app, port) => Bun.serve({ port, fetch: app.fetch })\\\\n' +\n ' - Node: (app, port) => serve({ fetch: app.fetch, port })'\n );\n }\n\n const port = options.port ?? 3000;\n\n // Start subscribers in background (non-blocking, local development only)\n await setupSubscribers(envParser, logger).catch((error) => {\n logger.error({ error }, 'Failed to start subscribers');\n });\n\n // Start queue workers in background (non-blocking, local development only)\n await setupQueues(envParser, logger).catch((error) => {\n logger.error({ error }, 'Failed to start queue workers');\n });\n\n // Schedule this app's crons. Caught like the others: a scheduler that\n // cannot start is not a reason for the HTTP server not to.\n await setupCrons(envParser, logger).catch((error) => {\n logger.error({ error }, 'Failed to schedule crons');\n });\n\n logger.info({ port }, 'Starting server');\n\n // Start HTTP server using provided serve function\n await options.serve(honoApp, port);\n\n logger.info({ port }, 'Server started');\n }\n };\n}\n\n// Default export for convenience\nexport default createApp;\n`;\n\n\t\tawait writeFile(appPath, content);\n\n\t\treturn appPath;\n\t}\n\n\t/**\n\t * One Lambda, one endpoint — and no environment import.\n\t *\n\t * The endpoint was built from its surface, so it carries the parser the\n\t * adaptor needs. This used to read\n\t * `import { envParser } from '../../config/env'`, a line the build wrote\n\t * because a generator can print a module specifier and cannot print an\n\t * object. Nothing has to be printed now, which is also why nothing has to be\n\t * named in config.\n\t */\n\tprivate generateAWSApiGatewayV1Handler(\n\t\timportPath: string,\n\t\texportName: string,\n\t\tdrivers?: StorageDrivers,\n\t): string {\n\t\treturn `import { AmazonApiGatewayV1Endpoint } from '@geekmidas/constructs/aws';\nimport { ${exportName} } from '${importPath}';\n${drivers?.imports ?? ''}\n${drivers?.setup ? `\\n// The handler registers the drivers its target needs.\\n${drivers.setup}\\n` : ''}\nconst adapter = new AmazonApiGatewayV1Endpoint(${exportName});\n\nexport const handler = adapter.handler;\n`;\n\t}\n\n\t/**\n\t * One Lambda, one endpoint — and no environment import.\n\t *\n\t * The endpoint was built from its surface, so it carries the parser the\n\t * adaptor needs. This used to read\n\t * `import { envParser } from '../../config/env'`, a line the build wrote\n\t * because a generator can print a module specifier and cannot print an\n\t * object. Nothing has to be printed now, which is also why nothing has to be\n\t * named in config.\n\t */\n\tprivate generateAWSApiGatewayV2Handler(\n\t\timportPath: string,\n\t\texportName: string,\n\t\tdrivers?: StorageDrivers,\n\t): string {\n\t\treturn `import { AmazonApiGatewayV2Endpoint } from '@geekmidas/constructs/aws';\nimport { ${exportName} } from '${importPath}';\n${drivers?.imports ?? ''}\n${drivers?.setup ? `\\n// The handler registers the drivers its target needs.\\n${drivers.setup}\\n` : ''}\nconst adapter = new AmazonApiGatewayV2Endpoint(${exportName});\n\nexport const handler = adapter.handler;\n`;\n\t}\n\n\tprivate generateServerHandler(\n\t\timportPath: string,\n\t\texportName: string,\n\t): string {\n\t\treturn `import { ${exportName} } from '${importPath}';\n\n// Server handler - implement based on your server framework\nexport const handler = ${exportName};\n`;\n\t}\n\n\t/**\n\t * Generate a production-optimized app.ts file\n\t * No dev tools (Telescope, Studio, WebSocket), includes health checks and graceful shutdown\n\t */\n\tprivate async generateProductionAppFile(\n\t\toutputDir: string,\n\t\tcontext: BuildContext,\n\t): Promise<string> {\n\t\tconst appFileName = 'app.ts';\n\t\tconst appPath = join(outputDir, appFileName);\n\n\t\t// The app entry belongs to the primary surface, so no owner is named.\n\t\tconst runtime = runtimeFor(context, dirname(appPath));\n\n\t\tconst production = context.production!;\n\t\tconst healthCheckPath = production.healthCheck;\n\t\tconst enableGracefulShutdown = production.gracefulShutdown;\n\t\tconst enableOpenApi = production.openapi;\n\t\tconst includeSubscribers = production.subscribers === 'include';\n\n\t\t// Generate imports for server hooks\n\t\tconst cors = corsFor(context.surface);\n\t\tlet hooksImports = '';\n\t\tlet beforeSetupCall = '';\n\t\tlet afterSetupCall = '';\n\t\tif (context.hooks?.serverHooksPath) {\n\t\t\tconst relativeHooksPath = relative(\n\t\t\t\tdirname(appPath),\n\t\t\t\tcontext.hooks.serverHooksPath,\n\t\t\t);\n\t\t\thooksImports = `import * as serverHooks from '${relativeHooksPath}';`;\n\t\t\tbeforeSetupCall = `\n // Call beforeSetup hook if defined\n if (typeof serverHooks.beforeSetup === 'function') {\n await serverHooks.beforeSetup(honoApp, { envParser, logger });\n }\n`;\n\t\t\tafterSetupCall = `\n // Call afterSetup hook if defined\n if (typeof serverHooks.afterSetup === 'function') {\n await serverHooks.afterSetup(honoApp, { envParser, logger });\n }\n`;\n\t\t}\n\n\t\t// Subscriber + queue setup code (background workers share the same flag)\n\t\tconst subscriberSetup = includeSubscribers\n\t\t\t? `\n // Start subscribers in background\n await setupSubscribers(envParser, logger).catch((error) => {\n logger.error({ error }, 'Failed to start subscribers');\n });\n\n // Start queue workers in background\n await setupQueues(envParser, logger).catch((error) => {\n logger.error({ error }, 'Failed to start queue workers');\n });\n\n // Schedule this app's crons. Caught like the others: a scheduler that\n // cannot start is not a reason for the HTTP server not to.\n await setupCrons(envParser, logger).catch((error) => {\n logger.error({ error }, 'Failed to schedule crons');\n });\n`\n\t\t\t: '';\n\n\t\tconst subscriberImport = includeSubscribers\n\t\t\t? `import { setupSubscribers } from './subscribers.js';\nimport { setupQueues } from './queues.js';\nimport { setupCrons } from './crons.js';`\n\t\t\t: '';\n\n\t\t// Graceful shutdown code\n\t\tconst gracefulShutdownCode = enableGracefulShutdown\n\t\t\t? `\n // Graceful shutdown handling\n let isShuttingDown = false;\n\n const shutdown = async () => {\n if (isShuttingDown) return;\n isShuttingDown = true;\n logger.info('Graceful shutdown initiated');\n // Allow in-flight requests to complete (30s timeout)\n setTimeout(() => process.exit(0), 30000);\n };\n\n process.on('SIGTERM', shutdown);\n process.on('SIGINT', shutdown);\n`\n\t\t\t: '';\n\n\t\t// Use endpoints/index.js for optimized builds, endpoints.js otherwise\n\t\tconst endpointsImportPath = production.optimizedHandlers\n\t\t\t? './endpoints/index.js'\n\t\t\t: './endpoints.js';\n\n\t\tconst content = `/**\n * Generated production server application\n *\n * This is a production-optimized build without dev tools.\n * - No Telescope debugging dashboard\n * - No Studio database browser\n * - No WebSocket updates\n * - Includes health checks and graceful shutdown\n */\nimport { Hono } from 'hono';\nimport type { Hono as HonoType } from 'hono';\nimport { setupEndpoints } from '${endpointsImportPath}';\n${subscriberImport}\n${runtime.imports}\n${runtime.bindings}\n${hooksImports}\n${cors.imports}\n${context.storageDrivers?.imports ?? ''}\n\n${\n\tcontext.storageDrivers?.setup\n\t\t? `// The entry point registers the drivers its target needs.\n${context.storageDrivers.setup}\n`\n\t\t: ''\n}\nexport interface ServerApp {\n app: HonoType;\n start: (options?: {\n port?: number;\n serve: (app: HonoType, port: number) => void | Promise<void>;\n }) => Promise<void>;\n}\n\n/**\n * Create and configure the production Hono application\n */\nexport async function createApp(app?: HonoType): Promise<ServerApp> {\n const honoApp = app || new Hono();\n\n // Health check endpoint (always first)\n honoApp.get('${healthCheckPath}', (c) => c.json({ status: 'ok', timestamp: Date.now() }));\n honoApp.get('/ready', (c) => c.json({ ready: true }));\n${cors.setup}${beforeSetupCall}\n // Setup HTTP endpoints (OpenAPI: ${enableOpenApi})\n await setupEndpoints(honoApp, envParser, logger, ${enableOpenApi});\n${afterSetupCall}\n return {\n app: honoApp,\n async start(options) {\n if (!options?.serve) {\n throw new Error(\n 'serve function is required. Pass a serve function for your runtime:\\\\n' +\n ' - Bun: (app, port) => Bun.serve({ port, fetch: app.fetch })\\\\n' +\n ' - Node: (app, port) => serve({ fetch: app.fetch, port })'\n );\n }\n\n const port = options.port ?? Number(process.env.PORT) ?? 3000;\n${gracefulShutdownCode}${subscriberSetup}\n logger.info({ port }, 'Starting production server');\n\n // Start HTTP server using provided serve function\n await options.serve(honoApp, port);\n\n logger.info({ port }, 'Production server started');\n }\n };\n}\n\n// Default export for convenience\nexport default createApp;\n`;\n\n\t\tawait writeFile(appPath, content);\n\n\t\t// Also generate the production server entry point\n\t\tawait this.generateProductionServerEntry(outputDir);\n\n\t\treturn appPath;\n\t}\n\n\t/**\n\t * Generate production server.ts entry point\n\t */\n\tprivate async generateProductionServerEntry(\n\t\toutputDir: string,\n\t): Promise<void> {\n\t\tconst serverPath = join(outputDir, 'server.ts');\n\n\t\tconst content = `#!/usr/bin/env node\n/**\n * Production server entry point\n * Generated by 'gkm build --provider server --production'\n */\nimport { serve } from '@hono/node-server';\nimport { createApp } from './app.js';\n\nconst port = Number(process.env.PORT) || 3000;\n\nconst { start } = await createApp();\n\nawait start({\n port,\n serve: (app, port) => { serve({ fetch: app.fetch, port }); },\n});\n`;\n\n\t\tawait writeFile(serverPath, content);\n\t}\n}\n","import type { Endpoint } from '@geekmidas/constructs/endpoints';\nimport {\n\tgetRegisteredZodJsonSchemas,\n\tgetSchemaMetadata,\n\tStandardSchemaJsonSchema,\n} from '@geekmidas/schema/conversion';\nimport type { StandardSchemaV1 } from '@standard-schema/spec';\n\ninterface OpenApiTsOptions {\n\ttitle?: string;\n\tversion?: string;\n\tdescription?: string;\n}\n\n// JSON Schema type definition\ninterface JsonSchema {\n\t// An array since Zod 4.6, which writes a union of primitives — a nullable\n\t// string among them — as `type: ['string', 'null']` rather than `anyOf`.\n\ttype?: string | string[];\n\t/** A literal — `z.literal('open')` is `{ type: 'string', const: 'open' }`. */\n\tconst?: unknown;\n\tproperties?: Record<string, JsonSchema>;\n\titems?: JsonSchema;\n\trequired?: string[];\n\tenum?: string[];\n\t$ref?: string;\n\tanyOf?: JsonSchema[];\n\toneOf?: JsonSchema[];\n\tallOf?: JsonSchema[];\n\tadditionalProperties?: boolean | JsonSchema;\n\t[key: string]: unknown;\n}\n\n// Security scheme type (OpenAPI 3.1)\ninterface SecuritySchemeObject {\n\ttype: 'apiKey' | 'http' | 'mutualTLS' | 'oauth2' | 'openIdConnect';\n\tdescription?: string;\n\tname?: string;\n\tin?: 'query' | 'header' | 'cookie';\n\tscheme?: string;\n\tbearerFormat?: string;\n\tflows?: Record<string, unknown>;\n\topenIdConnectUrl?: string;\n\t[key: string]: unknown;\n}\n\ninterface EndpointInfo {\n\tendpoint: string;\n\troute: string;\n\tmethod: string;\n\tauthorizerName: string | null;\n\t/** @deprecated Use securityScheme instead */\n\tauthorizerType: string | null;\n\t/** The OpenAPI security scheme definition for this endpoint's authorizer */\n\tsecurityScheme: SecuritySchemeObject | null;\n\tinput?: {\n\t\tbody?: StandardSchemaV1;\n\t\tquery?: StandardSchemaV1;\n\t\tparams?: StandardSchemaV1;\n\t};\n\toutput?: StandardSchemaV1;\n\tdescription?: string;\n\ttags?: string[];\n\toperationId?: string;\n\tresponseType: string;\n}\n\ninterface SecuritySchemeInfo {\n\tname: string;\n\ttype: string;\n\tscheme: SecuritySchemeObject;\n}\n\n/**\n * Generates TypeScript OpenAPI module from endpoints.\n * Outputs:\n * - securitySchemes: typed security scheme definitions\n * - endpointAuth: runtime map of endpoints to auth requirements\n * - paths: TypeScript interface for type-safe fetcher\n * - schema interfaces: reusable TypeScript types from Zod/Valibot schemas\n */\nexport class OpenApiTsGenerator {\n\tasync generate(\n\t\tendpoints: Endpoint<any, any, any, any, any, any>[],\n\t\toptions: OpenApiTsOptions = {},\n\t): Promise<string> {\n\t\tconst { title = 'API', version = '1.0.0', description } = options;\n\n\t\t// Extract endpoint info\n\t\tconst endpointInfos = await this.extractEndpointInfos(endpoints);\n\n\t\t// Collect unique security schemes\n\t\tconst securitySchemes = this.collectSecuritySchemes(endpointInfos);\n\n\t\t// Build endpoint auth map\n\t\tconst endpointAuth = this.buildEndpointAuthMap(endpointInfos);\n\n\t\t// Generate schema interfaces\n\t\tconst schemaInterfaces = await this.generateSchemaInterfaces(endpointInfos);\n\n\t\t// Generate paths interface\n\t\tconst pathsInterface = await this.generatePathsInterface(endpointInfos);\n\n\t\t// Build the final TypeScript module\n\t\treturn this.buildModule({\n\t\t\ttitle,\n\t\t\tversion,\n\t\t\tdescription,\n\t\t\tsecuritySchemes,\n\t\t\tendpointAuth,\n\t\t\tschemaInterfaces,\n\t\t\tpathsInterface,\n\t\t});\n\t}\n\n\tprivate async extractEndpointInfos(\n\t\tendpoints: Endpoint<any, any, any, any, any, any>[],\n\t): Promise<EndpointInfo[]> {\n\t\treturn endpoints.map((ep) => {\n\t\t\tconst route = ep.route.replace(/:(\\w+)/g, '{$1}');\n\t\t\tconst method = ep.method.toUpperCase();\n\n\t\t\t// Get security scheme from authorizer (if available)\n\t\t\t// This is the preferred way - the scheme is stored directly on the authorizer\n\t\t\tconst securityScheme = ep.authorizer?.securityScheme as\n\t\t\t\t| SecuritySchemeObject\n\t\t\t\t| undefined;\n\n\t\t\treturn {\n\t\t\t\tendpoint: `${method} ${route}`,\n\t\t\t\troute,\n\t\t\t\tmethod,\n\t\t\t\tauthorizerName: ep.authorizer?.name ?? null,\n\t\t\t\tauthorizerType: ep.authorizer?.type ?? null,\n\t\t\t\tsecurityScheme: securityScheme ?? null,\n\t\t\t\tinput: ep.input,\n\t\t\t\toutput: ep.outputSchema,\n\t\t\t\tdescription: ep.description,\n\t\t\t\ttags: ep.tags,\n\t\t\t\toperationId: ep.operationId,\n\t\t\t\tresponseType: ep.responseType ?? 'application/json',\n\t\t\t};\n\t\t});\n\t}\n\n\tprivate collectSecuritySchemes(\n\t\tendpointInfos: EndpointInfo[],\n\t): SecuritySchemeInfo[] {\n\t\tconst schemes = new Map<string, SecuritySchemeInfo>();\n\n\t\tfor (const info of endpointInfos) {\n\t\t\tif (info.authorizerName && !schemes.has(info.authorizerName)) {\n\t\t\t\t// Prefer the stored security scheme (from .securitySchemes() or built-ins)\n\t\t\t\t// Fall back to inference from authorizerType for backward compatibility\n\t\t\t\tconst scheme =\n\t\t\t\t\tinfo.securityScheme ??\n\t\t\t\t\t(info.authorizerType\n\t\t\t\t\t\t? this.mapAuthorizerToSecurityScheme(\n\t\t\t\t\t\t\t\tinfo.authorizerType,\n\t\t\t\t\t\t\t\tinfo.authorizerName,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: null);\n\n\t\t\t\tif (scheme) {\n\t\t\t\t\tschemes.set(info.authorizerName, {\n\t\t\t\t\t\tname: info.authorizerName,\n\t\t\t\t\t\ttype: scheme.type,\n\t\t\t\t\t\tscheme,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn Array.from(schemes.values());\n\t}\n\n\tprivate mapAuthorizerToSecurityScheme(\n\t\ttype: string,\n\t\t_name: string,\n\t): SecuritySchemeObject {\n\t\tswitch (type.toLowerCase()) {\n\t\t\tcase 'jwt':\n\t\t\tcase 'bearer':\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'http',\n\t\t\t\t\tscheme: 'bearer',\n\t\t\t\t\tbearerFormat: 'JWT',\n\t\t\t\t};\n\t\t\tcase 'iam':\n\t\t\tcase 'aws-sigv4':\n\t\t\tcase 'sigv4':\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'apiKey',\n\t\t\t\t\tin: 'header',\n\t\t\t\t\tname: 'Authorization',\n\t\t\t\t\t'x-amazon-apigateway-authtype': 'awsSigv4',\n\t\t\t\t};\n\t\t\tcase 'apikey':\n\t\t\tcase 'api-key':\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'apiKey',\n\t\t\t\t\tin: 'header',\n\t\t\t\t\tname: 'X-API-Key',\n\t\t\t\t};\n\t\t\tcase 'oauth2':\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'oauth2',\n\t\t\t\t\tflows: {},\n\t\t\t\t};\n\t\t\tcase 'oidc':\n\t\t\tcase 'openidconnect':\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'openIdConnect',\n\t\t\t\t\topenIdConnectUrl: '',\n\t\t\t\t};\n\t\t\tdefault:\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'http',\n\t\t\t\t\tscheme: 'bearer',\n\t\t\t\t};\n\t\t}\n\t}\n\n\tprivate buildEndpointAuthMap(\n\t\tendpointInfos: EndpointInfo[],\n\t): Record<string, string | null> {\n\t\tconst authMap: Record<string, string | null> = {};\n\n\t\tfor (const info of endpointInfos) {\n\t\t\tauthMap[info.endpoint] = info.authorizerName;\n\t\t}\n\n\t\treturn authMap;\n\t}\n\n\tprivate async generateSchemaInterfaces(\n\t\tendpointInfos: EndpointInfo[],\n\t): Promise<string> {\n\t\tconst interfaces: string[] = [];\n\t\tconst generatedNames = new Set<string>();\n\t\t// Collect nested schemas with $defs (from .meta({ id: 'X' }))\n\t\tconst collectedDefs = new Map<string, JsonSchema>();\n\n\t\tfor (const info of endpointInfos) {\n\t\t\tconst baseName = this.getSchemaBaseName(info);\n\n\t\t\t// Input body schema\n\t\t\tif (info.input?.body) {\n\t\t\t\tconst name = await this.getSchemaName(\n\t\t\t\t\tinfo.input.body,\n\t\t\t\t\t`${baseName}Input`,\n\t\t\t\t);\n\t\t\t\tif (!generatedNames.has(name)) {\n\t\t\t\t\tconst schema = await this.schemaToInterfaceWithDefs(\n\t\t\t\t\t\tinfo.input.body,\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tcollectedDefs,\n\t\t\t\t\t);\n\t\t\t\t\tif (schema) {\n\t\t\t\t\t\tinterfaces.push(schema);\n\t\t\t\t\t\tgeneratedNames.add(name);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Input params schema\n\t\t\tif (info.input?.params) {\n\t\t\t\tconst name = await this.getSchemaName(\n\t\t\t\t\tinfo.input.params,\n\t\t\t\t\t`${baseName}Params`,\n\t\t\t\t);\n\t\t\t\tif (!generatedNames.has(name)) {\n\t\t\t\t\tconst schema = await this.schemaToInterfaceWithDefs(\n\t\t\t\t\t\tinfo.input.params,\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tcollectedDefs,\n\t\t\t\t\t);\n\t\t\t\t\tif (schema) {\n\t\t\t\t\t\tinterfaces.push(schema);\n\t\t\t\t\t\tgeneratedNames.add(name);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Input query schema\n\t\t\tif (info.input?.query) {\n\t\t\t\tconst name = await this.getSchemaName(\n\t\t\t\t\tinfo.input.query,\n\t\t\t\t\t`${baseName}Query`,\n\t\t\t\t);\n\t\t\t\tif (!generatedNames.has(name)) {\n\t\t\t\t\tconst schema = await this.schemaToInterfaceWithDefs(\n\t\t\t\t\t\tinfo.input.query,\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tcollectedDefs,\n\t\t\t\t\t);\n\t\t\t\t\tif (schema) {\n\t\t\t\t\t\tinterfaces.push(schema);\n\t\t\t\t\t\tgeneratedNames.add(name);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Output schema\n\t\t\tif (info.output) {\n\t\t\t\tconst name = await this.getSchemaName(info.output, `${baseName}Output`);\n\t\t\t\tif (!generatedNames.has(name)) {\n\t\t\t\t\tconst schema = await this.schemaToInterfaceWithDefs(\n\t\t\t\t\t\tinfo.output,\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tcollectedDefs,\n\t\t\t\t\t);\n\t\t\t\t\tif (schema) {\n\t\t\t\t\t\tinterfaces.push(schema);\n\t\t\t\t\t\tgeneratedNames.add(name);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Pull in every schema registered via `.meta({ id })` in zod's global\n\t\t// registry, even when no endpoint references it directly. This makes\n\t\t// derived schemas like `UserSchema.pick(...)` discoverable through the\n\t\t// named parent type without requiring the parent to be wired into an\n\t\t// endpoint.\n\t\tconst registrySchemas = await getRegisteredZodJsonSchemas();\n\t\tfor (const [rawName, rawSchema] of Object.entries(registrySchemas)) {\n\t\t\tconst defName = this.pascalCase(rawName);\n\t\t\tif (collectedDefs.has(defName) || generatedNames.has(defName)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst {\n\t\t\t\tid: _id,\n\t\t\t\t$defs: _defs,\n\t\t\t\t...rest\n\t\t\t} = rawSchema as JsonSchema & {\n\t\t\t\tid?: string;\n\t\t\t};\n\t\t\tcollectedDefs.set(defName, rest as JsonSchema);\n\t\t}\n\n\t\t// Generate interfaces for collected $defs (nested schemas with .meta({ id: 'X' }))\n\t\tfor (const [defName, defSchema] of collectedDefs) {\n\t\t\tif (!generatedNames.has(defName)) {\n\t\t\t\tconst interfaceStr = this.jsonSchemaToInterface(defSchema, defName);\n\t\t\t\tinterfaces.push(interfaceStr);\n\t\t\t\tgeneratedNames.add(defName);\n\t\t\t}\n\t\t}\n\n\t\treturn interfaces.join('\\n\\n');\n\t}\n\n\t/**\n\t * Get the name for a schema, using metadata `id` if available,\n\t * otherwise falling back to the provided default name.\n\t */\n\tprivate async getSchemaName(\n\t\tschema: StandardSchemaV1,\n\t\tdefaultName: string,\n\t): Promise<string> {\n\t\ttry {\n\t\t\tconst metadata = await getSchemaMetadata(schema);\n\t\t\tif (metadata?.id) {\n\t\t\t\treturn this.pascalCase(metadata.id);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore metadata extraction errors\n\t\t}\n\t\treturn defaultName;\n\t}\n\n\tprivate getSchemaBaseName(info: EndpointInfo): string {\n\t\tif (info.operationId) {\n\t\t\treturn this.pascalCase(info.operationId);\n\t\t}\n\n\t\t// Generate name from method + route\n\t\tconst routeParts = info.route\n\t\t\t.replace(/[{}]/g, '')\n\t\t\t.split('/')\n\t\t\t.filter(Boolean)\n\t\t\t.map((part) => this.pascalCase(part));\n\n\t\treturn `${this.pascalCase(info.method.toLowerCase())}${routeParts.join('')}`;\n\t}\n\n\tprivate pascalCase(str: string): string {\n\t\treturn str\n\t\t\t.replace(/[-_](.)/g, (_, c) => c.toUpperCase())\n\t\t\t.replace(/^./, (c) => c.toUpperCase());\n\t}\n\n\t/**\n\t * Convert schema to interface while collecting $defs for nested schemas\n\t * with .meta({ id: 'X' }).\n\t */\n\tprivate async schemaToInterfaceWithDefs(\n\t\tschema: StandardSchemaV1,\n\t\tname: string,\n\t\tcollectedDefs: Map<string, JsonSchema>,\n\t): Promise<string | null> {\n\t\ttry {\n\t\t\t// Get raw JSON schema with $defs intact (don't use convertStandardSchemaToJsonSchema\n\t\t\t// which strips $defs)\n\t\t\tconst vendor = schema['~standard']?.vendor;\n\t\t\tif (!vendor || !(vendor in StandardSchemaJsonSchema)) {\n\t\t\t\treturn unknownType(name);\n\t\t\t}\n\n\t\t\tconst toJsonSchema =\n\t\t\t\tStandardSchemaJsonSchema[\n\t\t\t\t\tvendor as keyof typeof StandardSchemaJsonSchema\n\t\t\t\t];\n\t\t\tconst jsonSchema = await toJsonSchema(schema);\n\t\t\tif (!jsonSchema) return unknownType(name);\n\n\t\t\t// Extract $defs from the JSON schema (these come from .meta({ id: 'X' }))\n\t\t\tif (jsonSchema.$defs && typeof jsonSchema.$defs === 'object') {\n\t\t\t\tfor (const [defName, defSchema] of Object.entries(jsonSchema.$defs)) {\n\t\t\t\t\tif (!collectedDefs.has(defName)) {\n\t\t\t\t\t\t// Remove the 'id' field from the schema as it's just metadata\n\t\t\t\t\t\tconst { id, ...schemaWithoutId } = defSchema as JsonSchema & {\n\t\t\t\t\t\t\tid?: string;\n\t\t\t\t\t\t};\n\t\t\t\t\t\tcollectedDefs.set(defName, schemaWithoutId as JsonSchema);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Remove $defs from the schema before converting to interface\n\t\t\tconst { $defs, ...schemaWithoutDefs } = jsonSchema;\n\n\t\t\t// A schema that *is* a registered schema converts to nothing but a\n\t\t\t// `$ref` at it, and the def it points at has already been collected\n\t\t\t// above — so emitting a declaration here as well produces\n\t\t\t// `export type User = User`, a circular alias that is not a type.\n\t\t\t//\n\t\t\t// Zod used to inline this case and only started emitting the `$ref` in\n\t\t\t// 4.6, which is how the bug arrived without the generator changing.\n\t\t\tconst ref = (schemaWithoutDefs as { $ref?: string }).$ref;\n\t\t\tif (\n\t\t\t\tref &&\n\t\t\t\tObject.keys(schemaWithoutDefs).every(\n\t\t\t\t\t(k) => k === '$ref' || k === '$schema',\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tconst target = ref.split('/').pop();\n\t\t\t\t// The def's own interface is the declaration. Only alias it when the\n\t\t\t\t// names actually differ, which is when the alias says something.\n\t\t\t\treturn target === name ? null : `export type ${name} = ${target};`;\n\t\t\t}\n\n\t\t\treturn this.jsonSchemaToInterface(schemaWithoutDefs, name);\n\t\t} catch {\n\t\t\treturn unknownType(name);\n\t\t}\n\t}\n\n\tprivate jsonSchemaToInterface(schema: JsonSchema, name: string): string {\n\t\tif (schema.type !== 'object' || !schema.properties) {\n\t\t\t// For non-object types, create a type alias\n\t\t\tconst typeStr = this.jsonSchemaTypeToTs(schema);\n\t\t\treturn `export type ${name} = ${typeStr};`;\n\t\t}\n\n\t\tconst props: string[] = [];\n\t\tconst required = new Set(schema.required || []);\n\n\t\tfor (const [propName, propSchema] of Object.entries(schema.properties)) {\n\t\t\tconst isRequired = required.has(propName);\n\t\t\tconst typeStr = this.jsonSchemaTypeToTs(propSchema as JsonSchema);\n\t\t\tconst optionalMark = isRequired ? '' : '?';\n\t\t\tprops.push(` ${propName}${optionalMark}: ${typeStr};`);\n\t\t}\n\n\t\treturn `export interface ${name} {\\n${props.join('\\n')}\\n}`;\n\t}\n\n\tprivate jsonSchemaTypeToTs(schema: JsonSchema): string {\n\t\tif (!schema) return 'unknown';\n\n\t\tif (schema.$ref) {\n\t\t\t// Extract name from $ref\n\t\t\tconst refName = schema.$ref.split('/').pop() || 'unknown';\n\t\t\treturn refName;\n\t\t}\n\n\t\t// Before the type: a literal is narrower than the type it belongs to, and\n\t\t// reading only the type turned `'open' | 'closed'` into `string | string`.\n\t\tif (schema.const !== undefined) {\n\t\t\treturn typeof schema.const === 'string'\n\t\t\t\t? `'${schema.const}'`\n\t\t\t\t: String(schema.const);\n\t\t}\n\n\t\tif (schema.anyOf) {\n\t\t\treturn schema.anyOf\n\t\t\t\t.map((s: JsonSchema) => this.jsonSchemaTypeToTs(s))\n\t\t\t\t.join(' | ');\n\t\t}\n\n\t\tif (schema.oneOf) {\n\t\t\treturn schema.oneOf\n\t\t\t\t.map((s: JsonSchema) => this.jsonSchemaTypeToTs(s))\n\t\t\t\t.join(' | ');\n\t\t}\n\n\t\tif (schema.allOf) {\n\t\t\treturn schema.allOf\n\t\t\t\t.map((s: JsonSchema) => this.jsonSchemaTypeToTs(s))\n\t\t\t\t.join(' & ');\n\t\t}\n\n\t\tif (Array.isArray(schema.type)) {\n\t\t\treturn schema.type\n\t\t\t\t.map((type) => this.jsonSchemaTypeToTs({ ...schema, type }))\n\t\t\t\t.join(' | ');\n\t\t}\n\n\t\tswitch (schema.type) {\n\t\t\tcase 'string':\n\t\t\t\tif (schema.enum) {\n\t\t\t\t\treturn schema.enum.map((e: string) => `'${e}'`).join(' | ');\n\t\t\t\t}\n\t\t\t\treturn 'string';\n\t\t\tcase 'number':\n\t\t\tcase 'integer':\n\t\t\t\treturn 'number';\n\t\t\tcase 'boolean':\n\t\t\t\treturn 'boolean';\n\t\t\tcase 'null':\n\t\t\t\treturn 'null';\n\t\t\tcase 'array':\n\t\t\t\tif (schema.items) {\n\t\t\t\t\treturn `Array<${this.jsonSchemaTypeToTs(schema.items as JsonSchema)}>`;\n\t\t\t\t}\n\t\t\t\treturn 'Array<unknown>';\n\t\t\tcase 'object':\n\t\t\t\tif (schema.properties) {\n\t\t\t\t\tconst props: string[] = [];\n\t\t\t\t\tconst required = new Set(schema.required || []);\n\t\t\t\t\tfor (const [propName, propSchema] of Object.entries(\n\t\t\t\t\t\tschema.properties,\n\t\t\t\t\t)) {\n\t\t\t\t\t\tconst isRequired = required.has(propName);\n\t\t\t\t\t\tconst typeStr = this.jsonSchemaTypeToTs(propSchema as JsonSchema);\n\t\t\t\t\t\tconst optionalMark = isRequired ? '' : '?';\n\t\t\t\t\t\tprops.push(`${propName}${optionalMark}: ${typeStr}`);\n\t\t\t\t\t}\n\t\t\t\t\treturn `{ ${props.join('; ')} }`;\n\t\t\t\t}\n\t\t\t\tif (schema.additionalProperties) {\n\t\t\t\t\tconst valueType = this.jsonSchemaTypeToTs(\n\t\t\t\t\t\tschema.additionalProperties as JsonSchema,\n\t\t\t\t\t);\n\t\t\t\t\treturn `Record<string, ${valueType}>`;\n\t\t\t\t}\n\t\t\t\treturn 'Record<string, unknown>';\n\t\t\tdefault:\n\t\t\t\treturn 'unknown';\n\t\t}\n\t}\n\n\tprivate async generatePathsInterface(\n\t\tendpointInfos: EndpointInfo[],\n\t): Promise<string> {\n\t\tconst pathGroups = new Map<string, EndpointInfo[]>();\n\n\t\t// Group endpoints by route\n\t\tfor (const info of endpointInfos) {\n\t\t\tconst existing = pathGroups.get(info.route) || [];\n\t\t\texisting.push(info);\n\t\t\tpathGroups.set(info.route, existing);\n\t\t}\n\n\t\tconst pathEntries: string[] = [];\n\n\t\tfor (const [route, infos] of pathGroups) {\n\t\t\tconst methodEntries: string[] = [];\n\n\t\t\tfor (const info of infos) {\n\t\t\t\tconst methodDef = await this.generateMethodDefinition(info);\n\t\t\t\tmethodEntries.push(` ${info.method.toLowerCase()}: ${methodDef};`);\n\t\t\t}\n\n\t\t\t// Add path parameters if present\n\t\t\tconst firstWithParams = infos.find((i) => i.input?.params);\n\t\t\tlet paramsEntry = '';\n\t\t\tif (firstWithParams?.input?.params) {\n\t\t\t\tconst paramsName = await this.getSchemaName(\n\t\t\t\t\tfirstWithParams.input.params,\n\t\t\t\t\t`${this.getSchemaBaseName(firstWithParams)}Params`,\n\t\t\t\t);\n\t\t\t\tparamsEntry = `\\n parameters: {\\n path: ${paramsName};\\n };`;\n\t\t\t}\n\n\t\t\tpathEntries.push(\n\t\t\t\t` '${route}': {${paramsEntry}\\n${methodEntries.join('\\n')}\\n };`,\n\t\t\t);\n\t\t}\n\n\t\treturn `export interface paths {\\n${pathEntries.join('\\n')}\\n}`;\n\t}\n\n\tprivate async generateMethodDefinition(info: EndpointInfo): Promise<string> {\n\t\tconst parts: string[] = [];\n\t\tconst baseName = this.getSchemaBaseName(info);\n\n\t\t// Request body\n\t\tif (info.input?.body) {\n\t\t\tconst bodyName = await this.getSchemaName(\n\t\t\t\tinfo.input.body,\n\t\t\t\t`${baseName}Input`,\n\t\t\t);\n\t\t\tparts.push(`requestBody: {\n content: {\n 'application/json': ${bodyName};\n };\n }`);\n\t\t}\n\n\t\t// Query parameters\n\t\tif (info.input?.query) {\n\t\t\tconst queryName = await this.getSchemaName(\n\t\t\t\tinfo.input.query,\n\t\t\t\t`${baseName}Query`,\n\t\t\t);\n\t\t\tparts.push(`parameters: {\n query: ${queryName};\n }`);\n\t\t}\n\n\t\t// Responses\n\t\tconst outputName = info.output\n\t\t\t? await this.getSchemaName(info.output, `${baseName}Output`)\n\t\t\t: 'unknown';\n\t\tparts.push(`responses: {\n 200: {\n content: {\n '${info.responseType}': ${outputName};\n };\n };\n }`);\n\n\t\treturn `{\\n ${parts.join(';\\n ')};\\n }`;\n\t}\n\n\tprivate buildModule(params: {\n\t\ttitle: string;\n\t\tversion: string;\n\t\tdescription?: string;\n\t\tsecuritySchemes: SecuritySchemeInfo[];\n\t\tendpointAuth: Record<string, string | null>;\n\t\tschemaInterfaces: string;\n\t\tpathsInterface: string;\n\t}): string {\n\t\tconst {\n\t\t\ttitle,\n\t\t\tversion,\n\t\t\tdescription,\n\t\t\tsecuritySchemes,\n\t\t\tendpointAuth,\n\t\t\tschemaInterfaces,\n\t\t\tpathsInterface,\n\t\t} = params;\n\n\t\tconst securitySchemesObj = securitySchemes.reduce(\n\t\t\t(acc, s) => {\n\t\t\t\tacc[s.name] = s.scheme;\n\t\t\t\treturn acc;\n\t\t\t},\n\t\t\t{} as Record<string, SecuritySchemeObject>,\n\t\t);\n\n\t\tconst schemeNames = securitySchemes.map((s) => `'${s.name}'`).join(' | ');\n\n\t\t// Generate createApi only if there are security schemes\n\t\tconst hasSecuritySchemes = schemeNames.length > 0;\n\n\t\tconst createApiSection = hasSecuritySchemes\n\t\t\t? `\n// ============================================================\n// API Client Factory\n// ============================================================\n\nimport {\n createAuthAwareFetcher,\n type AuthStrategy,\n} from '@geekmidas/client/auth-fetcher';\nimport { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';\nimport type { QueryClient } from '@tanstack/react-query';\n\n/**\n * Options for creating the API client.\n */\nexport interface CreateApiOptions {\n /** Base URL for all API requests (required) */\n baseURL: string;\n /** Auth strategies for each security scheme used in this API */\n authStrategies: Record<SecuritySchemeId, AuthStrategy>;\n /** Optional React Query client instance */\n queryClient?: QueryClient;\n /** Optional request interceptor */\n onRequest?: (config: RequestInit) => RequestInit | Promise<RequestInit>;\n /** The \\`fetch\\` requests go out on — a test browser's, for one */\n fetch?: typeof fetch;\n}\n\n/**\n * Create a type-safe API client with authentication and React Query hooks.\n *\n * @example\n * \\`\\`\\`typescript\n * const api = createApi({\n * baseURL: 'https://api.example.com',\n * authStrategies: {\n * jwt: { type: 'bearer', tokenProvider },\n * },\n * });\n *\n * // Imperative fetch\n * const user = await api('GET /users/{id}', { params: { id: '123' } });\n *\n * // React Query hooks\n * const { data } = api.useQuery('GET /users/{id}', { params: { id: '123' } });\n * const mutation = api.useMutation('POST /users');\n * \\`\\`\\`\n */\nexport function createApi(options: CreateApiOptions) {\n const fetcher = createAuthAwareFetcher<paths, typeof endpointAuth, typeof securitySchemes>({\n baseURL: options.baseURL,\n endpointAuth,\n securitySchemes,\n authStrategies: options.authStrategies,\n onRequest: options.onRequest,\n ...(options.fetch ? { fetch: options.fetch } : {}),\n });\n\n const hooks = createEndpointHooks<paths>(fetcher, { queryClient: options.queryClient });\n\n return Object.assign(fetcher, hooks);\n}\n`\n\t\t\t: `\n// ============================================================\n// API Client Factory\n// ============================================================\n\nimport { createTypedFetcher, type FetcherOptions } from '@geekmidas/client/fetcher';\nimport { createEndpointHooks } from '@geekmidas/client/endpoint-hooks';\nimport type { QueryClient } from '@tanstack/react-query';\n\n/**\n * Options for creating the API client.\n */\nexport interface CreateApiOptions extends Omit<FetcherOptions, 'baseURL'> {\n /** Base URL for all API requests (required) */\n baseURL: string;\n /** Optional React Query client instance */\n queryClient?: QueryClient;\n}\n\n/**\n * Create a type-safe API client with React Query hooks.\n *\n * @example\n * \\`\\`\\`typescript\n * const api = createApi({\n * baseURL: 'https://api.example.com',\n * });\n *\n * // Imperative fetch\n * const data = await api('GET /health');\n *\n * // React Query hooks\n * const { data } = api.useQuery('GET /health');\n * \\`\\`\\`\n */\nexport function createApi(options: CreateApiOptions) {\n const { queryClient, ...fetcherOptions } = options;\n const request = createTypedFetcher<paths>(fetcherOptions);\n\n const hooks = createEndpointHooks<paths>(request, { queryClient });\n\n return Object.assign(request, hooks);\n}\n`;\n\n\t\treturn `// Auto-generated by @geekmidas/cli - DO NOT EDIT\n\n// ============================================================\n// Security Scheme Type\n// ============================================================\n\ninterface SecuritySchemeObject {\n type: 'apiKey' | 'http' | 'mutualTLS' | 'oauth2' | 'openIdConnect';\n description?: string;\n name?: string;\n in?: 'query' | 'header' | 'cookie';\n scheme?: string;\n bearerFormat?: string;\n flows?: Record<string, unknown>;\n openIdConnectUrl?: string;\n [key: string]: unknown;\n}\n\n// ============================================================\n// API Info\n// ============================================================\n\nexport const apiInfo = {\n title: '${title}',\n version: '${version}',${description ? `\\n description: '${description.replace(/'/g, \"\\\\'\")}',` : ''}\n} as const;\n\n// ============================================================\n// Security Schemes\n// ============================================================\n\n/**\n * Available security schemes for this API.\n * Maps authorizer names to OpenAPI security scheme definitions.\n */\nexport const securitySchemes = ${JSON.stringify(securitySchemesObj, null, 2).replace(/\"([a-zA-Z_$][a-zA-Z0-9_$]*)\":/g, '$1:')} as const satisfies Record<string, SecuritySchemeObject>;\n\nexport type SecuritySchemeId = ${schemeNames || 'never'};\n\n// ============================================================\n// Endpoint Authentication Map\n// ============================================================\n\n/**\n * Runtime map of endpoints to their required authentication scheme.\n * \\`null\\` indicates a public endpoint (no auth required).\n */\nexport const endpointAuth = ${JSON.stringify(endpointAuth, null, 2).replace(/\"([^\"]+)\":/g, \"'$1':\")} as const satisfies Record<string, SecuritySchemeId | null>;\n\nexport type EndpointString = keyof typeof endpointAuth;\n\nexport type AuthenticatedEndpoint = {\n [K in EndpointString]: typeof endpointAuth[K] extends null ? never : K;\n}[EndpointString];\n\nexport type PublicEndpoint = {\n [K in EndpointString]: typeof endpointAuth[K] extends null ? K : never;\n}[EndpointString];\n\n// ============================================================\n// Schema Definitions\n// ============================================================\n\n${schemaInterfaces}\n\n// ============================================================\n// OpenAPI Paths\n// ============================================================\n\n${pathsInterface}\n${createApiSection}\n`;\n\t}\n}\n\n/**\n * The declaration for a schema that cannot be converted — a vendor with no\n * converter, or one that fails. The paths interface names it either way, so\n * leaving it undeclared made the generated client fail to compile.\n */\nfunction unknownType(name: string): string {\n\treturn `export type ${name} = unknown;`;\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  let _aws_sdk_client_ssm = require("@aws-sdk/client-ssm");
2
- //#region src/secrets/ssm.ts
2
+ //#region src/secrets/aws.ts
3
3
  /**
4
4
  * A stage's secrets in SSM Parameter Store, in the stage's own AWS account.
5
5
  *
@@ -14,8 +14,9 @@ let _aws_sdk_client_ssm = require("@aws-sdk/client-ssm");
14
14
  function secretsParameterName(project, stage) {
15
15
  return `/gkm/${project}/${stage}/secrets`;
16
16
  }
17
- var SsmSecretsStore = class {
17
+ var AwsSecretsStore = class {
18
18
  options;
19
+ name = "ssm";
19
20
  client;
20
21
  constructor(options) {
21
22
  this.options = options;
@@ -31,7 +32,7 @@ var SsmSecretsStore = class {
31
32
  this.client = new _aws_sdk_client_ssm.SSMClient(config);
32
33
  return this.client;
33
34
  }
34
- async pull(stage) {
35
+ async read(stage) {
35
36
  const ssm = await this.ssm();
36
37
  try {
37
38
  const { Parameter } = await ssm.send(new _aws_sdk_client_ssm.GetParameterCommand({
@@ -44,7 +45,7 @@ var SsmSecretsStore = class {
44
45
  throw error;
45
46
  }
46
47
  }
47
- async push(stage, secrets) {
48
+ async write(stage, secrets) {
48
49
  await (await this.ssm()).send(new _aws_sdk_client_ssm.PutParameterCommand({
49
50
  Name: secretsParameterName(this.options.project, stage),
50
51
  Value: JSON.stringify(secrets),
@@ -55,6 +56,6 @@ var SsmSecretsStore = class {
55
56
  }
56
57
  };
57
58
  //#endregion
58
- exports.SsmSecretsStore = SsmSecretsStore;
59
+ exports.AwsSecretsStore = AwsSecretsStore;
59
60
 
60
- //# sourceMappingURL=ssm-FxeSAt35.cjs.map
61
+ //# sourceMappingURL=aws-DkHdTV-p.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aws-DkHdTV-p.cjs","names":["SSMClient","GetParameterCommand","ParameterNotFound","PutParameterCommand"],"sources":["../src/secrets/aws.ts"],"sourcesContent":["/**\n * A stage's secrets in SSM Parameter Store, in the stage's own AWS account.\n *\n * One parameter per stage, `/gkm/<project>/<stage>/secrets`, a SecureString —\n * encrypted at rest by KMS, so nothing about it needs a key file anywhere.\n * Which account it is in is whichever credentials are active: the stage's\n * OIDC role in a deploy job, or `--profile` on a developer's machine. With\n * staging and production in different accounts, each stage's secrets sit\n * beside its infrastructure, and a staging role cannot read production's.\n */\n\nimport {\n\tGetParameterCommand,\n\tParameterNotFound,\n\tPutParameterCommand,\n\tSSMClient,\n\ttype SSMClientConfig,\n} from '@aws-sdk/client-ssm';\nimport type { SecretsStore } from './store.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface AwsSecretsStoreOptions {\n\t/** The workspace name, which scopes the parameter path. */\n\tproject: string;\n\tregion: string;\n\t/**\n\t * A named AWS profile, resolved with `fromIni` only. The default chain\n\t * reads AWS_* from the environment first, so exported staging keys would\n\t * win over `--profile prod` and put production's secrets in staging.\n\t */\n\tprofile?: string;\n\t/** For tests: an endpoint such as the AWS emulator. */\n\tendpoint?: string;\n}\n\n/** The parameter a stage's secrets are kept in. */\nexport function secretsParameterName(project: string, stage: string): string {\n\treturn `/gkm/${project}/${stage}/secrets`;\n}\n\nexport class AwsSecretsStore implements SecretsStore {\n\treadonly name = 'ssm';\n\n\tprivate client?: SSMClient;\n\n\tconstructor(private readonly options: AwsSecretsStoreOptions) {}\n\n\tprivate async ssm(): Promise<SSMClient> {\n\t\tif (this.client) return this.client;\n\n\t\tconst config: SSMClientConfig = { region: this.options.region };\n\t\tif (this.options.profile) {\n\t\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\t\tconfig.credentials = fromIni({ profile: this.options.profile });\n\t\t}\n\t\tif (this.options.endpoint) {\n\t\t\tconfig.endpoint = this.options.endpoint;\n\t\t}\n\n\t\tthis.client = new SSMClient(config);\n\t\treturn this.client;\n\t}\n\n\tasync read(stage: string): Promise<StageSecrets | null> {\n\t\tconst ssm = await this.ssm();\n\n\t\ttry {\n\t\t\tconst { Parameter } = await ssm.send(\n\t\t\t\tnew GetParameterCommand({\n\t\t\t\t\tName: secretsParameterName(this.options.project, stage),\n\t\t\t\t\tWithDecryption: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn Parameter?.Value\n\t\t\t\t? (JSON.parse(Parameter.Value) as StageSecrets)\n\t\t\t\t: null;\n\t\t} catch (error) {\n\t\t\tif (error instanceof ParameterNotFound) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync write(stage: string, secrets: StageSecrets): Promise<void> {\n\t\tconst ssm = await this.ssm();\n\n\t\tawait ssm.send(\n\t\t\tnew PutParameterCommand({\n\t\t\t\tName: secretsParameterName(this.options.project, stage),\n\t\t\t\tValue: JSON.stringify(secrets),\n\t\t\t\tType: 'SecureString',\n\t\t\t\tOverwrite: true,\n\t\t\t\tDescription: `gkm secrets for ${this.options.project}/${stage}`,\n\t\t\t}),\n\t\t);\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AAoCA,SAAgB,qBAAqB,SAAiB,OAAuB;CAC5E,OAAO,QAAQ,QAAQ,GAAG,MAAM;AACjC;AAEA,IAAa,kBAAb,MAAqD;CAKvB;CAJ7B,OAAgB;CAEhB;CAEA,YAAY,SAAkD;EAAjC,KAAA,UAAA;CAAkC;CAE/D,MAAc,MAA0B;EACvC,IAAI,KAAK,QAAQ,OAAO,KAAK;EAE7B,MAAM,SAA0B,EAAE,QAAQ,KAAK,QAAQ,OAAO;EAC9D,IAAI,KAAK,QAAQ,SAAS;GACzB,MAAM,EAAE,YAAY,MAAM,OAAO;GACjC,OAAO,cAAc,QAAQ,EAAE,SAAS,KAAK,QAAQ,QAAQ,CAAC;EAC/D;EACA,IAAI,KAAK,QAAQ,UAChB,OAAO,WAAW,KAAK,QAAQ;EAGhC,KAAK,SAAS,IAAIA,oBAAAA,UAAU,MAAM;EAClC,OAAO,KAAK;CACb;CAEA,MAAM,KAAK,OAA6C;EACvD,MAAM,MAAM,MAAM,KAAK,IAAI;EAE3B,IAAI;GACH,MAAM,EAAE,cAAc,MAAM,IAAI,KAC/B,IAAIC,oBAAAA,oBAAoB;IACvB,MAAM,qBAAqB,KAAK,QAAQ,SAAS,KAAK;IACtD,gBAAgB;GACjB,CAAC,CACF;GACA,OAAO,WAAW,QACd,KAAK,MAAM,UAAU,KAAK,IAC3B;EACJ,SAAS,OAAO;GACf,IAAI,iBAAiBC,oBAAAA,mBAAmB,OAAO;GAC/C,MAAM;EACP;CACD;CAEA,MAAM,MAAM,OAAe,SAAsC;EAGhE,OAAM,MAFY,KAAK,IAAI,EAAA,CAEjB,KACT,IAAIC,oBAAAA,oBAAoB;GACvB,MAAM,qBAAqB,KAAK,QAAQ,SAAS,KAAK;GACtD,OAAO,KAAK,UAAU,OAAO;GAC7B,MAAM;GACN,WAAW;GACX,aAAa,mBAAmB,KAAK,QAAQ,QAAQ,GAAG;EACzD,CAAC,CACF;CACD;AACD"}
@@ -1,5 +1,5 @@
1
1
  import { GetParameterCommand, ParameterNotFound, PutParameterCommand, SSMClient } from "@aws-sdk/client-ssm";
2
- //#region src/secrets/ssm.ts
2
+ //#region src/secrets/aws.ts
3
3
  /**
4
4
  * A stage's secrets in SSM Parameter Store, in the stage's own AWS account.
5
5
  *
@@ -14,8 +14,9 @@ import { GetParameterCommand, ParameterNotFound, PutParameterCommand, SSMClient
14
14
  function secretsParameterName(project, stage) {
15
15
  return `/gkm/${project}/${stage}/secrets`;
16
16
  }
17
- var SsmSecretsStore = class {
17
+ var AwsSecretsStore = class {
18
18
  options;
19
+ name = "ssm";
19
20
  client;
20
21
  constructor(options) {
21
22
  this.options = options;
@@ -31,7 +32,7 @@ var SsmSecretsStore = class {
31
32
  this.client = new SSMClient(config);
32
33
  return this.client;
33
34
  }
34
- async pull(stage) {
35
+ async read(stage) {
35
36
  const ssm = await this.ssm();
36
37
  try {
37
38
  const { Parameter } = await ssm.send(new GetParameterCommand({
@@ -44,7 +45,7 @@ var SsmSecretsStore = class {
44
45
  throw error;
45
46
  }
46
47
  }
47
- async push(stage, secrets) {
48
+ async write(stage, secrets) {
48
49
  await (await this.ssm()).send(new PutParameterCommand({
49
50
  Name: secretsParameterName(this.options.project, stage),
50
51
  Value: JSON.stringify(secrets),
@@ -55,6 +56,6 @@ var SsmSecretsStore = class {
55
56
  }
56
57
  };
57
58
  //#endregion
58
- export { SsmSecretsStore };
59
+ export { AwsSecretsStore };
59
60
 
60
- //# sourceMappingURL=ssm-BKN1Cg4z.mjs.map
61
+ //# sourceMappingURL=aws-DojzQnLd.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aws-DojzQnLd.mjs","names":[],"sources":["../src/secrets/aws.ts"],"sourcesContent":["/**\n * A stage's secrets in SSM Parameter Store, in the stage's own AWS account.\n *\n * One parameter per stage, `/gkm/<project>/<stage>/secrets`, a SecureString —\n * encrypted at rest by KMS, so nothing about it needs a key file anywhere.\n * Which account it is in is whichever credentials are active: the stage's\n * OIDC role in a deploy job, or `--profile` on a developer's machine. With\n * staging and production in different accounts, each stage's secrets sit\n * beside its infrastructure, and a staging role cannot read production's.\n */\n\nimport {\n\tGetParameterCommand,\n\tParameterNotFound,\n\tPutParameterCommand,\n\tSSMClient,\n\ttype SSMClientConfig,\n} from '@aws-sdk/client-ssm';\nimport type { SecretsStore } from './store.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface AwsSecretsStoreOptions {\n\t/** The workspace name, which scopes the parameter path. */\n\tproject: string;\n\tregion: string;\n\t/**\n\t * A named AWS profile, resolved with `fromIni` only. The default chain\n\t * reads AWS_* from the environment first, so exported staging keys would\n\t * win over `--profile prod` and put production's secrets in staging.\n\t */\n\tprofile?: string;\n\t/** For tests: an endpoint such as the AWS emulator. */\n\tendpoint?: string;\n}\n\n/** The parameter a stage's secrets are kept in. */\nexport function secretsParameterName(project: string, stage: string): string {\n\treturn `/gkm/${project}/${stage}/secrets`;\n}\n\nexport class AwsSecretsStore implements SecretsStore {\n\treadonly name = 'ssm';\n\n\tprivate client?: SSMClient;\n\n\tconstructor(private readonly options: AwsSecretsStoreOptions) {}\n\n\tprivate async ssm(): Promise<SSMClient> {\n\t\tif (this.client) return this.client;\n\n\t\tconst config: SSMClientConfig = { region: this.options.region };\n\t\tif (this.options.profile) {\n\t\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\t\tconfig.credentials = fromIni({ profile: this.options.profile });\n\t\t}\n\t\tif (this.options.endpoint) {\n\t\t\tconfig.endpoint = this.options.endpoint;\n\t\t}\n\n\t\tthis.client = new SSMClient(config);\n\t\treturn this.client;\n\t}\n\n\tasync read(stage: string): Promise<StageSecrets | null> {\n\t\tconst ssm = await this.ssm();\n\n\t\ttry {\n\t\t\tconst { Parameter } = await ssm.send(\n\t\t\t\tnew GetParameterCommand({\n\t\t\t\t\tName: secretsParameterName(this.options.project, stage),\n\t\t\t\t\tWithDecryption: true,\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn Parameter?.Value\n\t\t\t\t? (JSON.parse(Parameter.Value) as StageSecrets)\n\t\t\t\t: null;\n\t\t} catch (error) {\n\t\t\tif (error instanceof ParameterNotFound) return null;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync write(stage: string, secrets: StageSecrets): Promise<void> {\n\t\tconst ssm = await this.ssm();\n\n\t\tawait ssm.send(\n\t\t\tnew PutParameterCommand({\n\t\t\t\tName: secretsParameterName(this.options.project, stage),\n\t\t\t\tValue: JSON.stringify(secrets),\n\t\t\t\tType: 'SecureString',\n\t\t\t\tOverwrite: true,\n\t\t\t\tDescription: `gkm secrets for ${this.options.project}/${stage}`,\n\t\t\t}),\n\t\t);\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AAoCA,SAAgB,qBAAqB,SAAiB,OAAuB;CAC5E,OAAO,QAAQ,QAAQ,GAAG,MAAM;AACjC;AAEA,IAAa,kBAAb,MAAqD;CAKvB;CAJ7B,OAAgB;CAEhB;CAEA,YAAY,SAAkD;EAAjC,KAAA,UAAA;CAAkC;CAE/D,MAAc,MAA0B;EACvC,IAAI,KAAK,QAAQ,OAAO,KAAK;EAE7B,MAAM,SAA0B,EAAE,QAAQ,KAAK,QAAQ,OAAO;EAC9D,IAAI,KAAK,QAAQ,SAAS;GACzB,MAAM,EAAE,YAAY,MAAM,OAAO;GACjC,OAAO,cAAc,QAAQ,EAAE,SAAS,KAAK,QAAQ,QAAQ,CAAC;EAC/D;EACA,IAAI,KAAK,QAAQ,UAChB,OAAO,WAAW,KAAK,QAAQ;EAGhC,KAAK,SAAS,IAAI,UAAU,MAAM;EAClC,OAAO,KAAK;CACb;CAEA,MAAM,KAAK,OAA6C;EACvD,MAAM,MAAM,MAAM,KAAK,IAAI;EAE3B,IAAI;GACH,MAAM,EAAE,cAAc,MAAM,IAAI,KAC/B,IAAI,oBAAoB;IACvB,MAAM,qBAAqB,KAAK,QAAQ,SAAS,KAAK;IACtD,gBAAgB;GACjB,CAAC,CACF;GACA,OAAO,WAAW,QACd,KAAK,MAAM,UAAU,KAAK,IAC3B;EACJ,SAAS,OAAO;GACf,IAAI,iBAAiB,mBAAmB,OAAO;GAC/C,MAAM;EACP;CACD;CAEA,MAAM,MAAM,OAAe,SAAsC;EAGhE,OAAM,MAFY,KAAK,IAAI,EAAA,CAEjB,KACT,IAAI,oBAAoB;GACvB,MAAM,qBAAqB,KAAK,QAAQ,SAAS,KAAK;GACtD,OAAO,KAAK,UAAU,OAAO;GAC7B,MAAM;GACN,WAAW;GACX,aAAa,mBAAmB,KAAK,QAAQ,QAAQ,GAAG;EACzD,CAAC,CACF;CACD;AACD"}
@@ -81,14 +81,20 @@ async function bundleServer(options) {
81
81
  for (const ext of external) args.push(`--external:${ext}`);
82
82
  let masterKey;
83
83
  if (stage) {
84
- const { readStageSecrets, toEmbeddableSecrets, validateEnvironmentVariables, initStageSecrets, writeStageSecrets } = await import("./storage-BjTLjDDp.mjs").then((n) => n.a);
85
- const { encryptSecrets, generateDefineOptions } = await import("./index.mjs").then((n) => n.a);
86
- let secrets = await readStageSecrets(stage);
84
+ const { toEmbeddableSecrets, validateEnvironmentVariables, initStageSecrets } = await import("./storage-BzaUxmqe.mjs").then((n) => n.r);
85
+ const { FileSecretsStore, secretsStoreFor } = await import("./store-DeGATCS-.mjs").then((n) => n.r);
86
+ const { ConfigNotFound, loadWorkspaceSettings } = await import("./config.mjs");
87
+ const { encryptSecrets, generateDefineOptions } = await import("./index.mjs").then((n) => n.i);
88
+ const store = await loadWorkspaceSettings().then((workspace) => secretsStoreFor(workspace, stage), (error) => {
89
+ if (error instanceof ConfigNotFound) return new FileSecretsStore(process.cwd());
90
+ throw error;
91
+ });
92
+ let secrets = await store.read(stage);
87
93
  if (!secrets) {
88
94
  console.log(` Initializing secrets for stage "${stage}"...`);
89
95
  secrets = initStageSecrets(stage);
90
- await writeStageSecrets(secrets);
91
- console.log(` ✓ Created .gkm/secrets/${stage}.json`);
96
+ await store.write(stage, secrets);
97
+ console.log(` ✓ Created the "${stage}" secrets (${store.name})`);
92
98
  }
93
99
  if (constructs && constructs.length > 0) {
94
100
  console.log(" Analyzing environment variable requirements...");
@@ -102,7 +108,7 @@ async function bundleServer(options) {
102
108
  ...validation.missing.map((v) => ` ❌ ${v}`),
103
109
  "",
104
110
  "To fix this, either:",
105
- ` 1. Add the missing variables to .gkm/secrets/${stage}.json using:`,
111
+ ` 1. Add the missing variables to the "${stage}" secrets using:`,
106
112
  ` gkm secrets:set <KEY> <VALUE> --stage ${stage}`,
107
113
  "",
108
114
  ` 2. Or import from a JSON file:`,
@@ -145,4 +151,4 @@ async function bundleServer(options) {
145
151
  //#endregion
146
152
  export { bundleServer };
147
153
 
148
- //# sourceMappingURL=bundler-D9IPNvDt.mjs.map
154
+ //# sourceMappingURL=bundler--fyybf1N.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundler--fyybf1N.mjs","names":[],"sources":["../src/build/bundler.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { createRequire } from 'node:module';\nimport { dirname, join } from 'node:path';\nimport type { Construct } from '@geekmidas/constructs';\n\n/**\n * Banner to inject into ESM bundle for CJS compatibility.\n * Creates a `require` function using Node's createRequire for packages\n * that internally use CommonJS require() for Node builtins.\n */\nconst ESM_CJS_COMPAT_BANNER =\n\t'import { createRequire } from \"module\"; const require = createRequire(import.meta.url);';\n\nexport interface BundleOptions {\n\t/** Entry point file (e.g., .gkm/server/server.ts) */\n\tentryPoint: string;\n\t/** Output directory for bundled files */\n\toutputDir: string;\n\t/** Minify the output (default: true) */\n\tminify: boolean;\n\t/** Generate sourcemaps (default: false) */\n\tsourcemap: boolean;\n\t/** Packages to exclude from bundling */\n\texternal: string[];\n\t/** Stage for secrets injection (optional) */\n\tstage?: string;\n\t/** Constructs to validate environment variables for */\n\tconstructs?: Construct[];\n}\n\nexport interface BundleResult {\n\t/** Path to the bundled output */\n\toutputPath: string;\n\t/** Ephemeral master key for deployment (only if stage was provided) */\n\tmasterKey?: string;\n}\n\n/**\n * The esbuild binary, resolved from this package rather than from PATH.\n *\n * It used to be `npx esbuild`, which is two assumptions: that esbuild is\n * installed somewhere npx can find, and that the *app's* directory is where to\n * look. Neither holds — the CLI did not depend on esbuild at all, so a deploy\n * run from an app that had not installed it failed at the bundle step with\n * `sh: esbuild: command not found`, after provisioning had already happened.\n *\n * The same fix `bin/gkm.mjs` makes for tsx: resolve from this package's own\n * `node_modules`, so the tool the CLI shells out to is the one it depends on.\n */\nfunction esbuildBinary(): string {\n\tconst require = createRequire(import.meta.url);\n\n\ttry {\n\t\t// The package root, then its bin — `require.resolve('esbuild')` gives the\n\t\t// JS entry point, which is a library rather than the CLI.\n\t\tconst pkg = require.resolve('esbuild/package.json');\n\t\treturn join(dirname(pkg), 'bin', 'esbuild');\n\t} catch {\n\t\t// Better a PATH lookup than a hard failure: a project that installed\n\t\t// esbuild itself still works.\n\t\treturn 'esbuild';\n\t}\n}\n\n/**\n * Collect the environment variables a build cannot proceed without.\n *\n * Uses the SnifferEnvironmentParser to detect which env vars each service\n * reads, and keeps only the ones it cannot do without. `markOptional` is what\n * separates them: the sniffer already knows which reads went through\n * `.optional()` or `.default()`, and asking for that distinction is the\n * difference between a build that stops and one that proceeds correctly.\n *\n * Without it, a key that is *absent by design* failed the build. A surface\n * publishes `AUTH_COOKIE_DOMAIN` only when there is a domain to widen a cookie\n * to — one host has nothing to share it with, so the derivation correctly\n * yields nothing, and Better Auth reads it as optional for exactly that\n * reason. Treating every sniffed read as required made the honest answer\n * indistinguishable from a missing secret.\n *\n * @param constructs - Array of constructs to analyze\n * @returns Deduplicated, sorted names of the required variables\n */\nasync function collectRequiredEnvVars(\n\tconstructs: Construct[],\n): Promise<string[]> {\n\t// A key read optionally by one construct and required by another stays\n\t// required: the strictest read is the one that fails at runtime, and taking\n\t// the union of the required reads is what says so.\n\tconst required = new Set<string>();\n\n\tfor (const construct of constructs) {\n\t\tfor (const name of await construct.getEnvironment({ markOptional: true })) {\n\t\t\tif (!name.endsWith('?')) required.add(name);\n\t\t}\n\t}\n\n\treturn Array.from(required).sort();\n}\n\n/**\n * Bundle the server application using esbuild.\n * Creates a fully standalone bundle with all dependencies included.\n *\n * @param options - Bundle configuration options\n * @returns Bundle result with output path and optional master key\n */\n\nexport async function bundleServer(\n\toptions: BundleOptions,\n): Promise<BundleResult> {\n\tconst {\n\t\tentryPoint,\n\t\toutputDir,\n\t\tminify,\n\t\tsourcemap,\n\t\texternal,\n\t\tstage,\n\t\tconstructs,\n\t} = options;\n\n\t// Ensure output directory exists\n\tawait mkdir(outputDir, { recursive: true });\n\n\tconst mjsOutput = join(outputDir, 'server.mjs');\n\n\t// Build command-line arguments for esbuild\n\tconst args = [\n\t\tesbuildBinary(),\n\t\tentryPoint,\n\t\t'--bundle',\n\t\t'--platform=node',\n\t\t'--target=node22',\n\t\t'--format=esm',\n\t\t`--outfile=${mjsOutput}`,\n\t\t'--packages=bundle', // Bundle all dependencies for standalone output\n\t\t`--banner:js=${ESM_CJS_COMPAT_BANNER}`, // CJS compatibility for packages like pino\n\t];\n\n\tif (minify) {\n\t\targs.push('--minify');\n\t}\n\n\tif (sourcemap) {\n\t\targs.push('--sourcemap');\n\t}\n\n\t// Add external packages (user-specified)\n\tfor (const ext of external) {\n\t\targs.push(`--external:${ext}`);\n\t}\n\n\t// Handle secrets injection if stage is provided\n\tlet masterKey: string | undefined;\n\n\tif (stage) {\n\t\tconst {\n\t\t\ttoEmbeddableSecrets,\n\t\t\tvalidateEnvironmentVariables,\n\t\t\tinitStageSecrets,\n\t\t} = await import('../secrets/storage');\n\t\tconst { FileSecretsStore, secretsStoreFor } = await import(\n\t\t\t'../secrets/store'\n\t\t);\n\t\tconst { ConfigNotFound, loadWorkspaceSettings } = await import('../config');\n\t\tconst { encryptSecrets, generateDefineOptions } = await import(\n\t\t\t'../secrets/encryption'\n\t\t);\n\n\t\t// The stage's own store — SSM for a deployed stage kept there. Outside\n\t\t// any workspace nothing names one, so it is the file.\n\t\tconst store = await loadWorkspaceSettings().then(\n\t\t\t(workspace) => secretsStoreFor(workspace, stage),\n\t\t\t(error) => {\n\t\t\t\tif (error instanceof ConfigNotFound) {\n\t\t\t\t\treturn new FileSecretsStore(process.cwd());\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t},\n\t\t);\n\t\tlet secrets = await store.read(stage);\n\n\t\tif (!secrets) {\n\t\t\t// Auto-initialize secrets for the stage\n\t\t\tconsole.log(` Initializing secrets for stage \"${stage}\"...`);\n\t\t\tsecrets = initStageSecrets(stage);\n\t\t\tawait store.write(stage, secrets);\n\t\t\tconsole.log(` ✓ Created the \"${stage}\" secrets (${store.name})`);\n\t\t}\n\n\t\t// Validate environment variables if constructs are provided\n\t\tif (constructs && constructs.length > 0) {\n\t\t\tconsole.log(' Analyzing environment variable requirements...');\n\t\t\tconst requiredVars = await collectRequiredEnvVars(constructs);\n\n\t\t\tif (requiredVars.length > 0) {\n\t\t\t\tconst validation = validateEnvironmentVariables(requiredVars, secrets);\n\n\t\t\t\tif (!validation.valid) {\n\t\t\t\t\tconst errorMessage = [\n\t\t\t\t\t\t`Missing environment variables for stage \"${stage}\":`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t...validation.missing.map((v) => ` ❌ ${v}`),\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t'To fix this, either:',\n\t\t\t\t\t\t` 1. Add the missing variables to the \"${stage}\" secrets using:`,\n\t\t\t\t\t\t` gkm secrets:set <KEY> <VALUE> --stage ${stage}`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t` 2. Or import from a JSON file:`,\n\t\t\t\t\t\t` gkm secrets:import secrets.json --stage ${stage}`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t'Required variables:',\n\t\t\t\t\t\t...validation.required.map((v) =>\n\t\t\t\t\t\t\tvalidation.missing.includes(v) ? ` ❌ ${v}` : ` ✓ ${v}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t].join('\\n');\n\n\t\t\t\t\tthrow new Error(errorMessage);\n\t\t\t\t}\n\n\t\t\t\tconsole.log(\n\t\t\t\t\t` ✓ All ${requiredVars.length} required environment variables found`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Convert to embeddable format and encrypt\n\t\tconst embeddable = toEmbeddableSecrets(secrets);\n\t\tconst encrypted = encryptSecrets(embeddable);\n\t\tmasterKey = encrypted.masterKey;\n\n\t\t// Add define options for build-time injection using esbuild's --define:KEY=VALUE format\n\t\tconst defines = generateDefineOptions(encrypted);\n\t\tfor (const [key, value] of Object.entries(defines)) {\n\t\t\targs.push(`--define:${key}=${JSON.stringify(value)}`);\n\t\t}\n\n\t\tconsole.log(` Secrets encrypted for stage \"${stage}\"`);\n\t}\n\n\ttry {\n\t\t// Run esbuild with command-line arguments\n\t\tconst [cmd, ...cmdArgs] = args as [string, ...string[]];\n\t\tconst result = spawnSync(cmd, cmdArgs, {\n\t\t\tcwd: process.cwd(),\n\t\t\tstdio: 'inherit',\n\t\t\tshell: process.platform === 'win32', // Only use shell on Windows for npx resolution\n\t\t});\n\n\t\tif (result.error) {\n\t\t\tthrow result.error;\n\t\t}\n\t\tif (result.status !== 0) {\n\t\t\tthrow new Error(`esbuild exited with code ${result.status}`);\n\t\t}\n\n\t\t// Add shebang to the bundled file\n\t\tconst { readFile } = await import('node:fs/promises');\n\t\tconst content = await readFile(mjsOutput, 'utf-8');\n\t\tif (!content.startsWith('#!')) {\n\t\t\tawait writeFile(mjsOutput, `#!/usr/bin/env node\\n${content}`);\n\t\t}\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to bundle server: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t}\n\n\treturn {\n\t\toutputPath: mjsOutput,\n\t\tmasterKey,\n\t};\n}\n"],"mappings":"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"}
@@ -81,14 +81,20 @@ async function bundleServer(options) {
81
81
  for (const ext of external) args.push(`--external:${ext}`);
82
82
  let masterKey;
83
83
  if (stage) {
84
- const { readStageSecrets, toEmbeddableSecrets, validateEnvironmentVariables, initStageSecrets, writeStageSecrets } = await Promise.resolve().then(() => require("./storage-C2JC31V9.cjs")).then((n) => n.storage_exports);
84
+ const { toEmbeddableSecrets, validateEnvironmentVariables, initStageSecrets } = await Promise.resolve().then(() => require("./storage-CSGoC3bK.cjs")).then((n) => n.storage_exports);
85
+ const { FileSecretsStore, secretsStoreFor } = await Promise.resolve().then(() => require("./store-DQgxT_WR.cjs")).then((n) => n.store_exports);
86
+ const { ConfigNotFound, loadWorkspaceSettings } = await Promise.resolve().then(() => require("./config.cjs"));
85
87
  const { encryptSecrets, generateDefineOptions } = await Promise.resolve().then(() => require("./index.cjs")).then((n) => n.encryption_exports);
86
- let secrets = await readStageSecrets(stage);
88
+ const store = await loadWorkspaceSettings().then((workspace) => secretsStoreFor(workspace, stage), (error) => {
89
+ if (error instanceof ConfigNotFound) return new FileSecretsStore(process.cwd());
90
+ throw error;
91
+ });
92
+ let secrets = await store.read(stage);
87
93
  if (!secrets) {
88
94
  console.log(` Initializing secrets for stage "${stage}"...`);
89
95
  secrets = initStageSecrets(stage);
90
- await writeStageSecrets(secrets);
91
- console.log(` ✓ Created .gkm/secrets/${stage}.json`);
96
+ await store.write(stage, secrets);
97
+ console.log(` ✓ Created the "${stage}" secrets (${store.name})`);
92
98
  }
93
99
  if (constructs && constructs.length > 0) {
94
100
  console.log(" Analyzing environment variable requirements...");
@@ -102,7 +108,7 @@ async function bundleServer(options) {
102
108
  ...validation.missing.map((v) => ` ❌ ${v}`),
103
109
  "",
104
110
  "To fix this, either:",
105
- ` 1. Add the missing variables to .gkm/secrets/${stage}.json using:`,
111
+ ` 1. Add the missing variables to the "${stage}" secrets using:`,
106
112
  ` gkm secrets:set <KEY> <VALUE> --stage ${stage}`,
107
113
  "",
108
114
  ` 2. Or import from a JSON file:`,
@@ -145,4 +151,4 @@ async function bundleServer(options) {
145
151
  //#endregion
146
152
  exports.bundleServer = bundleServer;
147
153
 
148
- //# sourceMappingURL=bundler-CYF5mWi9.cjs.map
154
+ //# sourceMappingURL=bundler-By6_sXiL.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundler-By6_sXiL.cjs","names":["require","createRequire","join","dirname","mkdir","spawnSync","writeFile"],"sources":["../src/build/bundler.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { createRequire } from 'node:module';\nimport { dirname, join } from 'node:path';\nimport type { Construct } from '@geekmidas/constructs';\n\n/**\n * Banner to inject into ESM bundle for CJS compatibility.\n * Creates a `require` function using Node's createRequire for packages\n * that internally use CommonJS require() for Node builtins.\n */\nconst ESM_CJS_COMPAT_BANNER =\n\t'import { createRequire } from \"module\"; const require = createRequire(import.meta.url);';\n\nexport interface BundleOptions {\n\t/** Entry point file (e.g., .gkm/server/server.ts) */\n\tentryPoint: string;\n\t/** Output directory for bundled files */\n\toutputDir: string;\n\t/** Minify the output (default: true) */\n\tminify: boolean;\n\t/** Generate sourcemaps (default: false) */\n\tsourcemap: boolean;\n\t/** Packages to exclude from bundling */\n\texternal: string[];\n\t/** Stage for secrets injection (optional) */\n\tstage?: string;\n\t/** Constructs to validate environment variables for */\n\tconstructs?: Construct[];\n}\n\nexport interface BundleResult {\n\t/** Path to the bundled output */\n\toutputPath: string;\n\t/** Ephemeral master key for deployment (only if stage was provided) */\n\tmasterKey?: string;\n}\n\n/**\n * The esbuild binary, resolved from this package rather than from PATH.\n *\n * It used to be `npx esbuild`, which is two assumptions: that esbuild is\n * installed somewhere npx can find, and that the *app's* directory is where to\n * look. Neither holds — the CLI did not depend on esbuild at all, so a deploy\n * run from an app that had not installed it failed at the bundle step with\n * `sh: esbuild: command not found`, after provisioning had already happened.\n *\n * The same fix `bin/gkm.mjs` makes for tsx: resolve from this package's own\n * `node_modules`, so the tool the CLI shells out to is the one it depends on.\n */\nfunction esbuildBinary(): string {\n\tconst require = createRequire(import.meta.url);\n\n\ttry {\n\t\t// The package root, then its bin — `require.resolve('esbuild')` gives the\n\t\t// JS entry point, which is a library rather than the CLI.\n\t\tconst pkg = require.resolve('esbuild/package.json');\n\t\treturn join(dirname(pkg), 'bin', 'esbuild');\n\t} catch {\n\t\t// Better a PATH lookup than a hard failure: a project that installed\n\t\t// esbuild itself still works.\n\t\treturn 'esbuild';\n\t}\n}\n\n/**\n * Collect the environment variables a build cannot proceed without.\n *\n * Uses the SnifferEnvironmentParser to detect which env vars each service\n * reads, and keeps only the ones it cannot do without. `markOptional` is what\n * separates them: the sniffer already knows which reads went through\n * `.optional()` or `.default()`, and asking for that distinction is the\n * difference between a build that stops and one that proceeds correctly.\n *\n * Without it, a key that is *absent by design* failed the build. A surface\n * publishes `AUTH_COOKIE_DOMAIN` only when there is a domain to widen a cookie\n * to — one host has nothing to share it with, so the derivation correctly\n * yields nothing, and Better Auth reads it as optional for exactly that\n * reason. Treating every sniffed read as required made the honest answer\n * indistinguishable from a missing secret.\n *\n * @param constructs - Array of constructs to analyze\n * @returns Deduplicated, sorted names of the required variables\n */\nasync function collectRequiredEnvVars(\n\tconstructs: Construct[],\n): Promise<string[]> {\n\t// A key read optionally by one construct and required by another stays\n\t// required: the strictest read is the one that fails at runtime, and taking\n\t// the union of the required reads is what says so.\n\tconst required = new Set<string>();\n\n\tfor (const construct of constructs) {\n\t\tfor (const name of await construct.getEnvironment({ markOptional: true })) {\n\t\t\tif (!name.endsWith('?')) required.add(name);\n\t\t}\n\t}\n\n\treturn Array.from(required).sort();\n}\n\n/**\n * Bundle the server application using esbuild.\n * Creates a fully standalone bundle with all dependencies included.\n *\n * @param options - Bundle configuration options\n * @returns Bundle result with output path and optional master key\n */\n\nexport async function bundleServer(\n\toptions: BundleOptions,\n): Promise<BundleResult> {\n\tconst {\n\t\tentryPoint,\n\t\toutputDir,\n\t\tminify,\n\t\tsourcemap,\n\t\texternal,\n\t\tstage,\n\t\tconstructs,\n\t} = options;\n\n\t// Ensure output directory exists\n\tawait mkdir(outputDir, { recursive: true });\n\n\tconst mjsOutput = join(outputDir, 'server.mjs');\n\n\t// Build command-line arguments for esbuild\n\tconst args = [\n\t\tesbuildBinary(),\n\t\tentryPoint,\n\t\t'--bundle',\n\t\t'--platform=node',\n\t\t'--target=node22',\n\t\t'--format=esm',\n\t\t`--outfile=${mjsOutput}`,\n\t\t'--packages=bundle', // Bundle all dependencies for standalone output\n\t\t`--banner:js=${ESM_CJS_COMPAT_BANNER}`, // CJS compatibility for packages like pino\n\t];\n\n\tif (minify) {\n\t\targs.push('--minify');\n\t}\n\n\tif (sourcemap) {\n\t\targs.push('--sourcemap');\n\t}\n\n\t// Add external packages (user-specified)\n\tfor (const ext of external) {\n\t\targs.push(`--external:${ext}`);\n\t}\n\n\t// Handle secrets injection if stage is provided\n\tlet masterKey: string | undefined;\n\n\tif (stage) {\n\t\tconst {\n\t\t\ttoEmbeddableSecrets,\n\t\t\tvalidateEnvironmentVariables,\n\t\t\tinitStageSecrets,\n\t\t} = await import('../secrets/storage');\n\t\tconst { FileSecretsStore, secretsStoreFor } = await import(\n\t\t\t'../secrets/store'\n\t\t);\n\t\tconst { ConfigNotFound, loadWorkspaceSettings } = await import('../config');\n\t\tconst { encryptSecrets, generateDefineOptions } = await import(\n\t\t\t'../secrets/encryption'\n\t\t);\n\n\t\t// The stage's own store — SSM for a deployed stage kept there. Outside\n\t\t// any workspace nothing names one, so it is the file.\n\t\tconst store = await loadWorkspaceSettings().then(\n\t\t\t(workspace) => secretsStoreFor(workspace, stage),\n\t\t\t(error) => {\n\t\t\t\tif (error instanceof ConfigNotFound) {\n\t\t\t\t\treturn new FileSecretsStore(process.cwd());\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t},\n\t\t);\n\t\tlet secrets = await store.read(stage);\n\n\t\tif (!secrets) {\n\t\t\t// Auto-initialize secrets for the stage\n\t\t\tconsole.log(` Initializing secrets for stage \"${stage}\"...`);\n\t\t\tsecrets = initStageSecrets(stage);\n\t\t\tawait store.write(stage, secrets);\n\t\t\tconsole.log(` ✓ Created the \"${stage}\" secrets (${store.name})`);\n\t\t}\n\n\t\t// Validate environment variables if constructs are provided\n\t\tif (constructs && constructs.length > 0) {\n\t\t\tconsole.log(' Analyzing environment variable requirements...');\n\t\t\tconst requiredVars = await collectRequiredEnvVars(constructs);\n\n\t\t\tif (requiredVars.length > 0) {\n\t\t\t\tconst validation = validateEnvironmentVariables(requiredVars, secrets);\n\n\t\t\t\tif (!validation.valid) {\n\t\t\t\t\tconst errorMessage = [\n\t\t\t\t\t\t`Missing environment variables for stage \"${stage}\":`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t...validation.missing.map((v) => ` ❌ ${v}`),\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t'To fix this, either:',\n\t\t\t\t\t\t` 1. Add the missing variables to the \"${stage}\" secrets using:`,\n\t\t\t\t\t\t` gkm secrets:set <KEY> <VALUE> --stage ${stage}`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t` 2. Or import from a JSON file:`,\n\t\t\t\t\t\t` gkm secrets:import secrets.json --stage ${stage}`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t'Required variables:',\n\t\t\t\t\t\t...validation.required.map((v) =>\n\t\t\t\t\t\t\tvalidation.missing.includes(v) ? ` ❌ ${v}` : ` ✓ ${v}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t].join('\\n');\n\n\t\t\t\t\tthrow new Error(errorMessage);\n\t\t\t\t}\n\n\t\t\t\tconsole.log(\n\t\t\t\t\t` ✓ All ${requiredVars.length} required environment variables found`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Convert to embeddable format and encrypt\n\t\tconst embeddable = toEmbeddableSecrets(secrets);\n\t\tconst encrypted = encryptSecrets(embeddable);\n\t\tmasterKey = encrypted.masterKey;\n\n\t\t// Add define options for build-time injection using esbuild's --define:KEY=VALUE format\n\t\tconst defines = generateDefineOptions(encrypted);\n\t\tfor (const [key, value] of Object.entries(defines)) {\n\t\t\targs.push(`--define:${key}=${JSON.stringify(value)}`);\n\t\t}\n\n\t\tconsole.log(` Secrets encrypted for stage \"${stage}\"`);\n\t}\n\n\ttry {\n\t\t// Run esbuild with command-line arguments\n\t\tconst [cmd, ...cmdArgs] = args as [string, ...string[]];\n\t\tconst result = spawnSync(cmd, cmdArgs, {\n\t\t\tcwd: process.cwd(),\n\t\t\tstdio: 'inherit',\n\t\t\tshell: process.platform === 'win32', // Only use shell on Windows for npx resolution\n\t\t});\n\n\t\tif (result.error) {\n\t\t\tthrow result.error;\n\t\t}\n\t\tif (result.status !== 0) {\n\t\t\tthrow new Error(`esbuild exited with code ${result.status}`);\n\t\t}\n\n\t\t// Add shebang to the bundled file\n\t\tconst { readFile } = await import('node:fs/promises');\n\t\tconst content = await readFile(mjsOutput, 'utf-8');\n\t\tif (!content.startsWith('#!')) {\n\t\t\tawait writeFile(mjsOutput, `#!/usr/bin/env node\\n${content}`);\n\t\t}\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to bundle server: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t}\n\n\treturn {\n\t\toutputPath: mjsOutput,\n\t\tmasterKey,\n\t};\n}\n"],"mappings":"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"}