@formicoidea/labre-framework-c4 0.33.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 (61) hide show
  1. package/dist/actions.d.ts +179 -0
  2. package/dist/actions.js +375 -0
  3. package/dist/background.d.ts +77 -0
  4. package/dist/background.js +223 -0
  5. package/dist/commands.d.ts +4 -0
  6. package/dist/commands.js +221 -0
  7. package/dist/component.d.ts +192 -0
  8. package/dist/component.js +188 -0
  9. package/dist/consts.d.ts +331 -0
  10. package/dist/consts.js +384 -0
  11. package/dist/descriptor.d.ts +12 -0
  12. package/dist/descriptor.js +10 -0
  13. package/dist/effects.d.ts +9 -0
  14. package/dist/effects.js +6 -0
  15. package/dist/element-renderer.d.ts +18 -0
  16. package/dist/element-renderer.js +14 -0
  17. package/dist/element-view.d.ts +51 -0
  18. package/dist/element-view.js +146 -0
  19. package/dist/export.d.ts +184 -0
  20. package/dist/export.js +454 -0
  21. package/dist/index.d.ts +16 -0
  22. package/dist/index.js +52 -0
  23. package/dist/interchange.d.ts +74 -0
  24. package/dist/interchange.js +143 -0
  25. package/dist/legend.d.ts +37 -0
  26. package/dist/legend.js +123 -0
  27. package/dist/levels.d.ts +70 -0
  28. package/dist/levels.js +46 -0
  29. package/dist/morph.d.ts +89 -0
  30. package/dist/morph.js +229 -0
  31. package/dist/node/node-renderer.d.ts +6 -0
  32. package/dist/node/node-renderer.js +304 -0
  33. package/dist/node/node-view.d.ts +45 -0
  34. package/dist/node/node-view.js +80 -0
  35. package/dist/node/type-line-watcher.d.ts +70 -0
  36. package/dist/node/type-line-watcher.js +142 -0
  37. package/dist/presets.d.ts +84 -0
  38. package/dist/presets.js +149 -0
  39. package/dist/profiles.d.ts +2 -0
  40. package/dist/profiles.js +177 -0
  41. package/dist/roles.d.ts +116 -0
  42. package/dist/roles.js +303 -0
  43. package/dist/rules.d.ts +95 -0
  44. package/dist/rules.js +1261 -0
  45. package/dist/toolbar/c4-menu.d.ts +11 -0
  46. package/dist/toolbar/c4-menu.js +14 -0
  47. package/dist/toolbar/c4-senior-button.d.ts +19 -0
  48. package/dist/toolbar/c4-senior-button.js +23 -0
  49. package/dist/toolbar/config.d.ts +150 -0
  50. package/dist/toolbar/config.js +436 -0
  51. package/dist/toolbar/icons.d.ts +90 -0
  52. package/dist/toolbar/icons.js +157 -0
  53. package/dist/toolbar/senior-tool.d.ts +1 -0
  54. package/dist/toolbar/senior-tool.js +11 -0
  55. package/dist/translations.d.ts +18 -0
  56. package/dist/translations.js +42 -0
  57. package/dist/type-line.d.ts +175 -0
  58. package/dist/type-line.js +244 -0
  59. package/dist/view.d.ts +33 -0
  60. package/dist/view.js +148 -0
  61. package/package.json +34 -0
