@formicoidea/labre-framework-ddd-event-storming 0.32.0 → 0.34.1
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/dist/actions.d.ts +29 -0
- package/dist/actions.js +78 -0
- package/dist/background.d.ts +2 -0
- package/dist/background.js +138 -0
- package/dist/commands-manifest.d.ts +18 -0
- package/dist/commands-manifest.js +106 -0
- package/dist/commands.js +46 -5
- package/dist/descriptor.d.ts +7 -2
- package/dist/descriptor.js +5 -2
- package/dist/element-renderer.d.ts +17 -0
- package/dist/element-renderer.js +15 -0
- package/dist/element-view.d.ts +18 -0
- package/dist/element-view.js +19 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +7 -2
- package/dist/legend.d.ts +16 -0
- package/dist/legend.js +58 -0
- package/dist/nudges.d.ts +26 -0
- package/dist/nudges.js +73 -0
- package/dist/profiles.d.ts +2 -0
- package/dist/profiles.js +121 -0
- package/dist/roles.d.ts +62 -0
- package/dist/roles.js +84 -0
- package/dist/rules.d.ts +20 -0
- package/dist/rules.js +241 -0
- package/dist/toolbar/board-config.d.ts +29 -0
- package/dist/toolbar/board-config.js +86 -0
- package/dist/toolbar/event-storming-menu.d.ts +1 -1
- package/dist/toolbar/event-storming-menu.js +1 -1
- package/dist/toolbar/senior-button.d.ts +1 -0
- package/dist/toolbar/senior-button.js +1 -0
- package/dist/toolbar/senior-tool.js +1 -0
- package/dist/translations.d.ts +7 -5
- package/dist/translations.js +20 -7
- package/dist/view.d.ts +17 -1
- package/dist/view.js +61 -3
- package/package.json +7 -3
package/dist/rules.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { EVENT_STORMING_BACKGROUND } from './background.js';
|
|
2
|
+
import { ES_ROLE, EVENT_STORMING_ROLES } from './roles.js';
|
|
3
|
+
/**
|
|
4
|
+
* Event Storming validation rules (WS5).
|
|
5
|
+
*
|
|
6
|
+
* DATA owned by the framework, versioned per rule: the engine
|
|
7
|
+
* (`@labre/affine-block-surface`) knows how to evaluate a FAMILY, never a
|
|
8
|
+
* concrete rule. Registered from the flag-gated
|
|
9
|
+
* `DddEventStormingViewExtension`, so switching the Event Storming flag off
|
|
10
|
+
* removes them with the rest of the tooling — boards already stormed keep
|
|
11
|
+
* rendering, they simply stop being checked (`docs/adr/0009`).
|
|
12
|
+
*
|
|
13
|
+
* ## Three rules, and the ones that are deliberately absent
|
|
14
|
+
*
|
|
15
|
+
* Event Storming's grammar is small and its etiquette is large, and the two
|
|
16
|
+
* must not be confused. What is decidable here is the DIRECTION of the frieze,
|
|
17
|
+
* the SHAPE of an arc and whether two stickies are on top of each other.
|
|
18
|
+
*
|
|
19
|
+
* What is not, and is a level-2 nudge instead (`./nudges.ts`, PO arbitration of
|
|
20
|
+
* 26/08/2026): **the past tense**. "Order placed" is a domain event and "Place
|
|
21
|
+
* order" is a command, and every workshop gets that wrong for the first hour —
|
|
22
|
+
* but deciding it means parsing a human sentence, in whatever language the room
|
|
23
|
+
* speaks, written in marker pen by somebody who abbreviates. There are no
|
|
24
|
+
* linguistic rules in this file, on purpose. A checklist item that says "the
|
|
25
|
+
* events are named in the past tense" costs a tick and is true; a regular
|
|
26
|
+
* expression over `Y.Text` costs the tool's credibility and is wrong every
|
|
27
|
+
* fifth sticky.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* **ES1** — a flow runs forwards in time.
|
|
31
|
+
*
|
|
32
|
+
* The one rule the frame makes possible, and the reason the board declares an
|
|
33
|
+
* axis at all: the source of a flow is what happens FIRST (tier 1 of
|
|
34
|
+
* `docs/adr/0010`, the verb is "leads to"), so its target belongs to the RIGHT
|
|
35
|
+
* of it. A flow drawn the other way says the effect precedes the cause — either
|
|
36
|
+
* the arc was drawn from the wrong end, or the two stickies need swapping, and
|
|
37
|
+
* only the room knows which.
|
|
38
|
+
*
|
|
39
|
+
* ## The tolerance, and why it is tiny
|
|
40
|
+
*
|
|
41
|
+
* `0.01` of the board's width — 32 units on the 3200-wide reference, about a
|
|
42
|
+
* quarter of a sticky. Much tighter than the Wardley equivalent's 2 %, and for
|
|
43
|
+
* the opposite reason: on a Wardley map two components drawn level are a chain
|
|
44
|
+
* waiting to be spread out, whereas on a frieze two stickies drawn level are
|
|
45
|
+
* two things that happen at the same moment — which is a claim, not a
|
|
46
|
+
* placeholder — and an arc between them is the one that wants asking about.
|
|
47
|
+
* The tolerance is here to absorb a hand, not a habit: stickies stacked to
|
|
48
|
+
* within a few pixels stay silent, anything genuinely to the left does not.
|
|
49
|
+
*
|
|
50
|
+
* As with every other position in this engine it is a RATIO, so the verdict
|
|
51
|
+
* survives the roll being unspooled (the lesson of the 01/08/2026 recette).
|
|
52
|
+
*
|
|
53
|
+
* Silent, from the family: an arc with a free end, an arc between two different
|
|
54
|
+
* boards, an arc carrying no role, and an arc on a board that has no
|
|
55
|
+
* `es:board` element under it — a frieze stormed on the bare canvas is a
|
|
56
|
+
* frieze, and it has no timeline to run against.
|
|
57
|
+
*/
|
|
58
|
+
const againstTimeline = {
|
|
59
|
+
id: 'es.against-timeline',
|
|
60
|
+
framework: 'ddd-event-storming',
|
|
61
|
+
family: 'relative-order-along-axis',
|
|
62
|
+
severity: 'warning',
|
|
63
|
+
// No `appliesTo`: the subject is a RELATION, and the role that names it is
|
|
64
|
+
// declared where the family reads it — naming one of the three indicted
|
|
65
|
+
// elements here would be data that lies.
|
|
66
|
+
roles: EVENT_STORMING_ROLES,
|
|
67
|
+
messageKey: 'com.labre.event-storming.validation.against-timeline',
|
|
68
|
+
messageFallback: 'This flow runs backwards along the timeline.',
|
|
69
|
+
suggestionKey: 'com.labre.event-storming.validation.against-timeline.suggestion',
|
|
70
|
+
suggestionFallback: 'Time runs left to right: move what follows to the right of what leads to it — or, if the arc was drawn from the wrong end, reverse it.',
|
|
71
|
+
version: 1,
|
|
72
|
+
provenance: {
|
|
73
|
+
source: 'recommendation',
|
|
74
|
+
reference: 'Event Storming (Brandolini) — the frieze runs left to right in time',
|
|
75
|
+
},
|
|
76
|
+
backgroundRole: ES_ROLE.board,
|
|
77
|
+
background: EVENT_STORMING_BACKGROUND,
|
|
78
|
+
relativeOrder: {
|
|
79
|
+
edgeRole: ES_ROLE.flow,
|
|
80
|
+
axis: 'time',
|
|
81
|
+
// The verb is "leads to": the source happens first, so the TARGET is the
|
|
82
|
+
// one further along the axis' forward sense.
|
|
83
|
+
expect: 'target-ahead',
|
|
84
|
+
toleranceRatio: 0.01,
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* The canonical Event Storming sentence, as a matrix.
|
|
89
|
+
*
|
|
90
|
+
* Read aloud, which is how it was checked: an actor issues a command; a command
|
|
91
|
+
* lands on an aggregate, or on an external system; an aggregate raises a domain
|
|
92
|
+
* event, and so does an external system; a domain event triggers a policy, and
|
|
93
|
+
* a domain event feeds a read model; a policy issues a command; a read model
|
|
94
|
+
* informs an actor.
|
|
95
|
+
*
|
|
96
|
+
* Nine sentences and no more. Every one of them is in Brandolini's own picture
|
|
97
|
+
* of the "Big Picture → Process modelling" grammar, and nothing has been added
|
|
98
|
+
* because it seemed plausible: a matrix that sanctions a sentence the notation
|
|
99
|
+
* does not say is worse than no matrix, because the sentence it fails to catch
|
|
100
|
+
* is the one somebody will ship.
|
|
101
|
+
*
|
|
102
|
+
* Exported so a test asserts THIS table rather than a copy of it.
|
|
103
|
+
*/
|
|
104
|
+
export const ES_FLOW_MATRIX = [
|
|
105
|
+
{ source: ES_ROLE.actor, edge: ES_ROLE.flow, target: ES_ROLE.command },
|
|
106
|
+
{ source: ES_ROLE.command, edge: ES_ROLE.flow, target: ES_ROLE.aggregate },
|
|
107
|
+
{ source: ES_ROLE.command, edge: ES_ROLE.flow, target: ES_ROLE.system },
|
|
108
|
+
{
|
|
109
|
+
source: ES_ROLE.aggregate,
|
|
110
|
+
edge: ES_ROLE.flow,
|
|
111
|
+
target: ES_ROLE.domainEvent,
|
|
112
|
+
},
|
|
113
|
+
{ source: ES_ROLE.system, edge: ES_ROLE.flow, target: ES_ROLE.domainEvent },
|
|
114
|
+
{ source: ES_ROLE.domainEvent, edge: ES_ROLE.flow, target: ES_ROLE.policy },
|
|
115
|
+
{
|
|
116
|
+
source: ES_ROLE.domainEvent,
|
|
117
|
+
edge: ES_ROLE.flow,
|
|
118
|
+
target: ES_ROLE.readModel,
|
|
119
|
+
},
|
|
120
|
+
{ source: ES_ROLE.policy, edge: ES_ROLE.flow, target: ES_ROLE.command },
|
|
121
|
+
{ source: ES_ROLE.readModel, edge: ES_ROLE.flow, target: ES_ROLE.actor },
|
|
122
|
+
];
|
|
123
|
+
/**
|
|
124
|
+
* **ES2** — a flow says one of the nine things the notation can say.
|
|
125
|
+
*
|
|
126
|
+
* The grammar rule, and the one that earns the framework its roles: a flow from
|
|
127
|
+
* a command straight to a domain event skips the aggregate, which is the whole
|
|
128
|
+
* modelling question Event Storming is run to answer. Reading it off the roles
|
|
129
|
+
* rather than off the colours means the verdict survives someone restyling a
|
|
130
|
+
* sticky, and reading it off `source → target` means it survives someone
|
|
131
|
+
* re-laying-out the wall.
|
|
132
|
+
*
|
|
133
|
+
* ## What it stays silent about, and why that is the rule
|
|
134
|
+
*
|
|
135
|
+
* **Hotspots and constraints are outside the alphabet.** Neither is cited by a
|
|
136
|
+
* triplet, so an arc with either at one end takes the whole edge out of the
|
|
137
|
+
* conversation — not the matrix, not the self-loop, nothing. That is the point
|
|
138
|
+
* of the family (`RelationEndpointsDef.allowed`) and it is the hard requirement
|
|
139
|
+
* for Event Storming in particular: a hotspot is a workshop saying "we do not
|
|
140
|
+
* know", and an arrow drawn at one is somebody parking a question. A tool that
|
|
141
|
+
* answered "that arc is forbidden" would be indicting the act of storming.
|
|
142
|
+
*
|
|
143
|
+
* The same silence covers an arc onto a plain rectangle, onto a note, onto an
|
|
144
|
+
* artefact of another framework, and every flow drawn before WS5 — those carry
|
|
145
|
+
* no role at all and are not even looked at.
|
|
146
|
+
*
|
|
147
|
+
* ## Self-loops yes, duplicates no
|
|
148
|
+
*
|
|
149
|
+
* A sticky leading to itself is not a sentence in this notation at whichever
|
|
150
|
+
* end you read it, so `forbidSelfLoop` is on.
|
|
151
|
+
*
|
|
152
|
+
* `forbidDuplicate` is deliberately OFF. Two flows between the same two
|
|
153
|
+
* stickies is what a wall looks like when a process has two paths to the same
|
|
154
|
+
* outcome and the room drew both; the frieze is read by following arcs, not by
|
|
155
|
+
* counting them, and a workshop is exactly where a line gets drawn twice while
|
|
156
|
+
* three people talk over each other. Nothing is lost by staying quiet — unlike
|
|
157
|
+
* a context map, where the same pattern twice between two contexts is a claim
|
|
158
|
+
* made twice.
|
|
159
|
+
*/
|
|
160
|
+
const forbiddenArc = {
|
|
161
|
+
id: 'es.forbidden-arc',
|
|
162
|
+
framework: 'ddd-event-storming',
|
|
163
|
+
family: 'relation-endpoints',
|
|
164
|
+
severity: 'warning',
|
|
165
|
+
roles: EVENT_STORMING_ROLES,
|
|
166
|
+
messageKey: 'com.labre.event-storming.validation.forbidden-arc',
|
|
167
|
+
messageFallback: 'This flow is not a sentence Event Storming says.',
|
|
168
|
+
suggestionKey: 'com.labre.event-storming.validation.forbidden-arc.suggestion',
|
|
169
|
+
suggestionFallback: 'The frieze reads: an actor issues a command, the command lands on an aggregate or an external system, and that raises a domain event, which triggers a policy or feeds a read model. Re-point the arc onto the sticky that is missing.',
|
|
170
|
+
version: 1,
|
|
171
|
+
provenance: {
|
|
172
|
+
source: 'recommendation',
|
|
173
|
+
reference: 'Event Storming (Brandolini) — the flow the big-picture notation draws',
|
|
174
|
+
},
|
|
175
|
+
backgroundRole: ES_ROLE.board,
|
|
176
|
+
background: EVENT_STORMING_BACKGROUND,
|
|
177
|
+
endpoints: {
|
|
178
|
+
edgeRole: ES_ROLE.flow,
|
|
179
|
+
allowed: ES_FLOW_MATRIX,
|
|
180
|
+
forbidSelfLoop: true,
|
|
181
|
+
// See the header: not on a wall.
|
|
182
|
+
selfLoop: {
|
|
183
|
+
messageKey: 'com.labre.event-storming.validation.flow-self-loop',
|
|
184
|
+
messageFallback: 'This flow loops back onto the sticky it starts from.',
|
|
185
|
+
suggestionKey: 'com.labre.event-storming.validation.flow-self-loop.suggestion',
|
|
186
|
+
suggestionFallback: 'Nothing leads to itself on a frieze — drop the loose end on what actually follows, or delete the arc.',
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* **ES3** — two stickies must not be on top of each other.
|
|
192
|
+
*
|
|
193
|
+
* A readability rule, not a semantic one, and the softest thing in the pack: a
|
|
194
|
+
* frieze you cannot read is useless, but a sticky nudged over its neighbour
|
|
195
|
+
* while somebody reorders the morning is not a mistake.
|
|
196
|
+
*
|
|
197
|
+
* Written on the PARENT role, which is what makes it one rule instead of
|
|
198
|
+
* thirty-six pairs: any two stickies hide each other, whichever kinds they are,
|
|
199
|
+
* and the tenth kind is covered on the day it lands.
|
|
200
|
+
*
|
|
201
|
+
* ## The threshold, and why it is wide
|
|
202
|
+
*
|
|
203
|
+
* **12 model units** — a tenth of the 120-unit sticky, against the 4 units
|
|
204
|
+
* Wardley uses for a node the size of a full stop. Stickies FRAME each other on
|
|
205
|
+
* a real wall: a workshop overlaps the corners of a run of events to say they
|
|
206
|
+
* belong together, tucks a policy under the event that triggers it, and shingles
|
|
207
|
+
* a stack of read models. At 4 units all of that would be reported, and the rule
|
|
208
|
+
* would be switched off by lunchtime. At 12 a deliberate tuck stays silent and a
|
|
209
|
+
* sticky genuinely covering another — half a label hidden — is still reported
|
|
210
|
+
* with an order of magnitude to spare.
|
|
211
|
+
*/
|
|
212
|
+
const overlappingStickies = {
|
|
213
|
+
id: 'es.overlapping-stickies',
|
|
214
|
+
framework: 'ddd-event-storming',
|
|
215
|
+
family: 'no-overlap',
|
|
216
|
+
severity: 'warning',
|
|
217
|
+
// No `appliesTo`: the subject is a PAIR, and naming one half of it here would
|
|
218
|
+
// be data that lies.
|
|
219
|
+
roles: EVENT_STORMING_ROLES,
|
|
220
|
+
messageKey: 'com.labre.event-storming.validation.overlapping-stickies',
|
|
221
|
+
messageFallback: 'These two stickies cover each other.',
|
|
222
|
+
suggestionKey: 'com.labre.event-storming.validation.overlapping-stickies.suggestion',
|
|
223
|
+
suggestionFallback: 'Slide one aside so both can be read.',
|
|
224
|
+
version: 1,
|
|
225
|
+
provenance: {
|
|
226
|
+
source: 'labre-convention',
|
|
227
|
+
reference: 'Labre readability convention — no Event Storming rule speaks of overlapping ink',
|
|
228
|
+
},
|
|
229
|
+
// Not a frame the rule measures against — an overlap is an overlap wherever
|
|
230
|
+
// it happens — but the board a finding is ATTRIBUTED to, so the arbitration
|
|
231
|
+
// "ignore this rule on the whole board" has one board to be written on.
|
|
232
|
+
backgroundRole: ES_ROLE.board,
|
|
233
|
+
overlap: [[ES_ROLE.sticky, ES_ROLE.sticky]],
|
|
234
|
+
// How deep a collision has to be before it is one. See the header.
|
|
235
|
+
minPenetration: 12,
|
|
236
|
+
};
|
|
237
|
+
export const EVENT_STORMING_RULES = [
|
|
238
|
+
againstTimeline,
|
|
239
|
+
forbiddenArc,
|
|
240
|
+
overlappingStickies,
|
|
241
|
+
];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ToolbarContext } from '@formicoidea/labre-core/shared/services';
|
|
2
|
+
/**
|
|
3
|
+
* The selected board's contextual toolbar: the resize toggle, and the automatic
|
|
4
|
+
* legend of the sticky kinds actually stuck to the board. Registered ALWAYS-ON
|
|
5
|
+
* (`DddEventStormingRenderViewExtension`) — a stored board must stay usable with
|
|
6
|
+
* the Event Storming button switched off (`docs/adr/0009`), legend included: a
|
|
7
|
+
* legend is real editable elements, so generating one is authoring a document,
|
|
8
|
+
* not tooling that a flag may take away.
|
|
9
|
+
*
|
|
10
|
+
* This is the module's ONLY legend gesture: the Event Storming palette never had
|
|
11
|
+
* a static Legend entry, and a wall of colour-coded stickies is exactly the
|
|
12
|
+
* board a reader needs one for.
|
|
13
|
+
*/
|
|
14
|
+
export declare const eventStormingBoardToolbarConfig: {
|
|
15
|
+
readonly actions: [{
|
|
16
|
+
readonly id: "a.toggle-resize";
|
|
17
|
+
readonly tooltipWording: import("@formicoidea/labre-core/shared/services").ChromeWording;
|
|
18
|
+
readonly icon: import("lit-html").TemplateResult<1>;
|
|
19
|
+
readonly active: (ctx: ToolbarContext) => boolean;
|
|
20
|
+
readonly run: (ctx: ToolbarContext) => void;
|
|
21
|
+
}, {
|
|
22
|
+
readonly id: "b.legend";
|
|
23
|
+
readonly tooltipWording: import("@formicoidea/labre-core/shared/services").ChromeWording;
|
|
24
|
+
readonly icon: import("lit-html").TemplateResult<1>;
|
|
25
|
+
readonly run: (ctx: ToolbarContext) => void;
|
|
26
|
+
}];
|
|
27
|
+
readonly when: (ctx: ToolbarContext) => boolean;
|
|
28
|
+
};
|
|
29
|
+
export declare const eventStormingBoardToolbarExtension: import("@formicoidea/labre-core/store").ExtensionType;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { EdgelessCRUDIdentifier } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { createAutoLegend, dddLegendIcon } from '@formicoidea/labre-ddd-shared';
|
|
3
|
+
import { EventStormingBoardElementModel } from '@formicoidea/labre-core/model';
|
|
4
|
+
import { BOARD_LEGEND_NOTATION, BOARD_RESIZE_TOGGLE, TelemetryProvider, ToolbarModuleExtension, } from '@formicoidea/labre-core/shared/services';
|
|
5
|
+
import { BlockFlavourIdentifier } from '@formicoidea/labre-core/std';
|
|
6
|
+
import { html } from 'lit';
|
|
7
|
+
import { EVENT_STORMING_AUTO_LEGEND } from '../legend.js';
|
|
8
|
+
const ResizeIcon = html `<svg
|
|
9
|
+
width="24"
|
|
10
|
+
height="24"
|
|
11
|
+
viewBox="0 0 24 24"
|
|
12
|
+
fill="none"
|
|
13
|
+
stroke="currentColor"
|
|
14
|
+
stroke-width="1.6"
|
|
15
|
+
stroke-linecap="round"
|
|
16
|
+
stroke-linejoin="round"
|
|
17
|
+
>
|
|
18
|
+
<path d="M9 5H5v4M15 19h4v-4" />
|
|
19
|
+
<path d="M5 5l6 6M19 19l-6-6" />
|
|
20
|
+
</svg>`;
|
|
21
|
+
/**
|
|
22
|
+
* The selected board's contextual toolbar: the resize toggle, and the automatic
|
|
23
|
+
* legend of the sticky kinds actually stuck to the board. Registered ALWAYS-ON
|
|
24
|
+
* (`DddEventStormingRenderViewExtension`) — a stored board must stay usable with
|
|
25
|
+
* the Event Storming button switched off (`docs/adr/0009`), legend included: a
|
|
26
|
+
* legend is real editable elements, so generating one is authoring a document,
|
|
27
|
+
* not tooling that a flag may take away.
|
|
28
|
+
*
|
|
29
|
+
* This is the module's ONLY legend gesture: the Event Storming palette never had
|
|
30
|
+
* a static Legend entry, and a wall of colour-coded stickies is exactly the
|
|
31
|
+
* board a reader needs one for.
|
|
32
|
+
*/
|
|
33
|
+
export const eventStormingBoardToolbarConfig = {
|
|
34
|
+
actions: [
|
|
35
|
+
{
|
|
36
|
+
id: 'a.toggle-resize',
|
|
37
|
+
tooltipWording: BOARD_RESIZE_TOGGLE,
|
|
38
|
+
icon: ResizeIcon,
|
|
39
|
+
active(ctx) {
|
|
40
|
+
const models = ctx.getSurfaceModelsByType(EventStormingBoardElementModel);
|
|
41
|
+
return models.length > 0 && models.every(model => model.resizeEnabled);
|
|
42
|
+
},
|
|
43
|
+
run(ctx) {
|
|
44
|
+
const models = ctx.getSurfaceModelsByType(EventStormingBoardElementModel);
|
|
45
|
+
if (!models.length)
|
|
46
|
+
return;
|
|
47
|
+
const enable = !models.every(model => model.resizeEnabled);
|
|
48
|
+
ctx.std.store.captureSync();
|
|
49
|
+
const crud = ctx.std.get(EdgelessCRUDIdentifier);
|
|
50
|
+
for (const model of models) {
|
|
51
|
+
crud.updateElement(model.id, { resizeEnabled: enable });
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: 'b.legend',
|
|
57
|
+
tooltipWording: BOARD_LEGEND_NOTATION,
|
|
58
|
+
icon: dddLegendIcon,
|
|
59
|
+
run(ctx) {
|
|
60
|
+
const board = ctx.getSurfaceModelsByType(EventStormingBoardElementModel)[0];
|
|
61
|
+
if (!board)
|
|
62
|
+
return;
|
|
63
|
+
createAutoLegend(ctx.std, board, EVENT_STORMING_AUTO_LEGEND);
|
|
64
|
+
ctx.std
|
|
65
|
+
.getOptional(TelemetryProvider)
|
|
66
|
+
?.track('FrameworkLegendCreated', {
|
|
67
|
+
// The WIRE value, which is not the module id: the framework is
|
|
68
|
+
// `ddd-event-storming` in code and `event-storming` in PostHog
|
|
69
|
+
// (`frameworks.ts` `telemetryKey`, and the only value
|
|
70
|
+
// `FrameworkElementEvent` accepts). Same convention as Wardley's own
|
|
71
|
+
// legend button, so the two are comparable.
|
|
72
|
+
framework: 'event-storming',
|
|
73
|
+
element: 'legend',
|
|
74
|
+
page: 'whiteboard editor',
|
|
75
|
+
segment: 'element toolbar',
|
|
76
|
+
module: 'event-storming toolbar',
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
when: (ctx) => ctx.getSurfaceModelsByType(EventStormingBoardElementModel).length > 0,
|
|
82
|
+
};
|
|
83
|
+
export const eventStormingBoardToolbarExtension = ToolbarModuleExtension({
|
|
84
|
+
id: BlockFlavourIdentifier('affine:surface:eventStorming'),
|
|
85
|
+
config: eventStormingBoardToolbarConfig,
|
|
86
|
+
});
|
|
@@ -7,7 +7,7 @@ import { EdgelessCommandMenu } from '@formicoidea/labre-core/widgets/edgeless-to
|
|
|
7
7
|
* helper — is gone with the duplication it carried (`docs/adr/0008`).
|
|
8
8
|
*
|
|
9
9
|
* The DDD palettes keep their tighter gap: they carry more buttons than the
|
|
10
|
-
* other frameworks (Context Map:
|
|
10
|
+
* other frameworks (Event Storming: 11, Context Map: 13).
|
|
11
11
|
*/
|
|
12
12
|
export declare class EdgelessDddEventStormingMenu extends EdgelessCommandMenu {
|
|
13
13
|
static styles: import("lit").CSSResultGroup[];
|
|
@@ -8,7 +8,7 @@ import { css } from 'lit';
|
|
|
8
8
|
* helper — is gone with the duplication it carried (`docs/adr/0008`).
|
|
9
9
|
*
|
|
10
10
|
* The DDD palettes keep their tighter gap: they carry more buttons than the
|
|
11
|
-
* other frameworks (Context Map:
|
|
11
|
+
* other frameworks (Event Storming: 11, Context Map: 13).
|
|
12
12
|
*/
|
|
13
13
|
export class EdgelessDddEventStormingMenu extends EdgelessCommandMenu {
|
|
14
14
|
constructor() {
|
|
@@ -2,5 +2,6 @@ import { DddSeniorButtonBase } from '@formicoidea/labre-ddd-shared';
|
|
|
2
2
|
export declare class EdgelessDddEventStormingSeniorButton extends DddSeniorButtonBase {
|
|
3
3
|
protected menuTag: "edgeless-ddd-event-storming-menu";
|
|
4
4
|
protected label: string;
|
|
5
|
+
protected labelKey: string;
|
|
5
6
|
protected icon: import("lit-html").TemplateResult<2>;
|
|
6
7
|
}
|
|
@@ -4,6 +4,7 @@ export class EdgelessDddEventStormingSeniorButton extends DddSeniorButtonBase {
|
|
|
4
4
|
super(...arguments);
|
|
5
5
|
this.menuTag = 'edgeless-ddd-event-storming-menu';
|
|
6
6
|
this.label = 'Event Storming';
|
|
7
|
+
this.labelKey = 'com.labre.framework.ddd-event-storming';
|
|
7
8
|
this.icon = eventStormingToolbarIcon;
|
|
8
9
|
}
|
|
9
10
|
}
|
|
@@ -3,6 +3,7 @@ import { html } from 'lit';
|
|
|
3
3
|
/** Independent senior tool — Event Storming. */
|
|
4
4
|
export const eventStormingSeniorTool = SeniorToolExtension('ddd-event-storming', ({ block }) => ({
|
|
5
5
|
name: 'Event Storming',
|
|
6
|
+
labelKey: 'com.labre.framework.ddd-event-storming',
|
|
6
7
|
content: html `<edgeless-ddd-event-storming-senior-button
|
|
7
8
|
.edgeless=${block}
|
|
8
9
|
></edgeless-ddd-event-storming-senior-button>`,
|
package/dist/translations.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { type TranslationKeyManifestEntry } from '@formicoidea/labre-core/std';
|
|
2
2
|
/**
|
|
3
|
-
* THIS framework's contribution to the translation-key manifest
|
|
3
|
+
* THIS framework's contribution to the translation-key manifest — every
|
|
4
|
+
* `com.labre.*` key Event Storming can hand to `TranslationProvider.t`,
|
|
5
|
+
* derived from the very declarations the editor registers (never restated).
|
|
4
6
|
*
|
|
5
7
|
* Its command labels and descriptions are built from a TEMPLATE, so the
|
|
6
|
-
* concrete keys exist nowhere but in the declarations themselves and the
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* concrete keys exist nowhere but in the declarations themselves and the core
|
|
9
|
+
* manifest could not restate them even if it wanted to. The contribution
|
|
10
|
+
* therefore ships WITH the framework: in the bundled distribution
|
|
11
|
+
* `@formicoidea/labre-framework-ddd-event-storming` carries it, and a host
|
|
10
12
|
* composes it into its catalogue exactly as it already composes
|
|
11
13
|
* `eventStormingCommands` into the command registry. See
|
|
12
14
|
* `packages/affine/all/src/translations.ts`.
|
package/dist/translations.js
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
|
-
import { commandTranslationEntries, } from '@formicoidea/labre-core/std';
|
|
1
|
+
import { collectTranslationKeys, commandCategoryTranslationEntries, commandTranslationEntries, mergeTranslationEntries, } from '@formicoidea/labre-core/std';
|
|
2
|
+
import { EVENT_STORMING_BACKGROUND } from './background.js';
|
|
2
3
|
import { eventStormingCommands } from './commands.js';
|
|
4
|
+
import { EVENT_STORMING_NUDGES } from './nudges.js';
|
|
5
|
+
import { EVENT_STORMING_PROFILES } from './profiles.js';
|
|
6
|
+
import { EVENT_STORMING_ROLES } from './roles.js';
|
|
7
|
+
import { EVENT_STORMING_RULES } from './rules.js';
|
|
3
8
|
/**
|
|
4
|
-
* THIS framework's contribution to the translation-key manifest
|
|
9
|
+
* THIS framework's contribution to the translation-key manifest — every
|
|
10
|
+
* `com.labre.*` key Event Storming can hand to `TranslationProvider.t`,
|
|
11
|
+
* derived from the very declarations the editor registers (never restated).
|
|
5
12
|
*
|
|
6
13
|
* Its command labels and descriptions are built from a TEMPLATE, so the
|
|
7
|
-
* concrete keys exist nowhere but in the declarations themselves and the
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
14
|
+
* concrete keys exist nowhere but in the declarations themselves and the core
|
|
15
|
+
* manifest could not restate them even if it wanted to. The contribution
|
|
16
|
+
* therefore ships WITH the framework: in the bundled distribution
|
|
17
|
+
* `@formicoidea/labre-framework-ddd-event-storming` carries it, and a host
|
|
11
18
|
* composes it into its catalogue exactly as it already composes
|
|
12
19
|
* `eventStormingCommands` into the command registry. See
|
|
13
20
|
* `packages/affine/all/src/translations.ts`.
|
|
14
21
|
*/
|
|
15
|
-
export const eventStormingTranslationEntries = commandTranslationEntries(eventStormingCommands)
|
|
22
|
+
export const eventStormingTranslationEntries = mergeTranslationEntries(commandTranslationEntries(eventStormingCommands),
|
|
23
|
+
// The catalogue's own group headers, derived from the very categories
|
|
24
|
+
// these commands declare. They ship WITH the framework because core's
|
|
25
|
+
// registry names no framework category in the bundled distribution, so a
|
|
26
|
+
// host that composed core's manifest alone drew translated entries under
|
|
27
|
+
// English headers (#183).
|
|
28
|
+
commandCategoryTranslationEntries(eventStormingCommands), collectTranslationKeys('role', EVENT_STORMING_ROLES), collectTranslationKeys('background', EVENT_STORMING_BACKGROUND), collectTranslationKeys('rule', EVENT_STORMING_RULES), collectTranslationKeys('nudge', EVENT_STORMING_NUDGES), collectTranslationKeys('profile', EVENT_STORMING_PROFILES));
|
package/dist/view.d.ts
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
import { type ViewExtensionContext, ViewExtensionProvider } from '@formicoidea/labre-core/ext-loader';
|
|
2
2
|
/**
|
|
3
|
-
* Event Storming —
|
|
3
|
+
* Event Storming rendering — ALWAYS registered, independent of any flag.
|
|
4
|
+
* Disabling `ddd-event-storming` hides only the creation tooling (see
|
|
5
|
+
* {@link DddEventStormingViewExtension}); boards already stormed must still
|
|
6
|
+
* paint, stay selectable, stay movable and keep their contextual toolbar. See
|
|
7
|
+
* `docs/adr/0009`.
|
|
8
|
+
*/
|
|
9
|
+
export declare class DddEventStormingRenderViewExtension extends ViewExtensionProvider {
|
|
10
|
+
name: string;
|
|
11
|
+
setup(context: ViewExtensionContext): void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Event Storming creation tooling — independently flag-gated
|
|
15
|
+
* (`ddd-event-storming`): the senior toolbar button, its palette and the
|
|
16
|
+
* validation rules, profiles and quality nudges. Both halves are tooling: a
|
|
17
|
+
* board stormed while the flag was on keeps rendering when it goes off, it just
|
|
18
|
+
* stops being checked — and the profile it was put on stays written, unread,
|
|
19
|
+
* until the flag comes back.
|
|
4
20
|
*
|
|
5
21
|
* Note: its Templates-panel category is registered by the aggregate package's
|
|
6
22
|
* {@link DddTemplatesViewExtension} (gated by `ddd-templates`), so templates
|
package/dist/view.js
CHANGED
|
@@ -1,10 +1,54 @@
|
|
|
1
|
+
import { QualityNudgeExtension, validationToolbarConfig, ValidationProfileExtension, ValidationRuleExtension, } from '@formicoidea/labre-core/blocks/surface';
|
|
1
2
|
import { ViewExtensionProvider, } from '@formicoidea/labre-core/ext-loader';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
3
|
+
import { ToolbarModuleExtension } from '@formicoidea/labre-core/shared/services';
|
|
4
|
+
import { BlockFlavourIdentifier, CommandExtension } from '@formicoidea/labre-core/std';
|
|
5
|
+
import { RoleVocabularyExtension } from '@formicoidea/labre-core/std/gfx';
|
|
6
|
+
import { eventStormingCommandIcons, eventStormingCommands } from './commands.js';
|
|
4
7
|
import { eventStormingEffects } from './effects.js';
|
|
8
|
+
import { EventStormingRendererExtension } from './element-renderer.js';
|
|
9
|
+
import { EventStormingInteraction, EventStormingView } from './element-view.js';
|
|
10
|
+
import { EVENT_STORMING_NUDGES } from './nudges.js';
|
|
11
|
+
import { EVENT_STORMING_PROFILES } from './profiles.js';
|
|
12
|
+
import { EVENT_STORMING_ROLES } from './roles.js';
|
|
13
|
+
import { EVENT_STORMING_RULES } from './rules.js';
|
|
14
|
+
import { eventStormingBoardToolbarExtension } from './toolbar/board-config.js';
|
|
5
15
|
import { eventStormingSeniorTool } from './toolbar/senior-tool.js';
|
|
6
16
|
/**
|
|
7
|
-
* Event Storming —
|
|
17
|
+
* Event Storming rendering — ALWAYS registered, independent of any flag.
|
|
18
|
+
* Disabling `ddd-event-storming` hides only the creation tooling (see
|
|
19
|
+
* {@link DddEventStormingViewExtension}); boards already stormed must still
|
|
20
|
+
* paint, stay selectable, stay movable and keep their contextual toolbar. See
|
|
21
|
+
* `docs/adr/0009`.
|
|
22
|
+
*/
|
|
23
|
+
export class DddEventStormingRenderViewExtension extends ViewExtensionProvider {
|
|
24
|
+
constructor() {
|
|
25
|
+
super(...arguments);
|
|
26
|
+
this.name = 'affine-ddd-event-storming-render-gfx';
|
|
27
|
+
}
|
|
28
|
+
setup(context) {
|
|
29
|
+
super.setup(context);
|
|
30
|
+
context.register(EventStormingView);
|
|
31
|
+
context.register(EventStormingRendererExtension);
|
|
32
|
+
// The role VOCABULARY, always on. A role is written in the DOCUMENT, not in
|
|
33
|
+
// the tooling: the direction reveal of a typed flow, the inversion command
|
|
34
|
+
// and the toolbar entry that must not lie about one all read this, and they
|
|
35
|
+
// have to keep working on a board stormed while the flag was on and opened
|
|
36
|
+
// while it is off (`docs/adr/0009`, `docs/adr/0010`). The rules that JUDGE
|
|
37
|
+
// those roles stay in the flag-gated extension below.
|
|
38
|
+
context.register(RoleVocabularyExtension(EVENT_STORMING_ROLES));
|
|
39
|
+
if (this.isEdgeless(context.scope)) {
|
|
40
|
+
context.register(EventStormingInteraction);
|
|
41
|
+
context.register(eventStormingBoardToolbarExtension);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Event Storming creation tooling — independently flag-gated
|
|
47
|
+
* (`ddd-event-storming`): the senior toolbar button, its palette and the
|
|
48
|
+
* validation rules, profiles and quality nudges. Both halves are tooling: a
|
|
49
|
+
* board stormed while the flag was on keeps rendering when it goes off, it just
|
|
50
|
+
* stops being checked — and the profile it was put on stays written, unread,
|
|
51
|
+
* until the flag comes back.
|
|
8
52
|
*
|
|
9
53
|
* Note: its Templates-panel category is registered by the aggregate package's
|
|
10
54
|
* {@link DddTemplatesViewExtension} (gated by `ddd-templates`), so templates
|
|
@@ -22,6 +66,20 @@ export class DddEventStormingViewExtension extends ViewExtensionProvider {
|
|
|
22
66
|
setup(context) {
|
|
23
67
|
super.setup(context);
|
|
24
68
|
if (this.isEdgeless(context.scope)) {
|
|
69
|
+
context.register(ValidationRuleExtension(EVENT_STORMING_RULES));
|
|
70
|
+
context.register(ValidationProfileExtension(EVENT_STORMING_PROFILES));
|
|
71
|
+
context.register(QualityNudgeExtension(EVENT_STORMING_NUDGES));
|
|
72
|
+
// The Validation dropdown on a selected board's contextual toolbar. A
|
|
73
|
+
// SECOND module on the same element, through the `custom:` flavour slot:
|
|
74
|
+
// `eventStormingBoardToolbarExtension` is registered always-on because a
|
|
75
|
+
// stored board must keep its resize toggle, while choosing how hard to
|
|
76
|
+
// check it is tooling and belongs here. The config names no framework —
|
|
77
|
+
// it reads roles and profiles — so it is the very same object Wardley
|
|
78
|
+
// registers on its own flavour.
|
|
79
|
+
context.register(ToolbarModuleExtension({
|
|
80
|
+
id: BlockFlavourIdentifier('custom:affine:surface:eventStorming'),
|
|
81
|
+
config: validationToolbarConfig,
|
|
82
|
+
}));
|
|
25
83
|
context.register(eventStormingSeniorTool);
|
|
26
84
|
context.register(CommandExtension(eventStormingCommands, eventStormingCommandIcons));
|
|
27
85
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formicoidea/labre-framework-ddd-event-storming",
|
|
3
3
|
"description": "Labre ddd-event-storming framework for @formicoidea/labre-core.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.34.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"author": "lajola",
|
|
@@ -21,14 +21,18 @@
|
|
|
21
21
|
"./descriptor": {
|
|
22
22
|
"types": "./dist/descriptor.d.ts",
|
|
23
23
|
"import": "./dist/descriptor.js"
|
|
24
|
+
},
|
|
25
|
+
"./commands-manifest": {
|
|
26
|
+
"types": "./dist/commands-manifest.d.ts",
|
|
27
|
+
"import": "./dist/commands-manifest.js"
|
|
24
28
|
}
|
|
25
29
|
},
|
|
26
30
|
"files": [
|
|
27
31
|
"dist"
|
|
28
32
|
],
|
|
29
33
|
"dependencies": {
|
|
30
|
-
"@formicoidea/labre-core": "0.
|
|
31
|
-
"@formicoidea/labre-ddd-shared": "0.
|
|
34
|
+
"@formicoidea/labre-core": "0.34.1",
|
|
35
|
+
"@formicoidea/labre-ddd-shared": "0.34.1",
|
|
32
36
|
"lit": "^3.2.0"
|
|
33
37
|
}
|
|
34
38
|
}
|