@peter.prib/renderengine 0.2.5 → 0.2.7

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 (67) hide show
  1. package/README.md +219 -213
  2. package/REFERENCE.md +272 -6
  3. package/dist/renderengine-workspace.umd.js +11980 -7421
  4. package/dist/renderengine.umd.js +8991 -5784
  5. package/examples/archimate.html +68 -66
  6. package/examples/data-model.html +534 -0
  7. package/package.json +70 -68
  8. package/src/bian.js +182 -0
  9. package/src/charting/CheckboxTreeSelector.js +373 -304
  10. package/src/charting/DiagramEditor.js +53 -32
  11. package/src/charting/DiagramTypeRegistry.js +13 -3
  12. package/src/charting/ModelCheckDialog.js +63 -0
  13. package/src/charting/ModelChecker.js +26 -0
  14. package/src/charting/ViewTabStrip.js +94 -81
  15. package/src/charting/archimate/ArchimateCanvasEditor.js +825 -815
  16. package/src/charting/archimate/ArchimateElementTemplate.js +208 -195
  17. package/src/charting/datamodel/BianLandscapeReader.js +185 -0
  18. package/src/charting/datamodel/BianSemanticApiReader.js +183 -0
  19. package/src/charting/datamodel/DataModel.js +645 -0
  20. package/src/charting/datamodel/DataModelChecker.js +92 -0
  21. package/src/charting/datamodel/DataModelCompared.js +127 -0
  22. package/src/charting/datamodel/DataModelContext.js +108 -0
  23. package/src/charting/datamodel/DataModelFileFormat.js +33 -0
  24. package/src/charting/datamodel/DataModelPerspective.js +129 -0
  25. package/src/charting/datamodel/DataType.js +108 -0
  26. package/src/charting/datamodel/EnumerationType.js +51 -0
  27. package/src/charting/datamodel/LevelMapping.js +38 -0
  28. package/src/charting/datamodel/LevelSchema.js +41 -0
  29. package/src/charting/datamodel/ServiceDomain.js +177 -0
  30. package/src/charting/datamodel/ServiceLandscape.js +104 -0
  31. package/src/charting/datamodel/StructuredType.js +63 -0
  32. package/src/charting/datamodel/index.js +15 -0
  33. package/src/charting/diagramModel/MergeModelCommand.js +43 -37
  34. package/src/charting/diagramModel/OpenViewList.js +75 -0
  35. package/src/charting/diagramModel/ViewSetChecker.js +22 -34
  36. package/src/charting/diagrams/DataModelDiagram.js +160 -86
  37. package/src/charting/diagrams/DataModelRelationshipStrategy.js +94 -38
  38. package/src/charting/diagrams/EdgeGeometry.js +578 -553
  39. package/src/charting/diagrams/GraphDiagram.js +11 -1
  40. package/src/charting/diagrams/NodeGeometry.js +169 -158
  41. package/src/charting/diagrams/nesting.js +31 -0
  42. package/src/charting/glyphs/PersonGlyph.js +24 -23
  43. package/src/charting/modelTree/ModelTree.js +202 -164
  44. package/src/charting/modelTree/ModelTreePanel.js +36 -3
  45. package/src/charting/modelTree/TreeViewMode.js +131 -110
  46. package/src/charting/modelTree/modes/FlatTreeMode.js +32 -46
  47. package/src/charting/modelTree/modes/LayerGroupedTreeMode.js +47 -62
  48. package/src/charting/modelTree/modes/TypeGroupedTreeMode.js +46 -60
  49. package/src/datamodel.js +132 -122
  50. package/src/diagramCatalog.js +35 -30
  51. package/src/index.js +2666 -2659
  52. package/src/workspace/EditorShell.js +166 -67
  53. package/src/workspace/PanelLayout.js +56 -0
  54. package/src/workspace/index.js +1 -0
  55. package/src/workspace/layout/TabsNode.js +205 -191
  56. package/src/workspace/parts/ArchimateCanvasPart.js +10 -9
  57. package/src/workspace/parts/ChartPart.js +6 -1
  58. package/src/workspace/parts/DataModelPart.js +544 -0
  59. package/src/workspace/parts/DiagramEditorPart.js +2 -8
  60. package/src/workspace/parts/EditorPart.js +461 -407
  61. package/src/workspace/parts/ModelFile.js +216 -187
  62. package/src/workspace/parts/ModelsPart.js +483 -192
  63. package/src/workspace/storage/BackupFolder.js +145 -0
  64. package/src/workspace/storage/KeyValueStore.js +92 -0
  65. package/src/workspace/storage/ModelLibrary.js +65 -0
  66. package/src/workspace/workArea.js +417 -413
  67. package/src/yaml/YamlReader.js +372 -0
