@eventcatalog/core 3.0.0-beta.9 → 3.0.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 (148) hide show
  1. package/README.md +41 -98
  2. package/dist/__mocks__/astro-content.cjs +32 -0
  3. package/dist/__mocks__/astro-content.d.cts +13 -0
  4. package/dist/__mocks__/astro-content.d.ts +13 -0
  5. package/dist/__mocks__/astro-content.js +7 -0
  6. package/dist/analytics/analytics.cjs +1 -1
  7. package/dist/analytics/analytics.js +2 -2
  8. package/dist/analytics/log-build.cjs +1 -1
  9. package/dist/analytics/log-build.js +3 -3
  10. package/dist/catalog-to-astro-content-directory.cjs +2 -19
  11. package/dist/catalog-to-astro-content-directory.d.cts +1 -2
  12. package/dist/catalog-to-astro-content-directory.d.ts +1 -2
  13. package/dist/catalog-to-astro-content-directory.js +3 -5
  14. package/dist/{chunk-R2BJ7MJG.js → chunk-6Z6ARMQS.js} +1 -17
  15. package/dist/{chunk-A4MGWK5T.js → chunk-BYP43AAT.js} +1 -1
  16. package/dist/{chunk-RAJ7TGWN.js → chunk-E5Q7TZYT.js} +1 -1
  17. package/dist/{chunk-TT4LZO2Q.js → chunk-EKGR533N.js} +1 -1
  18. package/dist/{chunk-2VPX4WIJ.js → chunk-KF5PARQK.js} +1 -1
  19. package/dist/{chunk-TC3R47V6.js → chunk-VO5WYA44.js} +1 -1
  20. package/dist/constants.cjs +1 -1
  21. package/dist/constants.js +1 -1
  22. package/dist/eventcatalog.cjs +20 -64
  23. package/dist/eventcatalog.config.d.cts +4 -0
  24. package/dist/eventcatalog.config.d.ts +4 -0
  25. package/dist/eventcatalog.js +26 -52
  26. package/dist/generate.cjs +1 -1
  27. package/dist/generate.js +3 -3
  28. package/dist/utils/cli-logger.cjs +1 -1
  29. package/dist/utils/cli-logger.js +2 -2
  30. package/eventcatalog/astro.config.mjs +4 -1
  31. package/eventcatalog/integrations/eventcatalog-features.ts +69 -0
  32. package/eventcatalog/public/icons/asyncapi-black.svg +2 -0
  33. package/eventcatalog/public/icons/graphql-black.svg +1 -0
  34. package/eventcatalog/public/icons/openapi-black.svg +1 -0
  35. package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +994 -0
  36. package/eventcatalog/src/components/ChatPanel/ChatPanelButton.tsx +24 -0
  37. package/eventcatalog/src/components/Grids/DomainGrid.tsx +310 -173
  38. package/eventcatalog/src/components/Grids/MessageGrid.tsx +299 -180
  39. package/eventcatalog/src/components/Grids/specification-utils.ts +106 -0
  40. package/eventcatalog/src/components/Header.astro +25 -5
  41. package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.tsx +14 -3
  42. package/eventcatalog/src/components/SchemaExplorer/ApiAccessSection.tsx +95 -90
  43. package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +144 -0
  44. package/eventcatalog/src/components/SchemaExplorer/Pagination.tsx +34 -8
  45. package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +2 -2
  46. package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsHeader.tsx +140 -109
  47. package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +5 -14
  48. package/eventcatalog/src/components/SchemaExplorer/SchemaExplorer.tsx +247 -59
  49. package/eventcatalog/src/components/SchemaExplorer/SchemaFilters.tsx +64 -126
  50. package/eventcatalog/src/components/SchemaExplorer/SchemaListItem.tsx +41 -43
  51. package/eventcatalog/src/components/Search/Search.astro +2 -2
  52. package/eventcatalog/src/components/Search/SearchDataLoader.astro +25 -0
  53. package/eventcatalog/src/components/SideNav/NestedSideBar/SearchBar.tsx +6 -3
  54. package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +44 -16
  55. package/eventcatalog/src/components/SideNav/SideNav.astro +0 -15
  56. package/eventcatalog/src/components/Tables/Table.tsx +96 -77
  57. package/eventcatalog/src/components/Tables/columns/ContainersTableColumns.tsx +108 -74
  58. package/eventcatalog/src/components/Tables/columns/DomainTableColumns.tsx +74 -55
  59. package/eventcatalog/src/components/Tables/columns/FlowTableColumns.tsx +36 -36
  60. package/eventcatalog/src/components/Tables/columns/MessageTableColumns.tsx +110 -77
  61. package/eventcatalog/src/components/Tables/columns/ServiceTableColumns.tsx +105 -94
  62. package/eventcatalog/src/components/Tables/columns/SharedColumns.tsx +31 -26
  63. package/eventcatalog/src/components/Tables/columns/TeamsTableColumns.tsx +115 -215
  64. package/eventcatalog/src/components/Tables/columns/UserTableColumns.tsx +145 -243
  65. package/eventcatalog/src/content.config.ts +1 -13
  66. package/eventcatalog/src/enterprise/ai/chat-api.ts +360 -0
  67. package/eventcatalog/src/enterprise/auth/[...auth].ts +3 -0
  68. package/eventcatalog/src/enterprise/auth/login.astro +420 -0
  69. package/eventcatalog/src/enterprise/collections/index.ts +0 -1
  70. package/eventcatalog/src/layouts/Footer.astro +8 -5
  71. package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +133 -117
  72. package/eventcatalog/src/pages/_index.astro +243 -559
  73. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.ts +8 -2
  74. package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +9 -5
  75. package/eventcatalog/src/pages/directory/[type]/index.astro +6 -0
  76. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/[filename].astro +19 -3
  77. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/changelog/index.astro +7 -7
  78. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +1 -1
  79. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +10 -7
  80. package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +194 -121
  81. package/eventcatalog/src/pages/docs/teams/[id]/index.astro +94 -70
  82. package/eventcatalog/src/pages/docs/teams/[id].mdx.ts +36 -0
  83. package/eventcatalog/src/pages/docs/users/[id]/index.astro +56 -45
  84. package/eventcatalog/src/pages/docs/users/[id].mdx.ts +36 -0
  85. package/eventcatalog/src/pages/schemas/explorer/_index.data.ts +178 -0
  86. package/eventcatalog/src/pages/schemas/explorer/index.astro +7 -157
  87. package/eventcatalog/src/pages/studio.astro +124 -72
  88. package/eventcatalog/src/remark-plugins/directives.ts +30 -9
  89. package/eventcatalog/src/{components/SideNav/NestedSideBar → stores/sidebar-store}/builders/container.ts +10 -1
  90. package/eventcatalog/src/{components/SideNav/NestedSideBar → stores/sidebar-store}/builders/domain.ts +17 -7
  91. package/eventcatalog/src/{components/SideNav/NestedSideBar → stores/sidebar-store}/builders/message.ts +10 -1
  92. package/eventcatalog/src/{components/SideNav/NestedSideBar → stores/sidebar-store}/builders/service.ts +11 -4
  93. package/eventcatalog/src/{components/SideNav/NestedSideBar → stores/sidebar-store}/builders/shared.ts +14 -0
  94. package/eventcatalog/src/stores/{sidebar-store.ts → sidebar-store/index.ts} +1 -1
  95. package/eventcatalog/src/utils/collections/channels.ts +0 -2
  96. package/eventcatalog/src/utils/collections/commands.ts +0 -2
  97. package/eventcatalog/src/utils/collections/containers.ts +0 -2
  98. package/eventcatalog/src/utils/collections/domains.ts +0 -2
  99. package/eventcatalog/src/utils/collections/entities.ts +0 -2
  100. package/eventcatalog/src/utils/collections/events.ts +0 -2
  101. package/eventcatalog/src/utils/collections/flows.ts +0 -2
  102. package/eventcatalog/src/utils/collections/queries.ts +0 -2
  103. package/eventcatalog/src/utils/collections/schemas.ts +45 -7
  104. package/eventcatalog/src/utils/collections/services.ts +0 -2
  105. package/eventcatalog/src/utils/feature.ts +9 -5
  106. package/eventcatalog/src/utils/node-graphs/services-node-graph.ts +1 -1
  107. package/eventcatalog/src/utils/resource-files.ts +86 -0
  108. package/package.json +12 -15
  109. package/default-files-for-collections/changelogs.md +0 -5
  110. package/default-files-for-collections/channels.md +0 -8
  111. package/default-files-for-collections/commands.md +0 -8
  112. package/default-files-for-collections/domains.md +0 -8
  113. package/default-files-for-collections/events.md +0 -8
  114. package/default-files-for-collections/flows.md +0 -11
  115. package/default-files-for-collections/queries.md +0 -8
  116. package/default-files-for-collections/services.md +0 -8
  117. package/default-files-for-collections/ubiquitousLanguages.md +0 -7
  118. package/eventcatalog/src/enterprise/collections/chat-prompts.ts +0 -32
  119. package/eventcatalog/src/enterprise/eventcatalog-chat/components/Chat.tsx +0 -60
  120. package/eventcatalog/src/enterprise/eventcatalog-chat/components/ChatMessage.tsx +0 -414
  121. package/eventcatalog/src/enterprise/eventcatalog-chat/components/ChatSidebar.tsx +0 -169
  122. package/eventcatalog/src/enterprise/eventcatalog-chat/components/InputModal.tsx +0 -244
  123. package/eventcatalog/src/enterprise/eventcatalog-chat/components/MentionInput.tsx +0 -211
  124. package/eventcatalog/src/enterprise/eventcatalog-chat/components/WelcomePromptArea.tsx +0 -176
  125. package/eventcatalog/src/enterprise/eventcatalog-chat/components/default-prompts.ts +0 -93
  126. package/eventcatalog/src/enterprise/eventcatalog-chat/components/hooks/ChatProvider.tsx +0 -143
  127. package/eventcatalog/src/enterprise/eventcatalog-chat/components/windows/ChatWindow.server.tsx +0 -387
  128. package/eventcatalog/src/enterprise/eventcatalog-chat/pages/api/chat.ts +0 -59
  129. package/eventcatalog/src/enterprise/eventcatalog-chat/pages/chat/index.astro +0 -104
  130. package/eventcatalog/src/enterprise/eventcatalog-chat/providers/ai-provider.ts +0 -140
  131. package/eventcatalog/src/enterprise/eventcatalog-chat/providers/anthropic.ts +0 -28
  132. package/eventcatalog/src/enterprise/eventcatalog-chat/providers/google.ts +0 -41
  133. package/eventcatalog/src/enterprise/eventcatalog-chat/providers/index.ts +0 -26
  134. package/eventcatalog/src/enterprise/eventcatalog-chat/providers/openai.ts +0 -61
  135. package/eventcatalog/src/enterprise/eventcatalog-chat/utils/chat-prompts.ts +0 -50
  136. package/eventcatalog/src/pages/auth/login.astro +0 -280
  137. package/eventcatalog/src/pages/chat/feature.astro +0 -179
  138. package/eventcatalog/src/pages/chat/index.astro +0 -10
  139. package/eventcatalog/src/pages/docs/_default-docs.mdx +0 -25
  140. package/eventcatalog/src/pages/docs/index.astro +0 -33
  141. package/eventcatalog/src/pages/nav-index.json.ts +0 -30
  142. /package/eventcatalog/src/{pages → enterprise}/auth/error.astro +0 -0
  143. /package/eventcatalog/src/{middleware-auth.ts → enterprise/auth/middleware/middleware-auth.ts} +0 -0
  144. /package/eventcatalog/src/{middleware.ts → enterprise/auth/middleware/middleware.ts} +0 -0
  145. /package/eventcatalog/src/{pages/unauthorized/index.astro → enterprise/auth/unauthorized.astro} +0 -0
  146. /package/eventcatalog/src/{pages → enterprise}/plans/index.astro +0 -0
  147. /package/eventcatalog/src/{components/SideNav/NestedSideBar → stores/sidebar-store}/builders/flow.ts +0 -0
  148. /package/eventcatalog/src/{components/SideNav/NestedSideBar/sidebar-builder.ts → stores/sidebar-store/state.ts} +0 -0
