@geekmidas/cli 0.53.0 → 1.0.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 (156) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +26 -5
  3. package/dist/CachedStateProvider-D73dCqfH.cjs +60 -0
  4. package/dist/CachedStateProvider-D73dCqfH.cjs.map +1 -0
  5. package/dist/CachedStateProvider-DVyKfaMm.mjs +54 -0
  6. package/dist/CachedStateProvider-DVyKfaMm.mjs.map +1 -0
  7. package/dist/CachedStateProvider-D_uISMmJ.cjs +3 -0
  8. package/dist/CachedStateProvider-OiFUGr7p.mjs +3 -0
  9. package/dist/HostingerProvider-DUV9-Tzg.cjs +210 -0
  10. package/dist/HostingerProvider-DUV9-Tzg.cjs.map +1 -0
  11. package/dist/HostingerProvider-DqUq6e9i.mjs +210 -0
  12. package/dist/HostingerProvider-DqUq6e9i.mjs.map +1 -0
  13. package/dist/LocalStateProvider-CdspeSVL.cjs +43 -0
  14. package/dist/LocalStateProvider-CdspeSVL.cjs.map +1 -0
  15. package/dist/LocalStateProvider-DxoSaWUV.mjs +42 -0
  16. package/dist/LocalStateProvider-DxoSaWUV.mjs.map +1 -0
  17. package/dist/Route53Provider-CpRIqu69.cjs +157 -0
  18. package/dist/Route53Provider-CpRIqu69.cjs.map +1 -0
  19. package/dist/Route53Provider-KUAX3vz9.mjs +156 -0
  20. package/dist/Route53Provider-KUAX3vz9.mjs.map +1 -0
  21. package/dist/SSMStateProvider-BxAPU99a.cjs +53 -0
  22. package/dist/SSMStateProvider-BxAPU99a.cjs.map +1 -0
  23. package/dist/SSMStateProvider-C4wp4AZe.mjs +52 -0
  24. package/dist/SSMStateProvider-C4wp4AZe.mjs.map +1 -0
  25. package/dist/{bundler-DGry2vaR.mjs → bundler-BqTN5Dj5.mjs} +3 -3
  26. package/dist/{bundler-DGry2vaR.mjs.map → bundler-BqTN5Dj5.mjs.map} +1 -1
  27. package/dist/{bundler-BB-kETMd.cjs → bundler-tHLLwYuU.cjs} +3 -3
  28. package/dist/{bundler-BB-kETMd.cjs.map → bundler-tHLLwYuU.cjs.map} +1 -1
  29. package/dist/{config-HYiM3iQJ.cjs → config-BGeJsW1r.cjs} +2 -2
  30. package/dist/{config-HYiM3iQJ.cjs.map → config-BGeJsW1r.cjs.map} +1 -1
  31. package/dist/{config-C3LSBNSl.mjs → config-C6awcFBx.mjs} +2 -2
  32. package/dist/{config-C3LSBNSl.mjs.map → config-C6awcFBx.mjs.map} +1 -1
  33. package/dist/config.cjs +2 -2
  34. package/dist/config.d.cts +1 -1
  35. package/dist/config.d.mts +2 -2
  36. package/dist/config.mjs +2 -2
  37. package/dist/credentials-C8DWtnMY.cjs +174 -0
  38. package/dist/credentials-C8DWtnMY.cjs.map +1 -0
  39. package/dist/credentials-DT1dSxIx.mjs +126 -0
  40. package/dist/credentials-DT1dSxIx.mjs.map +1 -0
  41. package/dist/deploy/sniffer-envkit-patch.cjs.map +1 -1
  42. package/dist/deploy/sniffer-envkit-patch.mjs.map +1 -1
  43. package/dist/deploy/sniffer-loader.cjs +1 -1
  44. package/dist/{dokploy-api-94KzmTVf.mjs → dokploy-api-7k3t7_zd.mjs} +1 -1
  45. package/dist/{dokploy-api-94KzmTVf.mjs.map → dokploy-api-7k3t7_zd.mjs.map} +1 -1
  46. package/dist/dokploy-api-CHa8G51l.mjs +3 -0
  47. package/dist/{dokploy-api-YD8WCQfW.cjs → dokploy-api-CQvhV6Hd.cjs} +1 -1
  48. package/dist/{dokploy-api-YD8WCQfW.cjs.map → dokploy-api-CQvhV6Hd.cjs.map} +1 -1
  49. package/dist/dokploy-api-CWc02yyg.cjs +3 -0
  50. package/dist/{encryption-DaCB_NmS.cjs → encryption-BE0UOb8j.cjs} +1 -1
  51. package/dist/{encryption-DaCB_NmS.cjs.map → encryption-BE0UOb8j.cjs.map} +1 -1
  52. package/dist/{encryption-Biq0EZ4m.cjs → encryption-Cv3zips0.cjs} +1 -1
  53. package/dist/{encryption-BC4MAODn.mjs → encryption-JtMsiGNp.mjs} +1 -1
  54. package/dist/{encryption-BC4MAODn.mjs.map → encryption-JtMsiGNp.mjs.map} +1 -1
  55. package/dist/encryption-UUmaWAmz.mjs +3 -0
  56. package/dist/{index-pOA56MWT.d.cts → index-B5rGIc4g.d.cts} +553 -196
  57. package/dist/index-B5rGIc4g.d.cts.map +1 -0
  58. package/dist/{index-A70abJ1m.d.mts → index-KFEbMIRa.d.mts} +554 -197
  59. package/dist/index-KFEbMIRa.d.mts.map +1 -0
  60. package/dist/index.cjs +2242 -568
  61. package/dist/index.cjs.map +1 -1
  62. package/dist/index.mjs +2219 -545
  63. package/dist/index.mjs.map +1 -1
  64. package/dist/{openapi-C3C-BzIZ.mjs → openapi-BMFmLnX6.mjs} +51 -7
  65. package/dist/openapi-BMFmLnX6.mjs.map +1 -0
  66. package/dist/{openapi-D7WwlpPF.cjs → openapi-D1KXv2Ml.cjs} +51 -7
  67. package/dist/openapi-D1KXv2Ml.cjs.map +1 -0
  68. package/dist/{openapi-react-query-C_MxpBgF.cjs → openapi-react-query-BeXvk-wa.cjs} +1 -1
  69. package/dist/{openapi-react-query-C_MxpBgF.cjs.map → openapi-react-query-BeXvk-wa.cjs.map} +1 -1
  70. package/dist/{openapi-react-query-ZoP9DPbY.mjs → openapi-react-query-DGEkD39r.mjs} +1 -1
  71. package/dist/{openapi-react-query-ZoP9DPbY.mjs.map → openapi-react-query-DGEkD39r.mjs.map} +1 -1
  72. package/dist/openapi-react-query.cjs +1 -1
  73. package/dist/openapi-react-query.mjs +1 -1
  74. package/dist/openapi.cjs +3 -3
  75. package/dist/openapi.d.cts +1 -1
  76. package/dist/openapi.d.mts +2 -2
  77. package/dist/openapi.mjs +3 -3
  78. package/dist/{storage-Dhst7BhI.mjs → storage-BMW6yLu3.mjs} +1 -1
  79. package/dist/{storage-Dhst7BhI.mjs.map → storage-BMW6yLu3.mjs.map} +1 -1
  80. package/dist/{storage-fOR8dMu5.cjs → storage-C7pmBq1u.cjs} +1 -1
  81. package/dist/{storage-BPRgh3DU.cjs → storage-CoCNe0Pt.cjs} +1 -1
  82. package/dist/{storage-BPRgh3DU.cjs.map → storage-CoCNe0Pt.cjs.map} +1 -1
  83. package/dist/{storage-DNj_I11J.mjs → storage-D8XzjVaO.mjs} +1 -1
  84. package/dist/{types-BtGL-8QS.d.mts → types-BldpmqQX.d.mts} +1 -1
  85. package/dist/{types-BtGL-8QS.d.mts.map → types-BldpmqQX.d.mts.map} +1 -1
  86. package/dist/workspace/index.cjs +1 -1
  87. package/dist/workspace/index.d.cts +1 -1
  88. package/dist/workspace/index.d.mts +2 -2
  89. package/dist/workspace/index.mjs +1 -1
  90. package/dist/{workspace-CaVW6j2q.cjs → workspace-BFRUOOrh.cjs} +309 -25
  91. package/dist/workspace-BFRUOOrh.cjs.map +1 -0
  92. package/dist/{workspace-DLFRaDc-.mjs → workspace-DAxG3_H2.mjs} +309 -25
  93. package/dist/workspace-DAxG3_H2.mjs.map +1 -0
  94. package/package.json +12 -8
  95. package/src/build/__tests__/handler-templates.spec.ts +115 -47
  96. package/src/deploy/CachedStateProvider.ts +86 -0
  97. package/src/deploy/LocalStateProvider.ts +57 -0
  98. package/src/deploy/SSMStateProvider.ts +93 -0
  99. package/src/deploy/StateProvider.ts +171 -0
  100. package/src/deploy/__tests__/CachedStateProvider.spec.ts +228 -0
  101. package/src/deploy/__tests__/HostingerProvider.spec.ts +347 -0
  102. package/src/deploy/__tests__/LocalStateProvider.spec.ts +126 -0
  103. package/src/deploy/__tests__/Route53Provider.spec.ts +402 -0
  104. package/src/deploy/__tests__/SSMStateProvider.spec.ts +177 -0
  105. package/src/deploy/__tests__/__fixtures__/env-parsers/throwing-env-parser.ts +1 -3
  106. package/src/deploy/__tests__/__fixtures__/route-apps/endpoints/auth.ts +16 -0
  107. package/src/deploy/__tests__/__fixtures__/route-apps/endpoints/health.ts +13 -0
  108. package/src/deploy/__tests__/__fixtures__/route-apps/endpoints/users.ts +15 -0
  109. package/src/deploy/__tests__/__fixtures__/route-apps/services.ts +55 -0
  110. package/src/deploy/__tests__/createDnsProvider.spec.ts +172 -0
  111. package/src/deploy/__tests__/createStateProvider.spec.ts +116 -0
  112. package/src/deploy/__tests__/dns-orchestration.spec.ts +192 -0
  113. package/src/deploy/__tests__/dns-verification.spec.ts +2 -2
  114. package/src/deploy/__tests__/env-resolver.spec.ts +41 -17
  115. package/src/deploy/__tests__/sniffer.spec.ts +168 -10
  116. package/src/deploy/__tests__/state.spec.ts +13 -5
  117. package/src/deploy/dns/DnsProvider.ts +163 -0
  118. package/src/deploy/dns/HostingerProvider.ts +100 -0
  119. package/src/deploy/dns/Route53Provider.ts +256 -0
  120. package/src/deploy/dns/index.ts +257 -165
  121. package/src/deploy/env-resolver.ts +12 -5
  122. package/src/deploy/index.ts +16 -13
  123. package/src/deploy/sniffer-envkit-patch.ts +3 -1
  124. package/src/deploy/sniffer-routes-worker.ts +104 -0
  125. package/src/deploy/sniffer.ts +130 -5
  126. package/src/deploy/state-commands.ts +274 -0
  127. package/src/dev/__tests__/entry.spec.ts +8 -2
  128. package/src/dev/__tests__/index.spec.ts +1 -3
  129. package/src/dev/index.ts +9 -3
  130. package/src/docker/__tests__/templates.spec.ts +3 -1
  131. package/src/docker/templates.ts +3 -3
  132. package/src/index.ts +88 -0
  133. package/src/init/__tests__/generators.spec.ts +273 -0
  134. package/src/init/__tests__/init.spec.ts +3 -3
  135. package/src/init/generators/auth.ts +1 -0
  136. package/src/init/generators/config.ts +2 -0
  137. package/src/init/generators/models.ts +6 -1
  138. package/src/init/generators/monorepo.ts +3 -0
  139. package/src/init/generators/ui.ts +1472 -0
  140. package/src/init/generators/web.ts +134 -87
  141. package/src/init/index.ts +22 -3
  142. package/src/init/templates/api.ts +109 -3
  143. package/src/openapi.ts +99 -13
  144. package/src/workspace/__tests__/schema.spec.ts +107 -0
  145. package/src/workspace/schema.ts +314 -4
  146. package/src/workspace/types.ts +22 -36
  147. package/dist/dokploy-api-CItuaWTq.mjs +0 -3
  148. package/dist/dokploy-api-DBNE8MDt.cjs +0 -3
  149. package/dist/encryption-CQXBZGkt.mjs +0 -3
  150. package/dist/index-A70abJ1m.d.mts.map +0 -1
  151. package/dist/index-pOA56MWT.d.cts.map +0 -1
  152. package/dist/openapi-C3C-BzIZ.mjs.map +0 -1
  153. package/dist/openapi-D7WwlpPF.cjs.map +0 -1
  154. package/dist/workspace-CaVW6j2q.cjs.map +0 -1
  155. package/dist/workspace-DLFRaDc-.mjs.map +0 -1
  156. package/tsconfig.tsbuildinfo +0 -1
