@geekmidas/cli 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/README.md +525 -0
  2. package/dist/bundler-DRXCw_YR.mjs +70 -0
  3. package/dist/bundler-DRXCw_YR.mjs.map +1 -0
  4. package/dist/bundler-WsEvH_b2.cjs +71 -0
  5. package/dist/bundler-WsEvH_b2.cjs.map +1 -0
  6. package/dist/{config-C9aXOHBe.cjs → config-AmInkU7k.cjs} +8 -8
  7. package/dist/config-AmInkU7k.cjs.map +1 -0
  8. package/dist/{config-BrkUalUh.mjs → config-DYULeEv8.mjs} +3 -3
  9. package/dist/config-DYULeEv8.mjs.map +1 -0
  10. package/dist/config.cjs +1 -1
  11. package/dist/config.d.cts +1 -1
  12. package/dist/config.d.mts +1 -1
  13. package/dist/config.mjs +1 -1
  14. package/dist/encryption-C8H-38Yy.mjs +42 -0
  15. package/dist/encryption-C8H-38Yy.mjs.map +1 -0
  16. package/dist/encryption-Dyf_r1h-.cjs +44 -0
  17. package/dist/encryption-Dyf_r1h-.cjs.map +1 -0
  18. package/dist/index.cjs +2116 -179
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.mjs +2134 -192
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/{openapi-CZLI4QTr.mjs → openapi-BfFlOBCG.mjs} +801 -38
  23. package/dist/openapi-BfFlOBCG.mjs.map +1 -0
  24. package/dist/{openapi-BeHLKcwP.cjs → openapi-Bt_1FDpT.cjs} +794 -31
  25. package/dist/openapi-Bt_1FDpT.cjs.map +1 -0
  26. package/dist/{openapi-react-query-o5iMi8tz.cjs → openapi-react-query-B-sNWHFU.cjs} +5 -5
  27. package/dist/openapi-react-query-B-sNWHFU.cjs.map +1 -0
  28. package/dist/{openapi-react-query-CcciaVu5.mjs → openapi-react-query-B6XTeGqS.mjs} +5 -5
  29. package/dist/openapi-react-query-B6XTeGqS.mjs.map +1 -0
  30. package/dist/openapi-react-query.cjs +1 -1
  31. package/dist/openapi-react-query.d.cts.map +1 -1
  32. package/dist/openapi-react-query.d.mts.map +1 -1
  33. package/dist/openapi-react-query.mjs +1 -1
  34. package/dist/openapi.cjs +2 -2
  35. package/dist/openapi.d.cts +1 -1
  36. package/dist/openapi.d.cts.map +1 -1
  37. package/dist/openapi.d.mts +1 -1
  38. package/dist/openapi.d.mts.map +1 -1
  39. package/dist/openapi.mjs +2 -2
  40. package/dist/storage-BUYQJgz7.cjs +4 -0
  41. package/dist/storage-BXoJvmv2.cjs +149 -0
  42. package/dist/storage-BXoJvmv2.cjs.map +1 -0
  43. package/dist/storage-C9PU_30f.mjs +101 -0
  44. package/dist/storage-C9PU_30f.mjs.map +1 -0
  45. package/dist/storage-DLJAYxzJ.mjs +3 -0
  46. package/dist/{types-b-vwGpqc.d.cts → types-BR0M2v_c.d.mts} +100 -1
  47. package/dist/types-BR0M2v_c.d.mts.map +1 -0
  48. package/dist/{types-DXgiA1sF.d.mts → types-BhkZc-vm.d.cts} +100 -1
  49. package/dist/types-BhkZc-vm.d.cts.map +1 -0
  50. package/examples/cron-example.ts +27 -27
  51. package/examples/env.ts +27 -27
  52. package/examples/function-example.ts +31 -31
  53. package/examples/gkm.config.json +20 -20
  54. package/examples/gkm.config.ts +8 -8
  55. package/examples/gkm.minimal.config.json +5 -5
  56. package/examples/gkm.production.config.json +25 -25
  57. package/examples/logger.ts +2 -2
  58. package/package.json +6 -6
  59. package/src/__tests__/EndpointGenerator.hooks.spec.ts +191 -191
  60. package/src/__tests__/config.spec.ts +55 -55
  61. package/src/__tests__/loadEnvFiles.spec.ts +93 -93
  62. package/src/__tests__/normalizeHooksConfig.spec.ts +58 -58
  63. package/src/__tests__/openapi-react-query.spec.ts +497 -497
  64. package/src/__tests__/openapi.spec.ts +428 -428
  65. package/src/__tests__/test-helpers.ts +76 -76
  66. package/src/auth/__tests__/credentials.spec.ts +204 -0
  67. package/src/auth/__tests__/index.spec.ts +168 -0
  68. package/src/auth/credentials.ts +187 -0
  69. package/src/auth/index.ts +226 -0
  70. package/src/build/__tests__/index-new.spec.ts +474 -474
  71. package/src/build/__tests__/manifests.spec.ts +333 -333
  72. package/src/build/bundler.ts +141 -0
  73. package/src/build/endpoint-analyzer.ts +236 -0
  74. package/src/build/handler-templates.ts +1253 -0
  75. package/src/build/index.ts +250 -179
  76. package/src/build/manifests.ts +52 -52
  77. package/src/build/providerResolver.ts +145 -145
  78. package/src/build/types.ts +64 -43
  79. package/src/config.ts +39 -39
  80. package/src/deploy/__tests__/docker.spec.ts +111 -0
  81. package/src/deploy/__tests__/dokploy.spec.ts +245 -0
  82. package/src/deploy/__tests__/init.spec.ts +662 -0
  83. package/src/deploy/docker.ts +128 -0
  84. package/src/deploy/dokploy.ts +204 -0
  85. package/src/deploy/index.ts +136 -0
  86. package/src/deploy/init.ts +484 -0
  87. package/src/deploy/types.ts +48 -0
  88. package/src/dev/__tests__/index.spec.ts +266 -266
  89. package/src/dev/index.ts +647 -601
  90. package/src/docker/__tests__/compose.spec.ts +531 -0
  91. package/src/docker/__tests__/templates.spec.ts +280 -0
  92. package/src/docker/compose.ts +273 -0
  93. package/src/docker/index.ts +230 -0
  94. package/src/docker/templates.ts +446 -0
  95. package/src/generators/CronGenerator.ts +72 -72
  96. package/src/generators/EndpointGenerator.ts +699 -398
  97. package/src/generators/FunctionGenerator.ts +84 -84
  98. package/src/generators/Generator.ts +72 -72
  99. package/src/generators/OpenApiTsGenerator.ts +577 -577
  100. package/src/generators/SubscriberGenerator.ts +124 -124
  101. package/src/generators/__tests__/CronGenerator.spec.ts +433 -433
  102. package/src/generators/__tests__/EndpointGenerator.spec.ts +532 -382
  103. package/src/generators/__tests__/FunctionGenerator.spec.ts +244 -244
  104. package/src/generators/__tests__/SubscriberGenerator.spec.ts +397 -382
  105. package/src/generators/index.ts +4 -4
  106. package/src/index.ts +623 -201
  107. package/src/init/__tests__/generators.spec.ts +334 -334
  108. package/src/init/__tests__/init.spec.ts +332 -332
  109. package/src/init/__tests__/utils.spec.ts +89 -89
  110. package/src/init/generators/config.ts +175 -175
  111. package/src/init/generators/docker.ts +41 -41
  112. package/src/init/generators/env.ts +72 -72
  113. package/src/init/generators/index.ts +1 -1
  114. package/src/init/generators/models.ts +64 -64
  115. package/src/init/generators/monorepo.ts +161 -161
  116. package/src/init/generators/package.ts +71 -71
  117. package/src/init/generators/source.ts +6 -6
  118. package/src/init/index.ts +203 -208
  119. package/src/init/templates/api.ts +115 -115
  120. package/src/init/templates/index.ts +75 -75
  121. package/src/init/templates/minimal.ts +98 -98
  122. package/src/init/templates/serverless.ts +89 -89
  123. package/src/init/templates/worker.ts +98 -98
  124. package/src/init/utils.ts +54 -56
  125. package/src/openapi-react-query.ts +194 -194
  126. package/src/openapi.ts +63 -63
  127. package/src/secrets/__tests__/encryption.spec.ts +226 -0
  128. package/src/secrets/__tests__/generator.spec.ts +319 -0
  129. package/src/secrets/__tests__/index.spec.ts +91 -0
  130. package/src/secrets/__tests__/storage.spec.ts +403 -0
  131. package/src/secrets/encryption.ts +91 -0
  132. package/src/secrets/generator.ts +164 -0
  133. package/src/secrets/index.ts +383 -0
  134. package/src/secrets/storage.ts +134 -0
  135. package/src/secrets/types.ts +53 -0
  136. package/src/types.ts +295 -176
  137. package/tsdown.config.ts +11 -8
  138. package/dist/config-BrkUalUh.mjs.map +0 -1
  139. package/dist/config-C9aXOHBe.cjs.map +0 -1
  140. package/dist/openapi-BeHLKcwP.cjs.map +0 -1
  141. package/dist/openapi-CZLI4QTr.mjs.map +0 -1
  142. package/dist/openapi-react-query-CcciaVu5.mjs.map +0 -1
  143. package/dist/openapi-react-query-o5iMi8tz.cjs.map +0 -1
  144. package/dist/types-DXgiA1sF.d.mts.map +0 -1
  145. package/dist/types-b-vwGpqc.d.cts.map +0 -1
