@geekmidas/cli 1.2.0 → 1.2.2

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/dist/openapi.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  require('./workspace-BMJE18LV.cjs');
3
3
  require('./config-Bayob8pB.cjs');
4
- const require_openapi = require('./openapi-BZP8jkI4.cjs');
4
+ const require_openapi = require('./openapi-CzfnHlhG.cjs');
5
5
 
6
6
  exports.OPENAPI_OUTPUT_PATH = require_openapi.OPENAPI_OUTPUT_PATH;
7
7
  exports.generateOpenApi = require_openapi.generateOpenApi;
@@ -1 +1 @@
1
- {"version":3,"file":"openapi.d.cts","names":[],"sources":["../src/openapi.ts"],"sourcesContent":[],"mappings":";;;;UASU,cAAA;;AAFiD;AAS3D;AAKA;;AACS,cANI,mBAAA,GAMJ,mBAAA;;AACO;AA4BhB;AAAqC,iBA9BrB,oBAAA,CA8BqB,MAAA,EA7B5B,SA6B4B,CAAA,EA5BlC,aA4BkC,GAAA;EAAA,OAC5B,EAAA,OAAA;CAAS;AAER;AAkCV;;;AAEG,iBAvCmB,eAAA,CAuCnB,MAAA,EAtCM,SAsCN,EAAA,QAAA,EAAA;EAAO,MAAA,CAAA,EAAA,OAAA;IApCP;;;;iBAkCmB,cAAA,WACZ,iBACP"}
1
+ {"version":3,"file":"openapi.d.cts","names":[],"sources":["../src/openapi.ts"],"sourcesContent":[],"mappings":";;;;UASU,cAAA;;AAFiD;AAS3D;AAKA;;AACS,cANI,mBAAA,GAMJ,mBAAA;;AACO;AA6BhB;AAAqC,iBA/BrB,oBAAA,CA+BqB,MAAA,EA9B5B,SA8B4B,CAAA,EA7BlC,aA6BkC,GAAA;EAAA,OAC5B,EAAA,OAAA;CAAS;AAER;AAkCV;;;AAEG,iBAvCmB,eAAA,CAuCnB,MAAA,EAtCM,SAsCN,EAAA,QAAA,EAAA;EAAO,MAAA,CAAA,EAAA,OAAA;IApCP;;;;iBAkCmB,cAAA,WACZ,iBACP"}
@@ -1 +1 @@
1
- {"version":3,"file":"openapi.d.mts","names":[],"sources":["../src/openapi.ts"],"sourcesContent":[],"mappings":";;;;UASU,cAAA;;AAFiD;AAS3D;AAKA;;AACS,cANI,mBAAA,GAMJ,mBAAA;;AACO;AA4BhB;AAAqC,iBA9BrB,oBAAA,CA8BqB,MAAA,EA7B5B,SA6B4B,CAAA,EA5BlC,aA4BkC,GAAA;EAAA,OAC5B,EAAA,OAAA;CAAS;AAER;AAkCV;;;AAEG,iBAvCmB,eAAA,CAuCnB,MAAA,EAtCM,SAsCN,EAAA,QAAA,EAAA;EAAO,MAAA,CAAA,EAAA,OAAA;IApCP;;;;iBAkCmB,cAAA,WACZ,iBACP"}
1
+ {"version":3,"file":"openapi.d.mts","names":[],"sources":["../src/openapi.ts"],"sourcesContent":[],"mappings":";;;;UASU,cAAA;;AAFiD;AAS3D;AAKA;;AACS,cANI,mBAAA,GAMJ,mBAAA;;AACO;AA6BhB;AAAqC,iBA/BrB,oBAAA,CA+BqB,MAAA,EA9B5B,SA8B4B,CAAA,EA7BlC,aA6BkC,GAAA;EAAA,OAC5B,EAAA,OAAA;CAAS;AAER;AAkCV;;;AAEG,iBAvCmB,eAAA,CAuCnB,MAAA,EAtCM,SAsCN,EAAA,QAAA,EAAA;EAAO,MAAA,CAAA,EAAA,OAAA;IApCP;;;;iBAkCmB,cAAA,WACZ,iBACP"}
package/dist/openapi.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  import "./workspace-CASoZOjs.mjs";
3
3
  import "./config-BQ4a36Rq.mjs";
