@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/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: 12).
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: 12).
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>`,
@@ -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
- * core manifest could not restate them even if it wanted to. The
8
- * contribution therefore ships WITH the framework: in the bundled
9
- * distribution `@formicoidea/labre-framework-ddd-event-storming` carries it, and a host
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`.
@@ -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
- * core manifest could not restate them even if it wanted to. The
9
- * contribution therefore ships WITH the framework: in the bundled
10
- * distribution `@formicoidea/labre-framework-ddd-event-storming` carries it, and a host
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 — independently flag-gated (`ddd-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 { CommandExtension } from '@formicoidea/labre-core/std';
3
- import { eventStormingCommandIcons, eventStormingCommands, } from './commands.js';
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 — independently flag-gated (`ddd-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.32.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.32.0",
31
- "@formicoidea/labre-ddd-shared": "0.32.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
  }