@eventcatalog/core 5.0.0-beta.3 → 5.0.0-beta.30

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 (277) hide show
  1. package/LICENSE +234 -134
  2. package/README.md +5 -5
  3. package/dist/analytics/analytics.cjs +18 -3
  4. package/dist/analytics/analytics.d.cts +1 -1
  5. package/dist/analytics/analytics.d.ts +1 -1
  6. package/dist/analytics/analytics.js +3 -2
  7. package/dist/analytics/log-build.cjs +160 -77
  8. package/dist/analytics/log-build.d.cts +2 -2
  9. package/dist/analytics/log-build.d.ts +2 -2
  10. package/dist/analytics/log-build.js +7 -5
  11. package/dist/analytics/studio-usage.cjs +90 -0
  12. package/dist/analytics/studio-usage.d.cts +5 -0
  13. package/dist/analytics/studio-usage.d.ts +5 -0
  14. package/dist/analytics/studio-usage.js +10 -0
  15. package/dist/analytics/telemetry.cjs +42 -0
  16. package/dist/analytics/telemetry.d.cts +20 -0
  17. package/dist/analytics/telemetry.d.ts +20 -0
  18. package/dist/analytics/telemetry.js +8 -0
  19. package/dist/catalog-runtime.cjs +1 -1
  20. package/dist/{chunk-RFIXKATC.js → chunk-4G7FSRPN.js} +35 -42
  21. package/dist/{chunk-6OC46WQJ.js → chunk-55QETQZ3.js} +66 -35
  22. package/dist/{chunk-TQLRG35T.js → chunk-AD7CL772.js} +78 -45
  23. package/dist/chunk-CA6WCQ2T.js +54 -0
  24. package/dist/chunk-CPW6G62V.js +17 -0
  25. package/dist/{chunk-423S2KHN.js → chunk-FLAZ2JVN.js} +4 -2
  26. package/dist/{chunk-BH2F5WPN.js → chunk-GQI5UYXZ.js} +1 -1
  27. package/dist/{chunk-GYIVJXOJ.js → chunk-NA7OTBWW.js} +1 -1
  28. package/dist/{chunk-MDWB5HLO.js → chunk-NKNOXQ62.js} +1 -1
  29. package/dist/{chunk-3PKS2IIP.js → chunk-OX6TSVHY.js} +1 -1
  30. package/dist/{chunk-36RYTYT5.js → chunk-PD7KTSYN.js} +1 -1
  31. package/dist/chunk-S5GWAUG5.js +29 -0
  32. package/dist/{chunk-NDJOAITI.js → chunk-T3OROMAU.js} +9 -3
  33. package/dist/constants.cjs +1 -1
  34. package/dist/constants.js +1 -1
  35. package/dist/eventcatalog-config-file-utils.cjs +34 -41
  36. package/dist/eventcatalog-config-file-utils.d.cts +56 -58
  37. package/dist/eventcatalog-config-file-utils.d.ts +56 -58
  38. package/dist/eventcatalog-config-file-utils.js +1 -1
  39. package/dist/eventcatalog.cjs +531 -356
  40. package/dist/eventcatalog.config.d.cts +59 -1
  41. package/dist/eventcatalog.config.d.ts +59 -1
  42. package/dist/eventcatalog.js +63 -25
  43. package/dist/features.cjs +34 -41
  44. package/dist/features.js +2 -2
  45. package/dist/federation/federate.cjs +37 -44
  46. package/dist/federation/federate.js +2 -2
  47. package/dist/generate.cjs +37 -42
  48. package/dist/generate.js +4 -4
  49. package/dist/resolve-catalog-dependencies.cjs +34 -41
  50. package/dist/resolve-catalog-dependencies.js +2 -2
  51. package/dist/runtime-dependencies/@eventcatalog__visualiser__studio-fonts.css.mjs +3 -0
  52. package/dist/runtime-dependencies/@eventcatalog__visualiser__studio.css.mjs +3 -0
  53. package/dist/runtime-dependencies/@eventcatalog__visualiser__studio.mjs +3 -0
  54. package/dist/runtime-dependencies/@hocuspocus__provider.mjs +3 -0
  55. package/dist/runtime-dependencies/@hocuspocus__server.mjs +3 -0
  56. package/dist/runtime-dependencies/@mcp-b__webmcp-local-relay.mjs +3 -0
  57. package/dist/runtime-dependencies/@mcp-b__webmcp-polyfill.mjs +3 -0
  58. package/dist/runtime-dependencies/htmlparser2.mjs +3 -0
  59. package/dist/runtime-dependencies/manifest.json +13 -1
  60. package/dist/runtime-dependencies/react-hotkeys-hook.mjs +3 -0
  61. package/dist/runtime-dependencies/react-syntax-highlighter__dist__cjs__create-element.js.mjs +3 -0
  62. package/dist/runtime-dependencies/y-protocols.mjs +3 -0
  63. package/dist/runtime-dependencies/y-protocols__awareness.mjs +3 -0
  64. package/dist/runtime-dependencies/yjs.mjs +3 -0
  65. package/dist/utils/child-process-signals.cjs +53 -0
  66. package/dist/utils/child-process-signals.d.cts +19 -0
  67. package/dist/utils/child-process-signals.d.ts +19 -0
  68. package/dist/utils/child-process-signals.js +6 -0
  69. package/dist/utils/cli-logger.cjs +1 -1
  70. package/dist/utils/cli-logger.js +2 -2
  71. package/dist/utils/license-status.cjs +78 -45
  72. package/dist/utils/license-status.d.cts +15 -5
  73. package/dist/utils/license-status.d.ts +15 -5
  74. package/dist/utils/license-status.js +1 -1
  75. package/eventcatalog/astro.config.mjs +23 -1
  76. package/eventcatalog/integrations/config-loader.mjs +56 -58
  77. package/eventcatalog/integrations/skip-startup-reload.mjs +31 -0
  78. package/eventcatalog/integrations/stable-dependency-order.mjs +29 -0
  79. package/eventcatalog/integrations/storage.mjs +20 -0
  80. package/eventcatalog/integrations/studio-server.mjs +32 -0
  81. package/eventcatalog/src/components/CatalogGraph/CatalogForceGraph.tsx +39 -0
  82. package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +2 -0
  83. package/eventcatalog/src/components/Header.astro +3 -3
  84. package/eventcatalog/src/components/Homepage/CatalogDashboard.astro +342 -0
  85. package/eventcatalog/src/components/MDX/NodeGraph/AstroNodeGraph.tsx +60 -4
  86. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +5 -1
  87. package/eventcatalog/src/components/NavigationProgress.astro +7 -0
  88. package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +2 -0
  89. package/eventcatalog/src/components/SchemaExplorer/ExamplesViewer.tsx +2 -0
  90. package/eventcatalog/src/components/SchemaExplorer/SchemaCodeModal.tsx +2 -0
  91. package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +2 -0
  92. package/eventcatalog/src/components/Search/SearchDataLoader.astro +16 -28
  93. package/eventcatalog/src/components/Search/SearchModal.tsx +2 -10
  94. package/eventcatalog/src/components/Search/search-utils.ts +26 -0
  95. package/eventcatalog/src/components/Seo.astro +5 -1
  96. package/eventcatalog/src/components/Settings/AssistantSettingsForm.tsx +53 -48
  97. package/eventcatalog/src/components/Settings/GeneralSettingsForm.tsx +183 -178
  98. package/eventcatalog/src/components/Settings/LicenseSettings.tsx +140 -31
  99. package/eventcatalog/src/components/Settings/LlmAccessSettingsForm.tsx +40 -35
  100. package/eventcatalog/src/components/Settings/LogoUpload.tsx +1 -1
  101. package/eventcatalog/src/components/Settings/McpSettingsForm.tsx +9 -8
  102. package/eventcatalog/src/components/Settings/Row.tsx +7 -4
  103. package/eventcatalog/src/components/Settings/SettingsShared.tsx +3 -3
  104. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +53 -83
  105. package/eventcatalog/src/components/SideNav/NestedSideBar/storage.ts +2 -0
  106. package/eventcatalog/src/components/SideNav/NestedSideBar/utils.ts +131 -0
  107. package/eventcatalog/src/components/SideNav/SideNav.astro +9 -1
  108. package/eventcatalog/src/components/TrialBadge.astro +20 -9
  109. package/eventcatalog/src/content.config.ts +19 -2
  110. package/eventcatalog/src/features/auth/error.astro +4 -2
  111. package/eventcatalog/src/features/auth/login.astro +2 -1
  112. package/eventcatalog/src/features/auth/middleware/middleware-auth.ts +10 -0
  113. package/eventcatalog/src/features/auth/unauthorized.astro +1 -1
  114. package/eventcatalog/src/features/fields/pages/fields.astro +1 -1
  115. package/eventcatalog/src/features/integrations/eventcatalog-features.ts +34 -1
  116. package/eventcatalog/src/features/mcp/apps/architecture-diagram/node-chat.tsx +6 -45
  117. package/eventcatalog/src/features/mcp/apps/canvas/shared.ts +47 -0
  118. package/eventcatalog/src/features/mcp/apps/canvas/styles.css +24 -0
  119. package/eventcatalog/src/features/mcp/apps/canvas/view.tsx +278 -0
  120. package/eventcatalog/src/features/mcp/apps/generated/canvas.html +272 -0
  121. package/eventcatalog/src/features/mcp/apps/generated/viewer.html +65 -65
  122. package/eventcatalog/src/features/mcp/apps/shared/app-view.tsx +2 -1
  123. package/eventcatalog/src/features/mcp/mcp-apps.ts +6 -3
  124. package/eventcatalog/src/features/mcp/mcp-server.ts +36 -5
  125. package/eventcatalog/src/features/server/start.ts +25 -0
  126. package/eventcatalog/src/features/storage/database.ts +88 -0
  127. package/eventcatalog/src/features/storage/migrations.ts +85 -0
  128. package/eventcatalog/src/features/storage/sql/database.sql +5 -0
  129. package/eventcatalog/src/features/storage/sql/migrations/0001-studio-canvases.sql +12 -0
  130. package/eventcatalog/src/features/storage/sql/migrations.sql +15 -0
  131. package/eventcatalog/src/features/storage/sql.ts +28 -0
  132. package/eventcatalog/src/features/studio/agent-choreography.ts +384 -0
  133. package/eventcatalog/src/features/studio/agents.ts +28 -0
  134. package/eventcatalog/src/features/studio/api/canvases-api.ts +219 -0
  135. package/eventcatalog/src/features/studio/api/canvases.ts +25 -0
  136. package/eventcatalog/src/features/studio/api/client.ts +40 -0
  137. package/eventcatalog/src/features/studio/canvas-actions.ts +540 -0
  138. package/eventcatalog/src/features/studio/canvas-preview.ts +52 -0
  139. package/eventcatalog/src/features/studio/catalog-resources.ts +155 -0
  140. package/eventcatalog/src/features/studio/catalog.ts +173 -0
  141. package/eventcatalog/src/features/studio/components/CanvasHeader.tsx +518 -0
  142. package/eventcatalog/src/features/studio/components/CanvasStatusMenu.tsx +118 -0
  143. package/eventcatalog/src/features/studio/components/ConnectAgentDialog.tsx +434 -0
  144. package/eventcatalog/src/features/studio/components/ConnectionsChooser.tsx +143 -0
  145. package/eventcatalog/src/features/studio/components/DeleteCanvasDialog.tsx +65 -0
  146. package/eventcatalog/src/features/studio/components/Dialog.tsx +60 -0
  147. package/eventcatalog/src/features/studio/components/JoinForm.tsx +109 -0
  148. package/eventcatalog/src/features/studio/components/LeftPanel.tsx +206 -0
  149. package/eventcatalog/src/features/studio/components/NodeDetails.tsx +183 -0
  150. package/eventcatalog/src/features/studio/components/OpenInStudioDialog.tsx +79 -0
  151. package/eventcatalog/src/features/studio/components/Presence.tsx +293 -0
  152. package/eventcatalog/src/features/studio/components/ShareDialog.tsx +396 -0
  153. package/eventcatalog/src/features/studio/components/ShortcutsDialog.tsx +41 -0
  154. package/eventcatalog/src/features/studio/components/StudioDesigner.tsx +2091 -0
  155. package/eventcatalog/src/features/studio/components/StudioNotice.astro +24 -0
  156. package/eventcatalog/src/features/studio/document-name.ts +7 -0
  157. package/eventcatalog/src/features/studio/from-visualiser.ts +138 -0
  158. package/eventcatalog/src/features/studio/hooks/presence-sender.ts +63 -0
  159. package/eventcatalog/src/features/studio/hooks/presence-store.ts +119 -0
  160. package/eventcatalog/src/features/studio/hooks/use-canvas-webmcp.ts +353 -0
  161. package/eventcatalog/src/features/studio/hooks/use-comments.ts +54 -0
  162. package/eventcatalog/src/features/studio/hooks/use-follow-camera.ts +109 -0
  163. package/eventcatalog/src/features/studio/hooks/use-studio-flow.ts +782 -0
  164. package/eventcatalog/src/features/studio/identity.ts +59 -0
  165. package/eventcatalog/src/features/studio/limits.ts +2 -0
  166. package/eventcatalog/src/features/studio/pages/[id].astro +69 -0
  167. package/eventcatalog/src/features/studio/pages/index.astro +784 -0
  168. package/eventcatalog/src/features/studio/pages/webmcp-relay.ts +27 -0
  169. package/eventcatalog/src/features/studio/server/canvas-mcp.ts +495 -0
  170. package/eventcatalog/src/features/studio/server/runtime.ts +498 -0
  171. package/eventcatalog/src/features/studio/server/sign-in.ts +18 -0
  172. package/eventcatalog/src/features/studio/server/sql/storage.sql +18 -0
  173. package/eventcatalog/src/features/studio/server/storage.ts +90 -0
  174. package/eventcatalog/src/features/studio/server/tool-icons.ts +30 -0
  175. package/eventcatalog/src/features/studio/tool-sync.ts +75 -0
  176. package/eventcatalog/src/layouts/BaseLayout.astro +4 -4
  177. package/eventcatalog/src/layouts/SettingsLayout.astro +2 -2
  178. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +13 -5
  179. package/eventcatalog/src/pages/_index.astro +5 -342
  180. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.spec.ts +2 -3
  181. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.ts +0 -7
  182. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +3 -1
  183. package/eventcatalog/src/pages/diagrams/[id]/[version]/_index.data.ts +0 -7
  184. package/eventcatalog/src/pages/diagrams/[id]/[version]/embed.astro +1 -0
  185. package/eventcatalog/src/pages/diagrams/[id]/[version]/index.astro +1 -0
  186. package/eventcatalog/src/pages/directory/[type]/_index.data.ts +5 -7
  187. package/eventcatalog/src/pages/directory/[type]/index.astro +3 -1
  188. package/eventcatalog/src/pages/discover/[type]/_index.data.ts +5 -7
  189. package/eventcatalog/src/pages/discover/[type]/index.astro +3 -1
  190. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/_index.data.ts +0 -7
  191. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/index.astro +1 -0
  192. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/_index.data.ts +0 -7
  193. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/index.astro +1 -0
  194. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +0 -7
  195. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/[filename].astro +3 -1
  196. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/_[filename].data.ts +0 -7
  197. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/_index.data.ts +0 -7
  198. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/index.astro +1 -0
  199. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/[...filename].astro +1 -0
  200. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/_[...filename].data.ts +0 -4
  201. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/field-lineage/_index.data.ts +0 -7
  202. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/field-lineage/index.astro +1 -0
  203. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +3 -1
  204. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/_[filename].data.ts +0 -7
  205. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +1 -0
  206. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_index.data.spec.ts +2 -3
  207. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_index.data.ts +0 -7
  208. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/index.astro +1 -0
  209. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/[filename].astro +1 -0
  210. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/_[filename].data.ts +0 -7
  211. package/eventcatalog/src/pages/docs/[type]/[id]/_index.data.ts +0 -7
  212. package/eventcatalog/src/pages/docs/[type]/[id]/index.astro +2 -1
  213. package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/_index.data.ts +0 -7
  214. package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/index.astro +1 -0
  215. package/eventcatalog/src/pages/docs/[type]/[id]/language/_index.data.ts +0 -7
  216. package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +1 -0
  217. package/eventcatalog/src/pages/docs/custom/[...path]/_index.data.ts +0 -7
  218. package/eventcatalog/src/pages/docs/custom/[...path]/index.astro +1 -0
  219. package/eventcatalog/src/pages/docs/services/[id]/[docType]/[docId]/index.astro +1 -1
  220. package/eventcatalog/src/pages/docs/services/[id]/[version]/resources/index.astro +1 -0
  221. package/eventcatalog/src/pages/docs/services/[id]/asyncapi/[filename].astro +1 -1
  222. package/eventcatalog/src/pages/docs/services/[id]/changelog/index.astro +1 -1
  223. package/eventcatalog/src/pages/docs/services/[id]/graphql/[filename].astro +1 -1
  224. package/eventcatalog/src/pages/docs/services/[id]/spec/[filename].astro +1 -1
  225. package/eventcatalog/src/pages/docs/teams/[id]/_index.data.ts +0 -7
  226. package/eventcatalog/src/pages/docs/teams/[id]/index.astro +1 -0
  227. package/eventcatalog/src/pages/docs/users/[id]/_index.data.ts +0 -7
  228. package/eventcatalog/src/pages/docs/users/[id]/index.astro +1 -0
  229. package/eventcatalog/src/pages/index.astro +4 -2
  230. package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/_index.data.ts +0 -7
  231. package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/index.astro +1 -0
  232. package/eventcatalog/src/pages/schemas/explorer/_index.data.ts +0 -7
  233. package/eventcatalog/src/pages/schemas/explorer/index.astro +4 -2
  234. package/eventcatalog/src/pages/settings/license.astro +9 -1
  235. package/eventcatalog/src/pages/triggers/[type]/[id]/[version]/_index.data.ts +0 -7
  236. package/eventcatalog/src/pages/triggers/[type]/[id]/[version]/index.astro +1 -0
  237. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/_index.data.ts +0 -7
  238. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/_index.data.ts +0 -7
  239. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/index.astro +1 -0
  240. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/entity-map/_index.data.ts +0 -7
  241. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/entity-map/index.astro +1 -0
  242. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/index.astro +1 -0
  243. package/eventcatalog/src/pages/visualiser/[type]/[id]/_index.data.ts +38 -0
  244. package/eventcatalog/src/pages/visualiser/[type]/[id]/index.astro +18 -41
  245. package/eventcatalog/src/pages/visualiser/designs/[id]/_index.data.ts +0 -7
  246. package/eventcatalog/src/pages/visualiser/designs/[id]/index.astro +2 -1
  247. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/_index.data.ts +0 -7
  248. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/index.astro +1 -0
  249. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/index.astro +1 -0
  250. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/systems-context/_index.data.ts +0 -7
  251. package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/systems-context/index.astro +1 -0
  252. package/eventcatalog/src/pages/visualiser/systems/[id]/[version]/context/_index.data.ts +0 -7
  253. package/eventcatalog/src/pages/visualiser/systems/[id]/[version]/context/index.astro +1 -0
  254. package/eventcatalog/src/pages/visualiser/systems/[id]/[version]/index.astro +1 -0
  255. package/eventcatalog/src/types/react-syntax-highlighter.d.ts +10 -0
  256. package/eventcatalog/src/utils/collections/util.ts +8 -4
  257. package/eventcatalog/src/utils/feature.ts +8 -0
  258. package/eventcatalog/src/utils/git.ts +22 -5
  259. package/eventcatalog/src/utils/homepage-dashboard.ts +143 -0
  260. package/eventcatalog/src/utils/license.ts +1 -1
  261. package/eventcatalog/src/utils/link-validation.ts +25 -19
  262. package/eventcatalog/src/utils/node-graphs/domain-entity-map.ts +3 -1
  263. package/eventcatalog/src/utils/node-graphs/domain-levels-node-graph.ts +3 -3
  264. package/eventcatalog/src/utils/node-graphs/export-mermaid.ts +0 -2
  265. package/eventcatalog/src/utils/node-graphs/field-node-graph.ts +4 -2
  266. package/eventcatalog/src/utils/node-graphs/layout-node-graph.ts +20 -1
  267. package/eventcatalog/src/utils/node-graphs/node-resource.ts +67 -0
  268. package/eventcatalog/src/utils/node-graphs/systems-node-graph.ts +1 -1
  269. package/eventcatalog/src/utils/page-loaders/hybrid-page.ts +4 -15
  270. package/eventcatalog/src/utils/page-title.ts +15 -0
  271. package/eventcatalog/src/utils/syntax-highlighter-renderer.ts +64 -0
  272. package/eventcatalog/src/utils/trial.ts +14 -1
  273. package/package.json +19 -13
  274. package/dist/runtime-dependencies/shelljs.mjs +0 -3
  275. package/eventcatalog/public/studio-bg.png +0 -0
  276. package/eventcatalog/src/components/Studio/StudioPageModal.tsx +0 -150
  277. package/eventcatalog/src/pages/studio.astro +0 -316
