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