@r0kshan/cairn 1.0.0-RC13 → 1.0.0-RC14

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/dist/cairn.mjs CHANGED
@@ -1,3 +1,23 @@
1
+ /*!
2
+ * cairn — Apache-2.0 — https://github.com/R0kshan/cairn
3
+ * Copyright 2026 Røkshan.
4
+ *
5
+ * This artifact contains third-party code and artwork:
6
+ *
7
+ * elkjs 0.12.0 — (c) 2017 Kiel University and others.
8
+ * Dual-licensed EPL-2.0 OR GPL-3.0-or-later; cairn elects EPL-2.0.
9
+ * Full text: licenses/elkjs-EPL-2.0.md — https://www.eclipse.org/legal/epl-2.0
10
+ *
11
+ * Simple Icons 16.29.0 artwork — CC0-1.0 project-wide.
12
+ * Six of the vendored icons carry their own terms instead — MIT,
13
+ * BSD-3-Clause, CC-BY-4.0 or Apache-2.0 — and are attributed individually.
14
+ * Full texts: licenses/
15
+ *
16
+ * Per-icon attribution and the full licence texts ship with this artifact:
17
+ * the package root (npm), the app directory (Scoop), or
18
+ * share/doc/cairn (Homebrew, install.sh). Also at
19
+ * https://github.com/R0kshan/cairn/blob/main/THIRD-PARTY-NOTICES.md
20
+ */
1
21
  var __create = Object.create;
2
22
  var __defProp = Object.defineProperty;
3
23
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -92369,10 +92389,6 @@ var darkPalette = {
92369
92389
  chipStroke: "#b08d2a",
92370
92390
  chipText: "#e0c068"
92371
92391
  };
