@eventcatalog/core 4.10.15 → 4.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/dist/analytics/analytics.cjs +1 -1
  2. package/dist/analytics/analytics.js +2 -2
  3. package/dist/analytics/log-build.cjs +1 -1
  4. package/dist/analytics/log-build.js +3 -3
  5. package/dist/astro-output.cjs +60 -1
  6. package/dist/astro-output.js +1 -1
  7. package/dist/{chunk-U52FCZQ6.js → chunk-2YJRF4ZX.js} +1 -1
  8. package/dist/chunk-634NFV5U.js +75 -0
  9. package/dist/{chunk-I6HUOX4R.js → chunk-AXDNSGAN.js} +1 -1
  10. package/dist/chunk-DHH5U5PF.js +53 -0
  11. package/dist/{chunk-A4JRWRCV.js → chunk-DSCIWCX2.js} +1 -1
  12. package/dist/chunk-FWXEV4LF.js +19 -0
  13. package/dist/{chunk-Q2ATWE7N.js → chunk-GZVDXCYF.js} +1 -1
  14. package/dist/{chunk-7LWOCX4R.js → chunk-RYNPEIHQ.js} +1 -1
  15. package/dist/{chunk-HBVTU6ZP.js → chunk-YYVW4DCO.js} +1 -1
  16. package/dist/constants.cjs +1 -1
  17. package/dist/constants.js +1 -1
  18. package/dist/copy-core.cjs +88 -0
  19. package/dist/copy-core.d.cts +4 -0
  20. package/dist/copy-core.d.ts +4 -0
  21. package/dist/copy-core.js +8 -0
  22. package/dist/eventcatalog.cjs +234 -115
  23. package/dist/eventcatalog.js +36 -30
  24. package/dist/federation/federate.js +3 -3
  25. package/dist/federation/source-provider.js +2 -2
  26. package/dist/generate.cjs +1 -1
  27. package/dist/generate.js +3 -3
  28. package/dist/update-check.cjs +44 -0
  29. package/dist/update-check.d.cts +18 -0
  30. package/dist/update-check.d.ts +18 -0
  31. package/dist/update-check.js +8 -0
  32. package/dist/utils/cli-logger.cjs +1 -1
  33. package/dist/utils/cli-logger.js +2 -2
  34. package/eventcatalog/astro.config.mjs +8 -1
  35. package/eventcatalog/public/icons/languages/android.svg +1 -0
  36. package/eventcatalog/public/icons/languages/angular.svg +1 -0
  37. package/eventcatalog/public/icons/languages/apple.svg +1 -0
  38. package/eventcatalog/public/icons/languages/arduino.svg +1 -0
  39. package/eventcatalog/public/icons/languages/bash.svg +1 -0
  40. package/eventcatalog/public/icons/languages/bun.svg +1 -0
  41. package/eventcatalog/public/icons/languages/c.svg +1 -0
  42. package/eventcatalog/public/icons/languages/clojure.svg +1 -0
  43. package/eventcatalog/public/icons/languages/cpp.svg +1 -0
  44. package/eventcatalog/public/icons/languages/crystal.svg +1 -0
  45. package/eventcatalog/public/icons/languages/csharp.svg +1 -0
  46. package/eventcatalog/public/icons/languages/css.svg +1 -0
  47. package/eventcatalog/public/icons/languages/curl.svg +1 -0
  48. package/eventcatalog/public/icons/languages/dart.svg +1 -0
  49. package/eventcatalog/public/icons/languages/deno.svg +1 -0
  50. package/eventcatalog/public/icons/languages/docker.svg +1 -0
  51. package/eventcatalog/public/icons/languages/elm.svg +1 -0
  52. package/eventcatalog/public/icons/languages/erlang.svg +1 -0
  53. package/eventcatalog/public/icons/languages/flutter.svg +1 -0
  54. package/eventcatalog/public/icons/languages/fortran.svg +1 -0
  55. package/eventcatalog/public/icons/languages/fsharp.svg +1 -0
  56. package/eventcatalog/public/icons/languages/git.svg +1 -0
  57. package/eventcatalog/public/icons/languages/gradle.svg +1 -0
  58. package/eventcatalog/public/icons/languages/graphql.svg +1 -0
  59. package/eventcatalog/public/icons/languages/grpc.svg +1 -0
  60. package/eventcatalog/public/icons/languages/haskell.svg +1 -0
  61. package/eventcatalog/public/icons/languages/hasura.svg +1 -0
  62. package/eventcatalog/public/icons/languages/html.svg +1 -0
  63. package/eventcatalog/public/icons/languages/httpie.svg +1 -0
  64. package/eventcatalog/public/icons/languages/javascript.svg +1 -0
  65. package/eventcatalog/public/icons/languages/json-schema.svg +1 -0
  66. package/eventcatalog/public/icons/languages/json.svg +1 -0
  67. package/eventcatalog/public/icons/languages/julia.svg +1 -0
  68. package/eventcatalog/public/icons/languages/jupyter.svg +1 -0
  69. package/eventcatalog/public/icons/languages/kafka.svg +1 -0
  70. package/eventcatalog/public/icons/languages/kotlin.svg +1 -0
  71. package/eventcatalog/public/icons/languages/lua.svg +1 -0
  72. package/eventcatalog/public/icons/languages/markdown.svg +1 -0
  73. package/eventcatalog/public/icons/languages/maven.svg +1 -0
  74. package/eventcatalog/public/icons/languages/mongodb.svg +1 -0
  75. package/eventcatalog/public/icons/languages/mysql.svg +1 -0
  76. package/eventcatalog/public/icons/languages/nginx.svg +1 -0
  77. package/eventcatalog/public/icons/languages/nim.svg +1 -0
  78. package/eventcatalog/public/icons/languages/ocaml.svg +1 -0
  79. package/eventcatalog/public/icons/languages/perl.svg +1 -0
  80. package/eventcatalog/public/icons/languages/php.svg +1 -0
  81. package/eventcatalog/public/icons/languages/postgresql.svg +1 -0
  82. package/eventcatalog/public/icons/languages/prisma.svg +1 -0
  83. package/eventcatalog/public/icons/languages/purescript.svg +1 -0
  84. package/eventcatalog/public/icons/languages/python.svg +1 -0
  85. package/eventcatalog/public/icons/languages/r.svg +1 -0
  86. package/eventcatalog/public/icons/languages/react.svg +1 -0
  87. package/eventcatalog/public/icons/languages/reasonml.svg +1 -0
  88. package/eventcatalog/public/icons/languages/redis.svg +1 -0
  89. package/eventcatalog/public/icons/languages/ruby.svg +1 -0
  90. package/eventcatalog/public/icons/languages/rust.svg +1 -0
  91. package/eventcatalog/public/icons/languages/sass.svg +1 -0
  92. package/eventcatalog/public/icons/languages/scala.svg +1 -0
  93. package/eventcatalog/public/icons/languages/solidity.svg +1 -0
  94. package/eventcatalog/public/icons/languages/spring.svg +1 -0
  95. package/eventcatalog/public/icons/languages/sqlite.svg +1 -0
  96. package/eventcatalog/public/icons/languages/svelte.svg +1 -0
  97. package/eventcatalog/public/icons/languages/swagger.svg +1 -0
  98. package/eventcatalog/public/icons/languages/swift.svg +1 -0
  99. package/eventcatalog/public/icons/languages/tailwind.svg +1 -0
  100. package/eventcatalog/public/icons/languages/terraform.svg +1 -0
  101. package/eventcatalog/public/icons/languages/toml.svg +1 -0
  102. package/eventcatalog/public/icons/languages/typescript.svg +1 -0
  103. package/eventcatalog/public/icons/languages/unity.svg +1 -0
  104. package/eventcatalog/public/icons/languages/vim.svg +1 -0
  105. package/eventcatalog/public/icons/languages/vue.svg +1 -0
  106. package/eventcatalog/public/icons/languages/webassembly.svg +1 -0
  107. package/eventcatalog/public/icons/languages/yaml.svg +1 -0
  108. package/eventcatalog/public/icons/languages/zig.svg +1 -0
  109. package/eventcatalog/src/components/MDX/CodeGroup/CodeGroup.astro +12 -0
  110. package/eventcatalog/src/components/MDX/CodeGroup/CodeGroup.tsx +176 -0
  111. package/eventcatalog/src/components/MDX/CodeGroup/MarkdownCodeGroup.tsx +22 -0
  112. package/eventcatalog/src/components/MDX/CodeGroup/code-group-icons.ts +345 -0
  113. package/eventcatalog/src/components/MDX/CodeGroup/code-group.css +22 -0
  114. package/eventcatalog/src/components/MDX/CodeGroup/parse-panels.ts +11 -0
  115. package/eventcatalog/src/components/MDX/Columns/Column.astro +10 -0
  116. package/eventcatalog/src/components/MDX/Columns/Columns.astro +14 -0
  117. package/eventcatalog/src/components/MDX/Columns/MarkdownColumns.tsx +37 -0
  118. package/eventcatalog/src/components/MDX/Columns/layout.ts +16 -0
  119. package/eventcatalog/src/components/MDX/components.tsx +6 -0
  120. package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +1 -1
  121. package/eventcatalog/src/components/SchemaExplorer/ExamplesViewer.tsx +151 -114
  122. package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +510 -248
  123. package/eventcatalog/src/components/SchemaExplorer/SchemaFlows.tsx +76 -0
  124. package/eventcatalog/src/components/SchemaExplorer/SchemaGraph.tsx +47 -0
  125. package/eventcatalog/src/components/SchemaExplorer/SchemaPageViewer.tsx +11 -0
  126. package/eventcatalog/src/components/SchemaExplorer/SchemaUsage.tsx +17 -0
  127. package/eventcatalog/src/components/SchemaExplorer/types.ts +48 -3
  128. package/eventcatalog/src/components/SchemaExplorer/utils.ts +28 -1
  129. package/eventcatalog/src/content.config.ts +28 -0
  130. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/[...filename].astro +38 -7
  131. package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/index.astro +144 -37
  132. package/eventcatalog/src/pages/schemas/explorer/index.astro +5 -0
  133. package/eventcatalog/src/plugins/empty-collection-warning.d.ts +21 -0
  134. package/eventcatalog/src/plugins/empty-collection-warning.mjs +107 -0
  135. package/eventcatalog/src/plugins/quiet-empty-collections-logger.mjs +11 -0
  136. package/eventcatalog/src/remark-plugins/code-group.ts +39 -0
  137. package/eventcatalog/src/remark-plugins/markdown-code-group.ts +59 -0
  138. package/eventcatalog/src/remark-plugins/markdown-columns.ts +66 -0
  139. package/eventcatalog/src/utils/collections/channels.ts +12 -4
  140. package/eventcatalog/src/utils/collections/examples.ts +133 -41
  141. package/eventcatalog/src/utils/collections/flows.ts +43 -0
  142. package/eventcatalog/src/utils/collections/messages.ts +2 -2
  143. package/eventcatalog/src/utils/collections/schemas.ts +15 -0
  144. package/eventcatalog/src/utils/node-graphs/message-node-graph.ts +17 -15
  145. package/eventcatalog/src/utils/schema-explorer.ts +73 -7
  146. package/eventcatalog/src/utils/schema-usage-graph.ts +92 -0
  147. package/package.json +5 -4
  148. package/dist/chunk-RDYYZQYU.js +0 -18
  149. package/eventcatalog/src/components/EnvironmentDropdown.test.ts +0 -64
  150. package/eventcatalog/src/components/Grids/message-link.spec.ts +0 -59
  151. package/eventcatalog/src/components/MDX/CustomProperties/custom-properties.spec.ts +0 -38
  152. package/eventcatalog/src/components/MDX/SchemaViewer/schema-viewer-utils.spec.ts +0 -116
  153. package/eventcatalog/src/components/SchemaExplorer/JSONSchemaViewer.test.tsx +0 -114
  154. package/eventcatalog/src/components/SchemaExplorer/ProtobufSchemaViewer.test.tsx +0 -91
  155. package/eventcatalog/src/components/SchemaExplorer/SchemaViewerHover.test.tsx +0 -33
  156. package/eventcatalog/src/components/SchemaExplorer/protobuf-validation.test.ts +0 -84
  157. package/eventcatalog/src/components/Search/search-utils.spec.ts +0 -173
  158. package/eventcatalog/src/components/SideNav/NestedSideBar/utils.spec.ts +0 -105
  159. package/eventcatalog/src/components/navigation-progress.spec.ts +0 -68
  160. package/eventcatalog/src/enterprise/directory/user-team-directory.spec.ts +0 -527
  161. package/eventcatalog/src/enterprise/fields/field-extractor.test.ts +0 -302
  162. package/eventcatalog/src/enterprise/fields/fields-db.test.ts +0 -185
  163. package/eventcatalog/src/stores/eventcatalog-store.spec.ts +0 -60
  164. package/eventcatalog/src/utils/collections/glob-loader.spec.ts +0 -87
  165. package/dist/{chunk-6K7XZAYI.js → chunk-A2RZR3U4.js} +3 -3
