@geekmidas/cli 0.10.0 → 0.13.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 (146) hide show
  1. package/README.md +525 -0
  2. package/dist/bundler-B1qy9b-j.cjs +112 -0
  3. package/dist/bundler-B1qy9b-j.cjs.map +1 -0
  4. package/dist/bundler-DskIqW2t.mjs +111 -0
  5. package/dist/bundler-DskIqW2t.mjs.map +1 -0
  6. package/dist/{config-C9aXOHBe.cjs → config-AmInkU7k.cjs} +8 -8
  7. package/dist/config-AmInkU7k.cjs.map +1 -0
  8. package/dist/{config-BrkUalUh.mjs → config-DYULeEv8.mjs} +3 -3
  9. package/dist/config-DYULeEv8.mjs.map +1 -0
  10. package/dist/config.cjs +1 -1
  11. package/dist/config.d.cts +1 -1
  12. package/dist/config.d.mts +1 -1
  13. package/dist/config.mjs +1 -1
  14. package/dist/encryption-C8H-38Yy.mjs +42 -0
  15. package/dist/encryption-C8H-38Yy.mjs.map +1 -0
  16. package/dist/encryption-Dyf_r1h-.cjs +44 -0
  17. package/dist/encryption-Dyf_r1h-.cjs.map +1 -0
  18. package/dist/index.cjs +2123 -179
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.mjs +2141 -192
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/{openapi-CZLI4QTr.mjs → openapi-BfFlOBCG.mjs} +801 -38
  23. package/dist/openapi-BfFlOBCG.mjs.map +1 -0
  24. package/dist/{openapi-BeHLKcwP.cjs → openapi-Bt_1FDpT.cjs} +794 -31
  25. package/dist/openapi-Bt_1FDpT.cjs.map +1 -0
  26. package/dist/{openapi-react-query-o5iMi8tz.cjs → openapi-react-query-B-sNWHFU.cjs} +5 -5
  27. package/dist/openapi-react-query-B-sNWHFU.cjs.map +1 -0
  28. package/dist/{openapi-react-query-CcciaVu5.mjs → openapi-react-query-B6XTeGqS.mjs} +5 -5
  29. package/dist/openapi-react-query-B6XTeGqS.mjs.map +1 -0
  30. package/dist/openapi-react-query.cjs +1 -1
  31. package/dist/openapi-react-query.d.cts.map +1 -1
  32. package/dist/openapi-react-query.d.mts.map +1 -1
  33. package/dist/openapi-react-query.mjs +1 -1
  34. package/dist/openapi.cjs +2 -2
  35. package/dist/openapi.d.cts +1 -1
  36. package/dist/openapi.d.cts.map +1 -1
  37. package/dist/openapi.d.mts +1 -1
  38. package/dist/openapi.d.mts.map +1 -1
  39. package/dist/openapi.mjs +2 -2
  40. package/dist/storage-BOOpAF8N.cjs +5 -0
  41. package/dist/storage-Bj1E26lU.cjs +187 -0
  42. package/dist/storage-Bj1E26lU.cjs.map +1 -0
  43. package/dist/storage-kSxTjkNb.mjs +133 -0
  44. package/dist/storage-kSxTjkNb.mjs.map +1 -0
  45. package/dist/storage-tgZSUnKl.mjs +3 -0
  46. package/dist/{types-b-vwGpqc.d.cts → types-BR0M2v_c.d.mts} +100 -1
  47. package/dist/types-BR0M2v_c.d.mts.map +1 -0
  48. package/dist/{types-DXgiA1sF.d.mts → types-BhkZc-vm.d.cts} +100 -1
  49. package/dist/types-BhkZc-vm.d.cts.map +1 -0
  50. package/examples/cron-example.ts +27 -27
  51. package/examples/env.ts +27 -27
  52. package/examples/function-example.ts +31 -31
  53. package/examples/gkm.config.json +20 -20
  54. package/examples/gkm.config.ts +8 -8
  55. package/examples/gkm.minimal.config.json +5 -5
  56. package/examples/gkm.production.config.json +25 -25
  57. package/examples/logger.ts +2 -2
  58. package/package.json +6 -6
  59. package/src/__tests__/EndpointGenerator.hooks.spec.ts +191 -191
  60. package/src/__tests__/config.spec.ts +55 -55
  61. package/src/__tests__/loadEnvFiles.spec.ts +93 -93
  62. package/src/__tests__/normalizeHooksConfig.spec.ts +58 -58
  63. package/src/__tests__/openapi-react-query.spec.ts +497 -497
  64. package/src/__tests__/openapi.spec.ts +428 -428
  65. package/src/__tests__/test-helpers.ts +76 -76
  66. package/src/auth/__tests__/credentials.spec.ts +204 -0
  67. package/src/auth/__tests__/index.spec.ts +168 -0
  68. package/src/auth/credentials.ts +187 -0
  69. package/src/auth/index.ts +226 -0
  70. package/src/build/__tests__/bundler.spec.ts +444 -0
  71. package/src/build/__tests__/index-new.spec.ts +474 -474
  72. package/src/build/__tests__/manifests.spec.ts +333 -333
  73. package/src/build/bundler.ts +210 -0
  74. package/src/build/endpoint-analyzer.ts +236 -0
  75. package/src/build/handler-templates.ts +1253 -0
  76. package/src/build/index.ts +260 -179
  77. package/src/build/manifests.ts +52 -52
  78. package/src/build/providerResolver.ts +145 -145
  79. package/src/build/types.ts +64 -43
  80. package/src/config.ts +39 -39
  81. package/src/deploy/__tests__/docker.spec.ts +111 -0
  82. package/src/deploy/__tests__/dokploy.spec.ts +245 -0
  83. package/src/deploy/__tests__/init.spec.ts +662 -0
  84. package/src/deploy/docker.ts +128 -0
  85. package/src/deploy/dokploy.ts +204 -0
  86. package/src/deploy/index.ts +136 -0
  87. package/src/deploy/init.ts +484 -0
  88. package/src/deploy/types.ts +48 -0
  89. package/src/dev/__tests__/index.spec.ts +266 -266
  90. package/src/dev/index.ts +647 -601
  91. package/src/docker/__tests__/compose.spec.ts +531 -0
  92. package/src/docker/__tests__/templates.spec.ts +280 -0
  93. package/src/docker/compose.ts +273 -0
  94. package/src/docker/index.ts +230 -0
  95. package/src/docker/templates.ts +446 -0
  96. package/src/generators/CronGenerator.ts +72 -72
  97. package/src/generators/EndpointGenerator.ts +699 -398
  98. package/src/generators/FunctionGenerator.ts +84 -84
  99. package/src/generators/Generator.ts +72 -72
  100. package/src/generators/OpenApiTsGenerator.ts +577 -577
  101. package/src/generators/SubscriberGenerator.ts +124 -124
  102. package/src/generators/__tests__/CronGenerator.spec.ts +433 -433
  103. package/src/generators/__tests__/EndpointGenerator.spec.ts +532 -382
  104. package/src/generators/__tests__/FunctionGenerator.spec.ts +244 -244
  105. package/src/generators/__tests__/SubscriberGenerator.spec.ts +397 -382
  106. package/src/generators/index.ts +4 -4
  107. package/src/index.ts +623 -201
  108. package/src/init/__tests__/generators.spec.ts +334 -334
  109. package/src/init/__tests__/init.spec.ts +332 -332
  110. package/src/init/__tests__/utils.spec.ts +89 -89
  111. package/src/init/generators/config.ts +175 -175
  112. package/src/init/generators/docker.ts +41 -41
  113. package/src/init/generators/env.ts +72 -72
  114. package/src/init/generators/index.ts +1 -1
  115. package/src/init/generators/models.ts +64 -64
  116. package/src/init/generators/monorepo.ts +161 -161
  117. package/src/init/generators/package.ts +71 -71
  118. package/src/init/generators/source.ts +6 -6
  119. package/src/init/index.ts +203 -208
  120. package/src/init/templates/api.ts +115 -115
  121. package/src/init/templates/index.ts +75 -75
  122. package/src/init/templates/minimal.ts +98 -98
  123. package/src/init/templates/serverless.ts +89 -89
  124. package/src/init/templates/worker.ts +98 -98
  125. package/src/init/utils.ts +54 -56
  126. package/src/openapi-react-query.ts +194 -194
  127. package/src/openapi.ts +63 -63
  128. package/src/secrets/__tests__/encryption.spec.ts +226 -0
  129. package/src/secrets/__tests__/generator.spec.ts +319 -0
  130. package/src/secrets/__tests__/index.spec.ts +91 -0
  131. package/src/secrets/__tests__/storage.spec.ts +611 -0
  132. package/src/secrets/encryption.ts +91 -0
  133. package/src/secrets/generator.ts +164 -0
  134. package/src/secrets/index.ts +383 -0
  135. package/src/secrets/storage.ts +192 -0
  136. package/src/secrets/types.ts +53 -0
  137. package/src/types.ts +295 -176
  138. package/tsdown.config.ts +11 -8
  139. package/dist/config-BrkUalUh.mjs.map +0 -1
  140. package/dist/config-C9aXOHBe.cjs.map +0 -1
  141. package/dist/openapi-BeHLKcwP.cjs.map +0 -1
  142. package/dist/openapi-CZLI4QTr.mjs.map +0 -1
  143. package/dist/openapi-react-query-CcciaVu5.mjs.map +0 -1
  144. package/dist/openapi-react-query-o5iMi8tz.cjs.map +0 -1
  145. package/dist/types-DXgiA1sF.d.mts.map +0 -1
  146. package/dist/types-b-vwGpqc.d.cts.map +0 -1
