@eventcatalog/core 4.12.3 → 5.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. package/LICENSE +226 -95
  2. package/NOTICE +30 -0
  3. package/README.md +3 -2
  4. package/dist/analytics/analytics.cjs +2 -1
  5. package/dist/analytics/analytics.js +2 -2
  6. package/dist/analytics/log-build.cjs +44 -16
  7. package/dist/analytics/log-build.d.cts +6 -5
  8. package/dist/analytics/log-build.d.ts +6 -5
  9. package/dist/analytics/log-build.js +4 -3
  10. package/dist/{chunk-U4YJSTGV.js → chunk-AYNZXUB5.js} +2 -10
  11. package/dist/{chunk-5BPN6QEC.js → chunk-K2ACDO7Y.js} +1 -1
  12. package/dist/{chunk-ZYTCBNB3.js → chunk-KYJZJLBD.js} +2 -1
  13. package/dist/{chunk-TV3YAWKQ.js → chunk-Q6SD5SNQ.js} +1 -1
  14. package/dist/{chunk-HPC6KUDQ.js → chunk-S2QGT7ZF.js} +1 -1
  15. package/dist/chunk-TQLRG35T.js +203 -0
  16. package/dist/{chunk-M577VUOX.js → chunk-UDS3SYLZ.js} +13 -16
  17. package/dist/constants.cjs +1 -1
  18. package/dist/constants.js +1 -1
  19. package/dist/eventcatalog.cjs +408 -291
  20. package/dist/eventcatalog.config.d.cts +2 -3
  21. package/dist/eventcatalog.config.d.ts +2 -3
  22. package/dist/eventcatalog.js +35 -98
  23. package/dist/federation/content-cache.d.cts +0 -5
  24. package/dist/federation/content-cache.d.ts +0 -5
  25. package/dist/federation/diagnostics.d.cts +0 -5
  26. package/dist/federation/diagnostics.d.ts +0 -5
  27. package/dist/federation/federate.cjs +0 -9
  28. package/dist/federation/federate.d.cts +0 -5
  29. package/dist/federation/federate.d.ts +0 -5
  30. package/dist/federation/federate.js +4 -5
  31. package/dist/federation/filesystem-source-provider.d.cts +0 -5
  32. package/dist/federation/filesystem-source-provider.d.ts +0 -5
  33. package/dist/federation/github-source-provider.d.cts +0 -5
  34. package/dist/federation/github-source-provider.d.ts +0 -5
  35. package/dist/federation/output-transaction.d.cts +0 -4
  36. package/dist/federation/output-transaction.d.ts +0 -4
  37. package/dist/federation/public-assets.d.cts +0 -5
  38. package/dist/federation/public-assets.d.ts +0 -5
  39. package/dist/federation/source-provider.d.cts +0 -5
  40. package/dist/federation/source-provider.d.ts +0 -5
  41. package/dist/federation/types.d.cts +0 -5
  42. package/dist/federation/types.d.ts +0 -5
  43. package/dist/generate.cjs +1 -1
  44. package/dist/generate.js +3 -3
  45. package/dist/license-C1y-56bb.d.cts +23 -0
  46. package/dist/license-C1y-56bb.d.ts +23 -0
  47. package/dist/runtime-dependencies/@eventcatalog__visualiser__layout.mjs +3 -0
  48. package/dist/runtime-dependencies/@modelcontextprotocol__ext-apps.mjs +3 -0
  49. package/dist/runtime-dependencies/@modelcontextprotocol__ext-apps__react.mjs +3 -0
  50. package/dist/runtime-dependencies/@modelcontextprotocol__ext-apps__server.mjs +3 -0
  51. package/dist/runtime-dependencies/@modelcontextprotocol__sdk__types.js.mjs +3 -0
  52. package/dist/runtime-dependencies/manifest.json +12 -1
  53. package/dist/runtime-dependencies/react-syntax-highlighter__dist__esm__languages__prism__graphql.mjs +3 -0
  54. package/dist/runtime-dependencies/react-syntax-highlighter__dist__esm__languages__prism__json.mjs +3 -0
  55. package/dist/runtime-dependencies/react-syntax-highlighter__dist__esm__languages__prism__markup.mjs +3 -0
  56. package/dist/runtime-dependencies/react-syntax-highlighter__dist__esm__languages__prism__protobuf.mjs +3 -0
  57. package/dist/runtime-dependencies/react-syntax-highlighter__dist__esm__languages__prism__yaml.mjs +3 -0
  58. package/dist/runtime-dependencies/react-syntax-highlighter__dist__esm__styles__prism__one-dark.mjs +3 -0
  59. package/dist/runtime-dependencies/react-syntax-highlighter__dist__esm__styles__prism__one-light.mjs +3 -0
  60. package/dist/utils/cli-logger.cjs +1 -1
  61. package/dist/utils/cli-logger.js +2 -2
  62. package/dist/utils/license-status.cjs +240 -0
  63. package/dist/utils/license-status.d.cts +28 -0
  64. package/dist/utils/license-status.d.ts +28 -0
  65. package/dist/utils/license-status.js +12 -0
  66. package/eventcatalog/astro.config.mjs +1 -1
  67. package/eventcatalog/integrations/eventcatalog-runtime.mjs +1 -1
  68. package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +29 -19
  69. package/eventcatalog/src/components/ChatPanel/OfflineReply.tsx +74 -0
  70. package/eventcatalog/src/components/FieldsExplorer/FieldFilters.tsx +24 -28
  71. package/eventcatalog/src/components/FieldsExplorer/FieldNodeGraph.tsx +16 -11
  72. package/eventcatalog/src/components/FieldsExplorer/FieldsExplorer.tsx +4 -218
  73. package/eventcatalog/src/components/FieldsExplorer/FieldsTable.tsx +27 -36
  74. package/eventcatalog/src/components/Header.astro +43 -58
  75. package/eventcatalog/src/components/Lists/PillListFlat.styles.css +17 -17
  76. package/eventcatalog/src/components/MDX/EntityMap/EntityMap.astro +1 -1
  77. package/eventcatalog/src/components/MDX/Flow/Flow.astro +4 -0
  78. package/eventcatalog/src/components/MDX/NodeGraph/AstroNodeGraph.tsx +14 -0
  79. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +49 -18
  80. package/eventcatalog/src/components/MDX/RemoteFile.astro +2 -24
  81. package/eventcatalog/src/components/SchemaExplorer/ApiAccessSection.tsx +59 -93
  82. package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +1 -32
  83. package/eventcatalog/src/components/SchemaExplorer/DiffViewer.tsx +20 -67
  84. package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +2 -11
  85. package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +11 -60
  86. package/eventcatalog/src/components/SchemaExplorer/SchemaExplorer.tsx +1 -3
  87. package/eventcatalog/src/components/SchemaExplorer/SchemaPageViewer.tsx +0 -3
  88. package/eventcatalog/src/components/SchemaExplorer/VersionHistoryModal.tsx +2 -9
  89. package/eventcatalog/src/components/SchemaExplorer/parse-schema.ts +46 -0
  90. package/eventcatalog/src/components/SchemaExplorer/utils.ts +11 -0
  91. package/eventcatalog/src/components/Settings/AssistantSettingsForm.tsx +2 -9
  92. package/eventcatalog/src/components/Settings/LicenseSettings.tsx +288 -0
  93. package/eventcatalog/src/components/Settings/LlmAccessSettingsForm.tsx +3 -19
  94. package/eventcatalog/src/components/Settings/McpSettingsForm.tsx +85 -30
  95. package/eventcatalog/src/components/Settings/SettingsShared.tsx +7 -75
  96. package/eventcatalog/src/components/SideNav/CustomDocsNav.astro +2 -2
  97. package/eventcatalog/src/components/TrialBadge.astro +82 -0
  98. package/eventcatalog/src/content.config.ts +9 -96
  99. package/eventcatalog/src/{enterprise → features}/ai/chat-api.ts +22 -34
  100. package/eventcatalog/src/{enterprise → features}/analytics/adapters/ga4.ts +0 -5
  101. package/eventcatalog/src/{enterprise → features}/analytics/adapters/gtm.ts +0 -5
  102. package/eventcatalog/src/{enterprise → features}/analytics/adapters/posthog.ts +0 -5
  103. package/eventcatalog/src/{enterprise → features}/analytics/components/AnalyticsHead.astro +2 -4
  104. package/eventcatalog/src/{enterprise → features}/analytics/components/AnalyticsTracker.astro +2 -4
  105. package/eventcatalog/src/{enterprise → features}/analytics/tracker.ts +0 -5
  106. package/eventcatalog/src/{enterprise → features}/api/catalog.ts +0 -5
  107. package/eventcatalog/src/{enterprise → features}/auth/[...auth].ts +0 -5
  108. package/eventcatalog/src/{enterprise → features}/auth/middleware/middleware-auth.ts +0 -5
  109. package/eventcatalog/src/{enterprise → features}/auth/middleware/middleware.ts +0 -5
  110. package/eventcatalog/src/{enterprise → features}/collections/custom-pages.ts +0 -5
  111. package/eventcatalog/src/{enterprise → features}/collections/index.ts +0 -5
  112. package/eventcatalog/src/{enterprise → features}/collections/resource-docs.ts +0 -5
  113. package/eventcatalog/src/{enterprise → features}/custom-documentation/collection.ts +0 -5
  114. package/eventcatalog/src/features/custom-documentation/components/CustomDocsNav/CustomDocsNav.astro +9 -0
  115. package/eventcatalog/src/{enterprise → features}/custom-documentation/components/CustomDocsNav/CustomDocsNavWrapper.tsx +0 -5
  116. package/eventcatalog/src/{enterprise → features}/custom-documentation/components/CustomDocsNav/components/NestedItem.tsx +1 -6
  117. package/eventcatalog/src/{enterprise → features}/custom-documentation/components/CustomDocsNav/components/NoResultsFound.tsx +0 -5
  118. package/eventcatalog/src/{enterprise → features}/custom-documentation/components/CustomDocsNav/index.tsx +1 -6
  119. package/eventcatalog/src/{enterprise → features}/custom-documentation/components/CustomDocsNav/types.ts +0 -5
  120. package/eventcatalog/src/{enterprise/custom-documentation/pages/docs/custom/index.astro → features/custom-documentation/components/CustomDocumentationPage.astro} +5 -5
  121. package/eventcatalog/src/{enterprise → features}/custom-documentation/utils/badge-styles.ts +0 -5
  122. package/eventcatalog/src/{enterprise → features}/custom-documentation/utils/custom-docs.ts +1 -6
  123. package/eventcatalog/src/{enterprise → features}/custom-documentation/utils/docs-search.ts +0 -5
  124. package/eventcatalog/src/{enterprise → features}/custom-pages/routes.ts +0 -5
  125. package/eventcatalog/src/{enterprise → features}/directory/user-team-directory.ts +0 -5
  126. package/eventcatalog/src/{enterprise → features}/fields/field-extractor.ts +0 -5
  127. package/eventcatalog/src/{enterprise → features}/fields/field-indexer.ts +0 -5
  128. package/eventcatalog/src/{enterprise → features}/fields/fields-db.ts +0 -5
  129. package/eventcatalog/src/{enterprise → features}/fields/pages/api/fields.ts +1 -6
  130. package/eventcatalog/src/{enterprise → features}/fields/pages/fields.astro +1 -2
  131. package/eventcatalog/src/{enterprise → features}/integrations/eventcatalog-features.ts +17 -75
  132. package/eventcatalog/src/features/mcp/apps/architecture-diagram/links.ts +59 -0
  133. package/eventcatalog/src/features/mcp/apps/architecture-diagram/node-chat.tsx +299 -0
  134. package/eventcatalog/src/features/mcp/apps/architecture-diagram/shared.ts +14 -0
  135. package/eventcatalog/src/features/mcp/apps/architecture-diagram/view.tsx +454 -0
  136. package/eventcatalog/src/features/mcp/apps/generated/viewer.html +432 -0
  137. package/eventcatalog/src/features/mcp/apps/mermaid-stub.ts +10 -0
  138. package/eventcatalog/src/features/mcp/apps/schema-viewer/schema-code.tsx +65 -0
  139. package/eventcatalog/src/features/mcp/apps/schema-viewer/shared.ts +23 -0
  140. package/eventcatalog/src/features/mcp/apps/schema-viewer/view.tsx +170 -0
  141. package/eventcatalog/src/features/mcp/apps/shared/app-view.tsx +98 -0
  142. package/eventcatalog/src/features/mcp/apps/shared/resource-types.ts +15 -0
  143. package/eventcatalog/src/features/mcp/apps/viewer/shared.ts +24 -0
  144. package/eventcatalog/src/features/mcp/apps/viewer/styles.css +25 -0
  145. package/eventcatalog/src/features/mcp/apps/viewer/view.tsx +59 -0
  146. package/eventcatalog/src/features/mcp/mcp-app-icons.ts +63 -0
  147. package/eventcatalog/src/features/mcp/mcp-apps.ts +160 -0
  148. package/eventcatalog/src/{enterprise → features}/mcp/mcp-auth.ts +0 -5
  149. package/eventcatalog/src/{enterprise → features}/mcp/mcp-scope.ts +7 -7
  150. package/eventcatalog/src/{enterprise → features}/mcp/mcp-scoped-tools.ts +47 -40
  151. package/eventcatalog/src/{enterprise → features}/mcp/mcp-server.ts +347 -34
  152. package/eventcatalog/src/{enterprise → features}/mcp/oauth-protected-resource.ts +0 -5
  153. package/eventcatalog/src/features/mcp/schema-viewer.ts +24 -0
  154. package/eventcatalog/src/{enterprise → features}/tools/catalog-tools.ts +133 -102
  155. package/eventcatalog/src/features/tools/index.ts +5 -0
  156. package/eventcatalog/src/{enterprise → features}/visualizer-layout/reset.ts +0 -5
  157. package/eventcatalog/src/{enterprise → features}/visualizer-layout/save.ts +0 -5
  158. package/eventcatalog/src/layouts/BaseLayout.astro +2 -2
  159. package/eventcatalog/src/layouts/Footer.astro +31 -39
  160. package/eventcatalog/src/layouts/SettingsLayout.astro +6 -6
  161. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +7 -37
  162. package/eventcatalog/src/{enterprise → pages}/api/schemas/[collection]/[id]/[version]/index.ts +0 -22
  163. package/eventcatalog/src/{enterprise → pages}/api/schemas/services/[id]/[version]/[specification]/index.ts +0 -19
  164. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/_index.data.ts +2 -6
  165. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/index.astro +1 -5
  166. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/_index.data.ts +2 -6
  167. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/index.astro +1 -5
  168. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId].md.ts +2 -6
  169. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId].mdx.ts +2 -6
  170. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +9 -5
  171. package/eventcatalog/src/pages/docs/custom/[...path]/index.astro +12 -0
  172. package/eventcatalog/src/{enterprise/custom-documentation/pages/docs/custom/root-index.astro → pages/docs/custom/index.astro} +1 -23
  173. package/eventcatalog/src/pages/docs/llm/llms-full.txt.ts +4 -9
  174. package/eventcatalog/src/pages/docs/llm/llms.txt.ts +4 -4
  175. package/eventcatalog/src/pages/docs/llm/schemas.txt.ts +0 -11
  176. package/eventcatalog/src/pages/docs/services/[id]/[docType]/[docId]/index.astro +2 -2
  177. package/eventcatalog/src/pages/index.astro +2 -2
  178. package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/index.astro +0 -3
  179. package/eventcatalog/src/pages/schemas/explorer/index.astro +1 -4
  180. package/eventcatalog/src/pages/settings/assistant.astro +2 -4
  181. package/eventcatalog/src/pages/settings/license.astro +18 -0
  182. package/eventcatalog/src/pages/settings/llm-access.astro +1 -4
  183. package/eventcatalog/src/pages/settings/mcp.astro +33 -4
  184. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/_index.data.ts +1 -2
  185. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/index.astro +2 -45
  186. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/entity-map/index.astro +1 -1
  187. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version].mermaid.ts +7 -109
  188. package/eventcatalog/src/pages/visualiser/_mermaid-route.ts +78 -0
  189. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/_index.data.ts +44 -0
  190. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/index.astro +1 -1
  191. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/index.astro +56 -0
  192. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/systems-context/index.astro +3 -40
  193. package/eventcatalog/src/pages/visualiser/domains/[id]/[version].mermaid.ts +20 -0
  194. package/eventcatalog/src/pages/visualiser/systems/[id]/[version]/_index.data.ts +29 -0
  195. package/eventcatalog/src/pages/visualiser/systems/[id]/[version]/context/index.astro +3 -38
  196. package/eventcatalog/src/pages/visualiser/systems/[id]/[version]/index.astro +56 -0
  197. package/eventcatalog/src/pages/visualiser/systems/[id]/[version].mermaid.ts +20 -0
  198. package/eventcatalog/src/stores/sidebar-store/builders/agent.ts +1 -1
  199. package/eventcatalog/src/stores/sidebar-store/builders/container.ts +1 -1
  200. package/eventcatalog/src/stores/sidebar-store/builders/data-product.ts +1 -1
  201. package/eventcatalog/src/stores/sidebar-store/builders/domain.ts +7 -32
  202. package/eventcatalog/src/stores/sidebar-store/builders/entity.ts +1 -1
  203. package/eventcatalog/src/stores/sidebar-store/builders/message.ts +1 -1
  204. package/eventcatalog/src/stores/sidebar-store/builders/service.ts +2 -8
  205. package/eventcatalog/src/stores/sidebar-store/builders/system.ts +2 -12
  206. package/eventcatalog/src/stores/sidebar-store/custom-sidebar.ts +0 -10
  207. package/eventcatalog/src/stores/sidebar-store/state.ts +4 -6
  208. package/eventcatalog/src/styles/theme.css +22 -18
  209. package/eventcatalog/src/styles/themes/forest.css +8 -8
  210. package/eventcatalog/src/styles/themes/ocean.css +8 -8
  211. package/eventcatalog/src/styles/themes/sapphire.css +8 -8
  212. package/eventcatalog/src/styles/themes/sunset.css +8 -8
  213. package/eventcatalog/src/utils/clipboard.ts +4 -4
  214. package/eventcatalog/src/utils/collections/flow-step-schema.ts +117 -0
  215. package/eventcatalog/src/utils/collections/pointer-schema.ts +7 -0
  216. package/eventcatalog/src/utils/collections/resource-docs.ts +677 -14
  217. package/eventcatalog/src/utils/collections/schema-loader.ts +0 -5
  218. package/eventcatalog/src/utils/feature.ts +14 -23
  219. package/eventcatalog/src/utils/license.ts +114 -0
  220. package/eventcatalog/src/utils/node-graphs/architecture-diagram-types.ts +32 -0
  221. package/eventcatalog/src/utils/node-graphs/architecture-diagram.ts +84 -0
  222. package/eventcatalog/src/utils/node-graphs/channel-node-graph.ts +4 -25
  223. package/eventcatalog/src/utils/node-graphs/container-node-graph.ts +12 -39
  224. package/eventcatalog/src/utils/node-graphs/data-products-node-graph.ts +11 -27
  225. package/eventcatalog/src/utils/node-graphs/domain-levels-node-graph.ts +679 -0
  226. package/eventcatalog/src/utils/node-graphs/domains-node-graph.ts +3 -20
  227. package/eventcatalog/src/utils/node-graphs/export-mermaid.ts +51 -4
  228. package/eventcatalog/src/utils/node-graphs/field-node-graph.ts +8 -23
  229. package/eventcatalog/src/utils/node-graphs/flow-lanes.ts +119 -0
  230. package/eventcatalog/src/utils/node-graphs/flows-node-graph.ts +133 -47
  231. package/eventcatalog/src/utils/node-graphs/layout-node-graph.ts +28 -0
  232. package/eventcatalog/src/utils/node-graphs/layout-persistence.ts +3 -3
  233. package/eventcatalog/src/utils/node-graphs/message-node-graph.ts +16 -34
  234. package/eventcatalog/src/utils/node-graphs/services-node-graph.ts +11 -25
  235. package/eventcatalog/src/utils/node-graphs/system-context-node-graph.ts +18 -54
  236. package/eventcatalog/src/utils/node-graphs/system-levels-node-graph.ts +59 -0
  237. package/eventcatalog/src/utils/node-graphs/systems-node-graph.ts +20 -80
  238. package/eventcatalog/src/utils/node-graphs/utils/utils.ts +1 -78
  239. package/eventcatalog/src/utils/trial.ts +36 -0
  240. package/eventcatalog/tsconfig.json +1 -1
  241. package/package.json +11 -11
  242. package/dist/chunk-FV56YFM4.js +0 -7
  243. package/dist/federation/entitlement.cjs +0 -31
  244. package/dist/federation/entitlement.d.cts +0 -12
  245. package/dist/federation/entitlement.d.ts +0 -12
  246. package/dist/federation/entitlement.js +0 -6
  247. package/dist/runtime-dependencies/dagre.mjs +0 -3
  248. package/eventcatalog/src/components/Settings/BillingSettingsForm.tsx +0 -265
  249. package/eventcatalog/src/enterprise/LICENSE +0 -57
  250. package/eventcatalog/src/enterprise/README.md +0 -5
  251. package/eventcatalog/src/enterprise/collections/resource-docs-utils.ts +0 -691
  252. package/eventcatalog/src/enterprise/custom-documentation/components/CustomDocsNav/CustomDocsNav.astro +0 -9
  253. package/eventcatalog/src/enterprise/custom-documentation/pages/docs/custom/[...path]/index.astro +0 -18
  254. package/eventcatalog/src/enterprise/custom-documentation/pages/docs/custom/feature.astro +0 -171
  255. package/eventcatalog/src/enterprise/feature.ts +0 -75
  256. package/eventcatalog/src/enterprise/tools/index.ts +0 -10
  257. package/eventcatalog/src/hooks/eventcatalog-visualizer.ts +0 -126
  258. package/eventcatalog/src/pages/settings/billing.astro +0 -17
  259. package/eventcatalog/src/pages/visualiser/domain-integrations/index.astro +0 -26
  260. package/eventcatalog/src/types/mcp-modules.d.ts +0 -67
  261. package/eventcatalog/src/utils/node-graphs/domains-canvas.ts +0 -261
  262. package/eventcatalog/src/utils/node-graphs/export-node-graph.ts +0 -91
  263. /package/eventcatalog/src/{enterprise → features}/auth/error.astro +0 -0
  264. /package/eventcatalog/src/{enterprise → features}/auth/login.astro +0 -0
  265. /package/eventcatalog/src/{enterprise → features}/auth/unauthorized.astro +0 -0
  266. /package/eventcatalog/src/{enterprise/custom-documentation/pages → pages}/docs/custom/[...path]/_index.data.ts +0 -0
  267. /package/eventcatalog/src/{enterprise/custom-documentation/pages → pages}/docs/custom/[...path].mdx.ts +0 -0