@@ -3,432 +3,432 @@ import { readFile, rm } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
4
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
5
  import {
6
- OPENAPI_OUTPUT_PATH,
7
- generateOpenApi,
8
- openapiCommand,
9
- resolveOpenApiConfig,
6
+ generateOpenApi,
7
+ OPENAPI_OUTPUT_PATH,
8
+ openapiCommand,
9
+ resolveOpenApiConfig,
10
10
  } from '../openapi';
11
11
  import type { GkmConfig } from '../types';
12
12
  import {
13
- cleanupDir,
14
- createMockEndpointFile,
15
- createTempDir,
16
- createTestFile,
13
+ cleanupDir,
14
+ createMockEndpointFile,
15
+ createTempDir,
16
+ createTestFile,
17
17
  } from './test-helpers';
18
18
 
19
19
  describe('resolveOpenApiConfig', () => {
20
- const baseConfig: GkmConfig = {
21
- routes: './src/endpoints/**/*.ts',
22
- envParser: './src/config/env#envParser',
23
- logger: './src/config/logger#logger',
24
- };
25
-
26
- it('should return disabled when openapi is false', () => {
27
- const result = resolveOpenApiConfig({ ...baseConfig, openapi: false });
28
- expect(result).toEqual({ enabled: false });
29
- });
30
-
31
- it('should return enabled with defaults when openapi is true', () => {
32
- const result = resolveOpenApiConfig({ ...baseConfig, openapi: true });
33
- expect(result).toEqual({
34
- enabled: true,
35
- title: 'API Documentation',
36
- version: '1.0.0',
37
- description: 'Auto-generated API documentation from endpoints',
38
- });
39
- });
40
-
41
- it('should return disabled when openapi is undefined', () => {
42
- const result = resolveOpenApiConfig({ ...baseConfig });
43
- expect(result.enabled).toBe(false);
44
- });
45
-
46
- it('should use custom config values when provided', () => {
47
- const result = resolveOpenApiConfig({
48
- ...baseConfig,
49
- openapi: {
50
- enabled: true,
51
- title: 'My API',
52
- version: '2.0.0',
53
- description: 'Custom description',
54
- },
55
- });
56
- expect(result).toEqual({
57
- enabled: true,
58
- title: 'My API',
59
- version: '2.0.0',
60
- description: 'Custom description',
61
- });
62
- });
63
-
64
- it('should use defaults for missing optional config values', () => {
65
- const result = resolveOpenApiConfig({
66
- ...baseConfig,
67
- openapi: { enabled: true },
68
- });
69
- expect(result).toEqual({
70
- enabled: true,
71
- title: 'API Documentation',
72
- version: '1.0.0',
73
- description: 'Auto-generated API documentation from endpoints',
74
- });
75
- });
76
-
77
- it('should be enabled by default when object provided without enabled field', () => {
78
- const result = resolveOpenApiConfig({
79
- ...baseConfig,
80
- openapi: { title: 'Custom Title' },
81
- });
82
- expect(result.enabled).toBe(true);
83
- });
20
+ const baseConfig: GkmConfig = {
21
+ routes: './src/endpoints/**/*.ts',
22
+ envParser: './src/config/env#envParser',
23
+ logger: './src/config/logger#logger',
24
+ };
25
+
26
+ it('should return disabled when openapi is false', () => {
27
+ const result = resolveOpenApiConfig({ ...baseConfig, openapi: false });
28
+ expect(result).toEqual({ enabled: false });
29
+ });
30
+
31
+ it('should return enabled with defaults when openapi is true', () => {
32
+ const result = resolveOpenApiConfig({ ...baseConfig, openapi: true });
33
+ expect(result).toEqual({
34
+ enabled: true,
35
+ title: 'API Documentation',
36
+ version: '1.0.0',
37
+ description: 'Auto-generated API documentation from endpoints',
38
+ });
39
+ });
40
+
41
+ it('should return disabled when openapi is undefined', () => {
42
+ const result = resolveOpenApiConfig({ ...baseConfig });
43
+ expect(result.enabled).toBe(false);
44
+ });
45
+
46
+ it('should use custom config values when provided', () => {
47
+ const result = resolveOpenApiConfig({
48
+ ...baseConfig,
49
+ openapi: {
50
+ enabled: true,
51
+ title: 'My API',
52
+ version: '2.0.0',
53
+ description: 'Custom description',
54
+ },
55
+ });
56
+ expect(result).toEqual({
57
+ enabled: true,
58
+ title: 'My API',
59
+ version: '2.0.0',
60
+ description: 'Custom description',
61
+ });
62
+ });
63
+
64
+ it('should use defaults for missing optional config values', () => {
65
+ const result = resolveOpenApiConfig({
66
+ ...baseConfig,
67
+ openapi: { enabled: true },
68
+ });
69
+ expect(result).toEqual({
70
+ enabled: true,
71
+ title: 'API Documentation',
72
+ version: '1.0.0',
73
+ description: 'Auto-generated API documentation from endpoints',
74
+ });
75
+ });
76
+
77
+ it('should be enabled by default when object provided without enabled field', () => {
78
+ const result = resolveOpenApiConfig({
79
+ ...baseConfig,
80
+ openapi: { title: 'Custom Title' },
81
+ });
82
+ expect(result.enabled).toBe(true);
83
+ });
84
84
  });
