@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.
- package/README.md +525 -0
- package/dist/bundler-DRXCw_YR.mjs +70 -0
- package/dist/bundler-DRXCw_YR.mjs.map +1 -0
- package/dist/bundler-WsEvH_b2.cjs +71 -0
- package/dist/bundler-WsEvH_b2.cjs.map +1 -0
- package/dist/{config-C9aXOHBe.cjs → config-AmInkU7k.cjs} +8 -8
- package/dist/config-AmInkU7k.cjs.map +1 -0
- package/dist/{config-BrkUalUh.mjs → config-DYULeEv8.mjs} +3 -3
- package/dist/config-DYULeEv8.mjs.map +1 -0
- package/dist/config.cjs +1 -1
- package/dist/config.d.cts +1 -1
- package/dist/config.d.mts +1 -1
- package/dist/config.mjs +1 -1
- package/dist/encryption-C8H-38Yy.mjs +42 -0
- package/dist/encryption-C8H-38Yy.mjs.map +1 -0
- package/dist/encryption-Dyf_r1h-.cjs +44 -0
- package/dist/encryption-Dyf_r1h-.cjs.map +1 -0
- package/dist/index.cjs +2116 -179
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +2134 -192
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-CZLI4QTr.mjs → openapi-BfFlOBCG.mjs} +801 -38
- package/dist/openapi-BfFlOBCG.mjs.map +1 -0
- package/dist/{openapi-BeHLKcwP.cjs → openapi-Bt_1FDpT.cjs} +794 -31
- package/dist/openapi-Bt_1FDpT.cjs.map +1 -0
- package/dist/{openapi-react-query-o5iMi8tz.cjs → openapi-react-query-B-sNWHFU.cjs} +5 -5
- package/dist/openapi-react-query-B-sNWHFU.cjs.map +1 -0
- package/dist/{openapi-react-query-CcciaVu5.mjs → openapi-react-query-B6XTeGqS.mjs} +5 -5
- package/dist/openapi-react-query-B6XTeGqS.mjs.map +1 -0
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.d.cts.map +1 -1
- package/dist/openapi-react-query.d.mts.map +1 -1
- package/dist/openapi-react-query.mjs +1 -1
- package/dist/openapi.cjs +2 -2
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.cts.map +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.d.mts.map +1 -1
- package/dist/openapi.mjs +2 -2
- package/dist/storage-BUYQJgz7.cjs +4 -0
- package/dist/storage-BXoJvmv2.cjs +149 -0
- package/dist/storage-BXoJvmv2.cjs.map +1 -0
- package/dist/storage-C9PU_30f.mjs +101 -0
- package/dist/storage-C9PU_30f.mjs.map +1 -0
- package/dist/storage-DLJAYxzJ.mjs +3 -0
- package/dist/{types-b-vwGpqc.d.cts → types-BR0M2v_c.d.mts} +100 -1
- package/dist/types-BR0M2v_c.d.mts.map +1 -0
- package/dist/{types-DXgiA1sF.d.mts → types-BhkZc-vm.d.cts} +100 -1
- package/dist/types-BhkZc-vm.d.cts.map +1 -0
- package/examples/cron-example.ts +27 -27
- package/examples/env.ts +27 -27
- package/examples/function-example.ts +31 -31
- package/examples/gkm.config.json +20 -20
- package/examples/gkm.config.ts +8 -8
- package/examples/gkm.minimal.config.json +5 -5
- package/examples/gkm.production.config.json +25 -25
- package/examples/logger.ts +2 -2
- package/package.json +6 -6
- package/src/__tests__/EndpointGenerator.hooks.spec.ts +191 -191
- package/src/__tests__/config.spec.ts +55 -55
- package/src/__tests__/loadEnvFiles.spec.ts +93 -93
- package/src/__tests__/normalizeHooksConfig.spec.ts +58 -58
- package/src/__tests__/openapi-react-query.spec.ts +497 -497
- package/src/__tests__/openapi.spec.ts +428 -428
- package/src/__tests__/test-helpers.ts +76 -76
- package/src/auth/__tests__/credentials.spec.ts +204 -0
- package/src/auth/__tests__/index.spec.ts +168 -0
- package/src/auth/credentials.ts +187 -0
- package/src/auth/index.ts +226 -0
- package/src/build/__tests__/index-new.spec.ts +474 -474
- package/src/build/__tests__/manifests.spec.ts +333 -333
- package/src/build/bundler.ts +141 -0
- package/src/build/endpoint-analyzer.ts +236 -0
- package/src/build/handler-templates.ts +1253 -0
- package/src/build/index.ts +250 -179
- package/src/build/manifests.ts +52 -52
- package/src/build/providerResolver.ts +145 -145
- package/src/build/types.ts +64 -43
- package/src/config.ts +39 -39
- package/src/deploy/__tests__/docker.spec.ts +111 -0
- package/src/deploy/__tests__/dokploy.spec.ts +245 -0
- package/src/deploy/__tests__/init.spec.ts +662 -0
- package/src/deploy/docker.ts +128 -0
- package/src/deploy/dokploy.ts +204 -0
- package/src/deploy/index.ts +136 -0
- package/src/deploy/init.ts +484 -0
- package/src/deploy/types.ts +48 -0
- package/src/dev/__tests__/index.spec.ts +266 -266
- package/src/dev/index.ts +647 -601
- package/src/docker/__tests__/compose.spec.ts +531 -0
- package/src/docker/__tests__/templates.spec.ts +280 -0
- package/src/docker/compose.ts +273 -0
- package/src/docker/index.ts +230 -0
- package/src/docker/templates.ts +446 -0
- package/src/generators/CronGenerator.ts +72 -72
- package/src/generators/EndpointGenerator.ts +699 -398
- package/src/generators/FunctionGenerator.ts +84 -84
- package/src/generators/Generator.ts +72 -72
- package/src/generators/OpenApiTsGenerator.ts +577 -577
- package/src/generators/SubscriberGenerator.ts +124 -124
- package/src/generators/__tests__/CronGenerator.spec.ts +433 -433
- package/src/generators/__tests__/EndpointGenerator.spec.ts +532 -382
- package/src/generators/__tests__/FunctionGenerator.spec.ts +244 -244
- package/src/generators/__tests__/SubscriberGenerator.spec.ts +397 -382
- package/src/generators/index.ts +4 -4
- package/src/index.ts +623 -201
- package/src/init/__tests__/generators.spec.ts +334 -334
- package/src/init/__tests__/init.spec.ts +332 -332
- package/src/init/__tests__/utils.spec.ts +89 -89
- package/src/init/generators/config.ts +175 -175
- package/src/init/generators/docker.ts +41 -41
- package/src/init/generators/env.ts +72 -72
- package/src/init/generators/index.ts +1 -1
- package/src/init/generators/models.ts +64 -64
- package/src/init/generators/monorepo.ts +161 -161
- package/src/init/generators/package.ts +71 -71
- package/src/init/generators/source.ts +6 -6
- package/src/init/index.ts +203 -208
- package/src/init/templates/api.ts +115 -115
- package/src/init/templates/index.ts +75 -75
- package/src/init/templates/minimal.ts +98 -98
- package/src/init/templates/serverless.ts +89 -89
- package/src/init/templates/worker.ts +98 -98
- package/src/init/utils.ts +54 -56
- package/src/openapi-react-query.ts +194 -194
- package/src/openapi.ts +63 -63
- package/src/secrets/__tests__/encryption.spec.ts +226 -0
- package/src/secrets/__tests__/generator.spec.ts +319 -0
- package/src/secrets/__tests__/index.spec.ts +91 -0
- package/src/secrets/__tests__/storage.spec.ts +403 -0
- package/src/secrets/encryption.ts +91 -0
- package/src/secrets/generator.ts +164 -0
- package/src/secrets/index.ts +383 -0
- package/src/secrets/storage.ts +134 -0
- package/src/secrets/types.ts +53 -0
- package/src/types.ts +295 -176
- package/tsdown.config.ts +11 -8
- package/dist/config-BrkUalUh.mjs.map +0 -1
- package/dist/config-C9aXOHBe.cjs.map +0 -1
- package/dist/openapi-BeHLKcwP.cjs.map +0 -1
- package/dist/openapi-CZLI4QTr.mjs.map +0 -1
- package/dist/openapi-react-query-CcciaVu5.mjs.map +0 -1
- package/dist/openapi-react-query-o5iMi8tz.cjs.map +0 -1
- package/dist/types-DXgiA1sF.d.mts.map +0 -1
- 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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
cleanupDir,
|
|
14
|
+
createMockEndpointFile,
|
|
15
|
+
createTempDir,
|
|
16
|
+
createTestFile,
|
|
17
17
|
} from './test-helpers';
|
|
18
18
|
|
|
19
19
|
describe('resolveOpenApiConfig', () => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
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
|
-
|
|
452
|
+
await createTestFile(tempDir, 'complex.ts', complexEndpointContent);
|
|
453
453
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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
|
-
|
|
463
|
+
process.chdir(tempDir);
|
|
464
464
|
|
|
465
|
-
|
|
465
|
+
await openapiCommand({ cwd: tempDir });
|
|
466
466
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
467
|
+
const content = await readFile(join(tempDir, OPENAPI_OUTPUT_PATH), 'utf-8');
|
|
468
|
+
expect(content).toContain('export interface paths');
|
|
469
|
+
});
|
|
470
470
|
});
|