@eventcatalog/core 2.4.0 → 2.5.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 (44) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +8 -1
  3. package/bin/dist/eventcatalog.cjs +5 -4
  4. package/bin/dist/eventcatalog.js +5 -4
  5. package/package.json +8 -6
  6. package/scripts/catalog-to-astro-content-directory.js +32 -1
  7. package/scripts/default-files-for-collections/flows.md +11 -0
  8. package/scripts/default-files-for-collections/pages.md +1 -0
  9. package/scripts/watcher.js +1 -2
  10. package/src/components/DocsNavigation.astro +5 -1
  11. package/src/components/MDX/Flow/Flow.astro +63 -0
  12. package/src/components/MDX/NodeGraph/NodeGraph.astro +12 -0
  13. package/src/components/MDX/NodeGraph/NodeGraph.tsx +75 -33
  14. package/src/components/MDX/NodeGraph/Nodes/ExternalSystem.tsx +79 -0
  15. package/src/components/MDX/NodeGraph/Nodes/Service.tsx +0 -12
  16. package/src/components/MDX/NodeGraph/Nodes/Step.tsx +69 -0
  17. package/src/components/MDX/NodeGraph/Nodes/User.tsx +79 -0
  18. package/src/components/MDX/components.tsx +2 -0
  19. package/src/components/SideBars/DomainSideBar.astro +1 -1
  20. package/src/components/Tables/columns/FlowTableColumns.tsx +82 -0
  21. package/src/components/Tables/columns/index.tsx +3 -0
  22. package/src/content/config.ts +65 -5
  23. package/src/layouts/DiscoverLayout.astro +11 -1
  24. package/src/layouts/VisualiserLayout.astro +33 -22
  25. package/src/pages/discover/[type]/index.astro +3 -0
  26. package/src/pages/docs/[type]/[id]/[version]/index.astro +12 -3
  27. package/src/pages/docs/teams/[id]/index.astro +14 -0
  28. package/src/pages/docs/users/[id]/index.astro +23 -4
  29. package/src/pages/visualiser/[type]/[id]/[version]/index.astro +9 -2
  30. package/src/types/index.ts +1 -1
  31. package/src/utils/collections/util.ts +22 -0
  32. package/src/utils/commands/node-graph.ts +13 -2
  33. package/src/utils/config/catalog.ts +1 -0
  34. package/src/utils/domains/domains.ts +3 -5
  35. package/src/utils/domains/node-graph.ts +28 -9
  36. package/src/utils/events/node-graph.ts +13 -2
  37. package/src/utils/flows/flows.ts +59 -0
  38. package/src/utils/flows/node-graph.ts +153 -0
  39. package/src/utils/services/node-graph.ts +16 -5
  40. package/src/utils/services/services.ts +3 -26
  41. package/src/utils/teams.ts +7 -1
  42. package/src/utils/users.ts +8 -0
  43. package/src/utils/versions/versions.ts +26 -0
  44. package/tailwind.config.mjs +1 -0
@@ -29,11 +29,18 @@ const { render, ...props } = Astro.props;
29
29
 
30
30
  const { Content } = await render();
31
31
 
32
+ const domains = props.data.ownedDomains as CollectionEntry<'domains'>[];
32
33
  const services = props.data.ownedServices as CollectionEntry<'services'>[];
33
34
  const events = props.data.ownedEvents as CollectionEntry<'events'>[];
34
35
  const commands = props.data.ownedCommands as CollectionEntry<'commands'>[];
35
36
  const teams = props.data.associatedTeams as CollectionEntry<'teams'>[];
36
37
 
