@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.
- package/LICENSE +384 -0
- package/dist/actions.d.ts +430 -0
- package/dist/actions.js +724 -0
- package/dist/background.d.ts +153 -0
- package/dist/background.js +557 -0
- package/dist/board-hit.d.ts +137 -0
- package/dist/board-hit.js +191 -0
- package/dist/commands-manifest.d.ts +19 -0
- package/dist/commands-manifest.js +571 -0
- package/dist/commands.d.ts +4 -0
- package/dist/commands.js +983 -0
- package/dist/component.d.ts +521 -0
- package/dist/component.js +708 -0
- package/dist/consts.d.ts +365 -0
- package/dist/consts.js +463 -0
- package/dist/descriptor.d.ts +12 -0
- package/dist/descriptor.js +10 -0
- package/dist/drawio-decode.d.ts +32 -0
- package/dist/drawio-decode.js +96 -0
- package/dist/drawio-import.d.ts +133 -0
- package/dist/drawio-import.js +885 -0
- package/dist/edge-morph.d.ts +154 -0
- package/dist/edge-morph.js +153 -0
- package/dist/edge-styles.d.ts +78 -0
- package/dist/edge-styles.js +210 -0
- package/dist/effects.d.ts +9 -0
- package/dist/effects.js +6 -0
- package/dist/element-renderer.d.ts +64 -0
- package/dist/element-renderer.js +114 -0
- package/dist/element-view.d.ts +234 -0
- package/dist/element-view.js +430 -0
- package/dist/export.d.ts +61 -0
- package/dist/export.js +41 -0
- package/dist/filename.d.ts +17 -0
- package/dist/filename.js +26 -0
- package/dist/frame-tag.d.ts +70 -0
- package/dist/frame-tag.js +78 -0
- package/dist/grammar.d.ts +695 -0
- package/dist/grammar.js +1436 -0
- package/dist/import.d.ts +306 -0
- package/dist/import.js +1270 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +67 -0
- package/dist/interchange.d.ts +125 -0
- package/dist/interchange.js +372 -0
- package/dist/keywords.d.ts +204 -0
- package/dist/keywords.js +361 -0
- package/dist/kinds.d.ts +73 -0
- package/dist/kinds.js +91 -0
- package/dist/legend.d.ts +30 -0
- package/dist/legend.js +255 -0
- package/dist/model.d.ts +862 -0
- package/dist/model.js +1316 -0
- package/dist/morph.d.ts +178 -0
- package/dist/morph.js +496 -0
- package/dist/node/act-glyphs.d.ts +30 -0
- package/dist/node/act-glyphs.js +142 -0
- package/dist/node/compartment-watcher.d.ts +107 -0
- package/dist/node/compartment-watcher.js +163 -0
- package/dist/node/fit.d.ts +85 -0
- package/dist/node/fit.js +129 -0
- package/dist/node/node-renderer.d.ts +49 -0
- package/dist/node/node-renderer.js +611 -0
- package/dist/node/node-view.d.ts +51 -0
- package/dist/node/node-view.js +89 -0
- package/dist/node/paint.d.ts +84 -0
- package/dist/node/paint.js +143 -0
- package/dist/node/sd-glyphs.d.ts +36 -0
- package/dist/node/sd-glyphs.js +79 -0
- package/dist/node/stm-glyphs.d.ts +22 -0
- package/dist/node/stm-glyphs.js +111 -0
- package/dist/node/tier-metrics.d.ts +38 -0
- package/dist/node/tier-metrics.js +45 -0
- package/dist/operators.d.ts +45 -0
- package/dist/operators.js +75 -0
- package/dist/plantuml-import.d.ts +113 -0
- package/dist/plantuml-import.js +1723 -0
- package/dist/plantuml.d.ts +94 -0
- package/dist/plantuml.js +985 -0
- package/dist/presets.d.ts +149 -0
- package/dist/presets.js +302 -0
- package/dist/profiles.d.ts +2 -0
- package/dist/profiles.js +358 -0
- package/dist/reading.d.ts +160 -0
- package/dist/reading.js +648 -0
- package/dist/roles.d.ts +257 -0
- package/dist/roles.js +997 -0
- package/dist/rules.d.ts +267 -0
- package/dist/rules.js +3090 -0
- package/dist/templates.d.ts +2 -0
- package/dist/templates.js +197 -0
- package/dist/toolbar/config.d.ts +228 -0
- package/dist/toolbar/config.js +833 -0
- package/dist/toolbar/icons.d.ts +440 -0
- package/dist/toolbar/icons.js +711 -0
- package/dist/toolbar/senior-tool.d.ts +11 -0
- package/dist/toolbar/senior-tool.js +21 -0
- package/dist/toolbar/uml-menu.d.ts +17 -0
- package/dist/toolbar/uml-menu.js +20 -0
- package/dist/toolbar/uml-senior-button.d.ts +18 -0
- package/dist/toolbar/uml-senior-button.js +22 -0
- package/dist/translations.d.ts +27 -0
- package/dist/translations.js +117 -0
- package/dist/view.d.ts +33 -0
- package/dist/view.js +236 -0
- package/dist/xmi-import.d.ts +174 -0
- package/dist/xmi-import.js +2268 -0
- package/dist/xmi.d.ts +61 -0
- package/dist/xmi.js +1860 -0
- package/dist/xml-reader.d.ts +157 -0
- package/dist/xml-reader.js +430 -0
- package/dist/xml.d.ts +72 -0
- package/dist/xml.js +96 -0
- package/package.json +38 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The HEADLESS half of the UML pack: everything a host, an exporter, a rule or
|
|
3
|
+
* a test can read and call without mounting an editor. The lit side — the
|
|
4
|
+
* senior button, its menu, the element views and the renderers — is reached
|
|
5
|
+
* through `./view` and is never re-exported here (`docs/adr/0009` on why the
|
|
6
|
+
* two halves are separate entry points), and the shortcut rows are reached
|
|
7
|
+
* through `./commands-manifest`, whose whole point is importing none of this.
|
|
8
|
+
*
|
|
9
|
+
* Unlike the older packs this index re-exports each module WHOLE rather than
|
|
10
|
+
* naming members one by one. UML 2.5.1 is the largest notation the library
|
|
11
|
+
* carries and its headless surface is the notation itself — the roles, the
|
|
12
|
+
* geometry, the grammar of a property or an operation, the IR and the two
|
|
13
|
+
* writers — so a curated list would have been a second inventory to keep in
|
|
14
|
+
* step with the first, and the thing it usually catches (a member leaking that
|
|
15
|
+
* should have stayed private) is caught here by the module boundary instead:
|
|
16
|
+
* what a module does not export, this file cannot.
|
|
17
|
+
*/
|
|
18
|
+
export * from './background.js';
|
|
19
|
+
export * from './board-hit.js';
|
|
20
|
+
export * from './component.js';
|
|
21
|
+
export * from './consts.js';
|
|
22
|
+
export * from './edge-morph.js';
|
|
23
|
+
export * from './edge-styles.js';
|
|
24
|
+
export * from './keywords.js';
|
|
25
|
+
export * from './kinds.js';
|
|
26
|
+
export * from './legend.js';
|
|
27
|
+
export * from './morph.js';
|
|
28
|
+
export * from './operators.js';
|
|
29
|
+
export * from './presets.js';
|
|
30
|
+
export * from './reading.js';
|
|
31
|
+
export * from './roles.js';
|
|
32
|
+
export * from './profiles.js';
|
|
33
|
+
export * from './rules.js';
|
|
34
|
+
export * from './drawio-decode.js';
|
|
35
|
+
export * from './drawio-import.js';
|
|
36
|
+
export * from './export.js';
|
|
37
|
+
export * from './filename.js';
|
|
38
|
+
export * from './grammar.js';
|
|
39
|
+
export * from './interchange.js';
|
|
40
|
+
export * from './model.js';
|
|
41
|
+
export * from './plantuml.js';
|
|
42
|
+
export * from './xmi.js';
|
|
43
|
+
export * from './xmi-import.js';
|
|
44
|
+
export * from './xml-reader.js';
|
|
45
|
+
export { umlCommandIcons, umlCommands } from './commands.js';
|
|
46
|
+
export { umlCommandsManifest } from './commands-manifest.js';
|
|
47
|
+
export { umlTranslationEntries } from './translations.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The HEADLESS half of the UML pack: everything a host, an exporter, a rule or
|
|
3
|
+
* a test can read and call without mounting an editor. The lit side — the
|
|
4
|
+
* senior button, its menu, the element views and the renderers — is reached
|
|
5
|
+
* through `./view` and is never re-exported here (`docs/adr/0009` on why the
|
|
6
|
+
* two halves are separate entry points), and the shortcut rows are reached
|
|
7
|
+
* through `./commands-manifest`, whose whole point is importing none of this.
|
|
8
|
+
*
|
|
9
|
+
* Unlike the older packs this index re-exports each module WHOLE rather than
|
|
10
|
+
* naming members one by one. UML 2.5.1 is the largest notation the library
|
|
11
|
+
* carries and its headless surface is the notation itself — the roles, the
|
|
12
|
+
* geometry, the grammar of a property or an operation, the IR and the two
|
|
13
|
+
* writers — so a curated list would have been a second inventory to keep in
|
|
14
|
+
* step with the first, and the thing it usually catches (a member leaking that
|
|
15
|
+
* should have stayed private) is caught here by the module boundary instead:
|
|
16
|
+
* what a module does not export, this file cannot.
|
|
17
|
+
*/
|
|
18
|
+
/* ── What the notation IS: roles, geometry, the seeds it writes ─────────── */
|
|
19
|
+
export * from './background.js';
|
|
20
|
+
export * from './board-hit.js';
|
|
21
|
+
export * from './component.js';
|
|
22
|
+
export * from './consts.js';
|
|
23
|
+
// The two morph declarations and the line table they read. Headless data: what a
|
|
24
|
+
// classifier and a relationship may BECOME, which kinds are reachable from
|
|
25
|
+
// which, and the patch each is worth. The toolbar module that draws them is
|
|
26
|
+
// `./view.js`; these are what it is parameterized by, and what the unit and
|
|
27
|
+
// integration suites exercise without one.
|
|
28
|
+
export * from './edge-morph.js';
|
|
29
|
+
export * from './edge-styles.js';
|
|
30
|
+
export * from './keywords.js';
|
|
31
|
+
export * from './kinds.js';
|
|
32
|
+
export * from './legend.js';
|
|
33
|
+
export * from './morph.js';
|
|
34
|
+
// The combined fragment's operator table — the picker's DATA, and the words
|
|
35
|
+
// `§17.6.4`'s pentagon tag is offered under. Headless like `kinds.ts` beside it.
|
|
36
|
+
export * from './operators.js';
|
|
37
|
+
export * from './presets.js';
|
|
38
|
+
export * from './reading.js';
|
|
39
|
+
export * from './roles.js';
|
|
40
|
+
/* ── What a finished diagram is HELD TO: the rule pack and its two levels ─ */
|
|
41
|
+
export * from './profiles.js';
|
|
42
|
+
export * from './rules.js';
|
|
43
|
+
/* ── What it can be READ as: the grammar, the IR and the two writers ────── */
|
|
44
|
+
// The draw.io reader and the one impure step in front of it. Two modules and
|
|
45
|
+
// not one, and the split is ADR 0019's: `importDrawio` is pure and synchronous
|
|
46
|
+
// like every other interchange reader (`docs/adr/0012` P3), `decodeDrawio`
|
|
47
|
+
// needs `DecompressionStream` and is the command's to call.
|
|
48
|
+
export * from './drawio-decode.js';
|
|
49
|
+
export * from './drawio-import.js';
|
|
50
|
+
export * from './export.js';
|
|
51
|
+
export * from './filename.js';
|
|
52
|
+
export * from './grammar.js';
|
|
53
|
+
export * from './interchange.js';
|
|
54
|
+
export * from './model.js';
|
|
55
|
+
export * from './plantuml.js';
|
|
56
|
+
export * from './xmi.js';
|
|
57
|
+
// The XMI reader, and the tolerant tokenizer under it. Exported for the reason
|
|
58
|
+
// P3 gives: the parser is a pure function of a string, so a host that is not an
|
|
59
|
+
// editor — labre-mcp, a script, a test — calls it without a surface in sight.
|
|
60
|
+
export * from './xmi-import.js';
|
|
61
|
+
export * from './xml-reader.js';
|
|
62
|
+
/* ── The toolbox, for the host that composes the command registry and the
|
|
63
|
+
translation-key manifest out of the frameworks it installed (see
|
|
64
|
+
`packages/affine/all/src/{commands,translations}.ts`) ──────────────── */
|
|
65
|
+
export { umlCommandIcons, umlCommands } from './commands.js';
|
|
66
|
+
export { umlCommandsManifest } from './commands-manifest.js';
|
|
67
|
+
export { umlTranslationEntries } from './translations.js';
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { InterchangeCapability, InterchangeExportCapability, InterchangeFormat, InterchangeImportCapability } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import type { GfxPrimitiveElementModel } from '@formicoidea/labre-core/std/gfx';
|
|
3
|
+
import { type UmlModel } from './model.js';
|
|
4
|
+
/**
|
|
5
|
+
* UML's entries in the interchange registry (`docs/adr/0012`, P1).
|
|
6
|
+
*
|
|
7
|
+
* Three formats and five capabilities (`docs/adr/0019`): PlantUML and XMI in
|
|
8
|
+
* both directions, draw.io in only. A row per TRIPLE and never one thing with
|
|
9
|
+
* several spellings — the three make different promises to whoever opens the
|
|
10
|
+
* file, and the registry says so in the only way it can. The asymmetry that is
|
|
11
|
+
* left is deliberate and stated rather than left to be assumed: there is no
|
|
12
|
+
* `uml:drawio:export`, because writing a picture back is a re-render (P2), and
|
|
13
|
+
* a board that came in from draw.io leaves through PlantUML or XMI with its
|
|
14
|
+
* model intact.
|
|
15
|
+
*
|
|
16
|
+
* Everything here is pure. The model builder, both writers and all three
|
|
17
|
+
* readers have never had a `std` in sight, and this file adds no editor to
|
|
18
|
+
* them — the one thing a reader genuinely cannot do, inflating a compressed
|
|
19
|
+
* `.drawio` container, is the COMMAND's and is documented at
|
|
20
|
+
* `runUmlDrawioImport` and in `drawio-decode.ts`. It picks the diagrams
|
|
21
|
+
* out of the elements it is handed, reads each one, and names the file.
|
|
22
|
+
*
|
|
23
|
+
* ## The selection, and what a whole surface means
|
|
24
|
+
*
|
|
25
|
+
* Expressed by the caller through WHICH DIAGRAMS it puts in the element list,
|
|
26
|
+
* exactly as C4 expresses it with boards. A UML diagram frame is one sheet — a
|
|
27
|
+
* class diagram, a use case diagram — and the whole point of drawing four of
|
|
28
|
+
* them side by side is that they are four diagrams (ADR 0017). So every
|
|
29
|
+
* `umlDiagram` in the list becomes a document (PlantUML) or a package (XMI), and
|
|
30
|
+
* a caller that wants one leaves the others out. A headless host that hands over
|
|
31
|
+
* the whole surface gets every diagram, which is the honest whole-surface
|
|
32
|
+
* answer.
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* PlantUML source. **Semantic** — the file carries a model, not a picture, so it
|
|
36
|
+
* takes the whole preservation contract the day an importer is written.
|
|
37
|
+
*
|
|
38
|
+
* `.puml` first because it is the extension a download is given and the one the
|
|
39
|
+
* PlantUML tooling watches for; `.plantuml` rides behind it, because half the
|
|
40
|
+
* editor plugins in the wild write that instead and a picker that refused them
|
|
41
|
+
* would refuse a valid diagram over a filename.
|
|
42
|
+
*
|
|
43
|
+
* No `mime` on the FORMAT, for the reason `C4_MERMAID_FORMAT` has none: PlantUML
|
|
44
|
+
* has no registered media type, so there is none to declare. What a DOWNLOAD is
|
|
45
|
+
* served as is a different question with an answer — `text/plain`, because an
|
|
46
|
+
* invented type is a file some browsers refuse to save — and it is stated on the
|
|
47
|
+
* export result, where it belongs. The charset rides with it: unlike XML, a
|
|
48
|
+
* PlantUML file has no prolog to declare its own encoding in, and `«interface»`
|
|
49
|
+
* is a keyword that stops meaning anything as mojibake.
|
|
50
|
+
*/
|
|
51
|
+
export declare const UML_PLANTUML_EXTENSION = ".puml";
|
|
52
|
+
export declare const UML_PLANTUML_MIME = "text/plain;charset=utf-8";
|
|
53
|
+
export declare const UML_PLANTUML_FORMAT: InterchangeFormat;
|
|
54
|
+
/**
|
|
55
|
+
* XMI 2.5.1. **Semantic**, and the one a modelling tool reads.
|
|
56
|
+
*
|
|
57
|
+
* `.xmi` is what the OMG's own specification calls the file and what Papyrus,
|
|
58
|
+
* MagicDraw and StarUML offer first; `.uml` rides behind it because Eclipse's
|
|
59
|
+
* UML2 writes model files under that name and an author who exported one from
|
|
60
|
+
* there will look for it. `application/xml` rather than `text/xml`: the file is
|
|
61
|
+
* not meant to be read as text by whatever opens it.
|
|
62
|
+
*/
|
|
63
|
+
export declare const UML_XMI_EXTENSION = ".xmi";
|
|
64
|
+
export declare const UML_XMI_MIME = "application/xml";
|
|
65
|
+
export declare const UML_XMI_FORMAT: InterchangeFormat;
|
|
66
|
+
/**
|
|
67
|
+
* The diagrams in a list of elements, read into models, in document order.
|
|
68
|
+
*
|
|
69
|
+
* The frames are picked on `type` rather than with `instanceof
|
|
70
|
+
* UmlDiagramElementModel`, which is where this differs from `c4BoardFrom`. The
|
|
71
|
+
* reason is the model builder's own contract: `umlModelFrom` reads STRUCTURAL
|
|
72
|
+
* records so that it is callable with plain objects and no store
|
|
73
|
+
* (`docs/adr/0012` P3), and an `instanceof` in front of it would put back the
|
|
74
|
+
* dependency the purity was for — a capability that only works when a Yjs
|
|
75
|
+
* document minted the elements. `type` is a getter every element model
|
|
76
|
+
* implements and a field every fixture carries, so the same predicate answers
|
|
77
|
+
* for both.
|
|
78
|
+
*
|
|
79
|
+
* Document order matters for the reason it does everywhere in this framework: it
|
|
80
|
+
* is the tie-break attribution breaks on — a centre inside two overlapping
|
|
81
|
+
* frames goes to the first — and the audit's `attribute()` breaks it the same
|
|
82
|
+
* way. Sorting here would make the export disagree with the badge the user can
|
|
83
|
+
* see.
|
|
84
|
+
*/
|
|
85
|
+
export declare function umlModelsFrom(elements: readonly GfxPrimitiveElementModel[]): UmlModel[];
|
|
86
|
+
/** `uml:plantuml:export` — UML's first capability. */
|
|
87
|
+
export declare const UML_PLANTUML_EXPORT: InterchangeExportCapability;
|
|
88
|
+
/** `uml:plantuml:import` — a `.puml` from a repository, read as sheets. */
|
|
89
|
+
export declare const UML_PLANTUML_IMPORT: InterchangeImportCapability;
|
|
90
|
+
/** `uml:xmi:export` — the interchange file a modelling tool reads. */
|
|
91
|
+
export declare const UML_XMI_EXPORT: InterchangeExportCapability;
|
|
92
|
+
/** `uml:xmi:import` — the file a modelling tool wrote, read back as sheets. */
|
|
93
|
+
export declare const UML_XMI_IMPORT: InterchangeImportCapability;
|
|
94
|
+
/**
|
|
95
|
+
* draw.io (`mxfile`). **Visual** — the file carries a drawing, not a model.
|
|
96
|
+
*
|
|
97
|
+
* The one tier difference in this pack, and it is the honest one (ADR 0012 P2,
|
|
98
|
+
* `docs/adr/0019`): a `.drawio` cell says `endArrow=block`, not
|
|
99
|
+
* `uml:Generalization`, so every UML fact this reader produces is a guess made
|
|
100
|
+
* from a style string. Good guesses — draw.io's own UML stencils write a small
|
|
101
|
+
* fixed vocabulary — but guesses, and the tier is what tells a user so before
|
|
102
|
+
* the picker closes.
|
|
103
|
+
*
|
|
104
|
+
* `.drawio` first because it is the extension the application writes and the
|
|
105
|
+
* one a picker should offer; `.drawio.xml` behind it for the uncompressed save,
|
|
106
|
+
* and the generic `.xml` last, because plenty of exports land under it and what
|
|
107
|
+
* the file actually IS is decided by the reader.
|
|
108
|
+
*
|
|
109
|
+
* There is no `uml:drawio:export` to pair this with, and there is not meant to
|
|
110
|
+
* be: writing a picture back is a re-render (P2), and a board that came in from
|
|
111
|
+
* draw.io leaves through PlantUML or XMI with its model intact.
|
|
112
|
+
*/
|
|
113
|
+
export declare const UML_DRAWIO_EXTENSION = ".drawio";
|
|
114
|
+
export declare const UML_DRAWIO_MIME = "application/xml";
|
|
115
|
+
export declare const UML_DRAWIO_FORMAT: InterchangeFormat;
|
|
116
|
+
/** `uml:drawio:import` — a draw.io drawing, read as UML. */
|
|
117
|
+
export declare const UML_DRAWIO_IMPORT: InterchangeImportCapability;
|
|
118
|
+
/**
|
|
119
|
+
* Everything UML registers, in one list the view extension can hand over.
|
|
120
|
+
*
|
|
121
|
+
* Sorted by capability id, which is the order `interchangeCapabilities` answers
|
|
122
|
+
* in anyway — so a menu built from this list and a menu built from the registry
|
|
123
|
+
* cannot come out in two different orders.
|
|
124
|
+
*/
|
|
125
|
+
export declare const UML_INTERCHANGE: readonly InterchangeCapability[];
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { interchangeCapabilityId } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { importDrawio, UML_DRAWIO_FORMAT_ID } from './drawio-import.js';
|
|
3
|
+
import { exportUmlPlantuml, exportUmlXmi } from './export.js';
|
|
4
|
+
import { umlSafeFilename } from './filename.js';
|
|
5
|
+
import { umlDrawnEdges, umlElementsFromModel } from './import.js';
|
|
6
|
+
import { umlModelFrom } from './model.js';
|
|
7
|
+
import { importPlantuml } from './plantuml-import.js';
|
|
8
|
+
import { importXmi, umlElementsWithForeign } from './xmi-import.js';
|
|
9
|
+
/**
|
|
10
|
+
* UML's entries in the interchange registry (`docs/adr/0012`, P1).
|
|
11
|
+
*
|
|
12
|
+
* Three formats and five capabilities (`docs/adr/0019`): PlantUML and XMI in
|
|
13
|
+
* both directions, draw.io in only. A row per TRIPLE and never one thing with
|
|
14
|
+
* several spellings — the three make different promises to whoever opens the
|
|
15
|
+
* file, and the registry says so in the only way it can. The asymmetry that is
|
|
16
|
+
* left is deliberate and stated rather than left to be assumed: there is no
|
|
17
|
+
* `uml:drawio:export`, because writing a picture back is a re-render (P2), and
|
|
18
|
+
* a board that came in from draw.io leaves through PlantUML or XMI with its
|
|
19
|
+
* model intact.
|
|
20
|
+
*
|
|
21
|
+
* Everything here is pure. The model builder, both writers and all three
|
|
22
|
+
* readers have never had a `std` in sight, and this file adds no editor to
|
|
23
|
+
* them — the one thing a reader genuinely cannot do, inflating a compressed
|
|
24
|
+
* `.drawio` container, is the COMMAND's and is documented at
|
|
25
|
+
* `runUmlDrawioImport` and in `drawio-decode.ts`. It picks the diagrams
|
|
26
|
+
* out of the elements it is handed, reads each one, and names the file.
|
|
27
|
+
*
|
|
28
|
+
* ## The selection, and what a whole surface means
|
|
29
|
+
*
|
|
30
|
+
* Expressed by the caller through WHICH DIAGRAMS it puts in the element list,
|
|
31
|
+
* exactly as C4 expresses it with boards. A UML diagram frame is one sheet — a
|
|
32
|
+
* class diagram, a use case diagram — and the whole point of drawing four of
|
|
33
|
+
* them side by side is that they are four diagrams (ADR 0017). So every
|
|
34
|
+
* `umlDiagram` in the list becomes a document (PlantUML) or a package (XMI), and
|
|
35
|
+
* a caller that wants one leaves the others out. A headless host that hands over
|
|
36
|
+
* the whole surface gets every diagram, which is the honest whole-surface
|
|
37
|
+
* answer.
|
|
38
|
+
*/
|
|
39
|
+
/* ── The formats ──────────────────────────────────────────────────────── */
|
|
40
|
+
/**
|
|
41
|
+
* PlantUML source. **Semantic** — the file carries a model, not a picture, so it
|
|
42
|
+
* takes the whole preservation contract the day an importer is written.
|
|
43
|
+
*
|
|
44
|
+
* `.puml` first because it is the extension a download is given and the one the
|
|
45
|
+
* PlantUML tooling watches for; `.plantuml` rides behind it, because half the
|
|
46
|
+
* editor plugins in the wild write that instead and a picker that refused them
|
|
47
|
+
* would refuse a valid diagram over a filename.
|
|
48
|
+
*
|
|
49
|
+
* No `mime` on the FORMAT, for the reason `C4_MERMAID_FORMAT` has none: PlantUML
|
|
50
|
+
* has no registered media type, so there is none to declare. What a DOWNLOAD is
|
|
51
|
+
* served as is a different question with an answer — `text/plain`, because an
|
|
52
|
+
* invented type is a file some browsers refuse to save — and it is stated on the
|
|
53
|
+
* export result, where it belongs. The charset rides with it: unlike XML, a
|
|
54
|
+
* PlantUML file has no prolog to declare its own encoding in, and `«interface»`
|
|
55
|
+
* is a keyword that stops meaning anything as mojibake.
|
|
56
|
+
*/
|
|
57
|
+
export const UML_PLANTUML_EXTENSION = '.puml';
|
|
58
|
+
export const UML_PLANTUML_MIME = 'text/plain;charset=utf-8';
|
|
59
|
+
export const UML_PLANTUML_FORMAT = {
|
|
60
|
+
id: 'plantuml',
|
|
61
|
+
tier: 'semantic',
|
|
62
|
+
extensions: [UML_PLANTUML_EXTENSION, '.plantuml'],
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* XMI 2.5.1. **Semantic**, and the one a modelling tool reads.
|
|
66
|
+
*
|
|
67
|
+
* `.xmi` is what the OMG's own specification calls the file and what Papyrus,
|
|
68
|
+
* MagicDraw and StarUML offer first; `.uml` rides behind it because Eclipse's
|
|
69
|
+
* UML2 writes model files under that name and an author who exported one from
|
|
70
|
+
* there will look for it. `application/xml` rather than `text/xml`: the file is
|
|
71
|
+
* not meant to be read as text by whatever opens it.
|
|
72
|
+
*/
|
|
73
|
+
export const UML_XMI_EXTENSION = '.xmi';
|
|
74
|
+
export const UML_XMI_MIME = 'application/xml';
|
|
75
|
+
export const UML_XMI_FORMAT = {
|
|
76
|
+
id: 'xmi',
|
|
77
|
+
tier: 'semantic',
|
|
78
|
+
extensions: [UML_XMI_EXTENSION, '.uml'],
|
|
79
|
+
mime: UML_XMI_MIME,
|
|
80
|
+
};
|
|
81
|
+
/* ── Picking the diagrams ─────────────────────────────────────────────── */
|
|
82
|
+
/**
|
|
83
|
+
* The diagrams in a list of elements, read into models, in document order.
|
|
84
|
+
*
|
|
85
|
+
* The frames are picked on `type` rather than with `instanceof
|
|
86
|
+
* UmlDiagramElementModel`, which is where this differs from `c4BoardFrom`. The
|
|
87
|
+
* reason is the model builder's own contract: `umlModelFrom` reads STRUCTURAL
|
|
88
|
+
* records so that it is callable with plain objects and no store
|
|
89
|
+
* (`docs/adr/0012` P3), and an `instanceof` in front of it would put back the
|
|
90
|
+
* dependency the purity was for — a capability that only works when a Yjs
|
|
91
|
+
* document minted the elements. `type` is a getter every element model
|
|
92
|
+
* implements and a field every fixture carries, so the same predicate answers
|
|
93
|
+
* for both.
|
|
94
|
+
*
|
|
95
|
+
* Document order matters for the reason it does everywhere in this framework: it
|
|
96
|
+
* is the tie-break attribution breaks on — a centre inside two overlapping
|
|
97
|
+
* frames goes to the first — and the audit's `attribute()` breaks it the same
|
|
98
|
+
* way. Sorting here would make the export disagree with the badge the user can
|
|
99
|
+
* see.
|
|
100
|
+
*/
|
|
101
|
+
export function umlModelsFrom(elements) {
|
|
102
|
+
const source = elements;
|
|
103
|
+
return source
|
|
104
|
+
.filter(element => element.type === 'umlDiagram')
|
|
105
|
+
.map(diagram => umlModelFrom(diagram, source));
|
|
106
|
+
}
|
|
107
|
+
/* ── The capabilities ─────────────────────────────────────────────────── */
|
|
108
|
+
/**
|
|
109
|
+
* The diagrams as PlantUML source.
|
|
110
|
+
*
|
|
111
|
+
* A thin adapter and nothing else: it reads the models, runs the writer, and
|
|
112
|
+
* names the file. There is no second door — `uml.exportPlantuml` calls THIS, so
|
|
113
|
+
* the command and the registry cannot produce different bytes, different
|
|
114
|
+
* filenames or different warnings: there is nowhere for them to differ.
|
|
115
|
+
*
|
|
116
|
+
* `warnings` is omitted rather than empty when the diagram came out whole, so a
|
|
117
|
+
* caller can ask `if (result.warnings)` and mean it.
|
|
118
|
+
*/
|
|
119
|
+
const runUmlPlantumlExport = (elements, context) => {
|
|
120
|
+
const name = umlSafeFilename(context.name);
|
|
121
|
+
const { text, warnings } = exportUmlPlantuml(umlModelsFrom(elements));
|
|
122
|
+
return {
|
|
123
|
+
text,
|
|
124
|
+
filename: `${name}${UML_PLANTUML_EXTENSION}`,
|
|
125
|
+
mime: UML_PLANTUML_MIME,
|
|
126
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
/** `uml:plantuml:export` — UML's first capability. */
|
|
130
|
+
export const UML_PLANTUML_EXPORT = {
|
|
131
|
+
id: interchangeCapabilityId('uml', UML_PLANTUML_FORMAT.id, 'export'),
|
|
132
|
+
framework: 'uml',
|
|
133
|
+
format: UML_PLANTUML_FORMAT,
|
|
134
|
+
direction: 'export',
|
|
135
|
+
run: runUmlPlantumlExport,
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* How many artefacts one model DREW — what `InterchangeReport.mapped` counts.
|
|
139
|
+
*
|
|
140
|
+
* The frame is deliberately not among them: no file this pack reads has a
|
|
141
|
+
* `umlDiagram` in it, Labre mints one per model, and a count that included it
|
|
142
|
+
* would claim the reader mapped one node more than the source had. Relations
|
|
143
|
+
* are, because a relationship is something the file said and the board now
|
|
144
|
+
* draws.
|
|
145
|
+
*
|
|
146
|
+
* The lines are counted through {@link umlDrawnEdges} rather than as
|
|
147
|
+
* `relations.length`, which is the same list the materializer walks: a behaviour
|
|
148
|
+
* edge may live on the Activity or the StateMachine instead (an XMI `<edge>` is
|
|
149
|
+
* a child of its Activity), and counting one list while drawing two would report
|
|
150
|
+
* an activity sheet as having no arrows on it.
|
|
151
|
+
*/
|
|
152
|
+
function umlMappedCount(model) {
|
|
153
|
+
// Every activity and every state machine, not the first of each. `activities`
|
|
154
|
+
// and `stateMachines` are LISTS holding zero or one today (`model.ts` says
|
|
155
|
+
// why: a list makes both writers a loop instead of a branch), and a `[0]`
|
|
156
|
+
// here would be a count that quietly went wrong the day a sheet carried two —
|
|
157
|
+
// the one failure mode a report has, which is being believed.
|
|
158
|
+
const sum = (counts) => counts.reduce((total, count) => total + count, 0);
|
|
159
|
+
return (model.classifiers.length +
|
|
160
|
+
model.packages.length +
|
|
161
|
+
model.actors.length +
|
|
162
|
+
model.useCases.length +
|
|
163
|
+
model.subjects.length +
|
|
164
|
+
model.notes.length +
|
|
165
|
+
model.components.length +
|
|
166
|
+
model.ports.length +
|
|
167
|
+
model.artifacts.length +
|
|
168
|
+
model.nodes.length +
|
|
169
|
+
sum(model.activities.map(activity => activity.nodes.length + activity.partitions.length)) +
|
|
170
|
+
sum(model.stateMachines.map(machine => machine.regions.length +
|
|
171
|
+
machine.states.length +
|
|
172
|
+
machine.finalStates.length +
|
|
173
|
+
machine.pseudostates.length)) +
|
|
174
|
+
umlDrawnEdges(model).length);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* PlantUML source as diagrams — the format a `.puml` in a `docs/` folder is in.
|
|
178
|
+
*
|
|
179
|
+
* Two steps: PARSE ({@link importPlantuml}, one model per
|
|
180
|
+
* `@startuml … @enduml`) and MATERIALIZE (the same
|
|
181
|
+
* {@link umlElementsFromModel} the other two readers end on). No third step,
|
|
182
|
+
* because there is nothing foreign to put back: PlantUML is a LINE language, so
|
|
183
|
+
* what this reader cannot map is a line rather than a fragment with an id, and
|
|
184
|
+
* a line is reported rather than carried onto an element that did not produce
|
|
185
|
+
* it.
|
|
186
|
+
*
|
|
187
|
+
* `quarantined` is therefore always zero, and it means what D5 says: nothing was
|
|
188
|
+
* kept-and-deliberately-not-written-back. Everything the parser did not
|
|
189
|
+
* understand is in the notes, and the writer next door emits none of it because
|
|
190
|
+
* it never understood it in the first place.
|
|
191
|
+
*/
|
|
192
|
+
const runUmlPlantumlImport = source => {
|
|
193
|
+
const { models, notes } = importPlantuml(source);
|
|
194
|
+
const materialized = umlElementsFromModel(models, {
|
|
195
|
+
formatId: UML_PLANTUML_FORMAT.id,
|
|
196
|
+
});
|
|
197
|
+
const all = [...notes, ...materialized.notes];
|
|
198
|
+
const report = {
|
|
199
|
+
mapped: models.reduce((total, model) => total + umlMappedCount(model), 0),
|
|
200
|
+
carried: all.filter(note => note.kind === 'carried').length,
|
|
201
|
+
quarantined: 0,
|
|
202
|
+
notes: all,
|
|
203
|
+
};
|
|
204
|
+
return { elements: materialized.elements, report };
|
|
205
|
+
};
|
|
206
|
+
/** `uml:plantuml:import` — a `.puml` from a repository, read as sheets. */
|
|
207
|
+
export const UML_PLANTUML_IMPORT = {
|
|
208
|
+
id: interchangeCapabilityId('uml', UML_PLANTUML_FORMAT.id, 'import'),
|
|
209
|
+
framework: 'uml',
|
|
210
|
+
format: UML_PLANTUML_FORMAT,
|
|
211
|
+
direction: 'import',
|
|
212
|
+
run: runUmlPlantumlImport,
|
|
213
|
+
};
|
|
214
|
+
/**
|
|
215
|
+
* The diagrams as one XMI 2.5.1 document.
|
|
216
|
+
*
|
|
217
|
+
* The sanitized name is handed to the writer as well as to the download, so the
|
|
218
|
+
* `uml:Model` inside the file is called what the file is called. That is
|
|
219
|
+
* inherited behaviour rather than a choice made here — `InterchangeExportContext`
|
|
220
|
+
* documents the same consequence for BPMN — and it is the honest one: a board
|
|
221
|
+
* titled `Order/to cash` is named `Order-to cash` in both places rather than
|
|
222
|
+
* differently in each.
|
|
223
|
+
*/
|
|
224
|
+
const runUmlXmiExport = (elements, context) => {
|
|
225
|
+
const name = umlSafeFilename(context.name);
|
|
226
|
+
const { text, warnings } = exportUmlXmi(umlModelsFrom(elements), { name });
|
|
227
|
+
return {
|
|
228
|
+
text,
|
|
229
|
+
filename: `${name}${UML_XMI_EXTENSION}`,
|
|
230
|
+
mime: UML_XMI_MIME,
|
|
231
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
232
|
+
};
|
|
233
|
+
};
|
|
234
|
+
/** `uml:xmi:export` — the interchange file a modelling tool reads. */
|
|
235
|
+
export const UML_XMI_EXPORT = {
|
|
236
|
+
id: interchangeCapabilityId('uml', UML_XMI_FORMAT.id, 'export'),
|
|
237
|
+
framework: 'uml',
|
|
238
|
+
format: UML_XMI_FORMAT,
|
|
239
|
+
direction: 'export',
|
|
240
|
+
run: runUmlXmiExport,
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* An XMI 2.5.1 document as diagrams — the one direction a modelling tool
|
|
244
|
+
* actually writes.
|
|
245
|
+
*
|
|
246
|
+
* Three steps and no fourth: PARSE the file into the phase-1 IR
|
|
247
|
+
* ({@link importXmi}, pure, with its own tokenizer because `DOMParser` is a DOM
|
|
248
|
+
* global and P3 forbids one), MATERIALIZE the IR onto a board (the same
|
|
249
|
+
* {@link umlElementsFromModel} the other two importers use — one layout, one
|
|
250
|
+
* set of seeded sizes, one place to change them), and put the FOREIGN MATTER
|
|
251
|
+
* back on the elements it came off ({@link umlElementsWithForeign}, matching on
|
|
252
|
+
* the source id the materializer already wrote under `interchange.xmi.id`).
|
|
253
|
+
*
|
|
254
|
+
* The counts come from the parser rather than from the notes, because they are
|
|
255
|
+
* what the parser actually did: `mapped` is the artefacts it built, `carried`
|
|
256
|
+
* the attributes it kept on them, `quarantined` the fragments it kept and
|
|
257
|
+
* declared un-re-emittable (D5, and see {@link importXmi}'s header for why
|
|
258
|
+
* everything unmapped in this format lands in that third column rather than the
|
|
259
|
+
* second). The materializer's own notes — D4's `invented-layout`, which every
|
|
260
|
+
* XMI file earns, because the format carries no coordinates — are appended.
|
|
261
|
+
*/
|
|
262
|
+
const runUmlXmiImport = (source, context) => {
|
|
263
|
+
const { models, layout, containment, foreign, report } = importXmi(source, context);
|
|
264
|
+
const materialized = umlElementsFromModel(models, {
|
|
265
|
+
formatId: UML_XMI_FORMAT.id,
|
|
266
|
+
// The boxes AND the nesting: an XMI model states containment structurally
|
|
267
|
+
// and this canvas states it geometrically, so the invented layout is what
|
|
268
|
+
// turns `packagedElement` back into a class drawn inside its package
|
|
269
|
+
// (`import.ts`, `UmlLayoutHints.containment`).
|
|
270
|
+
layout: { boxes: layout, containment },
|
|
271
|
+
});
|
|
272
|
+
return {
|
|
273
|
+
elements: umlElementsWithForeign(materialized.elements, foreign, UML_XMI_FORMAT.id),
|
|
274
|
+
report: {
|
|
275
|
+
...report,
|
|
276
|
+
notes: [...report.notes, ...materialized.notes],
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
};
|
|
280
|
+
/** `uml:xmi:import` — the file a modelling tool wrote, read back as sheets. */
|
|
281
|
+
export const UML_XMI_IMPORT = {
|
|
282
|
+
id: interchangeCapabilityId('uml', UML_XMI_FORMAT.id, 'import'),
|
|
283
|
+
framework: 'uml',
|
|
284
|
+
format: UML_XMI_FORMAT,
|
|
285
|
+
direction: 'import',
|
|
286
|
+
run: runUmlXmiImport,
|
|
287
|
+
};
|
|
288
|
+
/* ── The visual tier ──────────────────────────────────────────────────── */
|
|
289
|
+
/**
|
|
290
|
+
* draw.io (`mxfile`). **Visual** — the file carries a drawing, not a model.
|
|
291
|
+
*
|
|
292
|
+
* The one tier difference in this pack, and it is the honest one (ADR 0012 P2,
|
|
293
|
+
* `docs/adr/0019`): a `.drawio` cell says `endArrow=block`, not
|
|
294
|
+
* `uml:Generalization`, so every UML fact this reader produces is a guess made
|
|
295
|
+
* from a style string. Good guesses — draw.io's own UML stencils write a small
|
|
296
|
+
* fixed vocabulary — but guesses, and the tier is what tells a user so before
|
|
297
|
+
* the picker closes.
|
|
298
|
+
*
|
|
299
|
+
* `.drawio` first because it is the extension the application writes and the
|
|
300
|
+
* one a picker should offer; `.drawio.xml` behind it for the uncompressed save,
|
|
301
|
+
* and the generic `.xml` last, because plenty of exports land under it and what
|
|
302
|
+
* the file actually IS is decided by the reader.
|
|
303
|
+
*
|
|
304
|
+
* There is no `uml:drawio:export` to pair this with, and there is not meant to
|
|
305
|
+
* be: writing a picture back is a re-render (P2), and a board that came in from
|
|
306
|
+
* draw.io leaves through PlantUML or XMI with its model intact.
|
|
307
|
+
*/
|
|
308
|
+
export const UML_DRAWIO_EXTENSION = '.drawio';
|
|
309
|
+
export const UML_DRAWIO_MIME = 'application/xml';
|
|
310
|
+
export const UML_DRAWIO_FORMAT = {
|
|
311
|
+
id: UML_DRAWIO_FORMAT_ID,
|
|
312
|
+
tier: 'visual',
|
|
313
|
+
extensions: [UML_DRAWIO_EXTENSION, '.drawio.xml', '.xml'],
|
|
314
|
+
mime: UML_DRAWIO_MIME,
|
|
315
|
+
};
|
|
316
|
+
/**
|
|
317
|
+
* A draw.io drawing as a board — decoded XML in, elements out.
|
|
318
|
+
*
|
|
319
|
+
* The compressed-payload split (`docs/adr/0019`) shows up here as a REFUSAL
|
|
320
|
+
* rather than as a decoder: `run` is a pure synchronous function (P3) and
|
|
321
|
+
* inflating a raw-deflate stream is neither, so a payload that still needs
|
|
322
|
+
* `DecompressionStream` comes back as an empty board with one `warning` note
|
|
323
|
+
* saying to decode first. The command does that decoding before it calls this
|
|
324
|
+
* (`importUmlDrawioFile`), and a host calling the capability directly gets a
|
|
325
|
+
* sentence it can act on instead of an empty drawing it cannot explain.
|
|
326
|
+
*/
|
|
327
|
+
const runUmlDrawioImport = (source, context) => {
|
|
328
|
+
const { model, notes, layout } = importDrawio(source, { name: context.name });
|
|
329
|
+
const materialized = umlElementsFromModel([model], {
|
|
330
|
+
formatId: UML_DRAWIO_FORMAT.id,
|
|
331
|
+
layout,
|
|
332
|
+
});
|
|
333
|
+
const all = [...notes, ...materialized.notes];
|
|
334
|
+
return {
|
|
335
|
+
elements: materialized.elements,
|
|
336
|
+
report: {
|
|
337
|
+
// What became a drawn, editable artefact — the shapes and the connectors
|
|
338
|
+
// the reader recognised, counted by the same function the two semantic
|
|
339
|
+
// readers use so three formats cannot report the same board differently.
|
|
340
|
+
mapped: umlMappedCount(model),
|
|
341
|
+
carried: all.filter(note => note.kind === 'carried').length,
|
|
342
|
+
// Nothing, ever, and it is a fact about the TIER rather than an omission.
|
|
343
|
+
// Quarantine means "kept in the document and deliberately not re-emitted"
|
|
344
|
+
// (D5), and it only means something for a format Labre writes back. There
|
|
345
|
+
// is no draw.io writer, so there is nothing a fragment could contradict.
|
|
346
|
+
quarantined: 0,
|
|
347
|
+
notes: all,
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
};
|
|
351
|
+
/** `uml:drawio:import` — a draw.io drawing, read as UML. */
|
|
352
|
+
export const UML_DRAWIO_IMPORT = {
|
|
353
|
+
id: interchangeCapabilityId('uml', UML_DRAWIO_FORMAT.id, 'import'),
|
|
354
|
+
framework: 'uml',
|
|
355
|
+
format: UML_DRAWIO_FORMAT,
|
|
356
|
+
direction: 'import',
|
|
357
|
+
run: runUmlDrawioImport,
|
|
358
|
+
};
|
|
359
|
+
/**
|
|
360
|
+
* Everything UML registers, in one list the view extension can hand over.
|
|
361
|
+
*
|
|
362
|
+
* Sorted by capability id, which is the order `interchangeCapabilities` answers
|
|
363
|
+
* in anyway — so a menu built from this list and a menu built from the registry
|
|
364
|
+
* cannot come out in two different orders.
|
|
365
|
+
*/
|
|
366
|
+
export const UML_INTERCHANGE = [
|
|
367
|
+
UML_DRAWIO_IMPORT,
|
|
368
|
+
UML_PLANTUML_EXPORT,
|
|
369
|
+
UML_PLANTUML_IMPORT,
|
|
370
|
+
UML_XMI_EXPORT,
|
|
371
|
+
UML_XMI_IMPORT,
|
|
372
|
+
];
|