@eventcatalog/core 5.5.1 → 5.6.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.
Files changed (61) 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 +5 -5
  5. package/dist/{chunk-6KR5ZLEQ.js → chunk-7TTNTHHN.js} +10 -10
  6. package/dist/{chunk-K5QKWVI3.js → chunk-DEO4QEGO.js} +1 -1
  7. package/dist/{chunk-GPIKYBQ5.js → chunk-ETS4G6RR.js} +1 -1
  8. package/dist/{chunk-GZLVDQPM.js → chunk-T3U27UXC.js} +1 -1
  9. package/dist/{chunk-EBZQJP7F.js → chunk-XBRKEQ66.js} +1 -1
  10. package/dist/constants.cjs +1 -1
  11. package/dist/constants.js +1 -1
  12. package/dist/docs/development/customization/05-resource-sidebar.md +1 -1
  13. package/dist/docs/development/guides/domains/09-reference.md +5 -1
  14. package/dist/docs/development/guides/resources/schemas/03-add-specifications-to-services/04-add-generic-specifications.md +139 -0
  15. package/dist/docs/development/guides/resources/schemas/03-add-specifications-to-services/_category_.json +1 -1
  16. package/dist/docs/development/guides/resources/services/08-reference.md +6 -1
  17. package/dist/eventcatalog.cjs +1 -1
  18. package/dist/eventcatalog.js +10 -10
  19. package/dist/generate.cjs +1 -1
  20. package/dist/generate.js +3 -3
  21. package/dist/utils/cli-logger.cjs +1 -1
  22. package/dist/utils/cli-logger.js +2 -2
  23. package/eventcatalog/public/icons/specification.svg +1 -0
  24. package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +38 -20
  25. package/eventcatalog/src/components/Grids/specification-utils.ts +7 -1
  26. package/eventcatalog/src/components/MDX/ResourceRef/ResourceRef.astro +1 -1
  27. package/eventcatalog/src/components/SchemaExplorer/SchemaCodeBlock.tsx +58 -0
  28. package/eventcatalog/src/components/SchemaExplorer/SchemaCodeModal.tsx +2 -1
  29. package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +38 -76
  30. package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +15 -11
  31. package/eventcatalog/src/components/SchemaExplorer/SchemaExplorer.tsx +171 -142
  32. package/eventcatalog/src/components/SchemaExplorer/SchemaListItem.tsx +5 -8
  33. package/eventcatalog/src/components/SchemaExplorer/types.ts +2 -0
  34. package/eventcatalog/src/components/SchemaExplorer/utils.ts +70 -1
  35. package/eventcatalog/src/content.config.ts +3 -1
  36. package/eventcatalog/src/features/ai/chat-api.ts +6 -1
  37. package/eventcatalog/src/features/mcp/apps/architecture-diagram/view.tsx +8 -2
  38. package/eventcatalog/src/features/mcp/apps/canvas/view.tsx +2 -2
  39. package/eventcatalog/src/features/mcp/apps/generated/canvas.html +67 -67
  40. package/eventcatalog/src/features/mcp/apps/generated/viewer.html +72 -72
  41. package/eventcatalog/src/features/mcp/apps/schema-viewer/view.tsx +2 -1
  42. package/eventcatalog/src/features/mcp/apps/shared/app-view.tsx +24 -5
  43. package/eventcatalog/src/features/mcp/mcp-apps.ts +6 -1
  44. package/eventcatalog/src/features/mcp/mcp-server.ts +2 -0
  45. package/eventcatalog/src/features/studio/server/canvas-mcp.ts +2 -0
  46. package/eventcatalog/src/features/studio/tool-sync.ts +67 -3
  47. package/eventcatalog/src/features/tools/catalog-tools.ts +8 -3
  48. package/eventcatalog/src/pages/.well-known/api-catalog.ts +1 -11
  49. package/eventcatalog/src/pages/api-catalog/specifications/[collection]/[id]/[version]/[specification].ts +1 -11
  50. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/generic/[filename].astro +137 -0
  51. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/generic/_SpecificationSource.tsx +44 -0
  52. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/generic/_[filename].data.ts +75 -0
  53. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +14 -1
  54. package/eventcatalog/src/stores/sidebar-store/builders/domain.ts +10 -1
  55. package/eventcatalog/src/stores/sidebar-store/builders/service.ts +8 -1
  56. package/eventcatalog/src/stores/sidebar-store/custom-sidebar.ts +3 -1
  57. package/eventcatalog/src/utils/collections/util.ts +38 -1
  58. package/eventcatalog/src/utils/icon.ts +8 -0
  59. package/eventcatalog/src/utils/node-graphs/utils/utils.ts +5 -2
  60. package/eventcatalog/src/utils/schema-explorer.ts +7 -2
  61. package/package.json +6 -6