@@ -66,69 +66,77 @@ const pageTitle = `Team | ${props.data.name}`;
66
66
  <main class="flex sm:px-8 docs-layout h-full" data-pagefind-body data-pagefind-meta={`title:${pageTitle}`}>
67
67
  <div class="flex docs-layout w-full">
68
68
  <div class="w-full lg:mr-2 pr-8 overflow-y-auto py-8">
69
- <div class="border-b border-gray-200 pb-4">
69
+ <div class="border-b border-gray-200 pb-6">
70
70
  <div class="flex justify-start">
71
- <div class="flex flex-col justify-between space-y-2">
71
+ <div class="flex flex-col justify-between space-y-3">
72
72
  <div>
73
- <h2 class="text-4xl font-bold">{props.data.name} <span class="text-gray-300">(Team)</span></h2>
73
+ <span
74
+ class="inline-flex items-center px-2.5 py-0.5 rounded-md text-xs font-medium bg-gray-100 text-gray-700 mb-2"
75
+ >
76
+ Team
77
+ </span>
78
+ <h2 class="text-3xl font-bold text-gray-900">{props.data.name}</h2>
74
79
  </div>
75
- <div class="space-y-2">
80
+ {props.data.summary && <p class="text-base text-gray-600 max-w-2xl">{props.data.summary}</p>}
81
+ <div class="flex flex-wrap gap-3 pt-1">
76
82
  {
77
83
  props.data.email && (
78
- <div class="flex space-x-1 items-center text-xs text-gray-500 font-bold hover:underline hover:text-primary">
79
- <EnvelopeIcon className="w-4 h-4 text-purple-400" />
80
- <a href={`mailto:${props.data.email}`}>Email</a>
81
- </div>
84
+ <a
85
+ href={`mailto:${props.data.email}`}
86
+ class="inline-flex items-center gap-1.5 text-sm text-gray-600 hover:text-gray-900 transition-colors"
87
+ >
88
+ <EnvelopeIcon className="w-4 h-4 text-gray-400" />
89
+ <span>Email</span>
90
+ </a>
82
91
  )
83
92
  }