@@ -0,0 +1,76 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { ArrowTopRightOnSquareIcon, ChevronDownIcon } from '@heroicons/react/24/outline';
3
+ import SchemaGraph from './SchemaGraph';
4
+ import type { FlowUsage, SchemaItem } from './types';
5
+ import { getSchemaRelationshipHref } from './utils';
6
+
7
+ interface SchemaFlowsProps {
8
+ message: SchemaItem;
9
+ flows: FlowUsage[];
10
+ }
11
+
12
+ const flowKey = (flow: FlowUsage) => `${flow.id}-${flow.version}`;
13
+
14
+ /** The diagram of a flow the message appears in, with a picker when it appears in several. */
15
+ export default function SchemaFlows({ message, flows }: SchemaFlowsProps) {
16
+ const [selectedKey, setSelectedKey] = useState(() => (flows[0] ? flowKey(flows[0]) : ''));
17
+ // Keep the selection valid when the message (and so the list of flows) changes.
18
+ useEffect(() => {
19
+ if (!flows.some((flow) => flowKey(flow) === selectedKey)) setSelectedKey(flows[0] ? flowKey(flows[0]) : '');
20
+ }, [flows, selectedKey]);
21
+
22
+ const selected = flows.find((flow) => flowKey(flow) === selectedKey) ?? flows[0];
23
+ if (!selected) return null;
24
+
25
+ return (
26
+ <div className="flex h-full flex-col overflow-auto pr-1">
27
+ <div className="mb-4 flex shrink-0 flex-wrap items-center gap-3">
28
+ {flows.length > 1 ? (
29
+ <label className="relative inline-flex items-center">
30
+ <span className="sr-only">Flow</span>
31
+ <select
32
+ value={flowKey(selected)}
33
+ onChange={(event) => setSelectedKey(event.target.value)}
34
+ className="appearance-none rounded-md border border-[rgb(var(--ec-input-border))] bg-[rgb(var(--ec-input-bg))] py-1.5 pl-3 pr-8 text-sm font-medium text-[rgb(var(--ec-input-text))] focus:outline-none focus:ring-2 focus:ring-[rgb(var(--ec-accent)/0.4)]"
35
+ >
36
+ {flows.map((flow) => (
37
+ <option key={flowKey(flow)} value={flowKey(flow)}>
38
+ {flow.name || flow.id} (v{flow.version})
39
+ </option>
40
+ ))}
41
+ </select>
42
+ <ChevronDownIcon className="pointer-events-none absolute right-2 h-4 w-4 text-[rgb(var(--ec-icon-color))]" />
43
+ </label>
44
+ ) : (
45
+ <span className="text-sm font-semibold text-[rgb(var(--ec-page-text))]">
46
+ {selected.name || selected.id}
47
+ <span className="ml-2 font-mono text-[11px] font-normal text-[rgb(var(--ec-page-text-muted))]">
48
+ v{selected.version}
49
+ </span>
50
+ </span>
51
+ )}
52
+ <a
53
+ href={getSchemaRelationshipHref({ id: selected.id, version: selected.version, collection: 'flows' })}
54
+ className="inline-flex items-center gap-1 text-xs font-medium text-[rgb(var(--ec-accent))] hover:underline"
55
+ >
56
+ Open flow
57
+ <ArrowTopRightOnSquareIcon className="h-3.5 w-3.5" />
58
+ </a>
59
+ {selected.summary && <span className="basis-full text-xs text-[rgb(var(--ec-page-text-muted))]">{selected.summary}</span>}
60
+ </div>
61
+
62
+ {selected.graph ? (
63
+ <SchemaGraph
64
+ key={flowKey(selected)}
65
+ id={`schema-flow-${message.collection}-${message.data.id}-${message.data.version}-${flowKey(selected)}`}
66
+ graph={selected.graph}
67
+ showLegend={false}
68
+ />
69
+ ) : (
70
+ <p className="rounded-xl border border-dashed border-[rgb(var(--ec-page-border))] px-4 py-6 text-center text-xs text-[rgb(var(--ec-page-text-muted))]">
71
+ The diagram for this flow could not be built.
72
+ </p>
73
+ )}
74
+ </div>
75
+ );
76
+ }
@@ -0,0 +1,47 @@
1
+ import { useEffect, useState } from 'react';
2
+ import AstroNodeGraph from '@components/MDX/NodeGraph/AstroNodeGraph';
3
+ import type { MessageUsageGraph } from '@utils/schema-usage-graph';
4
+
5
+ interface SchemaGraphProps {
6
+ /** Unique on the page: it names the portal element the visualiser mounts into. */
7
+ id: string;
8
+ graph: MessageUsageGraph;
9
+ /** CSS height of the graph box. Omit to fill the remaining height of a flex column parent. */
10
+ height?: string;
11
+ showLegend?: boolean;
12
+ }
13
+
14
+ /**
15
+ * A visualiser node graph inside the schema panel. The visualiser is browser-only, so the
16
+ * graph mounts after hydration into the portal element rendered here; the box keeps its
17
+ * height meanwhile so the tab does not jump.
18
+ */
19
+ export default function SchemaGraph({ id, graph, height, showLegend = true }: SchemaGraphProps) {
20
+ const [mounted, setMounted] = useState(false);
21
+ useEffect(() => setMounted(true), []);
22
+ const portalId = `${id}-portal`;
23
+
24
+ return (
25
+ <>
26
+ <div
27
+ id={portalId}
28
+ className={`not-prose relative w-full overflow-hidden rounded-xl border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-card-bg))] ${height ? '' : 'min-h-[20em] flex-1'}`}
29
+ style={height ? { height } : undefined}
30
+ />
31
+ {mounted && (
32
+ <AstroNodeGraph
33
+ id={id}
34
+ nodes={graph.nodes}
35
+ edges={graph.edges}
36
+ mode="simple"
37
+ linkTo="docs"
38
+ showSearch={false}
39
+ includeKey={showLegend}
40
+ zoomOnScroll={false}
41
+ portalId={portalId}
42
+ disableMessageAnimation={true}
43
+ />
44
+ )}
45
+ </>
46
+ );
47
+ }
@@ -1,3 +1,4 @@
1
+ import type { ReactNode } from 'react';
1
2
  import SchemaDetailsPanel from './SchemaDetailsPanel';