@@ -0,0 +1,372 @@
1
+ /**
2
+ * YamlReader — reads the block YAML that specifications are written in
3
+ * (OpenAPI and AsyncAPI files: BIAN's semantic APIs), dependency-free:
4
+ * mappings and sequences by indentation, a sequence item that is a mapping,
5
+ * plain, single- and double-quoted scalars (a plain one may run on over more
6
+ * indented lines), block scalars (`|` literal, `>` folded, chomping `-`/`+`,
7
+ * an indentation digit), flow collections (`[a, b]`, `{a: b}`, over several
8
+ * lines too), comments, and `---`/`...` around one document. Plain scalars are
9
+ * typed as YAML 1.2's core schema has them (null, booleans, numbers).
10
+ * Anchors, aliases, tags and multiple documents are not read: such a file is
11
+ * refused with the line it fails on.
12
+ */
13
+ export class YamlReader {
14
+ /**
15
+ * @param {string} text
16
+ * @returns {*} the document's value
17
+ */
18
+ static parse(text) {
19
+ return new YamlReader(text).document();
20
+ }
21
+
22
+ constructor(text) {
23
+ this.lines = String(text).replace(/^/, '').split(/\r?\n/);
24
+ this.i = 0;
25
+ /** The line the value being read starts on. */
26
+ this.at = null;
27
+ }
28
+
29
+ document() {
30
+ this._skipBlank();
31
+ if (this.i < this.lines.length && /^---(\s|$)/.test(this.lines[this.i])) {
32
+ const rest = this.lines[this.i].slice(3).trim();
33
+ if (rest && !rest.startsWith('#')) this.lines[this.i] = rest; else this.i++;
34
+ }
35
+ this._skipBlank();
36
+ const value = this.i < this.lines.length ? this.node(0) : null;
37
+ this._skipBlank();
38
+ if (this.i < this.lines.length && !/^\.\.\.(\s|$)/.test(this.lines[this.i])) {
39
+ this.at = this.i;
40
+ this.fail(`Unexpected content at indentation ${this._indent(this.lines[this.i])}`);
41
+ }
42
+ return value;
43
+ }
44
+
45
+ /** Refuse the file, naming the line of the value being read. */
46
+ fail(message) {
47
+ throw new SyntaxError(`YAML line ${(this.at ?? this.i) + 1}: ${message}`);
48
+ }
49
+
50
+ /** Skip blank and comment-only lines. @private */
51
+ _skipBlank() {
52
+ while (this.i < this.lines.length && /^\s*(#.*)?$/.test(this.lines[this.i])) this.i++;
53
+ }
54
+
55
+ _indent(line) {
56
+ return line.length - line.trimStart().length;
57
+ }
58
+
59
+ /** The node whose first line is the next significant one, at least `min` deep. */
60
+ node(min) {
61
+ this._skipBlank();
62
+ if (this.i >= this.lines.length) return null;
63
+ const line = this.lines[this.i];
64
+ const indent = this._indent(line);
65
+ if (indent < min) return null;
66
+ const text = line.slice(indent);
67
+ if (/^-(\s|$)/.test(text)) return this.sequence(indent);
68
+ if (this._keyOf(text)) return this.mapping(indent);
69
+ this.i++;
70
+ return this.inline(text, indent - 1);
71
+ }
72
+
73
+ /** A sequence: items `- x` at `indent`. */
74
+ sequence(indent) {
75
+ const items = [];
76
+ for (;;) {
77
+ this._skipBlank();
78
+ if (this.i >= this.lines.length) break;
79
+ const line = this.lines[this.i];
80
+ if (this._indent(line) !== indent || !/^-(\s|$)/.test(line.slice(indent))) break;
81
+ const after = line.slice(indent + 1);
82
+ const gap = after.length - after.trimStart().length;
83
+ const rest = after.trim();
84
+ if (!rest || rest.startsWith('#')) {
85
+ this.i++;
86
+ items.push(this.node(indent + 1));
87
+ continue;
88
+ }
89
+ // What follows the dash is a node starting at its own column (a mapping's first key, another item).
90
+ const column = indent + 1 + gap;
91
+ if (/^-(\s|$)/.test(rest) || this._keyOf(rest)) {
92
+ this.lines[this.i] = ' '.repeat(column) + rest;
93
+ items.push(this.node(column));
94
+ } else {
95
+ this.i++;
96
+ items.push(this.inline(rest, indent));
97
+ }
98
+ }
99
+ return items;
100
+ }
101
+
102
+ /** A mapping: `key: value` lines at `indent`. */
103
+ mapping(indent) {
104
+ const map = {};
105
+ for (;;) {
106
+ this._skipBlank();
107
+ if (this.i >= this.lines.length) break;
108
+ const line = this.lines[this.i];
109
+ if (this._indent(line) !== indent) {
110
+ if (this._indent(line) > indent) {
111
+ this.at = this.i;
112
+ this.fail('Unexpected indentation');
113
+ }
114
+ break;
115
+ }
116
+ const text = line.slice(indent);
117
+ const key = this._keyOf(text);
118
+ if (!key) break;
119
+ const rest = text.slice(key.length).trim();
120
+ this.i++;
121
+ let value;
122
+ if (!rest || rest.startsWith('#')) {
123
+ this._skipBlank();
124
+ const next = this.lines[this.i];
125
+ // A sequence may sit at its key's own indentation.
126
+ if (next !== undefined && this._indent(next) === indent && /^-(\s|$)/.test(next.slice(indent))) value = this.sequence(indent);
127
+ else value = this.node(indent + 1);
128
+ } else {
129
+ value = this.inline(rest, indent);
130
+ }
131
+ map[this._keyName(key)] = value;
132
+ }
133
+ return map;
134
+ }
135
+
136
+ /**
137
+ * A key and its colon at the start of `text` (`name:`, `'quoted':`), or null.
138
+ * @private
139
+ */
140
+ _keyOf(text) {
141
+ let m;
142
+ if (text[0] === '"') m = /^"(?:[^"\\]|\\.)*"\s*:(?=\s|$)/.exec(text);
143
+ else if (text[0] === "'") m = /^'(?:[^']|'')*'\s*:(?=\s|$)/.exec(text);
144
+ else if (/^[[{|>!&*%@`#]/.test(text) || /^-(\s|$)/.test(text)) return null;
145
+ else m = /^[^#]*?:(?=\s|$)/.exec(text);
146
+ return m ? m[0] : null;
147
+ }
148
+
149
+ /** A key's name without its colon or quotes. @private */
150
+ _keyName(key) {
151
+ const raw = key.replace(/\s*:$/, '').trim();
152
+ return raw[0] === '"' || raw[0] === "'" ? this._quoted(raw) : raw;
153
+ }
154
+
155
+ /**
156
+ * A value written after its key or dash: a block scalar, a quoted or plain
157
+ * scalar (running on over lines deeper than `parent`), or a flow collection.
158
+ */
159
+ inline(text, parent) {
160
+ this.at = this.i - 1;
161
+ if (/^[|>]/.test(text)) return this.blockScalar(text, parent);
162
+ if (text[0] === '[' || text[0] === '{') return this.flow(text, parent);
163
+ if (text[0] === '"' || text[0] === "'") return this.quotedRunningOn(text, parent);
164
+ if (/^[&*!]/.test(text)) this.fail(`Anchors, aliases and tags are not read: "${text}"`);
165
+ const parts = [this._stripComment(text)];
166
+ while (this.i < this.lines.length) {
167
+ const line = this.lines[this.i];
168
+ if (/^\s*$/.test(line)) {
169
+ // A blank line inside a plain scalar is a line break, if the scalar goes on.
170
+ let j = this.i;
171
+ while (j < this.lines.length && /^\s*$/.test(this.lines[j])) j++;
172
+ if (j < this.lines.length && this._indent(this.lines[j]) > parent && !/^\s*#/.test(this.lines[j])) {
173
+ parts.push('\n'.repeat(j - this.i));
174
+ this.i = j;
175
+ continue;
176
+ }
177
+ break;
178
+ }
179
+ if (this._indent(line) <= parent || /^\s*#/.test(line)) break;
180
+ const more = line.trim();
181
+ if (this._keyOf(more) || /^-(\s|$)/.test(more)) break;
182
+ parts.push(this._stripComment(more));
183
+ this.i++;
184
+ }
185
+ const joined = parts.reduce((all, p) => (p.startsWith('\n') ? all + p : all && !all.endsWith('\n') ? `${all} ${p}` : all + p), '');
186
+ return parts.length === 1 ? YamlReader.plain(joined) : joined;
187
+ }
188
+
189
+ _stripComment(text) {
190
+ return text.replace(/\s+#.*$/, '').trim();
191
+ }
192
+
193
+ /** A quoted scalar, its closing quote perhaps on a later line (lines folded). */
194
+ quotedRunningOn(text, parent) {
195
+ const quote = text[0];
196
+ const closed = quote === '"' ? /^"(?:[^"\\]|\\.)*"/ : /^'(?:[^']|'')*'/;
197
+ let all = text;
198
+ while (!closed.test(all)) {
199
+ if (this.i >= this.lines.length) this.fail('A quoted scalar is not closed');
200
+ const line = this.lines[this.i++];
201
+ all += /^\s*$/.test(line) ? '\n' : ` ${line.trim()}`;
202
+ }
203
+ const m = closed.exec(all);
204
+ const after = this._stripComment(all.slice(m[0].length));
205
+ if (after) this.fail(`Unexpected "${after}" after a quoted scalar`);
206
+ return this._quoted(m[0].replace(/ \n /g, '\n'));
207
+ }
208
+
209
+ /** A quoted scalar's value: double quotes with escapes, single quotes with '' for '. @private */
210
+ _quoted(raw) {
211
+ const body = raw.slice(1, -1);
212
+ if (raw[0] === "'") return body.replace(/''/g, "'");
213
+ const escapes = { n: '\n', r: '\r', t: '\t', '"': '"', '\\': '\\', '/': '/', 0: '\0', b: '\b', f: '\f', e: '\x1b', ' ': ' ', N: '\u0085', _: ' ' };
214
+ return body.replace(/\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)/g, (_, e) => {
215
+ if (/^[xuU]/.test(e) && e.length > 1) return String.fromCodePoint(parseInt(e.slice(1), 16));
216
+ return escapes[e] ?? e;
217
+ });
218
+ }
219
+
220
+ /** A block scalar (`|` or `>`, chomping, indentation digit), its lines deeper than `parent`. */
221
+ blockScalar(header, parent) {
222
+ const m = /^([|>])([-+]?)([1-9]?)([-+]?)\s*(#.*)?$/.exec(header);
223
+ if (!m) this.fail(`Not a block scalar header: "${header}"`);
224
+ const [, style, chompA, digit, chompB] = m;
225
+ const chomp = chompA || chompB;
226
+ const raw = [];
227
+ while (this.i < this.lines.length) {
228
+ const line = this.lines[this.i];
229
+ if (!/^\s*$/.test(line) && this._indent(line) <= parent) break;
230
+ raw.push(line);
231
+ this.i++;
232
+ }
233
+ // Trailing blank lines belong to the scalar only for chomping; keep them apart.
234
+ let end = raw.length;
235
+ while (end > 0 && /^\s*$/.test(raw[end - 1])) end--;
236
+ const body = raw.slice(0, end), trailing = raw.length - end;
237
+ const first = body.find(l => !/^\s*$/.test(l));
238
+ const indent = digit ? parent + Number(digit) : first !== undefined ? this._indent(first) : parent + 1;
239
+ const lines = body.map(l => (/^\s*$/.test(l) ? '' : l.slice(indent)));
240
+ let text;
241
+ if (style === '|') {
242
+ text = lines.join('\n');
243
+ } else {
244
+ text = '';
245
+ lines.forEach((line, k) => {
246
+ if (k === 0) { text = line; return; }
247
+ const prev = lines[k - 1];
248
+ const moreIndented = (s) => /^\s/.test(s);
249
+ if (line === '') text += '\n';
250
+ else if (prev === '' || moreIndented(line) || moreIndented(prev)) text += (prev === '' ? '' : '\n') + line;
251
+ else text += ` ${line}`;
252
+ });
253
+ }
254
+ if (!body.length) return chomp === '+' ? '\n'.repeat(trailing) : '';
255
+ if (chomp === '-') return text;
256
+ if (chomp === '+') return `${text}\n${'\n'.repeat(trailing)}`;
257
+ return `${text}\n`;
258
+ }
259
+
260
+ /** A flow collection, perhaps over several lines. */
261
+ flow(text, parent) {
262
+ let all = text;
263
+ const balanced = (s) => {
264
+ let depth = 0, quote = null;
265
+ for (let k = 0; k < s.length; k++) {
266
+ const c = s[k];
267
+ if (quote) {
268
+ if (c === '\\' && quote === '"') k++;
269
+ else if (c === quote) quote = null;
270
+ } else if (c === '"' || c === "'") quote = c;
271
+ else if (c === '[' || c === '{') depth++;
272
+ else if (c === ']' || c === '}') depth--;
273
+ else if (c === '#' && /\s/.test(s[k - 1] ?? ' ') && depth === 0) break;
274
+ }
275
+ return depth === 0;
276
+ };
277
+ while (!balanced(all)) {
278
+ if (this.i >= this.lines.length) this.fail('A flow collection is not closed');
279
+ all += ` ${this.lines[this.i++].trim()}`;
280
+ }
281
+ const reader = new FlowReader(all, (msg) => this.fail(msg), (raw) => this._quoted(raw));
282
+ const value = reader.value();
283
+ reader.space();
284
+ if (reader.pos < all.length && !all.slice(reader.pos).trim().startsWith('#')) this.fail(`Unexpected "${all.slice(reader.pos).trim()}" after a flow collection`);
285
+ return value;
286
+ }
287
+
288
+ /** A plain scalar's value, typed as YAML 1.2's core schema has it. */
289
+ static plain(text) {
290
+ if (/^(null|Null|NULL|~)?$/.test(text)) return null;
291
+ if (/^(true|True|TRUE)$/.test(text)) return true;
292
+ if (/^(false|False|FALSE)$/.test(text)) return false;
293
+ if (/^[-+]?[0-9]+$/.test(text)) return Number(text);
294
+ if (/^0x[0-9a-fA-F]+$/.test(text)) return parseInt(text, 16);
295
+ if (/^0o[0-7]+$/.test(text)) return parseInt(text.slice(2), 8);
296
+ if (/^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$/.test(text)) return Number(text);
297
+ if (/^[-+]?\.(inf|Inf|INF)$/.test(text)) return text.startsWith('-') ? -Infinity : Infinity;
298
+ if (/^\.(nan|NaN|NAN)$/.test(text)) return NaN;
299
+ return text;
300
+ }
301
+ }
302
+
303
+ /** Reads one flow collection (`[...]`, `{...}`) from a string. @private */
304
+ class FlowReader {
305
+ constructor(text, fail, quoted) {
306
+ Object.assign(this, { text, fail, quoted, pos: 0 });
307
+ }
308
+
309
+ space() {
310
+ while (this.pos < this.text.length && /\s/.test(this.text[this.pos])) this.pos++;
311
+ }
312
+
313
+ value() {
314
+ this.space();
315
+ const c = this.text[this.pos];
316
+ if (c === '[') return this.list();
317
+ if (c === '{') return this.map();
318
+ if (c === '"' || c === "'") {
319
+ const m = (c === '"' ? /^"(?:[^"\\]|\\.)*"/ : /^'(?:[^']|'')*'/).exec(this.text.slice(this.pos));
320
+ if (!m) this.fail('A quoted scalar is not closed');
321
+ this.pos += m[0].length;
322
+ return this.quoted(m[0]);
323
+ }
324
+ const m = /^[^,\]}]*?(?=\s*(?:[,\]}]|:\s|$))/.exec(this.text.slice(this.pos));
325
+ this.pos += m[0].length;
326
+ return YamlReader.plain(m[0].trim());
327
+ }
328
+
329
+ list() {
330
+ this.pos++;
331
+ const items = [];
332
+ for (;;) {
333
+ this.space();
334
+ if (this.text[this.pos] === ']') { this.pos++; return items; }
335
+ items.push(this.entry());
336
+ this.space();
337
+ if (this.text[this.pos] === ',') this.pos++;
338
+ else if (this.text[this.pos] !== ']') this.fail('Expected , or ] in a flow sequence');
339
+ }
340
+ }
341
+
342
+ /** A flow sequence's entry: a value, or a single `key: value` pair (a one-key mapping). */
343
+ entry() {
344
+ const value = this.value();
345
+ this.space();
346
+ if (this.text[this.pos] === ':') {
347
+ this.pos++;
348
+ return { [String(value)]: this.value() };
349
+ }
350
+ return value;
351
+ }
352
+
353
+ map() {
354
+ this.pos++;
355
+ const map = {};
356
+ for (;;) {
357
+ this.space();
358
+ if (this.text[this.pos] === '}') { this.pos++; return map; }
359
+ const key = this.value();
360
+ this.space();
361
+ let value = null;
362
+ if (this.text[this.pos] === ':') {
363
+ this.pos++;
364
+ value = this.value();
365
+ }
366
+ map[String(key)] = value;
367
+ this.space();
368
+ if (this.text[this.pos] === ',') this.pos++;
369
+ else if (this.text[this.pos] !== '}') this.fail('Expected , or } in a flow mapping');
370
+ }
371
+ }
372
+ }