eva4j 1.0.16 → 1.0.18

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 (151) hide show
  1. package/AGENTS.md +220 -5
  2. package/DOMAIN_YAML_GUIDE.md +188 -3
  3. package/FUTURE_FEATURES.md +33 -52
  4. package/QUICK_REFERENCE.md +8 -4
  5. package/bin/eva4j.js +70 -2
  6. package/config/defaults.json +1 -0
  7. package/docs/CAMUNDA_DMN_GUIDE.md +1380 -0
  8. package/docs/KAFKA_PRODUCTION_CONFIG.md +441 -0
  9. package/docs/RABBITMQ_PRODUCTION_CONFIG.md +227 -0
  10. package/docs/commands/ADD_RABBITMQ_CLIENT.md +192 -0
  11. package/docs/commands/EVALUATE_SYSTEM.md +290 -10
  12. package/docs/commands/GENERATE_RABBITMQ_EVENT.md +341 -0
  13. package/docs/commands/GENERATE_RABBITMQ_LISTENER.md +595 -0
  14. package/docs/commands/GENERATE_TEMPORAL_FLOW.md +52 -12
  15. package/docs/commands/INDEX.md +27 -3
  16. package/docs/prototype/TEMPORAL_COMMUNICATION_PATTERNS.md +731 -0
  17. package/docs/prototype/TEMPORAL_DESIGN_METHODOLOGY.md +740 -0
  18. package/docs/prototype/system/RISKS.md +277 -0
  19. package/docs/prototype/system/customers.yaml +133 -0
  20. package/docs/prototype/system/inventory.yaml +109 -0
  21. package/docs/prototype/system/notifications.yaml +131 -0
  22. package/docs/prototype/system/orders.yaml +241 -0
  23. package/docs/prototype/system/payments.yaml +256 -0
  24. package/docs/prototype/system/products.yaml +168 -0
  25. package/docs/prototype/system/system.yaml +269 -0
  26. package/examples/domain-endpoints-multi-aggregate.yaml +140 -0
  27. package/examples/domain-events.yaml +26 -0
  28. package/examples/domain-read-models.yaml +113 -0
  29. package/examples/system/customer.yaml +89 -0
  30. package/examples/system/orders.yaml +119 -0
  31. package/examples/system/product.yaml +27 -0
  32. package/examples/system/system.yaml +80 -0
  33. package/package.json +1 -1
  34. package/read-model-spec.md +664 -0
  35. package/src/agents/design-gap-analyst-temporal.agent.md +452 -0
  36. package/src/agents/design-gap-analyst.agent.md +383 -0
  37. package/src/agents/design-reviewer-temporal.agent.md +412 -0
  38. package/src/agents/design-reviewer.agent.md +34 -5
  39. package/src/agents/implement-use-cases.prompt.md +179 -0
  40. package/src/agents/ux-gap-analyst.agent.md +412 -0
  41. package/src/commands/add-rabbitmq-client.js +261 -0
  42. package/src/commands/add-temporal-client.js +22 -2
  43. package/src/commands/build.js +267 -11
  44. package/src/commands/evaluate-system.js +700 -13
  45. package/src/commands/generate-entities.js +560 -24
  46. package/src/commands/generate-http-exchange.js +3 -0
  47. package/src/commands/generate-kafka-event.js +3 -0
  48. package/src/commands/generate-kafka-listener.js +3 -0
  49. package/src/commands/generate-rabbitmq-event.js +665 -0
  50. package/src/commands/generate-rabbitmq-listener.js +205 -0
  51. package/src/commands/generate-record.js +2 -2
  52. package/src/commands/generate-resource.js +4 -1
  53. package/src/commands/generate-temporal-activity.js +970 -33
  54. package/src/commands/generate-temporal-flow.js +98 -38
  55. package/src/commands/generate-temporal-system.js +708 -0
  56. package/src/commands/generate-usecase.js +4 -1
  57. package/src/skills/build-system-yaml/SKILL.md +343 -2
  58. package/src/skills/build-system-yaml/references/domain-yaml-spec.md +253 -26
  59. package/src/skills/build-system-yaml/references/module-spec.md +90 -9
  60. package/src/skills/build-system-yaml/references/system-yaml-spec.md +36 -0
  61. package/src/skills/build-temporal-system/SKILL.md +752 -0
  62. package/src/skills/build-temporal-system/references/temporal-communication-patterns.md +167 -0
  63. package/src/skills/build-temporal-system/references/temporal-domain-yaml-spec.md +449 -0
  64. package/src/skills/build-temporal-system/references/temporal-module-spec.md +353 -0
  65. package/src/skills/build-temporal-system/references/temporal-system-yaml-spec.md +326 -0
  66. package/src/skills/implement-use-case/SKILL.md +350 -0
  67. package/src/skills/implement-use-case/references/use-case-patterns.md +980 -0
  68. package/src/skills/requirements-elicitation/SKILL.md +228 -0
  69. package/src/skills/requirements-elicitation/references/interview-framework.md +260 -0
  70. package/src/skills/requirements-elicitation/references/output-templates.md +368 -0
  71. package/src/utils/bounded-context-diagram.js +844 -0
  72. package/src/utils/config-manager.js +4 -2
  73. package/src/utils/domain-validator.js +495 -17
  74. package/src/utils/naming.js +20 -0
  75. package/src/utils/system-validator.js +169 -11
  76. package/src/utils/system-yaml-parser.js +318 -0
  77. package/src/utils/temporal-validator.js +497 -0
  78. package/src/utils/validator.js +3 -1
  79. package/src/utils/yaml-to-entity.js +281 -9
  80. package/templates/aggregate/AggregateRepository.java.ejs +4 -0
  81. package/templates/aggregate/AggregateRepositoryImpl.java.ejs +8 -0
  82. package/templates/aggregate/AggregateRoot.java.ejs +38 -4
  83. package/templates/aggregate/DomainEventHandler.java.ejs +116 -22
  84. package/templates/aggregate/JpaAggregateRoot.java.ejs +4 -4
  85. package/templates/aggregate/JpaEntity.java.ejs +2 -2
  86. package/templates/base/docker/rabbitmq-services.yaml.ejs +12 -0
  87. package/templates/base/resources/parameters/develop/kafka.yaml.ejs +5 -0
  88. package/templates/base/resources/parameters/develop/rabbitmq.yaml.ejs +15 -0
  89. package/templates/base/resources/parameters/develop/temporal.yaml.ejs +0 -3
  90. package/templates/base/resources/parameters/local/kafka.yaml.ejs +5 -0
  91. package/templates/base/resources/parameters/local/rabbitmq.yaml.ejs +15 -0
  92. package/templates/base/resources/parameters/local/temporal.yaml.ejs +0 -3
  93. package/templates/base/resources/parameters/production/kafka.yaml.ejs +39 -8
  94. package/templates/base/resources/parameters/production/rabbitmq.yaml.ejs +32 -0
  95. package/templates/base/resources/parameters/production/temporal.yaml.ejs +0 -3
  96. package/templates/base/resources/parameters/test/kafka.yaml.ejs +12 -6
  97. package/templates/base/resources/parameters/test/rabbitmq.yaml.ejs +15 -0
  98. package/templates/base/resources/parameters/test/temporal.yaml.ejs +0 -3
  99. package/templates/base/root/AGENTS.md.ejs +1 -1
  100. package/templates/crud/DeleteCommandHandler.java.ejs +19 -1
  101. package/templates/crud/EndpointsController.java.ejs +1 -1
  102. package/templates/crud/ScaffoldCommand.java.ejs +5 -2
  103. package/templates/crud/ScaffoldCommandHandler.java.ejs +3 -1
  104. package/templates/crud/ScaffoldQuery.java.ejs +5 -2
  105. package/templates/crud/ScaffoldQueryHandler.java.ejs +3 -1
  106. package/templates/crud/SubEntityRemoveCommand.java.ejs +1 -1
  107. package/templates/crud/UpdateCommandHandler.java.ejs +53 -2
  108. package/templates/evaluate/report.html.ejs +1447 -90
  109. package/templates/kafka-event/KafkaConfigBean.java.ejs +1 -1
  110. package/templates/kafka-event/KafkaMessageBroker.java.ejs +3 -3
  111. package/templates/ports/PortAclMapper.java.ejs +35 -0
  112. package/templates/ports/PortFeignAdapter.java.ejs +7 -22
  113. package/templates/ports/PortFeignClient.java.ejs +4 -0
  114. package/templates/ports/PortResponseDto.java.ejs +1 -1
  115. package/templates/rabbitmq-event/RabbitConfigBean.java.ejs +33 -0
  116. package/templates/rabbitmq-event/RabbitConfigExchange.java.ejs +12 -0
  117. package/templates/rabbitmq-event/RabbitMessageBroker.java.ejs +35 -0
  118. package/templates/rabbitmq-event/RabbitMessageBrokerMethod.java.ejs +9 -0
  119. package/templates/rabbitmq-listener/RabbitConfigConsumerBean.java.ejs +33 -0
  120. package/templates/rabbitmq-listener/RabbitConfigConsumerExchange.java.ejs +12 -0
  121. package/templates/rabbitmq-listener/RabbitListenerClass.java.ejs +82 -0
  122. package/templates/rabbitmq-listener/RabbitListenerSimple.java.ejs +56 -0
  123. package/templates/read-model/ReadModelDomain.java.ejs +46 -0
  124. package/templates/read-model/ReadModelJpa.java.ejs +58 -0
  125. package/templates/read-model/ReadModelJpaRepository.java.ejs +13 -0
  126. package/templates/read-model/ReadModelKafkaListener.java.ejs +64 -0
  127. package/templates/read-model/ReadModelRabbitListener.java.ejs +71 -0
  128. package/templates/read-model/ReadModelRepository.java.ejs +42 -0
  129. package/templates/read-model/ReadModelRepositoryImpl.java.ejs +85 -0
  130. package/templates/read-model/ReadModelSyncHandler.java.ejs +54 -0
  131. package/templates/shared/configurations/kafkaConfig/KafkaConfig.java.ejs +18 -4
  132. package/templates/shared/configurations/rabbitmqConfig/RabbitMQConfig.java.ejs +100 -0
  133. package/templates/shared/configurations/temporalConfig/TemporalConfig.java.ejs +2 -64
  134. package/templates/shared/configurations/temporalConfig/TemporalWorkerFactoryLifecycle.java.ejs +41 -0
  135. package/templates/temporal-activity/ActivityImpl.java.ejs +68 -2
  136. package/templates/temporal-activity/ActivityInput.java.ejs +14 -0
  137. package/templates/temporal-activity/ActivityInterface.java.ejs +7 -1
  138. package/templates/temporal-activity/ActivityOutput.java.ejs +14 -0
  139. package/templates/temporal-activity/NestedType.java.ejs +12 -0
  140. package/templates/temporal-activity/SharedActivityInput.java.ejs +14 -0
  141. package/templates/temporal-activity/SharedActivityInterface.java.ejs +15 -0
  142. package/templates/temporal-activity/SharedActivityOutput.java.ejs +14 -0
  143. package/templates/temporal-activity/SharedNestedType.java.ejs +12 -0
  144. package/templates/temporal-flow/ModuleHeavyActivity.java.ejs +6 -0
  145. package/templates/temporal-flow/ModuleLightActivity.java.ejs +6 -0
  146. package/templates/temporal-flow/ModuleTemporalWorkerConfig.java.ejs +58 -0
  147. package/templates/temporal-flow/WorkFlowImpl.java.ejs +172 -12
  148. package/templates/temporal-flow/WorkFlowInput.java.ejs +11 -0
  149. package/templates/temporal-flow/WorkFlowInterface.java.ejs +5 -4
  150. package/templates/temporal-flow/WorkFlowService.java.ejs +42 -12
  151. package/COMMAND_EVALUATION.md +0 -911