84
93
  {
85
94
  props.data.slackDirectMessageUrl && (
86
- <div class="flex space-x-1 items-center text-xs text-gray-500 font-bold hover:underline hover:text-primary">
87
- <img src={buildUrl('/slack-icon.svg', true)} class="w-4 h-3" />
88
- <a href={`${props.data.slackDirectMessageUrl}`}>Send DM on Slack</a>
89
- </div>
95
+ <a
96
+ href={props.data.slackDirectMessageUrl}
97
+ class="inline-flex items-center gap-1.5 text-sm text-gray-600 hover:text-gray-900 transition-colors"
98
+ >
99
+ <img src={buildUrl('/slack-icon.svg', true)} class="w-4 h-4" alt="Slack" />
100
+ <span>Slack</span>
101
+ </a>
90
102
  )
91
103
  }
92
104
  {
93
105
  props.data.msTeamsDirectMessageUrl && (
94
- <div class="flex space-x-1 items-center text-xs text-gray-500 font-bold hover:underline hover:text-primary">
95
- <img src={buildUrl('/icons/ms-teams.svg', true)} class="w-4 h-4" />
96
- <a href={`${props.data.msTeamsDirectMessageUrl}`} target="_blank" rel="noopener noreferrer">
97
- Send DM on Teams
98
- </a>
99
- </div>
106
+ <a
107
+ href={props.data.msTeamsDirectMessageUrl}
108
+ target="_blank"
109
+ rel="noopener noreferrer"
110
+ class="inline-flex items-center gap-1.5 text-sm text-gray-600 hover:text-gray-900 transition-colors"
111
+ >
112
+ <img src={buildUrl('/icons/ms-teams.svg', true)} class="w-4 h-4" alt="Teams" />
113
+ <span>Teams</span>
114
+ </a>
100
115
  )
101
116
  }
