@eventcatalog/core 3.14.6 → 3.15.0-beta.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 (35) 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-LNIPDPVB.js → chunk-6NJZEWVT.js} +1 -1
  6. package/dist/{chunk-TERDXO46.js → chunk-GPD6XYQI.js} +1 -1
  7. package/dist/{chunk-NYVQSLA5.js → chunk-SEJTXM5L.js} +1 -1
  8. package/dist/{chunk-KWCAGR52.js → chunk-UFOXV4FN.js} +1 -1
  9. package/dist/{chunk-6NRRWRLT.js → chunk-ZP4JNCSQ.js} +1 -1
  10. package/dist/constants.cjs +1 -1
  11. package/dist/constants.js +1 -1
  12. package/dist/eventcatalog.cjs +75 -11
  13. package/dist/eventcatalog.js +80 -16
  14. package/dist/generate.cjs +1 -1
  15. package/dist/generate.js +3 -3
  16. package/dist/utils/cli-logger.cjs +1 -1
  17. package/dist/utils/cli-logger.js +2 -2
  18. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +56 -10
  19. package/eventcatalog/src/content.config.ts +89 -20
  20. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/_index.data.ts +85 -0
  21. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/index.astro +195 -0
  22. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/_index.data.ts +86 -0
  23. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/index.astro +195 -0
  24. package/eventcatalog/src/stores/sidebar-store/builders/container.ts +9 -0
  25. package/eventcatalog/src/stores/sidebar-store/builders/data-product.ts +15 -9
  26. package/eventcatalog/src/stores/sidebar-store/builders/domain.ts +9 -0
  27. package/eventcatalog/src/stores/sidebar-store/builders/flow.ts +13 -4
  28. package/eventcatalog/src/stores/sidebar-store/builders/message.ts +9 -0
  29. package/eventcatalog/src/stores/sidebar-store/builders/service.ts +9 -0
  30. package/eventcatalog/src/stores/sidebar-store/builders/shared.ts +82 -0
  31. package/eventcatalog/src/stores/sidebar-store/state.ts +27 -2
  32. package/eventcatalog/src/utils/collections/data-products.ts +6 -2
  33. package/eventcatalog/src/utils/collections/resource-docs.ts +601 -0
  34. package/eventcatalog/src/utils/feature.ts +1 -0
  35. package/package.json +1 -1
@@ -29,9 +29,14 @@ export const projectDirBase = (() => {
29
29
  return projectDir;
30
30
  })();
31
31
 
