@eventcatalog/core 3.43.1 → 3.44.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 (48) 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 +50 -2
  4. package/dist/analytics/log-build.js +4 -4
  5. package/dist/catalog-to-astro-content-directory.cjs +31 -1
  6. package/dist/catalog-to-astro-content-directory.js +2 -2
  7. package/dist/{chunk-2GQO7I7E.js → chunk-3UVY6YHH.js} +2 -2
  8. package/dist/{chunk-O6KT4DPL.js → chunk-4SMA4HQ3.js} +1 -1
  9. package/dist/{chunk-5T63CXKU.js → chunk-6QENHZZP.js} +32 -2
  10. package/dist/{chunk-ULZYHF3V.js → chunk-B7HCX5HM.js} +1 -1
  11. package/dist/{chunk-OOX6HAE4.js → chunk-DKKPJ3QM.js} +20 -2
  12. package/dist/{chunk-KV5FCOV4.js → chunk-EPDZMVQ3.js} +1 -1
  13. package/dist/{chunk-Z5QHV4ZY.js → chunk-L26L4Y5J.js} +1 -1
  14. package/dist/{chunk-WAJIJEI3.js → chunk-LHR4G2UO.js} +1 -1
  15. package/dist/{chunk-C6S5P57F.js → chunk-MFLSPCEA.js} +1 -1
  16. package/dist/constants.cjs +1 -1
  17. package/dist/constants.js +1 -1
  18. package/dist/docs/development/guides/owners/users/02-adding-users.md +1 -2
  19. package/dist/eventcatalog-config-file-utils.cjs +31 -1
  20. package/dist/eventcatalog-config-file-utils.js +1 -1
  21. package/dist/eventcatalog.cjs +50 -2
  22. package/dist/eventcatalog.config.d.cts +25 -0
  23. package/dist/eventcatalog.config.d.ts +25 -0
  24. package/dist/eventcatalog.js +9 -9
  25. package/dist/features.cjs +31 -1
  26. package/dist/features.js +2 -2
  27. package/dist/generate.cjs +32 -2
  28. package/dist/generate.js +4 -4
  29. package/dist/resolve-catalog-dependencies.cjs +31 -1
  30. package/dist/resolve-catalog-dependencies.js +2 -2
  31. package/dist/utils/cli-logger.cjs +1 -1
  32. package/dist/utils/cli-logger.js +2 -2
  33. package/eventcatalog/astro.config.mjs +9 -1
  34. package/eventcatalog/public/icons/azure.svg +23 -0
  35. package/eventcatalog/src/components/FieldsExplorer/FieldNodeGraph.tsx +2 -2
  36. package/eventcatalog/src/components/Tables/Table.tsx +4 -0
  37. package/eventcatalog/src/components/Tables/columns/DirectorySourceColumn.tsx +84 -0
  38. package/eventcatalog/src/components/Tables/columns/TeamsTableColumns.tsx +11 -0
  39. package/eventcatalog/src/components/Tables/columns/UserTableColumns.tsx +11 -0
  40. package/eventcatalog/src/content.config.ts +31 -9
  41. package/eventcatalog/src/enterprise/directory/user-team-directory.spec.ts +527 -0
  42. package/eventcatalog/src/enterprise/directory/user-team-directory.ts +191 -0
  43. package/eventcatalog/src/pages/directory/[type]/index.astro +2 -0
  44. package/eventcatalog/src/pages/docs/teams/[id]/index.astro +35 -5
  45. package/eventcatalog/src/pages/docs/users/[id]/index.astro +35 -0
  46. package/eventcatalog/src/stores/eventcatalog-store.spec.ts +60 -0
  47. package/eventcatalog/src/stores/eventcatalog-store.ts +103 -0
  48. package/package.json +8 -8
