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
package/AGENTS.md CHANGED
@@ -625,6 +625,7 @@ Si un evento **no declara `triggers`** ni `lifecycle`, el desarrollador debe lla
625
625
  - **C2-008** (error): valor de `lifecycle` inválido (no es `create`, `update`, `delete` ni `softDelete`)
626
626
  - **C2-009** (warning): `lifecycle: softDelete` sin `hasSoftDelete: true` en la entidad raíz, o `lifecycle: delete` con `hasSoftDelete: true`
627
627
  - **C2-010** (error): campo de lifecycle event no existe en la entidad raíz del agregado (excluyendo `{entityName}Id` y campos `*At` + `LocalDateTime`)
628
+ - **C2-012** (error): nombre del agregado no coincide con la entidad raíz — el generador usa `aggregate.name` para imports/mappers pero la clase de dominio se genera desde `entity.name`, causando `cannot find symbol`
628
629
 
629
630
  **Auto-wiring de broker:** Si el proyecto tiene un broker de mensajería instalado (`eva add kafka-client`), `eva g entities` genera automáticamente la capa de Integration Events para **todos** los eventos declarados — sin necesidad de ejecutar `eva g kafka-event` por separado:
630
631
 
@@ -1443,6 +1444,7 @@ private String customerId;
1443
1444
  5. **SABER** que cualquier otro nombre genera un **scaffold** con `UnsupportedOperationException` — el desarrollador debe implementar el handler
1444
1445
  6. **APLICAR** la regla anti-duplicado: si el mismo useCase aparece en v1 y v2, se genera solo una vez
1445
1446
  7. **NOMBRAR** los controladores según la convención: `{Aggregate}{VersionCapitalized}Controller` (ej: `OrderV1Controller`)
1447
+ 8. **SI** el módulo tiene **2+ agregados** → usar `basePath: ""` (string vacío, **NO** `basePath: /`) y paths absolutos por operación (ej: `/products`, `/categories/{id}`). Si tiene un solo agregado → `basePath: /recurso` con paths relativos
1446
1448
 
1447
1449
  ### Al Generar Código de Dominio
1448
1450
 
