@eventcatalog/core 2.3.4 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +7 -0
  3. package/bin/dist/eventcatalog.cjs +5 -4
  4. package/bin/dist/eventcatalog.js +5 -4
  5. package/package.json +9 -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/NodeGraph/NodeGraph.astro +12 -0
  12. package/src/components/MDX/NodeGraph/NodeGraph.tsx +16 -4
  13. package/src/components/MDX/NodeGraph/Nodes/ExternalSystem.tsx +79 -0
  14. package/src/components/MDX/NodeGraph/Nodes/Service.tsx +0 -12
  15. package/src/components/MDX/NodeGraph/Nodes/Step.tsx +69 -0
  16. package/src/components/MDX/NodeGraph/Nodes/User.tsx +79 -0
  17. package/src/components/SideBars/DomainSideBar.astro +1 -1
  18. package/src/components/Tables/columns/FlowTableColumns.tsx +82 -0
  19. package/src/components/Tables/columns/index.tsx +3 -0
  20. package/src/content/config.ts +65 -5
  21. package/src/layouts/DiscoverLayout.astro +11 -1
  22. package/src/layouts/VisualiserLayout.astro +33 -22
  23. package/src/pages/discover/[type]/index.astro +3 -0
  24. package/src/pages/docs/[type]/[id]/[version]/index.astro +10 -2
  25. package/src/pages/docs/teams/[id]/index.astro +14 -0
  26. package/src/pages/docs/users/[id]/index.astro +23 -4
  27. package/src/pages/visualiser/[type]/[id]/[version]/index.astro +9 -2
  28. package/src/types/index.ts +1 -1
  29. package/src/utils/collections/util.ts +22 -0
  30. package/src/utils/commands/node-graph.ts +13 -2
  31. package/src/utils/commands.ts +5 -4
  32. package/src/utils/config/catalog.ts +1 -0
  33. package/src/utils/domains/domains.ts +3 -5
  34. package/src/utils/domains/node-graph.ts +28 -9
  35. package/src/utils/events/node-graph.ts +13 -2
  36. package/src/utils/events.ts +3 -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 +17 -9
  40. package/src/utils/services/services.ts +16 -16
  41. package/src/utils/teams.ts +7 -1
  42. package/src/utils/users.ts +8 -0
  43. package/tailwind.config.mjs +1 -0
@@ -0,0 +1,82 @@
1
+ import { ServerIcon } from '@heroicons/react/20/solid';
2
+ import { RectangleGroupIcon } from '@heroicons/react/20/solid';
3
+ import { createColumnHelper } from '@tanstack/react-table';
4
+ import type { CollectionEntry } from 'astro:content';
5
+ import { filterByName, filterCollectionByName } from '../filters/custom-filters';
6
+ import { buildUrl } from '@utils/url-builder';
7
+ import { QueueListIcon } from '@heroicons/react/24/solid';
8
+
9
+ const columnHelper = createColumnHelper<CollectionEntry<'flows'>>();
10
+
11
+ export const columns = () => [
12
+ columnHelper.accessor('data.name', {
13
+ id: 'name',
14
+ header: () => <span>Flow</span>,
15
+ cell: (info) => {
16
+ const flowRaw = info.row.original;
17
+ const color = 'teal';
18
+ return (
19
+ <div className=" group ">
20
+ <a
21
+ href={buildUrl(`/docs/${flowRaw.collection}/${flowRaw.data.id}/${flowRaw.data.version}`)}
22
+ className={`group-hover:text-${color}-500 flex space-x-1 items-center`}
23
+ >
24
+ <div className={`flex items-center border border-gray-300 shadow-sm rounded-md group-hover:border-${color}-400`}>
25
+ <span className="flex items-center">
26
+ <span className={`bg-${color}-500 group-hover:bg-${color}-600 h-full rounded-tl rounded-bl p-1`}>
27
+ <QueueListIcon className="h-4 w-4 text-white" />
28
+ </span>
29
+ <span className="leading-none px-2 group-hover:underline group-hover:text-purple-500 font-light">
30
+ {flowRaw.data.name} (v{flowRaw.data.version})
31
+ </span>
32
+ </span>
33
+ </div>
34
+ </a>
35
+ </div>
36
+ );
37
+ },
38
+ footer: (info) => info.column.id,
39
+ meta: {
40
+ filterVariant: 'name',
41
+ },
42
+ filterFn: filterByName,
43
+ }),
44
+ columnHelper.accessor('data.version', {
45
+ header: () => <span>Version</span>,
46
+ cell: (info) => {
47
+ const service = info.row.original;
48
+ return (
49
+ <div className="text-left font-light">{`v${info.getValue()} ${service.data.latestVersion === service.data.version ? '(latest)' : ''}`}</div>
50
+ );
51
+ },
52
+ footer: (info) => info.column.id,
53
+ }),
54
+ columnHelper.accessor('data.summary', {
55
+ id: 'summary',
56
+ header: () => 'Summary',
57
+ cell: (info) => <span className="font-light ">{info.renderValue()}</span>,
58
+ footer: (info) => info.column.id,
59
+ meta: {
60
+ showFilter: false,
61
+ className: 'max-w-md',
62
+ },
63
+ }),
64
+ columnHelper.accessor('data.name', {
65
+ header: () => <span />,
66
+ cell: (info) => {
67
+ const domain = info.row.original;
68
+ return (
69
+ <a
70
+ className="hover:text-purple-500 hover:underline px-4 font-light"
71
+ href={buildUrl(`/visualiser/${domain.collection}/${domain.data.id}/${domain.data.version}`)}
72
+ >
73
+ Visualiser &rarr;
74
+ </a>
75
+ );
76
+ },
77
+ id: 'actions',
78
+ meta: {
79
+ showFilter: false,
80
+ },
81
+ }),
82
+ ];
@@ -1,6 +1,7 @@
1
1
  import { columns as MessageTableColumns } from './MessageTableColumns';
