@eventcatalog/core 2.58.2 → 2.59.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 (54) hide show
  1. package/dist/analytics/analytics.cjs +1 -1
  2. package/dist/analytics/analytics.js +2 -2
  3. package/dist/analytics/log-build.cjs +1 -1
  4. package/dist/analytics/log-build.js +3 -3
  5. package/dist/{chunk-PV4MP6U4.js → chunk-5GNBQGFW.js} +1 -1
  6. package/dist/{chunk-WK6GQM5P.js → chunk-AREJNU2F.js} +1 -1
  7. package/dist/{chunk-ZBPULBAC.js → chunk-LAI5QQUI.js} +1 -1
  8. package/dist/constants.cjs +1 -1
  9. package/dist/constants.js +1 -1
  10. package/dist/eventcatalog.cjs +1 -1
  11. package/dist/eventcatalog.js +3 -3
  12. package/eventcatalog/src/components/Grids/DomainGrid.tsx +67 -2
  13. package/eventcatalog/src/components/Grids/MessageGrid.tsx +157 -41
  14. package/eventcatalog/src/components/Grids/ServiceGrid.tsx +78 -14
  15. package/eventcatalog/src/components/MDX/NodeGraph/Edges/MultilineEdgeLabel.tsx +52 -0
  16. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +13 -0
  17. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.tsx +4 -1
  18. package/eventcatalog/src/components/MDX/NodeGraph/Nodes/Data.tsx +55 -16
  19. package/eventcatalog/src/components/SideBars/ContainerSideBar.astro +180 -0
  20. package/eventcatalog/src/components/SideBars/ServiceSideBar.astro +41 -0
  21. package/eventcatalog/src/components/SideNav/ListViewSideBar/components/MessageList.tsx +1 -1
  22. package/eventcatalog/src/components/SideNav/ListViewSideBar/index.tsx +250 -59
  23. package/eventcatalog/src/components/SideNav/ListViewSideBar/types.ts +3 -0
  24. package/eventcatalog/src/components/SideNav/ListViewSideBar/utils.ts +35 -1
  25. package/eventcatalog/src/components/SideNav/TreeView/getTreeView.ts +2 -2
  26. package/eventcatalog/src/components/Tables/Table.tsx +22 -2
  27. package/eventcatalog/src/components/Tables/columns/ContainersTableColumns.tsx +152 -0
  28. package/eventcatalog/src/components/Tables/columns/index.tsx +3 -0
  29. package/eventcatalog/src/content.config.ts +57 -1
  30. package/eventcatalog/src/layouts/DiscoverLayout.astro +11 -1
  31. package/eventcatalog/src/pages/architecture/architecture.astro +9 -1
  32. package/eventcatalog/src/pages/discover/[type]/_index.data.ts +1 -1
  33. package/eventcatalog/src/pages/discover/[type]/index.astro +11 -1
  34. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/_index.data.ts +11 -1
  35. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/_index.data.ts +1 -1
  36. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/index.astro +4 -6
  37. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +50 -1
  38. package/eventcatalog/src/pages/docs/[type]/[id]/[version].md.ts +2 -0
  39. package/eventcatalog/src/pages/docs/llm/llms-full.txt.ts +4 -1
  40. package/eventcatalog/src/pages/docs/llm/llms-services.txt.ts +19 -1
  41. package/eventcatalog/src/pages/docs/llm/llms.txt.ts +3 -0
  42. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/_index.data.ts +1 -1
  43. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/_index.data.ts +80 -0
  44. package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/data/index.astro +52 -0
  45. package/eventcatalog/src/pages/visualiser/[type]/[id]/index.astro +9 -2
  46. package/eventcatalog/src/types/index.ts +20 -2
  47. package/eventcatalog/src/utils/collections/containers.ts +94 -0
  48. package/eventcatalog/src/utils/collections/icons.ts +3 -1
  49. package/eventcatalog/src/utils/collections/services.ts +15 -1
  50. package/eventcatalog/src/utils/collections/util.ts +4 -2
  51. package/eventcatalog/src/utils/node-graphs/container-node-graph.ts +155 -0
  52. package/eventcatalog/src/utils/node-graphs/services-node-graph.ts +188 -82
  53. package/eventcatalog/src/utils/page-loaders/page-data-loader.ts +2 -0
  54. package/package.json +1 -1