package/src/index.ts CHANGED
@@ -6,6 +6,12 @@ import { loginCommand, logoutCommand, whoamiCommand } from './auth';
6
6
  import { buildCommand } from './build/index';
7
7
  import { type DeployProvider, deployCommand } from './deploy/index';
8
8
  import { deployInitCommand, deployListCommand } from './deploy/init';
9
+ import {
10
+ stateDiffCommand,
11
+ statePullCommand,
12
+ statePushCommand,
13
+ stateShowCommand,
14
+ } from './deploy/state-commands';
9
15
  import { devCommand, execCommand } from './dev/index';
10
16
  import { type DockerOptions, dockerCommand } from './docker/index';
11
17
  import { type InitOptions, initCommand } from './init/index';
@@ -710,4 +716,86 @@ program
710
716
  }
711
717
  });
712
718
 
719
+ // State management commands
720
+ program
721
+ .command('state:pull')
722
+ .description('Pull deployment state from remote to local')
723
+ .requiredOption(
724
+ '--stage <stage>',
725
+ 'Deployment stage (e.g., production, staging)',
726
+ )
727
+ .action(async (options: { stage: string }) => {
728
+ try {
729
+ const globalOptions = program.opts();
730
+ if (globalOptions.cwd) {
731
+ process.chdir(globalOptions.cwd);
732
+ }
733
+ await statePullCommand(options);
734
+ } catch (error) {
735
+ console.error(error instanceof Error ? error.message : 'Command failed');
736
+ process.exit(1);
737
+ }
738
+ });
739
+
740
+ program
741
+ .command('state:push')
742
+ .description('Push deployment state from local to remote')
743
+ .requiredOption(
744
+ '--stage <stage>',
745
+ 'Deployment stage (e.g., production, staging)',
746
+ )
747
+ .action(async (options: { stage: string }) => {
748
+ try {
749
+ const globalOptions = program.opts();
750
+ if (globalOptions.cwd) {
751
+ process.chdir(globalOptions.cwd);
752
+ }
753
+ await statePushCommand(options);
754
+ } catch (error) {
755
+ console.error(error instanceof Error ? error.message : 'Command failed');
756
+ process.exit(1);
757
+ }
758
+ });
759
+
760
+ program
761
+ .command('state:show')
762
+ .description('Show deployment state for a stage')
763
+ .requiredOption(
764
+ '--stage <stage>',
765
+ 'Deployment stage (e.g., production, staging)',
766
+ )
767
+ .option('--json', 'Output as JSON')
768
+ .action(async (options: { stage: string; json?: boolean }) => {
769
+ try {
770
+ const globalOptions = program.opts();
771
+ if (globalOptions.cwd) {
772
+ process.chdir(globalOptions.cwd);
773
+ }
774
+ await stateShowCommand(options);
775
+ } catch (error) {
776
+ console.error(error instanceof Error ? error.message : 'Command failed');
777
+ process.exit(1);
778
+ }
779
+ });
780
+
781
+ program
782
+ .command('state:diff')
783
+ .description('Compare local and remote deployment state')
784
+ .requiredOption(
785
+ '--stage <stage>',
786
+ 'Deployment stage (e.g., production, staging)',
787
+ )
788
+ .action(async (options: { stage: string }) => {
789
+ try {
790
+ const globalOptions = program.opts();
791
+ if (globalOptions.cwd) {
792
+ process.chdir(globalOptions.cwd);
793
+ }
794
+ await stateDiffCommand(options);
795
+ } catch (error) {
796
+ console.error(error instanceof Error ? error.message : 'Command failed');
797
+ process.exit(1);
798
+ }
799
+ });
800
+
713
801
  program.parse();