@@ -0,0 +1,24 @@
1
+ ---
2
+ /** A message in place of Studio (e.g. a deleted canvas, or Studio not being available), with a way back */
3
+ type Props = { title: string; message: string; href?: string; linkText?: string };
4
+ const { title, message, href, linkText } = Astro.props;
5
+ ---
6
+
7
+ <div class="flex h-full items-center justify-center p-4">
8
+ <div
9
+ class="max-w-md space-y-2 rounded-xl border p-5 text-center shadow-xl bg-[rgb(var(--ec-card-bg))] border-[rgb(var(--ec-page-border))]"
10
+ >
11
+ <h1 class="text-base font-semibold text-[rgb(var(--ec-page-text))]">{title}</h1>
12
+ <p class="text-sm text-[rgb(var(--ec-page-text-muted))]">{message}</p>
13
+ {
14
+ href && linkText && (
15
+ <a
16
+ href={href}
17
+ class="mt-2 inline-flex rounded-lg px-4 py-2 text-sm font-medium bg-[rgb(var(--ec-button-bg))] text-[rgb(var(--ec-button-text))] hover:bg-[rgb(var(--ec-button-bg-hover))]"
18
+ >
19
+ {linkText}
20
+ </a>
21
+ )
22
+ }
23
+ </div>
24
+ </div>
@@ -0,0 +1,7 @@
1
+ // Canvases' documents on the collaboration server are named `design:<canvasId>` (scripts and peers join by it)
2
+ const DOCUMENT_PREFIX = 'design:';
3
+ export const canvasDocumentName = (canvasId: string) => `${DOCUMENT_PREFIX}${canvasId}`;
4
+ export const canvasIdFromDocumentName = (name: string) =>
5
+ name.startsWith(DOCUMENT_PREFIX) ? name.slice(DOCUMENT_PREFIX.length) : undefined;
6
+ /** Why the collaboration server refuses a canvas that was deleted (told to anyone who still has it open) */
7
+ export const CANVAS_DELETED_REASON = 'canvas-deleted';
@@ -0,0 +1,138 @@
1
+ import type { Edge, Node } from '@xyflow/react';
2
+ import type { VisualiserSnapshot } from '@eventcatalog/visualiser';
3
+ import { getNodeCatalogResource } from '@utils/node-graphs/node-resource';
4
+ import type { DiagramLevelsSpec, NewCanvasEdges, NodeSpecs } from './canvas-actions';
5
+ import { DEFAULT_NODE_SIZE, isCatalogCollection, isGroupType } from '@eventcatalog/visualiser/studio';
6
+
7
+ /**
8
+ * A canvas started from a diagram (the visualiser's "Open in Studio"): the catalog resources and actors it shows,
9
+ * where they are on it (the same top-left corners), in the domains and systems they're shown in, and the
10
+ * connections between them, drawn along the same routes. Other nodes (e.g. flow steps, users, entities) are left
11
+ * out, with their connections. Its levels 1 and 2 come as the diagram shows them, for Studio to show them the same.
12
+ */
13
+
14
+ // Actors (people and roles) aren't catalog resources: they go on the canvas as Studio's actors, by the name shown
15
+ const ACTOR_TYPES = new Set(['context-actor', 'actor']);
16
+ const actorNameOf = (node: VisualiserSnapshot['nodes'][number]) =>
17
+ node.type && ACTOR_TYPES.has(node.type) && typeof node.data.name === 'string' ? node.data.name : undefined;
18
+
19
+ export const canvasFromVisualiser = (
20
+ snapshot: VisualiserSnapshot
21
+ ): { nodes: NodeSpecs; edges: NewCanvasEdges; levels: DiagramLevelsSpec } => {
22
+ const byId = new Map(snapshot.nodes.map((node) => [node.id, node]));
23
+ const depthOf = (node: VisualiserSnapshot['nodes'][number]): number => {
24
+ const parent = node.parentId ? byId.get(node.parentId) : undefined;
25
+ return parent ? 1 + depthOf(parent) : 0;
26
+ };
27
+
28
+ const containers = new Set<string>();
29
+ const parentOf = new Map<string, string>();
30
+ const nodes = [...snapshot.nodes]
31
+ // Containers before what's in them (specs refer to containers given before them)
32
+ .sort((a, b) => depthOf(a) - depthOf(b))
33
+ .flatMap((node) => {
34
+ const resource = getNodeCatalogResource(node);
35
+ const fromCatalog =
36
+ resource && isCatalogCollection(resource.collection) ? { collection: resource.collection, id: resource.id } : undefined;
37
+ const actorName = actorNameOf(node);
38
+ if (!fromCatalog && !actorName) return [];
39
+ // Domains and systems shown as boxes are containers on the canvas
40
+ const container = isGroupType(node.type);
41
+ // In the innermost container shown that's on the canvas
42
+ let inside: string | undefined;
43
+ for (let parent = node.parentId; parent && !inside; parent = byId.get(parent)?.parentId) {
44
+ if (containers.has(parent)) inside = parent;
45
+ }
46
+ if (container) containers.add(node.id);
47
+ if (inside) parentOf.set(node.id, inside);
48
+ // Where it is: its top-left corner, as cards are put by their centre at the size they're created at (and
49
+ // render at the size they need from there), and containers at their size
50
+ const left = node.center.x - node.width / 2;
51
+ const top = node.center.y - node.height / 2;
52
+ return [
53
+ {
54
+ ref: node.id,
55
+ ...(fromCatalog ? { resource: fromCatalog } : { type: 'actor', name: actorName }),
56
+ ...(container
57
+ ? { x: node.center.x, y: node.center.y, container: true, width: node.width, height: node.height }
58
+ : { x: left + DEFAULT_NODE_SIZE.width / 2, y: top + DEFAULT_NODE_SIZE.height / 2 }),
59
+ ...(inside && { inside }),
60
+ },
61
+ ];
62
+ });
63
+
64
+ // Connections between nodes on the canvas, not between a container and what's in it (it holds it instead)
65
+ const onCanvas = new Set(nodes.map((node) => node.ref));
66
+ const isInside = (nodeId: string, containerId: string) => {
67
+ for (let parent = parentOf.get(nodeId); parent; parent = parentOf.get(parent)) if (parent === containerId) return true;
68
+ return false;
69
+ };
70
+ const edges = snapshot.edges
71
+ .filter(({ source, target }) => onCanvas.has(source) && onCanvas.has(target) && source !== target)
72
+ .filter(({ source, target }) => !isInside(source, target) && !isInside(target, source))
73
+ .map(({ source, target, label, route }) => ({ from: source, to: target, ...(label && { label }), ...(route && { route }) }));
74
+
75
+ const levels = {
76
+ ...(snapshot.levels[1] && { 1: levelFromVisualiser(snapshot.levels[1], nodes) }),
77
+ ...(snapshot.levels[2] && { 2: levelFromVisualiser(snapshot.levels[2], nodes) }),
78
+ ...(Object.keys(snapshot.unavailableLevels).length > 0 && { unavailable: snapshot.unavailableLevels }),
79
+ };
80
+ return { nodes, edges, levels };
81
+ };
82
+
83
+ const sameResource = (a?: { collection: string; id: string }, b?: { collection: string; id: string }) =>
84
+ !!a && !!b && a.collection === b.collection && a.id === b.id;
85
+ const sizeOf = (node: Node) => ({
86
+ width: typeof node.style?.width === 'number' ? node.style.width : (node.width ?? node.measured?.width),
87
+ height: typeof node.style?.height === 'number' ? node.style.height : (node.height ?? node.measured?.height),
88
+ });
89
+
90
+ /**
91
+ * One of the diagram's levels, as it shows it: each node stands for the canvas node with the same id (on level 3),
92
+ * or the same catalog resource (e.g. a system shown as a card on level 1 and as a container on level 3), so moving
93
+ * between levels glides from one to the other. Nodes only on the level keep their own id.
94
+ */
95
+ const levelFromVisualiser = (level: { nodes: Node[]; edges: Edge[] }, specs: NodeSpecs) => {
96
+ const used = new Set<string>();
97
+ const refs = new Map<string, string>();
98
+ for (const node of level.nodes) {
99
+ const resource = getNodeCatalogResource(node);
100
+ const ref = [
101
+ specs.find((spec) => spec.ref === node.id)?.ref,
102
+ specs.find((spec) => sameResource(spec.resource, resource))?.ref,
103
+ ].find((candidate) => candidate && !used.has(candidate));
104
+ refs.set(node.id, ref ?? node.id);
105
+ used.add(ref ?? node.id);
106
+ }
107
+ return {
108
+ nodes: level.nodes.map((node) => {
109
+ const { width, height } = sizeOf(node);
110
+ // The diagram marks the resource whose page it's on ("Viewing"): a canvas isn't on one
111
+ const { isFocused: _, ...data } = node.data as Record<string, unknown>;
112
+ return {
113
+ ref: refs.get(node.id)!,
114
+ type: node.type ?? 'default',
115
+ data,
116
+ ...(node.parentId && refs.has(node.parentId) && { parent: refs.get(node.parentId) }),
117
+ x: node.position.x,
118
+ y: node.position.y,
119
+ ...(isGroupType(node.type) && width && height && { width, height }),
120
+ };
121
+ }),
122
+ edges: level.edges.flatMap((edge) => {
123
+ const [from, to] = [refs.get(edge.source), refs.get(edge.target)];
124
+ if (!from || !to) return [];
125
+ return [
126
+ {
127
+ from,
128
+ to,
129
+ ...(typeof edge.label === 'string' && { label: edge.label }),
130
+ ...(edge.type && { type: edge.type }),
131
+ ...(edge.style && { style: edge.style as Record<string, unknown> }),
132
+ ...(edge.markerEnd && { markerEnd: edge.markerEnd }),
133
+ ...(edge.data && { data: edge.data }),
134
+ },
135
+ ];
136
+ }),
137
+ };
138
+ };
@@ -0,0 +1,63 @@
1
+ import type { Awareness } from 'y-protocols/awareness';
2
+
3
+ /** About 30 times a second, like Excalidraw and tldraw: smooth for others, without a message on every mousemove */
4
+ export const PRESENCE_INTERVAL_MS = 33;
5
+ /**
6
+ * Once a second when nobody else is on the canvas (people or agents), as tldraw does: nobody sees it, and the
7
+ * collaboration server sends every one back. Someone arriving gets what's waiting straight away.
8
+ */
9
+ export const ALONE_INTERVAL_MS = 1000;
10
+
11
+ export type PresenceSender = {
12
+ /** Share these fields of our presence: straight away if nothing was sent lately, otherwise with the next send */
13
+ send: (fields: Record<string, unknown>) => void;
14
+ /** Send anything waiting now (e.g. a node dropped, so the drop isn't behind the drag) */
15
+ flush: () => void;
16
+ destroy: () => void;
17
+ };
18
+
19
+ /**
20
+ * Our presence (pointer, nodes being dragged, selection, view), sent to everyone at most every interval. Awareness
21
+ * sends the whole state on every change, so fields changed together (the pointer and the node it drags) go in one
22
+ * message, and the latest value always goes out (the pointer stops where it really stopped).
23
+ */
24
+ export function createPresenceSender(
25
+ awareness: Awareness,
26
+ intervalMs = PRESENCE_INTERVAL_MS,
27
+ aloneIntervalMs = ALONE_INTERVAL_MS
28
+ ): PresenceSender {
29
+ let pending: Record<string, unknown> | undefined;
30
+ let timer: ReturnType<typeof setTimeout> | undefined;
31
+ let lastSent = -Infinity;
32
+ const alone = () => awareness.getStates().size <= 1;
33
+
34
+ const flush = () => {
35
+ clearTimeout(timer);
36
+ timer = undefined;
37
+ if (!pending) return;
38
+ const fields = pending;
39
+ pending = undefined;
40
+ lastSent = performance.now();
41
+ awareness.setLocalState({ ...awareness.getLocalState(), ...fields });
42
+ };
43
+ // Someone arrived: they get what's waiting now, not at the next (slower) send
44
+ const onChange = ({ added }: { added: number[] }) => {
45
+ if (pending && added.some((id) => id !== awareness.clientID)) flush();
46
+ };
47
+ awareness.on('change', onChange);
48
+
49
+ return {
50
+ send: (fields) => {
51
+ pending = { ...pending, ...fields };
52
+ if (timer) return;
53
+ const interval = alone() ? aloneIntervalMs : intervalMs;
54
+ timer = setTimeout(flush, Math.max(0, interval - (performance.now() - lastSent)));
55
+ },
56
+ flush,
57
+ destroy: () => {
58
+ clearTimeout(timer);
59
+ pending = undefined;
60
+ awareness.off('change', onChange);
61
+ },
62
+ };
63
+ }
@@ -0,0 +1,119 @@
1
+ import { useSyncExternalStore } from 'react';
2
+ import type { XYPosition } from '@xyflow/react';
3
+ import type { Awareness } from 'y-protocols/awareness';
4
+
5
+ export type Peer = {
6
+ clientId: number;
7
+ name: string;
8
+ color: string;
9
+ /** A picture from their sign-in provider (SSO), shown over their initials */
10
+ picture?: string;
11
+ /** An AI agent (joined over MCP, or working through this tab with WebMCP), rather than a person */
12
+ agent?: boolean;
13
+ /** What an agent is doing, e.g. "Adding OrderService" */
14
+ activity?: string;
15
+ pointer: XYPosition | null;
16
+ selection: string[];
17
+ /** What they're looking at: the canvas position in the middle of their screen, and their zoom */
18
+ viewport?: { x: number; y: number; zoom: number };
19
+ /** Where a connection they're dragging starts (agents show theirs) */
20
+ connecting?: XYPosition | null;
21
+ };
22
+
23
+ type Listener = () => void;
24
+
25
+ /**
26
+ * Who's on a canvas and what they're doing (pointers, selections, views), from Yjs awareness, kept out of React
27
+ * state: only the components that draw presence subscribe, so a pointer moving doesn't re-render the canvas.
28
+ * Our own pointer, selection and view are sent to others but don't notify here (nothing here draws them).
29
+ */
30
+ export type PresenceStore = {
31
+ subscribe: (listener: Listener) => () => void;
32
+ /** Everyone, including us and an agent working through this tab. Changes whenever anyone does anything. */
33
+ getPeers: () => Peer[];
34
+ /**
35
+ * Everyone, for lists of who's here: changes only when people join, leave or rename, or agents change what
36
+ * they're doing (pointers and views in it may be out of date: read getPeers() for those)
37
+ */
38
+ getPeople: () => Peer[];
39
+ /** Show an agent working through this tab (WebMCP), which only this tab sees */
40
+ setLocalAgent: (agent: Peer | null) => void;
41
+ destroy: () => void;
42
+ };
43
+
44
+ const EMPTY: Peer[] = [];
45
+
46
+ const toPeers = (awareness: Awareness): Peer[] => {
47
+ const peers: Peer[] = [];
48
+ awareness.getStates().forEach((state, clientId) => {
49
+ if (!state.user) return;
50
+ peers.push({
51
+ clientId,
52
+ ...state.user,
53
+ activity: state.activity,
54
+ pointer: state.pointer ?? null,
55
+ selection: state.selection ?? [],
56
+ viewport: state.viewport,
57
+ connecting: state.connecting,
58
+ });
59
+ });
60
+ return peers;
61
+ };
62
+
63
+ // What getPeople() changes on: who people are, what agents are doing, and whether they're on the canvas (a pointer)
64
+ const identity = (peer: Peer) =>
65
+ [peer.clientId, peer.name, peer.color, peer.picture ?? '', peer.agent ? 1 : 0, peer.activity ?? '', peer.pointer ? 1 : 0].join(
66
+ '|'
67
+ );
68
+
69
+ export function createPresenceStore(awareness: Awareness): PresenceStore {
70
+ const listeners = new Set<Listener>();
71
+ let remote = toPeers(awareness);
72
+ let localAgent: Peer | null = null;
73
+ let peers = remote;
74
+ let people = remote;
75
+ let ownUser = JSON.stringify(awareness.getLocalState()?.user ?? null);
76
+
77
+ const update = () => {
78
+ peers = localAgent ? [...remote, localAgent] : remote;
79
+ const nextPeople = peers.length ? peers : EMPTY;
80
+ if (nextPeople.map(identity).join() !== people.map(identity).join()) people = nextPeople;
81
+ listeners.forEach((listener) => listener());
82
+ };
83
+
84
+ const onChange = ({ added, updated, removed }: { added: number[]; updated: number[]; removed: number[] }) => {
85
+ const others = [...added, ...updated, ...removed].some((id) => id !== awareness.clientID);
86
+ const user = JSON.stringify(awareness.getLocalState()?.user ?? null);
87
+ // Only our own pointer, selection or view changed: nothing shown here changed
88
+ if (!others && user === ownUser) return;
89
+ ownUser = user;
90
+ remote = toPeers(awareness);
91
+ update();
92
+ };
93
+ awareness.on('change', onChange);
94
+
95
+ return {
96
+ subscribe: (listener) => {
97
+ listeners.add(listener);
98
+ return () => listeners.delete(listener);
99
+ },
100
+ getPeers: () => peers,
101
+ getPeople: () => people,
102
+ setLocalAgent: (agent) => {
103
+ localAgent = agent;
104
+ update();
105
+ },
106
+ destroy: () => {
107
+ awareness.off('change', onChange);
108
+ listeners.clear();
109
+ },
110
+ };
111
+ }
112
+
113
+ const noStore = { subscribe: () => () => {}, getPeers: () => EMPTY, getPeople: () => EMPTY };
114
+
115
+ /** Everyone on the canvas, re-rendering only when people join, leave, rename or start or stop working */
116
+ export const usePeople = (store: PresenceStore | null) => {
117
+ const source = store ?? noStore;
118
+ return useSyncExternalStore(source.subscribe, source.getPeople);
119
+ };