@geekmidas/cli 0.18.0 → 0.19.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 (118) hide show
  1. package/dist/{bundler-C74EKlNa.cjs → bundler-CyHg1v_T.cjs} +3 -3
  2. package/dist/{bundler-C74EKlNa.cjs.map → bundler-CyHg1v_T.cjs.map} +1 -1
  3. package/dist/{bundler-B6z6HEeh.mjs → bundler-DQIuE3Kn.mjs} +3 -3
  4. package/dist/{bundler-B6z6HEeh.mjs.map → bundler-DQIuE3Kn.mjs.map} +1 -1
  5. package/dist/{config-DYULeEv8.mjs → config-BaYqrF3n.mjs} +48 -10
  6. package/dist/config-BaYqrF3n.mjs.map +1 -0
  7. package/dist/{config-AmInkU7k.cjs → config-CxrLu8ia.cjs} +53 -9
  8. package/dist/config-CxrLu8ia.cjs.map +1 -0
  9. package/dist/config.cjs +4 -1
  10. package/dist/config.d.cts +27 -2
  11. package/dist/config.d.cts.map +1 -1
  12. package/dist/config.d.mts +27 -2
  13. package/dist/config.d.mts.map +1 -1
  14. package/dist/config.mjs +3 -2
  15. package/dist/dokploy-api-B0w17y4_.mjs +3 -0
  16. package/dist/{dokploy-api-CaETb2L6.mjs → dokploy-api-B9qR2Yn1.mjs} +1 -1
  17. package/dist/{dokploy-api-CaETb2L6.mjs.map → dokploy-api-B9qR2Yn1.mjs.map} +1 -1
  18. package/dist/dokploy-api-BnGeUqN4.cjs +3 -0
  19. package/dist/{dokploy-api-C7F9VykY.cjs → dokploy-api-C5czOZoc.cjs} +1 -1
  20. package/dist/{dokploy-api-C7F9VykY.cjs.map → dokploy-api-C5czOZoc.cjs.map} +1 -1
  21. package/dist/{encryption-D7Efcdi9.cjs → encryption-BAz0xQ1Q.cjs} +1 -1
  22. package/dist/{encryption-D7Efcdi9.cjs.map → encryption-BAz0xQ1Q.cjs.map} +1 -1
  23. package/dist/{encryption-h4Nb6W-M.mjs → encryption-JtMsiGNp.mjs} +2 -2
  24. package/dist/{encryption-h4Nb6W-M.mjs.map → encryption-JtMsiGNp.mjs.map} +1 -1
  25. package/dist/index-CWN-bgrO.d.mts +495 -0
  26. package/dist/index-CWN-bgrO.d.mts.map +1 -0
  27. package/dist/index-DEWYvYvg.d.cts +495 -0
  28. package/dist/index-DEWYvYvg.d.cts.map +1 -0
  29. package/dist/index.cjs +2639 -563
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.mjs +2634 -563
  32. package/dist/index.mjs.map +1 -1
  33. package/dist/{openapi-CZVcfxk-.mjs → openapi-CgqR6Jkw.mjs} +3 -3
  34. package/dist/{openapi-CZVcfxk-.mjs.map → openapi-CgqR6Jkw.mjs.map} +1 -1
  35. package/dist/{openapi-C89hhkZC.cjs → openapi-DfpxS0xv.cjs} +8 -2
  36. package/dist/{openapi-C89hhkZC.cjs.map → openapi-DfpxS0xv.cjs.map} +1 -1
  37. package/dist/{openapi-react-query-CM2_qlW9.mjs → openapi-react-query-5rSortLH.mjs} +1 -1
  38. package/dist/{openapi-react-query-CM2_qlW9.mjs.map → openapi-react-query-5rSortLH.mjs.map} +1 -1
  39. package/dist/{openapi-react-query-iKjfLzff.cjs → openapi-react-query-DvNpdDpM.cjs} +1 -1
  40. package/dist/{openapi-react-query-iKjfLzff.cjs.map → openapi-react-query-DvNpdDpM.cjs.map} +1 -1
  41. package/dist/openapi-react-query.cjs +1 -1
  42. package/dist/openapi-react-query.mjs +1 -1
  43. package/dist/openapi.cjs +3 -2
  44. package/dist/openapi.d.cts +1 -1
  45. package/dist/openapi.d.mts +1 -1
  46. package/dist/openapi.mjs +3 -2
  47. package/dist/{storage-Bn3K9Ccu.cjs → storage-BPRgh3DU.cjs} +136 -5
  48. package/dist/storage-BPRgh3DU.cjs.map +1 -0
  49. package/dist/{storage-nkGIjeXt.mjs → storage-DNj_I11J.mjs} +1 -1
  50. package/dist/storage-Dhst7BhI.mjs +272 -0
  51. package/dist/storage-Dhst7BhI.mjs.map +1 -0
  52. package/dist/{storage-UfyTn7Zm.cjs → storage-fOR8dMu5.cjs} +1 -1
  53. package/dist/{types-iFk5ms7y.d.mts → types-K2uQJ-FO.d.mts} +2 -2
  54. package/dist/{types-BgaMXsUa.d.cts.map → types-K2uQJ-FO.d.mts.map} +1 -1
  55. package/dist/{types-BgaMXsUa.d.cts → types-l53qUmGt.d.cts} +2 -2
  56. package/dist/{types-iFk5ms7y.d.mts.map → types-l53qUmGt.d.cts.map} +1 -1
  57. package/dist/workspace/index.cjs +19 -0
  58. package/dist/workspace/index.d.cts +3 -0
  59. package/dist/workspace/index.d.mts +3 -0
  60. package/dist/workspace/index.mjs +3 -0
  61. package/dist/workspace-CPLEZDZf.mjs +3788 -0
  62. package/dist/workspace-CPLEZDZf.mjs.map +1 -0
  63. package/dist/workspace-iWgBlX6h.cjs +3885 -0
  64. package/dist/workspace-iWgBlX6h.cjs.map +1 -0
  65. package/package.json +8 -3
  66. package/src/build/__tests__/workspace-build.spec.ts +215 -0
  67. package/src/build/index.ts +189 -1
  68. package/src/config.ts +71 -14
  69. package/src/deploy/__tests__/docker.spec.ts +1 -1
  70. package/src/deploy/__tests__/index.spec.ts +305 -1
  71. package/src/deploy/index.ts +426 -4
  72. package/src/deploy/types.ts +32 -0
  73. package/src/dev/__tests__/index.spec.ts +572 -1
  74. package/src/dev/index.ts +582 -2
  75. package/src/docker/__tests__/compose.spec.ts +425 -0
  76. package/src/docker/__tests__/templates.spec.ts +145 -0
  77. package/src/docker/compose.ts +248 -0
  78. package/src/docker/index.ts +159 -3
  79. package/src/docker/templates.ts +219 -4
  80. package/src/index.ts +24 -0
  81. package/src/init/__tests__/generators.spec.ts +17 -24
  82. package/src/init/__tests__/init.spec.ts +157 -5
  83. package/src/init/generators/auth.ts +220 -0
  84. package/src/init/generators/config.ts +61 -4
  85. package/src/init/generators/docker.ts +115 -8
  86. package/src/init/generators/env.ts +7 -127
  87. package/src/init/generators/index.ts +1 -0
  88. package/src/init/generators/models.ts +3 -1
  89. package/src/init/generators/monorepo.ts +154 -10
  90. package/src/init/generators/package.ts +5 -3
  91. package/src/init/generators/web.ts +213 -0
  92. package/src/init/index.ts +290 -58
  93. package/src/init/templates/api.ts +38 -29
  94. package/src/init/templates/index.ts +132 -4
  95. package/src/init/templates/minimal.ts +33 -35
  96. package/src/init/templates/serverless.ts +16 -19
  97. package/src/init/templates/worker.ts +50 -25
  98. package/src/init/versions.ts +47 -0
  99. package/src/secrets/keystore.ts +144 -0
  100. package/src/secrets/storage.ts +109 -6
  101. package/src/test/index.ts +97 -0
  102. package/src/workspace/__tests__/client-generator.spec.ts +357 -0
  103. package/src/workspace/__tests__/index.spec.ts +543 -0
  104. package/src/workspace/__tests__/schema.spec.ts +519 -0
  105. package/src/workspace/__tests__/type-inference.spec.ts +251 -0
  106. package/src/workspace/client-generator.ts +307 -0
  107. package/src/workspace/index.ts +372 -0
  108. package/src/workspace/schema.ts +368 -0
  109. package/src/workspace/types.ts +336 -0
  110. package/tsconfig.tsbuildinfo +1 -1
  111. package/tsdown.config.ts +1 -0
  112. package/dist/config-AmInkU7k.cjs.map +0 -1
  113. package/dist/config-DYULeEv8.mjs.map +0 -1
  114. package/dist/dokploy-api-B7KxOQr3.cjs +0 -3
  115. package/dist/dokploy-api-DHvfmWbi.mjs +0 -3
  116. package/dist/storage-BaOP55oq.mjs +0 -147
  117. package/dist/storage-BaOP55oq.mjs.map +0 -1
  118. package/dist/storage-Bn3K9Ccu.cjs.map +0 -1
