@eventcatalog/core 4.0.6 → 4.0.7
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/dist/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +1 -1
- package/dist/analytics/log-build.js +3 -3
- package/dist/{chunk-ZYWUN3T3.js → chunk-ARHL5SPG.js} +1 -1
- package/dist/{chunk-ZXAF4JO7.js → chunk-IMCMPLIJ.js} +1 -1
- package/dist/{chunk-Z7BGWB5J.js → chunk-RBBWHWE4.js} +1 -1
- package/dist/{chunk-FHVBLKWN.js → chunk-WDA5QYVD.js} +1 -1
- package/dist/{chunk-7443A7LZ.js → chunk-ZOFYLCPF.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +1 -1
- package/dist/eventcatalog.js +5 -5
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +6 -2
- package/eventcatalog/src/components/MDX/RemoteFile.astro +5 -0
- package/eventcatalog/src/components/SchemaExplorer/DiffViewer.tsx +7 -9
- package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +127 -41
- package/eventcatalog/src/components/SchemaExplorer/VersionHistoryModal.tsx +2 -2
- package/eventcatalog/src/components/SchemaExplorer/types.ts +4 -4
- package/eventcatalog/src/utils/json-schema-refs.ts +158 -0
- package/package.json +3 -3
|
@@ -140,7 +140,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
|
|
|
140
140
|
var import_os = __toESM(require("os"), 1);
|
|
141
141
|
|
|
142
142
|
// package.json
|
|
143
|
-
var version = "4.0.
|
|
143
|
+
var version = "4.0.7";
|
|
144
144
|
|
|
145
145
|
// src/constants.ts
|
|
146
146
|
var VERSION = version;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
log_build_default
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-ARHL5SPG.js";
|
|
4
|
+
import "../chunk-WDA5QYVD.js";
|
|
5
5
|
import "../chunk-DAOXTQVS.js";
|
|
6
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-RBBWHWE4.js";
|
|
7
7
|
import "../chunk-6QENHZZP.js";
|
|
8
8
|
export {
|
|
9
9
|
log_build_default as default
|
package/dist/constants.cjs
CHANGED
package/dist/constants.js
CHANGED
package/dist/eventcatalog.cjs
CHANGED
|
@@ -144,7 +144,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
|
|
|
144
144
|
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
145
145
|
|
|
146
146
|
// package.json
|
|
147
|
-
var version = "4.0.
|
|
147
|
+
var version = "4.0.7";
|
|
148
148
|
|
|
149
149
|
// src/constants.ts
|
|
150
150
|
var VERSION = version;
|
package/dist/eventcatalog.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
log_build_default
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ARHL5SPG.js";
|
|
4
4
|
import {
|
|
5
5
|
runMigrations
|
|
6
6
|
} from "./chunk-XUAF2H54.js";
|
|
7
7
|
import "./chunk-CA4U2JP7.js";
|
|
8
8
|
import {
|
|
9
9
|
generate
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-ZOFYLCPF.js";
|
|
11
11
|
import {
|
|
12
12
|
logger
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-IMCMPLIJ.js";
|
|
14
14
|
import {
|
|
15
15
|
resolve_catalog_dependencies_default
|
|
16
16
|
} from "./chunk-LHR4G2UO.js";
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
import {
|
|
21
21
|
watch
|
|
22
22
|
} from "./chunk-O3Y2G6CY.js";
|
|
23
|
-
import "./chunk-
|
|
23
|
+
import "./chunk-WDA5QYVD.js";
|
|
24
24
|
import "./chunk-DAOXTQVS.js";
|
|
25
25
|
import {
|
|
26
26
|
catalogToAstro
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
import "./chunk-VC6VUJIX.js";
|
|
29
29
|
import {
|
|
30
30
|
VERSION
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-RBBWHWE4.js";
|
|
32
32
|
import {
|
|
33
33
|
linkCoreNodeModules,
|
|
34
34
|
resolveInstalledCoreNodeModules
|
package/dist/generate.cjs
CHANGED
|
@@ -108,7 +108,7 @@ var getEventCatalogConfigFile = async (projectDirectory) => {
|
|
|
108
108
|
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
109
109
|
|
|
110
110
|
// package.json
|
|
111
|
-
var version = "4.0.
|
|
111
|
+
var version = "4.0.7";
|
|
112
112
|
|
|
113
113
|
// src/constants.ts
|
|
114
114
|
var VERSION = version;
|
package/dist/generate.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generate
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-ZOFYLCPF.js";
|
|
4
|
+
import "./chunk-IMCMPLIJ.js";
|
|
5
|
+
import "./chunk-RBBWHWE4.js";
|
|
6
6
|
import "./chunk-6QENHZZP.js";
|
|
7
7
|
export {
|
|
8
8
|
generate
|
package/dist/utils/cli-logger.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { useEffect, useRef, useCallback, useState, useMemo, memo } from 'react';
|
|
2
2
|
import { X, Square, Trash2, BookOpen, Copy, Check, Maximize2, Minimize2, Wrench, ChevronDown, MessageSquare } from 'lucide-react';
|
|
3
3
|
import { useChat } from '@ai-sdk/react';
|
|
4
|
+
import { DefaultChatTransport } from 'ai';
|
|
4
5
|
import ReactMarkdown from 'react-markdown';
|
|
5
6
|
import remarkGfm from 'remark-gfm';
|
|
6
7
|
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
|
7
8
|
import { oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism';
|
|
8
9
|
import * as Dialog from '@radix-ui/react-dialog';
|
|
9
10
|
import * as Popover from '@radix-ui/react-popover';
|
|
11
|
+
import { buildUrl } from '@utils/url-builder';
|
|
10
12
|
|
|
11
13
|
interface ToolMetadata {
|
|
12
14
|
name: string;
|
|
@@ -527,6 +529,8 @@ const ChatPanel = ({ isOpen, onClose }: ChatPanelProps) => {
|
|
|
527
529
|
const [isFullscreen, setIsFullscreen] = useState(false);
|
|
528
530
|
const [tools, setTools] = useState<ToolMetadata[]>([]);
|
|
529
531
|
const [showScrollButton, setShowScrollButton] = useState(false);
|
|
532
|
+
const chatApiUrl = useMemo(() => buildUrl('/api/chat', true), []);
|
|
533
|
+
const chatTransport = useMemo(() => new DefaultChatTransport({ api: chatApiUrl }), [chatApiUrl]);
|
|
530
534
|
|
|
531
535
|
// Sort tools with custom ones first
|
|
532
536
|
const sortedTools = useMemo(() => {
|
|
@@ -540,7 +544,7 @@ const ChatPanel = ({ isOpen, onClose }: ChatPanelProps) => {
|
|
|
540
544
|
// Fetch available tools when panel opens
|
|
541
545
|
useEffect(() => {
|
|
542
546
|
if (isOpen && tools.length === 0) {
|
|
543
|
-
fetch(
|
|
547
|
+
fetch(chatApiUrl)
|
|
544
548
|
.then((res) => res.json())
|
|
545
549
|
.then((data) => {
|
|
546
550
|
if (data.tools) {
|
|
@@ -597,7 +601,7 @@ const ChatPanel = ({ isOpen, onClose }: ChatPanelProps) => {
|
|
|
597
601
|
setShowScrollButton(!isNearBottom);
|
|
598
602
|
}, []);
|
|
599
603
|
|
|
600
|
-
const { messages, sendMessage, stop, status, setMessages, error } = useChat();
|
|
604
|
+
const { messages, sendMessage, stop, status, setMessages, error } = useChat({ transport: chatTransport });
|
|
601
605
|
|
|
602
606
|
// Extract user-friendly error message
|
|
603
607
|
const errorMessage = error?.message || 'Something went wrong. Please try again.';
|
|
@@ -4,6 +4,7 @@ import JSONSchemaViewer from '@components/SchemaExplorer/JSONSchemaViewer';
|
|
|
4
4
|
import { Code } from 'astro-expressive-code/components';
|
|
5
5
|
import { isPrivateRemoteSchemaEnabled } from '@utils/feature';
|
|
6
6
|
import { resolveTemplateVariables } from '@utils/remote-file';
|
|
7
|
+
import { resolveSchemaRefs } from '@utils/json-schema-refs';
|
|
7
8
|
|
|
8
9
|
const {
|
|
9
10
|
url,
|
|
@@ -89,6 +90,10 @@ try {
|
|
|
89
90
|
if (isValidJSON(content)) {
|
|
90
91
|
contentType = 'json';
|
|
91
92
|
processedData = JSON.parse(content);
|
|
93
|
+
processedData = await resolveSchemaRefs(processedData, {
|
|
94
|
+
baseUrl: String(resolvedUrl),
|
|
95
|
+
headers: resolvedHeaders as HeadersInit,
|
|
96
|
+
});
|
|
92
97
|
|
|
93
98
|
// Check if it's a JSON Schema
|
|
94
99
|
isSchema = isJSONSchema(processedData);
|
|
@@ -122,10 +122,10 @@ export default function DiffViewer({ diffs, onOpenFullscreen, apiAccessEnabled =
|
|
|
122
122
|
{isDark && <style dangerouslySetInnerHTML={{ __html: DIFF_DARK_STYLES }} />}
|
|
123
123
|
<div className="mb-5 flex items-start justify-between">
|
|
124
124
|
<div className="flex-1">
|
|
125
|
-
<h3 className="text-base font-semibold text-[rgb(var(--ec-page-text))] mb-1">Version
|
|
125
|
+
<h3 className="text-base font-semibold text-[rgb(var(--ec-page-text))] mb-1">Version Comparison</h3>
|
|
126
126
|
<p className="text-sm text-[rgb(var(--ec-page-text-muted))]">
|
|
127
127
|
{apiAccessEnabled
|
|
128
|
-
? `${diffs.length}
|
|
128
|
+
? `${diffs.length} selected comparison${diffs.length !== 1 ? 's' : ''}`
|
|
129
129
|
: 'Compare schema versions side-by-side'}
|
|
130
130
|
</p>
|
|
131
131
|
</div>
|
|
@@ -142,21 +142,19 @@ export default function DiffViewer({ diffs, onOpenFullscreen, apiAccessEnabled =
|
|
|
142
142
|
</div>
|
|
143
143
|
{apiAccessEnabled ? (
|
|
144
144
|
<div className={`space-y-6 ${isDark ? 'diff-dark-mode' : ''}`}>
|
|
145
|
-
{diffs.map((diff
|
|
145
|
+
{diffs.map((diff) => (
|
|
146
146
|
<div
|
|
147
|
-
key={`${diff.
|
|
147
|
+
key={`${diff.fromVersion}-${diff.toVersion}`}
|
|
148
148
|
className="border border-[rgb(var(--ec-page-border))] rounded-lg overflow-hidden"
|
|
149
149
|
>
|
|
150
150
|
<div className="bg-[rgb(var(--ec-content-hover))] border-b border-[rgb(var(--ec-page-border))] px-4 py-2.5">
|
|
151
151
|
<div className="flex items-center justify-between">
|
|
152
152
|
<div className="text-sm">
|
|
153
|
-
<span className="font-semibold text-[rgb(var(--ec-page-text))]">v{diff.
|
|
153
|
+
<span className="font-semibold text-[rgb(var(--ec-page-text))]">v{diff.fromVersion}</span>
|
|
154
154
|
<span className="text-[rgb(var(--ec-page-text-muted))] mx-2">→</span>
|
|
155
|
-
<span className="font-semibold text-[rgb(var(--ec-page-text))]">v{diff.
|
|
155
|
+
<span className="font-semibold text-[rgb(var(--ec-page-text))]">v{diff.toVersion}</span>
|
|
156
156
|
</div>
|
|
157
|
-
<span className="text-xs text-[rgb(var(--ec-page-text-muted))]">
|
|
158
|
-
{index === 0 ? 'Latest change' : `${index + 1} version${index + 1 !== 1 ? 's' : ''} ago`}
|
|
159
|
-
</span>
|
|
157
|
+
<span className="text-xs text-[rgb(var(--ec-page-text-muted))]">Selected comparison</span>
|
|
160
158
|
</div>
|
|
161
159
|
</div>
|
|
162
160
|
<div className="relative">
|
|
@@ -64,43 +64,70 @@ export default function SchemaDetailsPanel({
|
|
|
64
64
|
const consumers = message.data.consumers || [];
|
|
65
65
|
const filename = message.data.schemaPath?.split('/').pop() || `${message.data.id}.${ext || 'json'}`;
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
return
|
|
103
|
-
|
|
67
|
+
const uniqueAvailableVersions = useMemo(
|
|
68
|
+
() =>
|
|
69
|
+
availableVersions.filter(
|
|
70
|
+
(version, index, versions) => versions.findIndex((item) => item.data.version === version.data.version) === index
|
|
71
|
+
),
|
|
72
|
+
[availableVersions]
|
|
73
|
+
);
|
|
74
|
+
const defaultToVersion = uniqueAvailableVersions[0]?.data.version || '';
|
|
75
|
+
const defaultFromVersion = uniqueAvailableVersions[1]?.data.version || defaultToVersion;
|
|
76
|
+
const [diffFromVersion, setDiffFromVersion] = useState(defaultFromVersion);
|
|
77
|
+
const [diffToVersion, setDiffToVersion] = useState(defaultToVersion);
|
|
78
|
+
const schemaResourceKey = [
|
|
79
|
+
message.collection,
|
|
80
|
+
message.data.id,
|
|
81
|
+
message.specType || '',
|
|
82
|
+
message.specFilenameWithoutExtension || message.specName || '',
|
|
83
|
+
].join(':');
|
|
84
|
+
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
setDiffFromVersion(defaultFromVersion);
|
|
87
|
+
setDiffToVersion(defaultToVersion);
|
|
88
|
+
setIsDiffModalOpen(false);
|
|
89
|
+
}, [schemaResourceKey, defaultFromVersion, defaultToVersion]);
|
|
90
|
+
|
|
91
|
+
const diffFromItem = useMemo(
|
|
92
|
+
() => uniqueAvailableVersions.find((version) => version.data.version === diffFromVersion),
|
|
93
|
+
[diffFromVersion, uniqueAvailableVersions]
|
|
94
|
+
);
|
|
95
|
+
const diffToItem = useMemo(
|
|
96
|
+
() => uniqueAvailableVersions.find((version) => version.data.version === diffToVersion),
|
|
97
|
+
[diffToVersion, uniqueAvailableVersions]
|
|
98
|
+
);
|
|
99
|
+
const hasDiffFromContent = !!diffFromItem?.schemaContent?.trim();
|
|
100
|
+
const hasDiffToContent = !!diffToItem?.schemaContent?.trim();
|
|
101
|
+
const selectedDiff: VersionDiff | null = useMemo(() => {
|
|
102
|
+
if (!diffFromItem || !diffToItem) return null;
|
|
103
|
+
if (diffFromItem.data.version === diffToItem.data.version) return null;
|
|
104
|
+
if (!diffFromItem.schemaContent?.trim() || !diffToItem.schemaContent?.trim()) return null;
|
|
105
|
+
|
|
106
|
+
const diff = Diff.createTwoFilesPatch(
|
|
107
|
+
`v${diffFromItem.data.version}`,
|
|
108
|
+
`v${diffToItem.data.version}`,
|
|
109
|
+
diffFromItem.schemaContent,
|
|
110
|
+
diffToItem.schemaContent,
|
|
111
|
+
'',
|
|
112
|
+
'',
|
|
113
|
+
{ context: 3 }
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const diffHtml = html(diff, {
|
|
117
|
+
drawFileList: false,
|
|
118
|
+
matching: 'lines',
|
|
119
|
+
outputFormat: 'side-by-side',
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
fromVersion: diffFromItem.data.version,
|
|
124
|
+
toVersion: diffToItem.data.version,
|
|
125
|
+
diffHtml,
|
|
126
|
+
fromContent: diffFromItem.schemaContent,
|
|
127
|
+
toContent: diffToItem.schemaContent,
|
|
128
|
+
};
|
|
129
|
+
}, [diffFromItem, diffToItem]);
|
|
130
|
+
const selectedDiffs = selectedDiff ? [selectedDiff] : [];
|
|
104
131
|
|
|
105
132
|
// Check if this is a JSON schema
|
|
106
133
|
const parsedSchema = useMemo(() => {
|
|
@@ -212,7 +239,7 @@ export default function SchemaDetailsPanel({
|
|
|
212
239
|
if (examples.length > 0) {
|
|
213
240
|
tabs.push({ id: 'examples', label: 'Usage Examples', icon: <BookOpenIcon className="h-3.5 w-3.5" /> });
|
|
214
241
|
}
|
|
215
|
-
if (
|
|
242
|
+
if (hasMultipleVersions) {
|
|
216
243
|
tabs.push({ id: 'diff', label: 'Changes', icon: <ClockIcon className="h-3.5 w-3.5" /> });
|
|
217
244
|
}
|
|
218
245
|
tabs.push({ id: 'api', label: 'API', icon: <GlobeAltIcon className="h-3.5 w-3.5" /> });
|
|
@@ -341,8 +368,67 @@ export default function SchemaDetailsPanel({
|
|
|
341
368
|
copiedId={copiedId}
|
|
342
369
|
apiAccessEnabled={apiAccessEnabled}
|
|
343
370
|
/>
|
|
344
|
-
) : activeTab === 'diff' &&
|
|
345
|
-
<
|
|
371
|
+
) : activeTab === 'diff' && hasMultipleVersions ? (
|
|
372
|
+
<div className="flex h-full min-h-0 flex-col overflow-hidden">
|
|
373
|
+
<div className="mb-4 flex flex-shrink-0 flex-col gap-3 rounded-lg border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-content-hover)/0.45)] p-3 sm:flex-row sm:items-end sm:justify-between">
|
|
374
|
+
<div className="grid flex-1 grid-cols-1 gap-3 sm:grid-cols-2">
|
|
375
|
+
<label className="flex flex-col gap-1.5">
|
|
376
|
+
<span className="text-[11px] font-medium uppercase tracking-[0.14em] text-[rgb(var(--ec-page-text-muted))]">
|
|
377
|
+
From
|
|
378
|
+
</span>
|
|
379
|
+
<select
|
|
380
|
+
value={diffFromVersion}
|
|
381
|
+
onChange={(event) => setDiffFromVersion(event.target.value)}
|
|
382
|
+
className="h-9 rounded-md border border-[rgb(var(--ec-dropdown-border))] bg-[rgb(var(--ec-dropdown-bg))] px-3 text-sm font-mono tabular-nums text-[rgb(var(--ec-page-text))] outline-hidden transition-colors focus:border-[rgb(var(--ec-accent))] focus:ring-1 focus:ring-[rgb(var(--ec-accent)/0.3)]"
|
|
383
|
+
>
|
|
384
|
+
{uniqueAvailableVersions.map((version) => (
|
|
385
|
+
<option key={`from-${version.data.version}`} value={version.data.version}>
|
|
386
|
+
v{version.data.version}
|
|
387
|
+
</option>
|
|
388
|
+
))}
|
|
389
|
+
</select>
|
|
390
|
+
</label>
|
|
391
|
+
<label className="flex flex-col gap-1.5">
|
|
392
|
+
<span className="text-[11px] font-medium uppercase tracking-[0.14em] text-[rgb(var(--ec-page-text-muted))]">
|
|
393
|
+
To
|
|
394
|
+
</span>
|
|
395
|
+
<select
|
|
396
|
+
value={diffToVersion}
|
|
397
|
+
onChange={(event) => setDiffToVersion(event.target.value)}
|
|
398
|
+
className="h-9 rounded-md border border-[rgb(var(--ec-dropdown-border))] bg-[rgb(var(--ec-dropdown-bg))] px-3 text-sm font-mono tabular-nums text-[rgb(var(--ec-page-text))] outline-hidden transition-colors focus:border-[rgb(var(--ec-accent))] focus:ring-1 focus:ring-[rgb(var(--ec-accent)/0.3)]"
|
|
399
|
+
>
|
|
400
|
+
{uniqueAvailableVersions.map((version) => (
|
|
401
|
+
<option key={`to-${version.data.version}`} value={version.data.version}>
|
|
402
|
+
v{version.data.version}
|
|
403
|
+
</option>
|
|
404
|
+
))}
|
|
405
|
+
</select>
|
|
406
|
+
</label>
|
|
407
|
+
</div>
|
|
408
|
+
<button
|
|
409
|
+
type="button"
|
|
410
|
+
onClick={() => setIsDiffModalOpen(true)}
|
|
411
|
+
disabled={!selectedDiff}
|
|
412
|
+
className="inline-flex h-9 items-center justify-center gap-1.5 rounded-md border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-dropdown-bg))] px-3 text-xs font-medium text-[rgb(var(--ec-page-text-muted))] transition-colors hover:bg-[rgb(var(--ec-content-hover))] hover:text-[rgb(var(--ec-page-text))] disabled:cursor-not-allowed disabled:opacity-50"
|
|
413
|
+
>
|
|
414
|
+
<ArrowTopRightOnSquareIcon className="h-3.5 w-3.5" />
|
|
415
|
+
Expand
|
|
416
|
+
</button>
|
|
417
|
+
</div>
|
|
418
|
+
<div className="min-h-0 flex-1 overflow-hidden">
|
|
419
|
+
{diffFromVersion === diffToVersion ? (
|
|
420
|
+
<div className="flex h-full items-center justify-center text-[rgb(var(--ec-page-text-muted))]">
|
|
421
|
+
<p className="text-sm">Select two different versions</p>
|
|
422
|
+
</div>
|
|
423
|
+
) : !hasDiffFromContent || !hasDiffToContent ? (
|
|
424
|
+
<div className="flex h-full items-center justify-center text-[rgb(var(--ec-page-text-muted))]">
|
|
425
|
+
<p className="text-sm">No schema content available</p>
|
|
426
|
+
</div>
|
|
427
|
+
) : (
|
|
428
|
+
<DiffViewer diffs={selectedDiffs} apiAccessEnabled={apiAccessEnabled} />
|
|
429
|
+
)}
|
|
430
|
+
</div>
|
|
431
|
+
</div>
|
|
346
432
|
) : (
|
|
347
433
|
<SchemaContentViewer
|
|
348
434
|
message={message}
|
|
@@ -507,7 +593,7 @@ export default function SchemaDetailsPanel({
|
|
|
507
593
|
<VersionHistoryModal
|
|
508
594
|
isOpen={isDiffModalOpen}
|
|
509
595
|
onOpenChange={setIsDiffModalOpen}
|
|
510
|
-
diffs={
|
|
596
|
+
diffs={selectedDiffs}
|
|
511
597
|
messageName={message.data.name}
|
|
512
598
|
apiAccessEnabled={apiAccessEnabled}
|
|
513
599
|
/>
|
|
@@ -28,7 +28,7 @@ export default function VersionHistoryModal({
|
|
|
28
28
|
<div className="flex items-center gap-3">
|
|
29
29
|
<ArrowsPointingOutIcon className="h-6 w-6 text-[rgb(var(--ec-icon-color))]" />
|
|
30
30
|
<div>
|
|
31
|
-
<Dialog.Title className="text-xl font-semibold text-[rgb(var(--ec-page-text))]">Version
|
|
31
|
+
<Dialog.Title className="text-xl font-semibold text-[rgb(var(--ec-page-text))]">Version Comparison</Dialog.Title>
|
|
32
32
|
<Dialog.Description className="text-sm text-[rgb(var(--ec-page-text-muted))] mt-1">
|
|
33
33
|
{messageName}
|
|
34
34
|
</Dialog.Description>
|
|
@@ -51,7 +51,7 @@ export default function VersionHistoryModal({
|
|
|
51
51
|
<DiffViewer diffs={diffs} apiAccessEnabled={apiAccessEnabled} />
|
|
52
52
|
) : (
|
|
53
53
|
<div className="flex items-center justify-center h-full">
|
|
54
|
-
<p className="text-[rgb(var(--ec-page-text-muted))] text-center">No version
|
|
54
|
+
<p className="text-[rgb(var(--ec-page-text-muted))] text-center">No version comparison available</p>
|
|
55
55
|
</div>
|
|
56
56
|
)}
|
|
57
57
|
</div>
|
|
@@ -45,9 +45,9 @@ export interface SchemaItem {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
export interface VersionDiff {
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
fromVersion: string;
|
|
49
|
+
toVersion: string;
|
|
50
50
|
diffHtml: string;
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
fromContent: string;
|
|
52
|
+
toContent: string;
|
|
53
53
|
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
type Fetcher = (input: string | URL, init?: RequestInit) => Promise<Response>;
|
|
2
|
+
|
|
3
|
+
type ResolveSchemaRefsOptions = {
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
headers?: HeadersInit;
|
|
6
|
+
fetcher?: Fetcher;
|
|
7
|
+
maxDepth?: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type ResolveContext = Required<Pick<ResolveSchemaRefsOptions, 'baseUrl' | 'fetcher' | 'maxDepth'>> & {
|
|
11
|
+
headers?: HeadersInit;
|
|
12
|
+
cache: Map<string, unknown>;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type RefTarget = {
|
|
16
|
+
schema: unknown;
|
|
17
|
+
root: unknown;
|
|
18
|
+
baseUrl: string;
|
|
19
|
+
refKey: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const isObject = (value: unknown): value is Record<string, unknown> =>
|
|
23
|
+
typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
24
|
+
|
|
25
|
+
const decodeJsonPointerSegment = (segment: string) => decodeURIComponent(segment).replace(/~1/g, '/').replace(/~0/g, '~');
|
|
26
|
+
|
|
27
|
+
const resolveJsonPointer = (document: unknown, pointer: string): unknown => {
|
|
28
|
+
if (!pointer || pointer === '#') return document;
|
|
29
|
+
|
|
30
|
+
const path = pointer.startsWith('#') ? pointer.slice(1) : pointer;
|
|
31
|
+
if (!path) return document;
|
|
32
|
+
if (!path.startsWith('/')) return undefined;
|
|
33
|
+
|
|
34
|
+
return path
|
|
35
|
+
.slice(1)
|
|
36
|
+
.split('/')
|
|
37
|
+
.map(decodeJsonPointerSegment)
|
|
38
|
+
.reduce<unknown>((current, segment) => {
|
|
39
|
+
if (current === undefined || current === null) return undefined;
|
|
40
|
+
if (Array.isArray(current)) return current[Number(segment)];
|
|
41
|
+
if (isObject(current)) return current[segment];
|
|
42
|
+
return undefined;
|
|
43
|
+
}, document);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const splitRef = (ref: string, baseUrl: string) => {
|
|
47
|
+
const [urlPart = '', fragmentPart = ''] = ref.split('#');
|
|
48
|
+
const url = urlPart ? new URL(urlPart, baseUrl).toString() : baseUrl;
|
|
49
|
+
return {
|
|
50
|
+
url,
|
|
51
|
+
fragment: `#${fragmentPart}`,
|
|
52
|
+
refKey: `${url}#${fragmentPart}`,
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const loadRemoteDocument = async (url: string, context: ResolveContext) => {
|
|
57
|
+
if (context.cache.has(url)) return context.cache.get(url);
|
|
58
|
+
|
|
59
|
+
const response = await context.fetcher(url, {
|
|
60
|
+
headers: context.headers,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
throw new Error(`Failed to fetch referenced schema: ${response.status} ${response.statusText}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const document = await response.json();
|
|
68
|
+
context.cache.set(url, document);
|
|
69
|
+
return document;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const resolveRef = async (
|
|
73
|
+
ref: string,
|
|
74
|
+
root: unknown,
|
|
75
|
+
currentBaseUrl: string,
|
|
76
|
+
context: ResolveContext
|
|
77
|
+
): Promise<RefTarget | undefined> => {
|
|
78
|
+
const { url, fragment, refKey } = splitRef(ref, currentBaseUrl);
|
|
79
|
+
const isCurrentDocument = url === currentBaseUrl;
|
|
80
|
+
const document = isCurrentDocument ? root : await loadRemoteDocument(url, context);
|
|
81
|
+
const schema = resolveJsonPointer(document, fragment);
|
|
82
|
+
|
|
83
|
+
if (schema === undefined) return undefined;
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
schema,
|
|
87
|
+
root: document,
|
|
88
|
+
baseUrl: url,
|
|
89
|
+
refKey,
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const resolveNode = async (
|
|
94
|
+
node: unknown,
|
|
95
|
+
root: unknown,
|
|
96
|
+
currentBaseUrl: string,
|
|
97
|
+
context: ResolveContext,
|
|
98
|
+
seenRefs: Set<string>,
|
|
99
|
+
depth: number
|
|
100
|
+
): Promise<unknown> => {
|
|
101
|
+
if (depth > context.maxDepth) return node;
|
|
102
|
+
|
|
103
|
+
if (Array.isArray(node)) {
|
|
104
|
+
return Promise.all(node.map((item) => resolveNode(item, root, currentBaseUrl, context, seenRefs, depth)));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!isObject(node)) return node;
|
|
108
|
+
|
|
109
|
+
const ref = typeof node.$ref === 'string' ? node.$ref : undefined;
|
|
110
|
+
if (ref) {
|
|
111
|
+
const target = await resolveRef(ref, root, currentBaseUrl, context);
|
|
112
|
+
if (!target || seenRefs.has(target.refKey)) return { ...node };
|
|
113
|
+
|
|
114
|
+
const nextSeenRefs = new Set(seenRefs);
|
|
115
|
+
nextSeenRefs.add(target.refKey);
|
|
116
|
+
|
|
117
|
+
const resolvedSchema = await resolveNode(target.schema, target.root, target.baseUrl, context, nextSeenRefs, depth + 1);
|
|
118
|
+
const siblingEntries = Object.entries(node).filter(([key]) => key !== '$ref');
|
|
119
|
+
const resolvedSiblings = Object.fromEntries(
|
|
120
|
+
await Promise.all(
|
|
121
|
+
siblingEntries.map(async ([key, value]) => [
|
|
122
|
+
key,
|
|
123
|
+
await resolveNode(value, root, currentBaseUrl, context, seenRefs, depth),
|
|
124
|
+
])
|
|
125
|
+
)
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
if (isObject(resolvedSchema)) {
|
|
129
|
+
return {
|
|
130
|
+
...resolvedSchema,
|
|
131
|
+
...resolvedSiblings,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return Object.keys(resolvedSiblings).length > 0 ? resolvedSiblings : resolvedSchema;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return Object.fromEntries(
|
|
139
|
+
await Promise.all(
|
|
140
|
+
Object.entries(node).map(async ([key, value]) => [
|
|
141
|
+
key,
|
|
142
|
+
await resolveNode(value, root, currentBaseUrl, context, seenRefs, depth),
|
|
143
|
+
])
|
|
144
|
+
)
|
|
145
|
+
);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export const resolveSchemaRefs = async (schema: unknown, options: ResolveSchemaRefsOptions): Promise<unknown> => {
|
|
149
|
+
const context: ResolveContext = {
|
|
150
|
+
baseUrl: options.baseUrl,
|
|
151
|
+
headers: options.headers,
|
|
152
|
+
fetcher: options.fetcher ?? fetch,
|
|
153
|
+
maxDepth: options.maxDepth ?? 25,
|
|
154
|
+
cache: new Map([[options.baseUrl, schema]]),
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
return resolveNode(schema, schema, options.baseUrl, context, new Set(), 0);
|
|
158
|
+
};
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"license": "SEE LICENSE IN LICENSE",
|
|
9
9
|
"type": "module",
|
|
10
|
-
"version": "4.0.
|
|
10
|
+
"version": "4.0.7",
|
|
11
11
|
"publishConfig": {
|
|
12
12
|
"access": "public"
|
|
13
13
|
},
|
|
@@ -116,9 +116,9 @@
|
|
|
116
116
|
"update-notifier": "^7.3.1",
|
|
117
117
|
"uuid": "^10.0.0",
|
|
118
118
|
"zod": "^4.3.6",
|
|
119
|
+
"@eventcatalog/linter": "1.1.4",
|
|
119
120
|
"@eventcatalog/sdk": "2.25.0",
|
|
120
|
-
"@eventcatalog/visualiser": "^4.0.
|
|
121
|
-
"@eventcatalog/linter": "1.1.3"
|
|
121
|
+
"@eventcatalog/visualiser": "^4.0.1"
|
|
122
122
|
},
|
|
123
123
|
"devDependencies": {
|
|
124
124
|
"@astrojs/check": "^0.9.9",
|