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

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-BH2F5WPN.js → chunk-2UCCTEIH.js} +1 -1
  21. package/dist/{chunk-RFIXKATC.js → chunk-4G7FSRPN.js} +35 -42
  22. package/dist/{chunk-TQLRG35T.js → chunk-AD7CL772.js} +78 -45
  23. package/dist/{chunk-NDJOAITI.js → chunk-BNSLGNQI.js} +9 -3
  24. package/dist/chunk-CA6WCQ2T.js +54 -0
  25. package/dist/chunk-CPW6G62V.js +17 -0
  26. package/dist/{chunk-MDWB5HLO.js → chunk-NKNOXQ62.js} +1 -1
  27. package/dist/{chunk-423S2KHN.js → chunk-OUEGYMBA.js} +4 -2
  28. package/dist/{chunk-3PKS2IIP.js → chunk-OX6TSVHY.js} +1 -1
  29. package/dist/{chunk-36RYTYT5.js → chunk-PD7KTSYN.js} +1 -1
  30. package/dist/chunk-S5GWAUG5.js +29 -0
  31. package/dist/{chunk-GYIVJXOJ.js → chunk-WADZVNCA.js} +1 -1
  32. package/dist/{chunk-6OC46WQJ.js → chunk-XRFL642Z.js} +66 -35
  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,75 @@
1
+ import * as Y from 'yjs';
2
+ import { applyAwarenessUpdate, encodeAwarenessUpdate, type Awareness } from 'y-protocols/awareness';
3
+ import type { Status } from './hooks/use-studio-flow';
4
+
5
+ /**
6
+ * Syncing a canvas through MCP tool calls, for when a view can't open the collaboration WebSocket
7
+ * (e.g. an MCP App inside a chat whose sandbox blocks it). Each call sends the server what it doesn't have
8
+ * yet (by its state vector) and our presence, and gets back what we don't have and everyone's presence.
9
+ * Slower than the WebSocket (it polls), but works wherever the chat can call the MCP server's tools.
10
+ */
11
+
12
+ export type SyncRequest = {
13
+ canvasId: string;
14
+ clientId: number;
15
+ /** What we have, so the server sends only what we're missing */
16
+ stateVector: string;
17
+ /** What we have that the server doesn't (as of its last state vector) */
18
+ update: string;
19
+ /** Our presence (name, pointer, selection...) */
20
+ awareness: string;
21
+ };
22
+ export type SyncResponse = { update: string; stateVector: string; awareness?: string };
23
+ export type ToolSync = (request: SyncRequest) => Promise<SyncResponse>;
24
+
25
+ // Binary Yjs updates travel as base64 in tool arguments and results (atob/btoa exist in browsers and Node)
26
+ export const toBase64 = (bytes: Uint8Array) => {
27
+ let text = '';
28
+ for (let i = 0; i < bytes.length; i += 0x8000) text += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
29
+ return btoa(text);
30
+ };
31
+ export const fromBase64 = (text: string) => Uint8Array.from(atob(text), (char) => char.charCodeAt(0));
32
+
33
+ /** Applied with this origin, so undo (which tracks local changes) leaves other people's changes alone */
34
+ const REMOTE = Symbol('tool-sync');
35
+
36
+ export function startToolSync(
37
+ doc: Y.Doc,
38
+ awareness: Awareness,
39
+ {
40
+ canvasId,
41
+ sync,
42
+ onStatus,
43
+ intervalMs = 1000,
44
+ }: { canvasId: string; sync: ToolSync; onStatus: (status: Status) => void; intervalMs?: number }
45
+ ) {
46
+ let serverVector: Uint8Array | undefined;
47
+ let stopped = false;
48
+ let timer: ReturnType<typeof setTimeout> | undefined;
49
+
50
+ const tick = async () => {
51
+ try {
52
+ const response = await sync({
53
+ canvasId,
54
+ clientId: doc.clientID,
55
+ stateVector: toBase64(Y.encodeStateVector(doc)),
56
+ update: toBase64(Y.encodeStateAsUpdate(doc, serverVector)),
57
+ awareness: toBase64(encodeAwarenessUpdate(awareness, [doc.clientID])),
58
+ });
59
+ if (stopped) return;
60
+ Y.applyUpdate(doc, fromBase64(response.update), REMOTE);
61
+ serverVector = fromBase64(response.stateVector);
62
+ if (response.awareness) applyAwarenessUpdate(awareness, fromBase64(response.awareness), REMOTE);
63
+ onStatus('connected');
64
+ } catch {
65
+ if (!stopped) onStatus('disconnected');
66
+ }
67
+ if (!stopped) timer = setTimeout(tick, intervalMs);
68
+ };
69
+ void tick();
70
+
71
+ return () => {
72
+ stopped = true;
73
+ clearTimeout(timer);
74
+ };
75
+ }
@@ -6,12 +6,12 @@ import NavigationProgress from '../components/NavigationProgress.astro';
6
6
  import config from '@config';
