@geekmidas/cli 1.10.34 → 1.10.36

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 (64) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/{config-Cuo8vFsp.cjs → config-QnuOcYXp.cjs} +2 -2
  3. package/dist/{config-Cuo8vFsp.cjs.map → config-QnuOcYXp.cjs.map} +1 -1
  4. package/dist/{config-B62g483e.mjs → config-U-gojtxn.mjs} +2 -2
  5. package/dist/{config-B62g483e.mjs.map → config-U-gojtxn.mjs.map} +1 -1
  6. package/dist/config.cjs +2 -2
  7. package/dist/config.d.cts +1 -1
  8. package/dist/config.d.mts +1 -1
  9. package/dist/config.mjs +2 -2
  10. package/dist/deploy/sniffer-loader.cjs +1 -1
  11. package/dist/{index-Dt_dZ7K4.d.mts → index-D7iT4dnv.d.mts} +41 -4
  12. package/dist/index-D7iT4dnv.d.mts.map +1 -0
  13. package/dist/{index-C3t5VL4R.d.cts → index-DRQq26DF.d.cts} +41 -4
  14. package/dist/index-DRQq26DF.d.cts.map +1 -0
  15. package/dist/index.cjs +1200 -63
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.mjs +1200 -63
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/{openapi-CflxypuN.mjs → openapi-BA0e3I_s.mjs} +65 -15
  20. package/dist/openapi-BA0e3I_s.mjs.map +1 -0
  21. package/dist/{openapi-B_rJjImN.cjs → openapi-Bb4UEyZN.cjs} +65 -15
  22. package/dist/openapi-Bb4UEyZN.cjs.map +1 -0
  23. package/dist/openapi.cjs +3 -3
  24. package/dist/openapi.d.cts +7 -0
  25. package/dist/openapi.d.cts.map +1 -1
  26. package/dist/openapi.d.mts +7 -0
  27. package/dist/openapi.d.mts.map +1 -1
  28. package/dist/openapi.mjs +3 -3
  29. package/dist/workspace/index.cjs +4 -1
  30. package/dist/workspace/index.d.cts +2 -2
  31. package/dist/workspace/index.d.mts +2 -2
  32. package/dist/workspace/index.mjs +2 -2
  33. package/dist/{workspace-CGYykWfn.cjs → workspace-BobDOIG9.cjs} +74 -3
  34. package/dist/workspace-BobDOIG9.cjs.map +1 -0
  35. package/dist/{workspace-Bi4X7Yzy.mjs → workspace-Cgmvgwh8.mjs} +57 -4
  36. package/dist/workspace-Cgmvgwh8.mjs.map +1 -0
  37. package/package.json +4 -4
  38. package/src/__tests__/openapi.spec.ts +99 -9
  39. package/src/deploy/__tests__/env-resolver.spec.ts +28 -0
  40. package/src/deploy/env-resolver.ts +5 -10
  41. package/src/deploy/index.ts +13 -7
  42. package/src/deploy/sniffer.ts +10 -4
  43. package/src/dev/__tests__/index.spec.ts +119 -0
  44. package/src/dev/index.ts +162 -37
  45. package/src/index.ts +6 -2
  46. package/src/init/__tests__/generators.spec.ts +147 -0
  47. package/src/init/generators/mobile-expo.ts +576 -0
  48. package/src/init/generators/monorepo.ts +42 -11
  49. package/src/init/generators/web-tanstack.ts +348 -0
  50. package/src/init/index.ts +40 -3
  51. package/src/init/templates/index.ts +31 -0
  52. package/src/init/versions.ts +3 -3
  53. package/src/openapi.ts +110 -23
  54. package/src/workspace/__tests__/index.spec.ts +92 -0
  55. package/src/workspace/__tests__/publicEnv.spec.ts +64 -0
  56. package/src/workspace/index.ts +16 -2
  57. package/src/workspace/publicEnv.ts +59 -0
  58. package/src/workspace/types.ts +6 -1
  59. package/dist/index-C3t5VL4R.d.cts.map +0 -1
  60. package/dist/index-Dt_dZ7K4.d.mts.map +0 -1
  61. package/dist/openapi-B_rJjImN.cjs.map +0 -1
  62. package/dist/openapi-CflxypuN.mjs.map +0 -1
  63. package/dist/workspace-Bi4X7Yzy.mjs.map +0 -1
  64. package/dist/workspace-CGYykWfn.cjs.map +0 -1
package/src/index.ts CHANGED
@@ -286,13 +286,17 @@ program
286
286
  program
287
287
  .command('openapi')
288
288
  .description('Generate OpenAPI specification from endpoints')