102
117
  </div>
103
118
  </div>
104
119
  </div>
105
- <h2 class="text-xl py-2 text-gray-500">{props.data.summary}</h2>
106
120
  </div>
107
- <div class="border-b border-gray-200" data-pagefind-ignore>
108
- <div class="mx-auto max-w-7xl px-6 lg:px-8">
109
- <div class="mx-auto max-w-2xl lg:max-w-none">
110
- <dl
111
- class="hidden lg:grid grid-cols-1 gap-0.5 overflow-hidden rounded-2xl text-center sm:grid-cols-4 lg:grid-cols-4"
112
- >
113
- <div class="flex flex-col p-8">
114
- <dt class="text-sm font-semibold leading-6 text-gray-600"># owned domains</dt>
115
- <dd class="order-first text-3xl font-semibold tracking-tight text-gray-900">{ownedDomainsList.length}</dd>
116
- </div>
117
- <div class="flex flex-col p-8">
118
- <dt class="text-sm font-semibold leading-6 text-gray-600"># owned services</dt>
119
- <dd class="order-first text-3xl font-semibold tracking-tight text-gray-900">{ownedServicesList.length}</dd>
120
- </div>
121
- <div class="flex flex-col p-8">
122
- <dt class="text-sm font-semibold leading-6 text-gray-600"># owned messages</dt>
123
- <dd class="order-first text-3xl font-semibold tracking-tight text-gray-900">{ownedMessagesList.length}</dd>
124
- </div>
125
- <div class="flex flex-col p-8">
126
- <dt class="text-sm font-semibold leading-6 text-gray-600"># team members</dt>
127
- <dd class="order-first text-3xl font-semibold tracking-tight text-gray-900">{membersList.length}</dd>
128
- </div>
129
- </dl>
121
+ <div class="border-b border-gray-200 py-4" data-pagefind-ignore>
122
+ <dl class="grid grid-cols-2 gap-3 sm:grid-cols-4">
123
+ <div class="flex flex-col p-4 bg-gray-50 rounded-lg">
124
+ <dt class="text-xs font-medium text-gray-500">Domains</dt>
125
+ <dd class="text-2xl font-semibold text-gray-900 mt-1">{ownedDomainsList.length}</dd>
130
126
  </div>
131
- </div>
127
+ <div class="flex flex-col p-4 bg-gray-50 rounded-lg">
128
+ <dt class="text-xs font-medium text-gray-500">Services</dt>
129
+ <dd class="text-2xl font-semibold text-gray-900 mt-1">{ownedServicesList.length}</dd>
130
+ </div>
131
+ <div class="flex flex-col p-4 bg-gray-50 rounded-lg">
132
+ <dt class="text-xs font-medium text-gray-500">Messages</dt>
133
+ <dd class="text-2xl font-semibold text-gray-900 mt-1">{ownedMessagesList.length}</dd>
134
+ </div>
135
+ <div class="flex flex-col p-4 bg-gray-50 rounded-lg">
136
+ <dt class="text-xs font-medium text-gray-500">Members</dt>
137
+ <dd class="text-2xl font-semibold text-gray-900 mt-1">{membersList.length}</dd>
138
+ </div>
139
+ </dl>
132
140
  </div>
133
141
  <div class="prose prose-md py-4 w-full">
134
142
  <Content components={components(props)} />
