@r0kshan/cairn 1.0.0-RC13 → 1.0.0-RC15
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/README.md +87 -21
- package/THIRD-PARTY-NOTICES.md +311 -7
- package/bin/cairn.mjs +1855 -677
- package/dist/cairn.mjs +1539 -611
- package/licenses/Apache-2.0.txt +73 -0
- package/licenses/BSD-3-Clause.txt +11 -0
- package/licenses/CC-BY-4.0.txt +396 -0
- package/licenses/LGPL-2.1.txt +501 -0
- package/licenses/MIT-javascript-logo.js.txt +21 -0
- package/licenses/README.md +50 -0
- package/licenses/bun-LICENSE.md +84 -0
- package/licenses/simple-icons-CC0-1.0.md +30 -0
- package/package.json +3 -3
package/bin/cairn.mjs
CHANGED
|
@@ -1,4 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/*!
|
|
3
|
+
* cairn — Apache-2.0 — https://github.com/R0kshan/cairn
|
|
4
|
+
* Copyright 2026 Røkshan.
|
|
5
|
+
*
|
|
6
|
+
* This artifact contains third-party code and artwork:
|
|
7
|
+
*
|
|
8
|
+
* elkjs 0.12.0 — (c) 2017 Kiel University and others.
|
|
9
|
+
* Dual-licensed EPL-2.0 OR GPL-3.0-or-later; cairn elects EPL-2.0.
|
|
10
|
+
* Full text: licenses/elkjs-EPL-2.0.md — https://www.eclipse.org/legal/epl-2.0
|
|
11
|
+
*
|
|
12
|
+
* Simple Icons 16.29.0 artwork — CC0-1.0 project-wide.
|
|
13
|
+
* Six of the vendored icons carry their own terms instead — MIT,
|
|
14
|
+
* BSD-3-Clause, CC-BY-4.0 or Apache-2.0 — and are attributed individually.
|
|
15
|
+
* Full texts: licenses/
|
|
16
|
+
*
|
|
17
|
+
* Per-icon attribution and the full licence texts ship with this artifact:
|
|
18
|
+
* the package root (npm), the app directory (Scoop), or
|
|
19
|
+
* share/doc/cairn (Homebrew, install.sh). Also at
|
|
20
|
+
* https://github.com/R0kshan/cairn/blob/main/THIRD-PARTY-NOTICES.md
|
|
21
|
+
*/
|
|
2
22
|
var __create = Object.create;
|
|
3
23
|
var __defProp = Object.defineProperty;
|
|
4
24
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -251,13 +271,13 @@ var require_elk_bundled = __commonJS({
|
|
|
251
271
|
this.id = id + 1;
|
|
252
272
|
msg.id = id;
|
|
253
273
|
var self2 = this;
|
|
254
|
-
return new Promise(function(
|
|
274
|
+
return new Promise(function(resolve2, reject) {
|
|
255
275
|
self2.resolvers[id] = function(err, res) {
|
|
256
276
|
if (err) {
|
|
257
277
|
self2.convertGwtStyleError(err);
|
|
258
278
|
reject(err);
|
|
259
279
|
} else {
|
|
260
|
-
|
|
280
|
+
resolve2(res);
|
|
261
281
|
}
|
|
262
282
|
};
|
|
263
283
|
self2.worker.postMessage(msg);
|
|
@@ -92320,11 +92340,12 @@ var init_lexer = __esm({
|
|
|
92320
92340
|
});
|
|
92321
92341
|
|
|
92322
92342
|
// src/models/ast.ts
|
|
92323
|
-
var ATTACH_SIDES, defaultDiagramStyle, explanations;
|
|
92343
|
+
var ATTACH_SIDES, ATTACH_ROLES, defaultDiagramStyle, explanations;
|
|
92324
92344
|
var init_ast = __esm({
|
|
92325
92345
|
"src/models/ast.ts"() {
|
|
92326
92346
|
"use strict";
|
|
92327
92347
|
ATTACH_SIDES = ["left", "right", "top", "bottom"];
|
|
92348
|
+
ATTACH_ROLES = ["producer", "consumer"];
|
|
92328
92349
|
defaultDiagramStyle = () => ({
|
|
92329
92350
|
crossingHops: true,
|
|
92330
92351
|
compact: false,
|
|
@@ -92343,7 +92364,11 @@ var init_ast = __esm({
|
|
|
92343
92364
|
});
|
|
92344
92365
|
explanations = {
|
|
92345
92366
|
E0101: "Syntax error: the file does not follow the DSL grammar.",
|
|
92367
|
+
E0105: '`logo:` takes either a built-in name (`logo: react`) or a quoted path to a file in the workspace (`logo: "./logos/acme.svg"`). A URL is refused on purpose: a diagram that fetches its logos stops being self-contained, leaks the reader\'s IP to whoever hosts them, and rots silently when the address dies \u2014 so cairn inlines a file instead of linking one.',
|
|
92346
92368
|
E0106: "`order:` takes a whole number \u2265 0. On a top-level element it sets the reading order the disposition defines \u2014 left to right for `wide`/`slide`, top to bottom for `tall`/`page` \u2014 by banding the element inside its own layout partition, so it never crosses into another partition. Inside a container the axis flips: a child's layer belongs to the flows and elk honors no constraint that would change it, so there `order:` sorts the siblings that share a layer. Values need not be contiguous.",
|
|
92369
|
+
E0107: 'Unknown built-in logo. The built-in set is deliberately small \u2014 it covers common stack technologies, not every brand. Anything outside it is still reachable as a workspace file: `logo: "./logos/name.svg"`.',
|
|
92370
|
+
E0108: "This element kind does not carry a logo. A logo marks what a piece of software is built with, so the kinds that accept one are the kinds that run code; actors are people and containers are groupings, so neither takes one.",
|
|
92371
|
+
W0580: "A `logo:` file could not be inlined \u2014 it is missing, too large, or not one of the supported types. Only a warning: the diagram is still valid and renders without the mark, because a decoration that failed to load is no reason to fail a build.",
|
|
92347
92372
|
E0201: "Unknown element kind for the active view. Each view defines its own kinds (e.g. logical: actor-group, actor, system, layer, block, external).",
|
|
92348
92373
|
E0202: "Duplicate identifier. IDs are flat and unique per file (decision D1): every element needs a distinct ID so flows can reference it unambiguously.",
|
|
92349
92374
|
E0203: "The logical view forbids unlabelled arrows: every flow must name the data, command or event it carries. A logical diagram tells what circulates, not just who talks to whom.",
|
|
@@ -92360,42 +92385,53 @@ var init_ast = __esm({
|
|
|
92360
92385
|
E0215: "A deployed application (app-instance) must sit on a server or in a zone. It shows WHERE an application runs, without its internal detail (C4 deployment convention).",
|
|
92361
92386
|
E0216: "Network zones belong to a site (or nest inside a larger zone): DMZ and LAN only mean something relative to a perimeter.",
|
|
92362
92387
|
W0540: 'C4 container-diagram practice: inter-process relationships should be labelled with their technology/protocol ("the how, not just the what"). Human/actor interactions are exempt. Add `(API_REST, JSON)` after the label, or ignore if the diagram is intentionally functional-only.',
|
|
92363
|
-
E0223: "Unknown
|
|
92388
|
+
E0223: "Unknown endpoint suffix. A flow endpoint may name the side it attaches to \u2014 `APP.right -> DB.left` \u2014 using the diagram as it is read: `left`, `right`, `top`, `bottom`. It may instead name its role towards a queue at the other end: `producer` or `consumer`. A declared side is a hint, not a guarantee: one the layout cannot reach is dropped with a W0570 warning rather than forced.",
|
|
92389
|
+
E0224: "An endpoint role says what this element does with the *queue* at the other end of the flow (`CAPTURE.producer -> EVENTS`), so the other end has to be one. Point the flow at a queue, or drop the role \u2014 between two ordinary elements the arrow already says everything a role would.",
|
|
92390
|
+
E0225: "A flow's queue endpoint carries both a declared side and a role at the other end, and they are two answers to one question: `producer` already means the queue's left cap and `consumer` its right. Keep the role and drop the side, or keep the side and drop the role.",
|
|
92364
92391
|
E0240: "The infrastructure view requires every flow to carry its protocol (and port if relevant): the flow matrix is the primary output of this view. Add `(HTTPS/443)` after the label.",
|
|
92365
92392
|
E0221: 'Unknown business-object reference: a flow carries `[AN_ID]` that no `business-object` declaration defines. Declare it once (`business-object ID "Name" "description"`) so the registry and the chips stay consistent.',
|
|
92366
|
-
E0222: "Business objects are a logical-view concept (what data circulates between functional blocks). They are not part of the application
|
|
92393
|
+
E0222: "Business objects are a logical-view concept (what data circulates between functional blocks). They are not part of the application or infrastructure views \u2014 model the exchange with the flow label and technical tail instead. Remove the `business-object` declaration and its `[refs]`, or switch the diagram to `logical`.",
|
|
92367
92394
|
W0530: "Completeness check: a declared business object is never carried by any flow \u2014 either connect it to the flows that transport it, or remove it from this view.",
|
|
92368
|
-
E0217: "A sensitive asset must sit inside a trust zone: its protection level is defined by the zone that contains it.",
|
|
92369
|
-
E0218: "A security node (firewall, WAF, bastion, reverse proxy) lives inside a trust zone \u2014 typically the exposed zone whose traffic it filters.",
|
|
92370
|
-
E0250: 'The security view requires each trust zone to declare a sensitivity level in parentheses after the label: `trust-zone DMZ "DMZ" (public)`. Levels, least \u2192 most trusted: public, internal, restricted, secret. The level drives the zone color and the trust-boundary crossing checks.',
|
|
92371
92395
|
W0570: "A declared attachment side (`APP.right -> DB.left`) did not survive layout: the flow ended up on another side of the element. Pins are honored where the layout can reach them and dropped where it cannot, rather than forced into an unreadable route. Move the element instead (`order:`), pin the other endpoint, or drop the pin.",
|
|
92372
|
-
W0571: "An endpoint reads `ID.side`, but `ID.side` is itself a declared element, and a declared id always wins \u2014 so the flow attaches to that element and no side is pinned. Rename the element if you meant the side."
|
|
92373
|
-
W0560: "Security check: this flow enters a more-trusted zone from a less-trusted one without passing through a security-node (firewall/WAF/bastion). Route it through a filtering point, or confirm the direct path is deliberate. This is the diagram equivalent of a missing firewall rule review.",
|
|
92374
|
-
W0561: "Security check: an inter-zone flow does not state its encryption/protocol. Cross-zone traffic should declare how it is protected: add `(TLS1.3)` or the relevant protocol after the label."
|
|
92396
|
+
W0571: "An endpoint reads `ID.side`, but `ID.side` is itself a declared element, and a declared id always wins \u2014 so the flow attaches to that element and no side is pinned. Rename the element if you meant the side."
|
|
92375
92397
|
};
|
|
92376
92398
|
}
|
|
92377
92399
|
});
|
|
92378
92400
|
|
|
92379
92401
|
// src/themes.ts
|
|
92402
|
+
function isDarkTheme(name) {
|
|
92403
|
+
return name === "classic-dark" || THEME_SPECS[name]?.dark === true;
|
|
92404
|
+
}
|
|
92405
|
+
function registerTheme(name, spec) {
|
|
92406
|
+
if (RESERVED_THEME_NAMES.includes(name)) {
|
|
92407
|
+
throw new Error(`\`${name}\` is a reserved theme name \u2014 rename the theme file`);
|
|
92408
|
+
}
|
|
92409
|
+
THEME_SPECS[name] = spec;
|
|
92410
|
+
themes[name] = buildTheme(spec);
|
|
92411
|
+
if (!themeNames.includes(name)) themeNames.push(name);
|
|
92412
|
+
}
|
|
92380
92413
|
function themeFor(name, view) {
|
|
92381
|
-
if (name === "classic")
|
|
92382
|
-
|
|
92383
|
-
|
|
92384
|
-
|
|
92385
|
-
|
|
92386
|
-
|
|
92387
|
-
|
|
92388
|
-
|
|
92389
|
-
|
|
92390
|
-
|
|
92391
|
-
|
|
92392
|
-
|
|
92393
|
-
|
|
92414
|
+
if (name === "classic") return { palette: lightPalette, kinds: view.defaults };
|
|
92415
|
+
if (name === "classic-dark") return { palette: darkPalette, kinds: view.defaultsDark };
|
|
92416
|
+
return applyTheme(themes[name] ?? themes.light, view);
|
|
92417
|
+
}
|
|
92418
|
+
function themeFromSpec(spec, view) {
|
|
92419
|
+
return applyTheme(buildTheme(spec), view);
|
|
92420
|
+
}
|
|
92421
|
+
function paletteFor(name) {
|
|
92422
|
+
if (name === "classic") return lightPalette;
|
|
92423
|
+
if (name === "classic-dark") return darkPalette;
|
|
92424
|
+
return themes[name]?.palette ?? lightPalette;
|
|
92425
|
+
}
|
|
92426
|
+
function paletteFromSpec(spec) {
|
|
92427
|
+
return buildTheme(spec).palette;
|
|
92428
|
+
}
|
|
92429
|
+
function applyTheme(theme, view) {
|
|
92394
92430
|
const kinds = {};
|
|
92395
|
-
for (const kind of view.kinds) kinds[kind] = theme.roles[roleForKind(kind
|
|
92396
|
-
return { palette: theme.palette, kinds
|
|
92431
|
+
for (const kind of view.kinds) kinds[kind] = theme.roles[roleForKind(kind)] ?? {};
|
|
92432
|
+
return { palette: theme.palette, kinds };
|
|
92397
92433
|
}
|
|
92398
|
-
var lightPalette, darkPalette,
|
|
92434
|
+
var lightPalette, darkPalette, flowPalette, containerStyle, leafStyle, KIND_ROLE_MAP, roleForKind, buildTheme, THEME_SPECS, themes, themeNames, RESERVED_THEME_NAMES;
|
|
92399
92435
|
var init_themes = __esm({
|
|
92400
92436
|
"src/themes.ts"() {
|
|
92401
92437
|
"use strict";
|
|
@@ -92447,10 +92483,6 @@ var init_themes = __esm({
|
|
|
92447
92483
|
chipStroke: "#b08d2a",
|
|
92448
92484
|
chipText: "#e0c068"
|
|
92449
92485
|
};
|
|
92450
|
-
palettes = {
|
|
92451
|
-
light: lightPalette,
|
|
92452
|
-
dark: darkPalette
|
|
92453
|
-
};
|
|
92454
92486
|
flowPalette = {
|
|
92455
92487
|
light: [
|
|
92456
92488
|
"#1f77b4",
|
|
@@ -92497,16 +92529,15 @@ var init_themes = __esm({
|
|
|
92497
92529
|
datastore: "datastore",
|
|
92498
92530
|
queue: "datastore",
|
|
92499
92531
|
gateway: "authGateway",
|
|
92532
|
+
firewall: "firewall",
|
|
92500
92533
|
auth: "auth",
|
|
92501
92534
|
idp: "identityProvider",
|
|
92502
92535
|
site: "site",
|
|
92503
92536
|
"network-zone": "networkZone",
|
|
92504
92537
|
server: "server",
|
|
92505
|
-
"app-instance": "appInstance"
|
|
92506
|
-
"security-node": "securityNode",
|
|
92507
|
-
asset: "leaf"
|
|
92538
|
+
"app-instance": "appInstance"
|
|
92508
92539
|
};
|
|
92509
|
-
roleForKind = (kind
|
|
92540
|
+
roleForKind = (kind) => KIND_ROLE_MAP[kind] ?? "leaf";
|
|
92510
92541
|
buildTheme = (spec) => {
|
|
92511
92542
|
const paletteSpec = spec.pal, accentColors = spec.accentColors;
|
|
92512
92543
|
const palette = {
|
|
@@ -92542,28 +92573,25 @@ var init_themes = __esm({
|
|
|
92542
92573
|
application: containerStyle(accentColors.appF, accentColors.app),
|
|
92543
92574
|
layer: containerStyle(accentColors.goldF, accentColors.gold, false, 1),
|
|
92544
92575
|
external: containerStyle(accentColors.violetF, accentColors.violet, true),
|
|
92545
|
-
untrusted: containerStyle(accentColors.redF, accentColors.red, true, 1.3),
|
|
92546
92576
|
leaf: leafStyle(accentColors.leafF, accentColors.leafS),
|
|
92547
92577
|
datastore: leafStyle(accentColors.purpleF, accentColors.purple),
|
|
92548
92578
|
site: containerStyle(accentColors.siteF, accentColors.siteS, false, 1.4),
|
|
92549
92579
|
networkZone: containerStyle(accentColors.greenF, accentColors.green, true),
|
|
92550
92580
|
server: containerStyle(accentColors.serverF, accentColors.serverS, false, 1.5),
|
|
92551
92581
|
appInstance: leafStyle(accentColors.aiF, accentColors.aiS, 1.2),
|
|
92552
|
-
|
|
92582
|
+
// Accent keys predate the roles that read them: `auth`/`authF` is the
|
|
92583
|
+
// gateway's orange, `authn`/`authnF` the auth middleware's blue. The two
|
|
92584
|
+
// used to share the `node` orange, which is what made a gateway and an
|
|
92585
|
+
// auth middleware indistinguishable in a dense infrastructure view.
|
|
92553
92586
|
authGateway: leafStyle(accentColors.authF, accentColors.auth, 1.4),
|
|
92554
|
-
|
|
92587
|
+
firewall: leafStyle(accentColors.fwF, accentColors.fw, 1.6),
|
|
92588
|
+
auth: leafStyle(accentColors.authnF, accentColors.authn, 1.5),
|
|
92555
92589
|
identityProvider: leafStyle(accentColors.idpF, accentColors.idp)
|
|
92556
|
-
},
|
|
92557
|
-
levels: {
|
|
92558
|
-
public: containerStyle(spec.lv.public[0], spec.lv.public[1], false, 1.4),
|
|
92559
|
-
internal: containerStyle(spec.lv.internal[0], spec.lv.internal[1], false, 1.4),
|
|
92560
|
-
restricted: containerStyle(spec.lv.restricted[0], spec.lv.restricted[1], false, 1.4),
|
|
92561
|
-
secret: containerStyle(spec.lv.secret[0], spec.lv.secret[1], false, 1.4)
|
|
92562
92590
|
}
|
|
92563
92591
|
};
|
|
92564
92592
|
};
|
|
92565
|
-
|
|
92566
|
-
light:
|
|
92593
|
+
THEME_SPECS = {
|
|
92594
|
+
light: {
|
|
92567
92595
|
pal: {
|
|
92568
92596
|
bg: "#ffffff",
|
|
92569
92597
|
text: "#17202c",
|
|
@@ -92611,16 +92639,15 @@ var init_themes = __esm({
|
|
|
92611
92639
|
auth: "#b85a30",
|
|
92612
92640
|
authF: "#f5e6dd",
|
|
92613
92641
|
idp: "#3a8f8f",
|
|
92614
|
-
idpF: "#e0f0f0"
|
|
92615
|
-
|
|
92616
|
-
|
|
92617
|
-
|
|
92618
|
-
|
|
92619
|
-
restricted: ["#e9f2fb", "#2f7cc4"],
|
|
92620
|
-
secret: ["#efe9f7", "#7a55a8"]
|
|
92642
|
+
idpF: "#e0f0f0",
|
|
92643
|
+
fw: "#c0392b",
|
|
92644
|
+
fwF: "#fdecea",
|
|
92645
|
+
authn: "#2f6fb5",
|
|
92646
|
+
authnF: "#e8f1fb"
|
|
92621
92647
|
}
|
|
92622
|
-
}
|
|
92623
|
-
dark:
|
|
92648
|
+
},
|
|
92649
|
+
dark: {
|
|
92650
|
+
dark: true,
|
|
92624
92651
|
pal: {
|
|
92625
92652
|
bg: "#1e2530",
|
|
92626
92653
|
text: "#e6edf3",
|
|
@@ -92668,16 +92695,14 @@ var init_themes = __esm({
|
|
|
92668
92695
|
auth: "#c96a4a",
|
|
92669
92696
|
authF: "#332218",
|
|
92670
92697
|
idp: "#4fafaf",
|
|
92671
|
-
idpF: "#1a2e2e"
|
|
92672
|
-
|
|
92673
|
-
|
|
92674
|
-
|
|
92675
|
-
|
|
92676
|
-
restricted: ["#1f2a37", "#4a86b8"],
|
|
92677
|
-
secret: ["#291f33", "#8a6cb0"]
|
|
92698
|
+
idpF: "#1a2e2e",
|
|
92699
|
+
fw: "#e5675a",
|
|
92700
|
+
fwF: "#33211f",
|
|
92701
|
+
authn: "#6fa8e0",
|
|
92702
|
+
authnF: "#1d2735"
|
|
92678
92703
|
}
|
|
92679
|
-
}
|
|
92680
|
-
slate:
|
|
92704
|
+
},
|
|
92705
|
+
slate: {
|
|
92681
92706
|
pal: {
|
|
92682
92707
|
bg: "#f7f9fb",
|
|
92683
92708
|
text: "#26303c",
|
|
@@ -92725,16 +92750,14 @@ var init_themes = __esm({
|
|
|
92725
92750
|
auth: "#a85a30",
|
|
92726
92751
|
authF: "#f0e4dd",
|
|
92727
92752
|
idp: "#3a8f8f",
|
|
92728
|
-
idpF: "#e4f0f0"
|
|
92729
|
-
|
|
92730
|
-
|
|
92731
|
-
|
|
92732
|
-
|
|
92733
|
-
restricted: ["#e8eff6", "#3b6ea5"],
|
|
92734
|
-
secret: ["#efecf5", "#7d6ba8"]
|
|
92753
|
+
idpF: "#e4f0f0",
|
|
92754
|
+
fw: "#bd4b3c",
|
|
92755
|
+
fwF: "#f9e9e6",
|
|
92756
|
+
authn: "#3a6ea5",
|
|
92757
|
+
authnF: "#e9f0f8"
|
|
92735
92758
|
}
|
|
92736
|
-
}
|
|
92737
|
-
sand:
|
|
92759
|
+
},
|
|
92760
|
+
sand: {
|
|
92738
92761
|
pal: {
|
|
92739
92762
|
bg: "#faf6ee",
|
|
92740
92763
|
text: "#3a2f22",
|
|
@@ -92782,16 +92805,14 @@ var init_themes = __esm({
|
|
|
92782
92805
|
auth: "#a85a30",
|
|
92783
92806
|
authF: "#f0e5dd",
|
|
92784
92807
|
idp: "#3a7a6f",
|
|
92785
|
-
idpF: "#e5f0ed"
|
|
92786
|
-
|
|
92787
|
-
|
|
92788
|
-
|
|
92789
|
-
|
|
92790
|
-
restricted: ["#e6f0f1", "#3f7a8c"],
|
|
92791
|
-
secret: ["#f0e8ef", "#8a5f7a"]
|
|
92808
|
+
idpF: "#e5f0ed",
|
|
92809
|
+
fw: "#b8492f",
|
|
92810
|
+
fwF: "#f8e8e0",
|
|
92811
|
+
authn: "#3a6b9a",
|
|
92812
|
+
authnF: "#e9eff7"
|
|
92792
92813
|
}
|
|
92793
|
-
}
|
|
92794
|
-
contrast:
|
|
92814
|
+
},
|
|
92815
|
+
contrast: {
|
|
92795
92816
|
pal: {
|
|
92796
92817
|
bg: "#ffffff",
|
|
92797
92818
|
text: "#000000",
|
|
@@ -92839,16 +92860,15 @@ var init_themes = __esm({
|
|
|
92839
92860
|
auth: "#993a1a",
|
|
92840
92861
|
authF: "#f6e3dd",
|
|
92841
92862
|
idp: "#005a5a",
|
|
92842
|
-
idpF: "#daf0f0"
|
|
92843
|
-
|
|
92844
|
-
|
|
92845
|
-
|
|
92846
|
-
|
|
92847
|
-
restricted: ["#e0edf7", "#005a9c"],
|
|
92848
|
-
secret: ["#eee0f7", "#6a2fa0"]
|
|
92863
|
+
idpF: "#daf0f0",
|
|
92864
|
+
fw: "#a3140a",
|
|
92865
|
+
fwF: "#fbe3df",
|
|
92866
|
+
authn: "#00408f",
|
|
92867
|
+
authnF: "#dfe8fb"
|
|
92849
92868
|
}
|
|
92850
|
-
}
|
|
92851
|
-
nord:
|
|
92869
|
+
},
|
|
92870
|
+
nord: {
|
|
92871
|
+
dark: true,
|
|
92852
92872
|
pal: {
|
|
92853
92873
|
bg: "#2e3440",
|
|
92854
92874
|
text: "#eceff4",
|
|
@@ -92896,16 +92916,14 @@ var init_themes = __esm({
|
|
|
92896
92916
|
auth: "#c96a4a",
|
|
92897
92917
|
authF: "#332218",
|
|
92898
92918
|
idp: "#6fafaf",
|
|
92899
|
-
idpF: "#203432"
|
|
92900
|
-
|
|
92901
|
-
|
|
92902
|
-
|
|
92903
|
-
|
|
92904
|
-
restricted: ["#2f3a44", "#81a1c1"],
|
|
92905
|
-
secret: ["#312a3a", "#a38bbd"]
|
|
92919
|
+
idpF: "#203432",
|
|
92920
|
+
fw: "#bf616a",
|
|
92921
|
+
fwF: "#3b2f34",
|
|
92922
|
+
authn: "#81a1c1",
|
|
92923
|
+
authnF: "#333c4a"
|
|
92906
92924
|
}
|
|
92907
|
-
}
|
|
92908
|
-
solarized:
|
|
92925
|
+
},
|
|
92926
|
+
solarized: {
|
|
92909
92927
|
pal: {
|
|
92910
92928
|
bg: "#fdf6e3",
|
|
92911
92929
|
text: "#586e75",
|
|
@@ -92953,17 +92971,25 @@ var init_themes = __esm({
|
|
|
92953
92971
|
auth: "#b85a30",
|
|
92954
92972
|
authF: "#f5e6dd",
|
|
92955
92973
|
idp: "#2aa198",
|
|
92956
|
-
idpF: "#dff0ec"
|
|
92957
|
-
|
|
92958
|
-
|
|
92959
|
-
|
|
92960
|
-
|
|
92961
|
-
restricted: ["#e3edf3", "#268bd2"],
|
|
92962
|
-
secret: ["#e8e6f2", "#6c71c4"]
|
|
92974
|
+
idpF: "#dff0ec",
|
|
92975
|
+
fw: "#dc322f",
|
|
92976
|
+
fwF: "#f7e3dd",
|
|
92977
|
+
authn: "#268bd2",
|
|
92978
|
+
authnF: "#e3edf5"
|
|
92963
92979
|
}
|
|
92964
|
-
}
|
|
92980
|
+
}
|
|
92965
92981
|
};
|
|
92982
|
+
themes = Object.fromEntries(
|
|
92983
|
+
Object.entries(THEME_SPECS).map(([name, spec]) => [name, buildTheme(spec)])
|
|
92984
|
+
);
|
|
92966
92985
|
themeNames = [...Object.keys(themes), "classic", "classic-dark"];
|
|
92986
|
+
RESERVED_THEME_NAMES = [
|
|
92987
|
+
"classic",
|
|
92988
|
+
"classic-dark",
|
|
92989
|
+
"__proto__",
|
|
92990
|
+
"constructor",
|
|
92991
|
+
"prototype"
|
|
92992
|
+
];
|
|
92967
92993
|
}
|
|
92968
92994
|
});
|
|
92969
92995
|
|
|
@@ -93087,7 +93113,7 @@ function parseFlow(p, sourceToken) {
|
|
|
93087
93113
|
model.flows.push(flow);
|
|
93088
93114
|
}
|
|
93089
93115
|
function parseElement(p, sourceToken, parent) {
|
|
93090
|
-
const { matchToken, advance, reportError,
|
|
93116
|
+
const { matchToken, advance, reportError, syncToNextLine, model } = p;
|
|
93091
93117
|
if (!matchToken("id")) {
|
|
93092
93118
|
reportError(
|
|
93093
93119
|
`invalid declaration: \`${sourceToken.text}\` alone on this line`,
|
|
@@ -93107,84 +93133,66 @@ function parseElement(p, sourceToken, parent) {
|
|
|
93107
93133
|
parent: parent ?? void 0
|
|
93108
93134
|
};
|
|
93109
93135
|
if (matchToken("str")) element.label = advance().text;
|
|
93110
|
-
if (matchToken("lparen")) {
|
|
93111
|
-
advance();
|
|
93112
|
-
if (matchToken("id")) {
|
|
93113
|
-
const token = advance();
|
|
93114
|
-
element.attr = {
|
|
93115
|
-
value: token.text,
|
|
93116
|
-
span: token.span
|
|
93117
|
-
};
|
|
93118
|
-
} else
|
|
93119
|
-
reportError(
|
|
93120
|
-
"attribute value expected after `(`",
|
|
93121
|
-
lookAhead().span,
|
|
93122
|
-
'e.g. `trust-zone DMZ "DMZ" (public)`'
|
|
93123
|
-
);
|
|
93124
|
-
if (matchToken("rparen")) advance();
|
|
93125
|
-
else
|
|
93126
|
-
reportError(
|
|
93127
|
-
"`)` expected to close the attribute",
|
|
93128
|
-
lookAhead().span,
|
|
93129
|
-
'e.g. `trust-zone DMZ "DMZ" (public)`'
|
|
93130
|
-
);
|
|
93131
|
-
}
|
|
93132
93136
|
if (matchToken("lbrace")) {
|
|
93133
93137
|
advance();
|
|
93134
93138
|
p.parseElementBody(element);
|
|
93135
93139
|
}
|
|
93136
93140
|
(parent ? parent.children : model.elements).push(element);
|
|
93137
93141
|
}
|
|
93138
|
-
function splitEndpoint(token, flow,
|
|
93142
|
+
function splitEndpoint(token, flow, end) {
|
|
93139
93143
|
const dot = token.text.lastIndexOf(".");
|
|
93140
93144
|
if (dot <= 0 || dot === token.text.length - 1) return null;
|
|
93141
93145
|
const base = token.text.slice(0, dot);
|
|
93142
|
-
const
|
|
93146
|
+
const suffix = token.text.slice(dot + 1);
|
|
93143
93147
|
return {
|
|
93144
93148
|
flow,
|
|
93145
|
-
|
|
93149
|
+
end,
|
|
93146
93150
|
raw: token.text,
|
|
93147
93151
|
rawSpan: token.span,
|
|
93148
93152
|
base,
|
|
93149
93153
|
baseSpan: { line: token.span.line, col: token.span.col, len: base.length },
|
|
93150
|
-
|
|
93151
|
-
|
|
93154
|
+
suffix,
|
|
93155
|
+
suffixSpan: { line: token.span.line, col: token.span.col + dot + 1, len: suffix.length }
|
|
93152
93156
|
};
|
|
93153
93157
|
}
|
|
93154
|
-
function
|
|
93158
|
+
function resolveEndpointSuffixes(splits, model, diagnostics) {
|
|
93155
93159
|
for (const split of splits) {
|
|
93160
|
+
const isSide = ATTACH_SIDES.includes(split.suffix);
|
|
93161
|
+
const isRole = ATTACH_ROLES.includes(split.suffix);
|
|
93156
93162
|
if (model.index.has(split.raw)) {
|
|
93157
|
-
if (
|
|
93163
|
+
if (isSide || isRole)
|
|
93158
93164
|
diagnostics.push({
|
|
93159
93165
|
code: "W0571",
|
|
93160
93166
|
severity: "warning",
|
|
93161
|
-
message: `\`${split.raw}\` is a declared element, so \`.${split.
|
|
93167
|
+
message: `\`${split.raw}\` is a declared element, so \`.${split.suffix}\` is not read as ${isSide ? "an attachment side" : "a role"}`,
|
|
93162
93168
|
span: split.rawSpan,
|
|
93163
93169
|
note: "a declared id always wins over the `ID.side` reading",
|
|
93164
|
-
help: `rename the element if you meant to attach the flow to the ${split.
|
|
93170
|
+
help: isSide ? `rename the element if you meant to attach the flow to the ${split.suffix} side of \`${split.base}\`` : `rename the element if you meant \`${split.base}\` to be the ${split.suffix} of the queue at the other end`
|
|
93165
93171
|
});
|
|
93166
93172
|
continue;
|
|
93167
93173
|
}
|
|
93168
93174
|
if (!model.index.has(split.base)) continue;
|
|
93169
|
-
|
|
93170
|
-
if (!known)
|
|
93175
|
+
if (!isSide && !isRole)
|
|
93171
93176
|
diagnostics.push({
|
|
93172
93177
|
code: "E0223",
|
|
93173
93178
|
severity: "error",
|
|
93174
|
-
message: `unknown
|
|
93175
|
-
span: split.
|
|
93176
|
-
note: "
|
|
93177
|
-
help: "use `left`, `right`, `top` or `bottom
|
|
93179
|
+
message: `unknown endpoint suffix \`${split.suffix}\``,
|
|
93180
|
+
span: split.suffixSpan,
|
|
93181
|
+
note: "an endpoint names either the side it attaches to, as the diagram is read, or its role towards a queue",
|
|
93182
|
+
help: "use `left`, `right`, `top` or `bottom` \u2014 `APP.right -> DB.left` \u2014 or `producer` / `consumer`, e.g. `CAPTURE.producer -> EVENTS`"
|
|
93178
93183
|
});
|
|
93179
|
-
const side =
|
|
93180
|
-
|
|
93184
|
+
const side = isSide ? { value: split.suffix, span: split.suffixSpan } : void 0;
|
|
93185
|
+
const role = isRole ? { value: split.suffix, span: split.suffixSpan } : void 0;
|
|
93186
|
+
if (split.end === "from") {
|
|
93181
93187
|
split.flow.from = split.base;
|
|
93182
93188
|
split.flow.fromSpan = split.baseSpan;
|
|
93183
93189
|
split.flow.fromSide = side;
|
|
93190
|
+
split.flow.fromRole = role;
|
|
93184
93191
|
} else {
|
|
93185
93192
|
split.flow.to = split.base;
|
|
93186
93193
|
split.flow.toSpan = split.baseSpan;
|
|
93187
93194
|
split.flow.toSide = side;
|
|
93195
|
+
split.flow.toRole = role;
|
|
93188
93196
|
}
|
|
93189
93197
|
}
|
|
93190
93198
|
}
|
|
@@ -93213,6 +93221,34 @@ function tryOrderEntry(p, parent) {
|
|
|
93213
93221
|
parent.order = { value, span: valueToken.span };
|
|
93214
93222
|
return true;
|
|
93215
93223
|
}
|
|
93224
|
+
function tryLogoEntry(p, parent) {
|
|
93225
|
+
const { matchToken, advance, reportCoded, save, restore, syncToNextLine } = p;
|
|
93226
|
+
if (!parent || !matchToken("id", "logo")) return false;
|
|
93227
|
+
const mark = save();
|
|
93228
|
+
const keyToken = advance();
|
|
93229
|
+
if (!matchToken("colon")) {
|
|
93230
|
+
restore(mark);
|
|
93231
|
+
return false;
|
|
93232
|
+
}
|
|
93233
|
+
advance();
|
|
93234
|
+
const valueToken = matchToken("id") || matchToken("str") ? advance() : null;
|
|
93235
|
+
if (!valueToken || valueToken.text.length === 0) {
|
|
93236
|
+
reportCoded(
|
|
93237
|
+
"E0105",
|
|
93238
|
+
"`logo` expects a built-in name or a quoted file path",
|
|
93239
|
+
(valueToken ?? keyToken).span,
|
|
93240
|
+
'e.g. `logo: react`, or `logo: "./logos/acme.svg"` for your own'
|
|
93241
|
+
);
|
|
93242
|
+
syncToNextLine();
|
|
93243
|
+
return true;
|
|
93244
|
+
}
|
|
93245
|
+
parent.logo = {
|
|
93246
|
+
value: valueToken.text,
|
|
93247
|
+
source: valueToken.kind === "str" ? "file" : "builtin",
|
|
93248
|
+
span: valueToken.span
|
|
93249
|
+
};
|
|
93250
|
+
return true;
|
|
93251
|
+
}
|
|
93216
93252
|
function tryLegendBlock(p) {
|
|
93217
93253
|
const { matchToken, advance, reportError, lookAhead, skipNewlines, syncToNextLine, model } = p;
|
|
93218
93254
|
if (!matchToken("id", "legend")) return false;
|
|
@@ -93420,6 +93456,7 @@ function parse(src) {
|
|
|
93420
93456
|
}
|
|
93421
93457
|
if (tryStyleBlock(parent)) return;
|
|
93422
93458
|
if (tryOrderEntry(parser, parent)) return;
|
|
93459
|
+
if (tryLogoEntry(parser, parent)) return;
|
|
93423
93460
|
if (!parent && tryLegendBlock(parser)) return;
|
|
93424
93461
|
if (!parent && tryBusinessObject(parser)) return;
|
|
93425
93462
|
parseFlowOrElement(parser, parent);
|
|
@@ -93432,7 +93469,7 @@ function parse(src) {
|
|
|
93432
93469
|
for (const [id, element] of indexElementsById(model.elements)) {
|
|
93433
93470
|
if (!model.index.has(id)) model.index.set(id, element);
|
|
93434
93471
|
}
|
|
93435
|
-
|
|
93472
|
+
resolveEndpointSuffixes(endpointSplits, model, diagnostics);
|
|
93436
93473
|
return { model, diags: diagnostics };
|
|
93437
93474
|
}
|
|
93438
93475
|
function applyStyleEntry(entry) {
|
|
@@ -93664,12 +93701,14 @@ var init_parser = __esm({
|
|
|
93664
93701
|
});
|
|
93665
93702
|
|
|
93666
93703
|
// src/views.ts
|
|
93667
|
-
var logicalView, applicationView, infrastructureView,
|
|
93704
|
+
var LINE_STYLES2, logicalView, applicationView, infrastructureView, views;
|
|
93668
93705
|
var init_views = __esm({
|
|
93669
93706
|
"src/views.ts"() {
|
|
93670
93707
|
"use strict";
|
|
93708
|
+
LINE_STYLES2 = ["solid", "dashed", "dotted"];
|
|
93671
93709
|
logicalView = {
|
|
93672
93710
|
name: "logical",
|
|
93711
|
+
laneKinds: ["external"],
|
|
93673
93712
|
kinds: ["actor-group", "actor", "system", "layer", "block", "external"],
|
|
93674
93713
|
containerKinds: ["actor-group", "system", "layer", "external"],
|
|
93675
93714
|
// Logical flows carry no technical detail (flowTech* are null below), so the
|
|
@@ -93698,6 +93737,16 @@ var init_views = __esm({
|
|
|
93698
93737
|
},
|
|
93699
93738
|
legendFlowLabel: "Functional flow (label = exchanged data)",
|
|
93700
93739
|
legendFlowLabelFr: "Flux fonctionnel (libell\xE9 = donn\xE9es \xE9chang\xE9es)",
|
|
93740
|
+
legendLineStyles: {
|
|
93741
|
+
solid: "Direct exchange",
|
|
93742
|
+
dashed: "Asynchronous or event-driven exchange",
|
|
93743
|
+
dotted: "Dependency \u2014 no data exchanged"
|
|
93744
|
+
},
|
|
93745
|
+
legendLineStylesFr: {
|
|
93746
|
+
solid: "\xC9change direct",
|
|
93747
|
+
dashed: "\xC9change asynchrone ou \xE9v\xE9nementiel",
|
|
93748
|
+
dotted: "D\xE9pendance \u2014 aucune donn\xE9e \xE9chang\xE9e"
|
|
93749
|
+
},
|
|
93701
93750
|
flowTechRequired: null,
|
|
93702
93751
|
flowTechRecommended: null,
|
|
93703
93752
|
businessObjects: true,
|
|
@@ -93792,17 +93841,33 @@ var init_views = __esm({
|
|
|
93792
93841
|
};
|
|
93793
93842
|
applicationView = {
|
|
93794
93843
|
name: "application",
|
|
93844
|
+
laneKinds: ["external"],
|
|
93845
|
+
hubKinds: ["queue"],
|
|
93795
93846
|
kinds: [
|
|
93796
93847
|
"actor-group",
|
|
93797
93848
|
"actor",
|
|
93798
93849
|
"system",
|
|
93799
93850
|
"application",
|
|
93800
93851
|
"module",
|
|
93852
|
+
"gateway",
|
|
93853
|
+
"auth",
|
|
93854
|
+
"idp",
|
|
93801
93855
|
"queue",
|
|
93802
93856
|
"datastore",
|
|
93803
93857
|
"external"
|
|
93804
93858
|
],
|
|
93805
93859
|
containerKinds: ["actor-group", "system", "application", "external"],
|
|
93860
|
+
// A logo says what a thing is built with, so it belongs to the kinds that run
|
|
93861
|
+
// code. `actor` and `actor-group` are people; `system` is a grouping whose
|
|
93862
|
+
// children carry their own stacks. `gateway`, `auth` and `idp` are left out
|
|
93863
|
+
// on purpose: they are drawn with a corner glyph, and the glyph renderer has
|
|
93864
|
+
// no logo slot — a logo on them would reserve width and draw nothing.
|
|
93865
|
+
logoKinds: ["application", "module", "queue", "datastore", "external"],
|
|
93866
|
+
// The same three kinds as the infrastructure view, drawn the same way: an API
|
|
93867
|
+
// gateway, an auth middleware and an identity provider are containers in
|
|
93868
|
+
// their own right at the application level, and the glyph is what tells them
|
|
93869
|
+
// apart from a plain `application` at a glance.
|
|
93870
|
+
glyphKinds: ["gateway", "auth", "idp"],
|
|
93806
93871
|
// C4-style `(API_REST, JSON)`: the protocol half is worth tabulating, the port is not.
|
|
93807
93872
|
// `zoneOf` walks ancestors nearest-first, so an endpoint inside an application
|
|
93808
93873
|
// still reads `Name (App)`; the system only annotates what sits directly in it.
|
|
@@ -93814,6 +93879,9 @@ var init_views = __esm({
|
|
|
93814
93879
|
"actor-group": 0,
|
|
93815
93880
|
system: 1,
|
|
93816
93881
|
application: 1,
|
|
93882
|
+
gateway: 1,
|
|
93883
|
+
auth: 1,
|
|
93884
|
+
idp: 1,
|
|
93817
93885
|
queue: 1,
|
|
93818
93886
|
datastore: 1,
|
|
93819
93887
|
external: 2
|
|
@@ -93824,6 +93892,9 @@ var init_views = __esm({
|
|
|
93824
93892
|
system: "System",
|
|
93825
93893
|
application: "Application",
|
|
93826
93894
|
module: "Application module",
|
|
93895
|
+
gateway: "Gateway / reverse proxy",
|
|
93896
|
+
auth: "Auth middleware",
|
|
93897
|
+
idp: "Identity provider (IdP)",
|
|
93827
93898
|
queue: "Message queue / broker",
|
|
93828
93899
|
datastore: "Datastore / registry",
|
|
93829
93900
|
external: "External system"
|
|
@@ -93834,6 +93905,9 @@ var init_views = __esm({
|
|
|
93834
93905
|
system: "Syst\xE8me",
|
|
93835
93906
|
application: "Application",
|
|
93836
93907
|
module: "Module applicatif",
|
|
93908
|
+
gateway: "Passerelle / proxy",
|
|
93909
|
+
auth: "Middleware d'authentification",
|
|
93910
|
+
idp: "Fournisseur d'identit\xE9 (IdP)",
|
|
93837
93911
|
queue: "File de messages / broker",
|
|
93838
93912
|
datastore: "Entrep\xF4t / r\xE9f\xE9rentiel",
|
|
93839
93913
|
external: "Syst\xE8me externe"
|
|
@@ -93845,6 +93919,16 @@ var init_views = __esm({
|
|
|
93845
93919
|
},
|
|
93846
93920
|
legendFlowLabel: "Application flow \u2014 (protocol, format) under the label",
|
|
93847
93921
|
legendFlowLabelFr: "Flux applicatif \u2014 (protocole, format) sous le libell\xE9",
|
|
93922
|
+
legendLineStyles: {
|
|
93923
|
+
solid: "Synchronous call (request / response)",
|
|
93924
|
+
dashed: "Asynchronous exchange (message, event)",
|
|
93925
|
+
dotted: "Dependency \u2014 no direct call"
|
|
93926
|
+
},
|
|
93927
|
+
legendLineStylesFr: {
|
|
93928
|
+
solid: "Appel synchrone (requ\xEAte / r\xE9ponse)",
|
|
93929
|
+
dashed: "\xC9change asynchrone (message, \xE9v\xE9nement)",
|
|
93930
|
+
dotted: "D\xE9pendance \u2014 sans appel direct"
|
|
93931
|
+
},
|
|
93848
93932
|
flowLabelRequired: null,
|
|
93849
93933
|
flowTechRequired: null,
|
|
93850
93934
|
flowTechRecommended: {
|
|
@@ -93871,7 +93955,7 @@ var init_views = __esm({
|
|
|
93871
93955
|
minCounts: [],
|
|
93872
93956
|
isolatedWarn: {
|
|
93873
93957
|
code: "W0510",
|
|
93874
|
-
kinds: ["module", "queue", "datastore"],
|
|
93958
|
+
kinds: ["module", "gateway", "auth", "idp", "queue", "datastore"],
|
|
93875
93959
|
message: "isolated element: no incoming or outgoing flow"
|
|
93876
93960
|
},
|
|
93877
93961
|
defaults: {
|
|
@@ -93893,6 +93977,20 @@ var init_views = __esm({
|
|
|
93893
93977
|
fill: "#ffffff",
|
|
93894
93978
|
stroke: { color: "#5b7a99", style: "solid", width: 1.3 }
|
|
93895
93979
|
},
|
|
93980
|
+
// Same colours as the infrastructure view's `gateway`, `auth` and `idp`: one
|
|
93981
|
+
// thing drawn one way, whichever view it appears in.
|
|
93982
|
+
gateway: {
|
|
93983
|
+
fill: "#f5e6dd",
|
|
93984
|
+
stroke: { color: "#bf5530", style: "solid", width: 1.6 }
|
|
93985
|
+
},
|
|
93986
|
+
auth: {
|
|
93987
|
+
fill: "#e8f1fb",
|
|
93988
|
+
stroke: { color: "#2f6fb5", style: "solid", width: 1.5 }
|
|
93989
|
+
},
|
|
93990
|
+
idp: {
|
|
93991
|
+
fill: "#e0f0f0",
|
|
93992
|
+
stroke: { color: "#3a8f8f", style: "solid", width: 1.3 }
|
|
93993
|
+
},
|
|
93896
93994
|
queue: {
|
|
93897
93995
|
fill: "#f3eef8",
|
|
93898
93996
|
stroke: { color: "#8a6fae", style: "solid", width: 1.3 }
|
|
@@ -93924,6 +94022,18 @@ var init_views = __esm({
|
|
|
93924
94022
|
fill: "#252a31",
|
|
93925
94023
|
stroke: { color: "#5f7f9e", style: "solid", width: 1.3 }
|
|
93926
94024
|
},
|
|
94025
|
+
gateway: {
|
|
94026
|
+
fill: "#332218",
|
|
94027
|
+
stroke: { color: "#c96a4a", style: "solid", width: 1.6 }
|
|
94028
|
+
},
|
|
94029
|
+
auth: {
|
|
94030
|
+
fill: "#1d2735",
|
|
94031
|
+
stroke: { color: "#6fa8e0", style: "solid", width: 1.5 }
|
|
94032
|
+
},
|
|
94033
|
+
idp: {
|
|
94034
|
+
fill: "#1a2e2e",
|
|
94035
|
+
stroke: { color: "#4fafaf", style: "solid", width: 1.3 }
|
|
94036
|
+
},
|
|
93927
94037
|
queue: {
|
|
93928
94038
|
fill: "#2a2433",
|
|
93929
94039
|
stroke: { color: "#7a5f9e", style: "solid", width: 1.3 }
|
|
@@ -93941,19 +94051,27 @@ var init_views = __esm({
|
|
|
93941
94051
|
};
|
|
93942
94052
|
infrastructureView = {
|
|
93943
94053
|
name: "infrastructure",
|
|
94054
|
+
laneKinds: ["external"],
|
|
94055
|
+
hubKinds: ["queue"],
|
|
93944
94056
|
kinds: [
|
|
93945
94057
|
"actor",
|
|
94058
|
+
"device",
|
|
93946
94059
|
"site",
|
|
93947
94060
|
"network-zone",
|
|
93948
94061
|
"server",
|
|
93949
94062
|
"app-instance",
|
|
93950
94063
|
"queue",
|
|
93951
94064
|
"gateway",
|
|
94065
|
+
"firewall",
|
|
93952
94066
|
"auth",
|
|
93953
94067
|
"idp",
|
|
93954
94068
|
"external"
|
|
93955
94069
|
],
|
|
93956
94070
|
containerKinds: ["site", "network-zone", "server"],
|
|
94071
|
+
glyphKinds: ["gateway", "firewall", "auth", "idp", "device"],
|
|
94072
|
+
// A `device` is a flow origin, so it belongs on the entry side with the
|
|
94073
|
+
// actors rather than in a declaration-order band with the zones.
|
|
94074
|
+
ingressKinds: ["actor", "actor-group", "device"],
|
|
93957
94075
|
// The reference shape: the matrice des flux techniques as an EA dossier expects it.
|
|
93958
94076
|
matrix: {
|
|
93959
94077
|
zoneKinds: ["network-zone", "site"],
|
|
@@ -93964,24 +94082,28 @@ var init_views = __esm({
|
|
|
93964
94082
|
actorLegend: true,
|
|
93965
94083
|
legendNames: {
|
|
93966
94084
|
actor: "User / consumer",
|
|
94085
|
+
device: "Device / workstation",
|
|
93967
94086
|
site: "Site / data center",
|
|
93968
94087
|
"network-zone": "Network zone",
|
|
93969
94088
|
server: "Server / VM",
|
|
93970
94089
|
"app-instance": "Deployed application",
|
|
93971
94090
|
queue: "Message queue / broker",
|
|
93972
94091
|
gateway: "Gateway / reverse proxy",
|
|
94092
|
+
firewall: "Firewall",
|
|
93973
94093
|
auth: "Auth middleware",
|
|
93974
94094
|
idp: "Identity provider (IdP)",
|
|
93975
94095
|
external: "External system"
|
|
93976
94096
|
},
|
|
93977
94097
|
legendNamesFr: {
|
|
93978
94098
|
actor: "Utilisateur / consommateur",
|
|
94099
|
+
device: "Poste de travail",
|
|
93979
94100
|
site: "Site / centre de donn\xE9es",
|
|
93980
94101
|
"network-zone": "Zone r\xE9seau",
|
|
93981
94102
|
server: "Serveur / VM",
|
|
93982
94103
|
"app-instance": "Application d\xE9ploy\xE9e",
|
|
93983
94104
|
queue: "File de messages / broker",
|
|
93984
94105
|
gateway: "Passerelle / proxy",
|
|
94106
|
+
firewall: "Pare-feu",
|
|
93985
94107
|
auth: "Middleware d'authentification",
|
|
93986
94108
|
idp: "Fournisseur d'identit\xE9 (IdP)",
|
|
93987
94109
|
external: "Syst\xE8me externe"
|
|
@@ -93993,6 +94115,16 @@ var init_views = __esm({
|
|
|
93993
94115
|
},
|
|
93994
94116
|
legendFlowLabel: "Technical flow (protocol, port)",
|
|
93995
94117
|
legendFlowLabelFr: "Flux technique (protocole, port)",
|
|
94118
|
+
legendLineStyles: {
|
|
94119
|
+
solid: "Permanent link \u2014 nominal traffic",
|
|
94120
|
+
dashed: "Asynchronous or intermittent link",
|
|
94121
|
+
dotted: "Dependency \u2014 outside nominal traffic"
|
|
94122
|
+
},
|
|
94123
|
+
legendLineStylesFr: {
|
|
94124
|
+
solid: "Lien permanent \u2014 trafic nominal",
|
|
94125
|
+
dashed: "Lien asynchrone ou intermittent",
|
|
94126
|
+
dotted: "D\xE9pendance \u2014 hors trafic nominal"
|
|
94127
|
+
},
|
|
93996
94128
|
flowLabelRequired: null,
|
|
93997
94129
|
flowTechRecommended: null,
|
|
93998
94130
|
flowTechRequired: {
|
|
@@ -94026,7 +94158,7 @@ var init_views = __esm({
|
|
|
94026
94158
|
minCounts: [],
|
|
94027
94159
|
isolatedWarn: {
|
|
94028
94160
|
code: "W0510",
|
|
94029
|
-
kinds: ["app-instance", "queue", "gateway", "auth", "idp"],
|
|
94161
|
+
kinds: ["app-instance", "queue", "gateway", "firewall", "auth", "idp", "device"],
|
|
94030
94162
|
message: "isolated element: no incoming or outgoing flow"
|
|
94031
94163
|
},
|
|
94032
94164
|
defaults: {
|
|
@@ -94043,6 +94175,12 @@ var init_views = __esm({
|
|
|
94043
94175
|
fill: "#ffffff",
|
|
94044
94176
|
stroke: { color: "#55606b", style: "solid", width: 1.5 }
|
|
94045
94177
|
},
|
|
94178
|
+
// The server's grey: a workstation is hardware like a server is, and the
|
|
94179
|
+
// monitor glyph is what tells the two apart.
|
|
94180
|
+
device: {
|
|
94181
|
+
fill: "#eef0f2",
|
|
94182
|
+
stroke: { color: "#55606b", style: "solid", width: 1.3 }
|
|
94183
|
+
},
|
|
94046
94184
|
"app-instance": {
|
|
94047
94185
|
fill: "#fff7e6",
|
|
94048
94186
|
stroke: { color: "#b08d2a", style: "solid", width: 1.2 }
|
|
@@ -94055,9 +94193,13 @@ var init_views = __esm({
|
|
|
94055
94193
|
fill: "#f5e6dd",
|
|
94056
94194
|
stroke: { color: "#bf5530", style: "solid", width: 1.6 }
|
|
94057
94195
|
},
|
|
94196
|
+
firewall: {
|
|
94197
|
+
fill: "#fdecea",
|
|
94198
|
+
stroke: { color: "#c0392b", style: "solid", width: 1.6 }
|
|
94199
|
+
},
|
|
94058
94200
|
auth: {
|
|
94059
|
-
fill: "#
|
|
94060
|
-
stroke: { color: "#
|
|
94201
|
+
fill: "#e8f1fb",
|
|
94202
|
+
stroke: { color: "#2f6fb5", style: "solid", width: 1.5 }
|
|
94061
94203
|
},
|
|
94062
94204
|
idp: {
|
|
94063
94205
|
fill: "#e0f0f0",
|
|
@@ -94082,6 +94224,10 @@ var init_views = __esm({
|
|
|
94082
94224
|
fill: "#252a31",
|
|
94083
94225
|
stroke: { color: "#6b7885", style: "solid", width: 1.5 }
|
|
94084
94226
|
},
|
|
94227
|
+
device: {
|
|
94228
|
+
fill: "#242a30",
|
|
94229
|
+
stroke: { color: "#6b7885", style: "solid", width: 1.3 }
|
|
94230
|
+
},
|
|
94085
94231
|
"app-instance": {
|
|
94086
94232
|
fill: "#2e2717",
|
|
94087
94233
|
stroke: { color: "#b08d2a", style: "solid", width: 1.2 }
|
|
@@ -94094,9 +94240,13 @@ var init_views = __esm({
|
|
|
94094
94240
|
fill: "#332218",
|
|
94095
94241
|
stroke: { color: "#c96a4a", style: "solid", width: 1.6 }
|
|
94096
94242
|
},
|
|
94243
|
+
firewall: {
|
|
94244
|
+
fill: "#33211f",
|
|
94245
|
+
stroke: { color: "#e5675a", style: "solid", width: 1.6 }
|
|
94246
|
+
},
|
|
94097
94247
|
auth: {
|
|
94098
|
-
fill: "#
|
|
94099
|
-
stroke: { color: "#
|
|
94248
|
+
fill: "#1d2735",
|
|
94249
|
+
stroke: { color: "#6fa8e0", style: "solid", width: 1.5 }
|
|
94100
94250
|
},
|
|
94101
94251
|
idp: {
|
|
94102
94252
|
fill: "#1a2e2e",
|
|
@@ -94108,186 +94258,186 @@ var init_views = __esm({
|
|
|
94108
94258
|
}
|
|
94109
94259
|
}
|
|
94110
94260
|
};
|
|
94111
|
-
|
|
94112
|
-
|
|
94113
|
-
|
|
94114
|
-
|
|
94115
|
-
|
|
94116
|
-
|
|
94117
|
-
|
|
94118
|
-
|
|
94119
|
-
|
|
94120
|
-
|
|
94261
|
+
views = {
|
|
94262
|
+
logical: logicalView,
|
|
94263
|
+
application: applicationView,
|
|
94264
|
+
infrastructure: infrastructureView
|
|
94265
|
+
};
|
|
94266
|
+
}
|
|
94267
|
+
});
|
|
94268
|
+
|
|
94269
|
+
// src/logos.ts
|
|
94270
|
+
var LOGOS, LOGO_NAMES;
|
|
94271
|
+
var init_logos = __esm({
|
|
94272
|
+
"src/logos.ts"() {
|
|
94273
|
+
"use strict";
|
|
94274
|
+
LOGOS = {
|
|
94275
|
+
angular: {
|
|
94276
|
+
title: "Angular",
|
|
94277
|
+
d: "M16.712 17.711H7.288l-1.204 2.916L12 24l5.916-3.373-1.204-2.916ZM14.692 0l7.832 16.855.814-12.856L14.692 0ZM9.308 0 .662 3.999l.814 12.856L9.308 0Zm-.405 13.93h6.198L12 6.396 8.903 13.93Z",
|
|
94278
|
+
license: "CC-BY-4.0",
|
|
94279
|
+
licenseUrl: "https://creativecommons.org/licenses/by/4.0/legalcode",
|
|
94280
|
+
source: "https://angular.dev/press-kit"
|
|
94121
94281
|
},
|
|
94122
|
-
|
|
94123
|
-
|
|
94124
|
-
|
|
94125
|
-
|
|
94126
|
-
|
|
94127
|
-
|
|
94128
|
-
external: "Untrusted external"
|
|
94282
|
+
apache: {
|
|
94283
|
+
title: "Apache",
|
|
94284
|
+
d: "M17.805 2.197v.066h.156v.44h.072v-.44h.156v-.066zm.9 0l-.175.353-.172-.353h-.087v.506h.067V2.3l.172.35h.045l.172-.35v.404h.066v-.506zm-4.257 1c-.204.31-.424.66-.66 1.06l-.04.062a44.457 44.457 0 00-1.265 2.29c-.187.36-.38.742-.577 1.146l2.267-.25c.66-.302.955-.578 1.242-.976a15.5 15.5 0 00.23-.342c.23-.363.46-.763.663-1.16.197-.386.37-.767.505-1.11.083-.22.15-.422.198-.6.042-.158.074-.307.1-.45-.884.15-1.965.295-2.668.33zM11.894 7.78l-.077.16c-.078.16-.157.32-.236.488-.086.18-.172.364-.26.552l-.132.287a75.265 75.265 0 00-1.427 3.3c-.163.397-.327.807-.493 1.23-.15.38-.297.765-.45 1.164l-.02.06c-.15.396-.3.802-.453 1.22l-.01.027.72-.08a.213.213 0 01-.042-.006c.863-.106 2.01-.75 2.75-1.547.342-.367.652-.8.94-1.306.213-.377.413-.795.604-1.258.168-.405.328-.843.48-1.318-.196.105-.423.18-.673.235a2.184 2.184 0 01-.273.046c.806-.31 1.314-.905 1.683-1.64a2.816 2.816 0 01-.968.428c-.06.012-.116.022-.174.03l-.043.006h.002c.278-.118.514-.248.718-.403a2.571 2.571 0 00.637-.698l.063-.104.077-.154a8.107 8.107 0 00.367-.85l.03-.088a3.04 3.04 0 00.123-.463.733.733 0 01-.094.065c-.243.145-.66.277-.996.34l.663-.074-.664.073h-.017l-.1.017c.006-.003.01-.006.017-.008l-2.265.25-.013.022zM8.27 16.45c-.117.323-.236.654-.355.992l-.005.015c-.016.046-.032.094-.05.142-.08.227-.15.432-.31.9.264.12.475.435.675.793a1.44 1.44 0 00-.466-.99c1.293.06 2.41-.27 2.99-1.217.05-.084.096-.173.14-.268-.26.333-.59.474-1.2.44 0 0-.004 0-.005.002l.004-.002c.9-.404 1.354-.79 1.754-1.433.094-.153.186-.32.28-.503-.788.81-1.702 1.04-2.664.865l-.72.078a6.43 6.43 0 00-.067.183zM15.42.112c-.376.222-1 .85-1.748 1.763l.686 1.294c.48-.687.97-1.307 1.462-1.836l.058-.062c-.02.02-.04.04-.057.062-.16.176-.644.74-1.375 1.863.703-.035 1.784-.18 2.666-.33.262-1.47-.258-2.142-.258-2.142s-.66-1.07-1.436-.61zm-3.084 6.402a40.253 40.253 0 011.306-2.26l.04-.064c.224-.352.45-.693.677-1.02l-.685-1.293-.157.192c-.197.245-.403.51-.613.79a39.853 39.853 0 00-2.016 2.97l-.022.038.893 1.763c.19-.378.38-.752.575-1.118zm-3.73 8.32c.158-.406.319-.81.483-1.225.156-.394.32-.79.484-1.19a91.133 91.133 0 011.6-3.604l.205-.424c.12-.243.237-.485.36-.724a.125.125 0 01.02-.04l-.895-1.763-.044.07c-.207.34-.414.687-.617 1.042a38.056 38.056 0 00-1.092 2.04l-.094.193a24.573 24.573 0 00-1.258 3.087 18.492 18.492 0 00-.52 1.997l.896 1.77c.117-.317.24-.638.364-.963zm-1.376-.476a13.38 13.38 0 00-.234 1.692c0 .02-.004.04-.005.06-.28-.45-1.03-.888-1.026-.884.537.778.944 1.55 1.005 2.31-.29.058-.684-.027-1.14-.195.475.436.83.556.97.588-.434.03-.89.328-1.346.67.668-.27 1.21-.38 1.596-.29-.61 1.74-1.23 3.655-1.843 5.69a.538.538 0 00.364-.354c.11-.368.84-2.786 1.978-5.965l.097-.27.028-.078c.12-.332.246-.672.374-1.02l.09-.237v-.004L7.24 14.3c-.003.02-.01.04-.012.06z",
|
|
94285
|
+
license: "Apache-2.0",
|
|
94286
|
+
licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt",
|
|
94287
|
+
source: "https://www.apache.org/foundation/press/kit"
|
|
94129
94288
|
},
|
|
94130
|
-
|
|
94131
|
-
|
|
94132
|
-
|
|
94133
|
-
|
|
94134
|
-
|
|
94135
|
-
|
|
94136
|
-
external: "Externe non ma\xEEtris\xE9"
|
|
94289
|
+
apachekafka: {
|
|
94290
|
+
title: "Apache Kafka",
|
|
94291
|
+
d: "M9.71 2.136a1.43 1.43 0 0 0-2.047 0h-.007a1.48 1.48 0 0 0-.421 1.042c0 .41.161.777.422 1.039l.007.007c.257.264.616.426 1.019.426.404 0 .766-.162 1.027-.426l.003-.007c.261-.262.421-.629.421-1.039 0-.408-.159-.777-.421-1.042H9.71zM8.683 22.295c.404 0 .766-.167 1.027-.429l.003-.008c.261-.261.421-.631.421-1.036 0-.41-.159-.778-.421-1.044H9.71a1.42 1.42 0 0 0-1.027-.432 1.4 1.4 0 0 0-1.02.432h-.007c-.26.266-.422.634-.422 1.044 0 .406.161.775.422 1.036l.007.008c.258.262.617.429 1.02.429zm7.89-4.462c.359-.096.683-.33.882-.684l.027-.052a1.47 1.47 0 0 0 .114-1.067 1.454 1.454 0 0 0-.675-.896l-.021-.014a1.425 1.425 0 0 0-1.078-.132c-.36.091-.684.335-.881.686-.2.349-.241.75-.146 1.119.099.363.33.691.675.896h.002c.346.203.737.239 1.101.144zm-6.405-7.342a2.083 2.083 0 0 0-1.485-.627c-.58 0-1.103.242-1.482.627-.378.385-.612.916-.612 1.507s.233 1.124.612 1.514a2.08 2.08 0 0 0 2.967 0c.379-.39.612-.923.612-1.514s-.233-1.122-.612-1.507zm-.835-2.51c.843.141 1.6.552 2.178 1.144h.004c.092.093.182.196.265.299l1.446-.851a3.176 3.176 0 0 1-.047-1.808 3.149 3.149 0 0 1 1.456-1.926l.025-.016a3.062 3.062 0 0 1 2.345-.306c.77.21 1.465.721 1.898 1.482v.002c.431.757.518 1.626.313 2.408a3.145 3.145 0 0 1-1.456 1.928l-.198.118h-.02a3.095 3.095 0 0 1-2.154.201 3.127 3.127 0 0 1-1.514-.944l-1.444.848a4.162 4.162 0 0 1 0 2.879l1.444.846c.413-.47.939-.789 1.514-.944a3.041 3.041 0 0 1 2.371.319l.048.023v.002a3.17 3.17 0 0 1 1.408 1.906 3.215 3.215 0 0 1-.313 2.405l-.026.053-.003-.005a3.147 3.147 0 0 1-1.867 1.436 3.096 3.096 0 0 1-2.371-.318v-.006a3.156 3.156 0 0 1-1.456-1.927 3.175 3.175 0 0 1 .047-1.805l-1.446-.848a3.905 3.905 0 0 1-.265.294l-.004.005a3.938 3.938 0 0 1-2.178 1.138v1.699a3.09 3.09 0 0 1 1.56.862l.002.004c.565.572.914 1.368.914 2.243 0 .873-.35 1.664-.914 2.239l-.002.009a3.1 3.1 0 0 1-2.21.931 3.1 3.1 0 0 1-2.206-.93h-.002v-.009a3.186 3.186 0 0 1-.916-2.239c0-.875.35-1.672.916-2.243v-.004h.002a3.1 3.1 0 0 1 1.558-.862v-1.699a3.926 3.926 0 0 1-2.176-1.138l-.006-.005a4.098 4.098 0 0 1-1.173-2.874c0-1.122.452-2.136 1.173-2.872h.006a3.947 3.947 0 0 1 2.176-1.144V6.289a3.137 3.137 0 0 1-1.558-.864h-.002v-.004a3.192 3.192 0 0 1-.916-2.243c0-.871.35-1.669.916-2.243l.002-.002A3.084 3.084 0 0 1 8.683 0c.861 0 1.641.355 2.21.932v.002h.002c.565.574.914 1.372.914 2.243 0 .876-.35 1.667-.914 2.243l-.002.005a3.142 3.142 0 0 1-1.56.864v1.692zm8.121-1.129l-.012-.019a1.452 1.452 0 0 0-.87-.668 1.43 1.43 0 0 0-1.103.146h.002c-.347.2-.58.529-.677.896-.095.365-.054.768.146 1.119l.007.009c.2.347.519.579.874.673.357.103.755.059 1.098-.144l.019-.009a1.47 1.47 0 0 0 .657-.885 1.493 1.493 0 0 0-.141-1.118",
|
|
94292
|
+
license: "Apache-2.0",
|
|
94293
|
+
licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt",
|
|
94294
|
+
source: "https://apache.org/logos"
|
|
94137
94295
|
},
|
|
94138
|
-
|
|
94139
|
-
|
|
94140
|
-
|
|
94141
|
-
|
|
94296
|
+
apachespark: {
|
|
94297
|
+
title: "Apache Spark",
|
|
94298
|
+
d: "M10.812 0c-.425.013-.845.215-1.196.605a3.593 3.593 0 00-.493.722c-.355.667-.425 1.415-.556 2.143a551.9 551.9 0 00-.726 4.087c-.027.16-.096.227-.244.273C5.83 8.386 4.06 8.94 2.3 9.514c-.387.125-.773.289-1.114.506-1.042.665-1.196 1.753-.415 2.71.346.422.79.715 1.284.936 1.1.49 2.202.976 3.3 1.47.019.01.036.013.053.019h-.004l1.306.535c0 .023.002.045 0 .073-.2 2.03-.39 4.063-.58 6.095-.04.419-.012.831.134 1.23.317.87 1.065 1.148 1.881.701.372-.204.666-.497.937-.818 1.372-1.623 2.746-3.244 4.113-4.872.111-.133.205-.15.363-.098.349.117.697.231 1.045.347h.001c.02.012.045.02.073.03l.142.042c1.248.416 2.68.775 3.929 1.19.4.132.622.164 1.045.098.311-.048.592-.062.828-.236.602-.33.995-.957.988-1.682-.005-.427-.154-.813-.35-1.186-.82-1.556-1.637-3.113-2.461-4.666-.078-.148-.076-.243.037-.375 1.381-1.615 2.756-3.236 4.133-4.855.272-.32.513-.658.653-1.058.308-.878-.09-1.57-1-1.741a2.783 2.783 0 00-1.235.069c-1.974.521-3.947 1.041-5.918 1.57-.175.047-.26.015-.355-.144a353.08 353.08 0 00-2.421-4.018 4.61 4.61 0 00-.652-.849c-.371-.37-.802-.549-1.227-.536zm.172 3.703a.592.592 0 01.189.211c.87 1.446 1.742 2.89 2.609 4.338.07.118.135.16.277.121 1.525-.41 3.052-.813 4.579-1.217.367-.098.735-.193 1.103-.289a.399.399 0 01-.1.2c-1.259 1.48-2.516 2.962-3.779 4.438-.11.13-.12.22-.04.37.937 1.803 1.768 3.309 2.498 4.76l-3.696-1.019c-.538-.18-1.077-.358-1.615-.539-.163-.055-.25-.03-.36.1-1.248 1.488-2.504 2.97-3.759 4.454a.398.398 0 01-.18.132c.035-.378.068-.757.104-1.136.149-1.572.297-3.144.451-4.716-.03-.318.117-.405-.322-.545-1.493-.593-3.346-1.321-4.816-1.905a.595.595 0 01.24-.134c1.797-.57 3.595-1.14 5.394-1.705.127-.04.199-.092.211-.233.013-.148.05-.294.076-.441.241-1.363.483-2.726.726-4.088.068-.386.14-.771.21-1.157z",
|
|
94299
|
+
license: "Apache-2.0",
|
|
94300
|
+
licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt",
|
|
94301
|
+
source: "https://apache.org/logos"
|
|
94142
94302
|
},
|
|
94143
|
-
|
|
94144
|
-
|
|
94145
|
-
|
|
94146
|
-
partitionByOrder: true,
|
|
94147
|
-
flowLabelRequired: {
|
|
94148
|
-
code: "E0203",
|
|
94149
|
-
message: "flow without a label",
|
|
94150
|
-
help: 'add a label describing the exchange: `A -> B : "\u2026" (TLS1.3)`'
|
|
94303
|
+
django: {
|
|
94304
|
+
title: "Django",
|
|
94305
|
+
d: "M11.146 0h3.924v18.166c-2.013.382-3.491.535-5.096.535-4.791 0-7.288-2.166-7.288-6.32 0-4.002 2.65-6.6 6.753-6.6.637 0 1.121.05 1.707.203zm0 9.143a3.894 3.894 0 00-1.325-.204c-1.988 0-3.134 1.223-3.134 3.365 0 2.09 1.096 3.236 3.109 3.236.433 0 .79-.025 1.35-.102V9.142zM21.314 6.06v9.098c0 3.134-.229 4.638-.917 5.937-.637 1.249-1.478 2.039-3.211 2.905l-3.644-1.733c1.733-.815 2.574-1.53 3.109-2.625.561-1.121.739-2.421.739-5.835V6.059h3.924zM17.39.021h3.924v4.026H17.39z"
|
|
94151
94306
|
},
|
|
94152
|
-
|
|
94153
|
-
|
|
94154
|
-
|
|
94155
|
-
kind: "trust-zone",
|
|
94156
|
-
values: SEC_LEVELS,
|
|
94157
|
-
code: "E0250",
|
|
94158
|
-
message: "trust zone without a valid sensitivity level",
|
|
94159
|
-
help: 'set a level in parentheses: `trust-zone DMZ "DMZ" (public)` \u2014 one of public, internal, restricted, secret'
|
|
94307
|
+
docker: {
|
|
94308
|
+
title: "Docker",
|
|
94309
|
+
d: "M13.983 11.078h2.119a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.119a.185.185 0 00-.185.185v1.888c0 .102.083.185.185.185m-2.954-5.43h2.118a.186.186 0 00.186-.186V3.574a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m0 2.716h2.118a.187.187 0 00.186-.186V6.29a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.887c0 .102.082.185.185.186m-2.93 0h2.12a.186.186 0 00.184-.186V6.29a.185.185 0 00-.185-.185H8.1a.185.185 0 00-.185.185v1.887c0 .102.083.185.185.186m-2.964 0h2.119a.186.186 0 00.185-.186V6.29a.185.185 0 00-.185-.185H5.136a.186.186 0 00-.186.185v1.887c0 .102.084.185.186.186m5.893 2.715h2.118a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m-2.93 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.083.185.185.185m-2.964 0h2.119a.185.185 0 00.185-.185V9.006a.185.185 0 00-.184-.186h-2.12a.186.186 0 00-.186.186v1.887c0 .102.084.185.186.185m-2.92 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.082.185.185.185M23.763 9.89c-.065-.051-.672-.51-1.954-.51-.338.001-.676.03-1.01.087-.248-1.7-1.653-2.53-1.716-2.566l-.344-.199-.226.327c-.284.438-.49.922-.612 1.43-.23.97-.09 1.882.403 2.661-.595.332-1.55.413-1.744.42H.751a.751.751 0 00-.75.748 11.376 11.376 0 00.692 4.062c.545 1.428 1.355 2.48 2.41 3.124 1.18.723 3.1 1.137 5.275 1.137.983.003 1.963-.086 2.93-.266a12.248 12.248 0 003.823-1.389c.98-.567 1.86-1.288 2.61-2.136 1.252-1.418 1.998-2.997 2.553-4.4h.221c1.372 0 2.215-.549 2.68-1.009.309-.293.55-.65.707-1.046l.098-.288Z"
|
|
94160
94310
|
},
|
|
94161
|
-
|
|
94162
|
-
|
|
94163
|
-
|
|
94164
|
-
nodeKind: "security-node",
|
|
94165
|
-
message: "unfiltered trust-boundary crossing",
|
|
94166
|
-
help: "route this flow through a `security-node` (firewall/WAF/bastion), or confirm the direct path is intended"
|
|
94311
|
+
dotnet: {
|
|
94312
|
+
title: ".NET",
|
|
94313
|
+
d: "M24 8.77h-2.468v7.565h-1.425V8.77h-2.462V7.53H24zm-6.852 7.565h-4.821V7.53h4.63v1.24h-3.205v2.494h2.953v1.234h-2.953v2.604h3.396zm-6.708 0H8.882L4.78 9.863a2.896 2.896 0 0 1-.258-.51h-.036c.032.189.048.592.048 1.21v5.772H3.157V7.53h1.659l3.965 6.32c.167.261.275.442.323.54h.024c-.04-.233-.06-.629-.06-1.185V7.529h1.372zm-8.703-.693a.868.829 0 0 1-.869.829.868.829 0 0 1-.868-.83.868.829 0 0 1 .868-.828.868.829 0 0 1 .869.829Z"
|
|
94167
94314
|
},
|
|
94168
|
-
|
|
94169
|
-
|
|
94170
|
-
|
|
94171
|
-
help: 'add the protocol/encryption on inter-zone flows: `A -> B : "\u2026" (TLS1.3)`'
|
|
94315
|
+
elasticsearch: {
|
|
94316
|
+
title: "Elasticsearch",
|
|
94317
|
+
d: "M13.394 0C8.683 0 4.609 2.716 2.644 6.667h15.641a4.77 4.77 0 0 0 3.073-1.11c.446-.375.864-.785 1.247-1.243l.001-.002A11.974 11.974 0 0 0 13.394 0zM1.804 8.889a12.009 12.009 0 0 0 0 6.222h14.7a3.111 3.111 0 1 0 0-6.222zm.84 8.444C4.61 21.283 8.684 24 13.395 24c3.701 0 7.011-1.677 9.212-4.312l-.001-.002a9.958 9.958 0 0 0-1.247-1.243 4.77 4.77 0 0 0-3.073-1.11z"
|
|
94172
94318
|
},
|
|
94173
|
-
|
|
94174
|
-
|
|
94175
|
-
|
|
94176
|
-
child: "asset",
|
|
94177
|
-
parents: ["trust-zone"],
|
|
94178
|
-
message: "sensitive asset outside any trust zone",
|
|
94179
|
-
help: "move this `asset` inside a `trust-zone`"
|
|
94180
|
-
},
|
|
94181
|
-
{
|
|
94182
|
-
code: "E0218",
|
|
94183
|
-
child: "security-node",
|
|
94184
|
-
parents: ["trust-zone"],
|
|
94185
|
-
message: "security node outside any trust zone",
|
|
94186
|
-
help: "place this `security-node` inside a `trust-zone` (typically the exposed one it protects)"
|
|
94187
|
-
},
|
|
94188
|
-
{
|
|
94189
|
-
code: "E0211",
|
|
94190
|
-
child: "actor",
|
|
94191
|
-
parents: ["actor-group"],
|
|
94192
|
-
message: "actor outside any group",
|
|
94193
|
-
help: "move this `actor` inside an `actor-group`"
|
|
94194
|
-
}
|
|
94195
|
-
],
|
|
94196
|
-
minCounts: [],
|
|
94197
|
-
isolatedWarn: {
|
|
94198
|
-
code: "W0510",
|
|
94199
|
-
kinds: ["asset"],
|
|
94200
|
-
message: "isolated element: no incoming or outgoing flow"
|
|
94319
|
+
express: {
|
|
94320
|
+
title: "Express",
|
|
94321
|
+
d: "M12.262 16.666h1.146l6.975-9.325H19.22zm9.778 1.441v.004l-4.334-5.706-.557.74 4.873 6.682H.945V4.173h9.505l5.026 6.7.574-.772-4.374-5.928h.003l-.719-.945H0v17.544h24zM10.917 8.705a3.8 3.8 0 0 0-1.292-1.183q-.796-.45-1.916-.45c-.746 0-1.37.14-1.906.424a3.76 3.76 0 0 0-1.31 1.12 4.9 4.9 0 0 0-.75 1.581 7.17 7.17 0 0 0 0 3.696c.148.567.402 1.101.75 1.573a3.5 3.5 0 0 0 1.31 1.066q.803.39 1.906.389 1.77 0 2.739-.868.966-.867 1.328-2.457h-1.139q-.271 1.084-.977 1.734-.704.651-1.952.65-.812 0-1.392-.342a3.1 3.1 0 0 1-.957-.869 3.5 3.5 0 0 1-.551-1.182 5 5 0 0 1-.17-1.133 9 9 0 0 0-.015-.286 4.5 4.5 0 0 1 .015-.829c.047-.418.147-.83.296-1.223A3.7 3.7 0 0 1 5.54 9.05a2.9 2.9 0 0 1 .922-.742q.541-.28 1.246-.28c.47 0 .869.093 1.23.28q.541.281.922.742.379.461.587 1.057t.225 1.246H5.625l.004.957h6.182a7.3 7.3 0 0 0-.18-1.924 4.9 4.9 0 0 0-.715-1.68z"
|
|
94201
94322
|
},
|
|
94202
|
-
|
|
94203
|
-
"
|
|
94204
|
-
|
|
94205
|
-
stroke: { color: "#8a8a85", style: "solid", width: 1.3 }
|
|
94206
|
-
},
|
|
94207
|
-
"security-node": {
|
|
94208
|
-
fill: "#fff7e6",
|
|
94209
|
-
stroke: { color: "#c46b2a", style: "solid", width: 1.6 }
|
|
94210
|
-
},
|
|
94211
|
-
asset: {
|
|
94212
|
-
fill: "#ffffff",
|
|
94213
|
-
stroke: { color: "#55606b", style: "solid", width: 1.3 }
|
|
94214
|
-
},
|
|
94215
|
-
"actor-group": {
|
|
94216
|
-
fill: "#eef4fb",
|
|
94217
|
-
stroke: { color: "#7a9cc4", style: "dashed", width: 1.2 }
|
|
94218
|
-
},
|
|
94219
|
-
external: {
|
|
94220
|
-
fill: "#fdecea",
|
|
94221
|
-
stroke: { color: "#d9534f", style: "dashed", width: 1.3 }
|
|
94222
|
-
},
|
|
94223
|
-
actor: {}
|
|
94323
|
+
fastapi: {
|
|
94324
|
+
title: "FastAPI",
|
|
94325
|
+
d: "M12 .0387C5.3729.0384.0003 5.3931 0 11.9988c-.001 6.6066 5.372 11.9628 12 11.9625 6.628.0003 12.001-5.3559 12-11.9625-.0003-6.6057-5.3729-11.9604-12-11.96m-.829 5.4153h7.55l-7.5805 5.3284h5.1828L5.279 18.5436q2.9466-6.5444 5.892-13.0896"
|
|
94224
94326
|
},
|
|
94225
|
-
|
|
94226
|
-
"
|
|
94227
|
-
|
|
94228
|
-
stroke: { color: "#8a8a72", style: "solid", width: 1.3 }
|
|
94229
|
-
},
|
|
94230
|
-
"security-node": {
|
|
94231
|
-
fill: "#2e2717",
|
|
94232
|
-
stroke: { color: "#c46b2a", style: "solid", width: 1.6 }
|
|
94233
|
-
},
|
|
94234
|
-
asset: {
|
|
94235
|
-
fill: "#252a31",
|
|
94236
|
-
stroke: { color: "#6b7885", style: "solid", width: 1.3 }
|
|
94237
|
-
},
|
|
94238
|
-
"actor-group": {
|
|
94239
|
-
fill: "#232a33",
|
|
94240
|
-
stroke: { color: "#5c7fa8", style: "dashed", width: 1.2 }
|
|
94241
|
-
},
|
|
94242
|
-
external: {
|
|
94243
|
-
fill: "#3a2422",
|
|
94244
|
-
stroke: { color: "#c25a54", style: "dashed", width: 1.3 }
|
|
94245
|
-
},
|
|
94246
|
-
actor: {}
|
|
94327
|
+
github: {
|
|
94328
|
+
title: "GitHub",
|
|
94329
|
+
d: "M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
|
|
94247
94330
|
},
|
|
94248
|
-
|
|
94249
|
-
|
|
94250
|
-
|
|
94251
|
-
stroke: { color: "#d9534f", style: "solid", width: 1.4 }
|
|
94252
|
-
},
|
|
94253
|
-
internal: {
|
|
94254
|
-
fill: "#fff4e5",
|
|
94255
|
-
stroke: { color: "#e0a458", style: "solid", width: 1.4 }
|
|
94256
|
-
},
|
|
94257
|
-
restricted: {
|
|
94258
|
-
fill: "#e8f1f8",
|
|
94259
|
-
stroke: { color: "#5b8db8", style: "solid", width: 1.4 }
|
|
94260
|
-
},
|
|
94261
|
-
secret: {
|
|
94262
|
-
fill: "#ece8f5",
|
|
94263
|
-
stroke: { color: "#7a5fae", style: "solid", width: 1.4 }
|
|
94264
|
-
}
|
|
94331
|
+
gitlab: {
|
|
94332
|
+
title: "GitLab",
|
|
94333
|
+
d: "m23.6004 9.5927-.0337-.0862L20.3.9814a.851.851 0 0 0-.3362-.405.8748.8748 0 0 0-.9997.0539.8748.8748 0 0 0-.29.4399l-2.2055 6.748H7.5375l-2.2057-6.748a.8573.8573 0 0 0-.29-.4412.8748.8748 0 0 0-.9997-.0537.8585.8585 0 0 0-.3362.4049L.4332 9.5015l-.0325.0862a6.0657 6.0657 0 0 0 2.0119 7.0105l.0113.0087.03.0213 4.976 3.7264 2.462 1.8633 1.4995 1.1321a1.0085 1.0085 0 0 0 1.2197 0l1.4995-1.1321 2.4619-1.8633 5.006-3.7489.0125-.01a6.0682 6.0682 0 0 0 2.0094-7.003z"
|
|
94265
94334
|
},
|
|
94266
|
-
|
|
94267
|
-
|
|
94268
|
-
|
|
94269
|
-
|
|
94270
|
-
|
|
94271
|
-
|
|
94272
|
-
|
|
94273
|
-
|
|
94274
|
-
|
|
94275
|
-
|
|
94276
|
-
|
|
94277
|
-
|
|
94278
|
-
|
|
94279
|
-
|
|
94280
|
-
|
|
94281
|
-
|
|
94282
|
-
|
|
94335
|
+
go: {
|
|
94336
|
+
title: "Go",
|
|
94337
|
+
d: "M1.811 10.231c-.047 0-.058-.023-.035-.059l.246-.315c.023-.035.081-.058.128-.058h4.172c.046 0 .058.035.035.07l-.199.303c-.023.036-.082.07-.117.07zM.047 11.306c-.047 0-.059-.023-.035-.058l.245-.316c.023-.035.082-.058.129-.058h5.328c.047 0 .07.035.058.07l-.093.28c-.012.047-.058.07-.105.07zm2.828 1.075c-.047 0-.059-.035-.035-.07l.163-.292c.023-.035.07-.07.117-.07h2.337c.047 0 .07.035.07.082l-.023.28c0 .047-.047.082-.082.082zm12.129-2.36c-.736.187-1.239.327-1.963.514-.176.046-.187.058-.34-.117-.174-.199-.303-.327-.548-.444-.737-.362-1.45-.257-2.115.175-.795.514-1.204 1.274-1.192 2.22.011.935.654 1.706 1.577 1.835.795.105 1.46-.175 1.987-.77.105-.13.198-.27.315-.434H10.47c-.245 0-.304-.152-.222-.35.152-.362.432-.97.596-1.274a.315.315 0 01.292-.187h4.253c-.023.316-.023.631-.07.947a4.983 4.983 0 01-.958 2.29c-.841 1.11-1.94 1.8-3.33 1.986-1.145.152-2.209-.07-3.143-.77-.865-.655-1.356-1.52-1.484-2.595-.152-1.274.222-2.419.993-3.424.83-1.086 1.928-1.776 3.272-2.02 1.098-.2 2.15-.07 3.096.571.62.41 1.063.97 1.356 1.648.07.105.023.164-.117.2m3.868 6.461c-1.064-.024-2.034-.328-2.852-1.029a3.665 3.665 0 01-1.262-2.255c-.21-1.32.152-2.489.947-3.529.853-1.122 1.881-1.706 3.272-1.95 1.192-.21 2.314-.095 3.33.595.923.63 1.496 1.484 1.648 2.605.198 1.578-.257 2.863-1.344 3.962-.771.783-1.718 1.273-2.805 1.495-.315.06-.63.07-.934.106zm2.78-4.72c-.011-.153-.011-.27-.034-.387-.21-1.157-1.274-1.81-2.384-1.554-1.087.245-1.788.935-2.045 2.033-.21.912.234 1.835 1.075 2.21.643.28 1.285.244 1.905-.07.923-.48 1.425-1.228 1.484-2.233z"
|
|
94338
|
+
},
|
|
94339
|
+
googlecloud: {
|
|
94340
|
+
title: "Google Cloud",
|
|
94341
|
+
d: "M12.19 2.38a9.344 9.344 0 0 0-9.234 6.893c.053-.02-.055.013 0 0-3.875 2.551-3.922 8.11-.247 10.941l.006-.007-.007.03a6.717 6.717 0 0 0 4.077 1.356h5.173l.03.03h5.192c6.687.053 9.376-8.605 3.835-12.35a9.365 9.365 0 0 0-2.821-4.552l-.043.043.006-.05A9.344 9.344 0 0 0 12.19 2.38zm-.358 4.146c1.244-.04 2.518.368 3.486 1.15a5.186 5.186 0 0 1 1.862 4.078v.518c3.53-.07 3.53 5.262 0 5.193h-5.193l-.008.009v-.04H6.785a2.59 2.59 0 0 1-1.067-.23h.001a2.597 2.597 0 1 1 3.437-3.437l3.013-3.012A6.747 6.747 0 0 0 8.11 8.24c.018-.01.04-.026.054-.023a5.186 5.186 0 0 1 3.67-1.69z"
|
|
94342
|
+
},
|
|
94343
|
+
grafana: {
|
|
94344
|
+
title: "Grafana",
|
|
94345
|
+
d: "M23.02 10.59a8.578 8.578 0 0 0-.862-3.034 8.911 8.911 0 0 0-1.789-2.445c.337-1.342-.413-2.505-.413-2.505-1.292-.08-2.113.4-2.416.62-.052-.02-.102-.044-.154-.064-.22-.089-.446-.172-.677-.247-.231-.073-.47-.14-.711-.197a9.867 9.867 0 0 0-.875-.161C14.557.753 12.94 0 12.94 0c-1.804 1.145-2.147 2.744-2.147 2.744l-.018.093c-.098.029-.2.057-.298.088-.138.042-.275.094-.413.143-.138.055-.275.107-.41.166a8.869 8.869 0 0 0-1.557.87l-.063-.029c-2.497-.955-4.716.195-4.716.195-.203 2.658.996 4.33 1.235 4.636a11.608 11.608 0 0 0-.607 2.635C1.636 12.677.953 15.014.953 15.014c1.926 2.214 4.171 2.351 4.171 2.351.003-.002.006-.002.006-.005.285.509.615.994.986 1.446.156.19.32.371.488.548-.704 2.009.099 3.68.099 3.68 2.144.08 3.553-.937 3.849-1.173a9.784 9.784 0 0 0 3.164.501h.08l.055-.003.107-.002.103-.005.003.002c1.01 1.44 2.788 1.646 2.788 1.646 1.264-1.332 1.337-2.653 1.337-2.94v-.058c0-.02-.003-.039-.003-.06.265-.187.52-.387.758-.6a7.875 7.875 0 0 0 1.415-1.7c1.43.083 2.437-.885 2.437-.885-.236-1.49-1.085-2.216-1.264-2.354l-.018-.013-.016-.013a.217.217 0 0 1-.031-.02c.008-.092.016-.18.02-.27.011-.162.016-.323.016-.48v-.253l-.005-.098-.008-.135a1.891 1.891 0 0 0-.01-.13c-.003-.042-.008-.083-.013-.125l-.016-.124-.018-.122a6.215 6.215 0 0 0-2.032-3.73 6.015 6.015 0 0 0-3.222-1.46 6.292 6.292 0 0 0-.85-.048l-.107.002h-.063l-.044.003-.104.008a4.777 4.777 0 0 0-3.335 1.695c-.332.4-.592.84-.768 1.297a4.594 4.594 0 0 0-.312 1.817l.003.091c.005.055.007.11.013.164a3.615 3.615 0 0 0 .698 1.82 3.53 3.53 0 0 0 1.827 1.282c.33.098.66.14.971.137.039 0 .078 0 .114-.002l.063-.003c.02 0 .041-.003.062-.003.034-.002.065-.007.099-.01.007 0 .018-.003.028-.003l.031-.005.06-.008a1.18 1.18 0 0 0 .112-.02c.036-.008.072-.013.109-.024a2.634 2.634 0 0 0 .914-.415c.028-.02.056-.041.085-.065a.248.248 0 0 0 .039-.35.244.244 0 0 0-.309-.06l-.078.042c-.09.044-.184.083-.283.116a2.476 2.476 0 0 1-.475.096c-.028.003-.054.006-.083.006l-.083.002c-.026 0-.054 0-.08-.002l-.102-.006h-.012l-.024.006c-.016-.003-.031-.003-.044-.006-.031-.002-.06-.007-.091-.01a2.59 2.59 0 0 1-.724-.213 2.557 2.557 0 0 1-.667-.438 2.52 2.52 0 0 1-.805-1.475 2.306 2.306 0 0 1-.029-.444l.006-.122v-.023l.002-.031c.003-.021.003-.04.005-.06a3.163 3.163 0 0 1 1.352-2.29 3.12 3.12 0 0 1 .937-.43 2.946 2.946 0 0 1 .776-.101h.06l.07.002.045.003h.026l.07.005a4.041 4.041 0 0 1 1.635.49 3.94 3.94 0 0 1 1.602 1.662 3.77 3.77 0 0 1 .397 1.414l.005.076.003.075c.002.026.002.05.002.075 0 .024.003.052 0 .07v.065l-.002.073-.008.174a6.195 6.195 0 0 1-.08.639 5.1 5.1 0 0 1-.267.927 5.31 5.31 0 0 1-.624 1.13 5.052 5.052 0 0 1-3.237 2.014 4.82 4.82 0 0 1-.649.066l-.039.003h-.287a6.607 6.607 0 0 1-1.716-.265 6.776 6.776 0 0 1-3.4-2.274 6.75 6.75 0 0 1-.746-1.15 6.616 6.616 0 0 1-.714-2.596l-.005-.083-.002-.02v-.056l-.003-.073v-.096l-.003-.104v-.07l.003-.163c.008-.22.026-.45.054-.678a8.707 8.707 0 0 1 .28-1.355c.128-.444.286-.872.473-1.277a7.04 7.04 0 0 1 1.456-2.1 5.925 5.925 0 0 1 .953-.763c.169-.111.343-.213.524-.306.089-.05.182-.091.273-.135.047-.02.093-.042.138-.062a7.177 7.177 0 0 1 .714-.267l.145-.045c.049-.015.098-.026.148-.041.098-.029.197-.052.296-.076.049-.013.1-.02.15-.033l.15-.032.151-.028.076-.013.075-.01.153-.024c.057-.01.114-.013.171-.023l.169-.021c.036-.003.073-.008.106-.01l.073-.008.036-.003.042-.002c.057-.003.114-.008.171-.01l.086-.006h.023l.037-.003.145-.007a7.999 7.999 0 0 1 1.708.125 7.917 7.917 0 0 1 2.048.68 8.253 8.253 0 0 1 1.672 1.09l.09.077.089.078c.06.052.114.107.171.159.057.052.112.106.166.16.052.055.107.107.159.164a8.671 8.671 0 0 1 1.41 1.978c.012.026.028.052.04.078l.04.078.075.156c.023.051.05.1.07.153l.065.15a8.848 8.848 0 0 1 .45 1.34.19.19 0 0 0 .201.142.186.186 0 0 0 .172-.184c.01-.246.002-.532-.024-.856z"
|
|
94346
|
+
},
|
|
94347
|
+
graphql: {
|
|
94348
|
+
title: "GraphQL",
|
|
94349
|
+
d: "M12.002 0a2.138 2.138 0 1 0 0 4.277 2.138 2.138 0 1 0 0-4.277zm8.54 4.931a2.138 2.138 0 1 0 0 4.277 2.138 2.138 0 1 0 0-4.277zm0 9.862a2.138 2.138 0 1 0 0 4.277 2.138 2.138 0 1 0 0-4.277zm-8.54 4.931a2.138 2.138 0 1 0 0 4.276 2.138 2.138 0 1 0 0-4.276zm-8.542-4.93a2.138 2.138 0 1 0 0 4.276 2.138 2.138 0 1 0 0-4.277zm0-9.863a2.138 2.138 0 1 0 0 4.277 2.138 2.138 0 1 0 0-4.277zm8.542-3.378L2.953 6.777v10.448l9.049 5.224 9.047-5.224V6.777zm0 1.601 7.66 13.27H4.34zm-1.387.371L3.97 15.037V7.363zm2.774 0 6.646 3.838v7.674zM5.355 17.44h13.293l-6.646 3.836z"
|
|
94350
|
+
},
|
|
94351
|
+
javascript: {
|
|
94352
|
+
title: "JavaScript",
|
|
94353
|
+
d: "M0 0h24v24H0V0zm22.034 18.276c-.175-1.095-.888-2.015-3.003-2.873-.736-.345-1.554-.585-1.797-1.14-.091-.33-.105-.51-.046-.705.15-.646.915-.84 1.515-.66.39.12.75.42.976.9 1.034-.676 1.034-.676 1.755-1.125-.27-.42-.404-.601-.586-.78-.63-.705-1.469-1.065-2.834-1.034l-.705.089c-.676.165-1.32.525-1.71 1.005-1.14 1.291-.811 3.541.569 4.471 1.365 1.02 3.361 1.244 3.616 2.205.24 1.17-.87 1.545-1.966 1.41-.811-.18-1.26-.586-1.755-1.336l-1.83 1.051c.21.48.45.689.81 1.109 1.74 1.756 6.09 1.666 6.871-1.004.029-.09.24-.705.074-1.65l.046.067zm-8.983-7.245h-2.248c0 1.938-.009 3.864-.009 5.805 0 1.232.063 2.363-.138 2.711-.33.689-1.18.601-1.566.48-.396-.196-.597-.466-.83-.855-.063-.105-.11-.196-.127-.196l-1.825 1.125c.305.63.75 1.172 1.324 1.517.855.51 2.004.675 3.207.405.783-.226 1.458-.691 1.811-1.411.51-.93.402-2.07.397-3.346.012-2.054 0-4.109 0-6.179l.004-.056z",
|
|
94354
|
+
license: "MIT",
|
|
94355
|
+
licenseUrl: "https://spdx.org/licenses/MIT.html",
|
|
94356
|
+
source: "https://github.com/voodootikigod/logo.js/blob/1544bdeed6d618a6cfe4f0650d04ab8d9cfa76d9/js.svg",
|
|
94357
|
+
copyright: "Copyright (c) 2011 Christopher Williams <chris@iterativedesigns.com>"
|
|
94358
|
+
},
|
|
94359
|
+
kotlin: { title: "Kotlin", d: "M24 24H0V0h24L12 12Z" },
|
|
94360
|
+
kubernetes: {
|
|
94361
|
+
title: "Kubernetes",
|
|
94362
|
+
d: "M10.204 14.35l.007.01-.999 2.413a5.171 5.171 0 0 1-2.075-2.597l2.578-.437.004.005a.44.44 0 0 1 .484.606zm-.833-2.129a.44.44 0 0 0 .173-.756l.002-.011L7.585 9.7a5.143 5.143 0 0 0-.73 3.255l2.514-.725.002-.009zm1.145-1.98a.44.44 0 0 0 .699-.337l.01-.005.15-2.62a5.144 5.144 0 0 0-3.01 1.442l2.147 1.523.004-.002zm.76 2.75l.723.349.722-.347.18-.78-.5-.623h-.804l-.5.623.179.779zm1.5-3.095a.44.44 0 0 0 .7.336l.008.003 2.134-1.513a5.188 5.188 0 0 0-2.992-1.442l.148 2.615.002.001zm10.876 5.97l-5.773 7.181a1.6 1.6 0 0 1-1.248.594l-9.261.003a1.6 1.6 0 0 1-1.247-.596l-5.776-7.18a1.583 1.583 0 0 1-.307-1.34L2.1 5.573c.108-.47.425-.864.863-1.073L11.305.513a1.606 1.606 0 0 1 1.385 0l8.345 3.985c.438.209.755.604.863 1.073l2.062 8.955c.108.47-.005.963-.308 1.34zm-3.289-2.057c-.042-.01-.103-.026-.145-.034-.174-.033-.315-.025-.479-.038-.35-.037-.638-.067-.895-.148-.105-.04-.18-.165-.216-.216l-.201-.059a6.45 6.45 0 0 0-.105-2.332 6.465 6.465 0 0 0-.936-2.163c.052-.047.15-.133.177-.159.008-.09.001-.183.094-.282.197-.185.444-.338.743-.522.142-.084.273-.137.415-.242.032-.024.076-.062.11-.089.24-.191.295-.52.123-.736-.172-.216-.506-.236-.745-.045-.034.027-.08.062-.111.088-.134.116-.217.23-.33.35-.246.25-.45.458-.673.609-.097.056-.239.037-.303.033l-.19.135a6.545 6.545 0 0 0-4.146-2.003l-.012-.223c-.065-.062-.143-.115-.163-.25-.022-.268.015-.557.057-.905.023-.163.061-.298.068-.475.001-.04-.001-.099-.001-.142 0-.306-.224-.555-.5-.555-.275 0-.499.249-.499.555l.001.014c0 .041-.002.092 0 .128.006.177.044.312.067.475.042.348.078.637.056.906a.545.545 0 0 1-.162.258l-.012.211a6.424 6.424 0 0 0-4.166 2.003 8.373 8.373 0 0 1-.18-.128c-.09.012-.18.04-.297-.029-.223-.15-.427-.358-.673-.608-.113-.12-.195-.234-.329-.349-.03-.026-.077-.062-.111-.088a.594.594 0 0 0-.348-.132.481.481 0 0 0-.398.176c-.172.216-.117.546.123.737l.007.005.104.083c.142.105.272.159.414.242.299.185.546.338.743.522.076.082.09.226.1.288l.16.143a6.462 6.462 0 0 0-1.02 4.506l-.208.06c-.055.072-.133.184-.215.217-.257.081-.546.11-.895.147-.164.014-.305.006-.48.039-.037.007-.09.02-.133.03l-.004.002-.007.002c-.295.071-.484.342-.423.608.061.267.349.429.645.365l.007-.001.01-.003.129-.029c.17-.046.294-.113.448-.172.33-.118.604-.217.87-.256.112-.009.23.069.288.101l.217-.037a6.5 6.5 0 0 0 2.88 3.596l-.09.218c.033.084.069.199.044.282-.097.252-.263.517-.452.813-.091.136-.185.242-.268.399-.02.037-.045.095-.064.134-.128.275-.034.591.213.71.248.12.556-.007.69-.282v-.002c.02-.039.046-.09.062-.127.07-.162.094-.301.144-.458.132-.332.205-.68.387-.897.05-.06.13-.082.215-.105l.113-.205a6.453 6.453 0 0 0 4.609.012l.106.192c.086.028.18.042.256.155.136.232.229.507.342.84.05.156.074.295.145.457.016.037.043.09.062.129.133.276.442.402.69.282.247-.118.341-.435.213-.71-.02-.039-.045-.096-.065-.134-.083-.156-.177-.261-.268-.398-.19-.296-.346-.541-.443-.793-.04-.13.007-.21.038-.294-.018-.022-.059-.144-.083-.202a6.499 6.499 0 0 0 2.88-3.622c.064.01.176.03.213.038.075-.05.144-.114.28-.104.266.039.54.138.87.256.154.06.277.128.448.173.036.01.088.019.13.028l.009.003.007.001c.297.064.584-.098.645-.365.06-.266-.128-.537-.423-.608zM16.4 9.701l-1.95 1.746v.005a.44.44 0 0 0 .173.757l.003.01 2.526.728a5.199 5.199 0 0 0-.108-1.674A5.208 5.208 0 0 0 16.4 9.7zm-4.013 5.325a.437.437 0 0 0-.404-.232.44.44 0 0 0-.372.233h-.002l-1.268 2.292a5.164 5.164 0 0 0 3.326.003l-1.27-2.296h-.01zm1.888-1.293a.44.44 0 0 0-.27.036.44.44 0 0 0-.214.572l-.003.004 1.01 2.438a5.15 5.15 0 0 0 2.081-2.615l-2.6-.44-.004.005z"
|
|
94363
|
+
},
|
|
94364
|
+
mongodb: {
|
|
94365
|
+
title: "MongoDB",
|
|
94366
|
+
d: "M17.193 9.555c-1.264-5.58-4.252-7.414-4.573-8.115-.28-.394-.53-.954-.735-1.44-.036.495-.055.685-.523 1.184-.723.566-4.438 3.682-4.74 10.02-.282 5.912 4.27 9.435 4.888 9.884l.07.05A73.49 73.49 0 0111.91 24h.481c.114-1.032.284-2.056.51-3.07.417-.296.604-.463.85-.693a11.342 11.342 0 003.639-8.464c.01-.814-.103-1.662-.197-2.218zm-5.336 8.195s0-8.291.275-8.29c.213 0 .49 10.695.49 10.695-.381-.045-.765-1.76-.765-2.405z"
|
|
94367
|
+
},
|
|
94368
|
+
mysql: {
|
|
94369
|
+
title: "MySQL",
|
|
94370
|
+
d: "M16.405 5.501c-.115 0-.193.014-.274.033v.013h.014c.054.104.146.18.214.273.054.107.1.214.154.32l.014-.015c.094-.066.14-.172.14-.333-.04-.047-.046-.094-.08-.14-.04-.067-.126-.1-.18-.153zM5.77 18.695h-.927a50.854 50.854 0 00-.27-4.41h-.008l-1.41 4.41H2.45l-1.4-4.41h-.01a72.892 72.892 0 00-.195 4.41H0c.055-1.966.192-3.81.41-5.53h1.15l1.335 4.064h.008l1.347-4.064h1.095c.242 2.015.384 3.86.428 5.53zm4.017-4.08c-.378 2.045-.876 3.533-1.492 4.46-.482.716-1.01 1.073-1.583 1.073-.153 0-.34-.046-.566-.138v-.494c.11.017.24.026.386.026.268 0 .483-.075.647-.222.197-.18.295-.382.295-.605 0-.155-.077-.47-.23-.944L6.23 14.615h.91l.727 2.36c.164.536.233.91.205 1.123.4-1.064.678-2.227.835-3.483zm12.325 4.08h-2.63v-5.53h.885v4.85h1.745zm-3.32.135l-1.016-.5c.09-.076.177-.158.255-.25.433-.506.648-1.258.648-2.253 0-1.83-.718-2.746-2.155-2.746-.704 0-1.254.232-1.65.697-.43.508-.646 1.256-.646 2.245 0 .972.19 1.686.574 2.14.35.41.877.615 1.583.615.264 0 .506-.033.725-.098l1.325.772.36-.622zM15.5 17.588c-.225-.36-.337-.94-.337-1.736 0-1.393.424-2.09 1.27-2.09.443 0 .77.167.977.5.224.362.336.936.336 1.723 0 1.404-.424 2.108-1.27 2.108-.445 0-.77-.167-.978-.5zm-1.658-.425c0 .47-.172.856-.516 1.156-.344.3-.803.45-1.384.45-.543 0-1.064-.172-1.573-.515l.237-.476c.438.22.833.328 1.19.328.332 0 .593-.073.783-.22a.754.754 0 00.3-.615c0-.33-.23-.61-.648-.845-.388-.213-1.163-.657-1.163-.657-.422-.307-.632-.636-.632-1.177 0-.45.157-.81.47-1.085.315-.278.72-.415 1.22-.415.512 0 .98.136 1.4.41l-.213.476a2.726 2.726 0 00-1.064-.23c-.283 0-.502.068-.654.206a.685.685 0 00-.248.524c0 .328.234.61.666.85.393.215 1.187.67 1.187.67.433.305.648.63.648 1.168zm9.382-5.852c-.535-.014-.95.04-1.297.188-.1.04-.26.04-.274.167.055.053.063.14.11.214.08.134.218.313.346.407.14.11.28.216.427.31.26.16.555.255.81.416.145.094.293.213.44.313.073.05.12.14.214.172v-.02c-.046-.06-.06-.147-.105-.214-.067-.067-.134-.127-.2-.193a3.223 3.223 0 00-.695-.675c-.214-.146-.682-.35-.77-.595l-.013-.014c.146-.013.32-.066.46-.106.227-.06.435-.047.67-.106.106-.027.213-.06.32-.094v-.06c-.12-.12-.21-.283-.334-.395a8.867 8.867 0 00-1.104-.823c-.21-.134-.476-.22-.697-.334-.08-.04-.214-.06-.26-.127-.12-.146-.19-.34-.275-.514a17.69 17.69 0 01-.547-1.163c-.12-.262-.193-.523-.34-.763-.69-1.137-1.437-1.826-2.586-2.5-.247-.14-.543-.2-.856-.274-.167-.008-.334-.02-.5-.027-.11-.047-.216-.174-.31-.235-.38-.24-1.364-.76-1.644-.072-.18.434.267.862.422 1.082.115.153.26.328.34.5.047.116.06.235.107.356.106.294.207.622.347.897.073.14.153.287.247.413.054.073.146.107.167.227-.094.136-.1.334-.154.5-.24.757-.146 1.693.194 2.25.107.166.362.534.703.393.3-.12.234-.5.32-.835.02-.08.007-.133.048-.187v.015c.094.188.188.367.274.555.206.328.566.668.867.895.16.12.287.328.487.402v-.02h-.015c-.043-.058-.1-.086-.154-.133a3.445 3.445 0 01-.35-.4 8.76 8.76 0 01-.747-1.218c-.11-.21-.202-.436-.29-.643-.04-.08-.04-.2-.107-.24-.1.146-.247.273-.32.453-.127.288-.14.642-.188 1.01-.027.007-.014 0-.027.014-.214-.052-.287-.274-.367-.46-.2-.475-.233-1.238-.06-1.785.047-.14.247-.582.167-.716-.042-.127-.174-.2-.247-.303a2.478 2.478 0 01-.24-.427c-.16-.374-.24-.788-.414-1.162-.08-.173-.22-.354-.334-.513-.127-.18-.267-.307-.368-.52-.033-.073-.08-.194-.027-.274.014-.054.042-.075.094-.09.088-.072.335.022.422.062.247.1.455.194.662.334.094.066.195.193.315.226h.14c.214.047.455.014.655.073.355.114.675.28.962.46a5.953 5.953 0 012.085 2.286c.08.154.115.295.188.455.14.33.313.663.455.982.14.315.275.636.476.897.1.14.502.213.682.286.133.06.34.115.46.188.23.14.454.3.67.454.11.076.443.243.463.378z"
|
|
94371
|
+
},
|
|
94372
|
+
natsdotio: {
|
|
94373
|
+
title: "NATS.io",
|
|
94374
|
+
d: "M12.004 0H.404v18.807h9.938l1.714 1.602v-.026L15.966 24v-5.193h7.63V0H12.003zm7.578 14.45H15.38L6.898 6.519v7.93H4.116V4.376h4.349l8.344 7.784V4.375h2.773V14.45z"
|
|
94375
|
+
},
|
|
94376
|
+
nextdotjs: {
|
|
94377
|
+
title: "Next.js",
|
|
94378
|
+
d: "M18.665 21.978C16.758 23.255 14.465 24 12 24 5.377 24 0 18.623 0 12S5.377 0 12 0s12 5.377 12 12c0 3.583-1.574 6.801-4.067 9.001L9.219 7.2H7.2v9.596h1.615V9.251l9.85 12.727Zm-3.332-8.533 1.6 2.061V7.2h-1.6v6.245Z"
|
|
94379
|
+
},
|
|
94380
|
+
nginx: {
|
|
94381
|
+
title: "NGINX",
|
|
94382
|
+
d: "M12 0L1.605 6v12L12 24l10.395-6V6L12 0zm6 16.59c0 .705-.646 1.29-1.529 1.29-.631 0-1.351-.255-1.801-.81l-6-7.141v6.66c0 .721-.57 1.29-1.274 1.29H7.32c-.721 0-1.29-.6-1.29-1.29V7.41c0-.705.63-1.29 1.5-1.29.646 0 1.38.255 1.83.81l5.97 7.141V7.41c0-.721.6-1.29 1.29-1.29h.075c.72 0 1.29.6 1.29 1.29v9.18H18z"
|
|
94383
|
+
},
|
|
94384
|
+
nodedotjs: {
|
|
94385
|
+
title: "Node.js",
|
|
94386
|
+
d: "M11.998,24c-0.321,0-0.641-0.084-0.922-0.247l-2.936-1.737c-0.438-0.245-0.224-0.332-0.08-0.383 c0.585-0.203,0.703-0.25,1.328-0.604c0.065-0.037,0.151-0.023,0.218,0.017l2.256,1.339c0.082,0.045,0.197,0.045,0.272,0l8.795-5.076 c0.082-0.047,0.134-0.141,0.134-0.238V6.921c0-0.099-0.053-0.192-0.137-0.242l-8.791-5.072c-0.081-0.047-0.189-0.047-0.271,0 L3.075,6.68C2.99,6.729,2.936,6.825,2.936,6.921v10.15c0,0.097,0.054,0.189,0.139,0.235l2.409,1.392 c1.307,0.654,2.108-0.116,2.108-0.89V7.787c0-0.142,0.114-0.253,0.256-0.253h1.115c0.139,0,0.255,0.112,0.255,0.253v10.021 c0,1.745-0.95,2.745-2.604,2.745c-0.508,0-0.909,0-2.026-0.551L2.28,18.675c-0.57-0.329-0.922-0.945-0.922-1.604V6.921 c0-0.659,0.353-1.275,0.922-1.603l8.795-5.082c0.557-0.315,1.296-0.315,1.848,0l8.794,5.082c0.57,0.329,0.924,0.944,0.924,1.603 v10.15c0,0.659-0.354,1.273-0.924,1.604l-8.794,5.078C12.643,23.916,12.324,24,11.998,24z M19.099,13.993 c0-1.9-1.284-2.406-3.987-2.763c-2.731-0.361-3.009-0.548-3.009-1.187c0-0.528,0.235-1.233,2.258-1.233 c1.807,0,2.473,0.389,2.747,1.607c0.024,0.115,0.129,0.199,0.247,0.199h1.141c0.071,0,0.138-0.031,0.186-0.081 c0.048-0.054,0.074-0.123,0.067-0.196c-0.177-2.098-1.571-3.076-4.388-3.076c-2.508,0-4.004,1.058-4.004,2.833 c0,1.925,1.488,2.457,3.895,2.695c2.88,0.282,3.103,0.703,3.103,1.269c0,0.983-0.789,1.402-2.642,1.402 c-2.327,0-2.839-0.584-3.011-1.742c-0.02-0.124-0.126-0.215-0.253-0.215h-1.137c-0.141,0-0.254,0.112-0.254,0.253 c0,1.482,0.806,3.248,4.655,3.248C17.501,17.007,19.099,15.91,19.099,13.993z"
|
|
94387
|
+
},
|
|
94388
|
+
openjdk: {
|
|
94389
|
+
title: "OpenJDK",
|
|
94390
|
+
d: "M11.915 0 11.7.215C9.515 2.4 7.47 6.39 6.046 10.483c-1.064 1.024-3.633 2.81-3.711 3.551-.093.87 1.746 2.611 1.55 3.235-.198.625-1.304 1.408-1.014 1.939.1.188.823.011 1.277-.491a13.389 13.389 0 0 0-.017 2.14c.076.906.27 1.668.643 2.232.372.563.956.911 1.667.911.397 0 .727-.114 1.024-.264.298-.149.571-.33.91-.5.68-.34 1.634-.666 3.53-.604 1.903.062 2.872.39 3.559.704.687.314 1.15.664 1.925.664.767 0 1.395-.336 1.807-.9.412-.563.631-1.33.72-2.24.06-.623.055-1.32 0-2.066.454.45 1.117.604 1.213.424.29-.53-.816-1.314-1.013-1.937-.198-.624 1.642-2.366 1.549-3.236-.08-.748-2.707-2.568-3.748-3.586C16.428 6.374 14.308 2.394 12.13.215zm.175 6.038a2.95 2.95 0 0 1 2.943 2.942 2.95 2.95 0 0 1-2.943 2.943A2.95 2.95 0 0 1 9.148 8.98a2.95 2.95 0 0 1 2.942-2.942zM8.685 7.983a3.515 3.515 0 0 0-.145.997c0 1.951 1.6 3.55 3.55 3.55 1.95 0 3.55-1.598 3.55-3.55 0-.329-.046-.648-.132-.951.334.095.64.208.915.336a42.699 42.699 0 0 1 2.042 5.829c.678 2.545 1.01 4.92.846 6.607-.082.844-.29 1.51-.606 1.94-.315.431-.713.651-1.315.651-.593 0-.932-.27-1.673-.61-.741-.338-1.825-.694-3.792-.758-1.974-.064-3.073.293-3.821.669-.375.188-.659.373-.911.5s-.466.2-.752.2c-.53 0-.876-.209-1.16-.64-.285-.43-.474-1.101-.545-1.948-.141-1.693.176-4.069.823-6.614a43.155 43.155 0 0 1 1.934-5.783c.348-.167.749-.31 1.192-.425zm-3.382 4.362a.216.216 0 0 1 .13.031c-.166.56-.323 1.116-.463 1.665a33.849 33.849 0 0 0-.547 2.555 3.9 3.9 0 0 0-.2-.39c-.58-1.012-.914-1.642-1.16-2.08.315-.24 1.679-1.755 2.24-1.781zm13.394.01c.562.027 1.926 1.543 2.24 1.783-.246.438-.58 1.068-1.16 2.08a4.428 4.428 0 0 0-.163.309 32.354 32.354 0 0 0-.562-2.49 40.579 40.579 0 0 0-.482-1.652.216.216 0 0 1 .127-.03z",
|
|
94391
|
+
license: "BSD-3-Clause",
|
|
94392
|
+
licenseUrl: "https://spdx.org/licenses/BSD-3-Clause.html",
|
|
94393
|
+
source: "https://github.com/openjdk/duke/blob/master/vector/Agent.svg"
|
|
94394
|
+
},
|
|
94395
|
+
postgresql: {
|
|
94396
|
+
title: "PostgreSQL",
|
|
94397
|
+
d: "M23.5594 14.7228a.5269.5269 0 0 0-.0563-.1191c-.139-.2632-.4768-.3418-1.0074-.2321-1.6533.3411-2.2935.1312-2.5256-.0191 1.342-2.0482 2.445-4.522 3.0411-6.8297.2714-1.0507.7982-3.5237.1222-4.7316a1.5641 1.5641 0 0 0-.1509-.235C21.6931.9086 19.8007.0248 17.5099.0005c-1.4947-.0158-2.7705.3461-3.1161.4794a9.449 9.449 0 0 0-.5159-.0816 8.044 8.044 0 0 0-1.3114-.1278c-1.1822-.0184-2.2038.2642-3.0498.8406-.8573-.3211-4.7888-1.645-7.2219.0788C.9359 2.1526.3086 3.8733.4302 6.3043c.0409.818.5069 3.334 1.2423 5.7436.4598 1.5065.9387 2.7019 1.4334 3.582.553.9942 1.1259 1.5933 1.7143 1.7895.4474.1491 1.1327.1441 1.8581-.7279.8012-.9635 1.5903-1.8258 1.9446-2.2069.4351.2355.9064.3625 1.39.3772a.0569.0569 0 0 0 .0004.0041 11.0312 11.0312 0 0 0-.2472.3054c-.3389.4302-.4094.5197-1.5002.7443-.3102.064-1.1344.2339-1.1464.8115-.0025.1224.0329.2309.0919.3268.2269.4231.9216.6097 1.015.6331 1.3345.3335 2.5044.092 3.3714-.6787-.017 2.231.0775 4.4174.3454 5.0874.2212.5529.7618 1.9045 2.4692 1.9043.2505 0 .5263-.0291.8296-.0941 1.7819-.3821 2.5557-1.1696 2.855-2.9059.1503-.8707.4016-2.8753.5388-4.1012.0169-.0703.0357-.1207.057-.1362.0007-.0005.0697-.0471.4272.0307a.3673.3673 0 0 0 .0443.0068l.2539.0223.0149.001c.8468.0384 1.9114-.1426 2.5312-.4308.6438-.2988 1.8057-1.0323 1.5951-1.6698zM2.371 11.8765c-.7435-2.4358-1.1779-4.8851-1.2123-5.5719-.1086-2.1714.4171-3.6829 1.5623-4.4927 1.8367-1.2986 4.8398-.5408 6.108-.13-.0032.0032-.0066.0061-.0098.0094-2.0238 2.044-1.9758 5.536-1.9708 5.7495-.0002.0823.0066.1989.0162.3593.0348.5873.0996 1.6804-.0735 2.9184-.1609 1.1504.1937 2.2764.9728 3.0892.0806.0841.1648.1631.2518.2374-.3468.3714-1.1004 1.1926-1.9025 2.1576-.5677.6825-.9597.5517-1.0886.5087-.3919-.1307-.813-.5871-1.2381-1.3223-.4796-.839-.9635-2.0317-1.4155-3.5126zm6.0072 5.0871c-.1711-.0428-.3271-.1132-.4322-.1772.0889-.0394.2374-.0902.4833-.1409 1.2833-.2641 1.4815-.4506 1.9143-1.0002.0992-.126.2116-.2687.3673-.4426a.3549.3549 0 0 0 .0737-.1298c.1708-.1513.2724-.1099.4369-.0417.156.0646.3078.26.3695.4752.0291.1016.0619.2945-.0452.4444-.9043 1.2658-2.2216 1.2494-3.1676 1.0128zm2.094-3.988-.0525.141c-.133.3566-.2567.6881-.3334 1.003-.6674-.0021-1.3168-.2872-1.8105-.8024-.6279-.6551-.9131-1.5664-.7825-2.5004.1828-1.3079.1153-2.4468.079-3.0586-.005-.0857-.0095-.1607-.0122-.2199.2957-.2621 1.6659-.9962 2.6429-.7724.4459.1022.7176.4057.8305.928.5846 2.7038.0774 3.8307-.3302 4.7363-.084.1866-.1633.3629-.2311.5454zm7.3637 4.5725c-.0169.1768-.0358.376-.0618.5959l-.146.4383a.3547.3547 0 0 0-.0182.1077c-.0059.4747-.054.6489-.115.8693-.0634.2292-.1353.4891-.1794 1.0575-.11 1.4143-.8782 2.2267-2.4172 2.5565-1.5155.3251-1.7843-.4968-2.0212-1.2217a6.5824 6.5824 0 0 0-.0769-.2266c-.2154-.5858-.1911-1.4119-.1574-2.5551.0165-.5612-.0249-1.9013-.3302-2.6462.0044-.2932.0106-.5909.019-.8918a.3529.3529 0 0 0-.0153-.1126 1.4927 1.4927 0 0 0-.0439-.208c-.1226-.4283-.4213-.7866-.7797-.9351-.1424-.059-.4038-.1672-.7178-.0869.067-.276.1831-.5875.309-.9249l.0529-.142c.0595-.16.134-.3257.213-.5012.4265-.9476 1.0106-2.2453.3766-5.1772-.2374-1.0981-1.0304-1.6343-2.2324-1.5098-.7207.0746-1.3799.3654-1.7088.5321a5.6716 5.6716 0 0 0-.1958.1041c.0918-1.1064.4386-3.1741 1.7357-4.4823a4.0306 4.0306 0 0 1 .3033-.276.3532.3532 0 0 0 .1447-.0644c.7524-.5706 1.6945-.8506 2.802-.8325.4091.0067.8017.0339 1.1742.081 1.939.3544 3.2439 1.4468 4.0359 2.3827.8143.9623 1.2552 1.9315 1.4312 2.4543-1.3232-.1346-2.2234.1268-2.6797.779-.9926 1.4189.543 4.1729 1.2811 5.4964.1353.2426.2522.4522.2889.5413.2403.5825.5515.9713.7787 1.2552.0696.087.1372.1714.1885.245-.4008.1155-1.1208.3825-1.0552 1.717-.0123.1563-.0423.4469-.0834.8148-.0461.2077-.0702.4603-.0994.7662zm.8905-1.6211c-.0405-.8316.2691-.9185.5967-1.0105a2.8566 2.8566 0 0 0 .135-.0406 1.202 1.202 0 0 0 .1342.103c.5703.3765 1.5823.4213 3.0068.1344-.2016.1769-.5189.3994-.9533.6011-.4098.1903-1.0957.333-1.7473.3636-.7197.0336-1.0859-.0807-1.1721-.151zm.5695-9.2712c-.0059.3508-.0542.6692-.1054 1.0017-.055.3576-.112.7274-.1264 1.1762-.0142.4368.0404.8909.0932 1.3301.1066.887.216 1.8003-.2075 2.7014a3.5272 3.5272 0 0 1-.1876-.3856c-.0527-.1276-.1669-.3326-.3251-.6162-.6156-1.1041-2.0574-3.6896-1.3193-4.7446.3795-.5427 1.3408-.5661 2.1781-.463zm.2284 7.0137a12.3762 12.3762 0 0 0-.0853-.1074l-.0355-.0444c.7262-1.1995.5842-2.3862.4578-3.4385-.0519-.4318-.1009-.8396-.0885-1.2226.0129-.4061.0666-.7543.1185-1.0911.0639-.415.1288-.8443.1109-1.3505.0134-.0531.0188-.1158.0118-.1902-.0457-.4855-.5999-1.938-1.7294-3.253-.6076-.7073-1.4896-1.4972-2.6889-2.0395.5251-.1066 1.2328-.2035 2.0244-.1859 2.0515.0456 3.6746.8135 4.8242 2.2824a.908.908 0 0 1 .0667.1002c.7231 1.3556-.2762 6.2751-2.9867 10.5405zm-8.8166-6.1162c-.025.1794-.3089.4225-.6211.4225a.5821.5821 0 0 1-.0809-.0056c-.1873-.026-.3765-.144-.5059-.3156-.0458-.0605-.1203-.178-.1055-.2844.0055-.0401.0261-.0985.0925-.1488.1182-.0894.3518-.1226.6096-.0867.3163.0441.6426.1938.6113.4186zm7.9305-.4114c.0111.0792-.049.201-.1531.3102-.0683.0717-.212.1961-.4079.2232a.5456.5456 0 0 1-.075.0052c-.2935 0-.5414-.2344-.5607-.3717-.024-.1765.2641-.3106.5611-.352.297-.0414.6111.0088.6356.1851z"
|
|
94398
|
+
},
|
|
94399
|
+
prometheus: {
|
|
94400
|
+
title: "Prometheus",
|
|
94401
|
+
d: "M12 0C5.373 0 0 5.372 0 12c0 6.627 5.373 12 12 12s12-5.373 12-12c0-6.628-5.373-12-12-12zm0 22.46c-1.885 0-3.414-1.26-3.414-2.814h6.828c0 1.553-1.528 2.813-3.414 2.813zm5.64-3.745H6.36v-2.046h11.28v2.046zm-.04-3.098H6.391c-.037-.043-.075-.086-.111-.13-1.155-1.401-1.427-2.133-1.69-2.879-.005-.025 1.4.287 2.395.511 0 0 .513.119 1.262.255-.72-.843-1.147-1.915-1.147-3.01 0-2.406 1.845-4.508 1.18-6.207.648.053 1.34 1.367 1.387 3.422.689-.951.977-2.69.977-3.755 0-1.103.727-2.385 1.454-2.429-.648 1.069.168 1.984.894 4.256.272.854.237 2.29.447 3.201.07-1.892.395-4.652 1.595-5.605-.529 1.2.079 2.702.494 3.424.671 1.164 1.078 2.047 1.078 3.716a4.642 4.642 0 01-1.11 2.996c.792-.149 1.34-.283 1.34-.283l2.573-.502s-.374 1.538-1.81 3.019z"
|
|
94402
|
+
},
|
|
94403
|
+
python: {
|
|
94404
|
+
title: "Python",
|
|
94405
|
+
d: "M14.25.18l.9.2.73.26.59.3.45.32.34.34.25.34.16.33.1.3.04.26.02.2-.01.13V8.5l-.05.63-.13.55-.21.46-.26.38-.3.31-.33.25-.35.19-.35.14-.33.1-.3.07-.26.04-.21.02H8.77l-.69.05-.59.14-.5.22-.41.27-.33.32-.27.35-.2.36-.15.37-.1.35-.07.32-.04.27-.02.21v3.06H3.17l-.21-.03-.28-.07-.32-.12-.35-.18-.36-.26-.36-.36-.35-.46-.32-.59-.28-.73-.21-.88-.14-1.05-.05-1.23.06-1.22.16-1.04.24-.87.32-.71.36-.57.4-.44.42-.33.42-.24.4-.16.36-.1.32-.05.24-.01h.16l.06.01h8.16v-.83H6.18l-.01-2.75-.02-.37.05-.34.11-.31.17-.28.25-.26.31-.23.38-.2.44-.18.51-.15.58-.12.64-.1.71-.06.77-.04.84-.02 1.27.05zm-6.3 1.98l-.23.33-.08.41.08.41.23.34.33.22.41.09.41-.09.33-.22.23-.34.08-.41-.08-.41-.23-.33-.33-.22-.41-.09-.41.09zm13.09 3.95l.28.06.32.12.35.18.36.27.36.35.35.47.32.59.28.73.21.88.14 1.04.05 1.23-.06 1.23-.16 1.04-.24.86-.32.71-.36.57-.4.45-.42.33-.42.24-.4.16-.36.09-.32.05-.24.02-.16-.01h-8.22v.82h5.84l.01 2.76.02.36-.05.34-.11.31-.17.29-.25.25-.31.24-.38.2-.44.17-.51.15-.58.13-.64.09-.71.07-.77.04-.84.01-1.27-.04-1.07-.14-.9-.2-.73-.25-.59-.3-.45-.33-.34-.34-.25-.34-.16-.33-.1-.3-.04-.25-.02-.2.01-.13v-5.34l.05-.64.13-.54.21-.46.26-.38.3-.32.33-.24.35-.2.35-.14.33-.1.3-.06.26-.04.21-.02.13-.01h5.84l.69-.05.59-.14.5-.21.41-.28.33-.32.27-.35.2-.36.15-.36.1-.35.07-.32.04-.28.02-.21V6.07h2.09l.14.01zm-6.47 14.25l-.23.33-.08.41.08.41.23.33.33.23.41.08.41-.08.33-.23.23-.33.08-.41-.08-.41-.23-.33-.33-.23-.41-.08-.41.08z"
|
|
94406
|
+
},
|
|
94407
|
+
rabbitmq: {
|
|
94408
|
+
title: "RabbitMQ",
|
|
94409
|
+
d: "M23.035 9.601h-7.677a.956.956 0 01-.962-.962V.962a.956.956 0 00-.962-.956H10.56a.956.956 0 00-.962.956V8.64a.956.956 0 01-.962.962H5.762a.956.956 0 01-.961-.962V.962A.956.956 0 003.839 0H.959a.956.956 0 00-.956.962v22.076A.956.956 0 00.965 24h22.07a.956.956 0 00.962-.962V10.58a.956.956 0 00-.962-.98zm-3.86 8.152a1.437 1.437 0 01-1.437 1.443h-1.924a1.437 1.437 0 01-1.436-1.443v-1.917a1.437 1.437 0 011.436-1.443h1.924a1.437 1.437 0 011.437 1.443z"
|
|
94410
|
+
},
|
|
94411
|
+
react: {
|
|
94412
|
+
title: "React",
|
|
94413
|
+
d: "M14.23 12.004a2.236 2.236 0 0 1-2.235 2.236 2.236 2.236 0 0 1-2.236-2.236 2.236 2.236 0 0 1 2.235-2.236 2.236 2.236 0 0 1 2.236 2.236zm2.648-10.69c-1.346 0-3.107.96-4.888 2.622-1.78-1.653-3.542-2.602-4.887-2.602-.41 0-.783.093-1.106.278-1.375.793-1.683 3.264-.973 6.365C1.98 8.917 0 10.42 0 12.004c0 1.59 1.99 3.097 5.043 4.03-.704 3.113-.39 5.588.988 6.38.32.187.69.275 1.102.275 1.345 0 3.107-.96 4.888-2.624 1.78 1.654 3.542 2.603 4.887 2.603.41 0 .783-.09 1.106-.275 1.374-.792 1.683-3.263.973-6.365C22.02 15.096 24 13.59 24 12.004c0-1.59-1.99-3.097-5.043-4.032.704-3.11.39-5.587-.988-6.38-.318-.184-.688-.277-1.092-.278zm-.005 1.09v.006c.225 0 .406.044.558.127.666.382.955 1.835.73 3.704-.054.46-.142.945-.25 1.44-.96-.236-2.006-.417-3.107-.534-.66-.905-1.345-1.727-2.035-2.447 1.592-1.48 3.087-2.292 4.105-2.295zm-9.77.02c1.012 0 2.514.808 4.11 2.28-.686.72-1.37 1.537-2.02 2.442-1.107.117-2.154.298-3.113.538-.112-.49-.195-.964-.254-1.42-.23-1.868.054-3.32.714-3.707.19-.09.4-.127.563-.132zm4.882 3.05c.455.468.91.992 1.36 1.564-.44-.02-.89-.034-1.345-.034-.46 0-.915.01-1.36.034.44-.572.895-1.096 1.345-1.565zM12 8.1c.74 0 1.477.034 2.202.093.406.582.802 1.203 1.183 1.86.372.64.71 1.29 1.018 1.946-.308.655-.646 1.31-1.013 1.95-.38.66-.773 1.288-1.18 1.87-.728.063-1.466.098-2.21.098-.74 0-1.477-.035-2.202-.093-.406-.582-.802-1.204-1.183-1.86-.372-.64-.71-1.29-1.018-1.946.303-.657.646-1.313 1.013-1.954.38-.66.773-1.286 1.18-1.868.728-.064 1.466-.098 2.21-.098zm-3.635.254c-.24.377-.48.763-.704 1.16-.225.39-.435.782-.635 1.174-.265-.656-.49-1.31-.676-1.947.64-.15 1.315-.283 2.015-.386zm7.26 0c.695.103 1.365.23 2.006.387-.18.632-.405 1.282-.66 1.933-.2-.39-.41-.783-.64-1.174-.225-.392-.465-.774-.705-1.146zm3.063.675c.484.15.944.317 1.375.498 1.732.74 2.852 1.708 2.852 2.476-.005.768-1.125 1.74-2.857 2.475-.42.18-.88.342-1.355.493-.28-.958-.646-1.956-1.1-2.98.45-1.017.81-2.01 1.085-2.964zm-13.395.004c.278.96.645 1.957 1.1 2.98-.45 1.017-.812 2.01-1.086 2.964-.484-.15-.944-.318-1.37-.5-1.732-.737-2.852-1.706-2.852-2.474 0-.768 1.12-1.742 2.852-2.476.42-.18.88-.342 1.356-.494zm11.678 4.28c.265.657.49 1.312.676 1.948-.64.157-1.316.29-2.016.39.24-.375.48-.762.705-1.158.225-.39.435-.788.636-1.18zm-9.945.02c.2.392.41.783.64 1.175.23.39.465.772.705 1.143-.695-.102-1.365-.23-2.006-.386.18-.63.406-1.282.66-1.933zM17.92 16.32c.112.493.2.968.254 1.423.23 1.868-.054 3.32-.714 3.708-.147.09-.338.128-.563.128-1.012 0-2.514-.807-4.11-2.28.686-.72 1.37-1.536 2.02-2.44 1.107-.118 2.154-.3 3.113-.54zm-11.83.01c.96.234 2.006.415 3.107.532.66.905 1.345 1.727 2.035 2.446-1.595 1.483-3.092 2.295-4.11 2.295-.22-.005-.406-.05-.553-.132-.666-.38-.955-1.834-.73-3.703.054-.46.142-.944.25-1.438zm4.56.64c.44.02.89.034 1.345.034.46 0 .915-.01 1.36-.034-.44.572-.895 1.095-1.345 1.565-.455-.47-.91-.993-1.36-1.565z"
|
|
94414
|
+
},
|
|
94415
|
+
redis: {
|
|
94416
|
+
title: "Redis",
|
|
94417
|
+
d: "M22.71 13.145c-1.66 2.092-3.452 4.483-7.038 4.483-3.203 0-4.397-2.825-4.48-5.12.701 1.484 2.073 2.685 4.214 2.63 4.117-.133 6.94-3.852 6.94-7.239 0-4.05-3.022-6.972-8.268-6.972-3.752 0-8.4 1.428-11.455 3.685C2.59 6.937 3.885 9.958 4.35 9.626c2.648-1.904 4.748-3.13 6.784-3.744C8.12 9.244.886 17.05 0 18.425c.1 1.261 1.66 4.648 2.424 4.648.232 0 .431-.133.664-.365a100.49 100.49 0 0 0 5.54-6.765c.222 3.104 1.748 6.898 6.014 6.898 3.819 0 7.604-2.756 9.33-8.965.2-.764-.73-1.361-1.261-.73zm-4.349-5.013c0 1.959-1.926 2.922-3.685 2.922-.941 0-1.664-.247-2.235-.568 1.051-1.592 2.092-3.225 3.21-4.973 1.972.334 2.71 1.43 2.71 2.619z"
|
|
94418
|
+
},
|
|
94419
|
+
spring: {
|
|
94420
|
+
title: "Spring",
|
|
94421
|
+
d: "M21.8537 1.4158a10.4504 10.4504 0 0 1-1.284 2.2471A11.9666 11.9666 0 1 0 3.8518 20.7757l.4445.3951a11.9543 11.9543 0 0 0 19.6316-8.2971c.3457-3.0126-.568-6.8649-2.0743-11.458zM5.5805 20.8745a1.0174 1.0174 0 1 1-.1482-1.4323 1.0396 1.0396 0 0 1 .1482 1.4323zm16.1991-3.5806c-2.9385 3.9263-9.2601 2.5928-13.2852 2.7904 0 0-.7161.0494-1.4323.1481 0 0 .2717-.1234.6174-.2469 2.8398-.9877 4.1732-1.1853 5.9018-2.0743 3.2349-1.6545 6.4698-5.2844 7.1118-9.0379-1.2347 3.6053-4.9881 6.7167-8.3959 7.9761-2.3459.8643-6.5685 1.7039-6.5685 1.7039l-.1729-.0988c-2.8645-1.4076-2.9632-7.6304 2.2718-9.6306 2.2966-.889 4.4696-.395 6.9637-.9877 2.6422-.6174 5.7043-2.5929 6.939-5.1857 1.3828 4.1732 3.062 10.643.0493 14.6434z"
|
|
94422
|
+
},
|
|
94423
|
+
sqlite: {
|
|
94424
|
+
title: "SQLite",
|
|
94425
|
+
d: "M21.678.521c-1.032-.92-2.28-.55-3.513.544a8.71 8.71 0 0 0-.547.535c-2.109 2.237-4.066 6.38-4.674 9.544.237.48.422 1.093.544 1.561a13.044 13.044 0 0 1 .164.703s-.019-.071-.096-.296l-.05-.146a1.689 1.689 0 0 0-.033-.08c-.138-.32-.518-.995-.686-1.289-.143.423-.27.818-.376 1.176.484.884.778 2.4.778 2.4s-.025-.099-.147-.442c-.107-.303-.644-1.244-.772-1.464-.217.804-.304 1.346-.226 1.478.152.256.296.698.422 1.186.286 1.1.485 2.44.485 2.44l.017.224a22.41 22.41 0 0 0 .056 2.748c.095 1.146.273 2.13.5 2.657l.155-.084c-.334-1.038-.47-2.399-.41-3.967.09-2.398.642-5.29 1.661-8.304 1.723-4.55 4.113-8.201 6.3-9.945-1.993 1.8-4.692 7.63-5.5 9.788-.904 2.416-1.545 4.684-1.931 6.857.666-2.037 2.821-2.912 2.821-2.912s1.057-1.304 2.292-3.166c-.74.169-1.955.458-2.362.629-.6.251-.762.337-.762.337s1.945-1.184 3.613-1.72C21.695 7.9 24.195 2.767 21.678.521m-18.573.543A1.842 1.842 0 0 0 1.27 2.9v16.608a1.84 1.84 0 0 0 1.835 1.834h9.418a22.953 22.953 0 0 1-.052-2.707c-.006-.062-.011-.141-.016-.2a27.01 27.01 0 0 0-.473-2.378c-.121-.47-.275-.898-.369-1.057-.116-.197-.098-.31-.097-.432 0-.12.015-.245.037-.386a9.98 9.98 0 0 1 .234-1.045l.217-.028c-.017-.035-.014-.065-.031-.097l-.041-.381a32.8 32.8 0 0 1 .382-1.194l.2-.019c-.008-.016-.01-.038-.018-.053l-.043-.316c.63-3.28 2.587-7.443 4.8-9.791.066-.069.133-.128.198-.194Z"
|
|
94426
|
+
},
|
|
94427
|
+
terraform: {
|
|
94428
|
+
title: "Terraform",
|
|
94429
|
+
d: "M1.44 0v7.575l6.561 3.79V3.787zm21.12 4.227l-6.561 3.791v7.574l6.56-3.787zM8.72 4.23v7.575l6.561 3.787V8.018zm0 8.405v7.575L15.28 24v-7.578z"
|
|
94430
|
+
},
|
|
94431
|
+
typescript: {
|
|
94432
|
+
title: "TypeScript",
|
|
94433
|
+
d: "M1.125 0C.502 0 0 .502 0 1.125v21.75C0 23.498.502 24 1.125 24h21.75c.623 0 1.125-.502 1.125-1.125V1.125C24 .502 23.498 0 22.875 0zm17.363 9.75c.612 0 1.154.037 1.627.111a6.38 6.38 0 0 1 1.306.34v2.458a3.95 3.95 0 0 0-.643-.361 5.093 5.093 0 0 0-.717-.26 5.453 5.453 0 0 0-1.426-.2c-.3 0-.573.028-.819.086a2.1 2.1 0 0 0-.623.242c-.17.104-.3.229-.393.374a.888.888 0 0 0-.14.49c0 .196.053.373.156.529.104.156.252.304.443.444s.423.276.696.41c.273.135.582.274.926.416.47.197.892.407 1.266.628.374.222.695.473.963.753.268.279.472.598.614.957.142.359.214.776.214 1.253 0 .657-.125 1.21-.373 1.656a3.033 3.033 0 0 1-1.012 1.085 4.38 4.38 0 0 1-1.487.596c-.566.12-1.163.18-1.79.18a9.916 9.916 0 0 1-1.84-.164 5.544 5.544 0 0 1-1.512-.493v-2.63a5.033 5.033 0 0 0 3.237 1.2c.333 0 .624-.03.872-.09.249-.06.456-.144.623-.25.166-.108.29-.234.373-.38a1.023 1.023 0 0 0-.074-1.089 2.12 2.12 0 0 0-.537-.5 5.597 5.597 0 0 0-.807-.444 27.72 27.72 0 0 0-1.007-.436c-.918-.383-1.602-.852-2.053-1.405-.45-.553-.676-1.222-.676-2.005 0-.614.123-1.141.369-1.582.246-.441.58-.804 1.004-1.089a4.494 4.494 0 0 1 1.47-.629 7.536 7.536 0 0 1 1.77-.201zm-15.113.188h9.563v2.166H9.506v9.646H6.789v-9.646H3.375z"
|
|
94434
|
+
},
|
|
94435
|
+
vault: {
|
|
94436
|
+
title: "Vault",
|
|
94437
|
+
d: "M0 0l11.955 24L24 0zm13.366 4.827h1.393v1.38h-1.393zm-2.77 5.569H9.22V8.993h1.389zm0-2.087H9.22V6.906h1.389zm0-2.086H9.22V4.819h1.389zm2.087 6.263h-1.377V11.08h1.388zm0-2.09h-1.377V8.993h1.388zm0-2.087h-1.377V6.906h1.388zm0-2.086h-1.377V4.819h1.388zm.683.683h1.393v1.389h-1.393zm0 3.475V8.993h1.389v1.388Z"
|
|
94283
94438
|
}
|
|
94284
94439
|
};
|
|
94285
|
-
|
|
94286
|
-
logical: logicalView,
|
|
94287
|
-
application: applicationView,
|
|
94288
|
-
infrastructure: infrastructureView,
|
|
94289
|
-
security: securityView
|
|
94290
|
-
};
|
|
94440
|
+
LOGO_NAMES = Object.keys(LOGOS);
|
|
94291
94441
|
}
|
|
94292
94442
|
});
|
|
94293
94443
|
|
|
@@ -94313,8 +94463,8 @@ function validate(model) {
|
|
|
94313
94463
|
...checkMissingLabels(elements),
|
|
94314
94464
|
...checkNesting(elements, view),
|
|
94315
94465
|
...checkFlows(model, view),
|
|
94316
|
-
...
|
|
94317
|
-
...
|
|
94466
|
+
...checkEndpointRoles(model, view),
|
|
94467
|
+
...checkLogos(elements, view),
|
|
94318
94468
|
...checkBusinessObjects(model, view),
|
|
94319
94469
|
...checkMinimumCounts(elements, model, view),
|
|
94320
94470
|
...checkIsolatedElements(model, view, elements)
|
|
@@ -94356,6 +94506,48 @@ function checkUnknownKinds(elements, view) {
|
|
|
94356
94506
|
}
|
|
94357
94507
|
return diagnostics;
|
|
94358
94508
|
}
|
|
94509
|
+
function checkLogos(elements, view) {
|
|
94510
|
+
const diagnostics = [];
|
|
94511
|
+
const logoKinds = view.logoKinds ?? [];
|
|
94512
|
+
for (const element of elements) {
|
|
94513
|
+
const logo = element.logo;
|
|
94514
|
+
if (!logo) continue;
|
|
94515
|
+
if (!logoKinds.includes(element.kind)) {
|
|
94516
|
+
diagnostics.push({
|
|
94517
|
+
code: "E0108",
|
|
94518
|
+
severity: "error",
|
|
94519
|
+
message: `\`${element.kind}\` does not carry a logo`,
|
|
94520
|
+
span: logo.span,
|
|
94521
|
+
note: logoKinds.length ? `the \`${view.name}\` view accepts \`logo\` on: ${logoKinds.join(", ")}` : `the \`${view.name}\` view has no kinds that carry a logo`
|
|
94522
|
+
});
|
|
94523
|
+
continue;
|
|
94524
|
+
}
|
|
94525
|
+
if (logo.source === "builtin") {
|
|
94526
|
+
if (Object.hasOwn(LOGOS, logo.value)) continue;
|
|
94527
|
+
const suggestion = nearest(logo.value, LOGO_NAMES);
|
|
94528
|
+
diagnostics.push({
|
|
94529
|
+
code: "E0107",
|
|
94530
|
+
severity: "error",
|
|
94531
|
+
message: `unknown built-in logo \`${logo.value}\``,
|
|
94532
|
+
span: logo.span,
|
|
94533
|
+
note: `${LOGO_NAMES.length} built-ins available \u2014 run \`cairn logos\` to list them`,
|
|
94534
|
+
help: suggestion !== void 0 ? `did you mean \`${suggestion}\`?` : `use a file instead: \`logo: "./logos/${logo.value}.svg"\``
|
|
94535
|
+
});
|
|
94536
|
+
continue;
|
|
94537
|
+
}
|
|
94538
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(logo.value) || logo.value.startsWith("//")) {
|
|
94539
|
+
diagnostics.push({
|
|
94540
|
+
code: "E0105",
|
|
94541
|
+
severity: "error",
|
|
94542
|
+
message: `\`logo\` will not fetch \`${logo.value}\``,
|
|
94543
|
+
span: logo.span,
|
|
94544
|
+
note: "a linked logo leaves the diagram depending on a server that can change or vanish",
|
|
94545
|
+
help: 'download it next to the diagram and point at the file: `logo: "./logos/name.svg"`'
|
|
94546
|
+
});
|
|
94547
|
+
}
|
|
94548
|
+
}
|
|
94549
|
+
return diagnostics;
|
|
94550
|
+
}
|
|
94359
94551
|
function checkMissingLabels(elements) {
|
|
94360
94552
|
const diagnostics = [];
|
|
94361
94553
|
for (const element of elements) {
|
|
@@ -94390,6 +94582,42 @@ function checkNesting(elements, view) {
|
|
|
94390
94582
|
}
|
|
94391
94583
|
return diagnostics;
|
|
94392
94584
|
}
|
|
94585
|
+
function checkEndpointRoles(model, view) {
|
|
94586
|
+
const diagnostics = [];
|
|
94587
|
+
const hubKinds = new Set(view.hubKinds ?? []);
|
|
94588
|
+
const isHub = (id) => hubKinds.has(model.index.get(id)?.kind ?? "");
|
|
94589
|
+
for (const flow of model.flows) {
|
|
94590
|
+
for (const [role, hubId, hubSide] of [
|
|
94591
|
+
[flow.fromRole, flow.to, flow.toSide],
|
|
94592
|
+
[flow.toRole, flow.from, flow.fromSide]
|
|
94593
|
+
]) {
|
|
94594
|
+
if (!role) continue;
|
|
94595
|
+
if (!model.index.has(hubId)) continue;
|
|
94596
|
+
if (!isHub(hubId)) {
|
|
94597
|
+
const kinds = [...hubKinds];
|
|
94598
|
+
diagnostics.push({
|
|
94599
|
+
code: "E0224",
|
|
94600
|
+
severity: "error",
|
|
94601
|
+
message: `\`${role.value}\` needs a ${kinds.join(" or ") || "queue"} at the other end of the flow`,
|
|
94602
|
+
span: role.span,
|
|
94603
|
+
note: `\`${hubId}\` is ${model.index.get(hubId)?.kind ? `a \`${model.index.get(hubId).kind}\`` : "not one"}`,
|
|
94604
|
+
help: "a role says what this element does with a queue \u2014 between two ordinary elements the arrow already says it"
|
|
94605
|
+
});
|
|
94606
|
+
continue;
|
|
94607
|
+
}
|
|
94608
|
+
if (hubSide)
|
|
94609
|
+
diagnostics.push({
|
|
94610
|
+
code: "E0225",
|
|
94611
|
+
severity: "error",
|
|
94612
|
+
message: `\`${hubId}.${hubSide.value}\` and \`${role.value}\` both decide where this flow meets the queue`,
|
|
94613
|
+
span: hubSide.span,
|
|
94614
|
+
note: "`producer` means the queue's left cap and `consumer` its right",
|
|
94615
|
+
help: "keep the role and drop the side, or keep the side and drop the role"
|
|
94616
|
+
});
|
|
94617
|
+
}
|
|
94618
|
+
}
|
|
94619
|
+
return diagnostics;
|
|
94620
|
+
}
|
|
94393
94621
|
function checkFlows(model, view) {
|
|
94394
94622
|
const diagnostics = [];
|
|
94395
94623
|
const isActor = (id) => {
|
|
@@ -94447,75 +94675,6 @@ function checkFlows(model, view) {
|
|
|
94447
94675
|
}
|
|
94448
94676
|
return diagnostics;
|
|
94449
94677
|
}
|
|
94450
|
-
function checkElementAttributes(elements, view) {
|
|
94451
|
-
const spec = view.attrSpec;
|
|
94452
|
-
if (!spec) return [];
|
|
94453
|
-
const diagnostics = [];
|
|
94454
|
-
for (const element of elements) {
|
|
94455
|
-
if (element.kind !== spec.kind) continue;
|
|
94456
|
-
if (!element.attr) {
|
|
94457
|
-
diagnostics.push({
|
|
94458
|
-
code: spec.code,
|
|
94459
|
-
severity: "error",
|
|
94460
|
-
message: spec.message + ` (\`${element.id}\`)`,
|
|
94461
|
-
span: element.idSpan,
|
|
94462
|
-
help: spec.help
|
|
94463
|
-
});
|
|
94464
|
-
} else if (!spec.values.includes(element.attr.value)) {
|
|
94465
|
-
const suggestion = nearest(element.attr.value, spec.values);
|
|
94466
|
-
diagnostics.push({
|
|
94467
|
-
code: spec.code,
|
|
94468
|
-
severity: "error",
|
|
94469
|
-
message: `invalid ${spec.kind} value \`${element.attr.value}\` (\`${element.id}\`)`,
|
|
94470
|
-
span: element.attr.span,
|
|
94471
|
-
note: `allowed: ${spec.values.join(", ")}`,
|
|
94472
|
-
help: suggestion ? `did you mean \`${suggestion}\`?` : spec.help
|
|
94473
|
-
});
|
|
94474
|
-
}
|
|
94475
|
-
}
|
|
94476
|
-
return diagnostics;
|
|
94477
|
-
}
|
|
94478
|
-
function checkTrustBoundaries(model, view) {
|
|
94479
|
-
if (!view.boundaryLint && !view.crossZoneTechRecommended) return [];
|
|
94480
|
-
const diagnostics = [];
|
|
94481
|
-
const zoneOf2 = (id) => {
|
|
94482
|
-
for (let ancestor = model.index.get(id)?.parent; ancestor; ancestor = ancestor.parent)
|
|
94483
|
-
if (ancestor.kind === "trust-zone") return ancestor;
|
|
94484
|
-
return void 0;
|
|
94485
|
-
};
|
|
94486
|
-
const trustLevelOf = (id) => {
|
|
94487
|
-
const level = zoneOf2(id)?.attr?.value;
|
|
94488
|
-
return level && view.trustOrder?.[level] !== void 0 ? view.trustOrder[level] : -1;
|
|
94489
|
-
};
|
|
94490
|
-
const lint = view.boundaryLint;
|
|
94491
|
-
const isSecurityNode = (id) => lint !== void 0 && model.index.get(id)?.kind === lint.nodeKind;
|
|
94492
|
-
for (const flow of model.flows) {
|
|
94493
|
-
if (!model.index.has(flow.from) || !model.index.has(flow.to)) continue;
|
|
94494
|
-
const crossesZone = zoneOf2(flow.from) !== zoneOf2(flow.to);
|
|
94495
|
-
const boundaryViolation = lint !== void 0 && trustLevelOf(flow.to) > trustLevelOf(flow.from) && !isSecurityNode(flow.from) && !isSecurityNode(flow.to);
|
|
94496
|
-
if (boundaryViolation) {
|
|
94497
|
-
diagnostics.push({
|
|
94498
|
-
code: lint.code,
|
|
94499
|
-
severity: "warning",
|
|
94500
|
-
message: lint.message,
|
|
94501
|
-
span: flow.span,
|
|
94502
|
-
note: `flow enters a more-trusted zone without passing a \`${lint.nodeKind}\``,
|
|
94503
|
-
help: lint.help
|
|
94504
|
-
});
|
|
94505
|
-
}
|
|
94506
|
-
if (view.crossZoneTechRecommended && crossesZone && !flow.tech?.protocol) {
|
|
94507
|
-
diagnostics.push({
|
|
94508
|
-
code: view.crossZoneTechRecommended.code,
|
|
94509
|
-
severity: "warning",
|
|
94510
|
-
message: view.crossZoneTechRecommended.message,
|
|
94511
|
-
span: flow.span,
|
|
94512
|
-
note: "inter-zone flow \u2014 state how the traffic is protected",
|
|
94513
|
-
help: view.crossZoneTechRecommended.help
|
|
94514
|
-
});
|
|
94515
|
-
}
|
|
94516
|
-
}
|
|
94517
|
-
return diagnostics;
|
|
94518
|
-
}
|
|
94519
94678
|
function checkForbiddenBusinessObjects(model, view) {
|
|
94520
94679
|
const diagnostics = [];
|
|
94521
94680
|
for (const businessObject of model.businessObjects) {
|
|
@@ -94674,6 +94833,7 @@ var init_validator = __esm({
|
|
|
94674
94833
|
"use strict";
|
|
94675
94834
|
init_views();
|
|
94676
94835
|
init_element_tree();
|
|
94836
|
+
init_logos();
|
|
94677
94837
|
}
|
|
94678
94838
|
});
|
|
94679
94839
|
|
|
@@ -94780,13 +94940,15 @@ function wrapText(text, maxChars) {
|
|
|
94780
94940
|
if (currentLine) lines.push(currentLine);
|
|
94781
94941
|
return lines.join("\n");
|
|
94782
94942
|
}
|
|
94783
|
-
var DEFAULT_FONT_SIZE_NODE, FONT_SIZE_BASE, CHAR_WIDTH, fontSizes, measure, CHIP_HEIGHT, chipW, techText, flowLabelBox, nodeSize;
|
|
94943
|
+
var DEFAULT_FONT_SIZE_NODE, FONT_SIZE_BASE, CHAR_WIDTH, GLYPH_GUTTER, LOGO_GUTTER, fontSizes, measure, CHIP_HEIGHT, chipW, techText, flowLabelBox, nodeSize;
|
|
94784
94944
|
var init_text_metrics = __esm({
|
|
94785
94945
|
"src/text-metrics.ts"() {
|
|
94786
94946
|
"use strict";
|
|
94787
94947
|
DEFAULT_FONT_SIZE_NODE = 12.5;
|
|
94788
94948
|
FONT_SIZE_BASE = 12.5;
|
|
94789
94949
|
CHAR_WIDTH = 0.56;
|
|
94950
|
+
GLYPH_GUTTER = 26;
|
|
94951
|
+
LOGO_GUTTER = 30;
|
|
94790
94952
|
fontSizes = (base) => {
|
|
94791
94953
|
const scale = base / FONT_SIZE_BASE;
|
|
94792
94954
|
return {
|
|
@@ -94823,11 +94985,11 @@ var init_text_metrics = __esm({
|
|
|
94823
94985
|
height: measured.height + (tech ? 12 * scale : 0) + (chipNames.length ? CHIP_HEIGHT * scale : 0)
|
|
94824
94986
|
};
|
|
94825
94987
|
};
|
|
94826
|
-
nodeSize = (kind, label, fontSize = DEFAULT_FONT_SIZE_NODE) => {
|
|
94988
|
+
nodeSize = (kind, label, fontSize = DEFAULT_FONT_SIZE_NODE, gutter = 0) => {
|
|
94827
94989
|
const isActor = kind === "actor";
|
|
94828
94990
|
const measured = measure(label, isActor ? fontSize - 1.5 : fontSize);
|
|
94829
94991
|
return {
|
|
94830
|
-
width: isActor ? Math.max(64, measured.width + 8) : Math.max(140, measured.width + 16),
|
|
94992
|
+
width: isActor ? Math.max(64, measured.width + 8) : Math.max(140, measured.width + 16 + gutter),
|
|
94831
94993
|
height: isActor ? 56 + (label.split("\n").length - 1) * 11 : Math.max(46, measured.height + 18)
|
|
94832
94994
|
};
|
|
94833
94995
|
};
|
|
@@ -94835,12 +94997,13 @@ var init_text_metrics = __esm({
|
|
|
94835
94997
|
});
|
|
94836
94998
|
|
|
94837
94999
|
// src/slide-fold.ts
|
|
94838
|
-
function foldStyle(model) {
|
|
95000
|
+
function foldStyle(model, view) {
|
|
94839
95001
|
const businessObjectNames = new Map(model.businessObjects.map((bo) => [bo.id, bo.name]));
|
|
94840
95002
|
const numbered = model.style.flowText === "numbered";
|
|
94841
95003
|
const { edge, node, cont, scale } = fontSizes(model.style.font.size);
|
|
94842
95004
|
return {
|
|
94843
95005
|
numbered,
|
|
95006
|
+
glyphKinds: new Set(view.glyphKinds ?? []),
|
|
94844
95007
|
edge,
|
|
94845
95008
|
node,
|
|
94846
95009
|
cont,
|
|
@@ -94855,7 +95018,15 @@ function foldStyle(model) {
|
|
|
94855
95018
|
})
|
|
94856
95019
|
};
|
|
94857
95020
|
}
|
|
94858
|
-
function
|
|
95021
|
+
function leafSize(element, style) {
|
|
95022
|
+
return nodeSize(
|
|
95023
|
+
element.kind,
|
|
95024
|
+
element.label ?? element.id,
|
|
95025
|
+
style.node,
|
|
95026
|
+
style.glyphKinds.has(element.kind) ? GLYPH_GUTTER : 0
|
|
95027
|
+
);
|
|
95028
|
+
}
|
|
95029
|
+
function toElkNode(element, style) {
|
|
94859
95030
|
if (element.children.length) {
|
|
94860
95031
|
const lineCount = (element.label ?? element.id).split("\n").length;
|
|
94861
95032
|
return {
|
|
@@ -94866,13 +95037,13 @@ function toElkNode(element, containerFontSize, nodeFontSize) {
|
|
|
94866
95037
|
labels: [
|
|
94867
95038
|
{
|
|
94868
95039
|
text: element.label ?? element.id,
|
|
94869
|
-
...measure(element.label ?? element.id,
|
|
95040
|
+
...measure(element.label ?? element.id, style.cont)
|
|
94870
95041
|
}
|
|
94871
95042
|
],
|
|
94872
|
-
children: element.children.map((child) => toElkNode(child,
|
|
95043
|
+
children: element.children.map((child) => toElkNode(child, style))
|
|
94873
95044
|
};
|
|
94874
95045
|
}
|
|
94875
|
-
const size =
|
|
95046
|
+
const size = leafSize(element, style);
|
|
94876
95047
|
return { id: element.id, width: size.width, height: size.height };
|
|
94877
95048
|
}
|
|
94878
95049
|
function walkFoldedNodes(elkNode, offset, elementById, syntheticIds) {
|
|
@@ -94951,7 +95122,7 @@ function internalFlowLabels(flow, style) {
|
|
|
94951
95122
|
];
|
|
94952
95123
|
}
|
|
94953
95124
|
function buildGroupGraph(group, style, fg) {
|
|
94954
|
-
const node = toElkNode(group, style
|
|
95125
|
+
const node = toElkNode(group, style);
|
|
94955
95126
|
const nodeChildren = node.children ??= [];
|
|
94956
95127
|
for (const flow of fg.interFlows) {
|
|
94957
95128
|
if (fg.rootOf.get(flow.from) === group) {
|
|
@@ -95009,7 +95180,7 @@ function buildGroupGraph(group, style, fg) {
|
|
|
95009
95180
|
function layoutColumn(elements, style) {
|
|
95010
95181
|
return elements.map((group) => {
|
|
95011
95182
|
const blocks = group.children.map((child) => {
|
|
95012
|
-
const size =
|
|
95183
|
+
const size = leafSize(child, style);
|
|
95013
95184
|
return { element: child, width: size.width, height: size.height, x: 0, y: 0 };
|
|
95014
95185
|
});
|
|
95015
95186
|
const columnWidth = Math.max(
|
|
@@ -95047,7 +95218,7 @@ function layoutMiddleRows(middles, middleResults, geom, style) {
|
|
|
95047
95218
|
let yCursor = 16 + geom.gutterHeight(0);
|
|
95048
95219
|
middles.forEach((element, index) => {
|
|
95049
95220
|
const result = middleResults.get(element.id) ?? null;
|
|
95050
|
-
const size = result ? { width: result.children[0].width, height: result.children[0].height } :
|
|
95221
|
+
const size = result ? { width: result.children[0].width, height: result.children[0].height } : leafSize(element, style);
|
|
95051
95222
|
rows.push({
|
|
95052
95223
|
element,
|
|
95053
95224
|
box: { x: geom.xMiddle, y: yCursor, width: size.width, height: size.height },
|
|
@@ -95203,7 +95374,7 @@ function connectorLabel(flow, routed, style) {
|
|
|
95203
95374
|
}
|
|
95204
95375
|
async function foldedLayout(model, view, elk) {
|
|
95205
95376
|
const roots = model.elements;
|
|
95206
|
-
const style = foldStyle(model);
|
|
95377
|
+
const style = foldStyle(model, view);
|
|
95207
95378
|
const partitionOf = (element) => view.partitions[element.kind] ?? 1;
|
|
95208
95379
|
const sources = roots.filter((element) => partitionOf(element) === 0);
|
|
95209
95380
|
const middles = roots.filter((element) => partitionOf(element) === 1);
|
|
@@ -95250,7 +95421,7 @@ async function foldedLayout(model, view, elk) {
|
|
|
95250
95421
|
const widthSource = Math.max(0, ...sourceColumns.map((col) => col.width));
|
|
95251
95422
|
const widthMiddle = Math.max(
|
|
95252
95423
|
...middles.map(
|
|
95253
|
-
(element) => element.children.length ? middleResults.get(element.id).children[0].width :
|
|
95424
|
+
(element) => element.children.length ? middleResults.get(element.id).children[0].width : leafSize(element, style).width
|
|
95254
95425
|
)
|
|
95255
95426
|
);
|
|
95256
95427
|
const widthSink = Math.max(0, ...sinkColumns.map((col) => col.width));
|
|
@@ -96158,13 +96329,34 @@ function compactVertical(scene) {
|
|
|
96158
96329
|
}
|
|
96159
96330
|
scene.height = Math.ceil(heightAfter + bottomMargin);
|
|
96160
96331
|
}
|
|
96161
|
-
|
|
96332
|
+
function fitCanvas(scene) {
|
|
96333
|
+
let maxX = 0;
|
|
96334
|
+
let maxY = 0;
|
|
96335
|
+
for (const node of scene.nodes) {
|
|
96336
|
+
maxX = Math.max(maxX, node.x + node.width);
|
|
96337
|
+
maxY = Math.max(maxY, node.y + node.height);
|
|
96338
|
+
}
|
|
96339
|
+
for (const edge of scene.edges) {
|
|
96340
|
+
for (const point of edge.pts) {
|
|
96341
|
+
maxX = Math.max(maxX, point.x);
|
|
96342
|
+
maxY = Math.max(maxY, point.y);
|
|
96343
|
+
}
|
|
96344
|
+
for (const label of edge.labels) {
|
|
96345
|
+
maxX = Math.max(maxX, label.x + label.width);
|
|
96346
|
+
maxY = Math.max(maxY, label.y + label.height);
|
|
96347
|
+
}
|
|
96348
|
+
}
|
|
96349
|
+
scene.width = Math.max(scene.width, Math.ceil(maxX) + CANVAS_MARGIN);
|
|
96350
|
+
scene.height = Math.max(scene.height, Math.ceil(maxY) + CANVAS_MARGIN);
|
|
96351
|
+
}
|
|
96352
|
+
var KEEP_GAP, EDGE_MARGIN, MIN_SAVING, CANVAS_MARGIN;
|
|
96162
96353
|
var init_compact = __esm({
|
|
96163
96354
|
"src/compact.ts"() {
|
|
96164
96355
|
"use strict";
|
|
96165
96356
|
KEEP_GAP = 14;
|
|
96166
96357
|
EDGE_MARGIN = 22;
|
|
96167
96358
|
MIN_SAVING = 4;
|
|
96359
|
+
CANVAS_MARGIN = 10;
|
|
96168
96360
|
}
|
|
96169
96361
|
});
|
|
96170
96362
|
|
|
@@ -96288,8 +96480,7 @@ function labelSeatable(blockers, pts, label) {
|
|
|
96288
96480
|
function scanLabelSeats(ctx, subj, profile) {
|
|
96289
96481
|
for (const label of subj.edge.labels) {
|
|
96290
96482
|
if (!label.width || !label.height) continue;
|
|
96291
|
-
if (!labelSeatable(ctx.blockers, subj.pts, label))
|
|
96292
|
-
profile.set(`unlabelled:${subj.edge.id}`, 1);
|
|
96483
|
+
if (!labelSeatable(ctx.blockers, subj.pts, label)) profile.set(`unlabelled:${subj.edge.id}`, 1);
|
|
96293
96484
|
}
|
|
96294
96485
|
}
|
|
96295
96486
|
function scanArrowRoom(subj, profile) {
|
|
@@ -96362,8 +96553,7 @@ function scanCrossings(a, b, key, profile) {
|
|
|
96362
96553
|
if (!end) continue;
|
|
96363
96554
|
const dx = Math.max(0, end.node.x - hit.x, hit.x - (end.node.x + end.node.width));
|
|
96364
96555
|
const dy = Math.max(0, end.node.y - hit.y, hit.y - (end.node.y + end.node.height));
|
|
96365
|
-
if (dx * dx + dy * dy <= FAN_REACH * FAN_REACH)
|
|
96366
|
-
profile.set(`fan:${key}@${end.node.id}`, 2);
|
|
96556
|
+
if (dx * dx + dy * dy <= FAN_REACH * FAN_REACH) profile.set(`fan:${key}@${end.node.id}`, 2);
|
|
96367
96557
|
}
|
|
96368
96558
|
}
|
|
96369
96559
|
}
|
|
@@ -96391,8 +96581,7 @@ function scanSharedSeats(a, b, key, profile) {
|
|
|
96391
96581
|
}
|
|
96392
96582
|
const side = vertical ? seatA.node.height : seatA.node.width;
|
|
96393
96583
|
const need = Math.min(12, (side - 6) / 2);
|
|
96394
|
-
if (apart < need * 0.8)
|
|
96395
|
-
profile.set(`tight:${key}@${seatA.node.id}|${seatA.side}`, 4);
|
|
96584
|
+
if (apart < need * 0.8) profile.set(`tight:${key}@${seatA.node.id}|${seatA.side}`, 4);
|
|
96396
96585
|
}
|
|
96397
96586
|
}
|
|
96398
96587
|
function scanPairs(ctx, subj, query) {
|
|
@@ -96407,7 +96596,11 @@ function scanPairs(ctx, subj, query) {
|
|
|
96407
96596
|
if (ids.has(other.id) && other.id < subj.edge.id) continue;
|
|
96408
96597
|
const overridden = overrides.has(other.id);
|
|
96409
96598
|
if (farApart(myBounds, ctx.boundsFor(other, theirs, overridden))) continue;
|
|
96410
|
-
const them = {
|
|
96599
|
+
const them = {
|
|
96600
|
+
edge: other,
|
|
96601
|
+
pts: theirs,
|
|
96602
|
+
ends: ctx.endsFor(other, theirs, overridden)
|
|
96603
|
+
};
|
|
96411
96604
|
const key = pair(subj.edge.id, other.id);
|
|
96412
96605
|
scanCrossings(subj, them, key, profile);
|
|
96413
96606
|
scanParallelRuns(mine, ctx.runsFor(other, theirs, overridden), key, profile);
|
|
@@ -98841,8 +99034,8 @@ function optimiseRoutes(scene, titleBoxes = [], folded = false) {
|
|
|
98841
99034
|
const subject = { ends, edge };
|
|
98842
99035
|
const out = [];
|
|
98843
99036
|
const seen = /* @__PURE__ */ new Set();
|
|
98844
|
-
const aSides = edge
|
|
98845
|
-
const bSides = edge
|
|
99037
|
+
const aSides = sideFixed(edge, "start") ? [ends[0].side] : SIDES;
|
|
99038
|
+
const bSides = sideFixed(edge, "end") ? [ends[1].side] : SIDES;
|
|
98846
99039
|
for (const aSide of aSides)
|
|
98847
99040
|
for (const bSide of bSides) {
|
|
98848
99041
|
const sides = { a: aSide, b: bSide };
|
|
@@ -98901,12 +99094,13 @@ function optimiseRoutes(scene, titleBoxes = [], folded = false) {
|
|
|
98901
99094
|
}
|
|
98902
99095
|
for (const edge of scene.edges) if (edge.pts.length >= 2) enforceOrthogonal(edge);
|
|
98903
99096
|
}
|
|
98904
|
-
var ORTHOGONAL_EPSILON, MIN_HUG_SPAN, CHANNEL_INSET, SNAP, JOG_SNAP, SIDE_INSET, MIN_SIDE_INSET, FAN_REACH2, SEAT_OFFSETS, segmentsCross, segmentLength, wouldMerge, seatWithAlong, crossingsOf, SEAT_GRID, SEAT_CLEAR, CHANNEL_MARGIN, CHANNEL_STEP, CHANNEL_CLEAR;
|
|
99097
|
+
var sideFixed, ORTHOGONAL_EPSILON, MIN_HUG_SPAN, CHANNEL_INSET, SNAP, JOG_SNAP, SIDE_INSET, MIN_SIDE_INSET, FAN_REACH2, SEAT_OFFSETS, segmentsCross, segmentLength, wouldMerge, seatWithAlong, crossingsOf, SEAT_GRID, SEAT_CLEAR, CHANNEL_MARGIN, CHANNEL_STEP, CHANNEL_CLEAR;
|
|
98905
99098
|
var init_edge_tidy = __esm({
|
|
98906
99099
|
"src/edge-tidy.ts"() {
|
|
98907
99100
|
"use strict";
|
|
98908
99101
|
init_readability();
|
|
98909
99102
|
init_geometry();
|
|
99103
|
+
sideFixed = (edge, end) => !!(edge.pinned?.[end] || edge.hubSided?.[end]);
|
|
98910
99104
|
ORTHOGONAL_EPSILON = 0.5;
|
|
98911
99105
|
MIN_HUG_SPAN = 24;
|
|
98912
99106
|
CHANNEL_INSET = 24;
|
|
@@ -99233,7 +99427,8 @@ function computeIngressExternalElements(model) {
|
|
|
99233
99427
|
}
|
|
99234
99428
|
return ingressExternalElements;
|
|
99235
99429
|
}
|
|
99236
|
-
function toElkNode2(element,
|
|
99430
|
+
function toElkNode2(element, sizing, root = false) {
|
|
99431
|
+
const { compact, fonts, glyphKinds } = sizing;
|
|
99237
99432
|
const { cont: containerFontSize, node: nodeFontSize } = fonts;
|
|
99238
99433
|
if (element.children.length) {
|
|
99239
99434
|
const lineCount = (element.label ?? element.id).split("\n").length;
|
|
@@ -99250,17 +99445,16 @@ function toElkNode2(element, compact, fonts, root = false) {
|
|
|
99250
99445
|
...measure(element.label ?? element.id, containerFontSize)
|
|
99251
99446
|
}
|
|
99252
99447
|
],
|
|
99253
|
-
children: element.children.map(
|
|
99254
|
-
(child) => toElkNode2(child, compact, fonts)
|
|
99255
|
-
)
|
|
99448
|
+
children: element.children.map((child) => toElkNode2(child, sizing))
|
|
99256
99449
|
};
|
|
99257
99450
|
}
|
|
99258
99451
|
const measured = measure(element.label ?? element.id, nodeFontSize);
|
|
99259
99452
|
const isActor = element.kind === "actor";
|
|
99453
|
+
const gutter = (glyphKinds.has(element.kind) ? GLYPH_GUTTER : 0) + (element.logo ? LOGO_GUTTER : 0);
|
|
99260
99454
|
return {
|
|
99261
99455
|
id: element.id,
|
|
99262
99456
|
...element.order && !root ? { layoutOptions: orderOption(element) } : {},
|
|
99263
|
-
width: isActor ? Math.max(64, measure(element.label ?? element.id, nodeFontSize - 1.5).width + 8) : Math.max(compact ? 98 : 108, measured.width + (compact ? 10 : 12)),
|
|
99457
|
+
width: isActor ? Math.max(64, measure(element.label ?? element.id, nodeFontSize - 1.5).width + 8) : Math.max(compact ? 98 : 108, measured.width + (compact ? 10 : 12) + gutter),
|
|
99264
99458
|
height: isActor ? 54 + ((element.label ?? element.id).split("\n").length - 1) * 11 : Math.max(compact ? 36 : 38, measured.height + (compact ? 10 : 12))
|
|
99265
99459
|
};
|
|
99266
99460
|
}
|
|
@@ -99418,20 +99612,20 @@ function selectionExtras(scene, model) {
|
|
|
99418
99612
|
}
|
|
99419
99613
|
return extra;
|
|
99420
99614
|
}
|
|
99421
|
-
function
|
|
99615
|
+
function tallyProfile(profile) {
|
|
99422
99616
|
const normalize = (key) => key.replace(/@[-\d.,]+$/, "").replace(/:\d+(?=@|$)/, "");
|
|
99423
|
-
const
|
|
99424
|
-
|
|
99425
|
-
|
|
99426
|
-
|
|
99427
|
-
|
|
99428
|
-
|
|
99429
|
-
|
|
99430
|
-
|
|
99431
|
-
|
|
99432
|
-
|
|
99433
|
-
const was =
|
|
99434
|
-
const now =
|
|
99617
|
+
const out = /* @__PURE__ */ new Map();
|
|
99618
|
+
for (const [key, tier] of profile) {
|
|
99619
|
+
const id = normalize(key);
|
|
99620
|
+
const entry = out.get(id) ?? { tier, count: 0 };
|
|
99621
|
+
entry.count++;
|
|
99622
|
+
out.set(id, entry);
|
|
99623
|
+
}
|
|
99624
|
+
return out;
|
|
99625
|
+
}
|
|
99626
|
+
function relayoutVerdict(before, after) {
|
|
99627
|
+
const was = tallyProfile(before);
|
|
99628
|
+
const now = tallyProfile(after);
|
|
99435
99629
|
for (let tier = 0; tier < 5; tier++) {
|
|
99436
99630
|
let gained = false;
|
|
99437
99631
|
let lost = false;
|
|
@@ -99447,6 +99641,62 @@ function relayoutVerdict(before, after) {
|
|
|
99447
99641
|
}
|
|
99448
99642
|
return -1;
|
|
99449
99643
|
}
|
|
99644
|
+
function noLadderRegression(before, after) {
|
|
99645
|
+
const perTier = (profile) => {
|
|
99646
|
+
const totals = [0, 0, 0, 0, 0];
|
|
99647
|
+
for (const { tier, count } of tallyProfile(profile).values()) totals[tier] += count;
|
|
99648
|
+
return totals;
|
|
99649
|
+
};
|
|
99650
|
+
const was = perTier(before);
|
|
99651
|
+
const now = perTier(after);
|
|
99652
|
+
return now.every((count, tier) => count <= was[tier]);
|
|
99653
|
+
}
|
|
99654
|
+
function nodeCoverage(scene) {
|
|
99655
|
+
if (!scene.nodes.length) return 1;
|
|
99656
|
+
const left = Math.min(...scene.nodes.map((n) => n.x));
|
|
99657
|
+
const top = Math.min(...scene.nodes.map((n) => n.y));
|
|
99658
|
+
const right = Math.max(...scene.nodes.map((n) => n.x + n.width));
|
|
99659
|
+
const bottom = Math.max(...scene.nodes.map((n) => n.y + n.height));
|
|
99660
|
+
const width = right - left;
|
|
99661
|
+
const height = bottom - top;
|
|
99662
|
+
if (width <= 0 || height <= 0) return 1;
|
|
99663
|
+
const COLUMNS = 32;
|
|
99664
|
+
const ROWS = 16;
|
|
99665
|
+
let covered = 0;
|
|
99666
|
+
for (let row = 0; row < ROWS; row++) {
|
|
99667
|
+
for (let column = 0; column < COLUMNS; column++) {
|
|
99668
|
+
const cellLeft = left + column * width / COLUMNS;
|
|
99669
|
+
const cellRight = left + (column + 1) * width / COLUMNS;
|
|
99670
|
+
const cellTop = top + row * height / ROWS;
|
|
99671
|
+
const cellBottom = top + (row + 1) * height / ROWS;
|
|
99672
|
+
if (scene.nodes.some(
|
|
99673
|
+
(n) => n.x < cellRight && n.x + n.width > cellLeft && n.y < cellBottom && n.y + n.height > cellTop
|
|
99674
|
+
))
|
|
99675
|
+
covered++;
|
|
99676
|
+
}
|
|
99677
|
+
}
|
|
99678
|
+
return covered / (COLUMNS * ROWS);
|
|
99679
|
+
}
|
|
99680
|
+
async function denserLayout(base, elkPass) {
|
|
99681
|
+
const densitySpecs = [
|
|
99682
|
+
{ minLayers: true, dense: true },
|
|
99683
|
+
{ placement: "LINEAR_SEGMENTS", dense: true }
|
|
99684
|
+
];
|
|
99685
|
+
const laidOut = await Promise.allSettled(densitySpecs.map((spec) => elkPass.layout(spec)));
|
|
99686
|
+
const areaOf = (scene) => scene.width * scene.height;
|
|
99687
|
+
const baseProfile = elkPass.profile(base);
|
|
99688
|
+
let bestArea = areaOf(base) * DENSITY_GAIN;
|
|
99689
|
+
let winner = null;
|
|
99690
|
+
for (const [index, settled] of laidOut.entries()) {
|
|
99691
|
+
if (settled.status !== "fulfilled") continue;
|
|
99692
|
+
const candidate = elkPass.toScene(settled.value);
|
|
99693
|
+
if (areaOf(candidate) > bestArea) continue;
|
|
99694
|
+
if (!noLadderRegression(baseProfile, elkPass.profile(candidate))) continue;
|
|
99695
|
+
bestArea = areaOf(candidate);
|
|
99696
|
+
winner = { scene: candidate, options: densitySpecs[index] };
|
|
99697
|
+
}
|
|
99698
|
+
return winner;
|
|
99699
|
+
}
|
|
99450
99700
|
function sideToward(from, to) {
|
|
99451
99701
|
const dx = centerOf(to).x - centerOf(from).x;
|
|
99452
99702
|
const dy = centerOf(to).y - centerOf(from).y;
|
|
@@ -99509,7 +99759,7 @@ function constrainPorts(graph, scene, flagged, model) {
|
|
|
99509
99759
|
}
|
|
99510
99760
|
function elkPartitionOf(element, index, view, ingressExternal) {
|
|
99511
99761
|
if (!view.partitionByOrder) return view.partitions[element.kind] ?? 1;
|
|
99512
|
-
if (
|
|
99762
|
+
if ((view.ingressKinds ?? DEFAULT_INGRESS_KINDS).includes(element.kind)) return INGRESS_PARTITION;
|
|
99513
99763
|
if (element.kind === "external")
|
|
99514
99764
|
return ingressExternal.has(element.id) ? INGRESS_PARTITION : EGRESS_PARTITION;
|
|
99515
99765
|
if (view.partitions[element.kind] !== void 0) return 90 + view.partitions[element.kind];
|
|
@@ -99533,16 +99783,13 @@ function readingSlots(model, view, ingressExternal) {
|
|
|
99533
99783
|
const lowestOf = /* @__PURE__ */ new Map();
|
|
99534
99784
|
for (const band of new Set(bandOf.values())) {
|
|
99535
99785
|
const members = model.elements.filter((element) => bandOf.get(element.id) === band);
|
|
99536
|
-
const declared = [
|
|
99537
|
-
|
|
99538
|
-
|
|
99786
|
+
const declared = [...new Set(members.filter((m) => m.order).map((m) => m.order.value))].sort(
|
|
99787
|
+
(a, b) => a - b
|
|
99788
|
+
);
|
|
99539
99789
|
const lowest = declared.length ? 1 : 0;
|
|
99540
99790
|
lowestOf.set(band, lowest);
|
|
99541
99791
|
for (const member of members)
|
|
99542
|
-
slotOf.set(
|
|
99543
|
-
member.id,
|
|
99544
|
-
member.order ? declared.indexOf(member.order.value) + 1 : lowest
|
|
99545
|
-
);
|
|
99792
|
+
slotOf.set(member.id, member.order ? declared.indexOf(member.order.value) + 1 : lowest);
|
|
99546
99793
|
}
|
|
99547
99794
|
const ordered = new Set(model.elements.filter((element) => element.order).map((e) => e.id));
|
|
99548
99795
|
for (let pass = 0; pass < model.elements.length; pass++) {
|
|
@@ -99561,13 +99808,15 @@ function readingSlots(model, view, ingressExternal) {
|
|
|
99561
99808
|
}
|
|
99562
99809
|
return slotOf;
|
|
99563
99810
|
}
|
|
99811
|
+
function elkEnds(flow) {
|
|
99812
|
+
return laidOutReversed(flow) ? { sources: [flow.to], targets: [flow.from] } : { sources: [flow.from], targets: [flow.to] };
|
|
99813
|
+
}
|
|
99564
99814
|
function elkFlowEdge(flow, ctx, labelWrap) {
|
|
99565
99815
|
const { compact, numbered, fonts, businessObjectName } = ctx;
|
|
99566
99816
|
if (numbered)
|
|
99567
99817
|
return {
|
|
99568
99818
|
id: flow.id,
|
|
99569
|
-
|
|
99570
|
-
targets: [flow.to],
|
|
99819
|
+
...elkEnds(flow),
|
|
99571
99820
|
labels: [
|
|
99572
99821
|
{
|
|
99573
99822
|
text: String(parseInt(flow.id.slice(1), 10)),
|
|
@@ -99592,13 +99841,35 @@ function elkFlowEdge(flow, ctx, labelWrap) {
|
|
|
99592
99841
|
});
|
|
99593
99842
|
return {
|
|
99594
99843
|
id: flow.id,
|
|
99595
|
-
|
|
99596
|
-
targets: [flow.to],
|
|
99844
|
+
...elkEnds(flow),
|
|
99597
99845
|
labels: text || chips.length ? [{ text, ...labelBox }] : []
|
|
99598
99846
|
};
|
|
99599
99847
|
}
|
|
99600
|
-
function
|
|
99601
|
-
|
|
99848
|
+
function laidOutReversed(flow) {
|
|
99849
|
+
return flow.fromRole?.value === "consumer" || flow.toRole?.value === "producer";
|
|
99850
|
+
}
|
|
99851
|
+
function roleCap(role) {
|
|
99852
|
+
if (!role) return null;
|
|
99853
|
+
return role.value === "producer" ? "left" : "right";
|
|
99854
|
+
}
|
|
99855
|
+
function hubFlowSides(model, view) {
|
|
99856
|
+
const derived = /* @__PURE__ */ new Map();
|
|
99857
|
+
const hubKinds = new Set(view.hubKinds ?? []);
|
|
99858
|
+
if (!hubKinds.size) return derived;
|
|
99859
|
+
const hubs = new Set(
|
|
99860
|
+
indexElementsById(model.elements).filter(([, element]) => hubKinds.has(element.kind)).map(([id]) => id)
|
|
99861
|
+
);
|
|
99862
|
+
if (!hubs.size) return derived;
|
|
99863
|
+
for (const flow of model.flows) {
|
|
99864
|
+
const sides = {};
|
|
99865
|
+
if (hubs.has(flow.to) && !flow.toSide) sides.to = roleCap(flow.fromRole) ?? "left";
|
|
99866
|
+
if (hubs.has(flow.from) && !flow.fromSide) sides.from = roleCap(flow.toRole) ?? "right";
|
|
99867
|
+
if (sides.to || sides.from) derived.set(flow.id, sides);
|
|
99868
|
+
}
|
|
99869
|
+
return derived;
|
|
99870
|
+
}
|
|
99871
|
+
function applyDeclaredPorts(graph, model, derived) {
|
|
99872
|
+
const pinned = model.flows.filter((flow) => flow.fromSide || flow.toSide || derived.has(flow.id));
|
|
99602
99873
|
if (!pinned.length) return;
|
|
99603
99874
|
const elkById = /* @__PURE__ */ new Map();
|
|
99604
99875
|
const register = (node) => {
|
|
@@ -99609,14 +99880,15 @@ function applyDeclaredPorts(graph, model) {
|
|
|
99609
99880
|
for (const flow of pinned) {
|
|
99610
99881
|
const elkEdge = (graph.edges ?? []).find((edge) => edge.id === flow.id);
|
|
99611
99882
|
if (!elkEdge) continue;
|
|
99883
|
+
const hub = derived.get(flow.id);
|
|
99612
99884
|
for (const [role, declared, nodeId] of [
|
|
99613
|
-
["out", flow.fromSide, flow.from],
|
|
99614
|
-
["in", flow.toSide, flow.to]
|
|
99885
|
+
["out", flow.fromSide ?? sideRequest(hub?.from), flow.from],
|
|
99886
|
+
["in", flow.toSide ?? sideRequest(hub?.to), flow.to]
|
|
99615
99887
|
]) {
|
|
99616
99888
|
if (!declared) continue;
|
|
99617
99889
|
const elkNode = elkById.get(nodeId);
|
|
99618
99890
|
if (!elkNode) continue;
|
|
99619
|
-
const portId = `${flow.id}#${role}`;
|
|
99891
|
+
const portId = `${flow.id}#${role === "out" !== laidOutReversed(flow) ? "out" : "in"}`;
|
|
99620
99892
|
elkNode.ports = [
|
|
99621
99893
|
...elkNode.ports ?? [],
|
|
99622
99894
|
{
|
|
@@ -99627,7 +99899,7 @@ function applyDeclaredPorts(graph, model) {
|
|
|
99627
99899
|
}
|
|
99628
99900
|
];
|
|
99629
99901
|
elkNode.layoutOptions = { ...elkNode.layoutOptions, "elk.portConstraints": "FIXED_SIDE" };
|
|
99630
|
-
if (role === "out") elkEdge.sources = [portId];
|
|
99902
|
+
if (role === "out" !== laidOutReversed(flow)) elkEdge.sources = [portId];
|
|
99631
99903
|
else elkEdge.targets = [portId];
|
|
99632
99904
|
}
|
|
99633
99905
|
}
|
|
@@ -99672,6 +99944,7 @@ function attachSideDiagnostics(scene, model) {
|
|
|
99672
99944
|
}
|
|
99673
99945
|
function buildElkGraph(ctx, direction, options) {
|
|
99674
99946
|
const { model, view, ingressExternal, slotOf, compact, numbered, fonts } = ctx;
|
|
99947
|
+
const glyphKinds = new Set(view.glyphKinds ?? []);
|
|
99675
99948
|
const graph = {
|
|
99676
99949
|
id: "root",
|
|
99677
99950
|
layoutOptions: {
|
|
@@ -99688,7 +99961,7 @@ function buildElkGraph(ctx, direction, options) {
|
|
|
99688
99961
|
"elk.edgeRouting": "ORTHOGONAL",
|
|
99689
99962
|
"elk.partitioning.activate": "true",
|
|
99690
99963
|
"elk.layered.nodePlacement.strategy": "NETWORK_SIMPLEX",
|
|
99691
|
-
"elk.layered.compaction.postCompaction.strategy": "EDGE_LENGTH",
|
|
99964
|
+
"elk.layered.compaction.postCompaction.strategy": options?.postCompaction ?? "EDGE_LENGTH",
|
|
99692
99965
|
"elk.layered.feedbackEdges": "true",
|
|
99693
99966
|
"elk.layered.thoroughness": "30",
|
|
99694
99967
|
"elk.separateConnectedComponents": "false",
|
|
@@ -99707,25 +99980,38 @@ function buildElkGraph(ctx, direction, options) {
|
|
|
99707
99980
|
"elk.spacing.edgeNode": "18",
|
|
99708
99981
|
"elk.layered.thoroughness": "80",
|
|
99709
99982
|
"elk.layered.nodePlacement.favorStraightEdges": "true"
|
|
99710
|
-
} : {}
|
|
99983
|
+
} : {},
|
|
99984
|
+
// Spread last, after every spacing default above, because overriding them
|
|
99985
|
+
// is the whole point — a density candidate that lands before them is
|
|
99986
|
+
// silently shadowed and lays out exactly like the layout it was meant to
|
|
99987
|
+
// beat.
|
|
99988
|
+
...options?.dense ? {
|
|
99989
|
+
"elk.layered.spacing.nodeNodeBetweenLayers": "14",
|
|
99990
|
+
"elk.spacing.nodeNode": "10",
|
|
99991
|
+
"elk.spacing.edgeEdge": "8",
|
|
99992
|
+
"elk.spacing.edgeNode": "9"
|
|
99993
|
+
} : {},
|
|
99994
|
+
...options?.placement ? { "elk.layered.nodePlacement.strategy": options.placement } : {}
|
|
99711
99995
|
},
|
|
99712
99996
|
children: model.elements.map((element, index) => {
|
|
99713
|
-
const elkNode = toElkNode2(element, compact, fonts, true);
|
|
99997
|
+
const elkNode = toElkNode2(element, { compact, fonts, glyphKinds }, true);
|
|
99714
99998
|
const band = elkPartitionOf(element, index, view, ingressExternal);
|
|
99715
99999
|
const slot = slotOf.get(element.id);
|
|
99716
100000
|
elkNode.layoutOptions = {
|
|
99717
100001
|
...elkNode.layoutOptions,
|
|
99718
100002
|
// Without an `order:` anywhere the band is emitted as it always was, so
|
|
99719
100003
|
// the drawing is byte-identical to one from before the hint existed.
|
|
99720
|
-
"elk.partitioning.partition": String(
|
|
99721
|
-
slot === void 0 ? band : band * SLOT_SCALE + slot
|
|
99722
|
-
)
|
|
100004
|
+
"elk.partitioning.partition": String(slot === void 0 ? band : band * SLOT_SCALE + slot)
|
|
99723
100005
|
};
|
|
99724
100006
|
return elkNode;
|
|
99725
100007
|
}),
|
|
99726
100008
|
edges: model.flows.map((flow) => elkFlowEdge(flow, ctx, options?.labelWrap))
|
|
99727
100009
|
};
|
|
99728
|
-
applyDeclaredPorts(
|
|
100010
|
+
applyDeclaredPorts(
|
|
100011
|
+
graph,
|
|
100012
|
+
model,
|
|
100013
|
+
options?.hubPorts === false ? /* @__PURE__ */ new Map() : hubFlowSides(model, view)
|
|
100014
|
+
);
|
|
99729
100015
|
return graph;
|
|
99730
100016
|
}
|
|
99731
100017
|
function recordRepairs(scene, routesBefore) {
|
|
@@ -99738,8 +100024,127 @@ function recordRepairs(scene, routesBefore) {
|
|
|
99738
100024
|
if (moved) edge.repairedFrom = original;
|
|
99739
100025
|
}
|
|
99740
100026
|
}
|
|
100027
|
+
function segmentsCross2(a1, a2, b1, b2) {
|
|
100028
|
+
const aH = Math.abs(a1.y - a2.y) < 0.5;
|
|
100029
|
+
const bH = Math.abs(b1.y - b2.y) < 0.5;
|
|
100030
|
+
if (aH === bH) return false;
|
|
100031
|
+
const [h1, h2, v1, v2] = aH ? [a1, a2, b1, b2] : [b1, b2, a1, a2];
|
|
100032
|
+
const hy = h1.y;
|
|
100033
|
+
const vx = v1.x;
|
|
100034
|
+
return vx > Math.min(h1.x, h2.x) + 0.5 && vx < Math.max(h1.x, h2.x) - 0.5 && hy > Math.min(v1.y, v2.y) + 0.5 && hy < Math.max(v1.y, v2.y) - 0.5;
|
|
100035
|
+
}
|
|
100036
|
+
function shortJogs(edges) {
|
|
100037
|
+
let total = 0;
|
|
100038
|
+
for (const edge of edges)
|
|
100039
|
+
for (let i = 1; i + 2 < edge.pts.length; i++) {
|
|
100040
|
+
const run = Math.abs(edge.pts[i + 1].x - edge.pts[i].x) + Math.abs(edge.pts[i + 1].y - edge.pts[i].y);
|
|
100041
|
+
if (run > 0 && run <= 20) total++;
|
|
100042
|
+
}
|
|
100043
|
+
return total;
|
|
100044
|
+
}
|
|
100045
|
+
function crossingsAround(scene, subject) {
|
|
100046
|
+
let total = 0;
|
|
100047
|
+
const subjectIds = new Set(subject.map((edge) => edge.id));
|
|
100048
|
+
for (const edge of subject)
|
|
100049
|
+
for (const other of scene.edges) {
|
|
100050
|
+
if (other.id === edge.id) continue;
|
|
100051
|
+
if (subjectIds.has(other.id) && other.id < edge.id) continue;
|
|
100052
|
+
for (let i = 1; i < edge.pts.length; i++)
|
|
100053
|
+
for (let j = 1; j < other.pts.length; j++)
|
|
100054
|
+
if (segmentsCross2(edge.pts[i - 1], edge.pts[i], other.pts[j - 1], other.pts[j])) total++;
|
|
100055
|
+
}
|
|
100056
|
+
return total;
|
|
100057
|
+
}
|
|
100058
|
+
function leafBoxesOf(scene) {
|
|
100059
|
+
return scene.nodes.filter((node) => !node.container);
|
|
100060
|
+
}
|
|
100061
|
+
function crossesLeaf(edge, leaves, attached) {
|
|
100062
|
+
for (let i = 1; i < edge.pts.length; i++) {
|
|
100063
|
+
const a = edge.pts[i - 1];
|
|
100064
|
+
const b = edge.pts[i];
|
|
100065
|
+
const horizontal = Math.abs(a.y - b.y) < 0.5;
|
|
100066
|
+
const vertical = Math.abs(a.x - b.x) < 0.5;
|
|
100067
|
+
if (!horizontal && !vertical) continue;
|
|
100068
|
+
const at = horizontal ? a.y : a.x;
|
|
100069
|
+
const lo = horizontal ? Math.min(a.x, b.x) : Math.min(a.y, b.y);
|
|
100070
|
+
const hi = horizontal ? Math.max(a.x, b.x) : Math.max(a.y, b.y);
|
|
100071
|
+
for (const node of leaves) {
|
|
100072
|
+
if (attached.has(node.id)) continue;
|
|
100073
|
+
const struck = horizontal ? at > node.y + 1 && at < node.y + node.height - 1 && lo < node.x + node.width - 1 && hi > node.x + 1 : at > node.x + 1 && at < node.x + node.width - 1 && lo < node.y + node.height - 1 && hi > node.y + 1;
|
|
100074
|
+
if (struck) return true;
|
|
100075
|
+
}
|
|
100076
|
+
}
|
|
100077
|
+
return false;
|
|
100078
|
+
}
|
|
100079
|
+
function snapLanes(scene, laneOf, axis) {
|
|
100080
|
+
if (!laneOf.size) return;
|
|
100081
|
+
const leaves = leafBoxesOf(scene);
|
|
100082
|
+
const tilted = (edges) => edges.reduce(
|
|
100083
|
+
(total, edge) => total + edge.pts.filter(
|
|
100084
|
+
(point, i) => i > 0 && Math.abs(point.x - edge.pts[i - 1].x) >= 0.5 && Math.abs(point.y - edge.pts[i - 1].y) >= 0.5
|
|
100085
|
+
).length,
|
|
100086
|
+
0
|
|
100087
|
+
);
|
|
100088
|
+
const touching = /* @__PURE__ */ new Map();
|
|
100089
|
+
const attachedTo = /* @__PURE__ */ new Map();
|
|
100090
|
+
for (const edge of scene.edges) {
|
|
100091
|
+
const ends = /* @__PURE__ */ new Set();
|
|
100092
|
+
for (const point of [edge.pts[0], edge.pts[edge.pts.length - 1]]) {
|
|
100093
|
+
if (!point) continue;
|
|
100094
|
+
for (const node of scene.nodes)
|
|
100095
|
+
if (point.x >= node.x - 1 && point.x <= node.x + node.width + 1 && point.y >= node.y - 1 && point.y <= node.y + node.height + 1) {
|
|
100096
|
+
ends.add(node.id);
|
|
100097
|
+
if (!touching.has(node.id)) touching.set(node.id, []);
|
|
100098
|
+
touching.get(node.id).push(edge);
|
|
100099
|
+
}
|
|
100100
|
+
}
|
|
100101
|
+
attachedTo.set(edge.id, ends);
|
|
100102
|
+
}
|
|
100103
|
+
const byLane = /* @__PURE__ */ new Map();
|
|
100104
|
+
for (const node of scene.nodes) {
|
|
100105
|
+
const lane = laneOf.get(node.id);
|
|
100106
|
+
if (lane === void 0) continue;
|
|
100107
|
+
if (!byLane.has(lane)) byLane.set(lane, []);
|
|
100108
|
+
byLane.get(lane).push(node);
|
|
100109
|
+
}
|
|
100110
|
+
for (const members of [...byLane.entries()].sort((a, b) => a[0] - b[0]).map((e) => e[1])) {
|
|
100111
|
+
if (members.length < 2) continue;
|
|
100112
|
+
const target = Math.max(...members.map((node) => node[axis]));
|
|
100113
|
+
for (const node of members) {
|
|
100114
|
+
const delta = target - node[axis];
|
|
100115
|
+
if (!delta) continue;
|
|
100116
|
+
const box = { x: node.x, y: node.y, w: node.width, h: node.height };
|
|
100117
|
+
const edges = touching.get(node.id) ?? [];
|
|
100118
|
+
const before = edges.map((edge) => edge.pts.map((point) => ({ ...point })));
|
|
100119
|
+
const crossingsBefore = crossingsAround(scene, edges);
|
|
100120
|
+
const jogsBefore = shortJogs(edges);
|
|
100121
|
+
const tiltsBefore = tilted(edges);
|
|
100122
|
+
node[axis] = target;
|
|
100123
|
+
for (const edge of edges)
|
|
100124
|
+
for (const point of edge.pts)
|
|
100125
|
+
if (point.x >= box.x - 1 && point.x <= box.x + box.w + 1 && point.y >= box.y - 1 && point.y <= box.y + box.h + 1)
|
|
100126
|
+
point[axis] += delta;
|
|
100127
|
+
const broke = crossingsAround(scene, edges) > crossingsBefore || shortJogs(edges) > jogsBefore || tilted(edges) > tiltsBefore || edges.some((edge) => crossesLeaf(edge, leaves, attachedTo.get(edge.id) ?? /* @__PURE__ */ new Set())) || // Containers included: a lane member is top-level, so it has no
|
|
100128
|
+
// legitimate container ancestor and may not land on one.
|
|
100129
|
+
scene.nodes.some(
|
|
100130
|
+
(other) => other !== node && !node.container && other.x < node.x + node.width && node.x < other.x + other.width && other.y < node.y + node.height && node.y < other.y + other.height
|
|
100131
|
+
);
|
|
100132
|
+
if (broke) {
|
|
100133
|
+
node[axis] = target - delta;
|
|
100134
|
+
edges.forEach((edge, i) => {
|
|
100135
|
+
edge.pts = before[i];
|
|
100136
|
+
});
|
|
100137
|
+
}
|
|
100138
|
+
}
|
|
100139
|
+
}
|
|
100140
|
+
const maxX = Math.max(...scene.nodes.map((node) => node.x + node.width));
|
|
100141
|
+
const maxY = Math.max(...scene.nodes.map((node) => node.y + node.height));
|
|
100142
|
+
scene.width = Math.max(scene.width, Math.ceil(maxX) + 10);
|
|
100143
|
+
scene.height = Math.max(scene.height, Math.ceil(maxY) + 10);
|
|
100144
|
+
}
|
|
99741
100145
|
function runGeometryPasses(scene, model, options) {
|
|
99742
100146
|
const { numbered, sideways } = options;
|
|
100147
|
+
if (options.laneOf) snapLanes(scene, options.laneOf, sideways ? "y" : "x");
|
|
99743
100148
|
const titleBoxes = titleBoxesOf(scene, model);
|
|
99744
100149
|
if (sideways) transpose(scene, titleBoxes);
|
|
99745
100150
|
rerouteDetours(scene, model, numbered, titleBoxes);
|
|
@@ -99758,6 +100163,95 @@ function runGeometryPasses(scene, model, options) {
|
|
|
99758
100163
|
clearSideHugs(scene, settledTitles);
|
|
99759
100164
|
anchorFlowLabels(scene, settledTitles);
|
|
99760
100165
|
swapCrossingSiblingSeats(scene);
|
|
100166
|
+
fitCanvas(scene);
|
|
100167
|
+
}
|
|
100168
|
+
function laneAssignment(model, view) {
|
|
100169
|
+
const laneOf = /* @__PURE__ */ new Map();
|
|
100170
|
+
const laneKinds = new Set(view.laneKinds ?? []);
|
|
100171
|
+
if (!laneKinds.size || model.style.compact) return laneOf;
|
|
100172
|
+
const rootOf = /* @__PURE__ */ new Map();
|
|
100173
|
+
const walk = (element, root) => {
|
|
100174
|
+
rootOf.set(element.id, root);
|
|
100175
|
+
for (const child of element.children ?? []) walk(child, root);
|
|
100176
|
+
};
|
|
100177
|
+
for (const element of model.elements) walk(element, element.id);
|
|
100178
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
100179
|
+
for (const flow of model.flows) {
|
|
100180
|
+
const from = rootOf.get(flow.from);
|
|
100181
|
+
const to = rootOf.get(flow.to);
|
|
100182
|
+
if (!from || !to || from === to) continue;
|
|
100183
|
+
if (!adjacency.has(from)) adjacency.set(from, /* @__PURE__ */ new Set());
|
|
100184
|
+
adjacency.get(from).add(to);
|
|
100185
|
+
}
|
|
100186
|
+
const reaches = (from, to) => {
|
|
100187
|
+
const seen = /* @__PURE__ */ new Set([from]);
|
|
100188
|
+
const queue = [from];
|
|
100189
|
+
while (queue.length) {
|
|
100190
|
+
for (const next of adjacency.get(queue.pop()) ?? []) {
|
|
100191
|
+
if (next === to) return true;
|
|
100192
|
+
if (!seen.has(next)) {
|
|
100193
|
+
seen.add(next);
|
|
100194
|
+
queue.push(next);
|
|
100195
|
+
}
|
|
100196
|
+
}
|
|
100197
|
+
}
|
|
100198
|
+
return false;
|
|
100199
|
+
};
|
|
100200
|
+
const byKind = /* @__PURE__ */ new Map();
|
|
100201
|
+
for (const element of model.elements) {
|
|
100202
|
+
if (!laneKinds.has(element.kind)) continue;
|
|
100203
|
+
if ((element.children ?? []).length) continue;
|
|
100204
|
+
if (!byKind.has(element.kind)) byKind.set(element.kind, []);
|
|
100205
|
+
byKind.get(element.kind).push(element);
|
|
100206
|
+
}
|
|
100207
|
+
const pinned = /* @__PURE__ */ new Set();
|
|
100208
|
+
for (const flow of model.flows) {
|
|
100209
|
+
if (!flow.fromSide && !flow.toSide) continue;
|
|
100210
|
+
pinned.add(rootOf.get(flow.from) ?? flow.from);
|
|
100211
|
+
pinned.add(rootOf.get(flow.to) ?? flow.to);
|
|
100212
|
+
}
|
|
100213
|
+
let laneId = 0;
|
|
100214
|
+
for (const members of byKind.values()) {
|
|
100215
|
+
if (members.length < 2) continue;
|
|
100216
|
+
if (members.some((member) => pinned.has(member.id))) continue;
|
|
100217
|
+
if (members.some((member) => member.order)) continue;
|
|
100218
|
+
if (members.some((a) => members.some((b) => a !== b && reaches(a.id, b.id)))) continue;
|
|
100219
|
+
laneId++;
|
|
100220
|
+
for (const member of members) laneOf.set(member.id, laneId);
|
|
100221
|
+
}
|
|
100222
|
+
return laneOf;
|
|
100223
|
+
}
|
|
100224
|
+
async function withHubPortFallback(elk, makeGraph, direction, options) {
|
|
100225
|
+
const run = async (spec) => await elk.layout(makeGraph(direction, spec));
|
|
100226
|
+
const rungs = options?.hubPorts === false ? [] : [
|
|
100227
|
+
{ ...options, postCompaction: "EDGE_LENGTH_CONSTRAINT_LOCKING" },
|
|
100228
|
+
{ ...options, postCompaction: "NONE" },
|
|
100229
|
+
{ ...options, hubPorts: false }
|
|
100230
|
+
];
|
|
100231
|
+
try {
|
|
100232
|
+
return { result: await run(options), options };
|
|
100233
|
+
} catch (error) {
|
|
100234
|
+
for (const [index, rung] of rungs.entries()) {
|
|
100235
|
+
try {
|
|
100236
|
+
return { result: await run(rung), options: rung };
|
|
100237
|
+
} catch (rungError) {
|
|
100238
|
+
if (index === rungs.length - 1) throw rungError;
|
|
100239
|
+
}
|
|
100240
|
+
}
|
|
100241
|
+
throw error;
|
|
100242
|
+
}
|
|
100243
|
+
}
|
|
100244
|
+
function markDeclaredTerminals(edges, model, view, hubPorts) {
|
|
100245
|
+
const pinnedFlows = new Map(
|
|
100246
|
+
model.flows.filter((flow) => flow.fromSide || flow.toSide).map((flow) => [flow.id, { start: !!flow.fromSide, end: !!flow.toSide }])
|
|
100247
|
+
);
|
|
100248
|
+
const hubSides = hubPorts ? hubFlowSides(model, view) : /* @__PURE__ */ new Map();
|
|
100249
|
+
for (const edge of edges) {
|
|
100250
|
+
const pinned = pinnedFlows.get(edge.id);
|
|
100251
|
+
if (pinned) edge.pinned = { ...pinned };
|
|
100252
|
+
const hub = hubSides.get(edge.id);
|
|
100253
|
+
if (hub) edge.hubSided = { start: !!hub.from, end: !!hub.to };
|
|
100254
|
+
}
|
|
99761
100255
|
}
|
|
99762
100256
|
async function layout(model, view) {
|
|
99763
100257
|
const elk = await getElk();
|
|
@@ -99771,10 +100265,6 @@ async function layout(model, view) {
|
|
|
99771
100265
|
scale: fontScale
|
|
99772
100266
|
} = fontSizes(model.style.font.size);
|
|
99773
100267
|
const disposition = model.style.disposition;
|
|
99774
|
-
const ASPECT_TARGETS = {
|
|
99775
|
-
slide: 16 / 9,
|
|
99776
|
-
page: 0.71
|
|
99777
|
-
};
|
|
99778
100268
|
const aspectTarget = ASPECT_TARGETS[disposition];
|
|
99779
100269
|
const ingressExternalElements = view.partitionByOrder ? computeIngressExternalElements(model) : /* @__PURE__ */ new Set();
|
|
99780
100270
|
const graphContext = {
|
|
@@ -99794,7 +100284,7 @@ async function layout(model, view) {
|
|
|
99794
100284
|
};
|
|
99795
100285
|
const makeGraph = (direction, options) => buildElkGraph(graphContext, direction, options);
|
|
99796
100286
|
const kindOf = new Map(indexElementsById(model.elements));
|
|
99797
|
-
const sceneFromResult = (result2, layoutMs2) => {
|
|
100287
|
+
const sceneFromResult = (result2, layoutMs2, laidOutWith, lanes = true) => {
|
|
99798
100288
|
const origins = {
|
|
99799
100289
|
root: { x: 0, y: 0 }
|
|
99800
100290
|
};
|
|
@@ -99802,13 +100292,9 @@ async function layout(model, view) {
|
|
|
99802
100292
|
const nodes = walkedNodes.map((walked) => walked.node);
|
|
99803
100293
|
for (const walked of walkedNodes) origins[walked.id] = { x: walked.x, y: walked.y };
|
|
99804
100294
|
const edges = collectSceneEdges(result2, origins, numbered, edgeFontSize);
|
|
99805
|
-
const
|
|
99806
|
-
|
|
99807
|
-
);
|
|
99808
|
-
for (const edge of edges) {
|
|
99809
|
-
const pinned = pinnedFlows.get(edge.id);
|
|
99810
|
-
if (pinned) edge.pinned = { ...pinned };
|
|
99811
|
-
}
|
|
100295
|
+
const reversed = new Set(model.flows.filter(laidOutReversed).map((flow) => flow.id));
|
|
100296
|
+
for (const edge of edges) if (reversed.has(edge.id)) edge.pts.reverse();
|
|
100297
|
+
markDeclaredTerminals(edges, model, view, laidOutWith?.hubPorts !== false);
|
|
99812
100298
|
const scene = {
|
|
99813
100299
|
width: Math.ceil(result2.width),
|
|
99814
100300
|
height: Math.ceil(result2.height),
|
|
@@ -99818,11 +100304,14 @@ async function layout(model, view) {
|
|
|
99818
100304
|
};
|
|
99819
100305
|
runGeometryPasses(scene, model, {
|
|
99820
100306
|
numbered,
|
|
99821
|
-
sideways: disposition === "page" || disposition === "tall"
|
|
100307
|
+
sideways: disposition === "page" || disposition === "tall",
|
|
100308
|
+
laneOf: lanes ? laneOf : void 0
|
|
99822
100309
|
});
|
|
99823
100310
|
return scene;
|
|
99824
100311
|
};
|
|
100312
|
+
const laneOf = laneAssignment(model, view);
|
|
99825
100313
|
const startTime = Date.now();
|
|
100314
|
+
const layoutGraph = (direction, options) => withHubPortFallback(elk, makeGraph, direction, options);
|
|
99826
100315
|
let result;
|
|
99827
100316
|
let winnerDirection;
|
|
99828
100317
|
let winnerOptions;
|
|
@@ -99833,9 +100322,10 @@ async function layout(model, view) {
|
|
|
99833
100322
|
{ direction: "RIGHT", options: { labelWrap: 14, tight: true } },
|
|
99834
100323
|
{ direction: "RIGHT", options: { labelWrap: 14, tight: true, minLayers: true } }
|
|
99835
100324
|
] : [{ direction: "DOWN" }, { direction: "DOWN", options: { labelWrap: 16 } }];
|
|
99836
|
-
const
|
|
99837
|
-
graphSpecs.map((spec) =>
|
|
100325
|
+
const laidOutSpecs = await Promise.all(
|
|
100326
|
+
graphSpecs.map((spec) => layoutGraph(spec.direction, spec.options))
|
|
99838
100327
|
);
|
|
100328
|
+
const candidates = laidOutSpecs.map((laidOut) => laidOut.result);
|
|
99839
100329
|
const preferWide = disposition === "slide";
|
|
99840
100330
|
const orientedLayouts = candidates.map((layoutResult, index) => ({ layoutResult, index })).filter(
|
|
99841
100331
|
({ layoutResult }) => preferWide ? layoutResult.width >= layoutResult.height : layoutResult.height >= layoutResult.width
|
|
@@ -99848,7 +100338,7 @@ async function layout(model, view) {
|
|
|
99848
100338
|
);
|
|
99849
100339
|
result = winner.layoutResult;
|
|
99850
100340
|
winnerDirection = graphSpecs[winner.index].direction;
|
|
99851
|
-
winnerOptions =
|
|
100341
|
+
winnerOptions = laidOutSpecs[winner.index].options;
|
|
99852
100342
|
if (disposition === "slide") {
|
|
99853
100343
|
const folded = await foldedLayout(model, view, elk);
|
|
99854
100344
|
if (folded && fitScore(result) >= fitScore(folded) * 1.1) {
|
|
@@ -99862,20 +100352,32 @@ async function layout(model, view) {
|
|
|
99862
100352
|
}
|
|
99863
100353
|
} else {
|
|
99864
100354
|
winnerDirection = disposition === "tall" ? "DOWN" : "RIGHT";
|
|
99865
|
-
winnerOptions =
|
|
99866
|
-
result = await elk.layout(makeGraph(winnerDirection));
|
|
100355
|
+
({ result, options: winnerOptions } = await layoutGraph(winnerDirection));
|
|
99867
100356
|
}
|
|
99868
100357
|
const layoutMs = Date.now() - startTime;
|
|
99869
|
-
|
|
99870
|
-
const skipPortPass = !!globalThis.process?.env?.CAIRN_NO_PORT_PASS;
|
|
99871
|
-
if (skipPortPass) return base;
|
|
100358
|
+
let base = sceneFromResult(result, layoutMs, winnerOptions);
|
|
99872
100359
|
const layoutProfile = (candidate) => {
|
|
99873
100360
|
const everyEdge = new Set(candidate.edges.map((edge) => edge.id));
|
|
99874
100361
|
const profile = inspect(candidate, titleBoxesOf(candidate, model)).local(everyEdge, /* @__PURE__ */ new Map());
|
|
99875
100362
|
for (const [key, tier] of selectionExtras(candidate, model)) profile.set(key, tier);
|
|
99876
100363
|
return profile;
|
|
99877
100364
|
};
|
|
100365
|
+
if (!aspectTarget && nodeCoverage(base) < DENSE_ENOUGH) {
|
|
100366
|
+
const denser = await denserLayout(base, {
|
|
100367
|
+
layout: (spec) => elk.layout(makeGraph(winnerDirection, spec)),
|
|
100368
|
+
// Its specs carry the hub ports (none of them sets `hubPorts: false`), and
|
|
100369
|
+
// a candidate elk refuses is dropped rather than retried without them.
|
|
100370
|
+
toScene: (laidOut) => sceneFromResult(laidOut, Date.now() - startTime),
|
|
100371
|
+
profile: layoutProfile
|
|
100372
|
+
});
|
|
100373
|
+
if (denser) {
|
|
100374
|
+
base = denser.scene;
|
|
100375
|
+
winnerOptions = denser.options;
|
|
100376
|
+
}
|
|
100377
|
+
}
|
|
99878
100378
|
const baseProfile = layoutProfile(base);
|
|
100379
|
+
const skipPortPass = !!globalThis.process?.env?.CAIRN_NO_PORT_PASS;
|
|
100380
|
+
if (skipPortPass) return base;
|
|
99879
100381
|
let current = base;
|
|
99880
100382
|
let best = null;
|
|
99881
100383
|
for (let round = 0; round < PORT_PASS_ROUNDS; round++) {
|
|
@@ -99886,7 +100388,7 @@ async function layout(model, view) {
|
|
|
99886
100388
|
const constrained = makeGraph(winnerDirection, winnerOptions);
|
|
99887
100389
|
constrainPorts(constrained, current, flagged, model);
|
|
99888
100390
|
const reresult = await elk.layout(constrained);
|
|
99889
|
-
candidate = sceneFromResult(reresult, Date.now() - startTime);
|
|
100391
|
+
candidate = sceneFromResult(reresult, Date.now() - startTime, winnerOptions);
|
|
99890
100392
|
} catch {
|
|
99891
100393
|
break;
|
|
99892
100394
|
}
|
|
@@ -99898,7 +100400,7 @@ async function layout(model, view) {
|
|
|
99898
100400
|
}
|
|
99899
100401
|
return best?.scene ?? base;
|
|
99900
100402
|
}
|
|
99901
|
-
var orderOption, semiInteractiveOption, PORT_PASS_ROUNDS, centerOf, INGRESS_PARTITION, EGRESS_PARTITION, COMPACT_WRAP, SLOT_SCALE, SIDE_TO_ELK;
|
|
100403
|
+
var orderOption, semiInteractiveOption, PORT_PASS_ROUNDS, centerOf, DENSE_ENOUGH, DENSITY_GAIN, INGRESS_PARTITION, DEFAULT_INGRESS_KINDS, EGRESS_PARTITION, COMPACT_WRAP, SLOT_SCALE, SIDE_TO_ELK, sideRequest, ASPECT_TARGETS;
|
|
99902
100404
|
var init_scene_layout = __esm({
|
|
99903
100405
|
"src/scene-layout.ts"() {
|
|
99904
100406
|
"use strict";
|
|
@@ -99916,7 +100418,10 @@ var init_scene_layout = __esm({
|
|
|
99916
100418
|
semiInteractiveOption = (children) => children.some((child) => child.order) ? { "elk.layered.crossingMinimization.semiInteractive": "true" } : {};
|
|
99917
100419
|
PORT_PASS_ROUNDS = 2;
|
|
99918
100420
|
centerOf = (n) => ({ x: n.x + n.width / 2, y: n.y + n.height / 2 });
|
|
100421
|
+
DENSE_ENOUGH = 0.6;
|
|
100422
|
+
DENSITY_GAIN = 0.95;
|
|
99919
100423
|
INGRESS_PARTITION = -1;
|
|
100424
|
+
DEFAULT_INGRESS_KINDS = ["actor", "actor-group"];
|
|
99920
100425
|
EGRESS_PARTITION = 900;
|
|
99921
100426
|
COMPACT_WRAP = 10;
|
|
99922
100427
|
SLOT_SCALE = 1e3;
|
|
@@ -99926,6 +100431,11 @@ var init_scene_layout = __esm({
|
|
|
99926
100431
|
top: "NORTH",
|
|
99927
100432
|
bottom: "SOUTH"
|
|
99928
100433
|
};
|
|
100434
|
+
sideRequest = (side) => side ? { value: side } : void 0;
|
|
100435
|
+
ASPECT_TARGETS = {
|
|
100436
|
+
slide: 16 / 9,
|
|
100437
|
+
page: 0.71
|
|
100438
|
+
};
|
|
99929
100439
|
}
|
|
99930
100440
|
});
|
|
99931
100441
|
|
|
@@ -99976,16 +100486,62 @@ var init_localization = __esm({
|
|
|
99976
100486
|
});
|
|
99977
100487
|
|
|
99978
100488
|
// src/xml-escape.ts
|
|
99979
|
-
var esc, escAttr;
|
|
100489
|
+
var esc, escAttr, escComment;
|
|
99980
100490
|
var init_xml_escape = __esm({
|
|
99981
100491
|
"src/xml-escape.ts"() {
|
|
99982
100492
|
"use strict";
|
|
99983
100493
|
esc = (text) => text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
99984
100494
|
escAttr = (text) => esc(text).replace(/"/g, """);
|
|
100495
|
+
escComment = (text) => text.replace(/-{2,}/g, (run) => [...run].join(" ")).replace(/-$/, "- ");
|
|
100496
|
+
}
|
|
100497
|
+
});
|
|
100498
|
+
|
|
100499
|
+
// src/logo-attribution.ts
|
|
100500
|
+
function logoAttributionComment(drawn) {
|
|
100501
|
+
const licensed = [...drawn].sort().map((slug) => ({ slug, logo: LOGOS[slug] })).filter((entry) => entry.logo?.license);
|
|
100502
|
+
if (licensed.length === 0) return "";
|
|
100503
|
+
const lines = [...PREAMBLE];
|
|
100504
|
+
for (const { slug, logo } of licensed) {
|
|
100505
|
+
lines.push("", `${logo.title} (${slug}) \u2014 ${logo.license}`);
|
|
100506
|
+
if (logo.copyright) lines.push(` ${logo.copyright}`);
|
|
100507
|
+
if (logo.source) lines.push(` artwork: ${logo.source}`);
|
|
100508
|
+
if (logo.licenseUrl) lines.push(` licence: ${logo.licenseUrl}`);
|
|
100509
|
+
}
|
|
100510
|
+
return `<!--
|
|
100511
|
+
${lines.map((line) => escComment(` ${line}`).trimEnd()).join("\n")}
|
|
100512
|
+
-->
|
|
100513
|
+
`;
|
|
100514
|
+
}
|
|
100515
|
+
var PREAMBLE;
|
|
100516
|
+
var init_logo_attribution = __esm({
|
|
100517
|
+
"src/logo-attribution.ts"() {
|
|
100518
|
+
"use strict";
|
|
100519
|
+
init_logos();
|
|
100520
|
+
init_xml_escape();
|
|
100521
|
+
PREAMBLE = [
|
|
100522
|
+
"This diagram contains third-party artwork under its own licence.",
|
|
100523
|
+
"Keep this notice with the file \u2014 it is the attribution those licences ask for.",
|
|
100524
|
+
"",
|
|
100525
|
+
"Every mark below is modified from the original: simple-icons redrew it as a",
|
|
100526
|
+
"single monochrome path, and cairn paints that path in the diagram's colours."
|
|
100527
|
+
];
|
|
99985
100528
|
}
|
|
99986
100529
|
});
|
|
99987
100530
|
|
|
99988
100531
|
// src/svg-render.ts
|
|
100532
|
+
function bandLines(text, from, right, fontSize) {
|
|
100533
|
+
const chars = Math.floor((right - from) / (fontSize * RENDER_CHAR_WIDTH));
|
|
100534
|
+
return bandWrap(text, Math.max(8, chars));
|
|
100535
|
+
}
|
|
100536
|
+
function bandWrap(text, maxChars) {
|
|
100537
|
+
return wrapText(text, maxChars).split("\n").flatMap((line) => {
|
|
100538
|
+
if (line.length <= maxChars) return [line];
|
|
100539
|
+
const parts = [];
|
|
100540
|
+
for (let index = 0; index < line.length; index += maxChars)
|
|
100541
|
+
parts.push(line.slice(index, index + maxChars));
|
|
100542
|
+
return parts;
|
|
100543
|
+
});
|
|
100544
|
+
}
|
|
99989
100545
|
function assignSourceHues(model, hues) {
|
|
99990
100546
|
const sourceHue = /* @__PURE__ */ new Map();
|
|
99991
100547
|
for (const flow of model.flows) {
|
|
@@ -99996,7 +100552,7 @@ function assignSourceHues(model, hues) {
|
|
|
99996
100552
|
}
|
|
99997
100553
|
function collectElementStyles(elements) {
|
|
99998
100554
|
return elements.flatMap((element) => [
|
|
99999
|
-
{ id: element.id, style: element.style,
|
|
100555
|
+
{ id: element.id, style: element.style, logo: element.logo },
|
|
100000
100556
|
...collectElementStyles(element.children)
|
|
100001
100557
|
]);
|
|
100002
100558
|
}
|
|
@@ -100038,19 +100594,39 @@ function auditRouteRepairs(deps) {
|
|
|
100038
100594
|
for (let tier = 0; tier < 5; tier++) if (a[tier] !== b[tier]) return a[tier] < b[tier];
|
|
100039
100595
|
return false;
|
|
100040
100596
|
};
|
|
100597
|
+
const flowOfBreach = (key) => {
|
|
100598
|
+
if (key.startsWith("adrift:"))
|
|
100599
|
+
return labels[Number.parseInt(key.slice("adrift:".length), 10)]?.flowId ?? null;
|
|
100600
|
+
const body = key.slice(key.indexOf(":") + 1);
|
|
100601
|
+
const cut = body.search(/[~:]/);
|
|
100602
|
+
return cut < 0 ? body : body.slice(0, cut);
|
|
100603
|
+
};
|
|
100041
100604
|
const withRepair = stateHarm();
|
|
100042
100605
|
const breachesWith = breaches();
|
|
100043
100606
|
const repairedRoutes = repaired.map((edge) => edge.pts);
|
|
100044
|
-
|
|
100045
|
-
|
|
100046
|
-
const withoutRepair = stateHarm();
|
|
100047
|
-
const breachesWithout = breaches();
|
|
100048
|
-
const breaksAPromise = [...breachesWith].some((key) => !breachesWithout.has(key));
|
|
100049
|
-
if (!breaksAPromise && !lessDamaged(withoutRepair, withRepair)) {
|
|
100607
|
+
const replacedRoutes = repaired.map((edge) => edge.repairedFrom);
|
|
100608
|
+
const applyKeep = (keep2) => {
|
|
100050
100609
|
repaired.forEach((edge, index) => {
|
|
100051
|
-
edge.pts = repairedRoutes[index];
|
|
100610
|
+
edge.pts = keep2[index] ? repairedRoutes[index] : replacedRoutes[index];
|
|
100052
100611
|
});
|
|
100053
100612
|
resettle();
|
|
100613
|
+
};
|
|
100614
|
+
applyKeep(repaired.map(() => false));
|
|
100615
|
+
const withoutRepair = stateHarm();
|
|
100616
|
+
const breachesWithout = breaches();
|
|
100617
|
+
const broken = [...breachesWith].filter((key) => !breachesWithout.has(key));
|
|
100618
|
+
if (!broken.length) {
|
|
100619
|
+
if (!lessDamaged(withoutRepair, withRepair)) applyKeep(repaired.map(() => true));
|
|
100620
|
+
for (const edge of repaired) edge.repairedFrom = void 0;
|
|
100621
|
+
return;
|
|
100622
|
+
}
|
|
100623
|
+
const blamed = new Set(broken.map(flowOfBreach).filter((id) => id !== null));
|
|
100624
|
+
const keep = repaired.map((edge) => !blamed.has(edge.id));
|
|
100625
|
+
if (keep.some(Boolean) && keep.some((kept) => !kept)) {
|
|
100626
|
+
applyKeep(keep);
|
|
100627
|
+
const partly = stateHarm();
|
|
100628
|
+
const stillBroken = [...breaches()].some((key) => !breachesWithout.has(key));
|
|
100629
|
+
if (stillBroken || lessDamaged(withoutRepair, partly)) applyKeep(repaired.map(() => false));
|
|
100054
100630
|
}
|
|
100055
100631
|
for (const edge of repaired) edge.repairedFrom = void 0;
|
|
100056
100632
|
}
|
|
@@ -100091,13 +100667,66 @@ function settleOneLabel(s, label) {
|
|
|
100091
100667
|
label.x = origin.x;
|
|
100092
100668
|
label.y = origin.y;
|
|
100093
100669
|
}
|
|
100094
|
-
function
|
|
100095
|
-
const
|
|
100670
|
+
function glyphSvg(kind, stroke, box) {
|
|
100671
|
+
const glyph = GLYPHS[kind];
|
|
100672
|
+
if (!glyph) return "";
|
|
100673
|
+
const { x, y, scale = 1 } = box;
|
|
100674
|
+
const width = round1(1.3 * scale);
|
|
100675
|
+
return glyph({
|
|
100676
|
+
x: (v) => round1(x + v * scale),
|
|
100677
|
+
y: (v) => round1(y + v * scale),
|
|
100678
|
+
r: (v) => round1(v * scale),
|
|
100679
|
+
line: `fill="none" stroke="${stroke}" stroke-width="${width}"`,
|
|
100680
|
+
stroke
|
|
100681
|
+
});
|
|
100682
|
+
}
|
|
100683
|
+
function logoSvg(mark) {
|
|
100684
|
+
const { logo, resolved, node, stroke, drawn, inset = {} } = mark;
|
|
100685
|
+
if (!logo) return "";
|
|
100686
|
+
const x = round1(node.x + node.width - (inset.right ?? LOGO_BOX.right) - LOGO_BOX.size);
|
|
100687
|
+
const y = round1(node.y + (inset.top ?? LOGO_BOX.top));
|
|
100688
|
+
if (logo.source === "file") {
|
|
100689
|
+
const href = resolved?.get(node.id);
|
|
100690
|
+
if (!href) return "";
|
|
100691
|
+
return `<image x="${x}" y="${y}" width="${LOGO_BOX.size}" height="${LOGO_BOX.size}" href="${escAttr(href)}" preserveAspectRatio="xMidYMid meet"/>
|
|
100692
|
+
`;
|
|
100693
|
+
}
|
|
100694
|
+
const builtin = Object.hasOwn(LOGOS, logo.value) ? LOGOS[logo.value] : void 0;
|
|
100695
|
+
if (!builtin) return "";
|
|
100696
|
+
drawn.add(logo.value);
|
|
100697
|
+
const scale = Math.round(LOGO_BOX.size / 24 * 1e4) / 1e4;
|
|
100698
|
+
return `<g transform="translate(${x} ${y}) scale(${scale})" fill="${stroke}"><title>${esc(builtin.title)}</title><path d="${builtin.d}"/></g>
|
|
100699
|
+
`;
|
|
100700
|
+
}
|
|
100701
|
+
function createNodeLabelHelpers(nodeFontSize) {
|
|
100096
100702
|
const centeredNodeLabel = (lines, centerX2, topBaseline, fill) => lines.map(
|
|
100097
100703
|
(line, index) => `<text x="${centerX2}" y="${topBaseline + index * (nodeFontSize + 2)}" font-size="${nodeFontSize}" text-anchor="middle" fill="${fill}">${esc(line)}</text>
|
|
100098
100704
|
`
|
|
100099
100705
|
).join("");
|
|
100100
100706
|
const centerLinesY = (top, height, lineCount) => top + height / 2 - (lineCount - 1) * (nodeFontSize + 2) / 2 + 4;
|
|
100707
|
+
const glyphLabelCenterX = (node) => node.x + GLYPH_GUTTER + (node.width - GLYPH_GUTTER) / 2;
|
|
100708
|
+
const logoLabelCenterX = (node, hasLogo) => node.x + (node.width - (hasLogo ? LOGO_GUTTER : 0)) / 2;
|
|
100709
|
+
return { centeredNodeLabel, centerLinesY, glyphLabelCenterX, logoLabelCenterX };
|
|
100710
|
+
}
|
|
100711
|
+
function createNodeRenderers(paint) {
|
|
100712
|
+
const {
|
|
100713
|
+
palette,
|
|
100714
|
+
nodeFontSize,
|
|
100715
|
+
containerFontSize,
|
|
100716
|
+
resolveStyle,
|
|
100717
|
+
elementLogo,
|
|
100718
|
+
resolvedLogos,
|
|
100719
|
+
drawnLogos
|
|
100720
|
+
} = paint;
|
|
100721
|
+
const logoFor = (node, stroke, inset) => logoSvg({
|
|
100722
|
+
logo: elementLogo.get(node.id),
|
|
100723
|
+
resolved: resolvedLogos,
|
|
100724
|
+
node,
|
|
100725
|
+
stroke,
|
|
100726
|
+
drawn: drawnLogos,
|
|
100727
|
+
inset
|
|
100728
|
+
});
|
|
100729
|
+
const { centeredNodeLabel, centerLinesY, glyphLabelCenterX, logoLabelCenterX } = createNodeLabelHelpers(nodeFontSize);
|
|
100101
100730
|
const renderContainerNode = (node) => {
|
|
100102
100731
|
const nodeStyle = resolveStyle(node.kind, node.id);
|
|
100103
100732
|
const fill = escAttr(nodeStyle.fill ?? palette.containerFill), stroke = escAttr(nodeStyle.stroke?.color ?? palette.containerStroke), text = escAttr(nodeStyle.text ?? palette.containerLabel);
|
|
@@ -100108,12 +100737,7 @@ function createNodeRenderers(paint) {
|
|
|
100108
100737
|
svg += `<text x="${node.x + 10}" y="${node.y + 18 + index * 14}" font-size="${containerFontSize}" font-weight="bold" fill="${text}">${esc(line)}</text>
|
|
100109
100738
|
`;
|
|
100110
100739
|
});
|
|
100111
|
-
|
|
100112
|
-
if (level) {
|
|
100113
|
-
const word = (style.lang === "fr" ? SEC_LEVEL_FR[level] : level) ?? level;
|
|
100114
|
-
svg += `<text x="${node.x + node.width - 9}" y="${node.y + node.height - 6}" font-size="${annot.tag}" text-anchor="end" font-weight="bold" fill="${stroke}" letter-spacing="0.5">${esc(word.toUpperCase())}</text>
|
|
100115
|
-
`;
|
|
100116
|
-
}
|
|
100740
|
+
svg += logoFor(node, stroke);
|
|
100117
100741
|
return svg;
|
|
100118
100742
|
};
|
|
100119
100743
|
const renderActor = (node, nodeStyle, lines) => {
|
|
@@ -100136,9 +100760,12 @@ function createNodeRenderers(paint) {
|
|
|
100136
100760
|
<ellipse cx="${node.x + node.width / 2}" cy="${node.y + ry}" rx="${node.width / 2}" ry="${ry}" fill="${fill}" stroke="${stroke}" stroke-width="1.3"/>
|
|
100137
100761
|
`;
|
|
100138
100762
|
const centerY2 = node.y + ry + (node.height - ry) / 2 - (lines.length - 1) * (nodeFontSize + 2) / 2 + 4;
|
|
100139
|
-
|
|
100763
|
+
const logo = logoFor(node, stroke, {
|
|
100764
|
+
top: ry + (node.height - 2 * ry - LOGO_BOX.size) / 2
|
|
100765
|
+
});
|
|
100766
|
+
return body + logo + centeredNodeLabel(
|
|
100140
100767
|
lines,
|
|
100141
|
-
node
|
|
100768
|
+
logoLabelCenterX(node, logo !== ""),
|
|
100142
100769
|
centerY2,
|
|
100143
100770
|
escAttr(nodeStyle.text ?? palette.nodeText)
|
|
100144
100771
|
);
|
|
@@ -100149,50 +100776,38 @@ function createNodeRenderers(paint) {
|
|
|
100149
100776
|
const body = `<path d="M ${node.x + rx} ${node.y} h ${node.width - 2 * rx} a ${rx} ${node.height / 2} 0 0 1 0 ${node.height} h ${-(node.width - 2 * rx)} a ${rx} ${node.height / 2} 0 0 1 0 ${-node.height}" fill="${fill}" stroke="${stroke}" stroke-width="1.3"/>
|
|
100150
100777
|
<ellipse cx="${node.x + rx}" cy="${node.y + node.height / 2}" rx="${rx}" ry="${node.height / 2}" fill="${fill}" stroke="${stroke}" stroke-width="1.3"/>
|
|
100151
100778
|
`;
|
|
100152
|
-
|
|
100779
|
+
const logo = logoFor(node, stroke, { right: LOGO_BOX.right + rx });
|
|
100780
|
+
return body + logo + centeredNodeLabel(
|
|
100153
100781
|
lines,
|
|
100154
|
-
node.x + rx + (node.width - rx) / 2,
|
|
100782
|
+
node.x + rx + (node.width - rx - (logo === "" ? 0 : LOGO_GUTTER)) / 2,
|
|
100155
100783
|
centerLinesY(node.y, node.height, lines.length),
|
|
100156
100784
|
text
|
|
100157
100785
|
);
|
|
100158
100786
|
};
|
|
100159
|
-
const
|
|
100160
|
-
const centerX2 = node.x + node.width / 2;
|
|
100161
|
-
const fill = escAttr(nodeStyle.fill ?? palette.nodeFill), stroke = escAttr(nodeStyle.stroke?.color ?? palette.nodeStroke), text = escAttr(nodeStyle.text ?? palette.nodeText);
|
|
100162
|
-
const body = `<rect x="${node.x}" y="${node.y}" width="${node.width}" height="${node.height}" rx="4" fill="${fill}" stroke="${stroke}" stroke-width="1.3"/>
|
|
100163
|
-
<path d="M ${round1(node.x + 8)} ${round1(node.y + 8)} L ${round1(node.x + 22)} ${round1(node.y + 8)} Q ${round1(node.x + 24)} ${round1(node.y + 13)} ${round1(node.x + 15)} ${round1(node.y + 20)} Q ${round1(node.x + 6)} ${round1(node.y + 13)} ${round1(node.x + 8)} ${round1(node.y + 8)}" fill="none" stroke="${stroke}" stroke-width="1.3"/>
|
|
100164
|
-
`;
|
|
100165
|
-
return body + centeredNodeLabel(lines, centerX2 + 10, centerLinesY(node.y, node.height, lines.length), text);
|
|
100166
|
-
};
|
|
100167
|
-
const renderAuth = (node, nodeStyle, lines) => {
|
|
100168
|
-
const centerX2 = node.x + node.width / 2;
|
|
100169
|
-
const fill = escAttr(nodeStyle.fill ?? palette.nodeFill), stroke = escAttr(nodeStyle.stroke?.color ?? palette.nodeStroke), text = escAttr(nodeStyle.text ?? palette.nodeText);
|
|
100170
|
-
const body = `<rect x="${node.x}" y="${node.y}" width="${node.width}" height="${node.height}" rx="4" fill="${fill}" stroke="${stroke}" stroke-width="1.3"/>
|
|
100171
|
-
<rect x="${node.x + 6}" y="${node.y + 6}" width="18" height="14" rx="3" fill="none" stroke="${stroke}" stroke-width="1.3"/>
|
|
100172
|
-
<path d="M ${node.x + 10} ${node.y + 9} v -4 a 5 5 0 0 1 10 0 v 4" fill="none" stroke="${stroke}" stroke-width="1.3"/>
|
|
100173
|
-
<circle cx="${node.x + 15}" cy="${node.y + 16}" r="2.5" fill="${stroke}"/>
|
|
100174
|
-
`;
|
|
100175
|
-
return body + centeredNodeLabel(lines, centerX2 + 10, centerLinesY(node.y, node.height, lines.length), text);
|
|
100176
|
-
};
|
|
100177
|
-
const renderPlainBox = (node, nodeStyle, lines) => {
|
|
100787
|
+
const renderGlyphBox = (node, nodeStyle, lines) => {
|
|
100178
100788
|
const fill = escAttr(nodeStyle.fill ?? palette.nodeFill), stroke = escAttr(nodeStyle.stroke?.color ?? palette.nodeStroke), text = escAttr(nodeStyle.text ?? palette.nodeText);
|
|
100179
100789
|
const dash = dashArray(nodeStyle.stroke?.style);
|
|
100180
100790
|
const body = `<rect x="${node.x}" y="${node.y}" width="${node.width}" height="${node.height}" rx="4" fill="${fill}" stroke="${stroke}" stroke-width="${nodeStyle.stroke?.width ?? 1.3}"${dash ? ` stroke-dasharray="${dash}"` : ""}/>
|
|
100181
|
-
|
|
100791
|
+
` + glyphSvg(node.kind, stroke, {
|
|
100792
|
+
x: node.x + GLYPH_BOX.left,
|
|
100793
|
+
y: node.y + GLYPH_BOX.top
|
|
100794
|
+
}) + "\n";
|
|
100182
100795
|
return body + centeredNodeLabel(
|
|
100183
100796
|
lines,
|
|
100184
|
-
node
|
|
100797
|
+
glyphLabelCenterX(node),
|
|
100185
100798
|
centerLinesY(node.y, node.height, lines.length),
|
|
100186
100799
|
text
|
|
100187
100800
|
);
|
|
100188
100801
|
};
|
|
100189
|
-
const
|
|
100802
|
+
const renderPlainBox = (node, nodeStyle, lines) => {
|
|
100190
100803
|
const fill = escAttr(nodeStyle.fill ?? palette.nodeFill), stroke = escAttr(nodeStyle.stroke?.color ?? palette.nodeStroke), text = escAttr(nodeStyle.text ?? palette.nodeText);
|
|
100191
|
-
const
|
|
100804
|
+
const dash = dashArray(nodeStyle.stroke?.style);
|
|
100805
|
+
const logo = logoFor(node, stroke);
|
|
100806
|
+
const body = `<rect x="${node.x}" y="${node.y}" width="${node.width}" height="${node.height}" rx="4" fill="${fill}" stroke="${stroke}" stroke-width="${nodeStyle.stroke?.width ?? 1.3}"${dash ? ` stroke-dasharray="${dash}"` : ""}/>
|
|
100192
100807
|
`;
|
|
100193
|
-
return body + centeredNodeLabel(
|
|
100808
|
+
return body + logo + centeredNodeLabel(
|
|
100194
100809
|
lines,
|
|
100195
|
-
node
|
|
100810
|
+
logoLabelCenterX(node, logo !== ""),
|
|
100196
100811
|
centerLinesY(node.y, node.height, lines.length),
|
|
100197
100812
|
text
|
|
100198
100813
|
);
|
|
@@ -100207,14 +100822,8 @@ function createNodeRenderers(paint) {
|
|
|
100207
100822
|
return renderDatastore(node, nodeStyle, lines);
|
|
100208
100823
|
case "queue":
|
|
100209
100824
|
return renderQueue(node, nodeStyle, lines);
|
|
100210
|
-
case "gateway":
|
|
100211
|
-
return renderGateway(node, nodeStyle, lines);
|
|
100212
|
-
case "auth":
|
|
100213
|
-
return renderAuth(node, nodeStyle, lines);
|
|
100214
|
-
case "idp":
|
|
100215
|
-
return renderIdp(node, nodeStyle, lines);
|
|
100216
100825
|
default:
|
|
100217
|
-
return renderPlainBox(node, nodeStyle, lines);
|
|
100826
|
+
return GLYPHS[node.kind] ? renderGlyphBox(node, nodeStyle, lines) : renderPlainBox(node, nodeStyle, lines);
|
|
100218
100827
|
}
|
|
100219
100828
|
};
|
|
100220
100829
|
return { renderContainerNode, renderLeafNode };
|
|
@@ -100281,15 +100890,20 @@ function createLabelSettler(deps) {
|
|
|
100281
100890
|
const a = edge.pts[index];
|
|
100282
100891
|
const b = edge.pts[index + 1];
|
|
100283
100892
|
const vertical = Math.abs(a.x - b.x) < Math.abs(a.y - b.y);
|
|
100284
|
-
const
|
|
100285
|
-
const
|
|
100286
|
-
|
|
100287
|
-
|
|
100288
|
-
|
|
100289
|
-
|
|
100290
|
-
|
|
100291
|
-
|
|
100292
|
-
|
|
100893
|
+
const size = vertical ? label.height : label.width;
|
|
100894
|
+
const start = vertical ? Math.min(a.y, b.y) : Math.min(a.x, b.x);
|
|
100895
|
+
const end = vertical ? Math.max(a.y, b.y) : Math.max(a.x, b.x);
|
|
100896
|
+
const seat = vertical ? (a.y + b.y) / 2 - lead : (a.x + b.x) / 2 - label.width / 2;
|
|
100897
|
+
const back = Math.max(0, seat - start);
|
|
100898
|
+
const ahead = Math.max(0, end - size - seat);
|
|
100899
|
+
if (back === 0 && ahead === 0) continue;
|
|
100900
|
+
for (const fraction of [0.25, 0.5, 0.75, 1])
|
|
100901
|
+
for (const room of [-back, ahead]) {
|
|
100902
|
+
const shift = room * fraction;
|
|
100903
|
+
seats.push(
|
|
100904
|
+
vertical ? { x: (a.x + b.x) / 2 - label.width / 2, y: seat + shift } : { x: seat + shift, y: (a.y + b.y) / 2 - lead }
|
|
100905
|
+
);
|
|
100906
|
+
}
|
|
100293
100907
|
}
|
|
100294
100908
|
return seats;
|
|
100295
100909
|
};
|
|
@@ -100336,27 +100950,103 @@ function createLabelSettler(deps) {
|
|
|
100336
100950
|
settleLabelPositions
|
|
100337
100951
|
};
|
|
100338
100952
|
}
|
|
100953
|
+
function kindKeysSvg(paint, kinds, y, x) {
|
|
100954
|
+
const { scene, palette, legendNames, scaled, resolveStyle } = paint;
|
|
100955
|
+
let svg = "";
|
|
100956
|
+
let keyX = x;
|
|
100957
|
+
let bandY = y;
|
|
100958
|
+
const LINE_H = scaled(12);
|
|
100959
|
+
for (const kind of kinds) {
|
|
100960
|
+
const nodeStyle = resolveStyle(kind, "");
|
|
100961
|
+
if (kind === "actor") {
|
|
100962
|
+
const stroke = nodeStyle.stroke?.color ?? palette.actorStroke;
|
|
100963
|
+
svg += `<circle cx="${keyX + scaled(13)}" cy="${bandY + scaled(5)}" r="${scaled(3)}" fill="none" stroke="${stroke}" stroke-width="1.2"/>
|
|
100964
|
+
`;
|
|
100965
|
+
svg += `<path d="M ${keyX + scaled(8)} ${bandY + scaled(15)} q ${scaled(5)} ${scaled(-7)} ${scaled(10)} 0" fill="none" stroke="${stroke}" stroke-width="1.2"/>
|
|
100966
|
+
`;
|
|
100967
|
+
} else {
|
|
100968
|
+
const dash = dashArray(nodeStyle.stroke?.style);
|
|
100969
|
+
const stroke = nodeStyle.stroke?.color ?? palette.nodeStroke;
|
|
100970
|
+
svg += `<rect x="${keyX}" y="${bandY + 2}" width="${scaled(26)}" height="${scaled(14)}" rx="3" fill="${nodeStyle.fill ?? palette.nodeFill}" stroke="${escAttr(stroke)}"${dash ? ` stroke-dasharray="${dash}"` : ""}/>
|
|
100971
|
+
`;
|
|
100972
|
+
const glyphScale = (scaled(14) - scaled(4)) / GLYPH_BOX.height;
|
|
100973
|
+
svg += glyphSvg(kind, escAttr(stroke), {
|
|
100974
|
+
x: keyX + (scaled(26) - GLYPH_BOX.width * glyphScale) / 2,
|
|
100975
|
+
y: bandY + 2 + (scaled(14) - GLYPH_BOX.height * glyphScale) / 2,
|
|
100976
|
+
scale: glyphScale
|
|
100977
|
+
});
|
|
100978
|
+
}
|
|
100979
|
+
const name = legendNames[kind];
|
|
100980
|
+
const lines = bandLines(name, keyX + scaled(32), bandRightMargin(scene), scaled(10));
|
|
100981
|
+
for (const [row, line] of lines.entries())
|
|
100982
|
+
svg += `<text x="${keyX + scaled(32)}" y="${bandY + scaled(13) + row * LINE_H}" font-size="${scaled(10)}" fill="${palette.bandText}">${esc(line)}</text>
|
|
100983
|
+
`;
|
|
100984
|
+
keyX += scaled(40) + Math.ceil(name.length * scaled(10) * RENDER_CHAR_WIDTH) + scaled(24);
|
|
100985
|
+
bandY += (lines.length - 1) * LINE_H;
|
|
100986
|
+
if (lines.length > 1 || keyX > scene.width - 220) {
|
|
100987
|
+
keyX = x;
|
|
100988
|
+
bandY += scaled(22);
|
|
100989
|
+
}
|
|
100990
|
+
}
|
|
100991
|
+
return { svg, bandY: kinds.length ? bandY + scaled(24) : bandY };
|
|
100992
|
+
}
|
|
100993
|
+
function lineStyleKeysSvg(paint, y, x) {
|
|
100994
|
+
const { scene, model, style, palette, legendLineStyles, scaled, defaultEdgeColor, markerName } = paint;
|
|
100995
|
+
const styles = LINE_STYLES2.filter(
|
|
100996
|
+
(lineStyle) => model.flows.some((flow) => lineStyleOf(flow, style) === lineStyle)
|
|
100997
|
+
);
|
|
100998
|
+
if (styles.length < 2) return { svg: "", bandY: y };
|
|
100999
|
+
const maxX = scene.width - 20;
|
|
101000
|
+
let svg = "";
|
|
101001
|
+
let keyX = x;
|
|
101002
|
+
let bandY = y;
|
|
101003
|
+
const LINE_H = scaled(12);
|
|
101004
|
+
for (const [index, lineStyle] of styles.entries()) {
|
|
101005
|
+
const meaning = legendLineStyles[lineStyle];
|
|
101006
|
+
const keyWidth = scaled(40) + Math.ceil(meaning.length * scaled(10) * RENDER_CHAR_WIDTH) + scaled(24);
|
|
101007
|
+
if (keyX > x && keyX + keyWidth > maxX) {
|
|
101008
|
+
keyX = x;
|
|
101009
|
+
bandY += scaled(22);
|
|
101010
|
+
}
|
|
101011
|
+
const dash = dashArray(lineStyle);
|
|
101012
|
+
svg += `<line x1="${keyX}" y1="${bandY + 8}" x2="${keyX + scaled(26)}" y2="${bandY + 8}" stroke="${escAttr(defaultEdgeColor)}" stroke-width="1.3"${dash ? ` stroke-dasharray="${dash}"` : ""} marker-end="url(#${markerName(defaultEdgeColor)})"/>
|
|
101013
|
+
`;
|
|
101014
|
+
const lines = bandLines(meaning, keyX + scaled(40), maxX, scaled(10));
|
|
101015
|
+
for (const [row, line] of lines.entries())
|
|
101016
|
+
svg += `<text x="${keyX + scaled(32)}" y="${bandY + scaled(12) + row * LINE_H}" font-size="${scaled(10)}" fill="${palette.bandText}">${esc(line)}</text>
|
|
101017
|
+
`;
|
|
101018
|
+
if (lines.length === 1) {
|
|
101019
|
+
keyX += keyWidth;
|
|
101020
|
+
continue;
|
|
101021
|
+
}
|
|
101022
|
+
bandY += (lines.length - 1) * LINE_H;
|
|
101023
|
+
if (index < styles.length - 1) {
|
|
101024
|
+
bandY += scaled(22);
|
|
101025
|
+
keyX = x;
|
|
101026
|
+
}
|
|
101027
|
+
}
|
|
101028
|
+
return { svg, bandY: bandY + scaled(24) };
|
|
101029
|
+
}
|
|
100339
101030
|
function createBandRenderers(paint) {
|
|
100340
101031
|
const {
|
|
100341
101032
|
scene,
|
|
100342
101033
|
model,
|
|
100343
|
-
view,
|
|
100344
101034
|
style,
|
|
100345
101035
|
palette,
|
|
100346
101036
|
annot,
|
|
100347
101037
|
ui,
|
|
100348
|
-
legendNames,
|
|
100349
101038
|
legendFlowLabel,
|
|
100350
101039
|
scaled,
|
|
100351
101040
|
objectName,
|
|
100352
|
-
resolveStyle,
|
|
100353
101041
|
defaultEdgeColor,
|
|
100354
101042
|
markerName,
|
|
100355
101043
|
numbered
|
|
100356
101044
|
} = paint;
|
|
100357
101045
|
let bandY = scene.height;
|
|
100358
101046
|
let bandsSvg = "";
|
|
100359
|
-
const
|
|
101047
|
+
const rightMargin = bandRightMargin(scene);
|
|
101048
|
+
const stackedTitle = rightMargin - BAND_CONTENT_X < MIN_BAND_TEXT;
|
|
101049
|
+
const contentX = stackedTitle ? 20 : BAND_CONTENT_X;
|
|
100360
101050
|
const chip = (x, y, name) => {
|
|
100361
101051
|
const width = chipW(name, annot.scale);
|
|
100362
101052
|
return {
|
|
@@ -100371,7 +101061,7 @@ function createBandRenderers(paint) {
|
|
|
100371
101061
|
`;
|
|
100372
101062
|
bandsSvg += `<text x="20" y="${bandY + scaled(32)}" font-size="${scaled(11)}" font-weight="bold" fill="${palette.bandTitle}">${esc(title)}</text>
|
|
100373
101063
|
`;
|
|
100374
|
-
bandY += scaled(20);
|
|
101064
|
+
bandY += scaled(stackedTitle ? 34 : 20);
|
|
100375
101065
|
};
|
|
100376
101066
|
const flowChipsSvg = (flow, startX, startY) => {
|
|
100377
101067
|
if (!flow.objects?.length) return "";
|
|
@@ -100403,7 +101093,7 @@ function createBandRenderers(paint) {
|
|
|
100403
101093
|
const textW = Math.max(60, colW - BADGE - (chipsW ? chipsW + 6 : 0));
|
|
100404
101094
|
const maxChars = Math.max(6, Math.floor(textW / (scaled(10) * RENDER_CHAR_WIDTH)));
|
|
100405
101095
|
const raw = (flow.label ?? "") + (tech ? " " + tech : "");
|
|
100406
|
-
const lines = raw.split("\n").flatMap((segment) =>
|
|
101096
|
+
const lines = raw.split("\n").flatMap((segment) => bandWrap(segment, maxChars));
|
|
100407
101097
|
return { flow, lines };
|
|
100408
101098
|
});
|
|
100409
101099
|
const rows = Math.ceil(entries.length / cols);
|
|
@@ -100430,59 +101120,50 @@ function createBandRenderers(paint) {
|
|
|
100430
101120
|
};
|
|
100431
101121
|
const renderObjectsBand = () => {
|
|
100432
101122
|
beginBand(ui.objects);
|
|
100433
|
-
|
|
100434
|
-
|
|
101123
|
+
const LINE_H = scaled(12);
|
|
101124
|
+
const chipRow = (name, reading) => {
|
|
101125
|
+
const chipResult = chip(contentX, bandY + 2, name);
|
|
100435
101126
|
bandsSvg += chipResult.svg;
|
|
100436
|
-
|
|
100437
|
-
|
|
100438
|
-
|
|
100439
|
-
|
|
100440
|
-
}
|
|
100441
|
-
const keyChip = chip(contentX, bandY + 2, ui.businessObject);
|
|
100442
|
-
bandsSvg += keyChip.svg;
|
|
100443
|
-
bandsSvg += `<text x="${contentX + keyChip.width + 10}" y="${bandY + scaled(13)}" font-size="${scaled(10)}" fill="${palette.bandMuted}">${esc(ui.carriedByFlow)}</text>
|
|
101127
|
+
const textX = contentX + chipResult.width + 10;
|
|
101128
|
+
const lines = reading ? bandLines(reading, textX, rightMargin, scaled(10)) : [];
|
|
101129
|
+
for (const [row, line] of lines.entries())
|
|
101130
|
+
bandsSvg += `<text x="${textX}" y="${bandY + scaled(13) + row * LINE_H}" font-size="${scaled(10)}" fill="${palette.bandMuted}">${esc(line)}</text>
|
|
100444
101131
|
`;
|
|
100445
|
-
|
|
101132
|
+
bandY += scaled(24) + Math.max(0, lines.length - 1) * LINE_H;
|
|
101133
|
+
};
|
|
101134
|
+
for (const bo of model.businessObjects)
|
|
101135
|
+
chipRow(bo.name, bo.description ? `\u2014 ${bo.description}` : "");
|
|
101136
|
+
if (model.flows.some((flow) => flow.objects?.length))
|
|
101137
|
+
chipRow(ui.businessObject, ui.carriedByFlow);
|
|
101138
|
+
bandY += 6;
|
|
100446
101139
|
};
|
|
100447
101140
|
const renderLegendBand = () => {
|
|
101141
|
+
const kinds = legendKinds(paint);
|
|
101142
|
+
if (!kinds.length && !model.flows.length && !model.legendNotes.length) return;
|
|
100448
101143
|
beginBand(ui.legend);
|
|
100449
|
-
|
|
100450
|
-
|
|
100451
|
-
|
|
100452
|
-
)
|
|
100453
|
-
|
|
100454
|
-
const nodeStyle = resolveStyle(kind, "");
|
|
100455
|
-
if (kind === "actor") {
|
|
100456
|
-
const stroke = nodeStyle.stroke?.color ?? palette.actorStroke;
|
|
100457
|
-
bandsSvg += `<circle cx="${lx + scaled(13)}" cy="${bandY + scaled(5)}" r="${scaled(3)}" fill="none" stroke="${stroke}" stroke-width="1.2"/>
|
|
100458
|
-
`;
|
|
100459
|
-
bandsSvg += `<path d="M ${lx + scaled(8)} ${bandY + scaled(15)} q ${scaled(5)} ${scaled(-7)} ${scaled(10)} 0" fill="none" stroke="${stroke}" stroke-width="1.2"/>
|
|
100460
|
-
`;
|
|
100461
|
-
} else {
|
|
100462
|
-
const dash = dashArray(nodeStyle.stroke?.style);
|
|
100463
|
-
bandsSvg += `<rect x="${lx}" y="${bandY + 2}" width="${scaled(26)}" height="${scaled(14)}" rx="3" fill="${nodeStyle.fill ?? palette.nodeFill}" stroke="${nodeStyle.stroke?.color ?? palette.nodeStroke}"${dash ? ` stroke-dasharray="${dash}"` : ""}/>
|
|
101144
|
+
const kindKeys = kindKeysSvg(paint, kinds, bandY, contentX);
|
|
101145
|
+
bandsSvg += kindKeys.svg;
|
|
101146
|
+
bandY = kindKeys.bandY;
|
|
101147
|
+
if (model.flows.length) {
|
|
101148
|
+
bandsSvg += `<line x1="${contentX}" y1="${bandY + 8}" x2="${contentX + scaled(26)}" y2="${bandY + 8}" stroke="${escAttr(defaultEdgeColor)}" stroke-width="1.3" marker-end="url(#${markerName(defaultEdgeColor)})"/>
|
|
100464
101149
|
`;
|
|
100465
|
-
|
|
100466
|
-
const
|
|
100467
|
-
|
|
101150
|
+
const flowLabelText = (numbered ? legendFlowLabel + " \u2014 " + ui.numberedSuffix : legendFlowLabel) + (style.flowColor === "by-source" ? style.lang === "fr" ? " \u2014 couleur = source" : " \u2014 colour = source" : "");
|
|
101151
|
+
const flowKeyX = contentX + scaled(32);
|
|
101152
|
+
const flowKeyLines = bandLines(flowLabelText, flowKeyX, rightMargin, scaled(10));
|
|
101153
|
+
for (const [row, line] of flowKeyLines.entries())
|
|
101154
|
+
bandsSvg += `<text x="${flowKeyX}" y="${bandY + scaled(12) + row * scaled(12)}" font-size="${scaled(10)}" fill="${palette.bandText}">${esc(line)}</text>
|
|
100468
101155
|
`;
|
|
100469
|
-
|
|
100470
|
-
|
|
100471
|
-
|
|
100472
|
-
|
|
100473
|
-
}
|
|
101156
|
+
bandY += scaled(24) + (flowKeyLines.length - 1) * scaled(12);
|
|
101157
|
+
const lineStyleKeys = lineStyleKeysSvg(paint, bandY, contentX);
|
|
101158
|
+
bandsSvg += lineStyleKeys.svg;
|
|
101159
|
+
bandY = lineStyleKeys.bandY;
|
|
100474
101160
|
}
|
|
100475
|
-
bandY += scaled(24);
|
|
100476
|
-
bandsSvg += `<line x1="${contentX}" y1="${bandY + 8}" x2="${contentX + scaled(26)}" y2="${bandY + 8}" stroke="${escAttr(defaultEdgeColor)}" stroke-width="1.3" marker-end="url(#${markerName(defaultEdgeColor)})"/>
|
|
100477
|
-
`;
|
|
100478
|
-
const flowLabelText = (numbered ? legendFlowLabel + " \u2014 " + ui.numberedSuffix : legendFlowLabel) + (style.flowColor === "by-source" ? style.lang === "fr" ? " \u2014 couleur = source" : " \u2014 colour = source" : "");
|
|
100479
|
-
bandsSvg += `<text x="${contentX + scaled(32)}" y="${bandY + scaled(12)}" font-size="${scaled(10)}" fill="${palette.bandText}">${esc(flowLabelText)}</text>
|
|
100480
|
-
`;
|
|
100481
|
-
bandY += scaled(24);
|
|
100482
101161
|
for (const note of model.legendNotes) {
|
|
100483
|
-
|
|
101162
|
+
const lines = bandLines(note, contentX, rightMargin, scaled(10));
|
|
101163
|
+
for (const [row, line] of lines.entries())
|
|
101164
|
+
bandsSvg += `<text x="${contentX}" y="${bandY + scaled(12) + row * scaled(12)}" font-size="${scaled(10)}" fill="${palette.bandText}" font-style="italic">${esc(line)}</text>
|
|
100484
101165
|
`;
|
|
100485
|
-
bandY += scaled(20);
|
|
101166
|
+
bandY += scaled(20) + (lines.length - 1) * scaled(12);
|
|
100486
101167
|
}
|
|
100487
101168
|
};
|
|
100488
101169
|
return {
|
|
@@ -100581,7 +101262,7 @@ function createEdgePainter(paint) {
|
|
|
100581
101262
|
const flowStyle = flow?.style;
|
|
100582
101263
|
const color = flowColorOf(flow);
|
|
100583
101264
|
const headColor = style.flowColor === "by-source" ? color : defaultEdgeColor;
|
|
100584
|
-
const dash = dashArray(
|
|
101265
|
+
const dash = dashArray(lineStyleOf(flow, style));
|
|
100585
101266
|
const width = flowStyle?.stroke?.width ?? style.flowStroke.width;
|
|
100586
101267
|
return `<path d="${edgePath(edge.pts)}" fill="none" stroke="${escAttr(color)}" stroke-width="${width}"${dash ? ` stroke-dasharray="${dash}"` : ""} marker-end="url(#${markerName(headColor)})"/>
|
|
100587
101268
|
`;
|
|
@@ -100597,7 +101278,28 @@ function createEdgePainter(paint) {
|
|
|
100597
101278
|
};
|
|
100598
101279
|
return { renderEdgePath, renderEdgeLabels };
|
|
100599
101280
|
}
|
|
100600
|
-
function
|
|
101281
|
+
function svgDocument(args2) {
|
|
101282
|
+
const {
|
|
101283
|
+
width,
|
|
101284
|
+
height,
|
|
101285
|
+
fontFamily,
|
|
101286
|
+
background,
|
|
101287
|
+
arrowMarkers,
|
|
101288
|
+
markerSize,
|
|
101289
|
+
body,
|
|
101290
|
+
bandsSvg,
|
|
101291
|
+
attribution
|
|
101292
|
+
} = args2;
|
|
101293
|
+
const markers = [...arrowMarkers].map(
|
|
101294
|
+
([color, markerName]) => `<marker id="${markerName}" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="${markerSize}" markerHeight="${markerSize}" orient="auto-start-reverse">
|
|
101295
|
+
<path d="M0,0 L10,5 L0,10 z" fill="${escAttr(color)}"/></marker>`
|
|
101296
|
+
).join("\n");
|
|
101297
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" font-family="${escAttr(fontFamily)},Arial,sans-serif">
|
|
101298
|
+
${attribution}<defs>${markers}</defs>
|
|
101299
|
+
<rect width="${width}" height="${height}" fill="${escAttr(background)}"/>
|
|
101300
|
+
` + body + bandsSvg + "</svg>\n";
|
|
101301
|
+
}
|
|
101302
|
+
function render(model, view, scene, options) {
|
|
100601
101303
|
const style = model.style;
|
|
100602
101304
|
const fonts = fontSizes(style.font.size);
|
|
100603
101305
|
const { edge: edgeFontSize, node: nodeFontSize, cont: containerFontSize } = fonts;
|
|
@@ -100613,10 +101315,10 @@ function render(model, view, scene) {
|
|
|
100613
101315
|
chipTextDy: round1(11 * fonts.scale)
|
|
100614
101316
|
};
|
|
100615
101317
|
const scaled = (n) => round1(n * fonts.scale);
|
|
100616
|
-
const { palette, kinds: kindDefaults
|
|
100617
|
-
const
|
|
101318
|
+
const { palette, kinds: kindDefaults } = options?.theme ? themeFromSpec(options.theme, view) : themeFor(style.theme, view);
|
|
101319
|
+
const onDarkGround = options?.theme ? options.theme.dark === true : isDarkTheme(style.theme);
|
|
100618
101320
|
const defaultEdgeColor = style.flowStrokeColorSet ? style.flowStroke.color : style.accent ?? palette.edge;
|
|
100619
|
-
const sourceHue = style.flowColor === "by-source" ? assignSourceHues(model, flowPalette[
|
|
101321
|
+
const sourceHue = style.flowColor === "by-source" ? assignSourceHues(model, flowPalette[onDarkGround ? "dark" : "light"]) : /* @__PURE__ */ new Map();
|
|
100620
101322
|
const flowColorOf = (flow) => flow?.style?.stroke?.color ?? (style.flowColor === "by-source" ? sourceHue.get(flow?.from ?? "") ?? defaultEdgeColor : defaultEdgeColor);
|
|
100621
101323
|
const arrowMarkers = /* @__PURE__ */ new Map();
|
|
100622
101324
|
const markerName = (color) => {
|
|
@@ -100633,16 +101335,15 @@ function render(model, view, scene) {
|
|
|
100633
101335
|
const ui = UI[style.lang] ?? UI.en;
|
|
100634
101336
|
const legendNames = style.lang === "fr" ? view.legendNamesFr : view.legendNames;
|
|
100635
101337
|
const legendFlowLabel = style.lang === "fr" ? view.legendFlowLabelFr : view.legendFlowLabel;
|
|
101338
|
+
const legendLineStyles = style.lang === "fr" ? view.legendLineStylesFr : view.legendLineStyles;
|
|
100636
101339
|
const elementStyle = /* @__PURE__ */ new Map();
|
|
100637
|
-
const
|
|
101340
|
+
const elementLogo = /* @__PURE__ */ new Map();
|
|
100638
101341
|
for (const entry of collectElementStyles(model.elements)) {
|
|
100639
101342
|
elementStyle.set(entry.id, entry.style);
|
|
100640
|
-
|
|
101343
|
+
if (entry.logo) elementLogo.set(entry.id, entry.logo);
|
|
100641
101344
|
}
|
|
100642
101345
|
const resolveStyle = (kind, id) => {
|
|
100643
|
-
|
|
100644
|
-
const level = elementAttr.get(id);
|
|
100645
|
-
if (kind === "trust-zone" && level && levelDefaults?.[level]) base = levelDefaults[level];
|
|
101346
|
+
const base = kindDefaults[kind] ?? {};
|
|
100646
101347
|
const perKind = style.kind[kind] ?? {};
|
|
100647
101348
|
const inline = elementStyle.get(id) ?? {};
|
|
100648
101349
|
return {
|
|
@@ -100677,14 +101378,16 @@ function render(model, view, scene) {
|
|
|
100677
101378
|
});
|
|
100678
101379
|
const overlapsAfter = countLabelOverlaps();
|
|
100679
101380
|
compactVertical(scene);
|
|
101381
|
+
fitCanvas(scene);
|
|
101382
|
+
const drawnLogos = /* @__PURE__ */ new Set();
|
|
100680
101383
|
const { renderContainerNode, renderLeafNode } = createNodeRenderers({
|
|
100681
101384
|
palette,
|
|
100682
|
-
style,
|
|
100683
|
-
annot,
|
|
100684
101385
|
nodeFontSize,
|
|
100685
101386
|
containerFontSize,
|
|
100686
101387
|
resolveStyle,
|
|
100687
|
-
|
|
101388
|
+
elementLogo,
|
|
101389
|
+
resolvedLogos: options?.logos,
|
|
101390
|
+
drawnLogos
|
|
100688
101391
|
});
|
|
100689
101392
|
const { renderEdgePath, renderEdgeLabels } = createEdgePainter({
|
|
100690
101393
|
scene,
|
|
@@ -100715,6 +101418,7 @@ function render(model, view, scene) {
|
|
|
100715
101418
|
ui,
|
|
100716
101419
|
legendNames,
|
|
100717
101420
|
legendFlowLabel,
|
|
101421
|
+
legendLineStyles,
|
|
100718
101422
|
scaled,
|
|
100719
101423
|
objectName,
|
|
100720
101424
|
resolveStyle,
|
|
@@ -100727,24 +101431,28 @@ function render(model, view, scene) {
|
|
|
100727
101431
|
if (style.legend === "auto") bands.renderLegendBand();
|
|
100728
101432
|
const bandsSvg = bands.bandsSvg();
|
|
100729
101433
|
const bandY = bands.bandY();
|
|
100730
|
-
const
|
|
100731
|
-
const totalHeight = bandY > viewHeight ? bandY + 14 : viewHeight;
|
|
100732
|
-
const markerSize = style.arrows === "large" ? round1(11 * fonts.scale) : 7;
|
|
101434
|
+
const viewHeight = scene.height;
|
|
100733
101435
|
if (arrowMarkers.size === 0) markerName(defaultEdgeColor);
|
|
100734
|
-
const
|
|
100735
|
-
|
|
100736
|
-
|
|
100737
|
-
|
|
100738
|
-
|
|
100739
|
-
|
|
100740
|
-
|
|
100741
|
-
|
|
101436
|
+
const svg = svgDocument({
|
|
101437
|
+
width: scene.width,
|
|
101438
|
+
height: bandY > viewHeight ? bandY + 14 : viewHeight,
|
|
101439
|
+
fontFamily: style.font.family,
|
|
101440
|
+
background: style.background ?? palette.background,
|
|
101441
|
+
arrowMarkers,
|
|
101442
|
+
markerSize: style.arrows === "large" ? round1(11 * fonts.scale) : 7,
|
|
101443
|
+
body,
|
|
101444
|
+
bandsSvg,
|
|
101445
|
+
// Read after the body is built, so it names the marks that were painted
|
|
101446
|
+
// rather than the ones the model asked for.
|
|
101447
|
+
attribution: logoAttributionComment(drawnLogos)
|
|
101448
|
+
});
|
|
100742
101449
|
return { svg, overlapsBefore, overlapsAfter };
|
|
100743
101450
|
}
|
|
100744
|
-
var HOP_RADIUS, LABEL_HALO, RENDER_CHAR_WIDTH,
|
|
101451
|
+
var HOP_RADIUS, LABEL_HALO, RENDER_CHAR_WIDTH, BAND_CONTENT_X, MIN_BAND_TEXT, bandRightMargin, dashArray, lineStyleOf, round1, ADRIFT_SQ, ATTACHED_SQ, GLYPH_BOX, GLYPHS, LOGO_BOX, legendKinds;
|
|
100745
101452
|
var init_svg_render = __esm({
|
|
100746
101453
|
"src/svg-render.ts"() {
|
|
100747
101454
|
"use strict";
|
|
101455
|
+
init_views();
|
|
100748
101456
|
init_themes();
|
|
100749
101457
|
init_localization();
|
|
100750
101458
|
init_xml_escape();
|
|
@@ -100755,19 +101463,283 @@ var init_svg_render = __esm({
|
|
|
100755
101463
|
init_route_detour();
|
|
100756
101464
|
init_readability();
|
|
100757
101465
|
init_text_metrics();
|
|
101466
|
+
init_logo_attribution();
|
|
101467
|
+
init_logos();
|
|
100758
101468
|
HOP_RADIUS = 5;
|
|
100759
101469
|
LABEL_HALO = 4;
|
|
100760
101470
|
RENDER_CHAR_WIDTH = 0.52;
|
|
100761
|
-
|
|
100762
|
-
|
|
100763
|
-
|
|
100764
|
-
restricted: "restreint",
|
|
100765
|
-
secret: "secret"
|
|
100766
|
-
};
|
|
101471
|
+
BAND_CONTENT_X = 150;
|
|
101472
|
+
MIN_BAND_TEXT = 200;
|
|
101473
|
+
bandRightMargin = (scene) => scene.width - 20;
|
|
100767
101474
|
dashArray = (lineStyle) => lineStyle === "dashed" ? "5 3" : lineStyle === "dotted" ? "2 2.5" : void 0;
|
|
101475
|
+
lineStyleOf = (flow, style) => flow?.style?.stroke?.style ?? flow?.lineStyle ?? style.flowStroke.style;
|
|
100768
101476
|
round1 = (n) => Math.round(n * 10) / 10;
|
|
100769
101477
|
ADRIFT_SQ = 20 * 20;
|
|
100770
101478
|
ATTACHED_SQ = 6 * 6;
|
|
101479
|
+
GLYPH_BOX = { width: 18, height: 16, left: 6, top: 7 };
|
|
101480
|
+
GLYPHS = {
|
|
101481
|
+
// Padlock: authentication is a check something must pass.
|
|
101482
|
+
auth: ({ x, y, r, line, stroke }) => `<rect x="${x(3)}" y="${y(7)}" width="${r(12)}" height="${r(9)}" rx="${r(2)}" ${line}/><path d="M ${x(6)} ${y(7)} v ${-r(3)} a ${r(3)} ${r(3)} 0 0 1 ${r(6)} 0 v ${r(3)}" ${line}/><circle cx="${x(9)}" cy="${y(11)}" r="${r(1.5)}" fill="${stroke}"/>`,
|
|
101483
|
+
// Two posts with traffic passing between them: a gateway routes, it does not block.
|
|
101484
|
+
gateway: ({ x, y, r, line }) => `<path d="M ${x(2)} ${y(1)} V ${y(15)} M ${x(16)} ${y(1)} V ${y(15)}" ${line}/><path d="M ${x(4)} ${y(8)} H ${x(14)}" ${line}/><path d="M ${x(11)} ${y(5)} l ${r(3)} ${r(3)} l ${-r(3)} ${r(3)}" ${line}/>`,
|
|
101485
|
+
// ID badge: an identity provider issues who-you-are, it does not check it.
|
|
101486
|
+
idp: ({ x, y, r, line }) => `<rect x="${x(3)}" y="${y(2)}" width="${r(12)}" height="${r(13)}" rx="${r(2)}" ${line}/><path d="M ${x(7)} ${y(2)} H ${x(11)}" ${line}/><circle cx="${x(9)}" cy="${y(7)}" r="${r(2)}" ${line}/><path d="M ${x(5)} ${y(13)} q ${r(4)} ${-r(4)} ${r(8)} 0" ${line}/>`,
|
|
101487
|
+
// Brick wall: a firewall is a barrier, and no other kind reads as one.
|
|
101488
|
+
firewall: ({ x, y, r, line }) => `<rect x="${x(2)}" y="${y(2)}" width="${r(14)}" height="${r(12)}" rx="${r(1)}" ${line}/><path d="M ${x(2)} ${y(6)} H ${x(16)} M ${x(2)} ${y(10)} H ${x(16)}" ${line}/><path d="M ${x(9)} ${y(2)} V ${y(6)} M ${x(6)} ${y(6)} V ${y(10)} M ${x(12)} ${y(6)} V ${y(10)} M ${x(9)} ${y(10)} V ${y(14)}" ${line}/>`,
|
|
101489
|
+
// Monitor on a stand: a device is the machine a person works at. The screen is
|
|
101490
|
+
// left empty on purpose — that is what separates it from the firewall's wall
|
|
101491
|
+
// at legend size, where both are a rectangle and little else survives.
|
|
101492
|
+
device: ({ x, y, r, line }) => `<rect x="${x(2)}" y="${y(1)}" width="${r(14)}" height="${r(9)}" rx="${r(1)}" ${line}/><path d="M ${x(9)} ${y(10)} V ${y(13)}" ${line}/><path d="M ${x(4)} ${y(13)} H ${x(14)}" ${line}/>`
|
|
101493
|
+
};
|
|
101494
|
+
LOGO_BOX = { size: 18, right: 7, top: 6 };
|
|
101495
|
+
legendKinds = (paint) => [...new Set(paint.scene.nodes.map((node) => node.kind))].filter(
|
|
101496
|
+
(kind) => paint.legendNames[kind] && (kind !== "actor" || paint.view.actorLegend)
|
|
101497
|
+
);
|
|
101498
|
+
}
|
|
101499
|
+
});
|
|
101500
|
+
|
|
101501
|
+
// src/css-colours.ts
|
|
101502
|
+
var HEX, NUMBER, PERCENT, AMOUNT, ANGLE, commaForm, spaceForm, RGB, HSL, NUMERIC_COLOUR, COLOUR_KEYWORDS, isColour;
|
|
101503
|
+
var init_css_colours = __esm({
|
|
101504
|
+
"src/css-colours.ts"() {
|
|
101505
|
+
"use strict";
|
|
101506
|
+
HEX = "#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})";
|
|
101507
|
+
NUMBER = String.raw`[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?`;
|
|
101508
|
+
PERCENT = `${NUMBER}%`;
|
|
101509
|
+
AMOUNT = `${NUMBER}%?`;
|
|
101510
|
+
ANGLE = `${NUMBER}(?:deg|grad|rad|turn)?`;
|
|
101511
|
+
commaForm = (first, second, third) => `${first}\\s*,\\s*${second}\\s*,\\s*${third}(?:\\s*,\\s*${AMOUNT})?`;
|
|
101512
|
+
spaceForm = (first, second, third) => `${first}\\s+${second}\\s+${third}(?:\\s*/\\s*${AMOUNT})?`;
|
|
101513
|
+
RGB = `rgba?\\(\\s*(?:${commaForm(NUMBER, NUMBER, NUMBER)}|${commaForm(PERCENT, PERCENT, PERCENT)}|${spaceForm(AMOUNT, AMOUNT, AMOUNT)})\\s*\\)`;
|
|
101514
|
+
HSL = `hsla?\\(\\s*(?:${commaForm(ANGLE, PERCENT, PERCENT)}|${spaceForm(ANGLE, AMOUNT, AMOUNT)})\\s*\\)`;
|
|
101515
|
+
NUMERIC_COLOUR = new RegExp(`^(?:${HEX}|${RGB}|${HSL})$`, "i");
|
|
101516
|
+
COLOUR_KEYWORDS = /* @__PURE__ */ new Set([
|
|
101517
|
+
"currentcolor",
|
|
101518
|
+
"transparent",
|
|
101519
|
+
"aliceblue",
|
|
101520
|
+
"antiquewhite",
|
|
101521
|
+
"aqua",
|
|
101522
|
+
"aquamarine",
|
|
101523
|
+
"azure",
|
|
101524
|
+
"beige",
|
|
101525
|
+
"bisque",
|
|
101526
|
+
"black",
|
|
101527
|
+
"blanchedalmond",
|
|
101528
|
+
"blue",
|
|
101529
|
+
"blueviolet",
|
|
101530
|
+
"brown",
|
|
101531
|
+
"burlywood",
|
|
101532
|
+
"cadetblue",
|
|
101533
|
+
"chartreuse",
|
|
101534
|
+
"chocolate",
|
|
101535
|
+
"coral",
|
|
101536
|
+
"cornflowerblue",
|
|
101537
|
+
"cornsilk",
|
|
101538
|
+
"crimson",
|
|
101539
|
+
"cyan",
|
|
101540
|
+
"darkblue",
|
|
101541
|
+
"darkcyan",
|
|
101542
|
+
"darkgoldenrod",
|
|
101543
|
+
"darkgray",
|
|
101544
|
+
"darkgreen",
|
|
101545
|
+
"darkgrey",
|
|
101546
|
+
"darkkhaki",
|
|
101547
|
+
"darkmagenta",
|
|
101548
|
+
"darkolivegreen",
|
|
101549
|
+
"darkorange",
|
|
101550
|
+
"darkorchid",
|
|
101551
|
+
"darkred",
|
|
101552
|
+
"darksalmon",
|
|
101553
|
+
"darkseagreen",
|
|
101554
|
+
"darkslateblue",
|
|
101555
|
+
"darkslategray",
|
|
101556
|
+
"darkslategrey",
|
|
101557
|
+
"darkturquoise",
|
|
101558
|
+
"darkviolet",
|
|
101559
|
+
"deeppink",
|
|
101560
|
+
"deepskyblue",
|
|
101561
|
+
"dimgray",
|
|
101562
|
+
"dimgrey",
|
|
101563
|
+
"dodgerblue",
|
|
101564
|
+
"firebrick",
|
|
101565
|
+
"floralwhite",
|
|
101566
|
+
"forestgreen",
|
|
101567
|
+
"fuchsia",
|
|
101568
|
+
"gainsboro",
|
|
101569
|
+
"ghostwhite",
|
|
101570
|
+
"gold",
|
|
101571
|
+
"goldenrod",
|
|
101572
|
+
"gray",
|
|
101573
|
+
"green",
|
|
101574
|
+
"greenyellow",
|
|
101575
|
+
"grey",
|
|
101576
|
+
"honeydew",
|
|
101577
|
+
"hotpink",
|
|
101578
|
+
"indianred",
|
|
101579
|
+
"indigo",
|
|
101580
|
+
"ivory",
|
|
101581
|
+
"khaki",
|
|
101582
|
+
"lavender",
|
|
101583
|
+
"lavenderblush",
|
|
101584
|
+
"lawngreen",
|
|
101585
|
+
"lemonchiffon",
|
|
101586
|
+
"lightblue",
|
|
101587
|
+
"lightcoral",
|
|
101588
|
+
"lightcyan",
|
|
101589
|
+
"lightgoldenrodyellow",
|
|
101590
|
+
"lightgray",
|
|
101591
|
+
"lightgreen",
|
|
101592
|
+
"lightgrey",
|
|
101593
|
+
"lightpink",
|
|
101594
|
+
"lightsalmon",
|
|
101595
|
+
"lightseagreen",
|
|
101596
|
+
"lightskyblue",
|
|
101597
|
+
"lightslategray",
|
|
101598
|
+
"lightslategrey",
|
|
101599
|
+
"lightsteelblue",
|
|
101600
|
+
"lightyellow",
|
|
101601
|
+
"lime",
|
|
101602
|
+
"limegreen",
|
|
101603
|
+
"linen",
|
|
101604
|
+
"magenta",
|
|
101605
|
+
"maroon",
|
|
101606
|
+
"mediumaquamarine",
|
|
101607
|
+
"mediumblue",
|
|
101608
|
+
"mediumorchid",
|
|
101609
|
+
"mediumpurple",
|
|
101610
|
+
"mediumseagreen",
|
|
101611
|
+
"mediumslateblue",
|
|
101612
|
+
"mediumspringgreen",
|
|
101613
|
+
"mediumturquoise",
|
|
101614
|
+
"mediumvioletred",
|
|
101615
|
+
"midnightblue",
|
|
101616
|
+
"mintcream",
|
|
101617
|
+
"mistyrose",
|
|
101618
|
+
"moccasin",
|
|
101619
|
+
"navajowhite",
|
|
101620
|
+
"navy",
|
|
101621
|
+
"oldlace",
|
|
101622
|
+
"olive",
|
|
101623
|
+
"olivedrab",
|
|
101624
|
+
"orange",
|
|
101625
|
+
"orangered",
|
|
101626
|
+
"orchid",
|
|
101627
|
+
"palegoldenrod",
|
|
101628
|
+
"palegreen",
|
|
101629
|
+
"paleturquoise",
|
|
101630
|
+
"palevioletred",
|
|
101631
|
+
"papayawhip",
|
|
101632
|
+
"peachpuff",
|
|
101633
|
+
"peru",
|
|
101634
|
+
"pink",
|
|
101635
|
+
"plum",
|
|
101636
|
+
"powderblue",
|
|
101637
|
+
"purple",
|
|
101638
|
+
"rebeccapurple",
|
|
101639
|
+
"red",
|
|
101640
|
+
"rosybrown",
|
|
101641
|
+
"royalblue",
|
|
101642
|
+
"saddlebrown",
|
|
101643
|
+
"salmon",
|
|
101644
|
+
"sandybrown",
|
|
101645
|
+
"seagreen",
|
|
101646
|
+
"seashell",
|
|
101647
|
+
"sienna",
|
|
101648
|
+
"silver",
|
|
101649
|
+
"skyblue",
|
|
101650
|
+
"slateblue",
|
|
101651
|
+
"slategray",
|
|
101652
|
+
"slategrey",
|
|
101653
|
+
"snow",
|
|
101654
|
+
"springgreen",
|
|
101655
|
+
"steelblue",
|
|
101656
|
+
"tan",
|
|
101657
|
+
"teal",
|
|
101658
|
+
"thistle",
|
|
101659
|
+
"tomato",
|
|
101660
|
+
"turquoise",
|
|
101661
|
+
"violet",
|
|
101662
|
+
"wheat",
|
|
101663
|
+
"white",
|
|
101664
|
+
"whitesmoke",
|
|
101665
|
+
"yellow",
|
|
101666
|
+
"yellowgreen"
|
|
101667
|
+
]);
|
|
101668
|
+
isColour = (value) => NUMERIC_COLOUR.test(value) || COLOUR_KEYWORDS.has(value.toLowerCase());
|
|
101669
|
+
}
|
|
101670
|
+
});
|
|
101671
|
+
|
|
101672
|
+
// src/theme-spec.ts
|
|
101673
|
+
function checkEntry(path, value, arity) {
|
|
101674
|
+
if (arity === 1) {
|
|
101675
|
+
if (typeof value !== "string") {
|
|
101676
|
+
throw new ThemeSpecError(`\`${path}\` must be a colour, got ${describe(value)}`);
|
|
101677
|
+
}
|
|
101678
|
+
} else if (!Array.isArray(value) || value.length !== arity) {
|
|
101679
|
+
throw new ThemeSpecError(`\`${path}\` must be ${arity} colours, got ${describe(value)}`);
|
|
101680
|
+
}
|
|
101681
|
+
for (const colour of Array.isArray(value) ? value : [value]) {
|
|
101682
|
+
if (typeof colour !== "string" || !isColour(colour)) {
|
|
101683
|
+
throw new ThemeSpecError(`\`${path}\`: \`${String(colour)}\` is not a colour`);
|
|
101684
|
+
}
|
|
101685
|
+
}
|
|
101686
|
+
}
|
|
101687
|
+
function resolveThemeSpec(input) {
|
|
101688
|
+
if (!isObject(input)) throw new ThemeSpecError("a theme must be an object");
|
|
101689
|
+
const base = input.extends ?? DEFAULT_BASE;
|
|
101690
|
+
if (typeof base !== "string") throw new ThemeSpecError("`extends` must be a theme name");
|
|
101691
|
+
const baseSpec = Object.hasOwn(THEME_SPECS, base) ? THEME_SPECS[base] : void 0;
|
|
101692
|
+
if (!baseSpec) {
|
|
101693
|
+
throw new ThemeSpecError(
|
|
101694
|
+
`extends unknown theme \`${base}\` \u2014 pick one of: ${Object.keys(THEME_SPECS).join(", ")}`
|
|
101695
|
+
);
|
|
101696
|
+
}
|
|
101697
|
+
if ("dark" in input && typeof input.dark !== "boolean") {
|
|
101698
|
+
throw new ThemeSpecError("`dark` must be true or false");
|
|
101699
|
+
}
|
|
101700
|
+
for (const [section, value] of Object.entries(input)) {
|
|
101701
|
+
if (section === "extends" || section === "dark") continue;
|
|
101702
|
+
if (!SECTIONS.includes(section)) {
|
|
101703
|
+
throw new ThemeSpecError(
|
|
101704
|
+
`unknown section \`${section}\` \u2014 a theme overrides ${SECTIONS.join(", ")}`
|
|
101705
|
+
);
|
|
101706
|
+
}
|
|
101707
|
+
if (!isObject(value)) {
|
|
101708
|
+
throw new ThemeSpecError(
|
|
101709
|
+
`\`${section}\` must be an object of colours, got ${describe(value)}`
|
|
101710
|
+
);
|
|
101711
|
+
}
|
|
101712
|
+
for (const [key, colour] of Object.entries(value)) {
|
|
101713
|
+
checkEntry(`${section}.${key}`, colour, arityOf(section, key));
|
|
101714
|
+
}
|
|
101715
|
+
}
|
|
101716
|
+
return merge(baseSpec, input);
|
|
101717
|
+
}
|
|
101718
|
+
var DEFAULT_BASE, ThemeSpecError, isObject, merge, SECTIONS, arityOf, describe;
|
|
101719
|
+
var init_theme_spec = __esm({
|
|
101720
|
+
"src/theme-spec.ts"() {
|
|
101721
|
+
"use strict";
|
|
101722
|
+
init_css_colours();
|
|
101723
|
+
init_themes();
|
|
101724
|
+
DEFAULT_BASE = "light";
|
|
101725
|
+
ThemeSpecError = class extends Error {
|
|
101726
|
+
};
|
|
101727
|
+
isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
101728
|
+
merge = (base, patch) => {
|
|
101729
|
+
const merged = { ...base };
|
|
101730
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
101731
|
+
if (key === "extends") continue;
|
|
101732
|
+
const current = merged[key];
|
|
101733
|
+
merged[key] = isObject(value) && isObject(current) ? { ...current, ...value } : value;
|
|
101734
|
+
}
|
|
101735
|
+
return merged;
|
|
101736
|
+
};
|
|
101737
|
+
SECTIONS = ["pal", "accentColors"];
|
|
101738
|
+
arityOf = (section, key) => {
|
|
101739
|
+
if (section === "pal") return key === "chip" ? 3 : key === "badge" ? 2 : 1;
|
|
101740
|
+
return 1;
|
|
101741
|
+
};
|
|
101742
|
+
describe = (value) => Array.isArray(value) ? `an array of ${value.length}` : typeof value;
|
|
100771
101743
|
}
|
|
100772
101744
|
});
|
|
100773
101745
|
|
|
@@ -100834,8 +101806,9 @@ function matrixMd(matrix) {
|
|
|
100834
101806
|
for (const row of matrix.rows) outLines.push("| " + row.cells.map(mdCell).join(" | ") + " |");
|
|
100835
101807
|
return outLines.join("\n") + "\n";
|
|
100836
101808
|
}
|
|
100837
|
-
function matrixSvg(matrix) {
|
|
100838
|
-
const
|
|
101809
|
+
function matrixSvg(matrix, options) {
|
|
101810
|
+
const requested = options?.theme;
|
|
101811
|
+
const pal = typeof requested === "object" ? paletteFromSpec(resolveThemeSpec(requested)) : paletteFor(requested ?? matrix.style.theme);
|
|
100839
101812
|
const title = matrix.heading;
|
|
100840
101813
|
const headers = matrix.columns.map((column) => column.label);
|
|
100841
101814
|
const cells = matrix.rows.map((row) => row.cells);
|
|
@@ -100903,6 +101876,7 @@ var init_flow_matrix = __esm({
|
|
|
100903
101876
|
"use strict";
|
|
100904
101877
|
init_localization();
|
|
100905
101878
|
init_themes();
|
|
101879
|
+
init_theme_spec();
|
|
100906
101880
|
init_xml_escape();
|
|
100907
101881
|
init_text_metrics();
|
|
100908
101882
|
COLUMN_PRESENTATION = {
|
|
@@ -100939,9 +101913,81 @@ var init_flow_matrix = __esm({
|
|
|
100939
101913
|
}
|
|
100940
101914
|
});
|
|
100941
101915
|
|
|
101916
|
+
// src/logo-files.ts
|
|
101917
|
+
import { closeSync, constants, fstatSync, openSync, readSync } from "node:fs";
|
|
101918
|
+
import { dirname, resolve, extname } from "node:path";
|
|
101919
|
+
function resolveLogoFiles(model, sourceFile) {
|
|
101920
|
+
const logos = /* @__PURE__ */ new Map();
|
|
101921
|
+
const diagnostics = [];
|
|
101922
|
+
const base = dirname(resolve(sourceFile));
|
|
101923
|
+
for (const element of model.elements.flatMap((root) => subtreeElements(root))) {
|
|
101924
|
+
const logo = element.logo;
|
|
101925
|
+
if (logo?.source !== "file") continue;
|
|
101926
|
+
const warn = (message, help) => diagnostics.push({
|
|
101927
|
+
code: "W0580",
|
|
101928
|
+
severity: "warning",
|
|
101929
|
+
message,
|
|
101930
|
+
span: logo.span,
|
|
101931
|
+
help
|
|
101932
|
+
});
|
|
101933
|
+
const mime = MIME_BY_EXT[extname(logo.value).toLowerCase()];
|
|
101934
|
+
if (!mime) {
|
|
101935
|
+
warn(
|
|
101936
|
+
`unsupported logo file type \`${extname(logo.value) || logo.value}\``,
|
|
101937
|
+
`use one of: ${Object.keys(MIME_BY_EXT).join(", ")}`
|
|
101938
|
+
);
|
|
101939
|
+
continue;
|
|
101940
|
+
}
|
|
101941
|
+
const path = resolve(base, logo.value);
|
|
101942
|
+
let bytes;
|
|
101943
|
+
try {
|
|
101944
|
+
const fd = openSync(path, constants.O_RDONLY | (constants.O_NONBLOCK ?? 0));
|
|
101945
|
+
try {
|
|
101946
|
+
const stat = fstatSync(fd);
|
|
101947
|
+
if (!stat.isFile()) {
|
|
101948
|
+
warn(`logo file \`${logo.value}\` is not a regular file`, "point at an image file");
|
|
101949
|
+
continue;
|
|
101950
|
+
}
|
|
101951
|
+
if (stat.size > MAX_BYTES) {
|
|
101952
|
+
warn(
|
|
101953
|
+
`logo file is ${Math.round(stat.size / 1024)} KB, over the ${MAX_BYTES / 1024} KB limit`,
|
|
101954
|
+
"a corner mark needs very little \u2014 export it smaller, or use a built-in"
|
|
101955
|
+
);
|
|
101956
|
+
continue;
|
|
101957
|
+
}
|
|
101958
|
+
bytes = Buffer.alloc(stat.size);
|
|
101959
|
+
const read = readSync(fd, bytes, 0, stat.size, 0);
|
|
101960
|
+
if (read < stat.size) bytes = bytes.subarray(0, read);
|
|
101961
|
+
} finally {
|
|
101962
|
+
closeSync(fd);
|
|
101963
|
+
}
|
|
101964
|
+
} catch {
|
|
101965
|
+
warn(`cannot read logo file \`${logo.value}\``, `looked in \`${base}\``);
|
|
101966
|
+
continue;
|
|
101967
|
+
}
|
|
101968
|
+
logos.set(element.id, `data:${mime};base64,${bytes.toString("base64")}`);
|
|
101969
|
+
}
|
|
101970
|
+
return { logos, diagnostics };
|
|
101971
|
+
}
|
|
101972
|
+
var MIME_BY_EXT, MAX_BYTES;
|
|
101973
|
+
var init_logo_files = __esm({
|
|
101974
|
+
"src/logo-files.ts"() {
|
|
101975
|
+
"use strict";
|
|
101976
|
+
init_element_tree();
|
|
101977
|
+
MIME_BY_EXT = {
|
|
101978
|
+
".svg": "image/svg+xml",
|
|
101979
|
+
".png": "image/png",
|
|
101980
|
+
".jpg": "image/jpeg",
|
|
101981
|
+
".jpeg": "image/jpeg",
|
|
101982
|
+
".webp": "image/webp"
|
|
101983
|
+
};
|
|
101984
|
+
MAX_BYTES = 256 * 1024;
|
|
101985
|
+
}
|
|
101986
|
+
});
|
|
101987
|
+
|
|
100942
101988
|
// src/watch.ts
|
|
100943
101989
|
import { watch as fsWatch, readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
100944
|
-
import { dirname, basename } from "node:path";
|
|
101990
|
+
import { dirname as dirname2, basename } from "node:path";
|
|
100945
101991
|
function errorPanelSvg(file, diagnostics) {
|
|
100946
101992
|
const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error").slice(0, 10);
|
|
100947
101993
|
const more = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length - errors.length;
|
|
@@ -100966,7 +102012,7 @@ function errorPanelSvg(file, diagnostics) {
|
|
|
100966
102012
|
`;
|
|
100967
102013
|
return out + "</svg>\n";
|
|
100968
102014
|
}
|
|
100969
|
-
function watchCommand(file, outFile) {
|
|
102015
|
+
function watchCommand(file, outFile, theme) {
|
|
100970
102016
|
if (!existsSync(file)) {
|
|
100971
102017
|
console.error(`error: file not found \`${file}\``);
|
|
100972
102018
|
process.exit(2);
|
|
@@ -100982,6 +102028,7 @@ function watchCommand(file, outFile) {
|
|
|
100982
102028
|
const src = readFileSync(file, "utf8");
|
|
100983
102029
|
const { model, diags } = parse(src);
|
|
100984
102030
|
diags.push(...validate(model));
|
|
102031
|
+
if (theme) model.style.theme = theme;
|
|
100985
102032
|
const errors = diags.filter((diagnostic) => diagnostic.severity === "error");
|
|
100986
102033
|
const stamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
100987
102034
|
console.clear();
|
|
@@ -101000,7 +102047,9 @@ function watchCommand(file, outFile) {
|
|
|
101000
102047
|
const view = views[model.type];
|
|
101001
102048
|
const scene = await layout(model, view);
|
|
101002
102049
|
diags.push(...attachSideDiagnostics(scene, model));
|
|
101003
|
-
const {
|
|
102050
|
+
const { logos, diagnostics: logoDiagnostics } = resolveLogoFiles(model, file);
|
|
102051
|
+
diags.push(...logoDiagnostics);
|
|
102052
|
+
const { svg, overlapsAfter } = render(model, view, scene, { logos });
|
|
101004
102053
|
writeFileSync(outFile, svg);
|
|
101005
102054
|
if (diags.length)
|
|
101006
102055
|
console.log(renderHuman(file, src, diags, process.stdout.isTTY ?? false) + "\n");
|
|
@@ -101033,7 +102082,7 @@ function watchCommand(file, outFile) {
|
|
|
101033
102082
|
}
|
|
101034
102083
|
};
|
|
101035
102084
|
let timer = null;
|
|
101036
|
-
fsWatch(
|
|
102085
|
+
fsWatch(dirname2(file) || ".", (_event, fileName) => {
|
|
101037
102086
|
if (fileName && fileName !== basename(file)) return;
|
|
101038
102087
|
if (timer) clearTimeout(timer);
|
|
101039
102088
|
timer = setTimeout(rebuild, 120);
|
|
@@ -101050,28 +102099,113 @@ var init_watch = __esm({
|
|
|
101050
102099
|
init_svg_render();
|
|
101051
102100
|
init_views();
|
|
101052
102101
|
init_xml_escape();
|
|
102102
|
+
init_logo_files();
|
|
102103
|
+
}
|
|
102104
|
+
});
|
|
102105
|
+
|
|
102106
|
+
// src/theme-file.ts
|
|
102107
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
102108
|
+
function loadThemeFile(path) {
|
|
102109
|
+
let raw;
|
|
102110
|
+
try {
|
|
102111
|
+
raw = readFileSync2(path, "utf8");
|
|
102112
|
+
} catch {
|
|
102113
|
+
throw new ThemeFileError(`cannot read theme file \`${path}\``);
|
|
102114
|
+
}
|
|
102115
|
+
let parsed;
|
|
102116
|
+
try {
|
|
102117
|
+
parsed = JSON.parse(raw);
|
|
102118
|
+
} catch (error) {
|
|
102119
|
+
throw new ThemeFileError(
|
|
102120
|
+
`\`${path}\` is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
102121
|
+
);
|
|
102122
|
+
}
|
|
102123
|
+
let spec;
|
|
102124
|
+
try {
|
|
102125
|
+
spec = resolveThemeSpec(parsed);
|
|
102126
|
+
} catch (error) {
|
|
102127
|
+
if (error instanceof ThemeSpecError) throw new ThemeFileError(`\`${path}\`: ${error.message}`);
|
|
102128
|
+
throw error;
|
|
102129
|
+
}
|
|
102130
|
+
const name = (path.split(/[\\/]/).pop() ?? path).replace(/\.json$/i, "");
|
|
102131
|
+
if (!name) throw new ThemeFileError(`\`${path}\` has no usable theme name`);
|
|
102132
|
+
return { name, spec };
|
|
102133
|
+
}
|
|
102134
|
+
var ThemeFileError;
|
|
102135
|
+
var init_theme_file = __esm({
|
|
102136
|
+
"src/theme-file.ts"() {
|
|
102137
|
+
"use strict";
|
|
102138
|
+
init_theme_spec();
|
|
102139
|
+
ThemeFileError = class extends Error {
|
|
102140
|
+
};
|
|
102141
|
+
}
|
|
102142
|
+
});
|
|
102143
|
+
|
|
102144
|
+
// src/notice.ts
|
|
102145
|
+
function notice({ bun = embedsBun() } = {}) {
|
|
102146
|
+
return COMMON + (bun ? BUN_NOTICE : "") + "\n" + FOOTER;
|
|
102147
|
+
}
|
|
102148
|
+
var ELKJS_VERSION, SIMPLE_ICONS_VERSION, BUN_VERSION, NOTICES_URL, COMMON, BUN_NOTICE, FOOTER, embedsBun;
|
|
102149
|
+
var init_notice = __esm({
|
|
102150
|
+
"src/notice.ts"() {
|
|
102151
|
+
"use strict";
|
|
102152
|
+
ELKJS_VERSION = "0.12.0";
|
|
102153
|
+
SIMPLE_ICONS_VERSION = "16.29.0";
|
|
102154
|
+
BUN_VERSION = "1.4.0";
|
|
102155
|
+
NOTICES_URL = "https://github.com/R0kshan/cairn/blob/main/THIRD-PARTY-NOTICES.md";
|
|
102156
|
+
COMMON = `cairn \u2014 Apache-2.0 \u2014 https://github.com/R0kshan/cairn
|
|
102157
|
+
Copyright 2026 R\xF8kshan.
|
|
102158
|
+
|
|
102159
|
+
This artifact contains third-party code and artwork:
|
|
102160
|
+
|
|
102161
|
+
elkjs ${ELKJS_VERSION} \u2014 (c) 2017 Kiel University and others.
|
|
102162
|
+
Dual-licensed EPL-2.0 OR GPL-3.0-or-later; cairn elects EPL-2.0.
|
|
102163
|
+
Full text: licenses/elkjs-EPL-2.0.md \u2014 https://www.eclipse.org/legal/epl-2.0
|
|
102164
|
+
|
|
102165
|
+
Simple Icons ${SIMPLE_ICONS_VERSION} artwork \u2014 CC0-1.0 project-wide.
|
|
102166
|
+
Six of the vendored icons carry their own terms instead \u2014 MIT,
|
|
102167
|
+
BSD-3-Clause, CC-BY-4.0 or Apache-2.0 \u2014 and are attributed individually.
|
|
102168
|
+
Full texts: licenses/`;
|
|
102169
|
+
BUN_NOTICE = `
|
|
102170
|
+
|
|
102171
|
+
Bun ${BUN_VERSION} runtime \u2014 MIT \u2014 embedded by \`bun build --compile\`.
|
|
102172
|
+
It statically links JavaScriptCore/WebKit, which is LGPL-2.1 in part,
|
|
102173
|
+
alongside BSD- and Apache-licensed components. Bun's own LICENSE.md
|
|
102174
|
+
enumerates all of them verbatim: licenses/bun-LICENSE.md
|
|
102175
|
+
Relinking under LGPL-2.1 \xA76: see THIRD-PARTY-NOTICES.md.`;
|
|
102176
|
+
FOOTER = `
|
|
102177
|
+
Per-icon attribution and the full licence texts ship with this artifact:
|
|
102178
|
+
the package root (npm), the app directory (Scoop), or
|
|
102179
|
+
share/doc/cairn (Homebrew, install.sh). Also at
|
|
102180
|
+
${NOTICES_URL}`;
|
|
102181
|
+
embedsBun = () => typeof CAIRN_EMBEDS_BUN !== "undefined" && CAIRN_EMBEDS_BUN;
|
|
101053
102182
|
}
|
|
101054
102183
|
});
|
|
101055
102184
|
|
|
101056
102185
|
// src/cli.ts
|
|
101057
102186
|
var cli_exports = {};
|
|
101058
|
-
import { readFileSync as
|
|
102187
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "node:fs";
|
|
101059
102188
|
function usage() {
|
|
101060
102189
|
console.log(`cairn \u2014 architecture diagrams as code
|
|
101061
102190
|
|
|
101062
102191
|
Usage:
|
|
101063
|
-
cairn new (--logical-architecture|-L|-A|-I
|
|
102192
|
+
cairn new (--logical-architecture|-L|-A|-I) <file.cairn> scaffold a typed template
|
|
101064
102193
|
(-L logical, -A application,
|
|
101065
|
-
-I infrastructure
|
|
102194
|
+
-I infrastructure)
|
|
101066
102195
|
cairn validate <file.cairn> [--format json] [--strict]
|
|
101067
|
-
cairn build <file.cairn> [-o output.svg]
|
|
101068
|
-
cairn matrix <file.cairn> [--format csv|md|svg] [-o out]
|
|
102196
|
+
cairn build <file.cairn> [-o output.svg] [--theme <name|file.json>]
|
|
102197
|
+
cairn matrix <file.cairn> [--format csv|md|svg] [-o out] [--theme <name|file.json>]
|
|
102198
|
+
matrice des flux techniques
|
|
101069
102199
|
(default csv; honors style { lang })
|
|
101070
|
-
cairn watch <file.cairn> [-o output.svg]
|
|
102200
|
+
cairn watch <file.cairn> [-o output.svg] [--theme <name|file.json>]
|
|
102201
|
+
rebuild on save; SVG stays fresh
|
|
101071
102202
|
(error panel on failure) for an
|
|
101072
102203
|
editor auto-refresh preview
|
|
101073
102204
|
cairn explain <code> rule rationale (e.g. E0203)
|
|
102205
|
+
cairn logos list the built-in \`logo:\` names
|
|
102206
|
+
cairn themes list the built-in theme names
|
|
101074
102207
|
cairn version | --version | -v print the installed version
|
|
102208
|
+
cairn version --licenses third-party notices this build carries
|
|
101075
102209
|
`);
|
|
101076
102210
|
process.exit(2);
|
|
101077
102211
|
}
|
|
@@ -101080,7 +102214,7 @@ function loadAndCheck(file) {
|
|
|
101080
102214
|
console.error(`error: file not found \`${file}\``);
|
|
101081
102215
|
process.exit(2);
|
|
101082
102216
|
}
|
|
101083
|
-
const src =
|
|
102217
|
+
const src = readFileSync3(file, "utf8");
|
|
101084
102218
|
const { model, diags } = parse(src);
|
|
101085
102219
|
diags.push(...validate(model));
|
|
101086
102220
|
return { src, model, diagnostics: diags };
|
|
@@ -101111,13 +102245,41 @@ function densityReport(disposition, scene, typeSpan) {
|
|
|
101111
102245
|
}
|
|
101112
102246
|
};
|
|
101113
102247
|
}
|
|
102248
|
+
function themeFromArgs() {
|
|
102249
|
+
const index = args.indexOf("--theme");
|
|
102250
|
+
if (index === -1) return void 0;
|
|
102251
|
+
const value = args[index + 1];
|
|
102252
|
+
if (!value || value.startsWith("-")) {
|
|
102253
|
+
console.error("error: `--theme` needs a theme name or a path to a .json theme file");
|
|
102254
|
+
process.exit(2);
|
|
102255
|
+
}
|
|
102256
|
+
if (!value.toLowerCase().endsWith(".json")) {
|
|
102257
|
+
if (!themeNames.includes(value)) {
|
|
102258
|
+
console.error(
|
|
102259
|
+
`error: unknown theme \`${value}\`
|
|
102260
|
+
available: ${themeNames.join(", ")}
|
|
102261
|
+
or pass a path to a .json theme file`
|
|
102262
|
+
);
|
|
102263
|
+
process.exit(2);
|
|
102264
|
+
}
|
|
102265
|
+
return value;
|
|
102266
|
+
}
|
|
102267
|
+
try {
|
|
102268
|
+
const { name, spec } = loadThemeFile(value);
|
|
102269
|
+
registerTheme(name, spec);
|
|
102270
|
+
return name;
|
|
102271
|
+
} catch (error) {
|
|
102272
|
+
console.error(`error: ${error instanceof Error ? error.message : String(error)}`);
|
|
102273
|
+
process.exit(2);
|
|
102274
|
+
}
|
|
102275
|
+
}
|
|
101114
102276
|
function exitIfErrors(file, src, diagnostics) {
|
|
101115
102277
|
if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
101116
102278
|
console.error(renderHuman(file, src, diagnostics, process.stderr.isTTY ?? false));
|
|
101117
102279
|
process.exit(1);
|
|
101118
102280
|
}
|
|
101119
102281
|
}
|
|
101120
|
-
var TYPE_FLAGS, TEMPLATE_LOGICAL, TEMPLATE_APPLICATION, TEMPLATE_INFRASTRUCTURE,
|
|
102282
|
+
var TYPE_FLAGS, TEMPLATE_LOGICAL, TEMPLATE_APPLICATION, TEMPLATE_INFRASTRUCTURE, TEMPLATES, args, command, version, VALUE_FLAGS, positionalFile, PRINT_FRAMES;
|
|
101121
102283
|
var init_cli = __esm({
|
|
101122
102284
|
"src/cli.ts"() {
|
|
101123
102285
|
"use strict";
|
|
@@ -101130,15 +102292,18 @@ var init_cli = __esm({
|
|
|
101130
102292
|
init_views();
|
|
101131
102293
|
init_ast();
|
|
101132
102294
|
init_watch();
|
|
102295
|
+
init_logo_files();
|
|
102296
|
+
init_theme_file();
|
|
102297
|
+
init_themes();
|
|
102298
|
+
init_logos();
|
|
102299
|
+
init_notice();
|
|
101133
102300
|
TYPE_FLAGS = {
|
|
101134
102301
|
"--logical-architecture": "logical",
|
|
101135
102302
|
"-L": "logical",
|
|
101136
102303
|
"--application-architecture": "application",
|
|
101137
102304
|
"-A": "application",
|
|
101138
102305
|
"--infrastructure-architecture": "infrastructure",
|
|
101139
|
-
"-I": "infrastructure"
|
|
101140
|
-
"--security-architecture": "security",
|
|
101141
|
-
"-S": "security"
|
|
102306
|
+
"-I": "infrastructure"
|
|
101142
102307
|
};
|
|
101143
102308
|
TEMPLATE_LOGICAL = `diagram logical "Diagram title"
|
|
101144
102309
|
|
|
@@ -101259,45 +102424,16 @@ CORE_I -> PARTNER : "Nightly export" (SFTP/22)
|
|
|
101259
102424
|
# Export the flow matrix beside the physical diagram (French DA deliverable):
|
|
101260
102425
|
# cairn matrix this-file.cairn --format csv # or md | svg
|
|
101261
102426
|
# Add \`style { lang: fr }\` above to get French column headers (N\xB0, Protocole\u2026).
|
|
101262
|
-
`;
|
|
101263
|
-
TEMPLATE_SECURITY = `diagram security "Diagram title"
|
|
101264
|
-
|
|
101265
|
-
# A security diagram shows trust zones (each with a sensitivity level), the
|
|
101266
|
-
# filtering/security nodes between them, and the sensitive assets they protect.
|
|
101267
|
-
# Zones band left\u2192right in declaration order (most exposed \u2192 most protected).
|
|
101268
|
-
# Levels, least\u2192most trusted: public | internal | restricted | secret.
|
|
101269
|
-
|
|
101270
|
-
trust-zone INTERNET "Internet" (public) {
|
|
101271
|
-
security-node WAF "WAF / reverse proxy"
|
|
101272
|
-
}
|
|
101273
|
-
|
|
101274
|
-
trust-zone LAN "Internal zone" (internal) {
|
|
101275
|
-
asset APP "Business application"
|
|
101276
|
-
}
|
|
101277
|
-
|
|
101278
|
-
trust-zone DATA "Data zone" (restricted) {
|
|
101279
|
-
asset DB "Customer database"
|
|
101280
|
-
}
|
|
101281
|
-
|
|
101282
|
-
external USERS "End users"
|
|
101283
|
-
|
|
101284
|
-
# ---- security flows (cross-zone flows should be filtered + encrypted) ----
|
|
101285
|
-
USERS -> WAF : "HTTPS access" (TLS1.3)
|
|
101286
|
-
WAF -> APP : "Filtered traffic" (mTLS)
|
|
101287
|
-
APP -> DB : "Read/write" (TLS1.3)
|
|
101288
|
-
|
|
101289
|
-
# W0560 warns on any flow entering a more-trusted zone without a security-node.
|
|
101290
102427
|
`;
|
|
101291
102428
|
TEMPLATES = {
|
|
101292
102429
|
logical: TEMPLATE_LOGICAL,
|
|
101293
102430
|
application: TEMPLATE_APPLICATION,
|
|
101294
|
-
infrastructure: TEMPLATE_INFRASTRUCTURE
|
|
101295
|
-
security: TEMPLATE_SECURITY
|
|
102431
|
+
infrastructure: TEMPLATE_INFRASTRUCTURE
|
|
101296
102432
|
};
|
|
101297
102433
|
args = process.argv.slice(2);
|
|
101298
102434
|
command = args[0];
|
|
101299
|
-
version = true ? "1.0.0-
|
|
101300
|
-
VALUE_FLAGS = /* @__PURE__ */ new Set(["-o", "--format"]);
|
|
102435
|
+
version = true ? "1.0.0-RC15" : pkg.version;
|
|
102436
|
+
VALUE_FLAGS = /* @__PURE__ */ new Set(["-o", "--format", "--theme"]);
|
|
101301
102437
|
positionalFile = () => {
|
|
101302
102438
|
for (let index = 0; index < args.length; index++) {
|
|
101303
102439
|
const arg = args[index];
|
|
@@ -101315,7 +102451,24 @@ APP -> DB : "Read/write" (TLS1.3)
|
|
|
101315
102451
|
page: { width: 700, height: 1e3, name: "A4 page" }
|
|
101316
102452
|
};
|
|
101317
102453
|
if (command === "version" || command === "--version" || command === "-v") {
|
|
101318
|
-
|
|
102454
|
+
const LICENCE_FLAGS = /* @__PURE__ */ new Set(["--licenses", "--licences"]);
|
|
102455
|
+
const unknown = args.slice(1).filter((arg) => arg.startsWith("-") && !LICENCE_FLAGS.has(arg));
|
|
102456
|
+
if (unknown.length) {
|
|
102457
|
+
console.error(
|
|
102458
|
+
`unknown flag${unknown.length > 1 ? "s" : ""} for \`cairn version\`: ${unknown.join(", ")}
|
|
102459
|
+
the only one it takes is \`--licenses\` (third-party notices for this build)`
|
|
102460
|
+
);
|
|
102461
|
+
process.exit(2);
|
|
102462
|
+
}
|
|
102463
|
+
if (args.some((arg) => LICENCE_FLAGS.has(arg))) {
|
|
102464
|
+
console.log(`cairn v${version}
|
|
102465
|
+
`);
|
|
102466
|
+
console.log(notice());
|
|
102467
|
+
} else {
|
|
102468
|
+
console.log(`cairn v${version}`);
|
|
102469
|
+
if (process.stdout.isTTY)
|
|
102470
|
+
console.log("Apache-2.0, and bundles third-party code \u2014 `cairn version --licenses`");
|
|
102471
|
+
}
|
|
101319
102472
|
} else if (command === "validate") {
|
|
101320
102473
|
const file = positionalFile();
|
|
101321
102474
|
if (!file) usage();
|
|
@@ -101332,12 +102485,16 @@ APP -> DB : "Read/write" (TLS1.3)
|
|
|
101332
102485
|
} else if (command === "build") {
|
|
101333
102486
|
const file = positionalFile();
|
|
101334
102487
|
if (!file) usage();
|
|
102488
|
+
const themeOverride = themeFromArgs();
|
|
101335
102489
|
const outFile = resolveOutputPath(file, ".svg");
|
|
101336
102490
|
const { src, model, diagnostics } = loadAndCheck(file);
|
|
101337
102491
|
exitIfErrors(file, src, diagnostics);
|
|
102492
|
+
if (themeOverride) model.style.theme = themeOverride;
|
|
101338
102493
|
const view = views[model.type];
|
|
101339
102494
|
layout(model, view).then((scene) => {
|
|
101340
|
-
const {
|
|
102495
|
+
const { logos, diagnostics: logoDiagnostics } = resolveLogoFiles(model, file);
|
|
102496
|
+
diagnostics.push(...logoDiagnostics);
|
|
102497
|
+
const { svg, overlapsBefore, overlapsAfter } = render(model, view, scene, { logos });
|
|
101341
102498
|
writeFileSync2(outFile, svg);
|
|
101342
102499
|
const { fitInfo, diagnostic } = densityReport(
|
|
101343
102500
|
model.style.disposition,
|
|
@@ -101360,6 +102517,7 @@ APP -> DB : "Read/write" (TLS1.3)
|
|
|
101360
102517
|
} else if (command === "matrix") {
|
|
101361
102518
|
const file = positionalFile();
|
|
101362
102519
|
if (!file) usage();
|
|
102520
|
+
const themeOverride = themeFromArgs();
|
|
101363
102521
|
const formatIndex = args.indexOf("--format");
|
|
101364
102522
|
const format = formatIndex >= 0 ? args[formatIndex + 1] : "csv";
|
|
101365
102523
|
if (!["csv", "md", "svg"].includes(format)) {
|
|
@@ -101372,6 +102530,7 @@ APP -> DB : "Read/write" (TLS1.3)
|
|
|
101372
102530
|
console.error(`error: \`${file}\` declares no flows \u2014 nothing to tabulate`);
|
|
101373
102531
|
process.exit(1);
|
|
101374
102532
|
}
|
|
102533
|
+
if (themeOverride) model.style.theme = themeOverride;
|
|
101375
102534
|
const ext = format === "svg" ? "svg" : format === "md" ? "md" : "csv";
|
|
101376
102535
|
const outFile = resolveOutputPath(file, ".flow." + ext);
|
|
101377
102536
|
const lang = model.style.lang;
|
|
@@ -101384,7 +102543,7 @@ APP -> DB : "Read/write" (TLS1.3)
|
|
|
101384
102543
|
} else if (command === "watch") {
|
|
101385
102544
|
const file = positionalFile();
|
|
101386
102545
|
if (!file) usage();
|
|
101387
|
-
watchCommand(file, resolveOutputPath(file, ".svg"));
|
|
102546
|
+
watchCommand(file, resolveOutputPath(file, ".svg"), themeFromArgs());
|
|
101388
102547
|
} else if (command === "new") {
|
|
101389
102548
|
const type = args.map((arg) => TYPE_FLAGS[arg]).find(Boolean);
|
|
101390
102549
|
const file = positionalFile();
|
|
@@ -101408,6 +102567,25 @@ APP -> DB : "Read/write" (TLS1.3)
|
|
|
101408
102567
|
console.log(
|
|
101409
102568
|
`\u2713 ${file} created (${type} view) \u2014 fill in the sections, then run \`cairn validate ${file}\``
|
|
101410
102569
|
);
|
|
102570
|
+
} else if (command === "logos") {
|
|
102571
|
+
const width = LOGO_NAMES.reduce((widest, name) => Math.max(widest, name.length), 0) + 2;
|
|
102572
|
+
const perLine = Math.max(1, Math.floor(78 / width));
|
|
102573
|
+
console.log(`${LOGO_NAMES.length} built-in logos \u2014 use as \`logo: <name>\` on an element:
|
|
102574
|
+
`);
|
|
102575
|
+
for (let index = 0; index < LOGO_NAMES.length; index += perLine)
|
|
102576
|
+
console.log(
|
|
102577
|
+
" " + LOGO_NAMES.slice(index, index + perLine).map((n) => n.padEnd(width)).join("").trimEnd()
|
|
102578
|
+
);
|
|
102579
|
+
console.log('\nAnything else: point at a file \u2014 `logo: "./logos/name.svg"`.');
|
|
102580
|
+
} else if (command === "themes") {
|
|
102581
|
+
console.log(
|
|
102582
|
+
`${themeNames.length} built-in themes \u2014 \`--theme <name>\`, or \`style { theme: \u2026 }\`:
|
|
102583
|
+
`
|
|
102584
|
+
);
|
|
102585
|
+
for (const name of themeNames) console.log(` ${name}`);
|
|
102586
|
+
console.log(
|
|
102587
|
+
'\nFor your own colours, pass a JSON file instead \u2014 `--theme ./my-theme.json`.\nIt extends a built-in and overrides only what it names:\n\n { "extends": "dark", "pal": { "bg": "#0d1117" } }\n'
|
|
102588
|
+
);
|
|
101411
102589
|
} else if (command === "explain") {
|
|
101412
102590
|
const code = args[1];
|
|
101413
102591
|
if (!code) usage();
|