@geekmidas/cli 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/dist/bundler-BjholBlA.cjs +131 -0
  2. package/dist/bundler-BjholBlA.cjs.map +1 -0
  3. package/dist/bundler-DWctKN1z.mjs +130 -0
  4. package/dist/bundler-DWctKN1z.mjs.map +1 -0
  5. package/dist/config.d.cts +1 -1
  6. package/dist/config.d.mts +1 -1
  7. package/dist/dokploy-api-B7KxOQr3.cjs +3 -0
  8. package/dist/dokploy-api-C7F9VykY.cjs +317 -0
  9. package/dist/dokploy-api-C7F9VykY.cjs.map +1 -0
  10. package/dist/dokploy-api-CaETb2L6.mjs +305 -0
  11. package/dist/dokploy-api-CaETb2L6.mjs.map +1 -0
  12. package/dist/dokploy-api-DHvfmWbi.mjs +3 -0
  13. package/dist/{encryption-Dyf_r1h-.cjs → encryption-D7Efcdi9.cjs} +1 -1
  14. package/dist/{encryption-Dyf_r1h-.cjs.map → encryption-D7Efcdi9.cjs.map} +1 -1
  15. package/dist/{encryption-C8H-38Yy.mjs → encryption-h4Nb6W-M.mjs} +1 -1
  16. package/dist/{encryption-C8H-38Yy.mjs.map → encryption-h4Nb6W-M.mjs.map} +1 -1
  17. package/dist/index.cjs +1520 -1136
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.mjs +1520 -1136
  20. package/dist/index.mjs.map +1 -1
  21. package/dist/{openapi-Bt_1FDpT.cjs → openapi-C89hhkZC.cjs} +3 -3
  22. package/dist/{openapi-Bt_1FDpT.cjs.map → openapi-C89hhkZC.cjs.map} +1 -1
  23. package/dist/{openapi-BfFlOBCG.mjs → openapi-CZVcfxk-.mjs} +3 -3
  24. package/dist/{openapi-BfFlOBCG.mjs.map → openapi-CZVcfxk-.mjs.map} +1 -1
  25. package/dist/{openapi-react-query-B6XTeGqS.mjs → openapi-react-query-CM2_qlW9.mjs} +1 -1
  26. package/dist/{openapi-react-query-B6XTeGqS.mjs.map → openapi-react-query-CM2_qlW9.mjs.map} +1 -1
  27. package/dist/{openapi-react-query-B-sNWHFU.cjs → openapi-react-query-iKjfLzff.cjs} +1 -1
  28. package/dist/{openapi-react-query-B-sNWHFU.cjs.map → openapi-react-query-iKjfLzff.cjs.map} +1 -1
  29. package/dist/openapi-react-query.cjs +1 -1
  30. package/dist/openapi-react-query.mjs +1 -1
  31. package/dist/openapi.cjs +1 -1
  32. package/dist/openapi.d.cts +1 -1
  33. package/dist/openapi.d.mts +1 -1
  34. package/dist/openapi.mjs +1 -1
  35. package/dist/{storage-C9PU_30f.mjs → storage-BaOP55oq.mjs} +48 -2
  36. package/dist/storage-BaOP55oq.mjs.map +1 -0
  37. package/dist/{storage-BXoJvmv2.cjs → storage-Bn3K9Ccu.cjs} +59 -1
  38. package/dist/storage-Bn3K9Ccu.cjs.map +1 -0
  39. package/dist/storage-UfyTn7Zm.cjs +7 -0
  40. package/dist/storage-nkGIjeXt.mjs +3 -0
  41. package/dist/{types-BR0M2v_c.d.mts → types-BgaMXsUa.d.cts} +3 -1
  42. package/dist/{types-BR0M2v_c.d.mts.map → types-BgaMXsUa.d.cts.map} +1 -1
  43. package/dist/{types-BhkZc-vm.d.cts → types-iFk5ms7y.d.mts} +3 -1
  44. package/dist/{types-BhkZc-vm.d.cts.map → types-iFk5ms7y.d.mts.map} +1 -1
  45. package/package.json +4 -4
  46. package/src/auth/__tests__/credentials.spec.ts +127 -0
  47. package/src/auth/__tests__/index.spec.ts +69 -0
  48. package/src/auth/credentials.ts +33 -0
  49. package/src/auth/index.ts +57 -50
  50. package/src/build/__tests__/bundler.spec.ts +444 -0
  51. package/src/build/__tests__/endpoint-analyzer.spec.ts +623 -0
  52. package/src/build/__tests__/handler-templates.spec.ts +272 -0
  53. package/src/build/bundler.ts +126 -8
  54. package/src/build/index.ts +31 -0
  55. package/src/build/types.ts +6 -0
  56. package/src/deploy/__tests__/dokploy-api.spec.ts +698 -0
  57. package/src/deploy/__tests__/dokploy.spec.ts +196 -6
  58. package/src/deploy/__tests__/index.spec.ts +339 -0
  59. package/src/deploy/__tests__/init.spec.ts +147 -16
  60. package/src/deploy/docker.ts +32 -3
  61. package/src/deploy/dokploy-api.ts +581 -0
  62. package/src/deploy/dokploy.ts +66 -93
  63. package/src/deploy/index.ts +587 -32
  64. package/src/deploy/init.ts +192 -249
  65. package/src/deploy/types.ts +19 -1
  66. package/src/dev/__tests__/index.spec.ts +95 -0
  67. package/src/docker/__tests__/templates.spec.ts +144 -0
  68. package/src/docker/index.ts +96 -6
  69. package/src/docker/templates.ts +114 -27
  70. package/src/generators/EndpointGenerator.ts +2 -2
  71. package/src/index.ts +34 -13
  72. package/src/secrets/__tests__/storage.spec.ts +208 -0
  73. package/src/secrets/storage.ts +73 -0
  74. package/src/types.ts +2 -0
  75. package/dist/bundler-DRXCw_YR.mjs +0 -70
  76. package/dist/bundler-DRXCw_YR.mjs.map +0 -1
  77. package/dist/bundler-WsEvH_b2.cjs +0 -71
  78. package/dist/bundler-WsEvH_b2.cjs.map +0 -1
  79. package/dist/storage-BUYQJgz7.cjs +0 -4
  80. package/dist/storage-BXoJvmv2.cjs.map +0 -1
  81. package/dist/storage-C9PU_30f.mjs.map +0 -1
  82. package/dist/storage-DLJAYxzJ.mjs +0 -3
