@formicoidea/labre-framework-uml 0.42.0

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 (114) hide show
  1. package/LICENSE +384 -0
  2. package/dist/actions.d.ts +430 -0
  3. package/dist/actions.js +724 -0
  4. package/dist/background.d.ts +153 -0
  5. package/dist/background.js +557 -0
  6. package/dist/board-hit.d.ts +137 -0
  7. package/dist/board-hit.js +191 -0
  8. package/dist/commands-manifest.d.ts +19 -0
  9. package/dist/commands-manifest.js +571 -0
  10. package/dist/commands.d.ts +4 -0
  11. package/dist/commands.js +983 -0
  12. package/dist/component.d.ts +521 -0
  13. package/dist/component.js +708 -0
  14. package/dist/consts.d.ts +365 -0
  15. package/dist/consts.js +463 -0
  16. package/dist/descriptor.d.ts +12 -0
  17. package/dist/descriptor.js +10 -0
  18. package/dist/drawio-decode.d.ts +32 -0
  19. package/dist/drawio-decode.js +96 -0
  20. package/dist/drawio-import.d.ts +133 -0
  21. package/dist/drawio-import.js +885 -0
  22. package/dist/edge-morph.d.ts +154 -0
  23. package/dist/edge-morph.js +153 -0
  24. package/dist/edge-styles.d.ts +78 -0
  25. package/dist/edge-styles.js +210 -0
  26. package/dist/effects.d.ts +9 -0
  27. package/dist/effects.js +6 -0
  28. package/dist/element-renderer.d.ts +64 -0
  29. package/dist/element-renderer.js +114 -0
  30. package/dist/element-view.d.ts +234 -0
  31. package/dist/element-view.js +430 -0
  32. package/dist/export.d.ts +61 -0
  33. package/dist/export.js +41 -0
  34. package/dist/filename.d.ts +17 -0
  35. package/dist/filename.js +26 -0
  36. package/dist/frame-tag.d.ts +70 -0
  37. package/dist/frame-tag.js +78 -0
  38. package/dist/grammar.d.ts +695 -0
  39. package/dist/grammar.js +1436 -0
  40. package/dist/import.d.ts +306 -0
  41. package/dist/import.js +1270 -0
  42. package/dist/index.d.ts +47 -0
  43. package/dist/index.js +67 -0
  44. package/dist/interchange.d.ts +125 -0
  45. package/dist/interchange.js +372 -0
  46. package/dist/keywords.d.ts +204 -0
  47. package/dist/keywords.js +361 -0
  48. package/dist/kinds.d.ts +73 -0
  49. package/dist/kinds.js +91 -0
  50. package/dist/legend.d.ts +30 -0
  51. package/dist/legend.js +255 -0
  52. package/dist/model.d.ts +862 -0
  53. package/dist/model.js +1316 -0
  54. package/dist/morph.d.ts +178 -0
  55. package/dist/morph.js +496 -0
  56. package/dist/node/act-glyphs.d.ts +30 -0
  57. package/dist/node/act-glyphs.js +142 -0
  58. package/dist/node/compartment-watcher.d.ts +107 -0
  59. package/dist/node/compartment-watcher.js +163 -0
  60. package/dist/node/fit.d.ts +85 -0
  61. package/dist/node/fit.js +129 -0
  62. package/dist/node/node-renderer.d.ts +49 -0
  63. package/dist/node/node-renderer.js +611 -0
  64. package/dist/node/node-view.d.ts +51 -0
  65. package/dist/node/node-view.js +89 -0
  66. package/dist/node/paint.d.ts +84 -0
  67. package/dist/node/paint.js +143 -0
  68. package/dist/node/sd-glyphs.d.ts +36 -0
  69. package/dist/node/sd-glyphs.js +79 -0
  70. package/dist/node/stm-glyphs.d.ts +22 -0
  71. package/dist/node/stm-glyphs.js +111 -0
  72. package/dist/node/tier-metrics.d.ts +38 -0
  73. package/dist/node/tier-metrics.js +45 -0
  74. package/dist/operators.d.ts +45 -0
  75. package/dist/operators.js +75 -0
  76. package/dist/plantuml-import.d.ts +113 -0
  77. package/dist/plantuml-import.js +1723 -0
  78. package/dist/plantuml.d.ts +94 -0
  79. package/dist/plantuml.js +985 -0
  80. package/dist/presets.d.ts +149 -0
  81. package/dist/presets.js +302 -0
  82. package/dist/profiles.d.ts +2 -0
  83. package/dist/profiles.js +358 -0
  84. package/dist/reading.d.ts +160 -0
  85. package/dist/reading.js +648 -0
  86. package/dist/roles.d.ts +257 -0
  87. package/dist/roles.js +997 -0
  88. package/dist/rules.d.ts +267 -0
  89. package/dist/rules.js +3090 -0
  90. package/dist/templates.d.ts +2 -0
  91. package/dist/templates.js +197 -0
  92. package/dist/toolbar/config.d.ts +228 -0
  93. package/dist/toolbar/config.js +833 -0
  94. package/dist/toolbar/icons.d.ts +440 -0
  95. package/dist/toolbar/icons.js +711 -0
  96. package/dist/toolbar/senior-tool.d.ts +11 -0
  97. package/dist/toolbar/senior-tool.js +21 -0
  98. package/dist/toolbar/uml-menu.d.ts +17 -0
  99. package/dist/toolbar/uml-menu.js +20 -0
  100. package/dist/toolbar/uml-senior-button.d.ts +18 -0
  101. package/dist/toolbar/uml-senior-button.js +22 -0
  102. package/dist/translations.d.ts +27 -0
  103. package/dist/translations.js +117 -0
  104. package/dist/view.d.ts +33 -0
  105. package/dist/view.js +236 -0
  106. package/dist/xmi-import.d.ts +174 -0
  107. package/dist/xmi-import.js +2268 -0
  108. package/dist/xmi.d.ts +61 -0
  109. package/dist/xmi.js +1860 -0
  110. package/dist/xml-reader.d.ts +157 -0
  111. package/dist/xml-reader.js +430 -0
  112. package/dist/xml.d.ts +72 -0
  113. package/dist/xml.js +96 -0
  114. package/package.json +38 -0