85
85
 
86
86
  describe('generateOpenApi', () => {
87
- let tempDir: string;
88
- const originalCwd = process.cwd();
89
-
90
- beforeEach(async () => {
91
- tempDir = realpathSync(await createTempDir('openapi-gen-'));
92
- // Change to temp dir so output goes there
93
- process.chdir(tempDir);
94
- });
95
-
96
- afterEach(async () => {
97
- process.chdir(originalCwd);
98
- await cleanupDir(tempDir);
99
- vi.restoreAllMocks();
100
- });
101
-
102
- it('should return null when openapi is disabled', async () => {
103
- const config: GkmConfig = {
104
- routes: './src/endpoints/**/*.ts',
105
- envParser: './src/config/env#envParser',
106
- logger: './src/config/logger#logger',
107
- openapi: false,
108
- };
109
-
110
- const result = await generateOpenApi(config);
111
- expect(result).toBeNull();
112
- });
113
-
114
- it('should return null when openapi is undefined', async () => {
115
- const config: GkmConfig = {
116
- routes: './src/endpoints/**/*.ts',
117
- envParser: './src/config/env#envParser',
118
- logger: './src/config/logger#logger',
119
- };
120
-
121
- const result = await generateOpenApi(config);
122
- expect(result).toBeNull();
123
- });
124
-
125
- it('should generate to fixed .gkm/openapi.ts path', async () => {
126
- await createMockEndpointFile(tempDir, 'test.ts', 'test', '/test', 'GET');
127
-
128
- const config: GkmConfig = {
129
- routes: `${tempDir}/**/*.ts`,
130
- envParser: './src/config/env#envParser',
131
- logger: './src/config/logger#logger',
132
- openapi: { enabled: true },
133
- };
134
-
135
- const result = await generateOpenApi(config, { silent: true });
136
-
137
- expect(result).not.toBeNull();
138
- expect(result?.endpointCount).toBe(1);
139
- expect(result?.outputPath).toBe(join(tempDir, OPENAPI_OUTPUT_PATH));
140
- expect(existsSync(join(tempDir, OPENAPI_OUTPUT_PATH))).toBe(true);
141
- });
142
-
143
- it('should generate TypeScript content', async () => {
144
- await createMockEndpointFile(tempDir, 'test.ts', 'test', '/test', 'GET');
145
-
146
- const config: GkmConfig = {
147
- routes: `${tempDir}/**/*.ts`,
148
- envParser: './src/config/env#envParser',
149
- logger: './src/config/logger#logger',
150
- openapi: { enabled: true },
151
- };
152
-
153
- await generateOpenApi(config, { silent: true });
154
-
155
- const content = await readFile(join(tempDir, OPENAPI_OUTPUT_PATH), 'utf-8');
156
- expect(content).toContain('// Auto-generated by @geekmidas/cli');
157
- expect(content).toContain('export const securitySchemes');
158
- expect(content).toContain('export interface paths');
159
- });
160
-
161
- it('should log no endpoints message when none found', async () => {
162
- const config: GkmConfig = {
163
- routes: `${tempDir}/nonexistent/**/*.ts`,
164
- envParser: './src/config/env#envParser',
165
- logger: './src/config/logger#logger',
166
- openapi: { enabled: true },
167
- };
168
-
169
- const consoleSpy = vi.spyOn(console, 'log');
170
- const result = await generateOpenApi(config);
171
-
172
- expect(result).toBeNull();
173
- expect(consoleSpy).toHaveBeenCalledWith(
174
- 'No valid endpoints found for OpenAPI generation',
175
- );
176
- });
87
+ let tempDir: string;
88
+ const originalCwd = process.cwd();
89
+
90
+ beforeEach(async () => {
91
+ tempDir = realpathSync(await createTempDir('openapi-gen-'));
92
+ // Change to temp dir so output goes there
93
+ process.chdir(tempDir);
94
+ });
95
+
96
+ afterEach(async () => {
97
+ process.chdir(originalCwd);
98
+ await cleanupDir(tempDir);
99
+ vi.restoreAllMocks();
100
+ });
101
+
102
+ it('should return null when openapi is disabled', async () => {
103
+ const config: GkmConfig = {
104
+ routes: './src/endpoints/**/*.ts',
105
+ envParser: './src/config/env#envParser',
106
+ logger: './src/config/logger#logger',
107
+ openapi: false,
108
+ };
109
+
110
+ const result = await generateOpenApi(config);
111
+ expect(result).toBeNull();
112
+ });
113
+
114
+ it('should return null when openapi is undefined', async () => {
115
+ const config: GkmConfig = {
116
+ routes: './src/endpoints/**/*.ts',
117
+ envParser: './src/config/env#envParser',
118
+ logger: './src/config/logger#logger',
119
+ };
120
+
121
+ const result = await generateOpenApi(config);
122
+ expect(result).toBeNull();
123
+ });
124
+
125
+ it('should generate to fixed .gkm/openapi.ts path', async () => {
126
+ await createMockEndpointFile(tempDir, 'test.ts', 'test', '/test', 'GET');
127
+
128
+ const config: GkmConfig = {
129
+ routes: `${tempDir}/**/*.ts`,
130
+ envParser: './src/config/env#envParser',
131
+ logger: './src/config/logger#logger',
132
+ openapi: { enabled: true },
133
+ };
134
+
135
+ const result = await generateOpenApi(config, { silent: true });
136
+
137
+ expect(result).not.toBeNull();
138
+ expect(result?.endpointCount).toBe(1);
139
+ expect(result?.outputPath).toBe(join(tempDir, OPENAPI_OUTPUT_PATH));
140
+ expect(existsSync(join(tempDir, OPENAPI_OUTPUT_PATH))).toBe(true);
141
+ });
142
+
143
+ it('should generate TypeScript content', async () => {
144
+ await createMockEndpointFile(tempDir, 'test.ts', 'test', '/test', 'GET');
145
+
146
+ const config: GkmConfig = {
147
+ routes: `${tempDir}/**/*.ts`,
148
+ envParser: './src/config/env#envParser',
149
+ logger: './src/config/logger#logger',
150
+ openapi: { enabled: true },
151
+ };
152
+
153
+ await generateOpenApi(config, { silent: true });
154
+
155
+ const content = await readFile(join(tempDir, OPENAPI_OUTPUT_PATH), 'utf-8');
156
+ expect(content).toContain('// Auto-generated by @geekmidas/cli');
157
+ expect(content).toContain('export const securitySchemes');
158
+ expect(content).toContain('export interface paths');
159
+ });
160
+
161
+ it('should log no endpoints message when none found', async () => {
162
+ const config: GkmConfig = {
163
+ routes: `${tempDir}/nonexistent/**/*.ts`,
164
+ envParser: './src/config/env#envParser',
165
+ logger: './src/config/logger#logger',
166
+ openapi: { enabled: true },
167
+ };
168
+
169
+ const consoleSpy = vi.spyOn(console, 'log');
170
+ const result = await generateOpenApi(config);
171
+
172
+ expect(result).toBeNull();
173
+ expect(consoleSpy).toHaveBeenCalledWith(
174
+ 'No valid endpoints found for OpenAPI generation',
175
+ );
176
+ });
177
177
  });