4
- import { OPENAPI_OUTPUT_PATH, generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi-DrbBWq0s.mjs";
4
+ import { OPENAPI_OUTPUT_PATH, generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi-BZ4Qik9w.mjs";
5
5
 
6
6
  export { OPENAPI_OUTPUT_PATH, generateOpenApi, openapiCommand, resolveOpenApiConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekmidas/cli",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs",
5
5
  "private": false,
6
6
  "type": "module",
@@ -54,10 +54,10 @@
54
54
  "prompts": "~2.4.2",
55
55
  "tsx": "~4.20.3",
56
56
  "@geekmidas/constructs": "~1.0.0",
57
- "@geekmidas/envkit": "~1.0.0",
58
57
  "@geekmidas/errors": "~1.0.0",
59
- "@geekmidas/schema": "~1.0.0",
60
- "@geekmidas/logger": "~1.0.0"
58
+ "@geekmidas/envkit": "~1.0.0",
59
+ "@geekmidas/logger": "~1.0.0",
60
+ "@geekmidas/schema": "~1.0.0"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@types/lodash.kebabcase": "^4.1.9",
@@ -1,5 +1,5 @@
1
1
  import { existsSync, realpathSync } from 'node:fs';
2
- import { readFile, rm } from 'node:fs/promises';
2
+ import { mkdir, 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 {
@@ -38,9 +38,9 @@ describe('resolveOpenApiConfig', () => {
38
38
  });
39
39
  });
40
40
 
41
- it('should return disabled when openapi is undefined', () => {
41
+ it('should return enabled by default when openapi is undefined', () => {
42
42
  const result = resolveOpenApiConfig({ ...baseConfig });
43
- expect(result.enabled).toBe(false);
43
+ expect(result.enabled).toBe(true);
44
44
  });
45
45
 
46
46
  it('should use custom config values when provided', () => {
@@ -111,9 +111,9 @@ describe('generateOpenApi', () => {
111
111
  expect(result).toBeNull();
112
112
  });
113
113
 
114
- it('should return null when openapi is undefined', async () => {
114
+ it('should return null when no endpoints are found', async () => {
115
115
  const config: GkmConfig = {
116
- routes: './src/endpoints/**/*.ts',
116
+ routes: './src/endpoints/**/*.ts', // Path doesn't exist
117
117
  envParser: './src/config/env#envParser',
118
118
  logger: './src/config/logger#logger',
119
119
  };
@@ -468,3 +468,383 @@ export const complexEndpoint = e
468
468
  expect(content).toContain('export interface paths');
469
469
  });
470
470
  });
471
+
472
+ describe('openapiCommand - workspace mode', () => {
473
+ let tempDir: string;
474
+ const originalCwd = process.cwd();
475
+
476
+ beforeEach(async () => {
477
+ tempDir = realpathSync(await createTempDir('openapi-workspace-'));
478
+ });
479
+
480
+ afterEach(async () => {
481
+ process.chdir(originalCwd);
482
+ await cleanupDir(tempDir);
483
+ vi.restoreAllMocks();
484
+ });
485
+
486
+ it('should generate OpenAPI for backend app in workspace', async () => {
487
+ // Create workspace structure
488
+ const apiDir = join(tempDir, 'apps/api');
489
+ await mkdir(apiDir, { recursive: true });
490
+
491
+ // Create endpoint in backend app
492
+ await createMockEndpointFile(
493
+ apiDir,
494
+ 'src/endpoints/users.ts',
495
+ 'getUsers',
496
+ '/users',
497
+ 'GET',
498
+ );
499
+
500
+ // Create workspace config (gkm.config.json)
501
+ await createTestFile(
502
+ tempDir,
503
+ 'gkm.config.json',
504
+ JSON.stringify({
505
+ name: 'test-workspace',
506
+ apps: {
507
+ api: {
508
+ type: 'backend',
509
+ path: 'apps/api',
510
+ port: 3000,
511
+ routes: './src/endpoints/**/*.ts',
512
+ openapi: { enabled: true },
513
+ },
514
+ },
515
+ }),
516
+ );
517
+
518
+ process.chdir(tempDir);
519
+ const consoleSpy = vi.spyOn(console, 'log');
520
+
521
+ await openapiCommand({ cwd: tempDir });
522
+
523
+ // Should generate OpenAPI in the backend app's .gkm folder
524
+ const outputPath = join(apiDir, OPENAPI_OUTPUT_PATH);
525
+ expect(existsSync(outputPath)).toBe(true);
526
+
527
+ const content = await readFile(outputPath, 'utf-8');
528
+ expect(content).toContain('export interface paths');
529
+ expect(content).toContain("'/users'");
530
+
531
+ expect(consoleSpy).toHaveBeenCalledWith(
532
+ expect.stringContaining('[api] Generated OpenAPI'),
533
+ );
534
+ });
535
+
536
+ it('should copy OpenAPI to frontend app with client.output', async () => {
537
+ // Create workspace structure
538
+ const apiDir = join(tempDir, 'apps/api');
539
+ const webDir = join(tempDir, 'apps/web');
540
+ await mkdir(apiDir, { recursive: true });
541
+ await mkdir(webDir, { recursive: true });
542
+
543
+ // Create endpoint in backend app
544
+ await createMockEndpointFile(
545
+ apiDir,
546
+ 'src/endpoints/users.ts',
547
+ 'getUsers',
548
+ '/users',
549
+ 'GET',
550
+ );
551
+
552
+ // Create workspace config with frontend that depends on backend
553
+ await createTestFile(
554
+ tempDir,
555
+ 'gkm.config.json',
556
+ JSON.stringify({
557
+ name: 'test-workspace',
558
+ apps: {
559
+ api: {
560
+ type: 'backend',
561
+ path: 'apps/api',
562
+ port: 3000,
563
+ routes: './src/endpoints/**/*.ts',
564
+ openapi: { enabled: true },
565
+ },
566
+ web: {
567
+ type: 'frontend',
568
+ framework: 'nextjs',
569
+ path: 'apps/web',
570
+ port: 3001,
571
+ dependencies: ['api'],
572
+ client: {
573
+ output: './src/api',
574
+ },
575
+ },
576
+ },
577
+ }),
578
+ );
579
+
580
+ process.chdir(tempDir);
581
+ const consoleSpy = vi.spyOn(console, 'log');
582
+
583
+ await openapiCommand({ cwd: tempDir });
584
+
585
+ // Should generate OpenAPI in backend app
586
+ const backendOutput = join(apiDir, OPENAPI_OUTPUT_PATH);
587
+ expect(existsSync(backendOutput)).toBe(true);
588
+
589
+ // Should copy to frontend app's client output path
590
+ const frontendOutput = join(webDir, 'src/api/openapi.ts');
591
+ expect(existsSync(frontendOutput)).toBe(true);
592
+
593
+ // Content should be the same
594
+ const backendContent = await readFile(backendOutput, 'utf-8');
595
+ const frontendContent = await readFile(frontendOutput, 'utf-8');
596
+ expect(frontendContent).toBe(backendContent);
597
+
598
+ expect(consoleSpy).toHaveBeenCalledWith(
599
+ expect.stringContaining('[web] ./src/api/openapi.ts'),
600
+ );
601
+ });
602
+
603
+ it('should only copy to frontend apps that depend on the backend', async () => {
604
+ // Create workspace structure with multiple apps
605
+ const apiDir = join(tempDir, 'apps/api');
606
+ const authDir = join(tempDir, 'apps/auth');
607
+ const webDir = join(tempDir, 'apps/web');
608
+ const adminDir = join(tempDir, 'apps/admin');
609
+ await mkdir(apiDir, { recursive: true });
610
+ await mkdir(authDir, { recursive: true });
611
+ await mkdir(webDir, { recursive: true });
612
+ await mkdir(adminDir, { recursive: true });
613
+
614
+ // Create endpoints
615
+ await createMockEndpointFile(
616
+ apiDir,
617
+ 'src/endpoints/users.ts',
618
+ 'getUsers',
619
+ '/users',
620
+ 'GET',
621
+ );
622
+ await createMockEndpointFile(
623
+ authDir,
624
+ 'src/endpoints/login.ts',
625
+ 'login',
626
+ '/login',
627
+ 'POST',
628
+ );
629
+
630
+ // Create workspace config
631
+ await createTestFile(
632
+ tempDir,
633
+ 'gkm.config.json',
634
+ JSON.stringify({
635
+ name: 'test-workspace',
636
+ apps: {
637
+ api: {
638
+ type: 'backend',
639
+ path: 'apps/api',
640
+ port: 3000,
641
+ routes: './src/endpoints/**/*.ts',
642
+ openapi: { enabled: true },
643
+ },
644
+ auth: {
645
+ type: 'backend',
646
+ path: 'apps/auth',
647
+ port: 3001,
648
+ routes: './src/endpoints/**/*.ts',
649
+ openapi: { enabled: true },
650
+ },
651
+ web: {
652
+ type: 'frontend',
653
+ framework: 'nextjs',
654
+ path: 'apps/web',
655
+ port: 3002,
656
+ dependencies: ['api'], // Only depends on api
657
+ client: {
658
+ output: './src/api',
659
+ },
660
+ },
661
+ admin: {
662
+ type: 'frontend',
663
+ framework: 'nextjs',
664
+ path: 'apps/admin',
665
+ port: 3003,
666
+ dependencies: ['auth'], // Only depends on auth
667
+ client: {
668
+ output: './src/client',
669
+ },
670
+ },
671
+ },
672
+ }),
673
+ );
674
+
675
+ process.chdir(tempDir);
676
+
677
+ await openapiCommand({ cwd: tempDir });
678
+
679
+ // Web should have api's OpenAPI (not auth's)
680
+ const webApiOutput = join(webDir, 'src/api/openapi.ts');
681
+ expect(existsSync(webApiOutput)).toBe(true);
682
+ const webContent = await readFile(webApiOutput, 'utf-8');
683
+ expect(webContent).toContain("'/users'");
684
+ expect(webContent).not.toContain("'/login'");
685
+
686
+ // Admin should have auth's OpenAPI (not api's)
687
+ const adminClientOutput = join(adminDir, 'src/client/openapi.ts');
688
+ expect(existsSync(adminClientOutput)).toBe(true);
689
+ const adminContent = await readFile(adminClientOutput, 'utf-8');
690
+ expect(adminContent).toContain("'/login'");
691
+ expect(adminContent).not.toContain("'/users'");
692
+ });
693
+
694
+ it('should not copy to frontend with empty dependencies array', async () => {
695
+ // Create workspace structure
696
+ const apiDir = join(tempDir, 'apps/api');
697
+ const webDir = join(tempDir, 'apps/web');
698
+ await mkdir(apiDir, { recursive: true });
699
+ await mkdir(webDir, { recursive: true });
700
+
701
+ await createMockEndpointFile(
702
+ apiDir,
703
+ 'src/endpoints/users.ts',
704
+ 'getUsers',
705
+ '/users',
706
+ 'GET',
707
+ );
708
+
709
+ // Frontend with empty dependencies array (depends on nothing)
710
+ await createTestFile(
711
+ tempDir,
712
+ 'gkm.config.json',
713
+ JSON.stringify({
714
+ name: 'test-workspace',
715
+ apps: {
716
+ api: {
717
+ type: 'backend',
718
+ path: 'apps/api',
719
+ port: 3000,
720
+ routes: './src/endpoints/**/*.ts',
721
+ openapi: { enabled: true },
722
+ },
723
+ web: {
724
+ type: 'frontend',
725
+ framework: 'nextjs',
726
+ path: 'apps/web',
727
+ port: 3001,
728
+ dependencies: [], // Empty array means depends on nothing
729
+ client: {
730
+ output: './src/api',
731
+ },
732
+ },
733
+ },
734
+ }),
735
+ );
736
+
737
+ process.chdir(tempDir);
738
+
739
+ await openapiCommand({ cwd: tempDir });
740
+
741
+ // Should NOT copy to frontend since it has no dependencies
742
+ const frontendOutput = join(webDir, 'src/api/openapi.ts');
743
+ expect(existsSync(frontendOutput)).toBe(false);
744
+ });
745
+
746
+ it('should skip frontend apps without client.output', async () => {
747
+ const apiDir = join(tempDir, 'apps/api');
748
+ const webDir = join(tempDir, 'apps/web');
749
+ await mkdir(apiDir, { recursive: true });
750
+ await mkdir(webDir, { recursive: true });
751
+
752
+ await createMockEndpointFile(
753
+ apiDir,
754
+ 'src/endpoints/users.ts',
755
+ 'getUsers',
756
+ '/users',
757
+ 'GET',
758
+ );
759
+
760
+ // Frontend without client.output
761
+ await createTestFile(
762
+ tempDir,
763
+ 'gkm.config.json',
764
+ JSON.stringify({
765
+ name: 'test-workspace',
766
+ apps: {
767
+ api: {
768
+ type: 'backend',
769
+ path: 'apps/api',
770
+ port: 3000,
771
+ routes: './src/endpoints/**/*.ts',
772
+ openapi: { enabled: true },
773
+ },
774
+ web: {
775
+ type: 'frontend',
776
+ framework: 'nextjs',
777
+ path: 'apps/web',
778
+ port: 3001,
779
+ dependencies: ['api'],
780
+ // No client.output configured
781
+ },
782
+ },
783
+ }),
784
+ );
785
+
786
+ process.chdir(tempDir);
787
+
788
+ await openapiCommand({ cwd: tempDir });
789
+
790
+ // Backend should still have OpenAPI generated
791
+ expect(existsSync(join(apiDir, OPENAPI_OUTPUT_PATH))).toBe(true);
792
+
793
+ // But no files should be created in frontend
794
+ expect(existsSync(join(webDir, 'src/api/openapi.ts'))).toBe(false);
795
+ });
796
+
797
+ it('should handle nested client.output paths', async () => {
798
+ const apiDir = join(tempDir, 'apps/api');
799
+ const webDir = join(tempDir, 'apps/web');
800
+ await mkdir(apiDir, { recursive: true });
801
+ await mkdir(webDir, { recursive: true });
802
+
803
+ await createMockEndpointFile(
804
+ apiDir,
805
+ 'src/endpoints/users.ts',
806
+ 'getUsers',
807
+ '/users',
808
+ 'GET',
809
+ );
810
+
811
+ // Deeply nested client output path
812
+ await createTestFile(
813
+ tempDir,
814
+ 'gkm.config.json',
815
+ JSON.stringify({
816
+ name: 'test-workspace',
817
+ apps: {
818
+ api: {
819
+ type: 'backend',
820
+ path: 'apps/api',
821
+ port: 3000,
822
+ routes: './src/endpoints/**/*.ts',
823
+ openapi: { enabled: true },
824
+ },
825
+ web: {
826
+ type: 'frontend',
827
+ framework: 'nextjs',
828
+ path: 'apps/web',
829
+ port: 3001,
830
+ dependencies: ['api'],
831
+ client: {
832
+ output: './src/lib/api/generated',
833
+ },
834
+ },
835
+ },
836
+ }),
837
+ );
838
+
839
+ process.chdir(tempDir);
840
+
841
+ await openapiCommand({ cwd: tempDir });
842
+
843
+ // Should create nested directories and file
844
+ const frontendOutput = join(webDir, 'src/lib/api/generated/openapi.ts');
845
+ expect(existsSync(frontendOutput)).toBe(true);
846
+
847
+ const content = await readFile(frontendOutput, 'utf-8');
848
+ expect(content).toContain('export interface paths');
849
+ });
850
+ });
@@ -205,7 +205,7 @@ async function sniffEntryFile(
205
205
  env: {
206
206
  ...process.env,
207
207
  // Ensure tsx is available for TypeScript entry files
208
- NODE_OPTIONS: '--import tsx',
208
+ NODE_OPTIONS: '--import=tsx',
209
209
  },
210
210
  },
211
211
  );
package/src/dev/index.ts CHANGED
@@ -1294,11 +1294,12 @@ function generateCredentialsInjection(secretsJsonPath: string): string {
1294
1294
  return `import { Credentials } from '@geekmidas/envkit/credentials';
1295
1295
  import { existsSync, readFileSync } from 'node:fs';
1296
1296
 
1297
- // Inject dev secrets into Credentials
1297
+ // Inject dev secrets into Credentials and process.env
1298
1298
  const secretsPath = '${secretsJsonPath}';
1299
1299
  if (existsSync(secretsPath)) {
1300
1300
  const secrets = JSON.parse(readFileSync(secretsPath, 'utf-8'));
1301
1301
  Object.assign(Credentials, secrets);
1302
+ Object.assign(process.env, secrets);
1302
1303
  // Debug: uncomment to verify preload is running
1303
1304
  // console.log('[gkm preload] Injected', Object.keys(secrets).length, 'credentials');
1304
1305
  }
@@ -1911,8 +1912,8 @@ export async function execCommand(
1911
1912
  // Merge NODE_OPTIONS with existing value (if any)
1912
1913
  // Add tsx loader first so our .ts preload can be loaded
1913
1914
  const existingNodeOptions = process.env.NODE_OPTIONS ?? '';
1914
- const tsxImport = '--import tsx';
1915
- const preloadImport = `--import ${preloadPath}`;
1915
+ const tsxImport = '--import=tsx';
1916
+ const preloadImport = `--import=${preloadPath}`;
1916
1917
 
1917
1918
  // Build NODE_OPTIONS: existing + tsx loader + our preload
1918
1919
  const nodeOptions = [existingNodeOptions, tsxImport, preloadImport]
@@ -722,7 +722,6 @@ export function createApi(options: CreateApiOptions) {
722
722
  `;
723
723
 
724
724
  return `// Auto-generated by @geekmidas/cli - DO NOT EDIT
725
- // Generated: ${new Date().toISOString()}
726
725
 
727
726
  // ============================================================
728
727
  // Security Scheme Type
@@ -317,7 +317,7 @@ export { Button, buttonVariants };
317
317
 
318
318
  // src/components/ui/button/button.stories.tsx
319
319
  const buttonStories = `import type { Meta, StoryObj } from '@storybook/react';
320
- import { Button } from '.';
320
+ import { Button } from '~/components/ui/button';
321
321
 
322
322
  const meta: Meta<typeof Button> = {
323
323
  title: 'Components/Button',
@@ -488,7 +488,7 @@ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
488
488
 
489
489
  // src/components/ui/input/input.stories.tsx
490
490
  const inputStories = `import type { Meta, StoryObj } from '@storybook/react';
491
- import { Input } from '.';
491
+ import { Input } from '~/components/ui/input';
492
492
 
493
493
  const meta: Meta<typeof Input> = {
494
494
  title: 'Components/Input',
@@ -544,7 +544,7 @@ export const WithValue: Story = {
544
544
 
545
545
  // src/components/ui/card/card.stories.tsx
546
546
  const cardStories = `import type { Meta, StoryObj } from '@storybook/react';
547
- import { Button } from '../button';
547
+ import { Button } from '~/components/ui/button';
548
548
  import {
549
549
  Card,
550
550
  CardContent,
@@ -552,8 +552,8 @@ import {
552
552
  CardFooter,
553
553
  CardHeader,
554
554
  CardTitle,
555
- } from '.';
556
- import { Input } from '../input';
555
+ } from '~/components/ui/card';
556
+ import { Input } from '~/components/ui/input';
557
557
 
558
558
  const meta: Meta<typeof Card> = {
559
559
  title: 'Components/Card',
@@ -635,8 +635,8 @@ export { Label };
635
635
 
636
636
  // src/components/ui/label/label.stories.tsx
637
637
  const labelStories = `import type { Meta, StoryObj } from '@storybook/react';
638
- import { Input } from '../input';
639
- import { Label } from '.';
638
+ import { Input } from '~/components/ui/input';
639
+ import { Label } from '~/components/ui/label';
640
640
 
641
641
  const meta: Meta<typeof Label> = {
642
642
  title: 'Components/Label',
@@ -715,7 +715,7 @@ export { Badge, badgeVariants };
715
715
 
716
716
  // src/components/ui/badge/badge.stories.tsx
717
717
  const badgeStories = `import type { Meta, StoryObj } from '@storybook/react';
718
- import { Badge } from '.';
718
+ import { Badge } from '~/components/ui/badge';
719
719
 
720
720
  const meta: Meta<typeof Badge> = {
721
721
  title: 'Components/Badge',
@@ -795,7 +795,7 @@ export { Separator };
795
795
 
796
796
  // src/components/ui/separator/separator.stories.tsx
797
797
  const separatorStories = `import type { Meta, StoryObj } from '@storybook/react';
798
- import { Separator } from '.';
798
+ import { Separator } from '~/components/ui/separator';
799
799
 
800
800
  const meta: Meta<typeof Separator> = {
801
801
  title: 'Components/Separator',
@@ -904,11 +904,11 @@ export { Tabs, TabsList, TabsTrigger, TabsContent };
904
904
 
905
905
  // src/components/ui/tabs/tabs.stories.tsx
906
906
  const tabsStories = `import type { Meta, StoryObj } from '@storybook/react';
907
- import { Tabs, TabsContent, TabsList, TabsTrigger } from '.';
908
- import { Button } from '../button';
909
- import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '../card';
910
- import { Input } from '../input';
911
- import { Label } from '../label';
907
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from '~/components/ui/tabs';
908
+ import { Button } from '~/components/ui/button';
909
+ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '~/components/ui/card';
910
+ import { Input } from '~/components/ui/input';
911
+ import { Label } from '~/components/ui/label';
912
912
 
913
913
  const meta: Meta<typeof Tabs> = {
914
914
  title: 'Components/Tabs',
@@ -1012,8 +1012,8 @@ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
1012
1012
 
1013
1013
  // src/components/ui/tooltip/tooltip.stories.tsx
1014
1014
  const tooltipStories = `import type { Meta, StoryObj } from '@storybook/react';
1015
- import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '.';
1016
- import { Button } from '../button';
1015
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '~/components/ui/tooltip';
1016
+ import { Button } from '~/components/ui/button';
1017
1017
 
1018
1018
  const meta: Meta<typeof Tooltip> = {
1019
1019
  title: 'Components/Tooltip',
@@ -1217,10 +1217,10 @@ import {
1217
1217
  DialogHeader,
1218
1218
  DialogTitle,
1219
1219
  DialogTrigger,
1220
- } from '.';
1221
- import { Button } from '../button';
1222
- import { Input } from '../input';
1223
- import { Label } from '../label';
1220
+ } from '~/components/ui/dialog';
1221
+ import { Button } from '~/components/ui/button';
1222
+ import { Input } from '~/components/ui/input';
1223
+ import { Label } from '~/components/ui/label';
1224
1224
 
1225
1225
  const meta: Meta<typeof Dialog> = {
1226
1226
  title: 'Components/Dialog',
@@ -29,6 +29,7 @@ export function generateWebAppFiles(options: TemplateOptions): GeneratedFile[] {
29
29
  [modelsPackage]: 'workspace:*',
30
30
  [uiPackage]: 'workspace:*',
31
31
  '@geekmidas/client': GEEKMIDAS_VERSIONS['@geekmidas/client'],
32
+ '@geekmidas/envkit': GEEKMIDAS_VERSIONS['@geekmidas/envkit'],
32
33
  '@tanstack/react-query': '~5.80.0',
33
34
  'better-auth': '~1.2.0',
34
35
  next: '~16.1.0',
@@ -42,6 +43,7 @@ export function generateWebAppFiles(options: TemplateOptions): GeneratedFile[] {
42
43
  '@types/react': '~19.0.0',
43
44
  '@types/react-dom': '~19.0.0',
44
45
  tailwindcss: '^4.0.0',
46
+ tsx: '~4.20.0',
45
47
  typescript: '~5.8.2',
46
48
  },
47
49
  };
package/src/openapi.ts CHANGED
@@ -27,8 +27,9 @@ export function resolveOpenApiConfig(
27
27
  }
28
28
 
29
29
  if (config.openapi === true || config.openapi === undefined) {
30
+ // Enable by default when not explicitly set (undefined) or explicitly true
30
31
  return {
31
- enabled: config.openapi === true,
32
+ enabled: true,
32
33
  title: 'API Documentation',
33
34
  version: '1.0.0',
34
35
  description: 'Auto-generated API documentation from endpoints',