@@ -2337,7 +2337,7 @@ public void removeOrderItem(OrderItem orderItem) {
2337
2337
 
2338
2338
  **Genera en JPA:**
2339
2339
  ```java
2340
- @OneToMany(mappedBy = "order", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}, fetch = FetchType.LAZY)
2340
+ @OneToMany(mappedBy = "order", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}, orphanRemoval = true, fetch = FetchType.LAZY)
2341
2341
  @Builder.Default
2342
2342
  private List<OrderItemJpa> orderItems = new ArrayList<>();
2343
2343
  ```
@@ -2565,6 +2565,8 @@ relationships:
2565
2565
  | `DETACH` | Al separar el padre, separa los hijos | ⚠️ Rara vez necesario |
2566
2566
  | `ALL` | Todas las operaciones anteriores | ⚠️ Solo si estás seguro |
2567
2567
 
2568
+ > **Nota:** eva4j genera automáticamente `orphanRemoval = true` en todas las relaciones `@OneToMany` y `@OneToOne(mappedBy)`. Esto asegura que al remover un hijo de la colección del padre, JPA elimina la fila de la base de datos. Es el comportamiento correcto en DDD: las entidades secundarias del agregado no tienen existencia independiente — su ciclo de vida lo controla la raíz.
2569
+
2568
2570
  #### **Configuraciones Recomendadas:**
2569
2571
 
2570
2572
  ```yaml
@@ -120,13 +120,17 @@ eva detach order
120
120
 
121
121
  ## 🧩 Temporal Queue Model
122
122
 
123
+ Queues are **module-scoped** — each module gets its own set of queues prefixed with the module name in SCREAMING_SNAKE_CASE:
124
+
123
125
  | Queue | Purpose | ActivityOptions var |
124
126
  |-------|---------|---------------------|
125
- | `FLOW_QUEUE` | Workflow orchestration | — |
126
- | `LIGHT_TASK_QUEUE` | Fast activities < 30 s | `lightActivityOptions` |
127
- | `HEAVY_TASK_QUEUE` | Long-running ≤ 2 min | `heavyActivityOptions` |
127
+ | `{MODULE}_WORKFLOW_QUEUE` | Workflow orchestration | — |
128
+ | `{MODULE}_LIGHT_TASK_QUEUE` | Fast activities < 30 s | `lightActivityOptions` |
129
+ | `{MODULE}_HEAVY_TASK_QUEUE` | Long-running ≤ 2 min | `heavyActivityOptions` |
130
+
131
+ Example for module `order`: `ORDER_WORKFLOW_QUEUE`, `ORDER_LIGHT_TASK_QUEUE`, `ORDER_HEAVY_TASK_QUEUE`.
128
132
 
129
- Activities are registered automatically via Spring DI (`LightActivity` / `HeavyActivity` marker interfaces). No manual `TemporalConfig.java` patching needed.
133
+ Activities are registered automatically via Spring DI (`{Module}LightActivity` / `{Module}HeavyActivity` marker interfaces). Each module has its own `{Module}TemporalWorkerConfig.java` — no shared `TemporalConfig.java` patching needed.
130
134
 
131
135
  ---
132
136
 
package/bin/eva4j.js CHANGED
@@ -6,16 +6,20 @@ const packageJson = require('../package.json');
6
6
  const createCommand = require('../src/commands/create');
7
7
  const addModuleCommand = require('../src/commands/add-module');
8
8
  const addKafkaClientCommand = require('../src/commands/add-kafka-client');
9
+ const addRabbitMQClientCommand = require('../src/commands/add-rabbitmq-client');
9
10
  const addTemporalClientCommand = require('../src/commands/add-temporal-client');
10
11
  const generateUsecaseCommand = require('../src/commands/generate-usecase');
11
12
  const generateHttpExchangeCommand = require('../src/commands/generate-http-exchange');
12
13
  const generateKafkaEventCommand = require('../src/commands/generate-kafka-event');
13
14
  const generateKafkaListenerCommand = require('../src/commands/generate-kafka-listener');
15
+ const generateRabbitMQEventCommand = require('../src/commands/generate-rabbitmq-event');
16
+ const generateRabbitMQListenerCommand = require('../src/commands/generate-rabbitmq-listener');
14
17
  const generateResourceCommand = require('../src/commands/generate-resource');
15
18
  const generateRecordCommand = require('../src/commands/generate-record');
16
19
  const generateEntitiesCommand = require('../src/commands/generate-entities');
17
20
  const generateTemporalFlowCommand = require('../src/commands/generate-temporal-flow');
18
21
  const generateTemporalActivityCommand = require('../src/commands/generate-temporal-activity');
22
+ const generateTemporalSystemCommand = require('../src/commands/generate-temporal-system');
19
23
  const buildCommand = require('../src/commands/build');
20
24
  const evaluateSystemCommand = require('../src/commands/evaluate-system');
21
25
  const infoCommand = require('../src/commands/info');
@@ -45,7 +49,7 @@ program
45
49
  // Add module command
46
50
  program
47
51
  .command('add <type> [name]')
48
- .description('Add components to the project. Use: module [name], kafka-client, temporal-client')
52
+ .description('Add components to the project. Use: module [name], kafka-client, rabbitmq-client, temporal-client')
49
53
  .action(async (type, name, options) => {
50
54
  if (type === 'kafka-client') {
51
55
  try {
@@ -57,6 +61,16 @@ program
57
61
  return;
58
62
  }
59
63
 
64
+ if (type === 'rabbitmq-client') {
65
+ try {
66
+ await addRabbitMQClientCommand(options);
67
+ } catch (error) {
68
+ console.error(chalk.red('Error:'), error.message);
69
+ process.exit(1);
70
+ }
71
+ return;
72
+ }
73
+
60
74
  if (type === 'temporal-client') {
61
75
  try {
62
76
  await addTemporalClientCommand(options);
@@ -72,6 +86,7 @@ program
72
86
  console.log(chalk.yellow('\nUsage:'));
73
87
  console.log(chalk.gray(' eva4j add module [module-name] # Interactive or with name'));
74
88
  console.log(chalk.gray(' eva4j add kafka-client'));
89
+ console.log(chalk.gray(' eva4j add rabbitmq-client'));
75
90
  console.log(chalk.gray(' eva4j add temporal-client'));
76
91
  console.log(chalk.gray('\nExamples:'));
77
92
  console.log(chalk.gray(' eva4j add module user'));
@@ -91,7 +106,7 @@ program
91
106
  program
92
107
  .command('generate <type> [module] [name]')
93
108
  .alias('g')
94
- .description('Generate components (usecase, http-exchange, kafka-event, kafka-listener, resource, record)')
109
+ .description('Generate components (usecase, http-exchange, kafka-event, kafka-listener, rabbitmq-event, rabbitmq-listener, resource, record)')
95
110
  .option('--force', 'Overwrite files even if they were manually modified (bypasses safe mode)')
96
111
  .action(async (type, module, name, options) => {
97
112
  if (type === 'usecase') {
@@ -166,6 +181,42 @@ program
166
181
  return;
167
182
  }
168
183
 
184
+ if (type === 'rabbitmq-event') {
185
+ if (!module) {
186
+ console.error(chalk.red('❌ Module name is required'));
187
+ console.log(chalk.gray('Usage: eva4j generate rabbitmq-event <module> [event-name]'));
188
+ console.log(chalk.gray('Examples:'));
189
+ console.log(chalk.gray(' eva4j generate rabbitmq-event user user-created'));
190
+ console.log(chalk.gray(' eva4j generate rabbitmq-event user # Will prompt for event name\n'));
191
+ process.exit(1);
192
+ }
193
+ try {
194
+ await generateRabbitMQEventCommand(module, name, options);
195
+ } catch (error) {
196
+ console.error(chalk.red('Error:'), error.message);
197
+ process.exit(1);
198
+ }
199
+ return;
200
+ }
201
+
202
+ if (type === 'rabbitmq-listener') {
203
+ if (!module) {
204
+ console.error(chalk.red('❌ Module name is required'));
205
+ console.log(chalk.gray('Usage: eva4j generate rabbitmq-listener <module>'));
206
+ console.log(chalk.gray('Examples:'));
207
+ console.log(chalk.gray(' eva4j generate rabbitmq-listener user'));
208
+ console.log(chalk.gray(' eva4j g rabbitmq-listener order\n'));
209
+ process.exit(1);
210
+ }
211
+ try {
212
+ await generateRabbitMQListenerCommand(module, options);
213
+ } catch (error) {
214
+ console.error(chalk.red('Error:'), error.message);
215
+ process.exit(1);
216
+ }
217
+ return;
218
+ }
219
+
169
220
  if (type === 'record') {
170
221
  // Record command doesn't require module/name as parameters
171
222
  // They will be prompted interactively
@@ -239,6 +290,16 @@ program
239
290
  return;
240
291
  }
241
292
 
293
+ if (type === 'temporal-system') {
294
+ try {
295
+ await generateTemporalSystemCommand(options);
296
+ } catch (error) {
297
+ console.error(chalk.red('Error:'), error.message);
298
+ process.exit(1);
299
+ }
300
+ return;
301
+ }
302
+
242
303
  if (type === 'resource') {
243
304
  if (!module) {
244
305
  console.error(chalk.red('❌ Module name is required'));
@@ -263,8 +324,11 @@ program
263
324
  console.log(chalk.gray(' eva4j generate http-exchange <port-name> <module>'));
264
325
  console.log(chalk.gray(' eva4j generate kafka-event <event-name> <module>'));
265
326
  console.log(chalk.gray(' eva4j generate kafka-listener <module>'));
327
+ console.log(chalk.gray(' eva4j generate rabbitmq-event <event-name> <module>'));
328
+ console.log(chalk.gray(' eva4j generate rabbitmq-listener <module>'));
266
329
  console.log(chalk.gray(' eva4j generate temporal-flow <module> [workflow-name]'));
267
330
  console.log(chalk.gray(' eva4j generate temporal-activity <module> [activity-name]'));
331
+ console.log(chalk.gray(' eva4j generate temporal-system'));
268
332
  console.log(chalk.gray(' eva4j generate resource <module>'));
269
333
  console.log(chalk.gray(' eva4j generate record'));
270
334
  console.log(chalk.gray(' eva4j generate entities <module>'));
@@ -273,8 +337,11 @@ program
273
337
  console.log(chalk.gray(' eva4j g http-exchange user-service-port user'));
274
338
  console.log(chalk.gray(' eva4j g kafka-event user-created user'));
275
339
  console.log(chalk.gray(' eva4j g kafka-listener user'));
340
+ console.log(chalk.gray(' eva4j g rabbitmq-event user-created user'));
341
+ console.log(chalk.gray(' eva4j g rabbitmq-listener user'));
276
342
  console.log(chalk.gray(' eva4j g temporal-flow order process-order'));
277
343
  console.log(chalk.gray(' eva4j g temporal-activity order register-order'));
344
+ console.log(chalk.gray(' eva4j g temporal-system # Reads from system/system.yaml'));
278
345
  console.log(chalk.gray(' eva4j g resource product'));
279
346
  console.log(chalk.gray(' eva4j g record # Reads JSON from clipboard'));
280
347
  console.log(chalk.gray(' eva4j g entities order # Generates from domain.yaml\n'));
@@ -359,6 +426,7 @@ program.on('--help', () => {
359
426
  console.log(chalk.gray(' $ eva4j add module user'));
360
427
  console.log(chalk.gray(' $ eva4j add module product'));
361
428
  console.log(chalk.gray(' $ eva4j add kafka-client'));
429
+ console.log(chalk.gray(' $ eva4j add rabbitmq-client'));
362
430
  console.log(chalk.gray(' $ eva4j generate usecase create-provider provider'));
363
431
  console.log(chalk.gray(' $ eva4j g usecase get-all-products product'));
364
432
  console.log(chalk.gray(' $ eva4j g http-exchange user-service-port user'));
@@ -8,6 +8,7 @@
8
8
  "temporalSdkVersion": "1.24.1",
9
9
  "temporalDockerVersion": "1.21.5",
10
10
  "kafkaConfluentVersion": "7.6.0",
11
+ "rabbitmqVersion": "4-management",
11
12
  "springdocVersion": "2.8.13",
12
13
  "gradleVersion": "8.7",
13
14
  "encoding": "UTF-8",