@eventcatalog/core 3.36.1 → 3.36.3

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 (30) 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-V22QY5Q3.js → chunk-4U6TPQGC.js} +1 -1
  6. package/dist/{chunk-HDENGAZL.js → chunk-6L5LHQ3C.js} +1 -1
  7. package/dist/{chunk-C7JCOHTI.js → chunk-DTE424XY.js} +1 -1
  8. package/dist/{chunk-6D65JSOA.js → chunk-SPFT3VTV.js} +1 -1
  9. package/dist/{chunk-UJ7DX4SA.js → chunk-ZE3NM2MH.js} +3 -3
  10. package/dist/constants.cjs +1 -1
  11. package/dist/constants.js +1 -1
  12. package/dist/eventcatalog.cjs +1 -1
  13. package/dist/eventcatalog.config.d.cts +5 -1
  14. package/dist/eventcatalog.config.d.ts +5 -1
  15. package/dist/eventcatalog.js +10 -10
  16. package/dist/generate.cjs +1 -1
  17. package/dist/generate.js +3 -3
  18. package/dist/utils/cli-logger.cjs +1 -1
  19. package/dist/utils/cli-logger.js +2 -2
  20. package/eventcatalog/src/content.config.ts +24 -1
  21. package/eventcatalog/src/layouts/BaseLayout.astro +4 -7
  22. package/eventcatalog/src/stores/sidebar-store/builders/container.ts +10 -1
  23. package/eventcatalog/src/stores/sidebar-store/builders/data-product.ts +10 -1
  24. package/eventcatalog/src/stores/sidebar-store/builders/flow.ts +38 -0
  25. package/eventcatalog/src/stores/sidebar-store/builders/shared.ts +1 -0
  26. package/eventcatalog/src/stores/sidebar-store/state.ts +85 -2
  27. package/eventcatalog/src/stores/theme-store.ts +9 -13
  28. package/eventcatalog/src/utils/collections/flows.ts +29 -1
  29. package/eventcatalog/src/utils/node-graphs/flows-node-graph.ts +46 -1
  30. package/package.json +4 -4
@@ -37,7 +37,7 @@ var import_axios = __toESM(require("axios"), 1);
37
37
  var import_os = __toESM(require("os"), 1);
38
38
 
39
39
  // package.json
40
- var version = "3.36.1";
40
+ var version = "3.36.3";
41
41
 
42
42
  // src/constants.ts
43
43
  var VERSION = version;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  raiseEvent
3
- } from "../chunk-V22QY5Q3.js";
4
- import "../chunk-C7JCOHTI.js";
3
+ } from "../chunk-4U6TPQGC.js";
4
+ import "../chunk-DTE424XY.js";
5
5
  export {
6
6
  raiseEvent
7
7
  };
@@ -111,7 +111,7 @@ var import_axios = __toESM(require("axios"), 1);
111
111
  var import_os = __toESM(require("os"), 1);
112
112
 
113
113
  // package.json
114
- var version = "3.36.1";
114
+ var version = "3.36.3";
115
115
 
116
116
  // src/constants.ts
117
117
  var VERSION = version;
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  log_build_default
3
- } from "../chunk-UJ7DX4SA.js";
3
+ } from "../chunk-ZE3NM2MH.js";
4
+ import "../chunk-4U6TPQGC.js";
4
5
  import "../chunk-4UVFXLPI.js";
5
- import "../chunk-V22QY5Q3.js";
6
- import "../chunk-C7JCOHTI.js";
6
+ import "../chunk-DTE424XY.js";
7
7
  import "../chunk-5T63CXKU.js";
8
8
  export {
9
9
  log_build_default as default
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-C7JCOHTI.js";
3
+ } from "./chunk-DTE424XY.js";
4
4
 
5
5
  // src/analytics/analytics.js
6
6
  import axios from "axios";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  logger
3
- } from "./chunk-6D65JSOA.js";
3
+ } from "./chunk-SPFT3VTV.js";
4
4
  import {
5
5
  cleanup,
6
6
  getEventCatalogConfigFile
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "3.36.1";
2
+ var version = "3.36.3";
3
3
 
4
4
  // src/constants.ts
5
5
  var VERSION = version;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-C7JCOHTI.js";
3
+ } from "./chunk-DTE424XY.js";
4
4
 
5
5
  // src/utils/cli-logger.ts
6
6
  import pc from "picocolors";
@@ -1,10 +1,10 @@
1
+ import {
2
+ raiseEvent
3
+ } from "./chunk-4U6TPQGC.js";
1
4
  import {
2
5
  countResources,
3
6
  serializeCounts
4
7
  } from "./chunk-4UVFXLPI.js";