@@ -0,0 +1,58 @@
1
+ import type { ReactNode } from 'react';
2
+ import { ClipboardDocumentIcon, ArrowsPointingOutIcon } from '@heroicons/react/24/outline';
3
+ import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
4
+ import { oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
5
+ import { oneLight } from 'react-syntax-highlighter/dist/cjs/styles/prism';
6
+ import { getLanguageForHighlight, SCHEMA_CODE_STYLE } from './utils';
7
+ import { useDarkMode } from './useDarkMode';
8
+ import { renderHighlightedRows } from '@utils/syntax-highlighter-renderer';
9
+
10
+ export const SCHEMA_CODE_ACTION_CLASS =
11
+ 'pointer-events-auto inline-flex items-center gap-1 rounded-md bg-[rgb(var(--ec-content-hover)/0.9)] text-[11px] font-medium text-[rgb(var(--ec-page-text-muted))] backdrop-blur-xs transition-colors hover:bg-[rgb(var(--ec-page-bg)/0.75)] hover:text-[rgb(var(--ec-page-text))]';
12
+
13
+ interface SchemaCodeBlockProps {
14
+ code: string;
15
+ /** The schema's extension or format, see getLanguageForHighlight */
16
+ extension?: string;
17
+ onCopy: () => void;
18
+ isCopied: boolean;
19
+ onOpenFullscreen?: () => void;
20
+ /** Extra actions shown before the fullscreen and copy buttons */
21
+ actions?: ReactNode;
22
+ }
23
+
24
+ /** Highlighted schema source with line numbers, as shown on the Schema tab of the Schema Explorer and on specification pages */
25
+ export default function SchemaCodeBlock({ code, extension, onCopy, isCopied, onOpenFullscreen, actions }: SchemaCodeBlockProps) {
26
+ const isDarkMode = useDarkMode();
27
+
28
+ return (
29
+ <div className="relative flex h-full min-h-0 flex-col overflow-hidden rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-code-bg))]">
30
+ <div className="pointer-events-none absolute right-4 top-4 z-10 flex items-center gap-1.5">
31
+ {actions}
32
+ {onOpenFullscreen && (
33
+ <button onClick={onOpenFullscreen} className={`${SCHEMA_CODE_ACTION_CLASS} p-1.5`} title="Open in fullscreen">
34
+ <ArrowsPointingOutIcon className="h-3.5 w-3.5" />
35
+ </button>
36
+ )}
37
+ <button onClick={onCopy} className={`${SCHEMA_CODE_ACTION_CLASS} px-2 py-1.5`} title="Copy code">
38
+ <ClipboardDocumentIcon className="h-3 w-3" />
39
+ {isCopied ? 'Copied!' : 'Copy'}
40
+ </button>
41
+ </div>
42
+
43
+ <div className="min-h-0 flex-1 overflow-auto">
44
+ <SyntaxHighlighter
45
+ renderer={renderHighlightedRows}
46
+ language={getLanguageForHighlight(extension)}
47
+ style={isDarkMode ? oneDark : oneLight}
48
+ customStyle={{ ...SCHEMA_CODE_STYLE, height: '100%' }}
49
+ showLineNumbers={true}
50
+ wrapLines={true}
51
+ wrapLongLines={true}
52
+ >
53
+ {code}
54
+ </SyntaxHighlighter>
55
+ </div>
56
+ </div>
57
+ );
58
+ }
@@ -11,7 +11,8 @@ import { renderHighlightedRows } from '@utils/syntax-highlighter-renderer';
11
11
  interface SchemaCodeModalProps {
12
12
  isOpen: boolean;
13
13
  onOpenChange: (open: boolean) => void;
14
- message: SchemaItem;
14
+ // Only what the modal shows, so specification pages can open it too
15
+ message: Pick<SchemaItem, 'schemaExtension' | 'schemaContent'> & { data: Pick<SchemaItem['data'], 'name' | 'version'> };
15
16
  onCopy: () => void;
16
17
  isCopied: boolean;
17
18
  }
@@ -1,15 +1,16 @@
1
- import { ClipboardDocumentIcon, ArrowsPointingOutIcon } from '@heroicons/react/24/outline';
2
- import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
3
- import { oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
4
- import { oneLight } from 'react-syntax-highlighter/dist/cjs/styles/prism';
5
1
  import { buildUrl } from '@utils/url-builder';
6
2
  import JSONSchemaViewer from './JSONSchemaViewer';
7
3
  import AvroSchemaViewer from './AvroSchemaViewer';
8
4
  import ProtobufSchemaViewer from './ProtobufSchemaViewer';
9
- import { getLanguageForHighlight, SCHEMA_CODE_STYLE } from './utils';
5
+ import SchemaCodeBlock, { SCHEMA_CODE_ACTION_CLASS } from './SchemaCodeBlock';
10
6
  import type { SchemaItem } from './types';
11
- import { useDarkMode } from './useDarkMode';
12
- import { renderHighlightedRows } from '@utils/syntax-highlighter-renderer';
7
+
8
+ const SPEC_URL_SEGMENTS: Record<string, string> = {
9
+ openapi: 'spec',
10
+ asyncapi: 'asyncapi',
11
+ graphql: 'graphql',
12
+ generic: 'generic',
13
+ };
13
14
 
14
15
  interface SchemaContentViewerProps {
15
16
  message: SchemaItem;
@@ -34,8 +35,6 @@ export default function SchemaContentViewer({
34
35
  showRequired = false,
35
36
  onOpenFullscreen,
36
37
  }: SchemaContentViewerProps) {
37
- const isDarkMode = useDarkMode();
38
-
39
38
  if (!message.schemaContent) {
40
39
  return (
41
40
  <div className="flex items-center justify-center h-full text-[rgb(var(--ec-page-text-muted))]">
@@ -57,73 +56,36 @@ export default function SchemaContentViewer({
57
56
  }
58
57
  }
59
58
 
60
- return (
61
- <div className="relative flex h-full min-h-0 flex-col overflow-hidden rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-code-bg))]">
62
- <div className="pointer-events-none absolute right-4 top-4 z-10 flex items-center gap-1.5">
63
- {message.collection === 'services' &&
64
- (() => {
65
- const specType = message.specType || 'openapi';
66
- const specFilename = message.specFilenameWithoutExtension || 'schema';
67
-
68
- let urlSegment = 'spec';
69
- if (specType === 'asyncapi') {
70
- urlSegment = 'asyncapi';
71
- } else if (specType === 'graphql') {
72
- urlSegment = 'graphql';
73
- }
74
-
75
- const specUrl = buildUrl(`/docs/services/${message.data.id}/${message.data.version}/${urlSegment}/${specFilename}`);
59
+ // Services link to their specification's own page
60
+ const specUrl =
61
+ message.collection === 'services'
62
+ ? buildUrl(
63
+ `/docs/services/${message.data.id}/${message.data.version}/${SPEC_URL_SEGMENTS[message.specType || 'openapi'] ?? 'spec'}/${message.specFilenameWithoutExtension || 'schema'}`
64
+ )
65
+ : undefined;
76
66
 
77
- return (
78
- <a
79
- href={specUrl}
80
- className="pointer-events-auto inline-flex items-center gap-1 rounded-md bg-[rgb(var(--ec-content-hover)/0.9)] px-2 py-1 text-[11px] font-medium text-[rgb(var(--ec-page-text-muted))] backdrop-blur-xs transition-colors hover:bg-[rgb(var(--ec-page-bg)/0.75)] hover:text-[rgb(var(--ec-page-text))]"
81
- title="View full specification"
82
- >
83
- <svg xmlns="http://www.w3.org/2000/svg" className="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
84
- <path
85
- strokeLinecap="round"
86
- strokeLinejoin="round"
87
- strokeWidth={2}
88
- d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
89
- />
90
- </svg>
91
- View Spec
92
- </a>
93
- );
94
- })()}
95
- {onOpenFullscreen && (
96
- <button
97
- onClick={onOpenFullscreen}
98
- className="pointer-events-auto inline-flex items-center rounded-md bg-[rgb(var(--ec-content-hover)/0.9)] p-1.5 text-[rgb(var(--ec-page-text-muted))] backdrop-blur-xs transition-colors hover:bg-[rgb(var(--ec-page-bg)/0.75)] hover:text-[rgb(var(--ec-page-text))]"
99
- title="Open in fullscreen"
100
- >
101
- <ArrowsPointingOutIcon className="h-3.5 w-3.5" />
102
- </button>
103
- )}
104
- <button
105
- onClick={onCopy}
106
- className="pointer-events-auto inline-flex items-center gap-1 rounded-md bg-[rgb(var(--ec-content-hover)/0.9)] px-2 py-1.5 text-[11px] font-medium text-[rgb(var(--ec-page-text-muted))] backdrop-blur-xs transition-colors hover:bg-[rgb(var(--ec-page-bg)/0.75)] hover:text-[rgb(var(--ec-page-text))]"
107
- title="Copy code"
108
- >
109
- <ClipboardDocumentIcon className="h-3 w-3" />
110
- {isCopied ? 'Copied!' : 'Copy'}
111
- </button>
112
- </div>
113
-
114
- <div className="min-h-0 flex-1 overflow-auto">
115
- <SyntaxHighlighter
116
- renderer={renderHighlightedRows}
117
- language={getLanguageForHighlight(message.schemaExtension)}
118
- style={isDarkMode ? oneDark : oneLight}
119
- customStyle={{ ...SCHEMA_CODE_STYLE, height: '100%' }}
120
- showLineNumbers={true}
121
- wrapLines={true}
122
- wrapLongLines={true}
123
- >
124
- {message.schemaContent}
125
- </SyntaxHighlighter>
126
- </div>
127
- </div>
67
+ return (
68
+ <SchemaCodeBlock
69
+ code={message.schemaContent}
70
+ extension={message.schemaExtension}
71
+ onCopy={onCopy}
72
+ isCopied={isCopied}
73
+ onOpenFullscreen={onOpenFullscreen}
74
+ actions={
75
+ specUrl && (
76
+ <a href={specUrl} className={`${SCHEMA_CODE_ACTION_CLASS} px-2 py-1`} title="View full specification">
77
+ <svg xmlns="http://www.w3.org/2000/svg" className="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
78
+ <path
79
+ strokeLinecap="round"
80
+ strokeLinejoin="round"
81
+ strokeWidth={2}
82
+ d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
83
+ />
84
+ </svg>
85
+ View Spec
86
+ </a>
87
+ )
88
+ }
89
+ />
128
90
  );
129
91
  }
@@ -30,9 +30,10 @@ import SchemaCodeModal from './SchemaCodeModal';
30
30
  import SchemaViewerModal from './SchemaViewerModal';
31
31
  import {
32
32
  copyToClipboard,
33
+ getSchemaDownloadExtension,
33
34
  downloadSchema,
34
- getSchemaTypeLabel,
35
- ICON_SPECS,
35
+ getSchemaDisplayLabel,
36
+ getSchemaIconUrl,
36
37
  getSchemaRelationshipReference,
37
38
  getSchemaRelationshipHref,
38
39
  SCHEMA_RELATIONSHIP_LABELS,
@@ -95,7 +96,7 @@ const SchemaMetadata = ({ message, owners }: { message: SchemaItem; owners: Owne
95
96
  const schemaRows: MetadataRow[] = [
96
97
  { label: 'Name', value: message.data.name },
97
98
  ...(message.schemaName && message.schemaName !== message.data.name ? [{ label: 'Schema', value: message.schemaName }] : []),
98
- { label: 'Format', value: getSchemaTypeLabel(message.schemaExtension) },
99
+ { label: 'Format', value: getSchemaDisplayLabel(message) },
99
100
  {
100
101
  label: MESSAGE_TYPE_LABELS[message.collection] ? 'Message Type' : 'Resource',
101
102
  value: (
@@ -249,7 +250,7 @@ const SchemaVersionsTable = ({
249
250
  {version.data.summary ? <p className="line-clamp-2 max-w-2xl">{version.data.summary}</p> : <span>-</span>}
250
251
  </td>
251
252
  <td className="whitespace-nowrap px-4 py-3 text-sm text-[rgb(var(--ec-page-text-muted))]">
252
- {getSchemaTypeLabel(version.schemaExtension)}
253
+ {getSchemaDisplayLabel(version)}
253
254
  </td>
254
255
  <td className="whitespace-nowrap px-4 py-3 text-sm text-[rgb(var(--ec-page-text-muted))]">
255
256
  {version.source?.updatedAt ? formatSchemaUpdatedAt(version.source.updatedAt) : '-'}
@@ -343,7 +344,7 @@ export default function SchemaDetailsPanel({
343
344
 
344
345
  const { color } = getCollectionStyles(message.collection);
345
346
  const ext = message.schemaExtension?.toLowerCase() || '';
346
- const iconSpec = ICON_SPECS[ext];
347
+ const iconUrl = getSchemaIconUrl(message);
347
348
  const owners = message.data.owners || [];
348
349
  const producers = message.data.producers || [];
349
350
  const consumers = message.data.consumers || [];
@@ -429,10 +430,13 @@ export default function SchemaDetailsPanel({
429
430
  }, [comparing, diffFromItem, diffToItem]);
430
431
  const selectedDiffs = selectedDiff ? [selectedDiff] : [];
431
432
 
432
- // Parse JSON Schema, Avro and Protobuf schemas for the Properties tab
433
+ // Parse JSON Schema, Avro and Protobuf schemas for the Properties tab. Generic specifications are only shown as code.
433
434
  const viewableSchema = useMemo(
434
- () => parseSchemaForViewer(message.schemaExtension, message.schemaContent),
435
- [message.schemaContent, message.schemaExtension]
435
+ () =>
436
+ message.specType === 'generic'
437
+ ? ({ kind: 'code' } as const)
438
+ : parseSchemaForViewer(message.schemaExtension, message.schemaContent),
439
+ [message.schemaContent, message.schemaExtension, message.specType]
436
440
  );
437
441
  const parsedSchema = viewableSchema.kind === 'json' ? viewableSchema.schema : null;
438
442
  const parsedAvroSchema = viewableSchema.kind === 'avro' ? viewableSchema.schema : null;
@@ -457,7 +461,7 @@ export default function SchemaDetailsPanel({
457
461
 
458
462
  const handleDownload = () => {
459
463
  if (!message.schemaContent) return;
460
- downloadSchema(message.schemaContent, message.data.id, message.schemaExtension || 'json');
464
+ downloadSchema(message.schemaContent, message.data.id, getSchemaDownloadExtension(message));
461
465
  };
462
466
 
463
467
  const isCopied = copiedId === message.data.id;
@@ -558,8 +562,8 @@ export default function SchemaDetailsPanel({
558
562
  <div className="min-w-0 flex-1">
559
563
  <div className="flex min-w-0 items-center gap-3">
560
564
  <div className="flex-shrink-0 flex items-center justify-center w-9 h-9 rounded-lg bg-[rgb(var(--ec-content-hover))] border border-[rgb(var(--ec-page-border)/0.5)]">
561
- {iconSpec ? (
562
- <img src={buildUrl(`/icons/${iconSpec}.svg`, true)} alt={`${ext} icon`} className="h-5 w-5 schema-icon" />
565
+ {iconUrl ? (
566
+ <img src={iconUrl} alt={`${ext} icon`} className="h-5 w-5 schema-icon" />
563
567
  ) : (
564
568
  <span className="text-xs font-bold font-mono text-[rgb(var(--ec-page-text-muted))]">{'{ }'}</span>
565
569
  )}