@@ -0,0 +1,272 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import type { EndpointAnalysis, EndpointFeatures } from '../endpoint-analyzer';
3
+ import {
4
+ generateOptimizedImports,
5
+ generateValidatorFactories,
6
+ } from '../handler-templates';
7
+
8
+ // Helper to create endpoint features
9
+ function createFeatures(
10
+ overrides: Partial<EndpointFeatures> = {},
11
+ ): EndpointFeatures {
12
+ return {
13
+ hasAuth: false,
14
+ hasServices: false,
15
+ hasDatabase: false,
16
+ hasBodyValidation: false,
17
+ hasQueryValidation: false,
18
+ hasParamValidation: false,
19
+ hasAudits: false,
20
+ hasEvents: false,
21
+ hasRateLimit: false,
22
+ hasRls: false,
23
+ hasOutputValidation: false,
24
+ ...overrides,
25
+ };
26
+ }
27
+
28
+ // Helper to create analysis
29
+ function createAnalysis(
30
+ overrides: Partial<EndpointAnalysis> = {},
31
+ ): EndpointAnalysis {
32
+ return {
33
+ route: '/test',
34
+ method: 'GET',
35
+ exportName: 'testEndpoint',
36
+ tier: 'minimal',
37
+ serviceNames: [],
38
+ features: createFeatures(),
39
+ ...overrides,
40
+ };
41
+ }
42
+
43
+ describe('handler-templates', () => {
44
+ describe('generateOptimizedImports', () => {
45
+ it('should always include base imports', () => {
46
+ const result = generateOptimizedImports([]);
47
+
48
+ expect(result).toContain('import type { EnvironmentParser }');
49
+ expect(result).toContain('import type { Logger }');
50
+ expect(result).toContain('import type { Hono }');
51
+ expect(result).toContain('import { Endpoint }');
52
+ });
53
+
54
+ it('should include validator import when body validation needed', () => {
55
+ const analyses = [
56
+ createAnalysis({
57
+ features: createFeatures({ hasBodyValidation: true }),
58
+ }),
59
+ ];
60
+
61
+ const result = generateOptimizedImports(analyses);
62
+ expect(result).toContain('import { validator }');
63
+ });
64
+
65
+ it('should include validator import when query validation needed', () => {
66
+ const analyses = [
67
+ createAnalysis({
68
+ features: createFeatures({ hasQueryValidation: true }),
69
+ }),
70
+ ];
71
+
72
+ const result = generateOptimizedImports(analyses);
73
+ expect(result).toContain('import { validator }');
74
+ });
75
+
76
+ it('should include validator import when param validation needed', () => {
77
+ const analyses = [
78
+ createAnalysis({
79
+ features: createFeatures({ hasParamValidation: true }),
80
+ }),
81
+ ];
82
+
83
+ const result = generateOptimizedImports(analyses);
84
+ expect(result).toContain('import { validator }');
85
+ });
86
+
87
+ it('should include ResponseBuilder for standard tier', () => {
88
+ const analyses = [
89
+ createAnalysis({
90
+ tier: 'standard',
91
+ features: createFeatures({ hasAuth: true }),
92
+ }),
93
+ ];
94
+
95
+ const result = generateOptimizedImports(analyses);
96
+ expect(result).toContain('import { ResponseBuilder }');
97
+ });
98
+
99
+ it('should include ResponseBuilder for full tier', () => {
100
+ const analyses = [
101
+ createAnalysis({
102
+ tier: 'full',
103
+ features: createFeatures({ hasAudits: true }),
104
+ }),
105
+ ];
106
+
107
+ const result = generateOptimizedImports(analyses);
108
+ expect(result).toContain('import { ResponseBuilder }');
109
+ });
110
+
111
+ it('should include ServiceDiscovery when services are used', () => {
112
+ const analyses = [
113
+ createAnalysis({
114
+ features: createFeatures({ hasServices: true }),
115
+ }),
116
+ ];
117
+
118
+ const result = generateOptimizedImports(analyses);
119
+ expect(result).toContain('import { ServiceDiscovery }');
120
+ });
121
+
122
+ it('should include ServiceDiscovery when database is used', () => {
123
+ const analyses = [
124
+ createAnalysis({
125
+ features: createFeatures({ hasDatabase: true }),
126
+ }),
127
+ ];
128
+
129
+ const result = generateOptimizedImports(analyses);
130
+ expect(result).toContain('import { ServiceDiscovery }');
131
+ });
132
+
133
+ it('should include events import when events are used', () => {
134
+ const analyses = [
135
+ createAnalysis({
136
+ features: createFeatures({ hasEvents: true }),
137
+ }),
138
+ ];
139
+
140
+ const result = generateOptimizedImports(analyses);
141
+ expect(result).toContain('import { publishConstructEvents }');
142
+ });
143
+
144
+ it('should include audit imports when audits are used', () => {
145
+ const analyses = [
146
+ createAnalysis({
147
+ features: createFeatures({ hasAudits: true }),
148
+ }),
149
+ ];
150
+
151
+ const result = generateOptimizedImports(analyses);
152
+ expect(result).toContain('createAuditContext');
153
+ expect(result).toContain('withAuditableEndpointTransaction');
154
+ });
155
+
156
+ it('should include createError when rate limiting is used', () => {
157
+ const analyses = [
158
+ createAnalysis({
159
+ features: createFeatures({ hasRateLimit: true }),
160
+ }),
161
+ ];
162
+
163
+ const result = generateOptimizedImports(analyses);
164
+ expect(result).toContain('import { createError }');
165
+ });
166
+
167
+ it('should include RLS imports when RLS is used', () => {
168
+ const analyses = [
169
+ createAnalysis({
170
+ features: createFeatures({ hasRls: true }),
171
+ }),
172
+ ];
173
+
174
+ const result = generateOptimizedImports(analyses);
175
+ expect(result).toContain('withRlsContext');
176
+ expect(result).toContain('extractRlsContext');
177
+ });
178
+
179
+ it('should not include optional imports when not needed', () => {
180
+ const analyses = [createAnalysis()];
181
+
182
+ const result = generateOptimizedImports(analyses);
183
+ expect(result).not.toContain('import { validator }');
184
+ expect(result).not.toContain('ServiceDiscovery');
185
+ expect(result).not.toContain('publishConstructEvents');
186
+ expect(result).not.toContain('createAuditContext');
187
+ expect(result).not.toContain('createError');
188
+ expect(result).not.toContain('withRlsContext');
189
+ });
190
+ });
191
+
192
+ describe('generateValidatorFactories', () => {
193
+ it('should return empty string when no validation needed', () => {
194
+ const result = generateValidatorFactories([createAnalysis()]);
195
+ expect(result).toBe('');
196
+ });
197
+
198
+ it('should generate body validator when body validation needed', () => {
199
+ const analyses = [
200
+ createAnalysis({
201
+ features: createFeatures({ hasBodyValidation: true }),
202
+ }),
203
+ ];
204
+
205
+ const result = generateValidatorFactories(analyses);
206
+ expect(result).toContain('validateBody');
207
+ expect(result).toContain("validator('json'");
208
+ expect(result).toContain('endpoint.input?.body');
209
+ });
210
+
211
+ it('should generate query validator when query validation needed', () => {
212
+ const analyses = [
213
+ createAnalysis({
214
+ features: createFeatures({ hasQueryValidation: true }),
215
+ }),
216
+ ];
217
+
218
+ const result = generateValidatorFactories(analyses);
219
+ expect(result).toContain('validateQuery');
220
+ expect(result).toContain("validator('query'");
221
+ expect(result).toContain('endpoint.input?.query');
222
+ });
223
+
224
+ it('should generate params validator when param validation needed', () => {
225
+ const analyses = [
226
+ createAnalysis({
227
+ features: createFeatures({ hasParamValidation: true }),
228
+ }),
229
+ ];
230
+
231
+ const result = generateValidatorFactories(analyses);
232
+ expect(result).toContain('validateParams');
233
+ expect(result).toContain("validator('param'");
234
+ expect(result).toContain('endpoint.input?.params');
235
+ });
236
+
237
+ it('should generate all validators when all validation types needed', () => {
238
+ const analyses = [
239
+ createAnalysis({
240
+ features: createFeatures({
241
+ hasBodyValidation: true,
242
+ hasQueryValidation: true,
243
+ hasParamValidation: true,
244
+ }),
245
+ }),
246
+ ];
247
+
248
+ const result = generateValidatorFactories(analyses);
249
+ expect(result).toContain('validateBody');
250
+ expect(result).toContain('validateQuery');
251
+ expect(result).toContain('validateParams');
252
+ });
253
+
254
+ it('should handle multiple endpoints with different validation', () => {
255
+ const analyses = [
256
+ createAnalysis({
257
+ exportName: 'endpoint1',
258
+ features: createFeatures({ hasBodyValidation: true }),
259
+ }),
260
+ createAnalysis({
261
+ exportName: 'endpoint2',
262
+ features: createFeatures({ hasQueryValidation: true }),
263
+ }),
264
+ ];
265
+
266
+ const result = generateValidatorFactories(analyses);
267
+ expect(result).toContain('validateBody');
268
+ expect(result).toContain('validateQuery');
269
+ expect(result).not.toContain('validateParams');
270
+ });
271
+ });
272
+ });
@@ -1,7 +1,8 @@
1
- import { execSync } from 'node:child_process';
1
+ import { spawnSync } from 'node:child_process';
2
2
  import { existsSync } from 'node:fs';