178
178
 
179
179
  describe('openapiCommand', () => {
180
- let tempDir: string;
181
- const originalCwd = process.cwd();
182
-
183
- beforeEach(async () => {
184
- tempDir = realpathSync(await createTempDir('openapi-cmd-'));
185
- });
186
-
187
- afterEach(async () => {
188
- process.chdir(originalCwd);
189
- // Clean up any generated .gkm folder in current directory
190
- await rm(join(originalCwd, '.gkm'), { recursive: true, force: true });
191
- await cleanupDir(tempDir);
192
- vi.restoreAllMocks();
193
- });
194
-
195
- it('should generate OpenAPI client to .gkm/openapi.ts', async () => {
196
- await createMockEndpointFile(
197
- tempDir,
198
- 'test.ts',
199
- 'testEndpoint',
200
- '/test',
201
- 'GET',
202
- );
203
-
204
- await createTestFile(
205
- tempDir,
206
- 'gkm.config.json',
207
- JSON.stringify({
208
- routes: [`${tempDir}/**/*.ts`],
209
- openapi: { enabled: true },
210
- }),
211
- );
212
-
213
- // Change to temp dir so output goes there
214
- process.chdir(tempDir);
215
-
216
- await openapiCommand({ cwd: tempDir });
217
-
218
- const outputPath = join(tempDir, OPENAPI_OUTPUT_PATH);
219
- expect(existsSync(outputPath)).toBe(true);
220
-
221
- const content = await readFile(outputPath, 'utf-8');
222
- expect(content).toContain('// Auto-generated by @geekmidas/cli');
223
- });
224
-
225
- it('should enable openapi with defaults when not configured', async () => {
226
- await createMockEndpointFile(
227
- tempDir,
228
- 'test.ts',
229
- 'testEndpoint',
230
- '/test',
231
- 'GET',
232
- );
233
-
234
- await createTestFile(
235
- tempDir,
236
- 'gkm.config.json',
237
- JSON.stringify({
238
- routes: [`${tempDir}/**/*.ts`],
239
- }),
240
- );
241
-
242
- process.chdir(tempDir);
243
- const consoleSpy = vi.spyOn(console, 'log');
244
-
245
- await openapiCommand({ cwd: tempDir });
246
-
247
- expect(consoleSpy).toHaveBeenCalledWith(
248
- expect.stringContaining('Found 1 endpoints'),
249
- );
250
- expect(existsSync(join(tempDir, OPENAPI_OUTPUT_PATH))).toBe(true);
251
- });
252
-
253
- it('should include endpoint auth map', async () => {
254
- await createMockEndpointFile(
255
- tempDir,
256
- 'getUser.ts',
257
- 'getUser',
258
- '/users/:id',
259
- 'GET',
260
- );
261
-
262
- await createTestFile(
263
- tempDir,
264
- 'gkm.config.json',
265
- JSON.stringify({
266
- routes: [`${tempDir}/**/*.ts`],
267
- openapi: { enabled: true },
268
- }),
269
- );
270
-
271
- process.chdir(tempDir);
272
-
273
- await openapiCommand({ cwd: tempDir });
274
-
275
- const content = await readFile(join(tempDir, OPENAPI_OUTPUT_PATH), 'utf-8');
276
- expect(content).toContain('endpointAuth');
277
- expect(content).toContain("'GET /users/{id}'");
278
- });
279
-
280
- it('should handle no endpoints found', async () => {
281
- await createTestFile(
282
- tempDir,
283
- 'gkm.config.json',
284
- JSON.stringify({
285
- routes: [`${tempDir}/nonexistent/**/*.ts`],
286
- openapi: { enabled: true },
287
- }),
288
- );
289
-
290
- process.chdir(tempDir);
291
- const consoleSpy = vi.spyOn(console, 'log');
292
-
293
- await openapiCommand({ cwd: tempDir });
294
-
295
- expect(consoleSpy).toHaveBeenCalledWith(
296
- 'No valid endpoints found for OpenAPI generation',
297
- );
298
- });
299
-
300
- it('should generate with multiple endpoints', async () => {
301
- await createMockEndpointFile(
302
- tempDir,
303
- 'getUsers.ts',
304
- 'getUsers',
305
- '/users',
306
- 'GET',
307
- );
308
- await createMockEndpointFile(
309
- tempDir,
310
- 'createUser.ts',
311
- 'createUser',
312
- '/users',
313
- 'POST',
314
- );
315
- await createMockEndpointFile(
316
- tempDir,
317
- 'deleteUser.ts',
318
- 'deleteUser',
319
- '/users/:id',
320
- 'DELETE',
321
- );
322
-
323
- await createTestFile(
324
- tempDir,
325
- 'gkm.config.json',
326
- JSON.stringify({
327
- routes: [`${tempDir}/**/*.ts`],
328
- openapi: { enabled: true },
329
- }),
330
- );
331
-
332
- process.chdir(tempDir);
333
- const consoleSpy = vi.spyOn(console, 'log');
334
-
335
- await openapiCommand({ cwd: tempDir });
336
-
337
- expect(consoleSpy).toHaveBeenCalledWith(
338
- expect.stringContaining('Found 3 endpoints'),
339
- );
340
- });
341
-
342
- it('should create .gkm directory if it does not exist', async () => {
343
- await createMockEndpointFile(
344
- tempDir,
345
- 'endpoint.ts',
346
- 'testEndpoint',
347
- '/test',
348
- 'GET',
349
- );
350
-
351
- await createTestFile(
352
- tempDir,
353
- 'gkm.config.json',
354
- JSON.stringify({
355
- routes: [`${tempDir}/**/*.ts`],
356
- openapi: { enabled: true },
357
- }),
358
- );
359
-
360
- process.chdir(tempDir);
361
-
362
- await openapiCommand({ cwd: tempDir });
363
-
364
- expect(existsSync(join(tempDir, '.gkm'))).toBe(true);
365
- expect(existsSync(join(tempDir, OPENAPI_OUTPUT_PATH))).toBe(true);
366
- });
367
-
368
- it('should throw error when config loading fails', async () => {
369
- process.chdir(tempDir);
370
-
371
- await expect(openapiCommand({ cwd: tempDir })).rejects.toThrow(
372
- /OpenAPI generation failed/,
373
- );
374
- });
375
-
376
- it('should throw error for invalid TypeScript files', async () => {
377
- await createTestFile(
378
- tempDir,
379
- 'invalid.ts',
380
- 'this is not valid typescript {[}]',
381
- );
382
-
383
- await createTestFile(
384
- tempDir,
385
- 'gkm.config.json',
386
- JSON.stringify({
387
- routes: [`${tempDir}/**/*.ts`],
388
- openapi: { enabled: true },
389
- }),
390
- );
391
-
392
- process.chdir(tempDir);
393
-
394
- await expect(openapiCommand({ cwd: tempDir })).rejects.toThrow(
395
- /OpenAPI generation failed/,
396
- );
397
- });
398
-
399
- it('should log generation success', async () => {
400
- await createMockEndpointFile(
401
- tempDir,
402
- 'endpoint.ts',
403
- 'testEndpoint',
404
- '/test',
405
- 'GET',
406
- );
407
-
408
- await createTestFile(
409
- tempDir,
410
- 'gkm.config.json',
411
- JSON.stringify({
412
- routes: [`${tempDir}/**/*.ts`],
413
- openapi: { enabled: true },
414
- }),
415
- );
416
-
417
- process.chdir(tempDir);
418
- const consoleSpy = vi.spyOn(console, 'log');
419
-
420
- await openapiCommand({ cwd: tempDir });
421
-
422
- expect(consoleSpy).toHaveBeenCalledWith(
423
- expect.stringContaining('OpenAPI client generated'),
424
- );
425
- expect(consoleSpy).toHaveBeenCalledWith(
426
- expect.stringContaining('Found 1 endpoints'),
427
- );
428
- });
429
-
430
- it('should handle endpoints with complex schemas', async () => {
431
- const complexEndpointContent = `
180
+ let tempDir: string;
181
+ const originalCwd = process.cwd();
182
+
183
+ beforeEach(async () => {
184
+ tempDir = realpathSync(await createTempDir('openapi-cmd-'));
185
+ });
186
+
187
+ afterEach(async () => {
188
+ process.chdir(originalCwd);
189
+ // Clean up any generated .gkm folder in current directory
190
+ await rm(join(originalCwd, '.gkm'), { recursive: true, force: true });
191
+ await cleanupDir(tempDir);
192
+ vi.restoreAllMocks();
193
+ });
194
+
195
+ it('should generate OpenAPI client to .gkm/openapi.ts', async () => {
196
+ await createMockEndpointFile(
197
+ tempDir,
198
+ 'test.ts',
199
+ 'testEndpoint',
200
+ '/test',
201
+ 'GET',
202
+ );
203
+
204
+ await createTestFile(
205
+ tempDir,
206
+ 'gkm.config.json',
207
+ JSON.stringify({
208
+ routes: [`${tempDir}/**/*.ts`],
209
+ openapi: { enabled: true },
210
+ }),
211
+ );
212
+
213
+ // Change to temp dir so output goes there
214
+ process.chdir(tempDir);
215
+
216
+ await openapiCommand({ cwd: tempDir });
217
+
218
+ const outputPath = join(tempDir, OPENAPI_OUTPUT_PATH);
219
+ expect(existsSync(outputPath)).toBe(true);
220
+
221
+ const content = await readFile(outputPath, 'utf-8');
222
+ expect(content).toContain('// Auto-generated by @geekmidas/cli');
223
+ });
224
+
225
+ it('should enable openapi with defaults when not configured', async () => {
226
+ await createMockEndpointFile(
227
+ tempDir,
228
+ 'test.ts',
229
+ 'testEndpoint',
230
+ '/test',
231
+ 'GET',
232
+ );
233
+
234
+ await createTestFile(
235
+ tempDir,
236
+ 'gkm.config.json',
237
+ JSON.stringify({
238
+ routes: [`${tempDir}/**/*.ts`],
239
+ }),
240
+ );
241
+
242
+ process.chdir(tempDir);
243
+ const consoleSpy = vi.spyOn(console, 'log');
244
+
245
+ await openapiCommand({ cwd: tempDir });
246
+
247
+ expect(consoleSpy).toHaveBeenCalledWith(
248
+ expect.stringContaining('Found 1 endpoints'),
249
+ );
250
+ expect(existsSync(join(tempDir, OPENAPI_OUTPUT_PATH))).toBe(true);
251
+ });
252
+
253
+ it('should include endpoint auth map', async () => {
254
+ await createMockEndpointFile(
255
+ tempDir,
256
+ 'getUser.ts',
257
+ 'getUser',
258
+ '/users/:id',
259
+ 'GET',
260
+ );
261
+
262
+ await createTestFile(
263
+ tempDir,
264
+ 'gkm.config.json',
265
+ JSON.stringify({
266
+ routes: [`${tempDir}/**/*.ts`],
267
+ openapi: { enabled: true },
268
+ }),
269
+ );
270
+
271
+ process.chdir(tempDir);
272
+
273
+ await openapiCommand({ cwd: tempDir });
274
+
275
+ const content = await readFile(join(tempDir, OPENAPI_OUTPUT_PATH), 'utf-8');
276
+ expect(content).toContain('endpointAuth');
277
+ expect(content).toContain("'GET /users/{id}'");
278
+ });
279
+
280
+ it('should handle no endpoints found', async () => {
281
+ await createTestFile(
282
+ tempDir,
283
+ 'gkm.config.json',
284
+ JSON.stringify({
285
+ routes: [`${tempDir}/nonexistent/**/*.ts`],
286
+ openapi: { enabled: true },
287
+ }),
288
+ );
289
+
290
+ process.chdir(tempDir);
291
+ const consoleSpy = vi.spyOn(console, 'log');
292
+
293
+ await openapiCommand({ cwd: tempDir });
294
+
295
+ expect(consoleSpy).toHaveBeenCalledWith(
296
+ 'No valid endpoints found for OpenAPI generation',
297
+ );
298
+ });
299
+
300
+ it('should generate with multiple endpoints', async () => {
301
+ await createMockEndpointFile(
302
+ tempDir,
303
+ 'getUsers.ts',
304
+ 'getUsers',
305
+ '/users',
306
+ 'GET',
307
+ );
308
+ await createMockEndpointFile(
309
+ tempDir,
310
+ 'createUser.ts',
311
+ 'createUser',
312
+ '/users',
313
+ 'POST',
314
+ );
315
+ await createMockEndpointFile(
316
+ tempDir,
317
+ 'deleteUser.ts',
318
+ 'deleteUser',
319
+ '/users/:id',
320
+ 'DELETE',
321
+ );
322
+
323
+ await createTestFile(
324
+ tempDir,
325
+ 'gkm.config.json',
326
+ JSON.stringify({
327
+ routes: [`${tempDir}/**/*.ts`],
328
+ openapi: { enabled: true },
329
+ }),
330
+ );
331
+
332
+ process.chdir(tempDir);
333
+ const consoleSpy = vi.spyOn(console, 'log');
334
+
335
+ await openapiCommand({ cwd: tempDir });
336
+
337
+ expect(consoleSpy).toHaveBeenCalledWith(
338
+ expect.stringContaining('Found 3 endpoints'),
339
+ );
340
+ });
341
+
342
+ it('should create .gkm directory if it does not exist', async () => {
343
+ await createMockEndpointFile(
344
+ tempDir,
345
+ 'endpoint.ts',
346
+ 'testEndpoint',
347
+ '/test',
348
+ 'GET',
349
+ );
350
+
351
+ await createTestFile(
352
+ tempDir,
353
+ 'gkm.config.json',
354
+ JSON.stringify({
355
+ routes: [`${tempDir}/**/*.ts`],
356
+ openapi: { enabled: true },
357
+ }),
358
+ );
359
+
360
+ process.chdir(tempDir);
361
+
362
+ await openapiCommand({ cwd: tempDir });
363
+
364
+ expect(existsSync(join(tempDir, '.gkm'))).toBe(true);
365
+ expect(existsSync(join(tempDir, OPENAPI_OUTPUT_PATH))).toBe(true);
366
+ });
367
+
368
+ it('should throw error when config loading fails', async () => {
369
+ process.chdir(tempDir);
370
+
371
+ await expect(openapiCommand({ cwd: tempDir })).rejects.toThrow(
372
+ /OpenAPI generation failed/,
373
+ );
374
+ });
375
+
376
+ it('should throw error for invalid TypeScript files', async () => {
377
+ await createTestFile(
378
+ tempDir,
379
+ 'invalid.ts',
380
+ 'this is not valid typescript {[}]',
381
+ );
382
+
383
+ await createTestFile(
384
+ tempDir,
385
+ 'gkm.config.json',
386
+ JSON.stringify({
387
+ routes: [`${tempDir}/**/*.ts`],
388
+ openapi: { enabled: true },
389
+ }),
390
+ );
391
+
392
+ process.chdir(tempDir);
393
+
394
+ await expect(openapiCommand({ cwd: tempDir })).rejects.toThrow(
395
+ /OpenAPI generation failed/,
396
+ );
397
+ });
398
+
399
+ it('should log generation success', async () => {
400
+ await createMockEndpointFile(
401
+ tempDir,
402
+ 'endpoint.ts',
403
+ 'testEndpoint',
404
+ '/test',
405
+ 'GET',
406
+ );
407
+
408
+ await createTestFile(
409
+ tempDir,
410
+ 'gkm.config.json',
411
+ JSON.stringify({
412
+ routes: [`${tempDir}/**/*.ts`],
413
+ openapi: { enabled: true },
414
+ }),
415
+ );
416
+
417
+ process.chdir(tempDir);
418
+ const consoleSpy = vi.spyOn(console, 'log');
419
+
420
+ await openapiCommand({ cwd: tempDir });
421
+
422
+ expect(consoleSpy).toHaveBeenCalledWith(
423
+ expect.stringContaining('OpenAPI client generated'),
424
+ );
425
+ expect(consoleSpy).toHaveBeenCalledWith(
426
+ expect.stringContaining('Found 1 endpoints'),
427
+ );
428
+ });
429
+
430
+ it('should handle endpoints with complex schemas', async () => {
431
+ const complexEndpointContent = `
432
432
  import { e } from '@geekmidas/constructs/endpoints';
433
433
  import { z } from 'zod';
434
434
 
@@ -449,22 +449,22 @@ export const complexEndpoint = e
449
449
  .handle(async () => ({ id: '123', status: 'active' as const }));
450
450
  `;
451
451
 
452
- await createTestFile(tempDir, 'complex.ts', complexEndpointContent);
452
+ await createTestFile(tempDir, 'complex.ts', complexEndpointContent);
453
453
 
454
- await createTestFile(
455
- tempDir,
456
- 'gkm.config.json',
457
- JSON.stringify({
458
- routes: [`${tempDir}/**/*.ts`],
459
- openapi: { enabled: true },
460
- }),
461
- );
454
+ await createTestFile(
455
+ tempDir,
456
+ 'gkm.config.json',
457
+ JSON.stringify({
458
+ routes: [`${tempDir}/**/*.ts`],
459
+ openapi: { enabled: true },
460
+ }),
461
+ );
462
462
 
463
- process.chdir(tempDir);
463
+ process.chdir(tempDir);
464
464
 
465
- await openapiCommand({ cwd: tempDir });
465
+ await openapiCommand({ cwd: tempDir });
466
466
 
467
- const content = await readFile(join(tempDir, OPENAPI_OUTPUT_PATH), 'utf-8');
468
- expect(content).toContain('export interface paths');
469
- });
467
+ const content = await readFile(join(tempDir, OPENAPI_OUTPUT_PATH), 'utf-8');
468
+ expect(content).toContain('export interface paths');
469
+ });
470
470
  });