@@ -136,33 +144,49 @@ const pageTitle = `Team | ${props.data.name}`;
136
144
  </div>
137
145
  <aside class="hidden lg:block sticky top-0 pb-10 w-96 overflow-y-auto py-2" data-pagefind-ignore>
138
146
  <div class="sticky top-28 left-0 h-full overflow-y-auto pr-6 py-4">
139
- <PillListFlat
140
- color="pink"
141
- title={`Owned domains (${ownedDomainsList.length})`}
142
- pills={ownedDomainsList}
143
- emptyMessage={`${props.data.name} does not own any domains.`}
144
- client:load
145
- />
146
- <PillListFlat
147
- color="blue"
148
- title={`Owned services (${ownedServicesList.length})`}
149
- pills={ownedServicesList}
150
- emptyMessage={`This team does not own any services .`}
151
- client:load
152
- />
153
- <PillListFlat
154
- color="red"
155
- title={`Owned messages (${ownedMessagesList.length})`}
156
- pills={ownedMessagesList}
157
- emptyMessage={`This team does not own any messages .`}
158
- client:load
159
- />
160
- <OwnersList
161
- title={`Team members (${membersList.length})`}
162
- owners={membersList}
163
- emptyMessage={`This team does not have any members.`}
164
- client:load
165
- />
147
+ {
148
+ ownedDomainsList.length > 0 && (
149
+ <PillListFlat
150
+ color="pink"
151
+ title={`Owned domains (${ownedDomainsList.length})`}
152
+ pills={ownedDomainsList}
153
+ emptyMessage={`${props.data.name} does not own any domains.`}
154
+ client:load
155
+ />
156
+ )
157
+ }
158
+ {
159
+ ownedServicesList.length > 0 && (
160
+ <PillListFlat
161
+ color="blue"
162
+ title={`Owned services (${ownedServicesList.length})`}
163
+ pills={ownedServicesList}
164
+ emptyMessage={`This team does not own any services.`}
165
+ client:load
166
+ />
167
+ )
168
+ }
169
+ {
170
+ ownedMessagesList.length > 0 && (
171
+ <PillListFlat
172
+ color="red"
173
+ title={`Owned messages (${ownedMessagesList.length})`}
174
+ pills={ownedMessagesList}
175
+ emptyMessage={`This team does not own any messages.`}
176
+ client:load
177
+ />
178
+ )
179
+ }
180
+ {
181
+ membersList.length > 0 && (
182
+ <OwnersList
183
+ title={`Team members (${membersList.length})`}
184
+ owners={membersList}
185
+ emptyMessage={`This team does not have any members.`}
186
+ client:load
187
+ />
188
+ )
189
+ }
166
190
  </div>
167
191
  </aside>
168
192
  </div>
@@ -0,0 +1,36 @@
1
+ // This file exposes the markdown for EventCatalog in the Url
2
+ // For example http://localhost:3000/docs/teams/full-stack loads the Page and http://localhost:3000/docs/teams/full-stack.mdx loads the markdown
3
+ // This is used for the LLMs to load the markdown for the given item (llms.txt);
4
+
5
+ import type { APIRoute } from 'astro';
6
+ import { getCollection } from 'astro:content';
7
+ import config from '@config';
8
+ import fs from 'fs';
9
+
10
+ const teams = await getCollection('teams');
11
+
12
+ export async function getStaticPaths() {
13
+ // Just return empty array if LLMs are not enabled
14
+ if (!config.llmsTxt?.enabled) {
15
+ return [];
16
+ }
17
+
18
+ return teams.map((team) => ({
19
+ params: { type: 'teams', id: team.data.id },
20
+ props: { content: team },
21
+ }));
22
+ }
23
+
24
+ export const GET: APIRoute = async ({ params, props }) => {
25
+ // Just return empty array if LLMs are not enabled
26
+ if (!config.llmsTxt?.enabled) {
27
+ return new Response('llms.txt is not enabled for this Catalog.', { status: 404 });
28
+ }
29
+
30
+ if (props?.content?.filePath) {
31
+ const file = fs.readFileSync(props.content.filePath, 'utf8');
32
+ return new Response(file, { status: 200 });
33
+ }
34
+
35
+ return new Response('Not found', { status: 404 });
36
+ };
@@ -63,70 +63,81 @@ const pageTitle = `User | ${props.data.name}`;
63
63
  <main class="flex sm:px-8 docs-layout h-full" data-pagefind-body data-pagefind-meta={`title:${pageTitle}`}>
64
64
  <div class="flex docs-layout w-full">
65
65
  <div class="w-full lg:mr-2 pr-8 overflow-y-auto py-8">