7
7
 
8
8
  interface Props {
9
- title: string;
9
+ /** The page's own title; the catalog's title follows it (the homepage has none) */
10
+ title?: string;
10
11
  description?: string;
11
- ogTitle?: string;
12
12
  }
13
13
 
14
- const { title, description, ogTitle } = Astro.props;
14
+ const { title, description } = Astro.props;
15
15
  const catalogTheme = config.theme || 'default';
16
16
  ---
17
17
 
@@ -50,7 +50,7 @@ const catalogTheme = config.theme || 'default';
50
50
  document.addEventListener('astro:page-load', applyTheme);
51
51
  })();
52
52
  </script>
53
- <SEO title={title} description={description} ogTitle={ogTitle} />
53
+ <SEO title={title} description={description} />
54
54
  <AnalyticsHead />
55
55
  <slot name="head" />
56
56
  </head>
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  import VerticalSideBarLayout from './VerticalSideBarLayout.astro';
3
3
  import { buildUrl } from '@utils/url-builder';
4
- import { Settings, Server, Bot, FileText, BadgeCheck } from 'lucide-react';
4
+ import { Settings, Server, Bot, ScanText, BadgeCheck } from 'lucide-react';
5
5
 
6
6
  export type SettingsSection = 'general' | 'mcp' | 'assistant' | 'llm-access' | 'license';
7
7
 
@@ -55,7 +55,7 @@ const groups: SectionGroup[] = [
55
55
  id: 'llm-access',
56
56
  label: 'LLM Access',
57
57
  href: buildUrl('/settings/llm-access'),
58
- icon: FileText,
58
+ icon: ScanText,
59
59
  },
60
60
  ],