@@ -33,6 +33,8 @@ function mapToItem(i: any) {
33
33
  id: d.data.id,
34
34
  name: d.data.name,
35
35
  // @ts-ignore
36
+ source: d.data?.source,
37
+ // @ts-ignore
36
38
  role: d.data?.role,
37
39
  // @ts-ignore
38
40
  avatarUrl: d.data?.avatarUrl,
@@ -7,6 +7,7 @@ import type { CollectionEntry } from 'astro:content';
7
7
  import OwnersList from '@components/Lists/OwnersList';
8
8
  import PillListFlat from '@components/Lists/PillListFlat';
9
9
  import EnvelopeIcon from '@heroicons/react/16/solid/EnvelopeIcon';
10
+ import { Github } from 'lucide-react';
10
11
  import { formatAdrStatus } from '@utils/collections/adrs';
11
12
  import { buildUrl } from '@utils/url-builder';
12
13
  import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
@@ -78,6 +79,13 @@ const ownedAdrsList = adrs.map((p) => ({
78
79
  }));
79
80
 
80
81
  const pageTitle = `Team | ${props.data.name}`;
82
+ const source = props.data.source;
83
+ const sourceLabels: Record<string, string> = {
84
+ github: 'GitHub',
85
+ 'microsoft-entra': 'Microsoft Entra ID',
86
+ };
87
+ const sourceLabel = source?.provider ? (sourceLabels[source.provider] ?? source.provider) : undefined;
88
+ const sourceTitle = sourceLabel ? `Synced from ${sourceLabel}` : undefined;
81
89
  ---
82
90
 
83
91
  <VerticalSideBarLayout title={pageTitle} description={props.data.summary}>
@@ -92,11 +100,6 @@ const pageTitle = `Team | ${props.data.name}`;
92
100
  <div class="flex justify-start">
93
101
  <div class="flex flex-col justify-between space-y-3">
94
102
  <div>
95
- <span
96
- class="inline-flex items-center px-2.5 py-0.5 rounded-md text-xs font-medium bg-[rgb(var(--ec-content-hover))] text-[rgb(var(--ec-page-text-muted))] mb-2"
97
- >
98
- Team
99
- </span>
100
103
  <h2 class="text-3xl font-bold text-[rgb(var(--ec-page-text))]">{props.data.name}</h2>
101
104
  </div>
102
105
  {
@@ -140,6 +143,33 @@ const pageTitle = `Team | ${props.data.name}`;
140
143
  </a>
141
144
  )
142
145
  }
146
+ {
147
+ source && sourceLabel && source.url && (
148
+ <a
149
+ href={source.url}
150
+ target="_blank"
151
+ rel="noopener noreferrer"
152
+ title={sourceTitle}
153
+ class="inline-flex items-center gap-1.5 text-sm text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))] transition-colors"
154
+ >
155
+ {source.provider === 'github' && <Github className="w-4 h-4 text-[rgb(var(--ec-icon-color))]" />}
156
+ {source.provider === 'microsoft-entra' && <img src={buildUrl('/icons/azure.svg', true)} class="w-4 h-4" alt="" />}
157
+ <span>{sourceLabel}</span>
158
+ </a>
159
+ )
160
+ }
161
+ {
162
+ source && sourceLabel && !source.url && (
163
+ <span
164
+ title={sourceTitle}
165
+ class="inline-flex items-center gap-1.5 text-sm text-[rgb(var(--ec-page-text-muted))]"
166
+ >
167
+ {source.provider === 'github' && <Github className="w-4 h-4 text-[rgb(var(--ec-icon-color))]" />}
168
+ {source.provider === 'microsoft-entra' && <img src={buildUrl('/icons/azure.svg', true)} class="w-4 h-4" alt="" />}
169
+ <span>{sourceLabel}</span>
170
+ </span>
171
+ )
172
+ }
143
173
  </div>
144
174
  </div>
145
175
  </div>
@@ -7,6 +7,7 @@ import type { CollectionEntry } from 'astro:content';
7
7
  import OwnersList from '@components/Lists/OwnersList';
8
8
  import PillListFlat from '@components/Lists/PillListFlat';
9
9
  import EnvelopeIcon from '@heroicons/react/16/solid/EnvelopeIcon';
10
+ import { Github } from 'lucide-react';
10
11
  import { formatAdrStatus } from '@utils/collections/adrs';
11
12
  import { buildUrl } from '@utils/url-builder';
12
13
  import VerticalSideBarLayout from '@layouts/VerticalSideBarLayout.astro';
@@ -75,6 +76,13 @@ const associatedTeams = teams.map((o) => ({
75
76
  }));
76
77
 
77
78
  const pageTitle = `User | ${props.data.name}`;
79
+ const source = props.data.source;
80
+ const sourceLabels: Record<string, string> = {
81
+ github: 'GitHub',
82
+ 'microsoft-entra': 'Microsoft Entra ID',
83
+ };
84
+ const sourceLabel = source?.provider ? (sourceLabels[source.provider] ?? source.provider) : undefined;
85
+ const sourceTitle = sourceLabel ? `Synced from ${sourceLabel}` : undefined;
78
86
  ---
79
87
 
80
88
  <VerticalSideBarLayout title={pageTitle}>
@@ -141,6 +149,33 @@ const pageTitle = `User | ${props.data.name}`;
141
149
  </a>
142
150
  )
143
151
  }
