@geekmidas/cli 0.48.0 → 0.49.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 (49) hide show
  1. package/dist/{dokploy-api-DvzIDxTj.mjs → dokploy-api-94KzmTVf.mjs} +4 -4
  2. package/dist/dokploy-api-94KzmTVf.mjs.map +1 -0
  3. package/dist/dokploy-api-CItuaWTq.mjs +3 -0
  4. package/dist/dokploy-api-DBNE8MDt.cjs +3 -0
  5. package/dist/{dokploy-api-BDLu0qWi.cjs → dokploy-api-YD8WCQfW.cjs} +4 -4
  6. package/dist/dokploy-api-YD8WCQfW.cjs.map +1 -0
  7. package/dist/index.cjs +2392 -1888
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.mjs +2389 -1885
  10. package/dist/index.mjs.map +1 -1
  11. package/package.json +6 -4
  12. package/src/build/__tests__/handler-templates.spec.ts +947 -0
  13. package/src/deploy/__tests__/__fixtures__/entry-apps/async-entry.ts +24 -0
  14. package/src/deploy/__tests__/__fixtures__/entry-apps/nested-config-entry.ts +24 -0
  15. package/src/deploy/__tests__/__fixtures__/entry-apps/no-env-entry.ts +12 -0
  16. package/src/deploy/__tests__/__fixtures__/entry-apps/simple-entry.ts +14 -0
  17. package/src/deploy/__tests__/__fixtures__/entry-apps/throwing-entry.ts +16 -0
  18. package/src/deploy/__tests__/__fixtures__/env-parsers/non-function-export.ts +10 -0
  19. package/src/deploy/__tests__/__fixtures__/env-parsers/parseable-env-parser.ts +18 -0
  20. package/src/deploy/__tests__/__fixtures__/env-parsers/throwing-env-parser.ts +18 -0
  21. package/src/deploy/__tests__/__fixtures__/env-parsers/valid-env-parser.ts +16 -0
  22. package/src/deploy/__tests__/dns-verification.spec.ts +229 -0
  23. package/src/deploy/__tests__/dokploy-api.spec.ts +2 -3
  24. package/src/deploy/__tests__/domain.spec.ts +7 -3
  25. package/src/deploy/__tests__/env-resolver.spec.ts +469 -0
  26. package/src/deploy/__tests__/index.spec.ts +12 -12
  27. package/src/deploy/__tests__/secrets.spec.ts +4 -1
  28. package/src/deploy/__tests__/sniffer.spec.ts +326 -1
  29. package/src/deploy/__tests__/state.spec.ts +844 -0
  30. package/src/deploy/dns/hostinger-api.ts +4 -1
  31. package/src/deploy/dns/index.ts +113 -1
  32. package/src/deploy/docker.ts +1 -2
  33. package/src/deploy/dokploy-api.ts +18 -9
  34. package/src/deploy/domain.ts +5 -4
  35. package/src/deploy/env-resolver.ts +278 -0
  36. package/src/deploy/index.ts +525 -119
  37. package/src/deploy/secrets.ts +7 -2
  38. package/src/deploy/sniffer-envkit-patch.ts +43 -0
  39. package/src/deploy/sniffer-hooks.ts +52 -0
  40. package/src/deploy/sniffer-loader.ts +23 -0
  41. package/src/deploy/sniffer-worker.ts +74 -0
  42. package/src/deploy/sniffer.ts +136 -14
  43. package/src/deploy/state.ts +162 -1
  44. package/src/init/versions.ts +3 -3
  45. package/tsconfig.tsbuildinfo +1 -1
  46. package/dist/dokploy-api-BDLu0qWi.cjs.map +0 -1
  47. package/dist/dokploy-api-BN3V57z1.mjs +0 -3
  48. package/dist/dokploy-api-BdCKjFDA.cjs +0 -3
  49. package/dist/dokploy-api-DvzIDxTj.mjs.map +0 -1
@@ -1,7 +1,13 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import type { EndpointAnalysis, EndpointFeatures } from '../endpoint-analyzer';
3
3
  import {
4
+ generateEndpointFilesByTier,
5
+ generateEndpointFilesNested,
6
+ generateMinimalHandler,
7
+ generateOptimizedEndpointsFile,
4
8
  generateOptimizedImports,
9
+ generateOptimizedSetupFunction,
10
+ generateStandardHandler,
5
11
  generateValidatorFactories,
6
12
  } from '../handler-templates';
7
13
 
@@ -269,4 +275,945 @@ describe('handler-templates', () => {
269
275
  expect(result).not.toContain('validateParams');
270
276
  });
271
277
  });