2
3
  import type { SchemaItem } from './types';
3
4
 
@@ -7,6 +8,11 @@ interface SchemaPageViewerProps {
7
8
  apiAccessEnabled?: boolean;
8
9
  showOwners?: boolean;
9
10
  showProducersConsumers?: boolean;
11
+ /** Tab requested in the URL, resolved on the server to avoid a flash of the default tab. */
12
+ initialTab?: string;
13
+ /** Examples rendered on the server through the MDX pipeline, passed as the `examples` slot. */
14
+ examples?: ReactNode;
15
+ hasRenderedExamples?: boolean;
10
16
  }
11
17
 
12
18
  export default function SchemaPageViewer({
@@ -15,6 +21,9 @@ export default function SchemaPageViewer({
15
21
  apiAccessEnabled = false,
16
22
  showOwners = true,
17
23
  showProducersConsumers = true,
24
+ initialTab,
25
+ examples,
26
+ hasRenderedExamples = false,
18
27
  }: SchemaPageViewerProps) {
19
28
  const handleVersionChange = (version: string) => {
20
29
  // Construct new URL
@@ -32,6 +41,8 @@ export default function SchemaPageViewer({
32
41
  apiAccessEnabled={apiAccessEnabled}
33
42
  showOwners={showOwners}
34
43
  showProducersConsumers={showProducersConsumers}
44
+ initialTab={initialTab}
45
+ renderedExamples={hasRenderedExamples ? examples : undefined}
35
46
  />
36
47
  );
37
48
  }
@@ -0,0 +1,17 @@
1
+ import SchemaGraph from './SchemaGraph';
2
+ import type { SchemaItem } from './types';
3
+
4
+ /** Producers, consumers and channels of the message, drawn with the same node graph as the docs page. */
5
+ export default function SchemaUsage({ message }: { message: SchemaItem }) {
6
+ return (
7
+ <div className="flex h-full flex-col overflow-auto pr-1">
8
+ {message.graph ? (
9
+ <SchemaGraph id={`schema-usage-${message.collection}-${message.data.id}-${message.data.version}`} graph={message.graph} />
10
+ ) : (
11
+ <p className="rounded-xl border border-dashed border-[rgb(var(--ec-page-border))] px-4 py-6 text-center text-xs text-[rgb(var(--ec-page-text-muted))]">
12
+ No producers or consumers declared for this message.
13
+ </p>
14
+ )}
15
+ </div>
16
+ );
17
+ }
@@ -1,15 +1,33 @@
1
1
  import type { CollectionMessageTypes } from '@types';
2
2
  import type { MessageExample } from '@utils/collections/examples';
3
+ import type { MessageUsageGraph } from '@utils/schema-usage-graph';
3
4
  export type { MessageExample };
4
5
 
5
- export interface Producer {
6
+ /** A resource related to a message: something that produces or consumes it, or a flow that includes it. */
7
+ export type SchemaRelationshipCollection = 'services' | 'agents' | 'data-products' | 'flows';
8
+
9
+ export interface SchemaRelationship {
6
10
  id: string;
7
11
  version: string;
12
+ /** Which kind of resource produces or consumes the message. Defaults to a service when unknown. */
13
+ collection?: SchemaRelationshipCollection;
14
+ name?: string;
15
+ summary?: string;
16
+ }
17
+
18
+ export type Producer = SchemaRelationship;
19
+ export type Consumer = SchemaRelationship;
20
+
21
+ export interface FlowUsage extends SchemaRelationship {
22
+ /** The flow's diagram, as rendered by the visualiser. */
23
+ graph?: MessageUsageGraph;
8
24
  }
9
25
 
10
- export interface Consumer {
26
+ /** A channel the message is published to. */
27
+ export interface MessageChannel {
11
28
  id: string;
12
29
  version: string;
30
+ name?: string;
13
31
  }
14
32
 
15
33
  export interface Owner {
@@ -19,6 +37,21 @@ export interface Owner {
19
37
  href: string;
20
38
  }
21
39
 
40
+ /** Where a remote schema was resolved from. Absent for schema files stored in the catalog. */
41
+ export interface SchemaSourceInfo {
42
+ provider: string;
43
+ /** Browsable link to the schema, when the source provides one. */
44
+ url?: string;
45
+ repository?: string;
46
+ path?: string;
47
+ ref?: string;
48
+ commit?: string;
49
+ /** ISO 8601 timestamp of when the schema was first created in the source. */
50
+ createdAt?: string;
51
+ /** ISO 8601 timestamp of when the schema was last updated in the source. */
52
+ updatedAt?: string;
53
+ }
54
+
22
55
  export interface SchemaItem {
23
56
  collection: CollectionMessageTypes | 'services' | 'domains' | 'data-products';
24
57
  data: {
@@ -29,6 +62,10 @@ export interface SchemaItem {
29
62
  schemaPath?: string;
30
63
  producers?: Producer[];
31
64
  consumers?: Consumer[];
65
+ /** Flows with a step that sends or receives this message. */
66
+ flows?: FlowUsage[];
67
+ /** Channels the message is published to. */
68
+ channels?: MessageChannel[];
32
69
  producerName?: string;
33
70
  owners?: Owner[];
34
71
  };
@@ -36,6 +73,11 @@ export interface SchemaItem {
36
73
  /** Internal URL for loading this version's content without embedding it in the page. */
37
74
  contentUrl?: string;
38
75
  schemaExtension?: string;
76
+ /** The schema reference used by the message, for example `source://contracts/events/OrderPlaced.schema.json`. */
77
+ schemaRef?: string;
78
+ /** Display name of the schema entry itself, as declared on the message or reported by the source. */
79
+ schemaName?: string;
80
+ source?: SchemaSourceInfo;
39
81
  specType?: string;
40
82
  specName?: string;
41
83
  specFilenameWithoutExtension?: string;
@@ -45,12 +87,15 @@ export interface SchemaItem {
45
87
  dataProductVersion?: string;
46
88
  // Examples
47
89
  examples?: MessageExample[];
90
+ /** Producer → message → consumer graph for the Usage tab, as rendered by the visualiser. */
91
+ graph?: MessageUsageGraph;
48
92
  }
49
93
 
50
94
  export interface SchemaDetails {
51
95
  schemaContent: string;
52
96
  examples: MessageExample[];
53
- data?: Pick<SchemaItem['data'], 'producers' | 'consumers'>;
97
+ graph?: MessageUsageGraph;
98
+ data?: Pick<SchemaItem['data'], 'producers' | 'consumers' | 'flows' | 'channels'>;
54
99
  }
55
100
 
56
101
  export interface VersionDiff {
@@ -1,3 +1,6 @@
1
+ import type { SchemaRelationshipCollection } from './types';
2
+ import { buildUrl } from '@utils/url-builder';
3
+
1
4
  // Schema types that should use SVG icons
2
5
  export const ICON_SPECS: Record<string, string> = {
3
6
  openapi: 'openapi',
@@ -45,14 +48,38 @@ export function extractServiceName(refId: string): string {
45
48
  export function getSchemaRelationshipReference(reference: {
46
49
  id: string;
47
50
  version?: string;
48
- data?: { id: string; version: string };
51
+ collection?: string;
52
+ name?: string;
53
+ summary?: string;
54
+ data?: { id: string; version: string; name?: string; summary?: string };
49
55
  }) {
56
+ const collection = (
57
+ reference.collection === 'agents' || reference.collection === 'data-products' || reference.collection === 'flows'
58
+ ? reference.collection
59
+ : 'services'
60
+ ) as SchemaRelationshipCollection;
50
61
  return {
51
62
  id: reference.data?.id ?? reference.id,
52
63
  version: reference.data?.version ?? reference.version,
64
+ collection,
65
+ name: reference.data?.name ?? reference.name,
66
+ summary: reference.data?.summary ?? reference.summary,
53
67
  };
54
68
  }
55
69
 
70
+ export const SCHEMA_RELATIONSHIP_LABELS: Record<SchemaRelationshipCollection, string> = {
71
+ services: 'Service',
72
+ agents: 'Agent',
73
+ 'data-products': 'Data Product',
74
+ flows: 'Flow',
75
+ };
76
+
77
+ export const getSchemaRelationshipHref = (reference: {
78
+ id: string;
79
+ version?: string;
80
+ collection: SchemaRelationshipCollection;
81
+ }) => buildUrl(`/docs/${reference.collection}/${reference.id}/${reference.version}`);
82
+
56
83
  export const getLanguageForHighlight = (extension?: string): string => {
57
84
  if (!extension) return 'json';
58
85
  const ext = extension.toLowerCase();
@@ -1193,6 +1193,10 @@ const schemas = defineCollection({
1193
1193
  url: z.string().optional(),
1194
1194
  ref: z.string().optional(),
1195
1195
  branch: z.string().optional(),
1196
+ repository: z.string().optional(),
1197
+ commit: z.string().optional(),
1198
+ createdAt: z.string().optional(),
1199
+ updatedAt: z.string().optional(),
1196
1200
  }),
1197
1201
  readOnly: z.boolean().optional(),
1198
1202
  })
@@ -1266,10 +1270,34 @@ const sidebars = defineCollection({
1266
1270
  schema: sidebarSchema,
1267
1271
  });
1268
1272
 
1273
+ /**
1274
+ * Usage examples: Markdown or MDX files inside a message's `examples/` folder. Loaded as a
1275
+ * collection so they render through the MDX pipeline with EventCatalog's custom components.
1276
+ */
1277
+ const examples = defineCollection({
1278
+ loader: globWithSafeWatcher({
1279
+ pattern: withIgnoredBuildArtifacts([
1280
+ '**/events/*/examples/**/*.(md|mdx)',
1281
+ '**/events/*/versioned/*/examples/**/*.(md|mdx)',
1282
+ '**/commands/*/examples/**/*.(md|mdx)',
1283
+ '**/commands/*/versioned/*/examples/**/*.(md|mdx)',
1284
+ '**/queries/*/examples/**/*.(md|mdx)',
1285
+ '**/queries/*/versioned/*/examples/**/*.(md|mdx)',
1286
+ ]),
1287
+ base: projectDirBase,
1288
+ generateId: ({ entry }) => entry,
1289
+ }),
1290
+ schema: z.object({
1291
+ title: z.string().optional(),
1292
+ summary: z.string().optional(),
1293
+ }),
1294
+ });
1295
+
1269
1296
  export const collections = {
1270
1297
  events,
1271
1298
  commands,
1272
1299
  queries,
1300
+ examples,
1273
1301
  services,
1274
1302
  agents,
1275
1303
  adrs,
@@ -1,9 +1,12 @@
1
1
  ---
2
- import { Code } from 'astro-expressive-code/components';
2
+ import { marked } from 'marked';
3
+ import path from 'node:path';
4
+ import { getCollection, render } from 'astro:content';
5
+ import components from '@components/MDX/components';
6
+ import { getExampleEntriesForMessage } from '@utils/collections/examples';
3
7
  import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
4
8
  import Footer from '@layouts/Footer.astro';
5
9
  import { Page } from './_[...filename].data';
6
- import { getLanguageForHighlight } from '@components/SchemaExplorer/utils';
7
10
 
8
11
  export const prerender = Page.prerender;
9
12
  export const getStaticPaths = Page.getStaticPaths;
@@ -12,7 +15,13 @@ const props = await Page.getData(Astro);
12
15
  const { collection, data, example } = props;
13
16
 
14
17
  const pageTitle = `${collection} | ${data.name} | ${example.title}`.replace(/^\w/, (c: string) => c.toUpperCase());
15
- const lang = getLanguageForHighlight(example.extension);
18
+ // Prefer the MDX pipeline so EventCatalog components work; fall back to plain Markdown.
19
+ const examplesDir = path.join(path.dirname(props.filePath ?? ''), 'examples');
20
+ const exampleEntry = getExampleEntriesForMessage(await getCollection('examples'), props.filePath).find(
21
+ (entry) => path.relative(examplesDir, entry.filePath ?? '').replace(/\\/g, '/') === example.fileName
22
+ );
23
+ const Content = exampleEntry ? (await render(exampleEntry)).Content : undefined;
24
+ const exampleHtml = !Content && example.renderMode === 'markdown' ? await marked.parse(example.content) : '';
16
25
  ---
17
26
 
18
27
  <VerticalSideBarLayout title={pageTitle} description={`Example: ${example.title} for ${data.name}`}>
@@ -24,14 +33,36 @@ const lang = getLanguageForHighlight(example.extension);
24
33
  {example.title}
25
34
  </h1>
26
35
  <h2 class="text-lg text-[rgb(var(--ec-page-text-muted))] font-medium mb-1">
27
- Example for {data.name} <span class="text-[rgb(var(--ec-page-text-muted))]">(v{data.version})</span>
36
+ Example for {data.name}
37
+ <span class="text-[rgb(var(--ec-page-text-muted))]">(v{data.version})</span>
28
38
  </h2>
29
39
  </div>
30
40
 
31
41
  <div class="mt-6">
32
- <div class="not-prose overflow-x-auto">
33
- <Code code={example.content} title={example.fileName} lang={lang} />
34
- </div>
42
+ {example.summary && <p class="mb-4 text-[rgb(var(--ec-page-text-muted))]">{example.summary}</p>}
43
+ {
44
+ Content ? (
45
+ <div class="prose prose-md max-w-none">
46
+ <Content components={components(props)} />
47
+ </div>
48
+ ) : example.renderMode === 'markdown' ? (
49
+ <div class="prose prose-md max-w-none" set:html={exampleHtml} />
50
+ ) : (
51
+ <pre class="overflow-auto rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-code-bg))] p-5 text-sm">
52
+ <code>{example.content}</code>
53
+ </pre>
54
+ )
55
+ }
56
+ {
57
+ example.usage && (
58
+ <section class="mt-6">
59
+ <h2 class="mb-2 text-sm font-semibold uppercase tracking-wider text-[rgb(var(--ec-page-text-muted))]">Usage</h2>
60
+ <pre class="overflow-auto rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-code-bg))] p-5 text-sm">
61
+ <code>{example.usage}</code>
62
+ </pre>
63
+ </section>
64
+ )
65
+ }
35
66
  </div>
36
67
 
37
68
  <Footer />