@@ -0,0 +1,357 @@
1
+ import { existsSync, mkdirSync, rmSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
5
+ import {
6
+ clearSpecHashCache,
7
+ generateClientForFrontend,
8
+ getBackendDependencies,
9
+ getDependentFrontends,
10
+ getFirstRoute,
11
+ normalizeRoutes,
12
+ shouldRegenerateClient,
13
+ } from '../client-generator.js';
14
+ import type { NormalizedWorkspace } from '../types.js';
15
+
16
+ describe('Client Generator', () => {
17
+ describe('normalizeRoutes', () => {
18
+ it('should return empty array for undefined', () => {
19
+ expect(normalizeRoutes(undefined)).toEqual([]);
20
+ });
21
+
22
+ it('should return array as-is', () => {
23
+ expect(normalizeRoutes(['./src/**/*.ts', './api/**/*.ts'])).toEqual([
24
+ './src/**/*.ts',
25
+ './api/**/*.ts',
26
+ ]);
27
+ });
28
+
29
+ it('should wrap string in array', () => {
30
+ expect(normalizeRoutes('./src/**/*.ts')).toEqual(['./src/**/*.ts']);
31
+ });
32
+ });
33
+
34
+ describe('getFirstRoute', () => {
35
+ it('should return null for undefined', () => {
36
+ expect(getFirstRoute(undefined)).toBeNull();
37
+ });
38
+
39
+ it('should return first element of array', () => {
40
+ expect(getFirstRoute(['./src/**/*.ts', './api/**/*.ts'])).toBe(
41
+ './src/**/*.ts',
42
+ );
43
+ });
44
+
45
+ it('should return string directly', () => {
46
+ expect(getFirstRoute('./src/**/*.ts')).toBe('./src/**/*.ts');
47
+ });
48
+
49
+ it('should return null for empty array', () => {
50
+ expect(getFirstRoute([])).toBeNull();
51
+ });
52
+ });
53
+
54
+ describe('shouldRegenerateClient', () => {
55
+ it('should return true for TypeScript files in routes directory', () => {
56
+ expect(
57
+ shouldRegenerateClient(
58
+ 'apps/api/src/endpoints/users.ts',
59
+ './src/endpoints/**/*.ts',
60
+ ),
61
+ ).toBe(true);
62
+ });
63
+
64
+ it('should return false for non-TypeScript files', () => {
65
+ expect(
66
+ shouldRegenerateClient(
67
+ 'apps/api/src/endpoints/README.md',
68
+ './src/endpoints/**/*.ts',
69
+ ),
70
+ ).toBe(false);
71
+ });
72
+
73
+ it('should return false for files outside routes pattern', () => {
74
+ expect(
75
+ shouldRegenerateClient(
76
+ 'apps/api/src/utils/helpers.ts',
77
+ './src/endpoints/**/*.ts',
78
+ ),
79
+ ).toBe(false);
80
+ });
81
+
82
+ it('should return true for .tsx files', () => {
83
+ expect(
84
+ shouldRegenerateClient(
85
+ 'apps/api/src/endpoints/users.tsx',
86
+ './src/endpoints/**/*.ts',
87
+ ),
88
+ ).toBe(true);
89
+ });
90
+ });
91
+
92
+ describe('getBackendDependencies', () => {
93
+ it('should return backend dependencies with routes', () => {
94
+ const workspace: NormalizedWorkspace = {
95
+ name: 'test',
96
+ root: '/test',
97
+ apps: {
98
+ api: {
99
+ type: 'backend',
100
+ path: 'apps/api',
101
+ port: 3000,
102
+ dependencies: [],
103
+ routes: './src/**/*.ts',
104
+ },
105
+ auth: {
106
+ type: 'backend',
107
+ path: 'apps/auth',
108
+ port: 3001,
109
+ dependencies: [],
110
+ routes: './src/**/*.ts',
111
+ },
112
+ web: {
113
+ type: 'frontend',
114
+ path: 'apps/web',
115
+ port: 3002,
116
+ dependencies: ['api', 'auth'],
117
+ },
118
+ },
119
+ services: {},
120
+ deploy: { default: 'dokploy' },
121
+ shared: { packages: [] },
122
+ secrets: {},
123
+ };
124
+
125
+ const deps = getBackendDependencies(workspace, 'web');
126
+ expect(deps).toEqual(['api', 'auth']);
127
+ });
128
+
129
+ it('should filter out backends without routes', () => {
130
+ const workspace: NormalizedWorkspace = {
131
+ name: 'test',
132
+ root: '/test',
133
+ apps: {
134
+ api: {
135
+ type: 'backend',
136
+ path: 'apps/api',
137
+ port: 3000,
138
+ dependencies: [],
139
+ routes: './src/**/*.ts',
140
+ },
141
+ worker: {
142
+ type: 'backend',
143
+ path: 'apps/worker',
144
+ port: 3001,
145
+ dependencies: [],
146
+ // No routes - not an HTTP backend
147
+ },
148
+ web: {
149
+ type: 'frontend',
150
+ path: 'apps/web',
151
+ port: 3002,
152
+ dependencies: ['api', 'worker'],
153
+ },
154
+ },
155
+ services: {},
156
+ deploy: { default: 'dokploy' },
157
+ shared: { packages: [] },
158
+ secrets: {},
159
+ };
160
+
161
+ const deps = getBackendDependencies(workspace, 'web');
162
+ expect(deps).toEqual(['api']);
163
+ });
164
+
165
+ it('should return empty array for non-frontend app', () => {
166
+ const workspace: NormalizedWorkspace = {
167
+ name: 'test',
168
+ root: '/test',
169
+ apps: {
170
+ api: {
171
+ type: 'backend',
172
+ path: 'apps/api',
173
+ port: 3000,
174
+ dependencies: [],
175
+ routes: './src/**/*.ts',
176
+ },
177
+ },
178
+ services: {},
179
+ deploy: { default: 'dokploy' },
180
+ shared: { packages: [] },
181
+ secrets: {},
182
+ };
183
+
184
+ const deps = getBackendDependencies(workspace, 'api');
185
+ expect(deps).toEqual([]);
186
+ });
187
+ });
188
+
189
+ describe('getDependentFrontends', () => {
190
+ it('should find frontends that depend on a backend', () => {
191
+ const workspace: NormalizedWorkspace = {
192
+ name: 'test',
193
+ root: '/test',
194
+ apps: {
195
+ api: {
196
+ type: 'backend',
197
+ path: 'apps/api',
198
+ port: 3000,
199
+ dependencies: [],
200
+ routes: './src/**/*.ts',
201
+ },
202
+ web: {
203
+ type: 'frontend',
204
+ path: 'apps/web',
205
+ port: 3001,
206
+ dependencies: ['api'],
207
+ },
208
+ admin: {
209
+ type: 'frontend',
210
+ path: 'apps/admin',
211
+ port: 3002,
212
+ dependencies: ['api'],
213
+ },
214
+ docs: {
215
+ type: 'frontend',
216
+ path: 'apps/docs',
217
+ port: 3003,
218
+ dependencies: [], // No API dependency
219
+ },
220
+ },
221
+ services: {},
222
+ deploy: { default: 'dokploy' },
223
+ shared: { packages: [] },
224
+ secrets: {},
225
+ };
226
+
227
+ const dependents = getDependentFrontends(workspace, 'api');
228
+ expect(dependents).toEqual(['web', 'admin']);
229
+ });
230
+
231
+ it('should return empty array for backend with no dependents', () => {
232
+ const workspace: NormalizedWorkspace = {
233
+ name: 'test',
234
+ root: '/test',
235
+ apps: {
236
+ api: {
237
+ type: 'backend',
238
+ path: 'apps/api',
239
+ port: 3000,
240
+ dependencies: [],
241
+ routes: './src/**/*.ts',
242
+ },
243
+ },
244
+ services: {},
245
+ deploy: { default: 'dokploy' },
246
+ shared: { packages: [] },
247
+ secrets: {},
248
+ };
249
+
250
+ const dependents = getDependentFrontends(workspace, 'api');
251
+ expect(dependents).toEqual([]);
252
+ });
253
+ });
254
+
255
+ describe('generateClientForFrontend', () => {
256
+ let testDir: string;
257
+
258
+ beforeEach(() => {
259
+ testDir = join(
260
+ tmpdir(),
261
+ `gkm-client-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
262
+ );
263
+ mkdirSync(testDir, { recursive: true });
264
+ clearSpecHashCache();
265
+ });
266
+
267
+ afterEach(() => {
268
+ if (existsSync(testDir)) {
269
+ rmSync(testDir, { recursive: true, force: true });
270
+ }
271
+ });
272
+
273
+ it('should return empty array for frontend with no backend dependencies', async () => {
274
+ const workspace: NormalizedWorkspace = {
275
+ name: 'test',
276
+ root: testDir,
277
+ apps: {
278
+ web: {
279
+ type: 'frontend',
280
+ path: 'apps/web',
281
+ port: 3001,
282
+ dependencies: [],
283
+ },
284
+ },
285
+ services: {},
286
+ deploy: { default: 'dokploy' },
287
+ shared: { packages: [] },
288
+ secrets: {},
289
+ };
290
+
291
+ const results = await generateClientForFrontend(workspace, 'web');
292
+ expect(results).toEqual([]);
293
+ });
294
+
295
+ it('should return empty array for non-frontend app', async () => {
296
+ const workspace: NormalizedWorkspace = {
297
+ name: 'test',
298
+ root: testDir,
299
+ apps: {
300
+ api: {
301
+ type: 'backend',
302
+ path: 'apps/api',
303
+ port: 3000,
304
+ dependencies: [],
305
+ routes: './src/**/*.ts',
306
+ },
307
+ },
308
+ services: {},
309
+ deploy: { default: 'dokploy' },
310
+ shared: { packages: [] },
311
+ secrets: {},
312
+ };
313
+
314
+ const results = await generateClientForFrontend(workspace, 'api');
315
+ expect(results).toEqual([]);
316
+ });
317
+
318
+ it('should use configured client output path', async () => {
319
+ // Create minimal workspace structure
320
+ const apiDir = join(testDir, 'apps/api');
321
+ const webDir = join(testDir, 'apps/web');
322
+ mkdirSync(join(apiDir, 'src/endpoints'), { recursive: true });
323
+ mkdirSync(webDir, { recursive: true });
324
+
325
+ const workspace: NormalizedWorkspace = {
326
+ name: 'test',
327
+ root: testDir,
328
+ apps: {
329
+ api: {
330
+ type: 'backend',
331
+ path: 'apps/api',
332
+ port: 3000,
333
+ dependencies: [],
334
+ routes: './src/endpoints/**/*.ts',
335
+ },
336
+ web: {
337
+ type: 'frontend',
338
+ path: 'apps/web',
339
+ port: 3001,
340
+ dependencies: ['api'],
341
+ client: { output: 'lib/api' },
342
+ },
343
+ },
344
+ services: {},
345
+ deploy: { default: 'dokploy' },
346
+ shared: { packages: [] },
347
+ secrets: {},
348
+ };
349
+
350
+ // This will fail to generate because there are no endpoints,
351
+ // but we can verify the output path would be correct
352
+ const results = await generateClientForFrontend(workspace, 'web');
353
+ expect(results).toHaveLength(1);
354
+ expect(results[0]?.reason).toBe('No endpoints found in backend');
355
+ });
356
+ });
357
+ });