32
+ const withIgnoredBuildArtifacts = (patterns: string | string[]) => {
33
+ const patternList = Array.isArray(patterns) ? patterns : [patterns];
34
+ return [...patternList, '!dist/**'];
35
+ };
36
+
32
37
  const pages = defineCollection({
33
38
  loader: glob({
34
- pattern: ['**/pages/*.(md|mdx)'],
39
+ pattern: withIgnoredBuildArtifacts(['**/pages/*.(md|mdx)']),
35
40
  base: projectDirBase,
36
41
  }),
37
42
  schema: z
@@ -90,7 +95,7 @@ const resourcePointer = z.object({
90
95
 
91
96
  const changelogs = defineCollection({
92
97
  loader: glob({
93
- pattern: ['**/changelog.(md|mdx)'],
98
+ pattern: withIgnoredBuildArtifacts(['**/changelog.(md|mdx)']),
94
99
  base: projectDirBase,
95
100
  }),
96
101
  schema: z.object({
@@ -229,7 +234,7 @@ const flowStep = z
229
234
 
230
235
  const flows = defineCollection({
231
236
  loader: glob({
232
- pattern: ['**/flows/*/index.(md|mdx)', '**/flows/*/versioned/*/index.(md|mdx)'],
237
+ pattern: withIgnoredBuildArtifacts(['**/flows/*/index.(md|mdx)', '**/flows/*/versioned/*/index.(md|mdx)']),
233
238
  base: projectDirBase,
234
239
  generateId: ({ data }) => {
235
240
  return `${data.id}-${data.version}`;
@@ -317,7 +322,7 @@ const messageDetailsPanelPropertySchema = z.object({
317
322
 
318
323
  const events = defineCollection({
319
324
  loader: glob({
320
- pattern: ['**/events/*/index.(md|mdx)', '**/events/*/versioned/*/index.(md|mdx)'],
325
+ pattern: withIgnoredBuildArtifacts(['**/events/*/index.(md|mdx)', '**/events/*/versioned/*/index.(md|mdx)']),
321
326
  base: projectDirBase,
322
327
  generateId: ({ data, ...rest }) => {
323
328
  return `${data.id}-${data.version}`;
@@ -337,7 +342,7 @@ const events = defineCollection({
337
342
 
338
343
  const commands = defineCollection({
339
344
  loader: glob({
340
- pattern: ['**/commands/*/index.(md|mdx)', '**/commands/*/versioned/*/index.(md|mdx)'],
345
+ pattern: withIgnoredBuildArtifacts(['**/commands/*/index.(md|mdx)', '**/commands/*/versioned/*/index.(md|mdx)']),
341
346
  base: projectDirBase,
342
347
  generateId: ({ data }) => {
343
348
  return `${data.id}-${data.version}`;
@@ -357,7 +362,7 @@ const commands = defineCollection({
357
362
 
358
363
  const queries = defineCollection({
359
364
  loader: glob({
360
- pattern: ['**/queries/*/index.(md|mdx)', '**/queries/*/versioned/*/index.(md|mdx)'],
365
+ pattern: withIgnoredBuildArtifacts(['**/queries/*/index.(md|mdx)', '**/queries/*/versioned/*/index.(md|mdx)']),
361
366
  base: projectDirBase,
362
367
  generateId: ({ data }) => {
363
368
  return `${data.id}-${data.version}`;
@@ -389,7 +394,7 @@ const dataProductOutputPointer = z.object({
389
394
 
390
395
  const dataProducts = defineCollection({
391
396
  loader: glob({
392
- pattern: ['**/data-products/*/index.(md|mdx)', '**/data-products/*/versioned/*/index.(md|mdx)'],
397
+ pattern: withIgnoredBuildArtifacts(['**/data-products/*/index.(md|mdx)', '**/data-products/*/versioned/*/index.(md|mdx)']),
393
398
  base: projectDirBase,
394
399
  generateId: ({ data }) => {
395
400
  return `${data.id}-${data.version}`;
@@ -405,7 +410,7 @@ const dataProducts = defineCollection({
405
410
 
406
411
  const services = defineCollection({
407
412
  loader: glob({
408
- pattern: [
413
+ pattern: withIgnoredBuildArtifacts([
409
414
  'domains/*/services/*/index.(md|mdx)',
410
415
  'domains/*/services/*/versioned/*/index.(md|mdx)',
411
416
 
@@ -416,7 +421,7 @@ const services = defineCollection({
416
421
  // Capture services in the root
417
422
  'services/*/index.(md|mdx)', // ✅ Capture only services markdown files
418
423
  'services/*/versioned/*/index.(md|mdx)', // ✅ Capture versioned files inside services
419
- ],
424
+ ]),
420
425
  base: projectDirBase,
421
426
  generateId: ({ data, ...rest }) => {
422
427
  return `${data.id}-${data.version}`;
@@ -466,7 +471,7 @@ const dataClassificationEnum = z.enum(['public', 'internal', 'confidential', 're
466
471
 
467
472
  const containers = defineCollection({
468
473
  loader: glob({
469
- pattern: ['**/containers/**/index.(md|mdx)', '**/containers/**/versioned/*/index.(md|mdx)'],
474
+ pattern: withIgnoredBuildArtifacts(['**/containers/**/index.(md|mdx)', '**/containers/**/versioned/*/index.(md|mdx)']),
470
475
  base: projectDirBase,
471
476
  generateId: ({ data }) => {
472
477
  return `${data.id}-${data.version}`;
@@ -505,15 +510,66 @@ const containers = defineCollection({
505
510
  const customPages = defineCollection({
506
511
  loader: glob({
507
512
  // any number of child folders
508
- pattern: ['docs/*.(md|mdx)', 'docs/**/*.@(md|mdx)'],
513
+ pattern: withIgnoredBuildArtifacts(['docs/*.(md|mdx)', 'docs/**/*.@(md|mdx)']),
509
514
  base: projectDirBase,
510
515
  }),
511
516
  schema: customPagesSchema,
512
517
  });
513
518
 
519
+ const resourceDocs = defineCollection({
520
+ loader: glob({
521
+ // Resource-level docs are restricted to known resource paths.
522
+ // This avoids scanning external docs such as node_modules/**/docs.
523
+ pattern: withIgnoredBuildArtifacts([
524
+ '{events,commands,queries,services,flows,containers,channels,entities,data-products}/*/docs/**/*.@(md|mdx)',
525
+ '{events,commands,queries,services,flows,containers,channels,entities,data-products}/*/versioned/*/docs/**/*.@(md|mdx)',
526
+ 'domains/*/docs/**/*.@(md|mdx)',
527
+ 'domains/*/versioned/*/docs/**/*.@(md|mdx)',
528
+ 'domains/*/subdomains/*/docs/**/*.@(md|mdx)',
529
+ 'domains/*/subdomains/*/versioned/*/docs/**/*.@(md|mdx)',
530
+ ]),
531
+ base: projectDirBase,
532
+ }),
533
+ schema: z.object({
534
+ id: z.string().optional(),
535
+ type: z.string().optional(),
536
+ version: z.string().optional(),
537
+ order: z.number().optional(),
538
+ badges: z.array(badge).optional(),
539
+ title: z.string().optional(),
540
+ summary: z.string().optional(),
541
+ slug: z.string().optional(),
542
+ hidden: z.boolean().optional(),
543
+ }),
544
+ });
545
+
546
+ const resourceDocCategories = defineCollection({
547
+ loader: glob({
548
+ pattern: withIgnoredBuildArtifacts([
549
+ '{events,commands,queries,services,flows,containers,channels,entities,data-products}/*/docs/**/category.json',
550
+ '{events,commands,queries,services,flows,containers,channels,entities,data-products}/*/docs/**/_category_.json',
551
+ '{events,commands,queries,services,flows,containers,channels,entities,data-products}/*/versioned/*/docs/**/category.json',
552
+ '{events,commands,queries,services,flows,containers,channels,entities,data-products}/*/versioned/*/docs/**/_category_.json',
553
+ 'domains/*/docs/**/category.json',
554
+ 'domains/*/docs/**/_category_.json',
555
+ 'domains/*/versioned/*/docs/**/category.json',
556
+ 'domains/*/versioned/*/docs/**/_category_.json',
557
+ 'domains/*/subdomains/*/docs/**/category.json',
558
+ 'domains/*/subdomains/*/docs/**/_category_.json',
559
+ 'domains/*/subdomains/*/versioned/*/docs/**/category.json',
560
+ 'domains/*/subdomains/*/versioned/*/docs/**/_category_.json',
561
+ ]),
562
+ base: projectDirBase,
563
+ }),
564
+ schema: z.object({
565
+ label: z.string().optional(),
566
+ position: z.number().optional(),
567
+ }),
568
+ });
569
+
514
570
  const domains = defineCollection({
515
571
  loader: glob({
516
- pattern: [
572
+ pattern: withIgnoredBuildArtifacts([
517
573
  // ✅ Strictly include only index.md at the expected levels
518
574
  'domains/*/index.(md|mdx)',
519
575
  'domains/*/versioned/*/index.(md|mdx)',
@@ -521,7 +577,7 @@ const domains = defineCollection({
521
577
  // Capture subdomain folders
522
578
  'domains/*/subdomains/*/index.(md|mdx)',
523
579
  'domains/*/subdomains/*/versioned/*/index.(md|mdx)',
524
- ],
580
+ ]),
525
581
  base: projectDirBase,
526
582
  generateId: ({ data, ...rest }) => {
527
583
  return `${data.id}-${data.version}`;
@@ -557,7 +613,7 @@ const domains = defineCollection({
557
613
 
558
614
  const channels = defineCollection({
559
615
  loader: glob({
560
- pattern: ['**/channels/**/index.(md|mdx)', '**/channels/**/versioned/*/index.(md|mdx)'],
616
+ pattern: withIgnoredBuildArtifacts(['**/channels/**/index.(md|mdx)', '**/channels/**/versioned/*/index.(md|mdx)']),
561
617
  base: projectDirBase,
562
618
  generateId: ({ data }) => {
563
619
  return `${data.id}-${data.version}`;
@@ -600,7 +656,10 @@ const channels = defineCollection({
600
656
 
601
657
  const ubiquitousLanguages = defineCollection({
602
658
  loader: glob({
603
- pattern: ['domains/*/ubiquitous-language.(md|mdx)', 'domains/*/subdomains/*/ubiquitous-language.(md|mdx)'],
659
+ pattern: withIgnoredBuildArtifacts([
660
+ 'domains/*/ubiquitous-language.(md|mdx)',
661
+ 'domains/*/subdomains/*/ubiquitous-language.(md|mdx)',
662
+ ]),
604
663
  base: projectDirBase,
605
664
  generateId: ({ data }) => {
606
665
  // File has no id, so we need to generate one
@@ -624,7 +683,7 @@ const ubiquitousLanguages = defineCollection({
624
683
 
625
684
  const entities = defineCollection({
626
685
  loader: glob({
627
- pattern: ['**/entities/*/index.(md|mdx)', '**/entities/*/versioned/*/index.(md|mdx)'],
686
+ pattern: withIgnoredBuildArtifacts(['**/entities/*/index.(md|mdx)', '**/entities/*/versioned/*/index.(md|mdx)']),
628
687
  base: projectDirBase,
629
688
  generateId: ({ data, ...rest }) => {
630
689
  return `${data.id}-${data.version}`;
@@ -672,7 +731,11 @@ const entities = defineCollection({
672
731
  });
673
732
 
674
733
  const users = defineCollection({
675
- loader: glob({ pattern: 'users/*.(md|mdx)', base: projectDirBase, generateId: ({ data }) => data.id as string }),
734
+ loader: glob({
735
+ pattern: withIgnoredBuildArtifacts('users/*.(md|mdx)'),
736
+ base: projectDirBase,
737
+ generateId: ({ data }) => data.id as string,
738
+ }),
676
739
  schema: z.object({
677
740
  id: z.string(),
678
741
  name: z.string(),
@@ -692,7 +755,11 @@ const users = defineCollection({
692
755
  });
693
756
 
694
757
  const teams = defineCollection({
695
- loader: glob({ pattern: 'teams/*.(md|mdx)', base: projectDirBase, generateId: ({ data }) => data.id as string }),
758
+ loader: glob({
759
+ pattern: withIgnoredBuildArtifacts('teams/*.(md|mdx)'),
760
+ base: projectDirBase,
761
+ generateId: ({ data }) => data.id as string,
762
+ }),
696
763
  schema: z.object({
697
764
  id: z.string(),
698
765
  name: z.string(),
@@ -712,7 +779,7 @@ const teams = defineCollection({
712
779
 
713
780
  const designs = defineCollection({
714
781
  loader: async () => {
715
- const data = await globPackage('**/**/*.ecstudio', { cwd: projectDirBase });
782
+ const data = await globPackage('**/**/*.ecstudio', { cwd: projectDirBase, ignore: ['dist/**'] });
716
783
  // File all the files in the designs folder
717
784
  // Limit 3 designs community edition?
718
785
  const files = data.reduce<{ id: string; name: string }[]>((acc, filePath) => {
@@ -741,7 +808,7 @@ const designs = defineCollection({
741
808
 
742
809
  const diagrams = defineCollection({
743
810
  loader: glob({
744
- pattern: ['**/diagrams/**/index.(md|mdx)', '**/diagrams/**/versioned/*/index.(md|mdx)'],
811
+ pattern: withIgnoredBuildArtifacts(['**/diagrams/**/index.(md|mdx)', '**/diagrams/**/versioned/*/index.(md|mdx)']),
745
812
  base: projectDirBase,
746
813
  generateId: ({ data }) => `${data.id}-${data.version}`,
747
814
  }),
@@ -778,6 +845,8 @@ export const collections = {
778
845
 
779
846
  // EventCatalog Pro Collections
780
847
  customPages,
848
+ resourceDocs,
849
+ resourceDocCategories,
781
850
 
782
851
  // EventCatalog Studio Collections
783
852
  designs,
@@ -0,0 +1,85 @@
1
+ import { isSSR, isResourceDocsEnabled } from '@utils/feature';
2
+ import { HybridPage } from '@utils/page-loaders/hybrid-page';
3
+ import { getResourceDocs, getResourceDocsForResource, type ResourceCollection } from '@utils/collections/resource-docs';
4
+
5
+ const supportedResourceCollections = new Set<ResourceCollection>([
6
+ 'domains',
7
+ 'services',
8
+ 'events',
9
+ 'commands',
10
+ 'queries',
11
+ 'flows',
12
+ 'containers',
13
+ 'channels',
14
+ 'entities',
15
+ 'data-products',
16
+ ]);
17
+
18
+ export class Page extends HybridPage {
19
+ static async getStaticPaths() {
20
+ if (isSSR() || !isResourceDocsEnabled()) {
21
+ return [];
22
+ }
23
+
24
+ const docs = await getResourceDocs();
25
+
26
+ return docs.map((doc) => ({
27
+ params: {
28
+ type: doc.data.resourceCollection,
29
+ id: doc.data.resourceId,
30
+ version: doc.data.resourceVersion,
31
+ docType: doc.data.type,
32
+ docId: doc.data.id,
33
+ docVersion: doc.data.version,
34
+ },
35
+ props: {},
36
+ }));
37
+ }
38
+
39
+ protected static async fetchData(params: any) {
40
+ if (!isResourceDocsEnabled()) {
41
+ return null;
42
+ }
43
+
44
+ const decodeParam = (value: string) => {
45
+ try {
46
+ return decodeURIComponent(value);
47
+ } catch {
48
+ return value;
49
+ }
50
+ };
51
+
52
+ const type = decodeParam(params.type);
53
+ const id = decodeParam(params.id);
54
+ const version = decodeParam(params.version);
55
+ const docType = decodeParam(params.docType);
56
+ const docId = decodeParam(params.docId);
57
+ const docVersion = decodeParam(params.docVersion);
58
+ if (!type || !id || !version || !docType || !docId || !docVersion) {
59
+ return null;
60
+ }
61
+
62
+ if (!supportedResourceCollections.has(type as ResourceCollection)) {
63
+ return null;
64
+ }
65
+
66
+ const docsForResource = await getResourceDocsForResource(type as ResourceCollection, id, version);
67
+ const doc = docsForResource.find(
68
+ (resourceDoc) =>
69
+ resourceDoc.data.type === docType && resourceDoc.data.id === docId && resourceDoc.data.version === docVersion
70
+ );
71
+
72
+ if (!doc) {
73
+ return null;
74
+ }
75
+
76
+ return doc;
77
+ }
78
+
79
+ protected static createNotFoundResponse(): Response {
80
+ return new Response(null, {
81
+ status: 404,
82
+ statusText: 'Resource documentation not found',
83
+ });
84
+ }
85
+ }
@@ -0,0 +1,195 @@
1
+ ---
2
+ import { render } from 'astro:content';
3
+
4
+ import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
5
+ import components from '@components/MDX/components';
6
+ import { buildUrl } from '@utils/url-builder';
7
+ import { AlignLeftIcon, HistoryIcon } from 'lucide-react';
8
+ import { isResourceDocsEnabled } from '@utils/feature';
9
+ import { getIcon } from '@utils/badges';
10
+ import { collectionToResourceMap } from '@utils/collections/util';
11
+
12
+ import { Page } from './_index.data';
13
+
14
+ export const prerender = Page.prerender;
15
+ export const getStaticPaths = Page.getStaticPaths;
16
+
17
+ if (!isResourceDocsEnabled()) {
18
+ return Astro.redirect(buildUrl('/docs/custom/feature'));
19
+ }
20
+
21
+ const props = await Page.getData(Astro);
22
+ const { Content, headings } = await render(props);
23
+
24
+ const title = props.data.title || props.data.id;
25
+ const pageTitle = `${title} | ${props.data.resourceId}`;
26
+ const versions = props.data.versions || [props.data.version];
27
+ const badges = (props.data.badges || []).map((badge: any) => ({
28
+ ...badge,
29
+ iconComponent: badge.icon ? getIcon(badge.icon) : null,
30
+ }));
31
+
32
+ const docsBasePath = `/docs/${props.data.resourceCollection}/${props.data.resourceId}/${props.data.resourceVersion}/${encodeURIComponent(props.data.type)}/${encodeURIComponent(props.data.id)}`;
33
+ const singularResourceName =
34
+ collectionToResourceMap[props.data.resourceCollection as keyof typeof collectionToResourceMap] ??
35
+ props.data.resourceCollection.slice(0, props.data.resourceCollection.length - 1);
36
+
37
+ const pagefindAttributes =
38
+ props.data.version === props.data.latestVersion
39
+ ? {
40
+ 'data-pagefind-body': '',
41
+ 'data-pagefind-meta': `title:${pageTitle}`,
42
+ }
43
+ : {};
44
+ ---
45
+
46
+ <VerticalSideBarLayout title={pageTitle} description={props.data.summary}>
47
+ <main class="flex docs-layout h-full bg-[rgb(var(--ec-page-bg))]" {...pagefindAttributes}>
48
+ <div class="flex docs-layout w-full pl-16">
49
+ <div class="w-full lg:mr-2 pr-8 overflow-y-auto py-8 bg-[rgb(var(--ec-page-bg))]">
50
+ <div class="border-b border-[rgb(var(--ec-page-border))] pb-4">
51
+ <p class="text-xs uppercase tracking-wide text-[rgb(var(--ec-page-text-muted))]">
52
+ <a
53
+ href={buildUrl(`/docs/${props.data.resourceCollection}/${props.data.resourceId}/${props.data.resourceVersion}`)}
54
+ class="hover:underline"
55
+ >
56
+ {singularResourceName}: {props.data.resourceId}
57
+ </a>
58
+ </p>
59
+ <div class="flex items-center gap-2 pt-1">
60
+ <h2 id="doc-page-header" class="text-2xl md:text-4xl font-bold text-[rgb(var(--ec-page-text))]">{title}</h2>
61
+ <span
62
+ class="text-xs rounded-md px-2 py-1 bg-[rgb(var(--ec-content-hover))] text-[rgb(var(--ec-page-text-muted))] border border-[rgb(var(--ec-page-border))]"
63
+ >
64
+ v{props.data.version}
65
+ {props.data.version === props.data.latestVersion && ' (latest)'}
66
+ </span>
67
+ </div>
68
+ {
69
+ props.data.summary && (
70
+ <p class="text-lg pt-2 text-[rgb(var(--ec-page-text-muted))] font-light">{props.data.summary}</p>
71
+ )
72
+ }
73
+ {
74
+ badges.length > 0 && (
75
+ <div class="flex flex-wrap gap-3 pt-4">
76
+ {badges.map((badge: any) => (
77
+ <span
78
+ class={`
79
+ inline-flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm font-medium
80
+ bg-[rgb(var(--ec-content-hover))] border border-[rgb(var(--ec-page-border))]
81
+ text-[rgb(var(--ec-page-text))]
82
+ `}
83
+ >
84
+ {badge.iconComponent && (
85
+ <badge.iconComponent className="w-4 h-4 flex-shrink-0 text-[rgb(var(--ec-icon-color))]" />
86
+ )}
87
+ <span>{badge.content}</span>
88
+ </span>
89
+ ))}
90
+ </div>
91
+ )
92
+ }
93
+ </div>
94
+ <div data-pagefind-ignore>
95
+ {
96
+ props.data.version !== props.data.latestVersion && (
97
+ <div class="rounded-md bg-[rgb(var(--ec-accent-subtle))] p-4 not-prose my-4">
98
+ <div class="flex">
99
+ <div class="flex-shrink-0">
100
+ <svg class="h-5 w-5 text-[rgb(var(--ec-accent))]" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
101
+ <path
102
+ fill-rule="evenodd"
103
+ d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z"
104
+ clip-rule="evenodd"
105
+ />
106
+ </svg>
107
+ </div>
108
+ <div class="ml-3">
109
+ <h3 class="text-sm font-medium text-[rgb(var(--ec-accent-text))]">New version found</h3>
110
+ <div class="mt-2 text-sm text-[rgb(var(--ec-accent-text))]">
111
+ <p>
112
+ You are looking at a previous version of the {singularResourceName} doc <strong>{title}</strong>.{' '}
113
+ <a
114
+ class="underline hover:text-primary block pt-2"
115
+ href={buildUrl(`${docsBasePath}/${props.data.latestVersion}`)}
116
+ >
117
+ The latest version of this doc is <span>v{props.data.latestVersion}</span> &rarr;
118
+ </a>
119
+ </p>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ </div>
124
+ )
125
+ }
126
+ </div>
127
+ <div class="prose py-8 max-w-none">
128
+ <Content components={components(props)} />
129
+ </div>
130
+ </div>
131
+ <aside class="hidden lg:block sticky top-0 pb-10 w-80 overflow-y-auto border-l border-[rgb(var(--ec-page-border))] py-2">
132
+ <div class="sticky top-28 left-0 h-full overflow-y-auto px-6 py-4">
133
+ <h3 class="text-sm text-[rgb(var(--ec-page-text))] font-semibold capitalize flex items-center gap-2">
134
+ <AlignLeftIcon className="w-4 h-4" />
135
+ On this page
136
+ </h3>
137
+ <nav class="space-y-1 text-sm py-4">
138
+ {
139
+ headings.map((heading) => {
140
+ if (heading.depth > 3) {
141
+ return null;
142
+ }
143
+ return (
144
+ <a
145
+ href={`#${heading.slug}`}
146
+ class={`block text-[12px] py-0.5 ${heading.depth === 1 ? 'font-light' : ''} text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))]`}
147
+ style={`padding-left: ${(heading.depth - 1) * 8}px`}
148
+ >
149
+ {heading.text}
150
+ </a>
151
+ );
152
+ })
153
+ }
154
+ </nav>
155
+ <div class="space-y-2 pb-4">
156
+ <span class="text-xs text-[rgb(var(--ec-page-text))] font-semibold capitalize">Versions ({versions.length})</span>
157
+ <ul role="list" class="space-y-2">
158
+ {
159
+ versions.map((version: string) => (
160
+ <li class="version-item rounded-md px-1 group w-full">
161
+ <a class="flex items-center space-x-2" href={buildUrl(`${docsBasePath}/${encodeURIComponent(version)}`)}>
162
+ <HistoryIcon
163
+ className="h-4 w-4 text-[rgb(var(--ec-page-text-muted))] group-hover:text-[rgb(var(--ec-accent-text))]"
164
+ strokeWidth={1}
165
+ />
166
+ <span
167
+ class={`font-light text-xs text-[rgb(var(--ec-page-text))] group-hover:text-[rgb(var(--ec-accent-text))] ${
168
+ version === props.data.version ? 'underline' : ''
169
+ }`}
170
+ >
171
+ {version === props.data.latestVersion ? `v${version} (latest)` : `v${version}`}
172
+ </span>
173
+ </a>
174
+ </li>
175
+ ))
176
+ }
177
+ </ul>
178
+ <div class="border-b border-[rgb(var(--ec-page-border))] pt-2"></div>
179
+ </div>
180
+ </div>
181
+ </aside>
182
+ </div>
183
+ </main>
184
+ </VerticalSideBarLayout>
185
+
186
+ <style is:global>
187
+ .docs-layout .prose {
188
+ max-width: none;
189
+ overflow: auto;
190
+ }
191
+
192
+ .version-item:hover {
193
+ background: linear-gradient(to left, rgb(var(--ec-accent-gradient-from)), rgb(var(--ec-accent-gradient-to)));
194
+ }
195
+ </style>
@@ -0,0 +1,86 @@
1
+ import { isSSR, isResourceDocsEnabled } from '@utils/feature';
2
+ import { HybridPage } from '@utils/page-loaders/hybrid-page';
3
+ import { getResourceDocs, getResourceDocsForResource, type ResourceCollection } from '@utils/collections/resource-docs';
4
+
5
+ const supportedResourceCollections = new Set<ResourceCollection>([
6
+ 'domains',
7
+ 'services',
8
+ 'events',
9
+ 'commands',
10
+ 'queries',
11
+ 'flows',
12
+ 'containers',
13
+ 'channels',
14
+ 'entities',
15
+ 'data-products',
16
+ ]);
17
+
18
+ export class Page extends HybridPage {
19
+ static async getStaticPaths() {
20
+ if (isSSR() || !isResourceDocsEnabled()) {
21
+ return [];
22
+ }
23
+
24
+ const docs = await getResourceDocs();
25
+ const latestDocs = docs.filter((doc) => doc.data.version === doc.data.latestVersion);
26
+
27
+ return latestDocs.map((doc) => ({
28
+ params: {
29
+ type: doc.data.resourceCollection,
30
+ id: doc.data.resourceId,
31
+ version: doc.data.resourceVersion,
32
+ docType: doc.data.type,
33
+ docId: doc.data.id,
34
+ },
35
+ props: {},
36
+ }));
37
+ }
38
+
39
+ protected static async fetchData(params: any) {
40
+ if (!isResourceDocsEnabled()) {
41
+ return null;
42
+ }
43
+
44
+ const decodeParam = (value: string) => {
45
+ try {
46
+ return decodeURIComponent(value);
47
+ } catch {
48
+ return value;
49
+ }
50
+ };
51
+
52
+ const type = decodeParam(params.type);
53
+ const id = decodeParam(params.id);
54
+ const version = decodeParam(params.version);
55
+ const docType = decodeParam(params.docType);
56
+ const docId = decodeParam(params.docId);
57
+ if (!type || !id || !version || !docType || !docId) {
58
+ return null;
59
+ }
60
+
61
+ if (!supportedResourceCollections.has(type as ResourceCollection)) {
62
+ return null;
63
+ }
64
+
65
+ const docsForResource = await getResourceDocsForResource(type as ResourceCollection, id, version);
66
+ const doc = docsForResource.find(
67
+ (resourceDoc) =>
68
+ resourceDoc.data.type === docType &&
69
+ resourceDoc.data.id === docId &&
70
+ resourceDoc.data.version === resourceDoc.data.latestVersion
71
+ );
72
+
73
+ if (!doc) {
74
+ return null;
75
+ }
76
+
77
+ return doc;
78
+ }
79
+
80
+ protected static createNotFoundResponse(): Response {
81
+ return new Response(null, {
82
+ status: 404,
83
+ statusText: 'Resource documentation not found',
84
+ });
85
+ }
86
+ }