@@ -5,7 +5,7 @@ const path = require('path');
5
5
  const fs = require('fs-extra');
6
6
  const ConfigManager = require('../utils/config-manager');
7
7
  const { isEva4jProject, moduleExists } = require('../utils/validator');
8
- const { toPackagePath, toPascalCase } = require('../utils/naming');
8
+ const { toPackagePath, toPascalCase, toCamelCase, toScreamingSnakeCase } = require('../utils/naming');
9
9
  const { renderAndWrite } = require('../utils/template-engine');
10
10
  const ChecksumManager = require('../utils/checksum-manager');
11
11
 
@@ -48,6 +48,9 @@ async function generateTemporalFlowCommand(moduleName, flowName, options = {}) {
48
48
  const { packageName } = projectConfig;
49
49
  const packagePath = toPackagePath(packageName);
50
50
 
51
+ // Normalise module name to camelCase (system.yaml uses kebab-case, .eva4j.json stores camelCase)
52
+ moduleName = toCamelCase(moduleName);
53
+
51
54
  if (!(await configManager.moduleExists(moduleName))) {
52
55
  console.error(chalk.red(`āŒ Module '${moduleName}' does not exist`));
53
56
  console.error(chalk.gray(`Create it first using: eva add module ${moduleName}`));
@@ -72,6 +75,8 @@ async function generateTemporalFlowCommand(moduleName, flowName, options = {}) {
72
75
  }
73
76
 
74
77
  const flowPascalCase = toPascalCase(flowName);
78
+ const modulePascalCase = toPascalCase(moduleName);
79
+ const moduleScreamingSnake = toScreamingSnakeCase(moduleName);
75
80
 
76
81
  const moduleBasePath = path.join(
77
82
  projectDir,
@@ -92,6 +97,9 @@ async function generateTemporalFlowCommand(moduleName, flowName, options = {}) {
92
97
  packageName,
93
98
  moduleName,
94
99
  flowPascalCase,
100
+ modulePascalCase,
101
+ moduleCamelCase: moduleName,
102
+ moduleScreamingSnake,
95
103
  };
96
104
 
97
105
  const templatesDir = path.join(__dirname, '..', '..', 'templates', 'temporal-flow');
@@ -125,9 +133,42 @@ async function generateTemporalFlowCommand(moduleName, flowName, options = {}) {
125
133
  writeOptions
126
134
  );
127
135
 
128
- // 4. Register workflow implementation in TemporalConfig.java
129
- spinner.text = 'Registering workflow in TemporalConfig...';
130
- await registerWorkflowInTemporalConfig(projectDir, packagePath, packageName, moduleName, flowPascalCase);
136
+ // 4. Generate module-scoped marker interfaces (idempotent)
137
+ spinner.text = `Generating ${modulePascalCase} Temporal interfaces...`;
138
+ const interfacesDir = path.join(moduleBasePath, 'domain', 'interfaces');
139
+ await renderAndWrite(
140
+ path.join(templatesDir, 'ModuleHeavyActivity.java.ejs'),
141
+ path.join(interfacesDir, `${modulePascalCase}HeavyActivity.java`),
142
+ context,
143
+ writeOptions
144
+ );
145
+ await renderAndWrite(
146
+ path.join(templatesDir, 'ModuleLightActivity.java.ejs'),
147
+ path.join(interfacesDir, `${modulePascalCase}LightActivity.java`),
148
+ context,
149
+ writeOptions
150
+ );
151
+
152
+ // 5. Generate ModuleTemporalWorkerConfig (idempotent)
153
+ spinner.text = `Generating ${modulePascalCase}TemporalWorkerConfig...`;
154
+ const configDir = path.join(moduleBasePath, 'infrastructure', 'configurations');
155
+ await renderAndWrite(
156
+ path.join(templatesDir, 'ModuleTemporalWorkerConfig.java.ejs'),
157
+ path.join(configDir, `${modulePascalCase}TemporalWorkerConfig.java`),
158
+ context,
159
+ writeOptions
160
+ );
161
+
162
+ // 6. Register workflow in module worker config
163
+ spinner.text = 'Registering workflow in module worker config...';
164
+ await registerWorkflowInModuleWorkerConfig(
165
+ path.join(configDir, `${modulePascalCase}TemporalWorkerConfig.java`),
166
+ packageName, moduleName, flowPascalCase, modulePascalCase
167
+ );
168
+
169
+ // 7. Append module queue section to temporal.yaml (idempotent)
170
+ spinner.text = 'Updating temporal.yaml with module queues...';
171
+ await appendModuleQueues(projectDir, moduleName, moduleScreamingSnake);
131
172
 
132
173
  spinner.succeed(chalk.green(`āœ… ${flowPascalCase}WorkFlow generated successfully`));
133
174
 
@@ -135,8 +176,13 @@ async function generateTemporalFlowCommand(moduleName, flowName, options = {}) {
135
176
  console.log(chalk.gray(` ${moduleName}/application/usecases/${flowPascalCase}WorkFlow.java`));
136
177
  console.log(chalk.gray(` ${moduleName}/application/usecases/${flowPascalCase}WorkFlowImpl.java`));
137
178
  console.log(chalk.gray(` ${moduleName}/application/usecases/${flowPascalCase}WorkFlowService.java`));
138
- console.log(chalk.blue('\nšŸ“ Updated files:'));
139
- console.log(chalk.gray(' shared/infrastructure/configurations/temporalConfig/TemporalConfig.java'));
179
+ console.log(chalk.gray(` ${moduleName}/domain/interfaces/${modulePascalCase}HeavyActivity.java`));
180
+ console.log(chalk.gray(` ${moduleName}/domain/interfaces/${modulePascalCase}LightActivity.java`));
181
+ console.log(chalk.gray(` ${moduleName}/infrastructure/configurations/${modulePascalCase}TemporalWorkerConfig.java`));
182
+ console.log(chalk.blue('\nšŸ“ Queue names:'));
183
+ console.log(chalk.gray(` Flow: ${moduleScreamingSnake}_WORKFLOW_QUEUE`));
184
+ console.log(chalk.gray(` Heavy: ${moduleScreamingSnake}_HEAVY_TASK_QUEUE`));
185
+ console.log(chalk.gray(` Light: ${moduleScreamingSnake}_LIGHT_TASK_QUEUE`));
140
186
 
141
187
  await checksumManager.save();
142
188
  } catch (error) {
@@ -146,22 +192,9 @@ async function generateTemporalFlowCommand(moduleName, flowName, options = {}) {
146
192
  }
147
193
  }
148
194
 
149
- async function registerWorkflowInTemporalConfig(projectDir, packagePath, packageName, moduleName, flowPascalCase) {
150
- const configPath = path.join(
151
- projectDir,
152
- 'src',
153
- 'main',
154
- 'java',
155
- packagePath,
156
- 'shared',
157
- 'infrastructure',
158
- 'configurations',
159
- 'temporalConfig',
160
- 'TemporalConfig.java'
161
- );
162
-
195
+ async function registerWorkflowInModuleWorkerConfig(configPath, packageName, moduleName, flowPascalCase, modulePascalCase) {
163
196
  if (!(await fs.pathExists(configPath))) {
164
- console.warn(chalk.yellow('\nāš ļø TemporalConfig.java not found — skipping auto-registration'));
197
+ console.warn(chalk.yellow(`\nāš ļø ${modulePascalCase}TemporalWorkerConfig.java not found — skipping auto-registration`));
165
198
  return;
166
199
  }
167
200
 
@@ -172,40 +205,31 @@ async function registerWorkflowInTemporalConfig(projectDir, packagePath, package
172
205
 
173
206
  // Add import if not already present
174
207
  if (!content.includes(importLine)) {
175
- const importBlockMatch = content.match(/^import .+;(\r?\n)/m);
176
- if (importBlockMatch) {
177
- // Find position after last import line
178
- const allImports = [...content.matchAll(/^import .+;/gm)];
179
- if (allImports.length > 0) {
180
- const lastImport = allImports[allImports.length - 1];
181
- const insertPos = lastImport.index + lastImport[0].length;
182
- content = content.slice(0, insertPos) + '\n' + importLine + content.slice(insertPos);
183
- }
208
+ const allImports = [...content.matchAll(/^import .+;/gm)];
209
+ if (allImports.length > 0) {
210
+ const lastImport = allImports[allImports.length - 1];
211
+ const insertPos = lastImport.index + lastImport[0].length;
212
+ content = content.slice(0, insertPos) + '\n' + importLine + content.slice(insertPos);
184
213
  }
185
214
  }
186
215
 
187
- // Remove TODO comment and commented-out example placeholder
188
- content = content.replace(/[ \t]*\/\/ TODO: register your workflow implementation types here\r?\n/g, '');
189
- content = content.replace(/[ \t]*\/\/ workflowWorker\.registerWorkflowImplementationTypes\(MyWorkflowImpl\.class\);\r?\n?/g, '');
190
-
191
216
  // Check if there is already an active registerWorkflowImplementationTypes call
192
217
  const activeRegisterRegex = /workflowWorker\.registerWorkflowImplementationTypes\(([^)]+)\);/;
193
218
  if (activeRegisterRegex.test(content)) {
194
- // Add the new class, filtering out the stale MyWorkflowImpl example if still present
195
219
  content = content.replace(activeRegisterRegex, (match, classes) => {
196
220
  const classList = classes
197
221
  .split(',')
198
222
  .map((c) => c.trim())
199
- .filter((c) => c !== 'MyWorkflowImpl.class');
223
+ .filter((c) => c.length > 0);
200
224
  if (!classList.includes(`${implClass}.class`)) {
201
225
  classList.push(`${implClass}.class`);
202
226
  }
203
227
  return `workflowWorker.registerWorkflowImplementationTypes(${classList.join(', ')});`;
204
228
  });
205
229
  } else {
206
- // Insert active registration after the workflowWorker declaration line
230
+ // Insert active registration after the comment marker
207
231
  content = content.replace(
208
- /(Worker workflowWorker = workerFactory\.newWorker\([^;]+;\r?\n)/,
232
+ /(\/\/ registered by eva g temporal-flow\r?\n)/,
209
233
  `$1 workflowWorker.registerWorkflowImplementationTypes(${implClass}.class);\n`
210
234
  );
211
235
  }
@@ -213,4 +237,40 @@ async function registerWorkflowInTemporalConfig(projectDir, packagePath, package
213
237
  await fs.writeFile(configPath, content, 'utf-8');
214
238
  }
215
239
 
240
+ /**
241
+ * Append module-specific queue configuration to temporal.yaml files (idempotent)
242
+ */
243
+ async function appendModuleQueues(projectDir, moduleName, moduleScreamingSnake) {
244
+ const environments = ['local', 'develop', 'test', 'production'];
245
+ const resourcesDir = path.join(projectDir, 'src', 'main', 'resources', 'parameters');
246
+
247
+ const moduleSection = [
248
+ ` ${moduleName}:`,
249
+ ` flow-queue: ${moduleScreamingSnake}_WORKFLOW_QUEUE`,
250
+ ` heavy-queue: ${moduleScreamingSnake}_HEAVY_TASK_QUEUE`,
251
+ ` light-queue: ${moduleScreamingSnake}_LIGHT_TASK_QUEUE`,
252
+ ].join('\n');
253
+
254
+ for (const env of environments) {
255
+ const yamlPath = path.join(resourcesDir, env, 'temporal.yaml');
256
+
257
+ if (!(await fs.pathExists(yamlPath))) continue;
258
+
259
+ let content = await fs.readFile(yamlPath, 'utf-8');
260
+
261
+ // Skip if module already registered
262
+ if (content.includes(`${moduleName}:`)) continue;
263
+
264
+ // Add modules: section if not present
265
+ if (!content.includes('modules:')) {
266
+ content = content.trimEnd() + '\n modules:\n' + moduleSection + '\n';
267
+ } else {
268
+ // Append under existing modules: section
269
+ content = content.trimEnd() + '\n' + moduleSection + '\n';
270
+ }
271
+
272
+ await fs.writeFile(yamlPath, content, 'utf-8');
273
+ }
274
+ }
275
+
216
276
  module.exports = generateTemporalFlowCommand;