@xeno-js/cli 0.1.2 → 0.1.4

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 (88) hide show
  1. package/LICENSE +18 -12
  2. package/README.md +461 -236
  3. package/dist/index.js +1018 -688
  4. package/dist/index.js.map +1 -1
  5. package/package.json +91 -66
  6. package/.github/.copilot-instruction.md +0 -73
  7. package/.github/agents/git-operator.agent.md +0 -86
  8. package/.github/agents/unit-tester.agent.md +0 -68
  9. package/.husky/commit-msg +0 -1
  10. package/.husky/pre-commit +0 -1
  11. package/.husky/pre-push +0 -1
  12. package/.prettierignore +0 -7
  13. package/.prettierrc.json +0 -37
  14. package/commitlint.config.cjs +0 -39
  15. package/eslint.config.mjs +0 -306
  16. package/lint-staged.config.mjs +0 -5
  17. package/logo/logo.png +0 -0
  18. package/src/domain/contracts/icommand-cli.contracts.ts +0 -13
  19. package/src/domain/contracts/icommand-runner.contracts.ts +0 -3
  20. package/src/domain/contracts/idispatch.contracts.ts +0 -3
  21. package/src/domain/contracts/ifile-service.contracts.ts +0 -3
  22. package/src/domain/contracts/igenerator.contracts.ts +0 -5
  23. package/src/domain/contracts/index.ts +0 -6
  24. package/src/domain/contracts/iscaffold-strategy.contracts.ts +0 -7
  25. package/src/domain/index.ts +0 -1
  26. package/src/index.ts +0 -30
  27. package/src/infrastructure/bootstrap/bootstrapper.ts +0 -30
  28. package/src/infrastructure/bootstrap/index.ts +0 -1
  29. package/src/infrastructure/builder/builder.test.ts +0 -36
  30. package/src/infrastructure/builder/builder.ts +0 -15
  31. package/src/infrastructure/builder/index.ts +0 -1
  32. package/src/infrastructure/command_launcher/command.utils.ts +0 -31
  33. package/src/infrastructure/command_launcher/index.ts +0 -0
  34. package/src/infrastructure/commands/command-runner.service.ts +0 -36
  35. package/src/infrastructure/commands/generate.command.ts +0 -55
  36. package/src/infrastructure/commands/help.command.ts +0 -63
  37. package/src/infrastructure/commands/index.ts +0 -4
  38. package/src/infrastructure/commands/new-project.command.ts +0 -47
  39. package/src/infrastructure/dispatcher/dispatcher.ts +0 -45
  40. package/src/infrastructure/dispatcher/index.ts +0 -1
  41. package/src/infrastructure/file/file.service.ts +0 -14
  42. package/src/infrastructure/file/index.ts +0 -1
  43. package/src/infrastructure/generators/core/bootstrap.generator.ts +0 -118
  44. package/src/infrastructure/generators/core/command/cqrs.generator.ts +0 -63
  45. package/src/infrastructure/generators/core/command/generate-command.generator.ts +0 -191
  46. package/src/infrastructure/generators/core/command/generate-query.generator.ts +0 -150
  47. package/src/infrastructure/generators/core/drizzle-sql-lite.generator.ts +0 -76
  48. package/src/infrastructure/generators/core/drizzle.generator.ts +0 -86
  49. package/src/infrastructure/generators/core/env.generator.ts +0 -69
  50. package/src/infrastructure/generators/core/index.ts +0 -9
  51. package/src/infrastructure/generators/core/main.generator.ts +0 -41
  52. package/src/infrastructure/generators/core/packagejson.generator.ts +0 -70
  53. package/src/infrastructure/generators/core/readme.generator.ts +0 -427
  54. package/src/infrastructure/generators/core/registry.generator.ts +0 -40
  55. package/src/infrastructure/generators/core/tsconfig.generator.ts +0 -39
  56. package/src/infrastructure/generators/gitignore.generator.ts +0 -60
  57. package/src/infrastructure/generators/vue/app-vue.generator.ts +0 -21
  58. package/src/infrastructure/generators/vue/bootstrap-ts.generator.ts +0 -60
  59. package/src/infrastructure/generators/vue/command_query/cqrs.generator.ts +0 -36
  60. package/src/infrastructure/generators/vue/command_query/generate-command-vue.generator.ts +0 -153
  61. package/src/infrastructure/generators/vue/command_query/generate-query-vue.generator.ts +0 -161
  62. package/src/infrastructure/generators/vue/env.generator.ts +0 -39
  63. package/src/infrastructure/generators/vue/index-html.generator.ts +0 -24
  64. package/src/infrastructure/generators/vue/index.ts +0 -12
  65. package/src/infrastructure/generators/vue/main.generator.ts +0 -41
  66. package/src/infrastructure/generators/vue/packagejson.generator.ts +0 -50
  67. package/src/infrastructure/generators/vue/readme.generator.ts +0 -389
  68. package/src/infrastructure/generators/vue/registry-ts.generator.ts +0 -26
  69. package/src/infrastructure/generators/vue/router.generator.ts +0 -35
  70. package/src/infrastructure/generators/vue/tailwind.generator.ts +0 -17
  71. package/src/infrastructure/generators/vue/tsconfig.generator.ts +0 -38
  72. package/src/infrastructure/generators/vue/use-app.generator.ts +0 -38
  73. package/src/infrastructure/generators/vue/vite-config.generator.ts +0 -35
  74. package/src/infrastructure/index.ts +0 -5
  75. package/src/infrastructure/strategies/core-scaffold.strategy.ts +0 -100
  76. package/src/infrastructure/strategies/index.ts +0 -2
  77. package/src/infrastructure/strategies/vue-scaffold.strategy.ts +0 -79
  78. package/src/shared/constants/command.constants.ts +0 -18
  79. package/src/shared/constants/core.constants.ts +0 -7
  80. package/src/shared/constants/index.ts +0 -2
  81. package/src/shared/index.ts +0 -3
  82. package/src/shared/types/common.types.ts +0 -68
  83. package/src/shared/types/index.ts +0 -1
  84. package/src/shared/utils/guards.utils.ts +0 -334
  85. package/src/shared/utils/index.ts +0 -2
  86. package/src/shared/utils/string.utils.ts +0 -60
  87. package/tsconfig.json +0 -15
  88. package/tsup.config.ts +0 -13