2
2
  import { columns as ServiceTableColumns } from './ServiceTableColumns';
3
3
  import { columns as DomainTableColumns } from './DomainTableColumns';
4
+ import { columns as FlowTableColumns } from './FlowTableColumns';
4
5
 
5
6
  export const getColumnsByCollection = (collection: string): any => {
6
7
  switch (collection) {
@@ -11,6 +12,8 @@ export const getColumnsByCollection = (collection: string): any => {
11
12
  return ServiceTableColumns();
12
13
  case 'domains':
13
14
  return DomainTableColumns();
15
+ case 'flows':
16
+ return FlowTableColumns();
14
17
  default:
15
18
  return [];
16
19
  }
@@ -13,6 +13,11 @@ const pages = defineCollection({
13
13
  }),
14
14
  });
15
15
 
16
+ const pointer = z.object({
17
+ id: z.string(),
18
+ version: z.string().optional().default('latest'),
19
+ });
20
+
16
21
  const changelogs = defineCollection({
17
22
  type: 'content',
18
23
  schema: z.object({
@@ -58,6 +63,63 @@ const baseSchema = z.object({
58
63
  .optional(),
59
64
  });
60
65
 
66
+ const flowStep = z
67
+ .union([
68
+ // Can be a string or a number just to reference a step
69
+ z.union([z.string(), z.number()]),
70
+ z
71
+ .object({
72
+ id: z.union([z.string(), z.number()]),
73
+ label: z.string().optional(),
74
+ })
75
+ .optional(),
76
+ ])
77
+ .optional();
78
+
79
+ const flows = defineCollection({
80
+ type: 'content',
81
+ schema: z
82
+ .object({
83
+ steps: z.array(
84
+ z
85
+ .object({
86
+ id: z.union([z.string(), z.number()]),
87
+ type: z.enum(['node', 'message', 'user', 'actor']).optional(),
88
+ title: z.string(),
89
+ summary: z.string().optional(),
90
+ message: pointer.optional(),
91
+ service: pointer.optional(),
92
+ actor: z
93
+ .object({
94
+ name: z.string(),
95
+ })
96
+ .optional(),
97
+ externalSystem: z
98
+ .object({
99
+ name: z.string(),
100
+ summary: z.string().optional(),
101
+ url: z.string().url().optional(),
102
+ })
103
+ .optional(),
104
+ next_step: flowStep,
105
+ next_steps: z.array(flowStep).optional(),
106
+ })
107
+ .refine((data) => {
108
+ if (!data.message && !data.service && !data.actor) return true;
109
+ // Cant have both next_steps and next_steps
110
+ if (data.next_step && data.next_steps) return false;
111
+ // Either message or service or actor must be present, but not all
112
+ return (
113
+ (data.message && !data.service && !data.actor) ||
114
+ (!data.message && data.service) ||
115
+ (data.actor && !data.message && !data.service)
116
+ );
117
+ })
118
+ ),
119
+ })
120
+ .merge(baseSchema),
121
+ });
122
+
61
123
  const events = defineCollection({
62
124
  type: 'content',
63
125
  schema: z
@@ -78,11 +140,6 @@ const commands = defineCollection({
78
140
  .merge(baseSchema),
79
141
  });
80
142
 
81
- const pointer = z.object({
82
- id: z.string(),
83
- version: z.string(),
84
- });
85
-
86
143
  const services = defineCollection({
87
144
  type: 'content',
88
145
  schema: z
@@ -112,6 +169,7 @@ const users = defineCollection({
112
169
  hidden: z.boolean().optional(),
113
170
  email: z.string().optional(),
114
171
  slackDirectMessageUrl: z.string().optional(),
172
+ ownedDomains: z.array(reference('domains')).optional(),
115
173
  ownedServices: z.array(reference('services')).optional(),
116
174
  ownedEvents: z.array(reference('events')).optional(),
117
175
  ownedCommands: z.array(reference('commands')).optional(),
@@ -130,6 +188,7 @@ const teams = defineCollection({
130
188
  slackDirectMessageUrl: z.string().optional(),
131
189
  members: z.array(reference('users')).optional(),
132
190
  ownedCommands: z.array(reference('commands')).optional(),
191
+ ownedDomains: z.array(reference('domains')).optional(),
133
192
  ownedServices: z.array(reference('services')).optional(),
134
193
  ownedEvents: z.array(reference('events')).optional(),
135
194
  }),
@@ -142,6 +201,7 @@ export const collections = {
142
201
  users,
143
202
  teams,
144
203
  domains,
204
+ flows,
145
205
  pages,
146
206
  changelogs,
147
207
  };
@@ -1,11 +1,12 @@
1
1
  ---
2
2
  import { Table } from '@components/Tables/Table';
3
- import { RectangleGroupIcon } from '@heroicons/react/24/outline';
3
+ import { QueueListIcon, RectangleGroupIcon } from '@heroicons/react/24/outline';
4
4
  import EnvelopeIcon from '@heroicons/react/24/outline/EnvelopeIcon';
5
5
  import ServerIcon from '@heroicons/react/24/outline/ServerIcon';
6
6
  import PlainPage from '@layouts/PlainPage.astro';
7
7
  import { getCommands } from '@utils/commands';
8
8
  import { getDomains } from '@utils/domains/domains';
9
+ import { getFlows } from '@utils/flows/flows';
9
10
  import { getEvents } from '@utils/events';
10
11
  import { getServices } from '@utils/services/services';
11
12
  import { buildUrl } from '@utils/url-builder';
@@ -14,6 +15,7 @@ const events = await getEvents();
14
15
  const commands = await getCommands();
15
16
  const services = await getServices();
16
17
  const domains = await getDomains();
18
+ const flows = await getFlows();
17
19
 
18
20
  const { title, subtitle, data, type } = Astro.props;
19
21
  const currentPath = Astro.url.pathname;
@@ -51,6 +53,14 @@ const tabs = [
51
53
  activeColor: 'yellow',
52
54
  enabled: domains.length > 0,
53
55
  },
56
+ {
57
+ label: `Flows (${flows.length})`,
58
+ href: buildUrl('/discover/flows'),
59
+ isActive: currentPath === '/discover/flows',
60
+ icon: QueueListIcon,
61
+ activeColor: 'teal',
62
+ enabled: flows.length > 0,
63
+ },
54
64
  ];
55
65
  ---
56
66
 
@@ -1,29 +1,36 @@
1
1
  ---
2
2
  import BasicList from '@components/Lists/BasicList';
3
3
  import PlainPage from '@layouts/PlainPage.astro';
4
- import type { CollectionTypes } from '@types';
5
4
  import { getCommands } from '@utils/commands';
6
5
  import { getDomains } from '@utils/domains/domains';
7
6
  import { getEvents } from '@utils/events';
7
+ import { getFlows } from '@utils/flows/flows';
8
8
  import { getServices } from '@utils/services/services';
9
9
  import { buildUrl } from '@utils/url-builder';
10
- import type { CollectionEntry } from 'astro:content';
11
10
  import { ViewTransitions } from 'astro:transitions';
12
11
 
13
- const [services, events, commands, domains] = await Promise.all([getServices(), getEvents(), getCommands(), getDomains()]);
12
+ const [services, events, commands, domains, flows] = await Promise.all([
13
+ getServices(),
14
+ getEvents(),
15
+ getCommands(),
16
+ getDomains(),
17
+ getFlows(),
18
+ ]);
14
19
 
15
20
  // @ts-ignore for large catalogs https://github.com/event-catalog/eventcatalog/issues/552
16
- const navItems = [...domains, ...services, ...events, ...commands];
21
+ const navItems = [...domains, ...services, ...events, ...commands, ...flows];
17
22
 
18
- const navigation = navItems.reduce((acc, item) => {
19
- // @ts-ignore
20
- const group = acc[item.collection] || [];
23
+ const navigation = navItems
24
+ .filter((item) => item.data.version === item.data.latestVersion)
25
+ .reduce((acc, item) => {
26
+ // @ts-ignore
27
+ const group = acc[item.collection] || [];
21
28
 
22
- return {
23
- ...acc,
24
- [item.collection]: [...group, item],
25
- };
26
- }, {}) as any;
29
+ return {
30
+ ...acc,
31
+ [item.collection]: [...group, item],
32
+ };
33
+ }, {}) as any;
27
34
 
28
35
  const currentPath = Astro.url.pathname;
29
36
  const { title, description } = Astro.props;
@@ -53,16 +60,20 @@ const getColor = (collection: string) => {
53
60
  >
54
61
  {
55
62
  Object.keys(navigation).map((key: any) => {
56
- const items = navigation[key].map((item: any) => {
57
- const isCurrent = currentPath.includes(`${item.collection}/${item.data.id}/${item.data.version}`);
58
- return {
59
- label: item.data.name,
60
- version: item.data.version,
61
- color: getColor(item.collection),
62
- href: buildUrl(`/visualiser/${item.collection}/${item.data.id}/${item.data.version}`),
63
- active: isCurrent,
64
- };
65
- });
63
+ const items = navigation[key]
64
+ .map((item: any) => {
65
+ const isCurrent = currentPath.includes(`${item.collection}/${item.data.id}/${item.data.version}`);
66
+ const isLatest = item.data.version === item.data.latestVersion;
67
+ if (!isLatest) return null;
68
+ return {
69
+ label: item.data.name,
70
+ version: item.data.version,
71
+ color: getColor(item.collection),
72
+ href: buildUrl(`/visualiser/${item.collection}/${item.data.id}/${item.data.version}`),
73
+ active: isCurrent,
74
+ };
75
+ })
76
+ .filter((n: any) => n !== null);
66
77
  return (
67
78
  <BasicList
68
79
  title={`${key} (${navigation[key].length})`}
@@ -6,12 +6,14 @@ import type { CollectionTypes } from '@types';
6
6
  import { getCommands } from '@utils/commands';
7
7
  import { getServices } from '@utils/services/services';
8
8
  import { getDomains } from '@utils/domains/domains';
9
+ import { getFlows } from '@utils/flows/flows';
9
10
 
10
11
  export async function getStaticPaths() {
11
12
  const events = await getEvents();
12
13
  const commands = await getCommands();
13
14
  const services = await getServices();
14
15
  const domains = await getDomains();
16
+ const flows = await getFlows();
15
17
 
16
18
  const buildPage = (type: string, collection: CollectionEntry<CollectionTypes>[]) => {
17
19
  return {
@@ -30,6 +32,7 @@ export async function getStaticPaths() {
30
32
  buildPage('commands', commands),
31
33
  buildPage('services', services),
32
34
  buildPage('domains', domains),
35
+ buildPage('flows', flows),
33
36
  ];
34
37
  }
35
38
 
@@ -13,18 +13,20 @@ import MessageSideBar from '@components/SideBars/MessageSideBar.astro';
13
13
  import { getEvents } from '@utils/events';
14
14
  import { getServices } from '@utils/services/services';
15
15
  import { getCommands } from '@utils/commands';
16
- import { EnvelopeIcon, PencilIcon, RectangleGroupIcon, ServerIcon } from '@heroicons/react/24/outline';
16
+ import { EnvelopeIcon, PencilIcon, QueueListIcon, RectangleGroupIcon, ServerIcon } from '@heroicons/react/24/outline';
17
17
  import { getDomains } from '@utils/domains/domains';
18
18
  import DomainSideBar from '@components/SideBars/DomainSideBar.astro';
19
19
  import type { CollectionTypes } from '@types';
20
20
  import SchemaViewer from '@components/MDX/SchemaViewer/SchemaViewer.astro';
21
21
  import { buildUrl } from '@utils/url-builder';
22
+ import { getFlows } from '@utils/flows/flows';
22
23
 
23
24
  export async function getStaticPaths() {
24
25
  const events = await getEvents();
25
26
  const commands = await getCommands();
26
27
  const services = await getServices();
27
28
  const domains = await getDomains();
29
+ const flows = await getFlows();
28
30
 
29
31
  const buildPages = (collection: CollectionEntry<CollectionTypes>[]) => {
30
32
  return collection.map((item) => ({
@@ -40,7 +42,7 @@ export async function getStaticPaths() {
40
42
  }));
41
43
  };
42
44
 
43
- return [...buildPages(domains), ...buildPages(events), ...buildPages(services), ...buildPages(commands)];
45
+ return [...buildPages(domains), ...buildPages(events), ...buildPages(services), ...buildPages(commands), ...buildPages(flows)];
44
46
  }
45
47
 
46
48
  const props = Astro.props;
@@ -70,6 +72,12 @@ const getBadge = () => {
70
72
  class: 'text-yellow-400',
71
73
  };
72
74
  }
75
+
76
+ if (props.collection === 'flows') {
77
+ return { backgroundColor: 'teal', textColor: 'teal', content: 'Flow', icon: QueueListIcon, class: 'text-gray' };
78
+ }
79
+
80
+ return { backgroundColor: 'teal', textColor: 'teal', content: '', icon: QueueListIcon, class: 'text-gray' };
73
81
  };
74
82
 
75
83
  const badges = [getBadge(), ...contentBadges];
@@ -34,6 +34,7 @@ const { Content } = await render();
34
34
  const membersRaw = props.data.members || [];
35
35
  const members = await Promise.all(membersRaw.map((m) => getEntry(m)));
36
36
 
37
+ const domains = props.data.ownedDomains as CollectionEntry<'domains'>[];
37
38
  const services = props.data.ownedServices as CollectionEntry<'services'>[];
38
39
  const events = props.data.ownedEvents as CollectionEntry<'events'>[];
39
40
  const commands = props.data.ownedCommands as CollectionEntry<'commands'>[];
@@ -46,6 +47,12 @@ const membersList = members.map((o) => ({
46
47
  href: buildUrl(`/docs/${o.collection}/${o.data.id}`),
47
48
  }));
48
49
 
50
+ const ownedDomainsList = domains.map((p) => ({
51
+ label: `${p.data.name}`,
52
+ href: buildUrl(`/docs/${p.collection}/${p.data.id}/${p.data.version}`),
53
+ tag: `v${p.data.version}`,
54
+ }));
55
+
49
56
  const ownedServicesList = services.map((p) => ({
50
57
  label: `${p.data.name} (service)`,
51
58
  href: buildUrl(`/docs/${p.collection}/${p.data.id}/${p.data.version}`),
@@ -112,6 +119,13 @@ const ownedEventsList = [...events, ...commands].map((p) => ({
112
119
  </main>
113
120
  <aside class="sticky top-20 h-[calc(100vh-theme(spacing.16))] w-56 overflow-y-auto">
114
121
  <div class="divide-y-2 divide-gray-100">
122
+ <PillList
123
+ color="pink"
124
+ title={`Owned domains (${ownedDomainsList.length})`}
125
+ pills={ownedDomainsList}
126
+ emptyMessage={`${props.data.name} does not own any domains.`}
127
+ client:load
128
+ />
115
129
  <PillList
116
130
  color="blue"
117
131
  title={`Owned services (${ownedServicesList.length})`}
@@ -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
  });
@@ -2,6 +2,7 @@ import { getCollection } from 'astro:content';
2
2
  import type { CollectionEntry } from 'astro:content';
3
3
  import path from 'path';
4
4
  import { getVersionForCollectionItem } from './collections/util';
5
+ import { satisfies } from 'semver';
5
6
 
6
7
  const PROJECT_DIR = process.env.PROJECT_DIR || process.cwd();
7
8
 
@@ -29,16 +30,16 @@ export const getCommands = async ({ getAllVersions = true }: Props = {}): Promis
29
30
  const producers = services.filter((service) => {
30
31
  if (!service.data.sends) return false;
31
32
  return service.data.sends.find((item) => {
32
- if (item.version) return item.id === command.data.id && item.version === command.data.version;
33
- return item.id == command.data.id;
33
+ if (item.version) return item.id === command.data.id && satisfies(command.data.version, item.version);
34
+ return item.id == command.data.id; // ??
34
35
  });
35
36
  });
36
37
 
37
38
  const consumers = services.filter((service) => {
38
39
  if (!service.data.receives) return false;
39
40
  return service.data.receives.find((item) => {
40
- if (item.version) return item.id === command.data.id && item.version === command.data.version;
41
- return item.id == command.data.id;
41
+ if (item.version) return item.id === command.data.id && satisfies(command.data.version, item.version);
42
+ return item.id == command.data.id; // ??
42
43
  });
43
44
  });
44
45
 
@@ -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,