@gravito/scaffold 3.2.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -6
- package/dist/index.cjs +491 -87
- package/dist/index.d.ts +240 -3
- package/dist/index.js +491 -87
- package/package.json +5 -2
- package/dist/index.d.cts +0 -686
package/dist/index.cjs
CHANGED
|
@@ -51,6 +51,11 @@ var DependencyValidator = class _DependencyValidator {
|
|
|
51
51
|
* Driver 到 Package 的映射規則
|
|
52
52
|
*/
|
|
53
53
|
static DRIVER_DEPENDENCIES = [
|
|
54
|
+
{
|
|
55
|
+
driver: "none",
|
|
56
|
+
requiredPackages: [],
|
|
57
|
+
description: "No database driver - developer to choose and install"
|
|
58
|
+
},
|
|
54
59
|
{
|
|
55
60
|
driver: "redis",
|
|
56
61
|
requiredPackages: ["@gravito/ion"],
|
|
@@ -527,12 +532,14 @@ var BaseGenerator = class {
|
|
|
527
532
|
templateManager;
|
|
528
533
|
fileMerger;
|
|
529
534
|
filesCreated = [];
|
|
535
|
+
context = null;
|
|
530
536
|
constructor(config) {
|
|
531
537
|
this.config = config;
|
|
532
538
|
this.templateManager = new TemplateManager(config.templatesDir);
|
|
533
539
|
this.fileMerger = new FileMerger();
|
|
534
540
|
}
|
|
535
541
|
async generate(context) {
|
|
542
|
+
this.context = context;
|
|
536
543
|
this.filesCreated = [];
|
|
537
544
|
const structure = this.getDirectoryStructure(context);
|
|
538
545
|
await this.createStructure(context.targetDir, structure, context);
|
|
@@ -768,6 +775,22 @@ describe('Example Test', () => {
|
|
|
768
775
|
|
|
769
776
|
// src/utils/ConfigGenerator.ts
|
|
770
777
|
var ConfigGenerator = class {
|
|
778
|
+
/**
|
|
779
|
+
* Generate database configuration based on driver type
|
|
780
|
+
*/
|
|
781
|
+
static generateDatabaseConfig(driver) {
|
|
782
|
+
switch (driver) {
|
|
783
|
+
case "none":
|
|
784
|
+
return this.generateNoDatabaseConfig();
|
|
785
|
+
case "sqlite":
|
|
786
|
+
return this.generateSimpleDatabaseConfig();
|
|
787
|
+
case "postgresql":
|
|
788
|
+
case "mysql":
|
|
789
|
+
return this.generateDetailedDatabaseConfig();
|
|
790
|
+
default:
|
|
791
|
+
return this.generateNoDatabaseConfig();
|
|
792
|
+
}
|
|
793
|
+
}
|
|
771
794
|
/**
|
|
772
795
|
* Generate app configuration (simple version for Clean Architecture)
|
|
773
796
|
*/
|
|
@@ -850,6 +873,58 @@ export default {
|
|
|
850
873
|
// 'AppServiceProvider',
|
|
851
874
|
],
|
|
852
875
|
}
|
|
876
|
+
`;
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Generate database configuration (no default driver - developer to choose)
|
|
880
|
+
*/
|
|
881
|
+
static generateNoDatabaseConfig() {
|
|
882
|
+
return `/**
|
|
883
|
+
* Database Configuration
|
|
884
|
+
*
|
|
885
|
+
* Configure your database connection below.
|
|
886
|
+
* Choose one of the following drivers by installing the required package:
|
|
887
|
+
*
|
|
888
|
+
* SQLite: bun add better-sqlite3
|
|
889
|
+
* PostgreSQL: bun add pg
|
|
890
|
+
* MySQL: bun add mysql2
|
|
891
|
+
*/
|
|
892
|
+
export default {
|
|
893
|
+
default: process.env.DB_CONNECTION ?? 'sqlite',
|
|
894
|
+
|
|
895
|
+
connections: {
|
|
896
|
+
sqlite: {
|
|
897
|
+
driver: 'sqlite',
|
|
898
|
+
database: process.env.DB_DATABASE ?? 'database/database.sqlite',
|
|
899
|
+
},
|
|
900
|
+
|
|
901
|
+
postgresql: {
|
|
902
|
+
driver: 'postgresql',
|
|
903
|
+
host: process.env.DB_HOST ?? 'localhost',
|
|
904
|
+
port: Number(process.env.DB_PORT ?? 5432),
|
|
905
|
+
database: process.env.DB_DATABASE ?? 'forge',
|
|
906
|
+
username: process.env.DB_USERNAME ?? 'forge',
|
|
907
|
+
password: process.env.DB_PASSWORD ?? '',
|
|
908
|
+
},
|
|
909
|
+
|
|
910
|
+
mysql: {
|
|
911
|
+
driver: 'mysql',
|
|
912
|
+
host: process.env.DB_HOST ?? 'localhost',
|
|
913
|
+
port: Number(process.env.DB_PORT ?? 3306),
|
|
914
|
+
database: process.env.DB_DATABASE ?? 'forge',
|
|
915
|
+
username: process.env.DB_USERNAME ?? 'forge',
|
|
916
|
+
password: process.env.DB_PASSWORD ?? '',
|
|
917
|
+
},
|
|
918
|
+
},
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* Migration settings
|
|
922
|
+
*/
|
|
923
|
+
migrations: {
|
|
924
|
+
table: 'migrations',
|
|
925
|
+
path: 'database/migrations',
|
|
926
|
+
},
|
|
927
|
+
}
|
|
853
928
|
`;
|
|
854
929
|
}
|
|
855
930
|
/**
|
|
@@ -980,6 +1055,118 @@ export default {
|
|
|
980
1055
|
}
|
|
981
1056
|
`;
|
|
982
1057
|
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Generate workers configuration for job queue system
|
|
1060
|
+
*/
|
|
1061
|
+
static generateWorkersConfig(level = "basic") {
|
|
1062
|
+
switch (level) {
|
|
1063
|
+
case "advanced":
|
|
1064
|
+
return this.generateAdvancedWorkersConfig();
|
|
1065
|
+
case "production":
|
|
1066
|
+
return this.generateProductionWorkersConfig();
|
|
1067
|
+
default:
|
|
1068
|
+
return this.generateBasicWorkersConfig();
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Generate basic workers configuration
|
|
1073
|
+
*/
|
|
1074
|
+
static generateBasicWorkersConfig() {
|
|
1075
|
+
return ` /**
|
|
1076
|
+
* Workers Configuration
|
|
1077
|
+
*
|
|
1078
|
+
* Manages job execution in isolated worker threads.
|
|
1079
|
+
* Automatically selects best available runtime (Bun or Node.js).
|
|
1080
|
+
*/
|
|
1081
|
+
workers: {
|
|
1082
|
+
// Runtime environment: 'auto' | 'bun' | 'node'
|
|
1083
|
+
runtime: process.env.WORKERS_RUNTIME as 'auto' | 'bun' | 'node' ?? 'auto',
|
|
1084
|
+
|
|
1085
|
+
pool: {
|
|
1086
|
+
poolSize: Number.parseInt(process.env.WORKERS_POOL_SIZE ?? '4', 10),
|
|
1087
|
+
minWorkers: Number.parseInt(process.env.WORKERS_MIN_WORKERS ?? '0', 10),
|
|
1088
|
+
healthCheckInterval: 30000,
|
|
1089
|
+
},
|
|
1090
|
+
|
|
1091
|
+
execution: {
|
|
1092
|
+
maxExecutionTime: Number.parseInt(process.env.WORKERS_MAX_EXECUTION_TIME ?? '30000', 10),
|
|
1093
|
+
maxMemory: Number.parseInt(process.env.WORKERS_MAX_MEMORY ?? '0', 10),
|
|
1094
|
+
idleTimeout: Number.parseInt(process.env.WORKERS_IDLE_TIMEOUT ?? '60000', 10),
|
|
1095
|
+
isolateContexts: process.env.WORKERS_ISOLATE_CONTEXTS === 'true',
|
|
1096
|
+
},
|
|
1097
|
+
},`;
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Generate advanced workers configuration with Bun optimizations
|
|
1101
|
+
*/
|
|
1102
|
+
static generateAdvancedWorkersConfig() {
|
|
1103
|
+
return ` /**
|
|
1104
|
+
* Workers Configuration (Advanced)
|
|
1105
|
+
*
|
|
1106
|
+
* Manages job execution in isolated worker threads.
|
|
1107
|
+
* Includes Bun-specific optimizations for enhanced performance.
|
|
1108
|
+
*
|
|
1109
|
+
* Performance characteristics:
|
|
1110
|
+
* - Bun: 2-241x faster message passing, 20-30% less memory (smol mode)
|
|
1111
|
+
* - Node.js: Stable, widely tested, compatible
|
|
1112
|
+
*/
|
|
1113
|
+
workers: {
|
|
1114
|
+
runtime: process.env.WORKERS_RUNTIME as 'auto' | 'bun' | 'node' ?? 'auto',
|
|
1115
|
+
|
|
1116
|
+
pool: {
|
|
1117
|
+
poolSize: Number.parseInt(process.env.WORKERS_POOL_SIZE ?? '4', 10),
|
|
1118
|
+
minWorkers: Number.parseInt(process.env.WORKERS_MIN_WORKERS ?? '1', 10),
|
|
1119
|
+
healthCheckInterval: 30000,
|
|
1120
|
+
},
|
|
1121
|
+
|
|
1122
|
+
execution: {
|
|
1123
|
+
maxExecutionTime: Number.parseInt(process.env.WORKERS_MAX_EXECUTION_TIME ?? '30000', 10),
|
|
1124
|
+
maxMemory: Number.parseInt(process.env.WORKERS_MAX_MEMORY ?? '0', 10),
|
|
1125
|
+
idleTimeout: Number.parseInt(process.env.WORKERS_IDLE_TIMEOUT ?? '60000', 10),
|
|
1126
|
+
isolateContexts: process.env.WORKERS_ISOLATE_CONTEXTS === 'true',
|
|
1127
|
+
},
|
|
1128
|
+
|
|
1129
|
+
// Bun-specific optimizations
|
|
1130
|
+
bun: {
|
|
1131
|
+
smol: process.env.WORKERS_BUN_SMOL === 'true',
|
|
1132
|
+
preload: process.env.WORKERS_BUN_PRELOAD
|
|
1133
|
+
? process.env.WORKERS_BUN_PRELOAD.split(',').map((p) => p.trim())
|
|
1134
|
+
: undefined,
|
|
1135
|
+
inspectPort: process.env.WORKERS_BUN_INSPECT_PORT
|
|
1136
|
+
? Number.parseInt(process.env.WORKERS_BUN_INSPECT_PORT, 10)
|
|
1137
|
+
: undefined,
|
|
1138
|
+
},
|
|
1139
|
+
},`;
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Generate production-optimized workers configuration
|
|
1143
|
+
*/
|
|
1144
|
+
static generateProductionWorkersConfig() {
|
|
1145
|
+
return ` /**
|
|
1146
|
+
* Workers Configuration (Production Optimized)
|
|
1147
|
+
*/
|
|
1148
|
+
workers: {
|
|
1149
|
+
runtime: 'auto' as const,
|
|
1150
|
+
|
|
1151
|
+
pool: {
|
|
1152
|
+
poolSize: Number.parseInt(process.env.WORKERS_POOL_SIZE ?? '8', 10),
|
|
1153
|
+
minWorkers: 2,
|
|
1154
|
+
healthCheckInterval: 30000,
|
|
1155
|
+
},
|
|
1156
|
+
|
|
1157
|
+
execution: {
|
|
1158
|
+
maxExecutionTime: 30000,
|
|
1159
|
+
maxMemory: Number.parseInt(process.env.WORKERS_MAX_MEMORY ?? '512', 10),
|
|
1160
|
+
idleTimeout: 60000,
|
|
1161
|
+
isolateContexts: false,
|
|
1162
|
+
},
|
|
1163
|
+
|
|
1164
|
+
bun: {
|
|
1165
|
+
smol: true,
|
|
1166
|
+
preload: process.env.WORKERS_BUN_PRELOAD?.split(',').map((p) => p.trim()),
|
|
1167
|
+
},
|
|
1168
|
+
},`;
|
|
1169
|
+
}
|
|
983
1170
|
};
|
|
984
1171
|
|
|
985
1172
|
// src/utils/ServiceProviderGenerator.ts
|
|
@@ -1385,7 +1572,8 @@ var CleanArchitectureGenerator = class extends BaseGenerator {
|
|
|
1385
1572
|
return ConfigGenerator.generateSimpleAppConfig(context);
|
|
1386
1573
|
}
|
|
1387
1574
|
generateDatabaseConfig() {
|
|
1388
|
-
|
|
1575
|
+
const driver = this.context?.profileConfig?.drivers?.database ?? "none";
|
|
1576
|
+
return ConfigGenerator.generateDatabaseConfig(driver);
|
|
1389
1577
|
}
|
|
1390
1578
|
generateAuthConfig() {
|
|
1391
1579
|
return ConfigGenerator.generateAuthConfig();
|
|
@@ -1993,7 +2181,9 @@ Created with \u2764\uFE0F using Gravito Framework
|
|
|
1993
2181
|
|
|
1994
2182
|
// src/generators/ddd/BootstrapGenerator.ts
|
|
1995
2183
|
var BootstrapGenerator = class {
|
|
2184
|
+
context = null;
|
|
1996
2185
|
generate(context) {
|
|
2186
|
+
this.context = context;
|
|
1997
2187
|
return {
|
|
1998
2188
|
type: "directory",
|
|
1999
2189
|
name: "Bootstrap",
|
|
@@ -2006,6 +2196,7 @@ var BootstrapGenerator = class {
|
|
|
2006
2196
|
};
|
|
2007
2197
|
}
|
|
2008
2198
|
generateConfigDirectory(context) {
|
|
2199
|
+
this.context = context;
|
|
2009
2200
|
return {
|
|
2010
2201
|
type: "directory",
|
|
2011
2202
|
name: "config",
|
|
@@ -2201,13 +2392,8 @@ export default {
|
|
|
2201
2392
|
`;
|
|
2202
2393
|
}
|
|
2203
2394
|
generateDatabaseConfig() {
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
connections: {
|
|
2207
|
-
sqlite: { driver: 'sqlite', database: 'database/database.sqlite' },
|
|
2208
|
-
},
|
|
2209
|
-
}
|
|
2210
|
-
`;
|
|
2395
|
+
const driver = this.context?.profileConfig?.drivers?.database ?? "none";
|
|
2396
|
+
return ConfigGenerator.generateDatabaseConfig(driver);
|
|
2211
2397
|
}
|
|
2212
2398
|
generateCacheConfig() {
|
|
2213
2399
|
return `export default {
|
|
@@ -2368,6 +2554,30 @@ var ModuleGenerator = class {
|
|
|
2368
2554
|
]
|
|
2369
2555
|
}
|
|
2370
2556
|
]
|
|
2557
|
+
},
|
|
2558
|
+
// UserInterface Layer
|
|
2559
|
+
{
|
|
2560
|
+
type: "directory",
|
|
2561
|
+
name: "UserInterface",
|
|
2562
|
+
children: [
|
|
2563
|
+
{
|
|
2564
|
+
type: "directory",
|
|
2565
|
+
name: "Http",
|
|
2566
|
+
children: [
|
|
2567
|
+
{
|
|
2568
|
+
type: "directory",
|
|
2569
|
+
name: "Controllers",
|
|
2570
|
+
children: [
|
|
2571
|
+
{
|
|
2572
|
+
type: "file",
|
|
2573
|
+
name: `${name}Controller.ts`,
|
|
2574
|
+
content: this.generateController(name)
|
|
2575
|
+
}
|
|
2576
|
+
]
|
|
2577
|
+
}
|
|
2578
|
+
]
|
|
2579
|
+
}
|
|
2580
|
+
]
|
|
2371
2581
|
}
|
|
2372
2582
|
]
|
|
2373
2583
|
};
|
|
@@ -2383,17 +2593,13 @@ import { ${name}Created } from '../../Events/${name}Created'
|
|
|
2383
2593
|
import { ${name}Status } from './${name}Status'
|
|
2384
2594
|
|
|
2385
2595
|
export interface ${name}Props {
|
|
2386
|
-
// Add properties here
|
|
2387
2596
|
status: ${name}Status
|
|
2388
2597
|
createdAt: Date
|
|
2389
2598
|
}
|
|
2390
2599
|
|
|
2391
2600
|
export class ${name} extends AggregateRoot<Id> {
|
|
2392
|
-
private props: ${name}Props
|
|
2393
|
-
|
|
2394
|
-
private constructor(id: Id, props: ${name}Props) {
|
|
2601
|
+
private constructor(id: Id, private props: ${name}Props) {
|
|
2395
2602
|
super(id)
|
|
2396
|
-
this.props = props
|
|
2397
2603
|
}
|
|
2398
2604
|
|
|
2399
2605
|
static create(id: Id): ${name} {
|
|
@@ -2411,7 +2617,12 @@ export class ${name} extends AggregateRoot<Id> {
|
|
|
2411
2617
|
return this.props.status
|
|
2412
2618
|
}
|
|
2413
2619
|
|
|
2414
|
-
|
|
2620
|
+
/**
|
|
2621
|
+
* Complete the ${name} process
|
|
2622
|
+
*/
|
|
2623
|
+
complete(): void {
|
|
2624
|
+
this.props.status = ${name}Status.COMPLETED
|
|
2625
|
+
}
|
|
2415
2626
|
}
|
|
2416
2627
|
`;
|
|
2417
2628
|
}
|
|
@@ -2436,17 +2647,13 @@ export enum ${name}Status {
|
|
|
2436
2647
|
import { DomainEvent } from '@gravito/enterprise'
|
|
2437
2648
|
|
|
2438
2649
|
export class ${name}Created extends DomainEvent {
|
|
2439
|
-
constructor(public readonly
|
|
2650
|
+
constructor(public readonly aggregateId: string) {
|
|
2440
2651
|
super()
|
|
2441
2652
|
}
|
|
2442
2653
|
|
|
2443
2654
|
override get eventName(): string {
|
|
2444
2655
|
return '${name.toLowerCase()}.created'
|
|
2445
2656
|
}
|
|
2446
|
-
|
|
2447
|
-
get aggregateId(): string {
|
|
2448
|
-
return this.${name.toLowerCase()}Id
|
|
2449
|
-
}
|
|
2450
2657
|
}
|
|
2451
2658
|
`;
|
|
2452
2659
|
}
|
|
@@ -2473,7 +2680,6 @@ import { Command } from '@gravito/enterprise'
|
|
|
2473
2680
|
|
|
2474
2681
|
export class Create${name}Command extends Command {
|
|
2475
2682
|
constructor(
|
|
2476
|
-
// Add command properties
|
|
2477
2683
|
public readonly id?: string
|
|
2478
2684
|
) {
|
|
2479
2685
|
super()
|
|
@@ -2528,13 +2734,15 @@ export class Get${name}ByIdQuery extends Query {
|
|
|
2528
2734
|
import { QueryHandler } from '@gravito/enterprise'
|
|
2529
2735
|
import type { I${name}Repository } from '../../../Domain/Repositories/I${name}Repository'
|
|
2530
2736
|
import type { ${name}DTO } from '../../DTOs/${name}DTO'
|
|
2737
|
+
import { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
|
|
2531
2738
|
import type { Get${name}ByIdQuery } from './Get${name}ByIdQuery'
|
|
2532
2739
|
|
|
2533
2740
|
export class Get${name}ByIdHandler implements QueryHandler<Get${name}ByIdQuery, ${name}DTO | null> {
|
|
2534
2741
|
constructor(private repository: I${name}Repository) {}
|
|
2535
2742
|
|
|
2536
2743
|
async handle(query: Get${name}ByIdQuery): Promise<${name}DTO | null> {
|
|
2537
|
-
const
|
|
2744
|
+
const id = Id.from(query.id)
|
|
2745
|
+
const aggregate = await this.repository.findById(id)
|
|
2538
2746
|
if (!aggregate) return null
|
|
2539
2747
|
|
|
2540
2748
|
return {
|
|
@@ -2555,7 +2763,6 @@ import type { ${name}Status } from '../../Domain/Aggregates/${name}/${name}Statu
|
|
|
2555
2763
|
export interface ${name}DTO {
|
|
2556
2764
|
id: string
|
|
2557
2765
|
status: ${name}Status
|
|
2558
|
-
// Add more fields
|
|
2559
2766
|
}
|
|
2560
2767
|
`;
|
|
2561
2768
|
}
|
|
@@ -2566,29 +2773,29 @@ export interface ${name}DTO {
|
|
|
2566
2773
|
|
|
2567
2774
|
import type { ${name} } from '../../Domain/Aggregates/${name}/${name}'
|
|
2568
2775
|
import type { I${name}Repository } from '../../Domain/Repositories/I${name}Repository'
|
|
2569
|
-
import
|
|
2570
|
-
|
|
2571
|
-
const store = new Map<string, ${name}>()
|
|
2776
|
+
import { Id } from '../../../../../Shared/Domain/ValueObjects/Id'
|
|
2572
2777
|
|
|
2573
2778
|
export class ${name}Repository implements I${name}Repository {
|
|
2779
|
+
private store = new Map<string, ${name}>()
|
|
2780
|
+
|
|
2574
2781
|
async findById(id: Id): Promise<${name} | null> {
|
|
2575
|
-
return store.get(id.value) ?? null
|
|
2782
|
+
return this.store.get(id.value) ?? null
|
|
2576
2783
|
}
|
|
2577
2784
|
|
|
2578
2785
|
async save(aggregate: ${name}): Promise<void> {
|
|
2579
|
-
store.set(aggregate.id.value, aggregate)
|
|
2786
|
+
this.store.set(aggregate.id.value, aggregate)
|
|
2580
2787
|
}
|
|
2581
2788
|
|
|
2582
2789
|
async delete(id: Id): Promise<void> {
|
|
2583
|
-
store.delete(id.value)
|
|
2790
|
+
this.store.delete(id.value)
|
|
2584
2791
|
}
|
|
2585
2792
|
|
|
2586
2793
|
async findAll(): Promise<${name}[]> {
|
|
2587
|
-
return Array.from(store.values())
|
|
2794
|
+
return Array.from(this.store.values())
|
|
2588
2795
|
}
|
|
2589
2796
|
|
|
2590
2797
|
async exists(id: Id): Promise<boolean> {
|
|
2591
|
-
return store.has(id.value)
|
|
2798
|
+
return this.store.has(id.value)
|
|
2592
2799
|
}
|
|
2593
2800
|
}
|
|
2594
2801
|
`;
|
|
@@ -2610,6 +2817,37 @@ export class ${name}ServiceProvider extends ServiceProvider {
|
|
|
2610
2817
|
console.log('[${name}] Module loaded')
|
|
2611
2818
|
}
|
|
2612
2819
|
}
|
|
2820
|
+
`;
|
|
2821
|
+
}
|
|
2822
|
+
generateController(name) {
|
|
2823
|
+
return `/**
|
|
2824
|
+
* ${name} Controller
|
|
2825
|
+
*/
|
|
2826
|
+
|
|
2827
|
+
import type { GravitoContext } from '@gravito/core'
|
|
2828
|
+
|
|
2829
|
+
export class ${name}Controller {
|
|
2830
|
+
/**
|
|
2831
|
+
* GET /${name.toLowerCase()}
|
|
2832
|
+
*/
|
|
2833
|
+
async index(ctx: GravitoContext) {
|
|
2834
|
+
return ctx.json({
|
|
2835
|
+
success: true,
|
|
2836
|
+
data: []
|
|
2837
|
+
})
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
/**
|
|
2841
|
+
* GET /${name.toLowerCase()}/:id
|
|
2842
|
+
*/
|
|
2843
|
+
async show(ctx: GravitoContext) {
|
|
2844
|
+
const id = ctx.req.param('id')
|
|
2845
|
+
return ctx.json({
|
|
2846
|
+
success: true,
|
|
2847
|
+
data: { id }
|
|
2848
|
+
})
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2613
2851
|
`;
|
|
2614
2852
|
}
|
|
2615
2853
|
};
|
|
@@ -2784,24 +3022,39 @@ export class Email extends ValueObject<EmailProps> {
|
|
|
2784
3022
|
|
|
2785
3023
|
import type { DomainEvent } from '@gravito/enterprise'
|
|
2786
3024
|
|
|
2787
|
-
type EventHandler = (event:
|
|
3025
|
+
type EventHandler<T extends DomainEvent = any> = (event: T) => void | Promise<void>
|
|
2788
3026
|
|
|
2789
3027
|
export class EventDispatcher {
|
|
2790
3028
|
private handlers: Map<string, EventHandler[]> = new Map()
|
|
2791
3029
|
|
|
2792
|
-
|
|
3030
|
+
/**
|
|
3031
|
+
* Subscribe to an event
|
|
3032
|
+
*/
|
|
3033
|
+
subscribe<T extends DomainEvent>(eventName: string, handler: EventHandler<T>): void {
|
|
2793
3034
|
const handlers = this.handlers.get(eventName) ?? []
|
|
2794
3035
|
handlers.push(handler)
|
|
2795
3036
|
this.handlers.set(eventName, handlers)
|
|
2796
3037
|
}
|
|
2797
3038
|
|
|
3039
|
+
/**
|
|
3040
|
+
* Dispatch a single event
|
|
3041
|
+
*/
|
|
2798
3042
|
async dispatch(event: DomainEvent): Promise<void> {
|
|
2799
3043
|
const handlers = this.handlers.get(event.eventName) ?? []
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
3044
|
+
const promises = handlers.map(handler => {
|
|
3045
|
+
try {
|
|
3046
|
+
return handler(event)
|
|
3047
|
+
} catch (error) {
|
|
3048
|
+
console.error(\`[EventDispatcher] Error in handler for \${event.eventName}:\`, error)
|
|
3049
|
+
}
|
|
3050
|
+
})
|
|
3051
|
+
|
|
3052
|
+
await Promise.all(promises)
|
|
2803
3053
|
}
|
|
2804
3054
|
|
|
3055
|
+
/**
|
|
3056
|
+
* Dispatch multiple events sequentially
|
|
3057
|
+
*/
|
|
2805
3058
|
async dispatchAll(events: DomainEvent[]): Promise<void> {
|
|
2806
3059
|
for (const event of events) {
|
|
2807
3060
|
await this.dispatch(event)
|
|
@@ -3194,7 +3447,8 @@ describe('Example Test', () => {
|
|
|
3194
3447
|
return ConfigGenerator.generateDetailedAppConfig(context);
|
|
3195
3448
|
}
|
|
3196
3449
|
generateDatabaseConfig() {
|
|
3197
|
-
|
|
3450
|
+
const driver = this.context?.profileConfig?.drivers?.database ?? "none";
|
|
3451
|
+
return ConfigGenerator.generateDatabaseConfig(driver);
|
|
3198
3452
|
}
|
|
3199
3453
|
generateAuthConfig() {
|
|
3200
3454
|
return `/**
|
|
@@ -3910,9 +4164,9 @@ var SatelliteGenerator = class extends BaseGenerator {
|
|
|
3910
4164
|
children: [
|
|
3911
4165
|
{
|
|
3912
4166
|
type: "directory",
|
|
3913
|
-
name: "
|
|
4167
|
+
name: "Aggregates",
|
|
3914
4168
|
children: [
|
|
3915
|
-
{ type: "file", name: `${name}.ts`, content: this.
|
|
4169
|
+
{ type: "file", name: `${name}.ts`, content: this.generateAggregate(name) }
|
|
3916
4170
|
]
|
|
3917
4171
|
},
|
|
3918
4172
|
{
|
|
@@ -3926,8 +4180,24 @@ var SatelliteGenerator = class extends BaseGenerator {
|
|
|
3926
4180
|
}
|
|
3927
4181
|
]
|
|
3928
4182
|
},
|
|
3929
|
-
{
|
|
3930
|
-
|
|
4183
|
+
{
|
|
4184
|
+
type: "directory",
|
|
4185
|
+
name: "ValueObjects",
|
|
4186
|
+
children: [
|
|
4187
|
+
{ type: "file", name: `${name}Id.ts`, content: this.generateIdValueObject(name) }
|
|
4188
|
+
]
|
|
4189
|
+
},
|
|
4190
|
+
{
|
|
4191
|
+
type: "directory",
|
|
4192
|
+
name: "Events",
|
|
4193
|
+
children: [
|
|
4194
|
+
{
|
|
4195
|
+
type: "file",
|
|
4196
|
+
name: `${name}Created.ts`,
|
|
4197
|
+
content: this.generateCreatedEvent(name)
|
|
4198
|
+
}
|
|
4199
|
+
]
|
|
4200
|
+
}
|
|
3931
4201
|
]
|
|
3932
4202
|
},
|
|
3933
4203
|
// Application Layer
|
|
@@ -3968,6 +4238,31 @@ var SatelliteGenerator = class extends BaseGenerator {
|
|
|
3968
4238
|
}
|
|
3969
4239
|
]
|
|
3970
4240
|
},
|
|
4241
|
+
// Interface Layer (HTTP/API)
|
|
4242
|
+
{
|
|
4243
|
+
type: "directory",
|
|
4244
|
+
name: "Interface",
|
|
4245
|
+
children: [
|
|
4246
|
+
{
|
|
4247
|
+
type: "directory",
|
|
4248
|
+
name: "Http",
|
|
4249
|
+
children: [
|
|
4250
|
+
{
|
|
4251
|
+
type: "directory",
|
|
4252
|
+
name: "Controllers",
|
|
4253
|
+
children: [
|
|
4254
|
+
{
|
|
4255
|
+
type: "file",
|
|
4256
|
+
name: `${name}Controller.ts`,
|
|
4257
|
+
content: this.generateController(name)
|
|
4258
|
+
}
|
|
4259
|
+
]
|
|
4260
|
+
},
|
|
4261
|
+
{ type: "directory", name: "Middleware", children: [] }
|
|
4262
|
+
]
|
|
4263
|
+
}
|
|
4264
|
+
]
|
|
4265
|
+
},
|
|
3971
4266
|
// Entry Point
|
|
3972
4267
|
{ type: "file", name: "index.ts", content: this.generateEntryPoint(name) },
|
|
3973
4268
|
{
|
|
@@ -3985,13 +4280,12 @@ var SatelliteGenerator = class extends BaseGenerator {
|
|
|
3985
4280
|
{
|
|
3986
4281
|
type: "file",
|
|
3987
4282
|
name: "unit.test.ts",
|
|
3988
|
-
content:
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
});`
|
|
4283
|
+
content: this.generateUnitTest(name)
|
|
4284
|
+
},
|
|
4285
|
+
{
|
|
4286
|
+
type: "file",
|
|
4287
|
+
name: "integration.test.ts",
|
|
4288
|
+
content: this.generateIntegrationTest(name)
|
|
3995
4289
|
}
|
|
3996
4290
|
]
|
|
3997
4291
|
}
|
|
@@ -4000,35 +4294,76 @@ describe("${name}", () => {
|
|
|
4000
4294
|
// ─────────────────────────────────────────────────────────────
|
|
4001
4295
|
// Domain Templates
|
|
4002
4296
|
// ─────────────────────────────────────────────────────────────
|
|
4003
|
-
|
|
4004
|
-
return `import {
|
|
4297
|
+
generateIdValueObject(name) {
|
|
4298
|
+
return `import { ValueObject } from '@gravito/enterprise'
|
|
4299
|
+
|
|
4300
|
+
interface IdProps {
|
|
4301
|
+
value: string
|
|
4302
|
+
}
|
|
4303
|
+
|
|
4304
|
+
export class ${name}Id extends ValueObject<IdProps> {
|
|
4305
|
+
constructor(value: string) {
|
|
4306
|
+
super({ value })
|
|
4307
|
+
}
|
|
4308
|
+
|
|
4309
|
+
static create(): ${name}Id {
|
|
4310
|
+
return new ${name}Id(crypto.randomUUID())
|
|
4311
|
+
}
|
|
4312
|
+
|
|
4313
|
+
get value(): string { return this.props.value }
|
|
4314
|
+
}
|
|
4315
|
+
`;
|
|
4316
|
+
}
|
|
4317
|
+
generateAggregate(name) {
|
|
4318
|
+
return `import { AggregateRoot } from '@gravito/enterprise'
|
|
4319
|
+
import { ${name}Id } from '../ValueObjects/${name}Id'
|
|
4320
|
+
import { ${name}Created } from '../Events/${name}Created'
|
|
4005
4321
|
|
|
4006
4322
|
export interface ${name}Props {
|
|
4007
4323
|
name: string
|
|
4008
4324
|
createdAt: Date
|
|
4009
4325
|
}
|
|
4010
4326
|
|
|
4011
|
-
export class ${name} extends
|
|
4012
|
-
constructor(id:
|
|
4327
|
+
export class ${name} extends AggregateRoot<${name}Id> {
|
|
4328
|
+
constructor(id: ${name}Id, private props: ${name}Props) {
|
|
4013
4329
|
super(id)
|
|
4014
4330
|
}
|
|
4015
4331
|
|
|
4016
|
-
static create(id:
|
|
4017
|
-
|
|
4332
|
+
static create(id: ${name}Id, name: string): ${name} {
|
|
4333
|
+
const aggregate = new ${name}(id, {
|
|
4018
4334
|
name,
|
|
4019
4335
|
createdAt: new Date()
|
|
4020
4336
|
})
|
|
4337
|
+
|
|
4338
|
+
aggregate.addDomainEvent(new ${name}Created(id.value))
|
|
4339
|
+
|
|
4340
|
+
return aggregate
|
|
4021
4341
|
}
|
|
4022
4342
|
|
|
4023
4343
|
get name() { return this.props.name }
|
|
4024
4344
|
}
|
|
4345
|
+
`;
|
|
4346
|
+
}
|
|
4347
|
+
generateCreatedEvent(name) {
|
|
4348
|
+
return `import { DomainEvent } from '@gravito/enterprise'
|
|
4349
|
+
|
|
4350
|
+
export class ${name}Created extends DomainEvent {
|
|
4351
|
+
constructor(public readonly aggregateId: string) {
|
|
4352
|
+
super()
|
|
4353
|
+
}
|
|
4354
|
+
|
|
4355
|
+
get eventName(): string {
|
|
4356
|
+
return '${this.context?.nameKebabCase}.created'
|
|
4357
|
+
}
|
|
4358
|
+
}
|
|
4025
4359
|
`;
|
|
4026
4360
|
}
|
|
4027
4361
|
generateRepositoryInterface(name) {
|
|
4028
4362
|
return `import { Repository } from '@gravito/enterprise'
|
|
4029
|
-
import { ${name} } from '../
|
|
4363
|
+
import { ${name} } from '../Aggregates/${name}'
|
|
4364
|
+
import { ${name}Id } from '../ValueObjects/${name}Id'
|
|
4030
4365
|
|
|
4031
|
-
export interface I${name}Repository extends Repository<${name},
|
|
4366
|
+
export interface I${name}Repository extends Repository<${name}, ${name}Id> {
|
|
4032
4367
|
// Add custom methods here
|
|
4033
4368
|
}
|
|
4034
4369
|
`;
|
|
@@ -4039,7 +4374,8 @@ export interface I${name}Repository extends Repository<${name}, string> {
|
|
|
4039
4374
|
generateUseCase(name) {
|
|
4040
4375
|
return `import { UseCase } from '@gravito/enterprise'
|
|
4041
4376
|
import { I${name}Repository } from '../../Domain/Contracts/I${name}Repository'
|
|
4042
|
-
import { ${name} } from '../../Domain/
|
|
4377
|
+
import { ${name} } from '../../Domain/Aggregates/${name}'
|
|
4378
|
+
import { ${name}Id } from '../../Domain/ValueObjects/${name}Id'
|
|
4043
4379
|
|
|
4044
4380
|
export interface Create${name}Input {
|
|
4045
4381
|
name: string
|
|
@@ -4051,12 +4387,12 @@ export class Create${name} extends UseCase<Create${name}Input, string> {
|
|
|
4051
4387
|
}
|
|
4052
4388
|
|
|
4053
4389
|
async execute(input: Create${name}Input): Promise<string> {
|
|
4054
|
-
const id =
|
|
4390
|
+
const id = ${name}Id.create()
|
|
4055
4391
|
const entity = ${name}.create(id, input.name)
|
|
4056
4392
|
|
|
4057
4393
|
await this.repository.save(entity)
|
|
4058
4394
|
|
|
4059
|
-
return id
|
|
4395
|
+
return id.value
|
|
4060
4396
|
}
|
|
4061
4397
|
}
|
|
4062
4398
|
`;
|
|
@@ -4066,17 +4402,16 @@ export class Create${name} extends UseCase<Create${name}Input, string> {
|
|
|
4066
4402
|
// ─────────────────────────────────────────────────────────────
|
|
4067
4403
|
generateAtlasRepository(name) {
|
|
4068
4404
|
return `import { I${name}Repository } from '../../Domain/Contracts/I${name}Repository'
|
|
4069
|
-
import { ${name} } from '../../Domain/
|
|
4070
|
-
import {
|
|
4405
|
+
import { ${name} } from '../../Domain/Aggregates/${name}'
|
|
4406
|
+
import { ${name}Id } from '../../Domain/ValueObjects/${name}Id'
|
|
4071
4407
|
|
|
4072
4408
|
export class Atlas${name}Repository implements I${name}Repository {
|
|
4073
4409
|
async save(entity: ${name}): Promise<void> {
|
|
4074
|
-
//
|
|
4075
|
-
console.log('[Atlas] Saving
|
|
4076
|
-
// await DB.table('${name.toLowerCase()}s').insert({ ... })
|
|
4410
|
+
// Implementation using @gravito/atlas
|
|
4411
|
+
console.log('[Atlas] Saving aggregate:', entity.id.value)
|
|
4077
4412
|
}
|
|
4078
4413
|
|
|
4079
|
-
async findById(id:
|
|
4414
|
+
async findById(id: ${name}Id): Promise<${name} | null> {
|
|
4080
4415
|
return null
|
|
4081
4416
|
}
|
|
4082
4417
|
|
|
@@ -4084,12 +4419,77 @@ export class Atlas${name}Repository implements I${name}Repository {
|
|
|
4084
4419
|
return []
|
|
4085
4420
|
}
|
|
4086
4421
|
|
|
4087
|
-
async delete(id:
|
|
4422
|
+
async delete(id: ${name}Id): Promise<void> {}
|
|
4088
4423
|
|
|
4089
|
-
async exists(id:
|
|
4424
|
+
async exists(id: ${name}Id): Promise<boolean> {
|
|
4090
4425
|
return false
|
|
4091
4426
|
}
|
|
4092
4427
|
}
|
|
4428
|
+
`;
|
|
4429
|
+
}
|
|
4430
|
+
// ─────────────────────────────────────────────────────────────
|
|
4431
|
+
// Test Templates
|
|
4432
|
+
// ─────────────────────────────────────────────────────────────
|
|
4433
|
+
generateUnitTest(name) {
|
|
4434
|
+
return `import { describe, it, expect } from "bun:test";
|
|
4435
|
+
import { ${name} } from "../src/Domain/Aggregates/${name}";
|
|
4436
|
+
import { ${name}Id } from "../src/Domain/ValueObjects/${name}Id";
|
|
4437
|
+
|
|
4438
|
+
describe("${name} Aggregate", () => {
|
|
4439
|
+
it("should create a new aggregate with a domain event", () => {
|
|
4440
|
+
const id = ${name}Id.create();
|
|
4441
|
+
const aggregate = ${name}.create(id, "Test Name");
|
|
4442
|
+
|
|
4443
|
+
expect(aggregate.id).toBe(id);
|
|
4444
|
+
expect(aggregate.name).toBe("Test Name");
|
|
4445
|
+
expect(aggregate.pullDomainEvents()).toHaveLength(1);
|
|
4446
|
+
});
|
|
4447
|
+
});`;
|
|
4448
|
+
}
|
|
4449
|
+
generateIntegrationTest(name) {
|
|
4450
|
+
return `import { describe, it, expect, beforeAll } from "bun:test";
|
|
4451
|
+
import { PlanetCore } from "@gravito/core";
|
|
4452
|
+
|
|
4453
|
+
describe("${name} Integration", () => {
|
|
4454
|
+
let core: PlanetCore;
|
|
4455
|
+
|
|
4456
|
+
beforeAll(async () => {
|
|
4457
|
+
core = new PlanetCore();
|
|
4458
|
+
// Setup dependencies here
|
|
4459
|
+
});
|
|
4460
|
+
|
|
4461
|
+
it("should handle the creation flow", async () => {
|
|
4462
|
+
expect(true).toBe(true); // Placeholder for actual integration logic
|
|
4463
|
+
});
|
|
4464
|
+
});`;
|
|
4465
|
+
}
|
|
4466
|
+
// ─────────────────────────────────────────────────────────────
|
|
4467
|
+
// Interface Templates
|
|
4468
|
+
// ─────────────────────────────────────────────────────────────
|
|
4469
|
+
generateController(name) {
|
|
4470
|
+
return `import type { GravitoContext } from '@gravito/core'
|
|
4471
|
+
import { Create${name} } from '../../../Application/UseCases/Create${name}'
|
|
4472
|
+
|
|
4473
|
+
export class ${name}Controller {
|
|
4474
|
+
constructor(private createUseCase: Create${name}) {}
|
|
4475
|
+
|
|
4476
|
+
async store(ctx: GravitoContext) {
|
|
4477
|
+
const body = await ctx.req.json()
|
|
4478
|
+
const id = await this.createUseCase.execute({ name: body.name })
|
|
4479
|
+
|
|
4480
|
+
return ctx.json({
|
|
4481
|
+
success: true,
|
|
4482
|
+
data: { id }
|
|
4483
|
+
}, 201)
|
|
4484
|
+
}
|
|
4485
|
+
|
|
4486
|
+
async index(ctx: GravitoContext) {
|
|
4487
|
+
return ctx.json({
|
|
4488
|
+
success: true,
|
|
4489
|
+
data: []
|
|
4490
|
+
})
|
|
4491
|
+
}
|
|
4492
|
+
}
|
|
4093
4493
|
`;
|
|
4094
4494
|
}
|
|
4095
4495
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -4098,17 +4498,22 @@ export class Atlas${name}Repository implements I${name}Repository {
|
|
|
4098
4498
|
generateEntryPoint(name) {
|
|
4099
4499
|
return `import { ServiceProvider, type Container } from '@gravito/core'
|
|
4100
4500
|
import { Atlas${name}Repository } from './Infrastructure/Persistence/Atlas${name}Repository'
|
|
4501
|
+
import { Create${name} } from './Application/UseCases/Create${name}'
|
|
4502
|
+
import { ${name}Controller } from './Interface/Http/Controllers/${name}Controller'
|
|
4101
4503
|
|
|
4102
4504
|
export class ${name}ServiceProvider extends ServiceProvider {
|
|
4103
4505
|
register(container: Container): void {
|
|
4104
|
-
// Bind Repository
|
|
4506
|
+
// 1. Bind Repository (Infrastructure)
|
|
4105
4507
|
container.singleton('${name.toLowerCase()}.repo', () => new Atlas${name}Repository())
|
|
4106
4508
|
|
|
4107
|
-
// Bind UseCases
|
|
4108
|
-
container.singleton('${name.toLowerCase()}.create', () => {
|
|
4109
|
-
return new
|
|
4110
|
-
|
|
4111
|
-
|
|
4509
|
+
// 2. Bind UseCases (Application)
|
|
4510
|
+
container.singleton('${name.toLowerCase()}.usecase.create', (c) => {
|
|
4511
|
+
return new Create${name}(c.make('${name.toLowerCase()}.repo'))
|
|
4512
|
+
})
|
|
4513
|
+
|
|
4514
|
+
// 3. Bind Controllers (Interface)
|
|
4515
|
+
container.singleton('${name.toLowerCase()}.controller', (c) => {
|
|
4516
|
+
return new ${name}Controller(c.make('${name.toLowerCase()}.usecase.create'))
|
|
4112
4517
|
})
|
|
4113
4518
|
}
|
|
4114
4519
|
|
|
@@ -4213,13 +4618,15 @@ var ProfileResolver = class _ProfileResolver {
|
|
|
4213
4618
|
static DEFAULTS = {
|
|
4214
4619
|
core: {
|
|
4215
4620
|
drivers: {
|
|
4216
|
-
database: "
|
|
4621
|
+
database: "none",
|
|
4217
4622
|
cache: "memory",
|
|
4218
4623
|
queue: "sync",
|
|
4219
4624
|
storage: "local",
|
|
4220
4625
|
session: "file"
|
|
4221
4626
|
},
|
|
4222
|
-
features: []
|
|
4627
|
+
features: [],
|
|
4628
|
+
workers: "basic"
|
|
4629
|
+
// Basic workers configuration for core profile
|
|
4223
4630
|
},
|
|
4224
4631
|
scale: {
|
|
4225
4632
|
drivers: {
|
|
@@ -4229,7 +4636,9 @@ var ProfileResolver = class _ProfileResolver {
|
|
|
4229
4636
|
storage: "s3",
|
|
4230
4637
|
session: "redis"
|
|
4231
4638
|
},
|
|
4232
|
-
features: ["stream", "nebula"]
|
|
4639
|
+
features: ["stream", "nebula"],
|
|
4640
|
+
workers: "advanced"
|
|
4641
|
+
// Advanced workers with Bun optimizations
|
|
4233
4642
|
},
|
|
4234
4643
|
enterprise: {
|
|
4235
4644
|
drivers: {
|
|
@@ -4239,14 +4648,17 @@ var ProfileResolver = class _ProfileResolver {
|
|
|
4239
4648
|
storage: "s3",
|
|
4240
4649
|
session: "redis"
|
|
4241
4650
|
},
|
|
4242
|
-
features: ["stream", "nebula", "monitor", "sentinel", "fortify"]
|
|
4651
|
+
features: ["stream", "nebula", "monitor", "sentinel", "fortify"],
|
|
4652
|
+
workers: "production"
|
|
4653
|
+
// Production-optimized workers
|
|
4243
4654
|
}
|
|
4244
4655
|
};
|
|
4245
4656
|
resolve(profile = "core", withFeatures = []) {
|
|
4246
4657
|
const base = _ProfileResolver.DEFAULTS[profile] || _ProfileResolver.DEFAULTS.core;
|
|
4247
4658
|
const config = {
|
|
4248
4659
|
drivers: { ...base.drivers },
|
|
4249
|
-
features: [...base.features]
|
|
4660
|
+
features: [...base.features],
|
|
4661
|
+
workers: base.workers
|
|
4250
4662
|
};
|
|
4251
4663
|
for (const feature of withFeatures) {
|
|
4252
4664
|
this.applyFeature(config, feature);
|
|
@@ -4513,16 +4925,8 @@ describe('Example Test', () => {
|
|
|
4513
4925
|
`;
|
|
4514
4926
|
}
|
|
4515
4927
|
generateDatabaseConfig() {
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
connections: {
|
|
4519
|
-
sqlite: {
|
|
4520
|
-
driver: 'sqlite',
|
|
4521
|
-
database: process.env.DB_DATABASE ?? 'database/database.sqlite',
|
|
4522
|
-
},
|
|
4523
|
-
},
|
|
4524
|
-
}
|
|
4525
|
-
`;
|
|
4928
|
+
const driver = this.context?.profileConfig?.drivers?.database ?? "none";
|
|
4929
|
+
return ConfigGenerator.generateDatabaseConfig(driver);
|
|
4526
4930
|
}
|
|
4527
4931
|
// ─────────────────────────────────────────────────────────────
|
|
4528
4932
|
// Model Generators
|