152
+ {
153
+ source && sourceLabel && source.url && (
154
+ <a
155
+ href={source.url}
156
+ target="_blank"
157
+ rel="noopener noreferrer"
158
+ title={sourceTitle}
159
+ class="inline-flex items-center gap-1.5 text-sm text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-page-text))] transition-colors"
160
+ >
161
+ {source.provider === 'github' && <Github className="w-4 h-4 text-[rgb(var(--ec-icon-color))]" />}
162
+ {source.provider === 'microsoft-entra' && <img src={buildUrl('/icons/azure.svg', true)} class="w-4 h-4" alt="" />}
163
+ <span>{sourceLabel}</span>
164
+ </a>
165
+ )
166
+ }
167
+ {
168
+ source && sourceLabel && !source.url && (
169
+ <span
170
+ title={sourceTitle}
171
+ class="inline-flex items-center gap-1.5 text-sm text-[rgb(var(--ec-page-text-muted))]"
172
+ >
173
+ {source.provider === 'github' && <Github className="w-4 h-4 text-[rgb(var(--ec-icon-color))]" />}
174
+ {source.provider === 'microsoft-entra' && <img src={buildUrl('/icons/azure.svg', true)} class="w-4 h-4" alt="" />}
175
+ <span>{sourceLabel}</span>
176
+ </span>
177
+ )
178
+ }
144
179
  </div>
145
180
  </div>
146
181
  </div>
@@ -0,0 +1,60 @@
1
+ import { mkdtemp, readFile, rm } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import path from 'node:path';
4
+ import { describe, expect, it } from 'vitest';
5
+ import { EventCatalogStore } from './eventcatalog-store';
6
+
7
+ type TestStoreResources = {
8
+ users: { id: string; name: string }[];
9
+ teams: { id: string; name: string }[];
10
+ };
11
+
12
+ describe('EventCatalogStore', () => {
13
+ it('writes one collection while preserving the rest of the store', async () => {
14
+ const tempDir = await mkdtemp(path.join(tmpdir(), 'eventcatalog-store-'));
15
+ const storePath = EventCatalogStore.getStorePath(tempDir, 'directory');
16
+ const store = new EventCatalogStore<TestStoreResources>({
17
+ storePath,
18
+ resources: {
19
+ users: [],
20
+ teams: [],
21
+ },
22
+ });
23
+
24
+ try {
25
+ await store.writeCollection('teams', [{ id: 'platform', name: 'Platform' }]);
26
+ await store.writeCollection('users', [{ id: 'jane', name: 'Jane' }]);
27
+
28
+ const stored = JSON.parse(await readFile(storePath, 'utf8'));
29
+ expect(stored).toMatchObject({
30
+ version: '1',
31
+ resources: {
32
+ users: [{ id: 'jane', name: 'Jane' }],
33
+ teams: [{ id: 'platform', name: 'Platform' }],
34
+ },
35
+ });
36
+ expect(stored.generatedAt).toEqual(expect.any(String));
37
+ } finally {
38
+ await rm(tempDir, { recursive: true, force: true });
39
+ }
40
+ });
41
+
42
+ it('does not create a store file when clearing a collection that has not been generated', async () => {
43
+ const tempDir = await mkdtemp(path.join(tmpdir(), 'eventcatalog-store-'));
44
+ const storePath = EventCatalogStore.getStorePath(tempDir, 'directory');
45
+ const store = new EventCatalogStore<TestStoreResources>({
46
+ storePath,
47
+ resources: {
48
+ users: [],
49
+ teams: [],
50
+ },
51
+ });
52
+
53
+ try {
54
+ await store.clearCollectionIfStoreExists('users');
55
+ expect(await store.exists()).toBe(false);
56
+ } finally {
57
+ await rm(tempDir, { recursive: true, force: true });
58
+ }
59
+ });
60
+ });
@@ -0,0 +1,103 @@
1
+ import { access, mkdir, readFile, rename, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ type StoreResource = {
5
+ id: string;
6
+ };
7
+
8
+ type StoreResources<TResources> = {
9
+ [K in keyof TResources]: StoreResource[];
10
+ };
11
+
12
+ type EventCatalogStoreFile<TResources> = {
13
+ version: '1';
14
+ generatedAt: string;
15
+ resources: TResources;
16
+ };
17
+
18
+ type EventCatalogStoreOptions<TResources> = {
19
+ storePath: string;
20
+ resources: TResources;
21
+ };
22
+
23
+ const writeQueues = new Map<string, Promise<void>>();
24
+
25
+ export class EventCatalogStore<TResources extends StoreResources<TResources>> {
26
+ static getStorePath(projectDir: string, name: string) {
27
+ return path.join(projectDir, '.eventcatalog', 'store', `${name}.json`);
28
+ }
29
+
30
+ constructor(private options: EventCatalogStoreOptions<TResources>) {}
31
+
32
+ async exists() {
33
+ try {
34
+ await access(this.options.storePath);
35
+ return true;
36
+ } catch {
37
+ return false;
38
+ }
39
+ }
40
+
41
+ async read(): Promise<EventCatalogStoreFile<TResources>> {
42
+ try {
43
+ const store = JSON.parse(await readFile(this.options.storePath, 'utf8')) as Partial<EventCatalogStoreFile<TResources>>;
44
+ return {
45
+ version: '1',
46
+ generatedAt: store.generatedAt ?? new Date().toISOString(),
47
+ resources: this.normalizeResources(store.resources),
48
+ };
49
+ } catch {
50
+ return this.createEmptyStore();
51
+ }
52
+ }
53
+
54
+ async writeCollection<TKey extends keyof TResources & string>(collection: TKey, resources: TResources[TKey]) {
55
+ await this.queueWrite(async () => {
56
+ const store = await this.read();
57
+ store.generatedAt = new Date().toISOString();
58
+ store.resources[collection] = [...resources].sort((a, b) => a.id.localeCompare(b.id)) as TResources[TKey];
59
+
60
+ await this.write(store);
61
+ });
62
+ }
63
+
64
+ async clearCollectionIfStoreExists<TKey extends keyof TResources & string>(collection: TKey) {
65
+ if (!(await this.exists())) return;
66
+ await this.writeCollection(collection, [] as unknown as TResources[TKey]);
67
+ }
68
+
69
+ private createEmptyStore(): EventCatalogStoreFile<TResources> {
70
+ return {
71
+ version: '1',
72
+ generatedAt: new Date().toISOString(),
73
+ resources: this.normalizeResources(),
74
+ };
75
+ }
76
+
77
+ private normalizeResources(resources?: Partial<TResources>): TResources {
78
+ const normalized = {} as TResources;
79
+
80
+ for (const collection of Object.keys(this.options.resources) as (keyof TResources & string)[]) {
81
+ const value = resources?.[collection];
82
+ normalized[collection] = (
83
+ Array.isArray(value) ? value : [...this.options.resources[collection]]
84
+ ) as TResources[typeof collection];
85
+ }
86
+
87
+ return normalized;
88
+ }
89
+
90
+ private async write(store: EventCatalogStoreFile<TResources>) {
91
+ await mkdir(path.dirname(this.options.storePath), { recursive: true });
92
+ await writeFile(`${this.options.storePath}.tmp`, `${JSON.stringify(store, null, 2)}\n`);
93
+ await rename(`${this.options.storePath}.tmp`, this.options.storePath);
94
+ }
95
+
96
+ private async queueWrite(write: () => Promise<void>) {
97
+ const previousWrite = writeQueues.get(this.options.storePath) ?? Promise.resolve();
98
+ const nextWrite = previousWrite.catch(() => undefined).then(write);
99
+
100
+ writeQueues.set(this.options.storePath, nextWrite);
101
+ await nextWrite;
102
+ }
103
+ }
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "license": "SEE LICENSE IN LICENSE",
9
9
  "type": "module",
10
- "version": "3.43.1",
10
+ "version": "3.44.1",
11
11
  "publishConfig": {
12
12
  "access": "public"
13
13
  },
@@ -29,10 +29,10 @@
29
29
  ],