66
- <div class="border-b border-gray-200 pb-4">
67
- <div class="flex justify-start space-x-8">
68
- <img src={props.data.avatarUrl} alt="Profile picture" class="shadow-md w-28 rounded-md" />
69
- <div class="flex flex-col justify-between">
66
+ <div class="border-b border-gray-200 pb-6">
67
+ <div class="flex justify-start gap-6">
68
+ {
69
+ props.data.avatarUrl && (
70
+ <img
71
+ src={props.data.avatarUrl}
72
+ alt={`${props.data.name}'s profile picture`}
73
+ class="w-20 h-20 rounded-full object-cover border-2 border-gray-100"
74
+ />
75
+ )
76
+ }
77
+ <div class="flex flex-col justify-center space-y-2">
70
78
  <div>
71
- <h2 class="text-4xl font-bold">{props.data.name}</h2>
72
- <span class="text-xl font-bold text-gray-500">{props.data.role}</span>
79
+ <h2 class="text-3xl font-bold text-gray-900">{props.data.name}</h2>
80
+ {props.data.role && <span class="text-base font-medium text-gray-500">{props.data.role}</span>}
73
81
  </div>
74
- <div class="space-y-1">
82
+ <div class="flex flex-wrap gap-3">
75
83
  {
76
84
  props.data.email && (
77
- <div class="flex space-x-1 items-center text-xs text-gray-500 font-bold hover:underline hover:text-primary">
78
- <EnvelopeIcon className="w-4 h-4 text-purple-400" />
79
- <a href={`mailto:${props.data.email}`}>Email</a>
80
- </div>
85
+ <a
86
+ href={`mailto:${props.data.email}`}
87
+ class="inline-flex items-center gap-1.5 text-sm text-gray-600 hover:text-gray-900 transition-colors"
88
+ >
89
+ <EnvelopeIcon className="w-4 h-4 text-gray-400" />
90
+ <span>Email</span>
91
+ </a>
81
92
  )
82
93
  }
83
94
  {
84
95
  props.data.slackDirectMessageUrl && (
85
- <div class="flex space-x-1 items-center text-xs text-gray-500 font-bold hover:underline hover:text-primary">
86
- <img src={buildUrl('/slack-icon.svg', true)} class="w-4 h-3" />
87
- <a href={`${props.data.slackDirectMessageUrl}`}>Send DM on Slack</a>
88
- </div>
96
+ <a
97
+ href={props.data.slackDirectMessageUrl}
98
+ class="inline-flex items-center gap-1.5 text-sm text-gray-600 hover:text-gray-900 transition-colors"
99
+ >
100
+ <img src={buildUrl('/slack-icon.svg', true)} class="w-4 h-4" alt="Slack" />
101
+ <span>Slack</span>
102
+ </a>
89
103
  )
90
104
  }
91
105
  {
92
106
  props.data.msTeamsDirectMessageUrl && (
93
- <div class="flex space-x-1 items-center text-xs text-gray-500 font-bold hover:underline hover:text-primary">
94
- <img src={buildUrl('/icons/ms-teams.svg', true)} class="w-4 h-4" />
95
- <a href={`${props.data.msTeamsDirectMessageUrl}`} target="_blank" rel="noopener noreferrer">
96
- Send DM on Teams
97
- </a>
98
- </div>
107
+ <a
108
+ href={props.data.msTeamsDirectMessageUrl}
109
+ target="_blank"
110
+ rel="noopener noreferrer"
111
+ class="inline-flex items-center gap-1.5 text-sm text-gray-600 hover:text-gray-900 transition-colors"
112
+ >
113
+ <img src={buildUrl('/icons/ms-teams.svg', true)} class="w-4 h-4" alt="Teams" />
114
+ <span>Teams</span>
115
+ </a>
99
116
  )
100
117
  }
101
118
  </div>
102
119
  </div>
103
120
  </div>
104
121
  </div>
105
- <div class="border-b border-gray-200" data-pagefind-ignore>
106
- <div class="mx-auto max-w-7xl px-6 lg:px-8">
107
- <div class="mx-auto max-w-2xl lg:max-w-none">
108
- <dl
109
- class="hidden lg:grid grid-cols-1 gap-0.5 overflow-hidden rounded-2xl text-center sm:grid-cols-4 lg:grid-cols-4"
110
- >
111
- <div class="flex flex-col p-8">
112
- <dt class="text-sm font-semibold leading-6 text-gray-600"># owned domains</dt>
113
- <dd class="order-first text-3xl font-semibold tracking-tight text-gray-900">{ownedDomainsList.length}</dd>
114
- </div>
115
- <div class="flex flex-col p-8">
116
- <dt class="text-sm font-semibold leading-6 text-gray-600"># owned services</dt>
117
- <dd class="order-first text-3xl font-semibold tracking-tight text-gray-900">{ownedServicesList.length}</dd>
118
- </div>
119
- <div class="flex flex-col p-8">
120
- <dt class="text-sm font-semibold leading-6 text-gray-600"># owned messages</dt>
121
- <dd class="order-first text-3xl font-semibold tracking-tight text-gray-900">{ownedMessageList.length}</dd>
122
- </div>
123
- <div class="flex flex-col p-8">
124
- <dt class="text-sm font-semibold leading-6 text-gray-600"># teams joined</dt>
125
- <dd class="order-first text-3xl font-semibold tracking-tight text-gray-900">{associatedTeams.length}</dd>
126
- </div>
127
- </dl>
122
+ <div class="border-b border-gray-200 py-4" data-pagefind-ignore>
123
+ <dl class="grid grid-cols-2 gap-3 sm:grid-cols-4">
124
+ <div class="flex flex-col p-4 bg-gray-50 rounded-lg">
125
+ <dt class="text-xs font-medium text-gray-500">Domains</dt>
126
+ <dd class="text-2xl font-semibold text-gray-900 mt-1">{ownedDomainsList.length}</dd>
128
127
  </div>