@@ -3,50 +3,50 @@ import { join } from 'node:path';
3
3
  import { itWithDir } from '@geekmidas/testkit/os';
4
4
  import { describe, expect, vi } from 'vitest';
5
5
  import {
6
- createMockCronFile,
7
- createMockEndpointFile,
8
- createMockFunctionFile,
9
- createTestFile,
6
+ createMockCronFile,
7
+ createMockEndpointFile,
8
+ createMockFunctionFile,
9
+ createTestFile,
10
10
  } from '../../__tests__/test-helpers';
11
11
  import { buildCommand } from '../index';
12
12
 
13
13
  describe('buildCommand', () => {
14
- itWithDir(
15
- 'should build endpoints, functions, and crons for multiple providers',
16
- async ({ dir }) => {
17
- // Create test files that will be discovered
18
- await createMockEndpointFile(
19
- dir,
20
- 'src/endpoints/users.ts',
21
- 'getUsersEndpoint',
22
- '/users',
23
- 'GET',
24
- );
25
- await createMockEndpointFile(
26
- dir,
27
- 'src/endpoints/posts.ts',
28
- 'getPostsEndpoint',
29
- '/posts',
30
- 'GET',
31
- );
32
- await createMockFunctionFile(
33
- dir,
34
- 'src/functions/process.ts',
35
- 'processDataFunction',
36
- 60,
37
- );
38
- await createMockCronFile(
39
- dir,
40
- 'src/crons/cleanup.ts',
41
- 'cleanupCron',
42
- 'rate(1 day)',
43
- );
44
-
45
- // Create a basic config file
46
- await createTestFile(
47
- dir,
48
- 'gkm.config.ts',
49
- `
14
+ itWithDir(
15
+ 'should build endpoints, functions, and crons for multiple providers',
16
+ async ({ dir }) => {
17
+ // Create test files that will be discovered
18
+ await createMockEndpointFile(
19
+ dir,
20
+ 'src/endpoints/users.ts',
21
+ 'getUsersEndpoint',
22
+ '/users',
23
+ 'GET',
24
+ );
25
+ await createMockEndpointFile(
26
+ dir,
27
+ 'src/endpoints/posts.ts',
28
+ 'getPostsEndpoint',
29
+ '/posts',
30
+ 'GET',
31
+ );
32
+ await createMockFunctionFile(
33
+ dir,
34
+ 'src/functions/process.ts',
35
+ 'processDataFunction',
36
+ 60,
37
+ );
38
+ await createMockCronFile(
39
+ dir,
40
+ 'src/crons/cleanup.ts',
41
+ 'cleanupCron',
42
+ 'rate(1 day)',
43
+ );
44
+
45
+ // Create a basic config file
46
+ await createTestFile(
47
+ dir,
48
+ 'gkm.config.ts',
49
+ `
50
50
  export default {
51
51
  routes: './src/endpoints/**/*.ts',
52
52
  functions: './src/functions/**/*.ts',
@@ -55,101 +55,101 @@ export default {
55
55
  logger: './config/logger',
56
56
  };
57
57
  `,
58
- );
59
-
60
- // Create env and logger files
61
- await createTestFile(dir, 'config/env.ts', 'export default {}');
62
- await createTestFile(dir, 'config/logger.ts', 'export default {}');
63
-
64
- const originalCwd = process.cwd();
65
- process.chdir(dir);
66
-
67
- try {
68
- await buildCommand({ provider: 'server' });
69
-
70
- // Check that output directories were created
71
- const serverDir = join(dir, '.gkm', 'server');
72
-
73
- // Check app.ts has the createApp function with new API
74
- const appContent = await readFile(join(serverDir, 'app.ts'), 'utf-8');
75
- expect(appContent).toContain('function createApp');
76
- expect(appContent).toContain('interface ServerApp');
77
- expect(appContent).toContain('async start(options');
78
-
79
- // Check endpoints.ts has the HonoEndpoint setup
80
- const endpointsContent = await readFile(
81
- join(serverDir, 'endpoints.ts'),
82
- 'utf-8',
83
- );
84
- expect(endpointsContent).toContain('HonoEndpoint');
85
-
86
- // Verify server manifest was created at .gkm/manifest/server.ts
87
- const manifestPath = join(dir, '.gkm', 'manifest', 'server.ts');
88
- const manifestContent = await readFile(manifestPath, 'utf-8');
89
-
90
- // Verify manifest structure
91
- expect(manifestContent).toContain('export const manifest = {');
92
- expect(manifestContent).toContain('} as const;');
93
- expect(manifestContent).toContain('app:');
94
- expect(manifestContent).toContain('routes:');
95
-
96
- // Verify derived types are exported
97
- expect(manifestContent).toContain('export type Route =');
98
- expect(manifestContent).toContain('export type Authorizer =');
99
- } finally {
100
- process.chdir(originalCwd);
101
- }
102
- },
103
- );
104
-
105
- itWithDir(
106
- 'should perform complete build with all construct types for AWS Lambda',
107
- async ({ dir }) => {
108
- // Create comprehensive test setup with all construct types
109
- await createMockEndpointFile(
110
- dir,
111
- 'src/endpoints/users.ts',
112
- 'getUsersEndpoint',
113
- '/users',
114
- 'GET',
115
- );
116
- await createMockEndpointFile(
117
- dir,
118
- 'src/endpoints/posts.ts',
119
- 'getPostsEndpoint',
120
- '/posts',
121
- 'POST',
122
- );
123
- await createMockFunctionFile(
124
- dir,
125
- 'src/functions/processData.ts',
126
- 'processDataFunction',
127
- 300,
128
- );
129
- await createMockFunctionFile(
130
- dir,
131
- 'src/functions/sendEmail.ts',
132
- 'sendEmailFunction',
133
- 30,
134
- );
135
- await createMockCronFile(
136
- dir,
137
- 'src/crons/dailyCleanup.ts',
138
- 'dailyCleanupCron',
139
- 'rate(1 day)',
140
- );
141
- await createMockCronFile(
142
- dir,
143
- 'src/crons/hourlyReport.ts',
144
- 'hourlyReportCron',
145
- 'cron(0 * * * ? *)',
146
- );
147
-
148
- // Create config
149
- await createTestFile(
150
- dir,
151
- 'gkm.config.ts',
152
- `
58
+ );
59
+
60
+ // Create env and logger files
61
+ await createTestFile(dir, 'config/env.ts', 'export default {}');
62
+ await createTestFile(dir, 'config/logger.ts', 'export default {}');
63
+
64
+ const originalCwd = process.cwd();
65
+ process.chdir(dir);
66
+
67
+ try {
68
+ await buildCommand({ provider: 'server' });
69
+
70
+ // Check that output directories were created
71
+ const serverDir = join(dir, '.gkm', 'server');
72
+
73
+ // Check app.ts has the createApp function with new API
74
+ const appContent = await readFile(join(serverDir, 'app.ts'), 'utf-8');
75
+ expect(appContent).toContain('function createApp');
76
+ expect(appContent).toContain('interface ServerApp');
77
+ expect(appContent).toContain('async start(options');
78
+
79
+ // Check endpoints.ts has the HonoEndpoint setup
80
+ const endpointsContent = await readFile(
81
+ join(serverDir, 'endpoints.ts'),
82
+ 'utf-8',
83
+ );
84
+ expect(endpointsContent).toContain('HonoEndpoint');
85
+
86
+ // Verify server manifest was created at .gkm/manifest/server.ts
87
+ const manifestPath = join(dir, '.gkm', 'manifest', 'server.ts');
88
+ const manifestContent = await readFile(manifestPath, 'utf-8');
89
+
90
+ // Verify manifest structure
91
+ expect(manifestContent).toContain('export const manifest = {');
92
+ expect(manifestContent).toContain('} as const;');
93
+ expect(manifestContent).toContain('app:');
94
+ expect(manifestContent).toContain('routes:');
95
+
96
+ // Verify derived types are exported
97
+ expect(manifestContent).toContain('export type Route =');
98
+ expect(manifestContent).toContain('export type Authorizer =');
99
+ } finally {
100
+ process.chdir(originalCwd);
101
+ }
102
+ },
103
+ );
104
+
105
+ itWithDir(
106
+ 'should perform complete build with all construct types for AWS Lambda',
107
+ async ({ dir }) => {
108
+ // Create comprehensive test setup with all construct types
109
+ await createMockEndpointFile(
110
+ dir,
111
+ 'src/endpoints/users.ts',
112
+ 'getUsersEndpoint',
113
+ '/users',
114
+ 'GET',
115
+ );
116
+ await createMockEndpointFile(
117
+ dir,
118
+ 'src/endpoints/posts.ts',
119
+ 'getPostsEndpoint',
120
+ '/posts',
121
+ 'POST',
122
+ );
123
+ await createMockFunctionFile(
124
+ dir,
125
+ 'src/functions/processData.ts',
126
+ 'processDataFunction',
127
+ 300,
128
+ );
129
+ await createMockFunctionFile(
130
+ dir,
131
+ 'src/functions/sendEmail.ts',
132
+ 'sendEmailFunction',
133
+ 30,
134
+ );
135
+ await createMockCronFile(
136
+ dir,
137
+ 'src/crons/dailyCleanup.ts',
138
+ 'dailyCleanupCron',
139
+ 'rate(1 day)',
140
+ );
141
+ await createMockCronFile(
142
+ dir,
143
+ 'src/crons/hourlyReport.ts',
144
+ 'hourlyReportCron',
145
+ 'cron(0 * * * ? *)',
146
+ );
147
+
148
+ // Create config
149
+ await createTestFile(
150
+ dir,
151
+ 'gkm.config.ts',
152
+ `
153
153
  export default {
154
154
  routes: './src/endpoints/**/*.ts',
155
155
  functions: './src/functions/**/*.ts',
@@ -158,105 +158,105 @@ export default {
158
158
  logger: './config/logger',
159
159
  };
160
160
  `,
161
- );
162
-
163
- // Create env and logger files
164
- await createTestFile(dir, 'config/env.ts', 'export default {}');
165
- await createTestFile(dir, 'config/logger.ts', 'export default {}');
166
-
167
- const originalCwd = process.cwd();
168
- process.chdir(dir);
169
-
170
- try {
171
- // Build for AWS Lambda
172
- await buildCommand({ provider: 'aws' });
173
-
174
- const awsLambdaDir = join(dir, '.gkm', 'aws-lambda');
175
- const awsApiGatewayV2Dir = join(dir, '.gkm', 'aws-apigatewayv2');
176
-
177
- // Verify Lambda handlers were created
178
- expect(
179
- await readFile(
180
- join(awsLambdaDir, 'functions', 'processDataFunction.ts'),
181
- 'utf-8',
182
- ),
183
- ).toContain('AWSLambdaFunction');
184
- expect(
185
- await readFile(
186
- join(awsLambdaDir, 'functions', 'sendEmailFunction.ts'),
187
- 'utf-8',
188
- ),
189
- ).toContain('AWSLambdaFunction');
190
-
191
- // Verify Cron handlers were created
192
- expect(
193
- await readFile(
194
- join(awsLambdaDir, 'crons', 'dailyCleanupCron.ts'),
195
- 'utf-8',
196
- ),
197
- ).toContain('AWSScheduledFunction');
198
- expect(
199
- await readFile(
200
- join(awsLambdaDir, 'crons', 'hourlyReportCron.ts'),
201
- 'utf-8',
202
- ),
203
- ).toContain('AWSScheduledFunction');
204
-
205
- // Verify API Gateway handlers were created
206
- expect(
207
- await readFile(
208
- join(awsApiGatewayV2Dir, 'getUsersEndpoint.ts'),
209
- 'utf-8',
210
- ),
211
- ).toContain('AmazonApiGatewayV2Endpoint');
212
- expect(
213
- await readFile(
214
- join(awsApiGatewayV2Dir, 'getPostsEndpoint.ts'),
215
- 'utf-8',
216
- ),
217
- ).toContain('AmazonApiGatewayV2Endpoint');
218
-
219
- // Verify AWS manifest was created at .gkm/manifest/aws.ts
220
- const manifestPath = join(dir, '.gkm', 'manifest', 'aws.ts');
221
- const manifestContent = await readFile(manifestPath, 'utf-8');
222
-
223
- // Verify manifest is TypeScript with as const
224
- expect(manifestContent).toContain('export const manifest = {');
225
- expect(manifestContent).toContain('} as const;');
226
-
227
- // Verify derived types are exported
228
- expect(manifestContent).toContain('export type Route =');
229
- expect(manifestContent).toContain('export type Function =');
230
- expect(manifestContent).toContain('export type Cron =');
231
- expect(manifestContent).toContain('export type Authorizer =');
232
-
233
- // Verify routes are included (JSON.stringify output uses quoted keys/values)
234
- expect(manifestContent).toContain('/users');
235
- expect(manifestContent).toContain('/posts');
236
- expect(manifestContent).toContain('GET');
237
- expect(manifestContent).toContain('POST');
238
-
239
- // Verify functions are included
240
- expect(manifestContent).toContain('processDataFunction');
241
- expect(manifestContent).toContain('sendEmailFunction');
242
-
243
- // Verify crons are included
244
- expect(manifestContent).toContain('dailyCleanupCron');
245
- expect(manifestContent).toContain('hourlyReportCron');
246
- expect(manifestContent).toContain('rate(1 day)');
247
- expect(manifestContent).toContain('cron(0 * * * ? *)');
248
- } finally {
249
- process.chdir(originalCwd);
250
- }
251
- },
252
- );
253
-
254
- itWithDir('should handle case with no constructs found', async ({ dir }) => {
255
- // Create a basic config file with no actual construct files
256
- await createTestFile(
257
- dir,
258
- 'gkm.config.ts',
259
- `
161
+ );
162
+
163
+ // Create env and logger files
164
+ await createTestFile(dir, 'config/env.ts', 'export default {}');
165
+ await createTestFile(dir, 'config/logger.ts', 'export default {}');
166
+
167
+ const originalCwd = process.cwd();
168
+ process.chdir(dir);
169
+
170
+ try {
171
+ // Build for AWS Lambda
172
+ await buildCommand({ provider: 'aws' });
173
+
174
+ const awsLambdaDir = join(dir, '.gkm', 'aws-lambda');
175
+ const awsApiGatewayV2Dir = join(dir, '.gkm', 'aws-apigatewayv2');
176
+
177
+ // Verify Lambda handlers were created
178
+ expect(
179
+ await readFile(
180
+ join(awsLambdaDir, 'functions', 'processDataFunction.ts'),
181
+ 'utf-8',
182
+ ),
183
+ ).toContain('AWSLambdaFunction');
184
+ expect(
185
+ await readFile(
186
+ join(awsLambdaDir, 'functions', 'sendEmailFunction.ts'),
187
+ 'utf-8',
188
+ ),
189
+ ).toContain('AWSLambdaFunction');
190
+
191
+ // Verify Cron handlers were created
192
+ expect(
193
+ await readFile(
194
+ join(awsLambdaDir, 'crons', 'dailyCleanupCron.ts'),
195
+ 'utf-8',
196
+ ),
197
+ ).toContain('AWSScheduledFunction');
198
+ expect(
199
+ await readFile(
200
+ join(awsLambdaDir, 'crons', 'hourlyReportCron.ts'),
201
+ 'utf-8',
202
+ ),
203
+ ).toContain('AWSScheduledFunction');
204
+
205
+ // Verify API Gateway handlers were created
206
+ expect(
207
+ await readFile(
208
+ join(awsApiGatewayV2Dir, 'getUsersEndpoint.ts'),
209
+ 'utf-8',
210
+ ),
211
+ ).toContain('AmazonApiGatewayV2Endpoint');
212
+ expect(
213
+ await readFile(
214
+ join(awsApiGatewayV2Dir, 'getPostsEndpoint.ts'),
215
+ 'utf-8',
216
+ ),
217
+ ).toContain('AmazonApiGatewayV2Endpoint');
218
+
219
+ // Verify AWS manifest was created at .gkm/manifest/aws.ts
220
+ const manifestPath = join(dir, '.gkm', 'manifest', 'aws.ts');
221
+ const manifestContent = await readFile(manifestPath, 'utf-8');
222
+
223
+ // Verify manifest is TypeScript with as const
224
+ expect(manifestContent).toContain('export const manifest = {');
225
+ expect(manifestContent).toContain('} as const;');
226
+
227
+ // Verify derived types are exported
228
+ expect(manifestContent).toContain('export type Route =');
229
+ expect(manifestContent).toContain('export type Function =');
230
+ expect(manifestContent).toContain('export type Cron =');
231
+ expect(manifestContent).toContain('export type Authorizer =');
232
+
233
+ // Verify routes are included (JSON.stringify output uses quoted keys/values)
234
+ expect(manifestContent).toContain('/users');
235
+ expect(manifestContent).toContain('/posts');
236
+ expect(manifestContent).toContain('GET');
237
+ expect(manifestContent).toContain('POST');
238
+
239
+ // Verify functions are included
240
+ expect(manifestContent).toContain('processDataFunction');
241
+ expect(manifestContent).toContain('sendEmailFunction');
242
+
243
+ // Verify crons are included
244
+ expect(manifestContent).toContain('dailyCleanupCron');
245
+ expect(manifestContent).toContain('hourlyReportCron');
246
+ expect(manifestContent).toContain('rate(1 day)');
247
+ expect(manifestContent).toContain('cron(0 * * * ? *)');
248
+ } finally {
249
+ process.chdir(originalCwd);
250
+ }
251
+ },
252
+ );
253
+
254
+ itWithDir('should handle case with no constructs found', async ({ dir }) => {
255
+ // Create a basic config file with no actual construct files
256
+ await createTestFile(
257
+ dir,
258
+ 'gkm.config.ts',
259
+ `
260
260
  export default {
261
261
  routes: './src/endpoints/**/*.ts',
262
262
  functions: './src/functions/**/*.ts',
@@ -265,41 +265,41 @@ export default {
265
265
  logger: './config/logger',
266
266
  };
267
267
  `,
268
- );
269
-
270
- // Create env and logger files
271
- await createTestFile(dir, 'config/env.ts', 'export default {}');
272
- await createTestFile(dir, 'config/logger.ts', 'export default {}');
273
-
274
- const originalCwd = process.cwd();
275
- process.chdir(dir);
276
-
277
- const logSpy = vi.spyOn(console, 'log');
278
-
279
- try {
280
- await buildCommand({ provider: 'server' });
281
-
282
- expect(logSpy).toHaveBeenCalledWith('Found 0 endpoints');
283
- expect(logSpy).toHaveBeenCalledWith('Found 0 functions');
284
- expect(logSpy).toHaveBeenCalledWith('Found 0 crons');
285
- expect(logSpy).toHaveBeenCalledWith('Found 0 subscribers');
286
- expect(logSpy).toHaveBeenCalledWith(
287
- 'No endpoints, functions, crons, or subscribers found to process',
288
- );
289
- } finally {
290
- process.chdir(originalCwd);
291
- logSpy.mockRestore();
292
- }
293
- });
294
-
295
- itWithDir(
296
- 'should handle optional functions and crons config',
297
- async ({ dir }) => {
298
- // Create config with undefined functions and crons
299
- await createTestFile(
300
- dir,
301
- 'gkm.config.ts',
302
- `
268
+ );
269
+
270
+ // Create env and logger files
271
+ await createTestFile(dir, 'config/env.ts', 'export default {}');
272
+ await createTestFile(dir, 'config/logger.ts', 'export default {}');
273
+
274
+ const originalCwd = process.cwd();
275
+ process.chdir(dir);
276
+
277
+ const logSpy = vi.spyOn(console, 'log');
278
+
279
+ try {
280
+ await buildCommand({ provider: 'server' });
281
+
282
+ expect(logSpy).toHaveBeenCalledWith('Found 0 endpoints');
283
+ expect(logSpy).toHaveBeenCalledWith('Found 0 functions');
284
+ expect(logSpy).toHaveBeenCalledWith('Found 0 crons');
285
+ expect(logSpy).toHaveBeenCalledWith('Found 0 subscribers');
286
+ expect(logSpy).toHaveBeenCalledWith(
287
+ 'No endpoints, functions, crons, or subscribers found to process',
288
+ );
289
+ } finally {
290
+ process.chdir(originalCwd);
291
+ logSpy.mockRestore();
292
+ }
293
+ });
294
+
295
+ itWithDir(
296
+ 'should handle optional functions and crons config',
297
+ async ({ dir }) => {
298
+ // Create config with undefined functions and crons
299
+ await createTestFile(
300
+ dir,
301
+ 'gkm.config.ts',
302
+ `
303
303
  export default {
304
304
  routes: './src/endpoints/**/*.ts',
305
305
  functions: undefined,
@@ -308,54 +308,54 @@ export default {
308
308
  logger: './config/logger',
309
309
  };
310
310
  `,
311
- );
312
-
313
- await createMockEndpointFile(
314
- dir,
315
- 'src/endpoints/test.ts',
316
- 'testEndpoint',
317
- '/test',
318
- 'GET',
319
- );
320
-
321
- // Create env and logger files
322
- await createTestFile(dir, 'config/env.ts', 'export default {}');
323
- await createTestFile(dir, 'config/logger.ts', 'export default {}');
324
-
325
- const originalCwd = process.cwd();
326
- process.chdir(dir);
327
-
328
- const logSpy = vi.spyOn(console, 'log');
329
-
330
- try {
331
- await buildCommand({ provider: 'server' });
332
-
333
- expect(logSpy).toHaveBeenCalledWith('Found 1 endpoints');
334
- expect(logSpy).toHaveBeenCalledWith('Found 0 functions');
335
- expect(logSpy).toHaveBeenCalledWith('Found 0 crons');
336
-
337
- // Should not log functions or crons loading messages
338
- expect(logSpy).not.toHaveBeenCalledWith(
339
- expect.stringContaining('Loading functions'),
340
- );
341
- expect(logSpy).not.toHaveBeenCalledWith(
342
- expect.stringContaining('Loading crons'),
343
- );
344
- } finally {
345
- process.chdir(originalCwd);
346
- logSpy.mockRestore();
347
- }
348
- },
349
- );
350
-
351
- itWithDir(
352
- 'should parse envParser configuration correctly',
353
- async ({ dir }) => {
354
- // Create config with custom named exports
355
- await createTestFile(
356
- dir,
357
- 'gkm.config.ts',
358
- `
311
+ );
312
+
313
+ await createMockEndpointFile(
314
+ dir,
315
+ 'src/endpoints/test.ts',
316
+ 'testEndpoint',
317
+ '/test',
318
+ 'GET',
319
+ );
320
+
321
+ // Create env and logger files
322
+ await createTestFile(dir, 'config/env.ts', 'export default {}');
323
+ await createTestFile(dir, 'config/logger.ts', 'export default {}');
324
+
325
+ const originalCwd = process.cwd();
326
+ process.chdir(dir);
327
+
328
+ const logSpy = vi.spyOn(console, 'log');
329
+
330
+ try {
331
+ await buildCommand({ provider: 'server' });
332
+
333
+ expect(logSpy).toHaveBeenCalledWith('Found 1 endpoints');
334
+ expect(logSpy).toHaveBeenCalledWith('Found 0 functions');
335
+ expect(logSpy).toHaveBeenCalledWith('Found 0 crons');
336
+
337
+ // Should not log functions or crons loading messages
338
+ expect(logSpy).not.toHaveBeenCalledWith(
339
+ expect.stringContaining('Loading functions'),
340
+ );
341
+ expect(logSpy).not.toHaveBeenCalledWith(
342
+ expect.stringContaining('Loading crons'),
343
+ );
344
+ } finally {
345
+ process.chdir(originalCwd);
346
+ logSpy.mockRestore();
347
+ }
348
+ },
349
+ );
350
+
351
+ itWithDir(
352
+ 'should parse envParser configuration correctly',
353
+ async ({ dir }) => {
354
+ // Create config with custom named exports
355
+ await createTestFile(
356
+ dir,
357
+ 'gkm.config.ts',
358
+ `
359
359
  export default {
360
360
  routes: './src/endpoints/**/*.ts',
361
361
  functions: undefined,
@@ -364,52 +364,52 @@ export default {
364
364
  logger: './config/logger#customLogger',
365
365
  };
366
366
  `,
367
- );
368
-
369
- await createMockEndpointFile(
370
- dir,
371
- 'src/endpoints/test.ts',
372
- 'testEndpoint',
373
- '/test',
374
- 'GET',
375
- );
376
-
377
- // Create env and logger files with named exports
378
- await createTestFile(
379
- dir,
380
- 'config/env.ts',
381
- 'export const customEnvParser = {}',
382
- );
383
- await createTestFile(
384
- dir,
385
- 'config/logger.ts',
386
- 'export const customLogger = {}',
387
- );
388
-
389
- const originalCwd = process.cwd();
390
- process.chdir(dir);
391
-
392
- try {
393
- await buildCommand({ provider: 'aws' });
394
-
395
- // Verify that a handler file was generated with correct imports
396
- const handlerFile = join(dir, '.gkm/aws-apigatewayv2/testEndpoint.ts');
397
- const handlerContent = await readFile(handlerFile, 'utf-8');
398
- expect(handlerContent).toContain('{ customEnvParser as envParser }');
399
- } finally {
400
- process.chdir(originalCwd);
401
- }
402
- },
403
- );
404
-
405
- itWithDir(
406
- 'should create output directories for each provider',
407
- async ({ dir }) => {
408
- // Create config with multiple providers
409
- await createTestFile(
410
- dir,
411
- 'gkm.config.ts',
412
- `
367
+ );
368
+
369
+ await createMockEndpointFile(
370
+ dir,
371
+ 'src/endpoints/test.ts',
372
+ 'testEndpoint',
373
+ '/test',
374
+ 'GET',
375
+ );
376
+
377
+ // Create env and logger files with named exports
378
+ await createTestFile(
379
+ dir,
380
+ 'config/env.ts',
381
+ 'export const customEnvParser = {}',
382
+ );
383
+ await createTestFile(
384
+ dir,
385
+ 'config/logger.ts',
386
+ 'export const customLogger = {}',
387
+ );
388
+
389
+ const originalCwd = process.cwd();
390
+ process.chdir(dir);
391
+
392
+ try {
393
+ await buildCommand({ provider: 'aws' });
394
+
395
+ // Verify that a handler file was generated with correct imports
396
+ const handlerFile = join(dir, '.gkm/aws-apigatewayv2/testEndpoint.ts');
397
+ const handlerContent = await readFile(handlerFile, 'utf-8');
398
+ expect(handlerContent).toContain('{ customEnvParser as envParser }');
399
+ } finally {
400
+ process.chdir(originalCwd);
401
+ }
402
+ },
403
+ );
404
+
405
+ itWithDir(
406
+ 'should create output directories for each provider',
407
+ async ({ dir }) => {
408
+ // Create config with multiple providers
409
+ await createTestFile(
410
+ dir,
411
+ 'gkm.config.ts',
412
+ `
413
413
  export default {
414
414
  routes: './src/endpoints/**/*.ts',
415
415
  functions: undefined,
@@ -418,48 +418,48 @@ export default {
418
418
  logger: './config/logger',
419
419
  };
420
420
  `,
421
- );
422
-
423
- await createMockEndpointFile(
424
- dir,
425
- 'src/endpoints/test.ts',
426
- 'testEndpoint',
427
- '/test',
428
- 'GET',
429
- );
430
-
431
- // Create env and logger files
432
- await createTestFile(dir, 'config/env.ts', 'export default {}');
433
- await createTestFile(dir, 'config/logger.ts', 'export default {}');
434
-
435
- const originalCwd = process.cwd();
436
- process.chdir(dir);
437
-
438
- try {
439
- await buildCommand({ provider: 'aws' });
440
-
441
- const v2HandlerFile = join(
442
- dir,
443
- '.gkm/aws-apigatewayv2/testEndpoint.ts',
444
- );
445
-
446
- const v2Content = await readFile(v2HandlerFile, 'utf-8');
447
-
448
- expect(v2Content).toContain('AmazonApiGatewayV2Endpoint');
449
- } finally {
450
- process.chdir(originalCwd);
451
- }
452
- },
453
- );
454
-
455
- itWithDir(
456
- 'should handle default import patterns for envParser and logger',
457
- async ({ dir }) => {
458
- // Create config with default import patterns
459
- await createTestFile(
460
- dir,
461
- 'gkm.config.ts',
462
- `
421
+ );
422
+
423
+ await createMockEndpointFile(
424
+ dir,
425
+ 'src/endpoints/test.ts',
426
+ 'testEndpoint',
427
+ '/test',
428
+ 'GET',
429
+ );
430
+
431
+ // Create env and logger files
432
+ await createTestFile(dir, 'config/env.ts', 'export default {}');
433
+ await createTestFile(dir, 'config/logger.ts', 'export default {}');
434
+
435
+ const originalCwd = process.cwd();
436
+ process.chdir(dir);
437
+
438
+ try {
439
+ await buildCommand({ provider: 'aws' });
440
+
441
+ const v2HandlerFile = join(
442
+ dir,
443
+ '.gkm/aws-apigatewayv2/testEndpoint.ts',
444
+ );
445
+
446
+ const v2Content = await readFile(v2HandlerFile, 'utf-8');
447
+
448
+ expect(v2Content).toContain('AmazonApiGatewayV2Endpoint');
449
+ } finally {
450
+ process.chdir(originalCwd);
451
+ }
452
+ },
453
+ );
454
+
455
+ itWithDir(
456
+ 'should handle default import patterns for envParser and logger',
457
+ async ({ dir }) => {
458
+ // Create config with default import patterns
459
+ await createTestFile(
460
+ dir,
461
+ 'gkm.config.ts',
462
+ `
463
463
  export default {
464
464
  routes: './src/endpoints/**/*.ts',
465
465
  functions: undefined,
@@ -468,45 +468,45 @@ export default {
468
468
  logger: './config/logger',
469
469
  };
470
470
  `,
471
- );
472
-
473
- await createMockEndpointFile(
474
- dir,
475
- 'src/endpoints/test.ts',
476
- 'testEndpoint',
477
- '/test',
478
- 'GET',
479
- );
480
-
481
- // Create env and logger files with default exports
482
- await createTestFile(dir, 'config/env.ts', 'export default {}');
483
- await createTestFile(dir, 'config/logger.ts', 'export default {}');
484
-
485
- const originalCwd = process.cwd();
486
- process.chdir(dir);
487
-
488
- try {
489
- await buildCommand({ provider: 'aws' });
490
-
491
- // Verify that a handler file was generated with default imports
492
- const handlerFile = join(dir, '.gkm/aws-apigatewayv2/testEndpoint.ts');
493
- const handlerContent = await readFile(handlerFile, 'utf-8');
494
- expect(handlerContent).toContain('import envParser');
495
- expect(handlerContent).not.toContain('{ envParser }');
496
- } finally {
497
- process.chdir(originalCwd);
498
- }
499
- },
500
- );
501
-
502
- itWithDir(
503
- 'should handle envParser pattern with same name as expected',
504
- async ({ dir }) => {
505
- // Create config with named exports that match expected names
506
- await createTestFile(
507
- dir,
508
- 'gkm.config.ts',
509
- `
471
+ );
472
+
473
+ await createMockEndpointFile(
474
+ dir,
475
+ 'src/endpoints/test.ts',
476
+ 'testEndpoint',
477
+ '/test',
478
+ 'GET',
479
+ );
480
+
481
+ // Create env and logger files with default exports
482
+ await createTestFile(dir, 'config/env.ts', 'export default {}');
483
+ await createTestFile(dir, 'config/logger.ts', 'export default {}');
484
+
485
+ const originalCwd = process.cwd();
486
+ process.chdir(dir);
487
+
488
+ try {
489
+ await buildCommand({ provider: 'aws' });
490
+
491
+ // Verify that a handler file was generated with default imports
492
+ const handlerFile = join(dir, '.gkm/aws-apigatewayv2/testEndpoint.ts');
493
+ const handlerContent = await readFile(handlerFile, 'utf-8');
494
+ expect(handlerContent).toContain('import envParser');
495
+ expect(handlerContent).not.toContain('{ envParser }');
496
+ } finally {
497
+ process.chdir(originalCwd);
498
+ }
499
+ },
500
+ );
501
+
502
+ itWithDir(
503
+ 'should handle envParser pattern with same name as expected',
504
+ async ({ dir }) => {
505
+ // Create config with named exports that match expected names
506
+ await createTestFile(
507
+ dir,
508
+ 'gkm.config.ts',
509
+ `
510
510
  export default {
511
511
  routes: './src/endpoints/**/*.ts',
512
512
  functions: undefined,
@@ -515,34 +515,34 @@ export default {
515
515
  logger: './config/logger#logger',
516
516
  };
517
517
  `,
518
- );
519
-
520
- await createMockEndpointFile(
521
- dir,
522
- 'src/endpoints/test.ts',
523
- 'testEndpoint',
524
- '/test',
525
- 'GET',
526
- );
527
-
528
- // Create env and logger files with named exports
529
- await createTestFile(dir, 'config/env.ts', 'export const envParser = {}');
530
- await createTestFile(dir, 'config/logger.ts', 'export const logger = {}');
531
-
532
- const originalCwd = process.cwd();
533
- process.chdir(dir);
534
-
535
- try {
536
- await buildCommand({ provider: 'aws' });
537
-
538
- // Verify that a handler file was generated with named imports
539
- const handlerFile = join(dir, '.gkm/aws-apigatewayv2/testEndpoint.ts');
540
- const handlerContent = await readFile(handlerFile, 'utf-8');
541
-
542
- expect(handlerContent).toContain('{ envParser }');
543
- } finally {
544
- process.chdir(originalCwd);
545
- }
546
- },
547
- );
518
+ );
519
+
520
+ await createMockEndpointFile(
521
+ dir,
522
+ 'src/endpoints/test.ts',
523
+ 'testEndpoint',
524
+ '/test',
525
+ 'GET',
526
+ );
527
+
528
+ // Create env and logger files with named exports
529
+ await createTestFile(dir, 'config/env.ts', 'export const envParser = {}');
530
+ await createTestFile(dir, 'config/logger.ts', 'export const logger = {}');
531
+
532
+ const originalCwd = process.cwd();
533
+ process.chdir(dir);
534
+
535
+ try {
536
+ await buildCommand({ provider: 'aws' });
537
+
538
+ // Verify that a handler file was generated with named imports
539
+ const handlerFile = join(dir, '.gkm/aws-apigatewayv2/testEndpoint.ts');
540
+ const handlerContent = await readFile(handlerFile, 'utf-8');
541
+
542
+ expect(handlerContent).toContain('{ envParser }');
543
+ } finally {
544
+ process.chdir(originalCwd);
545
+ }
546
+ },
547
+ );
548
548
  });