@eventcatalog/core 3.35.1 → 3.36.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.
- package/dist/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +1 -1
- package/dist/analytics/log-build.js +3 -3
- package/dist/{chunk-4SNN54V4.js → chunk-6D65JSOA.js} +1 -1
- package/dist/{chunk-B7C4DHFE.js → chunk-C7JCOHTI.js} +1 -1
- package/dist/chunk-D6IBLY3O.js +320 -0
- package/dist/{chunk-R4DR3YAH.js → chunk-HDENGAZL.js} +1 -1
- package/dist/{chunk-JEQZWJWP.js → chunk-UJ7DX4SA.js} +3 -3
- package/dist/{chunk-3KXCGYET.js → chunk-ULZYHF3V.js} +5 -0
- package/dist/{chunk-VJ357XOI.js → chunk-V22QY5Q3.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/docs/api/02-config.md +22 -0
- package/dist/docs/api/_category_.json +1 -1
- package/dist/docs/contributing/_category_.json +1 -1
- package/dist/docs/development/_category_.json +1 -1
- package/dist/docs/development/ask-your-architecture/02-eventcatalog-assistant/_category_.json +1 -1
- package/dist/docs/development/ask-your-architecture/03-mcp-server/_category_.json +1 -1
- package/dist/docs/development/ask-your-architecture/04-skills/_category_.json +1 -1
- package/dist/docs/development/authentication/providers/_category_.json +1 -1
- package/dist/docs/development/bring-your-own-documentation/custom-pages/_category_.json +1 -1
- package/dist/docs/development/customization/01-customize-landing-page.md +1 -1
- package/dist/docs/development/customization/03-search.md +79 -0
- package/dist/docs/development/customization/custom-components/_category_.json +1 -1
- package/dist/docs/development/customization/customize-sidebars/_category_.json +1 -1
- package/dist/docs/development/customization/customize-visualizer/_category_.json +1 -1
- package/dist/docs/development/design/_category_.json +1 -1
- package/dist/docs/development/guides/changelogs/_category_.json +1 -1
- package/dist/docs/development/guides/channels/_category_.json +1 -1
- package/dist/docs/development/guides/channels/ownership-and-components/_category_.json +1 -1
- package/dist/docs/development/guides/channels/versioning-and-lifecycle/_category_.json +1 -1
- package/dist/docs/development/guides/data/_category_.json +1 -1
- package/dist/docs/development/guides/data/ownership-and-components/_category_.json +1 -1
- package/dist/docs/development/guides/data-products/_category_.json +1 -1
- package/dist/docs/development/guides/domains/02-creating-domains/_category_.json +1 -1
- package/dist/docs/development/guides/domains/03-ownership-and-language/_category_.json +1 -1
- package/dist/docs/development/guides/domains/05-entities/_category_.json +1 -1
- package/dist/docs/development/guides/domains/_category_.json +1 -1
- package/dist/docs/development/guides/flows/_category_.json +1 -1
- package/dist/docs/development/guides/messages/_category_.json +1 -1
- package/dist/docs/development/guides/messages/commands/_category_.json +1 -1
- package/dist/docs/development/guides/messages/common/_category_.json +1 -1
- package/dist/docs/development/guides/messages/events/_category_.json +1 -1
- package/dist/docs/development/guides/messages/queries/_category_.json +1 -1
- package/dist/docs/development/guides/owners/_category_.json +1 -1
- package/dist/docs/development/guides/owners/teams/_category_.json +1 -1
- package/dist/docs/development/guides/owners/users/_category_.json +1 -1
- package/dist/docs/development/guides/schemas/_category_.json +1 -1
- package/dist/docs/development/guides/services/_category_.json +1 -1
- package/dist/docs/development/guides/services/adding-to-services/_category_.json +1 -1
- package/dist/docs/development/guides/services/ownership-and-components/_category_.json +1 -1
- package/dist/docs/development/guides/services/versioning-and-lifecycle/_category_.json +1 -1
- package/dist/docs/plugins/_category_.json +1 -1
- package/dist/docs/plugins/amazon-apigateway/_category_.json +1 -1
- package/dist/docs/plugins/asyncapi/_category_.json +1 -1
- package/dist/docs/plugins/aws-glue-registry/_category_.json +1 -1
- package/dist/docs/plugins/backstage/_category_.json +1 -1
- package/dist/docs/plugins/confluent-schema-registry/_category_.json +1 -1
- package/dist/docs/plugins/eventbridge/_category_.json +1 -1
- package/dist/docs/plugins/eventcatalog-federation/_category_.json +1 -1
- package/dist/docs/plugins/github/_category_.json +1 -1
- package/dist/docs/plugins/graphql/_category_.json +1 -1
- package/dist/docs/plugins/hookdeck/_category_.json +1 -1
- package/dist/docs/plugins/openapi/_category_.json +1 -1
- package/dist/eventcatalog.cjs +434 -35
- package/dist/eventcatalog.config.d.cts +8 -0
- package/dist/eventcatalog.config.d.ts +8 -0
- package/dist/eventcatalog.js +87 -10
- package/dist/features.cjs +6 -0
- package/dist/features.d.cts +2 -1
- package/dist/features.d.ts +2 -1
- package/dist/features.js +3 -1
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/search-indexer.cjs +356 -0
- package/dist/search-indexer.d.cts +30 -0
- package/dist/search-indexer.d.ts +30 -0
- package/dist/search-indexer.js +10 -0
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/eventcatalog/astro.config.mjs +28 -32
- package/eventcatalog/src/components/Search/SearchModal.tsx +248 -148
- package/eventcatalog/src/components/Search/search-utils.spec.ts +138 -1
- package/eventcatalog/src/components/Search/search-utils.ts +271 -0
- package/eventcatalog/src/env.d.ts +1 -0
- package/package.json +3 -2
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { describe, expect, it, beforeEach } from 'vitest';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
applyActiveFilter,
|
|
4
|
+
getIndexedResultRank,
|
|
5
|
+
getSearchFilters,
|
|
6
|
+
getUrlForSearchItem,
|
|
7
|
+
hasMeaningfulIndexedMatch,
|
|
8
|
+
highlightQuery,
|
|
9
|
+
mapPagefindResultsToSearchItems,
|
|
10
|
+
normalizeResultUrl,
|
|
11
|
+
} from './search-utils';
|
|
3
12
|
|
|
4
13
|
declare global {
|
|
5
14
|
interface Window {
|
|
@@ -34,3 +43,131 @@ describe('getUrlForSearchItem', () => {
|
|
|
34
43
|
expect(getUrlForSearchItem({}, 'unknown:Customer360:1.0.0')).toBeNull();
|
|
35
44
|
});
|
|
36
45
|
});
|
|
46
|
+
|
|
47
|
+
describe('indexed search result helpers', () => {
|
|
48
|
+
it('filters weak single-character highlighted matches for longer queries', () => {
|
|
49
|
+
expect(
|
|
50
|
+
hasMeaningfulIndexedMatch({
|
|
51
|
+
query: 'random',
|
|
52
|
+
title: 'Orders Service',
|
|
53
|
+
content: 'Order Metadata in api service server logs',
|
|
54
|
+
excerpt: 'Order Metadata in api db L -- <mark>R</mark> server',
|
|
55
|
+
})
|
|
56
|
+
).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('keeps short but meaningful marked terms', () => {
|
|
60
|
+
expect(
|
|
61
|
+
hasMeaningfulIndexedMatch({
|
|
62
|
+
query: 'db',
|
|
63
|
+
title: 'Orders Service',
|
|
64
|
+
content: 'Order Metadata in api service server logs',
|
|
65
|
+
excerpt: 'Order Metadata in api <mark>db</mark>',
|
|
66
|
+
})
|
|
67
|
+
).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('keeps exact title or content matches even without marked excerpts', () => {
|
|
71
|
+
expect(
|
|
72
|
+
hasMeaningfulIndexedMatch({
|
|
73
|
+
query: 'payments',
|
|
74
|
+
title: 'Payments Database',
|
|
75
|
+
content: '',
|
|
76
|
+
excerpt: '',
|
|
77
|
+
})
|
|
78
|
+
).toBe(true);
|
|
79
|
+
|
|
80
|
+
expect(
|
|
81
|
+
hasMeaningfulIndexedMatch({
|
|
82
|
+
query: 'idempotency',
|
|
83
|
+
title: 'Payments Database',
|
|
84
|
+
content: 'Always include an idempotency key in payment creation.',
|
|
85
|
+
excerpt: '',
|
|
86
|
+
})
|
|
87
|
+
).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('normalizes Pagefind URLs to root-relative paths', () => {
|
|
91
|
+
expect(normalizeResultUrl('docs/containers/payments-db/0.0.1')).toBe('/docs/containers/payments-db/0.0.1');
|
|
92
|
+
expect(normalizeResultUrl('/docs/containers/payments-db/0.0.1')).toBe('/docs/containers/payments-db/0.0.1');
|
|
93
|
+
expect(normalizeResultUrl('https://example.com/docs')).toBe('https://example.com/docs');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('highlights title matches while escaping unsafe title text', () => {
|
|
97
|
+
expect(highlightQuery('Payment <Service>', 'payment')).toBe('<mark>Payment</mark> <Service>');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('ranks title matches above id/url matches and content matches', () => {
|
|
101
|
+
const query = 'payment';
|
|
102
|
+
const contentRank = getIndexedResultRank({
|
|
103
|
+
query,
|
|
104
|
+
title: 'Orders Service',
|
|
105
|
+
url: '/docs/services/OrdersService/1.0.0',
|
|
106
|
+
content: 'Handles payment retries',
|
|
107
|
+
});
|
|
108
|
+
const urlRank = getIndexedResultRank({
|
|
109
|
+
query,
|
|
110
|
+
title: 'Orders Service',
|
|
111
|
+
url: '/docs/services/PaymentService/1.0.0',
|
|
112
|
+
content: 'Handles orders',
|
|
113
|
+
});
|
|
114
|
+
const titleRank = getIndexedResultRank({
|
|
115
|
+
query,
|
|
116
|
+
title: 'Payment Service',
|
|
117
|
+
url: '/docs/services/PaymentService/1.0.0',
|
|
118
|
+
content: 'Handles orders',
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
expect(titleRank).toBeGreaterThan(urlRank);
|
|
122
|
+
expect(urlRank).toBeGreaterThan(contentRank);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('applies grouped search filters', () => {
|
|
126
|
+
const items = [{ type: 'Event' }, { type: 'Command' }, { type: 'Team' }, { type: 'User' }, { type: 'Service' }];
|
|
127
|
+
|
|
128
|
+
expect(applyActiveFilter(items, 'Message')).toEqual([{ type: 'Event' }, { type: 'Command' }]);
|
|
129
|
+
expect(applyActiveFilter(items, 'Team')).toEqual([{ type: 'Team' }, { type: 'User' }]);
|
|
130
|
+
expect(applyActiveFilter(items, 'Service')).toEqual([{ type: 'Service' }]);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('builds filter metadata without encoding behavior in labels', () => {
|
|
134
|
+
expect(getSearchFilters({ items: [], query: 'missing' })).toEqual([{ id: 'all', name: 'All (0)', count: 0 }]);
|
|
135
|
+
expect(getSearchFilters({ items: [{ type: 'Event' }, { type: 'Service' }], query: 'order' })).toEqual([
|
|
136
|
+
{ id: 'all', name: 'All (2)', count: 2 },
|
|
137
|
+
{ id: 'Service', name: 'Services (1)', count: 1 },
|
|
138
|
+
{ id: 'Message', name: 'Messages (1)', count: 1 },
|
|
139
|
+
]);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('maps Pagefind results into ranked search items', async () => {
|
|
143
|
+
const items = await mapPagefindResultsToSearchItems({
|
|
144
|
+
query: 'payment',
|
|
145
|
+
limit: 10,
|
|
146
|
+
results: [
|
|
147
|
+
{
|
|
148
|
+
id: 'content',
|
|
149
|
+
score: 10,
|
|
150
|
+
data: async () => ({
|
|
151
|
+
url: 'docs/services/OrdersService/1.0.0',
|
|
152
|
+
content: 'Handles payment retries',
|
|
153
|
+
excerpt: 'Handles <mark>payment</mark> retries',
|
|
154
|
+
meta: { title: 'Orders Service', type: 'Service', id: 'OrdersService' },
|
|
155
|
+
}),
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: 'title',
|
|
159
|
+
score: 1,
|
|
160
|
+
data: async () => ({
|
|
161
|
+
url: 'docs/services/PaymentService/1.0.0',
|
|
162
|
+
content: 'Handles orders',
|
|
163
|
+
excerpt: '<mark>Payment</mark> service',
|
|
164
|
+
meta: { title: 'Payment Service', type: 'Service', id: 'PaymentService' },
|
|
165
|
+
}),
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
expect(items.map((item) => item.id)).toEqual(['title', 'content']);
|
|
171
|
+
expect(items[0].url).toBe('/docs/services/PaymentService/1.0.0');
|
|
172
|
+
});
|
|
173
|
+
});
|
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
import { buildUrl } from '@utils/url-builder';
|
|
2
2
|
|
|
3
|
+
export interface SearchNode {
|
|
4
|
+
key: string;
|
|
5
|
+
title: string;
|
|
6
|
+
badge?: string;
|
|
7
|
+
summary?: string;
|
|
8
|
+
href?: string;
|
|
9
|
+
icon?: string;
|
|
10
|
+
leftIcon?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface SearchItem {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
url: string;
|
|
17
|
+
type: string;
|
|
18
|
+
key?: string;
|
|
19
|
+
rawNode: {
|
|
20
|
+
title?: string;
|
|
21
|
+
badge?: string;
|
|
22
|
+
summary?: string;
|
|
23
|
+
icon?: string;
|
|
24
|
+
leftIcon?: string;
|
|
25
|
+
matchedExcerpt?: string;
|
|
26
|
+
};
|
|
27
|
+
isFavorite?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface SearchFilter {
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
count: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface PagefindResult {
|
|
37
|
+
id: string;
|
|
38
|
+
score?: number;
|
|
39
|
+
data: () => Promise<{
|
|
40
|
+
url: string;
|
|
41
|
+
excerpt?: string;
|
|
42
|
+
content?: string;
|
|
43
|
+
meta?: Record<string, any>;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
|
|
3
47
|
const docsPathByType: Record<string, string> = {
|
|
4
48
|
channel: 'channels',
|
|
5
49
|
command: 'commands',
|
|
@@ -32,3 +76,230 @@ export const getUrlForSearchItem = (node: { href?: string }, key: string) => {
|
|
|
32
76
|
|
|
33
77
|
return buildUrl(`/docs/${docsPath}/${id}/${version}`);
|
|
34
78
|
};
|
|
79
|
+
|
|
80
|
+
export const stripHtml = (value: string) =>
|
|
81
|
+
value
|
|
82
|
+
.replace(/<[^>]+>/g, '')
|
|
83
|
+
.replace(/\s+/g, ' ')
|
|
84
|
+
.trim();
|
|
85
|
+
|
|
86
|
+
const escapeHtml = (value: string) =>
|
|
87
|
+
value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
88
|
+
|
|
89
|
+
const escapeRegExp = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
90
|
+
|
|
91
|
+
const getSearchTerms = (query: string) => [...new Set(query.trim().toLowerCase().split(/\s+/).filter(Boolean))];
|
|
92
|
+
|
|
93
|
+
const getMarkedTerms = (value: string) => {
|
|
94
|
+
return [...value.matchAll(/<mark>(.*?)<\/mark>/gi)].map((match) => stripHtml(match[1]).toLowerCase());
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const highlightQuery = (value: string, query: string) => {
|
|
98
|
+
const terms = getSearchTerms(query).sort((a, b) => b.length - a.length);
|
|
99
|
+
|
|
100
|
+
if (terms.length === 0) {
|
|
101
|
+
return escapeHtml(value);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const termLookup = new Set(terms);
|
|
105
|
+
const pattern = new RegExp(`(${terms.map(escapeRegExp).join('|')})`, 'gi');
|
|
106
|
+
|
|
107
|
+
return value
|
|
108
|
+
.split(pattern)
|
|
109
|
+
.map((part) => (termLookup.has(part.toLowerCase()) ? `<mark>${escapeHtml(part)}</mark>` : escapeHtml(part)))
|
|
110
|
+
.join('');
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const normalizeResultUrl = (url: string) => {
|
|
114
|
+
if (/^(https?:)?\/\//.test(url) || url.startsWith('/')) {
|
|
115
|
+
return url;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return `/${url}`;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export const hasMeaningfulIndexedMatch = ({
|
|
122
|
+
query,
|
|
123
|
+
title,
|
|
124
|
+
content,
|
|
125
|
+
excerpt,
|
|
126
|
+
}: {
|
|
127
|
+
query: string;
|
|
128
|
+
title: string;
|
|
129
|
+
content?: string;
|
|
130
|
+
excerpt: string;
|
|
131
|
+
}) => {
|
|
132
|
+
const terms = getSearchTerms(query);
|
|
133
|
+
if (terms.length === 0) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const searchableText = `${title} ${content || ''}`.toLowerCase();
|
|
138
|
+
if (terms.some((term) => searchableText.includes(term))) {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const minimumMarkedLength = Math.min(3, Math.max(...terms.map((term) => term.length)));
|
|
143
|
+
return getMarkedTerms(excerpt).some((term) => term.length >= minimumMarkedLength);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export const getIndexedResultRank = ({
|
|
147
|
+
query,
|
|
148
|
+
title,
|
|
149
|
+
id,
|
|
150
|
+
url,
|
|
151
|
+
content,
|
|
152
|
+
}: {
|
|
153
|
+
query: string;
|
|
154
|
+
title: string;
|
|
155
|
+
id?: string;
|
|
156
|
+
url: string;
|
|
157
|
+
content?: string;
|
|
158
|
+
}) => {
|
|
159
|
+
const terms = getSearchTerms(query);
|
|
160
|
+
const titleText = title.toLowerCase();
|
|
161
|
+
const identityText = `${id || ''} ${url}`.toLowerCase();
|
|
162
|
+
const contentText = (content || '').toLowerCase();
|
|
163
|
+
|
|
164
|
+
if (terms.some((term) => titleText.includes(term))) return 3;
|
|
165
|
+
if (terms.some((term) => identityText.includes(term))) return 2;
|
|
166
|
+
if (terms.some((term) => contentText.includes(term))) return 1;
|
|
167
|
+
return 0;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export const applyActiveFilter = <T extends { type: string }>(items: T[], activeFilter: string) => {
|
|
171
|
+
if (activeFilter === 'all') {
|
|
172
|
+
return items;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (activeFilter === 'Message') {
|
|
176
|
+
return items.filter((item) => ['Event', 'Command', 'Query'].includes(item.type));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (activeFilter === 'Team') {
|
|
180
|
+
return items.filter((item) => ['Team', 'User'].includes(item.type));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return items.filter((item) => item.type === activeFilter);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export const getSearchFilters = ({ items, query }: { items: Array<{ type: string }>; query: string }): SearchFilter[] => {
|
|
187
|
+
if (!items.length && query !== '') {
|
|
188
|
+
return [{ id: 'all', name: 'All (0)', count: 0 }];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const counts: Record<string, number> = {
|
|
192
|
+
all: items.length,
|
|
193
|
+
Domain: 0,
|
|
194
|
+
Service: 0,
|
|
195
|
+
Message: 0,
|
|
196
|
+
Team: 0,
|
|
197
|
+
Container: 0,
|
|
198
|
+
Entity: 0,
|
|
199
|
+
Design: 0,
|
|
200
|
+
Channel: 0,
|
|
201
|
+
Flow: 0,
|
|
202
|
+
'Data Product': 0,
|
|
203
|
+
'Custom Doc': 0,
|
|
204
|
+
'Resource Doc': 0,
|
|
205
|
+
Changelog: 0,
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
items.forEach((item) => {
|
|
209
|
+
if (counts[item.type] !== undefined) {
|
|
210
|
+
counts[item.type]++;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (['Event', 'Command', 'Query'].includes(item.type)) {
|
|
214
|
+
counts.Message++;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (['Team', 'User'].includes(item.type)) {
|
|
218
|
+
counts.Team++;
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const filters: SearchFilter[] = [{ id: 'all', name: `All (${counts.all})`, count: counts.all }];
|
|
223
|
+
const addFilter = (id: string, name: string) => {
|
|
224
|
+
if (counts[id] > 0) {
|
|
225
|
+
filters.push({ id, name: `${name} (${counts[id]})`, count: counts[id] });
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
addFilter('Domain', 'Domains');
|
|
230
|
+
addFilter('Service', 'Services');
|
|
231
|
+
addFilter('Message', 'Messages');
|
|
232
|
+
addFilter('Container', 'Data Stores');
|
|
233
|
+
addFilter('Entity', 'Entities');
|
|
234
|
+
addFilter('Channel', 'Channels');
|
|
235
|
+
addFilter('Flow', 'Flows');
|
|
236
|
+
addFilter('Data Product', 'Data Products');
|
|
237
|
+
addFilter('Custom Doc', 'Custom Docs');
|
|
238
|
+
addFilter('Resource Doc', 'Resource Docs');
|
|
239
|
+
addFilter('Changelog', 'Changelogs');
|
|
240
|
+
addFilter('Design', 'Designs');
|
|
241
|
+
addFilter('Team', 'Teams & Users');
|
|
242
|
+
|
|
243
|
+
return filters;
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
export const mapPagefindResultsToSearchItems = async ({
|
|
247
|
+
results,
|
|
248
|
+
query,
|
|
249
|
+
limit,
|
|
250
|
+
}: {
|
|
251
|
+
results: PagefindResult[];
|
|
252
|
+
query: string;
|
|
253
|
+
limit: number;
|
|
254
|
+
}): Promise<SearchItem[]> => {
|
|
255
|
+
const mappedResults = await Promise.all(
|
|
256
|
+
results.slice(0, limit).map(async (result, resultIndex) => {
|
|
257
|
+
const data = await result.data();
|
|
258
|
+
const meta = data.meta || {};
|
|
259
|
+
const type = meta.type || 'Page';
|
|
260
|
+
const title = meta.title || data.url || 'Untitled';
|
|
261
|
+
const summary = meta.summary || stripHtml(data.excerpt || '');
|
|
262
|
+
const url = normalizeResultUrl(data.url);
|
|
263
|
+
|
|
264
|
+
if (
|
|
265
|
+
!hasMeaningfulIndexedMatch({
|
|
266
|
+
query,
|
|
267
|
+
title,
|
|
268
|
+
content: data.content,
|
|
269
|
+
excerpt: data.excerpt || '',
|
|
270
|
+
})
|
|
271
|
+
) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
item: {
|
|
277
|
+
id: result.id,
|
|
278
|
+
name: title,
|
|
279
|
+
url,
|
|
280
|
+
type,
|
|
281
|
+
rawNode: {
|
|
282
|
+
title,
|
|
283
|
+
badge: type,
|
|
284
|
+
summary,
|
|
285
|
+
matchedExcerpt: data.excerpt || summary,
|
|
286
|
+
},
|
|
287
|
+
} satisfies SearchItem,
|
|
288
|
+
resultIndex,
|
|
289
|
+
rank: getIndexedResultRank({
|
|
290
|
+
query,
|
|
291
|
+
title,
|
|
292
|
+
id: meta.id,
|
|
293
|
+
url,
|
|
294
|
+
content: data.content,
|
|
295
|
+
}),
|
|
296
|
+
score: typeof result.score === 'number' ? result.score : 0,
|
|
297
|
+
};
|
|
298
|
+
})
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
return mappedResults
|
|
302
|
+
.filter((result): result is NonNullable<typeof result> => result !== null)
|
|
303
|
+
.sort((a, b) => b.rank - a.rank || b.score - a.score || a.resultIndex - b.resultIndex)
|
|
304
|
+
.map((result) => result.item);
|
|
305
|
+
};
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"license": "SEE LICENSE IN LICENSE",
|
|
9
9
|
"type": "module",
|
|
10
|
-
"version": "3.
|
|
10
|
+
"version": "3.36.1",
|
|
11
11
|
"publishConfig": {
|
|
12
12
|
"access": "public"
|
|
13
13
|
},
|
|
@@ -80,6 +80,7 @@
|
|
|
80
80
|
"mdast-util-find-and-replace": "^3.0.2",
|
|
81
81
|
"mermaid": "^11.12.3",
|
|
82
82
|
"nanostores": "^1.1.0",
|
|
83
|
+
"pagefind": "^1.5.2",
|
|
83
84
|
"pako": "^2.1.0",
|
|
84
85
|
"picocolors": "^1.1.1",
|
|
85
86
|
"react": "^18.3.1",
|
|
@@ -105,8 +106,8 @@
|
|
|
105
106
|
"update-notifier": "^7.3.1",
|
|
106
107
|
"uuid": "^10.0.0",
|
|
107
108
|
"zod": "^4.3.6",
|
|
108
|
-
"@eventcatalog/linter": "1.0.22",
|
|
109
109
|
"@eventcatalog/sdk": "2.21.0",
|
|
110
|
+
"@eventcatalog/linter": "1.0.22",
|
|
110
111
|
"@eventcatalog/visualiser": "^3.20.0"
|
|
111
112
|
},
|
|
112
113
|
"devDependencies": {
|