@@ -0,0 +1,679 @@
1
+ import { getDomains } from '@utils/collections/domains';
2
+ import { getNodesAndEdges as getSystemNodesAndEdges } from './systems-node-graph';
3
+ import { getNodesAndEdges as getServiceNodesAndEdges } from './services-node-graph';
4
+ import { getNodesAndEdgesForDomainSystems as getDomainContextNodesAndEdges } from './system-context-node-graph';
5
+ import { createEdge, generateIdForNode } from '@utils/node-graphs/utils/utils';
6
+ import { layoutLevels } from '@utils/node-graphs/layout-node-graph';
7
+ import { getMessagesLabel, hideMessageNodes, isCarrier } from '@eventcatalog/visualiser/layout';
8
+
9
+ interface NodesAndEdgesProps {
10
+ id: string;
11
+ version: string;
12
+ mode?: 'simple' | 'full';
13
+ channelRenderMode?: 'single' | 'flat';
14
+ }
15
+
16
+ const CACHE_ENABLED = process.env.DISABLE_EVENTCATALOG_CACHE !== 'true';
17
+
18
+ type Graph = { nodes: any[]; edges: any[] };
19
+ type Tree = { domain: any; groupId: string; parentId?: string }[];
20
+ type Owners = Map<string, { domain: any; system?: any }>;
21
+
22
+ const MESSAGE_TYPES = new Set(['events', 'commands', 'queries']);
23
+ const SERVICE_TYPES = new Set(['services', 'agents']);
24
+
25
+ const systemGroupId = (system: any) => `system-group-${system.data.id}-${system.data.version}`;
26
+ const CONTEXT_EDGE_PREFIX = 'context-';
27
+ const domainGroupId = (domain: any) => `domain-group-${domain.data.id}-${domain.data.version}`;
28
+ const domainNodeId = (domain: any) => `domain-${domain.data.id}-${domain.data.version}`;
29
+
30
+ /** A system shown as a single node (not expanded into what's inside it) */
31
+ const createSystemNode = (system: any, parentId?: string) => ({
32
+ id: generateIdForNode(system),
33
+ type: 'systems',
34
+ parentId,
35
+ position: { x: 0, y: 0 },
36
+ data: {
37
+ mode: 'full',
38
+ system: {
39
+ id: system.data.id,
40
+ version: system.data.version,
41
+ name: system.data.name,
42
+ summary: system.data.summary,
43
+ scope: system.data.scope,
44
+ },
45
+ servicesCount: (system.data.services || []).length,
46
+ containersCount: (system.data.containers || []).length,
47
+ },
48
+ });
49
+
50
+ /** A domain shown as a single card, with counts of what's in it */
51
+ const createDomainCard = (domain: any, { parentId, subdomain = false }: { parentId?: string; subdomain?: boolean } = {}) => {
52
+ const systems = (domain.data.systems || []) as any[];
53
+ return {
54
+ id: domainNodeId(domain),
55
+ type: 'context-domain',
56
+ ...(parentId ? { parentId } : {}),
57
+ position: { x: 0, y: 0 },
58
+ data: {
59
+ mode: 'full',
60
+ domain: {
61
+ id: domain.data.id,
62
+ version: domain.data.version,
63
+ name: domain.data.name,
64
+ summary: domain.data.summary,
65
+ },
66
+ subdomain,
67
+ systemsCount: systems.length,
68
+ servicesCount:
69
+ systems.reduce((count, system) => count + (system.data.services || []).length, 0) + (domain.data.services || []).length,
70
+ entitiesCount: (domain.data.entities || []).length,
71
+ },
72
+ };
73
+ };
74
+
75
+ /**
76
+ * Level 1: the domain with its subdomains and systems, its context (related
77
+ * systems and actors) and the other domains its systems send messages to or
78
+ * receive them from. Everything inside a system collapses into the system, and
79
+ * anything else in a (sub)domain (e.g. a service not in a system) into the
80
+ * domain, so edges connect domains, systems and actors. A subdomain without
81
+ * systems is shown as a card.
82
+ */
83
+ export const collapseSystems = (
84
+ graph: Graph,
85
+ systems: any[],
86
+ contextNodes: Map<string, any>,
87
+ subdomains: Map<string, any>
88
+ ): Graph => {
89
+ const nodesById = new Map(graph.nodes.map((node) => [node.id, node]));
90
+ const systemsByGroupId = new Map<string, any>(systems.map((system: any) => [systemGroupId(system), system]));
91
+ const ancestors = (nodeId: string) => {
92
+ const ids: string[] = [];
93
+ for (let node = nodesById.get(nodeId); node?.parentId; node = nodesById.get(node.parentId)) ids.push(node.parentId);
94
+ return ids;
95
+ };
96
+
97
+ // Subdomains without systems (in them or their subdomains) become cards,
98
+ // along with everything in them
99
+ const withSystems = new Set([...systemsByGroupId.keys()].flatMap((groupId) => ancestors(groupId)));
100
+ const cards = new Map<string, any>();
101
+ subdomains.forEach((subdomain, groupId) => {
102
+ if (withSystems.has(groupId) || ancestors(groupId).some((id) => subdomains.has(id) && !withSystems.has(id))) return;
103
+ cards.set(groupId, createDomainCard(subdomain, { parentId: nodesById.get(groupId)?.parentId, subdomain: true }));
104
+ });
105
+
106
+ // The node something is shown as: its system, the card of the subdomain it's
107
+ // in, the (sub)domain it's in, or itself
108
+ const representative = (nodeId: string): string => {
109
+ for (let node = nodesById.get(nodeId); node; node = node.parentId ? nodesById.get(node.parentId) : undefined) {
110
+ const system: any = systemsByGroupId.get(node.id);
111
+ if (system) return generateIdForNode(system);
112
+ const card = cards.get(node.id);
113
+ if (card) return card.id;
114
+ if (node.type === 'domain-group' || node.type === 'context-domain') return node.id;
115
+ }
116
+ return nodeId;
117
+ };
118
+
119
+ const systemNodes = [...systemsByGroupId].map(([groupId, system]) => {
120
+ const parentId = nodesById.get(groupId)?.parentId;
121
+ // Prefer the node from the context diagram, it has the system's details
122
+ const contextNode = contextNodes.get(generateIdForNode(system));
123
+ return contextNode ? { ...contextNode, parentId } : createSystemNode(system, parentId);
124
+ });
125
+ const otherNodes = graph.nodes.filter(
126
+ (node) => representative(node.id) === node.id && !systemsByGroupId.has(node.id) && node.type !== 'data'
127
+ );
128
+ const shownNodes = new Map([...otherNodes, ...systemNodes, ...cards.values()].map((node) => [node.id, node]));
129
+ // Whether one shown node is inside another (e.g. a system in its domain)
130
+ const isInside = (nodeId: string, groupId: string) => {
131
+ for (let node = shownNodes.get(nodeId); node?.parentId; node = shownNodes.get(node.parentId)) {
132
+ if (node.parentId === groupId) return true;
133
+ }
134
+ return false;
135
+ };
136
+
137
+ // Edges between systems / actors / domains: context relationships as they
138
+ // are, edges to nodes kept as their first label, the rest labelled with the
139
+ // messages they carry
140
+ const connections = new Map<
141
+ string,
142
+ { source: string; target: string; messages: Map<string, string>; relationship?: any; label?: string; crossDomain?: boolean }
143
+ >();
144
+ graph.edges.forEach((edge) => {
145
+ const source = representative(edge.source);
146
+ const target = representative(edge.target);
147
+ if (
148
+ source === target ||
149
+ !shownNodes.has(source) ||
150
+ !shownNodes.has(target) ||
151
+ isInside(source, target) ||
152
+ isInside(target, source)
153
+ )
154
+ return;
155
+ const key = `${source}->${target}`;
156
+ const connection = connections.get(key) ?? { source, target, messages: new Map<string, string>() };
157
+ if (edge.id.startsWith(CONTEXT_EDGE_PREFIX)) connection.relationship = edge;
158
+ if (edge.data?.crossDomain) connection.crossDomain = true;
159
+ [edge.source, edge.target].forEach((nodeId) => {
160
+ const node = nodesById.get(nodeId);
161
+ if (MESSAGE_TYPES.has(node?.type)) connection.messages.set(node.data?.message?.name ?? nodeId, node.type);
162
+ });
163
+ connections.set(key, connection);
164
+ });
165
+
166
+ // A relationship already connects the pair (either way), so skip messages
167
+ const related = new Set(
168
+ [...connections.values()]
169
+ .filter((c) => c.relationship)
170
+ .flatMap((c) => [`${c.source}->${c.target}`, `${c.target}->${c.source}`])
171
+ );
172
+
173
+ return {
174
+ nodes: [...shownNodes.values()],
175
+ edges: [...connections.values()]
176
+ .filter((c) => c.relationship || !related.has(`${c.source}->${c.target}`))
177
+ .map(({ source, target, messages, relationship, label, crossDomain }) =>
178
+ relationship
179
+ ? { ...relationship, source, target }
180
+ : createEdge({
181
+ id: `${source}->${target}`,
182
+ source,
183
+ target,
184
+ label: label ?? getMessagesLabel(messages),
185
+ ...(crossDomain ? { data: { crossDomain } } : {}),
186
+ })
187
+ ),
188
+ };
189
+ };
190
+
191
+ /**
192
+ * The domain and its subdomains (and theirs), each drawn as a group inside the
193
+ * group of the domain it's a subdomain of.
194
+ */
195
+ export const buildDomainTree = (domain: any, findDomain: (id: string, version?: string) => any): Tree => {
196
+ const tree: Tree = [];
197
+ const addToTree = (d: any, parentId?: string) => {
198
+ if (tree.some((entry) => entry.domain.data.id === d.data.id)) return;
199
+ tree.push({ domain: d, groupId: domainGroupId(d), parentId });
200
+ (d.data.domains || []).forEach((subdomain: any) => {
201
+ const found = findDomain(subdomain.data.id, subdomain.data.version);
202
+ if (found) addToTree(found, domainGroupId(d));
203
+ });
204
+ };
205
+ addToTree(domain);
206
+ return tree;
207
+ };
208
+
209
+ /** Which domain (and system) each service belongs to, and the domain each subdomain is in */
210
+ export const collectOwners = (domains: any[]) => {
211
+ const owners: Owners = new Map();
212
+ const parentDomainOf = new Map<string, any>();
213
+ domains.forEach((d: any) => {
214
+ (d.data.systems || []).forEach((system: any) =>
215
+ (system.data.services || []).forEach((service: any) => owners.set(generateIdForNode(service), { domain: d, system }))
216
+ );
217
+ (d.data.domains || []).forEach((subdomain: any) => {
218
+ if (!parentDomainOf.has(subdomain.data.id)) parentDomainOf.set(subdomain.data.id, d);
219
+ });
220
+ });
221
+ // Services outside systems, unless a system already claims them
222
+ domains.forEach((d: any) =>
223
+ (d.data.services || []).forEach((service: any) => {
224
+ const serviceId = generateIdForNode(service);
225
+ if (!owners.has(serviceId)) owners.set(serviceId, { domain: d });
226
+ })
227
+ );
228
+ return { owners, parentDomainOf };
229
+ };
230
+
231
+ /**
232
+ * The domain's graph from the graphs of its (and its subdomains') systems and
233
+ * services outside systems: each node placed in its (sub)domain or system, and
234
+ * services in other domains replaced by those domains, as a card (or a box
235
+ * around their subdomains' cards). Returns the nodes, the edges (pointed at the
236
+ * replacements) and the nodes standing for other domains.
237
+ */
238
+ export const buildDomainGraph = ({
239
+ tree,
240
+ owners,
241
+ parentDomainOf,
242
+ systems,
243
+ systemGraphs,
244
+ looseServices,
245
+ serviceGraphs,
246
+ mode,
247
+ }: {
248
+ tree: Tree;
249
+ owners: Owners;
250
+ parentDomainOf: Map<string, any>;
251
+ systems: { system: any; groupId: string }[];
252
+ systemGraphs: Graph[];
253
+ looseServices: { service: any; groupId: string }[];
254
+ serviceGraphs: Graph[];
255
+ mode: string;
256
+ }) => {
257
+ const groupOfDomain = new Map(tree.map((entry) => [entry.domain.data.id, entry.groupId]));
258
+ const groupOfSystem = new Map(systems.map(({ system, groupId }) => [systemGroupId(system), groupId]));
259
+ const nodes = new Map<string, any>(
260
+ tree.map(({ domain: d, groupId, parentId }) => [
261
+ groupId,
262
+ {
263
+ id: groupId,
264
+ type: 'domain-group',
265
+ position: { x: 0, y: 0 },
266
+ ...(parentId ? { parentId } : {}),
267
+ draggable: false,
268
+ selectable: false,
269
+ data: {
270
+ domain: { name: d.data.name, version: d.data.version },
271
+ // The domain viewed may itself be a subdomain of another
272
+ subdomain: !!parentId || parentDomainOf.has(d.data.id),
273
+ isFocused: !parentId,
274
+ },
275
+ },
276
+ ])
277
+ );
278
+ const edges: any[] = [];
279
+ // Nodes replaced by another (services in other domains become the domain)
280
+ const replacedBy = new Map<string, string>();
281
+
282
+ // Other domains are shown as a single card, with counts of what's in them. A
283
+ // subdomain of another domain is shown inside a box for that domain (and
284
+ // that inside its domain's, if it's a subdomain too)
285
+ const inParentDomain = (other: any) => {
286
+ const parent = parentDomainOf.get(other.data.id);
287
+ return parent && (groupOfDomain.get(parent.data.id) ?? addOtherDomainBox(parent));
288
+ };
289
+ // Cards replaced by a box, when a domain's subdomains are shown in it
290
+ const cardBoxes = new Map<string, string>();
291
+ const addOtherDomain = (other: any): string => {
292
+ const boxId = domainGroupId(other);
293
+ if (nodes.has(boxId)) return boxId;
294
+ const nodeId = domainNodeId(other);
295
+ if (!nodes.has(nodeId)) {
296
+ const parentId = inParentDomain(other);
297
+ const card = createDomainCard(other, { parentId, subdomain: !!parentId });
298
+ nodes.set(nodeId, { ...card, data: { ...card.data, otherDomain: true } });
299
+ }
300
+ return nodeId;
301
+ };
302
+ const addOtherDomainBox = (other: any): string => {
303
+ const boxId = domainGroupId(other);
304
+ if (!nodes.has(boxId)) {
305
+ const parentId = inParentDomain(other);
306
+ nodes.set(boxId, {
307
+ id: boxId,
308
+ type: 'domain-group',
309
+ position: { x: 0, y: 0 },
310
+ ...(parentId ? { parentId } : {}),
311
+ data: {
312
+ domain: { name: other.data.name, version: other.data.version },
313
+ subdomain: !!parentId,
314
+ isFocused: false,
315
+ otherDomain: true,
316
+ },
317
+ });
318
+ // Shown as the box from now on, rather than a card as well
319
+ const cardId = domainNodeId(other);
320
+ if (nodes.has(cardId)) {
321
+ nodes.delete(cardId);
322
+ cardBoxes.set(cardId, boxId);
323
+ }
324
+ }
325
+ return boxId;
326
+ };
327
+
328
+ // Adds a graph's nodes: a system's (its nodes are in its group), or a service's
329
+ // outside any system, whose messages, channels and data stores go `inside`
330
+ // the (sub)domain it's in
331
+ const addNodes = (graphNodes: any[], inside?: string) =>
332
+ graphNodes.forEach((node: any) => {
333
+ if (node.type === 'system-group') {
334
+ nodes.set(node.id, {
335
+ ...node,
336
+ parentId: groupOfSystem.get(node.id) ?? inside,
337
+ data: { ...node.data, isFocused: false },
338
+ });
339
+ return;
340
+ }
341
+
342
+ const owner = SERVICE_TYPES.has(node.type) ? owners.get(node.id) : undefined;
343
+ if (owner && !groupOfDomain.has(owner.domain.data.id)) {
344
+ replacedBy.set(node.id, addOtherDomain(owner.domain));
345
+ return;
346
+ }
347
+ if (owner && owner.system) {
348
+ // Only placed by the system that owns it
349
+ if (systemGroupId(owner.system) === node.parentId) nodes.set(node.id, node);
350
+ return;
351
+ }
352
+ if (owner) {
353
+ // A service in the domain (or a subdomain), but not in any of its
354
+ // systems (not marked as the one being viewed, as it is in its own diagram)
355
+ nodes.set(node.id, {
356
+ ...node,
357
+ parentId: groupOfDomain.get(owner.domain.data.id),
358
+ extent: undefined,
359
+ data: { ...node.data, isFocused: false },
360
+ });
361
+ return;
362
+ }
363
+ // Messages, channels, data stores: kept in the first graph they appear in
364
+ if (nodes.has(node.id)) return;
365
+ nodes.set(node.id, node.parentId || !inside || SERVICE_TYPES.has(node.type) ? node : { ...node, parentId: inside });
366
+ });
367
+
368
+ systems.forEach(({ system, groupId: domainGroup }, index) => {
369
+ const graph = systemGraphs[index];
370
+ const groupId = systemGroupId(system);
371
+ // A system with nothing in it still shows as an (empty) system
372
+ if (!graph.nodes.some((node: any) => node.id === groupId)) {
373
+ nodes.set(groupId, {
374
+ id: groupId,
375
+ type: 'system-group',
376
+ parentId: domainGroup,
377
+ position: { x: 0, y: 0 },
378
+ data: { system: { name: system.data.name, version: system.data.version } },
379
+ });
380
+ }
381
+ addNodes(graph.nodes);
382
+ edges.push(...graph.edges);
383
+ });
384
+ serviceGraphs.forEach((graph, index) => {
385
+ addNodes(graph.nodes, looseServices[index].groupId);
386
+ edges.push(...graph.edges);
387
+ });
388
+
389
+ // Services outside systems that weren't in any graph
390
+ looseServices.forEach(({ service, groupId }) => {
391
+ const serviceId = generateIdForNode(service);
392
+ if (nodes.has(serviceId)) return;
393
+ nodes.set(serviceId, {
394
+ id: serviceId,
395
+ type: service.collection,
396
+ parentId: groupId,
397
+ position: { x: 0, y: 0 },
398
+ data: { mode, service: { ...service.data } },
399
+ });
400
+ });
401
+
402
+ // Other domains shown as a box (around their subdomains) rather than a card
403
+ replacedBy.forEach((nodeId, replaced) => {
404
+ const boxId = cardBoxes.get(nodeId);
405
+ if (boxId) replacedBy.set(replaced, boxId);
406
+ });
407
+
408
+ const graphEdges = connectEdges(edges, replacedBy, nodes);
409
+ placeMessagesWithPublishers(nodes, graphEdges);
410
+ return { nodes, edges: graphEdges, otherDomainIds: new Set(replacedBy.values()) };
411
+ };
412
+
413
+ /**
414
+ * Messages are placed in the first graph they appear in, which can be a
415
+ * system consuming them. Moves each message into the system (or domain) of the
416
+ * services publishing it, when they're all in the same one.
417
+ */
418
+ export const placeMessagesWithPublishers = (nodes: Map<string, any>, edges: any[]) => {
419
+ const publisherGroups = new Map<string, Set<string | undefined>>();
420
+ edges.forEach((edge) => {
421
+ const publisher = nodes.get(edge.source);
422
+ if (!MESSAGE_TYPES.has(nodes.get(edge.target)?.type) || !SERVICE_TYPES.has(publisher?.type)) return;
423
+ const groups = publisherGroups.get(edge.target) || new Set();
424
+ groups.add(publisher.parentId);
425
+ publisherGroups.set(edge.target, groups);
426
+ });
427
+ publisherGroups.forEach((groups, messageId) => {
428
+ const [parentId] = [...groups];
429
+ const message = nodes.get(messageId);
430
+ if (groups.size === 1 && parentId && message.parentId !== parentId) {
431
+ nodes.set(messageId, { ...message, parentId, extent: undefined });
432
+ }
433
+ });
434
+ };
435
+
436
+ /**
437
+ * Edges pointed at the nodes replacing their ends, dropping duplicates, ones to
438
+ * nowhere and ones between a domain and a subdomain in it.
439
+ */
440
+ export const connectEdges = (edges: any[], replacedBy: Map<string, string>, nodes: Map<string, any>) => {
441
+ const isInside = (nodeId: string, groupId: string) => {
442
+ for (let node = nodes.get(nodeId); node?.parentId; node = nodes.get(node.parentId)) {
443
+ if (node.parentId === groupId) return true;
444
+ }
445
+ return false;
446
+ };
447
+ const seen = new Set<string>();
448
+ return edges
449
+ .map((edge) => ({
450
+ ...edge,
451
+ source: replacedBy.get(edge.source) ?? edge.source,
452
+ target: replacedBy.get(edge.target) ?? edge.target,
453
+ }))
454
+ .filter((edge) => {
455
+ const key = `${edge.source}->${edge.target}`;
456
+ if (
457
+ edge.source === edge.target ||
458
+ !nodes.has(edge.source) ||
459
+ !nodes.has(edge.target) ||
460
+ isInside(edge.source, edge.target) ||
461
+ isInside(edge.target, edge.source) ||
462
+ seen.has(key)
463
+ )
464
+ return false;
465
+ seen.add(key);
466
+ return true;
467
+ });
468
+ };
469
+
470
+ /**
471
+ * The context diagrams of the domain and its subdomains: relationships between
472
+ * their systems, and to related systems and actors outside them. Their systems
473
+ * are shown expanded, so relationships connect to the expanded systems.
474
+ */
475
+ export const addContext = (contexts: Graph[], systems: { system: any }[], nodes: Map<string, any>) => {
476
+ const contextNodesById = new Map(contexts.flatMap((context) => context.nodes.map((node: any) => [node.id, node])));
477
+ const contextEdgesById = new Map(contexts.flatMap((context) => context.edges.map((edge: any) => [edge.id, edge])));
478
+ const domainSystemIds = new Set(systems.map(({ system }) => generateIdForNode(system)));
479
+ const toSystemGroup = (nodeId: string) => (domainSystemIds.has(nodeId) ? `system-group-${nodeId}` : nodeId);
480
+ return {
481
+ contextNodesById,
482
+ nodes: [...contextNodesById.values()].filter((node: any) => !domainSystemIds.has(node.id) && !nodes.has(node.id)),
483
+ edges: [...contextEdgesById.values()].map((edge: any) => ({
484
+ ...edge,
485
+ id: `${CONTEXT_EDGE_PREFIX}${edge.id}`,
486
+ source: toSystemGroup(edge.source),
487
+ target: toSystemGroup(edge.target),
488
+ })),
489
+ };
490
+ };
491
+
492
+ /**
493
+ * Marks cross-domain communication. Messages and channels only other domains
494
+ * send to (e.g. a message this domain consumes, and the topic it comes
495
+ * through) are another domain's: they sit outside this domain, between the
496
+ * domain sending them and their consumers, rather than in the system that
497
+ * consumes them. They, and this domain's messages and channels that reach
498
+ * other domains, are on a cross-domain path (`data.crossDomain`), as are the
499
+ * edges to and from them and other domains.
500
+ */
501
+ export const markCrossDomain = (graph: Graph, otherDomainIds: Set<string>): Graph => {
502
+ const sendersOf = new Map<string, string[]>();
503
+ const receiversOf = new Map<string, string[]>();
504
+ graph.edges.forEach((edge) => {
505
+ const senders = sendersOf.get(edge.target) || [];
506
+ senders.push(edge.source);
507
+ sendersOf.set(edge.target, senders);
508
+ const receivers = receiversOf.get(edge.source) || [];
509
+ receivers.push(edge.target);
510
+ receiversOf.set(edge.source, receivers);
511
+ });
512
+ const carriers = graph.nodes.filter(isCarrier);
513
+
514
+ // Along chains (message -> channel -> channel), until nothing more is found
515
+ const spread = (reached: Set<string>, isReached: (nodeId: string, reached: Set<string>) => boolean) => {
516
+ for (let found = true; found; ) {
517
+ found = false;
518
+ carriers.forEach((node) => {
519
+ if (!reached.has(node.id) && isReached(node.id, reached)) {
520
+ reached.add(node.id);
521
+ found = true;
522
+ }
523
+ });
524
+ }
525
+ return reached;
526
+ };
527
+ // Sent only by other domains (or their messages and channels)
528
+ const external = spread(new Set(otherDomainIds), (nodeId, reached) => {
529
+ const senders = sendersOf.get(nodeId) || [];
530
+ return senders.length > 0 && senders.every((sender) => reached.has(sender));
531
+ });
532
+ // Reaching other domains (directly, or through messages and channels that do)
533
+ const outgoing = spread(new Set(external), (nodeId, reached) =>
534
+ (receiversOf.get(nodeId) || []).some((receiver) => reached.has(receiver))
535
+ );
536
+
537
+ const isExternalCarrier = (node: any) => external.has(node.id) && !otherDomainIds.has(node.id);
538
+ const onCrossDomainPath = (nodeId: string) => outgoing.has(nodeId);
539
+ return {
540
+ nodes: graph.nodes.map((node) => {
541
+ if (isExternalCarrier(node)) {
542
+ return {
543
+ ...node,
544
+ parentId: undefined,
545
+ extent: undefined,
546
+ data: { ...node.data, otherDomain: true, crossDomain: true },
547
+ };
548
+ }
549
+ return isCarrier(node) && onCrossDomainPath(node.id) ? { ...node, data: { ...node.data, crossDomain: true } } : node;
550
+ }),
551
+ edges: graph.edges.map((edge) =>
552
+ onCrossDomainPath(edge.source) || onCrossDomainPath(edge.target)
553
+ ? { ...edge, data: { ...edge.data, crossDomain: true } }
554
+ : edge
555
+ ),
556
+ };
557
+ };
558
+
559
+ /**
560
+ * Level 3: the domain with its subdomains (as domains inside it) and systems
561
+ * expanded into their services, data stores, messages and channels. Services in
562
+ * other domains are shown as those domains. Returns level 1 (`overview`, when
563
+ * the domain has systems or subdomains) and level 2 (`hiddenMessages`) too,
564
+ * built from the same graph.
565
+ */
566
+ type DomainLevels = Graph & { overview?: Graph; hiddenMessages: Graph };
567
+
568
+ // Built once for each domain version and options: the Diagram page and every
569
+ // docs page embedding the domain share it
570
+ const levelsCache = new Map<string, Promise<DomainLevels>>();
571
+
572
+ export const getNodesAndEdges = (props: NodesAndEdgesProps): Promise<DomainLevels> => {
573
+ if (!CACHE_ENABLED) return buildLevels(props);
574
+ const { id, version, mode = 'full', channelRenderMode } = props;
575
+ const key = [id, version, mode, channelRenderMode].join('/');
576
+ if (!levelsCache.has(key)) levelsCache.set(key, buildLevels(props));
577
+ return levelsCache.get(key)!;
578
+ };
579
+
580
+ const buildLevels = async ({ id, version, mode = 'full', channelRenderMode }: NodesAndEdgesProps): Promise<DomainLevels> => {
581
+ // Each domain with only its own services (not its subdomains'): the version
582
+ // shown (which may be an older one), and the latest of every domain for its
583
+ // subdomains and the other domains around it
584
+ const [allVersions, domains] = await Promise.all([
585
+ getDomains({ getAllVersions: true, includeServicesInSubdomains: false }),
586
+ getDomains({ getAllVersions: false, includeServicesInSubdomains: false }),
587
+ ]);
588
+ const domain = allVersions.find((d) => d.data.id === id && d.data.version === version);
589
+ if (!domain) return { nodes: [], edges: [], hiddenMessages: { nodes: [], edges: [] } };
590
+ // Subdomains' versions in frontmatter can be loose, so fall back to the latest
591
+ const findDomain = (domainId: string, domainVersion?: string) =>
592
+ domains.find((d) => d.data.id === domainId && d.data.version === domainVersion) ??
593
+ domains.find((d) => d.data.id === domainId);
594
+
595
+ const tree = buildDomainTree(domain, findDomain);
596
+ // The tree's domains last, so the versions shown own their services
597
+ const { owners, parentDomainOf } = collectOwners([...domains, ...tree.map((entry) => entry.domain)]);
598
+ // A service the domain (or a subdomain) lists itself is shown as its own,
599
+ // even when a system in another domain has it too
600
+ const treeDomainIds = new Set(tree.map((entry) => entry.domain.data.id));
601
+ tree.forEach(({ domain: d }) =>
602
+ (d.data.services || []).forEach((service: any) => {
603
+ const serviceId = generateIdForNode(service);
604
+ if (!treeDomainIds.has(owners.get(serviceId)?.domain.data.id)) owners.set(serviceId, { domain: d });
605
+ })
606
+ );
607
+
608
+ // Every system in the domain and its subdomains, and the group each is in,
609
+ // and the services in them that aren't in any system
610
+ const systems = tree.flatMap(({ domain: d, groupId }) =>
611
+ ((d.data.systems || []) as any[]).map((system) => ({ system, groupId }))
612
+ );
613
+ const looseServices = tree.flatMap(({ domain: d, groupId }) =>
614
+ ((d.data.services || []) as any[])
615
+ .filter((service) => !owners.get(generateIdForNode(service))?.system)
616
+ .map((service) => ({ service, groupId }))
617
+ );
618
+ const [systemGraphs, serviceGraphs, contexts] = await Promise.all([
619
+ Promise.all(
620
+ systems.map(({ system }) =>
621
+ getSystemNodesAndEdges({
622
+ id: system.data.id,
623
+ version: system.data.version,
624
+ mode,
625
+ channelRenderMode,
626
+ wrapInSystemGroup: true,
627
+ layout: false,
628
+ })
629
+ )
630
+ ),
631
+ Promise.all(
632
+ looseServices.map(({ service }) =>
633
+ getServiceNodesAndEdges({
634
+ id: service.data.id,
635
+ version: service.data.version,
636
+ mode,
637
+ renderAllEdges: true,
638
+ channelRenderMode,
639
+ collection: service.collection === 'agents' ? 'agents' : 'services',
640
+ layout: false,
641
+ })
642
+ )
643
+ ),
644
+ Promise.all(
645
+ tree.map(({ domain: d }) => getDomainContextNodesAndEdges({ id: d.data.id, version: d.data.version, mode, layout: false }))
646
+ ),
647
+ ]);
648
+
649
+ const { nodes, edges, otherDomainIds } = buildDomainGraph({
650
+ tree,
651
+ owners,
652
+ parentDomainOf,
653
+ systems,
654
+ systemGraphs,
655
+ looseServices,
656
+ serviceGraphs,
657
+ mode,
658
+ });
659
+ const context = addContext(contexts, systems, nodes);
660
+ const graph = { nodes: [...nodes.values(), ...context.nodes], edges: [...edges, ...context.edges] };
661
+ const detailedGraph = markCrossDomain(graph, otherDomainIds);
662
+
663
+ // Level 1 (domains, systems and their relationships) collapses each system,
664
+ // including the messages its services consume. Messages outside systems (sent
665
+ // by services not in one) are hidden too, with the nodes either side connected
666
+ // directly. A domain without systems or subdomains has no level 1.
667
+ if (systems.length === 0 && tree.length === 1) return layoutLevels(detailedGraph);
668
+ // Built from the graph before messages only other domains send were moved
669
+ // out of the systems consuming them, so they collapse into those systems
670
+ const collapsed = collapseSystems(
671
+ { ...graph, edges: detailedGraph.edges },
672
+ systems.map(({ system }) => system),
673
+ context.contextNodesById,
674
+ new Map(tree.filter((entry) => entry.parentId).map((entry) => [entry.groupId, entry.domain]))
675
+ );
676
+ const overview = hideMessageNodes(collapsed.nodes, collapsed.edges);
677
+
678
+ return layoutLevels(detailedGraph, overview);
679
+ };