92372
- var palettes = {
92373
- light: lightPalette,
92374
- dark: darkPalette
92375
- };
92376
92392
  var flowPalette = {
92377
92393
  light: [
92378
92394
  "#1f77b4",
@@ -92419,16 +92435,15 @@ var KIND_ROLE_MAP = {
92419
92435
  datastore: "datastore",
92420
92436
  queue: "datastore",
92421
92437
  gateway: "authGateway",
92438
+ firewall: "firewall",
92422
92439
  auth: "auth",
92423
92440
  idp: "identityProvider",
92424
92441
  site: "site",
92425
92442
  "network-zone": "networkZone",
92426
92443
  server: "server",
92427
- "app-instance": "appInstance",
92428
- "security-node": "securityNode",
92429
- asset: "leaf"
92444
+ "app-instance": "appInstance"
92430
92445
  };
92431
- var roleForKind = (kind, viewName) => viewName === "security" && kind === "external" ? "untrusted" : KIND_ROLE_MAP[kind] ?? "leaf";
92446
+ var roleForKind = (kind) => KIND_ROLE_MAP[kind] ?? "leaf";
92432
92447
  var buildTheme = (spec) => {
92433
92448
  const paletteSpec = spec.pal, accentColors = spec.accentColors;
92434
92449
  const palette = {
@@ -92464,28 +92479,25 @@ var buildTheme = (spec) => {
92464
92479
  application: containerStyle(accentColors.appF, accentColors.app),
92465
92480
  layer: containerStyle(accentColors.goldF, accentColors.gold, false, 1),
92466
92481
  external: containerStyle(accentColors.violetF, accentColors.violet, true),
92467
- untrusted: containerStyle(accentColors.redF, accentColors.red, true, 1.3),
92468
92482
  leaf: leafStyle(accentColors.leafF, accentColors.leafS),
92469
92483
  datastore: leafStyle(accentColors.purpleF, accentColors.purple),
92470
92484
  site: containerStyle(accentColors.siteF, accentColors.siteS, false, 1.4),
92471
92485
  networkZone: containerStyle(accentColors.greenF, accentColors.green, true),
92472
92486
  server: containerStyle(accentColors.serverF, accentColors.serverS, false, 1.5),
92473
92487
  appInstance: leafStyle(accentColors.aiF, accentColors.aiS, 1.2),
92474
- securityNode: leafStyle(accentColors.nodeF, accentColors.node, 1.6),
92488
+ // Accent keys predate the roles that read them: `auth`/`authF` is the
92489
+ // gateway's orange, `authn`/`authnF` the auth middleware's blue. The two
92490
+ // used to share the `node` orange, which is what made a gateway and an
92491
+ // auth middleware indistinguishable in a dense infrastructure view.
92475
92492
  authGateway: leafStyle(accentColors.authF, accentColors.auth, 1.4),
92476
- auth: leafStyle(accentColors.nodeF, accentColors.node, 1.5),
92493
+ firewall: leafStyle(accentColors.fwF, accentColors.fw, 1.6),
92494
+ auth: leafStyle(accentColors.authnF, accentColors.authn, 1.5),
92477
92495
  identityProvider: leafStyle(accentColors.idpF, accentColors.idp)
92478
- },
92479
- levels: {
92480
- public: containerStyle(spec.lv.public[0], spec.lv.public[1], false, 1.4),
92481
- internal: containerStyle(spec.lv.internal[0], spec.lv.internal[1], false, 1.4),
92482
- restricted: containerStyle(spec.lv.restricted[0], spec.lv.restricted[1], false, 1.4),
92483
- secret: containerStyle(spec.lv.secret[0], spec.lv.secret[1], false, 1.4)
92484
92496
  }
92485
92497
  };
92486
92498
  };
92487
- var themes = {
92488
- light: buildTheme({
92499
+ var THEME_SPECS = {
92500
+ light: {
92489
92501
  pal: {
92490
92502
  bg: "#ffffff",
92491
92503
  text: "#17202c",
@@ -92533,16 +92545,15 @@ var themes = {
92533
92545
  auth: "#b85a30",
92534
92546
  authF: "#f5e6dd",
92535
92547
  idp: "#3a8f8f",
92536
- idpF: "#e0f0f0"
92537
- },
92538
- lv: {
92539
- public: ["#fdeceb", "#d0463f"],
92540
- internal: ["#fef2e2", "#cf9436"],
92541
- restricted: ["#e9f2fb", "#2f7cc4"],
92542
- secret: ["#efe9f7", "#7a55a8"]
92548
+ idpF: "#e0f0f0",
92549
+ fw: "#c0392b",
92550
+ fwF: "#fdecea",
92551
+ authn: "#2f6fb5",
92552
+ authnF: "#e8f1fb"
92543
92553
  }
92544
- }),
92545
- dark: buildTheme({
92554
+ },
92555
+ dark: {
92556
+ dark: true,
92546
92557
  pal: {
92547
92558
  bg: "#1e2530",
92548
92559
  text: "#e6edf3",
@@ -92590,16 +92601,14 @@ var themes = {
92590
92601
  auth: "#c96a4a",
92591
92602
  authF: "#332218",
92592
92603
  idp: "#4fafaf",
92593
- idpF: "#1a2e2e"
92594
- },
92595
- lv: {
92596
- public: ["#3a2422", "#c25a54"],
92597
- internal: ["#332a1c", "#c08a44"],
92598
- restricted: ["#1f2a37", "#4a86b8"],
92599
- secret: ["#291f33", "#8a6cb0"]
92604
+ idpF: "#1a2e2e",
92605
+ fw: "#e5675a",
92606
+ fwF: "#33211f",
92607
+ authn: "#6fa8e0",
92608
+ authnF: "#1d2735"
92600
92609
  }
92601
- }),
92602
- slate: buildTheme({
92610
+ },
92611
+ slate: {
92603
92612
  pal: {
92604
92613
  bg: "#f7f9fb",
92605
92614
  text: "#26303c",
@@ -92647,16 +92656,14 @@ var themes = {
92647
92656
  auth: "#a85a30",
92648
92657
  authF: "#f0e4dd",
92649
92658
  idp: "#3a8f8f",
92650
- idpF: "#e4f0f0"
92651
- },
92652
- lv: {
92653
- public: ["#f7ece9", "#c05a4a"],
92654
- internal: ["#f3efe6", "#a8823f"],
92655
- restricted: ["#e8eff6", "#3b6ea5"],
92656
- secret: ["#efecf5", "#7d6ba8"]
92659
+ idpF: "#e4f0f0",
92660
+ fw: "#bd4b3c",
92661
+ fwF: "#f9e9e6",
92662
+ authn: "#3a6ea5",
92663
+ authnF: "#e9f0f8"
92657
92664
  }
92658
- }),
92659
- sand: buildTheme({
92665
+ },
92666
+ sand: {
92660
92667
  pal: {
92661
92668
  bg: "#faf6ee",
92662
92669
  text: "#3a2f22",
@@ -92704,16 +92711,14 @@ var themes = {
92704
92711
  auth: "#a85a30",
92705
92712
  authF: "#f0e5dd",
92706
92713
  idp: "#3a7a6f",
92707
- idpF: "#e5f0ed"
92708
- },
92709
- lv: {
92710
- public: ["#f7e2da", "#c0562a"],
92711
- internal: ["#f6ecd2", "#b0842e"],
92712
- restricted: ["#e6f0f1", "#3f7a8c"],
92713
- secret: ["#f0e8ef", "#8a5f7a"]
92714
+ idpF: "#e5f0ed",
92715
+ fw: "#b8492f",
92716
+ fwF: "#f8e8e0",
92717
+ authn: "#3a6b9a",
92718
+ authnF: "#e9eff7"
92714
92719
  }
92715
- }),
92716
- contrast: buildTheme({
92720
+ },
92721
+ contrast: {
92717
92722
  pal: {
92718
92723
  bg: "#ffffff",
92719
92724
  text: "#000000",
@@ -92761,16 +92766,15 @@ var themes = {
92761
92766
  auth: "#993a1a",
92762
92767
  authF: "#f6e3dd",
92763
92768
  idp: "#005a5a",
92764
- idpF: "#daf0f0"
92765
- },
92766
- lv: {
92767
- public: ["#f9dcd6", "#c0341a"],
92768
- internal: ["#f6e6c8", "#9a6a00"],
92769
- restricted: ["#e0edf7", "#005a9c"],
92770
- secret: ["#eee0f7", "#6a2fa0"]
92769
+ idpF: "#daf0f0",
92770
+ fw: "#a3140a",
92771
+ fwF: "#fbe3df",
92772
+ authn: "#00408f",
92773
+ authnF: "#dfe8fb"
92771
92774
  }
92772
- }),
92773
- nord: buildTheme({
92775
+ },
92776
+ nord: {
92777
+ dark: true,
92774
92778
  pal: {
92775
92779
  bg: "#2e3440",
92776
92780
  text: "#eceff4",
@@ -92818,16 +92822,14 @@ var themes = {
92818
92822
  auth: "#c96a4a",
92819
92823
  authF: "#332218",
92820
92824
  idp: "#6fafaf",
92821
- idpF: "#203432"
92822
- },
92823
- lv: {
92824
- public: ["#3a2a2d", "#bf616a"],
92825
- internal: ["#3a3524", "#d0a85f"],
92826
- restricted: ["#2f3a44", "#81a1c1"],
92827
- secret: ["#312a3a", "#a38bbd"]
92825
+ idpF: "#203432",
92826
+ fw: "#bf616a",
92827
+ fwF: "#3b2f34",
92828
+ authn: "#81a1c1",
92829
+ authnF: "#333c4a"
92828
92830
  }
92829
- }),
92830
- solarized: buildTheme({
92831
+ },
92832
+ solarized: {
92831
92833
  pal: {
92832
92834
  bg: "#fdf6e3",
92833
92835
  text: "#586e75",
@@ -92875,34 +92877,41 @@ var themes = {
92875
92877
  auth: "#b85a30",
92876
92878
  authF: "#f5e6dd",
92877
92879
  idp: "#2aa198",
92878
- idpF: "#dff0ec"
92879
- },
92880
- lv: {
92881
- public: ["#f7ddd6", "#dc322f"],
92882
- internal: ["#f2e6c8", "#b58900"],
92883
- restricted: ["#e3edf3", "#268bd2"],
92884
- secret: ["#e8e6f2", "#6c71c4"]
92880
+ idpF: "#dff0ec",
92881
+ fw: "#dc322f",
92882
+ fwF: "#f7e3dd",
92883
+ authn: "#268bd2",
92884
+ authnF: "#e3edf5"
92885
92885
  }
92886
- })
92886
+ }
92887
92887
  };
92888
+ var themes = Object.fromEntries(
92889
+ Object.entries(THEME_SPECS).map(([name, spec]) => [name, buildTheme(spec)])
92890
+ );
92888
92891
  var themeNames = [...Object.keys(themes), "classic", "classic-dark"];
92892
+ function isDarkTheme(name) {
92893
+ return name === "classic-dark" || THEME_SPECS[name]?.dark === true;
92894
+ }
92889
92895
  function themeFor(name, view) {
92890
- if (name === "classic")
92891
- return {
92892
- palette: lightPalette,
92893
- kinds: view.defaults,
92894
- levels: view.levelDefaults ?? {}
92895
- };
92896
- if (name === "classic-dark")
92897
- return {
92898
- palette: darkPalette,
92899
- kinds: view.defaultsDark,
92900
- levels: view.levelDefaultsDark ?? {}
92901
- };
92902
- const theme = themes[name] ?? themes.light;
92896
+ if (name === "classic") return { palette: lightPalette, kinds: view.defaults };
92897
+ if (name === "classic-dark") return { palette: darkPalette, kinds: view.defaultsDark };
92898
+ return applyTheme(themes[name] ?? themes.light, view);
92899
+ }
92900
+ function themeFromSpec(spec, view) {
92901
+ return applyTheme(buildTheme(spec), view);
92902
+ }
92903
+ function paletteFor(name) {
92904
+ if (name === "classic") return lightPalette;
92905
+ if (name === "classic-dark") return darkPalette;
92906
+ return themes[name]?.palette ?? lightPalette;
92907
+ }
92908
+ function paletteFromSpec(spec) {
92909
+ return buildTheme(spec).palette;
92910
+ }
92911
+ function applyTheme(theme, view) {
92903
92912
  const kinds = {};
92904
- for (const kind of view.kinds) kinds[kind] = theme.roles[roleForKind(kind, view.name)] ?? {};
92905
- return { palette: theme.palette, kinds, levels: theme.levels };
92913
+ for (const kind of view.kinds) kinds[kind] = theme.roles[roleForKind(kind)] ?? {};
92914
+ return { palette: theme.palette, kinds };
92906
92915
  }
92907
92916
 
92908
92917
  // src/element-tree.ts
@@ -93020,7 +93029,7 @@ function parseFlow(p, sourceToken) {
93020
93029
  model.flows.push(flow);
93021
93030
  }
93022
93031
  function parseElement(p, sourceToken, parent) {
93023
- const { matchToken, advance, reportError, lookAhead, syncToNextLine, model } = p;
93032
+ const { matchToken, advance, reportError, syncToNextLine, model } = p;
93024
93033
  if (!matchToken("id")) {
93025
93034
  reportError(
93026
93035
  `invalid declaration: \`${sourceToken.text}\` alone on this line`,
@@ -93040,28 +93049,6 @@ function parseElement(p, sourceToken, parent) {
93040
93049
  parent: parent ?? void 0
93041
93050
  };
93042
93051
  if (matchToken("str")) element.label = advance().text;
93043
- if (matchToken("lparen")) {
93044
- advance();
93045
- if (matchToken("id")) {
93046
- const token = advance();
93047
- element.attr = {
93048
- value: token.text,
93049
- span: token.span
93050
- };
93051
- } else
93052
- reportError(
93053
- "attribute value expected after `(`",
93054
- lookAhead().span,
93055
- 'e.g. `trust-zone DMZ "DMZ" (public)`'
93056
- );
93057
- if (matchToken("rparen")) advance();
93058
- else
93059
- reportError(
93060
- "`)` expected to close the attribute",
93061
- lookAhead().span,
93062
- 'e.g. `trust-zone DMZ "DMZ" (public)`'
93063
- );
93064
- }
93065
93052
  if (matchToken("lbrace")) {
93066
93053
  advance();
93067
93054
  p.parseElementBody(element);
@@ -93146,6 +93133,34 @@ function tryOrderEntry(p, parent) {
93146
93133
  parent.order = { value, span: valueToken.span };
93147
93134
  return true;
93148
93135
  }
93136
+ function tryLogoEntry(p, parent) {
93137
+ const { matchToken, advance, reportCoded, save, restore, syncToNextLine } = p;
93138
+ if (!parent || !matchToken("id", "logo")) return false;
93139
+ const mark = save();
93140
+ const keyToken = advance();
93141
+ if (!matchToken("colon")) {
93142
+ restore(mark);
93143
+ return false;
93144
+ }
93145
+ advance();
93146
+ const valueToken = matchToken("id") || matchToken("str") ? advance() : null;
93147
+ if (!valueToken || valueToken.text.length === 0) {
93148
+ reportCoded(
93149
+ "E0105",
93150
+ "`logo` expects a built-in name or a quoted file path",
93151
+ (valueToken ?? keyToken).span,
93152
+ 'e.g. `logo: react`, or `logo: "./logos/acme.svg"` for your own'
93153
+ );
93154
+ syncToNextLine();
93155
+ return true;
93156
+ }
93157
+ parent.logo = {
93158
+ value: valueToken.text,
93159
+ source: valueToken.kind === "str" ? "file" : "builtin",
93160
+ span: valueToken.span
93161
+ };
93162
+ return true;
93163
+ }
93149
93164
  function tryLegendBlock(p) {
93150
93165
  const { matchToken, advance, reportError, lookAhead, skipNewlines, syncToNextLine, model } = p;
93151
93166
  if (!matchToken("id", "legend")) return false;
@@ -93353,6 +93368,7 @@ function parse(src) {
93353
93368
  }
93354
93369
  if (tryStyleBlock(parent)) return;
93355
93370
  if (tryOrderEntry(parser, parent)) return;
93371
+ if (tryLogoEntry(parser, parent)) return;
93356
93372
  if (!parent && tryLegendBlock(parser)) return;
93357
93373
  if (!parent && tryBusinessObject(parser)) return;
93358
93374
  parseFlowOrElement(parser, parent);
@@ -93589,6 +93605,7 @@ function applyStyleEntry(entry) {
93589
93605
  // src/views.ts
93590
93606
  var logicalView = {
93591
93607
  name: "logical",
93608
+ laneKinds: ["external"],
93592
93609
  kinds: ["actor-group", "actor", "system", "layer", "block", "external"],
93593
93610
  containerKinds: ["actor-group", "system", "layer", "external"],
93594
93611
  // Logical flows carry no technical detail (flowTech* are null below), so the
@@ -93711,17 +93728,32 @@ var logicalView = {
93711
93728
  };
93712
93729
  var applicationView = {
93713
93730
  name: "application",
93731
+ laneKinds: ["external"],
93714
93732
  kinds: [
93715
93733
  "actor-group",
93716
93734
  "actor",
93717
93735
  "system",
93718
93736
  "application",
93719
93737
  "module",
93738
+ "gateway",
93739
+ "auth",
93740
+ "idp",
93720
93741
  "queue",
93721
93742
  "datastore",
93722
93743
  "external"
93723
93744
  ],
93724
93745
  containerKinds: ["actor-group", "system", "application", "external"],
93746
+ // A logo says what a thing is built with, so it belongs to the kinds that run
93747
+ // code. `actor` and `actor-group` are people; `system` is a grouping whose
93748
+ // children carry their own stacks. `gateway`, `auth` and `idp` are left out
93749
+ // on purpose: they are drawn with a corner glyph, and the glyph renderer has
93750
+ // no logo slot — a logo on them would reserve width and draw nothing.
93751
+ logoKinds: ["application", "module", "queue", "datastore", "external"],
93752
+ // The same three kinds as the infrastructure view, drawn the same way: an API
93753
+ // gateway, an auth middleware and an identity provider are containers in
93754
+ // their own right at the application level, and the glyph is what tells them
93755
+ // apart from a plain `application` at a glance.
93756
+ glyphKinds: ["gateway", "auth", "idp"],
93725
93757
  // C4-style `(API_REST, JSON)`: the protocol half is worth tabulating, the port is not.
93726
93758
  // `zoneOf` walks ancestors nearest-first, so an endpoint inside an application
93727
93759
  // still reads `Name (App)`; the system only annotates what sits directly in it.
@@ -93733,6 +93765,9 @@ var applicationView = {
93733
93765
  "actor-group": 0,
93734
93766
  system: 1,
93735
93767
  application: 1,
93768
+ gateway: 1,
93769
+ auth: 1,
93770
+ idp: 1,
93736
93771
  queue: 1,
93737
93772
  datastore: 1,
93738
93773
  external: 2
@@ -93743,6 +93778,9 @@ var applicationView = {
93743
93778
  system: "System",
93744
93779
  application: "Application",
93745
93780
  module: "Application module",
93781
+ gateway: "Gateway / reverse proxy",
93782
+ auth: "Auth middleware",
93783
+ idp: "Identity provider (IdP)",
93746
93784
  queue: "Message queue / broker",
93747
93785
  datastore: "Datastore / registry",
93748
93786
  external: "External system"
@@ -93753,6 +93791,9 @@ var applicationView = {
93753
93791
  system: "Syst\xE8me",
93754
93792
  application: "Application",
93755
93793
  module: "Module applicatif",
93794
+ gateway: "Passerelle / proxy",
93795
+ auth: "Middleware d'authentification",
93796
+ idp: "Fournisseur d'identit\xE9 (IdP)",
93756
93797
  queue: "File de messages / broker",
93757
93798
  datastore: "Entrep\xF4t / r\xE9f\xE9rentiel",
93758
93799
  external: "Syst\xE8me externe"
@@ -93790,7 +93831,7 @@ var applicationView = {
93790
93831
  minCounts: [],
93791
93832
  isolatedWarn: {
93792
93833
  code: "W0510",
93793
- kinds: ["module", "queue", "datastore"],
93834
+ kinds: ["module", "gateway", "auth", "idp", "queue", "datastore"],
93794
93835
  message: "isolated element: no incoming or outgoing flow"
93795
93836
  },
93796
93837
  defaults: {
@@ -93812,6 +93853,20 @@ var applicationView = {
93812
93853
  fill: "#ffffff",
93813
93854
  stroke: { color: "#5b7a99", style: "solid", width: 1.3 }
93814
93855
  },
93856
+ // Same colours as the infrastructure view's `gateway`, `auth` and `idp`: one
93857
+ // thing drawn one way, whichever view it appears in.
93858
+ gateway: {
93859
+ fill: "#f5e6dd",
93860
+ stroke: { color: "#bf5530", style: "solid", width: 1.6 }
93861
+ },
93862
+ auth: {
93863
+ fill: "#e8f1fb",
93864
+ stroke: { color: "#2f6fb5", style: "solid", width: 1.5 }
93865
+ },
93866
+ idp: {
93867
+ fill: "#e0f0f0",
93868
+ stroke: { color: "#3a8f8f", style: "solid", width: 1.3 }
93869
+ },
93815
93870
  queue: {
93816
93871
  fill: "#f3eef8",
93817
93872
  stroke: { color: "#8a6fae", style: "solid", width: 1.3 }
@@ -93843,6 +93898,18 @@ var applicationView = {
93843
93898
  fill: "#252a31",
93844
93899
  stroke: { color: "#5f7f9e", style: "solid", width: 1.3 }
93845
93900
  },
93901
+ gateway: {
93902
+ fill: "#332218",
93903
+ stroke: { color: "#c96a4a", style: "solid", width: 1.6 }
93904
+ },
93905
+ auth: {
93906
+ fill: "#1d2735",
93907
+ stroke: { color: "#6fa8e0", style: "solid", width: 1.5 }
93908
+ },
93909
+ idp: {
93910
+ fill: "#1a2e2e",
93911
+ stroke: { color: "#4fafaf", style: "solid", width: 1.3 }
93912
+ },
93846
93913
  queue: {
93847
93914
  fill: "#2a2433",
93848
93915
  stroke: { color: "#7a5f9e", style: "solid", width: 1.3 }
@@ -93860,19 +93927,26 @@ var applicationView = {
93860
93927
  };
93861
93928
  var infrastructureView = {
93862
93929
  name: "infrastructure",
93930
+ laneKinds: ["external"],
93863
93931
  kinds: [
93864
93932
  "actor",
93933
+ "device",
93865
93934
  "site",
93866
93935
  "network-zone",
93867
93936
  "server",
93868
93937
  "app-instance",
93869
93938
  "queue",
93870
93939
  "gateway",
93940
+ "firewall",
93871
93941
  "auth",
93872
93942
  "idp",
93873
93943
  "external"
93874
93944
  ],
93875
93945
  containerKinds: ["site", "network-zone", "server"],
93946
+ glyphKinds: ["gateway", "firewall", "auth", "idp", "device"],
93947
+ // A `device` is a flow origin, so it belongs on the entry side with the
93948
+ // actors rather than in a declaration-order band with the zones.
93949
+ ingressKinds: ["actor", "actor-group", "device"],
93876
93950
  // The reference shape: the matrice des flux techniques as an EA dossier expects it.
93877
93951
  matrix: {
93878
93952
  zoneKinds: ["network-zone", "site"],
@@ -93883,24 +93957,28 @@ var infrastructureView = {
93883
93957
  actorLegend: true,
93884
93958
  legendNames: {
93885
93959
  actor: "User / consumer",
93960
+ device: "Device / workstation",
93886
93961
  site: "Site / data center",
93887
93962
  "network-zone": "Network zone",
93888
93963
  server: "Server / VM",
93889
93964
  "app-instance": "Deployed application",
93890
93965
  queue: "Message queue / broker",
93891
93966
  gateway: "Gateway / reverse proxy",
93967
+ firewall: "Firewall",
93892
93968
  auth: "Auth middleware",
93893
93969
  idp: "Identity provider (IdP)",
93894
93970
  external: "External system"
93895
93971
  },
93896
93972
  legendNamesFr: {
93897
93973
  actor: "Utilisateur / consommateur",
93974
+ device: "Poste de travail",
93898
93975
  site: "Site / centre de donn\xE9es",
93899
93976
  "network-zone": "Zone r\xE9seau",
93900
93977
  server: "Serveur / VM",
93901
93978
  "app-instance": "Application d\xE9ploy\xE9e",
93902
93979
  queue: "File de messages / broker",
93903
93980
  gateway: "Passerelle / proxy",
93981
+ firewall: "Pare-feu",
93904
93982
  auth: "Middleware d'authentification",
93905
93983
  idp: "Fournisseur d'identit\xE9 (IdP)",
93906
93984
  external: "Syst\xE8me externe"
@@ -93945,7 +94023,7 @@ var infrastructureView = {
93945
94023
  minCounts: [],
93946
94024
  isolatedWarn: {
93947
94025
  code: "W0510",
93948
- kinds: ["app-instance", "queue", "gateway", "auth", "idp"],
94026
+ kinds: ["app-instance", "queue", "gateway", "firewall", "auth", "idp", "device"],
93949
94027
  message: "isolated element: no incoming or outgoing flow"
93950
94028
  },
93951
94029
  defaults: {
@@ -93962,6 +94040,12 @@ var infrastructureView = {
93962
94040
  fill: "#ffffff",
93963
94041
  stroke: { color: "#55606b", style: "solid", width: 1.5 }
93964
94042
  },
94043
+ // The server's grey: a workstation is hardware like a server is, and the
94044
+ // monitor glyph is what tells the two apart.
94045
+ device: {
94046
+ fill: "#eef0f2",
94047
+ stroke: { color: "#55606b", style: "solid", width: 1.3 }
94048
+ },
93965
94049
  "app-instance": {
93966
94050
  fill: "#fff7e6",
93967
94051
  stroke: { color: "#b08d2a", style: "solid", width: 1.2 }
@@ -93974,9 +94058,13 @@ var infrastructureView = {
93974
94058
  fill: "#f5e6dd",
93975
94059
  stroke: { color: "#bf5530", style: "solid", width: 1.6 }
93976
94060
  },
94061
+ firewall: {
94062
+ fill: "#fdecea",
94063
+ stroke: { color: "#c0392b", style: "solid", width: 1.6 }
94064
+ },
93977
94065
  auth: {
93978
- fill: "#fef3e2",
93979
- stroke: { color: "#d68a2a", style: "solid", width: 1.5 }
94066
+ fill: "#e8f1fb",
94067
+ stroke: { color: "#2f6fb5", style: "solid", width: 1.5 }
93980
94068
  },
93981
94069
  idp: {
93982
94070
  fill: "#e0f0f0",
@@ -94001,6 +94089,10 @@ var infrastructureView = {
94001
94089
  fill: "#252a31",
94002
94090
  stroke: { color: "#6b7885", style: "solid", width: 1.5 }
94003
94091
  },
94092
+ device: {
94093
+ fill: "#242a30",
94094
+ stroke: { color: "#6b7885", style: "solid", width: 1.3 }
94095
+ },
94004
94096
  "app-instance": {
94005
94097
  fill: "#2e2717",
94006
94098
  stroke: { color: "#b08d2a", style: "solid", width: 1.2 }
@@ -94013,9 +94105,13 @@ var infrastructureView = {
94013
94105
  fill: "#332218",
94014
94106
  stroke: { color: "#c96a4a", style: "solid", width: 1.6 }
94015
94107
  },
94108
+ firewall: {
94109
+ fill: "#33211f",
94110
+ stroke: { color: "#e5675a", style: "solid", width: 1.6 }
94111
+ },
94016
94112
  auth: {
94017
- fill: "#332614",
94018
- stroke: { color: "#b88a30", style: "solid", width: 1.5 }
94113
+ fill: "#1d2735",
94114
+ stroke: { color: "#6fa8e0", style: "solid", width: 1.5 }
94019
94115
  },
94020
94116
  idp: {
94021
94117
  fill: "#1a2e2e",
@@ -94027,186 +94123,180 @@ var infrastructureView = {
94027
94123
  }
94028
94124
  }
94029
94125
  };
94030
- var SEC_LEVELS = ["public", "internal", "restricted", "secret"];
94031
- var securityView = {
94032
- name: "security",
94033
- kinds: ["trust-zone", "security-node", "asset", "actor-group", "actor", "external"],
94034
- containerKinds: ["trust-zone", "actor-group"],
94035
- // Security flows state their encryption (W0561) but no port; the trust zone
94036
- // an endpoint sits in is the point of the view, so it annotates every row.
94037
- matrix: {
94038
- zoneKinds: ["trust-zone"],
94039
- columns: ["num", "source", "dest", "proto", "nature"]
94126
+ var views = {
94127
+ logical: logicalView,
94128
+ application: applicationView,
94129
+ infrastructure: infrastructureView
94130
+ };
94131
+
94132
+ // src/logos.ts
94133
+ var LOGOS = {
94134
+ angular: {
94135
+ title: "Angular",
94136
+ 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",
94137
+ license: "CC-BY-4.0",
94138
+ licenseUrl: "https://creativecommons.org/licenses/by/4.0/legalcode",
94139
+ source: "https://angular.dev/press-kit"
94040
94140
  },
94041
- legendNames: {
94042
- "trust-zone": "Trust zone (sensitivity)",
94043
- "security-node": "Filtering / security node",
94044
- asset: "Sensitive asset",
94045
- "actor-group": "Actor group",
94046
- actor: "Actor",
94047
- external: "Untrusted external"
94141
+ apache: {
94142
+ title: "Apache",
94143
+ 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",
94144
+ license: "Apache-2.0",
94145
+ licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt",
94146
+ source: "https://www.apache.org/foundation/press/kit"
94048
94147
  },
94049
- legendNamesFr: {
94050
- "trust-zone": "Zone de confiance (sensibilit\xE9)",
94051
- "security-node": "N\u0153ud de filtrage / s\xE9curit\xE9",
94052
- asset: "Actif sensible",
94053
- "actor-group": "Groupe d'acteurs",
94054
- actor: "Acteur",
94055
- external: "Externe non ma\xEEtris\xE9"
94148
+ apachekafka: {
94149
+ title: "Apache Kafka",
94150
+ 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",
94151
+ license: "Apache-2.0",
94152
+ licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt",
94153
+ source: "https://apache.org/logos"
94056
94154
  },
94057
- bandTitles: {
94058
- flows: "FLOWS",
94059
- objects: "BUSINESS OBJECTS",
94060
- legend: "LEGEND"
94155
+ apachespark: {
94156
+ title: "Apache Spark",
94157
+ 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",
94158
+ license: "Apache-2.0",
94159
+ licenseUrl: "https://www.apache.org/licenses/LICENSE-2.0.txt",
94160
+ source: "https://apache.org/logos"
94061
94161
  },
94062
- legendFlowLabel: "Security flow \u2014 cross-zone flows should be filtered and encrypted",
94063
- legendFlowLabelFr: "Flux de s\xE9curit\xE9 \u2014 les flux inter-zones doivent \xEAtre filtr\xE9s et chiffr\xE9s",
94064
- partitions: {},
94065
- partitionByOrder: true,
94066
- flowLabelRequired: {
94067
- code: "E0203",
94068
- message: "flow without a label",
94069
- help: 'add a label describing the exchange: `A -> B : "\u2026" (TLS1.3)`'
94162
+ django: {
94163
+ title: "Django",
94164
+ 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"
94070
94165
  },
94071
- flowTechRequired: null,
94072
- flowTechRecommended: null,
94073
- attrSpec: {
94074
- kind: "trust-zone",
94075
- values: SEC_LEVELS,
94076
- code: "E0250",
94077
- message: "trust zone without a valid sensitivity level",
94078
- help: 'set a level in parentheses: `trust-zone DMZ "DMZ" (public)` \u2014 one of public, internal, restricted, secret'
94166
+ docker: {
94167
+ title: "Docker",
94168
+ 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"
94079
94169
  },
94080
- trustOrder: { public: 0, internal: 1, restricted: 2, secret: 3 },
94081
- boundaryLint: {
94082
- code: "W0560",
94083
- nodeKind: "security-node",
94084
- message: "unfiltered trust-boundary crossing",
94085
- help: "route this flow through a `security-node` (firewall/WAF/bastion), or confirm the direct path is intended"
94170
+ dotnet: {
94171
+ title: ".NET",
94172
+ 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"
94086
94173
  },
94087
- crossZoneTechRecommended: {
94088
- code: "W0561",
94089
- message: "cross-zone flow without stated encryption/protocol",
94090
- help: 'add the protocol/encryption on inter-zone flows: `A -> B : "\u2026" (TLS1.3)`'
94174
+ elasticsearch: {
94175
+ title: "Elasticsearch",
94176
+ 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"
94091
94177
  },
94092
- nesting: [
94093
- {
94094
- code: "E0217",
94095
- child: "asset",
94096
- parents: ["trust-zone"],
94097
- message: "sensitive asset outside any trust zone",
94098
- help: "move this `asset` inside a `trust-zone`"
94099
- },
94100
- {
94101
- code: "E0218",
94102
- child: "security-node",
94103
- parents: ["trust-zone"],
94104
- message: "security node outside any trust zone",
94105
- help: "place this `security-node` inside a `trust-zone` (typically the exposed one it protects)"
94106
- },
94107
- {
94108
- code: "E0211",
94109
- child: "actor",
94110
- parents: ["actor-group"],
94111
- message: "actor outside any group",
94112
- help: "move this `actor` inside an `actor-group`"
94113
- }
94114
- ],
94115
- minCounts: [],
94116
- isolatedWarn: {
94117
- code: "W0510",
94118
- kinds: ["asset"],
94119
- message: "isolated element: no incoming or outgoing flow"
94178
+ express: {
94179
+ title: "Express",
94180
+ 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"
94120
94181
  },
94121
- defaults: {
94122
- "trust-zone": {
94123
- fill: "#f5f5f4",
94124
- stroke: { color: "#8a8a85", style: "solid", width: 1.3 }
94125
- },
94126
- "security-node": {
94127
- fill: "#fff7e6",
94128
- stroke: { color: "#c46b2a", style: "solid", width: 1.6 }
94129
- },
94130
- asset: {
94131
- fill: "#ffffff",
94132
- stroke: { color: "#55606b", style: "solid", width: 1.3 }
94133
- },
94134
- "actor-group": {
94135
- fill: "#eef4fb",
94136
- stroke: { color: "#7a9cc4", style: "dashed", width: 1.2 }
94137
- },
94138
- external: {
94139
- fill: "#fdecea",
94140
- stroke: { color: "#d9534f", style: "dashed", width: 1.3 }
94141
- },
94142
- actor: {}
94182
+ fastapi: {
94183
+ title: "FastAPI",
94184
+ 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"
94143
94185
  },
94144
- defaultsDark: {
94145
- "trust-zone": {
94146
- fill: "#26261f",
94147
- stroke: { color: "#8a8a72", style: "solid", width: 1.3 }
94148
- },
94149
- "security-node": {
94150
- fill: "#2e2717",
94151
- stroke: { color: "#c46b2a", style: "solid", width: 1.6 }
94152
- },
94153
- asset: {
94154
- fill: "#252a31",
94155
- stroke: { color: "#6b7885", style: "solid", width: 1.3 }
94156
- },
94157
- "actor-group": {
94158
- fill: "#232a33",
94159
- stroke: { color: "#5c7fa8", style: "dashed", width: 1.2 }
94160
- },
94161
- external: {
94162
- fill: "#3a2422",
94163
- stroke: { color: "#c25a54", style: "dashed", width: 1.3 }
94164
- },
94165
- actor: {}
94186
+ github: {
94187
+ title: "GitHub",
94188
+ 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"
94166
94189
  },
94167
- levelDefaults: {
94168
- public: {
94169
- fill: "#fdecea",
94170
- stroke: { color: "#d9534f", style: "solid", width: 1.4 }
94171
- },
94172
- internal: {
94173
- fill: "#fff4e5",
94174
- stroke: { color: "#e0a458", style: "solid", width: 1.4 }
94175
- },
94176
- restricted: {
94177
- fill: "#e8f1f8",
94178
- stroke: { color: "#5b8db8", style: "solid", width: 1.4 }
94179
- },
94180
- secret: {
94181
- fill: "#ece8f5",
94182
- stroke: { color: "#7a5fae", style: "solid", width: 1.4 }
94183
- }
94190
+ gitlab: {
94191
+ title: "GitLab",
94192
+ 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"
94184
94193
  },
94185
- levelDefaultsDark: {
94186
- public: {
94187
- fill: "#3a2422",
94188
- stroke: { color: "#c25a54", style: "solid", width: 1.4 }
94189
- },
94190
- internal: {
94191
- fill: "#332a1c",
94192
- stroke: { color: "#c08a44", style: "solid", width: 1.4 }
94193
- },
94194
- restricted: {
94195
- fill: "#1f2a33",
94196
- stroke: { color: "#4a7ba6", style: "solid", width: 1.4 }
94197
- },
94198
- secret: {
94199
- fill: "#2a2433",
94200
- stroke: { color: "#7a5f9e", style: "solid", width: 1.4 }
94201
- }
94194
+ go: {
94195
+ title: "Go",
94196
+ 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"
94197
+ },
94198
+ googlecloud: {
94199
+ title: "Google Cloud",
94200
+ 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"
94201
+ },
94202
+ grafana: {
94203
+ title: "Grafana",
94204
+ 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"
94205
+ },
94206
+ graphql: {
94207
+ title: "GraphQL",
94208
+ 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"
94209
+ },
94210
+ javascript: {
94211
+ title: "JavaScript",
94212
+ 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",
94213
+ license: "MIT",
94214
+ licenseUrl: "https://spdx.org/licenses/MIT.html",
94215
+ source: "https://github.com/voodootikigod/logo.js/blob/1544bdeed6d618a6cfe4f0650d04ab8d9cfa76d9/js.svg",
94216
+ copyright: "Copyright (c) 2011 Christopher Williams <chris@iterativedesigns.com>"
94217
+ },
94218
+ kotlin: { title: "Kotlin", d: "M24 24H0V0h24L12 12Z" },
94219
+ kubernetes: {
94220
+ title: "Kubernetes",
94221
+ 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"
94222
+ },
94223
+ mongodb: {
94224
+ title: "MongoDB",
94225
+ 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"
94226
+ },
94227
+ mysql: {
94228
+ title: "MySQL",
94229
+ 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"
94230
+ },
94231
+ natsdotio: {
94232
+ title: "NATS.io",
94233
+ 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"
94234
+ },
94235
+ nextdotjs: {
94236
+ title: "Next.js",
94237
+ 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"
94238
+ },
94239
+ nginx: {
94240
+ title: "NGINX",
94241
+ 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"
94242
+ },
94243
+ nodedotjs: {
94244
+ title: "Node.js",
94245
+ 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"
94246
+ },
94247
+ openjdk: {
94248
+ title: "OpenJDK",
94249
+ 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",
94250
+ license: "BSD-3-Clause",
94251
+ licenseUrl: "https://spdx.org/licenses/BSD-3-Clause.html",
94252
+ source: "https://github.com/openjdk/duke/blob/master/vector/Agent.svg"
94253
+ },
94254
+ postgresql: {
94255
+ title: "PostgreSQL",
94256
+ 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"
94257
+ },
94258
+ prometheus: {
94259
+ title: "Prometheus",
94260
+ 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"
94261
+ },
94262
+ python: {
94263
+ title: "Python",
94264
+ 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"
94265
+ },
94266
+ rabbitmq: {
94267
+ title: "RabbitMQ",
94268
+ 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"
94269
+ },
94270
+ react: {
94271
+ title: "React",
94272
+ 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"
94273
+ },
94274
+ redis: {
94275
+ title: "Redis",
94276
+ 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"
94277
+ },
94278
+ spring: {
94279
+ title: "Spring",
94280
+ 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"
94281
+ },
94282
+ sqlite: {
94283
+ title: "SQLite",
94284
+ 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"
94285
+ },
94286
+ terraform: {
94287
+ title: "Terraform",
94288
+ 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"
94289
+ },
94290
+ typescript: {
94291
+ title: "TypeScript",
94292
+ 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"
94293
+ },
94294
+ vault: {
94295
+ title: "Vault",
94296
+ 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"
94202
94297
  }
94203
94298
  };
94204
- var views = {
94205
- logical: logicalView,
94206
- application: applicationView,
94207
- infrastructure: infrastructureView,
94208
- security: securityView
94209
- };
94299
+ var LOGO_NAMES = Object.keys(LOGOS);
94210
94300
 
94211
94301
  // src/validator.ts
94212
94302
  function validate(model) {
@@ -94230,8 +94320,7 @@ function validate(model) {
94230
94320
  ...checkMissingLabels(elements),
94231
94321
  ...checkNesting(elements, view),
94232
94322
  ...checkFlows(model, view),
94233
- ...checkElementAttributes(elements, view),
94234
- ...checkTrustBoundaries(model, view),
94323
+ ...checkLogos(elements, view),
94235
94324
  ...checkBusinessObjects(model, view),
94236
94325
  ...checkMinimumCounts(elements, model, view),
94237
94326
  ...checkIsolatedElements(model, view, elements)
@@ -94273,6 +94362,48 @@ function checkUnknownKinds(elements, view) {
94273
94362
  }
94274
94363
  return diagnostics;
94275
94364
  }
94365
+ function checkLogos(elements, view) {
94366
+ const diagnostics = [];
94367
+ const logoKinds = view.logoKinds ?? [];
94368
+ for (const element of elements) {
94369
+ const logo = element.logo;
94370
+ if (!logo) continue;
94371
+ if (!logoKinds.includes(element.kind)) {
94372
+ diagnostics.push({
94373
+ code: "E0108",
94374
+ severity: "error",
94375
+ message: `\`${element.kind}\` does not carry a logo`,
94376
+ span: logo.span,
94377
+ note: logoKinds.length ? `the \`${view.name}\` view accepts \`logo\` on: ${logoKinds.join(", ")}` : `the \`${view.name}\` view has no kinds that carry a logo`
94378
+ });
94379
+ continue;
94380
+ }
94381
+ if (logo.source === "builtin") {
94382
+ if (Object.hasOwn(LOGOS, logo.value)) continue;
94383
+ const suggestion = nearest(logo.value, LOGO_NAMES);
94384
+ diagnostics.push({
94385
+ code: "E0107",
94386
+ severity: "error",
94387
+ message: `unknown built-in logo \`${logo.value}\``,
94388
+ span: logo.span,
94389
+ note: `${LOGO_NAMES.length} built-ins available \u2014 run \`cairn logos\` to list them`,
94390
+ help: suggestion !== void 0 ? `did you mean \`${suggestion}\`?` : `use a file instead: \`logo: "./logos/${logo.value}.svg"\``
94391
+ });
94392
+ continue;
94393
+ }
94394
+ if (/^[a-z][a-z0-9+.-]*:/i.test(logo.value) || logo.value.startsWith("//")) {
94395
+ diagnostics.push({
94396
+ code: "E0105",
94397
+ severity: "error",
94398
+ message: `\`logo\` will not fetch \`${logo.value}\``,
94399
+ span: logo.span,
94400
+ note: "a linked logo leaves the diagram depending on a server that can change or vanish",
94401
+ help: 'download it next to the diagram and point at the file: `logo: "./logos/name.svg"`'
94402
+ });
94403
+ }
94404
+ }
94405
+ return diagnostics;
94406
+ }
94276
94407
  function checkMissingLabels(elements) {
94277
94408
  const diagnostics = [];
94278
94409
  for (const element of elements) {
@@ -94364,75 +94495,6 @@ function checkFlows(model, view) {
94364
94495
  }
94365
94496
  return diagnostics;
94366
94497
  }
94367
- function checkElementAttributes(elements, view) {
94368
- const spec = view.attrSpec;
94369
- if (!spec) return [];
94370
- const diagnostics = [];
94371
- for (const element of elements) {
94372
- if (element.kind !== spec.kind) continue;
94373
- if (!element.attr) {
94374
- diagnostics.push({
94375
- code: spec.code,
94376
- severity: "error",
94377
- message: spec.message + ` (\`${element.id}\`)`,
94378
- span: element.idSpan,
94379
- help: spec.help
94380
- });
94381
- } else if (!spec.values.includes(element.attr.value)) {
94382
- const suggestion = nearest(element.attr.value, spec.values);
94383
- diagnostics.push({
94384
- code: spec.code,
94385
- severity: "error",
94386
- message: `invalid ${spec.kind} value \`${element.attr.value}\` (\`${element.id}\`)`,
94387
- span: element.attr.span,
94388
- note: `allowed: ${spec.values.join(", ")}`,
94389
- help: suggestion ? `did you mean \`${suggestion}\`?` : spec.help
94390
- });
94391
- }
94392
- }
94393
- return diagnostics;
94394
- }
94395
- function checkTrustBoundaries(model, view) {
94396
- if (!view.boundaryLint && !view.crossZoneTechRecommended) return [];
94397
- const diagnostics = [];
94398
- const zoneOf2 = (id) => {
94399
- for (let ancestor = model.index.get(id)?.parent; ancestor; ancestor = ancestor.parent)
94400
- if (ancestor.kind === "trust-zone") return ancestor;
94401
- return void 0;
94402
- };
94403
- const trustLevelOf = (id) => {
94404
- const level = zoneOf2(id)?.attr?.value;
94405
- return level && view.trustOrder?.[level] !== void 0 ? view.trustOrder[level] : -1;
94406
- };
94407
- const lint = view.boundaryLint;
94408
- const isSecurityNode = (id) => lint !== void 0 && model.index.get(id)?.kind === lint.nodeKind;
94409
- for (const flow of model.flows) {
94410
- if (!model.index.has(flow.from) || !model.index.has(flow.to)) continue;
94411
- const crossesZone = zoneOf2(flow.from) !== zoneOf2(flow.to);
94412
- const boundaryViolation = lint !== void 0 && trustLevelOf(flow.to) > trustLevelOf(flow.from) && !isSecurityNode(flow.from) && !isSecurityNode(flow.to);
94413
- if (boundaryViolation) {
94414
- diagnostics.push({
94415
- code: lint.code,
94416
- severity: "warning",
94417
- message: lint.message,
94418
- span: flow.span,
94419
- note: `flow enters a more-trusted zone without passing a \`${lint.nodeKind}\``,
94420
- help: lint.help
94421
- });
94422
- }
94423
- if (view.crossZoneTechRecommended && crossesZone && !flow.tech?.protocol) {
94424
- diagnostics.push({
94425
- code: view.crossZoneTechRecommended.code,
94426
- severity: "warning",
94427
- message: view.crossZoneTechRecommended.message,
94428
- span: flow.span,
94429
- note: "inter-zone flow \u2014 state how the traffic is protected",
94430
- help: view.crossZoneTechRecommended.help
94431
- });
94432
- }
94433
- }
94434
- return diagnostics;
94435
- }
94436
94498
  function checkForbiddenBusinessObjects(model, view) {
94437
94499
  const diagnostics = [];
94438
94500
  for (const businessObject of model.businessObjects) {
@@ -94591,6 +94653,8 @@ function editDistance(wordA, wordB, cap) {
94591
94653
  var DEFAULT_FONT_SIZE_NODE = 12.5;
94592
94654
  var FONT_SIZE_BASE = 12.5;
94593
94655
  var CHAR_WIDTH = 0.56;
94656
+ var GLYPH_GUTTER = 26;
94657
+ var LOGO_GUTTER = 30;
94594
94658
  var fontSizes = (base) => {
94595
94659
  const scale = base / FONT_SIZE_BASE;
94596
94660
  return {
@@ -94640,11 +94704,11 @@ var flowLabelBox = (opts) => {
94640
94704
  height: measured.height + (tech ? 12 * scale : 0) + (chipNames.length ? CHIP_HEIGHT * scale : 0)
94641
94705
  };
94642
94706
  };
94643
- var nodeSize = (kind, label, fontSize = DEFAULT_FONT_SIZE_NODE) => {
94707
+ var nodeSize = (kind, label, fontSize = DEFAULT_FONT_SIZE_NODE, gutter = 0) => {
94644
94708
  const isActor = kind === "actor";
94645
94709
  const measured = measure(label, isActor ? fontSize - 1.5 : fontSize);
94646
94710
  return {
94647
- width: isActor ? Math.max(64, measured.width + 8) : Math.max(140, measured.width + 16),
94711
+ width: isActor ? Math.max(64, measured.width + 8) : Math.max(140, measured.width + 16 + gutter),
94648
94712
  height: isActor ? 56 + (label.split("\n").length - 1) * 11 : Math.max(46, measured.height + 18)
94649
94713
  };
94650
94714
  };
@@ -94672,12 +94736,13 @@ var LaneAllocator = class {
94672
94736
  return this.lanes.length - 1;
94673
94737
  }
94674
94738
  };
94675
- function foldStyle(model) {
94739
+ function foldStyle(model, view) {
94676
94740
  const businessObjectNames = new Map(model.businessObjects.map((bo) => [bo.id, bo.name]));
94677
94741
  const numbered = model.style.flowText === "numbered";
94678
94742
  const { edge, node, cont, scale } = fontSizes(model.style.font.size);
94679
94743
  return {
94680
94744
  numbered,
94745
+ glyphKinds: new Set(view.glyphKinds ?? []),
94681
94746
  edge,
94682
94747
  node,
94683
94748
  cont,
@@ -94692,7 +94757,15 @@ function foldStyle(model) {
94692
94757
  })
94693
94758
  };
94694
94759
  }
94695
- function toElkNode(element, containerFontSize, nodeFontSize) {
94760
+ function leafSize(element, style) {
94761
+ return nodeSize(
94762
+ element.kind,
94763
+ element.label ?? element.id,
94764
+ style.node,
94765
+ style.glyphKinds.has(element.kind) ? GLYPH_GUTTER : 0
94766
+ );
94767
+ }
94768
+ function toElkNode(element, style) {
94696
94769
  if (element.children.length) {
94697
94770
  const lineCount = (element.label ?? element.id).split("\n").length;
94698
94771
  return {
@@ -94703,13 +94776,13 @@ function toElkNode(element, containerFontSize, nodeFontSize) {
94703
94776
  labels: [
94704
94777
  {
94705
94778
  text: element.label ?? element.id,
94706
- ...measure(element.label ?? element.id, containerFontSize)
94779
+ ...measure(element.label ?? element.id, style.cont)
94707
94780
  }
94708
94781
  ],
94709
- children: element.children.map((child) => toElkNode(child, containerFontSize, nodeFontSize))
94782
+ children: element.children.map((child) => toElkNode(child, style))
94710
94783
  };
94711
94784
  }
94712
- const size = nodeSize(element.kind, element.label ?? element.id, nodeFontSize);
94785
+ const size = leafSize(element, style);
94713
94786
  return { id: element.id, width: size.width, height: size.height };
94714
94787
  }
94715
94788
  function walkFoldedNodes(elkNode, offset, elementById, syntheticIds) {
@@ -94788,7 +94861,7 @@ function internalFlowLabels(flow, style) {
94788
94861
  ];
94789
94862
  }
94790
94863
  function buildGroupGraph(group, style, fg) {
94791
- const node = toElkNode(group, style.cont, style.node);
94864
+ const node = toElkNode(group, style);
94792
94865
  const nodeChildren = node.children ??= [];
94793
94866
  for (const flow of fg.interFlows) {
94794
94867
  if (fg.rootOf.get(flow.from) === group) {
@@ -94846,7 +94919,7 @@ function buildGroupGraph(group, style, fg) {
94846
94919
  function layoutColumn(elements, style) {
94847
94920
  return elements.map((group) => {
94848
94921
  const blocks = group.children.map((child) => {
94849
- const size = nodeSize(child.kind, child.label ?? child.id, style.node);
94922
+ const size = leafSize(child, style);
94850
94923
  return { element: child, width: size.width, height: size.height, x: 0, y: 0 };
94851
94924
  });
94852
94925
  const columnWidth = Math.max(
@@ -94884,7 +94957,7 @@ function layoutMiddleRows(middles, middleResults, geom, style) {
94884
94957
  let yCursor = 16 + geom.gutterHeight(0);
94885
94958
  middles.forEach((element, index) => {
94886
94959
  const result = middleResults.get(element.id) ?? null;
94887
- const size = result ? { width: result.children[0].width, height: result.children[0].height } : nodeSize(element.kind, element.label ?? element.id, style.node);
94960
+ const size = result ? { width: result.children[0].width, height: result.children[0].height } : leafSize(element, style);
94888
94961
  rows.push({
94889
94962
  element,
94890
94963
  box: { x: geom.xMiddle, y: yCursor, width: size.width, height: size.height },
@@ -95048,7 +95121,7 @@ var sideMid = (box, side) => ({
95048
95121
  });
95049
95122
  async function foldedLayout(model, view, elk) {
95050
95123
  const roots = model.elements;
95051
- const style = foldStyle(model);
95124
+ const style = foldStyle(model, view);
95052
95125
  const partitionOf = (element) => view.partitions[element.kind] ?? 1;
95053
95126
  const sources = roots.filter((element) => partitionOf(element) === 0);
95054
95127
  const middles = roots.filter((element) => partitionOf(element) === 1);
@@ -95095,7 +95168,7 @@ async function foldedLayout(model, view, elk) {
95095
95168
  const widthSource = Math.max(0, ...sourceColumns.map((col) => col.width));
95096
95169
  const widthMiddle = Math.max(
95097
95170
  ...middles.map(
95098
- (element) => element.children.length ? middleResults.get(element.id).children[0].width : nodeSize(element.kind, element.label ?? element.id, style.node).width
95171
+ (element) => element.children.length ? middleResults.get(element.id).children[0].width : leafSize(element, style).width
95099
95172
  )
95100
95173
  );
95101
95174
  const widthSink = Math.max(0, ...sinkColumns.map((col) => col.width));
@@ -96085,8 +96158,7 @@ function labelSeatable(blockers, pts, label) {
96085
96158
  function scanLabelSeats(ctx, subj, profile) {
96086
96159
  for (const label of subj.edge.labels) {
96087
96160
  if (!label.width || !label.height) continue;
96088
- if (!labelSeatable(ctx.blockers, subj.pts, label))
96089
- profile.set(`unlabelled:${subj.edge.id}`, 1);
96161
+ if (!labelSeatable(ctx.blockers, subj.pts, label)) profile.set(`unlabelled:${subj.edge.id}`, 1);
96090
96162
  }
96091
96163
  }
96092
96164
  function scanArrowRoom(subj, profile) {
@@ -96159,8 +96231,7 @@ function scanCrossings(a, b, key, profile) {
96159
96231
  if (!end) continue;
96160
96232
  const dx = Math.max(0, end.node.x - hit.x, hit.x - (end.node.x + end.node.width));
96161
96233
  const dy = Math.max(0, end.node.y - hit.y, hit.y - (end.node.y + end.node.height));
96162
- if (dx * dx + dy * dy <= FAN_REACH * FAN_REACH)
96163
- profile.set(`fan:${key}@${end.node.id}`, 2);
96234
+ if (dx * dx + dy * dy <= FAN_REACH * FAN_REACH) profile.set(`fan:${key}@${end.node.id}`, 2);
96164
96235
  }
96165
96236
  }
96166
96237
  }
@@ -96188,8 +96259,7 @@ function scanSharedSeats(a, b, key, profile) {
96188
96259
  }
96189
96260
  const side = vertical ? seatA.node.height : seatA.node.width;
96190
96261
  const need = Math.min(12, (side - 6) / 2);
96191
- if (apart < need * 0.8)
96192
- profile.set(`tight:${key}@${seatA.node.id}|${seatA.side}`, 4);
96262
+ if (apart < need * 0.8) profile.set(`tight:${key}@${seatA.node.id}|${seatA.side}`, 4);
96193
96263
  }
96194
96264
  }
96195
96265
  function scanPairs(ctx, subj, query) {
@@ -96204,7 +96274,11 @@ function scanPairs(ctx, subj, query) {
96204
96274
  if (ids.has(other.id) && other.id < subj.edge.id) continue;
96205
96275
  const overridden = overrides.has(other.id);
96206
96276
  if (farApart(myBounds, ctx.boundsFor(other, theirs, overridden))) continue;
96207
- const them = { edge: other, pts: theirs, ends: ctx.endsFor(other, theirs, overridden) };
96277
+ const them = {
96278
+ edge: other,
96279
+ pts: theirs,
96280
+ ends: ctx.endsFor(other, theirs, overridden)
96281
+ };
96208
96282
  const key = pair(subj.edge.id, other.id);
96209
96283
  scanCrossings(subj, them, key, profile);
96210
96284
  scanParallelRuns(mine, ctx.runsFor(other, theirs, overridden), key, profile);
@@ -99001,7 +99075,8 @@ function computeIngressExternalElements(model) {
99001
99075
  }
99002
99076
  var orderOption = (element) => element.order ? { "elk.position": `(0,${element.order.value})` } : {};
99003
99077
  var semiInteractiveOption = (children) => children.some((child) => child.order) ? { "elk.layered.crossingMinimization.semiInteractive": "true" } : {};
99004
- function toElkNode2(element, compact, fonts, root = false) {
99078
+ function toElkNode2(element, sizing, root = false) {
99079
+ const { compact, fonts, glyphKinds } = sizing;
99005
99080
  const { cont: containerFontSize, node: nodeFontSize } = fonts;
99006
99081
  if (element.children.length) {
99007
99082
  const lineCount = (element.label ?? element.id).split("\n").length;
@@ -99018,17 +99093,16 @@ function toElkNode2(element, compact, fonts, root = false) {
99018
99093
  ...measure(element.label ?? element.id, containerFontSize)
99019
99094
  }
99020
99095
  ],
99021
- children: element.children.map(
99022
- (child) => toElkNode2(child, compact, fonts)
99023
- )
99096
+ children: element.children.map((child) => toElkNode2(child, sizing))
99024
99097
  };
99025
99098
  }
99026
99099
  const measured = measure(element.label ?? element.id, nodeFontSize);
99027
99100
  const isActor = element.kind === "actor";
99101
+ const gutter = (glyphKinds.has(element.kind) ? GLYPH_GUTTER : 0) + (element.logo ? LOGO_GUTTER : 0);
99028
99102
  return {
99029
99103
  id: element.id,
99030
99104
  ...element.order && !root ? { layoutOptions: orderOption(element) } : {},
99031
- 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)),
99105
+ 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),
99032
99106
  height: isActor ? 54 + ((element.label ?? element.id).split("\n").length - 1) * 11 : Math.max(compact ? 36 : 38, measured.height + (compact ? 10 : 12))
99033
99107
  };
99034
99108
  }
@@ -99187,20 +99261,20 @@ function selectionExtras(scene, model) {
99187
99261
  }
99188
99262
  return extra;
99189
99263
  }
99190
- function relayoutVerdict(before, after) {
99264
+ function tallyProfile(profile) {
99191
99265
  const normalize = (key) => key.replace(/@[-\d.,]+$/, "").replace(/:\d+(?=@|$)/, "");
99192
- const tally = (profile) => {
99193
- const out = /* @__PURE__ */ new Map();
99194
- for (const [key, tier] of profile) {
99195
- const id = normalize(key);
99196
- const entry = out.get(id) ?? { tier, count: 0 };
99197
- entry.count++;
99198
- out.set(id, entry);
99199
- }
99200
- return out;
99201
- };
99202
- const was = tally(before);
99203
- const now = tally(after);
99266
+ const out = /* @__PURE__ */ new Map();
99267
+ for (const [key, tier] of profile) {
99268
+ const id = normalize(key);
99269
+ const entry = out.get(id) ?? { tier, count: 0 };
99270
+ entry.count++;
99271
+ out.set(id, entry);
99272
+ }
99273
+ return out;
99274
+ }
99275
+ function relayoutVerdict(before, after) {
99276
+ const was = tallyProfile(before);
99277
+ const now = tallyProfile(after);
99204
99278
  for (let tier = 0; tier < 5; tier++) {
99205
99279
  let gained = false;
99206
99280
  let lost = false;
@@ -99216,6 +99290,62 @@ function relayoutVerdict(before, after) {
99216
99290
  }
99217
99291
  return -1;
99218
99292
  }
99293
+ function noLadderRegression(before, after) {
99294
+ const perTier = (profile) => {
99295
+ const totals = [0, 0, 0, 0, 0];
99296
+ for (const { tier, count } of tallyProfile(profile).values()) totals[tier] += count;
99297
+ return totals;
99298
+ };
99299
+ const was = perTier(before);
99300
+ const now = perTier(after);
99301
+ return now.every((count, tier) => count <= was[tier]);
99302
+ }
99303
+ function nodeCoverage(scene) {
99304
+ if (!scene.nodes.length) return 1;
99305
+ const left = Math.min(...scene.nodes.map((n) => n.x));
99306
+ const top = Math.min(...scene.nodes.map((n) => n.y));
99307
+ const right = Math.max(...scene.nodes.map((n) => n.x + n.width));
99308
+ const bottom = Math.max(...scene.nodes.map((n) => n.y + n.height));
99309
+ const width = right - left;
99310
+ const height = bottom - top;
99311
+ if (width <= 0 || height <= 0) return 1;
99312
+ const COLUMNS = 32;
99313
+ const ROWS = 16;
99314
+ let covered = 0;
99315
+ for (let row = 0; row < ROWS; row++) {
99316
+ for (let column = 0; column < COLUMNS; column++) {
99317
+ const cellLeft = left + column * width / COLUMNS;
99318
+ const cellRight = left + (column + 1) * width / COLUMNS;
99319
+ const cellTop = top + row * height / ROWS;
99320
+ const cellBottom = top + (row + 1) * height / ROWS;
99321
+ if (scene.nodes.some(
99322
+ (n) => n.x < cellRight && n.x + n.width > cellLeft && n.y < cellBottom && n.y + n.height > cellTop
99323
+ ))
99324
+ covered++;
99325
+ }
99326
+ }
99327
+ return covered / (COLUMNS * ROWS);
99328
+ }
99329
+ async function denserLayout(base, elkPass) {
99330
+ const densitySpecs = [
99331
+ { minLayers: true, dense: true },
99332
+ { placement: "LINEAR_SEGMENTS", dense: true }
99333
+ ];
99334
+ const laidOut = await Promise.allSettled(densitySpecs.map((spec) => elkPass.layout(spec)));
99335
+ const areaOf = (scene) => scene.width * scene.height;
99336
+ const baseProfile = elkPass.profile(base);
99337
+ let bestArea = areaOf(base) * DENSITY_GAIN;
99338
+ let winner = null;
99339
+ for (const [index, settled] of laidOut.entries()) {
99340
+ if (settled.status !== "fulfilled") continue;
99341
+ const candidate = elkPass.toScene(settled.value);
99342
+ if (areaOf(candidate) > bestArea) continue;
99343
+ if (!noLadderRegression(baseProfile, elkPass.profile(candidate))) continue;
99344
+ bestArea = areaOf(candidate);
99345
+ winner = { scene: candidate, options: densitySpecs[index] };
99346
+ }
99347
+ return winner;
99348
+ }
99219
99349
  var centerOf = (n) => ({ x: n.x + n.width / 2, y: n.y + n.height / 2 });
99220
99350
  function sideToward(from, to) {
99221
99351
  const dx = centerOf(to).x - centerOf(from).x;
@@ -99277,13 +99407,17 @@ function constrainPorts(graph, scene, flagged, model) {
99277
99407
  elkNode.ports = [...elkNode.ports ?? [], ...ports];
99278
99408
  }
99279
99409
  }
99410
+ var DENSE_ENOUGH = 0.6;
99411
+ var DENSITY_GAIN = 0.95;
99280
99412
  var INGRESS_PARTITION = -1;
99413
+ var DEFAULT_INGRESS_KINDS = ["actor", "actor-group"];
99281
99414
  var EGRESS_PARTITION = 900;
99282
99415
  var COMPACT_WRAP = 10;
99283
99416
  var SLOT_SCALE = 1e3;
99284
99417
  function elkPartitionOf(element, index, view, ingressExternal) {
99285
99418
  if (!view.partitionByOrder) return view.partitions[element.kind] ?? 1;
99286
- if (element.kind === "actor" || element.kind === "actor-group") return INGRESS_PARTITION;
99419
+ if ((view.ingressKinds ?? DEFAULT_INGRESS_KINDS).includes(element.kind))
99420
+ return INGRESS_PARTITION;
99287
99421
  if (element.kind === "external")
99288
99422
  return ingressExternal.has(element.id) ? INGRESS_PARTITION : EGRESS_PARTITION;
99289
99423
  if (view.partitions[element.kind] !== void 0) return 90 + view.partitions[element.kind];
@@ -99307,16 +99441,13 @@ function readingSlots(model, view, ingressExternal) {
99307
99441
  const lowestOf = /* @__PURE__ */ new Map();
99308
99442
  for (const band of new Set(bandOf.values())) {
99309
99443
  const members = model.elements.filter((element) => bandOf.get(element.id) === band);
99310
- const declared = [
99311
- ...new Set(members.filter((m) => m.order).map((m) => m.order.value))
99312
- ].sort((a, b) => a - b);
99444
+ const declared = [...new Set(members.filter((m) => m.order).map((m) => m.order.value))].sort(
99445
+ (a, b) => a - b
99446
+ );
99313
99447
  const lowest = declared.length ? 1 : 0;
99314
99448
  lowestOf.set(band, lowest);
99315
99449
  for (const member of members)
99316
- slotOf.set(
99317
- member.id,
99318
- member.order ? declared.indexOf(member.order.value) + 1 : lowest
99319
- );
99450
+ slotOf.set(member.id, member.order ? declared.indexOf(member.order.value) + 1 : lowest);
99320
99451
  }
99321
99452
  const ordered = new Set(model.elements.filter((element) => element.order).map((e) => e.id));
99322
99453
  for (let pass = 0; pass < model.elements.length; pass++) {
@@ -99452,6 +99583,7 @@ function attachSideDiagnostics(scene, model) {
99452
99583
  }
99453
99584
  function buildElkGraph(ctx, direction, options) {
99454
99585
  const { model, view, ingressExternal, slotOf, compact, numbered, fonts } = ctx;
99586
+ const glyphKinds = new Set(view.glyphKinds ?? []);
99455
99587
  const graph = {
99456
99588
  id: "root",
99457
99589
  layoutOptions: {
@@ -99487,19 +99619,28 @@ function buildElkGraph(ctx, direction, options) {
99487
99619
  "elk.spacing.edgeNode": "18",
99488
99620
  "elk.layered.thoroughness": "80",
99489
99621
  "elk.layered.nodePlacement.favorStraightEdges": "true"
99490
- } : {}
99622
+ } : {},
99623
+ // Spread last, after every spacing default above, because overriding them
99624
+ // is the whole point — a density candidate that lands before them is
99625
+ // silently shadowed and lays out exactly like the layout it was meant to
99626
+ // beat.
99627
+ ...options?.dense ? {
99628
+ "elk.layered.spacing.nodeNodeBetweenLayers": "14",
99629
+ "elk.spacing.nodeNode": "10",
99630
+ "elk.spacing.edgeEdge": "8",
99631
+ "elk.spacing.edgeNode": "9"
99632
+ } : {},
99633
+ ...options?.placement ? { "elk.layered.nodePlacement.strategy": options.placement } : {}
99491
99634
  },
99492
99635
  children: model.elements.map((element, index) => {
99493
- const elkNode = toElkNode2(element, compact, fonts, true);
99636
+ const elkNode = toElkNode2(element, { compact, fonts, glyphKinds }, true);
99494
99637
  const band = elkPartitionOf(element, index, view, ingressExternal);
99495
99638
  const slot = slotOf.get(element.id);
99496
99639
  elkNode.layoutOptions = {
99497
99640
  ...elkNode.layoutOptions,
99498
99641
  // Without an `order:` anywhere the band is emitted as it always was, so
99499
99642
  // the drawing is byte-identical to one from before the hint existed.
99500
- "elk.partitioning.partition": String(
99501
- slot === void 0 ? band : band * SLOT_SCALE + slot
99502
- )
99643
+ "elk.partitioning.partition": String(slot === void 0 ? band : band * SLOT_SCALE + slot)
99503
99644
  };
99504
99645
  return elkNode;
99505
99646
  }),
@@ -99518,8 +99659,123 @@ function recordRepairs(scene, routesBefore) {
99518
99659
  if (moved) edge.repairedFrom = original;
99519
99660
  }
99520
99661
  }
99662
+ function segmentsCross2(a1, a2, b1, b2) {
99663
+ const aH = Math.abs(a1.y - a2.y) < 0.5;
99664
+ const bH = Math.abs(b1.y - b2.y) < 0.5;
99665
+ if (aH === bH) return false;
99666
+ const [h1, h2, v1, v2] = aH ? [a1, a2, b1, b2] : [b1, b2, a1, a2];
99667
+ const hy = h1.y;
99668
+ const vx = v1.x;
99669
+ 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;
99670
+ }
99671
+ function shortJogs(edges) {
99672
+ let total = 0;
99673
+ for (const edge of edges)
99674
+ for (let i = 1; i + 2 < edge.pts.length; i++) {
99675
+ const run = Math.abs(edge.pts[i + 1].x - edge.pts[i].x) + Math.abs(edge.pts[i + 1].y - edge.pts[i].y);
99676
+ if (run > 0 && run <= 20) total++;
99677
+ }
99678
+ return total;
99679
+ }
99680
+ function crossingsAround(scene, subject) {
99681
+ let total = 0;
99682
+ const subjectIds = new Set(subject.map((edge) => edge.id));
99683
+ for (const edge of subject)
99684
+ for (const other of scene.edges) {
99685
+ if (other.id === edge.id) continue;
99686
+ if (subjectIds.has(other.id) && other.id < edge.id) continue;
99687
+ for (let i = 1; i < edge.pts.length; i++)
99688
+ for (let j = 1; j < other.pts.length; j++)
99689
+ if (segmentsCross2(edge.pts[i - 1], edge.pts[i], other.pts[j - 1], other.pts[j])) total++;
99690
+ }
99691
+ return total;
99692
+ }
99693
+ function leafBoxesOf(scene) {
99694
+ return scene.nodes.filter((node) => !node.container);
99695
+ }
99696
+ function crossesLeaf(edge, leaves, attached) {
99697
+ for (let i = 1; i < edge.pts.length; i++) {
99698
+ const a = edge.pts[i - 1];
99699
+ const b = edge.pts[i];
99700
+ const horizontal = Math.abs(a.y - b.y) < 0.5;
99701
+ const vertical = Math.abs(a.x - b.x) < 0.5;
99702
+ if (!horizontal && !vertical) continue;
99703
+ const at = horizontal ? a.y : a.x;
99704
+ const lo = horizontal ? Math.min(a.x, b.x) : Math.min(a.y, b.y);
99705
+ const hi = horizontal ? Math.max(a.x, b.x) : Math.max(a.y, b.y);
99706
+ for (const node of leaves) {
99707
+ if (attached.has(node.id)) continue;
99708
+ 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;
99709
+ if (struck) return true;
99710
+ }
99711
+ }
99712
+ return false;
99713
+ }
99714
+ function snapLanes(scene, laneOf, axis) {
99715
+ if (!laneOf.size) return;
99716
+ const leaves = leafBoxesOf(scene);
99717
+ const tilted = (edges) => edges.reduce(
99718
+ (total, edge) => total + edge.pts.filter((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).length,
99719
+ 0
99720
+ );
99721
+ const touching = /* @__PURE__ */ new Map();
99722
+ const attachedTo = /* @__PURE__ */ new Map();
99723
+ for (const edge of scene.edges) {
99724
+ const ends = /* @__PURE__ */ new Set();
99725
+ for (const point of [edge.pts[0], edge.pts[edge.pts.length - 1]]) {
99726
+ if (!point) continue;
99727
+ for (const node of scene.nodes)
99728
+ 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) {
99729
+ ends.add(node.id);
99730
+ if (!touching.has(node.id)) touching.set(node.id, []);
99731
+ touching.get(node.id).push(edge);
99732
+ }
99733
+ }
99734
+ attachedTo.set(edge.id, ends);
99735
+ }
99736
+ const byLane = /* @__PURE__ */ new Map();
99737
+ for (const node of scene.nodes) {
99738
+ const lane = laneOf.get(node.id);
99739
+ if (lane === void 0) continue;
99740
+ if (!byLane.has(lane)) byLane.set(lane, []);
99741
+ byLane.get(lane).push(node);
99742
+ }
99743
+ for (const members of [...byLane.entries()].sort((a, b) => a[0] - b[0]).map((e) => e[1])) {
99744
+ if (members.length < 2) continue;
99745
+ const target = Math.max(...members.map((node) => node[axis]));
99746
+ for (const node of members) {
99747
+ const delta = target - node[axis];
99748
+ if (!delta) continue;
99749
+ const box = { x: node.x, y: node.y, w: node.width, h: node.height };
99750
+ const edges = touching.get(node.id) ?? [];
99751
+ const before = edges.map((edge) => edge.pts.map((point) => ({ ...point })));
99752
+ const crossingsBefore = crossingsAround(scene, edges);
99753
+ const jogsBefore = shortJogs(edges);
99754
+ const tiltsBefore = tilted(edges);
99755
+ node[axis] = target;
99756
+ for (const edge of edges)
99757
+ for (const point of edge.pts)
99758
+ 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)
99759
+ point[axis] += delta;
99760
+ 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
99761
+ // legitimate container ancestor and may not land on one.
99762
+ scene.nodes.some((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);
99763
+ if (broke) {
99764
+ node[axis] = target - delta;
99765
+ edges.forEach((edge, i) => {
99766
+ edge.pts = before[i];
99767
+ });
99768
+ }
99769
+ }
99770
+ }
99771
+ const maxX = Math.max(...scene.nodes.map((node) => node.x + node.width));
99772
+ const maxY = Math.max(...scene.nodes.map((node) => node.y + node.height));
99773
+ scene.width = Math.max(scene.width, Math.ceil(maxX) + 10);
99774
+ scene.height = Math.max(scene.height, Math.ceil(maxY) + 10);
99775
+ }
99521
99776
  function runGeometryPasses(scene, model, options) {
99522
99777
  const { numbered, sideways } = options;
99778
+ if (options.laneOf) snapLanes(scene, options.laneOf, sideways ? "y" : "x");
99523
99779
  const titleBoxes = titleBoxesOf(scene, model);
99524
99780
  if (sideways) transpose(scene, titleBoxes);
99525
99781
  rerouteDetours(scene, model, numbered, titleBoxes);
@@ -99539,6 +99795,66 @@ function runGeometryPasses(scene, model, options) {
99539
99795
  anchorFlowLabels(scene, settledTitles);
99540
99796
  swapCrossingSiblingSeats(scene);
99541
99797
  }
99798
+ function laneAssignment(model, view) {
99799
+ const laneOf = /* @__PURE__ */ new Map();
99800
+ const laneKinds = new Set(view.laneKinds ?? []);
99801
+ if (!laneKinds.size || model.style.compact) return laneOf;
99802
+ const rootOf = /* @__PURE__ */ new Map();
99803
+ const walk = (element, root) => {
99804
+ rootOf.set(element.id, root);
99805
+ for (const child of element.children ?? []) walk(child, root);
99806
+ };
99807
+ for (const element of model.elements) walk(element, element.id);
99808
+ const adjacency = /* @__PURE__ */ new Map();
99809
+ for (const flow of model.flows) {
99810
+ const from = rootOf.get(flow.from);
99811
+ const to = rootOf.get(flow.to);
99812
+ if (!from || !to || from === to) continue;
99813
+ if (!adjacency.has(from)) adjacency.set(from, /* @__PURE__ */ new Set());
99814
+ adjacency.get(from).add(to);
99815
+ }
99816
+ const reaches = (from, to) => {
99817
+ const seen = /* @__PURE__ */ new Set([from]);
99818
+ const queue = [from];
99819
+ while (queue.length) {
99820
+ for (const next of adjacency.get(queue.pop()) ?? []) {
99821
+ if (next === to) return true;
99822
+ if (!seen.has(next)) {
99823
+ seen.add(next);
99824
+ queue.push(next);
99825
+ }
99826
+ }
99827
+ }
99828
+ return false;
99829
+ };
99830
+ const byKind = /* @__PURE__ */ new Map();
99831
+ for (const element of model.elements) {
99832
+ if (!laneKinds.has(element.kind)) continue;
99833
+ if ((element.children ?? []).length) continue;
99834
+ if (!byKind.has(element.kind)) byKind.set(element.kind, []);
99835
+ byKind.get(element.kind).push(element);
99836
+ }
99837
+ const pinned = /* @__PURE__ */ new Set();
99838
+ for (const flow of model.flows) {
99839
+ if (!flow.fromSide && !flow.toSide) continue;
99840
+ pinned.add(rootOf.get(flow.from) ?? flow.from);
99841
+ pinned.add(rootOf.get(flow.to) ?? flow.to);
99842
+ }
99843
+ let laneId = 0;
99844
+ for (const members of byKind.values()) {
99845
+ if (members.length < 2) continue;
99846
+ if (members.some((member) => pinned.has(member.id))) continue;
99847
+ if (members.some((member) => member.order)) continue;
99848
+ if (members.some((a) => members.some((b) => a !== b && reaches(a.id, b.id)))) continue;
99849
+ laneId++;
99850
+ for (const member of members) laneOf.set(member.id, laneId);
99851
+ }
99852
+ return laneOf;
99853
+ }
99854
+ var ASPECT_TARGETS = {
99855
+ slide: 16 / 9,
99856
+ page: 0.71
99857
+ };
99542
99858
  async function layout(model, view) {
99543
99859
  const elk = await getElk();
99544
99860
  const businessObjectName = new Map(model.businessObjects.map((bo) => [bo.id, bo.name]));
@@ -99551,10 +99867,6 @@ async function layout(model, view) {
99551
99867
  scale: fontScale
99552
99868
  } = fontSizes(model.style.font.size);
99553
99869
  const disposition = model.style.disposition;
99554
- const ASPECT_TARGETS = {
99555
- slide: 16 / 9,
99556
- page: 0.71
99557
- };
99558
99870
  const aspectTarget = ASPECT_TARGETS[disposition];
99559
99871
  const ingressExternalElements = view.partitionByOrder ? computeIngressExternalElements(model) : /* @__PURE__ */ new Set();
99560
99872
  const graphContext = {
@@ -99574,7 +99886,7 @@ async function layout(model, view) {
99574
99886
  };
99575
99887
  const makeGraph = (direction, options) => buildElkGraph(graphContext, direction, options);
99576
99888
  const kindOf = new Map(indexElementsById(model.elements));
99577
- const sceneFromResult = (result2, layoutMs2) => {
99889
+ const sceneFromResult = (result2, layoutMs2, lanes = true) => {
99578
99890
  const origins = {
99579
99891
  root: { x: 0, y: 0 }
99580
99892
  };
@@ -99598,10 +99910,12 @@ async function layout(model, view) {
99598
99910
  };
99599
99911
  runGeometryPasses(scene, model, {
99600
99912
  numbered,
99601
- sideways: disposition === "page" || disposition === "tall"
99913
+ sideways: disposition === "page" || disposition === "tall",
99914
+ laneOf: lanes ? laneOf : void 0
99602
99915
  });
99603
99916
  return scene;
99604
99917
  };
99918
+ const laneOf = laneAssignment(model, view);
99605
99919
  const startTime = Date.now();
99606
99920
  let result;
99607
99921
  let winnerDirection;
@@ -99646,16 +99960,27 @@ async function layout(model, view) {
99646
99960
  result = await elk.layout(makeGraph(winnerDirection));
99647
99961
  }
99648
99962
  const layoutMs = Date.now() - startTime;
99649
- const base = sceneFromResult(result, layoutMs);
99650
- const skipPortPass = !!globalThis.process?.env?.CAIRN_NO_PORT_PASS;
99651
- if (skipPortPass) return base;
99963
+ let base = sceneFromResult(result, layoutMs);
99652
99964
  const layoutProfile = (candidate) => {
99653
99965
  const everyEdge = new Set(candidate.edges.map((edge) => edge.id));
99654
99966
  const profile = inspect(candidate, titleBoxesOf(candidate, model)).local(everyEdge, /* @__PURE__ */ new Map());
99655
99967
  for (const [key, tier] of selectionExtras(candidate, model)) profile.set(key, tier);
99656
99968
  return profile;
99657
99969
  };
99970
+ if (!aspectTarget && nodeCoverage(base) < DENSE_ENOUGH) {
99971
+ const denser = await denserLayout(base, {
99972
+ layout: (spec) => elk.layout(makeGraph(winnerDirection, spec)),
99973
+ toScene: (laidOut) => sceneFromResult(laidOut, Date.now() - startTime),
99974
+ profile: layoutProfile
99975
+ });
99976
+ if (denser) {
99977
+ base = denser.scene;
99978
+ winnerOptions = denser.options;
99979
+ }
99980
+ }
99658
99981
  const baseProfile = layoutProfile(base);
99982
+ const skipPortPass = !!globalThis.process?.env?.CAIRN_NO_PORT_PASS;
99983
+ if (skipPortPass) return base;
99659
99984
  let current = base;
99660
99985
  let best = null;
99661
99986
  for (let round = 0; round < PORT_PASS_ROUNDS; round++) {
@@ -99722,17 +100047,36 @@ var UI = {
99722
100047
  // src/xml-escape.ts
99723
100048
  var esc = (text) => text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
99724
100049
  var escAttr = (text) => esc(text).replace(/"/g, "&quot;");
100050
+ var escComment = (text) => text.replace(/-{2,}/g, (run) => [...run].join(" ")).replace(/-$/, "- ");
100051
+
100052
+ // src/logo-attribution.ts
100053
+ var PREAMBLE = [
100054
+ "This diagram contains third-party artwork under its own licence.",
100055
+ "Keep this notice with the file \u2014 it is the attribution those licences ask for.",
100056
+ "",
100057
+ "Every mark below is modified from the original: simple-icons redrew it as a",
100058
+ "single monochrome path, and cairn paints that path in the diagram's colours."
100059
+ ];
100060
+ function logoAttributionComment(drawn) {
100061
+ const licensed = [...drawn].sort().map((slug) => ({ slug, logo: LOGOS[slug] })).filter((entry) => entry.logo?.license);
100062
+ if (licensed.length === 0) return "";
100063
+ const lines = [...PREAMBLE];
100064
+ for (const { slug, logo } of licensed) {
100065
+ lines.push("", `${logo.title} (${slug}) \u2014 ${logo.license}`);
100066
+ if (logo.copyright) lines.push(` ${logo.copyright}`);
100067
+ if (logo.source) lines.push(` artwork: ${logo.source}`);
100068
+ if (logo.licenseUrl) lines.push(` licence: ${logo.licenseUrl}`);
100069
+ }
100070
+ return `<!--
100071
+ ${lines.map((line) => escComment(` ${line}`).trimEnd()).join("\n")}
100072
+ -->
100073
+ `;
100074
+ }
99725
100075
 
99726
100076
  // src/svg-render.ts
99727
100077
  var HOP_RADIUS = 5;
99728
100078
  var LABEL_HALO = 4;
99729
100079
  var RENDER_CHAR_WIDTH = 0.52;
99730
- var SEC_LEVEL_FR = {
99731
- public: "public",
99732
- internal: "interne",
99733
- restricted: "restreint",
99734
- secret: "secret"
99735
- };
99736
100080
  var dashArray = (lineStyle) => lineStyle === "dashed" ? "5 3" : lineStyle === "dotted" ? "2 2.5" : void 0;
99737
100081
  var round1 = (n) => Math.round(n * 10) / 10;
99738
100082
  function assignSourceHues(model, hues) {
@@ -99745,7 +100089,7 @@ function assignSourceHues(model, hues) {
99745
100089
  }
99746
100090
  function collectElementStyles(elements) {
99747
100091
  return elements.flatMap((element) => [
99748
- { id: element.id, style: element.style, attrValue: element.attr?.value },
100092
+ { id: element.id, style: element.style, logo: element.logo },
99749
100093
  ...collectElementStyles(element.children)
99750
100094
  ]);
99751
100095
  }
@@ -99842,13 +100186,82 @@ function settleOneLabel(s, label) {
99842
100186
  label.x = origin.x;
99843
100187
  label.y = origin.y;
99844
100188
  }
99845
- function createNodeRenderers(paint) {
99846
- const { palette, style, annot, nodeFontSize, containerFontSize, resolveStyle, elementAttr } = paint;
100189
+ var GLYPH_BOX = { width: 18, height: 16, left: 6, top: 7 };
100190
+ var GLYPHS = {
100191
+ // Padlock: authentication is a check something must pass.
100192
+ 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}"/>`,
100193
+ // Two posts with traffic passing between them: a gateway routes, it does not block.
100194
+ 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}/>`,
100195
+ // ID badge: an identity provider issues who-you-are, it does not check it.
100196
+ 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}/>`,
100197
+ // Brick wall: a firewall is a barrier, and no other kind reads as one.
100198
+ 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}/>`,
100199
+ // Monitor on a stand: a device is the machine a person works at. The screen is
100200
+ // left empty on purpose — that is what separates it from the firewall's wall
100201
+ // at legend size, where both are a rectangle and little else survives.
100202
+ 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}/>`
100203
+ };
100204
+ function glyphSvg(kind, stroke, box) {
100205
+ const glyph = GLYPHS[kind];
100206
+ if (!glyph) return "";
100207
+ const { x, y, scale = 1 } = box;
100208
+ const width = round1(1.3 * scale);
100209
+ return glyph({
100210
+ x: (v) => round1(x + v * scale),
100211
+ y: (v) => round1(y + v * scale),
100212
+ r: (v) => round1(v * scale),
100213
+ line: `fill="none" stroke="${stroke}" stroke-width="${width}"`,
100214
+ stroke
100215
+ });
100216
+ }
100217
+ var LOGO_BOX = { size: 18, right: 7, top: 6 };
100218
+ function logoSvg(mark) {
100219
+ const { logo, resolved, node, stroke, drawn, inset = {} } = mark;
100220
+ if (!logo) return "";
100221
+ const x = round1(node.x + node.width - (inset.right ?? LOGO_BOX.right) - LOGO_BOX.size);
100222
+ const y = round1(node.y + (inset.top ?? LOGO_BOX.top));
100223
+ if (logo.source === "file") {
100224
+ const href = resolved?.get(node.id);
100225
+ if (!href) return "";
100226
+ return `<image x="${x}" y="${y}" width="${LOGO_BOX.size}" height="${LOGO_BOX.size}" href="${escAttr(href)}" preserveAspectRatio="xMidYMid meet"/>
100227
+ `;
100228
+ }
100229
+ const builtin = Object.hasOwn(LOGOS, logo.value) ? LOGOS[logo.value] : void 0;
100230
+ if (!builtin) return "";
100231
+ drawn.add(logo.value);
100232
+ const scale = Math.round(LOGO_BOX.size / 24 * 1e4) / 1e4;
100233
+ return `<g transform="translate(${x} ${y}) scale(${scale})" fill="${stroke}"><title>${esc(builtin.title)}</title><path d="${builtin.d}"/></g>
100234
+ `;
100235
+ }
100236
+ function createNodeLabelHelpers(nodeFontSize) {
99847
100237
  const centeredNodeLabel = (lines, centerX2, topBaseline, fill) => lines.map(
99848
100238
  (line, index) => `<text x="${centerX2}" y="${topBaseline + index * (nodeFontSize + 2)}" font-size="${nodeFontSize}" text-anchor="middle" fill="${fill}">${esc(line)}</text>
99849
100239
  `
99850
100240
  ).join("");
99851
100241
  const centerLinesY = (top, height, lineCount) => top + height / 2 - (lineCount - 1) * (nodeFontSize + 2) / 2 + 4;
100242
+ const glyphLabelCenterX = (node) => node.x + GLYPH_GUTTER + (node.width - GLYPH_GUTTER) / 2;
100243
+ const logoLabelCenterX = (node, hasLogo) => node.x + (node.width - (hasLogo ? LOGO_GUTTER : 0)) / 2;
100244
+ return { centeredNodeLabel, centerLinesY, glyphLabelCenterX, logoLabelCenterX };
100245
+ }
100246
+ function createNodeRenderers(paint) {
100247
+ const {
100248
+ palette,
100249
+ nodeFontSize,
100250
+ containerFontSize,
100251
+ resolveStyle,
100252
+ elementLogo,
100253
+ resolvedLogos,
100254
+ drawnLogos
100255
+ } = paint;
100256
+ const logoFor = (node, stroke, inset) => logoSvg({
100257
+ logo: elementLogo.get(node.id),
100258
+ resolved: resolvedLogos,
100259
+ node,
100260
+ stroke,
100261
+ drawn: drawnLogos,
100262
+ inset
100263
+ });
100264
+ const { centeredNodeLabel, centerLinesY, glyphLabelCenterX, logoLabelCenterX } = createNodeLabelHelpers(nodeFontSize);
99852
100265
  const renderContainerNode = (node) => {
99853
100266
  const nodeStyle = resolveStyle(node.kind, node.id);
99854
100267
  const fill = escAttr(nodeStyle.fill ?? palette.containerFill), stroke = escAttr(nodeStyle.stroke?.color ?? palette.containerStroke), text = escAttr(nodeStyle.text ?? palette.containerLabel);
@@ -99859,12 +100272,7 @@ function createNodeRenderers(paint) {
99859
100272
  svg += `<text x="${node.x + 10}" y="${node.y + 18 + index * 14}" font-size="${containerFontSize}" font-weight="bold" fill="${text}">${esc(line)}</text>
99860
100273
  `;
99861
100274
  });
99862
- const level = node.kind === "trust-zone" ? elementAttr.get(node.id) : void 0;
99863
- if (level) {
99864
- const word = (style.lang === "fr" ? SEC_LEVEL_FR[level] : level) ?? level;
99865
- 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>
99866
- `;
99867
- }
100275
+ svg += logoFor(node, stroke);
99868
100276
  return svg;
99869
100277
  };
99870
100278
  const renderActor = (node, nodeStyle, lines) => {
@@ -99887,9 +100295,12 @@ function createNodeRenderers(paint) {
99887
100295
  <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"/>
99888
100296
  `;
99889
100297
  const centerY2 = node.y + ry + (node.height - ry) / 2 - (lines.length - 1) * (nodeFontSize + 2) / 2 + 4;
99890
- return body + centeredNodeLabel(
100298
+ const logo = logoFor(node, stroke, {
100299
+ top: ry + (node.height - 2 * ry - LOGO_BOX.size) / 2
100300
+ });
100301
+ return body + logo + centeredNodeLabel(
99891
100302
  lines,
99892
- node.x + node.width / 2,
100303
+ logoLabelCenterX(node, logo !== ""),
99893
100304
  centerY2,
99894
100305
  escAttr(nodeStyle.text ?? palette.nodeText)
99895
100306
  );
@@ -99900,50 +100311,38 @@ function createNodeRenderers(paint) {
99900
100311
  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"/>
99901
100312
  <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"/>
99902
100313
  `;
99903
- return body + centeredNodeLabel(
100314
+ const logo = logoFor(node, stroke, { right: LOGO_BOX.right + rx });
100315
+ return body + logo + centeredNodeLabel(
99904
100316
  lines,
99905
- node.x + rx + (node.width - rx) / 2,
100317
+ node.x + rx + (node.width - rx - (logo === "" ? 0 : LOGO_GUTTER)) / 2,
99906
100318
  centerLinesY(node.y, node.height, lines.length),
99907
100319
  text
99908
100320
  );
99909
100321
  };
99910
- const renderGateway = (node, nodeStyle, lines) => {
99911
- const centerX2 = node.x + node.width / 2;
99912
- const fill = escAttr(nodeStyle.fill ?? palette.nodeFill), stroke = escAttr(nodeStyle.stroke?.color ?? palette.nodeStroke), text = escAttr(nodeStyle.text ?? palette.nodeText);
99913
- 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"/>
99914
- <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"/>
99915
- `;
99916
- return body + centeredNodeLabel(lines, centerX2 + 10, centerLinesY(node.y, node.height, lines.length), text);
99917
- };
99918
- const renderAuth = (node, nodeStyle, lines) => {
99919
- const centerX2 = node.x + node.width / 2;
99920
- const fill = escAttr(nodeStyle.fill ?? palette.nodeFill), stroke = escAttr(nodeStyle.stroke?.color ?? palette.nodeStroke), text = escAttr(nodeStyle.text ?? palette.nodeText);
99921
- 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"/>
99922
- <rect x="${node.x + 6}" y="${node.y + 6}" width="18" height="14" rx="3" fill="none" stroke="${stroke}" stroke-width="1.3"/>
99923
- <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"/>
99924
- <circle cx="${node.x + 15}" cy="${node.y + 16}" r="2.5" fill="${stroke}"/>
99925
- `;
99926
- return body + centeredNodeLabel(lines, centerX2 + 10, centerLinesY(node.y, node.height, lines.length), text);
99927
- };
99928
- const renderPlainBox = (node, nodeStyle, lines) => {
100322
+ const renderGlyphBox = (node, nodeStyle, lines) => {
99929
100323
  const fill = escAttr(nodeStyle.fill ?? palette.nodeFill), stroke = escAttr(nodeStyle.stroke?.color ?? palette.nodeStroke), text = escAttr(nodeStyle.text ?? palette.nodeText);
99930
100324
  const dash = dashArray(nodeStyle.stroke?.style);
99931
100325
  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}"` : ""}/>
99932
- `;
100326
+ ` + glyphSvg(node.kind, stroke, {
100327
+ x: node.x + GLYPH_BOX.left,
100328
+ y: node.y + GLYPH_BOX.top
100329
+ }) + "\n";
99933
100330
  return body + centeredNodeLabel(
99934
100331
  lines,
99935
- node.x + node.width / 2,
100332
+ glyphLabelCenterX(node),
99936
100333
  centerLinesY(node.y, node.height, lines.length),
99937
100334
  text
99938
100335
  );
99939
100336
  };
99940
- const renderIdp = (node, nodeStyle, lines) => {
100337
+ const renderPlainBox = (node, nodeStyle, lines) => {
99941
100338
  const fill = escAttr(nodeStyle.fill ?? palette.nodeFill), stroke = escAttr(nodeStyle.stroke?.color ?? palette.nodeStroke), text = escAttr(nodeStyle.text ?? palette.nodeText);
99942
- 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}"/>
100339
+ const dash = dashArray(nodeStyle.stroke?.style);
100340
+ const logo = logoFor(node, stroke);
100341
+ 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}"` : ""}/>
99943
100342
  `;
99944
- return body + centeredNodeLabel(
100343
+ return body + logo + centeredNodeLabel(
99945
100344
  lines,
99946
- node.x + node.width / 2,
100345
+ logoLabelCenterX(node, logo !== ""),
99947
100346
  centerLinesY(node.y, node.height, lines.length),
99948
100347
  text
99949
100348
  );
@@ -99958,14 +100357,8 @@ function createNodeRenderers(paint) {
99958
100357
  return renderDatastore(node, nodeStyle, lines);
99959
100358
  case "queue":
99960
100359
  return renderQueue(node, nodeStyle, lines);
99961
- case "gateway":
99962
- return renderGateway(node, nodeStyle, lines);
99963
- case "auth":
99964
- return renderAuth(node, nodeStyle, lines);
99965
- case "idp":
99966
- return renderIdp(node, nodeStyle, lines);
99967
100360
  default:
99968
- return renderPlainBox(node, nodeStyle, lines);
100361
+ return GLYPHS[node.kind] ? renderGlyphBox(node, nodeStyle, lines) : renderPlainBox(node, nodeStyle, lines);
99969
100362
  }
99970
100363
  };
99971
100364
  return { renderContainerNode, renderLeafNode };
@@ -100211,8 +100604,15 @@ function createBandRenderers(paint) {
100211
100604
  `;
100212
100605
  } else {
100213
100606
  const dash = dashArray(nodeStyle.stroke?.style);
100214
- 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}"` : ""}/>
100607
+ const stroke = nodeStyle.stroke?.color ?? palette.nodeStroke;
100608
+ bandsSvg += `<rect x="${lx}" y="${bandY + 2}" width="${scaled(26)}" height="${scaled(14)}" rx="3" fill="${nodeStyle.fill ?? palette.nodeFill}" stroke="${escAttr(stroke)}"${dash ? ` stroke-dasharray="${dash}"` : ""}/>
100215
100609
  `;
100610
+ const glyphScale = (scaled(14) - scaled(4)) / GLYPH_BOX.height;
100611
+ bandsSvg += glyphSvg(kind, escAttr(stroke), {
100612
+ x: lx + (scaled(26) - GLYPH_BOX.width * glyphScale) / 2,
100613
+ y: bandY + 2 + (scaled(14) - GLYPH_BOX.height * glyphScale) / 2,
100614
+ scale: glyphScale
100615
+ });
100216
100616
  }
100217
100617
  const name = legendNames[kind];
100218
100618
  bandsSvg += `<text x="${lx + scaled(32)}" y="${bandY + scaled(13)}" font-size="${scaled(10)}" fill="${palette.bandText}">${esc(name)}</text>
@@ -100348,7 +100748,28 @@ function createEdgePainter(paint) {
100348
100748
  };
100349
100749
  return { renderEdgePath, renderEdgeLabels };
100350
100750
  }
100351
- function render(model, view, scene) {
100751
+ function svgDocument(args) {
100752
+ const {
100753
+ width,
100754
+ height,
100755
+ fontFamily,
100756
+ background,
100757
+ arrowMarkers,
100758
+ markerSize,
100759
+ body,
100760
+ bandsSvg,
100761
+ attribution
100762
+ } = args;
100763
+ const markers = [...arrowMarkers].map(
100764
+ ([color, markerName]) => `<marker id="${markerName}" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="${markerSize}" markerHeight="${markerSize}" orient="auto-start-reverse">
100765
+ <path d="M0,0 L10,5 L0,10 z" fill="${escAttr(color)}"/></marker>`
100766
+ ).join("\n");
100767
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" font-family="${escAttr(fontFamily)},Arial,sans-serif">
100768
+ ${attribution}<defs>${markers}</defs>
100769
+ <rect width="${width}" height="${height}" fill="${escAttr(background)}"/>
100770
+ ` + body + bandsSvg + "</svg>\n";
100771
+ }
100772
+ function render(model, view, scene, options) {
100352
100773
  const style = model.style;
100353
100774
  const fonts = fontSizes(style.font.size);
100354
100775
  const { edge: edgeFontSize, node: nodeFontSize, cont: containerFontSize } = fonts;
@@ -100364,10 +100785,10 @@ function render(model, view, scene) {
100364
100785
  chipTextDy: round1(11 * fonts.scale)
100365
100786
  };
100366
100787
  const scaled = (n) => round1(n * fonts.scale);
100367
- const { palette, kinds: kindDefaults, levels: levelDefaults } = themeFor(style.theme, view);
100368
- const isDarkTheme = ["dark", "nord", "classic-dark"].includes(style.theme);
100788
+ const { palette, kinds: kindDefaults } = options?.theme ? themeFromSpec(options.theme, view) : themeFor(style.theme, view);
100789
+ const onDarkGround = options?.theme ? options.theme.dark === true : isDarkTheme(style.theme);
100369
100790
  const defaultEdgeColor = style.flowStrokeColorSet ? style.flowStroke.color : style.accent ?? palette.edge;
100370
- const sourceHue = style.flowColor === "by-source" ? assignSourceHues(model, flowPalette[isDarkTheme ? "dark" : "light"]) : /* @__PURE__ */ new Map();
100791
+ const sourceHue = style.flowColor === "by-source" ? assignSourceHues(model, flowPalette[onDarkGround ? "dark" : "light"]) : /* @__PURE__ */ new Map();
100371
100792
  const flowColorOf = (flow) => flow?.style?.stroke?.color ?? (style.flowColor === "by-source" ? sourceHue.get(flow?.from ?? "") ?? defaultEdgeColor : defaultEdgeColor);
100372
100793
  const arrowMarkers = /* @__PURE__ */ new Map();
100373
100794
  const markerName = (color) => {
@@ -100385,15 +100806,13 @@ function render(model, view, scene) {
100385
100806
  const legendNames = style.lang === "fr" ? view.legendNamesFr : view.legendNames;
100386
100807
  const legendFlowLabel = style.lang === "fr" ? view.legendFlowLabelFr : view.legendFlowLabel;
100387
100808
  const elementStyle = /* @__PURE__ */ new Map();
100388
- const elementAttr = /* @__PURE__ */ new Map();
100809
+ const elementLogo = /* @__PURE__ */ new Map();
100389
100810
  for (const entry of collectElementStyles(model.elements)) {
100390
100811
  elementStyle.set(entry.id, entry.style);
100391
- elementAttr.set(entry.id, entry.attrValue);
100812
+ if (entry.logo) elementLogo.set(entry.id, entry.logo);
100392
100813
  }
100393
100814
  const resolveStyle = (kind, id) => {
100394
- let base = kindDefaults[kind] ?? {};
100395
- const level = elementAttr.get(id);
100396
- if (kind === "trust-zone" && level && levelDefaults?.[level]) base = levelDefaults[level];
100815
+ const base = kindDefaults[kind] ?? {};
100397
100816
  const perKind = style.kind[kind] ?? {};
100398
100817
  const inline = elementStyle.get(id) ?? {};
100399
100818
  return {
@@ -100428,14 +100847,15 @@ function render(model, view, scene) {
100428
100847
  });
100429
100848
  const overlapsAfter = countLabelOverlaps();
100430
100849
  compactVertical(scene);
100850
+ const drawnLogos = /* @__PURE__ */ new Set();
100431
100851
  const { renderContainerNode, renderLeafNode } = createNodeRenderers({
100432
100852
  palette,
100433
- style,
100434
- annot,
100435
100853
  nodeFontSize,
100436
100854
  containerFontSize,
100437
100855
  resolveStyle,
100438
- elementAttr
100856
+ elementLogo,
100857
+ resolvedLogos: options?.logos,
100858
+ drawnLogos
100439
100859
  });
100440
100860
  const { renderEdgePath, renderEdgeLabels } = createEdgePainter({
100441
100861
  scene,
@@ -100478,21 +100898,255 @@ function render(model, view, scene) {
100478
100898
  if (style.legend === "auto") bands.renderLegendBand();
100479
100899
  const bandsSvg = bands.bandsSvg();
100480
100900
  const bandY = bands.bandY();
100481
- const viewWidth = scene.width, viewHeight = scene.height;
100482
- const totalHeight = bandY > viewHeight ? bandY + 14 : viewHeight;
100483
- const markerSize = style.arrows === "large" ? round1(11 * fonts.scale) : 7;
100901
+ const viewHeight = scene.height;
100484
100902
  if (arrowMarkers.size === 0) markerName(defaultEdgeColor);
100485
- const markers = [...arrowMarkers].map(
100486
- ([color, markerName2]) => `<marker id="${markerName2}" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="${markerSize}" markerHeight="${markerSize}" orient="auto-start-reverse">
100487
- <path d="M0,0 L10,5 L0,10 z" fill="${escAttr(color)}"/></marker>`
100488
- ).join("\n");
100489
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${viewWidth} ${totalHeight}" font-family="${escAttr(style.font.family)},Arial,sans-serif">
100490
- <defs>${markers}</defs>
100491
- <rect width="${viewWidth}" height="${totalHeight}" fill="${escAttr(style.background ?? palette.background)}"/>
100492
- ` + body + bandsSvg + "</svg>\n";
100903
+ const svg = svgDocument({
100904
+ width: scene.width,
100905
+ height: bandY > viewHeight ? bandY + 14 : viewHeight,
100906
+ fontFamily: style.font.family,
100907
+ background: style.background ?? palette.background,
100908
+ arrowMarkers,
100909
+ markerSize: style.arrows === "large" ? round1(11 * fonts.scale) : 7,
100910
+ body,
100911
+ bandsSvg,
100912
+ // Read after the body is built, so it names the marks that were painted
100913
+ // rather than the ones the model asked for.
100914
+ attribution: logoAttributionComment(drawnLogos)
100915
+ });
100493
100916
  return { svg, overlapsBefore, overlapsAfter };
100494
100917
  }
100495
100918
 
100919
+ // src/css-colours.ts
100920
+ var HEX = "#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})";
100921
+ var NUMBER = String.raw`[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?`;
100922
+ var PERCENT = `${NUMBER}%`;
100923
+ var AMOUNT = `${NUMBER}%?`;
100924
+ var ANGLE = `${NUMBER}(?:deg|grad|rad|turn)?`;
100925
+ var commaForm = (first, second, third) => `${first}\\s*,\\s*${second}\\s*,\\s*${third}(?:\\s*,\\s*${AMOUNT})?`;
100926
+ var spaceForm = (first, second, third) => `${first}\\s+${second}\\s+${third}(?:\\s*/\\s*${AMOUNT})?`;
100927
+ var RGB = `rgba?\\(\\s*(?:${commaForm(NUMBER, NUMBER, NUMBER)}|${commaForm(PERCENT, PERCENT, PERCENT)}|${spaceForm(AMOUNT, AMOUNT, AMOUNT)})\\s*\\)`;
100928
+ var HSL = `hsla?\\(\\s*(?:${commaForm(ANGLE, PERCENT, PERCENT)}|${spaceForm(ANGLE, AMOUNT, AMOUNT)})\\s*\\)`;
100929
+ var NUMERIC_COLOUR = new RegExp(`^(?:${HEX}|${RGB}|${HSL})$`, "i");
100930
+ var COLOUR_KEYWORDS = /* @__PURE__ */ new Set([
100931
+ "currentcolor",
100932
+ "transparent",
100933
+ "aliceblue",
100934
+ "antiquewhite",
100935
+ "aqua",
100936
+ "aquamarine",
100937
+ "azure",
100938
+ "beige",
100939
+ "bisque",
100940
+ "black",
100941
+ "blanchedalmond",
100942
+ "blue",
100943
+ "blueviolet",
100944
+ "brown",
100945
+ "burlywood",
100946
+ "cadetblue",
100947
+ "chartreuse",
100948
+ "chocolate",
100949
+ "coral",
100950
+ "cornflowerblue",
100951
+ "cornsilk",
100952
+ "crimson",
100953
+ "cyan",
100954
+ "darkblue",
100955
+ "darkcyan",
100956
+ "darkgoldenrod",
100957
+ "darkgray",
100958
+ "darkgreen",
100959
+ "darkgrey",
100960
+ "darkkhaki",
100961
+ "darkmagenta",
100962
+ "darkolivegreen",
100963
+ "darkorange",
100964
+ "darkorchid",
100965
+ "darkred",
100966
+ "darksalmon",
100967
+ "darkseagreen",
100968
+ "darkslateblue",
100969
+ "darkslategray",
100970
+ "darkslategrey",
100971
+ "darkturquoise",
100972
+ "darkviolet",
100973
+ "deeppink",
100974
+ "deepskyblue",
100975
+ "dimgray",
100976
+ "dimgrey",
100977
+ "dodgerblue",
100978
+ "firebrick",
100979
+ "floralwhite",
100980
+ "forestgreen",
100981
+ "fuchsia",
100982
+ "gainsboro",
100983
+ "ghostwhite",
100984
+ "gold",
100985
+ "goldenrod",
100986
+ "gray",
100987
+ "green",
100988
+ "greenyellow",
100989
+ "grey",
100990
+ "honeydew",
100991
+ "hotpink",
100992
+ "indianred",
100993
+ "indigo",
100994
+ "ivory",
100995
+ "khaki",
100996
+ "lavender",
100997
+ "lavenderblush",
100998
+ "lawngreen",
100999
+ "lemonchiffon",
101000
+ "lightblue",
101001
+ "lightcoral",
101002
+ "lightcyan",
101003
+ "lightgoldenrodyellow",
101004
+ "lightgray",
101005
+ "lightgreen",
101006
+ "lightgrey",
101007
+ "lightpink",
101008
+ "lightsalmon",
101009
+ "lightseagreen",
101010
+ "lightskyblue",
101011
+ "lightslategray",
101012
+ "lightslategrey",
101013
+ "lightsteelblue",
101014
+ "lightyellow",
101015
+ "lime",
101016
+ "limegreen",
101017
+ "linen",
101018
+ "magenta",
101019
+ "maroon",
101020
+ "mediumaquamarine",
101021
+ "mediumblue",
101022
+ "mediumorchid",
101023
+ "mediumpurple",
101024
+ "mediumseagreen",
101025
+ "mediumslateblue",
101026
+ "mediumspringgreen",
101027
+ "mediumturquoise",
101028
+ "mediumvioletred",
101029
+ "midnightblue",
101030
+ "mintcream",
101031
+ "mistyrose",
101032
+ "moccasin",
101033
+ "navajowhite",
101034
+ "navy",
101035
+ "oldlace",
101036
+ "olive",
101037
+ "olivedrab",
101038
+ "orange",
101039
+ "orangered",
101040
+ "orchid",
101041
+ "palegoldenrod",
101042
+ "palegreen",
101043
+ "paleturquoise",
101044
+ "palevioletred",
101045
+ "papayawhip",
101046
+ "peachpuff",
101047
+ "peru",
101048
+ "pink",
101049
+ "plum",
101050
+ "powderblue",
101051
+ "purple",
101052
+ "rebeccapurple",
101053
+ "red",
101054
+ "rosybrown",
101055
+ "royalblue",
101056
+ "saddlebrown",
101057
+ "salmon",
101058
+ "sandybrown",
101059
+ "seagreen",
101060
+ "seashell",
101061
+ "sienna",
101062
+ "silver",
101063
+ "skyblue",
101064
+ "slateblue",
101065
+ "slategray",
101066
+ "slategrey",
101067
+ "snow",
101068
+ "springgreen",
101069
+ "steelblue",
101070
+ "tan",
101071
+ "teal",
101072
+ "thistle",
101073
+ "tomato",
101074
+ "turquoise",
101075
+ "violet",
101076
+ "wheat",
101077
+ "white",
101078
+ "whitesmoke",
101079
+ "yellow",
101080
+ "yellowgreen"
101081
+ ]);
101082
+ var isColour = (value) => NUMERIC_COLOUR.test(value) || COLOUR_KEYWORDS.has(value.toLowerCase());
101083
+
101084
+ // src/theme-spec.ts
101085
+ var DEFAULT_BASE = "light";
101086
+ var ThemeSpecError = class extends Error {
101087
+ };
101088
+ var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
101089
+ var merge = (base, patch) => {
101090
+ const merged = { ...base };
101091
+ for (const [key, value] of Object.entries(patch)) {
101092
+ if (key === "extends") continue;
101093
+ const current = merged[key];
101094
+ merged[key] = isObject(value) && isObject(current) ? { ...current, ...value } : value;
101095
+ }
101096
+ return merged;
101097
+ };
101098
+ var SECTIONS = ["pal", "accentColors"];
101099
+ var arityOf = (section, key) => {
101100
+ if (section === "pal") return key === "chip" ? 3 : key === "badge" ? 2 : 1;
101101
+ return 1;
101102
+ };
101103
+ var describe = (value) => Array.isArray(value) ? `an array of ${value.length}` : typeof value;
101104
+ function checkEntry(path, value, arity) {
101105
+ if (arity === 1) {
101106
+ if (typeof value !== "string") {
101107
+ throw new ThemeSpecError(`\`${path}\` must be a colour, got ${describe(value)}`);
101108
+ }
101109
+ } else if (!Array.isArray(value) || value.length !== arity) {
101110
+ throw new ThemeSpecError(`\`${path}\` must be ${arity} colours, got ${describe(value)}`);
101111
+ }
101112
+ for (const colour of Array.isArray(value) ? value : [value]) {
101113
+ if (typeof colour !== "string" || !isColour(colour)) {
101114
+ throw new ThemeSpecError(`\`${path}\`: \`${String(colour)}\` is not a colour`);
101115
+ }
101116
+ }
101117
+ }
101118
+ function resolveThemeSpec(input) {
101119
+ if (!isObject(input)) throw new ThemeSpecError("a theme must be an object");
101120
+ const base = input.extends ?? DEFAULT_BASE;
101121
+ if (typeof base !== "string") throw new ThemeSpecError("`extends` must be a theme name");
101122
+ const baseSpec = Object.hasOwn(THEME_SPECS, base) ? THEME_SPECS[base] : void 0;
101123
+ if (!baseSpec) {
101124
+ throw new ThemeSpecError(
101125
+ `extends unknown theme \`${base}\` \u2014 pick one of: ${Object.keys(THEME_SPECS).join(", ")}`
101126
+ );
101127
+ }
101128
+ if ("dark" in input && typeof input.dark !== "boolean") {
101129
+ throw new ThemeSpecError("`dark` must be true or false");
101130
+ }
101131
+ for (const [section, value] of Object.entries(input)) {
101132
+ if (section === "extends" || section === "dark") continue;
101133
+ if (!SECTIONS.includes(section)) {
101134
+ throw new ThemeSpecError(
101135
+ `unknown section \`${section}\` \u2014 a theme overrides ${SECTIONS.join(", ")}`
101136
+ );
101137
+ }
101138
+ if (!isObject(value)) {
101139
+ throw new ThemeSpecError(
101140
+ `\`${section}\` must be an object of colours, got ${describe(value)}`
101141
+ );
101142
+ }
101143
+ for (const [key, colour] of Object.entries(value)) {
101144
+ checkEntry(`${section}.${key}`, colour, arityOf(section, key));
101145
+ }
101146
+ }
101147
+ return merge(baseSpec, input);
101148
+ }
101149
+
100496
101150
  // src/flow-matrix.ts
100497
101151
  var COLUMN_PRESENTATION = {
100498
101152
  num: { align: "middle", maxWidth: 220 },
@@ -100587,8 +101241,9 @@ function matrixMd(matrix) {
100587
101241
  for (const row of matrix.rows) outLines.push("| " + row.cells.map(mdCell).join(" | ") + " |");
100588
101242
  return outLines.join("\n") + "\n";
100589
101243
  }
100590
- function matrixSvg(matrix) {
100591
- const pal = palettes[matrix.style.theme] ?? lightPalette;
101244
+ function matrixSvg(matrix, options) {
101245
+ const requested = options?.theme;
101246
+ const pal = typeof requested === "object" ? paletteFromSpec(resolveThemeSpec(requested)) : paletteFor(requested ?? matrix.style.theme);
100592
101247
  const title = matrix.heading;
100593
101248
  const headers = matrix.columns.map((column) => column.label);
100594
101249
  const cells = matrix.rows.map((row) => row.cells);
@@ -100654,7 +101309,8 @@ function matrixSvg(matrix) {
100654
101309
  // src/compile.ts
100655
101310
  async function compile(source, options) {
100656
101311
  const { model, diags } = parse(source);
100657
- if (options?.theme) model.style.theme = options.theme;
101312
+ const theme = typeof options?.theme === "object" ? resolveThemeSpec(options.theme) : void 0;
101313
+ if (typeof options?.theme === "string") model.style.theme = options.theme;
100658
101314
  diags.push(...validate(model));
100659
101315
  const errors = diags.filter((diagnostic) => diagnostic.severity === "error");
100660
101316
  if (errors.length || !model.type || !views[model.type]) {
@@ -100664,7 +101320,7 @@ async function compile(source, options) {
100664
101320
  const matrix = options?.matrix ? buildFlowMatrix(model, view) : null;
100665
101321
  const scene = await layout(model, view);
100666
101322
  diags.push(...attachSideDiagnostics(scene, model));
100667
- const { svg, overlapsAfter } = render(model, view, scene);
101323
+ const { svg, overlapsAfter } = render(model, view, scene, { logos: options?.logos, theme });
100668
101324
  return {
100669
101325
  svg,
100670
101326
  diagnostics: diags,
@@ -100679,16 +101335,18 @@ async function compile(source, options) {
100679
101335
  }
100680
101336
 
100681
101337
  // src/api.ts
100682
- var version = true ? "1.0.0-RC13" : pkg.version;
101338
+ var version = true ? "1.0.0-RC14" : pkg.version;
100683
101339
 
100684
101340
  // src/playground.ts
100685
101341
  var ElkClass = import_elk_bundled.default;
100686
101342
  setElkFactory(() => new ElkClass());
100687
101343
  export {
101344
+ ThemeSpecError,
100688
101345
  compile,
100689
101346
  matrixCsv,
100690
101347
  matrixMd,
100691
101348
  matrixSvg,
101349
+ resolveThemeSpec,
100692
101350
  themeNames,
100693
101351
  version
100694
101352
  };