@eventcatalog/core 2.65.0 → 3.0.0-beta.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/README.md +1 -26
- 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-NK6OYMRD.js → chunk-JB4YT5JY.js} +1 -1
- package/dist/{chunk-BMDTX5IN.js → chunk-TQ4HZREX.js} +1 -1
- package/dist/{chunk-IJRFYF4B.js → chunk-X4W4YC3U.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +1 -21
- package/dist/eventcatalog.config.d.cts +10 -0
- package/dist/eventcatalog.config.d.ts +10 -0
- package/dist/eventcatalog.js +3 -20
- package/eventcatalog/src/components/CopyAsMarkdown.tsx +19 -1
- package/eventcatalog/src/components/FavoriteButton.tsx +54 -0
- package/eventcatalog/src/components/Grids/DomainGrid.tsx +386 -362
- package/eventcatalog/src/components/Grids/MessageGrid.tsx +166 -518
- package/eventcatalog/src/components/Header.astro +48 -23
- package/eventcatalog/src/components/Lists/VersionList.astro +2 -2
- package/eventcatalog/src/components/MDX/Design/Design.astro +4 -1
- package/eventcatalog/src/components/MDX/Flow/Flow.astro +2 -1
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +3 -3
- package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +8 -2
- package/eventcatalog/src/components/SchemaExplorer/SchemaPageViewer.tsx +37 -0
- package/eventcatalog/src/components/Search/Search.astro +48 -28
- package/eventcatalog/src/components/Search/SearchModal.tsx +393 -702
- package/eventcatalog/src/components/SideNav/NestedSideBar/SearchBar.tsx +298 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/container.ts +66 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/domain.ts +101 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/flow.ts +29 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/message.ts +84 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/service.ts +147 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/builders/shared.ts +146 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +1073 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/sidebar-builder.ts +365 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/storage.ts +90 -0
- package/eventcatalog/src/components/SideNav/SideNav.astro +18 -28
- package/eventcatalog/src/content.config.ts +2 -0
- package/eventcatalog/src/enterprise/custom-documentation/pages/docs/custom/index.astro +10 -4
- package/eventcatalog/src/enterprise/eventcatalog-chat/pages/chat/index.astro +3 -3
- package/eventcatalog/src/layouts/DirectoryLayout.astro +2 -2
- package/eventcatalog/src/layouts/DiscoverLayout.astro +3 -3
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +85 -63
- package/eventcatalog/src/layouts/VisualiserLayout.astro +3 -3
- package/eventcatalog/src/pages/_index.astro +530 -110
- package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.ts +64 -0
- package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +29 -0
- package/eventcatalog/src/pages/directory/[type]/_index.data.ts +4 -4
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +1 -4
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/_index.data.ts +3 -3
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/index.astro +1 -5
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +362 -190
- package/eventcatalog/src/pages/docs/[type]/[id]/[version].md.ts +1 -1
- package/eventcatalog/src/pages/docs/[type]/[id]/index.astro +4 -4
- package/eventcatalog/src/pages/docs/[type]/[id]/language/_index.data.ts +1 -4
- package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +3 -27
- package/eventcatalog/src/pages/docs/teams/[id]/_index.data.ts +2 -2
- package/eventcatalog/src/pages/docs/users/[id]/_index.data.ts +2 -2
- package/eventcatalog/src/pages/index.astro +14 -5
- package/eventcatalog/src/pages/nav-index.json.ts +30 -0
- package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/_index.data.ts +77 -0
- package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/index.astro +90 -0
- package/eventcatalog/src/pages/schemas/{index.astro → explorer/index.astro} +3 -3
- package/eventcatalog/src/pages/studio.astro +3 -3
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/_index.data.ts +4 -3
- package/eventcatalog/src/pages/visualiser/[type]/[id]/index.astro +2 -2
- package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/_index.data.ts +4 -3
- package/eventcatalog/src/stores/favorites-store.ts +83 -0
- package/eventcatalog/src/stores/sidebar-store.ts +8 -0
- package/eventcatalog/src/utils/collections/changelogs.ts +7 -4
- package/eventcatalog/src/utils/{channels.ts → collections/channels.ts} +81 -31
- package/eventcatalog/src/utils/collections/commands.ts +134 -0
- package/eventcatalog/src/utils/collections/containers.ts +44 -33
- package/eventcatalog/src/utils/collections/domains.ts +204 -62
- package/eventcatalog/src/utils/{entities.ts → collections/entities.ts} +44 -24
- package/eventcatalog/src/utils/collections/events.ts +136 -0
- package/eventcatalog/src/utils/collections/flows.ts +59 -25
- package/eventcatalog/src/utils/{messages.ts → collections/messages.ts} +13 -4
- package/eventcatalog/src/utils/{queries.ts → collections/queries.ts} +49 -28
- package/eventcatalog/src/utils/collections/services.ts +100 -68
- package/eventcatalog/src/utils/collections/teams.ts +94 -0
- package/eventcatalog/src/utils/collections/users.ts +122 -0
- package/eventcatalog/src/utils/collections/util.ts +57 -1
- package/eventcatalog/src/utils/feature.ts +3 -1
- package/eventcatalog/src/utils/{collections/file-diffs.ts → file-diffs.ts} +1 -1
- package/eventcatalog/src/utils/node-graphs/container-node-graph.ts +2 -0
- package/eventcatalog/src/utils/node-graphs/domain-entity-map.ts +16 -6
- package/eventcatalog/src/utils/node-graphs/domains-canvas.ts +14 -10
- package/eventcatalog/src/utils/node-graphs/domains-node-graph.ts +36 -64
- package/eventcatalog/src/utils/node-graphs/flows-node-graph.ts +23 -19
- package/eventcatalog/src/utils/node-graphs/message-node-graph.ts +36 -49
- package/eventcatalog/src/utils/node-graphs/services-node-graph.ts +22 -18
- package/eventcatalog/src/utils/page-loaders/page-data-loader.ts +4 -4
- package/eventcatalog/tailwind.config.mjs +14 -0
- package/eventcatalog/tsconfig.json +2 -1
- package/package.json +7 -4
- package/eventcatalog/public/logo_old.png +0 -0
- package/eventcatalog/src/components/DiscoverInsight.astro +0 -61
- package/eventcatalog/src/components/Grids/ServiceGrid.tsx +0 -534
- package/eventcatalog/src/components/Lists/CustomSideBarSectionList.astro +0 -55
- package/eventcatalog/src/components/Lists/ProtocolList.tsx +0 -74
- package/eventcatalog/src/components/Lists/RepositoryList.astro +0 -37
- package/eventcatalog/src/components/Lists/SpecificationsList.astro +0 -67
- package/eventcatalog/src/components/SideBars/ChannelSideBar.astro +0 -204
- package/eventcatalog/src/components/SideBars/ContainerSideBar.astro +0 -180
- package/eventcatalog/src/components/SideBars/DomainSideBar.astro +0 -273
- package/eventcatalog/src/components/SideBars/EntitySideBar.astro +0 -139
- package/eventcatalog/src/components/SideBars/FlowSideBar.astro +0 -128
- package/eventcatalog/src/components/SideBars/MessageSideBar.astro +0 -248
- package/eventcatalog/src/components/SideBars/ServiceSideBar.astro +0 -294
- package/eventcatalog/src/components/SideNav/ListViewSideBar/components/CollapsibleGroup.tsx +0 -46
- package/eventcatalog/src/components/SideNav/ListViewSideBar/components/MessageList.tsx +0 -78
- package/eventcatalog/src/components/SideNav/ListViewSideBar/components/SpecificationList.tsx +0 -83
- package/eventcatalog/src/components/SideNav/ListViewSideBar/index.tsx +0 -1250
- package/eventcatalog/src/components/SideNav/ListViewSideBar/types.ts +0 -91
- package/eventcatalog/src/components/SideNav/ListViewSideBar/utils.ts +0 -201
- package/eventcatalog/src/components/SideNav/TreeView/getTreeView.ts +0 -190
- package/eventcatalog/src/components/SideNav/TreeView/index.tsx +0 -94
- package/eventcatalog/src/components/TreeView/index.tsx +0 -328
- package/eventcatalog/src/components/TreeView/styles.module.css +0 -264
- package/eventcatalog/src/components/TreeView/useSlots.ts +0 -95
- package/eventcatalog/src/pages/architecture/[type]/index.astro +0 -14
- package/eventcatalog/src/pages/architecture/architecture.astro +0 -101
- package/eventcatalog/src/pages/architecture/docs/[type]/index.astro +0 -14
- package/eventcatalog/src/utils/commands.ts +0 -112
- package/eventcatalog/src/utils/events.ts +0 -108
- package/eventcatalog/src/utils/generators/index.ts +0 -10
- package/eventcatalog/src/utils/teams.ts +0 -72
- package/eventcatalog/src/utils/users.ts +0 -72
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { CollectionTypes } from '@types';
|
|
2
|
+
import { getCollection } from 'astro:content';
|
|
3
|
+
import type { CollectionEntry } from 'astro:content';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
export type User = CollectionEntry<'users'>;
|
|
7
|
+
|
|
8
|
+
// Simple in-memory cache
|
|
9
|
+
let memoryCache: User[] = [];
|
|
10
|
+
|
|
11
|
+
export const getUsers = async (): Promise<User[]> => {
|
|
12
|
+
// console.time('✅ New getUsers');
|
|
13
|
+
|
|
14
|
+
if (memoryCache.length > 0) {
|
|
15
|
+
// console.timeEnd('✅ New getUsers');
|
|
16
|
+
return memoryCache;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 1. Fetch all collections in parallel
|
|
20
|
+
const [allUsers, allDomains, allServices, allEvents, allCommands, allQueries, allTeams] = await Promise.all([
|
|
21
|
+
getCollection('users'),
|
|
22
|
+
getCollection('domains'),
|
|
23
|
+
getCollection('services'),
|
|
24
|
+
getCollection('events'),
|
|
25
|
+
getCollection('commands'),
|
|
26
|
+
getCollection('queries'),
|
|
27
|
+
getCollection('teams'),
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
// 2. Filter users
|
|
31
|
+
const targetUsers = allUsers.filter((user) => user.data.hidden !== true);
|
|
32
|
+
const visibleTeams = allTeams.filter((team) => team.data.hidden !== true);
|
|
33
|
+
|
|
34
|
+
// 3. Process users (Optimization: Iterate once over relationships if possible,
|
|
35
|
+
// but since we need to check ownership for EACH user against ALL items,
|
|
36
|
+
// we can't easily invert the map without building an "owner" index first.
|
|
37
|
+
// Given users/teams count is usually lower than events/services, iterating users and filtering items is acceptable,
|
|
38
|
+
// OR we can index items by ownerID for O(1) lookup. Let's try indexing items by ownerID.)
|
|
39
|
+
|
|
40
|
+
// Build Owner Index: Map<OwnerID, Item[]>
|
|
41
|
+
const ownershipMap = new Map<string, CollectionEntry<CollectionTypes>[]>();
|
|
42
|
+
|
|
43
|
+
const addToIndex = (items: CollectionEntry<CollectionTypes>[]) => {
|
|
44
|
+
for (const item of items) {
|
|
45
|
+
if (item.data.owners) {
|
|
46
|
+
for (const owner of item.data.owners) {
|
|
47
|
+
if (!ownershipMap.has(owner.id)) {
|
|
48
|
+
ownershipMap.set(owner.id, []);
|
|
49
|
+
}
|
|
50
|
+
ownershipMap.get(owner.id)!.push(item);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
addToIndex(allDomains);
|
|
57
|
+
addToIndex(allServices);
|
|
58
|
+
addToIndex(allEvents);
|
|
59
|
+
addToIndex(allCommands);
|
|
60
|
+
addToIndex(allQueries);
|
|
61
|
+
|
|
62
|
+
// Team Membership Index: Map<UserID, Team[]>
|
|
63
|
+
const teamMembershipMap = new Map<string, typeof visibleTeams>();
|
|
64
|
+
for (const team of visibleTeams) {
|
|
65
|
+
if (team.data.members) {
|
|
66
|
+
for (const member of team.data.members) {
|
|
67
|
+
if (!teamMembershipMap.has(member.id)) {
|
|
68
|
+
teamMembershipMap.set(member.id, []);
|
|
69
|
+
}
|
|
70
|
+
teamMembershipMap.get(member.id)!.push(team);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const mappedUsers = targetUsers.map((user) => {
|
|
76
|
+
const userId = user.data.id;
|
|
77
|
+
const associatedTeams = teamMembershipMap.get(userId) || [];
|
|
78
|
+
const associatedTeamIds = associatedTeams.map((t) => t.data.id);
|
|
79
|
+
|
|
80
|
+
// Collect all owned items directly owned by user OR by their teams
|
|
81
|
+
const directOwnedItems = ownershipMap.get(userId) || [];
|
|
82
|
+
const teamOwnedItems = associatedTeamIds.flatMap((teamId) => ownershipMap.get(teamId) || []);
|
|
83
|
+
|
|
84
|
+
// Combine and deduplicate items (by ID+Version or just reference equality since they come from same source arrays)
|
|
85
|
+
const allOwnedItems = Array.from(new Set([...directOwnedItems, ...teamOwnedItems]));
|
|
86
|
+
|
|
87
|
+
// Categorize items
|
|
88
|
+
const ownedDomains = allOwnedItems.filter((i) => i.collection === 'domains') as CollectionEntry<'domains'>[];
|
|
89
|
+
const ownedServices = allOwnedItems.filter((i) => i.collection === 'services') as CollectionEntry<'services'>[];
|
|
90
|
+
const ownedEvents = allOwnedItems.filter((i) => i.collection === 'events') as CollectionEntry<'events'>[];
|
|
91
|
+
const ownedCommands = allOwnedItems.filter((i) => i.collection === 'commands') as CollectionEntry<'commands'>[];
|
|
92
|
+
const ownedQueries = allOwnedItems.filter((i) => i.collection === 'queries') as CollectionEntry<'queries'>[];
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
...user,
|
|
96
|
+
data: {
|
|
97
|
+
...user.data,
|
|
98
|
+
ownedDomains,
|
|
99
|
+
ownedServices,
|
|
100
|
+
ownedEvents,
|
|
101
|
+
ownedCommands,
|
|
102
|
+
ownedQueries,
|
|
103
|
+
associatedTeams,
|
|
104
|
+
},
|
|
105
|
+
catalog: {
|
|
106
|
+
path: path.join(user.collection, user.id.replace('/index.mdx', '')),
|
|
107
|
+
filePath: path.join(process.cwd(), 'src', 'catalog-files', user.collection, user.id.replace('/index.mdx', '')),
|
|
108
|
+
type: 'user',
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// order them by the name of the user
|
|
114
|
+
mappedUsers.sort((a, b) => {
|
|
115
|
+
return (a.data.name || a.data.id).localeCompare(b.data.name || b.data.id);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
memoryCache = mappedUsers;
|
|
119
|
+
// console.timeEnd('✅ New getUsers');
|
|
120
|
+
|
|
121
|
+
return mappedUsers;
|
|
122
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CollectionTypes } from '@types';
|
|
2
2
|
import type { CollectionEntry } from 'astro:content';
|
|
3
|
-
import { coerce, compare, eq, satisfies as satisfiesRange } from 'semver';
|
|
3
|
+
import semver, { coerce, compare, eq, satisfies as satisfiesRange } from 'semver';
|
|
4
4
|
|
|
5
5
|
export const getPreviousVersion = (version: string, versions: string[]) => {
|
|
6
6
|
const index = versions.indexOf(version);
|
|
@@ -116,6 +116,7 @@ export const resourceToCollectionMap = {
|
|
|
116
116
|
user: 'users',
|
|
117
117
|
team: 'teams',
|
|
118
118
|
container: 'containers',
|
|
119
|
+
entity: 'entities',
|
|
119
120
|
} as const;
|
|
120
121
|
|
|
121
122
|
export const collectionToResourceMap = {
|
|
@@ -129,6 +130,7 @@ export const collectionToResourceMap = {
|
|
|
129
130
|
users: 'user',
|
|
130
131
|
teams: 'team',
|
|
131
132
|
containers: 'container',
|
|
133
|
+
entities: 'entity',
|
|
132
134
|
} as const;
|
|
133
135
|
|
|
134
136
|
export const getDeprecatedDetails = (item: CollectionEntry<CollectionTypes>) => {
|
|
@@ -165,3 +167,57 @@ export const removeContentFromCollection = (collection: CollectionEntry<Collecti
|
|
|
165
167
|
catalog: undefined,
|
|
166
168
|
}));
|
|
167
169
|
};
|
|
170
|
+
|
|
171
|
+
// --- OPTIMIZATION HELPERS ---
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Groups items by ID and sorts them by version (Newest first).
|
|
175
|
+
* This allows O(1) lookup for "latest" (index 0) and specific versions.
|
|
176
|
+
*/
|
|
177
|
+
export const createVersionedMap = <T extends { data: { id: string; version?: string } }>(items: T[]) => {
|
|
178
|
+
const map = new Map<string, T[]>();
|
|
179
|
+
|
|
180
|
+
for (const item of items) {
|
|
181
|
+
const id = item.data.id;
|
|
182
|
+
if (!map.has(id)) map.set(id, []);
|
|
183
|
+
map.get(id)!.push(item);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Sort every entry so index [0] is always the latest version
|
|
187
|
+
for (const [key, list] of map.entries()) {
|
|
188
|
+
list.sort((a, b) => {
|
|
189
|
+
// specific version sorting logic (fallback to string compare if not valid semver)
|
|
190
|
+
const vA = a.data.version || '0.0.0';
|
|
191
|
+
const vB = b.data.version || '0.0.0';
|
|
192
|
+
return semver.valid(vB) && semver.valid(vA) ? semver.rcompare(vA, vB) : vB.localeCompare(vA);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return map;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Fast lookup helper.
|
|
200
|
+
* If version is provided, find it. If not, return the first (latest) item.
|
|
201
|
+
*/
|
|
202
|
+
export const findInMap = <T extends { data: { version?: string } }>(
|
|
203
|
+
map: Map<string, T[]>,
|
|
204
|
+
id: string,
|
|
205
|
+
version?: string
|
|
206
|
+
): T | undefined => {
|
|
207
|
+
const items = map.get(id);
|
|
208
|
+
if (!items || items.length === 0) return undefined;
|
|
209
|
+
|
|
210
|
+
// If no version specified or 'latest', return the first item (which is sorted to be latest)
|
|
211
|
+
if (!version || version === 'latest') return items[0];
|
|
212
|
+
|
|
213
|
+
// Try exact match
|
|
214
|
+
const exactMatch = items.find((i) => i.data.version === version);
|
|
215
|
+
if (exactMatch) return exactMatch;
|
|
216
|
+
|
|
217
|
+
// Try semver match if not exact
|
|
218
|
+
if (semver.validRange(version)) {
|
|
219
|
+
return items.find((i) => semver.satisfies(i.data.version || '0.0.0', version));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return undefined;
|
|
223
|
+
};
|
|
@@ -34,7 +34,7 @@ export const showCustomBranding = () => {
|
|
|
34
34
|
return isEventCatalogStarterEnabled() || isEventCatalogScaleEnabled();
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
export const isChangelogEnabled = () => config?.changelog?.enabled ??
|
|
37
|
+
export const isChangelogEnabled = () => config?.changelog?.enabled ?? false;
|
|
38
38
|
|
|
39
39
|
export const isCustomDocsEnabled = () => isEventCatalogStarterEnabled() || isEventCatalogScaleEnabled();
|
|
40
40
|
export const isEventCatalogChatEnabled = () => {
|
|
@@ -55,3 +55,5 @@ export const isAuthEnabled = () => {
|
|
|
55
55
|
};
|
|
56
56
|
|
|
57
57
|
export const isSSR = () => config?.output === 'server';
|
|
58
|
+
export const isRSSEnabled = () => config?.rss?.enabled ?? false;
|
|
59
|
+
export const isVisualiserEnabled = () => config?.visualiser?.enabled ?? true;
|
|
@@ -2,7 +2,7 @@ import { readdir, readFile } from 'node:fs/promises';
|
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
import { formatPatch, structuredPatch } from 'diff';
|
|
4
4
|
import { html, parse } from 'diff2html';
|
|
5
|
-
import { getItemsFromCollectionByIdAndSemverOrLatest } from './util';
|
|
5
|
+
import { getItemsFromCollectionByIdAndSemverOrLatest } from './collections/util';
|
|
6
6
|
import type { CollectionEntry } from 'astro:content';
|
|
7
7
|
import type { CollectionTypes } from '@types';
|
|
8
8
|
|
|
@@ -17,12 +17,14 @@ interface Props {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const getNodesAndEdges = async ({ id, version, defaultFlow, mode = 'simple', channelRenderMode = 'flat' }: Props) => {
|
|
20
|
+
// 1. Fetch data
|
|
20
21
|
const containers = await getContainers();
|
|
21
22
|
|
|
22
23
|
const flow = defaultFlow || createDagreGraph({ ranksep: 300, nodesep: 50 });
|
|
23
24
|
const nodes = [] as any,
|
|
24
25
|
edges = [] as any;
|
|
25
26
|
|
|
27
|
+
// Optimized: Use find since we're looking for a specific item
|
|
26
28
|
const container = containers.find((container) => container.data.id === id && container.data.version === version);
|
|
27
29
|
|
|
28
30
|
// Nothing found...
|
|
@@ -2,9 +2,9 @@ import { getCollection, getEntry } from 'astro:content';
|
|
|
2
2
|
import { generateIdForNode } from './utils/utils';
|
|
3
3
|
import ELK from 'elkjs/lib/elk.bundled.js';
|
|
4
4
|
import { MarkerType } from '@xyflow/react';
|
|
5
|
-
import {
|
|
5
|
+
import { createVersionedMap, findInMap } from '@utils/collections/util';
|
|
6
6
|
import { getVersionFromCollection } from '@utils/collections/versions';
|
|
7
|
-
import { getEntities, type Entity } from '@utils/entities';
|
|
7
|
+
import { getEntities, type Entity } from '@utils/collections/entities';
|
|
8
8
|
import { getDomains, type Domain } from '@utils/collections/domains';
|
|
9
9
|
import { getServices, type Service } from '@utils/collections/services';
|
|
10
10
|
|
|
@@ -21,9 +21,8 @@ export const getNodesAndEdges = async ({ id, version, entities, type = 'domains'
|
|
|
21
21
|
let nodes = [] as any,
|
|
22
22
|
edges = [] as any;
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
const allEntities = await getEntities();
|
|
26
|
-
const allServices = await getServices();
|
|
24
|
+
// 1. Fetch all collections in parallel
|
|
25
|
+
const [allDomains, allEntities, allServices] = await Promise.all([getDomains(), getEntities(), getServices()]);
|
|
27
26
|
|
|
28
27
|
let resource = null;
|
|
29
28
|
|
|
@@ -65,14 +64,25 @@ export const getNodesAndEdges = async ({ id, version, entities, type = 'domains'
|
|
|
65
64
|
const externalToDomain = Array.from(new Set<string>(listOfReferencedEntities as string[])) // Remove duplicates
|
|
66
65
|
.filter((entityId: any) => !resourceEntities.some((domainEntity: any) => domainEntity.id === entityId));
|
|
67
66
|
|
|
67
|
+
// 2. Build optimized maps
|
|
68
|
+
// Only build domain map if we have domains to search
|
|
69
|
+
// Only build entity map if we have entities to search
|
|
70
|
+
const entityMap = createVersionedMap(allEntities);
|
|
71
|
+
|
|
68
72
|
// Helper function to find which domain an entity belongs to
|
|
73
|
+
// Optimized to use direct iteration over domains (domains usually contain entity arrays)
|
|
74
|
+
// We can't easily map entity->domain without scanning domains first unless we build a reverse index.
|
|
75
|
+
// Given domains count is usually manageable, scanning is acceptable, OR we could build an index if needed.
|
|
76
|
+
// For now, let's keep the scan but make it efficient.
|
|
69
77
|
const findEntityDomain = (entityId: string) => {
|
|
70
78
|
return allDomains.find((domain) => domain.data.entities?.some((domainEntity: any) => domainEntity.data.id === entityId));
|
|
71
79
|
};
|
|
72
80
|
|
|
73
81
|
const addedExternalEntities = [];
|
|
82
|
+
|
|
74
83
|
for (const entityId of externalToDomain) {
|
|
75
|
-
|
|
84
|
+
// 3. Use map lookup
|
|
85
|
+
const externalEntity = findInMap(entityMap, entityId as string, 'latest') as Entity;
|
|
76
86
|
|
|
77
87
|
if (externalEntity) {
|
|
78
88
|
const nodeId = generateIdForNode(externalEntity);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { getCollection, type CollectionEntry } from 'astro:content';
|
|
2
2
|
import dagre from 'dagre';
|
|
3
3
|
import { generateIdForNode, createDagreGraph, calculatedNodes, createEdge } from '@utils/node-graphs/utils/utils';
|
|
4
|
-
import {
|
|
4
|
+
import { findInMap, createVersionedMap } from '@utils/collections/util';
|
|
5
5
|
import type { Node, Edge } from '@xyflow/react';
|
|
6
6
|
import { getDomains } from '@utils/collections/domains';
|
|
7
|
+
import type { CollectionMessageTypes } from '@types';
|
|
7
8
|
|
|
8
9
|
interface DomainCanvasData {
|
|
9
10
|
domainNodes: Node[];
|
|
@@ -69,10 +70,14 @@ export const getDomainsCanvasData = async (): Promise<DomainCanvasData> => {
|
|
|
69
70
|
} as Node);
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
// Get all messages for version resolution
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
// Get all messages for version resolution in parallel
|
|
74
|
+
const [events, commands, queries] = await Promise.all([
|
|
75
|
+
getCollection('events'),
|
|
76
|
+
getCollection('commands'),
|
|
77
|
+
getCollection('queries'),
|
|
78
|
+
]);
|
|
79
|
+
const allMessages = [...events, ...commands, ...queries];
|
|
80
|
+
const messageMap = createVersionedMap(allMessages);
|
|
76
81
|
|
|
77
82
|
// Map to track unique messages and their publishers/consumers across domains
|
|
78
83
|
const messageRelationships = new Map<
|
|
@@ -89,9 +94,9 @@ export const getDomainsCanvasData = async (): Promise<DomainCanvasData> => {
|
|
|
89
94
|
domainData.services.forEach((service: any) => {
|
|
90
95
|
// Track messages this service sends
|
|
91
96
|
const sendsRaw = service.data.sends ?? [];
|
|
97
|
+
|
|
92
98
|
const sendsHydrated = sendsRaw
|
|
93
|
-
.map((message: any) =>
|
|
94
|
-
.flat()
|
|
99
|
+
.map((message: any) => findInMap(messageMap, message.id, message.version))
|
|
95
100
|
.filter((e: any) => e !== undefined);
|
|
96
101
|
|
|
97
102
|
sendsHydrated.forEach((sentMessage: any) => {
|
|
@@ -115,8 +120,7 @@ export const getDomainsCanvasData = async (): Promise<DomainCanvasData> => {
|
|
|
115
120
|
// Track messages this service receives
|
|
116
121
|
const receivesRaw = service.data.receives ?? [];
|
|
117
122
|
const receivesHydrated = receivesRaw
|
|
118
|
-
.map((message: any) =>
|
|
119
|
-
.flat()
|
|
123
|
+
.map((message: any) => findInMap(messageMap, message.id, message.version))
|
|
120
124
|
.filter((e: any) => e !== undefined);
|
|
121
125
|
|
|
122
126
|
receivesHydrated.forEach((receivedMessage: any) => {
|
|
@@ -154,7 +158,7 @@ export const getDomainsCanvasData = async (): Promise<DomainCanvasData> => {
|
|
|
154
158
|
|
|
155
159
|
if (crossesDomainBoundary) {
|
|
156
160
|
// Find the actual message object
|
|
157
|
-
const messageObject =
|
|
161
|
+
const messageObject = findInMap(messageMap, message.id, message.version) as CollectionEntry<CollectionMessageTypes>;
|
|
158
162
|
|
|
159
163
|
if (messageObject) {
|
|
160
164
|
const messageNodeId = `message-${messageKey}`;
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from '@utils/node-graphs/utils/utils';
|
|
10
10
|
import { getNodesAndEdges as getServicesNodeAndEdges } from './services-node-graph';
|
|
11
11
|
import merge from 'lodash.merge';
|
|
12
|
-
import {
|
|
12
|
+
import { createVersionedMap, findInMap } from '@utils/collections/util';
|
|
13
13
|
import type { Node } from '@xyflow/react';
|
|
14
14
|
import { getProducersOfMessage } from '@utils/collections/services';
|
|
15
15
|
|
|
@@ -25,22 +25,29 @@ export const getNodesAndEdgesForDomainContextMap = async ({ defaultFlow = null }
|
|
|
25
25
|
let nodes = [] as any,
|
|
26
26
|
edges = [] as any;
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
// 1. Parallel Fetching
|
|
29
|
+
const [allDomains, services, events, commands, queries] = await Promise.all([
|
|
30
|
+
getCollection('domains'),
|
|
31
|
+
getCollection('services'),
|
|
32
|
+
getCollection('events'),
|
|
33
|
+
getCollection('commands'),
|
|
34
|
+
getCollection('queries'),
|
|
35
|
+
]);
|
|
35
36
|
|
|
37
|
+
const domains = allDomains.filter((domain) => !domain.id.includes('/versioned'));
|
|
36
38
|
const messages = [...events, ...commands, ...queries];
|
|
37
39
|
|
|
40
|
+
// 2. Build optimized maps
|
|
41
|
+
const serviceMap = createVersionedMap(services);
|
|
42
|
+
const messageMap = createVersionedMap(messages);
|
|
43
|
+
|
|
38
44
|
domains.forEach((domain, index) => {
|
|
39
45
|
const nodeId = generateIdForNode(domain);
|
|
40
46
|
const rawServices = domain.data.services ?? [];
|
|
47
|
+
|
|
48
|
+
// Optimized service resolution
|
|
41
49
|
const domainServices = rawServices
|
|
42
|
-
.map((service) =>
|
|
43
|
-
.flat()
|
|
50
|
+
.map((service) => findInMap(serviceMap, service.id, service.version))
|
|
44
51
|
.filter((e) => e !== undefined);
|
|
45
52
|
|
|
46
53
|
// Calculate domain node size based on services
|
|
@@ -60,14 +67,12 @@ export const getNodesAndEdgesForDomainContextMap = async ({ defaultFlow = null }
|
|
|
60
67
|
const rowIndex = Math.floor(index / DOMAINS_PER_ROW);
|
|
61
68
|
const colIndex = index % DOMAINS_PER_ROW;
|
|
62
69
|
|
|
63
|
-
const test = servicesCount * SERVICE_HEIGHT + PADDING * 2;
|
|
64
|
-
|
|
65
70
|
nodes.push({
|
|
66
71
|
id: nodeId,
|
|
67
72
|
type: 'group',
|
|
68
73
|
position: {
|
|
69
|
-
x: colIndex * (domainWidth + 400),
|
|
70
|
-
y: rowIndex * (domainHeight + 300),
|
|
74
|
+
x: colIndex * (domainWidth + 400),
|
|
75
|
+
y: rowIndex * (domainHeight + 300),
|
|
71
76
|
},
|
|
72
77
|
style: {
|
|
73
78
|
width: domainWidth,
|
|
@@ -107,7 +112,6 @@ export const getNodesAndEdgesForDomainContextMap = async ({ defaultFlow = null }
|
|
|
107
112
|
domainServices.forEach((service, serviceIndex) => {
|
|
108
113
|
const row = Math.floor(serviceIndex / SERVICES_PER_ROW);
|
|
109
114
|
const col = serviceIndex % SERVICES_PER_ROW;
|
|
110
|
-
const serviceNodeId = `service-${domain.id}-${service.id}`;
|
|
111
115
|
|
|
112
116
|
// Add spacing between services
|
|
113
117
|
const SERVICE_MARGIN = 25;
|
|
@@ -136,10 +140,13 @@ export const getNodesAndEdgesForDomainContextMap = async ({ defaultFlow = null }
|
|
|
136
140
|
const rawReceives = service.data.receives ?? [];
|
|
137
141
|
const rawSends = service.data.sends ?? [];
|
|
138
142
|
|
|
143
|
+
// Optimized message resolution
|
|
139
144
|
const receives = rawReceives
|
|
140
|
-
.map((receive) =>
|
|
141
|
-
.
|
|
142
|
-
|
|
145
|
+
.map((receive) => findInMap(messageMap, receive.id, receive.version))
|
|
146
|
+
.filter((msg): msg is any => !!msg); // Filter undefined
|
|
147
|
+
|
|
148
|
+
// Note: 'sends' was defined but not used in the original loop logic for edges?
|
|
149
|
+
// Based on original code, it iterates `receives`.
|
|
143
150
|
|
|
144
151
|
for (const receive of receives) {
|
|
145
152
|
const producers = getProducersOfMessage(services, receive);
|
|
@@ -148,19 +155,6 @@ export const getNodesAndEdgesForDomainContextMap = async ({ defaultFlow = null }
|
|
|
148
155
|
const isSameDomain = domainServices.some((domainService) => domainService.data.id === producer.data.id);
|
|
149
156
|
|
|
150
157
|
if (!isSameDomain) {
|
|
151
|
-
// WIP... adding messages?
|
|
152
|
-
// edges.push(createEdge({
|
|
153
|
-
// id: generatedIdForEdge(receive, service),
|
|
154
|
-
// source: generateIdForNode(receive),
|
|
155
|
-
// target: generateIdForNode(service),
|
|
156
|
-
// label: getEdgeLabelForServiceAsTarget(receive),
|
|
157
|
-
// zIndex: 1000,
|
|
158
|
-
// }));
|
|
159
|
-
|
|
160
|
-
// Find the producer and consumer nodes to get their positions
|
|
161
|
-
// const producerNode = nodes.find(n => n.id === generateIdForNode(producer));
|
|
162
|
-
// const consumerNode = nodes.find(n => n.id === generateIdForNode(service));
|
|
163
|
-
|
|
164
158
|
edges.push(
|
|
165
159
|
createEdge({
|
|
166
160
|
id: generatedIdForEdge(producer, service),
|
|
@@ -170,32 +164,6 @@ export const getNodesAndEdgesForDomainContextMap = async ({ defaultFlow = null }
|
|
|
170
164
|
zIndex: 1000,
|
|
171
165
|
})
|
|
172
166
|
);
|
|
173
|
-
|
|
174
|
-
// // Calculate middle position between producer and consumer
|
|
175
|
-
// const messageX = (producerNode?.position?.x ?? 0) +
|
|
176
|
-
// ((consumerNode?.position?.x ?? 0) - (producerNode?.position?.x ?? 0)) / 2;
|
|
177
|
-
// const messageY = (producerNode?.position?.y ?? 0) +
|
|
178
|
-
// ((consumerNode?.position?.y ?? 0) - (producerNode?.position?.y ?? 0)) / 2;
|
|
179
|
-
|
|
180
|
-
// nodes.push({
|
|
181
|
-
// id: generateIdForNode(receive),
|
|
182
|
-
// type: receive.collection,
|
|
183
|
-
// sourcePosition: 'right',
|
|
184
|
-
// targetPosition: 'left',
|
|
185
|
-
// data: {
|
|
186
|
-
// message: receive,
|
|
187
|
-
// mode: 'full',
|
|
188
|
-
// },
|
|
189
|
-
// position: { x: messageX, y: messageY },
|
|
190
|
-
// });
|
|
191
|
-
|
|
192
|
-
// edges.push(createEdge({
|
|
193
|
-
// id: generatedIdForEdge(producer, receive),
|
|
194
|
-
// source: generateIdForNode(producer),
|
|
195
|
-
// target: generateIdForNode(receive),
|
|
196
|
-
// label: getEdgeLabelForServiceAsTarget(receive),
|
|
197
|
-
// zIndex: 1000,
|
|
198
|
-
// }));
|
|
199
167
|
}
|
|
200
168
|
}
|
|
201
169
|
}
|
|
@@ -230,7 +198,8 @@ export const getNodesAndEdges = async ({
|
|
|
230
198
|
let nodes = new Map(),
|
|
231
199
|
edges = new Map();
|
|
232
200
|
|
|
233
|
-
|
|
201
|
+
// 1. Parallel Fetching
|
|
202
|
+
const [domains, services] = await Promise.all([getCollection('domains'), getCollection('services')]);
|
|
234
203
|
|
|
235
204
|
const domain = domains.find((service) => service.data.id === id && service.data.version === version);
|
|
236
205
|
|
|
@@ -242,19 +211,22 @@ export const getNodesAndEdges = async ({
|
|
|
242
211
|
};
|
|
243
212
|
}
|
|
244
213
|
|
|
214
|
+
// 2. Build optimized maps
|
|
215
|
+
const serviceMap = createVersionedMap(services);
|
|
216
|
+
const domainMap = createVersionedMap(domains);
|
|
217
|
+
|
|
245
218
|
const rawServices = domain?.data.services || [];
|
|
246
219
|
const rawSubDomains = domain?.data.domains || [];
|
|
247
220
|
|
|
248
|
-
|
|
249
|
-
|
|
221
|
+
// Optimized hydration
|
|
250
222
|
const domainServicesWithVersion = rawServices
|
|
251
|
-
.map((service) =>
|
|
252
|
-
.
|
|
223
|
+
.map((service) => findInMap(serviceMap, service.id, service.version))
|
|
224
|
+
.filter((s): s is any => !!s)
|
|
253
225
|
.map((svc) => ({ id: svc.data.id, version: svc.data.version }));
|
|
254
226
|
|
|
255
227
|
const domainSubDomainsWithVersion = rawSubDomains
|
|
256
|
-
.map((subDomain) =>
|
|
257
|
-
.
|
|
228
|
+
.map((subDomain) => findInMap(domainMap, subDomain.id, subDomain.version))
|
|
229
|
+
.filter((d): d is any => !!d)
|
|
258
230
|
.map((svc) => ({ id: svc.data.id, version: svc.data.version }));
|
|
259
231
|
|
|
260
232
|
// Get all the nodes for everyhing
|
|
@@ -3,7 +3,7 @@ import dagre from 'dagre';
|
|
|
3
3
|
import { createDagreGraph, calculatedNodes } from '@utils/node-graphs/utils/utils';
|
|
4
4
|
import { MarkerType } from '@xyflow/react';
|
|
5
5
|
import type { Node as NodeType } from '@xyflow/react';
|
|
6
|
-
import {
|
|
6
|
+
import { createVersionedMap, findInMap } from '@utils/collections/util';
|
|
7
7
|
|
|
8
8
|
type DagreGraph = any;
|
|
9
9
|
|
|
@@ -15,9 +15,8 @@ interface Props {
|
|
|
15
15
|
renderAllEdges?: boolean;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const getServiceNode = (step: any,
|
|
19
|
-
const
|
|
20
|
-
const service = servicesForVersion?.[0];
|
|
18
|
+
const getServiceNode = (step: any, serviceMap: Map<string, any[]>) => {
|
|
19
|
+
const service = findInMap(serviceMap, step.service.id, step.service.version);
|
|
21
20
|
return {
|
|
22
21
|
...step,
|
|
23
22
|
type: service ? service.collection : 'step',
|
|
@@ -25,9 +24,8 @@ const getServiceNode = (step: any, services: CollectionEntry<'services'>[]) => {
|
|
|
25
24
|
};
|
|
26
25
|
};
|
|
27
26
|
|
|
28
|
-
const getFlowNode = (step: any,
|
|
29
|
-
const
|
|
30
|
-
const flow = flowsForVersion?.[0];
|
|
27
|
+
const getFlowNode = (step: any, flowMap: Map<string, any[]>) => {
|
|
28
|
+
const flow = findInMap(flowMap, step.flow.id, step.flow.version);
|
|
31
29
|
return {
|
|
32
30
|
...step,
|
|
33
31
|
type: flow ? flow.collection : 'step',
|
|
@@ -35,9 +33,8 @@ const getFlowNode = (step: any, flows: CollectionEntry<'flows'>[]) => {
|
|
|
35
33
|
};
|
|
36
34
|
};
|
|
37
35
|
|
|
38
|
-
const getMessageNode = (step: any,
|
|
39
|
-
const
|
|
40
|
-
const message = messagesForVersion[0];
|
|
36
|
+
const getMessageNode = (step: any, messageMap: Map<string, any[]>) => {
|
|
37
|
+
const message = findInMap(messageMap, step.message.id, step.message.version);
|
|
41
38
|
return {
|
|
42
39
|
...step,
|
|
43
40
|
type: message ? message.collection : 'step',
|
|
@@ -50,7 +47,15 @@ export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simpl
|
|
|
50
47
|
const nodes = [] as any,
|
|
51
48
|
edges = [] as any;
|
|
52
49
|
|
|
53
|
-
|
|
50
|
+
// Fetch all collections in parallel
|
|
51
|
+
const [flows, events, commands, queries, services] = await Promise.all([
|
|
52
|
+
getCollection('flows'),
|
|
53
|
+
getCollection('events'),
|
|
54
|
+
getCollection('commands'),
|
|
55
|
+
getCollection('queries'),
|
|
56
|
+
getCollection('services'),
|
|
57
|
+
]);
|
|
58
|
+
|
|
54
59
|
const flow = flows.find((flow) => flow.data.id === id && flow.data.version === version);
|
|
55
60
|
|
|
56
61
|
// Nothing found...
|
|
@@ -61,20 +66,19 @@ export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simpl
|
|
|
61
66
|
};
|
|
62
67
|
}
|
|
63
68
|
|
|
64
|
-
|
|
65
|
-
const commands = await getCollection('commands');
|
|
66
|
-
const queries = await getCollection('queries');
|
|
67
|
-
const services = await getCollection('services');
|
|
68
|
-
|
|
69
|
+
// Build maps for O(1) lookups
|
|
69
70
|
const messages = [...events, ...commands, ...queries];
|
|
71
|
+
const messageMap = createVersionedMap(messages);
|
|
72
|
+
const serviceMap = createVersionedMap(services);
|
|
73
|
+
const flowMap = createVersionedMap(flows);
|
|
70
74
|
|
|
71
75
|
const steps = flow?.data.steps || [];
|
|
72
76
|
|
|
73
77
|
// Hydrate the steps with information they may need.
|
|
74
78
|
const hydratedSteps = steps.map((step: any) => {
|
|
75
|
-
if (step.service) return getServiceNode(step,
|
|
76
|
-
if (step.flow) return getFlowNode(step,
|
|
77
|
-
if (step.message) return getMessageNode(step,
|
|
79
|
+
if (step.service) return getServiceNode(step, serviceMap);
|
|
80
|
+
if (step.flow) return getFlowNode(step, flowMap);
|
|
81
|
+
if (step.message) return getMessageNode(step, messageMap);
|
|
78
82
|
if (step.actor) return { ...step, type: 'actor', actor: step.actor };
|
|
79
83
|
if (step.custom) return { ...step, type: 'custom', custom: step.custom };
|
|
80
84
|
if (step.externalSystem) return { ...step, type: 'externalSystem', externalSystem: step.externalSystem };
|