38
+ const ownedDomainsList = domains.map((p) => ({
39
+ label: `${p.data.name}`,
40
+ href: buildUrl(`/docs/${p.collection}/${p.data.id}/${p.data.version}`),
41
+ tag: `v${p.data.version}`,
42
+ }));
43
+
37
44
  const ownedServicesList = services.map((p) => ({
38
45
  label: `${p.data.name} (service)`,
39
46
  href: buildUrl(`/docs/${p.collection}/${p.data.id}/${p.data.version}`),
@@ -41,7 +48,8 @@ const ownedServicesList = services.map((p) => ({
41
48
  }));
42
49
 
43
50
  const ownedMessageList = [...events, ...commands].map((p) => ({
44
- label: `${p.data.name} (${p.collection.slice(0, -1)})`,
51
+ label: `${p.data.name}`,
52
+ badge: p.collection,
45
53
  color: p.collection === 'events' ? 'orange' : 'blue',
46
54
  tag: `v${p.data.version}`,
47
55
  href: buildUrl(`/docs/${p.collection}/${p.data.id}/${p.data.version}`),
@@ -91,7 +99,11 @@ const associatedTeams = teams.map((o) => ({
91
99
  <div class="border-b border-gray-200">
92
100
  <div class="mx-auto max-w-7xl px-6 lg:px-8">
93
101
  <div class="mx-auto max-w-2xl lg:max-w-none">
94
- <dl class="hidden lg:grid grid-cols-1 gap-0.5 overflow-hidden rounded-2xl text-center sm:grid-cols-3 lg:grid-cols-3">
102
+ <dl class="hidden lg:grid grid-cols-1 gap-0.5 overflow-hidden rounded-2xl text-center sm:grid-cols-4 lg:grid-cols-4">
103
+ <div class="flex flex-col p-8">
104
+ <dt class="text-sm font-semibold leading-6 text-gray-600"># owned domains</dt>
105
+ <dd class="order-first text-3xl font-semibold tracking-tight text-gray-900">{ownedDomainsList.length}</dd>
106
+ </div>
95
107
  <div class="flex flex-col p-8">
96
108
  <dt class="text-sm font-semibold leading-6 text-gray-600"># owned services</dt>
97
109
  <dd class="order-first text-3xl font-semibold tracking-tight text-gray-900">{ownedServicesList.length}</dd>
@@ -118,18 +130,25 @@ const associatedTeams = teams.map((o) => ({
118
130
  </main>
119
131
  <aside class="sticky top-20 h-[calc(100vh-theme(spacing.16))] w-56 overflow-y-auto">
120
132
  <div class="divide-y-2 divide-gray-100">
133
+ <PillList
134
+ color="pink"
135
+ title={`Owned domains (${ownedDomainsList.length})`}
136
+ pills={ownedDomainsList}
137
+ emptyMessage={`${props.data.name} does not own any domains.`}
138
+ client:load
139
+ />
121
140
  <PillList
122
141
  color="pink"
123
142
  title={`Owned services (${ownedServicesList.length})`}
124
143
  pills={ownedServicesList}
125
- emptyMessage={`${props.data.name} does not own any services .`}
144
+ emptyMessage={`${props.data.name} does not own any services.`}
126
145
  client:load
127
146
  />
128
147
  <PillList
129
148
  color="orange"
130
149
  title={`Owned messages (${ownedMessageList.length})`}
131
150
  pills={ownedMessageList}
132
- emptyMessage={`${props.data.name} does not own any messages .`}
151
+ emptyMessage={`${props.data.name} does not own any messages.`}
133
152
  client:load
134
153
  />
135
154
  <OwnersList
@@ -5,12 +5,19 @@ import type { CollectionTypes } from '@types';
5
5
  import { getCommands } from '@utils/commands';
6
6
  import { getDomains } from '@utils/domains/domains';
7
7
  import { getEvents } from '@utils/events';
8
+ import { getFlows } from '@utils/flows/flows';
8
9
  import { getServices } from '@utils/services/services';
9
10
  import { buildUrl } from '@utils/url-builder';
10
11
  import type { CollectionEntry } from 'astro:content';
11
12
 
12
13
  export async function getStaticPaths() {
13
- const [services, events, commands, domains] = await Promise.all([getServices(), getEvents(), getCommands(), getDomains()]);
14
+ const [services, events, commands, domains, flows] = await Promise.all([
15
+ getServices(),
16
+ getEvents(),
17
+ getCommands(),
18
+ getDomains(),
19
+ getFlows(),
20
+ ]);
14
21
 
15
22
  const buildPages = (collection: CollectionEntry<CollectionTypes>[]) => {
16
23
  return collection.map((item) => ({
@@ -26,7 +33,7 @@ export async function getStaticPaths() {
26
33
  }));
27
34
  };
28
35
 
29
- return [...buildPages(services), ...buildPages(events), ...buildPages(commands), ...buildPages(domains)];
36
+ return [...buildPages(services), ...buildPages(events), ...buildPages(commands), ...buildPages(domains), ...buildPages(flows)];
30
37
  }
31
38
 
32
39
  const props = Astro.props;
@@ -1,2 +1,2 @@
1
- export type CollectionTypes = 'commands' | 'events' | 'domains' | 'services';
1
+ export type CollectionTypes = 'commands' | 'events' | 'domains' | 'services' | 'flows';
2
2
  export type CollectionMessageTypes = 'commands' | 'events';
@@ -1,5 +1,6 @@
1
1
  import type { CollectionTypes } from '@types';
2
2
  import type { CollectionEntry } from 'astro:content';
3
+ import { satisfies, validRange } from 'semver';
3
4
 
4
5
  export const getVersions = (data: CollectionEntry<CollectionTypes>[]) => {
5
6
  const allVersions = data.map((item) => item.data.version).sort();
@@ -20,3 +21,24 @@ export const getVersionForCollectionItem = (
20
21
  const latestVersion = versions[0];
21
22
  return { versions, latestVersion };
22
23
  };
24
+
25
+ export const getItemsFromCollectionByIdAndSemverOrLatest = <T extends { data: { id: string; version: string } }>(
26
+ collection: T[],
27
+ id: string,
28
+ version?: string
29
+ ): T[] => {
30
+ const semverRange = validRange(version);
31
+ const filteredCollection = collection.filter((c) => c.data.id == id);
32
+
33
+ if (semverRange) {
34
+ return filteredCollection.filter((c) => satisfies(c.data.version, semverRange));
35
+ }
36
+
37
+ // Order by version
38
+ const sorted = filteredCollection.sort((a, b) => {
39
+ return a.data.version.localeCompare(b.data.version);
40
+ });
41
+
42
+ // latest version
43
+ return sorted.length > 0 ? [sorted[sorted.length - 1]] : [];
44
+ };
@@ -2,6 +2,7 @@ import { getCommands } from '@utils/commands';
2
2
  import { calculatedNodes, createDagreGraph, generateIdForNode, generatedIdForEdge } from '@utils/node-graph-utils/utils';
3
3
  import { type CollectionEntry } from 'astro:content';
4
4
  import dagre from 'dagre';
5
+ import { MarkerType } from 'reactflow';
5
6
 
6
7
  type DagreGraph = any;
7
8
 
@@ -50,7 +51,12 @@ export const getNodesAndEdges = async ({ id, version, defaultFlow, mode = 'simpl
50
51
  label: 'invokes',
51
52
  animated: false,
52
53
  markerEnd: {
53
- type: 'arrow',
54
+ type: MarkerType.ArrowClosed,
55
+ width: 40,
56
+ height: 40,
57
+ },
58
+ style: {
59
+ strokeWidth: 1,
54
60
  },
55
61
  });
56
62
  });
@@ -84,7 +90,12 @@ export const getNodesAndEdges = async ({ id, version, defaultFlow, mode = 'simpl
84
90
  label: 'accepts',
85
91
  animated: false,
86
92
  markerEnd: {
87
- type: 'arrow',
93
+ type: MarkerType.ArrowClosed,
94
+ width: 40,
95
+ height: 40,
96
+ },
97
+ style: {
98
+ strokeWidth: 1,
88
99
  },
89
100
  });
90
101
  });
@@ -9,6 +9,7 @@ export type CatalogConfig = {
9
9
  sidebar: {
10
10
  showPageHeadings?: boolean;
11
11
  domains?: SideBarItemConfig;
12
+ flows?: SideBarItemConfig;
12
13
  services?: SideBarItemConfig;
13
14
  messages?: SideBarItemConfig;
14
15
  teams?: SideBarItemConfig;
@@ -1,4 +1,4 @@
1
- import { getVersionForCollectionItem, getVersions } from '@utils/collections/util';
1
+ import { getItemsFromCollectionByIdAndSemverOrLatest, getVersionForCollectionItem } from '@utils/collections/util';
2
2
  import { getCollection } from 'astro:content';
3
3
  import type { CollectionEntry } from 'astro:content';
4
4
  import path from 'path';
@@ -28,10 +28,8 @@ export const getDomains = async ({ getAllVersions = true }: Props = {}): Promise
28
28
  const servicesInDomain = domain.data.services || [];
29
29
 
30
30
  const services = servicesInDomain
31
- .map((_service) => {
32
- return servicesCollection.find((service) => _service.id === service.data.id);
33
- })
34
- .filter((e) => e !== undefined);
31
+ .map((_service) => getItemsFromCollectionByIdAndSemverOrLatest(servicesCollection, _service.id, _service.version))
32
+ .flat();
35
33
 
36
34
  return {
37
35
  ...domain,
@@ -1,14 +1,15 @@
1
- // import { getColor } from '@utils/colors';
2
1
  import { getCollection } from 'astro:content';
3
2
  import dagre from 'dagre';
4
3
  import { getNodesAndEdges as getServicesNodeAndEdges } from '../services/node-graph';
4
+ import merge from 'lodash.merge';
5
+ import { getItemsFromCollectionByIdAndSemverOrLatest } from '@utils/collections/util';
5
6
 
6
7
  type DagreGraph = any;
7
8
 
8
9
  // Creates a new dagre graph
9
10
  export const getDagreGraph = () => {
10
11
  const graph = new dagre.graphlib.Graph({ compound: true });
11
- graph.setGraph({ rankdir: 'LR', ranksep: 200, nodesep: 200 });
12
+ graph.setGraph({ rankdir: 'LR', ranksep: 200, nodesep: 100 });
12
13
  graph.setDefaultEdgeLabel(() => ({}));
13
14
  return graph;
14
15
  };
@@ -22,8 +23,8 @@ interface Props {
22
23
 
23
24
  export const getNodesAndEdges = async ({ id, version, defaultFlow, mode = 'simple' }: Props) => {
24
25
  const flow = defaultFlow || getDagreGraph();
25
- let nodes = [] as any,
26
- edges = [] as any;
26
+ let nodes = new Map(),
27
+ edges = new Map();
27
28
 
28
29
  const domains = await getCollection('domains');
29
30
 
@@ -39,9 +40,16 @@ export const getNodesAndEdges = async ({ id, version, defaultFlow, mode = 'simpl
39
40
 
40
41
  const rawServices = domain?.data.services || [];
41
42
 
43
+ const servicesCollection = await getCollection('services');
44
+
45
+ const domainServicesWithVersion = rawServices
46
+ .map((service) => getItemsFromCollectionByIdAndSemverOrLatest(servicesCollection, service.id, service.version))
47
+ .flat()
48
+ .map((svc) => ({ id: svc.data.id, version: svc.data.version }));
49
+
42
50
  // Get all the nodes for everyhing
43
51
 
44
- for (const service of rawServices) {
52
+ for (const service of domainServicesWithVersion) {
45
53
  const { nodes: serviceNodes, edges: serviceEdges } = await getServicesNodeAndEdges({
46
54
  id: service.id,
47
55
  version: service.version,
@@ -49,12 +57,23 @@ export const getNodesAndEdges = async ({ id, version, defaultFlow, mode = 'simpl
49
57
  mode,
50
58
  renderAllEdges: true,
51
59
  });
52
- nodes = [...nodes, ...serviceNodes];
53
- edges = [...edges, ...serviceEdges];
60
+ serviceNodes.forEach((n) => {
61
+ /**
62
+ * A message could be sent by one service and received by another service on the same domain.
63
+ * So, we need deep merge the message to keep the `showSource` and `showTarget` as true.
64
+ *
65
+ * Let's see an example:
66
+ * Take an `OrderPlaced` event sent by the `OrderService` `{ showSource: true }` and
67
+ * received by `PaymentService` `{ showTarget: true }`.
68
+ */
69
+ nodes.set(n.id, nodes.has(n.id) ? merge(nodes.get(n.id), n) : n);
70
+ });
71
+ // @ts-ignore
72
+ serviceEdges.forEach((e) => edges.set(e.id, e));
54
73
  }
55
74
 
56
75
  return {
57
- nodes: nodes,
58
- edges: edges,
76
+ nodes: [...nodes.values()],
77
+ edges: [...edges.values()],
59
78
  };
60
79
  };
@@ -3,6 +3,7 @@ import { getEvents } from '@utils/events';
3
3
  import type { CollectionEntry } from 'astro:content';
4
4
  import dagre from 'dagre';
5
5
  import { calculatedNodes, createDagreGraph, generatedIdForEdge, generateIdForNode } from '../node-graph-utils/utils';
6
+ import { MarkerType } from 'reactflow';
6
7
 
7
8
  type DagreGraph = any;
8
9
 
@@ -51,7 +52,12 @@ export const getNodesAndEdges = async ({ id, version, defaultFlow, mode = 'simpl
51
52
  label: 'publishes event',
52
53
  animated: false,
53
54
  markerEnd: {
54
- type: 'arrow',
55
+ type: MarkerType.ArrowClosed,
56
+ width: 40,
57
+ height: 40,
58
+ },
59
+ style: {
60
+ strokeWidth: 1,
55
61
  },
56
62
  });
57
63
  });
@@ -85,7 +91,12 @@ export const getNodesAndEdges = async ({ id, version, defaultFlow, mode = 'simpl
85
91
  label: 'subscribed by',
86
92
  animated: false,
87
93
  markerEnd: {
88
- type: 'arrow',
94
+ type: MarkerType.ArrowClosed,
95
+ width: 40,
96
+ height: 40,
97
+ },
98
+ style: {
99
+ strokeWidth: 1,
89
100
  },
90
101
  });
91
102
  });
@@ -0,0 +1,59 @@
1
+ import { getVersionForCollectionItem, getVersions } from '@utils/collections/util';
2
+ import { getVersionFromCollection } from '@utils/versions/versions';
3
+ import { getCollection } from 'astro:content';
4
+ import type { CollectionEntry } from 'astro:content';
5
+ import path from 'path';
6
+
7
+ const PROJECT_DIR = process.env.PROJECT_DIR || process.cwd();
8
+
9
+ export type Flow = CollectionEntry<'flows'>;
10
+
11
+ interface Props {
12
+ getAllVersions?: boolean;
13
+ }
14
+
15
+ export const getFlows = async ({ getAllVersions = true }: Props = {}): Promise<Flow[]> => {
16
+ // Get flows that are not versioned
17
+ const flows = await getCollection('flows', (flow) => {
18
+ return (getAllVersions || !flow.slug.includes('versioned')) && flow.data.hidden !== true;
19
+ });
20
+
21
+ const events = await getCollection('events');
22
+ const commands = await getCollection('commands');
23
+
24
+ const allMessages = [...events, ...commands];
25
+
26
+ // @ts-ignore // TODO: Fix this type
27
+ return flows.map((flow) => {
28
+ // @ts-ignore
29
+ const { latestVersion, versions } = getVersionForCollectionItem(flow, flows);
30
+ const steps = flow.data.steps || [];
31
+
32
+ const hydrateSteps = steps.map((step) => {
33
+ if (!step.message) return { ...flow, data: { ...flow.data, type: 'node' } };
34
+ const message = getVersionFromCollection(allMessages, step.message.id, step.message.version);
35
+ return {
36
+ ...step,
37
+ type: 'message',
38
+ message: message,
39
+ };
40
+ });
41
+
42
+ return {
43
+ ...flow,
44
+ data: {
45
+ ...flow.data,
46
+ steps: hydrateSteps,
47
+ versions,
48
+ latestVersion,
49
+ },
50
+ catalog: {
51
+ path: path.join(flow.collection, flow.id.replace('/index.mdx', '')),
52
+ absoluteFilePath: path.join(PROJECT_DIR, flow.collection, flow.id.replace('/index.mdx', '/index.md')),
53
+ filePath: path.join(process.cwd(), 'src', 'catalog-files', flow.collection, flow.id.replace('/index.mdx', '')),
54
+ publicPath: path.join('/generated', flow.collection, flow.id.replace('/index.mdx', '')),
55
+ type: 'flow',
56
+ },
57
+ };
58
+ });
59
+ };
@@ -0,0 +1,153 @@
1
+ import { getCollection, type CollectionEntry } from 'astro:content';
2
+ import dagre from 'dagre';
3
+ import { createDagreGraph, calculatedNodes } from '@utils/node-graph-utils/utils';
4
+ import { MarkerType } from 'reactflow';
5
+ import type { Node as NodeType } from 'reactflow';
6
+ import { getVersionFromCollection } from '@utils/versions/versions';
7
+
8
+ type DagreGraph = any;
9
+
10
+ interface Props {
11
+ id: string;
12
+ version: string;
13
+ defaultFlow?: DagreGraph;
14
+ mode?: 'simple' | 'full';
15
+ renderAllEdges?: boolean;
16
+ }
17
+
18
+ const getServiceNode = (step: any, services: CollectionEntry<'services'>[]) => {
19
+ const service = services.find(
20
+ (service) => service.data.id === step.service.id && service.data.version === step.service.version
21
+ );
22
+ return {
23
+ ...step,
24
+ type: service ? service.collection : 'step',
25
+ service,
26
+ };
27
+ };
28
+
29
+ const getMessageNode = (step: any, messages: CollectionEntry<'events' | 'commands'>[]) => {
30
+ const messagesForVersion = getVersionFromCollection(messages, step.message.id, step.message.version);
31
+ const message = messagesForVersion[0];
32
+ return {
33
+ ...step,
34
+ type: message ? message.collection : 'step',
35
+ message,
36
+ };
37
+ };
38
+
39
+ export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simple', renderAllEdges = false }: Props) => {
40
+ const graph = defaultFlow || createDagreGraph({ ranksep: 360, nodesep: 200 });
41
+ const nodes = [] as any,
42
+ edges = [] as any;
43
+
44
+ const flows = await getCollection('flows');
45
+ const flow = flows.find((flow) => flow.data.id === id && flow.data.version === version);
46
+
47
+ // Nothing found...
48
+ if (!flow) {
49
+ return {
50
+ nodes: [],
51
+ edges: [],
52
+ };
53
+ }
54
+
55
+ const events = await getCollection('events');
56
+ const commands = await getCollection('commands');
57
+ const services = await getCollection('services');
58
+
59
+ const messages = [...events, ...commands];
60
+
61
+ const steps = flow?.data.steps || [];
62
+
63
+ // Hydrate the steps with information they may need.
64
+ const hydratedSteps = steps.map((step: any) => {
65
+ if (step.service) return getServiceNode(step, services);
66
+ if (step.message) return getMessageNode(step, messages);
67
+ if (step.actor) return { ...step, type: 'actor', actor: step.actor };
68
+ if (step.externalSystem) return { ...step, type: 'externalSystem', externalSystem: step.externalSystem };
69
+ return { ...step, type: 'step' };
70
+ });
71
+
72
+ // Create nodes
73
+ hydratedSteps.forEach((step, index: number) => {
74
+ const node = {
75
+ id: `step-${step.id}`,
76
+ sourcePosition: 'right',
77
+ targetPosition: 'left',
78
+ data: {
79
+ mode,
80
+ step: step,
81
+ showTarget: true,
82
+ showSource: true,
83
+ },
84
+ position: { x: 250, y: index * 150 },
85
+ type: step.type,
86
+ } as NodeType;
87
+
88
+ if (step.service) node.data.service = step.service;
89
+ if (step.message) node.data.message = step.message;
90
+ if (step.actor) node.data.actor = step.actor;
91
+ if (step.externalSystem) node.data.externalSystem = step.externalSystem;
92
+
93
+ nodes.push(node);
94
+ });
95
+
96
+ // Create Edges
97
+ hydratedSteps.forEach((step, index: number) => {
98
+ let paths = step.next_steps || [];
99
+
100
+ if (step.next_step) {
101
+ // If its a string or number
102
+ if (!step.next_step?.id) {
103
+ paths = [{ id: step.next_step }];
104
+ } else {
105
+ paths = [step.next_step];
106
+ }
107
+ }
108
+
109
+ paths = paths.map((path: any) => {
110
+ if (typeof path === 'string') {
111
+ return { id: path };
112
+ }
113
+ return path;
114
+ });
115
+
116
+ paths.forEach((path: any) => {
117
+ edges.push({
118
+ id: `step-${step.id}-step-${path.id}`,
119
+ source: `step-${step.id}`,
120
+ target: `step-${path.id}`,
121
+ type: 'smoothstep',
122
+ label: path.label,
123
+ animated: true,
124
+ markerEnd: {
125
+ type: MarkerType.ArrowClosed,
126
+ width: 20,
127
+ height: 20,
128
+ color: '#acacac',
129
+ },
130
+ style: {
131
+ strokeWidth: 2,
132
+ stroke: '#acacac',
133
+ },
134
+ });
135
+ });
136
+ });
137
+
138
+ nodes.forEach((node: any) => {
139
+ graph.setNode(node.id, { width: 150, height: 100 });
140
+ });
141
+
142
+ edges.forEach((edge: any) => {
143
+ graph.setEdge(edge.source, edge.target);
144
+ });
145
+
146
+ // Render the diagram in memory getting hte X and Y
147
+ dagre.layout(graph);
148
+
149
+ return {
150
+ nodes: calculatedNodes(graph, nodes),
151
+ edges: edges,
152
+ };
153
+ };
@@ -1,8 +1,9 @@
1
1
  // import { getColor } from '@utils/colors';
2
2
  import { getCollection, type CollectionEntry } from 'astro:content';
3
3
  import dagre from 'dagre';
4
- import { getVersion } from './services';
5
4
  import { createDagreGraph, generateIdForNode, generatedIdForEdge, calculatedNodes } from '@utils/node-graph-utils/utils';
5
+ import { MarkerType } from 'reactflow';
6
+ import { getVersionFromCollection } from '@utils/versions/versions';
6
7
 
7
8
  type DagreGraph = any;
8
9
 
@@ -40,12 +41,12 @@ export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simpl
40
41
  const messages = [...events, ...commands];
41
42
 
42
43
  const receivesHydrated = receivesRaw
43
- .map((message) => getVersion(messages, message.id, message.version))
44
+ .map((message) => getVersionFromCollection(messages, message.id, message.version))
44
45
  .flat()
45
46
  .filter((e) => e !== undefined);
46
47
 
47
48
  const sendsHydrated = sendsRaw
48
- .map((message) => getVersion(messages, message.id, message.version))
49
+ .map((message) => getVersionFromCollection(messages, message.id, message.version))
49
50
  .flat()
50
51
  .filter((e) => e !== undefined);
51
52
 
@@ -73,7 +74,12 @@ export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simpl
73
74
  label: receive?.collection === 'events' ? 'receives event' : 'accepts',
74
75
  animated: false,
75
76
  markerEnd: {
76
- type: 'arrow',
77
+ type: MarkerType.ArrowClosed,
78
+ width: 40,
79
+ height: 40,
80
+ },
81
+ style: {
82
+ strokeWidth: 1,
77
83
  },
78
84
  });
79
85
  });
@@ -107,7 +113,12 @@ export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simpl
107
113
  label: send?.collection === 'events' ? 'publishes event' : 'invokes command',
108
114
  animated: false,
109
115
  markerEnd: {
110
- type: 'arrow',
116
+ type: MarkerType.ArrowClosed,
117
+ width: 40,
118
+ height: 40,
119
+ },
120
+ style: {
121
+ strokeWidth: 1,
111
122
  },
112
123
  });
113
124
  });
@@ -1,36 +1,13 @@
1
1
  import { getVersionForCollectionItem } from '@utils/collections/util';
2
+ import { getVersionFromCollection } from '@utils/versions/versions';
2
3
  import { getCollection } from 'astro:content';
3
4
  import type { CollectionEntry } from 'astro:content';
4
5
  import path from 'path';
5
- import { satisfies, validRange } from 'semver';
6
6
 
7
7
  const PROJECT_DIR = process.env.PROJECT_DIR || process.cwd();
8
8
 
9
9
  export type Service = CollectionEntry<'services'>;
10
10
 
11
- export const getVersion = (
12
- collection: CollectionEntry<'events' | 'commands'>[],
13
- id: string,
14
- version?: string
15
- ): CollectionEntry<'events' | 'commands'>[] => {
16
- const data = collection;
17
- const semverRange = validRange(version);
18
-
19
- if (semverRange) {
20
- return data.filter((msg) => msg.data.id == id).filter((msg) => satisfies(msg.data.version, semverRange));
21
- }
22
-
23
- const filteredEvents = data.filter((event) => event.data.id === id);
24
-
25
- // Order by version
26
- const sorted = filteredEvents.sort((a, b) => {
27
- return a.data.version.localeCompare(b.data.version);
28
- });
29
-
30
- // latest version
31
- return [sorted[sorted.length - 1]];
32
- };
33
-
34
11
  interface Props {
35
12
  getAllVersions?: boolean;
36
13
  }
@@ -53,12 +30,12 @@ export const getServices = async ({ getAllVersions = true }: Props = {}): Promis
53
30
  const receivesMessages = service.data.receives || [];
54
31
 
55
32
  const sends = sendsMessages
56
- .map((message) => getVersion(allMessages, message.id, message.version))
33
+ .map((message) => getVersionFromCollection(allMessages, message.id, message.version))
57
34
  .flat()
58
35
  .filter((e) => e !== undefined);
59
36
 
60
37
  const receives = receivesMessages
61
- .map((message) => getVersion(allMessages, message.id, message.version))
38
+ .map((message) => getVersionFromCollection(allMessages, message.id, message.version))
62
39
  .flat()
63
40
  .filter((e) => e !== undefined);
64
41
 
@@ -9,7 +9,8 @@ export const getTeams = async (): Promise<Team[]> => {
9
9
  const teams = await getCollection('teams', (team) => {
10
10
  return team.data.hidden !== true;
11
11
  });
12
-
12
+ // What do they own?
13
+ const domains = await getCollection('domains');
13
14
  // What do they own?
14
15
  const services = await getCollection('services');
15
16
  // What do they own?
@@ -17,6 +18,10 @@ export const getTeams = async (): Promise<Team[]> => {
17
18
  const commands = await getCollection('commands');
18
19
 
19
20
  return teams.map((team) => {
21
+ const ownedDomains = domains.filter((domain) => {
22
+ return domain.data.owners?.find((owner) => owner.slug === team.data.id);
23
+ });
24
+
20
25
  const ownedServices = services.filter((service) => {
21
26
  return service.data.owners?.find((owner) => owner.slug === team.data.id);
22
27
  });
@@ -33,6 +38,7 @@ export const getTeams = async (): Promise<Team[]> => {
33
38
  ...team,
34
39
  data: {
35
40
  ...team.data,
41
+ ownedDomains,
36
42
  ownedServices,
37
43
  ownedCommands,
38
44
  ownedEvents,