@geekmidas/cli 0.18.0 → 0.20.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 +2640 -564
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.mjs +2635 -564
  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 +9 -4
  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
@@ -1,8 +1,14 @@
1
1
  import { HttpResponse, http } from 'msw';
2
2
  import { setupServer } from 'msw/node';
3
3
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
+ import type { NormalizedWorkspace } from '../../workspace/types.js';
4
5
  import { DokployApi } from '../dokploy-api';
5
- import { generateTag, provisionServices } from '../index';
6
+ import {
7
+ generateTag,
8
+ provisionServices,
9
+ workspaceDeployCommand,
10
+ } from '../index';
11
+ import type { DeployOptions } from '../types';
6
12
 
7
13
  const BASE_URL = 'https://dokploy.example.com';
8
14
 
@@ -399,3 +405,301 @@ describe('generateTag edge cases', () => {
399
405
  }
400
406
  });
401
407
  });
408
+
409
+ describe('workspaceDeployCommand', () => {
410
+ /** Create a minimal workspace config for testing */
411
+ function createWorkspace(
412
+ overrides: Partial<NormalizedWorkspace> = {},
413
+ ): NormalizedWorkspace {
414
+ return {
415
+ name: 'test-workspace',
416
+ root: '/workspace',
417
+ apps: {
418
+ api: {
419
+ type: 'backend',
420
+ path: 'apps/api',
421
+ port: 3000,
422
+ dependencies: [],
423
+ },
424
+ web: {
425
+ type: 'frontend',
426
+ path: 'apps/web',
427
+ port: 3001,
428
+ dependencies: ['api'],
429
+ framework: 'nextjs',
430
+ },
431
+ },
432
+ services: {},
433
+ deploy: { default: 'dokploy' },
434
+ shared: { packages: [] },
435
+ secrets: {},
436
+ ...overrides,
437
+ };
438
+ }
439
+
440
+ describe('provider validation', () => {
441
+ it('should reject non-dokploy providers', async () => {
442
+ const workspace = createWorkspace();
443
+ const options: DeployOptions = {
444
+ provider: 'docker',
445
+ stage: 'production',
446
+ };
447
+
448
+ await expect(workspaceDeployCommand(workspace, options)).rejects.toThrow(
449
+ 'Workspace deployment only supports Dokploy',
450
+ );
451
+ });
452
+
453
+ it('should reject aws-lambda provider', async () => {
454
+ const workspace = createWorkspace();
455
+ const options: DeployOptions = {
456
+ provider: 'aws-lambda',
457
+ stage: 'production',
458
+ };
459
+
460
+ await expect(workspaceDeployCommand(workspace, options)).rejects.toThrow(
461
+ 'Workspace deployment only supports Dokploy',
462
+ );
463
+ });
464
+ });
465
+
466
+ describe('selective app deployment', () => {
467
+ it('should validate selected apps exist', async () => {
468
+ const workspace = createWorkspace();
469
+ const options: DeployOptions = {
470
+ provider: 'dokploy',
471
+ stage: 'production',
472
+ apps: ['api', 'nonexistent'],
473
+ };
474
+
475
+ // Mock credentials check to fail fast
476
+ vi.mock('../../auth', () => ({
477
+ getDokployCredentials: vi.fn().mockResolvedValue(null),
478
+ getDokployRegistryId: vi.fn().mockResolvedValue(null),
479
+ storeDokployCredentials: vi.fn(),
480
+ validateDokployToken: vi.fn().mockResolvedValue(true),
481
+ }));
482
+
483
+ await expect(workspaceDeployCommand(workspace, options)).rejects.toThrow(
484
+ 'Unknown apps: nonexistent',
485
+ );
486
+ });
487
+
488
+ it('should filter selected apps while maintaining dependency order', () => {
489
+ // Test the filtering logic directly
490
+ const buildOrder = ['api', 'auth', 'web', 'admin'];
491
+ const selectedApps = ['web', 'api'];
492
+
493
+ const filtered = buildOrder.filter((name) => selectedApps.includes(name));
494
+
495
+ // Should be in dependency order (api before web)
496
+ expect(filtered).toEqual(['api', 'web']);
497
+ });
498
+ });
499
+
500
+ describe('dependency ordering', () => {
501
+ it('should deploy backends before dependent frontends', () => {
502
+ // The getAppBuildOrder function returns topologically sorted order
503
+ // This is a unit test for the ordering logic
504
+ const workspace = createWorkspace({
505
+ apps: {
506
+ api: {
507
+ type: 'backend',
508
+ path: 'apps/api',
509
+ port: 3000,
510
+ dependencies: [],
511
+ },
512
+ auth: {
513
+ type: 'backend',
514
+ path: 'apps/auth',
515
+ port: 3001,
516
+ dependencies: [],
517
+ },
518
+ web: {
519
+ type: 'frontend',
520
+ path: 'apps/web',
521
+ port: 3002,
522
+ dependencies: ['api', 'auth'],
523
+ framework: 'nextjs',
524
+ },
525
+ },
526
+ });
527
+
528
+ // Import getAppBuildOrder to verify ordering
529
+ const { getAppBuildOrder } = require('../../workspace/index.js');
530
+ const order = getAppBuildOrder(workspace);
531
+
532
+ // api and auth should come before web
533
+ const apiIndex = order.indexOf('api');
534
+ const authIndex = order.indexOf('auth');
535
+ const webIndex = order.indexOf('web');
536
+
537
+ expect(apiIndex).toBeLessThan(webIndex);
538
+ expect(authIndex).toBeLessThan(webIndex);
539
+ });
540
+
541
+ it('should handle chain dependencies correctly', () => {
542
+ const workspace = createWorkspace({
543
+ apps: {
544
+ db: {
545
+ type: 'backend',
546
+ path: 'apps/db',
547
+ port: 3000,
548
+ dependencies: [],
549
+ },
550
+ api: {
551
+ type: 'backend',
552
+ path: 'apps/api',
553
+ port: 3001,
554
+ dependencies: ['db'],
555
+ },
556
+ web: {
557
+ type: 'frontend',
558
+ path: 'apps/web',
559
+ port: 3002,
560
+ dependencies: ['api'],
561
+ framework: 'nextjs',
562
+ },
563
+ },
564
+ });
565
+
566
+ const { getAppBuildOrder } = require('../../workspace/index.js');
567
+ const order = getAppBuildOrder(workspace);
568
+
569
+ // db -> api -> web
570
+ expect(order.indexOf('db')).toBeLessThan(order.indexOf('api'));
571
+ expect(order.indexOf('api')).toBeLessThan(order.indexOf('web'));
572
+ });
573
+ });
574
+
575
+ describe('environment variable injection', () => {
576
+ it('should inject APP_URL for dependencies', () => {
577
+ // Test the environment variable construction logic
578
+ const deployedAppUrls: Record<string, string> = {
579
+ api: 'http://test-workspace-api:3000',
580
+ auth: 'http://test-workspace-auth:3001',
581
+ };
582
+
583
+ const appDependencies = ['api', 'auth'];
584
+ const envVars: string[] = [];
585
+
586
+ for (const dep of appDependencies) {
587
+ const depUrl = deployedAppUrls[dep];
588
+ if (depUrl) {
589
+ envVars.push(`${dep.toUpperCase()}_URL=${depUrl}`);
590
+ }
591
+ }
592
+
593
+ expect(envVars).toContain('API_URL=http://test-workspace-api:3000');
594
+ expect(envVars).toContain('AUTH_URL=http://test-workspace-auth:3001');
595
+ });
596
+
597
+ it('should inject DATABASE_URL for backend apps', () => {
598
+ const workspaceName = 'test-workspace';
599
+ const hasPostgres = true;
600
+ const appType: 'backend' | 'frontend' = 'backend';
601
+
602
+ const envVars: string[] = [];
603
+
604
+ if (appType === 'backend' && hasPostgres) {
605
+ envVars.push(
606
+ `DATABASE_URL=\${DATABASE_URL:-postgresql://postgres:postgres@${workspaceName}-db:5432/app}`,
607
+ );
608
+ }
609
+
610
+ expect(envVars).toHaveLength(1);
611
+ expect(envVars[0]).toContain('test-workspace-db');
612
+ });
613
+
614
+ it('should not inject DATABASE_URL for frontend apps', () => {
615
+ const hasPostgres = true;
616
+ const appType: 'backend' | 'frontend' = 'frontend';
617
+
618
+ const envVars: string[] = [];
619
+
620
+ if (appType === 'backend' && hasPostgres) {
621
+ envVars.push(`DATABASE_URL=...`);
622
+ }
623
+
624
+ expect(envVars).toHaveLength(0);
625
+ });
626
+ });
627
+
628
+ describe('image naming', () => {
629
+ it('should construct image name from workspace and app name', () => {
630
+ const workspaceName = 'my-workspace';
631
+ const appName = 'api';
632
+ const imageTag = 'production-2024-01-01T12-00-00';
633
+ const registry = 'ghcr.io/myorg';
634
+
635
+ const imageName = `${workspaceName}-${appName}`;
636
+ const imageRef = registry
637
+ ? `${registry}/${imageName}:${imageTag}`
638
+ : `${imageName}:${imageTag}`;
639
+
640
+ expect(imageName).toBe('my-workspace-api');
641
+ expect(imageRef).toBe(
642
+ 'ghcr.io/myorg/my-workspace-api:production-2024-01-01T12-00-00',
643
+ );
644
+ });
645
+
646
+ it('should handle workspace name with special characters', () => {
647
+ const workspaceName = 'my-cool-workspace';
648
+ const appName = 'web-frontend';
649
+
650
+ const imageName = `${workspaceName}-${appName}`;
651
+
652
+ expect(imageName).toBe('my-cool-workspace-web-frontend');
653
+ });
654
+ });
655
+
656
+ describe('result types', () => {
657
+ it('should have correct structure for AppDeployResult', () => {
658
+ const successResult = {
659
+ appName: 'api',
660
+ type: 'backend' as const,
661
+ success: true,
662
+ applicationId: 'app_123',
663
+ imageRef: 'ghcr.io/org/api:v1',
664
+ };
665
+
666
+ expect(successResult.appName).toBe('api');
667
+ expect(successResult.type).toBe('backend');
668
+ expect(successResult.success).toBe(true);
669
+ expect(successResult.applicationId).toBe('app_123');
670
+ });
671
+
672
+ it('should have correct structure for failed AppDeployResult', () => {
673
+ const failedResult = {
674
+ appName: 'web',
675
+ type: 'frontend' as const,
676
+ success: false,
677
+ error: 'Build failed',
678
+ };
679
+
680
+ expect(failedResult.success).toBe(false);
681
+ expect(failedResult.error).toBe('Build failed');
682
+ });
683
+
684
+ it('should have correct structure for WorkspaceDeployResult', () => {
685
+ const result = {
686
+ apps: [
687
+ { appName: 'api', type: 'backend' as const, success: true },
688
+ {
689
+ appName: 'web',
690
+ type: 'frontend' as const,
691
+ success: false,
692
+ error: 'Failed',
693
+ },
694
+ ],
695
+ projectId: 'proj_123',
696
+ successCount: 1,
697
+ failedCount: 1,
698
+ };
699
+
700
+ expect(result.apps).toHaveLength(2);
701
+ expect(result.successCount).toBe(1);
702
+ expect(result.failedCount).toBe(1);
703
+ });
704
+ });
705
+ });