@eventcatalog/core 4.0.3 → 4.0.5

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.
@@ -36,7 +36,7 @@ module.exports = __toCommonJS(analytics_exports);
36
36
  var import_os = __toESM(require("os"), 1);
37
37
 
38
38
  // package.json
39
- var version = "4.0.3";
39
+ var version = "4.0.5";
40
40
 
41
41
  // src/constants.ts
42
42
  var VERSION = version;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  raiseEvent
3
- } from "../chunk-5OGYWSP3.js";
4
- import "../chunk-K54LSZNF.js";
3
+ } from "../chunk-P4JJRN7P.js";
4
+ import "../chunk-3RHS7TFM.js";
5
5
  export {
6
6
  raiseEvent
7
7
  };
@@ -140,7 +140,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
140
140
  var import_os = __toESM(require("os"), 1);
141
141
 
142
142
  // package.json
143
- var version = "4.0.3";
143
+ var version = "4.0.5";
144
144
 
145
145
  // src/constants.ts
146
146
  var VERSION = version;
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  log_build_default
3
- } from "../chunk-UXP5LLH2.js";
4
- import "../chunk-5OGYWSP3.js";
3
+ } from "../chunk-YGSPS7DC.js";
4
+ import "../chunk-P4JJRN7P.js";
5
5
  import "../chunk-DAOXTQVS.js";
6
- import "../chunk-K54LSZNF.js";
6
+ import "../chunk-3RHS7TFM.js";
7
7
  import "../chunk-6QENHZZP.js";
8
8
  export {
9
9
  log_build_default as default
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "4.0.3";
2
+ var version = "4.0.5";
3
3
 
4
4
  // src/constants.ts
5
5
  var VERSION = version;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  logger
3
- } from "./chunk-2CTETYS7.js";
3
+ } from "./chunk-VENVGFLE.js";
4
4
  import {
5
5
  cleanup,
6
6
  getEventCatalogConfigFile
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-K54LSZNF.js";
3
+ } from "./chunk-3RHS7TFM.js";
4
4
 
5
5
  // src/analytics/analytics.js
6
6
  import os from "os";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-K54LSZNF.js";
3
+ } from "./chunk-3RHS7TFM.js";
4
4
 
5
5
  // src/utils/cli-logger.ts
6
6
  import pc from "picocolors";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  raiseEvent
3
- } from "./chunk-5OGYWSP3.js";
3
+ } from "./chunk-P4JJRN7P.js";
4
4
  import {
5
5
  countResources,
6
6
  serializeCounts
@@ -25,7 +25,7 @@ __export(constants_exports, {
25
25
  module.exports = __toCommonJS(constants_exports);
26
26
 
27
27
  // package.json
28
- var version = "4.0.3";
28
+ var version = "4.0.5";
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-K54LSZNF.js";
3
+ } from "./chunk-3RHS7TFM.js";
4
4
  export {
5
5
  VERSION
6
6
  };
@@ -144,7 +144,7 @@ var verifyRequiredFieldsAreInCatalogConfigFile = async (projectDirectory) => {
144
144
  var import_picocolors = __toESM(require("picocolors"), 1);
145
145
 
146
146
  // package.json
147
- var version = "4.0.3";
147
+ var version = "4.0.5";
148
148
 
149
149
  // src/constants.ts
150
150
  var VERSION = version;
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  log_build_default
3
- } from "./chunk-UXP5LLH2.js";
3
+ } from "./chunk-YGSPS7DC.js";
4
4
  import {
5
5
  runMigrations
6
6
  } from "./chunk-XUAF2H54.js";
7
7
  import "./chunk-CA4U2JP7.js";
8
8
  import {
9
9
  generate
10
- } from "./chunk-63J5GUOH.js";
10
+ } from "./chunk-KDWSGOTX.js";
11
11
  import {
12
12
  logger
13
- } from "./chunk-2CTETYS7.js";
13
+ } from "./chunk-VENVGFLE.js";
14
14
  import {
15
15
  resolve_catalog_dependencies_default
16
16
  } from "./chunk-LHR4G2UO.js";