package/dist/roles.js ADDED
@@ -0,0 +1,997 @@
1
+ /**
2
+ * Role ids, keyed by their own name.
3
+ *
4
+ * Keyed by the ROLE and not by the `kind`: UML has thirty-eight node kinds and
5
+ * forty-one node roles, because `uml:classifier`, `uml:control-node` and
6
+ * `uml:pseudostate` are parents nothing is ever drawn as. ({@link
7
+ * UML_ROLE.node} is a parent too, and IS drawn — §19.4 makes a Node
8
+ * instantiable — so it does not add a role of its own.)
9
+ * {@link UML_ROLE_OF_KIND} is the bridge, and it is the only place the two
10
+ * vocabularies meet.
11
+ */
12
+ export const UML_ROLE = {
13
+ classifier: 'uml:classifier',
14
+ class: 'uml:class',
15
+ interface: 'uml:interface',
16
+ enumeration: 'uml:enumeration',
17
+ object: 'uml:object',
18
+ package: 'uml:package',
19
+ note: 'uml:note',
20
+ actor: 'uml:actor',
21
+ 'use-case': 'uml:use-case',
22
+ component: 'uml:component',
23
+ port: 'uml:port',
24
+ 'provided-interface': 'uml:provided-interface',
25
+ 'required-interface': 'uml:required-interface',
26
+ artifact: 'uml:artifact',
27
+ node: 'uml:node',
28
+ device: 'uml:device',
29
+ 'execution-environment': 'uml:execution-environment',
30
+ 'control-node': 'uml:control-node',
31
+ action: 'uml:action',
32
+ initial: 'uml:initial',
33
+ 'activity-final': 'uml:activity-final',
34
+ 'flow-final': 'uml:flow-final',
35
+ decision: 'uml:decision',
36
+ fork: 'uml:fork',
37
+ 'object-node': 'uml:object-node',
38
+ 'send-signal': 'uml:send-signal',
39
+ 'accept-event': 'uml:accept-event',
40
+ 'time-event': 'uml:time-event',
41
+ state: 'uml:state',
42
+ 'final-state': 'uml:final-state',
43
+ pseudostate: 'uml:pseudostate',
44
+ choice: 'uml:choice',
45
+ junction: 'uml:junction',
46
+ 'shallow-history': 'uml:shallow-history',
47
+ 'deep-history': 'uml:deep-history',
48
+ 'entry-point': 'uml:entry-point',
49
+ 'exit-point': 'uml:exit-point',
50
+ terminate: 'uml:terminate',
51
+ lifeline: 'uml:lifeline',
52
+ execution: 'uml:execution',
53
+ destruction: 'uml:destruction',
54
+ name: 'uml:name',
55
+ attributes: 'uml:attributes',
56
+ operations: 'uml:operations',
57
+ label: 'uml:label',
58
+ 'lifeline-ident': 'uml:lifeline-ident',
59
+ diagram: 'uml:diagram',
60
+ subject: 'uml:subject',
61
+ partition: 'uml:partition',
62
+ region: 'uml:region',
63
+ fragment: 'uml:fragment',
64
+ operand: 'uml:operand',
65
+ association: 'uml:association',
66
+ aggregation: 'uml:aggregation',
67
+ composition: 'uml:composition',
68
+ generalization: 'uml:generalization',
69
+ realization: 'uml:realization',
70
+ dependency: 'uml:dependency',
71
+ anchor: 'uml:anchor',
72
+ include: 'uml:include',
73
+ extend: 'uml:extend',
74
+ deploy: 'uml:deploy',
75
+ manifest: 'uml:manifest',
76
+ 'communication-path': 'uml:communication-path',
77
+ 'control-flow': 'uml:control-flow',
78
+ 'object-flow': 'uml:object-flow',
79
+ transition: 'uml:transition',
80
+ message: 'uml:message',
81
+ 'message-sync': 'uml:message-sync',
82
+ 'message-async': 'uml:message-async',
83
+ 'message-reply': 'uml:message-reply',
84
+ 'message-create': 'uml:message-create',
85
+ 'message-delete': 'uml:message-delete',
86
+ };
87
+ const _everyRoleIsMapped = true;
88
+ void _everyRoleIsMapped;
89
+ /**
90
+ * i18n key stem of a role id: `uml:class` → `com.labre.uml.role.class`.
91
+ *
92
+ * Exported because the creation sites seed an artefact's NAME from the same key
93
+ * a role labels itself with (a diagram drawn in a French host should not be
94
+ * called "Diagram"): one key for the role and for the seed, because they are the
95
+ * same noun and a host must not be asked to word it twice.
96
+ */
97
+ export const umlRoleKey = (local) => `com.labre.uml.role.${local}`;
98
+ /** The same, from a full role id. */
99
+ const roleKey = (id) => umlRoleKey(id.slice('uml:'.length));
100
+ /** The diagram frame's own key, read by `createUmlDiagram` to seed its name. */
101
+ export const umlDiagramRoleKey = roleKey(UML_ROLE.diagram);
102
+ /** The subject's own key, read by `createUmlSubject` to seed its name. */
103
+ export const umlSubjectRoleKey = roleKey(UML_ROLE.subject);
104
+ /** The partition's own key, read by `createUmlPartition` to seed its name. */
105
+ export const umlPartitionRoleKey = roleKey(UML_ROLE.partition);
106
+ /** The composite state's own key, read by `createUmlRegion` to seed its name. */
107
+ export const umlRegionRoleKey = roleKey(UML_ROLE.region);
108
+ /**
109
+ * The combined fragment's own key.
110
+ *
111
+ * Unlike the four above it seeds NO name — §17.6.4 writes a guard only where
112
+ * there is one, so `UmlFragmentElementModel.name` starts empty — and the key is
113
+ * exported for the toolbar and the templates, which still have to call the
114
+ * thing something in the user's language.
115
+ */
116
+ export const umlFragmentRoleKey = roleKey(UML_ROLE.fragment);
117
+ /**
118
+ * The classifiers (§9.2, §11.4.4) — the compartmented rectangle, and the three
119
+ * keywords that tell its flavours apart — plus every artefact that is not a
120
+ * classifier and is drawn as something else entirely: the four of phase 1, and
121
+ * the eight components and deployment artefacts phase 2 appended.
122
+ *
123
+ * `uml:classifier` is declared but never stamped on an element: it is the
124
+ * ancestor a rule about naming, about visibility or about features is written
125
+ * on, so that the same sentence reaches a class, an interface and an enumeration
126
+ * without being restated three times.
127
+ */
128
+ const ELEMENT_DEFS = [
129
+ {
130
+ id: UML_ROLE.classifier,
131
+ kind: 'node',
132
+ labelKey: roleKey(UML_ROLE.classifier),
133
+ labelFallback: 'Classifier',
134
+ },
135
+ {
136
+ id: UML_ROLE.class,
137
+ parent: UML_ROLE.classifier,
138
+ kind: 'node',
139
+ labelKey: roleKey(UML_ROLE.class),
140
+ labelFallback: 'Class',
141
+ },
142
+ {
143
+ id: UML_ROLE.interface,
144
+ parent: UML_ROLE.classifier,
145
+ kind: 'node',
146
+ labelKey: roleKey(UML_ROLE.interface),
147
+ labelFallback: 'Interface',
148
+ },
149
+ {
150
+ id: UML_ROLE.enumeration,
151
+ parent: UML_ROLE.classifier,
152
+ kind: 'node',
153
+ labelKey: roleKey(UML_ROLE.enumeration),
154
+ labelFallback: 'Enumeration',
155
+ },
156
+ // An INSTANCE of a classifier (§9.8), not a kind of one — see the note at the
157
+ // top of this file on why it stays out of the chain above.
158
+ {
159
+ id: UML_ROLE.object,
160
+ kind: 'node',
161
+ labelKey: roleKey(UML_ROLE.object),
162
+ labelFallback: 'Object',
163
+ },
164
+ {
165
+ id: UML_ROLE.package,
166
+ kind: 'node',
167
+ labelKey: roleKey(UML_ROLE.package),
168
+ labelFallback: 'Package',
169
+ },
170
+ {
171
+ id: UML_ROLE.note,
172
+ kind: 'node',
173
+ labelKey: roleKey(UML_ROLE.note),
174
+ labelFallback: 'Note',
175
+ },
176
+ {
177
+ id: UML_ROLE.actor,
178
+ kind: 'node',
179
+ labelKey: roleKey(UML_ROLE.actor),
180
+ labelFallback: 'Actor',
181
+ },
182
+ {
183
+ id: UML_ROLE['use-case'],
184
+ kind: 'node',
185
+ labelKey: roleKey(UML_ROLE['use-case']),
186
+ labelFallback: 'Use case',
187
+ },
188
+ // ── The component artefacts (phase 2) ───────────────────────────────────
189
+ // §11.6.4: a Component is drawn as a rectangle with the two-tabbed icon in
190
+ // its top-right corner. Flat, and not under `uml:classifier`: see the header.
191
+ {
192
+ id: UML_ROLE.component,
193
+ kind: 'node',
194
+ labelKey: roleKey(UML_ROLE.component),
195
+ labelFallback: 'Component',
196
+ },
197
+ // §11.3.4: a Port is a small square ON the border of the component that owns
198
+ // it — a part of that component, never a specialisation of it.
199
+ {
200
+ id: UML_ROLE.port,
201
+ kind: 'node',
202
+ labelKey: roleKey(UML_ROLE.port),
203
+ labelFallback: 'Port',
204
+ },
205
+ // §10.4.4: the ball-and-socket pair. Two roles rather than one `uml:interface
206
+ // -point` with a flag, because they are two STATEMENTS — one says a component
207
+ // offers a service, the other that it needs one — and a rule about a required
208
+ // interface left dangling must not fall on every lollipop on the sheet.
209
+ {
210
+ id: UML_ROLE['provided-interface'],
211
+ kind: 'node',
212
+ labelKey: roleKey(UML_ROLE['provided-interface']),
213
+ labelFallback: 'Provided interface',
214
+ },
215
+ {
216
+ id: UML_ROLE['required-interface'],
217
+ kind: 'node',
218
+ labelKey: roleKey(UML_ROLE['required-interface']),
219
+ labelFallback: 'Required interface',
220
+ },
221
+ // ── The deployment artefacts (phase 2) ──────────────────────────────────
222
+ // §19.3.4: an Artifact is a physical file — drawn as a rectangle with the
223
+ // document icon, and named `«artifact»` over its own file name.
224
+ {
225
+ id: UML_ROLE.artifact,
226
+ kind: 'node',
227
+ labelKey: roleKey(UML_ROLE.artifact),
228
+ labelFallback: 'Artifact',
229
+ },
230
+ // §19.4: the cube, and the two things the spec derives from it. The parent is
231
+ // itself drawn — a Node is instantiable — which is what makes this chain
232
+ // different from `uml:classifier`.
233
+ {
234
+ id: UML_ROLE.node,
235
+ kind: 'node',
236
+ labelKey: roleKey(UML_ROLE.node),
237
+ labelFallback: 'Node',
238
+ },
239
+ {
240
+ id: UML_ROLE.device,
241
+ parent: UML_ROLE.node,
242
+ kind: 'node',
243
+ labelKey: roleKey(UML_ROLE.device),
244
+ labelFallback: 'Device',
245
+ },
246
+ {
247
+ id: UML_ROLE['execution-environment'],
248
+ parent: UML_ROLE.node,
249
+ kind: 'node',
250
+ labelKey: roleKey(UML_ROLE['execution-environment']),
251
+ labelFallback: 'Execution environment',
252
+ },
253
+ // ── The activity artefacts (phase 2) ────────────────────────────────────
254
+ // §15.3.4: an Action is the round-cornered rectangle work is written in. Flat
255
+ // — it is the only thing on an activity diagram that DOES anything, and the
256
+ // control nodes below route between actions rather than specialise them.
257
+ {
258
+ id: UML_ROLE.action,
259
+ kind: 'node',
260
+ labelKey: roleKey(UML_ROLE.action),
261
+ labelFallback: 'Action',
262
+ },
263
+ // §15.3.4: the ControlNodes. An abstract parent, stamped on nothing, for the
264
+ // reason `uml:classifier` is one: "a control node carries no name" and "a
265
+ // control flow may end on a control node" are sentences about the five of
266
+ // them at once, and restating each five times is how four of them stay right.
267
+ {
268
+ id: UML_ROLE['control-node'],
269
+ kind: 'node',
270
+ labelKey: roleKey(UML_ROLE['control-node']),
271
+ labelFallback: 'Control node',
272
+ },
273
+ // The filled disc where a flow begins. Filed under the control node because
274
+ // §15.3.4 puts it there — and USED on state machines too, where §14.2.4 draws
275
+ // the identical disc and means the identical thing (one per region, no
276
+ // incoming edge). That cross-family reading is deliberate and recorded here:
277
+ // a rule written on `uml:initial` fires on both diagrams, which is what the
278
+ // two specifications between them say.
279
+ {
280
+ id: UML_ROLE.initial,
281
+ parent: UML_ROLE['control-node'],
282
+ kind: 'node',
283
+ labelKey: roleKey(UML_ROLE.initial),
284
+ labelFallback: 'Initial node',
285
+ },
286
+ {
287
+ id: UML_ROLE['activity-final'],
288
+ parent: UML_ROLE['control-node'],
289
+ kind: 'node',
290
+ labelKey: roleKey(UML_ROLE['activity-final']),
291
+ labelFallback: 'Activity final',
292
+ },
293
+ {
294
+ id: UML_ROLE['flow-final'],
295
+ parent: UML_ROLE['control-node'],
296
+ kind: 'node',
297
+ labelKey: roleKey(UML_ROLE['flow-final']),
298
+ labelFallback: 'Flow final',
299
+ },
300
+ {
301
+ id: UML_ROLE.decision,
302
+ parent: UML_ROLE['control-node'],
303
+ kind: 'node',
304
+ labelKey: roleKey(UML_ROLE.decision),
305
+ labelFallback: 'Decision',
306
+ },
307
+ // §15.3.4 draws a fork and a join as the SAME bar and tells them apart by how
308
+ // many edges run in and out. One role, therefore: two would be this
309
+ // vocabulary claiming a distinction the picture does not carry, and a rule
310
+ // that wants it can count the edges.
311
+ {
312
+ id: UML_ROLE.fork,
313
+ parent: UML_ROLE['control-node'],
314
+ kind: 'node',
315
+ labelKey: roleKey(UML_ROLE.fork),
316
+ labelFallback: 'Fork / join',
317
+ },
318
+ // §15.4.4: an ObjectNode is the DATA that flows between actions, drawn as a
319
+ // plain rectangle. NOT a control node — it is what moves, not what routes —
320
+ // and not an `uml:object` either: an object diagram's instance specification
321
+ // and an activity's object node are different metaclasses that happen to
322
+ // share a rectangle, which is exactly the trap this vocabulary avoids.
323
+ {
324
+ id: UML_ROLE['object-node'],
325
+ kind: 'node',
326
+ labelKey: roleKey(UML_ROLE['object-node']),
327
+ labelFallback: 'Object node',
328
+ },
329
+ // §16.3.4: the two invocation shapes, drawn as the convex and the concave
330
+ // pentagon. Flat and separate, because they are two statements — one sends,
331
+ // the other waits — and the notation gives each its own silhouette.
332
+ {
333
+ id: UML_ROLE['send-signal'],
334
+ kind: 'node',
335
+ labelKey: roleKey(UML_ROLE['send-signal']),
336
+ labelFallback: 'Send signal',
337
+ },
338
+ {
339
+ id: UML_ROLE['accept-event'],
340
+ kind: 'node',
341
+ labelKey: roleKey(UML_ROLE['accept-event']),
342
+ labelFallback: 'Accept event',
343
+ },
344
+ // §16.10.4: the hourglass — an AcceptEventAction whose trigger is a
345
+ // TimeEvent. A role of its own rather than a flag on `uml:accept-event`,
346
+ // because the notation draws a different picture for it and a reader tells
347
+ // "waits for an order" from "waits two days" by that picture alone.
348
+ {
349
+ id: UML_ROLE['time-event'],
350
+ kind: 'node',
351
+ labelKey: roleKey(UML_ROLE['time-event']),
352
+ labelFallback: 'Time event',
353
+ },
354
+ // ── The state machine artefacts (phase 2) ───────────────────────────────
355
+ // §14.2.4: a State is the round-cornered rectangle a machine rests in. Flat,
356
+ // and emphatically not under `uml:action`: an action is work that happens, a
357
+ // state is a condition that holds, and the two share a silhouette and
358
+ // nothing else.
359
+ {
360
+ id: UML_ROLE.state,
361
+ kind: 'node',
362
+ labelKey: roleKey(UML_ROLE.state),
363
+ labelFallback: 'State',
364
+ },
365
+ // The bullseye. Drawn identically to `uml:activity-final` and kept a separate
366
+ // role all the same, because the two are different metaclasses on different
367
+ // diagrams: a FinalState is a vertex a transition lands on, an
368
+ // ActivityFinalNode stops every flow in the activity. A rule about one must
369
+ // not be handed the other.
370
+ {
371
+ id: UML_ROLE['final-state'],
372
+ kind: 'node',
373
+ labelKey: roleKey(UML_ROLE['final-state']),
374
+ labelFallback: 'Final state',
375
+ },
376
+ // §14.2.4: the Pseudostates. The state machine's own abstract parent, the
377
+ // counterpart of `uml:control-node` and stamped on nothing for the same
378
+ // reason: "a pseudostate carries no name" and "a pseudostate is never where a
379
+ // machine rests" are sentences about all seven at once.
380
+ {
381
+ id: UML_ROLE.pseudostate,
382
+ kind: 'node',
383
+ labelKey: roleKey(UML_ROLE.pseudostate),
384
+ labelFallback: 'Pseudostate',
385
+ },
386
+ {
387
+ id: UML_ROLE.choice,
388
+ parent: UML_ROLE.pseudostate,
389
+ kind: 'node',
390
+ labelKey: roleKey(UML_ROLE.choice),
391
+ labelFallback: 'Choice',
392
+ },
393
+ {
394
+ id: UML_ROLE.junction,
395
+ parent: UML_ROLE.pseudostate,
396
+ kind: 'node',
397
+ labelKey: roleKey(UML_ROLE.junction),
398
+ labelFallback: 'Junction',
399
+ },
400
+ {
401
+ id: UML_ROLE['shallow-history'],
402
+ parent: UML_ROLE.pseudostate,
403
+ kind: 'node',
404
+ labelKey: roleKey(UML_ROLE['shallow-history']),
405
+ labelFallback: 'Shallow history',
406
+ },
407
+ {
408
+ id: UML_ROLE['deep-history'],
409
+ parent: UML_ROLE.pseudostate,
410
+ kind: 'node',
411
+ labelKey: roleKey(UML_ROLE['deep-history']),
412
+ labelFallback: 'Deep history',
413
+ },
414
+ {
415
+ id: UML_ROLE['entry-point'],
416
+ parent: UML_ROLE.pseudostate,
417
+ kind: 'node',
418
+ labelKey: roleKey(UML_ROLE['entry-point']),
419
+ labelFallback: 'Entry point',
420
+ },
421
+ {
422
+ id: UML_ROLE['exit-point'],
423
+ parent: UML_ROLE.pseudostate,
424
+ kind: 'node',
425
+ labelKey: roleKey(UML_ROLE['exit-point']),
426
+ labelFallback: 'Exit point',
427
+ },
428
+ {
429
+ id: UML_ROLE.terminate,
430
+ parent: UML_ROLE.pseudostate,
431
+ kind: 'node',
432
+ labelKey: roleKey(UML_ROLE.terminate),
433
+ labelFallback: 'Terminate',
434
+ },
435
+ // ── The interaction artefacts (phase 3) ─────────────────────────────────
436
+ // §17.3.4: the participant column — a named head over a dashed spine. FLAT
437
+ // under nothing, and pointedly not a child of `uml:object` although its name
438
+ // is written with the same `name : Type` grammar (§17.3.4 reuses §9.8.4's
439
+ // ident): an instance specification is a thing that exists, a lifeline is a
440
+ // thing that TAKES PART, and the rules that police the two share nothing.
441
+ {
442
+ id: UML_ROLE.lifeline,
443
+ kind: 'node',
444
+ labelKey: roleKey(UML_ROLE.lifeline),
445
+ labelFallback: 'Lifeline',
446
+ },
447
+ // §17.2.4's ExecutionSpecification: the thin bar drawn ON a spine saying the
448
+ // participant is busy for that stretch of the conversation. A node of its
449
+ // own rather than a decoration of the lifeline, because a lifeline may carry
450
+ // several and they nest — and because the author positions each one by hand.
451
+ {
452
+ id: UML_ROLE.execution,
453
+ kind: 'node',
454
+ labelKey: roleKey(UML_ROLE.execution),
455
+ labelFallback: 'Execution',
456
+ },
457
+ // §17.2.4's DestructionOccurrenceSpecification: the X that ends a lifeline.
458
+ // Flat, and not a child of `uml:terminate` although both are drawn as a bare
459
+ // cross: a terminate pseudostate kills a state MACHINE, a destruction ends
460
+ // one PARTICIPANT's life in one interaction.
461
+ {
462
+ id: UML_ROLE.destruction,
463
+ kind: 'node',
464
+ labelKey: roleKey(UML_ROLE.destruction),
465
+ labelFallback: 'Destruction',
466
+ },
467
+ ];
468
+ /**
469
+ * The written tiers of an artefact's label, as roles on the canvas TEXT
470
+ * elements that carry them.
471
+ *
472
+ * A UML node is a GROUP — the shape plus its words — for the reason C4's
473
+ * component is one (PO recette, 28/08/2026): an architect writes ON the picture,
474
+ * in place, with the ordinary text toolbar, and a field would have been a second
475
+ * place to write the same sentence reachable only through a form. Which leaves
476
+ * one question — given a group of a box and three texts, WHICH text is the
477
+ * attribute compartment? — and the role is the answer that survives a child
478
+ * reordered inside its group, a group ungrouped and regrouped, a tier copied to
479
+ * another node, a tier deleted and redrawn.
480
+ *
481
+ * `uml:name` is the name compartment of every kind that HAS compartments, plus
482
+ * the package's and the note's single line. `uml:label` is the one word written
483
+ * against an actor or inside a use case: the same tier, named differently
484
+ * because it is not a compartment — there is no rectangle for it to be a
485
+ * compartment OF — and a reading profile has to know which of the two to ask for
486
+ * (see `reading.ts`).
487
+ *
488
+ * `kind: 'text'`, as `c4:title` and `wardley:label` are: the BOX of a text
489
+ * element is a creation-time default and not a statement about anything, so a
490
+ * rule measuring one must measure its ink.
491
+ *
492
+ * The GROUP itself is deliberately role-less: a role makes an element count as
493
+ * an artefact for every rule written against roles, and the wrapper round a box
494
+ * is not a second box.
495
+ */
496
+ const TIER_DEFS = [
497
+ {
498
+ id: UML_ROLE.name,
499
+ kind: 'text',
500
+ labelKey: roleKey(UML_ROLE.name),
501
+ labelFallback: 'Name',
502
+ },
503
+ {
504
+ id: UML_ROLE.attributes,
505
+ kind: 'text',
506
+ labelKey: roleKey(UML_ROLE.attributes),
507
+ labelFallback: 'Attributes',
508
+ },
509
+ {
510
+ id: UML_ROLE.operations,
511
+ kind: 'text',
512
+ labelKey: roleKey(UML_ROLE.operations),
513
+ labelFallback: 'Operations',
514
+ },
515
+ {
516
+ id: UML_ROLE.label,
517
+ kind: 'text',
518
+ labelKey: roleKey(UML_ROLE.label),
519
+ labelFallback: 'Label',
520
+ },
521
+ // §17.3.4's LIFELINE HEAD, and the reason it is a fourth tier rather than a
522
+ // fourth user of `uml:label`.
523
+ //
524
+ // What is written in a lifeline's head is not a name: it is a
525
+ // `<lifelineident>` — `order : Order`, `self`, `customers[i] : Customer` — a
526
+ // production the clause prints a BNF for and `parseLifelineIdent` reads back.
527
+ // An actor's word and a use case's phrase are prose, and the clause that
528
+ // draws them prints no grammar at all. Filing all three under `uml:label`
529
+ // would mean the two questions a tier can be asked — "is anything written
530
+ // here" and "does what is written parse" — could only ever be asked of the
531
+ // three together: the spelling rule would indict `Place an order (fast)` on
532
+ // an ellipse, and the naming rule would tell an author of a sequence diagram
533
+ // that their lifeline is an unnamed use case.
534
+ //
535
+ // FLAT, with no `parent: UML_ROLE.label`, and that is the same statement made
536
+ // once more: `roleIsA` would put every rule written on the label tier back on
537
+ // the head, which is precisely what splitting the tier was for. Hierarchy in
538
+ // this vocabulary is only ever the specification's own, and §17.3.4 makes a
539
+ // lifeline ident a kind of nothing.
540
+ {
541
+ id: UML_ROLE['lifeline-ident'],
542
+ kind: 'text',
543
+ labelKey: roleKey(UML_ROLE['lifeline-ident']),
544
+ labelFallback: 'Lifeline identifier',
545
+ },
546
+ ];
547
+ /**
548
+ * The frames: the sheet the diagram is drawn on (Annex A) and the rectangle a
549
+ * use case diagram draws round the system's own use cases (§18.1.4).
550
+ *
551
+ * Parent-less, and unrelated to each other: a diagram frame is WHERE the drawing
552
+ * lives, a subject is a statement made INSIDE it about which use cases the
553
+ * system offers. Neither is an element of the model, so a rule about classes,
554
+ * packages, actors or use cases must fall on neither.
555
+ *
556
+ * The subject has no children, unlike `c4:boundary`: §18.1.4 draws one rectangle
557
+ * with one name — no variants, no keyword line, no second tier — so there is
558
+ * nothing here for a specialisation to specialise.
559
+ */
560
+ const FRAME_DEFS = [
561
+ {
562
+ id: UML_ROLE.diagram,
563
+ kind: 'node',
564
+ labelKey: roleKey(UML_ROLE.diagram),
565
+ labelFallback: 'UML diagram',
566
+ },
567
+ {
568
+ id: UML_ROLE.subject,
569
+ kind: 'node',
570
+ labelKey: roleKey(UML_ROLE.subject),
571
+ labelFallback: 'Subject',
572
+ },
573
+ // §15.6.4: the swimlane. A frame and not an artefact, for the reason the
574
+ // subject is one — it says who is responsible for the actions drawn in it,
575
+ // and a rule written about actions must not fall on the lane holding them.
576
+ // Membership is geometry at read time (R11), never a stored list.
577
+ {
578
+ id: UML_ROLE.partition,
579
+ kind: 'node',
580
+ labelKey: roleKey(UML_ROLE.partition),
581
+ labelFallback: 'Partition',
582
+ },
583
+ // §14.2.4: the composite state. A frame, although it IS a state — the one
584
+ // place this vocabulary files a model element as furniture, and it does so
585
+ // because the picture is furniture: a rectangle drawn round a sub-machine,
586
+ // with the sub-states inside it by geometry. A rule about states is written
587
+ // on `uml:state`, which this is deliberately not a child of.
588
+ {
589
+ id: UML_ROLE.region,
590
+ kind: 'node',
591
+ labelKey: roleKey(UML_ROLE.region),
592
+ labelFallback: 'Composite state',
593
+ },
594
+ // §17.6.4: the COMBINED FRAGMENT — the rectangle drawn round part of a
595
+ // conversation with `alt`, `loop` or `opt` in its corner — and, under the
596
+ // `ref` operator, §17.7.4's InteractionUse. A frame for the reason the
597
+ // partition is one: it says something ABOUT the messages inside it, and a
598
+ // rule written on messages must not fall on the box round them. What it
599
+ // holds is geometry at read time (R11), never a stored list.
600
+ {
601
+ id: UML_ROLE.fragment,
602
+ kind: 'node',
603
+ labelKey: roleKey(UML_ROLE.fragment),
604
+ labelFallback: 'Combined fragment',
605
+ },
606
+ // One OPERAND of that fragment — the band between two dashed separators, with
607
+ // its own guard. Declared and never stamped on an element, exactly as
608
+ // `uml:classifier` is: an operand is an INSTANCE ZONE of the fragment's own
609
+ // plot (the mechanism the BPMN pool's lanes use), so the only thing that
610
+ // carries this id is a reported zone. It is here so a rule about what may sit
611
+ // in an operand has a name to be written against.
612
+ {
613
+ id: UML_ROLE.operand,
614
+ kind: 'node',
615
+ labelKey: roleKey(UML_ROLE.operand),
616
+ labelFallback: 'Operand',
617
+ },
618
+ ];
619
+ /**
620
+ * The relationships — nine typed edges under `docs/adr/0010`.
621
+ *
622
+ * Tier 1 of that ADR is generic — `source` is the subject of the role's verb,
623
+ * `target` its object. Tier 2 is the `direction` block, and UML is the framework
624
+ * that needs it most: the notation's whole grammar is in WHICH END carries the
625
+ * decoration, and an architect who draws a generalization the wrong way round
626
+ * has drawn the opposite statement. So every relationship that HAS a reading
627
+ * declares its verb and the gesture that produces it.
628
+ *
629
+ * Two declare none, and that is a statement too:
630
+ *
631
+ * - `uml:association` is UNDIRECTED (§11.5.4): a plain line between two
632
+ * classifiers says they are related and nothing about which way. Giving it a
633
+ * default verb would put words in the diagram's mouth every time somebody
634
+ * dragged a line and moved on — and, worse, would make the reveal claim a
635
+ * direction the notation explicitly declines to draw.
636
+ * - `uml:anchor` is the dotted line from a note to what it annotates. It has no
637
+ * verb because a comment is not a relationship between two model elements; it
638
+ * is a piece of paper pinned to one.
639
+ *
640
+ * ## The diamond ends, and where the source is
641
+ *
642
+ * For `uml:aggregation` and `uml:composition` the source is the WHOLE — the end
643
+ * that carries the diamond, which `actions.ts` arms as the connector's FRONT
644
+ * endpoint. That is the one asymmetry a user can get backwards without noticing,
645
+ * because both ends of the line look alike until the diamond lands, so the
646
+ * gesture hint says it in as many words.
647
+ */
648
+ const RELATIONSHIP_DEFS = [
649
+ {
650
+ id: UML_ROLE.association,
651
+ kind: 'edge',
652
+ labelKey: roleKey(UML_ROLE.association),
653
+ labelFallback: 'Association',
654
+ },
655
+ {
656
+ id: UML_ROLE.aggregation,
657
+ parent: UML_ROLE.association,
658
+ kind: 'edge',
659
+ labelKey: roleKey(UML_ROLE.aggregation),
660
+ labelFallback: 'Aggregation',
661
+ direction: {
662
+ verbKey: `${roleKey(UML_ROLE.aggregation)}.verb`,
663
+ verbFallback: 'is composed of',
664
+ gestureHintKey: `${roleKey(UML_ROLE.aggregation)}.gesture`,
665
+ gestureHintFallback: 'Drag from the whole to the part — the diamond lands on the whole.',
666
+ },
667
+ },
668
+ {
669
+ id: UML_ROLE.composition,
670
+ parent: UML_ROLE.association,
671
+ kind: 'edge',
672
+ labelKey: roleKey(UML_ROLE.composition),
673
+ labelFallback: 'Composition',
674
+ direction: {
675
+ verbKey: `${roleKey(UML_ROLE.composition)}.verb`,
676
+ verbFallback: 'is composed of',
677
+ gestureHintKey: `${roleKey(UML_ROLE.composition)}.gesture`,
678
+ gestureHintFallback: 'Drag from the whole to the part — the diamond lands on the whole.',
679
+ },
680
+ },
681
+ {
682
+ id: UML_ROLE.generalization,
683
+ kind: 'edge',
684
+ labelKey: roleKey(UML_ROLE.generalization),
685
+ labelFallback: 'Generalization',
686
+ direction: {
687
+ verbKey: `${roleKey(UML_ROLE.generalization)}.verb`,
688
+ verbFallback: 'is a',
689
+ gestureHintKey: `${roleKey(UML_ROLE.generalization)}.gesture`,
690
+ gestureHintFallback: 'Drag from the specific classifier to the general one — the hollow ' +
691
+ 'triangle lands on the general.',
692
+ },
693
+ },
694
+ {
695
+ id: UML_ROLE.realization,
696
+ kind: 'edge',
697
+ labelKey: roleKey(UML_ROLE.realization),
698
+ labelFallback: 'Realization',
699
+ direction: {
700
+ verbKey: `${roleKey(UML_ROLE.realization)}.verb`,
701
+ verbFallback: 'realizes',
702
+ gestureHintKey: `${roleKey(UML_ROLE.realization)}.gesture`,
703
+ gestureHintFallback: 'Drag from the class to the interface it implements.',
704
+ },
705
+ },
706
+ {
707
+ id: UML_ROLE.dependency,
708
+ kind: 'edge',
709
+ labelKey: roleKey(UML_ROLE.dependency),
710
+ labelFallback: 'Dependency',
711
+ direction: {
712
+ verbKey: `${roleKey(UML_ROLE.dependency)}.verb`,
713
+ verbFallback: 'depends on',
714
+ gestureHintKey: `${roleKey(UML_ROLE.dependency)}.gesture`,
715
+ gestureHintFallback: 'Drag from the element that needs the other to the one it needs.',
716
+ },
717
+ },
718
+ {
719
+ id: UML_ROLE.anchor,
720
+ kind: 'edge',
721
+ labelKey: roleKey(UML_ROLE.anchor),
722
+ labelFallback: 'Anchor',
723
+ },
724
+ {
725
+ id: UML_ROLE.include,
726
+ kind: 'edge',
727
+ labelKey: roleKey(UML_ROLE.include),
728
+ labelFallback: 'Include',
729
+ direction: {
730
+ verbKey: `${roleKey(UML_ROLE.include)}.verb`,
731
+ verbFallback: 'includes',
732
+ gestureHintKey: `${roleKey(UML_ROLE.include)}.gesture`,
733
+ gestureHintFallback: 'Drag from the base use case to the one it always includes.',
734
+ },
735
+ },
736
+ {
737
+ id: UML_ROLE.extend,
738
+ kind: 'edge',
739
+ labelKey: roleKey(UML_ROLE.extend),
740
+ labelFallback: 'Extend',
741
+ direction: {
742
+ verbKey: `${roleKey(UML_ROLE.extend)}.verb`,
743
+ verbFallback: 'extends',
744
+ gestureHintKey: `${roleKey(UML_ROLE.extend)}.gesture`,
745
+ gestureHintFallback: 'Drag from the extending use case to the base one it may extend.',
746
+ },
747
+ },
748
+ // ── The deployment relationships (phase 2) ──────────────────────────────
749
+ // §19.2.4: a Deployment is drawn as a dashed arrow keyworded `«deploy»`, from
750
+ // the artifact TO the node it runs on. The source is the artifact because the
751
+ // artifact is the subject of the sentence: `order.jar is deployed on
752
+ // :AppServer`, never the other way round.
753
+ {
754
+ id: UML_ROLE.deploy,
755
+ kind: 'edge',
756
+ labelKey: roleKey(UML_ROLE.deploy),
757
+ labelFallback: 'Deploy',
758
+ direction: {
759
+ verbKey: `${roleKey(UML_ROLE.deploy)}.verb`,
760
+ verbFallback: 'is deployed on',
761
+ gestureHintKey: `${roleKey(UML_ROLE.deploy)}.gesture`,
762
+ gestureHintFallback: 'Drag from the artifact to the node it runs on — the arrow lands on the node.',
763
+ },
764
+ },
765
+ // §19.3.4: a Manifestation is the other dashed arrow off an artifact, and it
766
+ // says what the file IS a physical embodiment of. Same source for the same
767
+ // reason: `order.jar manifests Ordering`.
768
+ {
769
+ id: UML_ROLE.manifest,
770
+ kind: 'edge',
771
+ labelKey: roleKey(UML_ROLE.manifest),
772
+ labelFallback: 'Manifest',
773
+ direction: {
774
+ verbKey: `${roleKey(UML_ROLE.manifest)}.verb`,
775
+ verbFallback: 'manifests',
776
+ gestureHintKey: `${roleKey(UML_ROLE.manifest)}.gesture`,
777
+ gestureHintFallback: 'Drag from the artifact to the component it is a physical copy of.',
778
+ },
779
+ },
780
+ // §19.4.4: a CommunicationPath is a plain solid line between two nodes saying
781
+ // they can exchange messages. UNDIRECTED, and verbless for the reason
782
+ // `uml:association` is: the notation draws no arrow, so claiming a direction
783
+ // would put words in the diagram's mouth. Flat under nothing — see the header
784
+ // on the one place this file departs from the metamodel.
785
+ {
786
+ id: UML_ROLE['communication-path'],
787
+ kind: 'edge',
788
+ labelKey: roleKey(UML_ROLE['communication-path']),
789
+ labelFallback: 'Communication path',
790
+ },
791
+ // ── The behaviour relationships (phase 2) ───────────────────────────────
792
+ // §15.2.4: the two ActivityEdges. Both are a solid line with an open arrow,
793
+ // and both read "flows to" — what tells them apart is what is AT THE ENDS: a
794
+ // control flow sequences two actions, an object flow carries a value into or
795
+ // out of one. Two roles rather than one with a flag, because "an object flow
796
+ // must touch an object node" is a sentence that has to reach one of them and
797
+ // not the other.
798
+ {
799
+ id: UML_ROLE['control-flow'],
800
+ kind: 'edge',
801
+ labelKey: roleKey(UML_ROLE['control-flow']),
802
+ labelFallback: 'Control flow',
803
+ direction: {
804
+ verbKey: `${roleKey(UML_ROLE['control-flow'])}.verb`,
805
+ verbFallback: 'flows to',
806
+ gestureHintKey: `${roleKey(UML_ROLE['control-flow'])}.gesture`,
807
+ gestureHintFallback: 'Drag from the action that finishes to the one that starts next — the arrow lands on the next.',
808
+ },
809
+ },
810
+ {
811
+ id: UML_ROLE['object-flow'],
812
+ kind: 'edge',
813
+ labelKey: roleKey(UML_ROLE['object-flow']),
814
+ labelFallback: 'Object flow',
815
+ direction: {
816
+ verbKey: `${roleKey(UML_ROLE['object-flow'])}.verb`,
817
+ verbFallback: 'flows to',
818
+ gestureHintKey: `${roleKey(UML_ROLE['object-flow'])}.gesture`,
819
+ gestureHintFallback: 'Drag from where the value comes from to where it is consumed.',
820
+ },
821
+ },
822
+ // §14.2.4.8: a Transition — the arrow between two vertices of a state
823
+ // machine, labelled `trigger [guard] / effect`. FLAT under nothing and not a
824
+ // sibling of the two flows above: an activity edge carries a token between
825
+ // actions, a transition fires a machine from one state into another, and the
826
+ // rules that police the two have different endpoint tables.
827
+ {
828
+ id: UML_ROLE.transition,
829
+ kind: 'edge',
830
+ labelKey: roleKey(UML_ROLE.transition),
831
+ labelFallback: 'Transition',
832
+ direction: {
833
+ verbKey: `${roleKey(UML_ROLE.transition)}.verb`,
834
+ verbFallback: 'transitions to',
835
+ gestureHintKey: `${roleKey(UML_ROLE.transition)}.gesture`,
836
+ gestureHintFallback: 'Drag from the state the machine leaves to the state it enters.',
837
+ },
838
+ },
839
+ // ── The interaction relationships (phase 3) ─────────────────────────────
840
+ // §17.4.4: a Message. The PARENT is verbless, exactly as `uml:association`
841
+ // is, and for a sharper version of the same reason — the five children below
842
+ // say five different things ("calls", "sends", "replies to", "creates",
843
+ // "destroys") and a default verb on the parent would be a sixth, claimed by
844
+ // whichever of them a rule happened to reach through it.
845
+ //
846
+ // The SOURCE is the sender on all five, without exception. That is the one
847
+ // thing a reader of a sequence diagram may never have to guess: the arrow
848
+ // points at the receiver, the line's height is when it happens, and a
849
+ // message drawn the wrong way round is the opposite exchange.
850
+ {
851
+ id: UML_ROLE.message,
852
+ kind: 'edge',
853
+ labelKey: roleKey(UML_ROLE.message),
854
+ labelFallback: 'Message',
855
+ },
856
+ {
857
+ id: UML_ROLE['message-sync'],
858
+ parent: UML_ROLE.message,
859
+ kind: 'edge',
860
+ labelKey: roleKey(UML_ROLE['message-sync']),
861
+ labelFallback: 'Synchronous message',
862
+ direction: {
863
+ verbKey: `${roleKey(UML_ROLE['message-sync'])}.verb`,
864
+ verbFallback: 'calls',
865
+ gestureHintKey: `${roleKey(UML_ROLE['message-sync'])}.gesture`,
866
+ gestureHintFallback: 'Drag from the sender to the receiver — the filled arrowhead lands on the receiver.',
867
+ },
868
+ },
869
+ {
870
+ id: UML_ROLE['message-async'],
871
+ parent: UML_ROLE.message,
872
+ kind: 'edge',
873
+ labelKey: roleKey(UML_ROLE['message-async']),
874
+ labelFallback: 'Asynchronous message',
875
+ direction: {
876
+ verbKey: `${roleKey(UML_ROLE['message-async'])}.verb`,
877
+ verbFallback: 'sends',
878
+ gestureHintKey: `${roleKey(UML_ROLE['message-async'])}.gesture`,
879
+ gestureHintFallback: 'Drag from the sender to the receiver — the open arrowhead says the sender does not wait.',
880
+ },
881
+ },
882
+ // The dashed line back. Its source is still the SENDER — the participant the
883
+ // reply comes FROM, which is the one that was called — so an author draws
884
+ // every message the same way round and the notation sorts itself out.
885
+ {
886
+ id: UML_ROLE['message-reply'],
887
+ parent: UML_ROLE.message,
888
+ kind: 'edge',
889
+ labelKey: roleKey(UML_ROLE['message-reply']),
890
+ labelFallback: 'Reply message',
891
+ direction: {
892
+ verbKey: `${roleKey(UML_ROLE['message-reply'])}.verb`,
893
+ verbFallback: 'replies to',
894
+ gestureHintKey: `${roleKey(UML_ROLE['message-reply'])}.gesture`,
895
+ gestureHintFallback: 'Drag from the participant that was called back to the one that called it.',
896
+ },
897
+ },
898
+ // §17.4.4's createMessage: the dashed arrow that lands on a lifeline's HEAD,
899
+ // drawn at the height the participant comes into existence.
900
+ {
901
+ id: UML_ROLE['message-create'],
902
+ parent: UML_ROLE.message,
903
+ kind: 'edge',
904
+ labelKey: roleKey(UML_ROLE['message-create']),
905
+ labelFallback: 'Create message',
906
+ direction: {
907
+ verbKey: `${roleKey(UML_ROLE['message-create'])}.verb`,
908
+ verbFallback: 'creates',
909
+ gestureHintKey: `${roleKey(UML_ROLE['message-create'])}.gesture`,
910
+ gestureHintFallback: 'Drag from the sender to the head of the lifeline it brings into existence.',
911
+ },
912
+ },
913
+ // …and its opposite: the message whose arrival ends a life, drawn to the X.
914
+ {
915
+ id: UML_ROLE['message-delete'],
916
+ parent: UML_ROLE.message,
917
+ kind: 'edge',
918
+ labelKey: roleKey(UML_ROLE['message-delete']),
919
+ labelFallback: 'Delete message',
920
+ direction: {
921
+ verbKey: `${roleKey(UML_ROLE['message-delete'])}.verb`,
922
+ verbFallback: 'destroys',
923
+ gestureHintKey: `${roleKey(UML_ROLE['message-delete'])}.gesture`,
924
+ gestureHintFallback: 'Drag from the sender to the destruction mark that ends the receiver.',
925
+ },
926
+ },
927
+ ];
928
+ const DEFS = [
929
+ ...ELEMENT_DEFS,
930
+ ...TIER_DEFS,
931
+ ...FRAME_DEFS,
932
+ ...RELATIONSHIP_DEFS,
933
+ ];
934
+ // Null prototype: this is a lookup table keyed by ids that may one day come
935
+ // from host-supplied packs, so `defs['toString']` must not resolve.
936
+ export const UML_ROLES = Object.assign(Object.create(null), Object.fromEntries(DEFS.map(def => [def.id, def])));
937
+ /**
938
+ * The `kind` discriminant → the role it means.
939
+ *
940
+ * `kind` drives the renderer and is what the palette writes; the ROLE is the
941
+ * semantic authority. The two are posted side by side at every creation site,
942
+ * the way C4, BPMN and Wardley already do it, and this table is the single place
943
+ * that says which kind means which role. Total over {@link UmlNodeKind} by its
944
+ * type, so a new kind cannot land without being given a meaning.
945
+ *
946
+ * One kind, one role, with none collapsed — unlike C4, where four kinds are a
947
+ * second DRAWING of a level. Here the thirty-eight kinds are thirty-eight
948
+ * different artefacts of the specification, and the three roles with no kind of
949
+ * their own — `uml:classifier`, `uml:control-node`, `uml:pseudostate` — are
950
+ * ancestors rather than any of them.
951
+ */
952
+ export const UML_ROLE_OF_KIND = {
953
+ class: UML_ROLE.class,
954
+ interface: UML_ROLE.interface,
955
+ enumeration: UML_ROLE.enumeration,
956
+ object: UML_ROLE.object,
957
+ package: UML_ROLE.package,
958
+ note: UML_ROLE.note,
959
+ actor: UML_ROLE.actor,
960
+ 'use-case': UML_ROLE['use-case'],
961
+ component: UML_ROLE.component,
962
+ port: UML_ROLE.port,
963
+ 'provided-interface': UML_ROLE['provided-interface'],
964
+ 'required-interface': UML_ROLE['required-interface'],
965
+ artifact: UML_ROLE.artifact,
966
+ // The one role that is BOTH a parent and a drawing: §19.4 makes Node
967
+ // instantiable and derives Device and ExecutionEnvironment from it.
968
+ node: UML_ROLE.node,
969
+ device: UML_ROLE.device,
970
+ 'execution-environment': UML_ROLE['execution-environment'],
971
+ // ── The activity artefacts (phase 2) ────────────────────────────────────
972
+ action: UML_ROLE.action,
973
+ // Shared by `act` and `stm`: the same disc, the same meaning. See the def.
974
+ initial: UML_ROLE.initial,
975
+ 'activity-final': UML_ROLE['activity-final'],
976
+ 'flow-final': UML_ROLE['flow-final'],
977
+ decision: UML_ROLE.decision,
978
+ fork: UML_ROLE.fork,
979
+ 'object-node': UML_ROLE['object-node'],
980
+ 'send-signal': UML_ROLE['send-signal'],
981
+ 'accept-event': UML_ROLE['accept-event'],
982
+ 'time-event': UML_ROLE['time-event'],
983
+ // ── The state machine artefacts (phase 2) ───────────────────────────────
984
+ state: UML_ROLE.state,
985
+ 'final-state': UML_ROLE['final-state'],
986
+ choice: UML_ROLE.choice,
987
+ junction: UML_ROLE.junction,
988
+ 'shallow-history': UML_ROLE['shallow-history'],
989
+ 'deep-history': UML_ROLE['deep-history'],
990
+ 'entry-point': UML_ROLE['entry-point'],
991
+ 'exit-point': UML_ROLE['exit-point'],
992
+ terminate: UML_ROLE.terminate,
993
+ // ── The interaction artefacts (phase 3) ─────────────────────────────────
994
+ lifeline: UML_ROLE.lifeline,
995
+ execution: UML_ROLE.execution,
996
+ destruction: UML_ROLE.destruction,
997
+ };