129
- </div>
128
+ <div class="flex flex-col p-4 bg-gray-50 rounded-lg">
129
+ <dt class="text-xs font-medium text-gray-500">Services</dt>
130
+ <dd class="text-2xl font-semibold text-gray-900 mt-1">{ownedServicesList.length}</dd>
131
+ </div>
132
+ <div class="flex flex-col p-4 bg-gray-50 rounded-lg">
133
+ <dt class="text-xs font-medium text-gray-500">Messages</dt>
134
+ <dd class="text-2xl font-semibold text-gray-900 mt-1">{ownedMessageList.length}</dd>
135
+ </div>
136
+ <div class="flex flex-col p-4 bg-gray-50 rounded-lg">
137
+ <dt class="text-xs font-medium text-gray-500">Teams</dt>
138
+ <dd class="text-2xl font-semibold text-gray-900 mt-1">{associatedTeams.length}</dd>
139
+ </div>
140
+ </dl>
130
141
  </div>
131
142
  <div class="prose prose-md py-4 w-full">
132
143
  <Content components={components(props)} />
@@ -0,0 +1,36 @@
1
+ // This file exposes the markdown for EventCatalog in the Url
2
+ // For example http://localhost:3000/docs/users/dboyne loads the Page and http://localhost:3000/docs/users/dboyne.mdx loads the markdown
3
+ // This is used for the LLMs to load the markdown for the given item (llms.txt);
4
+
5
+ import type { APIRoute } from 'astro';
6
+ import { getCollection } from 'astro:content';
7
+ import config from '@config';
8
+ import fs from 'fs';
9
+
10
+ const users = await getCollection('users');
11
+
12
+ export async function getStaticPaths() {
13
+ // Just return empty array if LLMs are not enabled
14
+ if (!config.llmsTxt?.enabled) {
15
+ return [];
16
+ }
17
+
18
+ return users.map((user) => ({
19
+ params: { type: 'users', id: user.data.id },
20
+ props: { content: user },
21
+ }));
22
+ }
23
+
24
+ export const GET: APIRoute = async ({ params, props }) => {
25
+ // Just return empty array if LLMs are not enabled
26
+ if (!config.llmsTxt?.enabled) {
27
+ return new Response('llms.txt is not enabled for this Catalog.', { status: 404 });
28
+ }
29
+
30
+ if (props?.content?.filePath) {
31
+ const file = fs.readFileSync(props.content?.filePath, 'utf8');
32
+ return new Response(file, { status: 200 });
33
+ }
34
+
35
+ return new Response('Not found', { status: 404 });
36
+ };
@@ -0,0 +1,178 @@
1
+ import { isSSR } from '@utils/feature';
2
+ import { HybridPage } from '@utils/page-loaders/hybrid-page';
3
+ import { getEvents } from '@utils/collections/events';
4
+ import { getCommands } from '@utils/collections/commands';
5
+ import { getQueries } from '@utils/collections/queries';
6
+ import { getServices, getSpecificationsForService } from '@utils/collections/services';
7
+ import { getOwner } from '@utils/collections/owners';
8
+ import { buildUrl } from '@utils/url-builder';
9
+ import { resourceFileExists, readResourceFile } from '@utils/resource-files';
10
+ import path from 'path';
11
+
12
+ // Helper function to enrich owners with full details
13
+ async function enrichOwners(ownersRaw: any[]) {
14
+ if (!ownersRaw || ownersRaw.length === 0) return [];
15
+
16
+ const owners = await Promise.all(ownersRaw.map(getOwner));
17
+ const filteredOwners = owners.filter((o) => o !== undefined);
18
+
19
+ return filteredOwners.map((o) => ({
20
+ id: o.data.id,
21
+ name: o.data.name,
22
+ type: o.collection,
23
+ href: buildUrl(`/docs/${o.collection}/${o.data.id}`),
24
+ }));
25
+ }
26
+
27
+ async function fetchAllSchemas() {
28
+ // Fetch all messages
29
+ const events = await getEvents({ getAllVersions: true });
30
+ const commands = await getCommands({ getAllVersions: true });
31
+ const queries = await getQueries({ getAllVersions: true });
32
+
33
+ // Fetch all services
34
+ const services = await getServices({ getAllVersions: true });
35
+
36
+ // Combine all messages
37
+ const allMessages = [...events, ...commands, ...queries];
38
+
39
+ // Filter messages with schemas and read schema content - only keep essential data
40
+ const messagesWithSchemas = await Promise.all(
41
+ allMessages
42
+ .filter((message) => message.data.schemaPath)
43
+ .filter((message) => resourceFileExists(message, message.data.schemaPath ?? ''))
44
+ .map(async (message) => {
45
+ try {
46
+ const schemaPath = message.data.schemaPath ?? '';
47
+ const schemaContent = readResourceFile(message, schemaPath) ?? '';
48
+ const schemaExtension = path.extname(schemaPath).slice(1);
49
+ const enrichedOwners = await enrichOwners(message.data.owners || []);
50
+
51
+ return {
52
+ collection: message.collection,
53
+ data: {
54
+ id: message.data.id,
55
+ name: message.data.name,
56
+ version: message.data.version,
57
+ summary: message.data.summary,
58
+ schemaPath: message.data.schemaPath,
59
+ producers: message.data.producers || [],
60
+ consumers: message.data.consumers || [],
61
+ owners: enrichedOwners,
62
+ },
63
+ schemaContent,
64
+ schemaExtension,
65
+ };
66
+ } catch (error) {
67
+ console.error(`Error reading schema for ${message.data.id}:`, error);
68
+ const enrichedOwners = await enrichOwners(message.data.owners || []);
69
+ return {
70
+ collection: message.collection,
71
+ data: {
72
+ id: message.data.id,
73
+ name: message.data.name,
74
+ version: message.data.version,
75
+ summary: message.data.summary,
76
+ schemaPath: message.data.schemaPath,
77
+ producers: message.data.producers || [],
78
+ consumers: message.data.consumers || [],
79
+ owners: enrichedOwners,
80
+ },
81
+ schemaContent: '',
82
+ schemaExtension: 'json',
83
+ };
84
+ }
85
+ })
86
+ );
87
+
88
+ // Filter services with specifications and read spec content - only keep essential data
89
+ const servicesWithSpecs = await Promise.all(
90
+ services.map(async (service) => {
91
+ try {
92
+ const specifications = getSpecificationsForService(service);
93
+
94
+ if (specifications.length === 0) {
95
+ return null;
96
+ }
97
+
98
+ return await Promise.all(
99
+ specifications.map(async (spec) => {
100
+ if (!resourceFileExists(service, spec.path)) {
101
+ return null;
102
+ }
103
+
104
+ const schemaContent = readResourceFile(service, spec.path) ?? '';
105
+ const schemaExtension = spec.type;
106
+ const enrichedOwners = await enrichOwners(service.data.owners || []);
107
+
108
+ return {
109
+ collection: 'services',
110
+ data: {
111
+ id: `${service.data.id}`,
112
+ name: `${service.data.name} - ${spec.name}`,
113
+ version: service.data.version,
114
+ summary: service.data.summary,
115
+ schemaPath: spec.path,
116
+ owners: enrichedOwners,
117
+ },
118
+ schemaContent,
119
+ schemaExtension,
120
+ specType: spec.type,
121
+ specName: spec.name,
122
+ specFilenameWithoutExtension: spec.filenameWithoutExtension,
123
+ };
124
+ })
125
+ );
126
+ } catch (error) {
127
+ console.error(`Error reading specifications for service ${service.data.id}:`, error);
128
+ return null;
129
+ }
130
+ })
131
+ );
132
+
133
+ // Flatten and filter out null values
134
+ const flatServicesWithSpecs = servicesWithSpecs.flat().filter((service) => service !== null);
135
+
136
+ return [...messagesWithSchemas, ...flatServicesWithSpecs];
137
+ }
138
+
139
+ export class Page extends HybridPage {
140
+ static get prerender(): boolean {
141
+ return !isSSR();
142
+ }
143
+
144
+ static async getStaticPaths(): Promise<Array<{ params: any; props: any }>> {
145
+ if (isSSR()) {
146
+ return [];
147
+ }
148
+
149
+ const allSchemas = await fetchAllSchemas();
150
+
151
+ return [
152
+ {
153
+ params: {},
154
+ props: {
155
+ schemas: allSchemas,
156
+ },
157
+ },
158
+ ];
159
+ }
160
+
161
+ protected static async fetchData(_params: any) {
162
+ const allSchemas = await fetchAllSchemas();
163
+ return {
164
+ schemas: allSchemas,
165
+ };
166
+ }
167
+
168
+ protected static hasValidProps(props: any): boolean {
169
+ return props && props.schemas !== undefined;
170
+ }
171
+
172
+ protected static createNotFoundResponse(): Response {
173
+ return new Response(null, {
174
+ status: 404,
175
+ statusText: 'Schema explorer not found',
176
+ });
177
+ }
178
+ }