@@ -20,7 +20,7 @@ import {
20
20
  import {
21
21
  watch
22
22
  } from "./chunk-O3Y2G6CY.js";
23
- import "./chunk-5OGYWSP3.js";
23
+ import "./chunk-P4JJRN7P.js";
24
24
  import "./chunk-DAOXTQVS.js";
25
25
  import {
26
26
  catalogToAstro
@@ -28,7 +28,7 @@ import {
28
28
  import "./chunk-VC6VUJIX.js";
29
29
  import {
30
30
  VERSION
31
- } from "./chunk-K54LSZNF.js";
31
+ } from "./chunk-3RHS7TFM.js";
32
32
  import {
33
33
  linkCoreNodeModules,
34
34
  resolveInstalledCoreNodeModules
package/dist/generate.cjs CHANGED
@@ -108,7 +108,7 @@ var getEventCatalogConfigFile = async (projectDirectory) => {
108
108
  var import_picocolors = __toESM(require("picocolors"), 1);
109
109
 
110
110
  // package.json
111
- var version = "4.0.3";
111
+ var version = "4.0.5";
112
112
 
113
113
  // src/constants.ts
114
114
  var VERSION = version;
package/dist/generate.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  generate
3
- } from "./chunk-63J5GUOH.js";
4
- import "./chunk-2CTETYS7.js";
5
- import "./chunk-K54LSZNF.js";
3
+ } from "./chunk-KDWSGOTX.js";
4
+ import "./chunk-VENVGFLE.js";
5
+ import "./chunk-3RHS7TFM.js";
6
6
  import "./chunk-6QENHZZP.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 = "4.0.3";
39
+ var version = "4.0.5";
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-2CTETYS7.js";
4
- import "../chunk-K54LSZNF.js";
3
+ } from "../chunk-VENVGFLE.js";
4
+ import "../chunk-3RHS7TFM.js";
5
5
  export {
6
6
  logger
7
7
  };
@@ -20,6 +20,7 @@ import { resolveIconUrl } from '@utils/icon';
20
20
  import { FilterDropdown, CheckboxItem } from './FilterComponents';
21
21
  import { getDiscoverColumns } from './columns';
22
22
  import { formatAdrStatus, type AdrStatus } from '@utils/collections/adr-constants';
23
+ import { buildDiscoverFilterSearch, filterKnownValues, parseDiscoverFilterSearch } from './url-filters';
23
24
 
24
25
  export type CollectionType =
25
26
  | 'agents'
@@ -53,6 +54,7 @@ export interface DiscoverTableData {
53
54
  hasServices?: boolean;
54
55
  hasFlows?: boolean;
55
56
  isSubdomain?: boolean;
57
+ parentDomains?: Array<{ id: string; name: string; version: string }>;
56
58
  data: {
57
59
  id: string;
58
60
  name: string;
@@ -83,6 +85,7 @@ export interface DiscoverTableData {
83
85
  model?: any;
84
86
  agents?: Array<any>;
85
87
  services?: Array<any>;
88
+ domains?: Array<any>;
86
89
  flows?: Array<any>;
87
90
  servicesThatWriteToContainer?: Array<any>;
88
91
  servicesThatReadFromContainer?: Array<any>;
@@ -161,6 +164,14 @@ const AgentProviderIcon = ({ provider, className }: { provider: string; classNam
161
164
  return <img src={providerIconUrls.default} alt="" className={className} loading="lazy" />;
162
165
  };
163
166
 
167
+ const areStringArraysEqual = (left: string[], right: string[]) =>
168
+ left.length === right.length && left.every((value, index) => value === right[index]);
169
+
170
+ const DOMAIN_TYPE_OPTIONS = [
171
+ { id: 'root', label: 'Root domains' },
172
+ { id: 'subdomain', label: 'Subdomains' },
173
+ ];
174
+
164
175
  export function DiscoverTable<T extends DiscoverTableData>({
165
176
  data: initialData,
166
177
  collectionType,
@@ -185,34 +196,6 @@ export function DiscoverTable<T extends DiscoverTableData>({
185
196
  () => getDiscoverColumns(collectionType, tableConfiguration ?? { columns: {} }),
186
197
  [collectionType, tableConfiguration]
187
198
  );
188
- const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
189
- const [sorting, setSorting] = useState<SortingState>([]);
190
- const [tableFilter, setTableFilter] = useState('');
191
- const PAGE_SIZE_OPTIONS = [10, 25, 50, 100];
192
- const PAGE_SIZE_STORAGE_KEY = 'eventcatalog-discover-page-size';
193
- const [pageSize, setPageSize] = useState<number>(() => {
194
- if (typeof window === 'undefined') return 10;
195
- const stored = Number(window.localStorage.getItem(PAGE_SIZE_STORAGE_KEY));
196
- return PAGE_SIZE_OPTIONS.includes(stored) ? stored : 10;
197
- });
198
- useEffect(() => {
199
- if (typeof window !== 'undefined') {
200
- window.localStorage.setItem(PAGE_SIZE_STORAGE_KEY, String(pageSize));
201
- }
202
- }, [pageSize]);
203
- const [showOnlyLatest, setShowOnlyLatest] = useState(true);
204
- const [onlyShowDrafts, setOnlyShowDrafts] = useState(false);
205
- const [selectedDomains, setSelectedDomains] = useState<string[]>([]);
206
- const [selectedOwners, setSelectedOwners] = useState<string[]>([]);
207
- const [selectedProducers, setSelectedProducers] = useState<string[]>([]);
208
- const [selectedConsumers, setSelectedConsumers] = useState<string[]>([]);
209
- const [selectedAgentProviders, setSelectedAgentProviders] = useState<string[]>([]);
210
- const [selectedAgentModels, setSelectedAgentModels] = useState<string[]>([]);
211
- const [selectedBadges, setSelectedBadges] = useState<string[]>([]);
212
- const [selectedProperties, setSelectedProperties] = useState<string[]>([]);
213
- const [selectedStatuses, setSelectedStatuses] = useState<string[]>([]);
214
- const selectedKind = collectionKinds.find((kind) => kind.id === collectionType);
215
-
216
199
  // Collect unique badges from all items
217
200
  const allBadges = useMemo(() => {
218
201
  const badgeMap = new Map<string, { content: string; backgroundColor?: string; textColor?: string; count: number }>();
@@ -234,6 +217,179 @@ export function DiscoverTable<T extends DiscoverTableData>({
234
217
  });
235
218
  return Array.from(badgeMap.values()).sort((a, b) => b.count - a.count);
236
219
  }, [initialData]);
220
+ const statusCounts = useMemo(() => {
221
+ const counts: Record<string, number> = {};
222
+ initialData.forEach((item) => {
223
+ const status = item.data.status;
224
+ if (status) counts[status] = (counts[status] || 0) + 1;
225
+ });
226
+ return counts;
227
+ }, [initialData]);
228
+
229
+ const statusOptions = useMemo(
230
+ () =>
231
+ Object.keys(statusCounts)
232
+ .map((status) => ({
233
+ id: status,
234
+ name: formatAdrStatus(status as AdrStatus),
235
+ count: statusCounts[status],
236
+ }))
237
+ .sort((a, b) => a.name.localeCompare(b.name)),
238
+ [statusCounts]
239
+ );
240
+ const [initialUrlFilters] = useState(() =>
241
+ parseDiscoverFilterSearch(typeof window === 'undefined' ? '' : window.location.search)
242
+ );
243
+ const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
244
+ const [sorting, setSorting] = useState<SortingState>([]);
245
+ const [tableFilter, setTableFilter] = useState(initialUrlFilters.q);
246
+ const PAGE_SIZE_OPTIONS = [10, 25, 50, 100];
247
+ const PAGE_SIZE_STORAGE_KEY = 'eventcatalog-discover-page-size';
248
+ const [pageSize, setPageSize] = useState<number>(() => {
249
+ if (typeof window === 'undefined') return 10;
250
+ const stored = Number(window.localStorage.getItem(PAGE_SIZE_STORAGE_KEY));
251
+ return PAGE_SIZE_OPTIONS.includes(stored) ? stored : 10;
252
+ });
253
+ useEffect(() => {
254
+ if (typeof window !== 'undefined') {
255
+ window.localStorage.setItem(PAGE_SIZE_STORAGE_KEY, String(pageSize));
256
+ }
257
+ }, [pageSize]);
258
+ const [showOnlyLatest, setShowOnlyLatest] = useState(initialUrlFilters.showOnlyLatest);
259
+ const [onlyShowDrafts, setOnlyShowDrafts] = useState(initialUrlFilters.onlyShowDrafts);
260
+ const [selectedDomains, setSelectedDomains] = useState<string[]>(() =>
261
+ showDomainsFilter ? filterKnownValues(initialUrlFilters.domains, new Set(domains.map((domain) => domain.id))) : []
262
+ );
263
+ const [selectedOwners, setSelectedOwners] = useState<string[]>(() =>
264
+ showOwnersFilter ? filterKnownValues(initialUrlFilters.owners, new Set(owners.map((owner) => owner.id))) : []
265
+ );
266
+ const [selectedProducers, setSelectedProducers] = useState<string[]>(() =>
267
+ showProducersFilter ? filterKnownValues(initialUrlFilters.producers, new Set(producers.map((producer) => producer.id))) : []
268
+ );
269
+ const [selectedConsumers, setSelectedConsumers] = useState<string[]>(() =>
270
+ showConsumersFilter ? filterKnownValues(initialUrlFilters.consumers, new Set(consumers.map((consumer) => consumer.id))) : []
271
+ );
272
+ const [selectedAgentProviders, setSelectedAgentProviders] = useState<string[]>(() =>
273
+ collectionType === 'agents'
274
+ ? filterKnownValues(initialUrlFilters.agentProviders, new Set(agentProviders.map((provider) => provider.id)))
275
+ : []
276
+ );
277
+ const [selectedAgentModels, setSelectedAgentModels] = useState<string[]>(() =>
278
+ collectionType === 'agents'
279
+ ? filterKnownValues(initialUrlFilters.agentModels, new Set(agentModels.map((model) => model.id)))
280
+ : []
281
+ );
282
+ const [selectedDomainTypes, setSelectedDomainTypes] = useState<string[]>(() =>
283
+ collectionType === 'domains'
284
+ ? initialUrlFilters.domainTypes.length > 0
285
+ ? filterKnownValues(initialUrlFilters.domainTypes, new Set(DOMAIN_TYPE_OPTIONS.map((option) => option.id)))
286
+ : ['root']
287
+ : []
288
+ );
289
+ const [selectedBadges, setSelectedBadges] = useState<string[]>(() =>
290
+ filterKnownValues(initialUrlFilters.badges, new Set(allBadges.map((badge) => badge.content)))
291
+ );
292
+ const [selectedProperties, setSelectedProperties] = useState<string[]>(() =>
293
+ filterKnownValues(initialUrlFilters.properties, new Set(propertyOptions.map((property) => property.id)))
294
+ );
295
+ const [selectedStatuses, setSelectedStatuses] = useState<string[]>(() =>
296
+ collectionType === 'adrs'
297
+ ? filterKnownValues(initialUrlFilters.statuses, new Set(statusOptions.map((status) => status.id)))
298
+ : []
299
+ );
300
+ const selectedKind = collectionKinds.find((kind) => kind.id === collectionType);
301
+ const knownFilterValues = useMemo(
302
+ () => ({
303
+ domains: showDomainsFilter ? new Set(domains.map((domain) => domain.id)) : new Set<string>(),
304
+ owners: showOwnersFilter ? new Set(owners.map((owner) => owner.id)) : new Set<string>(),
305
+ producers: showProducersFilter ? new Set(producers.map((producer) => producer.id)) : new Set<string>(),
306
+ consumers: showConsumersFilter ? new Set(consumers.map((consumer) => consumer.id)) : new Set<string>(),
307
+ agentProviders: collectionType === 'agents' ? new Set(agentProviders.map((provider) => provider.id)) : new Set<string>(),
308
+ agentModels: collectionType === 'agents' ? new Set(agentModels.map((model) => model.id)) : new Set<string>(),
309
+ domainTypes: collectionType === 'domains' ? new Set(DOMAIN_TYPE_OPTIONS.map((option) => option.id)) : new Set<string>(),
310
+ badges: new Set(allBadges.map((badge) => badge.content)),
311
+ properties: new Set(propertyOptions.map((property) => property.id)),
312
+ statuses: collectionType === 'adrs' ? new Set(statusOptions.map((status) => status.id)) : new Set<string>(),
313
+ }),
314
+ [
315
+ agentModels,
316
+ agentProviders,
317
+ allBadges,
318
+ collectionType,
319
+ consumers,
320
+ domains,
321
+ owners,
322
+ producers,
323
+ propertyOptions,
324
+ showConsumersFilter,
325
+ showDomainsFilter,
326
+ showOwnersFilter,
327
+ showProducersFilter,
328
+ statusOptions,
329
+ ]
330
+ );
331
+ const currentUrlFilters = useMemo(
332
+ () => ({
333
+ q: tableFilter,
334
+ domains: selectedDomains,
335
+ owners: selectedOwners,
336
+ producers: selectedProducers,
337
+ consumers: selectedConsumers,
338
+ agentProviders: selectedAgentProviders,
339
+ agentModels: selectedAgentModels,
340
+ badges: selectedBadges,
341
+ properties: selectedProperties,
342
+ statuses: selectedStatuses,
343
+ domainTypes: selectedDomainTypes,
344
+ showOnlyLatest,
345
+ onlyShowDrafts,
346
+ }),
347
+ [
348
+ onlyShowDrafts,
349
+ selectedAgentModels,
350
+ selectedAgentProviders,
351
+ selectedBadges,
352
+ selectedConsumers,
353
+ selectedDomainTypes,
354
+ selectedDomains,
355
+ selectedOwners,
356
+ selectedProducers,
357
+ selectedProperties,
358
+ selectedStatuses,
359
+ showOnlyLatest,
360
+ tableFilter,
361
+ ]
362
+ );
363
+
364
+ useEffect(() => {
365
+ const pruneState = (values: string[], knownValues: Set<string>) => {
366
+ const nextValues = filterKnownValues(values, knownValues);
367
+ return areStringArraysEqual(values, nextValues) ? values : nextValues;
368
+ };
369
+
370
+ setSelectedDomains((values) => pruneState(values, knownFilterValues.domains));
371
+ setSelectedOwners((values) => pruneState(values, knownFilterValues.owners));
372
+ setSelectedProducers((values) => pruneState(values, knownFilterValues.producers));
373
+ setSelectedConsumers((values) => pruneState(values, knownFilterValues.consumers));
374
+ setSelectedAgentProviders((values) => pruneState(values, knownFilterValues.agentProviders));
375
+ setSelectedAgentModels((values) => pruneState(values, knownFilterValues.agentModels));
376
+ setSelectedDomainTypes((values) => pruneState(values, knownFilterValues.domainTypes));
377
+ setSelectedBadges((values) => pruneState(values, knownFilterValues.badges));
378
+ setSelectedProperties((values) => pruneState(values, knownFilterValues.properties));
379
+ setSelectedStatuses((values) => pruneState(values, knownFilterValues.statuses));
380
+ }, [knownFilterValues]);
381
+
382
+ useEffect(() => {
383
+ if (typeof window === 'undefined') return;
384
+
385
+ const nextSearch = buildDiscoverFilterSearch(currentUrlFilters, window.location.search);
386
+ const nextUrl = `${window.location.pathname}${nextSearch}${window.location.hash}`;
387
+ const currentUrl = `${window.location.pathname}${window.location.search}${window.location.hash}`;
388
+
389
+ if (nextUrl !== currentUrl) {
390
+ window.history.replaceState(window.history.state, '', nextUrl);
391
+ }
392
+ }, [currentUrlFilters]);
237
393
 
238
394
  // Filter data based on all filter states
239
395
  const filteredData = useMemo(() => {
@@ -264,6 +420,15 @@ export function DiscoverTable<T extends DiscoverTableData>({
264
420
  }
265
421
  }
266
422
 
423
+ // Domain type filter
424
+ if (collectionType === 'domains' && selectedDomainTypes.length > 0) {
425
+ const matchesRoot = selectedDomainTypes.includes('root') && !row.isSubdomain;
426
+ const matchesSubdomain = selectedDomainTypes.includes('subdomain') && row.isSubdomain;
427
+ if (!matchesRoot && !matchesSubdomain) {
428
+ return false;
429
+ }
430
+ }
431
+
267
432
  // Owner filter
268
433
  if (selectedOwners.length > 0) {
269
434
  const itemOwners = row.owners || [];
@@ -403,6 +568,7 @@ export function DiscoverTable<T extends DiscoverTableData>({
403
568
  selectedOwners,
404
569
  selectedProducers,
405
570
  selectedConsumers,
571
+ selectedDomainTypes,
406
572
  selectedAgentProviders,
407
573
  selectedAgentModels,
408
574
  selectedBadges,
@@ -514,27 +680,18 @@ export function DiscoverTable<T extends DiscoverTableData>({
514
680
  return counts;
515
681
  }, [initialData]);
516
682
 
517
- const statusCounts = useMemo(() => {
518
- const counts: Record<string, number> = {};
683
+ const domainTypeCounts = useMemo(() => {
684
+ const counts: Record<string, number> = { root: 0, subdomain: 0 };
519
685
  initialData.forEach((item) => {
520
- const status = item.data.status;
521
- if (status) counts[status] = (counts[status] || 0) + 1;
686
+ if (item.isSubdomain) {
687
+ counts.subdomain++;
688
+ } else {
689
+ counts.root++;
690
+ }
522
691
  });
523
692
  return counts;
524
693
  }, [initialData]);
525
694
 
526
- const statusOptions = useMemo(
527
- () =>
528
- Object.keys(statusCounts)
529
- .map((status) => ({
530
- id: status,
531
- name: formatAdrStatus(status as AdrStatus),
532
- count: statusCounts[status],
533
- }))
534
- .sort((a, b) => a.name.localeCompare(b.name)),
535
- [statusCounts]
536
- );
537
-
538
695
  const toggleDomain = (domainId: string) => {
539
696
  setSelectedDomains((prev) => (prev.includes(domainId) ? prev.filter((id) => id !== domainId) : [...prev, domainId]));
540
697
  };
@@ -561,6 +718,12 @@ export function DiscoverTable<T extends DiscoverTableData>({
561
718
  setSelectedAgentModels((prev) => (prev.includes(modelId) ? prev.filter((id) => id !== modelId) : [...prev, modelId]));
562
719
  };
563
720
 
721
+ const toggleDomainType = (domainType: string) => {
722
+ setSelectedDomainTypes((prev) =>
723
+ prev.includes(domainType) ? prev.filter((id) => id !== domainType) : [...prev, domainType]
724
+ );
725
+ };
726
+
564
727
  const toggleBadge = (badgeContent: string) => {
565
728
  setSelectedBadges((prev) => (prev.includes(badgeContent) ? prev.filter((b) => b !== badgeContent) : [...prev, badgeContent]));
566
729
  };
@@ -576,7 +739,7 @@ export function DiscoverTable<T extends DiscoverTableData>({
576
739
  const navigateToKind = (kindId: CollectionType) => {
577
740
  const selectedKind = collectionKinds.find((kind) => kind.id === kindId);
578
741
  if (!selectedKind || selectedKind.id === collectionType) return;
579
- window.location.href = selectedKind.href;
742
+ window.location.href = `${selectedKind.href}${buildDiscoverFilterSearch(currentUrlFilters)}`;
580
743
  };
581
744
 
582
745
  const clearAllFilters = () => {
@@ -586,6 +749,7 @@ export function DiscoverTable<T extends DiscoverTableData>({
586
749
  setSelectedConsumers([]);
587
750
  setSelectedAgentProviders([]);
588
751
  setSelectedAgentModels([]);
752
+ setSelectedDomainTypes(collectionType === 'domains' ? ['root'] : []);
589
753
  setSelectedBadges([]);
590
754
  setSelectedStatuses([]);
591
755
  setSelectedProperties([]);
@@ -602,11 +766,13 @@ export function DiscoverTable<T extends DiscoverTableData>({
602
766
  selectedConsumers.length +
603
767
  selectedAgentProviders.length +
604
768
  selectedAgentModels.length +
769
+ selectedDomainTypes.length +
605
770
  selectedBadges.length +
606
771
  selectedStatuses.length +
607
772
  selectedProperties.length +
608
773
  (!showOnlyLatest ? 1 : 0) +
609
- (onlyShowDrafts ? 1 : 0);
774
+ (onlyShowDrafts ? 1 : 0) +
775
+ (tableFilter ? 1 : 0);
610
776
 
611
777
  // Get selected domain names for display
612
778
  const selectedDomainNames = selectedDomains.map((id) => domains.find((d) => d.id === id)?.name || id);
@@ -624,6 +790,9 @@ export function DiscoverTable<T extends DiscoverTableData>({
624
790
  (id) => agentProviders.find((provider) => provider.id === id)?.name || id
625
791
  );
626
792
  const selectedAgentModelNames = selectedAgentModels.map((id) => agentModels.find((model) => model.id === id)?.name || id);
793
+ const selectedDomainTypeNames = selectedDomainTypes.map(
794
+ (id) => DOMAIN_TYPE_OPTIONS.find((option) => option.id === id)?.label || id
795
+ );
627
796
  const selectedStatusNames = selectedStatuses.map((id) => statusOptions.find((status) => status.id === id)?.name || id);
628
797
 
629
798
  // Filter producers/consumers to only show those with count > 0
@@ -735,6 +904,33 @@ export function DiscoverTable<T extends DiscoverTableData>({
735
904
 
736
905
  {/* Catalog Filters Section */}
737
906
  <div className="space-y-3">
907
+ {collectionType === 'domains' && (
908
+ <div>
909
+ <label className="block text-xs font-medium text-[rgb(var(--ec-page-text)/0.8)] mb-1.5">
910
+ Filter by Domain type
911
+ </label>
912
+ <FilterDropdown
913
+ label="All domains"
914
+ selectedItems={selectedDomainTypeNames}
915
+ onClear={() => setSelectedDomainTypes([])}
916
+ onRemoveItem={(label) => {
917
+ const option = DOMAIN_TYPE_OPTIONS.find((item) => item.label === label);
918
+ if (option) toggleDomainType(option.id);
919
+ }}
920
+ >
921
+ {DOMAIN_TYPE_OPTIONS.map((option) => (
922
+ <CheckboxItem
923
+ key={option.id}
924
+ label={option.label}
925
+ checked={selectedDomainTypes.includes(option.id)}
926
+ onChange={() => toggleDomainType(option.id)}
927
+ count={domainTypeCounts[option.id] || 0}
928
+ />
929
+ ))}
930
+ </FilterDropdown>
931
+ </div>
932
+ )}
933
+
738
934
  {collectionType === 'agents' && (filteredAgentProviders.length > 0 || filteredAgentModels.length > 0) && (
739
935
  <>
740
936
  {filteredAgentProviders.length > 0 && (
@@ -19,6 +19,7 @@ import { favoritesStore, toggleFavorite, type FavoriteItem } from '../../../stor
19
19
  import type { DiscoverTableData, CollectionType } from './DiscoverTable';
20
20
  import type { TableConfiguration } from '@types';
21
21
  import { formatAdrDate, isAdrCollection } from '@utils/collections/adr-constants';
22
+ import { CornerDownRight } from 'lucide-react';
22
23
 
23
24
  const columnHelper = createColumnHelper<DiscoverTableData>();
24
25
 
@@ -121,22 +122,48 @@ const ResourceNameCell = ({ item }: { item: DiscoverTableData }) => {
121
122
  const { color, Icon } = getColorAndIconForCollection(item.collection);
122
123
  const resourceIcon = item.data.icon;
123
124
  const resourceIconUrl = isIconPath(resourceIcon) ? resolveIconUrl(resourceIcon) : null;
125
+ const isDomain = item.collection === 'domains';
126
+ const subdomains = isDomain ? item.data.domains || [] : [];
124
127
 
125
128
  return (
126
- <a
127
- href={buildUrl(`/docs/${item.collection}/${item.data.id}/${item.data.version}`)}
128
- className="group inline-flex items-center gap-2.5 hover:text-[rgb(var(--ec-accent))] transition-colors"
129
- >
130
- {resourceIconUrl ? (
131
- <img src={resourceIconUrl} alt="" className="h-5 w-5 flex-shrink-0 rounded-sm object-contain" />
132
- ) : (
133
- <Icon className={`h-4 w-4 flex-shrink-0 ${getCollectionTextColorClass(color, 'text-[rgb(var(--ec-icon-color))]')}`} />
129
+ <div className="flex min-w-0 flex-col gap-2">
130
+ <a
131
+ href={buildUrl(`/docs/${item.collection}/${item.data.id}/${item.data.version}`)}
132
+ className="group inline-flex items-center gap-2.5 hover:text-[rgb(var(--ec-accent))] transition-colors"
133
+ >
134
+ {resourceIconUrl ? (
135
+ <img src={resourceIconUrl} alt="" className="h-5 w-5 flex-shrink-0 rounded-sm object-contain" />
136
+ ) : (
137
+ <Icon className={`h-4 w-4 flex-shrink-0 ${getCollectionTextColorClass(color, 'text-[rgb(var(--ec-icon-color))]')}`} />
138
+ )}
139
+ <span className="text-sm font-semibold text-[rgb(var(--ec-page-text))] group-hover:text-[rgb(var(--ec-accent))]">
140
+ {item.data.name}
141
+ </span>
142
+ {!isLatestVersion && <span className="text-xs text-[rgb(var(--ec-icon-color))]">v{item.data.version}</span>}
143
+ {item.isSubdomain && (
144
+ <span className="rounded-full border border-[rgb(var(--ec-page-border))] px-1.5 py-0.5 text-[10px] font-medium text-[rgb(var(--ec-page-text-muted))]">
145
+ Subdomain
146
+ </span>
147
+ )}
148
+ </a>
149
+
150
+ {subdomains.length > 0 && (
151
+ <div className="flex flex-col gap-1 pl-6">
152
+ {subdomains.map((domain: any) => (
153
+ <a
154
+ key={`${domain.data.id}-${domain.data.version}`}
155
+ href={buildUrl(`/docs/${domain.collection || 'domains'}/${domain.data.id}/${domain.data.version}`)}
156
+ className="group/subdomain inline-flex w-fit items-center gap-1.5 text-xs text-[rgb(var(--ec-page-text-muted))] hover:text-[rgb(var(--ec-accent))]"
157
+ >
158
+ <CornerDownRight className="h-3.5 w-3.5 flex-shrink-0 text-[rgb(var(--ec-icon-color))] group-hover/subdomain:text-[rgb(var(--ec-accent))]" />
159
+ <span className="font-medium text-[rgb(var(--ec-page-text))] group-hover/subdomain:text-[rgb(var(--ec-accent))]">
160
+ {domain.data.name}
161
+ </span>
162
+ </a>
163
+ ))}
164
+ </div>
134
165
  )}
135
- <span className="text-sm font-semibold text-[rgb(var(--ec-page-text))] group-hover:text-[rgb(var(--ec-accent))]">
136
- {item.data.name}
137
- </span>
138
- {!isLatestVersion && <span className="text-xs text-[rgb(var(--ec-icon-color))]">v{item.data.version}</span>}
139
- </a>
166
+ </div>
140
167
  );
141
168
  };
142
169
 
@@ -0,0 +1,89 @@
1
+ export interface DiscoverFilterQueryState {
2
+ q: string;
3
+ domains: string[];
4
+ owners: string[];
5
+ producers: string[];
6
+ consumers: string[];
7
+ agentProviders: string[];
8
+ agentModels: string[];
9
+ domainTypes: string[];
10
+ badges: string[];
11
+ properties: string[];
12
+ statuses: string[];
13
+ showOnlyLatest: boolean;
14
+ onlyShowDrafts: boolean;
15
+ }
16
+
17
+ const FILTER_PARAM_NAMES = [
18
+ 'q',
19
+ 'domain',
20
+ 'owner',
21
+ 'producer',
22
+ 'consumer',
23
+ 'agentProvider',
24
+ 'agentModel',
25
+ 'domainType',
26
+ 'badge',
27
+ 'property',
28
+ 'status',
29
+ 'latest',
30
+ 'drafts',
31
+ ];
32
+
33
+ const uniqueNonEmpty = (values: string[]) => Array.from(new Set(values.map((value) => value.trim()).filter(Boolean)));
34
+
35
+ const getValues = (params: URLSearchParams, paramName: string) => uniqueNonEmpty(params.getAll(paramName));
36
+
37
+ const isFalse = (value: string | null) => value === 'false' || value === '0';
38
+
39
+ const isTrue = (value: string | null) => value === 'true' || value === '1';
40
+
41
+ export const parseDiscoverFilterSearch = (search: string): DiscoverFilterQueryState => {
42
+ const params = new URLSearchParams(search);
43
+
44
+ return {
45
+ q: params.get('q')?.trim() ?? '',
46
+ domains: getValues(params, 'domain'),
47
+ owners: getValues(params, 'owner'),
48
+ producers: getValues(params, 'producer'),
49
+ consumers: getValues(params, 'consumer'),
50
+ agentProviders: getValues(params, 'agentProvider'),
51
+ agentModels: getValues(params, 'agentModel'),
52
+ domainTypes: getValues(params, 'domainType'),
53
+ badges: getValues(params, 'badge'),
54
+ properties: getValues(params, 'property'),
55
+ statuses: getValues(params, 'status'),
56
+ showOnlyLatest: !isFalse(params.get('latest')),
57
+ onlyShowDrafts: isTrue(params.get('drafts')),
58
+ };
59
+ };
60
+
61
+ export const filterKnownValues = (values: string[], knownValues: Set<string>) => values.filter((value) => knownValues.has(value));
62
+
63
+ export const buildDiscoverFilterSearch = (filters: DiscoverFilterQueryState, currentSearch = '') => {
64
+ const params = new URLSearchParams(currentSearch);
65
+ FILTER_PARAM_NAMES.forEach((paramName) => params.delete(paramName));
66
+
67
+ const appendValues = (paramName: string, values: string[]) => {
68
+ uniqueNonEmpty(values).forEach((value) => params.append(paramName, value));
69
+ };
70
+
71
+ const q = filters.q.trim();
72
+ if (q) params.set('q', q);
73
+ appendValues('domain', filters.domains);
74
+ appendValues('owner', filters.owners);
75
+ appendValues('producer', filters.producers);
76
+ appendValues('consumer', filters.consumers);
77
+ appendValues('agentProvider', filters.agentProviders);
78
+ appendValues('agentModel', filters.agentModels);
79
+ appendValues('domainType', filters.domainTypes);
80
+ appendValues('badge', filters.badges);
81
+ appendValues('property', filters.properties);
82
+ appendValues('status', filters.statuses);
83
+
84
+ if (!filters.showOnlyLatest) params.set('latest', 'false');
85
+ if (filters.onlyShowDrafts) params.set('drafts', 'true');
86
+
87
+ const search = params.toString();
88
+ return search ? `?${search}` : '';
89
+ };
@@ -93,7 +93,6 @@ const typeConfig: Record<
93
93
  propertyOptions: [
94
94
  { id: 'hasServices', label: 'Has Services' },
95
95
  { id: 'hasOwners', label: 'Has Owners' },
96
- { id: 'isSubdomain', label: 'Is Subdomain' },
97
96
  { id: 'isDeprecated', label: 'Is Deprecated' },
98
97
  ],
99
98
  },
@@ -285,6 +284,21 @@ function resolvePointer(pointer: { id: string; version?: string }) {
285
284
  const allSubdomainIds = new Set(
286
285
  domains.flatMap((d: any) => (d.data?.domains || []).map((sd: any) => sd.data?.id || sd.id)).filter(Boolean)
287
286
  );
287
+ const parentDomainsBySubdomainId = domains.reduce((parentsBySubdomain: Map<string, any[]>, domain: any) => {
288
+ (domain.data?.domains || []).forEach((subdomain: any) => {
289
+ const subdomainId = subdomain.data?.id || subdomain.id;
290
+ if (!subdomainId) return;
291
+
292
+ const parents = parentsBySubdomain.get(subdomainId) || [];
293
+ parents.push({
294
+ id: domain.data.id,
295
+ name: domain.data.name,
296
+ version: domain.data.version,
297
+ });
298
+ parentsBySubdomain.set(subdomainId, parents);
299
+ });
300
+ return parentsBySubdomain;
301
+ }, new Map<string, any[]>());
288
302
 
289
303
  const isAgentLike = type === 'agents';
290
304
  const isServiceLike = type === 'services' || type === 'external-systems';
@@ -314,6 +328,7 @@ const tableData = enrichedData.map((d: any) => ({
314
328
  hasServices: type === 'domains' || type === 'systems' ? (d.data?.services || []).length > 0 : false,
315
329
  hasFlows: type === 'systems' ? (d.data?.flows || []).length > 0 : false,
316
330
  isSubdomain: type === 'domains' ? allSubdomainIds.has(d.data.id) : false,
331
+ parentDomains: type === 'domains' ? parentDomainsBySubdomainId.get(d.data.id) || [] : undefined,
317
332
  // Service-specific properties
318
333
  domains: isServiceOrAgentLike ? d.enrichedDomains : undefined,
319
334
  hasSpecifications: isServiceLike ? hasSpecifications(d) : false,
@@ -347,6 +362,7 @@ const tableData = enrichedData.map((d: any) => ({
347
362
  tools: d.data?.tools ?? [],
348
363
  agents: d.data?.agents?.map(mapToItem) ?? [],
349
364
  services: d.data?.services?.map(mapToItem) ?? [],
365
+ domains: d.data?.domains?.map(mapToItem) ?? [],
350
366
  flows: d.data?.flows?.map(mapToItem) ?? [],
351
367
  servicesThatWriteToContainer: d.data?.servicesThatWriteToContainer?.map(mapToItem) ?? [],
352
368
  servicesThatReadFromContainer: d.data?.servicesThatReadFromContainer?.map(mapToItem) ?? [],
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "license": "SEE LICENSE IN LICENSE",
9
9
  "type": "module",
10
- "version": "4.0.3",
10
+ "version": "4.0.5",
11
11
  "publishConfig": {
12
12
  "access": "public"
13
13
  },