5
- import {
6
- raiseEvent
7
- } from "./chunk-V22QY5Q3.js";
8
8
  import {
9
9
  getEventCatalogConfigFile,
10
10
  verifyRequiredFieldsAreInCatalogConfigFile
@@ -25,7 +25,7 @@ __export(constants_exports, {
25
25
  module.exports = __toCommonJS(constants_exports);
26
26
 
27
27
  // package.json
28
- var version = "3.36.1";
28
+ var version = "3.36.3";
29
29
 
30
30
  // src/constants.ts
31
31
  var VERSION = version;
package/dist/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-C7JCOHTI.js";
3
+ } from "./chunk-DTE424XY.js";
4
4
  export {
5
5
  VERSION
6
6
  };
@@ -114,7 +114,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
114
114
  var import_picocolors = __toESM(require("picocolors"), 1);
115
115
 
116
116
  // package.json
117
- var version = "3.36.1";
117
+ var version = "3.36.3";
118
118
 
119
119
  // src/constants.ts
120
120
  var VERSION = version;
@@ -64,6 +64,7 @@ type IntegrationsConfig = {
64
64
  /** Log all analytics events to the browser console */
65
65
  debug?: boolean;
66
66
  };
67
+ type CatalogTheme = 'default' | 'ocean' | 'sapphire' | 'sunset' | 'forest' | (string & {});
67
68
  type ScalarConfiguration = any;
68
69
  interface Config {
69
70
  title: string;
@@ -82,9 +83,12 @@ interface Config {
82
83
  * Theme for the catalog UI.
83
84
  * - 'default': Default purple/slate theme
84
85
  * - 'ocean': Deep blue/teal ocean-inspired theme
86
+ * - 'sapphire': Blue/violet theme
87
+ * - 'sunset': Orange/pink sunset-inspired theme
88
+ * - 'forest': Green forest-inspired theme
85
89
  * @default 'default'
86
90
  */
87
- theme?: 'default' | 'ocean';
91
+ theme?: CatalogTheme;
88
92
  auth?: AuthConfig;
89
93
  rss?: {
90
94
  enabled: boolean;
@@ -64,6 +64,7 @@ type IntegrationsConfig = {
64
64
  /** Log all analytics events to the browser console */
65
65
  debug?: boolean;
66
66
  };
67
+ type CatalogTheme = 'default' | 'ocean' | 'sapphire' | 'sunset' | 'forest' | (string & {});
67
68
  type ScalarConfiguration = any;
68
69
  interface Config {
69
70
  title: string;
@@ -82,9 +83,12 @@ interface Config {
82
83
  * Theme for the catalog UI.
83
84
  * - 'default': Default purple/slate theme
84
85
  * - 'ocean': Deep blue/teal ocean-inspired theme
86
+ * - 'sapphire': Blue/violet theme
87
+ * - 'sunset': Orange/pink sunset-inspired theme
88
+ * - 'forest': Green forest-inspired theme
85
89
  * @default 'default'
86
90
  */
87
- theme?: 'default' | 'ocean';
91
+ theme?: CatalogTheme;
88
92
  auth?: AuthConfig;
89
93
  rss?: {
90
94
  enabled: boolean;
@@ -1,7 +1,7 @@
1
1
  import {
2
- log_build_default
3
- } from "./chunk-UJ7DX4SA.js";
4
- import "./chunk-4UVFXLPI.js";
2
+ runMigrations
3
+ } from "./chunk-XUAF2H54.js";
4
+ import "./chunk-CA4U2JP7.js";
5
5
  import {
6
6
  resolve_catalog_dependencies_default
7
7
  } from "./chunk-WAJIJEI3.js";
@@ -12,10 +12,10 @@ import {
12
12
  watch
13
13
  } from "./chunk-K3ZVEX2Y.js";
14
14
  import {
15
- runMigrations
16
- } from "./chunk-XUAF2H54.js";
17
- import "./chunk-CA4U2JP7.js";
18
- import "./chunk-V22QY5Q3.js";
15
+ log_build_default
16
+ } from "./chunk-ZE3NM2MH.js";
17
+ import "./chunk-4U6TPQGC.js";
18
+ import "./chunk-4UVFXLPI.js";
19
19
  import {
20
20
  catalogToAstro
21
21
  } from "./chunk-YDXB3BD2.js";
@@ -28,13 +28,13 @@ import {
28
28
  } from "./chunk-ULZYHF3V.js";
29
29
  import {
30
30
  generate
31
- } from "./chunk-HDENGAZL.js";
31
+ } from "./chunk-6L5LHQ3C.js";
32
32
  import {
33
33
  logger
34
- } from "./chunk-6D65JSOA.js";
34
+ } from "./chunk-SPFT3VTV.js";
35
35
  import {
36
36
  VERSION
37
- } from "./chunk-C7JCOHTI.js";
37
+ } from "./chunk-DTE424XY.js";
38
38
  import {
39
39
  getEventCatalogConfigFile,
40
40
  verifyRequiredFieldsAreInCatalogConfigFile
package/dist/generate.cjs CHANGED
@@ -78,7 +78,7 @@ var getEventCatalogConfigFile = async (projectDirectory) => {
78
78
  var import_picocolors = __toESM(require("picocolors"), 1);
79
79
 
80
80
  // package.json
81
- var version = "3.36.1";
81
+ var version = "3.36.3";
82
82
 
83
83
  // src/constants.ts
84
84
  var VERSION = version;
package/dist/generate.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  generate
3
- } from "./chunk-HDENGAZL.js";
4
- import "./chunk-6D65JSOA.js";
5
- import "./chunk-C7JCOHTI.js";
3
+ } from "./chunk-6L5LHQ3C.js";
4
+ import "./chunk-SPFT3VTV.js";
5
+ import "./chunk-DTE424XY.js";
6
6
  import "./chunk-5T63CXKU.js";
7
7
  export {
8
8
  generate
@@ -36,7 +36,7 @@ module.exports = __toCommonJS(cli_logger_exports);
36
36
  var import_picocolors = __toESM(require("picocolors"), 1);
37
37
 
38
38
  // package.json
39
- var version = "3.36.1";
39
+ var version = "3.36.3";
40
40
 
41
41
  // src/constants.ts
42
42
  var VERSION = version;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  logger
3
- } from "../chunk-6D65JSOA.js";
4
- import "../chunk-C7JCOHTI.js";
3
+ } from "../chunk-SPFT3VTV.js";
4
+ import "../chunk-DTE424XY.js";
5
5
  export {
6
6
  logger
7
7
  };
@@ -266,6 +266,8 @@ const flows = defineCollection({
266
266
  message: pointer.optional(),
267
267
  service: pointer.optional(),
268
268
  flow: pointer.optional(),
269
+ container: pointer.optional(),
270
+ dataProduct: pointer.optional(),
269
271
 
270
272
  actor: z
271
273
  .object({
@@ -308,7 +310,15 @@ const flows = defineCollection({
308
310
  if (data.next_step && data.next_steps) return false;
309
311
 
310
312
  // Either one or non types can be present
311
- const typesUsed = [data.message, data.service, data.flow, data.actor, data.custom].filter((v) => v).length;
313
+ const typesUsed = [
314
+ data.message,
315
+ data.service,
316
+ data.flow,
317
+ data.container,
318
+ data.dataProduct,
319
+ data.actor,
320
+ data.custom,
321
+ ].filter((v) => v).length;
312
322
  return typesUsed === 0 || typesUsed === 1;
313
323
  })
314
324
  ),
@@ -425,6 +435,18 @@ const dataProducts = defineCollection({
425
435
  .object({
426
436
  inputs: z.array(pointer).optional(),
427
437
  outputs: z.array(dataProductOutputPointer).optional(),
438
+ detailsPanel: z
439
+ .object({
440
+ domains: detailPanelPropertySchema.optional(),
441
+ inputs: detailPanelPropertySchema.optional(),
442
+ outputs: detailPanelPropertySchema.optional(),
443
+ versions: detailPanelPropertySchema.optional(),
444
+ repository: detailPanelPropertySchema.optional(),
445
+ owners: detailPanelPropertySchema.optional(),
446
+ changelog: detailPanelPropertySchema.optional(),
447
+ flows: detailPanelPropertySchema.optional(),
448
+ })
449
+ .optional(),
428
450
  })
429
451
  .extend(baseSchema.shape),
430
452
  });
@@ -517,6 +539,7 @@ const containers = defineCollection({
517
539
  changelog: detailPanelPropertySchema.optional(),
518
540
  attachments: detailPanelPropertySchema.optional(),
519
541
  services: detailPanelPropertySchema.optional(),
542
+ flows: detailPanelPropertySchema.optional(),
520
543
  })
521
544
  .optional(),
522
545
  services: z.array(reference('services')).optional(),
@@ -20,17 +20,14 @@ const catalogTheme = config.theme || 'default';
20
20
  <!-- Inline script to prevent flash of wrong theme -->
21
21
  <script is:inline define:vars={{ catalogTheme }}>
22
22
  (function () {
23
- function getSystemTheme() {
24
- if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
25
- return 'dark';
26
- }
27
- return 'light';
23
+ function getDefaultTheme() {
24
+ return 'dark';
28
25
  }
29
26
 
30
27
  function applyTheme() {
31
- // Check localStorage first, fall back to system preference
28
+ // Check localStorage first, fall back to dark mode
32
29
  const stored = localStorage.getItem('eventcatalog-theme');
33
- const theme = stored === 'light' || stored === 'dark' ? stored : getSystemTheme();
30
+ const theme = stored === 'light' || stored === 'dark' ? stored : getDefaultTheme();
34
31
  document.documentElement.setAttribute('data-theme', theme);
35
32
  // Also ensure catalog theme is set (for page transitions)
36
33
  document.documentElement.setAttribute('data-catalog-theme', catalogTheme);
@@ -16,7 +16,8 @@ import { iconFieldsForResource } from '@utils/icon';
16
16
  export const buildContainerNode = (
17
17
  container: CollectionEntry<'containers'>,
18
18
  owners: any[],
19
- context: ResourceGroupContext
19
+ context: ResourceGroupContext,
20
+ flowRefs: string[] = []
20
21
  ): NavNode => {
21
22
  const servicesWritingToContainer = container.data.servicesThatWriteToContainer || [];
22
23
  const servicesReadingFromContainer = container.data.servicesThatReadFromContainer || [];
@@ -36,6 +37,7 @@ export const buildContainerNode = (
36
37
  ...dataProductsReadingFromContainer.map((dp: any) => `data-product:${dp.data.id}:${dp.data.version}`),
37
38
  ];
38
39
  const renderReads = allReads.length > 0 && shouldRenderSideBarSection(container, 'services');
40
+ const renderFlows = flowRefs.length > 0 && shouldRenderSideBarSection(container, 'flows');
39
41
 
40
42
  const renderVisualiser = isVisualiserEnabled();
41
43
 
@@ -108,6 +110,13 @@ export const buildContainerNode = (
108
110
  icon: 'ArrowDownToLine',
109
111
  pages: allReads,
110
112
  },
113
+ renderFlows && {
114
+ type: 'group',
115
+ title: 'Flows',
116
+ icon: 'Waypoints',
117
+ pages: flowRefs,
118
+ visible: flowRefs.length > 0,
119
+ },
111
120
  renderOwners && buildOwnersSection(owners),
112
121
  renderRepository && buildRepositorySection(container.data.repository as { url: string; language: string }),
113
122
  hasAttachments && buildAttachmentsSection(container.data.attachments as any[]),
@@ -60,13 +60,15 @@ const resolvePointerToRef = (pointer: { id: string; version?: string }, context:
60
60
  export const buildDataProductNode = (
61
61
  dataProduct: CollectionEntry<'data-products'>,
62
62
  owners: any[],
63
- context: DataProductContext
63
+ context: DataProductContext,
64
+ flowRefs: string[] = []
64
65
  ): NavNode => {
65
66
  const inputs = dataProduct.data.inputs || [];
66
67
  const outputs = dataProduct.data.outputs || [];
67
68
 
68
69
  const renderVisualiser = isVisualiserEnabled();
69
70
  const renderOwners = owners.length > 0 && shouldRenderSideBarSection(dataProduct, 'owners');
71
+ const renderFlows = flowRefs.length > 0 && shouldRenderSideBarSection(dataProduct, 'flows');
70
72
  const docsSection = buildResourceDocsSection(
71
73
  'data-products',
72
74
  dataProduct.data.id,
@@ -139,6 +141,13 @@ export const buildDataProductNode = (
139
141
  icon: 'FileCheck',
140
142
  pages: dataContracts,
141
143
  },
144
+ renderFlows && {
145
+ type: 'group',
146
+ title: 'Flows',
147
+ icon: 'Waypoints',
148
+ pages: flowRefs,
149
+ visible: flowRefs.length > 0,
150
+ },
142
151
  renderOwners && buildOwnersSection(owners),
143
152
  ].filter(Boolean) as ChildRef[],
144
153
  };
@@ -56,6 +56,8 @@ export const buildFlowNode = (flow: CollectionEntry<'flows'>, context: ResourceG
56
56
  const queryMap = createVersionedMap(context.queries);
57
57
  const serviceMap = createVersionedMap(context.services);
58
58
  const flowMap = createVersionedMap(context.flows);
59
+ const containerMap = createVersionedMap(context.containers);
60
+ const dataProductMap = createVersionedMap(context.dataProducts);
59
61
  const messageRefs = uniqueRefs(
60
62
  steps.map((step) => resolveMessageStep(step, { eventMap, commandMap, queryMap })).filter(Boolean) as string[]
61
63
  );
@@ -71,6 +73,30 @@ export const buildFlowNode = (flow: CollectionEntry<'flows'>, context: ResourceG
71
73
  .filter(Boolean)
72
74
  .map((referencedFlow) => `flow:${referencedFlow!.data.id}:${referencedFlow!.data.version}`)
73
75
  );
76
+ const containerRefs = uniqueRefs(
77
+ steps
78
+ .map((step: any) => {
79
+ const hydratedContainer = Array.isArray(step.container) ? step.container[0] : undefined;
80
+ if (hydratedContainer?.collection && hydratedContainer?.data) return hydratedContainer;
81
+
82
+ const pointer = Array.isArray(step.container) ? undefined : step.container;
83
+ return pointer ? resolvePointer(containerMap, pointer) : undefined;
84
+ })
85
+ .filter(Boolean)
86
+ .map((container) => `container:${container!.data.id}:${container!.data.version}`)
87
+ );
88
+ const dataProductRefs = uniqueRefs(
89
+ steps
90
+ .map((step: any) => {
91
+ const hydratedDataProduct = Array.isArray(step.dataProduct) ? step.dataProduct[0] : undefined;
92
+ if (hydratedDataProduct?.collection && hydratedDataProduct?.data) return hydratedDataProduct;
93
+
94
+ const pointer = Array.isArray(step.dataProduct) ? undefined : step.dataProduct;
95
+ return pointer ? resolvePointer(dataProductMap, pointer) : undefined;
96
+ })
97
+ .filter(Boolean)
98
+ .map((dataProduct) => `data-product:${dataProduct!.data.id}:${dataProduct!.data.version}`)
99
+ );
74
100
 
75
101
  return {
76
102
  type: 'item',
@@ -120,6 +146,18 @@ export const buildFlowNode = (flow: CollectionEntry<'flows'>, context: ResourceG
120
146
  icon: 'Waypoints',
121
147
  pages: flowRefs,
122
148
  },
149
+ containerRefs.length > 0 && {
150
+ type: 'group',
151
+ title: 'Data Stores',
152
+ icon: 'Database',
153
+ pages: containerRefs,
154
+ },
155
+ dataProductRefs.length > 0 && {
156
+ type: 'group',
157
+ title: 'Data Products',
158
+ icon: 'Package',
159
+ pages: dataProductRefs,
160
+ },
123
161
  ].filter(Boolean) as ChildRef[],
124
162
  };
125
163
  };
@@ -61,6 +61,7 @@ export type ResourceGroupContext = {
61
61
  queries: CollectionEntry<'queries'>[];
62
62
  flows: CollectionEntry<'flows'>[];
63
63
  containers: CollectionEntry<'containers'>[];
64
+ dataProducts: CollectionEntry<'data-products'>[];
64
65
  diagrams: CollectionEntry<'diagrams'>[];
65
66
  resourceDocs: ResourceDocEntry[];
66
67
  resourceDocCategories: ResourceDocCategoryEntry[];
@@ -35,6 +35,8 @@ let memoryCache: NavigationData | null = null;
35
35
 
36
36
  type MessageEntry = CollectionEntry<'events' | 'commands' | 'queries'>;
37
37
  type ServiceEntry = CollectionEntry<'services'>;
38
+ type ContainerEntry = CollectionEntry<'containers'>;
39
+ type DataProductEntry = CollectionEntry<'data-products'>;
38
40
 
39
41
  const getMessageNodeKey = (message: MessageEntry) =>
40
42
  `${pluralizeMessageType(message)}:${message.data.id}:${message.data.version}`;
@@ -119,6 +121,78 @@ const buildFlowReferencesByService = ({
119
121
  return flowRefsByService;
120
122
  };
121
123
 
124
+ const buildFlowReferencesByContainer = ({
125
+ flows,
126
+ containers,
127
+ }: {
128
+ flows: CollectionEntry<'flows'>[];
129
+ containers: CollectionEntry<'containers'>[];
130
+ }) => {
131
+ const containerMap = createVersionedMap(containers);
132
+ const flowRefsByContainer = new Map<string, string[]>();
133
+
134
+ const addFlowRef = (container: ContainerEntry, flow: CollectionEntry<'flows'>) => {
135
+ const containerKey = `container:${container.data.id}:${container.data.version}`;
136
+ const flowKey = `flow:${flow.data.id}:${flow.data.version}`;
137
+ flowRefsByContainer.set(containerKey, uniqueRefs([...(flowRefsByContainer.get(containerKey) || []), flowKey]));
138
+ };
139
+
140
+ for (const flow of flows) {
141
+ for (const step of flow.data.steps || []) {
142
+ if (!step.container) continue;
143
+
144
+ const hydratedContainer = Array.isArray(step.container) ? step.container[0] : undefined;
145
+ if (hydratedContainer?.collection && hydratedContainer?.data) {
146
+ addFlowRef(hydratedContainer as ContainerEntry, flow);
147
+ continue;
148
+ }
149
+
150
+ if (Array.isArray(step.container)) continue;
151
+
152
+ const container = findInMap(containerMap, step.container.id, step.container.version);
153
+ if (container) addFlowRef(container as ContainerEntry, flow);
154
+ }
155
+ }
156
+
157
+ return flowRefsByContainer;
158
+ };
159
+
160
+ const buildFlowReferencesByDataProduct = ({
161
+ flows,
162
+ dataProducts,
163
+ }: {
164
+ flows: CollectionEntry<'flows'>[];
165
+ dataProducts: CollectionEntry<'data-products'>[];
166
+ }) => {
167
+ const dataProductMap = createVersionedMap(dataProducts);
168
+ const flowRefsByDataProduct = new Map<string, string[]>();
169
+
170
+ const addFlowRef = (dataProduct: DataProductEntry, flow: CollectionEntry<'flows'>) => {
171
+ const dataProductKey = `data-product:${dataProduct.data.id}:${dataProduct.data.version}`;
172
+ const flowKey = `flow:${flow.data.id}:${flow.data.version}`;
173
+ flowRefsByDataProduct.set(dataProductKey, uniqueRefs([...(flowRefsByDataProduct.get(dataProductKey) || []), flowKey]));
174
+ };
175
+
176
+ for (const flow of flows) {
177
+ for (const step of flow.data.steps || []) {
178
+ if (!step.dataProduct) continue;
179
+
180
+ const hydratedDataProduct = Array.isArray(step.dataProduct) ? step.dataProduct[0] : undefined;
181
+ if (hydratedDataProduct?.collection && hydratedDataProduct?.data) {
182
+ addFlowRef(hydratedDataProduct as DataProductEntry, flow);
183
+ continue;
184
+ }
185
+
186
+ if (Array.isArray(step.dataProduct)) continue;
187
+
188
+ const dataProduct = findInMap(dataProductMap, step.dataProduct.id, step.dataProduct.version);
189
+ if (dataProduct) addFlowRef(dataProduct as DataProductEntry, flow);
190
+ }
191
+ }
192
+
193
+ return flowRefsByDataProduct;
194
+ };
195
+
122
196
  /**
123
197
  * Get the navigation data for the sidebar
124
198
  */
@@ -326,10 +400,12 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
326
400
  {} as Record<string, NavNode | string>
327
401
  );
328
402
 
403
+ const flowRefsByContainer = buildFlowReferencesByContainer({ flows, containers });
404
+
329
405
  const containerNodes = containerWithOwners.reduce(
330
406
  (acc, { container, owners }) => {
331
407
  const versionedKey = `container:${container.data.id}:${container.data.version}`;
332
- acc[versionedKey] = buildContainerNode(container, owners, context);
408
+ acc[versionedKey] = buildContainerNode(container, owners, context, flowRefsByContainer.get(versionedKey) || []);
333
409
  if (container.data.latestVersion === container.data.version) {
334
410
  // Store reference to versioned key instead of duplicating the full node
335
411
  acc[`container:${container.data.id}`] = versionedKey;
@@ -358,10 +434,17 @@ export const getNestedSideBarData = async (): Promise<NavigationData> => {
358
434
  resourceDocCategories,
359
435
  };
360
436
 
437
+ const flowRefsByDataProduct = buildFlowReferencesByDataProduct({ flows, dataProducts });
438
+
361
439
  const dataProductNodes = dataProductWithOwners.reduce(
362
440
  (acc, { dataProduct, owners }) => {
363
441
  const versionedKey = `data-product:${dataProduct.data.id}:${dataProduct.data.version}`;
364
- acc[versionedKey] = buildDataProductNode(dataProduct, owners, dataProductContext);
442
+ acc[versionedKey] = buildDataProductNode(
443
+ dataProduct,
444
+ owners,
445
+ dataProductContext,
446
+ flowRefsByDataProduct.get(versionedKey) || []
447
+ );
365
448
  if (dataProduct.data.latestVersion === dataProduct.data.version) {
366
449
  acc[`data-product:${dataProduct.data.id}`] = versionedKey;
367
450
  }
@@ -4,14 +4,11 @@ const THEME_KEY = 'eventcatalog-theme';
4
4
 
5
5
  export type Theme = 'light' | 'dark';
6
6
 
7
- export const themeStore = atom<Theme>('light');
7
+ export const themeStore = atom<Theme>('dark');
8
8
 
9
- // Get system preference
10
- const getSystemTheme = (): Theme => {
11
- if (typeof window !== 'undefined' && window.matchMedia) {
12
- return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
13
- }
14
- return 'light';
9
+ // Default theme when the user has not explicitly chosen one.
10
+ const getDefaultTheme = (): Theme => {
11
+ return 'dark';
15
12
  };
16
13
 
17
14
  // Apply theme to document via data-theme attribute
@@ -31,15 +28,14 @@ const initStore = () => {
31
28
  themeStore.set(stored);
32
29
  applyTheme(stored);
33
30
  } else {
34
- // No stored preference, use system preference
35
- const systemTheme = getSystemTheme();
36
- themeStore.set(systemTheme);
37
- applyTheme(systemTheme);
31
+ // No stored preference, use the catalog default
32
+ const defaultTheme = getDefaultTheme();
33
+ themeStore.set(defaultTheme);
34
+ applyTheme(defaultTheme);
38
35
  }
39
36
  } catch (e) {
40
37
  console.warn('Failed to load theme:', e);
41
- const systemTheme = getSystemTheme();
42
- applyTheme(systemTheme);
38
+ applyTheme(getDefaultTheme());
43
39
  }
44
40
  }
45
41
  };
@@ -24,11 +24,13 @@ export const getFlows = async ({ getAllVersions = true }: Props = {}): Promise<F
24
24
  }
25
25
 
26
26
  // 1. Fetch collections in parallel
27
- const [allFlows, allEvents, allCommands, allQueries] = await Promise.all([
27
+ const [allFlows, allEvents, allCommands, allQueries, allContainers, allDataProducts] = await Promise.all([
28
28
  getCollection('flows'),
29
29
  getCollection('events'),
30
30
  getCollection('commands'),
31
31
  getCollection('queries'),
32
+ getCollection('containers'),
33
+ getCollection('data-products'),
32
34
  ]);
33
35
 
34
36
  const allMessages = [...allEvents, ...allCommands, ...allQueries];
@@ -36,6 +38,8 @@ export const getFlows = async ({ getAllVersions = true }: Props = {}): Promise<F
36
38
  // 2. Build optimized maps
37
39
  const flowMap = createVersionedMap(allFlows);
38
40
  const messageMap = createVersionedMap(allMessages);
41
+ const containerMap = createVersionedMap(allContainers);
42
+ const dataProductMap = createVersionedMap(allDataProducts);
39
43
 
40
44
  // 3. Filter flows
41
45
  const targetFlows = allFlows.filter((flow) => {
@@ -54,6 +58,30 @@ export const getFlows = async ({ getAllVersions = true }: Props = {}): Promise<F
54
58
  const steps = flow.data.steps || [];
55
59
 
56
60
  const hydrateSteps = steps.map((step) => {
61
+ if (step.container) {
62
+ const pointer = step.container;
63
+ if (!pointer) return { ...step, type: 'node' };
64
+ const container = findInMap(containerMap, pointer.id, pointer.version);
65
+
66
+ return {
67
+ ...step,
68
+ type: 'container',
69
+ container: container ? [container] : [],
70
+ };
71
+ }
72
+
73
+ if (step.dataProduct) {
74
+ const pointer = step.dataProduct;
75
+ if (!pointer) return { ...step, type: 'node' };
76
+ const dataProduct = findInMap(dataProductMap, pointer.id, pointer.version);
77
+
78
+ return {
79
+ ...step,
80
+ type: 'data-products',
81
+ dataProduct: dataProduct ? [dataProduct] : [],
82
+ };
83
+ }
84
+
57
85
  if (!step.message) return { ...step, type: 'node' }; // Preserve existing step data for non-messages
58
86
 
59
87
  const message = findInMap(messageMap, step.message.id, step.message.version);
@@ -26,6 +26,8 @@ interface Maps {
26
26
  messageMap: Map<string, any[]>;
27
27
  serviceMap: Map<string, any[]>;
28
28
  flowMap: Map<string, any[]>;
29
+ containerMap: Map<string, any[]>;
30
+ dataProductMap: Map<string, any[]>;
29
31
  }
30
32
 
31
33
  const getServiceNode = (step: any, serviceMap: Map<string, any[]>) => {
@@ -46,6 +48,26 @@ const getFlowNode = (step: any, flowMap: Map<string, any[]>) => {
46
48
  };
47
49
  };
48
50
 
51
+ const getContainerNode = (step: any, containerMap: Map<string, any[]>) => {
52
+ const pointer = step.container;
53
+ const container = findInMap(containerMap, pointer.id, pointer.version);
54
+ return {
55
+ ...step,
56
+ type: container ? 'data' : 'step',
57
+ container,
58
+ };
59
+ };
60
+
61
+ const getDataProductNode = (step: any, dataProductMap: Map<string, any[]>) => {
62
+ const pointer = step.dataProduct;
63
+ const dataProduct = findInMap(dataProductMap, pointer.id, pointer.version);
64
+ return {
65
+ ...step,
66
+ type: dataProduct ? 'data-products' : 'step',
67
+ dataProduct,
68
+ };
69
+ };
70
+
49
71
  const getMessageNode = (step: any, messageMap: Map<string, any[]>) => {
50
72
  const message = findInMap(messageMap, step.message.id, step.message.version);
51
73
  return {
@@ -97,6 +119,8 @@ const buildFlowGraphInternal = (
97
119
  const hydratedSteps = steps.map((step: any) => {
98
120
  if (step.service) return getServiceNode(step, maps.serviceMap);
99
121
  if (step.flow) return getFlowNode(step, maps.flowMap);
122
+ if (step.container) return getContainerNode(step, maps.containerMap);
123
+ if (step.dataProduct) return getDataProductNode(step, maps.dataProductMap);
100
124
  if (step.message) return getMessageNode(step, maps.messageMap);
101
125
  if (step.actor) return { ...step, type: 'actor', actor: step.actor };
102
126
  if (step.custom) return { ...step, type: 'custom', custom: step.custom };
@@ -159,6 +183,23 @@ const buildFlowGraphInternal = (
159
183
  version: step.message.data.version,
160
184
  });
161
185
  }
186
+ if (step.container?.data) {
187
+ node.data.data = { ...step.container.data };
188
+ node.data.container = { ...step.container, ...step.container.data };
189
+ node.data.contextMenu = buildContextMenuForResource({
190
+ collection: 'containers',
191
+ id: step.container.data.id,
192
+ version: step.container.data.version,
193
+ });
194
+ }
195
+ if (step.dataProduct?.data) {
196
+ node.data.dataProduct = { ...step.dataProduct, ...step.dataProduct.data };
197
+ node.data.contextMenu = buildContextMenuForResource({
198
+ collection: 'data-products',
199
+ id: step.dataProduct.data.id,
200
+ version: step.dataProduct.data.version,
201
+ });
202
+ }
162
203
  if (step.actor) {
163
204
  node.data.actor = { ...step.actor, ...step.actor.data };
164
205
  node.data = { ...node.data, ...step.actor };
@@ -214,12 +255,14 @@ const buildFlowGraphInternal = (
214
255
  export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simple', renderAllEdges = false }: Props) => {
215
256
  const graph = defaultFlow || createDagreGraph({ ranksep: 360, nodesep: 200 });
216
257
 
217
- const [flows, events, commands, queries, services] = await Promise.all([
258
+ const [flows, events, commands, queries, services, containers, dataProducts] = await Promise.all([
218
259
  getCollection('flows'),
219
260
  getCollection('events'),
220
261
  getCollection('commands'),
221
262
  getCollection('queries'),
222
263
  getCollection('services'),
264
+ getCollection('containers'),
265
+ getCollection('data-products'),
223
266
  ]);
224
267
 
225
268
  const flow = flows.find((flow) => flow.data.id === id && flow.data.version === version);
@@ -236,6 +279,8 @@ export const getNodesAndEdges = async ({ id, defaultFlow, version, mode = 'simpl
236
279
  messageMap: createVersionedMap(messages),
237
280
  serviceMap: createVersionedMap(services),
238
281
  flowMap: createVersionedMap(flows),
282
+ containerMap: createVersionedMap(containers),
283
+ dataProductMap: createVersionedMap(dataProducts),
239
284
  };
240
285
 
241
286
  const subFlowCache = new Map<string, { nodes: any[]; edges: any[] }>();
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "license": "SEE LICENSE IN LICENSE",
9
9
  "type": "module",
10
- "version": "3.36.1",
10
+ "version": "3.36.3",
11
11
  "publishConfig": {
12
12
  "access": "public"
13
13
  },
@@ -106,9 +106,9 @@
106
106
  "update-notifier": "^7.3.1",
107
107
  "uuid": "^10.0.0",
108
108
  "zod": "^4.3.6",
109
- "@eventcatalog/sdk": "2.21.0",
110
- "@eventcatalog/linter": "1.0.22",
111
- "@eventcatalog/visualiser": "^3.20.0"
109
+ "@eventcatalog/linter": "1.0.23",
110
+ "@eventcatalog/visualiser": "^3.20.2",
111
+ "@eventcatalog/sdk": "2.21.1"
112
112
  },
113
113
  "devDependencies": {
114
114
  "@astrojs/check": "^0.9.9",