package/dist/index.js CHANGED
@@ -474,20 +474,27 @@ var init_generate_command_generator = __esm({
474
474
  }
475
475
  _fileService;
476
476
  async generate(pascalName, lowerName, tokenPrefix, componentDir, hasZod, hasDrizzle) {
477
+ const appDir = import_node_path.default.join(componentDir, "application");
478
+ const infraDir = import_node_path.default.join(componentDir, "infrastructure");
479
+ const domainDir = import_node_path.default.join(componentDir, "domain");
480
+ const presDir = import_node_path.default.join(componentDir, "presentation");
477
481
  const commandContent = `import { Command } from '@xeno-js/core';
482
+ export interface ${pascalName}Payload {
483
+ // TODO: Define your command payload properties here
484
+ }
478
485
 
479
486
  /*
480
487
  * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
481
- * Please replace <any> with your specific payload and response types.
488
+ * Please replace <void> with your specific payload and response types.
482
489
  */
483
- export class ${pascalName}Command extends Command<any> {
484
- constructor(public readonly payload: any) {
490
+ export class ${pascalName}Command extends Command<void> {
491
+ constructor(public readonly payload: ${pascalName}Payload) {
485
492
  super('${tokenPrefix}_COMMAND_HANDLER');
486
493
  }
487
494
  }
488
495
  `;
489
496
  await this._fileService.writeFileRecursive(
490
- import_node_path.default.join(componentDir, `${lowerName}.command.ts`),
497
+ import_node_path.default.join(appDir, `${lowerName}.command.ts`),
491
498
  commandContent
492
499
  );
493
500
  const handlerContent = `import type { IFactory, ResultType, UserContext } from '@xeno-js/core';
@@ -496,16 +503,16 @@ import { ${pascalName}Command } from './${lowerName}.command';
496
503
 
497
504
  /*
498
505
  * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
499
- * Please replace <any> with your specific response type.
506
+ * Please replace <void> with your specific response type.
500
507
  */
501
- export class ${pascalName}Handler extends BaseHandler<${pascalName}Command, any> {
508
+ export class ${pascalName}Handler extends BaseHandler<${pascalName}Command, void> {
502
509
  constructor(
503
510
  identityFactory: IFactory<void, UserContext>
504
511
  ) {
505
512
  super(identityFactory);
506
513
  }
507
514
 
508
- public async executeAsync(request: ${pascalName}Command, singal: AbortSignal): Promise<ResultType<any>> {
515
+ protected async executeAsync(request: ${pascalName}Command, singal: AbortSignal): Promise<ResultType<void>> {
509
516
  // TODO: Implement your business logic here
510
517
 
511
518
  return Result.ok();
@@ -513,18 +520,18 @@ export class ${pascalName}Handler extends BaseHandler<${pascalName}Command, any>
513
520
  }
514
521
  `;
515
522
  await this._fileService.writeFileRecursive(
516
- import_node_path.default.join(componentDir, `${lowerName}.handler.ts`),
523
+ import_node_path.default.join(appDir, `${lowerName}.handler.ts`),
517
524
  handlerContent
518
525
  );
519
526
  const controllerContent = `import type { IContextAccessor, IMediator, RequestContext, ResponseDto } from '@xeno-js/core';
520
527
  import { BaseController } from '@xeno-js/core';
521
- import { ${pascalName}Command } from './${lowerName}.command';
528
+ import { ${pascalName}Command, ${pascalName}Payload } from '../application/${lowerName}.command';
522
529
 
523
530
  /*
524
531
  * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
525
- * Please replace <any, any> with your specific request and response types.
532
+ * Please replace <{pascalName}Payload, void> with your specific request and response types.
526
533
  */
527
- export class ${pascalName}Controller extends BaseController<any, any> {
534
+ export class ${pascalName}Controller extends BaseController<${pascalName}Payload, void> {
528
535
  constructor(
529
536
  requestContext: IContextAccessor<RequestContext>,
530
537
  mediator: IMediator,
@@ -532,7 +539,7 @@ export class ${pascalName}Controller extends BaseController<any, any> {
532
539
  super(requestContext, mediator);
533
540
  }
534
541
 
535
- public async handle(request: any): Promise<ResponseDto<any>> {
542
+ public async handle(request: {pascalName}Payload): Promise<ResponseDto<void>> {
536
543
  const cmd = new ${pascalName}Command(request);
537
544
  const result = await this._send(cmd);
538
545
 
@@ -545,7 +552,7 @@ export class ${pascalName}Controller extends BaseController<any, any> {
545
552
  }
546
553
  `;
547
554
  await this._fileService.writeFileRecursive(
548
- import_node_path.default.join(componentDir, `${lowerName}.controller.ts`),
555
+ import_node_path.default.join(presDir, `${lowerName}.controller.ts`),
549
556
  controllerContent
550
557
  );
551
558
  const entityContent = `import { Entity } from '@xeno-js/core';
@@ -569,23 +576,27 @@ export class ${pascalName} extends Entity<${pascalName}Props> {
569
576
  }
570
577
  `;
571
578
  await this._fileService.writeFileRecursive(
572
- import_node_path.default.join(componentDir, `${lowerName}.entity.ts`),
579
+ import_node_path.default.join(domainDir, `${lowerName}.entity.ts`),
573
580
  entityContent
574
581
  );
575
582
  if (hasZod) {
576
583
  const zodContent = `import { z } from 'zod';
577
584
  import { ZodUtils } from '@xeno-js/core';
578
585
 
579
- /*
586
+ /**
580
587
  * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
581
588
  * Define the actual Zod validation schema for your command payload.
582
589
  */
583
590
  export const ${pascalName}Schema = ZodUtils.createCommandSchema({
584
- payload: z.any() // TODO: Update with strict validation
591
+ payload: z.object({
592
+ // TODO: Define strict validation rules here, e.g.:
593
+ // email: z.string().email(),
594
+ // name: z.string().min(1),
595
+ }),
585
596
  });
586
597
  `;
587
598
  await this._fileService.writeFileRecursive(
588
- import_node_path.default.join(componentDir, `${lowerName}.schema-zod.ts`),
599
+ import_node_path.default.join(infraDir, `${lowerName}.schema-zod.ts`),
589
600
  zodContent
590
601
  );
591
602
  }
@@ -607,14 +618,14 @@ export type ${pascalName}Dto = typeof ${lowerName}s.$inferSelect;
607
618
  export type New${pascalName}Dto = typeof ${lowerName}s.$inferInsert;
608
619
  `;
609
620
  await this._fileService.writeFileRecursive(
610
- import_node_path.default.join(componentDir, `${lowerName}.schema-db.ts`),
621
+ import_node_path.default.join(infraDir, `${lowerName}.schema-db.ts`),
611
622
  dbContent
612
623
  );
613
624
  }
614
625
  const moduleContent = `import type { IServiceContainer, IConfigurationService } from '@xeno-js/core';
615
626
  import { TOKENS } from '@xeno-js/core';
616
- import { ${pascalName}Controller } from './${lowerName}.controller';
617
- import { ${pascalName}Handler } from './${lowerName}.handler';
627
+ import { ${pascalName}Controller } from './presentation/${lowerName}.controller';
628
+ import { ${pascalName}Handler } from './application/${lowerName}.handler';
618
629
 
619
630
  export const ${pascalName}Module = Object.freeze({
620
631
  register(opts: IServiceContainer<any>, _config: IConfigurationService): void {
@@ -656,14 +667,21 @@ var init_generate_query_generator = __esm({
656
667
  }
657
668
  _fileService;
658
669
  async generate(pascalName, lowerName, tokenPrefix, componentDir, hasZod) {
670
+ const appDir = import_node_path2.default.join(componentDir, "application");
671
+ const infraDir = import_node_path2.default.join(componentDir, "infrastructure");
672
+ const domainDir = import_node_path2.default.join(componentDir, "domain");
673
+ const presDir = import_node_path2.default.join(componentDir, "presentation");
659
674
  const queryContent = `import { BaseQuery } from '@xeno-js/core';
675
+ export interface ${pascalName}Payload {
676
+ // TODO: Define your command payload properties here
677
+ }
660
678
 
661
679
  /*
662
680
  * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
663
- * Please replace <any> with your specific payload and response types.
681
+ * Please replace <void> with your specific payload and response types.
664
682
  */
665
- export class ${pascalName}Query extends BaseQuery<any> {
666
- constructor(public readonly payload: any) {
683
+ export class ${pascalName}Query extends BaseQuery<void> {
684
+ constructor(public readonly payload: ${pascalName}Payload) {
667
685
  super(
668
686
  '${tokenPrefix}_QUERY_HANDLER',
669
687
  // Default cache options:
@@ -679,7 +697,7 @@ export class ${pascalName}Query extends BaseQuery<any> {
679
697
  }
680
698
  `;
681
699
  await this._fileService.writeFileRecursive(
682
- import_node_path2.default.join(componentDir, `${lowerName}.query.ts`),
700
+ import_node_path2.default.join(appDir, `${lowerName}.query.ts`),
683
701
  queryContent
684
702
  );
685
703
  const handlerContent = `import type { IFactory, ResultType, UserContext } from '@xeno-js/core';
@@ -688,9 +706,9 @@ import { ${pascalName}Query } from './${lowerName}.query';
688
706
 
689
707
  /*
690
708
  * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
691
- * Please replace <any> with your specific response type.
709
+ * Please replace <void> with your specific response type.
692
710
  */
693
- export class ${pascalName}Handler extends BaseHandler<${pascalName}Query, any> {
711
+ export class ${pascalName}Handler extends BaseHandler<${pascalName}Query, void> {
694
712
  constructor(
695
713
  identityFactory: IFactory<void, UserContext>
696
714
  // TODO: Inject your ReadDao or DataSource here
@@ -698,7 +716,7 @@ export class ${pascalName}Handler extends BaseHandler<${pascalName}Query, any> {
698
716
  super(identityFactory);
699
717
  }
700
718
 
701
- public async executeAsync(request: ${pascalName}Query, singal: AbortSignal): Promise<ResultType<any>> {
719
+ protected async executeAsync(request: ${pascalName}Query, singal: AbortSignal): Promise<ResultType<void>> {
702
720
  // TODO: Implement your query logic here
703
721
 
704
722
  return Result.ok();
@@ -706,18 +724,18 @@ export class ${pascalName}Handler extends BaseHandler<${pascalName}Query, any> {
706
724
  }
707
725
  `;
708
726
  await this._fileService.writeFileRecursive(
709
- import_node_path2.default.join(componentDir, `${lowerName}.handler.ts`),
727
+ import_node_path2.default.join(appDir, `${lowerName}.handler.ts`),
710
728
  handlerContent
711
729
  );
712
730
  const controllerContent = `import type { IContextAccessor, IMediator, RequestContext, ResponseDto } from '@xeno-js/core';
713
731
  import { BaseController } from '@xeno-js/core';
714
- import { ${pascalName}Query } from './${lowerName}.query';
732
+ import { ${pascalName}Query, ${pascalName}Payload } from '../presentation/${lowerName}.query';
715
733
 
716
734
  /*
717
735
  * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
718
- * Please replace <any, any> with your specific request and response types.
736
+ * Please replace <${pascalName}Payload, void> with your specific request and response types.
719
737
  */
720
- export class ${pascalName}Controller extends BaseController<any, any> {
738
+ export class ${pascalName}Controller extends BaseController<${pascalName}Payload, void> {
721
739
  constructor(
722
740
  requestContext: IContextAccessor<RequestContext>,
723
741
  mediator: IMediator,
@@ -725,7 +743,7 @@ export class ${pascalName}Controller extends BaseController<any, any> {
725
743
  super(requestContext, mediator);
726
744
  }
727
745
 
728
- public async handle(request: any): Promise<ResponseDto<any>> {
746
+ public async handle(request: ${pascalName}Payload): Promise<ResponseDto<void>> {
729
747
  const query = new ${pascalName}Query(request);
730
748
  const result = await this._query(query);
731
749
 
@@ -738,23 +756,51 @@ export class ${pascalName}Controller extends BaseController<any, any> {
738
756
  }
739
757
  `;
740
758
  await this._fileService.writeFileRecursive(
741
- import_node_path2.default.join(componentDir, `${lowerName}.controller.ts`),
759
+ import_node_path2.default.join(presDir, `${lowerName}.controller.ts`),
742
760
  controllerContent
743
761
  );
762
+ const entityContent = `import { Entity } from '@xeno-js/core';
763
+
764
+ export interface ${pascalName}Props {
765
+ // TODO: Define your entity properties
766
+ }
767
+
768
+ /*
769
+ * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
770
+ * Adjust the properties and types according to your domain logic.
771
+ */
772
+ export class ${pascalName} extends Entity<${pascalName}Props> {
773
+ private constructor(props: ${pascalName}Props, id?: string) {
774
+ super(props, id);
775
+ }
776
+
777
+ static create(props: ${pascalName}Props, id?: string): ${pascalName} {
778
+ return new ${pascalName}(props, id);
779
+ }
780
+ }
781
+ `;
782
+ await this._fileService.writeFileRecursive(
783
+ import_node_path2.default.join(domainDir, `${lowerName}.entity.ts`),
784
+ entityContent
785
+ );
744
786
  if (hasZod) {
745
787
  const zodContent = `import { z } from 'zod';
746
788
  import { ZodUtils } from '@xeno-js/core';
747
789
 
748
- /*
790
+ /**
749
791
  * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
750
- * Define the actual Zod validation schema for your query payload.
792
+ * Define the actual Zod validation schema for your command payload.
751
793
  */
752
- export const ${pascalName}Schema = ZodUtils.createQuerySchema({
753
- payload: z.any() // TODO: Update with strict validation
794
+ export const ${pascalName}Schema = ZodUtils.createCommandSchema({
795
+ payload: z.object({
796
+ // TODO: Define strict validation rules here, e.g.:
797
+ // email: z.string().email(),
798
+ // name: z.string().min(1),
799
+ }),
754
800
  });
755
801
  `;
756
802
  await this._fileService.writeFileRecursive(
757
- import_node_path2.default.join(componentDir, `${lowerName}.schema-zod.ts`),
803
+ import_node_path2.default.join(infraDir, `${lowerName}.schema-zod.ts`),
758
804
  zodContent
759
805
  );
760
806
  }
@@ -772,8 +818,8 @@ export const ${pascalName}Module = Object.freeze({
772
818
  * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
773
819
  * Please copy and paste the following tokens to your MyRegistry interface (usually in src/registry.ts):
774
820
  *
775
- * ${tokenPrefix}_QUERY_CONTROLLER: IController<any, any>;
776
- * ${tokenPrefix}_QUERY_HANDLER: IHandler<${pascalName}Query, any>;
821
+ * ${tokenPrefix}_QUERY_CONTROLLER: IController<${pascalName}Query, void>;
822
+ * ${tokenPrefix}_QUERY_HANDLER: IHandler<${pascalName}Query, void>;
777
823
  */
778
824
  }
779
825
  } as const);
@@ -866,6 +912,9 @@ var init_generate_command_vue_generator = __esm({
866
912
  }
867
913
  _fileService;
868
914
  async generate(pascalName, lowerName, tokenPrefix, componentDir) {
915
+ const appDir = import_node_path4.default.join(componentDir, "application");
916
+ const domainDir = import_node_path4.default.join(componentDir, "domain");
917
+ const presDir = import_node_path4.default.join(componentDir, "presentation");
869
918
  const commandContent = `import { Command } from '@xeno-js/vue';
870
919
 
871
920
  export class ${pascalName}Command extends Command {
@@ -875,13 +924,13 @@ export class ${pascalName}Command extends Command {
875
924
  }
876
925
  `;
877
926
  await this._fileService.writeFileRecursive(
878
- import_node_path4.default.join(componentDir, `${lowerName}.command.ts`),
927
+ import_node_path4.default.join(appDir, `${lowerName}.command.ts`),
879
928
  commandContent
880
929
  );
881
- const handlerContent = `import type { IHandler, ResultType } from '@xeno-js/vue';
882
- import { AppError, Result } from '@xeno-js/vue';
930
+ const handlerContent = `import type { ResultType } from '@xeno-js/vue';
931
+ import { AppError, BaseHandler, Result } from '@xeno-js/vue';
883
932
  import type { ${pascalName}Command } from './${lowerName}.command';
884
- import type { ${pascalName}Response } from './${lowerName}.model';
933
+ import type { ${pascalName}Response } from '../entity/${lowerName}.model';
885
934
 
886
935
  /*
887
936
  * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
@@ -896,28 +945,26 @@ import type { ${pascalName}Response } from './${lowerName}.model';
896
945
  * register('${tokenPrefix}_HANDLER', new ${pascalName}Handler());
897
946
  * });
898
947
  */
899
- export class ${pascalName}Handler implements IHandler<${pascalName}Command, ${pascalName}Response> {
948
+ export class ${pascalName}Handler extends BaseHandler<${pascalName}Command, ${pascalName}Response> {
900
949
  constructor(
901
950
  // TODO: Inject your remote DataSources or other services here
902
951
  ) {}
903
952
 
904
- public async handle(command: ${pascalName}Command, signal: AbortSignal): Promise<ResultType<${pascalName}Response>> {
905
- AppError.throwIfAborted(signal, this.constructor.name);
906
-
953
+ protected async executeAsync(command: ${pascalName}Command, signal: AbortSignal): Promise<ResultType<${pascalName}Response>> {
907
954
  // TODO: Implement your frontend business logic or API calls here
908
955
  return Result.ok();
909
956
  }
910
957
  }
911
958
  `;
912
959
  await this._fileService.writeFileRecursive(
913
- import_node_path4.default.join(componentDir, `${lowerName}.handler.ts`),
960
+ import_node_path4.default.join(appDir, `${lowerName}.handler.ts`),
914
961
  handlerContent
915
962
  );
916
963
  const composableContent = `import { ref } from 'vue';
917
964
  import { AppError, Result, type ApiResponseDto, type ResultType } from '@xeno-js/vue';
918
965
  import { ServicesUtils } from '@/use-app';
919
- import { ${pascalName}Command } from './${lowerName}.command';
920
- import type { ${pascalName}Request, ${pascalName}Response } from './${lowerName}.model';
966
+ import { ${pascalName}Command } from '../application/${lowerName}.command';
967
+ import type { ${pascalName}Request, ${pascalName}Response } from '../domain/${lowerName}.model';
921
968
 
922
969
  /*
923
970
  * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
@@ -984,7 +1031,7 @@ export function use${pascalName}() {
984
1031
  }
985
1032
  `;
986
1033
  await this._fileService.writeFileRecursive(
987
- import_node_path4.default.join(componentDir, `use-${lowerName}.composable.ts`),
1034
+ import_node_path4.default.join(presDir, `use-${lowerName}.composable.ts`),
988
1035
  composableContent
989
1036
  );
990
1037
  const modelContent = `export interface ${pascalName}Request {
@@ -996,7 +1043,7 @@ export interface ${pascalName}Response {
996
1043
  }
997
1044
  `;
998
1045
  await this._fileService.writeFileRecursive(
999
- import_node_path4.default.join(componentDir, `${lowerName}.model.ts`),
1046
+ import_node_path4.default.join(domainDir, `${lowerName}.model.ts`),
1000
1047
  modelContent
1001
1048
  );
1002
1049
  }
@@ -1020,6 +1067,9 @@ var init_generate_query_vue_generator = __esm({
1020
1067
  }
1021
1068
  _fileService;
1022
1069
  async generate(pascalName, lowerName, tokenPrefix, componentDir) {
1070
+ const appDir = import_node_path5.default.join(componentDir, "application");
1071
+ const domainDir = import_node_path5.default.join(componentDir, "domain");
1072
+ const presDir = import_node_path5.default.join(componentDir, "presentation");
1023
1073
  const queryContent = `import { Query } from '@xeno-js/vue';
1024
1074
  import type { ${pascalName}Response } from './${lowerName}.model';
1025
1075
 
@@ -1036,13 +1086,13 @@ export class ${pascalName}Query extends Query<${pascalName}Response> {
1036
1086
  }
1037
1087
  `;
1038
1088
  await this._fileService.writeFileRecursive(
1039
- import_node_path5.default.join(componentDir, `${lowerName}.query.ts`),
1089
+ import_node_path5.default.join(appDir, `${lowerName}.query.ts`),
1040
1090
  queryContent
1041
1091
  );
1042
- const handlerContent = `import type { IHandler, ResultType } from '@xeno-js/vue';
1043
- import { AppError, Result } from '@xeno-js/vue';
1092
+ const handlerContent = `import type { ResultType } from '@xeno-js/vue';
1093
+ import { AppError, BaseHandler, Result } from '@xeno-js/vue';
1044
1094
  import type { ${pascalName}Query } from './${lowerName}.query';
1045
- import type { ${pascalName}Response } from './${lowerName}.model';
1095
+ import type { ${pascalName}Response } from '../domain/${lowerName}.model';
1046
1096
 
1047
1097
  /*
1048
1098
  * \u26A0\uFE0F WARNING: ACTION REQUIRED \u26A0\uFE0F
@@ -1057,28 +1107,26 @@ import type { ${pascalName}Response } from './${lowerName}.model';
1057
1107
  * register('${tokenPrefix}_HANDLER', new ${pascalName}Handler());
1058
1108
  * });
1059
1109
  */
1060
- export class ${pascalName}Handler implements IHandler<${pascalName}Query, ${pascalName}Response> {
1110
+ export class ${pascalName}Handler extends BaseHandler<${pascalName}Query, ${pascalName}Response> {
1061
1111
  constructor(
1062
1112
  // TODO: Inject your remote DataSources or other services here
1063
1113
  ) {}
1064
1114
 
1065
- public async handle(query: ${pascalName}Query, signal: AbortSignal): Promise<ResultType<${pascalName}Response>> {
1066
- AppError.throwIfAborted(signal, this.constructor.name);
1067
-
1115
+ protected async executeAsync(query: ${pascalName}Query, signal: AbortSignal): Promise<ResultType<${pascalName}Response>> {
1068
1116
  // TODO: Implement your frontend business logic or API calls here
1069
1117
  return Result.ok();
1070
1118
  }
1071
1119
  }
1072
1120
  `;
1073
1121
  await this._fileService.writeFileRecursive(
1074
- import_node_path5.default.join(componentDir, `${lowerName}.handler.ts`),
1122
+ import_node_path5.default.join(appDir, `${lowerName}.handler.ts`),
1075
1123
  handlerContent
1076
1124
  );
1077
1125
  const composableContent = `import { ref } from 'vue';
1078
1126
  import { AppError, Result, type ApiResponseDto, type ResultType } from '@xeno-js/vue';
1079
1127
  import { ServicesUtils } from '@/use-app';
1080
- import { ${pascalName}Query } from './${lowerName}.query';
1081
- import type { ${pascalName}Response } from './${lowerName}.model';
1128
+ import { ${pascalName}Query } from '../application/${lowerName}.query';
1129
+ import type { ${pascalName}Response } from '../domain/${lowerName}.model';
1082
1130
 
1083
1131
  export function use${pascalName}() {
1084
1132
  const loading = ref(false);
@@ -1149,7 +1197,7 @@ const fetch = async (): Promise<ResultType<${pascalName}Response>> => {
1149
1197
  }
1150
1198
  `;
1151
1199
  await this._fileService.writeFileRecursive(
1152
- import_node_path5.default.join(componentDir, `use-${lowerName}.composable.ts`),
1200
+ import_node_path5.default.join(presDir, `use-${lowerName}.composable.ts`),
1153
1201
  composableContent
1154
1202
  );
1155
1203
  const modelContent = `export interface ${pascalName}Response {
@@ -1157,7 +1205,7 @@ const fetch = async (): Promise<ResultType<${pascalName}Response>> => {
1157
1205
  }
1158
1206
  `;
1159
1207
  await this._fileService.writeFileRecursive(
1160
- import_node_path5.default.join(componentDir, `${lowerName}.model.ts`),
1208
+ import_node_path5.default.join(domainDir, `${lowerName}.model.ts`),
1161
1209
  modelContent
1162
1210
  );
1163
1211
  }
@@ -1230,28 +1278,27 @@ var init_packagejson_generator = __esm({
1230
1278
  scripts: {
1231
1279
  dev: "vite",
1232
1280
  build: "vue-tsc -b && vite build",
1233
- preview: "vite preview",
1234
- g: "xeno-js generate"
1281
+ preview: "vite preview"
1235
1282
  },
1236
1283
  dependencies: {
1237
1284
  "@xeno-js/vue": "latest",
1238
- "vue": "^3.5.0"
1285
+ "vue": "^3.5.43"
1239
1286
  },
1240
1287
  devDependencies: {
1241
1288
  "@types/node": "^20.0.0",
1242
1289
  "@vitejs/plugin-vue": "^5.1.0",
1243
- "typescript": "^5.5.0",
1290
+ "typescript": "^5.8.0",
1244
1291
  "vite": "^5.4.0",
1245
- "vue-tsc": "^2.1.0"
1292
+ "vue-tsc": "^3.3.11"
1246
1293
  }
1247
1294
  };
1248
- if (options.axios) packageJson.dependencies["axios"] = "^1.7.0";
1295
+ if (options.axios) packageJson.dependencies["axios"] = "^1.20.0";
1249
1296
  if (options.cockatiel) packageJson.dependencies["cockatiel"] = "^4.0.0";
1250
- if (options.supabase) packageJson.dependencies["@supabase/supabase-js"] = "^2.45.0";
1251
- if (options.sentry) packageJson.dependencies["@sentry/vue"] = "^8.28.0";
1297
+ if (options.supabase) packageJson.dependencies["@supabase/supabase-js"] = "^2.117.1";
1298
+ if (options.sentry) packageJson.dependencies["@sentry/vue"] = "^10.75.2";
1252
1299
  if (options.pinia) packageJson.dependencies["pinia"] = "^2.2.0";
1253
- if (options.router) packageJson.dependencies["vue-router"] = "^4.4.0";
1254
- if (options.zod) packageJson.dependencies["zod"] = "^4.4.3";
1300
+ if (options.router) packageJson.dependencies["vue-router"] = "^4.6.4";
1301
+ if (options.zod) packageJson.dependencies["zod"] = "^4.6.5";
1255
1302
  if (options.tailwind) {
1256
1303
  packageJson.devDependencies["tailwindcss"] = "^4.0.0";
1257
1304
  packageJson.devDependencies["@tailwindcss/vite"] = "^4.0.0";
@@ -1416,28 +1463,30 @@ var init_readme_generator = __esm({
1416
1463
  this._fileService = _fileService;
1417
1464
  }
1418
1465
  _fileService;
1419
- async generate(projectPath, options) {
1420
- const content = `# ${options.targetDir}
1421
-
1466
+ async generate(projectPath, _options) {
1467
+ const content = `
1422
1468
  <div align="center">
1423
- <img src="logo/logo.png" alt="Xeno Logo" width="140" />
1469
+ <img src="logo/logo.png" alt="Xeno Vue Logo" width="140" />
1424
1470
 
1425
1471
  <h1>Xeno Vue</h1>
1426
1472
 
1427
- <p><em>Enterprise-grade DDD & CQRS framework for Vue.js</em></p>
1473
+ <p><strong>Your UI framework handles the UI. Xeno handles the application.</strong></p>
1474
+
1475
+ <p>
1476
+ Application architecture for Vue with explicit dependency injection,
1477
+ CQRS, composable pipelines, and clear boundaries between presentation,
1478
+ application behavior, and infrastructure.
1479
+ </p>
1428
1480
 
1429
1481
  <p>
1430
- <a href="https://github.com/xeno-js/xeno-js">
1431
- <img src="https://img.shields.io/badge/Powered%20by-Xeno-blueviolet?style=flat-square" alt="Powered by Xeno" />
1432
- </a>
1433
- <a href="https://github.com/xeno-js/xeno-fe/blob/main/LICENSE">
1434
- <img src="https://img.shields.io/npm/l/@xeno-js/vue?style=flat-square" alt="License: ISC" />
1435
- </a>
1436
1482
  <a href="https://www.npmjs.com/package/@xeno-js/vue">
1437
- <img src="https://img.shields.io/npm/v/@xeno-js/vue?style=flat-square" alt="NPM Version" />
1483
+ <img src="https://img.shields.io/npm/v/@xeno-js/vue?style=flat-square" alt="npm version" />
1438
1484
  </a>
1439
- <a href="https://buymeacoffee.com/xenojs">
1440
- <img src="https://img.shields.io/badge/Buy%20Me%20A%20Coffee-Support-FFdd00?style=flat-square&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me A Coffee" />
1485
+ <a href="https://github.com/xeno-js/xeno-fe">
1486
+ <img src="https://img.shields.io/github/stars/xeno-js/xeno-fe?style=flat-square" alt="GitHub stars" />
1487
+ </a>
1488
+ <a href="https://img.shields.io/npm/l/@xeno-js/vue?style=flat-square">
1489
+ <img src="https://img.shields.io/npm/l/@xeno-js/vue?style=flat-square" alt="License: MIT" />
1441
1490
  </a>
1442
1491
  </p>
1443
1492
  </div>
@@ -1446,352 +1495,585 @@ var init_readme_generator = __esm({
1446
1495
 
1447
1496
  ## What is Xeno Vue?
1448
1497
 
1449
- **Xeno Vue** (\`@xeno-js/vue\`) is an enterprise-grade, deterministic
1450
- architectural framework that brings the strictness of **Domain-Driven Design
1451
- (DDD)** and **Command Query Responsibility Segregation (CQRS)** natively to the
1452
- browser.
1498
+ **Xeno Vue** (\`@xeno-js / vue\`) brings Xeno's application architecture to Vue applications.
1499
+
1500
+ It provides the composition root and application building blocks needed to keep
1501
+ application behavior explicit instead of putting all of it inside Vue
1502
+ components.
1503
+
1504
+ The package is built around:
1505
+
1506
+ * explicit dependency injection;
1507
+ * a typed application registry;
1508
+ * commands and queries through a client-side mediator;
1509
+ * composable application pipelines;
1510
+ * remote data source boundaries;
1511
+ * browser request and identity context;
1512
+ * optional authentication, logging, validation, and caching integrations.
1453
1513
 
1454
- By shifting operational logic, remote data fetching, and state mutations away
1455
- from Vue components and Pinia stores, Xeno ensures your frontend architecture
1456
- remains pristine, highly testable, and completely decoupled from the UI layer.
1457
- It treats the browser as a complex distributed client, not just a document
1458
- viewer.
1514
+ The goal is simple:
1515
+
1516
+ > **Keep Vue responsible for presentation. Keep application behavior explicit.**
1459
1517
 
1460
1518
  ---
1461
1519
 
1462
- ## \u{1F4A1} Why Choose Xeno Vue?
1463
-
1464
- Modern frontend development often leads to "Spaghetti State" where API calls,
1465
- business rules, and DOM manipulations are tightly coupled inside components.
1466
- Xeno Vue fixes this with an emphasis on pure Dependency Injection (DI) and clean
1467
- boundaries.
1468
-
1469
- - **Zero-Magic Dependency Injection**: Xeno provides an explicit
1470
- \`XenoAppBuilder\` to construct your IoC container at bootstrap. No hidden Vue
1471
- plugins doing implicit injections. You have total control over the dependency
1472
- graph.
1473
- - **Total UI Decoupling**: Vue components act strictly as the Presentation
1474
- Layer. Business logic, API calls, and CQRS handlers live in pure, isolated
1475
- TypeScript classes. You can swap Vue for React tomorrow without touching your
1476
- core domain.
1477
- - **Enterprise-Grade Client Resiliency**: The browser is a hostile, unreliable
1478
- environment. Xeno Vue natively integrates \`Cockatiel\` and \`Axios\` to provide
1479
- out-of-the-box circuit breakers, retries with jitter, and bulkheads directly
1480
- in the client.
1481
- - **Frontend Middleware Pipeline**: Handle Authentication, CSRF validation,
1482
- aggressive Query Caching, and Performance logging _before_ a command is
1483
- executed or an API call fires, using the native \`Mediator\` pipeline.
1520
+ ## The boundary
1521
+
1522
+ A Vue application does not need to put every concern into components, stores, or router handlers.
1523
+
1524
+ Xeno gives the application layer a distinct place to live:
1525
+
1526
+ \`\`\`text
1527
+ Vue UI
1528
+ \u2193
1529
+ Application
1530
+ \u2193
1531
+ Domain / Shared
1532
+ \u2193
1533
+ Infrastructure
1534
+ \u2193
1535
+ HTTP / external systems
1536
+ \`\`\`
1537
+
1538
+ Vue remains your presentation layer.
1539
+
1540
+ Xeno provides the application composition and execution model around it.
1484
1541
 
1485
1542
  ---
1486
1543
 
1487
- ## \u{1F4D6} Documentation & Getting Started
1544
+ ## Why Xeno Vue?
1545
+
1546
+ As an application grows, API calls, validation, logging, caching, authentication,
1547
+ and application decisions can end up spread across components.
1548
+
1549
+ Xeno makes those responsibilities explicit.
1550
+
1551
+ ### Explicit dependency injection
1552
+
1553
+ \`XenoAppBuilder\` is the composition root.
1554
+
1555
+ Dependencies are registered in code instead of being discovered through decorators,
1556
+ runtime scanning, or hidden framework conventions.
1488
1557
 
1489
- To explore the architecture, programmatic configurations, and extension
1490
- workflows of Xeno Vue, read the full technical manuals located inside the main
1491
- documentation hub:
1558
+ \`\`\`ts
1559
+ import { XenoAppBuilder } from '@xeno-js/vue'
1492
1560
 
1493
- - **[Framework Documentation Repository](https://www.xeno-js.it/vue/overview)**
1561
+ const builder = XenoAppBuilder.create()
1562
+ \`\`\`
1494
1563
 
1495
- Inside, you will find exhaustive, step-by-step assembly guides covering frontend
1496
- IoC building (\`XenoAppBuilder\`), Vue \`Provide/Inject\` boundaries, and CQRS
1497
- composables.
1564
+ You configure the services your application needs through the builder.
1498
1565
 
1499
1566
  ---
1500
1567
 
1501
- ## \u{1F680} Live Executable Demos
1568
+ ### CQRS in the browser
1569
+
1570
+ The client mediator exposes two execution paths:
1571
+
1572
+ \`\`\`text
1573
+ Command
1574
+ \u2193
1575
+ Command pipeline
1576
+ \u2193
1577
+ Application action
1578
+
1579
+ Query
1580
+ \u2193
1581
+ Query pipeline
1582
+ \u2193
1583
+ Application action
1584
+ \`\`\`
1585
+
1586
+ Commands are sent with:
1587
+
1588
+ \`\`\`ts
1589
+ await services.mediator.send(command, action)
1590
+ \`\`\`
1502
1591
 
1503
- Want to see how Xeno Vue works? Check out the functional example application
1504
- showcasing end-to-end command/query segregation, resilient HTTP fetching, and
1505
- pure Composition API integration.
1592
+ Queries are executed with:
1506
1593
 
1507
- - **[Xeno Vue Demo Architecture](https://www.xeno-js.it/demo)**
1594
+ \`\`\`ts
1595
+ await services.mediator.query(query, action)
1596
+ \`\`\`
1597
+
1598
+ The application action is supplied by your code, so the transport remains behind
1599
+ an infrastructure boundary.
1508
1600
 
1509
1601
  ---
1510
1602
 
1511
- ## \u{1F4E6} Installation
1603
+ ## Pipelines
1512
1604
 
1513
- Install the Vue package:
1605
+ Cross-cutting behavior belongs in the application execution pipeline.
1514
1606
 
1515
- \`\`\`bash
1516
- npm install @xeno-js/vue
1607
+ The current implementation provides pipeline building blocks for:
1517
1608
 
1518
- \`\`\`
1609
+ * exception handling;
1610
+ * logging;
1611
+ * performance thresholds;
1612
+ * validation with Zod schemas;
1613
+ * query caching.
1519
1614
 
1520
- Xeno uses **Optional Peer Dependencies**. You only install the external
1521
- libraries you actually need.
1615
+ Pipelines are composed by the application module and executed around commands
1616
+ and queries.
1522
1617
 
1523
- \`\`\`bash
1524
- # Example: Install tools only if you enable them in the builder
1525
- npm install axios cockatiel zod @supabase/supabase-js @sentry/vue
1618
+ For example:
1526
1619
 
1527
- \`\`\`
1620
+ \`\`\`ts
1621
+ builder.addPipeline((config) => {
1622
+ config.queryCaching = true
1623
+ config.threshold = 500
1624
+ })
1625
+ \`\`\`
1626
+
1627
+ Query caching uses the configured cache and is intended for query requests.
1528
1628
 
1529
1629
  ---
1530
1630
 
1531
- ## \u26A1 Bootstrapping & Component Example
1631
+ ## Remote data sources
1532
1632
 
1533
- Below is an architectural example of how to configure the Xeno \`XenoAppBuilder\`,
1534
- inject it into the Vue application, and consume it using Composition API
1535
- Composables.
1633
+ HTTP concerns can be isolated behind a remote data source.
1536
1634
 
1537
- ### 1. Initialize the Container (\`src/bootstrap.ts\`)
1635
+ \`\`\`ts
1636
+ import { RemoteDataSource } from '@xeno-js/vue'
1538
1637
 
1539
- \`\`\`typescript
1540
- import { XenoAppBuilder, LOG_LEVEL, TOKENS } from '@xeno-js/vue'
1541
- import { BffRemoteDataSource } from './infrastructure'
1542
-
1543
- import type { RemoteDataSource, XenoVueRegistry } from '@xeno-js/vue'
1544
-
1545
- export type MyRegistry = XenoVueRegistry<{
1546
- BFF_REMOTE_DS: RemoteDataSource
1547
- }>
1548
-
1549
- // Create the root IoC container context for the browser
1550
- const builder = XenoAppBuilder.create<MyRegistry>()
1551
- .addContext((opts) => {
1552
- opts.contextAccessor = useContextStore()
1553
- })
1554
- .addLogger((opts, config) => {
1555
- opts.console = config.get('VITE_APP_ENV') === 'development'
1556
- opts.level = LOG_LEVEL.DEBUG
1557
- })
1558
- .addAuth((opts, config) => {
1559
- opts.url = config.getOrThrow('VITE_SUPABASE_URL')
1560
- opts.key = config.getOrThrow('VITE_SUPABASE_KEY')
1561
- })
1562
- .addHttpCore('BFF_REMOTE_DS', (opts, config) => {
1563
- opts.client = {
1564
- baseURL: config.getOrThrow('VITE_API_BASE_URL'),
1565
- timeoutMs: 10000,
1638
+ export class UsersRemoteDataSource extends RemoteDataSource {
1639
+ public getById(id: string) {
1640
+ return this.get<User>(\`/users/\${id}\`)
1641
+ }
1642
+
1643
+ public create(payload: CreateUserPayload) {
1644
+ return this.post<User, CreateUserPayload>('/users', payload)
1645
+ }
1566
1646
  }
1567
- opts.resilience.retry.attempts = 3
1568
- opts.factory = (http, resilience) =>
1569
- new BffRemoteDataSource(http, resilience)
1570
- })
1571
- .addPipeline((config) => {
1572
- config.queryCaching = true // Enable in-memory caching for Queries
1573
- })
1647
+ \`\`\`
1574
1648
 
1575
- export async function bootstrap() {
1576
- return await builder.build()
1577
- }
1578
- \`\`\`
1649
+ The data source depends on the framework-neutral HTTP client abstraction.
1579
1650
 
1580
- ### 2. Inject into Vue (\`src/main.ts\`)
1651
+ \`XenoAppBuilder.addHttpCore()\` then provides the concrete HTTP client and registers
1652
+ the resulting data source in the application registry.
1581
1653
 
1582
- \`\`\`typescript
1583
- import { createApp } from 'vue'
1584
- import App from './App.vue'
1585
- import { bootstrap } from './bootstrap'
1586
- import { XENO_SERVICES_KEY, ServicesUtils } from '@xeno-js/vue'
1654
+ \`\`\`ts
1655
+ type AppRegistry = XenoVueRegistry<{
1656
+ users: UsersRemoteDataSource
1657
+ }>
1587
1658
 
1588
- async function mountApp() {
1589
- try {
1590
- const app = createApp(App)
1591
- const container = await bootstrap()
1659
+ const builder = XenoAppBuilder.create<AppRegistry>()
1660
+ .addHttpCore('users', (config) => {
1661
+ config.client.baseURL = '/api'
1662
+ config.factory = (http) => new UsersRemoteDataSource(http)
1663
+ })
1664
+ \`\`\`
1592
1665
 
1593
- // Provide the Xeno IoC container globally to all components
1594
- app.provide(XENO_SERVICES_KEY, container)
1666
+ Axios is used by the built-in HTTP adapter when \`addHttpCore()\` is configured.
1595
1667
 
1596
- // Register global services utility for composables
1597
- ServicesUtils.setGlobalServices(container)
1668
+ ---
1598
1669
 
1599
- app.mount('#app')
1600
- } catch (error) {
1601
- console.error('Critical error during frontend bootstrap:', error)
1602
- }
1603
- }
1670
+ ## Application context
1604
1671
 
1605
- mountApp()
1606
- \`\`\`
1672
+ Browser applications still need contextual information around the current
1673
+ execution.
1607
1674
 
1608
- ### 3. CQRS Composable Usage (\`src/features/users/use-create-user.ts\`)
1675
+ Xeno provides a browser context accessor containing information such as:
1609
1676
 
1610
- \`\`\`typescript
1611
- import { ref } from 'vue'
1612
- import { Result } from '@xeno-js/shared'
1613
- import { ServicesUtils } from '@/use-app'
1614
- import { CreateUserCommand } from './create-user.command'
1677
+ * request ID;
1678
+ * correlation ID;
1679
+ * user identity;
1680
+ * user agent;
1681
+ * path;
1682
+ * origin;
1683
+ * transport metadata.
1615
1684
 
1616
- export function useCreateUser() {
1617
- const loading = ref(false)
1618
- const error = ref<string | null>(null)
1685
+ You can use the default accessor or provide your own implementation.
1619
1686
 
1620
- const execute = async (payload: { email: string; name: string }) => {
1621
- if (loading.value) return Result.fail(new Error('Already executing'))
1622
- loading.value = true
1623
- error.value = null
1687
+ \`\`\`ts
1688
+ builder.addContext((config) => {
1689
+ // Optional custom context accessor configuration.
1690
+ })
1691
+ \`\`\`
1624
1692
 
1625
- try {
1626
- // Safely resolve the Mediator from the Xeno Container
1627
- const { mediator, BFF_REMOTE_DS } = ServicesUtils.useApp()
1628
- const command = new CreateUserCommand()
1693
+ ---
1629
1694
 
1630
- const result = await mediator.send(command, async () => {
1631
- const apiResult = await bffRemoteDs.post('/v1/users', payload)
1695
+ ## Logging
1632
1696
 
1633
- if (!apiResult.isOk()) return Result.fail(apiResult.getErrorOrThrow())
1634
- return Result.ok(apiResult.getValueOrThrow())
1635
- })
1697
+ Logging is configured independently from the application code.
1698
+
1699
+ The package currently supports:
1700
+
1701
+ * console logging;
1702
+ * Sentry logging;
1703
+ * custom logger clients.
1704
+
1705
+ Example:
1706
+
1707
+ \`\`\`ts
1708
+ builder.addLogger((config) => {
1709
+ config.console = true
1710
+ })
1711
+ \`\`\`
1712
+
1713
+ Optional integrations are loaded when they are configured, keeping them outside
1714
+ the default bootstrap path.
1715
+
1716
+ ---
1717
+
1718
+ ## Authentication
1719
+
1720
+ Supabase authentication is available as an infrastructure integration.
1721
+
1722
+ \`\`\`ts
1723
+ builder.addAuth((config, env) => {
1724
+ config.url = env.getOrThrow('SUPABASE_URL')
1725
+ config.key = env.getOrThrow('SUPABASE_KEY')
1726
+ })
1727
+ \`\`\`
1728
+
1729
+ The default configuration service resolves \`VITE_\` environment variables in
1730
+ Vite applications.
1731
+
1732
+ For example:
1733
+
1734
+ \`\`\`text
1735
+ VITE_SUPABASE_URL
1736
+ VITE_SUPABASE_KEY
1737
+ \`\`\`
1636
1738
 
1637
- if (!result.isOk()) {
1638
- error.value = result.getErrorOrThrow().message
1739
+ Authentication is an integration, not part of the application's business rules.
1740
+
1741
+ ---
1742
+
1743
+ ## Validation
1744
+
1745
+ Application request validation can be configured with Zod schemas.
1746
+
1747
+ \`\`\`ts
1748
+ builder.addPipeline((config) => {
1749
+ config.schemas = {
1750
+ CreateUser: createUserSchema,
1751
+ UpdateUser: updateUserSchema,
1639
1752
  }
1753
+ })
1754
+ \`\`\`
1755
+
1756
+ Validation is executed as part of the application pipeline.
1757
+
1758
+ Zod remains optional until validation schemas are actually configured.
1759
+
1760
+ ---
1761
+
1762
+ ## Query caching
1763
+
1764
+ The package includes an in-memory cache path for query requests.
1765
+
1766
+ Enable it through the pipeline configuration:
1767
+
1768
+ \`\`\`ts
1769
+ builder.addPipeline((config) => {
1770
+ config.queryCaching = true
1771
+ })
1772
+ \`\`\`
1773
+
1774
+ Cache keys can be contextual or user-scoped through the shared cache key builder.
1775
+
1776
+ ---
1777
+
1778
+ ## Bootstrap
1779
+
1780
+ A complete browser composition root can look like this:
1781
+
1782
+ \`\`\`ts
1783
+ import { XenoAppBuilder } from '@xeno-js/vue'
1784
+
1785
+ const builder = XenoAppBuilder.create()
1786
+ .addContext(() => { })
1787
+ .addLogger((config) => {
1788
+ config.console = true
1789
+ })
1790
+ .addAuth((config, env) => {
1791
+ config.url = env.getOrThrow('SUPABASE_URL')
1792
+ config.key = env.getOrThrow('SUPABASE_KEY')
1793
+ })
1794
+ .addPipeline((config) => {
1795
+ config.queryCaching = true
1796
+ config.threshold = 500
1797
+ })
1640
1798
 
1641
- return result
1642
- } finally {
1643
- loading.value = false
1799
+ export async function bootstrap() {
1800
+ return builder.build()
1644
1801
  }
1645
- }
1802
+ \`\`\`
1646
1803
 
1647
- return { loading, error, execute }
1648
- }
1649
- \`\`\`
1804
+ The builder executes the registered tasks and returns a frozen application
1805
+ registry.
1806
+
1807
+ The composition root is application code: there is no requirement to hide it
1808
+ behind Vue plugins or decorators.
1650
1809
 
1651
1810
  ---
1652
1811
 
1653
- ## \u{1F6E0} Scaffold your project with CLI
1812
+ ## Providing services to Vue
1654
1813
 
1655
- Xeno includes an official CLI tool, \`@xeno-js/cli\`, designed to bootstrap your
1656
- new application in seconds. It offers an interactive setup for Vue projects,
1657
- instantly scaffolding the CQRS files, composables, and dependency injection
1658
- boundaries.
1814
+ The package exports \`XENO_SERVICES_KEY\` so the application registry can be made
1815
+ available through Vue's \`provide / inject\` mechanism.
1659
1816
 
1660
- \`\`\`bash
1661
- # Generate a new Vue project
1662
- npx @xeno-js/cli new my-frontend-app --vue
1817
+ \`\`\`ts
1818
+ import { createApp } from 'vue'
1819
+ import { XENO_SERVICES_KEY } from '@xeno-js/vue'
1663
1820
 
1664
- # Generate a Vue Command with its Composable
1665
- npx @xeno-js/cli g command CreateUser --vue
1821
+ import App from './App.vue'
1822
+ import { bootstrap } from './bootstrap'
1666
1823
 
1667
- \`\`\`
1824
+ async function mountApp() {
1825
+ const app = createApp(App)
1826
+ const services = await bootstrap()
1827
+
1828
+ app.provide(XENO_SERVICES_KEY, services)
1829
+ app.mount('#app')
1830
+ }
1831
+
1832
+ mountApp()
1833
+ \`\`\`
1834
+
1835
+ From there, your application code can resolve the registry through Vue's normal
1836
+ dependency injection mechanism.
1837
+
1838
+ The package does not require a custom state-management abstraction.
1839
+
1840
+ ---
1841
+
1842
+ ## Keeping application logic outside components
1843
+
1844
+ A component can remain focused on presentation while application behavior lives
1845
+ in a composable or application service written by your project.
1846
+
1847
+ For example:
1848
+
1849
+ \`\`\`ts
1850
+ import { inject } from 'vue'
1851
+ import { XENO_SERVICES_KEY } from '@xeno-js/vue'
1852
+
1853
+ export function useUsersApplication() {
1854
+ const services = inject(XENO_SERVICES_KEY)
1855
+
1856
+ if (!services) {
1857
+ throw new Error('Xeno services are not available')
1858
+ }
1859
+
1860
+ return services
1861
+ }
1862
+ \`\`\`
1668
1863
 
1669
- Check the
1670
- **[CLI Documentation](https://www.google.com/search?q=https://www.xeno-js.it/cli/overview&utm_source=gemini)**
1671
- for full options.
1864
+ The composable belongs to your application.
1865
+
1866
+ Xeno provides the application infrastructure it uses.
1672
1867
 
1673
1868
  ---
1674
1869
 
1675
- ## \u{1F91D} For Contributors
1870
+ ## Public API
1871
+
1872
+ The package root currently exposes:
1873
+
1874
+ \`\`\`text
1875
+ XenoAppBuilder
1876
+ RemoteDataSource
1877
+ XENO_SERVICES_KEY
1878
+ \`\`\`
1676
1879
 
1677
- We welcome contributions to Xeno! To maintain the highest code quality and
1678
- stability of the core framework, **direct pushes to the \`main\` and \`develop\`
1679
- branches are strictly prohibited.** Please follow this Git Flow to contribute:
1880
+ It also re-exports the public contracts and primitives from \`@xeno-js / shared\`.
1680
1881
 
1681
- 1. **Branch off from \`develop**\`: Create a new branch for your feature or
1682
- bugfix.
1882
+ The concrete internal pipeline implementations are assembled by the builder and
1883
+ are not intended to be the primary public API of the package.
1884
+
1885
+ ---
1886
+
1887
+ ## Installation
1888
+
1889
+ Install Vue, Shared, and Xeno Vue:
1683
1890
 
1684
1891
  \`\`\`bash
1685
- git checkout develop
1686
- git pull origin develop
1687
- git checkout -b feat/your-awesome-feature
1892
+ npm install @xeno-js / vue @xeno-js / shared vue
1893
+ \`\`\`
1688
1894
 
1689
- \`\`\`
1895
+ Install the integrations you actually use.
1690
1896
 
1691
- 2. **Make your changes**: Write your code and ensure it passes all local checks
1692
- (linting, types, and tests).
1897
+ For HTTP data sources:
1693
1898
 
1694
1899
  \`\`\`bash
1695
- npm run check
1900
+ npm install axios
1901
+ \`\`\`
1696
1902
 
1697
- \`\`\`
1903
+ For validation:
1904
+
1905
+ \`\`\`bash
1906
+ npm install zod
1907
+ \`\`\`
1908
+
1909
+ For Supabase authentication:
1910
+
1911
+ \`\`\`bash
1912
+ npm install @supabase/supabase-js
1913
+ \`\`\`
1914
+
1915
+ For Sentry:
1916
+
1917
+ \`\`\`bash
1918
+ npm install @sentry/vue
1919
+ \`\`\`
1698
1920
 
1699
- 3. **Commit your changes**: We enforce
1700
- [Conventional Commits](https://www.conventionalcommits.org/?utm_source=gemini).
1701
- Husky will verify your commit message format.
1702
- 4. **Commit Format:**
1921
+ For Vue Router integration:
1703
1922
 
1704
1923
  \`\`\`bash
1705
- feat(scope): add new feature
1706
- fix(scope): resolve bug
1707
- chore(scope): update dependencies
1924
+ npm install vue - router
1925
+ \`\`\`
1926
+
1927
+ ---
1928
+
1929
+ ## CLI
1930
+
1931
+ The Xeno ecosystem includes an official CLI for creating Vue application
1932
+ structures:
1933
+
1934
+ \`\`\`bash
1935
+ npx @xeno-js / cli new my - app--vue
1936
+ \`\`\`
1937
+
1938
+ You can also generate application components inside an existing project:
1939
+
1940
+ \`\`\`bash
1941
+ npx @xeno-js / cli g command CreateUser--vue
1942
+ \`\`\`
1943
+
1944
+ CLI:
1708
1945
 
1946
+ https://github.com/xeno-js/xeno-cli
1947
+
1948
+ ---
1949
+
1950
+ ## Xeno ecosystem
1951
+
1952
+ \`\`\`text
1953
+ @xeno-js / shared
1954
+ Define the application.
1955
+
1956
+ @xeno-js / core
1957
+ Execute the backend application.
1958
+
1959
+ @xeno-js / vue
1960
+ Bring the application architecture to Vue.
1961
+
1962
+ @xeno-js / cli
1963
+ Get started with the structure.
1709
1964
  \`\`\`
1710
1965
 
1711
- 5. **Submit a Pull Request (PR)**: Push your branch to GitHub and open a Pull
1712
- Request targeting the **\`develop\`** branch.
1713
- 6. **Review**: The repository owner will review your code, run pipeline tests,
1714
- and merge it into \`develop\`.
1966
+ A useful mental model is:
1967
+
1968
+ \`\`\`text
1969
+ Transport
1970
+ hosts the application
1971
+
1972
+ Application
1973
+ executes use cases
1974
+
1975
+ Domain
1976
+ defines business rules
1977
+
1978
+ Infrastructure
1979
+ connects external systems
1980
+ \`\`\`
1715
1981
 
1716
- _Note: The \`main\` branch is strictly reserved for production releases. Code
1717
- flows from feature branches \u27A1\uFE0F \`develop\` \u27A1\uFE0F \`main\`._
1982
+ ---
1718
1983
 
1719
- ### Scripts
1984
+ ## Keep your stack
1720
1985
 
1721
- | Command | Description |
1722
- | ----------------------- | ---------------------------------------------- |
1723
- | \`npm run build\` | Builds the TypeScript source code into \`dist/\` |
1724
- | \`npm run typecheck\` | Checks types without emitting files |
1725
- | \`npm run lint\` | Runs ESLint |
1726
- | \`npm run format\` | Formats code with Prettier |
1727
- | \`npm run test\` | Runs the Vitest test suite |
1728
- | \`npm run test:coverage\` | Runs tests and generates a coverage report |
1986
+ Xeno Vue does not replace Vue.
1729
1987
 
1730
- ### Code Quality (Husky & Git Hooks)
1988
+ It does not replace your router.
1731
1989
 
1732
- This project strictly enforces code quality rules before pushing to the
1733
- repository:
1990
+ It does not replace your state management solution.
1734
1991
 
1735
- - **\`pre-commit\`**: Runs \`lint-staged\` on staged files (ESLint + Prettier).
1736
- - **\`commit-msg\`**: Checks commit messages with \`commitlint\` (we use
1737
- Conventional Commits).
1738
- - **\`pre-push\`**: Runs type checking, linting, and testing before code leaves
1739
- your machine.
1992
+ It provides a place for application behavior and infrastructure composition to
1993
+ live alongside the tools you already use.
1740
1994
 
1741
1995
  ---
1742
1996
 
1743
- ## \u{1F331} Support & Appreciation
1997
+ ## Browser environment
1998
+
1999
+ The default configuration service is designed for Vite/browser applications.
2000
+
2001
+ The package provides browser-oriented request context data and integrates with
2002
+ Vue's dependency injection system.
2003
+
2004
+ Node.js \`20 + \` is required by the package tooling.
2005
+
2006
+ ---
1744
2007
 
1745
- Building, benchmarking, and maintaining a progressive, enterprise-ready
1746
- open-source framework requires a massive amount of continuous dedication and
1747
- architectural engineering.
2008
+ ## Development
1748
2009
 
1749
- If Xeno has brought value to your development workflows, helped decouple your
1750
- core business logic, or simplified your system infrastructure layout, consider
1751
- supporting its open-source lifecycle. Your backing directly accelerates our
1752
- strategic roadmap for new out-of-the-box transport integrations and keeps the
1753
- documentation pristine.
2010
+ Clone the repository:
1754
2011
 
1755
- **Want to know how you can contribute or sponsor Xeno?** We rely on the
1756
- commitment of our community to keep the project independent and thriving.
1757
- Whether you are an individual developer or a business using Xeno, your support
1758
- makes a real difference.
2012
+ \`\`\`bash
2013
+ git clone https://github.com/xeno-js/xeno-fe.git
2014
+ cd xeno - fe
2015
+ npm install
2016
+ \`\`\`
2017
+
2018
+ Run the checks:
2019
+
2020
+ \`\`\`bash
2021
+ npm run check
2022
+ \`\`\`
1759
2023
 
1760
- \u{1F449}
1761
- **[Read our support guidelines and find out how to help](https://www.xeno-js.it/support-us)**
2024
+ Useful commands:
1762
2025
 
1763
- Thank you for being part of this decoupled open-source journey!
2026
+ \`\`\`bash
2027
+ npm run build
2028
+ npm run typecheck
2029
+ npm run lint
2030
+ npm run test
2031
+ npm run test: coverage
2032
+ npm run format
2033
+ \`\`\`
1764
2034
 
1765
- <amp-bounce>
1766
- </amp-bounce>
1767
- <a href="https://www.buymeacoffee.com/xenojs" target="_blank">
1768
- <img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="42" style="height: 42px !important;" />
1769
- </a>
2035
+ Development happens from feature branches targeting \`develop\`.
1770
2036
 
1771
2037
  ---
1772
2038
 
1773
- ## \u{1F6E1}\uFE0F Powered by Xeno
1774
-
1775
- If you are using Xeno in your project, let the world know! Add this badge to
1776
- your README:
1777
-
1778
- \`\`\`html
1779
- <a
1780
- href="[https://github.com/xeno-js/xeno-fe](https://github.com/xeno-js/xeno-fe)"
1781
- target="_blank"
1782
- >
1783
- <img
1784
- src="[https://img.shields.io/badge/Powered%20by-Xeno-black?style=flat-square](https://img.shields.io/badge/Powered%20by-Xeno-black?style=flat-square)"
1785
- alt="Powered by Xeno"
1786
- height="20"
1787
- />
1788
- </a>
1789
- \`\`\`
2039
+ ## Contributing
2040
+
2041
+ Contributions are welcome.
2042
+
2043
+ Create a feature branch from \`develop\`:
2044
+
2045
+ \`\`\`bash
2046
+ git checkout develop
2047
+ git pull origin develop
2048
+ git checkout - b feat / your - feature
2049
+ \`\`\`
2050
+
2051
+ Run the project checks before opening a pull request:
2052
+
2053
+ \`\`\`bash
2054
+ npm run check
2055
+ \`\`\`
2056
+
2057
+ We use Conventional Commits:
2058
+
2059
+ \`\`\`text
2060
+ feat(vue): add application service
2061
+ fix(datasource): correct request handling
2062
+ refactor(builder): simplify bootstrap
2063
+ docs(readme): clarify architecture
2064
+ \`\`\`
1790
2065
 
1791
- ## \u{1F4C4} License
2066
+ Open pull requests against:
2067
+
2068
+ \`\`\`text
2069
+ develop
2070
+ \`\`\`
2071
+
2072
+ ---
1792
2073
 
1793
- Copyright (c) 2026 Xeno. Licensed under the
1794
- [ISC License](https://www.google.com/search?q=LICENSE&utm_source=gemini).
2074
+ ## License
2075
+
2076
+ MIT License. See \`LICENSE\`.
1795
2077
  `;
1796
2078
  await this._fileService.writeFileRecursive(import_node_path12.default.join(projectPath, "README.md"), content);
1797
2079
  }
@@ -1925,7 +2207,7 @@ var init_bootstrap_ts_generator = __esm({
1925
2207
  if (options.sentry) {
1926
2208
  builderNodes += `
1927
2209
  .addLogger((opts, config) => {
1928
- const env = config.get(COMMON_CONSTANTS.ENV) ?? 'development'
2210
+ const env = config.get('APP_ENV') ?? 'development'
1929
2211
  const isDev = env.toLowerCase() === 'development'
1930
2212
  opts.console = isDev
1931
2213
  if (!isDev) {
@@ -2320,8 +2602,7 @@ var init_packagejson_generator2 = __esm({
2320
2602
  types: "./dist/index.d.ts",
2321
2603
  scripts: {
2322
2604
  start: "tsx src/main.ts",
2323
- build: "tsc --project tsconfig.json",
2324
- g: "xeno-js generate"
2605
+ build: "tsc --project tsconfig.json"
2325
2606
  },
2326
2607
  dependencies: {
2327
2608
  "@xeno-js/core": "latest",
@@ -2330,35 +2611,35 @@ var init_packagejson_generator2 = __esm({
2330
2611
  devDependencies: {
2331
2612
  "@types/node": "^20.0.0",
2332
2613
  "tsx": "^4.7.0",
2333
- "typescript": "^5.4.0"
2614
+ "typescript": "^5.8.0"
2334
2615
  }
2335
2616
  };
2336
2617
  if (options.database !== CORE_CONSTANTS.NONE) {
2337
- if (options.database === CORE_CONSTANTS.DRIZZLE) {
2338
- packageJson.dependencies["drizzle-orm"] = "^0.45.2";
2339
- packageJson.dependencies["pg"] = "^8.22.0";
2340
- packageJson.dependencies["postgres"] = "^3.4.9";
2341
- packageJson.devDependencies["@types/pg"] = "^8.11.0";
2342
- packageJson.devDependencies["drizzle-kit"] = "^0.31.10";
2343
- packageJson.scripts["db:migrate"] = "drizzle-kit migrate";
2344
- } else if (options.database === CORE_CONSTANTS.SQL_LITE) {
2345
- packageJson.dependencies["drizzle-orm"] = "^0.45.2";
2346
- packageJson.dependencies["@libsql/client"] = "^0.14.0";
2347
- packageJson.devDependencies["drizzle-kit"] = "^0.31.10";
2348
- }
2618
+ packageJson.dependencies["drizzle-orm"] = "^0.45.3";
2619
+ packageJson.dependencies["pg"] = "^8.23.0";
2620
+ packageJson.dependencies["postgres"] = "^3.4.9";
2621
+ packageJson.devDependencies["@types/pg"] = "^8.23.1";
2622
+ packageJson.devDependencies["drizzle-kit"] = "^0.31.11";
2623
+ packageJson.scripts["db:migrate"] = "drizzle-kit migrate";
2624
+ packageJson.dependencies["drizzle-orm"] = "^0.45.3";
2625
+ packageJson.devDependencies["drizzle-kit"] = "^0.31.11";
2626
+ packageJson.dependencies["@libsql/client"] = "^0.14.0";
2349
2627
  packageJson.scripts["db:generate"] = "drizzle-kit generate";
2350
2628
  packageJson.scripts["db:push"] = "drizzle-kit push";
2351
2629
  }
2352
- if (options.axios) packageJson.dependencies["axios"] = "^1.16.1";
2630
+ if (options.axios) packageJson.dependencies["axios"] = "^1.20.0";
2353
2631
  if (options.cockatiel) packageJson.dependencies["cockatiel"] = "^4.0.0";
2354
2632
  if (options.pino) {
2355
2633
  packageJson.dependencies["pino"] = "^10.3.1";
2356
2634
  packageJson.devDependencies["pino-pretty"] = "^11.2.2";
2357
2635
  }
2358
- if (options.sentry) packageJson.dependencies["@sentry/node"] = "^7.64.0";
2359
- if (options.redis) packageJson.dependencies["ioredis"] = "^5.3.1";
2360
- if (options.supabase) packageJson.dependencies["@supabase/supabase-js"] = "^2.35.0";
2361
- if (options.zod) packageJson.dependencies["zod"] = "^4.4.3";
2636
+ if (options.sentry) packageJson.dependencies["@sentry/node"] = "^11.0.0";
2637
+ if (options.redis) packageJson.dependencies["ioredis"] = "^6.0.0";
2638
+ if (options.supabase) {
2639
+ packageJson.dependencies["@supabase/supabase-js"] = "^2.117.1";
2640
+ packageJson.dependencies["@supabase/ssr"] = "^0.12.7";
2641
+ }
2642
+ if (options.zod) packageJson.dependencies["zod"] = "^4.6.5";
2362
2643
  const filePath = import_node_path22.default.join(projectPath, "package.json");
2363
2644
  await this._fileService.writeFileRecursive(filePath, JSON.stringify(packageJson, null, 2));
2364
2645
  }
@@ -2572,23 +2853,17 @@ var init_readme_generator2 = __esm({
2572
2853
  _fileService;
2573
2854
  async generate(projectPath, _options) {
2574
2855
  const content = `
2575
- <div align="center">
2856
+ <div align="center">
2576
2857
  <img src="logo/logo.png" alt="Xeno Logo" width="140" />
2577
2858
 
2578
- <h1>Xeno</h1>
2579
-
2580
- <p><em>Enterprise-grade DDD & CQRS framework for Node.js</em></p>
2859
+ <h1>Xeno.JS</h1>
2860
+ <p><strong>The application architecture framework for TypeScript.</strong></p>
2861
+ <p>Build long-lived applications with explicit dependency injection, DDD, CQRS, and transport-independent business logic.</p>
2581
2862
 
2582
2863
  <p>
2583
- <a href="https://github.com/xeno-js/xeno-js">
2584
- <img src="https://img.shields.io/badge/Powered%20by-Xeno-blueviolet?style=flat-square" alt="Powered by Xeno" />
2585
- </a>
2586
- <a href="https://github.com/xeno-js/xeno-js/blob/main/LICENSE">
2587
- <img src="https://img.shields.io/npm/l/@xeno?style=flat-square" alt="License: ISC" />
2588
- </a>
2589
- <a href="https://www.npmjs.com/package/@xeno/core">
2590
- <img src="https://img.shields.io/npm/v/@xeno/core?style=flat-square" alt="NPM Version" />
2591
- </a>
2864
+ <a href="https://www.npmjs.com/package/@xeno-js/core"><img src="https://img.shields.io/npm/v/@xeno-js/core?style=flat-square" alt="NPM Version" /></a>
2865
+ <a href="https://github.com/xeno-js/xeno-js"><img src="https://img.shields.io/badge/Powered%20by-Xeno-blueviolet?style=flat-square" alt="Powered by Xeno" /></a>
2866
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="License: MIT" /></a>
2592
2867
  <a href="https://buymeacoffee.com/xenojs">
2593
2868
  <img src="https://img.shields.io/badge/Buy%20Me%20A%20Coffee-Support-FFdd00?style=flat-square&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me A Coffee" />
2594
2869
  </a>
@@ -2599,391 +2874,443 @@ var init_readme_generator2 = __esm({
2599
2874
 
2600
2875
  ## What is Xeno?
2601
2876
 
2602
- **Xeno** is an enterprise-grade, runtime-agnostic architectural framework for
2603
- Node.js built natively with TypeScript. It provides structural primitives for
2604
- implementing robust **Domain-Driven Design (DDD)** and **Command Query
2605
- Responsibility Segregation (CQRS)** patterns. By shifting operational logic away
2606
- from delivery mechanisms and transport frameworks, Xeno ensures your core
2607
- application architecture remains pristine, testable, and completely isolated
2608
- from external infrastructural churn.
2877
+ Xeno is a TypeScript application architecture framework for Node.js.
2609
2878
 
2610
- ---
2879
+ It provides explicit building blocks for applications organized around:
2880
+
2881
+ - **Dependency Injection** with explicit service registration and lifetimes
2882
+ - **Domain-Driven Design (DDD)** and domain/application boundaries
2883
+ - **CQRS** with commands, queries, handlers, and composable pipelines
2884
+ - **Request context** built around asynchronous execution context
2885
+ - **Repositories and data sources** that keep persistence behind application
2886
+ boundaries
2887
+ - **Infrastructure adapters** for databases, Redis, authentication, logging,
2888
+ resilience, and other integrations
2889
+
2890
+ The goal is simple: **make application architecture explicit in code.**
2611
2891
 
2612
- ## \u{1F4A1} Why Choose Xeno?
2613
-
2614
- Modern Node.js frameworks often tie business workflows tightly to HTTP server
2615
- abstractions or rely heavily on experimental language features. Xeno fixes this
2616
- with an emphasis on developer experience, type safety, and clean separation of
2617
- concerns.
2618
-
2619
- - **Zero Decorators**: Xeno eliminates reliance on experimental or unstable TS
2620
- decorator specifications (\`reflect-metadata\`). The IoC container
2621
- (\`ServiceContainer\`) uses pure, explicit functional factories that optimize
2622
- compilation speeds and eliminate runtime black-box behaviors.
2623
- - **Complete Server Decoupling**: Xeno does not care if you use Fastify, Hono,
2624
- Express, Koa, or AWS Lambda. The presentation layer handles incoming data
2625
- using plain, primitive contracts, making migration or multi-runtime hosting
2626
- completely seamless.
2627
- - **Pay-For-What-You-Use (Opt-in Modularity)**: Core dependencies are
2628
- strategically classified as optional peer dependencies. If your architecture
2629
- doesn't use Redis, Sentry, or Supabase, you do not pull them into your node
2630
- modules.
2631
- - **Enterprise-Grade Resiliency & Cross-Cutting Pipelines**: Address complex
2632
- distributed patterns natively without code duplication. Xeno provides
2633
- out-of-the-box composite behaviors:
2634
- - **Idempotency**: Implements multi-tenant logic keyspaces matching advanced
2635
- SaaS factory patterns for logical partitioning.
2636
- - **Concurrency Control**: Mitigates thundering herd impacts via advanced
2637
- backoff retry strategies coupled with randomized jitter.
2638
- - **Resilience Policies**: Deep integration with circuit breakers, bulkheads,
2639
- and fallbacks.
2640
- - **Deterministic Type Safety**: Strong infrastructure validation strategies
2641
- using Zod schemas.
2892
+ Xeno is not tied to a specific HTTP server. Your application layer can remain
2893
+ independent from the transport that delivers a request.
2642
2894
 
2643
2895
  ---
2644
2896
 
2645
- ## \u{1F4D6} Documentation & Getting Started
2897
+ ## Why Xeno?
2646
2898
 
2647
- To explore the architecture, programmatic configurations, and extension
2648
- workflows of Xeno, read the full technical manuals located inside the main
2649
- documentation hub:
2899
+ ### 01 \u2014 Explicit Architecture
2650
2900
 
2651
- - **[Framework Documentation Repository](https://www.xeno-js.it/introduction)**
2901
+ **Your dependency graph is code.**
2652
2902
 
2653
- Inside, you will find exhaustive, step-by-step assembly guides covering core
2654
- host building (\`AppBuilder\`), isolated request middleware lifecycles, functional
2655
- \`Result\` monads, and zero-trust authorization pipeline behavior tracks.
2903
+ Xeno does not require decorators, runtime scanning, or implicit dependency
2904
+ discovery. Services are registered explicitly, and their lifetimes are visible
2905
+ at the composition root.
2656
2906
 
2657
- ---
2907
+ \`\`\`typescript
2908
+ services.addScoped('USER_REPOSITORY', (container) => {
2909
+ return new UserRepository(
2910
+ container.resolve('USER_DATA_SOURCE'),
2911
+ container.resolve('USER_MAPPER'),
2912
+ )
2913
+ })
2914
+ \`\`\`
2915
+
2916
+ This makes the composition of the application easier to inspect, test, and
2917
+ reason about.
2918
+
2919
+ ### 02 \u2014 Transport Independence
2920
+
2921
+ Business logic should not belong to your HTTP framework.
2922
+
2923
+ Xeno keeps application concerns separate from delivery mechanisms, allowing the
2924
+ same application architecture to be hosted behind transports such as Fastify,
2925
+ Hono, Express, or other adapters.
2926
+
2927
+ \`\`\`text
2928
+ HTTP / CLI / Worker / Lambda
2929
+ |
2930
+ v
2931
+ Presentation
2932
+ |
2933
+ v
2934
+ Application
2935
+ Commands / Queries
2936
+ |
2937
+ v
2938
+ Domain
2939
+ |
2940
+ v
2941
+ Infrastructure
2942
+ DB / Redis / APIs
2943
+ \`\`\`
2944
+
2945
+ ### 03 \u2014 CQRS as an Application Primitive
2946
+
2947
+ Commands and queries are first-class application concepts.
2948
+
2949
+ Pipelines can compose cross-cutting behavior around execution, such as:
2950
+
2951
+ - authorization
2952
+ - idempotency
2953
+ - concurrency control
2954
+ - caching
2955
+ - resilience policies
2956
+ - request context
2957
+
2958
+ This keeps cross-cutting concerns out of individual handlers.
2658
2959
 
2659
- ## \u{1F680} Live Executable Demos
2960
+ ### 04 \u2014 Explicit Lifetimes and Request Boundaries
2660
2961
 
2661
- Want to see how Xeno works? Check out the functional example application
2662
- showcasing end-to-end command/query segregation, multi-tenant databases, and
2663
- resilient schema handling.
2962
+ Xeno distinguishes service lifetimes such as singleton, scoped, and transient
2963
+ services.
2664
2964
 
2665
- You can dive straight into the explicit source code modules of specialized
2666
- sandbox environments:
2965
+ Request-scoped dependencies are resolved inside an explicit application scope,
2966
+ while request metadata can be carried through asynchronous execution using
2967
+ \`AsyncLocalStorage\`.
2667
2968
 
2668
- - **[\`pipelines_middleware_demo/\`](https://www.xeno-js.it/demo)**: Traces
2669
- an execution thread from the raw HTTP transport presentation layer, executing
2670
- automated header extraction and anchoring metadata variables into
2671
- \`AsyncLocalStorage\` thread boundaries.
2969
+ Database transaction state is scoped to the same application boundary, allowing
2970
+ \`UnitOfWork\` and \`DbContext\` to operate against the transaction associated with
2971
+ the current scope.
2972
+
2973
+ ### 05 \u2014 Infrastructure Stays Outside the Domain
2974
+
2975
+ Database clients, Redis, HTTP clients, authentication providers, loggers, and
2976
+ other infrastructure integrations are composed at the edge of the application.
2977
+
2978
+ Your domain and application code can depend on contracts instead of concrete
2979
+ infrastructure.
2672
2980
 
2673
2981
  ---
2674
2982
 
2675
- ## \u{1F4E6} Installation
2983
+ ## Architecture
2984
+
2985
+ A typical Xeno application can be organized like this:
2986
+
2987
+ \`\`\`text
2988
+ + ------------------------------------------+
2989
+ | Presentation |
2990
+ | HTTP / CLI / Workers / Lambda |
2991
+ +--------------------- +--------------------+
2992
+ |
2993
+ v
2994
+ + ------------------------------------------+
2995
+ | Application |
2996
+ | Commands / Queries / Handlers / Pipes |
2997
+ +--------------------- +--------------------+
2998
+ |
2999
+ v
3000
+ + ------------------------------------------+
3001
+ | Domain |
3002
+ | Entities / Policies / Rules |
3003
+ +--------------------- +--------------------+
3004
+ |
3005
+ v
3006
+ + ------------------------------------------+
3007
+ | Infrastructure |
3008
+ | DB / Redis / APIs / Auth / Logs |
3009
+ +------------------------------------------+
3010
+ \`\`\`
3011
+
3012
+ Xeno's core is focused on composition and application architecture.
3013
+ Infrastructure capabilities can be enabled only when they are needed.
2676
3014
 
2677
- Install the core package:
3015
+ ---
2678
3016
 
2679
- \`\`\`bash
2680
- npm install @xeno-js/core
3017
+ ## Core Concepts
2681
3018
 
2682
- \`\`\`
3019
+ | Concept | Purpose |
3020
+ | ------------------ | --------------------------------------------------------- |
3021
+ | \`AppBuilder\` | Composition root for assembling an application |
3022
+ | \`ServiceContainer\` | Explicit dependency injection and service lifetimes |
3023
+ | \`CQRS\` | Commands, queries, handlers, and mediator-based execution |
3024
+ | \`Pipelines\` | Cross-cutting behavior around application execution |
3025
+ | \`Request Context\` | Request metadata across asynchronous execution |
3026
+ | \`Repository\` | Application-facing persistence abstraction |
3027
+ | \`DataSource\` | Infrastructure-facing data access implementation |
3028
+ | \`Module\` | Explicit registration of related capabilities |
3029
+ | \`Result\` | Typed success/failure flow for application operations |
2683
3030
 
2684
- Xeno uses **Optional Peer Dependencies**. You only install the external
2685
- libraries you actually need. Node.js will strictly lazy-load only the modules
2686
- you enable in the configuration.
3031
+ ---
3032
+
3033
+ ## Installation
2687
3034
 
2688
3035
  \`\`\`bash
2689
- # Example: Install tools only if you enable them in the builder
2690
- npm install zod pino cockatiel drizzle-orm
3036
+ npm install @xeno-js / core
3037
+ \`\`\`
2691
3038
 
2692
- \`\`\`
3039
+ Install only the integrations your application uses. Xeno exposes optional
3040
+ infrastructure dependencies for capabilities such as databases, Redis, logging,
3041
+ resilience, and authentication.
2693
3042
 
2694
- ---
3043
+ For example:
3044
+
3045
+ \`\`\`bash
3046
+ npm install zod pino cockatiel drizzle - orm
3047
+ \`\`\`
2695
3048
 
2696
- ## \u26A1 Bootstrapping & Middleware Example
3049
+ ---
2697
3050
 
2698
- Below is an architectural example of how to configure the Xeno
2699
- \`ServiceContainer\`, load core modules, and process an incoming application
2700
- payload natively inside a server middleware wrapper.
3051
+ ## A Small Example
2701
3052
 
2702
- ### 1. Initialize the Container and Configure Modules
3053
+ The composition root is explicit:
2703
3054
 
2704
3055
  \`\`\`typescript
2705
- import { AppBuilder, LOG_LEVEL, TOKENS, XenoRegistry } from '@xeno-js/core'
2706
- import { FindUserQueryHandler } from './user/cqrs/handlers/index'
2707
- import { FindUserController } from './user/controllers/index'
2708
- import { UserMapper } from './user/mappers/user.mapper'
2709
- import { UserWriteRepository } from './user/repositories/user-write.repository'
2710
- import { UserDataSource } from './user/datasources/user.datasource'
2711
-
2712
- // Map your registry token with XenoRegistry<TSchemaDb, TExtension>
2713
- type MyRegistry = XenoRegistry<{ /** Your Db Schema here **/}, {
2714
- USER_MAPPER_TOKEN: UserMapper
2715
- USER_DS_TOKEN: UserDataSource
2716
- USER_REPOSITORY_TOKEN: UserWriteRepository
2717
- FIND_USER_QUERY_HANDLER_TOKEN: FindUserQueryHandler
2718
- FIND_USER_CONTROLLER_TOKEN: FindUserController
2719
- }>
2720
-
2721
- // Create the root IoC container context
2722
- export const bootstrap = new AppBuilder<MyRegistry>()
2723
- // Configure middleware and only PUBLIC routes
2724
- .addMiddlewares(opts => {
2725
- opts.routeRegistry = {
2726
- '/api/user/:id': ['GET', 'POST'],
2727
- }
2728
- })
2729
- // Configure the CQRS pipeline
2730
- // Can register Policies for your intent
2731
- .addPipeline((config) => {
2732
- config.authorization.policies = {
2733
- 'FIND_USER_QUERY_HANDLER_TOKEN': {
2734
- // Add authz by user id
2735
- userId: true
2736
- // Add authz by tenant id
2737
- tenantId: true
2738
- // Add authz by roles
2739
- roles: ['admin']
2740
- // Add authz by perissions
2741
- permissions: ['read'],
2742
- }
2743
- }
2744
- // Can add idempotency pipeline for command
2745
- config.commandBus.idempotency = { lockTtlSeconds: 30, processedTtlSeconds: 60 }
2746
- // Can add concurrency pipeline for command
2747
- config.commandBus.concurrency = { delayConfig: { baseDelayMs: 100, maxJitterMs: 500 }, maxRetries: 3 }
2748
- // Can add caching pipeline for query
2749
- config.queryBus.isEnabled = true
2750
- })
2751
- // Configure Database with drizzle
2752
- .addDb((opts, config) => {
2753
- opts.connectionString = config.getOrThrow('DATABASE_URL')
2754
- })
2755
- // Configure Authentication with supabase
2756
- .addAuth((opts, config) => {
2757
- opts.key = 'demo-key'
2758
- opts.url = config.getOrThrow('API_BASE_URL')
3056
+ // src/bootstrap.ts
3057
+ import { AppBuilder } from '@xeno-js/core'
3058
+
3059
+ const app = new AppBuilder()
3060
+ .addServices((services) => {
3061
+ services.addScoped('USER_REPOSITORY', (container) => {
3062
+ return new UserRepository(container.resolve('USER_DATA_SOURCE'))
2759
3063
  })
2760
- // Configure your logger (e.g. Console, Sentry, Pino or custom logger)
2761
- .addLogger((config) => {
2762
- config.level = LOG_LEVEL.INFO
2763
- config.console = true
3064
+
3065
+ services.addScoped('FIND_USER_HANDLER', (container) => {
3066
+ return new FindUserHandler(container.resolve('USER_REPOSITORY'))
2764
3067
  })
2765
- // Register your services
2766
- .addServices((services) => {
2767
- // REGISTER MAPPER
2768
- services.addScoped('USER_MAPPER_TOKEN', () => new UserMapper())
2769
-
2770
- // REGISTER DATASOURCES
2771
- services.addScoped('USER_DS_TOKEN', (c) => new UserDataSource(c.resolve(TOKENS.DB_CONTEXT)))
2772
-
2773
- // REGISTER REPOSITORIES
2774
- services.addScoped('USER_REPOSITORY_TOKEN', (c) => new UserWriteRepository(c.resolve('USER_DS_TOKEN'), c.resolve('USER_MAPPER_TOKEN')))
2775
-
2776
- // REGISTER HANDLERS
2777
- services.addScoped('FIND_USER_QUERY_HANDLER_TOKEN', (c) => {
2778
- const requestcontext = c.resolve('USER_CONTEXT_FACTORY')
2779
- const repository = c.resolve('USER_READ_REPOSITORY')
2780
- return new FindUserQueryHandler(repository, requestcontext)
2781
- })
2782
-
2783
- // REGISTER CONTROLLERS
2784
- services.addTransient('FIND_USER_CONTROLLER_TOKEN', (c) => {
2785
- return new FindUserController(c.resolve('CONTEXT_ACCESSOR'), c.resolve('MEDIATOR'))
2786
- })
3068
+
3069
+ services.addTransient('FIND_USER_CONTROLLER', (c) => {
3070
+ return new FindUserController(
3071
+ c.resolve(TOKENS.REQUEST_CONTEXT),
3072
+ c.resolve(TOKENS.MEDIATOR),
3073
+ )
2787
3074
  })
3075
+ })
2788
3076
 
2789
- \`\`\`
3077
+ await app.build()
3078
+ \`\`\`
3079
+
3080
+ The transport remains outside the application composition:
2790
3081
 
2791
- ### 2. Wrap and Run within Server Middleware (e.g., Fastify / Hono)
3082
+ > \u26A0\uFE0F **Implementation note: Example using Fastify** The following snippet uses
3083
+ > **Fastify** solely for demonstration purposes to illustrate the transport
3084
+ > layer. Thanks to the framework's agnostic architecture, the underlying logic
3085
+ > (\`container\` and \`handler\`) remains unchanged regardless of the chosen HTTP
3086
+ > system (e.g., Express, Koa) or interface (CLI, gRPC).
2792
3087
 
2793
3088
  \`\`\`typescript
2794
- import 'dotenv/config'
2795
- import { ContainerUtils } from '@xeno-js/core'
2796
- import fastify from 'fastify'
2797
- import { bootstrap } from './bootstrap'
3089
+ import Fastify from 'fastify'
3090
+ import { app } from './bootstrap'
2798
3091
 
2799
- async function runDemo() {
2800
- console.log('\u2699\uFE0F Initialized Xeno Container...')
2801
- try {
2802
- // 1. Bootstrap the application and get the service container
2803
- const xenoApp = await bootstrap.build()
2804
-
2805
- console.log('\u{1F680} Starting Fastify server on http://localhost:3000...')
2806
-
2807
- // 2. Resolve the middleware from the container
2808
- const middleware = xeno.resolve('MIDDLEWARE')
2809
- // 3. Create a Fastify instance to handle HTTP requests
2810
- const app = fastify()
2811
-
2812
- // \u2500\u2500\u2500 ENDPOINT 2: QUERY \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
2813
- app.get('/api/user/:id', async (request, reply) => {
2814
- // 4. Execute the middleware to handle the request context and authentication, then call the StatusController's handle method with the request payload.
2815
- const responseDto = await middleware.execute(
2816
- { path: '/api/user/:id', method: 'GET', transport: { res: reply, req: request } },
2817
- { ...request.headers },
2818
- async () => {
2819
- const { id } = request.params as any
2820
- const controller = ContainerUtils.resolveServiceScoped('FIND_USER_CONTROLLER_TOKEN', xenoApp)
2821
- return await controller.handle({ id: id ?? '123' });
2822
- }
2823
- );
3092
+ const fastify = Fastify({ logger: true })
2824
3093
 
2825
- return reply
2826
- .status(responseDto.status)
2827
- .type('application/json')
2828
- .send(responseDto.data)
2829
- })
3094
+ fastify.get('/users/:id', async (req, reply) => {
3095
+ const endpoint = req.url
2830
3096
 
2831
- console.log('\u2705 Routes set up. Ready to accept requests.')
3097
+ const action = async () => {
3098
+ const controller = ContainerUtils.resolveServiceScoped(
3099
+ 'FIND_USER_CONTROLLER',
3100
+ app,
3101
+ )
3102
+ return await controller.handle()
3103
+ }
2832
3104
 
2833
- // \u2500\u2500\u2500 START SERVER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
2834
- try {
2835
- await app.listen({ port: 3000 })
2836
- console.log('\u{1F680} Application running on http://localhost:3000')
2837
- console.log('\u{1F449} GET /api/user/:id (GET: api/user/1)')
2838
- } catch (err) {
2839
- console.error('Error starting Fastify server:', err)
2840
- app.log.error(err)
2841
- process.exit(1)
2842
- }
2843
- } catch (error) {
2844
- console.error('Error during bootstrap or server setup:', error)
2845
- process.exit(1)
2846
- }
2847
- }
3105
+ const result = await ContainerUtils.runExecute(
3106
+ endpoint,
3107
+ req.method,
3108
+ req.headers,
3109
+ { reply, req },
3110
+ app,
3111
+ action,
3112
+ )
2848
3113
 
2849
- runDemo()
2850
- \`\`\`
3114
+ return reply.send(result)
3115
+ })
3116
+ \`\`\`
3117
+
3118
+ The HTTP adapter is responsible for HTTP. The application handler is responsible
3119
+ for the use case.
2851
3120
 
2852
3121
  ---
2853
3122
 
2854
- ## \u{1F6E0} Scaffold your project with CLI
3123
+ ## CQRS & Pipelines
3124
+
3125
+ Cross-cutting behavior can be composed around commands and queries:
3126
+
3127
+ \`\`\`typescript
3128
+ .addPipeline((config) => {
3129
+ config.authorization.policies = {
3130
+ FIND_USER_QUERY_HANDLER: {
3131
+ roles: ['admin'],
3132
+ permissions: ['read'],
3133
+ },
3134
+ }
3135
+
3136
+ config.commandBus.idempotency = {
3137
+ lockTtlSeconds: 30,
3138
+ processedTtlSeconds: 60,
3139
+ }
3140
+
3141
+ config.commandBus.concurrency = {
3142
+ delayConfig: {
3143
+ baseDelayMs: 100,
3144
+ maxJitterMs: 500,
3145
+ },
3146
+ maxRetries: 3,
3147
+ }
2855
3148
 
2856
- Xeno includes an official CLI tool, \`@xeno-js/cli\`, designed to bootstrap your
2857
- new application in seconds. It offers an interactive setup to select exactly the
2858
- modules you need (Database, HTTP, Auth, Logging, etc.), ensuring you start with
2859
- a clean, pre-configured architecture tailored to your specific requirements.
3149
+ config.queryBus.isEnabled = true
3150
+ })
3151
+ \`\`\`
2860
3152
 
2861
- If you want to learn how to use it, see the full options available, or
2862
- understand how the scaffolding engine works, check the
2863
- **[CLI Documentation](https://www.xeno-js.it/cli/overview)**.
3153
+ The exact pipeline configuration depends on the integrations enabled by your
3154
+ application.
2864
3155
 
2865
3156
  ---
2866
3157
 
2867
- ## \u{1F91D} For Contributors
3158
+ ## Infrastructure & Integrations
2868
3159
 
2869
- We welcome contributions to Xeno! To maintain the highest code quality and
2870
- stability of the core framework, **direct pushes to the \`main\` and \`develop\`
2871
- branches are strictly prohibited.** Please follow this Git Flow to contribute:
3160
+ Xeno Core can be composed with infrastructure such as:
2872
3161
 
2873
- 1. **Branch off from \`develop\`**: Create a new branch for your feature or
2874
- bugfix.
3162
+ - **Database:** Drizzle ORM, PostgreSQL, LibSQL
3163
+ - **Cache / distributed coordination:** Redis
3164
+ - **Authentication:** Supabase integrations and custom strategies
3165
+ - **HTTP clients:** Axios
3166
+ - **Resilience:** Cockatiel
3167
+ - **Logging:** Console, Pino, Sentry, or custom loggers
3168
+ - **Validation:** Zod
2875
3169
 
2876
- \`\`\`bash
2877
- git checkout develop
2878
- git pull origin develop
2879
- git checkout -b feat/your-awesome-feature
2880
- \`\`\`
3170
+ These integrations are opt-in rather than mandatory parts of the application
3171
+ architecture.
3172
+
3173
+ ---
2881
3174
 
2882
- 2. **Make your changes**: Write your code and ensure it passes all local checks
2883
- (linting, types, and tests).
3175
+ ## CLI
3176
+
3177
+ Use the official CLI to scaffold a Xeno application:
2884
3178
 
2885
3179
  \`\`\`bash
2886
- npm run check
2887
- \`\`\`
3180
+ npm install @xeno-js / cli
3181
+ xeno - js new my - xeno - app--core
3182
+ \`\`\`
2888
3183
 
2889
- 3. **Commit your changes**: We enforce
2890
- [Conventional Commits](https://www.conventionalcommits.org/). Husky will
2891
- verify your commit message format.
3184
+ See the [CLI documentation](https://www.xeno-js.it/cli/overview).
2892
3185
 
2893
- 4. **Commit Format:**
3186
+ ---
2894
3187
 
2895
- \`\`\`bash
2896
- feat(scope): add new feature
2897
- fix(scope): resolve bug
2898
- chore(scope): update dependencies
2899
- \`\`\`
3188
+ ## Documentation
2900
3189
 
2901
- 5. **Submit a Pull Request (PR)**: Push your branch to GitHub and open a Pull
2902
- Request targeting the **\`develop\`** branch.
3190
+ The documentation hub contains the architecture and integration guides:
2903
3191
 
2904
- 6. **Review**: The repository owner will review your code, run pipeline tests,
2905
- and merge it into \`develop\`.
3192
+ **[xeno-js.it](https://www.xeno-js.it/introduction)**
2906
3193
 
2907
- _Note: The \`main\` branch is strictly reserved for production releases. Code
2908
- flows from feature branches \u27A1\uFE0F \`develop\` \u27A1\uFE0F \`main\`._
3194
+ Recommended starting points:
2909
3195
 
2910
- ### Scripts
3196
+ - [Introduction](https://www.xeno-js.it/introduction)
3197
+ - [Fundamentals](https://www.xeno-js.it/docs/core/overview)
3198
+ - [Dependency Injection](https://www.xeno-js.it/docs/core/fundamentals/app-builder)
3199
+ - [CQRS](https://www.xeno-js.it/docs/core/fundamentals/command-query)
3200
+ - [Pipelines](https://www.xeno-js.it/docs/core/cqrs/overview)
3201
+ - [Request Lifecycle](https://www.xeno-js.it/docs/core/fundamentals/service-container)
3202
+ - [Middleware](https://www.xeno-js.it/docs/core/fundamentals/middleware)
3203
+ - [CLI](https://www.xeno-js.it/docs/cli/overview)
2911
3204
 
2912
- | Command | Description |
2913
- | ----------------------- | ---------------------------------------------- |
2914
- | \`npm run build\` | Builds the TypeScript source code into \`dist/\` |
2915
- | \`npm run typecheck\` | Checks types without emitting files |
2916
- | \`npm run lint\` | Runs ESLint |
2917
- | \`npm run format\` | Formats code with Prettier |
2918
- | \`npm run test\` | Runs the Vitest test suite |
2919
- | \`npm run test:coverage\` | Runs tests and generates a coverage report |
3205
+ ---
2920
3206
 
2921
- ### Code Quality (Husky & Git Hooks)
3207
+ ## Ecosystem
2922
3208
 
2923
- This project strictly enforces code quality rules before pushing to the
2924
- repository:
3209
+ Xeno is designed as an ecosystem rather than a single monolithic package:
2925
3210
 
2926
- - **\`pre-commit\`**: Runs \`lint-staged\` on staged files (ESLint + Prettier).
2927
- - **\`commit-msg\`**: Checks commit messages with \`commitlint\` (we use
2928
- Conventional Commits).
2929
- - **\`pre-push\`**: Runs type checking, linting, and testing before code leaves
2930
- your machine.
3211
+ | Package | Role |
3212
+ | ----------------- | ----------------------------------------- |
3213
+ | \`@xeno-js / core\` | Application architecture and backend core |
3214
+ | \`@xeno-js / shared\` | Shared contracts and types |
3215
+ | \`@xeno-js / vue\` | Vue integration |
3216
+ | \`@xeno-js / cli\` | Project scaffolding and developer tooling |
2931
3217
 
2932
3218
  ---
2933
3219
 
2934
- ## \u{1F331} Support & Appreciation
3220
+ ## What Xeno Is Not
2935
3221
 
2936
- Building, benchmarking, and maintaining a progressive, enterprise-ready
2937
- open-source framework requires a massive amount of continuous dedication and
2938
- architectural engineering.
3222
+ Xeno is not primarily an HTTP framework.
2939
3223
 
2940
- If Xeno has brought value to your development workflows, helped decouple your
2941
- core business logic, or simplified your system infrastructure layout, consider
2942
- supporting its open-source lifecycle. Your backing directly accelerates our
2943
- strategic roadmap for new out-of-the-box transport integrations (such as gRPC,
2944
- RabbitMQ, and GraphQL) and keeps the documentation pristine.
3224
+ If you are looking for a framework centered on routing, controllers, middleware,
3225
+ and server lifecycle, there are excellent options already available in the
3226
+ Node.js ecosystem.
2945
3227
 
2946
- **Want to know how you can contribute or sponsor Xeno?** We rely on the
2947
- commitment of our community to keep the project independent and thriving.
2948
- Whether you are an individual developer or a business using Xeno, your support
2949
- makes a real difference.
3228
+ Xeno focuses on the layer above transport:
2950
3229
 
2951
- \u{1F449}
2952
- **[Read our support guidelines and find out how to help](https://www.xeno-js.it/support-us)**
3230
+ > **How should a TypeScript application be structured so that its business
3231
+ > logic, dependencies, and infrastructure boundaries remain explicit as the
3232
+ > application grows?**
2953
3233
 
2954
- Thank you for being part of this decoupled open-source journey!
3234
+ ---
2955
3235
 
2956
- <amp-bounce>
2957
- </amp-bounce>
2958
- <a href="https://www.buymeacoffee.com/xenojs" target="_blank">
2959
- <img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="42" style="height: 42px !important;" />
2960
- </a>
3236
+ ## Production Considerations
3237
+
3238
+ Xeno provides architectural primitives, but application correctness still
3239
+ depends on how those primitives are composed.
3240
+
3241
+ Before deploying an application, test the behaviors that matter to your
3242
+ workload, especially:
3243
+
3244
+ - request and transaction isolation
3245
+ - service lifetime boundaries
3246
+ - authorization policies
3247
+ - idempotency semantics
3248
+ - concurrency behavior
3249
+ - cache consistency
3250
+ - failure and retry behavior
3251
+ - trusted proxy / client IP configuration
3252
+ - database transaction boundaries
3253
+
3254
+ The framework is designed to make these boundaries explicit rather than hide
3255
+ them behind conventions.
2961
3256
 
2962
3257
  ---
2963
3258
 
2964
- ## \u{1F6E1}\uFE0F Powered by Xeno
2965
-
2966
- If you are using Xeno in your project, let the world know! Add this badge to
2967
- your README:
2968
-
2969
- \`\`\`html
2970
- <a
2971
- href="[https://github.com/xeno-js/xeno-js](https://github.com/xeno-js/xeno-js)"
2972
- target="_blank"
2973
- >
2974
- <img
2975
- src="[https://img.shields.io/badge/Powered%20by-Xeno-black?style=flat-square](https://img.shields.io/badge/Powered%20by-Xeno-black?style=flat-square)"
2976
- alt="Powered by Xeno"
2977
- height="20"
2978
- />
2979
- </a>
2980
- \`\`\`
3259
+ ## Contributing
2981
3260
 
2982
- ## \u{1F4C4} License
3261
+ Contributions are welcome.
2983
3262
 
2984
- Copyright (c) 2026 Xeno. Licensed under the [ISC License](LICENSE).
3263
+ Development happens from feature branches targeting \`develop\`.
2985
3264
 
2986
- `;
3265
+ \`\`\`bash
3266
+ git checkout develop
3267
+ git pull origin develop
3268
+ git checkout - b feat / your - feature
3269
+
3270
+ npm install
3271
+ npm run check
3272
+ \`\`\`
3273
+
3274
+ We use Conventional Commits:
3275
+
3276
+ \`\`\`bash
3277
+ feat(scope): add new feature
3278
+ fix(scope): resolve bug
3279
+ chore(scope): update dependencies
3280
+ \`\`\`
3281
+
3282
+ Before opening a pull request, run:
3283
+
3284
+ \`\`\`bash
3285
+ npm run check
3286
+ \`\`\`
3287
+
3288
+ | Command | Description |
3289
+ | ----------------------- | ------------------------ |
3290
+ | \`npm run build\` | Build the package |
3291
+ | \`npm run typecheck\` | TypeScript type checking |
3292
+ | \`npm run lint\` | ESLint |
3293
+ | \`npm run format: check\` | Prettier validation |
3294
+ | \`npm run test\` | Vitest test suite |
3295
+ | \`npm run test: coverage\` | Test suite with coverage |
3296
+
3297
+ ---
3298
+
3299
+ ## Support
3300
+
3301
+ If Xeno is useful to you, you can support the project through the community and
3302
+ sponsorship channels documented on the website:
3303
+
3304
+ **[Support Xeno](https://www.xeno-js.it/docs/support-us)**
3305
+
3306
+ ---
3307
+
3308
+ ## License
3309
+
3310
+ Copyright (c) 2026 Xeno.
3311
+
3312
+ Licensed under the [MIT License](LICENSE).
3313
+ `;
2987
3314
  const filePath = import_node_path25.default.join(projectPath, "readme");
2988
3315
  await this._fileService.writeFileRecursive(filePath, content);
2989
3316
  }
@@ -3050,13 +3377,12 @@ var init_tsconfig_generator2 = __esm({
3050
3377
  "src/infrastructure/generators/core/tsconfig.generator.ts"() {
3051
3378
  "use strict";
3052
3379
  import_node_path27 = __toESM(require("path"));
3053
- init_shared();
3054
3380
  TsconfigGenerator = class {
3055
3381
  constructor(_fileService) {
3056
3382
  this._fileService = _fileService;
3057
3383
  }
3058
3384
  _fileService;
3059
- async generate(projectPath, options) {
3385
+ async generate(projectPath, _options) {
3060
3386
  const tsconfig = {
3061
3387
  compilerOptions: {
3062
3388
  target: "ES2022",
@@ -3075,9 +3401,6 @@ var init_tsconfig_generator2 = __esm({
3075
3401
  },
3076
3402
  include: ["src/**/*.ts"]
3077
3403
  };
3078
- if (options.database === CORE_CONSTANTS.DRIZZLE || options.database === CORE_CONSTANTS.SQL_LITE) {
3079
- tsconfig.include.push("drizzle.config.ts");
3080
- }
3081
3404
  const filePath = import_node_path27.default.join(projectPath, "tsconfig.json");
3082
3405
  await this._fileService.writeFileRecursive(filePath, JSON.stringify(tsconfig, null, 2));
3083
3406
  }
@@ -3111,7 +3434,8 @@ var init_drizzle_generator = __esm({
3111
3434
  );
3112
3435
  }
3113
3436
  composeDrizzleConfig() {
3114
- return `import 'dotenv/config';
3437
+ return `/// <reference types="node" />
3438
+ import 'dotenv/config';
3115
3439
  import { defineConfig } from 'drizzle-kit';
3116
3440
 
3117
3441
  /**
@@ -3191,7 +3515,8 @@ var init_drizzle_sql_lite_generator = __esm({
3191
3515
  await this._fileService.writeFileRecursive(schemaPath, schemaContent);
3192
3516
  }
3193
3517
  composeDrizzleConfig() {
3194
- return `import 'dotenv/config';
3518
+ return `/// <reference types="node" />
3519
+ import 'dotenv/config';
3195
3520
  import { defineConfig } from 'drizzle-kit';
3196
3521
 
3197
3522
  /**
@@ -3400,7 +3725,7 @@ var CommandRunner = class {
3400
3725
  const child = (0, import_node_child_process.spawn)(cmd, args, {
3401
3726
  cwd,
3402
3727
  stdio: "inherit",
3403
- shell: false
3728
+ shell: isWin
3404
3729
  });
3405
3730
  child.on("error", (error) => {
3406
3731
  reject(new Error(`Error during execution of the command ${command}: ${error.message}`));
@@ -3420,11 +3745,16 @@ init_command_constants();
3420
3745
 
3421
3746
  // src/infrastructure/file/file.service.ts
3422
3747
  var import_promises = require("fs/promises");
3423
- var import_path = require("path");
3424
3748
  var FileUtils = class {
3425
3749
  async writeFileRecursive(filePath, content) {
3426
- const dir = (0, import_path.dirname)(filePath);
3427
- await (0, import_promises.mkdir)(dir, { recursive: true });
3750
+ try {
3751
+ await (0, import_promises.access)(filePath);
3752
+ throw new Error(`[Xeno CLI Error]: Aborted. File already exists at path: ${filePath}`);
3753
+ } catch (error) {
3754
+ if (error.code !== "ENOENT") {
3755
+ throw error;
3756
+ }
3757
+ }
3428
3758
  await (0, import_promises.writeFile)(filePath, content, "utf8");
3429
3759
  }
3430
3760
  };