30
30
  "dependencies": {
31
31
  "@ai-sdk/react": "^3.0.17",
32
- "@astrojs/markdown-remark": "^7.1.1",
33
- "@astrojs/mdx": "^5.0.4",
34
- "@astrojs/node": "^10.1.0",
35
- "@astrojs/react": "^5.0.4",
32
+ "@astrojs/markdown-remark": "^7.2.0",
33
+ "@astrojs/mdx": "^6.0.1",
34
+ "@astrojs/node": "^10.1.2",
35
+ "@astrojs/react": "^5.0.6",
36
36
  "@astrojs/rss": "^4.0.18",
37
37
  "@asyncapi/avro-schema-parser": "3.0.24",
38
38
  "@asyncapi/parser": "^3.6.0",
@@ -58,7 +58,7 @@
58
58
  "@tanstack/react-table": "^8.17.3",
59
59
  "@xyflow/react": "^12.3.6",
60
60
  "ai": "^6.0.17",
61
- "astro": "^6.3.1",
61
+ "astro": "^6.4.1",
62
62
  "astro-compress": "^2.4.0",
63
63
  "astro-expressive-code": "^0.41.7",
64
64
  "astro-seo": "^0.8.4",
@@ -112,9 +112,9 @@
112
112
  "update-notifier": "^7.3.1",
113
113
  "uuid": "^10.0.0",
114
114
  "zod": "^4.3.6",
115
- "@eventcatalog/linter": "1.0.27",
115
+ "@eventcatalog/sdk": "2.24.1",
116
116
  "@eventcatalog/visualiser": "^3.22.1",
117
- "@eventcatalog/sdk": "2.23.1"
117
+ "@eventcatalog/linter": "1.0.29"
118
118
  },
119
119
  "devDependencies": {
120
120
  "@astrojs/check": "^0.9.9",