@eventmodelers/cli 1.0.14 → 1.0.17
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/package.json +1 -1
- package/shared/build-kit/lib/adapters/supabase-realtime-adapter.js +1 -1
- package/stacks/axon/templates/.claude/skills/build-automation/SKILL.md +6 -2
- package/stacks/axon/templates/.claude/skills/build-automation-workflow/SKILL.md +13 -7
- package/stacks/axon/templates/.claude/skills/build-state-change/SKILL.md +19 -13
- package/stacks/axon/templates/.claude/skills/build-state-change/references/rest-api-patterns.md +2 -2
- package/stacks/axon/templates/.claude/skills/build-state-view/SKILL.md +6 -2
- package/stacks/axon/templates/build-kit/CLAUDE.md +7 -0
- package/stacks/modeling-kit/templates/.claude/skills/html-screen/SKILL.md +6 -4
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@ export async function createSupabaseRealtimeAdapter(cfg, initialToken) {
|
|
|
10
10
|
await supabase.realtime.setAuth(initialToken);
|
|
11
11
|
|
|
12
12
|
return {
|
|
13
|
-
subscribe(topic, handlers, onStatus) {
|
|
13
|
+
async subscribe(topic, handlers, onStatus) {
|
|
14
14
|
let channel = supabase.channel(topic, { config: { private: true } });
|
|
15
15
|
for (const [event, handler] of Object.entries(handlers)) {
|
|
16
16
|
channel = channel.on('broadcast', { event }, (msg) => handler(msg.payload));
|
|
@@ -38,6 +38,10 @@ Read the target project's `.build-kit/CLAUDE.md` and explore existing slices. Lo
|
|
|
38
38
|
- Feature flag patterns (Step 4 — optional)
|
|
39
39
|
- Spring Boot test annotation: check if the project defines a meta-annotation over `@SpringBootTest`
|
|
40
40
|
|
|
41
|
+
**Determine `{basePackage}`** — every path below is rooted at
|
|
42
|
+
`{basePackage}.slices.{context}.automation.{slicename}`. Resolve `{basePackage}` as documented in
|
|
43
|
+
`.build-kit/CLAUDE.md`'s Structure section.
|
|
44
|
+
|
|
41
45
|
## Step 1: Understand the Input
|
|
42
46
|
|
|
43
47
|
Extract these elements regardless of input format:
|
|
@@ -131,7 +135,7 @@ If a command fails, the event handler fails and the event processor retries.
|
|
|
131
135
|
### Stateless Automation
|
|
132
136
|
|
|
133
137
|
New automation slices live under
|
|
134
|
-
`src/main/java
|
|
138
|
+
`src/main/java/.../slices/{context}/automation/{slicename}/`.
|
|
135
139
|
|
|
136
140
|
#### Strategy interface (if needed)
|
|
137
141
|
|
|
@@ -280,7 +284,7 @@ Add to ALL config files when using `@ConditionalOnProperty`:
|
|
|
280
284
|
**For stateless automations** — pure unit tests with a mocked `CommandDispatcher`:
|
|
281
285
|
|
|
282
286
|
```java
|
|
283
|
-
// File: src/test/java
|
|
287
|
+
// File: src/test/java/.../slices/{context}/automation/{slicename}/{AutomationName}Test.java
|
|
284
288
|
class {AutomationName}ProcessorTest {
|
|
285
289
|
|
|
286
290
|
private {AutomationName}Processor processor;
|
|
@@ -19,7 +19,8 @@ description: >
|
|
|
19
19
|
> **Preview feature** — AF5 Workflow APIs may change without notice and are not yet for production.
|
|
20
20
|
|
|
21
21
|
New workflow slices live under
|
|
22
|
-
`src/main/java
|
|
22
|
+
`src/main/java/.../slices/{context}/automation/{slicename}/` — see Step 0 below for how to resolve the
|
|
23
|
+
`{basePackage}` this path is rooted at.
|
|
23
24
|
|
|
24
25
|
---
|
|
25
26
|
|
|
@@ -32,6 +33,11 @@ Read `.build-kit/CLAUDE.md` and check whether the project already uses AF5 Workf
|
|
|
32
33
|
- A `WorkflowModule` bean in any `@Configuration` class
|
|
33
34
|
- Classes annotated with `@Workflow`
|
|
34
35
|
- Existing `*Workflow.java` files under any slice package
|
|
36
|
+
|
|
37
|
+
**Determine `{basePackage}`** — every path in this skill is rooted at
|
|
38
|
+
`{basePackage}.slices.{context}.automation.{slicename}`. Resolve `{basePackage}` as documented in
|
|
39
|
+
`.build-kit/CLAUDE.md`'s Structure section.
|
|
40
|
+
|
|
35
41
|
- The `WorkflowModule` dependency in `pom.xml`:
|
|
36
42
|
|
|
37
43
|
```xml
|
|
@@ -92,7 +98,7 @@ The simplest way to define a workflow using `@Workflow`:
|
|
|
92
98
|
|
|
93
99
|
```java
|
|
94
100
|
// File: {slicename}/{SliceName}Workflow.java
|
|
95
|
-
package
|
|
101
|
+
package {basePackage}.slices.{context}.automation.{slicename};
|
|
96
102
|
|
|
97
103
|
import org.axonframework.workflow.annotation.Workflow;
|
|
98
104
|
import org.axonframework.workflow.SimpleWorkflowContext;
|
|
@@ -100,7 +106,7 @@ import org.axonframework.workflow.SimpleWorkflowContext;
|
|
|
100
106
|
@Workflow(
|
|
101
107
|
idProperty = "{triggerEventIdField}", // field name on the trigger event that becomes the workflow ID
|
|
102
108
|
startOnEventClass = {TriggerEvent}.class, // event that starts a new workflow instance
|
|
103
|
-
workflowNamespace = "
|
|
109
|
+
workflowNamespace = "{basePackage}.slices.{context}"
|
|
104
110
|
)
|
|
105
111
|
public class {SliceName}Workflow {
|
|
106
112
|
|
|
@@ -333,7 +339,7 @@ public void execute(SimpleWorkflowContext ctx) {
|
|
|
333
339
|
|
|
334
340
|
```java
|
|
335
341
|
@Workflow(idProperty = "id", startOnEventClass = OrderPlaced.class,
|
|
336
|
-
workflowNamespace = "
|
|
342
|
+
workflowNamespace = "{basePackage}.slices.{context}")
|
|
337
343
|
public class {SliceName}Workflow {
|
|
338
344
|
|
|
339
345
|
public void execute(SimpleWorkflowContext ctx) { /* ... */ }
|
|
@@ -476,14 +482,14 @@ class {SliceName}WorkflowIntegrationTest extends AbstractDeclarativeTestBase {
|
|
|
476
482
|
## Files to Create / Modify
|
|
477
483
|
|
|
478
484
|
```
|
|
479
|
-
src/main/java
|
|
485
|
+
src/main/java/.../slices/{context}/automation/{slicename}/
|
|
480
486
|
├── {SliceName}Workflow.java ← @Workflow class (execute method + lifecycle listeners)
|
|
481
487
|
└── {SliceName}WorkflowServices.java ← @Component step handlers (Spring beans)
|
|
482
488
|
|
|
483
|
-
src/main/java
|
|
489
|
+
src/main/java/.../slices/{context}/
|
|
484
490
|
└── WorkflowConfiguration.java ← WorkflowModule bean (one per context, create if missing)
|
|
485
491
|
|
|
486
|
-
src/test/java
|
|
492
|
+
src/test/java/.../slices/{context}/automation/{slicename}/
|
|
487
493
|
└── {SliceName}WorkflowTest.java ← unit tests with mocked context + services
|
|
488
494
|
```
|
|
489
495
|
|
|
@@ -35,12 +35,18 @@ Read `.build-kit/.slices/{context}/{slicename}/slice.json`. Extract, and use **o
|
|
|
35
35
|
|
|
36
36
|
Never invent a field, business rule, or event that isn't in slice.json.
|
|
37
37
|
|
|
38
|
+
## Step 0a: Determine `{basePackage}`
|
|
39
|
+
|
|
40
|
+
Every code example below is rooted at `{basePackage}.slices.{context}.{slicename}`. Resolve
|
|
41
|
+
`{basePackage}` as documented in `.build-kit/CLAUDE.md`'s Structure section — never hardcode
|
|
42
|
+
`io.axoniq.quickstart` (the shipped quickstart scaffold's package) or any other specific package.
|
|
43
|
+
|
|
38
44
|
## Step 1: Command
|
|
39
45
|
|
|
40
46
|
**Exactly one field has `idAttribute: true` in slice.json** — annotate it directly:
|
|
41
47
|
|
|
42
48
|
```java
|
|
43
|
-
package
|
|
49
|
+
package {basePackage}.slices.{context}.{slicename};
|
|
44
50
|
|
|
45
51
|
import org.axonframework.messaging.commandhandling.annotation.Command;
|
|
46
52
|
import org.axonframework.modelling.annotation.TargetEntityId;
|
|
@@ -61,13 +67,13 @@ one field directly.** Instead build a compound id record and put `@TargetEntityI
|
|
|
61
67
|
verified against the `SubscribeToCourse` slice (`email` + `courseId` both `idAttribute: true`):
|
|
62
68
|
|
|
63
69
|
```java
|
|
64
|
-
package
|
|
70
|
+
package {basePackage}.slices.{context}.{slicename};
|
|
65
71
|
|
|
66
72
|
public record {SliceName}Id(String field1, String field2) {}
|
|
67
73
|
```
|
|
68
74
|
|
|
69
75
|
```java
|
|
70
|
-
package
|
|
76
|
+
package {basePackage}.slices.{context}.{slicename};
|
|
71
77
|
|
|
72
78
|
import org.axonframework.messaging.commandhandling.annotation.Command;
|
|
73
79
|
import org.axonframework.modelling.annotation.TargetEntityId;
|
|
@@ -93,7 +99,7 @@ Check `src/main/java/.../{context}/events/` first; add to the existing sealed/ma
|
|
|
93
99
|
than creating a duplicate.
|
|
94
100
|
|
|
95
101
|
```java
|
|
96
|
-
package
|
|
102
|
+
package {basePackage}.slices.{context}.events;
|
|
97
103
|
|
|
98
104
|
import org.axonframework.eventsourcing.annotation.EventTag;
|
|
99
105
|
import org.axonframework.messaging.eventhandling.annotation.Event;
|
|
@@ -119,10 +125,10 @@ Package-private, mutable field(s) — **not** an immutable `State` record with f
|
|
|
119
125
|
check needs, nothing else.
|
|
120
126
|
|
|
121
127
|
```java
|
|
122
|
-
package
|
|
128
|
+
package {basePackage}.slices.{context}.{slicename};
|
|
123
129
|
|
|
124
|
-
import
|
|
125
|
-
import
|
|
130
|
+
import {basePackage}.slices.{context}.events.{EventName};
|
|
131
|
+
import {basePackage}.slices.{context}.events.EventTags;
|
|
126
132
|
import org.axonframework.eventsourcing.annotation.EventCriteriaBuilder;
|
|
127
133
|
import org.axonframework.eventsourcing.annotation.EventSourcingHandler;
|
|
128
134
|
import org.axonframework.eventsourcing.annotation.reflection.EntityCreator;
|
|
@@ -164,7 +170,7 @@ resolved automatically from `Configuration` — pass it plus the event class str
|
|
|
164
170
|
tempting to think `andBeingOneOfTypes(new QualifiedName({EventName}.class))` should work without it,
|
|
165
171
|
since `QualifiedName` has a `Class<?>` constructor. Verified experimentally that it does NOT:
|
|
166
172
|
`QualifiedName(Class<?>)`'s only job is `clazz.getName()` — the raw Java class name
|
|
167
|
-
(`
|
|
173
|
+
(`{basePackage}.slices.foo.events.CustomerRegistered`) — not the `@Event(namespace, name)` value
|
|
168
174
|
the event was actually appended under (`Foo.CustomerRegistered`). Swapping to it in
|
|
169
175
|
`SubscribeToCourseDecisionModel` as a test made 2 of 3 passing tests fail immediately, silently, with
|
|
170
176
|
no exception pointing at the real cause — the criteria just stopped matching anything, identical in
|
|
@@ -220,9 +226,9 @@ itself.
|
|
|
220
226
|
## Step 4: Command handler
|
|
221
227
|
|
|
222
228
|
```java
|
|
223
|
-
package
|
|
229
|
+
package {basePackage}.slices.{context}.{slicename};
|
|
224
230
|
|
|
225
|
-
import
|
|
231
|
+
import {basePackage}.slices.{context}.events.{EventName};
|
|
226
232
|
import org.axonframework.messaging.commandhandling.annotation.CommandHandler;
|
|
227
233
|
import org.axonframework.messaging.core.Metadata;
|
|
228
234
|
import org.axonframework.messaging.eventhandling.gateway.EventAppender;
|
|
@@ -255,7 +261,7 @@ exist as a type in this version.
|
|
|
255
261
|
## Step 5: REST endpoint — only if slice.json shows an inbound `SCREEN` dependency on the command
|
|
256
262
|
|
|
257
263
|
```java
|
|
258
|
-
package
|
|
264
|
+
package {basePackage}.slices.{context}.{slicename};
|
|
259
265
|
|
|
260
266
|
import org.axonframework.messaging.commandhandling.gateway.CommandGateway;
|
|
261
267
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|
@@ -323,9 +329,9 @@ One-time `pom.xml` addition (no version needed — resolved via the project's ex
|
|
|
323
329
|
```
|
|
324
330
|
|
|
325
331
|
```java
|
|
326
|
-
package
|
|
332
|
+
package {basePackage}.slices.{context}.{slicename};
|
|
327
333
|
|
|
328
|
-
import
|
|
334
|
+
import {basePackage}.slices.{context}.events.{EventName};
|
|
329
335
|
import org.axonframework.eventsourcing.configuration.EventSourcedEntityModule;
|
|
330
336
|
import org.axonframework.eventsourcing.configuration.EventSourcingConfigurer;
|
|
331
337
|
import org.axonframework.messaging.commandhandling.configuration.CommandHandlingModule;
|
package/stacks/axon/templates/.claude/skills/build-state-change/references/rest-api-patterns.md
CHANGED
|
@@ -9,7 +9,7 @@ directly, and tests use `WebTestClient`, not `MockMvc`.
|
|
|
9
9
|
## Controller
|
|
10
10
|
|
|
11
11
|
```java
|
|
12
|
-
package
|
|
12
|
+
package {basePackage}.slices.{context}.{slicename};
|
|
13
13
|
|
|
14
14
|
import org.axonframework.messaging.commandhandling.gateway.CommandGateway;
|
|
15
15
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|
@@ -59,7 +59,7 @@ shape for the first one, matching the project's WebFlux stack and the `@Conditio
|
|
|
59
59
|
feature-flag convention (tests disable all slices by default; opt in per test class):
|
|
60
60
|
|
|
61
61
|
```java
|
|
62
|
-
package
|
|
62
|
+
package {basePackage}.slices.{context}.{slicename};
|
|
63
63
|
|
|
64
64
|
import org.axonframework.messaging.commandhandling.gateway.CommandGateway;
|
|
65
65
|
import org.junit.jupiter.api.Test;
|
|
@@ -21,6 +21,10 @@ description: >
|
|
|
21
21
|
|
|
22
22
|
Before writing any code, read the target project's `.build-kit/CLAUDE.md`
|
|
23
23
|
|
|
24
|
+
**Determine `{basePackage}`** — every code example below is rooted at
|
|
25
|
+
`{basePackage}.slices.{context}.{slicename}`. Resolve `{basePackage}` as documented in
|
|
26
|
+
`.build-kit/CLAUDE.md`'s Structure section.
|
|
27
|
+
|
|
24
28
|
## Step 1: Ensure Events Exist
|
|
25
29
|
|
|
26
30
|
Before implementing the read slice, verify that all events the projector handles exist in the
|
|
@@ -70,7 +74,7 @@ comments** — those are only for write slices.
|
|
|
70
74
|
### Slice package structure
|
|
71
75
|
|
|
72
76
|
```
|
|
73
|
-
|
|
77
|
+
.../slices/{context}/{slicename}/ (i.e. {basePackage}.slices.{context}.{slicename} — see Step 0)
|
|
74
78
|
├── Get{SliceName}.java ← query record + nested Result
|
|
75
79
|
├── {SliceName}Summary.java ← read model (projection output shape)
|
|
76
80
|
├── {SliceName}Projector.java ← @Component with @EventHandler + @QueryHandler
|
|
@@ -252,7 +256,7 @@ Pure unit tests — instantiate the projector directly, no Spring context needed
|
|
|
252
256
|
Fast, no container startup.
|
|
253
257
|
|
|
254
258
|
```java
|
|
255
|
-
// File: src/test/java/
|
|
259
|
+
// File: src/test/java/.../slices/{context}/{slicename}/{SliceName}ProjectorTest.java
|
|
256
260
|
class {SliceName}ProjectorTest {
|
|
257
261
|
|
|
258
262
|
private {SliceName}Projector projector;
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Read Events in src/events to understand the global structure.
|
|
4
4
|
|
|
5
|
+
`<basePackage>` in this project's Java code (`src/main/java/<basePackage>/slices/...`) is this
|
|
6
|
+
project's own Java package prefix, not a fixed value — resolve it, in order: (1) the package of the
|
|
7
|
+
project's `@SpringBootApplication` class, (2) the package of any existing slice already under
|
|
8
|
+
`.../slices/{context}/{slicename}/`, (3) only if no code exists yet, Maven's `<groupId>` in `pom.xml`
|
|
9
|
+
or Gradle's `group` property in `build.gradle`/`build.gradle.kts`. Never hardcode
|
|
10
|
+
`io.axoniq.quickstart` (the shipped quickstart scaffold's package) or any other specific package.
|
|
11
|
+
|
|
5
12
|
## File Structure Constraints
|
|
6
13
|
|
|
7
14
|
- **Strict Path Limitation**: if not instructed otherwise, only check `src/slices/{slicename}/*.ts`
|
|
@@ -68,12 +68,14 @@ Guidelines:
|
|
|
68
68
|
|
|
69
69
|
### Marks — only when the user explicitly asks for one
|
|
70
70
|
|
|
71
|
-
The canvas has a native "Marks" feature (outline highlight, optional blur
|
|
71
|
+
The canvas has a native "Marks" feature (outline highlight, plus an optional "blur outside" or "white outside" spotlight) for calling out part of a screen — see `HtmlEditorModal.tsx`'s Highlight tool and `markBlur.ts`/`markStyles.ts` in the main app. **Do not add marks by default.** Only apply the effects below when the request explicitly asks to highlight/mark/call out/circle/spotlight, or blur/obscure/white-out part of the screen (e.g. "highlight the submit button", "blur everything except the email field", "white out everything but the header"). An ordinary "design a screen" request gets no marks.
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
This skill only has a `pages`/`backgroundColor` field to send (no separate marks API — the native feature persists marks as board metadata, not through this render call), so reproduce the same visual language directly as inline CSS on the target element(s), self-contained in the page HTML same as any other styling in Step 3. No `<script>`/`<style>` tags are needed (and `<script>` is stripped anyway) — inline `style="..."` reproduces the same CSS the native feature injects:
|
|
74
74
|
|
|
75
|
-
- **Mark / highlight an area** — add to the target element's `style`: `outline:4px solid <color> !important;outline-offset:1px;`. Default color `#e74c3c` (red) unless the user names one; other options mirror the app's mark picker: `#1e293b` (dark slate), `#2ecc71` (green), `#3b82f6` (blue), `#f1c40f` (yellow), `#ffffff` (white).
|
|
76
|
-
- **Blur outside / spotlight an area** — add `style="filter:blur(6px) !important;"` to every other top-level sibling/section on the page so only the called-out element stays sharp.
|
|
75
|
+
- **Mark / highlight an area** — add to the target element's `style`: `outline:4px solid <color> !important;outline-offset:1px;`. Default color `#e74c3c` (red) unless the user names one; other options mirror the app's mark picker (`ColorPicker.tsx`): `#1e293b` (dark slate), `#2ecc71` (green), `#3b82f6` (blue), `#f1c40f` (yellow), `#ffffff` (white).
|
|
76
|
+
- **Blur outside / spotlight an area** — add `style="filter:blur(6px) !important;"` to every other top-level sibling/section on the page so only the called-out element stays sharp.
|
|
77
|
+
- **White outside / spotlight an area** — same idea, but instead add `style="filter:brightness(0) invert(1) !important;"` to every other top-level sibling/section (collapses them to solid white — works uniformly across text, shapes, and images, unlike a plain background-color override). Use this only when the user says "white out" / "whiteout" rather than "blur" — the two are mutually exclusive per mark in the native tool, so never apply both blur and white filters to the same sibling.
|
|
78
|
+
- Combine the outline rule with either spotlight rule if the user asked to both mark *and* blur/white-out.
|
|
77
79
|
|
|
78
80
|
Apply these only to the specific element(s) the request describes — don't guess at additional areas to call out.
|
|
79
81
|
|