@@ -0,0 +1,116 @@
1
+ import type { C4BoundaryVariant, C4NodeKind } from '@formicoidea/labre-core/model';
2
+ import type { RoleDefs, RoleId } from '@formicoidea/labre-core/std/gfx';
3
+ /**
4
+ * C4 role vocabulary.
5
+ *
6
+ * A role is the semantic identity of a C4 artefact, and here it is the ONLY
7
+ * thing that carries it: three of the four levels are drawn as the same rounded
8
+ * rectangle, so a rule that read the shape would find a box and learn nothing.
9
+ * The `kind` discriminant answers a different question (which glyph and which
10
+ * blue to paint); the role answers what the box MEANS.
11
+ *
12
+ * Hierarchy is DATA (`parent`), never TS inheritance. One specialisation is
13
+ * declared and it is the one C4 itself makes: a `c4:database` IS a
14
+ * `c4:container` — the notation gives it a cylinder instead of a box, but it is
15
+ * a container of the system all the same, so everything written about containers
16
+ * applies to it for free (see `roleIsA`). `mobile` and `browser` are the same
17
+ * statement without even a role of their own: they map straight onto
18
+ * `c4:container`, because a phone app and a single-page app are containers with
19
+ * a picture, and inventing a role per picture would let a rule about containers
20
+ * miss two of them.
21
+ *
22
+ * The four levels are deliberately FLAT — `c4:component` is not a child of
23
+ * `c4:container`, nor `c4:container` of `c4:system`. The relation between the
24
+ * levels is COMPOSITION ("a container is part of a system"), not
25
+ * specialisation ("a container is a kind of system"), and `roleIsA` means the
26
+ * second: filing them in a chain would make "every rule about a system also
27
+ * falls on every container" true, which is the opposite of what C4 says. What
28
+ * composition there is on the canvas is drawn — a boundary round the parts —
29
+ * and that is where a later rule must read it from.
30
+ *
31
+ * The **board** and the **boundary** are parent-less, the same call
32
+ * `wardley:map` and `bpmn:pool` both make: they are the FRAME the elements are
33
+ * drawn in and drawn round, and a rule written on the artefacts must never fall
34
+ * on the sheet holding them.
35
+ *
36
+ * The boundary is the ONE frame with children, and they are the second
37
+ * specialisation in the pack: `c4:system-boundary` and `c4:container-boundary`.
38
+ * Unlike the four levels, this really IS specialisation — a system boundary is a
39
+ * boundary, in the way a database is a container — and the split exists because
40
+ * C4's levels are ZOOMS of one element: the frame says which zoom the sheet is
41
+ * at, and what may be drawn inside it follows from that. Everything already
42
+ * written on `c4:boundary` keeps falling on both through `roleIsA`, which is what
43
+ * lets the two membership rules and the legend stay written on the parent.
44
+ *
45
+ * ## Compatibility
46
+ *
47
+ * Nothing is backfilled. A diagram drawn before today carries no role, so it is
48
+ * never evaluated and never says a word — the same promise every role in this
49
+ * library has made (PRD principle 8).
50
+ *
51
+ * And nothing is REWRITTEN either, which the boundary split is the first
52
+ * occasion to say out loud: a boundary drawn before it carries the PARENT role,
53
+ * `c4:boundary`, and that stays a role the vocabulary declares rather than a
54
+ * legacy alias to be migrated away. A rule framed against the parent reaches it;
55
+ * a rule framed against a child does not, and that asymmetry is deliberate — a
56
+ * zoom rule that needs to know WHICH level the frame is at cannot honestly guess
57
+ * it from a boundary that never said.
58
+ */
59
+ /** Every role this framework declares. */
60
+ export type C4Role = 'person' | 'system' | 'container' | 'database' | 'component' | 'title' | 'type-line' | 'description' | 'board' | 'boundary' | 'system-boundary' | 'container-boundary' | 'relationship';
61
+ export type C4RoleId = `c4:${C4Role}`;
62
+ /**
63
+ * Role ids, keyed by their own name.
64
+ *
65
+ * Keyed by the ROLE and not by the `kind`, unlike BPMN's table: C4 has nine
66
+ * kinds and five element roles, because four of the kinds are a second drawing
67
+ * of a level rather than a level of their own. {@link C4_ROLE_OF_KIND} is the
68
+ * bridge, and it is the only place the two vocabularies meet.
69
+ */
70
+ export declare const C4_ROLE: {
71
+ readonly person: "c4:person";
72
+ readonly system: "c4:system";
73
+ readonly container: "c4:container";
74
+ readonly database: "c4:database";
75
+ readonly component: "c4:component";
76
+ readonly title: "c4:title";
77
+ readonly 'type-line': "c4:type-line";
78
+ readonly description: "c4:description";
79
+ readonly board: "c4:board";
80
+ readonly boundary: "c4:boundary";
81
+ readonly 'system-boundary': "c4:system-boundary";
82
+ readonly 'container-boundary': "c4:container-boundary";
83
+ readonly relationship: "c4:relationship";
84
+ };
85
+ export declare const C4_ROLES: RoleDefs;
86
+ /**
87
+ * The `kind` discriminant → the role it means.
88
+ *
89
+ * `kind` drives the renderer and is what the palette writes; the ROLE is the
90
+ * semantic authority. The two are posted side by side at every creation site,
91
+ * the way Wardley and BPMN already do it, and this table is the single place
92
+ * that says which kind means which role. Total over {@link C4NodeKind} by its
93
+ * type, so a new kind cannot land without being given a meaning.
94
+ *
95
+ * Note what it collapses: `person-ext` means `c4:person` and `system-ext` means
96
+ * `c4:system`, because an external system is a system — the grey says it is out
97
+ * of scope, not that it is a different sort of thing. `mobile` and `browser`
98
+ * mean `c4:container` for the same reason.
99
+ */
100
+ export declare const C4_ROLE_OF_KIND: Record<C4NodeKind, RoleId>;
101
+ /**
102
+ * The boundary's `variant` → the role it means.
103
+ *
104
+ * The frame's twin of {@link C4_ROLE_OF_KIND}, and it exists for the same
105
+ * reason: the renderer and the exporter read `variant` (it picks the bracket
106
+ * line under the corner, `background.ts`), the RULES read the role, and this
107
+ * table is the single place that says the two are the same statement.
108
+ *
109
+ * Total over `C4BoundaryVariant` by its type, so a third kind of boundary cannot
110
+ * land without being given a meaning — and `createC4Boundary` writes both fields
111
+ * from it in one call, which is what keeps them from ever disagreeing. A
112
+ * boundary whose `variant` was never written carries the PARENT role and no
113
+ * bracket line: that is the pre-split document, and it is left exactly as it was
114
+ * found.
115
+ */
116
+ export declare const C4_BOUNDARY_ROLE: Record<C4BoundaryVariant, RoleId>;
package/dist/roles.js ADDED
@@ -0,0 +1,303 @@
1
+ /**
2
+ * Role ids, keyed by their own name.
3
+ *
4
+ * Keyed by the ROLE and not by the `kind`, unlike BPMN's table: C4 has nine
5
+ * kinds and five element roles, because four of the kinds are a second drawing
6
+ * of a level rather than a level of their own. {@link C4_ROLE_OF_KIND} is the
7
+ * bridge, and it is the only place the two vocabularies meet.
8
+ */
9
+ export const C4_ROLE = {
10
+ person: 'c4:person',
11
+ system: 'c4:system',
12
+ container: 'c4:container',
13
+ database: 'c4:database',
14
+ component: 'c4:component',
15
+ title: 'c4:title',
16
+ 'type-line': 'c4:type-line',
17
+ description: 'c4:description',
18
+ board: 'c4:board',
19
+ boundary: 'c4:boundary',
20
+ 'system-boundary': 'c4:system-boundary',
21
+ 'container-boundary': 'c4:container-boundary',
22
+ relationship: 'c4:relationship',
23
+ };
24
+ const _everyRoleIsMapped = true;
25
+ void _everyRoleIsMapped;
26
+ /**
27
+ * ## The i18n keys, and when they arrived
28
+ *
29
+ * The model slice shipped this vocabulary with its English wording and NO keys,
30
+ * deliberately: a framework contributes manifest entries through
31
+ * `FRAMEWORK_TRANSLATION_GROUPS`, whose `owner` must be a `FrameworkId` — which
32
+ * in turn must be a tooling flag key with a `FrameworkDescriptor` behind it
33
+ * (`frameworks.ts`: a senior button, an icon, a bundle). A key the manifest
34
+ * cannot name is a key a host meets and cannot translate, which
35
+ * `packages/affine/all/src/__tests__/translations/manifest.unit.spec.ts` fails
36
+ * the build over.
37
+ *
38
+ * The tooling slice gives C4 that identity, so the keys land here together with
39
+ * the `c4TranslationEntries` export that puts them in the manifest. Nothing in
40
+ * a stored document changed: an element persists the ROLE ID, never its label.
41
+ */
42
+ /** i18n key stem of a role id: `c4:person` → `com.labre.c4.role.person`. */
43
+ const roleKey = (id) => `com.labre.c4.role.${id.slice('c4:'.length)}`;
44
+ /**
45
+ * The four levels of the C4 model, flat — see the note at the top of this file
46
+ * on why composition is not specialisation — plus the one specialisation C4
47
+ * itself draws, the database under the container.
48
+ */
49
+ const ELEMENT_DEFS = [
50
+ {
51
+ id: C4_ROLE.person,
52
+ kind: 'node',
53
+ labelKey: roleKey(C4_ROLE.person),
54
+ labelFallback: 'Person',
55
+ },
56
+ {
57
+ id: C4_ROLE.system,
58
+ kind: 'node',
59
+ labelKey: roleKey(C4_ROLE.system),
60
+ labelFallback: 'Software system',
61
+ },
62
+ {
63
+ id: C4_ROLE.container,
64
+ kind: 'node',
65
+ labelKey: roleKey(C4_ROLE.container),
66
+ labelFallback: 'Container',
67
+ },
68
+ // The one specialisation in the pack. A data store is a container that keeps
69
+ // state — the stencil draws it as a cylinder — so a rule about containers
70
+ // reaches it, and a rule about persistence can single it out.
71
+ {
72
+ id: C4_ROLE.database,
73
+ parent: C4_ROLE.container,
74
+ kind: 'node',
75
+ labelKey: roleKey(C4_ROLE.database),
76
+ labelFallback: 'Database',
77
+ },
78
+ {
79
+ id: C4_ROLE.component,
80
+ kind: 'node',
81
+ labelKey: roleKey(C4_ROLE.component),
82
+ labelFallback: 'Component',
83
+ },
84
+ ];
85
+ /**
86
+ * The three written tiers of an element's label — its NAME, its type line and
87
+ * its description — as roles on the canvas TEXT elements that carry them.
88
+ *
89
+ * ## Why the tiers have roles at all
90
+ *
91
+ * Because a C4 component is a GROUP (PO recette, 28/08/2026): the shape and
92
+ * three texts, every one of them edited in place exactly like any other words on
93
+ * the canvas. Which leaves one question — given a group of three texts and a
94
+ * box, WHICH text is the name and which is the technology? — and the platform
95
+ * already has an answer for "what is this element, semantically". Roles survive
96
+ * what the alternatives do not: a child reordered inside its group, a group
97
+ * ungrouped and regrouped, a tier copied to another node, a tier deleted and
98
+ * redrawn. Position in `children` survives none of those.
99
+ *
100
+ * `kind: 'text'`, which is the same call `wardley:label` makes and for the same
101
+ * reason: these are free text elements whose BOX is a creation-time default and
102
+ * not a statement about anything. A rule measuring one must measure its ink.
103
+ *
104
+ * ## `c4:title` is where an element's NAME lives
105
+ *
106
+ * For one iteration the name was the shape's own native inner text and only the
107
+ * other two tiers were elements. The PO's follow-up closed that: two kinds of
108
+ * text in one component meant two editors, two toolbars and two sets of rules
109
+ * for the same three lines, and the odd one out was the one that mattered most.
110
+ * A C4 element's name is now `c4:title`, on equal terms with the tiers under it,
111
+ * and the shape carries no text at all.
112
+ *
113
+ * It is the role a rule about naming must read. Everything that asks "is this
114
+ * element named?" — for any of the nine artefacts, at any of the four levels —
115
+ * asks it of the `c4:title` grouped with the shape. The exporter still falls
116
+ * back to the shape's own inner text for an element drawn before this change,
117
+ * which is where those names really are; a rule may do the same or not, but it
118
+ * must not look for a name on the shape FIRST.
119
+ *
120
+ * ## What they are NOT
121
+ *
122
+ * Not levels of the model, not artefacts, and not a fourth thing a C4 diagram
123
+ * can contain. They are three lines of ONE element's label, which is why they
124
+ * are parent-less and why nothing in the pack maps a `kind` to them
125
+ * ({@link C4_ROLE_OF_KIND} stays the nine artefacts). The role stamped on the
126
+ * artefact stays on the SHAPE alone — the rules, the facts and the export all
127
+ * key on it, and a rule that fell on a type line would be asking a subtitle to
128
+ * have a name of its own.
129
+ *
130
+ * The GROUP itself is deliberately role-less, for the reason `legend.ts` gives
131
+ * about legend glyphs: a role makes an element count as an artefact for every
132
+ * rule written against roles, and the wrapper round a box is not a second box.
133
+ */
134
+ const TIER_DEFS = [
135
+ {
136
+ id: C4_ROLE.title,
137
+ kind: 'text',
138
+ labelKey: roleKey(C4_ROLE.title),
139
+ labelFallback: 'Name',
140
+ },
141
+ {
142
+ id: C4_ROLE['type-line'],
143
+ kind: 'text',
144
+ labelKey: roleKey(C4_ROLE['type-line']),
145
+ labelFallback: 'Type line',
146
+ },
147
+ {
148
+ id: C4_ROLE.description,
149
+ kind: 'text',
150
+ labelKey: roleKey(C4_ROLE.description),
151
+ labelFallback: 'Description',
152
+ },
153
+ ];
154
+ /**
155
+ * The frames: the sheet a diagram is drawn on, the dashed rectangle drawn round
156
+ * part of it, and the two LEVELS that rectangle can be drawn at.
157
+ *
158
+ * The board and the boundary are parent-less, and not related to each other
159
+ * either. A board is where the diagram lives; a boundary is a statement made
160
+ * INSIDE it about which elements belong to one system or one container. Neither
161
+ * is an element of the model, so a rule about people, systems, containers or
162
+ * components must fall on neither.
163
+ *
164
+ * ## Why the boundary has two children
165
+ *
166
+ * Because C4's four levels are ZOOMS of one element — "a software system is made
167
+ * up of containers, each of which contains components" — and the boundary is
168
+ * where the canvas says which zoom this part of the sheet is at. Until the
169
+ * children existed, "inside a boundary" was one undifferentiated fact, and the
170
+ * rules that read it could only ask whether an element was framed by SOMETHING;
171
+ * they could not ask the question the model actually poses, which is whether it
172
+ * is framed by the right LEVEL. A container drawn inside a container boundary is
173
+ * that boundary — drawn inside itself — and no rule could say so.
174
+ *
175
+ * They mirror the parent's `kind` (both are `node`, both are drawn as the same
176
+ * dashed rectangle) and specialise it, so everything already written on
177
+ * `c4:boundary` — `c4.homeless-component`, `c4.person-in-boundary`, the legend's
178
+ * "Boundary" row, the frame gate that decides where a profile may be chosen —
179
+ * keeps reaching them through `roleIsA` with nothing restated.
180
+ *
181
+ * A boundary is stamped with its child role and its `variant` at ONE place
182
+ * ({@link C4_BOUNDARY_ROLE}, read by `createC4Boundary`); nothing else writes
183
+ * either, and the two must never be made to disagree.
184
+ */
185
+ const FRAME_DEFS = [
186
+ {
187
+ id: C4_ROLE.board,
188
+ kind: 'node',
189
+ labelKey: roleKey(C4_ROLE.board),
190
+ labelFallback: 'C4 diagram',
191
+ },
192
+ {
193
+ id: C4_ROLE.boundary,
194
+ kind: 'node',
195
+ labelKey: roleKey(C4_ROLE.boundary),
196
+ labelFallback: 'Boundary',
197
+ },
198
+ {
199
+ id: C4_ROLE['system-boundary'],
200
+ parent: C4_ROLE.boundary,
201
+ kind: 'node',
202
+ labelKey: roleKey(C4_ROLE['system-boundary']),
203
+ labelFallback: 'System boundary',
204
+ },
205
+ {
206
+ id: C4_ROLE['container-boundary'],
207
+ parent: C4_ROLE.boundary,
208
+ kind: 'node',
209
+ labelKey: roleKey(C4_ROLE['container-boundary']),
210
+ labelFallback: 'Container boundary',
211
+ },
212
+ ];
213
+ /**
214
+ * The relationship — the only connecting object C4 has, and a typed edge under
215
+ * `docs/adr/0010`.
216
+ *
217
+ * Tier 1 of that ADR is generic — `source` is the subject of the role's verb,
218
+ * `target` its object — and it already applies: the source is the element that
219
+ * has the need and the target the one that meets it. Tier 2 is the `direction`
220
+ * block below. C4 asks every relationship to be READ as a sentence ("Customer
221
+ * uses Internet Banking System"), which is exactly what the direction reveal
222
+ * reads back, and it is why one role suffices where BPMN needs three: there is
223
+ * one kind of line on this canvas, and its LABEL is where the author says which
224
+ * kind of using it is.
225
+ */
226
+ const RELATIONSHIP_DEFS = [
227
+ {
228
+ id: C4_ROLE.relationship,
229
+ kind: 'edge',
230
+ labelKey: roleKey(C4_ROLE.relationship),
231
+ labelFallback: 'Relationship',
232
+ /**
233
+ * The verb is **"uses"** — the one C4 falls back to when an author writes
234
+ * nothing on the arrow, and the reading that decides which end is which: the
235
+ * source is the element with the need, the target the one that meets it.
236
+ *
237
+ * It is deliberately the weakest verb in the pack. "Sends a request to",
238
+ * "reads from", "authenticates against" are all relationships an author
239
+ * writes ON the line, and the role must not claim one of them: a default
240
+ * that guessed would put words in the diagram's mouth every time somebody
241
+ * dragged an arrow and moved on.
242
+ */
243
+ direction: {
244
+ verbKey: `${roleKey(C4_ROLE.relationship)}.verb`,
245
+ verbFallback: 'uses',
246
+ gestureHintKey: `${roleKey(C4_ROLE.relationship)}.gesture`,
247
+ gestureHintFallback: 'Drag from the element that has the need to the one that meets it.',
248
+ },
249
+ },
250
+ ];
251
+ const DEFS = [
252
+ ...ELEMENT_DEFS,
253
+ ...TIER_DEFS,
254
+ ...FRAME_DEFS,
255
+ ...RELATIONSHIP_DEFS,
256
+ ];
257
+ // Null prototype: this is a lookup table keyed by ids that may one day come
258
+ // from host-supplied packs, so `defs['toString']` must not resolve.
259
+ export const C4_ROLES = Object.assign(Object.create(null), Object.fromEntries(DEFS.map(def => [def.id, def])));
260
+ /**
261
+ * The `kind` discriminant → the role it means.
262
+ *
263
+ * `kind` drives the renderer and is what the palette writes; the ROLE is the
264
+ * semantic authority. The two are posted side by side at every creation site,
265
+ * the way Wardley and BPMN already do it, and this table is the single place
266
+ * that says which kind means which role. Total over {@link C4NodeKind} by its
267
+ * type, so a new kind cannot land without being given a meaning.
268
+ *
269
+ * Note what it collapses: `person-ext` means `c4:person` and `system-ext` means
270
+ * `c4:system`, because an external system is a system — the grey says it is out
271
+ * of scope, not that it is a different sort of thing. `mobile` and `browser`
272
+ * mean `c4:container` for the same reason.
273
+ */
274
+ export const C4_ROLE_OF_KIND = {
275
+ person: C4_ROLE.person,
276
+ 'person-ext': C4_ROLE.person,
277
+ system: C4_ROLE.system,
278
+ 'system-ext': C4_ROLE.system,
279
+ container: C4_ROLE.container,
280
+ database: C4_ROLE.database,
281
+ mobile: C4_ROLE.container,
282
+ browser: C4_ROLE.container,
283
+ component: C4_ROLE.component,
284
+ };
285
+ /**
286
+ * The boundary's `variant` → the role it means.
287
+ *
288
+ * The frame's twin of {@link C4_ROLE_OF_KIND}, and it exists for the same
289
+ * reason: the renderer and the exporter read `variant` (it picks the bracket
290
+ * line under the corner, `background.ts`), the RULES read the role, and this
291
+ * table is the single place that says the two are the same statement.
292
+ *
293
+ * Total over `C4BoundaryVariant` by its type, so a third kind of boundary cannot
294
+ * land without being given a meaning — and `createC4Boundary` writes both fields
295
+ * from it in one call, which is what keeps them from ever disagreeing. A
296
+ * boundary whose `variant` was never written carries the PARENT role and no
297
+ * bracket line: that is the pre-split document, and it is left exactly as it was
298
+ * found.
299
+ */
300
+ export const C4_BOUNDARY_ROLE = {
301
+ system: C4_ROLE['system-boundary'],
302
+ container: C4_ROLE['container-boundary'],
303
+ };
@@ -0,0 +1,95 @@
1
+ import type { EndpointTriplet, ValidationRule } from '@formicoidea/labre-core/blocks/surface';
2
+ /**
3
+ * Every ordered pair of the four levels — the ALPHABET, and nothing more.
4
+ *
5
+ * Not a grammar: this table sanctions all sixteen sentences, person → person
6
+ * included. It exists so {@link untypedLink} can say "between two C4 elements"
7
+ * — which is the only thing `flagNeutral` reads a matrix FOR — without also
8
+ * inheriting a judgement that belongs to {@link relationshipEndpoints}.
9
+ *
10
+ * ## Why the two tables are not one, which they were for a day
11
+ *
12
+ * `relation-endpoints` raises an off-matrix finding for every rule declaring a
13
+ * matrix, so two rules sharing one grammar report the same wrong sentence
14
+ * TWICE, with two brackets and two suggestions for one gesture to fix. BPMN
15
+ * never met this because its neutral-link rule reuses a matrix on which
16
+ * off-matrix is structurally unreachable (one triplet, one role). C4's grammar
17
+ * has exactly one removal, so it is reachable, and the alphabet has to be
18
+ * declared separately for the neutral rule to stay the single-verdict rule its
19
+ * own comment promises.
20
+ */
21
+ export declare const C4_ELEMENT_MATRIX: readonly EndpointTriplet[];
22
+ /**
23
+ * The sanctioned sentences of a relationship: **anything uses anything, except
24
+ * a person using a person.**
25
+ *
26
+ * C4 is deliberately permissive here and the matrix says so. A person uses a
27
+ * system; a container reads from a database; a component calls a system. The
28
+ * notation puts no level barrier on an arrow — a C4 diagram is drawn at one
29
+ * level, so the pairs that would be odd are odd because of the SHEET they are
30
+ * on, which is a judgement this pack cannot make (see the header on D3).
31
+ *
32
+ * The one sentence C4 does not have is **person → person**. Two people talking
33
+ * to each other is a true and important thing about an organisation, and it is
34
+ * not software: the model has no drawing for it, and a diagram that shows one
35
+ * is a diagram whose author reached for the wrong canvas. It is the only
36
+ * removal, and it is why the grammar is the alphabet minus one line rather than
37
+ * fifteen lines written out — the rule is "all of them but that one", and the
38
+ * data reads best when it says what the rule says.
39
+ *
40
+ * Exported so a test asserts THIS table rather than a copy of it.
41
+ */
42
+ export declare const C4_RELATIONSHIP_MATRIX: readonly EndpointTriplet[];
43
+ /**
44
+ * ## And the two levels that declare no rule at all
45
+ *
46
+ * C4 is named after its four C's — Context, Containers, Components, Code — and
47
+ * the picker offers all four (`levels.ts`). Two of them are levels a board can
48
+ * carry and NOTHING in this pack judges, deliberately, and it is worth saying
49
+ * why rather than leaving a reader to notice the gap.
50
+ *
51
+ * **`component`** — a component diagram shows one container's components, and
52
+ * around them it legitimately shows the containers they talk to, the systems
53
+ * behind those, and the people at the top of the chain: C4 draws the neighbours
54
+ * at every level. The frame it is defined by is a container boundary, which is
55
+ * therefore legal too, and a system boundary drawn round that is the ordinary
56
+ * nesting. That leaves no role a component diagram refuses.
57
+ *
58
+ * **`code`** — for the opposite reason. Not "nothing is forbidden" but "we know
59
+ * nothing yet": the editor draws no code-level artefact, so the pack has no
60
+ * vocabulary in which to say what such a sheet admits or refuses. The
61
+ * declaration is still the author's to make — that is a decision about the
62
+ * NOTATION's vocabulary, not about our tooling (`C4BoardLevel` says so at
63
+ * length) — and the tool records it without pretending to check it.
64
+ *
65
+ * A rule declared for either would be an empty `forbidden` list — data that can
66
+ * never fire, which this file already calls the worst thing declarative data can
67
+ * do. So there are two rules and not four, and {@link ViewAdmissibilityDef} is
68
+ * built to make that the natural outcome: a level absent from the table is a
69
+ * level the rule has nothing to say about, and the engine walks nothing for it.
70
+ */
71
+ /**
72
+ * The pack, whole: sixteen rules, all registered, all live.
73
+ *
74
+ * FIVE families, where fourteen rules needed four. C4 has one connecting object,
75
+ * four flat levels and two frames, so there is still no swimlane question, no
76
+ * graph traversal and no cardinality per frame to ask about — and the three zoom
77
+ * rules added nothing to the list, being two more `element-in-zone` rules and one
78
+ * more `element-in-background` rule, because the split that made them askable
79
+ * happened in the role VOCABULARY and not in the engine.
80
+ *
81
+ * The two LEVEL rules are the exception, and the fifth family is the honest
82
+ * reason: their question is asked of the SHEET, off a fact the sheet declares,
83
+ * and no family that starts from an artefact can express it. That is also why
84
+ * `view-admissibility` is generic and lives in the engine — C4 is its first
85
+ * consumer, not its owner.
86
+ *
87
+ * Sixteen and not fifteen because the grammar and the self-loop are two rules
88
+ * (see {@link relationshipSelfLoop}); sixteen and not nineteen because the four
89
+ * per-level naming rules collapsed into {@link unnamedElement} the moment an
90
+ * element's name became one text role instead of four shapes' inner text; and
91
+ * sixteen and not eighteen because two of the four levels a board can declare
92
+ * are judged by nothing — `component` forbids nothing and `code` is a level this
93
+ * pack cannot yet speak about (see above).
94
+ */
95
+ export declare const C4_RULES: readonly ValidationRule[];