3
3
  import { mkdir, rename, writeFile } from 'node:fs/promises';
4
4
  import { join } from 'node:path';
5
+ import type { Construct } from '@geekmidas/constructs';
5
6
 
6
7
  export interface BundleOptions {
7
8
  /** Entry point file (e.g., .gkm/server/server.ts) */
@@ -16,6 +17,14 @@ export interface BundleOptions {
16
17
  external: string[];
17
18
  /** Stage for secrets injection (optional) */
18
19
  stage?: string;
20
+ /** Constructs to validate environment variables for */
21
+ constructs?: Construct[];
22
+ /** Docker compose services configured (for auto-populating env vars) */
23
+ dockerServices?: {
24
+ postgres?: boolean;
25
+ redis?: boolean;
26
+ rabbitmq?: boolean;
27
+ };
19
28
  }
20
29
 
21
30
  export interface BundleResult {
@@ -25,16 +34,58 @@ export interface BundleResult {
25
34
  masterKey?: string;
26
35
  }
27
36
 
37
+ /**
38
+ * Collect all required environment variables from constructs.
39
+ * Uses the SnifferEnvironmentParser to detect which env vars each service needs.
40
+ *
41
+ * @param constructs - Array of constructs to analyze
42
+ * @returns Deduplicated array of required environment variable names
43
+ */
44
+ async function collectRequiredEnvVars(
45
+ constructs: Construct[],
46
+ ): Promise<string[]> {
47
+ const allEnvVars = new Set<string>();
48
+
49
+ for (const construct of constructs) {
50
+ const envVars = await construct.getEnvironment();
51
+ envVars.forEach((v) => allEnvVars.add(v));
52
+ }
53
+
54
+ return Array.from(allEnvVars).sort();
55
+ }
56
+
28
57
  /**
29
58
  * Bundle the server application using tsdown
30
59
  *
31
60
  * @param options - Bundle configuration options
32
61
  * @returns Bundle result with output path and optional master key
33
62
  */
63
+ /** Default env var values for docker compose services */
64
+ const DOCKER_SERVICE_ENV_VARS: Record<string, Record<string, string>> = {
65
+ postgres: {
66
+ DATABASE_URL: 'postgresql://postgres:postgres@postgres:5432/app',
67
+ },
68
+ redis: {
69
+ REDIS_URL: 'redis://redis:6379',
70
+ },
71
+ rabbitmq: {
72
+ RABBITMQ_URL: 'amqp://rabbitmq:5672',
73
+ },
74
+ };
75
+
34
76
  export async function bundleServer(
35
77
  options: BundleOptions,
36
78
  ): Promise<BundleResult> {
37
- const { entryPoint, outputDir, minify, sourcemap, external, stage } = options;
79
+ const {
80
+ entryPoint,
81
+ outputDir,
82
+ minify,
83
+ sourcemap,
84
+ external,
85
+ stage,
86
+ constructs,
87
+ dockerServices,
88
+ } = options;
38
89
 
39
90
  // Ensure output directory exists
40
91
  await mkdir(outputDir, { recursive: true });
@@ -76,9 +127,11 @@ export async function bundleServer(
76
127
  let masterKey: string | undefined;
77
128
 
78
129
  if (stage) {
79
- const { readStageSecrets, toEmbeddableSecrets } = await import(
80
- '../secrets/storage'
81
- );
130
+ const {
131
+ readStageSecrets,
132
+ toEmbeddableSecrets,
133
+ validateEnvironmentVariables,
134
+ } = await import('../secrets/storage');
82
135
  const { encryptSecrets, generateDefineOptions } = await import(
83
136
  '../secrets/encryption'
84
137
  );
@@ -91,15 +144,69 @@ export async function bundleServer(
91
144
  );
92
145
  }
93
146
 
147
+ // Auto-populate env vars from docker compose services
148
+ if (dockerServices) {
149
+ for (const [service, enabled] of Object.entries(dockerServices)) {
150
+ if (enabled && DOCKER_SERVICE_ENV_VARS[service]) {
151
+ for (const [envVar, defaultValue] of Object.entries(
152
+ DOCKER_SERVICE_ENV_VARS[service],
153
+ )) {
154
+ // Check if not already in urls or custom
155
+ const urlKey = envVar as keyof typeof secrets.urls;
156
+ if (!secrets.urls[urlKey] && !secrets.custom[envVar]) {
157
+ secrets.urls[urlKey] = defaultValue;
158
+ console.log(` Auto-populated ${envVar} from docker compose`);
159
+ }
160
+ }
161
+ }
162
+ }
163
+ }
164
+
165
+ // Validate environment variables if constructs are provided
166
+ if (constructs && constructs.length > 0) {
167
+ console.log(' Analyzing environment variable requirements...');
168
+ const requiredVars = await collectRequiredEnvVars(constructs);
169
+
170
+ if (requiredVars.length > 0) {
171
+ const validation = validateEnvironmentVariables(requiredVars, secrets);
172
+
173
+ if (!validation.valid) {
174
+ const errorMessage = [
175
+ `Missing environment variables for stage "${stage}":`,
176
+ '',
177
+ ...validation.missing.map((v) => ` ❌ ${v}`),
178
+ '',
179
+ 'To fix this, either:',
180
+ ` 1. Add the missing variables to .gkm/secrets/${stage}.json using:`,
181
+ ` gkm secrets:set <KEY> <VALUE> --stage ${stage}`,
182
+ '',
183
+ ` 2. Or import from a JSON file:`,
184
+ ` gkm secrets:import secrets.json --stage ${stage}`,
185
+ '',
186
+ 'Required variables:',
187
+ ...validation.required.map((v) =>
188
+ validation.missing.includes(v) ? ` ❌ ${v}` : ` ✓ ${v}`,
189
+ ),
190
+ ].join('\n');
191
+
192
+ throw new Error(errorMessage);
193
+ }
194
+
195
+ console.log(
196
+ ` ✓ All ${requiredVars.length} required environment variables found`,
197
+ );
198
+ }
199
+ }
200
+
94
201
  // Convert to embeddable format and encrypt
95
202
  const embeddable = toEmbeddableSecrets(secrets);
96
203
  const encrypted = encryptSecrets(embeddable);
97
204
  masterKey = encrypted.masterKey;
98
205
 
99
- // Add define options for build-time injection
206
+ // Add define options for build-time injection using tsdown's --env.* format
100
207
  const defines = generateDefineOptions(encrypted);
101
208
  for (const [key, value] of Object.entries(defines)) {
102
- args.push('--define', `${key}=${value}`);
209
+ args.push(`--env.${key}`, value);
103
210
  }
104
211
 
105
212
  console.log(` Secrets encrypted for stage "${stage}"`);
@@ -109,11 +216,22 @@ export async function bundleServer(
109
216
 
110
217
  try {
111
218
  // Run tsdown with command-line arguments
112
- execSync(args.join(' '), {
219
+ // Use spawnSync with args array to avoid shell escaping issues with --define values
220
+ // args is always populated with ['npx', 'tsdown', ...] so cmd is never undefined
221
+ const [cmd, ...cmdArgs] = args as [string, ...string[]];
222
+ const result = spawnSync(cmd, cmdArgs, {
113
223
  cwd: process.cwd(),
114
224
  stdio: 'inherit',
225
+ shell: process.platform === 'win32', // Only use shell on Windows for npx resolution
115
226
  });
116
227
 
228
+ if (result.error) {
229
+ throw result.error;
230
+ }
231
+ if (result.status !== 0) {
232
+ throw new Error(`tsdown exited with code ${result.status}`);
233
+ }
234
+
117
235
  // Rename output to .mjs for explicit ESM
118
236
  // tsdown outputs as server.js for ESM format
119
237
  const jsOutput = join(outputDir, 'server.js');
@@ -93,6 +93,22 @@ export async function buildCommand(
93
93
  logger.log(`🪝 Server hooks enabled`);
94
94
  }
95
95
 
96
+ // Extract docker compose services for env var auto-population
97
+ const services = config.docker?.compose?.services;
98
+ const dockerServices = services
99
+ ? Array.isArray(services)
100
+ ? {
101
+ postgres: services.includes('postgres'),
102
+ redis: services.includes('redis'),
103
+ rabbitmq: services.includes('rabbitmq'),
104
+ }
105
+ : {
106
+ postgres: Boolean(services.postgres),
107
+ redis: Boolean(services.redis),
108
+ rabbitmq: Boolean(services.rabbitmq),
109
+ }
110
+ : undefined;
111
+
96
112
  const buildContext: BuildContext = {
97
113
  envParserPath,
98
114
  envParserImportPattern,
@@ -102,6 +118,7 @@ export async function buildCommand(
102
118
  studio,
103
119
  hooks,
104
120
  production,
121
+ dockerServices,
105
122
  };
106
123
 
107
124
  // Initialize generators
@@ -236,6 +253,18 @@ async function buildForProvider(
236
253
  if (context.production?.bundle && !skipBundle) {
237
254
  logger.log(`\n📦 Bundling production server...`);
238
255
  const { bundleServer } = await import('./bundler');
256
+
257
+ // Collect all constructs for environment variable validation
258
+ const allConstructs = [
259
+ ...endpoints.map((e) => e.construct),
260
+ ...functions.map((f) => f.construct),
261
+ ...crons.map((c) => c.construct),
262
+ ...subscribers.map((s) => s.construct),
263
+ ];
264
+
265
+ // Get docker compose services for auto-populating env vars
266
+ const dockerServices = context.dockerServices;
267
+
239
268
  const bundleResult = await bundleServer({
240
269
  entryPoint: join(outputDir, 'server.ts'),
241
270
  outputDir: join(outputDir, 'dist'),
@@ -243,6 +272,8 @@ async function buildForProvider(
243
272
  sourcemap: false,
244
273
  external: context.production.external,
245
274
  stage,
275
+ constructs: allConstructs,
276
+ dockerServices,
246
277
  });
247
278
  masterKey = bundleResult.masterKey;
248
279
  logger.log(`✅ Bundle complete: .gkm/server/dist/server.mjs`);
@@ -84,6 +84,12 @@ export interface BuildContext {
84
84
  hooks?: NormalizedHooksConfig;
85
85
  /** Production build configuration */
86
86
  production?: NormalizedProductionConfig;
87
+ /** Docker compose services for auto-populating env vars */
88
+ dockerServices?: {
89
+ postgres?: boolean;
90
+ redis?: boolean;
91
+ rabbitmq?: boolean;
92
+ };
87
93
  }
88
94
 
89
95
  export interface ProviderBuildResult {