61
61
  },
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  interface Props {
3
- title: string;
3
+ /** The page's own title; the catalog's title follows it (the homepage has none) */
4
+ title?: string;
4
5
  sidebar?: boolean;
5
6
  description?: string;
6
7
  showNestedSideBar?: boolean;
@@ -52,7 +53,7 @@ import { buildUrl } from '@utils/url-builder';
52
53
  import { getQueries } from '@utils/collections/queries';
53
54
  import { getFirstNavigationPage } from '@features/custom-documentation/utils/custom-docs';
54
55
 
55
- import { isSSR } from '@utils/feature';
56
+ import { isCanvasEnabled, isSSR } from '@utils/feature';
56
57
 
57
58
  const customDocs = await getCollection('customPages');
58
59
  const firstCustomDocsPage = customDocs.length > 0 ? await getFirstNavigationPage() : null;
@@ -241,7 +242,7 @@ const builtinNavigationItems: Record<string, Omit<VerticalNavigationItem, 'curre
241
242
  '/studio': {
242
243
  id: '/studio',
243
244
  matchId: '/studio',
244
- label: 'EventCatalog Studio',
245
+ label: 'Studio',
245
246
  icon: 'SquareDashedMousePointer',
246
247
  href: buildUrl('/studio'),
247
248
  },
@@ -292,7 +293,12 @@ const builtinNavigationItems: Record<string, Omit<VerticalNavigationItem, 'curre
292
293
  const defaultNavigationGroups: NavigationGroupConfig[] = [
293
294
  {
294
295
  id: 'main',
295
- items: [{ id: '/' }, { id: '/docs/custom', visible: customDocs.length > 0 }],
296
+ items: [
297
+ { id: '/' },
298
+ { id: '/docs/custom', visible: customDocs.length > 0 },
299
+ // Collaborative canvases, where they're on
300
+ { id: '/studio', visible: isCanvasEnabled() },
301
+ ],
296
302
  },
297
303
  {
298
304
  id: 'browse',
@@ -371,6 +377,8 @@ const resolveNavigationItem = (item: NavigationItemConfig): VerticalNavigationIt
371
377
  if (item.visible === false) return null;
372
378
 
373
379
  const builtinId = navigationBuiltinAliases[item.id] || item.id;
380
+ // Studio is only there where canvases are on
381
+ if (builtinId === '/studio' && !item.href && !isCanvasEnabled()) return null;
374
382
  const builtin = builtinNavigationItems[builtinId];
375
383
  const href = item.href ? normalizeNavigationHref(item.href) : builtin?.href;
376
384
 
@@ -423,7 +431,7 @@ const { title, description, showNestedSideBar = true, showHeader = true, reduced
423
431
  const verticalNavAutoCollapsePaths = [buildUrl('/discover', true), buildUrl('/docs', true)];
424
432
  ---
425
433
 
426
- <BaseLayout title={`EventCatalog | ${title}`} description={description} ogTitle={title}>
434
+ <BaseLayout title={title} description={description}>
427
435
  <Fragment slot="head">
428
436
  <script is:inline define:vars={{ verticalNavAutoCollapsePaths }}>
429
437
  (() => {
@@ -1,358 +1,21 @@
1
1
  ---
2
- import { buildUrl } from '@utils/url-builder';
3
- import config from '@config';
4
-
5
- import { getCollection } from 'astro:content';
6
2
  import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
7
- import { BookOpenText, Workflow, TableProperties, BookUser, Code2, FileJson, ArrowUpRight, ArrowRight, Plus } from 'lucide-react';
3
+ import CatalogDashboard from '@components/Homepage/CatalogDashboard.astro';
8
4
 
9
5
  const { showNestedSideBar = true } = Astro.props;
10
-
11
- // Lightweight data fetch: only raw collections, no hydration.
12
- const [allDomains, allServices, allEvents, allCommands, allQueries, allFlows] = await Promise.all([
13
- getCollection('domains'),
14
- getCollection('services'),
15
- getCollection('events'),
16
- getCollection('commands'),
17
- getCollection('queries'),
18
- getCollection('flows'),
19
- ]);
20
-
21
- const isCurrentVersion = (item: { data: { hidden?: boolean }; filePath?: string }) =>
22
- item.data.hidden !== true && !item.filePath?.includes('versioned');
23
-
24
- const domains = allDomains.filter(isCurrentVersion);
25
- const services = allServices.filter(isCurrentVersion);
26
- const events = allEvents.filter(isCurrentVersion);
27
- const commands = allCommands.filter(isCurrentVersion);
28
- const queries = allQueries.filter(isCurrentVersion);
29
- const messages = [...events, ...commands, ...queries];
30
- const flows = allFlows.filter(isCurrentVersion);
31
-
32
- const getDefaultUrl = (route: string, defaultValue: string) => {
33
- if (domains.length > 0) return buildUrl(`/${route}/domains/${domains[0].data.id}/${domains[0].data.version}`);
34
- if (services.length > 0) return buildUrl(`/${route}/services/${services[0].data.id}/${services[0].data.version}`);
35
- if (flows.length > 0) return buildUrl(`/${route}/flows/${flows[0].data.id}/${flows[0].data.version}`);
36
- return buildUrl(defaultValue);
37
- };
38
-
39
- interface StatTile {
40
- label: string;
41
- count: number;
42
- href: string;
43
- emptyHref: string;
44
- tone: string; // CSS color expression — drives accent rail + count gradient
45
- }
46
-
47
- const statTiles: StatTile[] = [
48
- {
49
- label: 'Domains',
50
- count: domains.length,
51
- href: buildUrl('/discover/domains'),
52
- emptyHref: 'https://www.eventcatalog.dev/docs/domains',
53
- tone: '250 204 21', // amber-400
54
- },
55
- {
56
- label: 'Services',
57
- count: services.length,
58
- href: buildUrl('/discover/services'),
59
- emptyHref: 'https://www.eventcatalog.dev/docs/services',
60
- tone: '244 114 182', // pink-400
61
- },
62
- {
63
- label: 'Messages',
64
- count: messages.length,
65
- href: buildUrl('/discover/events'),
66
- emptyHref: 'https://www.eventcatalog.dev/docs/messages',
67
- tone: '167 139 250', // violet-400
68
- },
69
- {
70
- label: 'Flows',
71
- count: flows.length,
72
- href: buildUrl('/discover/flows'),
73
- emptyHref: 'https://www.eventcatalog.dev/docs/flows',
74
- tone: '94 234 212', // teal-300
75
- },
76
- ];
77
-
78
- interface ExploreCard {
79
- title: string;
80
- description: string;
81
- icon: any;
82
- href: string;
83
- external?: boolean;
84
- /** When true, renders as the lead "feature" card with extra width and CTA styling. */
85
- feature?: boolean;
86
- tone: string; // RGB triple for the icon tile
87
- }
88
-
89
- const exploreCards: ExploreCard[] = [
90
- {
91
- title: 'Documentation',
92
- description: 'Browse every domain, service, message, and flow your teams have published.',
93
- icon: BookOpenText,
94
- href: getDefaultUrl('docs', 'domains'),
95
- feature: true,
96
- tone: '96 165 250', // blue-400
97
- },
98
- {
99
- title: 'Visualizer',
100
- description: 'See how services connect — message flows across your architecture.',
101
- icon: Workflow,
102
- href: getDefaultUrl('visualiser', 'domains'),
103
- tone: '167 139 250', // violet-400
104
- },
105
- {
106
- title: 'Discover',
107
- description: 'Search every event, service, agent, and team across the catalog.',
108
- icon: TableProperties,
109
- href: buildUrl('/discover/events'),
110
- tone: '94 234 212', // teal-300
111
- },
112
- {
113
- title: 'Schema Explorer',
114
- description: 'Track schema evolution, compare versions, and catch breaking changes.',
115
- icon: FileJson,
116
- href: buildUrl('/schemas/explorer'),
117
- tone: '250 204 21', // amber-400
118
- },
119
- {
120
- title: 'Team Directory',
121
- description: 'Find owners, on-call contacts, and the team behind every service.',
122
- icon: BookUser,
123
- href: buildUrl('/directory/users'),
124
- tone: '251 146 60', // orange-400
125
- },
126
- {
127
- title: 'API & SDK',
128
- description: 'Programmatic access — query and manage your catalog with a typed SDK.',
129
- icon: Code2,
130
- href: 'https://www.eventcatalog.dev/docs/sdk',
131
- external: true,
132
- tone: '129 140 248', // indigo-400
133
- },
134
- ];
135
6
  ---
136
7
 
137
- <VerticalSideBarLayout title="EventCatalog" showNestedSideBar={showNestedSideBar}>
138
- <div class="ec-home min-h-screen">
139
- <!-- Hero -->
140
- <header class="ec-hero">
141
- <div class="text-[11px] font-semibold uppercase tracking-[0.18em] text-[rgb(var(--ec-accent))]">Catalog</div>
142
- <div class="mt-3 flex flex-col gap-3 md:flex-row md:items-end md:justify-between">
143
- <div>
144
- <h1 class="text-[28px] font-semibold leading-[1.1] tracking-tight text-[rgb(var(--ec-page-text))] md:text-[32px]">
145
- {config?.organizationName || 'EventCatalog'}
146
- </h1>
147
- <p class="mt-2 max-w-2xl text-[14px] leading-relaxed text-[rgb(var(--ec-page-text-muted))]">
148
- {config.tagline || 'Explore and understand your event-driven architecture.'}
149
- </p>
150
- </div>
151
- <div class="flex items-center gap-2">
152
- <a
153
- href={getDefaultUrl('docs', 'domains')}
154
- class="ec-btn-primary inline-flex items-center gap-1.5 rounded-md bg-[rgb(var(--ec-page-bg))] border border-[rgb(var(--ec-page-border))] px-3.5 py-2 text-[12.5px] font-semibold text-[rgb(var(--ec-page-text-muted))] transition-colors hover:border-[rgb(var(--ec-accent)/0.5)] hover:text-[rgb(var(--ec-accent))]"
155
- >
156
- Explore Catalog
157
- <ArrowRight className="h-3.5 w-3.5" />
158
- </a>
159
- </div>
160
- </div>
161
- </header>
162
-
163
- <div class="ec-divider"></div>
164
-
165
- <!-- Stat strip -->
166
- <section class="ec-stats">
167
- {
168
- statTiles.map((tile) => (
169
- <a
170
- href={tile.count > 0 ? tile.href : tile.emptyHref}
171
- target={tile.count === 0 ? '_blank' : undefined}
172
- rel={tile.count === 0 ? 'noreferrer' : undefined}
173
- class="ec-stat group"
174
- style={`--tone: ${tile.tone};`}
175
- >
176
- <div class="flex items-baseline justify-between gap-2">
177
- <span class="text-[11px] font-medium uppercase tracking-[0.14em] text-[rgb(var(--tone))]">{tile.label}</span>
178
- {tile.count > 0 ? (
179
- <ArrowUpRight className="h-3.5 w-3.5 text-[rgb(var(--ec-page-text-muted))] transition-transform duration-200 group-hover:-translate-y-0.5 group-hover:translate-x-0.5 group-hover:text-[rgb(var(--ec-accent))]" />
180
- ) : (
181
- <Plus className="h-3.5 w-3.5 text-[rgb(var(--ec-page-text-muted))] transition-colors group-hover:text-[rgb(var(--ec-accent))]" />
182
- )}
183
- </div>
184
- <div class="mt-3 flex items-baseline gap-2">
185
- <span class="font-mono text-[34px] font-semibold leading-none tracking-tight text-[rgb(var(--ec-page-text))]">
186
- {tile.count}
187
- </span>
188
- {tile.count === 0 && <span class="text-[12px] text-[rgb(var(--ec-page-text-muted))]">— add some</span>}
189
- </div>
190
- </a>
191
- ))
192
- }
193
- </section>
194
-
195
- <!-- Eyebrow + Explore grid -->
196
- <section class="mt-12">
197
- <div class="ec-section-eyebrow">
198
- <span class="text-[11px] font-semibold uppercase tracking-[0.18em] text-[rgb(var(--ec-accent))]">Explore</span>
199
- <span class="ec-section-rule"></span>
200
- <span class="font-mono text-[11px] text-[rgb(var(--ec-page-text-muted)/0.7)]">{exploreCards.length} sections</span>
201
- </div>
202
-
203
- <div class="ec-explore-grid mt-4">
204
- {
205
- exploreCards.map((card) => (
206
- <a
207
- href={card.href}
208
- target={card.external ? '_blank' : undefined}
209
- rel={card.external ? 'noreferrer' : undefined}
210
- class:list={['ec-card group', card.feature ? 'ec-card--feature' : '']}
211
- style={`--tone: ${card.tone};`}
212
- >
213
- <div class="flex items-start justify-between gap-3">
214
- <div class="ec-card-icon">
215
- <card.icon className="h-4 w-4" />
216
- </div>
217
- <ArrowUpRight className="h-3.5 w-3.5 text-[rgb(var(--ec-page-text-muted))] transition-transform duration-200 group-hover:-translate-y-0.5 group-hover:translate-x-0.5 group-hover:text-[rgb(var(--ec-accent))]" />
218
- </div>
219
- <h3 class="mt-4 text-[15px] font-semibold tracking-tight text-[rgb(var(--ec-page-text))]">{card.title}</h3>
220
- <p class="mt-1.5 text-[13px] leading-relaxed text-[rgb(var(--ec-page-text-muted))]">{card.description}</p>
221
- </a>
222
- ))
223
- }
224
- </div>
225
- </section>
8
+ <VerticalSideBarLayout showNestedSideBar={showNestedSideBar}>
9
+ <div class="ec-home">
10
+ <CatalogDashboard />
226
11
  </div>
227
12
  </VerticalSideBarLayout>
228
13
 
229
14
  <style is:global>
230
15
  .ec-home {
231
- /* Negate the layout wrapper's horizontal padding so the homepage controls its own. */
16
+ /* The layout pads its content: the homepage sets its own, wider spacing */
232
17
  margin-left: calc(var(--ec-app-content-padding-left, 5rem) * -1);
233
18
  margin-right: calc(var(--ec-app-content-padding-right, 5rem) * -1);
234
19
  padding: 2rem 2rem 0;
235
20
  }
236
- .ec-hero {
237
- padding-bottom: 1.5rem;
238
- }
239
- .ec-divider {
240
- height: 1px;
241
- background: linear-gradient(
242
- to right,
243
- transparent,
244
- rgb(var(--ec-page-border)) 12%,
245
- rgb(var(--ec-page-border)) 88%,
246
- transparent
247
- );
248
- margin: 0.25rem 0 1.5rem;
249
- }
250
- .ec-stats {
251
- display: grid;
252
- grid-template-columns: repeat(2, minmax(0, 1fr));
253
- gap: 0.75rem;
254
- }
255
- @media (min-width: 768px) {
256
- .ec-stats {
257
- grid-template-columns: repeat(4, minmax(0, 1fr));
258
- }
259
- }
260
- .ec-stat {
261
- position: relative;
262
- display: flex;
263
- flex-direction: column;
264
- overflow: hidden;
265
- border: 1px solid rgb(var(--ec-page-border));
266
- border-radius: 0.75rem;
267
- background:
268
- radial-gradient(80% 100% at 0% 0%, rgb(var(--tone) / 0.05), transparent 60%), rgb(var(--ec-card-bg, var(--ec-page-bg)));
269
- padding: 1rem 1.1rem 1.1rem;
270
- transition:
271
- border-color 200ms,
272
- transform 200ms,
273
- box-shadow 200ms;
274
- text-decoration: none;
275
- }
276
- .ec-stat:hover {
277
- border-color: rgb(var(--ec-accent) / 0.5);
278
- transform: translateY(-1px);
279
- }
280
-
281
- .ec-section-eyebrow {
282
- display: flex;
283
- align-items: center;
284
- gap: 0.75rem;
285
- }
286
- .ec-section-rule {
287
- flex: 1;
288
- height: 1px;
289
- background: linear-gradient(to right, rgb(var(--ec-accent) / 0.4), rgb(var(--ec-page-border)) 30%);
290
- }
291
-
292
- /* Explore grid: bordered cells with breathing room between them. */
293
- .ec-explore-grid {
294
- display: grid;
295
- grid-template-columns: repeat(1, minmax(0, 1fr));
296
- gap: 1rem;
297
- }
298
- @media (min-width: 640px) {
299
- .ec-explore-grid {
300
- grid-template-columns: repeat(2, minmax(0, 1fr));
301
- }
302
- }
303
- @media (min-width: 1024px) {
304
- .ec-explore-grid {
305
- grid-template-columns: repeat(3, minmax(0, 1fr));
306
- }
307
- }
308
-
309
- .ec-card {
310
- position: relative;
311
- display: flex;
312
- flex-direction: column;
313
- padding: 1.5rem 1.5rem 1.4rem;
314
- text-decoration: none;
315
- border: 1px solid rgb(var(--ec-page-border));
316
- border-radius: 0.875rem;
317
- background: rgb(var(--ec-card-bg, var(--ec-page-bg)));
318
- transition:
319
- background 200ms,
320
- border-color 200ms;
321
- min-height: 9.5rem;
322
- }
323
- .ec-card:hover {
324
- background: rgb(var(--ec-page-bg) / 0.4);
325
- border-color: rgb(var(--ec-accent) / 0.5);
326
- }
327
-
328
- .ec-card--feature {
329
- background:
330
- radial-gradient(60% 90% at 0% 0%, rgb(var(--tone) / 0.07), transparent 60%), rgb(var(--ec-card-bg, var(--ec-page-bg)));
331
- }
332
- .ec-card--feature:hover {
333
- background: radial-gradient(60% 90% at 0% 0%, rgb(var(--ec-accent) / 0.12), transparent 60%), rgb(var(--ec-page-bg) / 0.4);
334
- }
335
-
336
- .ec-card-icon {
337
- display: inline-flex;
338
- height: 2.25rem;
339
- width: 2.25rem;
340
- align-items: center;
341
- justify-content: center;
342
- border-radius: 0.5rem;
343
- background: rgb(var(--tone) / 0.12);
344
- color: rgb(var(--tone));
345
- transition:
346
- background 200ms,
347
- color 200ms;
348
- }
349
- .ec-card:hover .ec-card-icon {
350
- background: rgb(var(--ec-accent) / 0.2);
351
- color: rgb(var(--ec-accent));
352
- }
353
- .ec-card--feature .ec-card-icon {
354
- height: 2.5rem;
355
- width: 2.5rem;
356
- border-radius: 0.625rem;
357
- }
358
21
  </style>
@@ -68,10 +68,9 @@ describe('architecture page redirects', () => {
68
68
  it('returns not found for resources without an architecture page', async () => {
69
69
  mocks.isSSR.mockReturnValue(true);
70
70
 
71
+ // Nothing found: the page answers with the 404 page
71
72
  await expect(
72
73
  Page.getData({ params: { type: 'events', id: 'order-created', version: '1.0.0' }, props: {} } as any)
73
- ).rejects.toMatchObject({
74
- status: 404,
75
- });
74
+ ).resolves.toBeUndefined();
76
75
  });
77
76
  });
@@ -61,11 +61,4 @@ export class Page extends HybridPage {
61
61
 
62
62
  return { type, data: { id, name: match.item.data.name, version }, redirectPath: match.redirectPath };
63
63
  }
64
-
65
- protected static createNotFoundResponse(): Response {
66
- return new Response(null, {
67
- status: 404,
68
- statusText: 'Documentation not found',
69
- });
70
- }
71
64
  }
@@ -9,7 +9,9 @@ import { Page } from './_index.data';
9
9
  export const prerender = Page.prerender;
10
10
  export const getStaticPaths = Page.getStaticPaths;
11
11
 
12
- const { data, redirectPath } = await Page.getData(Astro);
12
+ const pageData = await Page.getData(Astro);
13
+ if (!pageData) return Astro.rewrite('/404');
14
+ const { data, redirectPath } = pageData;
13
15
  const redirectUrl = buildUrl(redirectPath);
14
16
 
15
17
  if (isSSR()) {
@@ -47,11 +47,4 @@ export class Page extends HybridPage {
47
47
  allVersions,
48
48
  };
49
49
  }
50
-
51
- protected static createNotFoundResponse(): Response {
52
- return new Response(null, {
53
- status: 404,
54
- statusText: 'Diagram not found',
55
- });
56
- }
57
50
  }
@@ -10,6 +10,7 @@ export const prerender = Page.prerender;
10
10
  export const getStaticPaths = Page.getStaticPaths;
11
11
 
12
12
  const props = await Page.getData(Astro);
13
+ if (!props) return Astro.rewrite('/404');
13
14
  const { Content } = await render(props);
14
15
 
15
16
  const currentVersion = props.data.version;
@@ -15,6 +15,7 @@ export const prerender = Page.prerender;
15
15
  export const getStaticPaths = Page.getStaticPaths;
16
16
 
17
17
  const props = await Page.getData(Astro);
18
+ if (!props) return Astro.rewrite('/404');
18
19
  const { Content, headings } = await render(props);
19
20
 
20
21
  const pageTitle = `Diagram | ${props.data.name}`;
@@ -45,6 +45,11 @@ export class Page extends HybridPage {
45
45
  teams: getTeams,
46
46
  };
47
47
 
48
+ // An unknown type (e.g. a mistyped URL) has no directory page
49
+ if (!Object.hasOwn(loaders, type)) {
50
+ return null;
51
+ }
52
+
48
53
  // Get all items of the specified type
49
54
  const items = await loaders[type as keyof typeof loaders]();
50
55
 
@@ -53,11 +58,4 @@ export class Page extends HybridPage {
53
58
  data: items,
54
59
  };
55
60
  }
56
-
57
- protected static createNotFoundResponse(): Response {
58
- return new Response(null, {
59
- status: 404,
60
- statusText: 'Directory type not found',
61
- });
62
- }
63
61
  }
@@ -7,7 +7,9 @@ import { Page } from './_index.data';
7
7
  export const prerender = Page.prerender;
8
8
  export const getStaticPaths = Page.getStaticPaths;
9
9
 
10
- const { type, data } = await Page.getData(Astro);
10
+ const pageData = await Page.getData(Astro);
11
+ if (!pageData) return Astro.rewrite('/404');
12
+ const { type, data } = pageData;
11
13
 
12
14
  function mapToItem(i: any) {
13
15
  return {
@@ -81,6 +81,11 @@ export class Page extends HybridPage {
81
81
  'data-products': getDataProducts,
82
82
  };
83
83
 
84
+ // An unknown type (e.g. a mistyped URL) has no discover page
85
+ if (!Object.hasOwn(loaders, type)) {
86
+ return null;
87
+ }
88
+
84
89
  // @ts-ignore
85
90
  const items = await loaders[type]();
86
91
 
@@ -89,11 +94,4 @@ export class Page extends HybridPage {
89
94
  data: items,
90
95
  };
91
96
  }
92
-
93
- protected static createNotFoundResponse(): Response {
94
- return new Response(null, {
95
- status: 404,
96
- statusText: 'Collection type not found',
97
- });
98
- }
99
97
  }
@@ -27,7 +27,9 @@ import { Page } from './_index.data';
27
27
  export const prerender = Page.prerender;
28
28
  export const getStaticPaths = Page.getStaticPaths;
29
29
 
30
- const { type, data } = await Page.getData(Astro);
30
+ const pageData = await Page.getData(Astro);
31
+ if (!pageData) return Astro.rewrite('/404');
32
+ const { type, data } = pageData;
31
33
 
32
34
  const events = await getEvents();
33
35
  const queries = await getQueries();