@@ -5,6 +5,7 @@ import { generateEnvFiles } from '../generators/env.js';
5
5
  import { generateModelsPackage } from '../generators/models.js';
6
6
  import { generateMonorepoFiles } from '../generators/monorepo.js';
7
7
  import { generatePackageJson } from '../generators/package.js';
8
+ import { generateUiPackageFiles } from '../generators/ui.js';
8
9
  import type { TemplateOptions } from '../templates/index.js';
9
10
  import { minimalTemplate } from '../templates/minimal.js';
10
11
  import { serverlessTemplate } from '../templates/serverless.js';
@@ -372,3 +373,275 @@ describe('generateModelsPackage', () => {
372
373
  expect(config.extends).toBe('../../tsconfig.json');
373
374
  });
374
375
  });
376
+
377
+ describe('generateUiPackageFiles', () => {
378
+ const fullstackOptions: TemplateOptions = {
379
+ ...baseOptions,
380
+ template: 'fullstack',
381
+ monorepo: true,
382
+ apiPath: 'apps/api',
383
+ };
384
+
385
+ it('should return empty array for non-monorepo', () => {
386
+ const files = generateUiPackageFiles(baseOptions);
387
+ expect(files).toHaveLength(0);
388
+ });
389
+
390
+ it('should return empty array for non-fullstack template', () => {
391
+ const options: TemplateOptions = {
392
+ ...baseOptions,
393
+ template: 'minimal',
394
+ monorepo: true,
395
+ apiPath: 'apps/api',
396
+ };
397
+ const files = generateUiPackageFiles(options);
398
+ expect(files).toHaveLength(0);
399
+ });
400
+
401
+ it('should generate UI package files for fullstack template', () => {
402
+ const files = generateUiPackageFiles(fullstackOptions);
403
+ const paths = files.map((f) => f.path);
404
+ expect(paths).toContain('packages/ui/package.json');
405
+ expect(paths).toContain('packages/ui/tsconfig.json');
406
+ expect(paths).toContain('packages/ui/components.json');
407
+ expect(paths).toContain('packages/ui/.storybook/main.ts');
408
+ expect(paths).toContain('packages/ui/.storybook/preview.ts');
409
+ expect(paths).toContain('packages/ui/src/styles/globals.css');
410
+ expect(paths).toContain('packages/ui/src/lib/utils.ts');
411
+ expect(paths).toContain('packages/ui/src/index.ts');
412
+ });
413
+
414
+ it('should use correct package name', () => {
415
+ const files = generateUiPackageFiles(fullstackOptions);
416
+ const pkgJson = files.find((f) => f.path === 'packages/ui/package.json');
417
+ expect(pkgJson).toBeDefined();
418
+ const pkg = JSON.parse(pkgJson!.content);
419
+ expect(pkg.name).toBe('@test-project/ui');
420
+ });
421
+
422
+ it('should have typescript source exports (not dist)', () => {
423
+ const files = generateUiPackageFiles(fullstackOptions);
424
+ const pkgJson = files.find((f) => f.path === 'packages/ui/package.json');
425
+ expect(pkgJson).toBeDefined();
426
+ const pkg = JSON.parse(pkgJson!.content);
427
+ expect(pkg.exports['.']).toBe('./src/index.ts');
428
+ expect(pkg.exports['./components']).toBe('./src/components/index.ts');
429
+ expect(pkg.exports['./lib/utils']).toBe('./src/lib/utils.ts');
430
+ expect(pkg.exports['./styles']).toBe('./src/styles/globals.css');
431
+ });
432
+
433
+ it('should not have build script (private packages are not built)', () => {
434
+ const files = generateUiPackageFiles(fullstackOptions);
435
+ const pkgJson = files.find((f) => f.path === 'packages/ui/package.json');
436
+ expect(pkgJson).toBeDefined();
437
+ const pkg = JSON.parse(pkgJson!.content);
438
+ expect(pkg.scripts.build).toBeUndefined();
439
+ expect(pkg.scripts.storybook).toBeDefined();
440
+ expect(pkg.scripts['build:storybook']).toBeDefined();
441
+ });
442
+
443
+ it('should include Radix UI dependencies', () => {
444
+ const files = generateUiPackageFiles(fullstackOptions);
445
+ const pkgJson = files.find((f) => f.path === 'packages/ui/package.json');
446
+ expect(pkgJson).toBeDefined();
447
+ const pkg = JSON.parse(pkgJson!.content);
448
+ expect(pkg.dependencies['@radix-ui/react-slot']).toBeDefined();
449
+ expect(pkg.dependencies['@radix-ui/react-dialog']).toBeDefined();
450
+ expect(pkg.dependencies['@radix-ui/react-label']).toBeDefined();
451
+ expect(pkg.dependencies['@radix-ui/react-separator']).toBeDefined();
452
+ expect(pkg.dependencies['@radix-ui/react-tabs']).toBeDefined();
453
+ expect(pkg.dependencies['@radix-ui/react-tooltip']).toBeDefined();
454
+ });
455
+
456
+ it('should include Storybook dependencies', () => {
457
+ const files = generateUiPackageFiles(fullstackOptions);
458
+ const pkgJson = files.find((f) => f.path === 'packages/ui/package.json');
459
+ expect(pkgJson).toBeDefined();
460
+ const pkg = JSON.parse(pkgJson!.content);
461
+ expect(pkg.devDependencies['@storybook/react']).toBeDefined();
462
+ expect(pkg.devDependencies['@storybook/react-vite']).toBeDefined();
463
+ expect(pkg.devDependencies['@storybook/addon-essentials']).toBeDefined();
464
+ expect(pkg.devDependencies.storybook).toBeDefined();
465
+ });
466
+
467
+ it('should include Tailwind CSS dependencies', () => {
468
+ const files = generateUiPackageFiles(fullstackOptions);
469
+ const pkgJson = files.find((f) => f.path === 'packages/ui/package.json');
470
+ expect(pkgJson).toBeDefined();
471
+ const pkg = JSON.parse(pkgJson!.content);
472
+ expect(pkg.devDependencies.tailwindcss).toBeDefined();
473
+ expect(pkg.devDependencies['@tailwindcss/vite']).toBeDefined();
474
+ expect(pkg.peerDependencies.tailwindcss).toBeDefined();
475
+ });
476
+
477
+ it('should extend root tsconfig with noEmit', () => {
478
+ const files = generateUiPackageFiles(fullstackOptions);
479
+ const tsConfig = files.find((f) => f.path === 'packages/ui/tsconfig.json');
480
+ expect(tsConfig).toBeDefined();
481
+ const config = JSON.parse(tsConfig!.content);
482
+ expect(config.extends).toBe('../../tsconfig.json');
483
+ expect(config.compilerOptions.noEmit).toBe(true);
484
+ expect(config.compilerOptions.jsx).toBe('react-jsx');
485
+ });
486
+
487
+ it('should not include tsdown.config.ts', () => {
488
+ const files = generateUiPackageFiles(fullstackOptions);
489
+ const paths = files.map((f) => f.path);
490
+ expect(paths).not.toContain('packages/ui/tsdown.config.ts');
491
+ });
492
+
493
+ it('should generate all component files in folder structure', () => {
494
+ const files = generateUiPackageFiles(fullstackOptions);
495
+ const paths = files.map((f) => f.path);
496
+
497
+ // Button
498
+ expect(paths).toContain('packages/ui/src/components/ui/button/index.tsx');
499
+ expect(paths).toContain(
500
+ 'packages/ui/src/components/ui/button/button.stories.tsx',
501
+ );
502
+
503
+ // Input
504
+ expect(paths).toContain('packages/ui/src/components/ui/input/index.tsx');
505
+ expect(paths).toContain(
506
+ 'packages/ui/src/components/ui/input/input.stories.tsx',
507
+ );
508
+
509
+ // Card
510
+ expect(paths).toContain('packages/ui/src/components/ui/card/index.tsx');
511
+ expect(paths).toContain(
512
+ 'packages/ui/src/components/ui/card/card.stories.tsx',
513
+ );
514
+
515
+ // Label
516
+ expect(paths).toContain('packages/ui/src/components/ui/label/index.tsx');
517
+ expect(paths).toContain(
518
+ 'packages/ui/src/components/ui/label/label.stories.tsx',
519
+ );
520
+
521
+ // Badge
522
+ expect(paths).toContain('packages/ui/src/components/ui/badge/index.tsx');
523
+ expect(paths).toContain(
524
+ 'packages/ui/src/components/ui/badge/badge.stories.tsx',
525
+ );
526
+
527
+ // Separator
528
+ expect(paths).toContain(
529
+ 'packages/ui/src/components/ui/separator/index.tsx',
530
+ );
531
+ expect(paths).toContain(
532
+ 'packages/ui/src/components/ui/separator/separator.stories.tsx',
533
+ );
534
+
535
+ // Tabs
536
+ expect(paths).toContain('packages/ui/src/components/ui/tabs/index.tsx');
537
+ expect(paths).toContain(
538
+ 'packages/ui/src/components/ui/tabs/tabs.stories.tsx',
539
+ );
540
+
541
+ // Tooltip
542
+ expect(paths).toContain('packages/ui/src/components/ui/tooltip/index.tsx');
543
+ expect(paths).toContain(
544
+ 'packages/ui/src/components/ui/tooltip/tooltip.stories.tsx',
545
+ );
546
+
547
+ // Dialog
548
+ expect(paths).toContain('packages/ui/src/components/ui/dialog/index.tsx');
549
+ expect(paths).toContain(
550
+ 'packages/ui/src/components/ui/dialog/dialog.stories.tsx',
551
+ );
552
+ });
553
+
554
+ it('should export all components from index', () => {
555
+ const files = generateUiPackageFiles(fullstackOptions);
556
+ const indexFile = files.find(
557
+ (f) => f.path === 'packages/ui/src/components/ui/index.ts',
558
+ );
559
+ expect(indexFile).toBeDefined();
560
+ expect(indexFile!.content).toContain("from './button'");
561
+ expect(indexFile!.content).toContain("from './input'");
562
+ expect(indexFile!.content).toContain("from './card'");
563
+ expect(indexFile!.content).toContain("from './label'");
564
+ expect(indexFile!.content).toContain("from './badge'");
565
+ expect(indexFile!.content).toContain("from './separator'");
566
+ expect(indexFile!.content).toContain("from './tabs'");
567
+ expect(indexFile!.content).toContain("from './tooltip'");
568
+ expect(indexFile!.content).toContain("from './dialog'");
569
+ });
570
+
571
+ it('should include cn utility function', () => {
572
+ const files = generateUiPackageFiles(fullstackOptions);
573
+ const utilsFile = files.find(
574
+ (f) => f.path === 'packages/ui/src/lib/utils.ts',
575
+ );
576
+ expect(utilsFile).toBeDefined();
577
+ expect(utilsFile!.content).toContain('export function cn');
578
+ expect(utilsFile!.content).toContain('clsx');
579
+ expect(utilsFile!.content).toContain('twMerge');
580
+ });
581
+
582
+ it('should include Tailwind v4 CSS with theme variables', () => {
583
+ const files = generateUiPackageFiles(fullstackOptions);
584
+ const globalsCss = files.find(
585
+ (f) => f.path === 'packages/ui/src/styles/globals.css',
586
+ );
587
+ expect(globalsCss).toBeDefined();
588
+ expect(globalsCss!.content).toContain('@import "tailwindcss"');
589
+ expect(globalsCss!.content).toContain('@theme');
590
+ expect(globalsCss!.content).toContain('--color-background');
591
+ expect(globalsCss!.content).toContain('--color-primary');
592
+ expect(globalsCss!.content).toContain('.dark');
593
+ });
594
+
595
+ it('should configure Storybook with Tailwind v4 plugin', () => {
596
+ const files = generateUiPackageFiles(fullstackOptions);
597
+ const storybookMain = files.find(
598
+ (f) => f.path === 'packages/ui/.storybook/main.ts',
599
+ );
600
+ expect(storybookMain).toBeDefined();
601
+ expect(storybookMain!.content).toContain('@storybook/react-vite');
602
+ expect(storybookMain!.content).toContain('@tailwindcss/vite');
603
+ expect(storybookMain!.content).toContain('viteFinal');
604
+ });
605
+
606
+ it('should import globals.css in Storybook preview', () => {
607
+ const files = generateUiPackageFiles(fullstackOptions);
608
+ const storybookPreview = files.find(
609
+ (f) => f.path === 'packages/ui/.storybook/preview.ts',
610
+ );
611
+ expect(storybookPreview).toBeDefined();
612
+ expect(storybookPreview!.content).toContain(
613
+ "import '../src/styles/globals.css'",
614
+ );
615
+ });
616
+
617
+ it('should include shadcn components.json config', () => {
618
+ const files = generateUiPackageFiles(fullstackOptions);
619
+ const componentsJson = files.find(
620
+ (f) => f.path === 'packages/ui/components.json',
621
+ );
622
+ expect(componentsJson).toBeDefined();
623
+ const config = JSON.parse(componentsJson!.content);
624
+ expect(config.$schema).toContain('ui.shadcn.com');
625
+ expect(config.style).toBe('new-york');
626
+ expect(config.tailwind.cssVariables).toBe(true);
627
+ expect(config.aliases.components).toBe('~/components');
628
+ expect(config.aliases.utils).toBe('~/lib/utils');
629
+ });
630
+
631
+ it('should use ~/* path alias in components', () => {
632
+ const files = generateUiPackageFiles(fullstackOptions);
633
+ const buttonFile = files.find(
634
+ (f) => f.path === 'packages/ui/src/components/ui/button/index.tsx',
635
+ );
636
+ expect(buttonFile).toBeDefined();
637
+ expect(buttonFile!.content).toContain("from '~/lib/utils'");
638
+ });
639
+
640
+ it('should configure ~/* path in tsconfig', () => {
641
+ const files = generateUiPackageFiles(fullstackOptions);
642
+ const tsConfig = files.find((f) => f.path === 'packages/ui/tsconfig.json');
643
+ expect(tsConfig).toBeDefined();
644
+ const config = JSON.parse(tsConfig!.content);
645
+ expect(config.compilerOptions.paths['~/*']).toEqual(['./src/*']);
646
+ });
647
+ });
@@ -176,9 +176,9 @@ describe('initCommand', () => {
176
176
  expect(
177
177
  existsSync(join(projectDir, 'packages/models/tsconfig.json')),
178
178
  ).toBe(true);
179
- expect(existsSync(join(projectDir, 'packages/models/src/common.ts'))).toBe(
180
- true,
181
- );
179
+ expect(
180
+ existsSync(join(projectDir, 'packages/models/src/common.ts')),
181
+ ).toBe(true);
182
182
  expect(existsSync(join(projectDir, 'packages/models/src/user.ts'))).toBe(
183
183
  true,
184
184
  );
@@ -55,6 +55,7 @@ export function generateAuthAppFiles(
55
55
  noEmit: true,
56
56
  baseUrl: '.',
57
57
  paths: {
58
+ '~/*': ['./src/*'],
58
59
  [`@${options.name}/*`]: ['../../packages/*/src'],
59
60
  },
60
61
  },
@@ -93,6 +93,7 @@ export default defineConfig({
93
93
  noEmit: true,
94
94
  baseUrl: '.',
95
95
  paths: {
96
+ '~/*': ['./src/*'],
96
97
  [`@${options.name}/*`]: ['../../packages/*/src'],
97
98
  },
98
99
  },
@@ -256,6 +257,7 @@ function generateSingleAppConfigFiles(
256
257
  noEmit: true,
257
258
  baseUrl: '.',
258
259
  paths: {
260
+ '~/*': ['./src/*'],
259
261
  [`@${options.name}/*`]: ['../../packages/*/src'],
260
262
  },
261
263
  },
@@ -51,7 +51,11 @@ export function generateModelsPackage(
51
51
  // Common Schemas
52
52
  // ============================================
53
53
 
54
- export const IdSchema = z.string().uuid();
54
+ export const IdSchema = z.uuid();
55
+
56
+ export const IdParamsSchema = z.object({
57
+ id: IdSchema,
58
+ });
55
59
 
56
60
  export const TimestampsSchema = z.object({
57
61
  createdAt: z.coerce.date(),
@@ -77,6 +81,7 @@ export const PaginatedResponseSchema = <T extends z.ZodTypeAny>(itemSchema: T) =
77
81
  // ============================================
78
82
 
79
83
  export type Id = z.infer<typeof IdSchema>;
84
+ export type IdParams = z.infer<typeof IdParamsSchema>;
80
85
  export type Timestamps = z.infer<typeof TimestampsSchema>;
81
86
  export type Pagination = z.infer<typeof PaginationSchema>;
82
87
  `;
@@ -34,6 +34,9 @@ export function generateMonorepoFiles(
34
34
  lint: 'biome lint .',
35
35
  fmt: 'biome format . --write',
36
36
  'fmt:check': 'biome format .',
37
+ ...(isFullstack
38
+ ? { storybook: 'pnpm --filter ./packages/ui storybook' }
39
+ : {}),
37
40
  ...(options.deployTarget === 'dokploy'
38
41
  ? { deploy: 'gkm deploy --provider dokploy --stage production' }
39
42
  : {}),