@geekmidas/cli 0.9.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 (146) 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-CFls09Ey.cjs → config-AmInkU7k.cjs} +10 -8
  7. package/dist/config-AmInkU7k.cjs.map +1 -0
  8. package/dist/{config-Bq72aj8e.mjs → config-DYULeEv8.mjs} +6 -4
  9. package/dist/config-DYULeEv8.mjs.map +1 -0
  10. package/dist/config.cjs +1 -1
  11. package/dist/config.d.cts +2 -1
  12. package/dist/config.d.cts.map +1 -0
  13. package/dist/config.d.mts +2 -1
  14. package/dist/config.d.mts.map +1 -0
  15. package/dist/config.mjs +1 -1
  16. package/dist/encryption-C8H-38Yy.mjs +42 -0
  17. package/dist/encryption-C8H-38Yy.mjs.map +1 -0
  18. package/dist/encryption-Dyf_r1h-.cjs +44 -0
  19. package/dist/encryption-Dyf_r1h-.cjs.map +1 -0
  20. package/dist/index.cjs +2125 -184
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.mjs +2143 -197
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/{openapi--vOy9mo4.mjs → openapi-BfFlOBCG.mjs} +812 -49
  25. package/dist/openapi-BfFlOBCG.mjs.map +1 -0
  26. package/dist/{openapi-CHhTPief.cjs → openapi-Bt_1FDpT.cjs} +805 -42
  27. package/dist/openapi-Bt_1FDpT.cjs.map +1 -0
  28. package/dist/{openapi-react-query-o5iMi8tz.cjs → openapi-react-query-B-sNWHFU.cjs} +5 -5
  29. package/dist/openapi-react-query-B-sNWHFU.cjs.map +1 -0
  30. package/dist/{openapi-react-query-CcciaVu5.mjs → openapi-react-query-B6XTeGqS.mjs} +5 -5
  31. package/dist/openapi-react-query-B6XTeGqS.mjs.map +1 -0
  32. package/dist/openapi-react-query.cjs +1 -1
  33. package/dist/openapi-react-query.d.cts.map +1 -0
  34. package/dist/openapi-react-query.d.mts.map +1 -0
  35. package/dist/openapi-react-query.mjs +1 -1
  36. package/dist/openapi.cjs +2 -2
  37. package/dist/openapi.d.cts +1 -1
  38. package/dist/openapi.d.cts.map +1 -0
  39. package/dist/openapi.d.mts +1 -1
  40. package/dist/openapi.d.mts.map +1 -0
  41. package/dist/openapi.mjs +2 -2
  42. package/dist/storage-BUYQJgz7.cjs +4 -0
  43. package/dist/storage-BXoJvmv2.cjs +149 -0
  44. package/dist/storage-BXoJvmv2.cjs.map +1 -0
  45. package/dist/storage-C9PU_30f.mjs +101 -0
  46. package/dist/storage-C9PU_30f.mjs.map +1 -0
  47. package/dist/storage-DLJAYxzJ.mjs +3 -0
  48. package/dist/{types-b-vwGpqc.d.cts → types-BR0M2v_c.d.mts} +100 -1
  49. package/dist/types-BR0M2v_c.d.mts.map +1 -0
  50. package/dist/{types-DXgiA1sF.d.mts → types-BhkZc-vm.d.cts} +100 -1
  51. package/dist/types-BhkZc-vm.d.cts.map +1 -0
  52. package/examples/cron-example.ts +27 -27
  53. package/examples/env.ts +27 -27
  54. package/examples/function-example.ts +31 -31
  55. package/examples/gkm.config.json +20 -20
  56. package/examples/gkm.config.ts +8 -8
  57. package/examples/gkm.minimal.config.json +5 -5
  58. package/examples/gkm.production.config.json +25 -25
  59. package/examples/logger.ts +2 -2
  60. package/package.json +6 -6
  61. package/src/__tests__/EndpointGenerator.hooks.spec.ts +191 -191
  62. package/src/__tests__/config.spec.ts +55 -55
  63. package/src/__tests__/loadEnvFiles.spec.ts +93 -93
  64. package/src/__tests__/normalizeHooksConfig.spec.ts +58 -58
  65. package/src/__tests__/openapi-react-query.spec.ts +497 -497
  66. package/src/__tests__/openapi.spec.ts +428 -428
  67. package/src/__tests__/test-helpers.ts +77 -76
  68. package/src/auth/__tests__/credentials.spec.ts +204 -0
  69. package/src/auth/__tests__/index.spec.ts +168 -0
  70. package/src/auth/credentials.ts +187 -0
  71. package/src/auth/index.ts +226 -0
  72. package/src/build/__tests__/index-new.spec.ts +474 -474
  73. package/src/build/__tests__/manifests.spec.ts +333 -333
  74. package/src/build/bundler.ts +141 -0
  75. package/src/build/endpoint-analyzer.ts +236 -0
  76. package/src/build/handler-templates.ts +1253 -0
  77. package/src/build/index.ts +250 -179
  78. package/src/build/manifests.ts +52 -52
  79. package/src/build/providerResolver.ts +145 -145
  80. package/src/build/types.ts +64 -43
  81. package/src/config.ts +39 -37
  82. package/src/deploy/__tests__/docker.spec.ts +111 -0
  83. package/src/deploy/__tests__/dokploy.spec.ts +245 -0
  84. package/src/deploy/__tests__/init.spec.ts +662 -0
  85. package/src/deploy/docker.ts +128 -0
  86. package/src/deploy/dokploy.ts +204 -0
  87. package/src/deploy/index.ts +136 -0
  88. package/src/deploy/init.ts +484 -0
  89. package/src/deploy/types.ts +48 -0
  90. package/src/dev/__tests__/index.spec.ts +266 -266
  91. package/src/dev/index.ts +647 -593
  92. package/src/docker/__tests__/compose.spec.ts +531 -0
  93. package/src/docker/__tests__/templates.spec.ts +280 -0
  94. package/src/docker/compose.ts +273 -0
  95. package/src/docker/index.ts +230 -0
  96. package/src/docker/templates.ts +446 -0
  97. package/src/generators/CronGenerator.ts +72 -72
  98. package/src/generators/EndpointGenerator.ts +699 -398
  99. package/src/generators/FunctionGenerator.ts +84 -84
  100. package/src/generators/Generator.ts +72 -72
  101. package/src/generators/OpenApiTsGenerator.ts +589 -589
  102. package/src/generators/SubscriberGenerator.ts +124 -124
  103. package/src/generators/__tests__/CronGenerator.spec.ts +433 -433
  104. package/src/generators/__tests__/EndpointGenerator.spec.ts +532 -382
  105. package/src/generators/__tests__/FunctionGenerator.spec.ts +244 -244
  106. package/src/generators/__tests__/SubscriberGenerator.spec.ts +397 -382
  107. package/src/generators/index.ts +4 -4
  108. package/src/index.ts +628 -206
  109. package/src/init/__tests__/generators.spec.ts +334 -334
  110. package/src/init/__tests__/init.spec.ts +332 -332
  111. package/src/init/__tests__/utils.spec.ts +89 -89
  112. package/src/init/generators/config.ts +175 -175
  113. package/src/init/generators/docker.ts +41 -41
  114. package/src/init/generators/env.ts +72 -72
  115. package/src/init/generators/index.ts +1 -1
  116. package/src/init/generators/models.ts +64 -64
  117. package/src/init/generators/monorepo.ts +161 -161
  118. package/src/init/generators/package.ts +71 -71
  119. package/src/init/generators/source.ts +6 -6
  120. package/src/init/index.ts +203 -208
  121. package/src/init/templates/api.ts +115 -115
  122. package/src/init/templates/index.ts +75 -75
  123. package/src/init/templates/minimal.ts +98 -98
  124. package/src/init/templates/serverless.ts +89 -89
  125. package/src/init/templates/worker.ts +98 -98
  126. package/src/init/utils.ts +54 -56
  127. package/src/openapi-react-query.ts +194 -194
  128. package/src/openapi.ts +63 -63
  129. package/src/secrets/__tests__/encryption.spec.ts +226 -0
  130. package/src/secrets/__tests__/generator.spec.ts +319 -0
  131. package/src/secrets/__tests__/index.spec.ts +91 -0
  132. package/src/secrets/__tests__/storage.spec.ts +403 -0
  133. package/src/secrets/encryption.ts +91 -0
  134. package/src/secrets/generator.ts +164 -0
  135. package/src/secrets/index.ts +383 -0
  136. package/src/secrets/storage.ts +134 -0
  137. package/src/secrets/types.ts +53 -0
  138. package/src/types.ts +295 -176
  139. package/tsconfig.json +9 -0
  140. package/tsdown.config.ts +11 -8
  141. package/dist/config-Bq72aj8e.mjs.map +0 -1
  142. package/dist/config-CFls09Ey.cjs.map +0 -1
  143. package/dist/openapi--vOy9mo4.mjs.map +0 -1
  144. package/dist/openapi-CHhTPief.cjs.map +0 -1
  145. package/dist/openapi-react-query-CcciaVu5.mjs.map +0 -1
  146. package/dist/openapi-react-query-o5iMi8tz.cjs.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
  });