289
- .action(async () => {
289
+ .option(
290
+ '--app <name>',
291
+ 'Workspace mode: generate for a single named backend app',
292
+ )
293
+ .action(async (options: { app?: string }) => {
290
294
  try {
291
295
  const globalOptions = program.opts();
292
296
  if (globalOptions.cwd) {
293
297
  process.chdir(globalOptions.cwd);
294
298
  }
295
- await openapiCommand({});
299
+ await openapiCommand({ app: options.app });
296
300
  } catch (error) {
297
301
  console.error(formatError(error));
298
302
  process.exit(1);
@@ -2,11 +2,13 @@ import { describe, expect, it } from 'vitest';
2
2
  import { generateConfigFiles } from '../generators/config.js';
3
3
  import { generateDockerFiles } from '../generators/docker.js';
4
4
  import { generateEnvFiles } from '../generators/env.js';
5
+ import { generateExpoAppFiles } from '../generators/mobile-expo.js';
5
6
  import { generateModelsPackage } from '../generators/models.js';
6
7
  import { generateMonorepoFiles } from '../generators/monorepo.js';
7
8
  import { generatePackageJson } from '../generators/package.js';
8
9
  import { generateTestFiles } from '../generators/test.js';
9
10
  import { generateUiPackageFiles } from '../generators/ui.js';
11
+ import { generateTanStackWebFiles } from '../generators/web-tanstack.js';
10
12
  import { apiTemplate } from '../templates/api.js';
11
13
  import type { TemplateOptions } from '../templates/index.js';
12
14
  import { minimalTemplate } from '../templates/minimal.js';
@@ -431,6 +433,151 @@ describe('generateMonorepoFiles', () => {
431
433
  });
432
434
  });
433
435
 
436
+ describe('generateTanStackWebFiles', () => {
437
+ const fullstackOptions: TemplateOptions = {
438
+ ...baseOptions,
439
+ template: 'fullstack',
440
+ monorepo: true,
441
+ apiPath: 'apps/api',
442
+ frontendFramework: 'tanstack-start',
443
+ };
444
+
445
+ it('returns empty array for non-fullstack template', () => {
446
+ expect(generateTanStackWebFiles(baseOptions)).toHaveLength(0);
447
+ });
448
+
449
+ it('generates the expected file paths', () => {
450
+ const files = generateTanStackWebFiles(fullstackOptions);
451
+ const paths = files.map((f) => f.path);
452
+ expect(paths).toContain('apps/web/package.json');
453
+ expect(paths).toContain('apps/web/vite.config.ts');
454
+ expect(paths).toContain('apps/web/src/router.tsx');
455
+ expect(paths).toContain('apps/web/src/routes/__root.tsx');
456
+ expect(paths).toContain('apps/web/src/routes/index.tsx');
457
+ expect(paths).toContain('apps/web/src/config/client.ts');
458
+ });
459
+
460
+ it('uses VITE_ prefix in client config, not NEXT_PUBLIC_', () => {
461
+ const files = generateTanStackWebFiles(fullstackOptions);
462
+ const clientConfig = files.find(
463
+ (f) => f.path === 'apps/web/src/config/client.ts',
464
+ );
465
+ expect(clientConfig).toBeDefined();
466
+ expect(clientConfig!.content).toContain('VITE_API_URL');
467
+ expect(clientConfig!.content).toContain('VITE_AUTH_URL');
468
+ expect(clientConfig!.content).not.toContain('NEXT_PUBLIC_');
469
+ });
470
+
471
+ it('declares tanstack and vite as dependencies', () => {
472
+ const files = generateTanStackWebFiles(fullstackOptions);
473
+ const pkg = JSON.parse(
474
+ files.find((f) => f.path === 'apps/web/package.json')!.content,
475
+ );
476
+ expect(pkg.dependencies['@tanstack/react-start']).toBeDefined();
477
+ expect(pkg.dependencies['@tanstack/react-router']).toBeDefined();
478
+ expect(pkg.devDependencies.vite).toBeDefined();
479
+ expect(pkg.dependencies.next).toBeUndefined();
480
+ });
481
+ });
482
+
483
+ describe('generateExpoAppFiles', () => {
484
+ const fullstackOptions: TemplateOptions = {
485
+ ...baseOptions,
486
+ template: 'fullstack',
487
+ monorepo: true,
488
+ apiPath: 'apps/api',
489
+ frontendFramework: 'expo',
490
+ };
491
+
492
+ it('returns empty array for non-fullstack template', () => {
493
+ expect(generateExpoAppFiles(baseOptions)).toHaveLength(0);
494
+ });
495
+
496
+ it('generates the expected Expo file paths', () => {
497
+ const files = generateExpoAppFiles(fullstackOptions);
498
+ const paths = files.map((f) => f.path);
499
+ expect(paths).toContain('apps/app/package.json');
500
+ expect(paths).toContain('apps/app/app.config.ts');
501
+ expect(paths).toContain('apps/app/eas.json');
502
+ expect(paths).toContain('apps/app/babel.config.js');
503
+ expect(paths).toContain('apps/app/metro.config.js');
504
+ expect(paths).toContain('apps/app/app/_layout.tsx');
505
+ expect(paths).toContain('apps/app/app/index.tsx');
506
+ expect(paths).toContain('apps/app/app/login.tsx');
507
+ expect(paths).toContain('apps/app/lib/auth-client.ts');
508
+ });
509
+
510
+ it('uses EXPO_PUBLIC_ prefix in config.ts', () => {
511
+ const files = generateExpoAppFiles(fullstackOptions);
512
+ const configTs = files.find((f) => f.path === 'apps/app/config.ts');
513
+ expect(configTs).toBeDefined();
514
+ expect(configTs!.content).toContain('EXPO_PUBLIC_API_URL');
515
+ expect(configTs!.content).toContain('EXPO_PUBLIC_AUTH_URL');
516
+ expect(configTs!.content).not.toContain('VITE_');
517
+ expect(configTs!.content).not.toContain('NEXT_PUBLIC_');
518
+ });
519
+
520
+ it('seeds eas.json with EXPO_PUBLIC_ env vars per profile', () => {
521
+ const files = generateExpoAppFiles(fullstackOptions);
522
+ const eas = JSON.parse(
523
+ files.find((f) => f.path === 'apps/app/eas.json')!.content,
524
+ );
525
+ expect(eas.build.dev.env.EXPO_PUBLIC_API_URL).toBeDefined();
526
+ expect(eas.build.dev.env.EXPO_PUBLIC_AUTH_URL).toBeDefined();
527
+ });
528
+
529
+ it('declares expo + better-auth/expo as dependencies', () => {
530
+ const files = generateExpoAppFiles(fullstackOptions);
531
+ const pkg = JSON.parse(
532
+ files.find((f) => f.path === 'apps/app/package.json')!.content,
533
+ );
534
+ expect(pkg.dependencies.expo).toBeDefined();
535
+ expect(pkg.dependencies['@better-auth/expo']).toBeDefined();
536
+ expect(pkg.dependencies['expo-router']).toBeDefined();
537
+ expect(pkg.dependencies.next).toBeUndefined();
538
+ });
539
+ });
540
+
541
+ describe('generateMonorepoFiles - frontendFramework wiring', () => {
542
+ const fullstackBase: TemplateOptions = {
543
+ ...baseOptions,
544
+ template: 'fullstack',
545
+ monorepo: true,
546
+ apiPath: 'apps/api',
547
+ };
548
+
549
+ it('emits framework: nextjs by default', () => {
550
+ const files = generateMonorepoFiles(
551
+ { ...fullstackBase, frontendFramework: 'nextjs' },
552
+ minimalTemplate,
553
+ );
554
+ const cfg = files.find((f) => f.path === 'gkm.config.ts');
555
+ expect(cfg!.content).toContain("framework: 'nextjs'");
556
+ expect(cfg!.content).toContain("path: 'apps/web'");
557
+ });
558
+
559
+ it('emits framework: tanstack-start when selected', () => {
560
+ const files = generateMonorepoFiles(
561
+ { ...fullstackBase, frontendFramework: 'tanstack-start' },
562
+ minimalTemplate,
563
+ );
564
+ const cfg = files.find((f) => f.path === 'gkm.config.ts');
565
+ expect(cfg!.content).toContain("framework: 'tanstack-start'");
566
+ expect(cfg!.content).toContain("path: 'apps/web'");
567
+ });
568
+
569
+ it('emits framework: expo and apps/app path when selected', () => {
570
+ const files = generateMonorepoFiles(
571
+ { ...fullstackBase, frontendFramework: 'expo' },
572
+ minimalTemplate,
573
+ );
574
+ const cfg = files.find((f) => f.path === 'gkm.config.ts');
575
+ expect(cfg!.content).toContain("framework: 'expo'");
576
+ expect(cfg!.content).toContain("path: 'apps/app'");
577
+ expect(cfg!.content).toContain('port: 8081');
578
+ });
579
+ });
580
+
434
581
  describe('generateModelsPackage', () => {
435
582
  it('should return empty array for non-monorepo', () => {
436
583
  const files = generateModelsPackage(baseOptions);