@@ -4,6 +4,7 @@ import { columns as ServiceTableColumns } from './ServiceTableColumns';
4
4
  import { columns as DomainTableColumns } from './DomainTableColumns';
5
5
  import { columns as FlowTableColumns } from './FlowTableColumns';
6
6
  import { columns as TeamsTableColumns } from './TeamsTableColumns';
7
+ import { columns as ContainerTableColumns } from './ContainersTableColumns';
7
8
  export const getColumnsByCollection = (collection: string): any => {
8
9
  switch (collection) {
9
10
  case 'events':
@@ -20,6 +21,8 @@ export const getColumnsByCollection = (collection: string): any => {
20
21
  return UserTableColumns();
21
22
  case 'teams':
22
23
  return TeamsTableColumns();
24
+ case 'containers':
25
+ return ContainerTableColumns();
23
26
  default:
24
27
  return [];
25
28
  }
@@ -47,7 +47,7 @@ const channelPointer = z
47
47
  const resourcePointer = z.object({
48
48
  id: z.string(),
49
49
  version: z.string().optional().default('latest'),
50
- type: z.enum(['service', 'event', 'command', 'query', 'flow', 'channel', 'domain', 'user', 'team']),
50
+ type: z.enum(['service', 'event', 'command', 'query', 'flow', 'channel', 'domain', 'user', 'team', 'container']),
51
51
  });
52
52
 
53
53
  const changelogs = defineCollection({
@@ -361,6 +361,8 @@ const services = defineCollection({
361
361
  sends: z.array(pointer).optional(),
362
362
  receives: z.array(pointer).optional(),
363
363
  entities: z.array(pointer).optional(),
364
+ writesTo: z.array(pointer).optional(),
365
+ readsFrom: z.array(pointer).optional(),
364
366
  detailsPanel: z
365
367
  .object({
366
368
  domains: detailPanelPropertySchema.optional(),
@@ -371,12 +373,65 @@ const services = defineCollection({
371
373
  repository: detailPanelPropertySchema.optional(),
372
374
  owners: detailPanelPropertySchema.optional(),
373
375
  changelog: detailPanelPropertySchema.optional(),
376
+ containers: detailPanelPropertySchema.optional(),
374
377
  })
375
378
  .optional(),
376
379
  })
377
380
  .merge(baseSchema),
378
381
  });
379
382
 
383
+ // 1) Put this near your other enums/utilities
384
+ const containerTypeEnum = z.enum([
385
+ // Core
386
+ 'database',
387
+ 'cache',
388
+ 'objectStore',
389
+ 'searchIndex',
390
+ 'dataWarehouse',
391
+ 'dataLake',
392
+ 'externalSaaS',
393
+ // Fallback
394
+ 'other',
395
+ ]);
396
+
397
+ const accessModeEnum = z.enum(['read', 'write', 'readWrite', 'appendOnly']);
398
+ const dataClassificationEnum = z.enum(['public', 'internal', 'confidential', 'regulated']);
399
+
400
+ const containers = defineCollection({
401
+ loader: glob({
402
+ pattern: ['**/containers/*/index.(md|mdx)', '**/containers/*/versioned/*/index.(md|mdx)'],
403
+ base: projectDirBase,
404
+ generateId: ({ data }) => {
405
+ return `${data.id}-${data.version}`;
406
+ },
407
+ }),
408
+ schema: z
409
+ .object({
410
+ container_type: containerTypeEnum, // <— the important discriminator inside DataContainer
411
+ technology: z.string().optional(), // e.g. "postgres@14", "kafka", "s3"
412
+ authoritative: z.boolean().optional().default(false),
413
+ access_mode: accessModeEnum.optional(), // read/write/readWrite/appendOnly
414
+ classification: dataClassificationEnum.optional(),
415
+ residency: z.string().optional(),
416
+ retention: z.string().optional(),
417
+ // details panel toggles (aligns with your pattern)
418
+ detailsPanel: z
419
+ .object({
420
+ versions: detailPanelPropertySchema.optional(),
421
+ repository: detailPanelPropertySchema.optional(),
422
+ owners: detailPanelPropertySchema.optional(),
423
+ changelog: detailPanelPropertySchema.optional(),
424
+ attachments: detailPanelPropertySchema.optional(),
425
+ })
426
+ .optional(),
427
+ services: z.array(reference('services')).optional(),
428
+
429
+ servicesThatWriteToContainer: z.array(reference('services')).optional(),
430
+ servicesThatReadFromContainer: z.array(reference('services')).optional(),
431
+ })
432
+ .merge(baseSchema),
433
+ });
434
+
380
435
  const customPages = defineCollection({
381
436
  loader: glob({
382
437
  // any number of child folders
@@ -628,6 +683,7 @@ export const collections = {
628
683
  flows,
629
684
  pages,
630
685
  changelogs,
686
+ containers,
631
687
 
632
688
  // DDD Collections
633
689
  ubiquitousLanguages,
@@ -9,6 +9,8 @@ import { getEvents } from '@utils/events';
9
9
  import { getServices } from '@utils/collections/services';
10
10
  import { buildUrl } from '@utils/url-builder';
11
11
  import { getQueries } from '@utils/queries';
12
+ import { getContainers } from '@utils/collections/containers';
13
+ import { DatabaseIcon } from 'lucide-react';
12
14
  import { MagnifyingGlassIcon } from '@heroicons/react/20/solid';
13
15
  import VerticalSideBarLayout from './VerticalSideBarLayout.astro';
14
16
  import Checkbox from '@components/Checkbox.astro';
@@ -19,7 +21,7 @@ const commands = await getCommands();
19
21
  const services = await getServices();
20
22
  const domains = await getDomains();
21
23
  const flows = await getFlows();
22
-
24
+ const containers = await getContainers();
23
25
  export interface Props<T extends TCollectionTypes> {
24
26
  title: string;
25
27
  subtitle: string;
@@ -74,6 +76,14 @@ const tabs = [
74
76
  activeColor: 'yellow',
75
77
  enabled: domains.length > 0,
76
78
  },
79
+ {
80
+ label: `Data (${containers.length})`,
81
+ href: buildUrl('/discover/containers'),
82
+ isActive: currentPath === '/discover/containers',
83
+ icon: DatabaseIcon,
84
+ activeColor: 'blue',
85
+ enabled: containers.length > 0,
86
+ },
77
87
  {
78
88
  label: `Flows (${flows.length})`,
79
89
  href: buildUrl('/discover/flows'),
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  import { getDomains, getMessagesForDomain } from '@utils/collections/domains';
3
3
  import { getServices } from '@utils/collections/services';
4
+ import { getContainers } from '@utils/collections/containers';
4
5
  import { getMessages } from '@utils/messages';
5
6
  import type { ExtendedDomain } from '@components/Grids/DomainGrid';
6
7
  import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
@@ -27,6 +28,7 @@ const { type, embeded = false } = Astro.props as { type: ValidType; embeded: boo
27
28
  // Get data based on type
28
29
  let items: Service[] | CollectionEntry<'commands'>[] | CollectionEntry<CollectionMessageTypes>[] = [];
29
30
  let domains: ExtendedDomain[] = [];
31
+ let containers: CollectionEntry<'containers'>[] = [];
30
32
 
31
33
  const getDomainsForArchitecturePages = async () => {
32
34
  const domains = await getDomains({ getAllVersions: false });
@@ -68,6 +70,7 @@ if (type === 'services') {
68
70
  const { events, commands, queries } = await getMessages({ getAllVersions: false, hydrateServices: false });
69
71
  const messages = [...events, ...commands, ...queries];
70
72
  items = removeContentFromCollection(messages) as unknown as CollectionEntry<CollectionMessageTypes>[];
73
+ containers = await getContainers({ getAllVersions: false });
71
74
  }
72
75
  ---
73
76
 
@@ -83,7 +86,12 @@ if (type === 'services') {
83
86
  }
84
87
  {
85
88
  type === 'messages' && (
86
- <MessageGrid messages={items as CollectionEntry<CollectionMessageTypes>[]} embeded={embeded} client:load />
89
+ <MessageGrid
90
+ messages={items as CollectionEntry<CollectionMessageTypes>[]}
91
+ embeded={embeded}
92
+ containers={containers}
93
+ client:load
94
+ />
87
95
  )
88
96
  }
89
97
  </div>
@@ -16,7 +16,7 @@ export class Page extends HybridPage {
16
16
  flows: getFlows,
17
17
  };
18
18
 
19
- const itemTypes = ['events', 'commands', 'queries', 'services', 'domains', 'flows'] as const;
19
+ const itemTypes = ['events', 'commands', 'queries', 'services', 'domains', 'flows', 'containers'] as const;
20
20
  const allItems = await Promise.all(itemTypes.map((type) => loaders[type]()));
21
21
 
22
22
  return allItems.flatMap((items, index) => ({
@@ -9,6 +9,12 @@ export const getStaticPaths = Page.getStaticPaths;
9
9
 
10
10
  const { type, data } = await Page.getData(Astro);
11
11
 
12
+ let title = `${type} (${data.length})`;
13
+
14
+ if (type === 'containers') {
15
+ title = `Data (${data.length})`;
16
+ }
17
+
12
18
  function mapToItem(i: any) {
13
19
  return {
14
20
  collection: i.collection,
@@ -22,7 +28,7 @@ function mapToItem(i: any) {
22
28
  ---
23
29
 
24
30
  <DiscoverLayout
25
- title={`${type} (${data.length})`}
31
+ title={title}
26
32
  subtitle={`Find, filter and search for any ${type} in your system.`}
27
33
  data={data.map(
28
34
  (d: CollectionEntry<CollectionTypes>) =>
@@ -46,6 +52,10 @@ function mapToItem(i: any) {
46
52
  sends: d.data?.sends?.map(mapToItem) ?? [],
47
53
  // @ts-ignore
48
54
  services: d.data?.services?.map(mapToItem) ?? [],
55
+ // @ts-ignore
56
+ servicesThatWriteToContainer: d.data?.servicesThatWriteToContainer?.map(mapToItem) ?? [],
57
+ // @ts-ignore
58
+ servicesThatReadFromContainer: d.data?.servicesThatReadFromContainer?.map(mapToItem) ?? [],
49
59
  },
50
60
  }) as DiscoverLayoutProps<typeof type>['data'][0]
51
61
  )}
@@ -12,7 +12,17 @@ export class Page extends HybridPage {
12
12
  return [];
13
13
  }
14
14
 
15
- const itemTypes: PageTypes[] = ['events', 'commands', 'queries', 'services', 'domains', 'flows', 'channels', 'entities'];
15
+ const itemTypes: PageTypes[] = [
16
+ 'events',
17
+ 'commands',
18
+ 'queries',
19
+ 'services',
20
+ 'domains',
21
+ 'flows',
22
+ 'channels',
23
+ 'entities',
24
+ 'containers',
25
+ ];
16
26
  const allItems = await Promise.all(itemTypes.map((type) => pageDataLoader[type]()));
17
27
 
18
28
  return allItems.flatMap((items, index) =>
@@ -14,7 +14,7 @@ export class Page extends HybridPage {
14
14
 
15
15
  const { pageDataLoader } = await import('@utils/page-loaders/page-data-loader');
16
16
 
17
- const itemTypes: PageTypes[] = ['events', 'commands', 'queries', 'services', 'domains', 'flows'];
17
+ const itemTypes: PageTypes[] = ['events', 'commands', 'queries', 'services', 'domains', 'flows', 'containers'];
18
18
  const allItems = await Promise.all(itemTypes.map((type) => pageDataLoader[type]()));
19
19
 
20
20
  return allItems.flatMap((items, index) =>
@@ -9,6 +9,7 @@ import {
9
9
  MagnifyingGlassIcon,
10
10
  QueueListIcon,
11
11
  } from '@heroicons/react/24/outline';
12
+ import { DatabaseIcon } from 'lucide-react';
12
13
  import { render, getEntry } from 'astro:content';
13
14
  import mdxComponents from '@components/MDX/components';
14
15
  import 'diff2html/bundles/css/diff2html.min.css';
@@ -96,18 +97,15 @@ const getBadge = () => {
96
97
  class: 'text-yellow-400',
97
98
  };
98
99
  }
100
+ if (props.collection === 'containers') {
101
+ return { backgroundColor: 'blue', textColor: 'blue', content: 'Container', icon: DatabaseIcon, class: 'text-blue-400' };
102
+ }
99
103
  if (props.collection === 'flows') {
100
104
  return { backgroundColor: 'teal', textColor: 'teal', content: 'Flow', icon: QueueListIcon, class: 'text-teal-400' };
101
105
  }
102
106
  };
103
107
 
104
108
  const badges = [getBadge()];
105
- import { HomeIcon } from '@heroicons/react/20/solid';
106
-
107
- const pages = [
108
- { name: 'Projects', href: '#', current: false },
109
- { name: 'Project Nero', href: '#', current: true },
110
- ];
111
109
  ---
112
110
 
113
111
  <VerticalSideBarLayout title="ChangeLog">
@@ -19,6 +19,7 @@ import DomainSideBar from '@components/SideBars/DomainSideBar.astro';
19
19
  import ChannelSideBar from '@components/SideBars/ChannelSideBar.astro';
20
20
  import FlowSideBar from '@components/SideBars/FlowSideBar.astro';
21
21
  import EntitySideBar from '@components/SideBars/EntitySideBar.astro';
22
+ import ContainerSideBar from '@components/SideBars/ContainerSideBar.astro';
22
23
  import CopyAsMarkdown from '@components/CopyAsMarkdown';
23
24
 
24
25
  import {
@@ -28,9 +29,11 @@ import {
28
29
  BoltIcon,
29
30
  ChatBubbleLeftIcon,
30
31
  MagnifyingGlassIcon,
32
+ MapIcon,
33
+ ClockIcon,
31
34
  } from '@heroicons/react/24/outline';
32
35
  import { ArrowsRightLeftIcon } from '@heroicons/react/20/solid';
33
- import { Box, Boxes, SquarePenIcon } from 'lucide-react';
36
+ import { Box, Boxes, SquarePenIcon, DatabaseIcon, DatabaseZapIcon, ShieldCheckIcon } from 'lucide-react';
34
37
  import type { CollectionTypes } from '@types';
35
38
 
36
39
  import { render } from 'astro:content';
@@ -101,6 +104,51 @@ const getBadge = () => {
101
104
  return [{ backgroundColor: 'teal', textColor: 'teal', content: 'Channel', icon: ArrowsRightLeftIcon, class: 'text-gray' }];
102
105
  }
103
106
 
107
+ if (props.collection === 'containers') {
108
+ const badges = [];
109
+ const content = props.data.container_type?.charAt(0).toUpperCase() + props.data.container_type?.slice(1) || 'Database';
110
+
111
+ badges.push({ backgroundColor: 'blue', textColor: 'blue', content: content, icon: DatabaseIcon, class: 'text-gray' });
112
+
113
+ if (props.data?.technology) {
114
+ badges.push({
115
+ backgroundColor: 'indigo',
116
+ textColor: 'indigo',
117
+ content: `${props.data.technology}`,
118
+ icon: DatabaseZapIcon,
119
+ });
120
+ }
121
+
122
+ if (props.data?.residency) {
123
+ badges.push({
124
+ backgroundColor: 'red',
125
+ textColor: 'red',
126
+ content: `Residency: ${props.data.residency}`,
127
+ icon: MapIcon,
128
+ });
129
+ }
130
+
131
+ if (props.data?.retention) {
132
+ badges.push({
133
+ backgroundColor: 'green',
134
+ textColor: 'green',
135
+ content: `Retention: ${props.data.retention}`,
136
+ icon: ClockIcon,
137
+ });
138
+ }
139
+
140
+ if (props.data?.access_mode) {
141
+ badges.push({
142
+ backgroundColor: 'green',
143
+ textColor: 'green',
144
+ content: `Access Mode: ${props.data.access_mode}`,
145
+ icon: ShieldCheckIcon,
146
+ });
147
+ }
148
+
149
+ return badges;
150
+ }
151
+
104
152
  if (props.collection === 'entities') {
105
153
  const entityBadges = [{ backgroundColor: 'purple', textColor: 'purple', content: 'Entity', icon: Box, class: 'text-gray' }];
106
154
  if (props.data.aggregateRoot) {
@@ -435,6 +483,7 @@ nodeGraphs.push({
435
483
  {props?.collection === 'channels' && <ChannelSideBar channel={props} />}
436
484
  {props?.collection === 'flows' && <FlowSideBar flow={props} />}
437
485
  {props?.collection === 'entities' && <EntitySideBar entity={props} />}
486
+ {props?.collection === 'containers' && <ContainerSideBar container={props} />}
438
487
  </aside>
439
488
  </div>
440
489
  </main>
@@ -15,6 +15,7 @@ const services = await getCollection('services');
15
15
  const domains = await getCollection('domains');
16
16
  const flows = await getCollection('flows');
17
17
  const channels = await getCollection('channels');
18
+ const containers = await getCollection('containers');
18
19
  const entities = await getEntities();
19
20
  export async function getStaticPaths() {
20
21
  // Just return empty array if LLMs are not enabled
@@ -30,6 +31,7 @@ export async function getStaticPaths() {
30
31
  domains,
31
32
  flows,
32
33
  channels,
34
+ containers,
33
35
  entities,
34
36
  };
35
37
  const paths = Object.keys(collections).map((type) => {
@@ -16,7 +16,8 @@ type AllowedCollections =
16
16
  | 'customPages'
17
17
  | 'channels'
18
18
  | 'entities'
19
- | 'flows';
19
+ | 'flows'
20
+ | 'containers';
20
21
 
21
22
  const events = await getCollection('events');
22
23
  const commands = await getCollection('commands');
@@ -28,6 +29,7 @@ const users = await getCollection('users');
28
29
  const entities = await getCollection('entities');
29
30
  const channels = await getCollection('channels');
30
31
  const flows = await getCollection('flows');
32
+ const containers = await getCollection('containers');
31
33
 
32
34
  const customDocs = await getCollection('customPages');
33
35
 
@@ -46,6 +48,7 @@ export const GET: APIRoute = async ({ params, request }) => {
46
48
  ...users,
47
49
  ...entities,
48
50
  ...channels,
51
+ ...containers,
49
52
  ...flows,
50
53
  ];
51
54
 
@@ -12,6 +12,8 @@ export const GET: APIRoute = async ({ params, request }) => {
12
12
  const formatServiceWithLinks = (service: CollectionEntry<'services'>) => {
13
13
  const sends = service.data.sends as unknown as CollectionEntry<'events'>[];
14
14
  const receives = service.data.receives as unknown as CollectionEntry<'events'>[];
15
+ const writesTo = service.data.writesTo as unknown as CollectionEntry<'containers'>[];
16
+ const readsFrom = service.data.readsFrom as unknown as CollectionEntry<'containers'>[];
15
17
 
16
18
  const sendsList =
17
19
  sends
@@ -29,7 +31,23 @@ export const GET: APIRoute = async ({ params, request }) => {
29
31
  )
30
32
  .join('') || ' - Does not receive any messages';
31
33
 
32
- return `## [${service.data.name} - ${service.data.version}](${baseUrl}/docs/services/${service.data.id}/${service.data.version}.mdx) - ${service.data.summary}\n ## Sends\n${sendsList}\n ## Receives\n${receivesList}`;
34
+ const writesToList =
35
+ writesTo
36
+ .map(
37
+ (write) =>
38
+ ` - [${write.data.name} - ${write.data.version}](${baseUrl}/docs/containers/${write.data.id}/${write.data.version}.mdx) - ${write.data.summary}`
39
+ )
40
+ .join('') || ' - Does not write to any containers';
41
+
42
+ const readsFromList =
43
+ readsFrom
44
+ .map(
45
+ (read) =>
46
+ ` - [${read.data.name} - ${read.data.version}](${baseUrl}/docs/containers/${read.data.id}/${read.data.version}.mdx) - ${read.data.summary}`
47
+ )
48
+ .join('') || ' - Does not read from any containers';
49
+
50
+ return `## [${service.data.name} - ${service.data.version}](${baseUrl}/docs/services/${service.data.id}/${service.data.version}.mdx) - ${service.data.summary}\n ## Sends\n${sendsList}\n ## Receives\n${receivesList}\n ## Writes to\n${writesToList}\n ## Reads from\n${readsFromList} \n`;
33
51
  };
34
52
 
35
53
  const content = ['# Services \n\n', services.map((item) => formatServiceWithLinks(item)).join('\n')].join('\n');
@@ -17,6 +17,7 @@ const users = await getCollection('users');
17
17
 
18
18
  const flows = await getCollection('flows');
19
19
  const channels = await getCollection('channels');
20
+ const containers = await getCollection('containers');
20
21
 
21
22
  const entities = await getCollection('entities');
22
23
 
@@ -115,6 +116,8 @@ export const GET: APIRoute = async ({ params, request }) => {
115
116
  )
116
117
  .join(''),
117
118
  ...(Object.keys(ubiquitousLanguages).length > 0 ? ['## Ubiquitous Language', renderUbiquitousLanguages(baseUrl)] : []),
119
+ '\n## Containers (Databases, External Systems)',
120
+ containers.map((item) => formatVersionedItem(item, 'containers')).join('\n'),
118
121
  '\n## Entities',
119
122
  renderEntities(baseUrl),
120
123
  '\n## Teams',
@@ -19,7 +19,7 @@ export class Page extends HybridPage {
19
19
  flows: getFlows,
20
20
  };
21
21
 
22
- const itemTypes: PageTypesWithFlows[] = ['events', 'commands', 'queries', 'services', 'domains', 'flows'];
22
+ const itemTypes: PageTypesWithFlows[] = ['events', 'commands', 'queries', 'services', 'domains', 'flows', 'containers'];
23
23
  const allItems = await Promise.all(itemTypes.map((type) => loaders[type]()));
24
24
 
25
25
  return allItems.flatMap((items, index) =>
@@ -0,0 +1,80 @@
1
+ import { HybridPage } from '@utils/page-loaders/hybrid-page';
2
+ import { isAuthEnabled } from '@utils/feature';
3
+ import { getServices, type Service } from '@utils/collections/services';
4
+
5
+ const serviceHasData = (service: Service) => {
6
+ return service.data.writesTo?.length || 0 > 0 || service.data.readsFrom?.length || 0 > 0;
7
+ };
8
+
9
+ export class Page extends HybridPage {
10
+ static async getStaticPaths(): Promise<Array<{ params: any; props: any }>> {
11
+ if (isAuthEnabled()) {
12
+ return [];
13
+ }
14
+
15
+ const services = await getServices();
16
+ const servicesWithData = services.filter((service) => serviceHasData(service));
17
+
18
+ return servicesWithData.flatMap((service) => {
19
+ return {
20
+ params: {
21
+ type: 'services',
22
+ id: service.data.id,
23
+ version: service.data.version,
24
+ },
25
+ props: {
26
+ type: 'service',
27
+ ...service,
28
+ },
29
+ };
30
+ });
31
+ }
32
+
33
+ protected static async fetchData(params: any) {
34
+ const { id, version } = params;
35
+
36
+ if (!id || !version) {
37
+ return null;
38
+ }
39
+
40
+ // Get all items of the specified type
41
+ const items = await getServices();
42
+
43
+ // Find the specific item by id and version, and only if it has entities
44
+ const item = items.find((i) => i.data.id === id && i.data.version === version && serviceHasData(i));
45
+
46
+ if (!item) {
47
+ return null;
48
+ }
49
+
50
+ return item;
51
+ }
52
+
53
+ protected static createNotFoundResponse(): Response {
54
+ return new Response(null, {
55
+ status: 404,
56
+ statusText: 'Service data page not found',
57
+ });
58
+ }
59
+
60
+ static get clientAuthScript(): string {
61
+ if (!isAuthEnabled()) {
62
+ return '';
63
+ }
64
+
65
+ return `
66
+ if (typeof window !== 'undefined' && !import.meta.env.SSR) {
67
+ fetch('/api/auth/session')
68
+ .then(res => res.json())
69
+ .then(session => {
70
+ if (!session?.user) {
71
+ window.location.href = '/auth/login?callbackUrl=' + encodeURIComponent(window.location.pathname);
72
+ }
73
+ })
74
+ .catch(() => {
75
+ window.location.href = '/auth/login?callbackUrl=' + encodeURIComponent(window.location.pathname);
76
+ });
77
+ }
78
+ `;
79
+ }
80
+ }
@@ -0,0 +1,52 @@
1
+ ---
2
+ import NodeGraph from '@components/MDX/NodeGraph/NodeGraph.astro';
3
+ import VisualiserLayout from '@layouts/VisualiserLayout.astro';
4
+ import { buildUrl } from '@utils/url-builder';
5
+ import { ClientRouter } from 'astro:transitions';
6
+
7
+ import { Page } from './_index.data';
8
+
9
+ export const prerender = Page.prerender;
10
+ export const getStaticPaths = Page.getStaticPaths;
11
+
12
+ // Get data
13
+ const props = await Page.getData(Astro);
14
+
15
+ const {
16
+ data: { id },
17
+ collection,
18
+ } = props;
19
+ ---
20
+
21
+ <VisualiserLayout title={`Visualiser | ${props.data.name} (${props.collection})`} description={props.data.summary}>
22
+ <div class="bg-gray-100/50 m-4">
23
+ <div class="h-[calc(100vh-130px)] w-full relative border border-gray-200" id={`${id}-portal`} transition:animate="fade"></div>
24
+ <NodeGraph
25
+ id={id}
26
+ collection={`${collection}-containers`}
27
+ title={`${props.data.name} (v${props.data.version}) - Data`}
28
+ mode="full"
29
+ linkTo="visualiser"
30
+ version={props.data.version}
31
+ linksToVisualiser={false}
32
+ href={{
33
+ label: `Open documentation for ${props.data.name} v${props.data.version}`,
34
+ url: buildUrl(`/docs/${props.collection}/${props.data.id}/${props.data.version}`),
35
+ }}
36
+ />
37
+ </div>
38
+ <ClientRouter />
39
+ </VisualiserLayout>
40
+
41
+ <script define:vars={{ id }}>
42
+ document.addEventListener('DOMContentLoaded', () => {
43
+ const urlSearchParams = new URLSearchParams(window.location.search);
44
+ const params = Object.fromEntries(urlSearchParams.entries());
45
+ const embeded = params.embed === 'true' ? true : false;
46
+ const viewport = document.getElementById(`${id}-portal`);
47
+
48
+ if (embeded) {
49
+ viewport.style.height = 'calc(100vh - 30px)';
50
+ }
51
+ });
52
+ </script>
@@ -5,13 +5,20 @@ import { getEvents } from '@utils/events';
5
5
  import { getCommands } from '@utils/commands';
6
6
  import { getServices } from '@utils/collections/services';
7
7
  import { getDomains } from '@utils/collections/domains';
8
+ import { getContainers } from '@utils/collections/containers';
8
9
  import type { CollectionEntry } from 'astro:content';
9
10
  import type { CollectionTypes } from '@types';
10
11
 
11
12
  export async function getStaticPaths() {
12
- const [events, commands, services, domains] = await Promise.all([getEvents(), getCommands(), getServices(), getDomains()]);
13
+ const [events, commands, services, domains, containers] = await Promise.all([
14
+ getEvents(),
15
+ getCommands(),
16
+ getServices(),
17
+ getDomains(),
18
+ getContainers(),
19
+ ]);
13
20
 
14
- const resources = [...domains, ...events, ...services, ...commands];
21
+ const resources = [...domains, ...events, ...services, ...commands, ...containers];
15
22
  const resourcesWithVisualiserEnabled = resources.filter((resource) => resource.data.visualiser !== false);
16
23
 
17
24
  const buildPages = (collection: CollectionEntry<CollectionTypes>[]) => {
@@ -1,4 +1,22 @@
1
- export type CollectionTypes = 'commands' | 'events' | 'queries' | 'domains' | 'services' | 'flows' | 'channels' | 'entities';
1
+ export type CollectionTypes =
2
+ | 'commands'
3
+ | 'events'
4
+ | 'queries'
5
+ | 'domains'
6
+ | 'services'
7
+ | 'flows'
8
+ | 'channels'
9
+ | 'entities'
10
+ | 'containers';
2
11
  export type CollectionMessageTypes = 'commands' | 'events' | 'queries';
3
12
  export type CollectionUserTypes = 'users';
4
- export type PageTypes = 'events' | 'commands' | 'queries' | 'services' | 'domains' | 'channels' | 'flows' | 'entities';
13
+ export type PageTypes =
14
+ | 'events'
15
+ | 'commands'
16
+ | 'queries'
17
+ | 'services'
18
+ | 'domains'
19
+ | 'channels'
20
+ | 'flows'
21
+ | 'entities'
22
+ | 'containers';