278
+
279
+ describe('generateMinimalHandler', () => {
280
+ it('should generate inline handler for truly minimal endpoints', () => {
281
+ const analysis = createAnalysis({
282
+ route: '/health',
283
+ method: 'GET',
284
+ exportName: 'healthEndpoint',
285
+ tier: 'minimal',
286
+ });
287
+
288
+ const result = generateMinimalHandler(analysis);
289
+
290
+ expect(result).toContain("app.get('/health'");
291
+ expect(result).toContain('healthEndpoint.handler');
292
+ expect(result).toContain('Minimal handler: /health (GET)');
293
+ expect(result).toContain('c.json(result');
294
+ });
295
+
296
+ it('should generate handler with body validation', () => {
297
+ const analysis = createAnalysis({
298
+ route: '/users',
299
+ method: 'POST',
300
+ exportName: 'createUserEndpoint',
301
+ tier: 'minimal',
302
+ features: createFeatures({ hasBodyValidation: true }),
303
+ });
304
+
305
+ const result = generateMinimalHandler(analysis);
306
+
307
+ expect(result).toContain("app.post('/users'");
308
+ expect(result).toContain('Minimal handler with validation');
309
+ expect(result).toContain("(c.req.valid as any)('json')");
310
+ });
311
+
312
+ it('should generate handler with query validation', () => {
313
+ const analysis = createAnalysis({
314
+ route: '/search',
315
+ method: 'GET',
316
+ exportName: 'searchEndpoint',
317
+ tier: 'minimal',
318
+ features: createFeatures({ hasQueryValidation: true }),
319
+ });
320
+
321
+ const result = generateMinimalHandler(analysis);
322
+
323
+ expect(result).toContain("(c.req.valid as any)('query')");
324
+ });
325
+
326
+ it('should generate handler with param validation', () => {
327
+ const analysis = createAnalysis({
328
+ route: '/users/:id',
329
+ method: 'GET',
330
+ exportName: 'getUserEndpoint',
331
+ tier: 'minimal',
332
+ features: createFeatures({ hasParamValidation: true }),
333
+ });
334
+
335
+ const result = generateMinimalHandler(analysis);
336
+
337
+ expect(result).toContain("(c.req.valid as any)('param')");
338
+ });
339
+
340
+ it('should generate handler with output validation', () => {
341
+ const analysis = createAnalysis({
342
+ route: '/data',
343
+ method: 'GET',
344
+ exportName: 'dataEndpoint',
345
+ tier: 'minimal',
346
+ features: createFeatures({ hasOutputValidation: true }),
347
+ });
348
+
349
+ const result = generateMinimalHandler(analysis);
350
+
351
+ expect(result).toContain('parseOutput(result)');
352
+ });
353
+
354
+ it('should generate handler with all validation types', () => {
355
+ const analysis = createAnalysis({
356
+ route: '/items/:id',
357
+ method: 'PUT',
358
+ exportName: 'updateItemEndpoint',
359
+ tier: 'minimal',
360
+ features: createFeatures({
361
+ hasBodyValidation: true,
362
+ hasQueryValidation: true,
363
+ hasParamValidation: true,
364
+ hasOutputValidation: true,
365
+ }),
366
+ });
367
+
368
+ const result = generateMinimalHandler(analysis);
369
+
370
+ expect(result).toContain("(c.req.valid as any)('json')");
371
+ expect(result).toContain("(c.req.valid as any)('query')");
372
+ expect(result).toContain("(c.req.valid as any)('param')");
373
+ expect(result).toContain('parseOutput(result)');
374
+ });
375
+ });
376
+
377
+ describe('generateStandardHandler', () => {
378
+ it('should generate handler with services', () => {
379
+ const analysis = createAnalysis({
380
+ route: '/api/data',
381
+ method: 'GET',
382
+ exportName: 'dataEndpoint',
383
+ tier: 'standard',
384
+ features: createFeatures({ hasServices: true }),
385
+ });
386
+
387
+ const result = generateStandardHandler(analysis);
388
+
389
+ expect(result).toContain('Standard handler: /api/data (GET)');
390
+ expect(result).toContain('serviceDiscovery.register');
391
+ expect(result).toContain('ResponseBuilder');
392
+ });
393
+
394
+ it('should generate handler with database', () => {
395
+ const analysis = createAnalysis({
396
+ route: '/api/users',
397
+ method: 'GET',
398
+ exportName: 'usersEndpoint',
399
+ tier: 'standard',
400
+ features: createFeatures({ hasDatabase: true }),
401
+ });
402
+
403
+ const result = generateStandardHandler(analysis);
404
+
405
+ expect(result).toContain('databaseService');
406
+ expect(result).toContain('serviceDiscovery.register');
407
+ });
408
+
409
+ it('should generate handler with authentication', () => {
410
+ const analysis = createAnalysis({
411
+ route: '/api/profile',
412
+ method: 'GET',
413
+ exportName: 'profileEndpoint',
414
+ tier: 'standard',
415
+ features: createFeatures({ hasAuth: true }),
416
+ });
417
+
418
+ const result = generateStandardHandler(analysis);
419
+
420
+ expect(result).toContain('// Authentication');
421
+ expect(result).toContain('getSession');
422
+ expect(result).toContain('authorize');
423
+ expect(result).toContain('Unauthorized');
424
+ });
425
+
426
+ it('should generate handler with events', () => {
427
+ const analysis = createAnalysis({
428
+ route: '/api/orders',
429
+ method: 'POST',
430
+ exportName: 'createOrderEndpoint',
431
+ tier: 'standard',
432
+ features: createFeatures({ hasEvents: true }),
433
+ });
434
+
435
+ const result = generateStandardHandler(analysis);
436
+
437
+ expect(result).toContain('publishConstructEvents');
438
+ expect(result).toContain('isSuccessStatus');
439
+ });
440
+
441
+ it('should generate handler without services when not needed', () => {
442
+ const analysis = createAnalysis({
443
+ route: '/api/simple',
444
+ method: 'GET',
445
+ exportName: 'simpleEndpoint',
446
+ tier: 'standard',
447
+ features: createFeatures({ hasAuth: true }),
448
+ });
449
+
450
+ const result = generateStandardHandler(analysis);
451
+
452
+ expect(result).toContain('const services = {};');
453
+ expect(result).toContain('const db = undefined;');
454
+ });
455
+
456
+ it('should generate handler with body validation', () => {
457
+ const analysis = createAnalysis({
458
+ route: '/api/items',
459
+ method: 'POST',
460
+ exportName: 'createItemEndpoint',
461
+ tier: 'standard',
462
+ features: createFeatures({
463
+ hasServices: true,
464
+ hasBodyValidation: true,
465
+ }),
466
+ });
467
+
468
+ const result = generateStandardHandler(analysis);
469
+
470
+ expect(result).toContain("(c.req.valid as any)('json')");
471
+ });
472
+
473
+ it('should generate handler with all features', () => {
474
+ const analysis = createAnalysis({
475
+ route: '/api/complex/:id',
476
+ method: 'PUT',
477
+ exportName: 'complexEndpoint',
478
+ tier: 'standard',
479
+ features: createFeatures({
480
+ hasAuth: true,
481
+ hasServices: true,
482
+ hasDatabase: true,
483
+ hasBodyValidation: true,
484
+ hasQueryValidation: true,
485
+ hasParamValidation: true,
486
+ hasEvents: true,
487
+ }),
488
+ });
489
+
490
+ const result = generateStandardHandler(analysis);
491
+
492
+ expect(result).toContain('getSession');
493
+ expect(result).toContain('serviceDiscovery.register');
494
+ expect(result).toContain('databaseService');
495
+ expect(result).toContain("(c.req.valid as any)('json')");
496
+ expect(result).toContain("(c.req.valid as any)('query')");
497
+ expect(result).toContain("(c.req.valid as any)('param')");
498
+ expect(result).toContain('publishConstructEvents');
499
+ });
500
+
501
+ it('should generate handler with output validation', () => {
502
+ const analysis = createAnalysis({
503
+ route: '/api/data',
504
+ method: 'GET',
505
+ exportName: 'dataEndpoint',
506
+ tier: 'standard',
507
+ features: createFeatures({
508
+ hasServices: true,
509
+ hasOutputValidation: true,
510
+ }),
511
+ });
512
+
513
+ const result = generateStandardHandler(analysis);
514
+
515
+ expect(result).toContain('outputSchema');
516
+ expect(result).toContain('parseOutput(data)');
517
+ });
518
+ });
519
+
520
+ describe('generateOptimizedSetupFunction', () => {
521
+ it('should generate setup function with only minimal endpoints', () => {
522
+ const analyses = [
523
+ createAnalysis({
524
+ route: '/health',
525
+ method: 'GET',
526
+ exportName: 'healthEndpoint',
527
+ tier: 'minimal',
528
+ }),
529
+ ];
530
+
531
+ const result = generateOptimizedSetupFunction(analyses, ['healthEndpoint']);
532
+
533
+ expect(result).toContain('export async function setupEndpoints');
534
+ expect(result).toContain('Minimal handlers (1 endpoints)');
535
+ expect(result).toContain('Standard handlers (0 endpoints)');
536
+ expect(result).toContain("app.get('/health'");
537
+ expect(result).not.toContain('HonoEndpoint.addRoutes');
538
+ });
539
+
540
+ it('should generate setup function with full endpoints', () => {
541
+ const analyses = [
542
+ createAnalysis({
543
+ route: '/api/audit',
544
+ method: 'POST',
545
+ exportName: 'auditEndpoint',
546
+ tier: 'full',
547
+ features: createFeatures({ hasAudits: true }),
548
+ }),
549
+ ];
550
+
551
+ const result = generateOptimizedSetupFunction(analyses, ['auditEndpoint']);
552
+
553
+ expect(result).toContain('import { HonoEndpoint }');
554
+ expect(result).toContain('HonoEndpoint.addRoutes');
555
+ expect(result).toContain('auditEndpoint');
556
+ expect(result).toContain('openApiOptions');
557
+ });
558
+
559
+ it('should generate setup function with mixed tiers', () => {
560
+ const analyses = [
561
+ createAnalysis({
562
+ route: '/health',
563
+ method: 'GET',
564
+ exportName: 'healthEndpoint',
565
+ tier: 'minimal',
566
+ }),
567
+ createAnalysis({
568
+ route: '/api/users',
569
+ method: 'GET',
570
+ exportName: 'usersEndpoint',
571
+ tier: 'standard',
572
+ features: createFeatures({ hasAuth: true }),
573
+ }),
574
+ createAnalysis({
575
+ route: '/api/audit',
576
+ method: 'POST',
577
+ exportName: 'auditEndpoint',
578
+ tier: 'full',
579
+ features: createFeatures({ hasAudits: true }),
580
+ }),
581
+ ];
582
+
583
+ const result = generateOptimizedSetupFunction(analyses, [
584
+ 'healthEndpoint',
585
+ 'usersEndpoint',
586
+ 'auditEndpoint',
587
+ ]);
588
+
589
+ expect(result).toContain('Minimal handlers (1 endpoints)');
590
+ expect(result).toContain('Standard handlers (1 endpoints)');
591
+ expect(result).toContain("app.get('/health'");
592
+ expect(result).toContain("app.get('/api/users'");
593
+ expect(result).toContain('HonoEndpoint.addRoutes');
594
+ });
595
+
596
+ it('should add swagger UI setup when enableOpenApi', () => {
597
+ const analyses = [
598
+ createAnalysis({
599
+ route: '/health',
600
+ method: 'GET',
601
+ exportName: 'healthEndpoint',
602
+ tier: 'minimal',
603
+ }),
604
+ ];
605
+
606
+ const result = generateOptimizedSetupFunction(analyses, ['healthEndpoint']);
607
+
608
+ expect(result).toContain('if (enableOpenApi)');
609
+ expect(result).toContain('swaggerUI');
610
+ expect(result).toContain('/__docs/ui');
611
+ });
612
+ });
613
+
614
+ describe('generateOptimizedEndpointsFile', () => {
615
+ it('should generate complete file with all sections', () => {
616
+ const analyses = [
617
+ createAnalysis({
618
+ route: '/health',
619
+ method: 'GET',
620
+ exportName: 'healthEndpoint',
621
+ tier: 'minimal',
622
+ }),
623
+ createAnalysis({
624
+ route: '/api/users',
625
+ method: 'POST',
626
+ exportName: 'createUserEndpoint',
627
+ tier: 'standard',
628
+ features: createFeatures({ hasAuth: true, hasBodyValidation: true }),
629
+ }),
630
+ ];
631
+
632
+ const endpointImports = `import { healthEndpoint } from './endpoints/health';\nimport { createUserEndpoint } from './endpoints/users';`;
633
+
634
+ const result = generateOptimizedEndpointsFile(
635
+ analyses,
636
+ endpointImports,
637
+ ['healthEndpoint', 'createUserEndpoint'],
638
+ );
639
+
640
+ expect(result).toContain('Generated optimized endpoints file');
641
+ expect(result).toContain('minimal: 1 endpoints');
642
+ expect(result).toContain('standard: 1 endpoints');
643
+ expect(result).toContain('full: 0 endpoints');
644
+ expect(result).toContain("import type { EnvironmentParser }");
645
+ expect(result).toContain("import { healthEndpoint }");
646
+ expect(result).toContain("import { createUserEndpoint }");
647
+ expect(result).toContain('validateBody');
648
+ expect(result).toContain('export async function setupEndpoints');
649
+ });
650
+
651
+ it('should not generate validator factories when not needed', () => {
652
+ const analyses = [
653
+ createAnalysis({
654
+ route: '/health',
655
+ method: 'GET',
656
+ exportName: 'healthEndpoint',
657
+ tier: 'minimal',
658
+ }),
659
+ ];
660
+
661
+ const result = generateOptimizedEndpointsFile(
662
+ analyses,
663
+ "import { healthEndpoint } from './endpoints/health';",
664
+ ['healthEndpoint'],
665
+ );
666
+
667
+ expect(result).not.toContain('validateBody');
668
+ expect(result).not.toContain('validateQuery');
669
+ expect(result).not.toContain('validateParams');
670
+ });
671
+ });
672
+
673
+ describe('generateEndpointFilesByTier', () => {
674
+ it('should generate all required files', () => {
675
+ const analyses = [
676
+ createAnalysis({
677
+ route: '/health',
678
+ method: 'GET',
679
+ exportName: 'healthEndpoint',
680
+ tier: 'minimal',
681
+ }),
682
+ ];
683
+
684
+ const endpointImports = [
685
+ { exportName: 'healthEndpoint', importPath: '../endpoints/health' },
686
+ ];
687
+
688
+ const files = generateEndpointFilesByTier(analyses, endpointImports);
689
+
690
+ expect(files).toHaveProperty('validators.ts');
691
+ expect(files).toHaveProperty('minimal.ts');
692
+ expect(files).toHaveProperty('standard.ts');
693
+ expect(files).toHaveProperty('full.ts');
694
+ expect(files).toHaveProperty('index.ts');
695
+ });
696
+
697
+ it('should generate validators file with needed validators', () => {
698
+ const analyses = [
699
+ createAnalysis({
700
+ route: '/api/users',
701
+ method: 'POST',
702
+ exportName: 'createUserEndpoint',
703
+ tier: 'minimal',
704
+ features: createFeatures({ hasBodyValidation: true }),
705
+ }),
706
+ ];
707
+
708
+ const endpointImports = [
709
+ { exportName: 'createUserEndpoint', importPath: '../endpoints/users' },
710
+ ];
711
+
712
+ const files = generateEndpointFilesByTier(analyses, endpointImports);
713
+
714
+ expect(files['validators.ts']).toContain('export const validateBody');
715
+ expect(files['validators.ts']).toContain("validator('json'");
716
+ });
717
+
718
+ it('should generate empty validators file when no validation needed', () => {
719
+ const analyses = [
720
+ createAnalysis({
721
+ route: '/health',
722
+ method: 'GET',
723
+ exportName: 'healthEndpoint',
724
+ tier: 'minimal',
725
+ }),
726
+ ];
727
+
728
+ const endpointImports = [
729
+ { exportName: 'healthEndpoint', importPath: '../endpoints/health' },
730
+ ];
731
+
732
+ const files = generateEndpointFilesByTier(analyses, endpointImports);
733
+
734
+ expect(files['validators.ts']).toContain('No validators needed');
735
+ expect(files['validators.ts']).toContain('export {}');
736
+ });
737
+
738
+ it('should generate minimal file with handlers', () => {
739
+ const analyses = [
740
+ createAnalysis({
741
+ route: '/health',
742
+ method: 'GET',
743
+ exportName: 'healthEndpoint',
744
+ tier: 'minimal',
745
+ }),
746
+ ];
747
+
748
+ const endpointImports = [
749
+ { exportName: 'healthEndpoint', importPath: '../endpoints/health' },
750
+ ];
751
+
752
+ const files = generateEndpointFilesByTier(analyses, endpointImports);
753
+
754
+ expect(files['minimal.ts']).toContain('Minimal-tier endpoint handlers');
755
+ expect(files['minimal.ts']).toContain('1 endpoints');
756
+ expect(files['minimal.ts']).toContain('export function setupMinimalEndpoints');
757
+ expect(files['minimal.ts']).toContain("import { healthEndpoint }");
758
+ expect(files['minimal.ts']).toContain("app.get('/health'");
759
+ });
760
+
761
+ it('should generate empty minimal file when no minimal endpoints', () => {
762
+ const analyses = [
763
+ createAnalysis({
764
+ route: '/api/users',
765
+ method: 'GET',
766
+ exportName: 'usersEndpoint',
767
+ tier: 'standard',
768
+ features: createFeatures({ hasAuth: true }),
769
+ }),
770
+ ];
771
+
772
+ const endpointImports = [
773
+ { exportName: 'usersEndpoint', importPath: '../endpoints/users' },
774
+ ];
775
+
776
+ const files = generateEndpointFilesByTier(analyses, endpointImports);
777
+
778
+ expect(files['minimal.ts']).toContain('No minimal-tier endpoints');
779
+ expect(files['minimal.ts']).toContain('_app: Hono');
780
+ });
781
+
782
+ it('should generate standard file with handlers', () => {
783
+ const analyses = [
784
+ createAnalysis({
785
+ route: '/api/users',
786
+ method: 'GET',
787
+ exportName: 'usersEndpoint',
788
+ tier: 'standard',
789
+ features: createFeatures({ hasAuth: true }),
790
+ }),
791
+ ];
792
+
793
+ const endpointImports = [
794
+ { exportName: 'usersEndpoint', importPath: '../endpoints/users' },
795
+ ];
796
+
797
+ const files = generateEndpointFilesByTier(analyses, endpointImports);
798
+
799
+ expect(files['standard.ts']).toContain('Standard-tier endpoint handlers');
800
+ expect(files['standard.ts']).toContain('1 endpoints');
801
+ expect(files['standard.ts']).toContain('export function setupStandardEndpoints');
802
+ expect(files['standard.ts']).toContain("import { usersEndpoint }");
803
+ });
804
+
805
+ it('should generate standard file with events import when needed', () => {
806
+ const analyses = [
807
+ createAnalysis({
808
+ route: '/api/orders',
809
+ method: 'POST',
810
+ exportName: 'createOrderEndpoint',
811
+ tier: 'standard',
812
+ features: createFeatures({ hasEvents: true }),
813
+ }),
814
+ ];
815
+
816
+ const endpointImports = [
817
+ { exportName: 'createOrderEndpoint', importPath: '../endpoints/orders' },
818
+ ];
819
+
820
+ const files = generateEndpointFilesByTier(analyses, endpointImports);
821
+
822
+ expect(files['standard.ts']).toContain('import { publishConstructEvents }');
823
+ });
824
+
825
+ it('should generate full file with HonoEndpoint', () => {
826
+ const analyses = [
827
+ createAnalysis({
828
+ route: '/api/audit',
829
+ method: 'POST',
830
+ exportName: 'auditEndpoint',
831
+ tier: 'full',
832
+ features: createFeatures({ hasAudits: true }),
833
+ }),
834
+ ];
835
+
836
+ const endpointImports = [
837
+ { exportName: 'auditEndpoint', importPath: '../endpoints/audit' },
838
+ ];
839
+
840
+ const files = generateEndpointFilesByTier(analyses, endpointImports);
841
+
842
+ expect(files['full.ts']).toContain('Full-tier endpoint handlers');
843
+ expect(files['full.ts']).toContain('import { HonoEndpoint }');
844
+ expect(files['full.ts']).toContain('HonoEndpoint.addRoutes');
845
+ expect(files['full.ts']).toContain("import { auditEndpoint }");
846
+ });
847
+
848
+ it('should generate empty full file when no full endpoints', () => {
849
+ const analyses = [
850
+ createAnalysis({
851
+ route: '/health',
852
+ method: 'GET',
853
+ exportName: 'healthEndpoint',
854
+ tier: 'minimal',
855
+ }),
856
+ ];
857
+
858
+ const endpointImports = [
859
+ { exportName: 'healthEndpoint', importPath: '../endpoints/health' },
860
+ ];
861
+
862
+ const files = generateEndpointFilesByTier(analyses, endpointImports);
863
+
864
+ expect(files['full.ts']).toContain('No full-tier endpoints');
865
+ });
866
+
867
+ it('should generate index file with correct counts', () => {
868
+ const analyses = [
869
+ createAnalysis({
870
+ route: '/health',
871
+ method: 'GET',
872
+ exportName: 'healthEndpoint',
873
+ tier: 'minimal',
874
+ }),
875
+ createAnalysis({
876
+ route: '/api/users',
877
+ method: 'GET',
878
+ exportName: 'usersEndpoint',
879
+ tier: 'standard',
880
+ features: createFeatures({ hasAuth: true }),
881
+ }),
882
+ createAnalysis({
883
+ route: '/api/audit',
884
+ method: 'POST',
885
+ exportName: 'auditEndpoint',
886
+ tier: 'full',
887
+ features: createFeatures({ hasAudits: true }),
888
+ }),
889
+ ];
890
+
891
+ const endpointImports = [
892
+ { exportName: 'healthEndpoint', importPath: '../endpoints/health' },
893
+ { exportName: 'usersEndpoint', importPath: '../endpoints/users' },
894
+ { exportName: 'auditEndpoint', importPath: '../endpoints/audit' },
895
+ ];
896
+
897
+ const files = generateEndpointFilesByTier(analyses, endpointImports);
898
+
899
+ expect(files['index.ts']).toContain('minimal: 1 endpoints');
900
+ expect(files['index.ts']).toContain('standard: 1 endpoints');
901
+ expect(files['index.ts']).toContain('full: 1 endpoints');
902
+ expect(files['index.ts']).toContain("import { setupMinimalEndpoints }");
903
+ expect(files['index.ts']).toContain("import { setupStandardEndpoints }");
904
+ expect(files['index.ts']).toContain("import { setupFullEndpoints }");
905
+ expect(files['index.ts']).toContain('export async function setupEndpoints');
906
+ });
907
+
908
+ it('should include validator imports in minimal file when needed', () => {
909
+ const analyses = [
910
+ createAnalysis({
911
+ route: '/api/users',
912
+ method: 'POST',
913
+ exportName: 'createUserEndpoint',
914
+ tier: 'minimal',
915
+ features: createFeatures({ hasBodyValidation: true }),
916
+ }),
917
+ ];
918
+
919
+ const endpointImports = [
920
+ { exportName: 'createUserEndpoint', importPath: '../endpoints/users' },
921
+ ];
922
+
923
+ const files = generateEndpointFilesByTier(analyses, endpointImports);
924
+
925
+ expect(files['minimal.ts']).toContain("import { validateBody } from './validators.js'");
926
+ });
927
+
928
+ it('should include validator imports in standard file when needed', () => {
929
+ const analyses = [
930
+ createAnalysis({
931
+ route: '/api/users',
932
+ method: 'POST',
933
+ exportName: 'createUserEndpoint',
934
+ tier: 'standard',
935
+ features: createFeatures({ hasAuth: true, hasBodyValidation: true }),
936
+ }),
937
+ ];
938
+
939
+ const endpointImports = [
940
+ { exportName: 'createUserEndpoint', importPath: '../endpoints/users' },
941
+ ];
942
+
943
+ const files = generateEndpointFilesByTier(analyses, endpointImports);
944
+
945
+ expect(files['standard.ts']).toContain("import { validateBody } from './validators.js'");
946
+ });
947
+ });
948
+
949
+ describe('generateEndpointFilesNested', () => {
950
+ it('should generate base files', () => {
951
+ const analyses = [
952
+ createAnalysis({
953
+ route: '/health',
954
+ method: 'GET',
955
+ exportName: 'healthEndpoint',
956
+ tier: 'minimal',
957
+ }),
958
+ ];
959
+
960
+ const endpointImports = [
961
+ { exportName: 'healthEndpoint', importPath: '../../endpoints/health' },
962
+ ];
963
+
964
+ const files = generateEndpointFilesNested(analyses, endpointImports);
965
+
966
+ expect(files).toHaveProperty('validators.ts');
967
+ expect(files).toHaveProperty('minimal/index.ts');
968
+ expect(files).toHaveProperty('standard/index.ts');
969
+ expect(files).toHaveProperty('full/index.ts');
970
+ expect(files).toHaveProperty('index.ts');
971
+ });
972
+
973
+ it('should generate individual endpoint files for minimal tier', () => {
974
+ const analyses = [
975
+ createAnalysis({
976
+ route: '/health',
977
+ method: 'GET',
978
+ exportName: 'healthEndpoint',
979
+ tier: 'minimal',
980
+ }),
981
+ ];
982
+
983
+ const endpointImports = [
984
+ { exportName: 'healthEndpoint', importPath: '../../endpoints/health' },
985
+ ];
986
+
987
+ const files = generateEndpointFilesNested(analyses, endpointImports);
988
+
989
+ expect(files).toHaveProperty('minimal/healthEndpoint.ts');
990
+ expect(files['minimal/healthEndpoint.ts']).toContain('Minimal endpoint: /health (GET)');
991
+ expect(files['minimal/healthEndpoint.ts']).toContain('export function setupHealthEndpoint');
992
+ expect(files['minimal/healthEndpoint.ts']).toContain("import { healthEndpoint }");
993
+ });
994
+
995
+ it('should generate individual endpoint files for standard tier', () => {
996
+ const analyses = [
997
+ createAnalysis({
998
+ route: '/api/users',
999
+ method: 'GET',
1000
+ exportName: 'usersEndpoint',
1001
+ tier: 'standard',
1002
+ features: createFeatures({ hasAuth: true }),
1003
+ }),
1004
+ ];
1005
+
1006
+ const endpointImports = [
1007
+ { exportName: 'usersEndpoint', importPath: '../../endpoints/users' },
1008
+ ];
1009
+
1010
+ const files = generateEndpointFilesNested(analyses, endpointImports);
1011
+
1012
+ expect(files).toHaveProperty('standard/usersEndpoint.ts');
1013
+ expect(files['standard/usersEndpoint.ts']).toContain('Standard endpoint: /api/users (GET)');
1014
+ expect(files['standard/usersEndpoint.ts']).toContain('export function setupUsersEndpoint');
1015
+ expect(files['standard/usersEndpoint.ts']).toContain("import { usersEndpoint }");
1016
+ });
1017
+
1018
+ it('should generate individual endpoint files for full tier', () => {
1019
+ const analyses = [
1020
+ createAnalysis({
1021
+ route: '/api/audit',
1022
+ method: 'POST',
1023
+ exportName: 'auditEndpoint',
1024
+ tier: 'full',
1025
+ features: createFeatures({ hasAudits: true }),
1026
+ }),
1027
+ ];
1028
+
1029
+ const endpointImports = [
1030
+ { exportName: 'auditEndpoint', importPath: '../../endpoints/audit' },
1031
+ ];
1032
+
1033
+ const files = generateEndpointFilesNested(analyses, endpointImports);
1034
+
1035
+ expect(files).toHaveProperty('full/auditEndpoint.ts');
1036
+ expect(files['full/auditEndpoint.ts']).toContain('Full endpoint: /api/audit (POST)');
1037
+ expect(files['full/auditEndpoint.ts']).toContain('export function setupAuditEndpoint');
1038
+ expect(files['full/auditEndpoint.ts']).toContain('import { HonoEndpoint }');
1039
+ });
1040
+
1041
+ it('should generate tier index files that import individual endpoints', () => {
1042
+ const analyses = [
1043
+ createAnalysis({
1044
+ route: '/health',
1045
+ method: 'GET',
1046
+ exportName: 'healthEndpoint',
1047
+ tier: 'minimal',
1048
+ }),
1049
+ createAnalysis({
1050
+ route: '/ready',
1051
+ method: 'GET',
1052
+ exportName: 'readyEndpoint',
1053
+ tier: 'minimal',
1054
+ }),
1055
+ ];
1056
+
1057
+ const endpointImports = [
1058
+ { exportName: 'healthEndpoint', importPath: '../../endpoints/health' },
1059
+ { exportName: 'readyEndpoint', importPath: '../../endpoints/ready' },
1060
+ ];
1061
+
1062
+ const files = generateEndpointFilesNested(analyses, endpointImports);
1063
+
1064
+ expect(files['minimal/index.ts']).toContain("import { setupHealthEndpoint } from './healthEndpoint.js'");
1065
+ expect(files['minimal/index.ts']).toContain("import { setupReadyEndpoint } from './readyEndpoint.js'");
1066
+ expect(files['minimal/index.ts']).toContain('setupHealthEndpoint(app, logger)');
1067
+ expect(files['minimal/index.ts']).toContain('setupReadyEndpoint(app, logger)');
1068
+ });
1069
+
1070
+ it('should generate empty tier index for tiers with no endpoints', () => {
1071
+ const analyses = [
1072
+ createAnalysis({
1073
+ route: '/health',
1074
+ method: 'GET',
1075
+ exportName: 'healthEndpoint',
1076
+ tier: 'minimal',
1077
+ }),
1078
+ ];
1079
+
1080
+ const endpointImports = [
1081
+ { exportName: 'healthEndpoint', importPath: '../../endpoints/health' },
1082
+ ];
1083
+
1084
+ const files = generateEndpointFilesNested(analyses, endpointImports);
1085
+
1086
+ expect(files['standard/index.ts']).toContain('No standard-tier endpoints');
1087
+ expect(files['full/index.ts']).toContain('No full-tier endpoints');
1088
+ });
1089
+
1090
+ it('should generate nested index file with correct imports', () => {
1091
+ const analyses = [
1092
+ createAnalysis({
1093
+ route: '/health',
1094
+ method: 'GET',
1095
+ exportName: 'healthEndpoint',
1096
+ tier: 'minimal',
1097
+ }),
1098
+ ];
1099
+
1100
+ const endpointImports = [
1101
+ { exportName: 'healthEndpoint', importPath: '../../endpoints/health' },
1102
+ ];
1103
+
1104
+ const files = generateEndpointFilesNested(analyses, endpointImports);
1105
+
1106
+ expect(files['index.ts']).toContain("import { setupMinimalEndpoints } from './minimal/index.js'");
1107
+ expect(files['index.ts']).toContain("import { setupStandardEndpoints } from './standard/index.js'");
1108
+ expect(files['index.ts']).toContain("import { setupFullEndpoints } from './full/index.js'");
1109
+ });
1110
+
1111
+ it('should include validator imports in individual endpoint files when needed', () => {
1112
+ const analyses = [
1113
+ createAnalysis({
1114
+ route: '/api/users',
1115
+ method: 'POST',
1116
+ exportName: 'createUserEndpoint',
1117
+ tier: 'minimal',
1118
+ features: createFeatures({ hasBodyValidation: true }),
1119
+ }),
1120
+ ];
1121
+
1122
+ const endpointImports = [
1123
+ { exportName: 'createUserEndpoint', importPath: '../../endpoints/users' },
1124
+ ];
1125
+
1126
+ const files = generateEndpointFilesNested(analyses, endpointImports);
1127
+
1128
+ expect(files['minimal/createUserEndpoint.ts']).toContain("import { validateBody } from '../validators.js'");
1129
+ });
1130
+
1131
+ it('should include events import in standard endpoint files when needed', () => {
1132
+ const analyses = [
1133
+ createAnalysis({
1134
+ route: '/api/orders',
1135
+ method: 'POST',
1136
+ exportName: 'createOrderEndpoint',
1137
+ tier: 'standard',
1138
+ features: createFeatures({ hasEvents: true }),
1139
+ }),
1140
+ ];
1141
+
1142
+ const endpointImports = [
1143
+ { exportName: 'createOrderEndpoint', importPath: '../../endpoints/orders' },
1144
+ ];
1145
+
1146
+ const files = generateEndpointFilesNested(analyses, endpointImports);
1147
+
1148
+ expect(files['standard/createOrderEndpoint.ts']).toContain('import { publishConstructEvents }');
1149
+ });
1150
+
1151
+ it('should skip endpoints without matching imports', () => {
1152
+ const analyses = [
1153
+ createAnalysis({
1154
+ route: '/health',
1155
+ method: 'GET',
1156
+ exportName: 'healthEndpoint',
1157
+ tier: 'minimal',
1158
+ }),
1159
+ createAnalysis({
1160
+ route: '/unknown',
1161
+ method: 'GET',
1162
+ exportName: 'unknownEndpoint',
1163
+ tier: 'minimal',
1164
+ }),
1165
+ ];
1166
+
1167
+ // Only provide import for healthEndpoint
1168
+ const endpointImports = [
1169
+ { exportName: 'healthEndpoint', importPath: '../../endpoints/health' },
1170
+ ];
1171
+
1172
+ const files = generateEndpointFilesNested(analyses, endpointImports);
1173
+
1174
+ expect(files).toHaveProperty('minimal/healthEndpoint.ts');
1175
+ expect(files).not.toHaveProperty('minimal/unknownEndpoint.ts');
1176
+ });
1177
+
1178
+ it('should generate standard tier index with correct function calls', () => {
1179
+ const analyses = [
1180
+ createAnalysis({
1181
+ route: '/api/users',
1182
+ method: 'GET',
1183
+ exportName: 'usersEndpoint',
1184
+ tier: 'standard',
1185
+ features: createFeatures({ hasAuth: true }),
1186
+ }),
1187
+ ];
1188
+
1189
+ const endpointImports = [
1190
+ { exportName: 'usersEndpoint', importPath: '../../endpoints/users' },
1191
+ ];
1192
+
1193
+ const files = generateEndpointFilesNested(analyses, endpointImports);
1194
+
1195
+ expect(files['standard/index.ts']).toContain('setupUsersEndpoint(app, serviceDiscovery, logger)');
1196
+ });
1197
+
1198
+ it('should generate full tier index with correct function calls', () => {
1199
+ const analyses = [
1200
+ createAnalysis({
1201
+ route: '/api/audit',
1202
+ method: 'POST',
1203
+ exportName: 'auditEndpoint',
1204
+ tier: 'full',
1205
+ features: createFeatures({ hasAudits: true }),
1206
+ }),
1207
+ ];
1208
+
1209
+ const endpointImports = [
1210
+ { exportName: 'auditEndpoint', importPath: '../../endpoints/audit' },
1211
+ ];
1212
+
1213
+ const files = generateEndpointFilesNested(analyses, endpointImports);
1214
+
1215
+ expect(files['full/index.ts']).toContain('setupAuditEndpoint(app, serviceDiscovery, openApiOptions)');
1216
+ expect(files['full/index.ts']).toContain('const openApiOptions');
1217
+ });
1218
+ });
272
1219
  });