@eventcatalog/core 5.0.0-beta.8 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +234 -134
- package/README.md +5 -5
- package/dist/analytics/analytics.cjs +18 -3
- package/dist/analytics/analytics.d.cts +1 -1
- package/dist/analytics/analytics.d.ts +1 -1
- package/dist/analytics/analytics.js +3 -2
- package/dist/analytics/log-build.cjs +157 -76
- package/dist/analytics/log-build.d.cts +2 -2
- package/dist/analytics/log-build.d.ts +2 -2
- package/dist/analytics/log-build.js +7 -5
- package/dist/analytics/studio-usage.cjs +90 -0
- package/dist/analytics/studio-usage.d.cts +5 -0
- package/dist/analytics/studio-usage.d.ts +5 -0
- package/dist/analytics/studio-usage.js +10 -0
- package/dist/analytics/telemetry.cjs +42 -0
- package/dist/analytics/telemetry.d.cts +20 -0
- package/dist/analytics/telemetry.d.ts +20 -0
- package/dist/analytics/telemetry.js +8 -0
- package/dist/catalog-runtime.cjs +1 -1
- package/dist/{chunk-RFIXKATC.js → chunk-4G7FSRPN.js} +35 -42
- package/dist/{chunk-AWOO63ME.js → chunk-AD7CL772.js} +11 -4
- package/dist/chunk-CA6WCQ2T.js +54 -0
- package/dist/chunk-CPW6G62V.js +17 -0
- package/dist/{chunk-PU25TOOJ.js → chunk-JZN4RSMF.js} +1 -1
- package/dist/{chunk-4W55ZOOS.js → chunk-NEAVAFSJ.js} +66 -35
- package/dist/{chunk-C6T2AAFR.js → chunk-NIVST4WY.js} +9 -3
- package/dist/{chunk-ENAG6UH7.js → chunk-NKNOXQ62.js} +1 -6
- package/dist/{chunk-AYNZXUB5.js → chunk-OX6TSVHY.js} +5 -5
- package/dist/{chunk-36RYTYT5.js → chunk-PD7KTSYN.js} +1 -1
- package/dist/{chunk-RDIZJ7UT.js → chunk-RE7UTPWH.js} +1 -1
- package/dist/chunk-S5GWAUG5.js +29 -0
- package/dist/{chunk-T4G3VVZ4.js → chunk-YQGRMDRB.js} +4 -2
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/docs/api/02-config.md +57 -7
- package/dist/docs/development/00-why-eventcatalog.md +11 -11
- package/dist/docs/development/01-fundamentals.md +20 -13
- package/dist/docs/development/01-getting-started/configuration-overview.md +2 -2
- package/dist/docs/development/01-getting-started/installation.md +9 -1
- package/dist/docs/development/_getting-started.mdx +1 -1
- package/dist/docs/development/ask-your-architecture/01-intro.md +5 -5
- package/dist/docs/development/ask-your-architecture/02-eventcatalog-assistant/01-what-is-eventcatalog-assistant.md +0 -4
- package/dist/docs/development/ask-your-architecture/02-eventcatalog-assistant/02-configuration.md +15 -3
- package/dist/docs/development/ask-your-architecture/02-eventcatalog-assistant/03-bring-your-own-tools.md +0 -3
- package/dist/docs/development/ask-your-architecture/02-eventcatalog-assistant/_category_.json +1 -1
- package/dist/docs/development/ask-your-architecture/02-llms.txt.md +1 -1
- package/dist/docs/development/ask-your-architecture/{05-skills/01-introduction.md → 03-install-skills.md} +57 -7
- package/dist/docs/development/ask-your-architecture/03-mcp-server/_category_.json +1 -1
- package/dist/docs/development/ask-your-architecture/03-mcp-server/getting-started.md +86 -33
- package/dist/docs/development/ask-your-architecture/03-mcp-server/introduction.md +14 -4
- package/dist/docs/development/ask-your-architecture/03-schemas.txt.md +1 -1
- package/dist/docs/development/ask-your-architecture/04-agents/_category_.json +1 -1
- package/dist/docs/development/authentication/01-introduction.md +2 -23
- package/dist/docs/development/authentication/02-enabling-authentication.md +0 -20
- package/dist/docs/development/authentication/07-rbac-middleware.md +0 -2
- package/dist/docs/development/authentication/providers/03-setting-up-github.md +0 -2
- package/dist/docs/development/authentication/providers/03a-setting-up-google.md +0 -2
- package/dist/docs/development/authentication/providers/04-setting-up-azure-ad.md +0 -2
- package/dist/docs/development/authentication/providers/05-setting-up-okta.md +0 -3
- package/dist/docs/development/authentication/providers/06-setting-up-auth0.md +0 -2
- package/dist/docs/development/bring-your-own-documentation/01-introduction.md +0 -4
- package/dist/docs/development/bring-your-own-documentation/custom-pages/01-introduction.md +0 -3
- package/dist/docs/development/bring-your-own-documentation/custom-pages/02-adding-custom-docs.md +0 -17
- package/dist/docs/development/bring-your-own-documentation/custom-pages/03-components.md +0 -4
- package/dist/docs/development/bring-your-own-documentation/custom-pages/04-owners.md +0 -2
- package/dist/docs/development/bring-your-own-documentation/resource-docs/01-introduction.md +1 -3
- package/dist/docs/development/bring-your-own-documentation/resource-docs/02-adding-resource-docs.md +1 -4
- package/dist/docs/development/bring-your-own-documentation/resource-docs/03-categories.md +0 -2
- package/dist/docs/development/bring-your-own-documentation/resource-docs/04-versioning.md +1 -3
- package/dist/docs/development/components/components/11-flow.md +17 -0
- package/dist/docs/development/components/components/15-nodegraph.md +43 -10
- package/dist/docs/development/components/components/18-remote-schema.md +1 -4
- package/dist/docs/development/components/custom-components/07-reference.md +14 -0
- package/dist/docs/development/customization/02-themes.md +0 -5
- package/dist/docs/development/customization/03-search.md +1 -1
- package/dist/docs/development/customization/05-custom-pages-and-api-routes/01-introduction.md +0 -2
- package/dist/docs/development/customization/05-custom-pages-and-api-routes/04-reference.md +7 -1
- package/dist/docs/development/customization/05-resource-sidebar.md +1 -1
- package/dist/docs/development/customization/09-embed-eventcatalog.md +20 -25
- package/dist/docs/development/deployment/hosting-options.md +7 -2
- package/dist/docs/development/developer-tools/eventcatalog-linter/how-to/05-fix-common-problems.md +16 -0
- package/dist/docs/development/developer-tools/eventcatalog-linter/reference/03-rules.md +31 -1
- package/dist/docs/development/developer-tools/eventcatalog-linter/reference/05-supported-resources.md +1 -1
- package/dist/docs/development/governance/architecture-change-detection/01-introduction.md +0 -10
- package/dist/docs/development/governance/architecture-change-detection/06-pipeline-gates.md +0 -4
- package/dist/docs/development/guides/97-architecture-graph.md +1 -1
- package/dist/docs/development/guides/99-adding-analytics.md +0 -7
- package/dist/docs/development/guides/domains/03-model-subdomains.md +2 -2
- package/dist/docs/development/guides/domains/04-add-resources-to-domains/01-add-systems-to-domains.md +3 -1
- package/dist/docs/development/guides/domains/04-add-resources-to-domains/02-add-services-to-domains.md +5 -1
- package/dist/docs/development/guides/domains/04-add-resources-to-domains/03-add-messages-to-domains.md +2 -2
- package/dist/docs/development/guides/domains/04-add-resources-to-domains/05-add-entities-to-domains.md +1 -1
- package/dist/docs/development/guides/domains/04-add-resources-to-domains/06-domain-resources-page.md +40 -0
- package/dist/docs/development/guides/domains/06-visualize-domains/01-domain-diagram.md +89 -0
- package/dist/docs/development/guides/domains/06-visualize-domains/_category_.json +1 -2
- package/dist/docs/development/guides/domains/09-reference.md +4 -4
- package/dist/docs/development/guides/owners/automated-teams-and-users/create-your-own-connector.md +0 -2
- package/dist/docs/development/guides/owners/automated-teams-and-users/sync-from-github.md +0 -3
- package/dist/docs/development/guides/owners/automated-teams-and-users/sync-from-microsoft-entra.md +0 -2
- package/dist/docs/development/guides/resources/adrs/03-linking-resources.md +8 -0
- package/dist/docs/development/guides/resources/adrs/06-reference.md +4 -4
- package/dist/docs/development/guides/resources/agents/11-reference.md +1 -1
- package/dist/docs/development/guides/resources/data/04-reference.md +1 -1
- package/dist/docs/development/guides/resources/data-products/07-reference.md +1 -1
- package/dist/docs/development/guides/resources/entities/05-entity-maps.md +1 -1
- package/dist/docs/development/guides/resources/entities/06-reference.md +2 -2
- package/dist/docs/development/guides/resources/flows/01-introduction.md +11 -1
- package/dist/docs/development/guides/resources/flows/03-model-flow-steps.md +65 -6
- package/dist/docs/development/guides/resources/flows/05-explore-flows.md +112 -0
- package/dist/docs/development/guides/resources/flows/06-reference.md +25 -4
- package/dist/docs/development/guides/resources/messages/01-what-are-messages.md +19 -0
- package/dist/docs/development/guides/resources/messages/04-message-channels/01-introduction.md +16 -0
- package/dist/docs/development/guides/resources/messages/04-message-channels/05-reference.md +2 -1
- package/dist/docs/development/guides/resources/messages/10-reference.md +6 -3
- package/dist/docs/development/guides/resources/schemas/04-explore-schemas/01-schema-explorer.md +2 -2
- package/dist/docs/development/guides/resources/schemas/04-explore-schemas/02-fields-explorer.md +0 -9
- package/dist/docs/development/guides/resources/schemas/05-schema-api.md +0 -3
- package/dist/docs/development/guides/resources/schemas/06-schema-mcp.md +11 -3
- package/dist/docs/development/guides/resources/services/04-add-resources-to-services/03-add-entities-to-services.md +1 -1
- package/dist/docs/development/guides/resources/services/05-resources-page.md +50 -0
- package/dist/docs/development/guides/resources/services/08-reference.md +2 -1
- package/dist/docs/development/guides/systems/01-introduction.md +1 -1
- package/dist/docs/development/guides/systems/02-create-system.md +2 -2
- package/dist/docs/development/guides/systems/03-add-resources-to-systems.md +35 -9
- package/dist/docs/development/guides/systems/04-model-relationships-and-actors.md +5 -3
- package/dist/docs/development/guides/systems/05-system-context-maps.md +39 -22
- package/dist/docs/development/guides/systems/06-reference.md +4 -4
- package/dist/docs/development/license/01-license-file.md +151 -0
- package/dist/docs/development/license/_category_.json +12 -0
- package/dist/docs/development/license-keys/_category_.json +3 -3
- package/dist/docs/development/license-keys/integrations.md +7 -37
- package/dist/docs/development/license-keys/license-validation.md +4 -0
- package/dist/docs/development/license-keys/overview.md +5 -1
- package/dist/docs/development/license-keys/plans.md +4 -2
- package/dist/docs/development/studio/01-introduction.md +60 -0
- package/dist/docs/development/studio/02-create-a-canvas.md +103 -0
- package/dist/docs/development/studio/03-set-up-persistent-storage.md +59 -0
- package/dist/docs/development/studio/04-collaborate-on-a-canvas.md +78 -0
- package/dist/docs/development/studio/05-connect-your-agent.md +102 -0
- package/dist/docs/development/studio/06-run-studio-in-production.md +54 -0
- package/dist/docs/development/studio/07-reference.md +294 -0
- package/dist/docs/development/studio/_category_.json +12 -0
- package/dist/docs/development/upgrading/v5.md +345 -0
- package/dist/docs/federation/00-overview.md +2 -8
- package/dist/docs/federation/01-first-federation.md +6 -18
- package/dist/docs/federation/explanation/how-it-works.md +0 -4
- package/dist/docs/federation/explanation/lockfile-and-cache.md +0 -4
- package/dist/docs/federation/explanation/mvp-status-and-feedback.md +0 -4
- package/dist/docs/federation/explanation/ownership-and-references.md +0 -4
- package/dist/docs/federation/how-to/configure-github-sources.md +6 -5
- package/dist/docs/federation/how-to/configure-validation-rules.md +0 -4
- package/dist/docs/federation/how-to/run-in-ci.md +2 -9
- package/dist/docs/federation/how-to/share-assets-and-components.md +0 -4
- package/dist/docs/federation/how-to/use-local-sources.md +0 -4
- package/dist/docs/federation/legacy-federation/00-introduction.md +4 -22
- package/dist/docs/federation/legacy-federation/01-installation.md +7 -58
- package/dist/docs/federation/legacy-federation/02-plugin-configuration.md +3 -21
- package/dist/docs/federation/legacy-federation/03-examples.md +3 -5
- package/dist/docs/federation/legacy-federation/04-configuration.md +3 -8
- package/dist/docs/federation/legacy-federation/05-setup-team-catalog.md +3 -6
- package/dist/docs/federation/reference/cli.md +2 -8
- package/dist/docs/federation/reference/configuration.md +0 -4
- package/dist/docs/federation/reference/diagnostic-rules.md +0 -5
- package/dist/docs/federation/reference/generated-output.md +0 -4
- package/dist/docs/federation/reference/supported-content.md +0 -4
- package/dist/docs/federation/reference/troubleshooting.md +0 -14
- package/dist/docs/plugins/02-generators.md +1 -1
- package/dist/docs/plugins/amazon-apigateway/00-intro.md +3 -28
- package/dist/docs/plugins/amazon-apigateway/01-installation.md +4 -56
- package/dist/docs/plugins/amazon-apigateway/02-plugin-configuration.md +1 -6
- package/dist/docs/plugins/apicurio/00-intro.md +2 -31
- package/dist/docs/plugins/apicurio/01-installation.md +2 -26
- package/dist/docs/plugins/apicurio/02-plugin-configuration.md +5 -13
- package/dist/docs/plugins/apicurio/03-features.md +1 -3
- package/dist/docs/plugins/asyncapi/00-intro.md +2 -28
- package/dist/docs/plugins/asyncapi/01-installation.md +3 -54
- package/dist/docs/plugins/asyncapi/02-plugin-configuration.md +2 -1
- package/dist/docs/plugins/aws-glue-registry/00-intro.md +4 -32
- package/dist/docs/plugins/aws-glue-registry/00a-installation.md +3 -53
- package/dist/docs/plugins/azure-schema-registry/00-intro.md +4 -32
- package/dist/docs/plugins/azure-schema-registry/01-installation.md +6 -33
- package/dist/docs/plugins/azure-schema-registry/02-plugin-configuration.md +1 -14
- package/dist/docs/plugins/azure-schema-registry/04-examples.md +0 -2
- package/dist/docs/plugins/backstage/00-overview.md +1 -7
- package/dist/docs/plugins/backstage/01-get-started.md +6 -15
- package/dist/docs/plugins/backstage/06-configuration.md +2 -8
- package/dist/docs/plugins/confluent-schema-registry/00-intro.md +2 -29
- package/dist/docs/plugins/confluent-schema-registry/01-installation.md +2 -26
- package/dist/docs/plugins/eventbridge/00-intro.md +0 -4
- package/dist/docs/plugins/eventbridge/00a-installation.md +1 -87
- package/dist/docs/plugins/eventbridge/03-api.md +2 -20
- package/dist/docs/plugins/github/00-intro.md +0 -31
- package/dist/docs/plugins/github/01-installation.md +2 -34
- package/dist/docs/plugins/graphql/00-intro.md +0 -30
- package/dist/docs/plugins/graphql/01-installation.md +1 -53
- package/dist/docs/plugins/graphql/02-plugin-configuration.md +0 -1
- package/dist/docs/plugins/hookdeck/01-intro.md +1 -1
- package/dist/docs/plugins/openapi/00-intro.md +0 -30
- package/dist/docs/plugins/openapi/01-installation.md +1 -57
- package/dist/docs/plugins/openapi/02-plugin-configuration.md +0 -5
- package/dist/docs/plugins/openapi/03-features.md +0 -4
- package/dist/eventcatalog-config-file-utils.cjs +34 -41
- package/dist/eventcatalog-config-file-utils.d.cts +56 -58
- package/dist/eventcatalog-config-file-utils.d.ts +56 -58
- package/dist/eventcatalog-config-file-utils.js +1 -1
- package/dist/eventcatalog.cjs +408 -281
- package/dist/eventcatalog.config.d.cts +26 -15
- package/dist/eventcatalog.config.d.ts +26 -15
- package/dist/eventcatalog.js +46 -27
- package/dist/features.cjs +36 -49
- package/dist/features.d.cts +1 -3
- package/dist/features.d.ts +1 -3
- package/dist/features.js +4 -6
- package/dist/federation/federate.cjs +37 -44
- package/dist/federation/federate.js +5 -5
- package/dist/federation/source-provider.js +2 -2
- package/dist/generate.cjs +37 -42
- package/dist/generate.js +4 -4
- package/dist/resolve-catalog-dependencies.cjs +34 -41
- package/dist/resolve-catalog-dependencies.js +2 -2
- package/dist/runtime-dependencies/@eventcatalog__visualiser__studio-fonts.css.mjs +3 -0
- package/dist/runtime-dependencies/@eventcatalog__visualiser__studio.css.mjs +3 -0
- package/dist/runtime-dependencies/@eventcatalog__visualiser__studio.mjs +3 -0
- package/dist/runtime-dependencies/htmlparser2.mjs +3 -0
- package/dist/runtime-dependencies/manifest.json +5 -1
- package/dist/runtime-dependencies/react-syntax-highlighter__dist__cjs__create-element.js.mjs +3 -0
- package/dist/utils/child-process-signals.cjs +53 -0
- package/dist/utils/child-process-signals.d.cts +19 -0
- package/dist/utils/child-process-signals.d.ts +19 -0
- package/dist/utils/child-process-signals.js +6 -0
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/dist/utils/license-status.cjs +11 -4
- package/dist/utils/license-status.d.cts +3 -2
- package/dist/utils/license-status.d.ts +3 -2
- package/dist/utils/license-status.js +1 -1
- package/eventcatalog/astro.config.mjs +21 -1
- package/eventcatalog/integrations/config-loader.mjs +56 -58
- package/eventcatalog/integrations/skip-startup-reload.mjs +31 -0
- package/eventcatalog/integrations/stable-dependency-order.mjs +29 -0
- package/eventcatalog/integrations/storage.mjs +20 -0
- package/eventcatalog/integrations/studio-server.mjs +4 -1
- package/eventcatalog/src/components/CatalogGraph/CatalogForceGraph.tsx +39 -0
- package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +2 -0
- package/eventcatalog/src/components/Homepage/CatalogDashboard.astro +342 -0
- package/eventcatalog/src/components/MDX/NodeGraph/AstroNodeGraph.tsx +60 -4
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +5 -1
- package/eventcatalog/src/components/NavigationProgress.astro +7 -0
- package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +2 -0
- package/eventcatalog/src/components/SchemaExplorer/ExamplesViewer.tsx +2 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaCodeModal.tsx +2 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +2 -0
- package/eventcatalog/src/components/Search/SearchDataLoader.astro +16 -28
- package/eventcatalog/src/components/Search/SearchModal.tsx +2 -10
- package/eventcatalog/src/components/Search/search-utils.ts +26 -0
- package/eventcatalog/src/components/Seo.astro +5 -1
- package/eventcatalog/src/components/Settings/AssistantSettingsForm.tsx +53 -48
- package/eventcatalog/src/components/Settings/GeneralSettingsForm.tsx +183 -178
- package/eventcatalog/src/components/Settings/LicenseSettings.tsx +140 -31
- package/eventcatalog/src/components/Settings/LlmAccessSettingsForm.tsx +40 -35
- package/eventcatalog/src/components/Settings/LogoUpload.tsx +1 -1
- package/eventcatalog/src/components/Settings/McpSettingsForm.tsx +9 -8
- package/eventcatalog/src/components/Settings/Row.tsx +7 -4
- package/eventcatalog/src/components/Settings/SettingsShared.tsx +3 -3
- package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +37 -76
- package/eventcatalog/src/components/SideNav/NestedSideBar/storage.ts +2 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/utils.ts +122 -1
- package/eventcatalog/src/components/SideNav/SideNav.astro +9 -1
- package/eventcatalog/src/components/TrialBadge.astro +20 -9
- package/eventcatalog/src/content.config.ts +19 -2
- package/eventcatalog/src/features/auth/error.astro +4 -2
- package/eventcatalog/src/features/auth/login.astro +2 -1
- package/eventcatalog/src/features/auth/middleware/middleware-auth.ts +21 -1
- package/eventcatalog/src/features/auth/unauthorized.astro +1 -1
- package/eventcatalog/src/features/fields/pages/fields.astro +1 -1
- package/eventcatalog/src/features/integrations/eventcatalog-features.ts +13 -7
- package/eventcatalog/src/features/mcp/apps/architecture-diagram/node-chat.tsx +6 -45
- package/eventcatalog/src/features/mcp/apps/canvas/shared.ts +3 -3
- package/eventcatalog/src/features/mcp/apps/canvas/styles.css +3 -1
- package/eventcatalog/src/features/mcp/apps/canvas/view.tsx +7 -5
- package/eventcatalog/src/features/mcp/apps/generated/canvas.html +123 -98
- package/eventcatalog/src/features/mcp/apps/generated/viewer.html +118 -101
- package/eventcatalog/src/features/mcp/mcp-server.ts +20 -4
- package/eventcatalog/src/features/server/start.ts +25 -0
- package/eventcatalog/src/features/storage/database.ts +88 -0
- package/eventcatalog/src/features/storage/migrations.ts +85 -0
- package/eventcatalog/src/features/storage/sql/database.sql +5 -0
- package/eventcatalog/src/features/storage/sql/migrations/0001-studio-canvases.sql +12 -0
- package/eventcatalog/src/features/storage/sql/migrations.sql +15 -0
- package/eventcatalog/src/features/storage/sql.ts +28 -0
- package/eventcatalog/src/features/studio/agent-choreography.ts +13 -10
- package/eventcatalog/src/features/studio/agents.ts +28 -0
- package/eventcatalog/src/features/studio/api/canvases-api.ts +219 -0
- package/eventcatalog/src/features/studio/api/canvases.ts +25 -0
- package/eventcatalog/src/features/studio/api/client.ts +40 -0
- package/eventcatalog/src/features/studio/canvas-actions.ts +160 -34
- package/eventcatalog/src/features/studio/canvas-preview.ts +1 -2
- package/eventcatalog/src/features/studio/catalog.ts +16 -14
- package/eventcatalog/src/features/studio/components/CanvasHeader.tsx +274 -105
- package/eventcatalog/src/features/studio/components/CanvasStatusMenu.tsx +15 -14
- package/eventcatalog/src/features/studio/components/ConnectAgentDialog.tsx +272 -119
- package/eventcatalog/src/features/studio/components/ConnectionsChooser.tsx +1 -2
- package/eventcatalog/src/features/studio/components/DeleteCanvasDialog.tsx +65 -0
- package/eventcatalog/src/features/studio/components/Dialog.tsx +60 -0
- package/eventcatalog/src/features/studio/components/JoinForm.tsx +101 -26
- package/eventcatalog/src/features/studio/components/LeftPanel.tsx +122 -230
- package/eventcatalog/src/features/studio/components/NodeDetails.tsx +183 -0
- package/eventcatalog/src/features/studio/components/OpenInStudioDialog.tsx +79 -0
- package/eventcatalog/src/features/studio/components/Presence.tsx +52 -45
- package/eventcatalog/src/features/studio/components/ShareDialog.tsx +277 -141
- package/eventcatalog/src/features/studio/components/ShortcutsDialog.tsx +41 -0
- package/eventcatalog/src/features/studio/components/StudioDesigner.tsx +1314 -385
- package/eventcatalog/src/features/studio/components/StudioNotice.astro +24 -0
- package/eventcatalog/src/features/studio/document-name.ts +7 -0
- package/eventcatalog/src/features/studio/from-visualiser.ts +138 -0
- package/eventcatalog/src/features/studio/hooks/presence-sender.ts +19 -2
- package/eventcatalog/src/features/studio/hooks/use-canvas-webmcp.ts +9 -3
- package/eventcatalog/src/features/studio/hooks/use-comments.ts +2 -2
- package/eventcatalog/src/features/studio/hooks/use-follow-camera.ts +8 -3
- package/eventcatalog/src/features/studio/hooks/use-studio-flow.ts +244 -32
- package/eventcatalog/src/features/studio/identity.ts +25 -1
- package/eventcatalog/src/features/studio/limits.ts +2 -0
- package/eventcatalog/src/features/studio/pages/[id].astro +42 -16
- package/eventcatalog/src/features/studio/pages/index.astro +596 -373
- package/eventcatalog/src/features/studio/pages/webmcp-relay.ts +12 -15
- package/eventcatalog/src/features/studio/server/canvas-mcp.ts +8 -23
- package/eventcatalog/src/features/studio/server/runtime.ts +150 -46
- package/eventcatalog/src/features/studio/server/sql/storage.sql +18 -0
- package/eventcatalog/src/features/studio/server/storage.ts +53 -73
- package/eventcatalog/src/features/studio/webmcp-relay/LICENSE +21 -0
- package/eventcatalog/src/features/studio/webmcp-relay/README.md +5 -0
- package/eventcatalog/src/features/studio/webmcp-relay/embed.js +2 -0
- package/eventcatalog/src/features/studio/webmcp-relay/widget.html +10 -0
- package/eventcatalog/src/features/studio/webmcp-relay/widget.js +1 -0
- package/eventcatalog/src/features/tools/catalog-tools.ts +3 -1
- package/eventcatalog/src/layouts/BaseLayout.astro +4 -4
- package/eventcatalog/src/layouts/SettingsLayout.astro +2 -2
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +3 -2
- package/eventcatalog/src/pages/_index.astro +5 -342
- package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.spec.ts +2 -3
- package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.ts +0 -7
- package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +3 -1
- package/eventcatalog/src/pages/diagrams/[id]/[version]/_index.data.ts +0 -7
- package/eventcatalog/src/pages/diagrams/[id]/[version]/embed.astro +1 -0
- package/eventcatalog/src/pages/diagrams/[id]/[version]/index.astro +1 -0
- package/eventcatalog/src/pages/directory/[type]/_index.data.ts +5 -7
- package/eventcatalog/src/pages/directory/[type]/index.astro +3 -1
- package/eventcatalog/src/pages/discover/[type]/_index.data.ts +5 -7
- package/eventcatalog/src/pages/discover/[type]/index.astro +3 -1
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/_index.data.ts +0 -7
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/index.astro +1 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/_index.data.ts +0 -7
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/index.astro +1 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +0 -7
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/[filename].astro +3 -1
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/_[filename].data.ts +0 -7
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/_index.data.ts +0 -7
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/index.astro +1 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/[...filename].astro +1 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/_[...filename].data.ts +0 -4
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/field-lineage/_index.data.ts +0 -7
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/field-lineage/index.astro +1 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +3 -1
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/_[filename].data.ts +0 -7
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +1 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_index.data.spec.ts +2 -3
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_index.data.ts +0 -7
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/index.astro +1 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/[filename].astro +1 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/_[filename].data.ts +0 -7
- package/eventcatalog/src/pages/docs/[type]/[id]/_index.data.ts +0 -7
- package/eventcatalog/src/pages/docs/[type]/[id]/index.astro +2 -1
- package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/_index.data.ts +0 -7
- package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/index.astro +1 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/language/_index.data.ts +0 -7
- package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +1 -0
- package/eventcatalog/src/pages/docs/custom/[...path]/_index.data.ts +0 -7
- package/eventcatalog/src/pages/docs/custom/[...path]/index.astro +1 -0
- package/eventcatalog/src/pages/docs/services/[id]/[docType]/[docId]/index.astro +1 -1
- package/eventcatalog/src/pages/docs/services/[id]/[version]/resources/index.astro +1 -0
- package/eventcatalog/src/pages/docs/services/[id]/asyncapi/[filename].astro +1 -1
- package/eventcatalog/src/pages/docs/services/[id]/changelog/index.astro +1 -1
- package/eventcatalog/src/pages/docs/services/[id]/graphql/[filename].astro +1 -1
- package/eventcatalog/src/pages/docs/services/[id]/spec/[filename].astro +1 -1
- package/eventcatalog/src/pages/docs/teams/[id]/_index.data.ts +0 -7
- package/eventcatalog/src/pages/docs/teams/[id]/index.astro +1 -0
- package/eventcatalog/src/pages/docs/users/[id]/_index.data.ts +0 -7
- package/eventcatalog/src/pages/docs/users/[id]/index.astro +1 -0
- package/eventcatalog/src/pages/index.astro +4 -2
- package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/_index.data.ts +0 -7
- package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/index.astro +9 -1
- package/eventcatalog/src/pages/schemas/explorer/_index.data.ts +0 -7
- package/eventcatalog/src/pages/schemas/explorer/index.astro +4 -2
- package/eventcatalog/src/pages/settings/license.astro +9 -1
- package/eventcatalog/src/pages/triggers/[type]/[id]/[version]/_index.data.ts +0 -7
- package/eventcatalog/src/pages/triggers/[type]/[id]/[version]/index.astro +1 -0
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/_index.data.ts +0 -7
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/_index.data.ts +0 -7
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/index.astro +1 -0
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/entity-map/_index.data.ts +0 -7
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/entity-map/index.astro +1 -0
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/index.astro +1 -0
- package/eventcatalog/src/pages/visualiser/[type]/[id]/_index.data.ts +38 -0
- package/eventcatalog/src/pages/visualiser/[type]/[id]/index.astro +18 -41
- package/eventcatalog/src/pages/visualiser/designs/[id]/_index.data.ts +0 -7
- package/eventcatalog/src/pages/visualiser/designs/[id]/index.astro +2 -1
- package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/_index.data.ts +0 -7
- package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/index.astro +1 -0
- package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/index.astro +1 -0
- package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/systems-context/_index.data.ts +0 -7
- package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/systems-context/index.astro +1 -0
- package/eventcatalog/src/pages/visualiser/systems/[id]/[version]/context/_index.data.ts +0 -7
- package/eventcatalog/src/pages/visualiser/systems/[id]/[version]/context/index.astro +1 -0
- package/eventcatalog/src/pages/visualiser/systems/[id]/[version]/index.astro +1 -0
- package/eventcatalog/src/types/react-syntax-highlighter.d.ts +10 -0
- package/eventcatalog/src/utils/collections/flows.ts +20 -7
- package/eventcatalog/src/utils/collections/resource-owners.ts +8 -2
- package/eventcatalog/src/utils/collections/resources.ts +2 -0
- package/eventcatalog/src/utils/collections/util.ts +8 -4
- package/eventcatalog/src/utils/git.ts +11 -0
- package/eventcatalog/src/utils/homepage-dashboard.ts +143 -0
- package/eventcatalog/src/utils/license.ts +1 -1
- package/eventcatalog/src/utils/link-validation.ts +25 -19
- package/eventcatalog/src/utils/node-graphs/domain-entity-map.ts +3 -1
- package/eventcatalog/src/utils/node-graphs/domain-levels-node-graph.ts +3 -3
- package/eventcatalog/src/utils/node-graphs/field-node-graph.ts +4 -2
- package/eventcatalog/src/utils/node-graphs/layout-node-graph.ts +20 -1
- package/eventcatalog/src/utils/node-graphs/node-resource.ts +67 -0
- package/eventcatalog/src/utils/node-graphs/systems-node-graph.ts +1 -1
- package/eventcatalog/src/utils/page-loaders/hybrid-page.ts +4 -15
- package/eventcatalog/src/utils/page-title.ts +15 -0
- package/eventcatalog/src/utils/syntax-highlighter-renderer.ts +64 -0
- package/eventcatalog/src/utils/trial.ts +13 -0
- package/package.json +11 -11
- package/dist/docs/development/ask-your-architecture/05-skills/02-installation.md +0 -60
- package/dist/docs/development/ask-your-architecture/05-skills/_category_.json +0 -11
- package/dist/docs/development/ask-your-architecture/06-slack-integration/01-introduction.md +0 -63
- package/dist/docs/development/ask-your-architecture/06-slack-integration/02-slack-app-setup.md +0 -154
- package/dist/docs/development/ask-your-architecture/06-slack-integration/03-installation.md +0 -169
- package/dist/docs/development/ask-your-architecture/06-slack-integration/04-deployment.md +0 -236
- package/dist/docs/development/ask-your-architecture/06-slack-integration/05-usage.md +0 -140
- package/dist/docs/development/ask-your-architecture/06-slack-integration/06-troubleshooting.md +0 -268
- package/dist/docs/development/ask-your-architecture/06-slack-integration/_category_.json +0 -13
- package/dist/docs/development/guides/domains/06-visualize-domains/01-domain-integration-map.md +0 -40
- package/dist/runtime-dependencies/@mcp-b__webmcp-local-relay.mjs +0 -3
- package/eventcatalog/src/features/studio/canvas-doc.ts +0 -409
- package/eventcatalog/src/features/studio/clipboard.ts +0 -97
- package/eventcatalog/src/features/studio/components/CanvasControls.tsx +0 -154
- package/eventcatalog/src/features/studio/components/Comments.tsx +0 -770
- package/eventcatalog/src/features/studio/components/ContainerChooser.tsx +0 -145
- package/eventcatalog/src/features/studio/components/DragGhost.tsx +0 -115
- package/eventcatalog/src/features/studio/components/Picture.tsx +0 -21
- package/eventcatalog/src/features/studio/components/PropertiesPanel.tsx +0 -212
- package/eventcatalog/src/features/studio/components/canvas-nodes.tsx +0 -255
- package/eventcatalog/src/features/studio/components/icons.ts +0 -53
- package/eventcatalog/src/features/studio/components/status.ts +0 -51
- package/eventcatalog/src/features/studio/edges.ts +0 -56
- package/eventcatalog/src/features/studio/flow-state.ts +0 -92
- package/eventcatalog/src/features/studio/grouping.ts +0 -226
- package/eventcatalog/src/features/studio/layout.ts +0 -42
- package/eventcatalog/src/features/studio/levels.ts +0 -282
- package/eventcatalog/src/features/studio/node-types.ts +0 -205
- package/eventcatalog/src/features/studio/pages/new.astro +0 -24
- package/eventcatalog/src/features/studio/pages/wake.ts +0 -15
- package/eventcatalog/src/features/studio/placement.ts +0 -197
- package/eventcatalog/src/features/studio/studio.css +0 -273
- package/dist/{chunk-A2RZR3U4.js → chunk-6K7XZAYI.js} +3 -3
|
@@ -41,6 +41,8 @@ type McpServerOptions = {
|
|
|
41
41
|
catalogUrl: string;
|
|
42
42
|
/** The MCP client's user agent, to name agents that join a canvas */
|
|
43
43
|
userAgent?: string;
|
|
44
|
+
/** Offer the canvas tools (by default when Studio is on, for the whole catalog) */
|
|
45
|
+
canvases?: boolean;
|
|
44
46
|
};
|
|
45
47
|
|
|
46
48
|
const catalogDirectory = process.env.PROJECT_DIR || process.cwd();
|
|
@@ -120,7 +122,10 @@ function getServerInstructions(scope?: McpScope) {
|
|
|
120
122
|
}
|
|
121
123
|
|
|
122
124
|
// Create MCP Server with tools that access Astro collections
|
|
123
|
-
function createMcpServer(
|
|
125
|
+
function createMcpServer(
|
|
126
|
+
scope: McpScope | undefined,
|
|
127
|
+
{ catalogUrl, userAgent, canvases = !scope && isCanvasEnabled() }: McpServerOptions
|
|
128
|
+
) {
|
|
124
129
|
const server = new McpServer(
|
|
125
130
|
{
|
|
126
131
|
name: scope ? `EventCatalog MCP Server — ${scope.name} ${scope.ref.kind}` : 'EventCatalog MCP Server',
|
|
@@ -837,7 +842,7 @@ function createMcpServer(scope: McpScope | undefined, { catalogUrl, userAgent }:
|
|
|
837
842
|
}
|
|
838
843
|
|
|
839
844
|
// Collaborative canvases (when the collaboration server runs in this process)
|
|
840
|
-
if (
|
|
845
|
+
if (canvases) registerCanvasTools(server, { catalogUrl, userAgent });
|
|
841
846
|
|
|
842
847
|
return server;
|
|
843
848
|
}
|
|
@@ -993,11 +998,22 @@ const handleMcpRequest = async (c: Context, kind?: McpScopeKind) => {
|
|
|
993
998
|
}
|
|
994
999
|
|
|
995
1000
|
const scope = await resolveRequestScope(c, kind);
|
|
996
|
-
// Canvas tools work on Studio's canvases: started (storage loaded) before they're listed
|
|
997
|
-
|
|
1001
|
+
// Canvas tools work on Studio's canvases: started (storage loaded) before they're listed, and left out when
|
|
1002
|
+
// Studio isn't available (the rest of the catalog's tools still work)
|
|
1003
|
+
const canvases =
|
|
1004
|
+
!scope &&
|
|
1005
|
+
isCanvasEnabled() &&
|
|
1006
|
+
(await startStudio().then(
|
|
1007
|
+
() => true,
|
|
1008
|
+
(error) => {
|
|
1009
|
+
console.error(`[studio] Canvas tools are off: ${(error as Error).message}`);
|
|
1010
|
+
return false;
|
|
1011
|
+
}
|
|
1012
|
+
));
|
|
998
1013
|
const server = createMcpServer(scope, {
|
|
999
1014
|
catalogUrl: new URL(c.req.url).origin,
|
|
1000
1015
|
userAgent: c.req.header('user-agent'),
|
|
1016
|
+
canvases,
|
|
1001
1017
|
});
|
|
1002
1018
|
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
1003
1019
|
sessionIdGenerator: undefined,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { APIRoute } from 'astro';
|
|
2
|
+
import { isCanvasEnabled } from '@utils/feature';
|
|
3
|
+
import { startStorage } from '../storage/database';
|
|
4
|
+
import { startStudio } from '../studio/server/runtime';
|
|
5
|
+
import { isSignedIn } from '../studio/server/sign-in';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* What the server starts before anyone asks for it: `eventcatalog start` calls this once the server listens. Each
|
|
9
|
+
* feature that needs starting has its own start function, called here in order: the storage configured is opened and
|
|
10
|
+
* migrated first (features keep their state in it), then Studio (so tabs reconnecting after a restart find it
|
|
11
|
+
* ready). Under /_eventcatalog, which sign-in lets through (the server calls it without a session); it says nothing
|
|
12
|
+
* about what's stored.
|
|
13
|
+
*/
|
|
14
|
+
export const GET: APIRoute = async () => {
|
|
15
|
+
try {
|
|
16
|
+
await startStorage();
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error(`[eventcatalog] Could not open the database: ${(error as Error).message}`);
|
|
19
|
+
}
|
|
20
|
+
// Studio says why if it can't start
|
|
21
|
+
if (isCanvasEnabled()) await startStudio({ isSignedIn }).catch(() => {});
|
|
22
|
+
return new Response(null, { status: 204 });
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const prerender = false;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import config from '../../utils/eventcatalog-config/source';
|
|
4
|
+
import type { Config } from '../../../../src/eventcatalog.config';
|
|
5
|
+
import { migrate, type Database } from './migrations';
|
|
6
|
+
import { namedStatements } from './sql';
|
|
7
|
+
import databaseSql from './sql/database.sql?raw';
|
|
8
|
+
|
|
9
|
+
/** `storage` in eventcatalog.config.js: memory (the default), or one SQLite database for the whole catalog */
|
|
10
|
+
export type StorageConfig = NonNullable<Config['storage']>;
|
|
11
|
+
export type StorageType = StorageConfig['type'];
|
|
12
|
+
export type { Database };
|
|
13
|
+
|
|
14
|
+
export const DEFAULT_STORAGE: StorageConfig = { type: 'memory' };
|
|
15
|
+
const DEFAULT_DATABASE_PATH = '.eventcatalog/eventcatalog.db';
|
|
16
|
+
|
|
17
|
+
const SQL = namedStatements(databaseSql, ['useWriteAheadLog']);
|
|
18
|
+
|
|
19
|
+
/** The database file a config asks for (relative to the catalog), or undefined when state is kept in memory */
|
|
20
|
+
export const getDatabasePath = (storage: StorageConfig | undefined, projectDirectory: string) => {
|
|
21
|
+
const chosen = storage ?? DEFAULT_STORAGE;
|
|
22
|
+
switch (chosen.type) {
|
|
23
|
+
case 'memory':
|
|
24
|
+
return undefined;
|
|
25
|
+
case 'sqlite':
|
|
26
|
+
return path.resolve(projectDirectory, chosen.path ?? DEFAULT_DATABASE_PATH);
|
|
27
|
+
default:
|
|
28
|
+
throw new Error(`Unknown storage type "${(chosen as { type?: string }).type}": use "memory" or "sqlite"`);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const open = async (file: string): Promise<Database> => {
|
|
33
|
+
// Node's built-in SQLite (no native package to install), asked of Node itself: the dev server loads this through
|
|
34
|
+
// Vite, which doesn't know node:sqlite and fails to import it
|
|
35
|
+
type Sqlite = { DatabaseSync: new (file: string) => Database };
|
|
36
|
+
const getBuiltinModule = (process as { getBuiltinModule?: (id: string) => unknown }).getBuiltinModule;
|
|
37
|
+
let sqlite: Sqlite | undefined;
|
|
38
|
+
try {
|
|
39
|
+
sqlite = getBuiltinModule
|
|
40
|
+
? (getBuiltinModule('node:sqlite') as Sqlite | undefined)
|
|
41
|
+
: await import(/* @vite-ignore */ ['node', 'sqlite'].join(':'));
|
|
42
|
+
} catch (error) {
|
|
43
|
+
throw new Error(`Could not load node:sqlite (${(error as Error).message})`);
|
|
44
|
+
}
|
|
45
|
+
if (!sqlite?.DatabaseSync) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`SQLite storage needs Node.js 22.13 or later (it uses node:sqlite); this is Node.js ${process.versions.node}`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
51
|
+
const database = new sqlite.DatabaseSync(file);
|
|
52
|
+
database.exec(SQL.useWriteAheadLog);
|
|
53
|
+
migrate(database);
|
|
54
|
+
return database;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Opened once per process, by file (kept on globalThis: the dev server's integrations and the app load this module
|
|
58
|
+
// separately, and code reloads in dev keep globalThis)
|
|
59
|
+
const DATABASES = Symbol.for('eventcatalog.databases');
|
|
60
|
+
const store = globalThis as typeof globalThis & { [DATABASES]?: Map<string, Promise<Database>> };
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The database in `file`, brought up to date by the migrations: opened the first time it's asked for, then shared by
|
|
64
|
+
* every feature. Rejects (every time) if it can't be opened or migrated, e.g. when a newer EventCatalog updated it.
|
|
65
|
+
*/
|
|
66
|
+
export const openDatabase = (file: string): Promise<Database> => {
|
|
67
|
+
const databases = (store[DATABASES] ??= new Map());
|
|
68
|
+
let database = databases.get(file);
|
|
69
|
+
if (!database) {
|
|
70
|
+
database = open(file);
|
|
71
|
+
databases.set(file, database);
|
|
72
|
+
}
|
|
73
|
+
return database;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Opens the storage configured, running the migrations it needs. Called as the dev server and `eventcatalog start`
|
|
78
|
+
* start (not on build: builds often run somewhere other than where the database lives), so it's ready before any
|
|
79
|
+
* feature uses it. Returns the database file, or undefined when state is kept in memory.
|
|
80
|
+
*/
|
|
81
|
+
export const startStorage = async (projectDirectory = process.env.PROJECT_DIR || process.cwd()) => {
|
|
82
|
+
if ((config as { studio?: { storage?: unknown } }).studio?.storage) {
|
|
83
|
+
console.warn('[eventcatalog] studio.storage is no longer used: set storage in eventcatalog.config.js instead');
|
|
84
|
+
}
|
|
85
|
+
const file = getDatabasePath(config.storage, projectDirectory);
|
|
86
|
+
if (file) await openDatabase(file);
|
|
87
|
+
return file;
|
|
88
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { namedStatements } from './sql';
|
|
2
|
+
import runnerSql from './sql/migrations.sql?raw';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Changes to the database's schema, for every feature that keeps state, as .sql files in ./sql/migrations named
|
|
6
|
+
* `NNNN-what-it-does.sql` (one numbered order for the whole catalog; tables are named for their feature, e.g.
|
|
7
|
+
* `studio_canvases`). They run in order when the database opens, each once, recorded in its `migrations` table. To
|
|
8
|
+
* change the schema, add a file with the next number: never change one that has shipped, since databases out there have
|
|
9
|
+
* already run it.
|
|
10
|
+
*/
|
|
11
|
+
export type Migration = { version: number; name: string; sql: string };
|
|
12
|
+
|
|
13
|
+
/** The parts of node:sqlite's DatabaseSync EventCatalog uses */
|
|
14
|
+
export type Database = {
|
|
15
|
+
exec: (sql: string) => void;
|
|
16
|
+
prepare: (sql: string) => { run: (...values: unknown[]) => unknown; all: () => unknown[] };
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Built into the server code (no files to find at runtime, wherever it's deployed)
|
|
20
|
+
const files = import.meta.glob<string>('./sql/migrations/*.sql', { query: '?raw', import: 'default', eager: true });
|
|
21
|
+
const SQL = namedStatements(runnerSql, ['createMigrationsTable', 'appliedVersions', 'recordMigration']);
|
|
22
|
+
|
|
23
|
+
/** Migrations from their files, in order (an error if a file is misnamed or two have the same number) */
|
|
24
|
+
export const toMigrations = (sources: Record<string, string>): Migration[] => {
|
|
25
|
+
const migrations = Object.entries(sources).map(([file, sql]) => {
|
|
26
|
+
const name = file
|
|
27
|
+
.split('/')
|
|
28
|
+
.pop()!
|
|
29
|
+
.replace(/\.sql$/, '');
|
|
30
|
+
const version = Number(/^(\d{4})-[a-z0-9-]+$/.exec(name)?.[1]);
|
|
31
|
+
if (!version) throw new Error(`Migration "${file}" should be named like 0003-what-it-does.sql`);
|
|
32
|
+
return { version, name, sql };
|
|
33
|
+
});
|
|
34
|
+
migrations.sort((a, b) => a.version - b.version);
|
|
35
|
+
migrations.forEach((migration, index) => {
|
|
36
|
+
if (migration.version !== index + 1) {
|
|
37
|
+
throw new Error(`Migrations should be numbered 1, 2, 3...: expected ${index + 1} before ${migration.name}`);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
return migrations;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const MIGRATIONS = toMigrations(files);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The database was updated by a newer EventCatalog. Features don't fall back to memory for this (changes would quietly
|
|
47
|
+
* stop being saved): they aren't available until EventCatalog is updated. Told by its code, as the dev server's
|
|
48
|
+
* integrations and the app load this module separately.
|
|
49
|
+
*/
|
|
50
|
+
const DATABASE_TOO_NEW = 'EVENTCATALOG_DATABASE_TOO_NEW';
|
|
51
|
+
export const isDatabaseTooNew = (error: unknown) => (error as { code?: unknown } | null)?.code === DATABASE_TOO_NEW;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Brings a database up to date: runs the migrations it hasn't run yet, each in a transaction (all of it or none).
|
|
55
|
+
* Refuses a database that has run migrations this version doesn't know (made by a newer EventCatalog).
|
|
56
|
+
*/
|
|
57
|
+
export const migrate = (database: Database, migrations: Migration[] = MIGRATIONS) => {
|
|
58
|
+
database.exec(SQL.createMigrationsTable);
|
|
59
|
+
const applied = new Set(
|
|
60
|
+
(database.prepare(SQL.appliedVersions).all() as { version: number }[]).map((row) => Number(row.version))
|
|
61
|
+
);
|
|
62
|
+
const latest = migrations.at(-1)?.version ?? 0;
|
|
63
|
+
const newer = [...applied].filter((version) => version > latest);
|
|
64
|
+
if (newer.length) {
|
|
65
|
+
throw Object.assign(
|
|
66
|
+
new Error(
|
|
67
|
+
`The EventCatalog database has been updated by a newer EventCatalog (migration ${Math.max(...newer)}; this version knows up to ${latest}). Update EventCatalog to use it.`
|
|
68
|
+
),
|
|
69
|
+
{ code: DATABASE_TOO_NEW }
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
const record = database.prepare(SQL.recordMigration);
|
|
73
|
+
for (const migration of migrations) {
|
|
74
|
+
if (applied.has(migration.version)) continue;
|
|
75
|
+
database.exec('BEGIN');
|
|
76
|
+
try {
|
|
77
|
+
database.exec(migration.sql);
|
|
78
|
+
record.run(migration.version, migration.name, Date.now());
|
|
79
|
+
database.exec('COMMIT');
|
|
80
|
+
} catch (error) {
|
|
81
|
+
database.exec('ROLLBACK');
|
|
82
|
+
throw new Error(`Could not run migration ${migration.name}: ${(error as Error).message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
-- Statements run as the database opens (see database.ts), each named by the `-- name:` line above it.
|
|
2
|
+
|
|
3
|
+
-- name: useWriteAheadLog
|
|
4
|
+
-- Readers don't wait for writers (set as the database opens: it can't be changed inside a migration's transaction)
|
|
5
|
+
PRAGMA journal_mode = WAL;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
-- Studio's canvases: each one's whole Yjs document (Y.encodeStateAsUpdate), and when it last changed.
|
|
2
|
+
CREATE TABLE studio_canvases (
|
|
3
|
+
name TEXT PRIMARY KEY,
|
|
4
|
+
state BLOB NOT NULL,
|
|
5
|
+
updated_at INTEGER NOT NULL
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
-- Studio canvases that were deleted, so a browser that still has one open can't bring it back.
|
|
9
|
+
CREATE TABLE studio_deleted_canvases (
|
|
10
|
+
name TEXT PRIMARY KEY,
|
|
11
|
+
deleted_at INTEGER NOT NULL
|
|
12
|
+
);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- The statements the migration runner runs (see migrations.ts), each named by the `-- name:` line above it. Its own
|
|
2
|
+
-- table is made here rather than in a migration: it records which migrations have run, so it's needed before any do.
|
|
3
|
+
|
|
4
|
+
-- name: createMigrationsTable
|
|
5
|
+
CREATE TABLE IF NOT EXISTS migrations (
|
|
6
|
+
version INTEGER PRIMARY KEY,
|
|
7
|
+
name TEXT NOT NULL,
|
|
8
|
+
applied_at INTEGER NOT NULL
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
-- name: appliedVersions
|
|
12
|
+
SELECT version FROM migrations;
|
|
13
|
+
|
|
14
|
+
-- name: recordMigration
|
|
15
|
+
INSERT INTO migrations (version, name, applied_at) VALUES (?, ?, ?);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Statements kept in .sql files (./sql), each named by a `-- name: <name>` line above it. A file's statements are
|
|
3
|
+
* looked up by the names the code expects: a missing one is an error when the module loads, not when it's first run.
|
|
4
|
+
*/
|
|
5
|
+
export const namedStatements = <Name extends string>(source: string, names: readonly Name[]): Record<Name, string> => {
|
|
6
|
+
const statements = new Map<string, string>();
|
|
7
|
+
let current: { name: string; lines: string[] } | undefined;
|
|
8
|
+
const finish = () => {
|
|
9
|
+
if (current) statements.set(current.name, current.lines.join('\n').trim());
|
|
10
|
+
};
|
|
11
|
+
for (const line of source.split('\n')) {
|
|
12
|
+
const name = /^--\s*name:\s*(\S+)\s*$/.exec(line)?.[1];
|
|
13
|
+
if (name) {
|
|
14
|
+
finish();
|
|
15
|
+
current = { name, lines: [] };
|
|
16
|
+
} else {
|
|
17
|
+
current?.lines.push(line);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
finish();
|
|
21
|
+
return Object.fromEntries(
|
|
22
|
+
names.map((name) => {
|
|
23
|
+
const statement = statements.get(name);
|
|
24
|
+
if (!statement) throw new Error(`No SQL statement named "${name}"`);
|
|
25
|
+
return [name, statement];
|
|
26
|
+
})
|
|
27
|
+
) as Record<Name, string>;
|
|
28
|
+
};
|
|
@@ -9,11 +9,16 @@ import {
|
|
|
9
9
|
fitContainersAround,
|
|
10
10
|
getCanvasMaps,
|
|
11
11
|
moveNode,
|
|
12
|
-
|
|
12
|
+
placeNode,
|
|
13
|
+
setEdgeRoutes,
|
|
13
14
|
setMeta,
|
|
14
15
|
type Author,
|
|
15
16
|
type CommentAnchor,
|
|
16
|
-
|
|
17
|
+
getAbsolutePosition,
|
|
18
|
+
sizeOf,
|
|
19
|
+
type LayoutResult,
|
|
20
|
+
getNodeName,
|
|
21
|
+
} from '@eventcatalog/visualiser/studio';
|
|
17
22
|
import {
|
|
18
23
|
describeNode,
|
|
19
24
|
nodeCenter,
|
|
@@ -27,9 +32,6 @@ import {
|
|
|
27
32
|
type NodeUpdate,
|
|
28
33
|
} from './canvas-actions';
|
|
29
34
|
import { getRelatedEdges } from './catalog';
|
|
30
|
-
import { getAbsolutePosition, sizeOf } from './grouping';
|
|
31
|
-
import type { LayoutResult } from './layout';
|
|
32
|
-
import { getNodeName } from './node-types';
|
|
33
35
|
|
|
34
36
|
/**
|
|
35
37
|
* Agents work on a canvas like a person would: their cursor travels to where they're going, they drop one node,
|
|
@@ -292,13 +294,14 @@ export async function playLayout(stage: AgentStage, layout: LayoutResult) {
|
|
|
292
294
|
hand.say('Done');
|
|
293
295
|
}
|
|
294
296
|
|
|
295
|
-
/**
|
|
297
|
+
/**
|
|
298
|
+
* Moves nodes, resizes containers and routes connections from a layout, in one change, and marks when it
|
|
299
|
+
* happened. Connections the layout didn't route lose their old route (drawn as a plain step instead).
|
|
300
|
+
*/
|
|
296
301
|
export const applyLayout = (doc: Y.Doc, layout: LayoutResult) =>
|
|
297
302
|
doc.transact(() => {
|
|
298
|
-
layout.forEach(({ position, size }, id) =>
|
|
299
|
-
|
|
300
|
-
if (size) resizeNode(doc, id, size);
|
|
301
|
-
});
|
|
303
|
+
layout.nodes.forEach(({ position, size }, id) => placeNode(doc, id, position, size));
|
|
304
|
+
setEdgeRoutes(doc, layout.routes);
|
|
302
305
|
setMeta(doc, { layoutAt: Date.now() });
|
|
303
306
|
});
|
|
304
307
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who an agent is, named after its client when it doesn't say, in its brand's colour: on the server (MCP tools) and
|
|
3
|
+
* in a chat's canvas view (which joins as the chat's agent). Their logos are in the visualiser's ClientLogos.
|
|
4
|
+
*/
|
|
5
|
+
export type AgentIdentity = { name: string; color: string };
|
|
6
|
+
|
|
7
|
+
const AGENT_COLORS: Record<string, string> = {
|
|
8
|
+
ChatGPT: '#10a37f',
|
|
9
|
+
Codex: '#10a37f',
|
|
10
|
+
Claude: '#d97757',
|
|
11
|
+
'Claude Code': '#d97757',
|
|
12
|
+
Cursor: '#111827',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/** Agents' clients, by what's in their user agent or host name (most specific first) */
|
|
16
|
+
const CLIENTS: [RegExp, string][] = [
|
|
17
|
+
[/claude[-_ ]?code/i, 'Claude Code'],
|
|
18
|
+
[/claude|anthropic/i, 'Claude'],
|
|
19
|
+
[/codex/i, 'Codex'],
|
|
20
|
+
[/openai|chatgpt/i, 'ChatGPT'],
|
|
21
|
+
[/cursor/i, 'Cursor'],
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
/** Who the agent is, from the name it gives, else its client's user agent (or a chat's host name) */
|
|
25
|
+
export const getAgent = (agentName: string | undefined, client = ''): AgentIdentity => {
|
|
26
|
+
const name = agentName?.trim() || CLIENTS.find(([pattern]) => pattern.test(client))?.[1] || 'AI agent';
|
|
27
|
+
return { name, color: AGENT_COLORS[name] ?? '#8b5cf6' };
|
|
28
|
+
};
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { Hono, type Context } from 'hono';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { paginate } from '@features/tools/catalog-tools';
|
|
4
|
+
import { type CanvasContent, type CanvasStatus } from '@eventcatalog/visualiser/studio';
|
|
5
|
+
import { MAX_TITLE_LENGTH } from '../limits';
|
|
6
|
+
import { diagramLevelsSchema, indexCatalog, newCanvasEdgesSchema, nodeSpecsSchema, planCanvas } from '../canvas-actions';
|
|
7
|
+
import { getCatalogResources } from '../catalog-resources';
|
|
8
|
+
import { MAX_NAME_LENGTH } from '../identity';
|
|
9
|
+
import type { CanvasSummary, StudioRuntime } from '../server/runtime';
|
|
10
|
+
import type { SignedInUser } from '../server/sign-in';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The Studio API: canvases as a whole (listing, creating, reading and deleting them). What's on a canvas is changed
|
|
14
|
+
* over the collaboration connection, or by agents through the MCP tools, never here: they're live documents that
|
|
15
|
+
* people and agents edit together.
|
|
16
|
+
*
|
|
17
|
+
* GET /api/studio/canvases Canvases, most recently changed first (?pageSize=, ?cursor= for the next page)
|
|
18
|
+
* (not ones left empty and untitled: GET /:id still finds them)
|
|
19
|
+
* POST /api/studio/canvases Creates one: { "title"?: string, "createdBy"?: string, "nodes"?: [...],
|
|
20
|
+
* "edges"?: [...] }, starting with the nodes and edges given, if any (as the
|
|
21
|
+
* add_to_canvas MCP tool takes them: catalog resources or new components, x/y
|
|
22
|
+
* their centres, and only the connections in edges are made, each drawn
|
|
23
|
+
* along its "route" if given: { "points": [{x, y}, ...], "label"?: {x, y} }),
|
|
24
|
+
* and "levels" (L1 and L2 laid out, e.g. as a diagram shows them, by the
|
|
25
|
+
* nodes' refs), shown as given until what's on the canvas changes
|
|
26
|
+
* GET /api/studio/canvases/:id One canvas
|
|
27
|
+
* POST /api/studio/canvases/:id/copy Copies its design (nodes and connections, not comments or status) to a new
|
|
28
|
+
* draft: { "title"?: string, "createdBy"?: string }
|
|
29
|
+
* DELETE /api/studio/canvases/:id Deletes it for everyone (anyone who has it open is told), for good
|
|
30
|
+
*
|
|
31
|
+
* Errors are `{ "error": "..." }` with the HTTP status. With sign-in on, only signed-in people can use it (401
|
|
32
|
+
* otherwise), and canvases are created by whoever is signed in. Requests that change something (POST, DELETE) from
|
|
33
|
+
* outside a browser send `Content-Type: application/json`: Astro refuses ones with no content type that don't come
|
|
34
|
+
* from the catalog's own pages.
|
|
35
|
+
*/
|
|
36
|
+
export const CANVASES_API_PATH = '/api/studio/canvases';
|
|
37
|
+
|
|
38
|
+
const CANVAS_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
39
|
+
const MAX_PAGE_SIZE = 100;
|
|
40
|
+
|
|
41
|
+
/** A canvas, as the API gives it */
|
|
42
|
+
export type ApiCanvas = {
|
|
43
|
+
id: string;
|
|
44
|
+
title: string | null;
|
|
45
|
+
status: CanvasStatus;
|
|
46
|
+
/** ISO 8601 */
|
|
47
|
+
createdAt: string | null;
|
|
48
|
+
/** Who started it (a person, or an agent) */
|
|
49
|
+
createdBy: string | null;
|
|
50
|
+
/** When anyone (a person, or an agent) last changed it (ISO 8601) */
|
|
51
|
+
updatedAt: string | null;
|
|
52
|
+
nodeCount: number;
|
|
53
|
+
edgeCount: number;
|
|
54
|
+
openComments: number;
|
|
55
|
+
/** Where people open it */
|
|
56
|
+
url: string;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type CanvasesApiEnv = {
|
|
60
|
+
Bindings: {
|
|
61
|
+
runtime: StudioRuntime;
|
|
62
|
+
/** Who's signed in (sign-in on) */
|
|
63
|
+
signedInAs?: SignedInUser;
|
|
64
|
+
/** A canvas's page */
|
|
65
|
+
canvasUrl: (canvasId: string) => string;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const MAX_NODES = 1000;
|
|
70
|
+
const MAX_EDGES = 2000;
|
|
71
|
+
|
|
72
|
+
const NewCanvas = z.object({
|
|
73
|
+
title: z.string().trim().max(MAX_TITLE_LENGTH).optional(),
|
|
74
|
+
// Ignored when someone's signed in: it's them
|
|
75
|
+
createdBy: z.string().trim().max(MAX_NAME_LENGTH).optional(),
|
|
76
|
+
});
|
|
77
|
+
// A new canvas can start with nodes and edges; a copy starts with the original's
|
|
78
|
+
const NewCanvasWithContent = NewCanvas.extend({
|
|
79
|
+
nodes: nodeSpecsSchema.max(MAX_NODES).optional(),
|
|
80
|
+
edges: newCanvasEdgesSchema.max(MAX_EDGES).optional(),
|
|
81
|
+
levels: diagramLevelsSchema.optional(),
|
|
82
|
+
}).refine((canvas) => !canvas.edges?.length || canvas.nodes, {
|
|
83
|
+
message: 'Edges connect the nodes given with them: give nodes too',
|
|
84
|
+
path: ['edges'],
|
|
85
|
+
});
|
|
86
|
+
const ListQuery = z.object({
|
|
87
|
+
cursor: z.string().optional(),
|
|
88
|
+
pageSize: z.coerce.number().int().min(1).max(MAX_PAGE_SIZE).optional(),
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const iso = (time?: number) => (time ? new Date(time).toISOString() : null);
|
|
92
|
+
const toApiCanvas = (canvas: CanvasSummary, canvasUrl: (canvasId: string) => string): ApiCanvas => ({
|
|
93
|
+
id: canvas.canvasId,
|
|
94
|
+
title: canvas.title ?? null,
|
|
95
|
+
status: canvas.status,
|
|
96
|
+
createdAt: iso(canvas.createdAt),
|
|
97
|
+
createdBy: canvas.createdBy ?? null,
|
|
98
|
+
updatedAt: iso(canvas.updatedAt),
|
|
99
|
+
nodeCount: canvas.nodeCount,
|
|
100
|
+
edgeCount: canvas.edgeCount,
|
|
101
|
+
openComments: canvas.openComments,
|
|
102
|
+
url: canvasUrl(canvas.canvasId),
|
|
103
|
+
});
|
|
104
|
+
const describeIssues = (error: z.ZodError) =>
|
|
105
|
+
error.issues.map((issue) => (issue.path.length ? `${issue.path.join('.')}: ${issue.message}` : issue.message)).join('; ');
|
|
106
|
+
|
|
107
|
+
type ApiContext = Context<CanvasesApiEnv>;
|
|
108
|
+
const noCanvas = (c: ApiContext, id: string) => c.json({ error: `No canvas "${id}"` }, 404);
|
|
109
|
+
|
|
110
|
+
type NewCanvasDetails = { title?: string; createdBy?: string };
|
|
111
|
+
|
|
112
|
+
/** The request's JSON body (or {} without one), or the response saying what's wrong with it */
|
|
113
|
+
const readJson = async (c: ApiContext): Promise<unknown> => {
|
|
114
|
+
// JSON only: a page on another site can send a form or plain text here without asking first, but not JSON
|
|
115
|
+
const body = await c.req.text();
|
|
116
|
+
if (body && !c.req.header('content-type')?.toLowerCase().startsWith('application/json')) {
|
|
117
|
+
return c.json({ error: 'Send the canvas as JSON (Content-Type: application/json)' }, 415);
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
return body ? JSON.parse(body) : {};
|
|
121
|
+
} catch {
|
|
122
|
+
return c.json({ error: 'The body is not valid JSON' }, 400);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const detailsOf = (c: ApiContext, canvas: z.output<typeof NewCanvas>): NewCanvasDetails => ({
|
|
127
|
+
title: canvas.title || undefined,
|
|
128
|
+
// Whoever's signed in, if anyone
|
|
129
|
+
createdBy: c.env.signedInAs?.name ?? (canvas.createdBy || undefined),
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
/** A copy's details from the request (optional), or the response saying what's wrong with them */
|
|
133
|
+
const readCopy = async (c: ApiContext): Promise<NewCanvasDetails | Response> => {
|
|
134
|
+
const json = await readJson(c);
|
|
135
|
+
if (json instanceof Response) return json;
|
|
136
|
+
const parsed = NewCanvas.safeParse(json);
|
|
137
|
+
if (!parsed.success) return c.json({ error: describeIssues(parsed.error) }, 400);
|
|
138
|
+
return detailsOf(c, parsed.data);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/** A new canvas's details and what it starts with (all optional), or the response saying what's wrong with them */
|
|
142
|
+
const readNewCanvas = async (c: ApiContext): Promise<{ details: NewCanvasDetails; content?: CanvasContent } | Response> => {
|
|
143
|
+
const json = await readJson(c);
|
|
144
|
+
if (json instanceof Response) return json;
|
|
145
|
+
const parsed = NewCanvasWithContent.safeParse(json);
|
|
146
|
+
if (!parsed.success) return c.json({ error: describeIssues(parsed.error) }, 400);
|
|
147
|
+
const details = detailsOf(c, parsed.data);
|
|
148
|
+
if (!parsed.data.nodes) return { details };
|
|
149
|
+
const { resources, relations } = await getCatalogResources();
|
|
150
|
+
const { content, errors } = planCanvas(
|
|
151
|
+
parsed.data.nodes,
|
|
152
|
+
parsed.data.edges ?? [],
|
|
153
|
+
indexCatalog(resources, relations),
|
|
154
|
+
parsed.data.levels
|
|
155
|
+
);
|
|
156
|
+
if (errors.length) return c.json({ error: errors.join('; ') }, 400);
|
|
157
|
+
return { details, content };
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/** The API, served under `basePath` (the catalog's base URL, then CANVASES_API_PATH) */
|
|
161
|
+
export const createCanvasesApi = (basePath: string) => {
|
|
162
|
+
const app = new Hono<CanvasesApiEnv>({ strict: false }).basePath(basePath);
|
|
163
|
+
|
|
164
|
+
app.get('/', (c) => {
|
|
165
|
+
const query = ListQuery.safeParse(c.req.query());
|
|
166
|
+
if (!query.success) return c.json({ error: describeIssues(query.error) }, 400);
|
|
167
|
+
const { runtime, canvasUrl } = c.env;
|
|
168
|
+
const page = paginate(runtime.listCanvases(), query.data.cursor, query.data.pageSize);
|
|
169
|
+
if ('error' in page) return c.json({ error: page.error }, 400);
|
|
170
|
+
return c.json({
|
|
171
|
+
canvases: page.items.map((canvas) => toApiCanvas(canvas, canvasUrl)),
|
|
172
|
+
totalCount: page.totalCount,
|
|
173
|
+
...(page.nextCursor && { nextCursor: page.nextCursor }),
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
/** A canvas just made, as created */
|
|
178
|
+
const created = (c: ApiContext, canvasId: string) => {
|
|
179
|
+
c.header('Location', `${basePath}/${canvasId}`);
|
|
180
|
+
return c.json({ canvas: toApiCanvas(c.env.runtime.getCanvas(canvasId)!, c.env.canvasUrl) }, 201);
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
app.post('/', async (c) => {
|
|
184
|
+
const canvas = await readNewCanvas(c);
|
|
185
|
+
if (canvas instanceof Response) return canvas;
|
|
186
|
+
return created(c, await c.env.runtime.createCanvas(canvas.details, canvas.content));
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
app.post('/:id/copy', async (c) => {
|
|
190
|
+
const id = c.req.param('id');
|
|
191
|
+
if (!CANVAS_ID.test(id)) return noCanvas(c, id);
|
|
192
|
+
const details = await readCopy(c);
|
|
193
|
+
if (details instanceof Response) return details;
|
|
194
|
+
const copyId = await c.env.runtime.copyCanvas(id, details);
|
|
195
|
+
return copyId ? created(c, copyId) : noCanvas(c, id);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
app.get('/:id', (c) => {
|
|
199
|
+
const id = c.req.param('id');
|
|
200
|
+
const canvas = CANVAS_ID.test(id) ? c.env.runtime.getCanvas(id) : undefined;
|
|
201
|
+
if (!canvas) return noCanvas(c, id);
|
|
202
|
+
return c.json({ canvas: toApiCanvas(canvas, c.env.canvasUrl) });
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
app.delete('/:id', (c) => {
|
|
206
|
+
const id = c.req.param('id');
|
|
207
|
+
if (!CANVAS_ID.test(id)) return noCanvas(c, id);
|
|
208
|
+
try {
|
|
209
|
+
if (!c.env.runtime.deleteCanvas(id)) return noCanvas(c, id);
|
|
210
|
+
} catch (error) {
|
|
211
|
+
console.error(`[studio] Could not delete canvas ${id}`, error);
|
|
212
|
+
return c.json({ error: `Could not delete the canvas: ${(error as Error).message}` }, 500);
|
|
213
|
+
}
|
|
214
|
+
return c.body(null, 204);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
app.notFound((c) => c.json({ error: 'Not found' }, 404));
|
|
218
|
+
return app;
|
|
219
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { APIRoute } from 'astro';
|
|
2
|
+
import { buildUrl } from '@utils/url-builder';
|
|
3
|
+
import { startStudio } from '../server/runtime';
|
|
4
|
+
import { getSignedInUser, isSignedIn } from '../server/sign-in';
|
|
5
|
+
import { CANVASES_API_PATH, createCanvasesApi } from './canvases-api';
|
|
6
|
+
|
|
7
|
+
// The Studio API (see canvases-api.ts)
|
|
8
|
+
const api = createCanvasesApi(buildUrl(CANVASES_API_PATH, true));
|
|
9
|
+
|
|
10
|
+
export const ALL: APIRoute = async ({ request, locals, url }) => {
|
|
11
|
+
let runtime: Awaited<ReturnType<typeof startStudio>>;
|
|
12
|
+
try {
|
|
13
|
+
runtime = await startStudio({ isSignedIn });
|
|
14
|
+
} catch (error) {
|
|
15
|
+
// Its storage can't be used (e.g. the database was updated by a newer EventCatalog)
|
|
16
|
+
return Response.json({ error: `Studio isn't available: ${(error as Error).message}` }, { status: 503 });
|
|
17
|
+
}
|
|
18
|
+
return api.fetch(request, {
|
|
19
|
+
runtime,
|
|
20
|
+
signedInAs: getSignedInUser(locals),
|
|
21
|
+
canvasUrl: (canvasId: string) => `${url.origin}${buildUrl(`/studio/${canvasId}`)}`,
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const prerender = false;
|